跳到正文
cccskills
免费在 GitHub 公开

mk-many-variants

Work with multi-version packages under pkgs-many (guile, go, erlang, perl, openssl, ...). Use when adding or changing a package that ships several versions side by side — covers the default.nix/variants.nix/generic.nix three-file pattern, the packageOlder/packageAtLeast version helpers, and mkVariantPassthru.

中文简介正在准备中。当前显示原文说明。

ekala-project/corepkgs星标 94(GitHub 94 + cccskills 0)

实测评分 未实测

查看安装方法

包含的文件(1)

  • SKILL.md9.5 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

mkManyVariants - Multi-Version Package Management

This skill helps you work with the mkManyVariants pattern used in this codebase for managing packages that need to support multiple versions (e.g., guile, go, erlang, perl, openssl).

Key Differences from Normal Nix Packages

Unlike a standard Nix package (like pkgs/xxHash/default.nix) which is a single default.nix file, mkManyVariants packages are split into three separate files:

Normal Package Structure (e.g., pkgs/xxHash/)

pkgs/xxHash/
└── default.nix          # Single file with version, src, buildInputs, etc.

mkManyVariants Package Structure (e.g., pkgs-many/isl/)

pkgs-many/isl/
├── default.nix          # Calls mkManyVariants with configuration
├── generic.nix          # Generic builder that works for all versions
├── variants.nix         # Defines each version's specific data
└── patches/             # Optional: version-specific patches
    ├── 0.20/
    └── 0.23/

The Three-File Pattern

1. default.nix - Configuration Entry Point

This file calls mkManyVariants with configuration:

{ mkManyVariants, callPackage }:

mkManyVariants {
  variants = ./variants.nix;           # Where version data lives
  aliases = { };                       # Deprecated/aliased versions
  name = "isl";                        # Package name for eol/removed messages
  eol = { };                           # EOL variants (warn + still build)
  removed = { };                       # Removed variants (throw on access)
  defaultSelector = (p: p.v0_20);     # Which version is the default
  genericBuilder = ./generic.nix;      # Builder that handles all versions
  inherit callPackage;
}

Differences from normal package:

  • No stdenv.mkDerivation call
  • No version/src/buildInputs specified here
  • Instead, configures mkManyVariants framework

2. variants.nix - Version Definitions

This file contains an attribute set where each attribute represents a version:

{
  v0_20 = rec {
    version = "0.20";
    src-url = [
      "https://libisl.sourceforge.io/isl-${version}.tar.xz"
    ];
    src-hash = "sha256-pVlqn7ils2XLYS5LlihzXW5n6RePrhNKgWrhlQF+d6o=";
    configureFlags = [
      "--with-gcc-arch=generic"
    ];
  };

  v0_23 = rec {
    version = "0.23";
    src-url = [
      "https://libisl.sourceforge.io/isl-${version}.tar.xz"
    ];
    src-hash = "sha256-XvxT767xUTAfTn3eOFa2aBLYFT3t4k+rF2c/gByGmPI=";
    configureFlags = [
      "--with-gcc-arch=generic"
    ];
  };
}

Key points:

  • Each version is a separate attribute (typically named v<major>_<minor>)
  • Use rec to allow self-referencing (e.g., version in src-url)
  • Common fields: version, src-url, src-hash
  • Version-specific fields: configureFlags, patches, cmakeFlags, etc.
  • Variant inheritance: Non-default variants inherit all attributes from the default variant (selected by defaultSelector). Variants only need to specify attributes that differ — there is no need to repeat version, src-url, src-hash, etc. when only changing build flags or feature toggles.

Feature-toggle variants (same version, different build options):

{
  default = {
    version = "3.27.1";
    src-hash = "sha256-XViRUuuAccAv6rjOarcZ5DGh+8PisXAPVDJjKouSZNw=";
    withGdb = true;
  };

  # Inherits version and src-hash from default, only overrides withGdb.
  light = {
    withGdb = false;
  };
}

3. generic.nix - Generic Builder with Conditional Logic

This file is a two-stage function that receives variant args first, then package args:

{
  version,
  src-url,
  src-hash,
  configureFlags ? [ ],
  patches ? [ ],
  packageAtLeast,     # Helper: version >= X
  packageOlder,       # Helper: version < X
  ...
}@variantArgs:

{
  lib,
  stdenv,
  fetchurl,
  gmp,
  buildPackages,
  ...
}@args:

stdenv.mkDerivation {
  pname = "isl";
  inherit version;

  src = fetchurl {
    url = src-url;
    hash = src-hash;
  };

  inherit patches;

  # Version-conditional logic using helper functions
  strictDeps = true;
  depsBuildBuild = lib.optionals (packageAtLeast "0.23") [
    buildPackages.stdenv.cc
  ];

  nativeBuildInputs =
    lib.optionals (stdenv.hostPlatform.isRiscV && packageOlder "0.23") [
      autoreconfHook
    ] ++ [
      updateAutotoolsGnuConfigScriptsHook
    ];

  inherit configureFlags;

  meta = { ... };
}

Key differences from normal package:

  • Two parameter sets: First variantArgs, then package dependencies
  • Helper functions available: packageAtLeast, packageOlder, packageBetween
  • Conditional logic: Use helpers to vary dependencies/patches by version
  • Generic src handling: Takes src-url and src-hash from variants

Version Helper Functions

These functions are automatically injected into variantArgs:

packageOlder

packageOlder "2.0"  # Returns true if version < 2.0

packageAtLeast

packageAtLeast "2.0"  # Returns true if version >= 2.0

packageBetween

packageBetween "2.0" "3.0"  # Returns true if 2.0 <= version < 3.0

Real-world examples from guile:

# Different source extensions by version
srcExtension = if packageOlder "2.0" then "tar.gz" else "tar.xz";

# Version ranges
patches =
  lib.optionals (packageOlder "2.0") [
    ./patches/1.8/...
  ]
  ++ lib.optionals (packageAtLeast "2.0" && packageOlder "2.2") [
    ./patches/2.0/...
  ]
  ++ lib.optionals (packageAtLeast "2.2") [
    ./patches/2.2/...
  ];

# Platform-specific conditionals with version
configureFlags = lib.optional (packageAtLeast "3.0" && stdenv.hostPlatform.isDarwin)
  "--disable-lto";

Common Patterns

Pattern 1: Version-Specific Patches

patches = [ ]
  ++ lib.optionals (packageOlder "2.0") [
    ./patches/1.x/fix-something.patch
  ]
  ++ lib.optionals (packageAtLeast "2.0") [
    ./patches/2.x/new-fix.patch
  ];

Pattern 2: Conditional Dependencies

buildInputs = [
  alwaysNeeded
] ++ lib.optionals (packageAtLeast "3.0") [
  onlyNeededInV3
];

Pattern 3: Setup Hooks by Version

setupHook =
  if packageOlder "2.0" then
    ./setup-hooks/v1-hook.sh
  else if packageOlder "3.0" then
    ./setup-hooks/v2-hook.sh
  else
    ./setup-hooks/v3-hook.sh;

Pattern 4: Using mkVariantPassthru (Advanced)

# In generic.nix, add to passthru to enable variant composition
passthru = mkVariantPassthru variantArgs // {
  # other passthru attrs
};

This allows accessing other variants from a built package: isl.variants.v0_27

EOL and Removed Variants

When a version reaches end-of-life or is dropped from variants.nix, use the eol and removed parameters to provide clear messages instead of silent breakage.

Both require name to be set.

eol — Warn but still build

For variants that are still in variants.nix but have reached end-of-life upstream. Accessing them emits a lib.warn and still returns the built package.

mkManyVariants {
  variants = ./variants.nix;
  name = "nodejs";
  eol = {
    v18 = "2025-04-30";    # Node.js 18 EOL date
    v20 = "2026-04-30";
  };
  # ...
}

Evaluating nodejs.v18 prints:

evaluation warning: nodejs.v18 is EOL as of 2025-04-30. It is recommended to use a newer version.

removed — Throw on access

For variants that have been deleted from variants.nix. Accessing them throws an error. Only honoured when config.aliases.nixpkgs is true (the default), so CI evaluation with aliases.nixpkgs = false is unaffected.

mkManyVariants {
  variants = ./variants.nix;
  name = "linux";
  removed = {
    v6_17 = "2026-08-30";
  };
  # ...
}

Evaluating linux.v6_17 throws:

error: linux.v6_17 is no longer available and was removed on 2026-08-30.

Aliases in aliases/nixpkgs.nix should continue pointing to the variant (e.g. linuxPackages_6_17 = linux.v6_17.pkgs;) so the specific error is emitted instead of a generic "attribute missing".

When to Use mkManyVariants

Use mkManyVariants when:

  • Package needs multiple versions available simultaneously
  • Different versions have conditional dependencies or build logic
  • Versions share most of the build logic but differ in specifics
  • Package needs feature-toggle variants of the same version (e.g., valgrind vs valgrind.light, graphviz.withXorg vs graphviz.withoutXorg)
  • Want to provide variant composition (access to other versions)

Don't use for:

  • Single-version packages with no feature variants (use normal pkgs/ structure)
  • Completely different build processes per version (use separate packages)

Creating a New mkManyVariants Package

  1. Create directory in pkgs-many/<package>/
  2. Create variants.nix with your versions
  3. Create generic.nix with shared build logic and conditionals
  4. Create default.nix calling mkManyVariants
  5. Choose default version with defaultSelector
  6. Test building: nix-build -A <package> (default) and nix-build -A <package>.variants.v<version>

Common Gotchas

  • Don't forget rec in variants.nix if using ${version} in fields
  • Two-stage function in generic.nix: variantArgs first, then package args
  • Helper functions only available in first parameter set (variantArgs)
  • Default selector must be a function taking the variants attrset

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Review a package expression for best-practices compliance. Use when reviewing PRs or auditing existing packages — checks meta completeness, build-system idioms (cmakeEntries, mesonEntries, pyproject), dependency placement, and packaging conventions.

中文简介正在准备中。当前显示原文说明。

ekala-project/corepkgs942026年10月26日 更新

cmake

免费

Package a CMake-based project for corepkgs. Use when a source tree has a CMakeLists.txt — covers the minimal derivation, cmakeEntries, cross-compilation, and diagnosing CMake configure failures.

中文简介正在准备中。当前显示原文说明。

ekala-project/corepkgs942026年10月26日 更新

Fix all broken package builds in the repository. Runs a full build of ci/packages.nix in the background while iterating on failures in the foreground until everything builds.

中文简介正在准备中。当前显示原文说明。

ekala-project/corepkgs942026年10月26日 更新

go

免费

Package a Go module for corepkgs. Use when writing or editing a buildGoModule derivation — covers vendorHash, ldflags, subPackages, tags, env.CGO_ENABLED, proxyVendor, shell completions, and pinning a Go version.

中文简介正在准备中。当前显示原文说明。

ekala-project/corepkgs942026年10月26日 更新

meson

免费

Package a Meson/Ninja project for corepkgs. Use when a source tree has a meson.build — covers mesonFlags, auto features, cross-compilation, and diagnosing Meson setup failures.

中文简介正在准备中。当前显示原文说明。

ekala-project/corepkgs942026年10月26日 更新

packaging

免费

corepkgs packaging conventions. Use when writing or reviewing any pkgs/<name>/default.nix — covers package organization (auto-registration, top-level.nix, alternative implementations), the finalAttrs pattern, meta attributes, where each kind of dependency belongs, fetchers, patching, install phases, multiple outputs, and passthru.

中文简介正在准备中。当前显示原文说明。

ekala-project/corepkgs942026年10月26日 更新

查看 ekala-project 的全部技能

报告此技能的问题