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

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

macports1.0: Handle multi-line messages for log file

For log messages with mulitple lines, repeat log level and phase for each line.
Assume that messages with -nonewline actually do not contain multiple lines.

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