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

Last change on this file since 42662 was 42662, checked in by raimue@…, 12 years ago

Merged revisions 34469,34852,34854-34855,34900,36952-36956,37507-37508,37511-37512,41040,41042-41046,41138-41139,41142-41143,41145,41151,41403,41458,41462-41463,42575,42626,42640-42641,42659 via svnmerge from
https://svn.macosforge.org/repository/macports/branches/variant-descs-14482/base

........

r34469 | raimue@… | 2008-02-26 07:08:09 +0100 (Tue, 26 Feb 2008) | 3 lines


port/port.tcl:
Reading from .config/variant_descriptions actually works

........

r34852 | raimue@… | 2008-03-09 02:45:22 +0100 (Sun, 09 Mar 2008) | 4 lines


macports1.0/macports.tcl:
New API: macports::getsourceconfigdir
Returns the path to .config for a porturl.

........

r34854 | raimue@… | 2008-03-09 03:11:27 +0100 (Sun, 09 Mar 2008) | 3 lines


port/port.tcl:
Use new API macports::getsourceconfigdir

........

r34855 | raimue@… | 2008-03-09 03:12:54 +0100 (Sun, 09 Mar 2008) | 3 lines


port/port.tcl:
Treat variant descriptions as strings to avoid problems with braces

........

r34900 | raimue@… | 2008-03-10 16:54:25 +0100 (Mon, 10 Mar 2008) | 3 lines


port/port.tcl:
Rename variable

........

r36952 | raimue@… | 2008-05-21 04:20:27 +0200 (Wed, 21 May 2008) | 3 lines


port/port.tcl:
Remove get_variant_desc, this will now be done in port1.0/portutil.tcl instead

........

r36953 | raimue@… | 2008-05-21 04:22:04 +0200 (Wed, 21 May 2008) | 3 lines


macports1.0/macports.tcl:
Give the worker access to variable porturl and proc getsourceconfigdir

........

r36954 | raimue@… | 2008-05-21 04:23:37 +0200 (Wed, 21 May 2008) | 3 lines


port1.0/tests:
Fix the portutil test after r36953

........

r36955 | raimue@… | 2008-05-21 05:01:11 +0200 (Wed, 21 May 2008) | 3 lines


macports1.0/macports.tcl:
Give worker access to getprotocol and getportdir as they are needed for getsourceconfigdir

........

r36956 | raimue@… | 2008-05-21 05:02:23 +0200 (Wed, 21 May 2008) | 3 lines


port1.0/portutil.tcl:
New proc variant_desc, reads global variant description file

........

r37507 | raimue@… | 2008-06-10 16:04:54 +0200 (Tue, 10 Jun 2008) | 4 lines


port1.0/portutil.tcl:
Don't warn about a missing description if it is set global,
but warn if the variant overrides the global description

........

r37508 | raimue@… | 2008-06-10 16:14:03 +0200 (Tue, 10 Jun 2008) | 3 lines


macports1.0/macports.tcl:
Use .resources instead of .config as it is a bit clearer, see #14553

........

r37511 | raimue@… | 2008-06-10 17:22:12 +0200 (Tue, 10 Jun 2008) | 5 lines


port1.0/portutil.tcl:
Switch back to this format:
name {description}
So this could be easily extended if ever needed.

........

r37512 | raimue@… | 2008-06-10 17:27:48 +0200 (Tue, 10 Jun 2008) | 3 lines


port1.0/portutil.tcl:
Add a warning if global variant description file could not be opened

........

r41040 | raimue@… | 2008-10-21 13:06:39 +0200 (Tue, 21 Oct 2008) | 4 lines


macports/macport.tcl:

  • New flag "default" for sources to indicate fallback for resources (group)
  • Add parameter to getsourceconfigdir to get path for a requested file

........

r41042 | raimue@… | 2008-10-21 13:11:44 +0200 (Tue, 21 Oct 2008) | 3 lines


macports1.0/macports.tcl:
Rename getsourceconfigdir to getportresourcepath

........

r41043 | raimue@… | 2008-10-21 13:15:16 +0200 (Tue, 21 Oct 2008) | 3 lines


port1.0/portutil.tcl:
Use getportresourcepath for the group files

........

r41044 | raimue@… | 2008-10-21 13:19:47 +0200 (Tue, 21 Oct 2008) | 3 lines


port1.0/portlint.tcl:
Use getresourcepath for group files

........

r41045 | raimue@… | 2008-10-21 13:20:36 +0200 (Tue, 21 Oct 2008) | 3 lines


port1.0/portmain.tcl:
Add a note that we should get rid of $portresourcepath in favor of [getportresourcepath]

........

r41046 | raimue@… | 2008-10-21 13:40:29 +0200 (Tue, 21 Oct 2008) | 3 lines


port1.0/portutil.tcl:
Missed one instance of getsourceconfigdir

........

r41138 | raimue@… | 2008-10-25 20:52:50 +0200 (Sat, 25 Oct 2008) | 3 lines


port1.0/portutil.tcl:
Use getportresourcepath for global variant descriptions

........

r41139 | raimue@… | 2008-10-25 21:23:15 +0200 (Sat, 25 Oct 2008) | 3 lines


port1.0/portmain.tcl:
Correct XXX tag

........

r41142 | raimue@… | 2008-10-25 23:11:30 +0200 (Sat, 25 Oct 2008) | 3 lines


port1.0/portfetch.tcl:
Use getportresourcepath

........

r41143 | raimue@… | 2008-10-25 23:12:04 +0200 (Sat, 25 Oct 2008) | 3 lines


port1.0/portdestroot.tcl:
Use getportresourcepath

........

r41145 | raimue@… | 2008-10-26 00:04:15 +0200 (Sun, 26 Oct 2008) | 3 lines


macports1.0/macports.tcl:
Fix a problem with URLs not using the file protocol

........

r41151 | raimue@… | 2008-10-26 03:09:54 +0100 (Sun, 26 Oct 2008) | 3 lines


macports1.0/macports.tcl:
Fix issues introduced in r41145, the file exists check was wrong

........

r41403 | raimue@… | 2008-11-01 22:59:21 +0100 (Sat, 01 Nov 2008) | 3 lines


port1.0/portutil.tcl:
Add a debug output which group files are used

........

r41458 | blb@… | 2008-11-03 22:58:28 +0100 (Mon, 03 Nov 2008) | 2 lines


Add [default] tag and description to sources.conf

........

r41462 | blb@… | 2008-11-04 02:12:28 +0100 (Tue, 04 Nov 2008) | 2 lines


No longer need to install resources with base

........

r41463 | blb@… | 2008-11-04 02:14:49 +0100 (Tue, 04 Nov 2008) | 4 lines


Move the install/ subdir (containing the mtree files) into .../share/macports
from the resources dir (the mtree contains a bit of install-time info, so it
shouldn't be with the resources stuff in the port tree)

........

r42575 | blb@… | 2008-11-25 01:53:05 +0100 (Tue, 25 Nov 2008) | 3 lines


Add script to handle upgrades through configure/make/make install and
the package, so [default] is added as appropriate to sources.conf

........

r42626 | raimue@… | 2008-11-27 02:21:15 +0100 (Thu, 27 Nov 2008) | 3 lines


package1.0/portpkg.tcl, package1.0/portmpkg.tcl:
Remove portresourcepath and use [getportresourcepath] instead

........

r42640 | raimue@… | 2008-11-27 11:49:32 +0100 (Thu, 27 Nov 2008) | 3 lines


package1.0/portrpm.tcl, package1.0/portsrpm.tcl:
Remove reference to portresurcepath which is not used at all

........

r42641 | raimue@… | 2008-11-27 11:52:12 +0100 (Thu, 27 Nov 2008) | 3 lines


port1.0/portmain.tcl:
Remove definition of portresourcepath as it is not used any more

........

r42659 | raimue@… | 2008-11-28 16:44:30 +0100 (Fri, 28 Nov 2008) | 3 lines


macports1.0/macports.tcl:
Rename portresourcepath from .resources to _resources

........

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.8 KB
Line 
1# et:ts=4
2# portlint.tcl
3# $Id: portlint.tcl 42662 2008-11-28 23:18:50Z raimue@macports.org $
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_state ${org.macports.lint} no
11target_provides ${org.macports.lint} lint
12target_requires ${org.macports.lint} main
13target_prerun ${org.macports.lint} lint_start
14
15set_ui_prefix
16
17set lint_portsystem \
18        "1.0"
19
20set lint_platforms [list \
21        "macosx" \
22        "darwin" \
23        "freebsd" \
24        "openbsd" \
25        "netbsd" \
26        "linux" \
27        "sunos" \
28        ]
29
30set lint_categories [list \
31        "aqua" \
32        "archivers" \
33        "audio" \
34        "benchmarks" \
35        "cad" \
36        "comms" \
37        "cross" \
38        "databases" \
39        "devel" \
40        "editors" \
41        "emulators" \
42        "erlang" \
43        "finance" \
44        "fuse" \
45        "games" \
46        "genealogy" \
47        "gis" \
48        "gnome" \
49        "gnustep" \
50        "graphics" \
51        "iphone" \
52        "irc" \
53        "java" \
54        "kde" \
55        "lang" \
56        "mail" \
57        "math" \
58        "multimedia" \
59        "net" \
60        "news" \
61        "office" \
62        "palm" \
63        "perl" \
64        "print" \
65        "python" \
66        "ruby" \
67        "science" \
68        "security" \
69        "shells" \
70        "sysutils" \
71        "tex" \
72        "textproc" \
73        "www" \
74        "x11" \
75        "xfce" \
76        "zope" \
77        ]
78
79set lint_required [list \
80        "name" \
81        "version" \
82        "description" \
83        "long_description" \
84        "categories" \
85        "maintainers" \
86        "platforms" \
87        "homepage" \
88        "master_sites" \
89        "checksums" \
90        ]
91
92set lint_optional [list \
93        "epoch" \
94        "revision" \
95        "worksrcdir" \
96        "distname" \
97        "use_automake" \
98        "use_autoconf" \
99        "use_configure" \
100        ]
101
102set lint_variants [list \
103        "universal" \
104        "docs" \
105        "aqua" \
106        "x11" \
107        ]
108
109
110proc seems_utf8 {str} {
111    set len [string length $str]
112    for {set i 0} {$i<$len} {incr i} {
113        set c [scan [string index $str $i] %c]
114        if {$c < 0x80} {
115            # ASCII
116            continue
117        } elseif {($c & 0xE0) == 0xC0} {
118            set n 1
119        } elseif {($c & 0xF0) == 0xE0} {
120            set n 2
121        } elseif {($c & 0xF8) == 0xF0} {
122            set n 3
123        } elseif {($c & 0xFC) == 0xF8} {
124            set n 4
125        } elseif {($c & 0xFE) == 0xFC} {
126            set n 5
127        } else {
128            return false
129        }
130        for {set j 0} {$j<$n} {incr j} {
131            incr i
132            if {$i == $len} {
133                return false
134            } elseif {([scan [string index $str $i] %c] & 0xC0) != 0x80} {
135                return false
136            }
137        }
138    }
139    return true
140}
141
142
143proc lint_start {args} {
144    global UI_PREFIX portname
145    ui_msg "$UI_PREFIX [format [msgcat::mc "Verifying Portfile for %s"] ${portname}]"
146}
147
148proc lint_main {args} {
149        global UI_PREFIX portname portpath porturl ports_lint_nitpick
150        set portfile ${portpath}/Portfile
151        set portdirs [split ${portpath} /]
152        set last [llength $portdirs]
153        incr last -1
154        set portdir [lindex $portdirs $last]
155        incr last -1
156        set portcatdir [lindex $portdirs $last]
157
158        set warnings 0
159        set errors 0
160
161    ###################################################################
162    ui_debug "$portfile"
163   
164    if {[info exists ports_lint_nitpick] && $ports_lint_nitpick eq "yes"} {
165        set nitpick true
166    } else {
167        set nitpick false
168    }
169
170    set topline_number 1
171    set require_blank false
172    set require_after ""
173    set seen_portsystem false
174    set seen_portgroup false
175    set in_description false
176
177    set local_variants [list]
178
179    set f [open $portfile RDONLY]
180    # read binary (to check UTF-8)
181    fconfigure $f -encoding binary
182    set lineno 1
183    while {1} {
184        set line [gets $f]
185        if {[eof $f]} {
186            if {$nitpick} {
187                seek $f -1 end
188                set last [read $f 1]
189                if {![string match "\n" $last]} {
190                    ui_warn "Line $lineno has missing newline (at end of file)"
191                    incr warnings
192                }
193            }
194            close $f
195            break
196        }
197        ui_debug "$lineno: $line"
198
199        if {![seems_utf8 $line]} {
200            ui_error "Line $lineno seems to contain an invalid UTF-8 sequence"
201            incr errors
202        }
203
204        if {[string equal "PortSystem" $require_after] && \
205            [string match "PortGroup*" $line]} {
206            set require_blank false
207        }
208
209        if {$nitpick && $require_blank && ($line != "")} {
210            ui_warn "Line $lineno should be a newline (after $require_after)"
211            incr warnings
212        }
213        set require_blank false
214
215        if {$nitpick && [regexp {\S[ \t]+$} $line]} {
216            # allow indented blank lines between blocks of code and such
217            ui_warn "Line $lineno has trailing whitespace before newline"
218            incr warnings
219        }
220
221        if {($lineno == $topline_number) && [string match "*-\*- *" $line]} {
222            ui_info "OK: Line $lineno has emacs/vim Mode"
223            incr topline_number
224        }
225        if {($lineno == $topline_number) && ![string match "*\$Id*\$" $line]} {
226            ui_warn "Line $lineno is missing RCS tag (\$Id\$)"
227            incr warnings
228        } elseif {($lineno == $topline_number)} {
229            ui_info "OK: Line $lineno has RCS tag (\$Id\$)"
230            set require_blank true
231            set require_after "RCS tag"
232        }
233
234        if {[string match "PortSystem*" $line]} {
235            if {$seen_portsystem} {
236                 ui_error "Line $lineno repeats PortSystem information"
237                 incr errors
238            }
239            regexp {PortSystem\s+([0-9.]+)} $line -> portsystem
240            if {![info exists portsystem]} {
241                 ui_error "Line $lineno has unrecognized PortSystem"
242                 incr errors
243            }
244            set seen_portsystem true
245            set require_blank true
246            set require_after "PortSystem"
247        }
248        if {[string match "PortGroup*" $line]} {
249            if {$seen_portgroup} {
250                 ui_error "Line $lineno repeats PortGroup information"
251                 incr errors
252            }
253            regexp {PortGroup\s+([a-z0-9]+)\s+([0-9.]+)} $line -> portgroup portgroupversion
254            if {![info exists portgroup]} {
255                 ui_error "Line $lineno has unrecognized PortGroup"
256                 incr errors
257            }
258            set seen_portgroup true
259            set require_blank true
260            set require_after "PortGroup"
261        }
262
263        # TODO: check for repeated variable definitions
264        # TODO: check the definition order of variables
265        # TODO: check length of description against max
266
267        if {[string match "long_description*" $line]} {
268            set in_description true
269        }
270        if {$in_description && ([string range $line end end] != "\\")} {
271            set in_description false
272            #set require_blank true
273            #set require_after "long_description"
274        } elseif {$in_description} {
275            set require_blank false
276        }
277
278        if {[string match "variant*" $line]} {
279            regexp {variant\s+(\w+)} $line -> variantname
280            if {[info exists variantname]} {
281                 lappend local_variants $variantname
282            }
283        }
284
285        if {[regexp {(^|\s)configure\s+\{\s*\}} $line]} {
286            ui_warn "Line $lineno should say \"use_configure no\" instead of declaring an empty configure phase"
287            incr warnings
288        }
289
290        ### TODO: more checks to Portfile syntax
291
292        incr lineno
293    }
294
295    ###################################################################
296
297    global os.platform os.arch os.version
298    global portversion portrevision portepoch
299    # hoping for "noarch" :
300    set portarch ${os.arch}
301    global description long_description platforms categories all_variants
302    global maintainers homepage master_sites checksums patchfiles
303    global depends_lib depends_build depends_run fetch.type
304   
305    global lint_portsystem lint_platforms lint_categories
306    global lint_required lint_optional lint_variants
307
308    if (!$seen_portsystem) {
309        ui_error "Didn't find PortSystem specification"
310        incr errors
311    }  elseif {$portsystem != $lint_portsystem} {
312        ui_error "Unknown PortSystem: $portsystem"
313        incr errors
314    } else {
315        ui_info "OK: Found PortSystem $portsystem"
316    }
317    if (!$seen_portgroup) {
318        # PortGroup is optional, so missing is OK
319    }  elseif {![file exists [getportresourcepath $porturl "port1.0/group/${portgroup}-${portgroupversion}.tcl"]]} {
320        ui_error "Unknown PortGroup: $portgroup-$portgroupversion"
321        incr errors
322    } else {
323        ui_info "OK: Found PortGroup $portgroup-$portgroupversion"
324    }
325
326    foreach req_var $lint_required {
327        if {$req_var == "name"} {
328            set var "portname"
329        } elseif {$req_var == "version"} {
330            set var "portversion"
331        } else {
332            set var $req_var
333        }
334
335       if {$var == "master_sites" && ${fetch.type} != "standard"} {
336             ui_info "OK: $var not required for fetch.type ${fetch.type}"
337             continue
338       }
339       
340       if {![info exists $var]} {
341            ui_error "Missing required variable: $req_var"
342            incr errors
343        } else {
344            ui_info "OK: Found required variable: $req_var"
345        }
346    }
347
348    foreach opt_var $lint_optional {
349       if {$opt_var == "epoch"} {
350            set var "portepoch"
351        } elseif {$opt_var == "revision"} {
352            set var "portrevision"
353        } else {
354            set var $opt_var
355       }
356       if {[info exists $var]} {
357            # TODO: check whether it was seen (or default)
358            ui_info "OK: Found optional variable: $opt_var"
359       }
360    }
361
362    if {[info exists platforms]} {
363        foreach platform $platforms {
364           if {[lsearch -exact $lint_platforms $platform] == -1} {
365                ui_error "Unknown platform: $platform"
366                incr errors
367            } else {
368                ui_info "OK: Found platform: $platform"
369            }
370        }
371    }
372
373    if {[info exists categories]} {
374        set category [lindex $categories 0]
375        if {[lsearch -exact $lint_categories $category] == -1} {
376            ui_error "Unknown category: $category"
377            incr errors
378        } else {
379            ui_info "OK: Found category: $category"
380        }
381        foreach secondary $categories {
382            if {[string match $secondary $category]} {
383                continue
384            }
385            ui_info "OK: Found category: $secondary"
386        }
387    }
388
389    if {![string is integer -strict $portepoch]} {
390        ui_error "Port epoch is not numeric:  $portepoch"
391        incr errors
392    }
393    if {![string is integer -strict $portrevision]} {
394        ui_error "Port revision is not numeric: $portrevision"
395        incr errors
396    }
397
398    set variantnumber 1
399    foreach variant $all_variants {
400        set variantname [ditem_key $variant name] 
401        set variantdesc [lindex [ditem_key $variant description] 0]
402        if {![info exists variantname] || $variantname == ""} {
403            ui_error "Variant number $variantnumber does not have a name"
404            incr errors
405        } else {
406            set name_ok true
407            set desc_ok true
408
409            if {![regexp {^[A-Za-z0-9_]+$} $variantname]} {
410                ui_error "Variant name $variantname is not valid; use \[A-Za-z0-9_\]+ only"
411                incr errors
412                set name_ok false
413            }
414
415            if {![info exists variantdesc] || $variantdesc == ""} {
416                # don't warn about missing descriptions for global variants
417                if {[lsearch -exact $local_variants $variantname] != -1 &&
418                    [lsearch -exact $lint_variants $variantname] == -1 && 
419                    [variant_desc $porturl $variantname] == ""} {
420                    ui_warn "Variant $variantname does not have a description"
421                    incr warnings
422                    set desc_ok false
423                } elseif {$variantdesc == ""} {
424                    set variantdesc "(pre-defined variant)"
425                }
426            } else {
427                if {[variant_desc $porturl $variantname] != ""} {
428                    ui_warn "Variant $variantname overrides global description"
429                    incr warnings
430                }
431            }
432
433            if {$name_ok} {
434                if {$desc_ok} {
435                    ui_info "OK: Found variant $variantname: $variantdesc"
436                } else {
437                    ui_info "OK: Found variant: $variantname"
438                }
439            }
440        }
441        incr variantnumber
442    }
443
444    set all_depends {}
445    if {[info exists depends_lib]} { eval "lappend all_depends $depends_lib" }
446    if {[info exists depends_build]} { eval "lappend all_depends $depends_build" }
447    if {[info exists depends_run]} { eval "lappend all_depends $depends_run" }
448    foreach depspec $all_depends {
449        set dep [lindex [split $depspec :] end]
450        if {[catch {set res [mport_search "^$dep\$"]} error]} {
451            global errorInfo
452            ui_debug "$errorInfo"
453            continue
454        }
455        if {$res == ""} {
456            ui_error "Unknown dependency: $dep"
457            incr errors
458        } else {
459            ui_info "OK: Found dependency: $dep"
460        }
461    }
462
463    if {[regexp "^(.+)nomaintainer(@macports.org)?(.+)$" $maintainers] } {
464        ui_error "Using nomaintainer together with other maintainer"
465        incr errors
466    }
467
468    if {[regexp "^openmaintainer(@macports.org)?$" $maintainers] } {
469        ui_error "Using openmaintainer without any other maintainer"
470        incr errors
471    }
472
473    if {[string match "*darwinports@opendarwin.org*" $maintainers]} {
474        ui_warn "Using legacy email address for no/open maintainer"
475        incr warnings
476    }
477
478    if {[string match "*nomaintainer@macports.org*" $maintainers] ||
479        [string match "*openmaintainer@macports.org*" $maintainers]} {
480        ui_warn "Using full email address for no/open maintainer"
481        incr warnings
482    }
483
484    # these checks are only valid for ports stored in the regular tree directories
485    if {$portcatdir != $category} {
486        ui_error "Portfile parent directory $portcatdir does not match primary category $category"
487        incr errors
488    } else {
489        ui_info "OK: Portfile parent directory matches primary category"
490    }
491    if {$portdir != $portname} {
492        ui_error "Portfile directory $portdir does not match port name $portname"
493        incr errors
494    } else {
495        ui_info "OK: Portfile directory matches port name"
496    }
497
498    if {$nitpick && [info exists patchfiles]} {
499        foreach patchfile $patchfiles {
500            if {![string match "patch-*.diff" $patchfile] && [file exists "$portpath/files/$patchfile"]} {
501                ui_warn "Patchfile $patchfile does not follow the source patch naming policy \"patch-*.diff\""
502                incr warnings
503            }
504        }
505    }
506
507    ### TODO: more checks to Tcl variables/sections
508
509    ui_debug "Name: $portname"
510    ui_debug "Epoch: $portepoch"
511    ui_debug "Version: $portversion"
512    ui_debug "Revision: $portrevision"
513    ui_debug "Arch: $portarch"
514    ###################################################################
515
516        ui_msg "$UI_PREFIX [format [msgcat::mc "%d errors and %d warnings found."] $errors $warnings]"
517
518        return {$errors > 0}
519}
Note: See TracBrowser for help on using the repository browser.