How to generate different dependencies on Debian and Ubuntu with a common source package

There are situations in which a given package needs to have different dependencies in Debian and in Ubuntu. Despite this difference it’s possible to keep a single source package that will build both variants of the package. Continue reading to discover a step-by-step explanation.

1. When is that needed?

While it is possible to have different dependencies depending on the distribution on which the package is built, it should be usually avoided when possible. This infrastructure should only be used as a last resort when there are no better alternatives.

Here are some examples of when it might be needed:

  • Ubuntu has some packages that Debian does not have (or vice-versa), and the resulting package would benefit from having them installed.
  • The package names differ between Ubuntu and Debian and it’s on purpose (i.e. the difference is a justified choice and not a mistake because both distributions failed to coordinate).
  • The packages are built differently in both distributions, and the run time dependencies are not the same due to this. Maybe some associated patches are only applied in Ubuntu.

2. Using substitution variables in debian/control

The dependency that varies between both distributions can’t be hardcoded in debian/control, instead you should put a substitution variable (substvar) that will be replaced at build time by dpkg-gencontrol. You can name it ${dist:Depends} for example:

[...]
Depends: bzip2, ${shlibs:Depends}, ${misc:Depends}, ${dist:Depends}
[...]

Note that you typically already have other substitution variables (${shlibs:Depends} comes from dpkg-shlibdeps and ${misc:Depends} from debhelper and its dh_* scripts).

3. dpkg-gencontrol needs a -V option

The value used to replace this new variable needs to be communicated to dpkg-gencontrol. You can use the -V option for this, the syntax would be something like this:

dpkg-gencontrol [...] -Vdist:Depends="foo (>= 2), bar"

If you use debhelper, you have to pass the option to dh_gencontrol after two dashes (--):

dh_gencontrol -- -Vdist:Depends="foo (>= 2), bar"

If you use CDBS, you can set the DEB_DH_GENCONTROL_ARGS_ALL make variable:

include /usr/share/cdbs/1/rules/debhelper.mk
DEB_DH_GENCONTROL_ARGS_ALL = -- -Vdist:Depends="foo (>= 2), bar"

The value given to dpkg-gencontrol is static in all those examples, now let’s see how we can use give a different value depending on the distribution that we’re targetting.

4. Using dpkg-vendor in debian/rules

dpkg-vendor is a small tool (provided by the dpkg-dev package) that parses the /etc/dpkg/origins/default file (provided by the base-files package) to know the current distribution and its ancestry. It can be used in debian/rules to adjust the behavior depending on the current distribution. You can check its man-page to learn about the various options supported but we’re only going to use --derives-from <vendor> in this sample. With this option the script exits with zero if the current distribution is or derives from the indicated distribution, or with 1 otherwise.

Now combining all together, we can use dpkg-vendor to dynamically define the content of the substitution variable in debian/rules. Let’s suppose that you want ${dist:Depends} to be “foo (>= 2)” on Ubuntu (and its derivatives) and “bar” everywhere else. Using debhelper’s 7 tiny rules file, this example could be:

ifeq ($(shell dpkg-vendor --derives-from Ubuntu && echo yes),yes)
	SUBSTVARS = -Vdist:Depends="foo (>= 2)"
else
	SUBSTVARS = -Vdist:Depends="bar"
endif

%:
	dh $@

override_dh_gencontrol:
	dh_gencontrol -- $(SUBSTVARS)

If you use CDBS, it could be this:

include /usr/share/cdbs/1/rules/debhelper.mk
ifeq ($(shell dpkg-vendor --derives-from Ubuntu && echo yes),yes)
	DEB_DH_GENCONTROL_ARGS_ALL = -- -Vdist:Depends="foo (>= 2)"
else
	DEB_DH_GENCONTROL_ARGS_ALL = -- -Vdist:Depends="bar"
endif

Do you want to read more tutorials like this one? Click here to subscribe to my free newsletter, you can opt to receive future articles by email.

Additional Resources

Get the Debian Administrator's Handbook

After a successful liberation campaign, the Debian Administrator's Handbook is now freely available. If you appreciate my articles and what I do for Debian, check out the book and grab a copy.

Comments

  1. Thanks for this post, it solves a few problems I encountered while packaging debian/ubuntu packages.

  2. What if the build-depends are different? For instance, a package I build (for various Ubuntu releases) depends on a specific kernel package of the form linux-2.6.xx-yy. The only solution I have found is to generate the debian/control file to have the right Build-Depends for each different kernel.

    • Well, build depends are part of the the source package so you can’t really have a common source package with different build-depends.

      Your solution is to have a common source repository from which you can generate the various source packages that you need. I think that’s the best you can do currently.

      But I’d like to suggest that ideally you should have a meta-package tracking the specific version that you need in each distribution, like linux-headers-2.6-686: on lenny it depends on on linux-headers-2.6.26-2-686 but on squeeze it depends on linux-headers-2.6.32-5-686. That way you’d always build-depend on the meta-package.

      • Does that mean it is not possible at all to vary the Build-Depends in that manner?
        In my case, my package depends on “libboost-signals-dev, postgresql-contrib-8.4, …” on Ubuntu and “libboost-signals1.42-dev, postgresql-contrib-8.3, …” on debian lenny.

        Also, I didn’t quite figure out how to correctly incorporate the example code into my rules file. Here is how I tried:
        rules

        • No, you can’t vary the Build-Depends with a substitution variable like described in this post. You don’t control the dpkg-source invocation and by default there’s no substvar file used by dpkg-source (the build-depends is stored in the .dsc file generated by dpkg-source).

          For your rules files, you don’t need a rule override_dh_gencontrol, that’s only for people using “dh”. Just replace the dh_gencontrol line with “dh_gencontrol — -Vdist:Depends=something”. The variable affectation is usually put before everything else, it’s always executed by make, it’s not part of the commands of a specific target.

          ifeq ($(shell dpkg-vendor --derives-from Ubuntu && echo yes),yes) 
               DEPENDS=postgresql-contrib-8.4, postgresql-8.4-postgis 
          else
               DEPENDS=postgresql-contrib-8.3, postgresql-8.3-postgis 
          endif
          [...]
          binary-arch: build install
          [...]
                  dh_gencontrol -- -Vdist:Depends="$(DEPENDS)"
          [...]
          
        • If you really need to solve the Build-Depends issue, you have to generate debian/control on the fly in the clean target (it’s the only target that is usually run before dpkg-source). Here you can use the dpkg-vendor invocation to verify the current distribution and decide what packages you’re going to inject in the Build-Depends field with some sed magic.

          • Ok, I try to modify the control file in the clean target, but it doesn’t seem to work:

            rules

            And that’s what I get:

            dpkg-buildpackage: Quellpaket flightpred
            dpkg-buildpackage: Host-Architektur amd64
            dpkg-checkbuilddeps: Warnung: kann Abhängigkeit ${dist:Build-Depends} nicht auswerten
            dpkg-checkbuilddeps: Fehler: Fehler beim Parsen von Build-Depends/Build-Depends-Indep)
            dpkg-buildpackage: Warnung: Bauabhängigkeiten/-konflikte nicht erfüllt; Abbruch
            dpkg-buildpackage: Warnung: (Verwenden Sie -d, um sich darüber hinwegzusetzen.)

          • Richard, I think this blog is not the best place for this… you should ask your next questions on debian-mentors@lists.debian.org.

            You can’t have a substitution variable in Build-Depends in debian/control. Currently you have to have debian/control.in with a marker in Build-Depends that you’re going to replace with sed.

            sed -e "s/@BUILD_DEPENDS@/postgresql.../" debian/control.in >debian/control
            

            But it’s merely a hack because doing this in the clean target means that it’s done after the build dependency check that you see failing. So you have to use the “-d” option when you want to rebuild the ubuntu source package on Debian or vice-versa (because both are different… they have different build-dependencies!).

  3. I think the “–derives-from” command is ambiguous. Ubuntu certainly derives from Debian, but nevertheless “dpkg-vendor –derives-from Ubuntu” is true on Ubuntu, while “dpkg-vendor –derives-from Debian” is not. How about Linux Mint? Is it considered to be derived from Ubuntu or rather from Debian or is it unique enough (just as seems for Ubuntu) to have “dpkg-vendor –derives-from Mint” return true?

    • Fabian, the answer given by dpkg-vendor is solely driven by the data in /etc/dpkg/origins/*. If Ubuntu hat the field “Parent: Debian” in /etc/dpkg/origins/ubuntu then dpkg-vendor --derives-from Debian would return true… feel free to report a bug about this on Launchpad (against the base-files package).

      I don’t know if Linux Mint is providing their own /etc/dpkg/origins/mint and if they have properly updated the information. Again, feel free to open a bug report in their bugtracker suggesting this.

    • BTW, note that --derives-from $self always return true. It’s not a sign that “it is unique enough”. The absence of the Parent field is what represents this more accurately. But then it doesn’t matter much at the first level of derivatives because --derives-from Debian is of not much interest given that it should in theory almost always return true.

    • Just tried on a Maverick:

      $ dpkg-vendor --derives-from Debian && echo yes || echo no
      yes
      

      and

      $ grep Parent /etc/dpkg/origins/default
      Parent: Debian
      

      :)

  4. Mathieu Parent says:

    What about a patch which should only applied on Ubuntu (or Debian)?

    For an example, see https://bugs.launchpad.net/ubuntu/+source/kolab-webadmin/+bug/234860 about the smarty path which is different in Ubuntu.

    • Hi Mathieu, I’ll cover this in an upcoming article in more depth… but you can have different set of patches indeed: you can have debian/patches/ubuntu.series, start it as a copy of debian/patches/series and add a supplementary patch at the end.

      (BTW, I just moved all my sites to a new server, and Mathieu's comment came in the middle of the switch so that's why you got it twice, I entered it manually on the new server...)

  5. Rodney Dawes says:

    Your CDBS example actually needs to have the — before the -V as well, btw.