116 lines
3.5 KiB
PHP
116 lines
3.5 KiB
PHP
|
<?php
|
||
|
namespace nur\tools\compctl;
|
||
|
|
||
|
use Exception;
|
||
|
use nur\A;
|
||
|
|
||
|
class ComposerWalker {
|
||
|
protected static function get_project_dir($name, string $projects_dir) {
|
||
|
$project_dir = A::get(ConfigFile::PROJNAMES, $name);
|
||
|
if ($project_dir === null) {
|
||
|
$project_dir = preg_replace('/\//', "-", $name, 1);
|
||
|
}
|
||
|
return "$projects_dir/$project_dir";
|
||
|
}
|
||
|
|
||
|
static function parse_project(string $dir, array &$projects, ?array &$new_projects=null, bool $throw=false): ?array {
|
||
|
try {
|
||
|
$composer = new ComposerFile("$dir/composer.json");
|
||
|
} catch (Exception $e) {
|
||
|
if ($throw) throw $e;
|
||
|
else return null;
|
||
|
}
|
||
|
|
||
|
$pname = $composer->getv("name");
|
||
|
if (array_key_exists($pname, $projects) && $projects[$pname] !== null) {
|
||
|
# projet déjà analysé
|
||
|
return $projects[$pname];
|
||
|
}
|
||
|
|
||
|
$project = [
|
||
|
"composer" => $composer,
|
||
|
"name" => $pname,
|
||
|
"requires" => [],
|
||
|
"branches" => [],
|
||
|
"dir" => realpath($dir),
|
||
|
];
|
||
|
$requires = $composer->geta("require");
|
||
|
if ($requires !== null) {
|
||
|
foreach ($requires as $dep => $version) {
|
||
|
# ignorer les projets qui n'ont pas le préfixe requis
|
||
|
$include_project = false;
|
||
|
foreach (ConfigFile::PREFIXES as $prefix) {
|
||
|
if (substr($dep, 0, strlen($prefix)) === $prefix) {
|
||
|
$include_project = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!$include_project) continue;
|
||
|
# sinon, ajouter le projet aux dépendances
|
||
|
$project["requires"][] = $dep;
|
||
|
if (!array_key_exists($dep, $projects)) {
|
||
|
$projects[$dep] = null;
|
||
|
$new_projects[$dep] = true;
|
||
|
}
|
||
|
if (substr($version, 0, 4) === "dev-") {
|
||
|
$project["branches"][$dep] = substr($version, 4);
|
||
|
} else {
|
||
|
$project["branches"][$dep] = $version;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
$projects[$pname] = $project;
|
||
|
return $project;
|
||
|
}
|
||
|
|
||
|
static function build_projects(array $project_dirs, ?string &$projects_dir): array {
|
||
|
$projects = [];
|
||
|
foreach ($project_dirs as $project_dir) {
|
||
|
if ($projects_dir === null) $projects_dir = dirname($project_dir);
|
||
|
# analyser le projet initial
|
||
|
$new_projects = [];
|
||
|
if (self::parse_project($project_dir, $projects, $new_projects) === null) {
|
||
|
continue;
|
||
|
}
|
||
|
# puis analyser récursivement tous les projets dépendants
|
||
|
while (true) {
|
||
|
$have_new = false;
|
||
|
foreach ($new_projects as $name => &$new) {
|
||
|
if ($new) {
|
||
|
$new = false;
|
||
|
$have_new = true;
|
||
|
$project_dir = self::get_project_dir($name, $projects_dir);
|
||
|
self::parse_project($project_dir, $projects, $new_projects);
|
||
|
}
|
||
|
}; unset($new);
|
||
|
if (!$have_new) break;
|
||
|
}
|
||
|
}
|
||
|
return $projects;
|
||
|
}
|
||
|
|
||
|
static function order_deps(?array $deps, array $projects): array {
|
||
|
if ($deps === null) $deps = array_keys($projects);
|
||
|
$pi = 0;
|
||
|
$max = count($deps);
|
||
|
while ($pi < $max) {
|
||
|
$modified = false;
|
||
|
$project = $projects[$deps[$pi]];
|
||
|
if ($project !== null) {
|
||
|
# dépendance dont on a trouvé la correspondance sur disque
|
||
|
foreach ($project["requires"] as $dep) {
|
||
|
$di = array_search($dep, $deps);
|
||
|
if ($di > $pi) {
|
||
|
# si la dépendance se situe après le projet, la placer avant
|
||
|
$deps = array_splice($deps, $di, 1);
|
||
|
$deps = array_splice($deps, $pi, 0, [$dep]);
|
||
|
$modified = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (!$modified) $pi++;
|
||
|
}
|
||
|
return $deps;
|
||
|
}
|
||
|
}
|