From 0eb2609693c7ce8d4e26b6036be1b87e97aec892 Mon Sep 17 00:00:00 2001
From: Sergey Fedorov <barracuda@macos-powerpc.org>
Date: Tue, 7 Jan 2025 06:10:25 +0800
Subject: [PATCH 07/16] Hierarchical portgroup search

Credits @RJVB
---
 src/macports1.0/macports.tcl | 43 +++++++++++++++++++++++++++++++++++-
 src/port1.0/portutil.tcl     | 34 ++++++++++++++++++++++++++--
 2 files changed, 74 insertions(+), 3 deletions(-)

diff --git a/src/macports1.0/macports.tcl b/src/macports1.0/macports.tcl
index 7c5539666..534cd9752 100644
--- a/src/macports1.0/macports.tcl
+++ b/src/macports1.0/macports.tcl
@@ -1184,7 +1184,7 @@ proc mportinit {{up_ui_options {}} {up_options {}} {up_variations {}}} {
             if {[regexp $sources_conf_source_re $line _ url flags]} {
                 set flags [split $flags ,]
                 foreach flag $flags {
-                    if {$flag ni [list nosync default]} {
+                    if {$flag ni [list nosync default own_portgroups_first]} {
                         ui_warn "$sources_conf source '$line' specifies invalid flag '$flag'"
                     }
                     if {$flag eq "default"} {
@@ -2021,6 +2021,9 @@ proc macports::worker_init {workername portpath porturl portbuildpath options va
     $workername alias getportresourcepath macports::getportresourcepath
     $workername alias getportlogpath macports::getportlogpath
     $workername alias getdefaultportresourcepath macports::getdefaultportresourcepath
+    $workername alias getlocalporttreelist macports::getlocalporttreelist
+    $workername alias getlocaltreeoptions macports::getlocaltreeoptions
+    $workername alias getallporttrees macports::getallporttrees
     $workername alias getprotocol macports::getprotocol
     $workername alias getportdir macports::getportdir
     $workername alias findBinary macports::findBinary
@@ -2398,6 +2401,44 @@ proc macports::getdefaultportresourcepath {{path {}}} {
     return $proposedpath
 }
 
+##
+# @return the list of local port trees
+#
+proc macports::getlocalporttreelist {} {
+    global macports::sources
+    set sourcetreelist {}
+    foreach source $sources {
+        if {[macports::getprotocol $source] eq "file"} {
+            lappend sourcetreelist [string range [lindex ${source} 0] 7 end]
+        }
+    }
+    return ${sourcetreelist}
+}
+
+##
+# @return the options for local port tree @param tree
+#
+proc macports::getlocaltreeoptions {path} {
+    global macports::sources
+    set sourcetreelist {}
+    set path [file normalize ${path}]
+    foreach source $sources {
+        set spath [file normalize [string range [lindex ${source} 0] 7 end]]
+        if {${spath} eq ${path}} {
+            return [lrange ${source} 1 end]
+        }
+    }
+    return {}
+}
+
+proc macports::getallporttrees {} {
+    global macports::sources
+    set sourcetreelist {}
+    foreach source $sources {
+        lappend sourcetreelist [lindex ${source} 0]
+    }
+    return ${sourcetreelist}
+}
 
 ##
 # Opens a MacPorts portfile specified by a URL. The URL can be local (starting
diff --git a/src/port1.0/portutil.tcl b/src/port1.0/portutil.tcl
index 4b52d019e..fd3f002b8 100644
--- a/src/port1.0/portutil.tcl
+++ b/src/port1.0/portutil.tcl
@@ -2656,14 +2656,44 @@ proc PortGroup {group version} {
         }
     }
 
-    set groupFile [getportresourcepath $porturl "port1.0/group/${group}-${version}.tcl"]
+    # optionally look first in the port's own tree (the old default behaviour)
+    set ownfirst [lsearch [getlocaltreeoptions [file dirname [getportresourcepath $porturl]]] own_portgroups_first]
+    if {${ownfirst} >= 0} {
+        # check if the requested PortGroup exists in the current port's ports tree, but
+        # don't return a fallback variant.
+        set groupFile [getportresourcepath $porturl "port1.0/group/${group}-${version}.tcl" no]
+    }
+    if {![info exists groupFile] || ![file exists ${groupFile}]} {
+        # no luck, scan the ports tree list in much the same way port lookup works:
+        # test each tree in the order they are listed in sources.conf, until a hit is found.
+        set lsources [getlocalporttreelist]
+        foreach source ${lsources} {
+            set groupFile [file join ${source} _resources port1.0/group/${group}-${version}.tcl]
+            if {[file exists ${groupFile}]} {
+                ui_debug "PortGroup ${group} ${version} found in $source"
+                break
+            }
+        }
+        if {![info exists groupFile] || ![file exists ${groupFile}]} {
+            # still nothing; redo the resource lookup but this time with a fallback.
+            # This should catch situations where port trees are configured with a
+            # protocol that is not "file://".
+            set groupFile [getportresourcepath $porturl "port1.0/group/${group}-${version}.tcl"]
+        }
+    } else {
+        ui_debug "Custom PortGroup ${group} ${version} found: $groupFile"
+    }
 
     if {[file exists $groupFile]} {
         lappend PortInfo(portgroups) [list $group $version $groupFile]
         uplevel [list source $groupFile]
-        ui_debug "Sourcing PortGroup $group $version from $groupFile"
+        ui_debug "Sourced PortGroup $group $version from $groupFile"
     } else {
         ui_error "${subport}: PortGroup ${group} ${version} could not be located. ${group}-${version}.tcl does not exist."
+        ui_info "         Trees checked: [getallporttrees]"
+        if {${ownfirst} >= 0} {
+            ui_info "         Also checked for [getportresourcepath $porturl "port1.0/group/${group}-${version}.tcl" no]"
+        }
         return -code error "PortGroup not found"
     }
 }
-- 
2.49.0

