Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/actions/setup-bsk/action.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
name: Set up Basilisk
description: >
Clones the Basilisk source tree and installs the BSK Python package set,
beta/alpha => develop branch clone + nightly wheel index
beta/alpha/aN/bN => develop branch clone + nightly wheel index
rc => release tag clone + TestPyPI
release => release tag clone + PyPI

inputs:
version:
description: >
BSK version string (e.g. 2.11.0beta, 2.10.2rc1, 2.11.0).
BSK version string (e.g. 2.12.0b0, 2.10.2rc1, 2.11.0).
Drives both the git ref to clone and the pip index to install from.
required: true
clone-source:
Expand All @@ -26,7 +26,7 @@ runs:
shell: bash
run: |
BSK="${{ inputs.version }}"
if [[ "$BSK" =~ beta|alpha ]]; then
if [[ "$BSK" =~ beta|alpha|[0-9]+(a|b)[0-9]+ ]]; then
REF=develop
else
REF="v${BSK}"
Expand All @@ -39,7 +39,7 @@ runs:
shell: bash
run: |
BSK="${{ inputs.version }}"
if [[ "$BSK" =~ beta|alpha ]]; then
if [[ "$BSK" =~ beta|alpha|[0-9]+(a|b)[0-9]+ ]]; then
pip install --pre \
--index-url https://avslab.github.io/basilisk/nightly/ \
--extra-index-url https://pypi.org/simple/ \
Expand Down
18 changes: 9 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ jobs:
run: pytest tests/test_smoke.py -v

examples:
name: Build example plugin (${{ matrix.os }}, ${{ matrix.python_label }})
name: Build example extension (${{ matrix.os }}, ${{ matrix.python_label }})
needs: build
runs-on: ${{ matrix.os }}
strategy:
Expand Down Expand Up @@ -179,23 +179,23 @@ jobs:
shell: bash
run: pip show bsk swig | grep -E "^(Name|Version):" && python -c "import bsk_sdk; print('bsk-sdk:', bsk_sdk.__version__, '| synced from BSK:', bsk_sdk.bsk_version())"

- name: Build example plugin wheel
- name: Build example extension wheel
# --no-isolation so the build inherits the locally installed bsk-sdk wheel,
# guaranteeing CI always tests the current branch rather than a stale PyPI version.
shell: bash
run: |
pip install build scikit-build-core
python -m build --wheel --no-isolation -o plugin-dist examples/custom-atm-plugin
python -m build --wheel --no-isolation -o extension-dist examples/custom-atm-extension

- uses: actions/upload-artifact@v6
with:
name: plugin-wheel-${{ matrix.os }}-py${{ matrix.python }}
path: plugin-dist/*.whl
name: extension-wheel-${{ matrix.os }}-py${{ matrix.python }}
path: extension-dist/*.whl
retention-days: 7

- name: Install plugin wheel and test dependencies
- name: Install extension wheel and test dependencies
shell: bash
run: pip install plugin-dist/*.whl pytest
run: pip install extension-dist/*.whl pytest

- name: Run example plugin tests
run: pytest examples/custom-atm-plugin/customExponentialAtmosphere/_UnitTest/test_customExponentialAtmosphere.py -v
- name: Run example extension tests
run: pytest examples/custom-atm-extension/customExponentialAtmosphere/_UnitTest/test_customExponentialAtmosphere.py -v
8 changes: 4 additions & 4 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ jobs:
pip install pytest
pytest tests/test_smoke.py -v

- name: Build and test example plugin
- name: Build and test example extension
run: |
pip install scikit-build-core
python -m build --wheel --no-isolation -o plugin-dist examples/custom-atm-plugin
pip install plugin-dist/*.whl
pytest examples/custom-atm-plugin/customExponentialAtmosphere/_UnitTest/test_customExponentialAtmosphere.py -v
python -m build --wheel --no-isolation -o extension-dist examples/custom-atm-extension
pip install extension-dist/*.whl
pytest examples/custom-atm-extension/customExponentialAtmosphere/_UnitTest/test_customExponentialAtmosphere.py -v

- name: Notify on failure
if: failure()
Expand Down
47 changes: 28 additions & 19 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ entire `bsk-sdk` repo.
## Repository Purpose

`bsk-sdk` is a Python wheel that lets external projects build
Basilisk-compatible SWIG plugins without vendoring the full Basilisk source
Basilisk-compatible SWIG extensions without vendoring the full Basilisk source
tree. The public contract is the installed wheel layout plus the CMake helpers
that downstream plugin authors call:
that downstream extension authors call:

- Python package: `src/bsk_sdk`
- CMake package: `cmake/bsk-sdkConfig.cmake.in`
- Plugin helper: `cmake/bsk_add_swig_module.cmake`
- Extension helper: `cmake/bsk_add_swig_module.cmake`
- Message helper: `cmake/bsk_generate_messages.cmake`
- Consumer example: `examples/custom-atm-plugin`
- Consumer example: `examples/custom-atm-extension`
- Basilisk source input: `external/basilisk` submodule
- Sync tooling: `tools/sync_all.py` and sibling `tools/sync_*.py` scripts

Prefer changes that preserve the downstream plugin experience:
Prefer changes that preserve the downstream extension experience:

```cmake
find_package(bsk-sdk CONFIG REQUIRED)
Expand All @@ -38,11 +38,11 @@ bsk_generate_messages(...)
- Keep PRs draft until the relevant build/test commands below have been run or
the skipped checks are clearly documented.
- PR descriptions should call out:
- The affected surface: packaging, CMake/SWIG, sync tooling, example plugin,
- The affected surface: packaging, CMake/SWIG, sync tooling, example extension,
CI, or docs.
- The Basilisk version or submodule commit, if it changed.
- The local verification commands and any skipped checks.
- Any downstream compatibility impact for plugin authors.
- Any downstream compatibility impact for extension authors.

## Generated And Vendored Boundaries

Expand All @@ -61,10 +61,19 @@ directories are ignored by git and should normally be recreated locally:
Use the sync scripts rather than hand-editing generated Basilisk copies:

```bash
# Only needed once in a fresh clone:
git submodule update --init --recursive

python3 tools/sync_all.py
```

If you already have a local Basilisk checkout, point the sync scripts at it
instead of moving the submodule:

```bash
python3 tools/sync_all.py --basilisk-root ~/Repos/basilisk
```

For an intentional Basilisk update:

```bash
Expand Down Expand Up @@ -94,7 +103,7 @@ python -m pip install dist/*.whl pytest --force-reinstall
pytest tests/test_smoke.py -v
```

Downstream plugin path:
Downstream extension path:

```bash
python -m pip install dist/*.whl --force-reinstall
Expand All @@ -103,9 +112,9 @@ subprocess.run([sys.executable, '-m', 'pip', 'install', \
f'bsk[all]=={bsk_sdk.bsk_version()}'], check=True)"
python -c "import Basilisk; print(Basilisk.__file__)"
python -m pip install build scikit-build-core
python -m build --wheel --no-isolation -o plugin-dist examples/custom-atm-plugin
python -m pip install plugin-dist/*.whl pytest --force-reinstall
pytest examples/custom-atm-plugin/customExponentialAtmosphere/_UnitTest/test_customExponentialAtmosphere.py -v
python -m build --wheel --no-isolation -o extension-dist examples/custom-atm-extension
python -m pip install extension-dist/*.whl pytest --force-reinstall
pytest examples/custom-atm-extension/customExponentialAtmosphere/_UnitTest/test_customExponentialAtmosphere.py -v
```

Sync path:
Expand All @@ -118,12 +127,12 @@ Notes:

- A local wheel build may need network access if CMake must fetch Eigen3 and it
is not already available.
- The example plugin build intentionally uses `--no-isolation` so it tests the
- The example extension build intentionally uses `--no-isolation` so it tests the
just-built SDK wheel instead of accidentally pulling a stale PyPI package.
- Install the exact `bsk[all]` version reported by `bsk_sdk.bsk_version()` and
confirm that `Basilisk` imports before running the example plugin test. The
confirm that `Basilisk` imports before running the example extension test. The
test uses `pytest.importorskip`, so a missing BSK installation can otherwise
produce a successful pytest exit without exercising the plugin.
produce a successful pytest exit without exercising the extension.
- CI runs Linux, macOS, and Windows against the minimum and maximum supported
Python versions. Review local-only changes with that matrix in mind.

Expand All @@ -140,7 +149,7 @@ Notes:
- Keep `pyproject.toml`, `CMakeLists.txt`, installed package paths, and smoke
tests in sync. If a file must ship in the wheel or sdist, update the install
rules and `tool.scikit-build.sdist.include`/`exclude` as needed.
- Avoid adding plugin-author requirements that contradict the core promise:
- Avoid adding extension-author requirements that contradict the core promise:
SDK sources, runtime-minimal sources, and built-in C message interface sources
should be handled by `bsk_add_swig_module`, not manually wired by consumers.

Expand Down Expand Up @@ -171,8 +180,8 @@ contract and the consumer build, not just whether the repository build passes.
mismatches should warn when that is the intended compatibility policy.
- Adding or moving installed files requires three-way review: CMake install
rules, Python path helper APIs, and `tests/test_smoke.py` assertions.
- Example plugin failures are usually the most realistic signal for downstream
users. Treat `examples/custom-atm-plugin` as a compatibility test, not just
- Example extension failures are usually the most realistic signal for downstream
users. Treat `examples/custom-atm-extension` as a compatibility test, not just
sample code.

## Review Checklist
Expand All @@ -181,11 +190,11 @@ For every PR, answer these before approving:

- Does the wheel still expose the expected package root, include directories,
CMake config files, SWIG directory, tools directory, and SDK source dirs?
- If the CMake helpers changed, does the example plugin still build and import
- If the CMake helpers changed, does the example extension still build and import
on a clean install?
- If sync tooling changed, does `python3 tools/sync_all.py` still stamp the BSK
version and recreate all required artifact directories/files?
- If dependencies changed, are `pyproject.toml`, CI, example plugin metadata,
- If dependencies changed, are `pyproject.toml`, CI, example extension metadata,
and CMake diagnostics consistent?
- If `external/basilisk` changed, is the submodule pointer intentional and is
the corresponding version impact documented?
Expand Down
43 changes: 32 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Basilisk SDK (`bsk-sdk`)

A self-contained Python wheel that gives external projects everything they need
to build [Basilisk](https://github.com/AVSLab/basilisk) compatible SWIG plugins
to build [Basilisk](https://github.com/AVSLab/basilisk) compatible SWIG extensions
without vendoring the full simulation codebase.

## Quick start
Expand All @@ -10,21 +10,21 @@ without vendoring the full simulation codebase.
pip install bsk-sdk
```

Then in your plugin's `CMakeLists.txt`:
Then in your extension's `CMakeLists.txt`:

```cmake
find_package(bsk-sdk CONFIG REQUIRED)

bsk_add_swig_module(
TARGET myPlugin
INTERFACE swig/myPlugin.i
SOURCES myPlugin.cpp
TARGET myExtension
INTERFACE swig/myExtension.i
SOURCES myExtension.cpp
)
```

`bsk_add_swig_module` automatically compiles the vendored Basilisk SDK sources
(arch_min, arch_utilities, runtime_min, and built-in C message interfaces)
directly into your plugin, so no separate link targets are needed. Basilisk
directly into your extension, so no separate link targets are needed. Basilisk
utility headers are available at their standard paths, for example:

```cpp
Expand All @@ -38,23 +38,36 @@ Basilisk modules:
#include "cMsgCInterface/SpicePlanetStateMsg_C.h"
```

If your plugin needs additional link targets, pass them via `LINK_LIBS` and
If your extension needs additional link targets, pass them via `LINK_LIBS` and
they will be appended after the SDK defaults.

See [`examples/custom-atm-plugin/`](examples/custom-atm-plugin/) for a
See [`examples/custom-atm-extension/`](examples/custom-atm-extension/) for a
complete working example.

## Syncing from Basilisk

The SDK vendors a curated subset of Basilisk headers and sources. These are
synced from a pinned Basilisk commit via a Git submodule.
The SDK vendors a curated subset of Basilisk headers and sources. By default,
these are synced from the `external/basilisk` Git submodule. For a fresh clone,
initialize the submodule once before running the default sync:

```bash
# Only needed once in a fresh clone:
git submodule update --init --recursive

python3 tools/sync_all.py
pip install -e .
```

If you already have a local Basilisk checkout, point the sync script at it
directly instead of moving the submodule checkout:

```bash
git -C ~/Repos/basilisk fetch --tags
git -C ~/Repos/basilisk checkout <tag-or-branch>
python3 tools/sync_all.py --basilisk-root ~/Repos/basilisk
pip install -e .
```

Or opt into auto-sync during build:

```bash
Expand All @@ -70,12 +83,20 @@ cd ../..
python3 tools/sync_all.py
```

Or sync from an existing Basilisk checkout without moving the submodule:

```bash
git -C ~/Repos/basilisk fetch --tags
git -C ~/Repos/basilisk checkout <tag-or-commit>
python3 tools/sync_all.py --basilisk-root ~/Repos/basilisk
```

## Versioning

The `bsk-sdk` package version tracks the Basilisk version it was synced from
(e.g. `bsk-sdk==2.9.1` contains headers from Basilisk `v2.9.1`).

At CMake configure time, the SDK checks that the installed Basilisk version
matches and errors out on a mismatch. This prevents silent ABI
incompatibilities where plugins are compiled against headers from one Basilisk
incompatibilities where extensions are compiled against headers from one Basilisk
version but linked against a different runtime.
6 changes: 3 additions & 3 deletions cmake/bsk-sdkConfig.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ set(BSK_SDK_RUNTIME_MIN_DIR "${BSK_SDK_PKG_DIR}/runtime_min" CACHE PATH "
set(BSK_SDK_SWIG_DIR "${BSK_SDK_PKG_DIR}/swig" CACHE PATH "bsk-sdk swig dir")
set(BSK_SDK_TOOLS_DIR "${BSK_SDK_PKG_DIR}/tools" CACHE PATH "bsk-sdk tools dir")

# Provide Eigen3::Eigen from bundled headers so plugins are self-contained.
# Provide Eigen3::Eigen from bundled headers so extensions are self-contained.
if(NOT TARGET Eigen3::Eigen)
add_library(Eigen3::Eigen IMPORTED INTERFACE GLOBAL)
set_target_properties(Eigen3::Eigen PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${BSK_SDK_INCLUDE_DIR}/eigen3"
)
endif()

# Header-only SDK target — SDK sources are compiled directly into each plugin.
# Header-only SDK target — SDK sources are compiled directly into each extension.
if(NOT TARGET bsk::sdk_headers)
add_library(bsk::sdk_headers IMPORTED INTERFACE GLOBAL)
set_target_properties(bsk::sdk_headers PROPERTIES
Expand Down Expand Up @@ -73,7 +73,7 @@ function(_bsk_sdk_check_basilisk_version)
"Basilisk version mismatch!\n"
" bsk-sdk was synced from Basilisk ${BSK_SDK_BSK_VERSION}\n"
" Installed Basilisk is ${_bsk_installed_version}\n\n"
"Plugins compiled against mismatched headers will have ABI incompatibilities.\n"
"Extensions compiled against mismatched headers will have ABI incompatibilities.\n"
"Either install the matching Basilisk version or re-sync the SDK:\n"
" pip install \"bsk[all]==${BSK_SDK_BSK_VERSION}\"\n"
" # or: cd external/basilisk && git checkout v${_bsk_installed_version} && cd ../.. && python3 tools/sync_all.py"
Expand Down
14 changes: 7 additions & 7 deletions cmake/bsk_add_swig_module.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ macro(_bsk_find_build_deps)
endif()
endmacro()

# Collect SDK implementation sources to compile directly into the plugin.
# Collect SDK implementation sources to compile directly into the extension.
# Falls back to the installed Basilisk runtime libs when sources are absent.
function(_bsk_resolve_sdk_sources out_sources out_link_libs)
if(DEFINED BSK_SDK_ARCH_MIN_DIR AND EXISTS "${BSK_SDK_ARCH_MIN_DIR}"
Expand Down Expand Up @@ -266,10 +266,10 @@ print(f'{exe_path};{lib_path}', end='')"
# Validate SWIG runtime version matches Basilisk's.
file(STRINGS "${_pip_swig_lib}/swigrun.swg" _swigrun_lines
REGEX "SWIG_RUNTIME_VERSION")
set(_plugin_rt "")
set(_extension_rt "")
foreach(_line IN LISTS _swigrun_lines)
if(_line MATCHES "#define SWIG_RUNTIME_VERSION \"([0-9]+)\"")
set(_plugin_rt "${CMAKE_MATCH_1}")
set(_extension_rt "${CMAKE_MATCH_1}")
endif()
endforeach()

Expand All @@ -287,14 +287,14 @@ except ImportError:\n\
OUTPUT_STRIP_TRAILING_WHITESPACE
)

if(_plugin_rt AND _bsk_rt AND NOT _plugin_rt STREQUAL _bsk_rt)
if(_extension_rt AND _bsk_rt AND NOT _extension_rt STREQUAL _bsk_rt)
message(FATAL_ERROR
"SWIG runtime version mismatch!\n"
" bsk was compiled with SWIG_RUNTIME_VERSION \"${_bsk_rt}\" "
"(capsule: swig_runtime_data${_bsk_rt})\n"
" pip swig ${_pip_swig_exe} uses SWIG_RUNTIME_VERSION \"${_plugin_rt}\" "
"(capsule: swig_runtime_data${_plugin_rt})\n\n"
"Plugins compiled with this SWIG version cannot exchange objects with "
" pip swig ${_pip_swig_exe} uses SWIG_RUNTIME_VERSION \"${_extension_rt}\" "
"(capsule: swig_runtime_data${_extension_rt})\n\n"
"Extensions compiled with this SWIG version cannot exchange objects with "
"Basilisk across module boundaries.\n"
"Install a SWIG version whose runtime epoch matches bsk (epoch ${_bsk_rt}):\n"
" SWIG runtime epoch 5 -> SWIG 4.4.1 (pip install \"swig==4.4.1\")\n"
Expand Down
Loading
Loading