Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Anyone Familiar With CMake?

This thread is locked; no one can reply to it. rss feed Print
Anyone Familiar With CMake?
RPG Hacker
Member #12,492
January 2011
avatar

I'm currently attempting to take my WIP game engine a step further by migrating it from Visual Studio to a cross-platform build solution. Since it seems to be the most popular one (and the one most active in development), I decided to go with CMake. However, it didn't take too long before I stumbled upon some weird behavior I didn't manage to solve myself.

The situation:
I'm taking baby steps with migrating my engine, so I'm starting with building the TLSF engine, which is my engine's first dependcy. For this, I'm using the following CMakeLists.txt:

#SelectExpand
1cmake_minimum_required(VERSION 3.5.2) 2 3include(${CMAKE_CURRENT_SOURCE_DIR}/../../properties/base.cmake) 4 5project(tlsf) 6 7add_library( 8 tlsf STATIC 9 ../../../source/dependencies/tlsf/tlsf-3.0/tlsf.c 10 ../../../source/dependencies/tlsf/tlsf-3.0/tlsf.h 11 ../../../source/dependencies/tlsf/tlsf-3.0/tlsfbits.h 12) 13 14set(CMAKE_INCLUDE_PATH "${CMAKE_INCLUDE_PATH};../../../source/dependencies/tlsf/tlsf-3.0/")

With the following text in base.cmake:

#SelectExpand
1if(NOT BASE_INCLUDED) 2 set(BASE_INCLUDED 1) 3 4 set(CMAKE_CONFIGURATION_TYPES "Debug;Release;Master" CACHE STRING "" FORCE) 5 if(NOT CMAKE_BUILD_TYPE) 6 message("No build type specified. Defaulting to 'Release'.") 7 set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE) 8 endif() 9 set_property(CACHE CMAKE_BUILD_TYPE PROPERTY HELPSTRING "The build type (Debug/Release/Master)") 10 set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;Master") 11 12 # I don't know if any of those even work, how can I confirm this? My intent is to copy all the configuration of Master build from Release build and then only change minor things afterwards. 13 set(CMAKE_C_FLAGS_MASTER "${CMAKE_C_FLAGS_RELEASE}") 14 set(CMAKE_CXX_FLAGS_MASTER "${CMAKE_CXX_FLAGS_RELEASE}") 15 set(CMAKE_EXE_LINKER_FLAGS_MASTER "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 16 set(CMAKE_STATIC_LINKER_FLAGS_MASTER "${CMAKE_STATIC_LINKER_FLAGS_RELEASE}") 17 set(CMAKE_SHARED_LINKER_FLAGS_MASTER "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 18 19 set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS "${COMPILE_DEFINITIONS}$<$<CONFIG:Debug>:;CGE_BUILD_DEBUG>") 20 set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS "${COMPILE_DEFINITIONS}$<$<CONFIG:Release>:;CGE_BUILD_RELEASE>") 21 set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS "${COMPILE_DEFINITIONS}$<$<CONFIG:Master>:;CGE_BUILD_MASTER>") 22 23 MARK_AS_ADVANCED(CMAKE_CONFIGURATION_TYPES) 24endif()

Basically, base.cmake is supposed to be a file that is included from all my CMakeLists.txt files and contains common stuff every project needs.

The problematic lines here are those three:

#SelectExpand
1 set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS "${COMPILE_DEFINITIONS}$<$<CONFIG:Debug>:;CGE_BUILD_DEBUG>") 2 set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS "${COMPILE_DEFINITIONS}$<$<CONFIG:Release>:;CGE_BUILD_RELEASE>") 3 set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS "${COMPILE_DEFINITIONS}$<$<CONFIG:Master>:;CGE_BUILD_MASTER>")

Basically, as you can probably guess, I'm trying to set the define CGE_BUILD_DEBUG in debug builds, CGE_BUILD_RELEASE in release builds and CGE_BUILD_MASTER in master builds. However, this doesn't work as intended. When I look at the preprocessor definitions in my Visual Studio solution, everything is incorrect. The last set_property always invalidates the previous two. That means: In master builds, CGE_BUILD_MASTER is set as intended, but in debug and release builds, nothing is set at all. It looks like ${COMPILE_DEFINITIONS} always returns an empty string for some reason, resulting in the previous contents of COMPILE_DEFINITIONS being lost.

I've also tried solving the problem in different ways, such as:

#SelectExpand
1 set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS_DEBUG "${COMPILE_DEFINITIONS_DEBUG};CGE_BUILD_DEBUG") 2 set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS_RELEASE "${COMPILE_DEFINITIONS_RELEASE};CGE_BUILD_RELEASE") 3 set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS_MASTER "${COMPILE_DEFINITIONS_RELEASE};CGE_BUILD_MASTER")

However, this just does nothing at all - in Visual Studio, the preprocessor definitions for all build configurations are completely empty. I have no idea what's going on.

Does anyone here have some experience with CMake and can push me into the right direction? Thanks in advance!

EDIT:
Alright. Figured this out today. COMPILE_DEFINITIONS is a property, but I tried to read it like a regular variable. Here is the solution to the problem:

#SelectExpand
1 get_property(CURRENT_COMPILE_DEFINITIONS DIRECTORY PROPERTY COMPILE_DEFINITIONS) 2 set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS "${CURRENT_COMPILE_DEFINITIONS}$<$<CONFIG:Debug>:;CGE_BUILD_DEBUG>") 3 get_property(CURRENT_COMPILE_DEFINITIONS DIRECTORY PROPERTY COMPILE_DEFINITIONS) 4 set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS "${CURRENT_COMPILE_DEFINITIONS}$<$<CONFIG:Release>:;CGE_BUILD_RELEASE>") 5 get_property(CURRENT_COMPILE_DEFINITIONS DIRECTORY PROPERTY COMPILE_DEFINITIONS) 6 set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS "${CURRENT_COMPILE_DEFINITIONS}$<$<CONFIG:Master>:;CGE_BUILD_MASTER>")

One problem still unsolved is how to copy all base configuration from Release to Master config. Currently, when I do this, the CGE_ defines are there, as intended, but Release configuration has an NDEBUG define, which Master build is missing. I don't know how to copy this over. If anyone has an idea, you can still feel free to post this here, even though the original problem has been solved.

EDIT:
Another thing to add (that I have now figured out): Apparently it was a bad idea from me to include the base.cmake file BEFORE the project() call. Should have included it afterwards -> that already cleared a lot of the problems I had (now Master configuration takes a lot of its variables from Release configuration).

m c
Member #5,337
December 2004
avatar

I'd help if I could, but cmake is too complicated for me.

in the cmake, can you do it like this:

if(config=debug) set_property(directory property compile_definitions "CGE_BUILD_DEBUG")
elseif(config=release) set_property(directory property compile_definitions "CGE_BUILD_RELEASE")

etc and then just re-run cmake to regenerate the solution to switch between debug and release?

What I do, is I have a makefile for most platforms, and then also a .txt that says "make a new empty project, drag and drop all files into it, add these libraries to the linker input lib1.lib lib2.lib lib3.lib and edit config.h" I cant do more because when I tried it couldn't work

(\ /)
(O.o)
(> <)

RPG Hacker
Member #12,492
January 2011
avatar

m c said:

if(config=debug) set_property(directory property compile_definitions "CGE_BUILD_DEBUG")
elseif(config=release) set_property(directory property compile_definitions "CGE_BUILD_RELEASE")

etc and then just re-run cmake to regenerate the solution to switch between debug and release?

That would probably work for single config generators (such as makefiles and similar), but it wouldn't work for multi config generators such as Visual Studio, XCode etc. The problem is that those IDEs only set their build configuration at build time (you can always switch between Debug and Release via a drop-down menu before building). When using if statements, you need to know the build configuration at CMake time, though, which doesn't work with those generators. That's why a different approach is needed.

However, I have sorted most of my CMake problems out by now. Some in a somewhat hacky way, many others probably in a rather nooby and unrecommended way, but so far, everything works as planned (though I still haven't fully migrated the project yet), that's the most important part for me. I don't mind if my CMake projects don't use perfect code that goes "by the book", as long as I don't have to make any major compromises (which, so far, I didn't - I could work around all of them).

Go to: