CMake: pass library name to grandparent, but only if grandparent exists - cmake

A library foo is to be built either as a project of its own, or as part of a larger project bar. For the latter, I found no better solution than line (*):
$ cat foo/lib/CMakeLists.txt
...
set(foo_LIBRARY foo PARENT_SCOPE)
...
$ cat foo/CMakeLists.txt
...
add_subdirectory(lib)
set(cerf_LIBRARY ${cerf_LIBRARY} PARENT_SCOPE) # (*)
...
$ cat bar/CMakeLists.txt
...
add_subdirectory(link-to-foo)
...
Now building bar works. But when building only foo, I get
CMake Warning (dev) at CMakeLists.txt:30 (set):
Cannot set "foo_LIBRARY": current scope has no parent.
Aiming for zero warnings in my projects, I'd need a better solution.

Related

How to get the name of the parent project in CMake?

Please see the below minimal example:
├───CMakeLists.txt
├───bar
│ ├───CMakeLists.txt
CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(foo)
add_subdirectory(bar)
bar/CMakeLists.txt
project(bar)
cmake_path(GET CMAKE_CURRENT_LIST_DIR PARENT_PATH BAR_PARENT_DIR)
# how can I get `foo` given ${BAR_PARENT_DIR}?
# or is there a better way to get `foo`?
The real use case is that originally foo was extracted via ${CMAKE_PROJECT_NAME}, but recently there's a need to make the repo submodule compatible. Once this repo is being used as a submodule, ${CMAKE_PROJECT_NAME} won't be equivalent to foo anymore. Additionally, bar is a submodule of foo, so we aren't allowed to hard code foo into bar/CMakeLists.txt because that would break other repos that are using bar as a submodule.
Is there a way to extract the project name of a CMakeLists.txt from a parent directory?
Edit: I am looking for a solution that will make the below scenario work. Meaning foo is submoduled by another project. e.g.
baz
├───CMakeLists.txt
├───foo
│ ├───CMakeLists.txt
│ ├───bar
│ ├───CMakeLists.txt
Yes, the project name of the immediate parent.
This is not so hard. The project() command always sets the PROJECT_NAME variable when it is called. So the value of that variable just before you call project() is the name of the immediate parent.
There's nothing standard for this, but it's trivial to implement:
cmake_minimum_required(VERSION 3.23)
set(PARENT_PROJECT_NAME "${PROJECT_NAME}")
project(bar)
if (PARENT_PROJECT_NAME)
message(STATUS "Found parent: ${PARENT_PROJECT_NAME}")
else ()
message(STATUS "No parent!")
endif ()

How can cmake detect misspelled variable names on command line?

My CMakeLists.txt can take variables and values when the user specifies them on the command line in the usual form -Dname=value. E.g.
% cmake -DmyVariable=someValue ..
How can CMakeLists.txt detect variables that aren’t actually relevant, e.g. in case the user mispells them:
% cmake -Dmyxvarble=someValue ..
For example, can CMakeLists.txt process each defined variable on the command line sequentially, thereby spotting misspelled variable names?
I’m running cmake version 3.18.0-rc2. Thanks!
You could query the cache entries of the toplevel dir and match against patterns of expected entries. Note though that this is not easy to maintain, since functionality like find_package relies on cache variables.
set(CACHE_VARIABLE_WHITELIST
MyProject_BINARY_DIR
MyProject_IS_TOP_LEVEL
MyProject_SOURCE_DIR
...
)
get_directory_property(CACHE_VARS DIRECTORY ${CMAKE_SOURCE_DIR} CACHE_VARIABLES)
foreach(CACHE_VAR IN LISTS CACHE_VARS)
# fatal error for any non-advanced cache variable
# not in the whitelist and not starting with CMAKE_
get_property(IS_ADVANCED CACHE ${CACHE_VAR} PROPERTY ADVANCED)
if (NOT IS_ADVANCED AND NOT CACHE_VAR MATCHES "^CMAKE_.*" AND NOT CACHE_VAR IN_LIST CACHE_VARIABLE_WHITELIST)
message(FATAL_ERROR "Unexpected cache variable set: ${CACHE_VAR}")
endif()
endforeach()

Is it possible to expand `include(foo.cmake)`'s content in cmake?

I would like to get the "expanded CMakeLists.txt" that replace each include(foo.cmake) with corresponding content. Don't know if it is possible.
For example, I have CMakeLists.txt with contents:
cmake_minimum_required(VERSION 3.20)
project(hello)
include(hello.cmake)
message(STATUS "***** hello_str is ${hello_str}")
and hello.cmake with contents:
set(hello_str "Hello, World")
I would get an expaned CMakeLists.txt with contents (yeah, just like C/C++'s preprocess)
cmake_minimum_required(VERSION 3.20)
project(hello)
set(hello_str "Hello, World") ##!!
message(STATUS "***** hello_str is ${hello_str}")
Is that possible? And how?
OK, due to people in the comments really didn't understand, I have to make it more clear.
In the cross-compilation stage, the commonly usage is:
mkdir build && cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=qnx-aarch64.toolchain.cmake
cmake --build .
But actually the qnx-aarch64.toolchain.cmake contains one line:
include(linux-aarch64.toolchain.cmake)
Thus, people have to have both qnx-aarch64.toolchain.cmake and linux-aarch64.toolchain.cmake, instead only one qnx-aarch64.toolchain.cmake file.
What I expected is only one qnx-aarch64.toolchain.cmake file to finish the cross-compilation.
#fabian You don't understand my question and keep telling very simple stuffs and assumes I don't know those stuffs.
Is that possible?
No. CMake 3.22 (the current at time of writing) and below do not provide this feature.

CMake: How do I get the project's directory from the toolchain file?

I have a project which is structured like this
project:
-- CMakeLists.txt
-- toolchain.cmake
-- source
-- includes
-- tools
-- arm-toolchain
-- arm-gnueabihf-gcc
-- ...
-- libraries
-- lib1
-- ...
-- CMakeLists.txt
-- lib2
-- ...
-- CMakeLists.txt
I would like to set the compiler for another architecture inside the toolchain.cmake like this:
...
set (CMAKE_C_COMPILER "${CMAKE_SOURCE_DIR}/tools/arm-toolchain/arm-gnueabihf-gcc")
...
Unfortunately the variable ${CMAKE_SOURCE_DIR} does change while compiling the project, at first it is:
CMAKE_SOURCE_DIR=/home/username/project
That's exactly what I expected. Unfortunately, it changes then to
CMAKE_SOURCE_DIR=/home/username/project/build/CMakeFiles/CMakeTmp
Therefore I cannot use ${CMAKE_SOURCE_DIR} as the root of my project-folder, is there another variable which does not change? Or, can I store the first value somehow? The problem is that the toolchain is invoked more than once, therefore a variable in which I store the initial value of ${CMAKE_SOURCE_DIR} is overwritten on the second run.
I found an answer here: https://gitlab.kitware.com/cmake/cmake/issues/16744
CMAKE_CURRENT_LIST_DIR does indeed work in this case.

CMake add prefix to each element of a list

In GNU Make I would have written this:
DEF=FOO BAR BAZ
CMAKE_REQUIRED_DEFINIITIONS=$(addprefix -D,$(FOO))
Is there an equivalent in CMake other than doing this:
macro(addprefix prefix list_name)
SET(${list_name}_TMP)
foreach(l ${list_name})
list(APPEND ${list_name}_TMP ${prefix}${l} )
endforeach()
SET(${list_name} ${list_name}_TMP)
UNSET(${list_name}_TMP)
endmacro(addprefix)
set(APPEND required_definitions FOO BAR BAZ)
set(CMAKE_REQUIRED_DEFINITIONS addprefix(-D required_definitions))
CMake is supposed to be easier to use than GNU Make...
Reformulating my previous comment as answer:
target_compile_definitions(your_target PUBLIC ${your_list})
is probably the CMake command you are looking for.