* [PATCH 0/2] Add "guix lint". @ 2014-07-21 23:51 Cyril Roelandt 2014-07-21 23:51 ` [PATCH 1/2] scripts: add guix lint Cyril Roelandt ` (2 more replies) 0 siblings, 3 replies; 15+ messages in thread From: Cyril Roelandt @ 2014-07-21 23:51 UTC (permalink / raw) To: guix-devel Hello, The first patch adds the "guix lint" command, that can be used to run a few "checkers" on a given package, or on all defined packages. It can currently be used to: - warn users about input packages that should be native inputs (such as pkg-config); - find stylistic issues in patch names; - find stylistic issues in the synopses (trailing period, for instance). Those checkers are not always right, but I think they might be useful to people getting started with Guix packaging, as some of these issues have been raised multiple times during patch reviews. Other checkers may be added in the future, depending on our needs. There are currently no tests; I'm willing to write them ASAP but wanted to get some feedback first, especially because I'm not an experienced Guile programmer. Cyril. --- Cyril Roelandt (2): scripts: add guix lint gnu/packages: Remove trailing periods in some synopses. Makefile.am | 1 + gnu/packages/fontutils.scm | 4 +- gnu/packages/gnome.scm | 2 +- gnu/packages/lua.scm | 2 +- gnu/packages/pdf.scm | 2 +- gnu/packages/python.scm | 4 +- gnu/packages/sdl.scm | 2 +- guix/scripts/lint.scm | 188 +++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 197 insertions(+), 8 deletions(-) create mode 100644 guix/scripts/lint.scm -- 1.8.4.rc3 ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 1/2] scripts: add guix lint 2014-07-21 23:51 [PATCH 0/2] Add "guix lint" Cyril Roelandt @ 2014-07-21 23:51 ` Cyril Roelandt 2014-07-22 9:03 ` Ludovic Courtès 2014-07-21 23:51 ` [PATCH 2/2] gnu/packages: Remove trailing periods in some synopses Cyril Roelandt 2014-07-22 19:27 ` [PATCH 0/2] Add "guix lint" Andreas Enge 2 siblings, 1 reply; 15+ messages in thread From: Cyril Roelandt @ 2014-07-21 23:51 UTC (permalink / raw) To: guix-devel * guix/scripts/lint.scm: New file. Defines a 'lint' tool for Guix packages. * Makefile.am (MODULES): Add it. --- Makefile.am | 1 + guix/scripts/lint.scm | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 guix/scripts/lint.scm diff --git a/Makefile.am b/Makefile.am index 41e0e67..e6d2556 100644 --- a/Makefile.am +++ b/Makefile.am @@ -87,6 +87,7 @@ MODULES = \ guix/scripts/authenticate.scm \ guix/scripts/refresh.scm \ guix/scripts/system.scm \ + guix/scripts/lint.scm \ guix.scm \ $(GNU_SYSTEM_MODULES) diff --git a/guix/scripts/lint.scm b/guix/scripts/lint.scm new file mode 100644 index 0000000..f60718f --- /dev/null +++ b/guix/scripts/lint.scm @@ -0,0 +1,188 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2014 Cyril Roelandt <tipecaml@gmail.com> +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. + +(define-module (guix scripts lint) + #:use-module (guix base32) + #:use-module (guix packages) + #:use-module (guix records) + #:use-module (guix ui) + #:use-module (guix utils) + #:use-module (gnu packages) + #:use-module (ice-9 match) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-9) + #:use-module (srfi srfi-11) + #:use-module (srfi srfi-37) + #:export (guix-lint)) + +\f +;;; +;;; Command-line options. +;;; + +(define %default-options + ;; Alist of default option values. + `((format . , bytevector->nix-base32-string))) + +(define (show-help) + (display (_ "Usage: guix lint [OPTION]... [PACKAGE] +Run a set of checkers on the specified package; if none is specified, run the checkers on all packages.\n")) + (display (_ " + -h, --help display this help and exit")) + (display (_ " + -l, --list-checkers display the list of available lint checkers")) + (display (_ " + -V, --version display version information and exit")) + (newline) + (show-bug-report-information)) + +(define %options + ;; Specification of the command-line options. + ;; TODO: add some options: + ;; * --checkers=checker1,checker2...: only run the specified checkers + ;; * --certainty=[low,medium,high]: only run checkers that have at least this + ;; 'certainty'. + (list (option '(#\h "help") #f #f + (lambda args + (show-help) + (exit 0))) + (option '(#\l "list-checkers") #f #f + (lambda args + (list-checkers-and-exit))) + (option '(#\V "version") #f #f + (lambda args + (show-version-and-exit "guix lint"))))) + +\f +;;; +;;; Helpers +;;; +(define (emit-warning package s) + (format #t "~a: ~a~%" (package-full-name package) s)) + +\f +;;; +;;; Checkers +;;; +(define-record-type* <lint-checker> + lint-checker make-lint-checker + lint-checker? + ;; TODO: add a 'certainty' field that shows how confident we are in the + ;; checker. Then allow users to only run checkers that have a certain + ;; 'certainty' level. + (name lint-checker-name) + (description lint-checker-description) + (check lint-checker-check)) + +(define (list-checkers-and-exit) + (format #t "Available checkers:~%") + (for-each (lambda (checker) + (format #t "- ~a: ~a~%" + (lint-checker-name checker) + (lint-checker-description checker))) + %checkers) + (exit 0)) + +(define (check-inputs-should-be-native package) + (let ((inputs (package-inputs package))) + (if (member "pkg-config" (map car inputs)) + (emit-warning package "pkg-config should probably be a native input")))) + +(define (check-synopsis-style package) + (define (check-final-period synopsis) + (if (string=? (string-take-right synopsis 1) ".") + (emit-warning package + "No period allowed at the end of the synopsis."))) + + (define (check-start-article synopsis) + (if (or (string=? (string-take synopsis 2) "A ") + (string=? (string-take synopsis 3) "An ")) + (emit-warning package + "No article allowed at the beginning of the synopsis."))) + + (let ((synopsis (package-synopsis package))) + (if (string? synopsis) + (begin + (check-final-period synopsis) + (check-start-article synopsis))))) + +(define (check-patches package) + (let ((patches (and=> (package-source package) origin-patches)) + (name (package-name package)) + (full-name (package-full-name package))) + (if (and patches + (any (lambda (patch) + (let ((filename (basename patch))) + (not (or (eq? (string-contains filename name) 0) + (eq? (string-contains filename full-name) 0))))) + patches)) + (emit-warning package + "Filenames of patches should start by the package name")))) + +(define %checkers + (list + (make-lint-checker "inputs-should-be-native" + "Identify inputs that should be native inputs" + check-inputs-should-be-native) + (make-lint-checker "patch-filenames" + "Validate filenames of patches" + check-patches) + (make-lint-checker "synopsis" + "Validate package synopsis" + check-synopsis-style))) + +(define (run-checkers package) + (for-each (lambda (checker) + ((lint-checker-check checker) package)) + %checkers)) + +\f +;;; +;;; Entry Point +;;; + +(define (guix-lint . args) + (define (parse-options) + ;; Return the alist of option values. + (args-fold* args %options + (lambda (opt name arg result) + (leave (_ "~A: unrecognized option~%") name)) + (lambda (arg result) + (alist-cons 'argument arg result)) + %default-options)) + + (let* ((opts (parse-options)) + (args (filter-map (match-lambda + (('argument . value) + value) + (_ #f)) + (reverse opts))) + (fmt (assq-ref opts 'format))) + + (if (null? args) + (fold-packages (lambda (p r) (run-checkers p)) '()) + (for-each (lambda (name) + (let*-values (((name version) + (package-name->name+version name))) + (let ((packages (find-packages-by-name name version))) + (case (length packages) + ((0) (format #t "No such package")); XXX + ((1) (run-checkers (car packages))) + (else (format #t + "More than one package found, be more precise")))))); XXX + args)))) -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH 1/2] scripts: add guix lint 2014-07-21 23:51 ` [PATCH 1/2] scripts: add guix lint Cyril Roelandt @ 2014-07-22 9:03 ` Ludovic Courtès 2014-07-22 13:38 ` Ludovic Courtès ` (2 more replies) 0 siblings, 3 replies; 15+ messages in thread From: Ludovic Courtès @ 2014-07-22 9:03 UTC (permalink / raw) To: Cyril Roelandt; +Cc: guix-devel Cyril Roelandt <tipecaml@gmail.com> skribis: > * guix/scripts/lint.scm: New file. Defines a 'lint' tool for Guix packages. > * Makefile.am (MODULES): Add it. This is nice! I think this should be modular so that, when possible, checkers can also be invoked at macro expansion time. For instance, style checkers for synopses could run just when you build the file, without even having to invoke ‘guix lint’ (but it’s nice to have both possibilities.) Anyway, that’s a very good start, so the comment above is mostly food for thought. > +(define %default-options > + ;; Alist of default option values. > + `((format . , bytevector->nix-base32-string))) Should be just '(). > +(define (emit-warning package s) > + (format #t "~a: ~a~%" (package-full-name package) s)) There should be two indentation spaces, not one (the same problem shows up in other places.) Could you check that? Besides, please and use ‘warning’ from (guix ui). Also, it would be great to report location information, so that the editor can directly jump to the offending line. See how build-aux/sync-descriptions.scm does it. > +(define (list-checkers-and-exit) > + (format #t "Available checkers:~%") Should be (_ "Available checkers:~%"). > + (for-each (lambda (checker) > + (format #t "- ~a: ~a~%" > + (lint-checker-name checker) > + (lint-checker-description checker))) Likewise, (gettext (lint-checker-description checker)). This file also needs to be added to po/guix/POTFILES.in, and po/guix/Makevars needs a --keyword=description as well. > +(define (check-inputs-should-be-native package) > + (let ((inputs (package-inputs package))) Please add a docstring to this and other top-level procedures. > + (if (member "pkg-config" (map car inputs)) > + (emit-warning package "pkg-config should probably be a native input")))) Please avoid looking at the ‘car’ of inputs, since it’s just a label. Also, prefer ‘when’ to one-arm ‘if’. So that should look like: (match inputs (((labels packages . _) ...) (when (member "pkg-config" (map package-name packages)) (emit-warning ...)))) > +(define (check-synopsis-style package) > + (define (check-final-period synopsis) > + (if (string=? (string-take-right synopsis 1) ".") > + (emit-warning package > + "No period allowed at the end of the synopsis."))) The warning message should be lower-case, with no period. > + (emit-warning package > + "Filenames of patches should start by the package name")))) “file names”, “start with”. > +(define %checkers > + (list > + (make-lint-checker "inputs-should-be-native" > + "Identify inputs that should be native inputs" > + check-inputs-should-be-native) > + (make-lint-checker "patch-filenames" > + "Validate filenames of patches" > + check-patches) > + (make-lint-checker "synopsis" > + "Validate package synopsis" > + check-synopsis-style))) Instead of ‘make-lint-checker’, use (lint-checker (name ...) (description "foo") ...) This will allow xgettext to extract all the ‘description’ things, as mentioned above. > +(define (guix-lint . args) > + (define (parse-options) > + ;; Return the alist of option values. > + (args-fold* args %options > + (lambda (opt name arg result) > + (leave (_ "~A: unrecognized option~%") name)) > + (lambda (arg result) > + (alist-cons 'argument arg result)) > + %default-options)) > + > + (let* ((opts (parse-options)) > + (args (filter-map (match-lambda > + (('argument . value) > + value) > + (_ #f)) > + (reverse opts))) > + (fmt (assq-ref opts 'format))) ‘fmt’ is unused, can be removed. > + (if (null? args) > + (fold-packages (lambda (p r) (run-checkers p)) '()) > + (for-each (lambda (name) > + (let*-values (((name version) > + (package-name->name+version name))) > + (let ((packages (find-packages-by-name name version))) > + (case (length packages) > + ((0) (format #t "No such package")); XXX > + ((1) (run-checkers (car packages))) > + (else (format #t > + "More than one package found, be more precise")))))); XXX Please move ‘specification->package’ from (guix build scripts) to (guix ui), and then use it instead of the above. Thanks! Ludo’. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/2] scripts: add guix lint 2014-07-22 9:03 ` Ludovic Courtès @ 2014-07-22 13:38 ` Ludovic Courtès 2014-07-22 14:31 ` Eric Bavier 2014-08-25 1:52 ` [PATCH] " Cyril Roelandt 2 siblings, 0 replies; 15+ messages in thread From: Ludovic Courtès @ 2014-07-22 13:38 UTC (permalink / raw) To: Cyril Roelandt; +Cc: guix-devel Two things I forgot to ask for (you’ll hate me for that ;-)): 1. Could you write tests for the checkers, as tests/lint.scm? The tests would make sure that for a ‘package’ structure that exposes the problem being checked for, the checker does emit a warning on ‘guix-warning-port’, like: (define (call-with-warnings thunk) (let ((port (open-output-string))) (parameterize ((guix-warning-port port)) (thunk)) (get-output-string port))) (test-assert "synopsis warning emitted" (->bool (string-contains (call-with-warnings (lambda () (check-foo-bar pkg))) "synopsis has a problem"))) 2. Add an “Invoking guix lint” node in guix.texi, with links to there from “Defining Packages”. TIA. :-) Ludo’. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/2] scripts: add guix lint 2014-07-22 9:03 ` Ludovic Courtès 2014-07-22 13:38 ` Ludovic Courtès @ 2014-07-22 14:31 ` Eric Bavier 2014-07-22 15:26 ` Ludovic Courtès 2014-08-25 1:52 ` [PATCH] " Cyril Roelandt 2 siblings, 1 reply; 15+ messages in thread From: Eric Bavier @ 2014-07-22 14:31 UTC (permalink / raw) To: Cyril Roelandt; +Cc: guix-devel Ludovic Courtès writes: > Cyril Roelandt <tipecaml@gmail.com> skribis: > >> * guix/scripts/lint.scm: New file. Defines a 'lint' tool for Guix packages. >> * Makefile.am (MODULES): Add it. > > This is nice! > > I think this should be modular so that, when possible, checkers can also > be invoked at macro expansion time. For instance, style checkers for > synopses could run just when you build the file, without even having to > invoke ‘guix lint’ (but it’s nice to have both possibilities.) I like this too. Would it make sense to have 'guix lint' operate at the file/module level instead of at the package level? That would be consistent with other lint-type tools, as far as I know. It would also, I think, help with things like syntax errors that currently result in 'guix build' reporting something like "guix build: error: foo: unknown package". -- Eric Bavier ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/2] scripts: add guix lint 2014-07-22 14:31 ` Eric Bavier @ 2014-07-22 15:26 ` Ludovic Courtès 0 siblings, 0 replies; 15+ messages in thread From: Ludovic Courtès @ 2014-07-22 15:26 UTC (permalink / raw) To: Eric Bavier; +Cc: guix-devel Eric Bavier <ericbavier@gmail.com> skribis: > Would it make sense to have 'guix lint' operate at the file/module level > instead of at the package level? You really mean at the syntactic level, no? > That would be consistent with other lint-type tools, as far as I know. > It would also, I think, help with things like syntax errors that > currently result in 'guix build' reporting something like "guix build: > error: foo: unknown package". Normally you get more details about that when typing ‘make’ (or C-c C-k in Geiser.) Detecting that would amount to doing the job of the compiler and module system, basically. Ludo’. ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH] scripts: add guix lint 2014-07-22 9:03 ` Ludovic Courtès 2014-07-22 13:38 ` Ludovic Courtès 2014-07-22 14:31 ` Eric Bavier @ 2014-08-25 1:52 ` Cyril Roelandt 2014-08-25 22:44 ` Ludovic Courtès 2 siblings, 1 reply; 15+ messages in thread From: Cyril Roelandt @ 2014-08-25 1:52 UTC (permalink / raw) To: guix-devel * guix/scripts/lint.scm: New file. Defines a 'lint' tool for Guix packages. * tests/lint.scm: New file. * Makefile.am (MODULES, SCM_TESTS): Add them. * guix/scripts/build.scm (specification->package): Move from here... * guix/ui.scm: ... to here. * po/guix/Makevars: Update appropriately. * po/guix/POTFILES.in: Update appropriately. * doc/guix.texi: Document "guix lint". --- Makefile.am | 4 +- doc/guix.texi | 27 +++++++ guix/scripts/build.scm | 21 ----- guix/scripts/lint.scm | 213 +++++++++++++++++++++++++++++++++++++++++++++++++ guix/ui.scm | 23 ++++++ po/guix/Makevars | 3 +- po/guix/POTFILES.in | 1 + tests/lint.scm | 110 +++++++++++++++++++++++++ 8 files changed, 379 insertions(+), 23 deletions(-) create mode 100644 guix/scripts/lint.scm create mode 100644 tests/lint.scm diff --git a/Makefile.am b/Makefile.am index fff5958..371b85c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -89,6 +89,7 @@ MODULES = \ guix/scripts/authenticate.scm \ guix/scripts/refresh.scm \ guix/scripts/system.scm \ + guix/scripts/lint.scm \ guix.scm \ $(GNU_SYSTEM_MODULES) @@ -159,7 +160,8 @@ SCM_TESTS = \ tests/nar.scm \ tests/union.scm \ tests/profiles.scm \ - tests/syscalls.scm + tests/syscalls.scm \ + tests/lint.scm SH_TESTS = \ tests/guix-build.sh \ diff --git a/doc/guix.texi b/doc/guix.texi index 09ed392..9a43543 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -1459,6 +1459,10 @@ package may actually be built using the @code{guix build} command-line tool (@pxref{Invoking guix build}). @xref{Packaging Guidelines}, for more information on how to test package definitions. +In order to catch common errors and stylistic issues, the packages might +actually be checked by running the @command{guix lint} command +(@pxref{Invoking guix lint}). + Eventually, updating the package definition to a new upstream version can be partly automated by the @command{guix refresh} command (@pxref{Invoking guix refresh}). @@ -2326,6 +2330,7 @@ programming interface of Guix in a convenient way. * Invoking guix download:: Downloading a file and printing its hash. * Invoking guix hash:: Computing the cryptographic hash of a file. * Invoking guix refresh:: Updating package definitions. +* Invoking guix lint:: Finding errors in package definitions. @end menu @node Invoking guix build @@ -2703,6 +2708,28 @@ for in @code{$PATH}. @end table +@node Invoking guix lint +@section Invoking @command{guix lint} +The @command{guix lint} command runs a few checks on a given set of +packages in order to find common mistakes in their definitions. + +The general syntax is: + +@example +guix lint @var{options} @var{package}@dots{} +@end example + +If no package is given on the command line, then all packages are checked. +The @var{options} may be zero or more of the following: + +@table @code + +@item --list-checkers +@itemx -l +List and describe all the available checkers that will be run on packages +and exit. + +@end table @c ********************************************************************* @node GNU Distribution diff --git a/guix/scripts/build.scm b/guix/scripts/build.scm index 5e4647d..b2682a1 100644 --- a/guix/scripts/build.scm +++ b/guix/scripts/build.scm @@ -41,27 +41,6 @@ guix-build)) -(define (specification->package spec) - "Return a package matching SPEC. SPEC may be a package name, or a package -name followed by a hyphen and a version number. If the version number is not -present, return the preferred newest version." - (let-values (((name version) - (package-name->name+version spec))) - (match (find-best-packages-by-name name version) - ((p) ; one match - p) - ((p x ...) ; several matches - (warning (_ "ambiguous package specification `~a'~%") spec) - (warning (_ "choosing ~a from ~a~%") - (package-full-name p) - (location->string (package-location p))) - p) - (_ ; no matches - (if version - (leave (_ "~A: package not found for version ~a~%") - name version) - (leave (_ "~A: unknown package~%") name)))))) - (define (register-root store paths root) "Register ROOT as an indirect GC root for all of PATHS." (let* ((root (string-append (canonicalize-path (dirname root)) diff --git a/guix/scripts/lint.scm b/guix/scripts/lint.scm new file mode 100644 index 0000000..e3b0697 --- /dev/null +++ b/guix/scripts/lint.scm @@ -0,0 +1,213 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2014 Cyril Roelandt <tipecaml@gmail.com> +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. + +(define-module (guix scripts lint) + #:use-module (guix base32) + #:use-module (guix packages) + #:use-module (guix records) + #:use-module (guix ui) + #:use-module (guix utils) + #:use-module (gnu packages) + #:use-module (ice-9 match) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-9) + #:use-module (srfi srfi-11) + #:use-module (srfi srfi-37) + #:export (guix-lint + check-inputs-should-be-native + check-patches + check-synopsis-style)) + +\f +;;; +;;; Command-line options. +;;; + +(define %default-options + ;; Alist of default option values. + '()) + +(define (show-help) + (display (_ "Usage: guix lint [OPTION]... [PACKAGE]... +Run a set of checkers on the specified package; if none is specified, run the checkers on all packages.\n")) + (display (_ " + -h, --help display this help and exit")) + (display (_ " + -l, --list-checkers display the list of available lint checkers")) + (display (_ " + -V, --version display version information and exit")) + (newline) + (show-bug-report-information)) + +(define %options + ;; Specification of the command-line options. + ;; TODO: add some options: + ;; * --checkers=checker1,checker2...: only run the specified checkers + ;; * --certainty=[low,medium,high]: only run checkers that have at least this + ;; 'certainty'. + (list (option '(#\h "help") #f #f + (lambda args + (show-help) + (exit 0))) + (option '(#\l "list-checkers") #f #f + (lambda args + (list-checkers-and-exit))) + (option '(#\V "version") #f #f + (lambda args + (show-version-and-exit "guix lint"))))) + +\f +;;; +;;; Helpers +;;; +(define* (emit-warning package message #:optional field) + ;; Emit a warning about PACKAGE, printing the location of FIELD if it is + ;; given, the location of PACKAGE otherwise, the full name of PACKAGE and the + ;; provided MESSAGE. + (let ((loc (or (package-field-location package field) + (package-location package)))) + (warning (_ "~a: ~a: ~a~%") + (location->string loc) + (package-full-name package) + message))) + +\f +;;; +;;; Checkers +;;; +(define-record-type* <lint-checker> + lint-checker make-lint-checker + lint-checker? + ;; TODO: add a 'certainty' field that shows how confident we are in the + ;; checker. Then allow users to only run checkers that have a certain + ;; 'certainty' level. + (name lint-checker-name) + (description lint-checker-description) + (check lint-checker-check)) + +(define (list-checkers-and-exit) + ;; Print information about all available checkers and exit. + (format #t (_ "Available checkers:~%")) + (for-each (lambda (checker) + (format #t "- ~a: ~a~%" + (lint-checker-name checker) + (lint-checker-description checker))) + %checkers) + (exit 0)) + +(define (check-inputs-should-be-native package) + ;; Emit a warning if some inputs of PACKAGE are likely to belong to its + ;; native inputs. + (let ((inputs (package-inputs package))) + (match inputs + (((labels packages . _) ...) + (when (member "pkg-config" + (map package-name (filter package? packages))) + (emit-warning package + "pkg-config should probably be a native input" + 'inputs)))))) + + +(define (check-synopsis-style package) + ;; Emit a warning if stylistic issues are found in the synopsis of PACKAGE. + (define (check-final-period synopsis) + ;; Synopsis should not end with a period, except for some special cases. + (if (and (string=? (string-take-right synopsis 1) ".") + (not (string=? (string-take-right synopsis 4) "etc."))) + (emit-warning package + "no period allowed at the end of the synopsis" + 'synopsis))) + + (define (check-start-article synopsis) + (if (or (string=? (string-take synopsis 2) "A ") + (string=? (string-take synopsis 3) "An ")) + (emit-warning package + "no article allowed at the beginning of the synopsis" + 'synopsis))) + + (let ((synopsis (package-synopsis package))) + (if (string? synopsis) + (begin + (check-final-period synopsis) + (check-start-article synopsis))))) + +(define (check-patches package) + ;; Emit a warning if the patches requires by PACKAGE are badly named. + (let ((patches (and=> (package-source package) origin-patches)) + (name (package-name package)) + (full-name (package-full-name package))) + (if (and patches + (any (lambda (patch) + (let ((filename (basename patch))) + (not (or (eq? (string-contains filename name) 0) + (eq? (string-contains filename full-name) 0))))) + patches)) + (emit-warning package + "file names of patches should start with the package name" + 'patches)))) + +(define %checkers + (list + (lint-checker + (name "inputs-should-be-native") + (description "Identify inputs that should be native inputs") + (check check-inputs-should-be-native)) + (lint-checker + (name "patch-filenames") + (description "Validate filenames of patches") + (check check-patches)) + (lint-checker + (name "synopsis") + (description "Validate package synopsis") + (check check-synopsis-style)))) + +(define (run-checkers package) + ;; Run all the checkers on PACKAGE. + (for-each (lambda (checker) + ((lint-checker-check checker) package)) + %checkers)) + +\f +;;; +;;; Entry Point +;;; + +(define (guix-lint . args) + (define (parse-options) + ;; Return the alist of option values. + (args-fold* args %options + (lambda (opt name arg result) + (leave (_ "~A: unrecognized option~%") name)) + (lambda (arg result) + (alist-cons 'argument arg result)) + %default-options)) + + (let* ((opts (parse-options)) + (args (filter-map (match-lambda + (('argument . value) + value) + (_ #f)) + (reverse opts)))) + + + (if (null? args) + (fold-packages (lambda (p r) (run-checkers p)) '()) + (for-each + (lambda (spec) + (run-checkers spec)) + (map specification->package args))))) diff --git a/guix/ui.scm b/guix/ui.scm index f11c2e9..86bd8f7 100644 --- a/guix/ui.scm +++ b/guix/ui.scm @@ -37,6 +37,7 @@ #:use-module (ice-9 match) #:use-module (ice-9 format) #:use-module (ice-9 regex) + #:autoload (gnu packages) (find-best-packages-by-name) #:export (_ N_ P_ @@ -64,6 +65,7 @@ program-name guix-warning-port warning + specification->package guix-main)) ;;; Commentary: @@ -669,6 +671,27 @@ found." (define guix-warning-port (make-parameter (current-warning-port))) +(define (specification->package spec) + "Return a package matching SPEC. SPEC may be a package name, or a package +name followed by a hyphen and a version number. If the version number is not +present, return the preferred newest version." + (let-values (((name version) + (package-name->name+version spec))) + (match (find-best-packages-by-name name version) + ((p) ; one match + p) + ((p x ...) ; several matches + (warning (_ "ambiguous package specification `~a'~%") spec) + (warning (_ "choosing ~a from ~a~%") + (package-full-name p) + (location->string (package-location p))) + p) + (_ ; no matches + (if version + (leave (_ "~A: package not found for version ~a~%") + name version) + (leave (_ "~A: unknown package~%") name)))))) + (define (guix-main arg0 . args) (initialize-guix) (let () diff --git a/po/guix/Makevars b/po/guix/Makevars index 87bb438..f5b498c 100644 --- a/po/guix/Makevars +++ b/po/guix/Makevars @@ -10,7 +10,8 @@ top_builddir = ../.. XGETTEXT_OPTIONS = \ --language=Scheme --from-code=UTF-8 \ --keyword=_ --keyword=N_ \ - --keyword=message + --keyword=message \ + --keyword=description COPYRIGHT_HOLDER = Ludovic Courtès diff --git a/po/guix/POTFILES.in b/po/guix/POTFILES.in index bf2d313..5cc68ff 100644 --- a/po/guix/POTFILES.in +++ b/po/guix/POTFILES.in @@ -10,6 +10,7 @@ guix/scripts/pull.scm guix/scripts/substitute-binary.scm guix/scripts/authenticate.scm guix/scripts/system.scm +guix/scripts/lint.scm guix/gnu-maintenance.scm guix/ui.scm guix/http-client.scm diff --git a/tests/lint.scm b/tests/lint.scm new file mode 100644 index 0000000..f6dae47 --- /dev/null +++ b/tests/lint.scm @@ -0,0 +1,110 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2012, 2013 Cyril Roelandt <tipecaml@gmail.com> +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. + + +(define-module (test-packages) + #:use-module (guix build download) + #:use-module (guix build-system gnu) + #:use-module (guix packages) + #:use-module (guix scripts lint) + #:use-module (guix ui) + #:use-module (gnu packages) + #:use-module (gnu packages pkg-config) + #:use-module (srfi srfi-64)) + +;; Test the linter. + +\f +(test-begin "lint") + +(define-syntax-rule (dummy-package name* extra-fields ...) + (package extra-fields ... (name name*) (version "0") (source #f) + (build-system gnu-build-system) + (synopsis #f) (description #f) + (home-page #f) (license #f) )) + +(define (call-with-warnings thunk) + (let ((port (open-output-string))) + (parameterize ((guix-warning-port port)) + (thunk)) + (get-output-string port))) + +(test-assert "synopsis: ends with a period" + (->bool + (string-contains (call-with-warnings + (lambda () + (let ((pkg (dummy-package "x" + (synopsis "Bad synopsis.")))) + (check-synopsis-style pkg)))) + "no period allowed at the end of the synopsis"))) + +(test-assert "synopsis: ends with 'etc.'" + (->bool + (string-null? (call-with-warnings + (lambda () + (let ((pkg (dummy-package "x" + (synopsis "Foo, bar, etc.")))) + (check-synopsis-style pkg))))))) + +(test-assert "synopsis: starts with 'A'" + (->bool + (string-contains (call-with-warnings + (lambda () + (let ((pkg (dummy-package "x" + (synopsis "A bad synopŝis")))) + (check-synopsis-style pkg)))) + "no article allowed at the beginning of the synopsis"))) + +(test-assert "synopsis: starts with 'An'" + (->bool + (string-contains (call-with-warnings + (lambda () + (let ((pkg (dummy-package "x" + (synopsis "An awful synopsis")))) + (check-synopsis-style pkg)))) + "no article allowed at the beginning of the synopsis"))) + +(test-assert "inputs: pkg-config is probably a native input" + (->bool + (string-contains + (call-with-warnings + (lambda () + (let ((pkg (dummy-package "x" + (inputs `(("pkg-config" ,pkg-config)))))) + (check-inputs-should-be-native pkg)))) + "pkg-config should probably be a native input"))) + +(test-assert "patches: file names" + (->bool + (string-contains + (call-with-warnings + (lambda () + (let ((pkg (dummy-package "x" + (source + (origin + (method url-fetch) + (uri "someurl") + (sha256 "somesha") + (patches (list "/path/to/y.patch"))))))) + (check-patches pkg)))) + "file names of patches should start with the package name"))) + +(test-end "lint") + +\f +(exit (= (test-runner-fail-count (test-runner-current)) 0)) -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH] scripts: add guix lint 2014-08-25 1:52 ` [PATCH] " Cyril Roelandt @ 2014-08-25 22:44 ` Ludovic Courtès 2014-09-01 0:39 ` [PATCH 1/2] Move specification->package to gnu/packages.scm Cyril Roelandt 0 siblings, 1 reply; 15+ messages in thread From: Ludovic Courtès @ 2014-08-25 22:44 UTC (permalink / raw) To: Cyril Roelandt; +Cc: guix-devel Cyril Roelandt <tipecaml@gmail.com> skribis: > * guix/scripts/lint.scm: New file. Defines a 'lint' tool for Guix packages. > * tests/lint.scm: New file. > * Makefile.am (MODULES, SCM_TESTS): Add them. > * guix/scripts/build.scm (specification->package): Move from here... > * guix/ui.scm: ... to here. > * po/guix/Makevars: Update appropriately. > * po/guix/POTFILES.in: Update appropriately. > * doc/guix.texi: Document "guix lint". Cool! > --- a/doc/guix.texi > +++ b/doc/guix.texi > @@ -1459,6 +1459,10 @@ package may actually be built using the @code{guix build} command-line > tool (@pxref{Invoking guix build}). @xref{Packaging Guidelines}, for > more information on how to test package definitions. > > +In order to catch common errors and stylistic issues, the packages might > +actually be checked by running the @command{guix lint} command > +(@pxref{Invoking guix lint}). What about just making it like this: [...] @xref{Packaging Guidelines}, for more information on how to test package definitions, and @ref{Invoking guix lint}, for information on how to check a definition for style conformance. > Eventually, updating the package definition to a new upstream version > can be partly automated by the @command{guix refresh} command > (@pxref{Invoking guix refresh}). > @@ -2326,6 +2330,7 @@ programming interface of Guix in a convenient way. > * Invoking guix download:: Downloading a file and printing its hash. > * Invoking guix hash:: Computing the cryptographic hash of a file. > * Invoking guix refresh:: Updating package definitions. > +* Invoking guix lint:: Finding errors in package definitions. > @end menu > > @node Invoking guix build > @@ -2703,6 +2708,28 @@ for in @code{$PATH}. > > @end table > > +@node Invoking guix lint > +@section Invoking @command{guix lint} > +The @command{guix lint} command runs a few checks on a given set of > +packages in order to find common mistakes in their definitions. To make the target audience clearer, what about this: The @command{guix lint} is meant to help package developers avoid common errors and use a consistent style. It runs a series of checks on the given set of packages [...] > -(define (specification->package spec) > - "Return a package matching SPEC. SPEC may be a package name, or a package > -name followed by a hyphen and a version number. If the version number is not > -present, return the preferred newest version." > - (let-values (((name version) > - (package-name->name+version spec))) > - (match (find-best-packages-by-name name version) Apologies for giving incorrect advice in my previous review; could you move this procedure to (gnu packages) (and not (guix ui), as I wrongfully suggested)? Ideally in a separate commit. The reason is to avoid introducing a dependency from (guix ui) to (gnu packages). OK to commit with these minor issues fixed. Thanks! Ludo’. ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 1/2] Move specification->package to gnu/packages.scm. 2014-08-25 22:44 ` Ludovic Courtès @ 2014-09-01 0:39 ` Cyril Roelandt 2014-09-01 0:39 ` [PATCH 2/2] scripts: add guix lint Cyril Roelandt 2014-09-01 8:55 ` [PATCH 1/2] Move specification->package to gnu/packages.scm Ludovic Courtès 0 siblings, 2 replies; 15+ messages in thread From: Cyril Roelandt @ 2014-09-01 0:39 UTC (permalink / raw) To: guix-devel * guix/scripts/build.scm (specification->package): Move from here... * gnu/packages.scm: ... to here. --- gnu/packages.scm | 26 +++++++++++++++++++++++++- guix/scripts/build.scm | 23 +---------------------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/gnu/packages.scm b/gnu/packages.scm index 14ad755..26d87c6 100644 --- a/gnu/packages.scm +++ b/gnu/packages.scm @@ -28,6 +28,7 @@ #:use-module (ice-9 vlist) #:use-module (ice-9 match) #:use-module (srfi srfi-1) + #:use-module (srfi srfi-11) #:use-module (srfi srfi-26) #:use-module (srfi srfi-39) #:export (search-patch @@ -45,7 +46,9 @@ package-transitive-dependents package-covering-dependents - check-package-freshness)) + check-package-freshness + + specification->package)) ;;; Commentary: ;;; @@ -326,3 +329,24 @@ but ~a is available upstream~%") (case key ((getaddrinfo-error ftp-error) #f) (else (apply throw key args)))))) + +(define (specification->package spec) + "Return a package matching SPEC. SPEC may be a package name, or a package +name followed by a hyphen and a version number. If the version number is not +present, return the preferred newest version." + (let-values (((name version) + (package-name->name+version spec))) + (match (find-best-packages-by-name name version) + ((p) ; one match + p) + ((p x ...) ; several matches + (warning (_ "ambiguous package specification `~a'~%") spec) + (warning (_ "choosing ~a from ~a~%") + (package-full-name p) + (location->string (package-location p))) + p) + (_ ; no matches + (if version + (leave (_ "~A: package not found for version ~a~%") + name version) + (leave (_ "~A: unknown package~%") name)))))) diff --git a/guix/scripts/build.scm b/guix/scripts/build.scm index 5e4647d..09401e9 100644 --- a/guix/scripts/build.scm +++ b/guix/scripts/build.scm @@ -33,7 +33,7 @@ #:use-module (srfi srfi-26) #:use-module (srfi srfi-34) #:use-module (srfi srfi-37) - #:autoload (gnu packages) (find-best-packages-by-name) + #:autoload (gnu packages) (specification->package) #:autoload (guix download) (download-to-store) #:export (%standard-build-options set-build-options-from-command-line @@ -41,27 +41,6 @@ guix-build)) -(define (specification->package spec) - "Return a package matching SPEC. SPEC may be a package name, or a package -name followed by a hyphen and a version number. If the version number is not -present, return the preferred newest version." - (let-values (((name version) - (package-name->name+version spec))) - (match (find-best-packages-by-name name version) - ((p) ; one match - p) - ((p x ...) ; several matches - (warning (_ "ambiguous package specification `~a'~%") spec) - (warning (_ "choosing ~a from ~a~%") - (package-full-name p) - (location->string (package-location p))) - p) - (_ ; no matches - (if version - (leave (_ "~A: package not found for version ~a~%") - name version) - (leave (_ "~A: unknown package~%") name)))))) - (define (register-root store paths root) "Register ROOT as an indirect GC root for all of PATHS." (let* ((root (string-append (canonicalize-path (dirname root)) -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 2/2] scripts: add guix lint 2014-09-01 0:39 ` [PATCH 1/2] Move specification->package to gnu/packages.scm Cyril Roelandt @ 2014-09-01 0:39 ` Cyril Roelandt 2014-09-01 21:19 ` Ludovic Courtès 2014-09-01 8:55 ` [PATCH 1/2] Move specification->package to gnu/packages.scm Ludovic Courtès 1 sibling, 1 reply; 15+ messages in thread From: Cyril Roelandt @ 2014-09-01 0:39 UTC (permalink / raw) To: guix-devel * guix/scripts/lint.scm: New file. Defines a 'lint' tool for Guix packages. * tests/lint.scm: New file. * Makefile.am (MODULES, SCM_TESTS): Add them. * po/guix/Makevars: Update appropriately. * po/guix/POTFILES.in: Update appropriately. * doc/guix.texi: Document "guix lint". --- Makefile.am | 4 +- doc/guix.texi | 29 ++++++- guix/scripts/lint.scm | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++ po/guix/Makevars | 3 +- po/guix/POTFILES.in | 1 + tests/lint.scm | 110 ++++++++++++++++++++++++++ 6 files changed, 357 insertions(+), 3 deletions(-) create mode 100644 guix/scripts/lint.scm create mode 100644 tests/lint.scm diff --git a/Makefile.am b/Makefile.am index fff5958..371b85c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -89,6 +89,7 @@ MODULES = \ guix/scripts/authenticate.scm \ guix/scripts/refresh.scm \ guix/scripts/system.scm \ + guix/scripts/lint.scm \ guix.scm \ $(GNU_SYSTEM_MODULES) @@ -159,7 +160,8 @@ SCM_TESTS = \ tests/nar.scm \ tests/union.scm \ tests/profiles.scm \ - tests/syscalls.scm + tests/syscalls.scm \ + tests/lint.scm SH_TESTS = \ tests/guix-build.sh \ diff --git a/doc/guix.texi b/doc/guix.texi index e34a70f..748480a 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -1459,7 +1459,10 @@ definitions like the one above may be automatically converted from the Nixpkgs distribution using the @command{guix import} command.}, the package may actually be built using the @code{guix build} command-line tool (@pxref{Invoking guix build}). @xref{Packaging Guidelines}, for -more information on how to test package definitions. +more information on how to test package definitions, and +@ref{Invoking guix lint}, for information on how to check a definition +for style conformance. + Eventually, updating the package definition to a new upstream version can be partly automated by the @command{guix refresh} command @@ -2328,6 +2331,7 @@ programming interface of Guix in a convenient way. * Invoking guix download:: Downloading a file and printing its hash. * Invoking guix hash:: Computing the cryptographic hash of a file. * Invoking guix refresh:: Updating package definitions. +* Invoking guix lint:: Finding errors in package definitions. @end menu @node Invoking guix build @@ -2705,6 +2709,29 @@ for in @code{$PATH}. @end table +@node Invoking guix lint +@section Invoking @command{guix lint} +The @command{guix lint} is meant to help package developers avoid common +errors and use a consistent style. It runs a few checks on a given set of +packages in order to find common mistakes in their definitions. + +The general syntax is: + +@example +guix lint @var{options} @var{package}@dots{} +@end example + +If no package is given on the command line, then all packages are checked. +The @var{options} may be zero or more of the following: + +@table @code + +@item --list-checkers +@itemx -l +List and describe all the available checkers that will be run on packages +and exit. + +@end table @c ********************************************************************* @node GNU Distribution diff --git a/guix/scripts/lint.scm b/guix/scripts/lint.scm new file mode 100644 index 0000000..e3b0697 --- /dev/null +++ b/guix/scripts/lint.scm @@ -0,0 +1,213 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2014 Cyril Roelandt <tipecaml@gmail.com> +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. + +(define-module (guix scripts lint) + #:use-module (guix base32) + #:use-module (guix packages) + #:use-module (guix records) + #:use-module (guix ui) + #:use-module (guix utils) + #:use-module (gnu packages) + #:use-module (ice-9 match) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-9) + #:use-module (srfi srfi-11) + #:use-module (srfi srfi-37) + #:export (guix-lint + check-inputs-should-be-native + check-patches + check-synopsis-style)) + +\f +;;; +;;; Command-line options. +;;; + +(define %default-options + ;; Alist of default option values. + '()) + +(define (show-help) + (display (_ "Usage: guix lint [OPTION]... [PACKAGE]... +Run a set of checkers on the specified package; if none is specified, run the checkers on all packages.\n")) + (display (_ " + -h, --help display this help and exit")) + (display (_ " + -l, --list-checkers display the list of available lint checkers")) + (display (_ " + -V, --version display version information and exit")) + (newline) + (show-bug-report-information)) + +(define %options + ;; Specification of the command-line options. + ;; TODO: add some options: + ;; * --checkers=checker1,checker2...: only run the specified checkers + ;; * --certainty=[low,medium,high]: only run checkers that have at least this + ;; 'certainty'. + (list (option '(#\h "help") #f #f + (lambda args + (show-help) + (exit 0))) + (option '(#\l "list-checkers") #f #f + (lambda args + (list-checkers-and-exit))) + (option '(#\V "version") #f #f + (lambda args + (show-version-and-exit "guix lint"))))) + +\f +;;; +;;; Helpers +;;; +(define* (emit-warning package message #:optional field) + ;; Emit a warning about PACKAGE, printing the location of FIELD if it is + ;; given, the location of PACKAGE otherwise, the full name of PACKAGE and the + ;; provided MESSAGE. + (let ((loc (or (package-field-location package field) + (package-location package)))) + (warning (_ "~a: ~a: ~a~%") + (location->string loc) + (package-full-name package) + message))) + +\f +;;; +;;; Checkers +;;; +(define-record-type* <lint-checker> + lint-checker make-lint-checker + lint-checker? + ;; TODO: add a 'certainty' field that shows how confident we are in the + ;; checker. Then allow users to only run checkers that have a certain + ;; 'certainty' level. + (name lint-checker-name) + (description lint-checker-description) + (check lint-checker-check)) + +(define (list-checkers-and-exit) + ;; Print information about all available checkers and exit. + (format #t (_ "Available checkers:~%")) + (for-each (lambda (checker) + (format #t "- ~a: ~a~%" + (lint-checker-name checker) + (lint-checker-description checker))) + %checkers) + (exit 0)) + +(define (check-inputs-should-be-native package) + ;; Emit a warning if some inputs of PACKAGE are likely to belong to its + ;; native inputs. + (let ((inputs (package-inputs package))) + (match inputs + (((labels packages . _) ...) + (when (member "pkg-config" + (map package-name (filter package? packages))) + (emit-warning package + "pkg-config should probably be a native input" + 'inputs)))))) + + +(define (check-synopsis-style package) + ;; Emit a warning if stylistic issues are found in the synopsis of PACKAGE. + (define (check-final-period synopsis) + ;; Synopsis should not end with a period, except for some special cases. + (if (and (string=? (string-take-right synopsis 1) ".") + (not (string=? (string-take-right synopsis 4) "etc."))) + (emit-warning package + "no period allowed at the end of the synopsis" + 'synopsis))) + + (define (check-start-article synopsis) + (if (or (string=? (string-take synopsis 2) "A ") + (string=? (string-take synopsis 3) "An ")) + (emit-warning package + "no article allowed at the beginning of the synopsis" + 'synopsis))) + + (let ((synopsis (package-synopsis package))) + (if (string? synopsis) + (begin + (check-final-period synopsis) + (check-start-article synopsis))))) + +(define (check-patches package) + ;; Emit a warning if the patches requires by PACKAGE are badly named. + (let ((patches (and=> (package-source package) origin-patches)) + (name (package-name package)) + (full-name (package-full-name package))) + (if (and patches + (any (lambda (patch) + (let ((filename (basename patch))) + (not (or (eq? (string-contains filename name) 0) + (eq? (string-contains filename full-name) 0))))) + patches)) + (emit-warning package + "file names of patches should start with the package name" + 'patches)))) + +(define %checkers + (list + (lint-checker + (name "inputs-should-be-native") + (description "Identify inputs that should be native inputs") + (check check-inputs-should-be-native)) + (lint-checker + (name "patch-filenames") + (description "Validate filenames of patches") + (check check-patches)) + (lint-checker + (name "synopsis") + (description "Validate package synopsis") + (check check-synopsis-style)))) + +(define (run-checkers package) + ;; Run all the checkers on PACKAGE. + (for-each (lambda (checker) + ((lint-checker-check checker) package)) + %checkers)) + +\f +;;; +;;; Entry Point +;;; + +(define (guix-lint . args) + (define (parse-options) + ;; Return the alist of option values. + (args-fold* args %options + (lambda (opt name arg result) + (leave (_ "~A: unrecognized option~%") name)) + (lambda (arg result) + (alist-cons 'argument arg result)) + %default-options)) + + (let* ((opts (parse-options)) + (args (filter-map (match-lambda + (('argument . value) + value) + (_ #f)) + (reverse opts)))) + + + (if (null? args) + (fold-packages (lambda (p r) (run-checkers p)) '()) + (for-each + (lambda (spec) + (run-checkers spec)) + (map specification->package args))))) diff --git a/po/guix/Makevars b/po/guix/Makevars index 87bb438..f5b498c 100644 --- a/po/guix/Makevars +++ b/po/guix/Makevars @@ -10,7 +10,8 @@ top_builddir = ../.. XGETTEXT_OPTIONS = \ --language=Scheme --from-code=UTF-8 \ --keyword=_ --keyword=N_ \ - --keyword=message + --keyword=message \ + --keyword=description COPYRIGHT_HOLDER = Ludovic Courtès diff --git a/po/guix/POTFILES.in b/po/guix/POTFILES.in index bf2d313..5cc68ff 100644 --- a/po/guix/POTFILES.in +++ b/po/guix/POTFILES.in @@ -10,6 +10,7 @@ guix/scripts/pull.scm guix/scripts/substitute-binary.scm guix/scripts/authenticate.scm guix/scripts/system.scm +guix/scripts/lint.scm guix/gnu-maintenance.scm guix/ui.scm guix/http-client.scm diff --git a/tests/lint.scm b/tests/lint.scm new file mode 100644 index 0000000..f6dae47 --- /dev/null +++ b/tests/lint.scm @@ -0,0 +1,110 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2012, 2013 Cyril Roelandt <tipecaml@gmail.com> +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. + + +(define-module (test-packages) + #:use-module (guix build download) + #:use-module (guix build-system gnu) + #:use-module (guix packages) + #:use-module (guix scripts lint) + #:use-module (guix ui) + #:use-module (gnu packages) + #:use-module (gnu packages pkg-config) + #:use-module (srfi srfi-64)) + +;; Test the linter. + +\f +(test-begin "lint") + +(define-syntax-rule (dummy-package name* extra-fields ...) + (package extra-fields ... (name name*) (version "0") (source #f) + (build-system gnu-build-system) + (synopsis #f) (description #f) + (home-page #f) (license #f) )) + +(define (call-with-warnings thunk) + (let ((port (open-output-string))) + (parameterize ((guix-warning-port port)) + (thunk)) + (get-output-string port))) + +(test-assert "synopsis: ends with a period" + (->bool + (string-contains (call-with-warnings + (lambda () + (let ((pkg (dummy-package "x" + (synopsis "Bad synopsis.")))) + (check-synopsis-style pkg)))) + "no period allowed at the end of the synopsis"))) + +(test-assert "synopsis: ends with 'etc.'" + (->bool + (string-null? (call-with-warnings + (lambda () + (let ((pkg (dummy-package "x" + (synopsis "Foo, bar, etc.")))) + (check-synopsis-style pkg))))))) + +(test-assert "synopsis: starts with 'A'" + (->bool + (string-contains (call-with-warnings + (lambda () + (let ((pkg (dummy-package "x" + (synopsis "A bad synopŝis")))) + (check-synopsis-style pkg)))) + "no article allowed at the beginning of the synopsis"))) + +(test-assert "synopsis: starts with 'An'" + (->bool + (string-contains (call-with-warnings + (lambda () + (let ((pkg (dummy-package "x" + (synopsis "An awful synopsis")))) + (check-synopsis-style pkg)))) + "no article allowed at the beginning of the synopsis"))) + +(test-assert "inputs: pkg-config is probably a native input" + (->bool + (string-contains + (call-with-warnings + (lambda () + (let ((pkg (dummy-package "x" + (inputs `(("pkg-config" ,pkg-config)))))) + (check-inputs-should-be-native pkg)))) + "pkg-config should probably be a native input"))) + +(test-assert "patches: file names" + (->bool + (string-contains + (call-with-warnings + (lambda () + (let ((pkg (dummy-package "x" + (source + (origin + (method url-fetch) + (uri "someurl") + (sha256 "somesha") + (patches (list "/path/to/y.patch"))))))) + (check-patches pkg)))) + "file names of patches should start with the package name"))) + +(test-end "lint") + +\f +(exit (= (test-runner-fail-count (test-runner-current)) 0)) -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH 2/2] scripts: add guix lint 2014-09-01 0:39 ` [PATCH 2/2] scripts: add guix lint Cyril Roelandt @ 2014-09-01 21:19 ` Ludovic Courtès 0 siblings, 0 replies; 15+ messages in thread From: Ludovic Courtès @ 2014-09-01 21:19 UTC (permalink / raw) To: Cyril Roelandt; +Cc: guix-devel Cyril Roelandt <tipecaml@gmail.com> skribis: > * guix/scripts/lint.scm: New file. Defines a 'lint' tool for Guix packages. > * tests/lint.scm: New file. > * Makefile.am (MODULES, SCM_TESTS): Add them. > * po/guix/Makevars: Update appropriately. > * po/guix/POTFILES.in: Update appropriately. > * doc/guix.texi: Document "guix lint". Looks good, please push. Thank you! Ludo’. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/2] Move specification->package to gnu/packages.scm. 2014-09-01 0:39 ` [PATCH 1/2] Move specification->package to gnu/packages.scm Cyril Roelandt 2014-09-01 0:39 ` [PATCH 2/2] scripts: add guix lint Cyril Roelandt @ 2014-09-01 8:55 ` Ludovic Courtès 1 sibling, 0 replies; 15+ messages in thread From: Ludovic Courtès @ 2014-09-01 8:55 UTC (permalink / raw) To: Cyril Roelandt; +Cc: guix-devel Cyril Roelandt <tipecaml@gmail.com> skribis: > * guix/scripts/build.scm (specification->package): Move from here... > * gnu/packages.scm: ... to here. LGTM, please push. Thanks, Ludo’. ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 2/2] gnu/packages: Remove trailing periods in some synopses. 2014-07-21 23:51 [PATCH 0/2] Add "guix lint" Cyril Roelandt 2014-07-21 23:51 ` [PATCH 1/2] scripts: add guix lint Cyril Roelandt @ 2014-07-21 23:51 ` Cyril Roelandt 2014-07-22 19:27 ` [PATCH 0/2] Add "guix lint" Andreas Enge 2 siblings, 0 replies; 15+ messages in thread From: Cyril Roelandt @ 2014-07-21 23:51 UTC (permalink / raw) To: guix-devel * gnu/packages/fontutils.scm (fontconfig): remove trailing period in synopsis. * gnu/packages/gnome.scm (gtkglext): Ditto. * gnu/packages/lua.scm (lua): Ditto. * gnu/packages/pdf.scm (xpdf): Ditto. * gnu/packages/python.scm (python-pytz, python2-pyicu): Ditto. * gnu/packages/sdl.scm (libmikmod): Ditto. --- gnu/packages/fontutils.scm | 4 ++-- gnu/packages/gnome.scm | 2 +- gnu/packages/lua.scm | 2 +- gnu/packages/pdf.scm | 2 +- gnu/packages/python.scm | 4 ++-- gnu/packages/sdl.scm | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gnu/packages/fontutils.scm b/gnu/packages/fontutils.scm index d17843e..055f94b 100644 --- a/gnu/packages/fontutils.scm +++ b/gnu/packages/fontutils.scm @@ -87,7 +87,7 @@ anti-aliased glyph bitmap generation with 256 gray levels.") (string-append "--with-add-fonts=" (assoc-ref %build-inputs "gs-fonts") "/share/fonts")))) - (synopsis "Fontconfig, a library for configuring and customising font access.") + (synopsis "Fontconfig, a library for configuring and customising font access") (description "Fontconfig can discover new fonts when installed automatically; perform font name substitution, so that appropriate alternative fonts can @@ -118,7 +118,7 @@ high quality, anti-aliased and subpixel rendered text on a display.") `(#:tests? #f ; no test target #:make-flags '("without_doc"))) - (synopsis "T1lib, a library for generating bitmaps from type 1 fonts.") + (synopsis "T1lib, a library for generating bitmaps from type 1 fonts") (description "T1lib is a library for generating/rasterising bitmaps from Type 1 fonts. It is based on the code of the X11 rasteriser of the X11 project. diff --git a/gnu/packages/gnome.scm b/gnu/packages/gnome.scm index 893c3e8..043fd1c 100644 --- a/gnu/packages/gnome.scm +++ b/gnu/packages/gnome.scm @@ -483,7 +483,7 @@ the API") ("glib" ,glib "bin"))) (propagated-inputs `(("pangox-compat" ,pangox-compat))) (home-page "https://projects.gnome.org/gtkglext") - (synopsis "OpenGL extension to GTK+.") + (synopsis "OpenGL extension to GTK+") (description "GtkGLExt is an OpenGL extension to GTK+. It provides additional GDK objects which support OpenGL rendering in GTK+ and GtkWidget API add-ons to make GTK+ widgets OpenGL-capable.") diff --git a/gnu/packages/lua.scm b/gnu/packages/lua.scm index a85c120..84d32a0 100644 --- a/gnu/packages/lua.scm +++ b/gnu/packages/lua.scm @@ -57,7 +57,7 @@ "/share/man/man1"))))) (alist-delete 'configure %standard-phases))))) (home-page "http://www.lua.org/") - (synopsis "An embeddable scripting language.") + (synopsis "An embeddable scripting language") (description "Lua is a powerful, fast, lightweight, embeddable scripting language. Lua combines simple procedural syntax with powerful data description constructs diff --git a/gnu/packages/pdf.scm b/gnu/packages/pdf.scm index 73c5f06..bbdc2cb 100644 --- a/gnu/packages/pdf.scm +++ b/gnu/packages/pdf.scm @@ -121,7 +121,7 @@ (string-append gs-fonts "/share/fonts/type1/ghostscript")) (("#fontFile") "fontFile")))) %standard-phases))) - (synopsis "Viewer for pdf files based on the Motif toolkit.") + (synopsis "Viewer for pdf files based on the Motif toolkit") (description "Xpdf is a viewer for Portable Document Format (PDF) files") (license license:gpl3) ; or gpl2, but not gpl2+ diff --git a/gnu/packages/python.scm b/gnu/packages/python.scm index 641a5df..8a5ca87 100644 --- a/gnu/packages/python.scm +++ b/gnu/packages/python.scm @@ -256,7 +256,7 @@ data types.") (build-system python-build-system) (arguments `(#:tests? #f)) ; no test target (home-page "https://launchpad.net/pytz") - (synopsis "The Python timezone library.") + (synopsis "The Python timezone library") (description "This library allows accurate and cross platform timezone calculations using Python 2.4 or higher and provides access to the Olson timezone database.") @@ -715,7 +715,7 @@ Python 3.3+.") #:tests? #f)) ; no check target (home-page "http://pyicu.osafoundation.org/") (synopsis - "Python extension wrapping the ICU C++ API.") + "Python extension wrapping the ICU C++ API") (description "PyICU is a python extension wrapping the ICU C++ API.") (license x11))) diff --git a/gnu/packages/sdl.scm b/gnu/packages/sdl.scm index 110cf52..02e9c29 100644 --- a/gnu/packages/sdl.scm +++ b/gnu/packages/sdl.scm @@ -104,7 +104,7 @@ joystick, and graphics hardware.") (build-system gnu-build-system) (inputs `(("alsa-lib" ,alsa-lib) ("libx11" ,libx11))) - (synopsis "Library for module sound formats.") + (synopsis "Library for module sound formats") (description "MikMod is able to play a wide range of module formats, as well as digital sound files. It can take advantage of particular features of your -- 1.8.4.rc3 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH 0/2] Add "guix lint". 2014-07-21 23:51 [PATCH 0/2] Add "guix lint" Cyril Roelandt 2014-07-21 23:51 ` [PATCH 1/2] scripts: add guix lint Cyril Roelandt 2014-07-21 23:51 ` [PATCH 2/2] gnu/packages: Remove trailing periods in some synopses Cyril Roelandt @ 2014-07-22 19:27 ` Andreas Enge 2014-07-22 20:25 ` Ludovic Courtès 2 siblings, 1 reply; 15+ messages in thread From: Andreas Enge @ 2014-07-22 19:27 UTC (permalink / raw) To: Cyril Roelandt; +Cc: guix-devel This looks interesting, but similarly to some comment about "guix package --show", I am wondering about the profusion of commands. Maybe we should use guix devel --lint instead? It would make clear that this option is only of interest to developers, and pave the way for more developer related functionalities. Andreas ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 0/2] Add "guix lint". 2014-07-22 19:27 ` [PATCH 0/2] Add "guix lint" Andreas Enge @ 2014-07-22 20:25 ` Ludovic Courtès 0 siblings, 0 replies; 15+ messages in thread From: Ludovic Courtès @ 2014-07-22 20:25 UTC (permalink / raw) To: Andreas Enge; +Cc: guix-devel Andreas Enge <andreas@enge.fr> skribis: > This looks interesting, but similarly to some comment about "guix package > --show", I am wondering about the profusion of commands. I think --show may be useful to non-developers. > Maybe we should use > guix devel --lint > instead? It would make clear that this option is only of interest to > developers, and pave the way for more developer related functionalities. The problem is that things like ‘guix hash’, ‘guix refresh’, and ‘guix lint’ already all have their own set of options. So bundling them together in a single sub-command would be inconvenient, at best. However, I sympathize with the idea of distinguishing between developer and non-developer functionality. There are two ways I tried to make this apparent: by explicitly stating it in the manual, and by hiding some sub-commands from the output of ‘guix --help’. Perhaps we could do more of the latter? How well does that help? Thanks, Ludo’. ^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2014-09-01 21:20 UTC | newest] Thread overview: 15+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2014-07-21 23:51 [PATCH 0/2] Add "guix lint" Cyril Roelandt 2014-07-21 23:51 ` [PATCH 1/2] scripts: add guix lint Cyril Roelandt 2014-07-22 9:03 ` Ludovic Courtès 2014-07-22 13:38 ` Ludovic Courtès 2014-07-22 14:31 ` Eric Bavier 2014-07-22 15:26 ` Ludovic Courtès 2014-08-25 1:52 ` [PATCH] " Cyril Roelandt 2014-08-25 22:44 ` Ludovic Courtès 2014-09-01 0:39 ` [PATCH 1/2] Move specification->package to gnu/packages.scm Cyril Roelandt 2014-09-01 0:39 ` [PATCH 2/2] scripts: add guix lint Cyril Roelandt 2014-09-01 21:19 ` Ludovic Courtès 2014-09-01 8:55 ` [PATCH 1/2] Move specification->package to gnu/packages.scm Ludovic Courtès 2014-07-21 23:51 ` [PATCH 2/2] gnu/packages: Remove trailing periods in some synopses Cyril Roelandt 2014-07-22 19:27 ` [PATCH 0/2] Add "guix lint" Andreas Enge 2014-07-22 20:25 ` Ludovic Courtès
Code repositories for project(s) associated with this public inbox https://git.savannah.gnu.org/cgit/guix.git This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).