4

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?

3 Answers 3

8

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.)

Sign up to request clarification or add additional context in comments.

1 Comment

To explain a bit: When Debug and Release are statically known constants, the compiler will (most probably) remove the unreachable branch from the executable as soon as optimization is turned on. One downside is that code analysis tools may report dead code.
4

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.

1 Comment

This was my first idea when I have seen this question :)
2

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; 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.