Seit wir bei der Arbeit den Prozessor gewechselt haben sind wir von unserem Buildsystem Make abgekommen und setzten auf die vom Hersteller des Controllers bereitgestellte IDE um den Buildprozess zu managen. Allerdings ist die Targetverwaltung in Eclipse recht mühsam. Aus diesem Grund habe ich mir mal CMake etwas genauer angeschaut und nun unseren Buildprozess darauf umgestellt. Heute zeige ich, wie man mit CMake Versionsinformationen aus Mercurial generieren kann.

Wenn wir eine Version erstellen, dann bekommt diese einen TAG der folgendermaßen aufgebaut ist v[MAJOR].[MINOR][RT][RN] mit

  • MAJOR - einstellige Nummer
  • Minor - zweistellige Nummer
  • RT - Release type aus {ALPHA, BETA, RELEASE}
  • RN - einstellige Releasenummer

oben: Log aus Mercurial; unten: Ergebnis Header

Header Prototyp

CMake braucht einen Prototypen wie der Header aussehen soll. Ich nenne ihn version.h.in

/**
 * \brief Version information
 *
 * This file is automatically generated by the buildsystem.
 * Do not edit.
 */

#ifndef VERSION_H
#define VERSION_H

#define VERSION_NODE                    "${HG_NODE}"
#define VERSION_AUTHOR                  "${HG_AUTHOR}"
#define VERSION_TAGS                    "${HG_TAGS}"
#define VERSION_MAJOR                   ${HG_VERSION_MAJOR}
#define VERSION_MINOR                   ${HG_VERSION_MINOR}
#define VERSION_RELEASE_TYPE_ALPHA      0
#define VERSION_RELEASE_TYPE_BETA       1
#define VERSION_RELEASE_TYPE_RELEASE    2
#define VERSION_RELEASE_TYPE            ${HG_VERSION_RELEASE_TYPE}
#define VERSION_RELEASE_NUMBER          ${HG_VERSION_RELEASE_NUMBER}

#endif /* VERSION_H */

mercurial.cmake

CMake bietet schon von sich aus das Paket find_package(Hg) an mit dem man ganz einfach überprüfen kann ob Mercurial installiert ist.

Dann muss man zunächst herausfinden welcher Commit gerade ausgecheckt ist und ob sich die Arbeitskopie verändert hat, es also uncommitted changes gibt. Mercurial signalisiert das durch ein Pluszeichen am Ende der Node.

Als nächstes kann man mit hg log und einem Template alle möglichen Informationen abfragen. Wenn man als Trennzeichen im Template das Semikolon verwendet, kann CMake den Rückgabewert direkt als list verwenden.

Zum Schluss muss dann noch configure_file mit dem oben beschriebenen Prototypen aufgerufen werden.

Das ganze wird dann mithilfe von cmake -P mercurial.cmake aufgerufen.

########################################################
# Check if mercurial is installed
find_package(Hg)
if(HG_FOUND)
    ####################################################
    # Some default values
    set(HG_VERSION_MAJOR -1)
    set(HG_VERSION_MINOR -1)
    set(HG_VERSION_RELEASE_TYPE VERSION_RELEASE_TYPE_ALPHA)
    set(HG_VERSION_RELEASE_NUMBER -1)

    ####################################################
    # Get the current changeset
    execute_process(
        COMMAND ${HG_EXECUTABLE} id -i
        OUTPUT_VARIABLE HG_NODE
        )
    string(STRIP ${HG_NODE} HG_NODE) # Remove any trailing or leading whitespaces

    ####################################################
    # Check if the working directory is clean
    string(FIND ${HG_NODE} "+" HG_WD_DIRTY)
    if(${HG_WD_DIRTY} LESS 0)
        set(HG_WD_DIRTY OFF)

        ################################################
        # Query all information needed...
        execute_process(
            COMMAND ${HG_EXECUTABLE} log -r${HG_NODE} --template="\;{node}\;{tags}\;{author}\;{date|shortdate}\;{date\(date,'%H:%M:%S'\)}\;"
            OUTPUT_VARIABLE HG_QUERY
            )
        ################################################
        # ...and sort them into the right variables
        list(GET HG_QUERY 1 HG_NODE_FULL)
        list(GET HG_QUERY 3 HG_AUTHOR)
        list(GET HG_QUERY 4 HG_DATE)
        list(GET HG_QUERY 5 HG_TIME)
        list(GET HG_QUERY 2 HG_TAGS)

        ################################################
        # Check if a 'version' Tag is found and extract
        # it's information
        string(FIND ${HG_TAGS} "v" HG_VERSION_IN_TAG)
        if(NOT ${HG_VERSION_IN_TAG} LESS 0)
            string(SUBSTRING ${HG_TAGS} ${HG_VERSION_IN_TAG} 7 HG_VERSION_TAG)
            string(SUBSTRING ${HG_VERSION_TAG} 1 1 HG_VERSION_MAJOR)
            string(SUBSTRING ${HG_VERSION_TAG} 3 2 HG_VERSION_MINOR)
            string(SUBSTRING ${HG_VERSION_TAG} 5 1 HG_VERSION_RELEASE_TYPE)
            string(SUBSTRING ${HG_VERSION_TAG} 6 1 HG_VERSION_RELEASE_NUMBER)
            if(${HG_VERSION_RELEASE_TYPE} STREQUAL "a")
                set(HG_VERSION_RELEASE_TYPE "VERSION_RELEASE_TYPE_ALPHA")
            elseif(${HG_VERSION_RELEASE_TYPE} STREQUAL "b")
                set(HG_VERSION_RELEASE_TYPE "VERSION_RELEASE_TYPE_BETA")
            elseif(${HG_VERSION_RELEASE_TYPE} STREQUAL "r")
                set(HG_VERSION_RELEASE_TYPE "VERSION_RELEASE_TYPE_RELEASE")
            endif()
        endif()
    else()
        set(HG_WD_DIRTY ON)
    endif()
    configure_file(version.h.in version.h)
endif()

Vorheriger Beitrag Nächster Beitrag