librelane 2.4.0.dev0__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.
Potentially problematic release.
This version of librelane might be problematic. Click here for more details.
- librelane/__init__.py +38 -0
- librelane/__main__.py +470 -0
- librelane/__version__.py +43 -0
- librelane/common/__init__.py +61 -0
- librelane/common/cli.py +75 -0
- librelane/common/drc.py +245 -0
- librelane/common/generic_dict.py +319 -0
- librelane/common/metrics/__init__.py +35 -0
- librelane/common/metrics/__main__.py +413 -0
- librelane/common/metrics/library.py +354 -0
- librelane/common/metrics/metric.py +186 -0
- librelane/common/metrics/util.py +279 -0
- librelane/common/misc.py +402 -0
- librelane/common/ring_buffer.py +63 -0
- librelane/common/tcl.py +80 -0
- librelane/common/toolbox.py +549 -0
- librelane/common/tpe.py +41 -0
- librelane/common/types.py +117 -0
- librelane/config/__init__.py +32 -0
- librelane/config/__main__.py +158 -0
- librelane/config/config.py +1025 -0
- librelane/config/flow.py +490 -0
- librelane/config/pdk_compat.py +255 -0
- librelane/config/preprocessor.py +464 -0
- librelane/config/removals.py +45 -0
- librelane/config/variable.py +722 -0
- librelane/container.py +264 -0
- librelane/env_info.py +306 -0
- librelane/examples/spm/config.yaml +33 -0
- librelane/examples/spm/pin_order.cfg +14 -0
- librelane/examples/spm/src/impl.sdc +73 -0
- librelane/examples/spm/src/signoff.sdc +68 -0
- librelane/examples/spm/src/spm.v +73 -0
- librelane/examples/spm/verify/spm_tb.v +106 -0
- librelane/examples/spm-user_project_wrapper/SPM_example.v +286 -0
- librelane/examples/spm-user_project_wrapper/base_sdc_file.sdc +145 -0
- librelane/examples/spm-user_project_wrapper/config-tut.json +12 -0
- librelane/examples/spm-user_project_wrapper/config.json +13 -0
- librelane/examples/spm-user_project_wrapper/defines.v +66 -0
- librelane/examples/spm-user_project_wrapper/template.def +7656 -0
- librelane/examples/spm-user_project_wrapper/user_project_wrapper.v +123 -0
- librelane/flows/__init__.py +24 -0
- librelane/flows/builtins.py +18 -0
- librelane/flows/classic.py +330 -0
- librelane/flows/cli.py +463 -0
- librelane/flows/flow.py +985 -0
- librelane/flows/misc.py +71 -0
- librelane/flows/optimizing.py +179 -0
- librelane/flows/sequential.py +367 -0
- librelane/flows/synth_explore.py +173 -0
- librelane/logging/__init__.py +40 -0
- librelane/logging/logger.py +323 -0
- librelane/open_pdks_rev +1 -0
- librelane/plugins.py +21 -0
- librelane/py.typed +0 -0
- librelane/scripts/base.sdc +80 -0
- librelane/scripts/klayout/Readme.md +2 -0
- librelane/scripts/klayout/open_design.py +63 -0
- librelane/scripts/klayout/render.py +121 -0
- librelane/scripts/klayout/stream_out.py +176 -0
- librelane/scripts/klayout/xml_drc_report_to_json.py +45 -0
- librelane/scripts/klayout/xor.drc +120 -0
- librelane/scripts/magic/Readme.md +1 -0
- librelane/scripts/magic/common/read.tcl +114 -0
- librelane/scripts/magic/def/antenna_check.tcl +35 -0
- librelane/scripts/magic/def/mag.tcl +19 -0
- librelane/scripts/magic/def/mag_gds.tcl +81 -0
- librelane/scripts/magic/drc.tcl +79 -0
- librelane/scripts/magic/extract_spice.tcl +98 -0
- librelane/scripts/magic/gds/drc_batch.tcl +74 -0
- librelane/scripts/magic/gds/erase_box.tcl +32 -0
- librelane/scripts/magic/gds/extras_mag.tcl +47 -0
- librelane/scripts/magic/gds/mag_with_pointers.tcl +32 -0
- librelane/scripts/magic/get_bbox.tcl +11 -0
- librelane/scripts/magic/lef/extras_maglef.tcl +63 -0
- librelane/scripts/magic/lef/maglef.tcl +27 -0
- librelane/scripts/magic/lef.tcl +57 -0
- librelane/scripts/magic/open.tcl +28 -0
- librelane/scripts/magic/wrapper.tcl +19 -0
- librelane/scripts/netgen/setup.tcl +28 -0
- librelane/scripts/odbpy/apply_def_template.py +49 -0
- librelane/scripts/odbpy/cell_frequency.py +107 -0
- librelane/scripts/odbpy/check_antenna_properties.py +116 -0
- librelane/scripts/odbpy/contextualize.py +109 -0
- librelane/scripts/odbpy/defutil.py +574 -0
- librelane/scripts/odbpy/diodes.py +373 -0
- librelane/scripts/odbpy/disconnected_pins.py +305 -0
- librelane/scripts/odbpy/exception_codes.py +17 -0
- librelane/scripts/odbpy/filter_unannotated.py +100 -0
- librelane/scripts/odbpy/io_place.py +482 -0
- librelane/scripts/odbpy/label_macro_pins.py +277 -0
- librelane/scripts/odbpy/lefutil.py +97 -0
- librelane/scripts/odbpy/placers.py +162 -0
- librelane/scripts/odbpy/power_utils.py +395 -0
- librelane/scripts/odbpy/random_place.py +57 -0
- librelane/scripts/odbpy/reader.py +246 -0
- librelane/scripts/odbpy/remove_buffers.py +173 -0
- librelane/scripts/odbpy/snap_to_grid.py +57 -0
- librelane/scripts/odbpy/wire_lengths.py +93 -0
- librelane/scripts/openroad/antenna_check.tcl +20 -0
- librelane/scripts/openroad/antenna_repair.tcl +31 -0
- librelane/scripts/openroad/basic_mp.tcl +24 -0
- librelane/scripts/openroad/buffer_list.tcl +10 -0
- librelane/scripts/openroad/common/dpl.tcl +24 -0
- librelane/scripts/openroad/common/dpl_cell_pad.tcl +26 -0
- librelane/scripts/openroad/common/grt.tcl +32 -0
- librelane/scripts/openroad/common/io.tcl +476 -0
- librelane/scripts/openroad/common/pdn_cfg.tcl +135 -0
- librelane/scripts/openroad/common/resizer.tcl +103 -0
- librelane/scripts/openroad/common/set_global_connections.tcl +78 -0
- librelane/scripts/openroad/common/set_layer_adjustments.tcl +31 -0
- librelane/scripts/openroad/common/set_power_nets.tcl +30 -0
- librelane/scripts/openroad/common/set_rc.tcl +75 -0
- librelane/scripts/openroad/common/set_routing_layers.tcl +30 -0
- librelane/scripts/openroad/cts.tcl +80 -0
- librelane/scripts/openroad/cut_rows.tcl +24 -0
- librelane/scripts/openroad/dpl.tcl +24 -0
- librelane/scripts/openroad/drt.tcl +37 -0
- librelane/scripts/openroad/fill.tcl +30 -0
- librelane/scripts/openroad/floorplan.tcl +145 -0
- librelane/scripts/openroad/gpl.tcl +88 -0
- librelane/scripts/openroad/grt.tcl +30 -0
- librelane/scripts/openroad/gui.tcl +15 -0
- librelane/scripts/openroad/insert_buffer.tcl +127 -0
- librelane/scripts/openroad/ioplacer.tcl +67 -0
- librelane/scripts/openroad/irdrop.tcl +51 -0
- librelane/scripts/openroad/pdn.tcl +52 -0
- librelane/scripts/openroad/rcx.tcl +32 -0
- librelane/scripts/openroad/repair_design.tcl +70 -0
- librelane/scripts/openroad/repair_design_postgrt.tcl +48 -0
- librelane/scripts/openroad/rsz_timing_postcts.tcl +68 -0
- librelane/scripts/openroad/rsz_timing_postgrt.tcl +70 -0
- librelane/scripts/openroad/sta/check_macro_instances.tcl +53 -0
- librelane/scripts/openroad/sta/corner.tcl +393 -0
- librelane/scripts/openroad/tapcell.tcl +25 -0
- librelane/scripts/openroad/write_views.tcl +27 -0
- librelane/scripts/pyosys/construct_abc_script.py +177 -0
- librelane/scripts/pyosys/json_header.py +84 -0
- librelane/scripts/pyosys/synthesize.py +493 -0
- librelane/scripts/pyosys/ys_common.py +153 -0
- librelane/scripts/tclsh/hello.tcl +1 -0
- librelane/state/__init__.py +24 -0
- librelane/state/__main__.py +61 -0
- librelane/state/design_format.py +180 -0
- librelane/state/state.py +351 -0
- librelane/steps/__init__.py +61 -0
- librelane/steps/__main__.py +511 -0
- librelane/steps/checker.py +637 -0
- librelane/steps/common_variables.py +340 -0
- librelane/steps/cvc_rv.py +169 -0
- librelane/steps/klayout.py +509 -0
- librelane/steps/magic.py +566 -0
- librelane/steps/misc.py +160 -0
- librelane/steps/netgen.py +253 -0
- librelane/steps/odb.py +955 -0
- librelane/steps/openroad.py +2433 -0
- librelane/steps/openroad_alerts.py +102 -0
- librelane/steps/pyosys.py +629 -0
- librelane/steps/step.py +1547 -0
- librelane/steps/tclstep.py +288 -0
- librelane/steps/verilator.py +222 -0
- librelane/steps/yosys.py +371 -0
- librelane-2.4.0.dev0.dist-info/METADATA +151 -0
- librelane-2.4.0.dev0.dist-info/RECORD +166 -0
- librelane-2.4.0.dev0.dist-info/WHEEL +4 -0
- librelane-2.4.0.dev0.dist-info/entry_points.txt +8 -0
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
# Copyright 2023 Efabless Corporation
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
import os
|
|
15
|
+
import shlex
|
|
16
|
+
import shutil
|
|
17
|
+
import datetime
|
|
18
|
+
import functools
|
|
19
|
+
import subprocess
|
|
20
|
+
from functools import partial
|
|
21
|
+
from typing import IO, Any, Dict, Optional, Sequence, Union
|
|
22
|
+
|
|
23
|
+
from click import pass_context, Context, argument
|
|
24
|
+
from cloup import (
|
|
25
|
+
group,
|
|
26
|
+
option,
|
|
27
|
+
command,
|
|
28
|
+
Path,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
from .step import Step, StepError, StepException
|
|
32
|
+
from ..logging import info, err, warn
|
|
33
|
+
from ..flows import cloup_flow_opts
|
|
34
|
+
from ..__version__ import __version__
|
|
35
|
+
from ..common.cli import formatter_settings
|
|
36
|
+
from ..common import mkdirp, Toolbox, get_librelane_root
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def load_step_from_inputs(
|
|
40
|
+
ctx: Context,
|
|
41
|
+
id: Optional[str],
|
|
42
|
+
config: str,
|
|
43
|
+
state_in: str,
|
|
44
|
+
pdk_root: Optional[str] = None,
|
|
45
|
+
) -> Step:
|
|
46
|
+
Target = Step
|
|
47
|
+
if id is not None:
|
|
48
|
+
if Found := Step.factory.get(id):
|
|
49
|
+
Target = Found
|
|
50
|
+
else:
|
|
51
|
+
err(
|
|
52
|
+
f"No step registered with id '{id}'. Ensure all relevant plugins are installed."
|
|
53
|
+
)
|
|
54
|
+
ctx.exit(-1)
|
|
55
|
+
|
|
56
|
+
return Target.load(
|
|
57
|
+
config=config,
|
|
58
|
+
state_in=state_in,
|
|
59
|
+
pdk_root=pdk_root,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
o = partial(option, show_default=True)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@command(formatter_settings=formatter_settings)
|
|
67
|
+
@o(
|
|
68
|
+
"-o",
|
|
69
|
+
"--output",
|
|
70
|
+
type=Path(
|
|
71
|
+
exists=False,
|
|
72
|
+
file_okay=False,
|
|
73
|
+
dir_okay=True,
|
|
74
|
+
),
|
|
75
|
+
help="The directory to store artifacts from step execution in",
|
|
76
|
+
default=os.path.join(
|
|
77
|
+
os.getcwd(),
|
|
78
|
+
datetime.datetime.now().astimezone().strftime("STEP_RUN_%Y-%m-%d_%H-%M-%S"),
|
|
79
|
+
),
|
|
80
|
+
)
|
|
81
|
+
@o(
|
|
82
|
+
"--id",
|
|
83
|
+
type=str,
|
|
84
|
+
required=False,
|
|
85
|
+
help="The ID for the step. Can be omitted if the configuration object has the ID in the key-path .meta.step.",
|
|
86
|
+
)
|
|
87
|
+
@o(
|
|
88
|
+
"-c",
|
|
89
|
+
"--config",
|
|
90
|
+
type=Path(
|
|
91
|
+
exists=True,
|
|
92
|
+
file_okay=True,
|
|
93
|
+
dir_okay=False,
|
|
94
|
+
),
|
|
95
|
+
required=True,
|
|
96
|
+
help="A step-specific config.json file",
|
|
97
|
+
)
|
|
98
|
+
@o(
|
|
99
|
+
"-i",
|
|
100
|
+
"--state-in",
|
|
101
|
+
type=Path(
|
|
102
|
+
exists=True,
|
|
103
|
+
file_okay=True,
|
|
104
|
+
dir_okay=False,
|
|
105
|
+
),
|
|
106
|
+
required=False,
|
|
107
|
+
)
|
|
108
|
+
@o(
|
|
109
|
+
"--pdk-root",
|
|
110
|
+
type=Path(
|
|
111
|
+
exists=True,
|
|
112
|
+
file_okay=False,
|
|
113
|
+
dir_okay=True,
|
|
114
|
+
),
|
|
115
|
+
is_eager=True,
|
|
116
|
+
default=os.environ.pop("PDK_ROOT", None),
|
|
117
|
+
help="Use this folder as the PDK root, if running a reproducible that doesn't include the PDK.",
|
|
118
|
+
)
|
|
119
|
+
@cloup_flow_opts(
|
|
120
|
+
config_options=False,
|
|
121
|
+
run_options=False,
|
|
122
|
+
sequential_flow_controls=False,
|
|
123
|
+
jobs=False,
|
|
124
|
+
accept_config_files=False,
|
|
125
|
+
)
|
|
126
|
+
@pass_context
|
|
127
|
+
def run(ctx, output, state_in, config, id, pdk_root, pdk, scl):
|
|
128
|
+
"""
|
|
129
|
+
Runs a step using a step-specific configuration object and an input state.
|
|
130
|
+
|
|
131
|
+
Useful for re-running a step that has already run, or running
|
|
132
|
+
filesystem-independent reproducibles.
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
step = load_step_from_inputs(ctx, id, config, state_in, pdk_root)
|
|
136
|
+
|
|
137
|
+
if step.config.meta.librelane_version != __version__:
|
|
138
|
+
warn(
|
|
139
|
+
"LibreLane version being used is different from the version this step was originally run with. Procceed with caution."
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
mkdirp(output)
|
|
143
|
+
toolbox_dir = os.path.join(output, "toolbox_tmp")
|
|
144
|
+
try:
|
|
145
|
+
step.start(
|
|
146
|
+
toolbox=Toolbox(toolbox_dir),
|
|
147
|
+
step_dir=output,
|
|
148
|
+
)
|
|
149
|
+
except StepException as e:
|
|
150
|
+
err("An unexpected error occurred while executing your step:")
|
|
151
|
+
err(e)
|
|
152
|
+
ctx.exit(-1)
|
|
153
|
+
except StepError as e:
|
|
154
|
+
err("An error occurred while executing your step:")
|
|
155
|
+
err(e)
|
|
156
|
+
ctx.exit(-1)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@command(formatter_settings=formatter_settings)
|
|
160
|
+
@o(
|
|
161
|
+
"-o",
|
|
162
|
+
"--output",
|
|
163
|
+
type=Path(
|
|
164
|
+
exists=False,
|
|
165
|
+
file_okay=True,
|
|
166
|
+
dir_okay=False,
|
|
167
|
+
),
|
|
168
|
+
help="Ejected run script",
|
|
169
|
+
default="run.sh",
|
|
170
|
+
)
|
|
171
|
+
@o(
|
|
172
|
+
"--id",
|
|
173
|
+
type=str,
|
|
174
|
+
required=False,
|
|
175
|
+
help="The ID for the step. Can be omitted if the configuration object has the ID in the key-path .meta.step.",
|
|
176
|
+
)
|
|
177
|
+
@o(
|
|
178
|
+
"-c",
|
|
179
|
+
"--config",
|
|
180
|
+
type=Path(
|
|
181
|
+
exists=True,
|
|
182
|
+
file_okay=True,
|
|
183
|
+
dir_okay=False,
|
|
184
|
+
),
|
|
185
|
+
required=True,
|
|
186
|
+
help="A step-specific config.json file",
|
|
187
|
+
)
|
|
188
|
+
@o(
|
|
189
|
+
"-i",
|
|
190
|
+
"--state-in",
|
|
191
|
+
type=Path(
|
|
192
|
+
exists=True,
|
|
193
|
+
file_okay=True,
|
|
194
|
+
dir_okay=False,
|
|
195
|
+
),
|
|
196
|
+
required=False,
|
|
197
|
+
)
|
|
198
|
+
@pass_context
|
|
199
|
+
def eject(ctx, output, state_in, config, id):
|
|
200
|
+
"""
|
|
201
|
+
For steps that rely on underlying utilities using a subprocess, this scripts
|
|
202
|
+
"ejects" LibreLane and just returns a shell script that runs this subprocess.
|
|
203
|
+
|
|
204
|
+
This is useful for:
|
|
205
|
+
|
|
206
|
+
* LibreLane developers and maintainers reporting issues to original tool
|
|
207
|
+
developers
|
|
208
|
+
* Advanced users who are sure that the issue is not LibreLane-specific and
|
|
209
|
+
would like to skip reporting an issue with LibreLane first
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
step = load_step_from_inputs(ctx, id, config, state_in)
|
|
213
|
+
|
|
214
|
+
if step.config.meta.librelane_version != __version__:
|
|
215
|
+
warn(
|
|
216
|
+
"LibreLane version being used is different from the version this step was originally run with. Procceed with caution."
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
toolbox_dir = os.path.join(".", "toolbox_tmp")
|
|
220
|
+
|
|
221
|
+
found_cmd: Optional[Sequence[Union[str, os.PathLike]]] = None
|
|
222
|
+
found_env: Optional[Dict[str, Any]] = None
|
|
223
|
+
found_stdin_data: Optional[Union[str, bytes]] = None
|
|
224
|
+
|
|
225
|
+
class Stop(Exception):
|
|
226
|
+
pass
|
|
227
|
+
|
|
228
|
+
def popen_substitute(
|
|
229
|
+
cmd: Sequence[Union[str, os.PathLike]],
|
|
230
|
+
env: Optional[Dict[str, Any]] = None,
|
|
231
|
+
stdin: Optional[IO[Any]] = None,
|
|
232
|
+
*args,
|
|
233
|
+
**kwargs,
|
|
234
|
+
) -> subprocess.Popen:
|
|
235
|
+
nonlocal found_env, found_cmd, found_stdin_data
|
|
236
|
+
found_cmd = cmd
|
|
237
|
+
found_env = env
|
|
238
|
+
if found_stdin := stdin:
|
|
239
|
+
found_stdin_data = found_stdin.read()
|
|
240
|
+
raise Stop()
|
|
241
|
+
|
|
242
|
+
step.run_subprocess = functools.partial(
|
|
243
|
+
step.run_subprocess,
|
|
244
|
+
_popen_callable=popen_substitute,
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
try:
|
|
248
|
+
step.start(
|
|
249
|
+
toolbox=Toolbox(toolbox_dir),
|
|
250
|
+
step_dir=".",
|
|
251
|
+
)
|
|
252
|
+
except Stop:
|
|
253
|
+
pass
|
|
254
|
+
except Exception as e:
|
|
255
|
+
info("An error occurred while attempting to execute the step:")
|
|
256
|
+
err(e)
|
|
257
|
+
info("This may affect the ejection process.")
|
|
258
|
+
|
|
259
|
+
if found_cmd is None:
|
|
260
|
+
err(
|
|
261
|
+
"Could not eject: The step did not successfully invoke a subprocess using run_subprocess."
|
|
262
|
+
)
|
|
263
|
+
exit(-1)
|
|
264
|
+
|
|
265
|
+
canon_scripts_dir = os.path.join(get_librelane_root(), "scripts")
|
|
266
|
+
target_scripts_dir = os.path.join(".", "scripts")
|
|
267
|
+
|
|
268
|
+
try:
|
|
269
|
+
shutil.rmtree(target_scripts_dir)
|
|
270
|
+
except FileNotFoundError:
|
|
271
|
+
pass
|
|
272
|
+
|
|
273
|
+
shutil.copytree(canon_scripts_dir, target_scripts_dir)
|
|
274
|
+
if chmod := shutil.which("chmod"):
|
|
275
|
+
# Nix's Files aren't writeable
|
|
276
|
+
subprocess.check_call([chmod, "-R", "755", target_scripts_dir])
|
|
277
|
+
|
|
278
|
+
current_env = os.environ
|
|
279
|
+
filtered_env = {
|
|
280
|
+
"STEP_DIR": ".",
|
|
281
|
+
"SCRIPTS_DIR": target_scripts_dir,
|
|
282
|
+
}
|
|
283
|
+
if found_env is not None:
|
|
284
|
+
for key, value in found_env.items():
|
|
285
|
+
if (
|
|
286
|
+
value == current_env.get(key)
|
|
287
|
+
or key in filtered_env
|
|
288
|
+
or key in ["PATH", "PYTHONPATH"]
|
|
289
|
+
):
|
|
290
|
+
continue
|
|
291
|
+
if os.path.isabs(value) and os.path.exists(value):
|
|
292
|
+
if value.startswith(canon_scripts_dir):
|
|
293
|
+
value = value.replace(canon_scripts_dir, target_scripts_dir)
|
|
294
|
+
filtered_env[key] = value
|
|
295
|
+
|
|
296
|
+
cat_in = ""
|
|
297
|
+
if found_stdin_data:
|
|
298
|
+
mode = "wb"
|
|
299
|
+
if isinstance(found_stdin_data, str):
|
|
300
|
+
mode = "w"
|
|
301
|
+
with open("STDIN", mode) as f:
|
|
302
|
+
f.write(found_stdin_data)
|
|
303
|
+
cat_in = "cat STDIN | "
|
|
304
|
+
|
|
305
|
+
found_cmd_filtered = []
|
|
306
|
+
for cmd in found_cmd:
|
|
307
|
+
cmd = str(cmd).replace(canon_scripts_dir, target_scripts_dir)
|
|
308
|
+
found_cmd_filtered.append(cmd)
|
|
309
|
+
|
|
310
|
+
with open(output, "w", encoding="utf8") as f:
|
|
311
|
+
f.write("#!/bin/sh\n")
|
|
312
|
+
for key, value in filtered_env.items():
|
|
313
|
+
f.write(f"export {key}={shlex.quote(str(value))}\n")
|
|
314
|
+
f.write("\n")
|
|
315
|
+
f.write(cat_in)
|
|
316
|
+
f.write(shlex.join([str(e) for e in found_cmd_filtered]))
|
|
317
|
+
f.write("\n")
|
|
318
|
+
|
|
319
|
+
if hasattr(os, "chmod"):
|
|
320
|
+
os.chmod(output, 0o755)
|
|
321
|
+
|
|
322
|
+
info("Ejected successfully.")
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@command(formatter_settings=formatter_settings)
|
|
326
|
+
@o(
|
|
327
|
+
"-o",
|
|
328
|
+
"--output",
|
|
329
|
+
type=Path(
|
|
330
|
+
exists=False,
|
|
331
|
+
file_okay=False,
|
|
332
|
+
dir_okay=True,
|
|
333
|
+
),
|
|
334
|
+
help="The output directory for the reproducible.",
|
|
335
|
+
default="reproducible",
|
|
336
|
+
)
|
|
337
|
+
@o(
|
|
338
|
+
"-d",
|
|
339
|
+
"--step-dir",
|
|
340
|
+
type=Path(
|
|
341
|
+
exists=False,
|
|
342
|
+
file_okay=False,
|
|
343
|
+
dir_okay=True,
|
|
344
|
+
),
|
|
345
|
+
help="The step directory from which to create the reproducible. If provided, --config and the input state can be omitted, and vice versa.",
|
|
346
|
+
default=None,
|
|
347
|
+
)
|
|
348
|
+
@o(
|
|
349
|
+
"--id",
|
|
350
|
+
type=str,
|
|
351
|
+
required=False,
|
|
352
|
+
help="The ID for the step. Can be omitted if the configuration object has the ID in the key-path .meta.step.",
|
|
353
|
+
)
|
|
354
|
+
@o(
|
|
355
|
+
"-c",
|
|
356
|
+
"--config",
|
|
357
|
+
type=Path(
|
|
358
|
+
exists=True,
|
|
359
|
+
file_okay=True,
|
|
360
|
+
dir_okay=False,
|
|
361
|
+
),
|
|
362
|
+
required=False,
|
|
363
|
+
help="A step-specific config.json file",
|
|
364
|
+
)
|
|
365
|
+
@o(
|
|
366
|
+
"-i",
|
|
367
|
+
"--state-in",
|
|
368
|
+
type=Path(
|
|
369
|
+
exists=True,
|
|
370
|
+
file_okay=True,
|
|
371
|
+
dir_okay=False,
|
|
372
|
+
),
|
|
373
|
+
required=False,
|
|
374
|
+
)
|
|
375
|
+
@o(
|
|
376
|
+
"--include-pdk/--no-include-pdk",
|
|
377
|
+
type=bool,
|
|
378
|
+
default=True,
|
|
379
|
+
)
|
|
380
|
+
@o(
|
|
381
|
+
"--flatten/--no-flatten",
|
|
382
|
+
type=bool,
|
|
383
|
+
default=False,
|
|
384
|
+
)
|
|
385
|
+
@pass_context
|
|
386
|
+
@argument(
|
|
387
|
+
"step_dir_arg",
|
|
388
|
+
type=Path(
|
|
389
|
+
exists=False,
|
|
390
|
+
file_okay=False,
|
|
391
|
+
dir_okay=True,
|
|
392
|
+
),
|
|
393
|
+
default=None,
|
|
394
|
+
required=False,
|
|
395
|
+
nargs=1,
|
|
396
|
+
)
|
|
397
|
+
def create_reproducible(
|
|
398
|
+
ctx,
|
|
399
|
+
output,
|
|
400
|
+
step_dir,
|
|
401
|
+
step_dir_arg,
|
|
402
|
+
id,
|
|
403
|
+
config,
|
|
404
|
+
state_in,
|
|
405
|
+
include_pdk,
|
|
406
|
+
flatten,
|
|
407
|
+
):
|
|
408
|
+
"""
|
|
409
|
+
Creates a filesystem-independent step reproducible.
|
|
410
|
+
|
|
411
|
+
The input can be either:
|
|
412
|
+
|
|
413
|
+
* Both a configuration object (--config) and an input state (--state-in)
|
|
414
|
+
|
|
415
|
+
* A step directory (--step-dir) generated from a previous run
|
|
416
|
+
* If not provided, the fallbacks are, in order of priority:
|
|
417
|
+
* The first non-flag argument
|
|
418
|
+
* The current working directory
|
|
419
|
+
|
|
420
|
+
These reproducibles are filesystem-independent, i.e. they can be run
|
|
421
|
+
on any computer that has the appropriate version of LibreLane 2 installed
|
|
422
|
+
(as well as the underlying utility for that specific step.)
|
|
423
|
+
|
|
424
|
+
The reproducible will report an error if LibreLane is not installed and will
|
|
425
|
+
emit a warning if the installed version of LibreLane mismatches the one
|
|
426
|
+
declared in the config file.
|
|
427
|
+
"""
|
|
428
|
+
step_dir = step_dir or step_dir_arg or os.getcwd()
|
|
429
|
+
|
|
430
|
+
if step_dir is None:
|
|
431
|
+
if config is None or state_in is None:
|
|
432
|
+
err("Either --step-dir or both --config and --state-in must be provided.")
|
|
433
|
+
ctx.exit(-1)
|
|
434
|
+
elif None in [config, state_in]:
|
|
435
|
+
err(
|
|
436
|
+
"Both --config and --state-in must be provided if the --step-dir is not provided."
|
|
437
|
+
)
|
|
438
|
+
ctx.exit(-1)
|
|
439
|
+
else:
|
|
440
|
+
if config is None:
|
|
441
|
+
config = os.path.join(step_dir, "config.json")
|
|
442
|
+
if state_in is None:
|
|
443
|
+
state_in = os.path.join(step_dir, "state_in.json")
|
|
444
|
+
|
|
445
|
+
step = load_step_from_inputs(ctx, id, config, state_in)
|
|
446
|
+
step.create_reproducible(output, include_pdk, flatten=flatten)
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
@command(formatter_settings=formatter_settings, hidden=True)
|
|
450
|
+
@o(
|
|
451
|
+
"-d",
|
|
452
|
+
"--step-dir",
|
|
453
|
+
type=Path(
|
|
454
|
+
exists=False,
|
|
455
|
+
file_okay=False,
|
|
456
|
+
dir_okay=True,
|
|
457
|
+
),
|
|
458
|
+
help="The step directory from which to create the test. If provided, --config and the input state can be omitted, and vice versa.",
|
|
459
|
+
default=None,
|
|
460
|
+
)
|
|
461
|
+
@o(
|
|
462
|
+
"--output",
|
|
463
|
+
type=Path(
|
|
464
|
+
exists=False,
|
|
465
|
+
file_okay=False,
|
|
466
|
+
dir_okay=True,
|
|
467
|
+
),
|
|
468
|
+
default=None,
|
|
469
|
+
)
|
|
470
|
+
@argument(
|
|
471
|
+
"step_dir_arg",
|
|
472
|
+
type=Path(
|
|
473
|
+
exists=False,
|
|
474
|
+
file_okay=False,
|
|
475
|
+
dir_okay=True,
|
|
476
|
+
),
|
|
477
|
+
default=None,
|
|
478
|
+
required=False,
|
|
479
|
+
nargs=1,
|
|
480
|
+
)
|
|
481
|
+
@pass_context
|
|
482
|
+
def create_test(ctx, step_dir, step_dir_arg, output):
|
|
483
|
+
step_dir = step_dir or step_dir_arg or os.getcwd()
|
|
484
|
+
config = os.path.join(step_dir, "config.json")
|
|
485
|
+
state_in = os.path.join(step_dir, "state_in.json")
|
|
486
|
+
if output is None:
|
|
487
|
+
output = os.path.join(step_dir, "test")
|
|
488
|
+
|
|
489
|
+
step = load_step_from_inputs(ctx, None, config, state_in)
|
|
490
|
+
step.create_reproducible(output, include_pdk=False, flatten=True)
|
|
491
|
+
os.remove(os.path.join(output, "run_ol.sh"))
|
|
492
|
+
if os.path.exists(os.path.join(output, "base.sdc")):
|
|
493
|
+
os.remove(os.path.join(output, "base.sdc"))
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
@group(formatter_settings=formatter_settings)
|
|
497
|
+
def cli():
|
|
498
|
+
"""
|
|
499
|
+
Try 'python3 -m librelane.steps COMMAND --help' for help with a specific
|
|
500
|
+
command.
|
|
501
|
+
"""
|
|
502
|
+
pass
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
cli.add_command(run)
|
|
506
|
+
cli.add_command(eject)
|
|
507
|
+
cli.add_command(create_reproducible)
|
|
508
|
+
cli.add_command(create_test)
|
|
509
|
+
|
|
510
|
+
if __name__ == "__main__":
|
|
511
|
+
cli()
|