siliconcompiler 0.30.0__py3-none-any.whl → 0.31.1__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/_metadata.py +1 -1
- siliconcompiler/apps/sc_install.py +26 -4
- siliconcompiler/apps/sc_remote.py +1 -3
- siliconcompiler/core.py +24 -9
- siliconcompiler/flowgraph.py +11 -23
- siliconcompiler/{package.py → package/__init__.py} +62 -176
- siliconcompiler/package/git.py +81 -0
- siliconcompiler/package/https.py +93 -0
- siliconcompiler/remote/schema.py +9 -8
- siliconcompiler/report/report.py +4 -3
- siliconcompiler/scheduler/__init__.py +127 -113
- siliconcompiler/scheduler/docker_runner.py +4 -4
- siliconcompiler/scheduler/run_node.py +3 -3
- siliconcompiler/scheduler/send_messages.py +1 -1
- siliconcompiler/schema/schema_cfg.py +367 -357
- siliconcompiler/schema/schema_obj.py +39 -29
- siliconcompiler/schema/utils.py +19 -0
- siliconcompiler/sphinx_ext/schemagen.py +3 -1
- siliconcompiler/templates/replay/replay.sh.j2 +92 -0
- siliconcompiler/templates/tcl/manifest.tcl.j2 +1 -1
- siliconcompiler/tools/_common/__init__.py +8 -2
- siliconcompiler/tools/_common/asic.py +1 -1
- siliconcompiler/tools/_common/tcl/sc_pin_constraints.tcl +3 -5
- siliconcompiler/tools/genfasm/genfasm.py +1 -1
- siliconcompiler/tools/klayout/export.py +5 -0
- siliconcompiler/tools/klayout/klayout.py +18 -1
- siliconcompiler/tools/klayout/klayout_export.py +4 -1
- siliconcompiler/tools/klayout/klayout_operations.py +5 -2
- siliconcompiler/tools/klayout/klayout_utils.py +23 -0
- siliconcompiler/tools/klayout/operations.py +5 -0
- siliconcompiler/tools/magic/magic.py +1 -1
- siliconcompiler/tools/openroad/_apr.py +14 -3
- siliconcompiler/tools/openroad/antenna_repair.py +2 -1
- siliconcompiler/tools/openroad/clock_tree_synthesis.py +2 -1
- siliconcompiler/tools/openroad/detailed_placement.py +2 -1
- siliconcompiler/tools/openroad/detailed_route.py +8 -0
- siliconcompiler/tools/openroad/fillercell_insertion.py +2 -1
- siliconcompiler/tools/openroad/global_placement.py +2 -1
- siliconcompiler/tools/openroad/pin_placement.py +2 -1
- siliconcompiler/tools/openroad/rdlroute.py +4 -0
- siliconcompiler/tools/openroad/repair_design.py +2 -1
- siliconcompiler/tools/openroad/repair_timing.py +2 -1
- siliconcompiler/tools/openroad/scripts/apr/preamble.tcl +6 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_clock_tree_synthesis.tcl +1 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_detailed_route.tcl +8 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_global_placement.tcl +1 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_global_route.tcl +2 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_init_floorplan.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/apr/sc_macro_placement.tcl +5 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_power_grid.tcl +1 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_repair_design.tcl +1 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_repair_timing.tcl +3 -0
- siliconcompiler/tools/openroad/scripts/common/procs.tcl +29 -12
- siliconcompiler/tools/openroad/scripts/common/reports.tcl +15 -0
- siliconcompiler/tools/openroad/scripts/common/write_images.tcl +28 -0
- siliconcompiler/tools/openroad/scripts/sc_rdlroute.tcl +3 -13
- siliconcompiler/tools/vpr/vpr.py +86 -6
- siliconcompiler/tools/yosys/__init__.py +7 -0
- siliconcompiler/tools/yosys/sc_syn.tcl +33 -24
- siliconcompiler/tools/yosys/syn_asic.py +27 -0
- siliconcompiler/tools/yosys/syn_asic.tcl +27 -0
- siliconcompiler/toolscripts/_tools.json +15 -3
- siliconcompiler/toolscripts/rhel8/install-yosys-moosic.sh +17 -0
- siliconcompiler/toolscripts/rhel8/install-yosys-slang.sh +22 -0
- siliconcompiler/toolscripts/rhel9/install-yosys-moosic.sh +17 -0
- siliconcompiler/toolscripts/rhel9/install-yosys-slang.sh +22 -0
- siliconcompiler/toolscripts/ubuntu20/install-yosys-moosic.sh +17 -0
- siliconcompiler/toolscripts/ubuntu20/install-yosys-slang.sh +22 -0
- siliconcompiler/toolscripts/ubuntu22/install-yosys-moosic.sh +17 -0
- siliconcompiler/toolscripts/ubuntu22/install-yosys-slang.sh +22 -0
- siliconcompiler/toolscripts/ubuntu24/install-yosys-moosic.sh +17 -0
- siliconcompiler/toolscripts/ubuntu24/install-yosys-slang.sh +22 -0
- siliconcompiler/utils/__init__.py +33 -5
- {siliconcompiler-0.30.0.dist-info → siliconcompiler-0.31.1.dist-info}/METADATA +21 -23
- {siliconcompiler-0.30.0.dist-info → siliconcompiler-0.31.1.dist-info}/RECORD +79 -66
- {siliconcompiler-0.30.0.dist-info → siliconcompiler-0.31.1.dist-info}/WHEEL +1 -1
- {siliconcompiler-0.30.0.dist-info → siliconcompiler-0.31.1.dist-info}/entry_points.txt +4 -0
- {siliconcompiler-0.30.0.dist-info → siliconcompiler-0.31.1.dist-info}/LICENSE +0 -0
- {siliconcompiler-0.30.0.dist-info → siliconcompiler-0.31.1.dist-info}/top_level.txt +0 -0
siliconcompiler/_metadata.py
CHANGED
|
@@ -17,7 +17,7 @@ class ChoiceOptional(Container):
|
|
|
17
17
|
def __init__(self, choices):
|
|
18
18
|
super().__init__()
|
|
19
19
|
|
|
20
|
-
self.__choices = set(choices)
|
|
20
|
+
self.__choices = sorted(set(choices))
|
|
21
21
|
|
|
22
22
|
def __contains__(self, item):
|
|
23
23
|
if not item:
|
|
@@ -30,7 +30,7 @@ class ChoiceOptional(Container):
|
|
|
30
30
|
|
|
31
31
|
def get_items(self, choices):
|
|
32
32
|
items = set(choices)
|
|
33
|
-
return sorted(
|
|
33
|
+
return sorted(items)
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
def install_tool(tool, script, build_dir, prefix):
|
|
@@ -101,6 +101,19 @@ def print_machine_info():
|
|
|
101
101
|
print("Scripts: ", _get_tool_script_dir())
|
|
102
102
|
|
|
103
103
|
|
|
104
|
+
def __print_summary(successful, failed):
|
|
105
|
+
max_len = 64
|
|
106
|
+
print("#"*max_len)
|
|
107
|
+
if successful:
|
|
108
|
+
msg = f"Installed: {', '.join(sorted(successful))}"
|
|
109
|
+
print(f"# {msg}")
|
|
110
|
+
|
|
111
|
+
if failed:
|
|
112
|
+
msg = f"Failed to install: {failed}"
|
|
113
|
+
print(f"# {msg}")
|
|
114
|
+
print("#"*max_len)
|
|
115
|
+
|
|
116
|
+
|
|
104
117
|
def _get_tool_script_dir():
|
|
105
118
|
return Path(siliconcompiler.__file__).parent / "toolscripts"
|
|
106
119
|
|
|
@@ -143,6 +156,10 @@ def _recommended_tool_groups(tools):
|
|
|
143
156
|
return filter_groups
|
|
144
157
|
|
|
145
158
|
|
|
159
|
+
class HelpFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter):
|
|
160
|
+
pass
|
|
161
|
+
|
|
162
|
+
|
|
146
163
|
def main():
|
|
147
164
|
progname = "sc-install"
|
|
148
165
|
description = """
|
|
@@ -174,7 +191,7 @@ To system debugging information (this should only be used to debug):
|
|
|
174
191
|
parser = argparse.ArgumentParser(
|
|
175
192
|
prog=progname,
|
|
176
193
|
description=description,
|
|
177
|
-
formatter_class=
|
|
194
|
+
formatter_class=HelpFormatter)
|
|
178
195
|
|
|
179
196
|
tools = _get_tools_list()
|
|
180
197
|
|
|
@@ -238,6 +255,7 @@ To system debugging information (this should only be used to debug):
|
|
|
238
255
|
args.tool.extend(tool_groups[group])
|
|
239
256
|
|
|
240
257
|
tools_handled = set()
|
|
258
|
+
tools_completed = set()
|
|
241
259
|
for tool in args.tool:
|
|
242
260
|
if tool in tools_handled:
|
|
243
261
|
continue
|
|
@@ -246,9 +264,14 @@ To system debugging information (this should only be used to debug):
|
|
|
246
264
|
show_tool(tool, tools[tool])
|
|
247
265
|
else:
|
|
248
266
|
if not install_tool(tool, tools[tool], args.build_dir, args.prefix):
|
|
267
|
+
__print_summary(tools_completed, tool)
|
|
249
268
|
return 1
|
|
269
|
+
else:
|
|
270
|
+
tools_completed.add(tool)
|
|
250
271
|
|
|
251
272
|
if not args.show:
|
|
273
|
+
__print_summary(tools_completed, None)
|
|
274
|
+
|
|
252
275
|
msgs = []
|
|
253
276
|
for env, path in (
|
|
254
277
|
("PATH", "bin"),
|
|
@@ -258,7 +281,6 @@ To system debugging information (this should only be used to debug):
|
|
|
258
281
|
os.path.expandvars(os.path.expanduser(p))
|
|
259
282
|
for p in os.getenv(env, "").split(":")
|
|
260
283
|
]
|
|
261
|
-
print(envs)
|
|
262
284
|
if check_path not in envs:
|
|
263
285
|
msgs.extend([
|
|
264
286
|
"",
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
# Copyright 2023 Silicon Compiler Authors. All Rights Reserved.
|
|
2
|
-
import copy
|
|
3
2
|
import os
|
|
4
3
|
import sys
|
|
5
4
|
|
|
@@ -157,7 +156,6 @@ To delete a job, use:
|
|
|
157
156
|
# in its "check_progress/ until job is done" loop.
|
|
158
157
|
elif args['reconnect']:
|
|
159
158
|
# Start from successors of entry nodes, so entry nodes are not fetched from remote.
|
|
160
|
-
environment = copy.deepcopy(os.environ)
|
|
161
159
|
flow = chip.get('option', 'flow')
|
|
162
160
|
entry_nodes = _get_flowgraph_entry_nodes(chip, flow)
|
|
163
161
|
for entry_node in entry_nodes:
|
|
@@ -177,7 +175,7 @@ To delete a job, use:
|
|
|
177
175
|
f'{chip.design}.pkg.json')
|
|
178
176
|
if os.path.exists(manifest):
|
|
179
177
|
chip.schema.read_journal(manifest)
|
|
180
|
-
_finalize_run(chip
|
|
178
|
+
_finalize_run(chip)
|
|
181
179
|
|
|
182
180
|
# Summarize the run.
|
|
183
181
|
chip.summary()
|
siliconcompiler/core.py
CHANGED
|
@@ -392,7 +392,7 @@ class Chip:
|
|
|
392
392
|
for vals, step, index in self.schema._getvals(*key):
|
|
393
393
|
if not vals:
|
|
394
394
|
continue
|
|
395
|
-
if self.get(*key, field='pernode')
|
|
395
|
+
if not self.get(*key, field='pernode').is_never():
|
|
396
396
|
if step is None:
|
|
397
397
|
step = Schema.GLOBAL_KEY
|
|
398
398
|
if index is None:
|
|
@@ -852,7 +852,7 @@ class Chip:
|
|
|
852
852
|
strict = self.schema.get('option', 'strict')
|
|
853
853
|
if field == 'value' and strict:
|
|
854
854
|
pernode = self.schema.get(*keypath, field='pernode')
|
|
855
|
-
if pernode ==
|
|
855
|
+
if pernode == schema_utils.PerNode.OPTIONAL and \
|
|
856
856
|
(step is None or index is None) and \
|
|
857
857
|
(Schema.GLOBAL_KEY not in (step, index)): # allow explicit access to global
|
|
858
858
|
self.error(
|
|
@@ -1111,7 +1111,7 @@ class Chip:
|
|
|
1111
1111
|
package_dir = os.path.dirname(os.path.abspath(filename))
|
|
1112
1112
|
|
|
1113
1113
|
def __make_path(rel, path):
|
|
1114
|
-
path = utils._resolve_env_vars(self, path)
|
|
1114
|
+
path = utils._resolve_env_vars(self, path, None, None)
|
|
1115
1115
|
if os.path.isabs(path):
|
|
1116
1116
|
if path.startswith(rel):
|
|
1117
1117
|
return os.path.relpath(path, rel), package_name
|
|
@@ -1282,7 +1282,7 @@ class Chip:
|
|
|
1282
1282
|
"""
|
|
1283
1283
|
strict = self.get('option', 'strict')
|
|
1284
1284
|
pernode = self.get(*keypath, field='pernode')
|
|
1285
|
-
if strict and pernode ==
|
|
1285
|
+
if strict and pernode == schema_utils.PerNode.OPTIONAL and (step is None or index is None):
|
|
1286
1286
|
self.error(
|
|
1287
1287
|
f"Invalid args to find_files() of keypath {keypath}: step and "
|
|
1288
1288
|
"index are required for reading from this parameter while "
|
|
@@ -1394,7 +1394,9 @@ class Chip:
|
|
|
1394
1394
|
result.append(utils.find_sc_file(self,
|
|
1395
1395
|
path,
|
|
1396
1396
|
missing_ok=missing_ok,
|
|
1397
|
-
search_paths=search_paths
|
|
1397
|
+
search_paths=search_paths,
|
|
1398
|
+
step=step,
|
|
1399
|
+
index=index))
|
|
1398
1400
|
|
|
1399
1401
|
if self._relative_path and not abs_path_only:
|
|
1400
1402
|
rel_result = []
|
|
@@ -2828,10 +2830,10 @@ class Chip:
|
|
|
2828
2830
|
set_step = None
|
|
2829
2831
|
set_index = None
|
|
2830
2832
|
pernode = self.get(*keypath, field='pernode')
|
|
2831
|
-
if pernode ==
|
|
2833
|
+
if pernode == schema_utils.PerNode.REQUIRED:
|
|
2832
2834
|
set_step = step
|
|
2833
2835
|
set_index = index
|
|
2834
|
-
elif pernode ==
|
|
2836
|
+
elif pernode == schema_utils.PerNode.OPTIONAL:
|
|
2835
2837
|
for vals, key_step, key_index in self.schema._getvals(*keypath):
|
|
2836
2838
|
if key_step == step and key_index == index and vals:
|
|
2837
2839
|
set_step = step
|
|
@@ -2887,7 +2889,7 @@ class Chip:
|
|
|
2887
2889
|
return self._dash
|
|
2888
2890
|
|
|
2889
2891
|
###########################################################################
|
|
2890
|
-
def summary(self, show_all_indices=False, generate_image=True, generate_html=
|
|
2892
|
+
def summary(self, show_all_indices=False, generate_image=True, generate_html=False):
|
|
2891
2893
|
'''
|
|
2892
2894
|
Prints a summary of the compilation manifest.
|
|
2893
2895
|
|
|
@@ -2927,6 +2929,10 @@ class Chip:
|
|
|
2927
2929
|
results_img = os.path.join(work_dir, f'{self.design}.png')
|
|
2928
2930
|
results_html = os.path.join(work_dir, 'report.html')
|
|
2929
2931
|
|
|
2932
|
+
for path in (results_img, results_html):
|
|
2933
|
+
if os.path.exists(path):
|
|
2934
|
+
os.remove(path)
|
|
2935
|
+
|
|
2930
2936
|
if generate_image:
|
|
2931
2937
|
_generate_summary_image(self, results_img)
|
|
2932
2938
|
|
|
@@ -2944,7 +2950,7 @@ class Chip:
|
|
|
2944
2950
|
elif os.path.isfile(results_html):
|
|
2945
2951
|
_open_html_report(self, results_html)
|
|
2946
2952
|
else:
|
|
2947
|
-
self.
|
|
2953
|
+
self.dashboard(wait=False)
|
|
2948
2954
|
|
|
2949
2955
|
###########################################################################
|
|
2950
2956
|
def clock(self, pin, period, jitter=0, mode='global'):
|
|
@@ -3092,6 +3098,13 @@ class Chip:
|
|
|
3092
3098
|
step (str): Step name
|
|
3093
3099
|
index (int): Step index
|
|
3094
3100
|
'''
|
|
3101
|
+
|
|
3102
|
+
if flow not in self.getkeys('flowgraph'):
|
|
3103
|
+
raise ValueError(f'{flow} is not in the manifest')
|
|
3104
|
+
|
|
3105
|
+
if step not in self.getkeys('flowgraph', flow):
|
|
3106
|
+
raise ValueError(f'{step} is not a valid step in {flow}')
|
|
3107
|
+
|
|
3095
3108
|
if index is None:
|
|
3096
3109
|
# Iterate over all indexes
|
|
3097
3110
|
for index in self.getkeys('flowgraph', flow, step):
|
|
@@ -3099,6 +3112,8 @@ class Chip:
|
|
|
3099
3112
|
return
|
|
3100
3113
|
|
|
3101
3114
|
index = str(index)
|
|
3115
|
+
if index not in self.getkeys('flowgraph', flow, step):
|
|
3116
|
+
raise ValueError(f'{index} is not a valid index for {step} in {flow}')
|
|
3102
3117
|
|
|
3103
3118
|
# Save input edges
|
|
3104
3119
|
node = (step, index)
|
siliconcompiler/flowgraph.py
CHANGED
|
@@ -4,23 +4,6 @@ from siliconcompiler import SiliconCompilerError, NodeStatus
|
|
|
4
4
|
from siliconcompiler.tools._common import input_file_node_name, get_tool_task
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
def _check_execution_nodes_inputs(chip, flow):
|
|
8
|
-
for node in nodes_to_execute(chip, flow):
|
|
9
|
-
if node in _get_execution_entry_nodes(chip, flow):
|
|
10
|
-
continue
|
|
11
|
-
pruned_node_inputs = set(_get_pruned_node_inputs(chip, flow, node))
|
|
12
|
-
node_inputs = set(_get_flowgraph_node_inputs(chip, flow, node))
|
|
13
|
-
tool, task = get_tool_task(chip, node[0], node[1], flow=flow)
|
|
14
|
-
if tool == 'builtin' and not pruned_node_inputs or \
|
|
15
|
-
tool != 'builtin' and pruned_node_inputs != node_inputs:
|
|
16
|
-
chip.logger.warning(
|
|
17
|
-
f'Flowgraph connection from {node_inputs.difference(pruned_node_inputs)} '
|
|
18
|
-
f'to {node} is missing. '
|
|
19
|
-
f'Double check your flowgraph and from/to/prune options.')
|
|
20
|
-
return False
|
|
21
|
-
return True
|
|
22
|
-
|
|
23
|
-
|
|
24
7
|
def _nodes_to_execute(chip, flow, from_nodes, to_nodes, prune_nodes):
|
|
25
8
|
'''
|
|
26
9
|
Assumes a flowgraph with valid edges for the inputs
|
|
@@ -283,12 +266,12 @@ def nodes_to_execute(chip, flow=None):
|
|
|
283
266
|
if flow is None:
|
|
284
267
|
flow = chip.get('option', 'flow')
|
|
285
268
|
|
|
286
|
-
from_nodes = _get_execution_entry_nodes(chip, flow)
|
|
287
|
-
to_nodes = _get_execution_exit_nodes(chip, flow)
|
|
288
|
-
prune_nodes = chip.get('option', 'prune')
|
|
269
|
+
from_nodes = set(_get_execution_entry_nodes(chip, flow))
|
|
270
|
+
to_nodes = set(_get_execution_exit_nodes(chip, flow))
|
|
271
|
+
prune_nodes = set(chip.get('option', 'prune'))
|
|
289
272
|
if from_nodes == to_nodes:
|
|
290
273
|
return list(filter(lambda node: node not in prune_nodes, from_nodes))
|
|
291
|
-
return _nodes_to_execute(chip, flow,
|
|
274
|
+
return _nodes_to_execute(chip, flow, from_nodes, to_nodes, prune_nodes)
|
|
292
275
|
|
|
293
276
|
|
|
294
277
|
###########################################################################
|
|
@@ -345,8 +328,13 @@ def _check_flowgraph(chip, flow=None):
|
|
|
345
328
|
chip.logger.error(f'{step} is not defined in the {flow} flowgraph')
|
|
346
329
|
error = True
|
|
347
330
|
|
|
348
|
-
|
|
349
|
-
|
|
331
|
+
for step, index in chip.get('option', 'prune'):
|
|
332
|
+
if step not in chip.getkeys('flowgraph', flow):
|
|
333
|
+
chip.logger.error(f'{step} is not defined in the {flow} flowgraph')
|
|
334
|
+
error = True
|
|
335
|
+
elif str(index) not in chip.getkeys('flowgraph', flow, step):
|
|
336
|
+
chip.logger.error(f'{step}{index} is not defined in the {flow} flowgraph')
|
|
337
|
+
error = True
|
|
350
338
|
|
|
351
339
|
unreachable_steps = _unreachable_steps_to_execute(chip, flow)
|
|
352
340
|
if unreachable_steps:
|
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
import os
|
|
2
|
-
import requests
|
|
3
|
-
import tarfile
|
|
4
|
-
import zipfile
|
|
5
|
-
from git import Repo, GitCommandError
|
|
6
2
|
from urllib.parse import urlparse
|
|
7
3
|
import importlib
|
|
8
|
-
import shutil
|
|
9
4
|
import re
|
|
10
5
|
from siliconcompiler import SiliconCompilerError
|
|
11
6
|
from siliconcompiler.utils import default_cache_dir, _resolve_env_vars
|
|
12
7
|
import json
|
|
13
8
|
from importlib.metadata import distributions, distribution
|
|
14
9
|
import functools
|
|
15
|
-
import fasteners
|
|
16
10
|
import time
|
|
17
11
|
from pathlib import Path
|
|
18
|
-
from io import BytesIO
|
|
19
12
|
|
|
20
13
|
from github import Github
|
|
21
14
|
import github.Auth
|
|
22
15
|
|
|
16
|
+
from siliconcompiler.utils import get_plugins
|
|
17
|
+
|
|
23
18
|
|
|
24
19
|
def get_cache_path(chip):
|
|
25
20
|
cache_path = chip.get('option', 'cachedir')
|
|
@@ -33,10 +28,45 @@ def get_cache_path(chip):
|
|
|
33
28
|
return cache_path
|
|
34
29
|
|
|
35
30
|
|
|
36
|
-
def
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
def get_download_cache_path(chip, package, ref):
|
|
32
|
+
cache_path = get_cache_path(chip)
|
|
33
|
+
if not os.path.exists(cache_path):
|
|
34
|
+
os.makedirs(cache_path, exist_ok=True)
|
|
35
|
+
|
|
36
|
+
if ref is None:
|
|
37
|
+
raise SiliconCompilerError(f'Reference is required for cached data: {package}', chip=chip)
|
|
38
|
+
|
|
39
|
+
return \
|
|
40
|
+
os.path.join(cache_path, f'{package}-{ref}'), \
|
|
41
|
+
os.path.join(cache_path, f'{package}-{ref}.lock')
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _file_path_resolver(chip, package, path, ref, url):
|
|
45
|
+
return os.path.abspath(path.replace('file://', ''))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _python_path_resolver(chip, package, path, ref, url):
|
|
49
|
+
return path_from_python(chip, url.netloc)
|
|
39
50
|
|
|
51
|
+
|
|
52
|
+
def _get_path_resolver(path):
|
|
53
|
+
url = urlparse(path)
|
|
54
|
+
|
|
55
|
+
for resolver in get_plugins("path_resolver"):
|
|
56
|
+
func = resolver(url)
|
|
57
|
+
if func:
|
|
58
|
+
return func, url
|
|
59
|
+
|
|
60
|
+
if url.scheme == "file":
|
|
61
|
+
return _file_path_resolver, url
|
|
62
|
+
|
|
63
|
+
if url.scheme == "python":
|
|
64
|
+
return _python_path_resolver, url
|
|
65
|
+
|
|
66
|
+
raise ValueError(f"{path} is not supported")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _path(chip, package):
|
|
40
70
|
# Initially try retrieving data source from schema
|
|
41
71
|
data = {}
|
|
42
72
|
data['path'] = chip.get('package', 'source', package, 'path')
|
|
@@ -46,50 +76,15 @@ def _path(chip, package, download_handler):
|
|
|
46
76
|
f'Could not find package source for {package} in schema. '
|
|
47
77
|
'You can use register_source() to add it.', chip=chip)
|
|
48
78
|
|
|
49
|
-
data['path'] = _resolve_env_vars(chip, data['path'])
|
|
79
|
+
data['path'] = _resolve_env_vars(chip, data['path'], None, None)
|
|
50
80
|
|
|
51
|
-
|
|
81
|
+
if os.path.exists(data['path']):
|
|
82
|
+
# Path is already a path
|
|
83
|
+
return os.path.abspath(data['path'])
|
|
52
84
|
|
|
53
|
-
|
|
54
|
-
if data['path'].startswith('file://') or os.path.exists(data['path']):
|
|
55
|
-
path = os.path.abspath(data['path'].replace('file://', ''))
|
|
56
|
-
chip.logger.info(f'Found {package} data at {path}')
|
|
57
|
-
chip._packages[package] = path
|
|
58
|
-
return path
|
|
59
|
-
elif data['path'].startswith('python://'):
|
|
60
|
-
path = path_from_python(chip, url.netloc)
|
|
61
|
-
chip.logger.info(f'Found {package} data at {path}')
|
|
62
|
-
chip._packages[package] = path
|
|
63
|
-
return path
|
|
64
|
-
|
|
65
|
-
# location of the python package
|
|
66
|
-
cache_path = get_cache_path(chip)
|
|
67
|
-
if not os.path.exists(cache_path):
|
|
68
|
-
os.makedirs(cache_path, exist_ok=True)
|
|
69
|
-
project_id = f'{package}-{data.get("ref")}'
|
|
70
|
-
if url.scheme not in ['git', 'git+https', 'https', 'git+ssh', 'ssh'] or not project_id:
|
|
71
|
-
raise SiliconCompilerError(
|
|
72
|
-
f'Could not find data path in package {package}: {data["path"]}',
|
|
73
|
-
chip=chip)
|
|
74
|
-
|
|
75
|
-
data_path = os.path.join(cache_path, project_id)
|
|
76
|
-
|
|
77
|
-
if download_handler:
|
|
78
|
-
download_handler(chip,
|
|
79
|
-
package,
|
|
80
|
-
data,
|
|
81
|
-
url,
|
|
82
|
-
data_path,
|
|
83
|
-
os.path.join(cache_path, f'{project_id}.lock'))
|
|
84
|
-
|
|
85
|
-
if os.path.exists(data_path):
|
|
86
|
-
if package not in chip._packages:
|
|
87
|
-
chip.logger.info(f'Saved {package} data to {data_path}')
|
|
88
|
-
chip._packages[package] = data_path
|
|
89
|
-
return data_path
|
|
85
|
+
path_resolver, url = _get_path_resolver(data['path'])
|
|
90
86
|
|
|
91
|
-
|
|
92
|
-
chip=chip)
|
|
87
|
+
return path_resolver(chip, package, data['path'], data['ref'], url)
|
|
93
88
|
|
|
94
89
|
|
|
95
90
|
def path(chip, package):
|
|
@@ -102,40 +97,25 @@ def path(chip, package):
|
|
|
102
97
|
path: Location of data source on the local system
|
|
103
98
|
"""
|
|
104
99
|
|
|
105
|
-
|
|
106
|
-
|
|
100
|
+
if package not in chip._packages:
|
|
101
|
+
changed = False
|
|
102
|
+
data_path = _path(chip, package)
|
|
107
103
|
|
|
108
|
-
|
|
109
|
-
|
|
104
|
+
if isinstance(data_path, tuple) and len(data_path) == 2:
|
|
105
|
+
data_path, changed = data_path
|
|
110
106
|
|
|
111
|
-
|
|
107
|
+
if os.path.exists(data_path):
|
|
108
|
+
if package not in chip._packages and changed:
|
|
109
|
+
chip.logger.info(f'Saved {package} data to {data_path}')
|
|
110
|
+
else:
|
|
111
|
+
chip.logger.info(f'Found {package} data at {data_path}')
|
|
112
112
|
|
|
113
|
-
# check cached package data source
|
|
114
|
-
if os.path.exists(data_path):
|
|
115
|
-
chip.logger.info(f'Found cached {package} data at {data_path}')
|
|
116
|
-
if url.scheme in ['git', 'git+https', 'ssh', 'git+ssh']:
|
|
117
|
-
try:
|
|
118
|
-
repo = Repo(data_path)
|
|
119
|
-
if repo.untracked_files or repo.index.diff("HEAD"):
|
|
120
|
-
chip.logger.warning('The repo of the cached data is dirty.')
|
|
121
|
-
_release_data_lock(data_lock)
|
|
122
|
-
chip._packages[package] = data_path
|
|
123
|
-
return
|
|
124
|
-
except GitCommandError:
|
|
125
|
-
chip.logger.warning('Deleting corrupted cache data.')
|
|
126
|
-
shutil.rmtree(path)
|
|
127
|
-
else:
|
|
128
|
-
_release_data_lock(data_lock)
|
|
129
113
|
chip._packages[package] = data_path
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if url.scheme in ['git', 'git+https', 'ssh', 'git+ssh']:
|
|
134
|
-
clone_synchronized(chip, package, data, data_path)
|
|
135
|
-
elif url.scheme == 'https':
|
|
136
|
-
extract_from_url(chip, package, data, data_path)
|
|
114
|
+
else:
|
|
115
|
+
raise SiliconCompilerError(f'Unable to locate {package} data in {data_path}',
|
|
116
|
+
chip=chip)
|
|
137
117
|
|
|
138
|
-
|
|
118
|
+
return chip._packages[package]
|
|
139
119
|
|
|
140
120
|
|
|
141
121
|
def __get_filebased_lock(data_lock):
|
|
@@ -143,7 +123,7 @@ def __get_filebased_lock(data_lock):
|
|
|
143
123
|
return Path(f'{base}.sc_lock')
|
|
144
124
|
|
|
145
125
|
|
|
146
|
-
def
|
|
126
|
+
def aquire_data_lock(data_path, data_lock):
|
|
147
127
|
# Wait a maximum of 10 minutes for other processes to finish
|
|
148
128
|
max_seconds = 10 * 60
|
|
149
129
|
try:
|
|
@@ -168,7 +148,7 @@ def _aquire_data_lock(data_path, data_lock):
|
|
|
168
148
|
'please delete it.')
|
|
169
149
|
|
|
170
150
|
|
|
171
|
-
def
|
|
151
|
+
def release_data_lock(data_lock):
|
|
172
152
|
# Check if file based locking method was used
|
|
173
153
|
lock_file = __get_filebased_lock(data_lock)
|
|
174
154
|
if lock_file.exists():
|
|
@@ -178,100 +158,6 @@ def _release_data_lock(data_lock):
|
|
|
178
158
|
data_lock.release()
|
|
179
159
|
|
|
180
160
|
|
|
181
|
-
def clone_synchronized(chip, package, data, data_path):
|
|
182
|
-
url = urlparse(data['path'])
|
|
183
|
-
try:
|
|
184
|
-
clone_from_git(chip, package, data, data_path)
|
|
185
|
-
except GitCommandError as e:
|
|
186
|
-
if 'Permission denied' in repr(e):
|
|
187
|
-
if url.scheme in ['ssh', 'git+ssh']:
|
|
188
|
-
chip.logger.error('Failed to authenticate. Please setup your git ssh.')
|
|
189
|
-
elif url.scheme in ['git', 'git+https']:
|
|
190
|
-
chip.logger.error('Failed to authenticate. Please use a token or ssh.')
|
|
191
|
-
else:
|
|
192
|
-
chip.logger.error(str(e))
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
def clone_from_git(chip, package, data, repo_path):
|
|
196
|
-
url = urlparse(data['path'])
|
|
197
|
-
if url.scheme in ['git', 'git+https'] and url.username:
|
|
198
|
-
chip.logger.warning('Your token is in the data source path and will be stored in the '
|
|
199
|
-
'schema. If you do not want this set the env variable GIT_TOKEN '
|
|
200
|
-
'or use ssh for authentication.')
|
|
201
|
-
if url.scheme in ['git+ssh', 'ssh']:
|
|
202
|
-
chip.logger.info(f'Cloning {package} data from {url.netloc}:{url.path[1:]}')
|
|
203
|
-
# Git requires the format git@github.com:org/repo instead of git@github.com/org/repo
|
|
204
|
-
repo = Repo.clone_from(f'{url.netloc}:{url.path[1:]}',
|
|
205
|
-
repo_path,
|
|
206
|
-
recurse_submodules=True)
|
|
207
|
-
else:
|
|
208
|
-
if os.environ.get('GIT_TOKEN') and not url.username:
|
|
209
|
-
url = url._replace(netloc=f'{os.environ.get("GIT_TOKEN")}@{url.hostname}')
|
|
210
|
-
url = url._replace(scheme='https')
|
|
211
|
-
chip.logger.info(f'Cloning {package} data from {url.geturl()}')
|
|
212
|
-
repo = Repo.clone_from(url.geturl(), repo_path, recurse_submodules=True)
|
|
213
|
-
chip.logger.info(f'Checking out {data["ref"]}')
|
|
214
|
-
repo.git.checkout(data["ref"])
|
|
215
|
-
for submodule in repo.submodules:
|
|
216
|
-
submodule.update(init=True)
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
def extract_from_url(chip, package, data, data_path):
|
|
220
|
-
url = urlparse(data['path'])
|
|
221
|
-
data_url = data.get('path')
|
|
222
|
-
headers = {}
|
|
223
|
-
if os.environ.get('GIT_TOKEN') or url.username:
|
|
224
|
-
headers['Authorization'] = f'token {os.environ.get("GIT_TOKEN") or url.username}'
|
|
225
|
-
if "github" in data_url:
|
|
226
|
-
headers['Accept'] = 'application/octet-stream'
|
|
227
|
-
data_url = data['path']
|
|
228
|
-
if data_url.endswith('/'):
|
|
229
|
-
data_url = f"{data_url}{data['ref']}.tar.gz"
|
|
230
|
-
chip.logger.info(f'Downloading {package} data from {data_url}')
|
|
231
|
-
response = requests.get(data_url, stream=True, headers=headers)
|
|
232
|
-
if not response.ok:
|
|
233
|
-
raise SiliconCompilerError(f'Failed to download {package} data source.', chip=chip)
|
|
234
|
-
|
|
235
|
-
fileobj = BytesIO(response.content)
|
|
236
|
-
try:
|
|
237
|
-
with tarfile.open(fileobj=fileobj, mode='r|gz') as tar_ref:
|
|
238
|
-
tar_ref.extractall(path=data_path)
|
|
239
|
-
except tarfile.ReadError:
|
|
240
|
-
fileobj.seek(0)
|
|
241
|
-
# Try as zip
|
|
242
|
-
with zipfile.ZipFile(fileobj) as zip_ref:
|
|
243
|
-
zip_ref.extractall(path=data_path)
|
|
244
|
-
|
|
245
|
-
if 'github' in url.netloc and len(os.listdir(data_path)) == 1:
|
|
246
|
-
# Github inserts one folder at the highest level of the tar file
|
|
247
|
-
# this compensates for this behavior
|
|
248
|
-
gh_url = urlparse(data_url)
|
|
249
|
-
|
|
250
|
-
repo = gh_url.path.split('/')[2]
|
|
251
|
-
|
|
252
|
-
ref = gh_url.path.split('/')[-1]
|
|
253
|
-
if repo.endswith('.git'):
|
|
254
|
-
ref = data['ref']
|
|
255
|
-
elif ref.endswith('.tar.gz'):
|
|
256
|
-
ref = ref[0:-7]
|
|
257
|
-
elif ref.endswith('.tgz'):
|
|
258
|
-
ref = ref[0:-4]
|
|
259
|
-
else:
|
|
260
|
-
ref = ref.split('.')[0]
|
|
261
|
-
|
|
262
|
-
if ref.startswith('v'):
|
|
263
|
-
ref = ref[1:]
|
|
264
|
-
|
|
265
|
-
github_folder = f"{repo}-{ref}"
|
|
266
|
-
|
|
267
|
-
if github_folder in os.listdir(data_path):
|
|
268
|
-
# This moves all files one level up
|
|
269
|
-
git_path = os.path.join(data_path, github_folder)
|
|
270
|
-
for data_file in os.listdir(git_path):
|
|
271
|
-
shutil.move(os.path.join(git_path, data_file), data_path)
|
|
272
|
-
os.removedirs(git_path)
|
|
273
|
-
|
|
274
|
-
|
|
275
161
|
def path_from_python(chip, python_package, append_path=None):
|
|
276
162
|
try:
|
|
277
163
|
module = importlib.import_module(python_package)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
|
|
3
|
+
import os.path
|
|
4
|
+
|
|
5
|
+
from git import Repo, GitCommandError
|
|
6
|
+
from fasteners import InterProcessLock
|
|
7
|
+
|
|
8
|
+
from siliconcompiler.package import get_download_cache_path
|
|
9
|
+
from siliconcompiler.package import aquire_data_lock, release_data_lock
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_resolver(url):
|
|
13
|
+
if url.scheme in ("git", "git+https", "git+ssh", "ssh"):
|
|
14
|
+
return git_resolver
|
|
15
|
+
return None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def git_resolver(chip, package, path, ref, url):
|
|
19
|
+
data_path, data_path_lock = get_download_cache_path(chip, package, ref)
|
|
20
|
+
|
|
21
|
+
# Acquire lock
|
|
22
|
+
data_lock = InterProcessLock(data_path_lock)
|
|
23
|
+
aquire_data_lock(data_path, data_lock)
|
|
24
|
+
|
|
25
|
+
if os.path.exists(data_path):
|
|
26
|
+
try:
|
|
27
|
+
repo = Repo(data_path)
|
|
28
|
+
if repo.untracked_files or repo.index.diff("HEAD"):
|
|
29
|
+
chip.logger.warning('The repo of the cached data is dirty.')
|
|
30
|
+
release_data_lock(data_lock)
|
|
31
|
+
return data_path, False
|
|
32
|
+
except GitCommandError:
|
|
33
|
+
chip.logger.warning('Deleting corrupted cache data.')
|
|
34
|
+
shutil.rmtree(data_path)
|
|
35
|
+
|
|
36
|
+
clone_synchronized(chip, package, path, ref, url, data_path)
|
|
37
|
+
|
|
38
|
+
release_data_lock(data_lock)
|
|
39
|
+
|
|
40
|
+
return data_path, True
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def clone_synchronized(chip, package, path, ref, url, data_path):
|
|
44
|
+
try:
|
|
45
|
+
clone_from_git(chip, package, path, ref, url, data_path)
|
|
46
|
+
except GitCommandError as e:
|
|
47
|
+
if 'Permission denied' in repr(e):
|
|
48
|
+
if url.scheme in ['ssh', 'git+ssh']:
|
|
49
|
+
chip.logger.error('Failed to authenticate. Please setup your git ssh.')
|
|
50
|
+
elif url.scheme in ['git', 'git+https']:
|
|
51
|
+
chip.logger.error('Failed to authenticate. Please use a token or ssh.')
|
|
52
|
+
else:
|
|
53
|
+
chip.logger.error(str(e))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def clone_from_git(chip, package, path, ref, url, data_path):
|
|
57
|
+
if url.scheme in ['git', 'git+https'] and url.username:
|
|
58
|
+
chip.logger.warning('Your token is in the data source path and will be stored in the '
|
|
59
|
+
'schema. If you do not want this set the env variable GIT_TOKEN '
|
|
60
|
+
'or use ssh for authentication.')
|
|
61
|
+
if url.scheme in ['git+ssh']:
|
|
62
|
+
chip.logger.info(f'Cloning {package} data from {url.netloc}:{url.path[1:]}')
|
|
63
|
+
# Git requires the format git@github.com:org/repo instead of git@github.com/org/repo
|
|
64
|
+
repo = Repo.clone_from(f'{url.netloc}/{url.path[1:]}',
|
|
65
|
+
data_path,
|
|
66
|
+
recurse_submodules=True)
|
|
67
|
+
elif url.scheme in ['ssh']:
|
|
68
|
+
chip.logger.info(f'Cloning {package} data from {path}')
|
|
69
|
+
repo = Repo.clone_from(path,
|
|
70
|
+
data_path,
|
|
71
|
+
recurse_submodules=True)
|
|
72
|
+
else:
|
|
73
|
+
if os.environ.get('GIT_TOKEN') and not url.username:
|
|
74
|
+
url = url._replace(netloc=f'{os.environ.get("GIT_TOKEN")}@{url.hostname}')
|
|
75
|
+
url = url._replace(scheme='https')
|
|
76
|
+
chip.logger.info(f'Cloning {package} data from {url.geturl()}')
|
|
77
|
+
repo = Repo.clone_from(url.geturl(), data_path, recurse_submodules=True)
|
|
78
|
+
chip.logger.info(f'Checking out {ref}')
|
|
79
|
+
repo.git.checkout(ref)
|
|
80
|
+
for submodule in repo.submodules:
|
|
81
|
+
submodule.update(init=True)
|