librelane 2.4.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.

Potentially problematic release.


This version of librelane might be problematic. Click here for more details.

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