From: Felix Fietkau Date: Sun, 23 Sep 2007 02:39:01 +0000 (+0000) Subject: add initial version of a package feeds management script X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=f8dfc5757363cfcfda561ecde2ee75d9a0e5d16f;p=openwrt%2Fstaging%2Fstintel.git add initial version of a package feeds management script SVN-Revision: 8973 --- diff --git a/feeds.conf b/feeds.conf new file mode 100644 index 0000000000..31dbe5f0f3 --- /dev/null +++ b/feeds.conf @@ -0,0 +1,2 @@ +src-svn packages https://svn.openwrt.org/openwrt/packages +src-svn xwrt http://svn.berlios.de/svnroot/repos/xwrt/trunk/package diff --git a/include/scan.mk b/include/scan.mk index dfe80fabaa..28f526aa33 100644 --- a/include/scan.mk +++ b/include/scan.mk @@ -1,15 +1,15 @@ include $(TOPDIR)/include/verbose.mk TMP_DIR:=$(TOPDIR)/tmp -all: tmp/.$(SCAN_TARGET) +all: $(TMP_DIR)/.$(SCAN_TARGET) include $(TOPDIR)/include/host.mk SCAN_TARGET ?= packageinfo SCAN_NAME ?= package SCAN_DIR ?= package -TARGET_STAMP:=tmp/info/.files-$(SCAN_TARGET).stamp -FILELIST:=tmp/info/.files-$(SCAN_TARGET)-$(SCAN_COOKIE) +TARGET_STAMP:=$(TMP_DIR)/info/.files-$(SCAN_TARGET).stamp +FILELIST:=$(TMP_DIR)/info/.files-$(SCAN_TARGET)-$(SCAN_COOKIE) ifeq ($(IS_TTY),1) define progress @@ -22,8 +22,8 @@ else endif define PackageDir - tmp/.$(SCAN_TARGET): tmp/info/.$(SCAN_TARGET)-$(1) - tmp/info/.$(SCAN_TARGET)-$(1): $(SCAN_DIR)/$(2)/Makefile $(SCAN_STAMP) $(foreach DEP,$(DEPS_$(SCAN_DIR)/$(1)/Makefile) $(SCAN_DEPS),$(wildcard $(if $(filter /%,$(DEP)),$(DEP),$(SCAN_DIR)/$(1)/$(DEP)))) + $(TMP_DIR)/.$(SCAN_TARGET): $(TMP_DIR)/info/.$(SCAN_TARGET)-$(1) + $(TMP_DIR)/info/.$(SCAN_TARGET)-$(1): $(SCAN_DIR)/$(2)/Makefile $(SCAN_STAMP) $(foreach DEP,$(DEPS_$(SCAN_DIR)/$(1)/Makefile) $(SCAN_DEPS),$(wildcard $(if $(filter /%,$(DEP)),$(DEP),$(SCAN_DIR)/$(1)/$(DEP)))) { \ $$(call progress,Collecting $(SCAN_NAME) info: $(SCAN_DIR)/$(2)) \ echo Source-Makefile: $(SCAN_DIR)/$(2)/Makefile; \ @@ -33,10 +33,10 @@ define PackageDir endef $(FILELIST): - rm -f tmp/info/.files-$(SCAN_TARGET)-* + rm -f $(TMP_DIR)/info/.files-$(SCAN_TARGET)-* $(call FIND_L, $(SCAN_DIR)) $(SCAN_EXTRA) -mindepth 1 $(if $(SCAN_DEPTH),-maxdepth $(SCAN_DEPTH)) -name Makefile | xargs grep -HE 'call (Build/DefaultTargets|Build(Package|Target)|.+Package)' | sed -e 's#^$(SCAN_DIR)/##' -e 's#/Makefile:.*##' | uniq > $@ -tmp/info/.files-$(SCAN_TARGET).mk: $(FILELIST) +$(TMP_DIR)/info/.files-$(SCAN_TARGET).mk: $(FILELIST) ( \ cat $< | awk '{print "$(SCAN_DIR)/" $$0 "/Makefile" }' | xargs grep -HE '^ *SCAN_DEPS *= *' | awk -F: '{ gsub(/^.*DEPS *= */, "", $$2); print "DEPS_" $$1 "=" $$2 }'; \ awk -v deps="$$DEPS" '{ \ @@ -47,7 +47,7 @@ tmp/info/.files-$(SCAN_TARGET).mk: $(FILELIST) true; \ ) > $@ --include tmp/info/.files-$(SCAN_TARGET).mk +-include $(TMP_DIR)/info/.files-$(SCAN_TARGET).mk $(TARGET_STAMP): ( \ @@ -60,9 +60,9 @@ $(TARGET_STAMP): } \ ) -tmp/.$(SCAN_TARGET): $(TARGET_STAMP) $(SCAN_STAMP) +$(TMP_DIR)/.$(SCAN_TARGET): $(TARGET_STAMP) $(SCAN_STAMP) $(call progress,Collecting $(SCAN_NAME) info: merging...) - cat $(FILELIST) | awk '{gsub(/\//, "_", $$0);print "tmp/info/.$(SCAN_TARGET)-" $$0}' | xargs cat > $@ + cat $(FILELIST) | awk '{gsub(/\//, "_", $$0);print "$(TMP_DIR)/info/.$(SCAN_TARGET)-" $$0}' | xargs cat > $@ $(call progress,Collecting $(SCAN_NAME) info: done) echo diff --git a/include/toplevel.mk b/include/toplevel.mk index 0a8bdedc3e..a9ca3a2806 100644 --- a/include/toplevel.mk +++ b/include/toplevel.mk @@ -30,15 +30,17 @@ endif SCAN_COOKIE?=$(shell echo $$$$) export SCAN_COOKIE +prepare-mk: FORCE ; + prepare-tmpinfo: FORCE mkdir -p tmp/info +$(NO_TRACE_MAKE) -s -f include/scan.mk SCAN_TARGET="packageinfo" SCAN_DIR="package" SCAN_NAME="package" SCAN_DEPS="$(TOPDIR)/include/package*.mk" SCAN_DEPTH=4 SCAN_EXTRA="" +$(NO_TRACE_MAKE) -s -f include/scan.mk SCAN_TARGET="targetinfo" SCAN_DIR="target/linux" SCAN_NAME="target" SCAN_DEPS="profiles/*.mk $(TOPDIR)/include/kernel*.mk" SCAN_DEPTH=2 SCAN_EXTRA="" SCAN_MAKEOPTS="TARGET_BUILD=1" for type in package target; do \ f=tmp/.$${type}info; t=tmp/.config-$${type}.in; \ - [ "$$t" -nt "$$f" ] || ./scripts/metadata.pl $${type}_config < "$$f" > "$$t" || { rm -f "$$t"; echo "Failed to build $$t"; false; break; }; \ + [ "$$t" -nt "$$f" ] || ./scripts/metadata.pl $${type}_config "$$f" > "$$t" || { rm -f "$$t"; echo "Failed to build $$t"; false; break; }; \ done - ./scripts/metadata.pl package_mk < tmp/.packageinfo > tmp/.packagedeps || { rm -f tmp/.packagedeps; false; } + ./scripts/metadata.pl package_mk tmp/.packageinfo > tmp/.packagedeps || { rm -f tmp/.packagedeps; false; } touch $(TOPDIR)/tmp/.build .config: ./scripts/config/conf prepare-tmpinfo @@ -64,7 +66,7 @@ defconfig: scripts/config/conf prepare-tmpinfo FORCE $< -D .config Config.in oldconfig: scripts/config/conf prepare-tmpinfo FORCE - $< -o Config.in + $< -$(if $(CONFDEFAULT),$(CONFDEFAULT),o) Config.in menuconfig: scripts/config/mconf prepare-tmpinfo FORCE if [ \! -f .config -a -e $(HOME)/.openwrt/defconfig ]; then \ diff --git a/scripts/config/conf.c b/scripts/config/conf.c index cb2093691e..6589aee451 100644 --- a/scripts/config/conf.c +++ b/scripts/config/conf.c @@ -579,28 +579,11 @@ int main(int ac, char **av) } case ask_all: case ask_new: - conf_read(NULL); - break; case set_no: case set_mod: case set_yes: case set_random: - name = getenv("KCONFIG_ALLCONFIG"); - if (name && !stat(name, &tmpstat)) { - conf_read_simple(name); - break; - } - switch (input_mode) { - case set_no: name = "allno.config"; break; - case set_mod: name = "allmod.config"; break; - case set_yes: name = "allyes.config"; break; - case set_random: name = "allrandom.config"; break; - default: break; - } - if (!stat(name, &tmpstat)) - conf_read_simple(name); - else if (!stat("all.config", &tmpstat)) - conf_read_simple("all.config"); + conf_read(NULL); break; default: break; diff --git a/scripts/feeds b/scripts/feeds new file mode 100755 index 0000000000..d62e8e79dd --- /dev/null +++ b/scripts/feeds @@ -0,0 +1,322 @@ +#!/usr/bin/perl +use Getopt::Std; +use FindBin; +use Cwd; +use lib "$FindBin::Bin"; +use metadata; +use warnings; +use strict; + +chdir "$FindBin::Bin/.."; +$ENV{TOPDIR}=getcwd(); + +my @feeds; +my %build_packages; +my %installed; + +sub parse_config() { + my $line = 0; + my %name; + + open FEEDS, "feeds.conf"; + while () { + chomp; + s/#.+$//; + next unless /\S/; + my @line = split /\s+/, $_, 3; + $line++; + + my $valid = 1; + $line[0] =~ /^src-\w+$/ or $valid = 0; + $line[1] =~ /^\w+$/ or $valid = 0; + $line[2] =~ /\s/ and $valid = 0; + $valid or die "Syntax error in feeds.list, line: $line\n"; + + $name{$line[1]} and die "Duplicate feed name '$line[1]', line: $line\n"; + $name{$line[1]} = 1; + + push @feeds, [@line]; + } + close FEEDS; +} + +sub update_svn($$) { + my $name = shift; + my $src = shift; + + system("svn co $src ./feeds/$name") == 0 or return 1; + -d "./feeds/$name.tmp" or mkdir "./feeds/$name.tmp" or return 1; + -d "./feeds/$name.tmp/info" or mkdir "./feeds/$name.tmp/info" or return 1; + + system("make -s prepare-mk TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\""); + system("make -s -f include/scan.mk IS_TTY=1 SCAN_TARGET=\"packageinfo\" SCAN_DIR=\"feeds/$name\" SCAN_NAME=\"package\" SCAN_DEPS=\"$ENV{TOPDIR}/include/package*.mk\" SCAN_DEPTH=4 SCAN_EXTRA=\"\" TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\""); + system("ln -sf $name.tmp/.packageinfo ./feeds/$name.index"); + + return 0; +} + +sub get_feed($) { + my $feed = shift; + + clear_packages(); + parse_package_metadata("./feeds/$feed.index") or return; + return { %package }; +} + +sub get_installed() { + system("make -s prepare-tmpinfo"); + clear_packages(); + parse_package_metadata("./tmp/.packageinfo"); + %installed = %package; +} + +sub search_feed { + my $feed = shift; + my @substr = @_; + my $display; + + return unless @substr > 0; + get_feed($feed); + foreach my $name (sort { lc($a) cmp lc($b) } keys %package) { + my $pkg = $package{$name}; + my $substr; + my $pkgmatch = 1; + + foreach my $substr (@substr) { + my $match; + foreach my $key (qw(name title description)) { + $substr and $pkg->{$key} =~ m/$substr/i and $match = 1; + } + $match or undef $pkgmatch; + }; + $pkgmatch and do { + $display or do { + print "Search results in feed '$feed':\n"; + $display = 1; + }; + printf "\%-25s\t\%s\n", $pkg->{name}, $pkg->{title}; + }; + } + return 0; +} + + +sub search { + my %opts; + + getopt('r:', \%opts); + foreach my $feed (@feeds) { + search_feed($feed->[1], @ARGV) if (!defined($opts{r}) or $opts{r} eq $feed->[1]); + } +} + +sub install_svn() { + my $feed = shift; + my $pkg = shift; + my $path = $pkg->{makefile}; + $path =~ s/\/Makefile$//; + + -d "./package/feeds" or mkdir "./package/feeds"; + -d "./package/feeds/$feed->[1]" or mkdir "./package/feeds/$feed->[1]"; + system("ln -sf ../../../$path ./package/feeds/$feed->[1]/"); + + return 0; +} + +my %install_method = ( + 'src-svn' => \&install_svn +); + +my %feed; + +sub lookup_package($$) { + my $feed = shift; + my $package = shift; + + foreach my $feed ($feed, @feeds) { + next unless $feed->[1]; + next unless $feed{$feed->[1]}; + $feed{$feed->[1]}->{$package} and return $feed; + } + return; +} + +sub install_package { + my $feed = shift; + my $name = shift; + my $ret = 0; + + $feed = lookup_package($feed, $name); + $feed or do { + $installed{$name} and return 0; + warn "WARNING: Package '$name' is not available.\n"; + return 1; + }; + + my $pkg = $feed{$feed->[1]}->{$name} or return 1; + my $src = $pkg->{src}; + my $type = $feed->[0]; + $src or $src = $name; + + # previously installed packages set the runtime package + # newly installed packages set the source package + $installed{$src} and return 0; + + # install all dependencies + foreach my $dep (@{$pkg->{depends}}) { + next if $dep =~ /@/; + $dep =~ s/^\+//; + install_package($feed, $dep) == 0 or $ret = 1; + } + + # check previously installed packages + $installed{$name} and return 0; + $installed{$src} = 1; + warn "Installing package '$src'\n"; + + $install_method{$type} or do { + warn "Unknown installation method: '$type'\n"; + return 1; + }; + + &{$install_method{$type}}($feed, $pkg) == 0 or do { + warn "failed.\n"; + return 1; + }; + + return $ret; +} + +sub refresh_config { + my $default = shift; + $default or $default = "o"; + + # workaround for timestamp check + system("rm -f tmp/.packageinfo"); + + # refresh the config + system("make oldconfig CONFDEFAULT=\"$default\" Config.in >/dev/null 2>/dev/null"); +} + +sub install { + my $name; + my %opts; + my $feed; + my $ret = 0; + + getopt('p:d:', \%opts); + get_installed(); + + foreach my $f (@feeds) { + # index all feeds + $feed{$f->[1]} = get_feed($f->[1]); + + # look up the preferred feed + $opts{p} and $f->[1] eq $opts{p} and $feed = $f; + } + + while ($name = shift @ARGV) { + install_package($feed, $name) == 0 or $ret = 1; + } + + # workaround for timestamp check + + # set the defaults + if ($opts{d} and $opts{d} =~ /^[ymn]$/) { + refresh_config($opts{d}); + } + + return $ret; +} + +sub uninstall { + my $name; + my $uninstall; + + if ($ARGV[0] eq '-a') { + system("rm -rf ./package/feeds"); + $uninstall = 1; + } else { + get_installed(); + while ($name = shift @ARGV) { + my $pkg = $installed{$name}; + $pkg or do { + warn "WARNING: $name not installed\n"; + next; + }; + $pkg->{src} and $name = $pkg->{src}; + warn "Uninstalling package '$name'\n"; + system("rm -f ./package/feeds/*/$name"); + $uninstall = 1; + } + } + $uninstall and refresh_config(); + return 0; +} + +sub usage() { + print < [options] + +Commands: + install [options] : Install a package + Options: + -p : Prefer this feed when installing packages + -d : Set default for newly installed packages + + search [options] : Search for a package + Options: + -r : Only search in this feed + + uninstall -a|: Uninstall a package + -a uninstalls all packages + + update: Update packages and lists of feeds in feeds.list + clean: Remove downloaded/generated files + +EOF + exit(1); +} + +my %update_method = ( + 'src-svn' => \&update_svn +); + +my %commands = ( + 'update' => sub { + -d "feeds" or do { + mkdir "feeds" or die "Unable to create the feeds directory"; + }; + $ENV{SCAN_COOKIE} = $$; + $ENV{KBUILD_VERBOSE} = 99; + foreach my $feed (@feeds) { + my ($type, $name, $src) = @$feed; + $update_method{$type} or do { + warn "Unknown type '$type' in feed $name\n"; + next; + }; + warn "Updating feed '$name'...\n"; + &{$update_method{$type}}($name, $src) == 0 or do { + warn "failed.\n"; + return 1; + }; + } + return 0; + }, + 'install' => \&install, + 'search' => \&search, + 'uninstall' => \&uninstall, + 'clean' => sub { + system("rm -rf feeds"); + } +); + +my $arg = shift @ARGV; +$arg or usage(); +parse_config; +foreach my $cmd (keys %commands) { + $arg eq $cmd and do { + exit(&{$commands{$cmd}}()); + }; +} +usage(); diff --git a/scripts/metadata.pl b/scripts/metadata.pl index 03041f97c4..f3d6e9df2c 100755 --- a/scripts/metadata.pl +++ b/scripts/metadata.pl @@ -1,23 +1,10 @@ #!/usr/bin/perl +use FindBin; +use lib "$FindBin::Bin"; use strict; -my %preconfig; -my %package; -my %srcpackage; -my %category; -my %subdir; -my %board; - -sub get_multiline { - my $prefix = shift; - my $str; - while (<>) { - last if /^@@/; - s/^\s*//g; - $str .= (($_ and $prefix) ? $prefix . $_ : $_); - } +use metadata; - return $str; -} +my %board; sub confstr($) { my $conf = shift; @@ -26,8 +13,13 @@ sub confstr($) { } sub parse_target_metadata() { - my ($target, @target, $profile); - while (<>) { + my $file = shift @ARGV; + my ($target, @target, $profile); + open FILE, "<$file" or do { + warn "Can't open file '$file': $!\n"; + return; + }; + while () { chomp; /^Target:\s*(.+)\s*$/ and do { $target = { @@ -46,7 +38,7 @@ sub parse_target_metadata() { /^Target-Path:\s*(.+)\s*$/ and $target->{path} = $1; /^Target-Arch:\s*(.+)\s*$/ and $target->{arch} = $1; /^Target-Features:\s*(.+)\s*$/ and $target->{features} = [ split(/\s+/, $1) ]; - /^Target-Description:/ and $target->{desc} = get_multiline(); + /^Target-Description:/ and $target->{desc} = get_multiline(*FILE); /^Linux-Version:\s*(.+)\s*$/ and $target->{version} = $1; /^Linux-Release:\s*(.+)\s*$/ and $target->{release} = $1; /^Linux-Kernel-Arch:\s*(.+)\s*$/ and $target->{karch} = $1; @@ -61,10 +53,11 @@ sub parse_target_metadata() { }; /^Target-Profile-Name:\s*(.+)\s*$/ and $profile->{name} = $1; /^Target-Profile-Packages:\s*(.*)\s*$/ and $profile->{packages} = [ split(/\s+/, $1) ]; - /^Target-Profile-Description:\s*(.*)\s*/ and $profile->{desc} = get_multiline(); - /^Target-Profile-Config:/ and $profile->{config} = get_multiline("\t"); + /^Target-Profile-Description:\s*(.*)\s*/ and $profile->{desc} = get_multiline(*FILE); + /^Target-Profile-Config:/ and $profile->{config} = get_multiline(*FILE, "\t"); /^Target-Profile-Kconfig:/ and $profile->{kconfig} = 1; } + close FILE; foreach my $target (@target) { @{$target->{profiles}} > 0 or $target->{profiles} = [ { @@ -77,78 +70,6 @@ sub parse_target_metadata() { return @target; } -sub parse_package_metadata() { - my $pkg; - my $makefile; - my $preconfig; - my $subdir; - my $src; - while (<>) { - chomp; - /^Source-Makefile: \s*((.+\/)([^\/]+)\/Makefile)\s*$/ and do { - $makefile = $1; - $subdir = $2; - $src = $3; - $subdir =~ s/^package\///; - $subdir{$src} = $subdir; - $srcpackage{$src} = []; - undef $pkg; - }; - /^Package:\s*(.+?)\s*$/ and do { - $pkg = {}; - $pkg->{src} = $src; - $pkg->{makefile} = $makefile; - $pkg->{name} = $1; - $pkg->{default} = "m if ALL"; - $pkg->{depends} = []; - $pkg->{builddepends} = []; - $pkg->{subdir} = $subdir; - $package{$1} = $pkg; - push @{$srcpackage{$src}}, $pkg; - }; - /^Version: \s*(.+)\s*$/ and $pkg->{version} = $1; - /^Title: \s*(.+)\s*$/ and $pkg->{title} = $1; - /^Menu: \s*(.+)\s*$/ and $pkg->{menu} = $1; - /^Submenu: \s*(.+)\s*$/ and $pkg->{submenu} = $1; - /^Submenu-Depends: \s*(.+)\s*$/ and $pkg->{submenudep} = $1; - /^Default: \s*(.+)\s*$/ and $pkg->{default} = $1; - /^Provides: \s*(.+)\s*$/ and do { - my @vpkg = split /\s+/, $1; - foreach my $vpkg (@vpkg) { - $package{$vpkg} or $package{$vpkg} = { vdepends => [] }; - push @{$package{$vpkg}->{vdepends}}, $pkg->{name}; - } - }; - /^Depends: \s*(.+)\s*$/ and $pkg->{depends} = [ split /\s+/, $1 ]; - /^Build-Depends: \s*(.+)\s*$/ and $pkg->{builddepends} = [ split /\s+/, $1 ]; - /^Category: \s*(.+)\s*$/ and do { - $pkg->{category} = $1; - defined $category{$1} or $category{$1} = {}; - defined $category{$1}->{$src} or $category{$1}->{$src} = []; - push @{$category{$1}->{$src}}, $pkg; - }; - /^Description: \s*(.*)\s*$/ and $pkg->{description} = "\t\t $1\n". get_multiline("\t\t "); - /^Config: \s*(.*)\s*$/ and $pkg->{config} = "$1\n".get_multiline(); - /^Prereq-Check:/ and $pkg->{prereq} = 1; - /^Preconfig:\s*(.+)\s*$/ and do { - my $pkgname = $pkg->{name}; - $preconfig{$pkgname} or $preconfig{$pkgname} = {}; - if (exists $preconfig{$pkgname}->{$1}) { - $preconfig = $preconfig{$pkgname}->{$1}; - } else { - $preconfig = { - id => $1 - }; - $preconfig{$pkgname}->{$1} = $preconfig; - } - }; - /^Preconfig-Type:\s*(.*?)\s*$/ and $preconfig->{type} = $1; - /^Preconfig-Label:\s*(.*?)\s*$/ and $preconfig->{label} = $1; - /^Preconfig-Default:\s*(.*?)\s*$/ and $preconfig->{default} = $1; - } - return %category; -} - sub gen_kconfig_overrides() { my %config; my %kconfig; @@ -318,20 +239,31 @@ EOF print "endchoice\n"; } - -sub find_package_dep($$) { +my %dep_check; +sub __find_package_dep($$) { my $pkg = shift; my $name = shift; my $deps = ($pkg->{vdepends} or $pkg->{depends}); return 0 unless defined $deps; foreach my $dep (@{$deps}) { + next if $dep_check{$dep}; + $dep_check{$dep} = 1; return 1 if $dep eq $name; - return 1 if ($package{$dep} and (find_package_dep($package{$dep},$name) == 1)); + return 1 if ($package{$dep} and (__find_package_dep($package{$dep},$name) == 1)); } return 0; } +# wrapper to avoid infinite recursion +sub find_package_dep($$) { + my $pkg = shift; + my $name = shift; + + %dep_check = (); + return __find_package_dep($pkg, $name); +} + sub package_depends($$) { my $a = shift; my $b = shift; @@ -452,7 +384,7 @@ sub print_package_config_category($) { } sub gen_package_config() { - parse_package_metadata(); + parse_package_metadata($ARGV[0]) or exit 1; print "menuconfig UCI_PRECONFIG\n\tbool \"Image configuration\"\n"; foreach my $preconfig (keys %preconfig) { foreach my $cfg (keys %{$preconfig{$preconfig}}) { @@ -478,7 +410,7 @@ sub gen_package_mk() { my %dep; my $line; - parse_package_metadata(); + parse_package_metadata($ARGV[0]) or exit 1; foreach my $name (sort {uc($a) cmp uc($b)} keys %package) { my $config; my $pkg = $package{$name}; diff --git a/scripts/metadata.pm b/scripts/metadata.pm new file mode 100644 index 0000000000..f90446298a --- /dev/null +++ b/scripts/metadata.pm @@ -0,0 +1,113 @@ +package metadata; +use base 'Exporter'; +use strict; +use warnings; +our @EXPORT = qw(%package %srcpackage %category %subdir %preconfig clear_packages parse_package_metadata get_multiline); + +our %package; +our %preconfig; +our %srcpackage; +our %category; +our %subdir; + +sub get_multiline { + my $fh = shift; + my $prefix = shift; + my $str; + while (<$fh>) { + last if /^@@/; + s/^\s*//g; + $str .= (($_ and $prefix) ? $prefix . $_ : $_); + } + + return $str ? $str : ""; +} + +sub clear_packages() { + %subdir = (); + %preconfig = (); + %package = (); + %srcpackage = (); + %category = (); +} + +sub parse_package_metadata($) { + my $file = shift; + my $pkg; + my $makefile; + my $preconfig; + my $subdir; + my $src; + + open FILE, "<$file" or do { + warn "Cannot open '$file': $!\n"; + return undef; + }; + while () { + chomp; + /^Source-Makefile: \s*((.+\/)([^\/]+)\/Makefile)\s*$/ and do { + $makefile = $1; + $subdir = $2; + $src = $3; + $subdir =~ s/^package\///; + $subdir{$src} = $subdir; + $srcpackage{$src} = []; + undef $pkg; + }; + /^Package:\s*(.+?)\s*$/ and do { + $pkg = {}; + $pkg->{src} = $src; + $pkg->{makefile} = $makefile; + $pkg->{name} = $1; + $pkg->{default} = "m if ALL"; + $pkg->{depends} = []; + $pkg->{builddepends} = []; + $pkg->{subdir} = $subdir; + $package{$1} = $pkg; + push @{$srcpackage{$src}}, $pkg; + }; + /^Version: \s*(.+)\s*$/ and $pkg->{version} = $1; + /^Title: \s*(.+)\s*$/ and $pkg->{title} = $1; + /^Menu: \s*(.+)\s*$/ and $pkg->{menu} = $1; + /^Submenu: \s*(.+)\s*$/ and $pkg->{submenu} = $1; + /^Submenu-Depends: \s*(.+)\s*$/ and $pkg->{submenudep} = $1; + /^Default: \s*(.+)\s*$/ and $pkg->{default} = $1; + /^Provides: \s*(.+)\s*$/ and do { + my @vpkg = split /\s+/, $1; + foreach my $vpkg (@vpkg) { + $package{$vpkg} or $package{$vpkg} = { vdepends => [] }; + push @{$package{$vpkg}->{vdepends}}, $pkg->{name}; + } + }; + /^Depends: \s*(.+)\s*$/ and $pkg->{depends} = [ split /\s+/, $1 ]; + /^Build-Depends: \s*(.+)\s*$/ and $pkg->{builddepends} = [ split /\s+/, $1 ]; + /^Category: \s*(.+)\s*$/ and do { + $pkg->{category} = $1; + defined $category{$1} or $category{$1} = {}; + defined $category{$1}->{$src} or $category{$1}->{$src} = []; + push @{$category{$1}->{$src}}, $pkg; + }; + /^Description: \s*(.*)\s*$/ and $pkg->{description} = "\t\t $1\n". get_multiline(*FILE, "\t\t "); + /^Config: \s*(.*)\s*$/ and $pkg->{config} = "$1\n".get_multiline(*FILE); + /^Prereq-Check:/ and $pkg->{prereq} = 1; + /^Preconfig:\s*(.+)\s*$/ and do { + my $pkgname = $pkg->{name}; + $preconfig{$pkgname} or $preconfig{$pkgname} = {}; + if (exists $preconfig{$pkgname}->{$1}) { + $preconfig = $preconfig{$pkgname}->{$1}; + } else { + $preconfig = { + id => $1 + }; + $preconfig{$pkgname}->{$1} = $preconfig; + } + }; + /^Preconfig-Type:\s*(.*?)\s*$/ and $preconfig->{type} = $1; + /^Preconfig-Label:\s*(.*?)\s*$/ and $preconfig->{label} = $1; + /^Preconfig-Default:\s*(.*?)\s*$/ and $preconfig->{default} = $1; + } + close FILE; + return %category; +} + +1;