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

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

Merge from trunk.

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