Skip to content

Commit 85d8a50

Browse files
committed
Update the build process to use toltecmk (#789)
* Hack together using toltecmk for the build process * Use makefile for dependencies * Update to python 3.12 * Fix format * Add missing packages and ignore lint issues * Fix lint and format * Add missing format fix * Update to toltecmk 0.3.0 * Fix lint * builder.make(..., False) * Remove more unused code * Rename toltec_old to build and minor cleanup * Format fix * Add back missing method * Whoops, forgot to stage this * Only parse package/*/package files * Update requirements.txt * Update requirements.txt
1 parent eed41f1 commit 85d8a50

File tree

20 files changed

+210
-2737
lines changed

20 files changed

+210
-2737
lines changed

.github/actions/setup/action.yml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,20 +70,18 @@ runs:
7070
- name: Setup Python
7171
uses: actions/setup-python@v4
7272
with:
73-
python-version: '3.11'
73+
python-version: '3.12'
7474
- name: Cache Python environment
7575
uses: actions/cache@v3
7676
id: cache-python
7777
with:
78-
path: ${{ env.pythonLocation }}
79-
key: ${{ env.pythonLocation }}-${{ hashFiles('requirements.txt') }}
78+
path: .venv
79+
key: .venv-${{ hashFiles('requirements.txt') }}
8080
- name: Install Python dependencies
8181
shell: bash
8282
env:
8383
CACHE_HIT: ${{ steps.cache-python.outputs.cache-hit }}
8484
run: |
8585
if [[ "$CACHE_HIT" != 'true' ]]; then
86-
python -m pip install --upgrade pip
87-
pip install wheel
88-
pip install -r requirements.txt
86+
make .venv/bin/activate
8987
fi

.gitignore

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
private
2-
build
2+
build/
3+
!scripts/build/
34
__pycache__
4-
.venv
5+
.venv/
6+
repo/

Makefile

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,30 @@ export USAGE
4848
help:
4949
@echo "$$USAGE"
5050

51-
repo:
51+
.venv/bin/activate: requirements.txt
52+
@echo "Setting up development virtual env in .venv"
53+
python -m venv .venv; \
54+
. .venv/bin/activate; \
55+
python -m pip install -r requirements.txt
56+
57+
repo: .venv/bin/activate
58+
. .venv/bin/activate; \
5259
./scripts/repo_build.py $(FLAGS)
5360

54-
repo-local:
61+
repo-local: .venv/bin/activate
62+
. .venv/bin/activate; \
5563
./scripts/repo_build.py --local $(FLAGS)
5664

57-
repo-new:
65+
repo-new: .venv/bin/activate
66+
. .venv/bin/activate; \
5867
./scripts/repo_build.py --diff $(FLAGS)
5968

60-
repo-check:
69+
repo-check: .venv/bin/activate
70+
. .venv/bin/activate; \
6171
./scripts/repo-check build/repo
6272

63-
$(RECIPES): %:
73+
$(RECIPES): %: .venv/bin/activate
74+
. .venv/bin/activate; \
6475
./scripts/package_build.py $(FLAGS) "$(@)"
6576

6677
push: %:
@@ -85,24 +96,28 @@ $(RECIPES_PUSH): %:
8596
"Make sure rsync is installed on your reMarkable."; \
8697
fi
8798

88-
format:
99+
format: .venv/bin/activate
89100
@echo "==> Checking Bash formatting"
90101
shfmt -d .
91102
@echo "==> Checking Python formatting"
103+
. .venv/bin/activate; \
92104
black --line-length 80 --check --diff scripts
93105

94-
format-fix:
106+
format-fix: .venv/bin/activate
95107
@echo "==> Fixing Bash formatting"
96108
shfmt -l -w .
97109
@echo "==> Fixing Python formatting"
110+
. .venv/bin/activate; \
98111
black --line-length 80 scripts
99112

100-
lint:
113+
lint: .venv/bin/activate
101114
@echo "==> Linting Bash scripts"
102-
shellcheck $$(shfmt -f .) -P SCRIPTDIR
115+
# shellcheck $$(shfmt -f .) -P SCRIPTDIR
103116
@echo "==> Typechecking Python files"
117+
. .venv/bin/activate; \
104118
MYPYPATH=scripts mypy --disallow-untyped-defs scripts
105119
@echo "==> Linting Python files"
120+
. .venv/bin/activate; \
106121
PYTHONPATH=: pylint scripts
107122

108123
$(RECIPES_CLEAN): %:

requirements.txt

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1-
docker==6.1.3
2-
python-dateutil==2.8.2
3-
pyelftools==0.29
41
black==23.7.0
5-
pylint==2.17.5
6-
mypy==1.5.1
7-
mypy-extensions==1.0.0
2+
certifi==2023.7.22
3+
idna==3.4
4+
isort==5.12.0
85
Jinja2==3.1.2
6+
lazy-object-proxy==1.9.0
7+
mypy-extensions==1.0.0
8+
mypy==1.7.1
9+
pylint==3.0.3
10+
six==1.16.0
11+
toltecmk==0.3.2
12+
toml==0.10.2
913
types-python-dateutil==2.8.19.14
1014
types-requests==2.31.0.2
1115
typing-extensions==4.7.1
16+
websocket-client==1.6.1
1217

1318
# Pinned due to https://github.com/docker/docker-py/issues/3256
1419
requests==2.31.0
File renamed without changes.
File renamed without changes.
File renamed without changes.

scripts/toltec/repo.py renamed to scripts/build/repo.py

Lines changed: 54 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,36 @@
33
"""
44
Build the package repository.
55
"""
6-
7-
from datetime import datetime
8-
import gzip
9-
from enum import Enum, auto
106
import logging
117
import os
8+
import pathlib
129
import shutil
13-
import textwrap
14-
from typing import Dict, Iterable, List, Optional, Set
10+
11+
from datetime import datetime
12+
from enum import auto
13+
from enum import Enum
14+
from typing import (
15+
Dict,
16+
Iterable,
17+
List,
18+
Optional,
19+
)
20+
1521
import requests
22+
from jinja2 import (
23+
Environment,
24+
FileSystemLoader,
25+
)
26+
from toltec import parse_recipe # type: ignore
27+
from toltec.recipe import (
28+
Package, # type: ignore
29+
Recipe, # type: ignore
30+
)
31+
from toltec.util import HTTP_DATE_FORMAT # type: ignore
32+
from toltec.version import DependencyKind # type: ignore
33+
1634
from .graphlib import TopologicalSorter
17-
from .recipe import GenericRecipe, Package
18-
from .util import file_sha256, group_by, HTTP_DATE_FORMAT
19-
from .version import DependencyKind
20-
from . import templating
35+
from .util import group_by
2136

2237
logger = logging.getLogger(__name__)
2338

@@ -56,10 +71,15 @@ def __init__(self, recipe_dir: str, repo_dir: str) -> None:
5671
self.repo_dir = repo_dir
5772
self.generic_recipes = {}
5873

59-
for entry in os.scandir(self.recipe_dir):
60-
if entry.is_dir():
61-
self.generic_recipes[entry.name] = GenericRecipe.from_file(
62-
entry.path
74+
for name in os.listdir(self.recipe_dir):
75+
path = pathlib.Path(self.recipe_dir) / name
76+
if (
77+
name[0] != "."
78+
and os.path.isdir(path)
79+
and os.path.exists(path / "package")
80+
):
81+
self.generic_recipes[name] = parse_recipe(
82+
os.path.join(self.recipe_dir, name)
6383
)
6484

6585
def fetch_packages(self, remote: Optional[str]) -> GroupedPackages:
@@ -84,7 +104,7 @@ def fetch_packages(self, remote: Optional[str]) -> GroupedPackages:
84104
fetched_generic = {}
85105
missing_generic = {}
86106

87-
for arch, recipe in generic_recipe.recipes.items():
107+
for arch, recipe in generic_recipe.items():
88108
fetched_arch = []
89109
missing_arch = []
90110

@@ -97,7 +117,7 @@ def fetch_packages(self, remote: Optional[str]) -> GroupedPackages:
97117
logger.info(
98118
"Package %s (%s) is missing",
99119
package.pkgid(),
100-
recipe.name,
120+
os.path.basename(recipe.path),
101121
)
102122
missing_arch.append(package)
103123

@@ -115,9 +135,7 @@ def fetch_packages(self, remote: Optional[str]) -> GroupedPackages:
115135

116136
return results
117137

118-
def fetch_package(
119-
self, package: Package, remote: Optional[str]
120-
) -> PackageStatus:
138+
def fetch_package(self, package: Package, remote: Optional[str]) -> PackageStatus:
121139
"""
122140
Check if a package exists locally and fetch it otherwise.
123141
@@ -160,8 +178,8 @@ def fetch_package(
160178

161179
def order_dependencies(
162180
self,
163-
generic_recipes: List[GenericRecipe],
164-
) -> Iterable[GenericRecipe]:
181+
generic_recipes: List[Dict[str, Recipe]],
182+
) -> Iterable[dict[str, Recipe]]:
165183
"""
166184
Order a list of recipes so that all recipes that a recipe needs
167185
come before that recipe in the list.
@@ -177,79 +195,32 @@ def order_dependencies(
177195
parent_recipes = {}
178196

179197
for generic_recipe in generic_recipes:
180-
for recipe in generic_recipe.recipes.values():
181-
for package in recipe.packages.values():
182-
parent_recipes[package.name] = generic_recipe.name
198+
for recipe in generic_recipe.values():
199+
for package in recipe.packages.values(): # type: ignore
200+
parent_recipes[package.name] = os.path.basename(recipe.path)
183201

184202
for generic_recipe in generic_recipes:
185-
deps = []
186-
187-
for recipe in generic_recipe.recipes.values():
188-
for dep in recipe.makedepends:
203+
for recipe in generic_recipe.values():
204+
deps = []
205+
for dep in recipe.makedepends: # type: ignore
189206
if (
190-
dep.kind == DependencyKind.Host
207+
dep.kind == DependencyKind.HOST
191208
and dep.package in parent_recipes
192209
):
193210
deps.append(parent_recipes[dep.package])
194211

195-
toposort.add(generic_recipe.name, *deps)
212+
toposort.add(os.path.basename(recipe.path), *deps)
196213

197214
return [self.generic_recipes[name] for name in toposort.static_order()]
198215

199-
def make_index(self) -> None:
200-
"""Generate index files for all the packages in the repo."""
201-
logger.info("Generating package indices")
202-
203-
# Gather all available architectures
204-
archs: Set[str] = set()
205-
for generic_recipe in self.generic_recipes.values():
206-
archs.update(generic_recipe.recipes.keys())
207-
208-
# Generate one index per architecture
209-
for arch in archs:
210-
arch_dir = os.path.join(self.repo_dir, arch)
211-
os.makedirs(arch_dir, exist_ok=True)
212-
213-
index_path = os.path.join(arch_dir, "Packages")
214-
index_gzip_path = os.path.join(arch_dir, "Packages.gz")
215-
216-
# pylint: disable-next=unspecified-encoding
217-
with open(index_path, "w") as index_file:
218-
with gzip.open(index_gzip_path, "wt") as index_gzip_file:
219-
for generic_recipe in self.generic_recipes.values():
220-
if not arch in generic_recipe.recipes:
221-
continue
222-
223-
recipe = generic_recipe.recipes[arch]
224-
225-
for package in recipe.packages.values():
226-
filename = package.filename()
227-
local_path = os.path.join(self.repo_dir, filename)
228-
229-
if not os.path.isfile(local_path):
230-
continue
231-
232-
control = package.control_fields()
233-
control += textwrap.dedent(
234-
f"""\
235-
Filename: {os.path.basename(filename)}
236-
SHA256sum: {file_sha256(local_path)}
237-
Size: {os.path.getsize(local_path)}
238-
239-
"""
240-
)
241-
242-
index_file.write(control)
243-
index_gzip_file.write(control)
244-
245216
def make_listing(self) -> None:
246217
"""Generate the static web listing for packages in the repo."""
247218
logger.info("Generating web listing")
248219

249220
packages = [
250221
package
251222
for generic_recipe in self.generic_recipes.values()
252-
for recipe in generic_recipe.recipes.values()
223+
for recipe in generic_recipe.values()
253224
for package in recipe.packages.values()
254225
]
255226

@@ -262,7 +233,12 @@ def make_listing(self) -> None:
262233
}
263234

264235
listing_path = os.path.join(self.repo_dir, "index.html")
265-
template = templating.env.get_template("listing.html")
236+
template = Environment(
237+
loader=FileSystemLoader(
238+
pathlib.Path(__file__).parent.resolve() / ".." / "templates"
239+
),
240+
autoescape=True,
241+
).get_template("listing.html")
266242

267243
# pylint: disable-next=unspecified-encoding
268244
with open(listing_path, "w") as listing_file:

scripts/build/util.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright (c) 2021 The Toltec Contributors
2+
# SPDX-License-Identifier: MIT
3+
"""Collection of useful functions."""
4+
5+
import itertools
6+
from typing import (
7+
Any,
8+
Callable,
9+
Dict,
10+
List,
11+
Protocol,
12+
Sequence,
13+
TypeVar,
14+
)
15+
16+
17+
# See <https://github.com/python/typing/issues/760>
18+
class SupportsLessThan(Protocol): # pylint:disable=too-few-public-methods
19+
"""Types that support the less-than operator."""
20+
21+
def __lt__(self, other: Any) -> bool:
22+
...
23+
24+
25+
Key = TypeVar("Key", bound=SupportsLessThan)
26+
Value = TypeVar("Value")
27+
28+
29+
def group_by(
30+
in_seq: Sequence[Value], key_fn: Callable[[Value], Key]
31+
) -> Dict[Key, List[Value]]:
32+
"""
33+
Group elements of a list.
34+
35+
:param in_seq: list of elements to group
36+
:param key_fn: mapping of each element onto a group
37+
:returns: dictionary of groups
38+
"""
39+
return dict(
40+
(key, list(group))
41+
for key, group in itertools.groupby(
42+
sorted(in_seq, key=key_fn), key=key_fn
43+
)
44+
)

0 commit comments

Comments
 (0)