CMake Basic Notes
Basic Build System
Build for executable:
cmake_minimum_required(VERSION 2.8.9)
project(directory_test)
add_definitions(-DUSEXX)
add_compile_options(-std=c++11 -Wall -Wextra)
# Bring the headers, such as Student.h into the project
include_directories(include)
# Can manually add the sources using the set command as follows:
# set(SOURCES src/mainApp.cpp src/Student.cpp)
# However, the file(GLOB...) allows for wildcard additions:
file(GLOB SOURCES "src/*.cpp")
message(STATUS "CMake demo: build for executable")
add_executable(testStudent ${SOURCES})
build for library
cmake_minimum_required(VERSION 2.8.9)
project(directory_test)
set(CMAKE_BUILD_TYPE Release)
# Bring the headers, such as Student.h into the project
include_directories(include)
# However, the file(GLOB...) allows for wildcard additions:
file(GLOB SOURCES "src/*.cpp")
# Generate the shared library from the sources
# flag: SHARED, STATIC, MODULE
add_library(testStudent SHARED ${SOURCES})
message(STATUS "CMake demo: build for library")
# Set the location for library installation -- i.e., /usr/lib in this case
# not really necessary in this example. Use "sudo make install" to apply
install(TARGETS testStudent DESTINATION /usr/lib)
use a shared or static library
cmake_minimum_required(VERSION 2.8.9)
project(TestLibrary)
# For the shared library:
set(PROJECT_LINK_LIBS libTestStudent.so)
link_directories(~/exploringBB/extras/cmake/studentLib_shared/build)
# For the static library:
# set (PROJECT_LINK_LIBS libTestStudent.a)
# link_directories(~/exploringBB/extras/cmake/studentLib_static/build)
include_directories(~/exploringBB/extras/cmake/studentLib_shared/include)
add_executable(libTest libTest.cpp)
target_link_libraries(libTest ${PROJECT_LINK_LIBS} )
message(STATUS "CMake demo: use library")
set output of library
# 指定lib输出目录
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# 指定版本
set(DEMO5_VERSION_MAJOR 1)
set(DEMO5_VERSION_MINOR 1)
set(DEMO5_VERSION_PATCH 1)
set(DEMO5_VERSION ${DEMO5_VERSION_MAJOR}.${DEMO5_VERSION_MINOR}.${DEMO5_VERSION_PATCH})
aux_source_directory(. SRC_LIST)
add_library(demo5 SHARED ${SRC_LIST})
set_target_properties(
demo5 PROPERTIES
VERSION ${DEMO5_VERSION}
SOVERSION ${DEMO5_VERSION_MAJOR}
)
build for library and executable
cmake_minimum_required(VERSION 3.5)
project(MiniSat VERSION 2.2 LANGUAGES CXX)
add_library(libMiniSat STATIC
miniSat/core/Solver.cc
miniSat/utils/Options.cc
miniSat/utils/System.cc
miniSat/simp/SimpSolver.cc
)
target_compile_features(libMiniSat
PUBLIC
cxx_attributes
cxx_defaulted_functions
cxx_deleted_functions
cxx_final
)
target_include_directories(libMiniSat PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(libMiniSat PUBLIC __STDC_LIMIT_MACROS __STDC_FORMAT_MACROS)
# Also build the two MiniSat executables
add_executable(miniSat miniSat/core/Main.cc)
target_link_libraries(miniSat libMiniSat)
add_executable(miniSat-simp miniSat/simp/Main.cc)
target_link_libraries(miniSat-simp libMiniSat)
Basic Options
make VERBOSE=1
Standard options:
-DCMAKE_BUILD_TYPE=
Pick from Release, RelWithDebInfo, Debug, or sometimes more-DCMAKE_INSTALL_PREFIX=
/usr/local (the default), ~/.local-D BUILD_SHARED_LIBS=
--trace
print every line of CMake
Flow Control
if control
- Unary: NOT, TARGET, EXISTS (file), DEFINED
- Binary: STREQUAL, AND, OR, MATCHES(regular expression), VERSION_LESS, VERSION_LESS_EQUAL
if(WIN32)
message("This is win32 platform")
else()
message("This is not win32 platform")
endif()
foreach control
set(FOR_LIST demo1.cpp demo2.cpp demo3.cpp)
foreach(f ${FOR_LIST})
message("now is file: " ${f})
endforeach ()
while control
set(A "1")
set(B "1")
while(A LESS "1000000")
message("${A}") # Print A
math(EXPR T "${A} + ${B}") # Add values of A and B; store result in T
set(A "${B}") # Assign the value of B to A
set(B "${T}") # Assign the value of T to B
endwhile()
function control
Basic Usage of Function
function(doubleIt VALUE)
math(EXPR RESULT "${VALUE} * 2")
message("${RESULT}")
endfunction()
doubleIt("4") # Prints: 8
function(doubleIt VARNAME VALUE)
math(EXPR RESULT "${VALUE} * 2")
set(${VARNAME} "${RESULT}" PARENT_SCOPE)
endfunction()
doubleIt(RESULT "4") # Tell the function to set the variable named RESULT
message("${RESULT}") # Prints: 8
function(doubleEach)
foreach(ARG ${ARGN}) # Iterate over each argument
math(EXPR N "${ARG} * 2") # Double ARG numeric value
message("${N}") # Print N
endforeach()
endfunction()
doubleEach(5 6 7 8) # Prints 10, 12, 14, 16 on separate lines
Parse Arguments of Function
function(COMPLEX)
cmake_parse_arguments(
COMPLEX_PREFIX
"SINGLE;ANOTHER"
"ONE_VALUE;ALSO_ONE_VALUE"
"MULTI_VALUES"
${ARGN}
)
endfunction()
complex(SINGLE ONE_VALUE value MULTI_VALUES some other values)
Inside the function after this call, you'll find:
COMPLEX_PREFIX_SINGLE = TRUE
COMPLEX_PREFIX_ANOTHER = FALSE
COMPLEX_PREFIX_ONE_VALUE = "value"
COMPLEX_PREFIX_ALSO_ONE_VALUE = <UNDEFINED>
COMPLEX_PREFIX_MULTI_VALUES = "some;other;values"
Useful Command
Checking Command
# does this system provide the log and exp functions?
include(CheckFunctionExists)
check_function_exists(log HAVE_LOG)
check_function_exists(exp HAVE_EXP)
Testing Command
#define a macro to simplify adding tests, then use it
macro(do_test arg result)
add_test(TutorialComp${arg} Tutorial ${arg})
set_tests_properties(TutorialComp${arg}
PROPERTIES PASS_REGULAR_EXPRESSION ${result})
endmacro(do_test)
# do a bunch of result based tests
do_test(25 "25 is 5")
do_test(-25 "-25 is 0")
include(CTest)
# does the application run
add_test (TutorialRuns Tutorial 25)
# does it sqrt of 25
add_test (TutorialComp25 Tutorial 25)
set_tests_properties (TutorialComp25 PROPERTIES PASS_REGULAR_EXPRESSION "25 is 5")
# does it handle negative numbers
add_test (TutorialNegative Tutorial -25)
set_tests_properties (
TutorialNegative PROPERTIES
PASS_REGULAR_EXPRESSION "-25 is 0"
)
# does it handle small numbers
add_test (TutorialSmall Tutorial 0.0001)
set_tests_properties (
TutorialSmall PROPERTIES
PASS_REGULAR_EXPRESSION "0.0001 is 0.01"
)
# does the usage message work?
add_test (TutorialUsage Tutorial)
set_tests_properties (
TutorialUsage PROPERTIES
PASS_REGULAR_EXPRESSION "Usage:.*number"
)
Option Command
# 是否使用我们自己的函数?
option(USE_MATH
"Use tutorial provided math implementation" ON)
# add the MathFunctions library?
if(USE_MATH)
include_directories("${PROJECT_SOURCE_DIR}/MathFunctions")
add_subdirectory(MathFunctions)
set(EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif(USE_MATH)
# add the executable
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial ${EXTRA_LIBS})
Math Command
set(ARGS "EXPR;T;1 + 1")
math(${ARGS}) # Equivalent to calling math(EXPR T "1 + 1")
List Command
set(MY_LIST These are separate arguments)
list(REMOVE_ITEM MY_LIST "separate") # Removes "separate" from the list
message("${MY_LIST}") # Prints: These;are;arguments
Package Command
# build a CPack driven installer package
include(InstallRequiredSystemLibraries)
set(CPACK_RESOURCE_FILE_LICENSE
"${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
include(CPack)
cpack --config CPackConfig.cmake
cpack --config CPackSourceConfig.cmake
Install Command
install binaries
INSTALL(TARGETS targets...
[[ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION < dir >]
[PERMISSIONS permissions...]
[CONFIGURATIONS
[Debug|Release|...]]
[COMPONENT < component >]
[OPTIONAL]
] [...])
INSTALL(TARGETS myRun myLib myStaticLib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION libStatic)
install normal files
INSTALL(FILES files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/t2)
install scripts
INSTALL(PROGRAMS files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
INSTALL(PROGRAMS runHello.sh DESTINATION bin)
install directories
INSTALL(DIRECTORY dirs... DESTINATION <dir>
[FILE_PERMISSIONS permissions...]
[DIRECTORY_PERMISSIONS permissions...]
[USE_SOURCE_PERMISSIONS]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[[PATTERN <pattern> | REGEX <regex>]
[EXCLUDE] [PERMISSIONS permissions...]] [...])
INSTALL(DIRECTORY icons scripts/ DESTINATION share/myProj
PATTERN "CVS" EXCLUDE
PATTERN "scripts/*"
PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
GROUP_EXECUTE GROUP_READ)
Install Demo
find_package(Bar 2.0 REQUIRED)
add_library(Foo ...)
target_link_libraries(Foo PRIVATE Bar::Bar)
install(TARGETS Foo EXPORT FooTargets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)
install(EXPORT FooTargets
FILE FooTargets.cmake
NAMESPACE Foo::
DESTINATION lib/cmake/Foo
)
find packages
find modules
cmake –-help-module-list
ls /usr/share/cmake/Modules/
cmake --help-module FindBZip2
Basic Usage of Find
project(helloWorld)
add_executable(helloWorld hello.c)
find_package (BZip2)
if (BZIP2_FOUND)
include_directories(${BZIP_INCLUDE_DIRS})
target_link_libraries(helloWorld ${BZIP2_LIBRARIES})
endif (BZIP2_FOUND)
find_path(Foo_INCLUDE_DIR foo.h)
find_library(Foo_LIBRARY foo)
mark_as_advanced(Foo_INCLUDE_DIR Foo_LIBRARY)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Foo
REQUIRED_VARS Foo_LIBRARY Foo_INCLUDE_DIR
)
if(Foo_FOUND AND NOT TARGET Foo::Foo)
add_library(Foo::Foo UNKNOWN IMPORTED)
set_target_properties(Foo::Foo PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
IMPORTED_LOCATION "${Foo_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${Foo_INCLUDE_DIR}"
)
endif()
Find CMake
Find.cmake: add find module for project
# cmake/FindDEMO9LIB.cmake
# 辅助输出信息
message("now using FindDEMO9LIB.cmake find demo9 lib")
# 将demo9.h文件路径赋值给DEMO9LIB_INCLUDE_DIR
FIND_PATH(DEMO9LIB_INCLUDE_DIR demo9.h /usr/include/demo9/
/usr/local/demo9/include/)
message("./h dir ${DEMO9LIB_INCLUDE_DIR}")
# 将libDemo9_lib.a文件路径赋值给DEMO9LIB_LIBRARY
FIND_LIBRARY(DEMO9LIB_LIBRARY libDemo9_lib.a /usr/local/demo9/lib/)
message("lib dir: ${DEMO9LIB_LIBRARY}")
if(DEMO9LIB_INCLUDE_DIR AND DEMO9LIB_LIBRARY)
# 设置变量结果
set(DEMO9LIB_FOUND TRUE)
endif(DEMO9LIB_INCLUDE_DIR AND DEMO9LIB_LIBRARY)
Full Find Demo
# CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(demo9)
# create libDemo9_lib.a
set(SRC_LIB demo9.cpp)
add_library(demo9_lib STATIC ${SRC_LIB})
# install it
install(TARGETS demo9_lib DESTINATION demo9/lib)
install(FILES demo9.h DESTINATION demo9/include)
# create demo9_main executable
set(SRC_EXE demo9_main.cpp)
# set demo9_lib cmake module path
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
message("cmake_module_path: ${CMAKE_MODULE_PATH}")
find_package(DEMO9LIB)
if(DEMO9LIB_FOUND)
add_executable(demo9_main ${SRC_EXE})
message("found demo9 ${DEMO9LIB_INCLUDE_DIR} ${DEMO9LIB_LIBRARY}")
include_directories(${DEMO9LIB_INCLUDE_DIR})
target_link_libraries(demo9_main ${DEMO9LIB_LIBRARY})
else()
message("not found DEMO9LIB_FOUND")
endif(DEMO9LIB_FOUND)
Useful Tools
ldd and ar
ldd library.so
ar -t library.a
Makefile
# -----------------------------------------------------------------------------
# CMake project wrapper Makefile ----------------------------------------------
# -----------------------------------------------------------------------------
SHELL := /bin/bash
RM := rm -rf
MKDIR := mkdir -p
all: ./build/Makefile
@ $(MAKE) -C build
./build/Makefile:
@ ($(MKDIR) build > /dev/null)
@ (cd build > /dev/null 2>&1 && cmake ..)
clean:
@ ($(MKDIR) build > /dev/null)
@ (cd build > /dev/null 2>&1 && cmake .. > /dev/null 2>&1)
@- $(MAKE) --silent -C build clean || true
@- $(RM) ./build/Makefile
@- $(RM) ./build/src
@- $(RM) ./build/test
@- $(RM) ./build/CMake*
@- $(RM) ./build/cmake.*
@- $(RM) ./build/*.cmake
@- $(RM) ./build/*.txt
ifeq ($(findstring clean,$(MAKECMDGOALS)),)
$(MAKECMDGOALS): ./build/Makefile
@ $(MAKE) -C build $(MAKECMDGOALS)
endif
Config Command
Version Config
cmake_minimum_required(VERSION 3.1)
if(${CMAKE_VERSION} VERSION_LESS 3.13)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.13)
endif()
Project Config
project(MyProject VERSION 1.0 DESCRIPTION "Very nice project" LANGUAGES CXX)
Environment Config
set(ENV{variable_name} value)
and get $ENV{variable_name}
environment variables
Library for Clients Usage
include(CMakePackageConfigHelpers)
write_basic_package_version_file("FooConfigVersion.cmake"
VERSION ${Foo_VERSION}
COMPATIBILITY SameMajorVersion
)
install(FILES "FooConfig.cmake" "FooConfigVersion.cmake"
DESTINATION lib/cmake/Foo
)
include(CMakeFindDependencyMacro)
find_dependency(Bar 2.0)
include("${CMAKE_CURRENT_LIST_DIR}/FooTargets.cmake")
Test Setting
set(CTEST_SOURCE_DIRECTORY "/source")
set(CTEST_BINARY_DIRECTORY "/binary")
set(ENV{CXXFLAGS} "--coverage")
set(CTEST_CMAKE_GENERATOR "Ninja")
set(CTEST_USE_LAUNCHERS 1)
set(CTEST_COVERAGE_COMMAND "gcov")
set(CTEST_MEMORYCHECK_COMMAND "valgrind")
#set(CTEST_MEMORYCHECK_TYPE "ThreadSanitizer")
ctest_start("Continuous")
ctest_configure()
ctest_build()
ctest_test()
ctest_coverage()
ctest_memcheck()
ctest_submit()
macro(package_add_test TESTNAME)
add_executable(${TESTNAME} ${ARGN})
target_link_libraries(${TESTNAME} gtest gmock gtest_main)
add_test(${TESTNAME} COMMAND ${TESTNAME})
set_target_properties(${TESTNAME} PROPERTIES FOLDER tests)
endmacro()
package_add_test(test1 test1.cpp)
Generator Expression
Most CMake commands happen at configure time, include the if statements seen above. Generator expressions were added at runtime. They are evaluated in target properties:
- If you want to put a compile flag only for the DEBUG configuration
- Limiting an item to a certain language only, such as CXX
- Accessing configuration dependent properties, like target file location
- Giving a different location for build and install directories
target_include_directories(MyTarget PUBLIC
$<BUILD_INTERFACE:"${CMAKE_CURRENT_SOURCE_DIR}/include">
$<INSTALL_INTERFACE:include>
)
Submodule and Dependencies
find_package(Git QUIET)
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
execute_process(COMMAND
${GIT_EXECUTABLE} submodule update --init --recursive
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} RESULT_VARIABLE GIT_SUBMOD_RESULT
)
if(NOT GIT_SUBMOD_RESULT EQUAL "0")
message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}")
endif()
endif()
CMake Patterns
Modern CMake
Modern CMake is all about targets and properties.
Constructors of Targets:
- add_executable()
- add_library()
Member variables of Targets:
- Target properties
Member functions:
- get_target_property()
- set_target_properties()
- get_property(TARGET)
- set_property(TARGET)
- target_compile_definitions()
- target_compile_features()
- target_compile_options()
- target_include_directories()
- target_link_libraries()
- target_sources()
Interface vs Private
interface properties model usage requirements, whereas private properties model build requirements of targets.
Nice Patterns
- Think in targets (Object-Oriented)
- Export your interface: You should be able to run from build or install
- Write a Config.cmake file: This is what a library author should do to support clients
- Make ALIAS targets to keep usage consistent
- Combine common functionality into clearly documented functions
- Use lowercase function names
- Upper case is for variables
- Use cmake_policy and/or range of versions
Anti Patterns
- Do not use global functions: e.g
link_directories
,include_libraries
- Don't add unneeded PUBLIC requirements e.g
-Wall
- Don't GLOB files
- Link to built files directly: Always link to targets if available
- Never skip PUBLIC/PRIVATE when linking
Makefile Notes
Makefile Macro
foo := a.o b.o c.o
bar := $(foo:.o=.c)
first_second = Hello
a = first
b = second
all = $($a_$b)
这里的 $a_$b
组成了 first_second
, 于是 $(all)
的值就是 Hello
.
Built-in Makefile Macro
- AR: 函数库打包程序. 默认命令是
ar
. - AS: 汇编语言编译程序. 默认命令是
as
. - CC: C 语言编译程序. 默认命令是
cc
. - CXX: C++语言编译程序. 默认命令是
g++
. - CO: 从 RCS 文件中扩展文件程序. 默认命令是
co
. - CPP: C 程序的预处理器(输出是标准输出设备). 默认命令是
$(CC) –E
. - FC: Fortran 和 RatFor 的编译器和预处理程序. 默认命令是
f77
. - GET: 从 SCCS 文件中扩展文件的程序. 默认命令是
get
. - LEX: Lex 方法分析器程序(针对于 C 或 RatFor). 默认命令是
lex
. - PC: Pascal 语言编译程序. 默认命令是
pc
. - YACC: Yacc 文法分析器(针对于 C 程序). 默认命令是
yacc
. - YACCR: Yacc 文法分析器(针对于 RatFor 程序). 默认命令是
yacc –r
. - MAKEINFO: 转换 TexInfo 源文件(.texi)到 Info 文件程序. 默认命令是
makeinfo
. - TEX: 从 TeX 源文件创建 TeX DVI 文件的程序. 默认命令是
tex
. - TEXI2DVI: 从 TexInfo 源文件创建军 TeX DVI 文件的程序. 默认命令是
texi2dvi
. - WEAVE: 转换 Web 到 TeX 的程序. 默认命令是
weave
. - CWEAVE: 转换 C Web 到 TeX 的程序. 默认命令是
cweave
. - TANGLE: 转换 Web 到 Pascal 语言的程序. 默认命令是
tangle
. - CTANGLE: 转换 C Web 到 C. 默认命令是
ctangle
. - RM: 删除文件命令. 默认命令是
rm –f
. - ARFLAGS: 函数库打包程序 AR 命令的参数. 默认值是
rv
. - ASFLAGS: 汇编语言编译器参数. (当明显地调用
.s
或.S
文件时). - CFLAGS: C 语言编译器参数.
- CXXFLAGS: C++语言编译器参数.
- COFLAGS: RCS 命令参数.
- CPPFLAGS: C 预处理器参数. (C 和 Fortran 编译器也会用到).
- FFLAGS: Fortran 语言编译器参数.
- GFLAGS: SCCS
get
程序参数. - LDFLAGS: 链接器参数. (如:
ld
). - LFLAGS: Lex 文法分析器参数.
- PFLAGS: Pascal 语言编译器参数.
- RFLAGS: RatFor 程序的 Fortran 编译器参数.
- YFLAGS: Yacc 文法分析器参数.
Built-in Makefile Variable
$@
: 表示规则中的目标文件集. 在模式规则中, 如果有多个目标,那么 "$@"就是匹配于目标中模式定义的集合.$%
: 仅当目标是函数库文件中,表示规则中的目标成员名. 例如,如果一个目标是foo.a(bar.o)
,那么$%
就是bar.o
,$@
就是foo.a
. 如果目标不是函数库文件 (Unix 下是[.a]
, Windows 下是[.lib]
), 那么其值为空.$<
: 依赖目标中的第一个目标名字. 如果依赖目标是以模式(即%
)定义的, 那么$<
将是符合模式的一系列的文件集. 注意, 其是一个一个取出来的.$?
: 所有比目标新的依赖目标的集合. 以空格分隔.$^
: 所有的依赖目标的集合. 以空格分隔. 如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份.$+
: 这个变量很像$^
,也是所有依赖目标的集合. 只是它不去除重复的依赖目标.$(@D)
: 表示$@
的目录部分 (不以斜杠作为结尾), 如果$@
值是dir/foo.o
,那么$(@D)
就是dir
, 而如果$@
中没有包含斜杠的话,其值就是.
(当前目录).$(@F)
: 表示$@
的文件部分,如果$@
值是dir/foo.o
, 那么$(@F)
就是foo.o
,$(@F)
相当于函数$(notdir $@)
.$(*D)
/$(*F)
: 和上面所述的同理,也是取文件的目录部分和文件部分. 对于上面的那个例子,$(*D)
返回dir
,而$(*F)
返回foo
.$(%D)
/$(%F)
: 分别表示了函数包文件成员的目录部分和文件部分. 这对于形同archive(member)
形式的目标中的member
中包含了不同的目录很有用.$(<D)
/$(<F)
: 分别表示依赖文件的目录部分和文件部分.$(^D)
/$(^F)
: 分别表示所有依赖文件的目录部分和文件部分 (无相同的).$(+D)
/$(+F)
: 分别表示所有依赖文件的目录部分和文件部分 (可以有相同的).$(?D)
/$(?F)
: 分别表示被更新的依赖文件的目录部分和文件部分.
Makefile Inexplicit Rules
Makefile C Rules
<n>.o
的目标的依赖目标会自动推导为<n>.c
,
并且其生成命令是 $(CC) –c $(CPPFLAGS) $(CFLAGS)
.
Makefile C++ Rules
<n>.o
的目标的依赖目标会自动推导为 <n>.cc
或是 <n>.C
,
并且其生成命令是 $(CXX) –c $(CPPFLAGS) $(CFLAGS)
(建议使用 .cc
作为 C++源文件的后缀, 而不是 .C
).
Makefile ASM Rules
<n>.o
的目标的依赖目标会自动推导为<n>.s
,默认使用编译品 as
,并且其生成命令是: $(AS) $(ASFLAGS)
。
<n>.s
的目标的依赖目标会自动推导为<n>.S
,默认使用 C 预编译器 cpp
,并且其生成命令是: $(AS) $(ASFLAGS)
。
Makefile Object Linking
<n>
目标依赖于<n>.o
,通过运行 C 的编译器来运行链接程序生成(一般是 ld
),其生成命令是:
$(CC) $(LDFLAGS) <n>.o $(LOADLIBES) $(LDLIBS)
这个规则对于只有一个源文件的工程有效,同时也对多个 Object 文件(由不同的源文件生成)的也有效。例如如下规则:
x : y.o z.o
并且 x.c
、 y.c
和 z.c
都存在时,隐含规则将执行如下命令:
cc -c x.c -o x.o cc -c y.c -o y.o cc -c z.c -o z.o cc x.o y.o z.o -o x rm -f x.o rm -f y.o rm -f z.o
Makefile Function
- filter
- shell
- subst
- wildcard
Makefile Best Practice
$(filter %.o,$(files)): %.o: %.c
$(filter %.elc,$(files)): %.elc: %.el
$(CC) -c $(CFLAGS) $< -o $@
(%.o) : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o
$(AR) r $@ $*.o
$(RM) $*.o
%.d: %.c
@set -e; rm -f $@; /
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; /
sed 's,/($*/)/.o[ :]*,/1.o $@ : ,g' < $@.$$$$ > $@; /
$(RM) -f $@.$$$$