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
30 changes: 0 additions & 30 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<!--
Thank you for contributing to libe3. Please fill in every section.
PRs that leave the mandatory boxes unchecked will not be reviewed.
-->

## Summary
Expand All @@ -26,39 +25,10 @@ PRs that leave the mandatory boxes unchecked will not be reviewed.

Closes #

## Mandatory test checklist

These mirror what CI (`.github/workflows/pr-tests.yml`) enforces. **All boxes must be ticked before review.**

- [ ] `./build_libe3 -c -d build -j $(nproc) -r -t` passes (Release build + tests)
- [ ] `./build_libe3 -c -d build -j $(nproc) -g -t` passes (Debug build + tests)
- [ ] `cd build && ctest --output-on-failure` is clean
- [ ] MPMC queue benchmark (`./build/test_bench_mpmc_queue`) shows no regression vs `main`
- [ ] `VERSION` bumped per [SemVer](https://semver.org/) if the public API or ABI changed
- [ ] If public headers under `include/` were touched, `./build_libe3 --docs` renders without new Doxygen warnings
- [ ] If new build dependencies were added, they are installed by `./build_libe3 -I` (update the script if needed)
- [ ] If the `libe3.pc` interface changed, downstream consumers (`dApp-openairinterface5g`) still link cleanly

## CI checklist

CI posts a single `CI report` comment on this PR once every workflow has finished; it carries the
verdict, a per-workflow table and the benchmark/E2E detail. Confirm against that comment:

- [ ] The report's verdict is green for the head commit
- [ ] `Unit Tests` is green (Debug + Release matrix on `ubuntu-latest`)
- [ ] `Commit policy` is green (trailers + linear history + each commit builds/tests independently)
- [ ] `MPMC Queue Benchmark` shows no regression (only runs when `include/libe3/mpmc_queue.hpp` changes)

## Twin-repo coordination

libe3 is paired with [`dapps`](https://github.com/wineslab/dApp-library) and [`dApp-openairinterface5g`](https://github.com/wineslab/dApp-openairinterface5g). **We do not accept patches that break or reduce compatibility with the twin repositories.**

- [ ] This PR does not change the E3 wire protocol or public ABI, OR a paired PR exists in each affected twin repo (link below).

Paired PR(s):

## Workflow confirmation

- [ ] My branch is a linear, fast-forward-able descendant of `main` (rebased if `main` moved), with no merge commits. (See `CONTRIBUTING.md` § Pull Request Process.)
- [ ] Every commit builds and passes tests on its own (atomic, `git bisect`-safe) with a descriptive message.
- [ ] I have read and followed `CONTRIBUTING.md`.
13 changes: 13 additions & 0 deletions .github/workflows/pr-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,19 @@ jobs:
cmake --build build-consume-pkgconfig -j $(nproc)
./build-consume-pkgconfig/consume

# The way users get the binding: the sdist that the release publishes,
# built by pip against the installed library.
- name: pip-install the libe3py sdist against the installed library
if: needs.changes.outputs.code == 'true'
run: |
set -euo pipefail
python3 -m venv "$RUNNER_TEMP/venv"
"$RUNNER_TEMP/venv/bin/pip" install build
"$RUNNER_TEMP/venv/bin/python" -m build --sdist -o "$RUNNER_TEMP/dist" .
"$RUNNER_TEMP/venv/bin/pip" install "$RUNNER_TEMP"/dist/libe3py-*.tar.gz \
--config-settings=cmake.define.CMAKE_PREFIX_PATH="$PWD/stage"
"$RUNNER_TEMP/venv/bin/python" tests/test_swig_smoke.py

# NOTE: the MPMC Queue Benchmark moved to .github/workflows/mpmc-bench.yml,
# where it is gated on changes to include/libe3/mpmc_queue.hpp so it only
# runs when the queue's core file changes. It isn't in the required-checks
Expand Down
76 changes: 75 additions & 1 deletion .github/workflows/tag-and-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -253,12 +253,80 @@ jobs:
name: libe3-docs
path: libe3-docs-${{ needs.create-tag.outputs.version }}.zip

build-python-sdist:
name: Build the libe3py source distribution
needs: create-tag
runs-on: ubuntu-latest
if: ${{ needs.create-tag.outputs.tag_created == 'true' && github.repository == 'wineslab/libe3' }}
timeout-minutes: 10
steps:
- uses: actions/checkout@v5
with:
persist-credentials: false
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Build the sdist
# sdist only: the module is compiled on the user's machine against the
# libe3 installed there (python/CMakeLists.txt), and a plain
# linux_x86_64 wheel would be rejected by PyPI anyway. Building the
# sdist compiles nothing.
run: |
python3 -m pip install build
python3 -m build --sdist
ls dist/libe3py-${{ needs.create-tag.outputs.version }}.tar.gz
- uses: actions/upload-artifact@v6
with:
name: libe3py-sdist
path: dist/

publish-to-pypi:
name: Publish libe3py to PyPI
needs:
- build-python-sdist
- create-tag
runs-on: ubuntu-latest
if: ${{ needs.create-tag.outputs.tag_created == 'true' && github.repository == 'wineslab/libe3' }}
environment:
name: pypi
url: https://pypi.org/p/libe3py
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v7
with:
name: libe3py-sdist
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1

publish-to-testpypi:
name: Publish libe3py to TestPyPI
needs:
- publish-to-pypi
- create-tag
runs-on: ubuntu-latest
if: ${{ needs.create-tag.outputs.tag_created == 'true' && github.repository == 'wineslab/libe3' }}
environment:
name: testpypi
url: https://test.pypi.org/p/libe3py
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v7
with:
name: libe3py-sdist
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/

github-release:
name: Upload Debian package to GitHub Release
name: Upload Debian packages and the libe3py sdist to GitHub Release
needs:
- build-deb-amd64
- build-deb-arm64
- build-docs
- build-python-sdist
- create-tag
runs-on: ubuntu-latest
if: ${{ needs.create-tag.outputs.tag_created == 'true' }}
Expand All @@ -284,6 +352,12 @@ jobs:
name: libe3-docs
path: dist/

- name: Download the libe3py sdist
uses: actions/download-artifact@v7
with:
name: libe3py-sdist
path: dist/

- name: Create GitHub Release and upload assets
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
5 changes: 4 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,16 @@ include(libe3Targets)
include(libe3Tests)
include(libe3Examples)
include(libe3Docs)
include(libe3SWIG)

# ============================================================================
# Installation
# ============================================================================
include(libe3Install)

# After libe3Install on purpose: install rules run in the order they are defined, so a failure
# to write the Python module must not stop the library and libe3.pc from being installed.
include(libe3SWIG)

# ============================================================================
# Summary
# ============================================================================
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ The following are **mandatory** for every contribution. PRs that do not meet the

### Pull requests

- All PRs must use `.github/PULL_REQUEST_TEMPLATE.md` and complete every checklist item.
- All PRs must use `.github/PULL_REQUEST_TEMPLATE.md` and fill in every section; CI (below) is what checks the build, the tests and the commit policy.
- The following CI workflows must be green on the latest commit before review:
- **`Unit Tests`** (`.github/workflows/pr-tests.yml`) — builds and runs `ctest --output-on-failure` for both `Debug` and `Release` on `ubuntu-latest`, plus the integration, all-encodings, and SWIG jobs.
- **`Commit policy`** (`.github/workflows/commit-trailers.yml`) — validates the AI-assistant trailer policy on every commit (see [AI assistants](#ai-assistants)), that the branch is a linear, fast-forward-able descendant of `main` (no merge commits), that every commit builds and passes tests on its own, and that every file carries an SPDX header (`reuse lint`, see [Licensing headers](#licensing-headers)).
Expand Down
33 changes: 27 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,28 @@ cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)
```

### Installing libe3

This is the recipe the OAI E3 agent and the dApp library point to. It installs libe3 like any other
library, once and with `sudo`, into `/usr/local`; after that nothing that builds against it needs `sudo`
or extra environment variables. Every wire encoding is built, since a gNB has to serve whichever one its
dApp uses:

```bash
git clone --branch <version> https://github.com/wineslab/libe3 && cd libe3
./build_libe3 -I # build dependencies (sudo), once
./build_libe3 --all-encodings --install # -> /usr/local, asks for sudo, refreshes the linker cache
pkg-config --modversion libe3
```

- C and C++ projects (the OAI gNB) find it through `pkg-config` or `find_package(libe3)`, with no setup.
- The Python binding is a separate package, `libe3py`, installed with pip into any virtual environment,
without root: `pip install libe3py==<version>` (or `pip install .` from this checkout). It is built
against the installed libe3 and must be the same version; the dApp library depends on it, so
`pip install dapps` brings it in.
- To install somewhere else, `--prefix DIR` (no `sudo` if `DIR` is yours); then `PKG_CONFIG_PATH` has to
include `DIR/lib/pkgconfig`, and pip needs `--config-settings=cmake.define.CMAKE_PREFIX_PATH=DIR`.

### Build Options

| Option | Default | Description |
Expand Down Expand Up @@ -492,10 +514,9 @@ libe3 ships an optional SWIG-generated Python binding so the same C++ library ca
# Install SWIG and Python development headers (Ubuntu)
sudo apt-get install -y swig python3-dev

# Build + install into the active interpreter's site-packages (activate your venv first)
./build_libe3 --install --enable-swig \
--cmake-opt "-DLIBE3_ENABLE_ASN1=ON -DLIBE3_ENABLE_JSON=ON"
python3 -c "import libe3py; print('libe3py OK')"
# Install libe3 first (see "Installing libe3"), then the binding, in a virtual environment
pip install libe3py # or, from this checkout: pip install .
python3 -c "import libe3py; print('libe3py OK', libe3py.__version__)"

# Or, build only (no install) and run the smoke test (CTest label "swig")
cmake -S . -B build -DLIBE3_ENABLE_SWIG=ON
Expand All @@ -510,8 +531,8 @@ the full **`DAppSession`** dApp seam — a complete lifecycle (`start` /
timeout_ms)`, designed for the sub-millisecond / high-throughput E3AP path
(GIL released during blocking calls). SM payloads cross as native `bytes`;
per-SM encoding stays in Python so existing Python SM implementations work
unchanged. `build_libe3 --install --enable-swig` installs `_libe3py.so` +
`libe3py.py` into `Python3_SITEARCH`.
unchanged. The binding is built by pip against the installed libe3 (`python/CMakeLists.txt`), links it
statically, and refuses an installed libe3 of another version.

See **[`swig/README.md`](swig/README.md)** for the architecture, rationale, and a
full Python usage example, and [`swig/e3_dapp_session.hpp`](swig/e3_dapp_session.hpp)
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.2
0.2.3
71 changes: 56 additions & 15 deletions build_libe3
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,19 @@ Features:
--enable-json Enable JSON encoding support (default: ON)
--disable-json Disable JSON encoding
--enable-protobuf Enable Protocol Buffers encoding support (default: OFF)
--all-encodings Enable ASN.1, JSON and Protocol Buffers (what a gNB that has to serve
any dApp needs; same as the three --enable-* options)
--disable-protobuf Disable Protocol Buffers encoding
--enable-sctp Enable the SCTP POSIX transport (default: OFF).
Needs the SCTP headers: libsctp-dev (Debian/Ubuntu),
lksctp-tools-devel (Fedora/RHEL), lksctp-tools (Arch).
--install-deps does NOT install them; SCTP is optional
and the deployed E3 links are IPC or TCP.
--disable-sctp Disable the SCTP transport
--enable-swig Build + install the Python (SWIG) bindings, libe3py
(default: OFF). With --install, installs libe3py into
the active interpreter's site-packages.
--enable-swig Build the Python (SWIG) binding, libe3py, in build/swig to develop and
test it (default: OFF). It is not installed with the library: install
it with pip (pip install libe3py, or pip install . in this checkout)
after installing libe3.
--disable-swig Disable the Python (SWIG) bindings
--enable-tests Build unit tests (default: ON)
--disable-tests Disable unit tests
Expand All @@ -139,9 +142,11 @@ Sanitizers:
Installation:
-------------
-I, --install-deps Install required dependencies (requires sudo)
--install Install library after building
--install Install library after building (uses sudo only if the prefix is not
writable by you, then refreshes the linker cache with ldconfig)
--uninstall Uninstall library using install manifest
--prefix DIR Installation prefix (default: /usr/local)
--prefix DIR Installation prefix (default: /usr/local). A prefix in your home, such as
~/.local/libe3, needs no sudo; put its lib/pkgconfig on PKG_CONFIG_PATH.

Testing:
--------
Expand Down Expand Up @@ -488,11 +493,41 @@ run_tests() {
install_library() {
local build_path="$SCRIPT_DIR/$BUILD_DIR"
cd "$build_path"

echo_info "Installing libe3..."
sudo cmake --install .


local prefix
prefix="$(sed -n 's/^CMAKE_INSTALL_PREFIX:PATH=//p' CMakeCache.txt | head -n1)"
prefix="${prefix:-/usr/local}"

# sudo only when the prefix (or the closest existing parent) is not writable by this user
local probe="$prefix"
while [ ! -e "$probe" ] && [ "$probe" != "/" ]; do probe="$(dirname "$probe")"; done
local runner=()
if [ ! -w "$probe" ]; then
runner=(sudo)
fi

echo_info "Installing libe3 into $prefix${runner:+ (with sudo)}..."
"${runner[@]}" cmake --install .

# A library in a directory the dynamic linker searches (/usr/local/lib) is only found at run
# time after the linker cache is refreshed.
if [ "$(uname -s)" = "Linux" ] && command -v ldconfig >/dev/null 2>&1 \
&& { [ "$prefix" = "/usr" ] || [ "$prefix" = "/usr/local" ]; }; then
"${runner[@]}" ldconfig || echo_warning "ldconfig failed; run it once by hand (sudo ldconfig)"
fi

echo_success "Installation completed!"
case "$prefix" in
/usr|/usr/local) ;;
*)
echo_info "libe3 is not in a system path. Before building something against it, run:"
echo " export PKG_CONFIG_PATH=$prefix/lib/pkgconfig:\$PKG_CONFIG_PATH"
;;
esac
if [ "$ENABLE_SWIG" -eq 1 ]; then
echo_info "The Python binding is not installed with the library. In your virtual environment run:"
echo " pip install libe3py==$(cat "$SCRIPT_DIR/VERSION") # or: pip install $SCRIPT_DIR"
fi
}

uninstall_library() {
Expand Down Expand Up @@ -676,6 +711,12 @@ main() {
ENABLE_PROTOBUF=1
shift
;;
--all-encodings)
ENABLE_ASN1=1
ENABLE_JSON=1
ENABLE_PROTOBUF=1
shift
;;
--enable-sctp)
ENABLE_SCTP=1
shift
Expand Down Expand Up @@ -823,12 +864,6 @@ main() {
esac
fi

# Check for required tools
if ! check_command cmake; then
echo_error "CMake is required but not found. Install with: --install-deps"
exit 1
fi

# Install dependencies if requested
if [ $INSTALL_DEPS -eq 1 ]; then
install_dependencies
Expand All @@ -838,6 +873,12 @@ main() {
echo_info "Dependencies installed. Please try to build libe3."
exit 0
fi

# Checked after --install-deps, which is what installs it on a fresh machine
if ! check_command cmake; then
echo_error "CMake is required but not found. Install with: --install-deps"
exit 1
fi

# Clean if requested
if [ $CLEAN_ALL -eq 1 ]; then
Expand Down
Loading
Loading