source: trunk/base/src/port1.0/portlint.tcl @ 30271

Last change on this file since 30271 was 30271, checked in by afb@…, 10 years ago

warn if using full email adress for no/open maintainer (to make jmpp happy)

File size: 11.1 KB
Line 
1# et:ts=4
2# portlint.tcl
3# $Id: portlint.tcl $
4
5package provide portlint 1.0
6package require portutil 1.0
7
8set org.macports.lint [target_new org.macports.lint lint_main]
9target_runtype ${org.macports.lint} always
10target_provides ${org.macports.lint} lint
11target_requires ${org.macports.lint} main
12target_prerun ${org.macports.lint} lint_start
13
14set_ui_prefix
15
16set lint_portsystem \
17        "1.0"
18
19set lint_platforms [list \
20        "macosx" \
21        "darwin" \
22        "freebsd" \
23        "openbsd" \
24        "netbsd" \
25        "linux" \
26        "sunos" \
27        ]
28
29set lint_categories [list \
30        "aqua" \
31        "archivers" \
32        "audio" \
33        "benchmarks" \
34        "cad" \
35        "comms" \
36        "cross" \
37        "databases" \
38        "devel" \
39        "editors" \
40        "emulators" \
41        "fuse" \
42        "games" \
43        "genealogy" \
44        "gnome" \
45        "gnustep" \
46        "graphics" \
47        "irc" \
48        "java" \
49        "kde" \
50        "lang" \
51        "mail" \
52        "math" \
53        "multimedia" \
54        "net" \
55        "news" \
56        "palm" \
57        "perl" \
58        "print" \
59        "python" \
60        "ruby" \
61        "science" \
62        "security" \
63        "shells" \
64        "sysutils" \
65        "tex" \
66        "textproc" \
67        "www" \
68        "x11" \
69        "xfce" \
70        "zope" \
71        ]
72
73set lint_required [list \
74        "name" \
75        "version" \
76        "description" \
77        "long_description" \
78        "categories" \
79        "maintainers" \
80        "platforms" \
81        "homepage" \
82        "master_sites" \
83        "checksums" \
84        ]
85
86set lint_optional [list \
87        "epoch" \
88        "revision" \
89        "worksrcdir" \
90        "distname" \
91        "use_automake" \
92        "use_autoconf" \
93        "use_configure" \
94        ]
95
96set lint_variants [list \
97        "universal" \
98        "docs" \
99        "aqua" \
100        "x11" \
101        ]
102
103
104proc seems_utf8 {str} {
105    set len [string length $str]
106    for {set i 0} {$i<$len} {incr i} {
107        set c [scan [string index $str $i] %c]
108        if {$c < 0x80} {
109            # ASCII
110            continue
111        } elseif {($c & 0xE0) == 0xC0} {
112            set n 1
113        } elseif {($c & 0xF0) == 0xE0} {
114            set n 2
115        } elseif {($c & 0xF8) == 0xF0} {
116            set n 3
117        } elseif {($c & 0xFC) == 0xF8} {
118            set n 4
119        } elseif {($c & 0xFE) == 0xFC} {
120            set n 5
121        } else {
122            return false
123        }
124        for {set j 0} {$j<$n} {incr j} {
125            incr i
126            if {$i == $len} {
127                return false
128            } elseif {([scan [string index $str $i] %c] & 0xC0) != 0x80} {
129                return false
130            }
131        }
132    }
133    return true
134}
135
136
137proc lint_start {args} {
138    global UI_PREFIX portname
139    ui_msg "$UI_PREFIX [format [msgcat::mc "Verifying Portfile for %s"] ${portname}]"
140}
141
142proc lint_main {args} {
143        global UI_PREFIX portname portpath portresourcepath
144        set portfile ${portpath}/Portfile
145        set groupdir ${portresourcepath}/group
146
147        set warnings 0
148        set errors 0
149
150    ###################################################################
151    ui_debug "$portfile"
152
153    set topline_number 1
154    set require_blank false
155    set require_after ""
156    set seen_portsystem false
157    set seen_portgroup false
158    set in_description false
159
160    set local_variants [list]
161
162    set f [open $portfile RDONLY]
163    # read binary (to check UTF-8)
164    fconfigure $f -encoding binary
165    set lineno 1
166    while {1} {
167        set line [gets $f]
168        if {[eof $f]} {
169            close $f
170            break
171        }
172        ui_debug "$lineno: $line"
173
174        if {![seems_utf8 $line]} {
175            ui_error "Line $lineno seems to contain an invalid UTF-8 sequence"
176            incr errors
177        }
178
179        if {[string equal "PortSystem" $require_after] && \
180            [string match "PortGroup*" $line]} {
181            set require_blank false
182        }
183
184        if {$require_blank && ($line != "")} {
185            ui_warn "Line $lineno should be a newline (after $require_after)"
186            incr warnings
187        }
188        set require_blank false
189
190        if {[string match "* " $line] || [string match "*\t" $line] &&
191            [string trim $line] != "" } {
192            # allow indented blank lines between blocks of code and such
193            ui_warn "Line $lineno has trailing whitespace before newline"
194            incr warnings
195        }
196
197        if {($lineno == $topline_number) && [string match "*-\*- Mode:*" $line]} {
198            ui_info "OK: Line $lineno has emacs/vim Mode"
199            incr topline_number
200        }
201        if {($lineno == $topline_number) && ![string match "*\$Id*" $line]} {
202            ui_warn "Line $lineno is missing RCS tag (\$Id)"
203            incr warnings
204        } elseif {($lineno == $topline_number)} {
205            ui_info "OK: Line $lineno has RCS tag (\$Id)"
206            set require_blank true
207            set require_after "RCS tag"
208        }
209
210        if {[string match "PortSystem*" $line]} {
211            if {$seen_portsystem} {
212                 ui_error "Line $lineno repeats PortSystem information"
213                 incr errors
214            }
215            regexp {PortSystem\s+([0-9.]+)} $line -> portsystem
216            if {![info exists portsystem]} {
217                 ui_error "Line $lineno has unrecognized PortSystem"
218                 incr errors
219            }
220            set seen_portsystem true
221            set require_blank true
222            set require_after "PortSystem"
223        }
224        if {[string match "PortGroup*" $line]} {
225            if {$seen_portgroup} {
226                 ui_error "Line $lineno repeats PortGroup information"
227                 incr errors
228            }
229            regexp {PortGroup\s+([a-z0-9]+)\s+([0-9.]+)} $line -> portgroup portgroupversion
230            if {![info exists portgroup]} {
231                 ui_error "Line $lineno has unrecognized PortGroup"
232                 incr errors
233            }
234            set seen_portgroup true
235            set require_blank true
236            set require_after "PortGroup"
237        }
238
239        # TODO: check for repeated variable definitions
240        # TODO: check the definition order of variables
241        # TODO: check length of description against max
242
243        if {[string match "long_description*" $line]} {
244            set in_description true
245        }
246        if {$in_description && ([string range $line end end] != "\\")} {
247            set in_description false
248            #set require_blank true
249            #set require_after "long_description"
250        } elseif {$in_description} {
251            set require_blank false
252        }
253
254        if {[string match "variant*" $line]} {
255            regexp {variant\s+(\w+)} $line -> variantname
256            if {[info exists variantname]} {
257                 lappend local_variants $variantname
258            }
259        }
260
261        ### TODO: more checks to Portfile syntax
262
263        incr lineno
264    }
265
266    ###################################################################
267
268    global os.platform os.arch os.version
269    global portversion portrevision portepoch
270    # hoping for "noarch" :
271    set portarch ${os.arch}
272    global description long_description platforms categories all_variants
273    global maintainers homepage master_sites checksums patchfiles
274   
275    global lint_portsystem lint_platforms lint_categories
276    global lint_required lint_optional lint_variants
277
278    if (!$seen_portsystem) {
279        ui_error "Didn't find PortSystem specification"
280        incr errors
281    }  elseif {$portsystem != $lint_portsystem} {
282        ui_error "Unknown PortSystem: $portsystem"
283        incr errors
284    } else {
285        ui_info "OK: Found PortSystem $portsystem"
286    }
287    if (!$seen_portgroup) {
288        # PortGroup is optional, so missing is OK
289    }  elseif {![file exists $groupdir/$portgroup-$portgroupversion.tcl]} {
290        ui_error "Unknown PortGroup: $portgroup-$portgroupversion"
291        incr errors
292    } else {
293        ui_info "OK: Found PortGroup $portgroup-$portgroupversion"
294    }
295
296    foreach req_var $lint_required {
297        if {$req_var == "name"} {
298            set var "portname"
299        } elseif {$req_var == "version"} {
300            set var "portversion"
301        } else {
302            set var $req_var
303        }
304       if {![info exists $var]} {
305            ui_error "Missing required variable: $req_var"
306            incr errors
307        } else {
308            ui_info "OK: Found required variable: $req_var"
309        }
310    }
311
312    foreach opt_var $lint_optional {
313       if {$opt_var == "epoch"} {
314            set var "portepoch"
315        } elseif {$opt_var == "revision"} {
316            set var "portrevision"
317        } else {
318            set var $opt_var
319       }
320       if {[info exists $var]} {
321            # TODO: check whether it was seen (or default)
322            ui_info "OK: Found optional variable: $opt_var"
323       }
324    }
325
326    if {[info exists platforms]} {
327        foreach platform $platforms {
328           if {[lsearch -exact $lint_platforms $platform] == -1} {
329                ui_error "Unknown platform: $platform"
330                incr errors
331            } else {
332                ui_info "OK: Found platform: $platform"
333            }
334        }
335    }
336
337    if {[info exists categories]} {
338        foreach category $categories {
339           if {[lsearch -exact $lint_categories $category] == -1} {
340                ui_error "Unknown category: $category"
341                incr errors
342            } else {
343                ui_info "OK: Found category: $category"
344            }
345        }
346    }
347
348    if {![string is integer -strict $portepoch]} {
349        ui_error "Port epoch is not numeric:  $portepoch"
350        incr errors
351    }
352    if {![string is integer -strict $portrevision]} {
353        ui_error "Port revision is not numeric: $portrevision"
354        incr errors
355    }
356
357    set variantnumber 1
358    foreach variant $all_variants {
359        set variantname [ditem_key $variant name] 
360        set variantdesc [lindex [ditem_key $variant description] 0]
361        if {![info exists variantname] || $variantname == ""} {
362            ui_error "Variant number $variantnumber does not have a name"
363            incr errors
364        } elseif {![info exists variantdesc] || $variantdesc == ""} {
365            ui_info "OK: Found variant: $variantname"
366            # don't warn about missing descriptions for global variants
367            if {[lsearch -exact $local_variants $variantname] != -1 &&
368                [lsearch -exact $lint_variants $variantname] == -1} {
369                ui_warn "Variant $variantname does not have a description"
370                incr warnings
371            }
372        } else {
373            ui_info "OK: Found variant $variantname: $variantdesc"
374        }
375        incr variantnumber
376    }
377
378    if {[string match "*darwinports@opendarwin.org*" $maintainers]} {
379        ui_warn "Using legacy email for no/open maintainer"
380        incr warnings
381    }
382
383    if {[string match "*nomaintainer@macports.org*" $maintainers] ||
384        [string match "*openmaintainer@macports.org*" $maintainers]} {
385        ui_warn "Using full email adress for no/open maintainer"
386        incr warnings
387    }
388
389    if {[info exists patchfiles]} {
390        foreach patchfile $patchfiles {
391            if {![string match "patch-*.diff" $patchfile]} {
392                ui_warn "Patchfile $patchfile does not follow the source patch naming policy \"patch-*.diff\""
393                incr warnings
394            }
395        }
396    }
397
398    ### TODO: more checks to Tcl variables/sections
399
400    ui_debug "Name: $portname"
401    ui_debug "Epoch: $portepoch"
402    ui_debug "Version: $portversion"
403    ui_debug "Revision: $portrevision"
404    ui_debug "Arch: $portarch"
405    ###################################################################
406
407        ui_msg "$UI_PREFIX [format [msgcat::mc "%d errors and %d warnings found."] $errors $warnings]"
408
409        return {$errors > 0}
410}
Note: See TracBrowser for help on using the repository browser.