diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index d1d1ee70..6782e187 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -43,7 +43,7 @@ jobs: { "stdlibs": ["libstdc++"], "tests": [ "Debug.Default", "Release.Default", "Release.TSan", - "Release.MaxSan", "Debug.Werror", "Debug.Dynamic", + "Release.MaxSan", "Debug.Werror", "Debug.Coverage" ] } @@ -78,7 +78,7 @@ jobs: { "stdlibs": ["libstdc++", "libc++"], "tests": [ "Debug.Default", "Release.Default", "Release.TSan", - "Release.MaxSan", "Debug.Werror", "Debug.Dynamic" + "Release.MaxSan", "Debug.Werror" ] } ] diff --git a/.github/workflows/static_exemplar_test.yml b/.github/workflows/static_exemplar_test.yml new file mode 100644 index 00000000..9810b9ef --- /dev/null +++ b/.github/workflows/static_exemplar_test.yml @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: Static Exemplar Test +on: + push: + branches: + - main + pull_request: + +jobs: + static-exemplar-test: + runs-on: ubuntu-latest + container: ghcr.io/bemanproject/infra-containers-gcc:latest + name: "Static exemplar smoke test" + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Test static exemplar + run: | + cd cookiecutter + source ./check_cookiecutter.sh + cookiecutter_venv_path=$(mktemp --directory --dry-run) + setup_venv "$cookiecutter_venv_path" + stamp "$PWD" "./static_exemplar" "static" + cd static_exemplar/exemplar + cmake -B build -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=./infra/cmake/use-fetch-content.cmake -DCMAKE_CXX_STANDARD=20 -DCMAKE_INSTALL_PREFIX=$PWD/dist + cmake --build build + ctest --test-dir build + cmake --install build diff --git a/CMakeLists.txt b/CMakeLists.txt index f9f7a49a..4d48f984 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,17 @@ option( # for find of beman-install-library include(infra/cmake/beman-install-library-config.cmake) -add_subdirectory(src/beman/exemplar) +add_library(beman.exemplar INTERFACE) +add_library(beman::exemplar ALIAS beman.exemplar) + +target_sources( + beman.exemplar + PUBLIC FILE_SET HEADERS BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include" +) + +set_target_properties(beman.exemplar PROPERTIES VERIFY_INTERFACE_HEADER_SETS ON) + +add_subdirectory(include/beman/exemplar) beman_install_library(beman.exemplar) diff --git a/README.md b/README.md index 93109300..33603a7c 100644 --- a/README.md +++ b/README.md @@ -312,7 +312,6 @@ cmake -B build -S . -DCMAKE_CXX_STANDARD=20 -DBEMAN_EXEMPLAR_BUILD_TESTS=OFF Enable building examples. Default: ON. Values: { ON, OFF }. - #### `BEMAN_EXEMPLAR_INSTALL_CONFIG_FILE_PACKAGE` Enable installing the CMake config file package. Default: ON. @@ -358,10 +357,9 @@ any libraries or executables that include `beman.exemplar` headers. target_link_libraries(yourlib PUBLIC beman::exemplar) ``` -### Produce beman.exemplar static library +### Produce beman.exemplar interface library -You can include exemplar's headers locally -by producing a static `libbeman.exemplar.a` library. +You can produce exemplar's interface library locally by: ```bash cmake --workflow --preset gcc-release @@ -377,11 +375,9 @@ This will generate the following directory structure at `/opt/beman`. │ └── exemplar │ └── identity.hpp └── lib - ├── cmake - │   └── beman.exemplar - │   ├── beman.exemplar-config-version.cmake - │   ├── beman.exemplar-config.cmake - │   ├── beman.exemplar-targets-debug.cmake - │   └── beman.exemplar-targets.cmake - └── libbeman.exemplar.a + └── cmake + └── beman.exemplar + ├── beman.exemplar-config-version.cmake + ├── beman.exemplar-config.cmake + └── beman.exemplar-targets.cmake ``` diff --git a/cookiecutter/check_cookiecutter.sh b/cookiecutter/check_cookiecutter.sh index fb5cb04c..a3f1ef7c 100755 --- a/cookiecutter/check_cookiecutter.sh +++ b/cookiecutter/check_cookiecutter.sh @@ -4,22 +4,31 @@ set -euo pipefail declare script_dir=$(realpath $(dirname "$BASH_SOURCE")) -function check_consistency() { - local out_dir_path - out_dir_path=$(mktemp --directory --dry-run) - cd /tmp +function stamp() { + local cookiecutter_dir="$1" ; shift + local output_dir="$1" ; shift + local library_type="$1" ; shift python3 \ -m cookiecutter \ --no-input \ - --output-dir "$out_dir_path" \ - "$script_dir" \ + --output-dir "$output_dir" \ + "$cookiecutter_dir" \ project_name="exemplar" \ cpp_build_version="17" \ paper="P0898R3" \ owner="bemanproject" \ description="A Beman Library Exemplar" \ - godbolt_link="https://godbolt.org/z/4qEPK87va" + godbolt_link="https://godbolt.org/z/4qEPK87va" \ + library_type="$library_type" +} + +function check_consistency() { + local out_dir_path + out_dir_path=$(mktemp --directory --dry-run) + cd /tmp + stamp "$script_dir" "$out_dir_path" "interface" cp "$script_dir"/../.github/workflows/cookiecutter_test.yml "$out_dir_path"/exemplar/.github/workflows + cp "$script_dir"/../.github/workflows/static_exemplar_test.yml "$out_dir_path"/exemplar/.github/workflows local diff_path diff_path=$(mktemp) diff -r "$script_dir/.." "$out_dir_path/exemplar" \ @@ -65,12 +74,17 @@ function check_templating() { rm "$grep_path" } -function main() { - local cookiecutter_venv_path - cookiecutter_venv_path=$(mktemp --directory --dry-run) +function setup_venv() { + local path="$1" ; shift python3 -m venv "$cookiecutter_venv_path" source "$cookiecutter_venv_path/bin/activate" python3 -m pip install cookiecutter >& /dev/null +} + +function main() { + local cookiecutter_venv_path + cookiecutter_venv_path=$(mktemp --directory --dry-run) + setup_venv "$cookiecutter_venv_path" check_consistency check_templating rm -rf "$cookiecutter_venv_path" diff --git a/cookiecutter/cookiecutter.json b/cookiecutter/cookiecutter.json index 5fbba469..0793d6a6 100644 --- a/cookiecutter/cookiecutter.json +++ b/cookiecutter/cookiecutter.json @@ -5,6 +5,7 @@ "owner": "github-user-name", "description": "Short project description.", "godbolt_link": "https://www.example.com", + "library_type": ["interface", "static"], "_copy_without_render": [ "infra" ], diff --git a/cookiecutter/hooks/post_gen_project.py b/cookiecutter/hooks/post_gen_project.py new file mode 100755 index 00000000..b5acb534 --- /dev/null +++ b/cookiecutter/hooks/post_gen_project.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +"""Post-generation hook to clean up template files based on library_type.""" + +import shutil +from pathlib import Path +import os + +# Get the library type from cookiecutter context +library_type = "{{ cookiecutter.library_type }}" + +# If interface library, remove the src/ directory (not needed for header-only) +if library_type == "interface": + src_dir = Path("src") + if src_dir.exists(): + shutil.rmtree(src_dir) diff --git a/cookiecutter/{{cookiecutter.project_name}}/.github/workflows/ci_tests.yml b/cookiecutter/{{cookiecutter.project_name}}/.github/workflows/ci_tests.yml index 139d3d73..375fe194 100644 --- a/cookiecutter/{{cookiecutter.project_name}}/.github/workflows/ci_tests.yml +++ b/cookiecutter/{{cookiecutter.project_name}}/.github/workflows/ci_tests.yml @@ -43,8 +43,9 @@ jobs: { "stdlibs": ["libstdc++"], "tests": [ "Debug.Default", "Release.Default", "Release.TSan", - "Release.MaxSan", "Debug.Werror", "Debug.Dynamic", - "Debug.Coverage" + "Release.MaxSan", "Debug.Werror", + "Debug.Coverage"{% if cookiecutter.library_type == "static" %}, "Debug.Dynamic"{% endif %} + ] } ] @@ -78,7 +79,8 @@ jobs: { "stdlibs": ["libstdc++", "libc++"], "tests": [ "Debug.Default", "Release.Default", "Release.TSan", - "Release.MaxSan", "Debug.Werror", "Debug.Dynamic" + "Release.MaxSan", "Debug.Werror"{% if cookiecutter.library_type == "static" %}, "Debug.Dynamic"{% endif %} + ] } ] diff --git a/cookiecutter/{{cookiecutter.project_name}}/CMakeLists.txt b/cookiecutter/{{cookiecutter.project_name}}/CMakeLists.txt index e284e48b..44203922 100644 --- a/cookiecutter/{{cookiecutter.project_name}}/CMakeLists.txt +++ b/cookiecutter/{{cookiecutter.project_name}}/CMakeLists.txt @@ -27,7 +27,24 @@ option( # for find of beman-install-library include(infra/cmake/beman-install-library-config.cmake) +{% if cookiecutter.library_type == "interface" %} +add_library(beman.{{cookiecutter.project_name}} INTERFACE) +{% else %} +add_library(beman.{{cookiecutter.project_name}}) +{% endif %} +add_library(beman::{{cookiecutter.project_name}} ALIAS beman.{{cookiecutter.project_name}}) + +target_sources( + beman.{{cookiecutter.project_name}} + PUBLIC FILE_SET HEADERS BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include" +) + +set_target_properties(beman.{{cookiecutter.project_name}} PROPERTIES VERIFY_INTERFACE_HEADER_SETS ON) + +add_subdirectory(include/beman/{{cookiecutter.project_name}}) +{% if cookiecutter.library_type == "static" %} add_subdirectory(src/beman/{{cookiecutter.project_name}}) +{% endif %} beman_install_library(beman.{{cookiecutter.project_name}}) diff --git a/cookiecutter/{{cookiecutter.project_name}}/README.md b/cookiecutter/{{cookiecutter.project_name}}/README.md index 02a66f17..1f972b05 100644 --- a/cookiecutter/{{cookiecutter.project_name}}/README.md +++ b/cookiecutter/{{cookiecutter.project_name}}/README.md @@ -312,7 +312,6 @@ cmake -B build -S . -DCMAKE_CXX_STANDARD=20 -DBEMAN_{{cookiecutter.project_name. Enable building examples. Default: ON. Values: { ON, OFF }. - #### `BEMAN_{{cookiecutter.project_name.upper()}}_INSTALL_CONFIG_FILE_PACKAGE` Enable installing the CMake config file package. Default: ON. @@ -358,10 +357,13 @@ any libraries or executables that include `beman.{{cookiecutter.project_name}}` target_link_libraries(yourlib PUBLIC beman::{{cookiecutter.project_name}}) ``` -### Produce beman.{{cookiecutter.project_name}} static library +### Produce beman.{{cookiecutter.project_name}} {{ cookiecutter.library_type }} library -You can include {{cookiecutter.project_name}}'s headers locally -by producing a static `libbeman.{{cookiecutter.project_name}}.a` library. +{% if cookiecutter.library_type == "interface" %} +You can produce {{cookiecutter.project_name}}'s interface library locally by: +{% else %} +You can produce {{cookiecutter.project_name}}'s static library `libbeman.{{cookiecutter.project_name}}.a` by: +{% endif %} ```bash cmake --workflow --preset gcc-release @@ -370,6 +372,21 @@ cmake --install build/gcc-release --prefix /opt/beman This will generate the following directory structure at `/opt/beman`. +{% if cookiecutter.library_type == "interface" %} +```txt +/opt/beman +├── include +│ └── beman +│ └── {{cookiecutter.project_name}} +│ └── identity.hpp +└── lib + └── cmake + └── beman.{{cookiecutter.project_name}} + ├── beman.{{cookiecutter.project_name}}-config-version.cmake + ├── beman.{{cookiecutter.project_name}}-config.cmake + └── beman.{{cookiecutter.project_name}}-targets.cmake +``` +{% else %} ```txt /opt/beman ├── include @@ -378,10 +395,11 @@ This will generate the following directory structure at `/opt/beman`. │ └── identity.hpp └── lib ├── cmake - │   └── beman.{{cookiecutter.project_name}} - │   ├── beman.{{cookiecutter.project_name}}-config-version.cmake - │   ├── beman.{{cookiecutter.project_name}}-config.cmake - │   ├── beman.{{cookiecutter.project_name}}-targets-debug.cmake - │   └── beman.{{cookiecutter.project_name}}-targets.cmake + │ └── beman.{{cookiecutter.project_name}} + │ ├── beman.{{cookiecutter.project_name}}-config-version.cmake + │ ├── beman.{{cookiecutter.project_name}}-config.cmake + │ ├── beman.{{cookiecutter.project_name}}-targets-debug.cmake + │ └── beman.{{cookiecutter.project_name}}-targets.cmake └── libbeman.{{cookiecutter.project_name}}.a ``` +{% endif %} diff --git a/cookiecutter/{{cookiecutter.project_name}}/include/beman/{{cookiecutter.project_name}}/CMakeLists.txt b/cookiecutter/{{cookiecutter.project_name}}/include/beman/{{cookiecutter.project_name}}/CMakeLists.txt new file mode 100644 index 00000000..ec7f23e6 --- /dev/null +++ b/cookiecutter/{{cookiecutter.project_name}}/include/beman/{{cookiecutter.project_name}}/CMakeLists.txt @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +target_sources(beman.{{cookiecutter.project_name}} PUBLIC FILE_SET HEADERS FILES identity.hpp) diff --git a/cookiecutter/{{cookiecutter.project_name}}/src/beman/{{cookiecutter.project_name}}/CMakeLists.txt b/cookiecutter/{{cookiecutter.project_name}}/src/beman/{{cookiecutter.project_name}}/CMakeLists.txt index c1100030..ca6c72ca 100644 --- a/cookiecutter/{{cookiecutter.project_name}}/src/beman/{{cookiecutter.project_name}}/CMakeLists.txt +++ b/cookiecutter/{{cookiecutter.project_name}}/src/beman/{{cookiecutter.project_name}}/CMakeLists.txt @@ -1,17 +1,7 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -add_library(beman.{{cookiecutter.project_name}}) -add_library(beman::{{cookiecutter.project_name}} ALIAS beman.{{cookiecutter.project_name}}) - -target_sources(beman.{{cookiecutter.project_name}} PRIVATE identity.cpp) - target_sources( beman.{{cookiecutter.project_name}} - PUBLIC - FILE_SET HEADERS - BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/../../../include" - FILES - "${CMAKE_CURRENT_SOURCE_DIR}/../../../include/beman/{{cookiecutter.project_name}}/identity.hpp" + PRIVATE + identity.cpp ) - -set_target_properties(beman.{{cookiecutter.project_name}} PROPERTIES VERIFY_INTERFACE_HEADER_SETS ON) diff --git a/cookiecutter/{{cookiecutter.project_name}}/src/beman/{{cookiecutter.project_name}}/identity.cpp b/cookiecutter/{{cookiecutter.project_name}}/src/beman/{{cookiecutter.project_name}}/identity.cpp index 1d076c7a..c1729968 100644 --- a/cookiecutter/{{cookiecutter.project_name}}/src/beman/{{cookiecutter.project_name}}/identity.cpp +++ b/cookiecutter/{{cookiecutter.project_name}}/src/beman/{{cookiecutter.project_name}}/identity.cpp @@ -1,3 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include + +// Implementation file for static library build. +// For a simple identity function, there's nothing to implement here, +// but this file exists to demonstrate the static library structure. diff --git a/include/beman/exemplar/CMakeLists.txt b/include/beman/exemplar/CMakeLists.txt new file mode 100644 index 00000000..6664097b --- /dev/null +++ b/include/beman/exemplar/CMakeLists.txt @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +target_sources(beman.exemplar PUBLIC FILE_SET HEADERS FILES identity.hpp) diff --git a/src/beman/exemplar/CMakeLists.txt b/src/beman/exemplar/CMakeLists.txt deleted file mode 100644 index 2ce8c18b..00000000 --- a/src/beman/exemplar/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -add_library(beman.exemplar) -add_library(beman::exemplar ALIAS beman.exemplar) - -target_sources(beman.exemplar PRIVATE identity.cpp) - -target_sources( - beman.exemplar - PUBLIC - FILE_SET HEADERS - BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/../../../include" - FILES - "${CMAKE_CURRENT_SOURCE_DIR}/../../../include/beman/exemplar/identity.hpp" -) - -set_target_properties(beman.exemplar PROPERTIES VERIFY_INTERFACE_HEADER_SETS ON) diff --git a/src/beman/exemplar/identity.cpp b/src/beman/exemplar/identity.cpp deleted file mode 100644 index 21ef59d5..00000000 --- a/src/beman/exemplar/identity.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include