https://www.youtube.com/playlist?list=PLK6MXr8gasrGmIiSuVQXpfFuE1uPT615s
1. Running CMake
cd makefile_test
mkdir build
subl CMakeLists.txt
cmake_minimum_required(VERSION 3.15.2)
project(makefile_test_project VERSION 1.0.0)
1.1 method 1: cmake directory
cd build
the directory to run cmake from
cmake ..
.. means to build from the parent folder, and the following messages pop up:
-- The C compiler identification is AppleClang 10.0.1.10010046
-- The CXX compiler identification is AppleClang 10.0.1.10010046
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/ysgc/cpp_doc/makefile_test/build
1.2 Method 2: ccmake, a command line app
could check the cmakecache when gui is not available
1.3 Method 3: cmake-gui
cmake-gui
set the source code directory to the parent folder "some_path/makefile_test"
set the built binaries to "some_path/makefile_test/build"
2. a hello-world example
subl main.cpp
//main.cpp
#include <iostream>
int main(){
std::cout << "hello world~" << std::endl;
return 0;
}
subl CMakeLists.txt
在下面加入一行 add
_executable(output_file_name xxxx.cpp)
cmake_minimum_required(VERSION 3.15.2)
project(makefile_test_project VERSION 1.0.0)
add_executable(test_main main.cpp)
cd build
cmake .
此处之前已经cmake ..
过了,所以可以直接在binary的文件夹下面cmake,和cmake+source_path效果一样。
make && ./test_main
3. Adding a library
the same file structure as the previous article:
// main.cpp
#include <iostream>
#include "funcs.h"
int main(){
std::cout << std::endl;
print_hello();
std::cout << std::endl;
std::cout << "The factorial of 5 is " << factorial(5) << std::endl;
return 0;
}
//funcs.h
#ifndef FUNCS_H
#define FUNCS_H
void print_hello();
int factorial(int n);
#endif
// func1.cpp
#include "funcs.h"
int factorial(int n){
if (n != 1){
return n*factorial(n-1);
}
return 1;
}
// func2.cpp
#include <iostream>
#include "funcs.h"
void print_hello(){
std::cout << "hello world!!!" << std::endl;
}
2.1 Method 1: put all the cpp file into "add_executable"
cmake_minimum_required(VERSION 3.15.2)
project(makefile_test_project VERSION 1.0.0)
add_executable(test_main main.cpp func1.cpp func2.cpp)
2.2 Method 2:
cmake_minimum_required(VERSION 3.15.2)
project(makefile_test_project VERSION 1.0.0)
add_library(
func12_lib
func1.cpp
func2.cpp
# funcs.h
)
add_executable(test_main main.cpp)
target_link_libraries(test_main PRIVATE func12_lib)
NOTE: the default type of add_library is "static", instead, it could be "add_library(func12_lib SHARED func1.cpp func2.cpp)"
NOTE: the other key for add_library is MODULE
NOTE: the "PRIVATE" key in "target_link_libraries" will be covered later
NOTE: run ldd test_main
~> get the dynamic linker dependencies
3. CMake with vscode
install the two extensions first:
4. Subdirectories and Target Interface Properties
4.1 Add a subdirectory
此时进行cmake build可以通过
4.2 Move head files into the subdirectory
cmake build通不过
-- Configuring done
CMake Error at CMakeLists.txt:6 (add_library):
Cannot find source file:func1.cpp
Tried extensions .c .C .c++ .cc .cpp .cxx .cu .m .M .mm .h .hh >.h++ .hm
.hpp .hxx .in .txxCMake Error at CMakeLists.txt:6 (add_library):
No SOURCES given to target: func12_libCMake Generate step failed. Build files cannot be regenerated correctly.
4.3 Edit the CMakeLists inside the subdirectory
Now, the building of cmake coud pass while the one of make could not.
cmake ..
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/ysgc/cpp_doc/makefile_test/build
make
Scanning dependencies of target func12_lib
[ 20%] Building CXX object lib_test/CMakeFiles/func12_lib.dir/src/lib_test/func1.cpp.o
[ 40%] Building CXX object lib_test/CMakeFiles/func12_lib.dir/src/lib_test/func2.cpp.o
[ 60%] Linking CXX static library libfunc12_lib.a
[ 60%] Built target func12_lib
Scanning dependencies of target test_main
[ 80%] Building CXX object CMakeFiles/test_main.dir/main.cpp.o
/Users/ysgc/cpp_doc/makefile_test/main.cpp:2:10: fatal error: 'funcs.h' file not found
#include "funcs.h"
^~~~~~~~~
1 error generated.
make[2]: *** [CMakeFiles/test_main.dir/main.cpp.o] Error 1
make[1]: *** [CMakeFiles/test_main.dir/all] Error 2
make: *** [all] Error 2
4.4 Link the library to main.cpp
Now, it's clear why a more proper structure should be:
so that there's a nickname for the library before the head file, and different head files can be written after its library name.
4.5 Move main into a subdirectory
# root folder CMakeLists.txt
cmake_minimum_required(VERSION 3.15.2)
project(makefile_test_project VERSION 1.0.0)
add_subdirectory(lib_test)
add_subdirectory(exec_test)
# /exec_test/CMakeLists.txt
add_executable(test_main main.cpp)
target_link_libraries(test_main PRIVATE func12_lib)
NOTE: now, the executable file is under /build/exec_test/
rather than /build/
, file's name is still "main_test"
5. Add a macro variable
# /lib_test/CMakeLists.txt
add_library(
func12_lib
func12_lib/func1.cpp
func12_lib/func2.cpp
)
target_include_directories(func12_lib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
target_compile_definitions(func12_lib PUBLIC FUNC12_LIB_VERSION=3)
Keyword: PUBLIC in the last line:
could be replaced by "PRIVATE" or "INTERFACE"
"PRIVATE" means the macro variable could only be used by func12_lib's file.
"INTERFACE" means the macro variable could only be used by files other than files in func12_lib
6. Scripting in CMakeLists.txt
-
set(MY_VARIABLE "I am a variable")
~> 意思是MY_VARIABLE等于"I am a varialbe" ~> 所有变量都是string;此处不加""就会变成"Iamavariable" -
message(STATUS "The variable value is: ${MY_VARIABLE}")
~>意思是执行到这一行的时候print(some_string)
Control flow and Auto-dereferencing
set(my_bool TRUE)
if(NOT my_bool) # if(NOT "${my_bool}")
message(STATUS "Condition was met")
else()
message(STATUS "Condition failed")
endif()
set(s1 "string1")
set(s2 "string1")
set(s3 "string3")
if(s1 STREQUAL s3)
message(STATUS "s1 = s3")
endif()
set(var 3)
if (var EQUAL "2")
message(STATUS "${var} not equal to 2")
endif()
if((s1 STREQUAL s3) AND (var EQUAL "2"))
message(STATUS "Condition was met in if")
elseif(my_bool)
message(STATUS "Condition was met in elseif")
endif()
while(var LESS 50)
message(STATUS "Value is ${var}")
math(EXPR var "${var} + 1")
endwhile()
foreach(item IN ITEMS foo bar baz qux)
message(STATUS "Item is: ${item}")
endforeach()
foreach(idx RANGE 0 99) # eq to foreach(idx RANGE 100)
message(STATUS "Index is ${idx}")
endforeach()
7. Functions, Scopes, Arguments, and List Expansion
- Array
set(my_list 4 5 6)
set_property(
GLOBAL # DIRECTORY ${PROJECT_SOURCE_DIR}
PROPERTY FOO
1
2
3
${my_list}
)
get_cmake_property(foo_value FOO)
message(STATUS "Value of FOO is ${foo_value}")
- function
function(function_test first_arg)
message(STATUS "You called the function with argument: ${first_arg}")
endfunction()
function_test(101)
function(function_test arg1 arg2)
foreach(arg IN LISTS ARGN)
message(STATUS "You called the function with argN: ${arg}")
endforeach()
foreach(arg IN LISTS ARGV)
message(STATUS "You called the function with argV: ${arg}")
endforeach()
endfunction()
function_test(101 102 103 104 105)
~>
-- You called the function with argument: 101
-- You called the function with argN: 103
-- You called the function with argN: 104
-- You called the function with argN: 105
-- You called the function with argV: 101
-- You called the function with argV: 102
-- You called the function with argV: 103
-- You called the function with argV: 104
-- You called the function with argV: 105
- dereference
function(increment var)
message(STATUS "Value is: ${var}")
endfunction()
set(value 14)
increment(value)
~> Value is: value
function(increment var)
message(STATUS "Value is: ${${var}}")
endfunction()
set(value 14)
increment(value)
~> Value is: 14
8. Test
https://www.youtube.com/watch?v=mBjRjZcRTA0&list=PLK6MXr8gasrGmIiSuVQXpfFuE1uPT615s&index=12
9.
https://www.youtube.com/watch?v=vyHp2YNUmSQ&list=PLK6MXr8gasrGmIiSuVQXpfFuE1uPT615s&index=13