cmake 错误地转义了 bison 目标选项

cmake incorrectly escapes bison target option

取这个最小化example

关键位置:

bison_target(parser
  numgrammar.y
  ${CMAKE_CURRENT_BINARY_DIR}/parser.cc
  COMPILE_FLAGS "--defines=${CMAKE_CURRENT_BINARY_DIR}/numgrammar.tab.hh")

然后请创建一些文件夹,其中 space 的名称如下:

> mkdir "test folder" && cd "test folder"
> cmake ${ADVGRAMMAR}
> make VERBOSE=1

您会看到如下内容:

> /usr/bin/bison --defines=/mnt/c/research/test folder/advgrammar/build/numgrammar.tab.hh -d -o ....

您看到的问题是:“测试文件夹”中的 space 未转义。

好的,现在让我们尝试逃脱:

bison_target(parser
  numgrammar.y
  ${CMAKE_CURRENT_BINARY_DIR}/parser.cc
  COMPILE_FLAGS "--defines=\"${CMAKE_CURRENT_BINARY_DIR}/numgrammar.tab.hh\"")

您将看到:

/usr/bin/bison --defines=\"/mnt/c/research/test folder/advgrammar/build/numgrammar.tab.hh\" -d -o ....

现在 cmake 为引号添加了错误的转义标记。

当然,如果在没有 spaces 的某个中性命名文件夹中 cmake,当然没有问题,但我想要任何文件夹。

我非常努力google。我尝试了带有 CONFIGURE 的字符串,我尝试了生成器表达式,我尝试了这个那个以及一切,看起来我无法获得我想要的行为。

我只想要这个:

/usr/bin/bison --defines="/mnt/c/research/test folder/advgrammar/build/numgrammar.tab.hh" -d -o ....

当然我可以用我自己的自定义目标来做。但是我想要 bison_target.

确实需要cmake专家的帮助。任何想法表示赞赏。

这是因为 bison_target 宏在 COMPILE_FLAGS 的值上调用 1 separate_arguments 而没有使用尊重原生的新形式shell 规则(它只是盲目地将空格替换为分号)。

不幸的是,宏也不给你机会以更现代的方式注入标志,所以我能想到的最好的办法就是使用 variable_watch 命令来破解内部结构bison_target,至少在修复此错误之前。

这是一个完整的例子:

cmake_minimum_required(VERSION 3.22)
project(test)

find_package(FLEX REQUIRED)
find_package(BISON REQUIRED)

# Hack around FindBISON's incorrect use of separate_arguments
if (CMAKE_VERSION VERSION_LESS 3.24)
  function(patch_flags variable access value ip stack)
    set(invalid "${CMAKE_CURRENT_BINARY_DIR}")
    separate_arguments(invalid)
    string(REPLACE "${invalid}" "${CMAKE_CURRENT_BINARY_DIR}" "${variable}" "${value}")
    set("${variable}" "${${variable}}" PARENT_SCOPE)
  endfunction()
  variable_watch(BISON_TARGET_cmdopt patch_flags)
endif ()

flex_target(scanner
  numgrammar.l
  "${CMAKE_CURRENT_BINARY_DIR}/lexer.cc"
)

bison_target(
  parser
  numgrammar.y
  "${CMAKE_CURRENT_BINARY_DIR}/parser.cc"
  COMPILE_FLAGS "--defines=${CMAKE_CURRENT_BINARY_DIR}/numgrammar.tab.hh"
)

add_flex_bison_dependency(scanner parser)

add_executable(
  numgrammar
  driver.cc
  ${BISON_parser_OUTPUTS}
  ${FLEX_scanner_OUTPUTS}
)

这是一个 shell 互动:

$ cmake -G Ninja -S . -B "build with space"
$ cmake --build "build with space" -- -nv  # n = dry-run, v = verbose
[1/6] cd "/home/alex/test/build with space" && /usr/bin/bison "--defines=/home/alex/test/build with space/numgrammar.tab.hh" -d -o "/home/alex/test/build with space/parser.cc" /home/alex/test/numgrammar.y
[2/6] cd "/home/alex/test/build with space" && /usr/bin/flex "-o/home/alex/test/build with space/lexer.cc" /home/alex/test/numgrammar.l
[3/6] /usr/bin/c++    -MD -MT CMakeFiles/numgrammar.dir/driver.cc.o -MF CMakeFiles/numgrammar.dir/driver.cc.o.d -o CMakeFiles/numgrammar.dir/driver.cc.o -c /home/alex/test/driver.cc
[4/6] /usr/bin/c++    -MD -MT CMakeFiles/numgrammar.dir/parser.cc.o -MF CMakeFiles/numgrammar.dir/parser.cc.o.d -o CMakeFiles/numgrammar.dir/parser.cc.o -c '/home/alex/test/build with space/parser.cc'
[5/6] /usr/bin/c++    -MD -MT CMakeFiles/numgrammar.dir/lexer.cc.o -MF CMakeFiles/numgrammar.dir/lexer.cc.o.d -o CMakeFiles/numgrammar.dir/lexer.cc.o -c '/home/alex/test/build with space/lexer.cc'
[6/6] : && /usr/bin/c++   CMakeFiles/numgrammar.dir/driver.cc.o CMakeFiles/numgrammar.dir/parser.cc.o CMakeFiles/numgrammar.dir/lexer.cc.o -o numgrammar   && :

如您所见,bison 规则正确地引用了空格。

... /usr/bin/bison "--defines=/home/alex/test/build with space/numgrammar.tab.hh" ...

1.参见 https://github.com/Kitware/CMake/blob/6b6bdcbb64e1aa2ddac4f09a0807553f5684165a/Modules/FindBISON.cmake#L131 and https://github.com/Kitware/CMake/blob/6b6bdcbb64e1aa2ddac4f09a0807553f5684165a/Modules/FindBISON.cmake#L249