CMake and FindProtobuf - cmake

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"

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?

How can we refer external cmake file from main CMakeLists.txt file?

Let's say I have some protobuf related cmake code as a library that resides inside CMakeLists.pro file and I need to include this library as a external file configuration. How to do that ?
I think this question is asking how to make cmake modules. IE you have helper code and would like to make it easily available.
A normal top level project has a top level cmake folder. Here is an example of what that would look like.
cmake
tests
src
.gitignore
CMakeLists.txt
README.md
Inside the cmake folder say you have a cmake module called foo.cmake
(It's important to make the file end with .cmake file extension)
Anyway this is what your foo.cmake may look like
# Include guards need at least cmake 3.10
include_guard() # Good practice to use an include_guard()
function(bar)
...
endfunction()
Now in your main cmakelists how do you call this function? Simple.
cmake_minimum_required(VERSION 3.18)
# Add the cmake folder to the cmake module path, this makes it easier to include files
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
# Include foo.cmake
include(foo)
# Call the bar function you defined in foo.cmake
bar()
And that's how you can refer to external cmake file from the main CMakeLists.txt

How must I use cmake so that the "swig plugin" honors the include path

I am using cmake 3.8.1 with the FindSWIG und UseSWIG "extensions" for using swig as code generator. The "swig .i" file contains an include statement to a file in a different directory. The important part of my CMakeLists.txt looks like this:
...
find_package (TCL REQUIRED)
if (TCL_FOUND)
set (HAVE_TCL_H 1)
endif (TCL_FOUND)
find_package (SWIG )
if (SWIG_FOUND)
include(${SWIG_USE_FILE})
set (SWIG_FILE /home/steve/cmake_games/src/foo/bar/bar_swig.i)
set_property(SOURCE ${SWIG_FILE} PROPERTY CPLUSPLUS ON)
swig_add_library (bar_tclext LANGUAGE tcl SOURCES ${SWIG_FILE})
include_directories (/home/steve/cmake_games/src/foo/bar /home/steve/cmake_games/src/this/that)
set_target_properties(bar_tclext PROPERTIES LINKER_LANGUAGE CXX)
swig_link_libraries(bar_tclext ${TCL_LIBRARY})
endif (SWIG_FOUND)
cmake generates make files without a problem. Executing make however leads to the following error message
/home/steve/cmake_games/src/foo/bar/bar_swig.i:3: Error: Unable to find 'that.iih'
that.iih is located in /home/steve/cmake_games/src/this/that
Looking at the swig call in the generated Makefile I can see that swig is not called with an include path.
You might think that CMakeLists.txt with absolute paths is weird. True! This is the case due to the fact that the CMakeLists.txt are generated by scripts that take advantage of our conventions in directory topology and naming. We want keep enforcing this by generating the CMakeLists.txt as part of the "configure" process.
What do I need to change in the CMakeLists.txt?
you should try to setup CMAKE_SWIG_FLAGS in your case something like:
list(APPEND CMAKE_SWIG_FLAGS
"-I/home/steve/cmake_games/src/foo/bar"
"-I/home/steve/cmake_games/src/this/that")
e.g.: https://github.com/google/or-tools/blob/master/cmake/python.cmake#L57
ps: if you have compile definitions you should also pass them to SWIG by hand since you can't say to swig macro to get all the properties of an associated target.

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

How to pass variable to cpack?

I have a cmake project which one of the install targets is a collection of files. This files change depending on the configuration (Release, Debug...).
I would like to be able to install the files like so:
install(DIRECTORY $<TARGET_FILE_DIR:tgt>
DESTINATION bin
COMPONENT files)
But cmake does not support that. Generator variables do not apply to DIRECTORY. So I was wondering if there is a way to either save the directory somewhere. Either the cache or a file and then load it into cpack.
So I guess the question is how to pass a variable from cmake to cpack?
This is a rather late answer, but I happened upon this question trying to solve a somewhat different problem that could also be summarized as: "How do I pass a variable to CPack?" In my case, I was making this call from a customized version of CPackDeb.cmake copied to my workspace:
find_program(OPKG_CMD NAMES opkg-build HINTS "${OPKG_HINT}")
# ^^^^^^^^^^^^
# This is what I wanted to pass to CPack
I was setting OPKG_HINT in a file included from my top-level CMakeLists.txt, but it was not getting passed through to cpack; the above find_program() invocation was seeing an empty string for OPKG_HINT.
The solution turned out to be stupid simple: just prepend CPACK_ to the variable name!
If I do this in CMakeLists.txt:
set(CPACK_OPKG_HINT "${_sysroot_top}/aarch64-poky-linux/usr/bin")
then I can put this in my CPackDeb.cmake file and it works fine:
find_program(OPKG_CMD NAMES opkg-build HINTS "${CPACK_OPKG_HINT}")
Anyway, this wound up being a bit of an X-Y problem for the OP, but... if you really need to set a variable at CMake time in such a way that it's accessible to cpack, prefixing the variable name with CPACK_ seems to do the trick nicely...
The following setup work if you use a "single-configuration generators (such as make and Ninja)" and call CMake with
cmake -DCMAKE_BUILD_TYPE=Release <source_dir>
https://cmake.org/cmake/help/v3.0/variable/CMAKE_BUILD_TYPE.html
You can define the ${dir} variable in another way if you like.
IF (CMAKE_BUILD_TYPE STREQUAL "Release")
SET(dir release_dir)
ELSE()
SET(dir debug_dir)
ENDIF()
INSTALL(DIRECTORY ${dir} DESTINATION bin COMPONENT files)
Until now this seems to be the best answer (from someone on the cmake mail list)
install(DIRECTORY path/to/Debug/dir
DESTINATION bin
CONFIGURATIONS Debug
COMPONENT files
)
install(DIRECTORY path/to/Release/dir
DESTINATION bin
CONFIGURATIONS Release
COMPONENT files
)
CMake 3.5 supports generator expressions for the DIRECTORY arguments. See installing directories.