CMake: header-only library with generated files - cmake

I have a library that needs to carry some constant data injected from the content of non-source files (in this case, OpenGL shader code). To achieve this, I'm using add_custom_command() to generate include files that I can then #include into my code to initialize const static variables.
This works perfectly with regular libraries (static or shared), but now I'd like to make my library header-only. The ability of C++ to let static methods return static data without running the risk of having that data duplicated in each translation unit ("magic statics") makes this possible.
The problem however is that CMake seems to assume that an INTERFACE library (which is the CMake feature that I'm using to create header-only libraries) does not need building - which, in this case, is wrong.
(I realize that there is no actual obligation for my library to be header-only. In this particular case, the reason I want this is that I would like the library, which is doing OpenGL, to remain independent of any specific binding library [such as GLEW or GLee or the newcomer glbinding]. By keeping my library header-only, I can leave that choice to the user - all he needs to do is #include the header of the binding library before mine.)
Does anyone see a way to have CMake trigger the header-generating custom commands, at the latest when the consumer project is being built?
EDIT: I just realized that I could have the "best of both worlds" as it were by keeping my library static but still keeping all my code except for the constant data in the header files. That way, there would still be no need to choose a specific OpenGL binding library.
However, there are still advantages to having a library be header-only - simplicity of use for one - so I'm leaving my question open.
EDIT #2: Here is the relevant part of my CMakeLists.txt file (I only stripped the library dependencies - all header-only - from the end):
set(SHADER_FILES "src/vertex.glsl" "src/fragment.glsl")
add_library(libGPCGUIGLRenderer INTERFACE)
target_sources(libGPCGUIGLRenderer INTERFACE ${SHADER_FILES})
target_include_directories(libGPCGUIGLRenderer BEFORE
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
# Embed shader files
source_group("Shader files" FILES ${SHADER_FILES})
set(GENERATED "${CMAKE_CURRENT_BINARY_DIR}/generated")
target_include_directories(libGPCGUIGLRenderer INTERFACE ${GENERATED})
# Find the GPC Bin2C utility
find_package(GPCBin2C REQUIRED)
# Add a custom target and a dependency for each shader file
foreach(shader ${SHADER_FILES})
get_filename_component(name "${shader}" NAME)
set(shader_header "${GENERATED}/${name}.h")
add_custom_command(
OUTPUT ${shader_header}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${shader}
COMMAND GPCBin2C --input=${CMAKE_CURRENT_SOURCE_DIR}/${shader} --output=${shader_header}
)
target_sources(libGPCGUIGLRenderer INTERFACE ${shader_header})
endforeach()

Creating a static library with headers as the only sources worked for me. It is, of course, only a work-around.
Creating a static library with only header files results in an empty library. Mine says !<arch> as the only content.
CMake will automatically get the dependencies correct across sub-directories.
Since all sources are headers, you need to tell CMake which linker language should be used.
Code:
set(OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated_include")
add_custom_command(
OUTPUT "${OUTDIR}/outfile.h"
# Replace the next two lines with a proper generating script.
COMMAND mkdir -p ${OUTDIR}
COMMAND touch ${OUTDIR}/outfile.h
)
# Note, I am only adding header files to the library.
add_library(generated-headers STATIC
"${OUTDIR}/outfile.h"
)
set_target_properties(generated-headers
PROPERTIES LINKER_LANGUAGE CXX)
target_include_directories(generated-headers PUBLIC ${OUTDIR})
Use in other directories like this:
# In any other directory of the same CMake project:
add_executable(main main.cpp)
target_link_libraries(main generated-headers)
Tested on CMake 3.2, 3.8 and 3.9. Using Ninja and Make generators.

You can use target_sources in CMake 3.1 to tell consumers to compile interface files:
add_library(source_only INTERFACE)
target_sources(source_only INTERFACE foo.cpp)
http://www.cmake.org/cmake/help/v3.1/command/target_sources.html

I ran into comparable problems when trying to use glad: https://github.com/Dav1dde/glad
It uses a custom CMake command to build a binding, which means the files you need to include in the project which uses glad do not exist, so that CMake does not build glad (which would create those files)...
I did not get to try it yet, but example 3 of the following link seems to be a good solution and I believe it may work in your case:
https://samthursfield.wordpress.com/2015/11/21/cmake-dependencies-between-targets-and-files-and-custom-commands/

Related

CMake: static library ignored (objects compiled twice) [duplicate]

I'm trying to move my project to CMake, and at the same time have some optimization on the compilation process.
Here's the deal:
I have several subdirs that are (have to be) each compiled into a static library (this works).
I want to gather all the object files from each subdir into another bigger, complete, static library.
It looks like this:
.
libBig.a # made from object from subdir1 and subdir2
subdir1/
src/
libSubdir1.a
subdir2/
src/
libSubdir2.a
Today, I managed to use a global variable in which every subdir CMakeLists.txt will append its own source files. I use this variable as a "source" input in my big library:
# the big library depends on all the source files
# ${all_src} is automatically filled with each subdir's cpp file
get_property( BigLib_src GLOBAL PROPERTY all_src)
add_library( Big STATIC ${BigLib_src}) # recompiles all the sources
Now, this works, not too bad, but the thing is, all my source files get compiled twice: once for the subdir library, and once for the big library.
CMake seems to forget that it has already built them.
I have to keep the subdir libraries and ar can't merge two static libraries.
Do you know how to do that?
You can use the new OBJECT library feature introduced in CMake 2.8.8. The idea is explained here. Basically, the OBJECT library is a similar concept to the convenience library known from Autotools to group object files.
Check the complete CMake OBJECT library tutorial.
As of CMake 2.8.8, you can do this using the OBJECT library type. See mloksot's answer. The old situation was that each target had its own directory and CMake would build every dependent object for every target. This guards against a case where one source file could be used multiple times with different CFLAGS. (Note that by default CMake is more conservative than automake here: automake will stop reusing object files for different targets only if the targets are built with different CFLAGS (and probably CPPFLAGS, too).

How would I include asio library using CMake?

I am trying to work on a project for a class and I want to use CMake to build the project. My current project looks like
|-bin
|-CMakeLists.txt
|-include
|-asio-1.12.2
|-chat_message.hpp
|-chat_message.cpp
|-CMakeLists.txt
|-src
|-Server.cpp
although my Server.cpp needs asio.hpp that is in /include/asio-1.12.2/include.
The professor has a makefile that compiles it with the flags
-DASIO_STANDALONE -Wall -O0 -g -std=c++11 -I./include -I./include/asio-1.12.2/include. My CMakeLists files look like this:
./CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 3.12)
PROJECT(Server VERSION 0.0.1)
SET(CPP_STANDARD 11)
SET(CPP_STANDARD_REQUIRED True)
ADD_SUBDIRECTORY(include)
ADD_EXECUTABLE(Server src/Server.cpp)
TARGET_LINK_LIBRARIES(
Server PRIVATE
chat_message
asio
)
./include/CMakeLists.txt
ADD_LIBRARY(
chat_message
chat_message.cpp
chat_message.hpp
)
ADD_LIBRARY(
asio
asio-1.12.2/include/asio.cpp
asio-1.12.2/include/asio.hpp
)
TARGET_INCLUDE_DIRECTORIES(
chat_message PUBLIC "${CMAKE_SOURCE_DIR}/include"
asio PUBLIC "${CMAKE_SOURCE_DIR}/include/asio-1.12.2/include"
)
How would I link the asio header file to the Server.cpp file WITH the flags needed?
First of all, as Tzyvarev pointed out in the comments, you must split the target_include_directories() command into two separate commands. This will then propagate asio and chat_message's include directories to your Server target, which will turn add the correct include flags to the compiler flags.
Note: I'd recommend switching from CMAKE_SOURCE_DIR to CMAKE_CURRENT_SOURCE_DIR and altering your paths accordingly to make your life slightly easier if in future you decide to change your project structure, as you will usually keep a CMakeLists.txt file in the same directory as the sources for a target it creates.
The -DASIO_STANDALONE option can be added with a target_compile_definitions() call:
target_compile_definitions(asio PUBLIC ASIO_STANDALONE)
Note you do not need the -D - CMake will generate the correct compiler flag for you. Also, since this is a requirement for the asio target and all its consumers will need it, it should be added to that, rather than its consumers - it will then propagate to dependencies as needed.
In your CMakeLists.txt you have set the CPP_STANDARD and CPP_STANDARD_REQUIRED variables. The one's you're after are CMAKE_CXX_STANDARD and CMAKE_CXX_STANDARD_REQUIRED respectively.
This will set the flag for all targets throughout your project.
There are different ways to add the error, optimization and debug symbols flags and which one you use depends on your use case. The following is not an exhaustive list.
If you want everyone who builds the library to have these, irrespectively of build configuration (debug/release/etc), you set the CMAKE_CXX_FLAGS variable in your CMakeLists.txt
If you want everyone to have the flags, but only in certain build types, set the CMAKE_CXX_FLAGS_<CONFIG> variable, where <CONFIG> is the build type selected (DEBUG/RELEASE/MINSIZEREL/RELWITHDEBINFO are available by default)
If you don't want to force the flags upon everyone, before invoking CMake you can set the CXXFLAGS environment variable. But note that according to documentation this will be ineffective if CMAKE_CXX_FLAGS is set in your CMake scripts.
If you want to add flags to a single target, you can call target_compile_options on it, and set the appropriate visibility option to enable/disable propagation to consumers.
However in general you do need to think about portability when using these. For example GCC may support a certain flag which in Clang could be different.
Edit to address this comment
Since the header-only ASIO library does not like being compiled with the compiler definition mentioned above, there are two ways to address it:
Remove the ASIO_STANDALONE compiler flag
This will be the easiest thing to do from your point of view, but as a knock-on effect it will require you to have Boost installed on your system, as not having the flag above will cause the pre-processor to go through some Boost includes. There may be other effects, but this is the first one I encountered before moving on to the solution below.
Keep the flag, and use a CMake interface library
add_library() can allow you to add a target that does not actually produce any compiled objects/libraries/executables, but simply a logical CMake target that can posses properties just like any other ones - include directories, link libraries, etc. So as a minimum you could do this:
add_library(asio INTERFACE)
target_compile_options(asio INTERFACE ASIO_STANDALONE)
target_include_directories(asio INTERFACE <dir where asio.hpp lives>)
target_link_libraries(asio INTERFACE <threads>) # Using ASIO requires you link your final executable/library with your system's threading library (e.g. pthread on linux)
Then when you link another target with it like
target_link_libraries(any_lib PRIVATE asio)
any_lib will inherit all properties required to build with ASIO.
The solution you choose will be dictated by your use case, but if you have to do it the same way as your professor, then go the INTERFACE library route.

Create dynamic library that implements multiple LLVM passes

I have a static library, which consists of several passes and is a part of opt. I want to make it dynamic and create one dylib file, so I could call opt, loading created .dylib and passing the desired LLVM pass. I'm using LLVM 7.0.
I created such dylib file with the help of Xcode (by adding to already existing dynamic library target additional passes), but I can't find any information, how to make it with the help of CMake.
At the moment, CMake file of the library looks like this:
add_llvm_library(MyLibrary
Pass1.cpp
Pass2.cpp
Pass3.cpp
...
PassN.cpp
DEPENDS
intrinsics_gen
)
What is expected is performing such command:
path/to/opt -load /path/to/MyLibrary.dylib -Pass3 ...
It isn't clear to me, whether it is possible to leave the structure of the library as is or if I have to modify it, by giving each pass a CMakeLists.txt file.
Tried this, faced up to the problem of multiple dependencies, which required to include several libraries. Linked libraries required required another libraries being linked. After a while, took a Hello CMakeLists.txt, which was provided in LLVM, as an example. Instead of add_llvm_library, wrote add_llvm_loadablemodule, making such CMakeLists.txt
if (WIN32 OR CYGWIN)
set(LLVM_LINK_COMPONENTS ...)
endif()
add_llvm_loadable_module(Mylib_Dylib
Pass1.cpp
Pass2.cpp
...
PassN.cpp
PLUGIN_TOOL
opt
)
It build Mylib.dylib, and it was possible to call a pass from it.
Since LLVM 8.0 the add_llvm_loadable_module CMake directive has been removed from LLVM. It has been replaced with using add_llvm_library with the MODULE argument.
if (WIN32 OR CYGWIN)
set(LLVM_LINK_COMPONENTS ...)
endif()
add_llvm_library(Mylib_Dylib MODULE
Pass1.cpp
Pass2.cpp
...
PassN.cpp
PLUGIN_TOOL
opt
)

Directing cmake to link against shared object with debug postfix (_d)

I've got a cmake project that pretty much looks like this:
cmake_minimum_required(VERSION 3.0)
SET(CMAKE_DEBUG_POSTFIX "_d")
include_directories(../TransfunctionerProject)
include_directories(../TransmogrifierProject)
set(Libraries
ContinuumTransfunctioner
Transmogrifier
)
set(SourceFiles
Wrapper.cpp
Logger.cpp
)
add_library(Frobnigator SHARED ${SourceFiles})
add_library(FrobnigatorStatic STATIC ${SourceFiles})
set_target_properties(FrobnigatorStatic PROPERTIES OUTPUT_NAME Frobnigator)
target_link_libraries(Frobnigator ${Libraries})
Where ContinuumTransfunctioner and Transmogrifier projects include the debug postfix directive SET(CMAKE_DEBUG_POSTFIX "_d") so that libContinuumTransfunctioner_d.so and libTransmogrifier_d.so both exist.
The problem is that the current project appears to be linking against the static library without the _d suffix and complains:
/usr/bin/ld: cannot find -lContinuumTransfunctioner
The Libraries that you pass into the target_link_libraries call are interpreted as filenames, not as target names.
This is the unfortunate fallback for that call in CMake: If you pass a random string to it, that cannot be interpreted in a meaningful way, CMake will always assume it to be plain library name. Sometimes this is just what you want, but the name has to be an exact match for an existing library. The whole debug postfix magic will be lost here.
What you might have wanted to do was to pass a library target name instead. This will trigger a much smarter handling of the dependency and would solve your problem. However, that only works if the library is a known target in the context of the target_link_libraries call. You can easily check this as follows:
if(TARGET ContinuumTransfunctioner)
message("Library target name")
else()
message("Plain library name")
endif()
target_link_libraries(Frobnigator ContinuumTransfunctioner)
So how do you get to the target name case? This depends on how your build is structured. If the library is being built as part of your CMake run, simply make sure that the corresponding add_library call is performed from a subdirectory that is pulled in via add_subdirectory from the file that performs the target_link_libraries call.
If the library in question is an external dependency, you need to build an imported target that carries all the relevant information where to find the library files (including any potential debug postfixes). This can be a bit cumbersome to do manually, so if you can, you might want to use CMake's packaging mechanism to generate this automatically as part of the library's build process.
Here's the solution, courtesy of the good people on the cmake mailing list:
# Note:
# $<$<CONFIG:Debug>:_d> is called a generator expression.
# It outputs _d if the build is debug.
#
set(Libraries
ContinuumTransfunctioner$<$<CONFIG:Debug>:_d>
Transmogrifier$<$<CONFIG:Debug>:_d>
)

CMake find_package dependency on subproject

I have the following directory layout:
main_folder
+ static_lib1
+ executable
Both 'static_lib1' and 'executable' have a full CMakeLists so that they can be
built independently.
The 'executable' depends on 'static_lib1'. It uses find_package() to locate 'static_lib1'.
The main_folder contains a CMakeLists that includes both 'static_lib1' and 'executable' via add_subdirectory for conveniently building the whole project in one go.
Everything works fine if I manually build 'static_lib1' and then 'executable'. But when running the CMakeLists from the main folder, I get an error because find_package is unable to find the library files from 'static_lib1' which have not yet been built.
How can I resolve this while keeping the CMakeLists files separate (i.e. without including the static_lib's CMakeLists from the executable's CMakeLists)?
In executable's CMakeLists.txt you can check if you are building stand-alone or as part of project:
if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR )
# stand-alone build
find_package(static_lib1)
else()
include_directories(../static_lib1)
link_directories(../static_lib1)
...
target_link_libraries(executable static_lib1)
endif()
Switch from a file-based approach to a target-based approach for handling the dependency from executable to static_lib1.
The original problem occurred because executable called find_package for locating static_lib1, which then attempted to fill a variable like STATIC_LIB1_LIBRARY with the paths to the library files by calling find_library. executable then consumes the content of that variable in a target_link_libraries(executable ${STATIC_LIB1_LIBRARY}) call. The problem here is, since those library files only get generated as part of the build, that call to find_library will not be able to find anything.
Building executable needs to support two scenarios here:
Building standalone, where a pre-compiled version of static_lib1 is located somewhere on the disc.
Building from main_folder, where both executable and static_lib1 are part of the same build.
The approach from the question supports scenario 1, but not scenario 2.
Instead of using using a variable to communicate a dependency between the two builds, use a target. The CMakeLists.txt for static_lib1 likely creates a library target like add_library(static_lib1 [...]). In executable we now simply do target_link_libraries(executable PUBLIC static_lib1). This is sufficient to support scenario 2.
To also allow for scenario 1 at the same time, we look at the call to find_package(static_lib1) in the CMakeLists.txt for executable. Instead of providing a variable like before, this call now needs to provide a target static_lib1 for consumption.
So we adapt the find script for static_lib1 to the following behavior:
If a target static_lib1 already exists, there's nothing to be done and the find script can just return (this is scenario 2).
Otherwise, we call find_library to locate the library file on disc (as before in the original approach) and then create a new imported target: add_library(static_lib1 STATIC IMPORTED). We then configure all relevant properties of the static library to that target. For instance, to add the location of the library file, we could do
set_target_properties(static_lib1 PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
IMPORTED_LOCATION ${STATIC_LIB1_LIBRARY}
)
To support multi-config generators like MSVC, instead of setting IMPORTED_LOCATION and IMPORTED_LINK_INTERFACE_LANGUAGES, you will want to set the configuration specific properties like IMPORTED_LOCATION_DEBUG and IMPORTED_LOCATION_RELEASE instead. Since this can get quite tedious to do manually, you can have CMake generate this information (and a bunch of other convenient stuff) for you in a package script. The find mechanism for package scripts works slightly different under the hood, but the code in the CMakeLists.txt for executable will look just the same, a simple call to find_package(static_lib1). The main difference is that this call will then not dispatch to a hand-written find script, but to a package script that was automatically generated by CMake as part of the build process of static_lib1.
I guess I will leave this answer for posterity since only recently I have searched for a solution to this problem and found out that...
Since CMake 3.24 it is possible!
It is possible to override subsequent calls to find_package() with FetchContent_Declare() flag OVERRIDE_FIND_PACKAGE.
Your
add_subdirectory("path/to/static_lib1")
call has to be replaced in main_folder/CMakeLists.txt with:
include(FetchContent)
FetchContent_Declare(
static_lib1
SOURCE_DIR "path/to/static_lib1"
OVERRIDE_FIND_PACKAGE
)
Any calls to find_package(static_lib1) will call FetchContent_MakeAvailable() for you, virtually making it identical to add_subdirectory() call.
You can read more about OVERRIDE_FIND_PACKAGE in CMake documentation.