source: trunk/base/src/macports1.0/macports.tcl @ 118563

Last change on this file since 118563 was 118563, checked in by cal@…, 5 years ago

base: remove all references to macports_tcl_dir from macports1.0

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 197.4 KB
Line 
1# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:filetype=tcl:et:sw=4:ts=4:sts=4
2# macports.tcl
3# $Id: macports.tcl 118563 2014-04-04 23:04:25Z cal@macports.org $
4#
5# Copyright (c) 2002 - 2003 Apple Inc.
6# Copyright (c) 2004 - 2005 Paul Guyot, <pguyot@kallisys.net>.
7# Copyright (c) 2004 - 2006 Ole Guldberg Jensen <olegb@opendarwin.org>.
8# Copyright (c) 2004 - 2005 Robert Shaw <rshaw@opendarwin.org>
9# Copyright (c) 2004 - 2013 The MacPorts Project
10# All rights reserved.
11#
12# Redistribution and use in source and binary forms, with or without
13# modification, are permitted provided that the following conditions
14# are met:
15# 1. Redistributions of source code must retain the above copyright
16#    notice, this list of conditions and the following disclaimer.
17# 2. Redistributions in binary form must reproduce the above copyright
18#    notice, this list of conditions and the following disclaimer in the
19#    documentation and/or other materials provided with the distribution.
20# 3. Neither the name of Apple Inc. nor the names of its contributors
21#    may be used to endorse or promote products derived from this software
22#    without specific prior written permission.
23#
24# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34# POSSIBILITY OF SUCH DAMAGE.
35#
36package provide macports 1.0
37package require macports_dlist 1.0
38package require macports_util 1.0
39package require Tclx
40
41namespace eval macports {
42    namespace export bootstrap_options user_options portinterp_options open_mports ui_priorities port_phases
43    variable bootstrap_options "\
44        portdbpath binpath auto_path extra_env sources_conf prefix portdbformat \
45        portarchivetype portautoclean \
46        porttrace portverbose keeplogs destroot_umask variants_conf rsync_server rsync_options \
47        rsync_dir startupitem_type startupitem_install place_worksymlink xcodeversion xcodebuildcmd \
48        configureccache ccache_dir ccache_size configuredistcc configurepipe buildnicevalue buildmakejobs \
49        applications_dir frameworks_dir developer_dir universal_archs build_arch macosx_sdk_version macosx_deployment_target \
50        macportsuser proxy_override_env proxy_http proxy_https proxy_ftp proxy_rsync proxy_skip \
51        master_site_local patch_site_local archive_site_local buildfromsource \
52        revupgrade_autorun revupgrade_mode revupgrade_check_id_loadcmds \
53        host_blacklist preferred_hosts sandbox_enable delete_la_files cxx_stdlib \
54        packagemaker_path default_compilers pkg_post_unarchive_deletions"
55    variable user_options {}
56    variable portinterp_options "\
57        portdbpath porturl portpath portbuildpath auto_path prefix prefix_frozen portsharepath \
58        registry.path registry.format user_home \
59        portarchivetype archivefetch_pubkeys portautoclean porttrace keeplogs portverbose destroot_umask \
60        rsync_server rsync_options rsync_dir startupitem_type startupitem_install place_worksymlink macportsuser \
61        configureccache ccache_dir ccache_size configuredistcc configurepipe buildnicevalue buildmakejobs \
62        applications_dir current_phase frameworks_dir developer_dir universal_archs build_arch \
63        os_arch os_endian os_version os_major os_minor os_platform macosx_version macosx_sdk_version macosx_deployment_target \
64        packagemaker_path default_compilers sandbox_enable delete_la_files cxx_stdlib \
65        pkg_post_unarchive_deletions $user_options"
66
67    # deferred options are only computed when needed.
68    # they are not exported to the trace thread.
69    # they are not exported to the interpreter in system_options array.
70    variable portinterp_deferred_options "xcodeversion xcodebuildcmd developer_dir"
71
72    variable open_mports {}
73
74    variable ui_priorities "error warn msg notice info debug any"
75    variable port_phases "any fetch checksum"
76    variable current_phase main
77
78    variable ui_prefix "---> "
79}
80
81# Provided UI instantiations
82# For standard messages, the following priorities are defined
83#     debug, info, msg, warn, error
84# Clients of the library are expected to provide ui_prefix and ui_channels with
85# the following prototypes.
86#     proc ui_prefix {priority}
87#     proc ui_channels {priority}
88# ui_prefix returns the prefix for the messages, if any.
89# ui_channels returns a list of channels to output the message to, empty for
90#     no message.
91# if these functions are not provided, defaults are used.
92# Clients of the library may optionally provide ui_init with the following
93# prototype.
94#     proc ui_init {priority prefix channels message}
95# ui_init needs to correctly define the proc ::ui_$priority {message} or throw
96# an error.
97# if this function is not provided or throws an error, default procedures for
98# ui_$priority are defined.
99
100# ui_options accessor
101proc macports::ui_isset {val} {
102    if {[info exists macports::ui_options($val)]} {
103        if {$macports::ui_options($val) eq {yes}} {
104            return 1
105        }
106    }
107    return 0
108}
109
110
111# global_options accessor
112proc macports::global_option_isset {val} {
113    if {[info exists macports::global_options($val)]} {
114        if {$macports::global_options($val) eq {yes}} {
115            return 1
116        }
117    }
118    return 0
119}
120
121proc macports::init_logging {mport} {
122    global macports::channels macports::portdbpath
123
124    if {[getuid] == 0 && [geteuid] != 0} {
125        seteuid 0; setegid 0
126    }
127    if {[catch {macports::ch_logging $mport} err]} {
128        ui_debug "Logging disabled, error opening log file: $err"
129        return 1
130    }
131    # Add our log-channel to all already initialized channels
132    foreach key [array names channels] {
133        set macports::channels($key) [concat $macports::channels($key) debuglog]
134    }
135    return 0
136}
137proc macports::ch_logging {mport} {
138    global ::debuglog ::debuglogname
139
140    set portname [_mportkey $mport subport]
141    set portpath [_mportkey $mport portpath]
142
143    ui_debug "Starting logging for $portname"
144
145    set logname [macports::getportlogpath $portpath $portname]
146    file mkdir $logname
147    set logname [file join $logname main.log]
148
149    set ::debuglogname $logname
150
151    # Truncate the file if already exists
152    set ::debuglog [open $::debuglogname w]
153    puts $::debuglog version:1
154}
155proc macports::push_log {mport} {
156    global ::logstack ::logenabled ::debuglog ::debuglogname
157    if {![info exists ::logenabled]} {
158        if {[macports::init_logging $mport] == 0} {
159            set ::logenabled yes
160            set ::logstack [list [list $::debuglog $::debuglogname]]
161            return
162        } else {
163            set ::logenabled no
164        }
165    }
166    if {$::logenabled} {
167        if {[getuid] == 0 && [geteuid] != 0} {
168            seteuid 0; setegid 0
169        }
170        if {[catch {macports::ch_logging $mport} err]} {
171            ui_debug "Logging disabled, error opening log file: $err"
172            return
173        }
174        lappend ::logstack [list $::debuglog $::debuglogname]
175    }
176}
177
178proc macports::pop_log {} {
179    global ::logenabled ::logstack ::debuglog ::debuglogname
180    if {![info exists ::logenabled]} {
181        return -code error "pop_log called before push_log"
182    }
183    if {$::logenabled && [llength $::logstack] > 0} {
184        close $::debuglog
185        set ::logstack [lreplace $::logstack end end]
186        if {[llength $::logstack] > 0} {
187            set top [lindex $::logstack end]
188            set ::debuglog [lindex $top 0]
189            set ::debuglogname [lindex $top 1]
190        } else {
191            unset ::debuglog
192            unset ::debuglogname
193        }
194    }
195}
196
197proc set_phase {phase} {
198    global macports::current_phase
199    set macports::current_phase $phase
200    if {$phase ne {main}} {
201        set cur_time [clock format [clock seconds] -format  {%+}]
202        ui_debug "$phase phase started at $cur_time"
203    }
204}
205
206proc ui_message {priority prefix phase args} {
207    global macports::channels ::debuglog macports::current_phase
208    foreach chan $macports::channels($priority) {
209        if {[info exists ::debuglog] && ($chan eq {debuglog})} {
210            set chan $::debuglog
211            if {[info exists macports::current_phase]} {
212                set phase $macports::current_phase
213            }
214            set strprefix ":${priority}:$phase "
215            if {[lindex $args 0] eq {-nonewline}} {
216                puts -nonewline $chan $strprefix[lindex $args 1]
217            } else {
218                puts $chan $strprefix[lindex $args 0]
219            }
220
221        } else {
222            if {[lindex $args 0] eq {-nonewline}} {
223                puts -nonewline $chan $prefix[lindex $args 1]
224            } else {
225                puts $chan $prefix[lindex $args 0]
226            }
227        }
228    }
229}
230proc macports::ui_init {priority args} {
231    global macports::channels ::debuglog
232    set default_channel [macports::ui_channels_default $priority]
233    # Get the list of channels.
234    if {[llength [info commands ui_channels]] > 0} {
235        set channels($priority) [ui_channels $priority]
236    } else {
237        set channels($priority) $default_channel
238    }
239
240    # if some priority initialized after log file is being created
241    if {[info exists ::debuglog]} {
242        set channels($priority) [concat $channels($priority) debuglog]
243    }
244    # Simplify ui_$priority.
245    try {
246        set prefix [ui_prefix $priority]
247    } catch * {
248        set prefix [ui_prefix_default $priority]
249    }
250    set phases {fetch checksum}
251    try {
252        eval ::ui_init $priority $prefix $channels($priority) $args
253    } catch * {
254        interp alias {} ui_$priority {} ui_message $priority $prefix {}
255        foreach phase $phases {
256            interp alias {} ui_${priority}_$phase {} ui_message $priority $prefix $phase
257        }
258    }
259}
260
261# Default implementation of ui_prefix
262proc macports::ui_prefix_default {priority} {
263    switch -- $priority {
264        debug {
265            return "DEBUG: "
266        }
267        error {
268            return "Error: "
269        }
270        warn {
271            return "Warning: "
272        }
273        default {
274            return {}
275        }
276    }
277}
278
279# Default implementation of ui_channels:
280# ui_options(ports_debug) - If set, output debugging messages
281# ui_options(ports_verbose) - If set, output info messages (ui_info)
282# ui_options(ports_quiet) - If set, don't output "standard messages"
283proc macports::ui_channels_default {priority} {
284    switch -- $priority {
285        debug {
286            if {[ui_isset ports_debug]} {
287                return stderr
288            } else {
289                return {}
290            }
291        }
292        info {
293            if {[ui_isset ports_verbose]} {
294                return stdout
295            } else {
296                return {}
297            }
298        }
299        notice {
300            if {[ui_isset ports_quiet]} {
301                return {}
302            } else {
303                return stdout
304            }
305        }
306        msg {
307            return stdout
308        }
309        warn -
310        error {
311            return stderr
312        }
313        default {
314            return stdout
315        }
316    }
317}
318
319proc ui_warn_once {id msg} {
320    variable macports::warning_done
321    if {![info exists macports::warning_done($id)]} {
322        ui_warn $msg
323        set macports::warning_done($id) 1
324    }
325}
326
327# Replace puts to catch errors (typically broken pipes when being piped to head)
328rename puts tcl::puts
329proc puts {args} {
330    catch "tcl::puts $args"
331}
332
333# find a binary either in a path defined at MacPorts' configuration time
334# or in the PATH environment variable through macports::binaryInPath (fallback)
335proc macports::findBinary {prog {autoconf_hint {}}} {
336    if {$autoconf_hint ne {} && [file executable $autoconf_hint]} {
337        return $autoconf_hint
338    } else {
339        if {[catch {set cmd_path [macports::binaryInPath $prog]} result] == 0} {
340            return $cmd_path
341        } else {
342            return -code error "$result or at its MacPorts configuration time location, did you move it?"
343        }
344    }
345}
346
347# check for a binary in the path
348# returns an error code if it cannot be found
349proc macports::binaryInPath {prog} {
350    global env
351    foreach dir [split $env(PATH) :] {
352        if {[file executable [file join $dir $prog]]} {
353            return [file join $dir $prog]
354        }
355    }
356    return -code error [format [msgcat::mc "Failed to locate '%s' in path: '%s'"] $prog $env(PATH)];
357}
358
359# deferred option processing
360proc macports::getoption {name} {
361    global macports::$name
362    return [set $name]
363}
364
365# deferred and on-need extraction of xcodeversion and xcodebuildcmd.
366proc macports::setxcodeinfo {name1 name2 op} {
367    global macports::xcodeversion macports::xcodebuildcmd
368
369    trace remove variable macports::xcodeversion read macports::setxcodeinfo
370    trace remove variable macports::xcodebuildcmd read macports::setxcodeinfo
371
372    if {![catch {findBinary xcodebuild $macports::autoconf::xcodebuild_path} xcodebuild]} {
373        if {![info exists xcodeversion]} {
374            # Determine xcode version
375            set macports::xcodeversion 2.0orlower
376            if {[catch {set xcodebuildversion [exec -- $xcodebuild -version 2> /dev/null]}] == 0} {
377                if {[regexp {Xcode ([0-9.]+)} $xcodebuildversion - xcode_v] == 1} {
378                    set macports::xcodeversion $xcode_v
379                } elseif {[regexp {DevToolsCore-(.*);} $xcodebuildversion - devtoolscore_v] == 1} {
380                    if {$devtoolscore_v >= 1809.0} {
381                        set macports::xcodeversion 3.2.6
382                    } elseif {$devtoolscore_v >= 1204.0} {
383                        set macports::xcodeversion 3.1.4
384                    } elseif {$devtoolscore_v >= 1100.0} {
385                        set macports::xcodeversion 3.1
386                    } elseif {$devtoolscore_v >= 921.0} {
387                        set macports::xcodeversion 3.0
388                    } elseif {$devtoolscore_v >= 798.0} {
389                        set macports::xcodeversion 2.5
390                    } elseif {$devtoolscore_v >= 762.0} {
391                        set macports::xcodeversion 2.4.1
392                    } elseif {$devtoolscore_v >= 757.0} {
393                        set macports::xcodeversion 2.4
394                    } elseif {$devtoolscore_v > 650.0} {
395                        # XXX find actual version corresponding to 2.3
396                        set macports::xcodeversion 2.3
397                    } elseif {$devtoolscore_v >= 650.0} {
398                        set macports::xcodeversion 2.2.1
399                    } elseif {$devtoolscore_v > 620.0} {
400                        # XXX find actual version corresponding to 2.2
401                        set macports::xcodeversion 2.2
402                    } elseif {$devtoolscore_v >= 620.0} {
403                        set macports::xcodeversion 2.1
404                    }
405                }
406            } else {
407                ui_warn "xcodebuild exists but failed to execute"
408                set macports::xcodeversion none
409            }
410        }
411        if {![info exists xcodebuildcmd]} {
412            set macports::xcodebuildcmd $xcodebuild
413        }
414    } else {
415        if {![info exists xcodeversion]} {
416            set macports::xcodeversion none
417        }
418        if {![info exists xcodebuildcmd]} {
419            set macports::xcodebuildcmd none
420        }
421    }
422}
423
424# deferred calculation of developer_dir
425proc macports::set_developer_dir {name1 name2 op} {
426    global macports::developer_dir macports::os_major macports::xcodeversion
427
428    trace remove variable macports::developer_dir read macports::set_developer_dir
429
430    # Look for xcodeselect, and make sure it has a valid value
431    if {![catch {findBinary xcode-select $macports::autoconf::xcode_select_path} xcodeselect]} {
432
433        # We have xcode-select: ask it where xcode is and check if it's valid.
434        # If no xcode is selected, xcode-select will fail, so catch that
435        if {![catch {exec $xcodeselect -print-path 2> /dev/null} devdir] &&
436            [_is_valid_developer_dir $devdir]} {
437            set macports::developer_dir $devdir
438            return
439        }
440
441        # The directory from xcode-select isn't correct.
442
443        # Ask mdfind where Xcode is and make some suggestions for the user,
444        # searching by bundle identifier for various Xcode versions (3.x and 4.x)
445        set installed_xcodes {}
446        if {![catch {findBinary mdfind $macports::autoconf::mdfind_path} mdfind]} {
447            set installed_xcodes [exec $mdfind "kMDItemCFBundleIdentifier == 'com.apple.Xcode' || kMDItemCFBundleIdentifier == 'com.apple.dt.Xcode'"]
448        }
449
450        # In case mdfind metadata wasn't complete, also look in two well-known locations for Xcode.app
451        foreach app {/Applications/Xcode.app /Developer/Applications/Xcode.app} {
452            if {[file isdirectory $app]} {
453                lappend installed_xcodes $app
454            }
455        }
456
457        # Form a list of unique xcode installations
458        set installed_xcodes [lsort -unique $installed_xcodes]
459
460        # Present instructions to the user
461        ui_error
462        if {[llength $installed_xcodes] > 0 && ![catch {findBinary mdls $macports::autoconf::mdls_path} mdls]} {
463            # One, or more than one, Xcode installations found
464            ui_error "No valid Xcode installation is properly selected."
465            ui_error "Please use xcode-select to select an Xcode installation:"
466            foreach xcode $installed_xcodes {
467                set vers [exec $mdls -raw -name kMDItemVersion $xcode]
468                if {$vers eq {(null)}} {set vers unknown}
469                if {[_is_valid_developer_dir ${xcode}/Contents/Developer]} {
470                    # Though xcode-select shipped with xcode 4.3 supports and encourages
471                    # direct use of the app path, older xcode-select does not.
472                    # Specify the Contents/Developer directory if it exists
473                    ui_error "    sudo xcode-select -switch ${xcode}/Contents/Developer # version $vers"
474                } elseif {[vercmp $vers 4.3] >= 0} {
475                    # Future proofing: fall back to the app-path only for xcode >= 4.3, since Contents/Developer doesn't exist
476                    ui_error "    sudo xcode-select -switch $xcode # version $vers"
477                } elseif {[_is_valid_developer_dir ${xcode}/../..]} {
478                    # Older xcode (< 4.3) is below the developer directory
479                    ui_error "    sudo xcode-select -switch [file normalize ${xcode}/../..] # version $vers"
480                } else {
481                    ui_error "    # malformed Xcode at ${xcode}, version $vers"
482                }
483            }
484        } else {
485            ui_error "No Xcode installation was found."
486            ui_error "Please install Xcode and/or run xcode-select to specify its location."
487        }
488        ui_error
489    }
490
491    # Try the default
492    if {$os_major >= 11 && [vercmp $xcodeversion 4.3] >= 0} {
493        set devdir /Applications/Xcode.app/Contents/Developer
494    } else {
495        set devdir /Developer
496    }
497
498    set macports::developer_dir $devdir
499}
500
501proc macports::_is_valid_developer_dir {dir} {
502    # Check whether specified directory looks valid for an Xcode installation
503
504    # Verify that the directory exists
505    if {![file isdirectory $dir]} {
506        return 0
507    }
508
509    # Verify that the directory has some key subdirectories
510    foreach subdir {Library usr} {
511        if {![file isdirectory ${dir}/$subdir]} {
512            return 0
513        }
514    }
515
516    # The specified directory seems valid for Xcode
517    return 1
518}
519
520
521proc mportinit {{up_ui_options {}} {up_options {}} {up_variations {}}} {
522    if {$up_ui_options eq {}} {
523        array set macports::ui_options {}
524    } else {
525        upvar $up_ui_options temp_ui_options
526        array set macports::ui_options [array get temp_ui_options]
527    }
528    if {$up_options eq {}} {
529        array set macports::global_options {}
530    } else {
531        upvar $up_options temp_options
532        array set macports::global_options [array get temp_options]
533    }
534    if {$up_variations eq {}} {
535        array set variations {}
536    } else {
537        upvar $up_variations variations
538    }
539
540    # Initialize ui_*
541    foreach priority $macports::ui_priorities {
542        macports::ui_init $priority
543    }
544
545
546    global auto_path env tcl_platform \
547        macports::autoconf::macports_conf_path \
548        macports::macports_user_dir \
549        macports::bootstrap_options \
550        macports::user_options \
551        macports::portconf \
552        macports::portsharepath \
553        macports::registry.format \
554        macports::registry.path \
555        macports::sources \
556        macports::sources_default \
557        macports::destroot_umask \
558        macports::prefix \
559        macports::macportsuser \
560        macports::prefix_frozen \
561        macports::xcodebuildcmd \
562        macports::xcodeversion \
563        macports::configureccache \
564        macports::ccache_dir \
565        macports::ccache_size \
566        macports::configuredistcc \
567        macports::configurepipe \
568        macports::buildnicevalue \
569        macports::buildmakejobs \
570        macports::universal_archs \
571        macports::build_arch \
572        macports::os_arch \
573        macports::os_endian \
574        macports::os_version \
575        macports::os_major \
576        macports::os_minor \
577        macports::os_platform \
578        macports::macosx_version \
579        macports::macosx_sdk_version \
580        macports::macosx_deployment_target \
581        macports::archivefetch_pubkeys \
582        macports::ping_cache \
583        macports::host_blacklisted \
584        macports::host_preferred \
585        macports::delete_la_files \
586        macports::cxx_stdlib
587
588    # Set the system encoding to utf-8
589    encoding system utf-8
590
591    # Set up signal handling for SIGTERM and SIGINT
592    # Specifying error here will case the program to abort where it is with
593    # a Tcl error, which can be caught, if necessary.
594    signal -restart error {TERM INT}
595
596    # set up platform info variables
597    set os_arch $tcl_platform(machine)
598    if {$os_arch eq {Power Macintosh}} {set os_arch "powerpc"}
599    if {$os_arch eq {i586} || $os_arch eq {i686} || $os_arch eq {x86_64}} {set os_arch "i386"}
600    set os_version $tcl_platform(osVersion)
601    set os_major [lindex [split $os_version .] 0]
602    set os_minor [lindex [split $os_version .] 1]
603    set os_platform [string tolower $tcl_platform(os)]
604    # Remove trailing "Endian"
605    set os_endian [string range $tcl_platform(byteOrder) 0 end-6]
606    set macosx_version {}
607    if {$os_platform eq "darwin" && [file executable /usr/bin/sw_vers]} {
608        if {![catch {exec /usr/bin/sw_vers -productVersion | cut -f1,2 -d.} result]} {
609            set macosx_version $result
610        } else {
611            ui_debug "sw_vers exists but running it failed: $result"
612        }
613    }
614
615    # Check that the current platform is the one we were configured for, otherwise need to do migration
616    if {($os_platform ne $macports::autoconf::os_platform) || ($os_major != $macports::autoconf::os_major)} {
617        ui_error "Current platform \"$os_platform $os_major\" does not match expected platform \"$macports::autoconf::os_platform $macports::autoconf::os_major\""
618        ui_error "If you upgraded your OS, please follow the migration instructions: https://trac.macports.org/wiki/Migration"
619        return -code error "OS platform mismatch"
620    }
621
622    # Ensure that the macports user directory (i.e. ~/.macports) exists if HOME is defined.
623    # Also save $HOME for later use before replacing it with our own.
624    if {[info exists env(HOME)]} {
625        set macports::user_home $env(HOME)
626        set macports::macports_user_dir [file normalize $macports::autoconf::macports_user_dir]
627    } elseif {[info exists env(SUDO_USER)] && $os_platform eq {darwin}} {
628        set macports::user_home [exec dscl -q . -read /Users/$env(SUDO_USER) NFSHomeDirectory | cut -d ' ' -f 2]
629        set macports::macports_user_dir [file join $macports::user_home [string range $macports::autoconf::macports_user_dir 2 end]]
630    } elseif {[exec id -u] != 0 && $os_platform eq {darwin}} {
631        set macports::user_home [exec dscl -q . -read /Users/[exec id -un] NFSHomeDirectory | cut -d ' ' -f 2]
632        set macports::macports_user_dir [file join $macports::user_home [string range $macports::autoconf::macports_user_dir 2 end]]
633    } else {
634        # Otherwise define the user directory as a directory that will never exist
635        set macports::macports_user_dir /dev/null/NO_HOME_DIR
636        set macports::user_home /dev/null/NO_HOME_DIR
637    }
638
639    # Configure the search path for configuration files
640    set conf_files {}
641    lappend conf_files ${macports_conf_path}/macports.conf
642    if {[file isdirectory $macports_user_dir]} {
643        lappend conf_files ${macports_user_dir}/macports.conf
644    }
645    if {[info exists env(PORTSRC)]} {
646        set PORTSRC $env(PORTSRC)
647        lappend conf_files $PORTSRC
648    }
649
650    # Process all configuration files we find on conf_files list
651    foreach file $conf_files {
652        if {[file exists $file]} {
653            set portconf $file
654            set fd [open $file r]
655            while {[gets $fd line] >= 0} {
656                if {[regexp {^(\w+)([ \t]+(.*))?$} $line match option ignore val] == 1} {
657                    if {[lsearch -exact $bootstrap_options $option] >= 0} {
658                        set macports::$option [string trim $val]
659                        global macports::$option
660                    }
661                }
662            }
663            close $fd
664        }
665    }
666
667    # Process per-user only settings
668    set per_user ${macports_user_dir}/user.conf
669    if {[file exists $per_user]} {
670        set fd [open $per_user r]
671        while {[gets $fd line] >= 0} {
672            if {[regexp {^(\w+)([ \t]+(.*))?$} $line match option ignore val] == 1} {
673                if {[lsearch -exact $user_options $option] >= 0} {
674                    set macports::$option $val
675                    global macports::$option
676                }
677            }
678        }
679        close $fd
680    }
681
682    if {![info exists sources_conf]} {
683        return -code error "sources_conf must be set in ${macports_conf_path}/macports.conf or in your ${macports_user_dir}/macports.conf file"
684    }
685    set fd [open $sources_conf r]
686    while {[gets $fd line] >= 0} {
687        set line [string trimright $line]
688        if {![regexp {^\s*#|^$} $line]} {
689            if {[regexp {^([\w-]+://\S+)(?:\s+\[(\w+(?:,\w+)*)\])?$} $line _ url flags]} {
690                set flags [split $flags ,]
691                foreach flag $flags {
692                    if {[lsearch -exact [list nosync default] $flag] == -1} {
693                        ui_warn "$sources_conf source '$line' specifies invalid flag '$flag'"
694                    }
695                    if {$flag eq {default}} {
696                        if {[info exists sources_default]} {
697                            ui_warn "More than one default port source is defined."
698                        }
699                        set sources_default [concat [list $url] $flags]
700                    }
701                }
702                lappend sources [concat [list $url] $flags]
703            } else {
704                ui_warn "$sources_conf specifies invalid source '$line', ignored."
705            }
706        }
707    }
708    close $fd
709    # Make sure the default port source is defined. Otherwise
710    # [macports::getportresourcepath] fails when the first source doesn't
711    # contain _resources.
712    if {![info exists sources_default]} {
713        ui_warn "No default port source specified in ${sources_conf}, using last source as default"
714        set sources_default [lindex $sources end]
715    }
716
717    if {![info exists sources]} {
718        if {[file isdirectory ports]} {
719            set sources file://[pwd]/ports
720        } else {
721            return -code error "No sources defined in $sources_conf"
722        }
723    }
724
725    if {[info exists variants_conf]} {
726        if {[file exists $variants_conf]} {
727            set fd [open $variants_conf r]
728            while {[gets $fd line] >= 0} {
729                set line [string trimright $line]
730                if {![regexp {^[\ \t]*#.*$|^$} $line]} {
731                    foreach arg [split $line " \t"] {
732                        if {[regexp {^([-+])([-A-Za-z0-9_+\.]+)$} $arg match sign opt] == 1} {
733                            if {![info exists variations($opt)]} {
734                                set variations($opt) $sign
735                            }
736                        } else {
737                            ui_warn "$variants_conf specifies invalid variant syntax '$arg', ignored."
738                        }
739                    }
740                }
741            }
742            close $fd
743        } else {
744            ui_debug "$variants_conf does not exist, variants_conf setting ignored."
745        }
746    }
747    global macports::global_variations
748    array set macports::global_variations [array get variations]
749
750    # pubkeys.conf
751    set macports::archivefetch_pubkeys {}
752    if {[file isfile [file join $macports_conf_path pubkeys.conf]]} {
753        set fd [open [file join $macports_conf_path pubkeys.conf] r]
754        while {[gets $fd line] >= 0} {
755            set line [string trim $line]
756            if {![regexp {^[\ \t]*#.*$|^$} $line]} {
757                lappend macports::archivefetch_pubkeys $line
758            }
759        }
760        close $fd
761    } else {
762        ui_debug "pubkeys.conf does not exist."
763    }
764
765    if {![info exists portdbpath]} {
766        return -code error "portdbpath must be set in ${macports_conf_path}/macports.conf or in your ${macports_user_dir}/macports.conf"
767    }
768    if {![file isdirectory $portdbpath]} {
769        if {![file exists $portdbpath]} {
770            if {[catch {file mkdir $portdbpath} result]} {
771                return -code error "portdbpath $portdbpath does not exist and could not be created: $result"
772            }
773        } else {
774            return -code error "$portdbpath is not a directory. Please create the directory $portdbpath and try again"
775        }
776    }
777
778    set env(HOME) [file join $portdbpath home]
779    set registry.path $portdbpath
780
781    # Format for receipts; currently only "sqlite" is allowed
782    # could previously be "flat", so we switch that to sqlite
783    if {![info exists portdbformat] || $portdbformat eq {flat} || $portdbformat eq {sqlite}} {
784        set registry.format receipt_sqlite
785    } else {
786        return -code error "unknown registry format '$portdbformat' set in macports.conf"
787    }
788
789    # Autoclean mode, whether to automatically call clean after "install"
790    if {![info exists portautoclean]} {
791        set macports::portautoclean yes
792        global macports::portautoclean
793    }
794    # whether to keep logs after successful builds
795    if {![info exists keeplogs]} {
796        set macports::keeplogs no
797        global macports::keeplogs
798    }
799
800    # Check command line override for autoclean
801    if {[info exists macports::global_options(ports_autoclean)]} {
802        if {$macports::global_options(ports_autoclean) ne $portautoclean} {
803            set macports::portautoclean $macports::global_options(ports_autoclean)
804        }
805    }
806    # Trace mode, whether to use darwintrace to debug ports.
807    if {![info exists porttrace]} {
808        set macports::porttrace no
809        global macports::porttrace
810    }
811    # Check command line override for trace
812    if {[info exists macports::global_options(ports_trace)]} {
813        if {$macports::global_options(ports_trace) ne $porttrace} {
814            set macports::porttrace $macports::global_options(ports_trace)
815        }
816    }
817    # Check command line override for source/binary only mode
818    if {![info exists macports::global_options(ports_binary_only)]
819        && ![info exists macports::global_options(ports_source_only)]
820        && [info exists macports::buildfromsource]} {
821        if {$macports::buildfromsource eq {never}} {
822            set macports::global_options(ports_binary_only) yes
823            set temp_options(ports_binary_only) yes
824        } elseif {$macports::buildfromsource eq {always}} {
825            set macports::global_options(ports_source_only) yes
826            set temp_options(ports_source_only) yes
827        } elseif {$macports::buildfromsource ne {ifneeded}} {
828            ui_warn "'buildfromsource' set to unknown value '$macports::buildfromsource', using 'ifneeded' instead"
829        }
830    }
831
832    # Duplicate prefix into prefix_frozen, so that port actions
833    # can always get to the original prefix, even if a portfile overrides prefix
834    set macports::prefix_frozen $prefix
835
836    if {![info exists macports::applications_dir]} {
837        set macports::applications_dir /Applications/MacPorts
838    }
839
840    # Export verbosity.
841    if {![info exists portverbose]} {
842        set macports::portverbose no
843        global macports::portverbose
844    }
845    if {[info exists macports::ui_options(ports_verbose)]} {
846        if {$macports::ui_options(ports_verbose) ne $portverbose} {
847            set macports::portverbose $macports::ui_options(ports_verbose)
848        }
849    }
850
851    # Archive type, what type of binary archive to use (CPIO, gzipped
852    # CPIO, XAR, etc.)
853    global macports::portarchivetype
854    if {![info exists portarchivetype]} {
855        set macports::portarchivetype tbz2
856    } else {
857        set macports::portarchivetype [lindex $portarchivetype 0]
858    }
859
860    # Set rync options
861    if {![info exists rsync_server]} {
862        global macports::rsync_server
863        set macports::rsync_server rsync.macports.org
864    }
865    if {![info exists rsync_dir]} {
866        global macports::rsync_dir
867        set macports::rsync_dir release/tarballs/base.tar
868    }
869    if {![info exists rsync_options]} {
870        global macports::rsync_options
871        set rsync_options "-rtzv --delete-after"
872    }
873
874    set portsharepath ${prefix}/share/macports
875    if {![file isdirectory $portsharepath]} {
876        return -code error "Data files directory '$portsharepath' must exist"
877    }
878
879    if {![info exists binpath]} {
880        set env(PATH) ${prefix}/bin:${prefix}/sbin:/bin:/sbin:/usr/bin:/usr/sbin
881    } else {
882        set env(PATH) $binpath
883    }
884
885    # Set startupitem default type (can be overridden by portfile)
886    if {![info exists macports::startupitem_type]} {
887        set macports::startupitem_type default
888    }
889
890    # Set whether startupitems are symlinked into system directories
891    if {![info exists macports::startupitem_install]} {
892        set macports::startupitem_install yes
893    }
894
895    # Default place_worksymlink
896    if {![info exists macports::place_worksymlink]} {
897        set macports::place_worksymlink yes
898    }
899
900    # Default mp configure options
901    if {![info exists macports::configureccache]} {
902        set macports::configureccache no
903    }
904    if {![info exists macports::ccache_dir]} {
905        set macports::ccache_dir [file join $portdbpath build .ccache]
906    }
907    if {![info exists macports::ccache_size]} {
908        set macports::ccache_size 2G
909    }
910    if {![info exists macports::configuredistcc]} {
911        set macports::configuredistcc no
912    }
913    if {![info exists macports::configurepipe]} {
914        set macports::configurepipe yes
915    }
916
917    # Default mp build options
918    if {![info exists macports::buildnicevalue]} {
919        set macports::buildnicevalue 0
920    }
921    if {![info exists macports::buildmakejobs]} {
922        set macports::buildmakejobs 0
923    }
924
925    # default user to run as when privileges can be dropped
926    if {![info exists macports::macportsuser]} {
927        set macports::macportsuser $macports::autoconf::macportsuser
928    }
929
930    # Default mp universal options
931    if {![info exists macports::universal_archs]} {
932        if {$os_major >= 10} {
933            set macports::universal_archs {x86_64 i386}
934        } else {
935            set macports::universal_archs {i386 ppc}
936        }
937    } elseif {[llength $macports::universal_archs] < 2} {
938        ui_warn "invalid universal_archs configured (should contain at least 2 archs)"
939    }
940
941    # Default arch to build for
942    if {![info exists macports::build_arch]} {
943        if {$os_platform eq {darwin}} {
944            if {$os_major >= 10} {
945                if {[sysctl hw.cpu64bit_capable] == 1} {
946                    set macports::build_arch x86_64
947                } else {
948                    set macports::build_arch i386
949                }
950            } else {
951                if {$os_arch eq {powerpc}} {
952                    set macports::build_arch ppc
953                } else {
954                    set macports::build_arch i386
955                }
956            }
957        } else {
958            set macports::build_arch {}
959        }
960    } else {
961        set macports::build_arch [lindex $macports::build_arch 0]
962    }
963
964    if {![info exists macports::macosx_deployment_target]} {
965        set macports::macosx_deployment_target $macosx_version
966    }
967    if {![info exists macports::macosx_sdk_version]} {
968        set macports::macosx_sdk_version $macosx_version
969    }
970
971    if {![info exists macports::revupgrade_autorun]} {
972        set macports::revupgrade_autorun yes
973    }
974    if {![info exists macports::revupgrade_mode]} {
975        set macports::revupgrade_mode rebuild
976    }
977    if {![info exists macports::delete_la_files]} {
978        if {$os_platform eq "darwin" && $os_major >= 13} {
979            set macports::delete_la_files yes
980        } else {
981            set macports::delete_la_files no
982        }
983    }
984    if {![info exists macports::cxx_stdlib]} {
985        if {$os_platform eq "darwin" && $os_major >= 13} {
986            set macports::cxx_stdlib libc++
987        } elseif {$os_platform eq "darwin"} {
988            set macports::cxx_stdlib libstdc++
989        } else {
990            set macports::cxx_stdlib {}
991        }
992    }
993    if {![info exists macports::global_options(ports_rev-upgrade_id-loadcmd-check)]
994         && [info exists macports::revupgrade_check_id_loadcmds]} {
995        set macports::global_options(ports_rev-upgrade_id-loadcmd-check) $macports::revupgrade_check_id_loadcmds
996        set temp_options(ports_rev-upgrade_id-loadcmd-check) $macports::revupgrade_check_id_loadcmds
997    }
998
999    if {![info exists macports::sandbox_enable]} {
1000        set macports::sandbox_enable yes
1001    }
1002
1003    # make tools we run operate in UTF-8 mode
1004    set env(LANG) en_US.UTF-8
1005
1006    # ENV cleanup.
1007    set keepenvkeys {
1008        DISPLAY DYLD_FALLBACK_FRAMEWORK_PATH
1009        DYLD_FALLBACK_LIBRARY_PATH DYLD_FRAMEWORK_PATH
1010        DYLD_LIBRARY_PATH DYLD_INSERT_LIBRARIES
1011        HOME JAVA_HOME MASTER_SITE_LOCAL ARCHIVE_SITE_LOCAL
1012        PATCH_SITE_LOCAL PATH PORTSRC RSYNC_PROXY
1013        USER GROUP LANG
1014        http_proxy HTTPS_PROXY FTP_PROXY ALL_PROXY NO_PROXY
1015        COLUMNS LINES
1016    }
1017    if {[info exists extra_env]} {
1018        set keepenvkeys [concat $keepenvkeys $extra_env]
1019    }
1020
1021    # set the hidden flag on $portdbpath to avoid spotlight indexing, which
1022    # might slow builds down considerably. You can avoid this by touching
1023    # $portdbpath/.nohide.
1024    if {$os_platform eq "darwin" && [vercmp [info tclversion] 8.5] >= 0 && ![file exists [file join $portdbpath .nohide]] && [file writable $portdbpath] && [file attributes $portdbpath -hidden] == 0} {
1025        if {[catch {file attributes $portdbpath -hidden yes} result]} {
1026            ui_debug "error setting hidden flag for $portdbpath: $result"
1027        }
1028    }
1029
1030    # don't keep unusable TMPDIR/TMP values
1031    foreach var {TMP TMPDIR} {
1032        if {[info exists env($var)] && [file writable $env($var)] &&
1033            ([getuid] != 0 || $macportsuser eq {root} ||
1034             [file attributes $env($var) -owner] eq $macportsuser)} {
1035            lappend keepenvkeys $var
1036        }
1037    }
1038
1039    set env_names [array names env]
1040    foreach envkey $env_names {
1041        if {[lsearch -exact $keepenvkeys $envkey] == -1} {
1042            unset env($envkey)
1043        }
1044    }
1045
1046    # unset environment an extra time, to work around bugs in Leopard Tcl
1047    if {$macosx_version eq {10.5}} {
1048        foreach envkey $env_names {
1049            if {[lsearch -exact $keepenvkeys $envkey] == -1} {
1050                unsetenv $envkey
1051            }
1052        }
1053    }
1054
1055    if {![info exists xcodeversion] || ![info exists xcodebuildcmd]} {
1056        # We'll resolve these later (if needed)
1057        trace add variable macports::xcodeversion read macports::setxcodeinfo
1058        trace add variable macports::xcodebuildcmd read macports::setxcodeinfo
1059    }
1060
1061    if {![info exists developer_dir]} {
1062        if {$os_platform eq {darwin}} {
1063            trace add variable macports::developer_dir read macports::set_developer_dir
1064        } else {
1065            set macports::developer_dir {}
1066        }
1067    } else {
1068        if {$os_platform eq {darwin} && ![file isdirectory $developer_dir]} {
1069            ui_warn "Your developer_dir setting in macports.conf points to a non-existing directory.\
1070                Since this is known to cause problems, please correct the setting or comment it and let\
1071                macports auto-discover the correct path."
1072        }
1073    }
1074
1075    if {[getuid] == 0 && $os_major >= 11 && $os_platform eq {darwin} &&
1076            [file isfile "${macports::user_home}/Library/Preferences/com.apple.dt.Xcode.plist"]} {
1077        macports::copy_xcode_plist $env(HOME)
1078    }
1079
1080    # Set the default umask
1081    if {![info exists destroot_umask]} {
1082        set destroot_umask 022
1083    }
1084
1085    if {[info exists master_site_local] && ![info exists env(MASTER_SITE_LOCAL)]} {
1086        set env(MASTER_SITE_LOCAL) $master_site_local
1087    }
1088    if {[info exists patch_site_local] && ![info exists env(PATCH_SITE_LOCAL)]} {
1089        set env(PATCH_SITE_LOCAL) $patch_site_local
1090    }
1091    if {[info exists archive_site_local] && ![info exists env(ARCHIVE_SITE_LOCAL)]} {
1092        set env(ARCHIVE_SITE_LOCAL) $archive_site_local
1093    }
1094
1095    # Proxy handling (done this late since Pextlib is needed)
1096    if {![info exists proxy_override_env]} {
1097        set proxy_override_env no
1098    }
1099    if {[catch {array set sysConfProxies [get_systemconfiguration_proxies]} result]} {
1100        return -code error "Unable to get proxy configuration from system: $result"
1101    }
1102    if {![info exists env(http_proxy)] || $proxy_override_env eq {yes}} {
1103        if {[info exists proxy_http]} {
1104            set env(http_proxy) $proxy_http
1105        } elseif {[info exists sysConfProxies(proxy_http)]} {
1106            set env(http_proxy) $sysConfProxies(proxy_http)
1107        }
1108    }
1109    if {![info exists env(HTTPS_PROXY)] || $proxy_override_env eq {yes}} {
1110        if {[info exists proxy_https]} {
1111            set env(HTTPS_PROXY) $proxy_https
1112        } elseif {[info exists sysConfProxies(proxy_https)]} {
1113            set env(HTTPS_PROXY) $sysConfProxies(proxy_https)
1114        }
1115    }
1116    if {![info exists env(FTP_PROXY)] || $proxy_override_env eq {yes}} {
1117        if {[info exists proxy_ftp]} {
1118            set env(FTP_PROXY) $proxy_ftp
1119        } elseif {[info exists sysConfProxies(proxy_ftp)]} {
1120            set env(FTP_PROXY) $sysConfProxies(proxy_ftp)
1121        }
1122    }
1123    if {![info exists env(RSYNC_PROXY)] || $proxy_override_env eq {yes}} {
1124        if {[info exists proxy_rsync]} {
1125            set env(RSYNC_PROXY) $proxy_rsync
1126        }
1127    }
1128    if {![info exists env(NO_PROXY)] || $proxy_override_env eq {yes}} {
1129        if {[info exists proxy_skip]} {
1130            set env(NO_PROXY) $proxy_skip
1131        } elseif {[info exists sysConfProxies(proxy_skip)]} {
1132            set env(NO_PROXY) $sysConfProxies(proxy_skip)
1133        }
1134    }
1135
1136    # add ccache to environment
1137    set env(CCACHE_DIR) $macports::ccache_dir
1138
1139    # load cached ping times
1140    if {[catch {
1141        set pingfile [open ${macports::portdbpath}/pingtimes r]
1142        array set macports::ping_cache [gets $pingfile]
1143        close $pingfile
1144    }]} {array set macports::ping_cache {}}
1145    # set up arrays of blacklisted and preferred hosts
1146    if {[info exists macports::host_blacklist]} {
1147        foreach host $macports::host_blacklist {
1148            set macports::host_blacklisted($host) 1
1149        }
1150    }
1151    if {[info exists macports::preferred_hosts]} {
1152        foreach host $macports::preferred_hosts {
1153            set macports::host_preferred($host) 1
1154        }
1155    }
1156
1157    # load the quick index
1158    _mports_load_quickindex
1159
1160    if {![info exists macports::ui_options(ports_no_old_index_warning)]} {
1161        set default_source_url [lindex $sources_default 0]
1162        if {[macports::getprotocol $default_source_url] eq {file} || [macports::getprotocol $default_source_url] eq {rsync}} {
1163            set default_portindex [macports::getindex $default_source_url]
1164            if {[file exists $default_portindex] && [clock seconds] - [file mtime $default_portindex] > 1209600} {
1165                ui_warn "port definitions are more than two weeks old, consider updating them by running 'port selfupdate'."
1166            }
1167        }
1168    }
1169
1170    # init registry
1171    set db_path [file join ${registry.path} registry registry.db]
1172    set db_exists [file exists $db_path]
1173    registry::open $db_path
1174    # for the benefit of the portimage code that is called from multiple interpreters
1175    global registry_open
1176    set registry_open yes
1177    # convert any flat receipts if we just created a new db
1178    if {$db_exists == 0 && [file exists ${registry.path}/receipts] && [file writable $db_path]} {
1179        ui_warn "Converting your registry to sqlite format, this might take a while..."
1180        if {[catch {registry::convert_to_sqlite}]} {
1181            ui_debug $::errorInfo
1182            file delete -force $db_path
1183            error "Failed to convert your registry to sqlite!"
1184        } else {
1185            ui_warn "Successfully converted your registry to sqlite!"
1186        }
1187    }
1188}
1189
1190# call this just before you exit
1191proc mportshutdown {} {
1192    # save ping times
1193    global macports::ping_cache macports::portdbpath
1194    if {[file writable $macports::portdbpath]} {
1195        catch {
1196            foreach host [array names ping_cache] {
1197                # don't save expired entries
1198                if {[clock seconds] - [lindex $ping_cache($host) 1] < 86400} {
1199                    lappend pinglist_fresh $host $ping_cache($host)
1200                }
1201            }
1202            set pingfile [open ${macports::portdbpath}/pingtimes w]
1203            puts $pingfile $pinglist_fresh
1204            close $pingfile
1205        }
1206    }
1207    # close it down so the cleanup stuff is called, e.g. vacuuming the db
1208    registry::close
1209}
1210
1211# link plist for xcode 4.3's benefit
1212proc macports::copy_xcode_plist {target_homedir} {
1213    global macports::user_home macports::macportsuser
1214    set user_plist "${user_home}/Library/Preferences/com.apple.dt.Xcode.plist"
1215    set target_dir "${target_homedir}/Library/Preferences"
1216    file delete -force "${target_dir}/com.apple.dt.Xcode.plist"
1217    if {[file isfile $user_plist]} {
1218        if {![file isdirectory $target_dir]} {
1219            if {[catch {file mkdir $target_dir} result]} {
1220                ui_warn "Failed to create Library/Preferences in ${target_homedir}: $result"
1221                return
1222            }
1223        }
1224        if {[file writable $target_dir] && [catch {
1225            ui_debug "Copying $user_plist to $target_dir"
1226            file copy -force $user_plist $target_dir
1227            file attributes ${target_dir}/com.apple.dt.Xcode.plist -owner $macportsuser -permissions 0644
1228        } result]} {
1229            ui_warn "Failed to copy com.apple.dt.Xcode.plist to ${target_dir}: $result"
1230        }
1231    }
1232}
1233
1234proc macports::worker_init {workername portpath porturl portbuildpath options variations} {
1235    global macports::portinterp_options macports::portinterp_deferred_options
1236
1237    # Hide any Tcl commands that should be inaccessible to port1.0 and Portfiles
1238    # exit: It should not be possible to exit the interpreter
1239    interp hide $workername exit
1240
1241    # cd: This is necessary for some code in port1.0, but should be hidden
1242    interp eval $workername "rename cd _cd"
1243
1244    # Tell the sub interpreter about all the Tcl packages we already
1245    # know about so it won't glob for packages.
1246    foreach pkgName [package names] {
1247        foreach pkgVers [package versions $pkgName] {
1248            set pkgLoadScript [package ifneeded $pkgName $pkgVers]
1249            $workername eval "package ifneeded $pkgName $pkgVers {$pkgLoadScript}"
1250        }
1251    }
1252
1253    # Create package require abstraction procedure
1254    $workername eval "proc PortSystem \{version\} \{ \n\
1255            package require port \$version \}"
1256
1257    # Clearly separate slave interpreters and the master interpreter.
1258    $workername alias mport_exec mportexec
1259    $workername alias mport_open mportopen
1260    $workername alias mport_close mportclose
1261    $workername alias mport_lookup mportlookup
1262    $workername alias mport_info mportinfo
1263    $workername alias set_phase set_phase
1264
1265    # instantiate the UI call-backs
1266    foreach priority $macports::ui_priorities {
1267        $workername alias ui_$priority ui_$priority
1268        foreach phase $macports::port_phases {
1269            $workername alias ui_${priority}_$phase ui_${priority}_$phase
1270        }
1271    }
1272    # add the UI progress call-back
1273    if {[info exists macports::ui_options(progress_download)]} {
1274        $workername alias ui_progress_download $macports::ui_options(progress_download)
1275    }
1276
1277    # notifications callback
1278    if {[info exists macports::ui_options(notifications_append)]} {
1279        $workername alias ui_notifications_append $macports::ui_options(notifications_append)
1280    } else {
1281        # provide a no-op if notifications_append wasn't set. See http://wiki.tcl.tk/3044
1282        $workername alias ui_notifications_append return -level 0
1283    }
1284
1285    $workername alias ui_prefix ui_prefix
1286    $workername alias ui_channels ui_channels
1287
1288    $workername alias ui_warn_once ui_warn_once
1289
1290    # Export some utility functions defined here.
1291    $workername alias macports_create_thread macports::create_thread
1292    $workername alias getportworkpath_from_buildpath macports::getportworkpath_from_buildpath
1293    $workername alias getportresourcepath macports::getportresourcepath
1294    $workername alias getportlogpath macports::getportlogpath
1295    $workername alias getdefaultportresourcepath macports::getdefaultportresourcepath
1296    $workername alias getprotocol macports::getprotocol
1297    $workername alias getportdir macports::getportdir
1298    $workername alias findBinary macports::findBinary
1299    $workername alias binaryInPath macports::binaryInPath
1300    $workername alias sysctl sysctl
1301    $workername alias realpath realpath
1302    $workername alias _mportsearchpath _mportsearchpath
1303    $workername alias _portnameactive _portnameactive
1304
1305    # New Registry/Receipts stuff
1306    $workername alias registry_new registry::new_entry
1307    $workername alias registry_open registry::open_entry
1308    $workername alias registry_write registry::write_entry
1309    $workername alias registry_prop_store registry::property_store
1310    $workername alias registry_prop_retr registry::property_retrieve
1311    $workername alias registry_exists registry::entry_exists
1312    $workername alias registry_exists_for_name registry::entry_exists_for_name
1313    $workername alias registry_activate portimage::activate
1314    $workername alias registry_deactivate portimage::deactivate
1315    $workername alias registry_deactivate_composite portimage::deactivate_composite
1316    $workername alias registry_uninstall registry_uninstall::uninstall
1317    $workername alias registry_register_deps registry::register_dependencies
1318    $workername alias registry_fileinfo_for_index registry::fileinfo_for_index
1319    $workername alias registry_fileinfo_for_file registry::fileinfo_for_file
1320    $workername alias registry_bulk_register_files registry::register_bulk_files
1321    $workername alias registry_active registry::active
1322    $workername alias registry_file_registered registry::file_registered
1323    $workername alias registry_port_registered registry::port_registered
1324    $workername alias registry_list_depends registry::list_depends
1325
1326    # deferred options processing.
1327    $workername alias getoption macports::getoption
1328
1329    # ping cache
1330    $workername alias get_pingtime macports::get_pingtime
1331    $workername alias set_pingtime macports::set_pingtime
1332
1333    # archive_sites.conf handling
1334    $workername alias get_archive_sites_conf_values macports::get_archive_sites_conf_values
1335
1336    foreach opt $portinterp_options {
1337        if {![info exists $opt]} {
1338            global macports::$opt
1339        }
1340        if {[info exists $opt]} {
1341            $workername eval set system_options($opt) \{[set $opt]\}
1342            $workername eval set $opt \{[set $opt]\}
1343        }
1344    }
1345
1346    foreach opt $portinterp_deferred_options {
1347        global macports::$opt
1348        # define the trace hook.
1349        $workername eval \
1350            "proc trace_$opt {name1 name2 op} { \n\
1351                trace remove variable ::$opt read ::trace_$opt \n\
1352                global $opt \n\
1353                set $opt \[getoption $opt\] \n\
1354            }"
1355        # next access will actually define the variable.
1356        $workername eval "trace add variable ::$opt read ::trace_$opt"
1357        # define some value now
1358        $workername eval set $opt ?
1359    }
1360
1361    foreach {opt val} $options {
1362        $workername eval set user_options($opt) $val
1363        $workername eval set $opt $val
1364    }
1365
1366    foreach {var val} $variations {
1367        $workername eval set variations($var) $val
1368    }
1369}
1370
1371# Create a thread with most configuration options set.
1372# The newly created thread is sent portinterp_options vars and knows where to
1373# find all packages we know.
1374proc macports::create_thread {} {
1375    package require Thread
1376
1377    global macports::portinterp_options
1378
1379    # Create the thread.
1380    set result [thread::create -preserved {thread::wait}]
1381
1382    # Tell the thread about all the Tcl packages we already
1383    # know about so it won't glob for packages.
1384    foreach pkgName [package names] {
1385        foreach pkgVers [package versions $pkgName] {
1386            set pkgLoadScript [package ifneeded $pkgName $pkgVers]
1387            thread::send -async $result "package ifneeded $pkgName $pkgVers {$pkgLoadScript}"
1388        }
1389    }
1390
1391    # inherit configuration variables.
1392    thread::send -async $result "namespace eval macports {}"
1393    foreach opt $portinterp_options {
1394        if {![info exists $opt]} {
1395            global macports::$opt
1396        }
1397        if {[info exists $opt]} {
1398            thread::send -async $result "global macports::$opt"
1399            set val [set macports::$opt]
1400            thread::send -async $result "set macports::$opt \"$val\""
1401        }
1402    }
1403
1404    return $result
1405}
1406
1407proc macports::get_tar_flags {suffix} {
1408    switch -- $suffix {
1409        .tbz -
1410        .tbz2 {
1411            return -j
1412        }
1413        .tgz {
1414            return -z
1415        }
1416        .txz {
1417            return "--use-compress-program [findBinary xz {}] -"
1418        }
1419        .tlz {
1420            return "--use-compress-program [findBinary lzma {}] -"
1421        }
1422        default {
1423            return -
1424        }
1425    }
1426}
1427
1428##
1429# Extracts a Portfile from a tarball pointed to by the given \a url to a path
1430# in \c $portdbpath and returns its path.
1431#
1432# @param url URL pointing to a tarball containing either a file named \c
1433#            Portfile at the root level -- in which case the tarball is
1434#            extracted completely, --  or a file named \c +CONTENTS at the root
1435#            level (i.e., the archive is a valid MacPorts binary archive), in
1436#            which case the Portfile is extracted from the file \c +PORTFILE
1437#            and put in a separate directory.
1438# @param local one, if the URL is local, zero otherwise
1439# @return a path to a directory containing the Portfile, or an error code
1440proc macports::fetch_port {url {local 0}} {
1441    global macports::portdbpath macports::ui_prefix macports::portverbose macports::ui_options
1442
1443    set fetchdir [file join $portdbpath portdirs]
1444    file mkdir $fetchdir
1445    if {![file writable $fetchdir]} {
1446        return -code error "Port remote fetch failed: You do not have permission to write to $fetchdir"
1447    }
1448
1449    if {$local} {
1450        set fetchfile $url
1451    } else {
1452        ui_msg "$macports::ui_prefix Fetching port $url"
1453        set fetchfile [file tail $url]
1454        set progressflag {}
1455        if {$macports::portverbose eq {yes}} {
1456            set progressflag "--progress builtin"
1457        } elseif {[info exists macports::ui_options(progress_download)]} {
1458            set progressflag "--progress ${macports::ui_options(progress_download)}"
1459        }
1460        if {[catch {eval curl fetch $progressflag {$url} {[file join $fetchdir $fetchfile]}} result]} {
1461            return -code error "Port remote fetch failed: $result"
1462        }
1463    }
1464
1465    set oldpwd [pwd]
1466    cd $fetchdir
1467
1468    # check if this is a binary archive or just the port dir by checking
1469    # whether the file "+CONTENTS" exists.
1470    set tarcmd [findBinary tar $macports::autoconf::tar_path]
1471    set tarflags [get_tar_flags [file extension $fetchfile]]
1472    set qflag $macports::autoconf::tar_q
1473    set cmdline "$tarcmd ${tarflags}${qflag}xOf \"$fetchfile\" +CONTENTS"
1474    ui_debug $cmdline
1475    if {![catch {set contents [eval exec $cmdline]}]} {
1476        # the file is probably a valid binary archive
1477        set binary 1
1478        ui_debug "getting port name from binary archive"
1479        # get the portname from the contents file
1480        foreach line [split $contents \n] {
1481            if {[lindex $line 0] eq {@name}} {
1482                # actually ${name}-${version}_$revision
1483                set portname [lindex $line 1]
1484            }
1485        }
1486        ui_debug "port name is '$portname'"
1487
1488        # create a correctly-named directory and put the Portfile there
1489        file mkdir $portname
1490        cd $portname
1491    } else {
1492        # the file is not a valid binary archive, assume it's an archive just
1493        # containing Portfile and the files directory
1494        set binary 0
1495        set portname [file rootname $fetchfile]
1496    }
1497
1498    # extract the portfile (and possibly files dir if not a binary archive)
1499    ui_debug "extracting port archive to [pwd]"
1500    if {$binary} {
1501        set cmdline "$tarcmd ${tarflags}${qflag}xOf \"../$fetchfile\" +PORTFILE > Portfile"
1502    } else {
1503        set cmdline "$tarcmd ${tarflags}xf \"$fetchfile\""
1504    }
1505    ui_debug $cmdline
1506    if {[catch {eval exec $cmdline} result]} {
1507        # clean up the archive, we don't need it anymore
1508        file delete [file join $fetchdir $fetchfile]
1509
1510        cd $oldpwd
1511        return -code error "Port extract failed: $result"
1512    }
1513
1514    # clean up the archive, we don't need it anymore
1515    file delete [file join $fetchdir $fetchfile]
1516
1517    cd $oldpwd
1518    return [file join $fetchdir $portname]
1519}
1520
1521proc macports::getprotocol {url} {
1522    if {[regexp {(?x)([^:]+)://.+} $url match protocol] == 1} {
1523        return $protocol
1524    } else {
1525        return -code error "Can't parse url $url"
1526    }
1527}
1528
1529##
1530# Return the directory where the port identified by the given \a url is
1531# located. Can be called with either local paths (starting with \c file://), or
1532# local or remote URLs pointing to a tarball that will be extracted.
1533#
1534# @param url URL identifying the port to be installed
1535# @return normalized path to the port's directory, or error when called with an
1536#         unsupported protocol, or if the tarball pointed to by \a url didn't
1537#         contain a Portfile.
1538proc macports::getportdir {url} {
1539    global macports::extracted_portdirs
1540
1541    set protocol [macports::getprotocol $url]
1542    switch -- $protocol {
1543        file {
1544            set path [file normalize [string range $url [expr {[string length $protocol] + 3}] end]]
1545            if {![file isfile $path]} {
1546                # the URL points to a local directory
1547                return $path
1548            } else {
1549                # the URL points to a local tarball that (hopefully) contains a Portfile
1550                # create a local dir for the extracted port, but only once
1551                if {![info exists macports::extracted_portdirs($url)]} {
1552                    set macports::extracted_portdirs($url) [macports::fetch_port $path 1]
1553                }
1554                return $macports::extracted_portdirs($url)
1555            }
1556        }
1557        https -
1558        http -
1559        ftp {
1560            # the URL points to a remote tarball that (hopefully) contains a Portfile
1561            # create a local dir for the extracted port, but only once
1562            if {![info exists macports::extracted_portdirs($url)]} {
1563                set macports::extracted_portdirs($url) [macports::fetch_port $url 0]
1564            }
1565            return $macports::extracted_portdirs($url)
1566        }
1567        default {
1568            return -code error "Unsupported protocol $protocol"
1569        }
1570    }
1571}
1572
1573##
1574# Get the path to the _resources directory of the source
1575#
1576# If the file is not available in the current source, it will fall back to the
1577# default source. This behavior is controlled by the fallback parameter.
1578#
1579# @param url port url
1580# @param path path in _resources we are interested in
1581# @param fallback fall back to the default source tree
1582# @return path to the _resources directory or the path to the fallback
1583proc macports::getportresourcepath {url {path {}} {fallback yes}} {
1584    global macports::sources_default
1585
1586    set protocol [getprotocol $url]
1587
1588    switch -- $protocol {
1589        file {
1590            set proposedpath [file normalize [file join [getportdir $url] .. ..]]
1591        }
1592        default {
1593            set proposedpath [getsourcepath $url]
1594        }
1595    }
1596
1597    # append requested path
1598    set proposedpath [file join $proposedpath _resources $path]
1599
1600    if {$fallback eq {yes} && ![file exists $proposedpath]} {
1601        return [getdefaultportresourcepath $path]
1602    }
1603
1604    return $proposedpath
1605}
1606
1607##
1608# Get the path to the _resources directory of the default source
1609#
1610# @param path path in _resources we are interested in
1611# @return path to the _resources directory of the default source
1612proc macports::getdefaultportresourcepath {{path {}}} {
1613    global macports::sources_default
1614
1615    set default_source_url [lindex $sources_default 0]
1616    if {[getprotocol $default_source_url] eq {file}} {
1617        set proposedpath [getportdir $default_source_url]
1618    } else {
1619        set proposedpath [getsourcepath $default_source_url]
1620    }
1621
1622    # append requested path
1623    set proposedpath [file join $proposedpath _resources $path]
1624
1625    return $proposedpath
1626}
1627
1628
1629##
1630# Opens a MacPorts portfile specified by a URL. The URL can be local (starting
1631# with file://), or remote (http, https, or ftp). In the local case, the URL
1632# can point to a directory containing a Portfile, or to a tarball in the format
1633# detailed below. In the remote case, the URL must point to a tarball. The
1634# Portfile is opened with the given list of options and variations. The result
1635# of this function should be treated as an opaque handle to a MacPorts
1636# Portfile.
1637#
1638# @param porturl URL to the directory of the port to be opened. Can the path to
1639#                a local directory, or an URL (both remote and local) pointing
1640#                to a tarball that
1641#                \li either contains a \c Portfile and possible a \c files
1642#                    directory, or
1643#                \li is a MacPorts binary archive, where the Portfile is in
1644#                    a file called \c +PORTFILE.
1645# @param options an optional array (in list format) of options
1646# @param variations an optional array (ist list format) of variations, passed
1647#                   to \c eval_variants after running the Portfile
1648# @param nocache a non-empty string, if port information caching should be
1649#                avoided.
1650proc mportopen {porturl {options {}} {variations {}} {nocache {}}} {
1651    global macports::portdbpath macports::portconf macports::open_mports auto_path
1652
1653    # Look for an already-open MPort with the same URL.
1654    # if found, return the existing reference and bump the refcount.
1655    if {$nocache ne {}} {
1656        set mport {}
1657    } else {
1658        set mport [dlist_match_multi $macports::open_mports [list porturl $porturl variations $variations options $options]]
1659    }
1660    if {$mport ne {}} {
1661        # just in case more than one somehow matches
1662        set mport [lindex $mport 0]
1663        set refcnt [ditem_key $mport refcnt]
1664        incr refcnt
1665        ditem_key $mport refcnt $refcnt
1666        return $mport
1667    }
1668
1669    # Will download if remote and extract if tarball.
1670    set portpath [macports::getportdir $porturl]
1671    ui_debug "Changing to port directory: $portpath"
1672    cd $portpath
1673    if {![file isfile Portfile]} {
1674        return -code error "Could not find Portfile in $portpath"
1675    }
1676
1677    set workername [interp create]
1678
1679    set mport [ditem_create]
1680    lappend macports::open_mports $mport
1681    ditem_key $mport porturl $porturl
1682    ditem_key $mport portpath $portpath
1683    ditem_key $mport workername $workername
1684    ditem_key $mport options $options
1685    ditem_key $mport variations $variations
1686    ditem_key $mport refcnt 1
1687
1688    macports::worker_init $workername $portpath $porturl [macports::getportbuildpath $portpath] $options $variations
1689
1690    $workername eval source Portfile
1691
1692    # add the default universal variant if appropriate, and set up flags that
1693    # are conditional on whether universal is set
1694    $workername eval universal_setup
1695
1696    # evaluate the variants
1697    if {[$workername eval eval_variants variations] != 0} {
1698        mportclose $mport
1699        error "Error evaluating variants"
1700    }
1701
1702    $workername eval port::run_callbacks
1703
1704    ditem_key $mport provides [$workername eval return \$subport]
1705
1706    return $mport
1707}
1708
1709# mportopen_installed
1710# opens a portfile stored in the registry
1711proc mportopen_installed {name version revision variants options} {
1712    global macports::registry.path
1713    set regref [lindex [registry::entry imaged $name $version $revision $variants] 0]
1714    set portfile_dir [file join ${registry.path} registry portfiles ${name}-${version}_${revision} [$regref portfile]]
1715
1716    set variations {}
1717    set minusvariant [lrange [split [$regref negated_variants] -] 1 end]
1718    set plusvariant [lrange [split [$regref variants] +] 1 end]
1719    foreach v $plusvariant {
1720        lappend variations $v +
1721    }
1722    foreach v $minusvariant {
1723        lappend variations $v -
1724    }
1725    lappend options subport $name
1726
1727    # find portgroups in registry
1728    set pgdirlist [list]
1729    foreach pg [$regref groups_used] {
1730        lappend pgdirlist [file join ${registry.path} registry portgroups [$pg sha256]-[$pg size]]
1731    }
1732    if {$pgdirlist ne {}} {
1733        lappend options _portgroup_search_dirs [list $pgdirlist]
1734    }
1735
1736    return [mportopen file://${portfile_dir}/ $options $variations]
1737}
1738
1739# Traverse a directory with ports, calling a function on the path of ports
1740# (at the second depth).
1741# I.e. the structure of dir shall be:
1742# category/port/
1743# with a Portfile file in category/port/
1744#
1745# func:     function to call on every port directory (it is passed
1746#           category/port/ as its parameter)
1747# root:     the directory with all the categories directories.
1748proc mporttraverse {func {root .}} {
1749    # Save the current directory
1750    set pwd [pwd]
1751
1752    # Join the root.
1753    set pathToRoot [file join $pwd $root]
1754
1755    # Go to root because some callers expects us to be there.
1756    cd $pathToRoot
1757
1758    foreach category [lsort -increasing -unique [readdir $root]] {
1759        set pathToCategory [file join $root $category]
1760        # process the category dirs but not _resources
1761        if {[file isdirectory $pathToCategory] && [string index [file tail $pathToCategory] 0] ne {_}} {
1762            # Iterate on port directories.
1763            foreach port [lsort -increasing -unique [readdir $pathToCategory]] {
1764                set pathToPort [file join $pathToCategory $port]
1765                if {[file isdirectory $pathToPort] &&
1766                  [file exists [file join $pathToPort Portfile]]} {
1767                    # Call the function.
1768                    $func [file join $category $port]
1769
1770                    # Restore the current directory because some
1771                    # functions changes it.
1772                    cd $pathToRoot
1773                }
1774            }
1775        }
1776    }
1777
1778    # Restore the current directory.
1779    cd $pwd
1780}
1781
1782### _mportsearchpath is private; subject to change without notice
1783
1784# depregex -> regex on the filename to find.
1785# search_path -> directories to search
1786# executable -> whether we want to check that the file is executable by current
1787#               user or not.
1788proc _mportsearchpath {depregex search_path {executable 0} {return_match 0}} {
1789    set found 0
1790    foreach path $search_path {
1791        if {![file isdirectory $path]} {
1792            continue
1793        }
1794
1795        if {[catch {set filelist [readdir $path]} result]} {
1796            return -code error "$result ($path)"
1797        }
1798
1799        foreach filename $filelist {
1800            if {[regexp $depregex $filename] &&
1801              (($executable == 0) || [file executable [file join $path $filename]])} {
1802                ui_debug "Found Dependency: path: $path filename: $filename regex: $depregex"
1803                set found 1
1804                break
1805            }
1806        }
1807
1808        if {$found} {
1809            break
1810        }
1811    }
1812    if {$return_match} {
1813        if {$found} {
1814            return [file join $path $filename]
1815        } else {
1816            return {}
1817        }
1818    } else {
1819        return $found
1820    }
1821}
1822
1823
1824### _mportinstalled is private; may change without notice
1825
1826# Determine if a port is already *installed*, as in "in the registry".
1827proc _mportinstalled {mport} {
1828    # Check for the presence of the port in the registry
1829    set workername [ditem_key $mport workername]
1830    return [$workername eval registry_exists_for_name \$subport]
1831}
1832
1833# Determine if a port is active
1834proc _mportactive {mport} {
1835    set workername [ditem_key $mport workername]
1836    if {![catch {set reslist [$workername eval registry_active \$subport]}] && [llength $reslist] > 0} {
1837        set i [lindex $reslist 0]
1838        set name [lindex $i 0]
1839        set version [lindex $i 1]
1840        set revision [lindex $i 2]
1841        set variants [lindex $i 3]
1842        array set portinfo [mportinfo $mport]
1843        if {$name eq $portinfo(name) && $version == $portinfo(version)
1844            && $revision == $portinfo(revision) && $variants == $portinfo(canonical_active_variants)} {
1845            return 1
1846        }
1847    }
1848    return 0
1849}
1850
1851# Determine if the named port is active
1852proc _portnameactive {portname} {
1853    if {[catch {set reslist [registry::active $portname]}]} {
1854        return 0
1855    } else {
1856        return [expr {[llength $reslist] > 0}]
1857    }
1858}
1859
1860### _mportispresent is private; may change without notice
1861
1862# Determine if some depspec is satisfied or if the given port is installed
1863# and active.
1864# We actually start with the registry (faster?)
1865#
1866# mport     the port declaring the dep (context in which to evaluate $prefix etc)
1867# depspec   the dependency test specification (path, bin, lib, etc.)
1868proc _mportispresent {mport depspec} {
1869    set portname [lindex [split $depspec :] end]
1870    ui_debug "Searching for dependency: $portname"
1871    set res [_portnameactive $portname]
1872    if {$res != 0} {
1873        ui_debug "Found Dependency: receipt exists for $portname"
1874        return 1
1875    } else {
1876        # The receipt test failed, use one of the depspec regex mechanisms
1877        ui_debug "Didn't find receipt, going to depspec regex for: $portname"
1878        set workername [ditem_key $mport workername]
1879        set type [lindex [split $depspec :] 0]
1880        switch -- $type {
1881            lib {return [$workername eval _libtest $depspec]}
1882            bin {return [$workername eval _bintest $depspec]}
1883            path {return [$workername eval _pathtest $depspec]}
1884            port {return 0}
1885            default {return -code error "unknown depspec type: $type"}
1886        }
1887        return 0
1888    }
1889}
1890
1891### _mporterrorifconflictsinstalled is private; may change without notice
1892
1893# Determine if the port, per the conflicts option, has any conflicts
1894# with what is installed. If it does, raises an error unless force
1895# option is set.
1896#
1897# mport   the port to check for conflicts
1898proc _mporterrorifconflictsinstalled {mport} {
1899    set conflictlist {}
1900    array set portinfo [mportinfo $mport]
1901
1902    if {[info exists portinfo(conflicts)] &&
1903        [llength $portinfo(conflicts)] > 0} {
1904        ui_debug "Checking for conflicts against [_mportkey $mport subport]"
1905        foreach conflictport $portinfo(conflicts) {
1906            if {[_mportispresent $mport port:$conflictport]} {
1907                lappend conflictlist $conflictport
1908            }
1909        }
1910    } else {
1911        ui_debug "[_mportkey $mport subport] has no conflicts"
1912    }
1913
1914    if {[llength $conflictlist] != 0} {
1915        if {[macports::global_option_isset ports_force]} {
1916            ui_warn "Force option set; installing $portinfo(name) despite conflicts with: $conflictlist"
1917        } else {
1918            if {![macports::ui_isset ports_debug]} {
1919                ui_msg {}
1920            }
1921            return -code error "Can't install $portinfo(name) because conflicting ports are active: $conflictlist"
1922        }
1923    }
1924}
1925
1926### _mportexec is private; may change without notice
1927
1928proc _mportexec {target mport} {
1929    set portname [_mportkey $mport subport]
1930    macports::push_log $mport
1931    # xxx: set the work path?
1932    set workername [ditem_key $mport workername]
1933    $workername eval validate_macportsuser
1934    if {![catch {$workername eval check_variants $target} result] && $result == 0 &&
1935        ![catch {$workername eval check_supported_archs} result] && $result == 0 &&
1936        ![catch {$workername eval eval_targets $target} result] && $result == 0} {
1937        # If auto-clean mode, clean-up after dependency install
1938        if {$macports::portautoclean eq {yes}} {
1939            # Make sure we are back in the port path before clean
1940            # otherwise if the current directory had been changed to
1941            # inside the port,  the next port may fail when trying to
1942            # install because [pwd] will return a "no file or directory"
1943            # error since the directory it was in is now gone.
1944            set portpath [ditem_key $mport portpath]
1945            catch {cd $portpath}
1946            $workername eval eval_targets clean
1947        }
1948        # XXX hack to avoid running out of fds due to sqlite temp files, ticket #24857
1949        interp delete $workername
1950        macports::pop_log
1951        return 0
1952    } else {
1953        # An error occurred.
1954        global ::logenabled ::debuglogname
1955        ui_debug $::errorInfo
1956        if {[info exists ::logenabled] && $::logenabled && [info exists ::debuglogname]} {
1957            ui_error "See $::debuglogname for details."
1958        }
1959        macports::pop_log
1960        return 1
1961    }
1962}
1963
1964# mportexec
1965# Execute the specified target of the given mport.
1966proc mportexec {mport target} {
1967    set workername [ditem_key $mport workername]
1968
1969    # check for existence of macportsuser and use fallback if necessary
1970    $workername eval validate_macportsuser
1971    # check variants
1972    if {[$workername eval check_variants $target] != 0} {
1973        return 1
1974    }
1975    set portname [_mportkey $mport subport]
1976    if {$target ne {clean}} {
1977        macports::push_log $mport
1978    }
1979
1980    # Use _target_needs_deps as a proxy for whether we're going to
1981    # build and will therefore need to check Xcode version and
1982    # supported_archs.
1983    if {[macports::_target_needs_deps $target]} {
1984        # possibly warn or error out depending on how old xcode is
1985        if {[$workername eval _check_xcode_version] != 0} {
1986            return 1
1987        }
1988        # error out if selected arch(s) not supported by this port
1989        if {[$workername eval check_supported_archs] != 0} {
1990            return 1
1991        }
1992    }
1993
1994    # Before we build the port, we must build its dependencies.
1995    set dlist {}
1996    if {[macports::_target_needs_deps $target] && [macports::_mport_has_deptypes $mport [macports::_deptypes_for_target $target $workername]]} {
1997        registry::exclusive_lock
1998        # see if we actually need to build this port
1999        if {($target ne {activate} && $target ne {install}) ||
2000            ![$workername eval registry_exists \$subport \$version \$revision \$portvariants]} {
2001
2002            # upgrade dependencies that are already installed
2003            if {![macports::global_option_isset ports_nodeps]} {
2004                macports::_upgrade_mport_deps $mport $target
2005            }
2006        }
2007
2008        ui_msg -nonewline "$macports::ui_prefix Computing dependencies for [_mportkey $mport subport]"
2009        if {[macports::ui_isset ports_debug]} {
2010            # play nice with debug messages
2011            ui_msg {}
2012        }
2013        if {[mportdepends $mport $target] != 0} {
2014            return 1
2015        }
2016        if {![macports::ui_isset ports_debug]} {
2017            ui_msg {}
2018        }
2019
2020        # Select out the dependents along the critical path,
2021        # but exclude this mport, we might not be installing it.
2022        set dlist [dlist_append_dependents $macports::open_mports $mport {}]
2023
2024        dlist_delete dlist $mport
2025
2026        # print the dep list
2027        if {[llength $dlist] > 0} {
2028            set depstring "$macports::ui_prefix Dependencies to be installed:"
2029            foreach ditem $dlist {
2030                append depstring " [ditem_key $ditem provides]"
2031            }
2032            ui_msg $depstring
2033        }
2034
2035        # install them
2036        set result [dlist_eval $dlist _mportactive [list _mportexec activate]]
2037
2038        registry::exclusive_unlock
2039
2040        if {$result ne {}} {
2041            ##
2042            # When this happens, the failing port usually already printed an
2043            # error message. Omit this one to avoid cluttering the output and
2044            # hiding the *real* problem.
2045
2046            #set errstring "The following dependencies were not installed:"
2047            #foreach ditem $result {
2048            #    append errstring " [ditem_key $ditem provides]"
2049            #}
2050            #ui_error $errstring
2051            foreach ditem $dlist {
2052                catch {mportclose $ditem}
2053            }
2054            return 1
2055        }
2056
2057        # Close the dependencies, we're done installing them.
2058        foreach ditem $dlist {
2059            mportclose $ditem
2060        }
2061    } else {
2062        # No dependencies, but we still need to check for conflicts.
2063        if {$target eq {} || $target eq {install} || $target eq {activate}} {
2064            _mporterrorifconflictsinstalled $mport
2065        }
2066    }
2067
2068    set clean 0
2069    if {$macports::portautoclean eq {yes} && ($target eq {install} || $target eq {activate})} {
2070        # If we're doing an install, check if we should clean after
2071        set clean 1
2072    }
2073
2074    # Build this port with the specified target
2075    set result [$workername eval eval_targets $target]
2076
2077    # If auto-clean mode and successful install, clean-up after install
2078    if {$result == 0 && $clean == 1} {
2079        # Make sure we are back in the port path, just in case
2080        set portpath [ditem_key $mport portpath]
2081        catch {cd $portpath}
2082        $workername eval eval_targets clean
2083    }
2084
2085    global ::logenabled ::debuglogname
2086    if {[info exists ::logenabled] && $::logenabled && [info exists ::debuglogname]} {
2087        if {$result != 0} {
2088            ui_error "See $::debuglogname for details."
2089        }
2090        macports::pop_log
2091    }
2092
2093    return $result
2094}
2095
2096# upgrade any dependencies of mport that are installed and needed for target
2097proc macports::_upgrade_mport_deps {mport target} {
2098    set options [ditem_key $mport options]
2099    set workername [ditem_key $mport workername]
2100    set deptypes [macports::_deptypes_for_target $target $workername]
2101    array set portinfo [mportinfo $mport]
2102    array set depscache {}
2103
2104    set required_archs [$workername eval get_canonical_archs]
2105    set depends_skip_archcheck [_mportkey $mport depends_skip_archcheck]
2106
2107    set test _portnameactive
2108
2109    foreach deptype $deptypes {
2110        if {![info exists portinfo($deptype)]} {
2111            continue
2112        }
2113        foreach depspec $portinfo($deptype) {
2114            set dep_portname [$workername eval _get_dep_port $depspec]
2115            if {$dep_portname ne {} && ![info exists depscache(port:$dep_portname)] && [$test $dep_portname]} {
2116                set variants {}
2117
2118                # check that the dep has the required archs
2119                set active_archs [_get_registry_archs $dep_portname]
2120                if {$deptype ne {depends_fetch} && $deptype ne {depends_extract}
2121                    && $active_archs ne {} && $active_archs ne {noarch} && $required_archs ne {noarch}
2122                    && [lsearch -exact $depends_skip_archcheck $dep_portname] == -1} {
2123                    set missing {}
2124                    foreach arch $required_archs {
2125                        if {[lsearch -exact $active_archs $arch] == -1} {
2126                            lappend missing $arch
2127                        }
2128                    }
2129                    if {[llength $missing] > 0} {
2130                        set res [mportlookup $dep_portname]
2131                        array unset dep_portinfo
2132                        array set dep_portinfo [lindex $res 1]
2133                        if {[info exists dep_portinfo(installs_libs)] && !$dep_portinfo(installs_libs)} {
2134                            set missing {}
2135                        }
2136                    }
2137                    if {[llength $missing] > 0} {
2138                        if {[info exists dep_portinfo(variants)] && [lsearch -exact $dep_portinfo(variants) universal] != -1} {
2139                            # dep offers a universal variant
2140                            if {[llength $active_archs] == 1} {
2141                                # not installed universal
2142                                set missing {}
2143                                foreach arch $required_archs {
2144                                    if {[lsearch -exact $macports::universal_archs $arch] == -1} {
2145                                        lappend missing $arch
2146                                    }
2147                                }
2148                                if {[llength $missing] > 0} {
2149                                    ui_error "Cannot install [_mportkey $mport subport] for the arch(s) '$required_archs' because"
2150                                    ui_error "its dependency $dep_portname is only installed for the arch '$active_archs'"
2151                                    ui_error "and the configured universal_archs '$macports::universal_archs' are not sufficient."
2152                                    return -code error "architecture mismatch"
2153                                } else {
2154                                    # upgrade the dep with +universal
2155                                    lappend variants universal +
2156                                    lappend options ports_upgrade_enforce-variants yes
2157                                    ui_debug "enforcing +universal upgrade for $dep_portname"
2158                                }
2159                            } else {
2160                                # already universal
2161                                ui_error "Cannot install [_mportkey $mport subport] for the arch(s) '$required_archs' because"
2162                                ui_error "its dependency $dep_portname is only installed for the archs '$active_archs'."
2163                                return -code error "architecture mismatch"
2164                            }
2165                        } else {
2166                            ui_error "Cannot install [_mportkey $mport subport] for the arch(s) '$required_archs' because"
2167                            ui_error "its dependency $dep_portname is only installed for the arch '$active_archs'"
2168                            ui_error "and does not have a universal variant."
2169                            return -code error "architecture mismatch"
2170                        }
2171                    }
2172                }
2173
2174                set status [macports::upgrade $dep_portname port:$dep_portname $variants $options depscache]
2175                # status 2 means the port was not found in the index
2176                if {$status != 0 && $status != 2 && ![macports::ui_isset ports_processall]} {
2177                    return -code error "upgrade $dep_portname failed"
2178                }
2179            }
2180        }
2181    }
2182}
2183
2184# get the archs with which the active version of portname is installed
2185proc macports::_get_registry_archs {portname} {
2186    set ilist [registry::active $portname]
2187    set i [lindex $ilist 0]
2188    set regref [registry::open_entry [lindex $i 0] [lindex $i 1] [lindex $i 2] [lindex $i 3] [lindex $i 5]]
2189    set archs [registry::property_retrieve $regref archs]
2190    if {$archs == 0} {
2191        set archs {}
2192    }
2193    return $archs
2194}
2195
2196proc macports::getsourcepath {url} {
2197    global macports::portdbpath
2198
2199    set source_path [split $url ://]
2200
2201    if {[_source_is_snapshot $url]} {
2202        # daily snapshot tarball
2203        return [file join $portdbpath sources [join [lrange $source_path 3 end-1] /] ports]
2204    }
2205
2206    return [file join $portdbpath sources [lindex $source_path 3] [lindex $source_path 4] [lindex $source_path 5]]
2207}
2208
2209##
2210# Checks whether a supplied source URL is for a daily snapshot tarball
2211# (private)
2212#
2213# @param url source URL to check
2214# @return a list containing filename and extension or an empty list
2215proc _source_is_snapshot {url {filename {}} {extension {}}} {
2216    upvar $filename myfilename
2217    upvar $extension myextension
2218
2219    if {[regexp {^(?:https?|ftp|rsync)://.+/(.+\.(tar\.gz|tar\.bz2|tar))$} $url -> f e]} {
2220        set myfilename $f
2221        set myextension $e
2222
2223        return 1
2224    }
2225
2226    return 0
2227}
2228
2229proc macports::getportbuildpath {id {portname {}}} {
2230    global macports::portdbpath
2231    regsub {://} $id {.} port_path
2232    regsub -all {/} $port_path {_} port_path
2233    return [file join $portdbpath build $port_path $portname]
2234}
2235
2236proc macports::getportlogpath {id {portname {}}} {
2237    global macports::portdbpath
2238    regsub {://} $id {.} port_path
2239    regsub -all {/} $port_path {_} port_path
2240    return [file join $portdbpath logs $port_path $portname]
2241}
2242
2243proc macports::getportworkpath_from_buildpath {portbuildpath} {
2244    return [file normalize [file join $portbuildpath work]]
2245}
2246
2247proc macports::getportworkpath_from_portdir {portpath {portname {}}} {
2248    return [macports::getportworkpath_from_buildpath [macports::getportbuildpath $portpath $portname]]
2249}
2250
2251proc macports::getindex {source} {
2252    # Special case file:// sources
2253    if {[macports::getprotocol $source] eq {file}} {
2254        return [file join [macports::getportdir $source] PortIndex]
2255    }
2256
2257    return [file join [macports::getsourcepath $source] PortIndex]
2258}
2259
2260proc mportsync {{optionslist {}}} {
2261    global macports::sources macports::portdbpath macports::rsync_options \
2262           tcl_platform macports::portverbose macports::autoconf::rsync_path \
2263           macports::autoconf::tar_path macports::autoconf::openssl_path \
2264           macports::ui_options
2265    array set options $optionslist
2266    if {[info exists options(no_reindex)]} {
2267        upvar $options(needed_portindex_var) any_needed_portindex
2268    }
2269
2270    set numfailed 0
2271
2272    ui_msg "$macports::ui_prefix Updating the ports tree"
2273    foreach source $sources {
2274        set flags [lrange $source 1 end]
2275        set source [lindex $source 0]
2276        if {[lsearch -exact $flags nosync] != -1} {
2277            ui_debug "Skipping $source"
2278            continue
2279        }
2280        set needs_portindex 0
2281        ui_info "Synchronizing local ports tree from $source"
2282        switch -regexp -- [macports::getprotocol $source] {
2283            {^file$} {
2284                set portdir [macports::getportdir $source]
2285                set svn_cmd {}
2286                catch {set svn_cmd [macports::findBinary svn]}
2287                set git_cmd {}
2288                catch {set git_cmd [macports::findBinary git]}
2289                if {$svn_cmd ne {} && ([file exists ${portdir}/.svn] || ![catch {exec $svn_cmd info $portdir > /dev/null 2>@1}])} {
2290                    set svn_commandline "$svn_cmd update --non-interactive $portdir"
2291                    ui_debug $svn_commandline
2292                    if {
2293                        [catch {
2294                            if {[getuid] == 0} {
2295                                set euid [geteuid]
2296                                set egid [getegid]
2297                                ui_debug "changing euid/egid - current euid: $euid - current egid: $egid"
2298                                setegid [name_to_gid [file attributes $portdir -group]]
2299                                seteuid [name_to_uid [file attributes $portdir -owner]]
2300                            }
2301                            system $svn_commandline
2302                            if {[getuid] == 0} {
2303                                seteuid $euid
2304                                setegid $egid
2305                            }
2306                        }]
2307                    } {
2308                        ui_debug $::errorInfo
2309                        ui_error "Synchronization of the local ports tree failed doing an svn update"
2310                        incr numfailed
2311                        continue
2312                    }
2313                } elseif {$git_cmd ne {} && ![catch {exec sh -c "cd ${portdir} && $git_cmd rev-parse --is-inside-work-tree"} result]} {
2314                    # determine what type of git repository this is
2315                    if {![catch {exec sh -c "cd ${portdir} && $git_cmd config --local --get svn-remote.svn.url"} result]} {
2316                        set git_action "svn rebase"
2317                    } else {
2318                        set git_action "pull --rebase"
2319                    }
2320                    set git_commandline "pushd $portdir ; $git_cmd $git_action ; popd"
2321                    ui_debug $git_commandline
2322                    if {
2323                        [catch {
2324                            if {[getuid] == 0} {
2325                                set euid [geteuid]
2326                                set egid [getegid]
2327                                ui_debug "changing euid/egid - current euid: $euid - current egid: $egid"
2328                                setegid [name_to_gid [file attributes $portdir -group]]
2329                                seteuid [name_to_uid [file attributes $portdir -owner]]
2330                            }
2331                            system $git_commandline
2332                            if {[getuid] == 0} {
2333                                seteuid $euid
2334                                setegid $egid
2335                            }
2336                        }]
2337                    } {
2338                        ui_debug $::errorInfo
2339                        ui_error "Synchronization of the local ports tree failed doing a git update"
2340                        incr numfailed
2341                        continue
2342                    }
2343                }
2344                set needs_portindex 1
2345            }
2346            {^rsync$} {
2347                # Where to, boss?
2348                set indexfile [macports::getindex $source]
2349                set destdir [file dirname $indexfile]
2350                set is_tarball [_source_is_snapshot $source]
2351                file mkdir $destdir
2352
2353                if {$is_tarball} {
2354                    set exclude_option {}
2355                    # need to do a few things before replacing the ports tree in this case
2356                    set destdir [file dirname $destdir]
2357                } else {
2358                    # Keep rsync happy with a trailing slash
2359                    if {[string index $source end] ne {/}} {
2360                        append source /
2361                    }
2362                    # don't sync PortIndex yet; we grab the platform specific one afterwards
2363                    set exclude_option '--exclude=/PortIndex*'
2364                }
2365                # Do rsync fetch
2366                set rsync_commandline "$macports::autoconf::rsync_path $rsync_options $exclude_option $source $destdir"
2367                ui_debug $rsync_commandline
2368                if {[catch {system $rsync_commandline}]} {
2369                    ui_error "Synchronization of the local ports tree failed doing rsync"
2370                    incr numfailed
2371                    continue
2372                }
2373
2374                if {$is_tarball} {
2375                    # verify signature for tarball
2376                    global macports::archivefetch_pubkeys
2377                    set rsync_commandline "$macports::autoconf::rsync_path $rsync_options $exclude_option ${source}.rmd160 $destdir"
2378                    ui_debug $rsync_commandline
2379                    if {[catch {system $rsync_commandline}]} {
2380                        ui_error "Synchronization of the ports tree signature failed doing rsync"
2381                        incr numfailed
2382                        continue
2383                    }
2384                    set tarball ${destdir}/[file tail $source]
2385                    set signature ${tarball}.rmd160
2386                    set openssl [macports::findBinary openssl $macports::autoconf::openssl_path]
2387                    set verified 0
2388                    foreach pubkey $macports::archivefetch_pubkeys {
2389                        if {![catch {exec $openssl dgst -ripemd160 -verify $pubkey -signature $signature $tarball} result]} {
2390                            set verified 1
2391                            ui_debug "successful verification with key $pubkey"
2392                            break
2393                        } else {
2394                            ui_debug "failed verification with key $pubkey"
2395                            ui_debug "openssl output: $result"
2396                        }
2397                    }
2398                    if {!$verified} {
2399                        ui_error "Failed to verify signature for ports tree!"
2400                        incr numfailed
2401                        continue
2402                    }
2403
2404                    # extract tarball and move into place
2405                    set tar [macports::findBinary tar $macports::autoconf::tar_path]
2406                    file mkdir ${destdir}/tmp
2407                    set tar_cmd "$tar -C ${destdir}/tmp -xf $tarball"
2408                    ui_debug $tar_cmd
2409                    if {[catch {system $tar_cmd}]} {
2410                        ui_error "Failed to extract ports tree from tarball!"
2411                        incr numfailed
2412                        continue
2413                    }
2414                    # save the local PortIndex data
2415                    if {[file isfile $indexfile]} {
2416                        file copy -force $indexfile ${destdir}/
2417                        file rename -force $indexfile ${destdir}/tmp/ports/
2418                        if {[file isfile ${indexfile}.quick]} {
2419                            file rename -force ${indexfile}.quick ${destdir}/tmp/ports/
2420                        }
2421                    }
2422                    file delete -force ${destdir}/ports
2423                    file rename ${destdir}/tmp/ports ${destdir}/ports
2424                    file delete -force ${destdir}/tmp
2425                }
2426
2427                set needs_portindex 1
2428                # now sync the index if the local file is missing or older than a day
2429                if {![file isfile $indexfile] || [clock seconds] - [file mtime $indexfile] > 86400
2430                      || [info exists options(no_reindex)]} {
2431                    if {$is_tarball} {
2432                        # chop ports.tar off the end
2433                        set index_source [string range $source 0 end-[string length [file tail $source]]]
2434                    } else {
2435                        set index_source $source
2436                    }
2437                    set remote_indexfile "${index_source}PortIndex_${macports::os_platform}_${macports::os_major}_${macports::os_arch}/PortIndex"
2438                    set rsync_commandline "$macports::autoconf::rsync_path $rsync_options $remote_indexfile $destdir"
2439                    ui_debug $rsync_commandline
2440                    if {[catch {system $rsync_commandline}]} {
2441                        ui_debug "Synchronization of the PortIndex failed doing rsync"
2442                    } else {
2443                        set ok 1
2444                        set needs_portindex 0
2445                        if {$is_tarball} {
2446                            set ok 0
2447                            set needs_portindex 1
2448                            # verify signature for PortIndex
2449                            set rsync_commandline "$macports::autoconf::rsync_path $rsync_options ${remote_indexfile}.rmd160 $destdir"
2450                            ui_debug $rsync_commandline
2451                            if {![catch {system $rsync_commandline}]} {
2452                                foreach pubkey $macports::archivefetch_pubkeys {
2453                                    if {![catch {exec $openssl dgst -ripemd160 -verify $pubkey -signature ${destdir}/PortIndex.rmd160 ${destdir}/PortIndex} result]} {
2454                                        set ok 1
2455                                        set needs_portindex 0
2456                                        ui_debug "successful verification with key $pubkey"
2457                                        break
2458                                    } else {
2459                                        ui_debug "failed verification with key $pubkey"
2460                                        ui_debug "openssl output: $result"
2461                                    }
2462                                }
2463                                if {$ok} {
2464                                    # move PortIndex into place
2465                                    file rename -force ${destdir}/PortIndex ${destdir}/ports/
2466                                }
2467                            }
2468                        }
2469                        if {$ok} {
2470                            mports_generate_quickindex $indexfile
2471                        }
2472                    }
2473                }
2474                if {[catch {system "chmod -R a+r \"$destdir\""}]} {
2475                    ui_warn "Setting world read permissions on parts of the ports tree failed, need root?"
2476                }
2477            }
2478            {^https?$|^ftp$} {
2479                if {![_source_is_snapshot $source filename extension]} {
2480                    ui_error "Synchronization using http, https and ftp only supported with tarballs."
2481                    ui_error "The source ${source} doesn't seem to point to a tarball."
2482                    ui_error "Please switch to a different sync protocol (e.g. rsync) in your sources.conf"
2483                    ui_error "Remove the line mentioned above from your sources.conf to silence this error."
2484                    incr numfailed
2485                    continue
2486                }
2487                # sync a daily port snapshot tarball
2488                set indexfile [macports::getindex $source]
2489                set destdir [file dirname $indexfile]
2490                set tarpath [file join [file normalize [file join $destdir ..]] $filename]
2491
2492                set updated 1
2493                if {[file isdirectory $destdir]} {
2494                    set moddate [file mtime $destdir]
2495                    if {[catch {set updated [curl isnewer $source $moddate]} error]} {
2496                        ui_warn "Cannot check if $source was updated, ($error)"
2497                    }
2498                }
2499
2500                if {(![info exists options(ports_force)] || $options(ports_force) ne {yes}) && $updated <= 0} {
2501                    ui_info "No updates for $source"
2502                    continue
2503                }
2504
2505                file mkdir $destdir
2506
2507                set progressflag {}
2508                if {$macports::portverbose eq {yes}} {
2509                    set progressflag "--progress builtin"
2510                } elseif {[info exists macports::ui_options(progress_download)]} {
2511                    set progressflag "--progress ${macports::ui_options(progress_download)}"
2512                }
2513
2514                if {[catch {eval curl fetch $progressflag {$source} {$tarpath}} error]} {
2515                    ui_error "Fetching $source failed ($error)"
2516                    incr numfailed
2517                    continue
2518                }
2519
2520                set extflag {}
2521                switch -- $extension {
2522                    {tar.gz} {
2523                        set extflag -z
2524                    }
2525                    {tar.bz2} {
2526                        set extflag -j
2527                    }
2528                }
2529
2530                set tar [macports::findBinary tar $macports::autoconf::tar_path]
2531                if {[catch {system "cd ${destdir}/.. && $tar $verboseflag $extflag -xf $filename"} error]} {
2532                    ui_error "Extracting $source failed ($error)"
2533                    incr numfailed
2534                    continue
2535                }
2536
2537                if {[catch {system "chmod -R a+r \"$destdir\""}]} {
2538                    ui_warn "Setting world read permissions on parts of the ports tree failed, need root?"
2539                }
2540
2541                set platindex "PortIndex_${macports::os_platform}_${macports::os_major}_${macports::os_arch}/PortIndex"
2542                if {[file isfile ${destdir}/$platindex] && [file isfile ${destdir}/${platindex}.quick]} {
2543                    file rename -force ${destdir}/$platindex ${destdir}/${platindex}.quick $destdir
2544                }
2545
2546                file delete $tarpath
2547            }
2548            {^mports$} {
2549                ui_error "Synchronization using the mports protocol no longer supported."
2550                ui_error "Please switch to a different sync protocol (e.g. rsync) in your sources.conf"
2551                ui_error "Remove the line starting with mports:// from your sources.conf to silence this error."
2552                incr numfailed
2553                continue
2554            }
2555            default {
2556                ui_warn "Unknown synchronization protocol for $source"
2557            }
2558        }
2559
2560        if {$needs_portindex} {
2561            set any_needed_portindex 1
2562            if {![info exists options(no_reindex)]} {
2563                global macports::prefix
2564                set indexdir [file dirname [macports::getindex $source]]
2565                if {[catch {system "${macports::prefix}/bin/portindex $indexdir"}]} {
2566                    ui_error "updating PortIndex for $source failed"
2567                }
2568            }
2569        }
2570    }
2571
2572    # refresh the quick index if necessary (batch or interactive run)
2573    if {[info exists macports::ui_options(ports_commandfiles)]} {
2574        _mports_load_quickindex
2575    }
2576
2577    if {$numfailed > 0} {
2578        return -code error "Synchronization of $numfailed source(s) failed"
2579    }
2580}
2581
2582##
2583# Searches all configured port sources for a given pattern in a given field
2584# using a given matching style and optional case-sensitivity.
2585#
2586# @param pattern pattern to search for; will be interpreted according to the \a
2587#                matchstyle parameter
2588# @param case_sensitive "yes", if a case-sensitive search should be performed,
2589#                       "no" otherwise. Defaults to "yes".
2590# @param matchstyle One of the values \c exact, \c glob and \c regexp, where \c
2591#                   exact performs a standard string comparison, \c glob
2592#                   performs Tcl string matching using <tt>[string match]</tt>
2593#                   and \c regexp interprets \a pattern as a regular
2594#                   expression.
2595# @param field name of the field to apply \a pattern to. Must be one of the
2596#              fields available in the used portindex. The portindex currently
2597#              contains
2598#                \li \c name (the default)
2599#                \li \c homepage
2600#                \li \c description
2601#                \li \c long_description
2602#                \li \c license
2603#                \li \c categories
2604#                \li \c platforms
2605#                \li \c maintainers
2606#                \li \c variants
2607#                \li \c portdir
2608#                \li all \c depends_* values
2609#                \li \c epoch
2610#                \li \c version
2611#                \li \c revision
2612#                \li \c replaced_by
2613#                \li \c installs_libs
2614# @return a list where each even index (starting with 0) contains the name of
2615#         a matching port. Each entry at an odd index is followed by its
2616#         corresponding line from the portindex, which can be passed to
2617#         <tt>array set</tt>. The whole return value can also be passed to
2618#         <tt>array set</tt> to create an associate array where the port names
2619#         are the keys and the lines from portindex are the values.
2620proc mportsearch {pattern {case_sensitive yes} {matchstyle regexp} {field name}} {
2621    global macports::sources
2622    set matches [list]
2623    set easy [expr {$field eq {name}}]
2624
2625    set found 0
2626    foreach source $sources {
2627        set source [lindex $source 0]
2628        set protocol [macports::getprotocol $source]
2629        if {[catch {set fd [open [macports::getindex $source] r]} result]} {
2630            ui_warn "Can't open index file for source: $source"
2631        } else {
2632            try {
2633                incr found 1
2634                while {[gets $fd line] >= 0} {
2635                    array unset portinfo
2636                    set name [lindex $line 0]
2637                    set len  [lindex $line 1]
2638                    set line [read $fd $len]
2639
2640                    if {$easy} {
2641                        set target $name
2642                    } else {
2643                        array set portinfo $line
2644                        if {![info exists portinfo($field)]} {
2645                            continue
2646                        }
2647                        set target $portinfo($field)
2648                    }
2649
2650                    switch -- $matchstyle {
2651                        exact {
2652                            if {$case_sensitive eq yes} {
2653                                set compres [string compare $pattern $target]
2654                            } else {
2655                                set compres [string compare -nocase $pattern $target]
2656                            }
2657                            set matchres [expr {0 == $compres}]
2658                        }
2659                        glob {
2660                            if {$case_sensitive eq yes} {
2661                                set matchres [string match $pattern $target]
2662                            } else {
2663                                set matchres [string match -nocase $pattern $target]
2664                            }
2665                        }
2666                        regexp {
2667                            if {$case_sensitive eq yes} {
2668                                set matchres [regexp -- $pattern $target]
2669                            } else {
2670                                set matchres [regexp -nocase -- $pattern $target]
2671                            }
2672                        }
2673                        default {
2674                            return -code error "mportsearch: Unsupported matching style: ${matchstyle}."
2675                        }
2676                    }
2677
2678                    if {$matchres == 1} {
2679                        if {$easy} {
2680                            array set portinfo $line
2681                        }
2682                        switch -- $protocol {
2683                            rsync {
2684                                # Rsync files are local
2685                                set source_url file://[macports::getsourcepath $source]
2686                            }
2687                            https -
2688                            http -
2689                            ftp {
2690                                # daily snapshot tarball
2691                                set source_url file://[macports::getsourcepath $source]
2692                            }
2693                            default {
2694                                set source_url $source
2695                            }
2696                        }
2697                        if {[info exists portinfo(portdir)]} {
2698                            set porturl ${source_url}/$portinfo(portdir)
2699                            lappend line porturl $porturl
2700                            ui_debug "Found port in $porturl"
2701                        } else {
2702                            ui_debug "Found port info: $line"
2703                        }
2704                        lappend matches $name
2705                        lappend matches $line
2706                    }
2707                }
2708            } catch * {
2709                ui_warn "It looks like your PortIndex file for $source may be corrupt."
2710                throw
2711            } finally {
2712                close $fd
2713            }
2714        }
2715    }
2716    if {!$found} {
2717        return -code error "No index(es) found! Have you synced your port definitions? Try running 'port selfupdate'."
2718    }
2719
2720    return $matches
2721}
2722
2723##
2724# Returns the PortInfo for a single named port. The info comes from the
2725# PortIndex, and name matching is case-insensitive. Unlike mportsearch, only
2726# the first match is returned, but the return format is otherwise identical.
2727# The advantage is that mportlookup is usually much faster than mportsearch,
2728# due to the use of the quick index, which is a name-based index into the
2729# PortIndex.
2730#
2731# @param name name of the port to look up. Returns the first match while
2732#             traversing the sources in-order.
2733# @return associative array in list form where the first field is the port name
2734#         and the second field is the line from PortIndex containing the port
2735#         info. See the return value of mportsearch().
2736# @see mportsearch()
2737proc mportlookup {name} {
2738    global macports::portdbpath macports::sources macports::quick_index
2739
2740    set sourceno 0
2741    set matches [list]
2742    foreach source $sources {
2743        set source [lindex $source 0]
2744        set protocol [macports::getprotocol $source]
2745        if {![info exists quick_index(${sourceno},[string tolower $name])]} {
2746            # no entry in this source, advance to next source
2747            incr sourceno 1
2748            continue
2749        }
2750        # The quick index is keyed on the port name, and provides the offset in
2751        # the main PortIndex where the given port's PortInfo line can be found.
2752        set offset $quick_index(${sourceno},[string tolower $name])
2753        incr sourceno 1
2754        if {[catch {set fd [open [macports::getindex $source] r]} result]} {
2755            ui_warn "Can't open index file for source: $source"
2756        } else {
2757            try {
2758                seek $fd $offset
2759                gets $fd line
2760                set name [lindex $line 0]
2761                set len  [lindex $line 1]
2762                set line [read $fd $len]
2763
2764                array set portinfo $line
2765
2766                switch -- $protocol {
2767                    rsync {
2768                        set source_url file://[macports::getsourcepath $source]
2769                    }
2770                    https -
2771                    http -
2772                    ftp {
2773                        set source_url file://[macports::getsourcepath $source]
2774                    }
2775                    default {
2776                        set source_url $source
2777                    }
2778                }
2779                if {[info exists portinfo(portdir)]} {
2780                    lappend line porturl ${source_url}/$portinfo(portdir)
2781                }
2782                lappend matches $name
2783                lappend matches $line
2784                close $fd
2785                set fd -1
2786            } catch * {
2787                ui_warn "It looks like your PortIndex file for $source may be corrupt."
2788            } finally {
2789                if {$fd != -1} {
2790                    close $fd
2791                }
2792            }
2793            if {[llength $matches] > 0} {
2794                # if we have a match, exit. If we don't, continue with the next
2795                # source.
2796                break
2797            }
2798        }
2799    }
2800
2801    return $matches
2802}
2803
2804##
2805# Returns all ports in the indices. Faster than 'mportsearch .*' because of the
2806# lack of matching.
2807#
2808# @return associative array in list form where the first field is the port name
2809#         and the second field is the line from PortIndex containing the port
2810#         info. See the return value of mportsearch().
2811# @see mportsearch()
2812proc mportlistall {} {
2813    global macports::sources
2814    set matches [list]
2815
2816    set found 0
2817    foreach source $sources {
2818        set source [lindex $source 0]
2819        set protocol [macports::getprotocol $source]
2820        if {![catch {set fd [open [macports::getindex $source] r]} result]} {
2821            try {
2822                incr found 1
2823                while {[gets $fd line] >= 0} {
2824                    array unset portinfo
2825                    set name [lindex $line 0]
2826                    set len  [lindex $line 1]
2827                    set line [read $fd $len]
2828
2829                    array set portinfo $line
2830
2831                    switch -- $protocol {
2832                        rsync {
2833                            set source_url file://[macports::getsourcepath $source]
2834                        }
2835                        https -
2836                        http -
2837                        ftp {
2838                            set source_url file://[macports::getsourcepath $source]
2839                        }
2840                        default {
2841                            set source_url $source
2842                        }
2843                    }
2844                    if {[info exists portinfo(portdir)]} {
2845                        lappend line porturl ${source_url}/$portinfo(portdir)
2846                    }
2847                    lappend matches $name $line
2848                }
2849            } catch * {
2850                ui_warn "It looks like your PortIndex file for $source may be corrupt."
2851                throw
2852            } finally {
2853                close $fd
2854            }
2855        } else {
2856            ui_warn "Can't open index file for source: $source"
2857        }
2858    }
2859    if {!$found} {
2860        return -code error "No index(es) found! Have you synced your port definitions? Try running 'port selfupdate'."
2861    }
2862
2863    return $matches
2864}
2865
2866##
2867# Loads PortIndex.quick from each source into the quick_index, generating it
2868# first if necessary. Private API of macports1.0, do not use this from outside
2869# macports1.0.
2870proc _mports_load_quickindex {} {
2871    global macports::sources macports::quick_index
2872
2873    unset -nocomplain macports::quick_index
2874
2875    set sourceno 0
2876    foreach source $sources {
2877        unset -nocomplain quicklist
2878        # chop off any tags
2879        set source [lindex $source 0]
2880        set index [macports::getindex $source]
2881        if {![file exists $index]} {
2882            incr sourceno
2883            continue
2884        }
2885        if {![file exists ${index}.quick]} {
2886            ui_warn "No quick index file found, attempting to generate one for source: $source"
2887            if {[catch {set quicklist [mports_generate_quickindex $index]}]} {
2888                incr sourceno
2889                continue
2890            }
2891        }
2892        # only need to read the quick index file if we didn't just update it
2893        if {![info exists quicklist]} {
2894            if {[catch {set fd [open ${index}.quick r]} result]} {
2895                ui_warn "Can't open quick index file for source: $source"
2896                incr sourceno
2897                continue
2898            } else {
2899                set quicklist [read $fd]
2900                close $fd
2901            }
2902        }
2903        foreach entry [split $quicklist \n] {
2904            set quick_index(${sourceno},[lindex $entry 0]) [lindex $entry 1]
2905        }
2906        incr sourceno 1
2907    }
2908    if {!$sourceno} {
2909        ui_warn "No index(es) found! Have you synced your port definitions? Try running 'port selfupdate'."
2910    }
2911}
2912
2913##
2914# Generates a PortIndex.quick file from a PortIndex by using the name field as
2915# key. This allows fast indexing into the PortIndex when using the port name as
2916# key.
2917#
2918# @param index the PortIndex file to create the index for. The resulting quick
2919#              index will be in a file named like \a index, but with ".quick"
2920#              appended.
2921# @return a list of entries written to the quick index file in the same format
2922#         if the file would just have been written.
2923# @throws if the given \a index cannot be opened, the output file cannot be
2924#         opened, an error occurs while using the PortIndex (e.g., because it
2925#         is corrupt), or the quick index generation failed for some other
2926#         reason.
2927proc mports_generate_quickindex {index} {
2928    if {[catch {set indexfd [open $index r]} result] || [catch {set quickfd [open ${index}.quick w]} result]} {
2929        ui_warn "Can't open index file: $index"
2930        return -code error
2931    } else {
2932        try {
2933            set offset [tell $indexfd]
2934            set quicklist {}
2935            while {[gets $indexfd line] >= 0} {
2936                if {[llength $line] != 2} {
2937                    continue
2938                }
2939                set name [lindex $line 0]
2940                append quicklist "[string tolower $name] $offset\n"
2941
2942                set len [lindex $line 1]
2943                read $indexfd $len
2944                set offset [tell $indexfd]
2945            }
2946            puts -nonewline $quickfd $quicklist
2947        } catch * {
2948            ui_warn "It looks like your PortIndex file $index may be corrupt."
2949            throw
2950        } finally {
2951            close $indexfd
2952            close $quickfd
2953        }
2954    }
2955    if {[info exists quicklist]} {
2956        return $quicklist
2957    } else {
2958        ui_warn "Failed to generate quick index for: $index"
2959        return -code error
2960    }
2961}
2962
2963proc mportinfo {mport} {
2964    set workername [ditem_key $mport workername]
2965    return [$workername eval array get ::PortInfo]
2966}
2967
2968proc mportclose {mport} {
2969    global macports::open_mports macports::extracted_portdirs
2970    set refcnt [ditem_key $mport refcnt]
2971    incr refcnt -1
2972    ditem_key $mport refcnt $refcnt
2973    if {$refcnt == 0} {
2974        dlist_delete macports::open_mports $mport
2975        set workername [ditem_key $mport workername]
2976        # the hack in _mportexec might have already deleted the worker
2977        if {[interp exists $workername]} {
2978            interp delete $workername
2979        }
2980        set porturl [ditem_key $mport porturl]
2981        if {[info exists macports::extracted_portdirs($porturl)]} {
2982            # TODO port.tcl calls mportopen multiple times on the same port to
2983            # determine a number of attributes and will close the port after
2984            # each call. $macports::extracted_portdirs($porturl) will however
2985            # stay set, which means it will not be extracted twice. We could
2986            # (1) unset $macports::extracted_portdirs($porturl), which would
2987            # lead to downloading the port multiple times, or (2) fix the
2988            # port.tcl code to delay mportclose until the end.
2989            #ui_debug "Removing temporary port directory $macports::extracted_portdirs($porturl)"
2990            #file delete -force $macports::extracted_portdirs($porturl)
2991        }
2992        ditem_delete $mport
2993    }
2994}
2995
2996##### Private Depspec API #####
2997# This API should be considered work in progress and subject to change without notice.
2998##### "
2999
3000# _mportkey
3001# - returns a variable from the port's interpreter
3002
3003proc _mportkey {mport key} {
3004    set workername [ditem_key $mport workername]
3005    return [$workername eval "return \$$key"]
3006}
3007
3008# mportdepends builds the list of mports which the given port depends on.
3009# This list is added to $mport.
3010# This list actually depends on the target.
3011# This method can optionally recurse through the dependencies, looking for
3012#   dependencies of dependencies.
3013# This method can optionally cut the search when ports are already installed or
3014#   the dependencies are satisfied.
3015#
3016# mport -> mport item
3017# target -> target to consider the dependency for
3018# recurseDeps -> if the search should be recursive
3019# skipSatisfied -> cut the search tree when encountering installed/satisfied
3020#                  dependencies ports.
3021# accDeps -> accumulator for recursive calls
3022# return 0 if everything was ok, an non zero integer otherwise.
3023proc mportdepends {mport {target {}} {recurseDeps 1} {skipSatisfied 1} {accDeps 0}} {
3024
3025    array set portinfo [mportinfo $mport]
3026    if {$accDeps} {
3027        upvar port_seen port_seen
3028    } else {
3029        array set port_seen {}
3030    }
3031
3032    # progress indicator
3033    if {![macports::ui_isset ports_debug]} {
3034        ui_info -nonewline .
3035        flush stdout
3036    }
3037
3038    if {$target eq {} || $target eq {install} || $target eq {activate}} {
3039        _mporterrorifconflictsinstalled $mport
3040    }
3041
3042    set workername [ditem_key $mport workername]
3043    set deptypes [macports::_deptypes_for_target $target $workername]
3044
3045    set depPorts {}
3046    if {[llength $deptypes] > 0} {
3047        array set optionsarray [ditem_key $mport options]
3048        # avoid propagating requested flag from parent
3049        unset -nocomplain optionsarray(ports_requested)
3050        # subport will be different for deps
3051        unset -nocomplain optionsarray(subport)
3052        set options [array get optionsarray]
3053        set variations [ditem_key $mport variations]
3054        set required_archs [$workername eval get_canonical_archs]
3055        set depends_skip_archcheck [_mportkey $mport depends_skip_archcheck]
3056    }
3057
3058    # Process the dependencies for each of the deptypes
3059    foreach deptype $deptypes {
3060        if {![info exists portinfo($deptype)]} {
3061            continue
3062        }
3063        foreach depspec $portinfo($deptype) {
3064            # get the portname that satisfies the depspec
3065            set dep_portname [$workername eval _get_dep_port $depspec]
3066            # skip port/archs combos we've already seen, and ones with the same port but less archs than ones we've seen (or noarch)
3067            set seenkey ${dep_portname},[join $required_archs ,]
3068            set seen 0
3069            if {[info exists port_seen($seenkey)]} {
3070                set seen 1
3071            } else {
3072                set prev_seenkeys [array names port_seen ${dep_portname},*]
3073                set nrequired [llength $required_archs]
3074                foreach key $prev_seenkeys {
3075                    set key_archs [lrange [split $key ,] 1 end]
3076                    if {$key_archs eq {noarch} || $required_archs eq {noarch} || [llength $key_archs] > $nrequired} {
3077                        set seen 1
3078                        set seenkey $key
3079                        break
3080                    }
3081                }
3082            }
3083            if {$seen} {
3084                if {$port_seen($seenkey) != 0} {
3085                    # nonzero means the dep is not satisfied, so we have to record it
3086                    ditem_append_unique $mport requires $port_seen($seenkey)
3087                }
3088                continue
3089            }
3090
3091            # Is that dependency satisfied or this port installed?
3092            # If we don't skip or if it is not, add it to the list.
3093            set present [_mportispresent $mport $depspec]
3094
3095            if {!$skipSatisfied && $dep_portname eq {}} {
3096                set dep_portname [lindex [split $depspec :] end]
3097            }
3098
3099            set check_archs 0
3100            if {$dep_portname ne {} && $deptype ne {depends_fetch} && $deptype ne {depends_extract} && [lsearch -exact $depends_skip_archcheck $dep_portname] == -1} {
3101                set check_archs 1
3102            }
3103
3104            # need to open the portfile even if the dep is installed if it doesn't have the right archs
3105            set parse 0
3106            if {!$skipSatisfied || !$present || ($check_archs && ![macports::_active_supports_archs $dep_portname $required_archs])} {
3107                set parse 1
3108            }
3109            if {$parse} {
3110                # Find the porturl
3111                if {[catch {set res [mportlookup $dep_portname]} error]} {
3112                    global errorInfo
3113                    ui_msg {}
3114                    ui_debug $errorInfo
3115                    ui_error "Internal error: port lookup failed: $error"
3116                    return 1
3117                }
3118
3119                array unset dep_portinfo
3120                array set dep_portinfo [lindex $res 1]
3121                if {![info exists dep_portinfo(porturl)]} {
3122                    if {![macports::ui_isset ports_debug]} {
3123                        ui_msg {}
3124                    }
3125                    ui_error "Dependency '$dep_portname' not found."
3126                    return 1
3127                } elseif {[info exists dep_portinfo(installs_libs)] && !$dep_portinfo(installs_libs)} {
3128                    set check_archs 0
3129                    if {$skipSatisfied && $present} {
3130                        set parse 0
3131                    }
3132                }
3133
3134                if {$parse} {
3135                    set dep_options $options
3136                    lappend dep_options subport $dep_portinfo(name)
3137                    # Figure out the depport. Check the open_mports list first, since
3138                    # we potentially leak mport references if we mportopen each time,
3139                    # because mportexec only closes each open mport once.
3140                    set depport [dlist_match_multi $macports::open_mports [list porturl $dep_portinfo(porturl) options $dep_options]]
3141
3142                    if {$depport eq {}} {
3143                        # We haven't opened this one yet.
3144                        set depport [mportopen $dep_portinfo(porturl) $dep_options $variations]
3145                    }
3146                }
3147            }
3148
3149            # check archs
3150            if {$parse && $check_archs
3151                && ![macports::_mport_supports_archs $depport $required_archs]} {
3152
3153                set supported_archs [_mportkey $depport supported_archs]
3154                array unset variation_array
3155                array set variation_array [[ditem_key $depport workername] eval "array get variations"]
3156                mportclose $depport
3157                set arch_mismatch 1
3158                set has_universal 0
3159                if {[info exists dep_portinfo(variants)] && [lsearch -exact $dep_portinfo(variants) universal] != -1} {
3160                    # a universal variant is offered
3161                    set has_universal 1
3162                    if {![info exists variation_array(universal)] || $variation_array(universal) ne {+}} {
3163                        set variation_array(universal) +
3164                        # try again with +universal
3165                        set depport [mportopen $dep_portinfo(porturl) $dep_options [array get variation_array]]
3166                        if {[macports::_mport_supports_archs $depport $required_archs]} {
3167                            set arch_mismatch 0
3168                        }
3169                    }
3170                }
3171                if {$arch_mismatch} {
3172                    macports::_explain_arch_mismatch [_mportkey $mport subport] $dep_portname $required_archs $supported_archs $has_universal
3173                    return -code error "architecture mismatch"
3174                }
3175            }
3176
3177            if {$parse} {
3178                if {$recurseDeps} {
3179                    # Add to the list we need to recurse on.
3180                    lappend depPorts $depport
3181                }
3182
3183                # Append the sub-port's provides to the port's requirements list.
3184                set depport_provides [ditem_key $depport provides]
3185                ditem_append_unique $mport requires $depport_provides
3186                # record actual archs we ended up getting
3187                set port_seen(${dep_portname},[join [macports::_mport_archs $depport] ,]) $depport_provides
3188            } elseif {$present && $dep_portname ne {}} {
3189                # record actual installed archs
3190                set port_seen(${dep_portname},[join [macports::_active_archs $dep_portname] ,]) 0
3191            }
3192        }
3193    }
3194
3195    # Loop on the depports.
3196    if {$recurseDeps} {
3197        # Dep ports should be installed (all dependencies must be satisfied).
3198        foreach depport $depPorts {
3199            # Any of these may have been closed by a previous recursive call
3200            # and replaced by a universal version. This is fine, just skip.
3201            if {[ditem_key $depport] ne {}} {
3202                set res [mportdepends $depport {} $recurseDeps $skipSatisfied 1]
3203                if {$res != 0} {
3204                    return $res
3205                }
3206            }
3207        }
3208    }
3209
3210    return 0
3211}
3212
3213# check if the given mport can support dependents with the given archs
3214proc macports::_mport_supports_archs {mport required_archs} {
3215    if {$required_archs eq {noarch}} {
3216        return 1
3217    }
3218    set provided_archs [_mport_archs $mport]
3219    if {$provided_archs eq {noarch}} {
3220        return 1
3221    }
3222    foreach arch $required_archs {
3223        if {[lsearch -exact $provided_archs $arch] == -1} {
3224            return 0
3225        }
3226    }
3227    return 1
3228}
3229
3230# return the archs of the given mport
3231proc macports::_mport_archs {mport} {
3232    set workername [ditem_key $mport workername]
3233    return [$workername eval get_canonical_archs]
3234}
3235
3236# check if the active version of a port supports the given archs
3237proc macports::_active_supports_archs {portname required_archs} {
3238    if {$required_archs eq {noarch}} {
3239        return 1
3240    }
3241    if {[catch {registry::active $portname}]} {
3242        return 0
3243    }
3244    set provided_archs [_active_archs $portname]
3245    if {$provided_archs eq {noarch} || $provided_archs eq {} || $provided_archs == 0} {
3246        return 1
3247    }
3248    foreach arch $required_archs {
3249        if {[lsearch -exact $provided_archs $arch] == -1} {
3250            return 0
3251        }
3252    }
3253    return 1
3254}
3255
3256# get the archs for a given active port
3257proc macports::_active_archs {portname} {
3258    if {[catch {set ilist [registry::active $portname]}]} {
3259        return {}
3260    }
3261    set i [lindex $ilist 0]
3262    set regref [registry::open_entry $portname [lindex $i 1] [lindex $i 2] [lindex $i 3] [lindex $i 5]]
3263    return [registry::property_retrieve $regref archs]
3264}
3265
3266# print an error message explaining why a port's archs are not provided by a dependency
3267proc macports::_explain_arch_mismatch {port dep required_archs supported_archs has_universal} {
3268    global macports::universal_archs
3269    if {![macports::ui_isset ports_debug]} {
3270        ui_msg {}
3271    }
3272    ui_error "Cannot install $port for the arch(s) '$required_archs' because"
3273    if {$supported_archs ne {}} {
3274        foreach arch $required_archs {
3275            if {[lsearch -exact $supported_archs $arch] == -1} {
3276                ui_error "its dependency $dep only supports the arch(s) '$supported_archs'."
3277                return
3278            }
3279        }
3280    }
3281    if {$has_universal} {
3282        foreach arch $required_archs {
3283            if {[lsearch -exact $universal_archs $arch] == -1} {
3284                ui_error "its dependency $dep does not build for the required arch(s) by default"
3285                ui_error "and the configured universal_archs '$universal_archs' are not sufficient."
3286                return
3287            }
3288        }
3289        ui_error "its dependency $dep cannot build for the required arch(s)."
3290        return
3291    }
3292    ui_error "its dependency $dep does not build for the required arch(s) by default"
3293    ui_error "and does not have a universal variant."
3294}
3295
3296# check if the given mport has any dependencies of the given types
3297proc macports::_mport_has_deptypes {mport deptypes} {
3298    array set portinfo [mportinfo $mport]
3299    foreach type $deptypes {
3300        if {[info exists portinfo($type)] && $portinfo($type) ne {}} {
3301            return 1
3302        }
3303    }
3304    return 0
3305}
3306
3307# check if the given target needs dependencies installed first
3308proc macports::_target_needs_deps {target} {
3309    # XXX: need a better way than checking this hardcoded list
3310    switch -- $target {
3311        fetch -
3312        checksum -
3313        extract -
3314        patch -
3315        configure -
3316        build -
3317        test -
3318        destroot -
3319        install -
3320        activate -
3321        dmg -
3322        mdmg -
3323        pkg -
3324        mpkg -
3325        rpm -
3326        dpkg -
3327        srpm {return 1}
3328        default {return 0}
3329    }
3330}
3331
3332# Determine dependency types required for target
3333proc macports::_deptypes_for_target {target workername} {
3334    switch -- $target {
3335        fetch       -
3336        checksum    {return depends_fetch}
3337        extract     -
3338        patch       {return "depends_fetch depends_extract"}
3339        configure   -
3340        build       {return "depends_fetch depends_extract depends_build depends_lib"}
3341        test        -
3342        srpm        -
3343        destroot    {return "depends_fetch depends_extract depends_build depends_lib depends_run"}
3344        dmg         -
3345        pkg         -
3346        mdmg        -
3347        mpkg        -
3348        rpm         -
3349        dpkg        {
3350            if {[global_option_isset ports_binary_only] ||
3351                (![global_option_isset ports_source_only] && [$workername eval _archive_available])} {
3352                return "depends_lib depends_run"
3353            } else {
3354                return "depends_fetch depends_extract depends_build depends_lib depends_run"
3355            }
3356        }
3357        install     -
3358        activate    -
3359        {}          {
3360            if {[global_option_isset ports_binary_only] ||
3361                [$workername eval registry_exists \$subport \$version \$revision \$portvariants]
3362                || (![global_option_isset ports_source_only] && [$workername eval _archive_available])} {
3363                return "depends_lib depends_run"
3364            } else {
3365                return "depends_fetch depends_extract depends_build depends_lib depends_run"
3366            }
3367        }
3368    }
3369    return {}
3370}
3371
3372# selfupdate procedure
3373proc macports::selfupdate {{optionslist {}} {updatestatusvar {}}} {
3374    global macports::prefix macports::portdbpath macports::rsync_server macports::rsync_dir \
3375           macports::rsync_options macports::autoconf::macports_version \
3376           macports::autoconf::rsync_path tcl_platform macports::autoconf::openssl_path \
3377           macports::autoconf::tar_path
3378    array set options $optionslist
3379
3380    # variable that indicates whether we actually updated base
3381    if {$updatestatusvar ne {}} {
3382        upvar $updatestatusvar updatestatus
3383        set updatestatus no
3384    }
3385
3386    # are we syncing a tarball? (implies detached signature)
3387    set is_tarball 0
3388    if {[string range $rsync_dir end-3 end] eq {.tar}} {
3389        set is_tarball 1
3390        set mp_source_path [file join $portdbpath sources $rsync_server [file dirname $rsync_dir]]
3391    } else {
3392        if {[string index $rsync_dir end] ne {/}} {
3393            append rsync_dir /
3394        }
3395        set mp_source_path [file join $portdbpath sources $rsync_server $rsync_dir]
3396    }
3397    # create the path to the to be downloaded sources if it doesn't exist
3398    if {![file exists $mp_source_path]} {
3399        file mkdir $mp_source_path
3400    }
3401    ui_debug "MacPorts sources location: $mp_source_path"
3402
3403    # sync the MacPorts sources
3404    ui_msg "$macports::ui_prefix Updating MacPorts base sources using rsync"
3405    if {[catch {system "$rsync_path $rsync_options rsync://${rsync_server}/$rsync_dir $mp_source_path"} result]} {
3406       return -code error "Error synchronizing MacPorts sources: $result"
3407    }
3408
3409    if {$is_tarball} {
3410        # verify signature for tarball
3411        global macports::archivefetch_pubkeys
3412        if {[catch {system "$rsync_path $rsync_options rsync://${rsync_server}/${rsync_dir}.rmd160 $mp_source_path"} result]} {
3413            return -code error "Error synchronizing MacPorts source signature: $result"
3414        }
3415        set openssl [findBinary openssl $macports::autoconf::openssl_path]
3416        set tarball ${mp_source_path}/[file tail $rsync_dir]
3417        set signature ${tarball}.rmd160
3418        set verified 0
3419        foreach pubkey $macports::archivefetch_pubkeys {
3420            if {![catch {exec $openssl dgst -ripemd160 -verify $pubkey -signature $signature $tarball} result]} {
3421                set verified 1
3422                ui_debug "successful verification with key $pubkey"
3423                break
3424            } else {
3425                ui_debug "failed verification with key $pubkey"
3426                ui_debug "openssl output: $result"
3427            }
3428        }
3429        if {!$verified} {
3430            return -code error "Failed to verify signature for MacPorts source!"
3431        }
3432
3433        # extract tarball and move into place
3434        set tar [macports::findBinary tar $macports::autoconf::tar_path]
3435        file mkdir ${mp_source_path}/tmp
3436        set tar_cmd "$tar -C ${mp_source_path}/tmp -xf $tarball"
3437        ui_debug $tar_cmd
3438        if {[catch {system $tar_cmd}]} {
3439            return -code error "Failed to extract MacPorts sources from tarball!"
3440        }
3441        file delete -force ${mp_source_path}/base
3442        file rename ${mp_source_path}/tmp/base ${mp_source_path}/base
3443        file delete -force ${mp_source_path}/tmp
3444        # set the final extracted source path
3445        set mp_source_path ${mp_source_path}/base
3446    }
3447
3448    # echo current MacPorts version
3449    ui_msg "MacPorts base version $macports::autoconf::macports_version installed,"
3450
3451    if {[info exists options(ports_force)] && $options(ports_force) eq {yes}} {
3452        set use_the_force_luke yes
3453        ui_debug "Forcing a rebuild and reinstallation of MacPorts"
3454    } else {
3455        set use_the_force_luke no
3456        ui_debug "Rebuilding and reinstalling MacPorts if needed"
3457    }
3458
3459    # Choose what version file to use: old, floating point format or new, real version number format
3460    set version_file [file join $mp_source_path config macports_version]
3461    if {[file exists $version_file]} {
3462        set fd [open $version_file r]
3463        gets $fd macports_version_new
3464        close $fd
3465        # echo downloaded MacPorts version
3466        ui_msg "MacPorts base version $macports_version_new downloaded."
3467    } else {
3468        ui_warn "No version file found, please rerun selfupdate."
3469        set macports_version_new 0
3470    }
3471
3472    # check if we we need to rebuild base
3473    set comp [vercmp $macports_version_new $macports::autoconf::macports_version]
3474
3475    # syncing ports tree.
3476    if {![info exists options(ports_selfupdate_nosync)] || $options(ports_selfupdate_nosync) ne {yes}} {
3477        if {$comp > 0} {
3478            # updated portfiles potentially need new base to parse - tell sync to try to
3479            # use prefabricated PortIndex files and signal if it couldn't
3480            lappend optionslist no_reindex 1 needed_portindex_var needed_portindex
3481        }
3482        if {[catch {mportsync $optionslist} result]} {
3483            return -code error "Couldn't sync the ports tree: $result"
3484        }
3485    }
3486
3487    if {$use_the_force_luke eq {yes} || $comp > 0} {
3488        if {[info exists options(ports_dryrun)] && $options(ports_dryrun) eq {yes}} {
3489            ui_msg "$macports::ui_prefix MacPorts base is outdated, selfupdate would install $macports_version_new (dry run)"
3490        } else {
3491            ui_msg "$macports::ui_prefix MacPorts base is outdated, installing new version $macports_version_new"
3492
3493            # get installation user/group and permissions
3494            set owner [file attributes $prefix -owner]
3495            set group [file attributes $prefix -group]
3496            set perms [string range [file attributes $prefix -permissions] end-3 end]
3497            if {$tcl_platform(user) ne {root} && $tcl_platform(user) ne $owner} {
3498                return -code error "User $tcl_platform(user) does not own $prefix - try using sudo"
3499            }
3500            ui_debug "Permissions OK"
3501
3502            set configure_args "--prefix=$prefix --with-install-user=$owner --with-install-group=$group --with-directory-mode=$perms"
3503            # too many users have an incompatible readline in /usr/local, see ticket #10651
3504            if {$tcl_platform(os) ne {Darwin} || $prefix eq {/usr/local}
3505                || ([glob -nocomplain /usr/local/lib/lib{readline,history}*] eq {} && [glob -nocomplain /usr/local/include/readline/*.h] eq {})} {
3506                append configure_args " --enable-readline"
3507            } else {
3508                ui_warn "Disabling readline support due to readline in /usr/local"
3509            }
3510
3511            if {$prefix eq {/usr/local} || $prefix eq {/usr}} {
3512                append configure_args " --with-unsupported-prefix"
3513            }
3514
3515            # Choose a sane compiler
3516            set cc_arg {}
3517            if {$::macports::os_platform eq {darwin}} {
3518                set cc_arg "CC=/usr/bin/cc OBJC=/usr/bin/cc "
3519            }
3520
3521            # do the actual configure, build and installation of new base
3522            ui_msg "Installing new MacPorts release in $prefix as ${owner}:${group}; permissions ${perms}\n"
3523            if {[catch {system "cd $mp_source_path && ${cc_arg}./configure $configure_args && make SELFUPDATING=1 && make install SELFUPDATING=1"} result]} {
3524                return -code error "Error installing new MacPorts base: $result"
3525            }
3526            if {[info exists updatestatus]} {
3527                set updatestatus yes
3528            }
3529        }
3530    } elseif {$comp < 0} {
3531        ui_msg "$macports::ui_prefix MacPorts base is probably trunk or a release candidate"
3532    } else {
3533        ui_msg "$macports::ui_prefix MacPorts base is already the latest version"
3534    }
3535
3536    # set the MacPorts sources to the right owner
3537    set sources_owner [file attributes [file join $portdbpath sources/] -owner]
3538    ui_debug "Setting MacPorts sources ownership to $sources_owner"
3539    if {[catch {exec [findBinary chown $macports::autoconf::chown_path] -R $sources_owner [file join $portdbpath sources/]} result]} {
3540        return -code error "Couldn't change permissions of the MacPorts sources at $mp_source_path to ${sources_owner}: $result"
3541    }
3542
3543    if {![info exists options(ports_selfupdate_nosync)] || $options(ports_selfupdate_nosync) ne {yes}} {
3544        if {[info exists needed_portindex]} {
3545            ui_msg "Not all sources could be fully synced using the old version of MacPorts."
3546            ui_msg "Please run selfupdate again now that MacPorts base has been updated."
3547        } else {
3548            ui_msg "\nThe ports tree has been updated. To upgrade your installed ports, you should run"
3549            ui_msg "  port upgrade outdated"
3550        }
3551    }
3552
3553    return 0
3554}
3555
3556# upgrade API wrapper procedure
3557# return codes:
3558#   0 = success
3559#   1 = general failure
3560#   2 = port name not found in index
3561#   3 = port not installed
3562proc macports::upgrade {portname dspec variationslist optionslist {depscachename {}}} {
3563    # only installed ports can be upgraded
3564    if {![registry::entry_exists_for_name $portname]} {
3565        ui_error "$portname is not installed"
3566        return 3
3567    }
3568    if {$depscachename ne {}} {
3569        upvar $depscachename depscache
3570    } else {
3571        array set depscache {}
3572    }
3573    # stop upgrade from being called via mportexec as well
3574    set orig_nodeps yes
3575    if {![info exists macports::global_options(ports_nodeps)]} {
3576        set macports::global_options(ports_nodeps) yes
3577        set orig_nodeps no
3578    }
3579
3580    # run the actual upgrade
3581    set status [macports::_upgrade $portname $dspec $variationslist $optionslist depscache]
3582
3583    if {!$orig_nodeps} {
3584        unset -nocomplain macports::global_options(ports_nodeps)
3585    }
3586
3587    return $status
3588}
3589
3590# main internal upgrade procedure
3591proc macports::_upgrade {portname dspec variationslist optionslist {depscachename {}}} {
3592    global macports::global_variations
3593    array set options $optionslist
3594
3595    if {$depscachename ne {}} {
3596        upvar $depscachename depscache
3597    }
3598
3599    # Is this a dry run?
3600    set is_dryrun no
3601    if {[info exists options(ports_dryrun)] && $options(ports_dryrun) eq {yes}} {
3602        set is_dryrun yes
3603    }
3604
3605    # Is this a rev-upgrade-called run?
3606    set is_revupgrade no
3607    if {[info exists options(ports_revupgrade)] && $options(ports_revupgrade)} {
3608        set is_revupgrade yes
3609        # unset revupgrade options so we can upgrade dependencies with the same
3610        # $options without also triggering a rebuild there, see #40150
3611        unset options(ports_revupgrade)
3612    }
3613    set is_revupgrade_second_run no
3614    if {[info exists options(ports_revupgrade_second_run)] && $options(ports_revupgrade_second_run)} {
3615        set is_revupgrade_second_run yes
3616        # unset revupgrade options so we can upgrade dependencies with the same
3617        # $options without also triggering a rebuild there, see #40150
3618        unset options(ports_revupgrade_second_run)
3619    }
3620
3621    # check if the port is in tree
3622    if {[catch {mportlookup $portname} result]} {
3623        global errorInfo
3624        ui_debug $errorInfo
3625        ui_error "port lookup failed: $result"
3626        return 1
3627    }
3628    # argh! port doesnt exist!
3629    if {$result eq {}} {
3630        ui_warn "No port $portname found in the index."
3631        return 2
3632    }
3633    # fill array with information
3634    array set portinfo [lindex $result 1]
3635    # set portname again since the one we were passed may not have had the correct case
3636    set portname $portinfo(name)
3637    set options(subport) $portname
3638
3639    set ilist {}
3640    if {[catch {set ilist [registry::installed $portname {}]} result]} {
3641        if {$result eq "Registry error: $portname not registered as installed."} {
3642            ui_debug "$portname is *not* installed by MacPorts"
3643
3644            # We need to pass _mportispresent a reference to the mport that is
3645            # actually declaring the dependency on the one we're checking for.
3646            # We got here via _upgrade_dependencies, so we grab it from 2 levels up.
3647            upvar 2 mport parentmport
3648            if {![_mportispresent $parentmport $dspec]} {
3649                # open porthandle
3650                set porturl $portinfo(porturl)
3651                if {![info exists porturl]} {
3652                    set porturl file://./
3653                }
3654                # Grab the variations from the parent
3655                upvar 2 variations variations
3656
3657                if {[catch {set mport [mportopen $porturl [array get options] [array get variations]]} result]} {
3658                    global errorInfo
3659                    ui_debug $errorInfo
3660                    ui_error "Unable to open port: $result"
3661                    return 1
3662                }
3663                # While we're at it, update the portinfo
3664                array unset portinfo
3665                array set portinfo [mportinfo $mport]
3666
3667                # upgrade its dependencies first
3668                set status [_upgrade_dependencies portinfo depscache variationslist options]
3669                if {$status != 0 && $status != 2 && ![ui_isset ports_processall]} {
3670                    catch {mportclose $mport}
3671                    return $status
3672                }
3673                # now install it
3674                if {[catch {set result [mportexec $mport activate]} result]} {
3675                    global errorInfo
3676                    ui_debug $errorInfo
3677                    ui_error "Unable to exec port: $result"
3678                    catch {mportclose $mport}
3679                    return 1
3680                }
3681                if {$result > 0} {
3682                    ui_error "Problem while installing $portname"
3683                    catch {mportclose $mport}
3684                    return $result
3685                }
3686                # we just installed it, so mark it done in the cache
3687                set depscache(port:$portname) 1
3688                mportclose $mport
3689            } else {
3690                # dependency is satisfied by something other than the named port
3691                ui_debug "$portname not installed, soft dependency satisfied"
3692                # mark this depspec as satisfied in the cache
3693                set depscache($dspec) 1
3694            }
3695            # the rest of the proc doesn't matter for a port that is freshly
3696            # installed or not installed
3697            return 0
3698        } else {
3699            ui_error "Checking installed version failed: $result"
3700            return 1
3701        }
3702    } else {
3703        # we'll now take care of upgrading it, so we can add it to the cache
3704        set depscache(port:$portname) 1
3705    }
3706
3707    # set version_in_tree and revision_in_tree
3708    if {![info exists portinfo(version)]} {
3709        ui_error "Invalid port entry for ${portname}, missing version"
3710        return 1
3711    }
3712    set version_in_tree $portinfo(version)
3713    set revision_in_tree $portinfo(revision)
3714    set epoch_in_tree $portinfo(epoch)
3715
3716    # find latest version installed and active version (if any)
3717    set anyactive no
3718    set version_installed {}
3719    foreach i $ilist {
3720        set variant [lindex $i 3]
3721        set version [lindex $i 1]
3722        set revision [lindex $i 2]
3723        set epoch [lindex $i 5]
3724        if {$version_installed eq {} || ($epoch > $epoch_installed && $version != $version_installed) ||
3725                ($epoch >= $epoch_installed && [vercmp $version $version_installed] > 0)
3726                || ($epoch >= $epoch_installed
3727                    && [vercmp $version $version_installed] == 0
3728                    && $revision > $revision_installed)} {
3729            set version_installed $version
3730            set revision_installed $revision
3731            set variant_installed $variant
3732            set epoch_installed $epoch
3733        }
3734
3735        set isactive [lindex $i 4]
3736        if {$isactive == 1} {
3737            set anyactive yes
3738            set version_active $version
3739            set revision_active $revision
3740            set variant_active $variant
3741            set epoch_active $epoch
3742        }
3743    }
3744
3745    # output version numbers
3746    ui_debug "epoch: in tree: $epoch_in_tree installed: $epoch_installed"
3747    ui_debug "$portname ${version_in_tree}_$revision_in_tree exists in the ports tree"
3748    ui_debug "$portname ${version_installed}_$revision_installed $variant_installed is the latest installed"
3749    if {$anyactive} {
3750        ui_debug "$portname ${version_active}_$revision_active $variant_active is active"
3751        # save existing variant for later use
3752        set oldvariant $variant_active
3753        set regref [registry::open_entry $portname $version_active $revision_active $variant_active $epoch_active]
3754    } else {
3755        ui_debug "no version of $portname is active"
3756        set oldvariant $variant_installed
3757        set regref [registry::open_entry $portname $version_installed $revision_installed $variant_installed $epoch_installed]
3758    }
3759    set oldnegatedvariant [registry::property_retrieve $regref negated_variants]
3760    if {$oldnegatedvariant == 0} {
3761        set oldnegatedvariant {}
3762    }
3763    set requestedflag [registry::property_retrieve $regref requested]
3764    set os_platform_installed [registry::property_retrieve $regref os_platform]
3765    set os_major_installed [registry::property_retrieve $regref os_major]
3766
3767    # Before we do
3768    # dependencies, we need to figure out the final variants,
3769    # open the port, and update the portinfo.
3770    set porturl $portinfo(porturl)
3771    if {![info exists porturl]} {
3772        set porturl file://./
3773    }
3774
3775    # Note $variationslist is left alone and so retains the original
3776    # requested variations, which should be passed to recursive calls to
3777    # upgrade; while variations gets existing variants and global variations
3778    # merged in later on, so it applies only to this port's upgrade
3779    array set variations $variationslist
3780
3781    set globalvarlist [array get macports::global_variations]
3782
3783    set minusvariant [lrange [split $oldnegatedvariant -] 1 end]
3784    set plusvariant [lrange [split $oldvariant +] 1 end]
3785    ui_debug "Merging existing variants '${oldvariant}$oldnegatedvariant' into variants"
3786    set oldvariantlist [list]
3787    foreach v $plusvariant {
3788        lappend oldvariantlist $v +
3789    }
3790    foreach v $minusvariant {
3791        lappend oldvariantlist $v -
3792    }
3793
3794    # merge in the old variants
3795    foreach {variation value} $oldvariantlist {
3796        if {![info exists variations($variation)]} {
3797            set variations($variation) $value
3798        }
3799    }
3800
3801    # Now merge in the global (i.e. variants.conf) variations.
3802    # We wait until now so that existing variants for this port
3803    # override global variations
3804    foreach {variation value} $globalvarlist {
3805        if {![info exists variations($variation)]} {
3806            set variations($variation) $value
3807        }
3808    }
3809
3810    ui_debug "new fully merged portvariants: [array get variations]"
3811
3812    # at this point we need to check if a different port will be replacing this one
3813    if {[info exists portinfo(replaced_by)] && ![info exists options(ports_upgrade_no-replace)]} {
3814        ui_msg "$macports::ui_prefix $portname is replaced by $portinfo(replaced_by)"
3815        if {[catch {mportlookup $portinfo(replaced_by)} result]} {
3816            global errorInfo
3817            ui_debug $errorInfo
3818            ui_error "port lookup failed: $result"
3819            return 1
3820        }
3821        if {$result eq {}} {
3822            ui_error "No port $portinfo(replaced_by) found."
3823            return 1
3824        }
3825        array unset portinfo
3826        array set portinfo [lindex $result 1]
3827        set newname $portinfo(name)
3828
3829        set porturl $portinfo(porturl)
3830        if {![info exists porturl]} {
3831            set porturl file://./
3832        }
3833        set depscache(port:$newname) 1
3834    } else {
3835        set newname $portname
3836    }
3837
3838    array set interp_options [array get options]
3839    set interp_options(ports_requested) $requestedflag
3840    set interp_options(subport) $newname
3841    # Mark this port to be rebuilt from source if this isn't the first time it
3842    # was flagged as broken by rev-upgrade
3843    if {$is_revupgrade_second_run} {
3844        set interp_options(ports_source_only) yes
3845    }
3846
3847    if {[catch {set mport [mportopen $porturl [array get interp_options] [array get variations]]} result]} {
3848        global errorInfo
3849        ui_debug $errorInfo
3850        ui_error "Unable to open port: $result"
3851        return 1
3852    }
3853    array unset interp_options
3854
3855    array unset portinfo
3856    array set portinfo [mportinfo $mport]
3857    set version_in_tree $portinfo(version)
3858    set revision_in_tree $portinfo(revision)
3859    set epoch_in_tree $portinfo(epoch)
3860
3861    set build_override 0
3862    set will_install yes
3863    # check installed version against version in ports
3864    if {([vercmp $version_installed $version_in_tree] > 0
3865            || ([vercmp $version_installed $version_in_tree] == 0
3866                && [vercmp $revision_installed $revision_in_tree] >= 0))
3867        && ![info exists options(ports_upgrade_force)]} {
3868        if {$portname ne $newname} {
3869            ui_debug "ignoring versions, installing replacement port"
3870        } elseif {$epoch_installed < $epoch_in_tree && $version_installed != $version_in_tree} {
3871            set build_override 1
3872            ui_debug "epoch override ... upgrading!"
3873        } elseif {[info exists options(ports_upgrade_enforce-variants)] && $options(ports_upgrade_enforce-variants) eq {yes}
3874                  && [info exists portinfo(canonical_active_variants)] && $portinfo(canonical_active_variants) != $oldvariant} {
3875            ui_debug "variant override ... upgrading!"
3876        } elseif {$os_platform_installed ne {} && $os_major_installed ne {} && $os_platform_installed != 0
3877                  && ([_mportkey $mport {{os.platform}}] != $os_platform_installed
3878                  || [_mportkey $mport {{os.major}}] != $os_major_installed)} {
3879            ui_debug "platform mismatch ... upgrading!"
3880            set build_override 1
3881        } elseif {$is_revupgrade_second_run} {
3882            ui_debug "rev-upgrade override ... upgrading (from source)!"
3883            set build_override 1
3884        } elseif {$is_revupgrade} {
3885            ui_debug "rev-upgrade override ... upgrading!"
3886            # in the first run of rev-upgrade, only activate possibly already existing files and check for missing dependencies
3887            # do nothing, just prevent will_install being set to no below
3888        } else {
3889            if {[info exists portinfo(canonical_active_variants)] && $portinfo(canonical_active_variants) != $oldvariant} {
3890                if {[llength $variationslist] > 0} {
3891                    ui_warn "Skipping upgrade since $portname ${version_installed}_$revision_installed >= $portname ${version_in_tree}_${revision_in_tree}, even though installed variants \"$oldvariant\" do not match \"$portinfo(canonical_active_variants)\". Use 'upgrade --enforce-variants' to switch to the requested variants."
3892                } else {
3893                    ui_debug "Skipping upgrade since $portname ${version_installed}_$revision_installed >= $portname ${version_in_tree}_${revision_in_tree}, even though installed variants \"$oldvariant\" do not match \"$portinfo(canonical_active_variants)\"."
3894                }
3895            } else {
3896                ui_debug "No need to upgrade! $portname ${version_installed}_$revision_installed >= $portname ${version_in_tree}_$revision_in_tree"
3897            }
3898            set will_install no
3899        }
3900    }
3901
3902    set will_build no
3903    set already_installed [registry::entry_exists $newname $version_in_tree $revision_in_tree $portinfo(canonical_active_variants)]
3904    # avoid building again unnecessarily
3905    if {$will_install &&
3906        ([info exists options(ports_upgrade_force)]
3907            || $build_override == 1
3908            || !$already_installed)} {
3909        set will_build yes
3910    }
3911
3912    # first upgrade dependencies
3913    if {![info exists options(ports_nodeps)]} {
3914        # the last arg is because we might have to build from source if a rebuild is being forced
3915        set status [_upgrade_dependencies portinfo depscache variationslist options [expr {$will_build && $already_installed}]]
3916        if {$status != 0 && $status != 2 && ![ui_isset ports_processall]} {
3917            catch {mportclose $mport}
3918            return $status
3919        }
3920    } else {
3921        ui_debug "Not following dependencies"
3922    }
3923
3924    if {!$will_install} {
3925        # nothing to do for this port, so just check if we have to do dependents
3926        if {[info exists options(ports_do_dependents)]} {
3927            # We do dependents ..
3928            set options(ports_nodeps) 1
3929
3930            registry::open_dep_map
3931            if {$anyactive} {
3932                set deplist [registry::list_dependents $portname $version_active $revision_active $variant_active]
3933            } else {
3934                set deplist [registry::list_dependents $portname $version_installed $revision_installed $variant_installed]
3935            }
3936
3937            if {[llength deplist] > 0} {
3938                foreach dep $deplist {
3939                    set mpname [lindex $dep 2]
3940                    if {![llength [array get depscache port:$mpname]]} {
3941                        set status [macports::_upgrade $mpname port:$mpname $variationslist [array get options] depscache]
3942                        if {$status != 0 && $status != 2 && ![ui_isset ports_processall]} {
3943                            catch {mportclose $mport}
3944                            return $status
3945                        }
3946                    }
3947                }
3948            }
3949        }
3950        mportclose $mport
3951        return 0
3952    }
3953
3954    if {$will_build} {
3955        if {$already_installed
3956            && ([info exists options(ports_upgrade_force)] || $build_override == 1)} {
3957            # Tell archivefetch/unarchive not to use the installed archive, i.e. a
3958            # fresh one will be either fetched or built locally.
3959            # Ideally this would be done in the interp_options when we mportopen,
3960            # but we don't know if we want to do this at that point.
3961            set workername [ditem_key $mport workername]
3962            $workername eval "set force_archive_refresh yes"
3963
3964            # run archivefetch and destroot for version_in_tree
3965            # doing this instead of just running install ensures that we have the
3966            # new copy ready but not yet installed, so we can safely uninstall the
3967            # existing one.
3968            if {[catch {set result [mportexec $mport archivefetch]} result] || $result != 0} {
3969                if {[info exists ::errorInfo]} {
3970                    ui_debug $::errorInfo
3971                }
3972                catch {mportclose $mport}
3973                return 1
3974            }
3975            # the following is a noop if archivefetch found an archive
3976            if {[catch {set result [mportexec $mport destroot]} result] || $result != 0} {
3977                if {[info exists ::errorInfo]} {
3978                    ui_debug $::errorInfo
3979                }
3980                catch {mportclose $mport}
3981                return 1
3982            }
3983        } else {
3984            # Normal non-forced case
3985            # install version_in_tree (but don't activate yet)
3986            if {[catch {set result [mportexec $mport install]} result] || $result != 0} {
3987                if {[info exists ::errorInfo]} {
3988                    ui_debug $::errorInfo
3989                }
3990                catch {mportclose $mport}
3991                return 1
3992            }
3993        }
3994    }
3995
3996    # are we installing an existing version due to force or epoch override?
3997    if {$already_installed
3998        && ([info exists options(ports_upgrade_force)] || $build_override == 1)} {
3999         ui_debug "Uninstalling $newname ${version_in_tree}_${revision_in_tree}$portinfo(canonical_active_variants)"
4000        # we have to force the uninstall in case of dependents
4001        set force_cur [info exists options(ports_force)]
4002        set options(ports_force) yes
4003        set existing_epoch [lindex [registry::installed $newname ${version_in_tree}_${revision_in_tree}$portinfo(canonical_active_variants)] 0 5]
4004        set newregref [registry::open_entry $newname $version_in_tree $revision_in_tree $portinfo(canonical_active_variants) $existing_epoch]
4005        if {$is_dryrun eq {yes}} {
4006            ui_msg "Skipping uninstall $newname @${version_in_tree}_${revision_in_tree}$portinfo(canonical_active_variants) (dry run)"
4007        } elseif {![registry::run_target $newregref uninstall [array get options]]
4008                  && [catch {registry_uninstall::uninstall $newname $version_in_tree $revision_in_tree $portinfo(canonical_active_variants) [array get options]} result]} {
4009            global errorInfo
4010            ui_debug $errorInfo
4011            ui_error "Uninstall $newname ${version_in_tree}_${revision_in_tree}$portinfo(canonical_active_variants) failed: $result"
4012            catch {mportclose $mport}
4013            return 1
4014        }
4015        if {!$force_cur} {
4016            unset options(ports_force)
4017        }
4018        if {$anyactive && $version_in_tree == $version_active && $revision_in_tree == $revision_active
4019            && $portinfo(canonical_active_variants) == $variant_active && $portname eq $newname} {
4020            set anyactive no
4021        }
4022    }
4023    if {$anyactive && $portname ne $newname} {
4024        # replaced_by in effect, deactivate the old port
4025        # we have to force the deactivate in case of dependents
4026        set force_cur [info exists options(ports_force)]
4027        set options(ports_force) yes
4028        if {$is_dryrun eq {yes}} {
4029            ui_msg "Skipping deactivate $portname @${version_active}_${revision_active}$variant_active (dry run)"
4030        } elseif {![catch {registry::active $portname}] &&
4031                  ![registry::run_target $regref deactivate [array get options]]
4032                  && [catch {portimage::deactivate $portname $version_active $revision_active $variant_active [array get options]} result]} {
4033            global errorInfo
4034            ui_debug $errorInfo
4035            ui_error "Deactivating $portname @${version_active}_${revision_active}$variant_active failed: $result"
4036            catch {mportclose $mport}
4037            return 1
4038        }
4039        if {!$force_cur} {
4040            unset options(ports_force)
4041        }
4042        set anyactive no
4043    }
4044    if {[info exists options(port_uninstall_old)] && $portname eq $newname} {
4045        # uninstalling now could fail due to dependents when not forced,
4046        # because the new version is not installed
4047        set uninstall_later yes
4048    }
4049
4050    if {$is_dryrun eq {yes}} {
4051        if {$anyactive} {
4052            ui_msg "Skipping deactivate $portname @${version_active}_${revision_active}$variant_active (dry run)"
4053        }
4054        ui_msg "Skipping activate $newname @${version_in_tree}_${revision_in_tree}$portinfo(canonical_active_variants) (dry run)"
4055    } elseif {[catch {set result [mportexec $mport activate]} result]} {
4056        global errorInfo
4057        ui_debug $errorInfo
4058        ui_error "Couldn't activate $newname ${version_in_tree}_${revision_in_tree}$portinfo(canonical_active_variants): $result"
4059        catch {mportclose $mport}
4060        return 1
4061    }
4062
4063    # Check if we have to do dependents
4064    if {[info exists options(ports_do_dependents)]} {
4065        # We do dependents ..
4066        set options(ports_nodeps) 1
4067
4068        registry::open_dep_map
4069        if {$portname ne $newname} {
4070            set deplist [registry::list_dependents $newname $version_in_tree $revision_in_tree $portinfo(canonical_active_variants)]
4071        } else {
4072            set deplist [list]
4073        }
4074        if {$anyactive} {
4075            set deplist [concat $deplist [registry::list_dependents $portname $version_active $revision_active $variant_active]]
4076        } else {
4077            set deplist [concat $deplist [registry::list_dependents $portname $version_installed $revision_installed $variant_installed]]
4078        }
4079
4080        if {[llength deplist] > 0} {
4081            foreach dep $deplist {
4082                set mpname [lindex $dep 2]
4083                if {![llength [array get depscache port:$mpname]]} {
4084                    set status [macports::_upgrade $mpname port:$mpname $variationslist [array get options] depscache]
4085                    if {$status != 0 && $status != 2 && ![ui_isset ports_processall]} {
4086                        catch {mportclose $mport}
4087                        return $status
4088                    }
4089                }
4090            }
4091        }
4092    }
4093
4094    if {[info exists uninstall_later] && $uninstall_later eq yes} {
4095        foreach i $ilist {
4096            set version [lindex $i 1]
4097            set revision [lindex $i 2]
4098            set variant [lindex $i 3]
4099            if {$version == $version_in_tree && $revision == $revision_in_tree && $variant == $portinfo(canonical_active_variants) && $portname eq $newname} {
4100                continue
4101            }
4102            set epoch [lindex $i 5]
4103            ui_debug "Uninstalling $portname ${version}_${revision}$variant"
4104            set regref [registry::open_entry $portname $version $revision $variant $epoch]
4105            if {$is_dryrun eq {yes}} {
4106                ui_msg "Skipping uninstall $portname @${version}_${revision}$variant (dry run)"
4107            } elseif {![registry::run_target $regref uninstall $optionslist]
4108                      && [catch {registry_uninstall::uninstall $portname $version $revision $variant $optionslist} result]} {
4109                global errorInfo
4110                ui_debug $errorInfo
4111                # replaced_by can mean that we try to uninstall all versions of the old port, so handle errors due to dependents
4112                if {$result ne "Please uninstall the ports that depend on $portname first." && ![ui_isset ports_processall]} {
4113                    ui_error "Uninstall $portname @${version}_${revision}$variant failed: $result"
4114                    catch {mportclose $mport}
4115                    return 1
4116                }
4117            }
4118        }
4119    }
4120
4121    # close the port handle
4122    mportclose $mport
4123    return 0
4124}
4125
4126# upgrade_dependencies: helper proc for upgrade
4127# Calls upgrade on each dependency listed in the PortInfo.
4128# Uses upvar to access the variables.
4129proc macports::_upgrade_dependencies {portinfoname depscachename variationslistname optionsname {build_needed no}} {
4130    upvar $portinfoname portinfo $depscachename depscache \
4131          $variationslistname variationslist \
4132          $optionsname options
4133    upvar mport parentmport
4134
4135    # If we're following dependents, we only want to follow this port's
4136    # dependents, not those of all its dependencies. Otherwise, we would
4137    # end up processing this port's dependents n+1 times (recursively!),
4138    # where n is the number of dependencies this port has, since this port
4139    # is of course a dependent of each of its dependencies. Plus the
4140    # dependencies could have any number of unrelated dependents.
4141
4142    # So we save whether we're following dependents, unset the option
4143    # while doing the dependencies, and restore it afterwards.
4144    set saved_do_dependents [info exists options(ports_do_dependents)]
4145    unset -nocomplain options(ports_do_dependents)
4146
4147    set parentworker [ditem_key $parentmport workername]
4148    # each required dep type is upgraded
4149    if {$build_needed && ![global_option_isset ports_binary_only]} {
4150        set dtypes [_deptypes_for_target destroot $parentworker]
4151    } else {
4152        set dtypes [_deptypes_for_target install $parentworker]
4153    }
4154
4155    set status 0
4156    foreach dtype $dtypes {
4157        if {[info exists portinfo($dtype)]} {
4158            foreach i $portinfo($dtype) {
4159                set d [$parentworker eval _get_dep_port $i]
4160                if {![llength [array get depscache port:$d]] && ![llength [array get depscache $i]]} {
4161                    if {$d ne {}} {
4162                        set dspec port:$d
4163                    } else {
4164                        set dspec $i
4165                        set d [lindex [split $i :] end]
4166                    }
4167                    set status [macports::_upgrade $d $dspec $variationslist [array get options] depscache]
4168                    if {$status != 0 && $status != 2 && ![ui_isset ports_processall]} break
4169                }
4170            }
4171        }
4172        if {$status != 0 && $status != 2 && ![ui_isset ports_processall]} break
4173    }
4174    # restore dependent-following to its former value
4175    if {$saved_do_dependents} {
4176        set options(ports_do_dependents) yes
4177    }
4178    return $status
4179}
4180
4181# mportselect
4182#   * command: The only valid commands are list, set, show and summary
4183#   * group: This argument should correspond to a directory under
4184#            ${macports::prefix}/etc/select.
4185#   * version: This argument is only used by the 'set' command.
4186# On error mportselect returns with the code 'error'.
4187proc mportselect {command {group ""} {version {}}} {
4188    ui_debug "mportselect \[$command] \[$group] \[$version]"
4189
4190    set conf_path ${macports::prefix}/etc/select/$group
4191    if {![file isdirectory $conf_path]} {
4192        return -code error "The specified group '$group' does not exist."
4193    }
4194
4195    switch -- $command {
4196        list {
4197            if {[catch {set versions [glob -directory $conf_path *]} result]} {
4198                global errorInfo
4199                ui_debug "${result}: $errorInfo"
4200                return -code error [concat "No configurations associated" \
4201                                           "with '$group' were found."]
4202            }
4203
4204            # Return the sorted list of versions (excluding base and current).
4205            set lversions {}
4206            foreach v $versions {
4207                # Only the file name corresponds to the version name.
4208                set v [file tail $v]
4209                if {$v eq {base} || $v eq {current}} {
4210                    continue
4211                }
4212                lappend lversions [file tail $v]
4213            }
4214            return [lsort $lversions]
4215        }
4216        summary {
4217            # Return the list of portgroups in ${macports::prefix}/etc/select
4218            if {[catch {set lportgroups [glob -directory $conf_path -tails *]} result]} {
4219                global errorInfo
4220                ui_debug "${result}: $errorInfo"
4221                return -code error [concat "No ports with the select" \
4222                                           "option were found."]
4223            }
4224            return [lsort $lportgroups]
4225        }
4226        set {
4227            # Use ${conf_path}/$version to read in sources.
4228            if {$version eq {} || $version eq {base} || $version eq {current}
4229                    || [catch {set src_file [open "${conf_path}/$version"]} result]} {
4230                global errorInfo
4231                ui_debug "${result}: $errorInfo"
4232                return -code error "The specified version '$version' is not valid."
4233            }
4234            set srcs [split [read -nonewline $src_file] \n]
4235            close $src_file
4236
4237            # Use ${conf_path}/base to read in targets.
4238            if {[catch {set tgt_file [open ${conf_path}/base]} result]} {
4239                global errorInfo
4240                ui_debug "${result}: $errorInfo"
4241                return -code error [concat "The configuration file" \
4242                                           "'${conf_path}/base' could not be" \
4243                                           "opened."]
4244            }
4245            set tgts [split [read -nonewline $tgt_file] \n]
4246            close $tgt_file
4247
4248            # Iterate through the configuration files executing the specified
4249            # actions.
4250            set i 0
4251            foreach tgt $tgts {
4252                set src [lindex $srcs $i]
4253
4254                switch -glob -- $src {
4255                    - {
4256                        # The source is unavailable for this file.
4257                        set tgt [file join $macports::prefix $tgt]
4258                        file delete $tgt
4259                        ui_debug "rm -f $tgt"
4260                    }
4261                    /* {
4262                        # The source is an absolute path.
4263                        set tgt [file join $macports::prefix $tgt]
4264                        file delete $tgt
4265                        file link -symbolic $tgt $src
4266                        ui_debug "ln -sf $src $tgt"
4267                    }
4268                    default {
4269                        # The source is a relative path.
4270                        set src [file join $macports::prefix $src]
4271                        set tgt [file join $macports::prefix $tgt]
4272                        file delete $tgt
4273                        file link -symbolic $tgt $src
4274                        ui_debug "ln -sf $src $tgt"
4275                    }
4276                }
4277                incr i
4278            }
4279
4280            # Update the selected version.
4281            set selected_version ${conf_path}/current
4282            if {[file exists $selected_version]} {
4283                file delete $selected_version
4284            }
4285            symlink $version $selected_version
4286            return
4287        }
4288        show {
4289            set selected_version ${conf_path}/current
4290
4291            if {![file exists $selected_version]} {
4292                return none
4293            } else {
4294                return [file readlink $selected_version]
4295            }
4296        }
4297    }
4298    return
4299}
4300
4301# Return a good temporary directory to use; /tmp if TMPDIR is not set
4302# in the environment
4303proc macports::gettmpdir {args} {
4304    global env
4305
4306    if {[info exists env(TMPDIR)]} {
4307        return $env(TMPDIR)
4308    } else {
4309        return /tmp
4310    }
4311}
4312
4313# check if the system we're on can run code of the given architecture
4314proc macports::arch_runnable {arch} {
4315    global macports::os_major macports::os_arch macports::os_platform
4316    if {$macports::os_platform eq {darwin}} {
4317        if {$macports::os_major >= 11 && [string first ppc $arch] == 0} {
4318            return no
4319        } elseif {$macports::os_arch eq {i386} && $arch eq {ppc64}} {
4320            return no
4321        } elseif {$macports::os_major <= 8 && $arch eq {x86_64}} {
4322            return no
4323        }
4324    }
4325    return yes
4326}
4327
4328proc macports::revupgrade {opts} {
4329    set run_loop 1
4330    array set broken_port_counts {}
4331    while {$run_loop == 1} {
4332        set run_loop [revupgrade_scanandrebuild broken_port_counts $opts]
4333    }
4334    return 0
4335}
4336
4337# returns 1 if ports were rebuilt and revupgrade_scanandrebuild should be called again
4338proc macports::revupgrade_scanandrebuild {broken_port_counts_name opts} {
4339    upvar $broken_port_counts_name broken_port_counts
4340    array set options $opts
4341
4342    set files [registry::file search active 1 binary -null]
4343    set files_count [llength $files]
4344    set fancy_output [expr {![macports::ui_isset ports_debug] && [info exists macports::ui_options(progress_generic)]}]
4345    if {$fancy_output} {
4346        set revupgrade_progress $macports::ui_options(progress_generic)
4347    }
4348    if {$files_count > 0} {
4349        registry::write {
4350            try {
4351                ui_msg "$macports::ui_prefix Updating database of binaries"
4352                set i 1
4353                if {$fancy_output} {
4354                    $revupgrade_progress start
4355                }
4356                foreach f $files {
4357                    if {$fancy_output} {
4358                        if {$files_count < 10000 || $i % 10 == 1} {
4359                            $revupgrade_progress update $i $files_count
4360                        }
4361                    }
4362                    set fpath [$f actual_path]
4363                    ui_debug "Updating binary flag for file $i of ${files_count}: $fpath"
4364                    incr i
4365
4366                    if {0 != [catch {$f binary [fileIsBinary $fpath]} fileIsBinaryError]} {
4367                        # handle errors (e.g. file not found, permission denied) gracefully
4368                        if {$fancy_output} {
4369                            $revupgrade_progress intermission
4370                        }
4371                        ui_warn "Error determining file type of `$fpath': $fileIsBinaryError"
4372                        ui_warn "A file belonging to the `[[registry::entry owner $fpath] name]' port is missing or unreadable. Consider reinstalling it."
4373                    }
4374                }
4375            } catch {*} {
4376                ui_error "Updating database of binaries failed"
4377                throw
4378            }
4379        }
4380        if {$fancy_output} {
4381            $revupgrade_progress finish
4382        }
4383    }
4384
4385    set broken_files {};
4386    set binaries [registry::file search active 1 binary 1]
4387    set binary_count [llength $binaries]
4388    if {$binary_count > 0} {
4389        ui_msg "$macports::ui_prefix Scanning binaries for linking errors"
4390        set handle [machista::create_handle]
4391        if {$handle eq {NULL}} {
4392            error "Error creating libmachista handle"
4393        }
4394        array unset files_warned_about
4395        array set files_warned_about [list]
4396
4397        if {$fancy_output} {
4398            $revupgrade_progress start
4399        }
4400
4401        set i 1
4402        foreach b $binaries {
4403            if {$fancy_output} {
4404                if {$binary_count < 10000 || $i % 10 == 1} {
4405                    $revupgrade_progress update $i $binary_count
4406                }
4407            }
4408            set bpath [$b actual_path]
4409            #ui_debug "${i}/${binary_count}: $bpath"
4410            incr i
4411
4412            set resultlist [machista::parse_file $handle $bpath]
4413            set returncode [lindex $resultlist 0]
4414            set result     [lindex $resultlist 1]
4415
4416            if {$returncode != $machista::SUCCESS} {
4417                if {$returncode == $machista::EMAGIC} {
4418                    # not a Mach-O file
4419                    # ignore silently, these are only static libs anyway
4420                    #ui_debug "Error parsing file ${bpath}: [machista::strerror $returncode]"
4421                } else {
4422                    if {$fancy_output} {
4423                        $revupgrade_progress intermission
4424                    }
4425                    ui_warn "Error parsing file ${bpath}: [machista::strerror $returncode]"
4426                }
4427                continue;
4428            }
4429
4430            set architecture [$result cget -mt_archs]
4431            while {$architecture ne {NULL}} {
4432                if {[info exists options(ports_rev-upgrade_id-loadcmd-check)] && $options(ports_rev-upgrade_id-loadcmd-check) eq {yes}} {
4433                    if {[$architecture cget -mat_install_name] ne {NULL} && [$architecture cget -mat_install_name] ne {}} {
4434                        # check if this lib's install name actually refers to this file itself
4435                        # if this is not the case software linking against this library might have erroneous load commands
4436                        if {0 == [catch {set idloadcmdpath [revupgrade_handle_special_paths $bpath [$architecture cget -mat_install_name]]}]} {
4437                            if {[string index $idloadcmdpath 0] ne {/}} {
4438                                set port [registry::entry owner $bpath]
4439                                if {$port ne {}} {
4440                                    set portname [$port name]
4441                                } else {
4442                                    set portname <unknown-port>
4443                                }
4444                                if {$fancy_output} {
4445                                    $revupgrade_progress intermission
4446                                }
4447                                ui_warn "ID load command in ${bpath}, arch [machista::get_arch_name [$architecture cget -mat_arch]] (belonging to port $portname) contains relative path"
4448                            } elseif {![file exists $idloadcmdpath]} {
4449                                set port [registry::entry owner $bpath]
4450                                if {$port ne {}} {
4451                                    set portname [$port name]
4452                                } else {
4453                                    set portname <unknown-port>
4454                                }
4455                                if {$fancy_output} {
4456                                    $revupgrade_progress intermission
4457                                }
4458                                ui_warn "ID load command in ${bpath}, arch [machista::get_arch_name [$architecture cget -mat_arch]] refers to non-existant file $idloadcmdpath"
4459                                ui_warn "This is probably a bug in the $portname port and might cause problems in libraries linking against this file"
4460                            } else {
4461
4462                                set hash_this [sha256 file $bpath]
4463                                set hash_idloadcmd [sha256 file $idloadcmdpath]
4464
4465                                if {$hash_this ne $hash_idloadcmd} {
4466                                    set port [registry::entry owner $bpath]
4467                                    if {$port ne {}} {
4468                                        set portname [$port name]
4469                                    } else {
4470                                        set portname <unknown-port>
4471                                    }
4472                                    if {$fancy_output} {
4473                                        $revupgrade_progress intermission
4474                                    }
4475                                    ui_warn "ID load command in ${bpath}, arch [machista::get_arch_name [$architecture cget -mat_arch]] refers to file ${idloadcmdpath}, which is a different file"
4476                                    ui_warn "This is probably a bug in the $portname port and might cause problems in libraries linking against this file"
4477                                }
4478                            }
4479                        }
4480                    }
4481                }
4482
4483                set archname [machista::get_arch_name [$architecture cget -mat_arch]]
4484                if {![arch_runnable $archname]} {
4485                    ui_debug "skipping $archname in $bpath since this system can't run it anyway"
4486                    set architecture [$architecture cget -next]
4487                    continue
4488                }
4489
4490                set loadcommand [$architecture cget -mat_loadcmds]
4491
4492                while {$loadcommand ne {NULL}} {
4493                    if {0 != [catch {set filepath [revupgrade_handle_special_paths $bpath [$loadcommand cget -mlt_install_name]]}]} {
4494                        set loadcommand [$loadcommand cget -next]
4495                        continue;
4496                    }
4497
4498                    set libresultlist [machista::parse_file $handle $filepath]
4499                    set libreturncode [lindex $libresultlist 0]
4500                    set libresult     [lindex $libresultlist 1]
4501
4502                    if {$libreturncode != $machista::SUCCESS} {
4503                        if {![info exists files_warned_about($filepath)]} {
4504                            if {$fancy_output} {
4505                                $revupgrade_progress intermission
4506                            }
4507                            ui_info "Could not open ${filepath}: [machista::strerror $libreturncode] (referenced from $bpath)"
4508                            set files_warned_about($filepath) yes
4509                        }
4510                        if {$libreturncode == $machista::EFILE} {
4511                            ui_debug "Marking $bpath as broken"
4512                            lappend broken_files $bpath
4513                        }
4514                        set loadcommand [$loadcommand cget -next]
4515                        continue;
4516                    }
4517
4518                    set libarchitecture [$libresult cget -mt_archs]
4519                    set libarch_found false;
4520                    while {$libarchitecture ne {NULL}} {
4521                        if {[$architecture cget -mat_arch] ne [$libarchitecture cget -mat_arch]} {
4522                            set libarchitecture [$libarchitecture cget -next]
4523                            continue;
4524                        }
4525
4526                        if {[$loadcommand cget -mlt_version] ne [$libarchitecture cget -mat_version] && [$loadcommand cget -mlt_comp_version] > [$libarchitecture cget -mat_comp_version]} {
4527                            if {$fancy_output} {
4528                                $revupgrade_progress intermission
4529                            }
4530                            ui_info "Incompatible library version: $bpath requires version [machista::format_dylib_version [$loadcommand cget -mlt_comp_version]] or later, but $filepath provides version [machista::format_dylib_version [$libarchitecture cget -mat_comp_version]]"
4531                            ui_debug "Marking $bpath as broken"
4532                            lappend broken_files $bpath
4533                        }
4534
4535                        set libarch_found true;
4536                        break;
4537                    }
4538
4539                    if {$libarch_found eq false} {
4540                        ui_debug "Missing architecture [machista::get_arch_name [$architecture cget -mat_arch]] in file $filepath"
4541                        if {[path_is_in_prefix $filepath]} {
4542                            ui_debug "Marking $bpath as broken"
4543                            lappend broken_files $bpath
4544                        } else {
4545                            ui_debug "Missing architecture [machista::get_arch_name [$architecture cget -mat_arch]] in file outside prefix referenced from $bpath"
4546                            # ui_debug "   How did you get that compiled anyway?"
4547                        }
4548                    }
4549                    set loadcommand [$loadcommand cget -next]
4550                }
4551
4552                set architecture [$architecture cget -next]
4553            }
4554        }
4555        if {$fancy_output} {
4556            $revupgrade_progress finish
4557        }
4558
4559        machista::destroy_handle $handle
4560
4561        if {[llength $broken_files] == 0} {
4562            ui_msg "$macports::ui_prefix No broken files found."
4563            return 0
4564        }
4565        ui_msg "$macports::ui_prefix Found [llength $broken_files] broken file(s), matching files to ports"
4566        set broken_ports {}
4567        set broken_files [lsort -unique $broken_files]
4568        foreach file $broken_files {
4569            set port [registry::entry owner $file]
4570            if {$port ne {}} {
4571                lappend broken_ports $port
4572                lappend broken_files_by_port($port) $file
4573            } else {
4574                ui_error "Broken file $file doesn't belong to any port."
4575            }
4576        }
4577        set broken_ports [lsort -unique $broken_ports]
4578
4579        if {$macports::revupgrade_mode eq {rebuild}} {
4580            # don't try to rebuild ports that don't exist in the tree
4581            set temp_broken_ports {}
4582            foreach port $broken_ports {
4583                set portname [$port name]
4584                if {[catch {mportlookup $portname} result]} {
4585                    ui_debug $::errorInfo
4586                    error "lookup of portname $portname failed: $result"
4587                }
4588                if {[llength $result] >= 2} {
4589                    lappend temp_broken_ports $port
4590                } else {
4591                    ui_warn "No port $portname found in the index; can't rebuild"
4592                }
4593            }
4594
4595            if {[llength $temp_broken_ports] == 0} {
4596                ui_msg "$macports::ui_prefix Broken files found, but all associated ports are not in the index and so cannot be rebuilt."
4597                return 0
4598            }
4599        } else {
4600            set temp_broken_ports $broken_ports
4601        }
4602
4603        set broken_ports {}
4604
4605        foreach port $temp_broken_ports {
4606            set portname [$port name]
4607
4608            if {![info exists broken_port_counts($portname)]} {
4609                set broken_port_counts($portname) 0
4610            }
4611            incr broken_port_counts($portname)
4612            if {$broken_port_counts($portname) > 3} {
4613                ui_error "Port $portname is still broken after rebuilding it more than 3 times."
4614                if {$fancy_output} {
4615                    ui_error "Please run port -d -y rev-upgrade and use the output to report a bug."
4616                }
4617                error "Port $portname still broken after rebuilding [expr {$broken_port_counts($portname) - 1}] time(s)"
4618            } elseif {$broken_port_counts($portname) > 1 && [global_option_isset ports_binary_only]} {
4619                error "Port $portname still broken after reinstalling -- can't rebuild due to binary-only mode"
4620            }
4621            lappend broken_ports $port
4622        }
4623        unset temp_broken_ports
4624
4625        if {$macports::revupgrade_mode ne {rebuild}} {
4626            ui_msg "$macports::ui_prefix Found [llength $broken_ports] broken port(s):"
4627            foreach port $broken_ports {
4628                ui_msg "     [$port name] @[$port version] [$port variants][$port negated_variants]"
4629                foreach f $broken_files_by_port($port) {
4630                    ui_msg "         $f"
4631                }
4632            }
4633            return 0
4634        }
4635
4636        ui_msg "$macports::ui_prefix Found [llength $broken_ports] broken port(s), determining rebuild order"
4637        # broken_ports are the nodes in our graph
4638        # now we need adjacents
4639        foreach port $broken_ports {
4640            # initialize with empty list
4641            set adjlist($port) {}
4642            set revadjlist($port) {}
4643            ui_debug "Broken: [$port name]"
4644        }
4645
4646        array set visited {}
4647        foreach port $broken_ports {
4648            # stack of broken nodes we've come across
4649            set stack {}
4650            lappend stack $port
4651
4652            # build graph
4653            if {![info exists visited($port)]} {
4654                revupgrade_buildgraph $port stack adjlist revadjlist visited
4655            }
4656        }
4657
4658        set unsorted_ports $broken_ports
4659        set topsort_ports {}
4660        while {[llength $unsorted_ports] > 0} {
4661            set lowest_adj_number [llength $adjlist([lindex $unsorted_ports 0])]
4662            set lowest_adj_port [lindex $unsorted_ports 0]
4663
4664            foreach port $unsorted_ports {
4665                set len [llength $adjlist($port)]
4666                if {$len < $lowest_adj_number} {
4667                    set lowest_adj_port $port
4668                    set lowest_adj_number $len
4669                }
4670                if {$len == 0} {
4671                    # this node has no further dependencies
4672                    # add it to topsorted list
4673                    lappend topsort_ports $port
4674                    # remove from unsorted list
4675                    set index [lsearch -exact $unsorted_ports $port]
4676                    set unsorted_ports [lreplace $unsorted_ports $index $index]
4677
4678                    # remove edges
4679                    foreach target $revadjlist($port) {
4680                        set index [lsearch -exact $adjlist($target) $port]
4681                        set adjlist($target) [lreplace $adjlist($target) $index $index]
4682                    }
4683
4684                    break;
4685                }
4686            }
4687
4688            # if we arrive here and lowest_adj_number is larger than 0, then we
4689            # have a loop in the graph and need to break it somehow
4690            if {$lowest_adj_number > 0} {
4691                ui_debug "Breaking loop in dependency graph by starting with [$lowest_adj_port name], which has $lowest_adj_number dependencies"
4692                lappend topsort_ports $lowest_adj_port
4693
4694                set index [lsearch -exact $unsorted_ports $lowest_adj_port]
4695                set unsorted_ports [lreplace $unsorted_ports $index $index]
4696
4697                foreach target $revadjlist($port) {
4698                    set index [lsearch -exact $adjlist($target) $lowest_adj_port]
4699                    set adjlist($target) [lreplace $adjlist($target) $index $index]
4700                }
4701            }
4702        }
4703
4704        ui_msg "$macports::ui_prefix Rebuilding in order"
4705        foreach port $topsort_ports {
4706            ui_msg "     [$port name] @[$port version] [$port variants][$port negated_variants]"
4707        }
4708
4709        # shared depscache for all ports that are going to be rebuilt
4710        array set depscache {}
4711        set status 0
4712        array set my_options [array get macports::global_options]
4713        set my_options(ports_revupgrade) yes
4714        foreach port $topsort_ports {
4715            set portname [$port name]
4716            if {![info exists depscache(port:$portname)]} {
4717                unset -nocomplain my_options(ports_revupgrade_second_run) \
4718                                  my_options(ports_nodeps)
4719                if {$broken_port_counts($portname) > 1} {
4720                    set my_options(ports_revupgrade_second_run) yes
4721
4722                    if {$broken_port_counts($portname) > 2} {
4723                        # runtime deps are upgraded the first time, build deps
4724                        # the second, so none left to do the third time
4725                        set my_options(ports_nodeps) yes
4726                    }
4727                }
4728
4729                # call macports::upgrade with ports_revupgrade option to rebuild the port
4730                set status [macports::upgrade $portname port:$portname \
4731                    {} [array get my_options] depscache]
4732                ui_debug "Rebuilding port $portname finished with status $status"
4733                if {$status != 0} {
4734                    error "Error rebuilding $portname"
4735                }
4736            }
4737        }
4738
4739        if {[info exists options(ports_dryrun)] && $options(ports_dryrun) eq {yes}} {
4740            ui_warn "If this was no dry run, rev-upgrade would now run the checks again to find unresolved and newly created problems"
4741            return 0
4742        }
4743        return 1
4744    }
4745
4746    return 0
4747}
4748
4749# Return whether a path is in the macports prefix
4750# Usage: path_is_in_prefix path_to_test
4751# Returns true if the path is in the prefix, false otherwise
4752proc macports::path_is_in_prefix {path} {
4753    global macports::prefix macports::applications_dir
4754    if {[string first $macports::prefix $path] == 0} {
4755        return yes
4756    }
4757    if {[string first $macports::applications_dir $path] == 0} {
4758        return yes
4759    }
4760    return no
4761}
4762
4763# Function to replace macros in loadcommand paths with their proper values (which are usually determined at load time)
4764# Usage: revupgrade_handle_special_paths name_of_file path_from_loadcommand
4765# Returns the corrected path on success or an error in case of failure.
4766# Note that we can't reliably replace @executable_path, because it's only clear when executing a file where it was executed from.
4767# Replacing @rpath does not work yet, but it might be possible to get it working using the rpath attribute in the file containing the
4768# loadcommand
4769proc macports::revupgrade_handle_special_paths {fname path} {
4770    set corrected_path $path
4771
4772    set loaderpath_idx [string first @loader_path $corrected_path]
4773    if {$loaderpath_idx != -1} {
4774        set corrected_path [string replace $corrected_path $loaderpath_idx ${loaderpath_idx}+11 [file dirname $fname]]
4775    }
4776
4777    set executablepath_idx [string first @executable_path $corrected_path]
4778    if {$executablepath_idx != -1} {
4779        ui_debug "Ignoring loadcommand containing @executable_path in $fname"
4780        error "@executable_path in loadcommand"
4781    }
4782
4783    set rpath_idx [string first @rpath $corrected_path]
4784    if {$rpath_idx != -1} {
4785        ui_debug "Ignoring loadcommand containing @rpath in $fname"
4786        error "@rpath in loadcommand"
4787    }
4788
4789    return $corrected_path
4790}
4791
4792# Recursively build the dependency graph between broken ports
4793# Usage: revupgrade_buildgraph start_port name_of_stack name_of_adjacency_list name_of_reverse_adjacency_list name_of_visited_map
4794proc macports::revupgrade_buildgraph {port stackname adjlistname revadjlistname visitedname} {
4795    upvar $stackname stack
4796    upvar $adjlistname adjlist
4797    upvar $revadjlistname revadjlist
4798    upvar $visitedname visited
4799
4800    set visited($port) true
4801
4802    ui_debug "Processing port [$port name] @[$port epoch]:[$port version]_[$port revision] [$port variants] [$port negated_variants]"
4803    set dependent_ports [$port dependents]
4804    foreach dep $dependent_ports {
4805        set is_broken_port false
4806
4807        if {[info exists adjlist($dep)]} {
4808            ui_debug "Dependent [$dep name] is broken, adding edge from [$dep name] to [[lindex $stack 0] name]"
4809            ui_debug "Making [$dep name] new head of stack"
4810            # $dep is one of the broken ports
4811            # add an edge to the last broken port in the DFS
4812            lappend revadjlist([lindex $stack 0]) $dep
4813            lappend adjlist($dep) [lindex $stack 0]
4814            # make this port the new last broken port by prepending it to the stack
4815            set stack [linsert $stack 0 $dep]
4816
4817            set is_broken_port true
4818        }
4819        if {![info exists visited($dep)]} {
4820            revupgrade_buildgraph $dep stack adjlist revadjlist visited
4821        }
4822        if {$is_broken_port} {
4823            ui_debug "Removing [$dep name] from stack"
4824            # remove $dep from the stack
4825            set stack [lrange $stack 1 end]
4826        }
4827    }
4828}
4829
4830# get cached ping time for host, modified by blacklist and preferred list
4831proc macports::get_pingtime {host} {
4832    global macports::ping_cache macports::host_blacklisted macports::host_preferred
4833    if {[info exists host_blacklisted($host)]} {
4834        return -1
4835    } elseif {[info exists host_preferred($host)]} {
4836        return 1
4837    } elseif {[info exists ping_cache($host)]} {
4838        # expire entries after 1 day
4839        if {[clock seconds] - [lindex $ping_cache($host) 1] <= 86400} {
4840            return [lindex $ping_cache($host) 0]
4841        }
4842    }
4843    return {}
4844}
4845
4846# cache a ping time of ms for host
4847proc macports::set_pingtime {host ms} {
4848    global macports::ping_cache
4849    set ping_cache($host) [list $ms [clock seconds]]
4850}
4851
4852# read and cache archive_sites.conf (called from port1.0 code)
4853proc macports::get_archive_sites_conf_values {} {
4854    global macports::archive_sites_conf_values macports::autoconf::macports_conf_path
4855    if {![info exists archive_sites_conf_values]} {
4856        set archive_sites_conf_values {}
4857        set all_names {}
4858        array set defaults {applications_dir /Applications/MacPorts prefix /opt/local type tbz2}
4859        set conf_file ${macports_conf_path}/archive_sites.conf
4860        set conf_options {applications_dir frameworks_dir name prefix type urls}
4861        if {[file isfile $conf_file]} {
4862            set fd [open $conf_file r]
4863            while {[gets $fd line] >= 0} {
4864                if {[regexp {^(\w+)([ \t]+(.*))?$} $line match option ignore val] == 1} {
4865                    if {[lsearch -exact $conf_options $option] >= 0} {
4866                        if {$option eq {name}} {
4867                            set cur_name $val
4868                            lappend all_names $val
4869                        } elseif {[info exists cur_name]} {
4870                            set trimmedval [string trim $val]
4871                            if {$option eq {urls}} {
4872                                set processed_urls {}
4873                                foreach url $trimmedval {
4874                                    lappend processed_urls ${url}:nosubdir
4875                                }
4876                                lappend archive_sites_conf_values portfetch::mirror_sites::sites($cur_name) $processed_urls
4877                                set sites($cur_name) $processed_urls
4878                            } else {
4879                                lappend archive_sites_conf_values portfetch::mirror_sites::archive_${option}($cur_name) $trimmedval
4880                                set archive_${option}($cur_name) $trimmedval
4881                            }
4882                        } else {
4883                            ui_warn "archive_sites.conf: ignoring '$option' occurring before name"
4884                        }
4885                    } else {
4886                        ui_warn "archive_sites.conf: ignoring unknown key '$option'"
4887                    }
4888                }
4889            }
4890            close $fd
4891
4892            # check for unspecified values and set to defaults
4893            foreach cur_name $all_names {
4894                foreach key [array names defaults] {
4895                    if {![info exists archive_${key}($cur_name)]} {
4896                        set archive_${key}($cur_name) $defaults($key)
4897                        lappend archive_sites_conf_values portfetch::mirror_sites::archive_${key}($cur_name) $defaults($key)
4898                    }
4899                }
4900                if {![info exists archive_frameworks_dir($cur_name)]} {
4901                    set archive_frameworks_dir($cur_name) $archive_prefix($cur_name)/Library/Frameworks
4902                    lappend archive_sites_conf_values portfetch::mirror_sites::archive_frameworks_dir($cur_name) $archive_frameworks_dir($cur_name)
4903                }
4904                if {![info exists sites($cur_name)]} {
4905                    ui_warn "archive_sites.conf: no urls set for $cur_name"
4906                    set sites($cur_name) {}
4907                    lappend archive_sites_conf_values portfetch::mirror_sites::sites($cur_name) {}
4908                }
4909            }
4910        }
4911    }
4912    return $archive_sites_conf_values
4913}
Note: See TracBrowser for help on using the repository browser.