[PATCH RFC/PoC] scripts: {package-}metadata: use 'SUBMENU' as subcategory path

Piotr Dymacz pepe2k at gmail.com
Thu Feb 29 02:19:58 PST 2024


Motivation for this change is to allow for a more nested menu structure
when manually configuring build system with 'make {menu,n,x}config'.

The current solution provides only 2 levels of nesting (without using
external/custom config/Config.in):
  - top level menu/category (Makefile variable 'CATEGORY')
  - 2nd level sub menu/category (Makefile variable 'SUBMENU')

To allow placing packages in a more nested menu structure, the 'SUBMENU'
variable is treated as a menu path with '\' character as separator*.

Defining 'SUBMENU' as 'Foo\Bar\Qux' for package 'Bob' and 'Foo\Bar' for
package 'Ben' with this change would create menu structure as follows:

  top level menu 'CATEGORY'  --->
  ..menu 'Foo'  --->
  ....menu 'Bar'  --->
  ......menu 'Qux'  --->
  ........[] package 'Bob'
  ......[] package 'Ben'

Currently existing menus and packages sorting methods were preserved
(menu first, packages below, alphabetical order) and the only difference
applies to the usage of 'SUBMENUDEP' Makefile variable which will affect
only last path element (the one package is placed under).

* The backslash instead of a more natural slash '/' was selected due to
  the fact that some of already existing core and community packages use
  slash in 'SUBMENU' variables. As this is more a RFC/PoC, final change
  might use different character or approach for defining menu path.

Signed-off-by: Piotr Dymacz <pepe2k at gmail.com>
---
 scripts/metadata.pm         |  6 ++-
 scripts/package-metadata.pl | 96 ++++++++++++++++++++++++-------------
 2 files changed, 68 insertions(+), 34 deletions(-)

diff --git a/scripts/metadata.pm b/scripts/metadata.pm
index ecfe42c0bc..21e1a4b250 100644
--- a/scripts/metadata.pm
+++ b/scripts/metadata.pm
@@ -283,7 +283,11 @@ sub parse_package_metadata($) {
 			defined $category{$1}{$src->{name}} or $category{$1}{$src->{name}} = [];
 			push @{$category{$1}{$src->{name}}}, $pkg;
 		};
-		/^Description: \s*(.*)\s*$/ and $pkg->{description} = "\t\t $1\n". get_multiline(*FILE, "\t\t ");
+		/^Description: \s*(.*)\s*$/ and do {
+			my $tabs = 2;
+			$pkg->{submenu} and $tabs += scalar(split(/\\/, $pkg->{submenu}));
+			$pkg->{description} = ("\t" x $tabs)." $1\n".get_multiline(*FILE, ("\t" x $tabs)." ");
+		};
 		/^Type: \s*(.+)\s*$/ and do {
 			$pkg->{type} = [ split /\s+/, $1 ];
 			undef $pkg->{tristate};
diff --git a/scripts/package-metadata.pl b/scripts/package-metadata.pl
index 9e0e6dd9e5..fa6b007f38 100755
--- a/scripts/package-metadata.pl
+++ b/scripts/package-metadata.pl
@@ -143,6 +143,7 @@ sub package_depends($$) {
 }
 
 sub mconf_depends {
+	my $tabs = shift;
 	my $pkgname = shift;
 	my $depends = shift;
 	my $only_dep = shift;
@@ -233,24 +234,25 @@ sub mconf_depends {
 	}
 
 	foreach my $tdep (@t_depends) {
-		mconf_depends($pkgname, $tdep->[0], 1, $dep, $seen, $tdep->[1]);
+		mconf_depends($tabs, $pkgname, $tdep->[0], 1, $dep, $seen, $tdep->[1]);
 	}
 
 	foreach my $depend (sort keys %$dep) {
 		my $m = $dep->{$depend};
-		$res .= "\t\t$m $depend\n";
+		$res .= ("\t" x $tabs)."$m $depend\n";
 	}
 	return $res;
 }
 
 sub mconf_conflicts {
+	my $tabs = shift;
 	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";
+		$res .= ("\t" x $tabs)."depends on m || (PACKAGE_$depend != y)\n";
 	}
 	return $res;
 }
@@ -265,34 +267,69 @@ sub print_package_config_category($) {
 	print "menu \"$cat\"\n\n";
 	my %spkg = %{$category{$cat}};
 
+	$menus{$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};
+			my $menu = $menus{$cat};
+			if ($pkg->{submenu}) {
+				my @submenus = split(/\\/, $pkg->{submenu});
+				foreach (@submenus) {
+					next unless length($_);
+					if (!$menu->{$_}) {
+						$menu->{$_} = {};
+					}
+
+					$menu = $menu->{$_};
+
+					if ($_ eq $submenus[-1]) {
+						$pkg->{submenudep} and $menu->{'_deps'} = $pkg->{submenudep};
+						push(@{$menu->{'_pkgs'}}, $pkg);
+					}
+				}
 			} else {
-				$menu = 'undef';
+				push(@{$menu->{'_pkgs'}}, $pkg);
 			}
-			$menus{$menu} or $menus{$menu} = [];
-			push @{$menus{$menu}}, $pkg;
 		}
 	}
+
+	print_package_config_subcategory($menus{$cat}, 0);
+	print "endmenu\n\n";
+
+	undef $category{$cat};
+}
+
+sub print_package_config_subcategory($$) {
+	my $subcategory = shift;
+	my $tabs = shift;
+
 	my @menus = sort {
-		($a eq 'undef' ?  1 : 0) or
-		($b eq 'undef' ? -1 : 0) or
+		($a eq '_pkgs' ?  1 : 0) or
+		($b eq '_pkgs' ? -1 : 0) or
 		($a cmp $b)
-	} keys %menus;
+	} keys %{$subcategory};
+
+	$tabs++;
+	foreach (@menus) {
+		next if $_ eq '_deps';
+
+		if ($_ ne '_pkgs') {
+			$subcategory->{$_}->{'_deps'} and print "\t" x $tabs, "if $subcategory->{$_}->{'_deps'}\n";
+			print "\t" x $tabs, "menu \"$_\"\n\n";
+			print_package_config_subcategory($subcategory->{$_}, $tabs);
+			print "\t" x $tabs, "endmenu\n";
+			$subcategory->{$_}->{'_deps'} and print "\t" x $tabs, "endif\n";
+			print "\n";
+
+			next;
+		}
 
-	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";
-		}
+		} @{$subcategory->{$_}};
+
 		foreach my $pkg (@pkgs) {
 			next if $pkg->{src}{ignore};
 			my $title = $pkg->{name};
@@ -301,12 +338,12 @@ sub print_package_config_category($) {
 				$title .= ("." x $c). " ". $pkg->{title};
 			}
 			$title = "\"$title\"";
-			print "\t";
+			print "\t" x $tabs;
 			$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";
+			print "\t" x ($tabs + 1), ($pkg->{tristate} ? 'tristate' : 'bool')." $title\n";
+			print "\t" x ($tabs + 1), "default y if DEFAULT_".$pkg->{name}."\n";
 			unless ($pkg->{hidden}) {
 				my @def = ("ALL");
 				if (!exists($pkg->{repository})) {
@@ -319,26 +356,19 @@ sub print_package_config_category($) {
 			}
 			if ($pkg->{default}) {
 				foreach my $default (split /\s*,\s*/, $pkg->{default}) {
-					print "\t\tdefault $default\n";
+					print "\t" x ($tabs + 1), "default $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 mconf_depends($tabs + 1, $pkg->{name}, $pkg->{depends}, 0);
+			print mconf_depends($tabs + 1, $pkg->{name}, $pkg->{mdepends}, 0);
+			print mconf_conflicts($tabs + 1, $pkg->{name}, $pkg->{conflicts});
+			print "\t" x ($tabs + 1), "help\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() {
-- 
2.43.2




More information about the openwrt-devel mailing list