#!/usr/bin/perl eval 'exec /usr/bin/perl -S $0 ${1+"$@"}' if $running_under_some_shell; #!/usr/bin/perl use strict; use File::Copy; use File::Temp qw/ tempfile tempdir /; # get libreoffice-build version from the given libreoffice-build sources sub get_config_version($) { my ($lo_core_dir) = @_; my $version; open (CONFIGURE, "$lo_core_dir/configure.in") || die "can't open \"$lo_core_dir/configure.in\" for reading: $!\n"; while (my $line = ) { chomp $line; if ($line =~ /AC_INIT\s*\(\s*libreoffice-build\s*,\s*([\w\.]*)\)/) { $version="$1"; } } close (CONFIGURE); return $version; } # increment the version for a test build: # + add 'a' if the version ended with a number # + bump the letter otherwise sub inc_test_version($) { my ($version) = @_; my $lastchar = chop $version; my $new_version; if ($lastchar =~ /\d/) { return "$version" . "$lastchar" . "a"; } elsif ($lastchar =~ /\w/) { # select next letter alhabeticaly: a->b, b->c, ... $lastchar =~ tr/0a-zA-Z/a-zA-Z0/; return "$version" . "$lastchar"; } else { die "Can't generate test version from \"$version$lastchar\n"; } } sub get_release_version($$$$) { my ($config_version, $state_config_version, $state_release_version, $inc_version) = @_; my $release_version; if (defined $state_config_version && defined $state_release_version && "$state_config_version" eq "$config_version") { $release_version = "$state_release_version"; } else { $release_version = "$config_version"; } if ( defined $inc_version ) { $release_version = inc_test_version($release_version); } return $release_version; } # copy files to temp dir; showing a progress; using a black list sub copy_dir_filter_and_show_progress($$) { my ($source_dir, $target_dir) = @_; print "Copying \"$source_dir\" -> \"$target_dir\"..."; # copy sources from git and show progress system ("cd $source_dir && " . "git archive --format=tar HEAD | " . " tar -xf - -C $target_dir --checkpoint=500 --checkpoint-action=exec=\"echo -n .\"") && die "Error: copying failed: $!\n"; print "\n"; } # copy the piece lo source directory into a tmp directory # omit the .git subdirectories sub copy_lo_piece_to_tempdir($$$) { my ($piece_dir, $piece, $piece_tarball_name) = @_; my $tempdir = tempdir( 'libreoffice-XXXXXX', DIR => File::Spec->tmpdir ); mkdir "$tempdir/$piece_tarball_name" || die "Can't create directory \"$tempdir/$piece_tarball_name\": $!\n"; copy_dir_filter_and_show_progress("$piece_dir", "$tempdir/$piece_tarball_name"); return $tempdir; } sub generate_lo_piece_changelog($$$) { my ($lo_piece_clone, $lo_piece_release_dir, $piece) = @_; print "Generating changelog for $piece...\n"; # print "1:$lo_piece_clone, 2:$lo_piece_release_dir, 3:$piece\n"; system ("cd $lo_piece_clone && " . "git log --date=short --pretty='format:@%cd %an <%ae> [%H]%n%n%s%n%n%e%b' | " . " sed -e 's|^\([^@]\)|\t\1|' -e 's|^@||' >$lo_piece_release_dir/ChangeLog" ) && die "Error: generating failed: $!\n"; } sub run_autoreconf($$) { my ($dir, $piece) = @_; print "Running autoreconf for $piece...\n"; system ("cd $dir && " . "autoreconf -f -i && " . "rm -rf autom4te.cache && " . "cd - >/dev/null 2>&1") && die "Error: autoreconf failed: $!\n"; } sub generate_sources_version_file($$) { my ($dir, $release_version) = @_; open (VERFILE, ">$dir/sources.ver") || die "Can't open $dir/sources.ver: $!\n"; print VERFILE "lo_sources_ver=$release_version\n"; close VERFILE; } sub generate_tarball($$$) { my ($dir, $tarball, $tar_compress_option) = @_; print "Creating $tarball..."; # generate the tarball in the current directory; avoid "./" prefix in the stored paths; show progress system ("tar -c $tar_compress_option -f $tarball -C $dir --checkpoint=500 --checkpoint-action=exec=\"echo -n .\" --transform=\"s|^\./||\" .") && die "Error: releasing failed: $!\n"; print "\n"; } sub generate_md5($) { my ($filename) = @_; print "Generating MD5...\n"; system ("md5sum $filename >$filename.md5") && die "Error: releasing failed: $!\n"; } sub default_releases_state_file($) { my ($lo_core_dir) = @_; my $rootdir = $lo_core_dir; $rootdir =~ s/^(.*?)\/?[^\/]+\/?$/$1/; my $releases_state_file; if ($rootdir) { $releases_state_file = "$rootdir/.releases"; } else { $releases_state_file = ".releases"; } return "$releases_state_file"; } sub load_releases_state($) { my ($releases_state_file) = @_; my $state_config_version; my $state_release_version; if (open (STATE, "$releases_state_file")) { while (my $line = ) { chomp $line; if ($line =~ /^\s*configure_version\s*=\s*(.*)$/) { $state_config_version = "$1"; } elsif ($line =~ /^\s*released_version\s*=\s*(.*)$/) { $state_release_version = "$1"; } } close (STATE); } return $state_config_version, $state_release_version; } sub save_releases_state($$$) { my ($releases_state_file, $config_version, $release_version) = @_; open (STATE, '>', "$releases_state_file") || die "Can't open \"$releases_state_file\" for writing: $!\n"; print STATE "configure_version = $config_version\n"; print STATE "released_version = $release_version\n"; close (STATE); } sub remove_tempdir($) { my ($tempdir) = @_; # print "Cleaning $tempdir...\n"; system ("rm -rf $tempdir") && die "Error: rm failed: $!\n"; } sub check_if_file_exists($$) { my ($file, $force) = @_; if (-e $file) { if (defined $force) { print "Warning: $file already exists and will be replaced!\n"; } else { die "Error: $file alrady exists.\n". " Use --force if you want to replace it.\n"; } } } sub check_if_already_released($$$$$$$) { my ($lo_core_tarball_name, $p_piece_tarball_name, $force, $bzip2, $xz, $pack_lo_core, $pack_lo_pieces) = @_; check_if_file_exists("$lo_core_tarball_name.tar.bz2", $force) if ($pack_lo_core && defined $bzip2); check_if_file_exists("$lo_core_tarball_name.tar.xz", $force) if ($pack_lo_core && defined $xz); if ($pack_lo_pieces) { foreach my $tarball_name ( values %{$p_piece_tarball_name} ) { check_if_file_exists("$tarball_name.tar.bz2", $force) if (defined $bzip2); check_if_file_exists("$tarball_name.tar.xz", $force) if (defined $xz); } } } sub prepare_piece_sources($$$$) { my ($piece_dir, $release_version, $piece, $piece_tarball_name) = @_; # prepare sources my $temp_dir = copy_lo_piece_to_tempdir($piece_dir, $piece, $piece_tarball_name); generate_lo_piece_changelog($piece_dir, "$temp_dir/$piece_tarball_name", $piece); run_autoreconf("$temp_dir/$piece_tarball_name", $piece) if ($piece eq 'core'); generate_sources_version_file("$temp_dir/$piece_tarball_name", $release_version) if ($piece eq 'core'); return $temp_dir; } sub pack_piece_sources($$$$) { my ($temp_dir, $md5, $tarball, $tar_compress_option) = @_; generate_tarball($temp_dir, $tarball, $tar_compress_option); generate_md5($tarball) if (defined $md5); } sub generate_piece_tarball($$$$$$$) { my ($piece_dir, $release_version, $piece, $md5, $bzip2, $xz, $piece_tarball_name) = @_; my $temp_dir = prepare_piece_sources($piece_dir, $release_version, $piece, $piece_tarball_name); pack_piece_sources($temp_dir, $md5, "$piece_tarball_name.tar.bz2", "--bzip2") if (defined $bzip2); pack_piece_sources($temp_dir, $md5, "$piece_tarball_name.tar.xz", "--xz") if (defined $xz); remove_tempdir($temp_dir); } sub generate_tarballs($$$$$$$$$$) { my ($source_dir, $release_version, $md5, $bzip2, $xz, $lo_core_tarball_name, $p_piece_tarball_name, $pack_lo_core, $pack_lo_pieces, $is_lo_core_dir) = @_; if ($pack_lo_core) { generate_piece_tarball($source_dir, $release_version, "core", $md5, $bzip2, $xz, $lo_core_tarball_name); } if ($pack_lo_pieces) { my $piece_dir = $source_dir; foreach my $piece ( keys %{$p_piece_tarball_name} ) { print "\n--- Generating $piece ---\n"; $piece_dir = "$source_dir/clone/$piece" if ($is_lo_core_dir); generate_piece_tarball($piece_dir, $release_version, $piece, $md5, $bzip2, $xz, $p_piece_tarball_name->{$piece}); } } } sub usage() { print "This tool helps to pack the libreoffice-build and piece sources\n\n" . "Usage:\n". "\tlo-pack-sources [--help]\n" . "\t [--force] [--md5] [--bzip2] [--xz]\n" . "\t [--version][--set-version=] [--inc-version]\n" . "\t [--no-lo-core] [--no-lo-pieces] [--piece=]\n" . "\t [dir]\n\n" . "Options:\n\n" . "\t--help: print this help\n" . "\t--force: replace an already existing release of the same version\n" . "\t--md5: generate md5 sum for the final tarball\n" . "\t--bzip2: generate tarballs compressed by bzip2\n" . "\t--xz: generate tarballs compressed by xz (default)\n" . "\t--version: just print version of the released package but do not\n" . "\t\trelease it; the version is affected by the other options, e.g.\n" . "\t\t--inc-version\n" . "\t--set-version: force another version\n" . "\t--inc-version: increment the latest version; there is a difference\n" . "\t\tbetween test release (default) and final (not yet supported)\n" . "\t--no-lo-core: do not pack the libreoffice-core tarball\n" . "\t--no-lo-pieces: do not pack the extra LO piece sources\n" . "\t--piece=: pack just a single piece, .e.g. \"help\",\n" . "\tdir: path of the source directory, either libreoffice-build or piece\n"; } my $ptf; my $md5; my $bzip2; my $xz; my $inc_version; my $config_version; my $set_version; my $get_config_version; my $release_version; my $pack_lo_core=1; my $pack_lo_pieces=1; my $source_dir; my $releases_state_file; my $state_config_version; my $state_release_version; my $lo_core_tarball_name; my $lo_core_tempdir; my $force; my $verbose=1; my $is_lo_core_dir=0; my @pieces=("binfilter", "dictionaries", "help", "translations"); my %piece_tarball_name; ################### # Arguments parsing ################### for my $arg (@ARGV) { if ($arg eq '--help' || $arg eq '-h') { usage; exit 0; } elsif ($arg eq '--force') { $force=1; } elsif ($arg eq '--md5') { $md5=1; } elsif ($arg eq '--bzip2') { $bzip2=1; } elsif ($arg eq '--xz') { $xz=1; } elsif ($arg eq '--version') { $get_config_version=1; $verbose = undef; } elsif ($arg eq '--inc-version') { $inc_version=1 } elsif ($arg =~ m/--set-version=(.*)/) { $set_version="$1"; } elsif ($arg eq '--no-lo-core') { $pack_lo_core=0; } elsif ($arg eq '--no-lo-pieces') { $pack_lo_pieces=0; } elsif ($arg =~ m/--piece=(.*)/) { # process just one piece and do not pack libreoffice-build @pieces=(); push @pieces, "$1"; $pack_lo_core=0; } elsif ($arg =~ /^-/ ) { die "Error: unknown option: $arg\n"; } else { if (! defined $source_dir) { $source_dir = $arg; } else { die "Error: Too many arguments $arg\n"; } } } ################### # Initial checks ################### unless ( defined $source_dir ) { die "Error: undefined source directory, try --help\n"; } unless ( -d "$source_dir" ) { die "Error: is not a directory: $source_dir\n"; } # check if it is a valid libreoffice-core directory $is_lo_core_dir=1 if (-f "$source_dir/autogen.sh" && -f "$source_dir/config_host.mk.in"); # all tarballs are generated from the libreoffice-core directory if (@pieces > 1 && $is_lo_core_dir == 0 ) { die "Error: \"$source_dir\" is not a valid libreoffice-core directory\n"; } # just a single piece tarball can be generated from piece directory; version must be explicitly set in this case if (@pieces == 1 && $is_lo_core_dir == 0 && ! defined $set_version ) { die "Error: version must be set using the --set-version= option\n" unless (defined $set_version); } if (defined $set_version && defined $inc_version) { die "Error: --set-version and --inc-version options can't be used together\n"; } # default compression $xz = 1 unless (defined $xz || defined $bzip2); ################### # Main logic ################### print "Source: $source_dir\n" if ($verbose); if ($is_lo_core_dir) { # detect some paths $releases_state_file = default_releases_state_file($source_dir) unless (defined $releases_state_file); # detect versions $config_version = get_config_version($source_dir); ($state_config_version, $state_release_version) = load_releases_state($releases_state_file); if (defined $set_version) { $release_version = "$set_version"; } else { $release_version = get_release_version($config_version, $state_config_version, $state_release_version, $inc_version); } } else { # must be single piece release with predefined version $release_version = "$set_version"; } # define tarball names $lo_core_tarball_name = "libreoffice-core-$release_version"; foreach my $piece (@pieces) { if (-d "$source_dir/clone/$piece") { $piece_tarball_name{$piece} = "libreoffice-$piece-$release_version"; } else { print "Warning: $piece sources are not available -> skipping\n"; } } print "Default version : $config_version\n" if ($verbose && defined $config_version); print "Last used version : $state_release_version\n" if ($verbose && defined $state_release_version); print "New version : $release_version\n" if ($verbose); # do the real job if ( defined $get_config_version ) { print "$release_version\n"; } else { check_if_already_released($lo_core_tarball_name, \%piece_tarball_name, $force, $bzip2, $xz, $pack_lo_core, $pack_lo_pieces); # give a chance to stop the process print ("\nWaiting 3 seconds...\n"); sleep 3; generate_tarballs($source_dir, $release_version, $md5, $bzip2, $xz, $lo_core_tarball_name, \%piece_tarball_name, $pack_lo_core, $pack_lo_pieces, $is_lo_core_dir); if ( defined $releases_state_file ) { save_releases_state($releases_state_file, $config_version, $release_version); } }