zero-build 0.1.0__py3-none-any.whl
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/__init__.py +20 -0
- zero/analyzers/__init__.py +0 -0
- zero/analyzers/cycle_detector.py +146 -0
- zero/analyzers/stale_detector.py +167 -0
- zero/builder/__init__.py +0 -0
- zero/builder/builder.py +175 -0
- zero/cli/__init__.py +0 -0
- zero/cli/__main__.py +5 -0
- zero/cli/args.py +60 -0
- zero/cli/main.py +20 -0
- zero/compilers/__init__.py +6 -0
- zero/compilers/base.py +36 -0
- zero/compilers/clang.py +7 -0
- zero/compilers/clangxx.py +7 -0
- zero/compilers/gcc.py +110 -0
- zero/compilers/get.py +25 -0
- zero/compilers/gxx.py +7 -0
- zero/compilers/types.py +9 -0
- zero/errors/__init__.py +4 -0
- zero/errors/errors.py +19 -0
- zero/graph/__init__.py +0 -0
- zero/graph/constructor.py +244 -0
- zero/graph/nodes.py +125 -0
- zero/graph/printer.py +100 -0
- zero/graph/visitor.py +51 -0
- zero/interface/__init__.py +0 -0
- zero/interface/build.py +49 -0
- zero/interface/executable.py +14 -0
- zero/interface/headers.py +57 -0
- zero/interface/library.py +11 -0
- zero/interface/precomp_lib.py +21 -0
- zero/interface/shared_lib.py +12 -0
- zero/interface/source.py +27 -0
- zero/interface/static_lib.py +12 -0
- zero/interface/target.py +90 -0
- zero/interface/types.py +4 -0
- zero/orchestrator/__init__.py +1 -0
- zero/orchestrator/config.py +26 -0
- zero/orchestrator/executor.py +14 -0
- zero/orchestrator/orchestrator.py +204 -0
- zero/py.typed +0 -0
- zero/reporter/__init__.py +2 -0
- zero/reporter/get.py +9 -0
- zero/reporter/reporter.py +43 -0
- zero/utils/__init__.py +1 -0
- zero/utils/module_loader.py +36 -0
- zero_build-0.1.0.dist-info/METADATA +64 -0
- zero_build-0.1.0.dist-info/RECORD +51 -0
- zero_build-0.1.0.dist-info/WHEEL +5 -0
- zero_build-0.1.0.dist-info/entry_points.txt +2 -0
- zero_build-0.1.0.dist-info/top_level.txt +1 -0
zero/__init__.py
ADDED
|
@@ -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
|
+
|
zero/builder/__init__.py
ADDED
|
File without changes
|
zero/builder/builder.py
ADDED
|
@@ -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)
|
zero/cli/__init__.py
ADDED
|
File without changes
|
zero/cli/__main__.py
ADDED
zero/cli/args.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def add_make_command(subparsers: argparse._SubParsersAction) -> None:
|
|
6
|
+
make_parser = subparsers.add_parser(
|
|
7
|
+
"make",
|
|
8
|
+
help="Build targets",
|
|
9
|
+
)
|
|
10
|
+
make_parser.add_argument(
|
|
11
|
+
"--fresh",
|
|
12
|
+
action="store_true",
|
|
13
|
+
help="Force a clean rebuild",
|
|
14
|
+
)
|
|
15
|
+
make_parser.add_argument(
|
|
16
|
+
"target",
|
|
17
|
+
nargs="*",
|
|
18
|
+
default=None,
|
|
19
|
+
help="Target to build",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def add_run_command(subparsers: argparse._SubParsersAction) -> None:
|
|
24
|
+
run_parser = subparsers.add_parser(
|
|
25
|
+
"run",
|
|
26
|
+
help="Run executables",
|
|
27
|
+
)
|
|
28
|
+
run_parser.add_argument(
|
|
29
|
+
"--fresh",
|
|
30
|
+
action="store_true",
|
|
31
|
+
help="Force a rebuild before running",
|
|
32
|
+
)
|
|
33
|
+
run_parser.add_argument(
|
|
34
|
+
"executable",
|
|
35
|
+
help="Path or name of the executable",
|
|
36
|
+
)
|
|
37
|
+
run_parser.add_argument(
|
|
38
|
+
"executable_args",
|
|
39
|
+
nargs=argparse.REMAINDER,
|
|
40
|
+
help="Arguments passed directly to the executable",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def setupParser() -> argparse.ArgumentParser:
|
|
45
|
+
parser = argparse.ArgumentParser(prog="zero", description="Zero build system")
|
|
46
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
47
|
+
|
|
48
|
+
add_make_command(subparsers)
|
|
49
|
+
add_run_command(subparsers)
|
|
50
|
+
|
|
51
|
+
return parser
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def parseArguments(
|
|
55
|
+
parser: argparse.ArgumentParser, args: list[str] | None = None
|
|
56
|
+
) -> argparse.Namespace:
|
|
57
|
+
if args is None:
|
|
58
|
+
args = sys.argv[1:]
|
|
59
|
+
|
|
60
|
+
return parser.parse_args(args)
|
zero/cli/main.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from src.zero.orchestrator import Orchestrator
|
|
2
|
+
from src.zero.cli.args import setupParser, parseArguments
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def main():
|
|
6
|
+
parser = setupParser()
|
|
7
|
+
args = parseArguments(parser)
|
|
8
|
+
|
|
9
|
+
orchestrator = Orchestrator()
|
|
10
|
+
|
|
11
|
+
if args.command == "make":
|
|
12
|
+
if len(args.target) == 0:
|
|
13
|
+
orchestrator.makeBuild(fresh=args.fresh)
|
|
14
|
+
else:
|
|
15
|
+
orchestrator.makeTargets(args.target, fresh=args.fresh)
|
|
16
|
+
|
|
17
|
+
elif args.command == "run":
|
|
18
|
+
orchestrator.runExecutable(args.executable, args.executable_args, fresh=args.fresh)
|
|
19
|
+
|
|
20
|
+
|
zero/compilers/base.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class BaseCompiler(ABC):
|
|
6
|
+
"""
|
|
7
|
+
Base class for Compilers utilizing pathlib.Path objects.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
def __init__(self) -> None:
|
|
11
|
+
super().__init__()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def getDependencies(self, filepath: Path, *, include_dirs: list[Path] = []) -> list[Path]:
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@abstractmethod
|
|
20
|
+
def buildFile(self, filepath: Path, outfile: Path, *, for_shared = False, include_dirs: list[Path] = [], arguments: list[str] = []) -> None:
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def buildStaticLib(self, objects: list[Path], outfile: Path) -> None:
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@abstractmethod
|
|
30
|
+
def buildSharedLib(self, objects: list[Path], libraries: list[Path], outfile: Path) -> None:
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@abstractmethod
|
|
35
|
+
def buildExecutable(self, objects: list[Path], libraries: list[Path], outfile: Path) -> None:
|
|
36
|
+
pass
|
zero/compilers/clang.py
ADDED