source: branches/gsoc13-tests/src/macports1.0/macports.tcl @ 110019

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

macports.tcl: fixed - rename puts - line

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