Building a VCS status package with Gprbuild


Would you like your executable files to be able to tell which commit in your version control system it corresponds to?

You can do it by:

  1. Creating a program (in this example a shell script), which generates Ada source files reporting the version control status.
  2. Defining a "language" in Gprbuild, which corresponds to reading out the version control status, using the program from the previous step.
  3. Write a project file, which builds "source files" in this language.

Generating an Ada package from Mercurial status information

Install hg_revision_to_ada - listed below - somewhere in your execution path.

#! /bin/bash
#-----------------------------------------------------------------------------
#  Usage:

function usage() {
    echo "Usage:"
    echo "  $0 --compile      <source file>"
    echo "  $0 --dependencies <source file>"
    echo
}

#-----------------------------------------------------------------------------
#  Compile:

function compile() {
    if [ -z "$1" ]; then
        return 1
    else
        local source="$1"
    fi

    if [ -s "${source}" ]; then
        local package_name=$(cat "${source}")
    else
        return 2
    fi

    local source_directory="$(dirname "${source}")"
    local target="${source%.hg_status}.ads"
    local buffer="$(mktemp)"

    local hg_revision="$(cd "${source_directory}" && hg tip --template '{node}' 2>/dev/null)"
    local hg_modifier="$(if [ $(cd "${source_directory}" && hg status 2>/dev/null | wc -c) -gt 0 ]; then
                             echo "plus changes"
                         else
                             echo "as committed"
                         fi)"

    if [ -z "${hg_revision}" ]; then
        hg_revision="Warning: Not built from a Mercurial check-out.      "
        hg_modifier=""
    fi

    (
        echo 'package '${package_name}' is'
        echo '   Revision : constant String (1 .. 53) :='
        echo '                "'"${hg_revision}"' '"${hg_modifier}"'";'
        echo 'end '${package_name}';'
    ) > "${buffer}" ; mv "${buffer}" "${target}"
}

#-----------------------------------------------------------------------------
#  Dependencies:

function report_dependencies() {
    echo "Dependency reporting not implemented yet." 1>&2
    return 3
}

#-----------------------------------------------------------------------------
#  Bad command-line arguments:

function bad_arguments() {
    (
        echo "Call:"
        echo "   $0 $*"
        echo
        usage
    ) 1>&2

    exit 1
}

#-----------------------------------------------------------------------------

case "$1" in
    "--help")         usage                    ;;
    "--compile")      compile             "$2" ;;
    "--dependencies") report_dependencies "$2" ;;
    *)                bad_arguments $@         ;;
esac

#-----------------------------------------------------------------------------

With this tool, you can have a source file giving the name and file name for the Ada package containing the version control status information:

$ cat src/version_control_status.hg_status
Version_Control_Status
$ hg_revision_to_ada --compile src/version_control_status.hg_status
$ cat version_control_status.ads
package Version_Control_Status is
   Revision : constant String (1 .. 53) :=
                "76dc54636c40d2c5043575dd5809582a0bd8e703 as committed";
end Version_Control_Status;
$

Defining a new programming language in Gprbuild

We make the file `/usr/share/gprconfig/hg_revision.xml` contain this:

<?xml version="1.0" ?>
<gprconfig>
  <compiler_description>
    <name>Mercurial_revision_to_Ada</name>
    <executable>hg_revision_to_ada</executable>
    <version>1.0</version>
    <languages>Mercurial</languages>
  </compiler_description>

  <configuration>
    <compilers>
      <compiler name="Mercurial_revision_to_Ada"/>
    </compilers>
    <config>
        package Naming is
           for Body_Suffix ("Mercurial") use ".hg_status";
        end Naming;

        package Compiler is
           for Driver ("Mercurial") use "hg_revision_to_ada";
           for Leading_Required_Switches ("Mercurial") use ("--compile");
           for Dependency_Kind ("Mercurial") use "None";
        end Compiler;
    </config>
  </configuration>
</gprconfig>

Project file

Now we can make a project file, which uses the language "Mercurial":

project VCS_Status is
   for Languages use ("Mercurial");
   for Source_Dirs use ("src");
   for Object_Dir  use  "generated";
end VCS_Status;

Using it

$ gprbuild -p -P vcs_status
hg_revision_to_ada --compile version_control_status.hg_status
$ cat generated/version_control_status.ads
package Version_Control_Status is
   Revision : constant String (1 .. 53) :=
                "76dc54636c40d2c5043575dd5809582a0bd8e703 as committed";
end Version_Control_Status;
$

Now you can change your `Makefile` from:

build:
        gprbuild -p -P some_project

to:

build:
        gprbuild -p -P vcs_status
        gprbuild -p -P some_project

and have access to the Ada package `Version_Control_Status` in your project once you add `generated` to the list of source directories in "some_project.gpr".