Using Cmake to add compiler options to existing cmake - 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?

Related

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.

Compiling Cmake subproject without compiling other subprojects

I have a large project which I have divided into "modules" or "subprojects". The directory structure looks like this (simplified, but the basic idea):
project-root/
CMakeLists.txt <-- Contains some variable definitions
module1/
CMakeLists.txt <-- Receives variable definitions from top-level
src/
module2/
CMakeLists.txt <-- Receives variable definitions from top-level
src/
The root CMakeLists.txt does not have any libraries or executables defined. It uses add_subdirectory(module1) and add_subdirectory(module2) to refer to the lower-level modules, which do have libraries and executables.
Now, all this works fine. I'm using CLion and it is able to build the entire project. My problem is when a developer wants to compile only one of the modules, without having to deal with the other modules.
I have created variables in the root-level CMakeLists.txt which are used by the CMakeLists.txt files of the modules. But if I try to compile only one of the modules, of course the variable is undefined (or blank). Is there a standard way of dealing with problems like this? I want to be able to compile each module independently, but I don't want to redefine the variables in every CMakeLists.txt file.
I believe it is exactly what people do when they add a "BUILD_TESTS" option.
Look at grpc for instance.
If you follow the option here, you'll see that if the option is defined, then they add a library (they do a whole bunch of other stuff, too).
So to summarize:
Create an option: option(BUILD_MODULE_1 "Build module 1" ON)
Call add_subdirectory(module1) only if(BUILD_MODULE_1)
In CLion, you can either edit the cache and reload CMake or add the option (e.g. -DBUILD_MODULE_1=OFF) to the "CMake options" in the settings.

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"

Including CMAKE_CURRENT_BINARY_DIR doesn't work for subdirectory

The project has roughly the following structure:
include/
lib_name/
public_foo.h
public_bar.h
src/
CMakeLists.txt
foo.c
foo.h
bar.c
bar.h
CMakeLists.txt
The top-level CMakeLists.txt file defines a shared library target
add_library(lib_name SHARED "src/foo.c" "src/bar.c")
At the same time, there's a custom command
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/src/foo-impl.c" ...)
that generated foo-impl.c in the build directory that is included by `foo.c' like this:
#include <some_header.h>
#include <other.h>
#include "foo-impl.c"
In order to be able to include foo-impl.cI do the following in src/CMakeLists.txt
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
Looks like it should work, right?
Well, it doesn't work - the src directory is not added to compiler flags for foo.c. It seems like include directories affect only targets defined in the same directory (or subdirectories).
I know I can just do
include_directories("${CMAKE_CURRENT_BINARY_DIR}/src")
from the root CMakeLists.txt (in fact, this is how I'm doing it at the moment) but that's not acceptable: every subdirectory will see the parent's files and this can eventually lead to file name collisions.
How do people overcome this in large projects? or maybe I'm doing something wrong?
Like most cmake commands, include_directories only affects targets in that directory and subdirectories. And IIRC only targets defined after the command. That's intentional. It's how you define different flags for different modules.
And when I say like most cmake commands, I really mean most. Most importantly including add_definitions and set except with CACHE argument.
Also remember, that include_directories apples to targets, not files.
Either define the library in the subdirectory (makes most sense to me), put everything in the top-level CMakeLists.txt or set the flags on the target only using set_target_properties(lib_name PROPERTIES COMPILE_FLAGS "-I${CMAKE_BINARY_DIR}/src") (must be done after the target is defined).

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.