From e956ae16a4c676cd8787b7f246350426522f87cf Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Mon, 24 Jul 2017 17:35:26 +0200 Subject: import: cpan: Updater returns a list of URLs. * guix/import/cpan.scm (latest-release): Return a list in the 'urls' field of 'upstream-source'. --- guix/import/cpan.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'guix') diff --git a/guix/import/cpan.scm b/guix/import/cpan.scm index a41f918049..9ee69e5296 100644 --- a/guix/import/cpan.scm +++ b/guix/import/cpan.scm @@ -296,7 +296,7 @@ META." (upstream-source (package (package-name package)) (version version) - (urls url)))))) + (urls (list url))))))) (define %cpan-updater (upstream-updater -- cgit v1.2.3 From 585347d7aa3a2a23359b354a550f161d5102c517 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Tue, 25 Jul 2017 12:14:04 +0200 Subject: Add 'guix weather'. * guix/scripts/weather.scm: New file. * Makefile.am (MODULES): Add it. * doc/guix.texi (Substitutes, Invoking guix publish): Mention "guix weather". (Invoking guix weather): New node. Co-authored-by: Ricardo Wurmus --- Makefile.am | 1 + doc/guix.texi | 75 ++++++++++++++- guix/scripts/weather.scm | 234 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 309 insertions(+), 1 deletion(-) create mode 100644 guix/scripts/weather.scm (limited to 'guix') diff --git a/Makefile.am b/Makefile.am index 4d1512f8ce..5888bc0266 100644 --- a/Makefile.am +++ b/Makefile.am @@ -171,6 +171,7 @@ MODULES = \ guix/scripts/import/texlive.scm \ guix/scripts/environment.scm \ guix/scripts/publish.scm \ + guix/scripts/weather.scm \ guix/scripts/edit.scm \ guix/scripts/size.scm \ guix/scripts/graph.scm \ diff --git a/doc/guix.texi b/doc/guix.texi index dfa1e22fcc..932b118f7d 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -158,6 +158,7 @@ Utilities * Invoking guix challenge:: Challenging substitute servers. * Invoking guix copy:: Copying to and from a remote store. * Invoking guix container:: Process isolation. +* Invoking guix weather:: Assessing substitute availability. Invoking @command{guix build} @@ -2201,6 +2202,9 @@ authenticates substitute information itself, as explained above, which is what we care about (whereas X.509 certificates are about authenticating bindings between domain names and public keys.) +You can get statistics on the substitutes provided by a server using the +@command{guix weather} command (@pxref{Invoking guix weather}). + The substitute mechanism can be disabled globally by running @code{guix-daemon} with @code{--no-substitutes} (@pxref{Invoking guix-daemon}). It can also be disabled temporarily by passing the @@ -4933,6 +4937,7 @@ the Scheme programming interface of Guix in a convenient way. * Invoking guix challenge:: Challenging substitute servers. * Invoking guix copy:: Copying to and from a remote store. * Invoking guix container:: Process isolation. +* Invoking guix weather:: Assessing substitute availability. @end menu @node Invoking guix build @@ -6869,7 +6874,8 @@ serves them. This ``on-the-fly'' mode is convenient in that it requires no setup and is immediately available. However, when serving lots of clients, we recommend using the @option{--cache} option, which enables caching of the archives before they are sent to clients---see below for -details. +details. The @command{guix weather} command provides a handy way to +check what a server provides (@pxref{Invoking guix weather}). As a bonus, @command{guix publish} also serves as a content-addressed mirror for source files referenced in @code{origin} records @@ -7269,6 +7275,73 @@ must be PID 1 of the container or one of its child processes. @end table +@node Invoking guix weather +@section Invoking @command{guix weather} + +Occasionally you're grumpy because substitutes are lacking and you end +up building packages by yourself (@pxref{Substitutes}). The +@command{guix weather} command reports on substitute availability on the +specified servers so you can have an idea of whether you'll be grumpy +today. It can sometimes be useful info as a user, but it is primarily +useful to people running @command{guix publish} (@pxref{Invoking guix +publish}). + +@cindex statistics, for substitutes +@cindex availability of substitutes +@cindex substitute availability +@cindex weather, substitute availability +Here's a sample run: + +@example +$ guix weather --substitute-urls=https://guix.example.org +computing 5,872 package derivations for x86_64-linux... +looking for 6,128 store items on https://guix.example.org.. +updating list of substitutes from 'https://guix.example.org'... 100.0% +https://guix.example.org + 43.4% substitutes available (2,658 out of 6,128) + 7,032.5 MiB of nars (compressed) + 19,824.2 MiB on disk (uncompressed) + 0.030 seconds per request (182.9 seconds in total) + 33.5 requests per second +@end example + +As you can see, it reports the fraction of all the packages for which +substitutes are available on the server---regardless of whether +substitutes are enabled, and regardless of whether this server's signing +key is authorized. It also reports the size of the compressed archives +(``nars'') provided by the server, the size the corresponding store +items occupy in the store (assuming deduplication is turned off), and +the server's throughput. + +To achieve that, @command{guix weather} queries over HTTP(S) meta-data +(@dfn{narinfos}) for all the relevant store items. Like @command{guix +challenge}, it ignores signatures on those substitutes, which is +innocuous since the command only gathers statistics and cannot install +those substitutes. + +Among other things, it is possible to query specific system types and +specific package sets. The available options are listed below. + +@table @code +@item --substitute-urls=@var{urls} +@var{urls} is the space-separated list of substitute server URLs to +query. When this option is omitted, the default set of substitute +servers is queried. + +@item --system=@var{system} +@itemx -s @var{system} +Query substitutes for @var{system}---e.g., @code{aarch64-linux}. This +option can be repeated, in which case @command{guix weather} will query +substitutes for several system types. + +@item --manifest=@var{file} +Instead of querying substitutes for all the packages, only ask for those +specified in @var{file}. @var{file} must contain a @dfn{manifest}, as +with the @code{-m} option of @command{guix package} (@pxref{Invoking +guix package}). +@end table + + @c ********************************************************************* @node GNU Distribution @chapter GNU Distribution diff --git a/guix/scripts/weather.scm b/guix/scripts/weather.scm new file mode 100644 index 0000000000..9cbeedd288 --- /dev/null +++ b/guix/scripts/weather.scm @@ -0,0 +1,234 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2017 Ludovic Courtès +;;; Copyright © 2017 Ricardo Wurmus +;;; +;;; 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 . + +(define-module (guix scripts weather) + #:use-module (guix ui) + #:use-module (guix scripts) + #:use-module (guix packages) + #:use-module (guix profiles) + #:use-module (guix derivations) + #:use-module (guix monads) + #:use-module (guix store) + #:use-module (guix grafts) + #:use-module (guix build syscalls) + #:use-module (guix scripts substitute) + #:use-module (gnu packages) + #:use-module (web uri) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-19) + #:use-module (srfi srfi-26) + #:use-module (srfi srfi-37) + #:use-module (ice-9 match) + #:use-module (ice-9 format) + #:export (guix-weather)) + +(define (all-packages) + "Return the list of public packages we are going to query." + (fold-packages (lambda (package result) + (match (package-replacement package) + ((? package? replacement) + (cons* replacement package result)) + (#f + (cons package result)))) + '())) + +(define* (package-outputs packages + #:optional (system (%current-system))) + "Return the list of outputs of all of PACKAGES for the given SYSTEM." + (let ((packages (filter (cut supported-package? <> system) packages))) + + (define update-progress! + (let ((total (length packages)) + (done 0) + (width (max 10 (- (terminal-columns) 10)))) + (lambda () + (set! done (+ 1 done)) + (let* ((ratio (/ done total 1.)) + (done (inexact->exact (round (* width ratio)))) + (left (- width done))) + (format (current-error-port) "~5,1f% [~a~a]\r" + (* ratio 100.) + (make-string done #\#) + (make-string left #\space)) + (when (>= done total) + (newline (current-error-port))) + (force-output (current-error-port)))))) + + (format (current-error-port) + (G_ "computing ~h package derivations for ~a...~%") + (length packages) system) + + (foldm %store-monad + (lambda (package result) + (mlet %store-monad ((drv (package->derivation package system + #:graft? #f))) + (update-progress!) + (match (derivation->output-paths drv) + (((names . items) ...) + (return (append items result)))))) + '() + packages))) + +(cond-expand + (guile-2.2 + ;; Guile 2.2.2 has a bug whereby 'time-monotonic' objects have seconds and + ;; nanoseconds swapped (fixed in Guile commit 886ac3e). Work around it. + (define time-monotonic time-tai)) + (else #t)) + +(define (call-with-time thunk kont) + "Call THUNK and pass KONT the elapsed time followed by THUNK's return +values." + (let* ((start (current-time time-monotonic)) + (result (call-with-values thunk list)) + (end (current-time time-monotonic))) + (apply kont (time-difference end start) result))) + +(define-syntax-rule (let/time ((time result exp)) body ...) + (call-with-time (lambda () exp) (lambda (time result) body ...))) + +(define (report-server-coverage server items) + "Report the subset of ITEMS available as substitutes on SERVER." + (define MiB (* (expt 2 20) 1.)) + + (format #t (G_ "looking for ~h store items on ~a...~%") + (length items) server) + + (let/time ((time narinfos (lookup-narinfos server items))) + (format #t "~a~%" server) + (let ((obtained (length narinfos)) + (requested (length items)) + (sizes (filter-map narinfo-file-size narinfos)) + (time (+ (time-second time) + (/ (time-nanosecond time) 1e9)))) + (format #t (G_ " ~2,1f% substitutes available (~h out of ~h)~%") + (* 100. (/ obtained requested 1.)) + obtained requested) + (let ((total (/ (reduce + 0 sizes) MiB))) + (match (length sizes) + ((? zero?) + (format #t (G_ " unknown substitute sizes~%"))) + (len + (if (= len obtained) + (format #t (G_ " ~,1h MiB of nars (compressed)~%") total) + (format #t (G_ " at least ~,1h MiB of nars (compressed)~%") + total))))) + (format #t (G_ " ~,1h MiB on disk (uncompressed)~%") + (/ (reduce + 0 (map narinfo-size narinfos)) MiB)) + (format #t (G_ " ~,3h seconds per request (~,1h seconds in total)~%") + (/ time requested 1.) time) + (format #t (G_ " ~,1h requests per second~%") + (/ requested time 1.))))) + + +;;; +;;; Command-line options. +;;; + +(define (show-help) + (display (G_ "Usage: guix weather [OPTIONS] +Report the availability of substitutes.\n")) + (display (G_ " + --substitute-urls=URLS + check for available substitutes at URLS")) + (display (G_ " + --manifest=MANIFEST + look up substitutes for packages specified in MANIFEST")) + (display (G_ " + -s, --system=SYSTEM consider substitutes for SYSTEM--e.g., \"i686-linux\"")) + (newline) + (display (G_ " + -h, --help display this help and exit")) + (display (G_ " + -V, --version display version information and exit")) + (newline) + (show-bug-report-information)) + +(define %options + (list (option '(#\h "help") #f #f + (lambda args + (show-help) + (exit 0))) + (option '(#\V "version") #f #f + (lambda args + (show-version-and-exit "guix challenge"))) + + (option '("substitute-urls") #t #f + (lambda (opt name arg result . rest) + (let ((urls (string-tokenize arg))) + (for-each (lambda (url) + (unless (string->uri url) + (leave (G_ "~a: invalid URL~%") url))) + urls) + (apply values + (alist-cons 'substitute-urls urls + (alist-delete 'substitute-urls result)) + rest)))) + (option '(#\m "manifest") #t #f + (lambda (opt name arg result) + (alist-cons 'manifest arg result))) + (option '(#\s "system") #t #f + (lambda (opt name arg result) + (alist-cons 'system arg result))))) + +(define %default-options + `((substitute-urls . ,%default-substitute-urls))) + +(define (load-manifest file) + "Load the manifest from FILE and return the list of packages it refers to." + (let* ((user-module (make-user-module '((guix profiles) (gnu)))) + (manifest (load* file user-module))) + (map manifest-entry-item + (manifest-transitive-entries manifest)))) + + +;;; +;;; Entry point. +;;; + +(define (guix-weather . args) + (with-error-handling + (let* ((opts (parse-command-line args %options + (list %default-options))) + (urls (assoc-ref opts 'substitute-urls)) + (systems (match (filter-map (match-lambda + (('system . system) system) + (_ #f)) + opts) + (() (list (%current-system))) + (systems systems))) + (packages (let ((file (assoc-ref opts 'manifest))) + (if file + (load-manifest file) + (all-packages)))) + (items (with-store store + (parameterize ((%graft? #f)) + (concatenate + (run-with-store store + (mapm %store-monad + (lambda (system) + (package-outputs packages system)) + systems))))))) + (for-each (lambda (server) + (report-server-coverage server items)) + urls)))) + +;;; Local Variables: +;;; eval: (put 'let/time 'scheme-indent-function 1) +;;; End: -- cgit v1.2.3 From 236cae0628fd02002ec2c5e0405880908f244b56 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Tue, 25 Jul 2017 21:37:06 +0200 Subject: offload: Disconnect sessions created by 'machine-load'. This fixes a memory leak that can be seen by running: (map (lambda _ (machine-load m)) (iota 1000)) * guix/scripts/offload.scm (machine-load): Add call to 'disconnect!'. --- guix/scripts/offload.scm | 1 + 1 file changed, 1 insertion(+) (limited to 'guix') diff --git a/guix/scripts/offload.scm b/guix/scripts/offload.scm index 566d117b02..868f54d1c6 100644 --- a/guix/scripts/offload.scm +++ b/guix/scripts/offload.scm @@ -400,6 +400,7 @@ allowed on MACHINE. Return +∞ if MACHINE is unreachable." "cat" "/proc/loadavg")) (line (read-line pipe))) (close-port pipe) + (disconnect! session) (if (eof-object? line) +inf.0 ;MACHINE does not respond, so assume it is infinitely loaded -- cgit v1.2.3 From 84620dd0c4f8f96cfdafb9a3ce8cce5d36a52b03 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Tue, 25 Jul 2017 21:55:20 +0200 Subject: offload: Fix potential file descriptor and memory leak. The '%slots' list could grow indefinitely; in practice though, guix-daemon is likely to restart 'guix offload' often enough. * guix/scripts/offload.scm (%slots): Remove. (choose-build-machine): Don't 'set!' %SLOTS. Return the acquired slot as a second value. (process-request): Adjust accordingly. Release the returned slot after 'transfer-and-offload'. --- guix/scripts/offload.scm | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) (limited to 'guix') diff --git a/guix/scripts/offload.scm b/guix/scripts/offload.scm index 868f54d1c6..d3cb64d604 100644 --- a/guix/scripts/offload.scm +++ b/guix/scripts/offload.scm @@ -428,13 +428,9 @@ allowed on MACHINE. Return +∞ if MACHINE is unreachable." "Return the name of the file used as a lock when choosing a build machine." (string-append %state-directory "/offload/machine-choice.lock")) - -(define %slots - ;; List of acquired build slots (open ports). - '()) - (define (choose-build-machine machines) - "Return the best machine among MACHINES, or #f." + "Return two values: the best machine among MACHINES and its build +slot (which must later be released with 'release-build-slot'), or #f and #f." ;; Proceed like this: ;; 1. Acquire the global machine-choice lock. @@ -481,14 +477,15 @@ allowed on MACHINE. Return +∞ if MACHINE is unreachable." ;; Release slots from the uninteresting machines. (for-each release-build-slot slots) - ;; Prevent SLOT from being GC'd. - (set! %slots (cons slot %slots)) - best)) + ;; The caller must keep SLOT to protect it from GC and to + ;; eventually release it. + (values best slot))) (begin ;; BEST is overloaded, so try the next one. (release-build-slot slot) (loop others)))) - (() #f))))) + (() + (values #f #f)))))) (define* (process-request wants-local? system drv features #:key @@ -506,19 +503,25 @@ allowed on MACHINE. Return +∞ if MACHINE is unreachable." ;; We'll never be able to match REQS. (display "# decline\n")) ((x ...) - (let ((machine (choose-build-machine candidates))) + (let-values (((machine slot) + (choose-build-machine candidates))) (if machine - (begin - ;; Offload DRV to MACHINE. - (display "# accept\n") - (let ((inputs (string-tokenize (read-line))) - (outputs (string-tokenize (read-line)))) - (transfer-and-offload drv machine - #:inputs inputs - #:outputs outputs - #:max-silent-time max-silent-time - #:build-timeout build-timeout - #:print-build-trace? print-build-trace?))) + (dynamic-wind + (const #f) + (lambda () + ;; Offload DRV to MACHINE. + (display "# accept\n") + (let ((inputs (string-tokenize (read-line))) + (outputs (string-tokenize (read-line)))) + (transfer-and-offload drv machine + #:inputs inputs + #:outputs outputs + #:max-silent-time max-silent-time + #:build-timeout build-timeout + #:print-build-trace? + print-build-trace?))) + (lambda () + (release-build-slot slot))) ;; Not now, all the machines are busy. (display "# postpone\n"))))))) -- cgit v1.2.3 From f135b4ae8397d2c501150d3ead3e0603e770ce3f Mon Sep 17 00:00:00 2001 From: Christopher Baines Date: Mon, 19 Jun 2017 08:14:43 +0100 Subject: git-download: Speed up 'git-predicate'. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adjust 'git-predicate' to use data structures that perform better when used with git repositories with a large number of files. Previously when matching either a regular file or directory, 'git-predicate' would search a list with a length equal to the number of files in the repository. As a search operation happens for roughly every file in the repository, this meant that the time taken to use 'git-predicate' to traverse all the files in a repository was roughly exponential with respect to the number of files in the repository. Now, for matching regular files or symlinks, 'git-predicate' uses a vhash using the inode value as the key. This should perform roughly in constant amount of time, instead of linear with respect to the number of files in the repository. For matching directories, 'git-predicate' now uses a tree structure stored in association lists. To check if a directory is in the tree, the tree is traversed from the root. The time complexity of this depends on the shape of the tree, but it should be an improvement on searching through the list of all files. * guix/git-download.scm (files->directory-tree, directory-in-tree?): New procedures. (git-predicate): Compute DIRECTORY-TREE. Turn INODES into a vhash. Adjust body of lambda accordingly. Co-authored-by: Ludovic Courtès --- guix/git-download.scm | 95 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 27 deletions(-) (limited to 'guix') diff --git a/guix/git-download.scm b/guix/git-download.scm index 316835502c..5019a3e62f 100644 --- a/guix/git-download.scm +++ b/guix/git-download.scm @@ -1,6 +1,7 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2014, 2015, 2016 Ludovic Courtès +;;; Copyright © 2014, 2015, 2016, 2017 Ludovic Courtès ;;; Copyright © 2017 Mathieu Lirzin +;;; Copyright © 2017 Christopher Baines ;;; ;;; This file is part of GNU Guix. ;;; @@ -28,6 +29,7 @@ #:use-module (ice-9 match) #:use-module (ice-9 popen) #:use-module (ice-9 rdelim) + #:use-module (ice-9 vlist) #:use-module (srfi srfi-1) #:export (git-reference git-reference? @@ -125,45 +127,84 @@ HASH-ALGO (a symbol). Use NAME as the file name, or a generic name if #f." "Return the file-name for packages using git-download." (string-append name "-" version "-checkout")) + +;;; +;;; 'git-predicate'. +;;; + +(define (files->directory-tree files) + "Return a tree of vhashes representing the directory listed in FILES, a list +like '(\"a/b\" \"b/c/d\")." + (fold (lambda (file result) + (let loop ((file (string-split file #\/)) + (result result)) + (match file + ((_) + result) + ((directory children ...) + (match (vhash-assoc directory result) + (#f + (vhash-cons directory (loop children vlist-null) + result)) + ((_ . previous) + ;; XXX: 'vhash-delete' is O(n). + (vhash-cons directory (loop children previous) + (vhash-delete directory result))))) + (() + result)))) + vlist-null + files)) + +(define (directory-in-tree? tree directory) + "Return true if DIRECTORY, a string like \"a/b\", denotes a directory listed +in TREE." + (let loop ((directory (string-split directory #\/)) + (tree tree)) + (match directory + (() + #t) + ((head . tail) + (match (vhash-assoc head tree) + ((_ . sub-tree) (loop tail sub-tree)) + (#f #f)))))) + (define (git-predicate directory) "Return a predicate that returns true if a file is part of the Git checkout living at DIRECTORY. Upon Git failure, return #f instead of a predicate. The returned predicate takes two arguments FILE and STAT where FILE is an absolute file name and STAT is the result of 'lstat'." - (define (parent-directory? thing directory) - ;; Return #t if DIRECTORY is the parent of THING. - (or (string-suffix? thing directory) - (and (string-index thing #\/) - (parent-directory? (dirname thing) directory)))) - - (let* ((pipe (with-directory-excursion directory - (open-pipe* OPEN_READ "git" "ls-files"))) - (files (let loop ((lines '())) - (match (read-line pipe) - ((? eof-object?) - (reverse lines)) - (line - (loop (cons line lines)))))) - (inodes (map (lambda (file) - (let ((stat (lstat - (string-append directory "/" file)))) - (cons (stat:dev stat) (stat:ino stat)))) - files)) - (status (close-pipe pipe))) + (let* ((pipe (with-directory-excursion directory + (open-pipe* OPEN_READ "git" "ls-files"))) + (files (let loop ((lines '())) + (match (read-line pipe) + ((? eof-object?) + (reverse lines)) + (line + (loop (cons line lines)))))) + (directory-tree (files->directory-tree files)) + (inodes (fold (lambda (file result) + (let ((stat + (lstat (string-append directory "/" + file)))) + (vhash-consv (stat:ino stat) (stat:dev stat) + result))) + vlist-null + files)) + (prefix-length (+ 1 (string-length (canonicalize-path directory)))) + (status (close-pipe pipe))) (and (zero? status) (lambda (file stat) (match (stat:type stat) ('directory - ;; 'git ls-files' does not list directories, only regular files, - ;; so we need this special trick. - (any (lambda (f) (parent-directory? f file)) - files)) + (directory-in-tree? directory-tree + (string-drop file prefix-length))) ((or 'regular 'symlink) ;; Comparing file names is always tricky business so we rely on ;; inode numbers instead - (member (cons (stat:dev stat) (stat:ino stat)) - inodes)) + (match (vhash-assv (stat:ino stat) inodes) + ((_ . dev) (= dev (stat:dev stat))) + (#f #f))) (_ #f)))))) -- cgit v1.2.3 From 952cf67cb13a63cc083f29165f5487d8d69dc04f Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Thu, 27 Jul 2017 11:05:03 +0200 Subject: weather: Show "-m" option in help message. Reported by Alex Kost . * guix/scripts/weather.scm (show-help): Show "-m". --- guix/scripts/weather.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'guix') diff --git a/guix/scripts/weather.scm b/guix/scripts/weather.scm index 9cbeedd288..4c4dfac8f6 100644 --- a/guix/scripts/weather.scm +++ b/guix/scripts/weather.scm @@ -148,7 +148,7 @@ Report the availability of substitutes.\n")) --substitute-urls=URLS check for available substitutes at URLS")) (display (G_ " - --manifest=MANIFEST + -m, --manifest=MANIFEST look up substitutes for packages specified in MANIFEST")) (display (G_ " -s, --system=SYSTEM consider substitutes for SYSTEM--e.g., \"i686-linux\"")) -- cgit v1.2.3