librelane 3.0.0.dev29__py3-none-any.whl → 3.0.0.dev33__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/__main__.py +29 -25
- librelane/common/__init__.py +1 -0
- librelane/common/misc.py +33 -1
- librelane/container.py +48 -27
- librelane/env_info.py +129 -115
- librelane/flows/flow.py +9 -13
- librelane/help/__main__.py +39 -0
- librelane/scripts/pyosys/json_header.py +1 -0
- librelane/scripts/pyosys/synthesize.py +2 -0
- librelane/scripts/pyosys/ys_common.py +2 -0
- librelane/steps/openroad.py +1 -1
- librelane/steps/pyosys.py +6 -1
- librelane/steps/tclstep.py +2 -1
- {librelane-3.0.0.dev29.dist-info → librelane-3.0.0.dev33.dist-info}/METADATA +1 -1
- {librelane-3.0.0.dev29.dist-info → librelane-3.0.0.dev33.dist-info}/RECORD +17 -16
- {librelane-3.0.0.dev29.dist-info → librelane-3.0.0.dev33.dist-info}/entry_points.txt +1 -0
- {librelane-3.0.0.dev29.dist-info → librelane-3.0.0.dev33.dist-info}/WHEEL +0 -0
librelane/__main__.py
CHANGED
|
@@ -22,7 +22,6 @@ import shutil
|
|
|
22
22
|
import marshal
|
|
23
23
|
import tempfile
|
|
24
24
|
import traceback
|
|
25
|
-
import subprocess
|
|
26
25
|
from textwrap import dedent
|
|
27
26
|
from functools import partial
|
|
28
27
|
from typing import Any, Dict, Sequence, Tuple, Type, Optional, List
|
|
@@ -275,20 +274,12 @@ def run_included_example(
|
|
|
275
274
|
if os.path.isdir(final_path):
|
|
276
275
|
print(f"A directory named {value} already exists.", file=sys.stderr)
|
|
277
276
|
ctx.exit(1)
|
|
278
|
-
# 1. Copy the files
|
|
279
|
-
shutil.copytree(
|
|
280
|
-
example_path,
|
|
281
|
-
final_path,
|
|
282
|
-
symlinks=False,
|
|
283
|
-
)
|
|
284
|
-
|
|
285
|
-
# 2. Make files writable
|
|
286
|
-
if os.name == "posix":
|
|
287
|
-
subprocess.check_call(["chmod", "-R", "755", final_path])
|
|
288
277
|
|
|
278
|
+
# 1. Copy the files
|
|
279
|
+
common.recreate_tree(example_path, final_path)
|
|
289
280
|
config_file = glob.glob(os.path.join(final_path, "config.*"))[0]
|
|
290
281
|
|
|
291
|
-
#
|
|
282
|
+
# 2. Run
|
|
292
283
|
run(
|
|
293
284
|
ctx,
|
|
294
285
|
config_files=[config_file],
|
|
@@ -318,28 +309,38 @@ def cli_in_container(
|
|
|
318
309
|
if not value:
|
|
319
310
|
return
|
|
320
311
|
|
|
321
|
-
|
|
322
|
-
|
|
312
|
+
mounts = list(ctx.params.get("docker_mounts") or ())
|
|
313
|
+
tty: bool = ctx.params.get("docker_tty", True)
|
|
323
314
|
pdk_root = ctx.params.get("pdk_root")
|
|
324
|
-
|
|
315
|
+
|
|
316
|
+
try:
|
|
317
|
+
containerized_index = sys.argv.index("--dockerized")
|
|
318
|
+
except ValueError:
|
|
319
|
+
containerized_index = sys.argv.index("--containerized")
|
|
320
|
+
|
|
321
|
+
argv = sys.argv[containerized_index + 1 :]
|
|
325
322
|
|
|
326
323
|
final_argv = ["zsh"]
|
|
327
324
|
if len(argv) != 0:
|
|
328
|
-
final_argv = ["librelane"] + argv
|
|
325
|
+
final_argv = ["python3", "-m", "librelane"] + argv
|
|
329
326
|
|
|
330
|
-
|
|
327
|
+
container_image = os.getenv(
|
|
331
328
|
"LIBRELANE_IMAGE_OVERRIDE", f"ghcr.io/librelane/librelane:{__version__}"
|
|
332
329
|
)
|
|
333
330
|
|
|
334
331
|
try:
|
|
335
332
|
run_in_container(
|
|
336
|
-
|
|
333
|
+
container_image,
|
|
337
334
|
final_argv,
|
|
338
335
|
pdk_root=pdk_root,
|
|
339
|
-
other_mounts=
|
|
340
|
-
tty=
|
|
336
|
+
other_mounts=mounts,
|
|
337
|
+
tty=tty,
|
|
341
338
|
)
|
|
339
|
+
except ValueError as e:
|
|
340
|
+
err(e)
|
|
341
|
+
ctx.exit(1)
|
|
342
342
|
except Exception as e:
|
|
343
|
+
traceback.print_exc(file=sys.stderr)
|
|
343
344
|
err(e)
|
|
344
345
|
ctx.exit(1)
|
|
345
346
|
|
|
@@ -374,25 +375,28 @@ o = partial(option, show_default=True)
|
|
|
374
375
|
"Containerization options",
|
|
375
376
|
o(
|
|
376
377
|
"--docker-mount",
|
|
378
|
+
"--container-mount",
|
|
377
379
|
"-m",
|
|
378
380
|
"docker_mounts",
|
|
379
381
|
multiple=True,
|
|
380
|
-
is_eager=True, #
|
|
382
|
+
is_eager=True, # container options should be processed before anything else
|
|
381
383
|
default=(),
|
|
382
|
-
help="Used to mount more directories in dockerized mode. If a valid directory is specified, it will be mounted in the same path in the container. Otherwise, the value of the option will be passed to the
|
|
384
|
+
help="Used to mount more directories in dockerized mode. If a valid directory is specified, it will be mounted in the same path in the container. Otherwise, the value of the option will be passed to the container engine verbatim. Must be passed before --containerized/--dockerized, has no effect if not set.",
|
|
383
385
|
),
|
|
384
386
|
o(
|
|
385
387
|
"--docker-tty/--docker-no-tty",
|
|
386
|
-
|
|
388
|
+
"--container-tty/--container-no-tty",
|
|
389
|
+
is_eager=True, # container options should be processed before anything else
|
|
387
390
|
default=True,
|
|
388
|
-
help="Controls the allocation of a virtual terminal by passing -t to the Docker-compatible container engine invocation. Must be passed before --dockerized, has no effect if
|
|
391
|
+
help="Controls the allocation of a virtual terminal by passing -t to the Docker-compatible container engine invocation. Must be passed before --containerized/--dockerized, has no effect if not set.",
|
|
389
392
|
),
|
|
390
393
|
o(
|
|
391
394
|
"--dockerized",
|
|
395
|
+
"--containerized",
|
|
392
396
|
default=False,
|
|
393
397
|
is_flag=True,
|
|
394
398
|
is_eager=True, # docker options should be processed before anything else
|
|
395
|
-
help="Run the remaining flags using a
|
|
399
|
+
help="Run the remaining flags using a containerized version of LibreLane. Some caveats apply. Must precede all options except --{docker,container}-mount, --{docker,container}-[no-]tty.",
|
|
396
400
|
callback=cli_in_container,
|
|
397
401
|
),
|
|
398
402
|
)
|
librelane/common/__init__.py
CHANGED
librelane/common/misc.py
CHANGED
|
@@ -21,6 +21,7 @@ import re
|
|
|
21
21
|
import glob
|
|
22
22
|
import gzip
|
|
23
23
|
import yaml
|
|
24
|
+
import shutil
|
|
24
25
|
import typing
|
|
25
26
|
import pathlib
|
|
26
27
|
import fnmatch
|
|
@@ -328,6 +329,37 @@ class Filter(object):
|
|
|
328
329
|
yield input
|
|
329
330
|
|
|
330
331
|
|
|
332
|
+
def recreate_tree(
|
|
333
|
+
source: AnyPath,
|
|
334
|
+
target: AnyPath,
|
|
335
|
+
):
|
|
336
|
+
"""
|
|
337
|
+
This function attempts to recreate a file tree from a source path in another
|
|
338
|
+
target path.
|
|
339
|
+
|
|
340
|
+
Permissions are not copied over. Symlinks and hardlinks are followed.
|
|
341
|
+
|
|
342
|
+
Directories are not recreated unless they contain files as (grand)children.
|
|
343
|
+
|
|
344
|
+
If the source and target are the same, the function returns early and does
|
|
345
|
+
nothing.
|
|
346
|
+
|
|
347
|
+
:param source: The source file tree to replicate
|
|
348
|
+
:param target: The target path to recreate the file tree within
|
|
349
|
+
"""
|
|
350
|
+
source = os.path.abspath(source)
|
|
351
|
+
target = os.path.abspath(target)
|
|
352
|
+
if os.path.exists(target) and os.path.samefile(source, target):
|
|
353
|
+
return
|
|
354
|
+
for dirname, _, files in os.walk(source):
|
|
355
|
+
for file in files:
|
|
356
|
+
resolved = os.path.join(dirname, file)
|
|
357
|
+
resolved_target = os.path.join(target, os.path.relpath(resolved, source))
|
|
358
|
+
os.makedirs(os.path.dirname(resolved_target), exist_ok=True)
|
|
359
|
+
with open(resolved, "rb") as fi, open(resolved_target, "wb") as fo:
|
|
360
|
+
shutil.copyfileobj(fi, fo)
|
|
361
|
+
|
|
362
|
+
|
|
331
363
|
def get_latest_file(in_path: Union[str, os.PathLike], filename: str) -> Optional[Path]:
|
|
332
364
|
"""
|
|
333
365
|
:param in_path: A directory to search in
|
|
@@ -394,7 +426,7 @@ def _get_process_limit() -> int:
|
|
|
394
426
|
|
|
395
427
|
def gzopen(filename: AnyPath, mode="rt") -> IO[Any]:
|
|
396
428
|
"""
|
|
397
|
-
This
|
|
429
|
+
This function (tries to?) emulate the gzopen from the Linux Standard Base,
|
|
398
430
|
specifically this part:
|
|
399
431
|
|
|
400
432
|
If path refers to an uncompressed file, and mode refers to a read mode,
|
librelane/container.py
CHANGED
|
@@ -18,7 +18,6 @@ import re
|
|
|
18
18
|
import uuid
|
|
19
19
|
import shlex
|
|
20
20
|
import pathlib
|
|
21
|
-
import tempfile
|
|
22
21
|
import subprocess
|
|
23
22
|
from typing import List, NoReturn, Sequence, Optional, Union, Tuple
|
|
24
23
|
|
|
@@ -27,15 +26,15 @@ import semver
|
|
|
27
26
|
|
|
28
27
|
from .common import mkdirp
|
|
29
28
|
from .logging import err, info, warn
|
|
30
|
-
from .env_info import OSInfo
|
|
29
|
+
from .env_info import ContainerInfo, OSInfo
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
__file_dir__ = os.path.dirname(os.path.abspath(__file__))
|
|
33
32
|
|
|
34
33
|
|
|
35
34
|
def permission_args(osinfo: OSInfo) -> List[str]:
|
|
36
35
|
if (
|
|
37
36
|
osinfo.kernel == "Linux"
|
|
38
|
-
and osinfo.container_info
|
|
37
|
+
and isinstance(osinfo.container_info, ContainerInfo)
|
|
39
38
|
and osinfo.container_info.engine == "docker"
|
|
40
39
|
and not osinfo.container_info.rootless
|
|
41
40
|
):
|
|
@@ -70,9 +69,9 @@ def gui_args(osinfo: OSInfo) -> List[str]:
|
|
|
70
69
|
return args
|
|
71
70
|
|
|
72
71
|
|
|
73
|
-
def image_exists(image: str) -> bool:
|
|
72
|
+
def image_exists(ce_path: str, image: str) -> bool:
|
|
74
73
|
images = (
|
|
75
|
-
subprocess.check_output([
|
|
74
|
+
subprocess.check_output([ce_path, "images", image])
|
|
76
75
|
.decode("utf8")
|
|
77
76
|
.rstrip()
|
|
78
77
|
.split("\n")[1:]
|
|
@@ -116,14 +115,14 @@ def remote_manifest_exists(image: str) -> bool:
|
|
|
116
115
|
return True
|
|
117
116
|
|
|
118
117
|
|
|
119
|
-
def ensure_image(image: str) -> bool:
|
|
120
|
-
if image_exists(image):
|
|
118
|
+
def ensure_image(ce_path: str, image: str) -> bool:
|
|
119
|
+
if image_exists(ce_path, image):
|
|
121
120
|
return True
|
|
122
121
|
|
|
123
122
|
try:
|
|
124
|
-
subprocess.check_call([
|
|
123
|
+
subprocess.check_call([ce_path, "pull", image])
|
|
125
124
|
except subprocess.CalledProcessError:
|
|
126
|
-
err(f"Failed to pull image {image} from the container registries.")
|
|
125
|
+
err(f"Failed to pull image '{image}' from the container registries.")
|
|
127
126
|
return False
|
|
128
127
|
|
|
129
128
|
return True
|
|
@@ -150,6 +149,22 @@ def sanitize_path(path: Union[str, os.PathLike]) -> Tuple[str, str]:
|
|
|
150
149
|
return (abspath, mountable_path)
|
|
151
150
|
|
|
152
151
|
|
|
152
|
+
def container_version_error(input: str, against: str) -> Optional[str]:
|
|
153
|
+
if input == "UNKNOWN":
|
|
154
|
+
return (
|
|
155
|
+
"Could not determine version for %s. You may encounter unexpected issues."
|
|
156
|
+
)
|
|
157
|
+
if semver.compare(input, against) < 0:
|
|
158
|
+
return f"Your %s version ({input}) is out of date. You may encounter unexpected issues."
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def ubuntu_version_at_least(current: str, minimum: str) -> bool:
|
|
163
|
+
if current == "UNKNOWN":
|
|
164
|
+
return False
|
|
165
|
+
return tuple(map(int, current.split("."))) >= tuple(map(int, minimum.split(".")))
|
|
166
|
+
|
|
167
|
+
|
|
153
168
|
def run_in_container(
|
|
154
169
|
image: str,
|
|
155
170
|
args: Sequence[str],
|
|
@@ -169,20 +184,31 @@ def run_in_container(
|
|
|
169
184
|
f"Unsupported host operating system '{osinfo.kernel}'. You may encounter unexpected issues."
|
|
170
185
|
)
|
|
171
186
|
|
|
172
|
-
if osinfo.container_info
|
|
187
|
+
if not isinstance(osinfo.container_info, ContainerInfo):
|
|
173
188
|
raise FileNotFoundError("No compatible container engine found.")
|
|
174
189
|
|
|
175
|
-
|
|
176
|
-
|
|
190
|
+
ce_path = osinfo.container_info.path
|
|
191
|
+
assert ce_path is not None
|
|
192
|
+
|
|
193
|
+
engine_name = osinfo.container_info.engine.lower()
|
|
194
|
+
if engine_name == "docker":
|
|
195
|
+
if error := container_version_error(osinfo.container_info.version, "25.0.5"):
|
|
196
|
+
warn(error % engine_name)
|
|
197
|
+
elif engine_name == "podman":
|
|
198
|
+
if osinfo.distro.lower() == "ubuntu" and not ubuntu_version_at_least(
|
|
199
|
+
osinfo.distro_version, "24.04"
|
|
200
|
+
):
|
|
177
201
|
warn(
|
|
178
|
-
|
|
202
|
+
"Versions of Podman for Ubuntu before Ubuntu 24.04 are generally pretty buggy. We recommend using Docker instead if possible."
|
|
179
203
|
)
|
|
204
|
+
elif error := container_version_error(osinfo.container_info.version, "4.1.0"):
|
|
205
|
+
warn(error % engine_name)
|
|
180
206
|
else:
|
|
181
207
|
warn(
|
|
182
|
-
f"Unsupported container engine '{osinfo.container_info.
|
|
208
|
+
f"Unsupported container engine referenced by '{osinfo.container_info.path}'. You may encounter unexpected issues."
|
|
183
209
|
)
|
|
184
210
|
|
|
185
|
-
if not ensure_image(image):
|
|
211
|
+
if not ensure_image(ce_path, image):
|
|
186
212
|
raise ValueError(f"Failed to use image '{image}'.")
|
|
187
213
|
|
|
188
214
|
terminal_args = ["-i"]
|
|
@@ -222,15 +248,6 @@ def run_in_container(
|
|
|
222
248
|
mount_args += ["-v", f"{from_cwd}:{to_cwd}"]
|
|
223
249
|
mount_args += ["-w", to_cwd]
|
|
224
250
|
|
|
225
|
-
tempdir = tempfile.mkdtemp("librelane_docker")
|
|
226
|
-
|
|
227
|
-
mount_args += [
|
|
228
|
-
"-v",
|
|
229
|
-
f"{tempdir}:/tmp",
|
|
230
|
-
"-e",
|
|
231
|
-
"TMPDIR=/tmp",
|
|
232
|
-
]
|
|
233
|
-
|
|
234
251
|
if other_mounts is not None:
|
|
235
252
|
for mount in other_mounts:
|
|
236
253
|
if os.path.isdir(mount):
|
|
@@ -242,9 +259,13 @@ def run_in_container(
|
|
|
242
259
|
|
|
243
260
|
container_id = str(uuid.uuid4())
|
|
244
261
|
|
|
262
|
+
if os.getenv("_MOUNT_HOST_LIBRELANE") == "1":
|
|
263
|
+
host_librelane_pythonpath = os.path.dirname(__file_dir__)
|
|
264
|
+
mount_args += ["-v", f"{host_librelane_pythonpath}:/host_librelane"]
|
|
265
|
+
|
|
245
266
|
cmd = (
|
|
246
267
|
[
|
|
247
|
-
|
|
268
|
+
ce_path,
|
|
248
269
|
"run",
|
|
249
270
|
"--rm",
|
|
250
271
|
"--name",
|
|
@@ -261,4 +282,4 @@ def run_in_container(
|
|
|
261
282
|
info("Running containerized command:")
|
|
262
283
|
print(shlex.join(cmd))
|
|
263
284
|
|
|
264
|
-
os.execlp(
|
|
285
|
+
os.execlp(ce_path, *cmd)
|
librelane/env_info.py
CHANGED
|
@@ -23,17 +23,16 @@ import os
|
|
|
23
23
|
import re
|
|
24
24
|
import sys
|
|
25
25
|
import json
|
|
26
|
+
import shutil
|
|
26
27
|
import tempfile
|
|
27
28
|
import platform
|
|
28
29
|
import subprocess
|
|
29
30
|
|
|
30
31
|
try:
|
|
31
|
-
from typing import Optional, Dict, List # noqa: F401
|
|
32
|
+
from typing import Union, Optional, Dict, List # noqa: F401
|
|
32
33
|
except ImportError:
|
|
33
34
|
pass
|
|
34
35
|
|
|
35
|
-
CONTAINER_ENGINE = os.getenv("OPENLANE_CONTAINER_ENGINE", "docker")
|
|
36
|
-
|
|
37
36
|
|
|
38
37
|
class StringRepresentable(object):
|
|
39
38
|
def __str__(self):
|
|
@@ -44,6 +43,7 @@ class StringRepresentable(object):
|
|
|
44
43
|
|
|
45
44
|
|
|
46
45
|
class ContainerInfo(StringRepresentable):
|
|
46
|
+
path = None # type: Optional[str]
|
|
47
47
|
engine = "UNKNOWN" # type: str
|
|
48
48
|
version = "UNKNOWN" # type: str
|
|
49
49
|
conmon = False # type: bool
|
|
@@ -54,63 +54,84 @@ class ContainerInfo(StringRepresentable):
|
|
|
54
54
|
self.version = "UNKNOWN"
|
|
55
55
|
self.conmon = False
|
|
56
56
|
self.rootless = False
|
|
57
|
+
self.seccomp = False
|
|
58
|
+
self.selinux = False
|
|
59
|
+
self.apparmor = False
|
|
57
60
|
|
|
58
61
|
@staticmethod
|
|
59
62
|
def get():
|
|
60
|
-
# type: () ->
|
|
63
|
+
# type: () -> Union[ContainerInfo, str]
|
|
64
|
+
cinfo = ContainerInfo()
|
|
65
|
+
# Here are the rules:
|
|
66
|
+
# 1. If LIBRELANE_CONTAINER_ENGINE exists, use that uncritically.
|
|
67
|
+
# 2. Else, if OPENLANE_CONTAINER_ENGINE exists, use that uncritically.
|
|
68
|
+
# 3. Else, if "docker" is in PATH, always use it.
|
|
69
|
+
# 4. Else, see if "podman" is in PATH, and use THAT.
|
|
70
|
+
# 5. If none exist, halt and return early.
|
|
71
|
+
|
|
72
|
+
container_engine = os.getenv(
|
|
73
|
+
"LIBRELANE_CONTAINER_ENGINE", os.getenv("OPENLANE_CONTAINER_ENGINE")
|
|
74
|
+
)
|
|
75
|
+
if container_engine is None or container_engine == "":
|
|
76
|
+
container_engine = shutil.which("docker")
|
|
77
|
+
if container_engine is None:
|
|
78
|
+
container_engine = shutil.which("podman")
|
|
79
|
+
if container_engine is None:
|
|
80
|
+
return "no compatible container engine found in PATH (tried docker, podman)"
|
|
61
81
|
try:
|
|
62
|
-
|
|
82
|
+
info_str = subprocess.check_output(
|
|
83
|
+
[container_engine, "info", "--format", "{{json .}}"]
|
|
84
|
+
).decode("utf8")
|
|
85
|
+
except Exception as e:
|
|
86
|
+
return "failed to get container engine info: %s" % str(e)
|
|
87
|
+
cinfo.path = container_engine
|
|
63
88
|
|
|
89
|
+
try:
|
|
90
|
+
info = json.loads(info_str)
|
|
91
|
+
except Exception as e:
|
|
92
|
+
return "result from '%s info' was not valid JSON: %s" % (
|
|
93
|
+
container_engine,
|
|
94
|
+
str(e),
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if (
|
|
98
|
+
info.get("Docker Root Dir") is not None
|
|
99
|
+
or info.get("DockerRootDir") is not None
|
|
100
|
+
):
|
|
101
|
+
cinfo.engine = "docker"
|
|
102
|
+
|
|
103
|
+
# Get Version
|
|
64
104
|
try:
|
|
65
|
-
|
|
66
|
-
[
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
105
|
+
version_output = (
|
|
106
|
+
subprocess.check_output([container_engine, "--version"])
|
|
107
|
+
.decode("utf8")
|
|
108
|
+
.strip()
|
|
109
|
+
)
|
|
110
|
+
cinfo.version = re.split(r"\s", version_output)[2].strip(",")
|
|
111
|
+
except Exception:
|
|
112
|
+
pass
|
|
70
113
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
cinfo.engine = "docker"
|
|
93
|
-
|
|
94
|
-
# Get Version
|
|
95
|
-
try:
|
|
96
|
-
version_output = (
|
|
97
|
-
subprocess.check_output([CONTAINER_ENGINE, "--version"])
|
|
98
|
-
.decode("utf8")
|
|
99
|
-
.strip()
|
|
100
|
-
)
|
|
101
|
-
cinfo.version = re.split(r"\s", version_output)[2].strip(",")
|
|
102
|
-
except Exception:
|
|
103
|
-
print("Could not extract Docker version.", file=sys.stderr)
|
|
104
|
-
|
|
105
|
-
security_options = info.get("SecurityOptions")
|
|
106
|
-
for option in security_options:
|
|
107
|
-
if "rootless" in option:
|
|
108
|
-
cinfo.rootless = True
|
|
109
|
-
|
|
110
|
-
return cinfo
|
|
111
|
-
except Exception as e:
|
|
112
|
-
print(e, file=sys.stderr)
|
|
113
|
-
return None
|
|
114
|
+
security_options = info.get("SecurityOptions")
|
|
115
|
+
for option in security_options:
|
|
116
|
+
if "rootless" in option:
|
|
117
|
+
cinfo.rootless = True
|
|
118
|
+
elif info.get("host") is not None:
|
|
119
|
+
host = info["host"]
|
|
120
|
+
conmon = host.get("conmon")
|
|
121
|
+
remote_socket = host.get("remoteSocket")
|
|
122
|
+
security = host.get("security")
|
|
123
|
+
if conmon is not None:
|
|
124
|
+
cinfo.conmon = True
|
|
125
|
+
if remote_socket is not None and "podman" in remote_socket["path"]:
|
|
126
|
+
cinfo.engine = "podman"
|
|
127
|
+
cinfo.version = info["version"]["Version"]
|
|
128
|
+
if security is not None:
|
|
129
|
+
cinfo.rootless = security.get("rootless", False)
|
|
130
|
+
cinfo.apparmor = security.get("apparmorEnabled", False)
|
|
131
|
+
cinfo.seccomp = security.get("seccompEnabled", False)
|
|
132
|
+
cinfo.selinux = security.get("selinuxEnabled", False)
|
|
133
|
+
|
|
134
|
+
return cinfo
|
|
114
135
|
|
|
115
136
|
|
|
116
137
|
class NixInfo(StringRepresentable):
|
|
@@ -127,73 +148,65 @@ class NixInfo(StringRepresentable):
|
|
|
127
148
|
|
|
128
149
|
@staticmethod
|
|
129
150
|
def get():
|
|
130
|
-
# type: () ->
|
|
151
|
+
# type: () -> Union[NixInfo, str]
|
|
131
152
|
ninfo = NixInfo()
|
|
153
|
+
if shutil.which("nix") is None:
|
|
154
|
+
return "nix not found in PATH"
|
|
132
155
|
try:
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
ninfo.version_string = version_str.strip()
|
|
138
|
-
except Exception as e:
|
|
139
|
-
raise Exception("Failed to get Nix info: %s" % str(e)) from None
|
|
156
|
+
version_str = subprocess.check_output(["nix", "--version"], encoding="utf8")
|
|
157
|
+
ninfo.version_string = version_str.strip()
|
|
158
|
+
except Exception as e:
|
|
159
|
+
return "could not get nix version: %s" % str(e)
|
|
140
160
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
161
|
+
try:
|
|
162
|
+
channels = {}
|
|
163
|
+
channels_raw = subprocess.check_output(
|
|
164
|
+
["nix-channel", "--list"], encoding="utf8"
|
|
165
|
+
)
|
|
166
|
+
for channel in channels_raw.splitlines():
|
|
167
|
+
name, url = channel.split(maxsplit=1)
|
|
168
|
+
channels[name] = url
|
|
169
|
+
ninfo.channels = channels
|
|
170
|
+
except Exception:
|
|
171
|
+
pass
|
|
172
|
+
|
|
173
|
+
with tempfile.TemporaryDirectory(prefix="librelane_env_report_") as d:
|
|
174
|
+
with open(os.path.join(d, "flake.nix"), "w") as f:
|
|
175
|
+
f.write("{}")
|
|
176
|
+
nix_command = subprocess.run(
|
|
177
|
+
["nix", "eval"],
|
|
178
|
+
stdout=subprocess.PIPE,
|
|
179
|
+
stderr=subprocess.STDOUT,
|
|
180
|
+
cwd=d,
|
|
181
|
+
encoding="utf8",
|
|
182
|
+
)
|
|
183
|
+
nix_command_result = nix_command.stdout
|
|
184
|
+
if "'nix-command'" in nix_command_result:
|
|
185
|
+
pass
|
|
186
|
+
elif "'flakes'" in nix_command_result:
|
|
187
|
+
ninfo.nix_command = True
|
|
188
|
+
elif "lacks attribute" in nix_command_result:
|
|
189
|
+
ninfo.nix_command = True
|
|
190
|
+
ninfo.flakes = True
|
|
191
|
+
else:
|
|
151
192
|
print(
|
|
152
|
-
"
|
|
193
|
+
"'nix flake' returned unexpected output: %s" % nix_command_result,
|
|
153
194
|
file=sys.stderr,
|
|
154
195
|
)
|
|
155
196
|
|
|
156
|
-
|
|
157
|
-
with open(os.path.join(d, "flake.nix"), "w") as f:
|
|
158
|
-
f.write("{}")
|
|
159
|
-
nix_command = subprocess.run(
|
|
160
|
-
["nix", "eval"],
|
|
161
|
-
stdout=subprocess.PIPE,
|
|
162
|
-
stderr=subprocess.STDOUT,
|
|
163
|
-
cwd=d,
|
|
164
|
-
encoding="utf8",
|
|
165
|
-
)
|
|
166
|
-
nix_command_result = nix_command.stdout
|
|
167
|
-
if "'nix-command'" in nix_command_result:
|
|
168
|
-
pass
|
|
169
|
-
elif "'flakes'" in nix_command_result:
|
|
170
|
-
ninfo.nix_command = True
|
|
171
|
-
elif "lacks attribute" in nix_command_result:
|
|
172
|
-
ninfo.nix_command = True
|
|
173
|
-
ninfo.flakes = True
|
|
174
|
-
else:
|
|
175
|
-
print(
|
|
176
|
-
"'nix flake' returned unexpected output: %s"
|
|
177
|
-
% nix_command_result,
|
|
178
|
-
file=sys.stderr,
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
return ninfo
|
|
182
|
-
except Exception as e:
|
|
183
|
-
print(e, file=sys.stderr)
|
|
184
|
-
return None
|
|
197
|
+
return ninfo
|
|
185
198
|
|
|
186
199
|
|
|
187
200
|
class OSInfo(StringRepresentable):
|
|
188
201
|
kernel = "" # type: str
|
|
189
202
|
kernel_version = "" # type: str
|
|
190
203
|
supported = False # type: bool
|
|
191
|
-
distro =
|
|
192
|
-
distro_version =
|
|
204
|
+
distro = "UNKNOWN" # type: str
|
|
205
|
+
distro_version = "UNKNOWN" # type: str
|
|
193
206
|
python_version = "" # type: str
|
|
194
207
|
python_path = [] # type: List[str]
|
|
195
|
-
container_info = None # type:
|
|
196
|
-
nix_info = None # type:
|
|
208
|
+
container_info = None # type: Union[ContainerInfo, str]
|
|
209
|
+
nix_info = None # type: Union[NixInfo, str]
|
|
197
210
|
|
|
198
211
|
def __init__(self):
|
|
199
212
|
self.kernel = platform.system()
|
|
@@ -201,8 +214,8 @@ class OSInfo(StringRepresentable):
|
|
|
201
214
|
platform.release()
|
|
202
215
|
) # Unintuitively enough, it's the kernel's release
|
|
203
216
|
self.supported = self.kernel in ["Darwin", "Linux"]
|
|
204
|
-
self.distro =
|
|
205
|
-
self.distro_version =
|
|
217
|
+
self.distro = "UNKNOWN"
|
|
218
|
+
self.distro_version = "UNKNOWN"
|
|
206
219
|
self.python_version = platform.python_version()
|
|
207
220
|
self.python_path = sys.path.copy()
|
|
208
221
|
self.tkinter = False
|
|
@@ -212,8 +225,8 @@ class OSInfo(StringRepresentable):
|
|
|
212
225
|
self.tkinter = True
|
|
213
226
|
except ImportError:
|
|
214
227
|
pass
|
|
215
|
-
self.container_info =
|
|
216
|
-
self.nix_info =
|
|
228
|
+
self.container_info = ""
|
|
229
|
+
self.nix_info = ""
|
|
217
230
|
|
|
218
231
|
@staticmethod
|
|
219
232
|
def get():
|
|
@@ -253,13 +266,14 @@ class OSInfo(StringRepresentable):
|
|
|
253
266
|
|
|
254
267
|
config[key] = value
|
|
255
268
|
|
|
256
|
-
osinfo.distro =
|
|
257
|
-
|
|
258
|
-
|
|
269
|
+
osinfo.distro = (
|
|
270
|
+
config.get("ID") or config.get("DISTRIB_ID") or "UNKNOWN"
|
|
271
|
+
)
|
|
272
|
+
osinfo.distro_version = (
|
|
273
|
+
config.get("VERSION_ID")
|
|
274
|
+
or config.get("DISTRIB_RELEASE")
|
|
275
|
+
or "UNKNOWN"
|
|
259
276
|
)
|
|
260
|
-
|
|
261
|
-
else:
|
|
262
|
-
print("Failed to get distribution info.", file=sys.stderr)
|
|
263
277
|
|
|
264
278
|
osinfo.container_info = ContainerInfo.get()
|
|
265
279
|
osinfo.nix_info = NixInfo.get()
|
librelane/flows/flow.py
CHANGED
|
@@ -375,7 +375,7 @@ class Flow(ABC):
|
|
|
375
375
|
self.progress_bar = FlowProgressBar(self.name)
|
|
376
376
|
|
|
377
377
|
@classmethod
|
|
378
|
-
def get_help_md(Self, myst_anchors: bool =
|
|
378
|
+
def get_help_md(Self, myst_anchors: bool = False) -> str: # pragma: no cover
|
|
379
379
|
"""
|
|
380
380
|
:returns: rendered Markdown help for this Flow
|
|
381
381
|
"""
|
|
@@ -415,10 +415,10 @@ class Flow(ABC):
|
|
|
415
415
|
flow_config_vars = Self.config_vars
|
|
416
416
|
|
|
417
417
|
if len(flow_config_vars):
|
|
418
|
+
config_var_anchors = f"({slugify(Self.__name__, lower=True)}-config-vars)="
|
|
418
419
|
result += textwrap.dedent(
|
|
419
420
|
f"""
|
|
420
|
-
|
|
421
|
-
|
|
421
|
+
{config_var_anchors * myst_anchors}
|
|
422
422
|
#### Flow-specific Configuration Variables
|
|
423
423
|
|
|
424
424
|
| Variable Name | Type | Description | Default | Units |
|
|
@@ -435,18 +435,14 @@ class Flow(ABC):
|
|
|
435
435
|
if len(Self.Steps):
|
|
436
436
|
result += "#### Included Steps\n"
|
|
437
437
|
for step in Self.Steps:
|
|
438
|
-
|
|
439
|
-
name = step.long_name
|
|
440
|
-
elif hasattr(step, "name"):
|
|
441
|
-
name = step.name
|
|
442
|
-
else:
|
|
443
|
-
name = step.id
|
|
438
|
+
imp_id = step.get_implementation_id()
|
|
444
439
|
if myst_anchors:
|
|
445
|
-
result += (
|
|
446
|
-
f"* [`{step.id}`](./step_config_vars.md#{slugify(name)})\n"
|
|
447
|
-
)
|
|
440
|
+
result += f"* [`{step.id}`](./step_config_vars.md#step-{slugify(imp_id, lower=True)})\n"
|
|
448
441
|
else:
|
|
449
|
-
|
|
442
|
+
variant_str = ""
|
|
443
|
+
if imp_id != step.id:
|
|
444
|
+
variant_str = f" (implementation: `{imp_id}`)"
|
|
445
|
+
result += f"* `{step.id}`{variant_str}\n"
|
|
450
446
|
|
|
451
447
|
return result
|
|
452
448
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Copyright 2025 LibreLane Contributors
|
|
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
|
+
from ..common.cli import formatter_settings
|
|
15
|
+
from ..flows import Flow
|
|
16
|
+
from ..steps import Step
|
|
17
|
+
from ..logging import console
|
|
18
|
+
|
|
19
|
+
import cloup
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@cloup.command(formatter_settings=formatter_settings)
|
|
23
|
+
@cloup.argument("step_or_flow")
|
|
24
|
+
@cloup.pass_context
|
|
25
|
+
def cli(ctx, step_or_flow):
|
|
26
|
+
"""
|
|
27
|
+
Displays rich help for the step or flow in question.
|
|
28
|
+
"""
|
|
29
|
+
if TargetFlow := Flow.factory.get(step_or_flow):
|
|
30
|
+
TargetFlow.display_help()
|
|
31
|
+
elif TargetStep := Step.factory.get(step_or_flow):
|
|
32
|
+
TargetStep.display_help()
|
|
33
|
+
else:
|
|
34
|
+
console.log(f"Unknown Flow or Step '{step_or_flow}'.")
|
|
35
|
+
ctx.exit(-1)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if __name__ == "__main__":
|
|
39
|
+
cli()
|
|
@@ -263,6 +263,7 @@ def synthesize(
|
|
|
263
263
|
includes=includes,
|
|
264
264
|
defines=defines,
|
|
265
265
|
use_slang=False,
|
|
266
|
+
slang_arguments=[],
|
|
266
267
|
)
|
|
267
268
|
elif verilog_files := config.get("VERILOG_FILES"):
|
|
268
269
|
d.read_verilog_files(
|
|
@@ -272,6 +273,7 @@ def synthesize(
|
|
|
272
273
|
includes=includes,
|
|
273
274
|
defines=defines,
|
|
274
275
|
use_slang=config["USE_SLANG"],
|
|
276
|
+
slang_arguments=config["SLANG_ARGUMENTS"] or [],
|
|
275
277
|
)
|
|
276
278
|
elif vhdl_files := config.get("VHDL_FILES"):
|
|
277
279
|
d.run_pass("plugin", "-i", "ghdl")
|
|
@@ -51,6 +51,7 @@ def _Design_read_verilog_files(
|
|
|
51
51
|
includes: Iterable[str],
|
|
52
52
|
defines: Iterable[str],
|
|
53
53
|
use_slang: bool = False,
|
|
54
|
+
slang_arguments: Iterable[str],
|
|
54
55
|
):
|
|
55
56
|
files = list(files) # for easier concatenation
|
|
56
57
|
include_args = [f"-I{dir}" for dir in includes]
|
|
@@ -72,6 +73,7 @@ def _Design_read_verilog_files(
|
|
|
72
73
|
*define_args,
|
|
73
74
|
*include_args,
|
|
74
75
|
*slang_chparam_args,
|
|
76
|
+
*slang_arguments,
|
|
75
77
|
*files,
|
|
76
78
|
)
|
|
77
79
|
else:
|
librelane/steps/openroad.py
CHANGED
librelane/steps/pyosys.py
CHANGED
|
@@ -128,6 +128,11 @@ verilog_rtl_cfg_vars = [
|
|
|
128
128
|
default=False,
|
|
129
129
|
deprecated_names=["USE_SYNLIG"],
|
|
130
130
|
),
|
|
131
|
+
Variable(
|
|
132
|
+
"SLANG_ARGUMENTS",
|
|
133
|
+
Optional[List[str]],
|
|
134
|
+
"Pass arguments to the Slang frontend.",
|
|
135
|
+
),
|
|
131
136
|
]
|
|
132
137
|
|
|
133
138
|
DesignFormat(
|
|
@@ -439,7 +444,7 @@ class SynthesisCommon(VerilogStep):
|
|
|
439
444
|
Variable(
|
|
440
445
|
"SYNTH_HIERARCHY_MODE",
|
|
441
446
|
Literal["flatten", "deferred_flatten", "keep"],
|
|
442
|
-
"Affects how hierarchy is maintained throughout and after synthesis. 'flatten' flattens it during and after synthesis. 'deferred_flatten' flattens it after synthesis. 'keep' never flattens it.",
|
|
447
|
+
"Affects how hierarchy is maintained throughout and after synthesis. 'flatten' flattens it during and after synthesis. 'deferred_flatten' flattens it after synthesis. 'keep' never flattens it. Please note that when using the Slang plugin, you need to pass '--keep-hierarchy' to `SLANG_ARGUMENTS` separately.",
|
|
443
448
|
default="flatten",
|
|
444
449
|
deprecated_names=[
|
|
445
450
|
(
|
librelane/steps/tclstep.py
CHANGED
|
@@ -161,7 +161,8 @@ class TclStep(Step):
|
|
|
161
161
|
|
|
162
162
|
for input in self.inputs:
|
|
163
163
|
key = f"CURRENT_{input.id.upper()}"
|
|
164
|
-
|
|
164
|
+
if input_path := state.get_by_df(input):
|
|
165
|
+
env[key] = TclStep.value_to_tcl(input_path)
|
|
165
166
|
|
|
166
167
|
for output in self.outputs:
|
|
167
168
|
if output.multiple:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
librelane/__init__.py,sha256=EMpoZrRmS_wsweKjhyAg52OXCK7HWQ8o8CVrYaX4ub0,1220
|
|
2
|
-
librelane/__main__.py,sha256
|
|
2
|
+
librelane/__main__.py,sha256=-yJXvww0nKyt-jcOl_QLnZskKZDL_2zScEgjSZ-7ANE,14702
|
|
3
3
|
librelane/__version__.py,sha256=dbE4stCACDmIoxgKksesAkTa-_hi5dW6nPLWw9Pfq3Q,1486
|
|
4
|
-
librelane/common/__init__.py,sha256=
|
|
4
|
+
librelane/common/__init__.py,sha256=fK52kxuueEpKI1HNNhUOJFQ4yvxJs0f0Vz1a5S-CBGY,1569
|
|
5
5
|
librelane/common/cli.py,sha256=xi48GBGHRsYrLGwx40ARwpykHx7GnuHbJjHxjOwtZ5Y,2349
|
|
6
6
|
librelane/common/drc.py,sha256=l1quZbHXGb7yjKCO5IFn-Xxf_zIx4f6kxqpNm3YmpOs,12809
|
|
7
7
|
librelane/common/generic_dict.py,sha256=ASa5wtVcLuGlsBqGfLxLYXrYksqQB62iHljV04plIqI,10010
|
|
@@ -10,7 +10,7 @@ librelane/common/metrics/__main__.py,sha256=1w23V_1-f0WUSfxG36ewNHctgPisQFFG4p-R
|
|
|
10
10
|
librelane/common/metrics/library.py,sha256=CG7rubLdjuCQL9-9bzAC-64hf-KlH-iu_Fg0oKuesqs,7373
|
|
11
11
|
librelane/common/metrics/metric.py,sha256=h3Xd26z5M80IJgVmmrBTjKcdGLb4I0wyjM-H4jdyi_0,6990
|
|
12
12
|
librelane/common/metrics/util.py,sha256=Bl_9znlot7-Os2VigYLSmMf56aAkGdv3evWz9vfK7K4,9344
|
|
13
|
-
librelane/common/misc.py,sha256=
|
|
13
|
+
librelane/common/misc.py,sha256=_xoU3lnA9Odf1PidjCJ4RDiGZQ3nd3YNGyooHu4RHe4,14757
|
|
14
14
|
librelane/common/ring_buffer.py,sha256=DGFen9y0JOmiL7E27tmzDTVSJKZtV-waF9hl5Rz9uek,1898
|
|
15
15
|
librelane/common/tcl.py,sha256=AfTxbSLA0VUXVMMwoAQndyQTcEZQoQfMa4FFizZiEgU,4341
|
|
16
16
|
librelane/common/toolbox.py,sha256=ijR__rVqQ_nJtfm34H-VdSCIeArKns7lVAc1TcTUSsQ,20975
|
|
@@ -24,8 +24,8 @@ librelane/config/pdk_compat.py,sha256=ofqYuD-MgTcfvPVXpGJo8H1GKzCvN6sxHsK_OqCVXa
|
|
|
24
24
|
librelane/config/preprocessor.py,sha256=ATi29SHz0_OBq1IqUkGxvhHUDKB5z5jO0KqvoQXg8R8,14913
|
|
25
25
|
librelane/config/removals.py,sha256=vxqTuRTJ0jt2TX4KmFZCZPTwghDFkCVjIhF2iReHwJA,2958
|
|
26
26
|
librelane/config/variable.py,sha256=YKRlnQu6YvkwnJ5zYfWTcj0fHP0Jcy22ZTb0i4kb3h4,26823
|
|
27
|
-
librelane/container.py,sha256=
|
|
28
|
-
librelane/env_info.py,sha256=
|
|
27
|
+
librelane/container.py,sha256=7w_V2Fpb3dbnZ8FqBce1vK31jH30UrxByppfEJRyG9M,8672
|
|
28
|
+
librelane/env_info.py,sha256=xF9iqwwJv5yZz7n7BTrrT_yP3Dp1HjAOUObNE9k_1g4,11074
|
|
29
29
|
librelane/examples/spm/config.yaml,sha256=H2ERY4xoIeXN7kM3N9yGWiFBbtByyaN2Ni1kFqYPtO4,612
|
|
30
30
|
librelane/examples/spm/pin_order.cfg,sha256=-8mTGFKnES0vhQATfaE2TXN_mdCZ3SZIN90Src1l6fY,52
|
|
31
31
|
librelane/examples/spm/src/impl.sdc,sha256=wP18UoVlOJ9q4lmUoa3XpgcpPdyzEqHBNxCgOOU7QH0,2961
|
|
@@ -43,11 +43,12 @@ librelane/flows/__init__.py,sha256=ghtmUG-taVpHJ3CKJRYZGn3dU0r93araT1EIGlBEsxg,8
|
|
|
43
43
|
librelane/flows/builtins.py,sha256=tR14Qc1ZUey2w-Ar4DWOvxuP7LGPtMecCJq8WgcYJpk,773
|
|
44
44
|
librelane/flows/classic.py,sha256=JB9gVgP2hHPhMuCJg7hvoj2BvJcvRec7suEXPgHmz14,10971
|
|
45
45
|
librelane/flows/cli.py,sha256=_aJjlalCsmUeLiqy1D8VFmBJ0dLelx24erqaK2q-AOs,16706
|
|
46
|
-
librelane/flows/flow.py,sha256=
|
|
46
|
+
librelane/flows/flow.py,sha256=1zRhYQvnRte-VNcsVmAkikD_kZJVbgsqgLR-8CGYaLI,37034
|
|
47
47
|
librelane/flows/misc.py,sha256=32Om3isexesfKKiJZCajNmINc-xdv7eVx_tgoh9SR6U,2015
|
|
48
48
|
librelane/flows/optimizing.py,sha256=OwZz6WGmXpliwO8vtmhjKHD-kzDyNv-zoCECZIigXsI,6076
|
|
49
49
|
librelane/flows/sequential.py,sha256=kBpR9kxfEfdTaNy9Ter2KNQXkW6qojCwoBsFJBwTq6I,15359
|
|
50
50
|
librelane/flows/synth_explore.py,sha256=8mpeuG6oxeEXVQi4NwS4I415eCu7Ak6DN4oK30h1eCQ,7418
|
|
51
|
+
librelane/help/__main__.py,sha256=gnm0yi-Ih8YoyY2cMiHONV2ZzR-tvHfdEHCb28YQJZ0,1243
|
|
51
52
|
librelane/logging/__init__.py,sha256=mrTnzjpH6AOu2CiDZYfOMCVByAS2Xeg9HS4FJyXsJOE,1043
|
|
52
53
|
librelane/logging/logger.py,sha256=kA61TGsR00Fi6kQSxgTC1pHpS_-zqC1PdQnYqnk2TWY,8632
|
|
53
54
|
librelane/pdk_hashes.yaml,sha256=wHPz6Ze4e1uhZTo7IKS4wcmU6ShcZdBtHcdACP5tYNI,153
|
|
@@ -141,9 +142,9 @@ librelane/scripts/openroad/ungpl.tcl,sha256=vhHxou1W3VROwJePoQzmWn0h0d5lQrrt1vof
|
|
|
141
142
|
librelane/scripts/openroad/write_cdl.tcl,sha256=uPO1IROPTr5NrW0-VZA8tXQD8aseGeXDXDsU8TX-9nQ,460
|
|
142
143
|
librelane/scripts/openroad/write_views.tcl,sha256=-MxTJsB4EF7l5trDaZe-VBFjhfzqRt8F5_DZrADTs0U,892
|
|
143
144
|
librelane/scripts/pyosys/construct_abc_script.py,sha256=3CCDz5ZTEPpWLco-OvikTmn361-BNitqjQE_-5zHm14,6733
|
|
144
|
-
librelane/scripts/pyosys/json_header.py,sha256=
|
|
145
|
-
librelane/scripts/pyosys/synthesize.py,sha256=
|
|
146
|
-
librelane/scripts/pyosys/ys_common.py,sha256=
|
|
145
|
+
librelane/scripts/pyosys/json_header.py,sha256=fbHPjUkTQHKbdGPh7P1jZjip3nat4k8oSD1N7rpXxFk,2389
|
|
146
|
+
librelane/scripts/pyosys/synthesize.py,sha256=5BlgXK5gfJWv9E8ki9UXUDLxlfKoGwXgqjdDMiPrms8,17020
|
|
147
|
+
librelane/scripts/pyosys/ys_common.py,sha256=r9BQ7j8gN6sgJM9nC3QPNZcZX10m_PF8We4S93qZ5w0,3957
|
|
147
148
|
librelane/scripts/tclsh/hello.tcl,sha256=kkR3akY7QnGHYXsQODYwLkMkUEOgWcNFtzaMTTEV2bY,34
|
|
148
149
|
librelane/state/__init__.py,sha256=DZ_RyKMr2oj4p5d32u8MmDKfCxR7OEdDw-1HWKTpatA,949
|
|
149
150
|
librelane/state/__main__.py,sha256=Ici4Ejg1ICUZNSYZRguC3BfEk_wFxsmE0ag0Vv8iY1I,1679
|
|
@@ -159,14 +160,14 @@ librelane/steps/magic.py,sha256=m4cZH2VomJs0RudtV8avSaZVqRj1NP7Pm2P6qo2z2X0,2091
|
|
|
159
160
|
librelane/steps/misc.py,sha256=8ubCvFeFEspXrgnzNWINY5-TXTyalNtlvcX8TSw0qdg,5685
|
|
160
161
|
librelane/steps/netgen.py,sha256=R9sDWv-9wKMdi2rkuLQdOc4uLlbYhXcKKd6WsZsnLt0,8953
|
|
161
162
|
librelane/steps/odb.py,sha256=-zsXi0jVdtfBfAJI0OC4x1jI_B2OX5YVn4uAn6NyFdk,38424
|
|
162
|
-
librelane/steps/openroad.py,sha256=
|
|
163
|
+
librelane/steps/openroad.py,sha256=hgpqsVQi7tFRlj75zefQkD3Vdmq21ubZAFWyU12WxUg,99586
|
|
163
164
|
librelane/steps/openroad_alerts.py,sha256=IJyB4piBDCKXhkJswHGMYCRDwbdQsR0GZlrGGDhmW6Q,3364
|
|
164
|
-
librelane/steps/pyosys.py,sha256=
|
|
165
|
+
librelane/steps/pyosys.py,sha256=BzTUYCFxXJWtHqPtEPAztoWNLbDWvECWnsWVdK_lljU,23589
|
|
165
166
|
librelane/steps/step.py,sha256=THIxZkhtkNYt1iRgMduD0ywrOTCaV7cCfUB2EqXN6-k,55751
|
|
166
|
-
librelane/steps/tclstep.py,sha256=
|
|
167
|
+
librelane/steps/tclstep.py,sha256=68AjCmbLhBscbzQDxRcPQVU-6UvZQNOalO7qNwUXCa4,10138
|
|
167
168
|
librelane/steps/verilator.py,sha256=MWx2TpLqYyea9_jSeLG9c2S5ujvYERQZRFNaMhfHxZE,7916
|
|
168
169
|
librelane/steps/yosys.py,sha256=lYdZPFvjcmdu_NE6rtB94_dysIK2qwGdGb480W6pg2w,12711
|
|
169
|
-
librelane-3.0.0.
|
|
170
|
-
librelane-3.0.0.
|
|
171
|
-
librelane-3.0.0.
|
|
172
|
-
librelane-3.0.0.
|
|
170
|
+
librelane-3.0.0.dev33.dist-info/METADATA,sha256=u7-qZzdB2GVYUEeR1RAAyHvIC8GU4Y3cprFnjR_uJyU,6561
|
|
171
|
+
librelane-3.0.0.dev33.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
172
|
+
librelane-3.0.0.dev33.dist-info/entry_points.txt,sha256=0eZs2NOH-w-W_GVRCs-ualst26XplkPpJkOnGWMaFw0,306
|
|
173
|
+
librelane-3.0.0.dev33.dist-info/RECORD,,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
librelane=librelane.__main__:cli
|
|
3
3
|
librelane.config=librelane.config.__main__:cli
|
|
4
4
|
librelane.env_info=librelane:env_info_cli
|
|
5
|
+
librelane.help=librelane.help.__main__:cli
|
|
5
6
|
librelane.state=librelane.state.__main__:cli
|
|
6
7
|
librelane.steps=librelane.steps.__main__:cli
|
|
7
8
|
openlane=librelane.__main__:cli
|
|
File without changes
|