mirror of
https://github.com/nix-community/dream2nix.git
synced 2024-12-25 23:41:42 +03:00
refactor python builder
This commit is contained in:
parent
eacec02ca8
commit
eac1e59763
@ -116,6 +116,7 @@
|
|||||||
devShellNodeModules = mkNodeModules {
|
devShellNodeModules = mkNodeModules {
|
||||||
isMain = true;
|
isMain = true;
|
||||||
installMethod = "copy";
|
installMethod = "copy";
|
||||||
|
reason = "devShell";
|
||||||
inherit pname version depsTree nodeModulesTree;
|
inherit pname version depsTree nodeModulesTree;
|
||||||
};
|
};
|
||||||
# type: nodeModules :: Derivation
|
# type: nodeModules :: Derivation
|
||||||
@ -143,8 +144,6 @@
|
|||||||
|
|
||||||
outputs = ["out" "lib"];
|
outputs = ["out" "lib"];
|
||||||
|
|
||||||
deps = nodeModules;
|
|
||||||
|
|
||||||
passthru = {
|
passthru = {
|
||||||
inherit nodeModules;
|
inherit nodeModules;
|
||||||
devShell = import ./lib/devShell.nix {
|
devShell = import ./lib/devShell.nix {
|
||||||
@ -217,7 +216,7 @@
|
|||||||
npm --production --offline --nodedir=$nodeSources run postinstall
|
npm --production --offline --nodedir=$nodeSources run postinstall
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
export NODE_MODULES_PATH=${nodeModules}
|
||||||
${nodejsBuilder}/bin/d2nMakeOutputs
|
${nodejsBuilder}/bin/d2nMakeOutputs
|
||||||
|
|
||||||
|
|
||||||
|
@ -122,6 +122,7 @@
|
|||||||
installPath ? "",
|
installPath ? "",
|
||||||
depsTree,
|
depsTree,
|
||||||
nodeModulesTree,
|
nodeModulesTree,
|
||||||
|
reason ? "default",
|
||||||
}:
|
}:
|
||||||
# dependency tree as JSON needed to build node_modules
|
# dependency tree as JSON needed to build node_modules
|
||||||
let
|
let
|
||||||
@ -140,11 +141,14 @@
|
|||||||
|
|
||||||
export isMain=${b.toString isMain}
|
export isMain=${b.toString isMain}
|
||||||
export installMethod=${installMethod}
|
export installMethod=${installMethod}
|
||||||
export installPath=${installPath}
|
export REASON=${reason}
|
||||||
|
|
||||||
${nodeModulesBuilder}
|
${nodeModulesBuilder}
|
||||||
|
|
||||||
cp -r /build/node_modules $out 2> /dev/null || mkdir $out
|
# make sure $out gets created every time, even if it is empty
|
||||||
|
if [ ! -d "$out" ]; then
|
||||||
|
mkdir -p $out
|
||||||
|
fi
|
||||||
'';
|
'';
|
||||||
in {
|
in {
|
||||||
inherit nodeModulesTree mkNodeModules;
|
inherit nodeModulesTree mkNodeModules;
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from .derivation import env
|
from .derivation import get_env
|
||||||
|
from .logger import logger
|
||||||
|
|
||||||
root = Path("/build")
|
# root is used to create the node_modules structure
|
||||||
node_modules = root / Path("node_modules")
|
# defaults to $out,
|
||||||
bin_dir = node_modules / Path(".bin")
|
# which will create the node_modules directly in
|
||||||
|
# $out of the derivation, and saves copy time
|
||||||
|
root = Path(get_env("out"))
|
||||||
|
bin_dir = root / Path(".bin")
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any, Callable, Literal, Optional, TypedDict, Union
|
from typing import Any, Callable, Literal, Optional, TypedDict
|
||||||
|
|
||||||
from .logger import logger
|
from .logger import logger
|
||||||
|
|
||||||
@ -130,7 +130,7 @@ def recurse_deps_tree(
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"stopped recursing the dependency tree at {dependency.repr()}\
|
f"stopped recursing the dependency tree at {dependency}\
|
||||||
-> because the predicate function returned 'False'"
|
-> because the predicate function returned 'False'"
|
||||||
)
|
)
|
||||||
return accumulator
|
return accumulator
|
||||||
|
@ -4,46 +4,54 @@
|
|||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any, Optional
|
from typing import Optional
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from .logger import logger
|
from .logger import logger
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
env: dict[str, str] = os.environ.copy()
|
env: dict[str, str] = os.environ.copy()
|
||||||
|
|
||||||
node_modules_link = env.get("NODE_MODULES_LINK")
|
|
||||||
|
@dataclass
|
||||||
|
class Input:
|
||||||
|
node_modules: Path
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Output:
|
class Output:
|
||||||
out: Path
|
out: Path
|
||||||
lib: Path
|
lib: Path
|
||||||
deps: Path
|
|
||||||
|
|
||||||
|
|
||||||
def get_outputs() -> Output:
|
def get_outputs() -> Output:
|
||||||
outputs = {
|
outputs = {
|
||||||
"out": Path(get_env().get("out")),
|
"out": Path(get_env("out")),
|
||||||
"lib": Path(get_env().get("lib")),
|
"lib": Path(get_env("lib")),
|
||||||
"deps": Path(get_env().get("deps")),
|
|
||||||
}
|
}
|
||||||
if None in outputs.values():
|
return Output(outputs["out"], outputs["lib"])
|
||||||
logger.error(
|
|
||||||
f"\
|
|
||||||
At least one out path uninitialized: {outputs}"
|
def get_inputs() -> Input:
|
||||||
)
|
node_modules_path = Path(get_env("NODE_MODULES_PATH"))
|
||||||
exit(1)
|
return Input(node_modules_path)
|
||||||
return Output(outputs["out"], outputs["lib"], outputs["deps"])
|
|
||||||
|
|
||||||
|
|
||||||
def is_main_package() -> bool:
|
def is_main_package() -> bool:
|
||||||
"""Returns True or False depending on the 'isMain' env variable."""
|
"""Returns True or False depending on the 'isMain' env variable."""
|
||||||
return bool(get_env().get("isMain"))
|
return bool(get_env("isMain"))
|
||||||
|
|
||||||
|
|
||||||
def get_env() -> dict[str, Any]:
|
def get_env(key: str) -> str:
|
||||||
"""Returns a copy of alle the current env variables"""
|
"""
|
||||||
return env
|
Returns the value of the required env variable
|
||||||
|
Prints an error end exits execution if the env variable is not set
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
value = env[key]
|
||||||
|
except KeyError:
|
||||||
|
logger.error(f"env variable ${key} is not set")
|
||||||
|
exit(1)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -54,7 +62,7 @@ class Info:
|
|||||||
|
|
||||||
def get_self() -> Info:
|
def get_self() -> Info:
|
||||||
""" """
|
""" """
|
||||||
return Info(get_env().get("pname", "unknown"), get_env().get("version", "unknown"))
|
return Info(env.get("pname", "unknown"), env.get("version", "unknown"))
|
||||||
|
|
||||||
|
|
||||||
class InstallMethod(Enum):
|
class InstallMethod(Enum):
|
||||||
@ -64,7 +72,7 @@ class InstallMethod(Enum):
|
|||||||
|
|
||||||
def get_install_method() -> InstallMethod:
|
def get_install_method() -> InstallMethod:
|
||||||
"""Returns the value of 'installMethod'"""
|
"""Returns the value of 'installMethod'"""
|
||||||
install_method: Optional[str] = get_env().get("installMethod")
|
install_method: Optional[str] = env.get("installMethod")
|
||||||
try:
|
try:
|
||||||
return InstallMethod(install_method)
|
return InstallMethod(install_method)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
@ -6,7 +6,7 @@ from typing import Any, Optional, TypedDict
|
|||||||
from .config import root
|
from .config import root
|
||||||
from .dependencies import Dependency, DepsTree, get_all_deps, recurse_deps_tree
|
from .dependencies import Dependency, DepsTree, get_all_deps, recurse_deps_tree
|
||||||
from .logger import logger
|
from .logger import logger
|
||||||
from .derivation import InstallMethod, get_install_method, get_self, node_modules_link
|
from .derivation import InstallMethod, get_install_method, get_self
|
||||||
from .package import (
|
from .package import (
|
||||||
NodeModulesPackage,
|
NodeModulesPackage,
|
||||||
NodeModulesTree,
|
NodeModulesTree,
|
||||||
@ -55,22 +55,31 @@ def _create_package_from_derivation(
|
|||||||
|
|
||||||
|
|
||||||
class Passthrough(TypedDict):
|
class Passthrough(TypedDict):
|
||||||
|
"""
|
||||||
|
Wrapper class
|
||||||
|
Holds global informations during recursion in <_make_folders_rec>
|
||||||
|
"""
|
||||||
|
|
||||||
all_deps: dict[str, Dependency]
|
all_deps: dict[str, Dependency]
|
||||||
flat_deps: list[str]
|
flat_deps: list[str]
|
||||||
|
|
||||||
|
|
||||||
def _make_folders_rec(
|
def _make_folders_rec(
|
||||||
node_modules_tree: NodeModulesPackage,
|
node_modules_tree: NodeModulesTree,
|
||||||
passthrough: Passthrough,
|
passthrough: Passthrough,
|
||||||
path: Path = Path("node_modules"),
|
path: Path = Path(""),
|
||||||
):
|
):
|
||||||
|
"""
|
||||||
|
Builds the node_modules folder structure
|
||||||
|
from the NodeModulesTree datastructures
|
||||||
|
"""
|
||||||
name: str
|
name: str
|
||||||
meta: NodeModulesTree
|
meta: NodeModulesPackage
|
||||||
|
|
||||||
for name, meta in node_modules_tree.items():
|
for name, meta in node_modules_tree.items():
|
||||||
|
|
||||||
version = meta["version"]
|
version = meta["version"]
|
||||||
dependencies: Optional[NodeModulesPackage] = meta.get("dependencies", None)
|
dependencies: Optional[NodeModulesTree] = meta.get("dependencies", None)
|
||||||
found_dependency = passthrough["all_deps"].get(f"{name}@{version}")
|
found_dependency = passthrough["all_deps"].get(f"{name}@{version}")
|
||||||
|
|
||||||
if found_dependency:
|
if found_dependency:
|
||||||
@ -121,11 +130,3 @@ def create_node_modules():
|
|||||||
nm_tree,
|
nm_tree,
|
||||||
passthrough={"all_deps": collected, "flat_deps": flat_deps},
|
passthrough={"all_deps": collected, "flat_deps": flat_deps},
|
||||||
)
|
)
|
||||||
|
|
||||||
if node_modules_link:
|
|
||||||
logger.debug(f"/build/node modules symlinked to '{node_modules_link}'.")
|
|
||||||
Path(node_modules_link).symlink_to(root / Path("node_modules"))
|
|
||||||
else:
|
|
||||||
logger.debug(
|
|
||||||
f"/build/node modules created but not exposed. $NODE_MODULES_LINK='{node_modules_link}'. Set it to valid path for automatic symlinks."
|
|
||||||
)
|
|
||||||
|
@ -22,7 +22,7 @@ def get_package_json(path: Path = Path("")) -> Union[dict[str, Any], None]:
|
|||||||
|
|
||||||
def has_scripts(
|
def has_scripts(
|
||||||
package_json: dict[str, Any],
|
package_json: dict[str, Any],
|
||||||
lifecycle_scripts: tuple[str] = (
|
lifecycle_scripts: tuple[str, str, str] = (
|
||||||
"preinstall",
|
"preinstall",
|
||||||
"install",
|
"install",
|
||||||
"postinstall",
|
"postinstall",
|
||||||
@ -56,14 +56,14 @@ def create_binary(target: Path, source: Path):
|
|||||||
|
|
||||||
def get_all_deps_tree() -> DepsTree:
|
def get_all_deps_tree() -> DepsTree:
|
||||||
deps = {}
|
deps = {}
|
||||||
dependenciesJsonPath = get_env().get("depsTreeJSONPath")
|
dependenciesJsonPath = get_env("depsTreeJSONPath")
|
||||||
if dependenciesJsonPath:
|
if dependenciesJsonPath:
|
||||||
with open(dependenciesJsonPath) as f:
|
with open(dependenciesJsonPath) as f:
|
||||||
deps = json.load(f)
|
deps = json.load(f)
|
||||||
return deps
|
return deps
|
||||||
|
|
||||||
|
|
||||||
class NodeModulesTree(TypedDict):
|
class NodeModulesPackage(TypedDict):
|
||||||
version: str
|
version: str
|
||||||
# mypy does not allow recursive types yet.
|
# mypy does not allow recursive types yet.
|
||||||
# The real type is:
|
# The real type is:
|
||||||
@ -71,12 +71,12 @@ class NodeModulesTree(TypedDict):
|
|||||||
dependencies: Optional[dict[str, Any]]
|
dependencies: Optional[dict[str, Any]]
|
||||||
|
|
||||||
|
|
||||||
NodeModulesPackage = dict[str, NodeModulesTree]
|
NodeModulesTree = dict[str, NodeModulesPackage]
|
||||||
|
|
||||||
|
|
||||||
def get_node_modules_tree() -> dict[str, Any]:
|
def get_node_modules_tree() -> dict[str, Any]:
|
||||||
tree = {}
|
tree = {}
|
||||||
dependenciesJsonPath = get_env().get("nmTreeJSONPath")
|
dependenciesJsonPath = get_env("nmTreeJSONPath")
|
||||||
if dependenciesJsonPath:
|
if dependenciesJsonPath:
|
||||||
with open(dependenciesJsonPath) as f:
|
with open(dependenciesJsonPath) as f:
|
||||||
tree = json.load(f)
|
tree = json.load(f)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
from .lib.checks import check_platform
|
from .lib.checks import check_platform
|
||||||
from .lib.config import node_modules
|
|
||||||
from .lib.derivation import (
|
from .lib.derivation import (
|
||||||
is_main_package,
|
is_main_package,
|
||||||
get_outputs,
|
get_outputs,
|
||||||
|
get_inputs,
|
||||||
get_self,
|
get_self,
|
||||||
InstallMethod,
|
InstallMethod,
|
||||||
get_install_method,
|
get_install_method,
|
||||||
@ -62,48 +62,39 @@ def makeOutputs():
|
|||||||
├── cli.js -> /nix/store/...-pname-1.0.0-lib/cli.js
|
├── cli.js -> /nix/store/...-pname-1.0.0-lib/cli.js
|
||||||
├── ...
|
├── ...
|
||||||
├── package.json -> /nix/store/...-pname-1.0.0-lib/package.json
|
├── package.json -> /nix/store/...-pname-1.0.0-lib/package.json
|
||||||
└── node_modules -> /nix/store/...-pname-1.0.0-deps
|
└── node_modules -> /nix/store/...-pname-1.0.0-node_modules
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# get the outputs from env ($out, $lib, $deps)
|
|
||||||
outputs = get_outputs()
|
outputs = get_outputs()
|
||||||
|
inputs = get_inputs()
|
||||||
|
pkg = get_self()
|
||||||
|
|
||||||
# create empty deps path for packages without dependencies
|
# create $lib output
|
||||||
if node_modules.exists():
|
|
||||||
# copy the tree and preserve symlinks
|
|
||||||
# copytree also checks for dangling symlinks and fails on broken links
|
|
||||||
shutil.copytree(node_modules, outputs.deps, symlinks=True)
|
|
||||||
else:
|
|
||||||
outputs.deps.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
# copy package content only
|
|
||||||
# TODO: apply filter logic from npm ('files' entry in package-json)
|
|
||||||
|
|
||||||
# remove the leftover symlink from d2nNodeModules phase
|
|
||||||
Path("./node_modules").unlink(missing_ok=True)
|
|
||||||
|
|
||||||
# TODO: run scripts ? (pre-, post-, install scripts)
|
|
||||||
shutil.copytree(Path("."), outputs.lib)
|
shutil.copytree(Path("."), outputs.lib)
|
||||||
|
|
||||||
|
# create $out output
|
||||||
bin_out = outputs.out / Path("bin")
|
bin_out = outputs.out / Path("bin")
|
||||||
lib_out = outputs.out / Path("lib")
|
lib_out = outputs.out / Path("lib")
|
||||||
bin_out.mkdir(parents=True, exist_ok=True)
|
bin_out.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
install_method = get_install_method()
|
install_method = get_install_method()
|
||||||
|
|
||||||
|
# create $out/lib
|
||||||
if install_method == InstallMethod.copy:
|
if install_method == InstallMethod.copy:
|
||||||
shutil.copytree(outputs.lib, lib_out, symlinks=True)
|
shutil.copytree(outputs.lib, lib_out, symlinks=True)
|
||||||
shutil.copytree(outputs.deps, lib_out / Path("node_modules"), symlinks=True)
|
shutil.copytree(
|
||||||
|
inputs.node_modules, lib_out / Path("node_modules"), symlinks=True
|
||||||
|
)
|
||||||
|
|
||||||
elif install_method == InstallMethod.symlink:
|
elif install_method == InstallMethod.symlink:
|
||||||
lib_out.mkdir(parents=True, exist_ok=True)
|
lib_out.mkdir(parents=True, exist_ok=True)
|
||||||
for entry in os.listdir(outputs.lib):
|
for entry in os.listdir(outputs.lib):
|
||||||
(lib_out / Path(entry)).symlink_to(outputs.lib / Path(entry))
|
(lib_out / Path(entry)).symlink_to(outputs.lib / Path(entry))
|
||||||
|
|
||||||
(lib_out / Path("node_modules")).symlink_to(outputs.deps)
|
(lib_out / Path("node_modules")).symlink_to(inputs.node_modules)
|
||||||
|
|
||||||
# create binaries
|
# create $out/bin
|
||||||
pkg = get_self()
|
# collect all binaries declared from package
|
||||||
|
# and create symlinks to their sources e.g. $out/bin/cli -> $out/lib/cli.json
|
||||||
dep = Dependency(name=pkg.name, version=pkg.version, derivation=outputs.lib)
|
dep = Dependency(name=pkg.name, version=pkg.version, derivation=outputs.lib)
|
||||||
binaries = get_bins(dep)
|
binaries = get_bins(dep)
|
||||||
for name, rel_path in binaries.items():
|
for name, rel_path in binaries.items():
|
||||||
|
Loading…
Reference in New Issue
Block a user