nowise / uup-build-system
类似于make的复杂依赖树构建系统
Requires
- php: >=7.4
- ext-json: *
- nowise/uup-application: ^1.0
- nowise/uup-application-options: ^1.0
Requires (Dev)
- phpunit/phpunit: ^9.5
README
一个类似于make的复杂依赖树构建系统。声明目标(目标和依赖关系)并评估依赖树以按正确顺序重建目标。
目标可以是程序性地在代码中定义,也可以在一个或多个make文件中声明。用户的剩余工作是实现目标接口的一些具体操作。使用TargetBase类简化这项任务。
开始使用
建议使用文件,尽管本README尝试尽可能简单和完整地描述两种模式。
生成文件
- 使用
generate
选项生成一个或多个make文件。 - 更改默认命名空间并添加目标类。
./vendor/bin/pbsmake generate > build.make
然后可以使用make命令(pbsmake)来评估目标。如果您更喜欢使用JSON风格的文件,请使用type=json。
./vendor/bin/pbsmake generate=implicit type=json > build.json
./vendor/bin/pbsmake build.json
程序性地
- 首先创建一个依赖树。
- 添加一个或多个目标定义。目标定义由要运行的代码和依赖关系列表组成。
- 获取完整树或子节点的节点评估器。
- 调用rebuild()来构建该节点、其依赖项和子节点。
提示:依赖树可以从(make/json)文件读取器中获取。
目标
目标具有唯一的名称和描述作为属性。该类提供用于检查目标是否是最新的isUpdated()
和用于构建目标的rebuild()
。名称用于其他目标以表达它们对此目标的依赖关系。
目标
目标由其目标和零个或多个依赖关系定义。依赖关系是与其他目标(目标)匹配的字符串。目标是用作构建依赖树的。
声明
可以程序性地在代码中声明所有内容应该如何构建,也可以通过文件静态声明,或者它们的混合。
节点和树
可以通过手动添加子节点(依赖项)然后使用根节点的节点评估器来手动构建依赖树。更方便的是使用依赖树,将节点添加到其中,作为依赖节点或使用目标定义。
文件
依赖关系可以在文本文件中声明,这些文件由文件读取器消费。同一读取器可用于从多个输入文件中读取规则。
$reader = new MakeFileReader(); $reader->addDependencies("makefile1.txt"); $reader->addDependencies("makefile2.txt"); $reader->getDependencyTree() ->getEvaluator() ->rebuild();
目前,支持GNU makefile或JSON文件格式。
makefile
以下是一个使用test目标类声明makefile的示例
VERBOSE := true DEBUG := true NAMESPACE := UUP\BuildSystem\Tests T1 : Target("T1") T2 : T1 Target("T2") T3 : T1 Target("T3") T4 : T2 Target("T4") T5 : T2 T3 Target("T5") T6 : T4 Target("T6") T7 : T5 Target("T7") T8 : T5 Target("T8", 123, true)
遵循约定,左侧是规则目标,右侧是依赖关系列表。Target类实现了执行该规则目标的PHP代码。T5目标依赖于T2和T3,而T6和T7都依赖于T5。
目标类将使用可变数量的参数构造。因此,可以在多个规则中使用相同的目标类并定义不同的行为。
将目标名称和依赖关系列表传递给目标类实例。
T1 : Target() # Constructs new Target(), with name T1 derived from target name. T8 : T5 Target(123, true) # Constructs new Target(123, true) named T8.
实际上,目标类将被不同的类替换。这只是一个用于测试的示例makefile。
选项
可选参数不仅限于简单的标量值。使用标准的JSON编码来传递复杂对象结构作为可选参数,这些参数将被解码为标准关联数组。
T8 : T5 Target(123, true, ["P1", "P2"]) T9 : T5 Target(123, true, {"name": "Anders", "city": "Uppsala", "hobbies": ["playing guitar", "watching sci-fi", "programming"]})
命名空间
默认命名空间在makefile中声明。如果类放置在多个命名空间中,可以声明它们为完全限定的,或者将声明拆分到多个文件中,每个文件都有自己的默认命名空间。
隐式
目标类可以从make规则(在Makefile或JSON文件中)推断出来。在这种情况下,左右手目标应该是一个类。上面的示例Makefile变为
VERBOSE := true DEBUG := true NAMESPACE := UUP\BuildSystem\Tests\Implicit T1 : T2 : T1 T3 : T1 T4 : T2 T5 : T2 T3 T6 : T4 T7 : T5 T8 : T5
每个T*类都存在于这个测试命名空间中,可以用它来测试
./vendor/bin/pbsmake example/file/implicit.make target=T3
可以混合使用规则和隐式/显式目标类。类不必在makefile中声明的命名空间中定义,如果存在于其他命名空间中,则使用完全限定的类名。
探查
在不带makefile列表的情况下调用pbsmake命令将导致脚本在当前目录中探查一些标准命名的文件:build.make
、build.json
、makefile
、makefile.txt
和*.pbs
。默认类型被认为是makefile。
传递递归选项(-r)以启用从当前目录开始的makefile的递归扫描。
详细模式 & 调试
从makefile中获取DEBUG和VERBOSE值,并填充到superglobal环境($_ENV
)中。
Array ( [PBS_MAKE_VERBOSE] => 1 [PBS_MAKE_DEBUG] => 1 )
为了方便,true/false、yes/no、on/off和0/1被视为布尔值。
伪目标
伪目标作为零个或多个真实目标的虚拟依赖项起作用。用简单的例子来说明这一点。
假设我们定义了这个伪目标的列表
PHONY := all clean dist-clean
然后我们可以设置T1和T2依赖伪目标all
T1 : all T2 : all T3 : T1 T4 : T2 T5 : T2 T3
从内置根节点开始评估依赖树将列出
./vendor/bin/pbsmake example/file/implicit.make -d target=root ... Evaluate node tree structure (graph): { "all": { "T1": { "T3": { "T5": [] } }, "T2": { "T4": [], "T5": [] } }, "clean": [], "dist-clean": [] } ...
如预期的那样,T1和T2都是伪目标all
的直接子节点。评估目标all将重建它们以及它们的依赖项。
./vendor/bin/pbsmake example/file/implicit.make -v target=all
...
Add dependencies from example/file/implicit.make
Making all (Phony target for all)
...
注意
如果没有指定target
选项,则将评估内置的root
目标。在上面的例子中,它将导致评估clean
和dist-clean
。
特殊目标
一个常见的任务是执行shell命令。为此存在一个内置目标
clean:
Shell("find -name *.tmp -type f | xargs rm -f")
将命令包裹在@(...)
或@@(...)
中以抑制输出。
clean:
Shell("@(find -name *.tmp -type f | xargs rm -f)")
另一个任务是运行任意的PHP代码。提供的代码将使用eval()
执行,确保不要使用外部输入来使用此功能。
finished:
Call("printf('Finished build at %s!\n', strftime('%c'));")
请参阅单元测试以获取更多示例。
评估
通常通过评估其根节点来完全重建树
$tree->getEvaluator()->rebuild();
也可以从注册表中获取树中的一个节点并对其进行评估
$tree->getRegistry()->getNode("T5") ->getEvaluator() ->rebuild();
示例
示例目录包含一些用于构建示例构建树的脚本。评估T5节点应按顺序重建T1、T2、T3、T5、T7和T8(除非依赖节点已经是最新的)。
从命令行运行它们
php example/definition-tree.php
排除子目标
默认情况下,构建一个目标节点及其所有依赖项和子节点。对于更标准的make模式,可以禁用构建子节点
$tree->getEvaluator() ->setRebuildChildren(false) ->rebuild();
然后输出将
++ Rebuild node T5:
Called isUpdated() on T1 (updated=0)
Called rebuild() on T1 (updated=0)
Called isUpdated() on T2 (updated=0)
Called rebuild() on T2 (updated=0)
Called isUpdated() on T3 (updated=0)
Called rebuild() on T3 (updated=0)
Called isUpdated() on T5 (updated=0)
Called rebuild() on T5 (updated=0)
++ Rebuild complete tree:
make命令
make命令pbsmake
可以用来执行makefile,使开始变得容易。像标准的make一样,可以传递一个可选的目标
./vendor/bin/pbsmake example/file/input.make target=T8 Called isUpdated() on T1 (updated=0) Called rebuild() on T1 (updated=0) Called isUpdated() on T2 (updated=0) Called rebuild() on T2 (updated=0) Called isUpdated() on T3 (updated=0) Called rebuild() on T3 (updated=0) Called isUpdated() on T5 (updated=0) Called rebuild() on T5 (updated=0) Called isUpdated() on T7 (updated=0) Called rebuild() on T7 (updated=0)
可以处理多个makefile。目前,一个限制是所有makefile必须具有相同类型。
./vendor/bin/pbsmake -h PHP make (build system command) Usage: pbsmake makefile1 [...makefiles] [target=name] [type=json] Options: target=name: Make this target. type=str: The type of makefile (make/json). compat[=bool]: Enable make compatible mode. generate[=mode]: Output template makefile (explicit/implicit). recursive: Recursive scan for makefiles (-r). Generic options: help: Show this casual help. version: Show version information. verbose: Run in verbose mode. quiet: Run in quiet mode. debug: Enable debug output. Copyright (C) 2021-2022 Nowise Systems