Last active
December 28, 2015 14:49
-
-
Save afresh1/7517222 to your computer and use it in GitHub Desktop.
A script that will create updated "starter" Makefiles for OpenBSD perl ports available on the CPAN. Should only require things in the base system, but does like an up-to-date /usr/ports path.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl | |
use strict; | |
use warnings; | |
# Copyright (c) 2013 Andrew Fresh <andrew@afresh1.com> | |
# | |
# 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. | |
use 5.010; | |
use Carp; | |
use Cwd; | |
use File::Path qw( make_path ); | |
use JSON::PP qw( decode_json ); | |
use constant ( | |
TABLENGTH => 8, | |
); | |
my $base_dir = '/tmp/generated_ports'; | |
my $base_url = 'http://api.metacpan.org/v0'; | |
my @get_cmd = qw( lynx --dump ); | |
my $makefile_template = '/usr/ports/infrastructure/templates/Makefile.template'; | |
my @distributions = @ARGV; | |
# A useful command: | |
# diff -Pbru /usr/ports/ . | grep -v 'Only in' | less | |
foreach my $distribution (@distributions) { | |
state %seen; | |
next if $seen{$distribution}++; | |
say "Making $distribution"; | |
my $dist_info = get_dist_info($distribution); | |
next if dist_is_up_to_date($dist_info); | |
make_port($dist_info); | |
} | |
## no critic 'subroutine prototypes' | |
sub uniq(@) { my %seen; return grep { !$seen{$_}++ } @_; } | |
sub make_port { | |
my ($di) = @_; | |
my $old_cwd = getcwd(); | |
my $dir = make_portdir($di); | |
chdir $dir or croak "Couldn't chdir $dir: $!"; | |
make_makefile($di); | |
make_descr( $di ) if $dir =~ m{/CPAN/}x; # Only works most of the time | |
chdir $old_cwd or croak "Couldn't chdir $old_cwd: $!"; | |
} | |
sub find_portdir { return "$base_dir/" . port_for_dist(@_) } | |
sub make_portdir { | |
my ($di) = @_; | |
my $port = port_for_dist($di); | |
my $port_dir = find_portdir($di); | |
make_path($port_dir) or die "Couldn't make_path $port_dir: $!" | |
unless -e $port_dir; | |
if (-e "/usr/ports/$port") { | |
(my $dst = $port_dir) =~ s{/[^/]+$}{}; | |
cp('-r', "/usr/ports/$port/", $dst); | |
rename( "$port_dir/Makefile", "$port_dir/Makefile.orig" ); | |
rename( "$port_dir/pkg/PLIST", "$port_dir/pkg/PLIST.orig" ); | |
unlink( "$port_dir/distinfo" ); | |
} | |
return $port_dir; | |
} | |
sub parse_makefile { | |
my ($path) = @_; | |
return unless -e $path; | |
my @makefile; | |
my %vars; | |
my $parse = sub { | |
state $line = ''; | |
$line .= shift; | |
return if /\\\n$/x; | |
if ($line =~ /^(\#?) \s* ([A-Z_]+) \s* = (\s*) (.*)/xms) { | |
my ($comment, $key, $spaces, $value) = ($1, $2, $3, $4); | |
my $tabs = $spaces =~ tr/\t/\t/; | |
push @makefile, { | |
key => $key, | |
value => $value, | |
tabs => $tabs, | |
commented => $comment ? 1 : 0, | |
}; | |
$vars{$key} = $value; | |
} | |
else { | |
push @makefile, $line; | |
} | |
$line = ''; | |
}; | |
open my $fh, '<', $path or croak("Couldn't open $path: $!"); | |
$parse->($_) while <$fh>; | |
close $fh; | |
return { | |
makefile => \@makefile, | |
vars => \%vars, | |
} | |
} | |
sub make_makefile { | |
my ($di) = @_; | |
my $port = port_for_dist($di); | |
my ($category) = split m{/}x, $port; | |
my $old_port = parse_makefile("Makefile.orig") || {}; | |
my @makefile = @{ $old_port->{makefile} || [] }; | |
@makefile = ( | |
'# $OpenBSD$' . "\n", | |
grep { $_ !~ /^\#/x } | |
@{ parse_makefile($makefile_template)->{makefile} } | |
) unless @makefile; | |
my %configs = ( | |
COMMENT => "$di->{abstract}\n", | |
MODULES => "cpan\n", | |
CATEGORIES => "$category\n", | |
MAINTAINER => "ports\@openbsd.org\n", | |
CPAN_AUTHOR => "$di->{author}\n", | |
%{ $old_port->{vars} || {} }, | |
); | |
# foreach my $config ( keys %configs ) { | |
# delete $configs{$config} if $configs{$config} =~ /\?\?\?/; | |
# my $value = port_value( $di, $config ); | |
# $configs{$config} = "$value\n" if $value; | |
# } | |
delete $configs{HOMEPAGE} | |
if $configs{HOMEPAGE} | |
&& $configs{HOMEPAGE} =~ /search\.cpan\.org/x; | |
# Picked up from elsewhere | |
$configs{MODULES} =~ s/ perl// if $configs{MODULES}; | |
$configs{CATEGORIES} =~ s/ perl5// if $configs{CATEGORIES}; | |
$configs{MAINTAINER} = "Andrew Fresh <andrew\@cpan.org>\n" | |
if !$configs{MAINTAINER} | |
|| $configs{MAINTAINER} =~ /ports\@openbsd.org/x; | |
my $distname = $di->{archive}; | |
if ( $distname =~ s/\.tar\.gz$//x ) { | |
# do nothing | |
# Eventually, if we strip other types | |
# It could become an EXTRACT_SUFX | |
} | |
$configs{DISTNAME} = $distname . "\n"; | |
makefile_depends( $di, \%configs ); | |
my $license = join ' ', | |
ref $di->{license} ? @{ $di->{license} || [] } : ( $di->{license} ); | |
$configs{PERMIT_PACKAGE_CDROM} = "Yes\n"; | |
open my $fh, '>', 'Makefile' or die "Couldn't open Makefile: $!"; | |
my $last_blank; | |
foreach (@makefile) { | |
my $is_blank = /^[\s\n]*$/xms; | |
next if $is_blank && $last_blank; | |
if (/\.include \s+ <bsd.port.mk>/x) { | |
foreach my $key (sort keys %configs) { | |
my $value = $configs{$key}; | |
next unless $value; | |
print $fh "$key = $value"; | |
} | |
} | |
if ( ref $_ eq 'HASH' ) { | |
my $key = $_->{key}; | |
my $value = delete $configs{$key}; | |
next unless $value; | |
my $tabs = "\t" x ($_->{tabs} || 1); | |
print $fh "# $license\n" | |
if $key eq 'PERMIT_PACKAGE_CDROM' && $license; | |
print $fh "$key =$tabs$value"; | |
} | |
else { | |
print $fh $_; | |
} | |
$last_blank = $is_blank; | |
} | |
close $fh; | |
} | |
sub makefile_depends { | |
my ($di, $config) = @_; | |
my %prereqs = %{ $di->{metadata}->{prereqs} || {} }; | |
#CONFIGURE_STYLE=modbuild | |
my %depend_map = ( | |
BUILD_DEPENDS => [ 'configure', 'build' ], | |
RUN_DEPENDS => ['runtime'], | |
TEST_DEPENDS => ['test'], | |
); | |
my %depends; | |
foreach my $type ( sort keys %depend_map ) { | |
my @ports; | |
foreach my $key ( @{ $depend_map{$type} } ) { | |
next unless $prereqs{$key}; | |
my $requires = $prereqs{$key}{requires}; | |
foreach my $module ( sort keys %{ $requires || {} } ) { | |
next if module_is_in_base($module); | |
my $dist = get_dist_for_module($module); | |
my $port = port_for_dist($dist); | |
say ". $port [$module]"; | |
push @{ $depends{$type} }, $port; | |
# Silly place to recurse . . . | |
push @distributions, $dist; | |
} | |
} | |
} | |
# People like to hide test depends in build depends because | |
# some tools don't make the distinction. | |
$depends{TEST_DEPENDS} = delete $depends{BUILD_DEPENDS} | |
if grep { /Test/ } @{ $depends{BUILD_DEPENDS} || [] }; | |
foreach my $type ( sort keys %depends ) { | |
my @ports = @{ $depends{$type} }; | |
my $tabs = 2 - int( length("$type =") / TABLENGTH ); | |
$config->{$type} = ( "\t" x $tabs ) | |
. join( " \\\n\t\t", sort( uniq( @ports ) ) ) . "\n"; | |
} | |
} | |
sub make_descr { | |
my ($di) = @_; | |
make_path('pkg') unless -e 'pkg'; | |
my @readme = split /\n/x, get_readme_for_dist( $di ); | |
my $descr = q{}; | |
my $in_descr = 0; | |
foreach (@readme) { | |
if (/^(\S+)/x) { | |
last if $in_descr; | |
$in_descr = $1 eq 'DESCRIPTION'; | |
next; | |
} | |
if ($in_descr) { | |
s/^\s+//x; | |
$descr .= "$_\n"; | |
} | |
} | |
open my $fh, '>', 'pkg/DESCR' or die "Couldn't open readme: $!"; | |
print $fh $descr; | |
close $fh; | |
} | |
sub port_for_dist { | |
my ($dist) = @_; | |
$dist = ref $dist ? $dist->{distribution} : $dist; | |
$dist = { | |
MailTools => 'Mail-Tools', | |
TimeDate => 'Time-TimeDate', | |
'YAML-LibYAML' => 'YAML-XS', | |
Mojolicious => 'Mojo', | |
'libwww-perl' => 'libwww', | |
}->{$dist} || $dist; | |
my ($dir) = glob("/usr/ports/*/p5-$dist"); | |
$dir = "CPAN/p5-$dist" unless $dir && $dir !~ /\*/; | |
$dir =~ s{^/usr/ports/+}{}; | |
return $dir; | |
} | |
sub module_is_in_base { | |
my ($module) = @_; | |
return 1 if $module eq 'perl'; | |
my $module_path = $module; | |
$module_path =~ s{::}{/}gx; | |
$module_path .= '.pm'; | |
foreach (@INC) { | |
next unless m{/usr/libdata/}x; | |
my $path = "$_/$module_path"; | |
return 1 if -e $path; | |
} | |
return 0; | |
} | |
sub dist_is_up_to_date { | |
my ($di) = @_; | |
my $current_distname = port_value( $di, 'DISTFILES' ); | |
return $current_distname eq $di->{archive}; | |
} | |
sub get_dist_info { | |
my ($distribution) = @_; | |
$distribution = get_dist_for_module($distribution) | |
if $distribution =~ /::/x; | |
return _get_json("release/$distribution"); | |
} | |
sub get_dist_for_module { | |
my ($module) = @_; | |
return _get_json("module/$module?fields=distribution")->{distribution}; | |
} | |
sub get_readme_for_dist { | |
my ($di) = @_; | |
my $path = join '/', 'source', @{$di}{qw( author name )}, 'README'; | |
return _get($path); | |
} | |
sub _get { | |
my ($url) = @_; | |
for ( 0 .. 2 ) { | |
open my $fh, '-|', @get_cmd, "$base_url/$url" or die "$!"; | |
my $content = do { local $/ = undef; <$fh> }; | |
close $fh; | |
return $content if $content; | |
sleep 2 * $_; | |
} | |
croak "Failed to get $base_url/$url"; | |
} | |
sub _get_json { return decode_json( _get(@_) ) } | |
sub port_value { | |
my ( $di, $variable ) = @_; | |
my $value = _make_in_port( $di, "show=$variable" ); | |
chomp $value; | |
return $value; | |
} | |
sub _make_in_port { | |
my ( $di, @args ) = @_; | |
my $old_cwd = getcwd(); | |
my $port = port_for_dist($di); | |
my $dir = "/usr/ports/$port"; | |
return '' unless -e $dir; | |
chdir $dir or die "Couldn't chdir $dir: $!"; | |
open my $fh, '-|', 'make', @args or die "Couldn't launch make"; | |
my $output = do { local $/ = undef; <$fh> }; | |
close $fh; | |
chdir $old_cwd or die "Couldn't chdir $old_cwd: $!"; | |
return $output; | |
} | |
sub cp { | |
my (@args) = @_; | |
system('/bin/cp', @args); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# I use this to try to make sure all BUILD and TEST DEPENDS are | |
# correctly setup. It does remove all packages, so you will want | |
# to make sure to run it on a machine dedicated to testing. | |
# | |
# If your ports have updated requirements, you may need to add | |
# this to your /etc/mk.conf | |
# PORTSDIR_PATH=${PORTSDIR}/mystuff:${PORTSDIR} | |
root=`pwd` | |
objdir= | |
for d in `printf "%s\n" */*/ | | |
perl -MList::Util=shuffle -e 'print shuffle(<>)'`; do | |
sudo pkg_delete /var/db/pkg/* | |
log=`echo $d | tr '/' '_' | sed -e 's/_$//'`.log | |
( | |
cd $root/$d | |
[ "$objdir" ] || objdir=`make show=BSDOBJDIR` | |
[ "$objdir" ] || exit | |
rm -rf $objdir/* | |
make fetch | |
[ -e distinfo ] || make makesum | |
[ -e pkg/PLIST ] || make update-plist | |
make clean=depends | |
make test | |
make clean=depends | |
) 2>&1 | tee $log | |
done | |
sudo pkg_delete /var/db/pkg/* |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment