source: trunk/base/portmgr/rpmall.tcl @ 15441

Last change on this file since 15441 was 15338, checked in by jmpp, 15 years ago

Submitted by: jmpp@
Reviewed by: jberry@

The pormgr/ dir is now out of src/, as it doesn't have much to do with our sources.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 13.7 KB
Line 
1#!/usr/bin/env tclsh
2# rpmall.tcl
3# $Id: rpmall.tcl,v 1.1 2005/12/06 20:47:42 jmpp Exp $
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 [dportsearch "^$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                foreach depspec $depends {
158                        set dep [lindex [split $depspec :] end]
159                        set x [get_dependencies $dep $includeBuildDeps]
160                        eval "lappend result $x"
161                        set result [lsort -unique $result]
162                }
163        }
164        return $result
165}
166
167# Install binary packages if they've already been built.  This will
168# speed up the testing, since we won't have to recompile dependencies
169# which have already been compiled.
170
171proc install_binary_if_available {dep} {
172        set portname [lindex $dep 0]
173        set portversion [lindex $dep 1]
174        set revision [lindex $dep 2]
175
176        foreach dir {"${prefix}/src/apple/RPMS" "/usr/src/apple/RPMS" "/darwinports/rpms/RPMS"} {
177                foreach arch {"ppc" "i386" "fat"} {
178                        set rpmpath "${dir}/${arch}/${portname}-${portversion}-${revision}.${arch}.rpm"
179                        if {[file readable $rpmpath]} {
180                                ui_msg "Installing binary: $rpmpath"
181                                if {[catch {system "rpm -Uvh --force $rpmpath"} error ]} {
182                                        global errorInfo
183                                        ui_debug "$errorInfo"
184                                        ui_error "Internal error: $error"
185                                } else {
186                                        return true
187                                }
188                        }
189                }
190        }
191        return false
192}
193
194
195# Standard procedures
196
197proc fatal args {
198    global argv0
199    puts stderr "$argv0: $args"
200    exit
201}
202
203# Main
204array set options [list]
205array set variations [list]
206
207#       set ui_options(ports_verbose) yes
208if {![file exists /usr/bin/sw_vers]} {
209        set variations(puredarwin) "+"
210}
211
212if {[catch {dportinit ui_options options variations} result]} {
213    puts "Failed to initialize ports system, $result"
214    exit 1
215}
216
217package require Pextlib
218
219# If no arguments were given, default to all ports.
220if {[llength $argv] == 0} {
221        lappend argv ".*"
222}
223
224foreach pname $argv {
225
226if {[catch {set allpackages [dportsearch "^${pname}\$"]} result]} {
227        puts "port search failed: $result"
228        exit 1
229}
230
231set logpath "/darwinports/logs"
232set logfd ""
233
234foreach {name array} $allpackages {
235        array unset portinfo
236        array set portinfo $array
237
238        #ui_msg "foo $portinfo(porturl)"
239
240        # Start with verbose output off;
241        # this will prevent the repopulation of /opt from getting logged.
242        set ui_options(ports_verbose) no
243
244        if {![info exists portinfo(porturl)]} {
245                puts stderr "Internal error: no porturl for $name"
246                continue
247        }
248        if {![info exists portinfo(revision)]} {
249                set portinfo(revision) 0
250        }
251
252        set porturl $portinfo(porturl)
253
254        # this is used to short-circuit the RPM check and
255        # move on to the next package
256
257        global exit_loop
258        set exit_loop false
259
260        # Skip up-to-date packages
261        if {[regsub {^file://} $portinfo(porturl) "" portpath]} {
262                if {[info exists portinfo(name)] &&
263                        [info exists portinfo(version)] &&
264                        [info exists portinfo(revision)]} {
265                        set portname $portinfo(name)
266                        set portversion $portinfo(version)
267                        set revision $portinfo(revision)
268
269                        foreach dir {"/opt/local/src/apple/RPMS" "/usr/src/apple/RPMS" "/darwinports/rpms/RPMS"} {
270                                foreach arch {"ppc" "i386" "fat"} {
271                                        set rpmpath "${dir}/${arch}/${portname}-${portversion}-${revision}.${arch}.rpm"
272                                        #ui_msg "trying ${rpmpath}"
273                                        if {[file readable $rpmpath] && ([file mtime ${rpmpath}] >= [file mtime ${portpath}/Portfile])} {
274                                                puts stderr "->    skipping ${portname}-${portversion}; package is up to date."
275                                                set exit_loop true
276                                                break
277                                        }
278                                }
279                                if {${exit_loop}} {
280                                        break
281                                }
282                        }
283                }
284        }
285        if {${exit_loop}} {
286                continue
287        }
288       
289        # Skip packages which previously failed
290        set exit_loop false
291               
292        # Skip up-to-date packages
293        if {[regsub {^file://} $portinfo(porturl) "" portpath]} {
294                if {[info exists portinfo(name)] &&
295                        [info exists portinfo(version)] &&
296                        [info exists portinfo(revision)]} {
297                        set portname $portinfo(name)
298                        set portversion $portinfo(version)
299                        set revision $portinfo(revision)
300
301                        set logfilepath "${logpath}/${portname}.log"
302                        if {[file readable ${logfilepath}] && ([file mtime ${logfilepath}] > [file mtime ${portpath}/Portfile])} {
303                                puts stderr "->    skipping ${portname}-${portversion}; package failed, but has not changed."
304                                set exit_loop true
305                        }
306                }
307        }
308        if {${exit_loop}} {
309                continue
310        }
311       
312        # Building the port:
313        # - remove /opt so it won't pollute the port.
314        # - re-install DarwinPorts.
315        # - keep distfiles outside /opt so we don't have to keep fetching them.
316        # - send out an email to the maintainer if any errors occurred.
317
318        set remove_files ""
319        foreach dir {"/opt/local/src/apple/RPMS" "/usr/src/apple/RPMS" "/darwinports/rpms/RPMS"} {
320                foreach arch {"ppc" "i386" "fat"} {
321                        set remove_files "${remove_files} '${dir}/${arch}/'*.rpm"
322                }
323        }
324        system "rpm -q --queryformat='%{name} ' -p ${remove_files} | xargs rpm -e || true"
325
326        ui_msg "->    Removing /opt"
327        #unset ui_options(ports_verbose)
328        if {[catch {system "rm -Rf /opt"} error]} {
329                puts stderr "Internal error: $error"
330        }
331        # this is bad on pure darwin  :)
332        #if {[catch {system "rm -Rf /usr/X11R6"} error]} {
333        #       puts stderr "Internal error: $error"
334        #}
335        #if {[catch {system "rm -Rf /etc/X11"} error]} {
336        #       puts stderr "Internal error: $error"
337        #}
338        #if {[catch {system "rm -Rf /etc/fonts"} error]} {
339        #       puts stderr "Internal error: $error"
340        #}
341        ui_msg "->    Installing darwinports"
342        if {[catch {system "cd $env(HOME)/darwinports && make && make install"} error]} {
343                puts stderr "Internal error: $error"
344        }
345        if {[catch {system "rmdir /opt/local/var/db/dports/distfiles"} error]} {
346                puts stderr "Internal error: $error"
347        }
348        if {[catch {system "ln -s /darwinports/distfiles /opt/local/var/db/dports/distfiles"} error]} {
349                puts stderr "Internal error: $error"
350        }
351        #set ui_options(ports_verbose) yes
352
353        # If there was a log file left over from the previous pass,
354        # then the port failed with an error.  Send the log in an
355        # email to the maintainers.
356        if {[string length $logfd] > 0} {
357                close $logfd
358                set logfd ""
359        }
360        #if {[file readable $logfilename]} {
361        #       if {[catch {system "cat $logfilename | /usr/sbin/sendmail -t"} error]} {
362        #               puts stderr "Internal error: $error"
363        #       }
364        #}
365
366        # Open the log file for writing
367        set logfd [open ${logpath}/${name}.log w]
368
369        set valid 1
370
371        set lint_errors {}
372        set portname ""
373        set portversion ""
374        set description ""
375        set category ""
376
377        if {![info exists portinfo(name)]} {
378                lappend lint_errors "missing name key"
379                set valid 0
380        } else {
381                set portname $portinfo(name)
382        }
383       
384        if {![info exists portinfo(description)]} {
385                lappend lint_errors "missing description key"
386                set valid 0
387        } else {
388                set description $portinfo(description)
389        }
390       
391        if {![info exists portinfo(version)]} {
392                lappend lint_errors "missing version key"
393                set valid 0
394        } else {
395                set portversion $portinfo(version)
396        }
397       
398        if {![info exists portinfo(categories)]} {
399                lappend lint_errors "missing categories key"
400                set valid 0
401        } else {
402                set category [lindex $portinfo(categories) 0]
403        }
404       
405        if {![info exists portinfo(maintainers)]} {
406                append lint_errors "missing maintainers key"
407                set valid 0
408                set maintainers kevin@opendarwin.org
409        } else {
410                set maintainers $portinfo(maintainers)
411        }
412       
413        pkg_ui_log "To: [join $maintainers {, }]"
414        pkg_ui_log "From: donotreply@opendarwin.org"
415        pkg_ui_log "Subject: DarwinPorts $portinfo(name)-$portinfo(version) build failure"
416        pkg_ui_log ""
417        pkg_ui_log "The following is a transcript produced by the DarwinPorts automated build       "
418        pkg_ui_log "system.  You are receiving this email because you are listed as a maintainer    "
419        pkg_ui_log "of this port, which has failed the automated packaging process.  Please update  "
420        pkg_ui_log "the port as soon as possible."
421        pkg_ui_log ""
422        pkg_ui_log ""
423        pkg_ui_log "Thank you,"
424        pkg_ui_log "The DarwinPorts Team"
425        pkg_ui_log ""
426        pkg_ui_log "================================================================================"
427        pkg_ui_log ""
428
429        if {!$valid} {
430                foreach error $lint_errors {
431                        ui_error $error
432                }
433        }
434
435        ui_msg "-->   Packaging ${category}/${portname}-${portversion}"
436
437        foreach prebuild {"ccache" "rpm" "unzip"} {
438                if {![file exists /bin/${prebuild}] && ![file exists /usr/bin/${prebuild}]} {
439                        ui_msg "--->  Pre-installing ${prebuild}"
440                        if {[catch {set search [dportsearch "^${prebuild}\$"]} error]} {
441                                global errorInfo
442                                ui_debug "$errorInfo"
443                                ui_error "Internal error: port search ${prebuild} failed: $error"
444                        }
445                        array set prebuildinfo [lindex $search 1]
446                        set ui_options(ports_verbose) yes
447                        if {[catch {set workername [dportopen $prebuildinfo(porturl) [array get options] [array get variations] yes]} result] ||
448                                $result == 1} {
449                                global errorInfo
450                                ui_debug "$errorInfo"
451                                ui_error "Internal error: unable to install ${prebuild}... exiting"
452                                exit 1
453                        }
454                        if {[catch {set result [dportexec $workername install]} result] ||
455                                $result == 1} {
456                                global errorInfo
457                                ui_debug "$errorInfo"
458                                ui_error "installation of ${prebuild} failed: $result"
459                                dportclose $workername
460                                exit 1
461                        }
462                }
463        }
464
465        # Turn on verbose output for the build
466        set ui_options(ports_verbose) yes
467        if {[catch {set workername [dportopen $porturl [array get options] [array get variations]]} result] ||
468                $result == 1} {
469                global errorInfo
470                ui_debug "$errorInfo"
471            ui_error "Internal error: unable to open port: $result"
472            continue
473        }
474        if {[catch {set result [dportexec $workername rpmpackage]} result] ||
475                $result == 1} {
476                global errorInfo
477                ui_debug "$errorInfo"
478            ui_error "port package failed: $result"
479                dportclose $workername
480            continue
481        }
482        set ui_options(ports_verbose) no
483        # Turn verbose output off after the build
484
485        dportclose $workername
486
487        # We made it to the end.  We can delete the log file.
488        close $logfd
489        set logfd ""
490        file delete ${logpath}/${name}.log
491}
492
493}
494# end foreach pname
Note: See TracBrowser for help on using the repository browser.