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

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

fix some cases where mportexec could return without cleaning up first

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 206.2 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 140540 2015-09-22 03:01:49Z jmr@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    set log_needs_pop no
2009    if {$target ne "clean"} {
2010        macports::push_log $mport
2011        set log_needs_pop yes
2012    }
2013
2014    # Use _target_needs_deps as a proxy for whether we're going to
2015    # build and will therefore need to check Xcode version and
2016    # supported_archs.
2017    if {[macports::_target_needs_deps $target]} {
2018        # possibly warn or error out depending on how old xcode is
2019        if {[$workername eval _check_xcode_version] != 0} {
2020            if {$log_needs_pop} {
2021                macports::pop_log
2022            }
2023            return 1
2024        }
2025        # error out if selected arch(s) not supported by this port
2026        if {[$workername eval check_supported_archs] != 0} {
2027            if {$log_needs_pop} {
2028                macports::pop_log
2029            }
2030            return 1
2031        }
2032    }
2033
2034    # Before we build the port, we must build its dependencies.
2035    set dlist {}
2036    if {[macports::_target_needs_deps $target] && [macports::_mport_has_deptypes $mport [macports::_deptypes_for_target $target $workername]]} {
2037        registry::exclusive_lock
2038        # see if we actually need to build this port
2039        if {$target ni {activate install} ||
2040            ![$workername eval registry_exists {$subport} {$version} {$revision} {$portvariants}]} {
2041
2042            # upgrade dependencies that are already installed
2043            if {![macports::global_option_isset ports_nodeps]} {
2044                macports::_upgrade_mport_deps $mport $target
2045            }
2046        }
2047
2048        ui_msg -nonewline "$macports::ui_prefix Computing dependencies for [_mportkey $mport subport]"
2049        if {[macports::ui_isset ports_debug]} {
2050            # play nice with debug messages
2051            ui_msg {}
2052        }
2053        if {[mportdepends $mport $target] != 0} {
2054            if {$log_needs_pop} {
2055                macports::pop_log
2056            }
2057            return 1
2058        }
2059        if {![macports::ui_isset ports_debug]} {
2060            ui_msg {}
2061        }
2062
2063        # Select out the dependents along the critical path,
2064        # but exclude this mport, we might not be installing it.
2065        set dlist [dlist_append_dependents $macports::open_mports $mport {}]
2066
2067        dlist_delete dlist $mport
2068
2069        # print the dep list
2070        if {[llength $dlist] > 0} {
2071            ##
2072            # User Interaction Question
2073            # Asking before installing dependencies
2074            if {[info exists macports::ui_options(questions_yesno)]} {
2075                set deplist {}
2076                foreach ditem $dlist {
2077                    lappend deplist [ditem_key $ditem provides]
2078                }
2079                set retvalue [$macports::ui_options(questions_yesno) "The following dependencies will be installed: " "TestCase#2" [lsort $deplist] {y} 0]
2080                if {$retvalue == 1} {
2081                    if {$log_needs_pop} {
2082                        macports::pop_log
2083                    }
2084                    foreach ditem $dlist {
2085                        mportclose $ditem
2086                    }
2087                    return 0
2088                } 
2089            } else {
2090                set depstring "$macports::ui_prefix Dependencies to be installed:"
2091                foreach ditem $dlist {
2092                    append depstring " [ditem_key $ditem provides]"
2093                }
2094                ui_msg $depstring
2095            }
2096        }
2097
2098        # install them
2099        set result [dlist_eval $dlist _mportactive [list _mportexec activate]]
2100
2101        registry::exclusive_unlock
2102
2103        if {$result ne ""} {
2104            ##
2105            # When this happens, the failing port usually already printed an
2106            # error message. Omit this one to avoid cluttering the output and
2107            # hiding the *real* problem.
2108
2109            #set errstring "The following dependencies were not installed:"
2110            #foreach ditem $result {
2111            #    append errstring " [ditem_key $ditem provides]"
2112            #}
2113            #ui_error $errstring
2114            foreach ditem $dlist {
2115                catch {mportclose $ditem}
2116            }
2117            if {$log_needs_pop} {
2118                macports::pop_log
2119            }
2120            return 1
2121        }
2122
2123        # Close the dependencies, we're done installing them.
2124        foreach ditem $dlist {
2125            mportclose $ditem
2126        }
2127    } else {
2128        # No dependencies, but we still need to check for conflicts.
2129        if {$target eq "" || $target eq "install" || $target eq "activate"} {
2130            if {[catch {_mporterrorifconflictsinstalled $mport}]} {
2131                if {$log_needs_pop} {
2132                    macports::pop_log
2133                }
2134                return 1
2135            }
2136        }
2137    }
2138
2139    set clean 0
2140    if {$macports::portautoclean && ($target eq "install" || $target eq "activate")} {
2141        # If we're doing an install, check if we should clean after
2142        set clean 1
2143    }
2144
2145    # Build this port with the specified target
2146    set result [$workername eval eval_targets $target]
2147
2148    # If auto-clean mode and successful install, clean-up after install
2149    if {$result == 0 && $clean == 1} {
2150        # Make sure we are back in the port path, just in case
2151        set portpath [ditem_key $mport portpath]
2152        catch {cd $portpath}
2153        $workername eval eval_targets clean
2154    }
2155
2156    global ::logenabled ::debuglogname
2157    if {$result != 0 && [info exists ::logenabled] && $::logenabled && [info exists ::debuglogname]} {
2158        ui_error "See $::debuglogname for details."
2159    }
2160
2161    if {$log_needs_pop} {
2162        macports::pop_log
2163    }
2164
2165    return $result
2166}
2167
2168# upgrade any dependencies of mport that are installed and needed for target
2169proc macports::_upgrade_mport_deps {mport target} {
2170    set options [ditem_key $mport options]
2171    set workername [ditem_key $mport workername]
2172    set deptypes [macports::_deptypes_for_target $target $workername]
2173    array set portinfo [mportinfo $mport]
2174    array set depscache {}
2175
2176    set required_archs [$workername eval get_canonical_archs]
2177    set depends_skip_archcheck [_mportkey $mport depends_skip_archcheck]
2178
2179    # Pluralize "arch" appropriately.
2180    set s [expr {[llength $required_archs] == 1 ? "" : "s"}]
2181
2182    set test _portnameactive
2183
2184    foreach deptype $deptypes {
2185        if {![info exists portinfo($deptype)]} {
2186            continue
2187        }
2188        foreach depspec $portinfo($deptype) {
2189            set dep_portname [$workername eval _get_dep_port $depspec]
2190            if {$dep_portname ne "" && ![info exists depscache(port:$dep_portname)] && [$test $dep_portname]} {
2191                set variants {}
2192
2193                # check that the dep has the required archs
2194                set active_archs [_get_registry_archs $dep_portname]
2195                if {$deptype ni {depends_fetch depends_extract} && $active_archs ni {{} noarch}
2196                    && $required_archs ne "noarch" && $dep_portname ni $depends_skip_archcheck} {
2197                    set missing {}
2198                    foreach arch $required_archs {
2199                        if {$arch ni $active_archs} {
2200                            lappend missing $arch
2201                        }
2202                    }
2203                    if {[llength $missing] > 0} {
2204                        set res [mportlookup $dep_portname]
2205                        array unset dep_portinfo
2206                        array set dep_portinfo [lindex $res 1]
2207                        if {[info exists dep_portinfo(installs_libs)] && !$dep_portinfo(installs_libs)} {
2208                            set missing {}
2209                        }
2210                    }
2211                    if {[llength $missing] > 0} {
2212                        if {[info exists dep_portinfo(variants)] && "universal" in $dep_portinfo(variants)} {
2213                            # dep offers a universal variant
2214                            if {[llength $active_archs] == 1} {
2215                                # not installed universal
2216                                set missing {}
2217                                foreach arch $required_archs {
2218                                    if {$arch ni $macports::universal_archs} {
2219                                        lappend missing $arch
2220                                    }
2221                                }
2222                                if {[llength $missing] > 0} {
2223                                    ui_error "Cannot install [_mportkey $mport subport] for the arch${s} '$required_archs' because"
2224                                    ui_error "its dependency $dep_portname is only installed for the arch '$active_archs'"
2225                                    ui_error "and the configured universal_archs '$macports::universal_archs' are not sufficient."
2226                                    return -code error "architecture mismatch"
2227                                } else {
2228                                    # upgrade the dep with +universal
2229                                    lappend variants universal +
2230                                    lappend options ports_upgrade_enforce-variants yes
2231                                    ui_debug "enforcing +universal upgrade for $dep_portname"
2232                                }
2233                            } else {
2234                                # already universal
2235                                ui_error "Cannot install [_mportkey $mport subport] for the arch${s} '$required_archs' because"
2236                                ui_error "its dependency $dep_portname is only installed for the archs '$active_archs'."
2237                                return -code error "architecture mismatch"
2238                            }
2239                        } else {
2240                            ui_error "Cannot install [_mportkey $mport subport] for the arch${s} '$required_archs' because"
2241                            ui_error "its dependency $dep_portname is only installed for the arch '$active_archs'"
2242                            ui_error "and does not have a universal variant."
2243                            return -code error "architecture mismatch"
2244                        }
2245                    }
2246                }
2247
2248                set status [macports::upgrade $dep_portname port:$dep_portname $variants $options depscache]
2249                # status 2 means the port was not found in the index
2250                if {$status != 0 && $status != 2 && ![macports::ui_isset ports_processall]} {
2251                    return -code error "upgrade $dep_portname failed"
2252                }
2253            }
2254        }
2255    }
2256}
2257
2258# get the archs with which the active version of portname is installed
2259proc macports::_get_registry_archs {portname} {
2260    set ilist [registry::active $portname]
2261    set i [lindex $ilist 0]
2262    set regref [registry::open_entry [lindex $i 0] [lindex $i 1] [lindex $i 2] [lindex $i 3] [lindex $i 5]]
2263    set archs [registry::property_retrieve $regref archs]
2264    if {$archs == 0} {
2265        set archs {}
2266    }
2267    return $archs
2268}
2269
2270proc macports::getsourcepath {url} {
2271    global macports::portdbpath
2272
2273    set source_path [split $url ://]
2274
2275    if {[_source_is_snapshot $url]} {
2276        # daily snapshot tarball
2277        return [file join $portdbpath sources [join [lrange $source_path 3 end-1] /] ports]
2278    }
2279
2280    return [file join $portdbpath sources [lindex $source_path 3] [lindex $source_path 4] [lindex $source_path 5]]
2281}
2282
2283##
2284# Checks whether a supplied source URL is for a daily snapshot tarball
2285# (private)
2286#
2287# @param url source URL to check
2288# @return a list containing filename and extension or an empty list
2289proc _source_is_snapshot {url {filename {}} {extension {}}} {
2290    upvar $filename myfilename
2291    upvar $extension myextension
2292
2293    if {[regexp {^(?:https?|ftp|rsync)://.+/(.+\.(tar\.gz|tar\.bz2|tar))$} $url -> f e]} {
2294        set myfilename $f
2295        set myextension $e
2296
2297        return 1
2298    }
2299
2300    return 0
2301}
2302
2303proc macports::getportbuildpath {id {portname {}}} {
2304    global macports::portdbpath
2305    regsub {://} $id {.} port_path
2306    regsub -all {/} $port_path {_} port_path
2307    return [file join $portdbpath build $port_path $portname]
2308}
2309
2310proc macports::getportlogpath {id {portname {}}} {
2311    global macports::portdbpath
2312    regsub {://} $id {.} port_path
2313    regsub -all {/} $port_path {_} port_path
2314    return [file join $portdbpath logs $port_path $portname]
2315}
2316
2317proc macports::getportworkpath_from_buildpath {portbuildpath} {
2318    return [file normalize [file join $portbuildpath work]]
2319}
2320
2321proc macports::getportworkpath_from_portdir {portpath {portname {}}} {
2322    return [macports::getportworkpath_from_buildpath [macports::getportbuildpath $portpath $portname]]
2323}
2324
2325proc macports::getindex {source} {
2326    # Special case file:// sources
2327    if {[macports::getprotocol $source] eq "file"} {
2328        return [file join [macports::getportdir $source] PortIndex]
2329    }
2330
2331    return [file join [macports::getsourcepath $source] PortIndex]
2332}
2333
2334# macports::GetVCSUpdateCmd --
2335#
2336# Determine whether the given directory is associated with a repository
2337# for a supported version control system. If so, return a list
2338# containing two strings:
2339#
2340#   1) The human-readable name of the version control system.
2341#   2) A command that will update the repository's working tree to the
2342#      latest commit/changeset/revision/whatever. This command should
2343#      work properly from any working directory, although it doesn't
2344#      have to worry about cleaning up after itself (restoring the
2345#      environment, changing back to the initial directory, etc.).
2346#
2347# If the directory is not associated with any supported system, return
2348# an empty list.
2349#
2350proc macports::GetVCSUpdateCmd portDir {
2351
2352    set oldPWD [pwd]
2353    cd $portDir
2354
2355    # Subversion
2356    if {![catch {macports::findBinary svn} svn] &&
2357        ([file exists .svn] ||
2358         ![catch {exec $svn info >/dev/null 2>@1}])
2359    } then {
2360        return [list Subversion "$svn update --non-interactive $portDir"]
2361    }
2362
2363    # Git
2364    if {![catch {macports::findBinary git} git] &&
2365        ![catch {exec $git rev-parse --is-inside-work-tree}]
2366    } then {
2367        if {![catch {exec $git config --local --get svn-remote.svn.url}]} {
2368            # git-svn repository
2369            return [list git-svn "cd $portDir && $git svn rebase || true"]
2370        }
2371        # regular git repository
2372        return [list Git "cd $portDir && $git pull --rebase || true"]
2373    }
2374
2375    # Add new VCSes here!
2376
2377    cd $oldPWD
2378    return [list]
2379}
2380
2381# macports::UpdateVCS --
2382#
2383# Execute the given command in a shell. If called with superuser
2384# privileges, execute the command as the user/group that owns the given
2385# directory, restoring privileges before returning.
2386#
2387# This proc could probably be generalized and used elsewhere.
2388#
2389proc macports::UpdateVCS {cmd portDir} {
2390    if {[getuid] == 0} {
2391        # Must change egid before dropping root euid.
2392        set oldEGID [getegid]
2393        set newEGID [name_to_gid [file attributes $portDir -group]]
2394        setegid $newEGID
2395        ui_debug "Changed effective group ID from $oldEGID to $newEGID"
2396        set oldEUID [geteuid]
2397        set newEUID [name_to_uid [file attributes $portDir -owner]]
2398        seteuid $newEUID
2399        ui_debug "Changed effective user ID from $oldEUID to $newEUID"
2400    }
2401    ui_debug $cmd
2402    catch {system $cmd} result options
2403    if {[getuid] == 0} {
2404        seteuid $oldEUID
2405        ui_debug "Changed effective user ID from $newEUID to $oldEUID"
2406        setegid $oldEGID
2407        ui_debug "Changed effective group ID from $newEGID to $oldEGID"
2408    }
2409    return -options $options $result
2410}
2411
2412proc mportsync {{optionslist {}}} {
2413    global macports::sources macports::portdbpath macports::rsync_options \
2414           tcl_platform macports::portverbose macports::autoconf::rsync_path \
2415           macports::autoconf::tar_path macports::autoconf::openssl_path \
2416           macports::ui_options
2417    array set options $optionslist
2418    if {[info exists options(no_reindex)]} {
2419        upvar $options(needed_portindex_var) any_needed_portindex
2420    }
2421
2422    set numfailed 0
2423
2424    ui_msg "$macports::ui_prefix Updating the ports tree"
2425    foreach source $sources {
2426        set flags [lrange $source 1 end]
2427        set source [lindex $source 0]
2428        if {"nosync" in $flags} {
2429            ui_debug "Skipping $source"
2430            continue
2431        }
2432        set needs_portindex false
2433        ui_info "Synchronizing local ports tree from $source"
2434        switch -regexp -- [macports::getprotocol $source] {
2435            {^file$} {
2436                set portdir [macports::getportdir $source]
2437                if {[catch {macports::GetVCSUpdateCmd $portdir} repoInfo]} {
2438                    ui_debug $::errorInfo
2439                    ui_info "Could not access contents of $portdir"
2440                    incr numfailed
2441                    continue
2442                }
2443                if {[llength $repoInfo]} {
2444                    lassign $repoInfo vcs cmd
2445                    if {[catch {macports::UpdateVCS $cmd $portdir}]} {
2446                        ui_debug $::errorInfo
2447                        ui_info "Syncing local $vcs ports tree failed"
2448                        incr numfailed
2449                        continue
2450                    }
2451                }
2452                set needs_portindex true
2453            }
2454            {^rsync$} {
2455                # Where to, boss?
2456                set indexfile [macports::getindex $source]
2457                set destdir [file dirname $indexfile]
2458                set is_tarball [_source_is_snapshot $source]
2459                file mkdir $destdir
2460
2461                if {$is_tarball} {
2462                    set exclude_option {}
2463                    # need to do a few things before replacing the ports tree in this case
2464                    set destdir [file dirname $destdir]
2465                } else {
2466                    # Keep rsync happy with a trailing slash
2467                    if {[string index $source end] ne "/"} {
2468                        append source /
2469                    }
2470                    # don't sync PortIndex yet; we grab the platform specific one afterwards
2471                    set exclude_option '--exclude=/PortIndex*'
2472                }
2473                # Do rsync fetch
2474                set rsync_commandline "$macports::autoconf::rsync_path $rsync_options $exclude_option $source $destdir"
2475                ui_debug $rsync_commandline
2476                if {[catch {system $rsync_commandline}]} {
2477                    ui_error "Synchronization of the local ports tree failed doing rsync"
2478                    incr numfailed
2479                    continue
2480                }
2481
2482                if {$is_tarball} {
2483                    # verify signature for tarball
2484                    global macports::archivefetch_pubkeys
2485                    set rsync_commandline "$macports::autoconf::rsync_path $rsync_options $exclude_option ${source}.rmd160 $destdir"
2486                    ui_debug $rsync_commandline
2487                    if {[catch {system $rsync_commandline}]} {
2488                        ui_error "Synchronization of the ports tree signature failed doing rsync"
2489                        incr numfailed
2490                        continue
2491                    }
2492                    set tarball ${destdir}/[file tail $source]
2493                    set signature ${tarball}.rmd160
2494                    set openssl [macports::findBinary openssl $macports::autoconf::openssl_path]
2495                    set verified 0
2496                    foreach pubkey $macports::archivefetch_pubkeys {
2497                        if {![catch {exec $openssl dgst -ripemd160 -verify $pubkey -signature $signature $tarball} result]} {
2498                            set verified 1
2499                            ui_debug "successful verification with key $pubkey"
2500                            break
2501                        } else {
2502                            ui_debug "failed verification with key $pubkey"
2503                            ui_debug "openssl output: $result"
2504                        }
2505                    }
2506                    if {!$verified} {
2507                        ui_error "Failed to verify signature for ports tree!"
2508                        incr numfailed
2509                        continue
2510                    }
2511
2512                    # extract tarball and move into place
2513                    set tar [macports::findBinary tar $macports::autoconf::tar_path]
2514                    file mkdir ${destdir}/tmp
2515                    set tar_cmd "$tar -C ${destdir}/tmp -xf $tarball"
2516                    ui_debug $tar_cmd
2517                    if {[catch {system $tar_cmd}]} {
2518                        ui_error "Failed to extract ports tree from tarball!"
2519                        incr numfailed
2520                        continue
2521                    }
2522                    # save the local PortIndex data
2523                    if {[file isfile $indexfile]} {
2524                        file copy -force $indexfile ${destdir}/
2525                        file rename -force $indexfile ${destdir}/tmp/ports/
2526                        if {[file isfile ${indexfile}.quick]} {
2527                            file rename -force ${indexfile}.quick ${destdir}/tmp/ports/
2528                        }
2529                    }
2530                    file delete -force ${destdir}/ports
2531                    file rename ${destdir}/tmp/ports ${destdir}/ports
2532                    file delete -force ${destdir}/tmp
2533                }
2534
2535                set needs_portindex true
2536                # now sync the index if the local file is missing or older than a day
2537                if {![file isfile $indexfile] || [clock seconds] - [file mtime $indexfile] > 86400
2538                      || [info exists options(no_reindex)]} {
2539                    if {$is_tarball} {
2540                        # chop ports.tar off the end
2541                        set index_source [string range $source 0 end-[string length [file tail $source]]]
2542                    } else {
2543                        set index_source $source
2544                    }
2545                    set remote_indexfile "${index_source}PortIndex_${macports::os_platform}_${macports::os_major}_${macports::os_arch}/PortIndex"
2546                    set rsync_commandline "$macports::autoconf::rsync_path $rsync_options $remote_indexfile $destdir"
2547                    ui_debug $rsync_commandline
2548                    if {[catch {system $rsync_commandline}]} {
2549                        ui_debug "Synchronization of the PortIndex failed doing rsync"
2550                    } else {
2551                        set ok 1
2552                        set needs_portindex false
2553                        if {$is_tarball} {
2554                            set ok 0
2555                            set needs_portindex true
2556                            # verify signature for PortIndex
2557                            set rsync_commandline "$macports::autoconf::rsync_path $rsync_options ${remote_indexfile}.rmd160 $destdir"
2558                            ui_debug $rsync_commandline
2559                            if {![catch {system $rsync_commandline}]} {
2560                                foreach pubkey $macports::archivefetch_pubkeys {
2561                                    if {![catch {exec $openssl dgst -ripemd160 -verify $pubkey -signature ${destdir}/PortIndex.rmd160 ${destdir}/PortIndex} result]} {
2562                                        set ok 1
2563                                        set needs_portindex false
2564                                        ui_debug "successful verification with key $pubkey"
2565                                        break
2566                                    } else {
2567                                        ui_debug "failed verification with key $pubkey"
2568                                        ui_debug "openssl output: $result"
2569                                    }
2570                                }
2571                                if {$ok} {
2572                                    # move PortIndex into place
2573                                    file rename -force ${destdir}/PortIndex ${destdir}/ports/
2574                                }
2575                            }
2576                        }
2577                        if {$ok} {
2578                            mports_generate_quickindex $indexfile
2579                        }
2580                    }
2581                }
2582                if {[catch {system "chmod -R a+r \"$destdir\""}]} {
2583                    ui_warn "Setting world read permissions on parts of the ports tree failed, need root?"
2584                }
2585            }
2586            {^https?$|^ftp$} {
2587                if {![_source_is_snapshot $source filename extension]} {
2588                    ui_error "Synchronization using http, https and ftp only supported with tarballs."
2589                    ui_error "The source ${source} doesn't seem to point to a tarball."
2590                    ui_error "Please switch to a different sync protocol (e.g. rsync) in your sources.conf"
2591                    ui_error "Remove the line mentioned above from your sources.conf to silence this error."
2592                    incr numfailed
2593                    continue
2594                }
2595                # sync a daily port snapshot tarball
2596                set indexfile [macports::getindex $source]
2597                set destdir [file dirname $indexfile]
2598                set tarpath [file join [file normalize [file join $destdir ..]] $filename]
2599
2600                set updated 1
2601                if {[file isdirectory $destdir]} {
2602                    set moddate [file mtime $destdir]
2603                    if {[catch {set updated [curl isnewer $source $moddate]} error]} {
2604                        ui_warn "Cannot check if $source was updated, ($error)"
2605                    }
2606                }
2607
2608                if {(![info exists options(ports_force)] || !$options(ports_force)) && $updated <= 0} {
2609                    ui_info "No updates for $source"
2610                    continue
2611                }
2612
2613                file mkdir $destdir
2614
2615                set progressflag {}
2616                if {$macports::portverbose} {
2617                    set progressflag "--progress builtin"
2618                    set verboseflag "-v"
2619                } elseif {[info exists macports::ui_options(progress_download)]} {
2620                    set progressflag "--progress ${macports::ui_options(progress_download)}"
2621                    set verboseflag ""
2622                }
2623
2624                try {
2625                    curl fetch {*}$progressflag $source $tarpath
2626                } catch {{POSIX SIG SIGINT} eCode eMessage} {
2627                    throw
2628                } catch {{POSIX SIG SIGTERM} eCode eMessage} {
2629                    throw
2630                } catch {{*} eCode eMessage} {
2631                    ui_error [msgcat::mc "Fetching %s failed: %s" $source $eMessage]
2632                    incr numfailed
2633                    continue
2634                }
2635
2636                set extflag {}
2637                switch -- $extension {
2638                    {tar.gz} {
2639                        set extflag -z
2640                    }
2641                    {tar.bz2} {
2642                        set extflag -j
2643                    }
2644                }
2645
2646                set tar [macports::findBinary tar $macports::autoconf::tar_path]
2647                if {[catch {system "cd ${destdir}/.. && $tar $verboseflag $extflag -xf $filename"} error]} {
2648                    ui_error "Extracting $source failed ($error)"
2649                    incr numfailed
2650                    continue
2651                }
2652
2653                if {[catch {system "chmod -R a+r \"$destdir\""}]} {
2654                    ui_warn "Setting world read permissions on parts of the ports tree failed, need root?"
2655                }
2656
2657                set platindex "PortIndex_${macports::os_platform}_${macports::os_major}_${macports::os_arch}/PortIndex"
2658                if {[file isfile ${destdir}/$platindex] && [file isfile ${destdir}/${platindex}.quick]} {
2659                    file rename -force ${destdir}/$platindex ${destdir}/${platindex}.quick $destdir
2660                }
2661
2662                file delete $tarpath
2663            }
2664            {^mports$} {
2665                ui_error "Synchronization using the mports protocol no longer supported."
2666                ui_error "Please switch to a different sync protocol (e.g. rsync) in your sources.conf"
2667                ui_error "Remove the line starting with mports:// from your sources.conf to silence this error."
2668                incr numfailed
2669                continue
2670            }
2671            default {
2672                ui_warn "Unknown synchronization protocol for $source"
2673            }
2674        }
2675
2676        if {$needs_portindex} {
2677            set any_needed_portindex true
2678            if {![info exists options(no_reindex)]} {
2679                global macports::prefix
2680                set indexdir [file dirname [macports::getindex $source]]
2681                if {[catch {system "${macports::prefix}/bin/portindex $indexdir"}]} {
2682                    ui_error "updating PortIndex for $source failed"
2683                }
2684            }
2685        }
2686    }
2687
2688    # refresh the quick index if necessary (batch or interactive run)
2689    if {[info exists macports::ui_options(ports_commandfiles)]} {
2690        _mports_load_quickindex
2691    }
2692
2693    if {$numfailed == 1} {
2694        return -code error "Synchronization of 1 source failed"
2695    }
2696    if {$numfailed >= 2} {
2697        return -code error "Synchronization of $numfailed sources failed"
2698    }
2699}
2700
2701##
2702# Searches all configured port sources for a given pattern in a given field
2703# using a given matching style and optional case-sensitivity.
2704#
2705# @param pattern pattern to search for; will be interpreted according to the \a
2706#                matchstyle parameter
2707# @param case_sensitive "yes", if a case-sensitive search should be performed,
2708#                       "no" otherwise. Defaults to "yes".
2709# @param matchstyle One of the values \c exact, \c glob and \c regexp, where \c
2710#                   exact performs a standard string comparison, \c glob
2711#                   performs Tcl string matching using <tt>[string match]</tt>
2712#                   and \c regexp interprets \a pattern as a regular
2713#                   expression.
2714# @param field name of the field to apply \a pattern to. Must be one of the
2715#              fields available in the used portindex. The portindex currently
2716#              contains
2717#                \li \c name (the default)
2718#                \li \c homepage
2719#                \li \c description
2720#                \li \c long_description
2721#                \li \c license
2722#                \li \c categories
2723#                \li \c platforms
2724#                \li \c maintainers
2725#                \li \c variants
2726#                \li \c portdir
2727#                \li all \c depends_* values
2728#                \li \c epoch
2729#                \li \c version
2730#                \li \c revision
2731#                \li \c replaced_by
2732#                \li \c installs_libs
2733# @return a list where each even index (starting with 0) contains the name of
2734#         a matching port. Each entry at an odd index is followed by its
2735#         corresponding line from the portindex, which can be passed to
2736#         <tt>array set</tt>. The whole return value can also be passed to
2737#         <tt>array set</tt> to create an associate array where the port names
2738#         are the keys and the lines from portindex are the values.
2739proc mportsearch {pattern {case_sensitive yes} {matchstyle regexp} {field name}} {
2740    global macports::sources
2741    set matches [list]
2742    set easy [expr {$field eq "name"}]
2743
2744    set found 0
2745    foreach source $sources {
2746        set source [lindex $source 0]
2747        set protocol [macports::getprotocol $source]
2748        if {[catch {set fd [open [macports::getindex $source] r]} result]} {
2749            ui_warn "Can't open index file for source: $source"
2750        } else {
2751            try {
2752                incr found 1
2753                while {[gets $fd line] >= 0} {
2754                    array unset portinfo
2755                    set name [lindex $line 0]
2756                    set len  [lindex $line 1]
2757                    set line [read $fd $len]
2758
2759                    if {$easy} {
2760                        set target $name
2761                    } else {
2762                        array set portinfo $line
2763                        if {![info exists portinfo($field)]} {
2764                            continue
2765                        }
2766                        set target $portinfo($field)
2767                    }
2768
2769                    switch -- $matchstyle {
2770                        exact {
2771                            if {$case_sensitive} {
2772                                set compres [string compare $pattern $target]
2773                            } else {
2774                                set compres [string compare -nocase $pattern $target]
2775                            }
2776                            set matchres [expr {0 == $compres}]
2777                        }
2778                        glob {
2779                            if {$case_sensitive} {
2780                                set matchres [string match $pattern $target]
2781                            } else {
2782                                set matchres [string match -nocase $pattern $target]
2783                            }
2784                        }
2785                        regexp {
2786                            if {$case_sensitive} {
2787                                set matchres [regexp -- $pattern $target]
2788                            } else {
2789                                set matchres [regexp -nocase -- $pattern $target]
2790                            }
2791                        }
2792                        default {
2793                            return -code error "mportsearch: Unsupported matching style: ${matchstyle}."
2794                        }
2795                    }
2796
2797                    if {$matchres == 1} {
2798                        if {$easy} {
2799                            array set portinfo $line
2800                        }
2801                        switch -- $protocol {
2802                            rsync {
2803                                # Rsync files are local
2804                                set source_url file://[macports::getsourcepath $source]
2805                            }
2806                            https -
2807                            http -
2808                            ftp {
2809                                # daily snapshot tarball
2810                                set source_url file://[macports::getsourcepath $source]
2811                            }
2812                            default {
2813                                set source_url $source
2814                            }
2815                        }
2816                        if {[info exists portinfo(portdir)]} {
2817                            set porturl ${source_url}/$portinfo(portdir)
2818                            lappend line porturl $porturl
2819                            ui_debug "Found port in $porturl"
2820                        } else {
2821                            ui_debug "Found port info: $line"
2822                        }
2823                        lappend matches $name
2824                        lappend matches $line
2825                    }
2826                }
2827            } catch * {
2828                ui_warn "It looks like your PortIndex file for $source may be corrupt."
2829                throw
2830            } finally {
2831                close $fd
2832            }
2833        }
2834    }
2835    if {!$found} {
2836        return -code error "No index(es) found! Have you synced your port definitions? Try running 'port selfupdate'."
2837    }
2838
2839    return $matches
2840}
2841
2842##
2843# Returns the PortInfo for a single named port. The info comes from the
2844# PortIndex, and name matching is case-insensitive. Unlike mportsearch, only
2845# the first match is returned, but the return format is otherwise identical.
2846# The advantage is that mportlookup is usually much faster than mportsearch,
2847# due to the use of the quick index, which is a name-based index into the
2848# PortIndex.
2849#
2850# @param name name of the port to look up. Returns the first match while
2851#             traversing the sources in-order.
2852# @return associative array in list form where the first field is the port name
2853#         and the second field is the line from PortIndex containing the port
2854#         info. See the return value of mportsearch().
2855# @see mportsearch()
2856proc mportlookup {name} {
2857    global macports::portdbpath macports::sources macports::quick_index
2858
2859    set sourceno 0
2860    set matches [list]
2861    foreach source $sources {
2862        set source [lindex $source 0]
2863        set protocol [macports::getprotocol $source]
2864        if {![info exists quick_index(${sourceno},[string tolower $name])]} {
2865            # no entry in this source, advance to next source
2866            incr sourceno 1
2867            continue
2868        }
2869        # The quick index is keyed on the port name, and provides the offset in
2870        # the main PortIndex where the given port's PortInfo line can be found.
2871        set offset $quick_index(${sourceno},[string tolower $name])
2872        incr sourceno 1
2873        if {[catch {set fd [open [macports::getindex $source] r]} result]} {
2874            ui_warn "Can't open index file for source: $source"
2875        } else {
2876            try {
2877                seek $fd $offset
2878                gets $fd line
2879                set name [lindex $line 0]
2880                set len  [lindex $line 1]
2881                set line [read $fd $len]
2882
2883                array set portinfo $line
2884
2885                switch -- $protocol {
2886                    rsync {
2887                        set source_url file://[macports::getsourcepath $source]
2888                    }
2889                    https -
2890                    http -
2891                    ftp {
2892                        set source_url file://[macports::getsourcepath $source]
2893                    }
2894                    default {
2895                        set source_url $source
2896                    }
2897                }
2898                if {[info exists portinfo(portdir)]} {
2899                    lappend line porturl ${source_url}/$portinfo(portdir)
2900                }
2901                lappend matches $name
2902                lappend matches $line
2903                close $fd
2904                set fd -1
2905            } catch * {
2906                ui_warn "It looks like your PortIndex file for $source may be corrupt."
2907            } finally {
2908                if {$fd != -1} {
2909                    close $fd
2910                }
2911            }
2912            if {[llength $matches] > 0} {
2913                # if we have a match, exit. If we don't, continue with the next
2914                # source.
2915                break
2916            }
2917        }
2918    }
2919
2920    return $matches
2921}
2922
2923##
2924# Returns all ports in the indices. Faster than 'mportsearch .*' because of the
2925# lack of matching.
2926#
2927# @return associative array in list form where the first field is the port name
2928#         and the second field is the line from PortIndex containing the port
2929#         info. See the return value of mportsearch().
2930# @see mportsearch()
2931proc mportlistall {} {
2932    global macports::sources
2933    set matches [list]
2934
2935    set found 0
2936    foreach source $sources {
2937        set source [lindex $source 0]
2938        set protocol [macports::getprotocol $source]
2939        if {![catch {set fd [open [macports::getindex $source] r]} result]} {
2940            try {
2941                incr found 1
2942                while {[gets $fd line] >= 0} {
2943                    array unset portinfo
2944                    set name [lindex $line 0]
2945                    set len  [lindex $line 1]
2946                    set line [read $fd $len]
2947
2948                    array set portinfo $line
2949
2950                    switch -- $protocol {
2951                        rsync {
2952                            set source_url file://[macports::getsourcepath $source]
2953                        }
2954                        https -
2955                        http -
2956                        ftp {
2957                            set source_url file://[macports::getsourcepath $source]
2958                        }
2959                        default {
2960                            set source_url $source
2961                        }
2962                    }
2963                    if {[info exists portinfo(portdir)]} {
2964                        lappend line porturl ${source_url}/$portinfo(portdir)
2965                    }
2966                    lappend matches $name $line
2967                }
2968            } catch * {
2969                ui_warn "It looks like your PortIndex file for $source may be corrupt."
2970                throw
2971            } finally {
2972                close $fd
2973            }
2974        } else {
2975            ui_warn "Can't open index file for source: $source"
2976        }
2977    }
2978    if {!$found} {
2979        return -code error "No index(es) found! Have you synced your port definitions? Try running 'port selfupdate'."
2980    }
2981
2982    return $matches
2983}
2984
2985##
2986# Loads PortIndex.quick from each source into the quick_index, generating it
2987# first if necessary. Private API of macports1.0, do not use this from outside
2988# macports1.0.
2989proc _mports_load_quickindex {} {
2990    global macports::sources macports::quick_index
2991
2992    unset -nocomplain macports::quick_index
2993
2994    set sourceno 0
2995    foreach source $sources {
2996        unset -nocomplain quicklist
2997        # chop off any tags
2998        set source [lindex $source 0]
2999        set index [macports::getindex $source]
3000        if {![file exists $index]} {
3001            incr sourceno
3002            continue
3003        }
3004        if {![file exists ${index}.quick]} {
3005            ui_warn "No quick index file found, attempting to generate one for source: $source"
3006            if {[catch {set quicklist [mports_generate_quickindex $index]}]} {
3007                incr sourceno
3008                continue
3009            }
3010        }
3011        # only need to read the quick index file if we didn't just update it
3012        if {![info exists quicklist]} {
3013            if {[catch {set fd [open ${index}.quick r]} result]} {
3014                ui_warn "Can't open quick index file for source: $source"
3015                incr sourceno
3016                continue
3017            } else {
3018                set quicklist [read $fd]
3019                close $fd
3020            }
3021        }
3022        foreach entry [split $quicklist \n] {
3023            set quick_index(${sourceno},[lindex $entry 0]) [lindex $entry 1]
3024        }
3025        incr sourceno 1
3026    }
3027    if {!$sourceno} {
3028        ui_warn "No index(es) found! Have you synced your port definitions? Try running 'port selfupdate'."
3029    }
3030}
3031
3032##
3033# Generates a PortIndex.quick file from a PortIndex by using the name field as
3034# key. This allows fast indexing into the PortIndex when using the port name as
3035# key.
3036#
3037# @param index the PortIndex file to create the index for. The resulting quick
3038#              index will be in a file named like \a index, but with ".quick"
3039#              appended.
3040# @return a list of entries written to the quick index file in the same format
3041#         if the file would just have been written.
3042# @throws if the given \a index cannot be opened, the output file cannot be
3043#         opened, an error occurs while using the PortIndex (e.g., because it
3044#         is corrupt), or the quick index generation failed for some other
3045#         reason.
3046proc mports_generate_quickindex {index} {
3047    if {[catch {set indexfd [open $index r]} result] || [catch {set quickfd [open ${index}.quick w]} result]} {
3048        ui_warn "Can't open index file: $index"
3049        return -code error
3050    } else {
3051        try {
3052            set offset [tell $indexfd]
3053            set quicklist {}
3054            while {[gets $indexfd line] >= 0} {
3055                if {[llength $line] != 2} {
3056                    continue
3057                }
3058                set name [lindex $line 0]
3059                append quicklist "[string tolower $name] $offset\n"
3060
3061                set len [lindex $line 1]
3062                read $indexfd $len
3063                set offset [tell $indexfd]
3064            }
3065            puts -nonewline $quickfd $quicklist
3066        } catch {{POSIX SIG SIGINT} eCode eMessage} {
3067            throw
3068        } catch {{POSIX SIG SIGTERM} eCode eMessage} {
3069            throw
3070        } catch {{*} eCode eMessage} {
3071            ui_warn "It looks like your PortIndex file $index may be corrupt."
3072            throw
3073        } finally {
3074            close $indexfd
3075            close $quickfd
3076        }
3077    }
3078    if {[info exists quicklist]} {
3079        return $quicklist
3080    } else {
3081        ui_warn "Failed to generate quick index for: $index"
3082        return -code error
3083    }
3084}
3085
3086proc mportinfo {mport} {
3087    set workername [ditem_key $mport workername]
3088    return [$workername eval array get ::PortInfo]
3089}
3090
3091proc mportclose {mport} {
3092    global macports::open_mports macports::extracted_portdirs
3093    set refcnt [ditem_key $mport refcnt]
3094    incr refcnt -1
3095    ditem_key $mport refcnt $refcnt
3096    if {$refcnt == 0} {
3097        dlist_delete macports::open_mports $mport
3098        set workername [ditem_key $mport workername]
3099        # the hack in _mportexec might have already deleted the worker
3100        if {[interp exists $workername]} {
3101            interp delete $workername
3102        }
3103        set porturl [ditem_key $mport porturl]
3104        if {[info exists macports::extracted_portdirs($porturl)]} {
3105            # TODO port.tcl calls mportopen multiple times on the same port to
3106            # determine a number of attributes and will close the port after
3107            # each call. $macports::extracted_portdirs($porturl) will however
3108            # stay set, which means it will not be extracted twice. We could
3109            # (1) unset $macports::extracted_portdirs($porturl), which would
3110            # lead to downloading the port multiple times, or (2) fix the
3111            # port.tcl code to delay mportclose until the end.
3112            #ui_debug "Removing temporary port directory $macports::extracted_portdirs($porturl)"
3113            #file delete -force $macports::extracted_portdirs($porturl)
3114        }
3115        ditem_delete $mport
3116    }
3117}
3118
3119##### Private Depspec API #####
3120# This API should be considered work in progress and subject to change without notice.
3121##### "
3122
3123# _mportkey
3124# - returns a variable from the port's interpreter
3125
3126proc _mportkey {mport key} {
3127    set workername [ditem_key $mport workername]
3128    return [$workername eval [list set $key]]
3129}
3130
3131# mportdepends builds the list of mports which the given port depends on.
3132# This list is added to $mport.
3133# This list actually depends on the target.
3134# This method can optionally recurse through the dependencies, looking for
3135#   dependencies of dependencies.
3136# This method can optionally cut the search when ports are already installed or
3137#   the dependencies are satisfied.
3138#
3139# mport -> mport item
3140# target -> target to consider the dependency for
3141# recurseDeps -> if the search should be recursive
3142# skipSatisfied -> cut the search tree when encountering installed/satisfied
3143#                  dependencies ports.
3144# accDeps -> accumulator for recursive calls
3145# return 0 if everything was ok, an non zero integer otherwise.
3146proc mportdepends {mport {target {}} {recurseDeps 1} {skipSatisfied 1} {accDeps 0}} {
3147
3148    array set portinfo [mportinfo $mport]
3149    if {$accDeps} {
3150        upvar port_seen port_seen
3151    } else {
3152        array set port_seen {}
3153    }
3154
3155    # progress indicator
3156    if {![macports::ui_isset ports_debug]} {
3157        ui_info -nonewline .
3158        flush stdout
3159    }
3160
3161    if {$target in {{} install activate}} {
3162        if {[catch {_mporterrorifconflictsinstalled $mport}]} {
3163            return 1
3164        }
3165    }
3166
3167    set workername [ditem_key $mport workername]
3168    set deptypes [macports::_deptypes_for_target $target $workername]
3169
3170    set depPorts {}
3171    if {[llength $deptypes] > 0} {
3172        array set optionsarray [ditem_key $mport options]
3173        # avoid propagating requested flag from parent
3174        unset -nocomplain optionsarray(ports_requested)
3175        # subport will be different for deps
3176        unset -nocomplain optionsarray(subport)
3177        set options [array get optionsarray]
3178        set variations [ditem_key $mport variations]
3179        set required_archs [$workername eval get_canonical_archs]
3180        set depends_skip_archcheck [_mportkey $mport depends_skip_archcheck]
3181    }
3182
3183    # Process the dependencies for each of the deptypes
3184    foreach deptype $deptypes {
3185        if {![info exists portinfo($deptype)]} {
3186            continue
3187        }
3188        foreach depspec $portinfo($deptype) {
3189            # get the portname that satisfies the depspec
3190            set dep_portname [$workername eval _get_dep_port $depspec]
3191            # skip port/archs combos we've already seen, and ones with the same port but less archs than ones we've seen (or noarch)
3192            set seenkey ${dep_portname},[join $required_archs ,]
3193            set seen 0
3194            if {[info exists port_seen($seenkey)]} {
3195                set seen 1
3196            } else {
3197                set prev_seenkeys [array names port_seen ${dep_portname},*]
3198                set nrequired [llength $required_archs]
3199                foreach key $prev_seenkeys {
3200                    set key_archs [lrange [split $key ,] 1 end]
3201                    if {$key_archs eq "noarch" || $required_archs eq "noarch" || [llength $key_archs] > $nrequired} {
3202                        set seen 1
3203                        set seenkey $key
3204                        break
3205                    }
3206                }
3207            }
3208            if {$seen} {
3209                if {$port_seen($seenkey) != 0} {
3210                    # nonzero means the dep is not satisfied, so we have to record it
3211                    ditem_append_unique $mport requires $port_seen($seenkey)
3212                }
3213                continue
3214            }
3215
3216            # Is that dependency satisfied or this port installed?
3217            # If we don't skip or if it is not, add it to the list.
3218            set present [_mportispresent $mport $depspec]
3219
3220            if {!$skipSatisfied && $dep_portname eq ""} {
3221                set dep_portname [lindex [split $depspec :] end]
3222            }
3223
3224            set check_archs 0
3225            if {$dep_portname ne "" && $deptype ni {depends_fetch depends_extract}
3226                && $dep_portname ni $depends_skip_archcheck} {
3227                set check_archs 1
3228            }
3229
3230            # need to open the portfile even if the dep is installed if it doesn't have the right archs
3231            set parse 0
3232            if {!$skipSatisfied || !$present || ($check_archs && ![macports::_active_supports_archs $dep_portname $required_archs])} {
3233                set parse 1
3234            }
3235            if {$parse} {
3236                # Find the porturl
3237                if {[catch {set res [mportlookup $dep_portname]} error]} {
3238                    global errorInfo
3239                    ui_msg {}
3240                    ui_debug $errorInfo
3241                    ui_error "Internal error: port lookup failed: $error"
3242                    return 1
3243                }
3244
3245                array unset dep_portinfo
3246                array set dep_portinfo [lindex $res 1]
3247                if {![info exists dep_portinfo(porturl)]} {
3248                    if {![macports::ui_isset ports_debug]} {
3249                        ui_msg {}
3250                    }
3251                    ui_error "Dependency '$dep_portname' not found."
3252                    return 1
3253                } elseif {[info exists dep_portinfo(installs_libs)] && !$dep_portinfo(installs_libs)} {
3254                    set check_archs 0
3255                    if {$skipSatisfied && $present} {
3256                        set parse 0
3257                    }
3258                }
3259
3260                if {$parse} {
3261                    set dep_options $options
3262                    lappend dep_options subport $dep_portinfo(name)
3263                    # Figure out the depport. Check the open_mports list first, since
3264                    # we potentially leak mport references if we mportopen each time,
3265                    # because mportexec only closes each open mport once.
3266                    set depport [dlist_match_multi $macports::open_mports [list porturl $dep_portinfo(porturl) options $dep_options]]
3267
3268                    if {$depport eq ""} {
3269                        # We haven't opened this one yet.
3270                        set depport [mportopen $dep_portinfo(porturl) $dep_options $variations]
3271                    }
3272                }
3273            }
3274
3275            # check archs
3276            if {$parse && $check_archs
3277                && ![macports::_mport_supports_archs $depport $required_archs]} {
3278
3279                set supported_archs [_mportkey $depport supported_archs]
3280                array unset variation_array
3281                array set variation_array [[ditem_key $depport workername] eval "array get variations"]
3282                mportclose $depport
3283                set arch_mismatch 1
3284                set has_universal 0
3285                if {[info exists dep_portinfo(variants)] && {universal} in $dep_portinfo(variants)} {
3286                    # a universal variant is offered
3287                    set has_universal 1
3288                    if {![info exists variation_array(universal)] || $variation_array(universal) ne "+"} {
3289                        set variation_array(universal) +
3290                        # try again with +universal
3291                        set depport [mportopen $dep_portinfo(porturl) $dep_options [array get variation_array]]
3292                        if {[macports::_mport_supports_archs $depport $required_archs]} {
3293                            set arch_mismatch 0
3294                        }
3295                    }
3296                }
3297                if {$arch_mismatch} {
3298                    macports::_explain_arch_mismatch [_mportkey $mport subport] $dep_portname $required_archs $supported_archs $has_universal
3299                    return 1
3300                }
3301            }
3302
3303            if {$parse} {
3304                if {$recurseDeps} {
3305                    # Add to the list we need to recurse on.
3306                    lappend depPorts $depport
3307                }
3308
3309                # Append the sub-port's provides to the port's requirements list.
3310                set depport_provides [ditem_key $depport provides]
3311                ditem_append_unique $mport requires $depport_provides
3312                # record actual archs we ended up getting
3313                set port_seen(${dep_portname},[join [macports::_mport_archs $depport] ,]) $depport_provides
3314            } elseif {$present && $dep_portname ne ""} {
3315                # record actual installed archs
3316                set port_seen(${dep_portname},[join [macports::_active_archs $dep_portname] ,]) 0
3317            }
3318        }
3319    }
3320
3321    # Loop on the depports.
3322    if {$recurseDeps} {
3323        # Dep ports should be installed (all dependencies must be satisfied).
3324        foreach depport $depPorts {
3325            # Any of these may have been closed by a previous recursive call
3326            # and replaced by a universal version. This is fine, just skip.
3327            if {[ditem_key $depport] ne ""} {
3328                set res [mportdepends $depport {} $recurseDeps $skipSatisfied 1]
3329                if {$res != 0} {
3330                    return $res
3331                }
3332            }
3333        }
3334    }
3335
3336    return 0
3337}
3338
3339# check if the given mport can support dependents with the given archs
3340proc macports::_mport_supports_archs {mport required_archs} {
3341    if {$required_archs eq "noarch"} {
3342        return 1
3343    }
3344    set provided_archs [_mport_archs $mport]
3345    if {$provided_archs eq "noarch"} {
3346        return 1
3347    }
3348    foreach arch $required_archs {
3349        if {$arch ni $provided_archs} {
3350            return 0
3351        }
3352    }
3353    return 1
3354}
3355
3356# return the archs of the given mport
3357proc macports::_mport_archs {mport} {
3358    set workername [ditem_key $mport workername]
3359    return [$workername eval get_canonical_archs]
3360}
3361
3362# check if the active version of a port supports the given archs
3363proc macports::_active_supports_archs {portname required_archs} {
3364    if {$required_archs eq "noarch"} {
3365        return 1
3366    }
3367    if {[catch {registry::active $portname}]} {
3368        return 0
3369    }
3370    set provided_archs [_active_archs $portname]
3371    if {$provided_archs eq "noarch" || $provided_archs eq "" || $provided_archs == 0} {
3372        return 1
3373    }
3374    foreach arch $required_archs {
3375        if {$arch ni $provided_archs} {
3376            return 0
3377        }
3378    }
3379    return 1
3380}
3381
3382# get the archs for a given active port
3383proc macports::_active_archs {portname} {
3384    if {[catch {set ilist [registry::active $portname]}]} {
3385        return {}
3386    }
3387    set i [lindex $ilist 0]
3388    set regref [registry::open_entry $portname [lindex $i 1] [lindex $i 2] [lindex $i 3] [lindex $i 5]]
3389    return [registry::property_retrieve $regref archs]
3390}
3391
3392# print an error message explaining why a port's archs are not provided by a dependency
3393proc macports::_explain_arch_mismatch {port dep required_archs supported_archs has_universal} {
3394    global macports::universal_archs
3395    if {![macports::ui_isset ports_debug]} {
3396        ui_msg {}
3397    }
3398
3399    set s [expr {[llength $required_archs] == 1 ? "" : "s"}]
3400
3401    ui_error "Cannot install $port for the arch${s} '$required_archs' because"
3402    if {$supported_archs ne ""} {
3403        set ss [expr {[llength $supported_archs] == 1 ? "" : "s"}]
3404        foreach arch $required_archs {
3405            if {$arch ni $supported_archs} {
3406                ui_error "its dependency $dep only supports the arch${ss} '$supported_archs'."
3407                return
3408            }
3409        }
3410    }
3411    if {$has_universal} {
3412        foreach arch $required_archs {
3413            if {$arch ni $universal_archs} {
3414                ui_error "its dependency $dep does not build for the required arch${s} by default"
3415                ui_error "and the configured universal_archs '$universal_archs' are not sufficient."
3416                return
3417            }
3418        }
3419        ui_error "its dependency $dep cannot build for the required arch${s}."
3420        return
3421    }
3422    ui_error "its dependency $dep does not build for the required arch${s} by default"
3423    ui_error "and does not have a universal variant."
3424}
3425
3426# check if the given mport has any dependencies of the given types
3427proc macports::_mport_has_deptypes {mport deptypes} {
3428    array set portinfo [mportinfo $mport]
3429    foreach type $deptypes {
3430        if {[info exists portinfo($type)] && $portinfo($type) ne ""} {
3431            return 1
3432        }
3433    }
3434    return 0
3435}
3436
3437# check if the given target needs dependencies installed first
3438proc macports::_target_needs_deps {target} {
3439    # XXX: need a better way than checking this hardcoded list
3440    switch -- $target {
3441        fetch -
3442        checksum -
3443        extract -
3444        patch -
3445        configure -
3446        build -
3447        test -
3448        destroot -
3449        install -
3450        activate -
3451        dmg -
3452        mdmg -
3453        pkg -
3454        mpkg {return 1}
3455        default {return 0}
3456    }
3457}
3458
3459# Determine dependency types required for target
3460proc macports::_deptypes_for_target {target workername} {
3461    switch -- $target {
3462        fetch       -
3463        checksum    {return depends_fetch}
3464        extract     -
3465        patch       {return "depends_fetch depends_extract"}
3466        configure   -
3467        build       {return "depends_fetch depends_extract depends_build depends_lib"}
3468        test        {return "depends_fetch depends_extract depends_build depends_lib depends_run depends_test"}
3469        destroot    {return "depends_fetch depends_extract depends_build depends_lib depends_run"}
3470        dmg         -
3471        pkg         -
3472        mdmg        -
3473        mpkg        {
3474            if {[global_option_isset ports_binary_only] ||
3475                (![global_option_isset ports_source_only] && [$workername eval _archive_available])} {
3476                return "depends_lib depends_run"
3477            } else {
3478                return "depends_fetch depends_extract depends_build depends_lib depends_run"
3479            }
3480        }
3481        install     -
3482        activate    -
3483        {}          {
3484            if {[global_option_isset ports_binary_only] ||
3485                [$workername eval registry_exists \$subport \$version \$revision \$portvariants]
3486                || (![global_option_isset ports_source_only] && [$workername eval _archive_available])} {
3487                return "depends_lib depends_run"
3488            } else {
3489                return "depends_fetch depends_extract depends_build depends_lib depends_run"
3490            }
3491        }
3492    }
3493    return {}
3494}
3495
3496# selfupdate procedure
3497proc macports::selfupdate {{optionslist {}} {updatestatusvar {}}} {
3498    global macports::prefix macports::portdbpath macports::rsync_server macports::rsync_dir \
3499           macports::rsync_options macports::autoconf::macports_version \
3500           macports::autoconf::rsync_path tcl_platform macports::autoconf::openssl_path \
3501           macports::autoconf::tar_path
3502    array set options $optionslist
3503
3504    # variable that indicates whether we actually updated base
3505    if {$updatestatusvar ne ""} {
3506        upvar $updatestatusvar updatestatus
3507        set updatestatus no
3508    }
3509
3510    # are we syncing a tarball? (implies detached signature)
3511    set is_tarball 0
3512    if {[string range $rsync_dir end-3 end] eq ".tar"} {
3513        set is_tarball 1
3514        set mp_source_path [file join $portdbpath sources $rsync_server [file dirname $rsync_dir]]
3515    } else {
3516        if {[string index $rsync_dir end] ne "/"} {
3517            append rsync_dir /
3518        }
3519        set mp_source_path [file join $portdbpath sources $rsync_server $rsync_dir]
3520    }
3521    # create the path to the to be downloaded sources if it doesn't exist
3522    if {![file exists $mp_source_path]} {
3523        file mkdir $mp_source_path
3524    }
3525    ui_debug "MacPorts sources location: $mp_source_path"
3526
3527    # sync the MacPorts sources
3528    ui_msg "$macports::ui_prefix Updating MacPorts base sources using rsync"
3529    if {[catch {system "$rsync_path $rsync_options rsync://${rsync_server}/$rsync_dir $mp_source_path"} result]} {
3530       return -code error "Error synchronizing MacPorts sources: $result"
3531    }
3532
3533    if {$is_tarball} {
3534        # verify signature for tarball
3535        global macports::archivefetch_pubkeys
3536        if {[catch {system "$rsync_path $rsync_options rsync://${rsync_server}/${rsync_dir}.rmd160 $mp_source_path"} result]} {
3537            return -code error "Error synchronizing MacPorts source signature: $result"
3538        }
3539        set openssl [findBinary openssl $macports::autoconf::openssl_path]
3540        set tarball ${mp_source_path}/[file tail $rsync_dir]
3541        set signature ${tarball}.rmd160
3542        set verified 0
3543        foreach pubkey $macports::archivefetch_pubkeys {
3544            if {![catch {exec $openssl dgst -ripemd160 -verify $pubkey -signature $signature $tarball} result]} {
3545                set verified 1
3546                ui_debug "successful verification with key $pubkey"
3547                break
3548            } else {
3549                ui_debug "failed verification with key $pubkey"
3550                ui_debug "openssl output: $result"
3551            }
3552        }
3553        if {!$verified} {
3554            return -code error "Failed to verify signature for MacPorts source!"
3555        }
3556
3557        # extract tarball and move into place
3558        set tar [macports::findBinary tar $macports::autoconf::tar_path]
3559        file mkdir ${mp_source_path}/tmp
3560        set tar_cmd "$tar -C ${mp_source_path}/tmp -xf $tarball"
3561        ui_debug $tar_cmd
3562        if {[catch {system $tar_cmd}]} {
3563            return -code error "Failed to extract MacPorts sources from tarball!"
3564        }
3565        file delete -force ${mp_source_path}/base
3566        file rename ${mp_source_path}/tmp/base ${mp_source_path}/base
3567        file delete -force ${mp_source_path}/tmp
3568        # set the final extracted source path
3569        set mp_source_path ${mp_source_path}/base
3570    }
3571
3572    # echo current MacPorts version
3573    ui_msg "MacPorts base version $macports::autoconf::macports_version installed,"
3574
3575    if {[info exists options(ports_force)] && $options(ports_force)} {
3576        set use_the_force_luke yes
3577        ui_debug "Forcing a rebuild and reinstallation of MacPorts"
3578    } else {
3579        set use_the_force_luke no
3580        ui_debug "Rebuilding and reinstalling MacPorts if needed"
3581    }
3582
3583    # Choose what version file to use: old, floating point format or new, real version number format
3584    set version_file [file join $mp_source_path config macports_version]
3585    if {[file exists $version_file]} {
3586        set fd [open $version_file r]
3587        gets $fd macports_version_new
3588        close $fd
3589        # echo downloaded MacPorts version
3590        ui_msg "MacPorts base version $macports_version_new downloaded."
3591    } else {
3592        ui_warn "No version file found, please rerun selfupdate."
3593        set macports_version_new 0
3594    }
3595
3596    # check if we we need to rebuild base
3597    set comp [vercmp $macports_version_new $macports::autoconf::macports_version]
3598
3599    # syncing ports tree.
3600    if {![info exists options(ports_selfupdate_nosync)] || !$options(ports_selfupdate_nosync)} {
3601        if {$comp > 0} {
3602            # updated portfiles potentially need new base to parse - tell sync to try to
3603            # use prefabricated PortIndex files and signal if it couldn't
3604            lappend optionslist no_reindex 1 needed_portindex_var needed_portindex
3605        }
3606        if {[catch {mportsync $optionslist} result]} {
3607            return -code error "Couldn't sync the ports tree: $result"
3608        }
3609    }
3610
3611    if {$use_the_force_luke || $comp > 0} {
3612        if {[info exists options(ports_dryrun)] && $options(ports_dryrun)} {
3613            ui_msg "$macports::ui_prefix MacPorts base is outdated, selfupdate would install $macports_version_new (dry run)"
3614        } else {
3615            ui_msg "$macports::ui_prefix MacPorts base is outdated, installing new version $macports_version_new"
3616
3617            # get installation user/group and permissions
3618            set owner [file attributes $prefix -owner]
3619            set group [file attributes $prefix -group]
3620            set perms [string range [file attributes $prefix -permissions] end-3 end]
3621            if {$tcl_platform(user) ne "root" && $tcl_platform(user) ne $owner} {
3622                return -code error "User $tcl_platform(user) does not own $prefix - try using sudo"
3623            }
3624            ui_debug "Permissions OK"
3625
3626            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]"
3627            # too many users have an incompatible readline in /usr/local, see ticket #10651
3628            if {$tcl_platform(os) ne "Darwin" || $prefix eq "/usr/local"
3629                || ([glob -nocomplain /usr/local/lib/lib{readline,history}*] eq "" && [glob -nocomplain /usr/local/include/readline/*.h] eq "")} {
3630                append configure_args " --enable-readline"
3631            } else {
3632                ui_warn "Disabling readline support due to readline in /usr/local"
3633            }
3634
3635            if {$prefix eq "/usr/local" || $prefix eq "/usr"} {
3636                append configure_args " --with-unsupported-prefix"
3637            }
3638
3639            # Choose a sane compiler
3640            set cc_arg {}
3641            if {$::macports::os_platform eq "darwin"} {
3642                set cc_arg "CC=/usr/bin/cc OBJC=/usr/bin/cc "
3643            }
3644
3645            # do the actual configure, build and installation of new base
3646            ui_msg "Installing new MacPorts release in $prefix as ${owner}:${group}; permissions ${perms}\n"
3647            if {[catch {system "cd $mp_source_path && ${cc_arg}./configure $configure_args && make SELFUPDATING=1 && make install SELFUPDATING=1"} result]} {
3648                return -code error "Error installing new MacPorts base: $result"
3649            }
3650            if {[info exists updatestatus]} {
3651                set updatestatus yes
3652            }
3653        }
3654    } elseif {$comp < 0} {
3655        ui_msg "$macports::ui_prefix MacPorts base is probably trunk or a release candidate"
3656    } else {
3657        ui_msg "$macports::ui_prefix MacPorts base is already the latest version"
3658    }
3659
3660    # set the MacPorts sources to the right owner
3661    set sources_owner [file attributes [file join $portdbpath sources/] -owner]
3662    ui_debug "Setting MacPorts sources ownership to $sources_owner"
3663    if {[catch {exec [findBinary chown $macports::autoconf::chown_path] -R $sources_owner [file join $portdbpath sources/]} result]} {
3664        return -code error "Couldn't change permissions of the MacPorts sources at $mp_source_path to ${sources_owner}: $result"
3665    }
3666
3667    if {![info exists options(ports_selfupdate_nosync)] || !$options(ports_selfupdate_nosync)} {
3668        if {[info exists needed_portindex]} {
3669            ui_msg "Not all sources could be fully synced using the old version of MacPorts."
3670            ui_msg "Please run selfupdate again now that MacPorts base has been updated."
3671        } else {
3672            ui_msg "\nThe ports tree has been updated. To upgrade your installed ports, you should run"
3673            ui_msg "  port upgrade outdated"
3674        }
3675    }
3676
3677    return 0
3678}
3679
3680# upgrade API wrapper procedure
3681# return codes:
3682#   0 = success
3683#   1 = general failure
3684#   2 = port name not found in index
3685#   3 = port not installed
3686proc macports::upgrade {portname dspec variationslist optionslist {depscachename {}}} {
3687    # only installed ports can be upgraded
3688    if {![registry::entry_exists_for_name $portname]} {
3689        ui_error "$portname is not installed"
3690        return 3
3691    }
3692    if {$depscachename ne ""} {
3693        upvar $depscachename depscache
3694    } else {
3695        array set depscache {}
3696    }
3697    # stop upgrade from being called via mportexec as well
3698    set orig_nodeps yes
3699    if {![info exists macports::global_options(ports_nodeps)]} {
3700        set macports::global_options(ports_nodeps) yes
3701        set orig_nodeps no
3702    }
3703
3704    # run the actual upgrade
3705    set status [macports::_upgrade $portname $dspec $variationslist $optionslist depscache]
3706
3707    if {!$orig_nodeps} {
3708        unset -nocomplain macports::global_options(ports_nodeps)
3709    }
3710
3711    return $status
3712}
3713
3714# main internal upgrade procedure
3715proc macports::_upgrade {portname dspec variationslist optionslist {depscachename {}}} {
3716    global macports::global_variations
3717    array set options $optionslist
3718
3719    if {$depscachename ne ""} {
3720        upvar $depscachename depscache
3721    }
3722
3723    # Is this a dry run?
3724    set is_dryrun no
3725    if {[info exists options(ports_dryrun)] && $options(ports_dryrun)} {
3726        set is_dryrun yes
3727    }
3728
3729    # Is this a rev-upgrade-called run?
3730    set is_revupgrade no
3731    if {[info exists options(ports_revupgrade)] && $options(ports_revupgrade)} {
3732        set is_revupgrade yes
3733        # unset revupgrade options so we can upgrade dependencies with the same
3734        # $options without also triggering a rebuild there, see #40150
3735        unset options(ports_revupgrade)
3736    }
3737    set is_revupgrade_second_run no
3738    if {[info exists options(ports_revupgrade_second_run)] && $options(ports_revupgrade_second_run)} {
3739        set is_revupgrade_second_run yes
3740        # unset revupgrade options so we can upgrade dependencies with the same
3741        # $options without also triggering a rebuild there, see #40150
3742        unset options(ports_revupgrade_second_run)
3743    }
3744
3745    # check if the port is in tree
3746    if {[catch {mportlookup $portname} result]} {
3747        global errorInfo
3748        ui_debug $errorInfo
3749        ui_error "port lookup failed: $result"
3750        return 1
3751    }
3752    # argh! port doesnt exist!
3753    if {$result eq ""} {
3754        ui_warn "No port $portname found in the index."
3755        return 2
3756    }
3757    # fill array with information
3758    array set portinfo [lindex $result 1]
3759    # set portname again since the one we were passed may not have had the correct case
3760    set portname $portinfo(name)
3761    set options(subport) $portname
3762
3763    set ilist {}
3764    if {[catch {set ilist [registry::installed $portname {}]} result]} {
3765        if {$result eq "Registry error: $portname not registered as installed."} {
3766            ui_debug "$portname is *not* installed by MacPorts"
3767
3768            # We need to pass _mportispresent a reference to the mport that is
3769            # actually declaring the dependency on the one we're checking for.
3770            # We got here via _upgrade_dependencies, so we grab it from 2 levels up.
3771            upvar 2 mport parentmport
3772            if {![_mportispresent $parentmport $dspec]} {
3773                # open porthandle
3774                set porturl $portinfo(porturl)
3775                if {![info exists porturl]} {
3776                    set porturl file://./
3777                }
3778                # Grab the variations from the parent
3779                upvar 2 variations variations
3780
3781                if {[catch {set mport [mportopen $porturl [array get options] [array get variations]]} result]} {
3782                    global errorInfo
3783                    ui_debug $errorInfo
3784                    ui_error "Unable to open port: $result"
3785                    return 1
3786                }
3787                # While we're at it, update the portinfo
3788                array unset portinfo
3789                array set portinfo [mportinfo $mport]
3790
3791                # upgrade its dependencies first
3792                set status [_upgrade_dependencies portinfo depscache variationslist options]
3793                if {$status != 0 && $status != 2 && ![ui_isset ports_processall]} {
3794                    catch {mportclose $mport}
3795                    return $status
3796                }
3797                # now install it
3798                if {[catch {set result [mportexec $mport activate]} result]} {
3799                    global errorInfo
3800                    ui_debug $errorInfo
3801                    ui_error "Unable to exec port: $result"
3802                    catch {mportclose $mport}
3803                    return 1
3804                }
3805                if {$result > 0} {
3806                    ui_error "Problem while installing $portname"
3807                    catch {mportclose $mport}
3808                    return $result
3809                }
3810                # we just installed it, so mark it done in the cache
3811                set depscache(port:$portname) 1
3812                mportclose $mport
3813            } else {
3814                # dependency is satisfied by something other than the named port
3815                ui_debug "$portname not installed, soft dependency satisfied"
3816                # mark this depspec as satisfied in the cache
3817                set depscache($dspec) 1
3818            }
3819            # the rest of the proc doesn't matter for a port that is freshly
3820            # installed or not installed
3821            return 0
3822        } else {
3823            ui_error "Checking installed version failed: $result"
3824            return 1
3825        }
3826    } else {
3827        # we'll now take care of upgrading it, so we can add it to the cache
3828        set depscache(port:$portname) 1
3829    }
3830
3831    # set version_in_tree and revision_in_tree
3832    if {![info exists portinfo(version)]} {
3833        ui_error "Invalid port entry for ${portname}, missing version"
3834        return 1
3835    }
3836    set version_in_tree $portinfo(version)
3837    set revision_in_tree $portinfo(revision)
3838    set epoch_in_tree $portinfo(epoch)
3839
3840    # find latest version installed and active version (if any)
3841    set anyactive no
3842    set version_installed {}
3843    foreach i $ilist {
3844        set variant [lindex $i 3]
3845        set version [lindex $i 1]
3846        set revision [lindex $i 2]
3847        set epoch [lindex $i 5]
3848        if {$version_installed eq "" || ($epoch > $epoch_installed && $version ne $version_installed) ||
3849                ($epoch >= $epoch_installed && [vercmp $version $version_installed] > 0)
3850                || ($epoch >= $epoch_installed
3851                    && [vercmp $version $version_installed] == 0
3852                    && $revision > $revision_installed)} {
3853            set version_installed $version
3854            set revision_installed $revision
3855            set variant_installed $variant
3856            set epoch_installed $epoch
3857        }
3858
3859        set isactive [lindex $i 4]
3860        if {$isactive == 1} {
3861            set anyactive yes
3862            set version_active $version
3863            set revision_active $revision
3864            set variant_active $variant
3865            set epoch_active $epoch
3866        }
3867    }
3868
3869    # output version numbers
3870    ui_debug "epoch: in tree: $epoch_in_tree installed: $epoch_installed"
3871    ui_debug "$portname ${version_in_tree}_$revision_in_tree exists in the ports tree"
3872    ui_debug "$portname ${version_installed}_$revision_installed $variant_installed is the latest installed"
3873    if {$anyactive} {
3874        ui_debug "$portname ${version_active}_$revision_active $variant_active is active"
3875        # save existing variant for later use
3876        set oldvariant $variant_active
3877        set regref [registry::open_entry $portname $version_active $revision_active $variant_active $epoch_active]
3878    } else {
3879        ui_debug "no version of $portname is active"
3880        set oldvariant $variant_installed
3881        set regref [registry::open_entry $portname $version_installed $revision_installed $variant_installed $epoch_installed]
3882    }
3883    set oldnegatedvariant [registry::property_retrieve $regref negated_variants]
3884    if {$oldnegatedvariant == 0} {
3885        set oldnegatedvariant {}
3886    }
3887    set requestedflag [registry::property_retrieve $regref requested]
3888    set os_platform_installed [registry::property_retrieve $regref os_platform]
3889    set os_major_installed [registry::property_retrieve $regref os_major]
3890
3891    # Before we do
3892    # dependencies, we need to figure out the final variants,
3893    # open the port, and update the portinfo.
3894    set porturl $portinfo(porturl)
3895    if {![info exists porturl]} {
3896        set porturl file://./
3897    }
3898
3899    # Note $variationslist is left alone and so retains the original
3900    # requested variations, which should be passed to recursive calls to
3901    # upgrade; while variations gets existing variants and global variations
3902    # merged in later on, so it applies only to this port's upgrade
3903    array set variations $variationslist
3904
3905    set globalvarlist [array get macports::global_variations]
3906
3907    set minusvariant [lrange [split $oldnegatedvariant -] 1 end]
3908    set plusvariant [lrange [split $oldvariant +] 1 end]
3909    ui_debug "Merging existing variants '${oldvariant}$oldnegatedvariant' into variants"
3910    set oldvariantlist [list]
3911    foreach v $plusvariant {
3912        lappend oldvariantlist $v +
3913    }
3914    foreach v $minusvariant {
3915        lappend oldvariantlist $v -
3916    }
3917
3918    # merge in the old variants
3919    foreach {variation value} $oldvariantlist {
3920        if {![info exists variations($variation)]} {
3921            set variations($variation) $value
3922        }
3923    }
3924
3925    # Now merge in the global (i.e. variants.conf) variations.
3926    # We wait until now so that existing variants for this port
3927    # override global variations
3928    foreach {variation value} $globalvarlist {
3929        if {![info exists variations($variation)]} {
3930            set variations($variation) $value
3931        }
3932    }
3933
3934    ui_debug "new fully merged portvariants: [array get variations]"
3935
3936    # at this point we need to check if a different port will be replacing this one
3937    if {[info exists portinfo(replaced_by)] && ![info exists options(ports_upgrade_no-replace)]} {
3938        ui_msg "$macports::ui_prefix $portname is replaced by $portinfo(replaced_by)"
3939        if {[catch {mportlookup $portinfo(replaced_by)} result]} {
3940            global errorInfo
3941            ui_debug $errorInfo
3942            ui_error "port lookup failed: $result"
3943            return 1
3944        }
3945        if {$result eq ""} {
3946            ui_error "No port $portinfo(replaced_by) found."
3947            return 1
3948        }
3949        array unset portinfo
3950        array set portinfo [lindex $result 1]
3951        set newname $portinfo(name)
3952
3953        set porturl $portinfo(porturl)
3954        if {![info exists porturl]} {
3955            set porturl file://./
3956        }
3957        set depscache(port:$newname) 1
3958    } else {
3959        set newname $portname
3960    }
3961
3962    array set interp_options [array get options]
3963    set interp_options(ports_requested) $requestedflag
3964    set interp_options(subport) $newname
3965    # Mark this port to be rebuilt from source if this isn't the first time it
3966    # was flagged as broken by rev-upgrade
3967    if {$is_revupgrade_second_run} {
3968        set interp_options(ports_source_only) yes
3969    }
3970
3971    if {[catch {set mport [mportopen $porturl [array get interp_options] [array get variations]]} result]} {
3972        global errorInfo
3973        ui_debug $errorInfo
3974        ui_error "Unable to open port: $result"
3975        return 1
3976    }
3977    array unset interp_options
3978
3979    array unset portinfo
3980    array set portinfo [mportinfo $mport]
3981    set version_in_tree $portinfo(version)
3982    set revision_in_tree $portinfo(revision)
3983    set epoch_in_tree $portinfo(epoch)
3984
3985    set build_override 0
3986    set will_install yes
3987    # check installed version against version in ports
3988    if {([vercmp $version_installed $version_in_tree] > 0
3989            || ([vercmp $version_installed $version_in_tree] == 0
3990                && [vercmp $revision_installed $revision_in_tree] >= 0))
3991        && ![info exists options(ports_upgrade_force)]} {
3992        if {$portname ne $newname} {
3993            ui_debug "ignoring versions, installing replacement port"
3994        } elseif {$epoch_installed < $epoch_in_tree && $version_installed ne $version_in_tree} {
3995            set build_override 1
3996            ui_debug "epoch override ... upgrading!"
3997        } elseif {[info exists options(ports_upgrade_enforce-variants)] && $options(ports_upgrade_enforce-variants)
3998                  && [info exists portinfo(canonical_active_variants)] && $portinfo(canonical_active_variants) ne $oldvariant} {
3999            ui_debug "variant override ... upgrading!"
4000        } elseif {$os_platform_installed ne "" && $os_major_installed ne "" && $os_platform_installed != 0
4001                  && ([_mportkey $mport os.platform] ne $os_platform_installed
4002                  || [_mportkey $mport os.major] != $os_major_installed)} {
4003            ui_debug "platform mismatch ... upgrading!"
4004            set build_override 1
4005        } elseif {$is_revupgrade_second_run} {
4006            ui_debug "rev-upgrade override ... upgrading (from source)!"
4007            set build_override 1
4008        } elseif {$is_revupgrade} {
4009            ui_debug "rev-upgrade override ... upgrading!"
4010            # in the first run of rev-upgrade, only activate possibly already existing files and check for missing dependencies
4011            # do nothing, just prevent will_install being set to no below
4012        } else {
4013            if {[info exists portinfo(canonical_active_variants)] && $portinfo(canonical_active_variants) ne $oldvariant} {
4014                if {[llength $variationslist] > 0} {
4015                    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."
4016                } else {
4017                    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)\"."
4018                }
4019            } else {
4020                ui_debug "No need to upgrade! $portname ${version_installed}_$revision_installed >= $portname ${version_in_tree}_$revision_in_tree"
4021            }
4022            set will_install no
4023        }
4024    }
4025
4026    set will_build no
4027    set already_installed [registry::entry_exists $newname $version_in_tree $revision_in_tree $portinfo(canonical_active_variants)]
4028    # avoid building again unnecessarily
4029    if {$will_install &&
4030        ([info exists options(ports_upgrade_force)]
4031            || $build_override == 1
4032            || !$already_installed)} {
4033        set will_build yes
4034    }
4035
4036    # first upgrade dependencies
4037    if {![info exists options(ports_nodeps)]} {
4038        # the last arg is because we might have to build from source if a rebuild is being forced
4039        set status [_upgrade_dependencies portinfo depscache variationslist options [expr {$will_build && $already_installed}]]
4040        if {$status != 0 && $status != 2 && ![ui_isset ports_processall]} {
4041            catch {mportclose $mport}
4042            return $status
4043        }
4044    } else {
4045        ui_debug "Not following dependencies"
4046    }
4047
4048    if {!$will_install} {
4049        # nothing to do for this port, so just check if we have to do dependents
4050        if {[info exists options(ports_do_dependents)]} {
4051            # We do dependents ..
4052            set options(ports_nodeps) 1
4053
4054            registry::open_dep_map
4055            if {$anyactive} {
4056                set deplist [registry::list_dependents $portname $version_active $revision_active $variant_active]
4057            } else {
4058                set deplist [registry::list_dependents $portname $version_installed $revision_installed $variant_installed]
4059            }
4060
4061            if {[llength deplist] > 0} {
4062                foreach dep $deplist {
4063                    set mpname [lindex $dep 2]
4064                    if {![llength [array get depscache port:$mpname]]} {
4065                        set status [macports::_upgrade $mpname port:$mpname $variationslist [array get options] depscache]
4066                        if {$status != 0 && $status != 2 && ![ui_isset ports_processall]} {
4067                            catch {mportclose $mport}
4068                            return $status
4069                        }
4070                    }
4071                }
4072            }
4073        }
4074        mportclose $mport
4075        return 0
4076    }
4077
4078    if {$will_build} {
4079        if {$already_installed
4080            && ([info exists options(ports_upgrade_force)] || $build_override == 1)} {
4081            # Tell archivefetch/unarchive not to use the installed archive, i.e. a
4082            # fresh one will be either fetched or built locally.
4083            # Ideally this would be done in the interp_options when we mportopen,
4084            # but we don't know if we want to do this at that point.
4085            set workername [ditem_key $mport workername]
4086            $workername eval "set force_archive_refresh yes"
4087
4088            # run archivefetch and destroot for version_in_tree
4089            # doing this instead of just running install ensures that we have the
4090            # new copy ready but not yet installed, so we can safely uninstall the
4091            # existing one.
4092            if {[catch {set result [mportexec $mport archivefetch]} result] || $result != 0} {
4093                if {[info exists ::errorInfo]} {
4094                    ui_debug $::errorInfo
4095                }
4096                catch {mportclose $mport}
4097                return 1
4098            }
4099            # the following is a noop if archivefetch found an archive
4100            if {[catch {set result [mportexec $mport destroot]} result] || $result != 0} {
4101                if {[info exists ::errorInfo]} {
4102                    ui_debug $::errorInfo
4103                }
4104                catch {mportclose $mport}
4105                return 1
4106            }
4107        } else {
4108            # Normal non-forced case
4109            # install version_in_tree (but don't activate yet)
4110            if {[catch {set result [mportexec $mport install]} result] || $result != 0} {
4111                if {[info exists ::errorInfo]} {
4112                    ui_debug $::errorInfo
4113                }
4114                catch {mportclose $mport}
4115                return 1
4116            }
4117        }
4118    }
4119
4120    # are we installing an existing version due to force or epoch override?
4121    if {$already_installed
4122        && ([info exists options(ports_upgrade_force)] || $build_override == 1)} {
4123         ui_debug "Uninstalling $newname ${version_in_tree}_${revision_in_tree}$portinfo(canonical_active_variants)"
4124        # we have to force the uninstall in case of dependents
4125        set force_cur [info exists options(ports_force)]
4126        set options(ports_force) yes
4127        set existing_epoch [lindex [registry::installed $newname ${version_in_tree}_${revision_in_tree}$portinfo(canonical_active_variants)] 0 5]
4128        set newregref [registry::open_entry $newname $version_in_tree $revision_in_tree $portinfo(canonical_active_variants) $existing_epoch]
4129        if {$is_dryrun} {
4130            ui_msg "Skipping uninstall $newname @${version_in_tree}_${revision_in_tree}$portinfo(canonical_active_variants) (dry run)"
4131        } elseif {![registry::run_target $newregref uninstall [array get options]]
4132                  && [catch {registry_uninstall::uninstall $newname $version_in_tree $revision_in_tree $portinfo(canonical_active_variants) [array get options]} result]} {
4133            global errorInfo
4134            ui_debug $errorInfo
4135            ui_error "Uninstall $newname ${version_in_tree}_${revision_in_tree}$portinfo(canonical_active_variants) failed: $result"
4136            catch {mportclose $mport}
4137            return 1
4138        }
4139        if {!$force_cur} {
4140            unset options(ports_force)
4141        }
4142        if {$anyactive && $version_in_tree eq $version_active && $revision_in_tree == $revision_active
4143            && $portinfo(canonical_active_variants) eq $variant_active && $portname eq $newname} {
4144            set anyactive no
4145        }
4146    }
4147    if {$anyactive && $portname ne $newname} {
4148        # replaced_by in effect, deactivate the old port
4149        # we have to force the deactivate in case of dependents
4150        set force_cur [info exists options(ports_force)]
4151        set options(ports_force) yes
4152        if {$is_dryrun} {
4153            ui_msg "Skipping deactivate $portname @${version_active}_${revision_active}$variant_active (dry run)"
4154        } elseif {![catch {registry::active $portname}] &&
4155                  ![registry::run_target $regref deactivate [array get options]]
4156                  && [catch {portimage::deactivate $portname $version_active $revision_active $variant_active [array get options]} result]} {
4157            global errorInfo
4158            ui_debug $errorInfo
4159            ui_error "Deactivating $portname @${version_active}_${revision_active}$variant_active failed: $result"
4160            catch {mportclose $mport}
4161            return 1
4162        }
4163        if {!$force_cur} {
4164            unset options(ports_force)
4165        }
4166        set anyactive no
4167    }
4168    if {[info exists options(port_uninstall_old)] && $portname eq $newname} {
4169        # uninstalling now could fail due to dependents when not forced,
4170        # because the new version is not installed
4171        set uninstall_later yes
4172    }
4173
4174    if {$is_dryrun} {
4175        if {$anyactive} {
4176            ui_msg "Skipping deactivate $portname @${version_active}_${revision_active}$variant_active (dry run)"
4177        }
4178        ui_msg "Skipping activate $newname @${version_in_tree}_${revision_in_tree}$portinfo(canonical_active_variants) (dry run)"
4179    } elseif {[catch {set result [mportexec $mport activate]} result]} {
4180        global errorInfo
4181        ui_debug $errorInfo
4182        ui_error "Couldn't activate $newname ${version_in_tree}_${revision_in_tree}$portinfo(canonical_active_variants): $result"
4183        catch {mportclose $mport}
4184        return 1
4185    }
4186
4187    # Check if we have to do dependents
4188    if {[info exists options(ports_do_dependents)]} {
4189        # We do dependents ..
4190        set options(ports_nodeps) 1
4191
4192        registry::open_dep_map
4193        if {$portname ne $newname} {
4194            set deplist [registry::list_dependents $newname $version_in_tree $revision_in_tree $portinfo(canonical_active_variants)]
4195        } else {
4196            set deplist [list]
4197        }
4198        if {$anyactive} {
4199            set deplist [concat $deplist [registry::list_dependents $portname $version_active $revision_active $variant_active]]
4200        } else {
4201            set deplist [concat $deplist [registry::list_dependents $portname $version_installed $revision_installed $variant_installed]]
4202        }
4203
4204        if {[llength deplist] > 0} {
4205            foreach dep $deplist {
4206                set mpname [lindex $dep 2]
4207                if {![llength [array get depscache port:$mpname]]} {
4208                    set status [macports::_upgrade $mpname port:$mpname $variationslist [array get options] depscache]
4209                    if {$status != 0 && $status != 2 && ![ui_isset ports_processall]} {
4210                        catch {mportclose $mport}
4211                        return $status
4212                    }
4213                }
4214            }
4215        }
4216    }
4217
4218    if {[info exists uninstall_later] && $uninstall_later} {
4219        foreach i $ilist {
4220            set version [lindex $i 1]
4221            set revision [lindex $i 2]
4222            set variant [lindex $i 3]
4223            if {$version eq $version_in_tree && $revision == $revision_in_tree && $variant eq $portinfo(canonical_active_variants) && $portname eq $newname} {
4224                continue
4225            }
4226            set epoch [lindex $i 5]
4227            ui_debug "Uninstalling $portname ${version}_${revision}$variant"
4228            set regref [registry::open_entry $portname $version $revision $variant $epoch]
4229            if {$is_dryrun} {
4230                ui_msg "Skipping uninstall $portname @${version}_${revision}$variant (dry run)"
4231            } elseif {![registry::run_target $regref uninstall $optionslist]
4232                      && [catch {registry_uninstall::uninstall $portname $version $revision $variant $optionslist} result]} {
4233                global errorInfo
4234                ui_debug $errorInfo
4235                # replaced_by can mean that we try to uninstall all versions of the old port, so handle errors due to dependents
4236                if {$result ne "Please uninstall the ports that depend on $portname first." && ![ui_isset ports_processall]} {
4237                    ui_error "Uninstall $portname @${version}_${revision}$variant failed: $result"
4238                    catch {mportclose $mport}
4239                    return 1
4240                }
4241            }
4242        }
4243    }
4244
4245    # close the port handle
4246    mportclose $mport
4247    return 0
4248}
4249
4250# upgrade_dependencies: helper proc for upgrade
4251# Calls upgrade on each dependency listed in the PortInfo.
4252# Uses upvar to access the variables.
4253proc macports::_upgrade_dependencies {portinfoname depscachename variationslistname optionsname {build_needed no}} {
4254    upvar $portinfoname portinfo $depscachename depscache \
4255          $variationslistname variationslist \
4256          $optionsname options
4257    upvar mport parentmport
4258
4259    # If we're following dependents, we only want to follow this port's
4260    # dependents, not those of all its dependencies. Otherwise, we would
4261    # end up processing this port's dependents n+1 times (recursively!),
4262    # where n is the number of dependencies this port has, since this port
4263    # is of course a dependent of each of its dependencies. Plus the
4264    # dependencies could have any number of unrelated dependents.
4265
4266    # So we save whether we're following dependents, unset the option
4267    # while doing the dependencies, and restore it afterwards.
4268    set saved_do_dependents [info exists options(ports_do_dependents)]
4269    unset -nocomplain options(ports_do_dependents)
4270
4271    set parentworker [ditem_key $parentmport workername]
4272    # each required dep type is upgraded
4273    if {$build_needed && ![global_option_isset ports_binary_only]} {
4274        set dtypes [_deptypes_for_target destroot $parentworker]
4275    } else {
4276        set dtypes [_deptypes_for_target install $parentworker]
4277    }
4278
4279    set status 0
4280    foreach dtype $dtypes {
4281        if {[info exists portinfo($dtype)]} {
4282            foreach i $portinfo($dtype) {
4283                set d [$parentworker eval _get_dep_port $i]
4284                if {![llength [array get depscache port:$d]] && ![llength [array get depscache $i]]} {
4285                    if {$d ne ""} {
4286                        set dspec port:$d
4287                    } else {
4288                        set dspec $i
4289                        set d [lindex [split $i :] end]
4290                    }
4291                    set status [macports::_upgrade $d $dspec $variationslist [array get options] depscache]
4292                    if {$status != 0 && $status != 2 && ![ui_isset ports_processall]} break
4293                }
4294            }
4295        }
4296        if {$status != 0 && $status != 2 && ![ui_isset ports_processall]} break
4297    }
4298    # restore dependent-following to its former value
4299    if {$saved_do_dependents} {
4300        set options(ports_do_dependents) yes
4301    }
4302    return $status
4303}
4304
4305# mportselect
4306#   * command: The only valid commands are list, set, show and summary
4307#   * group: This argument should correspond to a directory under
4308#            ${macports::prefix}/etc/select.
4309#   * version: This argument is only used by the 'set' command.
4310# On error mportselect returns with the code 'error'.
4311proc mportselect {command {group ""} {version {}}} {
4312    ui_debug "mportselect \[$command] \[$group] \[$version]"
4313
4314    set conf_path ${macports::prefix}/etc/select/$group
4315    if {![file isdirectory $conf_path]} {
4316        return -code error "The specified group '$group' does not exist."
4317    }
4318
4319    switch -- $command {
4320        list {
4321            if {[catch {set versions [glob -directory $conf_path *]} result]} {
4322                global errorInfo
4323                ui_debug "${result}: $errorInfo"
4324                return -code error [concat "No configurations associated" \
4325                                           "with '$group' were found."]
4326            }
4327
4328            # Return the sorted list of versions (excluding base and current).
4329            set lversions {}
4330            foreach v $versions {
4331                # Only the file name corresponds to the version name.
4332                set v [file tail $v]
4333                if {$v eq "base" || $v eq "current"} {
4334                    continue
4335                }
4336                lappend lversions [file tail $v]
4337            }
4338            return [lsort $lversions]
4339        }
4340        summary {
4341            # Return the list of portgroups in ${macports::prefix}/etc/select
4342            if {[catch {set lportgroups [glob -directory $conf_path -tails *]} result]} {
4343                global errorInfo
4344                ui_debug "${result}: $errorInfo"
4345                return -code error [concat "No ports with the select" \
4346                                           "option were found."]
4347            }
4348            return [lsort $lportgroups]
4349        }
4350        set {
4351            # Use ${conf_path}/$version to read in sources.
4352            if {$version eq "" || $version eq "base" || $version eq "current"
4353                    || [catch {set src_file [open "${conf_path}/$version"]} result]} {
4354                global errorInfo
4355                ui_debug "${result}: $errorInfo"
4356                return -code error "The specified version '$version' is not valid."
4357            }
4358            set srcs [split [read -nonewline $src_file] \n]
4359            close $src_file
4360
4361            # Use ${conf_path}/base to read in targets.
4362            if {[catch {set tgt_file [open ${conf_path}/base]} result]} {
4363                global errorInfo
4364                ui_debug "${result}: $errorInfo"
4365                return -code error [concat "The configuration file" \
4366                                           "'${conf_path}/base' could not be" \
4367                                           "opened."]
4368            }
4369            set tgts [split [read -nonewline $tgt_file] \n]
4370            close $tgt_file
4371
4372            # Iterate through the configuration files executing the specified
4373            # actions.
4374            set i 0
4375            foreach tgt $tgts {
4376                set src [lindex $srcs $i]
4377
4378                switch -glob -- $src {
4379                    - {
4380                        # The source is unavailable for this file.
4381                        set tgt [file join $macports::prefix $tgt]
4382                        file delete $tgt
4383                        ui_debug "rm -f $tgt"
4384                    }
4385                    /* {
4386                        # The source is an absolute path.
4387                        set tgt [file join $macports::prefix $tgt]
4388                        file delete $tgt
4389                        file link -symbolic $tgt $src
4390                        ui_debug "ln -sf $src $tgt"
4391                    }
4392                    default {
4393                        # The source is a relative path.
4394                        set src [file join $macports::prefix $src]
4395                        set tgt [file join $macports::prefix $tgt]
4396                        file delete $tgt
4397                        file link -symbolic $tgt $src
4398                        ui_debug "ln -sf $src $tgt"
4399                    }
4400                }
4401                incr i
4402            }
4403
4404            # Update the selected version.
4405            set selected_version ${conf_path}/current
4406            if {[file exists $selected_version]} {
4407                file delete $selected_version
4408            }
4409            symlink $version $selected_version
4410            return
4411        }
4412        show {
4413            set selected_version ${conf_path}/current
4414
4415            if {[catch {file type $selected_version} err]} {
4416                # this might be okay if nothing was selected yet,
4417                # just log the error for debugging purposes
4418                ui_debug "cannot determine selected version for $group: $err"
4419                return none
4420            } else {
4421                return [file readlink $selected_version]
4422            }
4423        }
4424    }
4425    return
4426}
4427
4428# Return a good temporary directory to use; /tmp if TMPDIR is not set
4429# in the environment
4430proc macports::gettmpdir {args} {
4431    global env
4432
4433    if {[info exists env(TMPDIR)]} {
4434        return $env(TMPDIR)
4435    } else {
4436        return /tmp
4437    }
4438}
4439
4440# check if the system we're on can run code of the given architecture
4441proc macports::arch_runnable {arch} {
4442    global macports::os_major macports::os_arch macports::os_platform
4443    if {$macports::os_platform eq "darwin"} {
4444        if {$macports::os_major >= 11 && [string first ppc $arch] == 0} {
4445            return no
4446        } elseif {$macports::os_arch eq "i386" && $arch eq "ppc64"} {
4447            return no
4448        } elseif {$macports::os_major <= 8 && $arch eq "x86_64"} {
4449            return no
4450        }
4451    }
4452    return yes
4453}
4454
4455proc macports::diagnose_main {opts} {
4456   
4457    # Calls the main function for the 'port diagnose' command.
4458    #
4459    # Args:
4460    #           None
4461    # Returns:
4462    #           0 on successful execution.
4463
4464    diagnose::main $opts
4465    return 0
4466}
4467
4468proc macports::reclaim_main {} {
4469
4470    # Calls the main function for the 'port reclaim' command.
4471    #
4472    # Args:
4473    #           None
4474    # Returns:
4475    #           None
4476
4477    reclaim::main
4478    return 0
4479}
4480
4481##
4482# Execute the rev-upgrade scan and attempt to rebuild all ports found to be
4483# broken. Depends on the revupgrade_mode setting from macports.conf.
4484#
4485# @param opts
4486#        A Tcl array serialized into a list using array get containing options
4487#        for MacPorts. Options used exclusively by rev-upgrade are
4488#        ports_rev-upgrade_id-loadcmd-check, a boolean indicating whether the
4489#        ID load command of binaries should be check for sanity. This is mostly
4490#        useful for maintainers.
4491# @return 0 if report-only mode is enabled, no ports are broken, or the
4492#         rebuilds finished successfully. 1 if an exception occured during the
4493#         execution of rev-upgrade, 2 if the execution was aborted on user
4494#         request.
4495proc macports::revupgrade {opts} {
4496    set run_loop 1
4497    array set broken_port_counts {}
4498    try {
4499        while {$run_loop == 1} {
4500            set run_loop [revupgrade_scanandrebuild broken_port_counts $opts]
4501        }
4502        return 0
4503    } catch {{POSIX SIG SIGINT} eCode eMessage} {
4504        ui_debug "rev-upgrade failed: $::errorInfo"
4505        ui_error [msgcat::mc "rev-upgrade aborted: SIGINT received."]
4506        return 2
4507    } catch {{POSIX SIG SIGTERM} eCode eMessage} {
4508        ui_error [msgcat::mc "rev-upgrade aborted: SIGTERM received."]
4509        return 2
4510    } catch {{*} eCode eMessage} {
4511        ui_debug "rev-upgrade failed: $::errorInfo"
4512        ui_error [msgcat::mc "rev-upgrade failed: %s" $eMessage]
4513        return 1
4514    }
4515}
4516
4517##
4518# Helper function for rev-upgrade. Do not consider this to be part of public
4519# API. Use macports::revupgrade instead.
4520#
4521# @param broken_port_counts_name
4522#        The name of a Tcl array that's being used to store the number of times
4523#        a port has been rebuilt so far.
4524# @param opts
4525#        A serialized version of a Tcl array that contains options for
4526#        MacPorts. Options used by this method are
4527#        ports_rev-upgrade_id-loadcmd-check, a boolean indicating whether the
4528#        ID loadcommand of binaries should also be checked during rev-upgrade
4529#        and ports_dryrun, a boolean indicating whether no action should be
4530#        taken.
4531# @return 1 if ports were rebuilt and this function should be called again,
4532#         0 otherwise.
4533proc macports::revupgrade_scanandrebuild {broken_port_counts_name opts} {
4534    upvar $broken_port_counts_name broken_port_counts
4535    array set options $opts
4536
4537    set files [registry::file search active 1 binary -null]
4538    set files_count [llength $files]
4539    set fancy_output [expr {![macports::ui_isset ports_debug] && [info exists macports::ui_options(progress_generic)]}]
4540    if {$fancy_output} {
4541        set revupgrade_progress $macports::ui_options(progress_generic)
4542    }
4543    if {$files_count > 0} {
4544        registry::write {
4545            try {
4546                ui_msg "$macports::ui_prefix Updating database of binaries"
4547                set i 1
4548                if {$fancy_output} {
4549                    $revupgrade_progress start
4550                }
4551                foreach f $files {
4552                    if {$fancy_output} {
4553                        if {$files_count < 10000 || $i % 100 == 1} {
4554                            $revupgrade_progress update $i $files_count
4555                        }
4556                    }
4557                    set fpath [$f actual_path]
4558                    ui_debug "Updating binary flag for file $i of ${files_count}: $fpath"
4559                    incr i
4560
4561                    if {0 != [catch {$f binary [fileIsBinary $fpath]} fileIsBinaryError]} {
4562                        # handle errors (e.g. file not found, permission denied) gracefully
4563                        if {$fancy_output} {
4564                            $revupgrade_progress intermission
4565                        }
4566                        ui_warn "Error determining file type of `$fpath': $fileIsBinaryError"
4567                        ui_warn "A file belonging to the `[[registry::entry owner $fpath] name]' port is missing or unreadable. Consider reinstalling it."
4568                    }
4569                }
4570            } catch {*} {
4571                if {${fancy_output}} {
4572                    $revupgrade_progress intermission
4573                }
4574                ui_error "Updating database of binaries failed"
4575                throw
4576            }
4577        }
4578        if {$fancy_output} {
4579            $revupgrade_progress finish
4580        }
4581    }
4582
4583    set broken_files {};
4584    set binaries [registry::file search active 1 binary 1]
4585    set binary_count [llength $binaries]
4586    if {$binary_count > 0} {
4587        ui_msg "$macports::ui_prefix Scanning binaries for linking errors"
4588        set handle [machista::create_handle]
4589        if {$handle eq "NULL"} {
4590            error "Error creating libmachista handle"
4591        }
4592        array unset files_warned_about
4593        array set files_warned_about [list]
4594
4595        if {$fancy_output} {
4596            $revupgrade_progress start
4597        }
4598
4599        try {
4600            set i 1
4601            foreach b $binaries {
4602                if {$fancy_output} {
4603                    if {$binary_count < 10000 || $i % 10 == 1} {
4604                        $revupgrade_progress update $i $binary_count
4605                    }
4606                }
4607                set bpath [$b actual_path]
4608                #ui_debug "${i}/${binary_count}: $bpath"
4609                incr i
4610
4611                set resultlist [machista::parse_file $handle $bpath]
4612                set returncode [lindex $resultlist 0]
4613                set result     [lindex $resultlist 1]
4614
4615                if {$returncode != $machista::SUCCESS} {
4616                    if {$returncode == $machista::EMAGIC} {
4617                        # not a Mach-O file
4618                        # ignore silently, these are only static libs anyway
4619                        #ui_debug "Error parsing file ${bpath}: [machista::strerror $returncode]"
4620                    } else {
4621                        if {$fancy_output} {
4622                            $revupgrade_progress intermission
4623                        }
4624                        ui_warn "Error parsing file ${bpath}: [machista::strerror $returncode]"
4625                    }
4626                    continue;
4627                }
4628
4629                set architecture [$result cget -mt_archs]
4630                while {$architecture ne "NULL"} {
4631                    if {[info exists options(ports_rev-upgrade_id-loadcmd-check)] && $options(ports_rev-upgrade_id-loadcmd-check)} {
4632                        if {[$architecture cget -mat_install_name] ne "NULL" && [$architecture cget -mat_install_name] ne ""} {
4633                            # check if this lib's install name actually refers to this file itself
4634                            # if this is not the case software linking against this library might have erroneous load commands
4635                            if {0 == [catch {set idloadcmdpath [revupgrade_handle_special_paths $bpath [$architecture cget -mat_install_name]]}]} {
4636                                if {[string index $idloadcmdpath 0] ne "/"} {
4637                                    set port [registry::entry owner $bpath]
4638                                    if {$port ne ""} {
4639                                        set portname [$port name]
4640                                    } else {
4641                                        set portname <unknown-port>
4642                                    }
4643                                    if {$fancy_output} {
4644                                        $revupgrade_progress intermission
4645                                    }
4646                                    ui_warn "ID load command in ${bpath}, arch [machista::get_arch_name [$architecture cget -mat_arch]] (belonging to port $portname) contains relative path"
4647                                } elseif {![file exists $idloadcmdpath]} {
4648                                    set port [registry::entry owner $bpath]
4649                                    if {$port ne ""} {
4650                                        set portname [$port name]
4651                                    } else {
4652                                        set portname <unknown-port>
4653                                    }
4654                                    if {$fancy_output} {
4655                                        $revupgrade_progress intermission
4656                                    }
4657                                    ui_warn "ID load command in ${bpath}, arch [machista::get_arch_name [$architecture cget -mat_arch]] refers to non-existent file $idloadcmdpath"
4658                                    ui_warn "This is probably a bug in the $portname port and might cause problems in libraries linking against this file"
4659                                } else {
4660                                    set hash_this [sha256 file $bpath]
4661                                    set hash_idloadcmd [sha256 file $idloadcmdpath]
4662
4663                                    if {$hash_this ne $hash_idloadcmd} {
4664                                        set port [registry::entry owner $bpath]
4665                                        if {$port ne ""} {
4666                                            set portname [$port name]
4667                                        } else {
4668                                            set portname <unknown-port>
4669                                        }
4670                                        if {$fancy_output} {
4671                                            $revupgrade_progress intermission
4672                                        }
4673                                        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"
4674                                        ui_warn "This is probably a bug in the $portname port and might cause problems in libraries linking against this file"
4675                                    }
4676                                }
4677                            }
4678                        }
4679                    }
4680
4681                    set archname [machista::get_arch_name [$architecture cget -mat_arch]]
4682                    if {![arch_runnable $archname]} {
4683                        ui_debug "skipping $archname in $bpath since this system can't run it anyway"
4684                        set architecture [$architecture cget -next]
4685                        continue
4686                    }
4687
4688                    set loadcommand [$architecture cget -mat_loadcmds]
4689
4690                    while {$loadcommand ne "NULL"} {
4691                        if {0 != [catch {set filepath [revupgrade_handle_special_paths $bpath [$loadcommand cget -mlt_install_name]]}]} {
4692                            set loadcommand [$loadcommand cget -next]
4693                            continue;
4694                        }
4695
4696                        set libresultlist [machista::parse_file $handle $filepath]
4697                        set libreturncode [lindex $libresultlist 0]
4698                        set libresult     [lindex $libresultlist 1]
4699
4700                        if {$libreturncode != $machista::SUCCESS} {
4701                            if {![info exists files_warned_about($filepath)]} {
4702                                if {$fancy_output} {
4703                                    $revupgrade_progress intermission
4704                                }
4705                                ui_info "Could not open ${filepath}: [machista::strerror $libreturncode] (referenced from $bpath)"
4706                                if {[string first [file separator] $filepath] == -1} {
4707                                    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."
4708                                }
4709                                set files_warned_about($filepath) yes
4710                            }
4711                            if {$libreturncode == $machista::EFILE} {
4712                                ui_debug "Marking $bpath as broken"
4713                                lappend broken_files $bpath
4714                            }
4715                            set loadcommand [$loadcommand cget -next]
4716                            continue;
4717                        }
4718
4719                        set libarchitecture [$libresult cget -mt_archs]
4720                        set libarch_found false;
4721                        while {$libarchitecture ne "NULL"} {
4722                            if {[$architecture cget -mat_arch] ne [$libarchitecture cget -mat_arch]} {
4723                                set libarchitecture [$libarchitecture cget -next]
4724                                continue;
4725                            }
4726
4727                            if {[$loadcommand cget -mlt_version] ne [$libarchitecture cget -mat_version] && [$loadcommand cget -mlt_comp_version] > [$libarchitecture cget -mat_comp_version]} {
4728                                if {$fancy_output} {
4729                                    $revupgrade_progress intermission
4730                                }
4731                                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]]"
4732                                ui_debug "Marking $bpath as broken"
4733                                lappend broken_files $bpath
4734                            }
4735
4736                            set libarch_found true;
4737                            break;
4738                        }
4739
4740                        if {!$libarch_found} {
4741                            ui_debug "Missing architecture [machista::get_arch_name [$architecture cget -mat_arch]] in file $filepath"
4742                            if {[path_is_in_prefix $filepath]} {
4743                                ui_debug "Marking $bpath as broken"
4744                                lappend broken_files $bpath
4745                            } else {
4746                                ui_debug "Missing architecture [machista::get_arch_name [$architecture cget -mat_arch]] in file outside prefix referenced from $bpath"
4747                                # ui_debug "   How did you get that compiled anyway?"
4748                            }
4749                        }
4750                        set loadcommand [$loadcommand cget -next]
4751                    }
4752
4753                    set architecture [$architecture cget -next]
4754                }
4755            }
4756        } catch {*} {
4757            if {$fancy_output} {
4758                $revupgrade_progress intermission
4759            }
4760            throw
4761        }
4762        if {$fancy_output} {
4763            $revupgrade_progress finish
4764        }
4765
4766        machista::destroy_handle $handle
4767
4768        set num_broken_files [llength $broken_files]
4769        set s [expr {$num_broken_files == 1 ? "" : "s"}]
4770
4771        if {$num_broken_files == 0} {
4772            ui_msg "$macports::ui_prefix No broken files found."
4773            return 0
4774        }
4775        ui_msg "$macports::ui_prefix Found $num_broken_files broken file${s}, matching files to ports"
4776        set broken_ports {}
4777        set broken_files [lsort -unique $broken_files]
4778        foreach file $broken_files {
4779            set port [registry::entry owner $file]
4780            if {$port ne ""} {
4781                lappend broken_ports $port
4782                lappend broken_files_by_port($port) $file
4783            } else {
4784                ui_error "Broken file $file doesn't belong to any port."
4785            }
4786        }
4787        set broken_ports [lsort -unique $broken_ports]
4788
4789        if {$macports::revupgrade_mode eq "rebuild"} {
4790            # don't try to rebuild ports that don't exist in the tree
4791            set temp_broken_ports {}
4792            foreach port $broken_ports {
4793                set portname [$port name]
4794                if {[catch {mportlookup $portname} result]} {
4795                    ui_debug $::errorInfo
4796                    error "lookup of portname $portname failed: $result"
4797                }
4798                if {[llength $result] >= 2} {
4799                    lappend temp_broken_ports $port
4800                } else {
4801                    ui_warn "No port $portname found in the index; can't rebuild"
4802                }
4803            }
4804
4805            if {[llength $temp_broken_ports] == 0} {
4806                ui_msg "$macports::ui_prefix Broken files found, but all associated ports are not in the index and so cannot be rebuilt."
4807                return 0
4808            }
4809        } else {
4810            set temp_broken_ports $broken_ports
4811        }
4812
4813        set broken_ports {}
4814
4815        foreach port $temp_broken_ports {
4816            set portname [$port name]
4817
4818            if {![info exists broken_port_counts($portname)]} {
4819                set broken_port_counts($portname) 0
4820            }
4821            incr broken_port_counts($portname)
4822            if {$broken_port_counts($portname) > 3} {
4823                ui_error "Port $portname is still broken after rebuilding it more than 3 times."
4824                if {$fancy_output} {
4825                    ui_error "Please run port -d -y rev-upgrade and use the output to report a bug."
4826                }
4827                set rebuild_tries [expr {$broken_port_counts($portname) - 1}]
4828                set s [expr {$rebuild_tries == 1 ? "" : "s"}]
4829                error "Port $portname still broken after rebuilding $rebuild_tries time${s}"
4830            } elseif {$broken_port_counts($portname) > 1 && [global_option_isset ports_binary_only]} {
4831                error "Port $portname still broken after reinstalling -- can't rebuild due to binary-only mode"
4832            }
4833            lappend broken_ports $port
4834        }
4835        unset temp_broken_ports
4836
4837        set num_broken_ports [llength $broken_ports]
4838        set s [expr {$num_broken_ports == 1 ? "" : "s"}]
4839
4840        if {$macports::revupgrade_mode ne "rebuild"} {
4841            ui_msg "$macports::ui_prefix Found $num_broken_ports broken port${s}:"
4842            foreach port $broken_ports {
4843                ui_msg "     [$port name] @[$port version] [$port variants][$port negated_variants]"
4844                foreach f $broken_files_by_port($port) {
4845                    ui_msg "         $f"
4846                }
4847            }
4848            return 0
4849        }
4850
4851        ui_msg "$macports::ui_prefix Found $num_broken_ports broken port${s}, determining rebuild order"
4852        # broken_ports are the nodes in our graph
4853        # now we need adjacents
4854        foreach port $broken_ports {
4855            # initialize with empty list
4856            set adjlist($port) {}
4857            set revadjlist($port) {}
4858            ui_debug "Broken: [$port name]"
4859        }
4860
4861        array set visited {}
4862        foreach port $broken_ports {
4863            # stack of broken nodes we've come across
4864            set stack {}
4865            lappend stack $port
4866
4867            # build graph
4868            if {![info exists visited($port)]} {
4869                revupgrade_buildgraph $port stack adjlist revadjlist visited
4870            }
4871        }
4872
4873        set unsorted_ports $broken_ports
4874        set topsort_ports {}
4875        while {[llength $unsorted_ports] > 0} {
4876            set lowest_adj_number [llength $adjlist([lindex $unsorted_ports 0])]
4877            set lowest_adj_port [lindex $unsorted_ports 0]
4878
4879            foreach port $unsorted_ports {
4880                set len [llength $adjlist($port)]
4881                if {$len < $lowest_adj_number} {
4882                    set lowest_adj_port $port
4883                    set lowest_adj_number $len
4884                }
4885                if {$len == 0} {
4886                    # this node has no further dependencies
4887                    # add it to topsorted list
4888                    lappend topsort_ports $port
4889                    # remove from unsorted list
4890                    set index [lsearch -exact $unsorted_ports $port]
4891                    set unsorted_ports [lreplace $unsorted_ports $index $index]
4892
4893                    # remove edges
4894                    foreach target $revadjlist($port) {
4895                        set index [lsearch -exact $adjlist($target) $port]
4896                        set adjlist($target) [lreplace $adjlist($target) $index $index]
4897                    }
4898
4899                    break;
4900                }
4901            }
4902
4903            # if we arrive here and lowest_adj_number is larger than 0, then we
4904            # have a loop in the graph and need to break it somehow
4905            if {$lowest_adj_number > 0} {
4906                ui_debug "Breaking loop in dependency graph by starting with [$lowest_adj_port name], which has $lowest_adj_number dependencies"
4907                lappend topsort_ports $lowest_adj_port
4908
4909                set index [lsearch -exact $unsorted_ports $lowest_adj_port]
4910                set unsorted_ports [lreplace $unsorted_ports $index $index]
4911
4912                foreach target $revadjlist($port) {
4913                    set index [lsearch -exact $adjlist($target) $lowest_adj_port]
4914                    set adjlist($target) [lreplace $adjlist($target) $index $index]
4915                }
4916            }
4917        }
4918
4919        set broken_portnames {}
4920        if {![info exists macports::ui_options(questions_yesno)]} {
4921            ui_msg "$macports::ui_prefix Rebuilding in order"
4922        }
4923        foreach port $topsort_ports {
4924            lappend broken_portnames [$port name]@[$port version][$port variants]
4925            if {![info exists macports::ui_options(questions_yesno)]} {
4926                ui_msg "     [$port name] @[$port version] [$port variants][$port negated_variants]"
4927            }
4928        }
4929
4930        ##
4931        # User Interaction Question
4932        # Asking before rebuilding in rev-upgrade
4933        if {[info exists macports::ui_options(questions_yesno)]} {
4934            ui_msg "You can always run 'port rev-upgrade' again to fix errors."
4935            set retvalue [$macports::ui_options(questions_yesno) "The following ports will be rebuilt:" "TestCase#1" $broken_portnames {y} 0]
4936            if {$retvalue == 1} {
4937                # quit as user answered 'no'
4938                return 0
4939            }
4940            unset macports::ui_options(questions_yesno)
4941        }
4942
4943        # shared depscache for all ports that are going to be rebuilt
4944        array set depscache {}
4945        set status 0
4946        array set my_options [array get macports::global_options]
4947        set my_options(ports_revupgrade) yes
4948        foreach port $topsort_ports {
4949            set portname [$port name]
4950            if {![info exists depscache(port:$portname)]} {
4951                unset -nocomplain my_options(ports_revupgrade_second_run) \
4952                                  my_options(ports_nodeps)
4953                if {$broken_port_counts($portname) > 1} {
4954                    set my_options(ports_revupgrade_second_run) yes
4955
4956                    if {$broken_port_counts($portname) > 2} {
4957                        # runtime deps are upgraded the first time, build deps
4958                        # the second, so none left to do the third time
4959                        set my_options(ports_nodeps) yes
4960                    }
4961                }
4962
4963                # call macports::upgrade with ports_revupgrade option to rebuild the port
4964                set status [macports::upgrade $portname port:$portname \
4965                    {} [array get my_options] depscache]
4966                ui_debug "Rebuilding port $portname finished with status $status"
4967                if {$status != 0} {
4968                    error "Error rebuilding $portname"
4969                }
4970            }
4971        }
4972
4973        if {[info exists options(ports_dryrun)] && $options(ports_dryrun)} {
4974            ui_warn "If this was no dry run, rev-upgrade would now run the checks again to find unresolved and newly created problems"
4975            return 0
4976        }
4977        return 1
4978    }
4979
4980    return 0
4981}
4982
4983# Return whether a path is in the macports prefix
4984# Usage: path_is_in_prefix path_to_test
4985# Returns true if the path is in the prefix, false otherwise
4986proc macports::path_is_in_prefix {path} {
4987    global macports::prefix macports::applications_dir
4988    if {[string first $macports::prefix $path] == 0} {
4989        return yes
4990    }
4991    if {[string first $macports::applications_dir $path] == 0} {
4992        return yes
4993    }
4994    return no
4995}
4996
4997# Function to replace macros in loadcommand paths with their proper values (which are usually determined at load time)
4998# Usage: revupgrade_handle_special_paths name_of_file path_from_loadcommand
4999# Returns the corrected path on success or an error in case of failure.
5000# Note that we can't reliably replace @executable_path, because it's only clear when executing a file where it was executed from.
5001# Replacing @rpath does not work yet, but it might be possible to get it working using the rpath attribute in the file containing the
5002# loadcommand
5003proc macports::revupgrade_handle_special_paths {fname path} {
5004    set corrected_path $path
5005
5006    set loaderpath_idx [string first @loader_path $corrected_path]
5007    if {$loaderpath_idx != -1} {
5008        set corrected_path [string replace $corrected_path $loaderpath_idx ${loaderpath_idx}+11 [file dirname $fname]]
5009    }
5010
5011    set executablepath_idx [string first @executable_path $corrected_path]
5012    if {$executablepath_idx != -1} {
5013        ui_debug "Ignoring loadcommand containing @executable_path in $fname"
5014        error "@executable_path in loadcommand"
5015    }
5016
5017    set rpath_idx [string first @rpath $corrected_path]
5018    if {$rpath_idx != -1} {
5019        ui_debug "Ignoring loadcommand containing @rpath in $fname"
5020        error "@rpath in loadcommand"
5021    }
5022
5023    return $corrected_path
5024}
5025
5026# Recursively build the dependency graph between broken ports
5027# Usage: revupgrade_buildgraph start_port name_of_stack name_of_adjacency_list name_of_reverse_adjacency_list name_of_visited_map
5028proc macports::revupgrade_buildgraph {port stackname adjlistname revadjlistname visitedname} {
5029    upvar $stackname stack
5030    upvar $adjlistname adjlist
5031    upvar $revadjlistname revadjlist
5032    upvar $visitedname visited
5033
5034    set visited($port) true
5035
5036    ui_debug "Processing port [$port name] @[$port epoch]:[$port version]_[$port revision] [$port variants] [$port negated_variants]"
5037    set dependent_ports [$port dependents]
5038    foreach dep $dependent_ports {
5039        set is_broken_port false
5040
5041        if {[info exists adjlist($dep)]} {
5042            ui_debug "Dependent [$dep name] is broken, adding edge from [$dep name] to [[lindex $stack 0] name]"
5043            ui_debug "Making [$dep name] new head of stack"
5044            # $dep is one of the broken ports
5045            # add an edge to the last broken port in the DFS
5046            lappend revadjlist([lindex $stack 0]) $dep
5047            lappend adjlist($dep) [lindex $stack 0]
5048            # make this port the new last broken port by prepending it to the stack
5049            set stack [linsert $stack 0 $dep]
5050
5051            set is_broken_port true
5052        }
5053        if {![info exists visited($dep)]} {
5054            revupgrade_buildgraph $dep stack adjlist revadjlist visited
5055        }
5056        if {$is_broken_port} {
5057            ui_debug "Removing [$dep name] from stack"
5058            # remove $dep from the stack
5059            set stack [lrange $stack 1 end]
5060        }
5061    }
5062}
5063
5064# get cached ping time for host, modified by blacklist and preferred list
5065proc macports::get_pingtime {host} {
5066    global macports::ping_cache macports::host_blacklisted macports::host_preferred
5067    if {[info exists host_blacklisted($host)]} {
5068        return -1
5069    } elseif {[info exists host_preferred($host)]} {
5070        return 1
5071    } elseif {[info exists ping_cache($host)]} {
5072        # expire entries after 1 day
5073        if {[clock seconds] - [lindex $ping_cache($host) 1] <= 86400} {
5074            return [lindex $ping_cache($host) 0]
5075        }
5076    }
5077    return {}
5078}
5079
5080# cache a ping time of ms for host
5081proc macports::set_pingtime {host ms} {
5082    global macports::ping_cache
5083    set ping_cache($host) [list $ms [clock seconds]]
5084}
5085
5086# read and cache archive_sites.conf (called from port1.0 code)
5087proc macports::get_archive_sites_conf_values {} {
5088    global macports::archive_sites_conf_values macports::autoconf::macports_conf_path
5089    if {![info exists archive_sites_conf_values]} {
5090        set archive_sites_conf_values {}
5091        set all_names {}
5092        array set defaults {applications_dir /Applications/MacPorts prefix /opt/local type tbz2}
5093        set conf_file ${macports_conf_path}/archive_sites.conf
5094        set conf_options {applications_dir frameworks_dir name prefix type urls}
5095        if {[file isfile $conf_file]} {
5096            set fd [open $conf_file r]
5097            while {[gets $fd line] >= 0} {
5098                if {[regexp {^(\w+)([ \t]+(.*))?$} $line match option ignore val] == 1} {
5099                    if {$option in $conf_options} {
5100                        if {$option eq "name"} {
5101                            set cur_name $val
5102                            lappend all_names $val
5103                        } elseif {[info exists cur_name]} {
5104                            set trimmedval [string trim $val]
5105                            if {$option eq "urls"} {
5106                                set processed_urls {}
5107                                foreach url $trimmedval {
5108                                    lappend processed_urls ${url}:nosubdir
5109                                }
5110                                lappend archive_sites_conf_values portfetch::mirror_sites::sites($cur_name) $processed_urls
5111                                set sites($cur_name) $processed_urls
5112                            } else {
5113                                lappend archive_sites_conf_values portfetch::mirror_sites::archive_${option}($cur_name) $trimmedval
5114                                set archive_${option}($cur_name) $trimmedval
5115                            }
5116                        } else {
5117                            ui_warn "archive_sites.conf: ignoring '$option' occurring before name"
5118                        }
5119                    } else {
5120                        ui_warn "archive_sites.conf: ignoring unknown key '$option'"
5121                    }
5122                }
5123            }
5124            close $fd
5125
5126            # check for unspecified values and set to defaults
5127            foreach cur_name $all_names {
5128                foreach key [array names defaults] {
5129                    if {![info exists archive_${key}($cur_name)]} {
5130                        set archive_${key}($cur_name) $defaults($key)
5131                        lappend archive_sites_conf_values portfetch::mirror_sites::archive_${key}($cur_name) $defaults($key)
5132                    }
5133                }
5134                if {![info exists archive_frameworks_dir($cur_name)]} {
5135                    set archive_frameworks_dir($cur_name) $archive_prefix($cur_name)/Library/Frameworks
5136                    lappend archive_sites_conf_values portfetch::mirror_sites::archive_frameworks_dir($cur_name) $archive_frameworks_dir($cur_name)
5137                }
5138                if {![info exists sites($cur_name)]} {
5139                    ui_warn "archive_sites.conf: no urls set for $cur_name"
5140                    set sites($cur_name) {}
5141                    lappend archive_sites_conf_values portfetch::mirror_sites::sites($cur_name) {}
5142                }
5143            }
5144        }
5145    }
5146    return $archive_sites_conf_values
5147}
5148
5149##
5150# Escape a string for use in a POSIX shell, e.g., when passing it to the \c system Pextlib extension. This is necessary
5151# to handle cases such as group names with backslashes correctly. See #43875 for an example of a problem caused by
5152# missing quotes.
5153#
5154# @param arg The argument that should be escaped for use in a POSIX shell
5155# @return A quoted version of the argument
5156proc macports::shellescape {arg} {
5157    set mapping {}
5158    # Replace each backslash by a double backslash. Apparently Bash treats Backslashes in single-quoted strings
5159    # differently depending on whether is was invoked as sh or bash: echo 'using \backslashes' preserves the backslash
5160    # in bash mode, but interprets it in sh mode. Since the `system' command uses sh, escape backslashes.
5161    lappend mapping "\\" "\\\\"
5162    # Replace each single quote with a single quote (closing the currently open string), an escaped single quote \'
5163    # (additional backslash needed to escape the backslash in Tcl), and another single quote (opening a new quoted
5164    # string).
5165    lappend mapping "'" "'\\''"
5166
5167    # Add a single quote at the start, escape all single quotes in the argument, and add a single quote at the end
5168    return "'[string map $mapping $arg]'"
5169}
Note: See TracBrowser for help on using the repository browser.