From 353d9d10dadeec69341eb8642722d76c17129b19 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Fri, 19 Jan 2024 13:52:13 +0100 Subject: [PATCH 1/4] ioc/common: config.epnix.outputs is an attrsOf package epnix.outputs is used in downstream's flake's `packages` output, which can only contain packages, so it's important to not break people's CI --- ioc/modules/common.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ioc/modules/common.nix b/ioc/modules/common.nix index a863e2a3..d98303da 100644 --- a/ioc/modules/common.nix +++ b/ioc/modules/common.nix @@ -27,7 +27,7 @@ with lib; { ~ The actual build of this distribution. ''; default = {}; - type = types.attrs; + type = with types; attrsOf package; }; epnix.pkgs = mkOption { From e7679dcf2b5548e8691a3a80eab0a6e70ed45478 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Fri, 19 Jan 2024 13:55:20 +0100 Subject: [PATCH 2/4] lib: refactor evaluation, make a function pkgs -> eval this moves the evaluation of the EPNix configuration into its own file this adds a new `epnixFunEval` which is a function that takes `pkgs` as an argument, and evals the EPNix config with this specific package set this function will be used so that an EPNix support module can be used by another EPNix top. It will reuse the same dependencies (the same epics-base, the same support modules, the same system libraries), and will reuse the same nixpkgs configuration (for example cross-compilation configuration). --- lib/default.nix | 62 +----------------------------- lib/evaluation.nix | 95 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 60 deletions(-) create mode 100644 lib/evaluation.nix diff --git a/lib/default.nix b/lib/default.nix index 1edfd521..38ed4520 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -7,71 +7,13 @@ with lib; let self = { documentation = import ./documentation.nix args; + evaluation = import ./evaluation.nix args; formats = import ./formats.nix args; licenses = import ./licenses.nix args; maintainers = import ./maintainers/maintainer-list.nix; testing = import ./testing.nix; - evalEpnixModules = { - nixpkgsConfig, - epnixConfig, - }: let - nixpkgsConfigWithDefaults = - { - crossSystem = null; - config = {}; - } - // nixpkgsConfig; - eval = evalModules { - modules = [ - ({config, ...}: { - config._module.args = let - # Configure the available packages with e.g. cross compilation - # and overlays - finalPkgs = import inputs.nixpkgs { - inherit (nixpkgsConfigWithDefaults) system crossSystem config; - inherit (config.nixpkgs) overlays; - }; - in { - # See: https://github.com/NixOS/nixpkgs/pull/190358 - pkgs = finalPkgs.__splicedPackages; - - # Used when we want to apply the same config in checks - inherit epnixConfig; - }; - }) - - epnixConfig - inputs.self.nixosModules.ioc - - # nixpkgs and assertions are separate, in case we want to include - # this module in a NixOS configuration, where `nixpkgs` and - # `assertions` options are already defined - ../ioc/modules/nixpkgs.nix - ../ioc/modules/assertions.nix - ]; - }; - - # From Robotnix - # From nixpkgs/nixos/modules/system/activation/top-level.nix - failedAssertions = map (x: x.message) (lib.filter (x: !x.assertion) eval.config.assertions); - - config = - if failedAssertions != [] - then throw "\nFailed assertions:\n${lib.concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}" - else lib.showWarnings eval.config.warnings eval.config; - in { - inherit (eval) options; - inherit config; - - inherit (config.epnix) outputs; - }; - - mkEpnixBuild = cfg: - (self.evalEpnixModules cfg).config.epnix.outputs.build; - - mkEpnixDevShell = cfg: - (self.evalEpnixModules cfg).config.epnix.outputs.devShell; + inherit (self.evaluation) evalEpnixModules mkEpnixBuild mkEpnixDevShell; # Like lib.getName, but also supports paths getName = thing: diff --git a/lib/evaluation.nix b/lib/evaluation.nix new file mode 100644 index 00000000..8e9cc6dd --- /dev/null +++ b/lib/evaluation.nix @@ -0,0 +1,95 @@ +{ + inputs, + lib, + ... +}: let + evalEpnixModules' = { + epnixConfig, + epnixFunEval, + pkgs, + }: let + eval = lib.evalModules { + modules = [ + ({config, ...}: { + config._module.args = { + pkgs = pkgs config; + + # Used when we want to apply the same config in checks + inherit epnixConfig; + inherit epnixFunEval; + }; + }) + + epnixConfig + inputs.self.nixosModules.ioc + + # nixpkgs and assertions are separate, in case we want to include + # this module in a NixOS configuration, where `nixpkgs` and + # `assertions` options are already defined + ../ioc/modules/nixpkgs.nix + ../ioc/modules/assertions.nix + ]; + }; + + # From Robotnix + # From nixpkgs/nixos/modules/system/activation/top-level.nix + failedAssertions = map (x: x.message) (lib.filter (x: !x.assertion) eval.config.assertions); + + config = + if failedAssertions != [] + then throw "\nFailed assertions:\n${lib.concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}" + else lib.showWarnings eval.config.warnings eval.config; + in { + inherit (eval) options; + inherit config; + + inherit (config.epnix) outputs generatedOverlay; + }; + + self = { + evalEpnixModules = { + nixpkgsConfig, + epnixConfig, + }: let + nixpkgsConfigWithDefaults = + { + crossSystem = null; + config = {}; + } + // nixpkgsConfig; + + pkgs = config: + (import inputs.nixpkgs { + inherit (nixpkgsConfigWithDefaults) system crossSystem config; + inherit (config.nixpkgs) overlays; + }) + # See: https://github.com/NixOS/nixpkgs/pull/190358 + .__splicedPackages; + + # As a function, + # so that we can import the package without fixing the dependencies. + # + # This is needed because, + # if this package is an EPICS support module, + # it needs to *not* depend on a specific version of epics-base. + # + # It needs to use the same version of epics-base + # that is going to be used by the final IOC. + epnixFunEval = pkgs: + evalEpnixModules' { + inherit epnixConfig epnixFunEval; + pkgs = config: pkgs; + }; + + fixedEval = evalEpnixModules' {inherit epnixConfig epnixFunEval pkgs;}; + in + fixedEval; + + mkEpnixBuild = cfg: + (self.evalEpnixModules cfg).config.epnix.outputs.build; + + mkEpnixDevShell = cfg: + (self.evalEpnixModules cfg).config.epnix.outputs.devShell; + }; +in + self From ae921da1422b0ff50bb518957f689d1801668561 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Fri, 19 Jan 2024 14:07:31 +0100 Subject: [PATCH 3/4] ioc/outputs: generate an overlay containing the top package this generates an nixpkgs overlay[1], with the current top as a package inside `epnix.support` [1]: https://nixos.org/manual/nixpkgs/stable/#chap-overlays this means that the top can be exported as a support module, and dependants top can import that support module by importing the overlay, and adding the package in the `epnix.support.modules` option --- ioc/modules/outputs.nix | 42 +++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/ioc/modules/outputs.nix b/ioc/modules/outputs.nix index 34865ca6..41f56887 100644 --- a/ioc/modules/outputs.nix +++ b/ioc/modules/outputs.nix @@ -3,6 +3,7 @@ lib, pkgs, epnix, + epnixFunEval, ... }: with lib; let @@ -19,25 +20,42 @@ with lib; let toUpper ]; in { - options.epnix.buildConfig = { - attrs = mkOption { - description = "Extra attributes to pass to the derivation"; - type = types.attrs; - default = {}; - }; + options.epnix = { + buildConfig = { + attrs = mkOption { + description = "Extra attributes to pass to the derivation"; + type = types.attrs; + default = {}; + }; + + src = mkOption { + description = '' + The source code for the top. - src = mkOption { - description = '' - The source code for the top. + Defaults to the directory containing the `flake.nix` file. + ''; + type = types.path; + }; + }; - Defaults to the directory containing the `flake.nix` file. - ''; - type = types.path; + generatedOverlay = mkOption { + description = "A generated overlay which has your package inside `pkgs.epnix.support`."; + type = with types; functionTo (functionTo attrs); }; }; config.epnix.buildConfig.src = mkDefault config.epnix.inputs.self; + config.epnix.generatedOverlay = final: prev: let + newEval = final.callPackage epnixFunEval final; + in { + epnix = prev.epnix.extend (_final: prev: { + support = prev.support.extend (_final: _prev: { + "${config.epnix.meta.name}" = newEval.config.epnix.outputs.build; + }); + }); + }; + config.epnix.outputs.build = pkgs.mkEpicsPackage ({ pname = "epnix-${config.epnix.meta.name}"; inherit (config.epnix.meta) version; From 7a37fb50f40e0aeb099b55740d93c476889b18c9 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Fri, 19 Jan 2024 14:10:45 +0100 Subject: [PATCH 4/4] templates/top: show how to export/import EPNix support modules importing an EPNix support modules by: - adding the repository as a flake input - importing its overlay - adding the package as a dependency in `epnix.support.modules` exporting an EPNix support modules by: - exporting the generated overlay --- templates/top/flake.nix | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/templates/top/flake.nix b/templates/top/flake.nix index a1cb68d4..3be88393 100644 --- a/templates/top/flake.nix +++ b/templates/top/flake.nix @@ -4,6 +4,14 @@ inputs.flake-utils.url = "github:numtide/flake-utils"; inputs.epnix.url = "github:epics-extensions/epnix"; + # If you have a support module as a separate EPNix repository, + # add it as an input here: + # --- + #inputs.mySupportModule = { + # url = "git+ssh://git@my-server.org/me/exampleApp.git"; + # inputs.epnix.follows = "epnix"; + #}; + # If you have an "App" as a separate repository, # add it as an input here: # --- @@ -18,10 +26,15 @@ epnix, ... } @ inputs: let - myEpnixDistribution = {pkgs, ...}: { + myEpnixConfig = {pkgs, ...}: { # Set your EPNix options here # --- + # If you have a support module as a separate EPNix repository, + # uncomment this line to make the package available: + # --- + #overlays = [inputs.mySupportModule.overlays.default]; + epnix = { inherit inputs; @@ -33,9 +46,9 @@ # --- #epics-base.releaseBranch = "3"; # Defaults to "7" - # Add one of the supported modules here: + # Add your support modules here: # --- - #support.modules = with pkgs.epnix.support; [ StreamDevice ]; + #support.modules = with pkgs.epnix.support; [ StreamDevice mySupportModule ]; # If you have an "App" as a separate repository, # add it here: @@ -69,7 +82,7 @@ # environment can be built on your machine. flake-utils.lib.eachSystem ["x86_64-linux"] (system: with epnix.lib; let - result = evalEpnixModules { + epnixDistribution = evalEpnixModules { nixpkgsConfig = { # This specifies the build architecture inherit system; @@ -81,17 +94,23 @@ # --- #crossSystem = epnix.inputs.nixpkgs.lib.systems.examples.armv7l-hf-multiplatform; }; - epnixConfig = myEpnixDistribution; + epnixConfig = myEpnixConfig; }; in { packages = - result.outputs + epnixDistribution.outputs // { default = self.packages.${system}.build; }; + inherit epnixDistribution; + devShells.default = self.packages.${system}.devShell; - checks = result.config.epnix.checks.derivations; - }); + checks = epnixDistribution.config.epnix.checks.derivations; + }) + // { + overlays.default = final: prev: + self.epnixDistribution.x86_64-linux.generatedOverlay final prev; + }; }