| #!/usr/bin/env perl |
| use Getopt::Std; |
| use FindBin; |
| use Cwd; |
| use lib "$FindBin::Bin"; |
| use metadata; |
| use warnings; |
| use strict; |
| use Cwd 'abs_path'; |
| |
| chdir "$FindBin::Bin/.."; |
| $ENV{TOPDIR} //= getcwd(); |
| chdir $ENV{TOPDIR}; |
| $ENV{GIT_CONFIG_PARAMETERS}="'core.autocrlf=false'"; |
| $ENV{GREP_OPTIONS}=""; |
| |
| my $mk=`command -v gmake 2>/dev/null`; # select the right 'make' program |
| chomp($mk); # trim trailing newline |
| $mk or $mk = "make"; # default to 'make' |
| |
| # check version of make |
| my @mkver = split /\s+/, `$mk -v`, 4; |
| my $valid_mk = 1; |
| $mkver[0] =~ /^GNU/ or $valid_mk = 0; |
| $mkver[1] =~ /^Make/ or $valid_mk = 0; |
| |
| my ($mkv1, $mkv2) = split /\./, $mkver[2]; |
| ($mkv1 >= 4 || ($mkv1 == 3 && $mkv2 >= 81)) or $valid_mk = 0; |
| |
| $valid_mk or die "Unsupported version of make found: $mk\n"; |
| |
| my @feeds; |
| my %build_packages; |
| my %installed; |
| my %installed_pkg; |
| my %installed_targets; |
| my %feed_cache; |
| |
| my $feed_package = {}; |
| my $feed_src = {}; |
| my $feed_target = {}; |
| my $feed_vpackage = {}; |
| |
| sub parse_file($$); |
| |
| sub parse_file($$) { |
| my ($fname, $existing) = @_; |
| my $line = 0; |
| my $fh; |
| |
| open $fh, $fname or return undef; |
| while (<$fh>) { |
| chomp; |
| s/#.+$//; |
| $line++; |
| next unless /\S/; |
| |
| my ($type, $flags, $name, $urls) = m!^src-([\w\-]+)((?:\s+--\w+(?:=\S+)?)*)\s+(\w+)(?:\s+(\S.*))?$!; |
| unless ($type && $name) { |
| die "Syntax error in $fname, line $line\n"; |
| } |
| |
| if ($existing->{$name}++) { |
| die "Duplicate feed name '$name' in '$fname' line: $line\n"; |
| } |
| |
| my @src = defined($urls) ? split /\s+/, $urls : (); |
| push @src, '' if @src == 0; |
| |
| my %flags; |
| if (defined $flags) { |
| while ($flags =~ m!\s+--(\w+)(?:=(\S+))?!g) { |
| $flags{$1} = defined($2) ? $2 : 1; |
| } |
| } |
| |
| if ($type eq "include") { |
| parse_file($urls, $existing) or |
| die "Unable to open included file '$urls'"; |
| next; |
| } |
| |
| push @feeds, ["src-$type", $name, \@src, \%flags]; |
| } |
| close $fh; |
| return 1; |
| } |
| |
| sub parse_config() { |
| my %name; |
| parse_file("feeds.conf", \%name) or |
| parse_file("feeds.conf.default", \%name) or |
| die "Unable to open feeds configuration"; |
| } |
| |
| sub update_location($$) |
| { |
| my $name = shift; |
| my $url = shift; |
| my $old_url; |
| |
| -d "./feeds/$name.tmp" or mkdir "./feeds/$name.tmp" or return 1; |
| |
| if( open LOC, "< ./feeds/$name.tmp/location" ) |
| { |
| chomp($old_url = readline LOC); |
| close LOC; |
| } |
| |
| if( !$old_url || $old_url ne $url ) |
| { |
| if( open LOC, "> ./feeds/$name.tmp/location" ) |
| { |
| print LOC $url, "\n"; |
| close LOC; |
| } |
| return $old_url ? 1 : 0; |
| } |
| |
| return 0; |
| } |
| |
| sub update_index($) |
| { |
| my $name = shift; |
| |
| -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("$mk -s prepare-mk OPENWRT_BUILD= TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\""); |
| system("$mk -s -f include/scan.mk IS_TTY=1 SCAN_TARGET=\"packageinfo\" SCAN_DIR=\"feeds/$name\" SCAN_NAME=\"package\" SCAN_DEPTH=5 SCAN_EXTRA=\"\" TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\""); |
| system("$mk -s -f include/scan.mk IS_TTY=1 SCAN_TARGET=\"targetinfo\" SCAN_DIR=\"feeds/$name\" SCAN_NAME=\"target\" SCAN_DEPTH=5 SCAN_EXTRA=\"\" SCAN_MAKEOPTS=\"TARGET_BUILD=1\" TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\""); |
| system("ln -sf $name.tmp/.packageinfo ./feeds/$name.index"); |
| system("ln -sf $name.tmp/.targetinfo ./feeds/$name.targetindex"); |
| |
| return 0; |
| } |
| |
| my %update_method = ( |
| 'src-svn' => { |
| 'init' => "svn checkout '%s' '%s'", |
| 'update' => "svn update", |
| 'controldir' => ".svn", |
| 'revision' => "svn info | grep 'Revision' | cut -d ' ' -f 2 | tr -d '\n'"}, |
| 'src-cpy' => { |
| 'init' => "cp -Rf '%s' '%s'", |
| 'update' => "", |
| 'revision' => "echo -n 'local'"}, |
| 'src-link' => { |
| 'init' => "ln -s '%s' '%s'", |
| 'update' => "", |
| 'revision' => "echo -n 'local'"}, |
| 'src-dummy' => { |
| 'init' => "true '%s' && mkdir '%s'", |
| 'update' => "", |
| 'revision' => "echo -n 'dummy'"}, |
| 'src-git' => { |
| 'init' => "git clone --depth 1 '%s' '%s'", |
| 'init_branch' => "git clone --depth 1 --branch '%s' '%s' '%s'", |
| 'init_commit' => "git clone --depth 1 '%s' '%s' && cd '%s' && git fetch --depth=1 origin '%s' && git -c advice.detachedHead=false checkout '%s' && cd -", |
| 'update' => "git pull --ff-only", |
| 'update_rebase' => "git pull --rebase=merges", |
| 'update_stash' => "git pull --rebase=merges --autostash", |
| 'update_force' => "git pull --ff-only || (git reset --hard HEAD; git pull --ff-only; exit 1)", |
| 'post_update' => "git submodule update --init --recursive --depth 1", |
| 'controldir' => ".git", |
| 'revision' => "git rev-parse HEAD | tr -d '\n'"}, |
| 'src-git-full' => { |
| 'init' => "git clone '%s' '%s'", |
| 'init_branch' => "git clone --branch '%s' '%s' '%s'", |
| 'init_commit' => "git clone '%s' '%s' && cd '%s' && git checkout -b '%s' '%s' && cd -", |
| 'update' => "git pull --ff-only", |
| 'update_rebase' => "git pull --rebase=merges", |
| 'update_stash' => "git pull --rebase=merges --autostash", |
| 'update_force' => "git pull --ff-only || (git reset --hard HEAD; git pull --ff-only; exit 1)", |
| 'post_update' => "git submodule update --init --recursive", |
| 'controldir' => ".git", |
| 'revision' => "git rev-parse HEAD | tr -d '\n'"}, |
| 'src-gitsvn' => { |
| 'init' => "git svn clone -r HEAD '%s' '%s'", |
| 'update' => "git svn rebase", |
| 'controldir' => ".git", |
| 'revision' => "git rev-parse HEAD | tr -d '\n'"}, |
| 'src-bzr' => { |
| 'init' => "bzr checkout --lightweight '%s' '%s'", |
| 'update' => "bzr update", |
| 'controldir' => ".bzr"}, |
| 'src-hg' => { |
| 'init' => "hg clone '%s' '%s'", |
| 'update' => "hg pull --update", |
| 'controldir' => ".hg"}, |
| 'src-darcs' => { |
| 'init' => "darcs get '%s' '%s'", |
| 'update' => "darcs pull -a", |
| 'controldir' => "_darcs"}, |
| ); |
| |
| # src-git: pull broken |
| # src-cpy: broken if `basename $src` != $name |
| |
| sub update_feed_via($$$$$$$) { |
| my $type = shift; |
| my $name = shift; |
| my $src = shift; |
| my $relocate = shift; |
| my $force = shift; |
| my $rebase = shift; |
| my $stash = shift; |
| |
| my $m = $update_method{$type}; |
| my $localpath = "./feeds/$name"; |
| my $safepath = $localpath; |
| $safepath =~ s/'/'\\''/; |
| my ($base_branch, $branch) = split(/;/, $src, 2); |
| my ($base_commit, $commit) = split(/\^/, $src, 2); |
| |
| if( $relocate || !$m->{'update'} || !-d "$localpath/$m->{'controldir'}" ) { |
| system("rm -rf '$safepath'"); |
| if ($m->{'init_branch'} and $branch) { |
| system(sprintf($m->{'init_branch'}, $branch, $base_branch, $safepath)) == 0 or return 1; |
| } elsif ($m->{'init_commit'} and $commit) { |
| system(sprintf($m->{'init_commit'}, $base_commit, $safepath, $safepath, $commit, $commit)) == 0 or return 1; |
| } else { |
| system(sprintf($m->{'init'}, $src, $safepath)) == 0 or return 1; |
| } |
| } elsif ($m->{'init_commit'} and $commit) { |
| # in case git hash has been provided don't update the feed |
| } else { |
| my $update_cmd = $m->{'update'}; |
| if ($force && exists $m->{'update_force'}) { |
| $update_cmd = $m->{'update_force'}; |
| } |
| if ($rebase && exists $m->{'update_rebase'}) { |
| $update_cmd = $m->{'update_rebase'}; |
| } |
| if ($stash && exists $m->{'update_stash'}) { |
| $update_cmd = $m->{'update_stash'}; |
| } |
| system("cd '$safepath'; $update_cmd") == 0 or return 1; |
| } |
| if ($m->{'post_update'}) { |
| my $cmd = $m->{'post_update'}; |
| system("cd '$safepath'; $cmd") == 0 or return 1; |
| } |
| |
| return 0; |
| } |
| |
| sub get_targets($) { |
| my $file = shift; |
| my @target = parse_target_metadata($file); |
| my %target; |
| foreach my $target (@target) { |
| $target{$target->{id}} = $target; |
| } |
| return %target |
| } |
| |
| sub get_feed($) { |
| my $feed = shift; |
| |
| if (!defined($feed_cache{$feed})) { |
| my $file = "./feeds/$feed.index"; |
| |
| clear_packages(); |
| -f $file or do { |
| print "Ignoring feed '$feed' - index missing\n"; |
| return; |
| }; |
| parse_package_metadata($file) or return; |
| my %target = get_targets("./feeds/$feed.targetindex"); |
| |
| $feed_cache{$feed} = [ { %package }, { %srcpackage }, { %target }, { %vpackage } ]; |
| } |
| |
| $feed_package = $feed_cache{$feed}->[0]; |
| $feed_src = $feed_cache{$feed}->[1]; |
| $feed_target = $feed_cache{$feed}->[2]; |
| $feed_vpackage = $feed_cache{$feed}->[3]; |
| } |
| |
| sub get_installed() { |
| system("$mk -s prepare-tmpinfo OPENWRT_BUILD="); |
| clear_packages(); |
| parse_package_metadata("./tmp/.packageinfo"); |
| %installed_pkg = %vpackage; |
| %installed = %srcpackage; |
| %installed_targets = get_targets("./tmp/.targetinfo"); |
| } |
| |
| 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 %$feed_package) { |
| my $pkg = $feed_package->{$name}; |
| my $substr; |
| my $pkgmatch = 1; |
| |
| foreach my $substr (@substr) { |
| my $match; |
| foreach my $key (qw(name title description src)) { |
| $pkg->{$key} and $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}; |
| }; |
| } |
| |
| foreach my $name (sort { lc($a) cmp lc($b) } keys %$feed_target) { |
| my $target = $feed_target->{$name}; |
| my $targetmatch = 1; |
| |
| foreach my $substr (@substr) { |
| my $match; |
| foreach my $key (qw(id name description)) { |
| $target->{$key} and $substr and $target->{$key} =~ m/$substr/i and $match = 1; |
| } |
| $match or undef $targetmatch; |
| }; |
| $targetmatch and do { |
| $display or do { |
| print "Search results in feed '$feed':\n"; |
| $display = 1; |
| }; |
| printf "TARGET: \%-17s\t\%s\n", $target->{id}, $target->{name}; |
| }; |
| } |
| 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 list_feed { |
| my $feed = shift; |
| |
| get_feed($feed); |
| foreach my $name (sort { lc($a) cmp lc($b) } keys %$feed_package) { |
| my $pkg = $feed_package->{$name}; |
| if($pkg->{name}) { |
| printf "\%-32s\t\%s\n", $pkg->{name}, $pkg->{title}; |
| } |
| } |
| |
| foreach my $name (sort { lc($a) cmp lc($b) } keys %$feed_target) { |
| my $target = $feed_target->{$name}; |
| if($target->{name}) { |
| printf "TARGET: \%-24s\t\%s\n", $target->{id}, $target->{name}; |
| } |
| } |
| |
| return 0; |
| } |
| |
| sub list { |
| my %opts; |
| |
| getopts('r:d:nshf', \%opts); |
| if ($opts{h}) { |
| usage(); |
| return 0; |
| } |
| if ($opts{n}) { |
| foreach my $feed (@feeds) { |
| printf "%s\n", $feed->[1]; |
| } |
| return 0; |
| } |
| if ($opts{s}) { |
| foreach my $feed (@feeds) { |
| my $localpath = "./feeds/$feed->[1]"; |
| my $m = $update_method{$feed->[0]}; |
| my $revision; |
| if (!-d "$localpath" || !$m->{'revision'}) { |
| $revision = "X"; |
| } |
| elsif( $m->{'controldir'} && -d "$localpath/$m->{'controldir'}" ) { |
| $revision = `cd '$localpath'; $m->{'revision'}`; |
| } |
| else { |
| $revision = "local"; |
| } |
| if ($opts{d}) { |
| printf "%s%s%s%s%s%s%s\n", $feed->[1], $opts{d}, $feed->[0], $opts{d}, $revision, $opts{d}, join(", ", @{$feed->[2]}); |
| } |
| elsif ($opts{f}) { |
| my $uri = join(", ", @{$feed->[2]}); |
| if ($revision ne "local" && $revision ne "X") { |
| $uri =~ s/[;^].*//; |
| $uri .= "^" . $revision; |
| } |
| printf "%s %s %s\n", $feed->[0], $feed->[1], $uri; |
| } |
| else { |
| printf "\%-10s \%-8s \%-8s \%s\n", $feed->[1], $feed->[0], $revision, join(", ", @{$feed->[2]}); |
| } |
| } |
| return 0; |
| } |
| foreach my $feed (@feeds) { |
| list_feed($feed->[1], @ARGV) if (!defined($opts{r}) or $opts{r} eq $feed->[1]); |
| } |
| return 0; |
| } |
| |
| sub do_install_src($$) { |
| my $feed = shift; |
| my $src = shift; |
| |
| my $path = $src->{makefile}; |
| if ($path) { |
| $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]/"); |
| } else { |
| warn "Package is not valid\n"; |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| sub do_install_target($) { |
| my $target = shift; |
| my $path = $target->{makefile}; |
| |
| if ($path) { |
| $path =~ s/\/Makefile$//; |
| my $name = $path; |
| $name =~ s/.*\///; |
| my $dest = "./target/linux/feeds/$name"; |
| |
| -d "./target/linux/feeds" or mkdir "./target/linux/feeds"; |
| |
| -e $dest and do { |
| warn "Path $dest already exists"; |
| return 1; |
| }; |
| |
| system("ln -sf ../../../$path ./target/linux/feeds/"); |
| } else { |
| warn "Target is not valid\n"; |
| return 1; |
| } |
| |
| # Clean packageinfo of linux kernel to force the scan. |
| # Otherwise kernel modules defined at target level are not scanned, as the |
| # linux kernel package was scanned before the installation of the target. |
| unlink "tmp/info/.packageinfo-kernel_linux"; |
| |
| return 0; |
| } |
| |
| sub lookup_src($$) { |
| my $feed = shift; |
| my $src = shift; |
| |
| foreach my $feed ($feed, @feeds) { |
| next unless $feed->[1]; |
| next unless $feed_cache{$feed->[1]}; |
| $feed_cache{$feed->[1]}->[1]->{$src} and return $feed; |
| } |
| return; |
| } |
| |
| sub lookup_package($$) { |
| my $feed = shift; |
| my $package = shift; |
| |
| foreach my $feed ($feed, @feeds) { |
| next unless $feed->[1]; |
| next unless $feed_cache{$feed->[1]}; |
| $feed_cache{$feed->[1]}->[3]->{$package} and return $feed; |
| } |
| return; |
| } |
| |
| sub lookup_target($$) { |
| my $feed = shift; |
| my $target = shift; |
| |
| foreach my $feed ($feed, @feeds) { |
| next unless $feed->[1]; |
| next unless $feed_cache{$feed->[1]}; |
| $feed_cache{$feed->[1]}->[2]->{$target} and return $feed; |
| } |
| return; |
| } |
| |
| sub is_core_src($) { |
| my $src = shift; |
| foreach my $file ("tmp/info/.packageinfo-$src", glob("tmp/info/.packageinfo-*_$src")) { |
| next unless index($file, "tmp/info/.packageinfo-feeds_"); |
| return 1 if -s $file; |
| } |
| return 0; |
| } |
| |
| sub install_target { |
| my $feed = shift; |
| my $name = shift; |
| my $force = shift; |
| |
| $feed = lookup_target($feed, $name); |
| my $feed_name = $feed->[1]; |
| |
| -e "target/linux/feeds/$name" and return 0; |
| |
| # enable force flag if feed src line was declared with --force |
| if (exists($feed->[3]{force})) { |
| $force = 1; |
| } |
| |
| $feed = $feed_cache{$feed_name}->[2]; |
| $feed or return 0; |
| |
| my $target = $feed->{$name}; |
| $target or return 0; |
| |
| if (-e "target/linux/$name") { |
| if ($force) { |
| warn "Overriding target '$name' with version from '$feed_name'\n"; |
| } else { |
| warn "WARNING: Not overriding core target '$name'; use -f to force\n"; |
| return 0; |
| } |
| } else { |
| warn "Installing target '$name'\n"; |
| } |
| return do_install_target($target); |
| } |
| |
| sub install_src { |
| my $feed = shift; |
| my $name = shift; |
| my $force = shift; |
| my $ret = 0; |
| |
| my $select_feed = lookup_src($feed, $name); |
| unless ($select_feed) { |
| $installed{$name} and return 0; |
| $feed_src->{$name} or warn "WARNING: No feed for source package '$name' found\n"; |
| return 0; |
| } |
| |
| # switch to the metadata for the selected feed |
| get_feed($select_feed->[1]); |
| my $src = $feed_src->{$name} or return 1; |
| |
| # enable force flag if feed src line was declared with --force |
| if (exists($select_feed->[3]{force})) { |
| $force = 1; |
| } |
| |
| # If it's a core package and we don't want to override, just return |
| my $override = 0; |
| if (is_core_src($name)) { |
| if (!$force) { |
| if ($name ne "toolchain" && $name ne "linux") { |
| warn "WARNING: Not overriding core package '$name'; use -f to force\n"; |
| } |
| return 0; |
| } |
| $override = 1; |
| } |
| |
| if ($installed{$name}) { |
| # newly installed packages set the source package to 1 |
| return 0 if ($installed{$name} == 1); |
| return 0 unless ($override); |
| } |
| |
| $installed{$name} = 1; |
| foreach my $pkg (@{$src->{packages}}) { |
| foreach my $vpkg (@{$pkg->{provides}}) { |
| $installed_pkg{$vpkg} = 1; |
| } |
| } |
| |
| if ($override) { |
| warn "Overriding core package '$name' with version from $select_feed->[1]\n"; |
| } else { |
| warn "Installing package '$name' from $select_feed->[1]\n"; |
| } |
| |
| do_install_src($select_feed, $src) == 0 or do { |
| warn "failed.\n"; |
| return 1; |
| }; |
| |
| # install all dependencies referenced from the source package |
| foreach my $dep ( |
| @{$src->{builddepends}}, |
| @{$src->{'builddepends/host'}}, |
| ) { |
| next if $dep =~ /@/; |
| $dep =~ s/^.+://; |
| $dep =~ s/\/.+$//; |
| next unless $dep; |
| install_src($feed, $dep, 0) == 0 or $ret = 1; |
| } |
| |
| foreach my $pkg (@{$src->{packages}}) { |
| foreach my $dep (@{$pkg->{depends}}) { |
| next if $dep =~ /@/; |
| $dep =~ s/^\+//; |
| $dep =~ s/^.+://; |
| next unless $dep; |
| install_package($feed, $dep, 0) == 0 or $ret = 1; |
| } |
| } |
| |
| return $ret; |
| } |
| |
| sub install_package { |
| my $feed = shift; |
| my $name = shift; |
| my $force = shift; |
| |
| my $select_feed = lookup_package($feed, $name); |
| unless ($select_feed) { |
| $installed_pkg{$name} and return 0; |
| $feed_vpackage->{$name} or warn "WARNING: No feed for package '$name' found\n"; |
| return 0; |
| } |
| |
| # switch to the metadata for the selected feed |
| get_feed($select_feed->[1]); |
| my $pkg = $feed_vpackage->{$name} or return 1; |
| return install_src($feed, $pkg->[0]{src}{name}, $force); |
| } |
| |
| sub install_target_or_package { |
| my $feed = shift; |
| my $name = shift; |
| my $force = shift; |
| |
| lookup_target($feed, $name) and do { |
| return install_target($feed, $name, $force); |
| }; |
| |
| lookup_src($feed, $name) and do { |
| return install_src($feed, $name, $force); |
| }; |
| |
| return install_package($feed, $name, $force); |
| } |
| |
| sub refresh_config { |
| my $default = shift; |
| |
| # Don't create .config if it doesn't already exist so that making a |
| # config only occurs when the user intends it do (however we do |
| # want to refresh an existing config). |
| return if not (-e '.config'); |
| |
| # workaround for timestamp check |
| system("rm -f tmp/.packageinfo"); |
| |
| # refresh the config |
| if ($default) { |
| system("$mk oldconfig CONFDEFAULT=\"$default\" Config.in >/dev/null 2>/dev/null"); |
| } else { |
| system("$mk defconfig Config.in >/dev/null 2>/dev/null"); |
| } |
| } |
| |
| sub install { |
| my $name; |
| my %opts; |
| my $feed; |
| my $ret = 0; |
| |
| getopts('ap:d:fh', \%opts); |
| |
| if ($opts{h}) { |
| usage(); |
| return 0; |
| } |
| |
| get_installed(); |
| |
| foreach my $f (@feeds) { |
| # fetch all feeds |
| get_feed($f->[1]); |
| |
| # look up the preferred feed |
| $opts{p} and $f->[1] eq $opts{p} and $feed = $f; |
| } |
| |
| if($opts{a}) { |
| foreach my $f (@feeds) { |
| if (!defined($opts{p}) or $opts{p} eq $f->[1]) { |
| printf "Installing all packages from feed %s.\n", $f->[1]; |
| get_feed($f->[1]); |
| foreach my $name (sort { lc($a) cmp lc($b) } keys %$feed_src) { |
| install_src($feed, $name, exists($opts{f})) == 0 or $ret = 1; |
| get_feed($f->[1]); |
| } |
| } |
| } |
| } else { |
| while ($name = shift @ARGV) { |
| install_target_or_package($feed, $name, exists($opts{f})) == 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_target($) { |
| my $dir = shift; |
| my $name = $dir; |
| $name =~ s/.*\///g; |
| |
| my $dest = readlink $dir; |
| return unless $dest =~ /..\/..\/feeds/; |
| warn "Uninstalling target '$name'\n"; |
| unlink "$dir"; |
| } |
| |
| sub uninstall { |
| my %opts; |
| my $name; |
| my $uninstall; |
| |
| getopts('ah', \%opts); |
| |
| if ($opts{h}) { |
| usage(); |
| return 0; |
| } |
| |
| if ($opts{a}) { |
| system("rm -rvf ./package/feeds"); |
| foreach my $dir (glob "target/linux/*") { |
| next unless -l $dir; |
| uninstall_target($dir); |
| } |
| $uninstall = 1; |
| } else { |
| if($#ARGV == -1) { |
| warn "WARNING: no package to uninstall\n"; |
| return 0; |
| } |
| get_installed(); |
| while ($name = shift @ARGV) { |
| my $target = "target/linux/feeds/$name"; |
| -l "$target" and do { |
| uninstall_target($target); |
| $uninstall = 1; |
| next; |
| }; |
| |
| my $pkg = $installed{$name}; |
| $pkg or do { |
| warn "WARNING: $name not installed\n"; |
| next; |
| }; |
| $pkg->{src} and $name = $pkg->{src}{name}; |
| warn "Uninstalling package '$name'\n"; |
| system("rm -f ./package/feeds/*/$name"); |
| $uninstall = 1; |
| } |
| } |
| $uninstall and refresh_config(); |
| return 0; |
| } |
| |
| sub update_feed($$$$$$) |
| { |
| my $type=shift; |
| my $name=shift; |
| my $src=shift; |
| my $force_update=shift; |
| my $rebase_update=shift; |
| my $stash_update=shift; |
| my $force_relocate=update_location( $name, "@$src" ); |
| my $rv=0; |
| |
| if( $force_relocate ) { |
| warn "Source of feed $name has changed, replacing copy\n"; |
| } |
| $update_method{$type} or do { |
| warn "Unknown type '$type' in feed $name\n"; |
| return 1; |
| }; |
| |
| my $failed = 1; |
| foreach my $feedsrc (@$src) { |
| warn "Updating feed '$name' from '$feedsrc' ...\n"; |
| if (update_feed_via($type, $name, $feedsrc, $force_relocate, $force_update, $rebase_update, $stash_update) != 0) { |
| if ($force_update) { |
| $rv=1; |
| $failed=0; |
| warn "failed, ignore.\n"; |
| next; |
| } |
| last; |
| } |
| $failed = 0; |
| } |
| $failed and do { |
| warn "failed.\n"; |
| return 1; |
| }; |
| return $rv; |
| } |
| |
| sub update { |
| my %opts; |
| my %argv_feeds; |
| my $failed=0; |
| |
| $ENV{SCAN_COOKIE} = $$; |
| $ENV{OPENWRT_VERBOSE} = 's'; |
| |
| getopts('ahifrs', \%opts); |
| %argv_feeds = map { $_ => 1 } @ARGV; |
| |
| if ($opts{h}) { |
| usage(); |
| return 0; |
| } |
| |
| if ($opts{f} && ($opts{r} || $opts{s})) { |
| warn "Force and rebase/stash are incompatible update options.\n";; |
| return 1; |
| } |
| |
| -d "feeds" or do { |
| mkdir "feeds" or die "Unable to create the feeds directory"; |
| }; |
| |
| my @index_feeds; |
| foreach my $feed (@feeds) { |
| my ($type, $name, $src) = @$feed; |
| next unless $#ARGV == -1 or $opts{a} or $argv_feeds{$name}; |
| if (not $opts{i}) { |
| update_feed($type, $name, $src, $opts{f}, $opts{r}, $opts{s}) == 0 or $failed=1; |
| } |
| push @index_feeds, $name; |
| } |
| foreach my $name (@index_feeds) { |
| warn "Create index file './feeds/$name.index' \n"; |
| update_index($name) == 0 or do { |
| warn "failed.\n"; |
| $failed=1; |
| }; |
| } |
| |
| refresh_config(); |
| |
| return $failed; |
| } |
| |
| sub feed_config() { |
| foreach my $feed (@feeds) { |
| my $installed = (-f "feeds/$feed->[1].index"); |
| |
| printf "\tconfig FEED_%s\n", $feed->[1]; |
| printf "\t\ttristate \"Enable feed %s\"\n", $feed->[1]; |
| printf "\t\tdepends on PER_FEED_REPO\n"; |
| printf "\t\tdefault y\n" if $installed; |
| printf "\t\thelp\n"; |
| printf "\t\t Enable the \\\"%s\\\" feed in opkg distfeeds.conf and apk repositories.\n", $feed->[1]; |
| printf "\t\t Say M to add the feed commented out.\n"; |
| printf "\n"; |
| } |
| |
| return 0; |
| } |
| |
| sub usage() { |
| print <<EOF; |
| Usage: $0 <command> [options] |
| |
| Commands: |
| list [options]: List feeds, their content and revisions (if installed) |
| Options: |
| -n : List of feed names. |
| -s : List of feed names and their URL. |
| -r <feedname>: List packages of specified feed. |
| -d <delimiter>: Use specified delimiter to distinguish rows (default: spaces) |
| -f : List feeds in opkg feeds.conf compatible format (when using -s). |
| |
| install [options] <package>: Install a package |
| Options: |
| -a : Install all packages from all feeds or from the specified feed using the -p option. |
| -p <feedname>: Prefer this feed when installing packages. |
| -d <y|m|n>: Set default for newly installed packages. |
| -f : Install will be forced even if the package exists in core OpenWrt (override) |
| |
| search [options] <substring>: Search for a package |
| Options: |
| -r <feedname>: Only search in this feed |
| |
| uninstall -a|<package>: Uninstall a package |
| Options: |
| -a : Uninstalls all packages. |
| |
| update -a|<feedname(s)>: Update packages and lists of feeds in feeds.conf . |
| Options: |
| -a : Update all feeds listed within feeds.conf. Otherwise the specified feeds will be updated. |
| -r : Update by rebase. (git only. Useful if local commits exist) |
| -s : Update by rebase and autostash. (git only. Useful if local commits and uncommited changes exist) |
| -i : Recreate the index only. No feed update from repository is performed. |
| -f : Force updating feeds even if there are changed, uncommitted files. |
| |
| clean: Remove downloaded/generated files. |
| |
| EOF |
| exit(1); |
| } |
| |
| my %commands = ( |
| 'list' => \&list, |
| 'update' => \&update, |
| 'install' => \&install, |
| 'search' => \&search, |
| 'uninstall' => \&uninstall, |
| 'feed_config' => \&feed_config, |
| 'clean' => sub { |
| system("rm -rf ./feeds ./package/feeds ./target/linux/feeds"); |
| } |
| ); |
| |
| my $arg = shift @ARGV; |
| $arg or usage(); |
| parse_config; |
| foreach my $cmd (keys %commands) { |
| $arg eq $cmd and do { |
| exit(&{$commands{$cmd}}()); |
| }; |
| } |
| usage(); |