Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -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;
};
}
59 changes: 59 additions & 0 deletions legacy-nix/mkAtomicFlake.nix
Original file line number Diff line number Diff line change
@@ -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
18 changes: 15 additions & 3 deletions src/core/compose.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -78,6 +80,8 @@ let
inherit __internal__test;
} (../. + "/[email protected]");

systemIsDefinedAndEnabled = system != null && l.elem "system" features;

coreFeatures' = core.features.resolve core.coreToml.features coreFeatures;
stdFeatures' = core.features.resolve core.stdToml.features stdFeatures;

Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -197,7 +209,7 @@ let
# Base case: no module
{ };

atomScope = l.removeAttrs (extern // atom // { inherit extern; }) [
atomScope = l.removeAttrs atom [
"atom"
(baseNameOf par)
];
Expand Down
3 changes: 2 additions & 1 deletion src/core/errors.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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.";
Expand Down
221 changes: 133 additions & 88 deletions src/core/importAtom.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion src/dev/shell.nix
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
pkgs ? atom.pkgs,
pkgs ? use.pkgs,
}:
pkgs.mkShell {
packages = with pkgs; [
Expand Down