From 324025f36a17aecc011552341cf34c3166fab3a2 Mon Sep 17 00:00:00 2001 From: friendlyanon <1736896+friendlyanon@users.noreply.github.com> Date: Wed, 26 May 2021 21:44:40 +0200 Subject: [PATCH] Port to CMake build system (#2) Port project to CMake Co-authored-by: friendlyanon --- .clang-tidy | 4 + .github/workflows/ci.yml | 131 +++++++++++++++++++++++++++++++ .gitignore | 2 + BUILDING.md | 71 +++++++++++++++++ CMakeLists.txt | 84 ++++++++++++++++++++ CMakePresets.json | 104 ++++++++++++++++++++++++ HACKING.md | 98 +++++++++++++++++++++++ Makefile | 78 ------------------ cmake/docs.cmake | 39 +++++++++ cmake/enum_setConfig.cmake | 25 ++++++ cmake/in-source-guard.cmake | 8 ++ cmake/install-rules.cmake | 62 +++++++++++++++ cmake/project-is-top-level.cmake | 6 ++ cmake/variables.cmake | 66 ++++++++++++++++ cmake/windows-set-path.cmake | 22 ++++++ docs/Doxyfile.in | 31 ++++++++ docs/conf.py.in | 6 ++ docs/pages/about.dox | 7 ++ example/CMakeLists.txt | 38 +++++++++ test/CMakeLists.txt | 51 ++++++++++++ test/{ => include}/testing.hpp | 0 test/main.cpp | 2 - vcpkg.json | 15 ++++ 23 files changed, 870 insertions(+), 80 deletions(-) create mode 100644 .clang-tidy create mode 100644 .github/workflows/ci.yml create mode 100644 BUILDING.md create mode 100644 CMakeLists.txt create mode 100644 CMakePresets.json create mode 100644 HACKING.md delete mode 100644 Makefile create mode 100644 cmake/docs.cmake create mode 100644 cmake/enum_setConfig.cmake create mode 100644 cmake/in-source-guard.cmake create mode 100644 cmake/install-rules.cmake create mode 100644 cmake/project-is-top-level.cmake create mode 100644 cmake/variables.cmake create mode 100644 cmake/windows-set-path.cmake create mode 100644 docs/Doxyfile.in create mode 100644 docs/conf.py.in create mode 100644 docs/pages/about.dox create mode 100644 example/CMakeLists.txt create mode 100644 test/CMakeLists.txt rename test/{ => include}/testing.hpp (100%) delete mode 100644 test/main.cpp create mode 100644 vcpkg.json diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..a589c72 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,4 @@ +--- +Checks: 'clang-diagnostic-*,clang-analyzer-*' +WarningsAsErrors: '' +... diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3315603 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,131 @@ +name: Continuous Integration + +on: + push: + branches: + - master + + pull_request: + branches: + - master + +jobs: + coverage: + runs-on: ubuntu-latest + + # To enable coverage, go to https://codecov.io/, acquire a token, put it + # into your secrets (Settings > Secrets > New repository secret) and delete + # the "if" conditional below. + # If you do not wish to use codecov, then simply delete this job from the + # workflow. + if: "false" + + steps: + - uses: actions/checkout@v1 + + - name: Install LCov + run: sudo apt-get update -q + && sudo apt-get install lcov -q -y + + - name: Install vcpkg + uses: lukka/run-vcpkg@v7 + with: + setupOnly: true + vcpkgGitCommitId: 7c55ecac266fc3e554bc315053dc45c11ec811af + + - name: Configure + run: cmake --preset=ci-coverage + -D "VCPKG_MANIFEST_FEATURES=test;magic-enum" + + - name: Build + run: cmake --build build -j 2 + + - name: Test + working-directory: build + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + PS4: "\e[36m+++\e[0m " + run: | + set -x + ctest --output-on-failure -j 2 + lcov -c -d .. -o coverage.info --include '${{ github.workspace }}/*' + lcov --list coverage.info + bash <(curl -s https://codecov.io/bash) -f coverage.info + + sanitize: + name: "sanitize${{ matrix.features && ' (with magic_enum)' }}" + + strategy: + fail-fast: false + + matrix: + features: [";magic-enum", ""] + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + + - name: Install vcpkg + uses: lukka/run-vcpkg@v7 + with: + setupOnly: true + vcpkgGitCommitId: 7c55ecac266fc3e554bc315053dc45c11ec811af + + - name: Configure + env: { CXX: clang++-11 } + run: cmake --preset=ci-sanitizer + -D "VCPKG_MANIFEST_FEATURES=test${{ matrix.features }}" + + - name: Build + run: cmake --build build -j 2 + + - name: Test + env: + ASAN_OPTIONS: "strict_string_checks=1:\ + detect_stack_use_after_return=1:\ + check_initialization_order=1:\ + strict_init_order=1:\ + detect_leaks=1" + UBSAN_OPTIONS: print_stacktrace=1 + run: ctest --test-dir build --output-on-failure -j 2 + + test: + name: "test (${{ matrix.os }}\ + ${{ matrix.features && ', with magic_enum' || '' }})" + + strategy: + fail-fast: false + + matrix: + os: [macos, ubuntu, windows] + + features: [";magic-enum", ""] + + runs-on: ${{ matrix.os }}-latest + + steps: + - uses: actions/checkout@v1 + + - name: Install static analyzers + if: matrix.os == 'ubuntu' + run: sudo apt-get install clang-tidy cppcheck -y -q + + - name: Install vcpkg + uses: lukka/run-vcpkg@v7 + with: + setupOnly: true + vcpkgGitCommitId: 7c55ecac266fc3e554bc315053dc45c11ec811af + + - name: Configure + run: cmake --preset=ci-${{ matrix.os }} + -D "VCPKG_MANIFEST_FEATURES=test${{ matrix.features }}" + + - name: Build + run: cmake --build build --config Release -j 2 + + - name: Install + run: cmake --install build --config Release --prefix prefix + + - name: Test + run: ctest --test-dir build --output-on-failure -C Release -j 2 diff --git a/.gitignore b/.gitignore index 567609b..2ac1559 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ build/ +prefix/ +CMakeUserPresets.json diff --git a/BUILDING.md b/BUILDING.md new file mode 100644 index 0000000..a0f6255 --- /dev/null +++ b/BUILDING.md @@ -0,0 +1,71 @@ +# Building with CMake + +## vcpkg dependencies + +This project uses vcpkg to manage its dependencies, but this is optional. You +may also install those dependencies manually or using any other package +manager. + +If you have vcpkg set up, then you have to provide some extra flags to the +configure command, which differs only in how you provide the paths depending on +your OS: + +```batch +rem Windows +-D "CMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake" +``` + +```sh +# Unix based (Linux, macOS) +-D "CMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" +``` + +You may also set the `VCPKG_MANIFEST_FEATURES` variable to `magic-enum` if you +would like to enable support for it. + +## Build + +Besides the ones for vcpkg dependencies (if you choose to provide them to the +project using vcpkg), this project doesn't require any special command-line +flags to build to keep things simple. + +Here are the steps for building in release mode with a single-configuration +generator, like the Unix Makefiles one: + +```sh +cmake -S . -B build -D CMAKE_BUILD_TYPE=Release # vcpkg flags +cmake --build build +``` + +Here are the steps for building in release mode with a multi-configuration +generator, like the Visual Studio ones: + +```sh +cmake -S . -B build # vcpkg flags +cmake --build build --config Release +``` + +## Install + +This project doesn't require any special command-line flags to install to keep +things simple. As a prerequisite, the project has to be built with the above +commands already. + +The below commands require at least CMake 3.15 to run, because that is the +version in which [Install a Project][1] was added. + +Here is the command for installing the release mode artifacts with a +single-configuration generator, like the Unix Makefiles one: + +```sh +cmake --install build +``` + +Here is the command for installing the release mode artifacts with a +multi-configuration generator, like the Visual Studio ones: + +```sh +cmake --install build --config Release +``` + +[1]: https://cmake.org/cmake/help/latest/manual/cmake.1.html#install-a-project diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f6bdc1e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,84 @@ +cmake_minimum_required(VERSION 3.14) + +include(cmake/in-source-guard.cmake) + +project( + enum_set + VERSION 0.1.0 + DESCRIPTION "A library of type safe sets over fixed size collections of types or values, including methods for accessing, modifying, visiting and iterating over those." + HOMEPAGE_URL "https://github.com/cdeln/cpp_enum_set" + LANGUAGES CXX +) + +include(cmake/project-is-top-level.cmake) +include(cmake/variables.cmake) + +# ---- Declare library ---- + +add_library(enum_set_enum_set INTERFACE) +add_library(enum_set::enum_set ALIAS enum_set_enum_set) + +set_property( + TARGET enum_set_enum_set PROPERTY + EXPORT_NAME enum_set +) + +target_include_directories( + enum_set_enum_set + ${enum_set_warning_guard} + INTERFACE + "$" +) + +# The following properties are only applied during build time +# For details, check the config file +set(std cxx_std_14) +if(enum_set_USE_MAGIC_ENUM) + set(std cxx_std_17) + find_package(magic_enum REQUIRED) + target_link_libraries( + enum_set_enum_set + INTERFACE + "$" + ) +endif() + +target_compile_features( + enum_set_enum_set + INTERFACE + "$" +) + +# ---- Install rules ---- + +include(cmake/install-rules.cmake) + +# ---- Examples ---- + +if(PROJECT_IS_TOP_LEVEL) + option(BUILD_EXAMPLES "Build examples tree." "${enum_set_DEVELOPER_MODE}") + if(BUILD_EXAMPLES) + add_subdirectory(example) + endif() +endif() + +# ---- Developer mode ---- + +if(NOT enum_set_DEVELOPER_MODE) + return() +elseif(NOT PROJECT_IS_TOP_LEVEL) + message( + AUTHOR_WARNING + "Developer mode is intended for developers of enum_set" + ) +endif() + +include(CTest) +if(BUILD_TESTING) + add_subdirectory(test) +endif() + +option(BUILD_DOCUMENTATION "Build documentation using Doxygen and m.css" OFF) +if(BUILD_DOCUMENTATION) + include(cmake/docs.cmake) +endif() diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..f2e141b --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,104 @@ +{ + "version": 1, + "cmakeMinimumRequired": { + "major": 3, + "minor": 14, + "patch": 0 + }, + "configurePresets": [ + { + "name": "vcpkg", + "hidden": true, + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + } + }, + { + "name": "dev-mode", + "hidden": true, + "cacheVariables": { + "enum_set_DEVELOPER_MODE": "ON" + } + }, + { + "name": "cppcheck", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_CPPCHECK": "cppcheck" + } + }, + { + "name": "clang-tidy", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_CLANG_TIDY": "clang-tidy;--header-filter=${sourceDir}/*" + } + }, + { + "name": "flags-unix", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_FLAGS": "-Wall -Wextra -pedantic", + "CMAKE_CXX_EXTENSIONS": "OFF" + } + }, + { + "name": "flags-windows", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_FLAGS": "/W4 /permissive- /EHsc /volatile:iso /Zc:__cplusplus /Zc:throwingNew" + } + }, + { + "name": "ci-build", + "binaryDir": "${sourceDir}/build", + "hidden": true + }, + { + "name": "ci-unix", + "generator": "Unix Makefiles", + "hidden": true, + "inherits": ["flags-unix", "ci-build"], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "ci-win64", + "inherits": ["flags-windows", "ci-build"], + "generator": "Visual Studio 16 2019", + "architecture": "x64", + "hidden": true + }, + { + "name": "ci-coverage", + "inherits": ["ci-unix", "dev-mode", "vcpkg"], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Coverage", + "CMAKE_CXX_FLAGS_COVERAGE": "-O0 -g --coverage -fkeep-inline-functions -fkeep-static-functions", + "CMAKE_EXE_LINKER_FLAGS_COVERAGE": "--coverage", + "CMAKE_SHARED_LINKER_FLAGS_COVERAGE": "--coverage" + } + }, + { + "name": "ci-sanitizer", + "inherits": ["ci-unix", "dev-mode", "vcpkg"], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Sanitizer", + "CMAKE_CXX_FLAGS_SANITIZER": "-O2 -g -fsanitize=address,undefined -fno-omit-frame-pointer -fno-common -fsanitize-address-use-after-scope" + } + }, + { + "name": "ci-macos", + "inherits": ["ci-unix", "dev-mode", "vcpkg"] + }, + { + "name": "ci-ubuntu", + "inherits": ["ci-unix", "clang-tidy", "cppcheck", "dev-mode", "vcpkg"] + }, + { + "name": "ci-windows", + "inherits": ["ci-win64", "dev-mode", "vcpkg"] + } + ] +} diff --git a/HACKING.md b/HACKING.md new file mode 100644 index 0000000..aa2e51d --- /dev/null +++ b/HACKING.md @@ -0,0 +1,98 @@ +# Hacking + +Here is some wisdom to help you build and test this project as a developer and +potential contributor. + +## Developer mode + +Build system targets that are only useful for developers of this project are +hidden if the `enum_set_DEVELOPER_MODE` option is disabled. Enabling this +option makes tests and other developer targets and options available. Not +enabling this option means that you are a consumer of this project and thus you +have no need for these targets and options. + +Developer mode is always set to on in CI workflows. + +### Presets + +This project makes use of [presets][1] to simplify the process of configuring +the project. As a developer, you are recommended to always have the [latest +CMake version][2] installed to make use of the latest Quality-of-Life +additions. + +You have a few options to pass `enum_set_DEVELOPER_MODE` to the configure +command, but this project prefers to use presets. + +As a developer, you should create a `CMakeUserPresets.json` file at the root of +the project: + +```json +{ + "version": 1, + "cmakeMinimumRequired": { + "major": 3, + "minor": 14, + "patch": 0 + }, + "configurePresets": [ + { + "name": "dev-base", + "hidden": true, + "inherits": ["dev-mode", "vcpkg", "ci-"] + }, + { + "name": "dev", + "buildDir": "${sourceDir}/build/dev", + "inherits": "dev-base", + "cacheVariables": { + "VCPKG_MANIFEST_FEATURES": "test" + } + }, + { + "name": "dev-magic_enum", + "buildDir": "${sourceDir}/build/dev-magic_enum", + "inherits": "dev-base", + "cacheVariables": { + "VCPKG_MANIFEST_FEATURES": "test;magic-enum" + } + } + ] +} +``` + +You should replace `` in your newly created presets file with the name of +the operating system you have, which may be `win64` or `unix`. You can see what +these correspond to in the [`CMakePresets.json`](CMakePresets.json) file. + +`CMakeUserPresets.json` is also the perfect place in which you can put all +sorts of things that you would otherwise want to pass to the configure command +in the terminal. + +### Configure, build and test + +To build the project as a developer, please make sure you have [vcpkg][3] +installed and properly set up at least with the `VCPKG_ROOT` env var pointing +to the right place, because some dependencies are provided with ports in the +project. + +If you followed the above instructions, then you can configure, build and test +the project respectively with the following commands from the project root on +Windows: + +```sh +cmake --preset=dev +cmake --build build --config Release +ctest --test-dir build -C Release +``` + +And here is the same on a Unix based system (Linux, macOS): + +```sh +cmake --preset=dev +cmake --build build +ctest --test-dir build +``` + +[1]: https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html +[2]: https://cmake.org/download/ +[3]: https://github.com/microsoft/vcpkg diff --git a/Makefile b/Makefile deleted file mode 100644 index 04692e2..0000000 --- a/Makefile +++ /dev/null @@ -1,78 +0,0 @@ -CXXFLAGS := -Wall -Wextra -Wpedantic -Werror - -sources := $(wildcard include/enum_set/*.hpp) -example_srcs := $(wildcard example/*.cpp) -example_exes := $(patsubst example/%.cpp,build/%.exe,$(example_srcs)) build/magic_enum_set_example.exe -test_sources := $(wildcard test/*.cpp) -test_cov_objs := $(patsubst test/%.cpp,build/%.coverage.o,$(test_sources)) build/test_magic.coverage.o -test_san_objs := $(patsubst test/%.cpp,build/%.sanitize.o,$(test_sources)) build/test_magic.sanitize.o -test_exes := build/test_coverage.exe build/test_sanitize.exe -base_flags := $(CXXFLAGS) -O0 -g3 -Iinclude -test_flags := $(base_flags) -Itest -coverage_flags := $(test_flags) --coverage -sanitize_flags := $(test_flags) -fsanitize-address-use-after-scope -fsanitize=address,undefined -report_flags := -r include/ --object-directory . --exclude-throw-branches --exclude-unreachable-branches --use-gcov-files - -all: $(test_exes) $(example_exes) - -build/test_coverage.exe: $(test_cov_objs) | build - $(CXX) -std=c++14 $(coverage_flags) $^ -o $@ - -build/test_sanitize.exe: $(test_san_objs) | build - $(CXX) -std=c++14 $(sanitize_flags) $^ -o $@ - -build/%.coverage.o: test/%.cpp $(sources) | build - $(CXX) -std=c++14 $(coverage_flags) -c $< -o $@ - -build/%.sanitize.o: test/%.cpp $(sources) | build - $(CXX) -std=c++14 $(sanitize_flags) -c $< -o $@ - -build/test_magic.coverage.o: test/magic/test_magic_enum_set.cpp include/enum_set/magic/magic_enum_set.hpp | build - $(CXX) -std=c++17 $(coverage_flags) -c $< -o $@ - -build/test_magic.sanitize.o: test/magic/test_magic_enum_set.cpp include/enum_set/magic/magic_enum_set.hpp | build - $(CXX) -std=c++17 $(sanitize_flags) -c $< -o $@ - -build/%.exe: example/%.cpp $(sources) | build - $(CXX) -std=c++14 $(base_flags) $< -o $@ - -build/magic_enum_set_example.exe: example/magic/magic_enum_set_example.cpp $(sources) | build - $(CXX) -std=c++17 $(base_flags) $< -o $@ - -build: - mkdir build - -.PHONY: analyze test_coverage test_sanitize test cover report install clean docker_build docker_run - -analyze: - cppcheck --bug-hunting --clang=$(shell which clang) -I ./include/ ./include/enum_set/* - -test_coverage: ./build/test_coverage.exe - ./build/test_coverage.exe - -test_sanitize: ./build/test_sanitize.exe - ./build/test_sanitize.exe - -test: test_coverage test_sanitize - -cover: test_coverage - gcov -b -c -m -r ./build/*.coverage.o - -report: cover - gcovr $(report_flags) --keep - gcovr $(report_flags) --keep --branches - gcovr $(report_flags) --html-details -o report.html - -install: - cp -r include/* /usr/local/include/ - -docker_build: - docker build -t cpp_enum_set:latest . - -docker_run: - docker run --rm -it -v $(shell pwd):/cpp_enum_set -w /cpp_enum_set cpp_enum_set:latest - -clean: - rm -rf build - rm -f *.gcov *.gcda *.gcno - rm -f report.html report.*.html diff --git a/cmake/docs.cmake b/cmake/docs.cmake new file mode 100644 index 0000000..49cb0c7 --- /dev/null +++ b/cmake/docs.cmake @@ -0,0 +1,39 @@ +# ---- Dependencies ---- + +find_package(Python3 REQUIRED) + +include(FetchContent) +FetchContent_Declare( + m.css + GIT_REPOSITORY "https://github.com/mosra/m.css.git" + GIT_TAG 9385194fa3392a7162e7535cc2478814e382ff8a + UPDATE_DISCONNECTED YES +) +FetchContent_MakeAvailable(m.css) + +# ---- Declare documentation target ---- + +set( + DOXYGEN_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/docs" + CACHE PATH "Path for the generated Doxygen documentation" +) + +set(working_dir "${PROJECT_BINARY_DIR}/docs") + +foreach(file IN ITEMS Doxyfile conf.py) + configure_file( + "${PROJECT_SOURCE_DIR}/docs/${file}.in" "${working_dir}/${file}" + @ONLY + ) +endforeach() + +set(mcss_script "${m.css_SOURCE_DIR}/documentation/doxygen.py") +set(config "${working_dir}/conf.py") + +add_custom_target( + docs + COMMAND "${Python3_EXECUTABLE}" "${mcss_script}" "${config}" + COMMENT "Building documentation using Doxygen and m.css" + WORKING_DIRECTORY "${working_dir}" + VERBATIM +) diff --git a/cmake/enum_setConfig.cmake b/cmake/enum_setConfig.cmake new file mode 100644 index 0000000..04dde84 --- /dev/null +++ b/cmake/enum_setConfig.cmake @@ -0,0 +1,25 @@ +# When installed with magic_enum support, the logic here allows the lack of +# magic_enum to gracefully fall back to not using it and operate only in C++14 +# mode, without the magic_enum dependency + +set(enum_set_HAS_MAGIC_ENUM_SUPPORT "@enum_set_USE_MAGIC_ENUM@") + +if(enum_set_HAS_MAGIC_ENUM_SUPPORT) + include(CMakeFindDependencyMacro) + find_dependency(magic_enum) +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/enum_setTargets.cmake") + +if(enum_set_HAS_MAGIC_ENUM_SUPPORT AND magic_enum_FOUND) + set_target_properties( + enum_set::enum_set PROPERTIES + INTERFACE_COMPILE_FEATURES cxx_std_17 + INTERFACE_LINK_LIBRARIES magic_enum::magic_enum + ) +else() + set_property( + TARGET enum_set::enum_set PROPERTY + INTERFACE_COMPILE_FEATURES cxx_std_14 + ) +endif() diff --git a/cmake/in-source-guard.cmake b/cmake/in-source-guard.cmake new file mode 100644 index 0000000..d33e2e4 --- /dev/null +++ b/cmake/in-source-guard.cmake @@ -0,0 +1,8 @@ +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) + message( + FATAL_ERROR + "In-source builds are not supported. " + "Make a new directory (e.g., 'build/'), and run CMake from there. " + "You may need to delete 'CMakeCache.txt' and 'CMakeFiles/' first." + ) +endif() diff --git a/cmake/install-rules.cmake b/cmake/install-rules.cmake new file mode 100644 index 0000000..aad5bfa --- /dev/null +++ b/cmake/install-rules.cmake @@ -0,0 +1,62 @@ +if(PROJECT_IS_TOP_LEVEL) + set(CMAKE_INSTALL_INCLUDEDIR include/enum_set CACHE PATH "") +endif() + +include(CMakePackageConfigHelpers) +include(GNUInstallDirs) + +set(filter PATTERN magic EXCLUDE) +if(enum_set_USE_MAGIC_ENUM) + set(filter "") +endif() + +install( + DIRECTORY "${PROJECT_SOURCE_DIR}/include/" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + COMPONENT enum_set_Development + ${filter} +) + +install( + TARGETS enum_set_enum_set + EXPORT enum_setTargets + INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" +) + +write_basic_package_version_file( + enum_setConfigVersion.cmake + COMPATIBILITY SameMajorVersion + ARCH_INDEPENDENT +) + +configure_file( + "${PROJECT_SOURCE_DIR}/cmake/enum_setConfig.cmake" + "${PROJECT_BINARY_DIR}/enum_setConfig.cmake" + @ONLY +) + +# Allow package maintainers to freely override the path for the configs +set( + enum_set_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/enum_set" + CACHE STRING "CMake package config location relative to the install prefix" +) +mark_as_advanced(enum_set_INSTALL_CMAKEDIR) + +install( + FILES + "${PROJECT_BINARY_DIR}/enum_setConfig.cmake" + "${PROJECT_BINARY_DIR}/enum_setConfigVersion.cmake" + DESTINATION "${enum_set_INSTALL_CMAKEDIR}" + COMPONENT enum_set_Development +) + +install( + EXPORT enum_setTargets + NAMESPACE enum_set:: + DESTINATION "${enum_set_INSTALL_CMAKEDIR}" + COMPONENT enum_set_Development +) + +if(PROJECT_IS_TOP_LEVEL) + include(CPack) +endif() diff --git a/cmake/project-is-top-level.cmake b/cmake/project-is-top-level.cmake new file mode 100644 index 0000000..3435fc0 --- /dev/null +++ b/cmake/project-is-top-level.cmake @@ -0,0 +1,6 @@ +# This variable is set by project() in CMake 3.21+ +string( + COMPARE EQUAL + "${CMAKE_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}" + PROJECT_IS_TOP_LEVEL +) diff --git a/cmake/variables.cmake b/cmake/variables.cmake new file mode 100644 index 0000000..74ad619 --- /dev/null +++ b/cmake/variables.cmake @@ -0,0 +1,66 @@ +# ---- Developer mode ---- + +# Developer mode enables targets and code paths in the CMake scripts that are +# only relevant for the developer(s) of enum_set +# Targets necessary to build the project must be provided unconditionally, so +# consumers can trivially build and package the project +if(PROJECT_IS_TOP_LEVEL) + option(enum_set_DEVELOPER_MODE "Enable developer mode" OFF) +endif() + +# ---- Warning guard ---- + +# target_include_directories with the SYSTEM modifier will request the compiler +# to omit warnings from the provided paths, if the compiler supports that +# This is to provide a user experience similar to find_package when +# add_subdirectory or FetchContent is used to consume this project +set(enum_set_warning_guard "") +if(NOT PROJECT_IS_TOP_LEVEL) + option( + enum_set_INCLUDES_WITH_SYSTEM + "Use SYSTEM modifier for enum_set's includes, disabling warnings" + ON + ) + mark_as_advanced(enum_set_INCLUDES_WITH_SYSTEM) + if(enum_set_INCLUDES_WITH_SYSTEM) + set(enum_set_warning_guard SYSTEM) + endif() +endif() + +# ---- magic_enum ---- + +# Detect the presence of magic_enum if the variable wasn't defined by the user +# already, so the variable can be automatically set in CI +set(value OFF) +if(NOT DEFINED enum_set_USE_MAGIC_ENUM) + find_package(magic_enum QUIET) + if(magic_enum_FOUND) + set(value ON) + message( + STATUS + "enum_set_USE_MAGIC_ENUM was not defined and magic_enum was found." + " Building with magic_enum support." + ) + else() + message( + STATUS + "enum_set_USE_MAGIC_ENUM was not defined and magic_enum was not found." + " Building without magic_enum support." + ) + endif() +endif() + +option( + enum_set_USE_MAGIC_ENUM + "Enable usage of magic_enum. Requires C++17" + "${value}" +) + +# If this project is vendored, then propagate this variable like the installed +# package does +if(NOT PROJECT_IS_TOP_LEVEL) + set( + enum_set_HAS_MAGIC_ENUM_SUPPORT + "${enum_set_USE_MAGIC_ENUM}" PARENT_SCOPE + ) +endif() diff --git a/cmake/windows-set-path.cmake b/cmake/windows-set-path.cmake new file mode 100644 index 0000000..d527c81 --- /dev/null +++ b/cmake/windows-set-path.cmake @@ -0,0 +1,22 @@ +# This function will add shared libraries to the PATH when running the test, so +# they can be found. Windows does not support RPATH or similar. See: +# https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order +# Usage: windows_set_path( ...) +function(windows_set_path TEST) + if(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + return() + endif() + + set(path "") + set(glue "") + foreach(target IN LISTS ARGN) + get_target_property(type "${target}" TYPE) + if(type STREQUAL "SHARED_LIBRARY") + set(path "${path}${glue}$") + set(glue "\;") # backslash is important + endif() + endforeach() + if(NOT path STREQUAL "") + set_property(TEST "${TEST}" PROPERTY ENVIRONMENT "PATH=${path}") + endif() +endfunction() diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in new file mode 100644 index 0000000..74763b2 --- /dev/null +++ b/docs/Doxyfile.in @@ -0,0 +1,31 @@ +# Configuration for Doxygen for use with CMake +# Only options that deviate from the default are included +# To create a new Doxyfile containing all available options, call `doxygen -g` + +# Get Project name and version from CMake +PROJECT_NAME = "@PROJECT_NAME@" +PROJECT_NUMBER = "@PROJECT_VERSION@" + +# Add sources +INPUT = "@PROJECT_SOURCE_DIR@/README.md" "@PROJECT_SOURCE_DIR@/include" "@PROJECT_SOURCE_DIR@/docs/pages" +EXTRACT_ALL = YES +RECURSIVE = YES +OUTPUT_DIRECTORY = "@DOXYGEN_OUTPUT_DIRECTORY@" + +# Use the README as a main page +USE_MDFILE_AS_MAINPAGE = "@PROJECT_SOURCE_DIR@/README.md" + +# set relative include paths +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = "@PROJECT_SOURCE_DIR@/include" "@PROJECT_SOURCE_DIR@" + +# We use m.css to generate the html documentation, so we only need XML output +GENERATE_XML = YES +GENERATE_HTML = NO +GENERATE_LATEX = NO +XML_PROGRAMLISTING = NO +CREATE_SUBDIRS = NO + +# Include all directories, files and namespaces in the documentation +# Disable to include only explicitly documented objects +M_SHOW_UNDOCUMENTED = YES diff --git a/docs/conf.py.in b/docs/conf.py.in new file mode 100644 index 0000000..b81e3d9 --- /dev/null +++ b/docs/conf.py.in @@ -0,0 +1,6 @@ +DOXYFILE = 'Doxyfile' + +LINKS_NAVBAR1 = [ + (None, 'pages', [(None, 'about')]), + (None, 'namespaces', []), +] diff --git a/docs/pages/about.dox b/docs/pages/about.dox new file mode 100644 index 0000000..2efbda9 --- /dev/null +++ b/docs/pages/about.dox @@ -0,0 +1,7 @@ +/** + * @page about About + * @section about-doxygen Doxygen documentation + * This page is auto generated using + * Doxygen, making use of some useful + * special commands. + */ diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 0000000..14277ae --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 3.14) + +project(enum_setExamples CXX) + +include(../cmake/project-is-top-level.cmake) + +if(PROJECT_IS_TOP_LEVEL) + find_package(enum_set REQUIRED) +endif() + +add_custom_target(run_examples) + +function(add_example NAME) + cmake_parse_arguments(PARSE_ARGV 1 "" "" "" "SOURCES;LIBS") + if("${_SOURCES}" STREQUAL "") + set(_SOURCES "${NAME}.cpp") + endif() + add_executable("${NAME}" ${_SOURCES}) + target_link_libraries("${NAME}" PRIVATE enum_set::enum_set ${_LIBS}) + add_custom_target( + "run_${NAME}" + COMMAND "$" + VERBATIM + ) + add_dependencies(run_examples "run_${NAME}") +endfunction() + +add_example(basic_tutorial) +add_example(rationale) +add_example(visitation_example) +# Transitive dependency we get from the find_dependency() command +if(TARGET magic_enum::magic_enum) + add_example( + magic_enum_set_example + SOURCES "${PROJECT_SOURCE_DIR}/magic/magic_enum_set_example.cpp" + LIBS magic_enum::magic_enum + ) +endif() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..c0061cf --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.14) + +project(enum_setTests LANGUAGES CXX) + +include(../cmake/project-is-top-level.cmake) + +if(PROJECT_IS_TOP_LEVEL) + find_package(enum_set REQUIRED) + enable_testing() +endif() + +find_package(doctest REQUIRED) +include(doctest) + +function(create_test NAME) + cmake_parse_arguments(PARSE_ARGV 1 "" "" "" "SOURCES;LIBS") + if("${_SOURCES}" STREQUAL "") + set(_SOURCES "${NAME}.cpp") + endif() + add_executable("${NAME}" ${_SOURCES}) + target_include_directories("${NAME}" PRIVATE "${PROJECT_SOURCE_DIR}/include") + target_compile_definitions( + "${NAME}" + PRIVATE + DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN + ) + target_link_libraries( + "${NAME}" + PRIVATE + enum_set::enum_set + doctest::doctest + ${_LIBS} + ) + doctest_discover_tests("${NAME}") +endfunction() + +create_test(test_bit_mask) +create_test(test_common) +create_test(test_enum_set) +create_test(test_index_set) +create_test(test_iterator) +create_test(test_type_set) +create_test(test_value_set) +# Transitive dependency we get from the find_dependency() command +if(TARGET magic_enum::magic_enum) + create_test( + test_magic_enum_set + SOURCES "${PROJECT_SOURCE_DIR}/magic/test_magic_enum_set.cpp" + LIBS magic_enum::magic_enum + ) +endif() diff --git a/test/testing.hpp b/test/include/testing.hpp similarity index 100% rename from test/testing.hpp rename to test/include/testing.hpp diff --git a/test/main.cpp b/test/main.cpp deleted file mode 100644 index 0a3f254..0000000 --- a/test/main.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN -#include diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..2636863 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,15 @@ +{ + "name": "enum-set", + "version-semver": "0.1.0", + "default-features": [], + "features": { + "magic-enum": { + "description": "Build with magic_enum", + "dependencies": ["magic-enum"] + }, + "test": { + "description": "Build tests", + "dependencies": ["doctest"] + } + } +}