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

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

base: Delay displaying notes for installed ports until the end of the current operation

Mostly written by Jeremy Lavergne (snc).

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