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

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

allow those pesky modelines

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