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.

Files changed (166) hide show
  1. librelane/__init__.py +38 -0
  2. librelane/__main__.py +470 -0
  3. librelane/__version__.py +43 -0
  4. librelane/common/__init__.py +61 -0
  5. librelane/common/cli.py +75 -0
  6. librelane/common/drc.py +245 -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 +402 -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 +117 -0
  19. librelane/config/__init__.py +32 -0
  20. librelane/config/__main__.py +158 -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 +722 -0
  27. librelane/container.py +264 -0
  28. librelane/env_info.py +306 -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 +330 -0
  45. librelane/flows/cli.py +463 -0
  46. librelane/flows/flow.py +985 -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/logging/__init__.py +40 -0
  52. librelane/logging/logger.py +323 -0
  53. librelane/open_pdks_rev +1 -0
  54. librelane/plugins.py +21 -0
  55. librelane/py.typed +0 -0
  56. librelane/scripts/base.sdc +80 -0
  57. librelane/scripts/klayout/Readme.md +2 -0
  58. librelane/scripts/klayout/open_design.py +63 -0
  59. librelane/scripts/klayout/render.py +121 -0
  60. librelane/scripts/klayout/stream_out.py +176 -0
  61. librelane/scripts/klayout/xml_drc_report_to_json.py +45 -0
  62. librelane/scripts/klayout/xor.drc +120 -0
  63. librelane/scripts/magic/Readme.md +1 -0
  64. librelane/scripts/magic/common/read.tcl +114 -0
  65. librelane/scripts/magic/def/antenna_check.tcl +35 -0
  66. librelane/scripts/magic/def/mag.tcl +19 -0
  67. librelane/scripts/magic/def/mag_gds.tcl +81 -0
  68. librelane/scripts/magic/drc.tcl +79 -0
  69. librelane/scripts/magic/extract_spice.tcl +98 -0
  70. librelane/scripts/magic/gds/drc_batch.tcl +74 -0
  71. librelane/scripts/magic/gds/erase_box.tcl +32 -0
  72. librelane/scripts/magic/gds/extras_mag.tcl +47 -0
  73. librelane/scripts/magic/gds/mag_with_pointers.tcl +32 -0
  74. librelane/scripts/magic/get_bbox.tcl +11 -0
  75. librelane/scripts/magic/lef/extras_maglef.tcl +63 -0
  76. librelane/scripts/magic/lef/maglef.tcl +27 -0
  77. librelane/scripts/magic/lef.tcl +57 -0
  78. librelane/scripts/magic/open.tcl +28 -0
  79. librelane/scripts/magic/wrapper.tcl +19 -0
  80. librelane/scripts/netgen/setup.tcl +28 -0
  81. librelane/scripts/odbpy/apply_def_template.py +49 -0
  82. librelane/scripts/odbpy/cell_frequency.py +107 -0
  83. librelane/scripts/odbpy/check_antenna_properties.py +116 -0
  84. librelane/scripts/odbpy/contextualize.py +109 -0
  85. librelane/scripts/odbpy/defutil.py +574 -0
  86. librelane/scripts/odbpy/diodes.py +373 -0
  87. librelane/scripts/odbpy/disconnected_pins.py +305 -0
  88. librelane/scripts/odbpy/exception_codes.py +17 -0
  89. librelane/scripts/odbpy/filter_unannotated.py +100 -0
  90. librelane/scripts/odbpy/io_place.py +482 -0
  91. librelane/scripts/odbpy/label_macro_pins.py +277 -0
  92. librelane/scripts/odbpy/lefutil.py +97 -0
  93. librelane/scripts/odbpy/placers.py +162 -0
  94. librelane/scripts/odbpy/power_utils.py +395 -0
  95. librelane/scripts/odbpy/random_place.py +57 -0
  96. librelane/scripts/odbpy/reader.py +246 -0
  97. librelane/scripts/odbpy/remove_buffers.py +173 -0
  98. librelane/scripts/odbpy/snap_to_grid.py +57 -0
  99. librelane/scripts/odbpy/wire_lengths.py +93 -0
  100. librelane/scripts/openroad/antenna_check.tcl +20 -0
  101. librelane/scripts/openroad/antenna_repair.tcl +31 -0
  102. librelane/scripts/openroad/basic_mp.tcl +24 -0
  103. librelane/scripts/openroad/buffer_list.tcl +10 -0
  104. librelane/scripts/openroad/common/dpl.tcl +24 -0
  105. librelane/scripts/openroad/common/dpl_cell_pad.tcl +26 -0
  106. librelane/scripts/openroad/common/grt.tcl +32 -0
  107. librelane/scripts/openroad/common/io.tcl +476 -0
  108. librelane/scripts/openroad/common/pdn_cfg.tcl +135 -0
  109. librelane/scripts/openroad/common/resizer.tcl +103 -0
  110. librelane/scripts/openroad/common/set_global_connections.tcl +78 -0
  111. librelane/scripts/openroad/common/set_layer_adjustments.tcl +31 -0
  112. librelane/scripts/openroad/common/set_power_nets.tcl +30 -0
  113. librelane/scripts/openroad/common/set_rc.tcl +75 -0
  114. librelane/scripts/openroad/common/set_routing_layers.tcl +30 -0
  115. librelane/scripts/openroad/cts.tcl +80 -0
  116. librelane/scripts/openroad/cut_rows.tcl +24 -0
  117. librelane/scripts/openroad/dpl.tcl +24 -0
  118. librelane/scripts/openroad/drt.tcl +37 -0
  119. librelane/scripts/openroad/fill.tcl +30 -0
  120. librelane/scripts/openroad/floorplan.tcl +145 -0
  121. librelane/scripts/openroad/gpl.tcl +88 -0
  122. librelane/scripts/openroad/grt.tcl +30 -0
  123. librelane/scripts/openroad/gui.tcl +15 -0
  124. librelane/scripts/openroad/insert_buffer.tcl +127 -0
  125. librelane/scripts/openroad/ioplacer.tcl +67 -0
  126. librelane/scripts/openroad/irdrop.tcl +51 -0
  127. librelane/scripts/openroad/pdn.tcl +52 -0
  128. librelane/scripts/openroad/rcx.tcl +32 -0
  129. librelane/scripts/openroad/repair_design.tcl +70 -0
  130. librelane/scripts/openroad/repair_design_postgrt.tcl +48 -0
  131. librelane/scripts/openroad/rsz_timing_postcts.tcl +68 -0
  132. librelane/scripts/openroad/rsz_timing_postgrt.tcl +70 -0
  133. librelane/scripts/openroad/sta/check_macro_instances.tcl +53 -0
  134. librelane/scripts/openroad/sta/corner.tcl +393 -0
  135. librelane/scripts/openroad/tapcell.tcl +25 -0
  136. librelane/scripts/openroad/write_views.tcl +27 -0
  137. librelane/scripts/pyosys/construct_abc_script.py +177 -0
  138. librelane/scripts/pyosys/json_header.py +84 -0
  139. librelane/scripts/pyosys/synthesize.py +493 -0
  140. librelane/scripts/pyosys/ys_common.py +153 -0
  141. librelane/scripts/tclsh/hello.tcl +1 -0
  142. librelane/state/__init__.py +24 -0
  143. librelane/state/__main__.py +61 -0
  144. librelane/state/design_format.py +180 -0
  145. librelane/state/state.py +351 -0
  146. librelane/steps/__init__.py +61 -0
  147. librelane/steps/__main__.py +511 -0
  148. librelane/steps/checker.py +637 -0
  149. librelane/steps/common_variables.py +340 -0
  150. librelane/steps/cvc_rv.py +169 -0
  151. librelane/steps/klayout.py +509 -0
  152. librelane/steps/magic.py +566 -0
  153. librelane/steps/misc.py +160 -0
  154. librelane/steps/netgen.py +253 -0
  155. librelane/steps/odb.py +955 -0
  156. librelane/steps/openroad.py +2433 -0
  157. librelane/steps/openroad_alerts.py +102 -0
  158. librelane/steps/pyosys.py +629 -0
  159. librelane/steps/step.py +1547 -0
  160. librelane/steps/tclstep.py +288 -0
  161. librelane/steps/verilator.py +222 -0
  162. librelane/steps/yosys.py +371 -0
  163. librelane-2.4.0.dev0.dist-info/METADATA +151 -0
  164. librelane-2.4.0.dev0.dist-info/RECORD +166 -0
  165. librelane-2.4.0.dev0.dist-info/WHEEL +4 -0
  166. 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()