Files
openwrt/scripts/package-metadata.pl
T
John Crispin 2d40469602 build: allow packages to build their variants in parallel
The variants of a package are built one after the other in one recipe.
For packages with many variants, such as the boot loaders, this makes
the variants the longest chain of the package build, also if nothing
has to be done.

Add PKG_PARALLEL_VARIANTS. If a package sets it, every variant gets its
own make target with the dependencies of the package, and the package
target depends on these targets. A package must only set it if its
variants share no files outside of their build directories.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00

894 lines
22 KiB
Perl
Executable File

#!/usr/bin/env perl
use FindBin;
use lib "$FindBin::Bin";
use strict;
use metadata;
use Getopt::Long;
use Time::Piece;
use JSON::PP;
my %board;
sub version_to_num($) {
my $str = shift;
my $num = 0;
if (defined($str) && $str =~ /^\d+(?:\.\d+)+$/)
{
my @n = (split(/\./, $str), 0, 0, 0, 0);
$num = ($n[0] << 24) | ($n[1] << 16) | ($n[2] << 8) | $n[3];
}
return $num;
}
sub version_filter_list(@) {
my $cmpver = version_to_num(shift @_);
my @items;
foreach my $item (@_)
{
if ($item =~ s/@(lt|le|gt|ge|eq|ne)(\d+(?:\.\d+)+)\b//)
{
my $op = $1;
my $symver = version_to_num($2);
if ($symver > 0 && $cmpver > 0)
{
next unless (($op eq 'lt' && $cmpver < $symver) ||
($op eq 'le' && $cmpver <= $symver) ||
($op eq 'gt' && $cmpver > $symver) ||
($op eq 'ge' && $cmpver >= $symver) ||
($op eq 'eq' && $cmpver == $symver) ||
($op eq 'ne' && $cmpver != $symver));
}
}
push @items, $item;
}
return @items;
}
sub gen_kconfig_overrides() {
my %config;
my %kconfig;
my $package;
my $pkginfo = shift @ARGV;
my $cfgfile = shift @ARGV;
my $patchver = shift @ARGV;
# parameter 2: build system config
open FILE, "<$cfgfile" or return;
while (<FILE>) {
/^(CONFIG_.+?)=(.+)$/ and $config{$1} = 1;
}
close FILE;
# parameter 1: package metadata
open FILE, "<$pkginfo" or return;
while (<FILE>) {
/^Package:\s*(.+?)\s*$/ and $package = $1;
/^Kernel-Config:\s*(.+?)\s*$/ and do {
my @config = split /\s+/, $1;
foreach my $config (version_filter_list($patchver, @config)) {
my $val = 'm';
my $override;
if ($config =~ /^(.+?)=(.+)$/) {
$config = $1;
$override = 1;
$val = $2;
}
if ($config{"CONFIG_PACKAGE_$package"} and ($config ne 'n')) {
next if $kconfig{$config} eq 'y';
$kconfig{$config} = $val;
} elsif (!$override) {
$kconfig{$config} or $kconfig{$config} = 'n';
}
}
};
};
close FILE;
foreach my $kconfig (sort keys %kconfig) {
if ($kconfig{$kconfig} eq 'n') {
print "# $kconfig is not set\n";
} else {
print "$kconfig=$kconfig{$kconfig}\n";
}
}
}
my %dep_check;
sub __find_package_dep($$) {
my $pkg = shift;
my $name = shift;
my $deps = $pkg->{depends};
return 0 unless defined $deps;
foreach my $vpkg (@{$deps}) {
foreach my $dep (@{$vpackage{$vpkg}}) {
next if $dep_check{$dep->{name}};
$dep_check{$dep->{name}} = 1;
return 1 if $dep->{name} eq $name;
return 1 if (__find_package_dep($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;
my $ret;
return 0 if ($a->{submenu} ne $b->{submenu});
if (find_package_dep($a, $b->{name}) == 1) {
$ret = 1;
} elsif (find_package_dep($b, $a->{name}) == 1) {
$ret = -1;
} else {
return 0;
}
return $ret;
}
sub mconf_depends {
my $pkgname = shift;
my $depends = shift;
my $only_dep = shift;
my $res;
my $dep = shift;
my $seen = shift;
my $parent_condition = shift;
$dep or $dep = {};
$seen or $seen = {};
my @t_depends;
$depends or return;
my @depends = @$depends;
foreach my $depend (@depends) {
my $m = "depends on";
my $flags = "";
$depend =~ s/^([@\+]+)// and $flags = $1;
my $condition = $parent_condition;
$depend = $2 if $depend =~ /^(.+):(.+)$/ and $dep->{$1} eq 'select';
next if $condition eq $depend;
next if $seen->{"$parent_condition:$depend"};
next if $seen->{":$depend"};
$seen->{"$parent_condition:$depend"} = 1;
if ($depend =~ /^(.+):(.+)$/) {
if ($1 ne "PACKAGE_$pkgname") {
if ($condition) {
$condition = "$condition && $1";
} else {
$condition = $1;
}
}
$depend = $2;
}
if ($flags =~ /\+/) {
my $vdep = $vpackage{$depend};
if ($vdep) {
my @vdeps;
foreach my $v (@$vdep) {
next if $v->{buildonly};
if ($v->{variant_default}) {
unshift @vdeps, $v->{name};
} else {
push @vdeps, $v->{name};
}
}
$depend = shift @vdeps;
if (@vdeps > 1) {
$condition = ($condition ? "$condition && " : '') . join("&&", map { "PACKAGE_$_<PACKAGE_$pkgname" } @vdeps);
} elsif (@vdeps > 0) {
$condition = ($condition ? "$condition && " : '') . "PACKAGE_${vdeps[0]}<PACKAGE_$pkgname";
}
}
# Menuconfig will not treat 'select FOO' as a real dependency
# thus if FOO depends on other config options, these dependencies
# will not be checked. To fix this, we simply emit all of FOO's
# depends here as well.
$package{$depend} and push @t_depends, [ $package{$depend}->{depends}, $condition ];
$m = "select";
next if $only_dep;
$flags =~ /@/ or $depend = "PACKAGE_$depend";
} else {
my $vdep = $vpackage{$depend};
if ($vdep && @$vdep > 0) {
$depend = join("||", map { "PACKAGE_".$_->{name} } @$vdep);
} else {
$flags =~ /@/ or $depend = "PACKAGE_$depend";
}
}
if ($condition) {
if ($m =~ /select/) {
next if $depend eq $condition;
$depend = "$depend if $condition";
} else {
next if $dep->{"$depend if $condition"};
$depend = "!($condition) || $depend" unless $dep->{$condition} eq 'select';
}
}
$dep->{$depend} =~ /select/ or $dep->{$depend} = $m;
}
foreach my $tdep (@t_depends) {
mconf_depends($pkgname, $tdep->[0], 1, $dep, $seen, $tdep->[1]);
}
foreach my $depend (sort keys %$dep) {
my $m = $dep->{$depend};
$res .= "\t\t$m $depend\n";
}
return $res;
}
sub mconf_conflicts {
my $pkgname = shift;
my $depends = shift;
my $res = "";
foreach my $depend (@$depends) {
next unless $package{$depend};
$res .= "\t\tdepends on m || (PACKAGE_$depend != y)\n";
}
return $res;
}
sub add_implicit_provides_conflicts {
foreach my $provide (keys %vpackage) {
next if $provide =~ /-any$/;
my $providers = $vpackage{$provide};
next unless $providers && @$providers > 1;
my $default_pkg;
my @non_defaults;
foreach my $pkg (@$providers) {
next if $pkg->{buildonly};
if ($pkg->{variant_default}) {
$default_pkg = $pkg;
} else {
push @non_defaults, $pkg;
}
}
next unless $default_pkg && @non_defaults;
my %existing_conflicts;
if ($default_pkg->{conflicts}) {
%existing_conflicts = map { $_ => 1 } @{$default_pkg->{conflicts}};
}
foreach my $non_default (@non_defaults) {
next if $existing_conflicts{$non_default->{name}};
my $already_conflicts = 0;
if ($non_default->{conflicts}) {
foreach my $c (@{$non_default->{conflicts}}) {
if ($c eq $default_pkg->{name}) {
$already_conflicts = 1;
last;
}
}
}
next if $already_conflicts;
$default_pkg->{conflicts} ||= [];
push @{$default_pkg->{conflicts}}, $non_default->{name};
}
}
}
sub print_package_config_category($) {
my $cat = shift;
my %menus;
my %menu_dep;
return unless $category{$cat};
print "menu \"$cat\"\n\n";
my %spkg = %{$category{$cat}};
foreach my $spkg (sort {uc($a) cmp uc($b)} keys %spkg) {
foreach my $pkg (@{$spkg{$spkg}}) {
next if $pkg->{buildonly};
my $menu = $pkg->{submenu};
if ($menu) {
$menu_dep{$menu} or $menu_dep{$menu} = $pkg->{submenudep};
} else {
$menu = 'undef';
}
$menus{$menu} or $menus{$menu} = [];
push @{$menus{$menu}}, $pkg;
}
}
my @menus = sort {
($a eq 'undef' ? 1 : 0) or
($b eq 'undef' ? -1 : 0) or
($a cmp $b)
} keys %menus;
foreach my $menu (@menus) {
my @pkgs = sort {
package_depends($a, $b) or
($a->{name} cmp $b->{name})
} @{$menus{$menu}};
if ($menu ne 'undef') {
$menu_dep{$menu} and print "if $menu_dep{$menu}\n";
print "menu \"$menu\"\n";
}
foreach my $pkg (@pkgs) {
next if $pkg->{src}{ignore};
my $title = $pkg->{name};
my $c = (72 - length($pkg->{name}) - length($pkg->{title}));
if ($c > 0) {
$title .= ("." x $c). " ". $pkg->{title};
}
$title = "\"$title\"";
print "\t";
$pkg->{menu} and print "menu";
print "config PACKAGE_".$pkg->{name}."\n";
$pkg->{hidden} and $title = "";
print "\t\t".($pkg->{tristate} ? 'tristate' : 'bool')." $title\n";
print "\t\tdefault y if DEFAULT_".$pkg->{name}."\n";
unless ($pkg->{hidden}) {
my @def = ("ALL");
if (!exists($pkg->{repository})) {
push @def, "ALL_NONSHARED";
}
if ($pkg->{name} =~ /^kmod-/) {
push @def, "ALL_KMODS";
}
$pkg->{default} ||= "m if " . join("||", @def);
}
if ($pkg->{default}) {
foreach my $default (split /\s*,\s*/, $pkg->{default}) {
print "\t\tdefault $default\n";
}
}
print mconf_depends($pkg->{name}, $pkg->{depends}, 0);
print mconf_depends($pkg->{name}, $pkg->{mdepends}, 0);
print mconf_conflicts($pkg->{name}, $pkg->{conflicts});
print "\t\thelp\n";
print $pkg->{description};
print "\n";
$pkg->{config} and print $pkg->{config}."\n";
}
if ($menu ne 'undef') {
print "endmenu\n";
$menu_dep{$menu} and print "endif\n";
}
}
print "endmenu\n\n";
undef $category{$cat};
}
sub print_package_overrides() {
keys %overrides > 0 or return;
print "\tconfig OVERRIDE_PKGS\n";
print "\t\tstring\n";
print "\t\tdefault \"".join(" ", sort keys %overrides)."\"\n\n";
}
sub gen_package_config() {
parse_package_metadata($ARGV[0]) or exit 1;
add_implicit_provides_conflicts();
print "menuconfig IMAGEOPT\n\tbool \"Image configuration\"\n\tdefault n\n";
print "source \"package/*/image-config.in\"\n";
if (scalar glob "package/feeds/*/*/image-config.in") {
print "source \"package/feeds/*/*/image-config.in\"\n";
}
print_package_config_category 'Base system';
foreach my $cat (sort {uc($a) cmp uc($b)} keys %category) {
print_package_config_category $cat;
}
print_package_overrides();
}
sub and_condition($) {
my $condition = shift;
my @spl_and = split('\&\&', $condition);
if (@spl_and == 1) {
return "\$(CONFIG_$spl_and[0])";
}
return "\$(and " . join (',', map("\$(CONFIG_$_)", @spl_and)) . ")";
}
sub gen_condition ($) {
my $condition = shift;
# remove '!()', just as include/package-pack.mk does
$condition =~ s/[()!]//g;
return join("", map(and_condition($_), split('\|\|', $condition)));
}
sub get_conditional_dep($$) {
my $condition = shift;
my $depstr = shift;
if ($condition) {
if ($condition =~ /^!(.+)/) {
return "\$(if " . gen_condition($1) . ",,$depstr)";
} else {
return "\$(if " . gen_condition($condition) . ",$depstr)";
}
} else {
return $depstr;
}
}
sub gen_package_mk() {
my $line;
parse_package_metadata($ARGV[0]) or exit 1;
foreach my $srcname (sort {uc($a) cmp uc($b)} keys %srcpackage) {
my $src = $srcpackage{$srcname};
my $variant_default;
my @variants;
my %variant_config;
my %deplines = ('' => {});
foreach my $pkg (@{$src->{packages}}) {
foreach my $dep (@{$pkg->{depends}}) {
next if ($dep =~ /@/);
my $condition;
$dep =~ s/\+//g;
if ($dep =~ /^(.+):(.+)/) {
$condition = $1;
$dep = $2;
}
my $vpkg_dep = $vpackage{$dep};
unless (defined $vpkg_dep) {
warn sprintf "WARNING: Makefile '%s' has a dependency on '%s', which does not exist\n",
$src->{makefile}, $dep;
next;
}
# Filter out self-depends
my @vdeps = grep { $srcname ne $_->{src}{name} } @{$vpkg_dep};
foreach my $vdep (@vdeps) {
my $depstr = sprintf '$(curdir)/%s/compile', $vdep->{src}{path};
if (@vdeps > 1) {
$depstr = sprintf '$(if $(CONFIG_PACKAGE_%s),%s)', $vdep->{name}, $depstr;
}
my $depline = get_conditional_dep($condition, $depstr);
if ($depline) {
$deplines{''}{$depline}++;
}
}
}
my $config = '';
$config = sprintf '$(CONFIG_PACKAGE_%s)', $pkg->{name} unless $pkg->{buildonly};
$pkg->{prereq} and printf "prereq-%s += %s\n", $config, $src->{path};
next if $pkg->{buildonly};
printf "package-%s += %s\n", $config, $src->{path};
if ($pkg->{variant}) {
if (!defined($variant_default) or $pkg->{variant_default}) {
$variant_default = $pkg->{variant};
}
push @variants, $pkg->{variant} unless defined $variant_config{$pkg->{variant}};
$variant_config{$pkg->{variant}} .= $config;
}
}
foreach my $variant (@variants) {
printf "\$(curdir)/%s/variants += \$(if %s,%s)\n", $src->{path}, $variant_config{$variant}, $variant;
}
if (defined($variant_default)) {
printf "\$(curdir)/%s/default-variant := %s\n", $src->{path}, $variant_default;
}
if ($src->{parallel_variants}) {
printf "\$(curdir)/%s/parallel-variants := 1\n", $src->{path};
}
unless (grep {!$_->{buildonly}} @{$src->{packages}}) {
printf "package- += %s\n", $src->{path};
}
if (@{$src->{buildtypes}} > 0) {
printf "buildtypes-%s = %s\n", $src->{path}, join(' ', @{$src->{buildtypes}});
}
foreach my $type ('', @{$src->{buildtypes}}) {
my $suffix = '';
$suffix = "/$type" if $type;
next unless $src->{"builddepends$suffix"};
defined $deplines{$suffix} or $deplines{$suffix} = {};
foreach my $dep (@{$src->{"builddepends$suffix"}}) {
my $depsuffix = "";
my $deptype = "";
my $condition;
if ($dep =~ /^(.+):(.+)/) {
$condition = $1;
$dep = $2;
}
if ($dep =~ /^(.+)\/(.+)/) {
$dep = $1;
$deptype = $2;
$depsuffix = "/$2";
}
next if $srcname.$suffix eq $dep.$depsuffix;
my $src_dep = $srcpackage{$dep};
unless (defined($src_dep) && (!$deptype || grep { $_ eq $deptype } @{$src_dep->{buildtypes}})) {
warn sprintf "WARNING: Makefile '%s' has a build dependency on '%s', which does not exist\n",
$src->{makefile}, $dep.$depsuffix;
next;
}
my $depstr = sprintf '$(curdir)/%s/compile', $src_dep->{path}.$depsuffix;
my $depline = get_conditional_dep($condition, $depstr);
if ($depline) {
$deplines{$suffix}{$depline}++;
}
}
}
foreach my $suffix (sort keys %deplines) {
my $depline = join(" ", sort keys %{$deplines{$suffix}});
if ($depline) {
$line .= sprintf "\$(curdir)/%s/compile += %s\n", $src->{path}.$suffix, $depline;
}
}
}
if ($line ne "") {
print "\n$line";
}
}
sub gen_package_source() {
parse_package_metadata($ARGV[0]) or exit 1;
foreach my $name (sort {uc($a) cmp uc($b)} keys %package) {
my $pkg = $package{$name};
if ($pkg->{name} && $pkg->{source}) {
print "$pkg->{name}: ";
print "$pkg->{source}\n";
}
}
}
sub gen_package_auxiliary() {
parse_package_metadata($ARGV[0]) or exit 1;
foreach my $name (sort {uc($a) cmp uc($b)} keys %package) {
my $pkg = $package{$name};
if ($pkg->{name} && $pkg->{repository}) {
print "Package/$name/subdir = $pkg->{repository}\n";
}
my %depends;
foreach my $dep (@{$pkg->{depends} || []}) {
next unless $dep =~ m!^\+?(?:([^:]+):)?([^@]+)$!;
my ($condition, $depname) = ($1, $2);
$depends{get_conditional_dep($condition, $depname)}++;
}
my @depends = sort keys %depends;
if (@depends > 0) {
foreach my $n (@{$pkg->{provides}}) {
# A real package of that name keeps its own dependencies
next if $n ne $name && $package{$n};
print "Package/$n/depends = @depends\n";
}
}
}
}
sub gen_package_license($) {
my $level = shift;
parse_package_metadata($ARGV[0]) or exit 1;
foreach my $name (sort {uc($a) cmp uc($b)} keys %package) {
my $pkg = $package{$name};
if ($pkg->{name}) {
if ($pkg->{license}) {
print "$pkg->{name}: ";
print "$pkg->{license}\n";
if ($pkg->{licensefiles} && $level == 0) {
print "\tFiles: $pkg->{licensefiles}\n";
}
} else {
if ($level == 1) {
print "$pkg->{name}: Missing license! ";
print "Please fix $pkg->{src}{makefile}\n";
}
}
}
}
}
sub gen_version_filtered_list() {
foreach my $item (version_filter_list(@ARGV)) {
print "$item\n";
}
}
sub gen_usergroup_list() {
parse_package_metadata($ARGV[0]) or exit 1;
for my $name (keys %usernames) {
print "user $name $usernames{$name}{id} $usernames{$name}{makefile}\n";
}
for my $name (keys %groupnames) {
print "group $name $groupnames{$name}{id} $groupnames{$name}{makefile}\n";
}
}
sub gen_package_manifest_json() {
my $json;
parse_package_metadata($ARGV[0]) or exit 1;
foreach my $name (sort {uc($a) cmp uc($b)} keys %package) {
my %depends;
my $pkg = $package{$name};
foreach my $dep (@{$pkg->{depends} || []}) {
if ($dep =~ m!^\+?(?:[^:]+:)?([^@]+)$!) {
$depends{$1}++;
}
}
my @depends = sort keys %depends;
my $pkg_deps = join ' ', map { qq/"$_",/ } @depends;
$pkg_deps =~ s/\,$//;
my $pkg_maintainer = join ' ', map { qq/"$_",/ } @{$pkg->{maintainer} || []};
$pkg_maintainer =~ s/\,$//;
$json = <<"END_JSON";
${json}{
"name":"$name",
"version":"$pkg->{version}",
"category":"$pkg->{category}",
"license":"$pkg->{license}",
"cpe_id":"$pkg->{cpe_id}",
"maintainer": [$pkg_maintainer],
"depends":[$pkg_deps]},
END_JSON
}
$json =~ s/[\n\r]//g;
$json =~ s/\,$//;
print "[$json]";
}
sub image_manifest_packages($)
{
my %packages;
my $imgmanifest = shift;
open FILE, "<$imgmanifest" or return;
while (<FILE>) {
/^(.+?) - (.+)$/ and $packages{$1} = $2;
}
close FILE;
return %packages;
}
sub dump_cyclonedxsbom_json {
my (@components) = @_;
my $uuid = sprintf(
"%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
rand(0xffff), rand(0xffff), rand(0xffff),
rand(0x0fff) | 0x4000,
rand(0x3fff) | 0x8000,
rand(0xffff), rand(0xffff), rand(0xffff)
);
my $cyclonedx = {
bomFormat => "CycloneDX",
specVersion => "1.4",
serialNumber => "urn:uuid:$uuid",
version => 1,
metadata => {
timestamp => gmtime->datetime . 'Z',
},
"components" => [@components],
};
return encode_json($cyclonedx);
}
sub gen_image_cyclonedxsbom() {
my $pkginfo = shift @ARGV;
my $imgmanifest = shift @ARGV;
my @components;
my %image_packages;
%image_packages = image_manifest_packages($imgmanifest);
%image_packages or exit 1;
parse_package_metadata($pkginfo) or exit 1;
$package{"kernel"} = {
license => "GPL-2.0",
cpe_id => "cpe:/o:linux:linux_kernel",
name => "kernel",
category => "operating-system",
};
my %abimap;
my @abipkgs = grep { defined $package{$_}->{abi_version} } keys %package;
foreach my $name (@abipkgs) {
my $pkg = $package{$name};
my $abipkg = $name . $pkg->{abi_version};
$abimap{$abipkg} = $name;
}
foreach my $name (sort {uc($a) cmp uc($b)} keys %image_packages) {
my $pkg = $package{$name};
if (!$pkg) {
$pkg = $package{$abimap{$name}};
next if !$pkg;
}
my @licenses;
my @license = split(/\s+/, $pkg->{license});
foreach my $lic (@license) {
push @licenses, (
{ "license" => { "name" => $lic } }
);
}
my $type;
if ($pkg->{category}) {
my $category = $pkg->{category};
my %cat_type = (
"operating-system" => "operating-system",
"Firmware" => "firmware",
"Libraries" => "library"
);
if ($cat_type{$category}) {
$type = $cat_type{$category};
} else {
$type = "application";
}
}
my $version = $pkg->{version};
if ($image_packages{$name}) {
$version = $image_packages{$name};
}
$version =~ s/-r\d+$// if $version;
if ($name =~ /^(kernel|kmod-)/ and $version =~ /^(\d+\.\d+\.\d+)/) {
$version = $1;
}
push @components, {
name => $pkg->{name},
version => $version,
@licenses > 0 ? (licenses => [ @licenses ]) : (),
$pkg->{cpe_id} ? (cpe => $pkg->{cpe_id}.":".$version) : (),
$type ? (type => $type) : (),
$version ? (version => $version) : (),
};
}
print dump_cyclonedxsbom_json(@components);
}
sub gen_package_cyclonedxsbom() {
my $pkgmanifest = shift @ARGV;
my @components;
my %mpkgs;
%mpkgs = parse_package_manifest_metadata($pkgmanifest);
%mpkgs or exit 1;
foreach my $name (sort {uc($a) cmp uc($b)} keys %mpkgs) {
my $pkg = $mpkgs{$name};
my @licenses;
my @license = split(/\s+/, $pkg->{license});
foreach my $lic (@license) {
push @licenses, (
{ "license" => { "name" => $lic } }
);
}
my $type;
if ($pkg->{section}) {
my $section = $pkg->{section};
my %section_type = (
"firmware" => "firmware",
"libs" => "library"
);
if ($section_type{$section}) {
$type = $section_type{$section};
} else {
$type = "application";
}
}
my $version = $pkg->{version};
$version =~ s/-r\d+$// if $version;
if ($name =~ /^(kernel|kmod-)/ and $version =~ /^(\d+\.\d+\.\d+)/) {
$version = $1;
}
push @components, {
name => $name,
version => $version,
@licenses > 0 ? (licenses => [ @licenses ]) : (),
$pkg->{cpe_id} ? (cpe => $pkg->{cpe_id}.":".$version) : (),
$type ? (type => $type) : (),
$version ? (version => $version) : (),
};
}
print dump_cyclonedxsbom_json(@components);
}
sub parse_command() {
GetOptions("ignore=s", \@ignore);
my $cmd = shift @ARGV;
for ($cmd) {
/^mk$/ and return gen_package_mk();
/^config$/ and return gen_package_config();
/^kconfig/ and return gen_kconfig_overrides();
/^source$/ and return gen_package_source();
/^pkgaux$/ and return gen_package_auxiliary();
/^pkgmanifestjson$/ and return gen_package_manifest_json();
/^imgcyclonedxsbom$/ and return gen_image_cyclonedxsbom();
/^pkgcyclonedxsbom$/ and return gen_package_cyclonedxsbom();
/^license$/ and return gen_package_license(0);
/^licensefull$/ and return gen_package_license(1);
/^usergroup$/ and return gen_usergroup_list();
/^version_filter$/ and return gen_version_filtered_list();
}
die <<EOF
Available Commands:
$0 mk [file] Package metadata in makefile format
$0 config [file] Package metadata in Kconfig format
$0 kconfig [file] [config] [patchver] Kernel config overrides
$0 source [file] Package source file information
$0 pkgaux [file] Package auxiliary variables in makefile format
$0 pkgmanifestjson [file] Package manifests in JSON format
$0 imgcyclonedxsbom <file> [manifest] Image package manifest in CycloneDX SBOM JSON format
$0 pkgcyclonedxsbom <file> Package manifest in CycloneDX SBOM JSON format
$0 license [file] Package license information
$0 licensefull [file] Package license information (full list)
$0 usergroup [file] Package usergroup allocation list
$0 version_filter [patchver] [list...] Filter list of version tagged strings
Options:
--ignore <name> Ignore the source package <name>
EOF
}
parse_command();