nowise/uup-build-system

类似于make的复杂依赖树构建系统

1.6.3 2022-02-21 18:14 UTC

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 makefileJSON文件格式。

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.makebuild.jsonmakefilemakefile.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目标。在上面的例子中,它将导致评估cleandist-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