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

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

macports1.0: do not treat broken select symlinks as "none"

After uninstalling a port the symlinks created by 'port select' will still
exist in the file system. We should attribute them to the previous selection
instead of 'none' which means no symlinks exist at all.

'file exists' tried to follow the broken symlink, but 'file type' does not and
leads to the expected result.

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