File Coverage

Build
Criterion Covered Total %
statement 1 1 100.0
branch n/a
condition n/a
subroutine n/a
pod n/a
total 1 1 100.0


line stmt bran cond sub pod time code
1             #! /usr/local/bin/perl
2             # MD5: 7324bb55a190bf66cb8de080618826ee
3              
4 1         66523 $Build_PL::ARGV = []
5             ;
6              
7             # line 1 "Build.PL"
8             #!/usr/bin/perl -w
9             # -*- perl -*-
10              
11             #
12             # Author: Slaven Rezic
13             #
14             # Copyright (C) 2017,2018,2019,2020,2021,2022,2023,2024 Slaven Rezic. All rights reserved.
15             # This program is free software; you can redistribute it and/or
16             # modify it under the same terms as Perl itself.
17             #
18             # Mail: slaven@rezic.de
19             # WWW: http://www.rezic.de/eserte/
20             #
21              
22             use strict;
23             use FindBin;
24             use lib "$FindBin::RealBin/lib";
25              
26             use Cwd qw(realpath getcwd);
27             use Digest::MD5 qw(md5_hex);
28             use File::Basename;
29             use Doit;
30             use Doit::Log;
31             use Doit::Util qw(in_directory get_os_release);
32              
33             my $doit = Doit->init;
34              
35             my $Build_PL_file_contents = do {
36             open my $fh, '<', 'Build.PL'
37             or error "Error opening Build.PL: $!";
38             local $/ = undef;
39             <$fh>;
40             };
41             my $Build_PL_md5hex = md5_hex $Build_PL_file_contents;
42              
43             for my $argv_i (0 .. $#ARGV) {
44             my $argv = $ARGV[$argv_i];
45             if ($argv =~ /^[^-].*=.*/ && ($argv_i == 0 || $ARGV[$argv_i-1] !~ /^--?config$/)) { # looks like oldfashioned option without leading dash (e.g. "installdirs=vendor"), but not if following --config
46             $argv =~ s/^/--/;
47             }
48             }
49              
50             my %action_with_arg_handling;
51              
52             if (basename($0) eq 'Build.PL') {
53             _Build_PL_mode();
54             }
55              
56             # Check if Build is up-to-date (md5 check, no timestamp check)
57             {
58             open my $fh, '<', $0
59             or error "Can't open $0: $!";
60             my $shebang = <$fh>;
61             my $md5_line = <$fh>;
62             if (my($old_md5hex) = $md5_line =~ m{^# MD5: (\S+)}) {
63             if ($old_md5hex ne $Build_PL_md5hex) {
64             my $perl;
65             if (($perl) = $shebang =~ m{^#!\s*(.*)}) {
66             # parsed it
67             } else {
68             warning "Cannot parse perl interpreter path out of '$shebang', fallback to 'perl'";
69             $perl = "perl";
70             }
71             error "Build.PL changed, please run '$perl Build.PL' again";
72             }
73             } else {
74             error "Unexpected: no MD5 found in '$md5_line'";
75             }
76             }
77              
78             require Getopt::Long;
79             my %opt = (verbose => 0, uninst => 0, destdir => '', create_packlist => 1);
80             $Build_PL::ARGV=$Build_PL::ARGV if 0; # cease -w
81             @ARGV = (@$Build_PL::ARGV, @ARGV);
82             Getopt::Long::GetOptions(
83             \%opt,
84             'allow_mb_mismatch=i',
85             'config=s%',
86             'create_packlist=i',
87             'destdir=s',
88             'installdirs=s',
89             'install_base=s',
90             'install_path=s%',
91             'jobs=i',
92             'prefix=s',
93             'pureperl-only:1',
94             'uninst:1',
95             'verbose:1',
96             'versionlib=s',
97             'version=s',
98             # The following are just ignored, but currently set by cover
99             'extra_compiler_flags=s',
100             'extra_linker_flags=s',
101             )
102             or error "usage: $0 [options]";
103              
104             my $action = shift || 'build';
105             $action =~ s/-/_/g;
106             if (!$action_with_arg_handling{$action}) {
107             if (@ARGV) {
108             error "No arguments allowed for action '$action'";
109             }
110             }
111              
112             tie my %Config, 'CustomConfig', $opt{config};
113              
114             {
115             no strict 'refs';
116             &$action;
117             }
118              
119             sub build {
120             build_libs();
121             manifypods();
122             }
123              
124             sub build_libs {
125             $doit->make_path('blib/lib');
126             $doit->make_path('blib/arch'); # not used, but keep ExtUtils::Install quiet
127             require File::Find;
128             my @pm_files;
129             File::Find::find(sub { no warnings 'once'; push @pm_files, $File::Find::name if /\.(pm|pod)$/ && -f $_ }, "lib");
130             my %seen_blib;
131             for my $file (@pm_files) {
132             my $dest = 'blib/'.$file;
133             if (!-e $dest || -M $dest > -M $file) {
134             $doit->make_path(dirname($dest));
135             $doit->copy($file, $dest);
136             }
137             $seen_blib{$dest} = 1;
138             }
139             File::Find::find
140             ({
141             wanted => sub {
142             if (-f $_ && !$seen_blib{$File::Find::name}) {
143             warning "Stray file $File::Find::name found, removing...";
144             $doit->unlink($File::Find::name);
145             }
146             },
147             no_chdir => 1,
148             },
149             "blib/lib", "blib/arch",
150             );
151             }
152              
153             sub manifypods {
154             # Handles only Pods in .pod files (which is the case in the Doit distribution)
155             require File::Find;
156             require File::Glob;
157             require Pod::Man;
158             my $mansep = $^O =~ m{^(MSWin32|cygwin)$} ? '.' : '::';
159             my %seen_blib_man;
160             File::Find::find
161             ({
162             wanted => sub {
163             if (-f $_ && /\.pod$/) {
164             no warnings 'once';
165             my $pod = $File::Find::name;
166             my $section = 3; # no scripts yet, so we can hardcode here
167             my %options = (section => $section);
168             (my $man = $pod) =~ s{^lib.}{};
169             $man =~ s{/+}{$mansep}g;
170             $man =~ s{\.pod$}{.$Config{"man${section}ext"}};
171             $man = "blib/man$section/$man";
172             if (!-e $man || -M $man > -M $pod || -M $man > -M "Build") {
173             $doit->make_path(dirname($man));
174             my $parser = Pod::Man->new(%options);
175             if ($doit->is_dry_run) {
176             info "$pod -> $man (dry-run)";
177             } else {
178             info "$pod -> $man";
179             $parser->parse_from_file($pod, $man)
180             or error "Could not install $man";
181             }
182             $doit->chmod(0644, $man); # XXX should this be changeable? like $PERM_RW in Makefile.PL?
183             }
184             $seen_blib_man{$man} = 1;
185             }
186             },
187             no_chdir => 1,
188             },
189             "lib");
190             File::Find::find
191             ({
192             wanted => sub {
193             if (-f $_ && !$seen_blib_man{$File::Find::name}) {
194             warning "Stray file $File::Find::name found, removing...";
195             $doit->unlink($File::Find::name);
196             }
197             },
198             no_chdir => 1,
199             },
200             File::Glob::bsd_glob("blib/man*")
201             );
202             }
203              
204             sub clean {
205             $doit->remove_tree('blib');
206             }
207              
208             sub realclean { &clean }
209              
210             BEGIN { $action_with_arg_handling{'test'} = 1 }
211             sub test {
212             my $usage = sub { error(($_[0] ? "$_[0]\n" : "") . "usage: $0 test -- [--no-build] [file...]") };
213             my $do_build = 1;
214             Getopt::Long::GetOptions(
215             'build!' => \$do_build,
216             ) or $usage->();
217             my @test_files = @ARGV; @ARGV = ();
218              
219             build() if $do_build;
220              
221             local $ENV{PERL_DL_NONLAZY} = 1;
222             require File::Glob;
223             require File::Spec;
224             local $ENV{HARNESS_OPTIONS} = $ENV{HARNESS_OPTIONS};
225             if ($opt{jobs} && (!defined $ENV{HARNESS_OPTIONS} || $ENV{HARNESS_OPTIONS} !~ /(^|:)j\d+($|:)/)) {
226             $doit->setenv(HARNESS_OPTIONS => (defined $ENV{HARNESS_OPTIONS} && $ENV{HARNESS_OPTIONS} ne '' ? $ENV{HARNESS_OPTIONS} . ':' : '') . "j$opt{jobs}")
227             }
228             if (0) {
229             $doit->system(_prove_path(), '-b', 't', @test_files); # use right perl?
230             } else {
231             if (!@test_files) {
232             @test_files = sort { lc $a cmp lc $b } File::Glob::bsd_glob(File::Spec->catfile("t", "*.t"));
233             }
234             if (0) {
235             $doit->system($^X, "-MExtUtils::Command::MM", "-MTest::Harness", "-e", 'undef *Test::Harness::Switches; test_harness(0, "blib/lib", "blib/arch")', @test_files);
236             } else {
237             require Test::Harness;
238             if ($doit->is_dry_run) {
239             info "test @test_files (dry-run)";
240             } else {
241             local @INC = @INC;
242             unshift @INC, map { File::Spec->rel2abs($_) } qw(blib/lib blib/arch);
243             info "test @test_files";
244             Test::Harness::runtests(@test_files);
245             }
246             }
247             }
248             }
249              
250             sub test_xt {
251             $doit->system(_prove_path(), '-b', ($opt{jobs} ? ('--jobs', $opt{jobs}) : ()), 'xt'); # use right perl?
252             }
253              
254             sub test_installed {
255             my $t_dir = "$FindBin::RealBin/t";
256             chdir "/"
257             or error "Cannot chdir to /: $!";
258             $doit->system(_prove_path(), $t_dir);
259             }
260              
261             BEGIN { $action_with_arg_handling{'test_in_docker'} = 1 }
262             sub test_in_docker {
263             my $usage = sub { error(($_[0] ? "$_[0]\n" : "") . "usage: $0 test_in_docker -- [--more-testing] dockerimage:tag") };
264             Getopt::Long::GetOptions(
265             "more-testing!" => \my $more_testing,
266             "invalidate-cache!" => \my $do_invalidate_cache,
267             ) or $usage->();
268             my $distro_spec = shift @ARGV or $usage->("dockerimage:tag missing");
269             @ARGV and $usage->("too many arguments");
270             if (!$distro_spec || $distro_spec !~ m{^.*:.*$}) {
271             $usage->("Please set dockerimage:tag argument to something like 'debian:bookworm'");
272             }
273             Doit::Log::set_label("[$distro_spec" . ($more_testing ? " (more)" : "") . "]");
274             for my $tool (qw(docker)) {
275             $doit->which($tool) or error "$tool is missing";
276             }
277             require File::Temp;
278             my $dir = File::Temp::tempdir("Doit_test_in_docker_XXXXXXXX", TMPDIR => 1, CLEANUP => 1);
279             my $dockerfile = <<"EOF";
280             FROM $distro_spec
281              
282             EOF
283             if ($do_invalidate_cache) {
284             _add_Dockerfile_invalidate_cmd(\$dockerfile);
285             }
286              
287             my $add_opts = {};
288             $dockerfile .= _fix_Dockerfile_for_dist($distro_spec, $add_opts);
289             _docker_add_distro_specific_files(dir => $dir, distro_spec => $distro_spec);
290             if (-e "$dir/.distro_support") {
291             $dockerfile .= <<'EOF';
292             COPY .distro_support .distro_support
293             RUN .distro_support/run.sh
294             EOF
295             }
296              
297             if ($distro_spec =~ m{^(centos|rockylinux|fedora):}) {
298             ######################################################################
299             # CentOS
300             # From https://fedoraproject.org/wiki/EPEL/FAQ#How_can_I_install_the_packages_from_the_EPEL_software_repository.3F
301             if ($distro_spec eq 'centos:6') {
302             $dockerfile .= <<"EOF";
303             RUN rpm -Uvh https://dl.fedoraproject.org/pub/archive/epel/6/x86_64/epel-release-6-8.noarch.rpm
304             EOF
305             } elsif ($distro_spec eq 'centos:7') {
306             $dockerfile .= <<"EOF";
307             RUN rpm -Uvh https://dl.fedoraproject.org/pub/archive/epel/7/x86_64/Packages/e/epel-release-7-14.noarch.rpm
308             EOF
309             } elsif ($distro_spec eq 'rockylinux:8') {
310             $dockerfile .= <<"EOF";
311             RUN yum -y install epel-release
312             EOF
313             } else {
314             info "No EPEL support for $distro_spec";
315             }
316             $dockerfile .= <<"EOF";
317             RUN yum -y install \\
318             sudo \\
319             rsync \\
320             "perl(Digest::MD5)" \\
321             "perl(ExtUtils::MakeMaker)" \\
322             "perl(FindBin)" \\
323             "perl(Hash::Util)" \\
324             "perl(Sys::Hostname)" \\
325             "perl(Test::More)" \\
326             "perl(Tie::File)" \\
327             make \\
328             git
329              
330             EOF
331             if ($distro_spec eq 'centos:8') {
332             # Requires also PowerTools repo additionally to EPEL
333             $dockerfile .= <<"EOF";
334             RUN dnf -y install 'dnf-command(config-manager)'
335             RUN yum config-manager --set-enabled PowerTools
336             EOF
337             } elsif ($distro_spec eq 'rockylinux:8') {
338             $dockerfile .= <<"EOF";
339             RUN dnf -y --enablerepo=powertools install perl-IPC-Run
340             EOF
341             } elsif ($distro_spec eq 'rockylinux:9') {
342             $dockerfile .= <<"EOF";
343             RUN dnf -y --enablerepo=crb install perl-IPC-Run
344             EOF
345             }
346             $dockerfile .= <<"EOF";
347             RUN yum -y install \\
348             "perl(IPC::Run)"
349             EOF
350             if ($distro_spec ne 'centos:8' && $distro_spec ne 'rockylinux:8' && $distro_spec ne 'rockylinux:9') {
351             # Currently not available in CentOS8 and Rocky
352             $dockerfile .= <<"EOF";
353             RUN yum -y install \\
354             "perl(Net::OpenSSH)"
355             EOF
356             }
357             if ($distro_spec eq 'centos:6') {
358             $dockerfile .= <<"EOF";
359             RUN curl -L https://cpanmin.us | perl - App::cpanminus
360              
361             # Not available as an RPM
362             RUN cpanm --quiet --notest CPAN::Meta
363             EOF
364             } else {
365             $dockerfile .= <<"EOF";
366             RUN yum -y install \\
367             "perl(CPAN::Meta)"
368             EOF
369             }
370             if ($more_testing) {
371             $dockerfile .= <<"EOF";
372             RUN yum -y install \\
373             "perl(Capture::Tiny)" \\
374             "perl(Term::ANSIColor)" \\
375             "perl(BSD::Resource)" \\
376             "perl(CPAN::Meta::Validator)" \\
377             "perl(LWP::UserAgent)" \\
378             "perl(Config::IniFiles)" \\
379             file
380              
381             ENV GITHUB_ACTIONS 1
382             ENV DOIT_TEST_WITH_SUDO 1
383              
384             EOF
385             if ($distro_spec eq 'centos:6') {
386             $dockerfile .= <<"EOF";
387             # Term::ANSIColor exists, but is too old (no colorstrip function)
388             RUN cpanm --quiet --notest "Term::ANSIColor~>=2.01"
389              
390             RUN yum -y install python34-pip
391              
392             EOF
393             } else {
394             $dockerfile .= <<"EOF";
395             RUN yum -y install python3-pip
396             EOF
397             }
398             }
399             } elsif ($distro_spec =~ m{^alpine(:|$)}) {
400             $dockerfile .= <<"EOF";
401             RUN apk update
402             # perl-utils for prove
403             # shadow for useradd
404             RUN apk add sudo rsync git perl perl-utils make shadow
405             EOF
406             if ($more_testing) {
407             $dockerfile .= <<"EOF";
408             RUN apk add py3-pip
409              
410             #ENV GITHUB_ACTIONS 1
411             ENV DOIT_TEST_WITH_SUDO 1
412              
413             EOF
414             }
415              
416             } else {
417             ######################################################################
418             # Debian-like (Debian, Ubuntu, LinuxMint ...)
419             my $packages_spec;
420             {
421             my @packages;
422             {
423             push @packages, (
424             'libipc-run-perl',
425             ($distro_spec eq 'ubuntu:precise' ? () : 'libnet-openssh-perl'),
426             'sudo',
427             'rsync',
428             'git',
429             );
430             }
431             if ($more_testing) {
432             push @packages, (
433             'libbsd-resource-perl',
434             'libcapture-tiny-perl',
435             'libconfig-inifiles-perl',
436             'libcpan-meta-perl',
437             'libdevel-hide-perl',
438             'libwww-perl',
439             'perl-modules',
440             'locales',
441             'file',
442             ($distro_spec eq 'ubuntu:precise' ? () : 'python3-pip'),
443             );
444             }
445             $packages_spec = join(" \\\n", map { " $_" } @packages);
446             }
447              
448             if ($distro_spec =~ m{^(ubuntu:precise)$}) {
449             # See https://gist.github.com/dergachev/f5da514802fcbbb441a1
450             $dockerfile .= <<'EOF'
451             RUN sed -i.bak -r 's/(archive|security).ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list
452             EOF
453             }
454             $dockerfile .= <<"EOF";
455             ENV DEBIAN_FRONTEND noninteractive
456              
457             RUN apt-get$add_opts->{apt_get_update_opts} update && apt-get install -y --no-install-recommends$add_opts->{apt_get_install_opts} \\
458             $packages_spec
459              
460             EOF
461             if ($more_testing) {
462             $dockerfile .= <<"EOF";
463             ENV GITHUB_ACTIONS 1
464             ENV DOIT_TEST_WITH_SUDO 1
465              
466             EOF
467             }
468             }
469              
470             for my $env_key (qw(HARNESS_OPTIONS)) {
471             if (defined $ENV{$env_key}) {
472             $dockerfile .= <<"EOF";
473             ENV $env_key "$ENV{$env_key}"
474             EOF
475             }
476             }
477              
478             $doit->write_binary("$dir/Dockerfile", $dockerfile);
479             in_directory {
480             (my $tag = $distro_spec) =~ s{:}{-}g;
481             $tag = 'doit-test-' . $tag;
482             $doit->system(qw(docker build -t), $tag, qw(.));
483             $doit->system(
484             qw(docker run), '-v', "$FindBin::RealBin:/data:ro", $tag,
485             'sh', '-c', 'rsync -a --no-owner --no-group --exclude=blib "--exclude=Doit-*.tar.gz" /data/ /tmp/Doit/ && cd /tmp/Doit && perl Build.PL && ./Build && ./Build generate_META_json && ./Build test && ./Build test_xt && ./Build dist_install_and_test'
486             );
487             } $dir;
488              
489             Doit::Log::set_label(undef);
490             }
491              
492             BEGIN { $action_with_arg_handling{'test_package_in_docker'} = 1 }
493             sub test_package_in_docker { # XXX Currently only support for deb packages
494             my $usage = sub { error(($_[0] ? "$_[0]\n" : "") . "usage: $0 test_package_in_docker -- --package package.deb dockerimage:tag") };
495             Getopt::Long::GetOptions(
496             "package=s" => \my $package,
497             ) or $usage->();
498             $package or $usage->("--package is mandatory");
499             my $distro_spec = shift @ARGV or $usage->("dockerimage:tag missing");
500             @ARGV and $usage->("too many arguments");
501             if (!$distro_spec || $distro_spec !~ m{^.*:.*$}) {
502             $usage->("Please set dockerimage:tag argument to something like 'debian:bookworm'");
503             }
504             Doit::Log::set_label("[$distro_spec]");
505             for my $tool (qw(docker)) {
506             $doit->which($tool) or error "$tool is missing";
507             }
508             require File::Temp;
509             my $dir = File::Temp::tempdir("Doit_test_in_docker_XXXXXXXX", TMPDIR => 1, CLEANUP => 1);
510             $doit->copy($package, $dir);
511             my $package_basename = basename($package);
512             my $dockerfile = <<"EOF";
513             FROM $distro_spec
514              
515             COPY $package_basename /tmp/$package_basename
516             EOF
517             my $add_opts = {};
518             $dockerfile .= _fix_Dockerfile_for_dist($distro_spec, $add_opts);
519              
520             $dockerfile .= <<"EOF";
521             ENV DEBIAN_FRONTEND noninteractive
522             RUN apt-get$add_opts->{apt_get_update_opts} update && \\
523             apt-get install -y --no-install-recommends$add_opts->{apt_get_install_opts} /tmp/$package_basename && \\
524             apt-get install -y --no-install-recommends$add_opts->{apt_get_install_opts} rsync
525             EOF
526              
527             $doit->write_binary("$dir/Dockerfile", $dockerfile);
528             in_directory {
529             $doit->system(qw(docker build -t doit-test .));
530             $doit->system(
531             qw(docker run), '-v', "$FindBin::RealBin:/data:ro", 'doit-test',
532             'sh', '-c', 'rsync -a --no-owner --no-group --exclude=blib "--exclude=Doit-*.tar.gz" /data/ /tmp/Doit/ && cd /tmp/Doit && perl Build.PL && ./Build test'
533             );
534             } $dir;
535              
536             Doit::Log::set_label(undef);
537             }
538              
539             # Run docker-based tests on a number of Linux distributions:
540             # the stable ones of Debian, Ubuntu and CentOS, and some
541             # older versions of these distributions.
542             sub test_standard {
543             Doit::Log::set_label("[local]");
544             $doit->system($^X, 'Build.PL');
545             $doit->system('./Build');
546             $doit->system('./Build', 'test');
547             Doit::Log::set_label(undef);
548             my @results;
549             for my $distro_spec ('debian:bookworm', 'ubuntu:jammy', 'debian:bullseye', 'ubuntu:focal', 'debian:buster', 'ubuntu:bionic', 'centos:7', 'debian:stretch', 'debian:jessie', 'ubuntu:xenial', 'ubuntu:precise', 'centos:6', 'alpine:latest') {
550             for my $more_testing (0, 1) {
551             my $t0 = time; # currently not hires, should be fine for now
552             $doit->system('./Build', 'test_in_docker', '--', ($more_testing ? '--more-testing' : ()), $distro_spec);
553             my $t1 = time;
554             push @results, { distro => $distro_spec, more_testing => $more_testing, result => 'pass', time => ($t1-$t0)."s" };
555             }
556             }
557              
558             my $report = "test_standard results\n";
559             my @cols = qw(distro more_testing result time);
560             my @col_widths;
561             for my $col (@cols) {
562             my $max_width;
563             for my $result (@results) {
564             my $width = length $result->{$col};
565             $max_width = $width if !defined $max_width || $max_width < $width;
566             }
567             push @col_widths, $max_width;
568             }
569             for my $result (@results) {
570             for my $col_i (0 .. $#cols) {
571             $report .= sprintf "%-${col_widths[$col_i]}s ", $result->{$cols[$col_i]};
572             }
573             $report .= "\n";
574             }
575             info $report;
576             }
577              
578             sub test_kwalitee (;$) {
579             my($distdir) = @_;
580             # If cwd is used then more tests fail,
581             # because META files are not available here.
582             $distdir = $FindBin::RealBin if !defined $distdir;
583              
584             if (eval { require Test::Kwalitee; 1 }) {
585             in_directory {
586             local $ENV{RELEASE_TESTING} = 1;
587             eval { $doit->system($^X, '-MTest::More', '-MTest::Kwalitee=kwalitee_ok', '-e', 'kwalitee_ok(qw(-has_manifest -has_meta_yml)); done_testing') };
588             } $distdir;
589             } else {
590             warning "Test::Kwalitee is not installed";
591             }
592             }
593              
594             sub test_pod (;$) {
595             my($distdir) = @_;
596             $distdir = $FindBin::RealBin if !defined $distdir;
597              
598             if (eval { require Test::Pod; 1 }) {
599             in_directory {
600             eval { $doit->system($^X, '-MTest::Pod', '-e', 'all_pod_files_ok()') };
601             } $distdir;
602             } else {
603             warning "Test::Pod is not installed";
604             }
605             }
606              
607             sub test_cpan_versions {
608             my($distdir) = @_;
609              
610             if ($doit->which("cpan_check_versions")) {
611             in_directory {
612             $doit->system('cpan_check_versions', '-remote');
613             } $distdir;
614             } else {
615             warning <
616             No cpan_check_versions found in PATH, cannot check for properly
617             incremented VERSIONs. Please check git://github.com/eserte/srezic-misc.git
618             EOF
619             }
620             }
621              
622             sub git_checks {
623             _check_clean_git();
624              
625             my $Doit_VERSION = _get_Doit_VERSION();
626              
627             my $out = $doit->info_qx(qw(git tag -l), $Doit_VERSION);
628             if (defined $out && $out ne '') {
629             error "A git tag $Doit_VERSION already exists";
630             }
631             }
632              
633             sub distdir {
634             my(%options) = @_;
635             my $temporary = delete $options{temporary};
636             error "Unhandled options: " . join(" ", %options) if %options;
637              
638             require File::Temp;
639             my $tempdir = File::Temp::tempdir("Doit_distdir_XXXXXXXX", TMPDIR => 1, CLEANUP => $temporary);
640             my $Doit_VERSION = _get_Doit_VERSION();
641             my $distdir = "$tempdir/Doit-$Doit_VERSION";
642             $doit->mkdir($distdir);
643             for my $line (split /\0/, $doit->info_qx({quiet=>1}, 'git', 'ls-files', '-z')) {
644             next if ($line =~ m{^( \.travis\.yml
645             | \.?appveyor\.yml
646             | \.github/.*
647             | \.gitignore
648             )$}x);
649             # XXX maybe implement also MANIFEST.SKIP?
650             my $dirname = dirname $line;
651             if ($dirname ne '.') {
652             $doit->make_path("$distdir/$dirname");
653             }
654             $doit->copy($line, "$distdir/$dirname/");
655             }
656              
657             generate_META_json("$distdir/META.json");
658             generate_META_yml("$distdir/META.yml" );
659              
660             test_kwalitee($distdir);
661             test_pod($distdir);
662              
663             # If not temporary, then it is assumed that the
664             # interactive distdir action is called, and
665             # provide information to the user.
666             if (!$temporary) {
667             info "Distribution directory is $distdir";
668             }
669              
670             $distdir;
671             }
672              
673             sub dist {
674             if (!$ENV{DOIT_TEST_SKIP_SOME_CHECKS}) {
675             git_checks();
676             }
677             # else skip because Doit version is usually not incremented in
678             # this phase and the git tag already exists
679              
680              
681             my $tarfile = _get_tarfilename();
682             if (-e $tarfile) {
683             error "$tarfile already exists";
684             }
685             my $distdir = distdir(temporary => 1);
686              
687             if (!$ENV{DOIT_TEST_SKIP_SOME_CHECKS}) {
688             test_cpan_versions($distdir);
689             }
690             # else because versions of all Doit modules are usually not
691             # incremented in this phase
692              
693             in_directory {
694             # Note: tar cfvz C:/... ... does not seem to work on Windows
695             # (for all or some tar versions?). Error message is:
696             # tar (child): Cannot connect to C: resolve failed
697             # So create in cwd first, and move to final location.
698             $doit->system('tar', 'cfvz', basename($tarfile), basename($distdir));
699             $doit->move(basename($tarfile), $tarfile);
700             } "$distdir/..";
701             }
702              
703             sub dist_install_and_test {
704             local $ENV{DOIT_TEST_SKIP_SOME_CHECKS} = 1; # XXX is there a better interface?
705             my $tarfile = _get_tarfilename();
706             if (!-e $tarfile) {
707             dist();
708             }
709             require File::Temp;
710             my $dir = File::Temp::tempdir("Doit_dist_install_and_test_XXXXXXXX", TMPDIR => 1, CLEANUP => 1);
711             my $Doit_VERSION = _get_Doit_VERSION();
712             in_directory {
713             # Note: see above for tar under Windows problems
714             $doit->move($tarfile, basename($tarfile));
715             $doit->system('tar', 'xfz', basename($tarfile));
716             in_directory {
717             $doit->system($^X, 'Build.PL');
718             my @Build = $^O eq 'MSWin32' ? ($^X, 'Build') : ('./Build');
719             my @sudo = $^O eq 'MSWin32' ? () : ('sudo');
720             $doit->system(@Build);
721             $doit->system(@Build, 'test');
722             $doit->system(@sudo, @Build, 'install');
723             $doit->system(@Build, 'test-installed');
724             } "Doit-$Doit_VERSION";
725             } $dir;
726             }
727              
728             sub dist_install_with_cpanm {
729             my $tarfile = _get_tarfilename();
730             if (!-e $tarfile) {
731             dist();
732             }
733             $doit->system('cpanm', $tarfile);
734             test_installed();
735             }
736              
737             sub look {
738             my $distdir = distdir(temporary => 1);
739             in_directory {
740             info "Spawning $ENV{SHELL} in $distdir...";
741             local $ENV{DOIT_BUILD_SHELL_LEVEL} = $ENV{DOIT_BUILD_SHELL_LEVEL};
742             $ENV{DOIT_BUILD_SHELL_LEVEL}++;
743             $doit->system($ENV{SHELL});
744             } $distdir;
745             }
746              
747             BEGIN { $action_with_arg_handling{'cover'} = 1 }
748             sub cover {
749             my(@files) = @ARGV;
750             if (@files) {
751             for my $file (@files) {
752             $doit->system($^X, '-MDevel::Cover', $file);
753             }
754             } else {
755             $doit->system(_cover_path(), '--test');
756             }
757             $doit->unlink('MYMETA.json.lock'); # https://github.com/pjcj/Devel--Cover/issues/263
758             }
759              
760             BEGIN { $action_with_arg_handling{'show_cover'} = 1 }
761             sub show_cover {
762             Getopt::Long::GetOptions(
763             'show-only' => \my $show_only,
764             ) or error "usage: $0 show_cover -- --show-only [file ...]";
765             my @files = @ARGV;
766             if (@files && $show_only) { error "Cannot specify --show-only together with list of files" }
767             if ($show_only) {
768             $doit->system(_cover_path());
769             } else {
770             cover(@files);
771             }
772             my $browser = $^O eq 'darwin' ? 'open' : $doit->which('xdg-open') ? 'xdg-open' : 'firefox';
773             $doit->system($browser, "$FindBin::RealBin/cover_db/coverage.html");
774             }
775              
776             sub install {
777             build();
778             # XXX check if test suite was run?
779              
780             # MOD_INSTALL = $(ABSPERLRUN) -MExtUtils::Install -e 'install([ from_to => {@ARGV}, verbose => '\''$(VERBINST)'\'', uninstall_shadows => '\''$(UNINST)'\'', dir_mode => '\''$(PERM_DIR)'\'' ]);' --
781             # $(MOD_INSTALL) \
782              
783             # for a perl install:
784             # read "$(PERL_ARCHLIB)/auto/$(FULLEXT)/.packlist" \
785             # write "$(DESTINSTALLARCHLIB)/auto/$(FULLEXT)/.packlist" \
786             # "$(INST_LIB)" "$(DESTINSTALLPRIVLIB)" \
787             # "$(INST_ARCHLIB)" "$(DESTINSTALLARCHLIB)" \
788             # "$(INST_BIN)" "$(DESTINSTALLBIN)" \
789             # "$(INST_SCRIPT)" "$(DESTINSTALLSCRIPT)" \
790             # "$(INST_MAN1DIR)" "$(DESTINSTALLMAN1DIR)" \
791             # "$(INST_MAN3DIR)" "$(DESTINSTALLMAN3DIR)"
792              
793             # for a site install:
794             # read "$(SITEARCHEXP)/auto/$(FULLEXT)/.packlist" \
795             # write "$(DESTINSTALLSITEARCH)/auto/$(FULLEXT)/.packlist" \
796             # "$(INST_LIB)" "$(DESTINSTALLSITELIB)" \
797             # "$(INST_ARCHLIB)" "$(DESTINSTALLSITEARCH)" \
798             # "$(INST_BIN)" "$(DESTINSTALLSITEBIN)" \
799             # "$(INST_SCRIPT)" "$(DESTINSTALLSITESCRIPT)" \
800             # "$(INST_MAN1DIR)" "$(DESTINSTALLSITEMAN1DIR)" \
801             # "$(INST_MAN3DIR)" "$(DESTINSTALLSITEMAN3DIR)"
802            
803             require Data::Dumper;
804             require ExtUtils::Install;
805             my $FULLEXT = 'Doit';
806             my $INST_LIB = 'blib/lib';
807             my $INST_ARCHLIB = 'blib/arch';
808             my $INST_BIN = 'blib/bin';
809             my $INST_SCRIPT = 'blib/script';
810             my $INST_MAN1DIR = 'blib/man1';
811             my $INST_MAN3DIR = 'blib/man3';
812             my $PERM_DIR = '755';
813             my @euii_args = (
814             from_to => {
815             (
816             $opt{install_base}
817             ?
818             (
819             read => "$Config{sitearchexp}/auto/$FULLEXT/.packlist",
820             ($opt{create_packlist} ? (write => "$opt{install_base}/lib/perl5/$Config{archname}/auto/$FULLEXT/.packlist") : ()),
821             $INST_LIB => "$opt{install_base}/lib/perl5",
822             $INST_ARCHLIB => "$opt{install_base}/lib/perl5",
823             $INST_BIN => "$opt{install_base}/bin",
824             $INST_SCRIPT => "$opt{install_base}/bin",
825             $INST_MAN1DIR => "$opt{install_base}/man/man1",
826             $INST_MAN3DIR => "$opt{install_base}/man/man3",
827             )
828             :
829             ($opt{installdirs}||'') eq 'core'
830             ?
831             (
832             read => "$Config{archlib}/auto/$FULLEXT/.packlist",
833             ($opt{create_packlist} ? (write => "$opt{destdir}$Config{installarchlib}/auto/$FULLEXT/.packlist") : ()),
834             $INST_LIB => "$opt{destdir}$Config{installprivlib}",
835             $INST_ARCHLIB => "$opt{destdir}$Config{installarchlib}",
836             $INST_BIN => "$opt{destdir}$Config{installbin}",
837             $INST_SCRIPT => "$opt{destdir}$Config{installscript}",
838             ($Config{installman1dir} ? ($INST_MAN1DIR => "$opt{destdir}$Config{installman1dir}") : ()),
839             ($Config{installman3dir} ? ($INST_MAN3DIR => "$opt{destdir}$Config{installman3dir}") : ()),
840             )
841             :
842             ($opt{installdirs}||'') eq 'vendor'
843             ?
844             (
845             read => "$Config{vendorlib}/auto/$FULLEXT/.packlist",
846             ($opt{create_packlist} ? (write => "$opt{destdir}$Config{installvendorarch}/auto/$FULLEXT/.packlist") : ()),
847             $INST_LIB => "$opt{destdir}$Config{installvendorlib}",
848             $INST_ARCHLIB => "$opt{destdir}$Config{installvendorarch}",
849             $INST_BIN => "$opt{destdir}$Config{installvendorbin}",
850             $INST_SCRIPT => "$opt{destdir}$Config{installvendorscript}",
851             ($Config{installvendorman1dir} ? ($INST_MAN1DIR => "$opt{destdir}$Config{installvendorman1dir}") : ()),
852             ($Config{installvendorman3dir} ? ($INST_MAN3DIR => "$opt{destdir}$Config{installvendorman3dir}") : ()),
853             )
854             : # default is site
855             (
856             read => "$Config{sitearchexp}/auto/$FULLEXT/.packlist",
857             ($opt{create_packlist} ? (write => "$opt{destdir}$Config{installsitearch}/auto/$FULLEXT/.packlist") : ()),
858             $INST_LIB => "$opt{destdir}$Config{installsitelib}",
859             $INST_ARCHLIB => "$opt{destdir}$Config{installsitearch}",
860             $INST_BIN => "$opt{destdir}$Config{installsitebin}",
861             $INST_SCRIPT => "$opt{destdir}$Config{installsitescript}",
862             ($Config{installsiteman1dir} ? ($INST_MAN1DIR => "$opt{destdir}$Config{installsiteman1dir}") : ()),
863             ($Config{installsiteman3dir} ? ($INST_MAN3DIR => "$opt{destdir}$Config{installsiteman3dir}") : ()),
864             )
865             )
866             },
867             verbose => $opt{verbose},
868             uninstall_shadows => $opt{uninst},
869             dir_mode => $PERM_DIR,
870             dry_run => $doit->is_dry_run,
871             );
872             my $euii_args_dump = Data::Dumper->new([{ @euii_args }],[qw()])->Indent(1)->Useqq(1)->Sortkeys(1)->Terse(1)->Dump;
873             info "Run ExtUtils::Install::install with parameters\n" . $euii_args_dump;
874             ExtUtils::Install::install([@euii_args]);
875             if ($doit->is_dry_run) {
876             info "(this was dry-run mode)";
877             }
878             }
879              
880             {
881             my $Doit_VERSION;
882              
883             sub _get_Doit_VERSION () {
884             return $Doit_VERSION if defined $Doit_VERSION;
885              
886             {
887             my $Doit_pm = $INC{"Doit.pm"};
888             open my $fh, $Doit_pm or die "Cannot open $Doit_pm: $!";
889             while(<$fh>) {
890             if (/\$VERSION\s*=\s*'(.*)'/) {
891             $Doit_VERSION = $1;
892             last;
893             }
894             }
895             if (!defined $Doit_VERSION) {
896             error "Fatal: Cannot find \$VERSION in $Doit_pm";
897             }
898             }
899             if ($Doit_VERSION !~ m{^\d+\.[\d_]+$}) {
900             error "Doit.pm VERSION $Doit_VERSION does not look as expected";
901             }
902             # Check also for numerical value of version
903             if (!defined $Doit::VERSION) {
904             error "Unexpected: cannot find \$VERSION in loaded Doit.pm";
905             }
906             {
907             (my $Doit_numerical_version = $Doit_VERSION) =~ s{_}{}g;
908             if ($Doit_numerical_version != $Doit::VERSION) {
909             error "Unexpected: parsed version $Doit_VERSION is not numerically equal to loaded version $Doit::VERSION";
910             }
911             }
912             $Doit_VERSION;
913             }
914             }
915              
916             sub _get_tarfilename () {
917             my $Doit_VERSION = _get_Doit_VERSION();
918             getcwd . "/Doit-" . $Doit_VERSION . ".tar.gz";
919             }
920              
921             sub _generate_META ($;$) {
922             my($destfile, $meta_version) = @_;
923              
924             require CPAN::Meta;
925             require ExtUtils::MakeMaker;
926             my $meta = CPAN::Meta->new({
927             name => 'Doit',
928             author => 'Slaven Rezic ',
929             abstract => 'a scripting framework',
930             license => 'perl',
931             version => MM->parse_version("$FindBin::RealBin/lib/Doit.pm"),
932             dynamic_config => 0,
933             prereqs => {
934             configure => {
935             requires => {
936             'Digest::MD5' => 0,
937             'Exporter' => 5.57, # use Exporter "import"
938             'File::Path' => 2.0, # make_path
939             },
940             },
941             runtime => {
942             recommends => {
943             'IPC::Run' => 0,
944             'Net::OpenSSH' => 0,
945             },
946             requires => {
947             'perl' => 5.006,
948             'Exporter' => 5.57, # use Exporter "import"
949             'File::Path' => 2.07, # make_path
950             },
951             },
952             test => {
953             requires => {
954             'Test::More' => 0,
955             },
956             },
957             },
958             resources => {
959             repository => { url => 'git://github.com/eserte/Doit' },
960             },
961             generated_by => "Doit version " . _get_Doit_VERSION(),
962             });
963             if ($doit->is_dry_run) {
964             info "Would create $destfile" . (defined $meta_version ? " (version $meta_version)" : "") . " (dry-run)";
965             } else {
966             $meta->save($destfile, (defined $meta_version ? { version => $meta_version } : ()));
967             }
968             }
969              
970             sub generate_META_json (;$) {
971             my $destfile = shift || 'META.json';
972             _generate_META $destfile;
973             }
974              
975             sub generate_META_yml (;$) {
976             my $destfile = shift || 'META.yml';
977             _generate_META $destfile, 1.4;
978             }
979              
980             # XXX the debian package build functionality should go into
981             # a separate Doit component
982              
983             sub debian_package {
984             _debian_package('--depends' => 'perl, libnet-openssh-perl, libipc-run-perl', '--add-distro-version' => '1');
985             }
986              
987             sub _debian_package {
988             my(%opts) = @_;
989              
990             for my $tool (qw(dh-make-perl)) { # git is checked if needed
991             $doit->which($tool) or error "$tool is missing";
992             }
993              
994             my $distdir = distdir(temporary => 1);
995              
996             my $version = delete $opts{'--version'};
997             my $add_distro_version = delete $opts{'--add-distro-version'};
998             if (!defined $version) {
999             $version = _basic_debian_version();
1000             if ($add_distro_version) {
1001             my $osr = get_os_release();
1002             if (!$osr) {
1003             error 'Cannot read /etc/os-release --- please specify --version manually';
1004             }
1005             my $dist_id = $osr->{ID};
1006             my $rel = $osr->{VERSION_ID};
1007             if ($dist_id eq 'debian') {
1008             $rel =~ s{^(\d+).*}{$1};
1009             $version .= "+deb${rel}u${add_distro_version}";
1010             } elsif ($dist_id eq 'linuxmint') {
1011             $version .= "+linuxmint${rel}u${add_distro_version}";
1012             } elsif ($dist_id eq 'ubuntu') {
1013             $version .= "~ubuntu${rel}.${add_distro_version}";
1014             } else {
1015             error "No distro version support for '$dist_id'";
1016             }
1017             }
1018             }
1019              
1020             my $deb;
1021             in_directory {
1022             $doit->system(qw(dh-make-perl --build --vcs none), (defined $version ? ('--version', $version) : ()), %opts);
1023             require File::Glob;
1024             my(@debs) = File::Glob::bsd_glob("$distdir/../*.deb");
1025             if (@debs != 1) {
1026             error "Expecting exactly one generated .deb, but got: <@debs>\n";
1027             }
1028             $deb = $debs[0];
1029             } $distdir;
1030             $doit->copy($deb, basename($deb));
1031             print basename($deb), "\n";
1032             }
1033              
1034             sub _basic_debian_version {
1035             my $version;
1036             $doit->which('git') or error 'git is missing --- please install it or alternatively specify --version manually';
1037             chomp(my $git_describe = eval { $doit->info_qx('git', 'describe') }); # XXX what if this fails? Optionally made fatal?
1038             if (defined $git_describe) {
1039             if ($git_describe =~ m{^([0-9\._]+)$}) {
1040             $version = $1;
1041             } elsif ($git_describe =~ m{^([0-9\._]+)-(\d+)-g(.*)}) {
1042             $version = $1."+git".$2."+".$3."-1"; # XXX make "1" configurable?
1043             } else {
1044             error "Cannot parse output from git describe: '$git_describe'";
1045             }
1046             $version =~ s{_}{-}; # replace "devel" version specifier
1047             } else {
1048             error "No information using git-describe available. Please specify --version manually.";
1049             }
1050             $version;
1051             }
1052              
1053             BEGIN { $action_with_arg_handling{'debian_package_with_docker'} = 1 }
1054             sub debian_package_with_docker {
1055             my $usage = sub { error(($_[0] ? "$_[0]\n" : "") . "usage: $0 debian_package_with_docker -- [--use-workdir | --tag ...] dockerimage:tag") };
1056             Getopt::Long::GetOptions(
1057             "use-workdir!" => \my $use_workdir,
1058             "tag=s" => \my $tag,
1059             ) or $usage->();
1060             $tag && $use_workdir and $usage->("Cannot use --use-workdir together with --tag");
1061             my $distro_spec = shift @ARGV or $usage->("dockerimage:tag missing");
1062             @ARGV and $usage->("too many arguments");
1063             if ($distro_spec eq 'self') {
1064             $distro_spec = join ":", @{ get_os_release() }{qw(ID VERSION_CODENAME)};
1065             }
1066             if (!$distro_spec || $distro_spec !~ m{^.*:.*$}) {
1067             $usage->("Please set dockerimage:tag argument to something like 'debian:bookworm' or 'self'");
1068             }
1069             for my $tool (qw(docker)) {
1070             $doit->which($tool) or error "$tool is missing";
1071             }
1072              
1073             # Cannot access directories outside of /Users when working
1074             # with docker-machine @ Mac, see for an explanation:
1075             # https://stackoverflow.com/questions/37673140/docker-volume-located-in-tmp-on-osx-empty
1076             my $limited_volume_availability = $^O eq 'darwin';
1077              
1078             require File::Temp;
1079             my $dir = File::Temp::tempdir("Doit_debian_package_docker_XXXXXXXX", TMPDIR => !$limited_volume_availability, CLEANUP => 1);
1080             $dir = realpath $dir; # docker volumes have to be specified with absolute paths
1081              
1082             my $pkgdir = $limited_volume_availability ? "$FindBin::RealBin/pkg" : "/tmp";
1083             if (!-d $pkgdir) {
1084             $doit->mkdir($pkgdir);
1085             }
1086              
1087             if ($use_workdir) {
1088             # no --cvs-exclude --- git-describe has to work
1089             $doit->system('rsync', '-a', '--exclude=blib', '.', $dir);
1090             } else {
1091             $doit->add_component('git');
1092             $doit->git_repo_update(
1093             repository => $FindBin::RealBin,
1094             directory => $dir,
1095             ($tag ? (branch => $tag) : ()),
1096             # no: we need the full history for correct git version calculation: clone_opts => [qw(--depth=1)],
1097             );
1098             }
1099             in_directory {
1100             my $dockerfile = <<"EOF";
1101             FROM $distro_spec
1102             EOF
1103             my $add_opts = {};
1104             $dockerfile .= _fix_Dockerfile_for_dist($distro_spec, $add_opts);
1105             $dockerfile .= <<"EOF";
1106              
1107             ENV DEBIAN_FRONTEND noninteractive
1108              
1109             RUN apt-get$add_opts->{apt_get_update_opts} update && apt-get$add_opts->{apt_get_install_opts} install -y --no-install-recommends \\
1110             dh-make-perl \\
1111             git
1112              
1113             # XXX This is depending on the current perl module and should be configurable
1114             RUN apt-get$add_opts->{apt_get_install_opts} install -y --no-install-recommends \\
1115             libipc-run-perl \\
1116             libnet-openssh-perl
1117              
1118             EOF
1119             for my $env_key (qw(DEBFULLNAME DEBEMAIL EMAIL)) {
1120             if ($ENV{$env_key}) {
1121             $dockerfile .= <<"EOF";
1122             ENV $env_key "$ENV{$env_key}"
1123             EOF
1124             }
1125             }
1126             $doit->write_binary("Dockerfile", $dockerfile);
1127              
1128             (my $label = $distro_spec) =~ s{:}{-}g;
1129             $label = "doit-deb-$label";
1130             $doit->system(qw(docker build -t), $label, qw(.));
1131             $doit->system(
1132             qw(docker run), '-v', "$dir:/data", '-v', "$pkgdir:/pkg", $label,
1133             'sh', '-c', 'git config --global --add safe.directory /data && cd /data && perl Build.PL && ./Build debian_package && cp *.deb /pkg'
1134             );
1135             } $dir;
1136             info "Package is available in $pkgdir";
1137             }
1138              
1139             sub release {
1140             my $Doit_VERSION = _get_Doit_VERSION();
1141             for my $existing_tag (split /\n/, $doit->info_qx({quiet=>1}, 'git', 'tag')) {
1142             if ($Doit_VERSION eq $existing_tag) {
1143             error "The version $Doit_VERSION is already tagged --- probably the release was already done";
1144             }
1145             }
1146              
1147             FIND_VERSION: {
1148             open my $cfh, '<', 'Changes'
1149             or error "Can't open Changes: $!";
1150             while(<$cfh>) {
1151             if (/^\Q$Doit_VERSION\E\s/) {
1152             last FIND_VERSION;
1153             }
1154             }
1155             error "Cannot find version $Doit_VERSION in Changes";
1156             }
1157              
1158             $doit->system('git', 'fetch');
1159              
1160             $doit->add_component('git');
1161             my $git_status = $doit->git_short_status;
1162             if ($git_status ne '' && $git_status ne '<') {
1163             error "Please check the git status";
1164             }
1165              
1166             # XXX TODO: check first if the current version already exists at CPAN
1167             my $tarfile = _get_tarfilename();
1168             if (!-e $tarfile) {
1169             dist();
1170             } else {
1171             info "Use existing tarball $tarfile";
1172             }
1173              
1174             print STDERR "Upload $tarfile? (y/n) ";
1175             if (!y_or_n()) {
1176             error "exiting release process";
1177             }
1178              
1179             $doit->system('cpan-upload', basename($tarfile));
1180              
1181             $doit->system('git', 'tag', '-a', '-m', "* $Doit_VERSION", $Doit_VERSION);
1182             $doit->system('git', 'push', 'origin', 'master', $Doit_VERSION);
1183             }
1184              
1185             sub ci_precheck {
1186             my @ci_systems = ('github-actions', 'appveyor');
1187              
1188             _check_clean_git();
1189              
1190             for my $ci_system (@ci_systems) {
1191             $doit->system('git', 'push', 'origin', "+HEAD:XXX-${ci_system}");
1192             }
1193              
1194             # check-ci is available here: https://github.com/eserte/srezic-misc/blob/master/scripts/check-ci
1195             $doit->system('check-ci', (map { "--$_" } @ci_systems));
1196             }
1197              
1198             sub _prove_path {
1199             my @directory_candidates = ($Config{bin}, dirname(realpath $^X));
1200             my @basename_candidates = ('prove', "prove$Config{version}");
1201             my @candidates = map {
1202             my $basename = $_;
1203             map {
1204             "$_/$basename";
1205             } @directory_candidates;
1206             } @basename_candidates;
1207             if ($^O eq 'MSWin32') {
1208             unshift @candidates, map { "$_.bat"} @candidates;
1209             }
1210             for my $candidate (@candidates) {
1211             if (-x $candidate) {
1212             return $candidate;
1213             }
1214             }
1215             error "No 'prove' for the current perl found";
1216             }
1217              
1218             sub _cover_path {
1219             my @candidates = ("$Config{bin}/cover");
1220             if ($^O eq 'MSWin32') {
1221             unshift @candidates, map { "$_.bat"} @candidates;
1222             }
1223             for my $candidate (@candidates) {
1224             if (-x $candidate) {
1225             return $candidate;
1226             }
1227             }
1228             error "No 'cover' for the current perl found";
1229             }
1230              
1231             sub _Build_PL_mode {
1232             tie my %Config, 'CustomConfig'; # no options here!
1233              
1234             if ($ENV{PERL_MB_OPT}) {
1235             require Text::ParseWords;
1236             push @ARGV, Text::ParseWords::shellwords($ENV{PERL_MB_OPT});
1237             }
1238              
1239             require Data::Dumper;
1240             my $argv_serialized = "\n" . '$Build_PL::ARGV = ' . Data::Dumper->new([\@ARGV], [])->Indent(1)->Useqq(1)->Sortkeys(1)->Terse(1)->Dump . ";\n\n";
1241             {
1242             if (-l "Build") { # formerly this used to be a symlink
1243             $doit->unlink("Build");
1244             }
1245             my $preamble = <<"EOF";
1246             #! $Config{perlpath}
1247             # MD5: $Build_PL_md5hex
1248             EOF
1249             $preamble .= $argv_serialized;
1250             $doit->write_binary({quiet=>1}, 'Build', $preamble . qq{# line 1 "Build.PL"\n} . $Build_PL_file_contents);
1251             $doit->chmod(0755, 'Build');
1252              
1253             eval {
1254             generate_META_json("MYMETA.json");
1255             generate_META_yml("MYMETA.yml" );
1256             };
1257             warning "Failure while generating MYMETA.* files, continue without.\nError was:\n$@" if $@;
1258             }
1259             exit;
1260             }
1261              
1262             # Return a string for adding to Dockerfile after the FROM line. Also
1263             # the 2nd parameter (which has to be an empty hashref) is filled with
1264             # further instructions to amend the Dockerfile (currently only
1265             # apt_get_update_opts).
1266             sub _fix_Dockerfile_for_dist {
1267             my($distro_spec, $add_opts_ref) = @_;
1268             if ($distro_spec =~ m{^debian:(wheezy|jessie|stretch|7|8|9)$}) {
1269             my $codename = $1;
1270             if ($codename eq '7') { $codename = 'wheezy' }
1271             elsif ($codename eq '8') { $codename = 'jessie' }
1272             elsif ($codename eq '9') { $codename = 'stretch' }
1273             # https://unix.stackexchange.com/questions/508724/failed-to-fetch-jessie-backports-repository
1274             $add_opts_ref->{apt_get_update_opts} = ' -o Acquire::Check-Valid-Until=false';
1275             # https://askubuntu.com/questions/74345/how-do-i-bypass-ignore-the-gpg-signature-checks-of-apt
1276             $add_opts_ref->{apt_get_install_opts} = ' -o APT::Get::AllowUnauthenticated=true';
1277             <
1278             RUN echo 'deb [check-valid-until=no] http://archive.debian.org/debian $codename main' > /etc/apt/sources.list
1279             RUN echo 'deb [check-valid-until=no] http://archive.debian.org/debian-security/ $codename/updates main' >> /etc/apt/sources.list
1280             EOF
1281             } elsif ($distro_spec =~ m{^perl:(5\.18\.\d+|.*stretch)$}) { # XXX add more perl versions based on jessie/stretch/... containers
1282             # docker perl images are based on debian base images; older
1283             # ones need patching
1284             $add_opts_ref->{apt_get_install_opts} = ' -o APT::Get::AllowUnauthenticated=true';
1285             <<'EOF';
1286             RUN perl -i -pe \
1287             's{deb http://(?:http.debian.net|deb.debian.org)/debian (\S+) main}{deb [check-valid-until=no] http://archive.debian.org/debian $1 main}; \
1288             s{deb http://security.debian.org.* ([^/]+)/updates main}{deb [check-valid-until=no] http://archive.debian.org/debian-security/ $1/updates main}; \
1289             $_ = "" if m{^deb .*-updates main}; \
1290             ' /etc/apt/sources.list
1291             EOF
1292             } else {
1293             $add_opts_ref->{apt_get_update_opts} = '';
1294             $add_opts_ref->{apt_get_install_opts} = '';
1295             '';
1296             }
1297             }
1298              
1299             sub _add_Dockerfile_invalidate_cmd {
1300             my $dockerfile_ref = shift;
1301             require POSIX;
1302             $$dockerfile_ref .= <<"EOF";
1303             # Just a hack to make sure that the following lines
1304             # are executed at least once a day
1305             RUN echo @{[ POSIX::strftime("%F", localtime) ]}
1306             EOF
1307             }
1308              
1309             sub _docker_add_distro_specific_files {
1310             my %opts = @_;
1311             my $dir = delete $opts{dir} || die 'Please specify dir';
1312             my $distro_spec = delete $opts{distro_spec} || die 'Please specify distro_spec';
1313             die 'Unhandled options: ' . join(' ', %opts) if %opts;
1314              
1315             if ($distro_spec eq 'centos:6' || $distro_spec eq 'centos:7') {
1316             $doit->mkdir("$dir/.distro_support");
1317             # Instructions from https://gcore.de/de/hilfe/linux/centos6-repo-nach-eol-anpassen.php
1318             if ($distro_spec eq 'centos:6') {
1319             $doit->write_binary("$dir/.distro_support/CentOS.repo", <<'EOF');
1320             [base]
1321             name=CentOS-6.10 - Base
1322             baseurl=http://vault.centos.org/6.10/os/$basearch/
1323             gpgcheck=1
1324             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
1325             enabled=1
1326             metadata_expire=never
1327              
1328             #released updates
1329             [updates]
1330             name=CentOS-6.10 - Updates
1331             baseurl=http://vault.centos.org/6.10/updates/$basearch/
1332             gpgcheck=1
1333             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
1334             enabled=1
1335             metadata_expire=never
1336              
1337             # additional packages that may be useful
1338             [extras]
1339             name=CentOS-6.10 - Extras
1340             baseurl=http://vault.centos.org/6.10/extras/$basearch/
1341             gpgcheck=1
1342             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
1343             enabled=1
1344             metadata_expire=never
1345              
1346             # additional packages that extend functionality of existing packages
1347             [centosplus]
1348             name=CentOS-6.10 - CentOSPlus
1349             baseurl=http://vault.centos.org/6.10/centosplus/$basearch/
1350             gpgcheck=1
1351             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
1352             enabled=0
1353             metadata_expire=never
1354              
1355             #contrib - packages by Centos Users
1356             [contrib]
1357             name=CentOS-6.10 - Contrib
1358             baseurl=http://vault.centos.org/6.10/contrib/$basearch/
1359             gpgcheck=1
1360             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6
1361             enabled=0
1362             metadata_expire=never
1363             EOF
1364             $doit->write_binary("$dir/.distro_support/epel.repo", <<'EOF');
1365             [epel]
1366             name=Extra Packages for Enterprise Linux 6 - $basearch
1367             baseurl=https://archives.fedoraproject.org/pub/archive/epel/6/$basearch
1368             enabled=1
1369             gpgcheck=1
1370             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
1371             metadata_expire=never
1372              
1373             [epel-debuginfo]
1374             name=Extra Packages for Enterprise Linux 6 - $basearch - Debug
1375             baseurl=https://archives.fedoraproject.org/pub/archive/epel/6/$basearch/debug
1376             enabled=0
1377             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
1378             gpgcheck=1
1379             metadata_expire=never
1380              
1381             [epel-source]
1382             name=Extra Packages for Enterprise Linux 6 - $basearch - Source
1383             baseurl=https://archives.fedoraproject.org/pub/archive/epel/6/SRPMS
1384             enabled=0
1385             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
1386             gpgcheck=1
1387             metadata_expire=never
1388             EOF
1389             } elsif ($distro_spec eq 'centos:7') {
1390             $doit->write_binary("$dir/.distro_support/CentOS.repo", <<'EOF');
1391             [base]
1392             name=CentOS-$releasever - Base
1393             baseurl=https://vault.centos.org/7.9.2009/os/$basearch/
1394             gpgcheck=1
1395             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
1396              
1397             [updates]
1398             name=CentOS-$releasever - Updates
1399             baseurl=https://vault.centos.org/7.9.2009/updates/$basearch/
1400             gpgcheck=1
1401             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
1402              
1403             [extras]
1404             name=CentOS-$releasever - Extras
1405             baseurl=https://vault.centos.org/7.9.2009/extras/$basearch/
1406             gpgcheck=1
1407             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
1408             EOF
1409             $doit->write_binary("$dir/.distro_support/epel.repo", <<'EOF');
1410             [epel]
1411             name=Extra Packages for Enterprise Linux 7 - $basearch
1412             baseurl=https://archives.fedoraproject.org/pub/archive/epel/7/$basearch
1413             enabled=1
1414             gpgcheck=1
1415             gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
1416             EOF
1417             } else {
1418             error "Should never happen";
1419             }
1420             $doit->write_binary("$dir/.distro_support/run.sh", <<'EOF');
1421             #! /bin/sh
1422             set -ex
1423             rm -f /etc/yum.repos.d/CentOS*.repo
1424             rm -f /etc/yum.repos.d/epel.repo
1425             cp .distro_support/CentOS.repo /etc/yum.repos.d/
1426             cp .distro_support/epel.repo /etc/yum.repos.d/
1427             yum clean all
1428             EOF
1429             $doit->chmod(0755, "$dir/.distro_support/run.sh");
1430             }
1431             }
1432              
1433             sub _check_clean_git {
1434             $doit->add_component('git');
1435              
1436             my $status = $doit->git_short_status;
1437             if ($status eq '<<') {
1438             error 'Working directory has uncomitted changes: ' . `git status`;
1439             }
1440             if ($status eq '*') {
1441             error 'Working directory has files not under git control (and not in .gitignore or .git/info/exclude): ' . `git status`;
1442             }
1443             }
1444              
1445             # REPO BEGIN
1446             # REPO NAME y_or_n /home/eserte/src/srezic-repository
1447             # REPO MD5 146cfcf8f954555fe0117a55b0ddc9b1
1448              
1449             #=head2 y_or_n
1450             #
1451             #Accept user input. Return true on 'y', return false on 'n', otherwise
1452             #ask again.
1453             #
1454             #A default may be supplied as an optional argument:
1455             #
1456             # y_or_n 'y';
1457             # y_or_n 'n';
1458             #
1459             #=cut
1460              
1461             sub y_or_n (;$) {
1462             my $default = shift;
1463             while () {
1464             chomp(my $yn = );
1465             if ($yn eq '' && defined $default) {
1466             $yn = $default;
1467             }
1468             if (lc $yn eq 'y') {
1469             return 1;
1470             } elsif (lc $yn eq 'n') {
1471             return 0;
1472             } else {
1473             print STDERR "Please answer y or n: ";
1474             }
1475             }
1476             }
1477             # REPO END
1478              
1479             {
1480             package CustomConfig;
1481             use Config '%Config';
1482             use Tie::Hash;
1483             our @ISA = qw(Tie::StdHash);
1484              
1485             sub TIEHASH {
1486             my($class, $custom_options) = @_;
1487             bless { custom_options => $custom_options }, $class;
1488             }
1489              
1490             sub FETCH {
1491             my($self, $key) = @_;
1492             if (exists $self->{custom_options}->{$key}) {
1493             return $self->{original_hash}->{$key};
1494             } else {
1495             return $Config::Config{$key};
1496             }
1497             }
1498             }
1499              
1500             1;
1501             __END__