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.