Gprdeps: a new tool for gpr project files linting

I have developed (the beginning of) a new tool, written in Rust. It is meant to replace a tool that my company DeepBlueCapital has developed internally and which was initially written in Python. But the Rust version is 10 or 15 times faster.

The URL is GitHub - briot/gprdeps: Dependency-graph build for AdaCore's GPR files

It starts by loading a GPR file and its dependencies (or all project files in a given directory and child directories). It then finds all source files for all the projects. So far this is similar to what gprbuild itself does, but gprdeps does this loading for all scenarios at the same time (so it knows sources for debug and release mode, or for tasking and non-tasking, and so on depending on which scenario variables you are using).

It then builds a large internal graph to represent project dependencies and file dependencies (which it gets by parsing Ada and C source files, not using ALI files like gprbuild does). So as soon as we finish loading, we have this big graph for all possible combinations of scenarios. gprbuild never builds such a graph, and only loads for one scenario. Also, did I mention speed ? On our 2 million lines of sources, the tool loads everything, including parsing all source files, in 0.3s on my laptop. gprbuild takes about 10s before it spawns any gcc (which is basically the only thing we can measure)

Once we have this graph, we can perform a number of queries:

  • given a source path, report all source files it (possibly indirectly) depends on
  • given a source path; report all source files that depend on it
  • given two paths, report the shortest dependency path between the two. Useful to understand why a file ends up in the closure and has to be elaborated, for instance
  • report unused source files; we have discovered quite a number of old files that were not used anymore for any executable or any compilation mode, but were still compiled by gprbuild when using -u for instance
  • find exactly which compilation switches would be used for a source file in any scenario

This is very much work in progress, and could be used as the basis for a better and faster gprbuild for instance, or maybe in the context of alire. Unfortunately, I will not be able to maintain it in the long term, so this tool is looking for maintainers if we are to add new features.
It is already useful as is though, so I encourage you to have a go.

Emmanuel

5 Likes

That is a great achievement you’ve made. I appreciate the fact that you listed some key examples of how the tool can be used. Given that AdaCore is supporting Rust and are the owners of gprbuild, my guess is they would likely be interested in taking over maintenance of such a useful tool, but you probably know better.

Out of genuine curiosity, and not to start a language debate, did you initially try an Ada implementation of the tool, only to later conclude a Rust version was the better option?

1 Like

This tool is how I always thought gprbuild should have been implemented. I was part of the design discussions when AdaCore initially developed the GPR files, but we unfortunately chose to reuse the GNAT front-end for the parsing (for reasons still unclear to me, as the parser is really the simple part here). Having all dependency information in memory from the get-go, and not relying on external tools (the compiler) to discover the dependencies as we go opens the door for quite a number of things.

I remember we had an issue in the compiler at some point with the order of -l (libraries) on the linker command line. Having a graph of dependencies would have let us find a proper ordering with a simple topological sort for instance. One other idea is to easy add extra steps for generating sources (this is just going from source A to source B via code generation, then source B to object C via compilation for instance). Instead, gprbuild uses ad-hoc algorithm every time.

I have written the tool in Rust because I wanted to learn that language. It would just as easily have been written in Ada I am sure. I must say I was very surprised by the speed of the executable, which I attribute to compiler optimizations that are possible because of the lack of aliasing. But Ada would likely have been pretty close too.

2 Likes

*Cough*, *cough* database.
(I’m of the opinion that we should have a whole DB-based Ada IDE, bootstrappable.)

IMO, this is because of the cousin of the “text is simple, text is cheap” mentality.

What was the most interesting thing you learned?

While the tool is very interesting, from an Ada advocacy point of view, as well as to find future volunteer maintainers, it is unfortunate it’s not written in Ada…

I agree. The general idea of the graph and how we process all scenarios simultaneously is really what this project brings. Hopefully it might prove useful to some future project.
In terms of performance, I think the default Ada containers will prove way slower, but they are not needed quite as much with the use of pointers.
I will not convert this tool myself, as I will stop doing Ada development soon as part of a career change, but I am certainly happy to discuss technical details if someone wants to try that.

1 Like

Congrats with the career change!
Despite no longer doing Ada professionally, I hope you will still keep a foot in the Ada world and the forums as you’ve been a long time contributor.

2 Likes

Who knows, maybe someone will have an interest and convert it to Ada. It would be an interesting comparison.

1 Like