From 087609f83faa6dccfd19ae8c3ec3fa7b5a7425f3 Mon Sep 17 00:00:00 2001 From: Andrea Bedini Date: Fri, 7 Jul 2023 15:49:54 +0800 Subject: [PATCH 01/17] Move custom types to haskellLib.types Move listOfFilteringNulls, getDefaultOrNull and uniqueStr to haskellLib.types --- lib/default.nix | 2 ++ lib/types.nix | 47 +++++++++++++++++++++++++++++++++++++++++++++ modules/package.nix | 4 +++- modules/plan.nix | 36 ++-------------------------------- 4 files changed, 54 insertions(+), 35 deletions(-) create mode 100644 lib/types.nix diff --git a/lib/default.nix b/lib/default.nix index 4dc1358917..ae0b23ce75 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -591,4 +591,6 @@ in { __toJSON (__attrNames (lib.filterAttrs (_: v: __length v > 1) ( builtins.groupBy (x: if __typeOf x == "set" then x.name or "noname" else "notset") x))) }"; + + types = import ./types.nix { inherit lib; }; } diff --git a/lib/types.nix b/lib/types.nix new file mode 100644 index 0000000000..5d030182bd --- /dev/null +++ b/lib/types.nix @@ -0,0 +1,47 @@ +{ lib }: + +with lib; + +rec { + # This is just like listOf, except that it filters out all null elements. + listOfFilteringNulls = elemType: types.listOf elemType // { + # Mostly copied from nixpkgs/lib/types.nix + merge = loc: defs: + map (x: x.value) (filter (x: x ? value && x.value != null) (concatLists (imap1 + (n: def: + if isList def.value then + imap1 + (m: def': + (mergeDefinitions + (loc ++ [ "[definition ${toString n}-entry ${toString m}]" ]) + elemType + [{ inherit (def) file; value = def'; }] + ).optionalValue + ) + def.value + else + throw "The option value `${showOption loc}` in `${def.file}` is not a list.") + defs))); + }; + + # dealing with str is a bit annoying especially with `nullOr str` as that apparently defaults to "" + # instead of null :shrug:. This then messes with our option inheritance logic. + # Hence we have a uniqueStr type that ensures multiple identically defined options are collapsed + # without raising an error. And a way to fetch default options that will retain `null` if the + # option is not defined or "". + getDefaultOrNull = def: key: if def ? ${key} && def.${key} != "" then def.${key} else null; + + mergeUniqueOption = locs: defs: + let + mergeOneOption = loc: defs': + # we ignore "" as optionalString, will default to "". + let defs = filter (x: x.value != "") defs'; in + if defs == [ ] then null + else if length defs != 1 then + throw "The unique option `${showOption loc}' is defined multiple times, in ${showFiles (getFiles defs)}; with values `${concatStringsSep "', `" (map (x: x.value) defs)}'." + else (head defs).value; + in + mergeOneOption locs (lists.unique defs); + + uniqueStr = types.str // { merge = mergeUniqueOption; }; +} diff --git a/modules/package.nix b/modules/package.nix index 01820bf5d8..5df4baab43 100644 --- a/modules/package.nix +++ b/modules/package.nix @@ -13,7 +13,7 @@ # tests = { "..." = { depends = ... }; ... }; # }; -{ parentConfig, mod_args, listOfFilteringNulls, componentOptions, packageOptions }: +{ parentConfig, mod_args, componentOptions, packageOptions }: { lib, config, pkgs, haskellLib, ... }: with lib; @@ -22,6 +22,8 @@ with types; # Work around issue that can cause _lots_ of files to be copied into the store. # See https://github.com/NixOS/nixpkgs/pull/64691 let + inherit (haskellLib.types) listOfFilteringNulls; + path = types.path // { check = x: types.path.check (x.origSrc or x); }; componentType = submodule { diff --git a/modules/plan.nix b/modules/plan.nix index 9dcfd1d436..fe1255d983 100644 --- a/modules/plan.nix +++ b/modules/plan.nix @@ -16,39 +16,7 @@ with lib; with types; let - # dealing with str is a bit annoying especially with `nullOr str` as that apparently defaults to "" - # instead of null :shrug:. This then messes with our option inheritance logic. - # Hence we have a uniqueStr type that ensures multiple identically defined options are collapsed - # without raising an error. And a way to fetch default options that will retain `null` if the - # option is not defined or "". - getDefaultOrNull = def: key: if def ? ${key} && def.${key} != "" then def.${key} else null; - mergeUniqueOption = locs: defs: let - mergeOneOption = loc: defs': - # we ignore "" as optionalString, will default to "". - let defs = filter (x: x.value != "") defs'; in - if defs == [] then null - else if length defs != 1 then - throw "The unique option `${showOption loc}' is defined multiple times, in ${showFiles (getFiles defs)}; with values `${concatStringsSep "', `" (map (x: x.value) defs)}'." - else (head defs).value; - in mergeOneOption locs (lists.unique defs); - uniqueStr = str // { merge = mergeUniqueOption; }; - - # This is just like listOf, except that it filters out all null elements. - listOfFilteringNulls = elemType: listOf elemType // { - # Mostly copied from nixpkgs/lib/types.nix - merge = loc: defs: - map (x: x.value) (filter (x: x ? value && x.value != null) (concatLists (imap1 (n: def: - if isList def.value then - imap1 (m: def': - (mergeDefinitions - (loc ++ ["[definition ${toString n}-entry ${toString m}]"]) - elemType - [{ inherit (def) file; value = def'; }] - ).optionalValue - ) def.value - else - throw "The option value `${showOption loc}` in `${def.file}` is not a list.") defs))); - }; + inherit (haskellLib.types) getDefaultOrNull listOfFilteringNulls uniqueStr; componentOptions = def: { buildable = mkOption { @@ -296,7 +264,7 @@ in { inherit (config.cabal) system compiler; }; in attrsOf (submodule (import ./package.nix { - inherit mod_args listOfFilteringNulls; + inherit mod_args; inherit componentOptions packageOptions; parentConfig = config; })); From 0b4aa370b2993fb7f8e3d420776f31c5593d4b3f Mon Sep 17 00:00:00 2001 From: Andrea Bedini Date: Fri, 7 Jul 2023 15:50:57 +0800 Subject: [PATCH 02/17] Lint --- modules/package.nix | 2 +- modules/plan.nix | 60 ++++++++++++++++++++++----------------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/modules/package.nix b/modules/package.nix index 5df4baab43..e75ecd160b 100644 --- a/modules/package.nix +++ b/modules/package.nix @@ -124,7 +124,7 @@ let in { # This is how the Nix expressions generated by *-to-nix receive # their flags argument. - config._module.args = mod_args // { flags = config.flags; }; + config._module.args = mod_args // { inherit (config) flags; }; options = (packageOptions parentConfig) // { # TODO: Add descriptions to everything. diff --git a/modules/plan.nix b/modules/plan.nix index fe1255d983..de077fa357 100644 --- a/modules/plan.nix +++ b/modules/plan.nix @@ -25,86 +25,86 @@ let }; configureFlags = mkOption { type = listOfFilteringNulls str; - default = (def.configureFlags or []); + default = def.configureFlags or []; }; setupBuildFlags = mkOption { type = listOfFilteringNulls str; - default = (def.setupBuildFlags or []); + default = def.setupBuildFlags or []; }; testFlags = mkOption { type = listOfFilteringNulls str; - default = (def.testFlags or []); + default = def.testFlags or []; }; setupInstallFlags = mkOption { type = listOfFilteringNulls str; - default = (def.setupInstallFlags or []); + default = def.setupInstallFlags or []; }; setupHaddockFlags = mkOption { type = listOfFilteringNulls str; - default = (def.setupHaddockFlags or []); + default = def.setupHaddockFlags or []; }; doExactConfig = mkOption { type = bool; - default = (def.doExactConfig or false); + default = def.doExactConfig or false; }; doCheck = mkOption { type = bool; - default = (def.doCheck or true); + default = def.doCheck or true; }; doCrossCheck = mkOption { description = "Run doCheck also in cross compilation settings. This can be tricky as the test logic must know how to run the tests on the target."; type = bool; - default = (def.doCrossCheck or false); + default = def.doCrossCheck or false; }; doHaddock = mkOption { description = "Enable building of the Haddock documentation from the annotated Haskell source code."; type = bool; - default = (def.doHaddock or true); + default = def.doHaddock or true; }; doHoogle = mkOption { description = "Also build a hoogle index."; type = bool; - default = (def.doHoogle or true); + default = def.doHoogle or true; }; doHyperlinkSource = mkOption { description = "Link documentation to the source code."; type = bool; - default = (def.doHyperlinkSource or true); + default = def.doHyperlinkSource or true; }; doQuickjump = mkOption { description = "Generate an index for interactive documentation navigation."; type = bool; - default = (def.doQuickjump or true); + default = def.doQuickjump or true; }; doCoverage = mkOption { description = "Enable production of test coverage reports."; type = bool; - default = (def.doCoverage or false); + default = def.doCoverage or false; }; dontPatchELF = mkOption { description = "If set, the patchelf command is not used to remove unnecessary RPATH entries. Only applies to Linux."; type = bool; - default = (def.dontPatchELF or true); + default = def.dontPatchELF or true; }; dontStrip = mkOption { description = "If set, libraries and executables are not stripped."; type = bool; - default = (def.dontStrip or true); + default = def.dontStrip or true; }; enableDeadCodeElimination = mkOption { description = "If set, enables split sections for link-time dead-code stripping. Only applies to Linux"; type = bool; - default = (def.enableDeadCodeElimination or true); + default = def.enableDeadCodeElimination or true; }; enableStatic = mkOption { description = "If set, enables building static libraries and executables."; type = bool; - default = (def.enableStatic or true); + default = def.enableStatic or true; }; enableShared = mkOption { description = "If set, enables building shared libraries."; type = bool; - default = (def.enableShared or true); + default = def.enableShared or true; }; configureAllComponents = mkOption { description = "If set all the components in the package are configured (useful for cabal-doctest)."; @@ -114,56 +114,56 @@ let shellHook = mkOption { description = "Hook to run when entering a shell"; type = unspecified; # Can be either a string or a function - default = (def.shellHook or ""); + default = def.shellHook or ""; }; enableLibraryProfiling = mkOption { type = bool; - default = (def.enableLibraryProfiling or false); + default = def.enableLibraryProfiling or false; }; enableSeparateDataOutput = mkOption { type = bool; - default = (def.enableSeparateDataOutput or true); + default = def.enableSeparateDataOutput or true; }; enableProfiling = mkOption { type = bool; - default = (def.enableProfiling or false); + default = def.enableProfiling or false; }; profilingDetail = mkOption { type = nullOr uniqueStr; - default = (def.profilingDetail or "default"); + default = def.profilingDetail or "default"; }; keepConfigFiles = mkOption { type = bool; - default = (def.keepConfigFiles or false); + default = def.keepConfigFiles or false; description = "Keep component configFiles in the store in a `configFiles` output"; }; keepGhc = mkOption { type = bool; - default = (def.keepGhc or false); + default = def.keepGhc or false; description = "Keep component wrapped ghc in the store in a `ghc` output"; }; keepSource = mkOption { type = bool; - default = (def.keepSource or false); + default = def.keepSource or false; description = "Keep component source in the store in a `source` output"; }; writeHieFiles = mkOption { type = bool; - default = (def.writeHieFiles or false); + default = def.writeHieFiles or false; description = "Write component `.hie` files in the store in a `hie` output"; }; }; packageOptions = def: componentOptions def // { preUnpack = mkOption { type = nullOr lines; - default = (def.preUnpack or null); + default = def.preUnpack or null; }; postUnpack = mkOption { type = nullOr uniqueStr; @@ -226,7 +226,7 @@ let }; hardeningDisable = mkOption { type = listOfFilteringNulls str; - default = (def.hardeningDisable or []); + default = def.hardeningDisable or []; }; ghcOptions = mkOption { type = listOfFilteringNulls str; @@ -234,7 +234,7 @@ let }; contentAddressed = mkOption { type = bool; - default = (def.contentAddressed or false); + default = def.contentAddressed or false; description = '' Build content addressed derivation, requires Nix to have experimental feature `ca-derivations` enabled. From d8e398ba3abcaec007c8ffc4869246aca0df1c47 Mon Sep 17 00:00:00 2001 From: Andrea Bedini Date: Fri, 7 Jul 2023 15:57:51 +0800 Subject: [PATCH 03/17] Use modules in place of passing arguments --- modules/package.nix | 4 ++-- modules/plan.nix | 25 ++++++++++++++----------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/modules/package.nix b/modules/package.nix index e75ecd160b..d4a2b32de9 100644 --- a/modules/package.nix +++ b/modules/package.nix @@ -13,7 +13,7 @@ # tests = { "..." = { depends = ... }; ... }; # }; -{ parentConfig, mod_args, componentOptions, packageOptions }: +{ parentConfig, componentOptions, packageOptions }: { lib, config, pkgs, haskellLib, ... }: with lib; @@ -124,7 +124,7 @@ let in { # This is how the Nix expressions generated by *-to-nix receive # their flags argument. - config._module.args = mod_args // { inherit (config) flags; }; + config._module.args = { inherit (config) flags; }; options = (packageOptions parentConfig) // { # TODO: Add descriptions to everything. diff --git a/modules/plan.nix b/modules/plan.nix index de077fa357..0088cdd84d 100644 --- a/modules/plan.nix +++ b/modules/plan.nix @@ -248,6 +248,19 @@ let }; }; + package = submodule [ + { + _module.args = { + inherit pkgs pkgconfPkgs haskellLib; + inherit (config) hsPkgs errorHandler; + inherit (config.cabal) system compiler; + }; + } + (import ./package.nix { + inherit componentOptions packageOptions; + parentConfig = config; + }) + ]; in { # Global options. These are passed down to the package level, and from there to the @@ -257,17 +270,7 @@ in { options = (packageOptions {}) // { packages = mkOption { - type = - let mod_args = { - inherit pkgs pkgconfPkgs haskellLib; - inherit (config) hsPkgs errorHandler; - inherit (config.cabal) system compiler; - }; in - attrsOf (submodule (import ./package.nix { - inherit mod_args; - inherit componentOptions packageOptions; - parentConfig = config; - })); + type = attrsOf package; }; compiler = { From 34cf41991dd48a91f74ab4cc6eb2127ec9b8b1cc Mon Sep 17 00:00:00 2001 From: Andrea Bedini Date: Fri, 7 Jul 2023 16:06:02 +0800 Subject: [PATCH 04/17] This is not used at all --- modules/package.nix | 2 +- modules/plan.nix | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/package.nix b/modules/package.nix index d4a2b32de9..8cca638ccc 100644 --- a/modules/package.nix +++ b/modules/package.nix @@ -13,7 +13,7 @@ # tests = { "..." = { depends = ... }; ... }; # }; -{ parentConfig, componentOptions, packageOptions }: +{ parentConfig, packageOptions, ... }: { lib, config, pkgs, haskellLib, ... }: with lib; diff --git a/modules/plan.nix b/modules/plan.nix index 0088cdd84d..8fd17e845d 100644 --- a/modules/plan.nix +++ b/modules/plan.nix @@ -257,7 +257,7 @@ let }; } (import ./package.nix { - inherit componentOptions packageOptions; + inherit packageOptions; parentConfig = config; }) ]; From 7bc4390c54a654c9c795b466726f2a0bf6e7d8ee Mon Sep 17 00:00:00 2001 From: Andrea Bedini Date: Fri, 7 Jul 2023 21:43:15 +0800 Subject: [PATCH 05/17] Formatting --- modules/package.nix | 108 +++++++++++---------- modules/plan.nix | 226 +++++++++++++++++++++++--------------------- 2 files changed, 172 insertions(+), 162 deletions(-) diff --git a/modules/package.nix b/modules/package.nix index 8cca638ccc..ce9bdc1eb2 100644 --- a/modules/package.nix +++ b/modules/package.nix @@ -44,75 +44,75 @@ let }; args = mkOption { type = listOf str; - default = []; + default = [ ]; }; }; }); - default = []; + default = [ ]; }; depends = mkOption { type = listOfFilteringNulls unspecified; - default = []; + default = [ ]; }; libs = mkOption { type = listOfFilteringNulls (either (nullOr package) (listOfFilteringNulls package)); - default = []; + default = [ ]; }; frameworks = mkOption { type = listOfFilteringNulls package; - default = []; + default = [ ]; }; pkgconfig = mkOption { type = listOf (listOfFilteringNulls package); - default = []; + default = [ ]; }; build-tools = mkOption { type = listOfFilteringNulls unspecified; - default = []; + default = [ ]; }; modules = mkOption { type = listOfFilteringNulls unspecified; - default = []; + default = [ ]; }; asmSources = mkOption { type = listOfFilteringNulls unspecified; - default = []; + default = [ ]; }; cmmSources = mkOption { type = listOfFilteringNulls unspecified; - default = []; + default = [ ]; }; cSources = mkOption { type = listOfFilteringNulls unspecified; - default = []; + default = [ ]; }; cxxSources = mkOption { type = listOfFilteringNulls unspecified; - default = []; + default = [ ]; }; jsSources = mkOption { type = listOfFilteringNulls unspecified; - default = []; + default = [ ]; }; hsSourceDirs = mkOption { type = listOfFilteringNulls unspecified; - default = ["."]; + default = [ "." ]; }; includeDirs = mkOption { type = listOfFilteringNulls unspecified; - default = []; + default = [ ]; }; includes = mkOption { type = listOfFilteringNulls unspecified; - default = []; + default = [ ]; }; mainPath = mkOption { type = listOfFilteringNulls unspecified; - default = []; + default = [ ]; }; extraSrcFiles = mkOption { type = listOfFilteringNulls unspecified; - default = []; + default = [ ]; }; platforms = mkOption { type = nullOr (listOfFilteringNulls unspecified); @@ -121,7 +121,8 @@ let }; }; -in { +in +{ # This is how the Nix expressions generated by *-to-nix receive # their flags argument. config._module.args = { inherit (config) flags; }; @@ -182,7 +183,7 @@ in { setup-depends = mkOption { type = listOfFilteringNulls unspecified; - default = []; + default = [ ]; }; detailLevel = mkOption { @@ -192,7 +193,7 @@ in { licenseFiles = mkOption { type = listOfFilteringNulls unspecified; - default = []; + default = [ ]; }; dataDir = mkOption { @@ -202,22 +203,22 @@ in { dataFiles = mkOption { type = listOfFilteringNulls unspecified; - default = []; + default = [ ]; }; extraSrcFiles = mkOption { type = listOfFilteringNulls unspecified; - default = []; + default = [ ]; }; extraTmpFiles = mkOption { type = listOfFilteringNulls unspecified; - default = []; + default = [ ]; }; extraDocFiles = mkOption { type = listOfFilteringNulls unspecified; - default = []; + default = [ ]; }; cleanHpack = mkOption { @@ -245,21 +246,21 @@ in { setup = mkOption { type = nullOr componentType; default = { - depends = []; - libs = []; - frameworks = []; + depends = [ ]; + libs = [ ]; + frameworks = [ ]; doExactConfig = false; # We have to set hsSourceDirs or cleanCabalComponent will # include everything (and as a result all the components of # the package will depend on everything in the package). # TODO find a better way - hsSourceDirs = ["setup-src"]; - includeDirs = []; - asmSources = []; - cSources = []; - cmmSources = []; - cxxSources = []; - jsSources = []; + hsSourceDirs = [ "setup-src" ]; + includeDirs = [ ]; + asmSources = [ ]; + cSources = [ ]; + cmmSources = [ ]; + cxxSources = [ ]; + jsSources = [ ]; extraSrcFiles = [ "Setup.hs" "Setup.lhs" ]; platforms = null; }; @@ -270,23 +271,23 @@ in { }; sublibs = mkOption { type = attrsOf componentType; - default = {}; + default = { }; }; foreignlibs = mkOption { type = attrsOf componentType; - default = {}; + default = { }; }; exes = mkOption { type = attrsOf componentType; - default = {}; + default = { }; }; tests = mkOption { type = attrsOf componentType; - default = {}; + default = { }; }; benchmarks = mkOption { type = attrsOf componentType; - default = {}; + default = { }; }; }; @@ -309,9 +310,9 @@ in { # do not know how to unpack source archive /nix/store/... apply = v: let storeDirMatch = __match "(${__storeDir}/[^/]+).*" v; - in if isString v && __getContext v == {} && storeDirMatch != null - then __appendContext v { ${__head storeDirMatch} = { path = true; }; } - else v; + in if isString v && __getContext v == { } && storeDirMatch != null + then __appendContext v { ${__head storeDirMatch} = { path = true; }; } + else v; }; package-description-override = mkOption { type = nullOr str; @@ -332,7 +333,7 @@ in { }; patches = mkOption { type = listOf (either unspecified path); - default = []; + default = [ ]; }; # This used to be `components.all` but it has been added back as `allComponent` to # to avoid confusion. It is not mapped by `builder/hspkg-builder.nix` to anything @@ -361,14 +362,17 @@ in { # returning true if any is true. config.allComponent = let allComps = haskellLib.getAllComponents config; - in lib.mkMerge ( - builtins.map (c: - # Exclude attributes that are likely to have conflicting definitions - # (a common use case for `all` is in `shellFor` and it only has an - # install phase). - builtins.removeAttrs c ["preCheck" "postCheck" "keepConfigFiles" "keepGhc" "keepSource"] - ) (lib.filter (c: c.buildable && c.planned) allComps) - ) // { + in lib.mkMerge + ( + builtins.map + (c: + # Exclude attributes that are likely to have conflicting definitions + # (a common use case for `all` is in `shellFor` and it only has an + # install phase). + builtins.removeAttrs c [ "preCheck" "postCheck" "keepConfigFiles" "keepGhc" "keepSource" ] + ) + (lib.filter (c: c.buildable && c.planned) allComps) + ) // { # If any one of the components needs us to keep one of these # then keep it for the `all` component keepConfigFiles = lib.foldl' (x: comp: x || comp.keepConfigFiles) false allComps; diff --git a/modules/plan.nix b/modules/plan.nix index 8fd17e845d..3997521694 100644 --- a/modules/plan.nix +++ b/modules/plan.nix @@ -19,103 +19,103 @@ let inherit (haskellLib.types) getDefaultOrNull listOfFilteringNulls uniqueStr; componentOptions = def: { - buildable = mkOption { - type = bool; - default = true; - }; - configureFlags = mkOption { - type = listOfFilteringNulls str; - default = def.configureFlags or []; - }; - setupBuildFlags = mkOption { - type = listOfFilteringNulls str; - default = def.setupBuildFlags or []; - }; - testFlags = mkOption { - type = listOfFilteringNulls str; - default = def.testFlags or []; - }; - setupInstallFlags = mkOption { - type = listOfFilteringNulls str; - default = def.setupInstallFlags or []; - }; - setupHaddockFlags = mkOption { - type = listOfFilteringNulls str; - default = def.setupHaddockFlags or []; - }; - doExactConfig = mkOption { - type = bool; - default = def.doExactConfig or false; - }; - doCheck = mkOption { - type = bool; - default = def.doCheck or true; - }; - doCrossCheck = mkOption { - description = "Run doCheck also in cross compilation settings. This can be tricky as the test logic must know how to run the tests on the target."; - type = bool; - default = def.doCrossCheck or false; - }; - doHaddock = mkOption { - description = "Enable building of the Haddock documentation from the annotated Haskell source code."; - type = bool; - default = def.doHaddock or true; - }; - doHoogle = mkOption { - description = "Also build a hoogle index."; - type = bool; - default = def.doHoogle or true; - }; - doHyperlinkSource = mkOption { - description = "Link documentation to the source code."; - type = bool; - default = def.doHyperlinkSource or true; - }; - doQuickjump = mkOption { - description = "Generate an index for interactive documentation navigation."; - type = bool; - default = def.doQuickjump or true; - }; - doCoverage = mkOption { - description = "Enable production of test coverage reports."; - type = bool; - default = def.doCoverage or false; - }; - dontPatchELF = mkOption { - description = "If set, the patchelf command is not used to remove unnecessary RPATH entries. Only applies to Linux."; - type = bool; - default = def.dontPatchELF or true; - }; - dontStrip = mkOption { - description = "If set, libraries and executables are not stripped."; - type = bool; - default = def.dontStrip or true; - }; - enableDeadCodeElimination = mkOption { - description = "If set, enables split sections for link-time dead-code stripping. Only applies to Linux"; - type = bool; - default = def.enableDeadCodeElimination or true; - }; - enableStatic = mkOption { - description = "If set, enables building static libraries and executables."; - type = bool; - default = def.enableStatic or true; - }; - enableShared = mkOption { - description = "If set, enables building shared libraries."; - type = bool; - default = def.enableShared or true; - }; - configureAllComponents = mkOption { - description = "If set all the components in the package are configured (useful for cabal-doctest)."; - type = bool; - default = false; - }; - shellHook = mkOption { - description = "Hook to run when entering a shell"; - type = unspecified; # Can be either a string or a function - default = def.shellHook or ""; - }; + buildable = mkOption { + type = bool; + default = true; + }; + configureFlags = mkOption { + type = listOfFilteringNulls str; + default = def.configureFlags or [ ]; + }; + setupBuildFlags = mkOption { + type = listOfFilteringNulls str; + default = def.setupBuildFlags or [ ]; + }; + testFlags = mkOption { + type = listOfFilteringNulls str; + default = def.testFlags or [ ]; + }; + setupInstallFlags = mkOption { + type = listOfFilteringNulls str; + default = def.setupInstallFlags or [ ]; + }; + setupHaddockFlags = mkOption { + type = listOfFilteringNulls str; + default = def.setupHaddockFlags or [ ]; + }; + doExactConfig = mkOption { + type = bool; + default = def.doExactConfig or false; + }; + doCheck = mkOption { + type = bool; + default = def.doCheck or true; + }; + doCrossCheck = mkOption { + description = "Run doCheck also in cross compilation settings. This can be tricky as the test logic must know how to run the tests on the target."; + type = bool; + default = def.doCrossCheck or false; + }; + doHaddock = mkOption { + description = "Enable building of the Haddock documentation from the annotated Haskell source code."; + type = bool; + default = def.doHaddock or true; + }; + doHoogle = mkOption { + description = "Also build a hoogle index."; + type = bool; + default = def.doHoogle or true; + }; + doHyperlinkSource = mkOption { + description = "Link documentation to the source code."; + type = bool; + default = def.doHyperlinkSource or true; + }; + doQuickjump = mkOption { + description = "Generate an index for interactive documentation navigation."; + type = bool; + default = def.doQuickjump or true; + }; + doCoverage = mkOption { + description = "Enable production of test coverage reports."; + type = bool; + default = def.doCoverage or false; + }; + dontPatchELF = mkOption { + description = "If set, the patchelf command is not used to remove unnecessary RPATH entries. Only applies to Linux."; + type = bool; + default = def.dontPatchELF or true; + }; + dontStrip = mkOption { + description = "If set, libraries and executables are not stripped."; + type = bool; + default = def.dontStrip or true; + }; + enableDeadCodeElimination = mkOption { + description = "If set, enables split sections for link-time dead-code stripping. Only applies to Linux"; + type = bool; + default = def.enableDeadCodeElimination or true; + }; + enableStatic = mkOption { + description = "If set, enables building static libraries and executables."; + type = bool; + default = def.enableStatic or true; + }; + enableShared = mkOption { + description = "If set, enables building shared libraries."; + type = bool; + default = def.enableShared or true; + }; + configureAllComponents = mkOption { + description = "If set all the components in the package are configured (useful for cabal-doctest)."; + type = bool; + default = false; + }; + shellHook = mkOption { + description = "Hook to run when entering a shell"; + type = unspecified; # Can be either a string or a function + default = def.shellHook or ""; + }; enableLibraryProfiling = mkOption { type = bool; default = def.enableLibraryProfiling or false; @@ -200,7 +200,7 @@ let # Wrapper for test executable run in checkPhase testWrapper = mkOption { type = listOfFilteringNulls str; - default = def.testWrapper or []; + default = def.testWrapper or [ ]; description = "A command to run for executing tests in checkPhase, which takes the original test command as its arguments."; example = "echo"; }; @@ -226,11 +226,11 @@ let }; hardeningDisable = mkOption { type = listOfFilteringNulls str; - default = def.hardeningDisable or []; + default = def.hardeningDisable or [ ]; }; ghcOptions = mkOption { type = listOfFilteringNulls str; - default = def.ghcOptions or []; + default = def.ghcOptions or [ ]; }; contentAddressed = mkOption { type = bool; @@ -262,12 +262,13 @@ let }) ]; -in { +in +{ # Global options. These are passed down to the package level, and from there to the # component level, unless specifically overridden. Depending on the flag flags are # combined or replaced. We seed the package Options with an empty set forcing the # default values. - options = (packageOptions {}) // { + options = (packageOptions { }) // { packages = mkOption { type = attrsOf package; @@ -303,14 +304,19 @@ in { config = let module = config.plan.pkg-def config.hackage.configs; in { inherit (module) compiler; - packages = lib.mapAttrs (name: { revision, ... }@revArgs: { system, compiler, flags, pkgs, hsPkgs, errorHandler, pkgconfPkgs, ... }@modArgs: + packages = lib.mapAttrs + (name: { revision, ... }@revArgs: { system, compiler, flags, pkgs, hsPkgs, errorHandler, pkgconfPkgs, ... }@modArgs: - let m = if revision == null - then (abort "${name} has no revision!") - else revision (modArgs // { hsPkgs = hsPkgs // (mapAttrs (l: _: hsPkgs.${name}.components.sublibs.${l}) (m.components.sublibs or {})); }); - in - m // { flags = lib.mapAttrs (_: lib.mkDefault) (m.flags // revArgs.flags or {}); - } - ) (lib.filterAttrs (n: v: v == null || v.revision != null ) module.packages); + let + m = + if revision == null + then (abort "${name} has no revision!") + else revision (modArgs // { hsPkgs = hsPkgs // (mapAttrs (l: _: hsPkgs.${name}.components.sublibs.${l}) (m.components.sublibs or { })); }); + in + m // { + flags = lib.mapAttrs (_: lib.mkDefault) (m.flags // revArgs.flags or { }); + } + ) + (lib.filterAttrs (n: v: v == null || v.revision != null) module.packages); }; } From bc37a58e2192a41990599188c15fc3aaeb1f4da8 Mon Sep 17 00:00:00 2001 From: Andrea Bedini Date: Sat, 8 Jul 2023 02:03:37 +0800 Subject: [PATCH 06/17] Move component options out --- modules/component-options.nix | 166 ++++++++++++++++++++++++++++++ modules/package.nix | 187 +++++++++++++++++----------------- modules/plan.nix | 175 ++++--------------------------- 3 files changed, 280 insertions(+), 248 deletions(-) create mode 100644 modules/component-options.nix diff --git a/modules/component-options.nix b/modules/component-options.nix new file mode 100644 index 0000000000..ad5783399d --- /dev/null +++ b/modules/component-options.nix @@ -0,0 +1,166 @@ +{ lib, haskellLib, ... }: +{ + options = { + buildable = lib.mkOption { + type = lib.types.bool; + default = true; + }; + + configureFlags = lib.mkOption { + type = haskellLib.types.listOfFilteringNulls lib.types.str; + default = []; + }; + + setupBuildFlags = lib.mkOption { + type = haskellLib.types.listOfFilteringNulls lib.types.str; + default = []; + }; + + testFlags = lib.mkOption { + type = haskellLib.types.listOfFilteringNulls lib.types.str; + default = []; + }; + + setupInstallFlags = lib.mkOption { + type = haskellLib.types.listOfFilteringNulls lib.types.str; + default = []; + }; + + setupHaddockFlags = lib.mkOption { + type = haskellLib.types.listOfFilteringNulls lib.types.str; + default = []; + }; + + doExactConfig = lib.mkOption { + type = lib.types.bool; + default = false; + }; + + doCheck = lib.mkOption { + type = lib.types.bool; + default = true; + }; + + doCrossCheck = lib.mkOption { + description = "Run doCheck also in cross compilation settings. This can be tricky as the test logic must know how to run the tests on the target."; + type = lib.types.bool; + default = false; + }; + + doHaddock = lib.mkOption { + description = "Enable building of the Haddock documentation from the annotated Haskell source code."; + type = lib.types.bool; + default = true; + }; + + doHoogle = lib.mkOption { + description = "Also build a hoogle index."; + type = lib.types.bool; + default = true; + }; + + doHyperlinkSource = lib.mkOption { + description = "Link documentation to the source code."; + type = lib.types.bool; + default = true; + }; + + doQuickjump = lib.mkOption { + description = "Generate an index for interactive documentation navigation."; + type = lib.types.bool; + default = true; + }; + + doCoverage = lib.mkOption { + description = "Enable production of test coverage reports."; + type = lib.types.bool; + default = false; + }; + + dontPatchELF = lib.mkOption { + description = "If set, the patchelf command is not used to remove unnecessary RPATH entries. Only applies to Linux."; + type = lib.types.bool; + default = true; + }; + + dontStrip = lib.mkOption { + description = "If set, libraries and executables are not stripped."; + type = lib.types.bool; + default = true; + }; + + enableDeadCodeElimination = lib.mkOption { + description = "If set, enables split sections for link-time dead-code stripping. Only applies to Linux"; + type = lib.types.bool; + default = true; + }; + + enableStatic = lib.mkOption { + description = "If set, enables building static libraries and executables."; + type = lib.types.bool; + default = true; + }; + + enableShared = lib.mkOption { + description = "If set, enables building shared libraries."; + type = lib.types.bool; + default = true; + }; + + configureAllComponents = lib.mkOption { + description = "If set all the components in the package are configured (useful for cabal-doctest)."; + type = lib.types.bool; + default = false; + }; + + shellHook = lib.mkOption { + description = "Hook to run when entering a shell"; + type = lib.types.unspecified; # Can be either a string or a function + default = ""; + }; + + enableLibraryProfiling = lib.mkOption { + type = lib.types.bool; + default = false; + }; + + enableSeparateDataOutput = lib.mkOption { + type = lib.types.bool; + default = true; + }; + + enableProfiling = lib.mkOption { + type = lib.types.bool; + default = false; + }; + + profilingDetail = lib.mkOption { + type = lib.types.nullOr haskellLib.types.uniqueStr; + default = "default"; + }; + + keepConfigFiles = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Keep component configFiles in the store in a `configFiles` output"; + }; + + keepGhc = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Keep component wrapped ghc in the store in a `ghc` output"; + }; + + keepSource = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Keep component source in the store in a `source` output"; + }; + + writeHieFiles = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Write component `.hie` files in the store in a `hie` output"; + }; + }; +} diff --git a/modules/package.nix b/modules/package.nix index ce9bdc1eb2..bedc976eac 100644 --- a/modules/package.nix +++ b/modules/package.nix @@ -26,103 +26,108 @@ let path = types.path // { check = x: types.path.check (x.origSrc or x); }; - componentType = submodule { - # add the shared componentOptions - options = (packageOptions config) // { - buildable = mkOption { - type = bool; - default = true; - }; - plugins = mkOption { - type = listOf (submodule { - options = { - library = mkOption { - type = unspecified; - }; - moduleName = mkOption { - type = str; + componentType = submodule [ + { + imports = [ ./component-options.nix ]; + options = (packageOptions config) // { + plugins = mkOption { + type = listOf (submodule { + options = { + library = mkOption { + type = unspecified; + }; + moduleName = mkOption { + type = str; + }; + args = mkOption { + type = listOf str; + default = [ ]; + }; }; - args = mkOption { - type = listOf str; - default = [ ]; - }; - }; - }); - default = [ ]; - }; - depends = mkOption { - type = listOfFilteringNulls unspecified; - default = [ ]; - }; - libs = mkOption { - type = listOfFilteringNulls (either (nullOr package) (listOfFilteringNulls package)); - default = [ ]; - }; - frameworks = mkOption { - type = listOfFilteringNulls package; - default = [ ]; - }; - pkgconfig = mkOption { - type = listOf (listOfFilteringNulls package); - default = [ ]; - }; - build-tools = mkOption { - type = listOfFilteringNulls unspecified; - default = [ ]; - }; - modules = mkOption { - type = listOfFilteringNulls unspecified; - default = [ ]; - }; - asmSources = mkOption { - type = listOfFilteringNulls unspecified; - default = [ ]; - }; - cmmSources = mkOption { - type = listOfFilteringNulls unspecified; - default = [ ]; - }; - cSources = mkOption { - type = listOfFilteringNulls unspecified; - default = [ ]; - }; - cxxSources = mkOption { - type = listOfFilteringNulls unspecified; - default = [ ]; - }; - jsSources = mkOption { - type = listOfFilteringNulls unspecified; - default = [ ]; - }; - hsSourceDirs = mkOption { - type = listOfFilteringNulls unspecified; - default = [ "." ]; - }; - includeDirs = mkOption { - type = listOfFilteringNulls unspecified; - default = [ ]; - }; - includes = mkOption { - type = listOfFilteringNulls unspecified; - default = [ ]; - }; - mainPath = mkOption { - type = listOfFilteringNulls unspecified; - default = [ ]; - }; - extraSrcFiles = mkOption { - type = listOfFilteringNulls unspecified; - default = [ ]; + }); + default = [ ]; + }; + depends = mkOption { + type = listOfFilteringNulls unspecified; + default = [ ]; + }; + libs = mkOption { + type = listOfFilteringNulls (either (nullOr package) (listOfFilteringNulls package)); + default = [ ]; + }; + frameworks = mkOption { + type = listOfFilteringNulls package; + default = [ ]; + }; + pkgconfig = mkOption { + type = listOf (listOfFilteringNulls package); + default = [ ]; + }; + build-tools = mkOption { + type = listOfFilteringNulls unspecified; + default = [ ]; + }; + modules = mkOption { + type = listOfFilteringNulls unspecified; + default = [ ]; + }; + asmSources = mkOption { + type = listOfFilteringNulls unspecified; + default = [ ]; + }; + cmmSources = mkOption { + type = listOfFilteringNulls unspecified; + default = [ ]; + }; + cSources = mkOption { + type = listOfFilteringNulls unspecified; + default = [ ]; + }; + cxxSources = mkOption { + type = listOfFilteringNulls unspecified; + default = [ ]; + }; + jsSources = mkOption { + type = listOfFilteringNulls unspecified; + default = [ ]; + }; + hsSourceDirs = mkOption { + type = listOfFilteringNulls unspecified; + default = [ "." ]; + }; + includeDirs = mkOption { + type = listOfFilteringNulls unspecified; + default = [ ]; + }; + includes = mkOption { + type = listOfFilteringNulls unspecified; + default = [ ]; + }; + mainPath = mkOption { + type = listOfFilteringNulls unspecified; + default = [ ]; + }; + extraSrcFiles = mkOption { + type = listOfFilteringNulls unspecified; + default = [ ]; + }; + platforms = mkOption { + type = nullOr (listOfFilteringNulls unspecified); + default = null; + }; }; - platforms = mkOption { - type = nullOr (listOfFilteringNulls unspecified); - default = null; + config = { + _module.args = { inherit haskellLib; }; }; - }; - }; + } + # pass down common options as default values + ({ lib, options, ... }: lib.mkDefault (lib.filterAttrs (n: _v: builtins.hasAttr n options) config)) + ]; in { + imports = [ ./component-options.nix ]; + # This is how the Nix expressions generated by *-to-nix receive # their flags argument. config._module.args = { inherit (config) flags; }; diff --git a/modules/plan.nix b/modules/plan.nix index 3997521694..de13832e57 100644 --- a/modules/plan.nix +++ b/modules/plan.nix @@ -18,149 +18,12 @@ with types; let inherit (haskellLib.types) getDefaultOrNull listOfFilteringNulls uniqueStr; - componentOptions = def: { - buildable = mkOption { - type = bool; - default = true; - }; - configureFlags = mkOption { - type = listOfFilteringNulls str; - default = def.configureFlags or [ ]; - }; - setupBuildFlags = mkOption { - type = listOfFilteringNulls str; - default = def.setupBuildFlags or [ ]; - }; - testFlags = mkOption { - type = listOfFilteringNulls str; - default = def.testFlags or [ ]; - }; - setupInstallFlags = mkOption { - type = listOfFilteringNulls str; - default = def.setupInstallFlags or [ ]; - }; - setupHaddockFlags = mkOption { - type = listOfFilteringNulls str; - default = def.setupHaddockFlags or [ ]; - }; - doExactConfig = mkOption { - type = bool; - default = def.doExactConfig or false; - }; - doCheck = mkOption { - type = bool; - default = def.doCheck or true; - }; - doCrossCheck = mkOption { - description = "Run doCheck also in cross compilation settings. This can be tricky as the test logic must know how to run the tests on the target."; - type = bool; - default = def.doCrossCheck or false; - }; - doHaddock = mkOption { - description = "Enable building of the Haddock documentation from the annotated Haskell source code."; - type = bool; - default = def.doHaddock or true; - }; - doHoogle = mkOption { - description = "Also build a hoogle index."; - type = bool; - default = def.doHoogle or true; - }; - doHyperlinkSource = mkOption { - description = "Link documentation to the source code."; - type = bool; - default = def.doHyperlinkSource or true; - }; - doQuickjump = mkOption { - description = "Generate an index for interactive documentation navigation."; - type = bool; - default = def.doQuickjump or true; - }; - doCoverage = mkOption { - description = "Enable production of test coverage reports."; - type = bool; - default = def.doCoverage or false; - }; - dontPatchELF = mkOption { - description = "If set, the patchelf command is not used to remove unnecessary RPATH entries. Only applies to Linux."; - type = bool; - default = def.dontPatchELF or true; - }; - dontStrip = mkOption { - description = "If set, libraries and executables are not stripped."; - type = bool; - default = def.dontStrip or true; - }; - enableDeadCodeElimination = mkOption { - description = "If set, enables split sections for link-time dead-code stripping. Only applies to Linux"; - type = bool; - default = def.enableDeadCodeElimination or true; - }; - enableStatic = mkOption { - description = "If set, enables building static libraries and executables."; - type = bool; - default = def.enableStatic or true; - }; - enableShared = mkOption { - description = "If set, enables building shared libraries."; - type = bool; - default = def.enableShared or true; - }; - configureAllComponents = mkOption { - description = "If set all the components in the package are configured (useful for cabal-doctest)."; - type = bool; - default = false; - }; - shellHook = mkOption { - description = "Hook to run when entering a shell"; - type = unspecified; # Can be either a string or a function - default = def.shellHook or ""; - }; - enableLibraryProfiling = mkOption { - type = bool; - default = def.enableLibraryProfiling or false; - }; + # NOTE: + # higher up settings work as default for the lower levels + # so project level doExactConfig sets the default for packages + # and package doExactConfig sets the default for its components - enableSeparateDataOutput = mkOption { - type = bool; - default = def.enableSeparateDataOutput or true; - }; - - enableProfiling = mkOption { - type = bool; - default = def.enableProfiling or false; - }; - - profilingDetail = mkOption { - type = nullOr uniqueStr; - default = def.profilingDetail or "default"; - }; - - keepConfigFiles = mkOption { - type = bool; - default = def.keepConfigFiles or false; - description = "Keep component configFiles in the store in a `configFiles` output"; - }; - - keepGhc = mkOption { - type = bool; - default = def.keepGhc or false; - description = "Keep component wrapped ghc in the store in a `ghc` output"; - }; - - keepSource = mkOption { - type = bool; - default = def.keepSource or false; - description = "Keep component source in the store in a `source` output"; - }; - - writeHieFiles = mkOption { - type = bool; - default = def.writeHieFiles or false; - description = "Write component `.hie` files in the store in a `hie` output"; - }; - }; - packageOptions = def: componentOptions def // { + packageOptions = def: { preUnpack = mkOption { type = nullOr lines; default = def.preUnpack or null; @@ -260,16 +123,19 @@ let inherit packageOptions; parentConfig = config; }) + # pass down common options as default values + ({ lib, options, ... }: lib.mkDefault (lib.filterAttrs (n: _v: builtins.hasAttr n options) config)) ]; in { + imports = [ ./component-options.nix ]; + # Global options. These are passed down to the package level, and from there to the # component level, unless specifically overridden. Depending on the flag flags are # combined or replaced. We seed the package Options with an empty set forcing the # default values. options = (packageOptions { }) // { - packages = mkOption { type = attrsOf package; }; @@ -304,19 +170,14 @@ in config = let module = config.plan.pkg-def config.hackage.configs; in { inherit (module) compiler; - packages = lib.mapAttrs - (name: { revision, ... }@revArgs: { system, compiler, flags, pkgs, hsPkgs, errorHandler, pkgconfPkgs, ... }@modArgs: - - let - m = - if revision == null - then (abort "${name} has no revision!") - else revision (modArgs // { hsPkgs = hsPkgs // (mapAttrs (l: _: hsPkgs.${name}.components.sublibs.${l}) (m.components.sublibs or { })); }); - in - m // { - flags = lib.mapAttrs (_: lib.mkDefault) (m.flags // revArgs.flags or { }); - } - ) - (lib.filterAttrs (n: v: v == null || v.revision != null) module.packages); + packages = lib.mapAttrs (name: { revision, ... }@revArgs: { system, compiler, flags, pkgs, hsPkgs, errorHandler, pkgconfPkgs, ... }@modArgs: + + let m = if revision == null + then (abort "${name} has no revision!") + else revision (modArgs // { hsPkgs = hsPkgs // (mapAttrs (l: _: hsPkgs.${name}.components.sublibs.${l}) (m.components.sublibs or {})); }); + in + m // { flags = lib.mapAttrs (_: lib.mkDefault) (m.flags // revArgs.flags or {}); + } + ) (lib.filterAttrs (n: v: v == null || v.revision != null ) module.packages); }; } From f00e64cdcd4a04409e31ec8a7ce77c664e213d8f Mon Sep 17 00:00:00 2001 From: Andrea Bedini Date: Sat, 8 Jul 2023 02:41:56 +0800 Subject: [PATCH 07/17] Move package options out --- modules/package-options.nix | 93 ++++++++++++ modules/package.nix | 290 +++++++++++++++++------------------- modules/plan.nix | 119 +-------------- 3 files changed, 238 insertions(+), 264 deletions(-) create mode 100644 modules/package-options.nix diff --git a/modules/package-options.nix b/modules/package-options.nix new file mode 100644 index 0000000000..c5a3f9dd4a --- /dev/null +++ b/modules/package-options.nix @@ -0,0 +1,93 @@ +{ lib, haskellLib, ... }: +let + inherit (haskellLib.types) listOfFilteringNulls; +in +{ + options = { + preUnpack = lib.mkOption { + type = lib.types.nullOr lib.types.lines; + default = null; + }; + postUnpack = lib.mkOption { + type = lib.types.nullOr haskellLib.types.uniqueStr; + default = null; + }; + prePatch = lib.mkOption { + type = lib.types.nullOr haskellLib.types.uniqueStr; + default = null; + }; + postPatch = lib.mkOption { + type = lib.types.nullOr haskellLib.types.uniqueStr; + default = null; + }; + preConfigure = lib.mkOption { + type = lib.types.nullOr haskellLib.types.uniqueStr; + default = null; + }; + postConfigure = lib.mkOption { + type = lib.types.nullOr haskellLib.types.uniqueStr; + default = null; + }; + preBuild = lib.mkOption { + type = lib.types.nullOr haskellLib.types.uniqueStr; + default = null; + }; + postBuild = lib.mkOption { + type = lib.types.nullOr haskellLib.types.uniqueStr; + default = null; + }; + preCheck = lib.mkOption { + type = lib.types.nullOr haskellLib.types.uniqueStr; + default = null; + }; + # Wrapper for test executable run in checkPhase + testWrapper = lib.mkOption { + type = listOfFilteringNulls lib.types.str; + default = [ ]; + description = "A command to run for executing tests in checkPhase, which takes the original test command as its arguments."; + example = "echo"; + }; + postCheck = lib.mkOption { + type = lib.types.nullOr haskellLib.types.uniqueStr; + default = null; + }; + preInstall = lib.mkOption { + type = lib.types.nullOr haskellLib.types.uniqueStr; + default = null; + }; + postInstall = lib.mkOption { + type = lib.types.nullOr haskellLib.types.uniqueStr; + default = null; + }; + preHaddock = lib.mkOption { + type = lib.types.nullOr haskellLib.types.uniqueStr; + default = null; + }; + postHaddock = lib.mkOption { + type = lib.types.nullOr haskellLib.types.uniqueStr; + default = null; + }; + hardeningDisable = lib.mkOption { + type = listOfFilteringNulls lib.types.str; + default = [ ]; + }; + ghcOptions = lib.mkOption { + type = listOfFilteringNulls lib.types.str; + default = [ ]; + }; + contentAddressed = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Build content addressed derivation, requires Nix to have experimental feature + `ca-derivations` enabled. + ''; + }; + planned = lib.mkOption { + description = "Set to true by `plan-to-nix` for any component that was included in the `plan.json` file."; + # This is here so that (rather than in componentOptions) so it can be set project wide for stack projects + type = lib.types.bool; + default = false; + }; + }; +} diff --git a/modules/package.nix b/modules/package.nix index bedc976eac..ef44c34628 100644 --- a/modules/package.nix +++ b/modules/package.nix @@ -1,118 +1,103 @@ -# package descriptions in hackage will look like: -# { system, compiler, flags, pkgs, hsPkgs, pkgconfPkgs }: -# { flags = { flag1 = false; flags2 = true; ... }; -# package = { specVersion = "X.Y"; identifier = { name = "..."; version = "a.b.c.d"; }; -# license = "..."; copyright = "..."; maintainer = "..."; author = "..."; -# homepage = "..."; url = "..."; synopsis = "..."; description = "..."; -# buildType = "Simple"; # or Custom, Autoconf, ... -# }; -# components = { -# "..." = { depends = [ (hsPkgs.base) ... ]; }; -# exes = { "..." = { depends = ... }; -# "..." = { depends = ... }; }; -# tests = { "..." = { depends = ... }; ... }; -# }; - -{ parentConfig, packageOptions, ... }: { lib, config, pkgs, haskellLib, ... }: -with lib; -with types; - # Work around issue that can cause _lots_ of files to be copied into the store. # See https://github.com/NixOS/nixpkgs/pull/64691 let inherit (haskellLib.types) listOfFilteringNulls; + inherit (lib) types; path = types.path // { check = x: types.path.check (x.origSrc or x); }; - componentType = submodule [ + componentType = types.submodule [ { - imports = [ ./component-options.nix ]; - options = (packageOptions config) // { - plugins = mkOption { - type = listOf (submodule { + imports = [ + ./component-options.nix + ./package-options.nix + ]; + options = { + plugins = lib.mkOption { + type = types.listOf (types.submodule { options = { - library = mkOption { - type = unspecified; + library = lib.mkOption { + type = types.unspecified; }; - moduleName = mkOption { - type = str; + moduleName = lib.mkOption { + type = types.str; }; - args = mkOption { - type = listOf str; + args = lib.mkOption { + type = types.listOf types.str; default = [ ]; }; }; }); default = [ ]; }; - depends = mkOption { - type = listOfFilteringNulls unspecified; + depends = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ ]; }; - libs = mkOption { - type = listOfFilteringNulls (either (nullOr package) (listOfFilteringNulls package)); + libs = lib.mkOption { + type = listOfFilteringNulls (types.either (types.nullOr types.package) (listOfFilteringNulls types.package)); default = [ ]; }; - frameworks = mkOption { - type = listOfFilteringNulls package; + frameworks = lib.mkOption { + type = listOfFilteringNulls types.package; default = [ ]; }; - pkgconfig = mkOption { - type = listOf (listOfFilteringNulls package); + pkgconfig = lib.mkOption { + type = types.listOf (listOfFilteringNulls types.package); default = [ ]; }; - build-tools = mkOption { - type = listOfFilteringNulls unspecified; + build-tools = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ ]; }; - modules = mkOption { - type = listOfFilteringNulls unspecified; + modules = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ ]; }; - asmSources = mkOption { - type = listOfFilteringNulls unspecified; + asmSources = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ ]; }; - cmmSources = mkOption { - type = listOfFilteringNulls unspecified; + cmmSources = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ ]; }; - cSources = mkOption { - type = listOfFilteringNulls unspecified; + cSources = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ ]; }; - cxxSources = mkOption { - type = listOfFilteringNulls unspecified; + cxxSources = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ ]; }; - jsSources = mkOption { - type = listOfFilteringNulls unspecified; + jsSources = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ ]; }; - hsSourceDirs = mkOption { - type = listOfFilteringNulls unspecified; + hsSourceDirs = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ "." ]; }; - includeDirs = mkOption { - type = listOfFilteringNulls unspecified; + includeDirs = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ ]; }; - includes = mkOption { - type = listOfFilteringNulls unspecified; + includes = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ ]; }; - mainPath = mkOption { - type = listOfFilteringNulls unspecified; + mainPath = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ ]; }; - extraSrcFiles = mkOption { - type = listOfFilteringNulls unspecified; + extraSrcFiles = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ ]; }; - platforms = mkOption { - type = nullOr (listOfFilteringNulls unspecified); + platforms = lib.mkOption { + type = types.nullOr (listOfFilteringNulls types.unspecified); default = null; }; }; @@ -126,130 +111,133 @@ let in { - imports = [ ./component-options.nix ]; + imports = [ + ./component-options.nix + ./package-options.nix + ]; # This is how the Nix expressions generated by *-to-nix receive # their flags argument. config._module.args = { inherit (config) flags; }; - options = (packageOptions parentConfig) // { + options = { # TODO: Add descriptions to everything. - flags = mkOption { - type = attrsOf bool; + flags = lib.mkOption { + type = types.attrsOf types.bool; }; package = { - specVersion = mkOption { - type = str; + specVersion = lib.mkOption { + type = types.str; }; - identifier.name = mkOption { - type = str; + identifier.name = lib.mkOption { + type = types.str; }; - identifier.version = mkOption { - type = str; + identifier.version = lib.mkOption { + type = types.str; }; - license = mkOption { - type = str; + license = lib.mkOption { + type = types.str; }; - copyright = mkOption { - type = str; + copyright = lib.mkOption { + type = types.str; }; - maintainer = mkOption { - type = str; + maintainer = lib.mkOption { + type = types.str; }; - author = mkOption { - type = str; + author = lib.mkOption { + type = types.str; }; - homepage = mkOption { - type = str; + homepage = lib.mkOption { + type = types.str; }; - url = mkOption { - type = str; + url = lib.mkOption { + type = types.str; }; - synopsis = mkOption { - type = str; + synopsis = lib.mkOption { + type = types.str; }; - description = mkOption { - type = str; + description = lib.mkOption { + type = types.str; }; - buildType = mkOption { - type = str; + buildType = lib.mkOption { + type = types.str; }; - setup-depends = mkOption { - type = listOfFilteringNulls unspecified; + setup-depends = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ ]; }; - detailLevel = mkOption { - type = str; + detailLevel = lib.mkOption { + type = types.str; default = "MinimalDetails"; }; - licenseFiles = mkOption { - type = listOfFilteringNulls unspecified; + licenseFiles = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ ]; }; - dataDir = mkOption { - type = str; + dataDir = lib.mkOption { + type = types.str; default = ""; }; - dataFiles = mkOption { - type = listOfFilteringNulls unspecified; + dataFiles = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ ]; }; - extraSrcFiles = mkOption { - type = listOfFilteringNulls unspecified; + extraSrcFiles = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ ]; }; - extraTmpFiles = mkOption { - type = listOfFilteringNulls unspecified; + extraTmpFiles = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ ]; }; - extraDocFiles = mkOption { - type = listOfFilteringNulls unspecified; + extraDocFiles = lib.mkOption { + type = listOfFilteringNulls types.unspecified; default = [ ]; }; - cleanHpack = mkOption { - type = bool; + cleanHpack = lib.mkOption { + type = types.bool; default = false; }; - isLocal = mkOption { - type = bool; + isLocal = lib.mkOption { + type = types.bool; default = false; }; - isProject = mkOption { - type = bool; + isProject = lib.mkOption { + type = types.bool; default = false; }; - buildable = mkOption { - type = bool; + buildable = lib.mkOption { + type = types.bool; default = true; }; }; components = { - setup = mkOption { - type = nullOr componentType; + setup = lib.mkOption { + type = types.nullOr componentType; default = { depends = [ ]; libs = [ ]; @@ -270,43 +258,43 @@ in platforms = null; }; }; - library = mkOption { - type = nullOr componentType; + library = lib.mkOption { + type = types.nullOr componentType; default = null; }; - sublibs = mkOption { - type = attrsOf componentType; + sublibs = lib.mkOption { + type = types.attrsOf componentType; default = { }; }; - foreignlibs = mkOption { - type = attrsOf componentType; + foreignlibs = lib.mkOption { + type = types.attrsOf componentType; default = { }; }; - exes = mkOption { - type = attrsOf componentType; + exes = lib.mkOption { + type = types.attrsOf componentType; default = { }; }; - tests = mkOption { - type = attrsOf componentType; + tests = lib.mkOption { + type = types.attrsOf componentType; default = { }; }; - benchmarks = mkOption { - type = attrsOf componentType; + benchmarks = lib.mkOption { + type = types.attrsOf componentType; default = { }; }; }; - name = mkOption { - type = str; + name = lib.mkOption { + type = types.str; default = "${config.package.identifier.name}-${config.package.identifier.version}"; defaultText = "\${config.package.identifier.name}-\${config.package.identifier.version}"; }; - sha256 = mkOption { - type = nullOr str; + sha256 = lib.mkOption { + type = types.nullOr types.str; default = null; }; - src = mkOption { - type = either path package; + src = lib.mkOption { + type = types.either path types.package; default = pkgs.fetchurl { url = "mirror://hackage/${config.name}.tar.gz"; inherit (config) sha256; }; defaultText = "pkgs.fetchurl { url = \"mirror://hackage/\${config.name}.tar.gz\"; inherit (config) sha256; };"; # Make sure paths have a context so they will be included in the derivation @@ -314,38 +302,38 @@ in # cannot see the input and fail with the error: # do not know how to unpack source archive /nix/store/... apply = v: - let storeDirMatch = __match "(${__storeDir}/[^/]+).*" v; - in if isString v && __getContext v == { } && storeDirMatch != null - then __appendContext v { ${__head storeDirMatch} = { path = true; }; } + let storeDirMatch = builtins.match "(${builtins.storeDir}/[^/]+).*" v; + in if builtins.isString v && builtins.getContext v == { } && storeDirMatch != null + then builtins.appendContext v { ${builtins.head storeDirMatch} = { path = true; }; } else v; }; - package-description-override = mkOption { - type = nullOr str; + package-description-override = lib.mkOption { + type = types.nullOr types.str; default = null; description = "Cabal file to use instead of the one shipped inside the package source distribution."; }; - cabal-generator = mkOption { - type = nullOr str; + cabal-generator = lib.mkOption { + type = types.nullOr types.str; default = null; }; - revision = mkOption { - type = nullOr int; + revision = lib.mkOption { + type = types.nullOr types.int; default = null; }; - revisionSha256 = mkOption { - type = nullOr str; + revisionSha256 = lib.mkOption { + type = types.nullOr types.str; default = null; }; - patches = mkOption { - type = listOf (either unspecified path); + patches = lib.mkOption { + type = types.listOf (types.either types.unspecified path); default = [ ]; }; # This used to be `components.all` but it has been added back as `allComponent` to # to avoid confusion. It is not mapped by `builder/hspkg-builder.nix` to anything # you can build. Instead it is used internally when `configureAllComponents` # is set or for tests whe on `cabal-doctest` is in the `setup-depends` of the package. - allComponent = mkOption { - type = componentType; + allComponent = lib.mkOption { + type = types.componentType; apply = all: all // { # TODO: Should this check for the entire component # definition to match, rather than just the identifier? diff --git a/modules/plan.nix b/modules/plan.nix index de13832e57..014f413706 100644 --- a/modules/plan.nix +++ b/modules/plan.nix @@ -1,116 +1,9 @@ -# The plan (that is, a package set description like an LTS set or a -# plan.nix (derived from plan.json)) will produce a structure that -# looks like, which is stored in config.plan.pkg-def: -# -# { packages = { "package" = { revision = hackageConfigs.$package.$version.revisions.default; -# flags = { flag1 = true; flag2 = false; ... }; }; -# ... }; -# compiler = { version = "X.Y.Z"; nix-name ="ghcXYZ"; -# # packages that come bundled with the compiler -# packages = { "bytestring" = "a.b.c.d"; ... }; }; -# } - { lib, config, pkgs, pkgconfPkgs, haskellLib, ... }: with lib; with types; let - inherit (haskellLib.types) getDefaultOrNull listOfFilteringNulls uniqueStr; - - # NOTE: - # higher up settings work as default for the lower levels - # so project level doExactConfig sets the default for packages - # and package doExactConfig sets the default for its components - - packageOptions = def: { - preUnpack = mkOption { - type = nullOr lines; - default = def.preUnpack or null; - }; - postUnpack = mkOption { - type = nullOr uniqueStr; - default = getDefaultOrNull def "postUnpack"; - }; - prePatch = mkOption { - type = nullOr uniqueStr; - default = getDefaultOrNull def "prePatch"; - }; - postPatch = mkOption { - type = nullOr uniqueStr; - default = getDefaultOrNull def "postPatch"; - }; - preConfigure = mkOption { - type = nullOr uniqueStr; - default = getDefaultOrNull def "preConfigure"; - }; - postConfigure = mkOption { - type = nullOr uniqueStr; - default = getDefaultOrNull def "postConfigure"; - }; - preBuild = mkOption { - type = nullOr uniqueStr; - default = getDefaultOrNull def "preBuild"; - }; - postBuild = mkOption { - type = nullOr uniqueStr; - default = getDefaultOrNull def "postBuild"; - }; - preCheck = mkOption { - type = nullOr uniqueStr; - default = getDefaultOrNull def "preCheck"; - }; - # Wrapper for test executable run in checkPhase - testWrapper = mkOption { - type = listOfFilteringNulls str; - default = def.testWrapper or [ ]; - description = "A command to run for executing tests in checkPhase, which takes the original test command as its arguments."; - example = "echo"; - }; - postCheck = mkOption { - type = nullOr uniqueStr; - default = getDefaultOrNull def "postCheck"; - }; - preInstall = mkOption { - type = nullOr uniqueStr; - default = getDefaultOrNull def "preInstall"; - }; - postInstall = mkOption { - type = nullOr uniqueStr; - default = getDefaultOrNull def "postInstall"; - }; - preHaddock = mkOption { - type = nullOr uniqueStr; - default = getDefaultOrNull def "preHaddock"; - }; - postHaddock = mkOption { - type = nullOr uniqueStr; - default = getDefaultOrNull def "postHaddock"; - }; - hardeningDisable = mkOption { - type = listOfFilteringNulls str; - default = def.hardeningDisable or [ ]; - }; - ghcOptions = mkOption { - type = listOfFilteringNulls str; - default = def.ghcOptions or [ ]; - }; - contentAddressed = mkOption { - type = bool; - default = def.contentAddressed or false; - description = '' - Build content addressed derivation, requires Nix to have experimental feature - `ca-derivations` enabled. - ''; - }; - planned = mkOption { - description = "Set to true by `plan-to-nix` for any component that was included in the `plan.json` file."; - # This is here so that (rather than in componentOptions) so it can be set project wide for stack projects - type = bool; - default = def.planned or false; - }; - }; - package = submodule [ { _module.args = { @@ -119,23 +12,23 @@ let inherit (config.cabal) system compiler; }; } - (import ./package.nix { - inherit packageOptions; - parentConfig = config; - }) + ./package.nix # pass down common options as default values ({ lib, options, ... }: lib.mkDefault (lib.filterAttrs (n: _v: builtins.hasAttr n options) config)) ]; in { - imports = [ ./component-options.nix ]; + imports = [ + ./component-options.nix + ./package-options.nix + ]; # Global options. These are passed down to the package level, and from there to the # component level, unless specifically overridden. Depending on the flag flags are # combined or replaced. We seed the package Options with an empty set forcing the # default values. - options = (packageOptions { }) // { + options = { packages = mkOption { type = attrsOf package; }; From 07737e8690e732e304f2373ef990dbe92e0a597b Mon Sep 17 00:00:00 2001 From: Andrea Bedini Date: Wed, 12 Jul 2023 14:57:14 +0800 Subject: [PATCH 08/17] Move component out --- modules/component.nix | 101 ++++++++++++++++++++++++++++++++++++++++++ modules/package.nix | 98 +--------------------------------------- 2 files changed, 103 insertions(+), 96 deletions(-) create mode 100644 modules/component.nix diff --git a/modules/component.nix b/modules/component.nix new file mode 100644 index 0000000000..5ab9e25ca4 --- /dev/null +++ b/modules/component.nix @@ -0,0 +1,101 @@ +{ lib, haskellLib, ... }: + +let + inherit (lib) types; + inherit (haskellLib.types) listOfFilteringNulls; + +in +{ + imports = [ + ./component-options.nix + ./package-options.nix + ]; + + options = { + plugins = lib.mkOption { + type = types.listOf (types.submodule { + options = { + library = lib.mkOption { + type = types.unspecified; + }; + moduleName = lib.mkOption { + type = types.str; + }; + args = lib.mkOption { + type = types.listOf types.str; + default = [ ]; + }; + }; + }); + default = [ ]; + }; + depends = lib.mkOption { + type = listOfFilteringNulls types.unspecified; + default = [ ]; + }; + libs = lib.mkOption { + type = listOfFilteringNulls (types.either (types.nullOr types.package) (listOfFilteringNulls types.package)); + default = [ ]; + }; + frameworks = lib.mkOption { + type = listOfFilteringNulls types.package; + default = [ ]; + }; + pkgconfig = lib.mkOption { + type = types.listOf (listOfFilteringNulls types.package); + default = [ ]; + }; + build-tools = lib.mkOption { + type = listOfFilteringNulls types.unspecified; + default = [ ]; + }; + modules = lib.mkOption { + type = listOfFilteringNulls types.unspecified; + default = [ ]; + }; + asmSources = lib.mkOption { + type = listOfFilteringNulls types.unspecified; + default = [ ]; + }; + cmmSources = lib.mkOption { + type = listOfFilteringNulls types.unspecified; + default = [ ]; + }; + cSources = lib.mkOption { + type = listOfFilteringNulls types.unspecified; + default = [ ]; + }; + cxxSources = lib.mkOption { + type = listOfFilteringNulls types.unspecified; + default = [ ]; + }; + jsSources = lib.mkOption { + type = listOfFilteringNulls types.unspecified; + default = [ ]; + }; + hsSourceDirs = lib.mkOption { + type = listOfFilteringNulls types.unspecified; + default = [ "." ]; + }; + includeDirs = lib.mkOption { + type = listOfFilteringNulls types.unspecified; + default = [ ]; + }; + includes = lib.mkOption { + type = listOfFilteringNulls types.unspecified; + default = [ ]; + }; + mainPath = lib.mkOption { + type = listOfFilteringNulls types.unspecified; + default = [ ]; + }; + extraSrcFiles = lib.mkOption { + type = listOfFilteringNulls types.unspecified; + default = [ ]; + }; + platforms = lib.mkOption { + type = types.nullOr (listOfFilteringNulls types.unspecified); + default = null; + }; + }; +} diff --git a/modules/package.nix b/modules/package.nix index ef44c34628..e4b361271c 100644 --- a/modules/package.nix +++ b/modules/package.nix @@ -9,102 +9,8 @@ let path = types.path // { check = x: types.path.check (x.origSrc or x); }; componentType = types.submodule [ - { - imports = [ - ./component-options.nix - ./package-options.nix - ]; - options = { - plugins = lib.mkOption { - type = types.listOf (types.submodule { - options = { - library = lib.mkOption { - type = types.unspecified; - }; - moduleName = lib.mkOption { - type = types.str; - }; - args = lib.mkOption { - type = types.listOf types.str; - default = [ ]; - }; - }; - }); - default = [ ]; - }; - depends = lib.mkOption { - type = listOfFilteringNulls types.unspecified; - default = [ ]; - }; - libs = lib.mkOption { - type = listOfFilteringNulls (types.either (types.nullOr types.package) (listOfFilteringNulls types.package)); - default = [ ]; - }; - frameworks = lib.mkOption { - type = listOfFilteringNulls types.package; - default = [ ]; - }; - pkgconfig = lib.mkOption { - type = types.listOf (listOfFilteringNulls types.package); - default = [ ]; - }; - build-tools = lib.mkOption { - type = listOfFilteringNulls types.unspecified; - default = [ ]; - }; - modules = lib.mkOption { - type = listOfFilteringNulls types.unspecified; - default = [ ]; - }; - asmSources = lib.mkOption { - type = listOfFilteringNulls types.unspecified; - default = [ ]; - }; - cmmSources = lib.mkOption { - type = listOfFilteringNulls types.unspecified; - default = [ ]; - }; - cSources = lib.mkOption { - type = listOfFilteringNulls types.unspecified; - default = [ ]; - }; - cxxSources = lib.mkOption { - type = listOfFilteringNulls types.unspecified; - default = [ ]; - }; - jsSources = lib.mkOption { - type = listOfFilteringNulls types.unspecified; - default = [ ]; - }; - hsSourceDirs = lib.mkOption { - type = listOfFilteringNulls types.unspecified; - default = [ "." ]; - }; - includeDirs = lib.mkOption { - type = listOfFilteringNulls types.unspecified; - default = [ ]; - }; - includes = lib.mkOption { - type = listOfFilteringNulls types.unspecified; - default = [ ]; - }; - mainPath = lib.mkOption { - type = listOfFilteringNulls types.unspecified; - default = [ ]; - }; - extraSrcFiles = lib.mkOption { - type = listOfFilteringNulls types.unspecified; - default = [ ]; - }; - platforms = lib.mkOption { - type = types.nullOr (listOfFilteringNulls types.unspecified); - default = null; - }; - }; - config = { - _module.args = { inherit haskellLib; }; - }; - } + ./component.nix + { _module.args = { inherit haskellLib; }; } # pass down common options as default values ({ lib, options, ... }: lib.mkDefault (lib.filterAttrs (n: _v: builtins.hasAttr n options) config)) ]; From 23dc4358d5f2cfe54b8c7960640cd1b607c8f214 Mon Sep 17 00:00:00 2001 From: Andrea Bedini Date: Thu, 13 Jul 2023 16:13:56 +0800 Subject: [PATCH 09/17] Fix bug --- modules/package.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/package.nix b/modules/package.nix index e4b361271c..c5ef05d018 100644 --- a/modules/package.nix +++ b/modules/package.nix @@ -239,7 +239,7 @@ in # you can build. Instead it is used internally when `configureAllComponents` # is set or for tests whe on `cabal-doctest` is in the `setup-depends` of the package. allComponent = lib.mkOption { - type = types.componentType; + type = componentType; apply = all: all // { # TODO: Should this check for the entire component # definition to match, rather than just the identifier? From a7060cc78a187d861849602b3791dba46b67ab89 Mon Sep 17 00:00:00 2001 From: Andrea Bedini Date: Wed, 12 Jul 2023 16:02:02 +0800 Subject: [PATCH 10/17] Minor adjustments Formatting --- modules/component.nix | 20 ++++++++++++++++++++ modules/package-options.nix | 27 +++++++++++++++++++++------ modules/package.nix | 30 +++++++++++++++++++++++++++--- 3 files changed, 68 insertions(+), 9 deletions(-) diff --git a/modules/component.nix b/modules/component.nix index 5ab9e25ca4..384ce10c61 100644 --- a/modules/component.nix +++ b/modules/component.nix @@ -18,81 +18,101 @@ in library = lib.mkOption { type = types.unspecified; }; + moduleName = lib.mkOption { type = types.str; }; + args = lib.mkOption { type = types.listOf types.str; default = [ ]; }; }; }); + default = [ ]; }; + depends = lib.mkOption { type = listOfFilteringNulls types.unspecified; default = [ ]; }; + libs = lib.mkOption { type = listOfFilteringNulls (types.either (types.nullOr types.package) (listOfFilteringNulls types.package)); default = [ ]; }; + frameworks = lib.mkOption { type = listOfFilteringNulls types.package; default = [ ]; }; + pkgconfig = lib.mkOption { type = types.listOf (listOfFilteringNulls types.package); default = [ ]; }; + build-tools = lib.mkOption { type = listOfFilteringNulls types.unspecified; default = [ ]; }; + modules = lib.mkOption { type = listOfFilteringNulls types.unspecified; default = [ ]; }; + asmSources = lib.mkOption { type = listOfFilteringNulls types.unspecified; default = [ ]; }; + cmmSources = lib.mkOption { type = listOfFilteringNulls types.unspecified; default = [ ]; }; + cSources = lib.mkOption { type = listOfFilteringNulls types.unspecified; default = [ ]; }; + cxxSources = lib.mkOption { type = listOfFilteringNulls types.unspecified; default = [ ]; }; + jsSources = lib.mkOption { type = listOfFilteringNulls types.unspecified; default = [ ]; }; + hsSourceDirs = lib.mkOption { type = listOfFilteringNulls types.unspecified; default = [ "." ]; }; + includeDirs = lib.mkOption { type = listOfFilteringNulls types.unspecified; default = [ ]; }; + includes = lib.mkOption { type = listOfFilteringNulls types.unspecified; default = [ ]; }; + mainPath = lib.mkOption { type = listOfFilteringNulls types.unspecified; default = [ ]; }; + extraSrcFiles = lib.mkOption { type = listOfFilteringNulls types.unspecified; default = [ ]; }; + platforms = lib.mkOption { type = types.nullOr (listOfFilteringNulls types.unspecified); default = null; diff --git a/modules/package-options.nix b/modules/package-options.nix index c5a3f9dd4a..4f4a84b80a 100644 --- a/modules/package-options.nix +++ b/modules/package-options.nix @@ -1,80 +1,94 @@ { lib, haskellLib, ... }: -let - inherit (haskellLib.types) listOfFilteringNulls; -in { options = { preUnpack = lib.mkOption { type = lib.types.nullOr lib.types.lines; default = null; }; + postUnpack = lib.mkOption { type = lib.types.nullOr haskellLib.types.uniqueStr; default = null; }; + prePatch = lib.mkOption { type = lib.types.nullOr haskellLib.types.uniqueStr; default = null; }; + postPatch = lib.mkOption { type = lib.types.nullOr haskellLib.types.uniqueStr; default = null; }; + preConfigure = lib.mkOption { type = lib.types.nullOr haskellLib.types.uniqueStr; default = null; }; + postConfigure = lib.mkOption { type = lib.types.nullOr haskellLib.types.uniqueStr; default = null; }; + preBuild = lib.mkOption { type = lib.types.nullOr haskellLib.types.uniqueStr; default = null; }; + postBuild = lib.mkOption { type = lib.types.nullOr haskellLib.types.uniqueStr; default = null; }; + preCheck = lib.mkOption { type = lib.types.nullOr haskellLib.types.uniqueStr; default = null; }; + # Wrapper for test executable run in checkPhase testWrapper = lib.mkOption { - type = listOfFilteringNulls lib.types.str; + type = haskellLib.types.listOfFilteringNulls lib.types.str; default = [ ]; description = "A command to run for executing tests in checkPhase, which takes the original test command as its arguments."; example = "echo"; }; + postCheck = lib.mkOption { type = lib.types.nullOr haskellLib.types.uniqueStr; default = null; }; + preInstall = lib.mkOption { type = lib.types.nullOr haskellLib.types.uniqueStr; default = null; }; + postInstall = lib.mkOption { type = lib.types.nullOr haskellLib.types.uniqueStr; default = null; }; + preHaddock = lib.mkOption { type = lib.types.nullOr haskellLib.types.uniqueStr; default = null; }; + postHaddock = lib.mkOption { type = lib.types.nullOr haskellLib.types.uniqueStr; default = null; }; + hardeningDisable = lib.mkOption { - type = listOfFilteringNulls lib.types.str; + type = haskellLib.types.listOfFilteringNulls lib.types.str; default = [ ]; }; + ghcOptions = lib.mkOption { - type = listOfFilteringNulls lib.types.str; + type = haskellLib.types.listOfFilteringNulls lib.types.str; default = [ ]; }; + contentAddressed = lib.mkOption { type = lib.types.bool; default = false; @@ -83,6 +97,7 @@ in `ca-derivations` enabled. ''; }; + planned = lib.mkOption { description = "Set to true by `plan-to-nix` for any component that was included in the `plan.json` file."; # This is here so that (rather than in componentOptions) so it can be set project wide for stack projects diff --git a/modules/package.nix b/modules/package.nix index c5ef05d018..c91a1f50a4 100644 --- a/modules/package.nix +++ b/modules/package.nix @@ -26,11 +26,12 @@ in # their flags argument. config._module.args = { inherit (config) flags; }; + # TODO: Add descriptions to everything. options = { - # TODO: Add descriptions to everything. flags = lib.mkOption { type = types.attrsOf types.bool; }; + package = { specVersion = lib.mkOption { type = types.str; @@ -164,26 +165,32 @@ in platforms = null; }; }; + library = lib.mkOption { type = types.nullOr componentType; default = null; }; + sublibs = lib.mkOption { type = types.attrsOf componentType; default = { }; }; + foreignlibs = lib.mkOption { type = types.attrsOf componentType; default = { }; }; + exes = lib.mkOption { type = types.attrsOf componentType; default = { }; }; + tests = lib.mkOption { type = types.attrsOf componentType; default = { }; }; + benchmarks = lib.mkOption { type = types.attrsOf componentType; default = { }; @@ -195,14 +202,25 @@ in default = "${config.package.identifier.name}-${config.package.identifier.version}"; defaultText = "\${config.package.identifier.name}-\${config.package.identifier.version}"; }; + sha256 = lib.mkOption { type = types.nullOr types.str; default = null; }; + src = lib.mkOption { type = types.either path types.package; - default = pkgs.fetchurl { url = "mirror://hackage/${config.name}.tar.gz"; inherit (config) sha256; }; - defaultText = "pkgs.fetchurl { url = \"mirror://hackage/\${config.name}.tar.gz\"; inherit (config) sha256; };"; + default = + pkgs.fetchurl { + url = "mirror://hackage/${config.name}.tar.gz"; + inherit (config) sha256; + }; + defaultText = '' + pkgs.fetchurl { + url = "mirror://hackage/${config.name}.tar.gz"; + inherit (config) sha256; + }; + ''; # Make sure paths have a context so they will be included in the derivation # inputs for the component derivations. Without this sandbox builds fail # cannot see the input and fail with the error: @@ -213,27 +231,33 @@ in then builtins.appendContext v { ${builtins.head storeDirMatch} = { path = true; }; } else v; }; + package-description-override = lib.mkOption { type = types.nullOr types.str; default = null; description = "Cabal file to use instead of the one shipped inside the package source distribution."; }; + cabal-generator = lib.mkOption { type = types.nullOr types.str; default = null; }; + revision = lib.mkOption { type = types.nullOr types.int; default = null; }; + revisionSha256 = lib.mkOption { type = types.nullOr types.str; default = null; }; + patches = lib.mkOption { type = types.listOf (types.either types.unspecified path); default = [ ]; }; + # This used to be `components.all` but it has been added back as `allComponent` to # to avoid confusion. It is not mapped by `builder/hspkg-builder.nix` to anything # you can build. Instead it is used internally when `configureAllComponents` From e03ad4c49f5fdae259dd6ba443c5b5677a581b83 Mon Sep 17 00:00:00 2001 From: Andrea Bedini Date: Mon, 17 Jul 2023 18:19:42 +0800 Subject: [PATCH 11/17] AH! --- modules/package.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/package.nix b/modules/package.nix index c91a1f50a4..f6f02af319 100644 --- a/modules/package.nix +++ b/modules/package.nix @@ -217,7 +217,7 @@ in }; defaultText = '' pkgs.fetchurl { - url = "mirror://hackage/${config.name}.tar.gz"; + url = "mirror://hackage/$'{config.name}.tar.gz"; inherit (config) sha256; }; ''; From 48fa8dd410b865588416b2e9d026b1eb56ec986e Mon Sep 17 00:00:00 2001 From: Andrea Bedini Date: Mon, 17 Jul 2023 21:49:57 +0800 Subject: [PATCH 12/17] Fix project mkFlake after #1993 Rewrite mkFlakeApps, mkFlakeChecks and mkFlakePackages --- lib/default.nix | 75 +++++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/lib/default.nix b/lib/default.nix index 4dc1358917..4531f25497 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -39,17 +39,19 @@ in { foldComponents = tys: f: z: conf: let - comps = conf.components or {}; + comps = conf.components or { }; # ensure that comps.library exists and is not null. - libComp = acc: if (comps.library or null) != null then f comps.library acc else acc; + libComp = acc: + if comps ? library then f comps.library acc else acc; subComps = acc: lib.foldr - (ty: acc': foldrAttrVals f acc' (comps.${ty} or {})) + (ty: acc': foldrAttrVals f acc' (comps.${ty} or { })) acc tys; - in libComp (subComps z); + in + libComp (subComps z); - getAllComponents = foldComponents subComponentTypes (c: acc: [c] ++ acc) []; + getAllComponents = foldComponents subComponentTypes (c: acc: [ c ] ++ acc) [ ]; componentPrefix = { sublibs = "lib"; @@ -424,36 +426,47 @@ in { }]) (packageNames coverageProject)); # Flake package names that are flat and match the cabal component names. - mkFlakePackages = haskellPackages: builtins.listToAttrs ( - lib.concatLists (lib.mapAttrsToList (packageName: package: - builtins.groupBy - (c: c.passthru.identifier.component-id) - ((lib.optional (package.components ? library) package.components.library) - ++ package.components.sublibs - ++ package.components.exes - ++ package.components.tests - ++ package.components.benchmarks) - ) haskellPackages)); + mkFlakePackages = + foldrAttrVals + (package: acc: + foldComponents + subComponentTypes + (component: a: a // { + ${component.passthru.identifier.component-id} = component; + }) + acc + package) + { }; # Flake package names that are flat and match the cabal component names. - mkFlakeApps = haskellPackages: builtins.listToAttrs ( - lib.concatLists (lib.mapAttrsToList (packageName: package: - builtins.groupBy - (c: c.passthru.identifier.component-id) - (package.components.exes - ++ package.components.tests - ++ package.components.benchmarks) - ) haskellPackages)); + mkFlakeApps = + foldrAttrVals + (package: acc: + foldComponents + [ "exes" "tests" "benchmarks" ] + (component: a: a // { + ${component.passthru.identifier.component-id} = { + type = "app"; + program = component.exePath; + }; + }) + acc + package) + { }; # Flatten the result of collectChecks or collectChecks' for use in flake `checks` - mkFlakeChecks = allChecks: builtins.listToAttrs ( - lib.concatLists (lib.mapAttrsToList (packageName: checks: - # Avoid `recurseForDerivations` issues - lib.optionals (lib.isAttrs checks) ( - lib.mapAttrsToList (n: v: - { name = "${packageName}:test:${n}"; value = v; }) - (lib.filterAttrs (_: lib.isDerivation) checks)) - ) allChecks)); + mkFlakeChecks = allChecks: + foldrAttrVals + (package: acc: + foldrAttrVals + (check: a: a // { + ${check.passthru.identifier.component-id} = check; + }) + acc + package) + { } + # Remove `recurseForDerivations` + (lib.filterAttrsRecursive (n: v: n != "recurseForDerivations") allChecks); removeRecurseForDerivations = x: let clean = builtins.removeAttrs x ["recurseForDerivations"]; From cfb53bafad3a9d505159434548e013ca7b1f7e65 Mon Sep 17 00:00:00 2001 From: Andrea Bedini Date: Mon, 17 Jul 2023 21:59:32 +0800 Subject: [PATCH 13/17] Lint --- flake.nix | 13 ++++++------- hix/default.nix | 2 +- hix/project/default.nix | 9 +++------ lib/default.nix | 2 +- overlays/hix.nix | 5 ++--- 5 files changed, 13 insertions(+), 18 deletions(-) diff --git a/flake.nix b/flake.nix index 581cabeef0..ee8aac6b0f 100644 --- a/flake.nix +++ b/flake.nix @@ -73,7 +73,7 @@ traceNames = prefix: builtins.mapAttrs (n: v: if builtins.isAttrs v then if v ? type && v.type == "derivation" - then __trace (prefix + n) v + then builtins.trace (prefix + n) v else traceNames (prefix + n + ".") v else v); @@ -103,7 +103,7 @@ # flake outputs so that we can incorporate the args passed # to the compat layer (e.g. sourcesOverride). overlays = [ allOverlays.combined ] - ++ (if checkMaterialization == true then + ++ (if checkMaterialization then [ (final: prev: { haskell-nix = prev.haskell-nix // { @@ -161,7 +161,7 @@ # Exposed so that buildkite can check that `allow-import-from-derivation=false` works for core of haskell.nix roots = legacyPackagesUnstable.haskell-nix.roots compiler; - packages = ((self.internal.compat { inherit system; }).hix).apps; + packages = (self.internal.compat { inherit system; }).hix.apps; allJobs = let @@ -180,15 +180,14 @@ let nixpkgsJobs = allJobs.${nixpkgsVer}; in lib.concatMap (compiler-nix-name: let ghcJobs = nixpkgsJobs.${compiler-nix-name}; - in ( - builtins.map (crossPlatform: { + in builtins.map (crossPlatform: { name = "required-${nixpkgsVer}-${compiler-nix-name}-${crossPlatform}"; value = legacyPackages.releaseTools.aggregate { name = "haskell.nix-${nixpkgsVer}-${compiler-nix-name}-${crossPlatform}"; meta.description = "All ${nixpkgsVer} ${compiler-nix-name} ${crossPlatform} jobs"; - constituents = lib.collect (d: lib.isDerivation d) ghcJobs.${crossPlatform}; + constituents = lib.collect lib.isDerivation ghcJobs.${crossPlatform}; }; - }) (names ghcJobs)) + }) (names ghcJobs) ) (names nixpkgsJobs) ) (names allJobs)); diff --git a/hix/default.nix b/hix/default.nix index 5b9cc97467..c23123f914 100644 --- a/hix/default.nix +++ b/hix/default.nix @@ -90,7 +90,7 @@ let echo "Updating $FLAKE/flake.nix and deleting old $FLAKE/flake.lock" rm $FLAKE/flake.lock else - echo "Updating $FLAKE/flake.nix" + echo "Updating $FLAKE/flake.nix" fi cp $HIX_FLAKE $FLAKE/flake.nix chmod +w $FLAKE/flake.nix diff --git a/hix/project/default.nix b/hix/project/default.nix index 785119046f..1f1db532c3 100644 --- a/hix/project/default.nix +++ b/hix/project/default.nix @@ -30,11 +30,8 @@ let then [] else [{ inherit name; value = commandArgs.${name}; }] ) (builtins.attrNames commandArgs)); - defaultArgs = { - nixpkgsPin = "nixpkgs-unstable"; - }; importDefaults = src: - if src == null || !(__pathExists src) + if src == null || !(builtins.pathExists src) then {} else import src; userDefaults = importDefaults (commandArgs.userDefaults or null); @@ -64,7 +61,7 @@ let commandArgs' ({config, ...}: { src = - if __pathExists (toString (src.origSrcSubDir or src) + "/.git") + if builtins.pathExists (toString (src.origSrcSubDir or src) + "/.git") then config.evalPackages.haskell-nix.haskellLib.cleanGit { inherit src name; } @@ -73,5 +70,5 @@ let ]; }) ]; - }).config) project shell; + }).config) project; in project diff --git a/lib/default.nix b/lib/default.nix index 4531f25497..6cd3fd474f 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -213,7 +213,7 @@ in { # # See docs/user-guide/clean-git.md for details of how to use this # with `cabalProject`. - cleanGits = { src, gitDirs, name ? null, caller ? "cleanGits" }@args: + cleanGits = { src, gitDirs, name ? null, caller ? "cleanGits" }: let # List of filters, one for each git directory. filters = builtins.map (subDir: diff --git a/overlays/hix.nix b/overlays/hix.nix index 7432283196..97d5c1c368 100644 --- a/overlays/hix.nix +++ b/overlays/hix.nix @@ -9,7 +9,6 @@ final: prev: { haskell-nix = prev.haskell-nix // { hix = { , ...}@commandArgs: let inherit (final) lib; - hixDefaults = { compiler-nix-name = lib.mkDefault "ghc8107"; }; inherit ((lib.evalModules { modules = [ (import ../modules/project-common.nix) @@ -31,7 +30,7 @@ final: prev: { haskell-nix = prev.haskell-nix // { hix = { else [{ inherit name; value = commandArgs.${name}; }] ) (builtins.attrNames commandArgs)); importDefaults = src: - if src == null || !(__pathExists src) + if src == null || !(builtins.pathExists src) then {} else import src; projectDefaults = importDefaults (toString (src.origSrcSubDir or src) + "/nix/hix.nix"); @@ -41,7 +40,7 @@ final: prev: { haskell-nix = prev.haskell-nix // { hix = { commandArgs' ({config, ...}: { src = - if __pathExists (toString (src.origSrcSubDir or src) + "/.git") + if builtins.pathExists (toString (src.origSrcSubDir or src) + "/.git") then config.evalPackages.haskell-nix.haskellLib.cleanGit { inherit src name; } From aa5d7b14e94110816fb2f4be0771c90e47a740e3 Mon Sep 17 00:00:00 2001 From: Andrea Bedini Date: Tue, 18 Jul 2023 00:12:02 +0800 Subject: [PATCH 14/17] Use pre-existing removeRecurseForDerivations --- lib/default.nix | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/default.nix b/lib/default.nix index 6cd3fd474f..5d491e5c82 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -465,8 +465,7 @@ in { acc package) { } - # Remove `recurseForDerivations` - (lib.filterAttrsRecursive (n: v: n != "recurseForDerivations") allChecks); + (removeRecurseForDerivations allChecks); removeRecurseForDerivations = x: let clean = builtins.removeAttrs x ["recurseForDerivations"]; From ce2636e9663cc0049483c74001a38042e151e913 Mon Sep 17 00:00:00 2001 From: Hamish Mackenzie Date: Tue, 25 Jul 2023 15:23:48 +1200 Subject: [PATCH 15/17] ifdLevel 0 --- ci.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci.nix b/ci.nix index 84f06a4ac6..9744c10be4 100644 --- a/ci.nix +++ b/ci.nix @@ -1,6 +1,6 @@ # 'supportedSystems' restricts the set of systems that we will evaluate for. Useful when you're evaluating # on a machine with e.g. no way to build the Darwin IFDs you need! -{ ifdLevel ? 3 +{ ifdLevel ? 0 # Whether or not we are evaluating in restricted mode. This is true in Hydra, but not in Hercules. , restrictEval ? false , checkMaterialization ? false From a3d83612bfdf9e3947751af6d10da648aec22b13 Mon Sep 17 00:00:00 2001 From: Hamish Mackenzie Date: Tue, 25 Jul 2023 15:34:30 +1200 Subject: [PATCH 16/17] ifdLevel 1 --- ci.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci.nix b/ci.nix index 9744c10be4..53e45482f7 100644 --- a/ci.nix +++ b/ci.nix @@ -1,6 +1,6 @@ # 'supportedSystems' restricts the set of systems that we will evaluate for. Useful when you're evaluating # on a machine with e.g. no way to build the Darwin IFDs you need! -{ ifdLevel ? 0 +{ ifdLevel ? 1 # Whether or not we are evaluating in restricted mode. This is true in Hydra, but not in Hercules. , restrictEval ? false , checkMaterialization ? false From f3318f496c8b6793be1fdc1744a4267f4c543f9b Mon Sep 17 00:00:00 2001 From: Hamish Mackenzie Date: Tue, 25 Jul 2023 22:24:14 +1200 Subject: [PATCH 17/17] ifdLevel 3 --- ci.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci.nix b/ci.nix index 53e45482f7..84f06a4ac6 100644 --- a/ci.nix +++ b/ci.nix @@ -1,6 +1,6 @@ # 'supportedSystems' restricts the set of systems that we will evaluate for. Useful when you're evaluating # on a machine with e.g. no way to build the Darwin IFDs you need! -{ ifdLevel ? 1 +{ ifdLevel ? 3 # Whether or not we are evaluating in restricted mode. This is true in Hydra, but not in Hercules. , restrictEval ? false , checkMaterialization ? false