rpm_spec.pm
author dcarbery
Mon, 23 Oct 2006 12:46:17 +0000
branchgnome-2-14
changeset 19195 352a4716f1f3
parent 4846 ffaaacbb77db
permissions -rw-r--r--
2006-10-23 Damien Carbery <[email protected]> * docs/ssa/*: Remove these, as they're now up on the JDS project pages under 'Tasks/Single Sys Admin'.

use strict;
use warnings;

package rpm_spec;

sub get_name_and_version ($);

use overload ('""' => \&get_name_and_version);	  

# Create a new rpm_spec object.
# Return undef if the spec_file is not found
sub new ($;$) {
    my $class = shift;
    my $spec_file = shift;
    my $arch = shift;
    my $self = {};

    if (! -r $spec_file) {
	return (undef);
    }

    $self->{file_name} = $spec_file;

    if (defined ($arch)) {
        $self->{default_build_architecture} = $arch;
    } else {
        $self->{default_build_architecture} = 'i386';
    }

    $self->{defines} = {};

    # some defaults:
    $self->{defines}->{_tmppath} = "/tmp";
    $self->{defines}->{_prefix} = "/usr";
    $self->{defines}->{_exec_prefix} = "/usr";
    $self->{defines}->{_defaultdocdir} = "%_prefix/doc";
    $self->{defines}->{_sysconfdir} = "/etc";
    $self->{defines}->{_datadir} = "%_prefix/share";
    $self->{defines}->{_bindir} = "%_exec_prefix/bin";
    $self->{defines}->{_libdir} = "%_exec_prefix/lib";
    $self->{defines}->{_docdir} = "%_datadir/doc";
    $self->{defines}->{_includedir} = "%_prefix/include";
    $self->{defines}->{_mandir} = "%_prefix/man";
    $self->{defines}->{_sbindir} = "%_exec_prefix/sbin";
    $self->{defines}->{__libtoolize} = "libtoolize";
    $self->{defines}->{optflags} = "-O3";

    return (bless $self, $class);
}

sub _partial_deref ($$) {
    my $self = shift;
    my $str = shift;

    my $def;
    my $val;

    while ($str =~ /\%{\?([^:]*):(.*)}/) {
        $def = $2;
        if (defined ($self->{defines}->{$1})) {
	    $str =~ s/\%{?([^:]*):(.*)}/$def/;
        } else {
            $str =~ s/\%{?([^:]*):(.*)}//;
	}
    }

    while ($str =~ /\%\(([^)]*)\)/) {
        $def = $1;
        $val = `$def`;
	$str =~ s/\%\(([^)]*)\)/$val/;
    }
    return $str;
}

sub _deref ($$) {
    my $self = shift;
    my $str = shift;

    my $def;
    my $val;

    while ($str =~ /\%(?:{([a-zA-Z_][a-zA-Z0-9_]*)}|([a-zA-Z_][a-zA-Z0-9_]*))/) {
	$def = $1;

	if (not defined $def) {
	    $def = $2;
	}

	$val = get_def_or_param ($self, $def);

	if (not defined ($val)) {
#	    print "WARNING: undefined symbol $def\n";
	    $val = "";
	}

	$str =~ s/\%{$def}/$val/g;
	$str =~ s/\%$def/$val/g;
    }

    return ($str);
}

sub _split_opts ($) {
    my $line = shift;
    my @tokens = ();

    $line =~ s/^\s*//;
    $line =~ s/\s*$//;
    while ($line ne "") {
        my $tok = "";
        while ($line =~ /^\S/) {
            if ($line =~ /^"((\\"|[^"])*)"/) {
                $tok = "$tok$1";
                $line =~ s/^"((\\"|[^"])*)"//;
            } elsif ($line =~ /^((\\"|[^\s"])+)/) {
                $tok = "$tok$1";
                $line =~ s/^((\\"|[^\s"])+)//;
            } else {
                print "ERROR: assertion failed: _split_opts ($line)\n"
            }
        }
        $tok =~ s/\\"/"/g;
        @tokens = (@tokens, $tok);
        $line =~ s/^\s*//;
    }
    return @tokens;
}

sub _read_spec ($) {
    my $self = shift;

    open SPEC_FILE, "<$self->{file_name}" or 
	$self->{error} = "Could not open file $self->{file_name} for reading",
	return (0);

    my $line;

    # initialisation
    my @packages = ();
    my @patches = ();
    my @sources = ();
    my @orig_sources = ();
    my @rpms = ();
    my @rpm_paths = ();
    my @files = ();

    $self->{rpm_loaded} = 0;
    $self->{orig_sources} = \@orig_sources;
    $self->{sources} = \@sources;
    $self->{patches} = \@patches;
    $self->{packages} = \@packages;
    $self->{rpms} = \@rpms;
    $self->{rpm_paths} = \@rpm_paths;
    $self->{files} = \@files;

    $self->{buildarchitecture} = $self->{default_build_architecture};

    my $add_ref;
    my $pkg_name = 'unknown';
    my $next_add_ref;

    while (1) {
	$line = <SPEC_FILE>;
	if (not defined ($line)) {
	    last;
	}

	$next_add_ref = undef;

	if ($line =~ /^\%{/) {
	    $line = $self->_partial_deref ($line);
	}

	if ($line =~ /^#/) {
	    # comment, nothing to do.
	} elsif ($line =~ /^[nN][aA][mM][eE]\s*:\s*(\S+)\s*$/) {
	    $pkg_name = $1;
	    my $ref = $self->{packages};
	    push (@$ref, $pkg_name);
	    $self->{$pkg_name} = {};
	    $self->{name} = $pkg_name;
	    my @provides = ($pkg_name);
	    $self->{$pkg_name}->{provides} = \@provides;
	    my @dummy = ();
	    $self->{$pkg_name}->{requires} = \@dummy;
	} elsif ($line =~ /^[vV][eE][rR][sS][iI][oO][nN]\s*:\s*(.*\S)\s*$/) {
	    if (not defined ($self->{version})) {
		$self->{version} = _deref($self, $1);
	    }
	    $self->{$pkg_name}->{version} = _deref($self, $1);
	    $self->{defines}->{version} = _deref($self, $1);
	} elsif ($line =~ /^[sS][uU][mM][mM][aA][rR][yY]\s*:\s*(.*\S)\s*$/) {
	    $self->{$pkg_name}->{summary} = $1;
	} elsif ($line =~ /^[gG][rR][oO][uU][pP]\s*:\s*(.*\S)\s*$/) {
	    $self->{$pkg_name}->{group} = $1;
	} elsif ($line =~ /^[pP][rR][oO][vV][iI][dD][eE][sS]\s*:\s*(.*\S)\s*$/) {	    my $ref = $self->{$pkg_name}->{provides};
	    push (@$ref, _deref($self, $1));
	} elsif ($line =~ /^[rR][eE][qQ][uU][iI][rR][eE][sS]\s*:\s*(.*\S)\s*$/) {
	    my $ref = $self->{$pkg_name}->{requires};
	    my @deps = split /,\s*/, _deref($self, $1);
	    push (@$ref, @deps);
	} elsif ($line =~ /^[pP][rR][eE][rR][eE][qQ]\s*:\s*(.*\S)\s*$/) {
	    my $ref = $self->{$pkg_name}->{requires};
	    my @deps = split /,\s*/, _deref($self, $1);
	    push (@$ref, @deps);
	} elsif ($line =~ /^[bB][uU][iI][lL][dD][rR][eE][qQ][uU][iI][rR][eE][sS]\s*:\s*(.*\S)\s*$/) {
	    my $ref = $self->{$pkg_name}->{requires};
	    my @deps = split /,\s*/, _deref($self, $1);
	    push (@$ref, @deps);
	} elsif ($line =~ /^[bB][uU][iI][lL][dD][aA][rR][cC][hH][iI][tT][eE][cC][tT][uU][rR][eE][sS]\s*:\s*(.*\S)\s*$/) {
	    $self->{buildarchitecture} = $1;
	} elsif ($line =~ /^[bB][uU][iI][lL][dD][aA][rR][cC][hH]\s*:\s*(.*\S)\s*$/) {
	    $self->{buildarchitecture} = $1;
	} elsif ($line =~ /^[eE][xX][cC][lL][uU][sS][iI][vV][eE][aA][rR][cC][hH]\s*:\s*(.*\S)\s*$/) {
	    $self->{buildarchitecture} = $1;
	} elsif ($line =~ /^\%package\s+-n\s+(\S+)$/) {
	    $pkg_name = $1;
	    my $ref = $self->{packages};
	    push (@$ref, $pkg_name);
	    $self->{$pkg_name} = {};
	    $self->{$pkg_name}->{version} = $self->{version};
	    my @provides = ($pkg_name);
	    $self->{$pkg_name}->{provides} = \@provides;
	    my @dummy = ();
	    $self->{$pkg_name}->{requires} = \@dummy;
	} elsif ($line =~ /^\%package\s+(\S+)$/) {
	    $pkg_name = $self->{name}."-$1";
	    my $ref = $self->{packages};
	    push (@$ref, $pkg_name);
	    $self->{$pkg_name} = {};
	    $self->{$pkg_name}->{version} = $self->{version};
	    my @provides = ($pkg_name);
	    $self->{$pkg_name}->{provides} = \@provides;
	    my @dummy = ();
	    $self->{$pkg_name}->{requires} = \@dummy;
	} elsif ($line =~ /^\%define\s+(\S+)\s+(.*\S)\s*$/) {
	    $self->{defines}->{$1} = _deref ($self, $2);
	} elsif ($line =~ /^\%description/) {
	    $self->{$pkg_name}->{description} = '';
	    $next_add_ref = \$self->{$pkg_name}->{description};
	} elsif ($line =~ /^\%files\s*(.*)\s*$/) {
	    my $files_opts = $1;
	    $files_opts =~ s/\s*$//;
	    my $pname = $self->{name};
	    if ($files_opts ne "") {
		if ($files_opts =~ /^([^-]\S*)/) {
		    $pname = "${pname}-$1";
		} elsif ($files_opts =~ /-n\s*(\S+)/) {
		    $pname = $1;
		}
	    }
	    my $ref = $self->{files};
	    push (@$ref, $pname);
	} elsif ($line =~ /^\%prep/) {
	} elsif ($line =~ /^Source([0-9]*)\s*:\s*(\S*)\s*$/) {
	    my $num = $1;
	    my $orig_src = $2;
	    my $src = $orig_src;
	    $src =~ s/^.*\/([^\/]*)$/$1/;
	    if ($num eq '') {
		$num = 0;
	    }
	    $self->{orig_sources}[$num] = _deref ($self, $orig_src);
	    $self->{sources}[$num] = _deref ($self, $src);
	} elsif ($line =~ /^Patch([0-9]*)\s*:\s*(\S*)\s*$/) {
	    if ($1 eq '') {
		$self->{patches}[0] = _deref ($self, $2);
	    } else {
		$self->{patches}[$1] = _deref ($self, $2);
	    }
	} elsif ($line =~ /^(\S+)\s*:\s*(.*\S)\s*$/) {
	    $self->{lc ($1)} = _deref ($self, $2);
	} elsif ($line =~ /^%setup.*$/) {
            my @setups = _split_opts ($line);
            while (@setups) {
                my $setup = shift (@setups);
                if ($setup eq '-n') {
                    $setup = _deref ($self, shift (@setups));
                    if ($setup ne '') {
                        $self->add_define('_build_src_dir_name', $setup);
                    }
                }
            }
	} else {
	    if (defined ($add_ref)) {
		$$add_ref = $$add_ref . $line;
		$next_add_ref = $add_ref;
	    } else {
		if (not defined ($self->{unknown})) {
		    $self->{unknown} = "$line";
		} else {
		    $self->{unknown} = $self->{unknown} . "$line";
		}
	    }
	}
	$add_ref = $next_add_ref;
    }

    close SPEC_FILE;

    @rpms = map "$_-$self->{$_}->{version}-$self->{release}.$self->{buildarchitecture}.rpm", @files;
    @rpm_paths = map "RPMS/$self->{buildarchitecture}/$_", @rpms;

    $self->{rpm_loaded} = 1;

    return 1;
}

sub get_param ($$) {
    my $self = shift;
    my $param = shift;

    if (not defined ($self->{rpm_loaded})) {
	if (not $self->_read_spec) {
	    return (undef);
	}
    }

    if (not defined ($self->{$param})) {
	$self->{error} = "Parameter \"$param\" not found in the spec file";
	return (undef);
    }

    return ($self->{$param});
}

sub get_param_array ($$) {
    my $self = shift;
    my $param = shift;

    if (not defined ($self->{rpm_loaded})) {
	if (not $self->_read_spec) {
	    return (undef);
	}
    }

    if (not defined ($self->{$param})) {
	$self->{error} = "Parameter \"$param\" not found in the spec file";
	return (undef);
    }

    my $ref = $self->{$param};
    return (@$ref);
}

sub get_pkg_param ($$$) {
    my $self = shift;
    my $pkg = shift;
    my $param = shift;

    if (not defined ($self->{rpm_loaded})) {
	if (not $self->_read_spec) {
	    return (undef);
	}
    }

    if (not defined ($self->{$pkg}->{$param})) {
	$self->{error} = "Parameter \"$param\" not defined for $pkg";
	return (undef);
    }

    return ($self->{$pkg}->{$param});
}

sub get_pkg_param_array ($$$) {
    my $self = shift;
    my $pkg = shift;
    my $param = shift;

    if (not defined ($self->{rpm_loaded})) {
	if (not $self->_read_spec) {
	    return (undef);
	}
    }

    if (not defined ($self->{$pkg}->{$param})) {
	$self->{error} = "Parameter \"$param\" not defined for $pkg";
	return (undef);
    }

    my $ref = $self->{$pkg}->{$param};
    return (@$ref);
}

sub get_def ($$) {
    my $self = shift;
    my $def = shift;

    if (not defined ($self->{rpm_loaded})) {
	if (not $self->_read_spec) {
	    return (undef);
	}
    }

    if (not defined ($self->{defines})) {
	return (undef);
    }

    if (not defined ($self->{defines}->{$def})) {
	return (undef);
    }

    return ($self->{defines}->{$def});
}

sub get_def_or_param ($$) {
    my $self = shift;
    my $def = shift;

    my $val = $self->get_def ($def);

    if (not defined ($val)) {
	$val = $self->get_param ($def);
    }

    if (not defined ($val)) {
	return (undef);
    }

    return ($val);
}

sub get_name ($) {
    my $self = shift;

    return ($self->get_param ("name"));
}

sub get_version ($) {
    my $self = shift;

    return ($self->get_param ("version"));
}

sub get_release ($) {
    my $self = shift;

    return ($self->get_param ("release"));
}

sub add_define ($$$) {
    my $self = shift;
    my $name = shift;
    my $value = shift;

    $self->{defines}->{$name} = $value;
}

sub get_name_and_version ($) {
    my $self = shift;

    my $name = $self->get_name;
    if (not defined ($name)) {
	return (undef);
    }

    my $version = $self->get_version;
    if (not defined ($version)) {
	return (undef);
    }

    my $release = $self->get_release;
    if (not defined ($release)) {
	return (undef);
    }

    return "$name-$version-$release";
}

1;