Differences between revisions 2 and 3
Revision 2 as of 2021-11-15 23:38:10
Size: 7934
Comment:
Revision 3 as of 2021-11-16 17:11:12
Size: 10004
Comment:
Deletions are marked like this. Additions are marked like this.
Line 37: Line 37:
(for `gnatmake` or `gprbuild`). (`GNATMAKEFLAGS` and `GPRBUILDFLAGS` after gnat-11).
Line 61: Line 61:
For example, if `ADAFLAGS=-O0` and the default is `-g -O2`,
the command line for compilation should contain `-g -O2 -O0`.
Line 70: Line 68:
There is no convention, but `gnatmake_options` and
`gprbuild_options` seem good names.
There is no convention, but `GNATMAKEFLAGS` and
`GPRBUILDFLAGS` follow GNU standards.
Line 76: Line 74:

* It should be possible to similarly change the build tool,
for example when cross-building.
The tradition is to name the variable after the capitalized tool.

* It should be possible to install an unstripped executable.
Line 86: Line 90:
The default recipes in GNU Make expand most of these variables,
so the following example builds `main` from `main.c`, `helper.h` and
`helper.c`.
Even for C, it is not easy to use the default GNU Make recipes,
because for example
Line 90: Line 93:
CFLAGS := -g -O2 -Wall $(CFLAGS)
CPPFLAGS := -Ifoo/ $(CPPFLAGS)
LDFLAGS := -Wl,--as-needed $(LDFLAGS)
LDLIBS := ../bar/libbar.so libbaz.a -lfoo $(LDLIBS)
CFLAGS := -g -O2 $(CFLAGS)
Line 96: Line 96:
If you want `make CFLAGS=-O0` to behave like `CFLAGS=-O0 make`,
you should repeat the default recipes.
give different results for `make CFLAGS=-O0` and `CFLAGS=-O0 make`.

The following Makefile is configurable enough as far as Debian is affected.
Line 99: Line 101:
main: main.o helper.o
        $(CC) -Wl,--as-needed $(LDFLAGS) $(TARGET_ARCH) $^ ../bar/libbar.so libbaz.a -lfoo $(LOADLIBES) $(LDLIBS) -o $@
maino.o helper.o: %.o: %.c
        $(CC) -g -O2 -Wall $(CFLAGS) -Ifoo/ $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<
}}}

Ada compilations require explicit recipes.
The targets are usually phony because `gnatmake` knows Ada
dependencies better than `make`.
{{{#!highlight Make
cargs = -g -O2 -gnatw $(ADAFLAGS)
largs = -Wl,--as-needed $(LDFLAGS) ../bar/libbar.so libbaz.a -lfoo $(LDLIBS)
.PHONY: build
build:
        gnatmake main.adb $(gnatmake_options) -cargs $(cargs) -largs $(largs)
# https://www.gnu.org/prep/standards/html_node/Command-Variables.html
GNATMAKE ?= gnatmake
GNATMAKEFLAGS ?= -v
ADAFLAGS ?= -g -O2
# CC ?= cc
CFLAGS ?= -g -O2
# CPPFLAGS ?=
# LDFLAGS ?=
# LDLIBS ?=
INSTALL ?= install
INSTALL_PROGRAM ?= $(INSTALL)
INSTALL_DATA ?= $(INSTALL) -m 644
STRIP ?= strip

# https://www.gnu.org/prep/standards/html_node/DESTDIR.html
# DESTDIR =

# https://www.gnu.org/prep/standards/html_node/Directory-Variables.html
# These path may also affect references embedded in executables
# (resources), documentation, pkg-config .pc files or GNAT .gpr
# projects (sources and libraries)...
prefix = /usr/local
exec_prefix = $(prefix)
bindir = $(exec_prefix)/bin
datarootdir = $(prefix)/share
mandir = $(datarootdir)/man

# Override is probable.
NUMJOBS != getconf _NPROCESSORS_ONLN

# Override is unexpected, these settings are either required or inoffensive.
executable = tool
ada_main_unit = ada_main_unit
gnatmakeflags = -eS -j$(NUMJOBS)
adaflags = -gnatwa -gnatwe
cflags = -Wall -Wextra
cppflags = -Ifoo/
ldflags = -Wl,--as-needed
ldlibs = ../bar/libbar.so libbaz.a -lfoo
c_objects = obj/c1.o obj/c2.o

all: $(c_objects) | obj
        $(GNATMAKE) $(ada_main_unit) -aIsrc -D obj -o obj/$(executable) \
          $(gnatmakeflags) $(GNATMAKEFLAGS) \
          -cargs $(adaflags) $(ADAFLAGS) \
          -largs $(ldflags) $(LDFLAGS) $(c_objects) $(ldlibs) $(LDLIBS)
$(c_objects): obj/%.o: src/%.c src/%.h | obj
        $(CC) $(cflags) $(CFLAGS) $(cppflags) $(CPPFLAGS) -c -o $@ $<
obj/c1.o: src/c2.h
obj:
        mkdir obj
install: all
        $(INSTALL_PROGRAM) obj/$(executable) -Dt $(DESTDIR)$(bindir)
        $(INSTALL_DATA) $(executable).1 -Dt $(DESTDIR)$(mandir)
install-strip: install
        $(STRIP) $(DESTDIR)$(bindir)/$(executable)
clean:
        rm -fr obj
.PHONY: all install clean

This page informally completes the Debian Policy for Ada.

Links for Debian maintainers of Ada packages

Debian patches GCC so that it handles SOURCE_DATE_EPOCH, so this reproducibility issue should not be a concern anymore. See the dedicated page for details.

The dh-ada-library helper may be convenient, even if you have to write a fake project for it.

Here is a full example.

Build flags in Debian

dpkg-buildflags computes CFLAGS, CPPFLAGS and LDFLAGS from the architecture, system-wide defaults, DEB_BUILD_MAINT_OPTIONS (set by the Debian maintainer in debian/rules), and DEB_BUILD_OPTIONS (set at build time). It is usually invoked implicitly by dpkg-buildpackage or buildflags.mk.

For historical reasons, dpkg-buildpackage invokes dpkg-buildflags, then exports the computed values before running debian/rules. It is recommended that debian/rules also computes and exports them, as dpkg-buildpackage is not the only way to invoke debian/rules.

The buildflags.mk Makefile snippet serves this purpose, and ada/debian_packaging.mk adds ADAFLAGS and BUILDER_OPTIONS (GNATMAKEFLAGS and GPRBUILDFLAGS after gnat-11).

   1 include /usr/share/dpkg/buildflags.mk
   2 include /usr/share/dpkg/buildopts.mk
   3 # Build-Depends: gnat (<= 10)
   4 include /usr/share/ada/debian_packaging-$(shell gnatgcc -dumpversion)
   5 # Build-Depends-Arch: gnat (<= 10)
   6 gcc_version != sed -n '/^ gnat-\(.*\),$$/{s//\1/;p;q}' debian/control
   7 include $(wildcard /usr/share/ada/debian_packaging-$(gcc_version))
   8 # Build-Depends: gnat (>= 11)
   9 include /usr/share/ada/debian_packaging.mk
  10 # Build-Depends-Arch: gnat (>= 11)
  11 include $(wildcard /usr/share/ada/debian_packaging.mk)
  12 

Suggestions for upstream maintainers

This section describes how to make an Ada build system easy to package for Debian.

The build system should support the configuration of build flags via environment variables or command-line arguments.

* ADAFLAGS should be added to the Ada compiler command line, after default arguments so that they take precedence.

* CFLAGS and CPPFLAGS should similarly affect the C compiler and preprocessor. The preprocessor is usually implicitly run by the compiler, so these variables often ends up concatened.

* A variable should similarly affect the gnatmake or gprbuild command lines. There is no convention, but GNATMAKEFLAGS and GPRBUILDFLAGS follow GNU standards.

* LDFLAGS and LDLIBS should be added respectively before and after the linker default arguments, so that options like -Wl,--as-needed work as expected.

* It should be possible to similarly change the build tool, for example when cross-building. The tradition is to name the variable after the capitalized tool.

* It should be possible to install an unstripped executable.

* For shared libraries, it should be possible to set the shared object version, that is the last part of the -Wl,-soname=libNAME.so.VERSION linker argument. There is no convention, but NAME_soversion comes to mind (or NAME_soname for the whole argument). The prefix matters because a typical link involves several libraries. When a source package builds several libraries, please allow a distinct override for each library, even if the default is common.

Even for C, it is not easy to use the default GNU Make recipes, because for example

   1 CFLAGS   := -g -O2 $(CFLAGS)
   2 main: helper.o

give different results for make CFLAGS=-O0 and CFLAGS=-O0 make.

The following Makefile is configurable enough as far as Debian is affected.

   1 # https://www.gnu.org/prep/standards/html_node/Command-Variables.html          
   2 GNATMAKE                   ?= gnatmake
   3 GNATMAKEFLAGS              ?= -v
   4 ADAFLAGS                   ?= -g -O2
   5 # CC                       ?= cc                                         
   6 CFLAGS                     ?= -g -O2
   7 # CPPFLAGS                 ?=                                         
   8 # LDFLAGS                  ?=                                         
   9 # LDLIBS                   ?=                                         
  10 INSTALL                    ?= install
  11 INSTALL_PROGRAM            ?= $(INSTALL)
  12 INSTALL_DATA               ?= $(INSTALL) -m 644
  13 STRIP                      ?= strip
  14 
  15 # https://www.gnu.org/prep/standards/html_node/DESTDIR.html                    
  16 # DESTDIR                   =                                         
  17 
  18 # https://www.gnu.org/prep/standards/html_node/Directory-Variables.html        
  19 # These path may also affect references embedded in executables                
  20 # (resources), documentation, pkg-config .pc files or GNAT .gpr                
  21 # projects (sources and libraries)...                                         
  22 prefix                      = /usr/local
  23 exec_prefix                 = $(prefix)
  24 bindir                      = $(exec_prefix)/bin
  25 datarootdir                 = $(prefix)/share
  26 mandir                      = $(datarootdir)/man
  27 
  28 # Override is probable.                                         
  29 NUMJOBS                    != getconf _NPROCESSORS_ONLN
  30 
  31 # Override is unexpected, these settings are either required or inoffensive.   
  32 executable                  = tool
  33 ada_main_unit               = ada_main_unit
  34 gnatmakeflags               = -eS -j$(NUMJOBS)
  35 adaflags                    = -gnatwa -gnatwe
  36 cflags                      = -Wall -Wextra
  37 cppflags                    = -Ifoo/
  38 ldflags                     = -Wl,--as-needed
  39 ldlibs                      = ../bar/libbar.so libbaz.a -lfoo
  40 c_objects                   = obj/c1.o obj/c2.o
  41 
  42 all: $(c_objects) | obj
  43         $(GNATMAKE) $(ada_main_unit) -aIsrc -D obj -o obj/$(executable) \
  44           $(gnatmakeflags) $(GNATMAKEFLAGS) \
  45           -cargs $(adaflags) $(ADAFLAGS) \
  46           -largs $(ldflags) $(LDFLAGS) $(c_objects) $(ldlibs) $(LDLIBS)
  47 $(c_objects): obj/%.o: src/%.c src/%.h | obj
  48         $(CC) $(cflags) $(CFLAGS) $(cppflags) $(CPPFLAGS) -c -o $@ $<
  49 obj/c1.o: src/c2.h
  50 obj:
  51         mkdir obj
  52 install: all
  53         $(INSTALL_PROGRAM) obj/$(executable) -Dt $(DESTDIR)$(bindir)
  54         $(INSTALL_DATA) $(executable).1 -Dt $(DESTDIR)$(mandir)
  55 install-strip: install
  56         $(STRIP) $(DESTDIR)$(bindir)/$(executable)
  57 clean:
  58         rm -fr obj
  59 .PHONY: all install clean

Here is an example of GNAT project.

   1    package Compiler is
   2       Adaflags := ("-g", "-O2");
   3       case Mode is
   4          when "production" => Adaflags := Adaflags & ("-gnatn");
   5          when "debug"      => Adaflags := Adaflags & ("-gnata");
   6       end case;
   7       Adaflags := Adaflags & External_As_List ("ADAFLAGS", " ");
   8       for Switches ("Ada") use Adaflags;
   9       for Switches ("C") use ("-g", "-O2", "-Ifoo/")
  10                              & External_As_List ("CFLAGS", " ")
  11                              & External_As_List ("CPPFLAGS", " ");
  12    end Compiler;
  13    package Linker is
  14       for Leading_Switches ("Ada") use ("-Wl,--as-needed")
  15                                        & External_As_List ("LDFLAGS", " ");
  16       for Switches ("Ada") use ("../bar/libbar.so", "libbaz.a", "-lfoo")
  17                                & External_As_List ("LDLIBS", " ");
  18    end Linker;

For a library project, there is no need to change the Compiler package. The object and library directoy paths should be reasonably unique for a given set of build flags. At least, PIC and non-PIC objects should be stored separately so that a static archive and a shared object may be built in a row.

   1    Pic    := ""; -- for now
   2    Ldlibs := ("../bar/libbar.so", "libbaz.a", "-lfoo")
   3              & External_As_List ("LDLIBS", " ");
   4    case project'Library_Kind is
   5       when "dynamic" | "relocatable" =>
   6          for Library_Version use "libNAME.so."
   7                                  & External ("NAME_soversion", "3.4");
   8          for Leading_Library_Options use ("-Wl,--as-needed")
   9                                          & External_As_List ("LDFLAGS", " ");
  10          for Library_Options use Ldlibs;
  11          Pic := "pic";
  12       when "static" =>
  13          Pic := "static";
  14       when "static-pic" =>
  15          Pic := "pic";
  16    end case;
  17    for Object_Dir  use "build/obj-" & Pic & "-" & Mode;
  18    for Library_Dir use "build/lib-" & Pic & "-" & Mode;
  19    package Linker is
  20       for Linker_Options use Ldlibs;
  21    end Linker;

Scenario variables from the environment or -X options should be prefered to constructs like gprbuild -cargs $(ADAFLAGS). The interaction of -cargs with Compiler.Switches is under-documented and hardly predictable. For the same reason, the options for gprbuild itself should be set either on the command line or in a Builder package, but not both.

At least for libraries, gprinstall or install targets are usually not flexible enough for Debian and completely ignored.

The clean target should simply rm -fr the build directory. gprclean is complex, slow, and typically requires several runs with different scenario variables.

In Makefiles, please try to catch only the expected error instead of prefixing a recipe with a dash. For example, if rm -fr foo ever fails, the error is probably important and unexpected enough to stop execution.

In Makefiles, please avoid .SILENT and use the @ prefix with parcimony. Verbose logs are quite useful when debugging.