zero-build 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zero_build-0.1.0/PKG-INFO +64 -0
- zero_build-0.1.0/README.md +54 -0
- zero_build-0.1.0/pyproject.toml +25 -0
- zero_build-0.1.0/setup.cfg +4 -0
- zero_build-0.1.0/src/zero/__init__.py +20 -0
- zero_build-0.1.0/src/zero/analyzers/__init__.py +0 -0
- zero_build-0.1.0/src/zero/analyzers/cycle_detector.py +146 -0
- zero_build-0.1.0/src/zero/analyzers/stale_detector.py +167 -0
- zero_build-0.1.0/src/zero/builder/__init__.py +0 -0
- zero_build-0.1.0/src/zero/builder/builder.py +175 -0
- zero_build-0.1.0/src/zero/cli/__init__.py +0 -0
- zero_build-0.1.0/src/zero/cli/__main__.py +5 -0
- zero_build-0.1.0/src/zero/cli/args.py +60 -0
- zero_build-0.1.0/src/zero/cli/main.py +20 -0
- zero_build-0.1.0/src/zero/compilers/__init__.py +6 -0
- zero_build-0.1.0/src/zero/compilers/base.py +36 -0
- zero_build-0.1.0/src/zero/compilers/clang.py +7 -0
- zero_build-0.1.0/src/zero/compilers/clangxx.py +7 -0
- zero_build-0.1.0/src/zero/compilers/gcc.py +110 -0
- zero_build-0.1.0/src/zero/compilers/get.py +25 -0
- zero_build-0.1.0/src/zero/compilers/gxx.py +7 -0
- zero_build-0.1.0/src/zero/compilers/types.py +9 -0
- zero_build-0.1.0/src/zero/errors/__init__.py +4 -0
- zero_build-0.1.0/src/zero/errors/errors.py +19 -0
- zero_build-0.1.0/src/zero/graph/__init__.py +0 -0
- zero_build-0.1.0/src/zero/graph/constructor.py +244 -0
- zero_build-0.1.0/src/zero/graph/nodes.py +125 -0
- zero_build-0.1.0/src/zero/graph/printer.py +100 -0
- zero_build-0.1.0/src/zero/graph/visitor.py +51 -0
- zero_build-0.1.0/src/zero/interface/__init__.py +0 -0
- zero_build-0.1.0/src/zero/interface/build.py +49 -0
- zero_build-0.1.0/src/zero/interface/executable.py +14 -0
- zero_build-0.1.0/src/zero/interface/headers.py +57 -0
- zero_build-0.1.0/src/zero/interface/library.py +11 -0
- zero_build-0.1.0/src/zero/interface/precomp_lib.py +21 -0
- zero_build-0.1.0/src/zero/interface/shared_lib.py +12 -0
- zero_build-0.1.0/src/zero/interface/source.py +27 -0
- zero_build-0.1.0/src/zero/interface/static_lib.py +12 -0
- zero_build-0.1.0/src/zero/interface/target.py +90 -0
- zero_build-0.1.0/src/zero/interface/types.py +4 -0
- zero_build-0.1.0/src/zero/orchestrator/__init__.py +1 -0
- zero_build-0.1.0/src/zero/orchestrator/config.py +26 -0
- zero_build-0.1.0/src/zero/orchestrator/executor.py +14 -0
- zero_build-0.1.0/src/zero/orchestrator/orchestrator.py +204 -0
- zero_build-0.1.0/src/zero/py.typed +0 -0
- zero_build-0.1.0/src/zero/reporter/__init__.py +2 -0
- zero_build-0.1.0/src/zero/reporter/get.py +9 -0
- zero_build-0.1.0/src/zero/reporter/reporter.py +43 -0
- zero_build-0.1.0/src/zero/utils/__init__.py +1 -0
- zero_build-0.1.0/src/zero/utils/module_loader.py +36 -0
- zero_build-0.1.0/src/zero_build.egg-info/PKG-INFO +64 -0
- zero_build-0.1.0/src/zero_build.egg-info/SOURCES.txt +54 -0
- zero_build-0.1.0/src/zero_build.egg-info/dependency_links.txt +1 -0
- zero_build-0.1.0/src/zero_build.egg-info/entry_points.txt +2 -0
- zero_build-0.1.0/src/zero_build.egg-info/requires.txt +1 -0
- zero_build-0.1.0/src/zero_build.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zero-build
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A clean, pythonic, DAG-based C/C++ build system.
|
|
5
|
+
Author-email: Haseeb <haseeeb8j@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/kxmtkw/Zero
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: rich>=13.0.0
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Zero - Super Easy Build System
|
|
13
|
+
|
|
14
|
+
`Zero` is a C/C++ build system designed to be as `easy` as possible to use, without lacking any features. Written in python!
|
|
15
|
+
|
|
16
|
+
The only reason `Zero` exists is because I hate CMake syntax.
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
First, create a `zero.py` file in your project root and import `zero`
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from zero import *
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Create a `Build` object named `build`.
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
build = Build()
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Configure the `build` object by specifying the compiler and the build directory.
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
build.compiler = "gcc"
|
|
36
|
+
build.directory = "build" # or Path("build") also works!
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
To create an executable, create an `Executable` object! Specify the sources an arguments!
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
main = Executable()
|
|
43
|
+
main.source = Source(
|
|
44
|
+
"src/main.c",
|
|
45
|
+
"src/utils.c" # or Path("src") / "utils.c" also works!
|
|
46
|
+
)
|
|
47
|
+
main.arguments = "-Wall", "-O3"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
And you are done! Yes that's all.
|
|
51
|
+
Run the `zero` cli tool to make your executable.
|
|
52
|
+
```bash
|
|
53
|
+
zero make
|
|
54
|
+
```
|
|
55
|
+
A binary named `main` can now be found at `build/bin/main`
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
`Zero` is still in its infant stage. Proper installation does not exist. To still install, run:
|
|
60
|
+
```bash
|
|
61
|
+
git clone https://github.com/kxmtkw/Zero.git zero
|
|
62
|
+
cd zero
|
|
63
|
+
pip install --break-system-packages -e .
|
|
64
|
+
```
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
|
|
2
|
+
# Zero - Super Easy Build System
|
|
3
|
+
|
|
4
|
+
`Zero` is a C/C++ build system designed to be as `easy` as possible to use, without lacking any features. Written in python!
|
|
5
|
+
|
|
6
|
+
The only reason `Zero` exists is because I hate CMake syntax.
|
|
7
|
+
|
|
8
|
+
## Usage
|
|
9
|
+
|
|
10
|
+
First, create a `zero.py` file in your project root and import `zero`
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
from zero import *
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Create a `Build` object named `build`.
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
build = Build()
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Configure the `build` object by specifying the compiler and the build directory.
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
build.compiler = "gcc"
|
|
26
|
+
build.directory = "build" # or Path("build") also works!
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
To create an executable, create an `Executable` object! Specify the sources an arguments!
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
main = Executable()
|
|
33
|
+
main.source = Source(
|
|
34
|
+
"src/main.c",
|
|
35
|
+
"src/utils.c" # or Path("src") / "utils.c" also works!
|
|
36
|
+
)
|
|
37
|
+
main.arguments = "-Wall", "-O3"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
And you are done! Yes that's all.
|
|
41
|
+
Run the `zero` cli tool to make your executable.
|
|
42
|
+
```bash
|
|
43
|
+
zero make
|
|
44
|
+
```
|
|
45
|
+
A binary named `main` can now be found at `build/bin/main`
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
`Zero` is still in its infant stage. Proper installation does not exist. To still install, run:
|
|
50
|
+
```bash
|
|
51
|
+
git clone https://github.com/kxmtkw/Zero.git zero
|
|
52
|
+
cd zero
|
|
53
|
+
pip install --break-system-packages -e .
|
|
54
|
+
```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "zero-build"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A clean, pythonic, DAG-based C/C++ build system."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Haseeb", email = "haseeeb8j@gmail.com" }
|
|
9
|
+
]
|
|
10
|
+
dependencies = [
|
|
11
|
+
"rich>=13.0.0",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[project.scripts]
|
|
15
|
+
zero = "zero.cli.main:main"
|
|
16
|
+
|
|
17
|
+
[project.urls]
|
|
18
|
+
Homepage = "https://github.com/kxmtkw/Zero"
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["setuptools >= 77.0.3"]
|
|
22
|
+
build-backend = "setuptools.build_meta"
|
|
23
|
+
|
|
24
|
+
[tool.setuptools.packages.find]
|
|
25
|
+
where = ["src"]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Main API classes
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from src.zero.interface.build import Build
|
|
5
|
+
from src.zero.interface.source import Source
|
|
6
|
+
from src.zero.interface.executable import Executable
|
|
7
|
+
from src.zero.interface.static_lib import StaticLibrary
|
|
8
|
+
from src.zero.interface.shared_lib import SharedLibrary
|
|
9
|
+
from src.zero.interface.precomp_lib import PreCompiledLibrary
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"Path",
|
|
13
|
+
|
|
14
|
+
"Build",
|
|
15
|
+
"Source",
|
|
16
|
+
"Executable",
|
|
17
|
+
"StaticLibrary",
|
|
18
|
+
"SharedLibrary",
|
|
19
|
+
"PreCompiledLibrary"
|
|
20
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
from src.zero.graph.nodes import Node
|
|
2
|
+
from src.zero.graph.visitor import NodeVisitor
|
|
3
|
+
from src.zero.graph.nodes import *
|
|
4
|
+
|
|
5
|
+
from src.zero.reporter import getReporter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CycleDetector(NodeVisitor):
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def __init__(self) -> None:
|
|
12
|
+
self.visited_nodes: set[Node] = set()
|
|
13
|
+
self.tracked_nodes: list[Node] = []
|
|
14
|
+
self.is_cycle_detected: bool = False
|
|
15
|
+
|
|
16
|
+
self.reporter = getReporter()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def getCycle(self, node: Node) -> list[Node]:
|
|
20
|
+
|
|
21
|
+
self.is_cycle_detected = True
|
|
22
|
+
|
|
23
|
+
cycle = []
|
|
24
|
+
|
|
25
|
+
for i in range(len(self.tracked_nodes)):
|
|
26
|
+
if self.tracked_nodes[i] == node:
|
|
27
|
+
cycle = self.tracked_nodes[i:]
|
|
28
|
+
cycle.append(node)
|
|
29
|
+
break
|
|
30
|
+
|
|
31
|
+
return cycle
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def visit(self, node: Node):
|
|
35
|
+
|
|
36
|
+
if self.is_cycle_detected:
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
return super().visit(node)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def visitRootNode(self, node: RootNode):
|
|
43
|
+
|
|
44
|
+
if node not in self.visited_nodes:
|
|
45
|
+
self.visited_nodes.add(node)
|
|
46
|
+
|
|
47
|
+
self.tracked_nodes.append(node)
|
|
48
|
+
|
|
49
|
+
for t in node.targets:
|
|
50
|
+
self.visit(t)
|
|
51
|
+
|
|
52
|
+
self.tracked_nodes.pop()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def visitExecutableNode(self, node: ExecutableNode):
|
|
56
|
+
if node not in self.visited_nodes:
|
|
57
|
+
self.visited_nodes.add(node)
|
|
58
|
+
|
|
59
|
+
if node in self.tracked_nodes:
|
|
60
|
+
self.getCycle(node)
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
self.tracked_nodes.append(node)
|
|
64
|
+
|
|
65
|
+
for lib in node.linked_libraries:
|
|
66
|
+
self.visit(lib)
|
|
67
|
+
|
|
68
|
+
for src in node.sources:
|
|
69
|
+
self.visit(src)
|
|
70
|
+
|
|
71
|
+
self.tracked_nodes.pop()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def visitStaticLibraryNode(self, node: StaticLibraryNode):
|
|
75
|
+
if node not in self.visited_nodes:
|
|
76
|
+
self.visited_nodes.add(node)
|
|
77
|
+
|
|
78
|
+
if node in self.tracked_nodes:
|
|
79
|
+
self.getCycle(node)
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
self.tracked_nodes.append(node)
|
|
83
|
+
|
|
84
|
+
for lib in node.linked_libraries:
|
|
85
|
+
self.visit(lib)
|
|
86
|
+
|
|
87
|
+
for src in node.sources:
|
|
88
|
+
self.visit(src)
|
|
89
|
+
|
|
90
|
+
self.tracked_nodes.pop()
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def visitSharedLibraryNode(self, node: SharedLibraryNode):
|
|
94
|
+
if node not in self.visited_nodes:
|
|
95
|
+
self.visited_nodes.add(node)
|
|
96
|
+
|
|
97
|
+
if node in self.tracked_nodes:
|
|
98
|
+
self.getCycle(node)
|
|
99
|
+
return
|
|
100
|
+
|
|
101
|
+
self.tracked_nodes.append(node)
|
|
102
|
+
|
|
103
|
+
for lib in node.linked_libraries:
|
|
104
|
+
self.visit(lib)
|
|
105
|
+
|
|
106
|
+
for src in node.sources:
|
|
107
|
+
self.visit(src)
|
|
108
|
+
|
|
109
|
+
self.tracked_nodes.pop()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def visitPreCompiledLibraryNode(self, node: PreCompiledLibraryNode):
|
|
113
|
+
if node not in self.visited_nodes:
|
|
114
|
+
self.visited_nodes.add(node)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def visitSourceNode(self, node: SourceNode):
|
|
118
|
+
if node not in self.visited_nodes:
|
|
119
|
+
self.visited_nodes.add(node)
|
|
120
|
+
|
|
121
|
+
if node in self.tracked_nodes:
|
|
122
|
+
self.getCycle(node)
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
self.tracked_nodes.append(node)
|
|
126
|
+
|
|
127
|
+
for header in node.deps:
|
|
128
|
+
self.visit(header)
|
|
129
|
+
|
|
130
|
+
self.tracked_nodes.pop()
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def visitHeaderNode(self, node: HeaderNode):
|
|
134
|
+
if node not in self.visited_nodes:
|
|
135
|
+
self.visited_nodes.add(node)
|
|
136
|
+
|
|
137
|
+
if node in self.tracked_nodes:
|
|
138
|
+
self.getCycle(node)
|
|
139
|
+
return
|
|
140
|
+
|
|
141
|
+
self.tracked_nodes.append(node)
|
|
142
|
+
|
|
143
|
+
for header in node.deps:
|
|
144
|
+
self.visit(header)
|
|
145
|
+
|
|
146
|
+
self.tracked_nodes.pop()
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
from src.zero.graph.nodes import Node
|
|
2
|
+
from src.zero.graph.visitor import NodeVisitor
|
|
3
|
+
from src.zero.graph.nodes import *
|
|
4
|
+
|
|
5
|
+
from src.zero.reporter import getReporter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def markStale(node: Node):
|
|
9
|
+
setattr(node, "_stale", True)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def isStale(node: Node) -> bool:
|
|
13
|
+
return hasattr(node, "_stale")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class StaleDetector(NodeVisitor):
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def __init__(self) -> None:
|
|
20
|
+
self.visited_nodes: set[Node] = set()
|
|
21
|
+
self.current_source_outpath: Path | None = None
|
|
22
|
+
self.stale_count = 0
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def markStale(self, node: Node):
|
|
26
|
+
if not hasattr(node, "_stale"):
|
|
27
|
+
setattr(node, "_stale", True)
|
|
28
|
+
self.stale_count += 1
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def getStaleCount(self) -> int:
|
|
32
|
+
return self.stale_count
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def visitRootNode(self, node: RootNode):
|
|
36
|
+
|
|
37
|
+
if node not in self.visited_nodes:
|
|
38
|
+
self.visited_nodes.add(node)
|
|
39
|
+
else:
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
for t in node.targets:
|
|
44
|
+
self.visit(t)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def visitExecutableNode(self, node: ExecutableNode):
|
|
49
|
+
|
|
50
|
+
if node not in self.visited_nodes:
|
|
51
|
+
self.visited_nodes.add(node)
|
|
52
|
+
else:
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
for lib in node.linked_libraries:
|
|
57
|
+
self.visit(lib)
|
|
58
|
+
|
|
59
|
+
if isStale(lib):
|
|
60
|
+
self.markStale(node)
|
|
61
|
+
|
|
62
|
+
for src in node.sources:
|
|
63
|
+
self.visit(src)
|
|
64
|
+
|
|
65
|
+
if isStale(src):
|
|
66
|
+
self.markStale(node)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def visitStaticLibraryNode(self, node: StaticLibraryNode):
|
|
70
|
+
|
|
71
|
+
if node not in self.visited_nodes:
|
|
72
|
+
self.visited_nodes.add(node)
|
|
73
|
+
else:
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
for lib in node.linked_libraries:
|
|
77
|
+
self.visit(lib)
|
|
78
|
+
|
|
79
|
+
if isStale(lib):
|
|
80
|
+
self.markStale(node)
|
|
81
|
+
|
|
82
|
+
for src in node.sources:
|
|
83
|
+
self.visit(src)
|
|
84
|
+
|
|
85
|
+
if isStale(src):
|
|
86
|
+
self.markStale(node)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def visitSharedLibraryNode(self, node: SharedLibraryNode):
|
|
90
|
+
|
|
91
|
+
if node not in self.visited_nodes:
|
|
92
|
+
self.visited_nodes.add(node)
|
|
93
|
+
else:
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
for lib in node.linked_libraries:
|
|
98
|
+
self.visit(lib)
|
|
99
|
+
|
|
100
|
+
if isStale(lib):
|
|
101
|
+
self.markStale(node)
|
|
102
|
+
|
|
103
|
+
for src in node.sources:
|
|
104
|
+
self.visit(src)
|
|
105
|
+
|
|
106
|
+
if isStale(src):
|
|
107
|
+
self.markStale(node)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def visitPreCompiledLibraryNode(self, node: PreCompiledLibraryNode):
|
|
111
|
+
|
|
112
|
+
if node not in self.visited_nodes:
|
|
113
|
+
self.visited_nodes.add(node)
|
|
114
|
+
else:
|
|
115
|
+
return
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def visitSourceNode(self, node: SourceNode):
|
|
120
|
+
|
|
121
|
+
if node not in self.visited_nodes:
|
|
122
|
+
self.visited_nodes.add(node)
|
|
123
|
+
else:
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
if not node.outpath.exists():
|
|
128
|
+
self.markStale(node)
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
if node.outpath.stat().st_mtime < node.filepath.stat().st_mtime:
|
|
132
|
+
self.markStale(node)
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
self.current_source_outpath = node.outpath
|
|
136
|
+
|
|
137
|
+
for header in node.deps:
|
|
138
|
+
self.visit(header)
|
|
139
|
+
|
|
140
|
+
if isStale(header):
|
|
141
|
+
self.markStale(node)
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
self.current_source_outpath = None
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def visitHeaderNode(self, node: HeaderNode):
|
|
148
|
+
|
|
149
|
+
if node not in self.visited_nodes:
|
|
150
|
+
self.visited_nodes.add(node)
|
|
151
|
+
else:
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
if self.current_source_outpath is None:
|
|
155
|
+
raise RuntimeError("Source path should not have been None. Unexpected.")
|
|
156
|
+
|
|
157
|
+
if self.current_source_outpath.stat().st_mtime < node.filepath.stat().st_mtime:
|
|
158
|
+
markStale(node)
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
for header in node.deps:
|
|
162
|
+
self.visit(header)
|
|
163
|
+
|
|
164
|
+
if isStale(header):
|
|
165
|
+
markStale(node)
|
|
166
|
+
return
|
|
167
|
+
|
|
File without changes
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
from src.zero.graph.nodes import *
|
|
2
|
+
from src.zero.graph.nodes import SharedLibraryNode
|
|
3
|
+
from src.zero.graph.visitor import NodeVisitor
|
|
4
|
+
|
|
5
|
+
from src.zero.compilers import BaseCompiler
|
|
6
|
+
|
|
7
|
+
from src.zero.orchestrator.config import BuildConfig
|
|
8
|
+
from src.zero.reporter import getReporter
|
|
9
|
+
from src.zero.analyzers.stale_detector import isStale
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Builder(NodeVisitor):
|
|
13
|
+
|
|
14
|
+
def __init__(self, config: BuildConfig) -> None:
|
|
15
|
+
|
|
16
|
+
super().__init__()
|
|
17
|
+
|
|
18
|
+
self.fresh_build = config.fresh_build
|
|
19
|
+
|
|
20
|
+
self.compiling_shared_lib = False
|
|
21
|
+
|
|
22
|
+
self.include_dirs: list[Path] = []
|
|
23
|
+
self.visited_nodes: set[object] = set()
|
|
24
|
+
self.current_target_arguments: list[str] = []
|
|
25
|
+
|
|
26
|
+
self.reporter = getReporter()
|
|
27
|
+
|
|
28
|
+
self.current_compiler: BaseCompiler
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def detectStaleness(self, node: Node) -> bool:
|
|
32
|
+
if self.fresh_build:
|
|
33
|
+
return True
|
|
34
|
+
else:
|
|
35
|
+
return isStale(node)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def visitRootNode(self, node: RootNode):
|
|
39
|
+
self.reporter.startPhase("Compilation", "Compiling")
|
|
40
|
+
|
|
41
|
+
for target in node.targets:
|
|
42
|
+
self.current_compiler = node.target_compilers[target]
|
|
43
|
+
self.visit(target)
|
|
44
|
+
|
|
45
|
+
self.reporter.endPhase("Build complete")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def visitStaticLibraryNode(self, node: StaticLibraryNode):
|
|
49
|
+
if node in self.visited_nodes:
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
if not self.detectStaleness(node):
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
include_dirs = []
|
|
56
|
+
|
|
57
|
+
for lib in node.linked_libraries:
|
|
58
|
+
self.visit(lib)
|
|
59
|
+
include_dirs.extend(lib.public_headers)
|
|
60
|
+
|
|
61
|
+
include_dirs.extend(node.public_headers)
|
|
62
|
+
include_dirs.extend(node.private_headers)
|
|
63
|
+
|
|
64
|
+
self.include_dirs = include_dirs
|
|
65
|
+
|
|
66
|
+
self.current_target_arguments = node.arguments
|
|
67
|
+
|
|
68
|
+
for deps in node.sources:
|
|
69
|
+
self.visit(deps)
|
|
70
|
+
|
|
71
|
+
self.current_compiler.buildStaticLib([n.outpath for n in node.sources], node.libpath)
|
|
72
|
+
|
|
73
|
+
self.reporter.taskDone("Link ", f"{node.libpath.name}")
|
|
74
|
+
|
|
75
|
+
self.visited_nodes.add(node)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def visitSharedLibraryNode(self, node: SharedLibraryNode):
|
|
79
|
+
if node in self.visited_nodes:
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
if not self.detectStaleness(node):
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
self.compiling_shared_lib = True
|
|
86
|
+
|
|
87
|
+
include_dirs = []
|
|
88
|
+
|
|
89
|
+
for lib in node.linked_libraries:
|
|
90
|
+
self.visit(lib)
|
|
91
|
+
include_dirs.extend(lib.public_headers)
|
|
92
|
+
|
|
93
|
+
include_dirs.extend(node.public_headers)
|
|
94
|
+
include_dirs.extend(node.private_headers)
|
|
95
|
+
|
|
96
|
+
self.include_dirs = include_dirs
|
|
97
|
+
|
|
98
|
+
self.current_target_arguments = node.arguments
|
|
99
|
+
|
|
100
|
+
for deps in node.sources:
|
|
101
|
+
self.visit(deps)
|
|
102
|
+
|
|
103
|
+
self.current_compiler.buildSharedLib([n.outpath for n in node.sources], [l.libpath for l in node.linked_libraries], node.libpath)
|
|
104
|
+
|
|
105
|
+
self.compiling_shared_lib = False
|
|
106
|
+
|
|
107
|
+
self.reporter.taskDone("Link ", f"{node.libpath.name}")
|
|
108
|
+
|
|
109
|
+
self.visited_nodes.add(node)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def visitPreCompiledLibraryNode(self, node: PreCompiledLibraryNode):
|
|
113
|
+
|
|
114
|
+
if not node.libpath.exists():
|
|
115
|
+
raise RuntimeError(f"Could not find pre-compiled library: {node.libpath}")
|
|
116
|
+
|
|
117
|
+
self.reporter.taskDone("Found", f"{node.libpath}")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def visitExecutableNode(self, node: ExecutableNode):
|
|
121
|
+
if node in self.visited_nodes:
|
|
122
|
+
return
|
|
123
|
+
|
|
124
|
+
if not self.detectStaleness(node):
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
include_dirs = []
|
|
128
|
+
|
|
129
|
+
for lib in node.linked_libraries:
|
|
130
|
+
self.visit(lib)
|
|
131
|
+
include_dirs.extend(lib.public_headers)
|
|
132
|
+
|
|
133
|
+
include_dirs.extend(node.private_headers)
|
|
134
|
+
|
|
135
|
+
self.include_dirs = include_dirs
|
|
136
|
+
|
|
137
|
+
self.current_target_arguments = node.arguments
|
|
138
|
+
|
|
139
|
+
for deps in node.sources:
|
|
140
|
+
self.visit(deps)
|
|
141
|
+
|
|
142
|
+
self.current_compiler.buildExecutable([n.outpath for n in node.sources], [n.libpath for n in node.linked_libraries], node.targetpath)
|
|
143
|
+
|
|
144
|
+
self.reporter.taskDone("Link ", f"{node.targetpath.name}")
|
|
145
|
+
|
|
146
|
+
self.visited_nodes.add(node)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def visitSourceNode(self, node: SourceNode):
|
|
150
|
+
|
|
151
|
+
if node in self.visited_nodes:
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
if not self.detectStaleness(node):
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
for deps in node.deps:
|
|
158
|
+
self.visit(deps)
|
|
159
|
+
|
|
160
|
+
self.current_compiler.buildFile(
|
|
161
|
+
node.filepath,
|
|
162
|
+
node.outpath,
|
|
163
|
+
for_shared=self.compiling_shared_lib,
|
|
164
|
+
include_dirs=self.include_dirs,
|
|
165
|
+
arguments=self.current_target_arguments
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
self.reporter.taskDone("Built", f"{node.filepath}")
|
|
169
|
+
|
|
170
|
+
self.visited_nodes.add(node)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def visitHeaderNode(self, node: HeaderNode):
|
|
174
|
+
for deps in node.deps:
|
|
175
|
+
self.visit(deps)
|
|
File without changes
|