I would like to instruct the Ada compiler to select between two different blocks of code, depending on predefined static compiler directives, such as for instance "DEBUG" or "RELEASE". I would like to do something like this:
if DEBUG then < COMPILE THIS CODE > end if;
if RELEASE then < COMPILE THIS OTHER CODE > end if;
C# and other languages offer the #define directive for this. Has Ada something similar to offer? And if not, how is this done in Ada?
Good Ada style is to write it exactly as you did, making sure that the entities Debug
and Release
are static.
One way to do that is to have a package, say Compilation_Mode
, which exists in two variants:
package Compilation_Mode is
Debug : constant Boolean := False;
Release : constant Boolean := True;
end Compilation_Mode;
and
package Compilation_Mode is
Debug : constant Boolean := True;
Release : constant Boolean := False;
end Compilation_Mode;
You then let your build system select the appropriate package. (Using gprbuild
, you could do it with a package Naming
in the project file.)
It is not very straightforward, but a possible way is to use separate compilation units. For example, say that you are inside the body of a package named Pkg
, and want some code that should do different things based on some scenario variable (this is a GNAT GPRBuild term, but the method can also be used with other build systems). You move that code into some subroutine, let's say, Do_Something
, and declare it as separate
:
package body Pkg is
-- ...
procedure Do_Something is separate;
-- ...
end Pkg;
This tells the compiler that this procedure is defined as separate compilation unit. Now, you put your two (or any number of) implementations in two separate files like this:
separate (Pkg) procedure Do_Something is
-- ...
begin
-- ...
end Do_Something;
This tells the compiler that this is the definition of a separate compilation unit from within Pkg
. And yes, this means that for each bunch of code, you would need to write two (or n with n = number of differing implementations) additional files.
A good method for managing those files would be to put all debug implementations in a debug directory and likewise for release implementations. You then instruct your build system to load sources from one or the other directory. For example with GPRBuild:
project My_Project is
-- define legal modes
type Mode_Type is ("debug", "release");
-- load mode from environment variable `Mode`, defaulting to "debug"
Mode : Mode_Type = external ("Mode", "debug");
-- add directory with appropriate separate implementations
case Mode is
when "debug" => My_Sources := My_Sources & "src/debug";
when "release" => My_Sources := My_Sources & "src/release";
end case;
-- define where the compiler should load sources from
for Source_Dirs use My_Sources;
-- ...
end My_Project;
Your src folder would have a layout like this:
src
debug
pkg-do_something.adb
release
pkg-do_something.adb
pkg.adb
pkg.ads
This approach works well with multiple, orthogonal scenario values. It also forces you to separate scenario-specific code from general code, which may be seen as good thing, but ymmv.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With