nur-sery/nur_src/tools/compctl/ConfigFile.php

238 lines
7.1 KiB
PHP
Raw Normal View History

2023-12-03 22:10:18 +04:00
<?php
namespace nur\tools\compctl;
use nur\A;
use nur\b\ValueException;
use nur\data\types\md_utils;
use nur\data\types\Metadata;
use nur\path;
use nur\str;
use nur\yaml;
class ConfigFile {
const DEFAULT_DEP = "dev-master";
const DEFAULT_DEPS = [
"wip" => "dev-wip",
"develop" => "dev-develop",
];
const DEFAULT_ALLOW_LINK = false;
const DEFAULT_ALLOW_LINKS = [
"wip" => true,
"develop" => true,
];
const DEFAULT_CONFIG = [
# cette définition tient compte des valeurs DEFAULT_* ci-dessus
"defaults" => [
"wip" => "dev-wip",
"develop" => "dev-develop",
"master" => [
"version" => "dev-master",
"allow_link" => false,
],
],
];
const PROJNAMES = [
# noms de projets particuliers
"nulib/php" => "nulib",
];
const DEP_SCHEMA = [
"libname" => ["string", null, "nom de la librairie"],
"version" => ["?string", null, "version de la librairie"],
"projname" => ["?string", null, "nom du répertoire du projet sur le système de fichier"],
"type" => ["?string", null, "type de dépendance"],
"url" => ["?string", null, "url de la dépendance"],
"allow_link" => ["?bool", null, "peut-on lier vers le projet sur disque?"],
"link" => ["?bool", null, "faut-il ajouter l'url dans [repositories]"],
"branch" => ["?string", null, "branche de la dépendance dans le projet"],
];
function __construct(string $configFile, ?string $projdir=null, bool $ensureExists=true) {
if ($projdir === null) $projdir = dirname($configFile);
if ($ensureExists && !file_exists($configFile)) {
$message = path::ppath($projdir).": aucune configuration n'a été trouvée";
throw new ValueException($message);
}
$this->configFile = $configFile;
$this->projdir = $projdir;
}
/** @var string */
protected $configFile;
/** @var string */
protected $projdir;
/** @var array */
protected $data;
protected function load(): array {
if ($this->data === null) {
$this->data = yaml::load($this->configFile);
}
return $this->data;
}
function getv(string $pkey="", $default=null) {
$this->load();
return A::pget($this->data, $pkey, $default);
}
function geta(string $pkey="", array $default=[]): array {
return A::with($this->getv($pkey, $default));
}
function getBranchDeps(): array {
return $this->geta("defaults");
}
function getRequires(): array {
return $this->geta("require");
}
function getBranches(): array {
return $this->geta("branch");
}
private static $dep_md;
private static function dep_md(): Metadata {
return md_utils::ensure_md(self::$dep_md, self::DEP_SCHEMA);
}
/** @var array */
protected $duns;
function getDeps(string $branch): array {
$dun = A::get($this->duns, $branch);
if ($dun !== null) return $dun;
$branches = $this->getBranches();
$source = $branch;
$valid = array_key_exists($source, $branches);
if (!$valid && ($pos = strpos($branch, "/")) !== false) {
# pas de définition pour cette branche, essayer avec la branche de base
# si la branche est de la forme base/xxx
$source = substr($branch, 0, $pos);
$valid = array_key_exists($source, $branches);
}
if (!$valid && array_key_exists("master", $branches)) {
# la branche master est utilisée par défaut si elle existe
$source = "master";
$valid = true;
}
if (!$valid) {
$dun = [null, null, null];
} else {
$projdir = $this->projdir;
$branchDeps = $this->getBranchDeps() + self::DEFAULT_DEPS;
$defaultDep = null;
foreach ($branchDeps as $dbranch => $ddep) {
if ($branch == $dbranch) {
$defaultDep = $ddep;
break;
}
}
if ($defaultDep === null) {
foreach ($branchDeps as $dbranch => $ddep) {
if (str::starts_with("$dbranch/", $branch)) {
$defaultDep = $ddep;
break;
}
}
}
if ($defaultDep === null) $defaultDep = self::DEFAULT_DEP;
self::dep_md()->ensureSchema($defaultDep, "");
$defaultAllowLink = null;
foreach (self::DEFAULT_ALLOW_LINKS as $albranch => $alvalue) {
if ($branch == $albranch || str::starts_with("$albranch/", $branch)) {
$defaultAllowLink = $alvalue;
break;
}
}
if ($defaultAllowLink === null) $defaultAllowLink = self::DEFAULT_ALLOW_LINK;
# tout d'abord, lister les dépendances
$bdeps = [];
foreach ($this->getRequires() as $libname => $dep) {
if (strpos($libname, "/") === false) continue;
if ($dep === false) continue;
if ($dep !== null) {
self::dep_md()->ensureSchema($dep, $libname);
}
$bdeps[$libname] = $dep;
}
foreach ($this->geta("branch.$source") as $libname => $dep) {
if ($dep === false) {
# une définition par défaut peut être supprimée avec la valeur false
A::del($bdeps, $libname);
continue;
}
if ($dep !== null) {
self::dep_md()->ensureSchema($dep, $libname);
if (!array_key_exists($libname, $bdeps)) $bdeps[$libname] = $dep;
elseif ($bdeps[$libname] === null) $bdeps[$libname] = $dep;
else A::merge_nn($bdeps[$libname], $dep);
}
}
$deps = [];
$isUrls = [];
$notFoundUrls = [];
foreach ($bdeps as $libname => $dep) {
if ($dep === null) {
$dep = $defaultDep;
$dep["libname"] = $libname;
}
self::dep_md()->ensureSchema($dep, $libname);
$version = $dep["version"];
if ($version === null) $version = $defaultDep["version"];
$projname = $dep["projname"];
if ($projname === null) $projname = A::get(self::PROJNAMES, $libname);
if ($projname === null) {
$projname = str_replace("/", "-", $libname);
}
$type = $dep["type"];
if ($type === null) $type = "path";
$url = $dep["url"];
if ($url === null) $url = "../$projname";
$allowLink = $dep["allow_link"];
if ($allowLink === null) $allowLink = $defaultDep["allow_link"];
if ($allowLink === null) $allowLink = $defaultAllowLink;
$link = $dep["link"];
if ($link === null) {
$isDev = str::starts_with("dev-", $version)
|| str::ends_with("-dev", $version);
$link = $allowLink && $isDev;
}
if ($link && (
$type !== "path" ||
!path::is_dir("$projdir/$url", true)
)) {
$notFoundUrls[] = $url;
$link = false;
}
$vbranch = $version;
if (!str::del_prefix($vbranch, "dev-")
&& !str::del_suffix($vbranch, "-dev")) {
$vbranch = null;
}
A::merge($dep, [
"projname" => $projname,
"type" => $type,
"url" => $url,
"allow_link" => $allowLink,
"link" => $link,
"branch" => $vbranch,
]);
$deps[$libname] = $dep;
$isUrls[$url] = true;
}
$dun = [$deps, $isUrls, $notFoundUrls];
}
return $this->duns[$branch] = $dun;
}
}