CMake Generator Expression not recognized by Ninja - cmake

I was trying to use a CMake Generator Expression such as:
$<TARGET_GENEX_EVAL:foo,$<TARGET_PROPERTY:$<TARGET_NAME_IF_EXISTS:foo>,PROP_BAR>>
This expression works fine when the Generator used is Visual Studio or Unix Makefiles but fails when the generator used is Ninja with errors such as:
CMake Error at cmake/<fname>.cmake:629 (add_custom_command):
Error evaluating generator expression:
$<TARGET_GENEX_EVAL:foo,$<TARGET_PROPERTY:$<TARGET_NAME_IF_EXISTS:foo>,PROP_BAR>>
Expression did not evaluate to a known generator expression
Call Stack (most recent call first):
cmake/<fname>.cmake:1269
<target>/CMakeLists.txt:16
Does Ninja not support Generator Expressions?
If it doesn't what are my best options?
If it does, what exactly is the issue?

Related

CMake knows std 20, but g++9 doesn't

I have a project, which cannot be compiled by a fellow, due to an unknown call to a method from the std library.
I suspect that it's due to the fellow's g++ version (9.4.2) because the function was added in the std 20 standard. To test if that is the case I installed g++-9 (Version 9.5.0) and pointed the /usr/bin/g++ symbolic link to g++-9 and stumbled upon another issue (the question).
When I run
$ g++-9 -std=c++20
g++-9: error: unrecognized command line option ‘-std=20’; did you mean ‘-std=c2x’?
g++-9: fatal error: no input files
compilation terminated.
I get an error stating, that the std standard version 20 is unknown.
But when I try to generate the build files for an cmake project with the following line in the CMakeLists.txt:
target_compile_features(${PROJECT_NAME}
PUBLIC
cxx_std_20
)
I get no error. But when replacing the 20 with a 23:
target_compile_features(${PROJECT_NAME}
PUBLIC
cxx_std_23
)
I get the error
CMake Error at CMakeLists.txt:74 (target_compile_features):
target_compile_features The compiler feature "cxx_std_23" is not known to
CXX compiler
"GNU"
version 9.5.0.
Why is the C++20 standard unknown to g++, but known, when generating build files with cmake?
This is some sort of follow up Question:
When I try to compile my project I get the error:
error: ‘std::stringstream’ {aka ‘class std::__cxx11::basic_stringstream<char>’} has no member named ‘view’
and view was added in c++20. Can it be, that view was jet not added in c++2a?
Well... if your compiler is GCC 9.5.0, the reason why CMake says it doesn't know about C++23 is because... it doesn't?
https://en.cppreference.com/w/cpp/compiler_support
The only thing that cppreference.com's compiler support table says that GCC v9 "knows" about C++23 is "Narrowing contextual conversions in static_assert and constexpr if"
And the reason why you get "g++-9: error: unrecognized command line option ‘-std=20’; did you mean ‘-std=c2x’?" for GCC 9.5.0 is probably just because C++20 support wasn't fully implemented yet. Again, look at the compiler support table in cppreference.com. There are lots of core language features and library features for C++20 that GCC didn't implement until version 10 (some even in version 9-13 Ex. "Atomic Compare-And-Exchange with padding bits")
So just do what it told you to do and take what you get, accepting that not all of the C++20 features will be available in GCC 9.5.0.
Or upgrade you compiler :P
The reason why it works with CMake's target_compile_features(... cxx_std_20) is because... CMake handles it.
See Modules/Compiler/GNU-C.cmake:
if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 9.1)
set(CMAKE_C23_STANDARD_COMPILE_OPTION "-std=c2x")
set(CMAKE_C23_EXTENSION_COMPILE_OPTION "-std=gnu2x")
endif()
and Modules/Compiler/GNU-CXX.cmake:
elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0)
set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std=c++2a")
set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std=gnu++2a")
endif()
The name of the option for standard C++20 up to GCC 9 is -std=c++2a. According to man gcc:
c++2a
The next revision of the ISO C++ standard, planned for 2020. Support is highly experimental, and will almost certainly change in incompatible ways in future releases.
So not all features can be expected to be in there.
GCC has a tradition of providing aliases for the not yet released standards or those with incomplete support. GCC 9 only knows c++2a and that became an alias for c++20 with GCC 10.
CMake can handle this. When in doubt what CMake uses as standard (or any other option), take a look in the build directory in flags.make or build.ninja (depending on which generator you're using).

using lists in generator expressions with "target_link_libraries" command (unwanted -l flag)

I'm trying to use generator expressions to link imported libraries for my downstream builds. (using cmake 3.11.1 btw)
Since the generator command in TARGET_LINK_LIBRARIES doesn't accept whitespace, I discovered I need to use the 'JOIN' command. This seems to be adding an unwanted -l flag to my generated ninja file, however, and breaking the build.
# link dependencies
TARGET_LINK_LIBRARIES(TGT1
"$<JOIN:$<$<BOOL:${DOWNSTREAM_PIPELINE}>:${IMPORTED_LIBS_FOR_TGT1}>, >")
TARGET_LINK_LIBRARIES(TGT2
"$<JOIN:$<$<BOOL:${DOWNSTREAM_PIPELINE}>:${IMPORTED_LIBS_FOR_TGT2}>, >" $<TARGET_FILE:TGT1>)
the result in my Ninja file is:
LINK_LIBRARIES = -llibModuleA libModuleB libModuleC ...
What's going on here?
Thanks #StephenNewell for the hint. Since the list had no spaces, I can use the BOOL generator expression. I was just having problems with the quotes being in the wrong place. The below works:
# link dependencies
TARGET_LINK_LIBRARIES(TGT1
"$<$<BOOL:${DOWNSTREAM_PIPELINE}>:${IMPORTED_LIBS_FOR_TGT1}>")
TARGET_LINK_LIBRARIES(TGT2
"$<$<BOOL:${DOWNSTREAM_PIPELINE}>:${IMPORTED_LIBS_FOR_TGT2}>" $<TARGET_FILE:TGT1>)

CMake Error : execution of make failed on Windows

I am getting errors when trying to build nanomsg project in Windows 7:
cmake ..
-- Building for: NMake Makefiles
-- The C compiler identification is GNU 4.7.1
-- Check for working C compiler: C:/Program Files (x86)/CodeBlocks/MinGW/bin/gcc.exe
CMake Error: Generator: execution of make failed. Make command was: "nmake" "/NOLOGO" "cmTC_5d837\fast"
-- Check for working C compiler: C:/Program Files (x86)/CodeBlocks/MinGW/bin/gcc.exe -- broken
CMake Error at C:/Program Files (x86)/cmake-3.9.4-win64-x64/share/cmake-3.9/Modules/CMakeTestCCompiler.cmake:51 (message):
The C compiler "C:/Program Files (x86)/CodeBlocks/MinGW/bin/gcc.exe" is not
able to compile a simple test program.
It fails with the following output:
Change Dir: C:/Users/User/Documents/Internal/nanomsg-master/build/CMakeFiles/CMakeTmp
Run Build Command:"nmake" "/NOLOGO" "cmTC_5d837\fast"
Generator: execution of make failed. Make command was: "nmake" "/NOLOGO"
"cmTC_5d837\fast"
CMake will not be able to correctly generate this project.
Call Stack (most recent call first):
CMakeLists.txt:29 (project)
-- Configuring incomplete, errors occurred!
See also "C:/Users/User/Documents/Internal/nanomsg-master/build/CMakeFiles/CMakeOutput.log".
See also "C:/Users/User/Documents/Internal/nanomsg-master/build/CMakeFiles/CMakeError.log".
I use gcc compiler and make from Mingw toolchain and I can run succesfully gcc.exe and mingw32-make.exe on a simple example.
In the file CMakeCache.txt the cache variables are set as follows:
//C compiler
CMAKE_C_COMPILER:FILEPATH=C:/Program Files (x86)/CodeBlocks/MinGW/bin/gcc.exe
//Program used to build from makefiles.
CMAKE_MAKE_PROGRAM:STRING=nmake
I think that the problem comes from CMAKE_MAKE_PROGRAM variable it should take C:/Program Files (x86)/CodeBlocks/MinGW/bin/mingw32-make.exe, however i dont understand from where it gets the value nmake.
Even i replaced it manually I get the same problem.
My questions :
How CMake fills the Cache variables ?
Why CMAKE_MAKE_PROGRAM takes the value nmake ?
Why changing manually this variable didn t solve the problem ?
CMake fills the cache file with the values it detects based what is in CMakeLists.txt and whatever files it includes in combination with any -D paramters supplied to cmake.
On Windows CMake will default to Microsoft's nmake tool. The way to override this is by passing parameter -G"MinGW Makefiles" to cmake, or in case you use MSYS shell -G"MSYS Makefiles".
But there is a faster build tool than make called Ninja (get it from https://ninja-build.org/) which you can use by passing -GNinja to cmake.
Note: I see you're using the old MinGW that comes with Code::Blocks. There is a more up to date successor to MinGW called MinGW-w64, which supports both Windows 32-bit and 64-bit. A recent standalone build can be downloaded from https://winlibs.com/ and it also includes ninja.exe.
P.S.: If you run into more issues building the nanomsg sources after following these tips, consider passing -DNN_TESTS:BOOL=OFF to cmake

How do I get a verbose output for CMake?

I would like to investigate why I have this error:
$ cmake ..
-- The C compiler identification is unknown
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /cygdrive/c/Users/Ycr/Home/bin/arm-none-eabi-gcc
-- Check for working C compiler: /cygdrive/c/Users/Ycr/Home/bin/arm-none-eabi-gcc -- broken
CMake Error at /usr/share/cmake-3.6.2/Modules/CMakeTestCCompiler.cmake:61 (message):
The C compiler "/cygdrive/c/Users/Ycr/Home/bin/arm-none-eabi-gcc" is not
able to compile a simple test program.
Unfortunately after the error:
I have no idea of what CMake did. I don't have a verbose log of the command it executed.
The CMakeFiles/cmTC_e4aa4.dir was cleaned after the error, so I have no possibility to explore the issue myself.
How should I investigate such an error?
I tried to use the --debug-trycompile option. This time CMake creates a CMakeTmp folder which makes perfectly without errors. However, I still have this CMakeFiles/cmTC_e4aa4.dir that generates errors and even with the option CMake unlinks the folder.
Getting a Verbose Log
The try_compile() calls that CMake does in the beginning to test the compiler, gives a detailed error output on the console and writes it to
[your binary output directory]/CMakeFiles/CMakeError.log
I've checked the source code again and there is no CMake option that would give more a more detailed output for CMake's internal try_compile() calls.
You could just force the output to standard output by adding some variable_watch() calls to your main CMakeLists.txt before your project() call like:
variable_watch(__CMAKE_C_COMPILER_OUTPUT)
variable_watch(__CMAKE_CXX_COMPILER_OUTPUT)
Keeping the Temporary Files
To keep the temporary file of try_compile, add --debug-trycompile to the cmake command line.
But be aware that the multiple compiler tests at the beginning overwrite the artifacts of previous ones:
It may however change the results of the try-compiles as old junk from a previous try-compile may cause a different test to either pass or fail incorrectly. This option is best used for one try-compile at a time, and only when debugging.
References
How to keep generated temporary files?
CMake error at CMakeLists.txt:30 (project): No CMAKE_C_COMPILER could be found
For me, none of the log files in my output directory contained useful information from try_compile(), even when using --debug-trycompile.
I ended up using the OUTPUT_VARIABLE option to capture and then print the output like this:
try_compile(<options> OUTPUT_VARIABLE TRY_COMPILE_OUTPUT)
message(WARNING ${TRY_COMPILE_OUTPUT})

How to specify a compiler in CMake?

I would like to use the IAR compiler. I noticed CMake has already have a bunch of files about this compiler:
https://github.com/jevinskie/cmake/blob/master/Modules/Compiler/IAR.cmake
From what I read the common solution is to specify manually ALL the toolchain in my CMakeLists.txt:
set(CMAKE_C_COMPILER iccarm)
set(CMAKE_CPP_COMPILER iccarm)
How CMake can link these definitions with `Modules/Compiler/IAR.cmake"?
I thought I would just have to do
include("Modules/Compiler/IAR.cmake")
What is the correct way to specify my IAR compiler?
When I do
cmake .
It still tries to use gcc instead of my IAR compiler. Why?
To select a specific compiler, you have several solutions, as exaplained in CMake wiki:
Method 1: use environment variables
For C and C++, set the CC and CXX environment variables. This method is not guaranteed to work for all generators. (Specifically, if you are trying to set Xcode's GCC_VERSION, this method confuses Xcode.)
For example:
CC=gcc-4.2 CXX=/usr/bin/g++-4.2 cmake -G "Your Generator" path/to/your/source
Method 2: use cmake -D
Set the appropriate CMAKE_FOO_COMPILER variable(s) to a valid compiler name or full path on the command-line using cmake -D.
For example:
cmake -G "Your Generator" -D CMAKE_C_COMPILER=gcc-4.2 -D CMAKE_CXX_COMPILER=g++-4.2 path/to/your/source
Method 3 (avoid): use set()
Set the appropriate CMAKE_FOO_COMPILER variable(s) to a valid compiler name or full path in a list file using set(). This must be done before any language is set (ie: before any project() or enable_language() command).
For example:
set(CMAKE_C_COMPILER "gcc-4.2")
set(CMAKE_CXX_COMPILER "/usr/bin/g++-4.2")
project("YourProjectName")
The wiki doesn't provide reason why 3rd method should be avoided...
I see more and more people who set CMAKE_C_COMPILER and other compiler-related variables in the CMakeLists.txt after the project call and wonder why this approach breaks sometimes.
What happens actually
When CMake executes the project() call, it looks for a default compiler executable and determines the way for use it: default compiler flags, default linker flags, compile features, etc.
And CMake stores path to that default compiler executable in the CMAKE_C_COMPILER variable.
When one sets CMAKE_C_COMPILER variable after the project() call, this only changes the compiler executable: default flags, features all remains set for the default compiler.
AS RESULT: When the project is built, a build system calls the project-specified compiler executable but with parameters suitable for the default compiler.
As one could guess, this approach would work only when one replaces a default compiler with a highly compatible one. E.g. replacement of gcc with clang could work sometimes.
This approach will never work for replacement of cl compiler (used in Visual Studio) with gcc one. Nor this will work when replacing a native compiler with a cross-compiler.
What to do
Never set a compiler in CMakeLists.txt.
If you want, e.g., to use clang instead of defaulted gcc, then either:
Pass -DCMAKE_C_COMPILER=<compiler> to cmake when configure the project. That way CMake will use this compiler instead of default one and on the project() call it will adjust all flags for the specified compiler.
Set CC environment variable (CXX for C++ compiler). CMake checks this variable when selects a default compiler.
(Only in rare cases) Set CMAKE_C_COMPILER variable before the project() call. This approach is similar to the first one, but makes the project less flexible.
If the ways above do not work
If on setting CMAKE_C_COMPILER in the command line CMake errors that a compiler cannot "compile a simple project", then something wrong in your environment.. or you specify a compiler incompatible for chosen generator or platform.
Examples:
Visual Studio generators work with cl compiler but cannot work with gcc.
A MinGW compiler usually requires MinGW Makefiles generator.
Incompatible generator cannot be fixed in CMakeLists.txt. One need to pass the proper -G option to the cmake executable (or select the proper generator in CMake GUI).
Cross-compiling
Cross-compiling usually requires setting CMAKE_SYSTEM_NAME variable, and this setting should normally be done in the toolchain file. That toolchain file is also responsible for set a compiler.
Setting CMAKE_SYSTEM_NAME in the CMakeLists.txt is almost always an error.
You need to create a toolchain file, and use the CmakeForceCompiler module.
Here is an example toolchain file for bare-metal ARM development with IAR:
include(CMakeForceCompiler)
set(CMAKE_SYSTEM_NAME Generic) # Or name of your OS if you have one
set(CMAKE_SYSTEM_PROCESSOR arm) # Or whatever
set(CMAKE_CROSSCOMPILING 1)
set(CMAKE_C_COMPILER iccarm) # Change the arm suffix if appropriate
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) # Required to make the previous line work for a target that requires a custom linker file
The last line is necessary because CMake will try to compile a test program with the compiler to make sure it works and to get some version information from preprocessor defines. Without this line, CMake will use add_executable() for the test program, and you will get the error "The C compiler "XXX" is not able to compile a simple test program." This is because the test program fails to link, as it doesn't have your custom linker file (I'm assuming bare-metal development since this is what IAR is usually used for). This line tells CMake to use add_library() instead, which makes the test succeed without the linker file. Source of this workaround: this CMake mailing list post.
Then, assuming that your toolchain file is named iar-toolchain.cmake, invoke CMake like this:
cmake -DCMAKE_TOOLCHAIN_FILE=iar-toolchain.cmake .
You can call cmake like this:
cmake -DCMAKE_C_COMPILER=iccarm ...
or
cmake -DCMAKE_CXX_COMPILER=...
If you don't want to use your PC's standard compiler, you have to give CMake the path to the compiler. You do this via environment variables, a toolchain file or direct definitions in the CMake command line (see e.g. CMake Error at CMakeLists.txt:30 (project): No CMAKE_C_COMPILER could be found).
Putting the compiler's name/path into your CMakeLists.txt would stop your project from being cross-platform.
CMake does check for the compiler ids by compiling special C/C++ files. So no need to manually include from Module/Compiler or Module/Platform.
This will be automatically done by CMake based on its compiler and platform checks.
References
CMake: In which Order are Files parsed (Cache, Toolchain, …)?
CMake GitLab Commit: Add support files for C, C++ and ASM for the IAR toolchain.
IAR Systems recently published a basic CMake tutorial with examples under their GitHub profile.
I like the the idea of a generic toolchain file which works seamlessly for both Windows and Linux compilers using find_program().
The following snippet will be used for when using C and can be used similarly for CXX:
# IAR C Compiler
find_program(CMAKE_C_COMPILER
NAMES icc${CMAKE_SYSTEM_PROCESSOR}
PATHS ${TOOLKIT}
"$ENV{ProgramFiles}/IAR Systems/*"
"$ENV{ProgramFiles\(x86\)}/IAR Systems/*"
/opt/iarsystems/bx${CMAKE_SYSTEM_PROCESSOR}
PATH_SUFFIXES bin ${CMAKE_SYSTEM_PROCESSOR}/bin
REQUIRED )
For ASM, I initially got puzzled with the NAMES but then I realized that the toolchain file was made that way for working with old Assemblers shipped with XLINK:
find_program(CMAKE_ASM_COMPILER
NAMES iasm${CMAKE_SYSTEM_PROCESSOR} a${CMAKE_SYSTEM_PROCESSOR}
PATHS ${TOOLKIT}
"$ENV{PROGRAMFILES}/IAR Systems/*"
"$ENV{ProgramFiles\(x86\)}/IAR Systems/*"
/opt/iarsystems/bx${CMAKE_SYSTEM_PROCESSOR}
PATH_SUFFIXES bin ${CMAKE_SYSTEM_PROCESSOR}/bin
REQUIRED )
Also, take a look at the full toolchain file. It will work automatically for "Arm" when the tools are installed on their default locations, otherwise it is just about updating the TOOLKIT variable and the compilers for all the supported languages should adjust automatically.
If your wanting to specify a compiler in cmake then just do ...
cmake_minimum_required(VERSION 3.22)
set(CMAKE_C_COMPILER "clang")
set(CMAKE_CXX_COMPILER "clang++")
Options 1 is only used if you want to specify what compiler you want to use as default for everything that you might compile on your computer. And I don't even think it would work on windows.
Option 2 would be used if you only want to use a different temporarily.
Option 3 is used if that's the compiler that should be used for that particular project. Also option 3 would be the most cross compatible.