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

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

lint variants and their descriptions

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