refactor: move dream2nix interface to modules

This commit is contained in:
Yusuf Bera Ertan 2022-11-20 09:03:09 +03:00
parent 4b4e82cb14
commit f44016a2d4
No known key found for this signature in database
GPG Key ID: 1D8F8FAF2294D6EA
32 changed files with 1042 additions and 1050 deletions

View File

@ -20,7 +20,7 @@
};
makeOutputs = pkgs: let
outputs = (initD2N pkgs).makeOutputs {
outputs = (initD2N pkgs).dream2nixInterface.makeOutputs {
source = inp.src;
settings = [
{

356
flake.nix
View File

@ -77,7 +77,6 @@
ghc-utils,
...
} @ inp: let
b = builtins;
l = lib // builtins;
lib = nixpkgs.lib;
@ -133,9 +132,6 @@
];
};
# create a directory containing the files listed in externalPaths
makeExternalDir = import ./src/utils/external-dir.nix;
# An interface to access files of external projects.
# This implementation accesses the flake inputs directly,
# but if dream2nix is used without flakes, it defaults
@ -168,212 +164,156 @@
system,
...
}: let
externalDir = makeExternalDir {
inherit externalPaths externalSources pkgs;
};
d2n = import ./src {
inherit externalDir externalPaths externalSources inputs lib pkgs;
config = {inherit overridesDirs;};
inherit externalPaths externalSources inputs lib pkgs;
dream2nixConfig = {inherit overridesDirs;};
};
docsCli = pkgs.callPackage ./src/utils/view-docs {
dream2nixDocsSrc = "${self}/docs/src";
};
in {
# all apps including cli, install, etc.
apps =
d2n.framework.flakeApps
// {
tests-unit.type = "app";
tests-unit.program =
b.toString
(d2n.callPackageDream ./tests/unit {
inherit self;
});
options = {
d2n = l.mkOption {
type = l.types.raw;
};
};
config = {
inherit d2n;
tests-integration.type = "app";
tests-integration.program =
b.toString
(d2n.callPackageDream ./tests/integration {
inherit self;
});
# all apps including cli, install, etc.
apps =
d2n.flakeApps
// {
# passes through extra flags to treefmt
format.type = "app";
format.program =
l.toString
(pkgs.writeScript "format" ''
export PATH="${alejandra.defaultPackage.${system}}/bin"
${pkgs.treefmt}/bin/treefmt --clear-cache "$@"
'');
tests-integration-d2n-flakes.type = "app";
tests-integration-d2n-flakes.program =
b.toString
(d2n.callPackageDream ./tests/integration-d2n-flakes {
inherit self;
});
docs.type = "app";
docs.program = "${docsCli}/bin/d2n-docs";
};
tests-examples.type = "app";
tests-examples.program =
b.toString
(d2n.callPackageDream ./tests/examples {
inherit self;
});
# a dev shell for working on dream2nix
# use via 'nix develop . -c $SHELL'
devShells = let
makeDevshell = import "${inp.devshell}/modules" pkgs;
mkShell = config:
(makeDevshell {
configuration = {
inherit config;
imports = [];
};
})
.shell;
in rec {
default = dream2nix-shell;
dream2nix-shell = mkShell {
devshell.name = "dream2nix-devshell";
tests-all.type = "app";
tests-all.program =
l.toString
(d2n.framework.utils.writePureShellScript
commands =
[
alejandra.defaultPackage.${system}
pkgs.coreutils
pkgs.gitMinimal
pkgs.nix
{package = pkgs.nix;}
{
package = pkgs.mdbook;
category = "documentation";
}
{
package = docsCli;
category = "documentation";
help = "CLI for listing and viewing dream2nix documentation";
}
{
package = pkgs.treefmt;
category = "formatting";
}
{
package = alejandra.defaultPackage.${system};
category = "formatting";
}
]
''
echo "check for correct formatting"
WORKDIR=$(realpath ./.)
cd $TMPDIR
cp -r $WORKDIR ./repo
cd ./repo
${config.apps.format.program} --fail-on-change
cd -
# using linux is highly recommended as cntr is amazing for debugging builds
++ lib.optional pkgs.stdenv.isLinux {
package = pkgs.cntr;
category = "debugging";
};
echo "running unit tests"
${config.apps.tests-unit.program}
devshell.startup = {
preCommitHooks.text = self.checks.${system}.pre-commit-check.shellHook;
dream2nixEnv.text = ''
export NIX_PATH=nixpkgs=${nixpkgs}
export d2nExternalDir=${d2n.externalDir}
export dream2nixWithExternals=${d2n.dream2nixWithExternals}
echo "running integration tests"
${config.apps.tests-integration.program}
echo "checking flakes under ./examples"
${config.apps.tests-examples.program}
echo "running nix flake check"
cd $WORKDIR
nix flake show >/dev/null
nix flake check
'');
# passes through extra flags to treefmt
format.type = "app";
format.program =
l.toString
(pkgs.writeScript "format" ''
export PATH="${alejandra.defaultPackage.${system}}/bin"
${pkgs.treefmt}/bin/treefmt --clear-cache "$@"
'');
docs.type = "app";
docs.program = "${docsCli}/bin/d2n-docs";
};
# a dev shell for working on dream2nix
# use via 'nix develop . -c $SHELL'
devShells = let
makeDevshell = import "${inp.devshell}/modules" pkgs;
mkShell = config:
(makeDevshell {
configuration = {
inherit config;
imports = [];
};
})
.shell;
in rec {
default = dream2nix-shell;
dream2nix-shell = mkShell {
devshell.name = "dream2nix-devshell";
commands =
[
{package = pkgs.nix;}
{
package = pkgs.mdbook;
category = "documentation";
}
{
package = docsCli;
category = "documentation";
help = "CLI for listing and viewing dream2nix documentation";
}
{
package = pkgs.treefmt;
category = "formatting";
}
{
package = alejandra.defaultPackage.${system};
category = "formatting";
}
]
# using linux is highly recommended as cntr is amazing for debugging builds
++ lib.optional pkgs.stdenv.isLinux {
package = pkgs.cntr;
category = "debugging";
};
devshell.startup = {
preCommitHooks.text = self.checks.${system}.pre-commit-check.shellHook;
dream2nixEnv.text = ''
export NIX_PATH=nixpkgs=${nixpkgs}
export d2nExternalDir=${externalDir}
export dream2nixWithExternals=${d2n.dream2nixWithExternals}
if [ -e ./overrides ]; then
export d2nOverridesDir=$(realpath ./overrides)
else
export d2nOverridesDir=${./overrides}
echo -e "\nManually execute 'export d2nOverridesDir={path to your dream2nix overrides dir}'"
fi
if [ -e ../dream2nix ]; then
export dream2nixWithExternals=$(realpath ./src)
else
export dream2nixWithExternals=${./src}
echo -e "\nManually execute 'export dream2nixWithExternals={path to your dream2nix checkout}'"
fi
'';
};
};
};
checks = {
pre-commit-check = pre-commit-hooks.lib.${system}.run {
src = ./.;
hooks = {
treefmt = {
enable = true;
name = "treefmt";
pass_filenames = false;
entry = l.toString (pkgs.writeScript "treefmt" ''
#!${pkgs.bash}/bin/bash
export PATH="$PATH:${alejandra.defaultPackage.${system}}/bin"
${pkgs.treefmt}/bin/treefmt --clear-cache --fail-on-change
'');
};
cleanup = {
enable = true;
name = "cleaned";
entry = l.toString (pkgs.writeScript "cleaned" ''
#!${pkgs.bash}/bin/bash
for badFile in $(find ./examples | grep 'flake.lock\|dream2nix-packages'); do
rm -rf $badFile
git add $badFile || :
done
'');
};
is-cleaned = {
enable = true;
name = "is-cleaned";
entry = l.toString (pkgs.writeScript "is-cleaned" ''
#!${pkgs.bash}/bin/bash
if find ./examples | grep -q 'flake.lock\|dream2nix-packages'; then
echo "./examples should not contain any flake.lock files or dream2nix-packages directories" >&2
exit 1
if [ -e ./overrides ]; then
export d2nOverridesDir=$(realpath ./overrides)
else
export d2nOverridesDir=${./overrides}
echo -e "\nManually execute 'export d2nOverridesDir={path to your dream2nix overrides dir}'"
fi
'');
if [ -e ../dream2nix ]; then
export dream2nixWithExternals=$(realpath ./src)
else
export dream2nixWithExternals=${./src}
echo -e "\nManually execute 'export dream2nixWithExternals={path to your dream2nix checkout}'"
fi
'';
};
};
};
};
packages = {
docs =
pkgs.runCommand
"dream2nix-docs"
{nativeBuildInputs = [pkgs.mdbook];}
''
mdbook build -d $out ${./.}/docs
'';
checks = {
pre-commit-check = pre-commit-hooks.lib.${system}.run {
src = ./.;
hooks = {
treefmt = {
enable = true;
name = "treefmt";
pass_filenames = false;
entry = l.toString (pkgs.writeScript "treefmt" ''
#!${pkgs.bash}/bin/bash
export PATH="$PATH:${alejandra.defaultPackage.${system}}/bin"
${pkgs.treefmt}/bin/treefmt --clear-cache --fail-on-change
'');
};
cleanup = {
enable = true;
name = "cleaned";
entry = l.toString (pkgs.writeScript "cleaned" ''
#!${pkgs.bash}/bin/bash
for badFile in $(find ./examples | grep 'flake.lock\|dream2nix-packages'); do
rm -rf $badFile
git add $badFile || :
done
'');
};
is-cleaned = {
enable = true;
name = "is-cleaned";
entry = l.toString (pkgs.writeScript "is-cleaned" ''
#!${pkgs.bash}/bin/bash
if find ./examples | grep -q 'flake.lock\|dream2nix-packages'; then
echo "./examples should not contain any flake.lock files or dream2nix-packages directories" >&2
exit 1
fi
'');
};
};
};
};
packages = {
docs =
pkgs.runCommand
"dream2nix-docs"
{nativeBuildInputs = [pkgs.mdbook];}
''
mdbook build -d $out ${./.}/docs
'';
};
};
};
@ -386,37 +326,13 @@
imports = [./src/modules/flake-parts];
dream2nix.lib = d2n-lib;
};
templates =
{
default = self.templates.simple;
simple = {
description = "Simple dream2nix flake";
path = ./templates/simple;
welcomeText = ''
You just created a simple dream2nix package!
start with typing `nix flake show` to discover the projects attributes.
commands:
- `nix develop` <-- enters the devShell
- `nix build .#` <-- builds the default package (`.#default`)
Start hacking and -_- have some fun!
> dont forget to add nix `result` folder to your `.gitignore`
'';
};
}
// (b.mapAttrs (name: value: {
description = "Example: ${name} template";
path = ./examples/${name};
}) (l.filterAttrs (n: v: v == "directory") (b.readDir ./examples)));
};
in
flake-parts.lib.mkFlake {inherit self;} {
imports = [
./tests
./templates
];
systems = [
"x86_64-linux"
"aarch64-linux"

View File

@ -14,7 +14,7 @@ utils.writePureShellScript
nixpkgs = <nixpkgs>;
l = (import \"\''${nixpkgs}/lib\") // builtins;
dream2nix = import ${dream2nixWithExternals} {
config = ''${dream2nixConfig:-"${dream2nixConfigFile}"};
dream2nixConfig = ''${dream2nixConfig:-"${dream2nixConfigFile}"};
};
in ''${@:$#}
"

View File

@ -30,7 +30,7 @@ utils.writePureShellScriptBin
resultBin="$TMPDIR/result"
${apps.callNixWithD2N} build -L --keep-failed --show-trace --out-link $resultBin \
"dream2nix.framework.indexers.$name.indexBin"
"dream2nix.indexers.$name.indexBin"
$resultBin $inputFile
''

View File

@ -60,7 +60,7 @@ in
if [ ! -e "$bin" ]; then
echo "building executable for translator $translator"
${apps.callNixWithD2N} build -o "$bin" "
dream2nix.framework.translators.$translator.finalTranslateBin
dream2nix.translators.$translator.finalTranslateBin
"
fi
done

View File

@ -45,7 +45,7 @@ utils.writePureShellScriptBin
translateBin="$TRANSLATOR_DIR/$translator"
else
translateBin=$(${apps.callNixWithD2N} build --print-out-paths --no-link "
dream2nix.framework.translators.$translator.finalTranslateBin
dream2nix.translators.$translator.finalTranslateBin
")
fi

View File

@ -19,7 +19,7 @@ pkgs.writers.writeBash
pkgs = import ${pkgs.path} {inherit system;};
d2n = import ${dream2nixWithExternals} {
inherit pkgs;
config = ''${dream2nixConfig:-"{}"};
dream2nixConfig = ''${dream2nixConfig:-"{}"};
};
src = d2n.fetchers.fetchSource {
source = b.fromJSON (b.readFile \"''${flakeSrcInfoPath:?"error: set source info path"}\");

View File

@ -1,747 +1 @@
# This is the system specific api for dream2nix.
# It requires passing one specific pkgs.
# If the intention is to generate output for several systems,
# use ./lib.nix instead.
{
pkgs ? import <nixpkgs> {},
lib ? pkgs.lib,
nix ? pkgs.nix,
# already validated config.
# this is mainly used by src/lib.nix since it loads the config beforehand.
loadedConfig ? null,
# default to empty dream2nix config. This is assumed to be not loaded.
config ?
if builtins ? getEnv && builtins.getEnv "dream2nixConfig" != ""
# if called via CLI, load config via env
then builtins.toPath (builtins.getEnv "dream2nixConfig")
# load from default directory
else {},
/*
Inputs that are not required for building, and therefore not need to be
copied alongside a dream2nix installation.
*/
inputs ?
(import ../flake-compat.nix {
src = ../.;
inherit (pkgs) system;
})
.inputs,
# dependencies of dream2nix builders
externalSources ?
lib.genAttrs
(lib.attrNames (builtins.readDir externalDir))
(inputName: "${/. + externalDir}/${inputName}"),
# will be defined if called via flake
externalPaths ? null,
# required for non-flake mode
externalDir ?
# if flake is used, construct external dir from flake inputs
if externalPaths != null
then
(import ./utils/external-dir.nix {
inherit externalPaths externalSources pkgs;
})
# if called via CLI, load externals via env
else if builtins ? getEnv && builtins.getEnv "d2nExternalDir" != ""
then /. + (builtins.getEnv "d2nExternalDir")
# load from default directory
else ./external,
} @ args: let
argsConfig = config;
in let
b = builtins;
l = lib // builtins;
config =
if loadedConfig != null
then loadedConfig
else
import ./modules/config.nix {
inherit lib;
rawConfig = argsConfig;
};
configFile = pkgs.writeText "dream2nix-config.json" (b.toJSON config);
framework = import ./modules/framework.nix {
inherit
inputs
lib
pkgs
externals
externalSources
dream2nixWithExternals
;
dream2nixConfigFile = configFile;
dream2nixConfig = config;
dream2nixInterface = {
inherit
makeOutputsForDreamLock
;
};
};
inherit (framework) dlib;
/*
The nixos module system seems to break pkgs.callPackage.
Therefore we always need to pass all of pkgs with callPackageDream.
callPackageDream should also be deprecated once all functionality is moved to
the module system.
*/
callPackageDreamArgs =
pkgs
// {
inherit callPackageDream;
inherit config;
inherit configFile;
inherit externals;
inherit externalSources;
inherit inputs;
inherit framework;
inherit dream2nixWithExternals;
inherit nix;
};
# like pkgs.callPackage, but includes all the dream2nix modules
callPackageDream = f: fargs:
(
if l.isFunction f
then f
else import f
) (callPackageDreamArgs // fargs);
inherit (framework) utils;
# updater modules to find newest package versions
updaters = callPackageDream ./updaters {};
externals = {
devshell = {
makeShell = import "${externalSources.devshell}/modules" pkgs;
imports.c = "${externalSources.devshell}/extra/language/c.nix";
};
crane = let
importLibFile = name: import "${externalSources.crane}/lib/${name}.nix";
makeHook = attrs: name:
pkgs.makeSetupHook
({inherit name;} // attrs)
"${externalSources.crane}/pkgs/${name}.sh";
genHooks = names: attrs: lib.genAttrs names (makeHook attrs);
in
{
cargoHostTarget,
cargoBuildBuild,
}: rec {
otherHooks =
genHooks [
"cargoHelperFunctions"
"configureCargoCommonVarsHook"
"configureCargoVendoredDepsHook"
]
{};
installHooks =
genHooks [
"inheritCargoArtifactsHook"
"installCargoArtifactsHook"
]
{
substitutions = {
zstd = "${pkgs.pkgsBuildBuild.zstd}/bin/zstd";
};
};
installLogHook = genHooks ["installFromCargoBuildLogHook"] {
substitutions = {
cargo = "${cargoBuildBuild}/bin/cargo";
jq = "${pkgs.pkgsBuildBuild.jq}/bin/jq";
};
};
# These aren't used by dream2nix
crateNameFromCargoToml = null;
vendorCargoDeps = null;
writeTOML = importLibFile "writeTOML" {
inherit (pkgs) runCommand pkgsBuildBuild;
};
cleanCargoToml = importLibFile "cleanCargoToml" {};
findCargoFiles = importLibFile "findCargoFiles" {
inherit (pkgs) lib;
};
mkDummySrc = importLibFile "mkDummySrc" {
inherit (pkgs) writeText runCommandLocal lib;
inherit writeTOML cleanCargoToml findCargoFiles;
};
mkCargoDerivation = importLibFile "mkCargoDerivation" {
cargo = cargoHostTarget;
inherit (pkgs) stdenv lib;
inherit
(installHooks)
inheritCargoArtifactsHook
installCargoArtifactsHook
;
inherit
(otherHooks)
configureCargoCommonVarsHook
configureCargoVendoredDepsHook
;
cargoHelperFunctionsHook = otherHooks.cargoHelperFunctions;
};
buildDepsOnly = importLibFile "buildDepsOnly" {
inherit
mkCargoDerivation
crateNameFromCargoToml
vendorCargoDeps
mkDummySrc
;
};
cargoBuild = importLibFile "cargoBuild" {
inherit
mkCargoDerivation
buildDepsOnly
crateNameFromCargoToml
vendorCargoDeps
;
};
buildPackage = importLibFile "buildPackage" {
inherit (pkgs) removeReferencesTo lib;
inherit (installLogHook) installFromCargoBuildLogHook;
inherit cargoBuild;
};
};
};
dreamOverrides = let
overridesDirs =
config.overridesDirs
++ (lib.optionals (b ? getEnv && b.getEnv "d2nOverridesDir" != "") [
(b.getEnv "d2nOverridesDir")
]);
in
utils.loadOverridesDirs overridesDirs pkgs;
# the location of the dream2nix framework for self references (update scripts, etc.)
dream2nixWithExternals =
if b.pathExists (./. + "/external")
then ./.
else
pkgs.runCommandLocal "dream2nix-full-src" {} ''
cp -r ${./.} $out
chmod +w $out
mkdir $out/external
cp -r ${externalDir}/* $out/external/
'';
# automatically find a suitable builder for a given dream lock
findBuilder = dreamLock: let
subsystem = dreamLock._generic.subsystem;
in
if ! framework.buildersBySubsystem ? ${subsystem}
then throw "Could not find any builder for subsystem '${subsystem}'"
else framework.buildersBySubsystem.${subsystem}.default;
# detect if granular or combined fetching must be used
findFetcher = dreamLock:
if null != dreamLock._generic.sourcesAggregatedHash or null
then framework.functions.combinedFetcher
else framework.functions.defaultFetcher;
# fetch only sources and do not build
fetchSources = {
dreamLock,
sourceRoot ? null,
fetcher ? null,
extract ? false,
sourceOverrides ? oldSources: {},
} @ args: let
# if dream lock is a file, read and parse it
dreamLock' = (utils.dream-lock.readDreamLock {inherit dreamLock;}).lock;
fetcher =
if args.fetcher or null == null
then findFetcher dreamLock'
else args.fetcher;
fetched = fetcher rec {
inherit sourceOverrides sourceRoot;
defaultPackage = dreamLock._generic.defaultPackage;
defaultPackageVersion = dreamLock._generic.packages."${defaultPackage}";
sources = dreamLock'.sources;
sourcesAggregatedHash = dreamLock'._generic.sourcesAggregatedHash;
};
fetchedSources = fetched.fetchedSources;
in
fetched
// {
fetchedSources =
if extract
then
lib.mapAttrs
(key: source: utils.extractSource {inherit source;})
fetchedSources
else fetchedSources;
};
# build a dream lock via a specific builder
callBuilder = {
builder,
builderArgs,
fetchedSources,
sourceRoot,
dreamLock,
inject,
sourceOverrides,
packageOverrides,
allOutputs,
} @ args: let
# inject dependencies
dreamLock = utils.dream-lock.injectDependencies args.dreamLock inject;
dreamLockInterface = (utils.dream-lock.readDreamLock {inherit dreamLock;}).interface;
produceDerivation = name: pkg:
utils.applyOverridesToPackage {
inherit pkg;
outputs = allOutputs;
pname = name;
conditionalOverrides = packageOverrides;
};
outputs = builder.build (builderArgs
// {
inherit
produceDerivation
dreamLock
sourceRoot
;
inherit
(dreamLockInterface)
subsystemAttrs
getSourceSpec
getRoot
getDependencies
getCyclicDependencies
defaultPackageName
defaultPackageVersion
packages
packageVersions
;
getSource = utils.dream-lock.getSource fetchedSources;
});
# Makes the packages tree compatible with flakes schema.
# For each package the attr `{pname}` will link to the latest release.
# Other package versions will be inside: `{pname}.versions`
# Adds a `default` package by using `defaultPackageName` and `defaultPackageVersion`.
formattedOutputs =
outputs
// {
packages = let
allPackages = outputs.packages or {};
latestPackages =
lib.mapAttrs'
(pname: releases: let
latest =
releases."${dlib.latestVersion (b.attrNames releases)}";
in (lib.nameValuePair
"${pname}"
(latest
// {
versions = releases;
})))
allPackages;
defaultPackage =
allPackages
."${dreamLockInterface.defaultPackageName}"
."${dreamLockInterface.defaultPackageVersion}";
in
latestPackages // {default = defaultPackage;};
};
in
formattedOutputs;
riseAndShine = throw ''
`riseAndShine` is deprecated. See usage in readme.md.
'';
makeOutputsForDreamLock = {
dreamLock,
sourceRoot ? null,
fetcher ? null,
builder ? null,
builderArgs ? {},
inject ? {},
sourceOverrides ? oldSources: {},
packageOverrides ? {},
} @ args: let
# parse dreamLock
dreamLockLoaded = utils.dream-lock.readDreamLock {inherit (args) dreamLock;};
dreamLock = dreamLockLoaded.lock;
dreamLockInterface = dreamLockLoaded.interface;
builder' =
if builder == null
then findBuilder dreamLock
else if l.isString builder
then framework.buildersBySubsystem.${dreamLock._generic.subsystem}.${builder}
else builder;
fetcher' =
if fetcher == null
then findFetcher dreamLock
else fetcher;
fetchedSources =
(fetchSources {
inherit dreamLock sourceOverrides sourceRoot;
fetcher = fetcher';
})
.fetchedSources;
builderOutputs = callBuilder {
inherit
dreamLock
fetchedSources
allOutputs
sourceOverrides
sourceRoot
;
builder = builder';
inherit builderArgs;
packageOverrides =
lib.recursiveUpdate
(dreamOverrides."${dreamLock._generic.subsystem}" or {})
(args.packageOverrides or {});
inject = args.inject or {};
};
allOutputs = builderOutputs;
in
allOutputs;
translateProjects = {
discoveredProjects ?
framework.functions.discoverers.discoverProjects
{inherit settings tree;},
projects ? {},
source ? throw "Pass either `source` or `tree` to translateProjects",
tree ? dlib.prepareSourceTree {inherit source;},
pname,
settings ? [],
} @ args: let
getTranslator = translatorName:
framework.translators.${translatorName};
isImpure = project: translatorName:
(getTranslator translatorName).type == "impure";
getInvalidationHash = project:
dlib.calcInvalidationHash {
inherit project source;
# TODO: add translatorArgs
translatorArgs = {};
translator = project.translator;
inherit config;
};
isResolved = project: let
dreamLockExists =
l.pathExists "${toString config.projectRoot}/${project.dreamLockPath}";
dreamLockValid =
project.dreamLock._generic.invalidationHash
or ""
== project.invalidationHash;
in
dreamLockExists && dreamLockValid;
getProjectKey = project: "${project.name}_|_${project.subsystem}_|_${project.relPath}";
# list of projects extended with some information requried for processing
projectsList =
l.map
(project: (let
self =
project
// rec {
dreamLock =
(utils.dream-lock.readDreamLock {
dreamLock = "${toString config.projectRoot}/${project.dreamLockPath}";
})
.lock;
impure = isImpure project translator;
invalidationHash = getInvalidationHash project;
key = getProjectKey project;
resolved = isResolved self;
translator = project.translator or (l.head project.translators);
};
in
self))
discoveredProjects;
# projects without existing valid dream-lock.json
projectsPureUnresolved =
l.filter
(project: ! project.resolved && ! project.impure)
projectsList;
# already resolved projects
projectsResolved =
l.filter
(project: project.resolved)
projectsList;
# list of pure projects extended with 'dreamLock' attribute
projectsResolvedOnTheFly =
l.forEach projectsPureUnresolved
(proj: let
translator = getTranslator proj.translator;
dreamLock'' = translator.finalTranslate {
inherit source tree discoveredProjects;
project = proj;
};
/*
simpleTranslate2 exposes result via `.result` in order to allow for
unit testing via `.inputs`.
*/
dreamLock' = dreamLock''.result or dreamLock'';
dreamLock =
dreamLock'
// {
_generic =
dreamLock'._generic
// {
invalidationHash = proj.invalidationHash;
};
};
in
proj
// {
inherit dreamLock;
});
resolvedProjects = projectsResolved ++ projectsResolvedOnTheFly;
in
resolvedProjects;
# transform a list of resolved projects to buildable outputs
realizeProjects = {
inject ? {},
translatedProjects ? translateProjects {inherit pname settings source;},
# alternative way of calling (for debugging)
pname ? null,
source ? null,
packageOverrides ? {},
sourceOverrides ? oldSources: {},
settings ? [],
}: let
dreamLocks = l.forEach translatedProjects (proj: proj.dreamLock);
defaultSourceOverride = dreamLock:
if source == null
then {}
else let
defaultPackage = dreamLock._generic.defaultPackage;
defaultPackageVersion =
dreamLock._generic.packages."${defaultPackage}";
in {
"${defaultPackage}"."${defaultPackageVersion}" = "${source}/${dreamLock._generic.location}";
};
# extends each package with a `.resolve` attribute
# and applies sourceOverrides
outputsForProject = proj: let
outputs = makeOutputsForDreamLock {
inherit inject packageOverrides;
sourceRoot = source;
builder = proj.builder or null;
dreamLock = proj.dreamLock;
sourceOverrides = oldSources:
dlib.recursiveUpdateUntilDepth
1
(defaultSourceOverride proj.dreamLock)
(sourceOverrides oldSources);
};
in
outputs
// {
packages =
l.mapAttrs
(pname: pkg:
pkg.overrideAttrs (old: {
passthru =
old.passthru
or {}
// {
resolve = utils.makeTranslateScript {
inherit source;
invalidationHash = proj.invalidationHash;
project = proj;
};
};
}))
(outputs.packages or {});
};
projectOutputs = l.map outputsForProject translatedProjects;
in
dlib.mergeFlakes projectOutputs;
generateImpureResolveScript = {
source,
impureProjects,
}: let
impureResolveScriptsList =
l.listToAttrs
(
l.map
(
project:
l.nameValuePair
"Name: ${project.name}; Subsystem: ${project.subsystem or "?"}; relPath: ${project.relPath}"
(utils.makeTranslateScript {inherit project source;})
)
impureProjects
);
resolveImpureScript =
utils.writePureShellScriptBin
"resolve"
[]
''
${l.concatStringsSep "\n"
(l.mapAttrsToList
(title: script: ''
echo "Resolving:: ${title}"
${script}/bin/resolve
'')
impureResolveScriptsList)}
'';
in
resolveImpureScript;
makeOutputs = {
source ? throw "pass a 'source' to 'makeOutputs'",
discoveredProjects ?
framework.functions.discoverers.discoverProjects {
inherit settings source;
},
pname ? null,
projects ? {},
settings ? [],
packageOverrides ? {},
sourceOverrides ? old: {},
inject ? {},
}: let
# if projects are defined manually, ignore discoveredProjects
finalProjects =
if projects != {}
then let
projectsList = l.attrValues projects;
in
# skip discovery and just add required attributes to project list
l.forEach projectsList
(proj:
proj
// {
relPath = proj.relPath or "";
translator = proj.translator or (l.head proj.translators);
dreamLockPath =
framework.functions.discoverers.getDreamLockPath
proj
(l.head projectsList);
})
else discoveredProjects;
impureProjects =
l.filter
(proj:
framework.translators."${proj.translator}".type
== "impure")
finalProjects;
resolveImpureScript = generateImpureResolveScript {
inherit impureProjects source;
};
translatedProjects = translateProjects {
discoveredProjects = finalProjects;
inherit
pname
settings
source
;
};
realizedProjects = realizeProjects {
inherit
inject
packageOverrides
sourceOverrides
translatedProjects
source
;
};
impureFakeDerivations =
l.listToAttrs
(l.map
(proj:
l.nameValuePair
proj.name
rec {
type = "derivation";
name = proj.name;
resolve = utils.makeTranslateScript {
project = proj;
inherit source;
};
drvPath = throw ''
The ${proj.subsystem} package ${proj.name} contains unresolved impurities.
Resolve by running the .resolve attribute of this derivation
or by resolving all impure projects by running the `resolveImpure` package
'';
})
impureProjects);
in
realizedProjects
// {
packages =
l.warnIf
(realizeProjects.packages.resolveImpure or null != null)
''
a builder outputted a package named 'resolveImpure'
this will be overridden by dream2nix!
''
impureFakeDerivations
// (realizedProjects.packages or {})
// {resolveImpure = resolveImpureScript;};
};
in {
inherit
callPackageDream
dream2nixWithExternals
framework
fetchSources
realizeProjects
translateProjects
riseAndShine
updaters
makeOutputsForDreamLock
makeOutputs
;
}
import ./modules/framework.nix

View File

@ -12,10 +12,9 @@
l = lib // builtins;
initDream2nix = config: pkgs:
import ./default.nix
{
loadedConfig = config;
inherit inputs pkgs externalPaths externalSources;
import ./modules/framework.nix {
inherit lib inputs pkgs externalPaths externalSources;
dream2nixConfig = config;
};
loadConfig = config'': let
@ -89,13 +88,9 @@
config = loadConfig (args.config or {});
framework = import ./modules/framework.nix {
inherit lib externalSources inputs;
inherit lib externalPaths externalSources inputs;
dream2nixConfig = config;
dream2nixConfigFile = l.toFile "dream2nix-config.json" (l.toJSON config);
pkgs = throw "pkgs is not available before nixpkgs is imported";
externals = throw "externals is not available before nixpkgs is imported";
dream2nixWithExternals = throw "not available before nixpkgs is imported";
dream2nixInterface = throw "not available before nixpkgs is imported";
};
systems =
@ -140,7 +135,7 @@
l.mapAttrs
(system: pkgs: let
dream2nix = dream2nixFor."${system}";
allOutputs = dream2nix.makeOutputs {
allOutputs = dream2nix.dream2nixInterface.makeOutputs {
discoveredProjects = finalProjects;
inherit
source
@ -193,7 +188,7 @@
l.mapAttrs
(system: pkgs: let
dream2nix = dream2nixFor."${system}";
allOutputs = dream2nix.framework.utils.makeOutputsForIndexes {
allOutputs = dream2nix.utils.makeOutputsForIndexes {
inherit
source
indexes

View File

@ -0,0 +1,6 @@
{
imports = [
./implementation.nix
./interface.nix
];
}

View File

@ -0,0 +1,523 @@
{config, ...}: let
l = config.lib // builtins;
b = builtins;
inherit (config) dream2nixConfig pkgs utils dlib lib;
dreamOverrides = let
overridesDirs =
config.dream2nixConfig.overridesDirs
++ (lib.optionals (b ? getEnv && b.getEnv "d2nOverridesDir" != "") [
(b.getEnv "d2nOverridesDir")
]);
in
utils.loadOverridesDirs overridesDirs pkgs;
# automatically find a suitable builder for a given dream lock
findBuilder = dreamLock: let
subsystem = dreamLock._generic.subsystem;
in
if ! config.buildersBySubsystem ? ${subsystem}
then throw "Could not find any builder for subsystem '${subsystem}'"
else config.buildersBySubsystem.${subsystem}.default;
# detect if granular or combined fetching must be used
findFetcher = dreamLock:
if null != dreamLock._generic.sourcesAggregatedHash or null
then config.functions.combinedFetcher
else config.functions.defaultFetcher;
# fetch only sources and do not build
fetchSources = {
dreamLock,
sourceRoot ? null,
fetcher ? null,
extract ? false,
sourceOverrides ? oldSources: {},
} @ args: let
# if dream lock is a file, read and parse it
dreamLock' = (utils.dream-lock.readDreamLock {inherit dreamLock;}).lock;
fetcher =
if args.fetcher or null == null
then findFetcher dreamLock'
else args.fetcher;
fetched = fetcher rec {
inherit sourceOverrides sourceRoot;
defaultPackage = dreamLock._generic.defaultPackage;
defaultPackageVersion = dreamLock._generic.packages."${defaultPackage}";
sources = dreamLock'.sources;
sourcesAggregatedHash = dreamLock'._generic.sourcesAggregatedHash;
};
fetchedSources = fetched.fetchedSources;
in
fetched
// {
fetchedSources =
if extract
then
lib.mapAttrs
(key: source: utils.extractSource {inherit source;})
fetchedSources
else fetchedSources;
};
# build a dream lock via a specific builder
callBuilder = {
builder,
builderArgs,
fetchedSources,
sourceRoot,
dreamLock,
inject,
sourceOverrides,
packageOverrides,
allOutputs,
} @ args: let
# inject dependencies
dreamLock = utils.dream-lock.injectDependencies args.dreamLock inject;
dreamLockInterface = (utils.dream-lock.readDreamLock {inherit dreamLock;}).interface;
produceDerivation = name: pkg:
utils.applyOverridesToPackage {
inherit pkg;
outputs = allOutputs;
pname = name;
conditionalOverrides = packageOverrides;
};
outputs = builder.build (builderArgs
// {
inherit
produceDerivation
dreamLock
sourceRoot
;
inherit
(dreamLockInterface)
subsystemAttrs
getSourceSpec
getRoot
getDependencies
getCyclicDependencies
defaultPackageName
defaultPackageVersion
packages
packageVersions
;
getSource = utils.dream-lock.getSource fetchedSources;
});
# Makes the packages tree compatible with flakes schema.
# For each package the attr `{pname}` will link to the latest release.
# Other package versions will be inside: `{pname}.versions`
# Adds a `default` package by using `defaultPackageName` and `defaultPackageVersion`.
formattedOutputs =
outputs
// {
packages = let
allPackages = outputs.packages or {};
latestPackages =
lib.mapAttrs'
(pname: releases: let
latest =
releases."${dlib.latestVersion (b.attrNames releases)}";
in (lib.nameValuePair
"${pname}"
(latest
// {
versions = releases;
})))
allPackages;
defaultPackage =
allPackages
."${dreamLockInterface.defaultPackageName}"
."${dreamLockInterface.defaultPackageVersion}";
in
latestPackages // {default = defaultPackage;};
};
in
formattedOutputs;
riseAndShine = throw ''
`riseAndShine` is deprecated. See usage in readme.md.
'';
makeOutputsForDreamLock = {
dreamLock,
sourceRoot ? null,
fetcher ? null,
builder ? null,
builderArgs ? {},
inject ? {},
sourceOverrides ? oldSources: {},
packageOverrides ? {},
} @ args: let
# parse dreamLock
dreamLockLoaded = utils.dream-lock.readDreamLock {inherit (args) dreamLock;};
dreamLock = dreamLockLoaded.lock;
dreamLockInterface = dreamLockLoaded.interface;
builder' =
if builder == null
then findBuilder dreamLock
else if l.isString builder
then config.buildersBySubsystem.${dreamLock._generic.subsystem}.${builder}
else builder;
fetcher' =
if fetcher == null
then findFetcher dreamLock
else fetcher;
fetchedSources =
(fetchSources {
inherit dreamLock sourceOverrides sourceRoot;
fetcher = fetcher';
})
.fetchedSources;
builderOutputs = callBuilder {
inherit
dreamLock
fetchedSources
allOutputs
sourceOverrides
sourceRoot
;
builder = builder';
inherit builderArgs;
packageOverrides =
lib.recursiveUpdate
(dreamOverrides."${dreamLock._generic.subsystem}" or {})
(args.packageOverrides or {});
inject = args.inject or {};
};
allOutputs = builderOutputs;
in
allOutputs;
translateProjects = {
discoveredProjects ?
config.functions.discoverers.discoverProjects
{inherit settings tree;},
projects ? {},
source ? throw "Pass either `source` or `tree` to translateProjects",
tree ? dlib.prepareSourceTree {inherit source;},
pname,
settings ? [],
} @ args: let
getTranslator = translatorName:
config.translators.${translatorName};
isImpure = project: translatorName:
(getTranslator translatorName).type == "impure";
getInvalidationHash = project:
dlib.calcInvalidationHash {
inherit project source;
# TODO: add translatorArgs
translatorArgs = {};
translator = project.translator;
config = dream2nixConfig;
};
isResolved = project: let
dreamLockExists =
l.pathExists "${toString dream2nixConfig.projectRoot}/${project.dreamLockPath}";
dreamLockValid =
project.dreamLock._generic.invalidationHash
or ""
== project.invalidationHash;
in
dreamLockExists && dreamLockValid;
getProjectKey = project: "${project.name}_|_${project.subsystem}_|_${project.relPath}";
# list of projects extended with some information requried for processing
projectsList =
l.map
(project: (let
self =
project
// rec {
dreamLock =
(utils.dream-lock.readDreamLock {
dreamLock = "${toString dream2nixConfig.projectRoot}/${project.dreamLockPath}";
})
.lock;
impure = isImpure project translator;
invalidationHash = getInvalidationHash project;
key = getProjectKey project;
resolved = isResolved self;
translator = project.translator or (l.head project.translators);
};
in
self))
discoveredProjects;
# projects without existing valid dream-lock.json
projectsPureUnresolved =
l.filter
(project: ! project.resolved && ! project.impure)
projectsList;
# already resolved projects
projectsResolved =
l.filter
(project: project.resolved)
projectsList;
# list of pure projects extended with 'dreamLock' attribute
projectsResolvedOnTheFly =
l.forEach projectsPureUnresolved
(proj: let
translator = getTranslator proj.translator;
dreamLock'' = translator.finalTranslate {
inherit source tree discoveredProjects;
project = proj;
};
/*
simpleTranslate2 exposes result via `.result` in order to allow for
unit testing via `.inputs`.
*/
dreamLock' = dreamLock''.result or dreamLock'';
dreamLock =
dreamLock'
// {
_generic =
dreamLock'._generic
// {
invalidationHash = proj.invalidationHash;
};
};
in
proj
// {
inherit dreamLock;
});
resolvedProjects = projectsResolved ++ projectsResolvedOnTheFly;
in
resolvedProjects;
# transform a list of resolved projects to buildable outputs
realizeProjects = {
inject ? {},
translatedProjects ? translateProjects {inherit pname settings source;},
# alternative way of calling (for debugging)
pname ? null,
source ? null,
packageOverrides ? {},
sourceOverrides ? oldSources: {},
settings ? [],
}: let
dreamLocks = l.forEach translatedProjects (proj: proj.dreamLock);
defaultSourceOverride = dreamLock:
if source == null
then {}
else let
defaultPackage = dreamLock._generic.defaultPackage;
defaultPackageVersion =
dreamLock._generic.packages."${defaultPackage}";
in {
"${defaultPackage}"."${defaultPackageVersion}" = "${source}/${dreamLock._generic.location}";
};
# extends each package with a `.resolve` attribute
# and applies sourceOverrides
outputsForProject = proj: let
outputs = makeOutputsForDreamLock {
inherit inject packageOverrides;
sourceRoot = source;
builder = proj.builder or null;
dreamLock = proj.dreamLock;
sourceOverrides = oldSources:
dlib.recursiveUpdateUntilDepth
1
(defaultSourceOverride proj.dreamLock)
(sourceOverrides oldSources);
};
in
outputs
// {
packages =
l.mapAttrs
(pname: pkg:
pkg.overrideAttrs (old: {
passthru =
old.passthru
or {}
// {
resolve = utils.makeTranslateScript {
inherit source;
invalidationHash = proj.invalidationHash;
project = proj;
};
};
}))
(outputs.packages or {});
};
projectOutputs = l.map outputsForProject translatedProjects;
in
dlib.mergeFlakes projectOutputs;
generateImpureResolveScript = {
source,
impureProjects,
}: let
impureResolveScriptsList =
l.listToAttrs
(
l.map
(
project:
l.nameValuePair
"Name: ${project.name}; Subsystem: ${project.subsystem or "?"}; relPath: ${project.relPath}"
(utils.makeTranslateScript {inherit project source;})
)
impureProjects
);
resolveImpureScript =
utils.writePureShellScriptBin
"resolve"
[]
''
${l.concatStringsSep "\n"
(l.mapAttrsToList
(title: script: ''
echo "Resolving:: ${title}"
${script}/bin/resolve
'')
impureResolveScriptsList)}
'';
in
resolveImpureScript;
makeOutputs = {
source ? throw "pass a 'source' to 'makeOutputs'",
discoveredProjects ?
config.functions.discoverers.discoverProjects {
inherit settings source;
},
pname ? null,
projects ? {},
settings ? [],
packageOverrides ? {},
sourceOverrides ? old: {},
inject ? {},
}: let
# if projects are defined manually, ignore discoveredProjects
finalProjects =
if projects != {}
then let
projectsList = l.attrValues projects;
in
# skip discovery and just add required attributes to project list
l.forEach projectsList
(proj:
proj
// {
relPath = proj.relPath or "";
translator = proj.translator or (l.head proj.translators);
dreamLockPath =
config.functions.discoverers.getDreamLockPath
proj
(l.head projectsList);
})
else discoveredProjects;
impureProjects =
l.filter
(proj:
config.translators."${proj.translator}".type
== "impure")
finalProjects;
resolveImpureScript = generateImpureResolveScript {
inherit impureProjects source;
};
translatedProjects = translateProjects {
discoveredProjects = finalProjects;
inherit
pname
settings
source
;
};
realizedProjects = realizeProjects {
inherit
inject
packageOverrides
sourceOverrides
translatedProjects
source
;
};
impureFakeDerivations =
l.listToAttrs
(l.map
(proj:
l.nameValuePair
proj.name
{
type = "derivation";
name = proj.name;
resolve = utils.makeTranslateScript {
project = proj;
inherit source;
};
drvPath = throw ''
The ${proj.subsystem} package ${proj.name} contains unresolved impurities.
Resolve by running the .resolve attribute of this derivation
or by resolving all impure projects by running the `resolveImpure` package
'';
})
impureProjects);
in
realizedProjects
// {
packages =
l.warnIf
(realizeProjects.packages.resolveImpure or null != null)
''
a builder outputted a package named 'resolveImpure'
this will be overridden by dream2nix!
''
impureFakeDerivations
// (realizedProjects.packages or {})
// {resolveImpure = resolveImpureScript;};
};
in {
config.dream2nixInterface = {
inherit
fetchSources
realizeProjects
translateProjects
riseAndShine
makeOutputsForDreamLock
makeOutputs
;
};
}

View File

@ -0,0 +1,10 @@
{config, ...}: let
l = config.lib // builtins;
t = l.types;
in {
options = {
dream2nixInterface = l.mkOption {
type = t.lazyAttrsOf t.raw;
};
};
}

6
src/modules/externals/default.nix vendored Normal file
View File

@ -0,0 +1,6 @@
{
imports = [
./implementation.nix
./interface.nix
];
}

103
src/modules/externals/implementation.nix vendored Normal file
View File

@ -0,0 +1,103 @@
{config, ...}: let
l = config.lib // builtins;
inherit (config) pkgs externalSources;
in {
config = {
externals = {
devshell = {
makeShell = import "${externalSources.devshell}/modules" pkgs;
imports.c = "${externalSources.devshell}/extra/language/c.nix";
};
crane = let
importLibFile = name: import "${externalSources.crane}/lib/${name}.nix";
makeHook = attrs: name:
pkgs.makeSetupHook
({inherit name;} // attrs)
"${externalSources.crane}/pkgs/${name}.sh";
genHooks = names: attrs: l.genAttrs names (makeHook attrs);
in
{
cargoHostTarget,
cargoBuildBuild,
}: rec {
otherHooks =
genHooks [
"cargoHelperFunctions"
"configureCargoCommonVarsHook"
"configureCargoVendoredDepsHook"
]
{};
installHooks =
genHooks [
"inheritCargoArtifactsHook"
"installCargoArtifactsHook"
]
{
substitutions = {
zstd = "${pkgs.pkgsBuildBuild.zstd}/bin/zstd";
};
};
installLogHook = genHooks ["installFromCargoBuildLogHook"] {
substitutions = {
cargo = "${cargoBuildBuild}/bin/cargo";
jq = "${pkgs.pkgsBuildBuild.jq}/bin/jq";
};
};
# These aren't used by dream2nix
crateNameFromCargoToml = null;
vendorCargoDeps = null;
writeTOML = importLibFile "writeTOML" {
inherit (pkgs) runCommand pkgsBuildBuild;
};
cleanCargoToml = importLibFile "cleanCargoToml" {};
findCargoFiles = importLibFile "findCargoFiles" {
inherit (pkgs) lib;
};
mkDummySrc = importLibFile "mkDummySrc" {
inherit (pkgs) writeText runCommandLocal lib;
inherit writeTOML cleanCargoToml findCargoFiles;
};
mkCargoDerivation = importLibFile "mkCargoDerivation" {
cargo = cargoHostTarget;
inherit (pkgs) stdenv lib;
inherit
(installHooks)
inheritCargoArtifactsHook
installCargoArtifactsHook
;
inherit
(otherHooks)
configureCargoCommonVarsHook
configureCargoVendoredDepsHook
;
cargoHelperFunctionsHook = otherHooks.cargoHelperFunctions;
};
buildDepsOnly = importLibFile "buildDepsOnly" {
inherit
mkCargoDerivation
crateNameFromCargoToml
vendorCargoDeps
mkDummySrc
;
};
cargoBuild = importLibFile "cargoBuild" {
inherit
mkCargoDerivation
buildDepsOnly
crateNameFromCargoToml
vendorCargoDeps
;
};
buildPackage = importLibFile "buildPackage" {
inherit (pkgs) removeReferencesTo lib;
inherit (installLogHook) installFromCargoBuildLogHook;
inherit cargoBuild;
};
};
};
};
}

10
src/modules/externals/interface.nix vendored Normal file
View File

@ -0,0 +1,10 @@
{config, ...}: let
l = config.lib // builtins;
t = l.types;
in {
options = {
externals = l.mkOption {
type = t.lazyAttrsOf t.raw;
};
};
}

View File

@ -27,7 +27,7 @@ in {
outputs =
l.mapAttrs
(_: args: instance.makeOutputs args)
(_: args: instance.dream2nixInterface.makeOutputs args)
config.dream2nix.inputs;
getAttrFromOutputs = attrName:

View File

@ -1,6 +1,6 @@
{
lib,
dream2nixConfig,
lib ? (args.pkgs or (import <nixpkgs> {})).lib,
dream2nixConfig ? {},
...
} @ args: let
topLevel = import ./top-level.nix args;

View File

@ -41,18 +41,18 @@
--show-trace --impure --raw --expr "
let
dream2nix = import ${dream2nixWithExternals} {
config = ${dream2nixConfigFile};
dream2nixConfig = ${dream2nixConfigFile};
};
translatorArgs =
(builtins.fromJSON
(builtins.unsafeDiscardStringContext (builtins.readFile '''$1''')));
dreamLock' =
dream2nix.framework.translatorsBySubsystem.${subsystem}.${name}.finalTranslate
dream2nix.translatorsBySubsystem.${subsystem}.${name}.finalTranslate
translatorArgs;
# simpleTranslate2 puts dream-lock in result
dreamLock = dreamLock'.result or dreamLock';
in
dream2nix.framework.utils.dream-lock.toJSON
dream2nix.utils.dream-lock.toJSON
# don't use nix to detect cycles, this will be more efficient in python
(dreamLock // {
_generic = builtins.removeAttrs dreamLock._generic [ \"cyclicDependencies\" ];

View File

@ -1,15 +1,43 @@
{
externals,
externalSources,
inputs,
lib,
pkgs,
dream2nixConfig,
dream2nixConfigFile,
dream2nixWithExternals,
dream2nixInterface,
inputs ?
(import ../../flake-compat.nix {
src = ../../.;
inherit (pkgs) system;
})
.inputs,
lib ? pkgs.lib,
pkgs ? import <nixpkgs> {},
dream2nixConfig ?
if builtins ? getEnv && builtins.getEnv "dream2nixConfig" != ""
# if called via CLI, load config via env
then builtins.toPath (builtins.getEnv "dream2nixConfig")
# load from default directory
else {},
externalPaths ? null,
externalSources ? null,
} @ args: let
b = builtins;
t = lib.types;
externalDir =
if externalPaths != null
then
(import ../utils/external-dir.nix {
inherit externalPaths externalSources pkgs;
})
# if called via CLI, load externals via env
else if b ? getEnv && b.getEnv "d2nExternalDir" != ""
then ../. + (b.getEnv "d2nExternalDir")
# load from default directory
else ../external;
externalSources =
args.externalSources
or (
lib.genAttrs
(lib.attrNames (builtins.readDir externalDir))
(inputName: "${../. + externalDir}/${inputName}")
);
in {
imports = [
./functions.discoverers
@ -34,41 +62,64 @@ in {
./dlib.parsing
./dlib.construct
./dlib.simpleTranslate2
./externals
./dream2nix-interface
];
options = {
lib = lib.mkOption {
type = t.raw;
};
externals = lib.mkOption {
type = t.lazyAttrsOf t.raw;
};
externalSources = lib.mkOption {
type = t.lazyAttrsOf t.path;
};
inputs = lib.mkOption {
type = t.lazyAttrsOf t.attrs;
};
pkgs = lib.mkOption {
type = t.raw;
};
externalDir = lib.mkOption {
type = t.path;
};
externalPaths = lib.mkOption {
type = t.listOf t.str;
};
externalSources = lib.mkOption {
type = t.lazyAttrsOf t.path;
};
dream2nixWithExternals = lib.mkOption {
type = t.path;
};
dream2nixConfig = lib.mkOption {
type = t.submoduleWith {
modules = [./config];
};
};
dream2nixWithExternals = lib.mkOption {
type = t.path;
};
dream2nixConfigFile = lib.mkOption {
type = t.path;
};
dream2nixInterface = lib.mkOption {
type = t.raw;
};
};
config =
args
// {
lib = args.lib // builtins;
};
config = {
inherit
dream2nixConfig
pkgs
inputs
externalPaths
externalSources
externalDir
;
lib = lib // builtins;
dream2nixConfigFile = b.toFile "dream2nix-config.json" (b.toJSON dream2nixConfig);
# the location of the dream2nix framework for self references (update scripts, etc.)
dream2nixWithExternals =
if b.pathExists (../. + "/external")
then ../.
else
pkgs.runCommandLocal "dream2nix-full-src" {} ''
cp -r ${../.} $out
chmod +w $out
mkdir $out/external
cp -r ${externalDir}/* $out/external/
'';
};
}

View File

@ -21,7 +21,7 @@ def aggregate_hashes(lock, outputDreamLock, dream2nix_src, dream2nix_config):
# compute FOD hash of aggregated sources
proc = nix(
"build", "--impure", "-L", "--show-trace", "--expr",
f"(import {dream2nix_src} {{ config = {dream2nix_config}; }}).fetchSources {{ dreamLock = {outputDreamLock}; }}"
f"(import {dream2nix_src} {{ dream2nixConfig = {dream2nix_config}; }}).dream2nixInterface.fetchSources {{ dreamLock = {outputDreamLock}; }}"
)
print(proc.stderr.decode())
# read the output hash from the failed build log

View File

@ -25,7 +25,7 @@ def callNixFunction(function_path, **kwargs):
d2n = (import {dream2nix_src} {{}});
in
builtins.toJSON (
(d2n.framework.dlib.callViaEnv d2n.{function_path})
(d2n.dlib.callViaEnv d2n.{function_path})
)
''',
env=env
@ -63,11 +63,11 @@ def eval(attr_path, wrapper_code=None, **kwargs):
d2n = (import {dream2nix_src} {{}});
result' =
if "{is_function_call}" == "True"
then d2n.framework.dlib.callViaEnv d2n.{attr_path}
then d2n.dlib.callViaEnv d2n.{attr_path}
else d2n.{attr_path};
result = (d2n.callPackageDream
result = (d2n.pkgs.callPackage
{wrapper_code_file.name}
{{ result = result'; }});
(d2n // {{ result = result'; }}));
in
b.toJSON (
# remove override attributes added by callPackage
@ -101,7 +101,7 @@ def buildNixFunction(function_path, **kwargs):
let
d2n = (import {dream2nix_src} {{}});
in
(d2n.framework.dlib.callViaEnv d2n.{function_path})
(d2n.dlib.callViaEnv d2n.{function_path})
''',
env=env
)

View File

@ -4,7 +4,7 @@
}: cwd: let
b = builtins;
dream2nix = import dream2nixWithExternals {
config = b.path {path = dream2nixConfig;};
dream2nixConfig = b.path {path = dream2nixConfig;};
};
parsed = b.fromTOML (builtins.readFile "${cwd}/gomod2nix.toml");
pkgs = import <nixpkgs> {};
@ -14,7 +14,7 @@
(goName: depAttrs: depAttrs // {inherit goName;})
parsed;
translated =
dream2nix.framework.utils.simpleTranslate
dream2nix.utils.simpleTranslate
({
getDepByNameVer,
dependenciesByOriginalID,
@ -68,4 +68,4 @@
};
});
in
dream2nix.framework.utils.dream-lock.toJSON translated
dream2nix.utils.dream-lock.toJSON translated

42
templates/default.nix Normal file
View File

@ -0,0 +1,42 @@
{
lib,
self,
...
}: let
l = lib // builtins;
in {
flake = {
templates =
{
default = self.templates.simple;
simple = {
description = "Simple dream2nix flake";
path = ./templates/simple;
welcomeText = ''
You just created a simple dream2nix package!
start with typing `nix flake show` to discover the projects attributes.
commands:
- `nix develop` <-- enters the devShell
- `nix build .#` <-- builds the default package (`.#default`)
Start hacking and -_- have some fun!
> dont forget to add nix `result` folder to your `.gitignore`
'';
};
}
// (
l.genAttrs
(self.lib.dlib.listDirs ../examples)
(name: {
description = "Example: ${name} template";
path = ../examples/${name};
})
);
};
}

76
tests/default.nix Normal file
View File

@ -0,0 +1,76 @@
{
inputs,
self,
...
}: {
perSystem = {
config,
pkgs,
system,
...
}: let
b = builtins;
callTests = f:
pkgs.callPackage f {
inherit self;
framework = config.d2n;
};
in {
apps = {
tests-unit.type = "app";
tests-unit.program =
b.toString
(callTests ./unit);
tests-integration.type = "app";
tests-integration.program =
b.toString
(callTests ./integration);
tests-integration-d2n-flakes.type = "app";
tests-integration-d2n-flakes.program =
b.toString
(callTests ./integration-d2n-flakes);
tests-examples.type = "app";
tests-examples.program =
b.toString
(callTests ./examples);
tests-all.type = "app";
tests-all.program =
b.toString
(config.d2n.utils.writePureShellScript
[
inputs.alejandra.defaultPackage.${system}
pkgs.coreutils
pkgs.gitMinimal
pkgs.nix
]
''
echo "check for correct formatting"
WORKDIR=$(realpath ./.)
cd $TMPDIR
cp -r $WORKDIR ./repo
cd ./repo
${config.apps.format.program} --fail-on-change
cd -
echo "running unit tests"
${config.apps.tests-unit.program}
echo "running integration tests"
${config.apps.tests-integration.program}
echo "checking flakes under ./examples"
${config.apps.tests-examples.program}
echo "running nix flake check"
cd $WORKDIR
nix flake show >/dev/null
nix flake check
'');
};
};
}

View File

@ -11,7 +11,6 @@
nix,
pkgs,
framework,
dream2nixWithExternals,
...
}: let
l = lib // builtins;

View File

@ -7,7 +7,6 @@
git,
parallel,
nix,
dream2nixWithExternals,
framework,
...
}: let

View File

@ -9,15 +9,18 @@
nix,
pkgs,
framework,
dream2nixWithExternals,
callPackageDream,
...
}: let
l = lib // builtins;
testDirs = l.attrNames (l.readDir ./tests);
testScripts =
map
(dir: callPackageDream (./tests + "/${dir}") {inherit self;})
(
dir:
pkgs.callPackage
(./tests + "/${dir}")
{inherit self framework;}
)
testDirs;
testScriptsFile = pkgs.writeText "scripts-list" (l.concatStringsSep "\n" testScripts);
execTest =

View File

@ -5,7 +5,6 @@
nix,
git,
python3,
dream2nixWithExternals,
framework,
...
}: let

View File

@ -26,7 +26,7 @@ exampleDreamLock = dict(
def test_dream_lock_inject():
result = nix_ffi.callNixFunction(
'framework.utils.dream-lock.injectDependencies',
'utils.dream-lock.injectDependencies',
dreamLock=exampleDreamLock,
inject=dict(
example={
@ -43,7 +43,7 @@ def test_dream_lock_inject():
def test_dream_lock_replace_root_sources():
result = nix_ffi.callNixFunction(
'framework.utils.dream-lock.replaceRootSources',
'utils.dream-lock.replaceRootSources',
dreamLock=exampleDreamLock,
newSourceRoot=dict(
type = "http",

View File

@ -5,7 +5,7 @@ import pytest
def get_projects_to_test():
tests = nix_ffi.eval(
'framework.translators',
'translators',
wrapper_code = '''
{result, ...}: let
lib = (import <nixpkgs> {}).lib;
@ -65,7 +65,7 @@ def check_format_sourceSpec(sourceSpec):
@pytest.mark.parametrize("p", projects)
def test_packageName(p):
defaultPackage = nix_ffi.eval(
f"framework.translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
f"translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
params=dict(
project=p['project'],
source=p['source'],
@ -81,7 +81,7 @@ def test_packageName(p):
@pytest.mark.parametrize("p", projects)
def test_exportedPackages(p):
exportedPackages = nix_ffi.eval(
f"framework.translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
f"translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
params=dict(
project=p['project'],
source=p['source'],
@ -97,7 +97,7 @@ def test_exportedPackages(p):
@pytest.mark.parametrize("p", projects)
def test_extraObjects(p):
extraObjects = nix_ffi.eval(
f"framework.translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
f"translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
params=dict(
project=p['project'],
source=p['source'],
@ -121,7 +121,7 @@ def test_extraObjects(p):
@pytest.mark.parametrize("p", projects)
def test_location(p):
location = nix_ffi.eval(
f"framework.translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
f"translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
params=dict(
project=p['project'],
source=p['source'],
@ -136,7 +136,7 @@ def test_location(p):
@pytest.mark.parametrize("p", projects)
def test_serializedRawObjects(p):
serializedRawObjects = nix_ffi.eval(
f"framework.translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
f"translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
params=dict(
project=p['project'],
source=p['source'],
@ -159,7 +159,7 @@ def test_serializedRawObjects(p):
@pytest.mark.parametrize("p", projects)
def test_subsystemName(p):
subsystemName = nix_ffi.eval(
f"framework.translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
f"translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
params=dict(
project=p['project'],
source=p['source'],
@ -175,7 +175,7 @@ def test_subsystemName(p):
@pytest.mark.parametrize("p", projects)
def test_subsystemAttrs(p):
subsystemAttrs = nix_ffi.eval(
f"framework.translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
f"translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
params=dict(
project=p['project'],
source=p['source'],
@ -191,7 +191,7 @@ def test_subsystemAttrs(p):
@pytest.mark.parametrize("p", projects)
def test_translatorName(p):
translatorName = nix_ffi.eval(
f"framework.translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
f"translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
params=dict(
project=p['project'],
source=p['source'],
@ -207,18 +207,18 @@ def test_translatorName(p):
@pytest.mark.parametrize("p", projects)
def test_extractors(p):
finalObjects = nix_ffi.eval(
f"framework.translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
f"translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
params=dict(
project=p['project'],
source=p['source'],
),
wrapper_code = '''
{result, framework, ...}:
{result, dlib, ...}:
let
l = builtins;
inputs = result.inputs;
rawObjects = inputs.serializedRawObjects;
s = framework.dlib.simpleTranslate2;
s = dlib.simpleTranslate2;
finalObjects = s.mkFinalObjects rawObjects inputs.extractors;
allDependencies = s.makeDependencies finalObjects;
@ -241,18 +241,18 @@ def test_extractors(p):
@pytest.mark.parametrize("p", projects)
def test_keys(p):
objectsByKey = nix_ffi.eval(
f"framework.translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
f"translatorsBySubsystem.{p['subsystem']}.{p['translator']}.finalTranslate",
params=dict(
project=p['project'],
source=p['source'],
),
wrapper_code = '''
{result, framework, ...}:
{result, dlib, ...}:
let
l = builtins;
inputs = result.inputs;
rawObjects = inputs.serializedRawObjects;
s = framework.dlib.simpleTranslate2;
s = dlib.simpleTranslate2;
finalObjects = s.mkFinalObjects rawObjects inputs.extractors;
allDependencies = s.makeDependencies finalObjects;

View File

@ -68,7 +68,7 @@ import nix_ffi
])
def test_translateShortcut(shortcut, expected):
result = nix_ffi.callNixFunction(
'framework.functions.fetchers.translateShortcut',
'functions.fetchers.translateShortcut',
shortcut=shortcut,
computeHash=False,
)

View File

@ -5,5 +5,5 @@ import nix_ffi
('3', [ '2', '3', '1' ]),
])
def test_latestVersion(expected, versions):
result = nix_ffi.callNixFunction('framework.dlib.latestVersion', versions=versions)
result = nix_ffi.callNixFunction('dlib.latestVersion', versions=versions)
assert result == expected