commit 3413c94f8d8aaa7e763439069f90d90f09c974b7 Author: ChronosXYZ Date: Tue May 14 17:46:35 2024 +0300 first commit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1c26303 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,194 @@ +name: Continuous Integration + +on: + push: + branches: + - master + + pull_request: + branches: + - master + +jobs: + lint: + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: { python-version: "3.8" } + + - name: Install codespell + run: pip3 install codespell + + - name: Lint + run: cmake -D FORMAT_COMMAND=clang-format-14 -P cmake/lint.cmake + + - name: Spell check + if: always() + run: cmake -P cmake/spell.cmake + + coverage: + needs: [lint] + + runs-on: ubuntu-22.04 + + # To enable coverage, delete the last line from the conditional below and + # edit the "" placeholder to your GitHub name. + # If you do not wish to use codecov, then simply delete this job from the + # workflow. + if: github.repository_owner == '' + && false + + steps: + - uses: actions/checkout@v4 + + - name: Install LCov + run: sudo apt-get update -q + && sudo apt-get install lcov -q -y + + - name: Configure + run: cmake --preset=ci-coverage + + - name: Build + run: cmake --build build/coverage -j 2 + + - name: Test + working-directory: build/coverage + run: ctest --output-on-failure --no-tests=error -j 2 + + - name: Process coverage info + run: cmake --build build/coverage -t coverage + + - name: Submit to codecov.io + uses: codecov/codecov-action@v4 + with: + file: build/coverage/coverage.info + token: ${{ secrets.CODECOV_TOKEN }} + + sanitize: + needs: [lint] + + runs-on: ubuntu-22.04 + + env: { CXX: clang++-14 } + + steps: + - uses: actions/checkout@v4 + + - name: Configure + run: cmake --preset=ci-sanitize + + - name: Build + run: cmake --build build/sanitize -j 2 + + - name: Test + working-directory: build/sanitize + env: + ASAN_OPTIONS: "strict_string_checks=1:\ + detect_stack_use_after_return=1:\ + check_initialization_order=1:\ + strict_init_order=1:\ + detect_leaks=1:\ + halt_on_error=1" + UBSAN_OPTIONS: "print_stacktrace=1:\ + halt_on_error=1" + run: ctest --output-on-failure --no-tests=error -j 2 + + test: + needs: [lint] + + strategy: + matrix: + os: [macos-12, ubuntu-22.04, windows-2022] + + type: [shared, static] + + include: + - { type: shared, shared: YES } + - { type: static, shared: NO } + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v4 + + - name: Install static analyzers + if: matrix.os == 'ubuntu-22.04' + run: >- + sudo apt-get install clang-tidy-14 cppcheck -y -q + + sudo update-alternatives --install + /usr/bin/clang-tidy clang-tidy + /usr/bin/clang-tidy-14 140 + + - name: Setup MultiToolTask + if: matrix.os == 'windows-2022' + run: | + Add-Content "$env:GITHUB_ENV" 'UseMultiToolTask=true' + Add-Content "$env:GITHUB_ENV" 'EnforceProcessCountAcrossBuilds=true' + + - name: Configure + shell: pwsh + run: cmake "--preset=ci-$("${{ matrix.os }}".split("-")[0])" + -D BUILD_SHARED_LIBS=${{ matrix.shared }} + + - name: Setup PATH + if: matrix.os == 'windows-2022' && matrix.type == 'shared' + run: Add-Content "$env:GITHUB_PATH" "$(Get-Location)\build\Release" + + - name: Build + run: cmake --build build --config Release -j 2 + + - name: Install + run: cmake --install build --config Release --prefix prefix + + - name: Test + working-directory: build + run: ctest --output-on-failure --no-tests=error -C Release -j 2 + + docs: + # Deploy docs only when builds succeed + needs: [sanitize, test] + + runs-on: ubuntu-22.04 + + # To enable, first you have to create an orphaned gh-pages branch: + # + # git switch --orphan gh-pages + # git commit --allow-empty -m "Initial commit" + # git push -u origin gh-pages + # + # Edit the placeholder below to your GitHub name, so this action + # runs only in your repository and no one else's fork. After these, delete + # this comment and the last line in the conditional below. + # If you do not wish to use GitHub Pages for deploying documentation, then + # simply delete this job similarly to the coverage one. + if: github.ref == 'refs/heads/master' + && github.event_name == 'push' + && github.repository_owner == '' + && false + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: { python-version: "3.8" } + + - name: Install m.css dependencies + run: pip3 install jinja2 Pygments + + - name: Install Doxygen + run: sudo apt-get update -q + && sudo apt-get install doxygen -q -y + + - name: Build docs + run: cmake "-DPROJECT_SOURCE_DIR=$PWD" "-DPROJECT_BINARY_DIR=$PWD/build" + -P cmake/docs-ci.cmake + + - name: Deploy docs + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: build/docs/html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b033b8d --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +**/.DS_Store +.idea/ +.vs/ +.vscode/ +build/ +cmake-build-*/ +prefix/ +.clangd +CMakeLists.txt.user +CMakeUserPresets.json +compile_commands.json +env.bat +env.ps1 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4fde530 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,82 @@ +cmake_minimum_required(VERSION 3.14) + +include(cmake/prelude.cmake) + +project( + nix-gcc-multi-issue + VERSION 0.1.0 + DESCRIPTION "Short description" + HOMEPAGE_URL "https://example.com/" + LANGUAGES CXX +) + +include(cmake/project-is-top-level.cmake) +include(cmake/variables.cmake) + +# ---- Declare library ---- +# build as 32-bit library +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32") +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +add_library( + nix-gcc-multi-issue_nix-gcc-multi-issue + SHARED + source/nix-gcc-multi-issue.cpp +) +add_library(nix-gcc-multi-issue::nix-gcc-multi-issue ALIAS nix-gcc-multi-issue_nix-gcc-multi-issue) + +include(GenerateExportHeader) +generate_export_header( + nix-gcc-multi-issue_nix-gcc-multi-issue + BASE_NAME nix-gcc-multi-issue + EXPORT_FILE_NAME export/nix-gcc-multi-issue/nix-gcc-multi-issue_export.hpp + CUSTOM_CONTENT_FROM_VARIABLE pragma_suppress_c4251 +) + +if(NOT BUILD_SHARED_LIBS) + target_compile_definitions(nix-gcc-multi-issue_nix-gcc-multi-issue PUBLIC NIX_GCC_MULTI_ISSUE_STATIC_DEFINE) +endif() + +set_target_properties( + nix-gcc-multi-issue_nix-gcc-multi-issue PROPERTIES + CXX_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN YES + VERSION "${PROJECT_VERSION}" + SOVERSION "${PROJECT_VERSION_MAJOR}" + EXPORT_NAME nix-gcc-multi-issue + OUTPUT_NAME nix-gcc-multi-issue +) + +target_include_directories( + nix-gcc-multi-issue_nix-gcc-multi-issue ${warning_guard} + PUBLIC + "\$" +) + +target_include_directories( + nix-gcc-multi-issue_nix-gcc-multi-issue SYSTEM + PUBLIC + "\$" +) + +target_compile_features(nix-gcc-multi-issue_nix-gcc-multi-issue PUBLIC cxx_std_17) + +# ---- Install rules ---- + +if(NOT CMAKE_SKIP_INSTALL_RULES) + include(cmake/install-rules.cmake) +endif() + +# ---- Developer mode ---- + +if(NOT nix-gcc-multi-issue_DEVELOPER_MODE) + return() +elseif(NOT PROJECT_IS_TOP_LEVEL) + message( + AUTHOR_WARNING + "Developer mode is intended for developers of nix-gcc-multi-issue" + ) +endif() + +include(cmake/dev-mode.cmake) diff --git a/README.md b/README.md new file mode 100644 index 0000000..1c13bed --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# nix-gcc-multi-issue + +This is the nix-gcc-multi-issue project. + +# Building and installing + +See the [BUILDING](BUILDING.md) document. + +# Contributing + +See the [CONTRIBUTING](CONTRIBUTING.md) document. + +# Licensing + + diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..83f652b --- /dev/null +++ b/build.sh @@ -0,0 +1,15 @@ +#!/bin/sh +[ -z $CONFIG ] && config=Release || config="$CONFIG" + +if [ "$SKIP_CMAKE" != "true" ]; then + cmake \ + -S . \ + -B build \ + -G Ninja \ + -DCMAKE_BUILD_TYPE=$config +fi + +cmake \ + --build build \ + --config $config \ + --parallel $(nproc) diff --git a/cmake/coverage.cmake b/cmake/coverage.cmake new file mode 100644 index 0000000..c89cc16 --- /dev/null +++ b/cmake/coverage.cmake @@ -0,0 +1,33 @@ +# ---- Variables ---- + +# We use variables separate from what CTest uses, because those have +# customization issues +set( + COVERAGE_TRACE_COMMAND + lcov -c -q + -o "${PROJECT_BINARY_DIR}/coverage.info" + -d "${PROJECT_BINARY_DIR}" + --include "${PROJECT_SOURCE_DIR}/*" + CACHE STRING + "; separated command to generate a trace for the 'coverage' target" +) + +set( + COVERAGE_HTML_COMMAND + genhtml --legend -f -q + "${PROJECT_BINARY_DIR}/coverage.info" + -p "${PROJECT_SOURCE_DIR}" + -o "${PROJECT_BINARY_DIR}/coverage_html" + CACHE STRING + "; separated command to generate an HTML report for the 'coverage' target" +) + +# ---- Coverage target ---- + +add_custom_target( + coverage + COMMAND ${COVERAGE_TRACE_COMMAND} + COMMAND ${COVERAGE_HTML_COMMAND} + COMMENT "Generating coverage report" + VERBATIM +) diff --git a/cmake/dev-mode.cmake b/cmake/dev-mode.cmake new file mode 100644 index 0000000..0011f5c --- /dev/null +++ b/cmake/dev-mode.cmake @@ -0,0 +1,21 @@ +include(cmake/folders.cmake) + +include(CTest) +if(BUILD_TESTING) + add_subdirectory(test) +endif() + +option(BUILD_MCSS_DOCS "Build documentation using Doxygen and m.css" OFF) +if(BUILD_MCSS_DOCS) + include(cmake/docs.cmake) +endif() + +option(ENABLE_COVERAGE "Enable coverage support separate from CTest's" OFF) +if(ENABLE_COVERAGE) + include(cmake/coverage.cmake) +endif() + +include(cmake/lint-targets.cmake) +include(cmake/spell-targets.cmake) + +add_folders(Project) diff --git a/cmake/docs-ci.cmake b/cmake/docs-ci.cmake new file mode 100644 index 0000000..ae7f0c7 --- /dev/null +++ b/cmake/docs-ci.cmake @@ -0,0 +1,112 @@ +cmake_minimum_required(VERSION 3.14) + +foreach(var IN ITEMS PROJECT_BINARY_DIR PROJECT_SOURCE_DIR) + if(NOT DEFINED "${var}") + message(FATAL_ERROR "${var} must be defined") + endif() +endforeach() +set(bin "${PROJECT_BINARY_DIR}") +set(src "${PROJECT_SOURCE_DIR}") + +# ---- Dependencies ---- + +set(mcss_SOURCE_DIR "${bin}/docs/.ci") +if(NOT IS_DIRECTORY "${mcss_SOURCE_DIR}") + file(MAKE_DIRECTORY "${mcss_SOURCE_DIR}") + file( + DOWNLOAD + https://github.com/friendlyanon/m.css/releases/download/release-1/mcss.zip + "${mcss_SOURCE_DIR}/mcss.zip" + STATUS status + EXPECTED_MD5 00cd2757ebafb9bcba7f5d399b3bec7f + ) + if(NOT status MATCHES "^0;") + message(FATAL_ERROR "Download failed with ${status}") + endif() + execute_process( + COMMAND "${CMAKE_COMMAND}" -E tar xf mcss.zip + WORKING_DIRECTORY "${mcss_SOURCE_DIR}" + RESULT_VARIABLE result + ) + if(NOT result EQUAL "0") + message(FATAL_ERROR "Extraction failed with ${result}") + endif() + file(REMOVE "${mcss_SOURCE_DIR}/mcss.zip") +endif() + +find_program(Python3_EXECUTABLE NAMES python3 python) +if(NOT Python3_EXECUTABLE) + message(FATAL_ERROR "Python executable was not found") +endif() + +# ---- Process project() call in CMakeLists.txt ---- + +file(READ "${src}/CMakeLists.txt" content) + +string(FIND "${content}" "project(" index) +if(index EQUAL "-1") + message(FATAL_ERROR "Could not find \"project(\"") +endif() +string(SUBSTRING "${content}" "${index}" -1 content) + +string(FIND "${content}" "\n)\n" index) +if(index EQUAL "-1") + message(FATAL_ERROR "Could not find \"\\n)\\n\"") +endif() +string(SUBSTRING "${content}" 0 "${index}" content) + +file(WRITE "${bin}/docs-ci.project.cmake" "docs_${content}\n)\n") + +macro(list_pop_front list out) + list(GET "${list}" 0 "${out}") + list(REMOVE_AT "${list}" 0) +endmacro() + +function(docs_project name) + cmake_parse_arguments(PARSE_ARGV 1 "" "" "VERSION;DESCRIPTION;HOMEPAGE_URL" LANGUAGES) + set(PROJECT_NAME "${name}" PARENT_SCOPE) + if(DEFINED _VERSION) + set(PROJECT_VERSION "${_VERSION}" PARENT_SCOPE) + string(REGEX MATCH "^[0-9]+(\\.[0-9]+)*" versions "${_VERSION}") + string(REPLACE . ";" versions "${versions}") + set(suffixes MAJOR MINOR PATCH TWEAK) + while(NOT versions STREQUAL "" AND NOT suffixes STREQUAL "") + list_pop_front(versions version) + list_pop_front(suffixes suffix) + set("PROJECT_VERSION_${suffix}" "${version}" PARENT_SCOPE) + endwhile() + endif() + if(DEFINED _DESCRIPTION) + set(PROJECT_DESCRIPTION "${_DESCRIPTION}" PARENT_SCOPE) + endif() + if(DEFINED _HOMEPAGE_URL) + set(PROJECT_HOMEPAGE_URL "${_HOMEPAGE_URL}" PARENT_SCOPE) + endif() +endfunction() + +include("${bin}/docs-ci.project.cmake") + +# ---- Generate docs ---- + +if(NOT DEFINED DOXYGEN_OUTPUT_DIRECTORY) + set(DOXYGEN_OUTPUT_DIRECTORY "${bin}/docs") +endif() +set(out "${DOXYGEN_OUTPUT_DIRECTORY}") + +foreach(file IN ITEMS Doxyfile conf.py) + configure_file("${src}/docs/${file}.in" "${bin}/docs/${file}" @ONLY) +endforeach() + +set(mcss_script "${mcss_SOURCE_DIR}/documentation/doxygen.py") +set(config "${bin}/docs/conf.py") + +file(REMOVE_RECURSE "${out}/html" "${out}/xml") + +execute_process( + COMMAND "${Python3_EXECUTABLE}" "${mcss_script}" "${config}" + WORKING_DIRECTORY "${bin}/docs" + RESULT_VARIABLE result +) +if(NOT result EQUAL "0") + message(FATAL_ERROR "m.css returned with ${result}") +endif() diff --git a/cmake/docs.cmake b/cmake/docs.cmake new file mode 100644 index 0000000..c6cdda6 --- /dev/null +++ b/cmake/docs.cmake @@ -0,0 +1,46 @@ +# ---- Dependencies ---- + +set(extract_timestamps "") +if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.24") + set(extract_timestamps DOWNLOAD_EXTRACT_TIMESTAMP YES) +endif() + +include(FetchContent) +FetchContent_Declare( + mcss URL + https://github.com/friendlyanon/m.css/releases/download/release-1/mcss.zip + URL_MD5 00cd2757ebafb9bcba7f5d399b3bec7f + SOURCE_DIR "${PROJECT_BINARY_DIR}/mcss" + UPDATE_DISCONNECTED YES + ${extract_timestamps} +) +FetchContent_MakeAvailable(mcss) + +find_package(Python3 3.6 REQUIRED) + +# ---- 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("docs/${file}.in" "${working_dir}/${file}" @ONLY) +endforeach() + +set(mcss_script "${mcss_SOURCE_DIR}/documentation/doxygen.py") +set(config "${working_dir}/conf.py") + +add_custom_target( + docs + COMMAND "${CMAKE_COMMAND}" -E remove_directory + "${DOXYGEN_OUTPUT_DIRECTORY}/html" + "${DOXYGEN_OUTPUT_DIRECTORY}/xml" + COMMAND "${Python3_EXECUTABLE}" "${mcss_script}" "${config}" + COMMENT "Building documentation using Doxygen and m.css" + WORKING_DIRECTORY "${working_dir}" + VERBATIM +) diff --git a/cmake/folders.cmake b/cmake/folders.cmake new file mode 100644 index 0000000..da7bd33 --- /dev/null +++ b/cmake/folders.cmake @@ -0,0 +1,21 @@ +set_property(GLOBAL PROPERTY USE_FOLDERS YES) + +# Call this function at the end of a directory scope to assign a folder to +# targets created in that directory. Utility targets will be assigned to the +# UtilityTargets folder, otherwise to the ${name}Targets folder. If a target +# already has a folder assigned, then that target will be skipped. +function(add_folders name) + get_property(targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS) + foreach(target IN LISTS targets) + get_property(folder TARGET "${target}" PROPERTY FOLDER) + if(DEFINED folder) + continue() + endif() + set(folder Utility) + get_property(type TARGET "${target}" PROPERTY TYPE) + if(NOT type STREQUAL "UTILITY") + set(folder "${name}") + endif() + set_property(TARGET "${target}" PROPERTY FOLDER "${folder}Targets") + endforeach() +endfunction() diff --git a/cmake/install-config.cmake b/cmake/install-config.cmake new file mode 100644 index 0000000..9152f97 --- /dev/null +++ b/cmake/install-config.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/nix-gcc-multi-issueTargets.cmake") diff --git a/cmake/install-rules.cmake b/cmake/install-rules.cmake new file mode 100644 index 0000000..fc9b942 --- /dev/null +++ b/cmake/install-rules.cmake @@ -0,0 +1,72 @@ +if(PROJECT_IS_TOP_LEVEL) + set( + CMAKE_INSTALL_INCLUDEDIR "include/nix-gcc-multi-issue-${PROJECT_VERSION}" + CACHE STRING "" + ) + set_property(CACHE CMAKE_INSTALL_INCLUDEDIR PROPERTY TYPE PATH) +endif() + +include(CMakePackageConfigHelpers) +include(GNUInstallDirs) + +# find_package() call for consumers to find this project +set(package nix-gcc-multi-issue) + +install( + DIRECTORY + include/ + "${PROJECT_BINARY_DIR}/export/" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + COMPONENT nix-gcc-multi-issue_Development +) + +install( + TARGETS nix-gcc-multi-issue_nix-gcc-multi-issue + EXPORT nix-gcc-multi-issueTargets + RUNTIME # + COMPONENT nix-gcc-multi-issue_Runtime + LIBRARY # + COMPONENT nix-gcc-multi-issue_Runtime + NAMELINK_COMPONENT nix-gcc-multi-issue_Development + ARCHIVE # + COMPONENT nix-gcc-multi-issue_Development + INCLUDES # + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" +) + +write_basic_package_version_file( + "${package}ConfigVersion.cmake" + COMPATIBILITY SameMajorVersion +) + +# Allow package maintainers to freely override the path for the configs +set( + nix-gcc-multi-issue_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${package}" + CACHE STRING "CMake package config location relative to the install prefix" +) +set_property(CACHE nix-gcc-multi-issue_INSTALL_CMAKEDIR PROPERTY TYPE PATH) +mark_as_advanced(nix-gcc-multi-issue_INSTALL_CMAKEDIR) + +install( + FILES cmake/install-config.cmake + DESTINATION "${nix-gcc-multi-issue_INSTALL_CMAKEDIR}" + RENAME "${package}Config.cmake" + COMPONENT nix-gcc-multi-issue_Development +) + +install( + FILES "${PROJECT_BINARY_DIR}/${package}ConfigVersion.cmake" + DESTINATION "${nix-gcc-multi-issue_INSTALL_CMAKEDIR}" + COMPONENT nix-gcc-multi-issue_Development +) + +install( + EXPORT nix-gcc-multi-issueTargets + NAMESPACE nix-gcc-multi-issue:: + DESTINATION "${nix-gcc-multi-issue_INSTALL_CMAKEDIR}" + COMPONENT nix-gcc-multi-issue_Development +) + +if(PROJECT_IS_TOP_LEVEL) + include(CPack) +endif() diff --git a/cmake/lint-targets.cmake b/cmake/lint-targets.cmake new file mode 100644 index 0000000..ad7bd9b --- /dev/null +++ b/cmake/lint-targets.cmake @@ -0,0 +1,33 @@ +set( + FORMAT_PATTERNS + source/*.cpp source/*.hpp + include/*.hpp + test/*.cpp test/*.hpp + CACHE STRING + "; separated patterns relative to the project source dir to format" +) + +set(FORMAT_COMMAND clang-format CACHE STRING "Formatter to use") + +add_custom_target( + format-check + COMMAND "${CMAKE_COMMAND}" + -D "FORMAT_COMMAND=${FORMAT_COMMAND}" + -D "PATTERNS=${FORMAT_PATTERNS}" + -P "${PROJECT_SOURCE_DIR}/cmake/lint.cmake" + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + COMMENT "Linting the code" + VERBATIM +) + +add_custom_target( + format-fix + COMMAND "${CMAKE_COMMAND}" + -D "FORMAT_COMMAND=${FORMAT_COMMAND}" + -D "PATTERNS=${FORMAT_PATTERNS}" + -D FIX=YES + -P "${PROJECT_SOURCE_DIR}/cmake/lint.cmake" + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + COMMENT "Fixing the code" + VERBATIM +) diff --git a/cmake/lint.cmake b/cmake/lint.cmake new file mode 100644 index 0000000..a05069b --- /dev/null +++ b/cmake/lint.cmake @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.14) + +macro(default name) + if(NOT DEFINED "${name}") + set("${name}" "${ARGN}") + endif() +endmacro() + +default(FORMAT_COMMAND clang-format) +default( + PATTERNS + source/*.cpp source/*.hpp + include/*.hpp + test/*.cpp test/*.hpp +) +default(FIX NO) + +set(flag --output-replacements-xml) +set(args OUTPUT_VARIABLE output) +if(FIX) + set(flag -i) + set(args "") +endif() + +file(GLOB_RECURSE files ${PATTERNS}) +set(badly_formatted "") +set(output "") +string(LENGTH "${CMAKE_SOURCE_DIR}/" path_prefix_length) + +foreach(file IN LISTS files) + execute_process( + COMMAND "${FORMAT_COMMAND}" --style=file "${flag}" "${file}" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + RESULT_VARIABLE result + ${args} + ) + if(NOT result EQUAL "0") + message(FATAL_ERROR "'${file}': formatter returned with ${result}") + endif() + if(NOT FIX AND output MATCHES "\n + +#include "nix-gcc-multi-issue/nix-gcc-multi-issue_export.hpp" + +/** + * A note about the MSVC warning C4251: + * This warning should be suppressed for private data members of the project's + * exported classes, because there are too many ways to work around it and all + * involve some kind of trade-off (increased code complexity requiring more + * developer time, writing boilerplate code, longer compile times), but those + * solutions are very situational and solve things in slightly different ways, + * depending on the requirements of the project. + * That is to say, there is no general solution. + * + * What can be done instead is understand where issues could arise where this + * warning is spotting a legitimate bug. I will give the general description of + * this warning's cause and break it down to make it trivial to understand. + * + * C4251 is emitted when an exported class has a non-static data member of a + * non-exported class type. + * + * The exported class in our case is the class below (exported_class), which + * has a non-static data member (m_name) of a non-exported class type + * (std::string). + * + * The rationale here is that the user of the exported class could attempt to + * access (directly, or via an inline member function) a static data member or + * a non-inline member function of the data member, resulting in a linker + * error. + * Inline member function above means member functions that are defined (not + * declared) in the class definition. + * + * Since this exported class never makes these non-exported types available to + * the user, we can safely ignore this warning. It's fine if there are + * non-exported class types as private member variables, because they are only + * accessed by the members of the exported class itself. + * + * The name() method below returns a pointer to the stored null-terminated + * string as a fundamental type (char const), so this is safe to use anywhere. + * The only downside is that you can have dangling pointers if the pointer + * outlives the class instance which stored the string. + * + * Shared libraries are not easy, they need some discipline to get right, but + * they also solve some other problems that make them worth the time invested. + */ + +/** + * @brief Reports the name of the library + * + * Please see the note above for considerations when creating shared libraries. + */ +class NIX_GCC_MULTI_ISSUE_EXPORT exported_class +{ +public: + /** + * @brief Initializes the name field to the name of the project + */ + exported_class(); + + /** + * @brief Returns a non-owning pointer to the string stored in this class + */ + auto name() const -> char const*; + +private: + NIX_GCC_MULTI_ISSUE_SUPPRESS_C4251 + std::string m_name; +}; diff --git a/source/nix-gcc-multi-issue.cpp b/source/nix-gcc-multi-issue.cpp new file mode 100644 index 0000000..2a3095d --- /dev/null +++ b/source/nix-gcc-multi-issue.cpp @@ -0,0 +1,15 @@ +#include +#include + +#include "nix-gcc-multi-issue/nix-gcc-multi-issue.hpp" + +exported_class::exported_class() + : m_name {"nix-gcc-multi-issue"} +{ + std::cout << "Hello, World!" << std::endl; +} + +auto exported_class::name() const -> char const* +{ + return m_name.c_str(); +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..01ff11b --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.14) + +project(nix-gcc-multi-issueTests LANGUAGES CXX) + +include(../cmake/project-is-top-level.cmake) +include(../cmake/folders.cmake) + +# ---- Dependencies ---- + +if(PROJECT_IS_TOP_LEVEL) + find_package(nix-gcc-multi-issue REQUIRED) + enable_testing() +endif() + +# ---- Tests ---- + +add_executable(nix-gcc-multi-issue_test source/nix-gcc-multi-issue_test.cpp) +target_link_libraries(nix-gcc-multi-issue_test PRIVATE nix-gcc-multi-issue::nix-gcc-multi-issue) +target_compile_features(nix-gcc-multi-issue_test PRIVATE cxx_std_17) + +add_test(NAME nix-gcc-multi-issue_test COMMAND nix-gcc-multi-issue_test) + +# ---- End-of-file commands ---- + +add_folders(Test) diff --git a/test/source/nix-gcc-multi-issue_test.cpp b/test/source/nix-gcc-multi-issue_test.cpp new file mode 100644 index 0000000..c3e6510 --- /dev/null +++ b/test/source/nix-gcc-multi-issue_test.cpp @@ -0,0 +1,10 @@ +#include + +#include "nix-gcc-multi-issue/nix-gcc-multi-issue.hpp" + +auto main() -> int +{ + auto const exported = exported_class {}; + + return std::string("nix-gcc-multi-issue") == exported.name() ? 0 : 1; +}