globbing files autogenerated by a sub_directory binary - cmake

I have the following problem, I have a separate cmake project that generates c++ files and I want to glob them into a library that uses them. Right now this is done in this manner
add_subdirectory(generator)
add_custom_target(run-generator ... byproduct GENERATED_FILES)
include(files.txt)
target_link_libraries(library ${GENERATED_FILES})
The include files.txt is actually a set(GENERATED_FILES all_autogen_files), right now they're fixed but in the future they might change. That is what I would want to have is something like
add_subdirectory(generator)
execute_process(generator_binary ... commands)
file(glob ${GENERATED_FILES} output_location_of_gen_files)
target_link_libraries(library ${GENERATED_FILES})
As I understood execute_process runs on the spot is read, so this would generate all the files before the file(glob) but I don't know how would I go about actually building the generator binary before the execute process, since right now what builds it before is that it is a dependency on the target_link_libraries

The only way this could possibly work is if you create a superbuild, i.e. a CMake project that builds everything with ExternalProjects. The reason is that you can't create new targets or add sources to existing targets during the build.
With a superbuild you need at least 3 separate CMake projects: One that builds the generator and then generates the files, one that globs the generated files and builds the rest of your build artifacts, and the superbuild project that adds both with ExternalProject_Add. By setting the dependencies correctly you can then ensure that the project that uses the generated files is configured after the generating project has been built.
However, globbing in CMake is discouraged anyway, so listing the files explicitly is the proper way to do it. If your code-generator starts generating new files then they should be added to the list manually in the same commit, as otherwise even with CONFIGURE_DEPENDS it is not guaranteed that the new files will be built when using globbing and the ExternalProject approach.

Related

Generating compile_commands.json without generating build files

I'd like to generate a compile_commands.json file for use with the clangd language server. However, EXPORT_COMPILE_COMMANDS only works for the make and ninja build systems. When building a project that uses a different build system it would be convenient to also be able to generate compile_commands.json files as if I was using make or ninja without actually generating any build files that interfere with the build system that I'm using to perform the build.
What is the most convenient way to do this with cmake?
I think your only option here is to have a different build folder with Ninja or Makefile to generates the compile_commands.json and have a different build folder for your "actual" build.
The thing is, CMake is a generator, and it doesn't support mixed builds; and in fact, it should not. If they do that, you will end up having random artifacts from different build systems inside the build folder that might eventually conflicts with each others.
That being said, you are aware that what you get in Ninja-based compile_commands.json is not going to be fully relevant to your "actual" build system that you want to use. I can see it being useful, but not the same for sure.

Is it possible to force CMake to run add_compile_definitions() each time?

I have an embedded project (using ESP-IDF which builds projects with CMake), where I have a props.json file that contains several settings (e.g. "device type"). For example based on the actual value of "deviceType" the CMake open and read props.json by calling execute_process() and jq, then defines C preprocessor macros, such as: DEVICE_TYPE_A by using add_compile_definitions().
The problem is that, this will run only when I modify the CMakeLists.txt or clean the whole project, but I don't want to recompile each components when I change the props.json only the files that I wrote (so, depend on the settings). I'd like to make CMake read the file each time I build the project without cleaning it.
I did my research, so I know there are add_custom_target() and add_custom_command() that behave that way, however add_compile_definitions() cannot be called in a script. Is there a solution to achieve this or should I just use a header file configured by configure_file() and leave add_compile_definitions() alone?
This is actually pretty easy and you don't need to manually reconfigure CMake. Just add the following to the CMakeLists.txt in the directory containing your props.json file:
set_property(DIRECTORY . APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS props.json)
This will add props.json to the list of files that the CMake-generated build scans when determining whether to re-run the CMake configure step. See the docs on CMAKE_CONFIGURE_DEPENDS for more detail.
In general, you should never need to manually re-run CMake1 after the first configure. If you do, it is an indication that you have not communicated all of the necessary information for CMake to generate a correct build system.
1 There is one notable exception: Xcode is known to be buggy when re-running the CMake configure step automatically.

Compile gtest from source with catkin

I am trying to compile gtest from source (instead of using the existing installed version). I am working on a catkin based cmake project.
I have added the sourcecode from https://github.com/google/googletest to my workspace and included the folder with add_subdirectory.
However, I get a nameclash with the existing gtest:
CMake Error at src/test_env/GTest/googletest/cmake/internal_utils.cmake:151 (add_library):
add_library cannot create target "gtest" because another target with the
same name already exists. The existing target is a shared library created
in source directory "/usr/src/gtest". See documentation for policy CMP0002
for more details.
From other posts, and the googletest instructions itself (https://github.com/google/googletest/tree/master/googletest#incorporating-into-an-existing-cmake-project) I understand that this should be no problem.
I think the problem might lie in how catkin handles gtest. And, admittedly, normally I could just use the installed version. But I want to make sure, that everyone uses the same (bundled) version of gtest.
Any suggestions and hints are welcomed.
Okay, so the error message is actually quite clear. A cmake "target" is "something that will be produced by the build", be it a library, or an executable, or something else. So, the problem is that you are trying to add a target named "gtest", and catkin already does the same thing. Both would produce the library "libgtest.so", and of course there can only be one of those in the same folder. You could rename "your" gtest by changing the target name in googletest/CMakelists.txt, but I would strongly advise you to not do that.
In my opinion, gtest shouldn't even be a shared library at all, especially if you are using different build flags for different projects in your repository. There is an alternative, and that is basically only including the gtest source code in a folder, and then including the header files and source files in your unittests main.cpp. googletest already comes with helpers for that, that is src/gtest-main.cc.
This is how I would structure it:
Add the gtest version you want as submodule to git (in case you use git). This way, you have a specified version for all projects in your repo, and can update it in a different branch. I will call that folder "GTEST_DIR".
Write your unittests in .cpp files, that #include <gtest/gtest.h>, one per hpp you want to test, and #include both the hpp and the cpp in your test.cpp. This enforces the separation of your tests from other classes and makes it very easy to switch out dependent classes with mocks or fake objects. You will not need a main() function, as that one is already in gtest-main.cc.
Write a cmake macro like this:
macro(add_gtest NAME FILES)
add_executable(my_gtest_$NAME
$FILES
GTEST_DIR/src/gtest.cc
GTEST_DIR/src/gtest-death-test.cc
GTEST_DIR/src/gtest-filepath.cc
GTEST_DIR/src/gtest-port.cc
GTEST_DIR/src/gtest-printers.cc
GTEST_DIR/src/gtest-test-part.cc
GTEST_DIR/src/gtest-typed-test.cc
GTEST_DIR/src/gtest-main.cc
)
target_include_directories(my_gtest_$NAME GTEST_DIR/include)
endmacro()
Of course, you can make this more complicated or less complicated, but that is the gist. Of course, compile times will be longer this way over using gtest as a shared library, but it actually makes sure your units get tested in isolation, which is very valueable in my opinion. Also, you can use ccache to greatly improve compile times in this scenario, because the gtest object files never change. Also, this will make sure gtest is compiled with exactly the flags you want it to. You could for example create 2 separate unit tests for the same class, one with exceptions enabled and one without.

CMake parallel build for Fortran-90+ with modules

I have a problem building multiple Fortran-90+ executable targets using CMake, when I use Fortran modules that are shared by all executables, and build in parallel using make -j. The issue seems to be that the compiled object files are placed in different subdirectories for each target, CMakeFiles/targetName.dir/src/file.f90.o, while the module files are placed in the same directory for every target (I can change this directory by setting Fortran_MODULE_DIRECTORY, but it will still be the same dir for all module files). The problem is then that all targets start writing these module files in parallel, and I get a
Fatal Error: Can't rename module file 'module.mod0' to 'module.mod': No such file or directory
when using gfortran (which apparently creates a .mod0 file and then renames it to .mod). The problem does not arise when I issue make without the -j option (serial build).
I can see two solutions, but I don't know how to implement them. Firstly, put the object files for all targets in the same directory rather than target-specific directories. This may be the preferred option, since I won't have to compile the shared source files N times for N targets. The make process will then recognise that the object files exist, and not compile the corresponding source files again, hence not touching the .mod(0) files again (I may need to make all following targets depend on the first).
The second solution would be to put the .mod(0) files in the target-specific directories, so that they are not overwritten or removed by the other targets. This would solve my problem, even though it would still involve more compiling than necessary. I have no idea how to accomplish either option, so any hint there or alternative solution is welcome.
The answer I was looking for was provided in the comments to the question by #RaulLaasner:
I would create an additional target in the form of a core library of the relevant source files, which can then be linked to all executables. This should work in parallel. The mod files can still be in put into a single directory.
I used add_library() and target_link_libraries() to achieve this.
Note that e.g. Gentoo ebuild scripts add --as-needed to the linker, which may cause undefined references in your core library when you link it and external libraries to form the executable. To prevent this, make sure you link the external libraries to your core library first. To this end, my CMakeListst.txt contains:
add_library( "Core" STATIC src/functions.f90 src/routines.f90 ) # creates libCore.a
target_link_libraries( Core ${EXTERNAL_LIBRARIES} ) # link external libraries to libCore.a
...
add_executable( myProgram1 src/myProgram1.f90 ) # creates the first executable
target_link_libraries( myProgram1 Core ) # links libCore.a to myProgram1
The last two lines can be repeated to build the other executables (e.g. using foreach()).

When should I rerun cmake?

After running the cmake command once to generate a build system, when, if ever, should I rerun the cmake command?
The generated build systems can detect changes in the associated CMakeLists.txt files and behave accordingly. You can see the logic for doing so in generated Makefiles. The exact rules for when this will happen successfully are mysterious to me.
When should I rerun cmake? Does the answer depend on the generator used?
This blog post (under heading: "Invoking CMake multiple times") points out the confusion over this issue and states that the answer is actually 'never', regardless of generator, but I find that surprising. Is it true?
The answer is simple:
The cmake binary of course needs to re-run each time you make changes to any build setting, but you wont need to do it by design; hence "never" is correct regarding commands you have to issue.
The build targets created by cmake automatically include checks for each file subsequently [=starting from the main CMakeLists.txt file] involved or included generating the current set of Makefiles/VS projects/whatever. When invoking make (assuming unix here) this automatically triggers a previous execution of cmake if necessary; so your generated projects include logic to invoke cmake itself! As all command-line parameters initially passed (e.g. cmake -DCMAKE_BUILD_TYPE=RELEASE .. will be stored in the CMakeCache.txt, you dont need to re-specify any of those on subsequent invocations, which is why the projects also can just run cmake and know it still does what you intended.
Some more detail:
CMake generates book-keeping files containing all files that were involved in Makefile/Project generation, see e.g. these sample contents of my <binary-dir>/CMakeFiles/Makefile.cmake file using MSYS makefiles:
# The top level Makefile was generated from the following files:
set(CMAKE_MAKEFILE_DEPENDS
"CMakeCache.txt"
"C:/Program Files (x86)/CMake/share/cmake-3.1/Modules/CMakeCCompiler.cmake.in"
"C:/Program Files (x86)/CMake/share/cmake-3.1/Modules/RepositoryInfo.txt.in"
"<my external project bin dir>/release/ep_tmp/IRON-cfgcmd.txt.in"
"../CMakeFindModuleWrappers/FindBLAS.cmake"
"../CMakeFindModuleWrappers/FindLAPACK.cmake"
"../CMakeLists.txt"
"../CMakeScripts/CreateLocalConfig.cmake"
"../Config/Variables.cmake"
"../Dependencies.cmake"
"CMakeFiles/3.1.0/CMakeCCompiler.cmake"
"CMakeFiles/3.1.0/CMakeRCCompiler.cmake")
Any modification to any of these files will trigger another cmake run whenever you choose to start a build of a target. I honestly dont know how fine-grained those dependencies tracking goes in CMake, i.e. if a target will just be build if any changes somewhere else wont affect the target's compilation. I wouldn't expect it as this can get messy quite quickly, and repeated CMake runs (correctly using the Cache capabilities) are very fast anyways.
The only case where you need to re-run cmake is when you change the compiler after you started a project(MyProject); but even this case is handled by newer CMake versions automatically now (with some yelling :-)).
additional comment responding to comments:
There are cases where you will need to manually re-run cmake, and that is whenever you write your configure scripts so badly that cmake cannot possibly detect files/dependencies you're creating. A typical scenario would be that your first cmake run creates files using e.g. execute_process and you would then include them using file(GLOB ..). This is BAD style and the CMake Docs for file explicitly say
Note: 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.
Btw this comment also sheds light on the above explained self-invocation by the generated build system :-)
The "proper" way to treat this kind of situations where you create source files during configure time is to use add_custom_command(OUTPUT ...), so that CMake is "aware" of a file being generated and tracks changes correctly. If for some reason you can't/won't use add_custom_command, you can still let CMake know of your file generation using the source file property GENERATED. Any source file with this flag set can be hard-coded into target source files and CMake wont complain about missing files at configure time (and expects this file to be generated some time during the (first!) cmake run.
Looking into this topic for reading the version information from a debian/changelog file (generation phase), I ran in the topic that cmake execution should be triggered as debian/changelog is modified. So I had the need to add debian/changelog to CMAKE_MAKEFILE_DEPENDS.
In my case, debian/changelog is read through execute_process. Execute_process unfortunately gives no possibility to add files processed to CMAKE_MAKEFILE_DEPENDS. But I found that running configure_file will do it. Actually I am really missing something like DEPENDENCIES in execute_process.
However, as I had the need to configure the debian/changelog file for my needs, the solution came implicitly to me.
I actually also found a hint about this in the official documentation of configure_file:
"If the input file is modified the build system will re-run CMake to re-configure the file and generate the build system again."
So using configure_file should be a safe to trigger the re-run of cmake.
From a user perspective, I would expect other commands to extend CMAKE_MAKEFILE_DEPENDS, too. E.g. execute_process (on demand) but also file(READ) (implicitly like configure_file). Perhaps there are others. Each read file is likely to influence the generation phase. As an alternative it would be nice to have a command to just extend the dependency list (hint for the cmake developers, perhaps one comes along).