From eb38bfb070b1676c561ba6de67ad8263157155b1 Mon Sep 17 00:00:00 2001 From: Adrien Date: Thu, 4 Mar 2021 11:50:33 +0100 Subject: [PATCH 01/19] Rename tools subfolders, lower case first letter UpstreamTwo components --- README.adoc | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/README.adoc b/README.adoc index 4313544..e320824 100644 --- a/README.adoc +++ b/README.adoc @@ -127,12 +127,6 @@ It is not trivial to distribute a project that builds easily on all environments **** **** -=== Design - -.TODO -**** -**** - == The process @@ -249,8 +243,8 @@ Once defined which component(s) will be held inside a repository, the repository CMakeLists.txt (cmr) README.{xy} cmake/ -toolA/ -toolB/ +toolOne/ +toolTwo/ ... src/ CMakeLists.txt (cmp) @@ -703,7 +697,7 @@ set(${PROJECT_NAME}_SOURCES ) *find_package(UpstreamOne REQUIRED) -find_package(UpstreamTwo 1.0 REQUIRED COMPONENTS CompA CompB) +find_package(UpstreamTwo 1.0 REQUIRED COMPONENTS compA compB) find_package(UpstreamThree 3.2.5 EXACT REQUIRED)* # Creates the library target @@ -723,8 +717,8 @@ target_include_directories(${PROJECT_NAME} *target_link_libraries(${PROJECT_NAME} PUBLIC nsOne::UpstreamOne - nsTwo::CompA - nsTwo::CompB + nsTwo::compA + nsTwo::compB PRIVATE nsThree::UpstreamThree INTERFACE @@ -872,7 +866,7 @@ set(${PROJECT_NAME}_SOURCES ) find_package(UpstreamOne REQUIRED) -find_package(UpstreamTwo 1.0 REQUIRED COMPONENTS CompA CompB) +find_package(UpstreamTwo 1.0 REQUIRED COMPONENTS compA compB) find_package(UpstreamThree 3.2.5 EXACT REQUIRED) # Creates the library target @@ -892,8 +886,8 @@ target_include_directories(${PROJECT_NAME} target_link_libraries(${PROJECT_NAME} PUBLIC nsOne::UpstreamOne - nsTwo::CompA - nsTwo::CompB + nsTwo::compA + nsTwo::compB PRIVATE nsThree::UpstreamThree INTERFACE @@ -1072,7 +1066,7 @@ set(${PROJECT_NAME}_SOURCES ) find_package(UpstreamOne REQUIRED) -find_package(UpstreamTwo 1.0 REQUIRED COMPONENTS CompA CompB) +find_package(UpstreamTwo 1.0 REQUIRED COMPONENTS compA compB) find_package(UpstreamThree 3.2.5 EXACT REQUIRED) # Creates the library target @@ -1092,8 +1086,8 @@ target_include_directories(${PROJECT_NAME} target_link_libraries(${PROJECT_NAME} PUBLIC nsOne::UpstreamOne - nsTwo::CompA - nsTwo::CompB + nsTwo::compA + nsTwo::compB PRIVATE nsThree::UpstreamThree INTERFACE @@ -1161,7 +1155,7 @@ The calls to `find_package` are moved away from the leaf `CMakeLists.txt` to a c [source, cmake] ---- @find_package@(UpstreamOne @REQUIRED@) -@find_package@(UpstreamTwo 1.0 @REQUIRED@ COMPONENTS CompA CompB) +@find_package@(UpstreamTwo 1.0 @REQUIRED@ COMPONENTS compA compB) @find_package@(UpstreamThree 3.2.5 EXACT @REQUIRED@ @QUIET@) ---- @@ -1208,8 +1202,8 @@ The package developer must then once again take measures to make sure they are e target_link_libraries(${PROJECT_NAME} PUBLIC nsOne::UpstreamOne - nsTwo::CompA - nsTwo::CompB + nsTwo::compA + nsTwo::compB PRIVATE nsThree::UpstreamThree INTERFACE @@ -1353,8 +1347,8 @@ target_include_directories(${PROJECT_NAME} target_link_libraries(${PROJECT_NAME} PUBLIC nsOne::UpstreamOne - nsTwo::CompA - nsTwo::CompB + nsTwo::compA + nsTwo::compB PRIVATE nsThree::UpstreamThree INTERFACE From a28eb0a53d3e7ce2e1a82c031f63a531d3460f7e Mon Sep 17 00:00:00 2001 From: Adrien Date: Thu, 4 Mar 2021 12:20:20 +0100 Subject: [PATCH 02/19] Rename "A", "B" and "C" targets to alpha, beta and gamma. --- README.adoc | 93 +++++++++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/README.adoc b/README.adoc index e320824..e2a2aac 100644 --- a/README.adoc +++ b/README.adoc @@ -209,7 +209,7 @@ In particular, the more correlated the submodules/module, the more this can beco [NOTE] .Correlation measure ==== -Likeliness that changes in entity B would entail changes in entity A. +Likeliness that changes in entity `B` would entail changes in entity `A`. ==== The proposed system recognises the existence of both mono and multi repo, @@ -218,10 +218,11 @@ placing them as extrema on a line along which organisations are allowed to move .Organically growing codebase [%collapsible] ==== -Application B can start as a library (libA) and its frontend (B). +Application *uno* can start as a library `libalpha` and its frontend `uno`. Seeing how they are lock-stepped, it makes sense to host both in the same repo (monorepo). -Then, identified generic functionalities can be moved out of libA in libCommon. libCommon can -start its existence in the same repo, and later on move to a separate repo to be offered to other internal projects and/or 3rd parties. There is value in adaptability. +Then, identified generic functionalities can be moved out of `libalpha` in `libcommon`. +`libcommon` can start its existence in the same repo, and later on move to a separate repo +to be offered to other internal projects and/or 3rd parties. There is value in adaptability. ==== [[anyrepo]] @@ -249,8 +250,8 @@ toolTwo/ src/ CMakeLists.txt (cmp) apps/ - C/ - C/ + gamma/ + gamma/ CMakeLists.txt (cmc-C) main.cpp appclass.h @@ -258,8 +259,8 @@ src/ ... ... libs/ - A/ - A/ + alpha/ + alpha/ CMakeLists.txt (cmc-A) accumulate.h accumulate.cpp @@ -268,8 +269,8 @@ src/ ... subcomponent/ ... - B/ - B/ + beta/ + beta/ CMakeLists.txt (cmc-B) multiply.h ... @@ -278,7 +279,7 @@ src/ resources/ ---- -NOTE: See <>. +NOTE: See <>. NOTE: {Sonat} is intended to be extensible and adaptable. + This is notably the case with the filesystem structure. @@ -401,10 +402,10 @@ It can use basic logic to conditionally add some components (e.g. Making the `te .src/CMakeLists.txt [source, cmake] ---- -add_subdirectory(libs/A/A) -add_subdirectory(libs/B/B) +add_subdirectory(libs/alpha/alpha) +add_subdirectory(libs/beta/beta) -add_subdirectory(apps/C/C) +add_subdirectory(apps/gamma/gamma) option(BUILD_tests) if (BUILD_tests) @@ -421,10 +422,10 @@ The process relies on the nested project name as the component's name, and addit This is to ensure a DRY solution, in particular when it comes to lists. -.src/libs/A/A/CMakeLists.txt (component without upstream dependencies) +.src/libs/alpha/alpha/CMakeLists.txt (component without upstream dependencies) [source, cmake] ---- -project(A VERSION "${CMAKE_PROJECT_VERSION}") +project(alpha VERSION "${CMAKE_PROJECT_VERSION}") set(${PROJECT_NAME}_HEADERS accumulate.h @@ -472,11 +473,11 @@ The base snippet above does a few things, and is hopefully direct about each: |=== | ---- -project(A VERSION "${CMAKE_PROJECT_VERSION}") +project(alpha VERSION "${CMAKE_PROJECT_VERSION}") ---- |Implicitly defines the variables: -* `PROJECT_NAME` initialised to "A" +* `PROJECT_NAME` initialised to "alpha" * `${PROJECT_NAME}_VERSION` initialised to the version provided to the root project() call | @@ -494,13 +495,13 @@ add_library(${PROJECT_NAME} ${${PROJECT_NAME}_HEADERS} ${${PROJECT_NAME}_SOURCES}) ---- -|Defines a target named `A` for this component with `add_library`. +|Defines a target named `alpha` for this component with `add_library`. It would build fine without listing the headers, yet doing so ensures they show up in IDEs. |---- add_library(myrepo::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) ---- -|[[cmake-alias-rationale]]Defines an alias `myrepo::A` for the target, so `A` is accessible to sibling components under namespace `myrepo`. It avoids to wonder "should the namespace be prepended in this situation?", while making it easier to relocate components independently. +|[[cmake-alias-rationale]]Defines an alias `myrepo::alpha` for the target, so `alpha` is accessible to sibling components under namespace `myrepo`. It avoids to wonder "should the namespace be prepended in this situation?", while making it easier to relocate components independently. | ---- target_include_directories(${PROJECT_NAME} @@ -535,8 +536,8 @@ this way the current component is the only component available in the added incl CAUTION: [[cmake-private-might-forward]]This describe the high level semantic from CMake user perspective. + In practice, `PRIVATE` requirement might still be propagated (in whole or in parts) to downstreams when the implementation dictates so. - For example this is mandatory when linking to a static library target A, itself privately linking to another static library target B. - Even though downstream code is not aware of B, linking downstream to A will also require linking downstream to symbols in B. + For example this is mandatory when linking to a static library target `alpha`, itself privately linking to another static library target `beta`. + Even though downstream code is not aware of `beta`, linking downstream to `alpha` will also require linking downstream to symbols in `beta`. See https://cmake.org/pipermail/cmake/2016-May/063400.html. | @@ -680,11 +681,11 @@ TIP: Even though a syntax without specifying the scope is available, always expl The updated leaf `CMakeLists.txt` for a component using dependencies would look something like: -.src/libs/A/A/CMakeLists.txt +.src/libs/alpha/alpha/CMakeLists.txt [source, cmake] [subs=+quotes] ---- -project(A VERSION "${CMAKE_PROJECT_VERSION}") +project(alpha VERSION "${CMAKE_PROJECT_VERSION}") set(${PROJECT_NAME}_HEADERS accumulate.h @@ -722,7 +723,7 @@ target_include_directories(${PROJECT_NAME} PRIVATE nsThree::UpstreamThree INTERFACE - myrepo::B)* + myrepo::beta)* # Defines target properties set_target_properties(${PROJECT_NAME} @@ -730,8 +731,8 @@ set_target_properties(${PROJECT_NAME} VERSION "${${PROJECT_NAME}_VERSION}") ---- -NOTE: It is also possible to specify normal (non-imported) targets defined by other components in the same repository, as is the case in this example with `myrepo::B`. -For uniformity, we are using the `ALIAS`ed target for B (following <>.) +NOTE: It is also possible to specify normal (non-imported) targets defined by other components in the same repository, as is the case in this example with `myrepo::beta`. +For uniformity, we are using the `ALIAS`ed target for `beta` (following <>.) [[cmake-package]] @@ -849,11 +850,11 @@ link:https://cmake.org/cmake/help/v3.14/module/CMakePackageConfigHelpers.html#co For a repository containing a *single component*, an updated leaf `CMakeLists.txt` able to produce a CMake package would look something like: -.src/libs/A/A/CMakeLists.txt +.src/libs/alpha/alpha/CMakeLists.txt [source, cmake] [subs=+quotes] ---- -project(A VERSION "${CMAKE_PROJECT_VERSION}") +project(alpha VERSION "${CMAKE_PROJECT_VERSION}") set(${PROJECT_NAME}_HEADERS accumulate.h @@ -891,7 +892,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE nsThree::UpstreamThree INTERFACE - myrepo::B) + myrepo::beta) # Defines target properties set_target_properties(${PROJECT_NAME} @@ -937,7 +938,7 @@ be discussed with CMake experts and maintainers to see if this situation can be ==== Multiple components in a single CMake package -The approach described above will produce a CMake package with the name of the leaf project (`A`, in this specific case). +The approach described above will produce a CMake package with the name of the leaf project (`alpha`, in this specific case). This is satisfying for single component repositories, yet a complication arises in the case of multiple components per repo. When applied in a repository containing many components, this produces as many packages as there are components. @@ -1049,11 +1050,11 @@ This multi-component transformation also induces two changes in the leaf CMakeLi * The version file is already generated at the top level, no need to version components individually * The install destination must be adapted to match the root project name. -.src/libs/A/A/CMakeLists.txt +.src/libs/alpha/alpha/CMakeLists.txt [source, cmake] [subs=+quotes] ---- -project(A VERSION "${CMAKE_PROJECT_VERSION}") +project(alpha VERSION "${CMAKE_PROJECT_VERSION}") set(${PROJECT_NAME}_HEADERS accumulate.h @@ -1091,7 +1092,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE nsThree::UpstreamThree INTERFACE - myrepo::B) + myrepo::beta) # Defines target properties set_target_properties(${PROJECT_NAME} @@ -1151,7 +1152,7 @@ The calls to `find_package` are moved away from the leaf `CMakeLists.txt` to a c * `REQUIRED` * `QUIET` -.src/libs/A/A/CMakeFinds.cmake.in +.src/libs/alpha/alpha/CMakeFinds.cmake.in [source, cmake] ---- @find_package@(UpstreamOne @REQUIRED@) @@ -1183,7 +1184,7 @@ In production code, this function should likely be factorised outside of any lea In substance, this generates a file with a content strictly equal to what was removed from the leaf `CMakeLists.txt`, and includes it: functionally equivalent. Yet, it will now be possible to reuse this information from the `AConfig.cmake` file after configuring it with different substitutions. -Yet, this does not address the case of internal dependencies: in the current example `A` having a requirement for `myrepo::B` is an internal dependency. + +Yet, this does not address the case of internal dependencies: in the current example `alpha` having a requirement for `myrepo::beta` is an internal dependency. + Since those targets are already defined under the same repository / same root `CMakeLists.txt`, they are not found via calls to `find_package` in their sibling components (in the build tree). On the other hand, when exporting a `xxxConfig.cmake` file, those sibling targets are not defined anymore. The package developer must then once again take measures to make sure they are explicitly found in the install tree. @@ -1194,7 +1195,7 @@ The package developer must then once again take measures to make sure they are e [subs=+quotes] ---- *set(${PROJECT_NAME}_INTERNAL_INTERFACE_DEPENDENCIES - B)* + beta)* #... @@ -1288,11 +1289,11 @@ NOTE: `configure_file(...PackageConfig.cmake.in ...)` was moved inside this func The install and packaging logic proposed by {Sonat} is now complete, which gives the following final leaf `CMakeLists.txt` for a multi-components repository: -.src/libs/A/A/CMakeLists.txt +.src/libs/alpha/alpha/CMakeLists.txt [source, cmake] [subs=+quotes] ---- -project(A VERSION "${CMAKE_PROJECT_VERSION}") +project(alpha VERSION "${CMAKE_PROJECT_VERSION}") set(${PROJECT_NAME}_HEADERS accumulate.h @@ -1314,7 +1315,7 @@ endfunction() local_find()* *set(${PROJECT_NAME}_INTERNAL_INTERFACE_DEPENDENCIES - B)* + beta)* *function(config_find) set (find_package "find_dependency") @@ -1399,7 +1400,7 @@ Could CMake provide the actual list of internal and external dependencies *which **** See: https://stackoverflow.com/q/58221190/1027706 -In short, `find_dependency(B)` indeed forwards `REQUIRED` from the calling `find_package(A)`, which makes the call fails in B, without the promised diagnostic mentioning that "A cannot be used without B". +In short, `find_dependency(beta)` indeed forwards `REQUIRED` from the calling `find_package(alpha)`, which makes the call fails in beta, without the promised diagnostic mentioning that "alpha cannot be used without beta". A more "natural" approach might actually be not to forward it, since `REQUIRED` actually only applies to the calling `find_package`, which might have independently `REQUIRED` and optional dependencies. **** @@ -1781,7 +1782,7 @@ This works consistently without any extra effort, *as longs as all downstream(s) Conan also offers a mechanism to specify a package usage requirements, via link:https://docs.conan.io/en/latest/reference/conanfile/attributes.html#cpp-info[`cpp_info` to be populated in `package_info()`]. When this attribute is correctly configured, the package can be consumed via other Conan generators. -For the repository in this guide, it would at least require to list individual include paths for the library components (`include/A` and `include/B`), +For the repository in this guide, it would at least require to list individual include paths for the library components (`include/alpha` and `include/beta`), since {Sonat} duplicates the component folder exactly for this reason (not being able to access separate components from a common include path). There might also be compiler flags, etc. Unfortunately, this would raise two problems: @@ -1816,8 +1817,8 @@ Such folder usually consists of 3 files: .conan/test_package/example.cpp ---- -#include -#include +#include +#include #include @@ -1841,10 +1842,10 @@ project(PackageTest CXX) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_CURRENT_BINARY_DIR}>) -find_package(MyRepository REQUIRED COMPONENTS A) +find_package(MyRepository REQUIRED COMPONENTS alpha) add_executable(example example.cpp) -target_link_libraries(example myrepo::A) +target_link_libraries(example myrepo::alpha) ---- Simple CMake project, defining the `example` target to compile the above `example.cpp` file. From 3072f08f5399a32cb4ba652365f6992e527baa4b Mon Sep 17 00:00:00 2001 From: Adrien Date: Thu, 4 Mar 2021 12:21:52 +0100 Subject: [PATCH 03/19] Use {cpp} more consistently. --- README.adoc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.adoc b/README.adoc index e2a2aac..1fe28be 100644 --- a/README.adoc +++ b/README.adoc @@ -14,7 +14,7 @@ endif::[] :toc: :toc-placement!: -An adaptable approach to build and distribute usable C++ software components +An adaptable approach to build and distribute usable {cpp} software components toc::[] @@ -187,7 +187,7 @@ for the changed recipe. [%collapsible] ==== Another illustration is how monorepo makes it harder for a single team to change compiler in isolation, even in the context of a stable ABI. -Since the new compiler might be more strict regarding C++ standard, it could raise new errors and warnings in the codebase. +Since the new compiler might be more strict regarding {cpp} standard, it could raise new errors and warnings in the codebase. The compiler change is applied to an entire repository at once: * In a multirepo, the team will be able to adapt its own component in isolation. @@ -313,7 +313,7 @@ Like the rest of the code, it should be treated as an evolving piece of informat ==== Portability considerations -Standard C++ is a cross platform language, with an ever growing ecosystem of tools. Yet the limiting factor for portability often turns out to be the build system. +Standard {cpp} is a cross platform language, with an ever growing ecosystem of tools. Yet the limiting factor for portability often turns out to be the build system. Achieving a cross-platform and cross-toolset (code editors, compilers and analysers) build system, while keeping it DRY, is a notable challenge. @@ -382,7 +382,7 @@ add_subdirectory(src) With the `add_subdirectory(src)` directive, CMake executes the named `CMakeLists.txt` in the `src/` subdirectory (cmp). -This top-level file sets the default (likely minimal requirement) C++ standard, unless a value was already provided for `CMAKE_CXX_STANDARD` variable. +This top-level file sets the default (likely minimal requirement) {cpp} standard, unless a value was already provided for `CMAKE_CXX_STANDARD` variable. NOTE: Making `CMAKE_CXX_STANDARD` a cache variable would allow to remove the `if`. Yet it is not known of which nature the variable could already be. (e.g. Conan `basic_conan_setup()` sets it as non-cache) @@ -1387,7 +1387,7 @@ As already evoked, the leaf `CMakeLists.txt` now contains even more generic boil Is there a canonical way to reduce this? Would there be interest in turning the repetitive code into an official CMake macro? -Even the explicit code is able to adapt to many more different situations, it feels like this case might be a sane default starting point for modern C++ libraries. +Even the explicit code is able to adapt to many more different situations, it feels like this case might be a sane default starting point for modern {cpp} libraries. Additionally, the current solution to keep the list of external and internal dependencies DRY is a hack, which might be wasteful (all the dependencies will be "found" by the package consumers, even the `PRIVATE` dependencies that are actually not forwarded): @@ -1604,7 +1604,7 @@ While CMake manages the details of the build system for an isolated repository, ** The code is unique, but there is a lot of variability in the produced artifacts. A first source of variability is the target environment: {cpp} model is write once, build everywhere (i.e. many times). -There is also variability in how a project is built even for a single defined environment (Debug/Release, compiler flags, C++ standard version, optional components, etc.) +There is also variability in how a project is built even for a single defined environment (Debug/Release, compiler flags, {cpp} standard version, optional components, etc.) ** Building might require to satisfy an arbitrarily complicated dependency graph. Conan tool is addressing these two issues: it resolves the dependency graphs, From 09a1f37bc35af74cd7fe81f2d3079deb36a3f19c Mon Sep 17 00:00:00 2001 From: Adrien Date: Thu, 4 Mar 2021 12:31:46 +0100 Subject: [PATCH 04/19] Fix casing for profiteableapp and beneficial project CMake commands. --- README.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.adoc b/README.adoc index 1fe28be..e94ec72 100644 --- a/README.adoc +++ b/README.adoc @@ -1941,7 +1941,7 @@ cd beneficialproject/build conan install ../conan # Generate project files -cmake -DCMAKE_PROJECT_beneficialproject_INCLUDE=conan/customconan.cmake \ +cmake -DCMAKE_PROJECT_BeneficialProject_INCLUDE=conan/customconan.cmake \ -DCMAKE_INSTALL_PREFIX=${local_sdk_folder}/beneficialproject \ .. ---- @@ -2001,7 +2001,7 @@ cd profitableapp/build # Install a restricted set of dependencies *conan install ../conan/conanfile-dev.txt* -cmake .. -DPROJECT_profitableapp_INCLUDE=conan/customconan.cmake \ +cmake .. -DPROJECT_ProfitableApp_INCLUDE=conan/customconan.cmake \ -DCMAKE_INSTALL_PREFIX=${local_sdk_folder}/profitableapp *-DCMAKE_PREFIX_PATH=${local_sdk_folder}* ---- @@ -2025,7 +2025,7 @@ This means that, before any edition applied to them becomes available to downstr In certain situations, it might be preferable to find the manually built upstream dependencies in their build tree. In this case, the *single* value provided to `CMAKE_PREFIX_PATH` should be replaced with *distinct definitions* for each upstream. In the current example, it would be replaced with + -`-Dbeneficialproject_DIR=${build_dir}`. +`-DBeneficialProject_DIR=${build_dir}`. Unless a dependency found in its build tree is a header-only library, it should still be rebuilt (but not necessarily installed anymore) in order for changes to propagate downstream. ==== From 02b168a5a54df7ba16338974e0f1828bc8f8fecf Mon Sep 17 00:00:00 2001 From: Adrien Date: Thu, 4 Mar 2021 15:21:38 +0100 Subject: [PATCH 05/19] Add a "naming convention" section, and corresponding notes with links. --- CHANGELOG.md | 5 +++++ README.adoc | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b35727..bee382e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- A naming convention for CMake packages and their components. + +### Changed +- A, B and C CMake targets are renamed alpha, beta and gamma respectively. ## [0.7.4] - 2021-03-04 ### Fixed diff --git a/README.adoc b/README.adoc index e94ec72..2f7a1a9 100644 --- a/README.adoc +++ b/README.adoc @@ -387,6 +387,8 @@ This top-level file sets the default (likely minimal requirement) {cpp} standard NOTE: Making `CMAKE_CXX_STANDARD` a cache variable would allow to remove the `if`. Yet it is not known of which nature the variable could already be. (e.g. Conan `basic_conan_setup()` sets it as non-cache) +NOTE: {Sonat} recommends that the root `project()` name <>. + .TODO **** Find a way to control warning level and enable _warning as errors_ for all / some targets, without making it a build requirement. @@ -480,6 +482,9 @@ project(alpha VERSION "${CMAKE_PROJECT_VERSION}") * `PROJECT_NAME` initialised to "alpha" * `${PROJECT_NAME}_VERSION` initialised to the version provided to the root project() call +NOTE: {Sonat} recommends that each CMake target (and associated leaf `project()`) name +<>. + | ---- set(${PROJECT_NAME}_HEADERS ...) @@ -628,7 +633,7 @@ find_package(UpstreamName [version [EXACT]] [REQUIRED]) `version`:: can be specified to add a lower requirement on the version number of the dependency. `EXACT` additional keyword makes it that only the exact version is accepted. -A second type of package can be distinguished, which propose <> to be included separately. In this case, the components to find are listed after `COMPONENTS` keyword (or `OPTIONAL_COMPONENTS` for non-required components). The syntax becomes: @@ -936,6 +941,7 @@ For the moment, it is recommended to factorise this logic in a custom CMake func be discussed with CMake experts and maintainers to see if this situation can be streamlined. **** +[[multiple-components]] ==== Multiple components in a single CMake package The approach described above will produce a CMake package with the name of the leaf project (`alpha`, in this specific case). @@ -2016,6 +2022,7 @@ Thanks to the adopted folder structure, this *single hint* is enough to find any This logic can be extended to explicitly build an arbitrary number of dependencies instead of relying on Conan to provide them. +[[cmake-explicit-upstream-path]] [CAUTION] .install tree vs. build tree ==== @@ -2161,6 +2168,36 @@ Might be illustrated with adding custom CMake targets to produce Windows `msi` i == Annexes +=== Naming conventions + +Naming and syntax convetions are not the intended scope of {Sonat}. + +Yet, it strongly encourages to follow existing convention when they exist: + + * Conan package reference are entirely lower-case (e.g. `beneficialproject/1.2.0@company/stable`) + +[[cmake-naming-convention]] +==== CMake package with multiple components + +Additionnally, there is a situation where using a predefined naming convention can make development more flexible. +When a repository is a <>, +one of the components might be named like the repository +(e.g. if there is one central component, with some peripheral helper components). + +To allow for this scenario without introducing collisions at the CMake `find_package()` level, +{Sonat} makes the following recommendations: + +* the root `project()` name always start with an uppercase letter. +* each CMake target (which will map to components in the package) always start with a lowercase letter. + +This results in `find_package()` calls looking like: + + find_package(Math COMPONENTS bignumber math) + +And the CMake path variable used to <> has the form: + + Math_DIR=${path_to_math_buildtree} + + === Automated QA .TODO From 9042ca152b56fa9b7c3faa8e3cd87cec218a45a6 Mon Sep 17 00:00:00 2001 From: Adrien Date: Thu, 4 Mar 2021 18:47:41 +0100 Subject: [PATCH 06/19] Emit warning on find_package() for a component matching package name. --- CHANGELOG.md | 2 ++ README.adoc | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bee382e..0b24461 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - A naming convention for CMake packages and their components. +- Warning message if calling find_package() for a component within a CMake package +with exactly matching names. ### Changed - A, B and C CMake targets are renamed alpha, beta and gamma respectively. diff --git a/README.adoc b/README.adoc index 2f7a1a9..3e5132b 100644 --- a/README.adoc +++ b/README.adoc @@ -1017,6 +1017,18 @@ endif() include(CMakeFindDependencyMacro) foreach(module ${${CMAKE_FIND_PACKAGE_NAME}_FIND_COMPONENTS}) set (_config_location "${CMAKE_CURRENT_LIST_DIR}") + + # Error when a component has exactly the same identifier as the package_name + # (would first find the current Config.cmake, because xxx_DIR variable is already set) + if(module STREQUAL ${CMAKE_FIND_PACKAGE_NAME}) + set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE + "The '${CMAKE_FIND_PACKAGE_NAME}' package cannot list a component with identical name.\ + Use at least a distinct capitalization \ + (advice: package names start with capital, components do not).") + set(${CMAKE_FIND_PACKAGE_NAME}_FOUND False) + return() + endif() + # find_dependency should forward the QUIET and REQUIRED arguments find_dependency(${module} CONFIG PATHS "${_config_location}" From 8bbfd5f67d111600f03b9ef70ab97536266a194d Mon Sep 17 00:00:00 2001 From: Adrien Date: Thu, 11 Mar 2021 11:39:23 +0100 Subject: [PATCH 07/19] Update Conan Center reference to CMake installer (remove @conan/stable) --- CHANGELOG.md | 1 + README.adoc | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b24461..4d61a07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ with exactly matching names. ### Changed - A, B and C CMake targets are renamed alpha, beta and gamma respectively. +- Conan installer for CMake uses updated "Conan Center" reference (without `@conan/stable` suffix). ## [0.7.4] - 2021-03-04 ### Fixed diff --git a/README.adoc b/README.adoc index 3e5132b..f70f8e0 100644 --- a/README.adoc +++ b/README.adoc @@ -1549,7 +1549,7 @@ class MyRepositoryConan(ConanFile): # Build dependencies # CMake will not need to be installed to build the project # And if it was installed in a non-compatible version, this will take precedence anyway - build_requires = "cmake_installer/3.15.4@conan/stable" + build_requires = "cmake_installer/3.15.7" # Build procedure: code retrieval @@ -1884,7 +1884,7 @@ class MyRepositoryTestConan(ConanFile): settings = "os", "compiler", "build_type", "arch" generators = "cmake_paths", "cmake" - build_requires = "cmake_installer/3.15.4@conan/stable" + build_requires = "cmake_installer/3.15.7" def build(self): cmake = CMake(self) From ff1522babac28a3f45e29420de2fec6adddc18d3 Mon Sep 17 00:00:00 2001 From: Adrien Date: Thu, 11 Mar 2021 12:13:19 +0100 Subject: [PATCH 08/19] Add "important" remark to use "ARCH_INDEPENDENT" for interface targets. --- CHANGELOG.md | 3 +++ README.adoc | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d61a07..fa8dd84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ with exactly matching names. - A, B and C CMake targets are renamed alpha, beta and gamma respectively. - Conan installer for CMake uses updated "Conan Center" reference (without `@conan/stable` suffix). +### Fixed +- Instruction to use `ARCH_INDEPENDENT` when producing a CMake version file for `INTERFACE` targets. + ## [0.7.4] - 2021-03-04 ### Fixed - ComponentPackageRootConfig.cmake uses a package name with capitalization diff --git a/README.adoc b/README.adoc index f70f8e0..ef8a606 100644 --- a/README.adoc +++ b/README.adoc @@ -579,6 +579,7 @@ an unusual folder hierarchy. Applications are created via link:https://cmake.org/cmake/help/latest/command/add_executable.html[`add_executable`]. When making a native GUI application link:https://cmake.org/cmake/help/latest/prop_tgt/WIN32_EXECUTABLE.html#prop_tgt:WIN32_EXECUTABLE[`WIN_32`] and/or link:https://cmake.org/cmake/help/latest/prop_tgt/MACOSX_BUNDLE.html#prop_tgt:MACOSX_BUNDLE[`MACOSX_BUNDLE`] should be added after the application name. +[[header-only-cmake-target]] ====== Header only CMake target Header only libraries are called link:https://cmake.org/cmake/help/latest/command/add_library.html#id6[Interface Libraries] in CMake. @@ -851,6 +852,13 @@ NOTE: It currently seems this file introduces an extra indirection for no reason NOTE: `AnyNewerVersion` can be replaced by any valid value for link:https://cmake.org/cmake/help/v3.14/module/CMakePackageConfigHelpers.html#command:write_basic_package_version_file[the `COMPATIBILITY` argument]. +IMPORTANT: For targets where the produced artifact will be the same for all supported systems, +i.e. <>, the extra argument `ARCH_INDEPENDENT` +should be given to `write_basic_package_version_file`. + +Failing to do so, when distributing the same package for all systems, +said package will break on architectures that do not match +the architecture where the unique package was produced. + ==== Putting it together For a repository containing a *single component*, an updated leaf `CMakeLists.txt` able to produce a CMake package would look something like: From f1195587b0c859f1fabc5033d15c5b7d8d2966f2 Mon Sep 17 00:00:00 2001 From: Adrien Date: Wed, 17 Mar 2021 17:51:08 +0100 Subject: [PATCH 09/19] Introduce subfolders for CMake multi-components config files. --- README.adoc | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/README.adoc b/README.adoc index ef8a606..c379e5f 100644 --- a/README.adoc +++ b/README.adoc @@ -1024,7 +1024,8 @@ endif() include(CMakeFindDependencyMacro) foreach(module ${${CMAKE_FIND_PACKAGE_NAME}_FIND_COMPONENTS}) - set (_config_location "${CMAKE_CURRENT_LIST_DIR}") + set (_config_location "${CMAKE_CURRENT_LIST_DIR}/${module}") + # Error when a component has exactly the same identifier as the package_name # (would first find the current Config.cmake, because xxx_DIR variable is already set) @@ -1071,10 +1072,16 @@ For this reason, re-set `_config_location` variable at each iteration of the `fo This template leverages the config files still produced and installed by each individual component in order to locate them, via the call to link:https://cmake.org/cmake/help/latest/module/CMakeFindDependencyMacro.html[`find_dependency()`]. -This multi-component transformation also induces two changes in the leaf CMakeLists.txt compared to what was presented above: +NOTE: The template is looking for the individual components config files in a *subfolder* with the component name, +by calling `find_dependency` with a `PATHS` value of `${CMAKE_CURRENT_LIST_DIR}/*${module}*`. + +This extra folder is added in case one of the components has the same name than the root CMake project, <>. +In such situtation, the the root project `XxxConfig.cmake` file and the component `xxxConfig.cmake` file would collide on case-insensitive file systems if they were both placed in the same folder. + +This multi-component transformation also induces three changes in the leaf CMakeLists.txt compared to what was presented above: -* The version file is already generated at the top level, no need to version components individually -* The install destination must be adapted to match the root project name. +* The version file is already generated at the top level, no need to version components individually. +* The config files must be placed in a subfolder with the component name (see note above). +* The install destination must be adapted to match the root project name and component subfolder. .src/libs/alpha/alpha/CMakeLists.txt [source, cmake] @@ -1131,10 +1138,10 @@ install(FILES ${${PROJECT_NAME}_HEADERS} # build tree export(EXPORT ${PROJECT_NAME}Targets - FILE ${CMAKE_BINARY_DIR}/${PROJECT_NAME}Targets.cmake + FILE ${CMAKE_BINARY_DIR}/*${PROJECT_NAME}/*${PROJECT_NAME}Targets.cmake NAMESPACE myrepo::) configure_file(${CMAKE_SOURCE_DIR}/cmake/PackageConfig.cmake.in - ${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake + ${CMAKE_BINARY_DIR}/*${PROJECT_NAME}/*${PROJECT_NAME}Config.cmake @ONLY) *# Removed lines* @@ -1142,11 +1149,11 @@ configure_file(${CMAKE_SOURCE_DIR}/cmake/PackageConfig.cmake.in # install tree install(EXPORT ${PROJECT_NAME}Targets FILE ${PROJECT_NAME}Targets.cmake - DESTINATION lib/cmake/*${CMAKE_PROJECT_NAME}* + DESTINATION lib/cmake/*${CMAKE_PROJECT_NAME}/${PROJECT_NAME}* NAMESPACE myrepo::) -install(FILES ${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake +install(FILES ${CMAKE_BINARY_DIR}/*${PROJECT_NAME}/*${PROJECT_NAME}Config.cmake *# Removed line* - DESTINATION lib/cmake/*${CMAKE_PROJECT_NAME}*) + DESTINATION lib/cmake/*${CMAKE_PROJECT_NAME}/${PROJECT_NAME}*) ---- [[cmake-package-upstream-dependencies]] From e68e21ea4e1d75cd61e45277bac33969ec9b74af Mon Sep 17 00:00:00 2001 From: Adrien Date: Wed, 17 Mar 2021 18:09:39 +0100 Subject: [PATCH 10/19] Propagate the subfolder addition to the following code snippets. --- README.adoc | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/README.adoc b/README.adoc index c379e5f..ca88959 100644 --- a/README.adoc +++ b/README.adoc @@ -1265,7 +1265,9 @@ This is achieved by configuring the `CMakeFinds.cmake.in` template with differen function(config_find) set (find_package "find_dependency") # Configure in build tree - configure_file(CMakeFinds.cmake.in ${CMAKE_BINARY_DIR}/${PROJECT_NAME}FindUpstream.cmake @ONLY) + configure_file(CMakeFinds.cmake.in + ${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}FindUpstream.cmake + @ONLY) endfunction() config_find() ---- @@ -1277,10 +1279,10 @@ This new file has to be deployed to the install tree: [source, cmake] [subs=+quotes] ---- - install(FILES ${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake + install(FILES ${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Config.cmake # Optional version file if single component repository - *${CMAKE_BINARY_DIR}/${PROJECT_NAME}FindUpstream.cmake* - DESTINATION lib/cmake/${CMAKE_PROJECT_NAME}) + *${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}FindUpstream.cmake* + DESTINATION lib/cmake/${CMAKE_PROJECT_NAME}/${PROJECT_NAME}) ---- The root template `PackageConfig.cmake.in` has to be edited to include this file: @@ -1304,19 +1306,21 @@ This takes place in the leaf `CMakeLists.txt`: ---- function(config_find) set (find_package "find_dependency") - configure_file(CMakeFinds.cmake.in ${CMAKE_BINARY_DIR}/${PROJECT_NAME}FindUpstream.cmake @ONLY) + configure_file(CMakeFinds.cmake.in + ${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}FindUpstream.cmake + @ONLY) *list(JOIN ${PROJECT_NAME}_INTERNAL_INTERFACE_DEPENDENCIES " " _joined_components) set(FIND_INTERNAL_COMPONENTS "find_dependency(${CMAKE_PROJECT_NAME} CONFIG COMPONENTS ${_joined_components})") configure_file(${CMAKE_SOURCE_DIR}/cmake/PackageConfig.cmake.in - ${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake + ${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Config.cmake @ONLY)* endfunction() config_find() ---- -NOTE: `configure_file(...PackageConfig.cmake.in ...)` was moved inside this function, to see the variable. +NOTE: `configure_file(...PackageConfig.cmake.in ...)` was moved inside this function, to access the `FIND_INTERNAL_COMPONENTS` variable. ==== Putting it together @@ -1352,13 +1356,15 @@ local_find()* *function(config_find) set (find_package "find_dependency") - configure_file(CMakeFinds.cmake.in ${CMAKE_BINARY_DIR}/${PROJECT_NAME}FindUpstream.cmake @ONLY) + configure_file(CMakeFinds.cmake.in + ${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}FindUpstream.cmake + @ONLY) list(JOIN ${PROJECT_NAME}_INTERNAL_INTERFACE_DEPENDENCIES " " _joined_components) set(FIND_INTERNAL_COMPONENTS "find_dependency(${CMAKE_PROJECT_NAME} CONFIG COMPONENTS ${_joined_components})") configure_file(${CMAKE_SOURCE_DIR}/cmake/PackageConfig.cmake.in - ${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake + ${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Config.cmake @ONLY) endfunction() config_find()* @@ -1399,18 +1405,18 @@ install(FILES ${${PROJECT_NAME}_HEADERS} # build tree export(EXPORT ${PROJECT_NAME}Targets - FILE ${CMAKE_BINARY_DIR}/${PROJECT_NAME}Targets.cmake + FILE ${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Targets.cmake NAMESPACE myrepo::) *#configure_file(... PackageConfig.cmake.in ...) moved in config_find() above* # install tree install(EXPORT ${PROJECT_NAME}Targets FILE ${PROJECT_NAME}Targets.cmake - DESTINATION lib/cmake/${CMAKE_PROJECT_NAME} + DESTINATION lib/cmake/${CMAKE_PROJECT_NAME}/${PROJECT_NAME} NAMESPACE myrepo::) -install(FILES ${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake - *${CMAKE_BINARY_DIR}/${PROJECT_NAME}FindUpstream.cmake* - DESTINATION lib/cmake/${CMAKE_PROJECT_NAME}) +install(FILES ${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Config.cmake + *${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}FindUpstream.cmake* + DESTINATION lib/cmake/${CMAKE_PROJECT_NAME}/${PROJECT_NAME}) ---- From 4f94fa6ea3c6a3a9b2fe2d067bf82e94db2b43c3 Mon Sep 17 00:00:00 2001 From: Adrien Date: Wed, 17 Mar 2021 18:12:08 +0100 Subject: [PATCH 11/19] Update changelog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa8dd84..542a504 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ with exactly matching names. ### Changed - A, B and C CMake targets are renamed alpha, beta and gamma respectively. - Conan installer for CMake uses updated "Conan Center" reference (without `@conan/stable` suffix). +- CMake packages with multiple components now have a separate subfolder +for each component config files. ### Fixed - Instruction to use `ARCH_INDEPENDENT` when producing a CMake version file for `INTERFACE` targets. From 5341ce2bc8a2d5f3148691c49b5492518d2cf3d2 Mon Sep 17 00:00:00 2001 From: Adrien Date: Thu, 18 Mar 2021 15:40:19 +0100 Subject: [PATCH 12/19] Remove the "cloned_repo" subfolder from the Conan recipe. --- CHANGELOG.md | 3 +++ README.adoc | 24 ++++++++++++++---------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 542a504..45e35f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,9 @@ for each component config files. ### Fixed - Instruction to use `ARCH_INDEPENDENT` when producing a CMake version file for `INTERFACE` targets. +## Removed +- Conan recipe does not clone projects into a `cloned_repo` subfolder anymore. + ## [0.7.4] - 2021-03-04 ### Fixed - ComponentPackageRootConfig.cmake uses a package name with capitalization diff --git a/README.adoc b/README.adoc index ca88959..74bed15 100644 --- a/README.adoc +++ b/README.adoc @@ -1577,7 +1577,6 @@ class MyRepositoryConan(ConanFile): # Git's repository origin remote and its current revision are captured by recipe export scm = { "type": "git", - "subfolder": "cloned_repo", "url": "auto", "revision": "auto", "submodule": "recursive", @@ -1588,7 +1587,7 @@ class MyRepositoryConan(ConanFile): def _configure_cmake(self): cmake = CMake(self) cmake.definitions["BUILD_tests"] = self.options.build_tests - cmake.configure(source_folder="cloned_repo") + cmake.configure() return cmake @@ -1615,11 +1614,6 @@ In particular, the build and packaging procedures are short, thanks to link:http . In each case, a `CMake` Python object is instantiated, its attributes defined from the provided settings and options, then it is configured. . `build()` or `install()` method is invoked according to the current step. Packaging leverages the installation logic provided by CMake through the `install` target. -CAUTION: {Sonat} introduces a `cloned_repo` subfolder to clone into. -Invoking `conan install`, Conan will copy the content of its source folder directly at the root of the build folder. -If we did not clone in a subfolder, the different files at the root of the repository would appear directly at the root of the build folder, which could augment the risk of filename collision. -In other words, it ensures an _out of source build_, with the specificity that the source folder is nested under the build folder. - CAUTION: The recipe revision mode is explicitly set to `revision_mode = scm`, instead of the default `hash` mode. As its value indicates, the default mode computes the recipe revision by hashing the recipe file. + Since hashing notably takes line endings into account, this might result in different revisions being computed @@ -1633,6 +1627,16 @@ NOTE: The `shared` option and `build_type` setting are common in recipes, thus C On the other hand, the custom `build_tests` option is manually forwarded. This explicit approach allows complete customisation of the CMake variables. The documentation provides link:https://docs.conan.io/en/latest/reference/build_helpers/cmake.html#definitions[the list of automatic variables]. +NOTE: Until v0.7, {Sonat} introduced a `cloned_repo` subfolder to clone into. +The rationale was that: when invoking `conan install`, Conan will copy the content of its source +folder directly at the root of the build folder. +With this `cloned_repo` subfolder, the different files at the root of the repository would not +be copied directly at the root of the build folder, reducing the risk of filename collision with build files. + + + +This `cloned_repo` subfolder has been deprecated, because it breaks Conan commands where +the source folder is explicitly provided via the `-sf` command line arguments. +(Usually pointing it to a development folder, not containing this articifial `cloned_repo` subfolder). + ==== Taking a step back As Conan package manager was introduced, now is a good time to take a look at the overall picture. @@ -1785,9 +1789,9 @@ from conans import ConanFile, CMake, tools def _configure_cmake(self): cmake = CMake(self) *cmake.definitions["CMAKE_PROJECT_MyRepository_INCLUDE"] = \ - path.join(self.source_folder, "cloned_repo", "conan", "customconan.cmake")* + path.join(self.source_folder, "conan", "customconan.cmake")* cmake.definitions["BUILD_tests"] = self.options.build_tests - cmake.configure(source_folder="cloned_repo") + cmake.configure() return cmake ... @@ -2062,7 +2066,7 @@ This logic can be extended to explicitly build an arbitrary number of dependenci By setting `CMAKE_PREFIX_PATH` to the install folder, the manually built upstream dependencies are found in their *install tree*. This means that, before any edition applied to them becomes available to downstream(s), they must first invoke their `install` target (e.g. `cmake --build . --target install`) -In certain situations, it might be preferable to find the manually built upstream dependencies in their build tree. +In certain situations, it might be preferable to find the manually built upstream dependencies in their *build tree*. In this case, the *single* value provided to `CMAKE_PREFIX_PATH` should be replaced with *distinct definitions* for each upstream. In the current example, it would be replaced with + `-DBeneficialProject_DIR=${build_dir}`. From f09fe3d915321b52cd2b66d16489e67354eca54c Mon Sep 17 00:00:00 2001 From: Adrien Date: Wed, 24 Mar 2021 14:59:28 +0100 Subject: [PATCH 13/19] Fix missed renames (AConfig instead of alphaConfig). --- README.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.adoc b/README.adoc index 74bed15..470dec9 100644 --- a/README.adoc +++ b/README.adoc @@ -1215,7 +1215,7 @@ NOTE: The sole purpose of defining a function here instead of inlining its conte In production code, this function should likely be factorised outside of any leaf `CMakeLists.txt`, and reused. In substance, this generates a file with a content strictly equal to what was removed from the leaf `CMakeLists.txt`, and includes it: functionally equivalent. -Yet, it will now be possible to reuse this information from the `AConfig.cmake` file after configuring it with different substitutions. +Yet, it will now be possible to reuse this information from the `alphaConfig.cmake` file after configuring it with different substitutions. Yet, this does not address the case of internal dependencies: in the current example `alpha` having a requirement for `myrepo::beta` is an internal dependency. + Since those targets are already defined under the same repository / same root `CMakeLists.txt`, they are not found via calls to `find_package` in their sibling components (in the build tree). @@ -1244,13 +1244,13 @@ target_link_libraries(${PROJECT_NAME} *${${PROJECT_NAME}_INTERNAL_INTERFACE_DEPENDENCIES}*) ---- -This also achieves functional equivalence to the previous solution, with the added ability to reuse this information for the generated `AConfig.cmake` file. +This also achieves functional equivalence to the previous solution, with the added ability to reuse this information for the generated `alphaConfig.cmake` file. NOTE: The `myrepo::` namespace is not directly prepended to the value(s) when the list is `set()`. + This list will also be used as a list of components in a `find_dependency` call, and components names in this context cannot be prefixed with the namespace. -Now, the dependencies information has to be made available and consumed by the package `AConfig.cmake` file. +Now, the dependencies information has to be made available and consumed by the package `alphaConfig.cmake` file. ===== Making dependency information available From 768146cc0c628209682e11e431447dc8b20ac62c Mon Sep 17 00:00:00 2001 From: Adrien Date: Wed, 24 Mar 2021 16:39:33 +0100 Subject: [PATCH 14/19] Handle internal CMake dependencies uniformly, no more special lists. --- CHANGELOG.md | 2 + README.adoc | 127 +++++++++++++++++++++------------------------------ 2 files changed, 55 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45e35f8..193e931 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ with exactly matching names. - Conan installer for CMake uses updated "Conan Center" reference (without `@conan/stable` suffix). - CMake packages with multiple components now have a separate subfolder for each component config files. +- CMake dependencies within the same repositories are now handled more uniformly +with the external dependencies. ### Fixed - Instruction to use `ARCH_INDEPENDENT` when producing a CMake version file for `INTERFACE` targets. diff --git a/README.adoc b/README.adoc index 470dec9..5faea2d 100644 --- a/README.adoc +++ b/README.adoc @@ -1159,19 +1159,20 @@ install(FILES ${CMAKE_BINARY_DIR}/*${PROJECT_NAME}/*${PROJECT_NAME}Config.cmake [[cmake-package-upstream-dependencies]] ==== Finding upstream dependencies from a CMake package -The current CMake code allows downstreams to find requested components in a package, each component - in turn forwarding its direct requirements, those direct requirements in turn doing the same: +The current CMake code allows downstreams to find requested components in a package, +each component forwarding its direct requirements. +Those direct requirements in turn forward their own requirements: the requirements are transitively forwarded by a recursive traversal of the upstream dependencies graph. Yet, for this exhaustive process to take place, each upstream must be found -(so its corresponding `IMPORTED` target does exist in the current CMake context) +(in order for its corresponding `IMPORTED` target to exist in the current CMake context) before it is expressed as a direct dependency on a target (via `target_link_libraries` for dependencies found as `IMPORTED` targets). -When implementing a component following {Sonat}, its direct dependencies are all found in the component's leaf `CMakeLists.txt`: this takes care of the first level of dependency. -Yet, those direct dependencies might have their own dependencies, which are no directly found in the current `CMakeLists.txt`. +When implementing a component following {Sonat}, its *direct* dependencies are all found in the component's leaf `CMakeLists.txt`: this takes care of the first level of dependency. +Yet, those direct dependencies might have their own dependencies, which are no directly found in the initial component's `CMakeLists.txt`. -CAUTION: The `xxxTarget.cmake` file generated by CMake for the direct dependencies does not find its direct dependencies. +CAUTION: To sum-up: the `xxxTarget.cmake` file generated by CMake for the direct dependencies does not find its direct dependencies. To be properly *self-contained*, a CMake package must thus *find its direct dependencies*. Issuing the necessary `find_` commands link:https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#creating-a-package-configuration-file[is a responsibility left to the package developer]. @@ -1202,9 +1203,9 @@ In `CMakeLists.txt`, the different `find_package()` calls are replaced with a si [source, cmake] ---- function(local_find) - set (REQUIRED "REQUIRED") - set (QUIET "QUIET") - set (find_package "find_package") + set(REQUIRED "REQUIRED") + set(QUIET "QUIET") + set(find_package "find_package") configure_file(CMakeFinds.cmake.in CMakeFinds.cmake @ONLY) include(${CMAKE_CURRENT_BINARY_DIR}/CMakeFinds.cmake) endfunction() @@ -1218,37 +1219,45 @@ In substance, this generates a file with a content strictly equal to what was re Yet, it will now be possible to reuse this information from the `alphaConfig.cmake` file after configuring it with different substitutions. Yet, this does not address the case of internal dependencies: in the current example `alpha` having a requirement for `myrepo::beta` is an internal dependency. + -Since those targets are already defined under the same repository / same root `CMakeLists.txt`, they are not found via calls to `find_package` in their sibling components (in the build tree). +Since those targets are already defined under the same repository / same root `CMakeLists.txt`, they are not found via calls to `find_package` in their sibling components: the actual target exists in the current CMake context. On the other hand, when exporting a `xxxConfig.cmake` file, those sibling targets are not defined anymore. The package developer must then once again take measures to make sure they are explicitly found in the install tree. -[[cmake-internal-dependencies-lists]]{Sonat} avoids duplication by defining re-usable list(s) of internal dependencies in the leaf `CMakeLists.txt`: +{Sonat} finds the internal dependencies alongside the other dependencies, +but it defines a separate substitution for internal components: +they are using `@find_internal_package@` instead of `@find_package@`. +.src/libs/alpha/alpha/CMakeFinds.cmake.in [source, cmake] [subs=+quotes] ---- -*set(${PROJECT_NAME}_INTERNAL_INTERFACE_DEPENDENCIES - beta)* +@find_package@(UpstreamOne @REQUIRED@) +@find_package@(UpstreamTwo 1.0 @REQUIRED@ COMPONENTS compA compB) +@find_package@(UpstreamThree 3.2.5 EXACT @REQUIRED@ @QUIET@) +*@find_internal_package@(MyRepository @REQUIRED@ COMPONENTS beta CONFIG)* +---- -#... +In the leaf `CMakeLists.txt`, the `local_find()` function defined above is extended +with a value for `@find_internal_package@`. +It will simply comment-out the instruction when the project itself is configured. -*list(TRANSFORM ${PROJECT_NAME}_INTERNAL_INTERFACE_DEPENDENCIES PREPEND myrepo::)* -target_link_libraries(${PROJECT_NAME} - PUBLIC - nsOne::UpstreamOne - nsTwo::compA - nsTwo::compB - PRIVATE - nsThree::UpstreamThree - INTERFACE - *${${PROJECT_NAME}_INTERNAL_INTERFACE_DEPENDENCIES}*) +[source, cmake] +[subs=+quotes] +---- +function(local_find) + set(REQUIRED "REQUIRED") + set(QUIET "QUIET") + set(find_package "find_package") + *set(find_internal_package "#Internal component: find_package")* + configure_file(CMakeFinds.cmake.in CMakeFinds.cmake @ONLY) + include(${CMAKE_CURRENT_BINARY_DIR}/CMakeFinds.cmake) +endfunction() +local_find() ---- -This also achieves functional equivalence to the previous solution, with the added ability to reuse this information for the generated `alphaConfig.cmake` file. -NOTE: The `myrepo::` namespace is not directly prepended to the value(s) when the list is `set()`. + -This list will also be used as a list of components in a `find_dependency` call, -and components names in this context cannot be prefixed with the namespace. +This also achieves functional equivalence to the previous solution, with the added ability +to use a different substitution when producing the find file for the package config. Now, the dependencies information has to be made available and consumed by the package `alphaConfig.cmake` file. @@ -1258,12 +1267,14 @@ Following link:https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html# the package will find its upstream dependencies via the `find_dependency()` macro instead of the `find_package()` function. This macro notably forwards `QUIET` and `REQUIRED` arguments, so they should not be written explicitly. -This is achieved by configuring the `CMakeFinds.cmake.in` template with different substitutions, in particular no substitution for `@REQUIRED@` nor `@QUIET@`: +This is achieved by configuring the `CMakeFinds.cmake.in` template a second time from the leaf `CMakeLists.txt`. +This time with different substitutions, in particular no substitution for `@REQUIRED@` nor `@QUIET@`: [source, cmake] ---- function(config_find) - set (find_package "find_dependency") + set(find_package "find_dependency") + set(find_internal_package "find_dependency") # Configure in build tree configure_file(CMakeFinds.cmake.in ${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}FindUpstream.cmake @@ -1272,7 +1283,7 @@ endfunction() config_find() ---- -NOTE: The resulting configured file appears at the root of the binary directory, instead of in the current binary directory as was the case with `local_find()` +NOTE: The resulting configured file is placed relative to the *root* of the binary directory, instead of in the *current* binary directory as was the case with `local_find()` This new file has to be deployed to the install tree: @@ -1292,36 +1303,11 @@ The root template `PackageConfig.cmake.in` has to be edited to include this file [subs=+quotes] ---- *include(CMakeFindDependencyMacro) # Provides find_dependency() macro -include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@FindUpstream.cmake" OPTIONAL) -@FIND_INTERNAL_COMPONENTS@* +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@FindUpstream.cmake" OPTIONAL)* include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") ---- -`FIND_INTERNAL_COMPONENTS` must be defined to an instruction finding the components in the <>. -This takes place in the leaf `CMakeLists.txt`: - -[source, cmake] -[subs=+quotes] ----- -function(config_find) - set (find_package "find_dependency") - configure_file(CMakeFinds.cmake.in - ${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}FindUpstream.cmake - @ONLY) - - *list(JOIN ${PROJECT_NAME}_INTERNAL_INTERFACE_DEPENDENCIES " " _joined_components) - set(FIND_INTERNAL_COMPONENTS - "find_dependency(${CMAKE_PROJECT_NAME} CONFIG COMPONENTS ${_joined_components})") - configure_file(${CMAKE_SOURCE_DIR}/cmake/PackageConfig.cmake.in - ${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Config.cmake - @ONLY)* -endfunction() -config_find() ----- - -NOTE: `configure_file(...PackageConfig.cmake.in ...)` was moved inside this function, to access the `FIND_INTERNAL_COMPONENTS` variable. - ==== Putting it together The install and packaging logic proposed by {Sonat} is now complete, which gives the following final leaf `CMakeLists.txt` for a multi-components repository: @@ -1343,29 +1329,21 @@ set(${PROJECT_NAME}_SOURCES ) *function(local_find) - set (REQUIRED "REQUIRED") - set (QUIET "QUIET") - set (find_package "find_package") + set(REQUIRED "REQUIRED") + set(QUIET "QUIET") + set(find_package "find_package") + set(find_internal_package "#Internal component: find_package") configure_file(CMakeFinds.cmake.in CMakeFinds.cmake @ONLY) include(${CMAKE_CURRENT_BINARY_DIR}/CMakeFinds.cmake) endfunction() local_find()* -*set(${PROJECT_NAME}_INTERNAL_INTERFACE_DEPENDENCIES - beta)* - *function(config_find) - set (find_package "find_dependency") + set(find_package "find_dependency") + set(find_internal_package "find_dependency") configure_file(CMakeFinds.cmake.in ${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}FindUpstream.cmake @ONLY) - - list(JOIN ${PROJECT_NAME}_INTERNAL_INTERFACE_DEPENDENCIES " " _joined_components) - set(FIND_INTERNAL_COMPONENTS - "find_dependency(${CMAKE_PROJECT_NAME} CONFIG COMPONENTS ${_joined_components})") - configure_file(${CMAKE_SOURCE_DIR}/cmake/PackageConfig.cmake.in - ${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Config.cmake - @ONLY) endfunction() config_find()* @@ -1383,7 +1361,6 @@ target_include_directories(${PROJECT_NAME} INTERFACE "$") -*list(TRANSFORM ${PROJECT_NAME}_INTERNAL_INTERFACE_DEPENDENCIES PREPEND myrepo::)* target_link_libraries(${PROJECT_NAME} PUBLIC nsOne::UpstreamOne @@ -1392,7 +1369,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE nsThree::UpstreamThree INTERFACE - *${${PROJECT_NAME}_INTERNAL_INTERFACE_DEPENDENCIES}*) + myrepo::beta) # Defines target properties set_target_properties(${PROJECT_NAME} @@ -1407,7 +1384,9 @@ install(FILES ${${PROJECT_NAME}_HEADERS} export(EXPORT ${PROJECT_NAME}Targets FILE ${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Targets.cmake NAMESPACE myrepo::) -*#configure_file(... PackageConfig.cmake.in ...) moved in config_find() above* +configure_file(${CMAKE_SOURCE_DIR}/cmake/PackageConfig.cmake.in + ${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Config.cmake + @ONLY) # install tree install(EXPORT ${PROJECT_NAME}Targets From eab43aa96c588c3bd78cb0dd9d48804f87e42d9b Mon Sep 17 00:00:00 2001 From: Adrien Date: Wed, 31 Mar 2021 14:31:41 +0200 Subject: [PATCH 15/19] Add more motivations and benefits for agreeing on a standard process. --- CHANGELOG.md | 1 + README.adoc | 29 +++++++++++++++++++++++------ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 193e931..3c69e47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - A naming convention for CMake packages and their components. - Warning message if calling find_package() for a component within a CMake package with exactly matching names. +- A few more motivations and benefits. ### Changed - A, B and C CMake targets are renamed alpha, beta and gamma respectively. diff --git a/README.adoc b/README.adoc index 5faea2d..5b08fe9 100644 --- a/README.adoc +++ b/README.adoc @@ -44,9 +44,11 @@ native software with the following properties: In the current era of Modern {cpp}, the language itself leads a potential change in the mindset of {cpp} developers. Those changes are pervading the {cpp} ecosystem and tooling. -This document does not explore the language itself, but how to actually achieve and take advantage of the properties in the above list, by relying on modern tools. +This document does not explore the language itself, but how to actually achieve and take advantage of the properties in the above list, +by relying on modern tools. -Equally important is hopefully being able to **convey the appropriate mindset**, so those properties are both understood and desired. +Equally important is hopefully being able to **convey the appropriate mindset**, +so those properties are both understood and desired. There is no standard, no undisputed approach, and no silver-bullet. But there are solutions with varying levels of traction and growth, @@ -63,8 +65,12 @@ Reaching a satisfying yet adaptable infrastructure requires a lot of research, p Adopting a common and predefined plan greatly reduce the upfrong cost, with other advantages: * Having *good defaults* saves on initial setup and discussions (i.e. https://www.youtube.com/watch?v=XkDEzfpdcSg&feature=youtu.be&t=195[avoid bike-shedding]). The value added part is the software, not its building and packaging logic. -* Having a common base gives better chance at *interoperability*. -* Someone already *documented the process* for you, and you might not even have to maintain this document. +* Having a common base gives better chance at *interoperability* of discrete software packages. +* Sharing tools and practices gives more chance to find *native* (and better tested) *support* +with service providers (CI/CD, package hosting, static analysis, etc.). +* Someone already *documented the process* for you, with changes appearing in separate versions. +This allows to point to an unambigous reference, and eases onboarding new developpers, +without your organization having to produce and maintain an internal document. === Philosophy @@ -111,16 +117,27 @@ It is not trivial to distribute a project that builds easily on all environments * Project versions * Static / Shared libraries -* ABIs (compilers, and compilers' versions) +* Compilers, and compilers' versions * Standard library -* The gazillion compilation flags, which are also compiler dependent +* Language version +* The compilation flags galore, which are also compiler dependent ** Debug, Release, MinSize, and a few other build types ** Optimisation level ** ... +* Calling conventions * The upstream dependency-diamond (two separate components might rely on the same upstream library) * Code instrumentation * ... +When developing sociable code, this is a strong motivation to ensure the component +can easily build on consumers' site. +Ideally supporting as much as the _usual_ parameters as feasible. + +Additionally, once the build process is made reliable, repeatable and consistent, +releasing feels safer and tends to happen more often. + +In parallel, growing confidence in the ability for downstreams to conduct correct and identifiable +builds means less pressure to host and distribute a growing number of combinations with each iteration. + === Audience .TODO From 185e3812dd33fe5e4109eadae4222ea1a6fffe01 Mon Sep 17 00:00:00 2001 From: Adrien Date: Wed, 31 Mar 2021 14:35:01 +0200 Subject: [PATCH 16/19] Move "C++ special case" above "Philosophy". --- CHANGELOG.md | 1 + README.adoc | 65 ++++++++++++++++++++++++++-------------------------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c69e47..1d464b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ with exactly matching names. for each component config files. - CMake dependencies within the same repositories are now handled more uniformly with the external dependencies. +- Re-order "Philosophy" after "C++ special case". ### Fixed - Instruction to use `ARCH_INDEPENDENT` when producing a CMake version file for `INTERFACE` targets. diff --git a/README.adoc b/README.adoc index 5b08fe9..9a0bc23 100644 --- a/README.adoc +++ b/README.adoc @@ -72,6 +72,38 @@ with service providers (CI/CD, package hosting, static analysis, etc.). This allows to point to an unambigous reference, and eases onboarding new developpers, without your organization having to produce and maintain an internal document. +[[special_case]] +=== {cpp} special case === + +Many high level languages are able to provide sociable components quite naturally, without requiring to first agree on some external process. +Or they have one (a few) de-facto standard. + +In addition to the fact that the {cpp} standard does not make any prescription, {cpp} (as other native languages) is a special case. +It is not trivial to distribute a project that builds easily on all environments, while it is close to impossible to distribute pre-built objects for all environments due to the combinatorial explosion of build parameters: + +* Project versions +* Static / Shared libraries +* Compilers, and compilers' versions +* Standard library +* Language version +* The compilation flags galore, which are also compiler dependent +** Debug, Release, MinSize, and a few other build types +** Optimisation level +** ... +* Calling conventions +* The upstream dependency-diamond (two separate components might rely on the same upstream library) +* Code instrumentation +* ... + +When developing sociable code, this is a strong motivation to ensure the component +can easily build on consumers' site. +Ideally supporting as much as the _usual_ parameters as feasible. + +Additionally, once the build process is made reliable, repeatable and consistent, +releasing feels safer and tends to happen more often. + +In parallel, growing confidence in the ability for downstreams to conduct correct and identifiable +builds means less pressure to host and distribute a growing number of combinations with each iteration. + === Philosophy Producing sociable components might require some change in the usual approach: @@ -105,39 +137,6 @@ Let's establish them as goals for the process: The actual system will not be able to strictly enforce all goals at once, but they remain excellent parameters to consider along the way, in order to make informed engineering decisions and trade-offs. - -[[special_case]] -=== {cpp} special case === - -Many high level languages are able to provide sociable components quite naturally, without requiring to first agree on some external process. -Or they have one (a few) de-facto standard. - -In addition to the fact that the {cpp} standard does not make any prescription, {cpp} (as other native languages) is a special case. -It is not trivial to distribute a project that builds easily on all environments, while it is close to impossible to distribute pre-built objects for all environments due to the combinatorial explosion of build parameters: - -* Project versions -* Static / Shared libraries -* Compilers, and compilers' versions -* Standard library -* Language version -* The compilation flags galore, which are also compiler dependent -** Debug, Release, MinSize, and a few other build types -** Optimisation level -** ... -* Calling conventions -* The upstream dependency-diamond (two separate components might rely on the same upstream library) -* Code instrumentation -* ... - -When developing sociable code, this is a strong motivation to ensure the component -can easily build on consumers' site. -Ideally supporting as much as the _usual_ parameters as feasible. - -Additionally, once the build process is made reliable, repeatable and consistent, -releasing feels safer and tends to happen more often. + -In parallel, growing confidence in the ability for downstreams to conduct correct and identifiable -builds means less pressure to host and distribute a growing number of combinations with each iteration. - === Audience .TODO From 249942be0eee47e4f0076ceeb31eff16c3a8a173 Mon Sep 17 00:00:00 2001 From: Adrien Date: Wed, 31 Mar 2021 14:38:15 +0200 Subject: [PATCH 17/19] Fix a typo in changelog (missing '#'). --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d464b7..40a7dc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,7 @@ with the external dependencies. ### Fixed - Instruction to use `ARCH_INDEPENDENT` when producing a CMake version file for `INTERFACE` targets. -## Removed +### Removed - Conan recipe does not clone projects into a `cloned_repo` subfolder anymore. ## [0.7.4] - 2021-03-04 From 882d1bf90b700e72ca058eadbc66bb0b0ef97639 Mon Sep 17 00:00:00 2001 From: Adrien Date: Wed, 31 Mar 2021 14:45:34 +0200 Subject: [PATCH 18/19] Raise an exception in release script if remote url cannot be parsed. --- repo-admin/release.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/repo-admin/release.py b/repo-admin/release.py index 21ebc69..fbaa973 100755 --- a/repo-admin/release.py +++ b/repo-admin/release.py @@ -3,6 +3,7 @@ import argparse from datetime import date from packaging import version +import os import re import subprocess @@ -24,7 +25,11 @@ def previous_version(content): def comparison_url(previous, current=None): origin_remote = subprocess.check_output(["git", "remote", "get-url", "origin"]).decode('utf-8') - base = re.search("git@github.com:(.+)\.git", origin_remote).group(1) + match = re.search("git@github.com:(.+)\.git", origin_remote) + if match is None: + raise Exception("Remote '{}' does not have the expected format for a Github ssh url." + .format(origin_remote.rstrip(os.linesep))) + base = match.group(1) current = "v{}".format(current) if current else "HEAD" if previous: return "https://github.com/{}/compare/v{}...{}".format(base, previous, current) From 41474988100495bae55abbd7983e1537458a59d6 Mon Sep 17 00:00:00 2001 From: Adrien Date: Wed, 31 Mar 2021 14:55:54 +0200 Subject: [PATCH 19/19] Prepare release v0.8.0 --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40a7dc9..5f1c56e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] + +## [0.8.0] - 2021-03-31 ### Added - A naming convention for CMake packages and their components. - Warning message if calling find_package() for a component within a CMake package @@ -61,7 +63,8 @@ because recipe hash is sensitive to line-endings. ### Added - Initial release of the document -[Unreleased]: https://github.com/Adnn/ModernCppComponent/compare/v0.7.4...HEAD +[Unreleased]: https://github.com/Adnn/ModernCppComponent/compare/v0.8.0...HEAD +[0.8.0]: https://github.com/Adnn/ModernCppComponent/compare/v0.7.4...v0.8.0 [0.7.4]: https://github.com/Adnn/ModernCppComponent/compare/v0.7.3...v0.7.4 [0.7.3]: https://github.com/Adnn/ModernCppComponent/compare/v0.7.2...v0.7.3 [0.7.2]: https://github.com/Adnn/ModernCppComponent/compare/v0.7.1...v0.7.2