Differences between revisions 1 and 2
Revision 1 as of 2021-11-14 20:31:21
Size: 7564
Revision 2 as of 2021-11-15 23:38:10
Size: 7934
Deletions are marked like this. Additions are marked like this.
Line 96: Line 96:
On the other hand, Ada compilations require explicit recipes. If you want `make CFLAGS=-O0` to behave like `CFLAGS=-O0 make`,
you should repeat the default recipes.
{{{#!highlight Make
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.

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 (for gnatmake or gprbuild).

   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)

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. For example, if ADAFLAGS=-O0 and the default is -g -O2, the command line for compilation should contain -g -O2 -O0.

* 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 gnatmake_options and gprbuild_options seem good names.

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

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

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.

   1 CFLAGS   := -g -O2 -Wall $(CFLAGS)
   2 CPPFLAGS := -Ifoo/ $(CPPFLAGS)
   3 LDFLAGS  := -Wl,--as-needed $(LDFLAGS)
   4 LDLIBS   := ../bar/libbar.so libbaz.a -lfoo $(LDLIBS)
   5 main: helper.o

If you want make CFLAGS=-O0 to behave like CFLAGS=-O0 make, you should repeat the default recipes.

   1 main: main.o helper.o
   2         $(CC) -Wl,--as-needed $(LDFLAGS) $(TARGET_ARCH) $^ ../bar/libbar.so libbaz.a -lfoo $(LOADLIBES) $(LDLIBS) -o $@
   3 maino.o helper.o: %.o: %.c
   4         $(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.

   1 cargs = -g -O2 -gnatw $(ADAFLAGS)
   2 largs = -Wl,--as-needed $(LDFLAGS) ../bar/libbar.so libbaz.a -lfoo $(LDLIBS)
   3 .PHONY: build
   4 build:
   5         gnatmake main.adb $(gnatmake_options) -cargs $(cargs) -largs $(largs)

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.