Compare commits

..

9 Commits

Author SHA1 Message Date
henderkes
cc59b39a02 string interpolation 2026-03-26 12:20:53 +07:00
henderkes
a8e50276d9 fix config validator to sort source.json (no changes) 2026-03-26 12:12:40 +07:00
henderkes
423b916736 add pecl type to downloader, fixes pecl/get giving us v3 of protobuf instead of v5. 2026-03-26 12:10:51 +07:00
Jerry Ma
d35cbd7bf8 Add Windows builders for libaom and brotli (#1072) 2026-03-23 16:21:46 +08:00
crazywhalecc
d076df6b04 Bump version number 2026-03-23 16:21:24 +08:00
crazywhalecc
a99b6bebae Remove freetype useless lib suggestions 2026-03-23 16:08:09 +08:00
Hendrik Mennen
864678ab46 Merge branch 'main' into feat/windows-libaom-brotli 2026-03-22 13:12:47 +01:00
Hendrik Mennen
c03508a84b Improve zlib Windows library detection for future zlib versions (#1070)
Co-authored-by: Hendrik Mennen <hmennen@gambio.ec1.de>
2026-03-22 20:11:29 +08:00
Hendrik Mennen
963e2a084a Add Windows builders for libaom and brotli libraries
Both libraries are listed in lib.json and used as transitive dependencies
(libaom via libavif, brotli via freetype/curl) but had no Windows builder,
causing builds to fail with "library [X] is in the lib.json list but not
supported to compile".

libaom: Uses builddir instead of build to avoid collision with the
source tree's build/cmake/ directory. Matches the Unix builder's
AOM_TARGET_CPU=generic setting for portability.

brotli: Standard CMake build with shared libs and tools disabled.

Also adds static-libs-windows entry for libaom in lib.json.
2026-03-22 09:07:47 +01:00
11 changed files with 168 additions and 104 deletions

View File

@@ -11,6 +11,7 @@
"require": { "require": {
"php": ">= 8.3", "php": ">= 8.3",
"ext-mbstring": "*", "ext-mbstring": "*",
"ext-simplexml": "*",
"ext-zlib": "*", "ext-zlib": "*",
"laravel/prompts": "^0.1.12", "laravel/prompts": "^0.1.12",
"symfony/console": "^5.4 || ^6 || ^7", "symfony/console": "^5.4 || ^6 || ^7",

View File

@@ -143,9 +143,7 @@
"zlib" "zlib"
], ],
"lib-suggests": [ "lib-suggests": [
"libpng", "libpng"
"bzip2",
"brotli"
] ]
}, },
"gettext": { "gettext": {
@@ -355,6 +353,9 @@
"static-libs-unix": [ "static-libs-unix": [
"libaom.a" "libaom.a"
], ],
"static-libs-windows": [
"aom.lib"
],
"cpp-library": true "cpp-library": true
}, },
"libargon2": { "libargon2": {

View File

@@ -8,30 +8,25 @@
"alt": false "alt": false
}, },
"amqp": { "amqp": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/amqp",
"path": "php-src/ext/amqp", "path": "php-src/ext/amqp",
"filename": "amqp.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"apcu": { "apcu": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/APCu", "pecl": "APCu",
"path": "php-src/ext/apcu", "path": "php-src/ext/apcu",
"filename": "apcu.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"ast": { "ast": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/ast",
"path": "php-src/ext/ast", "path": "php-src/ext/ast",
"filename": "ast.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -85,20 +80,16 @@
} }
}, },
"dio": { "dio": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/dio",
"path": "php-src/ext/dio", "path": "php-src/ext/dio",
"filename": "dio.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"ev": { "ev": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/ev",
"path": "php-src/ext/ev", "path": "php-src/ext/ev",
"filename": "ev.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -115,10 +106,8 @@
} }
}, },
"ext-ds": { "ext-ds": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/ds",
"path": "php-src/ext/ds", "path": "php-src/ext/ds",
"filename": "ds.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -134,10 +123,8 @@
} }
}, },
"ext-excimer": { "ext-excimer": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/excimer",
"path": "php-src/ext/excimer", "path": "php-src/ext/excimer",
"filename": "excimer.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -162,10 +149,8 @@
} }
}, },
"ext-grpc": { "ext-grpc": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/grpc",
"path": "php-src/ext/grpc", "path": "php-src/ext/grpc",
"filename": "grpc.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": [ "path": [
@@ -174,20 +159,16 @@
} }
}, },
"ext-imagick": { "ext-imagick": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/imagick",
"path": "php-src/ext/imagick", "path": "php-src/ext/imagick",
"filename": "imagick.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"ext-imap": { "ext-imap": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/imap",
"path": "php-src/ext/imap", "path": "php-src/ext/imap",
"filename": "imap.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": [ "path": [
@@ -207,19 +188,15 @@
} }
}, },
"ext-maxminddb": { "ext-maxminddb": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/maxminddb",
"filename": "ext-maxminddb.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"ext-memcache": { "ext-memcache": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/memcache",
"path": "php-src/ext/memcache", "path": "php-src/ext/memcache",
"filename": "memcache.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -235,10 +212,8 @@
} }
}, },
"ext-simdjson": { "ext-simdjson": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/simdjson",
"path": "php-src/ext/simdjson", "path": "php-src/ext/simdjson",
"filename": "simdjson.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -255,40 +230,32 @@
} }
}, },
"ext-ssh2": { "ext-ssh2": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/ssh2",
"path": "php-src/ext/ssh2", "path": "php-src/ext/ssh2",
"filename": "ssh2.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"ext-trader": { "ext-trader": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/trader",
"path": "php-src/ext/trader", "path": "php-src/ext/trader",
"filename": "trader.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"ext-uuid": { "ext-uuid": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/uuid",
"path": "php-src/ext/uuid", "path": "php-src/ext/uuid",
"filename": "uuid.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"ext-uv": { "ext-uv": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/uv",
"path": "php-src/ext/uv", "path": "php-src/ext/uv",
"filename": "uv.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -305,9 +272,7 @@
} }
}, },
"ext-zip": { "ext-zip": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/zip",
"filename": "ext-zip.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -412,10 +377,8 @@
} }
}, },
"igbinary": { "igbinary": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/igbinary",
"path": "php-src/ext/igbinary", "path": "php-src/ext/igbinary",
"filename": "igbinary.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "COPYING" "path": "COPYING"
@@ -439,10 +402,8 @@
} }
}, },
"inotify": { "inotify": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/inotify",
"path": "php-src/ext/inotify", "path": "php-src/ext/inotify",
"filename": "inotify.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -844,10 +805,8 @@
} }
}, },
"memcached": { "memcached": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/memcached",
"path": "php-src/ext/memcached", "path": "php-src/ext/memcached",
"filename": "memcached.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -885,10 +844,8 @@
} }
}, },
"msgpack": { "msgpack": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/msgpack",
"path": "php-src/ext/msgpack", "path": "php-src/ext/msgpack",
"filename": "msgpack.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -988,39 +945,31 @@
} }
}, },
"opentelemetry": { "opentelemetry": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/opentelemetry",
"path": "php-src/ext/opentelemetry", "path": "php-src/ext/opentelemetry",
"filename": "opentelemetry.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"parallel": { "parallel": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/parallel",
"path": "php-src/ext/parallel", "path": "php-src/ext/parallel",
"filename": "parallel.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"pcov": { "pcov": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/pcov",
"filename": "pcov.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"pdo_sqlsrv": { "pdo_sqlsrv": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/pdo_sqlsrv",
"path": "php-src/ext/pdo_sqlsrv", "path": "php-src/ext/pdo_sqlsrv",
"filename": "pdo_sqlsrv.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -1053,10 +1002,8 @@
} }
}, },
"protobuf": { "protobuf": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/protobuf",
"path": "php-src/ext/protobuf", "path": "php-src/ext/protobuf",
"filename": "protobuf.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -1115,10 +1062,8 @@
} }
}, },
"redis": { "redis": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/redis",
"path": "php-src/ext/redis", "path": "php-src/ext/redis",
"filename": "redis.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": [ "path": [
@@ -1155,10 +1100,8 @@
} }
}, },
"sqlsrv": { "sqlsrv": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/sqlsrv",
"path": "php-src/ext/sqlsrv", "path": "php-src/ext/sqlsrv",
"filename": "sqlsrv.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -1221,20 +1164,16 @@
} }
}, },
"xhprof": { "xhprof": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/xhprof",
"path": "php-src/ext/xhprof-src", "path": "php-src/ext/xhprof-src",
"filename": "xhprof.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"xlswriter": { "xlswriter": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/xlswriter",
"path": "php-src/ext/xlswriter", "path": "php-src/ext/xlswriter",
"filename": "xlswriter.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"
@@ -1252,10 +1191,8 @@
} }
}, },
"yac": { "yac": {
"type": "url", "type": "pecl",
"url": "https://pecl.php.net/get/yac",
"path": "php-src/ext/yac", "path": "php-src/ext/yac",
"filename": "yac.tgz",
"license": { "license": {
"type": "file", "type": "file",
"path": "LICENSE" "path": "LICENSE"

View File

@@ -34,7 +34,7 @@ use Symfony\Component\Console\Application;
*/ */
final class ConsoleApplication extends Application final class ConsoleApplication extends Application
{ {
public const string VERSION = '2.8.3'; public const string VERSION = '2.8.4';
public function __construct() public function __construct()
{ {

View File

@@ -13,8 +13,8 @@ trait freetype
{ {
$cmake = UnixCMakeExecutor::create($this) $cmake = UnixCMakeExecutor::create($this)
->optionalLib('libpng', ...cmake_boolean_args('FT_DISABLE_PNG', true)) ->optionalLib('libpng', ...cmake_boolean_args('FT_DISABLE_PNG', true))
->optionalLib('bzip2', ...cmake_boolean_args('FT_DISABLE_BZIP2', true)) ->addConfigureArgs('-DFT_DISABLE_BZIP2=ON')
->optionalLib('brotli', ...cmake_boolean_args('FT_DISABLE_BROTLI', true)) ->addConfigureArgs('-DFT_DISABLE_BROTLI=ON')
->addConfigureArgs('-DFT_DISABLE_HARFBUZZ=ON'); ->addConfigureArgs('-DFT_DISABLE_HARFBUZZ=ON');
// fix cmake 4.0 compatibility // fix cmake 4.0 compatibility

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace SPC\builder\windows\library;
use SPC\store\FileSystem;
class brotli extends WindowsLibraryBase
{
public const NAME = 'brotli';
protected function build(): void
{
// reset cmake
FileSystem::resetDir($this->source_dir . '\build');
// start build
cmd()->cd($this->source_dir)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
'-B build ' .
'-A x64 ' .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DBROTLI_BUILD_TOOLS=OFF ' .
'-DBROTLI_BUNDLED_MODE=OFF ' .
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
"--build build --config Release --target install -j{$this->builder->concurrency}"
);
}
}

View File

@@ -24,6 +24,8 @@ class freetype extends WindowsLibraryBase
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " . "-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'-DCMAKE_BUILD_TYPE=Release ' . '-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_SHARED_LIBS=OFF ' . '-DBUILD_SHARED_LIBS=OFF ' .
'-DFT_DISABLE_BROTLI=TRUE ' .
'-DFT_DISABLE_BZIP2=TRUE ' .
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' ' '-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
) )
->execWithWrapper( ->execWithWrapper(

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace SPC\builder\windows\library;
use SPC\store\FileSystem;
class libaom extends WindowsLibraryBase
{
public const NAME = 'libaom';
protected function build(): void
{
// libaom source tree contains a build/cmake/ directory with its own
// cmake modules, so we must use a different name for the build dir.
FileSystem::resetDir($this->source_dir . '\builddir');
// start build
cmd()->cd($this->source_dir)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
'-S . -B builddir ' .
'-A x64 ' .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'-DCMAKE_BUILD_TYPE=Release ' .
'-DBUILD_SHARED_LIBS=OFF ' .
'-DAOM_TARGET_CPU=generic ' .
'-DENABLE_DOCS=OFF ' .
'-DENABLE_EXAMPLES=OFF ' .
'-DENABLE_TESTDATA=OFF ' .
'-DENABLE_TESTS=OFF ' .
'-DENABLE_TOOLS=OFF ' .
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' '
)
->execWithWrapper(
$this->builder->makeSimpleWrapper('cmake'),
"--build builddir --config Release --target install -j{$this->builder->concurrency}"
);
}
}

View File

@@ -35,6 +35,7 @@ class zlib extends WindowsLibraryBase
'zlibstatic.lib', 'zlibstatic.lib',
'zs.lib', 'zs.lib',
'libzs.lib', 'libzs.lib',
'libz.lib',
]; ];
foreach ($detect_list as $item) { foreach ($detect_list as $item) {
if (file_exists(BUILD_LIB_PATH . '\\' . $item)) { if (file_exists(BUILD_LIB_PATH . '\\' . $item)) {

View File

@@ -16,6 +16,43 @@ use SPC\util\SPCTarget;
*/ */
class Downloader class Downloader
{ {
/**
* Get latest stable version from PECL
*
* @param string $name Source name
* @param array $source Source meta info: [pecl?]
* @return array<int, string> [url, filename]
*/
public static function getPECLInfo(string $name, array $source): array
{
$package = $source['pecl'] ?? (str_starts_with($name, 'ext-') ? substr($name, 4) : $name);
$lp = strtolower($package);
$api_url = "https://pecl.php.net/rest/r/{$lp}/allreleases.xml";
logger()->debug("Fetching {$name} source from PECL: {$api_url}");
$xml = self::curlExec(
url: $api_url,
retries: self::getRetryAttempts()
);
$dom = new \SimpleXMLElement($xml);
$version = null;
if ($source['prefer-stable'] ?? false) {
foreach ($dom->r as $release) {
if ((string) $release->s === 'stable') {
$version = (string) $release->v;
break;
}
}
}
$version ??= isset($dom->r[0]) ? (string) $dom->r[0]->v : null;
if ($version === null) {
throw new DownloaderException("failed to find any release for {$name} on PECL");
}
$url = "https://pecl.php.net/get/{$package}-{$version}.tgz";
$filename = "{$package}-{$version}.tgz";
logger()->info("Found {$name} PECL version: {$version}");
return [$url, $filename];
}
/** /**
* Get latest version from PIE config (Packagist) * Get latest version from PIE config (Packagist)
* *
@@ -612,6 +649,7 @@ class Downloader
* @param array{ * @param array{
* url?: string, * url?: string,
* repo?: string, * repo?: string,
* pecl?: string,
* rev?: string, * rev?: string,
* path?: string, * path?: string,
* filename?: string, * filename?: string,
@@ -631,6 +669,10 @@ class Downloader
{ {
try { try {
switch ($type) { switch ($type) {
case 'pecl': // PECL (latest stable)
[$url, $filename] = self::getPECLInfo($name, $conf);
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as);
break;
case 'pie': // Packagist case 'pie': // Packagist
[$url, $filename] = self::getPIEInfo($name, $conf); [$url, $filename] = self::getPIEInfo($name, $conf);
self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as, hooks: [[CurlHook::class, 'setupGithubToken']]); self::downloadFile($name, $url, $filename, $conf['path'] ?? $conf['extract'] ?? null, $download_as, hooks: [[CurlHook::class, 'setupGithubToken']]);

View File

@@ -22,7 +22,9 @@ class ConfigValidator
'regex' => 'string', // regex pattern 'regex' => 'string', // regex pattern
'rev' => 'string', // revision/branch 'rev' => 'string', // revision/branch
'repo' => 'string', // repository name 'repo' => 'string', // repository name
'pecl' => 'string', // PECL package name
'match' => 'string', // match pattern (aaa*bbb) 'match' => 'string', // match pattern (aaa*bbb)
'query' => 'string', // query string for API requests
'filename' => 'string', // filename 'filename' => 'string', // filename
'path' => 'string', // copy path 'path' => 'string', // copy path
'extract' => 'string', // copy path (alias of path) 'extract' => 'string', // copy path (alias of path)
@@ -73,12 +75,13 @@ class ConfigValidator
private const array SOURCE_TYPE_FIELDS = [ private const array SOURCE_TYPE_FIELDS = [
'filelist' => [['url', 'regex'], []], 'filelist' => [['url', 'regex'], []],
'git' => [['url', 'rev'], ['path', 'extract', 'submodules']], 'git' => [['url', 'rev'], ['path', 'extract', 'submodules']],
'ghtagtar' => [['repo'], ['path', 'extract', 'prefer-stable', 'match']], 'ghtagtar' => [['repo'], ['path', 'extract', 'prefer-stable', 'match', 'query']],
'ghtar' => [['repo'], ['path', 'extract', 'prefer-stable', 'match']], 'ghtar' => [['repo'], ['path', 'extract', 'prefer-stable', 'match', 'query']],
'ghrel' => [['repo', 'match'], ['path', 'extract', 'prefer-stable']], 'ghrel' => [['repo', 'match'], ['path', 'extract', 'prefer-stable']],
'url' => [['url'], ['filename', 'path', 'extract']], 'url' => [['url'], ['filename', 'path', 'extract']],
'bitbuckettag' => [['repo'], ['path', 'extract']], 'bitbuckettag' => [['repo'], ['path', 'extract']],
'local' => [['dirname'], ['path', 'extract']], 'local' => [['dirname'], ['path', 'extract']],
'pecl' => [[], ['pecl', 'path', 'prefer-stable']],
'pie' => [['repo'], ['path']], 'pie' => [['repo'], ['path']],
'custom' => [[], ['func']], 'custom' => [[], ['func']],
]; ];