Expanding CMake variables inside a string - cmake

My goal is to provide a default YAML config file that would be written at configure time.
Do something like that:
CMakeLists.txt:
...
get_filename_component(SOUND_FILE beep.wav ABSOLUTE)
file(READ conf.template CONF_TEMPLATE)
file(WRITE conf.example.yaml "${CONF_TEMPLATE}")
...
conf.template:
...
path: ${SOUND_FILE}
...
But when I write the file, variables are not expanded, it writes ${VAR_NAME}.
Is there a way to tell CMake to expand the variables in a string before writing to the file?

Use configure_file for templating.

Related

Debug cmake, where is variable defined?

I have very complex CMake project. Where variables are often defined like set("${scope}_${variable_name}" value..) or other complex way.
I need to find where a variable is defined, where it obtains a value.
I tried variable_watch at the beginning of the cmake, but that only gives me READ_ACCESS so I guess that setting the variable is not covered in variable_watch mechanics.
I need to find out where that variable is set, but I run out of ideas. Variable watch does not help, search sources fails due to complex variable definitions.
You can add on top of the CMakeLists:
macro(set name)
message(STATUS "defninng ${name}")
_set(${name} ${ARGV})
endmacro()
set(a b)
and print CMAKE_CURRENT_LIST_* variables.
You could do this without modifying the cmake files: Simply use grep (or windows equivalent) in combination with running the cmake configuration with the --trace-expand option.
Using this option for example
message("TGT_TYPE = ${TGT_TYPE}")
could result in console output like
/some/path/CMakeLists.txt(71): message(TGT_TYPE = UTILITY )
TGT_TYPE = UTILITY
so
cmake --trace-expand build_dir | grep -P "[sS][eE][tT]\s*\(\s*VARIABLE_NAME\s"
should provide you with the line containing the logic to set VARIABLE_NAME in the project you've set up in the directory build_dir.

CMake's objects output folder variable

How "${PROJECT_BINARY_DIR}/CMakeFiles/project.dir/", the place object files resulted from compilation will be placed on, can be un-hardcoded?
Going straightly to the problem, we have some tests that check objects resulted from compilation on harfbuzz cmake and we use a hardcoded string there but that doesn't seem right and I hope some ${} or $<> exist for that.
I'm afraid you're out of luck here. CMake keeps this as an internal implementation detail, by design.
I'd say it's unlikely to change, but if you want to be absolutely future-proof, you could use a workaround of creating a static library out of the object files and then manually unpacking it back into object files (using the appropriate archiver/librarian) as part of the test. If the object files are also used by another target, and linking to that static library wouldn't work for that target, you can make an object library out of the files and then use that in both the original target and the for-test static library.
Here's an example of how you might achieve this workaround:
add_library(MyObjectLib OBJECT src/a.cpp src/b.cpp)
add_executable(Main src/main.cpp $<TARGET_OBJECTS:MyObjectLib>)
add_library(LibForTesting STATIC $<TARGET_OBJECTS:MyObjectLib>)
add_test(
NAME check-static-inits.sh
COMMAND ${PROJECT_SOURCE_DIR}/src/prepare-and-check-static-inits.sh $<TARGET_FILE:LibForTesting>
)
And here's what the script prepare-and-check-static-inits.sh would look like (pseudo-code):
ar -x $1 -o some_dir
./check-static-inits.sh some_dir
Turning my comment into an answer
There is at the moment no variable or generator expression to get the list of object files used for linking a archive or library target.
But you could append compiler/archiver/linker calls with any program/script and utilize CMake's expansion rules inside those calls.
Note: That will only work CMake's Command-Line Build Tool Generators. And the list(APPEND ...) calls only have to be there once in your CMake code after your project() call.
Examples
Generate a symbolic link to <OBJECT_DIR> with <TARGET_NAME>
project(MyLib)
list(
APPEND CMAKE_CXX_ARCHIVE_FINISH
"\"${CMAKE_COMMAND}\" -E create_symlink \"<OBJECT_DIR>\" \"<TARGET_NAME>\""
)
[...]
add_library(MyLib STATIC src/a.cpp src/b.cpp)
Call some program to do something with the <OBJECTS> list (e.g. echo or write to a file)
project(MyExe)
list(
APPEND CMAKE_CXX_LINK_EXECUTABLE
"\"${CMAKE_COMMAND}\" -E echo \"<OBJECTS>\""
)
[...]
add_executable(MyExe main.cpp)
Directly do something after each object file is generated. In your case where you want to call objdump for each object file it would e.g. be:
list(
APPEND CMAKE_CXX_COMPILE_OBJECT
"${CMAKE_OBJDUMP} -t \"<OBJECT>\" > $(notdir <OBJECT>.dump)"
)
Unfortunately there is no expansion rule for "output file name" hence the platform/make specific trick with $(notdir ...).
References
CMAKE_<LANG>_ARCHIVE_FINISH
CMAKE_<LANG>_LINK_EXECUTABLE
CMAKE_<LANG>_COMPILE_OBJECT
How to get path to object files with CMake for both multiconfiguration generator and makefile based ones?

How to define a cmake macro in a sub_directory that uses the CURRENT_SOURCE_DIR?

What I want to do is to create a CMakeLists.txt that defines a convenience macro to use in parent scope. I can use the macro just fine. However, I used the ${CMAKE_CURRENT_SOURCE_DIR} which is unfortunately not the directory of the CMake script the macro is defined in, but the one it is called from. Is there any way I can change that?
MWE
cmake_minimum_required(VERSION 3.6)
project(git_info CXX)
macro(do_stuff)
message("This CMakeLists.txt file is in ${CMAKE_CURRENT_SOURCE_DIR}")
endmacro()
One ugly way I found was to export variables to the parent scope that contain the path and use that in the macro. But I would prefer to only export the macro definition, if possible, to keep things clean.
Edit:
To clarify a bit. I have one folder with my top-levelCMakeLists.txt and one folder (my_folder) inside with the above MWE CMakeLists.txt. The top-level CMakeLists.txt looks as follows:
cmake_minimum_required(VERSION 3.6)
project(top_project CXX)
add_subdirectory(my_folder)
do_stuff()
You have to transfer CMAKE_CURRENT_LIST_DIR outside the macro into another variable or - in your case - a user defined global property:
set_property(GLOBAL PROPERTY DoStuffPath "${CMAKE_CURRENT_LIST_DIR}")
macro(do_stuff)
get_property(_my_marcros_file GLOBAL PROPERTY DoStuffPath)
message("This CMakeLists.txt file is in ${_my_marcros_file}")
endmacro()
That also works when the macros are defined in a file added with include().
References
In CMake, how can I find the directory of an included file?
What's the CMake syntax to set and use variables?
Use CMAKE_SOURCE_DIR to get a path to outermost parent CMakeLists.txt.

Can we know the directory where the macro or functions are located in cmake

In CMAKE, it defines the following variables to indicate the directories of files:
CMAKE_CURRENT_LIST_DIR
CMAKE_CURRENT_BINARY_DIR
CMAKE_CURRENT_SOURCE_DIR
They are useful when you process CMake scripts. However, none of them can tell you the directory where MACROs or functions are defined. Give the following example CMakeLists.txt to illustrate my question
project(Hello)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/my_macro.cmake)
test_macro()
Then for the my_macro.cmake, we have definitions for test_macro():
macro(test_macro)
message("hello")
#?? Can we know the folder of this macro is located?
#?? the macro definition file's location
endmacro()
I don't think there's an off-the-shelf variable for that, but you could easily make your own:
my_macro.cmake:
set(test_macro__internal_dir ${CMAKE_CURRENT_LIST_DIR} CACHE INTERNAL "")
macro(test_macro)
message(STATUS "Defined in: ${test_macro__internal_dir}")
endmacro()
The set() line will be processed when the file is included, and the value of CMAKE_CURRENT_LIST_DIR from that processing cached for future use inside the macro.
With CMake 3.17 there is CMAKE_CURRENT_FUNCTION_LIST_DIR that can be used in functions, see https://cmake.org/cmake/help/v3.17/variable/CMAKE_CURRENT_FUNCTION_LIST_DIR.html
Unfortunately, there is no such thing for macros yet.

How replace string in a file with value of current directory using CMake

I'm trying to make another guy's research code reproducible, so that others don't have the same trouble I'm having right now. But, I'm lacking the experience with cmake for that. Here is what I could do after reading the docs:
In the same folder as my CMakeLists.txt I have a file called io_utils.h with a ROOT_PATH variable whose value is VALUE_ROOT_PATH.
I want to replace that string with the value of the file's current directory. So I tried to add the following to CMakeLists.txt:
# Set ROOT_PATH in io_utils.h
FIND_PATH(BUILD_PATH CMakeLists.txt . )
FILE(READ ${BUILD_PATH}io_utils.h IO_UTILS)
STRING(REGEX REPLACE "VALUE_ROOT_PATH" "${BUILD_PATH}" MOD_IO_UTILS "${IO_UTILS}" )
FILE(WRITE ${BUILD_PATH}io_utils.h "${MOD_IO_UTILS}")
I tried to make and install that but it is not working, the file isn't changed. What's wrong with what I did?
I suggest a different approach using the configure_file cmake macro. First, you create a template file that references any variables you plan to set in cmake, then the macro substitutes the actual value in place of the variable. For example, let's say we have a template file io_utils.h.in that looks something like this:
const char* ROOT_PATH = "${BUILD_PATH}";
In your CMakeLists.txt, you can do something like this:
configure_file( io_utils.h.in io_utils.h )
When you run cmake, it will use the first file as a template (io_utils.h.in here) to generate the second file (called io_utils.h here) with the value substituted in:
const char* ROOT_PATH = "/path/to/CMakeLists.txt";
By the way, CMake has a built-in variable that references the directory with the top-level CMakeLists.txt called CMAKE_SOURCE_DIR. Try replacing BUILD_PATH above with this variable.
In this case it seems to be a lot easier to define a macro and let the preprocessor do the job:
add_definition(ROOT_PATH ${VALUE_ROOT_PATH})