mojox-core 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mojox_core-0.5.0/.gitignore +10 -0
- mojox_core-0.5.0/PKG-INFO +178 -0
- mojox_core-0.5.0/README.md +166 -0
- mojox_core-0.5.0/pyproject.toml +28 -0
- mojox_core-0.5.0/src/mojox_core/__init__.py +63 -0
- mojox_core-0.5.0/src/mojox_core/_errors.py +16 -0
- mojox_core-0.5.0/src/mojox_core/_types.py +235 -0
- mojox_core-0.5.0/src/mojox_core/environment.py +134 -0
- mojox_core-0.5.0/src/mojox_core/io/__init__.py +1 -0
- mojox_core-0.5.0/src/mojox_core/io/environment.py +114 -0
- mojox_core-0.5.0/src/mojox_core/io/manifest.py +29 -0
- mojox_core-0.5.0/src/mojox_core/io/toolchain.py +98 -0
- mojox_core-0.5.0/src/mojox_core/manifest.py +354 -0
- mojox_core-0.5.0/src/mojox_core/metadata.py +118 -0
- mojox_core-0.5.0/src/mojox_core/plan.py +333 -0
- mojox_core-0.5.0/src/mojox_core/policy.py +160 -0
- mojox_core-0.5.0/src/mojox_core/py.typed +0 -0
- mojox_core-0.5.0/src/mojox_core/settings.py +90 -0
- mojox_core-0.5.0/src/mojox_core/targets.py +195 -0
- mojox_core-0.5.0/tests/conftest.py +75 -0
- mojox_core-0.5.0/tests/test_environment.py +73 -0
- mojox_core-0.5.0/tests/test_errors.py +21 -0
- mojox_core-0.5.0/tests/test_manifest.py +223 -0
- mojox_core-0.5.0/tests/test_metadata.py +108 -0
- mojox_core-0.5.0/tests/test_plan.py +453 -0
- mojox_core-0.5.0/tests/test_policy.py +156 -0
- mojox_core-0.5.0/tests/test_purity.py +70 -0
- mojox_core-0.5.0/tests/test_settings.py +59 -0
- mojox_core-0.5.0/tests/test_targets.py +276 -0
- mojox_core-0.5.0/tests/test_types.py +180 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: mojox-core
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Pure model layer for mojox: manifest parsing, target discovery, policy resolution, and build planning.
|
|
5
|
+
Project-URL: Repository, https://github.com/Conobi/mojox
|
|
6
|
+
Author: Conobi
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Requires-Dist: packaging>=23.0
|
|
10
|
+
Requires-Dist: tomli>=1.1.0; python_version < '3.11'
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# mojox-core
|
|
14
|
+
|
|
15
|
+
Pure model layer for the [mojox](https://github.com/Conobi/mojox) build system.
|
|
16
|
+
No I/O, no subprocess, no filesystem access at module level.
|
|
17
|
+
All data types are frozen dataclasses; every transformer is a pure function
|
|
18
|
+
(data in, data out).
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
uv add mojox-core
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Requires Python 3.10+. Only runtime dependency is `packaging>=23.0`
|
|
27
|
+
(plus `tomli` on Python < 3.11).
|
|
28
|
+
|
|
29
|
+
## Pipeline
|
|
30
|
+
|
|
31
|
+
The core exposes a five-stage pipeline where each step takes immutable data
|
|
32
|
+
and returns immutable data:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
parse -> discover -> resolve -> plan -> serialize
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
I/O readers live in the separate `mojox_core.io` subpackage. Consumers can
|
|
39
|
+
skip them entirely and feed fixtures directly into the pipeline.
|
|
40
|
+
|
|
41
|
+
## Quick start
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from pathlib import Path
|
|
45
|
+
|
|
46
|
+
from mojox_core import (
|
|
47
|
+
parse_manifest,
|
|
48
|
+
discover,
|
|
49
|
+
resolve,
|
|
50
|
+
plan,
|
|
51
|
+
serialize,
|
|
52
|
+
LocalSettings,
|
|
53
|
+
)
|
|
54
|
+
from mojox_core.io.manifest import read as read_manifest
|
|
55
|
+
from mojox_core.io.toolchain import resolve as resolve_toolchain
|
|
56
|
+
from mojox_core.io.environment import (
|
|
57
|
+
read_distributions,
|
|
58
|
+
read_lockfile,
|
|
59
|
+
read_host_facts,
|
|
60
|
+
)
|
|
61
|
+
from mojox_core import build_env
|
|
62
|
+
|
|
63
|
+
# 1. Read raw TOML (I/O)
|
|
64
|
+
raw = read_manifest(Path("."))
|
|
65
|
+
|
|
66
|
+
# 2. Parse manifest (pure)
|
|
67
|
+
manifest = parse_manifest(raw)
|
|
68
|
+
|
|
69
|
+
# 3. Discover targets by convention (pure)
|
|
70
|
+
targets = discover(manifest, Path("."))
|
|
71
|
+
|
|
72
|
+
# 4. Resolve build policy from all precedence layers (pure)
|
|
73
|
+
policy = resolve(manifest, "dev", {}, LocalSettings.EMPTY)
|
|
74
|
+
|
|
75
|
+
# 5. Detect toolchain and build environment (I/O)
|
|
76
|
+
toolchain = resolve_toolchain()
|
|
77
|
+
dists = read_distributions()
|
|
78
|
+
lock = read_lockfile(Path("."))
|
|
79
|
+
env = build_env(dists, lock, toolchain.mojo_path, toolchain.version)
|
|
80
|
+
host = read_host_facts(Path("."))
|
|
81
|
+
|
|
82
|
+
# 6. Plan mojo invocations (pure)
|
|
83
|
+
commands = plan(targets, env, policy, toolchain, host)
|
|
84
|
+
|
|
85
|
+
for cmd in commands:
|
|
86
|
+
print(cmd.kind, cmd.argv)
|
|
87
|
+
|
|
88
|
+
# 7. Serialize to the `mojox metadata` JSON schema (pure)
|
|
89
|
+
metadata = serialize(targets, env, policy, commands, toolchain, diagnostics=())
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Public API
|
|
93
|
+
|
|
94
|
+
### Types
|
|
95
|
+
|
|
96
|
+
All exported from `mojox_core`. Every type is a frozen dataclass or enum.
|
|
97
|
+
|
|
98
|
+
| Type | Description |
|
|
99
|
+
|------|-------------|
|
|
100
|
+
| `Manifest` | Parsed `[tool.mojox]` config and PEP 621 project metadata |
|
|
101
|
+
| `Command` | A planned mojo invocation (data, not execution) |
|
|
102
|
+
| `CommandKind` | Enum of command kinds |
|
|
103
|
+
| `Policy` | Resolved build policy (profile + defines + flags) |
|
|
104
|
+
| `Profile` | Build profile (`dev` or `release`) with optimization/debug settings |
|
|
105
|
+
| `Target` | A single build/test/example/binary target |
|
|
106
|
+
| `TargetKind` | Enum of target kinds |
|
|
107
|
+
| `TargetGraph` | The full set of discovered targets |
|
|
108
|
+
| `ResolvedEnv` | Resolved environment (import paths, library paths, mojo binary) |
|
|
109
|
+
| `Toolchain` | Detected Mojo toolchain info |
|
|
110
|
+
| `HostFacts` | Host platform facts |
|
|
111
|
+
| `LocalSettings` | Parsed `.mojox/config.toml` settings |
|
|
112
|
+
| `Diagnostic` | Lint diagnostic |
|
|
113
|
+
| `LintConfig` | Lint configuration |
|
|
114
|
+
| `DistEntry` | Distribution metadata entry |
|
|
115
|
+
| `DistKind` | Enum of distribution kinds |
|
|
116
|
+
| `BinaryEntry` | Binary entry in a distribution |
|
|
117
|
+
|
|
118
|
+
### Transformers
|
|
119
|
+
|
|
120
|
+
Pure functions. No side effects, no I/O.
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
parse_manifest(data: dict) -> Manifest
|
|
124
|
+
discover(manifest: Manifest, root: Path) -> TargetGraph
|
|
125
|
+
resolve(manifest: Manifest, profile_name: str, cli_overrides: dict, settings: LocalSettings) -> Policy
|
|
126
|
+
plan(graph: TargetGraph, env: ResolvedEnv, policy: Policy, toolchain: Toolchain, host: HostFacts) -> tuple[Command, ...]
|
|
127
|
+
build_env(dists: list[dict], lock_data: dict | None, mojo_path: str, mojo_version: str) -> ResolvedEnv
|
|
128
|
+
serialize(graph: TargetGraph, env: ResolvedEnv, policy: Policy, commands: tuple[Command, ...], toolchain: Toolchain, diagnostics: tuple[Diagnostic, ...]) -> dict
|
|
129
|
+
parse_settings(user: dict | None, project: dict | None, env: dict[str, str]) -> LocalSettings
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### I/O readers
|
|
133
|
+
|
|
134
|
+
In the `mojox_core.io` subpackage. These are the only functions that touch
|
|
135
|
+
the filesystem or run subprocesses.
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
mojox_core.io.manifest.read(path: Path) -> dict # read & parse pyproject.toml
|
|
139
|
+
mojox_core.io.toolchain.resolve() -> Toolchain # detect installed Mojo compiler
|
|
140
|
+
mojox_core.io.environment.read_distributions(platlib=None) -> list[dict] # scan installed dists
|
|
141
|
+
mojox_core.io.environment.read_lockfile(project_root: Path) -> dict | None
|
|
142
|
+
mojox_core.io.environment.read_host_facts(manifest_dir: Path) -> HostFacts
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Errors
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
class ConfigError(Exception):
|
|
149
|
+
key_path: str # dotted path to the offending key
|
|
150
|
+
message: str # human-readable explanation
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Raised on invalid manifest or settings. No `KeyError` or `TypeError` reaches
|
|
154
|
+
the caller -- every rejection is a `ConfigError` with a structured key path.
|
|
155
|
+
|
|
156
|
+
## Testing without I/O
|
|
157
|
+
|
|
158
|
+
Because the pipeline is pure, you can test any stage by constructing fixtures
|
|
159
|
+
directly:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from mojox_core import Manifest, discover, TargetGraph
|
|
163
|
+
|
|
164
|
+
manifest = Manifest(...) # build the frozen dataclass by hand
|
|
165
|
+
graph = discover(manifest, some_temp_dir)
|
|
166
|
+
assert isinstance(graph, TargetGraph)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
No mojo compiler, no installed packages, no pyproject.toml required.
|
|
170
|
+
|
|
171
|
+
## See also
|
|
172
|
+
|
|
173
|
+
- [mojox](https://github.com/Conobi/mojox) -- CLI that drives the full build
|
|
174
|
+
- [mojox-build](https://github.com/Conobi/mojox/tree/main/packages/mojox-build) -- PEP 517 build backend
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
MIT
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# mojox-core
|
|
2
|
+
|
|
3
|
+
Pure model layer for the [mojox](https://github.com/Conobi/mojox) build system.
|
|
4
|
+
No I/O, no subprocess, no filesystem access at module level.
|
|
5
|
+
All data types are frozen dataclasses; every transformer is a pure function
|
|
6
|
+
(data in, data out).
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
uv add mojox-core
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Requires Python 3.10+. Only runtime dependency is `packaging>=23.0`
|
|
15
|
+
(plus `tomli` on Python < 3.11).
|
|
16
|
+
|
|
17
|
+
## Pipeline
|
|
18
|
+
|
|
19
|
+
The core exposes a five-stage pipeline where each step takes immutable data
|
|
20
|
+
and returns immutable data:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
parse -> discover -> resolve -> plan -> serialize
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
I/O readers live in the separate `mojox_core.io` subpackage. Consumers can
|
|
27
|
+
skip them entirely and feed fixtures directly into the pipeline.
|
|
28
|
+
|
|
29
|
+
## Quick start
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
|
|
34
|
+
from mojox_core import (
|
|
35
|
+
parse_manifest,
|
|
36
|
+
discover,
|
|
37
|
+
resolve,
|
|
38
|
+
plan,
|
|
39
|
+
serialize,
|
|
40
|
+
LocalSettings,
|
|
41
|
+
)
|
|
42
|
+
from mojox_core.io.manifest import read as read_manifest
|
|
43
|
+
from mojox_core.io.toolchain import resolve as resolve_toolchain
|
|
44
|
+
from mojox_core.io.environment import (
|
|
45
|
+
read_distributions,
|
|
46
|
+
read_lockfile,
|
|
47
|
+
read_host_facts,
|
|
48
|
+
)
|
|
49
|
+
from mojox_core import build_env
|
|
50
|
+
|
|
51
|
+
# 1. Read raw TOML (I/O)
|
|
52
|
+
raw = read_manifest(Path("."))
|
|
53
|
+
|
|
54
|
+
# 2. Parse manifest (pure)
|
|
55
|
+
manifest = parse_manifest(raw)
|
|
56
|
+
|
|
57
|
+
# 3. Discover targets by convention (pure)
|
|
58
|
+
targets = discover(manifest, Path("."))
|
|
59
|
+
|
|
60
|
+
# 4. Resolve build policy from all precedence layers (pure)
|
|
61
|
+
policy = resolve(manifest, "dev", {}, LocalSettings.EMPTY)
|
|
62
|
+
|
|
63
|
+
# 5. Detect toolchain and build environment (I/O)
|
|
64
|
+
toolchain = resolve_toolchain()
|
|
65
|
+
dists = read_distributions()
|
|
66
|
+
lock = read_lockfile(Path("."))
|
|
67
|
+
env = build_env(dists, lock, toolchain.mojo_path, toolchain.version)
|
|
68
|
+
host = read_host_facts(Path("."))
|
|
69
|
+
|
|
70
|
+
# 6. Plan mojo invocations (pure)
|
|
71
|
+
commands = plan(targets, env, policy, toolchain, host)
|
|
72
|
+
|
|
73
|
+
for cmd in commands:
|
|
74
|
+
print(cmd.kind, cmd.argv)
|
|
75
|
+
|
|
76
|
+
# 7. Serialize to the `mojox metadata` JSON schema (pure)
|
|
77
|
+
metadata = serialize(targets, env, policy, commands, toolchain, diagnostics=())
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Public API
|
|
81
|
+
|
|
82
|
+
### Types
|
|
83
|
+
|
|
84
|
+
All exported from `mojox_core`. Every type is a frozen dataclass or enum.
|
|
85
|
+
|
|
86
|
+
| Type | Description |
|
|
87
|
+
|------|-------------|
|
|
88
|
+
| `Manifest` | Parsed `[tool.mojox]` config and PEP 621 project metadata |
|
|
89
|
+
| `Command` | A planned mojo invocation (data, not execution) |
|
|
90
|
+
| `CommandKind` | Enum of command kinds |
|
|
91
|
+
| `Policy` | Resolved build policy (profile + defines + flags) |
|
|
92
|
+
| `Profile` | Build profile (`dev` or `release`) with optimization/debug settings |
|
|
93
|
+
| `Target` | A single build/test/example/binary target |
|
|
94
|
+
| `TargetKind` | Enum of target kinds |
|
|
95
|
+
| `TargetGraph` | The full set of discovered targets |
|
|
96
|
+
| `ResolvedEnv` | Resolved environment (import paths, library paths, mojo binary) |
|
|
97
|
+
| `Toolchain` | Detected Mojo toolchain info |
|
|
98
|
+
| `HostFacts` | Host platform facts |
|
|
99
|
+
| `LocalSettings` | Parsed `.mojox/config.toml` settings |
|
|
100
|
+
| `Diagnostic` | Lint diagnostic |
|
|
101
|
+
| `LintConfig` | Lint configuration |
|
|
102
|
+
| `DistEntry` | Distribution metadata entry |
|
|
103
|
+
| `DistKind` | Enum of distribution kinds |
|
|
104
|
+
| `BinaryEntry` | Binary entry in a distribution |
|
|
105
|
+
|
|
106
|
+
### Transformers
|
|
107
|
+
|
|
108
|
+
Pure functions. No side effects, no I/O.
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
parse_manifest(data: dict) -> Manifest
|
|
112
|
+
discover(manifest: Manifest, root: Path) -> TargetGraph
|
|
113
|
+
resolve(manifest: Manifest, profile_name: str, cli_overrides: dict, settings: LocalSettings) -> Policy
|
|
114
|
+
plan(graph: TargetGraph, env: ResolvedEnv, policy: Policy, toolchain: Toolchain, host: HostFacts) -> tuple[Command, ...]
|
|
115
|
+
build_env(dists: list[dict], lock_data: dict | None, mojo_path: str, mojo_version: str) -> ResolvedEnv
|
|
116
|
+
serialize(graph: TargetGraph, env: ResolvedEnv, policy: Policy, commands: tuple[Command, ...], toolchain: Toolchain, diagnostics: tuple[Diagnostic, ...]) -> dict
|
|
117
|
+
parse_settings(user: dict | None, project: dict | None, env: dict[str, str]) -> LocalSettings
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### I/O readers
|
|
121
|
+
|
|
122
|
+
In the `mojox_core.io` subpackage. These are the only functions that touch
|
|
123
|
+
the filesystem or run subprocesses.
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
mojox_core.io.manifest.read(path: Path) -> dict # read & parse pyproject.toml
|
|
127
|
+
mojox_core.io.toolchain.resolve() -> Toolchain # detect installed Mojo compiler
|
|
128
|
+
mojox_core.io.environment.read_distributions(platlib=None) -> list[dict] # scan installed dists
|
|
129
|
+
mojox_core.io.environment.read_lockfile(project_root: Path) -> dict | None
|
|
130
|
+
mojox_core.io.environment.read_host_facts(manifest_dir: Path) -> HostFacts
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Errors
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
class ConfigError(Exception):
|
|
137
|
+
key_path: str # dotted path to the offending key
|
|
138
|
+
message: str # human-readable explanation
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Raised on invalid manifest or settings. No `KeyError` or `TypeError` reaches
|
|
142
|
+
the caller -- every rejection is a `ConfigError` with a structured key path.
|
|
143
|
+
|
|
144
|
+
## Testing without I/O
|
|
145
|
+
|
|
146
|
+
Because the pipeline is pure, you can test any stage by constructing fixtures
|
|
147
|
+
directly:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from mojox_core import Manifest, discover, TargetGraph
|
|
151
|
+
|
|
152
|
+
manifest = Manifest(...) # build the frozen dataclass by hand
|
|
153
|
+
graph = discover(manifest, some_temp_dir)
|
|
154
|
+
assert isinstance(graph, TargetGraph)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
No mojo compiler, no installed packages, no pyproject.toml required.
|
|
158
|
+
|
|
159
|
+
## See also
|
|
160
|
+
|
|
161
|
+
- [mojox](https://github.com/Conobi/mojox) -- CLI that drives the full build
|
|
162
|
+
- [mojox-build](https://github.com/Conobi/mojox/tree/main/packages/mojox-build) -- PEP 517 build backend
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "mojox-core"
|
|
3
|
+
version = "0.5.0"
|
|
4
|
+
description = "Pure model layer for mojox: manifest parsing, target discovery, policy resolution, and build planning."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
requires-python = ">=3.10"
|
|
8
|
+
authors = [{ name = "Conobi" }]
|
|
9
|
+
dependencies = [
|
|
10
|
+
"packaging>=23.0",
|
|
11
|
+
'tomli >= 1.1.0; python_version < "3.11"',
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[project.urls]
|
|
15
|
+
Repository = "https://github.com/Conobi/mojox"
|
|
16
|
+
|
|
17
|
+
[build-system]
|
|
18
|
+
requires = ["hatchling"]
|
|
19
|
+
build-backend = "hatchling.build"
|
|
20
|
+
|
|
21
|
+
[tool.hatch.build.targets.wheel]
|
|
22
|
+
packages = ["src/mojox_core"]
|
|
23
|
+
|
|
24
|
+
[tool.pytest.ini_options]
|
|
25
|
+
testpaths = ["tests"]
|
|
26
|
+
|
|
27
|
+
[tool.mypy]
|
|
28
|
+
strict = true
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""mojox-core: pure model layer for Mojo build tooling.
|
|
2
|
+
|
|
3
|
+
Public API:
|
|
4
|
+
- Types: Manifest, Command, Policy, ResolvedEnv, TargetGraph, etc.
|
|
5
|
+
- Transformers: parse_manifest, build_env, discover, resolve, plan, serialize
|
|
6
|
+
- Errors: ConfigError
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from ._errors import ConfigError
|
|
10
|
+
from ._types import (
|
|
11
|
+
BinaryEntry,
|
|
12
|
+
Command,
|
|
13
|
+
CommandKind,
|
|
14
|
+
Diagnostic,
|
|
15
|
+
DistEntry,
|
|
16
|
+
DistKind,
|
|
17
|
+
HostFacts,
|
|
18
|
+
LintConfig,
|
|
19
|
+
LocalSettings,
|
|
20
|
+
Manifest,
|
|
21
|
+
Policy,
|
|
22
|
+
Profile,
|
|
23
|
+
ResolvedEnv,
|
|
24
|
+
Target,
|
|
25
|
+
TargetGraph,
|
|
26
|
+
TargetKind,
|
|
27
|
+
Toolchain,
|
|
28
|
+
)
|
|
29
|
+
from .environment import build_env
|
|
30
|
+
from .manifest import parse_manifest
|
|
31
|
+
from .metadata import serialize
|
|
32
|
+
from .plan import plan
|
|
33
|
+
from .policy import resolve
|
|
34
|
+
from .settings import parse_settings
|
|
35
|
+
from .targets import discover
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"BinaryEntry",
|
|
39
|
+
"Command",
|
|
40
|
+
"CommandKind",
|
|
41
|
+
"ConfigError",
|
|
42
|
+
"Diagnostic",
|
|
43
|
+
"DistEntry",
|
|
44
|
+
"DistKind",
|
|
45
|
+
"HostFacts",
|
|
46
|
+
"LintConfig",
|
|
47
|
+
"LocalSettings",
|
|
48
|
+
"Manifest",
|
|
49
|
+
"Policy",
|
|
50
|
+
"Profile",
|
|
51
|
+
"ResolvedEnv",
|
|
52
|
+
"Target",
|
|
53
|
+
"TargetGraph",
|
|
54
|
+
"TargetKind",
|
|
55
|
+
"Toolchain",
|
|
56
|
+
"build_env",
|
|
57
|
+
"discover",
|
|
58
|
+
"parse_manifest",
|
|
59
|
+
"parse_settings",
|
|
60
|
+
"plan",
|
|
61
|
+
"resolve",
|
|
62
|
+
"serialize",
|
|
63
|
+
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""User-facing configuration errors with structured key paths."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ConfigError(Exception):
|
|
7
|
+
"""A configuration error carrying the offending key path and a remediation.
|
|
8
|
+
|
|
9
|
+
No KeyError, no TypeError, no stack trace reaches the user — every
|
|
10
|
+
rejection produces one of these with a human-readable message.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, key_path: str, message: str) -> None:
|
|
14
|
+
self.key_path = key_path
|
|
15
|
+
self.message = message
|
|
16
|
+
super().__init__(f"[{key_path}] {message}")
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""All frozen dataclasses for the mojox-core model layer.
|
|
2
|
+
|
|
3
|
+
Every type here is immutable (frozen=True). No type in this module imports
|
|
4
|
+
subprocess or performs I/O. Path-valued fields use PurePosixPath for lexical
|
|
5
|
+
paths (manifest-relative) and str for resolved absolute paths (from readers).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from collections.abc import Mapping
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from enum import Enum
|
|
13
|
+
from pathlib import PurePosixPath
|
|
14
|
+
from typing import ClassVar, Literal
|
|
15
|
+
|
|
16
|
+
# -- Enums -----------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CommandKind(Enum):
|
|
20
|
+
"""The kind of mojo invocation a Command represents."""
|
|
21
|
+
|
|
22
|
+
COMPILE_PACKAGE = "compile-package"
|
|
23
|
+
COMPILE_BINARY = "compile-binary"
|
|
24
|
+
RUN_TEST = "run-test"
|
|
25
|
+
CHECK_EXAMPLE = "check-example"
|
|
26
|
+
RUN = "run"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class TargetKind(Enum):
|
|
30
|
+
"""Discovered target kinds."""
|
|
31
|
+
|
|
32
|
+
LIB = "lib"
|
|
33
|
+
BIN = "bin"
|
|
34
|
+
TEST = "test"
|
|
35
|
+
EXAMPLE = "example"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class DistKind(Enum):
|
|
39
|
+
"""How a distribution provides its Mojo packages."""
|
|
40
|
+
|
|
41
|
+
SOURCE = "source"
|
|
42
|
+
PRECOMPILED = "precompiled"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# -- Leaf types ------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True)
|
|
49
|
+
class BinaryEntry:
|
|
50
|
+
"""One executable produced by ``mojo build``."""
|
|
51
|
+
|
|
52
|
+
source: str
|
|
53
|
+
name: str
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True)
|
|
57
|
+
class LintConfig:
|
|
58
|
+
"""Lint settings that translate to compiler flags."""
|
|
59
|
+
|
|
60
|
+
warnings_as_errors: bool = False
|
|
61
|
+
check_doc_strings: bool = False
|
|
62
|
+
missing_doc_strings: bool = False
|
|
63
|
+
unstable_apis: bool = False
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(frozen=True)
|
|
67
|
+
class Diagnostic:
|
|
68
|
+
"""A compiler or mojox diagnostic."""
|
|
69
|
+
|
|
70
|
+
kind: Literal["error", "warning", "note"]
|
|
71
|
+
message: str
|
|
72
|
+
file: str | None = None
|
|
73
|
+
line: int | None = None
|
|
74
|
+
column: int | None = None
|
|
75
|
+
source_text: str | None = None
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass(frozen=True)
|
|
79
|
+
class Target:
|
|
80
|
+
"""A discovered build target."""
|
|
81
|
+
|
|
82
|
+
kind: TargetKind
|
|
83
|
+
path: str
|
|
84
|
+
target_id: str
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass(frozen=True)
|
|
88
|
+
class DistEntry:
|
|
89
|
+
"""One installed distribution contributing to the include sequence."""
|
|
90
|
+
|
|
91
|
+
name: str
|
|
92
|
+
include_dir: str
|
|
93
|
+
kind: DistKind
|
|
94
|
+
packages: tuple[str, ...]
|
|
95
|
+
provenance: str
|
|
96
|
+
native_lib_dirs: tuple[str, ...] = ()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# -- Compound types --------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@dataclass(frozen=True)
|
|
103
|
+
class Profile:
|
|
104
|
+
"""A named compilation profile (dev, release, or user-declared)."""
|
|
105
|
+
|
|
106
|
+
optimize: int | None = None
|
|
107
|
+
debug_level: str | None = None
|
|
108
|
+
defines: dict[str, str] = field(default_factory=dict)
|
|
109
|
+
flags: tuple[str, ...] = ()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@dataclass(frozen=True)
|
|
113
|
+
class Manifest:
|
|
114
|
+
"""Parsed pyproject.toml -- portable, published, versioned with the code."""
|
|
115
|
+
|
|
116
|
+
# [project]
|
|
117
|
+
name: str
|
|
118
|
+
version: str
|
|
119
|
+
description: str | None
|
|
120
|
+
readme: str | None
|
|
121
|
+
license_expr: str | None
|
|
122
|
+
license_files: tuple[str, ...]
|
|
123
|
+
requires_python: str | None
|
|
124
|
+
dependencies: tuple[str, ...]
|
|
125
|
+
optional_dependencies: dict[str, tuple[str, ...]]
|
|
126
|
+
keywords: tuple[str, ...]
|
|
127
|
+
authors: tuple[dict[str, str], ...]
|
|
128
|
+
maintainers: tuple[dict[str, str], ...]
|
|
129
|
+
urls: dict[str, str]
|
|
130
|
+
classifiers: tuple[str, ...]
|
|
131
|
+
|
|
132
|
+
# [tool.mojox]
|
|
133
|
+
packages: tuple[str, ...] | None
|
|
134
|
+
package_root: str
|
|
135
|
+
binaries: tuple[BinaryEntry, ...]
|
|
136
|
+
test_roots: tuple[str, ...]
|
|
137
|
+
test_parallel: bool
|
|
138
|
+
defines: dict[str, str]
|
|
139
|
+
flags: tuple[str, ...]
|
|
140
|
+
lints: LintConfig
|
|
141
|
+
optimize: int | None
|
|
142
|
+
debug_level: str | None
|
|
143
|
+
pre_build: tuple[tuple[str, ...], ...]
|
|
144
|
+
native_libs: tuple[str, ...]
|
|
145
|
+
source_include: tuple[str, ...] | None
|
|
146
|
+
source_exclude: tuple[str, ...]
|
|
147
|
+
wheel_exclude: tuple[str, ...]
|
|
148
|
+
profiles: dict[str, Profile]
|
|
149
|
+
build_profile: str
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@dataclass(frozen=True)
|
|
153
|
+
class TargetGraph:
|
|
154
|
+
"""Discovered targets in topological order (lib targets first)."""
|
|
155
|
+
|
|
156
|
+
targets: tuple[Target, ...]
|
|
157
|
+
edges: tuple[tuple[str, str], ...]
|
|
158
|
+
unsearched_test_dirs: tuple[tuple[str, int], ...] = ()
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@dataclass(frozen=True)
|
|
162
|
+
class ResolvedEnv:
|
|
163
|
+
"""The resolved build environment -- include sequence, mojo binary, provenance."""
|
|
164
|
+
|
|
165
|
+
include_sequence: tuple[DistEntry, ...]
|
|
166
|
+
mojo_path: str
|
|
167
|
+
mojo_version: str
|
|
168
|
+
path_mojo: str | None
|
|
169
|
+
lock_version: int | None
|
|
170
|
+
diagnostics: tuple[Diagnostic, ...] = ()
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@dataclass(frozen=True)
|
|
174
|
+
class Toolchain:
|
|
175
|
+
"""Resolved Mojo toolchain identity."""
|
|
176
|
+
|
|
177
|
+
mojo_path: str
|
|
178
|
+
version: str
|
|
179
|
+
subcommand: str
|
|
180
|
+
extension: str
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@dataclass(frozen=True)
|
|
184
|
+
class HostFacts:
|
|
185
|
+
"""Machine-specific facts injected into the planner for determinism."""
|
|
186
|
+
|
|
187
|
+
cpu_count: int
|
|
188
|
+
available_memory_mb: int
|
|
189
|
+
manifest_dir: PurePosixPath
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@dataclass(frozen=True)
|
|
193
|
+
class LocalSettings:
|
|
194
|
+
"""Machine-local settings (from .mojox/config.toml + env vars)."""
|
|
195
|
+
|
|
196
|
+
jobs: int | None = None
|
|
197
|
+
timeout_s: int | None = None
|
|
198
|
+
config_paths: tuple[str, ...] = ()
|
|
199
|
+
env: dict[str, str] = field(default_factory=dict)
|
|
200
|
+
|
|
201
|
+
EMPTY: ClassVar[LocalSettings] # set below
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
# Bootstrap the EMPTY sentinel after the class is defined.
|
|
205
|
+
LocalSettings.EMPTY = LocalSettings()
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@dataclass(frozen=True)
|
|
209
|
+
class Policy:
|
|
210
|
+
"""The fully resolved flag set the planner consumes."""
|
|
211
|
+
|
|
212
|
+
optimize: int
|
|
213
|
+
debug_level: str
|
|
214
|
+
defines: dict[str, str]
|
|
215
|
+
flags: tuple[str, ...]
|
|
216
|
+
include_paths: tuple[str, ...]
|
|
217
|
+
lints: LintConfig
|
|
218
|
+
jobs: int
|
|
219
|
+
jobs_compile: int
|
|
220
|
+
jobs_tests: int
|
|
221
|
+
timeout_s: int
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@dataclass(frozen=True)
|
|
225
|
+
class Command:
|
|
226
|
+
"""One mojo invocation the planner produces."""
|
|
227
|
+
|
|
228
|
+
argv: tuple[str, ...]
|
|
229
|
+
cwd: PurePosixPath
|
|
230
|
+
env: Mapping[str, str]
|
|
231
|
+
kind: CommandKind
|
|
232
|
+
target_id: str
|
|
233
|
+
timeout_s: int | None
|
|
234
|
+
outputs: tuple[str, ...]
|
|
235
|
+
depends_on: tuple[str, ...]
|