MegaDrive
On this page

CMake Reminder

Just a reminder for cmake usage.

Build-Requirements of A Target

Everything that is needed to build that target.

Usage-Requirements of A Target

Everything that is needed to (successfully) use that target.
As a dependency of another target.

# 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:

target_link_libraries(hello-world INTERFACE hello)
target_include_directories(hello-world INTERFACE hello)

Keyword add_library

Normal Library

add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            [<source>...])

Object Libraries

add_library(<name> OBJECT [<source>...])

Interface Libraries (refer to header-only lib)

add_library(<name> INTERFACE)

Adding Build/Usage Requirements to Targets

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

# 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

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()

#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

Reference