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

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

update copyright notices

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