Projects
New Ticket     Wiki     Browse Source     Timeline     Roadmap     Bug Reports     Search

root/trunk/base/src/port/port.tcl

Revision 45036, 113.3 KB (checked in by perry@…, 19 hours ago)

port/port.tcl - Removed a needlessly nested if statement.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1#!/bin/sh
2# -*- 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
3# Run the Tcl interpreter \
4exec @TCLSH@ "$0" "$@"
5# port.tcl
6# $Id$
7#
8# Copyright (c) 2002-2007 The MacPorts Project.
9# Copyright (c) 2004 Robert Shaw <rshaw@opendarwin.org>
10# Copyright (c) 2002 Apple Computer, Inc.
11# All rights reserved.
12#
13# Redistribution and use in source and binary forms, with or without
14# modification, are permitted provided that the following conditions
15# are met:
16# 1. Redistributions of source code must retain the above copyright
17#    notice, this list of conditions and the following disclaimer.
18# 2. Redistributions in binary form must reproduce the above copyright
19#    notice, this list of conditions and the following disclaimer in the
20#    documentation and/or other materials provided with the distribution.
21# 3. Neither the name of Apple Computer, Inc. nor the names of its contributors
22#    may be used to endorse or promote products derived from this software
23#    without specific prior written permission.
24#
25# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35# POSSIBILITY OF SUCH DAMAGE.
36
37catch {source \
38    [file join "@TCL_PACKAGE_DIR@" macports1.0 macports_fastload.tcl]}
39package require macports
40package require Pextlib 1.0
41
42
43# Standard procedures
44proc print_usage {args} {
45    global cmdname
46    set syntax {
47        [-bcdfiknopqRstuvxy] [-D portdir] [-F cmdfile] action [privopts] [actionflags]
48        [[portname|pseudo-portname|port-url] [@version] [+-variant]... [option=value]...]...
49    }
50
51    puts stderr "Usage: $cmdname$syntax"
52    puts stderr "\"$cmdname help\" or \"man 1 port\" for more information."
53}
54
55proc print_help {args} {
56    global cmdname
57    global action_array
58   
59    set syntax {
60        [-bcdfiknopqRstuvx] [-D portdir] [-F cmdfile] action [privopts] [actionflags]
61        [[portname|pseudo-portname|port-url] [@version] [+-variant]... [option=value]...]...
62    }
63
64    # Generate and format the command list from the action_array
65    set cmds ""
66    set lineLen 0
67    foreach cmd [lsort [array names action_array]] {
68        if {$lineLen > 65} {
69            set cmds "$cmds,\n"
70            set lineLen 0
71        }
72        if {$lineLen == 0} {
73            set new "$cmd"
74        } else {
75            set new ", $cmd"
76        }
77        incr lineLen [string length $new]
78        set cmds "$cmds$new"
79    }
80   
81    set cmdText "
82Supported commands
83------------------
84$cmds
85"
86
87    set text {
88Pseudo-portnames
89----------------
90Pseudo-portnames are words that may be used in place of a portname, and
91which expand to some set of ports. The common pseudo-portnames are:
92all, current, active, inactive, installed, uninstalled, and outdated.
93These pseudo-portnames expand to the set of ports named.
94
95Additional pseudo-portnames start with...
96variants:, variant:, description:, depends:, depends_lib:, depends_run:,
97depends_build:, portdir:, homepage:, epoch:, platforms:, platform:, name:,
98long_description:, maintainers:, maintainer:, categories:, category:, version:,
99and revision:.
100These each select a set of ports based on a regex search of metadata
101about the ports. In all such cases, a standard regex pattern following
102the colon will be used to select the set of ports to which the
103pseudo-portname expands.
104
105Portnames that contain standard glob characters will be expanded to the
106set of ports matching the glob pattern.
107   
108Port expressions
109----------------
110Portnames, port glob patterns, and pseudo-portnames may be logically
111combined using expressions consisting of and, or, not, !, (, and ).
112   
113For more information
114--------------------
115See man pages: port(1), macports.conf(5), portfile(7), portgroup(7),
116porthier(7), portstyle(7). Also, see http://www.macports.org.
117    }
118
119
120    puts "$cmdname$syntax $cmdText $text"
121}
122
123
124# Produce error message and exit
125proc fatal s {
126    global argv0
127    ui_error "$argv0: $s"
128    exit 1
129}
130
131
132# Produce an error message, and exit, unless
133# we're handling errors in a soft fashion, in which
134# case we continue
135proc fatal_softcontinue s {
136    if {[macports::global_option_isset ports_force]} {
137        ui_error $s
138        return -code continue
139    } else {
140        fatal $s
141    }
142}
143
144
145# Produce an error message, and break, unless
146# we're handling errors in a soft fashion, in which
147# case we continue
148proc break_softcontinue { msg status name_status } {
149    upvar $name_status status_var
150    ui_error $msg
151    if {[macports::ui_isset ports_processall]} {
152        set status_var 0
153        return -code continue
154    } else {
155        set status_var $status
156        return -code break
157    }
158}
159
160
161# Form a composite version as is sometimes used for registry functions
162proc composite_version {version variations {emptyVersionOkay 0}} {
163    # Form a composite version out of the version and variations
164   
165    # Select the variations into positive and negative
166    set pos {}
167    set neg {}
168    foreach { key val } $variations {
169        if {$val == "+"} {
170            lappend pos $key
171        } elseif {$val == "-"} {
172            lappend neg $key
173        }
174    }
175
176    # If there is no version, we have nothing to do
177    set composite_version ""
178    if {$version != "" || $emptyVersionOkay} {
179        set pos_str ""
180        set neg_str ""
181
182        if {[llength $pos]} {
183            set pos_str "+[join [lsort -ascii $pos] "+"]"
184        }
185        if {[llength $neg]} {
186            set neg_str "-[join [lsort -ascii $neg] "-"]"
187        }
188
189        set composite_version "$version$pos_str$neg_str"
190    }
191
192    return $composite_version
193}
194
195
196proc split_variants {variants} {
197    set result {}
198    set l [regexp -all -inline -- {([-+])([[:alpha:]_]+[\w\.]*)} $variants]
199    foreach { match sign variant } $l {
200        lappend result $variant $sign
201    }
202    return $result
203}
204
205
206##
207# Maps friendly field names to their real name
208# Names which do not need mapping are not changed.
209#
210# @param field friendly name
211# @return real name
212proc map_friendly_field_names { field } {
213    switch -- $field {
214        variant -
215        platform -
216        maintainer {
217            set field "${field}s"
218        }
219        category {
220            set field "categories"
221        }
222    }
223
224    return $field
225}
226
227
228proc registry_installed {portname {portversion ""}} {
229    set ilist [registry::installed $portname $portversion]
230    if { [llength $ilist] > 1 } {
231        # set portname again since the one we were passed may not have had the correct case
232        set portname [lindex [lindex $ilist 0] 0]
233        puts "The following versions of $portname are currently installed:"
234        foreach i [portlist_sortint $ilist] {
235            set iname [lindex $i 0]
236            set iversion [lindex $i 1]
237            set irevision [lindex $i 2]
238            set ivariants [lindex $i 3]
239            set iactive [lindex $i 4]
240            if { $iactive == 0 } {
241                puts "  $iname ${iversion}_${irevision}${ivariants}"
242            } elseif { $iactive == 1 } {
243                puts "  $iname ${iversion}_${irevision}${ivariants} (active)"
244            }
245        }
246        return -code error "Registry error: Please specify the full version as recorded in the port registry."
247    } else {
248        return [lindex $ilist 0]
249    }
250}
251
252
253proc add_to_portlist {listname portentry} {
254    upvar $listname portlist
255    global global_options global_variations
256
257    # The portlist currently has the following elements in it:
258    #   url             if any
259    #   name
260    #   version         (version_revision)
261    #   variants array  (variant=>+-)
262    #   options array   (key=>value)
263    #   fullname        (name/version_revision+-variants)
264
265    array set port $portentry
266    if {![info exists port(url)]}       { set port(url) "" }
267    if {![info exists port(name)]}      { set port(name) "" }
268    if {![info exists port(version)]}   { set port(version) "" }
269    if {![info exists port(variants)]}  { set port(variants) "" }
270    if {![info exists port(options)]}   { set port(options) [array get global_options] }
271
272    # If neither portname nor url is specified, then default to the current port
273    if { $port(url) == "" && $port(name) == "" } {
274        set url file://.
275        set portname [url_to_portname $url]
276        set port(url) $url
277        set port(name) $portname
278        if {$portname == ""} {
279            ui_error "A default port name could not be supplied."
280        }
281    }
282
283
284    # Form the fully descriminated portname: portname/version_revison+-variants
285    set port(fullname) "$port(name)/[composite_version $port(version) $port(variants)]"
286   
287    # Add it to our portlist
288    lappend portlist [array get port]
289}
290
291
292proc add_ports_to_portlist {listname ports {overridelist ""}} {
293    upvar $listname portlist
294
295    array set overrides $overridelist
296
297    # Add each entry to the named portlist, overriding any values
298    # specified as overrides
299    foreach portentry $ports {
300        array set port $portentry
301        if ([info exists overrides(version)])   { set port(version) $overrides(version) }
302        if ([info exists overrides(variants)])  { set port(variants) $overrides(variants)   }
303        if ([info exists overrides(options)])   { set port(options) $overrides(options) }
304        add_to_portlist portlist [array get port]
305    }
306}
307
308
309proc url_to_portname { url {quiet 0} } {
310    # Save directory and restore the directory, since mportopen changes it
311    set savedir [pwd]
312    set portname ""
313    if {[catch {set ctx [mportopen $url]} result]} {
314        if {!$quiet} {
315            ui_msg "Can't map the URL '$url' to a port description file (\"${result}\")."
316            ui_msg "Please verify that the directory and portfile syntax are correct."
317        }
318    } else {
319        array set portinfo [mportinfo $ctx]
320        set portname $portinfo(name)
321        mportclose $ctx
322    }
323    cd $savedir
324    return $portname
325}
326
327
328# Supply a default porturl/portname if the portlist is empty
329proc require_portlist { nameportlist } {
330    global private_options
331    upvar $nameportlist portlist
332
333    if {[llength $portlist] == 0 && (![info exists private_options(ports_no_args)] || $private_options(ports_no_args) == "no")} {
334        ui_error "No ports found"
335        return 1
336    }
337
338    if {[llength $portlist] == 0} {
339        set portlist [get_current_port]
340
341        if {[llength $portlist] == 0} {
342            # there was no port in current directory
343            return 1
344        }
345    }
346
347    return 0
348}
349
350
351# Execute the enclosed block once for every element in the portlist
352# When the block is entered, the variables portname, portversion, options, and variations
353# will have been set
354proc foreachport {portlist block} {
355    # Restore cwd after each port, since mportopen changes it, and relative
356    # urls will break on subsequent passes
357    set savedir [pwd]
358    foreach portspec $portlist {
359        uplevel 1 "array set portspec { $portspec }"
360        uplevel 1 {
361            set porturl $portspec(url)
362            set portname $portspec(name)
363            set portversion $portspec(version)
364            array unset variations
365            array set variations $portspec(variants)
366            array unset options
367            array set options $portspec(options)
368        }
369        uplevel 1 $block
370        cd $savedir
371    }
372}
373
374
375proc portlist_compare { a b } {
376    array set a_ $a
377    array set b_ $b
378    set namecmp [string compare -nocase $a_(name) $b_(name)]
379    if {$namecmp != 0} {
380        return $namecmp
381    }
382    set avr_ [split $a_(version) "_"]
383    set bvr_ [split $b_(version) "_"]
384    set vercmp [rpm-vercomp [lindex $avr_ 0] [lindex $bvr_ 0]]
385    if {$vercmp != 0} {
386        return $vercmp
387    }
388    set ar_ [lindex $avr_ 1]
389    set br_ [lindex $bvr_ 1]
390    if {$ar_ < $br_} {
391        return -1
392    } elseif {$ar_ > $br_} {
393        return 1
394    } else {
395        return 0
396    }
397}
398
399# Sort two ports in NVR (name@version_revision) order
400proc portlist_sort { list } {
401    return [lsort -command portlist_compare $list]
402}
403
404proc portlist_compareint { a b } {
405    array set a_ [list "name" [lindex $a 0] "version" [lindex $a 1] "revision" [lindex $a 2]]
406    array set b_ [list "name" [lindex $b 0] "version" [lindex $b 1] "revision" [lindex $b 2]]
407    return [portlist_compare [array get a_] [array get b_]]
408}
409
410# Same as portlist_sort, but with numeric indexes
411proc portlist_sortint { list } {
412    return [lsort -command portlist_compareint $list]
413}
414
415proc regex_pat_sanitize { s } {
416    set sanitized [regsub -all {[\\(){}+$.^]} $s {\\&}]
417    return $sanitized
418}
419
420##
421# Makes sure we get the current terminal size
422proc term_init_size {} {
423    global env
424
425    if {![info exists env(COLUMNS)] || ![info exists env(LINES)]} {
426        if {[isatty stdout]} {
427            set size [term_get_size stdout]
428
429            if {![info exists env(LINES)]} {
430                set env(LINES) [lindex $size 0]
431            }
432
433            if {![info exists env(COLUMNS)]} {
434                set env(COLUMNS) [lindex $size 1]
435            }
436        }
437    }
438}
439
440##
441# Wraps a multi-line string at specified textwidth
442#
443# @see wrapline
444#
445# @param string input string
446# @param maxlen text width (0 defaults to current terminal width)
447# @param indent prepend to every line
448# @return wrapped string
449proc wrap {string maxlen {indent ""} {indentfirstline 1}} {
450    global env
451
452    if {$maxlen == 0} {
453        if {![info exists env(COLUMNS)]} {
454            # no width for wrapping
455            return $string
456        }
457        set maxlen $env(COLUMNS)
458    }
459
460    set splitstring {}
461    foreach line [split $string "\n"] {
462        lappend splitstring [wrapline $line $maxlen $indent $indentfirstline]
463    }
464    return [join $splitstring "\n"]
465}
466
467##
468# Wraps a line at specified textwidth
469#
470# @see wrap
471#
472# @param line input line
473# @param maxlen text width (0 defaults to current terminal width)
474# @param indent prepend to every line
475# @return wrapped string
476proc wrapline {line maxlen {indent ""} {indentfirstline 1}} {
477    global env
478
479    if {$maxlen == 0} {
480        if {![info exists env(COLUMNS)]} {
481            # no width for wrapping
482            return $string
483        }
484        set maxlen $env(COLUMNS)
485    }
486
487    set string [split $line " "]
488    if {$indentfirstline == 0} {
489        set newline ""
490        set maxlen [expr $maxlen - [string length $indent]]
491    } else {
492        set newline $indent
493    }
494    append newline [lindex $string 0]
495    set joiner " "
496    set first 1
497    foreach word [lrange $string 1 end] {
498        if {[string length $newline]+[string length $word] >= $maxlen} {
499            lappend lines $newline
500            set newline $indent
501            set joiner ""
502        }
503        append newline $joiner $word
504        set joiner " "
505        set first 0
506        if {$first == 1 && $indentfirstline == 0} {
507            set maxlen [expr $maxlen + [string length $indent]]
508        }
509    }
510    lappend lines $newline
511    return [join $lines "\n"]
512}
513
514##
515# Wraps a line at a specified width with a label in front
516#
517# @see wrap
518#
519# @param label label for output
520# @param string input string
521# @param maxlen text width (0 defaults to current terminal width)
522# @return wrapped string
523proc wraplabel {label string maxlen {indent ""}} {
524    append label ": [string repeat " " [expr [string length $indent] - [string length "$label: "]]]"
525    return "$label[wrap $string $maxlen $indent 0]"
526}
527
528proc unobscure_maintainers { list } {
529    set result {}
530    foreach m $list {
531        if {[string first "@" $m] < 0} {
532            if {[string first ":" $m] >= 0} {
533                set m [regsub -- "(.*):(.*)" $m "\\2@\\1"]
534            } else {
535                set m "$m@macports.org"
536            }
537        }
538        lappend result $m
539    }
540    return $result
541}
542
543
544##########################################
545# Port selection
546##########################################
547proc get_matching_ports {pattern {casesensitive no} {matchstyle glob} {field name}} {
548    if {[catch {set res [mportsearch $pattern $casesensitive $matchstyle $field]} result]} {
549        global errorInfo
550        ui_debug "$errorInfo"
551        fatal "search for portname $pattern failed: $result"
552    }
553
554    set results {}
555    foreach {name info} $res {
556        array unset portinfo
557        array set portinfo $info
558
559        #set variants {}
560        #if {[info exists portinfo(variants)]} {
561        #   foreach variant $portinfo(variants) {
562        #       lappend variants $variant "+"
563        #   }
564        #}
565        # For now, don't include version or variants with all ports list
566        #"$portinfo(version)_$portinfo(revision)"
567        #$variants
568        add_to_portlist results [list url $portinfo(porturl) name $name]
569    }
570
571    # Return the list of all ports, sorted
572    return [portlist_sort $results]
573}
574
575
576proc get_all_ports {} {
577    global all_ports_cache
578
579    if {![info exists all_ports_cache]} {
580        set all_ports_cache [get_matching_ports "*"]
581    }
582    return $all_ports_cache
583}
584
585
586proc get_current_ports {} {
587    # This is just a synonym for get_current_port that
588    # works with the regex in element
589    return [get_current_port]
590}
591
592
593proc get_current_port {} {
594    set url file://.
595    set portname [url_to_portname $url]
596    if {$portname == ""} {
597        ui_msg "To use the current port, you must be in a port's directory."
598        ui_msg "(you might also see this message if a pseudo-port such as"
599        ui_msg "outdated or installed expands to no ports)."
600        return [list]
601    }
602
603    set results {}
604    add_to_portlist results [list url $url name $portname]
605    return $results
606}
607
608
609proc get_installed_ports { {ignore_active yes} {active yes} } {
610    set ilist {}
611    if { [catch {set ilist [registry::installed]} result] } {
612        if {$result != "Registry error: No ports registered as installed."} {
613            global errorInfo
614            ui_debug "$errorInfo"
615            fatal "port installed failed: $result"
616        }
617    }
618
619    set results {}
620    foreach i $ilist {
621        set iname [lindex $i 0]
622        set iversion [lindex $i 1]
623        set irevision [lindex $i 2]
624        set ivariants [split_variants [lindex $i 3]]
625        set iactive [lindex $i 4]
626
627        if { ${ignore_active} == "yes" || (${active} == "yes") == (${iactive} != 0) } {
628            add_to_portlist results [list name $iname version "${iversion}_${irevision}" variants $ivariants]
629        }
630    }
631
632    # Return the list of ports, sorted
633    return [portlist_sort $results]
634}
635
636
637proc get_uninstalled_ports {} {
638    # Return all - installed
639    set all [get_all_ports]
640    set installed [get_installed_ports]
641    return [opComplement $all $installed]
642}
643
644
645proc get_active_ports {} {
646    return [get_installed_ports no yes]
647}
648
649
650proc get_inactive_ports {} {
651    return [get_installed_ports no no]
652}
653
654
655proc get_outdated_ports {} {
656    global macports::registry.installtype
657    set is_image_mode [expr 0 == [string compare "image" ${macports::registry.installtype}]]
658
659    # Get the list of installed ports
660    set ilist {}
661    if { [catch {set ilist [registry::installed]} result] } {
662        if {$result != "Registry error: No ports registered as installed."} {
663            global errorInfo
664            ui_debug "$errorInfo"
665            fatal "port installed failed: $result"
666        }
667    }
668
669    # Now process the list, keeping only those ports that are outdated
670    set results {}
671    if { [llength $ilist] > 0 } {
672        foreach i $ilist {
673
674            # Get information about the installed port
675            set portname            [lindex $i 0]
676            set installed_version   [lindex $i 1]
677            set installed_revision  [lindex $i 2]
678            set installed_compound  "${installed_version}_${installed_revision}"
679            set installed_variants  [lindex $i 3]
680
681            set is_active           [lindex $i 4]
682            if { $is_active == 0 && $is_image_mode } continue
683
684            set installed_epoch     [lindex $i 5]
685
686            # Get info about the port from the index
687            if {[catch {set res [mportsearch $portname no exact]} result]} {
688                global errorInfo
689                ui_debug "$errorInfo"
690                fatal "search for portname $portname failed: $result"
691            }
692            if {[llength $res] < 2} {
693                if {[macports::ui_isset ports_debug]} {
694                    puts stderr "$portname ($installed_compound is installed; the port was not found in the port index)"
695                }
696                continue
697            }
698            array unset portinfo
699            array set portinfo [lindex $res 1]
700
701            # Get information about latest available version and revision
702            set latest_version $portinfo(version)
703            set latest_revision     0
704            if {[info exists portinfo(revision)] && $portinfo(revision) > 0} {
705                set latest_revision $portinfo(revision)
706            }
707            set latest_compound     "${latest_version}_${latest_revision}"
708            set latest_epoch        0
709            if {[info exists portinfo(epoch)]} {
710                set latest_epoch    $portinfo(epoch)
711            }
712
713            # Compare versions, first checking epoch, then version, then revision
714            set comp_result [expr $installed_epoch - $latest_epoch]
715            if { $comp_result == 0 } {
716                set comp_result [rpm-vercomp $installed_version $latest_version]
717                if { $comp_result == 0 } {
718                    set comp_result [rpm-vercomp $installed_revision $latest_revision]
719                }
720            }
721
722            # Add outdated ports to our results list
723            if { $comp_result < 0 } {
724                add_to_portlist results [list name $portname version $installed_compound variants [split_variants $installed_variants]]
725            }
726        }
727    }
728
729    return $results
730}
731
732
733
734##########################################
735# Port expressions
736##########################################
737proc portExpr { resname } {
738    upvar $resname reslist
739    set result [seqExpr reslist]
740    return $result
741}
742
743
744proc seqExpr { resname } {
745    upvar $resname reslist
746   
747    # Evaluate a sequence of expressions a b c...
748    # These act the same as a or b or c
749
750    set result 1
751    while {$result} {
752        switch -- [lookahead] {
753            ;       -
754            )       -
755            _EOF_   { break }
756        }
757
758        set blist {}
759        set result [orExpr blist]
760        if {$result} {
761            # Calculate the union of result and b
762            set reslist [opUnion $reslist $blist]
763        }
764    }
765
766    return $result
767}
768
769
770