mirror of
https://github.com/boostorg/cmake.git
synced 2026-01-19 04:02:15 +00:00
Document enhanced scanner and add language tags
This commit is contained in:
125
developer.md
125
developer.md
@@ -13,7 +13,7 @@ name of the repository (or the directory name).
|
||||
For example, a `CMakeLists.txt` file for Boost.Core can be generated with
|
||||
`boostdep --cmake core`, and the result will be, as of this writing,
|
||||
|
||||
```
|
||||
```cmake
|
||||
# Generated by `boostdep --cmake core`
|
||||
# Copyright 2020 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
@@ -62,7 +62,7 @@ generated output provides a useful starting point. Its contents are explained
|
||||
below.
|
||||
|
||||
### Version Requirement
|
||||
```
|
||||
```cmake
|
||||
cmake_minimum_required(VERSION 3.5...3.16)
|
||||
```
|
||||
|
||||
@@ -72,7 +72,7 @@ error at configure time, and inability to proceed with building.
|
||||
|
||||
In addition, this number changes the behavior of newer CMake versions to
|
||||
attempt to be compatible with the stated version. If this only said
|
||||
```
|
||||
```cmake
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
```
|
||||
a newer version of CMake would have emulated version 3.5. The additional
|
||||
@@ -80,7 +80,7 @@ a newer version of CMake would have emulated version 3.5. The additional
|
||||
This is typically the latest version of CMake with which the `CMakeLists.txt`
|
||||
file has been tested. If you make changes to the file for other reasons, you
|
||||
may want to update the directive to, say,
|
||||
```
|
||||
```cmake
|
||||
cmake_minimum_required(VERSION 3.5...3.20)
|
||||
```
|
||||
You should avoid increasing the minimal CMake requirement above the Boost
|
||||
@@ -92,7 +92,7 @@ your library from the build with `-DBOOST_EXCLUDE_LIBRARIES`, which is not
|
||||
an ideal user experience.
|
||||
|
||||
### Project Declaration
|
||||
```
|
||||
```cmake
|
||||
project(boost_core VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX)
|
||||
```
|
||||
|
||||
@@ -111,7 +111,7 @@ Boost version (such as `1.77.0`.)
|
||||
If your library is included directly in a user project with
|
||||
`add_subdirectory`, `BOOST_SUPERPROJECT_VERSION` will not be set and
|
||||
the project version will be empty, as if it weren't given:
|
||||
```
|
||||
```cmake
|
||||
project(boost_core LANGUAGES CXX)
|
||||
```
|
||||
This is usually what one wants. Since manually maintaining a version
|
||||
@@ -125,7 +125,7 @@ C++. C is only needed if the library has C source files, which a
|
||||
header-only library does not have.
|
||||
|
||||
### Library Target Declaration
|
||||
```
|
||||
```cmake
|
||||
add_library(boost_core INTERFACE)
|
||||
```
|
||||
|
||||
@@ -133,7 +133,7 @@ The first `add_library` declares the library target, which by convention
|
||||
is `boost_libname`, same as the project name. `INTERFACE` means that
|
||||
this library is header-only and requires no building.
|
||||
|
||||
```
|
||||
```cmake
|
||||
add_library(Boost::core ALIAS boost_core)
|
||||
```
|
||||
|
||||
@@ -145,7 +145,7 @@ alphanumeric `boost_core` may refer to either a target or to a library
|
||||
on disk named f.ex. `libboost_core.so`.
|
||||
|
||||
### Include Directory Declaration
|
||||
```
|
||||
```cmake
|
||||
target_include_directories(boost_core INTERFACE include)
|
||||
```
|
||||
|
||||
@@ -156,18 +156,18 @@ location of the current `CMakeLists.txt` file.)
|
||||
|
||||
If you are familiar with CMake, your first impulse would be to declare this
|
||||
line wrong, and replace it with
|
||||
```
|
||||
```cmake
|
||||
target_include_directories(boost_core INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
```
|
||||
or
|
||||
```
|
||||
```cmake
|
||||
target_include_directories(boost_core INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
$<INSTALL_INTERFACE:include>)
|
||||
```
|
||||
or perhaps
|
||||
```
|
||||
```cmake
|
||||
target_include_directories(boost_core INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
|
||||
@@ -180,7 +180,7 @@ the value of the include path to something like that last alternative
|
||||
`BOOST_INSTALL_LAYOUT` and `BOOST_INSTALL_INCLUDE_SUBDIR`.)
|
||||
|
||||
### Dependencies
|
||||
```
|
||||
```cmake
|
||||
target_link_libraries(boost_core
|
||||
INTERFACE
|
||||
Boost::assert
|
||||
@@ -213,18 +213,47 @@ include path because it doesn't require any compilation. This is what the
|
||||
`INTERFACE` keyword means - it sets the "usage requirements" of the target,
|
||||
which are propagated upwards to its users.)
|
||||
|
||||
The exact form of the directive, with each `Boost::libname` target on its
|
||||
own line, with nothing else, is significant. (In particular, the closing
|
||||
parenthesis should not be placed on the same line as the last target.)
|
||||
This requirement is imposed by the behavior of the user-settable
|
||||
`BOOST_INCLUDE_LIBRARIES` option of the superproject, which requests only
|
||||
the listed libraries and their dependencies to be configured, built, and/or
|
||||
installed. To determine the dependencies, a simple-minded parser scans the
|
||||
`CMakeLists.txt` files, looking for strings matching `Boost::libname` on
|
||||
their own line.
|
||||
Note that the exact form of the directive, with each `Boost::libname` target on
|
||||
its own line, is not necessary. You could as well put them on a singe line:
|
||||
`target_link_libraries(boost_core INTERFACE Boost::assert Boost::config Boost::static_assert)`
|
||||
This dependency specification influences the behavior of the user-settable
|
||||
`BOOST_INCLUDE_LIBRARIES` option of the superproject, which requests only the
|
||||
listed libraries and their dependencies to be configured, built, and/or
|
||||
installed.
|
||||
To determine the dependencies, a simple parser scans the `CMakeLists.txt`
|
||||
files, looking for strings matching `Boost::libname`, excluding the name of the
|
||||
current library and those appearing in comments. Additionally, you can use
|
||||
pragmas to influence the scanner:
|
||||
|
||||
```cmake
|
||||
# Ignored by parser if in library folder "core"
|
||||
add_library(Boost::core ALIAS boost_core)
|
||||
# Parser adds "Boost::assert" "Boost::dummy"
|
||||
target_link_libraries(boost_core INTERFACE Boost::assert Boost::dummy
|
||||
# Only "Boost::config added for those 2 lines
|
||||
Boost::config # Boost::static_assert
|
||||
# Boost::ignored
|
||||
)
|
||||
|
||||
# Current dependencies: Boost::assert, Boost::dummy Boost::config
|
||||
...
|
||||
# Pragmas using Boost-Include, Boost-Exclude with or without colons and/or whitespace
|
||||
# Boost-Include: Boost::filesystem
|
||||
# Boost-Exclude Boost::dummy
|
||||
|
||||
# Final dependencies: Boost::assert, Boost::config, Boost::filesystem
|
||||
```
|
||||
|
||||
This is useful if the parser misdetects a dependency (please open an issue)
|
||||
or e.g. for optional dependencies.
|
||||
|
||||
Dependencies in `test/CMakeLists.txt` (and subfolders) are handled in the same
|
||||
way, except their tests won't be build and those libraries won't be installed
|
||||
unless they are dependencies in the root CMakeLists.txt of the current library
|
||||
or any (transitive) dependency of those.
|
||||
|
||||
### Testing Support
|
||||
```
|
||||
```cmake
|
||||
if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt")
|
||||
|
||||
add_subdirectory(test)
|
||||
@@ -251,7 +280,7 @@ or desired.
|
||||
If your library has a `test/CMakeLists.txt` file that is not intended to be
|
||||
used from the Boost superproject, and is incompatible with it, replace this
|
||||
block with either
|
||||
```
|
||||
```cmake
|
||||
if(BUILD_TESTING AND CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||
|
||||
add_subdirectory(test)
|
||||
@@ -261,7 +290,7 @@ endif()
|
||||
when your test suite is only intended to be used when your library is the
|
||||
root project (that's usually the case, so this option is the recommended one),
|
||||
or
|
||||
```
|
||||
```cmake
|
||||
if(BUILD_TESTING AND NOT BOOST_SUPERPROJECT_VERSION)
|
||||
|
||||
add_subdirectory(test)
|
||||
@@ -289,7 +318,7 @@ Let the superproject handle it.
|
||||
|
||||
If your library needs C++11 or above, you can declare this requirement by
|
||||
adding the following directive:
|
||||
```
|
||||
```cmake
|
||||
target_compile_features(boost_libname INTERFACE cxx_std_11)
|
||||
```
|
||||
(use `cxx_std_14` for C++14, `cxx_std_17` for C++17, and so on.)
|
||||
@@ -313,7 +342,7 @@ Many library authors who use CMake, however, add development-centric
|
||||
functionality to their `CMakeLists.txt` file; you might already have. In this
|
||||
case, try to keep the `CMakeLists.txt` portions described so far as close to
|
||||
unchanged as possible, and at the end, add a section guarded with
|
||||
```
|
||||
```cmake
|
||||
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||
|
||||
# Functionality enabled only when we're the root project
|
||||
@@ -333,7 +362,7 @@ Even if your library requires compilation, you can still use
|
||||
`boostdep --cmake libname` at least as a starting point. We'll take
|
||||
Timer as an example, with the output of `boostdep --cmake timer` given
|
||||
below:
|
||||
```
|
||||
```cmake
|
||||
# Generated by `boostdep --cmake timer`
|
||||
# Copyright 2020 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
@@ -386,7 +415,7 @@ We won't be repeating the explanations of the sections that match the
|
||||
header-only case, and will only focus on the differences.
|
||||
|
||||
### Source Files
|
||||
```
|
||||
```cmake
|
||||
add_library(boost_timer
|
||||
src/auto_timers_construction.cpp
|
||||
src/cpu_timer.cpp
|
||||
@@ -400,7 +429,7 @@ the contents of your `src` subdirectory (but ignores any subdirectories.)
|
||||
Since Timer is a simple library, this works as-is. Many compiled libraries
|
||||
however might require adjusting the source file list, or choosing it based
|
||||
on the platform. For example, Thread needs something like
|
||||
```
|
||||
```cmake
|
||||
if(BOOST_THREAD_THREADAPI STREQUAL win32)
|
||||
|
||||
set(THREAD_SOURCES
|
||||
@@ -428,7 +457,7 @@ The logic for choosing the source files is already spelled out in your
|
||||
|
||||
If your library has C source files, you'll need to also enable C as a
|
||||
language in your project declaration:
|
||||
```
|
||||
```cmake
|
||||
project(boost_container VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES C CXX)
|
||||
```
|
||||
although `boostdep` might already have done so for you.
|
||||
@@ -438,7 +467,7 @@ or a shared library depending on whether `BUILD_SHARED_LIBS` is set to `ON`
|
||||
or `OFF`. This is idiomatic CMake behavior and is what we want.
|
||||
|
||||
### Directive Scope
|
||||
```
|
||||
```cmake
|
||||
target_include_directories(boost_timer PUBLIC include)
|
||||
```
|
||||
|
||||
@@ -446,7 +475,7 @@ The only difference with the header-only case is the use of `PUBLIC` instead
|
||||
of `INTERFACE`. `PUBLIC` applies to both the library and its dependents; in
|
||||
`b2` terms it declares both a requirement and a usage-requirement.
|
||||
|
||||
```
|
||||
```cmake
|
||||
target_link_libraries(boost_timer
|
||||
PUBLIC
|
||||
Boost::config
|
||||
@@ -467,7 +496,7 @@ subdirectory in the `PUBLIC` section, and those referred to from the `src`
|
||||
subdirectory in the `PRIVATE` section.
|
||||
|
||||
### Compile Definitions
|
||||
```
|
||||
```cmake
|
||||
target_compile_definitions(boost_timer
|
||||
PUBLIC BOOST_TIMER_NO_LIB
|
||||
PRIVATE BOOST_TIMER_SOURCE
|
||||
@@ -480,7 +509,7 @@ to disable autolink and `BOOST_TIMER_SOURCE` when compiling the library to
|
||||
properly declare exported functions as exported (as opposed to imported,
|
||||
which will be the case when using the library.)
|
||||
|
||||
```
|
||||
```cmake
|
||||
if(BUILD_SHARED_LIBS)
|
||||
target_compile_definitions(boost_timer PUBLIC BOOST_TIMER_DYN_LINK)
|
||||
else()
|
||||
@@ -508,7 +537,7 @@ library targets, `boost_serialization` and `boost_wserialization`, and the
|
||||
procedure to install them entails adding
|
||||
[the following section](https://github.com/boostorg/serialization/blob/337b3fbc7c4648d6f95f863546b9482500c8dec5/CMakeLists.txt#L116-L118)
|
||||
to `CMakeLists.txt`:
|
||||
```
|
||||
```cmake
|
||||
if(BOOST_SUPERPROJECT_VERSION AND NOT CMAKE_VERSION VERSION_LESS 3.13)
|
||||
boost_install(TARGETS boost_serialization boost_wserialization
|
||||
VERSION ${BOOST_SUPERPROJECT_VERSION} HEADER_DIRECTORY include)
|
||||
@@ -528,7 +557,7 @@ than one library, see [Boost.Test](https://github.com/boostorg/test/blob/bce2d24
|
||||
|
||||
If your library uses multiple threads or threading primitives, you need to
|
||||
add the following snippet to your `CMakeLists.txt` file:
|
||||
```
|
||||
```cmake
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
```
|
||||
@@ -558,7 +587,7 @@ The recommended way to provide such optional functionality is to allow user
|
||||
configuration with sensible defaults, as shown in the following example that
|
||||
allows optional use of ZLib:
|
||||
|
||||
```
|
||||
```cmake
|
||||
find_package(ZLIB QUIET) # Look for ZLib
|
||||
|
||||
option(BOOST_MYLIB_ENABLE_ZLIB "Boost.MyLib: enable ZLib support" ${ZLIB_FOUND})
|
||||
@@ -629,7 +658,7 @@ uses the library with `add_subdirectory`. Typically, the options people add
|
||||
to their libraries are only relevant when the library is the root project.)
|
||||
|
||||
Definitely don't do this:
|
||||
```
|
||||
```cmake
|
||||
option(BOOST_MYLIB_MYOPTION "" ON)
|
||||
|
||||
if(BOOST_MYLIB_MYOPTION AND NOT BOOST_SUPERPROJECT_VERSION)
|
||||
@@ -642,7 +671,7 @@ endif()
|
||||
This displays the option, but makes it do nothing. Instead, either put the
|
||||
option declaration inside an `if`, or use
|
||||
[`CMakeDependentOption`](https://cmake.org/cmake/help/latest/module/CMakeDependentOption.html):
|
||||
```
|
||||
```cmake
|
||||
include(CMakeDependentOption)
|
||||
cmake_dependent_option(BOOST_MYLIB_MYOPTION "" ON "NOT BOOST_SUPERPROJECT_VERSION" OFF)
|
||||
|
||||
@@ -694,7 +723,7 @@ project/library, such as `boost_mylib-mytarget`.
|
||||
`add_test` does anything. Unless `BUILD_TESTING` is `ON`, to save time, you
|
||||
should avoid creating any tests or targets on which they depend. Usually, this
|
||||
translates to
|
||||
```
|
||||
```cmake
|
||||
if(BUILD_TESTING)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
@@ -709,7 +738,7 @@ one would write in C++ `foo? "bar": "baz"`, one could write in CMake
|
||||
|
||||
Don't do this. It's not the same. Generator expressions are evaluated in the
|
||||
generate phase, which happens after the configure phase. If you do
|
||||
```
|
||||
```cmake
|
||||
target_compile_definitions(boost_mylib PUBLIC $<IF:$<BOOL:${BUILD_SHARED_LIBS}>,BOOST_MYLIB_DYN_LINK,BOOST_MYLINK_STATIC_LINK>)
|
||||
```
|
||||
(and assuming `BUILD_SHARED_LIBS` is `ON`), you're not setting the
|
||||
@@ -719,7 +748,7 @@ to `$<IF:$<BOOL:ON>,BOOST_MYLIB_DYN_LINK,BOOST_MYLINK_STATIC_LINK>`.
|
||||
Yes, it will still be evaluated to the right thing during generation, but it's
|
||||
better to perform evaluations that only depend on configuration-time values at
|
||||
configuration time and write the less "clever"
|
||||
```
|
||||
```cmake
|
||||
if(BUILD_SHARED_LIBS)
|
||||
target_compile_definitions(boost_mylib PUBLIC BOOST_MYLIB_DYN_LINK)
|
||||
else()
|
||||
@@ -736,7 +765,7 @@ Boost with CMake (and optionally, running the tests, if one has a few days
|
||||
to spare).
|
||||
|
||||
The building procedure would generally involve issuing (from the Boost root)
|
||||
```
|
||||
```bash
|
||||
mkdir __build
|
||||
cd __build
|
||||
cmake <configuration options> ..
|
||||
@@ -748,7 +777,7 @@ configuration options in subdirectories of the "stage" directory, by default
|
||||
`stage/lib` and `stage/bin`.
|
||||
|
||||
Subsequent installation would be performed with
|
||||
```
|
||||
```bash
|
||||
cmake --build . --target install
|
||||
```
|
||||
assuming that `CMAKE_INSTALL_PREFIX` was set beforehand to the desired
|
||||
@@ -760,19 +789,19 @@ and installation procedure would need to be performed twice, once with
|
||||
`--config RelWithDebInfo`, as desired.)
|
||||
|
||||
Testing the entire Boost would be performed with
|
||||
```
|
||||
```bash
|
||||
cmake -DBUILD_TESTING=ON ..
|
||||
cmake --build . --target tests -j <threads>
|
||||
ctest --output-on-failure -j <threads>
|
||||
```
|
||||
|
||||
Again, when using the Visual Studio generator, this would be
|
||||
```
|
||||
```bash
|
||||
cmake --build . --target tests -j <threads> --config Debug
|
||||
ctest --output-on-failure -j <threads> -C Debug
|
||||
```
|
||||
resp.
|
||||
```
|
||||
```bash
|
||||
cmake --build . --target tests -j <threads> --config Release
|
||||
ctest --output-on-failure -j <threads> -C Release
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user