diff --git a/flake.nix b/flake.nix index ddd12be..a8295dd 100644 --- a/flake.nix +++ b/flake.nix @@ -28,6 +28,7 @@ }: { _file = ./flake.nix; + imports = [ ./input-propagation-workaround.nix ]; options.perSystem = flake-parts-lib.mkPerSystemOption ( { pkgs, ... }: { @@ -46,24 +47,20 @@ A module to load into your nix-cargo-integration [`perSystem.nci.projects..depsDrvConfig`](https://flake.parts/options/nix-cargo-integration.html#opt-perSystem.nci.projects._name_.depsDrvConfig) or similar such options. - This provides common build configuration (pkg-config, libclang, etc.) but you must - add the specific Nix C libraries your crates need to `buildInputs`: - - `nix-bindings-util-sys` needs `nix-util-c` - - `nix-bindings-store-sys` needs `nix-store-c` - - `nix-bindings-expr-sys` needs `nix-expr-c` - - `nix-bindings-fetchers-sys` needs `nix-fetchers-c` (Nix >= 2.29) - - `nix-bindings-flake-sys` needs `nix-flake-c` - - `nix-bindings-bdwgc-sys` needs `boehmgc` + This provides common build configuration (pkg-config, libclang, etc.) and + automatically adds Nix C library build inputs based on which nix-bindings + crates are *direct* dependencies of your crate. + + To disable automatic build input detection: + ```nix + nix-bindings-rust.inputPropagationWorkaround.enable = false; + ``` Example: ```nix - perSystem = perSystem@{ config, pkgs, ... }: { - nci.projects."my_project".depsDrvConfig = { + perSystem = perSystem@{ config, ... }: { + nci.projects."my_project".drvConfig = { imports = [ perSystem.config.nix-bindings-rust.nciBuildConfig ]; - mkDerivation.buildInputs = [ - perSystem.config.nix-bindings-rust.nixPackage.libs.nix-store-c - # ... add other libs as needed - ]; }; } ``` diff --git a/input-propagation-workaround.nix b/input-propagation-workaround.nix new file mode 100644 index 0000000..c3ac92f --- /dev/null +++ b/input-propagation-workaround.nix @@ -0,0 +1,143 @@ +# Workaround for missing native input propagation in nix-cargo-integration +# +# Automatically adds Nix C library build inputs based on which nix-bindings +# crates are direct dependencies of the crate being built. The mapping is +# recursive, so depending on nix-bindings-flake will also bring in the +# transitive C library dependencies (nix-fetchers-c, nix-expr-c, etc.). +# +# Note: For multi-crate workspaces, if your crate A depends on your crate B +# which depends on nix-bindings, you'll need to add an A -> B mapping to +# `crateInputMapping` so that A also gets B's nix-bindings inputs. +{ + perSystem = + { + lib, + config, + pkgs, + ... + }: + let + cfg = config.nix-bindings-rust.inputPropagationWorkaround; + nixPackage = config.nix-bindings-rust.nixPackage; + + nixLibs = + if nixPackage ? libs then + nixPackage.libs + else + # Fallback for older Nix versions without split libs + { + nix-util-c = nixPackage; + nix-store-c = nixPackage; + nix-expr-c = nixPackage; + nix-fetchers-c = nixPackage; + nix-flake-c = nixPackage; + }; + + # A module for nciBuildConfig that sets buildInputs based on nix-bindings dependencies. + # Uses options inspection to detect drvConfig vs depsDrvConfig context. + workaroundModule = + { + lib, + config, + options, + ... + }: + let + # rust-cargo-lock exists in drvConfig but not depsDrvConfig + isDrvConfig = options ? rust-cargo-lock; + + dreamLock = config.rust-cargo-lock.dreamLock; + depsList = dreamLock.dependencies.${config.name}.${config.version} or [ ]; + + # Convert list of deps to attrset keyed by name for efficient lookup + deps = builtins.listToAttrs ( + map (dep: { + name = dep.name; + value = dep; + }) depsList + ); + + # Inputs for the crate itself if it's in the mapping + selfInputs = cfg.crateInputMapping.${config.name} or [ ]; + + # Inputs for direct dependencies that have mappings + depInputs = lib.concatLists (lib.attrValues (lib.intersectAttrs deps cfg.crateInputMapping)); + + allInputs = selfInputs ++ depInputs; + in + { + config = lib.optionalAttrs isDrvConfig { + mkDerivation.buildInputs = allInputs; + rust-crane.depsDrv.mkDerivation.buildInputs = allInputs; + }; + }; + in + { + options.nix-bindings-rust.inputPropagationWorkaround = { + enable = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + Whether to automatically add Nix C library build inputs based on + which nix-bindings crates are direct dependencies. + + Set to `false` to disable automatic detection and specify buildInputs manually. + ''; + }; + + crateInputMapping = lib.mkOption { + type = lib.types.lazyAttrsOf (lib.types.listOf lib.types.package); + description = '' + Mapping from crate names to build inputs. Entries can reference + other entries for transitive dependencies. + + The input propagation workaround can see direct dependencies, so + if you have `my-crate -> nix-bindings`, that works out of the box. + If you have `my-other-crate -> my-crate -> nix-bindings`, then you + need to specify `my-other-crate -> my-crate` as follows: + + ```nix + nix-bindings-rust.inputPropagationWorkaround.crateInputMapping."my-other-crate" = + config.nix-bindings-rust.inputPropagationWorkaround.crateInputMapping."my-crate"; + ``` + ''; + default = { }; + }; + }; + + config = lib.mkIf cfg.enable { + nix-bindings-rust.inputPropagationWorkaround.crateInputMapping = { + # -sys crates with their transitive dependencies + "nix-bindings-bdwgc-sys" = [ pkgs.boehmgc ]; + "nix-bindings-util-sys" = [ nixLibs.nix-util-c.dev ]; + "nix-bindings-store-sys" = [ + nixLibs.nix-store-c.dev + ] + ++ cfg.crateInputMapping."nix-bindings-util-sys"; + "nix-bindings-expr-sys" = [ + nixLibs.nix-expr-c.dev + ] + ++ cfg.crateInputMapping."nix-bindings-store-sys" + ++ cfg.crateInputMapping."nix-bindings-bdwgc-sys"; + "nix-bindings-fetchers-sys" = [ + nixLibs.nix-fetchers-c.dev + ] + ++ cfg.crateInputMapping."nix-bindings-expr-sys"; + "nix-bindings-flake-sys" = [ + nixLibs.nix-flake-c.dev + ] + ++ cfg.crateInputMapping."nix-bindings-fetchers-sys" + ++ cfg.crateInputMapping."nix-bindings-bdwgc-sys"; + # High-level crates reference their -sys counterparts + "nix-bindings-bdwgc" = cfg.crateInputMapping."nix-bindings-bdwgc-sys"; + "nix-bindings-util" = cfg.crateInputMapping."nix-bindings-util-sys"; + "nix-bindings-store" = cfg.crateInputMapping."nix-bindings-store-sys"; + "nix-bindings-expr" = cfg.crateInputMapping."nix-bindings-expr-sys"; + "nix-bindings-fetchers" = cfg.crateInputMapping."nix-bindings-fetchers-sys"; + "nix-bindings-flake" = cfg.crateInputMapping."nix-bindings-flake-sys"; + }; + + nix-bindings-rust.nciBuildConfig.imports = [ workaroundModule ]; + }; + }; +} diff --git a/nci.nix b/nci.nix index 9a468b6..62c523e 100644 --- a/nci.nix +++ b/nci.nix @@ -1,24 +1,8 @@ { perSystem = - { - config, - pkgs, - ... - }: + { config, ... }: let cfg = config.nix-bindings-rust; - nixLibs = - if cfg.nixPackage ? libs then - cfg.nixPackage.libs - else - # Fallback for older Nix versions without split libs - { - nix-util-c = cfg.nixPackage; - nix-store-c = cfg.nixPackage; - nix-expr-c = cfg.nixPackage; - nix-fetchers-c = cfg.nixPackage; - nix-flake-c = cfg.nixPackage; - }; in { # https://flake.parts/options/nix-cargo-integration @@ -31,7 +15,7 @@ drvConfig = { imports = [ # Downstream projects import this into depsDrvConfig instead - config.nix-bindings-rust.nciBuildConfig + cfg.nciBuildConfig ]; # Extra settings for running the tests mkDerivation = { @@ -65,39 +49,5 @@ }; }; }; - - # Per-crate configuration: only provide the specific Nix libs each crate needs - # FIXME should use propagatedBuildInputs - nci.crates.nix-bindings-bdwgc-sys.drvConfig.mkDerivation.buildInputs = [ - pkgs.boehmgc - ]; - nci.crates.nix-bindings-util-sys.drvConfig.mkDerivation.buildInputs = [ - nixLibs.nix-util-c - ]; - nci.crates.nix-bindings-util.drvConfig.mkDerivation.buildInputs = - config.nci.crates.nix-bindings-util-sys.drvConfig.mkDerivation.buildInputs; - nci.crates.nix-bindings-store-sys.drvConfig.mkDerivation.buildInputs = - config.nci.crates.nix-bindings-util-sys.drvConfig.mkDerivation.buildInputs - ++ [ nixLibs.nix-store-c ]; - nci.crates.nix-bindings-store.drvConfig.mkDerivation.buildInputs = - config.nci.crates.nix-bindings-store-sys.drvConfig.mkDerivation.buildInputs; - nci.crates.nix-bindings-expr-sys.drvConfig.mkDerivation.buildInputs = - config.nci.crates.nix-bindings-store-sys.drvConfig.mkDerivation.buildInputs - ++ [ - nixLibs.nix-expr-c - pkgs.boehmgc - ]; - nci.crates.nix-bindings-expr.drvConfig.mkDerivation.buildInputs = - config.nci.crates.nix-bindings-expr-sys.drvConfig.mkDerivation.buildInputs; - nci.crates.nix-bindings-fetchers-sys.drvConfig.mkDerivation.buildInputs = - config.nci.crates.nix-bindings-expr-sys.drvConfig.mkDerivation.buildInputs - ++ [ nixLibs.nix-fetchers-c ]; - nci.crates.nix-bindings-fetchers.drvConfig.mkDerivation.buildInputs = - config.nci.crates.nix-bindings-fetchers-sys.drvConfig.mkDerivation.buildInputs; - nci.crates.nix-bindings-flake-sys.drvConfig.mkDerivation.buildInputs = - config.nci.crates.nix-bindings-fetchers-sys.drvConfig.mkDerivation.buildInputs - ++ [ nixLibs.nix-flake-c ]; - nci.crates.nix-bindings-flake.drvConfig.mkDerivation.buildInputs = - config.nci.crates.nix-bindings-flake-sys.drvConfig.mkDerivation.buildInputs; }; }