From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:470:142:3::10]:34306) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hbbxh-0007hn-Qm for guix-patches@gnu.org; Thu, 13 Jun 2019 22:29:07 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hbbxf-0006gn-9m for guix-patches@gnu.org; Thu, 13 Jun 2019 22:29:05 -0400 Received: from debbugs.gnu.org ([209.51.188.43]:51049) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hbbxe-0006g2-Nf for guix-patches@gnu.org; Thu, 13 Jun 2019 22:29:02 -0400 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1hbbxe-0001Dl-JU for guix-patches@gnu.org; Thu, 13 Jun 2019 22:29:02 -0400 Subject: bug#36048: [PATCH] guix: import: hackage: handle hackage revisions Resent-To: guix-patches@gnu.org Resent-Message-ID: From: Timothy Sample References: <20190601223636.74362-1-rob@vllmrt.net> <20190613193914.11552-1-rob@vllmrt.net> Date: Thu, 13 Jun 2019 22:28:20 -0400 In-Reply-To: <20190613193914.11552-1-rob@vllmrt.net> (Robert Vollmert's message of "Thu, 13 Jun 2019 21:39:14 +0200") Message-ID: <87blz0aojf.fsf@ngyro.com> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+kyle=kyleam.com@gnu.org Sender: "Guix-patches" To: Robert Vollmert Cc: 36048-done@debbugs.gnu.org Hi Robert, Robert Vollmert writes: > Hackage packages can have metadata revision (cabal-file only) > that aren't reflected in the source archive. haskell-build-system > has support for this, but previously `guix import hackage` would > create a definition based on the new cabal file but building using > the old cabal file. > > Compare https://debbugs.gnu.org/cgi/bugreport.cgi?bug=3D35750. > > * guix/import/cabal.scm: Parse `x-revision:` property. > * guix/import/hackage.scm: Compute hash of cabal file, and write > cabal-revision build system arguments. > * guix/tests/hackage.scm: Test import of cabal revision. > --- > guix/import/cabal.scm | 7 +++-- > guix/import/hackage.scm | 69 ++++++++++++++++++++++++++++------------- > tests/hackage.scm | 45 +++++++++++++++++++++++++++ > 3 files changed, 98 insertions(+), 23 deletions(-) > > diff --git a/guix/import/cabal.scm b/guix/import/cabal.scm > index 1a87be0b00..7dfe771e41 100644 > --- a/guix/import/cabal.scm > +++ b/guix/import/cabal.scm > @@ -40,6 +40,7 @@ > cabal-package? > cabal-package-name > cabal-package-version > + cabal-package-revision > cabal-package-license > cabal-package-home-page > cabal-package-source-repository > @@ -638,13 +639,14 @@ If #f use the function 'port-filename' to obtain it= ." > ;; information of the Cabal file, but only the ones we currently are > ;; interested in. > (define-record-type > - (make-cabal-package name version license home-page source-repository > + (make-cabal-package name version revision license home-page source-rep= ository > synopsis description > executables lib test-suites > flags eval-environment custom-setup) > cabal-package? > (name cabal-package-name) > (version cabal-package-version) > + (revision cabal-package-revision) > (license cabal-package-license) > (home-page cabal-package-home-page) > (source-repository cabal-package-source-repository) > @@ -838,6 +840,7 @@ See the manual for limitations."))))))) > (define (cabal-evaluated-sexp->package evaluated-sexp) > (let* ((name (lookup-join evaluated-sexp "name")) > (version (lookup-join evaluated-sexp "version")) > + (revision (lookup-join evaluated-sexp "x-revision")) > (license (lookup-join evaluated-sexp "license")) > (home-page (lookup-join evaluated-sexp "homepage")) > (home-page-or-hackage > @@ -856,7 +859,7 @@ See the manual for limitations."))))))) > (custom-setup (match (make-cabal-section evaluated-sexp 'cust= om-setup) > ((x) x) > (_ #f)))) > - (make-cabal-package name version license home-page-or-hackage > + (make-cabal-package name version revision license home-page-or-hac= kage > source-repository synopsis description executa= bles lib > test-suites flags eval-environment custom-setu= p))) >=20=20 > diff --git a/guix/import/hackage.scm b/guix/import/hackage.scm > index 366256b40d..b739b61157 100644 > --- a/guix/import/hackage.scm > +++ b/guix/import/hackage.scm > @@ -117,19 +117,34 @@ version is returned." > (#f name) > (m (match:substring m 1))))))) >=20=20 > +(define (read-cabal-and-hash port) > + "Given an input PORT, read a cabal file and its base32 hash from it, > +and return both values." > + (let-values (((port get-hash) (open-sha256-input-port port))) > + (values (read-cabal (canonical-newline-port port)) > + (bytevector->nix-base32-string (get-hash))))) > + > +(define (hackage-fetch-and-hash name-version) > + "Fetch the latest Cabal revision for the package NAME-VERSION, and ret= urn > +two values: the parsed Cabal file and its base32 hash. If the version pa= rt > +is omitted from the package name, then fetch the latest version. Return = #f > +on failure." > + (guard (c ((and (http-get-error? c) > + (=3D 404 (http-get-error-code c))) > + (values #f #f))) ;"expected" if package is unknown > + (let*-values (((name version) (package-name->name+version name-versi= on)) > + ((url) (hackage-cabal-url name version)) > + ((port) (http-fetch url)) > + ((cabal hash) (read-cabal-and-hash port))) > + (close-port port) > + (values cabal hash)))) > + > (define (hackage-fetch name-version) > "Return the Cabal file for the package NAME-VERSION, or #f on failure.= If > the version part is omitted from the package name, then return the latest > version." > - (guard (c ((and (http-get-error? c) > - (=3D 404 (http-get-error-code c))) > - #f)) ;"expected" if package is unknown > - (let-values (((name version) (package-name->name+version name-versio= n))) > - (let* ((url (hackage-cabal-url name version)) > - (port (http-fetch url)) > - (result (read-cabal (canonical-newline-port port)))) > - (close-port port) > - result)))) > + (let-values (((cabal hash) (hackage-fetch-and-hash name-version))) > + cabal)) >=20=20 > (define string->license > ;; List of valid values from > @@ -198,15 +213,20 @@ package being processed and is used to filter refer= ences to itself." > (cons own-name ghc-standard-libraries= )))) > dependencies)) >=20=20 > -(define* (hackage-module->sexp cabal #:key (include-test-dependencies? #= t)) > +(define* (hackage-module->sexp cabal cabal-hash > + #:key (include-test-dependencies? #t)) > "Return the `package' S-expression for a Cabal package. CABAL is the > -representation of a Cabal file as produced by 'read-cabal'." > +representation of a Cabal file as produced by 'read-cabal'. CABAL-HASH = is > +the hash of the Cabal file." >=20=20 > (define name > (cabal-package-name cabal)) >=20=20 > (define version > (cabal-package-version cabal)) > + > + (define revision > + (cabal-package-revision cabal)) >=20=20=20=20 > (define source-url > (hackage-source-url name version)) > @@ -252,9 +272,14 @@ representation of a Cabal file as produced by 'read-= cabal'." > (list 'quasiquote inputs)))))) >=20=20=20=20 > (define (maybe-arguments) > - (if (not include-test-dependencies?) > - '((arguments `(#:tests? #f))) > - '())) > + (match (append (if (not include-test-dependencies?) > + '(#:tests? #f) > + '()) > + (if (not (string-null? revision)) > + `(#:cabal-revision (,revision ,cabal-hash)) > + '())) > + (() '()) > + (args `((arguments (,'quasiquote ,args)))))) >=20=20 > (let ((tarball (with-store store > (download-to-store store source-url)))) > @@ -294,13 +319,15 @@ symbol 'true' or 'false'. The value associated wit= h other keys has to conform > to the Cabal file format definition. The default value associated with = the > keys \"os\", \"arch\" and \"impl\" is \"linux\", \"x86_64\" and \"ghc\" > respectively." > - (let ((cabal-meta (if port > - (read-cabal (canonical-newline-port port)) > - (hackage-fetch package-name)))) > - (and=3D> cabal-meta (compose (cut hackage-module->sexp <> > - #:include-test-dependencies? > - include-test-dependencies?) > - (cut eval-cabal <> cabal-environment))))) > + (let-values (((cabal hash) > + (if port > + (read-cabal-and-hash port) > + (hackage-fetch-and-hash package-name)))) > + (and=3D> cabal (compose (cut hackage-module->sexp <> > + hash > + #:include-test-dependencies? > + include-test-dependencies?) > + (cut eval-cabal <> cabal-environment))))) >=20=20 > (define hackage->guix-package/m ;memoized variant > (memoize hackage->guix-package)) > diff --git a/tests/hackage.scm b/tests/hackage.scm > index 38a5825af7..14176b2cf9 100644 > --- a/tests/hackage.scm > +++ b/tests/hackage.scm > @@ -274,6 +274,51 @@ executable cabal > (test-assert "hackage->guix-package test multiline desc (braced)" > (eval-test-with-cabal test-cabal-multiline-braced match-ghc-foo)) >=20=20 > +;; Check Hackage Cabal revisions. > +(define test-cabal-revision > + "name: foo > +version: 1.0.0 > +x-revision: 2 > +homepage: http://test.org > +synopsis: synopsis > +description: description > +license: BSD3 > +executable cabal > + build-depends: > + HTTP >=3D 4000.2.5 && < 4000.3, > + mtl >=3D 2.0 && < 3 > +") > + > +(define-package-matcher match-ghc-foo-revision > + ('package > + ('name "ghc-foo") > + ('version "1.0.0") > + ('source > + ('origin > + ('method 'url-fetch) > + ('uri ('string-append > + "https://hackage.haskell.org/package/foo/foo-" > + 'version > + ".tar.gz")) > + ('sha256 > + ('base32 > + (? string? hash))))) > + ('build-system 'haskell-build-system) > + ('inputs > + ('quasiquote > + (("ghc-http" ('unquote 'ghc-http))))) > + ('arguments > + ('quasiquote > + ('#:cabal-revision > + ("2" "0xxd88fb659f0krljidbvvmkh9ppjnx83j0nqzx8whcg4n5qbyng")))) > + ('home-page "http://test.org") > + ('synopsis (? string?)) > + ('description (? string?)) > + ('license 'bsd-3))) > + > +(test-assert "hackage->guix-package test cabal revision" > + (eval-test-with-cabal test-cabal-revision match-ghc-foo-revision)) > + > (test-assert "read-cabal test 1" > (match (call-with-input-string test-read-cabal-1 read-cabal) > ((("name" ("test-me")) Applied as ca45da9fc9b1eee399ce4344b18cbb129daeca4c! I did make a few changes. Most notably, I tried importing a package and it didn=E2=80=99t work! It turns out that =E2=80=9Chttp-fetch=E2=80=9D ret= urns two values, so I told =E2=80=9Clet-values=E2=80=9D to ignore the second one. Other than tha= t, I made a few changes to the commit message and docstrings, and kept =E2=80=9Ccabal-m= eta=E2=80=9D and =E2=80=9Ccabal-hash=E2=80=9D from before (instead of =E2=80=9Ccabal=E2= =80=9D and =E2=80=9Chash=E2=80=9D). Thanks for the patch! -- Tim