mirror of
https://github.com/nix-community/dream2nix.git
synced 2024-11-26 09:46:04 +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