Reputation: 39
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?
Upvotes: 4
Views: 250
Reputation: 39738
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.
Upvotes: 4
Reputation: 6611
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.)
Upvotes: 8
Reputation: 25511
Ada doesn’t, but the most popular Ada toolset (GNAT) includes a tool gnatprep (see here) to do this. Other toolsets may include equivalents.
For example,
RCC.RCC_Periph.AHB1ENR := ($SCL_Enable => 1, others => <>);
$SCL_GPIO.MODER.Arr ($SCL_Pin) := 2; -- alternate function
$SCL_GPIO.OTYPER.OT.Arr ($SCL_Pin) := 1; -- open drain
$SCL_GPIO.OSPEEDR.Arr ($SCL_Pin) := 1; -- medium speed
$SCL_GPIO.PUPDR.Arr ($SCL_Pin) := 0; -- nopullup, no pulldown
#if SCL_Pin < 8 then
$SCL_GPIO.AFRL.Arr ($SCL_Pin) := 4; -- DocID022152 Rev 6 Table 9
#else
$SCL_GPIO.AFRH.Arr ($SCL_Pin) := 4; -- DocID022152 Rev 6 Table 9
#end if;
if translated using this Makefile fragment
src/i2c1-device.adb: ../i2c/src/i2c-device.adb.pp $(DEFINITION)
gnatprep \
-c -v \
$< \
$@ \
$(DEFINITION)
where the file $(DEFINITION)
contains
SCL_Enable := GPIOBEN
SCL_GPIO := GPIO.GPIOB_Periph
SCL_Pin := 8
results in
RCC.RCC_Periph.AHB1ENR := (GPIOBEN => 1, others => <>);
GPIO.GPIOB_Periph.MODER.Arr (8) := 2; -- alternate function
GPIO.GPIOB_Periph.OTYPER.OT.Arr (8) := 1; -- open drain
GPIO.GPIOB_Periph.OSPEEDR.Arr (8) := 1; -- medium speed
GPIO.GPIOB_Periph.PUPDR.Arr (8) := 0; -- nopullup, no pulldown
--! #if SCL_Pin < 8 then
--! $SCL_GPIO.AFRL.Arr ($SCL_Pin) := 4; -- DocID022152 Rev 6 Table 9
--! #else
GPIO.GPIOB_Periph.AFRH.Arr (8) := 4; -- DocID022152 Rev 6 Table 9
--! #end if;
Upvotes: 2