From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp2 ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms11 with LMTPS id IPgRMD5bsV/ZMwAA0tVLHw (envelope-from ) for ; Sun, 15 Nov 2020 16:45:50 +0000 Received: from aspmx1.migadu.com ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp2 with LMTPS id IEL0Kz5bsV/pDgAAB5/wlQ (envelope-from ) for ; Sun, 15 Nov 2020 16:45:50 +0000 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 5E4A2940538 for ; Sun, 15 Nov 2020 16:45:46 +0000 (UTC) Received: from localhost ([::1]:58326 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1keL9t-0004zn-8A for larch@yhetil.org; Sun, 15 Nov 2020 11:45:45 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:47452) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1keKyC-0005XT-EG for guix-devel@gnu.org; Sun, 15 Nov 2020 11:33:40 -0500 Received: from fencepost.gnu.org ([2001:470:142:3::e]:44299) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1keKyC-00082f-43 for guix-devel@gnu.org; Sun, 15 Nov 2020 11:33:40 -0500 Received: from [2a01:e0a:1d:7270:af76:b9b:ca24:c465] (port=44976 helo=ribbon) by fencepost.gnu.org with esmtpsa (TLS1.2:RSA_AES_256_CBC_SHA1:256) (Exim 4.82) (envelope-from ) id 1keKy4-0007TJ-HZ for guix-devel@gnu.org; Sun, 15 Nov 2020 11:33:39 -0500 From: =?utf-8?Q?Ludovic_Court=C3=A8s?= To: Subject: A plan for parameterized packages X-URL: http://www.fdn.fr/~lcourtes/ X-Revolutionary-Date: 25 Brumaire an 229 de la =?utf-8?Q?R=C3=A9volution?= X-PGP-Key-ID: 0x090B11993D9AEBB5 X-PGP-Key: http://www.fdn.fr/~lcourtes/ludovic.asc X-PGP-Fingerprint: 3CE4 6455 8A84 FDC6 9DB4 0CFB 090B 1199 3D9A EBB5 X-OS: x86_64-pc-linux-gnu Date: Sun, 15 Nov 2020 17:33:28 +0100 Message-ID: <87eeku8trb.fsf@gnu.org> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 Content-Type: multipart/signed; boundary="==-=-="; micalg=pgp-sha512; protocol="application/pgp-signature" X-BeenThere: guix-devel@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "Development of GNU Guix and the GNU System distribution." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-devel-bounces+larch=yhetil.org@gnu.org Sender: "Guix-devel" X-Scanner: ns3122888.ip-94-23-21.eu Authentication-Results: aspmx1.migadu.com; dkim=none; dmarc=pass (policy=none) header.from=gnu.org; spf=pass (aspmx1.migadu.com: domain of guix-devel-bounces@gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=guix-devel-bounces@gnu.org X-Spam-Score: -3.61 X-TUID: Iwtg/lXVZ7Rb --==-=-= Content-Type: multipart/mixed; boundary="=-=-=" --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Hello Guix! For some time we=E2=80=99ve discussed ways to achieve =E2=80=9Cparameterized packages=E2=80=9D=E2=80=94packages where one can from the command line or f= rom Scheme configure optional build-time features, similar to Gentoo =E2=80=9CUSE flag= s=E2=80=9D. I still have mixed feeling about this feature: on one hand it can bring much welcome flexibility, but on the other hand it can also lead us to the wild west of untested package configurations and combinatorial explosion. It could also be argued that we achieve something similar today by simply defining package variants when we have to: https://guix.gnu.org/manual/devel/en/html_node/Defining-Package-Variants.= html That said, this message is about a possible implementation of package parameters, so here we go. :-) To me the requirements for package parameters are: 1. it must be possible to discover them and choose them from the UI; 2. they must contain on-line internationalized documentation such that the UI can list a package=E2=80=99s parameters and their type; 3. the chosen parameters when installing a package in a profile must be preserved; 4. it must be possible to enumerate all the possible values of a parameter, and thus to build the Cartesian product of all the possible parameter combinations of a package (or of a package graph!), so we can test those combinations as much as possible. This leads to the patches below. The last one gives an example use for Bitlbee: it adds a =E2=80=9Clibpurple=E2=80=9D parameter that allows users = to choose whether or not to enable the optional libpurple dependency: =2D-8<---------------cut here---------------start------------->8--- $ ./pre-inst-env guix build bitlbee --with-parameter=3Dbitlbee=3Dlibpurple= =3Dtrue -n The following derivation would be built: /gnu/store/mkknqgjsa93ajcl5d2krngizb11j1b0q-bitlbee-3.6.drv $ ./pre-inst-env guix build bitlbee --with-parameter=3Dbitlbee=3Dlibpurple= =3Dfalse -n /gnu/store/c2ckg51ffwgs6jni3l549k06w3jd3b7a-bitlbee-3.6 $ ./pre-inst-env guix build bitlbee --with-parameter=3Dbitlbee=3Dlibpurple= =3Dwat? -n guix build: error: wrong value $ ./pre-inst-env guix build bitlbee --with-parameter=3Dbitlbee=3Dlibviolet= =3Dtrue -n gnu/packages/messaging.scm:283:5: error: libviolet: no such package paramet= er $ ./pre-inst-env guix show bitlbee name: bitlbee version: 3.6 outputs: out parameters: libpurple systems: x86_64-linux i686-linux dependencies: check@0.12.0 glib@2.62.6 gnutls@3.6.12 libotr@4.1.1 perl@5.30= .2 pkg-config@0.29.2 python@3.8.2 location: gnu/packages/messaging.scm:243:2 homepage: https://www.bitlbee.org/ license: GPL 2+, FreeBSD synopsis: IRC to instant messaging gateway=20=20 description: BitlBee brings IM (instant messaging) to IRC clients, for peop= le who have an IRC client running all + the time and don't want to run an additional IM client. BitlBee currentl= y supports XMPP/Jabber (including Google + Talk), MSN Messenger, Yahoo! Messenger, AIM and ICQ, and the Twitter micr= oblogging network (plus all other Twitter + API compatible services like identi.ca and status.net). $ ./pre-inst-env guix install bitlbee --with-parameter=3Dbitlbee=3Dlibpurpl= e=3Dtrue -p /tmp/test-bitlbee The following package will be installed: bitlbee 3.6 The following derivation will be built: /gnu/store/clvs5521v5ybdw1z1yh97z2ky1dxm4d9-bitlbee-3.6.drv [...] $ cat /tmp/test-bitlbee/manifest ;; This file was automatically generated and is for internal use only. ;; It cannot be passed to the '--manifest' option. (manifest (version 3) (packages (("bitlbee" "3.6" "out" "/gnu/store/d67r9k5hwfm5hkd1d3pbhg49fcc2jj4q-bitlbee-3.6" (propagated-inputs ()) (search-paths ()) (properties (transformations (with-parameter . "bitlbee=3Dlibpurple=3Dtrue"))))))) =2D-8<---------------cut here---------------end--------------->8--- That=E2=80=99s it! We would need more things, like a =E2=80=98guix parameters=E2=80=99 command= to show the available parameters of a package and an =E2=80=98--all-parameter-values=E2= =80=99 option to =E2=80=98guix build=E2=80=99 to build all the variants of a given packag= e. An important question: do we have examples of packages for which we=E2=80= =99d like to have parameters? I=E2=80=99d grepped for =E2=80=9Cinherit=E2=80=9D= and that yields a few potential candidates, but also maybe a few potential non-candidates. Would this be a good fit for them? Thoughts? Ludo=E2=80=99. --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=0003-ui-package-recutils-emits-parameters-field.patch Content-Transfer-Encoding: quoted-printable From=209155411f2e8e78922e1e46d92068ac8f652ff0a5 Mon Sep 17 00:00:00 2001 From: =3D?UTF-8?q?Ludovic=3D20Court=3DC3=3DA8s?=3D Date: Sun, 15 Nov 2020 17:01:14 +0100 Subject: [PATCH 3/4] ui: 'package->recutils' emits "parameters" field. * guix/ui.scm (package->recutils): Add "parameters" recutils field. =2D-- guix/ui.scm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/guix/ui.scm b/guix/ui.scm index 4e686297e8..2485400cc9 100644 =2D-- a/guix/ui.scm +++ b/guix/ui.scm @@ -60,6 +60,7 @@ #:hide (package-name->name+version ;; Avoid "overrides core binding" warning. delete)) + #:autoload (guix parameters) (package-parameters package-parameter-nam= e) #:use-module (srfi srfi-1) #:use-module (srfi srfi-9 gnu) #:use-module (srfi srfi-11) @@ -1529,6 +1530,10 @@ HYPERLINKS? is true, emit hyperlink escape sequences= when appropriate." (format port "name: ~a~%" (package-name p)) (format port "version: ~a~%" (package-version p)) (format port "outputs: ~a~%" (string-join (package-outputs p))) + (match (package-parameters p) + (() #t) + (lst (format port "parameters:~{ ~a~}~%" + (map package-parameter-name lst)))) (format port "systems: ~a~%" (string-join (package-transitive-supported-systems p))) (format port "dependencies: ~a~%" =2D-=20 2.29.2 --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=0004-gnu-bitlbee-Add-libpurple-parameter.patch Content-Transfer-Encoding: quoted-printable From=2049d7746ada4d4674acbbfd2606ad9bff4f6207eb Mon Sep 17 00:00:00 2001 From: =3D?UTF-8?q?Ludovic=3D20Court=3DC3=3DA8s?=3D Date: Sun, 15 Nov 2020 17:04:18 +0100 Subject: [PATCH 4/4] gnu: bitlbee: Add "libpurple" parameter. * gnu/packages/messaging.scm (bitlbee)[inputs]: Add optional PIDGIN input. [properties]: New field. (bitlbee-purple): Mark as deprecated. =2D-- gnu/packages/messaging.scm | 43 +++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/gnu/packages/messaging.scm b/gnu/packages/messaging.scm index b462504894..2f0f44d10d 100644 =2D-- a/gnu/packages/messaging.scm +++ b/gnu/packages/messaging.scm @@ -120,6 +120,7 @@ #:use-module (guix build-system trivial) #:use-module (guix download) #:use-module (guix git-download) + #:use-module (guix parameters) #:use-module (guix hg-download) #:use-module ((guix licenses) #:prefix license:) #:use-module (guix packages) @@ -256,7 +257,8 @@ end-to-end encryption.") ("libotr" ,libotr) ("gnutls" ,gnutls) ("python" ,python) =2D ("perl" ,perl))) + ("perl" ,perl) + ,@(optionally 'libpurple? `("purple" ,pidgin)))) (arguments `(#:phases (modify-phases %standard-phases @@ -275,7 +277,21 @@ end-to-end encryption.") (invoke "./configure" (string-append "--prefix=3D" (assoc-ref outputs "out")) =2D "--otr=3D1")))))) + "--otr=3D1" + ,@(optionally 'libpurple? "--purple=3D1"))))) + + ;; XXX: Tests fail to link, and ./configure says that it's "support= ed + ;; on a best-effort basis" anyway. + #:tests? ,(not (assq-ref (package-properties this-package) + 'libpurple?)))) + (properties + `((parameters + ,(package-parameter (name "libpurple") + (description + "Whether to enable libpurple (Pidgin) +support.") + (property 'libpurple?) + (type boolean))))) (synopsis "IRC to instant messaging gateway") (description "BitlBee brings IM (instant messaging) to IRC clients, for people who have an IRC client running all the time and don't want to run an @@ -289,25 +305,10 @@ identi.ca and status.net).") (define-public bitlbee-purple ;; This variant uses libpurple, which provides support for more protocol= s at ;; the expense of a much bigger closure. =2D (package/inherit bitlbee =2D (name "bitlbee-purple") =2D (synopsis "IRC to instant messaging gateway (using Pidgin's libpurpl= e)") =2D (inputs `(("purple" ,pidgin) =2D ,@(package-inputs bitlbee))) =2D (arguments =2D (substitute-keyword-arguments (package-arguments bitlbee) =2D ((#:phases phases '%standard-phases) =2D `(modify-phases ,phases =2D (replace 'configure ;add "--purple=3D1" =2D (lambda* (#:key outputs #:allow-other-keys) =2D (invoke "./configure" =2D (string-append "--prefix=3D" =2D (assoc-ref outputs "out")) =2D "--otr=3D1" "--purple=3D1"))))) =2D ((#:tests? _ #t) =2D ;; XXX: Tests fail to link, and ./configure says that it's "supp= orted =2D ;; on a best-effort basis" anyway. =2D #f))))) + (deprecated-package "bitlbee-purple" + (package/inherit bitlbee + (properties `((libpurple? . #t) + ,@(package-properties bitlbee)))))) =20 (define-public bitlbee-discord (package =2D-=20 2.29.2 --=-=-= Content-Type: text/x-patch; charset=utf-8 Content-Disposition: inline; filename=0001-DRAFT-Add-guix-parameters.patch Content-Transfer-Encoding: quoted-printable From=20f42b68499c4e2a9bd368fe6a516932f5afa7a189 Mon Sep 17 00:00:00 2001 From: =3D?UTF-8?q?Ludovic=3D20Court=3DC3=3DA8s?=3D Date: Sun, 15 Nov 2020 16:58:52 +0100 Subject: [PATCH 1/4] DRAFT Add (guix parameters). DRAFT: Missing tests & doc. * guix/parameters.scm: New file. * Makefile.am (MODULES): Add it. =2D-- Makefile.am | 1 + guix/parameters.scm | 131 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 guix/parameters.scm diff --git a/Makefile.am b/Makefile.am index e7053ee4f4..72f955360d 100644 =2D-- a/Makefile.am +++ b/Makefile.am @@ -235,6 +235,7 @@ MODULES =3D \ guix/build/make-bootstrap.scm \ guix/search-paths.scm \ guix/packages.scm \ + guix/parameters.scm \ guix/import/cabal.scm \ guix/import/cpan.scm \ guix/import/cran.scm \ diff --git a/guix/parameters.scm b/guix/parameters.scm new file mode 100644 index 0000000000..e4f8240aa4 =2D-- /dev/null +++ b/guix/parameters.scm @@ -0,0 +1,131 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright =C2=A9 2020 Ludovic Court=C3=A8s +;;; +;;; 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 parameters) + #:use-module (guix packages) + #:use-module (guix records) + #:use-module (guix diagnostics) + #:use-module (guix i18n) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-34) + #:use-module (srfi srfi-35) + #:use-module (ice-9 match) + #:export (package-parameter + package-parameter? + package-parameter-name + package-parameter-property + package-parameter-type + package-parameter-description + + boolean + optionally + + package-parameters + lookup-package-parameter + package-parameter-value + set-package-parameter-value)) + +;;; Commentary: +;;; +;;; This module provides a way to express high-level "package parameters", +;;; which allow users to customize how packages are built. Parameters are= an +;;; interface that package developers define, where each parameter has a n= ame +;;; and type. The user interface then converts parameter values from stri= ng +;;; to Scheme values and records them in the package properties. +;;; +;;; Package parameters are discoverable; their description is +;;; internationalized. The possible values of a parameter can be enumerat= ed, +;;; and thus the Cartesian product of all possible parameter values for a +;;; package can be enumerated as well. +;;; +;;; Code: + +;; Package parameter interface. +(define-record-type* package-parameter + make-package-parameter + package-parameter? + (name package-parameter-name) + (property package-parameter-property (default (string->symbol name)= )) + (type package-parameter-type) + (description package-parameter-description)) + +;; Type of a package parameter. +(define-record-type* parameter-type + make-parameter-type + parameter-type? + (name parameter-type-name) ;debugging purposes onl= y! + (string->value parameter-type-string->value) + (value->string parameter-type-value->string) + (universe parameter-type-universe)) + +(define boolean + ;; The Boolean parameter type. + (parameter-type (name 'boolean) + (universe '(#true #false)) + (value->string + (match-lambda + (#f "false") + (#t "true"))) + (string->value + (lambda (str) + (cond ((string-ci=3D? str "true") + #t) + ((string-ci=3D? str "false") + #f) + (else + (raise (condition + (&message (message "wrong value"))))))= )))) + +(define (package-parameters package) + (or (assq-ref (package-properties package) 'parameters) + '())) + +(define (package-parameter-value package parameter) + (assq-ref (package-properties package) + (package-parameter-property parameter))) + +(define (lookup-package-parameter package name) + (find (lambda (parameter) + (string=3D? (package-parameter-name parameter) name)) + (package-parameters package))) + +(define (set-package-parameter-value package name value) + (let ((parameter (lookup-package-parameter package name)) + (location (package-field-location package 'properties))) + (unless parameter + (raise (apply make-compound-condition + (formatted-message + (G_ "~a: no such package parameter") + name) + (if location + (list (condition + (&error-location (location location)))) + '())))) + (let* ((property (package-parameter-property parameter)) + (type (package-parameter-type parameter)) + (value ((parameter-type-string->value type) value))) + (package/inherit package + (properties + (alist-cons property value + (alist-delete property (package-properties package) + eq?))))))) + +(define-syntax-rule (optionally property exp) + (if (assq-ref (package-properties this-package) property) + (list exp) + '())) =2D-=20 2.29.2 --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=0002-DRAFT-transformations-Add-with-parameter.patch Content-Transfer-Encoding: quoted-printable From=205d6d81e4c3e37de53fe7f62ff7ef94da8b2df033 Mon Sep 17 00:00:00 2001 From: =3D?UTF-8?q?Ludovic=3D20Court=3DC3=3DA8s?=3D Date: Sun, 15 Nov 2020 16:59:48 +0100 Subject: [PATCH 2/4] DRAFT transformations: Add '--with-parameter'. DRAFT: Missing tests & doc. * guix/transformations.scm (evaluate-parameter-specs) (transform-package-parameters): New procedures. (%transformations, %transformation-options): Add 'with-parameter'. =2D-- guix/transformations.scm | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/guix/transformations.scm b/guix/transformations.scm index 30142dd059..0f83eb470d 100644 =2D-- a/guix/transformations.scm +++ b/guix/transformations.scm @@ -25,6 +25,7 @@ #:autoload (guix download) (download-to-store) #:autoload (guix git-download) (git-reference? git-reference-url) #:autoload (guix git) (git-checkout git-checkout? git-checkout-url) + #:autoload (guix parameters) (set-package-parameter-value) #:use-module (guix utils) #:use-module (guix memoization) #:use-module (guix gexp) @@ -324,6 +325,41 @@ a checkout of the Git repository at the given URL." (rewrite obj) obj))) =20 +(define (evaluate-parameter-specs specs proc) + "Parse SPECS, a list of strings like \"bitlbee=3Dpurple=3Dtrue\", and re= turn a +list of spec/procedure pairs, where (PROC PACKAGE PARAMETER VALUE) is call= ed +to return the replacement package. Raise an error if an element of SPECS = uses +invalid syntax, or if a package it refers to could not be found." + (map (lambda (spec) + (match (string-tokenize spec %not-equal) + ((spec name value) + (define (replace old) + (proc old name value)) + + (cons spec replace)) + (_ + (raise + (formatted-message + (G_ "invalid package parameter specification: ~s") + spec))))) + specs)) + +(define (transform-package-parameters replacement-specs) + "Return a procedure that, when passed a package, replaces its direct +dependencies according to REPLACEMENT-SPECS. REPLACEMENT-SPECS is a list = of +strings like \"guile-next=3Dstable-3.0\" meaning that packages are built u= sing +'guile-next' from the latest commit on its 'stable-3.0' branch." + (define (replace old name value) + (set-package-parameter-value old name value)) + + (let* ((replacements (evaluate-parameter-specs replacement-specs + replace)) + (rewrite (package-input-rewriting/spec replacements))) + (lambda (obj) + (if (package? obj) + (rewrite obj) + obj)))) + (define (package-dependents/spec top bottom) "Return the list of dependents of BOTTOM, a spec string, that are also dependencies of TOP, a package." @@ -467,6 +503,7 @@ to the same package but with #:strip-binaries? #f in it= s 'arguments' field." (with-branch . ,transform-package-source-branch) (with-commit . ,transform-package-source-commit) (with-git-url . ,transform-package-source-git-url) + (with-parameter . ,transform-package-parameters) (with-c-toolchain . ,transform-package-toolchain) (with-debug-info . ,transform-package-with-debug-info) (without-tests . ,transform-package-tests))) @@ -503,6 +540,8 @@ to the same package but with #:strip-binaries? #f in it= s 'arguments' field." (parser 'with-commit)) (option '("with-git-url") #t #f (parser 'with-git-url)) + (option '("with-parameter") #t #f + (parser 'with-parameter)) (option '("with-c-toolchain") #t #f (parser 'with-c-toolchain)) (option '("with-debug-info") #t #f =2D-=20 2.29.2 --=-=-=-- --==-=-= Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQJBBAEBCgArFiEEPORkVYqE/cadtAz7CQsRmT2a67UFAl+xWFgNHGx1ZG9AZ251 Lm9yZwAKCRAJCxGZPZrrtflmD/wMrKsx6VT4k+TydcWXopP43NhWkTVgOxUoggYs sI94jCyE3LULFjT4Pf+jG8SUFh11y/saMKpxrVca8qBgyr97LWE2rhY80xIH1Qhj l8wkqW5VaIhVJWikr0uP1wu3iwUz84CgehfpJEgVvO+L+3qyE/8YxtNR6hWoimuo B2waMTMWm0yWhLj660DswM7AxqZZVgjkgVZk74Fq4YpeJudY+y+WlvQ+DyAEyJPw YqY/x5aFSIYj170n8tXzPL5Y+K6lErxp1CifNyMmoW4a36DOvrnx9fTBAmnkGSvU A32FwU3nfllkZtmuuEktw+trwyIrxt5OuWwk4+ySHc2fnsJz8FpxidSeKlYSY8uw bASDg7HzS+FTKBKVyEUl/Y6ayh1b5fJ1N8FOxfwINNmG5zB28fcZXF+riyDRNguw 05X39EgJSyBAaUomsq/opDzOYys/uAWlaqxcJYbfrnE8f+VOBHPloBou9F84Hd6k v4KDAKnU9GBCAk5Kb0dYVy4oLW0w9hhL0mLv9pUDXTJJtWR2QCaIEIHvH95LSKIu gJ/k/NVApUBZIMyqch4x5tDceH1/+dq53DAlKAu0Qr6dUx6hBVR/5dOtjySExa9W amMK79irh0sY/hm+BGiqFr3Vr9iSHaviaEAkrVoAb9lvTYnhjy0KSE7jsvAeTzpD 3BiSHA== =AF5+ -----END PGP SIGNATURE----- --==-=-=--