Search code examples
cmakefirebreath

Can someone explain this cmake script to me?


I feel like the entire cmake community is trolling me. None of the "tutorials" or resources make any sense to me. Its like I am missing something. I think what confuses me the most is the language and none of the tutorials I have seen come even close to being decent at explaining cmake to someone who has little unix and make experience.

Anyways, I am working with FireBreath and it uses cmake extensively, I think its time I start figuring out how to use it rather than changing the project files directly.

The root CMakeLists.txt file contains the following:

cmake_minimum_required (VERSION 2.6)
set (CMAKE_BACKWARDS_COMPATIBILITY 2.6)

Project(${PLUGIN_NAME})

file (GLOB GENERAL RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
    [^.]*.cpp
    [^.]*.h
    [^.]*.cmake
    )

include_directories(${PLUGIN_INCLUDE_DIRS})

# Generated files are stored in ${GENERATED} by the project configuration
SET_SOURCE_FILES_PROPERTIES(
    ${GENERATED}
    PROPERTIES
        GENERATED 1
    )

SOURCE_GROUP(Generated FILES
    ${GENERATED}
    )

SET( SOURCES
    ${GENERAL}
    ${GENERATED}
    )

I would really appreciate it if someone could explain each line to me. Especially what ${GENERAL} and ${GENERATED} are.


Solution

  • First of all, the cmake syntax is really simple. It consists of "commands" and "arguments". It's so simple that it takes a while for it to sink in. Everything is "command(arguments)". Additionally, command names are case-insensitive. Previously they had to be ALL CAPS, but since version 2.6 (I think) it doesn't matter. Arguments however are case-sensitive.

    cmake_minimum_required (VERSION 2.6)
    

    This command sets the minimum required version of cmake for a project. If the current version of cmake is lower than 2.6 it will stop processing and report an error. This prevents having to support ancient versions of the tool.

    set (CMAKE_BACKWARDS_COMPATIBILITY 2.6)
    

    Set the variable CMAKE_BACKWARDS_COMPATIBILITY to the value 2.6. This is actually a minor bug in the CMakeLists.txt file you've presented, as CMAKE_BACKWARDS_COMPATIBILITY should not be used for 2.6 and above. The script should probably use cmake_policy. This is to specify how newer versions of cmake should behave when faced with inconsistencies in previous versions of cmake. Any scripts you write from scratch today wouldn't need to worry about this.

    Project(${PLUGIN_NAME})
    

    Sets the project name to the value of whatever is in the variable PLUGIN_NAME. This value is shown as the project name in some IDEs. To write a value to a variable, you can use set(PLUGIN_NAME myName) and to read the value you use the ${} syntax: "${PLUGIN_NAME}". Some commands also write to variables as well, but you use them the same way as in the set command.

    file (GLOB GENERAL RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
         [^.]*.cpp
         [^.]*.h
         [^.]*.cmake
         )
    

    file is a command. Its first argument GLOB means "return files on disk whose names match the patterns I'll give as arguments". The next argument GENERAL is the variable that the result is stored to, like with set it writes the result to the variable and you can later read it with ${GENERAL}. RELATIVE and the path means return the file names relative to that path, not the complete path. So rather than "C:\some\long\path\src\foo.cpp" or "/home/me/some/path/src/foo.cpp" you would get "src\foo.cpp" or "src/foo.cpp". The variable CMAKE_CURRENT_SOURCE_DIR is a "magic variable" that CMake fills in for you and it refers to the path to the source directory currently being processed, where this CMakeLists.txt file lives. The last list of arguments are the patterns of files that will be matched. Basically, anything that has the file extension cpp, h or cmake.

    include_directories(${PLUGIN_INCLUDE_DIRS})
    

    Add the directories in ${PLUGIN_INCLUDE_DIRS} to those searched by the compiler for include files. This will result in extra "-I" arguments if you compile with gcc, for example.

    # Generated files are stored in ${GENERATED} by the project configuration
    

    Lines that start with # are comments.

    SET_SOURCE_FILES_PROPERTIES(
           ${GENERATED}
           PROPERTIES
               GENERATED 1
           )
    

    Files can have key/value pairs associated with them, and this affects how they are built. Here the files listed in the variable ${GENERATED} have the property "GENERATED" set to the value 1. What does that mean? Well, CMake now knows not to look for the files "${GENERATED}" on disk, as they will be created in another build step. In the snippet posted, nobody sets the variable ${GENERATED}. I imagine that it gets set elsewhere in the project files. Don't mix up the variable ${GENERATED} with the property GENERATED! This is a subtle point, and perhaps the variable should have been GENERATED_FILES to avoid confusion, i.e SET_SOURCE_FILES_PROPERTIES(${GENERATED_FILES} PROPERTIES GENERATED 1).

     SOURCE_GROUP(Generated FILES ${GENERATED})
    

    This creates a group, which in Visual Studio translates into a file tab, called "Generated" that contains the files in the variable ${GENERATED}.

     SET(SOURCES ${GENERAL} ${GENERATED})
    

    This line sets the variable SOURCES to whatever is in the variables ${GENERAL} and ${GENERATED}. Earlier we set ${GENERAL} to be the list of cpp, h and cmake files that were in the current source directory. In a C-like pseudo-code this is like "SOURCES = GENERAL + GENERATED". As a detail of implementation, the value SOURCES is actually a list and its contents are separated by ";" characters. Usually this is done so that later you can create a library or executable by just using the variable ${SOURCES} rather than repeating the other 2 variables everywhere.