Share cmake variables between different folders - cmake

Suppose I have a project setup like so:
Project
'-> CMakeLists.txt
'-> src
'-> CMakeLists.txt
'-> third_party
'-> CMakeLists.txt
In the CMakeLists.txt of third_party I have a variable set like so:
cmake_path( APPEND ${CMAKE_SOURCE_DIR} "third_party" OUTPUT_VARIABLE PROJECT_THIRD_PARTY_DIR )
In the top level CMakeLists.txt I have both 'third_party' and 'src' included and the third party folder has a particular prebuilt project as well as others that will be built. Is is possible to have access to the variable '${PROJECT_THIRD_PARTY_DIR}' from the src CMakeLists.txt or would I have to move the definition into the master CMakeLists.txt? From what I have found online it seems it can be done by using 'set( .... CACHE INTERNAL )' but I would prefer to use what I thought to be the correct (and newer) features of cmake 3.20 for this path variable.

First of all, you should minimize adding variables to your Cmake path.
Paths can change between operating systems and between different installations of the same operating system, so you should save information with Cmake variables.
Even if you are trying to use new Cmake, sticking with the set() command allows for better back-portability with earlier versions of Cmake. I would stay with the set(<blah name> CACHE INTERNAL ) call and move on.
Another note:
A variable defined in your top level Cmake file is accessible to the lower level CMake files. When the top level Cmake includes another Cmake file, the Cmake files included later also have access to the previous sub-Cmake file variables (with a few exceptions, like the filepath of the previous Cmake file). Ignoring the exceptions, the simple solution to me is to define your 3rd party sources first in your top level Cmake and then the later Cmake files in your own source code can access them.

Related

Using Cmake to add compiler options to existing cmake

I have a submodule with a CMakeLists.txt, which I would like to "amend" in a way to make it work for my specific purpose (I need to add compiler flags and a different output directory) The output is a static library. My current approach is as follows:
I have a CMakeLists.txt in the root directory, which adds the submodule CMakeLists.txt via add_subdirectory() like so:
set(CMAKE_CXX_FLAGS "-my_needed_flags "
add_subdirectory(SubmoduleLibrary)
set_target_properties(SubmoduleLibrary PROPERTIES ARCHIVE_OUTPUT_DIRECTORY output_folder)
add_custom_target(BuildLib)
add_dependencies(BuildLib SubmoduleLibrary)
Now this seems to work in some ways (the output directory is correctly chosen). But I get linker errors which I don't get when I compile the library adding the flags etc to the original CMakeLists.txt.
What is the correct way to build a library from an existing CMakeLists.txt with added parameters?

CMake generates project twice

I'm grouping sources in folders by some rules and I want to be able to individually test each group in isolation for faster iteration. To do so I want to CMake to generate one executable for each group. I simplified the project structure to:
root
build
group_1
CMakeLists.txt
group_1_file_1.cpp
group_1_tests
CMakeLists.txt
group_1_file_1_tests.cpp
where group_1/CMakeLists.txt is:
SET(group_1_srcs
${CMAKE_CURRENT_SOURCE_DIR}/group_1_file_1.cpp
)
#Generate standalone application for unit tests when cmake is started with this CMakeLists as root
IF(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
ADD_SUBDIRECTORY("group_1_tests/")
ENDIF()
and group_1/group_1_tests/CMakeLists.txt is
PROJECT(group_1_tests)
SET(group_1_tests_srcs
${CMAKE_CURRENT_SOURCE_DIR}/group_1_file_1_tests.cpp
)
ADD_EXECUTABLE(${PROJECT_NAME} "${group_1_tests_srcs}" "${group_1_srcs}")
Running cmake ../group_1 from root/build/ generates me the project I want, root/build/group_1_tests/group_1_tests.sln, but also root/build/Project.sln with the same files & settings as the previous one.
Can you please explain why this projects gets generate and how may I avoid it?
The answer is in the project() command documentation:
The top-level CMakeLists.txt file for a project must contain a literal, direct call to the project() command ... If no such call exists CMake will implicitly add one to the top that enables the default languages (C and CXX).
So Project is the default project's name. And from that point onward CMake generates a .sln solution for all CMakeLists.txt containing another project() command.
I would recommend you ignore the root solution file (if you don't want it) or restructure your project to have a single project() command call in the root CMakeList.txt (since your sub-level project won't run standalone without the top-level project anyway).
References
CMake Commit: ENH: generate a sln and dsw file for each sub project in a project

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"

CMake with regarding generated files

Good day everyone.
I have the following situation: I have a CMake file, which is supposed to compile my application, which consists of:
one or more cpp files
some template files (ecpp), which on their turn are generated into cpp files, which are compiled into the application (they are listed below in the WEB_COMPONENTS so for each component there is the associated .ecpp file and the .cpp that will be generated from it).
And here is the CMakeLists.txt (simplified)
cmake_minimum_required (VERSION 2.6)
set (PROJECT sinfonifry)
set (ECPPC /usr/local/bin/ecppc)
set (WEB_COMPONENTS
images
menu
css
)
set(${PROJECT}_SOURCES
""
CACHE INTERNAL ${PROJECT}_SOURCES
)
foreach(comp ${WEB_COMPONENTS})
list(APPEND ${PROJECT}_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${comp}.cpp )
execute_process(COMMAND ${ECPPC} -o ${CMAKE_CURRENT_BINARY_DIR}/${comp}.cpp -v
${CMAKE_CURRENT_SOURCE_DIR}/${comp}.ecpp
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_QUIET
)
endforeach()
list(APPEND ${PROJECT}_SOURCES main.cpp )
add_executable(${PROJECT}_exe ${${PROJECT}_SOURCES})
target_link_libraries(${PROJECT}_exe cxxtools dl tntnet tntdb)
Now, what happens: for the very first time (ie: make the build directory, run cmake-gui, select web component, configure, generate, make) the CMake nicely executes the ${ECPPC} command, ie. it generates the required CPP files in the binary directory, and links them together.
After a while, obviously while I work, I modify one of the component files (such as images.ecpp) and run make again in the build directory. But now, CMake does not pick up the changes of the ecpp files. I have to go to cmake-gui, delete cache, restart everything from zero. This is very tiresome and slow.
So, two questions:
Cand I tell CMake to track the changes of the images.ecpp and call the ${ECPPC} compiler on it if it changed?
How can I make clean so that it also removes the generated cpp files.
Thank you for your time, f.
Instead of execute_process() you want to use add_custom_command(). See here: https://stackoverflow.com/a/2362222/4323
Basically you tell CMake the OUTPUT (the generated filename), COMMAND, and DEPENDS (the .ecpp filename). This makes it understand how to turn the source into the necessary C++ generated file. Then, add the generated file to some target, e.g. add_executable(), or to an add_custom_command() dependency (if it didn't need to be compiled you'd more likely need that).

Changing cmake directories

I'm writing a cmake file for a project which has the following structure
project/ (root)
libraries/ (contains (precompiled) libraries
src/
code/ (contains a set of fortran files)
My CMakeLists.txt file is currently in project/ and effectively is just
cmake_minimum_required(VERSION 2.6)
enable_language(Fortran)
project(project1)
set(projsrc src/code)
set(libdir lib/)
find_library(PROJ_LIBRARY pr10 PATHS ${libdir})
add_executable (sc1 sc1.f90)
target_link_libraries(sc1 ${PROJ_LIBRARY})
This creates my binary in the same folder as the source code, when I actually want it in the level above (i.e. in the src folder - this structure will be changed so we have a bin folder eventually), but haven't worked out how to do it.
Some answers on SO say you have to have a CMakeLists.txt file in every folder - is this correct? Is it possible to set an environment variable or use a CMake variable (e.g. http://cmake.org/cmake/help/v2.8.8/cmake.html#command:set). It's also not very clear from some answers whether the solutions they have posted are C++ specific (as that is what language CMake most often seems to be used for).
Edit
I found out that I can change it to the behaviour I want by modifying it slightly:
cmake_minimum_required(VERSION 2.6)
enable_language(Fortran)
project(project1)
set(projsrc src/code)
set(libdir lib/)
find_library(PROJ_LIBRARY pr10 PATHS ${libdir})
add_executable (src/sc1 ${projsrc}/sc1.f90)
target_link_libraries(src/sc1 ${PROJ_LIBRARY})
However, this doesn't explain why my behaviour is different to how it should be, according to arrowdodger below. I'm also still trying to work out how to display the values of environment variables; I've tried the following with no luck:
message(${RUNTIME_OUTPUT_DIRECTORY})
message($ENV{RUNTIME_OUTPUT_DIRECTORY})
message(${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
message($ENV{CMAKE_RUNTIME_OUTPUT_DIRECTORY})
By default, binaries will appear in respective subdirectory of your build dir. By respective i mean the directory that contains CMakeLists.txt with add_executable() call.
For example, if you have following CMakeLists.txt
add_executable(tgt1 src1.f90)
add_executable(tgt2 subdir/src2.f90)
in the root folder, you will get both binaries in ${CMAKE_BINARY_DIR}. So if you wish tgt2 to be in ${CMAKE_BINARY_DIR}/subdir, you need to add CMakeLists.txt there and call add_executable(tgt2 src2.f90) from there.
You can change this behavior:
CMAKE_LIBRARY_OUTPUT_DIRECTORY, CMAKE_RUNTIME_OUTPUT_DIRECTORY and others.
You can also set respective target properties.