Hi,

Some of you have already seen and commented on my proposal on how to
change dh-golang to build and link against Go shared libraries, coming
(on amd64 at least) in Go 1.5:
https://docs.google.com/document/d/1IOlBWWgcDeB9PfRORENESYj8iJt4W2EwsbYcpg4akBE/edit#heading=h.j590j9v4qbxg.
I've just made some small edits to it around the name of the shared
objects that are built.

I'm attaching two patches that implement the scheme described in the
doc. I've not tested them super extensively but they do seem to work
(with a go package from my branch at
https://github.com/mwhudson/go/tree/ubuntu-vivid, ignore the branch
name, it builds fine on sid).

I'd be most interested in any comments anyone has.

Cheers,
mwh
From 98204f09f1f97e960cdaaad20c334943aac76257 Mon Sep 17 00:00:00 2001
From: Michael Hudson-Doyle <[email protected]>
Date: Fri, 12 Jun 2015 14:42:48 +1200
Subject: [PATCH 2/2] Add dh_makegolangshlibs

This makes sure that go packages depend on the ABI-stamped provides fields.
---
 Makefile.PL                             |  1 +
 lib/Debian/Debhelper/Sequence/golang.pm |  2 +
 script/dh_makegolangshlibs              | 87 +++++++++++++++++++++++++++++++++
 3 files changed, 90 insertions(+)
 create mode 100755 script/dh_makegolangshlibs

diff --git a/Makefile.PL b/Makefile.PL
index 07e86d6..ebf14c7 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -4,6 +4,7 @@ name 'dh-golang';
 version '1.0';
 
 install_script 'dh_golang';
+install_script 'dh_makegolangshlibs';
 
 #postamble <<'END_OF_MAKEFILE';
 #install:: extra_install
diff --git a/lib/Debian/Debhelper/Sequence/golang.pm b/lib/Debian/Debhelper/Sequence/golang.pm
index 2307c09..82058d6 100644
--- a/lib/Debian/Debhelper/Sequence/golang.pm
+++ b/lib/Debian/Debhelper/Sequence/golang.pm
@@ -4,6 +4,8 @@ use strict;
 use Debian::Debhelper::Dh_Lib;
 
 insert_before('dh_gencontrol', 'dh_golang');
+insert_before('dh_makeshlibs', 'dh_makegolangshlibs');
+remove_command('dh_makeshlibs');
 
 # XXX: -u is deprecated, but we cannot use “-- -Zxz” because additional command
 # options will be appended (“-O--buildsystem=golang”), resulting in
diff --git a/script/dh_makegolangshlibs b/script/dh_makegolangshlibs
new file mode 100755
index 0000000..479c854
--- /dev/null
+++ b/script/dh_makegolangshlibs
@@ -0,0 +1,87 @@
+#!/usr/bin/perl -w
+
+=head1 NAME
+
+dh_makegolangshlibs - Generates golang-specific shlibs files and substvars
+
+=cut
+
+use strict;
+use Debian::Debhelper::Dh_Lib;
+use File::Find;
+use MIME::Base64;
+
+=head1 SYNOPSIS
+
+B<dh_makegolangshlibs> [S<I<debhelper options>>]
+
+=head1 DESCRIPTION
+
+B<dh_makegolangshlibs> is a debhelper program which generates a
+substvar and shlibs file to enable packages depending on go shared
+libraries to conveniently depend on the ABI hash of the library rather
+than just the soversion.  It replaces dh_makeshlibs in the usual dh
+sequences.
+
+=head1 NOTES
+
+The best way to invoke B<dh_makegolangshlibs> is by using B<dh --with=golang>.
+
+=cut
+
+init();
+
+############################################################################
+# Generate ...
+############################################################################
+
+my ($libpkg, $devpkg, $libname, $sover);
+
+foreach my $pkg (@{$dh{DOPACKAGES}}) {
+    if ($pkg =~ /^lib(golang.*?)-?([0-9]+)$/) {
+        $libpkg = $pkg;
+        $libname = $1;
+        $sover = $2;
+    }
+    if ($pkg =~ /-dev$/) {
+        $devpkg = $pkg;
+    }
+}
+
+if (!defined($libpkg)) {
+    exit(0);
+} elsif (!defined($devpkg)) {
+    printf("found lib package but not dev");
+    exit(1);
+}
+
+my $sofile = sprintf(
+    "%s/usr/lib/%s/lib%s.so.%s",
+    tmpdir($libpkg), dpkg_architecture_value("DEB_HOST_MULTIARCH"),
+    $libname, $sover);
+
+my $hexhash=`/usr/lib/go/readabihash $sofile`;
+chomp($hexhash);
+
+my $libpkgdir=tmpdir($libpkg);
+if (! -d "$libpkgdir/DEBIAN") {
+    doit("install","-d","$libpkgdir/DEBIAN");
+}
+my $provides = "$libpkg-$hexhash";
+addsubstvar($libpkg, "golang:Provides", $provides);
+complex_doit("echo 'lib$libname $sover $provides' >>$libpkgdir/DEBIAN/shlibs");
+autoscript($libpkg,"postinst","postinst-makeshlibs");
+autoscript($libpkg,"postrm","postrm-makeshlibs");
+
+
+=head1 SEE ALSO
+
+dh(1)
+
+=head1 AUTHORS
+
+Michael Hudson-Doyle <[email protected]>
+
+=cut
+
+# vim:ts=4:sw=4:et
-- 
2.1.4

From f6209b6d256f710c38944cb03c2207cf84d819bc Mon Sep 17 00:00:00 2001
From: Michael Hudson-Doyle <[email protected]>
Date: Fri, 12 Jun 2015 13:13:47 +1200
Subject: [PATCH 1/2] Shared library support

Activated by putting a package with name matching /^lib(golang.*?)-?([0-9]+)$/
into d/control.
---
 debian/changelog                           |   6 +
 lib/Debian/Debhelper/Buildsystem/golang.pm | 218 ++++++++++++++++++++++++++---
 2 files changed, 201 insertions(+), 23 deletions(-)

diff --git a/debian/changelog b/debian/changelog
index 538bd5b..9cb6031 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+dh-golang (1.6ubuntu1) UNRELEASED; urgency=medium
+
+  * Shared libs 
+
+ -- Michael Stapelberg <[email protected]>  Fri, 12 Jun 2015 00:33:45 +0000
+
 dh-golang (1.6) unstable; urgency=low
 
   [ Michael Stapelberg ]
diff --git a/lib/Debian/Debhelper/Buildsystem/golang.pm b/lib/Debian/Debhelper/Buildsystem/golang.pm
index 49c19be..799cf79 100644
--- a/lib/Debian/Debhelper/Buildsystem/golang.pm
+++ b/lib/Debian/Debhelper/Buildsystem/golang.pm
@@ -2,7 +2,7 @@ package Debian::Debhelper::Buildsystem::golang;
 
 use strict;
 use base 'Debian::Debhelper::Buildsystem';
-use Debian::Debhelper::Dh_Lib; 
+use Debian::Debhelper::Dh_Lib;
 use File::Copy; # in core since 5.002
 use File::Path qw(make_path); # in core since 5.001
 use File::Find; # in core since 5
@@ -19,9 +19,57 @@ sub new {
     my $class = shift;
     my $this = $class->SUPER::new(@_);
     $this->prefer_out_of_source_building();
+    $this->read_shlibconfig();
     return $this;
 }
 
+sub read_shlibconfig {
+    my $this = shift;
+    open (CONTROL, 'debian/control') ||
+        error("cannot read debian/control: $!\n");
+
+    my $config = {linkshared => !!$ENV{DH_GOLANG_LINK_SHARED}};
+
+    while (<CONTROL>) {
+        chomp;
+        s/\s+$//;
+        if (/^Package:\s*(.*)/) {
+            my $pkg=$1;
+            if ($pkg =~ /^lib(golang.*?)-?([0-9]+)$/) {
+                $config->{libpkg} = $pkg;
+                $config->{libname} = $1;
+                $config->{sover} = $2;
+                $config->{linkshared} = 1;
+            } elsif ($pkg =~ /-dev$/) {
+                $config->{devpkg} = $pkg;
+            }
+        }
+    }
+
+    # XXX Could do some consistency checks here e.g
+    # 1) make sure there is a devpkg if there is a libpkg
+    # 2) make sure devpkg depends on libpkg
+    # 3) make sure libpkg has "Provides: ${golang:Provides}"
+    # Maybe this is more in the realm of lintian.
+
+    $config->{soname} = sprintf("lib%s.so.%s", $config->{libname}, $config->{sover});
+
+    close CONTROL;
+
+    $this->{shlibconfig} = $config;
+}
+
+
+sub set_GOPATH {
+    my $this = shift;
+
+    if ($this->{shlibconfig}->{linkshared}) {
+        $ENV{GOPATH} = $this->{cwd} . '/' . $this->get_builddir() . ':' . $this->{cwd} . '/' . $this->get_builddir() . '/shlibdeps' . ':' . $this->{cwd} . '/' . $this->get_builddir() . '/srcdeps';
+    } else {
+        $ENV{GOPATH} = $this->{cwd} . '/' . $this->get_builddir()
+    }
+}
+
 sub _link_contents {
     my ($src, $dst) = @_;
 
@@ -93,26 +141,50 @@ sub configure {
     # buildroot.
     ############################################################################
 
-    # NB: The naïve idea of just setting GOPATH=$builddir:/usr/share/godoc does
-    # not work. Let’s call the two paths in $GOPATH components. go(1), when
-    # installing a package, such as github.com/Debian/dcs/cmd/..., will also
-    # install the compiled dependencies, e.g. github.com/mstap/godebiancontrol.
-    # When such a dependency is found in a component’s src/ directory, the
-    # resulting files will be stored in the same component’s pkg/ directory.
-    # That is, in this example, go(1) wants to modify
-    # /usr/share/gocode/pkg/linux_amd64/github.com/mstap/godebiancontrol, which
-    # will obviously not succeed due to permission errors.
-    #
-    # Therefore, we just work with a single component that is under our control
-    # and symlink all the sources into that component ($builddir).
-
-    _link_contents('/usr/share/gocode/src', "$builddir/src");
+    if ($this->{shlibconfig}->{linkshared}) {
+        # When building or linking against shared libraries, we must make available any
+        # shared libraries that are already on the system. GOPATH is set up to have three
+        # components: $builddir (for the package we are building), $builddir/shlibdeps
+        # (for packages that are built into shared libraries that have already been
+        # installed) and $builddir/srcdeps (for dependencies that have only been installed
+        # as source). The shlibdeps component is deliberately set up so that the build
+        # can't write to it.
+        my $installed_shlib_data_dir = "/usr/lib/" . dpkg_architecture_value("DEB_HOST_MULTIARCH") . "/gocode";
+        if (-d $installed_shlib_data_dir) {
+            make_path("$builddir/shlibdeps/pkg");
+            # XXX Why can't we just symlink builddir/shlibdeps to $installed_shlib_data_dir?
+            # I forget, but there must be some reason which I should try and explain... (I
+            # think maybe putting symlinks on GOPATH doesn't work?)
+            $this->doit_in_builddir("ln", "-sT", "$installed_shlib_data_dir/src", "shlibdeps/src");
+            complex_doit("ln", "-s", "$installed_shlib_data_dir/pkg/*_dynlink", "$builddir/shlibdeps/pkg");
+        }
+        make_path("$builddir/srcdeps/src");
+        _link_contents('/usr/share/gocode/src', "$builddir/srcdeps/src");
+    } else {
+        # NB: The naïve idea of just setting GOPATH=$builddir:/usr/share/gocode does
+        # not work. Let’s call the two paths in $GOPATH components. go(1), when
+        # installing a package, such as github.com/Debian/dcs/cmd/..., will also
+        # install the compiled dependencies, e.g. github.com/mstap/godebiancontrol.
+        # When such a dependency is found in a component’s src/ directory, the
+        # resulting files will be stored in the same component’s pkg/ directory.
+        # That is, in this example, go(1) wants to modify
+        # /usr/share/gocode/pkg/linux_amd64/github.com/mstap/godebiancontrol, which
+        # will obviously not succeed due to permission errors.
+        #
+        # Therefore, we just work with a single component that is under our control
+        # and symlink all the sources into that component ($builddir).
+        _link_contents('/usr/share/gocode/src', "$builddir/src");
+    }
 }
 
 sub get_targets {
     my $buildpkg = $ENV{DH_GOLANG_BUILDPKG} || "$ENV{DH_GOPKG}/...";
-    my $output = qx(go list $buildpkg);
     my @excludes = split(/ /, $ENV{DH_GOLANG_EXCLUDES});
+    # XXX Why do this?
+    if (!@excludes) {
+        return ($buildpkg)
+    }
+    my $output = qx(go list $buildpkg);
     my @targets = split(/\n/, $output);
 
     # Remove all targets that are matched by one of the regular expressions in DH_GOLANG_EXCLUDES.
@@ -123,17 +195,70 @@ sub get_targets {
     return @targets;
 }
 
+sub buildX {
+    if ($dh{VERBOSE}) {
+        return ("-x");
+    } else {
+        return ();
+    }
+}
+
+# Return where the go tool thinks the shlib for our targets is.
+sub go_shlib_path {
+    my @targets = get_targets();
+    # other things will blow up if not every target has the same
+    # PkgTargetRoot so let's not worry about that here.
+    my @shlib = qx(go list -linkshared -f '{{ .Shlib }}' @targets);
+    if (@shlib) {
+        my $line = $shlib[0];
+        chomp($line);
+        return $line;
+    }
+}
+
+sub build_shlib {
+    my $this = shift;
+    my $config = $this->{shlibconfig};
+
+    my $ldflags = "-r '' -extldflags=-Wl,-soname=" . $config->{soname};
+
+    my @targets = get_targets();
+
+    $this->doit_in_builddir(
+        "go", "install", "-v", buildX(), "-ldflags", $ldflags,
+        "-buildmode=shared", "-linkshared", @targets);
+
+    my $shlib = go_shlib_path();
+    my $dsodir = dirname($shlib);
+
+    $this->doit_in_builddir("mv", "$shlib", "$dsodir/" . $config->{soname});
+    $this->doit_in_builddir("ln", "-s", $config->{soname}, "$shlib");
+
+    $this->doit_in_builddir(
+        "go", "install", "-v", buildX(), "-ldflags", "-r ''",
+        "-buildmode=exe", "-linkshared", @targets);
+}
+
 sub build {
     my $this = shift;
 
-    $ENV{GOPATH} = $this->{cwd} . '/' . $this->get_builddir();
-    $this->doit_in_builddir("go", "install", "-v", @_, get_targets());
+    $this->set_GOPATH();
+
+    if ($this->{shlibconfig}->{libpkg}) {
+        $this->build_shlib();
+    } elsif ($this->{shlibconfig}->{linkshared}) {
+        $this->doit_in_builddir(
+            "go", "install", buildX(), "-v", "-ldflags=-r ''", "-linkshared", @_, get_targets());
+    } else {
+        $this->doit_in_builddir("go", "install", "-v", @_, get_targets());
+    }
 }
 
 sub test {
     my $this = shift;
 
-    $ENV{GOPATH} = $this->{cwd} . '/' . $this->get_builddir();
+    $this->set_GOPATH();
+
     $this->doit_in_builddir("go", "test", "-v", @_, get_targets());
 }
 
@@ -142,16 +267,63 @@ sub install {
     my $destdir = shift;
     my $builddir = $this->get_builddir();
 
+    $this->set_GOPATH();
+
     my @binaries = <$builddir/bin/*>;
     if (@binaries > 0) {
         $this->doit_in_builddir('mkdir', '-p', "$destdir/usr");
         $this->doit_in_builddir('cp', '-r', 'bin', "$destdir/usr");
     }
 
-    # Path to the src/ directory within $destdir
-    my $dest_src = "$destdir/usr/share/gocode/src/$ENV{DH_GOPKG}";
-    $this->doit_in_builddir('mkdir', '-p', $dest_src);
-    $this->doit_in_builddir('cp', '-r', '-T', "src/$ENV{DH_GOPKG}", $dest_src);
+    if ($this->{shlibconfig}->{libpkg}) {
+        my $config = $this->{shlibconfig};
+        # Here we are shuffling files about for two packages:
+        # 1) The lib package, which just contains usr/lib/$triplet/$soname
+        # 2) The dev package, which contains:
+        #    a) the source at usr/share/gocode/src/$ENV{DH_GOPKG}
+        #    b) the .a files at usr/lib/$triplet/gocode/pkg/*_dynlink/$ENV{DH_GOPKG}
+        #    c) the .so symlink at usr/lib/$triplet/gocode/pkg/*_dynlink/lib${foo}.so to the lib as
+        #       installed by 1) (${foo} is determined by the go tool and we do not make
+        #       any assumptions about it here)
+        #    d) a symlink from usr/lib/$triplet/gocode/src/$ENV{DH_GOPKG} to
+        #       usr/share/gocode/src/$ENV{DH_GOPKG}
+        my $solink = go_shlib_path();
+
+        # lib package
+        my $shlib = dirname($solink) . "/" . $config->{soname};
+        my $final_shlib_dir = "/usr/lib/" . dpkg_architecture_value("DEB_HOST_MULTIARCH");
+        my $shlibdest = tmpdir($config->{libpkg}) . $final_shlib_dir;
+        doit('mkdir', '-p', $shlibdest);
+        doit('mv', $shlib, $shlibdest);
+
+        # dev package
+        # a) source
+        my $dest_src = tmpdir($config->{devpkg}) . "/usr/share/gocode/src/$ENV{DH_GOPKG}";
+        doit('mkdir', '-p', $dest_src);
+        doit('cp', '-r', '-T', "$builddir/src/$ENV{DH_GOPKG}", $dest_src);
+
+        my $dest_lib_prefix = tmpdir($config->{devpkg}) . $final_shlib_dir . "/gocode/";
+        my $goos_goarch_dynlink = basename((<$builddir/pkg/*_dynlink>)[0]);
+
+        # b) .a files (this copies the symlink too but that will get overwritten in the next step)
+        my $dest_pkg = $dest_lib_prefix . "pkg/$goos_goarch_dynlink";
+        doit('mkdir', '-p', $dest_pkg);
+        doit('cp', '-r', '-T', "$builddir/pkg/$goos_goarch_dynlink", $dest_pkg);
+
+        # c) .so symlink
+        my $dest_solink = $dest_lib_prefix . "pkg/$goos_goarch_dynlink/" . basename($solink);
+        doit('ln', '-s', '-f', '-T', $final_shlib_dir . "/" . $config->{soname}, $dest_solink);
+
+        # d) src symlink
+        my $dest_srclink = $dest_lib_prefix . "src/$ENV{DH_GOPKG}";
+        doit('mkdir', '-p', dirname($dest_srclink));
+        doit('ln', '-s', '-T', "/usr/share/gocode/src/$ENV{DH_GOPKG}", $dest_srclink);
+    } else {
+        # Path to the src/ directory within $destdir
+        my $dest_src = "$destdir/usr/share/gocode/src/$ENV{DH_GOPKG}";
+        $this->doit_in_builddir('mkdir', '-p', $dest_src);
+        $this->doit_in_builddir('cp', '-r', '-T', "src/$ENV{DH_GOPKG}", $dest_src);
+    }
 }
 
 sub clean {
-- 
2.1.4

_______________________________________________
Pkg-go-maintainers mailing list
[email protected]
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-go-maintainers

Reply via email to