CMake: How to add custom generic compilation rule? - cmake

I need to compile protocol buffer .proto files to .pb.cc,.pb.h files. There is a program for this conversion.
protoc test.proto --cpp_out .
How can I add such a generic rule in cmake? I can do this with add_custom_command. But I have to this for every .proto file. Is there a better way to do this?

It looks like CMake's FindProtobuf module provides this functionality via the function PROTOBUF_GENERATE_CPP.
You can pass multiple .proto files in the one call, e.g.
file(GLOB ProtoFiles "${CMAKE_CURRENT_SOURCE_DIR}/*.proto")
PROTOBUF_GENERATE_CPP(ProtoSources ProtoHeaders ${ProtoFiles})
Note that even though the CMakeLists.txt file which calls find_package(Protobuf) could be the top-level one, the CMakeLists.txt file(s) which invoke the function would need to be in the same directory as the .proto files.

Related

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

What is the purpose of a .cmake file?

I might be googling wrongly, but I'm unable to find what's the purpose of .cmake files.
I've just stumbled across the CMake tool for a project I've to work with and I'm having a hard time to understand how it works. I do understand that running the CMake command in a directory containing a CMakeLists.txt executes the commands in that file (along with the commands in the CMakeLists.txt contained in the sub-directories) but the purpose of .cmake files is a lot more fuzzy.
It seems that they are used to define functions/set variables that are thereafter used in the CMakeLists.txt but I'm not sure. How are they used by the CMake tool ?
You can include it with the include command. Similar to how other languages allow you to split source files into modules or header files, they can contain function definitions, macros which can then be reused across multiple CmakeLists.
See https://cmake.org/cmake/help/v3.0/command/include.html
Note that you can actually include any file, but a .cmake extension is commonly used.
Within this file you can define functions of macros which can be used in your CMakeLists.txt file. But there are some more other applications for .cmake files. For example if you want to provide a library or tool they should at least contain a <name>Config.cmake so that your library can be used with the find_package() command. Further information can be found in CMake Wiki
CMake input files are written in the "CMake Language" in source files named CMakeLists.txt or ending in a .cmake file name extension.
https://cmake.org/cmake/help/latest/manual/cmake-language.7.html
This means they're one and the same thing. "CMakeLists.txt" is simply the default name. If you decide to split the code (CMake is technically just another interpreted language) into separate files/modules (that you can include into each other), they obviously can't all have the same name, so you would use the ".cmake" extension. Theoretically, you could call "CmakeLists.txt" as "CmakeLists.cmake", but then cmake won't be able to find it by default, so you'd have to pass the filename as a command-line argument. So it is just a difference of naming conventions. Everything you can put in one file, you can put in the other, and vice-versa.

cmake use one cmakelist.txt for a project with subdirectories

i like to structure my code in multiple subdirs but i dont want to create a new cmakelist.txt in each new subdir.
my folder structure is something like this:
project
>cmakelist.txt
>build
>src
>main.cpp
>multiple_subdirs_or_(c|h)pp_files_with_more_subdirs_or_(c|h)pp_files
my cmakelist.txt looks like this:
...
file(GLOB_RECURSE cpps RELATIVE ${CMAKE_CURRENT_LIST_DIR} "src/*.cpp")
file(GLOB_RECURSE hpps RELATIVE ${CMAKE_CURRENT_LIST_DIR} "src/*.hpp")
#remove files with main
list(REMOVE_ITEM cpps "src/test.cpp")
#bins
add_executable(test src/test.cpp src/test.cpp ${hpps} ${cpps})
#same problem if this is used instead of the other add_executable
add_library(foo OBJECT ${cpps} ${hpps})
add_executable(test src/test.cpp $<TARGET_OBJECTS:foo>)
the problem with my file:
source files created after the execution of cmake are not compiled and the build fails if they are used.
as predicted by http://www.cmake.org/cmake/help/v3.0/command/file.html in section GLOB:
We do not recommend using GLOB to collect a list of source files from
your source tree. If no CMakeLists.txt file changes when a source is
added or removed then the generated build system cannot know when to
ask CMake to regenerate.
the question: is it possible to use a single cmakelist.txt for a project with multiple sub directories? (without the problems of file(GLOB ...) )
You have two totally unrelated things here.
First, can you use only a single CMakeLists.txt file for your whole project? Yes, of course you can (although I'd personally not go this way after a project has reached a certain size), and you're already doing this.
Second, the problem with GLOB. You already quoted the part of the documentation where it states what problems the use of GLOB has. This cannot really be avoided at the moment if you want to continue using GLOB, as this is part of the cmake design where they distinguish between what is done during configure and build time. The alternative is to list all files manually. Whether you do this in a single CMakeLists.txt file in your projects main directory, or in multiple files across your subdirectories does not matter.
To answer your question: yes, it is possible to handle a project with multiple sub-directories and one CMakeLists.txt. I have two considerations for you to take into account:
I strongly recommend you not using file(GLOB ...) for sources.
You have to list the files manually. For example (src/ is the source-subdirectory):
set(cpps src/file1.cpp src/file2.cpp src/file3.cpp)

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"

How do I merge subdirectories into a single executable?

I understand that every subdirectory has to have a CMakeLists.txt, but I don't want to create a library or an executable for each directory.
My folder structure:
/src
/exportedHeaders
/server
main.cpp
Each subfolder has a mix of .h and .cpp files. I don't understand how I can mix all of it up into a single executable, there doesn't seem to be a command for that.
I haven't really used CMake, so there is probably a better way, but as far as I know you can just specify the path to all the source files.
eg:
add_executable (myProgram main.cpp subdir1/foo.cpp subdir2/bla.cpp)
I imagine you could use file globbing to get *.cpp in each subdir, to reduce typing.
See this mailing list message for info about file globbing in CMake.