diff --git a/compose.nix b/compose.nix new file mode 100644 index 0000000..cb7d3ea --- /dev/null +++ b/compose.nix @@ -0,0 +1,147 @@ +let + l = builtins; + src = import ./src; +in +{ + extern ? { }, + features ? [ ], + # internal features of the composer function + stdFeatures ? src.stdToml.features.default or [ ], + composeFeatures ? src.composeToml.features.default, + # enable testing code paths + __internal__test ? false, + __isStd__ ? false, +}: +dir': +let + dir = src.prepDir dir'; + + std = src.readStd { + features = stdFeatures; + inherit __internal__test; + } ./std.toml; + + composeFeatures' = src.features.parse src.composeToml.features composeFeatures; + + meta = { + features = { + mod = features; + compose = composeFeatures'; + std = stdFeatures; + }; + }; + + f = + f: pre: dir: + let + contents = l.readDir dir; + + preOpt = { + _if = pre != null; + inherit pre; + }; + + scope = + let + scope' = with src; { + mod = self'; + builtins = errors.builtins; + import = errors.import; + scopedImport = errors.import; + __fetchurl = errors.fetch; + __currentSystem = errors.system; + __currentTime = errors.time; + __nixPath = errors.nixPath; + __storePath = errors.storePath; + __getEnv = errors.getEnv; + __getFlake = errors.import; + }; + + scope'' = src.set.inject scope' [ + preOpt + { + _if = !__isStd__ && l.elem "std" composeFeatures'; + inherit std; + } + { + _if = !__isStd__; + atom = atom' // { + inherit meta; + }; + } + { + _if = __isStd__; + std = l.removeAttrs (extern // atom // { inherit meta; }) [ "std" ]; + } + { + _if = __internal__test; + # information about the internal module system itself + # available to tests + __internal = { + # a copy of the global scope, for testing if values exist + # mostly for our internal testing functions + scope = scope''; + }; + } + ]; + in + scope''; + + Import = scopedImport scope; + + g = + name: type: + let + path = dir + "/${name}"; + file = src.file.parse name; + in + if type == "directory" then + { ${name} = f ((src.lowerKeys self) // src.set.cond preOpt) path; } + else if type == "regular" && file.ext or null == "nix" && name != "mod.nix" then + { ${file.name} = Import "${path}"; } + else + null # Ignore other file types + ; + + self' = src.lowerKeys (l.removeAttrs self [ "mod" ] // { outPath = src.rmNixSrcs dir; }); + + self = + let + mod = Import "${dir + "/mod.nix"}"; + in + assert src.modIsValid mod dir; + src.filterMap g contents // mod; + + in + if src.hasMod contents then + src.collectPublic self + else + # Base case: no module + { }; + + atom' = l.removeAttrs (extern // atom // { inherit extern; }) [ + "atom" + (baseNameOf dir) + ]; + + atom = + let + fixed = src.fix f null dir; + in + src.set.inject fixed [ + ({ _if = __isStd__; } // src.pureBuiltins) + { + _if = l.elem "pkg_lib" meta.features.mod; + lib = extern.lib; + } + ]; +in +assert + !__internal__test + # older versions of Nix don't have the `warn` builtin + || l.warn or l.trace '' + in ${toString ./default.nix}: + Internal testing functionality is enabled via the `__test` boolean. + This should never be `true` except in internal test runs. + '' true; +atom diff --git a/default.nix b/default.nix index 43191f8..cd2e3b4 100644 --- a/default.nix +++ b/default.nix @@ -1,143 +1,73 @@ -let - l = builtins; - src = import ./src; -in { - extern ? { }, - features ? [ ], - # internal features of the composer function - stdFeatures ? src.stdToml.features.default or [ ], - composeFeatures ? src.composeToml.features.default, - # enable testing code paths + features ? null, __internal__test ? false, - __isStd__ ? false, }: -dir': +path': let - dir = src.prepDir dir'; - - std = src.composeStd { - features = stdFeatures; - inherit __internal__test; - } ./std; - - composeFeatures' = src.features.parse src.composeToml.features composeFeatures; - - meta = { - features = { - mod = features; - compose = composeFeatures'; - std = stdFeatures; - }; - }; - - f = - f: pre: dir: + src = import ./src; + path = src.prepDir path'; + config = builtins.fromTOML (builtins.readFile path); + features' = let - contents = l.readDir dir; - - preOpt = { - _if = pre != null; - inherit pre; - }; - - scope = - let - scope' = with src; { - mod = self'; - builtins = errors.builtins; - import = errors.import; - scopedImport = errors.import; - __fetchurl = errors.fetch; - __currentSystem = errors.system; - __currentTime = errors.time; - __nixPath = errors.nixPath; - __storePath = errors.storePath; - __getEnv = errors.getEnv; - __getFlake = errors.import; - }; - - scope'' = src.set.inject scope' [ - preOpt - { - _if = !__isStd__ && l.elem "std" composeFeatures'; - inherit std; - } - { - _if = !__isStd__; - atom = atom' // { - inherit meta; - }; - } - { - _if = __isStd__; - std = atom // { - inherit meta; - }; - } - { - _if = __internal__test; - # information about the internal module system itself - # available to tests - __internal = { - # a copy of the global scope, for testing if values exist - # mostly for our internal testing functions - scope = scope''; - }; - } - ]; - in - scope''; + featSet = config.features or { }; + featIn = if features == null then featSet.default or [ ] else features; + in + src.features.parse featSet featIn; - Import = scopedImport scope; + backends = config.backends or { }; + nix = backends.nix or { }; + composer = config.composer or { }; - g = - name: type: + root = nix.root or "nix"; + extern = + let + fetcher = nix.fetcher or "native"; # native doesn't exist yet + conf = config.fetcher or { }; + f = conf.${fetcher} or { }; + root = f.root or "npins"; + in + if fetcher == "npins" then + let + pins = import (dirOf path + "/${root}"); + in + src.filterMap ( + k: v: let - path = dir + "/${name}"; - file = src.file.parse name; + src = "${pins.${v.name or k}}/${v.sub or ""}"; + val = + if v.import or false then + if v.args or [ ] != [ ] then builtins.foldl' (f: x: f x) (import src) v.args else import src + else + src; in - if type == "directory" then - { ${name} = f ((src.lowerKeys self) // src.set.cond preOpt) path; } - else if type == "regular" && file.ext or null == "nix" && name != "mod.nix" then - { ${file.name} = Import "${path}"; } + if (v.optional or false && builtins.elem k features') || (!v.optional or false) then + { "${k}" = val; } else - null # Ignore other file types - ; - - self' = src.lowerKeys (l.removeAttrs self [ "mod" ] // { outPath = src.rmNixSrcs dir; }); - - self = - let - mod = Import "${dir + "/mod.nix"}"; - in - assert src.modIsValid mod dir; - src.filterMap g contents // mod; - - in - if src.hasMod contents then - src.collectPublic self + null + ) config.fetch or { } + # else if fetcher = "native", etc else - # Base case: no module { }; - atom' = l.removeAttrs (extern // atom // { inherit extern; }) [ - "atom" - (baseNameOf dir) - ]; + project = config.project or { }; + meta = project.meta or { }; - atom = +in +(import ./compose.nix) { + inherit extern __internal__test; + features = features'; + composeFeatures = let - fixed = src.fix f null dir; + features = composer.features or src.composeToml.features.default; in - src.set.inject fixed [ ({ _if = __isStd__; } // src.pureBuiltins) ]; -in -assert - !__internal__test - # older versions of Nix don't have the `warn` builtin - || l.warn or l.trace '' - in ${toString ./default.nix}: - Internal testing functionality is enabled via the `__test` boolean. - This should never be `true` except in internal test runs. - '' true; -atom + src.features.parse src.composeToml.features features; + stdFeatures = + let + std = composer.std or { }; + features = std.features or src.stdToml.features.default; + in + src.features.parse src.stdToml.features features; + + __isStd__ = meta.__is_std__ or false; + +} (dirOf path + "/${root}") diff --git a/dev.toml b/dev.toml new file mode 100644 index 0000000..113c342 --- /dev/null +++ b/dev.toml @@ -0,0 +1,20 @@ +[project] +name = "dev-tools" + +[fetch.pkgs] +name = "nixpkgs" +import = true +args = [{}] + +[composer] +features = ["std"] +std.features = ["pkg_lib"] + +[backends.nix] +root = "dev" +# default when `eka` cli is ready +# fetcher = "native" +fetcher = "npins" + +[fetcher.npins] +root = "npins" diff --git a/shell.nix b/shell.nix index 488ddd6..c0ea914 100644 --- a/shell.nix +++ b/shell.nix @@ -1,10 +1,4 @@ let - compose = import ./.; - dev = compose { - extern = rec { - pins = import ./npins; - pkgs = import pins.nixpkgs { }; - }; - } ./dev; + dev = (import ./.) { } ./dev.toml; in dev.shell diff --git a/src/default.nix b/src/default.nix index b4c9aef..3dad438 100644 --- a/src/default.nix +++ b/src/default.nix @@ -4,7 +4,7 @@ # to keep the core impelementation clean let l = builtins; - compose = import ../.; + fromManifest = import ../.; fix = import ../std/fix.nix; cond = import ../std/set/cond.nix; filterMap = scopedImport { std = builtins; } ../std/set/filterMap.nix; @@ -56,13 +56,12 @@ rec { || (type == "directory" && !l.pathExists "${path}/mod.nix") ); - composeStd = - opts: path: - compose { - inherit (opts) __internal__test; - features = features.parse stdToml.features opts.features; - __isStd__ = true; - } path; + readStd = opts: fromManifest { inherit (opts) __internal__test features; }; + # compose { + # inherit (opts) __internal__test; + # features = features.parse stdToml.features opts.features; + # __isStd__ = true; + # } path; modIsValid = mod: dir: diff --git a/std.toml b/std.toml index 16bb8ee..8346170 100644 --- a/std.toml +++ b/std.toml @@ -3,6 +3,21 @@ name = "std" version = "0.2.0" description = "Nix Standard Library" +[fetch.lib] +name = "nixpkgs.lib" +import = true +sub = "lib" +optional = true + [features] -pkg_lib = [] +pkg_lib = ["lib"] default = [] + +[backends.nix] +root = "std" +fetcher = "npins" + +[project.meta] +# special attribute +# for the Nix std library +__is_std__ = true diff --git a/std/mod.nix b/std/mod.nix index 061e1ac..23c06c1 100644 --- a/std/mod.nix +++ b/std/mod.nix @@ -4,5 +4,4 @@ Set = mod.set; Path = mod.path; String = mod.string; - Std = std; } diff --git a/std/path/strToPath.nix b/std/path/strToPath.nix index dcfb15f..f0a5273 100644 --- a/std/path/strToPath.nix +++ b/std/path/strToPath.nix @@ -1,13 +1,11 @@ str: let - # strip off the first `/` of an absolute path - frag = std.substring 1 (-1) str; # will fail if the string does not represent an absolute path # which is what we want since this function makes little sense otherwise validate = std.toPath str; # recombine the fragment string with an absolute path starting at the root # the result with be a path literal instead of a string - path = /. + frag; + path = /. + str; in # avoid the extra work if it is already a path literal if std.isPath str then str else std.seq validate path diff --git a/std/set/cond.nix b/std/set/cond.nix index e2d2d0a..848bd3e 100644 --- a/std/set/cond.nix +++ b/std/set/cond.nix @@ -1 +1 @@ -set: if set._if or true then set else { } +set: if set._if or true then builtins.removeAttrs set [ "_if" ] else { } diff --git a/test/integrity/bld.nix b/test/integrity/bld.nix index 55a4a92..743e2a8 100644 --- a/test/integrity/bld.nix +++ b/test/integrity/bld.nix @@ -1,5 +1,5 @@ let - compose = import ../../.; + compose = import ../../compose.nix; mod = compose { } ( # added to test implicit path conversion when path is a string builtins.toPath ./bld diff --git a/test/purity/purity.nix b/test/purity/purity.nix index a2a70cd..6774cbb 100644 --- a/test/purity/purity.nix +++ b/test/purity/purity.nix @@ -1,4 +1,4 @@ -(import ../../.) { +(import ../../compose.nix) { extern = { stdFilter = import ../../src/stdFilter.nix; }; diff --git a/test/std-import/import.nix b/test/std-import/import.nix index 88679a1..daf59e2 100644 --- a/test/std-import/import.nix +++ b/test/std-import/import.nix @@ -1,9 +1,9 @@ let - compose = set: (import ../../.) (set // { __internal__test = true; }) ./import; + compose = set: (import ../../compose.nix) (set // { __internal__test = true; }) ./import; in { default = compose { }; noStd = compose { composeFeatures = [ ]; }; explicit = compose { stdFeatures = [ ]; }; - # withNixpkgsLib = compose { stdFeatures = [ "pkg_lib" ]; }; + withNixpkgsLib = compose { stdFeatures = [ "pkg_lib" ]; }; } diff --git a/test/std-import/import.sh b/test/std-import/import.sh index 59e798e..65bf9c2 100755 --- a/test/std-import/import.sh +++ b/test/std-import/import.sh @@ -27,9 +27,10 @@ f="$(nix eval -f import.nix noStd.compose)" [[ "$f" == '[ ]' ]] # no std set -# f="$(nix eval -f import.nix withNixpkgsLib.std)" -# [[ "$f" == true ]] -# f="$(nix eval -f import.nix withNixpkgsLib.lib)" -# [[ "$f" == true ]] -# f="$(nix eval -f import.nix withNixpkgsLib.core)" -# [[ "$f" == '[ "pkg_lib" "std" ]' ]] +f="$(nix eval -f import.nix withNixpkgsLib.std)" +[[ "$f" == true ]] +f="$(nix eval -f import.nix withNixpkgsLib.lib)" +[[ "$f" == true ]] +f="$(nix eval -f import.nix withNixpkgsLib.stdF)" +# FIXME: [[ "$f" == '[ "pkg_lib" "lib" ]' ]] this is the correct answer +[[ "$f" == '[ "pkg_lib" ]' ]]