source: trunk/base/portmgr/packaging/rpmall.tcl @ 51780

Last change on this file since 51780 was 51780, checked in by jmr@…, 8 years ago

Add depends_fetch and depends_extract options (#15161)

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 14.0 KB
Line 
1#!/usr/bin/env tclsh
2# rpmall.tcl
3# $Id: rpmall.tcl 51780 2009-06-03 06:29:12Z jmr@macports.org $
4#
5# Copyright (c) 2003 Benjamin Reed <ranger@befunk.com>
6# Copyright (c) 2003 Kevin Van Vechten <kevin@opendarwin.org>
7# Copyright (c) 2002 Apple Computer, Inc.
8# All rights reserved.
9#
10# Redistribution and use in source and binary forms, with or without
11# modification, are permitted provided that the following conditions
12# are met:
13# 1. Redistributions of source code must retain the above copyright
14#    notice, this list of conditions and the following disclaimer.
15# 2. Redistributions in binary form must reproduce the above copyright
16#    notice, this list of conditions and the following disclaimer in the
17#    documentation and/or other materials provided with the distribution.
18# 3. Neither the name of Apple Computer, Inc. nor the names of its contributors
19#    may be used to endorse or promote products derived from this software
20#    without specific prior written permission.
21#
22# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32# POSSIBILITY OF SUCH DAMAGE.
33
34package require darwinports
35
36# globals
37set portdir .
38
39# UI Instantiations
40array set ui_options {}
41# ui_options(ports_debug) - If set, output debugging messages.
42# ui_options(ports_verbose) - If set, output info messages (ui_info)
43# ui_options(ports_quiet) - If set, don't output "standard messages"
44
45# ui_options accessor
46proc ui_isset {val} {
47    global ui_options
48    if {[info exists ui_options($val)]} {
49        if {$ui_options($val) == "yes"} {
50            return 1
51        }
52    }
53    return 0
54}
55
56set options(package.destpath) "/darwinports/rpms"
57
58# UI Callback
59
60proc ui_prefix {priority} {
61    switch $priority {
62        debug {
63                return "DEBUG: "
64        }
65        error {
66                return "Error: "
67        }
68        warn {
69                return "Warning: "
70        }
71        default {
72                return ""
73        }
74    }
75}
76
77proc ui_channels {priority} {
78    global logfd
79    switch $priority {
80        debug {
81            if {[ui_isset ports_debug]} {
82                return {stdout}
83            } else {
84                return {}
85            }
86        }
87        info {
88                        # put verbose stuff only to the log file
89            if {[ui_isset ports_verbose]} {
90                return {$logfd}
91            } else {
92                return {}
93                        }
94                }
95        msg {
96            if {[ui_isset ports_quiet]} {
97                return {}
98                        } else {
99                                return {stdout}
100                        }
101                }
102        default {
103                return {stdout}
104        }
105    }
106}
107       
108proc pkg_ui_log {message} {
109    global logfd
110    if {[string length $logfd] > 0 } {
111        log_message $logfd $message
112    }
113}
114
115proc log_message {channel message} {
116    seek $channel 0 end
117    puts $channel $message
118    flush $channel
119}
120
121# Recursive bottom-up approach of building a list of dependencies.
122proc get_dependencies {portname includeBuildDeps} {
123        set result {}
124       
125        if {[catch {set search [mportsearch "^$portname\$"]} error]} {
126                global errorInfo
127                ui_debug "$errorInfo"
128                ui_error "Internal error: port search failed: $error"
129                return {}
130        }
131        foreach {name array} $search {
132                array set portinfo $array
133                if {![info exists portinfo(name)] ||
134                        ![info exists portinfo(version)] || 
135                        ![info exists portinfo(categories)]} {
136                        ui_error "Internal error: $name missing some portinfo keys"
137                        continue
138                }
139                if {![info exists portinfo(revision)]} {
140                        set portinfo(revision) 0
141                }
142               
143                set portname $portinfo(name)
144                set portversion $portinfo(version)
145                set revision $portinfo(revision)
146
147                # Append the package itself to the result list
148                lappend result [list $portname $portversion $revision]
149
150                # Append the package's dependents to the result list
151                set depends {}
152                if {[info exists portinfo(depends_run)]} { eval "lappend depends $portinfo(depends_run)" }
153                if {[info exists portinfo(depends_lib)]} { eval "lappend depends $portinfo(depends_lib)" }
154                if {$includeBuildDeps != "" && [info exists portinfo(depends_build)]} { 
155                        eval "lappend depends $portinfo(depends_build)"
156                }
157                if {$includeBuildDeps != "" && [info exists portinfo(depends_fetch)]} { 
158                        eval "lappend depends $portinfo(depends_fetch)"
159                }
160                if {$includeBuildDeps != "" && [info exists portinfo(depends_extract)]} { 
161                        eval "lappend depends $portinfo(depends_extract)"
162                }
163                foreach depspec $depends {
164                        set dep [lindex [split $depspec :] end]
165                        set x [get_dependencies $dep $includeBuildDeps]
166                        eval "lappend result $x"
167                        set result [lsort -unique $result]
168                }
169        }
170        return $result
171}
172
173# Install binary packages if they've already been built.  This will
174# speed up the testing, since we won't have to recompile dependencies
175# which have already been compiled.
176
177proc install_binary_if_available {dep} {
178        set portname [lindex $dep 0]
179        set portversion [lindex $dep 1]
180        set revision [lindex $dep 2]
181
182        foreach dir {"${prefix}/src/apple/RPMS" "/usr/src/apple/RPMS" "/darwinports/rpms/RPMS"} {
183                foreach arch {"ppc" "i386" "fat"} {
184                        set rpmpath "${dir}/${arch}/${portname}-${portversion}-${revision}.${arch}.rpm"
185                        if {[file readable $rpmpath]} {
186                                ui_msg "Installing binary: $rpmpath"
187                                if {[catch {system "rpm -Uvh --force $rpmpath"} error ]} {
188                                        global errorInfo
189                                        ui_debug "$errorInfo"
190                                        ui_error "Internal error: $error"
191                                } else {
192                                        return true
193                                }
194                        }
195                }
196        }
197        return false
198}
199
200
201# Standard procedures
202
203proc fatal args {
204    global argv0
205    puts stderr "$argv0: $args"
206    exit
207}
208
209# Main
210array set options [list]
211array set variations [list]
212
213#       set ui_options(ports_verbose) yes
214if {![file exists /usr/bin/sw_vers]} {
215        set variations(puredarwin) "+"
216}
217
218if {[catch {mportinit ui_options options variations} result]} {
219    puts "Failed to initialize ports system, $result"
220    exit 1
221}
222
223package require Pextlib
224
225# If no arguments were given, default to all ports.
226if {[llength $argv] == 0} {
227        lappend argv ".*"
228}
229
230foreach pname $argv {
231
232if {[catch {set allpackages [mportsearch "^${pname}\$"]} result]} {
233        puts "port search failed: $result"
234        exit 1
235}
236
237set logpath "/darwinports/logs"
238set logfd ""
239
240foreach {name array} $allpackages {
241        array unset portinfo
242        array set portinfo $array
243
244        #ui_msg "foo $portinfo(porturl)"
245
246        # Start with verbose output off;
247        # this will prevent the repopulation of /opt/local from getting logged.
248        set ui_options(ports_verbose) no
249
250        if {![info exists portinfo(porturl)]} {
251                puts stderr "Internal error: no porturl for $name"
252                continue
253        }
254        if {![info exists portinfo(revision)]} {
255                set portinfo(revision) 0
256        }
257
258        set porturl $portinfo(porturl)
259
260        # this is used to short-circuit the RPM check and
261        # move on to the next package
262
263        global exit_loop
264        set exit_loop false
265
266        # Skip up-to-date packages
267        if {[regsub {^file://} $portinfo(porturl) "" portpath]} {
268                if {[info exists portinfo(name)] &&
269                        [info exists portinfo(version)] &&
270                        [info exists portinfo(revision)]} {
271                        set portname $portinfo(name)
272                        set portversion $portinfo(version)
273                        set revision $portinfo(revision)
274
275                        foreach dir {"/opt/local/src/apple/RPMS" "/usr/src/apple/RPMS" "/darwinports/rpms/RPMS"} {
276                                foreach arch {"ppc" "i386" "fat"} {
277                                        set rpmpath "${dir}/${arch}/${portname}-${portversion}-${revision}.${arch}.rpm"
278                                        #ui_msg "trying ${rpmpath}"
279                                        if {[file readable $rpmpath] && ([file mtime ${rpmpath}] >= [file mtime ${portpath}/Portfile])} {
280                                                puts stderr "->    skipping ${portname}-${portversion}; package is up to date."
281                                                set exit_loop true
282                                                break
283                                        }
284                                }
285                                if {${exit_loop}} {
286                                        break
287                                }
288                        }
289                }
290        }
291        if {${exit_loop}} {
292                continue
293        }
294       
295        # Skip packages which previously failed
296        set exit_loop false
297               
298        # Skip up-to-date packages
299        if {[regsub {^file://} $portinfo(porturl) "" portpath]} {
300                if {[info exists portinfo(name)] &&
301                        [info exists portinfo(version)] &&
302                        [info exists portinfo(revision)]} {
303                        set portname $portinfo(name)
304                        set portversion $portinfo(version)
305                        set revision $portinfo(revision)
306
307                        set logfilepath "${logpath}/${portname}.log"
308                        if {[file readable ${logfilepath}] && ([file mtime ${logfilepath}] > [file mtime ${portpath}/Portfile])} {
309                                puts stderr "->    skipping ${portname}-${portversion}; package failed, but has not changed."
310                                set exit_loop true
311                        }
312                }
313        }
314        if {${exit_loop}} {
315                continue
316        }
317       
318        # Building the port:
319        # - remove /opt/local so it won't pollute the port.
320        # - re-install MacPorts.
321        # - keep distfiles outside /opt/local so we don't have to keep fetching them.
322        # - send out an email to the maintainer if any errors occurred.
323
324        set remove_files ""
325        foreach dir {"/opt/local/src/apple/RPMS" "/usr/src/apple/RPMS" "/darwinports/rpms/RPMS"} {
326                foreach arch {"ppc" "i386" "fat"} {
327                        set remove_files "${remove_files} '${dir}/${arch}/'*.rpm"
328                }
329        }
330        system "rpm -q --queryformat='%{name} ' -p ${remove_files} | xargs rpm -e || true"
331
332        ui_msg "->    Removing /opt/local"
333        #unset ui_options(ports_verbose)
334        if {[catch {system "rm -Rf /opt/local"} error]} {
335                puts stderr "Internal error: $error"
336        }
337        # this is bad on pure darwin  :)
338        #if {[catch {system "rm -Rf /usr/X11R6"} error]} {
339        #       puts stderr "Internal error: $error"
340        #}
341        #if {[catch {system "rm -Rf /etc/X11"} error]} {
342        #       puts stderr "Internal error: $error"
343        #}
344        #if {[catch {system "rm -Rf /etc/fonts"} error]} {
345        #       puts stderr "Internal error: $error"
346        #}
347        ui_msg "->    Installing MacPorts"
348        if {[catch {system "cd $env(HOME)/darwinports && make && make install"} error]} {
349                puts stderr "Internal error: $error"
350        }
351        if {[catch {system "rmdir /opt/local/var/db/dports/distfiles"} error]} {
352                puts stderr "Internal error: $error"
353        }
354        if {[catch {system "ln -s /darwinports/distfiles /opt/local/var/db/dports/distfiles"} error]} {
355                puts stderr "Internal error: $error"
356        }
357        #set ui_options(ports_verbose) yes
358
359        # If there was a log file left over from the previous pass,
360        # then the port failed with an error.  Send the log in an
361        # email to the maintainers.
362        if {[string length $logfd] > 0} {
363                close $logfd
364                set logfd ""
365        }
366        #if {[file readable $logfilename]} {
367        #       if {[catch {system "cat $logfilename | /usr/sbin/sendmail -t"} error]} {
368        #               puts stderr "Internal error: $error"
369        #       }
370        #}
371
372        # Open the log file for writing
373        set logfd [open ${logpath}/${name}.log w]
374
375        set valid 1
376
377        set lint_errors {}
378        set portname ""
379        set portversion ""
380        set description ""
381        set category ""
382
383        if {![info exists portinfo(name)]} {
384                lappend lint_errors "missing name key"
385                set valid 0
386        } else {
387                set portname $portinfo(name)
388        }
389       
390        if {![info exists portinfo(description)]} {
391                lappend lint_errors "missing description key"
392                set valid 0
393        } else {
394                set description $portinfo(description)
395        }
396       
397        if {![info exists portinfo(version)]} {
398                lappend lint_errors "missing version key"
399                set valid 0
400        } else {
401                set portversion $portinfo(version)
402        }
403       
404        if {![info exists portinfo(categories)]} {
405                lappend lint_errors "missing categories key"
406                set valid 0
407        } else {
408                set category [lindex $portinfo(categories) 0]
409        }
410       
411        if {![info exists portinfo(maintainers)]} {
412                append lint_errors "missing maintainers key"
413                set valid 0
414                set maintainers kevin@opendarwin.org
415        } else {
416                set maintainers $portinfo(maintainers)
417        }
418       
419        pkg_ui_log "To: [join $maintainers {, }]"
420        pkg_ui_log "From: donotreply@opendarwin.org"
421        pkg_ui_log "Subject: MacPorts $portinfo(name)-$portinfo(version) build failure"
422        pkg_ui_log ""
423        pkg_ui_log "The following is a transcript produced by the MacPorts automated build       "
424        pkg_ui_log "system.  You are receiving this email because you are listed as a maintainer    "
425        pkg_ui_log "of this port, which has failed the automated packaging process.  Please update  "
426        pkg_ui_log "the port as soon as possible."
427        pkg_ui_log ""
428        pkg_ui_log ""
429        pkg_ui_log "Thank you,"
430        pkg_ui_log "The MacPorts Team"
431        pkg_ui_log ""
432        pkg_ui_log "================================================================================"
433        pkg_ui_log ""
434
435        if {!$valid} {
436                foreach error $lint_errors {
437                        ui_error $error
438                }
439        }
440
441        ui_msg "-->   Packaging ${category}/${portname}-${portversion}"
442
443        foreach prebuild {"ccache" "rpm" "unzip"} {
444                if {![file exists /bin/${prebuild}] && ![file exists /usr/bin/${prebuild}]} {
445                        ui_msg "--->  Pre-installing ${prebuild}"
446                        if {[catch {set search [mportsearch "^${prebuild}\$"]} error]} {
447                                global errorInfo
448                                ui_debug "$errorInfo"
449                                ui_error "Internal error: port search ${prebuild} failed: $error"
450                        }
451                        array set prebuildinfo [lindex $search 1]
452                        set ui_options(ports_verbose) yes
453                        if {[catch {set workername [mportopen $prebuildinfo(porturl) [array get options] [array get variations] yes]} result] ||
454                                $result == 1} {
455                                global errorInfo
456                                ui_debug "$errorInfo"
457                                ui_error "Internal error: unable to install ${prebuild}... exiting"
458                                exit 1
459                        }
460                        if {[catch {set result [mportexec $workername install]} result] ||
461                                $result == 1} {
462                                global errorInfo
463                                ui_debug "$errorInfo"
464                                ui_error "installation of ${prebuild} failed: $result"
465                                mportclose $workername
466                                exit 1
467                        }
468                }
469        }
470
471        # Turn on verbose output for the build
472        set ui_options(ports_verbose) yes
473        if {[catch {set workername [mportopen $porturl [array get options] [array get variations]]} result] ||
474                $result == 1} {
475                global errorInfo
476                ui_debug "$errorInfo"
477            ui_error "Internal error: unable to open port: $result"
478            continue
479        }
480        if {[catch {set result [mportexec $workername rpmpackage]} result] ||
481                $result == 1} {
482                global errorInfo
483                ui_debug "$errorInfo"
484            ui_error "port package failed: $result"
485                mportclose $workername
486            continue
487        }
488        set ui_options(ports_verbose) no
489        # Turn verbose output off after the build
490
491        mportclose $workername
492
493        # We made it to the end.  We can delete the log file.
494        close $logfd
495        set logfd ""
496        file delete ${logpath}/${name}.log
497}
498
499}
500# end foreach pname
Note: See TracBrowser for help on using the repository browser.