7. 变量操作
7. 变量操作
有时候项目中的源文件并不一定都在同一个目录中,但是这些源文件最终却需要一起进行编译来生成最终的可执行文件或者库文件。如果我们通过file命令对各个目录下的源文件进行搜索,最后还需要做一个字符串拼接的操作,关于字符串拼接可以使用 set
命令也可以使用 list
命令。
7.1 使用 set 拼接
我的工程结构如下,做了少许更改,将源文件放在了两个目录中:
.
├── CMakeLists.txt
├── include
│ └── head.h
├── lib
│ └── libcalcso.so
├── main.cpp
├── src1
│ ├── div.cpp
│ └── mul.cpp
└── src2
├── add.cpp
└── sub.cpp
4 directories, 8 files
可以使用 set
拼接字符串,格式如下:
set(变量名1 ${变量名1} ${变量名2} ...)
上面的命令将从第二个参数开始,将其后所有的字符串进行拼接,最后把结果存储到第一个参数中。如果第一个参数中原来有数据,则会对原数据就行覆盖。
下面是项目的 CMakeLists.txt 文件
cmake_minimum_required(VERSION 3.17)
project(CALC_TEST)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_COMPILER clang++)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC1 ${CMAKE_CURRENT_SOURCE_DIR}/src1/*.cpp)
file(GLOB SRC2 ${CMAKE_CURRENT_SOURCE_DIR}/src2/*.cpp)
## 设定一个多余的变量,做演示用 ##
file(GLOB SRC3 ${PROJECT_SOURCE_DIR}/src1/*.cpp)
set(TEMP "Hello, World!")
set(SRC3 ${SRC3} ${TEMP})
#############################
link_directories(${PROJECT_SOURCE_DIR}/lib)
add_executable(main ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp ${SRC1} ${SRC2})
target_link_libraries(main calcso pthread)
# 输出一般日志信息
message(STATUS ${SRC3})
生成一个 build
目录,然后使用 cmake 进行构建:
yuzu@yuzu-Elitebook:~/cmake_proj/proj6/build$ cmake ..
-- The C compiler identification is GNU 11.4.0
-- The CXX compiler identification is GNU 11.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- /home/yuzu/cmake_proj/proj6/src1/div.cpp/home/yuzu/cmake_proj/proj6/src1/mul.cppHello, World!
-- Configuring done
-- Generating done
-- Build files have been written to: /home/yuzu/cmake_proj/proj6/build
yuzu@yuzu-Elitebook:~/cmake_proj/proj6/build$ make
[ 16%] Building CXX object CMakeFiles/main.dir/main.cpp.o
[ 33%] Building CXX object CMakeFiles/main.dir/src1/div.cpp.o
[ 50%] Building CXX object CMakeFiles/main.dir/src1/mul.cpp.o
[ 66%] Building CXX object CMakeFiles/main.dir/src2/add.cpp.o
[ 83%] Building CXX object CMakeFiles/main.dir/src2/sub.cpp.o
[100%] Linking CXX executable ../bin/main
[100%] Built target main
可以从输出结果的第 14 行看到,它成功输出了拼接后的字符串
7.2 使用 list 拼接
如果使用 list
进行字符串拼接,对应的命令格式如下:
list(APPEND <list> [<element> ...])
list 命令的功能比 set 要强大,字符串拼接只是它的其中一个功能,所以需要在它第一个参数的位置指定出我们要做的操作,APPEND
表示进行数据追加,后边的参数和 set 就一样了。
上面的 CMakeLists.txt 使用 list 进行重写:
cmake_minimum_required(VERSION 3.17)
project(CALC_TEST)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_COMPILER clang++)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC1 ${CMAKE_CURRENT_SOURCE_DIR}/src1/*.cpp)
file(GLOB SRC2 ${CMAKE_CURRENT_SOURCE_DIR}/src2/*.cpp)
## 设定一个多余的变量,做演示用 ##
file(GLOB SRC3 ${PROJECT_SOURCE_DIR}/src1/*.cpp)
# set(TEMP "Hello, World!")
# set(SRC3 ${SRC3} ${TEMP})
list(APPEND TEMP "Hello World!")
list(APPEND SRC3 ${TEMP})
#############################
link_directories(${PROJECT_SOURCE_DIR}/lib)
add_executable(main ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp ${SRC1} ${SRC2})
target_link_libraries(main calcso pthread)
# 输出一般日志信息
message(STATUS ${SRC3})
需要注意:当
list
被指定为APPEND
的方式,即追加,后面的参数会直接追加到前者的末尾。
我们需要的拼接结果:
-- /home/yuzu/cmake_proj/proj6/src1/div.cpp/home/yuzu/cmake_proj/proj6/src1/mul.cppHello World!
在 CMake 中,使用 set
命令可以创建一个 list
。一个在 list
内部是一个由分号 ;
分割的一组字符串。例如,set(var a b c d e)
命令将会创建一个 list:a;b;c;d;e
,但是最终打印变量值的时候得到的是 abcde
。
set(temp1 a;b;c;d;e)
set(temp2 a b c d e)
message(temp1)
message(temp2)
输出结果:
abcde
abcde
7.3 移除字符串
删除 build 和 bin 目录,恢复工程原本的结构:
.
├── CMakeLists.txt
├── include
│ └── head.h
├── lib
│ └── libcalcso.so
├── main.cpp
├── src1
│ ├── div.cpp
│ └── mul.cpp
└── src2
├── add.cpp
└── sub.cpp
4 directories, 8 files
我们在通过 file 搜索某个目录就得到了该目录下所有的源文件,但是其中有些源文件并不是我们所需要的。
如此时我们不需要工程目录 src2
下的 add.cpp
文件,我们就需要将他从文件字符串列表中去除,于是就要进行移除字符串的操作:
list(REMOVE_ITEM <list> <value> [<value> ...])
这里的列表项移除操作可以这么写:
list(REMOVE_ITEM SRC2 ${CMAKE_CURRENT_SOURCE_DIR}/src2/add.cpp)
完整的 CMakeLists.txt 如下:
cmake_minimum_required(VERSION 3.17)
project(CALC_TEST)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_COMPILER clang++)
include_directories(${PROJECT_SOURCE_DIR}/include)
## 演示用 ##
file(GLOB SRC1 ${CMAKE_CURRENT_SOURCE_DIR}/src1/*.cpp)
file(GLOB SRC2 ${CMAKE_CURRENT_SOURCE_DIR}/src2/*.cpp)
# 移除字符串
message(STATUS "message: ${SRC2}")
list(REMOVE_ITEM SRC2 ${CMAKE_CURRENT_SOURCE_DIR}/src2/add.cpp)
message(STATUS "message: ${SRC2}")
#############################
使用 cmake ..
后打印的结果:
...
-- message: /home/yuzu/cmake_proj/proj6/src2/add.cpp;/home/yuzu/cmake_proj/proj6/src2/sub.cpp
-- message: /home/yuzu/cmake_proj/proj6/src2/sub.cpp
...
从上面 CMakeLists.txt 第 13 行可以看到,我们移除的文件的名字指定给 list 就可以了。但是一定要注意通过 file
命令搜索源文件的时候得到的是文件的绝对路径(在 list 中每个文件对应的路径都是一个 item ,并且都是绝对路径),那么在移除的时候也要将该文件的绝对路径指定出来才可以,否是移除操作不会成功,这部分移除不成功也不会报错。
7.4 list 的其他功能
list 命令还有其它功能,但是并不常用,在此就不一一进行举例介绍了。
获取 list 长度
list(LENGTH <list> <output_variable>)
-
LENGTH
:子命令LENGTH
用于读取列表长度 -
<list>
:当前操作的列表 -
<output_variable>
:新创建的变量,用于存放列表的长度,如:list(LENGTH SRC1 LENGTH_1) message(STATUS ${LENGTH_1})
结果就为 2
读取列表中指定索引的元素
可以指定多个索引
list(GET <list> <element_index> [<element_index> ...] <output_variable>)
<list>
:当前操作的列表<element_index>
:列表元素的索引- 从 0 开始编号,索引 0 的元素为列表的第一个元素
- 索引也可以是负数,
-1
表示最后一个元素,-2
表示倒数第二个元素,以此类推 - 当索引(无论正负)超过列表的长度,运行会报错
<output_variable>
新创建的变量,存储指定索引元素的返回结果,也是一个列表
将 list 中元素用连接符组成一个字符串
list (JOIN <list> <glue> <output_variable>)
-
<list>
:当前操作的列表 -
<glue>
:指定的连接符(字符串) -
<output_variable>
:新创建的变量,存储返回的字符串
在 list 中指定位置插入若干元素
list(INSERT <list> <element_index> <element> [<element> ...])
将元素插入到列表的 0 索引位置
list (PREPEND <list> [<element> ...])
将列表中最后元素移除
list (POP_BACK <list> [<out-var>...])
将列表中第一个元素移除
list (POP_FRONT <list> [<out-var>...])
将指定的元素从列表中移除
list (REMOVE_ITEM <list> <value> [<value> ...])
将指定索引的元素从列表中移除
list (REMOVE_AT <list> <index> [<index> ...])
移除列表中的重复元素
list (REMOVE_DUPLICATES <list>)
列表排序
list (SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
COMPARE
:指定排序方法。有如下几种可选:STRING
:按照字幕顺序排序,为默认的排序方法FILE_BASENAME
:如果是一系列路径名,会使用basename
进行排序NATURAL
:使用自然数进行排序
CASE
:指明是否大小写敏感,有如下几种值可选:SENSITIVE
:按照大小写敏感的方式排序,为默认值INSENSITIVE
:按照大小写不敏感的方式排序
ORDER
:指明排序的顺序,有如下几种值可选:ASCENDING
:按照升序排列,为默认值DESCENDING
:按照降序排列