在shell脚本中传递CMAKE_CXX_FLAGS

时间:2018-03-24 00:21:21

标签: bash cmake escaping quoting

显然,我有一个涉及bash,cmake和引用的繁琐问题。 我需要多次编译程序,使用不同的cmake标志。经过大量的猜测,我在bash脚本中提出了以下结构:

cmake_params[1]=-DCMAKE_CXX_FLAGS=-O2\ -DNDEBUG
cmake_params[2]=-DCMAKE_CXX_FLAGS=-march=native\ -O2\ -DNDEBUG
# ....

#compile with:

for i in {1..n}
do
    # .. prepare ..
    mkdir build
    cd build
    cmake "${cmake_params[$i]}" ..
    make
done

请注意,我在cmake_params中转义了空格,并且使用双引号调用了cmake。但是,最后,我需要能够将标志扩展到以下内容:

cmake_params[1]=-DCMAKE_CXX_COMPILER="clang++"\
  -DCMAKE_C_COMPILER="clang"\
  -DCMAKE_CXX_FLAGS=-march=native\ -O3\ -DNDEBUG\
  -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=true\
  -DCMAKE_POLICY_DEFAULT_CMP0069=NEW

但那不会奏效。我试过CXX标志周围的引号,我试着省略编译器周围的引号,我试图在实际的cmake调用中省略引号,都没有成功。错误消息各不相同,最突出的是:

The CMAKE_CXX_COMPILER:

clang++ -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_FLAGS=-march=native -O3 -DNDEBUG
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=true -DCMAKE_POLICY_DEFAULT_CMP0069=NEW

is not a full path and was not found in the PATH.

很明显,cmake误解了我的命令行。为了更清楚,我想将clang ++作为CMAKE_CXX_COMPILER-march=native -O3 -DNDEBUG传递为CMAKE_CXX_FLAGS

哪个bash脚本会这样做以及为什么 - 是否有关于引用和参数传递如何工作的资源?我真的尝试了一件接一件的事情来找到有用的东西,但这甚至都没有教育过的猜测......

2 个答案:

答案 0 :(得分:1)

当你写(用bash)时:

cmake "${cmake_params[$i]}"

您要求它调用命令行实用程序cmake,向该实用程序传递一个参数,该参数是cmake_params数组的指示元素的值。与任何其他命令行实用程序一样,cmake不会尝试重新解释它接收的参数;它假定你已安排传递正确的论据。所以当它收到一个论点时说:

-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_FLAGS=-march=native -O3 -DNDEBUG -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=true -DCMAKE_POLICY_DEFAULT_CMP0069=NEW

继续将其内部变量CMAKE_C_COMPILER=之前的ID)的值设置为值clang -DCMAKE_CXX_FLAGS=-march=native -O3 -DNDEBUG -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=true -DCMAKE_POLICY_DEFAULT_CMP0069=NEW

cmake随后尝试通过搜索文件名clang -DCMAKE_CXX_FLAGS=-march=native -O3 -DNDEBUG -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=true -DCMAKE_POLICY_DEFAULT_CMP0069=NEW的路径来查找编译器。显然,它没有找到该文件。

如果事情不起作用,那么在文件路径中有空格的情况下使用文件名参数基本上是不可能的。

如果这对您不重要,您可以使用一个非常简单的解决方案:像这样调用cmake

cmake ${cmake_params[$i]}

告诉shell获取数组cmake_params的指示元素的值,然后(因为它没有被引用)它对值执行分词和路径名扩展,从而得到一个列表然后将参数单独传递给cmake

在您提供的示例中,这应该可以正常工作。但它很脆弱甚至危险。首先,因为将执行路径名扩展,所以值中的*可能会变成当前目录中的完整文件列表。其次,因为使用变量$IFS的值来完成分词,这可能会被改变。最后,请注意shell在扩展值内解释引号(包括反斜杠)或参数扩展,因此无法将值内的某些空格标记为免于分词。这意味着您无法使用包含空格的文件名或文件路径。

在现代bash版本中,您可以使用namerefs实现更精确的解决方案。 nameref是使用-n标志声明的变量,其中包含另一个变量的名称。您无法创建namerefs数组,但nameref可以引用数组。

考虑到这一点,我们可以重写您最初的想法:

# Each param set has its own array variable. There is no need to use
# integer suffixes; you could use more descriptive tags. Just make sure
# that there is no other variable whose name starts with the same prefix.
# Since we are creating arrays, we do *not* quote whitespace *unless*
# it is really part of a parameter value.
declare -a cmake_params_1=(-DCMAKE_CXX_FLAGS=-O2 -DNDEBUG)
declare -a cmake_params_2=(-DCMAKE_CXX_FLAGS=-march=native -O2 -DNDEBUG)
# ....

# Now we can loop through the various arrays of parameter lists: 

# ${!word*} expands to a list of existing variables whose names start with  word   
for name in "${!cmake_params_*}"; do
    # Create a nameref for the array whose name we got from the list
    declare -n array=$name
    # .. prepare ..
    mkdir build
    cd build
    # Here we can use "array" as though it were the variable named in the argument
    cmake "${array[@]}" ..
    make
done

答案 1 :(得分:0)

以下对我有用:

CMAKE_OPTS="-DBUILD_TESTING=OFF"
CMAKE_DEFAULTS="-GNinja \
                -DCMAKE_BUILD_TYPE=Release \
                \"-DCMAKE_CXX_FLAGS_RELEASE:STRING=-Wformat -Werror=format-security -DNDEBUG -Ofast -march=native -flto -mtune=native\" \
                -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} \
                -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} \
"

curl -L -o source.tar.gz ${SRC_URL}
tar -xzf source.tar.gz

rm -rf build_ && mkdir build_ && cd build_
eval "cmake ${CMAKE_DEFAULTS} ${CMAKE_OPTS} ${SRC_PATH}"
cmake --build . --target install

基本上,事情是将整个-DCMAKE_CXX_FLAGS_RELEASE包裹在\"\"中,然后在其上调用eval(杂质!我知道我知道...)。