Merge pull request #40 from DavHau/dev

Add tests; Improve: overrides, nodejs builder, output schema
This commit is contained in:
DavHau 2021-11-01 00:22:08 +07:00 committed by GitHub
commit f30b6759f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 580 additions and 201 deletions

43
checks.nix Normal file
View File

@ -0,0 +1,43 @@
{
lib ? pkgs.lib,
pkgs ? import <nixpkgs> {},
dream2nix ? import ./src { inherit pkgs; },
}:
let
lib = pkgs.lib // builtins;
makeTest =
{
name,
source,
cmds,
}:
let
outputs = dream2nix.riseAndShine {
inherit source;
};
commandsToRun = cmds outputs;
in
pkgs.runCommand "test-${name}" {}
(lib.concatStringsSep "\n" commandsToRun);
projects = {
prettier = {
source = lib.fetchTarball {
url = "https://github.com/prettier/prettier/tarball/2.4.1";
sha256 = "19b37qakhlsnr2n5bgv83aih5npgzbad1d2p2rs3zbq5syqbxdyi";
};
cmds = outputs: [
"${outputs.defaultPackage}/bin/prettier --version | grep -q 2.4.1 && mkdir $out"
];
};
};
allTests =
lib.mapAttrs
(name: args: makeTest (args // { inherit name; }))
projects;
in
allTests

View File

@ -40,7 +40,7 @@ In case of a `pure` or `IFD` translator:
# optionally specify additional arguments that the user can provide to the
# translator to customize its behavior
specialArgs = ...;
extraArgs = ...;
}
```
@ -53,7 +53,7 @@ In case of an `impure` translator:
#
# The first arg `$1` will be a json file containing the input parameters
# like defined in /specifications/translator-call-example.json and the
# additional arguments required according to specialArgs
# additional arguments required according to extraArgs
#
# The program is expected to create a file at the location specified
# by the input parameter `outFile`.
@ -66,7 +66,7 @@ In case of an `impure` translator:
# optionally specify additional arguments that the user can provide to the
# translator to customize its behavior
specialArgs = ...;
extraArgs = ...;
}
```

View File

@ -14,7 +14,7 @@
node2nix = { url = "github:svanderburg/node2nix"; flake = false; };
};
outputs = { self, mach-nix, nix-parsec, nixpkgs, node2nix, }:
outputs = { self, mach-nix, nix-parsec, nixpkgs, node2nix, }@inp:
let
b = builtins;
@ -28,40 +28,76 @@
);
# To use dream2nix in non-flake + non-IFD enabled repos, the source code of dream2nix
# must be installed into that repo (using nix run dream2nix#install).
# The problem is, we also need to install all of dream2nix' dependecies as well.
# Therefore 'externalSourcesFor' contains all relevant files of external projects we depend on.
makeExternalSources = pkgs: pkgs.runCommand "dream2nix-external" {} ''
mkdir -p $out/{mach-nix-lib,node2nix,nix-parsec}
cp ${mach-nix}/{lib/extractor/{default.nix,distutils.patch,setuptools.patch},LICENSE} $out/mach-nix-lib/
cp ${node2nix}/{nix/node-env.nix,LICENSE} $out/node2nix/
cp ${nix-parsec}/{parsec,lexer}.nix $out/nix-parsec/
'';
# must be installed into these repos (using nix run dream2nix#install).
# The problem is, all of dream2nix' dependecies need to be installed as well.
# Therefore 'externalPaths' contains all relevant files of external projects
# which dream2nix depends on. Exactly these files will be installed.
externalPaths = {
mach-nix = [
"lib/extractor/default.nix"
"lib/extractor/distutils.patch"
"lib/extractor/setuptools.patch"
"LICENSE"
];
node2nix = [
"nix/node-env.nix"
"LICENSE"
];
nix-parsec = [
"parsec.nix"
"lexer.nix"
];
};
externalSourcesFor = forAllSystems (system: makeExternalSources);
# create a directory containing the files listed in externalPaths
makeExternalDir = pkgs: pkgs.runCommand "dream2nix-external" {}
(lib.concatStringsSep "\n"
(lib.mapAttrsToList
(inputName: paths:
lib.concatStringsSep "\n"
(lib.forEach
paths
(path: ''
mkdir -p $out/${inputName}/$(dirname ${path})
cp ${inp."${inputName}"}/${path} $out/${inputName}/${path}
'')))
externalPaths));
externalDirFor = forAllSystems (system: makeExternalDir);
# An interface to access files of external projects.
# This implementation aceeses the flake inputs directly,
# but if dream2nix is used without flakes, it defaults
# to another implementation of that function which
# uses the installed external paths instead (see default.nix)
externalSources =
lib.genAttrs
(lib.attrNames externalPaths)
(inputName: inp."${inputName}");
overridesDir = "${./overrides}";
# system specific dream2nix api
dream2nixFor = forAllSystems (system: pkgs: import ./src rec {
externalSources = externalSourcesFor."${system}";
inherit pkgs;
inherit lib;
externalDir = externalDirFor."${system}";
inherit externalSources lib overridesDir pkgs;
});
in
{
# overlay with flakes enabled nix
# (all of dream2nix cli dependends on nix ^2.4)
overlay = new: old: {
nix = old.writeScriptBin "nix" ''
${new.nixUnstable}/bin/nix --option experimental-features "nix-command flakes" "$@"
overlay = final: prev: {
nix = prev.writeScriptBin "nix" ''
${final.nixUnstable}/bin/nix --option experimental-features "nix-command flakes" "$@"
'';
};
# System independent dream2nix api.
# Similar to drem2nixFor but will require 'system(s)' or 'pkgs' as an argument.
# Produces flake-like output schema.
lib.dream2nix = import ./src/lib.nix {
inherit makeExternalSources lib;
lib = import ./src/lib.nix {
inherit externalSources overridesDir lib;
nixpkgsSrc = "${nixpkgs}";
};
@ -91,12 +127,17 @@
shellHook = ''
export NIX_PATH=nixpkgs=${nixpkgs}
export d2nExternalSources=${externalSourcesFor."${system}"}
export d2nExternalDir=${externalDirFor."${system}"}
export dream2nixWithExternals=${dream2nixFor."${system}".dream2nixWithExternals}
export d2nExternalSources=$dream2nixWithExternals/external
export d2nOverridesDir=${./overrides}
echo "\nManually execute 'export dream2nixWithExternals={path to your dream2nix checkout}'"
'';
});
checks = forAllSystems (system: pkgs: import ./checks.nix {
inherit lib pkgs;
dream2nix = dream2nixFor."${system}";
});
};
}

View File

@ -0,0 +1,75 @@
{
lib,
pkgs,
}:
let
b = builtins;
in
{
gifsicle = {
add-binary = {
installScript = ''
ln -s ${pkgs.gifsicle}/bin/gifsicle ./vendor/gifsicle
npm run postinstall
'';
};
};
mozjpeg = {
add-binary = {
installScript = ''
ln -s ${pkgs.mozjpeg}/bin/cjpeg ./vendor/cjpeg
npm run postinstall
'';
};
};
optipng-bin = {
add-binary = {
installScript = ''
ln -s ${pkgs.optipng}/bin/optipng ./vendor/optipng
npm run postinstall
'';
};
};
pngquant-bin = {
add-binary = {
installScript = ''
ln -s ${pkgs.pngquant}/bin/pngquant ./vendor/pngquant
npm run postinstall
'';
};
};
webpack = {
remove-webpack-cli-check = {
_condition = pkg: pkg.version == "5.41.1";
ignoreScripts = false;
patches = [
./webpack/remove-webpack-cli-check.patch
];
};
};
webpack-cli = {
remove-webpack-check = {
_condition = pkg: pkg.version == "4.7.2";
ignoreScripts = false;
patches = [
./webpack-cli/remove-webpack-check.patch
];
};
};
"@mattermost/webapp" = {
run-webpack = {
installScript = ''
NODE_ENV=production node --max-old-space-size=8192 ./node_modules/webpack/bin/webpack.js
'';
};
};
}

View File

@ -0,0 +1,12 @@
diff --git a/packages/webpack-cli/bin/cli.js b/packages/webpack-cli/bin/cli.js
--- a/bin/cli.js
+++ b/bin/cli.js
@@ -21,7 +21,7 @@ if (!process.env.WEBPACK_CLI_SKIP_IMPORT_LOCAL) {
process.title = "webpack";
-if (utils.packageExists("webpack")) {
+if (true) {
runCLI(process.argv, originalModuleCompile);
} else {
const { promptInstallation, logger, colors } = utils;

View File

@ -0,0 +1,12 @@
diff --git a/bin/webpack.js b/bin/webpack.js
--- a/bin/webpack.js
+++ b/bin/webpack.js
@@ -87,7 +87,7 @@ const cli = {
url: "https://github.com/webpack/webpack-cli"
};
-if (!cli.installed) {
+if (false) {
const path = require("path");
const fs = require("graceful-fs");
const readLine = require("readline");

View File

@ -9,7 +9,7 @@ import networkx as nx
from cleo import Command, option
from utils import dream2nix_src, checkLockJSON, callNixFunction, buildNixFunction, buildNixAttribute, \
list_translators_for_source, order_dict, strip_hashes_from_lock
list_translators_for_source, sort_dict, strip_hashes_from_lock
class PackageCommand(Command):
@ -164,7 +164,7 @@ class PackageCommand(Command):
exit(1)
# raise error if any specified extra arg is unknown
unknown_extra_args = set(specified_extra_args.keys()) - set(translator['specialArgs'].keys())
unknown_extra_args = set(specified_extra_args.keys()) - set(translator['extraArgs'].keys())
if unknown_extra_args:
print(
f"Invalid extra args for translator '{translator['name']}': "
@ -176,7 +176,7 @@ class PackageCommand(Command):
# transform flags to bool
for argName, argVal in specified_extra_args.copy().items():
if translator['specialArgs'][argName]['type'] == 'flag':
if translator['extraArgs'][argName]['type'] == 'flag':
if argVal.lower() in ('yes', 'y', 'true'):
specified_extra_args[argName] = True
elif argVal.lower() in ('no', 'n', 'false'):
@ -188,18 +188,18 @@ class PackageCommand(Command):
)
specified_extra_args =\
{k: (bool(v) if translator['specialArgs'][k]['type'] == 'flag' else v ) \
{k: (bool(v) if translator['extraArgs'][k]['type'] == 'flag' else v ) \
for k, v in specified_extra_args.items()}
# on non-interactive session, assume defaults for unspecified extra args
if not self.io.is_interactive():
specified_extra_args.update(
{n: (True if v['type'] == 'flag' else v['default']) \
for n, v in translator['specialArgs'].items() \
for n, v in translator['extraArgs'].items() \
if n not in specified_extra_args and 'default' in v}
)
unspecified_extra_args = \
{n: v for n, v in translator['specialArgs'].items() \
{n: v for n, v in translator['extraArgs'].items() \
if n not in specified_extra_args}
# raise error if any extra arg unspecified in non-interactive session
if unspecified_extra_args:
@ -310,18 +310,18 @@ class PackageCommand(Command):
removed_edges.append((node_from, node_to))
except nx.NetworkXNoCycle:
continue
lock['generic']['dependenciesRemoved'] = {}
lock['generic']['cyclicDependencies'] = {}
if removed_edges:
lock['generic']['dependenciesRemoved'] = {}
lock['generic']['cyclicDependencies'] = {}
removed_cycles_text = 'Removed Cyclic dependencies:'
for node, removed_node in removed_edges:
removed_cycles_text += f"\n {node} -> {removed_node}"
n_name, n_ver = node.split('#')
if n_name not in lock['generic']['dependenciesRemoved']:
lock['generic']['dependenciesRemoved'][n_name] = {}
if n_ver not in lock['generic']['dependenciesRemoved'][n_name]:
lock['generic']['dependenciesRemoved'][n_name][n_ver] = []
lock['generic']['dependenciesRemoved'][n_name][n_ver].append(removed_node)
if n_name not in lock['generic']['cyclicDependencies']:
lock['generic']['cyclicDependencies'][n_name] = {}
if n_ver not in lock['generic']['cyclicDependencies'][n_name]:
lock['generic']['cyclicDependencies'][n_name][n_ver] = []
lock['generic']['cyclicDependencies'][n_name][n_ver].append(removed_node)
print(removed_cycles_text)
# calculate combined hash if --combined was specified
@ -357,9 +357,9 @@ class PackageCommand(Command):
lock['generic']['sourcesCombinedHash'] = hash
# re-write dream.lock
checkLockJSON(order_dict(lock))
checkLockJSON(sort_dict(lock))
with open(outputDreamLock, 'w') as f:
json.dump(order_dict(lock), f, indent=2)
json.dump(sort_dict(lock), f, indent=2)
# create default.nix
template = callNixFunction(

View File

@ -1,7 +1,6 @@
{
# from dream2nix
dream2nixWithExternals,
externalSources,
fetchers,
translators,

View File

@ -106,8 +106,8 @@ def list_translators_for_source(sourcePath):
return list(sorted(translatorsList, key=lambda t: t['compatible']))
def order_dict(d):
return {k: order_dict(v) if isinstance(v, dict) else v
def sort_dict(d):
return {k: sort_dict(v) if isinstance(v, dict) else v
for k, v in sorted(d.items())}
def strip_hashes_from_lock(lock):

View File

@ -2,8 +2,6 @@
pkgs,
callPackageDream,
externalSources,
dream2nixWithExternals,
translators,
...
}:

View File

@ -22,7 +22,7 @@
# attributes
buildSystemAttrs,
dependenciesRemoved,
cyclicDependencies,
mainPackageName,
mainPackageVersion,
packageVersions,
@ -40,11 +40,10 @@ let
b = builtins;
# tells if a dependency introduces a cycle
# -> needs to be built in a combined derivation
isCyclic = name: version:
b.elem name standalonePackageNames
||
(dependenciesRemoved ? "${name}"
&& dependenciesRemoved."${name}" ? "${version}");
cyclicDependencies ? "${name}"."${version}";
mainPackageKey =
"${mainPackageName}#${mainPackageVersion}";
@ -68,7 +67,8 @@ let
lib.genAttrs
versions
(version:
if isCyclic name version then
if isCyclic name version
|| b.elem name standalonePackageNames then
makeCombinedPackage name version
else
makePackage name version))
@ -81,14 +81,15 @@ let
inherit name version;
builder = builders.nodejs.node2nix;
inject =
lib.optionalAttrs (dependenciesRemoved ? "${name}"."${version}") {
lib.optionalAttrs (cyclicDependencies ? "${name}"."${version}") {
"${name}"."${version}" =
dependenciesRemoved."${name}"."${version}";
cyclicDependencies."${name}"."${version}";
};
};
in
built.package;
built.defaultPackage;
# Generates a derivation for a specific package name + version
makePackage = name: version:
let
@ -118,34 +119,37 @@ let
buildInputs = [ jq nodejs nodejs.python ];
# prevents running into ulimits
passAsFile = [ "dependenciesJson" "nodeDeps" ];
ignoreScripts = true;
preBuildPhases = [ "d2nPatchPhase" "d2nInstallDependenciesPhase" ];
dontUnpack = true;
preFixupPhases = [ "d2nPostInstallPhase" ];
# not used by default but can be enabled if needed
dontConfigure = true;
dontBuild = true;
dontNpmInstall = false;
# can be overridden to define alternative install command
# (defaults to 'npm run postinstall')
installScript = null;
# python script to modify some metadata to support installation
# (see comments below on d2nPatchPhase)
fixPackage = "${./fix-package.py}";
# costs performance and doesn't seem beneficial in most scenarios
dontStrip = true;
# not using the default unpackPhase,
# as it fails setting the permissions sometimes
installPhase = ''
runHook preInstall
# The default unpackPhase seemed to fail on setting permissions
# for some packages.
# TODO: debug nixpkgs unpackPhase and upstream improvement.
unpackPhase = ''
runHook preUnpack
nodeModules=$out/lib/node_modules
mkdir -p $nodeModules
cd $TMPDIR
export sourceRoot="$nodeModules/$packageName"
unpackFile $src
@ -156,30 +160,68 @@ let
if [ -f "$src" ]
then
# Figure out what directory has been unpacked
packageDir="$(find . -maxdepth 1 -type d | tail -1)"
export packageDir="$(find . -maxdepth 1 -type d | tail -1)"
# Restore write permissions
find "$packageDir" -type d -exec chmod u+x {} \;
chmod -R u+w "$packageDir"
# Move the extracted tarball into the output folder
mv "$packageDir" "$nodeModules/$packageName"
mv "$packageDir" "$sourceRoot"
elif [ -d "$src" ]
then
strippedName="$(stripHash $src)"
export strippedName="$(stripHash $src)"
# Restore write permissions
chmod -R u+w "$strippedName"
# Move the extracted directory into the output folder
mv "$strippedName" "$nodeModules/$packageName"
mv "$strippedName" "$sourceRoot"
fi
runHook postUnpack
'';
# The python script wich is executed in this phase:
# - ensures that the package is compatible to the current system
# - ensures the main version in package.json matches the expected
# - deletes "devDependencies" and "peerDependencies" from package.json
# (might block npm install in case npm install is used)
# - pins dependency versions in package.json
# - creates symlinks for executables declared in package.json
# Apart from that:
# - Any usage of 'link:' in package.json is replaced with 'file:'
# - If package-lock.json exists, it is deleted, as it might conflict
# with the parent package-lock.json.
d2nPatchPhase = ''
# delete package-lock.json as it can lead to conflicts
rm -f package-lock.json
# repair 'link:' -> 'file:'
mv $nodeModules/$packageName/package.json $nodeModules/$packageName/package.json.old
cat $nodeModules/$packageName/package.json.old | sed 's!link:!file\:!g' > $nodeModules/$packageName/package.json
rm $nodeModules/$packageName/package.json.old
# run python script (see commend above):
cp package.json package.json.bak
python $fixPackage \
|| \
# exit code 3 -> the package is incompatible to the current platform
# -> Let the build succeed, but don't create lib/node_packages
if [ "$?" == "3" ]; then
rm -r $out/*
echo "Not compatible with system $system" > $out/error
exit 0
else
exit 1
fi
'';
# - links all direct node dependencies into the node_modules directory
# - adds executables of direct node dependencies to PATH
# - adds the current node module to NODE_PATH
# - sets HOME=$TMPDIR, as this is required by some npm scripts
d2nInstallDependenciesPhase = ''
# symlink dependency packages into node_modules
for dep in $(cat $nodeDepsPath); do
# add bin to PATH
@ -192,12 +234,12 @@ let
if [[ $module == @* ]]; then
for submodule in $(ls $dep/lib/node_modules/$module); do
mkdir -p $nodeModules/$packageName/node_modules/$module
echo "ln -s $dep/lib/node_modules/$module/$submodule $nodeModules/$packageName/node_modules/$module/$submodule"
echo -e "creating link: $dep/lib/node_modules/$module/$submodule\n -> $nodeModules/$packageName/node_modules/$module/$submodule"
ln -s $dep/lib/node_modules/$module/$submodule $nodeModules/$packageName/node_modules/$module/$submodule
done
else
mkdir -p $nodeModules/$packageName/node_modules/
echo "ln -s $dep/lib/node_modules/$module $nodeModules/$packageName/node_modules/$module"
echo -e "creating link: $dep/lib/node_modules/$module\n -> $nodeModules/$packageName/node_modules/$module"
ln -s $dep/lib/node_modules/$module $nodeModules/$packageName/node_modules/$module
fi
done
@ -206,39 +248,30 @@ let
export NODE_PATH="$NODE_PATH:$nodeModules/$packageName/node_modules"
cd "$nodeModules/$packageName"
export HOME=$TMPDIR
'';
# delete package-lock.json as it can lead to conflicts
rm -f package-lock.json
# Run the install command which defaults to 'npm run postinstall'.
# Set alternative install command by overriding 'installScript'.
installPhase = ''
runHook preInstall
# pin dependency versions in package.json
cp package.json package.json.bak
python $fixPackage \
|| \
# exit code 3 -> the package is incompatible to the current platform
if [ "$?" == "3" ]; then
rm -r $out/*
echo "Not compatible with system $system" > $out/error
exit 0
else
exit 1
fi
# execute installation command
# execute install command
if [ -n "$installScript" ]; then
if [ -f "$installScript" ]; then
exec $installScript
else
echo "$installScript" | bash
fi
elif [ -z "$dontNpmInstall" ]; then
if [ "$(jq '.scripts.postinstall' ./package.json)" != "null" ]; then
npm --production --offline --nodedir=$nodeSources run postinstall
fi
elif [ "$(jq '.scripts.postinstall' ./package.json)" != "null" ]; then
npm --production --offline --nodedir=$nodeSources run postinstall
fi
runHook postInstall
'';
# Symlinks executables and manual pages to correct directories
d2nPostInstallPhase = ''
# Create symlink to the deployed executable folder, if applicable
if [ -d "$nodeModules/.bin" ]
then
@ -249,21 +282,20 @@ let
# Create symlinks to the deployed manual page folders, if applicable
if [ -d "$nodeModules/$packageName/man" ]
then
mkdir -p $out/share
for dir in "$nodeModules/$packageName/man/"*
mkdir -p $out/share
for dir in "$nodeModules/$packageName/man/"*
do
mkdir -p $out/share/man/$(basename "$dir")
for page in "$dir"/*
do
mkdir -p $out/share/man/$(basename "$dir")
for page in "$dir"/*
do
ln -s $page $out/share/man/$(basename "$dir")
done
ln -s $page $out/share/man/$(basename "$dir")
done
done
fi
runHook postInstall
'';
};
in
# apply packageOverrides to current derivation
(utils.applyOverridesToPackage packageOverrides pkg name);

View File

@ -29,7 +29,9 @@ if 'os' in package_json:
version = os.environ.get("version")
if package_json['version'] != version:
print(
"WARNING: Replacing version in package.json: "
"WARNING: The version of this package defined by its package.json "
"doesn't match the version expected by dream2nix."
"\n -> Replacing version in package.json: "
f"{package_json['version']} -> {version}",
file=sys.stderr
)
@ -40,7 +42,7 @@ if package_json['version'] != version:
# We rely on dream.lock having the correct dependencies specified
if 'devDependencies' in package_json:
print(
f"Removing devDependencies from package.json",
f"package.json: removed all devDependencies",
file=sys.stderr
)
changed = True
@ -50,7 +52,7 @@ if 'devDependencies' in package_json:
# We rely on dream.lock instead
if 'peerDependencies' in package_json:
print(
f"Removing peerDependencies from package.json",
f"package.json: removed all peerDependencies",
file=sys.stderr
)
changed = True
@ -65,8 +67,8 @@ if 'dependencies' in package_json:
package_json['dependencies'][pname] = actual_deps[pname]
changed = True
print(
f"Replacing loose version '{version}' with '{actual_deps[pname]}'"
f" for dependency '{pname}' in package.json",
f"package.json: Pinning version '{version}' to '{actual_deps[pname]}'"
f" for dependency '{pname}'",
file=sys.stderr
)

View File

@ -13,7 +13,7 @@
{
buildSystemAttrs,
dependenciesRemoved,
cyclicDependencies,
mainPackageName,
mainPackageVersion,
getCyclicDependencies,

View File

@ -1,29 +1,47 @@
# this is the system specific api for dream2nix
# it requires passing one specifi pkgs
# 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
# use ./lib.nix instead.
{
pkgs ? import <nixpkgs> {},
lib ? pkgs.lib,
# the dream2nix cli depends on some nix 2.4 features
nix ? pkgs.writeScriptBin "nix" ''
${pkgs.nixUnstable}/bin/nix --option experimental-features "nix-command flakes" "$@"
'',
# dependencies of dream2nix
externalSources ?
lib.genAttrs
(lib.attrNames (builtins.readDir externalDir))
(inputName: "${externalDir}/${inputName}"),
# required for non-flake mode
externalDir ?
# if called via CLI, load externals via env
if builtins ? getEnv && builtins.getEnv "d2nExternalSources" != "" then
builtins.getEnv "d2nExternalSources"
# load from default dircetory
if builtins ? getEnv && builtins.getEnv "d2nExternalDir" != "" then
builtins.getEnv "d2nExternalDir"
# load from default directory
else
./external,
# dream2nix overrides
overridesDir ?
# if called via CLI, load externals via env
if builtins ? getEnv && builtins.getEnv "d2nOverridesDir" != "" then
builtins.getEnv "d2nOverridesDir"
# load from default directory
else
./overrides,
}:
let
b = builtins;
utils = callPackageDream ./utils {};
# like pkgs.callPackage, but includes all the dream2nix modules
callPackageDream = f: args: pkgs.callPackage f (args // {
inherit builders;
inherit callPackageDream;
@ -36,13 +54,8 @@ let
inherit nix;
});
externals = {
node2nix = nodejs: pkgs.callPackage "${externalSources}/node2nix/node-env.nix" { inherit nodejs; };
nix-parsec = rec {
lexer = import "${externalSources}/nix-parsec/lexer.nix" { inherit parsec; };
parsec = import "${externalSources}/nix-parsec/parsec.nix";
};
};
utils = callPackageDream ./utils {};
config = builtins.fromJSON (builtins.readFile ./config.json);
@ -61,6 +74,20 @@ let
# the translator modules and utils for all subsystems
translators = callPackageDream ./translators {};
externals = {
node2nix = nodejs: pkgs.callPackage "${externalSources.node2nix}/nix/node-env.nix" { inherit nodejs; };
nix-parsec = rec {
lexer = import "${externalSources.nix-parsec}/lexer.nix" { inherit parsec; };
parsec = import "${externalSources.nix-parsec}/parsec.nix";
};
};
dreamOverrides = lib.genAttrs (utils.dirNames overridesDir) (name:
import (overridesDir + "/${name}") {
inherit lib pkgs;
}
);
# the location of the dream2nix framework for self references (update scripts, etc.)
dream2nixWithExternals =
if b.pathExists (./. + "/external") then
@ -70,16 +97,10 @@ let
cp -r ${./.} $out
chmod +w $out
mkdir $out/external
ls -lah ${externalSources}
cp -r ${externalSources}/* $out/external/
ls -lah ${externalDir}
cp -r ${externalDir}/* $out/external/
'';
in
rec {
inherit apps builders dream2nixWithExternals fetchers translators updaters utils;
# automatically find a suitable builder for a given generic lock
findBuilder = dreamLock:
let
@ -234,7 +255,7 @@ rec {
inherit (dreamLockInterface)
buildSystemAttrs
dependenciesRemoved
cyclicDependencies
getDependencies
getCyclicDependencies
mainPackageName
@ -306,10 +327,50 @@ rec {
;
builder = builder';
builderArgs = (args.builderArgs or {}) // {
inherit packageOverrides;
packageOverrides =
args.packageOverrides or {}
// dreamOverrides."${dreamLock.generic.buildSystem}" or {};
};
};
# 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`
formattedBuilderOutputs = builderOutputs // {
packages =
let
allPackages = builderOutputs.packages or {};
latestPackages =
lib.mapAttrs'
(pname: releases:
let
latest =
releases."${utils.latestVersion (b.attrNames releases)}";
in
(lib.nameValuePair
"${pname}"
(latest // {
versions = releases;
})))
allPackages;
in
latestPackages;
};
in
builderOutputs;
formattedBuilderOutputs;
in
{
inherit
apps
builders
dream2nixWithExternals
fetchers
fetchSources
riseAndShine
translators
updaters
utils
;
}

View File

@ -95,11 +95,11 @@ rec {
translateHttpUrl =
let
fetcher = fetchers.fetchurl;
fetcherOutputs = fetchers.fetchurl.outputs { url = shortcut; };
fetcher = fetchers.http;
fetcherOutputs = fetchers.http.outputs { url = shortcut; };
in
constructSource {
type = "fetchurl";
type = "http";
hash = fetcherOutputs.calcHash "sha256";
url = shortcut;
};

View File

@ -1,5 +1,5 @@
{
fetchurl,
http,
python3,
utils,
@ -25,7 +25,7 @@
calcHash = algo: utils.hashPath algo (
let
firstChar = builtins.substring 0 1 pname;
result = b.fetchurl {
result = b.http {
url =
"https://files.pythonhosted.org/packages/source/"
+ "${firstChar}/${pname}/${pname}-${version}.${extension}";
@ -38,7 +38,7 @@
fetched = hash:
let
firstChar = builtins.substring 0 1 pname;
result = (fetchurl {
result = (http {
url =
"https://files.pythonhosted.org/packages/source/"
+ "${firstChar}/${pname}/${pname}-${version}.${extension}";

View File

@ -6,8 +6,9 @@
nixpkgsSrc ? <nixpkgs>,
lib ? (import nixpkgsSrc {}).lib,
# dream2nix
makeExternalSources,
# (if called impurely ./default.nix will handle externals and overrides)
externalSources ? null,
overridesDir ? null,
}:
let
@ -79,10 +80,14 @@ in
dream2nixFor =
lib.mapAttrs
(system: pkgs:
import ./default.nix {
inherit pkgs;
externalSources = makeExternalSources pkgs;
})
import ./default.nix
({ inherit pkgs; }
// (lib.optionalAttrs (externalSources != null) {
inherit externalSources;
})
// (lib.optionalAttrs (overridesDir != null) {
inherit overridesDir;
})))
allPkgs;
allBuilderOutputs =

View File

@ -27,7 +27,7 @@
"url": "https://download.pypi.org/requests/2.28.0",
"hash": "000000000000000000000000000000000000000",
"version": "1.2.3",
"type": "fetchurl"
"type": "http"
}
},
"certifi": {
@ -35,7 +35,7 @@
"url": "https://download.pypi.org/certifi/2.0",
"hash": "000000000000000000000000000000000000000",
"version": "2.3.4",
"type": "fetchurl"
"type": "http"
}
}
}

View File

@ -16,7 +16,7 @@
"properties": {
"type": {
"enum": [
"fetchurl",
"http",
"git",
"github",
"gitlab",
@ -29,7 +29,7 @@
"allOf": [
{
"if": {
"properties": { "type": { "const": "fetchurl" } }
"properties": { "type": { "const": "http" } }
},
"then": {
"properties": {

View File

@ -13,7 +13,7 @@
{
# the input format is specified in /specifications/translator-call-example.json
# this script receives a json file including the input paths and specialArgs
# this script receives a json file including the input paths and extraArgs
translateBin = writeScriptBin "translate" ''
#!${bash}/bin/bash
@ -59,7 +59,7 @@
# - string argument (type = "argument")
# - boolean flag (type = "flag")
# String arguments contain a default value and examples. Flags do not.
specialArgs = {
extraArgs = {
# Example: boolean option
# Flags always default to 'false' if not specified by the user

View File

@ -67,7 +67,7 @@
# - string argument (type = "argument")
# - boolean flag (type = "flag")
# String arguments contain a default value and examples. Flags do not.
specialArgs = {
extraArgs = {
# Example: boolean option
# Flags always default to 'false' if not specified by the user

View File

@ -6,7 +6,6 @@
pkgs,
callPackageDream,
externalSources,
externals,
dream2nixWithExternals,
utils,
@ -36,7 +35,7 @@ let
inherit subsystem type name;
translate = args:
translator.translate
((getSpecialArgsDefaults translator.specialArgs or {}) // args);
((getextraArgsDefaults translator.extraArgs or {}) // args);
};
@ -99,7 +98,7 @@ let
(t: {
inherit (t)
name
specialArgs
extraArgs
subsystem
type
;
@ -107,7 +106,7 @@ let
});
# pupulates a translators special args with defaults
getSpecialArgsDefaults = specialArgsDef:
getextraArgsDefaults = extraArgsDef:
lib.mapAttrs
(name: def:
if def.type == "flag" then
@ -115,7 +114,7 @@ let
else
def.default or null
)
specialArgsDef;
extraArgsDef;
in
{

View File

@ -106,14 +106,14 @@
if identifyGitSource dependencyObject then
"git"
else
"fetchurl";
"http";
sourceConstructors = {
git = dependencyObject:
utils.parseGitUrl dependencyObject.version;
fetchurl = dependencyObject:
http = dependencyObject:
rec {
version = dependencyObject.version;
url = dependencyObject.resolved;
@ -139,7 +139,7 @@
inputFiles = [];
};
specialArgs = {
extraArgs = {
dev = {
description = "include dependencies for development";

View File

@ -141,7 +141,7 @@
else if lib.hasInfix "@link:" dependencyObject.yarnName then
"path"
else
"fetchurl";
"http";
sourceConstructors = {
@ -164,9 +164,9 @@
path = lib.last (lib.splitString "@link:" dependencyObject.yarnName);
};
fetchurl = dependencyObject:
http = dependencyObject:
{
type = "fetchurl";
type = "http";
version = dependencyObject.version;
hash =
if dependencyObject ? integrity then
@ -214,7 +214,7 @@
# - string argument (type = "argument")
# - boolean flag (type = "flag")
# String arguments contain a default value and examples. Flags do not.
specialArgs = {
extraArgs = {
name = {
description = "The name of the main package";

View File

@ -16,7 +16,7 @@
let
b = builtins;
machNixExtractor = "${externalSources}/mach-nix-lib/default.nix";
machNixExtractor = "${externalSources.mach-nix}/lib/default.nix";
setuptools_shim = ''
import sys, setuptools, tokenize, os; sys.argv[0] = 'setup.py'; __file__='setup.py';
@ -31,7 +31,7 @@ in
{
# the input format is specified in /specifications/translator-call-example.json
# this script receives a json file including the input paths and specialArgs
# this script receives a json file including the input paths and extraArgs
translateBin = utils.writePureShellScript
[
bash
@ -115,7 +115,7 @@ in
};
# define special args and provide defaults
specialArgs = {
extraArgs = {
# the python attribute
pythonAttr = {

View File

@ -79,7 +79,7 @@ def main():
dream_lock['sources'][pname][data['version']] = dict(
url=data['url'],
hash=data['sha256'],
type='fetchurl',
type='http',
)
# dump generic lock to stdout (json)

View File

@ -120,4 +120,42 @@ rec {
nameVersionToKey = nameVersion:
"${nameVersion.name}#${nameVersion.version}";
# determines if version v1 is greater than version v2
versionGreater = v1: v2:
versionGreaterList
(lib.splitString "." v1)
(lib.splitString "." v2);
# internal helper for 'versionGreater'
versionGreaterList = v1: v2:
let
head1 = b.head v1;
head2 = b.head v2;
n1 =
if builtins.match ''[[:digit:]]*'' head1 != null then
lib.toInt head1
else
0;
n2 = if builtins.match ''[[:digit:]]*'' head2 != null then
lib.toInt head2
else
0;
in
if n1 > n2 then
true
else
# end recursion condition
if b.length v1 == 1 || b.length v1 == 1 then
false
else
# continue recursion
versionGreaterList (b.tail v1) (b.tail v2);
# picks the latest version from a list of version strings
latestVersion = versions:
b.head
(lib.sort
(v1: v2: versionGreater v1 v2)
versions);
}

View File

@ -48,7 +48,7 @@ let
{}
(b.map (utils.keyToNameVersion) allDependencyKeys);
dependenciesRemoved =
cyclicDependencies =
lib.mapAttrs
(name: versions:
lib.mapAttrs
@ -56,7 +56,7 @@ let
lib.forEach removedKeys
(rKey: utils.keyToNameVersion rKey))
versions)
lock.generic.dependenciesRemoved or {};
lock.generic.cyclicDependencies or {};
# Format:
# {
@ -76,14 +76,14 @@ let
if dependenciesAttrs ? "${pname}#${version}" then
# filter out cyclicDependencies
lib.filter
(dep: ! b.elem dep (dependenciesRemoved."${pname}"."${version}" or []))
(dep: ! b.elem dep (cyclicDependencies."${pname}"."${version}" or []))
dependenciesAttrs."${pname}#${version}"
# assume no deps if package not found in dependencyGraph
else
[];
getCyclicDependencies = pname: version:
dependenciesRemoved."${pname}"."${version}" or [];
cyclicDependencies."${pname}"."${version}" or [];
in
{
@ -97,7 +97,7 @@ let
inherit
buildSystemAttrs
dependenciesRemoved
cyclicDependencies
getCyclicDependencies
getDependencies
packageVersions

View File

@ -6,21 +6,34 @@ let
b = builtins;
exampleOverrides = {
hello = [
{
description = "patch hello";
condition = pkg: if pkg.version > 3.0.0 then true else false;
overrideAttrs = old: {
patches = [];
throwErrorUnclearAttributeOverride = pname: overrideName: attrName:
throw ''
Error while applying override for ${pname}: '${overrideName}'
There are multiple override functions accepting an argument named '${attrName}'.
Please modify the override to clarify which override function should be used.
Instead of:
```
"${pname}" = {
"${overrideName}" = {
...
${attrName} = ...;
...
};
};
override = old: {
withPython = false;
```
Use:
```
"${pname}" = {
"${overrideName}" = {
...
overrideAttrs = oldAttrs: {
${attrName} = ...;
};
...
};
};
}
];
};
```
'';
applyOverridesToPackage = conditionalOverrides: pkg: pname:
if ! conditionalOverrides ? "${pname}" then
@ -31,16 +44,30 @@ let
# if condition is unset, it will be assumed true
evalCondition = condOverride: pkg:
if condOverride ? condition then
condOverride.condition pkg
if condOverride ? _condition then
condOverride._condition pkg
else
true;
# filter the overrides by the package name and conditions
overridesToApply =
(lib.filter
(condOverride: evalCondition condOverride pkg)
conditionalOverrides."${pname}");
let
overridesForPackage = conditionalOverrides."${pname}";
overridesListForPackage =
lib.mapAttrsToList
(_name: data: data // { inherit _name; })
overridesForPackage;
in
(lib.filter
(condOverride: evalCondition condOverride pkg)
overridesListForPackage);
# apply single attribute override
applySingleAttributeOverride = oldVal: functionOrValue:
if b.isFunction functionOrValue then
functionOrValue oldVal
else
functionOrValue;
# helper to apply one conditional override
# the condition is not evaluated anymore here
@ -48,8 +75,46 @@ let
let
overrideFuncs =
lib.mapAttrsToList
(funcName: func: { inherit funcName func; })
(lib.filterAttrs (n: v: lib.hasPrefix "override" n) condOverride);
(funcName: func: { inherit funcName func; })
(lib.filterAttrs (n: v: lib.hasPrefix "override" n) condOverride);
singleArgOverrideFuncs =
let
availableFunctions =
lib.mapAttrs
(funcName: func: lib.attrNames (lib.functionArgs func))
(lib.filterAttrs
(funcName: func: lib.hasPrefix "override" funcName)
pkg);
getOverrideFuncNameForAttrName = attrName:
let
applicableFuncs =
lib.attrNames
(lib.filterAttrs
(funcName: args: b.elem attrName args)
availableFunctions);
in
if b.length applicableFuncs == 0 then
"overrideAttrs"
else if b.length applicableFuncs >= 1 then
throwErrorUnclearAttributeOverride pname condOverride._name attrName
else
b.elemAt applicableFuncs 0;
attributeOverrides =
lib.filterAttrs
(n: v: ! lib.hasPrefix "override" n && ! lib.hasPrefix "_" n)
condOverride;
in
lib.mapAttrsToList
(attrName: funcOrValue: {
funcName = getOverrideFuncNameForAttrName attrName;
func = oldAttrs: { "${attrName}" = funcOrValue; };
})
attributeOverrides;
in
b.foldl'
(pkg: overrideFunc:
@ -59,14 +124,11 @@ let
updateAttrsFuncs = overrideFunc.func old;
in
lib.mapAttrs
(attrName: maybeFunction:
if b.isFunction maybeFunction then
maybeFunction old."${attrName}"
else
maybeFunction)
(attrName: functionOrValue:
applySingleAttributeOverride old."${attrName}" functionOrValue)
updateAttrsFuncs))
pkg
overrideFuncs;
(overrideFuncs ++ singleArgOverrideFuncs);
in
# apply the overrides to the given pkg
(lib.foldl