Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I include a literal double quote in a custom CMake command?

I'm trying to create a custom command that runs with some environment variables, such as LDFLAGS, whose value needs to be quoted if it contains spaces:

LDFLAGS="-Lmydir -Lmyotherdir"

I cannot find a way to include this argument in a CMake custom command, due to CMake's escaping rules. Here's what I've tried so far:

COMMAND LDFLAGS="-Ldir -Ldir2" echo blah VERBATIM)

yields "LDFLAGS=\"-Ldir -Ldir2\"" echo blah

COMMAND LDFLAGS=\"-Ldir -Ldir2\" echo blah VERBATIM)

yields LDFLAGS=\"-Ldir -Ldir2\" echo blah

It seems I either get the whole string quoted, or the escaped quotes don't resolve when used as part of the command.

I would appreciate either a way to include the literal double-quote or as an alternative a better way to set environment variables for a command. Please note that I'm still on CMake 2.8, so I don't have the new "env" command available in 3.2.

Note that this is not a duplicate of When to quote variables? as none of those quoting methods work for this particular case.

like image 277
Devin Lane Avatar asked Sep 07 '16 11:09

Devin Lane


People also ask

How do you pass command line arguments in CMake?

If you create the cache variable in the CMakeLists. txt file and then pass the argument via calling cmake, won't the CMakeList. txt file keep overwriting the argument value? No, the cache is populated on the first run with either the default value, or the value supplied on the command line if it is provided.

What is ${} in CMake?

Local Variables You access a variable by using ${} , such as ${MY_VARIABLE} . 1. CMake has the concept of scope; you can access the value of the variable after you set it as long as you are in the same scope. If you leave a function or a file in a sub directory, the variable will no longer be defined.

What is CMake option?

When CMake is first run in an empty build tree, it creates a CMakeCache. txt file and populates it with customizable settings for the project. This option may be used to specify a setting that takes priority over the project's default value. The option may be repeated for as many CACHE entries as desired.


2 Answers

The obvious choice - often recommended when hitting the boundaries of COMMAND especially with older versions of CMake - is to use an external script.

I just wanted to add some simple COMMAND only variations that do work and won't need a shell, but are - I have to admit - still partly platform dependent.

  • One example would be to put only the quoted part into a variable:

    set(vars_as_string "-Ldir -Ldir2")
    add_custom_target(
        QuotedEnvVar
        COMMAND env LD_FLAGS=${vars_as_string} | grep LD_FLAGS
    )
    

    Which actually does escape the space and not the quotes.

  • Another example would be to add it with escaped quotes as a "launcher" rule:

    add_custom_target(
        LauncherEnvVar
        COMMAND env | grep LD_FLAGS
    )
    set_target_properties(
        LauncherEnvVar 
        PROPERTIES RULE_LAUNCH_CUSTOM "env LD_FLAGS=\"-Ldir -Ldir2\""
    )
    

Edit: Added examples for multiple quoted arguments without the need of escaping quotes

  • Another example would be to "hide some of the complexity" in a function and - if you want to add this to all your custom command calls - use the global/directory RULE_LAUNCH_CUSTOM property:

    function(set_env)
        get_property(_env GLOBAL PROPERTY RULE_LAUNCH_CUSTOM)
        if (NOT _env)
            set_property(GLOBAL PROPERTY RULE_LAUNCH_CUSTOM "env")
        endif()
        foreach(_arg IN LISTS ARGN)
            set_property(GLOBAL APPEND_STRING PROPERTY RULE_LAUNCH_CUSTOM " ${_arg}")
        endforeach()
    endfunction(set_env)
    
    set_env(LDFLAGS="-Ldir1 -Ldir2" CFLAGS="-Idira -Idirb")
    
    add_custom_target(
        MultipleEnvVar
        COMMAND env | grep -E 'LDFLAGS|CFLAGS'
    )
    

Alternative (for CMake >= 3.0)

  • I think what we actually are looking for here (besides the cmake -E env ...) is named Bracket Argument and does allow any character without the need of adding backslashes:

    set_property(
        GLOBAL PROPERTY 
            RULE_LAUNCH_CUSTOM [=[env LDFLAGS="-Ldir1 -Ldir2" CFLAGS="-Idira -Idirb"]=]
    )
    add_custom_target(
        MultipleEnvVarNew
        COMMAND env | grep -E 'LDFLAGS|CFLAGS'
    )
    

References

  • 0005145: Set environment variables for ADD_CUSTOM_COMMAND/ADD_CUSTOM_TARGET
  • How to modify environment variables passed to custom CMake target?
  • [CMake] How to set environment variable for custom command
  • cmake: when to quote variables?
like image 55
Florian Avatar answered Oct 24 '22 21:10

Florian


You need three backslashes. I needed this recently to get a preprocessor define from PkgConfig and apply it to my C++ flags:

pkg_get_variable(SHADERDIR movit shaderdir)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSHADERDIR=\\\"${SHADERDIR}\\\"")
like image 32
ulatekh Avatar answered Oct 24 '22 22:10

ulatekh