Skip to content

Commit 760f6a6

Browse files
committed
docs: update BCASL and engine documentation with new features and changes
1 parent ed10679 commit 760f6a6

3 files changed

Lines changed: 200 additions & 39 deletions

File tree

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ python pycompiler_ark.py engines --dry-run
8282

8383
---
8484

85+
## 📚 Documentation
86+
87+
- [How to create an engine](docs/how_to_create_an_engine.md)
88+
- [How to create a BC plugin](docs/how_to_create_a_bc_plugin.md)
89+
90+
---
91+
8592
## 🔁 BCASL Pipeline (quick view)
8693

8794
```text

docs/how_to_create_a_bc_plugin.md

Lines changed: 108 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
**Overview**
2-
BC plugins (BCASL) run before compilation. They are Python packages placed in `Plugins/`, discovered automatically, and executed through the `on_pre_compile` hook.
2+
A BC plugin (BCASL) is a package placed in `Plugins/` and executed before compilation. It registers automatically, respects execution order (priority, tags, dependencies), and uses `PreCompileContext` to work with the workspace.
33

4-
**Steps**
5-
1. Create a package folder in `Plugins/<plugin_name>/` with an `__init__.py`.
6-
2. Implement a subclass of `BcPluginBase` and decorate it with `@bc_register`.
7-
3. Define a `PluginMeta` with a unique `id`.
8-
4. Configure the plugin in `bcasl.yml` (optional but recommended for ordering and enable/disable).
4+
**Discovery And Loading**
5+
- Plugins are discovered in `Plugins/<plugin_name>/`.
6+
- The folder must contain an `__init__.py`.
7+
- The loader imports each package and detects plugins via `@bc_register` or `bcasl_register(manager)`.
8+
- If `bcasl.yml` is missing, a default file is generated.
99

10-
**Minimal Plugin**
10+
**Package Layout**
11+
- `Plugins/<plugin_name>/__init__.py`: main plugin code.
12+
- Optional internal modules: helpers, config, assets.
13+
14+
**Recommended Registration (Decorator)**
1115
```python
1216
from __future__ import annotations
1317

@@ -50,8 +54,41 @@ class ExampleClean(BcPluginBase):
5054
log.log_warn(f"Failed to remove {pyc}: {exc}")
5155
```
5256

53-
**Configuration**
57+
**Legacy Registration (Function)**
58+
```python
59+
from bcasl import BCASL
60+
from Plugins_SDK.BcPluginContext import BcPluginBase, PluginMeta
61+
62+
class MyPlugin(BcPluginBase):
63+
meta = PluginMeta(id="legacy", name="Legacy", version="1.0.0")
64+
def on_pre_compile(self, ctx):
65+
pass
66+
67+
68+
def bcasl_register(manager: BCASL) -> None:
69+
manager.add_plugin(MyPlugin())
70+
```
71+
72+
**PluginMeta And Compatibility**
73+
Important fields.
74+
- `id`: unique and stable id (used in `bcasl.yml`).
75+
- `name`, `version`, `description`, `author`.
76+
- `tags`: used for default ordering when no explicit order is provided.
77+
- `required_*_version`: compatibility requirements (BCASL, Core, SDK, Context).
78+
79+
Validation.
80+
- `bcasl/validator.py` provides compatibility utilities.
81+
82+
**Ordering And Dependencies**
83+
- `priority`: lower runs earlier.
84+
- `requires`: list of required plugin IDs.
85+
- `tags`: used for default ordering if `plugin_order` is absent.
86+
- If a dependency cycle is found, BCASL falls back to a safe ordering.
87+
88+
**Configuration (bcasl.yml)**
5489
BCASL reads `bcasl.yml` or `.bcasl.yml` in the workspace root. Only the `.yml` extension is supported.
90+
91+
Example.
5592
```yaml
5693
required_files:
5794
- main.py
@@ -66,6 +103,11 @@ options:
66103
plugin_timeout_s: 5
67104
plugin_parallelism: 0
68105
iter_files_cache: true
106+
plugin_limits:
107+
mem_mb: 0
108+
cpu_time_s: 0
109+
nofile: 0
110+
fsize_mb: 0
69111
plugins:
70112
example.clean:
71113
enabled: true
@@ -74,9 +116,61 @@ plugin_order:
74116
- example.clean
75117
```
76118
77-
**Notes**
78-
- Plugin ids come from `PluginMeta.id`, and configuration uses those ids.
79-
- Plugins can also register via a legacy `bcasl_register(manager)` function, but `@bc_register` is preferred.
80-
- Use `Plugins_SDK.GeneralContext.Dialog` for UI interactions and logging. Direct Qt dialogs are blocked in sandboxed runs.
81-
- Timeouts can be controlled with `options.plugin_timeout_s` or the env var `PYCOMPILER_BCASL_PLUGIN_TIMEOUT`.
82-
- Parallelism can be controlled with `options.plugin_parallelism` or the env var `PYCOMPILER_BCASL_PARALLELISM`.
119+
Important notes.
120+
- Keys in `plugins` are the `PluginMeta.id` values.
121+
- `plugin_order` forces ordering and adjusts priority.
122+
- If `bcasl.yml` is missing, a default file is generated.
123+
124+
**Execution Context (PreCompileContext)**
125+
Key methods.
126+
- `get_workspace_root()` and `get_workspace_name()`.
127+
- `get_workspace_config()` and `get_workspace_metadata()`.
128+
- `get_file_patterns()` and `get_exclude_patterns()`.
129+
- `get_required_files()` and `has_required_file(name)`.
130+
- `iter_files(include, exclude)` with optional cache.
131+
- `is_workspace_valid()` checks presence of `bcasl.yml`.
132+
133+
Pattern usage example.
134+
```python
135+
for path in ctx.iter_files(ctx.get_file_patterns(), ctx.get_exclude_patterns()):
136+
...
137+
```
138+
139+
**Workspace Switch (Allowed)**
140+
A plugin can request a workspace change via the SDK.
141+
142+
```python
143+
from Plugins_SDK.BcPluginContext import set_selected_workspace
144+
145+
ok = set_selected_workspace("/path/to/new/workspace")
146+
```
147+
148+
Behavior.
149+
- The request is accepted by contract (returns True).
150+
- The target directory is created if needed (best‑effort).
151+
- If the UI is present, the Core applies the change and may stop ongoing builds.
152+
- After requesting a switch, avoid using the old `ctx` for sensitive actions.
153+
154+
**UI And Logs**
155+
- Use `Plugins_SDK.GeneralContext.Dialog` for messages and progress.
156+
- Dialogs are routed through the UI thread and inherit the theme.
157+
- Direct Qt dialogs (like `QProgressDialog`) are blocked in sandboxed runs.
158+
159+
**Sandbox, Timeout, Parallelism**
160+
- If `options.sandbox` is `true`, plugins can run in isolated processes.
161+
- Timeout via `options.plugin_timeout_s` or `PYCOMPILER_BCASL_PLUGIN_TIMEOUT`.
162+
- Parallelism via `options.plugin_parallelism` or `PYCOMPILER_BCASL_PARALLELISM`.
163+
- Resource limits via `options.plugin_limits` (mem, cpu, files, size).
164+
165+
**Plugins_SDK Utilities**
166+
The SDK provides many helpers.
167+
- Project and Python file analysis.
168+
- Dependency and venv inspection.
169+
- Git, Docker, CI, tests, metrics, security utilities.
170+
- Template generation with `Generate_Bc_Plugin_Template()`.
171+
172+
**Best Practices**
173+
- Keep plugins idempotent and error‑tolerant.
174+
- Use `ctx.iter_files` so you respect `exclude_patterns`.
175+
- Avoid relying on global state if sandbox is enabled.
176+
- Minimize external dependencies (stdlib preferred).

docs/how_to_create_an_engine.md

Lines changed: 85 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
**Overview**
2-
PyCompiler_ARK engines are Python packages loaded from `ENGINES/` at startup. Each engine registers itself with `@engine_register` and provides a `CompilerEngine` subclass that builds the command used to compile a file.
2+
A PyCompiler_ARK engine is a Python package placed in `ENGINES/` and auto‑loaded at startup. It registers itself with `@engine_register` and provides a `CompilerEngine` that builds the compile command and, optionally, a dedicated UI tab.
33

4-
**Steps**
5-
1. Create a package folder in `ENGINES/<engine_id>/` with an `__init__.py`.
6-
2. Implement a subclass of `CompilerEngine` in that `__init__.py`.
7-
3. Decorate the class with `@engine_register` and set the required attributes.
8-
4. Restart the app so the loader auto-discovers the package.
4+
**Discovery And Loading**
5+
- Engines are discovered only in `ENGINES/<engine_id>/`.
6+
- The folder must contain an `__init__.py`.
7+
- At startup, `EngineLoader` scans `ENGINES/` and imports each package.
8+
- Auto discovery can be disabled with `ARK_ENGINES_AUTO_DISCOVER=0`.
9+
10+
**Package Layout**
11+
- `ENGINES/<engine_id>/__init__.py`: engine code, registration, UI.
12+
- `ENGINES/<engine_id>/languages/<code>.json`: optional translations.
13+
- `ENGINES/<engine_id>/mapping.json`: optional mapping for the auto‑builder.
14+
- Optional internal modules, assets, helpers.
915

1016
**Minimal Example**
1117
```python
@@ -31,22 +37,76 @@ class MyEngine(CompilerEngine):
3137
return [sys.executable, "-m", "mytool", file]
3238
```
3339

34-
**Required API**
35-
- `id`: Unique engine id (string).
36-
- `name`: Display name.
37-
- `version`: Engine version.
38-
- `required_core_version` and `required_sdk_version`: Compatibility info.
39-
- `build_command(self, gui, file) -> list[str]`: Full command list with the program at index 0.
40-
41-
**Optional API**
42-
- `preflight(self, gui, file) -> bool`: Return False to abort.
43-
- `program_and_args(self, gui, file)`: Override if you need custom program/args separation.
44-
- `environment(self) -> dict[str, str] | None`: Inject environment variables.
45-
- `on_success(self, gui, file)`: Post-build hook.
46-
- `required_tools` property: Python and system tools that should be installed.
47-
- `create_tab(self, gui)`: Provide a custom settings tab for your engine.
48-
- `apply_i18n(self, gui, tr)`: Update text when language changes. Provide `languages/<code>.json` in your engine package if you want translations.
49-
50-
**Notes**
51-
- The loader only imports packages under `ENGINES/` that contain `__init__.py`. Plain `.py` files are ignored.
52-
- Keep engines stateless. GUI state is passed in `gui`.
40+
**Lifecycle**
41+
1. Package import from `ENGINES/<engine_id>`.
42+
2. `@engine_register` adds the class to the registry.
43+
3. The GUI calls `create_tab` if present to create a tab.
44+
4. When compile is triggered, the engine provides the command via `build_command`.
45+
5. The process runs the command and calls `on_success` on success.
46+
47+
**Full API**
48+
Required attributes.
49+
- `id`: stable unique id (used by UI and config).
50+
- `name`: display label.
51+
- `version`: engine version.
52+
- `required_core_version`: minimal Core version.
53+
- `required_sdk_version`: minimal SDK version.
54+
55+
Core methods.
56+
- `build_command(self, gui, file) -> list[str]`: full command, index 0 is the program.
57+
- `program_and_args(self, gui, file) -> (program, args) | None`: override if needed.
58+
- `preflight(self, gui, file) -> bool`: checks before compile, return False to abort.
59+
- `environment(self) -> dict[str, str] | None`: env vars to inject.
60+
- `on_success(self, gui, file) -> None`: post‑build hook.
61+
62+
UI and i18n.
63+
- `create_tab(self, gui) -> (QWidget, label) | None`: adds a tab.
64+
- `apply_i18n(self, gui, tr)`: update text on language change.
65+
66+
Tools and dependencies.
67+
- `required_tools`: dict `{ "python": [...], "system": [...] }`.
68+
- `ensure_tools_installed(self, gui)`: installs missing tools when possible.
69+
70+
**Tools And Dependencies**
71+
- Python tools install through the project venv when available.
72+
- System tools use `SysDependencyManager` (GUI supported) if available.
73+
- Keep the list minimal to avoid unnecessary installs.
74+
75+
**UI Tab**
76+
- In `create_tab`, create widgets and store them on `self` (ex: `self._opt_onefile`).
77+
- Avoid heavy work in `__init__` to keep loading fast.
78+
- Wire signals locally and use `gui.log.append(...)` for logs.
79+
80+
**I18n**
81+
- Add `languages/en.json`, `languages/fr.json`, etc. in your engine package.
82+
- Use `resolve_language_code` and `load_engine_language_file` (SDK) to load translations.
83+
- See `ENGINES/pyinstaller`, `ENGINES/nuitka`, `ENGINES/cx_freeze` for patterns.
84+
85+
**Auto Command Builder (Optional)**
86+
The auto‑builder can read a `mapping.json` to generate engine options from detected imports.
87+
88+
Minimal example.
89+
```json
90+
{
91+
"numpy": {
92+
"pyinstaller": ["--hidden-import", "{import_name}"],
93+
"nuitka": "--include-package={import_name}"
94+
},
95+
"__aliases__": {
96+
"import_to_package": {"cv2": "opencv-python"},
97+
"package_to_import_name": {"opencv-python": "cv2"}
98+
}
99+
}
100+
```
101+
102+
Key points.
103+
- Top‑level keys are package names.
104+
- Engine values accept `str`, `list[str]`, or `dict` with `args` or `flags`.
105+
- `"{import_name}"` is replaced by the actual import name.
106+
- For advanced logic, expose `AUTO_BUILDER` in `ENGINES/<engine_id>/auto_plugins.py`.
107+
108+
**Best Practices**
109+
- Keep engines stateless and drive behavior from `gui` and the target file.
110+
- Validate paths, handle exceptions, and log clearly.
111+
- Provide safe defaults when widgets are missing.
112+
- Use `CompilerCore.dry_run` when helpful.

0 commit comments

Comments
 (0)