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.
Related
I want the same as in
How to load variables in a "bar=foo" syntax in CMake?
with the difference that my "value" entries contain references to the "keys" (prospective variables).
Eg the BASEPATH variable:
BASEPATH:=/home/SomePath
LIB_BASE?=$(BASEPATH)/lib/$(LIBREV)
ACCELMOD_BASE=$(BASEPATH)/SomeOtherPath
I have modified my CMakeLists.txt script to extract the [Key/value] pair from each line to two variables(see code below) and end up with pairs like (note that the value still contains a reference to some variable's name which is usually defined at the beginning of the file):
[BASEPATH, /home/SomePath],
[LIB_BASE, ${BASEPATH}/lib/${LIBREV}],
[ACCELMOD_BASE, ${BASEPATH}/SomeOtherPath],
The code I have written is the following:
# Part2
file(STRINGS revisions.mk ConfigContents)
foreach(NameAndValue ${ConfigContents})
# Strip leading spaces
string(REGEX REPLACE "^[ ]+" "" NameAndValue ${NameAndValue})
#remove commented lines
string(FIND ${NameAndValue} "#" commentIndex)
if(${commentIndex} EQUAL 0)
continue()
endif()
# Find variable name
string(REGEX MATCH "^[^\\?:=]+" Name ${NameAndValue})
# Find the value
string(REGEX REPLACE "^(.+)=(.+)$" "\\2" Value ${NameAndValue})
# Replace () with {} to denote a cmake's variable reference
string(REGEX REPLACE "\\(([^)]+)\\)" "{\\1}" Value ${Value})
# Set the variable
set(${Name} ${Value})
message("> Value of " ${Name} " : " ${${Name}})
endforeach()
I expect that when I define a Key (Name) as a variable (using the set command) and set its value to the Value counterpart of the Key, the reference inside the string will be replaced with the current value of the referenced variable.
However this is not the case.
e.g. for the give input, the message command before the end of the loop returns:
>Value of BASEPATH: /home/SomePath
>Value of LIB_BASE : ${BASEPATH}/lib/${LIBREV}
>Value of ACCELMOD_BASE: $(BASEPATH)/SomeOtherPath
even though BASEPATH has already been defined.
To verify my expectation, I wrote the following simple code to simulate the behaviour in the loop:
set(BASE 123)
set(RIGHT '${BASE}/SomePath/AA')
set(LEFT_STR "LEFT")
set(${LEFT_STR} ${RIGHT})
message(">" ${LEFT} "<>" ${${LEFT_STR}})
and in this case the ${BASE} reference gets correctly replaced and
'123/SomePath/AA'<>'123/SomePath/AA'
is returned as expected.
What could I been getting wrong?
Since CMake 3.18 there is eval in CMake - cmake_language(EVAL CODE. See https://cmake.org/cmake/help/latest/command/cmake_language.html?highlight=eval .
cmake_langauge(EVAL CODE "set(${Name} ${Value})")
There is no eval in cmake. The popular and error-prone way around it is to create a script and then include it:
file(STRINGS revisions.mk ConfigContents)
set(varlist "")
foreach(NameAndValue ${ConfigContents})
# Strip leading spaces
string(REGEX REPLACE "^[ ]+" "" NameAndValue ${NameAndValue})
#remove commented lines
string(FIND ${NameAndValue} "#" commentIndex)
if(${commentIndex} EQUAL 0)
continue()
endif()
# Find variable name
string(REGEX MATCH "^[^\\?:=]+" Name ${NameAndValue})
# Find the value
string(REGEX REPLACE "^(.+)=(.+)$" "\\2" Value ${NameAndValue})
# Replace () with {} to denote a cmake's variable reference
string(REGEX REPLACE "\\(([^)]+)\\)" "{\\1}" Value ${Value})
# Set the variable
set(${Name} ${Value})
list(APPEND varlist ${Name})
endforeach()
set(script "")
foreach(i IN LISTS varlist)
string(APPEND script "set(${i} \"${${i}}\")\n")
endforeach()
file(WRITE script2.cmake ${script})
include(script2.cmake)
foreach(i IN LISTS varlist)
message(STATUS ${i}=${${i}})
endforeach()
This creates the script script2.cmake with the content:
set(BASEPATH "/home/SomePath")
set(LIB_BASE "${BASEPATH}/lib/${LIBREV}")
set(ACCELMOD_BASE "${BASEPATH}/SomeOtherPath")
and then includes it. Including such script would re-evaulate expressions thus resolve references.
I am searching header files in a directory
Replacing LONG string in all header files to SVN_LONG.
I don't wan't to replace LONG,LONG,LONG to SVN_LONG.
When I run below code it's replacing SLONG to SSVN_LONG, LONG to SVN_LONG etc..
So how to avoid SLONG, LONGINT not to replace, only LONG string have to replace SVN_LONG?
file ( GLOB headerfiles "../common/include/tar/*.h")
if ("${grep_word}" STREQUAL "")
SET (searchreg "LONG")
foreach( eachheaderfile ${headerfiles} )
MESSAGE(STATUS " INFILES= ${eachheaderfile}\n")
FILE(READ ${eachheaderfile} file_content)
#MESSAGE(STATUS " FILES_content= ${file_content}")
string(REGEX REPLACE "${searchreg}" "SVN_LONG" modified_file_content "${file_content}" )
FILE(WRITE ${eachheaderfile} ${modified_file_content})
FILE(READ ${eachheaderfile} file_content1)
MESSAGE(STATUS " FILES_content= ${file_content1}")
endforeach(eachheaderfile)
#MESSAGE(STATUS " outFILES= ${headerfiles}\n")
endif()
As usual with regular expressions, you need to match symbols before replaced string and restore them in replacement string. Command string(REGEX REPLACE) supports backslashed references for that purpose:
string(REGEX REPLACE "([^a-zA-Z])LONG|^LONG" "\\1SVN_LONG"
modified_file_content ${file_content})
Given command matches single symbol, which should be different from the letter, before word LONG and restore this symbol via backslash reference \1 (symbol "\" need to be doubled in the cmake command, because it is parsed by CMake itself before going to the command).
Alternation started with ^ is needed for match LONG at the beginning of the string.
I have an little library which has an file structure like this:
CMakeLists.txt
LibFoo/
Foo.h
FooWin.cpp
FooWin.inl
FooPosix.cpp
FooPosix.inl
And when i have to build the library in a specific OS (for example Windows) they should contain in the list before using file(GLOB_RECURSE) using a macro:
macro( add_recursive dir retVal)
file( GLOB_RECURSE ${retVal} ${dir}/*.h ${dir}/*.cpp ${dir}/*.c ${dir}/*.inl )
endmacro()
and my excluding pattern is like this (when Windows is the build host): *Posix.* but doesn't work, i tried with this approach:
macro( add_recursive dir retVal pattern)
file( GLOB_RECURSE ${retVal} ${dir}/*.h ${dir}/*.cpp ${dir}/*.c ${dir}/*.inl EXCLUDE PATTERN "${pattern}")
endmacro()
but the POSIX files still here anyways, CMake doesn't report an error or anything suggested about that.
You can use list filtering to remove items after the GLOB_RECURSE:
list(FILTER ${retVal} EXCLUDE REGEX "Posix.")
There is no EXCLUDE option for file(GLOB_RECURSE ...) command flow. You probably take this option from file(COPY|INSTALL ...), which is a different command flow.
You may iterate over list, obtained from file(GLOB_RECURSE) and exclude needed files manually:
macro( add_recursive dir retVal)
# Store unfiltered list of files into temporary list
file( GLOB_RECURSE _tmp_list ${dir}/*.h ${dir}/*.cpp ${dir}/*.c ${dir}/*.inl )
# Resulted list is initially empty. Copy all needed elements into it.
set(${retval})
foreach(f ${_tmp_list})
if(NOT f MATCHES "Posix.")
list(APPEND ${retVal} ${f})
endif()
endforeach()
endmacro()
This piece of code almost worked for me.
cmake nags about the set not having enough arguments and discontinues the macro.
This snippet also makes the extention and exclude filter variable
macro( add_recursive retVal dir ext excl)
# Store unfiltered list of files into temporary list
file( GLOB_RECURSE _tmp_list ${dir}/${ext})
# Resulted list is initially empty. Copy all needed elements into it.
set(${retval} "")
foreach(f ${_tmp_list})
if(NOT f MATCHES ${excl})
list(APPEND ${retVal} ${f})
endif()
endforeach()
endmacro( add_recursive )
#example of usage
add_recursive(inc "./" "*.h" "/exclude_folder/")
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()
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)