Skip to content

Commit ebbec6d

Browse files
Package infrastructure (#7)
# Package infrastructure - [x] Configuration Node for multiple package build backend - [x] Package base class - [ ] Implicit handling of kernel/runtime package - [x] Meson package support - [x] Cargo package support ## Change in configuration The followings nodes are renamed: - `sentry` --> `kernel` - `libshield` --> `runtime` (will cover runtime for supported languages) - `app` --> `application` The following properties are renamed: - `exelist` --> `provides` (which may contain other compile product than an executable) - `config_file` --> `config` - `deps` --> `depends` Scm and git configuration are no longer flattened. each package has a scm.<method>, only scm.git is supported yet. Application has a build.backend entry with meson/cargo value. A generic options list can be passed w/ build.options.<opt>, they can be either a boolean, a string or a integer value. There will be a set of "built-in" options derived according to the backend. Others, will be translate as-is and pass to configure step. e.g.: ```toml build.options.with_doc = true ``` --> ```meson -Dwith_doc=true ``` ### Configuration example ```toml name = 'Sample project' license = 'Apache-2.0' license_file = ['LICENSE.txt'] dts = 'dts/sample_project.dts' crossfile = 'cm33-none-eabi-gcc.ini' version = 'v0.0.1' [kernel] scm.git.uri = 'https://github.com/outpost-os/sentry-kernel.git' scm.git.revision = 'main' config = 'configs/sentry/nucleo_u5a5.config' [runtime] scm.git.uri = 'https://github.com/outpost-os/libshield.git' scm.git.revision = 'main' config = 'configs/shield/shield.config' [application.app1] scm.git.uri = 'https://github.com/outpost-os/app1.git' scm.git.revision = 'main' config = 'configs/app1.config' build.backend = 'meson' depends = [] provides = ['app1.elf'] [application.app2] scm.git.uri = 'https://github.com/outpost-os/app2.git' scm.git.revision = 'main' config = 'configs/app2.config' build.backend = 'meson' depends = [] provides = ['app2.elf'] ``` ## Package base class Provide base package attribute and property as source directory, build directory forge, config and dts handling, virtual methods for: - post download hook (e.g. meson subprojects download, cargo vendoring) - post update hook - install hook (e.g. cargo install can't install in a sysroot w/ elf suffix). - classmethod setup hook (each package backend may need a dedicated step before configure/compile, e.g. cargo need a config.toml to configure SDK path, meson a cross file, cmake a toolchain file for toolchain/sysroot configuration, etc.). ## Implicit kernel/runtime Kernel and runtime build and/or scm config shall not appear at integration level. A system integration should select an OS release (usually the tuple kernel, runtime, compiler collection). This is not supported yet but dependencies on kernel and runtime are implicit. In future version, this will be packaged as sdk source, project will only has to choose a version to use. ## Meson Package - [x] Meson package class inherit from Package - [x] Add option to enable/disable subprojects fetch/update (e.g. download might be false by defaut, update shall be true) - [ ] Impl virtual property for configure/compile/install args ## Cargo Package
2 parents d5ebb85 + baaebc8 commit ebbec6d

File tree

18 files changed

+750
-201
lines changed

18 files changed

+750
-201
lines changed

doc/commands/internals/install.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
`install`
2+
==========
3+
4+
.. argparse::
5+
:module: outpost.barbican._internals.install
6+
:func: argument_parser
7+
:prog: barbican --internal install
8+
9+
.. seealso::
10+
11+
:py:mod:`outpost.barbican._internals.install` module documentation

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ barbican = "outpost.barbican.barbican:main"
6060
[project.optional-dependencies]
6161
devel = [
6262
"flake8>=7.0,<8.0",
63-
"mypy>=1.8.0",
63+
"mypy>=1.12.0",
6464
"pytest>=4.6",
6565
"pytest-cov>=5.0.0",
6666
"pytest-dependency>=0.6",
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# SPDX-FileCopyrightText: 2024 Ledger SAS
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
from argparse import ArgumentParser, REMAINDER
6+
from pathlib import Path
7+
import shutil
8+
import typing as T
9+
10+
from ..console import console
11+
12+
13+
def run_install(from_dir: Path, files: list[Path], suffix: str = ""):
14+
for f in files:
15+
src = (from_dir / f.name).resolve(strict=True)
16+
dest = f.with_suffix(suffix)
17+
if not dest.parent.exists():
18+
dest.parent.mkdir(parents=True, exist_ok=True)
19+
console.message(f"Installing [i]{str(src)}[/i]→ [i]{str(dest)}[/i]")
20+
shutil.copy2(src, dest)
21+
22+
23+
def argument_parser() -> ArgumentParser:
24+
parser = ArgumentParser()
25+
parser.add_argument(
26+
"-s",
27+
"--suffix",
28+
action="store",
29+
type=str,
30+
default="",
31+
help="suffix to append to the installed file(s)",
32+
)
33+
parser.add_argument("from_dir", type=Path, help="directory from where files are installed")
34+
parser.add_argument("files", nargs=REMAINDER, type=Path, help="file(s) install destination")
35+
36+
return parser
37+
38+
39+
def run(argv: T.List[str]) -> None:
40+
args = argument_parser().parse_args(argv)
41+
run_install(args.from_dir, args.files, args.suffix)

src/outpost/barbican/barbican.py

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
from .console import console
2121
from .logger import logger, log_config
2222
from . import config
23-
from .package import Package
23+
from .package import Package, create_package, Backend
24+
from .package.meson import Meson
25+
from .package.cargo import Cargo
26+
2427
from .buildsys import ninja_backend
2528
from .utils import pathhelper
2629

@@ -55,14 +58,25 @@ def __init__(self, project_dir: pathlib.Path) -> None:
5558
# This will be, likely, false for next devel step.
5659

5760
# Instantiate Sentry kernel
58-
self._packages.append(Package("sentry", self, self._toml["sentry"]))
61+
self._packages.append(
62+
Meson("kernel", self, self._toml["kernel"], Package.Type.Kernel) # type: ignore
63+
)
5964
# Instantiate libshield
60-
self._packages.append(Package("libshield", self, self._toml["libshield"]))
65+
self._packages.append(
66+
Meson(
67+
"runtime",
68+
self,
69+
self._toml["runtime"],
70+
Package.Type.Runtime, # type: ignore[arg-type]
71+
)
72+
)
6173

62-
if "app" in self._toml:
74+
if "application" in self._toml:
6375
self._noapp = False
64-
for app, node in self._toml["app"].items():
65-
self._packages.append(Package(app, self, node, is_app=True))
76+
for app, node in self._toml["application"].items():
77+
self._packages.append(
78+
create_package(app, self, node, Package.Type.Application) # type: ignore
79+
)
6680
else:
6781
self._noapp = True
6882

@@ -98,10 +112,14 @@ def setup(self) -> None:
98112
)
99113

100114
ninja.add_meson_rules()
115+
ninja.add_cargo_rules()
101116

102117
# Add setup/compile/install targets for meson packages
103118
for p in self._packages:
104-
ninja.add_meson_package(p)
119+
if isinstance(p, Meson):
120+
ninja.add_meson_package(p)
121+
elif isinstance(p, Cargo):
122+
ninja.add_cargo_package(p)
105123

106124
if self._noapp:
107125
ninja.close()
@@ -114,29 +132,32 @@ def setup(self) -> None:
114132

115133
# linkerscript template file
116134
# XXX: hardcoded in early steps
117-
linker_script_template = pathlib.Path(self._packages[1].data_dir) / "linkerscript.ld.in"
135+
linker_script_template = (
136+
pathlib.Path(self.path.sysroot_data_dir) / "shield" / "linkerscript.ld.in"
137+
)
118138

119139
dummy_linker_script = pathlib.Path(self.path.private_build_dir, "dummy.lds")
120140
ninja.add_gen_ldscript_target(
121141
"dummy", dummy_linker_script, linker_script_template, pathlib.Path(dummy_layout[0])
122142
)
123143

124-
# Dummy link
125-
for app in self._packages[2:]:
126-
ninja.add_relink_meson_target(
127-
app.name,
128-
app.installed_exelist[0],
129-
app.dummy_linked_exelist[0],
130-
dummy_linker_script,
131-
)
144+
# Dummy link, for non pic application
145+
for package in self._packages:
146+
if package.is_application and package.backend == Backend.Meson:
147+
ninja.add_relink_meson_target(
148+
package.name,
149+
package.installed_targets[0],
150+
package.dummy_linked_targets[0],
151+
dummy_linker_script,
152+
)
132153

133154
layout_sys_exelist = []
134155
layout_app_exelist = []
135156
for package in self._packages:
136157
if package.is_sys_package:
137-
layout_sys_exelist.extend(package.installed_exelist)
138-
else:
139-
layout_app_exelist.extend(package.dummy_linked_exelist)
158+
layout_sys_exelist.extend(package.installed_targets)
159+
elif package.backend == Backend.Meson:
160+
layout_app_exelist.extend(package.dummy_linked_targets)
140161

141162
firmware_layout = ninja.add_internal_gen_memory_layout_target(
142163
output=pathlib.Path(self.path.private_build_dir, "layout.json"),
@@ -153,10 +174,10 @@ def setup(self) -> None:
153174

154175
# gen_ld/relink/gen_meta/objcopy app(s)
155176
for package in self._packages:
156-
if package.is_app_package:
177+
if package.is_application and package.backend == Backend.Meson:
157178
# XXX: Handle multiple exe package
158-
elf_in = package.installed_exelist[0]
159-
elf_out = package.relocated_exelist[0]
179+
elf_in = package.installed_targets[0]
180+
elf_out = package.relocated_targets[0]
160181
linker_script = pathlib.Path(self.path.private_build_dir, f"{elf_in.stem}.lds")
161182
metadata_out = elf_out.with_suffix(".meta")
162183
hex_out = elf_out.with_suffix(".hex")
@@ -185,12 +206,12 @@ def setup(self) -> None:
185206
app_metadata.append(metadata_out)
186207

187208
# Patch kernel/objcopy
188-
kernel_elf = self._packages[0].installed_exelist[1]
189-
kernel_patched_elf = self._packages[0].relocated_exelist[1]
209+
kernel_elf = self._packages[0].installed_targets[1]
210+
kernel_patched_elf = self._packages[0].relocated_targets[1]
190211
kernel_hex = kernel_patched_elf.with_suffix(".hex")
191-
# idle_elf = self._packages[0].installed_exelist[0]
212+
# idle_elf = self._packages[0].installed_targets[0]
192213
# XXX this is ugly (...)
193-
idle_hex = self._packages[0].installed_exelist[0].with_suffix(".hex")
214+
idle_hex = self._packages[0].installed_targets[0].with_suffix(".hex")
194215

195216
ninja.add_fixup_kernel_rule(kernel_elf, kernel_patched_elf, app_metadata)
196217
ninja.add_objcopy_rule(kernel_patched_elf, kernel_hex, "ihex", [], self._packages[0].name)

src/outpost/barbican/buildsys/ninja_backend.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def add_gen_ldscript_target(
139139
layout: Path,
140140
package_name: Optional[str] = None,
141141
) -> None:
142-
implicit_inputs = ["libshield_install.stamp"]
142+
implicit_inputs = ["runtime_install.stamp"]
143143
if name != "dummy":
144144
implicit_inputs.append(f"{package_name if package_name else name}_install.stamp")
145145
self._ninja.newline()
@@ -271,6 +271,55 @@ def add_meson_rules(self) -> None:
271271
"touch $out",
272272
)
273273

274+
def add_cargo_rules(self) -> None:
275+
self._ninja.newline()
276+
self._ninja.variable("cargo", find_program("cargo"))
277+
self._ninja.newline()
278+
self._ninja.rule(
279+
"cargo_compile",
280+
description="cargo compile $name",
281+
pool="console",
282+
command="$cargo build -Z unstable-options --manifest-path=$sourcedir/Cargo.toml "
283+
"--target-dir=$builddir --out-dir=$builddir && touch $out",
284+
)
285+
self._ninja.newline()
286+
self._ninja.rule(
287+
"cargo_install",
288+
description="cargo install $name",
289+
pool="console",
290+
command="touch $out",
291+
)
292+
293+
def add_cargo_package(self, package: "Package") -> None:
294+
self._ninja.newline()
295+
self._ninja.build(
296+
f"{package.name}_compile.stamp",
297+
"cargo_compile",
298+
variables={
299+
"sourcedir": package.src_dir,
300+
"builddir": package.build_dir,
301+
"name": package.name,
302+
},
303+
)
304+
self._ninja.newline()
305+
self._ninja.build(f"{package.name}_compile", "phony", f"{package.name}_compile.stamp")
306+
self._ninja.newline()
307+
self._ninja.build(
308+
f"{package.name}_install.stamp",
309+
"internal",
310+
implicit=f"{package.name}_compile",
311+
variables={
312+
"cmd": "install",
313+
"args": f"--suffix=.elf {str(package.build_dir)} "
314+
+ " ".join((str(t) for t in package.installed_targets)), # noqa: W503
315+
"description": f"cargo install {package.name}",
316+
},
317+
)
318+
319+
self._ninja.newline()
320+
self._ninja.build(f"{package.name}_install", "phony", f"{package.name}_install.stamp")
321+
self._ninja.newline()
322+
274323
def add_meson_package(self, package: "Package") -> None:
275324
self._ninja.newline()
276325
self._ninja.build(
@@ -280,7 +329,7 @@ def add_meson_package(self, package: "Package") -> None:
280329
"builddir": package.build_dir,
281330
"sourcedir": package.src_dir,
282331
"name": package.name,
283-
"opts": package.build_opts,
332+
"opts": package.build_options,
284333
},
285334
order_only=[f"{dep}_install.stamp" for dep in package.deps],
286335
)

0 commit comments

Comments
 (0)