diff --git a/doc/contributing.texi b/doc/contributing.texi index a66f5374b9..b61f6a534c 100644 --- a/doc/contributing.texi +++ b/doc/contributing.texi @@ -279,15 +279,31 @@ not affected by the change; @code{guix refresh --list-dependent @var{package}} will help you do that (@pxref{Invoking guix refresh}). @item +@cindex determinism, of build processes +@cindex reproducible builds, checking Check whether the package's build process is deterministic. This typically means checking whether an independent build of the package yields the exact same result that you obtained, bit for bit. -A simple way to do that is with @command{guix challenge} -(@pxref{Invoking guix challenge}). You may run it once the package has -been committed and built by @code{hydra.gnu.org} to check whether it -obtains the same result as you did. Better yet: Find another machine -that can build it and run @command{guix publish}. +A simple way to do that is by building the same package several times in +a row on your machine (@pxref{Invoking guix build}): + +@example +guix build --rounds=2 my-package +@end example + +This is enough to catch a class of common non-determinism issues, such +as timestamps or randomly-generated output in the build result. + +Another option is to use @command{guix challenge} (@pxref{Invoking guix +challenge}). You may run it once the package has been committed and +built by @code{hydra.gnu.org} to check whether it obtains the same +result as you did. Better yet: Find another machine that can build it +and run @command{guix publish}. Since the remote build machine is +likely different from yours, this can catch non-determinism issues +related to the hardware---e.g., use of different instruction set +extensions---or to the operating system kernel---e.g., reliance on +@code{uname} or @file{/proc} files. @end enumerate diff --git a/doc/guix.texi b/doc/guix.texi index 309548be88..390e7949c0 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -3876,6 +3876,20 @@ Do not use substitutes for build products. That is, always build things locally instead of allowing downloads of pre-built binaries (@pxref{Substitutes}). +@item --rounds=@var{n} +Build each derivation @var{n} times in a row, and raise an error if +consecutive build results are not bit-for-bit identical. + +This is a useful way to detect non-deterministic builds processes. +Non-deterministic build processes are a problem because they make it +practically impossible for users to @emph{verify} whether third-party +binaries are genuine. @xref{Invoking guix challenge}, for more. + +Note that, currently, the differing build results are not kept around, +so you will have to manually investigate in case of an error---e.g., by +stashing one of the build results with @code{guix archive --export}, +then rebuilding, and finally comparing the two results. + @item --no-build-hook Do not attempt to offload builds @i{via} the daemon's ``build hook'' (@pxref{Daemon Offload Setup}). That is, always build things locally diff --git a/guix/scripts/build.scm b/guix/scripts/build.scm index 192ed5cd45..072840b953 100644 --- a/guix/scripts/build.scm +++ b/guix/scripts/build.scm @@ -170,6 +170,8 @@ options handled by 'set-build-options-from-command-line', and listed in --timeout=SECONDS mark the build as failed after SECONDS of activity")) (display (_ " --verbosity=LEVEL use the given verbosity LEVEL")) + (display (_ " + --rounds=N build N times in a row to detect non-determinism")) (display (_ " -c, --cores=N allow the use of up to N CPU cores for the build")) (display (_ " @@ -181,6 +183,7 @@ options handled by 'set-build-options-from-command-line', and listed in ;; TODO: Add more options. (set-build-options store #:keep-failed? (assoc-ref opts 'keep-failed?) + #:rounds (assoc-ref opts 'rounds) #:build-cores (or (assoc-ref opts 'cores) 0) #:max-build-jobs (or (assoc-ref opts 'max-jobs) 1) #:fallback? (assoc-ref opts 'fallback?) @@ -210,6 +213,12 @@ options handled by 'set-build-options-from-command-line', and listed in (apply values (alist-cons 'keep-failed? #t result) rest))) + (option '("rounds") #t #f + (lambda (opt name arg result . rest) + (apply values + (alist-cons 'rounds (string->number* arg) + result) + rest))) (option '("fallback") #f #f (lambda (opt name arg result . rest) (apply values