Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
crossover / opt / cxoffice / bin / cxrepackage
Size: Mime:
#!/usr/bin/perl
# (c) Copyright 2006-2013. CodeWeavers, Inc.
use warnings;
use strict;

# Portable which(1) implementation
sub cxwhich($$;$)
{
    my ($dirs, $app, $noexec)=@_;
    if ($app =~ /^\//)
    {
        return $app if ((-x $app or $noexec) and -f $app);
    }
    elsif ($app =~ /\//)
    {
        require Cwd;
        my $path=Cwd::cwd() . "/$app";
        return $path if ((-x $path or $noexec) and -f $path);
    }
    else
    {
        foreach my $dir (split /:/, $dirs)
        {
            return "$dir/$app" if ($dir ne "" and (-x "$dir/$app" or $noexec) and -f "$dir/$app");
        }
    }
    return undef;
}

# Fast dirname() implementation
sub _cxdirname($)
{
    my ($path)=@_;
    return undef if (!defined $path);
    return "." if ($path !~ s!/+[^/]+/*$!!s);
    return "/" if ($path eq "");
    return $path;
}

# Locate where CrossOver is installed by looking for the directory
# where the cxmenu script is located, unwinding symlinks on the way
sub locate_cx_root(;$)
{
    my ($fallback)=@_;
    my $argv0=cxwhich($ENV{PATH},$0);
    $argv0=$0 if (!defined $argv0);
    if ($argv0 !~ m+^/+)
    {
        require Cwd;
        $argv0=Cwd::cwd() . "/$argv0";
    }
    my $dir=_cxdirname($argv0);
    my $bindir=$dir;
    $bindir =~ s%/lib$%/bin%;
    while (!-x "$bindir/cxmenu" or !-f "$bindir/cxmenu")
    {
        last if (!-l $argv0);
        $argv0=readlink($argv0);
        $argv0="$dir/$argv0" if ($argv0 !~ m+^/+);
        $dir=_cxdirname($argv0);
        $bindir=$dir;
        $bindir =~ s%/lib$%/bin%;
    }
    $bindir =~ s%/(?:\./)+%/%g;
    $bindir =~ s%/\.$%%;
    $ENV{CX_ROOT}=_cxdirname($bindir);
    if ((!-x "$ENV{CX_ROOT}/bin/cxmenu" or !-f "$ENV{CX_ROOT}/bin/cxmenu") and
        $fallback)
    {
        $ENV{CX_ROOT}=$fallback;
    }
    if (!-x "$ENV{CX_ROOT}/bin/cxmenu" or !-f "$ENV{CX_ROOT}/bin/cxmenu")
    {
        my $name0=$0;
        $name0 =~ s+^.*/++;
        print STDERR "$name0:error: could not find CrossOver in '$ENV{CX_ROOT}'\n";
        exit 1;
    }
    return $ENV{CX_ROOT};
}

BEGIN {
    unshift @INC, locate_cx_root() . "/lib/perl";
}
use CXLog;
use CXUtils;


{
    package CXLinuxDebian;
    use CXDebian;
    use base "CXDebian";

    sub new($$$$$)
    {
        my ($class, $deb_package, $productid, $buildroot, $installroot)=@_;
        my $self=CXDebian::new($class, $buildroot);
        if ($self)
        {
            $self->{deb_package}=$deb_package;
            $self->{conf_filter}=join("|", $productid, CXUtils::get_product_id(), CXUtils::get_builtin_product_id());
            $self->{installroot}=$installroot;
            $self->{docdir}="/usr/share/doc/$deb_package";
        }
        return $self;
    }

    sub map_directory($$$)
    {
        my ($self, $root, $dir)=@_;
        if ($self->{doc})
        {
            return "$self->{docdir}/$dir";
        }
        return undef if ($dir eq ".mojosetup"); # MojoSetup
        return "" if ($dir eq "bin");
        return "" if ($dir eq "doc"); # Non-Debian doc directory
        return "$self->{docdir}/$dir" if ($dir =~ s!^doc/!!);
        return "" if ($dir eq "etc");
        return "" if ($dir eq "lib" or $dir eq "lib/python");
        return undef if ($dir eq "support");
        return "$self->{installroot}/$dir";
    }

    sub map_file($$$)
    {
        my ($self, $root, $file)=@_;
        if ($self->{doc})
        {
            return undef if ($file eq "changelog.Debian.gz");
            return undef if ($file eq "copyright");
            return "$self->{docdir}/$file";
        }
        return undef if ($file eq ".uninstall"); # MojoSetup
        if ($file =~ m!^bin/[^/]+$! and -l "$root/$file")
        {
            # Don't package cxmenu's shortcut files
            my $dst=readlink "$root/$file";
            return undef if ($dst =~ m%/desktopdata/cxmenu/Shortcuts/%);
        }
        return undef if ($file =~ m%^etc/(?:$self->{conf_filter})\.conf%);
        return undef if ($file =~ m%^lib/python/.*\.pyc$%);
        return undef if ($file eq "doc"); # Debian's doc symlink
        return "$self->{docdir}/$file" if ($file =~ s!^doc/!!);
        return "$self->{docdir}/changelog" if ($file eq "changelog.txt");
        return "$self->{docdir}/changelog.html" if ($file eq "changelog.html");
        return "$self->{docdir}/license.txt" if ($file eq "license.txt");
        return "$self->{docdir}/README" if ($file eq "README");
        return "$self->{installroot}/$file";
    }
}

{
    package CXLinuxRPM;
    use CXRPM;
    use base "CXRPM";

    sub new($$)
    {
        my ($class, $productid)=@_;
        my $self=$class->SUPER::new();
        $self->{conf_filter}=join("|", $productid, CXUtils::get_product_id(), CXUtils::get_builtin_product_id());
        return $self;
    }

    sub map_directory($$$$)
    {
        my ($self, $root, $dir, $mode)=@_;
        if ($self->{doc})
        {
            push @{$self->{dirs}}, "doc/$dir";
            return ("doc/$dir", $mode);
        }
        return undef if ($dir eq ".mojosetup"); # MojoSetup
        return undef if ($dir eq "support");
        push @{$self->{dirs}}, $dir;
        return ($dir, $mode);
    }

    sub map_file($$$$)
    {
        my ($self, $root, $file, $mode)=@_;
        if ($self->{doc})
        {
            # Debian specific
            return undef if ($file =~ /^(?:changelog\.Debian(?:\.gz)?|copyright)$/);
            if ($file =~ /^changelog(\.gz)?$/)
            {
                push @{$self->{docfiles}}, "changelog.txt";
                return ("changelog.txt.gz", $mode, 1) if (defined $1);
                return ("changelog.txt", $mode);
            }
            if ($file =~ /^changelog\.html(\.gz)?$/)
            {
                push @{$self->{docfiles}}, "changelog.html";
                return ("changelog.html.gz", $mode, 1) if (defined $1);
                return ("changelog.html", $mode);
            }
            if ($file =~ /^README(\.gz)?$/)
            {
                push @{$self->{docfiles}}, "README";
                return ($file, $mode, defined $1);
            }
            if ($file =~ /^license\.txt(\.gz)?$/)
            {
                push @{$self->{files}}, "license.txt";
                return ($file, $mode, defined $1);
            }
            push @{$self->{files}}, "doc/$file";
            return ("doc/$file", $mode);
        }

        # MojoSetup
        return undef if ($file eq ".uninstall");
        # Debian's doc link
        return undef if ($file eq "doc" and -l "$root/$file");
        return undef if ($file =~ m%^etc/(?:$self->{conf_filter})\.conf%);
        if ($file =~ m!^bin/[^/]+$! and -l "$root/$file")
        {
            # Don't package cxmenu's shortcut files
            my $dst=readlink "$root/$file";
            return undef if ($dst =~ m%/desktopdata/cxmenu/Shortcuts/%);
        }
        elsif ($file =~ m%^(?:README|changelog\.txt|changelog\.html)$%)
        {
            push @{$self->{docfiles}}, $file;
            return ($file, $mode);
        }
        push @{$self->{files}}, $file;
        return ($file, $mode);
    }
}


{
    package CXRSunpkg;
    use CXSunpkg;
    use base "CXSunpkg";

    sub new($$$$)
    {
        my ($class, $pkgdir, $productid, $compress)=@_;
        my $self=$class->SUPER::new($pkgdir, $compress);
        $self->{conf_filter}=join("|", $productid, CXUtils::get_product_id(), CXUtils::get_builtin_product_id()) if ($self);
        return $self;
    }

    sub package_directory($$$$)
    {
        my ($self, $root, $dir, $mode)=@_;
        # Note: No need to handle Debian's special documentation directory here
        return undef if ($dir eq ".mojosetup"); # MojoSetup
        return undef if ($dir eq "support");
        return $mode;
    }

    sub package_file($$$$)
    {
        my ($self, $root, $file, $mode)=@_;
        # Note: No need to handle Debian's special documentation directory here
        return undef if ($file eq ".uninstall"); # MojoSetup
        return undef if ($file =~ m%^etc/(?:$self->{conf_filter})\.conf%);
        if ($file =~ m!^bin/[^/]+$! and -l "$root/$file")
        {
            # Don't package cxmenu's shortcut files
            my $dst=readlink "$root/$file";
            return undef if ($dst =~ m%/desktopdata/cxmenu/Shortcuts/%);
        }
        return $mode;
    }
}

sub create_tmpdir()
{
    my $tmpdir;
    if (-w $ENV{CX_ROOT})
    {
        $tmpdir="$ENV{CX_ROOT}/support/desktopdata";
        if ($> == 0)
        {
            # Make sure desktopdata is created with the proper permissions
            my $umask=umask 0022;
            my $rc=!cxmkpath($tmpdir);
            umask $umask;
            if ($rc)
            {
                cxerr("unable to create the '$tmpdir' directory: $@\n");
                return undef;
            }
        }
    }
    else
    {
        require CXBottle;
        $tmpdir=CXBottle::get_user_dir() . "/desktopdata";
    }

    return "$tmpdir/cxrepackage-$$";
}

sub move_package($$$$)
{
    my ($dir, $pattern, $label, $name)=@_;
    my @files=CXUtils::cxglob($dir, $pattern);
    if (!@files)
    {
        cxerr("unable to find the $name package\n");
        return 0;
    }
    if (@files > 1)
    {
        cxerr("found more than one $name package: @files\n");
        return 0;
    }
    my $filename=cxbasename($files[0]);
    if (!defined cxmv($files[0], $filename))
    {
        cxerr("unable to move '$files[0]' to the current directory: $!\n");
        return 0;
    }
    rmdir cxdirname($files[0]);
    print "$label: $filename\n";
    return 1;
}


# Process command-line options
my $opt_release;
my $opt_deb;
my $opt_deb32;
my $opt_deb64;
my $opt_rpm;
my $opt_sunpkg;
my $opt_sunzip;
my $opt_productid;
my $opt_verbose;
my $opt_help;
require CXOpts;
my $cxopts=CXOpts->new();
$cxopts->add_options(["release=s"     => \$opt_release,
                      "deb"           => \$opt_deb,
                      "deb32"         => \$opt_deb32,
                      "deb64"         => \$opt_deb64,
                      "rpm"           => \$opt_rpm,
                      "sunpkg"        => \$opt_sunpkg,
                      "sunzip=s"      => \$opt_sunzip,
                      "productid=s"   => \$opt_productid,
                      "verbose!"      => \$opt_verbose,
                      "?|h|help"      => \$opt_help
                     ]);
my $err=$cxopts->parse();
CXLog::fdopen(2) if ($opt_verbose);


# Validate the command line options
my $usage;
if ($err)
{
    cxerr("$err\n");
    $usage=2;
}
elsif ($opt_help)
{
    $usage=0;
}
else
{
    if (!defined $opt_release)
    {
        cxerr("you must specify the --release option\n");
        $usage=2;
    }
    if (!$opt_deb and !$opt_deb32 and !$opt_deb64 and !$opt_rpm and !$opt_sunpkg)
    {
        cxerr("you must specify the package format using --deb, --deb32, --deb64, --rpm or --sunpkg\n");
        $usage=2;
    }
    if (defined $opt_sunzip)
    {
        if (!$opt_sunpkg)
        {
            cxerr("--sunzip can only be used with --sunpkg\n");
            $usage=2;
        }
        if (defined $opt_sunzip)
        {
            require CXSunpkg;
            if (!CXSunpkg::is_valid_zip_format($opt_sunzip))
            {
                cxerr("unknown compression format '$opt_sunzip' for the --sunzip option\n");
                $usage=2;
            }
        }
    }
    $opt_sunzip||="bzip2";
    if (defined $opt_productid)
    {
        $err=CXUtils::check_product_id($opt_productid);
        if (defined $err)
        {
            cxerr("$err\n");
            $usage=1;
        }
    }
    else
    {
        $opt_productid=CXUtils::get_product_id();
    }
}


# Print usage
if (defined $usage)
{
    my $name0=cxname0();
    if ($usage)
    {
        cxerr("try '$name0 --help' for more information\n");
        exit $usage;
    }
    print "Usage: $name0 --release RELEASE [--deb] [--deb32] [--deb64] [--rpm] [--sunpkg]\n";
    print "                   [--sunzip FMT] [--productid ID] [--verbose] [--help]\n";

    print "\n";
    print "Builds a Debian or RPM package from the current CrossOver installation.\n";

    print "\n";
    print "Options:\n";
    print "  --release RELEASE Specifies the package's release number\n";
    print "  --deb             Generates a 32-bit multiarch Debian package\n";
    print "  --deb32           Generates a 32-bit Debian package\n";
    print "  --deb64           Generates a 64-bit Debian package\n";
    print "  --rpm             Generates an RPM package\n";
    print "  --sunpkg          Generates a Sun package\n";
    print "    --sunzip FMT      The compression format to use. The default is bzip2, the\n";
    print "                      others are none, gzip and 7z\n";
    print "  --help, -h        Shows this help message\n";

    print "\n";
    print "Advanced options:\n";
    print "  --productid ID    Use ID as the package's product id\n";
    print "                    The product id is what differentiates CrossOver packages\n";
    print "                    from each other in side-by-side installations\n";
    print "  --verbose         Output more information about what is going on\n";
    exit 0;
}

my $rc;
if ($opt_deb or $opt_deb32 or $opt_deb64)
{
    my @types;
    push @types, "march" if ($opt_deb);
    push @types, "i386" if ($opt_deb32);
    push @types, "amd64" if ($opt_deb64);
    foreach my $type (@types)
    {
        # Use a pseudo-loop to simplify error handling
        my ($tmpdir, $lrc);
      error: while (1)
        {
            my $template="$ENV{CX_ROOT}/share/crossover/cxrepackage/deb.";
            if (!-f "${template}build")
            {
                cxerr("the --deb/--deb32/--deb64 options are not supported on this platform\n");
                $lrc=1;
                last error;
            }

            $tmpdir=create_tmpdir();
            if (!defined $tmpdir)
            {
                $lrc=1;
                last error;
            }

            my $deb_package=($opt_productid eq CXUtils::get_builtin_product_id() ?
                             "crossover" :
                             $opt_productid);
            $deb_package="$deb_package-legacy" if ($type ne "march");
            $deb_package="ia32-$deb_package" if ($type eq "amd64");
            my $installroot="/opt/$opt_productid";
            my $cxdebian=CXLinuxDebian->new($deb_package, $opt_productid, $tmpdir, $installroot);
            if (!$cxdebian)
            {
                $lrc=1;
                last error;
            }

            # Register the files to install
            if (!$cxdebian->add_tree($ENV{CX_ROOT}))
            {
                $lrc=1;
                last error;
            }
            my $docdir=readlink("$ENV{CX_ROOT}/doc");
            if (defined $docdir and -d $docdir)
            {
                $cxdebian->{doc}=1;
                if (!$cxdebian->add_tree($docdir))
                {
                    $lrc=1;
                    last error;
                }
            }

            # Generate the Debian control files
            my ($arch, $csa, $cma)=($type eq "march") ? ("i386", "#", "") : ($type, "", "#");
            my $cwma=($type eq "i386") ? "#" : "";
            my $today=CXUtils::rfc822time(time);
            foreach my $file ("debian/build",
                              "debian/changelog",
                              "debian/compat",
                              "debian/control",
                              "debian/copyright",
                              "debian/doc-base",
                              "debian/lintian-overrides",
                              "debian/postinst",
                              "debian/postrm",
                              "debian/preinst",
                              "debian/prerm",
                              "debian/rules")
            {
                my $src="$template" . cxbasename($file);
                $lrc=CXUtils::generate_from_template("$tmpdir/$file", $src,
                    {arch            => $arch,
                     csa             => $csa,
                     cma             => $cma,
                     cwma            => $cwma,
                     deb_package     => $deb_package,
                     product_id      => $opt_productid,
                     release         => $opt_release,
                     date            => $today
                    });
                last error if ($lrc);
            }

            # Finally, generate the Debian package
            $lrc=$cxdebian->build($arch);
            last error if ($lrc);

            # Move the Debian package to the current directory
            my ($label, $bits)=($type eq "march") ? ("deb", "32") :
                               ($arch eq "i386")  ? ("deb32", "32") :
                                                    ("deb64", "64") ;
            $lrc=1 if (!move_package($tmpdir, "*.deb", $label, "${bits}bit Debian"));

            last;
        }

        if (defined $tmpdir)
        {
            require File::Path;
            File::Path::rmtree($tmpdir);
        }
        $rc=1 if ($lrc);
    }
}

if ($opt_rpm)
{
    # Use a pseudo-loop to simplify error handling
    my ($tmpdir, $lrc);
  error: while (1)
    {
        my $template="$ENV{CX_ROOT}/share/crossover/cxrepackage/rpm.spec";
        if (!-f $template)
        {
            cxerr("the --rpm option is not supported on this platform\n");
            $lrc=1;
            last error;
        }

        my $cxlinuxrpm=CXLinuxRPM->new($opt_productid);
        if (!defined $cxlinuxrpm->{rpmbuild})
        {
            $lrc=1;
            last error;
        }

        # Copy $CX_ROOT so it has the expected directory structure
        $tmpdir=create_tmpdir();
        if (!defined $tmpdir)
        {
            $lrc=1;
            last error;
        }

        my $imagedir="$tmpdir/image";
        if (!$cxlinuxrpm->create_image($ENV{CX_ROOT}, "$imagedir/opt/$opt_productid"))
        {
            $lrc=1;
            last error;
        }
        my $docdir=readlink("$ENV{CX_ROOT}/doc");
        if (defined $docdir and -d $docdir)
        {
            if (!cxmkpath("$imagedir/opt/$opt_productid/doc", 0700))
            {
                cxerr("unable to create the '$imagedir/opt/$opt_productid/doc' directory: $@\n");
                $lrc=1;
                last error;
            }
            my $mode=(stat($docdir))[2] & 07777;
            chmod($mode, "$imagedir/opt/$opt_productid/doc");

            $cxlinuxrpm->{doc}=1;
            if (!$cxlinuxrpm->create_image($docdir, "$imagedir/opt/$opt_productid"))
            {
                $lrc=1;
                last error;
            }
        }

        # Generate the spec file
        my $rpm_package=($opt_productid eq CXUtils::get_builtin_product_id() ?
                         "crossover" :
                         $opt_productid);
        require CXRPM;
        my $dir_list=join("\n", map { "\%dir " . CXRPM::escape_file_path("/opt/$opt_productid/$_") } sort @{$cxlinuxrpm->{dirs}});
        my $file_list=join("\n", (map { "%doc " . CXRPM::escape_file_path("/opt/$opt_productid/$_") } sort @{$cxlinuxrpm->{docfiles}}), (map { CXRPM::escape_file_path("/opt/$opt_productid/$_") } sort @{$cxlinuxrpm->{files}}));

        my $specfile="$tmpdir/rpm.spec";
        $lrc=CXUtils::generate_from_template($specfile, $template,
            {rpm_package => $rpm_package,
             product_id  => $opt_productid,
             release     => $opt_release,
             buildroot   => $imagedir,
             cx_root     => "/opt/$opt_productid",
             dir_list    => $dir_list,
             file_list   => $file_list
            });
        last error if ($lrc);

        # Finally, generate the RPM package
        $lrc=$cxlinuxrpm->build($tmpdir, "i386");
        last error if ($lrc);

        # Move the rpm package to the current directory
        $lrc=1 if (!move_package($tmpdir, "i?86/*.rpm", "rpm", "RPM"));

        last;
    }

    if (defined $tmpdir)
    {
        require File::Path;
        File::Path::rmtree($tmpdir);
    }
    $rc=1 if ($lrc);
}

if ($opt_sunpkg)
{
    # Use a pseudo-loop to simplify error handling
    my ($pkgdir, $lrc);
  error: while (1)
    {
        my $template="$ENV{CX_ROOT}/share/crossover/cxrepackage/sunpkg.";
        if (!-f "${template}pkginfo")
        {
            cxerr("the --sunpkg option is not supported on this platform\n");
            $lrc=1;
            last error;
        }

        require CXSunpkg;
        my $sun_package=($opt_productid eq CXUtils::get_builtin_product_id() ?
                         "cxoffice" :
                         $opt_productid);
        $sun_package=CXSunpkg::compute_package_name("", $sun_package);
        cxlog("Creating solaris tree for $sun_package\n");
        $pkgdir="$sun_package";
        $pkgdir="sunpkg/$pkgdir" if ($pkgdir eq $ENV{CX_ROOT});
        if (-d $pkgdir)
        {
            require File::Path;
            if (!File::Path::rmtree($pkgdir))
            {
                cxerr("unable to delete the '$pkgdir' directory\n");
                $lrc=1;
                last error;
            }
        }

        my $cxrsunpkg=CXRSunpkg->new($pkgdir, $opt_productid, $opt_sunzip);
        last if (!$cxrsunpkg);

        my ($mday, $mon, $year)=(localtime(time))[3,4,5];
        $mon++;
        $year+=1900;
        my $today=sprintf("%d/%02d/%02d", $year, $mon, $mday);

        if (!$cxrsunpkg->generate_install_files($template,
            ["pkginfo",
             "install/checkinstall",
             "install/compver",
             "install/copyright",
             "install/depend",
             "install/postinstall",
             "install/preremove"],
            {sun_package     => $sun_package,
             product_id      => $opt_productid,
             release         => $opt_release,
             date            => $today,
             noverify        => ($opt_sunzip eq "none" ? "" : "none")
            }))
        {
            $lrc=1;
            last error;
        }

        # Note: No need to handle Debian's special documentation directory here
        if (!$cxrsunpkg->add_tree("reloc/\$PKGINST", $ENV{CX_ROOT}, 0755) or
            !$cxrsunpkg->finalize("\$PKGINST"))
        {
            $lrc=1;
            last error;
        }

        print "sunpkg: $pkgdir\n";
        last;
    }

    if ($lrc and defined $pkgdir)
    {
        require File::Path;
        File::Path::rmtree($pkgdir);
    }
    $rc=1 if ($lrc);
}

exit($rc || 0);