How to set environment variable MAKEFLAGS in CMakeLists.txt? - cmake

I am trying to compile some code which is managed with Cmake with make, and I have noticed that when I use cmake 3.25.1, I do not get "Entering directory ..." messages from make; but when I use cmake 3.14.3, I get these messages.
I would like to suppress these messages regardless of the cmake version used. So, I found cmake: How to suppress "Entering directory" messages? where it says
You can use
MAKEFLAGS += --no-print-directory
... which, I gather, is an environment variable that make would read.
So, as per Setting a NEW environment variable with CMake?, I wrote at the start of my CMakeLists.txt file:
set(ENV{MAKEFLAGS} "--no-print-directory")
... then re-ran cmake, then ran make, and ... no effect, I still get:
$ make
make[1]: Entering directory '/c/src/build'
make[2]: Entering directory '/c/src/build'
...
So, how can I set this MAKEFLAGSenvironment variable in my CMakeLists.txt file, so make can see it, and perform without these printouts? Note that I would prefer NOT setting this variable permanently in my shell environment (MINGW64 bash), so I would like to handle this in CMakeLists.txt alone.

Related

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.

Can I setup default CMake cache variables to apply for all projects?

There are several settings I need to provide to CMake which are the same on every project. It gets annoying having to specify these on the command line every time I blow away my build area and start again.
For example:
cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${HOME}/local
Is there a way to provide values for common settings like CMAKE_BUILD_TYPE and CMAKE_INSTALL_PREFIX so that they are applied to all projects by default?
You may create "initial-cache" script
~/default.cmake:
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type")
set(CMAKE_INSTALL_PREFIX $ENV{HOME}/local CACHE PATH "Installation prefix")
and pass it to cmake as with -C option:
cmake -C ~/default.cmake ..
More info about -C option in cmake(1) documentation.
As for using these setting by default (that is, without any additional options to cmake), I don't know a clear way for doing this.
You may create wrapper script like default-cmake, which calls cmake with original plus additional parameters.
Another way is to create an initial CMakeCache.txt file that exactly contains those two variables:
$ cat CMakeCache.txt
CMAKE_BUILD_TYPE:STRING=Debug
CMAKE_INSTALL_PREFIX:PATH=~/local
Upon the next cmake run, the CMakeCache.txt will contains the remaining of the cmake run. You do not need to pass any additional flags to cmake, but it will scratch your inital CMakeCache.txt (thus you need to add it to .gitignore).

Why does CMake ignore exported CXX and CC environment variables?

I am running a CMake (3.4.3) like this as explained in the CMake FAQ's:
export CC="cc_args.py $PWD/../bin/gcc"
export CXX="cc_args.py $PWD/../bin/g++"
cmake -DCMAKE_BUILD_TYPE=Debug ..
However when I print CMAKE_CXX_COMPILER and CMAKE_C_COMPILER it still points to the system's default compilers in /usr/bin. It only works when I explicitly read-in the environment variables like this:
IF (NOT $ENV{CC} STREQUAL "")
SET(CMAKE_C_COMPILER $ENV{CC})
ENDIF ()
IF (NOT $ENV{CXX} STREQUAL "")
SET(CMAKE_CXX_COMPILER $ENV{CXX})
ENDIF ()
But even then the building fails with this message:
/bin/sh: 1: /home/peterg/bin/cc_args.py /home/peterg/Code/build/../bin/g++: not found
However I am certain that all paths are correct since executing just the path between the two colons outputs this as expected:
g++: fatal error: no input files
compilation terminated.
Update:
It seems the compiling process does not like spaces in the compiler paths. I've now created two scripts (one for GCC and one for CC) which wrap the commands and propagate the arguments and that seems to work. But it still seems I am doing something fundamentally wrong because CMake would also not accept the exported CC=proxy_script_cc.sh and GCC=proxy_script_gcc.sh variables without spaces by itself.
Turning my comment into an answer
Problem
I've given you code a try and could reproduce your problem
CMake Error at [...]/cmake-3.5/Modules/CMakeDetermineCXXCompiler.cmake:56 (message):
Could not find compiler set in environment variable CXX:
cc_args.py [... PWD ...]/../bin/g++.
If I look at CMakeDetermineCXXCompiler.cmake code and at get_filename_component() documentation, it just means that it didn't find cc_args.py in "the system search path" or relative to your binary output directory.
Solution
So it does work when you give a full path or a relative path to your binary output dir with something like
export CC="../cc_args.py ../bin/gcc"
export CXX="../cc_args.py ../bin/g++"
Alternative
CMake does allow to define "launcher scripts" e.g. with CMAKE_<LANG>_COMPILER_LAUNCHER
$ cmake -DCMAKE_BUILD_TYPE=Debug
-DCMAKE_C_COMPILER_LAUNCHER=../cc_args.py
-DCMAKE_CXX_COMPILER_LAUNCHER=../cc_args.py
..
References
How to Use CCache with CMake?
Save and reprint warnings for successfully-compiled files on subsequent builds?
Pass -DCMAKE_CXX_COMPILER=<path/to/compiler> to your CMake call. That's less error prone compared to fiddling with shell variables.

Get C/CXX FLAGS set by commands add_definitions() and add_compile_options()

In my CMakeLists.txt, global C and CXX flags are set using commands add_definitions()
and
add_compile_options.
Many attempts to retrieve the C/CXX flags:
${CMAKE_C_FLAGS} and ${CMAKE_CXX_FLAGS} are empty
${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE}} is the original default CMake value
(e.g. -g for CMAKE_C_FLAGS_DEBUG)
${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}} same as above
get_cmake_property(def COMPILE_DEFINITIONS) is NOTFOUND
get_cmake_property(opt COMPILE_OPTIONS) is also NOTFOUND
I want to pass my custom C/CXX flags to an external project based on ./configure:
ExternalProject_Add( my_external_lib
DEPENDS Threads::Threads
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}
BUILD_IN_SOURCE 1
UPDATE_COMMAND echo "Full clean" && make distclean
CONFIGURE_COMMAND ${CMAKE_CURRENT_LIST_DIR}/configure
--prefix=${CMAKE_BINARY_DIR}
--cc=${CMAKE_C_COMPILER}
--cxx=${CMAKE_CXX_COMPILER}
--CFLAGS=${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE}}
--CXXFLAGS=${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}}
--LDFLAGS=${CMAKE_STATIC_LINKER_FLAGS_${CMAKE_BUILD_TYPE}}
--ARFLAGS=${CMAKE_STATIC_LINKER_FLAGS_${CMAKE_BUILD_TYPE}}
--enable-static
)
(1) How to get compilation flags set by add_definitions() and add_compile_options?
Note: I use CMake version 3.3.2
While writing the question, I realize I am wondering another question:
I use ccache but the ./configure script does not see my environment variables CC and CXX when I set them like that:
get_cmake_property(launcher RULE_LAUNCH_COMPILE)
set(ENV{CC} ${launcher} ${CMAKE_C_COMPILER})
set(ENV{CXX} ${launcher} ${CMAKE_CXX_COMPILER})
(2) Are these above CMake statements correct?
Oops I have again another question:
I retrieve the LDFLAGS and ARFLAGS from the same variable ${CMAKE_STATIC_LINKER_FLAGS}.
(3) Is there another CMake variable to set ARFLAGS?
Several questions in one post, but all of them are simple:
Documentation link for add_definitions() explicitely refers to directory and target COMPILE_DEFINITION properties. The first of them is extracted by using get_directory_property(), the second by using get_target_property(). Any of them can also be extracted by using get_property(). get_cmake_property you tried to use is inapplicable here (it is used for other type of properties).
You set environment variables CC and CXX at configuration step, but all commands for ExternalProject_Add are executed at build step.
Currently ExternalProject_Add doesn't support simple setting environment variables. As adviced in this bugreport, for set variable's value without spaces you may prepend command with "VAR=VAL" clauses. But for your case this wouldn't work because of escaping problems.
See also CMake FAQ: How can I get or set environment variables?
[...]
environment variables SET in the CMakeLists.txt only
take effect for cmake itself (configure-time),
so you cannot use this method to set an environment variable
that a custom command might need (build-time).
Barring environment variable support by various CMake commands
(e.g. add_custom_command(), currently not supported yet),
an acceptable workaround may be to invoke shell scripts instead
which wrap the commands to be executed.
Simple wrapper script to set environment variables and run actual command:
wrapper.sh.in:
export "CC=ccache #CMAKE_C_COMPILER#"
export "CXX=ccache #CMAKE_XX_COMPILER#"
eval "$*"
CMakeLists.txt:
configure_file(wrapper.sh.in wrapper.sh #ONLY)
ExternalProject_Add(my_external_lib
...
CONFIGURE_COMMAND /bin/sh ${CMAKE_CURRENT_BINARY_DIR}/wrapper.sh
${CMAKE_CURRENT_LIST_DIR}/configure ...
BUILD_COMMAND /bin/sh ${CMAKE_CURRENT_BINARY_DIR}/wrapper.sh make
)
Note, that you should use wrapper for every command which requires this variables. In you case, these are CONFIGURE_COMMAND and BUILD_COMMAND.
Documentation for CMAKE_STATIC_LINKER_FLAGS says that these flags are used for linker, so you may probably use them as LDFLAGS. As for ARFLAGS, they are hardcoded directrly to CMAKE_C_ARCHIVE_CREATE variable, contained full command line for ar. By default this command simply uses linker flags, so your approach seems to be correct too.

Display CMake variables without running CMake on a CMakeLists.txt file or manually inspecting config.cmake?

Suppose I have a package called Foo. If I run CMake on a CMakeLists.txt file that contains find_package(Foo), then I can print out the values of variables such as ${Foo_LIBRARIES} and ${Foo_INCLUDES}.
Is there an easy way to display these variables without having to run CMake on a CMakeLists.txt file, and without having to manually inspect the config.cmake file?
You asked: (1) Is there an easy way to display these variables without having to run cmake on a CMakeLists.txt file, and (2) without having to manually inspect the config.cmake file?
I can give you a yes answer to (2) but it does require that you (re)run cmake. But since you can re-run your cmake configure step by simply executing cmake . in the build directory, re-running cmake should not keep you from trying this approach. My answer is given in this SO answer and uses the get_cmake_property command. Here is that code encapsulated into a cmake macro, print_all_variables, so I can use it when debugging my cmake scripts.
macro(print_all_variables)
message(STATUS "print_all_variables------------------------------------------{")
get_cmake_property(_variableNames VARIABLES)
foreach (_variableName ${_variableNames})
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
message(STATUS "print_all_variables------------------------------------------}")
endmacro()
The macros are invoked with same syntax as cmake functions:
print_all_variables()
To simply print a value, you could do something like this:
message(STATUS "foo include dir: ${foo_INCLUDE}")
where ${foo_INCLUDE} is the value you desire to print.
Note: I'm using cmake > 3.14
Run CMake and have a look at the cache with the ccmake GUI tool. Then you'll get all the variables.
Or run CMake with -LH then you will get all variables printed after configuration.
So I think it is not possible to get the variables without running CMake.
Run cmake in find-package mode. Example to display a package`s include directories:
cmake -DNAME=ZLIB -DCOMPILER_ID=GNU -DLANGUAGE=C -DMODE=COMPILE --find-package
Example to display the libraries:
cmake -DNAME=ZLIB -DCOMPILER_ID=GNU -DLANGUAGE=C -DMODE=LINK --find-package
The NAME must be set to the package name. You can obtain your COMPILER_ID on this page. LANGUAGE can be C, CXX or Fortran.
I am always suspicious of variables changing values throughout a script somewhere so I like to see the value of a variable at a particular point in the running script. Combining the ideas from both Phil and Aaron B. this is what I'm using:
function(PRINT_VAR VARNAME)
message(STATUS "${VARNAME}: ${${VARNAME}}")
endfunction()
PRINT_VAR("CMAKE_CXX_COMPILER")
Then I can just litter PRINT_VAR statements around like I'm debugging code back in 1980
These variables are generally hardcoded into FindFoo.cmake so that it is not possible to extract them without running the function first. Note that sometimes the value of Foo_LIBRARIES depends on the system configuration, which is unknown until find_package(Foo) is run.