/) {
$state = 1;
}
if ($state == 1 && $line =~/\<\/p\>/) {
$state = 2;
}
}
close(HTML);
$cummul =~ s/\
//g;
$cummul =~ s/\r//g;
$cummul =~ s/\<\/p\>//g;
$cummul =~ s/^\s*//g;
if (length($cummul)>10) {
$description = $cummul;
}
}
#
# If all else fails, just take the first paragraph of the
# readme file
#
sub guess_description_from_readme {
my ($file) = @_;
my $state = 0;
my $cummul = "";
open(FILE, $file);
while () {
my $line = $_;
if ($state == 1 && $line =~ /^\n/ && length($cummul) > 80) {
$state = 2;
}
if ($state == 0 && length($line)>1) {
$state = 1;
}
if ($state == 1) {
$cummul = $cummul . $line;
}
if ($line =~ /(http\:\/\/.*$name.*\.org)/) {
my $u = $1;
if ($u =~ /bug/ || length($url) > 1) {
} else {
$url = $u;
}
}
}
close(FILE);
if (length($cummul) > 4 && length($description)<3) {
$description = $cummul;
}
}
#
# Glue all the guesses together
#
sub guess_description {
my ($directory) = @_;
@files = <$directory/README*>;
foreach (@files) {
guess_description_from_readme($_);
}
if (length($name)>2) {
guess_description_from_freshmeat($name);
}
@files = <$directory/*.spec*>;
foreach (@files) {
guess_description_from_spec($_);
}
guess_description_from_debian_control($directory);
$name =~ s/ //g;
@files = <$directory/$name.pc*>;
foreach (@files) {
guess_description_from_pkgconfig($_);
}
@files = <$directory/*.pc.*>;
foreach (@files) {
guess_description_from_pkgconfig($_);
}
@files = <$directory/*.pc>;
foreach (@files) {
guess_description_from_pkgconfig($_);
}
@files = <$directory/*.doap>;
foreach (@files) {
guess_description_from_doap($_);
}
if (length($summary) < 2) {
$summary = $description;
$summary =~ s/\n/ /g;
$summary =~ s/\s+/ /g;
if ($summary =~ /(.*?)\./) {
$summary = $1;
}
}
}
# end of Description / Summary section
#
######################################################################
#
# Build the package, and wait for rpm to complain about unpackaged
# files.... which we then use as basis for our %files section
#
sub guess_files_from_rpmbuild {
my $infiles = 0;
open(OUTPUTF, "rpmbuild --nodeps --define \"\%_sourcedir $orgdir \" -ba $name.spec 2>&1 |");
while () {
my $line2 = $_;
if ($infiles == 1 && $line2 =~ /RPM build errors/) {
$infiles = 2;
}
if ($infiles == 1 && $line2 =~ /^Building/) {
$infiles = 2;
}
if ($infiles == 1) {
$line2 =~ s/\s*//g;
push(@allfiles, $line2);
}
if ($line2 =~ / Installed \(but unpackaged\) file\(s\) found\:/) {
$infiles = 1;
}
}
close(OUTPUTF);
if (@allfiles == 0) {
print "Build failed ... stopping here.\n";
exit(0);
}
}
sub guess_files_from_oscbuild {
my $infiles = 0;
my $restart = 0;
my $mustrestart = 0;
my $rcount = 0;
my $done_python = 0;
system("osc addremove &> /dev/null");
system("osc ci -m \"Initial import by autospectacle\" &> /dev/null");
retry:
if ($restart > 0) {
write_yaml();
print "Restarting the build\n";
}
$restart = 0;
$infiles = 0;
$mustrestart = 0;
open(OUTPUTF, "osc build --no-verify $name.spec 2>&1 |");
while () {
my $line2 = $_;
# print "line is $line2\n";
if ($infiles == 1 && $line2 =~ /RPM build errors/) {
$infiles = 2;
}
if ($infiles == 1 && $line2 =~ /^Building/) {
$infiles = 2;
}
if ($infiles == 1) {
$line2 =~ s/\s*//g;
push(@allfiles, $line2);
}
if ($line2 =~ /No package \'(.*)\' found/) {
push_pkgconfig_buildreq("$1");
$restart = $restart + 1;
print " Adding pkgconfig($1) requirement\n";
}
if ($line2 =~ /Package requirements \((.*?)\) were not met/) {
$pkg = $1;
# deal with versioned pkgconfig's by removing the spaces around >= 's
$pkg =~ s/\>\=\s/\>\=/g;
$pkg =~ s/\s\>\=/\>\=/g;
$pkg =~ s/\=\s/\=/g;
$pkg =~ s/\s\=/\=/g;
my @req = split(/ /,$pkg);
foreach (@req) {
push_pkgconfig_buildreq("$_");
$restart = $restart + 1;
print " Adding pkgconfig($_) requirement\n";
}
}
if ($line2 =~ /which: no qmake/) {
$restart += 1;
push_pkgconfig_buildreq("Qt");
print " Adding Qt requirement\n";
}
if ($line2 =~ /Cannot find development files for any supported version of libnl/) {
$restart += 1;
push_pkgconfig_buildreq("libnl-1");
print " Adding libnl requirement\n";
}
if ($line2 =~ //) {
$restart += 1;
push(@buildreqs, "cmake");
print " Adding cmake requirement\n";
}
if ($line2 =~ /checking for (.*?)\.\.\. not_found/ || $line2 =~ /checking for (.*?)\.\.\. no/ || $line2 =~ /checking (.*?)\.\.\. no/) {
$pkg = $1;
while (($key,$value) = each %failed_commands) {
if ($pkg eq $key) {
push(@buildreqs, $value);
print " Adding $value requirement\n";
$restart += $restart + 1;
$mustrestart = 1;
}
}
}
if ($line2 =~ /checking for [a-zA-Z0-9\_]+ in (.*?)\.\.\. no/) {
$pkg = $1;
while (($key,$value) = each %failed_libs) {
if ($pkg eq $key) {
push(@buildreqs, $value);
print " Adding $value requirement\n";
$restart += $restart + 1;
$mustrestart = 1;
}
}
}
if ($line2 =~ /-- Could NOT find ([a-zA-Z0-9]+)/) {
$pkg = $1;
while (($key,$value) = each %failed_libs) {
if ($pkg eq $key) {
push(@buildreqs, $value);
print " Adding $value requirement\n";
$restart += $restart + 1;
$mustrestart = 1;
}
}
}
if ($line2 =~ /fatal error\: (.*)\: No such file or directory/) {
$pkg = $1;
while (($key,$value) = each %failed_headers) {
if ($pkg eq $key) {
push_pkgconfig_buildreq($value);
print " Adding $value requirement\n";
$restart += $restart + 1;
}
}
}
if ($line2 =~ /checking for UDEV\.\.\. no/) {
print " Adding pkgconfig(udev) requirement\n";
push_pkgconfig_buildreq("udev");
}
if ($line2 =~ /checking for Apache .* module support/) {
print " Adding pkgconfig(httpd-devel) requirement\n";
push(@buildreqs, "httpd-devel");
if ($rcount < 3) {
$restart = $restart + 1;
}
}
if ($line2 =~ /([a-zA-Z0-9\-\_]*)\: command not found/i) {
my $cmd = $1;
my $found = 0;
while (($key,$value) = each %failed_commands) {
if ($cmd eq $key) {
push(@buildreqs, $value);
print " Adding $value requirement\n";
$restart += $restart + 1;
$mustrestart = 1;
$found = 1;
}
}
if ($found < 1) {
print " Command $cmd not found!\n";
}
}
if ($line2 =~ /checking for.*in -ljpeg... no/) {
push(@buildreqs, "libjpeg-devel");
print " Adding libjpeg-devel requirement\n";
$restart = $restart + 1;
}
if ($line2 =~ /fatal error\: zlib\.h\: No such file or directory/) {
push(@buildreqs, "zlib-devel");
print " Adding zlib-devel requirement\n";
$restart = $restart + 1;
}
if ($line2 =~ /error\: xml2-config not found/) {
push_pkgconfig_buildreq("libxml-2.0");
print " Adding libxml2-devel requirement\n";
$restart = $restart + 1;
}
if ($line2 =~ /checking \"location of ncurses\.h file\"/) {
push(@buildreqs, "ncurses-devel");
print " Adding ncurses-devel requirement\n";
$restart = $restart + 1;
}
if (($line2 =~ / \/usr\/include\/python2\.6$/ || $line2 =~ / to compile python extensions/) && $done_python == 0) {
push(@buildreqs, "python-devel");
print " Adding python-devel requirement\n";
$restart = $restart + 1;
$done_python = 1;
}
if ($line2 =~ /error: must install xorg-macros 1.6/) {
push_pkgconfig_buildreq("xorg-macros");
print " Adding xorg-macros requirement\n";
$restart = $restart + 1;
}
if ($line2 =~ /installing .*?.gmo as [a-zA-Z0-9\-\.\/\_]+?\/([a-zA-Z0-9\-\_\.]+)\.mo$/) {
my $loc = $1;
if ($loc eq $localename) {} else {
print " Changing localename from $localename to $loc\n";
$localename = $loc;
$restart = $restart + 1;
}
}
if ($infiles == 0 && $line2 =~ / Installed \(but unpackaged\) file\(s\) found\:/) {
$infiles = 1;
}
}
close(OUTPUTF);
if (@allfiles == 0 || $mustrestart > 0) {
if ($restart >= 1)
{
$rcount = $rcount + 1;
if ($rcount < 10) {
goto retry;
}
}
print "Build failed ... stopping here.\n";
exit(0);
}
}
sub process_rpmlint {
my $infiles = 0;
if ($oscmode == 0) {
return;
}
print "Verifying package ....\n";
system("osc addremove &> /dev/null");
system("osc ci -m \"Final import by autospectacle\" &> /dev/null");
open(OUTPUTF, "osc build --no-verify $name.spec 2>&1 |");
while () {
my $line2 = $_;
# print "line is $line2\n";
if ($infiles == 1 && $line2 =~ /RPM build errors/) {
$infiles = 2;
}
if ($infiles == 1 && $line2 =~ /^Building/) {
$infiles = 2;
}
if ($infiles == 1) {
$line2 =~ s/\s*//g;
push(@allfiles, $line2);
}
if ($infiles == 0 && $line2 =~ / Installed \(but unpackaged\) file\(s\) found\:/) {
$infiles = 1;
}
}
close(OUTPUTF);
}
sub guess_name_from_url {
my ($bigurl) = @_;
@spliturl = split(/\//, $bigurl);
while (@spliturl > 1) {
shift(@spliturl);
}
my $tarfile = $spliturl[0];
# Ensure correct name resolution from .zip&tgz archives
$tarfile =~ s/\.zip/\.tar/;
$tarfile =~ s/\.tgz/\.tar/;
$tarfile =~ s/\_/\-/g;
if ($tarfile =~ /(.*?)\-([0-9\.\-\~]+.*?)\.tar/) {
$name = $1;
$version = $2;
$version =~ s/\-/\_/g;
}
}
############################################################################
#
# Output functions
#
sub print_name_and_description
{
my @lines;
print OUTFILE "Name : $name\n";
print OUTFILE "Version : $version\n";
print OUTFILE "Release : 1\n";
# remove dupes
undef %saw;
@saw{@groups} = ();
@out = sort keys %saw;
if (@out == 1) {
foreach (@out) {
print OUTFILE "Group : $_\n";
}
} else {
print OUTFILE "Group : $group\n";
}
#
# Work around spectacle bug
$summary =~ s/\:\s/ /g;
$summary =~ s/^([a-z])/\u$1/ig;
$summary =~ s/\@//g;
$summary = substr($summary, 0, 79);
$summary =~ s/\.^//g;
if (length($summary) < 1) {
$summary = "TO BE FILLED IN";
}
#
print OUTFILE "Summary : $summary\n";
print OUTFILE "Description: |\n";
$description =~ s/"/\"/g;
$description =~ s/\@//g;
@lines = split(/\n/, $description);
foreach (@lines) {
print OUTFILE " $_\n";
}
if (length($url)>1) {
print OUTFILE "URL : $url\n";
}
# remove dupes
undef %saw;
@saw{@sources} = ();
@out = sort keys %saw;
print OUTFILE "Sources : \n";
foreach (@out) {
$source = $_;
$source =~ s/$version/\%\{version\}/g;
print OUTFILE " - $source\n";
}
if (@patches > 0) {
print OUTFILE "Patches: \n";
foreach (@patches) {
my $patch = $_;
print OUTFILE " - $patch\n";
}
}
print OUTFILE "\n";
if (length($configure)>2) {
print OUTFILE "Configure : $configure\n";
}
if (length($localename) > 2) {
print OUTFILE "LocaleName : $localename\n";
}
if (length($builder) > 2) {
print OUTFILE "Builder : $builder\n";
}
}
sub write_makefile
{
open(MAKEFILE, ">Makefile");
print MAKEFILE "PKG_NAME := $name\n";
print MAKEFILE "SPECFILE = \$(addsuffix .spec, \$(PKG_NAME))\n";
print MAKEFILE "YAMLFILE = \$(addsuffix .yaml, \$(PKG_NAME))\n";
print MAKEFILE "\n";
print MAKEFILE "include /usr/share/packaging-tools/Makefile.common\n";
close(MAKEFILE);
}
sub write_changelog
{
open(CHANGELOG, ">$name.changes");
$date = ` date +"%a %b %d %Y"`;
chomp($date);
print CHANGELOG "* $date - Autospectacle - $version\n";
print CHANGELOG "- Initial automated packaging\n";
close(CHANGELOG);
}
sub write_yaml
{
open(OUTFILE, ">$name.yaml");
print_name_and_description();
print_license();
print_pkgconfig();
print_buildreq();
print_files();
print_devel();
print_doc();
close(OUTFILE);
write_makefile();
write_changelog();
system("rm $name.spec 2>/dev/null");
system("specify &> /dev/null");
if ($oscmode > 0) {
system("osc addremove");
system("osc ci -m \"Import by autospectacle\" &> /dev/null");
}
}
sub write_bbfile
{
my $curdir = `pwd`;
chomp($curdir);
if ($python == 1) {
$name =~ s/python-//;
$name = lc("python-" . $name);
}
if (-e "$curdir/${name}_$version.bb") {
print "Wont overwrite file:";
print "$curdir/${name}_$version.bb, exiting\n";
return;
}
open(BBFILE, ">${name}_$version.bb");
print BBFILE "SUMMARY = \"$summary\"\n";
print BBFILE "DESCRIPTION = \"$description\"\n";
print BBFILE "HOMEPAGE = \"$homepage\"\n";
if ($python == 1) {
print BBFILE "SRCNAME = \"$summary\"\n";
}
print BBFILE "LICENSE = \"@license\"\n";
print BBFILE "LIC_FILES_CHKSUM = \"";
foreach (keys %lic_files) {
print BBFILE "file://" . basename($_) . ";md5=$lic_files{$_} \\\n";
}
print BBFILE "\"\n\n";
if (@license <= 0) {
print "Can NOT get license from package source files.\n";
print "Please update the LICENSE and LIC_FILES_CHKSUM manually.\n";
}
if (@buildreqs > 0) {
my %saw;
my @out = grep(!$saw{$_}++,@buildreqs);
print BBFILE "DEPENDS = \"@out\"\n\n";
};
if (@rdepends > 0) {
print BBFILE "RDEPENDS_\$\{PN\} += \"";
foreach (@rdepends) {
print BBFILE "$_ \\\n\t";
}
print BBFILE "\"\n";
}
print BBFILE 'PR = "r0"' . "\n";
if ($python == 1) {
print BBFILE "PV = \"$pversion\"\n\n";
}
print BBFILE "SRC_URI = \"";
foreach (@sources) {
print BBFILE "$_ \\\n";
}
print BBFILE "\"\n\n";
print BBFILE "SRC_URI[md5sum] = \"$md5sum\"\n";
print BBFILE "SRC_URI[sha256sum] = \"$sha256sum\"\n\n";
if ($python == 1) {
print BBFILE "S = \"\${WORKDIR}/\${SRCNAME}-\${PV}\"\n";
}
if (@inherits) {
print BBFILE "inherit ";
foreach (@inherits) {
print BBFILE "$_ ";
}
print BBFILE "\n";
}
close(BBFILE);
print "Create bb file: $curdir/${name}_$version.bb\n";
}
sub calculate_sums
{
@_ = basename $dir;
my $md5output = `md5sum @_`;
$md5output =~ /^([a-zA-Z0-9]*) /;
$md5sum = $1;
chomp($md5sum);
my $sha256output = `sha256sum @_`;
$sha256output =~ /^([a-zA-Z0-9]*) /;
$sha256sum = $1;
chomp($sha256sum);
}
############################################################################
#
# Main program
#
if ( @ARGV < 1 ) {
print "Usage: $0 [-r] \n";
exit(1);
}
# Recusive parsing of python dependencies using
# easy_install
my $recurse_python = 0;
if ($ARGV[0] eq "-r") {
$recurse_python = 1;
shift @ARGV;
}
if (@ARGV > 1) {
my $i = 1;
while ($i < @ARGV) {
my $patch = $ARGV[$i];
print "Adding patch $patch\n";
push(@patches, $patch);
$i++;
}
}
setup_licenses();
setup_files_rules();
setup_group_rules();
setup_pkgconfig_ban();
setup_failed_commands();
if (-e ".osc/_packages") {
$oscmode = 1;
}
my $tmpdir = tempdir();
$dir = $ARGV[0];
guess_name_from_url($dir);
push(@sources, $dir);
#system("cd $tmpdir; curl -s -O $dir");
$orgdir = `pwd`;
chomp($orgdir);
my $outputdir = $name;
if (! $name) {
$outputdir = basename $dir;
}
mkpath($outputdir);
chdir($outputdir);
print "Downloading package: $dir\n";
system("wget --quiet $dir") == 0 or die "Download $dir failed.";
calculate_sums($outputdir);
print "Unpacking to : $tmpdir\n";
my @tgzfiles = <$orgdir/$outputdir/*.tgz>;
foreach (@tgzfiles) {
my $tgz = basename $_;
my $tar = $tgz;
$tar =~ s/tgz/tar\.gz/g;
$dir =~ s/tgz/tar\.gz/g;
system("mv $orgdir/$outputdir/$tgz $orgdir/$outputdir/$tar");
guess_name_from_url($dir);
}
#
# I really really hate the fact that meego deleted the -a option from tar.
# this is a step backwards in time that is just silly.
#
my @sourcetars = <$orgdir/$outputdir/*\.tar\.bz2 $orgdir/$outputdir/*\.tar\.gz $orgdir/$outputdir/*\.zip>;
if ( length @sourcetars == 0) {
print "Can NOT find source tarball. Exiting...\n";
exit (1);
}
if (defined($sourcetars[0]) and $sourcetars[0] =~ ".*\.tar\.bz2") {
system("cd $tmpdir; tar -jxf $sourcetars[0] &>/dev/null");
} elsif (defined($sourcetars[0]) and $sourcetars[0] =~ ".*\.tar\.gz") {
system("cd $tmpdir; tar -zxf $sourcetars[0] &>/dev/null");
} elsif (defined($sourcetars[0]) and $sourcetars[0] =~ ".*\.zip") {
system("cd $tmpdir; unzip $sourcetars[0] &>/dev/null");
}
print "Parsing content ....\n";
my @dirs = <$tmpdir/*>;
foreach (@dirs) {
$dir = $_;
}
$fulldir = $dir;
if ( -e "$dir/setup.py" ) {
$python = 1;
$tmp_stools = `grep -r setuptools $dir/setup.py`;
if (length($tmp_stools) > 2) {
push(@inherits, "setuptools");
} else {
push(@inherits, "distutils");
}
$templic = `cd $dir; python setup.py --license;`;
$templic =~ s/[\r\n]+//g;
push(@license, $templic);
$summary = `cd $dir; python setup.py --name`;
$summary =~ s/[\r\n]+//g;
$description = `cd $dir; python setup.py --description`;
$description =~ s/[\r\n]+//g;
$homepage = `cd $dir; python setup.py --url`;
$homepage =~ s/[\r\n]+//g;
$pversion = `cd $dir; python setup.py -V`;
$pversion =~ s/[\r\n]+//g;
# $findoutput = `cd $dir; python setup.py --requires`;
# if (length($findoutput) < 3) {
$findoutput = `find $dir/*.egg-info/ -name "requires.txt" 2>/dev/null`;
# }
@findlist = split(/\n/, $findoutput);
foreach (@findlist) {
push(@rawpythondeps, `sed -e '/^\$/d' "$_" | sed '/^\\[/d'`);
chomp(@rawpythondeps);
push(@rdepends, `sed -e 's/python-//g' "$_" | sed '/^\\[/d'`);
chomp(@rdepends);
if ($recurse_python == 1) {
foreach (@rawpythondeps) {
my $ptempdir = tempdir();
$purl = `easy_install -aeb $ptempdir "$_" 2>/dev/null`;
$purl =~ s/#.*//g;
@purllist = $purl =~ m/Downloading (.*:\/\/.*\n)/g;
chomp(@purllist);
# Remove empty lines
@purllist = grep(/\S/, @purllist);
# Recursively create recipes for dependencies
if (@purllist != 0) {
if (fork) {
# Parent, do nothing
} else {
# child, execute
print "Recursively creating recipe for: $purllist[0]\n";
exec("cd .. ; create-recipe -r $purllist[0]");
}
}
}
wait;
}
foreach $item (@rdepends) {
@pyclean = split(/(\=|\<|\>).*/, $item);
if (defined($pyclean[0])) {
$item = lc("python-" . $pyclean[0]);
}
}
}
}
if ( -e "$dir/autogen.sh" ) {
$configure = "autogen";
$uses_configure = 1;
push(@inherits, "autotools");
}
if ( -e "$dir/BUILD-CMAKE" ) {
$configure = "cmake";
push(@buildreqs, "cmake");
$uses_configure = 1;
push(@inherits, "cmake");
}
if ( -e "$dir/configure" ) {
$configure = "";
}
my @files = <$dir/configure\.*>;
my $findoutput = `find $dir -name "configure.ac" 2>/dev/null`;
my @findlist = split(/\n/, $findoutput);
foreach (@findlist) {
push(@files, $_);
}
foreach (@files) {
process_configure_ac("$_");
}
$findoutput = `find $dir -name "*.pro" 2>/dev/null`;
@findlist = split(/\n/, $findoutput);
foreach (@findlist) {
process_qmake_pro("$_");
}
if (-e "$dir/$name.pro") {
$builder = "qmake";
push_pkgconfig_buildreq("Qt");
push(@inherits, "qmake2");
}
#
# This is a good place to generate configure.in
#
if (length($configure) > 2) {
if ($configure eq "autogen") {
system("cd $dir ; ./autogen.sh &> /dev/null");
}
}
@files = <$dir/configure>;
foreach (@files) {
process_configure("$_");
}
if ($uses_configure == 0) {
$configure = "none";
}
@files = <$dir/docs/license.txt>;
foreach (@files) {
guess_license_from_file("$_");
}
@files = <$dir/COPY*>;
foreach (@files) {
guess_license_from_file("$_");
}
@files = <$dir/LICENSE*>;
foreach (@files) {
guess_license_from_file("$_");
}
@files = <$dir/GPL*>;
foreach (@files) {
guess_license_from_file("$_");
}
if ($python != 1) {
guess_description($dir);
}
#
# Output of bbfile file
#
write_bbfile();
chdir($orgdir);
exit 0;
#
# Output of the yaml file
#
if ($oscmode == 1) {
print "Creating OBS project $name ...\n";
system("osc mkpac $name &> /dev/null");
system("mkdir $name &> /dev/null");
chdir($name);
system("mv ../$name*\.tar\.* .");
}
write_yaml();
print "Building package ....\n";
if ($oscmode == 0) {
guess_files_from_rpmbuild();
} else {
guess_files_from_oscbuild();
}
apply_files_rules();
$printed_subpackages = 0;
write_yaml();
process_rpmlint();
print "Spectacle creation complete.\n";