Gem #155: Enhancing the GPRBuild Database for a New Language

by Vincent Celier —AdaCore

Let's get started...

In Gem #152, we described how to define a new language inside a project file. That's useful when you are using the language very seldom and when you only have one project file for the language. But when you are using the language often and you have several projects using this language, it becomes rather cumbersome.

Fortunately, there's a way to indicate to gprbuild all the characteristics of a language without having to repeat them in every project that uses the language. In fact, that's the technique used to support Ada, C, C++, and Fortran natively in gprbuild.

gprconfig XML database

As we discussed in the earlier Gem, gprbuild reads the definition of the language and its support tools from the project file. In fact, it also uses a second (most often implicit) file, called the configuration project file, or simply the configuration file.

The configuration file uses the same syntax as the project files themselves, and is merged with all of the project files that are parsed by gprbuild. The goal here is thus to alter the contents of the configuration file, so as to add the information for the new language there, instead of in every project in the project tree.

When gprbuild is invoked without a specified configuration project file (indicated by switches --config= or --autoconf=), and there is no default configuration project file (default.cgpr), gprbuild invokes gprconfig to create a configuration project file, and then uses the newly created file. This is called auto-configuration.

To build this configuration project file, gprconfig uses an XML database. By default the XML files used are located in directory <prefix>/share/gprconfig, where you will find files such as compilers.xml, c.xml, and gnat.xml.

It's also possible to indicate that gprconfig should take into account other XML files in another directory. This is done through the --db switch:

--db dir  (Parse dir as an additional knowledge base)

The format of these XML files is described in the GPRbuild User's Guide, in 2.2.6 The GPRconfig knowledge base.

Creating the XML file

So, to describe the characteristics of language "New_Lang", we will create an XML file new_lang.xml in a directory "db".

An XML file in a gprconfig database must start with:

        <?xml version="1.0" ?>
        <gprconfig>

and end with:

        </gprconfig>

For each compilable language, there must be a <compiler_description> tag and a <configuration> tag.

Naming the compiler

The first part of work done by gprconfig is to determine which compilers are installed. To do this, it uses the information from the <compiler_description> nodes in any of the XML files. These nodes need to explain how to locate the compiler executable anywhere on the PATH, then how to extract information from it such as its version and the list of supported languages.

The <compiler_description> tag includes several child tags. Some are compulsary: <name>, <executable>, <version>, and <languages>.

In our XML file newlang.xml, the compiler_description tag will be:

        <compiler_description>
          <name>NEW_LANG</name>
          <executable>nlang</executable>
          <version>1.0</version>
          <languages>New_Lang</languages>
        </compiler_description>

indicating that it is describing the NEW_LANG compiler for language "New_Lang", that the compiler driver is "nlang", and that it has version 1.0.

In general, the version number is not hard-coded like it is here, but is the result of running the executable with a special switch, and then parsing the output by using regular expressions. See the file compilers.xml in gprbuild distribution for some examples.

Describing the characteristics of the language

After this first pass, gprconfig has a full list of all the compilers available on the system. Based on the needs of the projects (in particular which programming languages are used and the target), it will try to find a set of compatible compilers, and use those to generate the configuration file.

Full control is given over the attributes that need to be added to the configuration file, using the <configuration> node in the XML files.

The <configuration> tag needs two child tags: <compilers> and <config>.

The <compilers> tag indicates the different languages/compilers/versions this configuration applies to. Here, we only need to indicate that the name is "NEW_LANG".

	<compilers>
          <compiler name="NEW_LANG">
        </compiler>

The <config> tag indicates the chunks that need to be included in the configuration project file. Here we only need to have packages Naming and Compiler.

If there are several "chunks" in different <configuration> nodes for the same package, gprconfig automatically merges these chunks in the package in the generated configuration file.

So, our XML file new_lang.xml will contain:

	<?xml version="1.0" ?>
        <gprconfig>
          <compiler_description>
            <name>NEW_LANG</name>
            <executable>nlang</executable>
            <version>1.0</version>
            <languages>New_Lang</languages>
          </compiler_description>
	  <configuration>
            <compilers>
              <compiler name="NEW_LANG">
            </compiler>
            <config>
        package Naming is
           for Body_Suffix ("New_Lang") use ".nlng";
        end Naming;
	package Compiler is
           for Driver ("New_Lang") use "nlang";
           for Leading_Required_Switches ("New_Lang") use ("-c");
           for Dependency_Kind ("New_Lang") use "Makefile";
           for Dependency_Switches ("New_Lang") use ("--dependencies=");
           for Include_Switches ("New_Lang") use ("-I", "");
        end Compiler;
            </config>
          </configuration>
        </gprconfig>

Using gprbuild auto-configuration for the new language

We need to make sure that the compiler "nlang" is on the path.

If we have a project file prj.gpr in the current working directory that contains:

	project Prj is
           for Languages use ("New_Lang");
        end Prj;

and a New_Lang source foo.nlng, then invoking:

	gprbuild prj.gpr --db db

will invoke the compiler for foo.nlng:

	$ gprbuild prj.gpr --db db
	nlang -c foo.nlng
	$

We now have a way to compile sources of our language New_Lang without the need to repeat all the characteristics of the language in each of the project files with New_Lang sources. However, we still need to invoke gprbuild with the switch --db. Could we do better?

Incorporating XML files in the default gprconfig database

Yes, we can! If we simply copy our XML file new_lang.xml into the default gprconfig XML database <prefix>/share/gprconfig, the language New_Lang will be taken into account automatically by gprconfig and we will no longer need to invoke gprbuild (or gprconfig) with the switch --db:

	$ gprbuild prj.gpr
        nlang -c foo.nlng
        $

Summary

We have described a way to have a new language auto-configured in gprbuild, through an XML file.

Of course, this example is very simple. The full documentation of the gprconfig XML files is in the GPRbuild User's Guide in section (2.2.6 The GPRconfig knowledge base) and its subsections.

So, if you are interested in describing your new languages through XML files, we encourage you to read it, and to study the different XML files in <prefix>/share/gprconfig.


About the Author

Vincent Celier spent twenty years in the French Navy, as a radar and computer officer. He retired in 1988 with the rank of commander and joined CR2A, a software house, where he was one of the authors of the ISO Technical Report ExtrA (Extensions Temps Reel en Ada). In 1994 he emigrated to Vancouver, Canada, to work on CAATS, the Canadian Automated Air Traffic System, a large system written in Ada. He joined AdaCore in 2000. He is the main implementer of the Project Manager and of gprbuild, the multi-language builder.