New Ticket     Wiki     Browse Source     Timeline     Roadmap     Ticket Reports     Search

root/trunk/base/src/macports1.0/macports.tcl

Revision 93153, 186.9 KB (checked in by jmr@…, 22 hours ago)

don't override source-only option for first rev-upgrade rebuild, and give up after one reinstall if binary-only is set

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