[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Go (golang) packaging, part 2



Neil Williams <codehelp@debian.org> writes:
> Matthew Woodcraft <matthew@woodcraft.me.uk> wrote:

>> I don't think it's as clear-cut as that.

>> Debian handles multiple versions of C libraries at _runtime_ well, but
>> I think its support for C libraries still leaves a good deal to be
>> desired: it doesn't let you install multiple versions of -dev packages,

It can if upstream versions the headers.  A lot of upstreams don't do
this, but some do, and C provides a perfectly reasonable facility for
doing so (add the API version to the include paths).  Many common C
libraries, from Glib to APR, use this facility.

You certainly *can* break versioning in C, but my point is rather that C
provides all the facilities that you need to do this and they are in
reasonably common use in the C community.  (It's common not to bother to
version APIs because it's usually reasonably easy to maintain API
compatibility if you care, since you can add new fields to structs and add
new interfaces without changing the API even if the ABI changes.)

ABI versioning is much more important than API versioning because ABI
versioning affects end users who just want to install packages and have
them work, whereas API versioning only affects developers who by
definition have access to the source and can probably update things.

One of the challenges of interpreted (or JIT-compiled) languages like Perl
and Python is that the API and the ABI are the same thing, which means
that you have to version the API in order to version the ABI.  This is
unavoidable given the nature of the language, but is actually quite
awkward, since versioning the API generally requires that the source code
of all callers be aware of the versioning and update to use the new
version.  With C libraries with ABI versioning but no API versioning (the
most common case), one often doesn't need to make any changes at all to
the caller to move to a newer version of the ABI.

> More often than not, there is simply no need to have libfoo1-dev and
> libfoo2-dev - libfoo2 & it's libfoo-dev just live in experimental whilst
> the reverse deps migrate to the new API.

Right.  Plus, if upstream isn't versioning the API, the -dev packages
won't be coinstallable, and thus are somewhat pointless.

I could see how one could go about supporting something like this in a
language like Perl or Python.  It would require introducing the concept of
an API version at the language level, and the caller would be required to
declare the API version at the time it loads the module.  (Experience in
the REST world, specifically with the Facebook API, says that allowing the
caller to omit the API version and providing some default is a bad idea
and produces fragile clients.)  So, to take a Perl example (since that's
what I'm most familiar with), the current:

    use Date::Parse 2.20;

where the number is the minimum required version of Date::Parse (which is
kind of useless since it doesn't set a maximum version, and even with
support for setting a maximum version is annoying because it requires the
caller know release versions instead of API versions), one would instead
have something like:

    use_api Date::Parse 2;

which would load version 2 of the Date::Parse API.

Then, the module would declare what API versions it supports, and the
installation paths would have to be restructured so that modules providing
different versions would be co-installable.  One way backward-compatible
way to do that in Perl would be to install Date::Parse as both:

    Date/Parse/_API_2.pm
    Date/Parse.pm

Old Perls would use the second path.  New Perls, given the above
declaration, would only load the first path and would throw an error if it
didn't exist.  After a transition, or with an install option, you could
drop the second path, and then you could co-install two versions of
Date::Parse that provided different APIs because the next API version
would install:

    Date/Parse/_API_3.pm

and clients that did:

    use_api Date::Parse 3;

would get that one instead.  Note that this would also allow a single
source package to provide multiple versions of the API by just installing
multiple _API_n.pm modules.

This is all quite ugly, and is mostly just a thought experiment.  I'm sure
one could devise better schemes.  What surprises me is that no one has
done this in any of the newer languages.  (You really do want support for
this built into the language, since you want a bunch of help constructing
the install paths, supporting the automatic logic behind use_api, and
ideally you want the language to be able to figure out whether you changed
APIs at least enough to warn you about it.)

-- 
Russ Allbery (rra@debian.org)               <http://www.eyrie.org/~eagle/>


Reply to: