Setting CMAKE_SYSROOT by generator expression - cmake

In our project we are setting CMAKE_SYSROOT depending on the selected configuration. It is so because configuration expresses (amongst others) target platform (cross-compilation - but not only, also slight behavior differences).
We would like to express this with generator expression to be friendly towards multi-configuration IDEs.
However, we haven't found a way to do so. First, you will notice that CMAKE_SYSROOT doesn't even mention such a possibility. We still tried to set it to something like this (not exact value - just a sample!):
set(CMAKE_SYSROOT $<IF:$<CONFIG:hw1>,path1,path2>)
hoping that the value is used in a context where generator expressions are supported. But apparently, it is not. Instead, it seems that the verbatim value is provided in --sysroot argument (--sysroot="\$<IF:\$<CONFIG:hw1>,path1,path2>"). Adding quotes around the value doesn't change anything.
What other options do we have?
Let me also add a note on the CMAKE_TOOLCHAIN_FILE which is mentioned in the documentation of CMAKE_SYSROOT.
I don't see the CMAKE_TOOLCHAIN_FILE being set to anything after grep-ing through files generated by cmake -DCMAKE_BUILD_TYPE=hw1 ../ and our own sources.
Apparently, the file where we set the CMAKE_SYSROOT is not pointed to by CMAKE_TOOLCHAIN_FILE. Yet, still, the --sysroot argument is being set to the expected value. (Only not by generator expression!) So, it does work somehow.
I expect we will have the same issue with other variables as well:
CMAKE_SYSTEM_NAME,
CMAKE_SYSTEM_PROCESSOR,
CMAKE_CXX_COMPILER,
CMAKE_C_COMPILER
the last two depend on the CMAKE_SYSROOT anyway (in our case).

If you really want to pass different --sysroot flags to the linker on a multi-configuration generator you'll just have to pass it via target_link_options using a generator expression. But then you might have to update rpath items yourself, but I'm not sure about that.
Variables are not used at build time and the documentation for generator expressions state:
Generator expressions are allowed in the context of many target properties...
I didn't see anything in the set command that prevents CMAKE_SYSROOT being set outside a tool-chain file. I'm guessing that the phrase This variable may only be set in a toolchain file should be This variable is normally used in a toolchain file.
It almost seems like that you are trying to use build type as a switch between two different tool chains. If that is the case then I don't see how that could work correctly with a multi-configuration generator. Most of everything you want to set is determined and used at configuration time not build time.

Related

CMake function arguments -- why are they occasionally uppercase?

I've been combing through the CMake documentation for a while now trying to figure out why some function arguments are capitalised and some aren't. I was hoping someone might be able to explain the pattern of things being capitalised, or at the very least point me in the direction of the documentation. This documentation https://cmake.org/cmake/help/v3.0/manual/cmake-language.7.html#cmake-language-7 gives very little in the way of explaining why certain things are capitalised.
From my current understanding,
uppercase arguments can be:
Keyword arguments of the function
Flags
Properties
Lowercase arguments are:
Other types of inputs to the function
Here's an example of a CMake file which you can maybe help me dissect?
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
include(SomeLib)
include_directories(include)
add_library(mylib SHARED src/main.cpp)
target_link_libraries(mylib ${boost_LIBRARIES})
install(TARGETS mylib DESTINATION lib)
VERSION I'm guessing is a keyword,
SHARED is probably also a keyword, or possibly a property,
TARGETS and DESTINATION are also keywords?
There is no some accepted style for writing CMake code so you can encounter anything. What you usually see is a historical somewhat accepted style used by the devs (Kitware) which was adopted by many others.
Usually you use lower case for CMake command, macro and function names, so to correct your code you need to lower case for the version command: cmake_minimum_required(VERSION 2.8) because it is a command.
Some commands have named arguments, some not. Usually named arguments are in upper case. In your example TARGETS, VERSION, SHARED and DESTINATION are all named arguments (which CMake calls keywords). They can be of any case but we just used to make them uppercase. Such arguments got parsed with the cmake_parse_arguments help for functions & macros. Since all commands are implemented in C++ the argument parsing is done in C++ for them (probably with the same "command").
Apart from the command names, lower case might be used in functions for local variables but that's not established and you can find any kind of mixing in the wild.
This is an addon to #ixSci's answer.
Some history of CMake.
Look at this message from 2003 asking if there were plans to allow lowercase commands.
No plans on changing this.
Much has changed. Check out this commit from 9 years ago, which changed a bunch of existing code from uppercase to lowercase. Here's a quote:
Ancient CMake versions required upper-case commands.
Later command names became case-insensitive.
Now the preferred style is lower-case.
Nowadays, CMake commands are case-insensitive and remain backwards-compatible. However, CMake variables remain case-sensitive.
Further, CMake 2.6 introduced cmake_policy, which can be used to specify a specific syntax version. Maybe you're feeling nostalgic...

Cmake - Documentation

I use clang, gcc (and also their arm version).
is there somewhere a documentation, where i can see which values the following 2 arguments take:
set(CMAKE_SYSTEM_NAME <value>)
set(CMAKE_SYSTEM_PROCESSOR <value>)
i cannot find any doc, where cmake lists every possible input.
i just see that you have to assign a value to those, but what options do i have?
like system name: Linux is one, how about Windows?
And then what about processors, arm, x86, x86_64 (amd64)...
would be cool if someone knows a good source of documentation.
thanks
From the CMake wiki about cross compiling:
Once the system and the compiler are determined by CMake, it loads the
corresponding files in the following order:
Platform/${CMAKE_SYSTEM_NAME}.cmake (optional, but issues a stern
warning)
Platform/${CMAKE_SYSTEM_NAME}-<compiler>.cmake
(optional)
Platform/${CMAKE_SYSTEM_NAME}-<compiler>-${CMAKE_SYSTEM_PROCESSOR}.cmake
(optional)
So, for find out all possible values for variable CMAKE_SYSTEM_NAME you could check filenames under Modules/Platform and extract the first part of every filename.
As for CMAKE_SYSTEM_PROCESSOR variable, its only purpose is to include the latter file (of 3 components in the filename). From the same wiki:
This variable is not used very much except for one purpose,
it is used to load a CMAKE_SYSTEM_NAME-compiler-CMAKE_SYSTEM_PROCESSOR.cmake file,
which can be used to modify settings like compiler flags etc. for the target.
You probably only have to set this one if you are using a cross compiler
where every target hardware needs special build settings.

How to set CMAKE_FIND_LIBRARY_SUFFIXES to nothing

I have run into an issue when writing find scripts. For some reason, automatic appending of suffixes did not work: only library files with .dylib extension were found, however files with .a extension or no extension at all were not.
Practical experimentation lead me to setting CMAKE_FIND_LIBRARY_SUFFIXES to an empty value and restoring the value afterwards. However, this has following effect:
When cmake is run for the first time, generation fails with "CMake Error: Error required internal CMake variable not set, cmake may not be built correctly. Missing variable is CMAKE_FIND_LIBRARY_SUFFIXES".
When I run cmake second time the same way, generation succeeds.
My guess is, things that were found are saved in cache, and the script for finding them is not run the second time.
My question is, how can I work around that issue and ensure that both my framework (that does not have suffixes) is found and cmake does not stop generation?
I am generating on MacOS, for Xcode.
Not sure what is an "official" way for assign a list of a single empty extension, but you may assign a list of two empty extensions:
set(CMAKE_FIND_LIBRARY_SUFFIXES ";")
Unlike to empty value, this assignment would pass possible sanity checks like if(CMAKE_FIND_LIBRARY_SUFFIXES), which probably exists in CMake code as it reports about "Error required internal CMake variable not set".

Use generator expression to set build type specific flags

I'd like to set some specific compile flags based on my current build configuration in cmake. I thought generator expressions would allow me to do this, but they don't appear to be working the way I expected.
I'm using the following command to set compile options to my main target. Both the expressions appear to always evaluate true as --debug and -Oh are passed to compiler no matter what CMAKE_BUILD_TYPE is set to.
target_compile_options(${PROJECT_NAME}
PUBLIC
${COMMON_COMPILER_FLAGS}
$<$<CONFIG:Debug>:--debug>
$<$<CONFIG:Release>:-Oh>
)
I'm using cmake 3.4.1 on Windows and I'm cross-compiling with the IAR toolchain. To be more specific, I'm executing cmake from the bash shell in Cygwin, but its still the Windows executable. I'm using the Unix Makefile generator.
It looks like the second flag is getting picked up from somewhere and just so happens to be ordered at the right spot to give me the above impression.
I can update with an explanation for that when I determine it, but the generator expressions appear to have always been working. I inserted some keyboard-smash in for both and it became apparent only one was getting through to command line.
EDIT:
The unwanted optimization flag was coming from target_compile_options calls on a couple libraries that are linked to my main target. I had the scope options set to PUBLIC, which means the options populate INTERFACE_COMPILE_OPTIONS. I changed the scopes to PRIVATE and it got rid of the unwanted flag.

Cmake not setting RPATH when adding link_library with -L

When setting link libraries in the following manner
target_link_libraries (SOME_TARGET -L/somedir -lfoo)
cmake doesn't handle RPATHs. Is using '-L' and '-l' not best practice, or actually plain wrong? When creating my own Find*.cmake I usually use find_library() but the find script I got doesn't do this and resorts to the above form using '-L' and '-l'.
The documentation doesn't really explain how RPATHs are gathered, also the documentation isn't really clear how it handles "-l" and "-L" the only pointer you get is
"Item names starting with -, but not -l or -framework, are treated as
linker flags"
Specifying toolchain-dependent flags like -l and -L is generally not recommended, as it breaks portability and might have different effects than you expect.
The correct way to set the linker path would be the link_directories command.
The idiomatic solution in CMake is to use find_library for locating the library and then pass the full path to the linker, so you do not need to worry about link directories at all.
Now, the RPATH is a different beast, as it also determines where dynamic libraries can be located at runtime. Usually, the default settings work reasonably fine here. If you ever find yourself in the unfortunate situation where it does not, there is a number of target properties and CMake variables influencing this:
There are a few properties used to specify RPATH rules. INSTALL_RPATH
is a semicolon-separated list specifying the rpath to use in installed
targets (for platforms that support it). INSTALL_RPATH_USE_LINK_PATH
is a boolean that if set to true will append directories in the linker
search path and outside the project to the INSTALL_RPATH.
SKIP_BUILD_RPATH is a boolean specifying whether to skip automatic
generation of an rpath allowing the target to run from the build tree.
BUILD_WITH_INSTALL_RPATH is a boolean specifying whether to link the
target in the build tree with the INSTALL_RPATH. This takes precedence
over SKIP_BUILD_RPATH and avoids the need for relinking before
installation. INSTALL_NAME_DIR is a string specifying the directory
portion of the “install_name” field of shared libraries on Mac OSX to
use in the installed targets. When the target is created the values of
the variables CMAKE_INSTALL_RPATH, CMAKE_INSTALL_RPATH_USE_LINK_PATH,
CMAKE_SKIP_BUILD_RPATH, CMAKE_BUILD_WITH_INSTALL_RPATH, and
CMAKE_INSTALL_NAME_DIR are used to initialize these properties.
(From the set_target_properties docs)
Also, you might want to have a look at the CMake Wiki page for RPATH handling.
The whole RPATH business is unfortunately rather complex and a thorough explanation would require far more space than is appropriate for a StackOverflow answer, but I hope this is enough to get you started.
Basically, You're using target_link_libraries() wrong. According to documentation, You should provide target, libraries and maybe some CMake specific linkage flags.
For example something like that:
target_link_libraries(my_build_target somedir/foo.so)
If You're using Your own crafted Find*.cmake solutions, it's usualy being done like this:
find_library(foo)
//build main target somewhere here
//now link it:
target_link_libraries(my_build_target ${FOO_LIBRARIES})
NOTE: I assume Your crafted Find*.cmake files follows these guidelines and fills CMake variables like SOMELIB_LIBRARIES, and/or SOMELIB_INCLUDE_DIRS, etc.
NOTE2: for my personal opinion, target_link_directories() is pain in a butt and You should avoid using it if not really needed. It's difficult to maintain and uses paths relative to current source directory.