source: trunk/base/src/portmgr/rpmall.tcl @ 7156

Last change on this file since 7156 was 7156, checked in by jkh, 16 years ago

Add another (optional) argument to dportopen so that we can defeat the cache in cases where we need to open an existing instance
but set different options for it.
Reviewed by: kvv

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