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

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

base: remove all references to macports_tcl_dir, install Tcl packages in TCL_PACKAGE_PATH directly, fixes #43208

This completely removes the need for macports_fastload.tcl and all the other
hacks that MacPorts has been using to locate its own packages (such as appending
to $auto_path during mportinit and preloading some packages).

Note: ${TCL_PACKAGE_PATH}/macports1.0 previously was a symlink and now is
a directory. When the symlink isn't removed before trying to create the
directory, this causes a failure. The Makefile now ensures this doesn't happen,
but I don't know what installer(8) will do in this case. This means we might
have to add delete the symlink in installer preflight.

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