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

Last change on this file since 54352 was 54352, checked in by raimue@…, 10 years ago

macports1.0/macports.tcl:
Make the skip message output for non-matching variants more readable with
quotation marks when one of the variant sets is empty.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 104.9 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 54352 2009-07-25 09:15:42Z raimue@macports.org $
4#
5# Copyright (c) 2002 Apple Computer, 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# All rights reserved.
10#
11# Redistribution and use in source and binary forms, with or without
12# modification, are permitted provided that the following conditions
13# are met:
14# 1. Redistributions of source code must retain the above copyright
15#    notice, this list of conditions and the following disclaimer.
16# 2. Redistributions in binary form must reproduce the above copyright
17#    notice, this list of conditions and the following disclaimer in the
18#    documentation and/or other materials provided with the distribution.
19# 3. Neither the name of Apple Computer, Inc. nor the names of its contributors
20#    may be used to endorse or promote products derived from this software
21#    without specific prior written permission.
22#
23# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33# POSSIBILITY OF SUCH DAMAGE.
34#
35package provide macports 1.0
36package require macports_dlist 1.0
37package require macports_index 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
42    variable bootstrap_options "\
43        portdbpath libpath binpath auto_path extra_env sources_conf prefix portdbformat \
44        portinstalltype portarchivemode portarchivepath portarchivetype portautoclean \
45        porttrace portverbose destroot_umask variants_conf rsync_server rsync_options \
46        rsync_dir startupitem_type place_worksymlink xcodeversion xcodebuildcmd \
47        mp_remote_url mp_remote_submit_url configureccache configuredistcc configurepipe buildnicevalue buildmakejobs \
48        applications_dir frameworks_dir developer_dir universal_archs build_arch \
49        macportsuser proxy_override_env proxy_http proxy_https proxy_ftp proxy_rsync proxy_skip"
50    variable user_options "submitter_name submitter_email submitter_key"
51    variable portinterp_options "\
52        portdbpath porturl portpath portbuildpath auto_path prefix prefix_frozen portsharepath \
53        registry.path registry.format registry.installtype portarchivemode portarchivepath \
54        portarchivetype portautoclean porttrace portverbose destroot_umask rsync_server \
55        rsync_options rsync_dir startupitem_type place_worksymlink \
56        mp_remote_url mp_remote_submit_url configureccache configuredistcc configurepipe buildnicevalue buildmakejobs \
57        applications_dir frameworks_dir developer_dir universal_archs build_arch $user_options"
58
59    # deferred options are only computed when needed.
60    # they are not exported to the trace thread.
61    # they are not exported to the interpreter in system_options array.
62    variable portinterp_deferred_options "xcodeversion xcodebuildcmd"
63
64    variable open_mports {}
65
66    variable ui_priorities "debug info msg error warn"
67}
68
69# Provided UI instantiations
70# For standard messages, the following priorities are defined
71#     debug, info, msg, warn, error
72# Clients of the library are expected to provide ui_prefix and ui_channels with
73# the following prototypes.
74#     proc ui_prefix {priority}
75#     proc ui_channels {priority}
76# ui_prefix returns the prefix for the messages, if any.
77# ui_channels returns a list of channels to output the message to, empty for
78#     no message.
79# if these functions are not provided, defaults are used.
80# Clients of the library may optionally provide ui_init with the following
81# prototype.
82#     proc ui_init {priority prefix channels message}
83# ui_init needs to correctly define the proc ::ui_$priority {message} or throw
84# an error.
85# if this function is not provided or throws an error, default procedures for
86# ui_$priority are defined.
87
88# ui_options accessor
89proc macports::ui_isset {val} {
90    if {[info exists macports::ui_options($val)]} {
91        if {$macports::ui_options($val) == "yes"} {
92            return 1
93        }
94    }
95    return 0
96}
97
98
99# global_options accessor
100proc macports::global_option_isset {val} {
101    if {[info exists macports::global_options($val)]} {
102        if {$macports::global_options($val) == "yes"} {
103            return 1
104        }
105    }
106    return 0
107}
108
109
110proc macports::ui_init {priority args} {
111    # Get the list of channels.
112    if {[llength [info commands ui_channels]] > 0} {
113        set channels [ui_channels $priority]
114    } else {
115        set channels [ui_channels_default $priority]
116    }
117
118    # Simplify ui_$priority.
119    set nbchans [llength $channels]
120    if {$nbchans == 0} {
121        proc ::ui_$priority {args} {}
122    } else {
123        if {[llength [info commands ui_prefix]] > 0} {
124            set prefix [ui_prefix $priority]
125        } else {
126            set prefix [ui_prefix_default $priority]
127        }
128
129        if {[llength [info commands ::ui_init]] > 0} {
130            eval ::ui_init $priority $prefix $channels $args
131        } else {
132            if {$nbchans == 1} {
133                set chan [lindex $channels 0]
134                proc ::ui_$priority {args} [subst {
135                    if {\[lindex \$args 0\] == "-nonewline"} {
136                        puts -nonewline $chan "$prefix\[lindex \$args 1\]"
137                    } else {
138                        puts $chan "$prefix\[lindex \$args 0\]"
139                    }
140                }]
141            } else {
142                proc ::ui_$priority {args} [subst {
143                    foreach chan \$channels {
144                        if {\[lindex \$args 0\] == "-nonewline"} {
145                            puts -nonewline $chan "$prefix\[lindex \$args 1\]"
146                        } else {
147                            puts $chan "$prefix\[lindex \$args 0\]"
148                        }
149                    }
150                }]
151            }
152        }
153
154        # Call ui_$priority
155        eval ::ui_$priority $args
156    }
157}
158
159# Default implementation of ui_prefix
160proc macports::ui_prefix_default {priority} {
161    switch $priority {
162        debug {
163            return "DEBUG: "
164        }
165        error {
166            return "Error: "
167        }
168        warn {
169            return "Warning: "
170        }
171        default {
172            return ""
173        }
174    }
175}
176
177# Default implementation of ui_channels:
178# ui_options(ports_debug) - If set, output debugging messages
179# ui_options(ports_verbose) - If set, output info messages (ui_info)
180# ui_options(ports_quiet) - If set, don't output "standard messages"
181proc macports::ui_channels_default {priority} {
182    switch $priority {
183        debug {
184            if {[ui_isset ports_debug]} {
185                return {stderr}
186            } else {
187                return {}
188            }
189        }
190        info {
191            if {[ui_isset ports_verbose]} {
192                return {stdout}
193            } else {
194                return {}
195            }
196        }
197        msg {
198            if {[ui_isset ports_quiet]} {
199                return {}
200            } else {
201                return {stdout}
202            }
203        }
204        warn -
205        error {
206            return {stderr}
207        }
208        default {
209            return {stdout}
210        }
211    }
212}
213
214foreach priority ${macports::ui_priorities} {
215    proc ui_$priority {args} [subst { eval macports::ui_init $priority \$args }]
216}
217
218# Replace puts to catch errors (typically broken pipes when being piped to head)
219rename puts tcl::puts
220proc puts {args} {
221    catch "tcl::puts $args"
222}
223
224# find a binary either in a path defined at MacPorts' configuration time
225# or in the PATH environment variable through macports::binaryInPath (fallback)
226proc macports::findBinary {prog {autoconf_hint ""}} {
227    if {${autoconf_hint} != "" && [file executable ${autoconf_hint}]} {
228        return ${autoconf_hint}
229    } else {
230        if {[catch {set cmd_path [macports::binaryInPath ${prog}]} result] == 0} {
231            return ${cmd_path}
232        } else {
233            return -code error "${result} or at its MacPorts configuration time location, did you move it?"
234        }
235    }
236}
237
238# check for a binary in the path
239# returns an error code if it cannot be found
240proc macports::binaryInPath {prog} {
241    global env
242    foreach dir [split $env(PATH) :] {
243        if {[file executable [file join $dir $prog]]} {
244            return [file join $dir $prog]
245        }
246    }
247    return -code error [format [msgcat::mc "Failed to locate '%s' in path: '%s'"] $prog $env(PATH)];
248}
249
250# deferred option processing
251proc macports::getoption {name} {
252    global macports::$name
253    return [expr $$name]
254}
255
256# deferred and on-need extraction of xcodeversion and xcodebuildcmd.
257proc macports::setxcodeinfo {name1 name2 op} {
258    global macports::xcodeversion
259    global macports::xcodebuildcmd
260
261    trace remove variable macports::xcodeversion read macports::setxcodeinfo
262    trace remove variable macports::xcodebuildcmd read macports::setxcodeinfo
263
264    if {[catch {set xcodebuild [binaryInPath "xcodebuild"]}] == 0} {
265        if {![info exists xcodeversion]} {
266            # Determine xcode version (<= 2.0 or 2.1)
267            if {[catch {set xcodebuildversion [exec xcodebuild -version]}] == 0} {
268                if {[regexp "DevToolsCore-(.*); DevToolsSupport-(.*)" $xcodebuildversion devtoolscore_v devtoolssupport_v] == 1} {
269                    if {$devtoolscore_v >= 620.0 && $devtoolssupport_v >= 610.0} {
270                        # for now, we don't need to distinguish 2.1 from 2.1 or higher.
271                        set macports::xcodeversion "2.1"
272                    } else {
273                        set macports::xcodeversion "2.0orlower"
274                    }
275                } else {
276                    set macports::xcodeversion "2.0orlower"
277                }
278            } else {
279                set macports::xcodeversion "2.0orlower"
280            }
281        }
282
283        if {![info exists xcodebuildcmd]} {
284            set macports::xcodebuildcmd "xcodebuild"
285        }
286    } elseif {[catch {set pbxbuild [binaryInPath "pbxbuild"]}] == 0} {
287        if {![info exists xcodeversion]} {
288            set macports::xcodeversion "pb"
289        }
290        if {![info exists xcodebuildcmd]} {
291            set macports::xcodebuildcmd "pbxbuild"
292        }
293    } else {
294        if {![info exists xcodeversion]} {
295            set macports::xcodeversion "none"
296        }
297        if {![info exists xcodebuildcmd]} {
298            set macports::xcodebuildcmd "none"
299        }
300    }
301}
302
303proc mportinit {{up_ui_options {}} {up_options {}} {up_variations {}}} {
304    if {$up_ui_options eq ""} {
305        array set macports::ui_options {}
306    } else {
307        upvar $up_ui_options temp_ui_options
308        array set macports::ui_options [array get temp_ui_options]
309    }
310    if {$up_options eq ""} {
311        array set macports::global_options {}
312    } else {
313        upvar $up_options temp_options
314        array set macports::global_options [array get temp_options]
315    }
316    if {$up_variations eq ""} {
317        array set variations {}
318    } else {
319        upvar $up_variations variations
320    }
321
322    global auto_path env tcl_platform
323    global macports::autoconf::macports_conf_path
324    global macports::macports_user_dir
325    global macports::bootstrap_options
326    global macports::user_options
327    global macports::extra_env
328    global macports::portconf
329    global macports::portdbpath
330    global macports::portsharepath
331    global macports::registry.format
332    global macports::registry.path
333    global macports::sources
334    global macports::sources_default
335    global macports::sources_conf
336    global macports::destroot_umask
337    global macports::libpath
338    global macports::prefix
339    global macports::macportsuser
340    global macports::prefix_frozen
341    global macports::registry.installtype
342    global macports::rsync_dir
343    global macports::rsync_options
344    global macports::rsync_server
345    global macports::variants_conf
346    global macports::xcodebuildcmd
347    global macports::xcodeversion
348    global macports::configureccache
349    global macports::configuredistcc
350    global macports::configurepipe
351    global macports::buildnicevalue
352    global macports::buildmakejobs
353    global macports::universal_archs
354    global macports::build_arch
355
356    # Set the system encoding to utf-8
357    encoding system utf-8
358
359    # Ensure that the macports user directory exists if HOME is defined
360    if {[info exists env(HOME)]} {
361        set macports::macports_user_dir [file normalize $macports::autoconf::macports_user_dir]
362    } else {
363        # Otherwise define the user directory as a direcotory that will never exist
364        set macports::macports_user_dir "/dev/null/NO_HOME_DIR"
365    }
366
367    # Configure the search path for configuration files
368    set conf_files ""
369    lappend conf_files "${macports_conf_path}/macports.conf"
370    if { [file isdirectory $macports_user_dir] } {
371        lappend conf_files "${macports_user_dir}/macports.conf"
372    }
373    if {[info exists env(PORTSRC)]} {
374        set PORTSRC $env(PORTSRC)
375        lappend conf_files ${PORTSRC}
376    }
377
378    # Process the first configuration file we find on conf_files list
379    foreach file $conf_files {
380        if [file exists $file] {
381            set portconf $file
382            set fd [open $file r]
383            while {[gets $fd line] >= 0} {
384                if {[regexp {^(\w+)([ \t]+(.*))?$} $line match option ignore val] == 1} {
385                    if {[lsearch $bootstrap_options $option] >= 0} {
386                        set macports::$option [string trim $val]
387                        global macports::$option
388                    }
389                }
390            }
391            close $fd
392        }
393    }
394
395    # Process per-user only settings
396    set per_user "${macports_user_dir}/user.conf"
397    if [file exists $per_user] {
398        set fd [open $per_user r]
399        while {[gets $fd line] >= 0} {
400            if {[regexp {^(\w+)([ \t]+(.*))?$} $line match option ignore val] == 1} {
401                if {[lsearch $user_options $option] >= 0} {
402                    set macports::$option $val
403                    global macports::$option
404                }
405            }
406        }
407        close $fd
408    }
409
410    if {![info exists sources_conf]} {
411        return -code error "sources_conf must be set in ${macports_conf_path}/macports.conf or in your ${macports_user_dir}/macports.conf file"
412    }
413    set fd [open $sources_conf r]
414    while {[gets $fd line] >= 0} {
415        set line [string trimright $line]
416        if {![regexp {^\s*#|^$} $line]} {
417            if {[regexp {^([\w-]+://\S+)(?:\s+\[(\w+(?:,\w+)*)\])?$} $line _ url flags]} {
418                set flags [split $flags ,]
419                foreach flag $flags {
420                    if {[lsearch -exact [list nosync default] $flag] == -1} {
421                        ui_warn "$sources_conf source '$line' specifies invalid flag '$flag'"
422                    }
423                    if {$flag == "default"} {
424                        if {[info exists sources_default]} {
425                            ui_warn "More than one default port source is defined."
426                        }
427                        set sources_default [concat [list $url] $flags]
428                    }
429                }
430                lappend sources [concat [list $url] $flags]
431            } else {
432                ui_warn "$sources_conf specifies invalid source '$line', ignored."
433            }
434        }
435    }
436    close $fd
437    # Make sure the default port source is defined. Otherwise
438    # [macports::getportresourcepath] fails when the first source doesn't
439    # contain _resources.
440    if {![info exists sources_default]} {
441        ui_warn "No default port source specified in $sources_conf, using last source as default"
442        set sources_default [lindex $sources end]
443    }
444
445    if {![info exists sources]} {
446        if {[file isdirectory ports]} {
447            set sources "file://[pwd]/ports"
448        } else {
449            return -code error "No sources defined in $sources_conf"
450        }
451    }
452
453    if {[info exists variants_conf]} {
454        if {[file exist $variants_conf]} {
455            set fd [open $variants_conf r]
456            while {[gets $fd line] >= 0} {
457                set line [string trimright $line]
458                if {![regexp {^[\ \t]*#.*$|^$} $line]} {
459                    foreach arg [split $line " \t"] {
460                        if {[regexp {^([-+])([-A-Za-z0-9_+\.]+)$} $arg match sign opt] == 1} {
461                            if {![info exists variations($opt)]} {
462                                set variations($opt) $sign
463                            }
464                        } else {
465                            ui_warn "$variants_conf specifies invalid variant syntax '$arg', ignored."
466                        }
467                    }
468                }
469            }
470            close $fd
471        } else {
472            ui_debug "$variants_conf does not exist, variants_conf setting ignored."
473        }
474    }
475
476    if {![info exists portdbpath]} {
477        return -code error "portdbpath must be set in ${macports_conf_path}/macports.conf or in your ${macports_user_dir}/macports.conf"
478    }
479    if {![file isdirectory $portdbpath]} {
480        if {![file exists $portdbpath]} {
481            if {[catch {file mkdir $portdbpath} result]} {
482                return -code error "portdbpath $portdbpath does not exist and could not be created: $result"
483            }
484        } else {
485            return -code error "$portdbpath is not a directory. Please create the directory $portdbpath and try again"
486        }
487    }
488
489    set registry.path $portdbpath
490
491    # Format for receipts, can currently be either "flat" or "sqlite"
492    if {[info exists portdbformat]} {
493        if { $portdbformat == "sqlite" } {
494            return -code error "SQLite is not yet supported for registry storage."
495        }
496        set registry.format receipt_${portdbformat}
497    } else {
498        set registry.format receipt_flat
499    }
500
501    # Installation type, whether to use port "images" or install "direct"
502    if {[info exists portinstalltype]} {
503        set registry.installtype $portinstalltype
504    } else {
505        set registry.installtype image
506    }
507
508    # Autoclean mode, whether to automatically call clean after "install"
509    if {![info exists portautoclean]} {
510        set macports::portautoclean "yes"
511        global macports::portautoclean
512    }
513    # Check command line override for autoclean
514    if {[info exists macports::global_options(ports_autoclean)]} {
515        if {![string equal $macports::global_options(ports_autoclean) $portautoclean]} {
516            set macports::portautoclean $macports::global_options(ports_autoclean)
517        }
518    }
519    # Trace mode, whether to use darwintrace to debug ports.
520    if {![info exists porttrace]} {
521        set macports::porttrace "no"
522        global macports::porttrace
523    }
524    # Check command line override for trace
525    if {[info exists macports::global_options(ports_trace)]} {
526        if {![string equal $macports::global_options(ports_trace) $porttrace]} {
527            set macports::porttrace $macports::global_options(ports_trace)
528        }
529    }
530
531    # Duplicate prefix into prefix_frozen, so that port actions
532    # can always get to the original prefix, even if a portfile overrides prefix
533    set macports::prefix_frozen $prefix
534
535    # Export verbosity.
536    if {![info exists portverbose]} {
537        set macports::portverbose "no"
538        global macports::portverbose
539    }
540    if {[info exists macports::ui_options(ports_verbose)]} {
541        if {![string equal $macports::ui_options(ports_verbose) $portverbose]} {
542            set macports::portverbose $macports::ui_options(ports_verbose)
543        }
544    }
545
546    # Archive mode, whether to create/use binary archive packages
547    if {![info exists portarchivemode]} {
548        set macports::portarchivemode "yes"
549        global macports::portarchivemode
550    }
551
552    # Archive path, where to store/retrieve binary archive packages
553    if {![info exists portarchivepath]} {
554        set macports::portarchivepath [file join $portdbpath packages]
555        global macports::portarchivepath
556    }
557    if {$portarchivemode == "yes"} {
558        if {![file isdirectory $portarchivepath]} {
559            if {![file exists $portarchivepath]} {
560                if {[catch {file mkdir $portarchivepath} result]} {
561                    return -code error "portarchivepath $portarchivepath does not exist and could not be created: $result"
562                }
563            }
564        }
565        if {![file isdirectory $portarchivepath]} {
566            return -code error "$portarchivepath is not a directory. Please create the directory $portarchivepath and try again"
567        }
568    }
569
570    # Archive type, what type of binary archive to use (CPIO, gzipped
571    # CPIO, XAR, etc.)
572    if {![info exists portarchivetype]} {
573        set macports::portarchivetype "cpgz"
574        global macports::portarchivetype
575    }
576    # Convert archive type to a list for multi-archive support, colon or
577    # comma separators indicates to use multiple archive formats
578    # (reading and writing)
579    set macports::portarchivetype [split $portarchivetype {:,}]
580
581    # Set rync options
582    if {![info exists rsync_server]} {
583        set macports::rsync_server rsync.macports.org
584        global macports::rsync_server
585    }
586    if {![info exists rsync_dir]} {
587        set macports::rsync_dir release/base/
588        global macports::rsync_dir
589    }
590    if {![info exists rsync_options]} {
591        set rsync_options "-rtzv --delete-after"
592        global macports::rsync_options
593    }
594
595    set portsharepath ${prefix}/share/macports
596    if {![file isdirectory $portsharepath]} {
597        return -code error "Data files directory '$portsharepath' must exist"
598    }
599
600    if {![info exists libpath]} {
601        set libpath "${prefix}/share/macports/Tcl"
602    }
603
604    if {![info exists binpath]} {
605        set env(PATH) "${prefix}/bin:${prefix}/sbin:/bin:/sbin:/usr/bin:/usr/sbin"
606    } else {
607        set env(PATH) "$binpath"
608    }
609
610    # Set startupitem default type (can be overridden by portfile)
611    if {![info exists macports::startupitem_type]} {
612        set macports::startupitem_type "default"
613    }
614
615    # Default place_worksymlink
616    if {![info exists macports::place_worksymlink]} {
617        set macports::place_worksymlink yes
618    }
619
620    # Default mp remote options
621    if {![info exists macports::mp_remote_url]} {
622        set macports::mp_remote_url "http://db.macports.org"
623    }
624    if {![info exists macports::mp_remote_submit_url]} {
625        set macports::mp_remote_submit_url "${macports::mp_remote_url}/submit"
626    }
627
628    # Default mp configure options
629    if {![info exists macports::configureccache]} {
630        set macports::configureccache no
631    }
632    if {![info exists macports::configuredistcc]} {
633        set macports::configuredistcc no
634    }
635    if {![info exists macports::configurepipe]} {
636        set macports::configurepipe no
637    }
638
639    # Default mp build options
640    if {![info exists macports::buildnicevalue]} {
641        set macports::buildnicevalue 0
642    }
643    if {![info exists macports::buildmakejobs]} {
644        set macports::buildmakejobs 0
645    }
646   
647    # Default Xcode Tools path
648    if {![info exists macports::developer_dir]} {
649        set macports::developer_dir "/Developer"
650    }
651
652    # Default mp universal options
653    if {![info exists macports::universal_archs]} {
654        if {[lindex [split $tcl_platform(osVersion) .] 0] >= 10} {
655            set macports::universal_archs {x86_64 i386}
656        } else {
657            set macports::universal_archs {i386 ppc}
658        }
659    }
660   
661    # Default arch to build for
662    if {![info exists macports::build_arch]} {
663        if {$tcl_platform(os) == "Darwin"} {
664            if {[lindex [split $tcl_platform(osVersion) .] 0] >= 10} {
665                if {[sysctl hw.cpu64bit_capable] == 1} {
666                    set macports::build_arch x86_64
667                } else {
668                    set macports::build_arch i386
669                }
670            } else {
671                if {$tcl_platform(machine) == "Power Macintosh"} {
672                    set macports::build_arch ppc
673                } else {
674                    set macports::build_arch i386
675                }
676            }
677        } else {
678            set macports::build_arch ""
679        }
680    }
681
682    # ENV cleanup.
683    set keepenvkeys {
684        DISPLAY DYLD_FALLBACK_FRAMEWORK_PATH
685        DYLD_FALLBACK_LIBRARY_PATH DYLD_FRAMEWORK_PATH
686        DYLD_LIBRARY_PATH DYLD_INSERT_LIBRARIES
687        HOME JAVA_HOME MASTER_SITE_LOCAL
688        PATCH_SITE_LOCAL PATH PORTSRC RSYNC_PROXY TMP TMPDIR
689        USER GROUP
690        http_proxy HTTPS_PROXY FTP_PROXY ALL_PROXY NO_PROXY
691        COLUMNS LINES
692    }
693    if {[info exists extra_env]} {
694        set keepenvkeys [concat ${keepenvkeys} ${extra_env}]
695    }
696
697    foreach envkey [array names env] {
698        if {[lsearch $keepenvkeys $envkey] == -1} {
699            array unset env $envkey
700        }
701    }
702
703    if {![info exists xcodeversion] || ![info exists xcodebuildcmd]} {
704        # We'll resolve these later (if needed)
705        trace add variable macports::xcodeversion read macports::setxcodeinfo
706        trace add variable macports::xcodebuildcmd read macports::setxcodeinfo
707    }
708
709    # Set the default umask
710    if {![info exists destroot_umask]} {
711        set destroot_umask 022
712    }
713
714    if {[info exists master_site_local] && ![info exists env(MASTER_SITE_LOCAL)]} {
715        set env(MASTER_SITE_LOCAL) "$master_site_local"
716    }
717
718    if {[file isdirectory $libpath]} {
719        lappend auto_path $libpath
720        set macports::auto_path $auto_path
721
722        # XXX: not sure if this the best place, but it needs to happen
723        # early, and after auto_path has been set.  Or maybe Pextlib
724        # should ship with macports1.0 API?
725        package require Pextlib 1.0
726        package require registry 1.0
727    } else {
728        return -code error "Library directory '$libpath' must exist"
729    }
730
731    # unset environment an extra time, to work around bugs in Leopard Tcl
732    foreach envkey [array names env] {
733        if {[lsearch $keepenvkeys $envkey] == -1} {
734            unsetenv $envkey
735        }
736    }
737
738    # Proxy handling (done this late since Pextlib is needed)
739    if {![info exists proxy_override_env] } {
740        set proxy_override_env "no"
741    }
742    array set sysConfProxies [get_systemconfiguration_proxies]
743    if {![info exists env(http_proxy)] || $proxy_override_env == "yes" } {
744        if {[info exists proxy_http]} {
745            set env(http_proxy) $proxy_http
746        } elseif {[info exists sysConfProxies(proxy_http)]} {
747            set env(http_proxy) $sysConfProxies(proxy_http)
748        }
749    }
750    if {![info exists env(HTTPS_PROXY)] || $proxy_override_env == "yes" } {
751        if {[info exists proxy_https]} {
752            set env(HTTPS_PROXY) $proxy_https
753        } elseif {[info exists sysConfProxies(proxy_https)]} {
754            set env(HTTPS_PROXY) $sysConfProxies(proxy_https)
755        }
756    }
757    if {![info exists env(FTP_PROXY)] || $proxy_override_env == "yes" } {
758        if {[info exists proxy_ftp]} {
759            set env(FTP_PROXY) $proxy_ftp
760        } elseif {[info exists sysConfProxies(proxy_ftp)]} {
761            set env(FTP_PROXY) $sysConfProxies(proxy_ftp)
762        }
763    }
764    if {![info exists env(RSYNC_PROXY)] || $proxy_override_env == "yes" } {
765        if {[info exists proxy_rsync]} {
766            set env(RSYNC_PROXY) $proxy_rsync
767        }
768    }
769    if {![info exists env(NO_PROXY)] || $proxy_override_env == "yes" } {
770        if {[info exists proxy_skip]} {
771            set env(NO_PROXY) $proxy_skip
772        } elseif {[info exists sysConfProxies(proxy_skip)]} {
773            set env(NO_PROXY) $sysConfProxies(proxy_skip)
774        }
775    }
776
777    # load the quick index
778    _mports_load_quickindex
779
780    set default_source_url [lindex ${sources_default} 0]
781    if {[macports::getprotocol $default_source_url] == "file"} {
782        set default_portindex [macports::getindex $default_source_url]
783        if {[file exists $default_portindex] && [expr [clock seconds] - [file mtime $default_portindex]] > 1209600} {
784            ui_warn "port definitions are more than two weeks old, consider using selfupdate"
785        }
786    }
787}
788
789proc macports::worker_init {workername portpath porturl portbuildpath options variations} {
790    global macports::portinterp_options macports::portinterp_deferred_options registry.installtype
791
792    # Hide any Tcl commands that should be inaccessible to port1.0 and Portfiles
793    # exit: It should not be possible to exit the interpreter
794    interp hide $workername exit
795
796    # cd: This is necessary for some code in port1.0, but should be hidden
797    interp eval $workername "rename cd _cd"
798
799    # Tell the sub interpreter about all the Tcl packages we already
800    # know about so it won't glob for packages.
801    foreach pkgName [package names] {
802        foreach pkgVers [package versions $pkgName] {
803            set pkgLoadScript [package ifneeded $pkgName $pkgVers]
804            $workername eval "package ifneeded $pkgName $pkgVers {$pkgLoadScript}"
805        }
806    }
807
808    # Create package require abstraction procedure
809    $workername eval "proc PortSystem \{version\} \{ \n\
810            package require port \$version \}"
811
812    # Clearly separate slave interpreters and the master interpreter.
813    $workername alias mport_exec mportexec
814    $workername alias mport_open mportopen
815    $workername alias mport_close mportclose
816    $workername alias mport_lookup mportlookup
817
818    # instantiate the UI call-backs
819    foreach priority ${macports::ui_priorities} {
820        $workername alias ui_$priority ui_$priority
821    }
822    $workername alias ui_prefix ui_prefix
823    $workername alias ui_channels ui_channels
824
825    # Export some utility functions defined here.
826    $workername alias macports_create_thread macports::create_thread
827    $workername alias getportworkpath_from_buildpath macports::getportworkpath_from_buildpath
828    $workername alias getportresourcepath macports::getportresourcepath
829    $workername alias getdefaultportresourcepath macports::getdefaultportresourcepath
830    $workername alias getprotocol macports::getprotocol
831    $workername alias getportdir macports::getportdir
832    $workername alias findBinary macports::findBinary
833    $workername alias binaryInPath macports::binaryInPath
834
835    # New Registry/Receipts stuff
836    $workername alias registry_new registry::new_entry
837    $workername alias registry_open registry::open_entry
838    $workername alias registry_write registry::write_entry
839    $workername alias registry_prop_store registry::property_store
840    $workername alias registry_prop_retr registry::property_retrieve
841    $workername alias registry_delete registry::delete_entry
842    $workername alias registry_exists registry::entry_exists
843    $workername alias registry_exists_for_name registry::entry_exists_for_name
844    $workername alias registry_activate portimage::activate
845    $workername alias registry_deactivate portimage::deactivate
846    $workername alias registry_register_deps registry::register_dependencies
847    $workername alias registry_fileinfo_for_index registry::fileinfo_for_index
848    $workername alias registry_bulk_register_files registry::register_bulk_files
849    $workername alias registry_installed registry::installed
850    $workername alias registry_active registry::active
851
852    # deferred options processing.
853    $workername alias getoption macports::getoption
854
855    foreach opt $portinterp_options {
856        if {![info exists $opt]} {
857            global macports::$opt
858        }
859        if {[info exists $opt]} {
860            $workername eval set system_options($opt) \{[set $opt]\}
861            $workername eval set $opt \{[set $opt]\}
862        }
863    }
864
865    foreach opt $portinterp_deferred_options {
866        global macports::$opt
867        # define the trace hook.
868        $workername eval \
869            "proc trace_$opt {name1 name2 op} { \n\
870                trace remove variable ::$opt read ::trace_$opt \n\
871                global $opt \n\
872                set $opt \[getoption $opt\] \n\
873            }"
874        # next access will actually define the variable.
875        $workername eval "trace add variable ::$opt read ::trace_$opt"
876        # define some value now
877        $workername eval set $opt "?"
878    }
879
880    foreach {opt val} $options {
881        $workername eval set user_options($opt) $val
882        $workername eval set $opt $val
883    }
884
885    foreach {var val} $variations {
886        $workername eval set variations($var) $val
887    }
888
889    if { [info exists registry.installtype] } {
890        $workername eval set installtype ${registry.installtype}
891    }
892}
893
894# Create a thread with most configuration options set.
895# The newly created thread is sent portinterp_options vars and knows where to
896# find all packages we know.
897proc macports::create_thread {} {
898    package require Thread
899
900    global macports::portinterp_options
901
902    # Create the thread.
903    set result [thread::create -preserved {thread::wait}]
904
905    # Tell the thread about all the Tcl packages we already
906    # know about so it won't glob for packages.
907    foreach pkgName [package names] {
908        foreach pkgVers [package versions $pkgName] {
909            set pkgLoadScript [package ifneeded $pkgName $pkgVers]
910            thread::send -async $result "package ifneeded $pkgName $pkgVers {$pkgLoadScript}"
911        }
912    }
913
914    # inherit configuration variables.
915    thread::send -async $result "namespace eval macports {}"
916    foreach opt $portinterp_options {
917        if {![info exists $opt]} {
918            global macports::$opt
919        }
920        if {[info exists $opt]} {
921            thread::send -async $result "global macports::$opt"
922            set val [set macports::$opt]
923            thread::send -async $result "set macports::$opt \"$val\""
924        }
925    }
926
927    return $result
928}
929
930proc macports::fetch_port {url} {
931    global macports::portdbpath tcl_platform
932    set fetchdir [file join $portdbpath portdirs]
933    set fetchfile [file tail $url]
934    file mkdir $fetchdir
935    if {![file writable $fetchdir]} {
936        return -code error "Port remote fetch failed: You do not have permission to write to $fetchdir"
937    }
938    if {[catch {curl fetch $url [file join $fetchdir $fetchfile]} result]} {
939        return -code error "Port remote fetch failed: $result"
940    }
941    cd $fetchdir
942    if {[catch {exec [findBinary tar $macports::autoconf::tar_path] -zxf $fetchfile} result]} {
943        return -code error "Port extract failed: $result"
944    }
945    if {[regexp {(.+).tgz} $fetchfile match portdir] != 1} {
946        return -code error "Can't decipher portdir from $fetchfile"
947    }
948    return [file join $fetchdir $portdir]
949}
950
951proc macports::getprotocol {url} {
952    if {[regexp {(?x)([^:]+)://.+} $url match protocol] == 1} {
953        return ${protocol}
954    } else {
955        return -code error "Can't parse url $url"
956    }
957}
958
959# XXX: this really needs to be rethought in light of the remote index
960# I've added the destdir parameter.  This is the location a remotely
961# fetched port will be downloaded to (currently only applies to
962# mports:// sources).
963proc macports::getportdir {url {destdir "."}} {
964    if {[regexp {(?x)([^:]+)://(.+)} $url match protocol string] == 1} {
965        switch -regexp -- ${protocol} {
966            {^file$} {
967                return [file normalize $string]
968            }
969            {^mports$} {
970                return [macports::index::fetch_port $url $destdir]
971            }
972            {^https?$|^ftp$} {
973                return [macports::fetch_port $url]
974            }
975            default {
976                return -code error "Unsupported protocol $protocol"
977            }
978        }
979    } else {
980        return -code error "Can't parse url $url"
981    }
982}
983
984##
985# Get the path to the _resources directory of the source
986#
987# If the file is not available in the current source, it will fall back to the
988# default source. This behavior is controlled by the fallback parameter.
989#
990# @param url port url
991# @param path path in _resources we are interested in
992# @param fallback fall back to the default source tree
993# @return path to the _resources directory or the path to the fallback
994proc macports::getportresourcepath {url {path ""} {fallback yes}} {
995    global macports::sources_default
996
997    set protocol [getprotocol $url]
998
999    switch -- ${protocol} {
1000        file {
1001            set proposedpath [file normalize [file join [getportdir $url] .. ..]]
1002        }
1003        default {
1004            set proposedpath [getsourcepath $url]
1005        }
1006    }
1007
1008    # append requested path
1009    set proposedpath [file join $proposedpath _resources $path]
1010
1011    if {$fallback == "yes" && ![file exists $proposedpath]} {
1012        return [getdefaultportresourcepath $path]
1013    }
1014
1015    return $proposedpath
1016}
1017
1018##
1019# Get the path to the _resources directory of the default source
1020#
1021# @param path path in _resources we are interested in
1022# @return path to the _resources directory of the default source
1023proc macports::getdefaultportresourcepath {{path ""}} {
1024    global macports::sources_default
1025
1026    set default_source_url [lindex ${sources_default} 0]
1027    if {[getprotocol $default_source_url] == "file"} {
1028        set proposedpath [getportdir $default_source_url]
1029    } else {
1030        set proposedpath [getsourcepath $default_source_url]
1031    }
1032
1033    # append requested path
1034    set proposedpath [file join $proposedpath _resources $path]
1035
1036    return $proposedpath
1037}
1038
1039
1040# mport_filtervariants
1041# returns the given list of variants with implicitly-set ones removed
1042proc mport_filtervariants {variations {warn yes}} {
1043    # Iterate through the variants, filtering out
1044    # implicit ones. At the moment, the only implicit variants are
1045    # platform variants.
1046    set filteredvariations {}
1047
1048    foreach {variation value} $variations {
1049        switch -regexp $variation {
1050            ^(pure)?darwin         -
1051            ^(free|net|open){1}bsd -
1052            ^i386                  -
1053            ^linux                 -
1054            ^macosx                -
1055            ^powerpc               -
1056            ^solaris               -
1057            ^sunos {
1058                if {$warn} {
1059                    ui_warn "Implicit variants should not be explicitly set or unset. $variation will be ignored."
1060                }
1061            }
1062            default {
1063                lappend filteredvariations $variation $value
1064            }
1065        }
1066    }
1067    return $filteredvariations
1068}
1069
1070
1071# mportopen
1072# Opens a MacPorts portfile specified by a URL.  The Portfile is
1073# opened with the given list of options and variations.  The result
1074# of this function should be treated as an opaque handle to a
1075# MacPorts Portfile.
1076
1077proc mportopen {porturl {options ""} {variations ""} {nocache ""}} {
1078    global macports::portdbpath macports::portconf macports::open_mports auto_path
1079
1080    # Look for an already-open MPort with the same URL.
1081    # XXX: should compare options and variations here too.
1082    # if found, return the existing reference and bump the refcount.
1083    if {$nocache != ""} {
1084        set mport {}
1085    } else {
1086        set mport [dlist_search $macports::open_mports porturl $porturl]
1087    }
1088    if {$mport != {}} {
1089        set refcnt [ditem_key $mport refcnt]
1090        incr refcnt
1091        ditem_key $mport refcnt $refcnt
1092        return $mport
1093    }
1094
1095    array set options_array $options
1096    if {[info exists options_array(portdir)]} {
1097        set portdir $options_array(portdir)
1098    } else {
1099        set portdir ""
1100    }
1101
1102    set portpath [macports::getportdir $porturl $portdir]
1103    ui_debug "Changing to port directory: $portpath"
1104    cd $portpath
1105    if {![file isfile Portfile]} {
1106        return -code error "Could not find Portfile in $portpath"
1107    }
1108
1109    set workername [interp create]
1110
1111    set mport [ditem_create]
1112    lappend macports::open_mports $mport
1113    ditem_key $mport porturl $porturl
1114    ditem_key $mport portpath $portpath
1115    ditem_key $mport workername $workername
1116    ditem_key $mport options $options
1117    ditem_key $mport variations $variations
1118    ditem_key $mport refcnt 1
1119
1120    macports::worker_init $workername $portpath $porturl [macports::getportbuildpath $portpath] $options $variations
1121
1122    $workername eval source Portfile
1123
1124    # add the default universal variant if appropriate, and set up flags that
1125    # are conditional on whether universal is set
1126    $workername eval universal_setup
1127
1128    # evaluate the variants
1129    if {[$workername eval eval_variants variations] != 0} {
1130        mportclose $mport
1131        error "Error evaluating variants"
1132    }
1133
1134    ditem_key $mport provides [$workername eval return \$name]
1135
1136    return $mport
1137}
1138
1139# Traverse a directory with ports, calling a function on the path of ports
1140# (at the second depth).
1141# I.e. the structure of dir shall be:
1142# category/port/
1143# with a Portfile file in category/port/
1144#
1145# func:     function to call on every port directory (it is passed
1146#           category/port/ as its parameter)
1147# root:     the directory with all the categories directories.
1148proc mporttraverse {func {root .}} {
1149    # Save the current directory
1150    set pwd [pwd]
1151
1152    # Join the root.
1153    set pathToRoot [file join $pwd $root]
1154
1155    # Go to root because some callers expects us to be there.
1156    cd $pathToRoot
1157
1158    foreach category [lsort -increasing -unique [readdir $root]] {
1159        set pathToCategory [file join $root $category]
1160        # process the category dirs but not _resources
1161        if {[file isdirectory $pathToCategory] && [string index [file tail $pathToCategory] 0] != "_"} {
1162            # Iterate on port directories.
1163            foreach port [lsort -increasing -unique [readdir $pathToCategory]] {
1164                set pathToPort [file join $pathToCategory $port]
1165                if {[file isdirectory $pathToPort] &&
1166                  [file exists [file join $pathToPort "Portfile"]]} {
1167                    # Call the function.
1168                    $func [file join $category $port]
1169
1170                    # Restore the current directory because some
1171                    # functions changes it.
1172                    cd $pathToRoot
1173                }
1174            }
1175        }
1176    }
1177
1178    # Restore the current directory.
1179    cd $pwd
1180}
1181
1182### _mportsearchpath is private; subject to change without notice
1183
1184# depregex -> regex on the filename to find.
1185# search_path -> directories to search
1186# executable -> whether we want to check that the file is executable by current
1187#               user or not.
1188proc _mportsearchpath {depregex search_path {executable 0}} {
1189    set found 0
1190    foreach path $search_path {
1191        if {![file isdirectory $path]} {
1192            continue
1193        }
1194
1195        if {[catch {set filelist [readdir $path]} result]} {
1196            return -code error "$result ($path)"
1197            set filelist ""
1198        }
1199
1200        foreach filename $filelist {
1201            if {[regexp $depregex $filename] &&
1202              (($executable == 0) || [file executable [file join $path $filename]])} {
1203                ui_debug "Found Dependency: path: $path filename: $filename regex: $depregex"
1204                set found 1
1205                break
1206            }
1207        }
1208    }
1209    return $found
1210}
1211
1212### _libtest is private; subject to change without notice
1213# XXX - Architecture specific
1214# XXX - Rely on information from internal defines in cctools/dyld:
1215# define DEFAULT_FALLBACK_FRAMEWORK_PATH
1216# /Library/Frameworks:/Library/Frameworks:/Network/Library/Frameworks:/System/Library/Frameworks
1217# define DEFAULT_FALLBACK_LIBRARY_PATH /lib:/usr/local/lib:/lib:/usr/lib
1218#   -- Since /usr/local is bad, using /lib:/usr/lib only.
1219# Environment variables DYLD_FRAMEWORK_PATH, DYLD_LIBRARY_PATH,
1220# DYLD_FALLBACK_FRAMEWORK_PATH, and DYLD_FALLBACK_LIBRARY_PATH take precedence
1221
1222proc _libtest {mport depspec} {
1223    global env tcl_platform
1224    set depline [lindex [split $depspec :] 1]
1225    set prefix [_mportkey $mport prefix]
1226    set frameworks_dir [_mportkey $mport frameworks_dir]
1227
1228    if {[info exists env(DYLD_FRAMEWORK_PATH)]} {
1229        lappend search_path $env(DYLD_FRAMEWORK_PATH)
1230    } else {
1231        lappend search_path ${frameworks_dir} /Library/Frameworks /Network/Library/Frameworks /System/Library/Frameworks
1232    }
1233    if {[info exists env(DYLD_FALLBACK_FRAMEWORK_PATH)]} {
1234        lappend search_path $env(DYLD_FALLBACK_FRAMEWORK_PATH)
1235    }
1236    if {[info exists env(DYLD_LIBRARY_PATH)]} {
1237        lappend search_path $env(DYLD_LIBRARY_PATH)
1238    }
1239    lappend search_path /lib /usr/lib ${prefix}/lib
1240    if {[info exists env(DYLD_FALLBACK_LIBRARY_PATH)]} {
1241        lappend search_path $env(DYLD_FALLBACK_LIBRARY_PATH)
1242    }
1243
1244    set i [string first . $depline]
1245    if {$i < 0} {set i [string length $depline]}
1246    set depname [string range $depline 0 [expr $i - 1]]
1247    set depversion [string range $depline $i end]
1248    regsub {\.} $depversion {\.} depversion
1249    if {$tcl_platform(os) == "Darwin"} {
1250        set depregex \^${depname}${depversion}\\.dylib\$
1251    } else {
1252        set depregex \^${depname}\\.so${depversion}\$
1253    }
1254
1255    return [_mportsearchpath $depregex $search_path]
1256}
1257
1258### _bintest is private; subject to change without notice
1259
1260proc _bintest {mport depspec} {
1261    global env
1262    set depregex [lindex [split $depspec :] 1]
1263    set prefix [_mportkey $mport prefix]
1264
1265    set search_path [split $env(PATH) :]
1266
1267    set depregex \^$depregex\$
1268
1269    return [_mportsearchpath $depregex $search_path 1]
1270}
1271
1272### _pathtest is private; subject to change without notice
1273
1274proc _pathtest {mport depspec} {
1275    global env
1276    set depregex [lindex [split $depspec :] 1]
1277    set prefix [_mportkey $mport prefix]
1278
1279    # separate directory from regex
1280    set fullname $depregex
1281
1282    regexp {^(.*)/(.*?)$} "$fullname" match search_path depregex
1283
1284    if {[string index $search_path 0] != "/"} {
1285        # Prepend prefix if not an absolute path
1286        set search_path "${prefix}/${search_path}"
1287    }
1288
1289    set depregex \^$depregex\$
1290
1291    return [_mportsearchpath $depregex $search_path]
1292}
1293
1294### _porttest is private; subject to change without notice
1295
1296proc _porttest {mport depspec} {
1297    # We don't actually look for the port, but just return false
1298    # in order to let the mportdepends handle the dependency
1299    return 0
1300}
1301
1302### _mportinstalled is private; may change without notice
1303
1304# Determine if a port is already *installed*, as in "in the registry".
1305proc _mportinstalled {mport} {
1306    # Check for the presence of the port in the registry
1307    set workername [ditem_key $mport workername]
1308    return [$workername eval registry_exists_for_name \${name}]
1309}
1310
1311# Determine if a port is active (only for image mode)
1312proc _mportactive {mport} {
1313    set workername [ditem_key $mport workername]
1314    if {[catch {set reslist [$workername eval registry_active \${name}]}]} {
1315        return 0
1316    } else {
1317        return [expr [llength $reslist] > 0]
1318    }
1319}
1320
1321# Determine if the named port is active (only for image mode)
1322proc _portnameactive {portname} {
1323    if {[catch {set reslist [registry::active $portname]}]} {
1324        return 0
1325    } else {
1326        return [expr [llength $reslist] > 0]
1327    }
1328}
1329
1330### _mportispresent is private; may change without notice
1331
1332# Determine if some depspec is satisfied or if the given port is installed
1333# (and active, if we're in image mode).
1334# We actually start with the registry (faster?)
1335#
1336# mport     the port declaring the dep (context in which to evaluate $prefix etc)
1337# depspec   the dependency test specification (path, bin, lib, etc.)
1338proc _mportispresent {mport depspec} {
1339    set portname [lindex [split $depspec :] end]
1340    ui_debug "Searching for dependency: $portname"
1341    if {[string equal ${macports::registry.installtype} "image"]} {
1342        set res [_portnameactive $portname]
1343    } else {
1344        set res [registry::entry_exists_for_name $portname]
1345    }
1346    if {$res != 0} {
1347        ui_debug "Found Dependency: receipt exists for $portname"
1348        return 1
1349    } else {
1350        # The receipt test failed, use one of the depspec regex mechanisms
1351        ui_debug "Didn't find receipt, going to depspec regex for: $portname"
1352        set type [lindex [split $depspec :] 0]
1353        switch $type {
1354            lib { return [_libtest $mport $depspec] }
1355            bin { return [_bintest $mport $depspec] }
1356            path { return [_pathtest $mport $depspec] }
1357            port { return [_porttest $mport $depspec] }
1358            default {return -code error "unknown depspec type: $type"}
1359        }
1360        return 0
1361    }
1362}
1363
1364### _mportconflictsinstalled is private; may change without notice
1365
1366# Determine if the port, per the conflicts option, has any conflicts with
1367# what is installed.
1368#
1369# mport   the port to check for conflicts
1370# Returns a list of which installed ports conflict, or an empty list if none
1371proc _mportconflictsinstalled {mport conflictinfo} {
1372    set conflictlist {}
1373    if {[llength $conflictinfo] > 0} {
1374        ui_debug "Checking for conflicts against [_mportkey $mport name]"
1375        foreach conflictport ${conflictinfo} {
1376            if {[_mportispresent $mport port:${conflictport}]} {
1377                lappend conflictlist $conflictport
1378            }
1379        }
1380    } else {
1381        ui_debug "[_mportkey $mport name] has no conflicts"
1382    }
1383
1384    return $conflictlist
1385}
1386
1387
1388### _mportexec is private; may change without notice
1389
1390proc _mportexec {target mport} {
1391    # xxx: set the work path?
1392    set workername [ditem_key $mport workername]
1393    if {![catch {$workername eval check_variants variations $target} result] && $result == 0 &&
1394        ![catch {$workername eval eval_targets $target} result] && $result == 0} {
1395        # If auto-clean mode, clean-up after dependency install
1396        if {[string equal ${macports::portautoclean} "yes"]} {
1397            # Make sure we are back in the port path before clean
1398            # otherwise if the current directory had been changed to
1399            # inside the port,  the next port may fail when trying to
1400            # install because [pwd] will return a "no file or directory"
1401            # error since the directory it was in is now gone.
1402            set portpath [ditem_key $mport portpath]
1403            catch {cd $portpath}
1404            $workername eval eval_targets clean
1405        }
1406        return 0
1407    } else {
1408        # An error occurred.
1409        return 1
1410    }
1411}
1412
1413# mportexec
1414# Execute the specified target of the given mport.
1415proc mportexec {mport target} {
1416    global macports::registry.installtype
1417
1418    set workername [ditem_key $mport workername]
1419
1420    # check variants
1421    if {[$workername eval check_variants variations $target] != 0} {
1422        return 1
1423    }
1424
1425    # Before we build the port, we must build its dependencies.
1426    # XXX: need a more general way of comparing against targets
1427    set dlist {}
1428    if {   $target == "fetch" || $target == "checksum"
1429        || $target == "extract" || $target == "patch"
1430        || $target == "configure" || $target == "build"
1431        || $target == "test"
1432        || $target == "destroot" || $target == "install"
1433        || $target == "archive"
1434        || $target == "dmg" || $target == "mdmg"
1435        || $target == "pkg" || $target == "mpkg"
1436        || $target == "rpm" || $target == "dpkg"
1437        || $target == "srpm"|| $target == "portpkg" } {
1438
1439        ui_msg -nonewline "--->  Computing dependencies for [_mportkey $mport name]"
1440        if {[macports::ui_isset ports_debug]} {
1441            # play nice with debug messages
1442            ui_msg ""
1443        }
1444        if {[mportdepends $mport $target] != 0} {
1445            return 1
1446        }
1447        ui_msg ""
1448
1449        # Select out the dependents along the critical path,
1450        # but exclude this mport, we might not be installing it.
1451        set dlist [dlist_append_dependents $macports::open_mports $mport {}]
1452
1453        dlist_delete dlist $mport
1454
1455        # install them
1456        # xxx: as with below, this is ugly.  and deps need to be fixed to
1457        # understand Port Images before this can get prettier
1458        if { [string equal ${macports::registry.installtype} "image"] } {
1459            set result [dlist_eval $dlist _mportactive [list _mportexec "activate"]]
1460        } else {
1461            set result [dlist_eval $dlist _mportinstalled [list _mportexec "install"]]
1462        }
1463
1464        if {$result != {}} {
1465            set errstring "The following dependencies failed to build:"
1466            foreach ditem $result {
1467                append errstring " [ditem_key $ditem provides]"
1468            }
1469            ui_error $errstring
1470            return 1
1471        }
1472
1473        # Close the dependencies, we're done installing them.
1474        foreach ditem $dlist {
1475            mportclose $ditem
1476        }
1477    }
1478
1479    # If we're doing an install, check if we should clean after
1480    set clean 0
1481    if {[string equal ${macports::portautoclean} "yes"] && [string equal $target "install"] } {
1482        set clean 1
1483    }
1484
1485    # If we're doing image installs, then we should activate after install
1486    # xxx: This isn't pretty
1487    if { [string equal ${macports::registry.installtype} "image"] && [string equal $target "install"] } {
1488        set target activate
1489    }
1490
1491    # Build this port with the specified target
1492    set result [$workername eval eval_targets $target]
1493
1494    # If auto-clean mode and successful install, clean-up after install
1495    if {$result == 0 && $clean == 1} {
1496        # Make sure we are back in the port path, just in case
1497        set portpath [ditem_key $mport portpath]
1498        catch {cd $portpath}
1499        $workername eval eval_targets clean
1500    }
1501
1502    return $result
1503}
1504
1505proc macports::getsourcepath {url} {
1506    global macports::portdbpath
1507
1508    set source_path [split $url ://]
1509
1510    if {[_source_is_snapshot $url]} {
1511        # daily snapshot tarball
1512        return [file join $portdbpath sources [join [lrange $source_path 3 end-1] /] ports]
1513    }
1514
1515    return [file join $portdbpath sources [lindex $source_path 3] [lindex $source_path 4] [lindex $source_path 5]]
1516}
1517
1518##
1519# Checks whether a supplied source URL is for a daily snapshot tarball
1520# (private)
1521#
1522# @param url source URL to check
1523# @return a list containing filename and extension or an empty list
1524proc _source_is_snapshot {url {filename ""} {extension ""}} {
1525    upvar $filename myfilename
1526    upvar $extension myextension
1527
1528    if {[regexp {^(?:https?|ftp)://.+/(.+\.(tar\.gz|tar\.bz2))$} $url -> f e]} {
1529        set myfilename $f
1530        set myextension $e
1531
1532        return 1
1533    }
1534
1535    return 0
1536}
1537
1538proc macports::getportbuildpath {id} {
1539    global macports::portdbpath
1540    regsub {://} $id {.} port_path
1541    regsub -all {/} $port_path {_} port_path
1542    return [file join $portdbpath build $port_path]
1543}
1544
1545proc macports::getportworkpath_from_buildpath {portbuildpath} {
1546    return [file join $portbuildpath work]
1547}
1548
1549proc macports::getportworkpath_from_portdir {portpath} {
1550    return [macports::getportworkpath_from_buildpath [macports::getportbuildpath $portpath]]
1551}
1552
1553proc macports::getindex {source} {
1554    # Special case file:// sources
1555    if {[macports::getprotocol $source] == "file"} {
1556        return [file join [macports::getportdir $source] PortIndex]
1557    }
1558
1559    return [file join [macports::getsourcepath $source] PortIndex]
1560}
1561
1562proc mportsync {{optionslist {}}} {
1563    global macports::sources macports::portdbpath macports::rsync_options tcl_platform
1564    global macports::portverbose
1565    global macports::autoconf::rsync_path
1566    array set options $optionslist
1567
1568    set numfailed 0
1569
1570    ui_debug "Synchronizing ports tree(s)"
1571    foreach source $sources {
1572        set flags [lrange $source 1 end]
1573        set source [lindex $source 0]
1574        if {[lsearch -exact $flags nosync] != -1} {
1575            ui_debug "Skipping $source"
1576            continue
1577        }
1578        ui_info "Synchronizing local ports tree from $source"
1579        switch -regexp -- [macports::getprotocol $source] {
1580            {^file$} {
1581                set portdir [macports::getportdir $source]
1582                if {[file exists $portdir/.svn]} {
1583                    set svn_commandline "[macports::findBinary svn] update --non-interactive ${portdir}"
1584                    ui_debug $svn_commandline
1585                    if {
1586                        [catch {
1587                            set euid [geteuid]
1588                            set egid [getegid]
1589                            ui_debug "changing euid/egid - current euid: $euid - current egid: $egid"
1590                            setegid [name_to_gid [file attributes $portdir -group]]
1591                            seteuid [name_to_uid [file attributes $portdir -owner]]
1592                            system $svn_commandline
1593                            seteuid $euid
1594                            setegid $egid
1595                        }]
1596                    } {
1597                        ui_debug "$::errorInfo"
1598                        ui_error "Synchronization of the local ports tree failed doing an svn update"
1599                        incr numfailed
1600                        continue
1601                    }
1602                }
1603            }
1604            {^mports$} {
1605                macports::index::sync $macports::portdbpath $source
1606            }
1607            {^rsync$} {
1608                # Where to, boss?
1609                set destdir [file dirname [macports::getindex $source]]
1610                file mkdir $destdir
1611                # Keep rsync happy with a trailing slash
1612                if {[string index $source end] != "/"} {
1613                    set source "${source}/"
1614                }
1615                # Do rsync fetch
1616                set rsync_commandline "${macports::autoconf::rsync_path} ${rsync_options} ${source} ${destdir}"
1617                ui_debug $rsync_commandline
1618                if {[catch {system $rsync_commandline}]} {
1619                    ui_error "Synchronization of the local ports tree failed doing rsync"
1620                    incr numfailed
1621                    continue
1622                }
1623                if {[catch {system "chmod -R a+r \"$destdir\""}]} {
1624                    ui_warn "Setting world read permissions on parts of the ports tree failed, need root?"
1625                }
1626            }
1627            {^https?$|^ftp$} {
1628                if {[_source_is_snapshot $source filename extension]} {
1629                    # sync a daily port snapshot tarball
1630                    set indexfile [macports::getindex $source]
1631                    set destdir [file dirname $indexfile]
1632                    set tarpath [file join [file normalize [file join $destdir ..]] $filename]
1633
1634                    set updated 1
1635                    if {[file isdirectory $destdir]} {
1636                        set moddate [file mtime $destdir]
1637                        if {[catch {set updated [curl isnewer $source $moddate]} error]} {
1638                            ui_warn "Cannot check if $source was updated, ($error)"
1639                        }
1640                    }
1641
1642                    if {(![info exists options(ports_force)] || $options(ports_force) != "yes") && $updated <= 0} {
1643                        ui_info "No updates for $source"
1644                        continue
1645                    }
1646
1647                    file mkdir [file dirname $indexfile]
1648
1649                    set verboseflag {}
1650                    if {$macports::portverbose == "yes"} {
1651                        set verboseflag "-v"
1652                    }
1653
1654                    if {[catch {eval curl fetch $verboseflag {$source} {$tarpath}} error]} {
1655                        ui_error "Fetching $source failed ($error)"
1656                        incr numfailed
1657                        continue
1658                    }
1659
1660                    set extflag {}
1661                    switch $extension {
1662                        {tar.gz} {
1663                            set extflag "-z"
1664                        }
1665                        {tar.bz2} {
1666                            set extflag "-j"
1667                        }
1668                    }
1669
1670                    set tar [findBinary tar $macports::autoconf::tar_path]
1671                    if { [catch { system "cd $destdir/.. && $tar ${verboseflag} ${extflag} -xf $filename" } error] } {
1672                        ui_error "Extracting $source failed ($error)"
1673                        incr numfailed
1674                        continue
1675                    }
1676
1677                    if {[catch {system "chmod -R a+r \"$destdir\""}]} {
1678                        ui_warn "Setting world read permissions on parts of the ports tree failed, need root?"
1679                    }
1680
1681                    file delete $tarpath
1682                } else {
1683                    # sync just a PortIndex file
1684                    set indexfile [macports::getindex $source]
1685                    file mkdir [file dirname $indexfile]
1686                    curl fetch ${source}/PortIndex $indexfile
1687                    curl fetch ${source}/PortIndex.quick ${indexfile}.quick
1688                }
1689            }
1690            default {
1691                ui_warn "Unknown synchronization protocol for $source"
1692            }
1693        }
1694    }
1695
1696    if {$numfailed > 0} {
1697        return -code error "Synchronization of $numfailed source(s) failed"
1698    }
1699}
1700
1701proc mportsearch {pattern {case_sensitive yes} {matchstyle regexp} {field name}} {
1702    global macports::portdbpath macports::sources
1703    set matches [list]
1704    set easy [expr { $field == "name" }]
1705
1706    set found 0
1707    foreach source $sources {
1708        set flags [lrange $source 1 end]
1709        set source [lindex $source 0]
1710        if {[macports::getprotocol $source] == "mports"} {
1711            array set attrs [list name $pattern]
1712            set res [macports::index::search $macports::portdbpath $source [array get attrs]]
1713            eval lappend matches $res
1714        } else {
1715            if {[catch {set fd [open [macports::getindex $source] r]} result]} {
1716                ui_warn "Can't open index file for source: $source"
1717            } else {
1718                try {
1719                    incr found 1
1720                    while {[gets $fd line] >= 0} {
1721                        array unset portinfo
1722                        set name [lindex $line 0]
1723                        set len [lindex $line 1]
1724                        set line [read $fd $len]
1725
1726                        if {$easy} {
1727                            set target $name
1728                        } else {
1729                            array set portinfo $line
1730                            if {![info exists portinfo($field)]} continue
1731                            set target $portinfo($field)
1732                        }
1733
1734                        switch $matchstyle {
1735                            exact {
1736                                set matchres [expr 0 == ( {$case_sensitive == "yes"} ? [string compare $pattern $target] : [string compare -nocase $pattern $target] )]
1737                            }
1738                            glob {
1739                                set matchres [expr {$case_sensitive == "yes"} ? [string match $pattern $target] : [string match -nocase $pattern $target]]
1740                            }
1741                            regexp -
1742                            default {
1743                                set matchres [expr {$case_sensitive == "yes"} ? [regexp -- $pattern $target] : [regexp -nocase -- $pattern $target]]
1744                            }
1745                        }
1746
1747                        if {$matchres == 1} {
1748                            if {$easy} {
1749                                array set portinfo $line
1750                            }
1751                            switch -regexp -- [macports::getprotocol ${source}] {
1752                                {^rsync$} {
1753                                    # Rsync files are local
1754                                    set source_url "file://[macports::getsourcepath $source]"
1755                                }
1756                                {^https?$|^ftp$} {
1757                                    if {[_source_is_snapshot $source filename extension]} {
1758                                        # daily snapshot tarball
1759                                        set source_url "file://[macports::getsourcepath $source]"
1760                                    } else {
1761                                        # default action
1762                                        set source_url $source
1763                                    }
1764                                }
1765                                default {
1766                                    set source_url $source
1767                                }
1768                            }
1769                            if {[info exists portinfo(portarchive)]} {
1770                                set porturl ${source_url}/$portinfo(portarchive)
1771                            } elseif {[info exists portinfo(portdir)]} {
1772                                set porturl ${source_url}/$portinfo(portdir)
1773                            }
1774                            if {[info exists porturl]} {
1775                                lappend line porturl $porturl
1776                                ui_debug "Found port in $porturl"
1777                            } else {
1778                                ui_debug "Found port info: $line"
1779                            }
1780                            lappend matches $name
1781                            lappend matches $line
1782                        }
1783                    }
1784                } catch {*} {
1785                    ui_warn "It looks like your PortIndex file may be corrupt."
1786                    throw
1787                } finally {
1788                    close $fd
1789                }
1790            }
1791        }
1792    }
1793    if {!$found} {
1794        return -code error "No index(es) found! Have you synced your source indexes?"
1795    }
1796
1797    return $matches
1798}
1799
1800# Returns the PortInfo for a single named port. The info comes from the
1801# PortIndex, and name matching is case-insensitive. Unlike mportsearch, only
1802# the first match is returned, but the return format is otherwise identical.
1803# The advantage is that mportlookup is much faster than mportsearch, due to
1804# the use of the quick index.
1805proc mportlookup {name} {
1806    global macports::portdbpath macports::sources
1807
1808    set sourceno 0
1809    set matches [list]
1810    foreach source $sources {
1811        set source [lindex $source 0]
1812        if {[macports::getprotocol $source] != "mports"} {
1813            global macports::quick_index
1814            if {![info exists quick_index($sourceno,[string tolower $name])]} {
1815                incr sourceno 1
1816                continue
1817            }
1818            # The quick index is keyed on the port name, and provides the
1819            # offset in the main PortIndex where the given port's PortInfo
1820            # line can be found.
1821            set offset $quick_index($sourceno,[string tolower $name])
1822            incr sourceno 1
1823            if {[catch {set fd [open [macports::getindex $source] r]} result]} {
1824                ui_warn "Can't open index file for source: $source"
1825            } else {
1826                try {
1827                    seek $fd $offset
1828                    gets $fd line
1829                    set name [lindex $line 0]
1830                    set len [lindex $line 1]
1831                    set line [read $fd $len]
1832
1833                    array set portinfo $line
1834
1835                    switch -regexp -- [macports::getprotocol ${source}] {
1836                        {^rsync$} {
1837                            # Rsync files are local
1838                            set source_url "file://[macports::getsourcepath $source]"
1839                        }
1840                        {^https?$|^ftp$} {
1841                            if {[_source_is_snapshot $source filename extension]} {
1842                                # daily snapshot tarball
1843                                set source_url "file://[macports::getsourcepath $source]"
1844                             } else {
1845                                # default action
1846                                set source_url $source
1847                             }
1848                        }
1849                        default {
1850                            set source_url $source
1851                        }
1852                    }
1853                    if {[info exists portinfo(portarchive)]} {
1854                        set porturl ${source_url}/$portinfo(portarchive)
1855                    } elseif {[info exists portinfo(portdir)]} {
1856                        set porturl ${source_url}/$portinfo(portdir)
1857                    }
1858                    if {[info exists porturl]} {
1859                        lappend line porturl $porturl
1860                        ui_debug "Found port in $porturl"
1861                    } else {
1862                        ui_debug "Found port info: $line"
1863                    }
1864                    lappend matches $name
1865                    lappend matches $line
1866                    close $fd
1867                    set fd -1
1868                } catch {*} {
1869                    ui_warn "It looks like your PortIndex file may be corrupt."
1870                } finally {
1871                    if {$fd != -1} {
1872                        close $fd
1873                    }
1874                }
1875                if {[llength $matches] > 0} {
1876                    break
1877                }
1878            }
1879        } else {
1880            array set attrs [list name $name]
1881            set res [macports::index::search $macports::portdbpath $source [array get attrs]]
1882            if {[llength $res] > 0} {
1883                eval lappend matches $res
1884                break
1885            }
1886        }
1887    }
1888
1889    return $matches
1890}
1891
1892# Loads PortIndex.quick from each source into the quick_index, generating
1893# it first if necessary.
1894proc _mports_load_quickindex {args} {
1895    global macports::sources macports::quick_index
1896
1897    set sourceno 0
1898    foreach source $sources {
1899        unset -nocomplain quicklist
1900        # chop off any tags
1901        set source [lindex $source 0]
1902        set index [macports::getindex $source]
1903        if {![file exists ${index}]} {
1904            continue
1905        }
1906        if {![file exists ${index}.quick]} {
1907            ui_warn "No quick index file found, attempting to generate one for source: $source"
1908            if {[catch {set quicklist [mports_generate_quickindex ${index}]}]} {
1909                continue
1910            }
1911        }
1912        # only need to read the quick index file if we didn't just update it
1913        if {![info exists quicklist]} {
1914            if {[catch {set fd [open ${index}.quick r]} result]} {
1915                ui_warn "Can't open quick index file for source: $source"
1916                continue
1917            } else {
1918                set quicklist [read $fd]
1919                close $fd
1920            }
1921        }
1922        foreach entry [split $quicklist "\n"] {
1923            set quick_index($sourceno,[lindex $entry 0]) [lindex $entry 1]
1924        }
1925        incr sourceno 1
1926    }
1927    if {!$sourceno} {
1928        ui_warn "No index(es) found! Have you synced your source indexes?"
1929    }
1930}
1931
1932proc mports_generate_quickindex {index} {
1933    if {[catch {set indexfd [open ${index} r]} result] || [catch {set quickfd [open ${index}.quick w]} result]} {
1934        ui_warn "Can't open index file: $index"
1935        return -code error
1936    } else {
1937        try {
1938            set offset [tell $indexfd]
1939            set quicklist ""
1940            while {[gets $indexfd line] >= 0} {
1941                if {[llength $line] != 2} {
1942                    continue
1943                }
1944                set name [lindex $line 0]
1945                append quicklist "[string tolower $name] ${offset}\n"
1946
1947                set len [lindex $line 1]
1948                read $indexfd $len
1949                set offset [tell $indexfd]
1950            }
1951            puts -nonewline $quickfd $quicklist
1952        } catch {*} {
1953            ui_warn "It looks like your PortIndex file may be corrupt."
1954            throw
1955        } finally {
1956            close $indexfd
1957            close $quickfd
1958        }
1959    }
1960    if {[info exists quicklist]} {
1961        return $quicklist
1962    } else {
1963        ui_warn "Failed to generate quick index for: $index"
1964        return -code error
1965    }
1966}
1967
1968proc mportinfo {mport} {
1969    set workername [ditem_key $mport workername]
1970    return [$workername eval array get PortInfo]
1971}
1972
1973proc mportclose {mport} {
1974    global macports::open_mports
1975    set refcnt [ditem_key $mport refcnt]
1976    incr refcnt -1
1977    ditem_key $mport refcnt $refcnt
1978    if {$refcnt == 0} {
1979        dlist_delete macports::open_mports $mport
1980        set workername [ditem_key $mport workername]
1981        interp delete $workername
1982        ditem_delete $mport
1983    }
1984}
1985
1986##### Private Depspec API #####
1987# This API should be considered work in progress and subject to change without notice.
1988##### "
1989
1990# _mportkey
1991# - returns a variable from the port's interpreter
1992
1993proc _mportkey {mport key} {
1994    set workername [ditem_key $mport workername]
1995    return [$workername eval "return \$${key}"]
1996}
1997
1998# mportdepends builds the list of mports which the given port depends on.
1999# This list is added to $mport.
2000# This list actually depends on the target.
2001# This method can optionally recurse through the dependencies, looking for
2002#   dependencies of dependencies.
2003# This method can optionally cut the search when ports are already installed or
2004#   the dependencies are satisfied.
2005#
2006# mport -> mport item
2007# target -> target to consider the dependency for
2008# recurseDeps -> if the search should be recursive
2009# skipSatisfied -> cut the search tree when encountering installed/satisfied
2010#                  dependencies ports.
2011# accDeps -> accumulator for recursive calls
2012# return 0 if everything was ok, an non zero integer otherwise.
2013proc mportdepends {mport {target ""} {recurseDeps 1} {skipSatisfied 1}} {
2014
2015    array set portinfo [mportinfo $mport]
2016    set depends {}
2017    set deptypes {}
2018
2019    # progress indicator
2020    if {![macports::ui_isset ports_debug]} {
2021        ui_info -nonewline "."
2022        flush stdout
2023    }
2024   
2025    if {[info exists portinfo(conflicts)] && ($target == "" || $target == "install")} {
2026        set conflictports [_mportconflictsinstalled $mport $portinfo(conflicts)]
2027        if {[llength ${conflictports}] != 0} {
2028            if {[macports::global_option_isset ports_force]} {
2029                ui_warn "Force option set; installing $portinfo(name) despite conflicts with: ${conflictports}"
2030            } else {
2031                return -code error "Can't install $portinfo(name) because conflicting ports are installed: ${conflictports}"
2032            }
2033        }
2034    }
2035
2036    # Determine deptypes to look for based on target
2037    switch $target {
2038        fetch       -
2039        checksum    { set deptypes "depends_fetch" }
2040        extract     -
2041        patch       { set deptypes "depends_fetch depends_extract" }
2042        configure   -
2043        build       { set deptypes "depends_fetch depends_extract depends_lib depends_build" }
2044
2045        test        -
2046        destroot    -
2047        install     -
2048        archive     -
2049        dmg         -
2050        pkg         -
2051        portpkg     -
2052        mdmg        -
2053        mpkg        -
2054        rpm         -
2055        srpm        -
2056        dpkg        -
2057        ""          { set deptypes "depends_fetch depends_extract depends_lib depends_build depends_run" }
2058    }
2059
2060    # Gather the dependencies for deptypes
2061    foreach deptype $deptypes {
2062        # Add to the list of dependencies if the option exists and isn't empty.
2063        if {[info exists portinfo($deptype)] && $portinfo($deptype) != ""} {
2064            set depends [concat $depends $portinfo($deptype)]
2065        }
2066    }
2067
2068    set subPorts {}
2069    set options [ditem_key $mport options]
2070    set variations [ditem_key $mport variations]
2071
2072    foreach depspec $depends {
2073        # Is that dependency satisfied or this port installed?
2074        # If we don't skip or if it is not, add it to the list.
2075        if {!$skipSatisfied || ![_mportispresent $mport $depspec]} {
2076            # grab the portname portion of the depspec
2077            set dep_portname [lindex [split $depspec :] end]
2078
2079            # Find the porturl
2080            if {[catch {set res [mportlookup $dep_portname]} error]} {
2081                global errorInfo
2082                ui_debug "$errorInfo"
2083                ui_error "Internal error: port lookup failed: $error"
2084                return 1
2085            }
2086
2087            array unset portinfo
2088            array set portinfo [lindex $res 1]
2089            if {![info exists portinfo(porturl)]} {
2090                ui_error "Dependency '$dep_portname' not found."
2091                return 1
2092            }
2093
2094            # Figure out the subport. Check the open_mports list first, since
2095            # we potentially leak mport references if we mportopen each time,
2096            # because mportexec only closes each open mport once.
2097            set subport [dlist_search $macports::open_mports porturl $portinfo(porturl)]
2098            if {$subport == {}} {
2099                # We haven't opened this one yet.
2100                set subport [mportopen $portinfo(porturl) $options $variations]
2101                if {$recurseDeps} {
2102                    # Add to the list we need to recurse on.
2103                    lappend subPorts $subport
2104                }
2105            }
2106
2107            # Append the sub-port's provides to the port's requirements list.
2108            ditem_append_unique $mport requires "[ditem_key $subport provides]"
2109        }
2110    }
2111
2112    # Loop on the subports.
2113    if {$recurseDeps} {
2114        foreach subport $subPorts {
2115            # Sub ports should be installed (all dependencies must be satisfied).
2116            set res [mportdepends $subport "" $recurseDeps $skipSatisfied]
2117            if {$res != 0} {
2118                return $res
2119            }
2120        }
2121    }
2122
2123    return 0
2124}
2125
2126# selfupdate procedure
2127proc macports::selfupdate {{optionslist {}}} {
2128    global macports::prefix macports::portdbpath macports::libpath macports::rsync_server macports::rsync_dir macports::rsync_options
2129    global macports::autoconf::macports_version macports::autoconf::rsync_path
2130    array set options $optionslist
2131
2132    # syncing ports tree.
2133    if {![info exists options(ports_selfupdate_nosync)] || $options(ports_selfupdate_nosync) != "yes"} {
2134        ui_msg "--->  Updating the ports tree"
2135        if {[catch {mportsync $optionslist} result]} {
2136            return -code error "Couldn't sync the ports tree: $result"
2137        }
2138    }
2139
2140    # create the path to the to be downloaded sources if it doesn't exist
2141    set mp_source_path [file join $portdbpath sources ${rsync_server} ${rsync_dir}/]
2142    if {![file exists $mp_source_path]} {
2143        file mkdir $mp_source_path
2144    }
2145    ui_debug "MacPorts sources location: $mp_source_path"
2146
2147    # sync the MacPorts sources
2148    ui_msg "--->  Updating MacPorts base sources using rsync"
2149    if { [catch { system "$rsync_path $rsync_options rsync://${rsync_server}/${rsync_dir} $mp_source_path" } result ] } {
2150       return -code error "Error synchronizing MacPorts sources: $result"
2151    }
2152
2153    # echo current MacPorts version
2154    ui_msg "MacPorts base version $macports::autoconf::macports_version installed,"
2155
2156    if { [info exists options(ports_force)] && $options(ports_force) == "yes" } {
2157        set use_the_force_luke yes
2158        ui_debug "Forcing a rebuild and reinstallation of MacPorts"
2159    } else {
2160        set use_the_force_luke no
2161        ui_debug "Rebuilding and reinstalling MacPorts if needed"
2162    }
2163
2164    # Choose what version file to use: old, floating point format or new, real version number format
2165    set version_file [file join $mp_source_path config macports_version]
2166    if {[file exists $version_file]} {
2167        set fd [open $version_file r]
2168        gets $fd macports_version_new
2169        close $fd
2170        # echo downloaded MacPorts version
2171        ui_msg "MacPorts base version $macports_version_new downloaded."
2172    } else {
2173        ui_warn "No version file found, please rerun selfupdate."
2174        set macports_version_new 0
2175    }
2176
2177    # check if we we need to rebuild base
2178    set comp [rpm-vercomp $macports_version_new $macports::autoconf::macports_version]
2179    if {$use_the_force_luke == "yes" || $comp > 0} {
2180        if {[info exists options(ports_dryrun)] && $options(ports_dryrun) == "yes"} {
2181            ui_msg "--->  MacPorts base is outdated, selfupdate would install $macports_version_new (dry run)"
2182        } else {
2183            ui_msg "--->  MacPorts base is outdated, installing new version $macports_version_new"
2184
2185            # get installation user/group and permissions
2186            set owner [file attributes ${prefix} -owner]
2187            set group [file attributes ${prefix} -group]
2188            set perms [string range [file attributes ${prefix} -permissions] end-3 end]
2189            if {![string equal $tcl_platform(user) $owner]} {
2190                return -code error "User $tcl_platform(user) does not own ${prefix} - try using sudo"
2191            }
2192            ui_debug "Permissions OK"
2193
2194            # where to install our macports1.0 tcl package
2195            set mp_tclpackage_path [file join $portdbpath .tclpackage]
2196            if { [file exists $mp_tclpackage_path]} {
2197                set fd [open $mp_tclpackage_path r]
2198                gets $fd tclpackage
2199                close $fd
2200            } else {
2201                set tclpackage $libpath
2202            }
2203
2204            set configure_args "--prefix=$prefix --with-tclpackage=$tclpackage --with-install-user=$owner --with-install-group=$group --with-directory-mode=$perms"
2205            # too many users have an incompatible readline in /usr/local, see ticket #10651
2206            if {$tcl_platform(os) != "Darwin" || $prefix == "/usr/local"
2207                || ([glob -nocomplain "/usr/local/lib/lib{readline,history}*"] == "" && [glob -nocomplain "/usr/local/include/readline/*.h"] == "")} {
2208                append configure_args " --enable-readline"
2209            } else {
2210                ui_warn "Disabling readline support due to readline in /usr/local"
2211            }
2212
2213            # do the actual configure, build and installation of new base
2214            ui_msg "Installing new MacPorts release in $prefix as $owner:$group; permissions $perms; Tcl-Package in $tclpackage\n"
2215            if { [catch { system "cd $mp_source_path && ./configure $configure_args && make && make install" } result] } {
2216                return -code error "Error installing new MacPorts base: $result"
2217            }
2218        }
2219    } elseif {$comp < 0} {
2220        ui_msg "--->  MacPorts base is probably trunk or a release candidate"
2221    } else {
2222        ui_msg "--->  MacPorts base is already the latest version"
2223    }
2224
2225    # set the MacPorts sources to the right owner
2226    set sources_owner [file attributes [file join $portdbpath sources/] -owner]
2227    ui_debug "Setting MacPorts sources ownership to $sources_owner"
2228    if { [catch { exec [findBinary chown $macports::autoconf::chown_path] -R $sources_owner [file join $portdbpath sources/] } result] } {
2229        return -code error "Couldn't change permissions of the MacPorts sources at $mp_source_path to $sources_owner: $result"
2230    }
2231
2232    if {![info exists options(ports_selfupdate_nosync)] || $options(ports_selfupdate_nosync) != "yes"} {
2233        ui_msg "\nThe ports tree has been updated. To upgrade your installed ports, you should run"
2234        ui_msg "  port upgrade outdated"
2235    }
2236
2237    return 0
2238}
2239
2240# upgrade procedure
2241proc macports::upgrade {portname dspec globalvarlist variationslist optionslist {depscachename ""}} {
2242    global macports::registry.installtype
2243    global macports::portarchivemode
2244    array set options $optionslist
2245
2246    # Note $variationslist is left alone and so retains the original
2247    # requested variations, which should be passed to recursive calls to
2248    # upgrade; while variations gets existing variants and global variations
2249    # merged in later on, so it applies only to this port's upgrade
2250    array set variations $variationslist
2251
2252    if {![string match "" $depscachename]} {
2253        upvar $depscachename depscache
2254    }
2255
2256    # Is this a dry run?
2257    set is_dryrun no
2258    if {[info exists options(ports_dryrun)] && $options(ports_dryrun) eq "yes"} {
2259        set is_dryrun yes
2260    }
2261
2262    # check if the port is in tree
2263    if {[catch {mportlookup $portname} result]} {
2264        global errorInfo
2265        ui_debug "$errorInfo"
2266        ui_error "port lookup failed: $result"
2267        return 1
2268    }
2269    # argh! port doesnt exist!
2270    if {$result == ""} {
2271        ui_error "No port $portname found."
2272        return 1
2273    }
2274    # fill array with information
2275    array set portinfo [lindex $result 1]
2276    # set portname again since the one we were passed may not have had the correct case
2277    set portname $portinfo(name)
2278
2279    # set version_in_tree and revision_in_tree
2280    if {![info exists portinfo(version)]} {
2281        ui_error "Invalid port entry for $portname, missing version"
2282        return 1
2283    }
2284    set version_in_tree "$portinfo(version)"
2285    set revision_in_tree "$portinfo(revision)"
2286    set epoch_in_tree "$portinfo(epoch)"
2287
2288    set ilist {}
2289    if { [catch {set ilist [registry::installed $portname ""]} result] } {
2290        if {$result == "Registry error: $portname not registered as installed." } {
2291            ui_debug "$portname is *not* installed by MacPorts"
2292
2293            # We need to pass _mportispresent a reference to the mport that is
2294            # actually declaring the dependency on the one we're checking for.
2295            # We got here via _upgrade_dependencies, so we grab it from 2 levels up.
2296            upvar 2 workername parentworker
2297            if {![_mportispresent $parentworker $dspec ] } {
2298                # open porthandle
2299                set porturl $portinfo(porturl)
2300                if {![info exists porturl]} {
2301                    set porturl file://./
2302                }
2303                # Merge the global variations into the specified
2304                foreach { variation value } $globalvarlist {
2305                    if { ![info exists variations($variation)] } {
2306                        set variations($variation) $value
2307                    }
2308                }
2309
2310                if {[catch {set workername [mportopen $porturl [array get options] [array get variations]]} result]} {
2311                    global errorInfo
2312                    ui_debug "$errorInfo"
2313                    ui_error "Unable to open port: $result"
2314                    return 1
2315                }
2316                # While we're at it, update the portinfo
2317                array unset portinfo
2318                array set portinfo [mportinfo $workername]
2319               
2320                # upgrade its dependencies first
2321                set status [_upgrade_dependencies portinfo depscache globalvarlist variationslist options]
2322                if {$status != 0 && ![ui_isset ports_processall]} {
2323                    catch {mportclose $workername}
2324                    return $status
2325                }
2326                # now install it
2327                if {[catch {set result [mportexec $workername install]} result]} {
2328                    global errorInfo
2329                    ui_debug "$errorInfo"
2330                    ui_error "Unable to exec port: $result"
2331                    catch {mportclose $workername}
2332                    return 1
2333                }
2334                if {$result > 0} {
2335                    ui_error "Problem while installing $portname"
2336                    catch {mportclose $workername}
2337                    return $result
2338                }
2339                # we just installed it, so mark it done in the cache
2340                set depscache(port:${portname}) 1
2341                mportclose $workername
2342            } else {
2343                # dependency is satisfied by something other than the named port
2344                ui_debug "$portname not installed, soft dependency satisfied"
2345                # mark this depspec as satisfied in the cache
2346                set depscache($dspec) 1
2347            }
2348            # the rest of the proc doesn't matter for a port that is freshly
2349            # installed or not installed
2350            return 0
2351        } else {
2352            ui_error "Checking installed version failed: $result"
2353            return 1
2354        }
2355    } else {
2356        # we'll now take care of upgrading it, so we can add it to the cache
2357        set depscache(port:${portname}) 1
2358    }
2359    set anyactive no
2360    set version_installed {}
2361    set revision_installed {}
2362    set epoch_installed 0
2363    set variant_installed ""
2364
2365    # find latest version installed and active version (if any)
2366    foreach i $ilist {
2367        set variant [lindex $i 3]
2368        set version [lindex $i 1]
2369        set revision [lindex $i 2]
2370        set epoch [lindex $i 5]
2371        if { $version_installed == {} || $epoch > $epoch_installed ||
2372                ($epoch == $epoch_installed && [rpm-vercomp $version $version_installed] > 0)
2373                || ($epoch == $epoch_installed
2374                    && [rpm-vercomp $version $version_installed] == 0
2375                    && [rpm-vercomp $revision $revision_installed] > 0)} {
2376            set version_installed $version
2377            set revision_installed $revision
2378            set variant_installed $variant
2379            set epoch_installed $epoch
2380        }
2381
2382        set isactive [lindex $i 4]
2383        if {$isactive == 1} {
2384            set anyactive yes
2385            set version_active $version
2386            set revision_active $revision
2387            set variant_active $variant
2388        }
2389    }
2390
2391    # output version numbers
2392    ui_debug "epoch: in tree: $epoch_in_tree installed: $epoch_installed"
2393    ui_debug "$portname ${version_in_tree}_${revision_in_tree} exists in the ports tree"
2394    ui_debug "$portname ${version_installed}_${revision_installed} $variant_installed is the latest installed"
2395    if {$anyactive} {
2396        ui_debug "$portname ${version_active}_${revision_active} $variant_active is active"
2397    } else {
2398        ui_debug "no version of $portname is active"
2399    }
2400
2401    # save existing variant for later use
2402    if {$anyactive} {
2403        set oldvariant $variant_active
2404    } else {
2405        set oldvariant $variant_installed
2406    }
2407
2408    # Before we do
2409    # dependencies, we need to figure out the final variants,
2410    # open the port, and update the portinfo.
2411
2412    set porturl $portinfo(porturl)
2413    if {![info exists porturl]} {
2414        set porturl file://./
2415    }
2416
2417    # will break if we start recording negative variants (#2377)
2418    set variant [lrange [split $oldvariant +] 1 end]
2419    ui_debug "Merging existing variants $variant into variants"
2420    set oldvariantlist [list]
2421    foreach v $variant {
2422        lappend oldvariantlist $v "+"
2423    }
2424    # remove implicit variants, without printing warnings
2425    set oldvariantlist [mport_filtervariants $oldvariantlist no]
2426
2427    # check if the variants are present in $version_in_tree
2428    if {[info exists portinfo(variants)]} {
2429        set avariants $portinfo(variants)
2430    } else {
2431        set avariants {}
2432    }
2433    ui_debug "available variants are : $avariants"
2434    foreach {variation value} $oldvariantlist {
2435        if {[lsearch $avariants $variation] != -1} {
2436            ui_debug "variant $variation is present in $portname $version_in_tree"
2437            if { ![info exists variations($variation)]} {
2438                set variations($variation) $value
2439            }
2440        }
2441    }
2442
2443    # Now merge in the global (i.e. variants.conf) variations.
2444    # We wait until now so that existing variants for this port
2445    # override global variations
2446    foreach { variation value } $globalvarlist {
2447        if { ![info exists variations($variation)] } {
2448            set variations($variation) $value
2449        }
2450    }
2451
2452    ui_debug "new fully merged portvariants: [array get variations]"
2453
2454    if {[catch {set workername [mportopen $porturl [array get options] [array get variations]]} result]} {
2455        global errorInfo
2456        ui_debug "$errorInfo"
2457        ui_error "Unable to open port: $result"
2458        return 1
2459    }
2460
2461    array unset portinfo
2462    array set portinfo [mportinfo $workername]
2463    set portwasopened 1
2464    set version_in_tree "$portinfo(version)"
2465    set revision_in_tree "$portinfo(revision)"
2466    set epoch_in_tree "$portinfo(epoch)"
2467
2468
2469    # first upgrade dependencies
2470    if {![info exists options(ports_nodeps)]} {
2471        set status [_upgrade_dependencies portinfo depscache globalvarlist variationslist options]
2472        if {$status != 0 && ![ui_isset ports_processall]} {
2473            catch {mportclose $workername}
2474            return $status
2475        }
2476    } else {
2477        ui_debug "Not following dependencies"
2478    }
2479
2480    set epoch_override 0
2481    # check installed version against version in ports
2482    if { ( [rpm-vercomp $version_installed $version_in_tree] > 0
2483            || ([rpm-vercomp $version_installed $version_in_tree] == 0
2484                && [rpm-vercomp $revision_installed $revision_in_tree] >= 0 ))
2485        && ![info exists options(ports_force)] } {
2486        ui_debug "No need to upgrade! $portname ${version_installed}_${revision_installed} >= $portname ${version_in_tree}_${revision_in_tree}"
2487        if {[info exists portinfo(canonical_active_variants)]
2488            && $portinfo(canonical_active_variants) != $oldvariant} {
2489            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)\". Specify -f to force upgrade."
2490        }
2491        if { $epoch_installed >= $epoch_in_tree } {
2492            # Check if we have to do dependents
2493            if {[info exists options(ports_do_dependents)]} {
2494                # We do dependents ..
2495                set options(ports_nodeps) 1
2496
2497                registry::open_dep_map
2498                set deplist [registry::list_dependents $portname]
2499
2500                if { [llength deplist] > 0 } {
2501                    foreach dep $deplist {
2502                        set mpname [lindex $dep 2]
2503                        if {![llength [array get depscache port:${mpname}]]} {
2504                            set status [macports::upgrade $mpname port:${mpname} $globalvarlist $variationslist [array get options] depscache]
2505                            if {$status != 0 && ![ui_isset ports_processall]} {
2506                                catch {mportclose $workername}
2507                                return $status
2508                            }
2509                        }
2510                    }
2511                }
2512            }
2513            mportclose $workername
2514            return 0
2515        } else {
2516            set epoch_override 1
2517            ui_debug "epoch override ... upgrading!"
2518        }
2519    }
2520
2521
2522    # build or unarchive version_in_tree
2523    if {0 == [string compare "yes" ${macports::portarchivemode}]} {
2524        set upgrade_action "archive"
2525    } else {
2526        set upgrade_action "destroot"
2527    }
2528
2529    # avoid building again unnecessarily
2530    if {[info exists options(ports_force)] || $epoch_override == 1
2531        || ![registry::entry_exists $portname $version_in_tree $revision_in_tree $portinfo(canonical_active_variants)]} {
2532        if {[catch {set result [mportexec $workername $upgrade_action]} result] || $result != 0} {
2533            global errorInfo
2534            ui_debug "$errorInfo"
2535            ui_error "Unable to upgrade port: $result"
2536            catch {mportclose $workername}
2537            return 1
2538        }
2539    }
2540
2541    # always uninstall old port in direct mode
2542    if { 0 != [string compare "image" ${macports::registry.installtype}] } {
2543        # uninstall old
2544        ui_debug "Uninstalling $portname ${version_installed}_${revision_installed}${variant_installed}"
2545        # we have to force the uninstall in case of dependents
2546        set force_cur [info exists options(ports_force)]
2547        set options(ports_force) yes
2548        if {$is_dryrun eq "yes"} {
2549            ui_msg "Skipping uninstall $portname @${version_installed}_${revision_installed}${variant_installed} (dry run)"
2550        } elseif {[catch {portuninstall::uninstall $portname ${version_installed}_${revision_installed}${variant_installed} [array get options]} result]} {
2551            global errorInfo
2552            ui_debug "$errorInfo"
2553            ui_error "Uninstall $portname ${version_installed}_${revision_installed}${variant_installed} failed: $result"
2554            catch {mportclose $workername}
2555            return 1
2556        }
2557        if {!$force_cur} {
2558            unset options(ports_force)
2559        }
2560    } else {
2561        # are we installing an existing version due to force or epoch override?
2562        if {[registry::entry_exists $portname $version_in_tree $revision_in_tree $portinfo(canonical_active_variants)]} {
2563             ui_debug "Uninstalling $portname ${version_in_tree}_${revision_in_tree}$portinfo(canonical_active_variants)"
2564            # we have to force the uninstall in case of dependents
2565            set force_cur [info exists options(ports_force)]
2566            set options(ports_force) yes
2567            if {$is_dryrun eq "yes"} {
2568                ui_msg "Skipping uninstall $portname @${version_in_tree}_${revision_in_tree}$portinfo(canonical_active_variants) (dry run)"
2569            } elseif {[catch {portuninstall::uninstall $portname ${version_in_tree}_${revision_in_tree}$portinfo(canonical_active_variants) [array get options]} result]} {
2570                global errorInfo
2571                ui_debug "$errorInfo"
2572                ui_error "Uninstall $portname ${version_in_tree}_${revision_in_tree}$portinfo(canonical_active_variants) failed: $result"
2573                catch {mportclose $workername}
2574                return 1
2575            }
2576            if {!$force_cur} {
2577                unset options(ports_force)
2578            }
2579            if {$anyactive && $version_in_tree == $version_active && $revision_in_tree == $revision_active
2580                && $portinfo(canonical_active_variants) == $variant_active} {
2581                set anyactive no
2582            }
2583        }
2584        if {$anyactive} {
2585            # deactivate version_active
2586            if {$is_dryrun eq "yes"} {
2587                ui_msg "Skipping deactivate $portname @${version_active}_${revision_active} (dry run)"
2588            } elseif {[catch {portimage::deactivate $portname ${version_active}_${revision_active}${variant_active} $optionslist} result]} {
2589                global errorInfo
2590                ui_debug "$errorInfo"
2591                ui_error "Deactivating $portname ${version_active}_${revision_active} failed: $result"
2592                catch {mportclose $workername}
2593                return 1
2594            }
2595        }
2596        if {[info exists options(port_uninstall_old)]} {
2597            # uninstalling now could fail due to dependents when not forced,
2598            # because the new version is not installed
2599            set uninstall_later yes
2600        }
2601    }
2602
2603    if {$is_dryrun eq "yes"} {
2604        ui_msg "Skipping activate $portname @${version_in_tree}_${revision_in_tree} (dry run)"
2605    } elseif {[catch {set result [mportexec $workername install]} result]} {
2606        global errorInfo
2607        ui_debug "$errorInfo"
2608        ui_error "Couldn't activate $portname ${version_in_tree}_${revision_in_tree}: $result"
2609        catch {mportclose $workername}
2610        return 1
2611    }
2612
2613    if {[info exists uninstall_later] && $uninstall_later == yes} {
2614        foreach i $ilist {
2615            set version [lindex $i 1]
2616            set revision [lindex $i 2]
2617            set variant [lindex $i 3]
2618            if {$version == $version_in_tree && $revision == $revision_in_tree && $variant == $portinfo(canonical_active_variants)} {
2619                continue
2620            }
2621            ui_debug "Uninstalling $portname ${version}_${revision}${variant}"
2622            if {$is_dryrun eq "yes"} {
2623                ui_msg "Skipping uninstall $portname @${version}_${revision}${variant} (dry run)"
2624            } elseif {[catch {portuninstall::uninstall $portname ${version}_${revision}${variant} $optionslist} result]} {
2625                global errorInfo
2626                ui_debug "$errorInfo"
2627                ui_error "Uninstall $portname @${version}_${revision}${variant} failed: $result"
2628                catch {mportclose $workername}
2629                return 1
2630            }
2631        }
2632    }
2633
2634    # Check if we have to do dependents
2635    if {[info exists options(ports_do_dependents)]} {
2636        # We do dependents ..
2637        set options(ports_nodeps) 1
2638
2639        registry::open_dep_map
2640        set deplist [registry::list_dependents $portname]
2641
2642        if { [llength deplist] > 0 } {
2643            foreach dep $deplist {
2644                set mpname [lindex $dep 2]
2645                if {![llength [array get depscache port:${mpname}]]} {
2646                    set status [macports::upgrade $mpname port:${mpname} $globalvarlist $variationslist [array get options] depscache]
2647                    if {$status != 0 && ![ui_isset ports_processall]} {
2648                        catch {mportclose $workername}
2649                        return $status
2650                    }
2651                }
2652            }
2653        }
2654    }
2655
2656
2657    # close the port handle
2658    mportclose $workername
2659    return 0
2660}
2661
2662# upgrade_dependencies: helper proc for upgrade
2663# Calls upgrade on each dependency listed in the PortInfo.
2664# Uses upvar to access the variables.
2665proc macports::_upgrade_dependencies {portinfoname depscachename globalvarlistname variationslistname optionsname} {
2666    upvar $portinfoname portinfo $depscachename depscache \
2667          $globalvarlistname globalvarlist $variationslistname variationslist \
2668          $optionsname options
2669
2670    # If we're following dependents, we only want to follow this port's
2671    # dependents, not those of all its dependencies. Otherwise, we would
2672    # end up processing this port's dependents n+1 times (recursively!),
2673    # where n is the number of dependencies this port has, since this port
2674    # is of course a dependent of each of its dependencies. Plus the
2675    # dependencies could have any number of unrelated dependents.
2676
2677    # So we save whether we're following dependents, unset the option
2678    # while doing the dependencies, and restore it afterwards.
2679    set saved_do_dependents [info exists options(ports_do_dependents)]
2680    unset -nocomplain options(ports_do_dependents)
2681
2682    set status 0
2683    # each dep type is upgraded
2684    foreach dtype {depends_fetch depends_extract depends_build depends_lib depends_run} {
2685        if {[info exists portinfo($dtype)]} {
2686            foreach i $portinfo($dtype) {
2687                set d [lindex [split $i :] end]
2688                if {![llength [array get depscache port:${d}]] && ![llength [array get depscache $i]]} {
2689                    set status [upgrade $d $i $globalvarlist $variationslist [array get options] depscache]
2690                    if {$status != 0 && ![ui_isset ports_processall]} break
2691                }
2692            }
2693        }
2694        if {$status != 0 && ![ui_isset ports_processall]} break
2695    }
2696    # restore dependent-following to its former value
2697    if {$saved_do_dependents} {
2698        set options(ports_do_dependents) yes
2699    }
2700    return $status
2701}
2702
2703# mportselect
2704#   * command: The only valid commands are list, set and show
2705#   * group: This argument should correspond to a directory under
2706#            $macports::prefix/etc/select.
2707#   * version: This argument is only used by the 'set' command.
2708# On error mportselect returns with the code 'error'.
2709proc mportselect {command group {version ""}} {
2710    ui_debug "mportselect \[$command] \[$group] \[$version]"
2711
2712    set conf_path "$macports::prefix/etc/select/$group"
2713    if {![file isdirectory $conf_path]} {
2714        return -code error "The specified group '$group' does not exist."
2715    }
2716
2717    switch -- $command {
2718        list {
2719            if {[catch {set versions [glob -directory $conf_path *]}]} {
2720                return -code error [concat "No configurations associated " \
2721                                           "with '$group' were found."]
2722            }
2723
2724            # Return the sorted list of versions (excluding base and current).
2725            set lversions {}
2726            foreach v $versions {
2727                # Only the file name corresponds to the version name.
2728                set v [file tail $v]
2729                if {$v eq "base" || $v eq "current"} {
2730                    continue
2731                }
2732                lappend lversions [file tail $v]
2733            }
2734            return [lsort $lversions]
2735        }
2736        set {
2737            # Use $conf_path/$version to read in sources.
2738            if {[catch {set src_file [open "$conf_path/$version"]}]} {
2739                return -code error [concat "Verify that the specified " \
2740                                           "version '$version' is valid " \
2741                                           "(i.e., Is it listed when you " \
2742                                           "specify the --list command?)."]
2743            }
2744            set srcs [split [read -nonewline $src_file] "\n"]
2745            close $src_file
2746
2747            # Use $conf_path/base to read in targets.
2748            if {[catch {set tgt_file [open "$conf_path/base"]}]} {
2749                return -code error [concat "The configuration file " \
2750                                           "'$conf_path/base' could not be " \
2751                                           "opened."]
2752            }
2753            set tgts [split [read -nonewline $tgt_file] "\n"]
2754            close $tgt_file
2755
2756            # Iterate through the configuration files executing the specified
2757            # actions.
2758            set i 0
2759            foreach tgt $tgts {
2760                set src [lindex $srcs $i]
2761
2762                switch -glob -- $src {
2763                    - {
2764                        # The source is unavailable for this file.
2765                        set tgt [file join $macports::prefix $tgt]
2766                        file delete $tgt
2767                        ui_debug "rm -f $tgt"
2768                    }
2769                    /* {
2770                        # The source is an absolute path.
2771                        set tgt [file join $macports::prefix $tgt]
2772                        file delete $tgt
2773                        file link -symbolic $tgt $src
2774                        ui_debug "ln -sf $src $tgt"
2775                    }
2776                    default {
2777                        # The source is a relative path.
2778                        set src [file join $macports::prefix $src]
2779                        set tgt [file join $macports::prefix $tgt]
2780                        file delete $tgt
2781                        file link -symbolic $tgt $src
2782                        ui_debug "ln -sf $src $tgt"
2783                    }
2784                }
2785                set i [expr $i+1]
2786            }
2787
2788            # Update the selected version.
2789            set selected_version "$conf_path/current"
2790            if {[file exists $selected_version]} {
2791                file delete $selected_version
2792            }
2793            symlink $version $selected_version
2794            return
2795        }
2796        show {
2797            set selected_version "$conf_path/current"
2798
2799            if {![file exists $selected_version]} {
2800                return "none"
2801            } else {
2802                return [file readlink $selected_version]
2803            }
2804        }
2805    }
2806    return
2807}
Note: See TracBrowser for help on using the repository browser.