Gem #158: GPRinstall - Part 1

by Pascal Obry —EDF R&D

Let's get started...

It's quite easy to build a library using GPRbuild. GPRbuild handles multiple languages and multiple tool chains. Basically, most projects can be built using a simple command like:

$ gprbuild -p libprj.gpr

Note that -p tells gprbuild to create any missing obj, lib, or exec directories. The same option applies to gprinstall. After some minutes or hours, depending on the size of the code base, we end up with a set of object code and/or libraries. So far so good!

Our next step is to install all of this for use by another project. That is, we have a library that we want to make available to another project. The other project needs the Ada spec files that will be "withed" and the library file (static or dynamic) to link against. We then begin writing a set of makefile rules:

install:
        mkdir /some/prefix/include/prj
        mkdir /some/prefix/lib/prj
        cp lib/*.ali /some/prefix/

Wait... Do we have cp on Windows? Do we have to set the .ali file read-only? Do we have to copy the bodies? Oh, and of course we need to provide a project that will be installed at the right place to be able to "with" it from another project.

It's starting to look like there's a lot of work ahead...

Or not. In fact, GPRinstall will take care of everything. Yes, it really will take care of everything.

Specifically, GPRinstall will:

  • copy the right sources, given the current naming conventions and exceptions
  • copy the spec to the right place
  • copy the bodies to the right place, only if needed
  • copy the object code or the library to the right place
  • generate a project file automatically and put it in the right place (where gprbuild will look for it)
  • record all installed files and provide an easy way to uninstall exactly what has been installed, no more and no less.
  • provide a way to install multiple builds (debug/production, static/shared libraries are the most common), selectable with a project variable

The "right place" above is, by default, the location where the compiler is installed. Or course, GPRinstall has many switches to indicate where compilation artifacts are to be installed. So, to install the library project above we just have to run:

$ gprinstall -p libprj.gpr

If we have a second build for the shared library:

$ gprbuild -p -XLIBRARY_TYPE=relocatable libprj.gpr

then we can install it with:

$ gprinstall -p -XLIBRARY_TYPE=relocatable --build-name=shared libprj.gpr

Note that we have specified a build name here, which is just a string used to identify a specific installation. This string is added as a possible value in the BUILD variable of the generated project. On the first gprinstall invocation, we have used the default build name string which is "default". So we now have something like this in the generated project:

library project Libprj is
   type BUILD_KIND is ("default", "shared");
   BUILD : BUILD_KIND := external("LIBPRJ_BUILD", "default");
   for Languages use ("Ada");
   case BUILD is
      when "shared" =>
         for Source_Dirs use ("../../include/libprj.shared");
         for Library_Dir use "../../lib/libprj.shared";
         for Library_Kind use "relocatable";
      when "default" =>
         for Source_Dirs use ("../../include/libprj");
         for Library_Dir use "../../lib/libprj";
         for Library_Kind use "static";
   end case;
   ...

And if we decide to actually remove all installations of this library, we just have to run:

$ gprinstall --uninstall libprj

That's about it. Wait, no, in fact there's one more thing... we can now remove all the makefile mess!