Konstantin Vladimirov
Konstantin Vladimirov

Reputation: 7009

cmake incorrectly escapes bison target option

Take this minimized example

Critical place:

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

Then please create some folder with space inside name like:

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

You will see something like:

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

You see the problem: space in "test folder" not escaped.

Ok, now lets try to escape:

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

You will see:

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

Now cmake added wrong escape mark for quotes.

Of course no problem if cmake in some neutral-named folder without spaces, but I want any folder.

I tried to google really hard. I tried string with CONFIGURE, I tried generator expressions, I tried this and that and everything and looks like I can't get behavior I want.

I want simply this:

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

Of course I can do it with my own custom target. But I want this with bison_target.

Really need cmake experts help. Any ideas appreciated.

Upvotes: 1

Views: 279

Answers (2)

TonnyRed
TonnyRed

Reputation: 46

Just in case: there is an option DEFINES_FILE in the bison_target macro. As for me it looks like more portable. You can use it instead of COMPILE_FLAGS.

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

It works fine at least on Catalina.

$ cmake -G Ninja -S . -B "build with spaces"
$ cmake --build "build with spaces" -- -nv
[1/6] cd /Users/tonnyred/td && /usr/local/opt/bison/bin/bison "--defines=/Users/tonnyred/td/build with spaces/numgrammar.tab.hh" -o "/Users/tonnyred/td/build with spaces/parser.cc" numgrammar.y
[2/6] cd /Users/tonnyred/td && /usr/local/opt/flex/bin/flex "-o/Users/tonnyred/td/build with spaces/lexer.cc" numgrammar.l
...

Upvotes: 0

Alex Reinking
Alex Reinking

Reputation: 20016

This is because the bison_target macro calls1 separate_arguments on the value of the COMPILE_FLAGS without using the new form that respects native shell rules (it just blindly replaces spaces with semicolons).

Unfortunately, the macro doesn't give you a chance to inject flags in a more modern way, either, so the best I could come up with was to use the variable_watch command to hack at the internals of bison_target, at least until this bug is fixed.

Here's a full example:

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}
)

And here's a shell interaction:

$ 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   && :

As you can see the bison rule correctly quotes the spaces.

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

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

Upvotes: 2

Related Questions