From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp11.migadu.com ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms5.migadu.com with LMTPS id iDZ6AzfYf2M43QAAbAwnHQ (envelope-from ) for ; Thu, 24 Nov 2022 21:46:47 +0100 Received: from aspmx1.migadu.com ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp11.migadu.com with LMTPS id 0ECgAzfYf2OtXgEA9RJhRA (envelope-from ) for ; Thu, 24 Nov 2022 21:46:47 +0100 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 94CCC700 for ; Thu, 24 Nov 2022 21:46:46 +0100 (CET) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1oyJ10-0006AU-FC; Thu, 24 Nov 2022 15:40:10 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1oyJ0z-0006A5-27 for guix-devel@gnu.org; Thu, 24 Nov 2022 15:40:09 -0500 Received: from knopi.disroot.org ([178.21.23.139]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1oyJ0w-0004eh-FU for guix-devel@gnu.org; Thu, 24 Nov 2022 15:40:08 -0500 Received: from localhost (localhost [127.0.0.1]) by disroot.org (Postfix) with ESMTP id 4453C411B7 for ; Thu, 24 Nov 2022 21:26:44 +0100 (CET) X-Virus-Scanned: SPAM Filter at disroot.org Received: from knopi.disroot.org ([127.0.0.1]) by localhost (disroot.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 9RNtlK0SwiLd for ; Thu, 24 Nov 2022 21:26:42 +0100 (CET) Mime-Version: 1.0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=disroot.org; s=mail; t=1669321602; bh=s0v69mhzfpBw4uohS45xjNFezkF2UU/hFN+G58CrUAg=; h=Subject:From:To:Date:From; b=aAmxQdqlFf3iVTcOiFLNZbOA4FTZxpBDXjjtQk1+kPpxRwXz1TcgYPPkd4wYsp50N Mb1C7CTIxi2n7AfOVPcrHJJRko8gznS1E/TT4j6p18c9wSEECWi0vIcelgj0u1xDXE wqYWIpKvponuAjHBlcEvVoF7h3gsGPcJQAqf/UkK8F2z/3bBt3ZfnPSABmTZ5Db2VY k+caLEnan3+U9B4/J8tSfOecsRcBpICv/oKG32lftB/5IH2aB7mlsoIsTdrTNRVY8f 0PhaDll3KQP+86Kqc+05/4Rvoinn4YxJENx3E6pvD5WwisD8YrdNOa6F012KFz4y1W 4SEJbvnlJUtgw== Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=UTF-8 Subject: [RFC] package-with-features From: "(" To: Date: Thu, 24 Nov 2022 20:26:16 +0000 Message-Id: Received-SPF: pass client-ip=178.21.23.139; envelope-from=paren@disroot.org; helo=knopi.disroot.org X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: guix-devel@gnu.org X-Mailman-Version: 2.1.29 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-bounces+larch=yhetil.org@gnu.org X-Migadu-Flow: FLOW_IN X-Migadu-Country: US ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1669322806; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding:list-id:list-help: list-unsubscribe:list-subscribe:list-post:dkim-signature; bh=h5hSDWr25JhkeoGGhVM2KuOseKXY+glcJS3TNvGaZxo=; b=QOZXkBS5TlAoLp9y621e4xWLT9OxV2re+MHVwu6+niP0gQr5V0yhKELcU8hfx2vc0gvMFb 0DGykQsX8wr73JKSFRTxSo9t5BsnmoGJkvVHwhVgy1r4VrJSkJ77AClPkbhGvmjUWgPXnT KOMzP/aFMXqk2YrfCqSrtA6TQ+bmKp1y/3yCsdncQ/cSWhCMLEJMNt9NrAOO+EJ96H8EVa p49EIiNJ/X81iy3YDGI/5aY19CwBN9nD/DOPWYZnB9QMGV6mstsuv5ff+GWjYNZLfiWBk/ qUVNFIMUt9RnrsElP+VMrDOsGJ0fuSmiRl9qZOkdgxagXCS/OlxrINKFebpvTA== ARC-Seal: i=1; s=key1; d=yhetil.org; t=1669322806; a=rsa-sha256; cv=none; b=mx3qf70yQ2KZ8aNjcSjVfubZ3S1Y0pfR4ek2fwsoaPfhVxsALi4wGdxr2TH8A+l/HVb7lL NJ2XbofJG9RGkWFyKyqxsRkqH4IEHslwFEQAXiZjfgOsDU4IvrskrvotuxdnWu0TKIG8Wb ZT2pYYD+5DdhmHyU8e1nxbdOiKP5iczmsTsYmCZCRYI6w4N+g5cvUBwg2OfA82rcHTNBOK /A+QgObA11MS8FFpbXmb//tUZuzyBsLrsbHxpvQJ97QRp+U1/cDbgJ4ZfaJsI6XGLMMAh6 Qbq/fTwRVaLLbRG0Oci6ABjNHRYIwS93Lkj6ympzYhNpwQ9grslGgPjnC6KSIw== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=pass header.d=disroot.org header.s=mail header.b=aAmxQdql; dmarc=pass (policy=reject) header.from=disroot.org; spf=pass (aspmx1.migadu.com: domain of "guix-devel-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-devel-bounces+larch=yhetil.org@gnu.org" X-Migadu-Spam-Score: -1.48 Authentication-Results: aspmx1.migadu.com; dkim=pass header.d=disroot.org header.s=mail header.b=aAmxQdql; dmarc=pass (policy=reject) header.from=disroot.org; spf=pass (aspmx1.migadu.com: domain of "guix-devel-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-devel-bounces+larch=yhetil.org@gnu.org" X-Migadu-Queue-Id: 94CCC700 X-Spam-Score: -1.48 X-Migadu-Scanner: scn1.migadu.com X-TUID: h505hVONrMDc Heya Guix, This comment by oriansj on IRC: I am thinking in terms of gentoo builds and making it easy to avoid some packages from being downloaded or built like pulseaudio (I like alsa better) and trim down the dependencies to only those that are absolutely directly required. made me wonder how we could incorporate such a feature into Guix. # Why it's Needed Sure, it's already possible to create package variants: (define-public foo (package (name "foo") (version "1.2.3") (source (origin ...)) (build-system gnu-build-system) (arguments (list #:configure-flags #~(list "--enable-pulseaudio"))) (inputs (list pulseaudio ...)) ...)) (define-public foo/without-pulseaudio (package (inherit foo) (name "foo-without-pulseaudio") (arguments (substitute-keyword-arguments (package-arguments foo) ((#:configure-flags old-flags) #~(list #$@flags "--disable-pulseaudio")))) (inputs (modify-inputs (package-inputs foo) (delete pulseaudio))))) This is currently manageable, but what if there's another optional feature, say, pipewire support? (define-public foo/without-pipewire (package (inherit foo) (name "foo-without-pulseaudio") (arguments (substitute-keyword-arguments (package-arguments foo) ((#:configure-flags old-flags) #~(list #$@flags "--disable-pipewire")))) (inputs (modify-inputs (package-inputs foo) (delete pipewire))))) And it's entirely reasonable to want to disable both and just use Ye Olde ALSA... (define-public foo/without-pipewire-or-pulse (package (inherit foo/without-pipewire) (name "foo-without-pulseaudio-or-pulse") (arguments (substitute-keyword-arguments (package-arguments foo/without-pipewire) ((#:configure-flags old-flags) #~(list #$@flags "--disable-pulseaudio")))) (inputs (modify-inputs (package-inputs foo/without-pipewire) (delete pulseaudio))))) We only have two features, pulseaudio and pipewire, and it's already getting A Wee Bit Silly. Even worse if we have 3 features: /without-pipewire /without-pulse /without-jack /without-pipewire-or-pulse /without-pipewire-or-jack /without-pulse-or-jack /without-pipewire-pulse-or-jack And now there are seven variants, and eight ``foo'' packages in total. We can do better, surely? Here's my proposal: a new ``features'' field for ``package'' that accepts a list of records like this: (define-public foo (package ... (features (list (feature (name "jack") (default? #f) (description "JACK audio backend")) (feature (name "pipewire") (default? #t) (description "Pipewire audio backend")) (feature (name "pulseaudio") (default? #t) (description "PulseAudio audio backend")))) ...)) And then we'd simply have a ``feature?'' (or some other name) procedure that we can use in a package definition. (define-public foo (package ... (arguments (list #:configure-flags #~(list (if #$(feature? "jack") "--enable-jack" "--disable-jack") (if #$(feature? "pipewire") "--enable-pipewire" "--disable-pipewire") (if #$(feature? "pulseaudio") "--enable-pulseaudio" "--disable-pulseaudio")))) (inputs (append (list alsa-lib) (if (feature? "jack") (list jack) '()) (if (feature? "pipewire") (list pipewire) '()) (if (feature? "pulseaudio") (list pulseaudio) '()))) ...)) # Features and the CLI When you ``guix show'' this package, it would display something like this: name: foo version: 0.2.7 outputs: + out: everything features: + jack (disabled): JACK audio backend + pipewire (enabled): Pipewire audio backend + pulseaudio (enabled): PulseAudio audio backend systems: x86_64-linux dependencies: alsa-lib@... jack[jack]@... + pipewire[pipewire]@... pulseaudio[pulseaudio]@... location: ... homepage: ... license: ... synopsis: ... description: ... Note those square brackets; this is how we specify features in package specifications, as they are treated as normal characters in POSIX-like shells and Fish (probably most others, too). This installs foo without the pulseaudio feature. guix install foo[!pulseaudio] This installs foo with the jack feature and without the pipewire feature. guix install foo[jack,!pipewire] This installs foo with all optional features disabled. guix install foo[!all] And this installs foo with all optional features enabled. guix install foo[all] (The all feature is special, much like out is with outputs.) Now, what if we have bar, which depends on foo, and we want to disable pulseaudio for bar's foo dependency? guix install bar[foo[!pulseaudio]] Or we want to disable it for all foos in the transitive dependencies of bar? guix install bar[@foo[!pulseaudio]] # Feature APIs Obviously there's FEATURE? and the FEATURE record-type operators: (feature ...) (feature-name feature) (feature-default? feature) (feature-description feature) (feature? feature-name) There's also THIS-PACKAGE-FEATURE, which returns the feature record for a given name string: (this-package-feature feature-name) There's PACKAGE-WITH-FEATURES: (package-with-features FEATURES PACKAGE) And PACKAGE-INPUT-FEATURE-REWRITING: (package-input-feature-rewriting REPLACEMENTS) For example: ((package-input-feature-rewriting `((,foo "!pipewire" "jack"))) bar) Maybe we could also have a macro for PACKAGE-INPUT-FEATURE-REWRITING (and another for PACKAGE-INPUT-REWRITING, for orthogonality)? (with-package-features baz (foo =3D> "!pulseaudio" "jack") (bar =3D> "vulkan")) (with-package-replacements (list aparte irssi) (openssl =3D> libressl)) SPECIFICATION->PACKAGE and SPECIFICATION->PACKAGE+OUTPUT would have #:SPECIFY-FEATURES? flags: (specification->package "foo[!pipewire]" #:specify-features? #t) # Unanswered Questions - Should CI try to build at least some non-default feature permutations? (Probably not.) - Might there be a better syntax for features in package specs? - Is this actually a good idea? :P - Anything else...? -- (