siliconcompiler 0.34.2__py3-none-any.whl → 0.34.3__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.
- siliconcompiler/__init__.py +12 -5
- siliconcompiler/__main__.py +1 -7
- siliconcompiler/_metadata.py +1 -1
- siliconcompiler/apps/_common.py +104 -23
- siliconcompiler/apps/sc.py +4 -8
- siliconcompiler/apps/sc_dashboard.py +6 -4
- siliconcompiler/apps/sc_install.py +10 -6
- siliconcompiler/apps/sc_issue.py +7 -5
- siliconcompiler/apps/sc_remote.py +1 -1
- siliconcompiler/apps/sc_server.py +9 -14
- siliconcompiler/apps/sc_show.py +6 -5
- siliconcompiler/apps/smake.py +130 -94
- siliconcompiler/apps/utils/replay.py +4 -7
- siliconcompiler/apps/utils/summarize.py +3 -5
- siliconcompiler/asic.py +420 -0
- siliconcompiler/checklist.py +25 -2
- siliconcompiler/cmdlineschema.py +534 -0
- siliconcompiler/constraints/asic_component.py +2 -2
- siliconcompiler/constraints/asic_pins.py +2 -2
- siliconcompiler/constraints/asic_timing.py +3 -3
- siliconcompiler/core.py +7 -32
- siliconcompiler/data/templates/tcl/manifest.tcl.j2 +8 -0
- siliconcompiler/dependencyschema.py +89 -31
- siliconcompiler/design.py +176 -207
- siliconcompiler/filesetschema.py +250 -0
- siliconcompiler/flowgraph.py +274 -95
- siliconcompiler/fpga.py +124 -1
- siliconcompiler/library.py +218 -20
- siliconcompiler/metric.py +233 -20
- siliconcompiler/package/__init__.py +271 -50
- siliconcompiler/package/git.py +92 -16
- siliconcompiler/package/github.py +108 -12
- siliconcompiler/package/https.py +79 -16
- siliconcompiler/packageschema.py +88 -7
- siliconcompiler/pathschema.py +31 -2
- siliconcompiler/pdk.py +566 -1
- siliconcompiler/project.py +1095 -94
- siliconcompiler/record.py +38 -1
- siliconcompiler/remote/__init__.py +5 -2
- siliconcompiler/remote/client.py +11 -6
- siliconcompiler/remote/schema.py +5 -23
- siliconcompiler/remote/server.py +41 -54
- siliconcompiler/report/__init__.py +3 -3
- siliconcompiler/report/dashboard/__init__.py +48 -14
- siliconcompiler/report/dashboard/cli/__init__.py +99 -21
- siliconcompiler/report/dashboard/cli/board.py +364 -179
- siliconcompiler/report/dashboard/web/__init__.py +90 -12
- siliconcompiler/report/dashboard/web/components/__init__.py +219 -240
- siliconcompiler/report/dashboard/web/components/flowgraph.py +49 -26
- siliconcompiler/report/dashboard/web/components/graph.py +139 -100
- siliconcompiler/report/dashboard/web/layouts/__init__.py +29 -1
- siliconcompiler/report/dashboard/web/layouts/_common.py +38 -2
- siliconcompiler/report/dashboard/web/layouts/vertical_flowgraph.py +39 -26
- siliconcompiler/report/dashboard/web/layouts/vertical_flowgraph_node_tab.py +50 -50
- siliconcompiler/report/dashboard/web/layouts/vertical_flowgraph_sac_tabs.py +49 -46
- siliconcompiler/report/dashboard/web/state.py +141 -14
- siliconcompiler/report/dashboard/web/utils/__init__.py +79 -16
- siliconcompiler/report/dashboard/web/utils/file_utils.py +74 -11
- siliconcompiler/report/dashboard/web/viewer.py +25 -1
- siliconcompiler/report/report.py +5 -2
- siliconcompiler/report/summary_image.py +29 -11
- siliconcompiler/scheduler/__init__.py +9 -1
- siliconcompiler/scheduler/docker.py +79 -1
- siliconcompiler/scheduler/run_node.py +35 -19
- siliconcompiler/scheduler/scheduler.py +208 -24
- siliconcompiler/scheduler/schedulernode.py +372 -46
- siliconcompiler/scheduler/send_messages.py +77 -29
- siliconcompiler/scheduler/slurm.py +76 -12
- siliconcompiler/scheduler/taskscheduler.py +140 -20
- siliconcompiler/schema/__init__.py +0 -2
- siliconcompiler/schema/baseschema.py +194 -38
- siliconcompiler/schema/journal.py +7 -4
- siliconcompiler/schema/namedschema.py +16 -10
- siliconcompiler/schema/parameter.py +55 -9
- siliconcompiler/schema/parametervalue.py +60 -0
- siliconcompiler/schema/safeschema.py +25 -2
- siliconcompiler/schema/schema_cfg.py +5 -5
- siliconcompiler/schema/utils.py +2 -2
- siliconcompiler/schema_obj.py +20 -3
- siliconcompiler/tool.py +979 -302
- siliconcompiler/tools/bambu/__init__.py +41 -0
- siliconcompiler/tools/builtin/concatenate.py +2 -2
- siliconcompiler/tools/builtin/minimum.py +2 -1
- siliconcompiler/tools/builtin/mux.py +2 -1
- siliconcompiler/tools/builtin/nop.py +2 -1
- siliconcompiler/tools/builtin/verify.py +2 -1
- siliconcompiler/tools/klayout/__init__.py +95 -0
- siliconcompiler/tools/openroad/__init__.py +289 -0
- siliconcompiler/tools/openroad/scripts/apr/preamble.tcl +3 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_detailed_route.tcl +7 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_global_route.tcl +8 -4
- siliconcompiler/tools/openroad/scripts/apr/sc_init_floorplan.tcl +9 -5
- siliconcompiler/tools/openroad/scripts/common/write_images.tcl +5 -1
- siliconcompiler/tools/slang/__init__.py +1 -1
- siliconcompiler/tools/slang/elaborate.py +2 -1
- siliconcompiler/tools/vivado/scripts/sc_run.tcl +1 -1
- siliconcompiler/tools/vivado/scripts/sc_syn_fpga.tcl +8 -1
- siliconcompiler/tools/vivado/syn_fpga.py +6 -0
- siliconcompiler/tools/vivado/vivado.py +35 -2
- siliconcompiler/tools/vpr/__init__.py +150 -0
- siliconcompiler/tools/yosys/__init__.py +369 -1
- siliconcompiler/tools/yosys/scripts/procs.tcl +0 -1
- siliconcompiler/toolscripts/_tools.json +5 -10
- siliconcompiler/utils/__init__.py +66 -0
- siliconcompiler/utils/flowgraph.py +2 -2
- siliconcompiler/utils/issue.py +2 -1
- siliconcompiler/utils/logging.py +14 -0
- siliconcompiler/utils/multiprocessing.py +256 -0
- siliconcompiler/utils/showtools.py +10 -0
- {siliconcompiler-0.34.2.dist-info → siliconcompiler-0.34.3.dist-info}/METADATA +5 -5
- {siliconcompiler-0.34.2.dist-info → siliconcompiler-0.34.3.dist-info}/RECORD +115 -118
- {siliconcompiler-0.34.2.dist-info → siliconcompiler-0.34.3.dist-info}/entry_points.txt +3 -0
- siliconcompiler/schema/cmdlineschema.py +0 -250
- siliconcompiler/toolscripts/rhel8/install-slang.sh +0 -40
- siliconcompiler/toolscripts/rhel9/install-slang.sh +0 -40
- siliconcompiler/toolscripts/ubuntu20/install-slang.sh +0 -47
- siliconcompiler/toolscripts/ubuntu22/install-slang.sh +0 -37
- siliconcompiler/toolscripts/ubuntu24/install-slang.sh +0 -37
- {siliconcompiler-0.34.2.dist-info → siliconcompiler-0.34.3.dist-info}/WHEEL +0 -0
- {siliconcompiler-0.34.2.dist-info → siliconcompiler-0.34.3.dist-info}/licenses/LICENSE +0 -0
- {siliconcompiler-0.34.2.dist-info → siliconcompiler-0.34.3.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import logging
|
|
1
3
|
import os
|
|
2
4
|
import re
|
|
3
5
|
import shutil
|
|
4
6
|
import sys
|
|
7
|
+
import traceback
|
|
5
8
|
|
|
6
9
|
import os.path
|
|
7
10
|
|
|
@@ -9,19 +12,42 @@ from siliconcompiler import Schema
|
|
|
9
12
|
from siliconcompiler import NodeStatus
|
|
10
13
|
from siliconcompiler.schema import Journal
|
|
11
14
|
from siliconcompiler.flowgraph import RuntimeFlowgraph
|
|
12
|
-
from siliconcompiler.scheduler
|
|
13
|
-
from siliconcompiler.scheduler
|
|
14
|
-
from siliconcompiler.scheduler
|
|
15
|
-
from siliconcompiler.scheduler
|
|
15
|
+
from siliconcompiler.scheduler import SchedulerNode
|
|
16
|
+
from siliconcompiler.scheduler import SlurmSchedulerNode
|
|
17
|
+
from siliconcompiler.scheduler import DockerSchedulerNode
|
|
18
|
+
from siliconcompiler.scheduler import TaskScheduler
|
|
16
19
|
|
|
17
20
|
from siliconcompiler import utils
|
|
21
|
+
from siliconcompiler.utils.logging import SCLoggerFormatter
|
|
22
|
+
from siliconcompiler.utils.multiprocessing import MPManager
|
|
18
23
|
from siliconcompiler.scheduler import send_messages
|
|
19
24
|
|
|
20
25
|
|
|
21
26
|
class Scheduler:
|
|
27
|
+
"""
|
|
28
|
+
A class for orchestrating and executing a compilation flowgraph.
|
|
29
|
+
|
|
30
|
+
The Scheduler is responsible for managing the entire lifecycle of a compilation
|
|
31
|
+
run. It interprets the flowgraph defined in the Chip object, determines which
|
|
32
|
+
nodes (steps) need to be run based on user settings (like 'from', 'to') and
|
|
33
|
+
the state of previous runs, and then executes the tasks in the correct order.
|
|
34
|
+
|
|
35
|
+
It handles setting up individual task nodes, managing dependencies, logging,
|
|
36
|
+
and reporting results.
|
|
37
|
+
"""
|
|
38
|
+
|
|
22
39
|
def __init__(self, chip):
|
|
40
|
+
"""
|
|
41
|
+
Initializes the Scheduler.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
chip (Chip): The Chip object containing the configuration and flowgraph.
|
|
45
|
+
|
|
46
|
+
Raises:
|
|
47
|
+
ValueError: If the specified flow is not defined or fails validation.
|
|
48
|
+
"""
|
|
23
49
|
self.__chip = chip
|
|
24
|
-
self.__logger = chip.logger
|
|
50
|
+
self.__logger: logging.Logger = chip.logger
|
|
25
51
|
self.__name = chip.design
|
|
26
52
|
|
|
27
53
|
flow = self.__chip.get("option", "flow")
|
|
@@ -37,14 +63,14 @@ class Scheduler:
|
|
|
37
63
|
prune_nodes = self.__chip.get('option', 'prune')
|
|
38
64
|
|
|
39
65
|
if not self.__flow.validate(logger=self.__logger):
|
|
40
|
-
raise ValueError(f"{self.__flow.name
|
|
66
|
+
raise ValueError(f"{self.__flow.name} flowgraph contains errors and cannot be run.")
|
|
41
67
|
if not RuntimeFlowgraph.validate(
|
|
42
68
|
self.__flow,
|
|
43
69
|
from_steps=from_steps,
|
|
44
70
|
to_steps=to_steps,
|
|
45
71
|
prune_nodes=prune_nodes,
|
|
46
72
|
logger=chip.logger):
|
|
47
|
-
raise ValueError(f"{self.__flow.name
|
|
73
|
+
raise ValueError(f"{self.__flow.name} flowgraph contains errors and cannot be run.")
|
|
48
74
|
|
|
49
75
|
self.__flow_runtime = RuntimeFlowgraph(
|
|
50
76
|
self.__flow,
|
|
@@ -62,7 +88,30 @@ class Scheduler:
|
|
|
62
88
|
|
|
63
89
|
self.__tasks = {}
|
|
64
90
|
|
|
91
|
+
# Create dummy handler
|
|
92
|
+
self.__joblog_handler = logging.NullHandler()
|
|
93
|
+
self.__org_job_name = self.__chip.get("option", "jobname")
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def project(self):
|
|
97
|
+
"""
|
|
98
|
+
Returns the Project object associated with this scheduler.
|
|
99
|
+
|
|
100
|
+
This property provides access to the central Project object, which holds
|
|
101
|
+
the entire design configuration, flowgraph, and results.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Project: The Project object for the current project.
|
|
105
|
+
"""
|
|
106
|
+
return self.__chip
|
|
107
|
+
|
|
65
108
|
def __print_status(self, header):
|
|
109
|
+
"""
|
|
110
|
+
Private helper to print the current status of all nodes for debugging.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
header (str): A header message to print before the status list.
|
|
114
|
+
"""
|
|
66
115
|
self.__logger.debug(f"#### {header}")
|
|
67
116
|
for step, index in self.__flow.get_nodes():
|
|
68
117
|
self.__logger.debug(f"({step}, {index}) -> "
|
|
@@ -70,17 +119,102 @@ class Scheduler:
|
|
|
70
119
|
self.__logger.debug("####")
|
|
71
120
|
|
|
72
121
|
def check_manifest(self):
|
|
122
|
+
"""
|
|
123
|
+
Checks the validity of the Chip's manifest before a run.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
bool: True if the manifest is valid, False otherwise.
|
|
127
|
+
"""
|
|
73
128
|
self.__logger.info("Checking manifest before running.")
|
|
74
129
|
return self.__chip.check_manifest()
|
|
75
130
|
|
|
76
131
|
def run_core(self):
|
|
132
|
+
"""
|
|
133
|
+
Executes the core task scheduling loop.
|
|
134
|
+
|
|
135
|
+
This method initializes and runs the TaskScheduler, which manages the
|
|
136
|
+
execution of individual nodes based on their dependencies and status.
|
|
137
|
+
"""
|
|
77
138
|
self.__record.record_python_packages()
|
|
78
139
|
|
|
79
140
|
task_scheduler = TaskScheduler(self.__chip, self.__tasks)
|
|
80
|
-
task_scheduler.run()
|
|
141
|
+
task_scheduler.run(self.__joblog_handler)
|
|
81
142
|
task_scheduler.check()
|
|
82
143
|
|
|
144
|
+
def __excepthook(self, exc_type, exc_value, exc_traceback):
|
|
145
|
+
"""
|
|
146
|
+
Custom exception hook to ensure all fatal errors are logged.
|
|
147
|
+
|
|
148
|
+
This captures unhandled exceptions, logs them to the job log file,
|
|
149
|
+
and prints a traceback for debugging before the program terminates.
|
|
150
|
+
"""
|
|
151
|
+
if issubclass(exc_type, KeyboardInterrupt):
|
|
152
|
+
sys.__excepthook__(exc_type, exc_value, exc_traceback)
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
# Print a summary of the exception
|
|
156
|
+
except_msg = f"Exception raised: {exc_type.__name__}"
|
|
157
|
+
exc_value = str(exc_value).strip()
|
|
158
|
+
if exc_value:
|
|
159
|
+
except_msg += f" / {exc_value}"
|
|
160
|
+
self.__logger.error(except_msg)
|
|
161
|
+
|
|
162
|
+
trace = io.StringIO()
|
|
163
|
+
|
|
164
|
+
# Print the full traceback for debugging
|
|
165
|
+
self.__logger.error("Traceback (most recent call last):")
|
|
166
|
+
traceback.print_tb(exc_traceback, file=trace)
|
|
167
|
+
for line in trace.getvalue().splitlines():
|
|
168
|
+
self.__logger.error(line)
|
|
169
|
+
|
|
170
|
+
# Ensure dashboard receives a stop if running
|
|
171
|
+
if self.__chip._dash:
|
|
172
|
+
self.__chip._dash.stop()
|
|
173
|
+
|
|
174
|
+
# Mark error to keep logfile
|
|
175
|
+
MPManager.error("uncaught exception")
|
|
176
|
+
|
|
83
177
|
def run(self):
|
|
178
|
+
"""
|
|
179
|
+
The main entry point to start the compilation flow.
|
|
180
|
+
|
|
181
|
+
This method orchestrates the entire run, including:
|
|
182
|
+
- Setting up a custom exception hook for logging.
|
|
183
|
+
- Initializing the job directory and log files.
|
|
184
|
+
- Configuring and setting up all nodes in the flow.
|
|
185
|
+
- Validating the manifest.
|
|
186
|
+
- Executing the core run loop.
|
|
187
|
+
- Recording the final results and history.
|
|
188
|
+
"""
|
|
189
|
+
# Install hook to ensure exception is logged
|
|
190
|
+
org_excepthook = sys.excepthook
|
|
191
|
+
sys.excepthook = self.__excepthook
|
|
192
|
+
|
|
193
|
+
# Determine job name first so we can create a log
|
|
194
|
+
if not self.__increment_job_name():
|
|
195
|
+
# No need to copy, no remove org job name
|
|
196
|
+
self.__org_job_name = None
|
|
197
|
+
|
|
198
|
+
# Clean the directory early if needed
|
|
199
|
+
self.__clean_build_dir()
|
|
200
|
+
|
|
201
|
+
# Install job file logger
|
|
202
|
+
os.makedirs(self.__chip.getworkdir(), exist_ok=True)
|
|
203
|
+
file_log = os.path.join(self.__chip.getworkdir(), "job.log")
|
|
204
|
+
bak_count = 0
|
|
205
|
+
bak_file_log = f"{file_log}.bak"
|
|
206
|
+
while os.path.exists(bak_file_log):
|
|
207
|
+
bak_count += 1
|
|
208
|
+
bak_file_log = f"{file_log}.bak.{bak_count}"
|
|
209
|
+
if os.path.exists(file_log):
|
|
210
|
+
os.rename(file_log, bak_file_log)
|
|
211
|
+
self.__joblog_handler = logging.FileHandler(file_log)
|
|
212
|
+
self.__joblog_handler.setFormatter(SCLoggerFormatter())
|
|
213
|
+
self.__logger.addHandler(self.__joblog_handler)
|
|
214
|
+
|
|
215
|
+
# Configure run
|
|
216
|
+
self.__chip._init_run()
|
|
217
|
+
|
|
84
218
|
self.__run_setup()
|
|
85
219
|
self.configure_nodes()
|
|
86
220
|
|
|
@@ -99,7 +233,24 @@ class Scheduler:
|
|
|
99
233
|
|
|
100
234
|
send_messages.send(self.__chip, 'summary', None, None)
|
|
101
235
|
|
|
236
|
+
self.__logger.removeHandler(self.__joblog_handler)
|
|
237
|
+
self.__joblog_handler = logging.NullHandler()
|
|
238
|
+
|
|
239
|
+
# Restore hook
|
|
240
|
+
sys.excepthook = org_excepthook
|
|
241
|
+
|
|
102
242
|
def __mark_pending(self, step, index):
|
|
243
|
+
"""
|
|
244
|
+
Private helper to recursively mark a node and its dependents as PENDING.
|
|
245
|
+
|
|
246
|
+
When a node is determined to need a re-run, this function ensures that
|
|
247
|
+
it and all subsequent nodes in the flowgraph are marked as PENDING,
|
|
248
|
+
effectively queueing them for execution.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
step (str): The step of the node to mark.
|
|
252
|
+
index (str): The index of the node to mark.
|
|
253
|
+
"""
|
|
103
254
|
if (step, index) not in self.__flow_runtime.get_nodes():
|
|
104
255
|
return
|
|
105
256
|
|
|
@@ -112,10 +263,14 @@ class Scheduler:
|
|
|
112
263
|
self.__record.set('status', NodeStatus.PENDING, step=next_step, index=next_index)
|
|
113
264
|
|
|
114
265
|
def __run_setup(self):
|
|
115
|
-
|
|
266
|
+
"""
|
|
267
|
+
Private helper to perform initial setup for the entire run.
|
|
116
268
|
|
|
117
|
-
|
|
118
|
-
|
|
269
|
+
This includes checking for a display environment, creating SchedulerNode
|
|
270
|
+
objects for each task, and copying results from a previous job if one
|
|
271
|
+
is specified.
|
|
272
|
+
"""
|
|
273
|
+
self.__check_display()
|
|
119
274
|
|
|
120
275
|
# Create tasks
|
|
121
276
|
copy_from_nodes = set(self.__flow_load_runtime.get_nodes()).difference(
|
|
@@ -132,22 +287,27 @@ class Scheduler:
|
|
|
132
287
|
if self.__flow.get(step, index, "tool") == "builtin":
|
|
133
288
|
self.__tasks[(step, index)].set_builtin()
|
|
134
289
|
|
|
135
|
-
if
|
|
136
|
-
self.__tasks[(step, index)].copy_from(
|
|
290
|
+
if self.__org_job_name and (step, index) in copy_from_nodes:
|
|
291
|
+
self.__tasks[(step, index)].copy_from(self.__org_job_name)
|
|
137
292
|
|
|
138
|
-
if
|
|
293
|
+
if self.__org_job_name:
|
|
139
294
|
# Copy collection directory
|
|
140
|
-
copy_from = self.__chip._getcollectdir(jobname=
|
|
295
|
+
copy_from = self.__chip._getcollectdir(jobname=self.__org_job_name)
|
|
141
296
|
copy_to = self.__chip._getcollectdir()
|
|
142
297
|
if os.path.exists(copy_from):
|
|
143
298
|
shutil.copytree(copy_from, copy_to,
|
|
144
299
|
dirs_exist_ok=True,
|
|
145
300
|
copy_function=utils.link_copy)
|
|
146
301
|
|
|
147
|
-
self.__clean_build_dir()
|
|
148
302
|
self.__reset_flow_nodes()
|
|
149
303
|
|
|
150
304
|
def __reset_flow_nodes(self):
|
|
305
|
+
"""
|
|
306
|
+
Private helper to reset the status and metrics for all nodes in the flow.
|
|
307
|
+
|
|
308
|
+
This prepares the schema for a new run by clearing out results from any
|
|
309
|
+
previous executions.
|
|
310
|
+
"""
|
|
151
311
|
# Reset record
|
|
152
312
|
for step, index in self.__flow.get_nodes():
|
|
153
313
|
self.__record.clear(step, index, keep=['remoteid', 'status', 'pythonpackage'])
|
|
@@ -158,6 +318,12 @@ class Scheduler:
|
|
|
158
318
|
self.__metrics.clear(step, index)
|
|
159
319
|
|
|
160
320
|
def __clean_build_dir(self):
|
|
321
|
+
"""
|
|
322
|
+
Private helper to clean the build directory if necessary.
|
|
323
|
+
|
|
324
|
+
If ['option', 'clean'] is True and the run starts from the beginning,
|
|
325
|
+
the entire build directory is removed to ensure a fresh start.
|
|
326
|
+
"""
|
|
161
327
|
if self.__record.get('remoteid'):
|
|
162
328
|
return
|
|
163
329
|
|
|
@@ -169,6 +335,14 @@ class Scheduler:
|
|
|
169
335
|
shutil.rmtree(cur_job_dir)
|
|
170
336
|
|
|
171
337
|
def configure_nodes(self):
|
|
338
|
+
"""
|
|
339
|
+
Configures all nodes before execution.
|
|
340
|
+
|
|
341
|
+
This is a critical step that determines the final state of each node
|
|
342
|
+
(SUCCESS, PENDING, SKIPPED) before the scheduler starts. It loads
|
|
343
|
+
results from previous runs, checks for any modifications to parameters
|
|
344
|
+
or input files, and marks nodes for re-run accordingly.
|
|
345
|
+
"""
|
|
172
346
|
from_nodes = []
|
|
173
347
|
extra_setup_nodes = {}
|
|
174
348
|
|
|
@@ -253,8 +427,9 @@ class Scheduler:
|
|
|
253
427
|
|
|
254
428
|
self.__print_status("After ensure")
|
|
255
429
|
|
|
430
|
+
os.makedirs(self.__chip.getworkdir(), exist_ok=True)
|
|
256
431
|
self.__chip.write_manifest(os.path.join(self.__chip.getworkdir(),
|
|
257
|
-
f"{self.
|
|
432
|
+
f"{self.__name}.pkg.json"))
|
|
258
433
|
journal.stop()
|
|
259
434
|
|
|
260
435
|
# Clean nodes marked pending
|
|
@@ -264,9 +439,13 @@ class Scheduler:
|
|
|
264
439
|
self.__tasks[(step, index)].clean_directory()
|
|
265
440
|
|
|
266
441
|
def __check_display(self):
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
442
|
+
"""
|
|
443
|
+
Private helper to automatically disable GUI display on headless systems.
|
|
444
|
+
|
|
445
|
+
If running on Linux without a DISPLAY or WAYLAND_DISPLAY environment
|
|
446
|
+
variable, this sets ['option', 'nodisplay'] to True to prevent tools
|
|
447
|
+
from attempting to open a GUI.
|
|
448
|
+
"""
|
|
270
449
|
|
|
271
450
|
if not self.__chip.get('option', 'nodisplay') and sys.platform == 'linux' \
|
|
272
451
|
and 'DISPLAY' not in os.environ and 'WAYLAND_DISPLAY' not in os.environ:
|
|
@@ -275,11 +454,16 @@ class Scheduler:
|
|
|
275
454
|
self.__chip.set('option', 'nodisplay', True)
|
|
276
455
|
|
|
277
456
|
def __increment_job_name(self):
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
457
|
+
"""
|
|
458
|
+
Private helper to auto-increment the jobname if ['option', 'jobincr'] is True.
|
|
459
|
+
|
|
460
|
+
This prevents overwriting previous job results by finding the highest
|
|
461
|
+
numbered existing job directory and creating a new one with an
|
|
462
|
+
incremented number.
|
|
282
463
|
|
|
464
|
+
Returns:
|
|
465
|
+
bool: True if the job name was incremented, False otherwise.
|
|
466
|
+
"""
|
|
283
467
|
if not self.__chip.get('option', 'clean'):
|
|
284
468
|
return False
|
|
285
469
|
if not self.__chip.get('option', 'jobincr'):
|