ciel 0.21.0.dev1__tar.gz → 1.0.0__tar.gz
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.
- {ciel-0.21.0.dev1 → ciel-1.0.0}/PKG-INFO +3 -3
- {ciel-0.21.0.dev1 → ciel-1.0.0}/ciel/__main__.py +29 -86
- {ciel-0.21.0.dev1 → ciel-1.0.0}/ciel/build/__init__.py +10 -9
- {ciel-0.21.0.dev1 → ciel-1.0.0}/ciel/click_common.py +7 -28
- {ciel-0.21.0.dev1 → ciel-1.0.0}/ciel/common.py +5 -69
- {ciel-0.21.0.dev1 → ciel-1.0.0}/ciel/github.py +33 -29
- {ciel-0.21.0.dev1 → ciel-1.0.0}/ciel/manage.py +23 -24
- ciel-1.0.0/ciel/source.py +229 -0
- {ciel-0.21.0.dev1 → ciel-1.0.0}/pyproject.toml +5 -2
- {ciel-0.21.0.dev1 → ciel-1.0.0}/Readme.md +0 -0
- {ciel-0.21.0.dev1 → ciel-1.0.0}/ciel/__init__.py +0 -0
- {ciel-0.21.0.dev1 → ciel-1.0.0}/ciel/__version__.py +0 -0
- {ciel-0.21.0.dev1 → ciel-1.0.0}/ciel/build/common.py +0 -0
- {ciel-0.21.0.dev1 → ciel-1.0.0}/ciel/build/gf180mcu.py +0 -0
- {ciel-0.21.0.dev1 → ciel-1.0.0}/ciel/build/git_multi_clone.py +0 -0
- {ciel-0.21.0.dev1 → ciel-1.0.0}/ciel/build/ihp_sg13g2.py +0 -0
- {ciel-0.21.0.dev1 → ciel-1.0.0}/ciel/build/sky130.py +0 -0
- {ciel-0.21.0.dev1 → ciel-1.0.0}/ciel/families.py +0 -0
- {ciel-0.21.0.dev1 → ciel-1.0.0}/ciel/py.typed +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ciel
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: An PDK builder/version manager for PDKs in the open_pdks format
|
|
5
5
|
Home-page: https://github.com/fossi-foundation/ciel
|
|
6
6
|
License: Apache-2.0
|
|
7
|
-
Author:
|
|
8
|
-
Author-email: donn
|
|
7
|
+
Author: Mohamed Gaber
|
|
8
|
+
Author-email: me@donn.website
|
|
9
9
|
Requires-Python: >=3.8
|
|
10
10
|
Classifier: Intended Audience :: Developers
|
|
11
11
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
# Copyright 2025 The American University in Cairo
|
|
2
|
+
#
|
|
3
|
+
# Adapted from the Volare project
|
|
4
|
+
#
|
|
1
5
|
# Copyright 2022-2023 Efabless Corporation
|
|
2
6
|
#
|
|
3
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -25,11 +29,7 @@ from .common import (
|
|
|
25
29
|
resolve_version,
|
|
26
30
|
)
|
|
27
31
|
from .click_common import (
|
|
28
|
-
opt,
|
|
29
|
-
opt_build,
|
|
30
|
-
opt_push,
|
|
31
32
|
opt_pdk_root,
|
|
32
|
-
opt_token,
|
|
33
33
|
)
|
|
34
34
|
from .manage import (
|
|
35
35
|
print_installed_list,
|
|
@@ -41,6 +41,8 @@ from .build import (
|
|
|
41
41
|
build_cmd,
|
|
42
42
|
push_cmd,
|
|
43
43
|
)
|
|
44
|
+
from .github import opt_github_token
|
|
45
|
+
from .source import opt_data_source
|
|
44
46
|
|
|
45
47
|
|
|
46
48
|
@click.command("output")
|
|
@@ -113,9 +115,10 @@ def rm_cmd(pdk_root, pdk, version):
|
|
|
113
115
|
|
|
114
116
|
|
|
115
117
|
@click.command("ls")
|
|
116
|
-
@
|
|
118
|
+
@opt_data_source
|
|
119
|
+
@opt_github_token
|
|
117
120
|
@opt_pdk_root
|
|
118
|
-
def list_cmd(pdk_root, pdk):
|
|
121
|
+
def list_cmd(data_source, pdk_root, pdk):
|
|
119
122
|
"""Lists PDK versions that are locally installed. JSON if not outputting to a tty."""
|
|
120
123
|
|
|
121
124
|
pdk_versions = Version.get_all_installed(pdk_root, pdk)
|
|
@@ -125,6 +128,7 @@ def list_cmd(pdk_root, pdk):
|
|
|
125
128
|
print_installed_list(
|
|
126
129
|
pdk_root,
|
|
127
130
|
pdk,
|
|
131
|
+
data_source=data_source,
|
|
128
132
|
console=console,
|
|
129
133
|
installed_list=pdk_versions,
|
|
130
134
|
)
|
|
@@ -133,14 +137,14 @@ def list_cmd(pdk_root, pdk):
|
|
|
133
137
|
|
|
134
138
|
|
|
135
139
|
@click.command("ls-remote")
|
|
136
|
-
@
|
|
140
|
+
@opt_github_token
|
|
141
|
+
@opt_data_source
|
|
137
142
|
@opt_pdk_root
|
|
138
|
-
def list_remote_cmd(pdk_root, pdk):
|
|
143
|
+
def list_remote_cmd(data_source, pdk_root, pdk):
|
|
139
144
|
"""Lists PDK versions that are remotely available. JSON if not outputting to a tty."""
|
|
140
145
|
|
|
141
146
|
try:
|
|
142
|
-
|
|
143
|
-
pdk_versions = all_versions.get(pdk) or []
|
|
147
|
+
pdk_versions = data_source.get_available_versions(pdk)
|
|
144
148
|
|
|
145
149
|
if sys.stdout.isatty():
|
|
146
150
|
console = Console()
|
|
@@ -148,6 +152,13 @@ def list_remote_cmd(pdk_root, pdk):
|
|
|
148
152
|
else:
|
|
149
153
|
for version in pdk_versions:
|
|
150
154
|
print(version.name)
|
|
155
|
+
except ValueError as e:
|
|
156
|
+
if sys.stdout.isatty():
|
|
157
|
+
console = Console()
|
|
158
|
+
console.print(f"[red]{e}")
|
|
159
|
+
else:
|
|
160
|
+
print(f"{e}", file=sys.stderr)
|
|
161
|
+
sys.exit(-1)
|
|
151
162
|
except httpx.HTTPStatusError as e:
|
|
152
163
|
if sys.stdout.isatty():
|
|
153
164
|
console = Console()
|
|
@@ -184,7 +195,8 @@ def path_cmd(pdk_root, pdk, version):
|
|
|
184
195
|
|
|
185
196
|
|
|
186
197
|
@click.command("enable")
|
|
187
|
-
@
|
|
198
|
+
@opt_data_source
|
|
199
|
+
@opt_github_token
|
|
188
200
|
@opt_pdk_root
|
|
189
201
|
@click.option(
|
|
190
202
|
"-f",
|
|
@@ -202,6 +214,7 @@ def path_cmd(pdk_root, pdk, version):
|
|
|
202
214
|
)
|
|
203
215
|
@click.argument("version", required=False)
|
|
204
216
|
def enable_cmd(
|
|
217
|
+
data_source,
|
|
205
218
|
pdk_root,
|
|
206
219
|
pdk,
|
|
207
220
|
tool_metadata_file_path,
|
|
@@ -234,6 +247,7 @@ def enable_cmd(
|
|
|
234
247
|
version,
|
|
235
248
|
include_libraries=include_libraries,
|
|
236
249
|
output=console,
|
|
250
|
+
data_source=data_source,
|
|
237
251
|
)
|
|
238
252
|
except Exception as e:
|
|
239
253
|
console.print(f"[red]{e}")
|
|
@@ -241,7 +255,8 @@ def enable_cmd(
|
|
|
241
255
|
|
|
242
256
|
|
|
243
257
|
@click.command("fetch")
|
|
244
|
-
@
|
|
258
|
+
@opt_data_source
|
|
259
|
+
@opt_github_token
|
|
245
260
|
@opt_pdk_root
|
|
246
261
|
@click.option(
|
|
247
262
|
"-f",
|
|
@@ -259,6 +274,7 @@ def enable_cmd(
|
|
|
259
274
|
)
|
|
260
275
|
@click.argument("version", required=False)
|
|
261
276
|
def fetch_cmd(
|
|
277
|
+
data_source,
|
|
262
278
|
pdk_root,
|
|
263
279
|
pdk,
|
|
264
280
|
tool_metadata_file_path,
|
|
@@ -287,6 +303,7 @@ def fetch_cmd(
|
|
|
287
303
|
|
|
288
304
|
try:
|
|
289
305
|
version = fetch(
|
|
306
|
+
data_source=data_source,
|
|
290
307
|
pdk_root=pdk_root,
|
|
291
308
|
pdk=pdk,
|
|
292
309
|
version=version,
|
|
@@ -300,79 +317,6 @@ def fetch_cmd(
|
|
|
300
317
|
exit(-1)
|
|
301
318
|
|
|
302
319
|
|
|
303
|
-
@click.command("enable_or_build", hidden=True)
|
|
304
|
-
@opt_token
|
|
305
|
-
@opt_pdk_root
|
|
306
|
-
@opt_push
|
|
307
|
-
@opt_build
|
|
308
|
-
@opt("--also-push/--dont-push", default=False, help="Also push.")
|
|
309
|
-
@click.option(
|
|
310
|
-
"-f",
|
|
311
|
-
"--metadata-file",
|
|
312
|
-
"tool_metadata_file_path",
|
|
313
|
-
default=None,
|
|
314
|
-
help="Explicitly define a tool metadata file instead of searching for a metadata file",
|
|
315
|
-
)
|
|
316
|
-
@click.argument("version")
|
|
317
|
-
def enable_or_build_cmd(
|
|
318
|
-
include_libraries,
|
|
319
|
-
jobs,
|
|
320
|
-
pdk_root,
|
|
321
|
-
pdk,
|
|
322
|
-
owner,
|
|
323
|
-
repository,
|
|
324
|
-
pre,
|
|
325
|
-
clear_build_artifacts,
|
|
326
|
-
tool_metadata_file_path,
|
|
327
|
-
also_push,
|
|
328
|
-
version,
|
|
329
|
-
use_repo_at,
|
|
330
|
-
push_libraries,
|
|
331
|
-
):
|
|
332
|
-
"""
|
|
333
|
-
Attempts to activate a given PDK version. If the version is not found locally or remotely,
|
|
334
|
-
it will instead attempt to build said version.
|
|
335
|
-
|
|
336
|
-
Parameters: <version>
|
|
337
|
-
"""
|
|
338
|
-
if include_libraries == ():
|
|
339
|
-
include_libraries = None
|
|
340
|
-
if push_libraries == ():
|
|
341
|
-
push_libraries = include_libraries
|
|
342
|
-
|
|
343
|
-
console = Console()
|
|
344
|
-
try:
|
|
345
|
-
version = resolve_version(version, tool_metadata_file_path)
|
|
346
|
-
except Exception as e:
|
|
347
|
-
console.print(f"Could not determine open_pdks version: {e}")
|
|
348
|
-
exit(-1)
|
|
349
|
-
try:
|
|
350
|
-
enable(
|
|
351
|
-
pdk_root=pdk_root,
|
|
352
|
-
pdk=pdk,
|
|
353
|
-
version=version,
|
|
354
|
-
build_if_not_found=True,
|
|
355
|
-
also_push=also_push,
|
|
356
|
-
build_kwargs={
|
|
357
|
-
"include_libraries": include_libraries,
|
|
358
|
-
"jobs": jobs,
|
|
359
|
-
"clear_build_artifacts": clear_build_artifacts,
|
|
360
|
-
"use_repo_at": use_repo_at,
|
|
361
|
-
},
|
|
362
|
-
push_kwargs={
|
|
363
|
-
"owner": owner,
|
|
364
|
-
"repository": repository,
|
|
365
|
-
"pre": pre,
|
|
366
|
-
"push_libraries": push_libraries,
|
|
367
|
-
},
|
|
368
|
-
include_libraries=include_libraries,
|
|
369
|
-
output=console,
|
|
370
|
-
)
|
|
371
|
-
except Exception as e:
|
|
372
|
-
console.print(f"[red]{e}")
|
|
373
|
-
exit(-1)
|
|
374
|
-
|
|
375
|
-
|
|
376
320
|
@click.group()
|
|
377
321
|
@click.version_option(
|
|
378
322
|
__version__,
|
|
@@ -404,7 +348,6 @@ cli.add_command(list_cmd)
|
|
|
404
348
|
cli.add_command(list_remote_cmd)
|
|
405
349
|
cli.add_command(enable_cmd)
|
|
406
350
|
cli.add_command(fetch_cmd)
|
|
407
|
-
cli.add_command(enable_or_build_cmd)
|
|
408
351
|
|
|
409
352
|
try:
|
|
410
353
|
import ssl # noqa: F401
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
# Copyright 2025 The American University in Cairo
|
|
2
|
+
#
|
|
3
|
+
# Adapted from the Volare project
|
|
4
|
+
#
|
|
1
5
|
# Copyright 2022-2023 Efabless Corporation
|
|
2
6
|
#
|
|
3
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -28,7 +32,7 @@ from rich.progress import Progress
|
|
|
28
32
|
from ..github import (
|
|
29
33
|
GitHubSession,
|
|
30
34
|
get_commit_date,
|
|
31
|
-
|
|
35
|
+
opt_github_token,
|
|
32
36
|
)
|
|
33
37
|
from ..common import (
|
|
34
38
|
Version,
|
|
@@ -40,7 +44,6 @@ from ..click_common import (
|
|
|
40
44
|
opt_push,
|
|
41
45
|
opt_build,
|
|
42
46
|
opt_pdk_root,
|
|
43
|
-
opt_token,
|
|
44
47
|
)
|
|
45
48
|
from ..families import Family
|
|
46
49
|
|
|
@@ -79,7 +82,7 @@ def build(
|
|
|
79
82
|
|
|
80
83
|
|
|
81
84
|
@click.command("build")
|
|
82
|
-
@
|
|
85
|
+
@opt_github_token
|
|
83
86
|
@opt_pdk_root
|
|
84
87
|
@opt_build
|
|
85
88
|
@click.option(
|
|
@@ -135,16 +138,14 @@ def push(
|
|
|
135
138
|
pdk,
|
|
136
139
|
version,
|
|
137
140
|
*,
|
|
138
|
-
owner
|
|
139
|
-
repository
|
|
141
|
+
owner,
|
|
142
|
+
repository,
|
|
140
143
|
pre=False,
|
|
141
144
|
push_libraries=None,
|
|
142
|
-
session: Optional[GitHubSession] = None,
|
|
143
145
|
):
|
|
144
146
|
family = Family.by_name[pdk]
|
|
145
147
|
|
|
146
|
-
|
|
147
|
-
session = GitHubSession()
|
|
148
|
+
session = GitHubSession()
|
|
148
149
|
if session.github_token is None:
|
|
149
150
|
raise TypeError("No GitHub token was provided.")
|
|
150
151
|
|
|
@@ -231,7 +232,7 @@ def push(
|
|
|
231
232
|
|
|
232
233
|
|
|
233
234
|
@click.command("push", hidden=True)
|
|
234
|
-
@
|
|
235
|
+
@opt_github_token
|
|
235
236
|
@opt_pdk_root
|
|
236
237
|
@opt_push
|
|
237
238
|
@click.argument("version")
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
# Copyright 2025 The American University in Cairo
|
|
2
|
+
#
|
|
3
|
+
# Adapted from the Volare project
|
|
4
|
+
#
|
|
1
5
|
# Copyright 2022-2023 Efabless Corporation
|
|
2
6
|
#
|
|
3
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -13,12 +17,11 @@
|
|
|
13
17
|
# limitations under the License.
|
|
14
18
|
import os
|
|
15
19
|
from functools import partial
|
|
16
|
-
from typing import Callable
|
|
20
|
+
from typing import Callable
|
|
17
21
|
|
|
18
22
|
import click
|
|
19
23
|
|
|
20
24
|
from .common import VOLARE_RESOLVED_HOME
|
|
21
|
-
from .github import volare_repo, GitHubSession
|
|
22
25
|
|
|
23
26
|
opt = partial(click.option, show_default=True)
|
|
24
27
|
|
|
@@ -77,12 +80,10 @@ def opt_build(function: Callable):
|
|
|
77
80
|
|
|
78
81
|
|
|
79
82
|
def opt_push(function: Callable):
|
|
80
|
-
function = opt("-o", "--owner", default=
|
|
81
|
-
function
|
|
82
|
-
)
|
|
83
|
-
function = opt("-r", "--repository", default=volare_repo.name, help="Repository")(
|
|
83
|
+
function = opt("-o", "--owner", default="efabless", help="Repository Owner")(
|
|
84
84
|
function
|
|
85
85
|
)
|
|
86
|
+
function = opt("-r", "--repository", default="volare", help="Repository")(function)
|
|
86
87
|
function = opt(
|
|
87
88
|
"--pre/--prod", default=False, help="Push as pre-release or production"
|
|
88
89
|
)(function)
|
|
@@ -95,25 +96,3 @@ def opt_push(function: Callable):
|
|
|
95
96
|
help="Push only libraries in this list. You can use -L multiple times to include multiple libraries. Pass 'None' to push all libraries built.",
|
|
96
97
|
)(function)
|
|
97
98
|
return function
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def set_token_cb(
|
|
101
|
-
ctx: click.Context,
|
|
102
|
-
param: click.Parameter,
|
|
103
|
-
value: Optional[str],
|
|
104
|
-
):
|
|
105
|
-
GitHubSession.Token.override = value
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def opt_token(function: Callable) -> Callable:
|
|
109
|
-
function = opt(
|
|
110
|
-
"-t",
|
|
111
|
-
"--token",
|
|
112
|
-
"session",
|
|
113
|
-
default=None,
|
|
114
|
-
required=False,
|
|
115
|
-
expose_value=False,
|
|
116
|
-
help="Replace the GitHub token used for GitHub requests, which is by default the value of the environment variable GITHUB_TOKEN or None.",
|
|
117
|
-
callback=set_token_cb,
|
|
118
|
-
)(function)
|
|
119
|
-
return function
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
# Copyright 2025 The American University in Cairo
|
|
2
|
+
#
|
|
3
|
+
# Modified from the Volare project
|
|
4
|
+
#
|
|
1
5
|
# Copyright 2022-2023 Efabless Corporation
|
|
2
6
|
#
|
|
3
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -12,14 +16,12 @@
|
|
|
12
16
|
# See the License for the specific language governing permissions and
|
|
13
17
|
# limitations under the License.
|
|
14
18
|
import os
|
|
15
|
-
import re
|
|
16
19
|
import shutil
|
|
17
20
|
import pathlib
|
|
18
21
|
from datetime import datetime
|
|
19
22
|
from dataclasses import dataclass
|
|
20
|
-
from typing import
|
|
23
|
+
from typing import Optional, List
|
|
21
24
|
|
|
22
|
-
from . import github
|
|
23
25
|
from .families import Family
|
|
24
26
|
|
|
25
27
|
# -- Assorted Helper Functions
|
|
@@ -142,72 +144,6 @@ class Version(object):
|
|
|
142
144
|
if os.path.isdir(os.path.join(versions_dir, version))
|
|
143
145
|
]
|
|
144
146
|
|
|
145
|
-
@classmethod
|
|
146
|
-
def _from_github(
|
|
147
|
-
Self,
|
|
148
|
-
session: Optional[github.GitHubSession] = None,
|
|
149
|
-
) -> Dict[str, List["Version"]]:
|
|
150
|
-
releases = github.get_releases(session)
|
|
151
|
-
|
|
152
|
-
rvs_by_pdk: Dict[str, List["Version"]] = {}
|
|
153
|
-
|
|
154
|
-
commit_rx = re.compile(r"released on ([\d\-\:TZ]+)")
|
|
155
|
-
|
|
156
|
-
for release in releases:
|
|
157
|
-
if release["draft"]:
|
|
158
|
-
continue
|
|
159
|
-
family, hash = release["tag_name"].rsplit("-", maxsplit=1)
|
|
160
|
-
|
|
161
|
-
upload_date = date_from_iso8601(release["published_at"])
|
|
162
|
-
commit_date = None
|
|
163
|
-
|
|
164
|
-
commit_date_match = commit_rx.search(release["body"])
|
|
165
|
-
if commit_date_match is not None:
|
|
166
|
-
commit_date = date_from_iso8601(commit_date_match[1])
|
|
167
|
-
|
|
168
|
-
remote_version = Self(
|
|
169
|
-
name=hash,
|
|
170
|
-
pdk=family,
|
|
171
|
-
commit_date=commit_date,
|
|
172
|
-
upload_date=upload_date,
|
|
173
|
-
prerelease=release["prerelease"],
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
if rvs_by_pdk.get(family) is None:
|
|
177
|
-
rvs_by_pdk[family] = rvs_by_pdk.get(family) or []
|
|
178
|
-
|
|
179
|
-
rvs_by_pdk[family].append(remote_version)
|
|
180
|
-
|
|
181
|
-
for family in rvs_by_pdk.keys():
|
|
182
|
-
rvs_by_pdk[family].sort(reverse=True)
|
|
183
|
-
|
|
184
|
-
return rvs_by_pdk
|
|
185
|
-
|
|
186
|
-
def get_release_links(
|
|
187
|
-
self,
|
|
188
|
-
scl_filter: Iterable[str],
|
|
189
|
-
include_common: bool,
|
|
190
|
-
session: Optional[github.GitHubSession] = None,
|
|
191
|
-
) -> List[Tuple[str, str]]:
|
|
192
|
-
release = github.get_release_links(f"{self.pdk}-{self.name}", session)
|
|
193
|
-
|
|
194
|
-
assets = release["assets"]
|
|
195
|
-
zst_files = []
|
|
196
|
-
for asset in assets:
|
|
197
|
-
if asset["name"].endswith(".tar.zst"):
|
|
198
|
-
asset_scl = asset["name"][:-8]
|
|
199
|
-
if (
|
|
200
|
-
asset_scl == "common" and include_common
|
|
201
|
-
) or asset_scl in scl_filter:
|
|
202
|
-
zst_files.append((asset["name"], asset["browser_download_url"]))
|
|
203
|
-
|
|
204
|
-
if len(zst_files) == 0:
|
|
205
|
-
raise ValueError(
|
|
206
|
-
f"No files found for standard cell libraries: {scl_filter}."
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
return zst_files
|
|
210
|
-
|
|
211
147
|
|
|
212
148
|
def resolve_version(
|
|
213
149
|
version: Optional[str],
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
# Copyright 2025 The American University in Cairo
|
|
2
|
+
#
|
|
3
|
+
# Modified from the Volare project
|
|
4
|
+
#
|
|
1
5
|
# Copyright 2022-2023 Efabless Corporation
|
|
2
6
|
#
|
|
3
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -12,11 +16,12 @@
|
|
|
12
16
|
# See the License for the specific language governing permissions and
|
|
13
17
|
# limitations under the License.
|
|
14
18
|
import os
|
|
15
|
-
import subprocess
|
|
16
19
|
import sys
|
|
20
|
+
import click
|
|
21
|
+
import subprocess
|
|
17
22
|
from datetime import datetime
|
|
18
23
|
from dataclasses import dataclass
|
|
19
|
-
from typing import Any, ClassVar,
|
|
24
|
+
from typing import Any, ClassVar, Optional, Callable
|
|
20
25
|
|
|
21
26
|
import httpx
|
|
22
27
|
import ssl
|
|
@@ -28,6 +33,10 @@ class RepoInfo:
|
|
|
28
33
|
owner: str
|
|
29
34
|
name: str
|
|
30
35
|
|
|
36
|
+
@classmethod
|
|
37
|
+
def from_id(self, id_str: str) -> "RepoInfo":
|
|
38
|
+
return RepoInfo(*id_str.split("/", maxsplit=1))
|
|
39
|
+
|
|
31
40
|
@property
|
|
32
41
|
def id(self):
|
|
33
42
|
return f"{self.owner}/{self.name}"
|
|
@@ -41,11 +50,6 @@ class RepoInfo:
|
|
|
41
50
|
return f"https://api.github.com/repos/{self.id}"
|
|
42
51
|
|
|
43
52
|
|
|
44
|
-
volare_repo = RepoInfo(
|
|
45
|
-
os.getenv("VOLARE_REPO_OWNER", "efabless"),
|
|
46
|
-
os.getenv("VOLARE_REPO_NAME", "volare"),
|
|
47
|
-
)
|
|
48
|
-
|
|
49
53
|
opdks_repo = RepoInfo(
|
|
50
54
|
os.getenv("OPDKS_REPO_OWNER", "RTimothyEdwards"),
|
|
51
55
|
os.getenv("OPDKS_REPO_NAME", "open_pdks"),
|
|
@@ -68,16 +72,10 @@ class GitHubSession(httpx.Client):
|
|
|
68
72
|
# 0. Lowest priority: ghcli
|
|
69
73
|
try:
|
|
70
74
|
token = subprocess.check_output(
|
|
71
|
-
[
|
|
72
|
-
"gh",
|
|
73
|
-
"auth",
|
|
74
|
-
"token",
|
|
75
|
-
],
|
|
75
|
+
["gh", "auth", "token"],
|
|
76
76
|
encoding="utf8",
|
|
77
77
|
).strip()
|
|
78
|
-
except
|
|
79
|
-
pass
|
|
80
|
-
except subprocess.CalledProcessError:
|
|
78
|
+
except Exception:
|
|
81
79
|
pass
|
|
82
80
|
|
|
83
81
|
# 1. Higher priority: environment GITHUB_TOKEN
|
|
@@ -168,17 +166,23 @@ def get_commit_date(
|
|
|
168
166
|
return commit_date
|
|
169
167
|
|
|
170
168
|
|
|
171
|
-
def
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
169
|
+
def set_token_cb(
|
|
170
|
+
ctx: click.Context,
|
|
171
|
+
param: click.Parameter,
|
|
172
|
+
value: Optional[str],
|
|
173
|
+
):
|
|
174
|
+
GitHubSession.Token.override = value
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def opt_github_token(function: Callable) -> Callable:
|
|
178
|
+
function = click.option(
|
|
179
|
+
"-t",
|
|
180
|
+
"--github-token",
|
|
181
|
+
default=None,
|
|
182
|
+
required=False,
|
|
183
|
+
expose_value=False,
|
|
184
|
+
show_default=True,
|
|
185
|
+
help="Replace the token used for GitHub requests, which is by default the value of the environment variable GITHUB_TOKEN or None.",
|
|
186
|
+
callback=set_token_cb,
|
|
187
|
+
)(function)
|
|
188
|
+
return function
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
# Copyright 2025 The American University in Cairo
|
|
2
|
+
#
|
|
3
|
+
# Modified from the Volare project
|
|
4
|
+
#
|
|
1
5
|
# Copyright 2022-2023 Efabless Corporation
|
|
2
6
|
#
|
|
3
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -27,7 +31,6 @@ import zstandard as zstd
|
|
|
27
31
|
from rich.console import Console
|
|
28
32
|
|
|
29
33
|
from .build.git_multi_clone import mkdirp
|
|
30
|
-
from .github import GitHubSession
|
|
31
34
|
from .common import (
|
|
32
35
|
Version,
|
|
33
36
|
get_versions_dir,
|
|
@@ -35,6 +38,7 @@ from .common import (
|
|
|
35
38
|
)
|
|
36
39
|
from .build import build, push
|
|
37
40
|
from .families import Family
|
|
41
|
+
from .source import DataSource
|
|
38
42
|
|
|
39
43
|
|
|
40
44
|
class VersionNotFound(Exception):
|
|
@@ -45,9 +49,9 @@ def print_installed_list(
|
|
|
45
49
|
pdk_root: str,
|
|
46
50
|
pdk: str,
|
|
47
51
|
*,
|
|
52
|
+
data_source: DataSource,
|
|
48
53
|
console: Console,
|
|
49
54
|
installed_list: List[Version],
|
|
50
|
-
session: Optional[GitHubSession] = None,
|
|
51
55
|
):
|
|
52
56
|
if len(installed_list) == 0:
|
|
53
57
|
console.print("[red]No PDKs installed.")
|
|
@@ -56,8 +60,7 @@ def print_installed_list(
|
|
|
56
60
|
versions = installed_list
|
|
57
61
|
|
|
58
62
|
try:
|
|
59
|
-
|
|
60
|
-
remote_versions = all_remote_versions.get(pdk) or []
|
|
63
|
+
remote_versions = data_source.get_available_versions(pdk)
|
|
61
64
|
remote_version_dict = {rv.name: rv for rv in remote_versions}
|
|
62
65
|
for installed in installed_list:
|
|
63
66
|
remote_version = remote_version_dict.get(installed.name)
|
|
@@ -118,17 +121,14 @@ def fetch(
|
|
|
118
121
|
pdk: str,
|
|
119
122
|
version: str,
|
|
120
123
|
*,
|
|
124
|
+
data_source: DataSource,
|
|
121
125
|
build_if_not_found=False,
|
|
122
126
|
also_push=False,
|
|
123
127
|
build_kwargs: dict = {},
|
|
124
128
|
push_kwargs: dict = {},
|
|
125
129
|
include_libraries: Optional[Iterable[str]] = None,
|
|
126
130
|
output: Union[Console, io.TextIOWrapper] = Console(),
|
|
127
|
-
session: Optional[GitHubSession] = None,
|
|
128
131
|
) -> Version:
|
|
129
|
-
if session is None:
|
|
130
|
-
session = GitHubSession()
|
|
131
|
-
|
|
132
132
|
console = output
|
|
133
133
|
if not isinstance(console, Console):
|
|
134
134
|
console = Console(file=console)
|
|
@@ -178,16 +178,18 @@ def fetch(
|
|
|
178
178
|
|
|
179
179
|
tarball_paths = []
|
|
180
180
|
try:
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
181
|
+
client, assets = data_source.get_downloads_for_version(version_object)
|
|
182
|
+
assets_filtered = []
|
|
183
|
+
for asset in assets:
|
|
184
|
+
if asset.content == "common" and common_missing:
|
|
185
|
+
assets_filtered.append(asset)
|
|
186
|
+
elif asset.content in missing_libraries:
|
|
187
|
+
assets_filtered.append(asset)
|
|
186
188
|
tarball_directory = tempfile.TemporaryDirectory(suffix=".ciel")
|
|
187
|
-
for
|
|
188
|
-
tarball_path = os.path.join(tarball_directory.name,
|
|
189
|
+
for asset in assets_filtered:
|
|
190
|
+
tarball_path = os.path.join(tarball_directory.name, asset.filename)
|
|
189
191
|
tarball_paths.append(tarball_path)
|
|
190
|
-
with
|
|
192
|
+
with client.stream("get", asset.url) as r, rich.progress.Progress(
|
|
191
193
|
console=console
|
|
192
194
|
) as p:
|
|
193
195
|
total_str: Optional[str] = r.headers.get("Content-length", None)
|
|
@@ -195,7 +197,7 @@ def fetch(
|
|
|
195
197
|
if total_str is not None:
|
|
196
198
|
total_int = int(total_str)
|
|
197
199
|
task = p.add_task(
|
|
198
|
-
f"Downloading {
|
|
200
|
+
f"Downloading {asset.filename}…",
|
|
199
201
|
total=total_int,
|
|
200
202
|
)
|
|
201
203
|
r.raise_for_status()
|
|
@@ -204,7 +206,7 @@ def fetch(
|
|
|
204
206
|
p.advance(task, advance=len(chunk))
|
|
205
207
|
f.write(chunk)
|
|
206
208
|
|
|
207
|
-
with console.status(f"Unpacking {
|
|
209
|
+
with console.status(f"Unpacking {asset.filename}…"):
|
|
208
210
|
stream = zstd.open(tarball_path, mode="rb")
|
|
209
211
|
with tarfile.TarFile(fileobj=stream, mode="r") as tf:
|
|
210
212
|
for file in tf:
|
|
@@ -216,7 +218,7 @@ def fetch(
|
|
|
216
218
|
io = tf.extractfile(file)
|
|
217
219
|
if io is None:
|
|
218
220
|
raise IOError(
|
|
219
|
-
f"Failed to unpack file in {
|
|
221
|
+
f"Failed to unpack file in {asset.filename}'s tarball: {file.name}."
|
|
220
222
|
)
|
|
221
223
|
with open(final_path, "wb") as f:
|
|
222
224
|
f.write(io.read())
|
|
@@ -242,7 +244,6 @@ def fetch(
|
|
|
242
244
|
pdk_root=pdk_root,
|
|
243
245
|
pdk=pdk,
|
|
244
246
|
version=version,
|
|
245
|
-
session=session,
|
|
246
247
|
**push_kwargs,
|
|
247
248
|
)
|
|
248
249
|
else:
|
|
@@ -285,16 +286,14 @@ def enable(
|
|
|
285
286
|
pdk: str,
|
|
286
287
|
version: str,
|
|
287
288
|
*,
|
|
289
|
+
data_source: DataSource,
|
|
288
290
|
build_if_not_found: bool = False,
|
|
289
291
|
also_push: bool = False,
|
|
290
292
|
build_kwargs: dict = {},
|
|
291
293
|
push_kwargs: dict = {},
|
|
292
294
|
include_libraries: Optional[List[str]] = None,
|
|
293
295
|
output: Union[Console, io.TextIOWrapper] = Console(),
|
|
294
|
-
session: Optional[GitHubSession] = None,
|
|
295
296
|
) -> Version:
|
|
296
|
-
if session is None:
|
|
297
|
-
session = GitHubSession()
|
|
298
297
|
|
|
299
298
|
console = output
|
|
300
299
|
if not isinstance(console, Console):
|
|
@@ -315,13 +314,13 @@ def enable(
|
|
|
315
314
|
pdk_root,
|
|
316
315
|
pdk,
|
|
317
316
|
version,
|
|
317
|
+
data_source=data_source,
|
|
318
318
|
build_if_not_found=build_if_not_found,
|
|
319
319
|
also_push=also_push,
|
|
320
320
|
build_kwargs=build_kwargs,
|
|
321
321
|
push_kwargs=push_kwargs,
|
|
322
322
|
include_libraries=include_libraries,
|
|
323
323
|
output=output,
|
|
324
|
-
session=session,
|
|
325
324
|
)
|
|
326
325
|
|
|
327
326
|
current_file = os.path.join(get_ciel_dir(pdk_root, pdk), "current")
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# Copyright 2025 The American University in Cairo
|
|
2
|
+
#
|
|
3
|
+
# Modified from the Volare project
|
|
4
|
+
#
|
|
5
|
+
# Copyright 2022-2023 Efabless Corporation
|
|
6
|
+
#
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
# you may not use this file except in compliance with the License.
|
|
9
|
+
# You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
# See the License for the specific language governing permissions and
|
|
17
|
+
# limitations under the License.
|
|
18
|
+
import re
|
|
19
|
+
import sys
|
|
20
|
+
from dataclasses import dataclass
|
|
21
|
+
from typing import Dict, ClassVar, List, Tuple, Type, Callable
|
|
22
|
+
|
|
23
|
+
import click
|
|
24
|
+
import httpx
|
|
25
|
+
|
|
26
|
+
from .github import GitHubSession, RepoInfo
|
|
27
|
+
from .common import Version, date_from_iso8601
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class Asset:
|
|
32
|
+
content: str
|
|
33
|
+
filename: str
|
|
34
|
+
url: str
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class DataSource(object):
|
|
38
|
+
factory: ClassVar[Dict[str, Type["DataSource"]]] = {}
|
|
39
|
+
default: ClassVar["DataSource"]
|
|
40
|
+
|
|
41
|
+
def __init__(self, argument: str):
|
|
42
|
+
raise NotImplementedError()
|
|
43
|
+
|
|
44
|
+
def get_available_versions(self, pdk: str) -> List[Version]:
|
|
45
|
+
raise NotImplementedError()
|
|
46
|
+
|
|
47
|
+
def get_downloads_for_version(
|
|
48
|
+
self, version: Version
|
|
49
|
+
) -> Tuple[httpx.Client, List[Asset]]:
|
|
50
|
+
raise NotImplementedError()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class GitHubReleasesDataSource(DataSource):
|
|
54
|
+
def __init__(self, repo_id: str):
|
|
55
|
+
self.session = GitHubSession()
|
|
56
|
+
self.repo = RepoInfo.from_id(repo_id)
|
|
57
|
+
|
|
58
|
+
def get_available_versions(self, pdk: str) -> List[Version]:
|
|
59
|
+
page = 1
|
|
60
|
+
last = self.session.api(
|
|
61
|
+
self.repo,
|
|
62
|
+
"/releases",
|
|
63
|
+
"get",
|
|
64
|
+
params={"page": 1, "per_page": 100},
|
|
65
|
+
)
|
|
66
|
+
releases = last
|
|
67
|
+
while len(last) == 100:
|
|
68
|
+
page += 1
|
|
69
|
+
last = self.session.api(
|
|
70
|
+
self.repo,
|
|
71
|
+
"/releases",
|
|
72
|
+
"get",
|
|
73
|
+
params={"page": 1, "per_page": 100},
|
|
74
|
+
)
|
|
75
|
+
releases += last
|
|
76
|
+
|
|
77
|
+
versions = []
|
|
78
|
+
commit_rx = re.compile(r"released on ([\d\-\:TZ]+)")
|
|
79
|
+
for release in releases:
|
|
80
|
+
if release["draft"]:
|
|
81
|
+
continue
|
|
82
|
+
|
|
83
|
+
family, hash = release["tag_name"].rsplit("-", maxsplit=1)
|
|
84
|
+
|
|
85
|
+
if pdk != family:
|
|
86
|
+
continue
|
|
87
|
+
|
|
88
|
+
upload_date = date_from_iso8601(release["published_at"])
|
|
89
|
+
commit_date = None
|
|
90
|
+
|
|
91
|
+
commit_date_match = commit_rx.search(release["body"])
|
|
92
|
+
if commit_date_match is not None:
|
|
93
|
+
commit_date = date_from_iso8601(commit_date_match[1])
|
|
94
|
+
|
|
95
|
+
remote_version = Version(
|
|
96
|
+
name=hash,
|
|
97
|
+
pdk=family,
|
|
98
|
+
commit_date=commit_date,
|
|
99
|
+
upload_date=upload_date,
|
|
100
|
+
prerelease=release["prerelease"],
|
|
101
|
+
)
|
|
102
|
+
versions.append(remote_version)
|
|
103
|
+
|
|
104
|
+
versions.sort(reverse=True)
|
|
105
|
+
if len(versions) == 0:
|
|
106
|
+
raise ValueError(
|
|
107
|
+
f"No versions found for '{pdk}' on github.com/{self.repo.id}"
|
|
108
|
+
)
|
|
109
|
+
return versions
|
|
110
|
+
|
|
111
|
+
def get_downloads_for_version(
|
|
112
|
+
self, version: Version
|
|
113
|
+
) -> Tuple[httpx.Client, List[Asset]]:
|
|
114
|
+
release = self.session.api(
|
|
115
|
+
self.repo,
|
|
116
|
+
f"/releases/tags/{version.pdk}-{version.name}",
|
|
117
|
+
"get",
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
assets = release["assets"]
|
|
121
|
+
zst_files = []
|
|
122
|
+
for asset in assets:
|
|
123
|
+
if asset["name"].endswith(".tar.zst"):
|
|
124
|
+
content = asset["name"][:-8]
|
|
125
|
+
zst_files.append(
|
|
126
|
+
Asset(content, asset["name"], asset["browser_download_url"])
|
|
127
|
+
)
|
|
128
|
+
return (self.session, zst_files)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
DataSource.factory["github-releases"] = GitHubReleasesDataSource
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class StaticWebDataSource(DataSource):
|
|
135
|
+
def __init__(self, base_url: str):
|
|
136
|
+
self.session = GitHubSession()
|
|
137
|
+
self.base_url = base_url
|
|
138
|
+
|
|
139
|
+
def get_available_versions(self, pdk: str) -> List[Version]:
|
|
140
|
+
req = self.session.request("GET", self.base_url + f"/{pdk}/manifest.json")
|
|
141
|
+
try:
|
|
142
|
+
req.raise_for_status()
|
|
143
|
+
except httpx.HTTPStatusError as e:
|
|
144
|
+
if e.response.status_code == 404:
|
|
145
|
+
raise ValueError(
|
|
146
|
+
f"No versions found for '{pdk}' at '{self.base_url}'"
|
|
147
|
+
) from None
|
|
148
|
+
else:
|
|
149
|
+
raise e from None
|
|
150
|
+
manifest = req.json()
|
|
151
|
+
|
|
152
|
+
versions = []
|
|
153
|
+
for version in manifest["versions"]:
|
|
154
|
+
remote_version = Version(
|
|
155
|
+
name=version["version"],
|
|
156
|
+
pdk=manifest["pdk"],
|
|
157
|
+
commit_date=date_from_iso8601(version["date"]),
|
|
158
|
+
prerelease=version.get("prerelease", False),
|
|
159
|
+
)
|
|
160
|
+
versions.append(remote_version)
|
|
161
|
+
|
|
162
|
+
versions.sort(reverse=True)
|
|
163
|
+
if len(versions) == 0:
|
|
164
|
+
raise ValueError(f"No versions found for '{pdk}' on '{self.base_url}'")
|
|
165
|
+
return versions
|
|
166
|
+
|
|
167
|
+
def get_downloads_for_version(
|
|
168
|
+
self, version: Version
|
|
169
|
+
) -> Tuple[httpx.Client, List[Asset]]:
|
|
170
|
+
req = self.session.request(
|
|
171
|
+
"GET", self.base_url + f"/{version.pdk}/{version.name}/manifest.json"
|
|
172
|
+
)
|
|
173
|
+
try:
|
|
174
|
+
req.raise_for_status()
|
|
175
|
+
except httpx.HTTPStatusError as e:
|
|
176
|
+
if e.response.status_code == 404:
|
|
177
|
+
raise ValueError(
|
|
178
|
+
f"Manifest for '{version.pdk}/{version.name}' at '{self.base_url}'"
|
|
179
|
+
) from None
|
|
180
|
+
else:
|
|
181
|
+
raise e from None
|
|
182
|
+
manifest = req.json()
|
|
183
|
+
assets = []
|
|
184
|
+
for asset in manifest["assets"]:
|
|
185
|
+
assets.append(Asset(**asset))
|
|
186
|
+
return (self.session, assets)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
DataSource.factory["static-web"] = StaticWebDataSource
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def data_source_cb(
|
|
193
|
+
ctx: click.Context,
|
|
194
|
+
param: click.Parameter,
|
|
195
|
+
value: str,
|
|
196
|
+
):
|
|
197
|
+
source_id = value
|
|
198
|
+
elements = source_id.split(":", maxsplit=1)
|
|
199
|
+
if len(elements) != 2:
|
|
200
|
+
print(
|
|
201
|
+
"Data source must be in the format '{{class_id}}:{{argument}}' where class_id is one of:",
|
|
202
|
+
file=sys.stderr,
|
|
203
|
+
)
|
|
204
|
+
for id in DataSource.factory:
|
|
205
|
+
print(f"* {id}", file=sys.stderr)
|
|
206
|
+
ctx.exit(-1)
|
|
207
|
+
cls_id, target = elements
|
|
208
|
+
cls = DataSource.factory.get(cls_id)
|
|
209
|
+
if cls is None:
|
|
210
|
+
print(
|
|
211
|
+
f"Unknown data source class '{cls_id}', must be one of:",
|
|
212
|
+
file=sys.stderr,
|
|
213
|
+
)
|
|
214
|
+
for id in DataSource.factory:
|
|
215
|
+
print(f"* {id}", file=sys.stderr)
|
|
216
|
+
ctx.exit(-1)
|
|
217
|
+
return cls(target)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def opt_data_source(function: Callable) -> Callable:
|
|
221
|
+
function = click.option(
|
|
222
|
+
"--data-source",
|
|
223
|
+
default="static-web:https://fossi-foundation.github.io/ciel-releases",
|
|
224
|
+
required=False,
|
|
225
|
+
show_default=True,
|
|
226
|
+
help="The data source to use for operations that may require contacting a remote server, in the format '{class_id}:{argument}'",
|
|
227
|
+
callback=data_source_cb,
|
|
228
|
+
)(function)
|
|
229
|
+
return function
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "ciel"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "1.0.0"
|
|
4
4
|
description = "An PDK builder/version manager for PDKs in the open_pdks format"
|
|
5
|
-
authors = [
|
|
5
|
+
authors = [
|
|
6
|
+
"Mohamed Gaber <me@donn.website>",
|
|
7
|
+
"Efabless Corporation"
|
|
8
|
+
]
|
|
6
9
|
readme = "Readme.md"
|
|
7
10
|
license = "Apache-2.0"
|
|
8
11
|
repository = "https://github.com/fossi-foundation/ciel"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|