list containing generator expression invalid - cmake

cmake_minimum_required(VERSION 3.13)
project(hello)
set(SRCS main.c hello.c )
set(HEADERS hello.h )
add_executable(hello ${SRCS} ${HEADERS})
target_include_directories(hello PRIVATE
"inc1"
"inc2"
)
target_include_directories(hello PRIVATE "headers")
set(var3)
list(APPEND var3 "inc3")
list(APPEND var3 "inc4")
list(APPEND var3 "$<TARGET_PROPERTY:hello,INCLUDE_DIRECTORIES>")
message(STATUS "var3:${var3}")
list(LENGTH var3 list_len1)
message(STATUS "list_len1 :${list_len1}")
#file(GENERATE OUTPUT f.txt CONTENT ${list_len1})
file(GENERATE OUTPUT f.txt CONTENT ${var3})
it displays:
-- var3:inc3;inc4;$<TARGET_PROPERTY:hello,INCLUDE_DIRECTORIES>
-- list_len1 :3
CMake Error at CMakeLists.txt:29 (file):
file Unknown argument to GENERATE subcommand.
the line : file(GENERATE OUTPUT f.txt CONTENT ${var3})
generates the error. why?
we normally have a list of 3 elements, inc3, inc4 concatenated with the generator expression, which is when expanded :
/Users/windev/works/proj/inc1;/Users/windev/works/proj/inc2;/Users/windev/works/proj/headers
so why don't we have inc3,inc4,/Users/windev/works/proj/inc1;/Users/windev/works/proj/inc2;/Users/windev/works/proj/headers
printed in my f.txt
thanks

Specification of file(GENERATE) command
file(GENERATE OUTPUT output-file
<INPUT input-file|CONTENT content>
[CONDITION expression] [TARGET target])
implies, that content is exactly a single argument.
Because your variable var3 contains more than one value, then ${var3} is expanded to more than one value. This is what the error message about.
Proper call to file(GENERATE) would be
file(GENERATE OUTPUT f.txt CONTENT "${var3}")
With that call list elements in the generated file will be separated by a semicolon(;).
If you want space separator between the elements, then you need to join the list elements before generating a file:
string(JOIN " " var3_string ${var3})
file(GENERATE OUTPUT f.txt CONTENT "${var3_string}")
or use $<JOIN> generator expression:
file(GENERATE OUTPUT f.txt CONTENT "$<JOIN:${var3}, >")
The advantage of the latter variant is that it joins content of the generator expression too. (The string command treats the whole generator expression as a single value).

Related

Start reading file from a specific line using CMAKE

I want to start reading a file from a specific line. Cmake official docs suggest using file() with offset but I am not sure about its usage.
The file that I want to read is test.mak:
# -----------------------------------------------------------------------------
## TEST
# -----------------------------------------------------------------------------
TEST_COMPONENTS ?= ABC DEF GHI
# SYMBOLS
SYMBOLS_PROJ ?= A002
SYMBOLS_LABEL ?= TEST_A002_FINAL
I have a cmake file (the function is from internet and it works with my use case) where i want to read the test.mak file starting from "#SYMBOLS" so that the macros defined before this line are ignored/skipped, and then i want to set the macros in my current cmake:
function(Fileread MKFile)
file(READ "${MKFile}" FileContents [OFFSET "# SYMBOLS"])
string(REPLACE "?" "" FileContents ${FileContents})
string(REPLACE "\\\n" "" FileContents ${FileContents})
string(REPLACE "\n" ";" FileLines ${FileContents})
list(REMOVE_ITEM FileLines "")
foreach(line ${FileLines})
string(REPLACE "=" ";" line_split ${line})
list(LENGTH line_split count)
if (count LESS 2)
message(STATUS "Skipping ${line}")
continue()
endif()
list(GET line_split -1 value)
string(STRIP "${value}" value)
separate_arguments(value)
list(REMOVE_AT line_split -1)
foreach(var_name ${line_split})
string(STRIP ${var_name} var_name)
set(${var_name} ${value} PARENT_SCOPE)
endforeach()
endforeach()
endfunction()
Fileread("test.mak")
The offset setting is not working as a result of which i am also getting the macro TEST_COMPONENTS which i don't need. NOTE: TEST_COMPONENTS is just an example, there are multiple lines of macro definitions before "# SYMBOLS" that i would like to skip. Thanks for any suggestions to solve this in advance.
Use file(STRINGS) to read the lines of the text file into a list variable. You could then use list(POP_FRONT) until you encounter a matching line.
# Line 1
# Line 2
# Line 3
# Line 4
file(STRINGS ${CMAKE_CURRENT_LIST_FILE} THIS_FILE)
set(REDUCTION_SUCCESS False)
while(THIS_FILE)
list(POP_FRONT THIS_FILE LINE)
if (LINE MATCHES "^# SYMBOLS.*")
set(REDUCED_FILE ${LINE} ${THIS_FILE})
set(REDUCTION_SUCCESS True)
break()
endif()
endwhile()
if (REDUCTION_SUCCESS)
# use the contents of the REDUCED_FILE variable
# (print all remaining lines for the purpose of demonstation)
foreach(_LINE IN LISTS REDUCED_FILE)
message(STATUS "${_LINE}")
endforeach()
else()
message(FATAL_ERROR "No line containing '# SYMBOLS' found")
endif()
Replace one of the # Line 1 comments with # SYMBOLS to get a successful outcome. For simplicity this is just a cmake script that can be run with cmake -P script.cmake. The script file parses itself.
If the number of lines to skip is known, you could simplify the logic after the file(STRINGS) command to a single list(SUBLIST) call.

how to display and return a list with cmake

It's my first time with cmake, and I have two questions about lists:
1) How to display a list ?
2) How to return a list in a function ?
Here is my code:
function(GET_ALL_DIRS where SEP)
message (STATUS "Let's search all directories in ${where}")
file (GLOB TMP_LIST_DIR ${where}${SEP}*)
foreach (tmp_elem ${TMP_LIST_DIR})
if (IS_DIRECTORY ${tmp_elem})
list (APPEND "${every_class}" ${tmp_elem})
message ("We add ${tmp_elem}")
endif()
endforeach()
list (LENGTH "${every_class}" nb_elem)
message ("in the list there is ${nb_elem} elements")
set(${tst} "${every_class}" PARENT_SCOPE)
endfunction()
GET_ALL_DIRS (includes ${SEP})
list (LENGTH "${tst}" nb_elem)
message ("after get_all_dirs there is ${nb_elem} elements")
In the function I have the correct number of elements but after it I have 0... Why?
Function's parameter specifications
<list>
<variable>
means that CMake expects a name, not a dereference of that name (${..}).
Correct:
list(APPEND every_class ${tmp_elem})
list(LENGTH every_class nb_elem)
set(tst ${every_class} PARENT_SCOPE)
In CMake name of variable or list may itself be expressed as a result of deference of another variable. Constructions below are perfectly valid:
set(my_var_name "a")
set(${my_var_name} "some value") # Assign value to variable 'a'
set(name_suffix "b")
list(APPEND list_${name_suffix} "other value") # Appends to a list 'list_b'.
Such "dynamic" names are widely used within functions and macros.

Is there a good way to display "clean" text on stdout in CMake

Cmake version 2.8.10.2, OS centos 6.3
We're trying to get a "clean" display of text on stdout from within our cmake files. That is, text just as we intend, without a prefix. So far I've tried these variations
This goes to stderr (surprised me):
MESSAGE("my text")
This goes to stdout but prefixes each line with '-- ':
MESSAGE(STATUS "my text")
This sort of works, but the side effects are weird and make it undesirable for us:
FILE(WRITE /dev/stdout "my text")
The above goes to stdout, but breaks if the output from cmake itself is redirected to a file (cmake > file), although it is OK if you you pipe stdout first (cmake | cat > file) but that's hacky and means we have to tell everyone about the workaround which just isn't going to happen.
You could provide the following function:
function(CleanMessage)
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${ARGN}")
endfunction()
and use it like this:
CleanMessage("Clean text")
If you want to push the boat out, you could even extend the built-in message options to include a CLEAN one:
function(message MessageType)
if(${ARGV0} STREQUAL "CLEAN")
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${ARGN}")
else()
_message(${MessageType} "${ARGN}")
endif()
endfunction()
and use it like this:
message(STATUS "Incidental information")
message(CLEAN "Clean Incidental information")
message(WARNING "CMake Warning, continue processing")
This is safe to define in your top-level CMakeLists.txt once. However, if it's defined in e.g. a utilities file which could be included more than once, it will lead to infinite recursion. To avoid this, at the start of the utility file in which the function is defined, add:
if(OverloadMessageIncluded)
return()
endif()
set(OverloadMessageIncluded TRUE)
This is effectively a CMake version of a header guard.
If you prepend your message with a carriage return, you can simply overwrite the dashes.
string( ASCII 13 CR ) # carriage return character
message( STATUS "${CR}Hello!" )
To make it also work for e.g. empty or single-letter messages, you can explicitly overwrite the dashes with spaces.
function( CleanMessage Message )
string( ASCII 13 CR ) # carriage return character
set( ClearLine "${CR} ${CR}" )
message( STATUS "${ClearLine}${Message}" )
endfunction()

How do I correctly pass CMake list (semicolon-sep) of flags to set_target_properties?

CMake lists are essentially just semicolon-separated strings, but if you pass such a variable to a command, it does get expanded into multiple arguments - for example,
set(FLAGS f1 f2 f3)
# now FLAGS == 'f1;f2;f3'
add_custom_command(
...
COMMAND my_cmd ${FLAGS}
...
)
will correctly call my_cmd f1 f2 f3.
Now if I do this with
set_target_properties(
myTarget PROPERTIES
LINK_FLAGS "${LD_FLAGS}"
)
the expansion does not occur, and I end up with a single LD_FLAG that contains semicolons -- useless, instead of expanding it into a space-separated string.
Is there any way to make it so that when I pass a list to the LINK_FLAGS property (or any property that is), it gets expanded into multiple arguments rather than just one?
I don't think set_target_properties can do the expansion automatically, but you can use string (REPLACE ...) to expand a list into a space separated string:
string (REPLACE ";" " " LD_FLAGS_STR "${LD_FLAGS}")
set_target_properties(
myTarget PROPERTIES
LINK_FLAGS "${LD_FLAGS_STR}"
)
For using a cmake List as list, use
${LD_FLAG}
For using a cmake list as string, (i.e. list items are separated with ';'), use
"${LD_FLAG}"
So in your case, just remove "" should be sufficient.
The set_property command is designed for this
http://www.cmake.org/cmake/help/v3.0/command/set_property.html
set_property(TARGET tgt PROPERTY LINK_FLAGS foo bar)
I use it like strings
set(FLAGS " f1 f2 f3")
Note the space in the front, it allows you to concatenate other sets of flags.
For more complex projects, instead of if-elses you may also do a double expansion trick:
set(GCC_FLAGS " -Wl,--relax")
set(DIAB_FLAGS " -tPPCE500ES:cross")
set(MSVC_FLAGS " /RAINBOW_CRAP)
# ...
# ...LINUX_FLAGS, WINDOWS_FLAGS, etc...
set_target_properties(
myTarget PROPERTIES
LINK_FLAGS "${${COMPILER}_FLAGS} ${${SYSTEM}_FLAGS}"
)
# COMPILER and SYSTEM is set somewhere else, in toolchain files for example
separate_arguments(LD_FLAGS_AS_LIST NATIVE_COMMAND LD_FLAGS)
set_target_properties(
myTarget PROPERTIES
LINK_FLAGS "${LD_FLAGS_AS_LIST}"
)
As of CMake 3.12, you can use the list(JOIN <list> <glue> <output variable>) command described here:
set(FLAGS f1 f2 f3)
# now FLAGS == 'f1;f2;f3'
list(JOIN FLAGS " " FLAGS) # Note: Overwrites FLAGS variable
# now FLAGS == 'f1 f2 f3'
add_custom_command(
...
COMMAND my_cmd ${FLAGS}
...
)
This is a little more direct than using string(REPLACE ...).
In cmake 3.x there are a few approaches
The top level CMakeLists.txt file then looks something like this:
# The name of the included file could be anything,
# it doesn't have to be called CMakeLists.txt
include(foo/CMakeLists.txt)
include(bar/CMakeLists.txt)
add_executable(myApp ${myApp_SOURCES})
with the subdirectory files structured something like this:
list(APPEND myApp_SOURCES
${CMAKE_CURRENT_LIST_DIR}/foo.cpp
${CMAKE_CURRENT_LIST_DIR}/foo_p.cpp
)
Enhanced source file handling with target_sources()

CMake: Print out all accessible variables in a script

I'm wondering if there is a way to print out all accessible variables in CMake. I'm not interested in the CMake variables - as in the --help-variables option. I'm talking about my variables that I defined, or the variables defined by included scripts.
I'm currently including:
INCLUDE (${CMAKE_ROOT}/Modules/CMakeBackwardCompatibilityCXX.cmake)
And I was hoping that I could just print out all the variables that are here, instead of having to go through all the files and read what was available - I may find some variables I didn't know about that may be useful. It would be good to aid learning & discovery. It is strictly for debugging/development.
This is similar to the question in Print all local variables accessible to the current scope in Lua, but for CMake!
Has anyone done this?
Using the get_cmake_property function, the following loop will print out all CMake variables defined and their values:
get_cmake_property(_variableNames VARIABLES)
list (SORT _variableNames)
foreach (_variableName ${_variableNames})
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
This can also be embedded in a convenience function which can optionally use a regular expression to print only a subset of variables with matching names
function(dump_cmake_variables)
get_cmake_property(_variableNames VARIABLES)
list (SORT _variableNames)
foreach (_variableName ${_variableNames})
if (ARGV0)
unset(MATCHED)
string(REGEX MATCH ${ARGV0} MATCHED ${_variableName})
if (NOT MATCHED)
continue()
endif()
endif()
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
endfunction()
To print environment variables, use CMake's command mode:
execute_process(COMMAND "${CMAKE_COMMAND}" "-E" "environment")
Another way is to simply use:
cmake -LAH
From the manpage:
-L[A][H]
List non-advanced cached variables.
List cache variables will run CMake and list all the variables from the CMake cache that are not marked as INTERNAL or ADVANCED. This will effectively display current CMake settings [...].
If A is specified, then it will display also advanced variables.
If H is specified, it will also display help for each variable.
ccmake is a good interactive option to interactively inspect cached variables (option( or set( CACHE:
sudo apt-get install cmake-curses-gui
mkdir build
cd build
cmake ..
ccmake ..
Another way to view all cmake's internal variables, is by executing cmake with the --trace-expand option.
This will give you a trace of all .cmake files executed and variables set on each line.
based on #sakra
function(dump_cmake_variables)
get_cmake_property(_variableNames VARIABLES)
list (SORT _variableNames)
foreach (_variableName ${_variableNames})
if (ARGV0)
unset(MATCHED)
#case sensitive match
# string(REGEX MATCH ${ARGV0} MATCHED ${_variableName})
#
#case insenstitive match
string( TOLOWER "${ARGV0}" ARGV0_lower )
string( TOLOWER "${_variableName}" _variableName_lower )
string(REGEX MATCH ${ARGV0_lower} MATCHED ${_variableName_lower})
if (NOT MATCHED)
continue()
endif()
endif()
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
endfunction()
dump_cmake_variables("^Boost")
variable names are case sensitive
btw if you are interested in boost, it is Boost_INCLUDE_DIRS not BOOST_INCLUDE_DIRS, and it is Boost_LIBRARIES not BOOST_LIBRARIES, and by mistake I had BOOST_LIBRARIES instead of Boost_LIBRARIES, https://cmake.org/cmake/help/v3.0/module/FindBoost.html , better example for boost:
set(Boost_USE_STATIC_LIBS ON)
find_package(Boost REQUIRED COMPONENTS RANDOM)
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(myfile PRIVATE
${Boost_LIBRARIES}
)
You can use message :
message([STATUS] "SUB_SOURCES : ${SUB_SOURCES}")
None of the current answers allowed me to see the variables in my project subdirectory. Here's a solution:
function(print_directory_variables dir)
# Dump variables:
get_property(_variableNames DIRECTORY ${dir} PROPERTY VARIABLES)
list (SORT _variableNames)
foreach (_variableName ${_variableNames})
get_directory_property(_variableValue DIRECTORY ${dir} DEFINITION ${_variableName})
message(STATUS "DIR ${dir}: ${_variableName}=${_variableValue}")
endforeach()
endfunction(print_directory_variables)
# for example
print_directory_variables(.)
print_directory_variables(ui/qt)