diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..6fa9717 --- /dev/null +++ b/flake.nix @@ -0,0 +1,9 @@ +{ + description = "Composable Nix Modules"; + + outputs = _: { + core = import ./src/core/mod.nix; + importAtom = import ./src/core/importAtom.nix; + mkAtomicFlake = import ./legacy-nix/mkAtomicFlake.nix; + }; +} diff --git a/legacy-nix/mkAtomicFlake.nix b/legacy-nix/mkAtomicFlake.nix new file mode 100644 index 0000000..905493d --- /dev/null +++ b/legacy-nix/mkAtomicFlake.nix @@ -0,0 +1,59 @@ +{ + manifest, + noSystemManifest ? null, + perSystemNames ? [ + "checks" + "packages" + "apps" + "formatter" + "devShells" + "hydraJobs" + ], + systems ? [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ], +}: + +let + npinsSrcs = import ../src/npins; + lib = import (npinsSrcs."nixpkgs.lib" + "/lib"); + l = lib // builtins; + + importAtom = import ../src/core/importAtom.nix; + + noSystemAtom = importAtom { } noSystemManifest; + + verifyNoSystem = + atomManifest: + let + atomConfig = l.fromTOML (l.readFile atomManifest); + features = atomConfig.features.default or [ ]; + hasSystemFeature = l.elem "system" features; + in + !hasSystemFeature; + + hasNoSystemAtom = noSystemManifest != null && verifyNoSystem noSystemManifest; + + optionalNoSystemAtom = if hasNoSystemAtom then noSystemAtom else { }; + + transformedAtomFromSystem = + system: + let + evaluatedAtom = importAtom { inherit system; } manifest; + # perSystemAtomAttributes = l.getAttrs perSystemNames evaluatedAtom; + mkPerSystemValue = _: value: { ${system} = value; }; + in + l.mapAttrs mkPerSystemValue evaluatedAtom; + + accumulate = accumulator: set: accumulator // set; + combineSets = _: sets: l.foldl' accumulate { } sets; + + transformedAtoms = l.map transformedAtomFromSystem systems; + + combinedPerSystemAttributes = l.zipAttrsWith combineSets transformedAtoms; + +in +optionalNoSystemAtom // combinedPerSystemAttributes diff --git a/src/core/compose.nix b/src/core/compose.nix index 55b957d..de475a5 100644 --- a/src/core/compose.nix +++ b/src/core/compose.nix @@ -56,11 +56,13 @@ let l = builtins; core = import ./mod.nix; + in -{ +input@{ src, root, config, + system ? null, extern ? { }, features ? [ ], # internal features of the composer function @@ -78,6 +80,8 @@ let inherit __internal__test; } (../. + "/std@.toml"); + systemIsDefinedAndEnabled = system != null && l.elem "system" features; + coreFeatures' = core.features.resolve core.coreToml.features coreFeatures; stdFeatures' = core.features.resolve core.stdToml.features stdFeatures; @@ -112,7 +116,7 @@ let import = errors.import; scopedImport = errors.import; __fetchurl = errors.fetch; - __currentSystem = errors.system; + __currentSystem = errors.currentSystem; __currentTime = errors.time 0; __nixPath = errors.nixPath [ ]; __storePath = errors.storePath; @@ -134,6 +138,14 @@ let _if = __isStd__; std = l.removeAttrs (extern // atom) [ "std" ]; } + { + _if = !__isStd__; + system = if systemIsDefinedAndEnabled then input.system else core.errors.system; + } + { + _if = !__isStd__; + use = l.removeAttrs extern [ "std" ]; + } { _if = __internal__test; # information about the internal module system itself @@ -197,7 +209,7 @@ let # Base case: no module { }; - atomScope = l.removeAttrs (extern // atom // { inherit extern; }) [ + atomScope = l.removeAttrs atom [ "atom" (baseNameOf par) ]; diff --git a/src/core/errors.nix b/src/core/errors.nix index b7e3f42..f86b92c 100644 --- a/src/core/errors.nix +++ b/src/core/errors.nix @@ -44,7 +44,8 @@ in modFromDir (stripParentDir par path); import = abort "Importing arbitrary Nix files is forbidden. Declare your dependencies via the module system instead."; fetch = abort "Ad hoc fetching is illegal. Declare dependencies statically in the manifest instead."; - system = abort "Accessing the current system is impure. Declare supported systems in the manifest."; + currentSystem = abort "Accessing the current system is impure. Declare system in the manifest."; + system = abort "`system` has been accessed and is undefined"; time = _: warn "currentTime: Ignoring request for current time, returning: 0"; nixPath = _: warn "nixPath: ignoring impure NIX_PATH request, returning: []"; storePath = abort "Making explicit dependencies on store paths is illegal."; diff --git a/src/core/importAtom.nix b/src/core/importAtom.nix index f86ed6f..c85aa80 100644 --- a/src/core/importAtom.nix +++ b/src/core/importAtom.nix @@ -15,104 +15,149 @@ revalidating on the Nix side as an extra precaution, but initially, we just assume we have a valid input (and the CLI should type check on it's end) */ -{ - features ? null, - __internal__test ? false, -}: -path': let - mod = import ./mod.nix; + importAtom = + importAtomArgs@{ + system ? null, + features ? null, + __internal__test ? false, + }: + path': + let + mod = import ./mod.nix; - path = mod.prepDir path'; + path = mod.prepDir path'; + root = mod.prepDir (dirOf path); # TODO Is prepDir required twice? - file = builtins.readFile path; - config = builtins.fromTOML file; - atom = config.atom or { }; - id = builtins.seq version (atom.id or (mod.errors.missingAtom path' "id")); - version = atom.version or (mod.errors.missingAtom path' "version"); + file = builtins.readFile path; + config = builtins.fromTOML file; + atom = config.atom or { }; + id = builtins.seq version (atom.id or (mod.errors.missingAtom path' "id")); + version = atom.version or (mod.errors.missingAtom path' "version"); + core = config.core or { }; + std = config.std or { }; + meta = atom.meta or { }; - core = config.core or { }; - std = config.std or { }; + features = + let + atomFeatures = importAtomArgs.features or null; + featSet = config.features or { }; + default = featSet.default or [ ]; + argsHaveNoFeatures = atomFeatures == null; + featIn = if argsHaveNoFeatures then default else atomFeatures; + in + mod.features.resolve featSet featIn; - features' = - let - featSet = config.features or { }; - featIn = if features == null then featSet.default or [ ] else features; - in - mod.features.resolve featSet featIn; + backend = config.backend or { }; + nix = backend.nix or { }; - backend = config.backend or { }; - nix = backend.nix or { }; + src = builtins.seq id ( + let + file = mod.parse (baseNameOf path); + len = builtins.stringLength file.name; + in + builtins.substring 0 (len - 1) file.name + ); - root = mod.prepDir (dirOf path); - src = builtins.seq id ( - let - file = mod.parse (baseNameOf path); - len = builtins.stringLength file.name; - in - builtins.substring 0 (len - 1) file.name - ); - 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 - mod.filterMap ( - k: v: + extern = let - src = "${pins.${v.name or k}}/${v.subdir or ""}"; - val = - if v.import or false then - if v.args or [ ] != [ ] then - builtins.foldl' ( - f: x: - let - intersect = x // (builtins.intersectAttrs x extern); - in - if builtins.isAttrs x then f intersect else f x - ) (import src) v.args - else - import src + # TODO native doesn't exist yet + fetcher = nix.fetcher or "native"; + throwMissingNativeFetcher = abort "Native fetcher isn't implemented yet"; + + fetcherConfig = config.fetcher or { }; + npinRoot = fetcherConfig.npin.root or "npins"; + pins = import (dirOf path + "/${npinRoot}"); + + local = config.local or { }; + + isDepEnabled = + depName: depConfig: + let + optional = depConfig.optional or false; + featureIsEnabled = builtins.elem depName features; + in + (optional && featureIsEnabled) || (!optional); + + obtainLocalDep = + depName: depConfig: + let + depManifest = "${root}/${depConfig.path}"; + depIsEnabled = isDepEnabled depName depConfig; + dependency = importAtom { inherit system; } depManifest; + in + if depIsEnabled then { "${depName}" = dependency; } else null; + + obtainedLocalDeps = mod.filterMap obtainLocalDep local; + + localDeps = if local != { } then obtainedLocalDeps else { }; + + fetchEnabledNpinsDep = + depName: depConfig: + let + importDep = depConfig.import or false; + depArgs = depConfig.args or [ ]; + depHasArgs = depArgs != [ ]; + depIsEnabled = isDepEnabled depName depConfig; + + npinSrc = "${pins.${depConfig.name or depName}}/${depConfig.subdir or ""}"; + + applyArguments = + appliedFunction: nextArgument: + let + argsFromDeps = depConfig.argsFromDeps or true && builtins.isAttrs nextArgument; + argIntersectedwithDeps = nextArgument // (builtins.intersectAttrs nextArgument extern); + in + if argsFromDeps nextArgument then + appliedFunction argIntersectedwithDeps + else + appliedFunction nextArgument; + + importedSrcWithArgs = builtins.foldl' applyArguments (import npinSrc) depArgs; + + importedSrc = if depHasArgs then importedSrcWithArgs else import npinSrc; + + dependency = if importDep then importedSrc else npinSrc; + in + if depIsEnabled then { "${depName}" = dependency; } else null; + + npinsDeps = mod.filterMap fetchEnabledNpinsDep config.fetch or { }; + + externalDeps = + if fetcher == "npins" then + npinsDeps + else if fetcher == "native" then + throwMissingNativeFetcher else - src; - in - if (v.optional or false && builtins.elem k features') || (!v.optional or false) then - { "${k}" = val; } - else - null - ) config.fetch or { } - # else if fetcher = "native", etc - else - { }; + { }; - meta = atom.meta or { }; + in + localDeps // externalDeps; -in -mod.compose { - inherit - extern - __internal__test - config - root - src - ; - features = features'; - coreFeatures = - let - feat = core.features or mod.coreToml.features.default; - in - mod.features.resolve mod.coreToml.features feat; - stdFeatures = - let - feat = std.features or mod.stdToml.features.default; in - mod.features.resolve mod.stdToml.features feat; + mod.compose { + inherit + src + root + config + system + extern + features + __internal__test + ; + coreFeatures = + let + feat = core.features or mod.coreToml.features.default; + in + mod.features.resolve mod.coreToml.features feat; + stdFeatures = + let + feat = std.features or mod.stdToml.features.default; + in + mod.features.resolve mod.stdToml.features feat; - __isStd__ = meta.__is_std__ or false; -} + __isStd__ = meta.__is_std__ or false; + }; + +in +importAtom diff --git a/src/dev/shell.nix b/src/dev/shell.nix index 8c77a8f..e22c625 100644 --- a/src/dev/shell.nix +++ b/src/dev/shell.nix @@ -1,5 +1,5 @@ { - pkgs ? atom.pkgs, + pkgs ? use.pkgs, }: pkgs.mkShell { packages = with pkgs; [