mirror of
https://github.com/nix-community/dream2nix.git
synced 2024-11-29 23:35:33 +03:00
Port php to dream2nix modules (#623)
* init: composer * initial attempt to translating composer lock * fix(v1/php): use simpleTranslate2 instead of simpleTranslate * chore: rebasing * init: granular module * lib/internal/simpleTranslate2: revert accidental changes * php: fix php-granular builder module * php-compser-lock: cleanup --------- Co-authored-by: asrar <aszenz@gmail.com>
This commit is contained in:
parent
9b721a5cd3
commit
cf0bd2e99a
31
examples/dream2nix-packages-php/composer-project/default.nix
Normal file
31
examples/dream2nix-packages-php/composer-project/default.nix
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
dream2nix,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
imports = [
|
||||||
|
dream2nix.modules.drv-parts.php-composer-lock
|
||||||
|
dream2nix.modules.drv-parts.php-granular
|
||||||
|
];
|
||||||
|
|
||||||
|
mkDerivation = {
|
||||||
|
src = config.deps.fetchFromGitHub {
|
||||||
|
owner = "Gipetto";
|
||||||
|
repo = "CowSay";
|
||||||
|
rev = config.version;
|
||||||
|
sha256 = "sha256-jriyCzmvT2pPeNQskibBg0Bsh+h64cAEO+yOOfX2wbA=";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
deps = {nixpkgs, ...}: {
|
||||||
|
inherit
|
||||||
|
(nixpkgs)
|
||||||
|
fetchFromGitHub
|
||||||
|
stdenv
|
||||||
|
;
|
||||||
|
};
|
||||||
|
|
||||||
|
name = "cowsay";
|
||||||
|
version = "1.2.0";
|
||||||
|
}
|
197
lib/internal/php-semver.nix
Normal file
197
lib/internal/php-semver.nix
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
{lib, ...}: let
|
||||||
|
l = lib // builtins;
|
||||||
|
|
||||||
|
# Replace a list entry at defined index with set value
|
||||||
|
ireplace = idx: value: list:
|
||||||
|
l.genList (i:
|
||||||
|
if i == idx
|
||||||
|
then value
|
||||||
|
else (l.elemAt list i)) (l.length list);
|
||||||
|
|
||||||
|
orBlank = x:
|
||||||
|
if x != null
|
||||||
|
then x
|
||||||
|
else "";
|
||||||
|
|
||||||
|
operators = let
|
||||||
|
mkComparison = ret: version: v:
|
||||||
|
builtins.compareVersions version v == ret;
|
||||||
|
|
||||||
|
mkCaretComparison = version: v: let
|
||||||
|
ver = builtins.splitVersion v;
|
||||||
|
major = l.toInt (l.head ver);
|
||||||
|
upper = builtins.toString (l.toInt (l.head ver) + 1);
|
||||||
|
in
|
||||||
|
if major == 0
|
||||||
|
then mkTildeComparison version v
|
||||||
|
else operators.">=" version v && operators."<" version upper;
|
||||||
|
|
||||||
|
mkTildeComparison = version: v: let
|
||||||
|
ver = builtins.splitVersion v;
|
||||||
|
len = l.length ver;
|
||||||
|
truncated =
|
||||||
|
if len > 1
|
||||||
|
then l.init ver
|
||||||
|
else ver;
|
||||||
|
idx = (l.length truncated) - 1;
|
||||||
|
minor = l.toString (l.toInt (l.elemAt truncated idx) + 1);
|
||||||
|
upper = l.concatStringsSep "." (ireplace idx minor truncated);
|
||||||
|
in
|
||||||
|
operators.">=" version v && operators."<" version upper;
|
||||||
|
in {
|
||||||
|
# Prefix operators
|
||||||
|
"==" = mkComparison 0;
|
||||||
|
">" = mkComparison 1;
|
||||||
|
"<" = mkComparison (-1);
|
||||||
|
"!=" = v: c: !operators."==" v c;
|
||||||
|
">=" = v: c: operators."==" v c || operators.">" v c;
|
||||||
|
"<=" = v: c: operators."==" v c || operators."<" v c;
|
||||||
|
# Semver specific operators
|
||||||
|
"~" = mkTildeComparison;
|
||||||
|
"^" = mkCaretComparison;
|
||||||
|
};
|
||||||
|
|
||||||
|
re = {
|
||||||
|
operators = "([=><!~^]+)";
|
||||||
|
version = "((0|[1-9][0-9]*)[.](0|[1-9][0-9]*)[.](0|[1-9][0-9]*)[.](0|[1-9][0-9]*)|(0|[1-9][0-9]*)[.](0|[1-9][0-9]*)[.](0|[1-9][0-9]*)|(0|[1-9][0-9]*)[.](0|[1-9][0-9]*)|(0|[1-9][0-9]*))?(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\\+([0-9a-zA-Z-]+(\\.[0-9a-zA-Z-]+)*))?";
|
||||||
|
};
|
||||||
|
|
||||||
|
reLengths = {
|
||||||
|
operators = 1;
|
||||||
|
version = 16;
|
||||||
|
};
|
||||||
|
|
||||||
|
parseConstraint = constraintStr: let
|
||||||
|
# The common prefix operators
|
||||||
|
mPre = l.match "${re.operators} *${re.version}" constraintStr;
|
||||||
|
# There is an upper bound to the operator (this implementation is a bit hacky)
|
||||||
|
mUpperBound =
|
||||||
|
l.match "${re.operators} *${re.version} *< *${re.version}" constraintStr;
|
||||||
|
# There is also an infix operator to match ranges
|
||||||
|
mIn = l.match "${re.version} - *${re.version}" constraintStr;
|
||||||
|
# There is no operators
|
||||||
|
mNone = l.match "${re.version}" constraintStr;
|
||||||
|
in (
|
||||||
|
if mPre != null
|
||||||
|
then {
|
||||||
|
ops.t = l.elemAt mPre 0;
|
||||||
|
v = orBlank (l.elemAt mPre reLengths.operators);
|
||||||
|
}
|
||||||
|
# Infix operators are range matches
|
||||||
|
else if mIn != null
|
||||||
|
then {
|
||||||
|
ops = {
|
||||||
|
t = "-";
|
||||||
|
l = ">=";
|
||||||
|
u = "<=";
|
||||||
|
};
|
||||||
|
v = {
|
||||||
|
vl = orBlank (l.elemAt mIn 0);
|
||||||
|
vu = orBlank (l.elemAt mIn reLengths.version);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if mUpperBound != null
|
||||||
|
then {
|
||||||
|
ops = {
|
||||||
|
t = "-";
|
||||||
|
l = l.elemAt mUpperBound 0;
|
||||||
|
u = "<";
|
||||||
|
};
|
||||||
|
v = {
|
||||||
|
vl = orBlank (l.elemAt mUpperBound reLengths.operators);
|
||||||
|
vu = orBlank (l.elemAt mUpperBound (reLengths.operators + reLengths.version));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if mNone != null
|
||||||
|
then {
|
||||||
|
ops.t = "==";
|
||||||
|
v = orBlank (l.elemAt mNone 0);
|
||||||
|
}
|
||||||
|
else throw ''Constraint "${constraintStr}" could not be parsed''
|
||||||
|
);
|
||||||
|
|
||||||
|
satisfiesSingleInternal = version: constraint: let
|
||||||
|
inherit (parseConstraint constraint) ops v;
|
||||||
|
in
|
||||||
|
if ops.t == "-"
|
||||||
|
then (operators."${ops.l}" version v.vl && operators."${ops.u}" version v.vu)
|
||||||
|
else operators."${ops.t}" version v;
|
||||||
|
|
||||||
|
# remove v from version strings: ^v1.2.3 -> ^1.2.3
|
||||||
|
# remove branch suffix: ^1.2.x-dev -> ^1.2
|
||||||
|
satisfiesSingle = version: constraint: let
|
||||||
|
removeStability = c: let
|
||||||
|
m = l.match "^(.*)[@][[:alpha:]]+$" c;
|
||||||
|
in
|
||||||
|
if m != null && l.length m >= 0
|
||||||
|
then l.head m
|
||||||
|
else c;
|
||||||
|
removeSuffix = c: let
|
||||||
|
m = l.match "^(.*)[-][[:alpha:]]+$" c;
|
||||||
|
in
|
||||||
|
if m != null && l.length m >= 0
|
||||||
|
then l.head m
|
||||||
|
else c;
|
||||||
|
wildcard = c: let
|
||||||
|
m = l.match "^([[:d:]]+.*)[.][*x]$" c;
|
||||||
|
in
|
||||||
|
if m != null && l.length m >= 0
|
||||||
|
then "~${l.head m}.0"
|
||||||
|
else c;
|
||||||
|
removeV = c: let
|
||||||
|
m = l.match "^(.)*v([[:d:]]+[.].*)$" c;
|
||||||
|
in
|
||||||
|
if m != null && l.length m > 0
|
||||||
|
then l.concatStrings m
|
||||||
|
else c;
|
||||||
|
isVersionLike = c: let
|
||||||
|
m = l.match "^([0-9><=!-^~*]*)$" c;
|
||||||
|
in
|
||||||
|
m != null && l.length m > 0;
|
||||||
|
cleanConstraint = removeV (wildcard (removeSuffix (removeStability (l.removePrefix "dev-" constraint))));
|
||||||
|
cleanVersion = l.removePrefix "v" (wildcard (removeSuffix version));
|
||||||
|
in
|
||||||
|
(l.elem (removeStability constraint) ["" "*"])
|
||||||
|
|| (version == constraint)
|
||||||
|
|| ((isVersionLike cleanConstraint) && (satisfiesSingleInternal cleanVersion cleanConstraint));
|
||||||
|
|
||||||
|
trim = s: l.head (l.match "^[[:space:]]*(.*[^[:space:]])[[:space:]]*$" s);
|
||||||
|
splitAlternatives = v: let
|
||||||
|
# handle version alternatives: ^1.2 || ^2.0
|
||||||
|
clean = l.replaceStrings ["||"] ["|"] v;
|
||||||
|
in
|
||||||
|
map trim (l.splitString "|" clean);
|
||||||
|
splitConjunctives = v: let
|
||||||
|
clean =
|
||||||
|
l.replaceStrings
|
||||||
|
["," " - " " -" "- " " as "]
|
||||||
|
[" " "-" "-" "-" "##"]
|
||||||
|
v;
|
||||||
|
cleanInlineAlias = v: let
|
||||||
|
m = l.match "^(.*)[#][#](.*)$" v;
|
||||||
|
in
|
||||||
|
if m != null && l.length m > 0
|
||||||
|
then l.head m
|
||||||
|
else v;
|
||||||
|
in
|
||||||
|
map
|
||||||
|
(x: trim (cleanInlineAlias x))
|
||||||
|
(l.filter (x: x != "") (l.splitString " " clean));
|
||||||
|
in rec {
|
||||||
|
# matching a version with semver
|
||||||
|
# 1.0.2 (~1.0.1 || >=2.1 <2.4)
|
||||||
|
satisfies = version: constraint:
|
||||||
|
l.any
|
||||||
|
(c:
|
||||||
|
l.all
|
||||||
|
(satisfiesSingle version)
|
||||||
|
(splitConjunctives c))
|
||||||
|
(splitAlternatives constraint);
|
||||||
|
|
||||||
|
# matching multiversion like the one in `provide` with semver
|
||||||
|
# (1.0|2.0) (^2.0 || 3.2 - 3.6)
|
||||||
|
multiSatisfies = multiversion: constraint:
|
||||||
|
l.any
|
||||||
|
(version: satisfies version constraint)
|
||||||
|
(splitAlternatives multiversion);
|
||||||
|
}
|
51
modules/drv-parts/php-composer-lock/default.nix
Normal file
51
modules/drv-parts/php-composer-lock/default.nix
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
l = lib // builtins;
|
||||||
|
cfg = config.php-composer-lock;
|
||||||
|
|
||||||
|
dreamLockUtils = import ../../../lib/internal/dreamLockUtils.nix {inherit lib;};
|
||||||
|
nodejsUtils = import ../../../lib/internal/nodejsUtils.nix {inherit lib parseSpdxId;};
|
||||||
|
parseSpdxId = import ../../../lib/internal/parseSpdxId.nix {inherit lib;};
|
||||||
|
prepareSourceTree = import ../../../lib/internal/prepareSourceTree.nix {inherit lib;};
|
||||||
|
simpleTranslate2 = import ../../../lib/internal/simpleTranslate2.nix {inherit lib;};
|
||||||
|
|
||||||
|
translate = import ./translate.nix {
|
||||||
|
inherit lib dreamLockUtils nodejsUtils parseSpdxId simpleTranslate2;
|
||||||
|
};
|
||||||
|
|
||||||
|
dreamLock = translate {
|
||||||
|
projectName = cfg.composerJson.name;
|
||||||
|
projectRelPath = "";
|
||||||
|
source = cfg.source;
|
||||||
|
tree = prepareSourceTree {source = cfg.source;};
|
||||||
|
noDev = ! cfg.withDevDependencies;
|
||||||
|
# php = "unknown";
|
||||||
|
inherit (cfg) composerJson composerLock;
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
imports = [
|
||||||
|
./interface.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
# declare external dependencies
|
||||||
|
deps = {nixpkgs, ...}: {
|
||||||
|
inherit
|
||||||
|
(nixpkgs)
|
||||||
|
fetchgit
|
||||||
|
fetchurl
|
||||||
|
nix
|
||||||
|
runCommandLocal
|
||||||
|
;
|
||||||
|
};
|
||||||
|
php-composer-lock = {
|
||||||
|
inherit dreamLock;
|
||||||
|
composerJson = l.fromJSON (l.readFile cfg.composerJsonFile);
|
||||||
|
composerLock =
|
||||||
|
if cfg.composerLockFile != null
|
||||||
|
then l.fromJSON (l.readFile cfg.composerLockFile)
|
||||||
|
else lib.mkDefault {};
|
||||||
|
};
|
||||||
|
}
|
53
modules/drv-parts/php-composer-lock/interface.nix
Normal file
53
modules/drv-parts/php-composer-lock/interface.nix
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
options,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
l = lib // builtins;
|
||||||
|
t = l.types;
|
||||||
|
cfg = config.php-composer-lock;
|
||||||
|
in {
|
||||||
|
options.php-composer-lock = l.mapAttrs (_: l.mkOption) {
|
||||||
|
dreamLock = {
|
||||||
|
type = t.attrs;
|
||||||
|
internal = true;
|
||||||
|
description = "The content of the dream2nix generated lock file";
|
||||||
|
};
|
||||||
|
composerJsonFile = {
|
||||||
|
type = t.path;
|
||||||
|
description = ''
|
||||||
|
The composer.json file to use.
|
||||||
|
'';
|
||||||
|
default = cfg.source + "/composer.json";
|
||||||
|
};
|
||||||
|
composerJson = {
|
||||||
|
type = t.attrs;
|
||||||
|
description = "The content of the composer.json";
|
||||||
|
};
|
||||||
|
composerLockFile = {
|
||||||
|
type = t.nullOr t.path;
|
||||||
|
description = ''
|
||||||
|
The composer.lock file to use.
|
||||||
|
'';
|
||||||
|
default = cfg.source + "/composer.lock";
|
||||||
|
};
|
||||||
|
composerLock = {
|
||||||
|
type = t.attrs;
|
||||||
|
description = "The content of the composer.lock";
|
||||||
|
};
|
||||||
|
source = {
|
||||||
|
type = t.either t.path t.package;
|
||||||
|
description = "Source of the package";
|
||||||
|
default = config.mkDerivation.src;
|
||||||
|
};
|
||||||
|
withDevDependencies = {
|
||||||
|
type = t.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Whether to include development dependencies.
|
||||||
|
Usually it's a bad idea to disable this, as development dependencies can contain important build time dependencies.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
260
modules/drv-parts/php-composer-lock/translate.nix
Normal file
260
modules/drv-parts/php-composer-lock/translate.nix
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
nodejsUtils,
|
||||||
|
dreamLockUtils,
|
||||||
|
simpleTranslate2,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
l = lib // builtins;
|
||||||
|
# translate from a given source and a project specification to a dream-lock.
|
||||||
|
translate = {
|
||||||
|
projectName,
|
||||||
|
projectRelPath,
|
||||||
|
composerLock,
|
||||||
|
composerJson,
|
||||||
|
tree,
|
||||||
|
noDev,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit
|
||||||
|
(import ../../../lib/internal/php-semver.nix {inherit lib;})
|
||||||
|
satisfies
|
||||||
|
multiSatisfies
|
||||||
|
;
|
||||||
|
|
||||||
|
# get the root source and project source
|
||||||
|
rootSource = tree.fullPath;
|
||||||
|
projectTree = tree.getNodeFromPath projectRelPath;
|
||||||
|
|
||||||
|
composerJson = (projectTree.getNodeFromPath "composer.json").jsonContent;
|
||||||
|
composerLock = (projectTree.getNodeFromPath "composer.lock").jsonContent;
|
||||||
|
|
||||||
|
# toplevel php semver
|
||||||
|
phpSemver = composerJson.require."php" or "*";
|
||||||
|
# all the php extensions
|
||||||
|
phpExtensions = let
|
||||||
|
allDepNames = l.flatten (map (x: l.attrNames (getRequire x)) packages);
|
||||||
|
extensions = l.unique (l.filter (l.hasPrefix "ext-") allDepNames);
|
||||||
|
in
|
||||||
|
map (l.removePrefix "ext-") extensions;
|
||||||
|
|
||||||
|
composerPluginApiSemver = l.listToAttrs (l.flatten (map
|
||||||
|
(
|
||||||
|
pkg: let
|
||||||
|
requires = getRequire pkg;
|
||||||
|
in
|
||||||
|
l.optional (requires ? "composer-plugin-api")
|
||||||
|
{
|
||||||
|
name = "${pkg.name}@${pkg.version}";
|
||||||
|
value = requires."composer-plugin-api";
|
||||||
|
}
|
||||||
|
)
|
||||||
|
packages));
|
||||||
|
|
||||||
|
# get cleaned pkg attributes
|
||||||
|
getRequire = pkg:
|
||||||
|
l.mapAttrs
|
||||||
|
(_: version: resolvePkgVersion pkg version)
|
||||||
|
(pkg.require or {});
|
||||||
|
getProvide = pkg:
|
||||||
|
l.mapAttrs
|
||||||
|
(_: version: resolvePkgVersion pkg version)
|
||||||
|
(pkg.provide or {});
|
||||||
|
getReplace = pkg:
|
||||||
|
l.mapAttrs
|
||||||
|
(_: version: resolvePkgVersion pkg version)
|
||||||
|
(pkg.replace or {});
|
||||||
|
|
||||||
|
resolvePkgVersion = pkg: version:
|
||||||
|
if version == "self.version"
|
||||||
|
then pkg.version
|
||||||
|
else version;
|
||||||
|
|
||||||
|
# project package
|
||||||
|
toplevelPackage = {
|
||||||
|
name = projectName;
|
||||||
|
version = composerJson.version or "unknown";
|
||||||
|
source = {
|
||||||
|
type = "path";
|
||||||
|
path = rootSource;
|
||||||
|
};
|
||||||
|
require =
|
||||||
|
(l.optionalAttrs (!noDev) (composerJson.require-dev or {}))
|
||||||
|
// (composerJson.require or {});
|
||||||
|
};
|
||||||
|
# all the packages
|
||||||
|
packages =
|
||||||
|
# Add the top-level package, this is not written in composer.lock
|
||||||
|
[toplevelPackage]
|
||||||
|
++ composerLock.packages
|
||||||
|
++ (l.optionals (!noDev) (composerLock.packages-dev or []));
|
||||||
|
# packages with replace/provide applied
|
||||||
|
resolvedPackages = let
|
||||||
|
apply = pkg: dep: candidates: let
|
||||||
|
original = getRequire pkg;
|
||||||
|
applied =
|
||||||
|
l.filterAttrs
|
||||||
|
(
|
||||||
|
name: semver:
|
||||||
|
!((candidates ? "${name}") && (multiSatisfies candidates."${name}" semver))
|
||||||
|
)
|
||||||
|
original;
|
||||||
|
in
|
||||||
|
pkg
|
||||||
|
// {
|
||||||
|
require =
|
||||||
|
applied
|
||||||
|
// (
|
||||||
|
l.optionalAttrs
|
||||||
|
(applied != original)
|
||||||
|
{"${dep.name}" = "${dep.version}";}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
dropMissing = pkgs: let
|
||||||
|
doDropMissing = pkg:
|
||||||
|
pkg
|
||||||
|
// {
|
||||||
|
require =
|
||||||
|
l.filterAttrs
|
||||||
|
(name: semver: l.any (pkg: (pkg.name == name) && (satisfies pkg.version semver)) pkgs)
|
||||||
|
(getRequire pkg);
|
||||||
|
};
|
||||||
|
in
|
||||||
|
map doDropMissing pkgs;
|
||||||
|
doReplace = pkg:
|
||||||
|
l.foldl
|
||||||
|
(pkg: dep: apply pkg dep (getProvide dep))
|
||||||
|
pkg
|
||||||
|
packages;
|
||||||
|
doProvide = pkg:
|
||||||
|
l.foldl
|
||||||
|
(pkg: dep: apply pkg dep (getReplace dep))
|
||||||
|
pkg
|
||||||
|
packages;
|
||||||
|
in
|
||||||
|
dropMissing (map (pkg: (doProvide (doReplace pkg))) packages);
|
||||||
|
|
||||||
|
# resolve semvers into exact versions
|
||||||
|
pinPackages = pkgs: let
|
||||||
|
clean = requires:
|
||||||
|
l.filterAttrs
|
||||||
|
(name: _:
|
||||||
|
!(l.elem name ["php" "composer-plugin-api" "composer-runtime-api"])
|
||||||
|
&& !(l.strings.hasPrefix "ext-" name))
|
||||||
|
requires;
|
||||||
|
doPin = name: semver:
|
||||||
|
(l.head
|
||||||
|
(l.filter (dep: satisfies dep.version semver)
|
||||||
|
(l.filter (dep: dep.name == name)
|
||||||
|
resolvedPackages)))
|
||||||
|
.version;
|
||||||
|
doPins = pkg:
|
||||||
|
pkg
|
||||||
|
// {
|
||||||
|
require = l.mapAttrs doPin (clean pkg.require);
|
||||||
|
};
|
||||||
|
in
|
||||||
|
map doPins pkgs;
|
||||||
|
in
|
||||||
|
simpleTranslate2
|
||||||
|
({objectsByKey, ...}: rec {
|
||||||
|
translatorName = "composer-lock";
|
||||||
|
|
||||||
|
# relative path of the project within the source tree.
|
||||||
|
location = projectRelPath;
|
||||||
|
|
||||||
|
# the name of the subsystem
|
||||||
|
subsystemName = "php";
|
||||||
|
|
||||||
|
# Extract subsystem specific attributes.
|
||||||
|
# The structure of this should be defined in:
|
||||||
|
# ./src/specifications/{subsystem}
|
||||||
|
subsystemAttrs = {
|
||||||
|
inherit noDev;
|
||||||
|
inherit phpSemver phpExtensions;
|
||||||
|
inherit composerPluginApiSemver;
|
||||||
|
};
|
||||||
|
|
||||||
|
# name of the default package
|
||||||
|
defaultPackage = toplevelPackage.name;
|
||||||
|
|
||||||
|
/*
|
||||||
|
List the package candidates which should be exposed to the user.
|
||||||
|
Only top-level packages should be listed here.
|
||||||
|
Users will not be interested in all individual dependencies.
|
||||||
|
*/
|
||||||
|
exportedPackages = {
|
||||||
|
"${defaultPackage}" = toplevelPackage.version;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
a list of raw package objects
|
||||||
|
If the upstream format is a deep attrset, this list should contain
|
||||||
|
a flattened representation of all entries.
|
||||||
|
*/
|
||||||
|
serializedRawObjects = pinPackages resolvedPackages;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Define extractor functions which each extract one property from
|
||||||
|
a given raw object.
|
||||||
|
(Each rawObj comes from serializedRawObjects).
|
||||||
|
|
||||||
|
Extractors can access the fields extracted by other extractors
|
||||||
|
by accessing finalObj.
|
||||||
|
*/
|
||||||
|
extractors = {
|
||||||
|
name = rawObj: finalObj:
|
||||||
|
rawObj.name;
|
||||||
|
|
||||||
|
version = rawObj: finalObj:
|
||||||
|
rawObj.version;
|
||||||
|
|
||||||
|
dependencies = rawObj: finalObj:
|
||||||
|
l.attrsets.mapAttrsToList
|
||||||
|
(name: version: {inherit name version;})
|
||||||
|
(getRequire rawObj);
|
||||||
|
|
||||||
|
sourceSpec = rawObj: finalObj:
|
||||||
|
if rawObj ? "source" && rawObj.source.type == "path"
|
||||||
|
then {
|
||||||
|
inherit (rawObj.source) type path;
|
||||||
|
rootName = finalObj.name;
|
||||||
|
rootVersion = finalObj.version;
|
||||||
|
}
|
||||||
|
else if rawObj ? "source" && rawObj.source.type == "git"
|
||||||
|
then {
|
||||||
|
inherit (rawObj.source) type url;
|
||||||
|
rev = rawObj.source.reference;
|
||||||
|
submodules = false;
|
||||||
|
}
|
||||||
|
else if rawObj ? "dist" && rawObj.dist.type == "path"
|
||||||
|
then {
|
||||||
|
inherit (rawObj.dist) type;
|
||||||
|
path = rawObj.dist.url;
|
||||||
|
rootName = null;
|
||||||
|
rootVersion = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
l.abort ''
|
||||||
|
Cannot find source for ${finalObj.name}@${finalObj.version},
|
||||||
|
rawObj: ${l.toJSON rawObj}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Optionally define extra extractors which will be used to key all
|
||||||
|
final objects, so objects can be accessed via:
|
||||||
|
`objectsByKey.${keyName}.${value}`
|
||||||
|
*/
|
||||||
|
keys = {
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Optionally add extra objects (list of `finalObj`) to be added to
|
||||||
|
the dream-lock.
|
||||||
|
*/
|
||||||
|
extraObjects = [
|
||||||
|
];
|
||||||
|
});
|
||||||
|
in
|
||||||
|
translate
|
301
modules/drv-parts/php-granular/default.nix
Normal file
301
modules/drv-parts/php-granular/default.nix
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
dream2nix,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
l = lib // builtins;
|
||||||
|
|
||||||
|
cfg = config.php-granular;
|
||||||
|
|
||||||
|
dreamLock = config.php-composer-lock.dreamLock;
|
||||||
|
|
||||||
|
fetchDreamLockSources =
|
||||||
|
import ../../../lib/internal/fetchDreamLockSources.nix
|
||||||
|
{inherit lib;};
|
||||||
|
getDreamLockSource = import ../../../lib/internal/getDreamLockSource.nix {inherit lib;};
|
||||||
|
readDreamLock = import ../../../lib/internal/readDreamLock.nix {inherit lib;};
|
||||||
|
hashPath = import ../../../lib/internal/hashPath.nix {
|
||||||
|
inherit lib;
|
||||||
|
inherit (config.deps) runCommandLocal nix;
|
||||||
|
};
|
||||||
|
|
||||||
|
# fetchers
|
||||||
|
fetchers = {
|
||||||
|
git = import ../../../lib/internal/fetchers/git {
|
||||||
|
inherit hashPath;
|
||||||
|
inherit (config.deps) fetchgit;
|
||||||
|
};
|
||||||
|
path = import ../../../lib/internal/fetchers/path {
|
||||||
|
inherit hashPath;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
dreamLockLoaded = readDreamLock {inherit dreamLock;};
|
||||||
|
dreamLockInterface = dreamLockLoaded.interface;
|
||||||
|
|
||||||
|
inherit (dreamLockInterface) defaultPackageName defaultPackageVersion;
|
||||||
|
|
||||||
|
fetchedSources = fetchDreamLockSources {
|
||||||
|
inherit defaultPackageName defaultPackageVersion;
|
||||||
|
inherit (dreamLockLoaded.lock) sources;
|
||||||
|
inherit fetchers;
|
||||||
|
};
|
||||||
|
|
||||||
|
getSource = getDreamLockSource fetchedSources;
|
||||||
|
|
||||||
|
inherit
|
||||||
|
(dreamLockInterface)
|
||||||
|
getDependencies # name: version: -> [ {name=; version=; } ]
|
||||||
|
# Attributes
|
||||||
|
|
||||||
|
subsystemAttrs # attrset
|
||||||
|
packageVersions
|
||||||
|
;
|
||||||
|
|
||||||
|
inherit (import ../../../lib/internal/php-semver.nix {inherit lib;}) satisfies;
|
||||||
|
|
||||||
|
# php with required extensions
|
||||||
|
php =
|
||||||
|
if satisfies config.deps.php81.version subsystemAttrs.phpSemver
|
||||||
|
then
|
||||||
|
config.deps.php81.withExtensions
|
||||||
|
(
|
||||||
|
{
|
||||||
|
all,
|
||||||
|
enabled,
|
||||||
|
}:
|
||||||
|
l.unique (enabled
|
||||||
|
++ (l.attrValues (l.filterAttrs (e: _: l.elem e subsystemAttrs.phpExtensions) all)))
|
||||||
|
)
|
||||||
|
else
|
||||||
|
l.abort ''
|
||||||
|
Error: incompatible php versions.
|
||||||
|
Package "${defaultPackageName}" defines required php version:
|
||||||
|
"php": "${subsystemAttrs.phpSemver}"
|
||||||
|
Using php version "${config.deps.php81.version}" from attribute "config.deps.php81".
|
||||||
|
'';
|
||||||
|
composer = php.packages.composer;
|
||||||
|
|
||||||
|
# packages to export
|
||||||
|
# packages =
|
||||||
|
# {default = packages.${defaultPackageName};}
|
||||||
|
# // (
|
||||||
|
# lib.mapAttrs
|
||||||
|
# (name: version: {
|
||||||
|
# "${version}" = allPackages.${name}.${version};
|
||||||
|
# })
|
||||||
|
# dreamLockInterface.packages
|
||||||
|
# );
|
||||||
|
# devShells =
|
||||||
|
# {default = devShells.${defaultPackageName};}
|
||||||
|
# // (
|
||||||
|
# l.mapAttrs
|
||||||
|
# (name: version: packages.${name}.${version}.devShell)
|
||||||
|
# dreamLockInterface.packages
|
||||||
|
# );
|
||||||
|
|
||||||
|
# Generates a derivation for a specific package name + version
|
||||||
|
commonModule = name: version: let
|
||||||
|
isTopLevel = dreamLockInterface.packages.name or null == version;
|
||||||
|
|
||||||
|
# name = l.strings.sanitizeDerivationName name;
|
||||||
|
|
||||||
|
dependencies = getDependencies name version;
|
||||||
|
repositories = let
|
||||||
|
transform = dep: let
|
||||||
|
intoRepository = name: version: root: {
|
||||||
|
type = "path";
|
||||||
|
url = "${root}/vendor/${name}";
|
||||||
|
options = {
|
||||||
|
versions = {
|
||||||
|
"${l.strings.toLower name}" = "${version}";
|
||||||
|
};
|
||||||
|
symlink = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
getAllSubdependencies = deps: let
|
||||||
|
getSubdependencies = dep: let
|
||||||
|
subdeps = getDependencies dep.name dep.version;
|
||||||
|
in
|
||||||
|
l.flatten ([dep] ++ (getAllSubdependencies subdeps));
|
||||||
|
in
|
||||||
|
l.flatten (map getSubdependencies deps);
|
||||||
|
depRoot = cfg.deps."${dep.name}"."${dep.version}".public;
|
||||||
|
direct = intoRepository dep.name dep.version "${depRoot}/lib";
|
||||||
|
transitive =
|
||||||
|
map
|
||||||
|
(subdep: intoRepository subdep.name subdep.version "${depRoot}/lib/vendor/${dep.name}")
|
||||||
|
(getAllSubdependencies (getDependencies dep.name dep.version));
|
||||||
|
in
|
||||||
|
[direct] ++ transitive;
|
||||||
|
in
|
||||||
|
l.flatten (map transform dependencies);
|
||||||
|
repositoriesString =
|
||||||
|
l.toJSON
|
||||||
|
(repositories ++ [{packagist = false;}]);
|
||||||
|
|
||||||
|
dependenciesString = l.toJSON (l.listToAttrs (
|
||||||
|
map
|
||||||
|
(dep: {
|
||||||
|
name = l.strings.toLower dep.name;
|
||||||
|
value = dep.version;
|
||||||
|
})
|
||||||
|
(dependencies
|
||||||
|
++ l.optional (subsystemAttrs.composerPluginApiSemver ? "${name}@${version}")
|
||||||
|
{
|
||||||
|
name = "composer-plugin-api";
|
||||||
|
version = subsystemAttrs.composerPluginApiSemver."${name}@${version}";
|
||||||
|
})
|
||||||
|
));
|
||||||
|
|
||||||
|
versionString =
|
||||||
|
if version == "unknown"
|
||||||
|
then "0.0.0"
|
||||||
|
else version;
|
||||||
|
|
||||||
|
module = {config, ...}: {
|
||||||
|
imports = [
|
||||||
|
dream2nix.modules.drv-parts.mkDerivation
|
||||||
|
];
|
||||||
|
deps = {nixpkgs, ...}:
|
||||||
|
l.mapAttrs (_: l.mkDefault) {
|
||||||
|
inherit
|
||||||
|
(nixpkgs)
|
||||||
|
jq
|
||||||
|
mkShell
|
||||||
|
moreutils
|
||||||
|
php81
|
||||||
|
stdenv
|
||||||
|
;
|
||||||
|
};
|
||||||
|
public.devShell = import ./devShell.nix {
|
||||||
|
inherit
|
||||||
|
name
|
||||||
|
php
|
||||||
|
composer
|
||||||
|
;
|
||||||
|
pkg = config.public;
|
||||||
|
inherit (config.deps) mkShell;
|
||||||
|
};
|
||||||
|
php-granular = {
|
||||||
|
composerInstallFlags =
|
||||||
|
[
|
||||||
|
"--no-scripts"
|
||||||
|
"--no-plugins"
|
||||||
|
]
|
||||||
|
++ l.optional (subsystemAttrs.noDev || !isTopLevel) "--no-dev";
|
||||||
|
};
|
||||||
|
env = {
|
||||||
|
inherit dependenciesString repositoriesString;
|
||||||
|
};
|
||||||
|
mkDerivation = {
|
||||||
|
src = l.mkDefault (getSource name version);
|
||||||
|
|
||||||
|
nativeBuildInputs = with config.deps; [
|
||||||
|
jq
|
||||||
|
composer
|
||||||
|
moreutils
|
||||||
|
];
|
||||||
|
buildInputs = with config.deps;
|
||||||
|
[
|
||||||
|
php
|
||||||
|
composer
|
||||||
|
]
|
||||||
|
++ map (dep: cfg.deps."${dep.name}"."${dep.version}".public)
|
||||||
|
dependencies;
|
||||||
|
|
||||||
|
passAsFile = ["repositoriesString" "dependenciesString"];
|
||||||
|
|
||||||
|
unpackPhase = ''
|
||||||
|
runHook preUnpack
|
||||||
|
|
||||||
|
mkdir -p $out/lib/vendor/${name}
|
||||||
|
cd $out/lib/vendor/${name}
|
||||||
|
|
||||||
|
# copy source
|
||||||
|
cp -r ${config.mkDerivation.src}/* .
|
||||||
|
chmod -R +w .
|
||||||
|
|
||||||
|
# create composer.json if does not exist
|
||||||
|
if [ ! -f composer.json ]; then
|
||||||
|
echo "{}" > composer.json
|
||||||
|
fi
|
||||||
|
|
||||||
|
# save the original composer.json for reference
|
||||||
|
cp composer.json composer.json.orig
|
||||||
|
|
||||||
|
# set name & version
|
||||||
|
jq \
|
||||||
|
"(.name = \"${name}\") | \
|
||||||
|
(.version = \"${versionString}\") | \
|
||||||
|
(.extra.patches = {})" \
|
||||||
|
composer.json | sponge composer.json
|
||||||
|
|
||||||
|
runHook postUnpack
|
||||||
|
'';
|
||||||
|
configurePhase = ''
|
||||||
|
runHook preConfigure
|
||||||
|
|
||||||
|
# disable packagist, set path repositories
|
||||||
|
jq \
|
||||||
|
--slurpfile repositories $repositoriesStringPath \
|
||||||
|
--slurpfile dependencies $dependenciesStringPath \
|
||||||
|
"(.repositories = \$repositories[0]) | \
|
||||||
|
(.require = \$dependencies[0]) | \
|
||||||
|
(.\"require-dev\" = {})" \
|
||||||
|
composer.json | sponge composer.json
|
||||||
|
|
||||||
|
runHook postConfigure
|
||||||
|
'';
|
||||||
|
buildPhase = ''
|
||||||
|
runHook preBuild
|
||||||
|
|
||||||
|
# remove composer.lock if exists
|
||||||
|
rm -f composer.lock
|
||||||
|
|
||||||
|
# build
|
||||||
|
composer install ${l.strings.concatStringsSep " " config.php-granular.composerInstallFlags}
|
||||||
|
|
||||||
|
runHook postBuild
|
||||||
|
|
||||||
|
rm -rfv vendor/*/*/vendor
|
||||||
|
'';
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
|
||||||
|
BINS=$(jq -rcM "(.bin // [])[]" composer.json)
|
||||||
|
for bin in $BINS
|
||||||
|
do
|
||||||
|
mkdir -p $out/bin
|
||||||
|
pushd $out/bin
|
||||||
|
ln -s $out/lib/vendor/${name}/$bin
|
||||||
|
popd
|
||||||
|
done
|
||||||
|
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
module;
|
||||||
|
in {
|
||||||
|
imports = [
|
||||||
|
./interface.nix
|
||||||
|
(commonModule defaultPackageName defaultPackageVersion)
|
||||||
|
];
|
||||||
|
php-granular.deps =
|
||||||
|
lib.mapAttrs
|
||||||
|
(name: versions:
|
||||||
|
lib.genAttrs
|
||||||
|
versions
|
||||||
|
(
|
||||||
|
version:
|
||||||
|
# the submodule for this dependency
|
||||||
|
{...}: {
|
||||||
|
imports = [(commonModule name version)];
|
||||||
|
inherit name version;
|
||||||
|
}
|
||||||
|
))
|
||||||
|
packageVersions;
|
||||||
|
}
|
27
modules/drv-parts/php-granular/devShell.nix
Normal file
27
modules/drv-parts/php-granular/devShell.nix
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
name,
|
||||||
|
pkg,
|
||||||
|
mkShell,
|
||||||
|
php,
|
||||||
|
composer,
|
||||||
|
}:
|
||||||
|
mkShell {
|
||||||
|
buildInputs = [
|
||||||
|
php
|
||||||
|
composer
|
||||||
|
];
|
||||||
|
shellHook = let
|
||||||
|
vendorDir =
|
||||||
|
pkg.config.package-func.result.overrideAttrs
|
||||||
|
(_: {
|
||||||
|
dontInstall = true;
|
||||||
|
})
|
||||||
|
+ "/lib/vendor/${name}/vendor";
|
||||||
|
in ''
|
||||||
|
rm -rf ./vendor
|
||||||
|
mkdir vendor
|
||||||
|
cp -r ${vendorDir}/* vendor/
|
||||||
|
chmod -R +w ./vendor
|
||||||
|
export PATH="$PATH:$(realpath ./vendor)/bin"
|
||||||
|
'';
|
||||||
|
}
|
27
modules/drv-parts/php-granular/interface.nix
Normal file
27
modules/drv-parts/php-granular/interface.nix
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
dream2nix,
|
||||||
|
packageSets,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
l = lib // builtins;
|
||||||
|
t = l.types;
|
||||||
|
in {
|
||||||
|
options.php-granular = l.mapAttrs (_: l.mkOption) {
|
||||||
|
deps = {
|
||||||
|
type = t.attrsOf (t.attrsOf (t.submodule {
|
||||||
|
imports = [
|
||||||
|
dream2nix.modules.drv-parts.core
|
||||||
|
dream2nix.modules.drv-parts.mkDerivation
|
||||||
|
./interface.nix
|
||||||
|
];
|
||||||
|
_module.args = {inherit dream2nix packageSets;};
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
composerInstallFlags = {
|
||||||
|
type = t.listOf t.string;
|
||||||
|
default = [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user