diff --git a/README.md b/README.md index b08616a..1144ba7 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,8 @@ cpp_library_setup( DESCRIPTION "${PROJECT_DESCRIPTION}" NAMESPACE your_namespace HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/your_namespace/your_header.hpp + # Optional: add SOURCES for non-header-only libraries + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/your_library.cpp EXAMPLES your_example your_example_fail TESTS your_tests DOCS_EXCLUDE_SYMBOLS "your_namespace::implementation" @@ -66,14 +68,17 @@ cpp_library_setup( # Header specification (one required) HEADERS header_list # List of header files HEADER_DIR directory # Directory to install recursively - + + # Optional: source specification for non-header-only libraries + SOURCES source_list # List of source files (e.g., src/*.cpp) + # Optional features [EXAMPLES example_list] # Example executables to build - [TESTS test_list] # Test executables to build + [TESTS test_list] # Test executables to build [DOCS_EXCLUDE_SYMBOLS symbols] # Symbols to exclude from docs [REQUIRES_CPP_VERSION 17|20|23] # C++ version (default: 17) [ADDITIONAL_DEPS dep_list] # Extra CPM dependencies - + # Optional flags [CUSTOM_INSTALL] # Skip default installation [NO_PRESETS] # Skip CMakePresets.json generation @@ -83,6 +88,11 @@ cpp_library_setup( ``` ## Features +### Non-Header-Only Library Support + +- **Automatic detection of sources in `src/`**: If source files are present in `src/`, the template will build a regular (static) library instead of header-only INTERFACE target. + Specify sources manually with the `SOURCES` argument, or let the template auto-detect files in `src/`. + Both header-only and compiled libraries are supported seamlessly. ### Automated Infrastructure @@ -186,8 +196,8 @@ cpp_library_setup( # Clone or create your project mkdir my-library && cd my-library - # Create basic structure - mkdir -p include/your_namespace examples tests cmake + # Create basic structure + mkdir -p include/your_namespace src examples tests cmake # Add CPM.cmake curl -L https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/get_cpm.cmake -o cmake/CPM.cmake @@ -212,10 +222,11 @@ cpp_library_setup( The template automatically generates: -- **CMakePresets.json**: Build configurations for different purposes -- **.github/workflows/ci.yml**: Multi-platform CI/CD pipeline -- **.gitignore**: Standard ignores for C++ projects -- **Package config files**: For proper CMake integration + - **CMakePresets.json**: Build configurations for different purposes + - **.github/workflows/ci.yml**: Multi-platform CI/CD pipeline + - **.gitignore**: Standard ignores for C++ projects + - **src/**: Source directory for non-header-only libraries (auto-detected) + - **Package config files**: For proper CMake integration ## License diff --git a/cmake/cpp-library-setup.cmake b/cmake/cpp-library-setup.cmake index 9163fef..3468038 100644 --- a/cmake/cpp-library-setup.cmake +++ b/cmake/cpp-library-setup.cmake @@ -14,6 +14,7 @@ function(_cpp_library_setup_core) ) set(multiValueArgs HEADERS + SOURCES ) cmake_parse_arguments(ARG "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -21,28 +22,40 @@ function(_cpp_library_setup_core) # Extract the library name without namespace prefix for target naming string(REPLACE "${ARG_NAMESPACE}-" "" CLEAN_NAME "${ARG_NAME}") - # Create the INTERFACE library target - add_library(${ARG_NAME} INTERFACE) - add_library(${ARG_NAMESPACE}::${CLEAN_NAME} ALIAS ${ARG_NAME}) - - # Set include directories - target_include_directories(${ARG_NAME} INTERFACE - $ - $ - ) - - # Set C++ standard requirement - target_compile_features(${ARG_NAME} INTERFACE cxx_std_${ARG_REQUIRES_CPP_VERSION}) - - # Set up installation if headers are specified - if(ARG_HEADERS) - # Use FILE_SET for modern CMake header installation - target_sources(${ARG_NAME} INTERFACE - FILE_SET headers - TYPE HEADERS - BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include - FILES ${ARG_HEADERS} + if(ARG_SOURCES) + # Create a regular library if sources are present + add_library(${ARG_NAME} STATIC ${ARG_SOURCES}) + add_library(${ARG_NAMESPACE}::${CLEAN_NAME} ALIAS ${ARG_NAME}) + target_include_directories(${ARG_NAME} PUBLIC + $ + $ ) + target_compile_features(${ARG_NAME} PUBLIC cxx_std_${ARG_REQUIRES_CPP_VERSION}) + if(ARG_HEADERS) + target_sources(${ARG_NAME} PUBLIC + FILE_SET headers + TYPE HEADERS + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include + FILES ${ARG_HEADERS} + ) + endif() + else() + # Header-only INTERFACE target + add_library(${ARG_NAME} INTERFACE) + add_library(${ARG_NAMESPACE}::${CLEAN_NAME} ALIAS ${ARG_NAME}) + target_include_directories(${ARG_NAME} INTERFACE + $ + $ + ) + target_compile_features(${ARG_NAME} INTERFACE cxx_std_${ARG_REQUIRES_CPP_VERSION}) + if(ARG_HEADERS) + target_sources(${ARG_NAME} INTERFACE + FILE_SET headers + TYPE HEADERS + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include + FILES ${ARG_HEADERS} + ) + endif() endif() # Only set up full installation when building as top-level project @@ -93,3 +106,39 @@ function(_cpp_library_setup_core) endif() endfunction() + +# Function to copy static template files +function(_cpp_library_copy_templates) + set(options FORCE_INIT) + cmake_parse_arguments(ARG "${options}" "" "" ${ARGN}) + + # List of static template files to copy + set(TEMPLATE_FILES + ".clang-format" + ".gitignore" + ".gitattributes" + ".vscode/extensions.json" + "docs/index.html" + ) + + foreach(template_file IN LISTS TEMPLATE_FILES) + set(source_file "${CPP_LIBRARY_ROOT}/templates/${template_file}") + set(dest_file "${CMAKE_CURRENT_SOURCE_DIR}/${template_file}") + + # Check if template file exists + if(EXISTS "${source_file}") + # Copy if file doesn't exist or FORCE_INIT is enabled + if(NOT EXISTS "${dest_file}" OR ARG_FORCE_INIT) + # Create directory if needed + get_filename_component(dest_dir "${dest_file}" DIRECTORY) + file(MAKE_DIRECTORY "${dest_dir}") + + # Copy the file + file(COPY "${source_file}" DESTINATION "${dest_dir}") + message(STATUS "Copied template file: ${template_file}") + endif() + else() + message(WARNING "Template file not found: ${source_file}") + endif() + endforeach() +endfunction() diff --git a/cpp-library.cmake b/cpp-library.cmake index 983a20a..239f7d7 100644 --- a/cpp-library.cmake +++ b/cpp-library.cmake @@ -37,13 +37,22 @@ function(cpp_library_setup) ) set(multiValueArgs HEADERS # List of header files - EXAMPLES # Example executables to build - TESTS # Test executables to build - DOCS_EXCLUDE_SYMBOLS # Symbols to exclude from docs - ADDITIONAL_DEPS # Extra CPM dependencies + SOURCES # List of source files (optional, for non-header-only) + EXAMPLES # Example executables to build + TESTS # Test executables to build + DOCS_EXCLUDE_SYMBOLS # Symbols to exclude from docs + ADDITIONAL_DEPS # Extra CPM dependencies ) cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + # Detect sources in /src if SOURCES not provided + if(NOT ARG_SOURCES) + file(GLOB_RECURSE DETECTED_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "src/*.cpp" "src/*.c" "src/*.cc" "src/*.cxx") + if(DETECTED_SOURCES) + set(ARG_SOURCES ${DETECTED_SOURCES}) + endif() + endif() # Validate required arguments if(NOT ARG_NAME) @@ -79,6 +88,7 @@ function(cpp_library_setup) DESCRIPTION "${ARG_DESCRIPTION}" NAMESPACE "${ARG_NAMESPACE}" HEADERS "${ARG_HEADERS}" + SOURCES "${ARG_SOURCES}" HEADER_DIR "${ARG_HEADER_DIR}" REQUIRES_CPP_VERSION "${ARG_REQUIRES_CPP_VERSION}" TOP_LEVEL "${PROJECT_IS_TOP_LEVEL}" @@ -103,6 +113,9 @@ function(cpp_library_setup) if(NOT ARG_NO_PRESETS) _cpp_library_generate_presets(FORCE_INIT ${ARG_FORCE_INIT}) endif() + + # Copy static template files (like .clang-format, .gitignore, etc.) + _cpp_library_copy_templates(FORCE_INIT ${ARG_FORCE_INIT}) # Setup testing (if tests are specified) if(BUILD_TESTING AND ARG_TESTS) diff --git a/templates/.clang-format b/templates/.clang-format new file mode 100644 index 0000000..e3ffa6d --- /dev/null +++ b/templates/.clang-format @@ -0,0 +1,119 @@ +# Format style options described here: +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html + +# Many of the alignment and single line changes were made to facilitate setting +# breakpoints on specific expressions. + +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: false +# AlwaysBreakAfterDefinitionReturnType: TopLevel +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: false +# BraceWrapping: +# AfterClass: true +# AfterControlStatement: false +# AfterEnum: false +# AfterFunction: false +# AfterNamespace: false +# AfterObjCDeclaration: false +# AfterStruct: false +# AfterUnion: false +# AfterExternBlock: false +# BeforeCatch: false +# BeforeElse: false +# IndentBraces: false +# SplitEmptyFunction: true +# SplitEmptyRecord: true +# SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: AfterColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 100 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '$' +IndentCaseLabels: true +IndentPPDirectives: None # Other option is AfterHash, which indents top level includes as well +IndentWidth: 4 +IndentWrappedFunctionNames: true +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 1000 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp17 +TabWidth: 4 +UseTab: Never +--- +Language: Cpp +--- +Language: ObjC +PointerAlignment: Right +... diff --git a/templates/.gitattributes b/templates/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/templates/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/templates/.gitignore b/templates/.gitignore index e685d89..f2f3226 100644 --- a/templates/.gitignore +++ b/templates/.gitignore @@ -1,6 +1,5 @@ /.cpm-cache -/.cpp-library-cache /.cache /build -include/.DS_Store +.DS_Store compile_commands.json diff --git a/templates/.vscode/extensions.json b/templates/.vscode/extensions.json new file mode 100644 index 0000000..6ffa416 --- /dev/null +++ b/templates/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "matepek.vscode-catch2-test-adapter", + "llvm-vs-code-extensions.vscode-clangd", + "ms-vscode.live-server", + "xaver.clang-format" + ] +} diff --git a/templates/docs/index.html b/templates/docs/index.html new file mode 100644 index 0000000..90e6e9b --- /dev/null +++ b/templates/docs/index.html @@ -0,0 +1,14 @@ + + + + + + + Redirecting to Documentation + + + +

If you are not redirected automatically, click here.

+ + +