FYI/FWIW running an external procedure in a Portfile's context

René J.V. Bertin rjvbertin at
Tue Feb 2 10:02:15 PST 2016


I've often wanted to run arbitrary code outside of a Portfile (and the port command) but using variables and procedures available when in that Portfile's context. Today I wanted to modify part of a big port's post-destroot without having to go through the whole build process again (slow machine...) so I came up with with a solution which might be of interest to others. The Tcl code below expects to live in a file that is inside the target port's portdir but the principle can of course be used in something much more flexible/generic.

HTH :)


package require Tclx
package require macports
package require Pextlib
package require portutil

# Initialise mport
# This must be done following parse of global options, as some options are
# evaluated by mportinit.
if {[catch {mportinit ui_options global_options global_variations} result]} {
    global errorInfo
    puts "$errorInfo"
    fatal "Failed to initialise MacPorts, $result"

# open the port in corresponding to the directory we're living in
if {[catch {set mport [mportopen "file://[file dirname [info script]]"]} result]} {
    global errorInfo
    ui_debug $errorInfo
    ui_error "Unable to open port: $result"
    return 1

# get the interpreter that was used for evaluating code inside the Portfile
set portinterp [ditem_key ${mport} workername]

# One can export a number of variables of interest from that interpreter like so:
# set qt_cmake_module_dir [$portinterp eval set "qt_cmake_module_dir"]
# but it is much more practical to insert the code to run into the interpreter,
# and run it there.

proc inject {name} {
    if {[info exists ::auto_index($name)]} {
        set body "# $::auto_index($name)\n"
    } else {
        set body ""
    append body [info body $name]
    set argl {}
    foreach a [info args $name] {
        if {[info default $name $a def]} {
            lappend a $def
        lappend argl $a
    list proc $name $argl $body

# the code we want to run, written exactly like it would inside the Portfile:
proc fix_cmake_modules {} {
    global destroot subport
    ui_msg "${subport} has ${destroot}"
# snip

# inject the above procedure into $portinterp
$portinterp eval [inject "fix_cmake_modules"]
# evaluate the new procedure
$portinterp eval "fix_cmake_modules"

mportclose ${mport}

More information about the macports-dev mailing list