I've been assigned to completely run a project using CMake.
Basically, the project has over 20 modules, and for each module i created a CMake file
such as:
# Module: CFS
file(
GLOB_RECURSE
files
*.c
*.cpp
)
include_directories("${PROJECT_SOURCE_DIR}/include/PEM/cfs")
include_directories("${PROJECT_SOURCE_DIR}/include/PEM/kernel2")
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
add_library(cfs ${files})
kernel2 is another module and has its own CMakeFile.
Now the problem is that a third module: m3 requires headers from cfs (which also require headers from kernel2)
So i basically go with:
# Module: m3
file( ... )
include_directories("${PROJECT_SOURCE_DIR}/include/PEM/cfs")
add_library(m3 ${files})
target_link_library(m3 cfs)
Unfortunately this is not enough, kernel2 included files won't be found when i compile unless I add:
include_directories("${PROJECT_SOURCE_DIR}/include/PEM/kernel2")
Am I doing it wrong? Perhaps I should also add include files using add_library directive?
If you have #include directives in cfs's headers, then you should use
include_directories("${PROJECT_SOURCE_DIR}/include/PEM/kernel2")
It's not the problem of CMake, but how C/C++ compiler works.
For example, you have following header in cfs:
#include "kernel2/someclass.h"
class SomeCfsClass
{
private:
SomeKernelClass kernelObject;
}
Now if you wish to instantiate SomeCfsClass in your m3 module, the compiler should know it's size. But knowing it's size is not possible without knowning SomeKernelClass definition from kernel2/someclass.h.
This situation can be resolved by storing not the object, but pointer to it inside SomeCfsClass:
class SomeKernelClass; // forward declare SomeKernelClass
class SomeCfsClass
{
private:
SomeKernelClass * kernelObject;
}
But of course, there are cases, when it's simply impossible to avoid including.
As an alternative, i can suggest to use relative paths in #include directives, but this solution is somewhat hackish.
Related
we are working on an embedded project in C/C++ and currently some special needs appeared. Background is there are two compiled libraries which define the same symbols. The compiler allows to create relocatable output modules (with partial linking) and to hide symbols for other compilation units when linking. This also means the output module does not need to have all the symbols defined, this will be done in the final linking. Compiler used is TI LTS1.3.0. I will link directly to the relocatable-section of the manual: https://software-dl.ti.com/codegen/docs/tiarmclang/rel1_3_0_LTS/compiler_manual/linker_description/04_linker_options/linker-output-options.html#stdz0756429
The other part of the project is hardly built on CMake with static libraries which are linked against each other via target_link_libraries.
To get this working I created an "add_executable"-target for each of those both output modules with the same symbols. To those I pass the static-libraries by CMake and get the linked with target_link_libraries.
But now I have a problem. All contents of the static libraries are compiled in each of those output modules. This is unwanted behaviour since as said the final linking does the job of linking the missing stuff - so the static-libraries - to it. This should be done with another add_executable command via CMake as well.
using the target include directories property is not suitable since it only adds the include directories of the given target itself but not of the target the target will include and link against.
So e.g. if you have (pseudo code):
#library A
function( create_libA )
add_library( libA src/A.c )
target_include_directories( libA PUBLIC /inc ) #contains A.h
endfunction()
#library B. different location
function( create_libB LIBA )
add_library( libB src/B.c )
target_link_libraries( libB PUBLIC ${LIBA} )
target_include_directories( libB PUBLIC /inc ) #contains B.h
endfunction()
#target output module with partial linking. Only should link and compile LIBTOBELINKEDIN, not libB. different location.
function( build_part_module LIBB LIBTOBELINKEDIN )
add_executable( outputModuleA src/func.c ) #func.c does include A.h
#following would cause libA and libB also to be compiled and linked in the output due to transitive stuff as I understood, which is unwanted.
target_link_libraries( outputModuleA PUBLIC ${LIBB} ${LIBTOBELINKEDIN} )
#trying this
get_target_property(libBInc ${LIBB} INTERFACE_INCLUDE_DIRECTORIES)
#will only include B.h but not A.h. compilation will fail.
target_include_directories(outputModuleA /inc ${libBInc})
I did not find any solution in Cmake itself to solve this problem. It's confusing me since all the include-directories must be known when the libraries are passed transitive, which is stated in the documentation. But I understand that getting the target include directories of just the passed lib does not include the other ones.
Since target_link_libraries does also not work this way I can only think of a maybe recursive solution? But for that my knowledge is just non-existent.
target_link_libraries with something like HEADERS_ONLY would be helpfull for this job.
Also one can say: if the output module contains all the definitions it won't be a problem, since the linker then knows them and will do its magic.
But this is also unwanted, since we use the generated static-libraries to place them into sections in different regions of the RAM directly. This would then mean to create another linker-script for partial linking which defines sections which then can be again moved. But the more we go this direction, the less we need CMake for it.
Instead of get_target_property use $<TARGET_PROPERTY> generator expression: the property's value, extracted by that expression, already includes transitive propagation:
target_include_directories(outputModuleA PRIVATE
$<TARGET_PROPERTY:libB,INTERFACE_INCLUDE_DIRECTORIES>
)
Note, that generator expressions has limited usage: not all functions expects them. Documentation for target_include_directories clearly states that the command supports generator expressions.
I have a simple CMakeLists for my shader code which currently just looks like this -
target_sources( JonsEngine
PRIVATE
AmbientPixel.hlsl
AvgLuminance.hlsl
BoxBlurPixel.hlsl
Common.hlsl
Constants.h
DepthReadback.hlsl
DirectionalLightPixel.hlsl
DirectionalLightPCF2X2Pixel.hlsl
DirectionalLightPCF3X3Pixel.hlsl
DirectionalLightPCF5X5Pixel.hlsl
DirectionalLightPCF7X7Pixel.hlsl
FullscreenTriangle.hlsl
FullscreenTriangleTexcoord.hlsl
FXAA.hlsl
FXAAPixel.hlsl
GBufferPixel.hlsl
GBufferVertex.hlsl
GBufferVertexAnimated.hlsl
GBufferVertexStatic.hlsl
OptimizedPCF.hlsl
PointLightPixel.hlsl
SDSMFinalCompute.hlsl
SDSMInitialCompute.hlsl
SimpleColorPixel.hlsl
SimpleTexturePixel.hlsl
SkyboxPixel.hlsl
SkyboxVertex.hlsl
SSAOPixel.hlsl
TerrainDomain.hlsl
TerrainHull.hlsl
TerrainPixel.hlsl
TerrainPixelDebug.hlsl
Tonemapping.hlsl
TransformAnimatedVertex.hlsl
TransformStaticInstancedVertex.hlsl
TransformStaticVertex.hlsl
)
I am trying to add a bunch of compile flags to some of them using like -
set_source_files_properties( AmbientPixel.hlsl PROPERTIES COMPILE_FLAGS "/E\"ps_main\" /ps\"_5_0\"" )
However when I generate the solution via CMake it seems to ignore this when I look at the compile options for that source file. What am I doing wrong?
EDIT: CMake 3.14+
CMake treats HLSL files as source extra, as it is not a first class language in CMake. Others have had your issue as well, which prompted CMake to add the VS_SHADER_FLAGS option.
Try something like this instead:
add_executable(JonsEngine)
# Set HLSL source file properties with VS_SHADER_FLAGS.
set_source_files_properties( AmbientPixel.hlsl PROPERTIES VS_SHADER_FLAGS "/E\"ps_main\" /ps\"_5_0\"")
# Associate these source extras with the executable defined above.
target_sources(JonsEngine PRIVATE
AmbientPixel.hlsl
AvgLuminance.hlsl
#
# ... other code files here ...
#
)
There are several other shader options CMake created for the Visual Studio generator, such as VS_SHADER_TYPE and VS_SHADER_ENTRYPOINT which provide additional build information to the shader compiler.
CMake cache variables can be set from virtually everywhere (see here #Florian's What's the CMake syntax to set and use variables?). I was under the assumption that the set value is visible everywhere, even to CMake lists parsed before, but this isn't the case.
Use case
Module A uses ${CMAKE_MYDEF}.
Module B sets the cache variable CMAKE_MYDEF.
add_subdirectory(A) is called before add_subdirectory(B).
Short example showing the behavior
cmake_minimum_required(VERSION 3.7)
project(test)
add_executable(EXEC test.cpp)
target_compile_definitions(EXEC PRIVATE MYDEF=${CMAKE_MYDEF})
set(CMAKE_MYDEF "MyValue" CACHE STRING "")
Questions
How can I make sure CMAKE_MYDEF has the desired value regardless the order I add module A and module B?
Are there any ways to ensure the CMake configuration step is re-run twice or, if applicable, as long as the cache variables get changed? (This isn't probably a clean solution, but since I'm working with legacy code not everything can be done beautifully.)
Are there alternatives to cache variables to achieve the same result without re-running the CMake configuration by hand?
Is it possible to set compiler definitions in the generation phase (i.e. when all CMake cache variables are known and set)? Using some kind of generator expressions?
Edit: Short example solution
Following #Florian's answer, here the adapted example showing the solution:
cmake_minimum_required(VERSION 3.7)
project(test)
add_executable(EXEC test.cpp)
target_link_libraries(EXEC MyOtherLib)
add_library(MyOtherLib INTERFACE)
set(CMAKE_MYDEF "MyValue" CACHE STRING "")
target_compile_definitions(MyOtherLib INTERFACE MYDEF=${CMAKE_MYDEF})
Yes, I'm fully with #Tsyvarev's answer that CMake's parser works sequentially. So variables - even cached ones - or generator expressions - that can't read variables - are no good here.
I just wanted to add the possibilities you have using target and directory properties depending on the dependencies between A and B:
When A depends on B with e.g.
target_link_libraries(A PUBLIC B)
then a simple
target_compile_definitions(B PUBLIC MYDEF=SOME_DEF)
would propagate the necessary define to A.
When B depends an A and A is already known than it would be
target_link_libraries(B PUBLIC A)
target_compile_definitions(A PUBLIC MYDEF=SOME_OTHER_DEF)
If you're working with sub-directories I would recommend putting the definition in the root CMakeLists.txt globally:
add_definitions(-DMYDEF=GLOBAL_DEF)
Finally the full variant with sub-directories letting B decide what to do:
CMakeLists.txt
cmake_minimum_required(VERSION 3.7)
project(test)
add_subdirectory(A)
add_subdirectory(B)
A\CMakeLists.txt
file(WRITE a.cpp [=[
#include <iostream>
#ifndef MYDEF
#define MYDEF "Hello from A!"
#endif
void a()
{
std::cout << MYDEF << std::endl;
}
]=])
add_library(A a.cpp)
B\CMakeLists.txt
file(WRITE b.cpp [=[
void a();
void main()
{
a();
}
]=])
add_executable(B b.cpp)
target_link_libraries(B A)
if (TARGET "A")
target_compile_definitions(A PRIVATE MYDEF="Hello from B!")
else()
set_property(
DIRECTORY ".."
APPEND PROPERTY
COMPILE_DEFINITIONS "MYDEF=\"Hello from Global!\""
)
endif()
Reference
Is Cmake set variable recursive?
CMake processes scripts sequentially, starting from top-level CMakeLists.txt and executing its lines one by one.
So, if read variable before assigning it, you will get nothing. The only specific of CACHE variable in that scenario is possibility for that variable to be assigned on previous cmake invocation.
Needs for using a variable before its assigning taking a place usually signals about bad design. In many situations (even with legacy code), design can be fixed gracefully.
Forcing CMake to reconfigure the project can be accomplished e.g. by touching current script:
to force a re-configure, one could "cmake -E touch"
the CMAKE_CURRENT_LIST_FILE, somehow during target building
or some such.
I want to use CMake to create a simple static library Maths, and then link to that library to create a simple executable Alpha.
Here's my directory structure:
/Maths
/build
/
/CMakeLists.txt
/maths.cpp
/maths.h
/Alpha
/build
/
/CMakeLists.txt
/main.cpp
And here are the contents of those files:
* CMakeLists.txt (for Maths) *
cmake_minimum_required(VERSION 2.6)
project(Maths)
add_library(maths STATIC maths.cpp)
* maths.cpp *
int Double(int x)
{
int y = x * 2;
return y;
}
* maths.h *
int Double(int x);
* CMakeLists.txt (for Alpha) *
cmake_minimum_required(VERSION 2.8)
project(Alpha)
add_executable(alpha main.cpp ../Maths/maths.h)
target_link_libraries(maths ../Maths/build/libmaths.a)
* main.cpp *
#include <iostream>
#include "maths.h"
using namespace std;
int main()
{
int x = 5;
int y = Triple(x);
cout << "The answer = " << y << endl;
return 0;
}
So, I enter the directory /Maths/build, and run cmake ... Then, I run make. This creates the library file libmaths.a in the directory /Maths/build.
Then, I enter the directory /Alpha/build, and run cmake ... Then, I run make. However, this gives me the error message:
/usr/bin/ld: cannot find -lmaths
Why can make not find the library file that I just created?
I also tried copyiing libmaths.a and maths.h into the directory /Alpha, and changing Alpha's CMakeLists.txt to:
cmake_minimum_required(VERSION 2.8)
project(Alpha)
add_executable(alpha main.cpp maths.h)
target_link_libraries(maths libmaths.a)
But this gave the same error.
The essential problem here is that libmaths.a is not placed in one of the default linker directories.
The bigger problem is that you passed libmaths.a to target_link_libraries in the first place.
This is bad for several reasons, but the most obvious is that it's completely non-portable. A different compiler toolchain might use a different ending than .a for static libraries, so you won't be able to build there. Also, should you ever decide to switch to dynamic linking in the future, it means refactoring your existing CMake scripts.
The proper solution is to not pass hardcoded filenames to target_link_libraries. There are two roads that you can take to avoid this.
Use a find script. Essentially, you call find_library(MY_MATHLIB maths [...]) and then do target_link_libraries(alpha ${MY_MATHLIB}) instead. This will determine the absolute path of the .a file in a portable way and pass that to the linker. The biggest disadvantage of this approach is that the .a file must already be there for the find call to succeed. So if you want to build maths after running the CMake for alpha or build it as part of alpha, this won't work.
Since both libraries are under your control, consider using CMake's packaging mechanism. This is by far the most convenient approach for building, but unfortunately also the most difficult to set up. If you are willing to spend some time digging through the CMake docs and experimenting with the build, I encourage you to give it a try, but if you just want something that works quickly, you might want to stick with option 1.
Use a CMakeLists.txt in the root directory
/CMakeLists.txt:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT( MyProject )
ADD_SUBDIRECTORY( Maths )
ADD_SUBDIRECTORY( Alpha )
in Alpha/CMakeLists.txt:
TARGET_LINK_LIBRARIES( alpha maths )
Run CMake on the top-level CMakeLists.txt instead of the individual subdirectory ones
I feel a little stupid right now. After recently converting a few smaller projects to use CMake, I decided to also get rid of a few "Platform_Config.h" files. These files contain a few preprocessing directives like #define USE_NEW_CACHE and control compilation.
How would I 'convert' these defines to be controlled with CMake? Ideally by using these "cache" variables the user can easily edit.
There are two options. You can use the add_definitions method to pass defines as compiler flags: E.g. somewhere in your projects cmakelists.txt:
add_definitions( -DUSE_NEW_CACHE )
CMake will make sure the -D prefix is converted to the right flag for your compiler (/D for msvc and -D for gcc).
Alternatively, check out configure_file. It is more complex, but may be better suited to your original approach with a Platform_Config file.
You can create an input-file, similar to your original Platform_Config.h and add "#cmakedefine" lines to it.
Let's call in Platform_Config.h.in:
// In Platform_Config.h.in
#cmakedefine USE_NEW_CACHE
// end of Platform_Config.h.in
When then running
configure_file( ${CMAKE_SOURCE_DIR}/Platform_Config.h.in ${CMAKE_BINARY_DIR}/common/Platform_Config.h )
it will generate a new Platform_Config file in your build-dir. Those variables in cmake which are also a cmakedefine will be present in the generated file, the other ones will be commented out or undefed.
Of course, you should make sure the actual, generated file is then correctly found when including it in your source files.
option command might provide what you are looking for.
use it with the COMPILE DEFINITIONS property on the target and i think you are done.
To set the property on the target, use the command set target properties
option(DEBUGPRINTS "Prints a lot of debug prints")
target(myProgram ...)
if(DEBUGPRINTS)
set_target_properties(myProgram PROPERTIES COMPILE_DEFINITIONS "DEBUGPRINTS=1")
endif()
edit:
The option i wrote in the example shows up as a checkbox in the CMake GUI.
In case you want to set defines per target: Since 2.8.11 you can use target_compile_definitions.
In earlier versions you probably don't want to use set_target_properties as is, since it overwrites any defines you set previously. Call get_target_property first instead, then merge with previous values. See add_target_definitions here.
Use target_compile_options. Do not quote your define or it not be detected as a define. CMake parses off the /define and adds the actual define to the DefineConstants section of the csproj, if there are quotes it will put the entire quoted string in the AdditionalOptions section of the csproj.
An example from one of my projects that uses generator expressions:
target_compile_options( ${LIBRARY_NAME} PRIVATE
$<${IS_ART_ITERATION_BUILD}:/define:ART_ITERATION_BUILD>
)
An example without generator expressions:
target_compile_options( ${LIBRARY_NAME} PRIVATE
/define:GRAPHICS_VULKAN
)