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

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

base: setup signal handling in mportinit

Given the code already in registry2.0/portimage.tcl this will finally prevent
inconsistent state when an activation operation is interrupted. The code there
will correctly catch the error and roll back the changes made to the
filesystem.

There might be further places where MacPorts could be smarter now that is has
signal handling code, but I think this change is already a huge improvement.

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