From b671245a2fa93739f86f3eccd7e5fe607f7d45a6 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Thu, 27 Aug 2026 14:28:02 +0300 Subject: [PATCH 1/7] feat!: treat ihp-sg13g2 as a variant of the wider ihp-sg13 family - (breaking) rename family to ihp-sg13 and update ihp build scripts accordingly - fix copyrights on various files - update ci --- .github/scripts/generate_tag.py | 39 ------ .github/scripts/gh.py | 137 ---------------------- .github/workflows/ci.yml | 71 +++++++---- Authors.md | 14 +++ OSAcknowledgements | 9 -- Readme.md | 4 +- ciel/__main__.py | 2 +- ciel/build/__init__.py | 2 +- ciel/build/{ihp-sg13g2.py => ihp-sg13.py} | 38 +++--- ciel/click_common.py | 2 +- ciel/common.py | 4 +- ciel/families.py | 4 +- ciel/github.py | 2 +- ciel/manage.py | 2 +- ciel/source.py | 2 +- pyproject.toml | 2 +- 16 files changed, 92 insertions(+), 242 deletions(-) delete mode 100644 .github/scripts/generate_tag.py delete mode 100644 .github/scripts/gh.py create mode 100644 Authors.md delete mode 100644 OSAcknowledgements rename ciel/build/{ihp-sg13g2.py => ihp-sg13.py} (82%) diff --git a/.github/scripts/generate_tag.py b/.github/scripts/generate_tag.py deleted file mode 100644 index c3d8afc..0000000 --- a/.github/scripts/generate_tag.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# Copyright 2020 Efabless Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os -import sys - -from gh import gh - -sys.path.insert(0, os.getcwd()) - -import ciel # noqa: E402 - -print("Getting tags…") - -latest_tag = None -latest_tag_commit = None -tags = [pair[1] for pair in gh.ciel.tags] - -tag_exists = ciel.__version__ in tags - -if tag_exists: - print("Tag already exists. Leaving NEW_TAG unaltered.") -else: - new_tag = ciel.__version__ - - print("Found new tag %s." % new_tag) - gh.export_env("NEW_TAG", new_tag) diff --git a/.github/scripts/gh.py b/.github/scripts/gh.py deleted file mode 100644 index f8c1416..0000000 --- a/.github/scripts/gh.py +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2020 Efabless Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os -import subprocess -from types import SimpleNamespace - - -def export_env_default(key, value): - with open(os.getenv("GITHUB_ENV"), "a") as f: - f.write("%s=%s\n" % (key, value)) - - -export_env = export_env_default - - -class Repo(object): - def __init__(self, name, url, branch_rx=None, extraction_rx=None): - print("[Repo Object] Initializing repo %s with URL %s…" % (name, url)) - self.name = name - self.url = url - self.commit = None - self.branch_rx = branch_rx - self.extraction_rx = extraction_rx - - self._latest_commit = None - self._branches = None - self._tags = None - - @property - def latest_commit(self): - if self._latest_commit is None: - print("[Repo Object] Fetching latest commit for %s…" % self.name) - p = subprocess.check_output(["git", "ls-remote", self.url]).decode("utf8") - for line in p.split("\n"): - if "HEAD" in line: - self._latest_commit = line[:40] - return self._latest_commit - - @property - def branches(self): - if self._branches is None: - print("[Repo Object] Fetching branches for %s…" % self.name) - p = subprocess.check_output( - ["git", "ls-remote", "--heads", self.url] - ).decode("utf8") - branches = [] - for line in p.split("\n"): - if line.strip() == "": - continue - - match = line.split() - - hash = match[0] - name = match[1] - - branches.append((hash, name)) - self._branches = branches - return self._branches - - @property - def tags(self): - if self._tags is None: - print("[Repo Object] Fetching tags for %s…" % self.name) - p = subprocess.check_output( - ["git", "ls-remote", "--tags", "--sort=creatordate", self.url] - ).decode("utf8") - - tags = [] - for line in p.split("\n"): - if line.strip() == "": - continue - - match = line.split() - - hash = match[0] - name = match[1].split("/")[2] - - tags.append((hash, name)) - self._tags = tags - return self._tags - - def out_of_date(self): - return self.commit != self.latest_commit - - -if os.getenv("GITHUB_ACTIONS") != "true": - dn = os.path.dirname - git_directory = dn(dn(dn(os.path.realpath(__file__)))) - - def git_command(*args): - return subprocess.check_output(["git"] + list(args), cwd=git_directory).decode( - "utf-8" - )[:-1] - - repo_url = git_command("remote", "get-url", "origin") - branch = git_command("branch", "--show-current") - - os.environ["REPO_URL"] = repo_url - os.environ["GITHUB_WORKSPACE"] = git_directory - os.environ["GITHUB_EVENT_NAME"] = "workspace_dispatch" - os.environ["GITHUB_RUN_ID"] = "mock_gha_run" - - def export_env_alt(key, value): - os.environ[key] = value - print("Setting ENV[%s] to %s..." % (key, value)) - - export_env = export_env_alt - -origin = os.getenv("REPO_URL") -repo = Repo("ciel", origin) - -# public -gh = SimpleNamespace( - **{ - "run_id": os.getenv("GITHUB_RUN_ID"), - "origin": origin, - "root": os.getenv("GITHUB_WORKSPACE"), - "pdk": os.getenv("PDK_ROOT"), - "tool": os.getenv("TOOL"), - "event": SimpleNamespace(**{"name": os.getenv("GITHUB_EVENT_NAME")}), - "export_env": export_env, - "Repo": Repo, - "ciel": repo, - } -) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d79f32e..9b4274c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,16 +4,30 @@ name: CI on: push: branches: - - "*" + - "main" pull_request: +concurrency: + # Behavior: + # - Group all pull requests: latest push cancels previous ones + # - Group all branches: + # - If main/version- branches: run serially + # - Else, latest push cancels previous ones + group: > + ${{ + (github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number)) || + (github.event_name == 'push' && format('branch-{0}', github.ref_name)) || + '' + }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: lint: name: Lint Python Code runs-on: ubuntu-24.04 steps: - name: Check out Git repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Install Linters run: make venv - name: Lint @@ -24,7 +38,7 @@ jobs: needs: lint steps: - name: Check Git repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Set Up Python uses: actions/setup-python@v4 with: @@ -66,46 +80,51 @@ jobs: runs-on: ubuntu-24.04 if: github.event_name == 'push' && github.ref_name == 'main' outputs: - new_tag: ${{ steps.new_tag.outputs.new_tag }} + version: ${{ steps.get_version.outputs.version }} steps: - name: Check out Git repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: fetch-depth: 0 - - name: Export Repo URL - run: echo "REPO_URL=https://github.com/${{ github.repository }}" >> $GITHUB_ENV - - name: Set Up Python - uses: actions/setup-python@v4 - with: - python-version: "3.8" - - name: Set default for env.NEW_TAG - run: echo "NEW_TAG=NO_NEW_TAG" >> $GITHUB_ENV - - name: Check for new version + - id: get_version + name: Extract new version run: | - make venv - cd ${GITHUB_WORKSPACE}/ && ./venv/bin/python3 .github/scripts/generate_tag.py - - id: new_tag - name: Set new tag as job output - run: | - echo "new_tag=$NEW_TAG" >> $GITHUB_OUTPUT + VERSION="$(perl -ne 'print $1 if /^version\s*=\s*\"(.+?)\"/' pyproject.toml)" + if ! gh release view $VERSION 2>&1 > /dev/null; then + echo "Uploading new version $VERSION if CI passes." + echo "version=$VERSION" >> $GITHUB_OUTPUT + fi publish: name: Publish runs-on: ubuntu-24.04 needs: [lint, build, test, check_new_version] - if: needs.check_new_version.outputs.new_tag != 'NO_NEW_TAG' + if: needs.check_new_version.outputs.version != '' environment: pypi permissions: # IMPORTANT: this permission is mandatory for Trusted Publishing id-token: write + contents: write steps: - uses: actions/download-artifact@v8 with: name: wheel path: ./dist - - name: Tag Commit - uses: tvdias/github-tagger@v0.0.1 - with: - tag: "${{ needs.check_new_version.outputs.new_tag }}" - repo-token: "${{ secrets.BOT_TOKEN }}" + # 2nd to last because it is immutable but likely to fail, so if it fails + # I don't want PyPI publishing + - name: Create release + run: | + prerelease_arg=() + version="${{ needs.check_new_version.outputs.version }}" + if [[ "$version" = *b* ]] || [[ "$version" = *a* ]] || [[ "$version" = *.dev* ]] || [[ "$version" = *rc* ]]; then + prerelease_arg+=( "--prerelease" ) + fi + + gh release create $version \ + --generate-notes \ + --target "${{ github.ref_name }}" \ + "${prerelease_arg[@]}" \ + ./dist/* + env: + GH_TOKEN: ${{ github.token }} - name: Publish uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/Authors.md b/Authors.md new file mode 100644 index 0000000..bb21b96 --- /dev/null +++ b/Authors.md @@ -0,0 +1,14 @@ +# Authors + +All categories arranged alphabetically. + +> This list may be non-exhaustive and primarily reflects copyright holders for +> significant portions of the code. See +> https://github.com/librelane/librelane/graphs/contributors for a full list of +> human authors. + +* Efabless Corporation \ (until February 2023) + * Mohamed Gaber \ + * Kareem Farid \ +* Leo Moser \ +* Mohamed Gaber \ diff --git a/OSAcknowledgements b/OSAcknowledgements deleted file mode 100644 index 160b963..0000000 --- a/OSAcknowledgements +++ /dev/null @@ -1,9 +0,0 @@ -sky130-builds - -©2021-2022 The American University in Cairo & The Cloud V Project - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/Readme.md b/Readme.md index 85a57f0..6b32f68 100644 --- a/Readme.md +++ b/Readme.md @@ -46,11 +46,11 @@ ciel --version # About the builds In its current inception, ciel supports builds of **sky130** and **gf180mcu** PDKs using [Open-PDKs](https://github.com/rtimothyedwards/open_pdks), including the following libraries: -|sky130|gf180mcu|ihp-sg13g2| +|sky130|gf180mcu|ihp-sg13| |-|-|-| |sky130_fd_io|gf180mcu_fd_io|sg13g2_io| |sky130_fd_pr|gf180mcu_fd_pr|sg13g2_pr| -|sky130_fd_pr_reram|gf180mcu_fd_pr|sg13g2_pr| +|sky130_fd_pr_reram|-|-| |sky130_fd_sc_hd|gf180mcu_fd_sc_mcu7t5v0|sg13g2_stdcell| |sky130_ml_xx_hd|gf180mcu_fd_sc_mcu9t5v0|-| |sky130_fd_sc_hvl|gf180mcu_osu_sc_gp9t3v3|-| diff --git a/ciel/__main__.py b/ciel/__main__.py index a351741..91f03d5 100755 --- a/ciel/__main__.py +++ b/ciel/__main__.py @@ -1,4 +1,4 @@ -# Copyright 2025 The American University in Cairo +# Copyright 2025 Ciel Contributors # # Adapted from the Volare project # diff --git a/ciel/build/__init__.py b/ciel/build/__init__.py index 18918c2..57d47dc 100644 --- a/ciel/build/__init__.py +++ b/ciel/build/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2025 The American University in Cairo +# Copyright 2025 Ciel Contributors # # Adapted from the Volare project # diff --git a/ciel/build/ihp-sg13g2.py b/ciel/build/ihp-sg13.py similarity index 82% rename from ciel/build/ihp-sg13g2.py rename to ciel/build/ihp-sg13.py index 12d8e1d..af8e004 100644 --- a/ciel/build/ihp-sg13g2.py +++ b/ciel/build/ihp-sg13.py @@ -1,3 +1,7 @@ +# Copyright 2025 Ciel Contributors +# +# Adapted from the Volare project +# # Copyright 2022-2023 Efabless Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -68,24 +72,26 @@ def get_ihp( def build_ihp(build_directory, ihp_path): # """Build""" try: - shutil.rmtree(os.path.join(build_directory, "ihp-sg13g2")) + shutil.rmtree(os.path.join(build_directory, "ihp-sg13")) except FileNotFoundError: pass - shutil.copytree( - os.path.join(ihp_path, "ihp-sg13g2"), - os.path.join(build_directory, "ihp-sg13g2"), - ignore=lambda dir, files: ( - files if ".git" in os.path.split(dir) else [".git", ".DS_Store"] - ), - ) + ihp_sg13_family = Family.by_name["ihp-sg13"] + for variant in ihp_sg13_family.variants: + shutil.copytree( + os.path.join(ihp_path, variant), + os.path.join(build_directory, variant), + ignore=lambda dir, files: ( + files if ".git" in os.path.split(dir) else [".git", ".DS_Store"] + ), + ) def install_ihp(build_directory, pdk_root, version): console = Console() with console.status("Adding build to list of installed versions…"): - ihp_sg13g2_family = Family.by_name["ihp-sg13g2"] + ihp_sg13_family = Family.by_name["ihp-sg13"] - version_directory = Version(version, "ihp-sg13g2").get_dir(pdk_root) + version_directory = Version(version, "ihp-sg13").get_dir(pdk_root) if ( os.path.exists(version_directory) and len(os.listdir(version_directory)) != 0 @@ -94,9 +100,7 @@ def install_ihp(build_directory, pdk_root, version): it = 0 while os.path.exists(backup_path) and len(os.listdir(backup_path)) != 0: it += 1 - backup_path = Version(f"{version}.bk{it}", "ihp-sg13g2").get_dir( - pdk_root - ) + backup_path = Version(f"{version}.bk{it}", "ihp-sg13").get_dir(pdk_root) console.log( f"Build already found at {version_directory}, moving to {backup_path}…" ) @@ -105,7 +109,7 @@ def install_ihp(build_directory, pdk_root, version): console.log("Copying…") mkdirp(version_directory) - for variant in ihp_sg13g2_family.variants: + for variant in ihp_sg13_family.variants: variant_build_path = os.path.join(build_directory, variant) variant_install_path = os.path.join(version_directory, variant) if os.path.isdir(variant_build_path): @@ -131,10 +135,8 @@ def build( if using_repos is None: using_repos = {} - build_directory = os.path.join( - get_ciel_dir(pdk_root, "ihp-sg13g2"), "build", version - ) - timestamp = datetime.now().strftime("build_ihp-sg13g2-%Y-%m-%d-%H-%M-%S") + build_directory = os.path.join(get_ciel_dir(pdk_root, "ihp-sg13"), "build", version) + timestamp = datetime.now().strftime("build_ihp-sg13-%Y-%m-%d-%H-%M-%S") log_dir = os.path.join(build_directory, "logs", timestamp) mkdirp(log_dir) diff --git a/ciel/click_common.py b/ciel/click_common.py index ea5a20b..acf7d36 100644 --- a/ciel/click_common.py +++ b/ciel/click_common.py @@ -1,4 +1,4 @@ -# Copyright 2025 The American University in Cairo +# Copyright 2025 Ciel Contributors # # Adapted from the Volare project # diff --git a/ciel/common.py b/ciel/common.py index 8cd6703..29dd3ef 100644 --- a/ciel/common.py +++ b/ciel/common.py @@ -1,4 +1,4 @@ -# Copyright 2025 The American University in Cairo +# Copyright 2025 Ciel Contributors # # Modified from the Volare project # @@ -102,7 +102,7 @@ def resolve_pdk_family(selector: Optional[str]): return None if selector == "ihp_sg13g2": - selector = "ihp-sg13g2" + selector = "ihp-sg13" if selector in Family.by_name: return selector diff --git a/ciel/families.py b/ciel/families.py index e9b825e..09fc697 100644 --- a/ciel/families.py +++ b/ciel/families.py @@ -113,8 +113,8 @@ def resolve_libraries( ], repo=opdks_repo, ) -Family.by_name["ihp-sg13g2"] = Family( - name="ihp-sg13g2", +Family.by_name["ihp-sg13"] = Family( + name="ihp-sg13", variants=["ihp-sg13g2"], all_libraries=[ "sg13g2_io", diff --git a/ciel/github.py b/ciel/github.py index 0ce8ada..63c8e4a 100644 --- a/ciel/github.py +++ b/ciel/github.py @@ -1,4 +1,4 @@ -# Copyright 2025 The American University in Cairo +# Copyright 2025 Ciel Contributors # # Modified from the Volare project # diff --git a/ciel/manage.py b/ciel/manage.py index aa3f124..82f6fdf 100644 --- a/ciel/manage.py +++ b/ciel/manage.py @@ -1,4 +1,4 @@ -# Copyright 2025 The American University in Cairo +# Copyright 2025 Ciel Contributors # # Modified from the Volare project # diff --git a/ciel/source.py b/ciel/source.py index f208aab..1f8dc0a 100644 --- a/ciel/source.py +++ b/ciel/source.py @@ -1,4 +1,4 @@ -# Copyright 2025 The American University in Cairo +# Copyright 2025 Ciel Contributors # # Modified from the Volare project # diff --git a/pyproject.toml b/pyproject.toml index 8724c2b..b7207a8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "ciel" -version = "2.6.1" +version = "3.0.0" description = "An PDK builder/version manager for PDKs in the open_pdks format" authors = ["Mohamed Gaber ", "Efabless Corporation"] readme = "Readme.md" From 353f14af28702c0cd83ef992328fe5ce4c6f9fcd Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Thu, 27 Aug 2026 15:24:18 +0300 Subject: [PATCH 2/7] feat!: per-variant default library inclusions + api cleanup All of these are breaking changes btw: - outputs to non-ttys are now plain-text instead of JSON - Family.default_includes is now a dictionary of patterns to lists, where the patterns are to be matched against a variant to determine the default library set that should be built/pulled - Families now auto-register to both by_name and a new by_variant class dictionaries - Family.resolve_libraries now requires a new argument, variant - enable, fetch, push, and build now take a (pdk_family, pdk_variant) tuple instead of just the PDK family - enable and fetch no longer support automatic pushing - get_ciel_home now returns a pathlib.Path, and so does Version.get_dir - resolve_pdk_family's argument is no longer optional, moved to family.py, added to top-level exports - resolve_pdk_variant moved to families.py, added to top-level exports - remove deprecated method `get()` --- ciel/__init__.py | 11 +++- ciel/__main__.py | 130 ++++++++++++++++++++--------------------- ciel/build/__init__.py | 41 +++++++------ ciel/build/gf180mcu.py | 7 ++- ciel/build/ihp-sg13.py | 6 +- ciel/build/sky130.py | 7 ++- ciel/click_common.py | 13 +++-- ciel/common.py | 92 ++++------------------------- ciel/families.py | 118 ++++++++++++++++++++++++++++++------- ciel/manage.py | 75 +++++++++--------------- 10 files changed, 257 insertions(+), 243 deletions(-) diff --git a/ciel/__init__.py b/ciel/__init__.py index 22c4904..4277d4a 100644 --- a/ciel/__init__.py +++ b/ciel/__init__.py @@ -1,3 +1,7 @@ +# Copyright 2026 Ciel Contributors +# +# Adapted from the Volare Project +# # Copyright 2022-2023 Efabless Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,14 +18,17 @@ from .manage import ( VersionNotFound, enable, - get, fetch, ) from .common import ( get_ciel_home, Version, ) -from .families import Family +from .families import ( + Family, + resolve_pdk_family, + resolve_pdk_variant, +) from .github import ( GitHubSession, ) diff --git a/ciel/__main__.py b/ciel/__main__.py index 91f03d5..58f618a 100755 --- a/ciel/__main__.py +++ b/ciel/__main__.py @@ -29,7 +29,7 @@ get_ciel_home, ) from .click_common import ( - opt_pdk_root, + opt_pdk, arg_version, ) from .manage import ( @@ -49,35 +49,27 @@ @click.command("output") -@opt_pdk_root -def output_cmd(pdk_root, pdk_family): - """Outputs the currently enabled PDK version. - - If not outputting to a tty, the output is either the version string - unembellished, or, if no current version is enabled, an empty output with an - exit code of 1. - """ +@opt_pdk +def output_cmd(pdk_root, pdk_tuple): + """Outputs the currently enabled PDK version.""" + pdk_family, _ = pdk_tuple version = Version.get_current(pdk_root, pdk_family) - if sys.stdout.isatty(): - if version is None: - print( - f"No version of the PDK {pdk_family} is currently enabled at {pdk_root}." - ) - print("Invoke ciel --help for assistance installing and enabling versions.") - exit(1) - else: - print(f"Installed: {pdk_family} v{version.name}") - print("Invoke ciel --help for assistance installing and enabling versions.") - else: - if version is None: - exit(1) - else: - print(version.name, end="") + if version is None: + print( + f"No version of the PDK {pdk_family} is currently enabled at {pdk_root}.", + file=sys.stderr, + ) + print( + "Invoke ciel --help for assistance installing and enabling versions.", + file=sys.stderr, + ) + sys.exit(1) + print(version.name, end="") @click.command("prune") -@opt_pdk_root +@opt_pdk @click.option( "--yes", is_flag=True, @@ -85,9 +77,10 @@ def output_cmd(pdk_root, pdk_family): expose_value=False, prompt="Are you sure? This will delete all non-enabled versions of the PDK from your computer.", ) -def prune_cmd(pdk_root, pdk_family): +def prune_cmd(pdk_root, pdk_tuple): """Removes all PDKs other than, if it exists, the one currently set as 'enabled' in the PDK root.""" + pdk_family, _ = pdk_tuple pdk_versions = Version.get_all_installed(pdk_root, pdk_family) for version in pdk_versions: if version.is_current(pdk_root): @@ -100,7 +93,7 @@ def prune_cmd(pdk_root, pdk_family): @click.command("optimize") -@opt_pdk_root +@opt_pdk @arg_version def optimize_cmd(pdk_root, pdk_family, version): """ @@ -120,8 +113,8 @@ def optimize_cmd(pdk_root, pdk_family, version): @click.command("optimize-all") -@opt_pdk_root -def optimize_all_cmd(pdk_root, pdk_family): +@opt_pdk +def optimize_all_cmd(pdk_root, pdk_tuple): """ [Experimental] This command attempts to save space by converting identical files across variants for all versions of a specific PDK family to symbolic @@ -134,6 +127,7 @@ def optimize_all_cmd(pdk_root, pdk_family): """ recovered = 0 + pdk_family, _ = pdk_tuple for version in Version.get_all_installed(pdk_root, pdk_family): recovered += optimize(pdk_root, version) @@ -142,7 +136,7 @@ def optimize_all_cmd(pdk_root, pdk_family): @click.command("rm") -@opt_pdk_root +@opt_pdk @click.option( "--yes", is_flag=True, @@ -151,9 +145,10 @@ def optimize_all_cmd(pdk_root, pdk_family): prompt="Are you sure? This will delete this version of the PDK from your computer.", ) @arg_version -def rm_cmd(pdk_root, pdk_family, version): +def rm_cmd(pdk_root, pdk_tuple, version): """Removes the PDK version specified.""" + pdk_family, _ = pdk_tuple version_object = Version(version, pdk_family) try: version_object.uninstall(pdk_root) @@ -166,10 +161,16 @@ def rm_cmd(pdk_root, pdk_family, version): @click.command("ls") @opt_data_source @opt_github_token -@opt_pdk_root -def list_cmd(data_source, pdk_root, pdk_family): - """Lists PDK versions that are locally installed. JSON if not outputting to a tty.""" +@opt_pdk +def list_cmd(data_source, pdk_root, pdk_tuple): + """ + Lists PDK versions that are locally installed. + + If not outputting to a tty, each version will be output on its own line + in plain text. + """ + pdk_family, _ = pdk_tuple pdk_versions = Version.get_all_installed(pdk_root, pdk_family) if sys.stdout.isatty(): @@ -182,16 +183,23 @@ def list_cmd(data_source, pdk_root, pdk_family): installed_list=pdk_versions, ) else: - print(json.dumps([version.name for version in pdk_versions]), end="") + for version in pdk_versions: + print(version.name) @click.command("ls-remote") @opt_github_token @opt_data_source -@opt_pdk_root -def list_remote_cmd(data_source, pdk_root, pdk_family): - """Lists PDK versions that are remotely available. JSON if not outputting to a tty.""" +@opt_pdk +def list_remote_cmd(data_source, pdk_root, pdk_tuple): + """ + Lists PDK versions that are remotely available. + + If not outputting to a tty, each version will be output on its own line + in plain text. + """ + pdk_family, _ = pdk_tuple try: pdk_versions = data_source.get_available_versions(pdk_family) @@ -202,40 +210,32 @@ def list_remote_cmd(data_source, pdk_root, pdk_family): for version in pdk_versions: print(version.name) except ValueError as e: - if sys.stdout.isatty(): - console = Console() - console.print(f"[red]{e}") - else: - print(f"{e}", file=sys.stderr) + console = Console(stderr=True) + console.print(f"[red]{e}") sys.exit(-1) except httpx.HTTPStatusError as e: - if sys.stdout.isatty(): - console = Console() - console.print(f"[red]Encountered an error when polling version list: {e}") - else: - print(f"Failed to get version list: {e}", file=sys.stderr) + console = Console(stderr=True) + console.print(f"[red]Encountered an error when polling version list: {e}") sys.exit(-1) except httpx.NetworkError as e: - if sys.stdout.isatty(): - console = Console() - console.print( - "[red]You don't appear to be connected to the Internet. ls-remote cannot be used." - ) - else: - print(f"Failed to connect to remote server: {e}", file=sys.stderr) + console = Console(stderr=True) + console.print( + f"[red]You don't appear to be connected to the Internet. ls-remote cannot be used.: {e}" + ) sys.exit(-1) @click.command("path") -@opt_pdk_root +@opt_pdk @arg_version -def path_cmd(pdk_root, pdk_family, version): +def path_cmd(pdk_root, pdk_tuple, version): """ Prints the path of the ciel PDK root. If a version is provided over the commandline, it prints the path to this version instead. """ + pdk_family, _ = pdk_tuple if version is not None: version = Version(version, pdk_family) print(version.get_dir(pdk_root), end="") @@ -246,7 +246,7 @@ def path_cmd(pdk_root, pdk_family, version): @click.command("enable") @opt_data_source @opt_github_token -@opt_pdk_root +@opt_pdk @click.option( "-l", "--include-libraries", @@ -258,7 +258,7 @@ def path_cmd(pdk_root, pdk_family, version): def enable_cmd( data_source, pdk_root, - pdk_family, + pdk_tuple, version, include_libraries, ): @@ -274,7 +274,7 @@ def enable_cmd( try: enable( pdk_root, - pdk_family, + pdk_tuple, version, include_libraries=include_libraries, output=console, @@ -288,7 +288,7 @@ def enable_cmd( @click.command("fetch") @opt_data_source @opt_github_token -@opt_pdk_root +@opt_pdk @click.option( "-l", "--include-libraries", @@ -300,7 +300,7 @@ def enable_cmd( def fetch_cmd( data_source, pdk_root, - pdk_family, + pdk_tuple, version, include_libraries, ): @@ -316,10 +316,10 @@ def fetch_cmd( try: version = fetch( + pdk_root, + pdk_tuple, + version, data_source=data_source, - pdk_root=pdk_root, - pdk=pdk_family, - version=version, include_libraries=include_libraries, output=console, ) diff --git a/ciel/build/__init__.py b/ciel/build/__init__.py index 57d47dc..2f55703 100644 --- a/ciel/build/__init__.py +++ b/ciel/build/__init__.py @@ -22,7 +22,7 @@ import tempfile import importlib import subprocess -from typing import Optional, List, Dict +from typing import Optional, List, Dict, Tuple import click import zstandard as zstd @@ -42,7 +42,7 @@ from ..click_common import ( opt_push, opt_build, - opt_pdk_root, + opt_pdk, arg_version, ) from ..families import Family @@ -50,7 +50,7 @@ def build( pdk_root: str, - pdk_family: str, + pdk_tuple: Tuple[str, str], version: str, jobs: int = 1, sram: bool = True, # Deprecated @@ -58,6 +58,8 @@ def build( include_libraries: Optional[List[str]] = None, use_repo_at: Optional[List[str]] = None, ): + pdk_family, pdk_variant = pdk_tuple + use_repos = {} if use_repo_at is not None: for repo in use_repo_at: @@ -69,6 +71,7 @@ def build( kwargs = { "pdk_root": pdk_root, + "pdk_variant": pdk_variant, "version": version, "jobs": jobs, "clear_build_artifacts": clear_build_artifacts, @@ -83,14 +86,14 @@ def build( @click.command("build") @opt_github_token -@opt_pdk_root +@opt_pdk @opt_build @arg_version def build_cmd( include_libraries, jobs, pdk_root, - pdk_family, + pdk_tuple, clear_build_artifacts, version, use_repo_at, @@ -109,7 +112,7 @@ def build_cmd( build( pdk_root=pdk_root, - pdk_family=pdk_family, + pdk_tuple=pdk_tuple, version=version, jobs=jobs, clear_build_artifacts=clear_build_artifacts, @@ -120,7 +123,7 @@ def build_cmd( def push( pdk_root, - pdk_family, + pdk_tuple, version, *, owner, @@ -128,7 +131,11 @@ def push( pre=False, push_libraries=None, ): - family = Family.by_name[pdk_family] + # variant doesn't matter, we're pushing whatever we can unless an explicit + # list is provided + pdk_family_name, _ = pdk_tuple + + pdk_family = Family.by_name[pdk_family_name] session = GitHubSession() if session.github_token is None: @@ -137,10 +144,10 @@ def push( console = Console() if push_libraries is None or len(push_libraries) == 0: - push_libraries = family.all_libraries + push_libraries = pdk_family.all_libraries library_list = set(push_libraries) - version_object = Version(version, pdk_family) + version_object = Version(version, pdk_family_name) version_directory = version_object.get_dir(pdk_root) if not os.path.isdir(version_directory): raise FileNotFoundError(f"Version {version} not found.") @@ -181,15 +188,15 @@ def push( progress.remove_task(task) final_tarballs.append(tarball_path) - tag = f"{pdk_family}-{version}" + tag = f"{pdk_family_name}-{version}" # If someone wants to rewrite this to not use ghr, please, by all means. console.log("Starting upload…") - body = f"{pdk_family} variants built using ciel" - date = get_commit_date(version, family.repo, session) + body = f"{pdk_family_name} variants built using ciel" + date = get_commit_date(version, pdk_family.repo, session) if date is not None: - body = f"{pdk_family} variants (released on {date_to_iso8601(date)})" + body = f"{pdk_family_name} variants (released on {date_to_iso8601(date)})" for tarball_path in final_tarballs: subprocess.check_call( @@ -218,7 +225,7 @@ def push( @click.command("push", hidden=True) @opt_github_token -@opt_pdk_root +@opt_pdk @opt_push @click.argument("version") def push_cmd( @@ -226,7 +233,7 @@ def push_cmd( repository, pre, pdk_root, - pdk_family, + pdk_tuple, version, push_libraries, ): @@ -241,7 +248,7 @@ def push_cmd( try: push( pdk_root, - pdk_family, + pdk_tuple, version, owner=owner, repository=repository, diff --git a/ciel/build/gf180mcu.py b/ciel/build/gf180mcu.py index 62022d7..7886312 100644 --- a/ciel/build/gf180mcu.py +++ b/ciel/build/gf180mcu.py @@ -1,3 +1,7 @@ +# Copyright 2026 Ciel Contributors +# +# Adapted from Volare +# # Copyright 2022-2023 Efabless Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -225,6 +229,7 @@ def install_gf180mcu(build_directory, pdk_root, version): def build( pdk_root: str, + pdk_variant: str, version: str, jobs: int = 1, clear_build_artifacts: bool = True, @@ -232,7 +237,7 @@ def build( using_repos: Optional[Dict[str, str]] = None, ): family = Family.by_name["gf180mcu"] - library_set = family.resolve_libraries(include_libraries) + library_set = family.resolve_libraries(include_libraries, pdk_variant) if using_repos is None: using_repos = {} diff --git a/ciel/build/ihp-sg13.py b/ciel/build/ihp-sg13.py index af8e004..0b5c61e 100644 --- a/ciel/build/ihp-sg13.py +++ b/ciel/build/ihp-sg13.py @@ -71,11 +71,12 @@ def get_ihp( def build_ihp(build_directory, ihp_path): # """Build""" + ihp_sg13_family = Family.by_name["ihp-sg13"] try: - shutil.rmtree(os.path.join(build_directory, "ihp-sg13")) + for variant in ihp_sg13_family.variants: + shutil.rmtree(os.path.join(build_directory, variant)) except FileNotFoundError: pass - ihp_sg13_family = Family.by_name["ihp-sg13"] for variant in ihp_sg13_family.variants: shutil.copytree( os.path.join(ihp_path, variant), @@ -120,6 +121,7 @@ def install_ihp(build_directory, pdk_root, version): def build( pdk_root: str, + pdk_variant: str, version: str, jobs: int = 1, clear_build_artifacts: bool = True, diff --git a/ciel/build/sky130.py b/ciel/build/sky130.py index 6f25432..83bdd1d 100644 --- a/ciel/build/sky130.py +++ b/ciel/build/sky130.py @@ -1,3 +1,7 @@ +# Copyright 2026 Ciel Contributors +# +# Adapted from Volare +# # Copyright 2022-2023 Efabless Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -315,6 +319,7 @@ def install_sky130(build_directory, pdk_root, version): def build( pdk_root: str, + pdk_variant: str, version: str, jobs: int = 1, clear_build_artifacts: bool = True, @@ -322,7 +327,7 @@ def build( using_repos: Optional[Dict[str, str]] = None, ): family = Family.by_name["sky130"] - library_set = family.resolve_libraries(include_libraries) + library_set = family.resolve_libraries(include_libraries, pdk_variant) if using_repos is None: using_repos = {} diff --git a/ciel/click_common.py b/ciel/click_common.py index acf7d36..13de6e7 100644 --- a/ciel/click_common.py +++ b/ciel/click_common.py @@ -22,10 +22,9 @@ from .common import ( CIEL_RESOLVED_HOME, - resolve_pdk_family, resolve_version, ) -from .families import Family +from .families import Family, resolve_pdk_family, resolve_pdk_variant opt = partial(click.option, show_default=True) @@ -93,21 +92,23 @@ def process_value(self, ctx: click.Context, value): value = self.callback(ctx, self, value) try: - resolved = resolve_pdk_family(value) + family = resolve_pdk_family(value) + variant = resolve_pdk_variant(value) except ValueError as e: raise click.BadParameter(str(e), ctx=ctx, param=self) - return resolved + return (family, variant) -def opt_pdk_root(function: Callable): +def opt_pdk(function: Callable): function = opt( "--pdk-family", "--pdk", + "pdk_tuple", cls=PDKOption, required=True, envvar=["PDK_FAMILY", "PDK"], - help="A valid PDK family or variant (the latter of which is resolved to a family). If the environment PDK_FAMILY or PDK are set, they are used as secondary sources for this value.", + help="A valid PDK family or variant. If the environment PDK_FAMILY or PDK are set, they are used as secondary sources for this value.", )(function) function = opt( "--pdk-root", diff --git a/ciel/common.py b/ciel/common.py index 29dd3ef..74d5735 100644 --- a/ciel/common.py +++ b/ciel/common.py @@ -17,8 +17,7 @@ # limitations under the License. import os import shutil -import pathlib -import warnings +from pathlib import Path from datetime import datetime from dataclasses import dataclass from typing import Optional, List @@ -38,7 +37,7 @@ def date_from_iso8601(string: str) -> datetime: def mkdirp(path): - return pathlib.Path(path).mkdir(parents=True, exist_ok=True) + return Path(path).mkdir(parents=True, exist_ok=True) # -- API Variables @@ -61,83 +60,16 @@ def _get_current_version(pdk_root: str, pdk: str) -> Optional[str]: return version -def get_ciel_home(pdk_root: Optional[str] = None) -> str: - return pdk_root or CIEL_RESOLVED_HOME +def get_ciel_home(pdk_root: Optional[str] = None) -> Path: + return Path(pdk_root or CIEL_RESOLVED_HOME) -def get_ciel_dir(pdk_root: str, pdk: str) -> str: - return os.path.join(pdk_root, "ciel", pdk) +def get_ciel_dir(pdk_root: str, pdk: str) -> Path: + return Path(pdk_root) / "ciel" / pdk -def get_versions_dir(pdk_root: str, pdk: str) -> str: - return os.path.join(get_ciel_dir(pdk_root, pdk), "versions") - - -def resolve_pdk_family(selector: Optional[str]): - """ - :returns: - If selector is a valid PDK family, the same string. - - If selector is a valid PDK variant, the family the variant belongs to. - - If selector is None, the PDK_FAMILY and PDK environment variables are - used as fallbacks. If all are None, the function will simply return None. - - Starting Ciel 3.0.0, supplying None will no longer work and the selector - will be a string. - - If the selector is invalid, a ValueError will be raised. "ihp_sg13g2" - will resolve to "ihp-sg13g2" however for some semblance of backwards - compatibility with previous versions of Ciel/Volare. - """ - if selector is None: - warnings.warn( - "Passing None to resolve_pdk_family is deprecated and will be removed in Ciel 3.0.0. Please resolve any environment variables manually.", - DeprecationWarning, - stacklevel=2, - ) - if environment_specified_pdk := os.getenv("PDK_FAMILY") or os.getenv("PDK"): - selector = environment_specified_pdk - if selector is None: - return None - - if selector == "ihp_sg13g2": - selector = "ihp-sg13" - - if selector in Family.by_name: - return selector - - for pdk_family in Family.by_name.values(): - if selector in pdk_family.variants: - return pdk_family.name - - raise ValueError(f"'{selector}' is not a valid PDK family or variant.") - - -def resolve_pdk_variant(selector: Optional[str]): - """ - :returns: - If selector is a valid PDK variant, the same string. - - If selector is a valid PDK family, the default variant of said PDK. - - If selector is None, the PDK environment variables is used as a - fallback. If all are None, the function will simply return None. - - If the selector is invalid, a ValueError will be raised. - """ - selector = selector or os.getenv("PDK") - if selector is None: - return None - - if family := Family.by_name.get(selector): - return family.default_variant - - for pdk_family in Family.by_name.values(): - if selector in pdk_family.variants: - return selector - - raise ValueError(f"'{selector}' is not a valid PDK family or variant.") +def get_versions_dir(pdk_root: str, pdk: str) -> Path: + return get_ciel_dir(pdk_root, pdk) / "versions" @dataclass @@ -161,8 +93,8 @@ def is_installed(self, pdk_root: str) -> bool: def is_current(self, pdk_root: str) -> bool: return self.name == _get_current_version(pdk_root, self.pdk) - def get_dir(self, pdk_root: str) -> str: - return os.path.join(get_versions_dir(pdk_root, self.pdk), self.name) + def get_dir(self, pdk_root: str) -> Path: + return get_versions_dir(pdk_root, self.pdk) / self.name def unset_current(self, pdk_root: str): if not self.is_installed(pdk_root): @@ -202,14 +134,14 @@ def get_current(Self, pdk_root: str, pdk: str) -> Optional["Version"]: @classmethod def get_all_installed(Self, pdk_root: str, pdk: str) -> List["Version"]: versions_dir = get_versions_dir(pdk_root, pdk) - mkdirp(versions_dir) + versions_dir.mkdir(parents=True, exist_ok=True) return [ Version( name=version, pdk=pdk, ) for version in os.listdir(versions_dir) - if os.path.isdir(os.path.join(versions_dir, version)) + if (versions_dir / version).is_dir() ] diff --git a/ciel/families.py b/ciel/families.py index 09fc697..93653cc 100644 --- a/ciel/families.py +++ b/ciel/families.py @@ -1,3 +1,7 @@ +# Copyright 2026 Ciel Contributors +# +# Adapted from Volare +# # Copyright 2022-2023 Efabless Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -11,6 +15,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import fnmatch from dataclasses import dataclass from typing import Iterable, List, Dict, Optional, Set, ClassVar @@ -20,6 +25,7 @@ @dataclass class Family(object): by_name: ClassVar[Dict[str, "Family"]] = {} + by_variant: ClassVar[Dict[str, "Family"]] = {} name: str variants: List[str] @@ -27,17 +33,22 @@ class Family(object): repo: RepoInfo # lol no implicitly unwrapped optionals default_variant: str = None # type: ignore - default_includes: List[str] = None # type: ignore + default_includes: Dict[str, List[str]] = None # type: ignore def __post_init__(self): if self.default_variant is None: self.default_variant = self.variants[0] if self.default_includes is None: - self.default_includes = self.all_libraries.copy() + self.default_includes = {"*": self.all_libraries.copy()} + + Family.by_name[self.name] = self + for variant in self.variants: + Family.by_variant[variant] = self def resolve_libraries( self, input: Optional[Iterable[str]], + variant: str, ) -> Set[str]: if input is None: input = ("default",) @@ -47,7 +58,9 @@ def resolve_libraries( final_set = set(self.all_libraries) return final_set elif element.lower() == "default": - final_set = final_set.union(set(self.default_includes)) + for pattern, includes in self.default_includes.items(): + if fnmatch.fnmatch(variant, pattern): + final_set = final_set.union(includes) elif element in self.all_libraries: final_set.add(element) else: @@ -55,8 +68,7 @@ def resolve_libraries( return final_set -Family.by_name = {} -Family.by_name["sky130"] = Family( +Family( name="sky130", variants=["sky130A", "sky130B"], default_variant="sky130A", @@ -74,17 +86,21 @@ def resolve_libraries( "sky130_sram_macros", "sky130_fd_pr_reram", ], - default_includes=[ - "sky130_fd_io", - "sky130_fd_pr", - "sky130_fd_sc_hd", - "sky130_fd_sc_hvl", - "sky130_ml_xx_hd", - "sky130_sram_macros", - ], + default_includes={ + "*": [ + "sky130_fd_io", + "sky130_fd_pr", + "sky130_fd_sc_hd", + "sky130_fd_sc_hvl", + "sky130_ml_xx_hd", + "sky130_sram_macros", + ], + "sky130B": ["sky130_fd_pr_reram"], + }, repo=opdks_repo, ) -Family.by_name["gf180mcu"] = Family( + +Family( name="gf180mcu", variants=["gf180mcuA", "gf180mcuB", "gf180mcuC", "gf180mcuD"], default_variant="gf180mcuD", @@ -104,16 +120,19 @@ def resolve_libraries( "gf180mcu_ocd_alpha_large", "gf180mcu_ocd_alpha_misc", ], - default_includes=[ - "gf180mcu_fd_io", - "gf180mcu_fd_pr", - "gf180mcu_fd_sc_mcu7t5v0", - "gf180mcu_fd_sc_mcu9t5v0", - "gf180mcu_fd_ip_sram", - ], + default_includes={ + "*": [ + "gf180mcu_fd_io", + "gf180mcu_fd_pr", + "gf180mcu_fd_sc_mcu7t5v0", + "gf180mcu_fd_sc_mcu9t5v0", + "gf180mcu_fd_ip_sram", + ] + }, repo=opdks_repo, ) -Family.by_name["ihp-sg13"] = Family( + +Family( name="ihp-sg13", variants=["ihp-sg13g2"], all_libraries=[ @@ -122,5 +141,60 @@ def resolve_libraries( "sg13g2_sram", "sg13g2_stdcell", ], + default_includes={ + "ihp-sg13g2": [ + "sg13g2_io", + "sg13g2_pr", + "sg13g2_sram", + "sg13g2_stdcell", + ], + }, repo=ihp_repo, ) + + +def resolve_pdk_family(selector: str): + """ + :returns: + If selector is a valid PDK family, the same string. + + If selector is a valid PDK variant, the family the variant belongs to. + + If the selector is invalid, a ValueError will be raised. "ihp_sg13g2" + will resolve to "ihp-sg13g2" however for some semblance of backwards + compatibility with previous versions of Ciel/Volare. + """ + if selector == "ihp_sg13g2": + selector = "ihp-sg13" + + if selector in Family.by_name: + return selector + + for pdk_family in Family.by_name.values(): + if selector in pdk_family.variants: + return pdk_family.name + + raise ValueError(f"'{selector}' is not a valid PDK family or variant.") + + +def resolve_pdk_variant(selector: Optional[str]): + """ + :returns: + If selector is a valid PDK variant, the same string. + + If selector is a valid PDK family, the default variant of said PDK. + + If selector is None, the function will simply return None. + + If the selector is invalid, a ValueError will be raised. + """ + if selector is None: + return None + + if selector in Family.by_variant: + return str(selector) + + if family := Family.by_name.get(selector): + return family.default_variant + + raise ValueError(f"'{selector}' is not a valid PDK family or variant.") diff --git a/ciel/manage.py b/ciel/manage.py index 82f6fdf..71df50d 100644 --- a/ciel/manage.py +++ b/ciel/manage.py @@ -21,8 +21,8 @@ import hashlib import tarfile import tempfile -import warnings -from typing import Dict, Iterable, List, Optional, Union +from pathlib import Path +from typing import Dict, Iterable, List, Optional, Union, Tuple import rich import httpx @@ -37,7 +37,7 @@ get_versions_dir, get_ciel_dir, ) -from .build import build, push +from .build import build from .families import Family from .source import DataSource @@ -119,30 +119,30 @@ def print_remote_list( def fetch( pdk_root: str, - pdk: str, + pdk_tuple: Tuple[str, str], version: str, *, data_source: DataSource, build_if_not_found=False, - also_push=False, build_kwargs: dict = {}, - push_kwargs: dict = {}, include_libraries: Optional[Iterable[str]] = None, output: Union[Console, io.TextIOWrapper] = Console(), ) -> Version: + pdk_family_name, pdk_variant_name = pdk_tuple + console = output if not isinstance(console, Console): console = Console(file=console) - version_object = Version(version, pdk) + version_object = Version(version, pdk_family_name) version_directory = version_object.get_dir(pdk_root) - pdk_family = Family.by_name.get(pdk) + pdk_family = Family.by_name.get(pdk_family_name) if pdk_family is None: - raise ValueError(f"Unsupported PDK family '{pdk}'.") + raise ValueError(f"Unsupported PDK family '{pdk_family_name}'.") - library_set = pdk_family.resolve_libraries(include_libraries) + library_set = pdk_family.resolve_libraries(include_libraries, pdk_variant_name) variants = pdk_family.variants @@ -163,7 +163,7 @@ def fetch( if not found: missing_libraries.add(library) - affected_paths = [] + affected_paths: List[Path] = [] if len(missing_libraries) != 0 or common_missing: if common_missing: console.print( @@ -174,10 +174,10 @@ def fetch( console.print(f"Libraries {missing_libraries} not found, downloading them…") for variant in variants: affected_paths.append( - os.path.join(version_directory, variant, "libs.ref", library) + version_directory / variant / "libs.ref" / library ) - tarball_paths = [] + tarball_paths: List[Path] = [] try: client, assets = data_source.get_downloads_for_version(version_object) assets_filtered = [] @@ -186,9 +186,10 @@ def fetch( assets_filtered.append(asset) elif asset.content in missing_libraries: assets_filtered.append(asset) - tarball_directory = tempfile.TemporaryDirectory(suffix=".ciel") + tarball_directory_obj = tempfile.TemporaryDirectory(suffix=".ciel") + tarball_directory = Path(tarball_directory_obj.name) for asset in assets_filtered: - tarball_path = os.path.join(tarball_directory.name, asset.filename) + tarball_path = tarball_directory / asset.filename tarball_paths.append(tarball_path) with client.stream("get", asset.url) as r, rich.progress.Progress( console=console @@ -213,9 +214,8 @@ def fetch( for file in tf: if file.isdir(): continue - final_path = os.path.join(version_directory, file.name) - final_dir = os.path.dirname(final_path) - mkdirp(final_dir) + final_path = version_directory / file.name + final_path.parent.mkdir(parents=True, exist_ok=True) io = tf.extractfile(file) if io is None: raise IOError( @@ -232,21 +232,10 @@ def fetch( ) build( pdk_root=pdk_root, - pdk_family=pdk, + pdk_tuple=pdk_tuple, version=version, **build_kwargs, ) - if also_push: - if push_kwargs["push_libraries"] is None: - push_kwargs["push_libraries"] = Family.by_name[ - pdk - ].default_includes.copy() - push( - pdk_root=pdk_root, - pdk_family=pdk, - version=version, - **push_kwargs, - ) else: if e.response is not None: raise RuntimeError( @@ -279,33 +268,32 @@ def fetch( with open(variant_sources_file, "w") as f: print(f"{pdk_family.repo.name} {version}", file=f) - return Version(version, pdk) + return Version(version, pdk_family_name) def enable( pdk_root: str, - pdk: str, + pdk_tuple: Tuple[str, str], version: str, *, data_source: DataSource, build_if_not_found: bool = False, - also_push: bool = False, build_kwargs: dict = {}, - push_kwargs: dict = {}, include_libraries: Optional[List[str]] = None, output: Union[Console, io.TextIOWrapper] = Console(), ) -> Version: + pdk_family_name, _ = pdk_tuple console = output if not isinstance(console, Console): console = Console(file=console) - version_object = Version(version, pdk) + version_object = Version(version, pdk_family_name) version_directory = version_object.get_dir(pdk_root) - pdk_family = Family.by_name.get(pdk) + pdk_family = Family.by_name.get(pdk_family_name) if pdk_family is None: - raise ValueError(f"Unsupported PDK family '{pdk}'.") + raise ValueError(f"Unsupported PDK family '{pdk_family_name}'.") variants = pdk_family.variants version_paths = [os.path.join(version_directory, variant) for variant in variants] @@ -313,18 +301,16 @@ def enable( fetch( pdk_root, - pdk, + pdk_tuple, version, data_source=data_source, build_if_not_found=build_if_not_found, - also_push=also_push, build_kwargs=build_kwargs, - push_kwargs=push_kwargs, include_libraries=include_libraries, output=output, ) - current_file = os.path.join(get_ciel_dir(pdk_root, pdk), "current") + current_file = os.path.join(get_ciel_dir(pdk_root, pdk_family_name), "current") current_file_dir = os.path.dirname(current_file) mkdirp(current_file_dir) @@ -346,15 +332,10 @@ def enable( with open(current_file, "w") as f: f.write(version) - console.print(f"Version {version} enabled for the {pdk} PDK.") + console.print(f"Version {version} enabled for the {pdk_family_name} PDK.") return version_object -def get(*args, **kwargs): - warnings.warn("get() has been deprecated: use fetch()") - return fetch(*args, **kwargs) - - def optimize(pdk_root, version_object: Version): if os.name != "posix": return 0 From faec8f89b912be54797e2eec07d07ef5ba13757f Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Thu, 27 Aug 2026 16:05:54 +0300 Subject: [PATCH 3/7] feat: support ihp-sg13cmos5l adds new variant to ihp-sg13, three new libraries, and a new default include set if installation for that specific variant is requested --- ciel/build/ihp-sg13.py | 16 +++++++++++++--- ciel/families.py | 10 +++++++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/ciel/build/ihp-sg13.py b/ciel/build/ihp-sg13.py index 0b5c61e..bffacf6 100644 --- a/ciel/build/ihp-sg13.py +++ b/ciel/build/ihp-sg13.py @@ -18,6 +18,7 @@ import os import shutil import subprocess +from pathlib import Path from datetime import datetime from typing import Optional, List, Tuple, Dict from concurrent.futures import ThreadPoolExecutor @@ -71,6 +72,17 @@ def get_ihp( def build_ihp(build_directory, ihp_path): # """Build""" + def filter(dir_s, files): + dir = Path(dir_s) + if dir.name == ".git": + return files + rejects = [".git", ".DS_Store"] + for file in files: + # ignore bad symlinks + if not (Path(dir) / file).resolve().exists(): + rejects.append(file) + return rejects + ihp_sg13_family = Family.by_name["ihp-sg13"] try: for variant in ihp_sg13_family.variants: @@ -81,9 +93,7 @@ def build_ihp(build_directory, ihp_path): shutil.copytree( os.path.join(ihp_path, variant), os.path.join(build_directory, variant), - ignore=lambda dir, files: ( - files if ".git" in os.path.split(dir) else [".git", ".DS_Store"] - ), + ignore=filter, ) diff --git a/ciel/families.py b/ciel/families.py index 93653cc..15a0a48 100644 --- a/ciel/families.py +++ b/ciel/families.py @@ -134,12 +134,15 @@ def resolve_libraries( Family( name="ihp-sg13", - variants=["ihp-sg13g2"], + variants=["ihp-sg13g2", "ihp-sg13cmos5l"], all_libraries=[ "sg13g2_io", "sg13g2_pr", "sg13g2_sram", "sg13g2_stdcell", + "sg13cmos5l_io", + "sg13cmos5l_sram", + "sg13cmos5l_stdcell", ], default_includes={ "ihp-sg13g2": [ @@ -148,6 +151,11 @@ def resolve_libraries( "sg13g2_sram", "sg13g2_stdcell", ], + "ihp-sg13cmos5l": [ + "sg13cmos5l_io", + "sg13cmos5l_sram", + "sg13cmos5l_stdcell", + ], }, repo=ihp_repo, ) From 962517836512f8912e410a5f3e195e66e49d004c Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Fri, 28 Aug 2026 01:38:16 +0300 Subject: [PATCH 4/7] feat: allow GitHub data source to consume old versions named after PDK variant --- ciel/common.py | 1 + ciel/source.py | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/ciel/common.py b/ciel/common.py index 74d5735..e0f7698 100644 --- a/ciel/common.py +++ b/ciel/common.py @@ -79,6 +79,7 @@ class Version(object): commit_date: Optional[datetime] = None upload_date: Optional[datetime] = None prerelease: bool = False + data_source_pdk_override: Optional[str] = None def __lt__(self, rhs: "Version"): return (self.commit_date or datetime.min) < (rhs.commit_date or datetime.min) diff --git a/ciel/source.py b/ciel/source.py index 1f8dc0a..c22e0e9 100644 --- a/ciel/source.py +++ b/ciel/source.py @@ -25,6 +25,7 @@ from .github import GitHubSession, RepoInfo from .common import Version, date_from_iso8601 +from .families import Family @dataclass @@ -56,6 +57,7 @@ def __init__(self, repo_id: str): self.repo = RepoInfo.from_id(repo_id) def get_available_versions(self, pdk: str) -> List[Version]: + pdk_family = Family.by_name[pdk] page = 1 last = self.session.api( self.repo, @@ -80,9 +82,12 @@ def get_available_versions(self, pdk: str) -> List[Version]: if release["draft"]: continue - family, hash = release["tag_name"].rsplit("-", maxsplit=1) + release_family_name, hash = release["tag_name"].rsplit("-", maxsplit=1) - if pdk != family: + if ( + release_family_name != pdk_family.name + and release_family_name not in pdk_family.variants + ): continue upload_date = date_from_iso8601(release["published_at"]) @@ -94,10 +99,11 @@ def get_available_versions(self, pdk: str) -> List[Version]: remote_version = Version( name=hash, - pdk=family, + pdk=pdk_family.name, commit_date=commit_date, upload_date=upload_date, prerelease=release["prerelease"], + data_source_pdk_override=release_family_name, ) versions.append(remote_version) @@ -111,9 +117,11 @@ def get_available_versions(self, pdk: str) -> List[Version]: def get_downloads_for_version( self, version: Version ) -> Tuple[httpx.Client, List[Asset]]: + release_family_name = version.data_source_pdk_override or version.pdk + release = self.session.api( self.repo, - f"/releases/tags/{version.pdk}-{version.name}", + f"/releases/tags/{release_family_name}-{version.name}", "get", ) From 89ae2693cd044f2b9b57245837e8e6e4d09693b5 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Mon, 7 Sep 2026 20:39:41 +0300 Subject: [PATCH 5/7] nvm we're squashing this --- .flake8 | 12 --- Makefile | 5 +- ciel/__init__.py | 14 +++ ciel/__main__.py | 54 +++++----- ciel/__version__.py | 4 +- ciel/build/__init__.py | 50 +++++---- ciel/build/common.py | 8 +- ciel/build/gf180mcu.py | 81 +++++++------- ciel/build/git_multi_clone.py | 29 +++-- ciel/build/ihp-sg13.py | 34 +++--- ciel/build/sky130.py | 91 ++++++++-------- ciel/click_common.py | 9 +- ciel/common.py | 9 +- ciel/families.py | 8 +- ciel/github.py | 18 ++-- ciel/manage.py | 53 ++++++---- ciel/source.py | 28 +++-- poetry.lock | 193 +++++++--------------------------- pyproject.toml | 28 +++-- type_stubs/pcpp.pyi | 7 +- 20 files changed, 345 insertions(+), 390 deletions(-) delete mode 100644 .flake8 mode change 100755 => 100644 ciel/__main__.py diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 32394a6..0000000 --- a/.flake8 +++ /dev/null @@ -1,12 +0,0 @@ -[flake8] -ignore = E203,E231,E266,E302,E501,W503 -exclude = - .git, - __pycache__, - build/, - pdk/ - venv/, - .venv/, - !ciel/build -per-file-ignores = - */__init__.py:F401 diff --git a/Makefile b/Makefile index 7ff983e..006102d 100644 --- a/Makefile +++ b/Makefile @@ -9,9 +9,8 @@ dist: venv/manifest.txt .PHONY: lint lint: venv/manifest.txt - ./venv/bin/black --check . - ./venv/bin/flake8 . - ./venv/bin/mypy --check-untyped-defs . + ./venv/bin/ruff format --check . + ./venv/bin/ruff check venv: venv/manifest.txt venv/manifest.txt: ./pyproject.toml diff --git a/ciel/__init__.py b/ciel/__init__.py index 4277d4a..1534ad4 100644 --- a/ciel/__init__.py +++ b/ciel/__init__.py @@ -34,3 +34,17 @@ ) from .build import build from .__version__ import __version__ + +__all__ = [ + "Family", + "GitHubSession", + "Version", + "VersionNotFound", + "__version__", + "build", + "enable", + "fetch", + "get_ciel_home", + "resolve_pdk_family", + "resolve_pdk_variant", +] diff --git a/ciel/__main__.py b/ciel/__main__.py old mode 100755 new mode 100644 index 58f618a..c25210d --- a/ciel/__main__.py +++ b/ciel/__main__.py @@ -45,15 +45,15 @@ ) from .github import opt_github_token from .source import opt_data_source -from .families import Family +from .families import Family, resolve_pdk_family @click.command("output") @opt_pdk -def output_cmd(pdk_root, pdk_tuple): +def output_cmd(pdk_root, pdk_selector): """Outputs the currently enabled PDK version.""" - pdk_family, _ = pdk_tuple + pdk_family = resolve_pdk_family(pdk_selector) version = Version.get_current(pdk_root, pdk_family) if version is None: print( @@ -64,7 +64,7 @@ def output_cmd(pdk_root, pdk_tuple): "Invoke ciel --help for assistance installing and enabling versions.", file=sys.stderr, ) - sys.exit(1) + sys.sys.exit(1) print(version.name, end="") @@ -77,10 +77,10 @@ def output_cmd(pdk_root, pdk_tuple): expose_value=False, prompt="Are you sure? This will delete all non-enabled versions of the PDK from your computer.", ) -def prune_cmd(pdk_root, pdk_tuple): +def prune_cmd(pdk_root, pdk_selector): """Removes all PDKs other than, if it exists, the one currently set as 'enabled' in the PDK root.""" - pdk_family, _ = pdk_tuple + pdk_family = resolve_pdk_family(pdk_selector) pdk_versions = Version.get_all_installed(pdk_root, pdk_family) for version in pdk_versions: if version.is_current(pdk_root): @@ -114,7 +114,7 @@ def optimize_cmd(pdk_root, pdk_family, version): @click.command("optimize-all") @opt_pdk -def optimize_all_cmd(pdk_root, pdk_tuple): +def optimize_all_cmd(pdk_root, pdk_selector): """ [Experimental] This command attempts to save space by converting identical files across variants for all versions of a specific PDK family to symbolic @@ -127,7 +127,7 @@ def optimize_all_cmd(pdk_root, pdk_tuple): """ recovered = 0 - pdk_family, _ = pdk_tuple + pdk_family = resolve_pdk_family(pdk_selector) for version in Version.get_all_installed(pdk_root, pdk_family): recovered += optimize(pdk_root, version) @@ -145,24 +145,24 @@ def optimize_all_cmd(pdk_root, pdk_tuple): prompt="Are you sure? This will delete this version of the PDK from your computer.", ) @arg_version -def rm_cmd(pdk_root, pdk_tuple, version): +def rm_cmd(pdk_root, pdk_selector, version): """Removes the PDK version specified.""" - pdk_family, _ = pdk_tuple + pdk_family = resolve_pdk_family(pdk_selector) version_object = Version(version, pdk_family) try: version_object.uninstall(pdk_root) print(f"Deleted {version}.") except Exception as e: print(f"Failed to delete: {e}", file=sys.stderr) - exit(1) + sys.exit(1) @click.command("ls") @opt_data_source @opt_github_token @opt_pdk -def list_cmd(data_source, pdk_root, pdk_tuple): +def list_cmd(data_source, pdk_root, pdk_selector): """ Lists PDK versions that are locally installed. @@ -170,7 +170,7 @@ def list_cmd(data_source, pdk_root, pdk_tuple): in plain text. """ - pdk_family, _ = pdk_tuple + pdk_family = resolve_pdk_family(pdk_selector) pdk_versions = Version.get_all_installed(pdk_root, pdk_family) if sys.stdout.isatty(): @@ -191,7 +191,7 @@ def list_cmd(data_source, pdk_root, pdk_tuple): @opt_github_token @opt_data_source @opt_pdk -def list_remote_cmd(data_source, pdk_root, pdk_tuple): +def list_remote_cmd(data_source, pdk_root, pdk_selector): """ Lists PDK versions that are remotely available. @@ -199,7 +199,7 @@ def list_remote_cmd(data_source, pdk_root, pdk_tuple): in plain text. """ - pdk_family, _ = pdk_tuple + pdk_family = resolve_pdk_family(pdk_selector) try: pdk_versions = data_source.get_available_versions(pdk_family) @@ -212,30 +212,30 @@ def list_remote_cmd(data_source, pdk_root, pdk_tuple): except ValueError as e: console = Console(stderr=True) console.print(f"[red]{e}") - sys.exit(-1) + sys.sys.exit(-1) except httpx.HTTPStatusError as e: console = Console(stderr=True) console.print(f"[red]Encountered an error when polling version list: {e}") - sys.exit(-1) + sys.sys.exit(-1) except httpx.NetworkError as e: console = Console(stderr=True) console.print( f"[red]You don't appear to be connected to the Internet. ls-remote cannot be used.: {e}" ) - sys.exit(-1) + sys.sys.exit(-1) @click.command("path") @opt_pdk @arg_version -def path_cmd(pdk_root, pdk_tuple, version): +def path_cmd(pdk_root, pdk_selector, version): """ Prints the path of the ciel PDK root. If a version is provided over the commandline, it prints the path to this version instead. """ - pdk_family, _ = pdk_tuple + pdk_family = resolve_pdk_family(pdk_selector) if version is not None: version = Version(version, pdk_family) print(version.get_dir(pdk_root), end="") @@ -258,7 +258,7 @@ def path_cmd(pdk_root, pdk_tuple, version): def enable_cmd( data_source, pdk_root, - pdk_tuple, + pdk_selector, version, include_libraries, ): @@ -274,7 +274,7 @@ def enable_cmd( try: enable( pdk_root, - pdk_tuple, + pdk_selector, version, include_libraries=include_libraries, output=console, @@ -282,7 +282,7 @@ def enable_cmd( ) except Exception as e: console.print(f"[red]{e}") - exit(-1) + sys.exit(-1) @click.command("fetch") @@ -300,7 +300,7 @@ def enable_cmd( def fetch_cmd( data_source, pdk_root, - pdk_tuple, + pdk_selector, version, include_libraries, ): @@ -317,7 +317,7 @@ def fetch_cmd( try: version = fetch( pdk_root, - pdk_tuple, + pdk_selector, version, data_source=data_source, include_libraries=include_libraries, @@ -327,7 +327,7 @@ def fetch_cmd( except Exception as e: console.print(f"[red]{e}") - exit(-1) + sys.exit(-1) @click.command("ls-pdks") @@ -399,7 +399,7 @@ def cli(): file=sys.stderr, ) print("This is a fatal error. Ciel will now quit.", file=sys.stderr) - exit(-1) + sys.exit(-1) if __name__ == "__main__": diff --git a/ciel/__version__.py b/ciel/__version__.py index cf5f027..587a56f 100644 --- a/ciel/__version__.py +++ b/ciel/__version__.py @@ -26,7 +26,9 @@ def __get_version(): repo_directory = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) pyproject_path = os.path.join(repo_directory, "pyproject.toml") try: - match = rx.search(open(pyproject_path, encoding="utf8").read()) + with open(pyproject_path, encoding="utf8") as f: + pyproject_toml_str = f.read() + match = rx.search(pyproject_toml_str) assert match is not None, "pyproject.toml found, but without a version" return match[1] except FileNotFoundError: diff --git a/ciel/build/__init__.py b/ciel/build/__init__.py index 2f55703..a8bcb79 100644 --- a/ciel/build/__init__.py +++ b/ciel/build/__init__.py @@ -16,13 +16,14 @@ # See the License for the specific language governing permissions and # limitations under the License. import os +import sys import uuid import pathlib import tarfile import tempfile import importlib import subprocess -from typing import Optional, List, Dict, Tuple +from typing import Optional, List, Dict, Tuple, Union import click import zstandard as zstd @@ -45,12 +46,12 @@ opt_pdk, arg_version, ) -from ..families import Family +from ..families import Family, resolve_pdk_selector def build( pdk_root: str, - pdk_tuple: Tuple[str, str], + pdk: Union[str, Tuple[str, str]], version: str, jobs: int = 1, sram: bool = True, # Deprecated @@ -58,7 +59,10 @@ def build( include_libraries: Optional[List[str]] = None, use_repo_at: Optional[List[str]] = None, ): - pdk_family, pdk_variant = pdk_tuple + if isinstance(pdk, tuple): + pdk_family_name, pdk_variant_name = pdk + else: + pdk_family_name, pdk_variant_name = resolve_pdk_selector(pdk) use_repos = {} if use_repo_at is not None: @@ -66,12 +70,12 @@ def build( name, path = repo.split("=") use_repos[name] = os.path.abspath(path) - if pdk_family not in Family.by_name: - raise Exception(f"Unsupported PDK family '{pdk_family}'.") + if pdk_family_name not in Family.by_name: + raise ValueError(f"Unsupported PDK family '{pdk_family_name}'.") kwargs = { "pdk_root": pdk_root, - "pdk_variant": pdk_variant, + "pdk_variant": pdk_variant_name, "version": version, "jobs": jobs, "clear_build_artifacts": clear_build_artifacts, @@ -79,7 +83,7 @@ def build( "using_repos": use_repos, } - build_module = importlib.import_module(f".{pdk_family}", package=__name__) + build_module = importlib.import_module(f".{pdk_family_name}", package=__name__) build_function = build_module.build build_function(**kwargs) @@ -93,7 +97,7 @@ def build_cmd( include_libraries, jobs, pdk_root, - pdk_tuple, + pdk, clear_build_artifacts, version, use_repo_at, @@ -112,7 +116,7 @@ def build_cmd( build( pdk_root=pdk_root, - pdk_tuple=pdk_tuple, + pdk=pdk, version=version, jobs=jobs, clear_build_artifacts=clear_build_artifacts, @@ -123,7 +127,7 @@ def build_cmd( def push( pdk_root, - pdk_tuple, + pdk, version, *, owner, @@ -133,7 +137,10 @@ def push( ): # variant doesn't matter, we're pushing whatever we can unless an explicit # list is provided - pdk_family_name, _ = pdk_tuple + if isinstance(pdk, tuple): + pdk_family_name, _ = pdk + else: + pdk_family_name, _ = resolve_pdk_selector(pdk) pdk_family = Family.by_name[pdk_family_name] @@ -178,12 +185,13 @@ def push( for name, files in collections.items(): tarball_path = os.path.join(tarball_directory, f"{name}.tar.zst") task = progress.add_task(f"Compressing {name}…", total=len(files)) - with zstd.open(tarball_path, mode="wb") as stream: - with tarfile.TarFile(fileobj=stream, mode="w") as tf: - for i, file in enumerate(files): - progress.update(task, completed=i + 1) - path_in_tarball = os.path.relpath(file, version_directory) - tf.add(file, arcname=path_in_tarball) + with zstd.open(tarball_path, mode="wb") as stream, tarfile.TarFile( + fileobj=stream, mode="w" + ) as tf: + for i, file in enumerate(files): + progress.update(task, completed=i + 1) + path_in_tarball = os.path.relpath(file, version_directory) + tf.add(file, arcname=path_in_tarball) console.log(f"\nCompressed to {tarball_path}.") progress.remove_task(task) final_tarballs.append(tarball_path) @@ -233,7 +241,7 @@ def push_cmd( repository, pre, pdk_root, - pdk_tuple, + pdk, version, push_libraries, ): @@ -248,7 +256,7 @@ def push_cmd( try: push( pdk_root, - pdk_tuple, + pdk, version, owner=owner, repository=repository, @@ -257,4 +265,4 @@ def push_cmd( ) except Exception as e: console.print(f"[red]Failed to push version: {e}") - exit(-1) + sys.exit(-1) diff --git a/ciel/build/common.py b/ciel/build/common.py index f0bbb56..d4ecdd7 100644 --- a/ciel/build/common.py +++ b/ciel/build/common.py @@ -13,6 +13,7 @@ # limitations under the License. import os import re +import sys import shutil import subprocess @@ -66,8 +67,9 @@ def open_pdks_patch_gnu_sed(at_path: str): shutil.move(at_path, backup_path) with open(backup_path, "r") as file_in, open(at_path, "w") as file_out: - for line in file_in: - file_out.write(line.replace("${SED} -i ", "${SED} -i.bak ")) + file_out.writelines( + line.replace("${SED} -i ", "${SED} -i.bak ") for line in file_in + ) def patch_open_pdks(at_path: str): @@ -98,7 +100,7 @@ def is_ancestor(commit: str): print( f"Commit {head} cannot be built using Ciel: the minimum version of open_pdks buildable with Ciel is 1.0.381." ) - exit(-1) + sys.exit(-1) gf180mcu_sources_ok = is_ancestor("c1e2118846fd216b2c065a216950e75d2d67ccb8") if not gf180mcu_sources_ok: diff --git a/ciel/build/gf180mcu.py b/ciel/build/gf180mcu.py index 7886312..9cd17f2 100644 --- a/ciel/build/gf180mcu.py +++ b/ciel/build/gf180mcu.py @@ -17,6 +17,7 @@ # limitations under the License. import os import io +import sys import json import shlex import shutil @@ -50,18 +51,19 @@ def get_open_pdks( open_pdks_repo = None if repo_path is None: - with Progress() as progress: - with ThreadPoolExecutor(max_workers=jobs) as executor: - gmc = GitMultiClone(build_directory, progress) - open_pdks_future = executor.submit( - GitMultiClone.clone, - gmc, - opdks_repo.link, - version, - default_branch="main", - ) - open_pdks_repo = open_pdks_future.result() - repo_path = open_pdks_repo.path + with Progress() as progress, ThreadPoolExecutor( + max_workers=jobs + ) as executor: + gmc = GitMultiClone(build_directory, progress) + open_pdks_future = executor.submit( + GitMultiClone.clone, + gmc, + opdks_repo.link, + version, + default_branch="main", + ) + open_pdks_repo = open_pdks_future.result() + repo_path = open_pdks_repo.path console.log(f"Done fetching {open_pdks_repo.name}.") else: @@ -70,7 +72,8 @@ def get_open_pdks( patch_open_pdks(repo_path) try: - json_raw = open(f"{repo_path}/gf180mcu/gf180mcu.json").read() + with open(f"{repo_path}/gf180mcu/gf180mcu.json", encoding="utf8") as f: + json_raw = f.read() cpp = pcpp.Preprocessor() cpp.line_directive = None cpp.parse(json_raw) @@ -95,7 +98,7 @@ def get_open_pdks( except subprocess.CalledProcessError as e: print(e) print(e.stderr) - exit(-1) + sys.exit(-1) LIB_FLAG_MAP = { @@ -124,29 +127,29 @@ def build_variants( console = Console() def run_sh(script, log_to): - output_file = open(log_to, "w") - try: - subprocess.check_call( - ["sh", "-c", script], - cwd=open_pdks_path, - stdout=output_file, - stderr=output_file, - stdin=open(os.devnull), - ) - except subprocess.CalledProcessError as e: - console.log( - f"An error occurred while building the PDK. Check {log_to} for more information." - ) - raise e - - library_flags = set([LIB_FLAG_MAP[library] for library in include_libraries]) - library_flags_disable = set( - [ - LIB_FLAG_MAP[library].replace("enable", "disable") - for library in LIB_FLAG_MAP - if library not in include_libraries - ] - ) + with open(log_to, "w", encoding="utf8") as output_file, open( + os.devnull, encoding="utf8" + ) as devnull: + try: + subprocess.check_call( + ["sh", "-c", script], + cwd=open_pdks_path, + stdout=output_file, + stderr=output_file, + stdin=devnull, + ) + except subprocess.CalledProcessError as e: + console.log( + f"An error occurred while building the PDK. Check {log_to} for more information." + ) + raise e from None + + library_flags = {LIB_FLAG_MAP[library] for library in include_libraries} + library_flags_disable = { + LIB_FLAG_MAP[library].replace("enable", "disable") + for library in LIB_FLAG_MAP + if library not in include_libraries + } magic_dirname = os.path.dirname(magic_bin) configuration_flags = ["--enable-gf180mcu-pdk", "--with-reference"] + list( @@ -192,7 +195,7 @@ def run_sh(script, log_to): except subprocess.CalledProcessError as e: print(e) print(e.stderr) - exit(-1) + sys.exit(-1) def install_gf180mcu(build_directory, pdk_root, version): @@ -257,7 +260,7 @@ def build( magic_bin = shutil.which("magic") if magic_bin is None: print("Magic is either not installed or not in PATH.") - exit(-1) + sys.exit(-1) build_variants( magic_bin, diff --git a/ciel/build/git_multi_clone.py b/ciel/build/git_multi_clone.py index 48e91ed..927a5f3 100644 --- a/ciel/build/git_multi_clone.py +++ b/ciel/build/git_multi_clone.py @@ -22,7 +22,7 @@ from ..common import mkdirp -class Repository(object): +class Repository: @classmethod def from_path(Self, path): name = os.path.basename(path) @@ -30,9 +30,11 @@ def from_path(Self, path): ["git", "remote", "get-url", "origin"], stderr=subprocess.PIPE ).strip() - remote_branch_info = open( - os.path.join(path, ".git", "refs", "remotes", "origin", "HEAD") - ).read() + with open( + os.path.join(path, ".git", "refs", "remotes", "origin", "HEAD"), + encoding="utf8", + ) as f: + remote_branch_info = f.read() remote_branch = os.path.basename(remote_branch_info) return Self(name, url, path, remote_branch) @@ -78,9 +80,8 @@ def clone(self, callback=None): break if char_read in ["\n", "\r"]: match = ro_rx.search(buffer) - if match is not None: - if callback is not None: - callback(int(match[1])) + if match is not None and callback is not None: + callback(int(match[1])) buffer = "" else: buffer += char_read @@ -122,9 +123,8 @@ def pull(self, callback=None): break if char_read in ["\n", "\r"]: match = ro_rx.search(buffer) - if match is not None: - if callback is not None: - callback(int(match[1])) + if match is not None and callback is not None: + callback(int(match[1])) buffer = "" else: buffer += char_read @@ -145,7 +145,7 @@ def checkout_commit(self, commit: str): cwd=self.path, stderr=subprocess.PIPE, ) - except Exception: + except Exception: # noqa: S110 pass subprocess.check_output( ["git", "checkout", "-f", "-b", "current", commit], @@ -177,9 +177,8 @@ def init_submodule(self, submodule: Optional[str] = None, callback=None): break if char_read in ["\n", "\r"]: match = ro_rx.search(buffer) - if match is not None: - if callback is not None: - callback(int(match[1])) + if match is not None and callback is not None: + callback(int(match[1])) buffer = "" else: buffer += char_read @@ -187,7 +186,7 @@ def init_submodule(self, submodule: Optional[str] = None, callback=None): process.wait() -class GitMultiClone(object): +class GitMultiClone: progress: Progress def __init__(self, folder, progress): diff --git a/ciel/build/ihp-sg13.py b/ciel/build/ihp-sg13.py index bffacf6..44d0670 100644 --- a/ciel/build/ihp-sg13.py +++ b/ciel/build/ihp-sg13.py @@ -16,6 +16,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import os +import sys import shutil import subprocess from pathlib import Path @@ -43,21 +44,22 @@ def get_ihp( console = Console() if repo_path is None: - with Progress() as progress: - with ThreadPoolExecutor(max_workers=jobs) as executor: - gmc = GitMultiClone(build_directory, progress) - ihp_future = executor.submit( - GitMultiClone.clone, - gmc, - ihp_repo.link, - version, - ) - repo = ihp_future.result() - current_task = progress.add_task("Updating submodules…", total=100) - repo.init_submodule( - callback=lambda x: progress.update(current_task, completed=x) - ) - repo_path = repo.path + with Progress() as progress, ThreadPoolExecutor( + max_workers=jobs + ) as executor: + gmc = GitMultiClone(build_directory, progress) + ihp_future = executor.submit( + GitMultiClone.clone, + gmc, + ihp_repo.link, + version, + ) + repo = ihp_future.result() + current_task = progress.add_task("Updating submodules…", total=100) + repo.init_submodule( + callback=lambda x: progress.update(current_task, completed=x) + ) + repo_path = repo.path console.log(f"Done fetching {ihp_repo.name}.") else: console.log(f"Using IHP-Open-PDK at {repo_path} unaltered.") @@ -67,7 +69,7 @@ def get_ihp( except subprocess.CalledProcessError as e: print(e) print(e.stderr) - exit(-1) + sys.exit(-1) def build_ihp(build_directory, ihp_path): diff --git a/ciel/build/sky130.py b/ciel/build/sky130.py index 83bdd1d..c2bd5a1 100644 --- a/ciel/build/sky130.py +++ b/ciel/build/sky130.py @@ -17,6 +17,7 @@ # limitations under the License. import os import io +import sys import json import venv import shlex @@ -51,18 +52,19 @@ def get_open_pdks( open_pdks_repo = None if repo_path is None: - with Progress() as progress: - with ThreadPoolExecutor(max_workers=jobs) as executor: - gmc = GitMultiClone(build_directory, progress) - open_pdks_future = executor.submit( - GitMultiClone.clone, - gmc, - opdks_repo.link, - version, - default_branch="main", - ) - open_pdks_repo = open_pdks_future.result() - repo_path = open_pdks_repo.path + with Progress() as progress, ThreadPoolExecutor( + max_workers=jobs + ) as executor: + gmc = GitMultiClone(build_directory, progress) + open_pdks_future = executor.submit( + GitMultiClone.clone, + gmc, + opdks_repo.link, + version, + default_branch="main", + ) + open_pdks_repo = open_pdks_future.result() + repo_path = open_pdks_repo.path console.log(f"Done fetching {open_pdks_repo.name}.") else: @@ -71,7 +73,8 @@ def get_open_pdks( patch_open_pdks(repo_path) try: - json_raw = open(f"{repo_path}/sky130/sky130.json").read() + with open(f"{repo_path}/sky130/sky130.json", encoding="utf8") as f: + json_raw = f.read() cpp = pcpp.Preprocessor() cpp.line_directive = None cpp.parse(json_raw) @@ -96,7 +99,7 @@ def get_open_pdks( except subprocess.CalledProcessError as e: print(e) print(e.stderr) - exit(-1) + sys.exit(-1) def build_sky130_timing(build_directory, sky130_path, log_dir, jobs=1): @@ -131,7 +134,7 @@ def build_sky130_timing(build_directory, sky130_path, log_dir, jobs=1): set -e source {venv_path}/bin/activate python3 -m pip install wheel - python3 -m pip install {os.path.join(sky130_path, 'scripts', 'python-skywater-pdk')} + python3 -m pip install {os.path.join(sky130_path, "scripts", "python-skywater-pdk")} """, ], stdout=out, @@ -178,7 +181,7 @@ def do_submodule(submodule: str): except subprocess.CalledProcessError as e: print(e) print(e.stderr) - exit(-1) + sys.exit(-1) LIB_FLAG_MAP = { @@ -210,33 +213,33 @@ def build_variants( console = Console() def run_sh(script, log_to): - output_file = open(log_to, "w") - output_file.write(script + "\n") - output_file.write("---\n") - output_file.flush() - try: - subprocess.check_call( - ["sh", "-c", script], - cwd=open_pdks_path, - stdout=output_file, - stderr=output_file, - stdin=open(os.devnull), - ) - except subprocess.CalledProcessError as e: - console.log( - f"An error occurred while building the PDK. Check {log_to} for more information." - ) - raise e + with open(log_to, "w", encoding="utf8") as output_file, open( + os.devnull, encoding="utf8" + ) as devnull: + output_file.write(script + "\n") + output_file.write("---\n") + output_file.flush() + try: + subprocess.check_call( + ["sh", "-c", script], + cwd=open_pdks_path, + stdout=output_file, + stderr=output_file, + stdin=devnull, + ) + except subprocess.CalledProcessError as e: + console.log( + f"An error occurred while building the PDK. Check {log_to} for more information." + ) + raise e from None magic_dirname = os.path.dirname(magic_bin) - library_flags = set([LIB_FLAG_MAP[library] for library in include_libraries]) - library_flags_disable = set( - [ - LIB_FLAG_MAP[library].replace("enable", "disable") - for library in LIB_FLAG_MAP - if library not in include_libraries - ] - ) + library_flags = {LIB_FLAG_MAP[library] for library in include_libraries} + library_flags_disable = { + LIB_FLAG_MAP[library].replace("enable", "disable") + for library in LIB_FLAG_MAP + if library not in include_libraries + } configuration_flags = ["--enable-sky130-pdk", "--with-reference"] + list( library_flags.union(library_flags_disable) @@ -282,7 +285,7 @@ def run_sh(script, log_to): except subprocess.CalledProcessError as e: print(e) print(e.stderr) - exit(-1) + sys.exit(-1) def install_sky130(build_directory, pdk_root, version): @@ -347,7 +350,7 @@ def build( magic_bin = shutil.which("magic") if magic_bin is None: print("Magic is either not installed or not in PATH.") - exit(-1) + sys.exit(-1) build_variants( magic_bin, @@ -356,7 +359,7 @@ def build( library_set, log_dir, jobs, - ), + ) install_sky130(build_directory, pdk_root, version) if clear_build_artifacts: diff --git a/ciel/click_common.py b/ciel/click_common.py index 13de6e7..5c35a70 100644 --- a/ciel/click_common.py +++ b/ciel/click_common.py @@ -24,7 +24,7 @@ CIEL_RESOLVED_HOME, resolve_version, ) -from .families import Family, resolve_pdk_family, resolve_pdk_variant +from .families import Family, resolve_pdk_family opt = partial(click.option, show_default=True) @@ -92,19 +92,18 @@ def process_value(self, ctx: click.Context, value): value = self.callback(ctx, self, value) try: - family = resolve_pdk_family(value) - variant = resolve_pdk_variant(value) + resolve_pdk_family(value) except ValueError as e: raise click.BadParameter(str(e), ctx=ctx, param=self) - return (family, variant) + return value # pass selector as is, we just needed to validate it def opt_pdk(function: Callable): function = opt( "--pdk-family", "--pdk", - "pdk_tuple", + "pdk_selector", cls=PDKOption, required=True, envvar=["PDK_FAMILY", "PDK"], diff --git a/ciel/common.py b/ciel/common.py index e0f7698..ee73659 100644 --- a/ciel/common.py +++ b/ciel/common.py @@ -53,7 +53,8 @@ def _get_current_version(pdk_root: str, pdk: str) -> Optional[str]: mkdirp(current_file_dir) version = None try: - version = open(current_file).read().strip() + with open(current_file, encoding="utf8") as f: + version = f.read().strip() except FileNotFoundError: pass @@ -73,13 +74,12 @@ def get_versions_dir(pdk_root: str, pdk: str) -> Path: @dataclass -class Version(object): +class Version: name: str pdk: str commit_date: Optional[datetime] = None upload_date: Optional[datetime] = None prerelease: bool = False - data_source_pdk_override: Optional[str] = None def __lt__(self, rhs: "Version"): return (self.commit_date or datetime.min) < (rhs.commit_date or datetime.min) @@ -181,7 +181,8 @@ def resolve_version( "Any of ./tool_metadata.yml or ./dependencies/tool_metadata.yml not found. You'll need to specify the file path or the commits explicitly." ) - tool_metadata = yaml.safe_load(open(tool_metadata_file_path).read()) + with open(tool_metadata_file_path, encoding="utf8") as f: + tool_metadata = yaml.safe_load(f) open_pdks_list = [tool for tool in tool_metadata if tool["name"] == "open_pdks"] diff --git a/ciel/families.py b/ciel/families.py index 15a0a48..070b8af 100644 --- a/ciel/families.py +++ b/ciel/families.py @@ -17,13 +17,13 @@ # limitations under the License. import fnmatch from dataclasses import dataclass -from typing import Iterable, List, Dict, Optional, Set, ClassVar +from typing import Iterable, List, Dict, Optional, Set, ClassVar, Tuple from .github import RepoInfo, opdks_repo, ihp_repo @dataclass -class Family(object): +class Family: by_name: ClassVar[Dict[str, "Family"]] = {} by_variant: ClassVar[Dict[str, "Family"]] = {} @@ -206,3 +206,7 @@ def resolve_pdk_variant(selector: Optional[str]): return family.default_variant raise ValueError(f"'{selector}' is not a valid PDK family or variant.") + + +def resolve_pdk_selector(selector: Optional[str]) -> Tuple[str, str]: + return (resolve_pdk_family(selector), resolve_pdk_variant(selector)) diff --git a/ciel/github.py b/ciel/github.py index 63c8e4a..4cc5a73 100644 --- a/ciel/github.py +++ b/ciel/github.py @@ -62,7 +62,7 @@ def api(self): class GitHubSession(httpx.Client): - class Token(object): + class Token: override: ClassVar[Optional[str]] = None @classmethod @@ -75,7 +75,7 @@ def get_gh_token(Self) -> Optional[str]: ["gh", "auth", "token"], encoding="utf8", ).strip() - except Exception: + except Exception: # noqa: S110 pass # 1. Higher priority: environment GITHUB_TOKEN @@ -117,7 +117,7 @@ def __init__( f"Invalid SOCKS proxy: Ciel only supports http://, https:// and socks5:// schemes: {e.args[0]}", file=sys.stderr, ) - exit(-1) + sys.exit(-1) else: raise e from None github_token = github_token or GitHubSession.Token.get_gh_token() @@ -136,15 +136,18 @@ def api( endpoint: str, method: str, *args, + raw_request=False, **kwargs, ) -> Any: url = repo.api + endpoint - req = self.request(method, url, *args, **kwargs) - req.raise_for_status() + res = self.request(method, url, *args, **kwargs) + if raw_request: + return res + res.raise_for_status() try: - return req.json() + return res.json() except ValueError as e: - raise ValueError(f"Request {req.url} returned invalid JSON: {e}") from None + raise ValueError(f"Request {res.url} returned invalid JSON: {e}") from None @classmethod def get_user_agent(Self) -> str: @@ -165,6 +168,7 @@ def get_commit_date( return None date = response["commit"]["author"]["date"] + print(date) commit_date = datetime.strptime(date, "%Y-%m-%dT%H:%M:%SZ") return commit_date diff --git a/ciel/manage.py b/ciel/manage.py index 71df50d..9f54a90 100644 --- a/ciel/manage.py +++ b/ciel/manage.py @@ -22,7 +22,7 @@ import tarfile import tempfile from pathlib import Path -from typing import Dict, Iterable, List, Optional, Union, Tuple +from typing import Any, Dict, Iterable, List, Optional, Union, Tuple import rich import httpx @@ -38,7 +38,7 @@ get_ciel_dir, ) from .build import build -from .families import Family +from .families import Family, resolve_pdk_selector from .source import DataSource @@ -101,9 +101,9 @@ def print_remote_list( tree = rich.tree.Tree(f"Pre-built {pdk} PDK versions") for remote_version in pdk_list: name = remote_version.name - assert ( - remote_version.commit_date is not None - ), f"Remote version {name} has no commit date" + assert remote_version.commit_date is not None, ( + f"Remote version {name} has no commit date" + ) day = remote_version.commit_date.strftime("%Y.%m.%d") desc = f"[green]{name} ({day})" if remote_version.prerelease: @@ -119,16 +119,22 @@ def print_remote_list( def fetch( pdk_root: str, - pdk_tuple: Tuple[str, str], + pdk: Union[Tuple[str, str], str], version: str, *, data_source: DataSource, build_if_not_found=False, - build_kwargs: dict = {}, + build_kwargs: Optional[Dict[str, Any]] = None, include_libraries: Optional[Iterable[str]] = None, - output: Union[Console, io.TextIOWrapper] = Console(), + output: Union[Console, io.TextIOWrapper, None] = None, ) -> Version: - pdk_family_name, pdk_variant_name = pdk_tuple + if output is None: + output = Console() + + if isinstance(pdk, tuple): + pdk_family_name, pdk_variant_name = pdk + else: + pdk_family_name, pdk_variant_name = resolve_pdk_selector(pdk) console = output if not isinstance(console, Console): @@ -182,9 +188,11 @@ def fetch( client, assets = data_source.get_downloads_for_version(version_object) assets_filtered = [] for asset in assets: - if asset.content == "common" and common_missing: - assets_filtered.append(asset) - elif asset.content in missing_libraries: + if ( + asset.content == "common" + and common_missing + or asset.content in missing_libraries + ): assets_filtered.append(asset) tarball_directory_obj = tempfile.TemporaryDirectory(suffix=".ciel") tarball_directory = Path(tarball_directory_obj.name) @@ -218,7 +226,7 @@ def fetch( final_path.parent.mkdir(parents=True, exist_ok=True) io = tf.extractfile(file) if io is None: - raise IOError( + raise OSError( f"Failed to unpack file in {asset.filename}'s tarball: {file.name}." ) with open(final_path, "wb") as f: @@ -232,9 +240,9 @@ def fetch( ) build( pdk_root=pdk_root, - pdk_tuple=pdk_tuple, + pdk=pdk, version=version, - **build_kwargs, + **(build_kwargs or {}), ) else: if e.response is not None: @@ -273,16 +281,21 @@ def fetch( def enable( pdk_root: str, - pdk_tuple: Tuple[str, str], + pdk: Union[str, Tuple[str, str]], version: str, *, data_source: DataSource, build_if_not_found: bool = False, - build_kwargs: dict = {}, + build_kwargs: Optional[Dict[str, Any]] = None, include_libraries: Optional[List[str]] = None, - output: Union[Console, io.TextIOWrapper] = Console(), + output: Union[None, Console, io.TextIOWrapper] = None, ) -> Version: - pdk_family_name, _ = pdk_tuple + if output is None: + output = Console() + if isinstance(pdk, tuple): + pdk_family_name, _ = pdk + else: + pdk_family_name, _ = resolve_pdk_selector(pdk) console = output if not isinstance(console, Console): @@ -301,7 +314,7 @@ def enable( fetch( pdk_root, - pdk_tuple, + pdk, version, data_source=data_source, build_if_not_found=build_if_not_found, diff --git a/ciel/source.py b/ciel/source.py index c22e0e9..bfb3dac 100644 --- a/ciel/source.py +++ b/ciel/source.py @@ -35,7 +35,7 @@ class Asset: url: str -class DataSource(object): +class DataSource: factory: ClassVar[Dict[str, Type["DataSource"]]] = {} default: ClassVar["DataSource"] @@ -103,7 +103,6 @@ def get_available_versions(self, pdk: str) -> List[Version]: commit_date=commit_date, upload_date=upload_date, prerelease=release["prerelease"], - data_source_pdk_override=release_family_name, ) versions.append(remote_version) @@ -117,13 +116,30 @@ def get_available_versions(self, pdk: str) -> List[Version]: def get_downloads_for_version( self, version: Version ) -> Tuple[httpx.Client, List[Asset]]: - release_family_name = version.data_source_pdk_override or version.pdk - - release = self.session.api( + family_res: httpx.Response = self.session.api( self.repo, - f"/releases/tags/{release_family_name}-{version.name}", + f"/releases/tags/{version.pdk}-{version.name}", "get", + raw_request=True, ) + release = None + if family_res.status_code // 100 == 2: + release = family_res.json() + elif family_res.status_code == 404 and version.pdk in Family.by_name: + # try variants because ihp was renamed (grumble) + variants = Family.by_name[version.pdk].variants + for variant in variants: + variant_res: httpx.Response = self.session.api( + self.repo, + f"/releases/tags/{variant}-{version.name}", + "get", + raw_request=True, + ) + if variant_res.status_code != 404: + release = variant_res.json() + break + if release is None: + family_res.raise_for_status() assets = release["assets"] zst_files = [] diff --git a/poetry.lock b/poetry.lock index 9bf2bce..aae3799 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.4.3 and should not be changed by hand. [[package]] name = "anyio" @@ -23,53 +23,6 @@ doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21.0b1) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\""] trio = ["trio (>=0.26.1)"] -[[package]] -name = "black" -version = "24.8.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"}, - {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"}, - {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"}, - {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"}, - {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"}, - {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"}, - {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"}, - {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"}, - {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"}, - {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"}, - {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"}, - {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"}, - {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"}, - {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"}, - {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"}, - {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"}, - {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"}, - {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"}, - {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"}, - {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"}, - {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"}, - {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4) ; sys_platform != \"win32\" or implementation_name != \"pypy\"", "aiohttp (>=3.7.4,!=3.9.0) ; sys_platform == \"win32\" and implementation_name == \"pypy\""] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - [[package]] name = "certifi" version = "2024.12.14" @@ -169,7 +122,7 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" -groups = ["main", "dev"] +groups = ["main"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -184,7 +137,7 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main", "dev"] +groups = ["main"] markers = "platform_system == \"Windows\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, @@ -207,50 +160,33 @@ files = [ [package.extras] test = ["pytest (>=6)"] -[[package]] -name = "flake8" -version = "5.0.4" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.6.1" -groups = ["dev"] -files = [ - {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, - {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.9.0,<2.10.0" -pyflakes = ">=2.5.0,<2.6.0" - [[package]] name = "h11" -version = "0.14.0" +version = "0.16.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, + {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, + {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, ] [[package]] name = "httpcore" -version = "1.0.7" +version = "1.0.9" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, - {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, + {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, + {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, ] [package.dependencies] certifi = "*" -h11 = ">=0.13,<0.15" +h11 = ">=0.16" [package.extras] asyncio = ["anyio (>=4.0,<5.0)"] @@ -323,18 +259,6 @@ profiling = ["gprof2dot"] rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - [[package]] name = "mdurl" version = "0.1.2" @@ -407,30 +331,6 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] -[[package]] -name = "packaging" -version = "24.2" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, - {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - [[package]] name = "pcpp" version = "1.30" @@ -443,35 +343,6 @@ files = [ {file = "pcpp-1.30.tar.gz", hash = "sha256:5af9fbce55f136d7931ae915fae03c34030a3b36c496e72d9636cedc8e2543a1"}, ] -[[package]] -name = "platformdirs" -version = "4.3.6" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, - {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.11.2)"] - -[[package]] -name = "pycodestyle" -version = "2.9.1" -description = "Python style guide checker" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, - {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, -] - [[package]] name = "pycparser" version = "2.22" @@ -485,18 +356,6 @@ files = [ {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] -[[package]] -name = "pyflakes" -version = "2.5.0" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, - {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, -] - [[package]] name = "pygments" version = "2.19.1" @@ -594,6 +453,34 @@ pygments = ">=2.13.0,<3.0.0" [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] +[[package]] +name = "ruff" +version = "0.16.6" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "ruff-0.16.6-py3-none-linux_armv6l.whl", hash = "sha256:61c368c26bf8e973e5ab14a2772de587bc068ea3f9a277f673380749b4898fb8"}, + {file = "ruff-0.16.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ecf4f068e2e123e43a26e9db4e19524cc56563912404e83bbfca375757e45a32"}, + {file = "ruff-0.16.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:99b62ea33baf130f50368798d841f0d95527b6d817bf31817b65dd058f1d314c"}, + {file = "ruff-0.16.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fbf89013f2bb3f6835a6038ff658dc8a1b38c98dc8e724b964168ad4e881876"}, + {file = "ruff-0.16.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56a67065e22efa6bc4d498299d3bb06c0c90aace8fac2068b5a12f9dc4d8d51d"}, + {file = "ruff-0.16.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e25cc89174874b176a157e4428d66761c2c0c006654419bf384f967f361ff1b1"}, + {file = "ruff-0.16.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0700580ed5303723cb3c11c2f1d2a8913ce77b7ea86646dddb887f5417a9ba70"}, + {file = "ruff-0.16.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15f1d0b6e165a6e56567befb6629f8209271311d990bae0f37e6d065035ef5f3"}, + {file = "ruff-0.16.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d72c591a96986ee4268860e2b7235082129ca5e4cb9cbba653a4b57c11893757"}, + {file = "ruff-0.16.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:65a006baa18f33324325814c864daef03541d51564b98c517610ea756ab7003e"}, + {file = "ruff-0.16.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:cd02a7bf1a21a8735228a3e8c95a9dc5cf86bd2a52194f4aaae2a5755b4de0f4"}, + {file = "ruff-0.16.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:31b36f1e5ad85e0737f09d2be4e512e2e283583c14015da3b9dc07359ac0fc88"}, + {file = "ruff-0.16.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:61029b4ab4aa723fd3064fab96b1d814492596bf0c792679fffcbde1e1679953"}, + {file = "ruff-0.16.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9ac8998457832c2061709d900856b7ad271dace0cb41f346588d540162bfa718"}, + {file = "ruff-0.16.6-py3-none-win32.whl", hash = "sha256:0b87d9d16fcb63e8018423ca1d50b7260f15cb2da33e30db4baad4183a948c25"}, + {file = "ruff-0.16.6-py3-none-win_amd64.whl", hash = "sha256:10d21c51c3495d8eaea7b703a16592117ea6eb1d649e36335aa965ff1173eb39"}, + {file = "ruff-0.16.6-py3-none-win_arm64.whl", hash = "sha256:7a976c79b958f94e50a022a19f0f8c87387448020935ec14fc74331bd0a7f2c5"}, + {file = "ruff-0.16.6.tar.gz", hash = "sha256:dcf8a73d2ff77e99dde91244b4da16feba7f14e6beeb4015dee7c5a909e99050"}, +] + [[package]] name = "sniffio" version = "1.3.1" @@ -828,4 +715,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.1" python-versions = ">=3.8" -content-hash = "874548b418c4bf0200140f0798ee7f5dade38424b8b2eca2087de3d9c7eb26b9" +content-hash = "2620ad17b7fb74272772568def0034d4a519373b761f7700c2004227fb6d518d" diff --git a/pyproject.toml b/pyproject.toml index b7207a8..2f7ad1d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,7 @@ +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + [tool.poetry] name = "ciel" version = "3.0.0" @@ -26,8 +30,7 @@ zstandard = ">=0.19.0,<1" [tool.poetry.group.dev.dependencies] wheel = "*" -black = ">=24.4.0,<25" -flake8 = ">=4" +ruff = ">=0.16.6,<0.17.0" mypy = ">=1.9.0,<1.10.0" types-PyYAML = "*" types-setuptools = "*" @@ -37,10 +40,19 @@ types-attrs = "*" [tool.poetry.scripts] ciel = "ciel.__main__:cli" -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" +[tool.ruff] +target-version = "py38" -[tool.black] -include = 'ciel/[\w\-]+.py|ciel/build/[\w\-]+.py' -exclude = '.git|venv|.venv|.mypy_cache|(? None: ... - def parse(self, input, source: Optional[str] = None, ignore: dict = {}): ... + def parse(self, input, source: str | None = None, ignore: dict = {}): ... def write(self, io: StringIO): ... From 50ed6b338f69ddb96b227e6cff4f950d823eeded Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Mon, 7 Sep 2026 20:51:25 +0300 Subject: [PATCH 6/7] fix type errors, update readme --- Makefile | 1 + Readme.md | 4 +--- ciel/__main__.py | 8 ++++---- ciel/families.py | 2 +- ciel/source.py | 1 + 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 006102d..086a207 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ dist: venv/manifest.txt lint: venv/manifest.txt ./venv/bin/ruff format --check . ./venv/bin/ruff check + ./venv/bin/mypy --check-untyped-defs . venv: venv/manifest.txt venv/manifest.txt: ./pyproject.toml diff --git a/Readme.md b/Readme.md index 6b32f68..c167429 100644 --- a/Readme.md +++ b/Readme.md @@ -1,16 +1,14 @@

🌌 Ciel

License: Apache 2.0 + Python ≥3.8 CI Status Invite to FOSSi Chat - Code Style: Black

Ciel is a version manager (and builder) for builds of open-source process design kits (PDKs).

# Requirements -* Python 3.8+ with PIP -* macOS or GNU/Linux ## macOS Get [Homebrew](https://brew.sh) then: diff --git a/ciel/__main__.py b/ciel/__main__.py index c25210d..7af7f38 100644 --- a/ciel/__main__.py +++ b/ciel/__main__.py @@ -64,7 +64,7 @@ def output_cmd(pdk_root, pdk_selector): "Invoke ciel --help for assistance installing and enabling versions.", file=sys.stderr, ) - sys.sys.exit(1) + sys.exit(1) print(version.name, end="") @@ -212,17 +212,17 @@ def list_remote_cmd(data_source, pdk_root, pdk_selector): except ValueError as e: console = Console(stderr=True) console.print(f"[red]{e}") - sys.sys.exit(-1) + sys.exit(-1) except httpx.HTTPStatusError as e: console = Console(stderr=True) console.print(f"[red]Encountered an error when polling version list: {e}") - sys.sys.exit(-1) + sys.exit(-1) except httpx.NetworkError as e: console = Console(stderr=True) console.print( f"[red]You don't appear to be connected to the Internet. ls-remote cannot be used.: {e}" ) - sys.sys.exit(-1) + sys.exit(-1) @click.command("path") diff --git a/ciel/families.py b/ciel/families.py index 070b8af..a0f4cfb 100644 --- a/ciel/families.py +++ b/ciel/families.py @@ -208,5 +208,5 @@ def resolve_pdk_variant(selector: Optional[str]): raise ValueError(f"'{selector}' is not a valid PDK family or variant.") -def resolve_pdk_selector(selector: Optional[str]) -> Tuple[str, str]: +def resolve_pdk_selector(selector: str) -> Tuple[str, str]: return (resolve_pdk_family(selector), resolve_pdk_variant(selector)) diff --git a/ciel/source.py b/ciel/source.py index bfb3dac..8f9c74a 100644 --- a/ciel/source.py +++ b/ciel/source.py @@ -140,6 +140,7 @@ def get_downloads_for_version( break if release is None: family_res.raise_for_status() + assert release is not None # raise_for_status is a noreturn assets = release["assets"] zst_files = [] From 0a5e2c42285b92c7195d461f6125f449f96d8c68 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Mon, 7 Sep 2026 21:31:08 +0300 Subject: [PATCH 7/7] fix build_cmd, push_cmd --- ciel/build/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ciel/build/__init__.py b/ciel/build/__init__.py index a8bcb79..86c7327 100644 --- a/ciel/build/__init__.py +++ b/ciel/build/__init__.py @@ -97,7 +97,7 @@ def build_cmd( include_libraries, jobs, pdk_root, - pdk, + pdk_selector, clear_build_artifacts, version, use_repo_at, @@ -116,7 +116,7 @@ def build_cmd( build( pdk_root=pdk_root, - pdk=pdk, + pdk=pdk_selector, version=version, jobs=jobs, clear_build_artifacts=clear_build_artifacts, @@ -241,7 +241,7 @@ def push_cmd( repository, pre, pdk_root, - pdk, + pdk_selector, version, push_libraries, ): @@ -256,7 +256,7 @@ def push_cmd( try: push( pdk_root, - pdk, + pdk_selector, version, owner=owner, repository=repository,