Cmake - how include paths with only read access? [duplicate] - cmake

Is it possible to specify an include directory when running cmake. For example
cmake . -INCLUDE=/foo/bar
The header files are in a separate directory from the sources that I would like to compile, and I would like to remedy this without tinkering with the Makefile generated by cmake.
Update
The project does have a CMakeLists.txt. Excerpt:
INCLUDE_DIRECTORIES(${EO_SOURCE_DIR}/src)
INCLUDE_DIRECTORIES(${EO_SOURCE_DIR}/src/ga)
INCLUDE_DIRECTORIES(${EO_SOURCE_DIR}/src/utils)
Can ${EO_SOURCE_DIR} be set from the command line?

If the path to your headers is fixed in relation to your sources, then you should be able to avoid having to pass this info via the command line.
Say your project's directory structure is:
/CMakeLists.txt
/my_sources/main.cpp
/my_sources/foo.cpp
/my_includes/foo.hpp
and in your CMakeLists.txt, you currently have something like:
add_executable(MyExe my_sources/main.cpp my_sources/foo.cpp)
then to add the /my_includes folder to the the list of include search paths, you only need to add the following:
include_directories(my_includes)
For further info about include_directories, run
cmake --help-command include_directories
Answer to update in question:
Yes, using the -D command line option just do
cmake . -DEO_SOURCE_DIR:PATH=<Path to required dir>
The variable ${EO_SOURCE_DIR} gets cached as a result of this, so you only need this -D argument once (unless the required path changes or you delete your CMakeCache file, etc.)

Proper way to do this is to define a variable in CMakeLists.txt and ask user to set it:
set(YOURLIB_INCLUDE_DIR "" CACHE FILEPATH "Path to yourlib includes")
if(NOT EXISTS ${YOURLIB_INCLUDE_DIR}/header.h)
message(SEND_ERROR "Can't find header.h in ${YOURLIB_INCLUDE_DIR})
endif()
include_directories(${YOURLIB_INCLUDE_DIR})
Now you can set it from the command line: cmake -D YOURLIB_INCLUDE_DIR=/path/to/yourlib/include .

Related

Cmake get/query/print build settings on the command line?

Let's assume I have a Cmake C project, and I have something like this in the project:
project(my_project C CXX ASM)
set(my_executable_sources
main.c
file_one.c
)
set(my_executable_sources ${my_executable_sources} file_two.c)
add_executable(my_executable
${my_executable_sources}
file_three.c
)
Let's assume I'm in the ./build subfolder, and cmake ../ -G "Unix Makefiles" has passed successfully.
Can I somehow query build information from the command line using cmake?
For instance, I'm interested here in the final list of source files for my_executable; is there a command that would easily retrieve them? Say, like the following pseudocode:
$ cmake --pseudo-query-build --project="my_project" --target="my_executable" --query="source_files"
my_executable source files:
main.c
file_one.c
file_two.c
file_three.c
I don't see any cmake-generator-independent way of achieving this and even if you know the generator in use the project files generated.
You could modify your project to include a custom target that prints the sources though:
add_custom_target(print_my_executable_sources COMMAND ${CMAKE_COMMAND} -E echo "$<TARGET_PROPERTY:my_executable,SOURCES>" COMMAND_EXPAND_LISTS)
this allows you to use
cmake --build <binary_dir> [--config <configuration>] --target print_my_executable_sources
to print the sources, even if generator expressions are used in the sources.
Note: This does print all files in a single line; to get all the file names on separate lines, you could instead run cmake with the -P option passing the soures via -D and add logic to print one file name per line in the cmake script file.
Alternatively setting the CMAKE_EXPORT_COMPILE_COMMANDS variable to True during configuration could result in the generation of json files that would allow for the extraction of the information, but you'd need a json parser to extract the info. Furthermore this approach only works for the Makefile and Ninja CMake generators. Also I'm not sure you can tell which target a source file belongs to in all cases.
Start by obtaining the target's sources via this line (after add_executable):
get_target_property(MY_TARGET_SOURCES my_executable SOURCES)
And then proceed with a simple line at the end of the file (after add_executable)
message(STATUS ${MY_TARGET_SOURCES})
EDIT: For a full list of available target properties refer to this link.
EDIT2: As I've noticed now that you probably intend to just use it within the CLI, then for my solution you would also have to encapsulate it with a if/endif that checks for a definition e.g. if(SOURCES_DEBUG_INFO) and then run it with -DSOURCES_DEBUG_INFO=TRUE

How to get the working directory of the cmake process?

How to get the working directory from which cmake was called?
I know that I can run execute_process() at the beginning of my CMake code but maybe there is some better way like a built-in variable to the working directory of the CMake process.
What I want to achieve is to convert a relative path given in a CMake variable (given with -D...) to an absolute path in CMake.
What I want to achieve is to convert a relative path given in a CMake variable (given with -D...) to an absolute path in CMake.
Then simply create a cache variable of type PATH (for directories) or FILEPATH in your CMakeLists.txt. CMake will convert untyped command line arguments to absolute paths automatically.
$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
project(test NONE)
set(input_file "" CACHE FILEPATH "some input file")
message(STATUS "${input_file}")
$ cmake -S . -B build -Dinput_file=foo.txt
-- /path/to/foo.txt
-- Configuring done
-- Generating done
-- Build files have been written to: /path/to/build
See the documentation:
It is possible for the cache entry to exist prior to the call but have no type set if it was created on the cmake(1) command line by a user through the -D<var>=<value> option without specifying a type. In this case the set command will add the type. Furthermore, if the <type> is PATH or FILEPATH and the <value> provided on the command line is a relative path, then the set command will treat the path as relative to the current working directory and convert it to an absolute path.
Here: https://cmake.org/cmake/help/latest/command/set.html?highlight=current%20working%20directory
Relatedly, when running in script mode (rather than project mode), several of the usual variables are set to the current working directory:
When run in -P script mode, CMake sets the variables CMAKE_BINARY_DIR, CMAKE_SOURCE_DIR, CMAKE_CURRENT_BINARY_DIR and CMAKE_CURRENT_SOURCE_DIR to the current working directory.
https://cmake.org/cmake/help/latest/variable/CMAKE_SOURCE_DIR.html

Have CMake execute a file which is not CMakeLists.txt

Is that true that you can't customize the name of your CMakeLists.txt file? I read in a few places that make suffers from the same problem, but that's completely not true, you sure can:
~$ make -f whatever_name_you_feel_like
Can't you do this with CMake?
My situation is as follows: The project leader wants to have a certain CMakeLists.txt file run in the CI workflow and another when developing. I thought it would be possible to just keep 2 CMake files and tell cmake which one to execute.
It's not possible to use file with a name different to CMakeLists.txt, but I'm almost certain that's not actually what you want to do anyways.
I assume the cl version and the development version are mostly similar and only some details change. In this case you should not duplicate the logic. Instead add one or multiple options to your cmake project that can set when you set up the build dir and can even be changed without reconfiguring the whole project from scratch. Basically you add a cache variable to CMakeLists.txt which allows the user to overwrite the default value via -D command line option. The value can also be modified after the initial configuration using cmake-gui.
cmake_mimimum_required(VERSION 3.0.2)
project(MyProject)
# option set to true by default
set(MY_PROJECT_COMMAND_LINE_BUILD 1 CACHE BOOL "Use the command line configuration for MyProject")
#logic common to both configurations
add_executable(MyProg foo.cpp bar.cpp)
if(MY_PROJECT_COMMAND_LINE_BUILD)
#logic only for command line build
target_compile_definitions(MyProg PRIVATE COMMAND_LINE_BUILD)
else()
# logic only for non-command line build
target_compile_definitions(MyProg PRIVATE DEVELOPMENT_BUILD)
endif()
Ironically you could set up both from the command line:
Command line build
cmake -S sourceDir -B buildDir
Development build
cmake -D MY_PROJECT_COMMAND_LINE_BUILD:BOOL=0 -S sourceDir -B buildDir
If you don't want to enter the cache values in the command line every time you set up the project, you could also use a cmake script file to initialize the cache values using the -C command line option.
cmake -C developmentVersion.cmake -S sourceDir -B buildDir
developmentVersion.cmake:
set(MY_PROJECT_COMMAND_LINE_BUILD 0 CACHE BOOL "Use the command line configuration for MyProject")
Theoretically you could the whole CMakeLists.txt file in an if else endif structure and use include in one of the alternatives to competely replace the standard logic in the CMakeLists.txt file, but imho this is not a good idea.
Can't you do this with CMake?
No, it's not possible.
The project leader wants to have a certain CMakeLists.txt file run in the CI workflow and another when developing.
One way: copy or symlink proper CMakeLists.txt before executing cmake.
Preferably one would use cmake scripting language:
# CMakeLists.txt
if (MODE STREUQAL "CI_WORKFLOW")
include(CMakeLists-ci-workflow.txt)
elseif (MODE STREQUAL "DEVELOPING")
include(CMakeLists-developing.txt)
else()
message("SUPER ERROR")
fi()
and then separate CMakeLists-ci-workflow.txt and separate CMakeLists-developing.txt and do cmake -D MODE=DEVELOPING or -D MODE=CI_WORKFLOW.
But overall, the idea of "separate CMakeLists.txt" sounds bad to me. Instead use CMAKE_BUILD_TYPE=Debug for developing and CMAKE_BUILD_TYPE=Release for release builds, and use other cmake variables to differentiate settings, instead of duplicating configuration.

cmake - get the used commandline flags "-D"

i recently switched a few projects from autotools to cmake.
one common thing i liked on autotools is that - if i go into the src build directory. there is config.log/config.status - where at the top the ./configure --params command is listed - so it is easy to rerun the former used commandline flags.
(like after compiling some stuff - i want to add a another --enable-this - so copy & paste from config.log/status - and rerun the ./configure --old-params --enable-this)
in cmake - i have a bunch of -D flags - how can i find the used commandline like in config.log/status - with a cmake project?
i know there is the CMakeCache... - but its hard to extract the used flags
edit:
i came up with the following solution:
#save commandline to rebuild this :)
set(USED_CMD_LINE "cmake ")
set(MY_CMAKE_FLAGS CMAKE_BUILD_TYPE CMAKE_INSTALL_PREFIX ENABLE_SSL ENABLE_LUA ENABLE_SSH ENABLE_SNMP MYSQL_USER MYSQL_PASS MYSQL_HOST MYSQL_DB FULL_FEATURES USE_COVERAGE)
FOREACH(cmd_line_loop IN ITEMS ${MY_CMAKE_FLAGS})
if(${cmd_line_loop})
STRING(CONCAT USED_CMD_LINE ${USED_CMD_LINE} "-D" ${cmd_line_loop} "=" ${${cmd_line_loop}} " ")
endif()
ENDFOREACH(cmd_line_loop)
STRING(CONCAT USED_CMD_LINE ${USED_CMD_LINE} " .. ")
#store to a file aka "config.status"
FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/config.status ${USED_CMD_LINE} )
creates a file config.status in the build folder - containing all set cmake params.
pro:
seems to solve my problem
seems to work on subsequent cmake calls
con:
unable to set chmod on FILE(write ? the variable
MY_CMAKE_FLAGScontains the known flags - needs to be manually
updated if a new flag is added
regards
Cmake does not give you easy way to list all used -D flags (defines). However, for correctly written CMakeLists, it is not needed to know the full command line with all -D flags to change one particular define/option.
Consider this snipplet:
SET(my_var_1 TRUE CACHE BOOL "my var 1")
SET(my_var_2 TRUE CACHE BOOL "my var 2")
message(STATUS "my_var_1 ${my_var_1}")
message(STATUS "my_var_2 ${my_var_2}")
First cmake invocation:
>cmake .. -Dmy_var_1=FALSE
-- my_var_1 FALSE
-- my_var_2 TRUE
-- Configuring done
-- Generating done
-- Build files have been written to: out
Second cmake invocation:
>cmake .. -Dmy_var_2=FALSE
-- my_var_1 FALSE
-- my_var_2 FALSE
-- Configuring done
-- Generating done
-- Build files have been written to: out
Note that my_var_1=FALSE even it is not explicitely stated (taken from cache)
One feature that may be helpful is turning on the flag CMAKE_EXPORT_COMPILE_COMMANDS in the project's CMake cache. During build, this will make CMake generate a JSON file compile_commands.json in the binary directory that contains the exact compiler calls for all translation units.
You may want to take a look at what is done in the bootstrap script in CMake's source code:
# Write our default settings to Bootstrap${_cmk}/InitialCacheFlags.cmake.
echo '
# Generated by '"${cmake_source_dir}"'/bootstrap
# Default cmake settings. These may be overridden any settings below.
set (CMAKE_INSTALL_PREFIX "'"${cmake_prefix_dir}"'" CACHE PATH "Install path prefix, prepended onto install directories." FORCE)
set (CMAKE_DOC_DIR "'"${cmake_doc_dir}"'" CACHE PATH "Install location for documentation (relative to prefix)." FORCE)
set (CMAKE_MAN_DIR "'"${cmake_man_dir}"'" CACHE PATH "Install location for man pages (relative to prefix)." FORCE)
set (CMAKE_DATA_DIR "'"${cmake_data_dir}"'" CACHE PATH "Install location for data (relative to prefix)." FORCE)
' > "${cmake_bootstrap_dir}/InitialCacheFlags.cmake"
[...]
"${cmake_bootstrap_dir}/cmake" "${cmake_source_dir}" "-C${cmake_bootstrap_dir}/InitialCacheFlags.cmake" "-G${cmake_bootstrap_generator}" ${cmake_options} ${cmake_bootstrap_system_libs} "$#"
The boostrap script is generating a InitialCacheFlags.cmake file and is then preloading it with the cmake -C option.
And - if you additionally want to output the values to stdout - this initial-cache CMake script also accepts message() commands besides the set(... CACHE) commands.
See also How to store CMake build settings

CMake and FindProtobuf

I'm using the FindProtobuf module in a project where the protocol buffer files are in a sub-directory. I want the CMakeLists.txt file in that sub-directory to invoke protoc to generate the CPP Files. My project folder structure is like this:
cammy/CMakeLists.txt # Root CMakeLists.txt
cammy/protofiles/test.proto # protofile
cammy/protofiles/CMakeLists.txt
I have the include(FindProtobuf), the find_package invocation and the call to PROTOBUF_GENERATE_CPP in the CMakeLists.txt file in protobuf folder.
The executable build step is in the Root CMakeLists.txt file and I add the generated files
to the target executable in this file
ie.
add_executable( ${EXEC} ${SRCS} ${PROTO_SRC} ${PROTO_HEADER} )
target_link_libraries( ${EXEC} ${PROTOBUF_LIBRARIES} )
are both defined in the root CMakeLists.txt
When I run cmake, it does not run protoc to generate the Source files even though I expilicitly tie generated sources to the executable thereby creating a dependency.
When I move all the contents of CMakeLists.txt in the protofiles folder into the root CMakeLists.txt, the proto files are compiled.
Can anyone help me with this? I want all the protocol buffer building stuff to go in the CMakeLists.txt file created in the protofiles folder.
I also noticed that variables generated in the inner CMakeLists.txt ( like PROTO_SRC ) are defined in the inner file when printed ( ie I get the correct generated CPP filename ) but when I print the same variable in the root file.. it is empty. Its almost as though I need to "export" (if there were a way in cmake ) the variables out to the root folder.
Any help would be much appreciated.
Thanks
Kartik
I think [FindProtobuf][0] isn't really meant to be used this way. From its docs:
NOTE: The PROTOBUF_GENERATE_CPP macro & add_executable() or add_library()
calls only work properly within the same directory.
You're trying to use the PROTOBUF_GENERATE_CPP macro in a subdirectory, and although the CMake docs don't really make it clear, a subdirectory introduces a new scope for variables. This means that any variables set or modified in the subdir scope don't affect similarly-named variables in the parent scope. Hence the reason for PROTO_SRC being available in your protofiles dir, but not in the parent.
The way to pass variables up a scope is to use [set(... PARENT_SCOPE)][1], so in protofiles/CMakeLists.txt you could do:
PROTOBUF_GENERATE_CPP(PROTO_SRC PROTO_HEADER test.proto)
set(PROTOBUF_INCLUDE_DIRS ${PROTOBUF_INCLUDE_DIRS} PARENT_SCOPE)
set(PROTOBUF_LIBRARIES ${PROTOBUF_LIBRARIES} PARENT_SCOPE)
set(PROTO_SRC ${PROTO_SRC} PARENT_SCOPE)
set(PROTO_HEADER ${PROTO_HEADER} PARENT_SCOPE)
However, this still doesn't get us all the way!
CMake doesn't actually invoke the protoc compiler to generate the .pb.h and .pb.cc files - it uses [add_custom_command][2] to do this. The custom command specifies the .pb.h and .pb.cc files as outputs, and the custom command is only invoked (i.e. protoc executed) if a subsequent target which depends on these files is built.
So, at configure time (when CMake executes) these files don't exist. This is a problem if you try to add them as sources to an add_library or add_executable command - CMake needs to be told that these files don't exist when it runs, but that they will exist at build time.
The way to do that is to set the [GENERATED][3] property to TRUE for these files. The PROTOBUF_GENERATE_CPP macro does that automatically, but as with the variables, the property isn't populated up into the parent scope. So in your top-level CMakeLists.txt, you also need to add:
set_source_files_properties(${PROTO_SRC} ${PROTO_HEADER} PROPERTIES
GENERATED TRUE)
As you can see, using PROTOBUF_GENERATE_CPP in a different dir to the corresponding add_library/add_executable commands is a bit fragile. If you can avoid doing it, you probably should.
[0]: https://cmake.org/cmake/help/latest/module/FindProtobuf.html "CMake latest documentation for "FindProtobuf" module"
[1]: https://cmake.org/cmake/help/latest/command/set.html "CMake latest documentation for "set" command"
[2]: https://cmake.org/cmake/help/latest/command/add_custom_command.html "CMake latest documentation for "add_custom_command""
[3]: https://cmake.org/cmake/help/latest/prop_sf/GENERATED.html "CMake latest documentation for "GENERATED" source file property"