<pre style='margin:0'>
Joshua Root (jmroot) pushed a commit to branch master
in repository macports-base.
</pre>
<p><a href="https://github.com/macports/macports-base/commit/190bf965500d751cacee08c15f6aecb90aa42863">https://github.com/macports/macports-base/commit/190bf965500d751cacee08c15f6aecb90aa42863</a></p>
<pre style="white-space: pre; background: #F8F8F8">The following commit(s) were added to refs/heads/master by this push:
<span style='display:block; white-space:pre;color:#404040;'> new 190bf96 Support startupitems installed as LaunchAgents
</span>190bf96 is described below
<span style='display:block; white-space:pre;color:#808000;'>commit 190bf965500d751cacee08c15f6aecb90aa42863
</span>Author: Joshua Root <jmr@macports.org>
AuthorDate: Tue Apr 3 00:12:24 2018 +1000
<span style='display:block; white-space:pre;color:#404040;'> Support startupitems installed as LaunchAgents
</span><span style='display:block; white-space:pre;color:#404040;'>
</span><span style='display:block; white-space:pre;color:#404040;'> Distinguish beteeen LaunchDaemons and LaunchAgents based on
</span><span style='display:block; white-space:pre;color:#404040;'> startupitem.location. Require root privileges to load/unload Daemons,
</span><span style='display:block; white-space:pre;color:#404040;'> but load Agents as $sudo_user if available and running as root, or the
</span><span style='display:block; white-space:pre;color:#404040;'> current user if not root.
</span><span style='display:block; white-space:pre;color:#404040;'>
</span><span style='display:block; white-space:pre;color:#404040;'> Check if LaunchAgents are loaded by looking in the appropriate user's
</span><span style='display:block; white-space:pre;color:#404040;'> domain, as opposed to the system domain for LaunchDaemons.
</span>---
src/macports1.0/macports.tcl | 2 +-
src/port1.0/portload.tcl | 26 +++++++++++++++++++++++---
src/port1.0/portreload.tcl | 40 +++++++++++++++++++++++++++++++---------
src/port1.0/portstartupitem.tcl | 25 +++++++++++++++----------
src/port1.0/portunload.tcl | 26 +++++++++++++++++++++++---
src/port1.0/portutil.tcl | 31 +++++++++++++++++++++++++++++++
6 files changed, 124 insertions(+), 26 deletions(-)
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/macports1.0/macports.tcl b/src/macports1.0/macports.tcl
</span><span style='display:block; white-space:pre;color:#808080;'>index 0931327..318aa27 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/macports1.0/macports.tcl
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/macports1.0/macports.tcl
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -64,7 +64,7 @@ namespace eval macports {
</span> registry.path registry.format user_home user_path user_ssh_auth_sock \
portarchivetype archivefetch_pubkeys portautoclean porttrace keeplogs portverbose destroot_umask \
rsync_server rsync_options rsync_dir startupitem_autostart startupitem_type startupitem_install \
<span style='display:block; white-space:pre;background:#ffe0e0;'>- place_worksymlink macportsuser \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ place_worksymlink macportsuser sudo_user \
</span> configureccache ccache_dir ccache_size configuredistcc configurepipe buildnicevalue buildmakejobs \
applications_dir current_phase frameworks_dir developer_dir universal_archs build_arch \
os_arch os_endian os_version os_major os_minor os_platform macosx_version macosx_sdk_version macosx_deployment_target \
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/port1.0/portload.tcl b/src/port1.0/portload.tcl
</span><span style='display:block; white-space:pre;color:#808080;'>index 7f0d6fe..2845b70 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/port1.0/portload.tcl
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/port1.0/portload.tcl
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -47,20 +47,40 @@ options load.asroot
</span> set_ui_prefix
proc portload::load_main {args} {
<span style='display:block; white-space:pre;background:#ffe0e0;'>- global UI_PREFIX subport
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ global UI_PREFIX subport sudo_user
</span> set launchctl_path ${portutil::autoconf::launchctl_path}
portstartupitem::foreach_startupitem {
if {(![info exists ::portstartupitem::load_only] || $si_name in ${::portstartupitem::load_only})
&& (![info exists ::portstartupitem::autostart_only] || !$::portstartupitem::autostart_only || $si_autostart)} {
<span style='display:block; white-space:pre;background:#ffe0e0;'>- ui_notice "$UI_PREFIX [format [msgcat::mc "Loading startupitem '%s' for %s"] $si_name $subport]"
</span> set path /Library/${si_location}/${si_plist}
if {$launchctl_path eq ""} {
return -code error [format [msgcat::mc "launchctl command was not found by configure"]]
} elseif {![file exists $path]} {
return -code error [format [msgcat::mc "Launchd plist %s was not found"] $path]
} else {
<span style='display:block; white-space:pre;background:#ffe0e0;'>- exec -ignorestderr $launchctl_path load -w $path
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set skip 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {$si_location eq "LaunchDaemons"} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {[getuid] == 0} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set uid 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ui_warn [format [msgcat::mc "Skipping load of startupitem '%s' for %s, root privileges required"] $si_name $subport]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set skip 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } elseif {[getuid] == 0} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {[info exists sudo_user]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set uid [name_to_uid $sudo_user]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ui_warn [format [msgcat::mc "Skipping load of per-user startupitem '%s' for %s (running as root)"] $si_name $subport]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set skip 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set uid [getuid]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {!$skip} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ui_notice "$UI_PREFIX [format [msgcat::mc "Loading startupitem '%s' for %s"] $si_name $subport]"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ exec_as_uid $uid {system "$launchctl_path load -w $path"}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span> }
}
}
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/port1.0/portreload.tcl b/src/port1.0/portreload.tcl
</span><span style='display:block; white-space:pre;color:#808080;'>index ec394dc..40f2e15 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/port1.0/portreload.tcl
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/port1.0/portreload.tcl
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -46,24 +46,46 @@ options reload.asroot
</span> set_ui_prefix
proc portreload::reload_main {args} {
<span style='display:block; white-space:pre;background:#ffe0e0;'>- global UI_PREFIX subport
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ global UI_PREFIX subport sudo_user
</span> set launchctl_path ${portutil::autoconf::launchctl_path}
portstartupitem::foreach_startupitem {
<span style='display:block; white-space:pre;background:#ffe0e0;'>- ui_notice "$UI_PREFIX [format [msgcat::mc "Reloading startupitem '%s' for %s"] $si_name $subport]"
</span> set path /Library/${si_location}/${si_plist}
if {$launchctl_path eq ""} {
return -code error [format [msgcat::mc "launchctl command was not found by configure"]]
} elseif {![file exists $path]} {
return -code error [format [msgcat::mc "Launchd plist %s was not found"] $path]
} else {
<span style='display:block; white-space:pre;background:#ffe0e0;'>- # Basically run port unload; port load.
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- exec -ignorestderr $launchctl_path unload -w $path
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- # Let's wait a second. #36054 suggests some ports have problems
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- # when they are re-started too quickly, and I hope the second
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- # doesn't hurt too much.
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- after 1000
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- exec -ignorestderr $launchctl_path load -w $path
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set skip 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {$si_location eq "LaunchDaemons"} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {[getuid] == 0} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set uid 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ui_warn [format [msgcat::mc "Skipping reload of startupitem '%s' for %s, root privileges required"] $si_name $subport]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set skip 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } elseif {[getuid] == 0} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {[info exists sudo_user]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set uid [name_to_uid $sudo_user]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ui_warn [format [msgcat::mc "Skipping reload of per-user startupitem '%s' for %s (running as root)"] $si_name $subport]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set skip 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set uid [getuid]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {!$skip} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ui_notice "$UI_PREFIX [format [msgcat::mc "Reloading startupitem '%s' for %s"] $si_name $subport]"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # Basically run port unload; port load.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ exec_as_uid $uid {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ system "$launchctl_path unload -w $path"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # Let's wait a second. #36054 suggests some ports have problems
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # when they are re-started too quickly, and I hope the second
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # doesn't hurt too much.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ after 1000
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ system "$launchctl_path load -w $path"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span> }
}
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/port1.0/portstartupitem.tcl b/src/port1.0/portstartupitem.tcl
</span><span style='display:block; white-space:pre;color:#808080;'>index 2a9d54e..414015d 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/port1.0/portstartupitem.tcl
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/port1.0/portstartupitem.tcl
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -490,26 +490,31 @@ proc portstartupitem::loaded {} {
</span> return {}
}
set ret {}
<span style='display:block; white-space:pre;background:#ffe0e0;'>- global os.major
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ global os.major sudo_user
</span> foreach_startupitem {
if {$si_type ne "launchd"} {
continue
}
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {$si_location eq "LaunchDaemons" && [getuid] == 0} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set uid 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } elseif {[info exists sudo_user]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set uid [name_to_uid $sudo_user]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set uid [getuid]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span> if {${os.major} >= 14} {
<span style='display:block; white-space:pre;background:#ffe0e0;'>- if {![catch {exec -ignorestderr $launchctl_path print system/${si_uniquename} >&/dev/null}]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {$si_location eq "LaunchDaemons"} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set domain system
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set domain gui/${uid}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {![catch {exec -ignorestderr $launchctl_path print ${domain}/${si_uniquename} >&/dev/null}]} {
</span> lappend ret $si_name
}
} else {
<span style='display:block; white-space:pre;background:#ffe0e0;'>- if {[getuid] == 0} {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- elevateToRoot "launchctl list"
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- set elevated 1
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- }
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- if {![catch {exec -ignorestderr $launchctl_path list ${si_uniquename} >&/dev/null}]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {![catch {exec_as_uid $uid {system "$launchctl_path list ${si_uniquename}"}}]} {
</span> lappend ret $si_name
}
<span style='display:block; white-space:pre;background:#ffe0e0;'>- if {[info exists elevated]} {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- dropPrivileges
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- }
</span> }
}
return $ret
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/port1.0/portunload.tcl b/src/port1.0/portunload.tcl
</span><span style='display:block; white-space:pre;color:#808080;'>index 0f11fa0..a143796 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/port1.0/portunload.tcl
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/port1.0/portunload.tcl
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -47,18 +47,38 @@ options unload.asroot
</span> set_ui_prefix
proc portunload::unload_main {args} {
<span style='display:block; white-space:pre;background:#ffe0e0;'>- global UI_PREFIX subport
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ global UI_PREFIX subport sudo_user
</span> set launchctl_path ${portutil::autoconf::launchctl_path}
portstartupitem::foreach_startupitem {
<span style='display:block; white-space:pre;background:#ffe0e0;'>- ui_notice "$UI_PREFIX [format [msgcat::mc "Unloading startupitem '%s' for %s"] $si_name $subport]"
</span> set path /Library/${si_location}/${si_plist}
if {$launchctl_path eq ""} {
return -code error [format [msgcat::mc "launchctl command was not found by configure"]]
} elseif {![file exists $path]} {
return -code error [format [msgcat::mc "Launchd plist %s was not found"] $path]
} else {
<span style='display:block; white-space:pre;background:#ffe0e0;'>- exec -ignorestderr $launchctl_path unload -w $path
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set skip 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {$si_location eq "LaunchDaemons"} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {[getuid] == 0} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set uid 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ui_warn [format [msgcat::mc "Skipping unload of startupitem '%s' for %s, root privileges required"] $si_name $subport]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set skip 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } elseif {[getuid] == 0} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {[info exists sudo_user]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set uid [name_to_uid $sudo_user]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ui_warn [format [msgcat::mc "Unloading per-user startupitem '%s' for %s as root"] $si_name $subport]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set uid 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set uid [getuid]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {!$skip} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ui_notice "$UI_PREFIX [format [msgcat::mc "Unloading startupitem '%s' for %s"] $si_name $subport]"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ exec_as_uid $uid {system "$launchctl_path unload -w $path"}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span> }
}
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/port1.0/portutil.tcl b/src/port1.0/portutil.tcl
</span><span style='display:block; white-space:pre;color:#808080;'>index 3573666..8e1fd34 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/port1.0/portutil.tcl
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/port1.0/portutil.tcl
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -2995,6 +2995,37 @@ proc validate_macportsuser {} {
</span> }
}
<span style='display:block; white-space:pre;background:#e0ffe0;'>+# run code as a specified user
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+proc exec_as_uid {uid code} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ global macportsuser
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set elevated 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {[geteuid] != $uid} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ elevateToRoot "exec_as_uid"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {$uid == 0} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set elevated 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ setegid [uname_to_gid [uid_to_name $uid]]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ seteuid $uid
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ui_debug "dropping privileges: euid changed to [geteuid], egid changed to [getegid]."
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set retcode ok
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {[catch {uplevel 1 $code} result]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set retcode error
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {!$elevated && [getuid] == 0 && [geteuid] != [name_to_uid $macportsuser]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # have to change to $macportsuser via root
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ elevateToRoot "exec_as_uid"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set elevated 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {$elevated} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dropPrivileges
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return -code $retcode $result
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> # dependency analysis helpers
### _libtest is private; subject to change without notice
</pre><pre style='margin:0'>
</pre>