1

I am trying to have gprbuild automatically set some variables' values in my source code - one way or another. In particular I want the outputs of certain commands to be accessible from within the code. In C with Makefiles this is easy:

source:

#include <stdio.h>
int main() { printf("%s\n", COMMAND_OUTPUT); return 0; }

make:

result : source.c
    $(CC) -DCOMMAND_OUTPUT=`command -with -options`

However I have no idea how to do such a thing with gprbuild and Ada. (Short of ditching gprbuild and just using make - but I rather like gprbuild)

LambdaBeta
  • 1,479
  • 1
  • 13
  • 25
  • 1
    You might be able to use [gnatprep](https://gcc.gnu.org/onlinedocs/gnat_ugn/Preprocessing-with-gnatprep.html#Preprocessing-with-gnatprep), particularly in the [integrated preprocessing](https://gcc.gnu.org/onlinedocs/gnat_ugn/Integrated-Preprocessing.html) mode. I’ve used gnatprep, but not the integrated preprocessing. – Simon Wright Dec 18 '17 at 21:15
  • Why does the information have to be in the source? Generally you can put the information in a file and have your program read that file. – Jeffrey R. Carter Dec 19 '17 at 09:35

3 Answers3

2

Ada does not use a preprocessor like C does.You cannot expect Ada compilers to modify strings in your code. Use of such inline editing can easily become a violation of Ada strong typing, which would be very difficult to diagnose and would be completely invisible to source code static analysis.

Jim Rogers
  • 4,822
  • 1
  • 11
  • 24
  • It should still be possible to initialize a string like in Jacob Sparre Andersen's response somehow. If not then I guess I have to just use that solution. – LambdaBeta Dec 18 '17 at 20:10
  • 1
    This isn’t really true. [gnatprep](https://gcc.gnu.org/onlinedocs/gnat_ugn/Preprocessing-with-gnatprep.html#Preprocessing-with-gnatprep), though inelegant and somewhat limited (probably by design) will preprocess source. – Simon Wright Dec 18 '17 at 21:16
  • 2
    And gnatprep is not part of the Ada language. It is a tool provided by one vendor. – Jim Rogers Dec 19 '17 at 01:06
2

I solve that by generating an Ada file from the makefile before building.

An example:

HG_STATE_SOURCE     = src/mercurial.ads
HG_MODIFIER         = `test $$(hg status | wc -c || echo 0) -gt 0 && echo "plus changes" || echo "as committed"`
HG_REVISION         = `hg tip --template '{node}' 2>/dev/null || echo N/A_____________________________________`

[...]

$(HG_STATE_SOURCE): Makefile $(REPOSITORY_CONFIG) $(REPOSITORY_STATE) $(PROJECT_ROOT_SOURCE)
    @mkdir -p src
    @echo 'package 'Mercurial is'                                >  $(HG_STATE_SOURCE)
    @echo '   Revision : constant String (1 .. 53) :='           >> $(HG_STATE_SOURCE)
    @echo '                "'$(HG_REVISION)' '$(HG_MODIFIER)'";' >> $(HG_STATE_SOURCE)
    @echo 'end 'Mercurial;'                                      >> $(HG_STATE_SOURCE)
Jacob Sparre Andersen
  • 6,733
  • 17
  • 22
  • This would work perfectly if there was some way to pass it through using just gprbuild, although I guess I can always wrap the gprbuild in make. If its impossible to do natively with gprbuild then I'll accept your answer as correct. – LambdaBeta Dec 18 '17 at 20:10
  • After seeing the proposition from Simon Wright I will accept this answer as correct until such a time as gnatprep allows external commands to be run to generate its contents. – LambdaBeta Dec 18 '17 at 23:16
  • It is not a pleasant solution. I have a neater one somewhere. – Jacob Sparre Andersen Dec 19 '17 at 05:50
  • The blog entry describes how I do the same thing with gprbuild: http://ada.tips/building-a-vcs-status-package-with-gprbuild.html – Jacob Sparre Andersen Dec 19 '17 at 05:51
1

Yes, the gnatprep preprocessor allows exactly the same as what you have in your C code:

main.adb:

with Ada.Text_IO; use Ada.Text_IO;

procedure Main is
begin
   Put_Line ($Command_Output);
end Main;

simple_gnatprep.gpr:

project simple_gnatprep is

   for Create_Missing_Dirs use "True";

   Command_Output := external ("Command_Output");

   for Source_Dirs use (".");
   for Exec_Dir use ".";
   for Main use ("main.adb");

   for Object_Dir use "obj/" & "CommandOutput_" & Command_Output;

   package Compiler is
      for Switches ("Ada") use ("-gnateDCommand_Output=""" & Command_Output & """");
   end Compiler;
end simple_gnatprep;

Makefile:

COMMAND_OUTPUT=$(shell echo hello there)

all:
    gprbuild -d -p -g -XCommand_Output='${COMMAND_OUTPUT}'

clean:
    rm -rf obj/ *.exe

Note I have included the command output in the obj/ directory used, which will fail if the command outputs any symbol that cannot appear in a directory name. However, if you omit it then gprbuild will say that your executable is up-to-date when nothing has changed except the output of the command.

Another option is to always remove the object directory before compiling, but when possible it is better to include the value of any preprocessor symbols in the object path so that switching from one configuration (e.g. Debug / Release) to another and back doesn't throw away intermediate results and slow down your development process.

Gnatprep is only included in the GNAT compiler, because there isn't yet any provision for preprocessing in the Ada standard. For other compilers, you will need to run each file through gnatprep separately in the Makefile, and then pass it to the compiler. In this case there is no need to fiddle with object directory names, as the source file will always be new and the compiler will always have to recompile everything.

TamaMcGlinn
  • 2,840
  • 23
  • 34
  • 1
    Well answered! I must say, since asking the question I have used gnatprep, but didn't think of hooking it into a makefile in this way. Also the issue with output appearing in directory names could be solved with something like: `-XCommand_Hash='$(shell echo "$COMMAND_OUTPUT" | shasum -)'` and using `Command_Hash` as the file name. – LambdaBeta Dec 10 '20 at 17:24
  • note: this also works when calling alr, e.g. [`alr build -- -Xexecutables=src/build_wall.adb`](https://github.com/TamaMcGlinn/AdaBots_examples/blob/main/Makefile) to have a single project with configurable main program. I use this method to good effect in [ABE](https://github.com/TamaMcGlinn/AdaBots_editor), where cntrl+B is mapped to compile the current file as main. – TamaMcGlinn Jul 31 '22 at 13:52