On Wed, 11 Dec 2019 at 15:30:43 +0000, Stuart Henderson wrote: > The go.port.mk part of this breaks some existing ports e.g. net/dnscontrol, > net/wireguard-go. >
Here is a diff against the current ports tree - wireguard-go and dnscontrol build fine. I also tested other "MODGO_TYPE={bin,lib}" ports. As a reminder - the `portgen go` stuff only works for Go projects that don't import libs that have mixed case module names. Anything that uses something like https://github.com/BurntSushi/toml will be unable to be "portgen"'d. diff --git a/infrastructure/bin/portgen b/infrastructure/bin/portgen index ad5ab17f3cf..b7316d42b64 100755 --- a/infrastructure/bin/portgen +++ b/infrastructure/bin/portgen @@ -32,6 +32,7 @@ use lib ( "$portdir/infrastructure/lib", "$FindBin::Bin/../lib" ); use OpenBSD::PortGen::Port::CPAN; use OpenBSD::PortGen::Port::PyPI; use OpenBSD::PortGen::Port::Ruby; +use OpenBSD::PortGen::Port::Go; my ( $type, $module ) = @ARGV; @@ -44,6 +45,8 @@ if ( $type eq 'p5' ) { $o = OpenBSD::PortGen::Port::PyPI->new(); } elsif ( $type eq 'ruby' ) { $o = OpenBSD::PortGen::Port::Ruby->new(); +} elsif ( $type eq 'go' ) { + $o = OpenBSD::PortGen::Port::Go->new(); } else { die "unknown module type\n"; } diff --git a/infrastructure/lib/OpenBSD/PortGen/Port/Go.pm b/infrastructure/lib/OpenBSD/PortGen/Port/Go.pm new file mode 100644 index 00000000000..8ef9280a506 --- /dev/null +++ b/infrastructure/lib/OpenBSD/PortGen/Port/Go.pm @@ -0,0 +1,225 @@ +# $OpenBSD: Go.pm,v 1.16 2019/05/16 16:01:10 afresh1 Exp $ +# +# Copyright (c) 2019 Aaron Bieber <abie...@openbsd.org> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package OpenBSD::PortGen::Port::Go; + +use 5.028; +use utf8; +use warnings; +use strict; +use warnings qw(FATAL utf8); # fatalize encoding glitches +use open qw(:std :encoding(UTF-8)); # undeclared streams in UTF-8 + +use parent 'OpenBSD::PortGen::Port'; + +use Carp; +use Cwd; +use File::Temp qw/ tempdir /; +use Data::Dumper; + +use OpenBSD::PortGen::Dependency; + +sub ecosystem_prefix +{ + my $self = shift; + return ''; +} + +sub base_url +{ + my $self = shift; + return 'https://proxy.golang.org/'; +} + +sub get_dist_info +{ + my ( $self, $module ) = @_; + + my $json = $self->get_json( $module . '/@latest' ); + $json->{Name} = ( split '/', $module )[-1]; + $json->{Module} = $module; + + my %mods; + for ( $self->_go_mod_graph($json) ) { + my ($direct, $ephemeral) = @{$_}; + + for my $d ( $direct, $ephemeral ) { + #next if $d->{Module} eq $module; + next unless $d->{Version}; + $mods{ $d->{Module} }{ $d->{Version} } ||= $d; + } + } + + # Filter dependencies to the one with the highest version + foreach my $mod ( sort keys %mods ) { + # Sort semver numerically then the rest alphanumeric. + # This is overkill for sorting, but I already wrote it + my @versions = + map { $_->[0] } + sort { + $a->[1] <=> $b->[1] + || $a->[2] <=> $b->[2] + || $a->[3] <=> $b->[3] + || $a->[4] cmp $b->[4] + } + map { $_->[1] =~ s/^v//; $_ } + map { [ $_, split /[\.-]/, $_, 4 ] } + keys %{ $mods{$mod} }; + + push @{ $json->{Dist} }, $mods{$mod}{ $versions[-1] }; + push @{ $json->{Mods} }, map { $mods{$mod}{$_} } @versions; + } + + carp Dumper $json; + + return $json; +} + +sub _go_mod_graph +{ + my ($self, $json) = @_; + my $dir = tempdir(CLEANUP => 1); + + my $mod = $self->get("$json->{Module}/\@v/$json->{Version}.mod"); + my ($module) = $mod =~ /\bmodule\s+(.*)\n/; + $module =~ s/\s+$//; + unless ( $json->{Module} eq $module ) { + my $msg = "Module $json->{Module} doesn't match $module"; + warn "$msg\n"; + croak $msg; + } + + { + open my $fh, '>', $dir . "/go.mod" or die $!; + print $fh $mod; + close $fh; + } + + my $old_cwd = getcwd(); + chdir $dir or die "Unable to chdir '$dir': $!"; + + my @mods; + { + # Outputs: "direct_dep ephemeral_dep" + local $ENV{GOPATH} = "$dir/go"; + open my $fh, '-|', qw< go mod graph > + or die "Unable to spawn 'go mod path': $!"; + @mods = readline $fh; + close $fh + or die "Error closing pipe to 'go mod path': $!"; + } + + chdir $old_cwd or die "Unable to chdir '$old_cwd': $!"; + + chomp @mods; + + # parse the graph into pairs of hashrefs + return map { [ + map { + my ($m, $v) = split /@/; + { Module => $m, Version => $v }; + } split /\s/ + ] } grep { $_ } @mods; +} + +sub get_ver_info +{ + my ( $self, $module ) = @_; + return $self->get_json( $module . '/@latest' ); +} + +sub name_new_port +{ + my ( $self, $di ) = @_; + + my $name = $di->{Name}; + $name = $self->SUPER::name_new_port($name); + $name = "go/$name" unless $name =~ m{/}; + + return $name; +} + +sub fill_in_makefile +{ + my ( $self, $di, $vi ) = @_; + + $self->set_modules('lang/go'); + $self->set_comment("todo"); + $self->set_descr("TODO"); + + # TODO: License information can be queried from: + # https://pkg.go.dev/${di->{MOdule}}?tab=licenses + $self->set_license("unknown license"); + + $self->set_other( MODGO_MODNAME => $di->{Module} ); + $self->set_other( MODGO_VERSION => $di->{Version} ); + $self->set_distname($di->{Name} . '-${MODGO_VERSION}'); + + my @parts = split("-", $di->{Version}); + if (@parts > 1) { + $self->set_pkgname($di->{Name} . "-" . $parts[1]) + if $parts[1] =~ m/\d{6}/; + } else { + $parts[0] =~ s/^v//; + $self->set_pkgname($di->{Name} . "-" . $parts[0]); + } + + my @dist = map { [ $_->{Module}, $_->{Version} ] } + @{ $di->{Dist} || [] }; + my @mods = map { [ $_->{Module}, $_->{Version} ] } + @{ $di->{Mods} }; + + # Turn the deps into tab separated columns + foreach my $s ( \@dist, \@mods ) { + next unless @{$s}; # if there aren't any, don't try + my ($length) = sort { $b <=> $a } map { length $_->[0] } @$s; + my $n = ( 1 + int $length / 8 ); + @{$s} = map { + ( my $m = $_->[0] ) =~ s/\p{Upper}/!\L$&/g; + $m =~ s/[^\w\/\.]/sprintf "%%%02x", ord $&/ge; + my $tabs = "\t" x ( $n - int( length($m) / 8 ) ); + "$m$tabs $_->[1]" + } @{$s}; + } + + carp Dumper \@mods; + $self->set_other( MODGO_MODULES => \@dist ) if @dist; + $self->set_other( MODGO_MODFILES => \@mods ) if @mods; +} + +sub try_building +{ + my $self = shift; + $self->make_fake(); +} + +sub postextract +{ +} + +sub get_deps +{ + my ( $self, $di, $wrksrc ) = @_; + my $deps = OpenBSD::PortGen::Dependency->new(); + + return $deps->format; +} + +sub get_config_style +{ +} + +1; diff --git a/infrastructure/templates/Makefile.template b/infrastructure/templates/Makefile.template index 6be8b86b3ea..2ebf2d37880 100644 --- a/infrastructure/templates/Makefile.template +++ b/infrastructure/templates/Makefile.template @@ -22,7 +22,13 @@ COMMENT = ??? # #MODPY_EGG_VERSION = ??? +# MODGO_MODNAME should be set to the 'module' specified in the 'go.mod' file. +#MODGO_MODNAME = github.com/test/app # +# Version of port if using lang/go and MODGO_MODULES +# +#MODGO_VERSION = 0.1.1 + # What port/package will be created # # DISTNAME should not include suffix (like .tar.gz .tgz .tar.bz2 etc.) @@ -125,6 +131,15 @@ MASTER_SITES = ??? # If port is python3 only #MODPY_VERSION = ${MODPY_DEFAULT_VERSION_3} +# +# MODGO_ settings for when using lang/go module +# +# Get source from proxy.golang.org +#MODGO_MODULES = modulename version +# These are needed for dependency resolution. We don't actually need the +# coresponding code +#MODGO_MODFILES = modulename version + # Dependencies #BUILD_DEPENDS = ??? #RUN_DEPENDS = ??? diff --git a/lang/go/go.port.mk b/lang/go/go.port.mk index 983c3990706..908b3f2426c 100644 --- a/lang/go/go.port.mk +++ b/lang/go/go.port.mk @@ -4,6 +4,13 @@ ONLY_FOR_ARCHS ?= ${GO_ARCHS} MODGO_BUILDDEP ?= Yes +MODGO_DIST_SUBDIR ?= go_modules + +MASTER_SITE_ATHENS = https://proxy.golang.org/ + +MODGO_MASTER_SITESN = 9 +MASTER_SITES${MODGO_MASTER_SITESN} ?= ${MASTER_SITE_ATHENS} + MODGO_RUN_DEPENDS = lang/go MODGO_BUILD_DEPENDS = lang/go @@ -33,17 +40,12 @@ MODGO_TYPE ?= bin MODGO_WORKSPACE ?= ${WRKDIR}/go MODGO_GOCACHE ?= ${WRKDIR}/go-cache MODGO_GOPATH ?= ${MODGO_WORKSPACE}:${MODGO_PACKAGE_PATH} -MAKE_ENV += GOCACHE="${MODGO_GOCACHE}" \ - GOPATH="${MODGO_GOPATH}" \ - GO111MODULE=off # We cannot assume that the maching running the built code will have SSE, # even though the machine building the package has SSE. As such, we need # to explicitly disable SSE on i386 builds. MAKE_ENV += GO386=387 -# Ports are not allowed to fetch from the network at build time; point -# GOPROXY at an unreachable host so that failures are also visible to -# developers who don't have PORTS_PRIVSEP and a "deny .. _pbuild" PF rule. -MAKE_ENV += GOPROXY=invalid://ports.should.not.fetch.at.buildtime/ +MAKE_ENV += GOCACHE="${MODGO_GOCACHE}" + MODGO_CMD ?= ${SETENV} ${MAKE_ENV} go MODGO_BUILD_CMD = ${MODGO_CMD} install ${MODGO_FLAGS} MODGO_TEST_CMD = ${MODGO_CMD} test ${MODGO_FLAGS} ${MODGO_TEST_FLAGS} @@ -54,15 +56,34 @@ MODGO_BUILD_CMD += -ldflags="${MODGO_LDFLAGS}" MODGO_TEST_CMD += -ldflags="${MODGO_LDFLAGS}" .endif -.if defined(GH_ACCOUNT) && defined(GH_PROJECT) -ALL_TARGET ?= github.com/${GH_ACCOUNT}/${GH_PROJECT} +.if defined(MODGO_MODNAME) +EXTRACT_SUFX ?= .zip +PKGNAME ?= ${DISTNAME:S/-v/-/} +ALL_TARGET ?= ${MODGO_MODNAME} +DISTFILES = ${DISTNAME}${EXTRACT_SUFX}{${MODGO_VERSION}${EXTRACT_SUFX}} +MASTER_SITES ?= ${MASTER_SITE_ATHENS}${MODGO_MODNAME}/@v/ +. for _modname _modver in ${MODGO_MODULES} +SUPDISTFILES += ${MODGO_DIST_SUBDIR}/${_modname}/@v/${_modver}.zip{${_modname}/@v/${_modver}.zip}:${MODGO_MASTER_SITESN} +. endfor +. for _modname _modver in ${MODGO_MODFILES} +SUPDISTFILES += ${MODGO_DIST_SUBDIR}/${_modname}/@v/${_modver}.mod{${_modname}/@v/${_modver}.mod}:${MODGO_MASTER_SITESN} +. endfor +MAKE_ENV += GOPROXY=file://${DISTDIR}/${MODGO_DIST_SUBDIR} +MAKE_ENV += GO111MODULE=on GOPATH="${MODGO_GOPATH}" +.else +# ports are not allowed to fetch from the network at build time; point +# GOPROXY at an unreachable host so that failures are also visible to +# developers who don't have PORTS_PRIVSEP and a "deny .. _pbuild" PF rule. +MAKE_ENV += GOPROXY=invalid://ports.should.not.fetch.at.buildtime/ +MAKE_ENV += GO111MODULE=off GOPATH="${MODGO_GOPATH}" +. if defined(GH_ACCOUNT) && defined(GH_PROJECT) +ALL_TARGET ?= github.com/${GH_ACCOUNT}/${GH_PROJECT} +. endif .endif -TEST_TARGET ?= ${ALL_TARGET} -SEPARATE_BUILD ?= Yes -WRKSRC ?= ${MODGO_WORKSPACE}/src/${ALL_TARGET} +MODGO_TEST_TARGET ?= cd ${WRKSRC} && ${MODGO_CMD} test ${ALL_TARGET} -MODGO_SETUP_WORKSPACE = mkdir -p ${WRKSRC:H}; mv ${MODGO_SUBDIR} ${WRKSRC}; +SEPARATE_BUILD ?= Yes CATEGORIES += lang/go @@ -76,6 +97,14 @@ MODGO_LDFLAGS += -s -w MODGO_FLAGS += -x .endif +.if empty(MODGO_MODNAME) +WRKSRC ?= ${MODGO_WORKSPACE}/src/${ALL_TARGET} +MODGO_SETUP_WORKSPACE = mkdir -p ${WRKSRC:H}; mv ${MODGO_SUBDIR} ${WRKSRC}; +.else +WRKSRC ?= ${WRKDIR}/${MODGO_MODNAME}@${MODGO_VERSION} +MODGO_SETUP_WORKSPACE = ln -sf ${WRKSRC} ${WRKDIR}/${MODGO_MODNAME} +.endif + INSTALL_STRIP = .if ${MODGO_TYPE:L:Mbin} MODGO_INSTALL_TARGET = ${INSTALL_PROGRAM_DIR} ${PREFIX}/${MODGO_BINDIR} && \ @@ -99,14 +128,13 @@ MODGO_INSTALL_TARGET += ${INSTALL_DATA_DIR} ${MODGO_PACKAGE_PATH} && \ RUN_DEPENDS += ${MODGO_RUN_DEPENDS} .endif -MODGO_TEST_TARGET = ${MODGO_TEST_CMD} ${TEST_TARGET} - .if empty(CONFIGURE_STYLE) MODGO_pre-configure += ${MODGO_SETUP_WORKSPACE} . if !target(do-build) do-build: - ${MODGO_BUILD_TARGET} + cd ${WRKSRC} && \ + ${MODGO_BUILD_TARGET} . endif . if !target(do-install) -- PGP: 0x1F81112D62A9ADCE / 3586 3350 BFEA C101 DB1A 4AF0 1F81 112D 62A9 ADCE