Marcel Hernandez
Marcel Hernandez

Reputation: 1391

GPR project where every source file is a main file?

I have the following project structure, where every .adb file is a standalone executable that does not and will not depend on anything else:

project/
├── project.gpr
├── bin/
│   ├── bar
│   ├── baz
│   └── foo
├── obj/
│   └── .o's, .ali's, etcetera
└── src/
    ├── bar.adb
    ├── baz.adb
    └── foo.adb

And this is project.gpr:

project Project is
   for Source_Dirs use ("src");
   for Object_Dir use "obj";
   for Exec_Dir use "bin";
   for Main use ("src/foo.adb", "src/bar.adb", "src/baz.adb");
end Project;

Currently gprbuild and gprclean do exactly what I want, however the number of files under src/ might grow into the hundreds.

Is there a way to tell GPRbuild that every .adb file under src/ shall be considered a Main target without explicitly listing each one of them?

Upvotes: 2

Views: 1216

Answers (1)

LoneWanderer
LoneWanderer

Reputation: 3341

Since you are using GPR, you may consider the use of aggregate projects.

You define 1 GPR per executable to be produced. Then you define 1 aggregate GPR to build them all.

I do believe you are very close to a use case described in this link

Quoting:

Most often, an application is organized into modules and submodules, which are very conveniently represented as a project tree or graph (the root project A withs the projects for each modules (say B and C), which in turn with projects for submodules.

Very often, modules will build their own executables (for testing purposes for instance), or libraries (for easier reuse in various contexts).

However, if you build your project through gnatmake or gprbuild, using a syntax similar to

    gprbuild -PA.gpr

this will only rebuild the main programs of project A, not those of the imported projects B and C. Therefore you have to spawn several gnatmake commands, one per project, to build all executables. This is a little inconvenient, but more importantly is inefficient because gnatmake needs to do duplicate work to ensure that sources are up-to-date, and cannot easily compile things in parallel when using the -j switch.

Also libraries are always rebuilt when building a project.

You could therefore define an aggregate project Agg that groups A, B and C. Then, when you build with

     gprbuild -PAgg.gpr

this will build all mains from A, B and C.

    aggregate project Agg is
       for Project_Files use ("a.gpr", "b.gpr", "c.gpr");
    end Agg;

Of course, you end up having a little overhead of creating 1 GPR for each main, but it seems rather logic to clearly define each built artefact, specially if they will

never depend on each other

as you stated in your question.

However, it seems that GPR accepts a syntax for GPR aggregates where all GPRs in a given directory are taken into account. see here, and specifically here

Project_Files:

This attribute is compulsory. It specifies a list of constituent .gpr files that are grouped in the aggregate. The list may be empty. The project files can be any projects except configuration or abstract projects; they can be other aggregate projects. When grouping standard projects, you can have both the root of a project import closure (and you do not need to specify all its imported projects), and any project within the closure.

The basic idea is to specify all those projects that have main programs you want to build and link, or libraries you want to build. You can specify projects that do not use the Main attribute or the Library_* attributes, and the result will be to build all their source files (not just the ones needed by other projects).

[...]

Paths can also include the * and ** globbing patterns. The latter indicates that any subdirectory (recursively) will be searched for matching files. The ** pattern can only occur at the last position in the directory part (i.e. a/**/*.gpr is supported, but not **/a/*.gpr). Starting the pattern with ** is equivalent to starting with ./**.

At present the pattern * is only allowed in the filename part, not in the directory part. This is mostly for efficiency reasons to limit the number of system calls that are needed.

Here are a few examples:

    for Project_Files use ("a.gpr", "subdir/b.gpr");
    --  two specific projects relative to the directory of agg.gpr

    for Project_Files use ("/.gpr");
    --  all projects recursively

Upvotes: 1

Related Questions