CMake Reminder
Build-Requirements of A Target
Everything that is needed to build that target.
- source-files
- include search-paths
- pre-processor macros
- link-dependencies
- compiler/linker-options
- compiler/linker-features(e.g. support for a C++-standard)
Usage-Requirements of A Target
Everything that is needed to (successfully) use that target.
As a dependency of another target.
- Same as Build-Requirements, but source-files are normally not required.
# Adding build-requirements
target_include_directories( <target> PRIVATE <include-search-dir>... )
target_compile_definitions( <target> PRIVATE <macro-definitions>... )
target_compile_options( <target> PRIVATE <compiler-option>... )
target_compile_features( <target> PRIVATE <feature>... )
target_sources( <target> PRIVATE <source-file>... )
target_link_libraries( <target> PRIVATE <dependency>... )
target_link_options( <target> PRIVATE <linker-option>... )
target_link_directories( <target> PRIVATE <linker-search-dir>... )
# Adding usage-requirements
target_include_directories( <target> INTERFACE <include-search-dir>... )
#...more...
#Adding build-and-usage requirements
target_include_directories( <target> PUBLIC <include-search-dir>... )
#...more...
⚠️ Although target_link_libraries can be used without these keywords, you should never forget to use these keywords in Modern CMake!
Keyword PUBLIC
, INTERFACE
, PRIVATE
in CMake:
PRIVATE
: refer to build-requirement for a target. Only affect the current target, invisible ouside the target.INTERFACE
: refer to usage-requirement for a target. propagate the dependency outside of the target. A dependency which is not used by the implementation of a library, but only by its headers should be specified as anINTERFACE
dependency.
for example: main.cpp depends on hello_world.lib, and main.cpp calls functions in hello.lib. So we should write
target_link_libraries(hello-world INTERFACE hello)
target_include_directories(hello-world INTERFACE hello)
PUBLIC
: refer to build & usage-requirement. propogate the dependecy outside of the target.
Keyword add_library
Normal Library
add_library(<name> [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
[<source>...])
STATIC
libraries are archives of object files for use when linking other targets.SHARED
libraries are linked dynamically and loaded at runtime.MODULE
libraries are plugins that are not linked into other targets but may be loaded dynamically at runtime using dlopen-like functionality.
Object Libraries
add_library(<name> OBJECT [<source>...])
- An
OBJECT
library compiles source files but does not archive or link their object files into a library.
Interface Libraries (refer to header-only lib)
add_library(<name> INTERFACE)
- An
INTERFACE
library target does not directly create build output, though it may have properties set on it and it may be installed, exported and imported.
Adding Build/Usage Requirements to Targets
- Usage-requirements of dependencies need to become build-requirements of dependent targets using the same commands.
- Since CMake 3.12 the
target_link_libraries
command can be used withOBJECT
targets.- Usage-requirements of dependencies can be propagated to
OBJECT
targets.
- Usage-requirements of dependencies can be propagated to
More about propagation OBJECT
peculiarities
A Quiz:
# A common object-library target.
add_library( commonObjLib OBJECT )
target_sources( commonObjLib PRIVATE "common.cpp" )
target_include_directories( commonObjLib PUBLIC "include/common" )
# A shared-library target.
add_library( sharedLib1 SHARED )
# Another object-library target...
add_library( objLib2 OBJECT )
target_sources( objLib2 PRIVATE "source2.cpp" )
target_include_directories( objLib2 PUBLIC "include2" )
# ... and yet another one.
add_library( objLib3 OBJECT )
target_sources( objLib3 PRIVATE "source3.cpp" )
target_include_directories( objLib3 PUBLIC "include3" )
# Dependency on 'commonObjLib'.
target_link_libraries( sharedLib1 PUBLIC commonObjLib )
target_link_libraries( objLib2 PRIVATE commonObjLib )
target_link_libraries( objLib3 INTERFACE commonObjLib )
# Create executables linked against these targets.
add_executable( exe1 )
add_executable( exe2 )
add_executable( exe3 )
target_sources( exe1 PRIVATE "main1.cpp" )
target_sources( exe2 PRIVATE "main2.cpp" )
target_sources( exe3 PRIVATE "main3.cpp" )
target_link_libraries( exe1 PRIVATE sharedLib1 )
target_link_libraries( exe2 PRIVATE objLib2 )
target_link_libraries( exe3 PRIVATE objLib3 )
Question1: What (local) include-directories does each individual target (exe1, exe2 and exe3) know during compilation?
Answer:
exe1 — . and include/common
exe2 — . and include2
exe3 — . and include3 and include/common
Question2: The generated output-binary of which executable target (exe1, exe2 or exe3) contains the object-files for common.cpp?
Answer:
None! — Only the output-binary of sharedLib
contains the object-files received from
commonObjLib.
The reason: Rules For Linking OBJECT
Library Targets
- Usage-requirements of any target (even
OBJECT
library targets) on the right-hand-side of target_link_libraries are propagated to theOBJECT
library target on the left-hand-side. - Object-files(.obj) are only ever propagated to direct dependants! And only, if that direct dependant is not an
OBJECT
library target itself.
# Knowing usage-requirements from 'anyTarget' when compiling to object-files,
# but not further propagating them to other targets.
# usage-requirement of anyTarget => build-requirements of objTarget
target_link_libraries( objTarget PRIVATE anyTarget )
# Not knowing usage-requirements from 'anyTarget' when compiling to object-files,
# but further propagating them to other targets.
# usage-requirement of anyTarget => usage-requirements of objTarget
target_link_libraries( objTarget INTERFACE anyTarget )
# Knowing usage-requirements from 'anyTarget' when compiling to object-files,
# and further propagating them to other targets.
# usage-requirement of anyTarget => usage/build-requirements of objTarget
target_link_libraries( objTarget PUBLIC anyTarget )
# Object-files are only propagated to the build-requirements of another target
# if that other target is not an OBJECT library target itself!
# Object-files of objTarget => build-requirements of nonObjTarget
# Object-files of objTarget !=> build/usage-requirements of otherObjTarget
target_link_libraries( nonObjTarget PRIVATE objTarget ) # Directly propagate object-files.
target_link_libraries( otherObjTarget PRIVATE objTarget ) # No propagation of object-files.
# No object-files can be propagated to the usage-requirements of any other target!
target_link_libraries( anyTarget INTERFACE objTarget ) # No indirect propagation of object-files.
# Object-files are propagated exactly as for PRIVATE propagation of object-files.
# Object-files of objTarget => build-requirements of nonObjTarget
# Object-files of objTarget !=> build/usage-requirements of otherObjTarget
target_link_libraries( nonObjTarget PUBLIC objTarget ) # Directly propagate object-files.
target_link_libraries( otherObjTarget PUBLIC objTarget ) # No propagation of object-files.
Creating Targets from OBJECT
Targets
- Object-files from the
OBJECT
target are the sources of theSHARED
library target.
Linking All Together Into Executables
include_directories( "../library/include" ) # This is ugly!
Avoid relative path. In mordern Cmake. Dependencies propagate their include-search paths automatically, together with all other usage-requirements.
add_compile_options()
, CMAKE_CXX_FLAGS
, target_compile_options()
add_compile_options()
does append to theCOMPILE_OPTIONS
directory property and is taken as default for allCOMPILE_OPTIONS
properties of targets coming after theadd_compile_options()
command. So make sureadd_compile_options()
command is before theadd_library()
/add_executable()
.Adds options to the compiler command line for targets in the current directory and below that are added after this command is invoked.
CMAKE_CXX_FLAGS
applies to all targets in the current CMakeLists.txt. Do not useCMAKE_CXX_FLAGS
. It’s not transitive, and is a global bulid requirements.target_compile_options()
: Add compile option with fine grained control of transition (Use this in the most of condition!).
#Dont do this!
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11" CACHE
STRING "C++ compile-flags" FORCE )
#Do this
target_compile_features( basicmath_ObjLib
PUBLIC cxx_constexpr )
VSCode相關設定
Configure arguments的寫法,以opencv為例:
-DOPENCV_EXTRA_MODULES_PATH=C:/Users/MSI/source/repos/opencv_contrib/modules
- 加在Cmake: Configure Args變數內
- 要加上-D
- 路徑linux型斜線,不加雙引號""