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
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include build_backend.py
91 changes: 91 additions & 0 deletions build_backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"""PEP 517 adapter for fbuild's setuptools backend.

Pip cannot append arbitrary Cargo arguments to a PEP 517 build. It can,
however, pass backend configuration through ``--config-settings``. Translate
the fbuild-specific profile setting into the environment variable consumed by
``setup.py`` and delegate all other behavior to setuptools.
"""

from __future__ import annotations

import os
from contextlib import contextmanager
from typing import Iterator

from setuptools import build_meta as _setuptools


def _setting(config_settings: dict[str, object] | None, name: str) -> str | None:
if not config_settings or name not in config_settings:
return None
value = config_settings[name]
if isinstance(value, list):
value = value[-1] if value else ""
return str(value).strip().lower()


@contextmanager
def _profile_environment(config_settings: dict[str, object] | None) -> Iterator[None]:
"""Apply the requested fbuild profile only while setuptools builds."""
profile = _setting(config_settings, "fbuild-profile")
release = _setting(config_settings, "fbuild-release")
if profile is not None and profile not in {"dev", "debug", "release"}:
raise ValueError("fbuild-profile must be 'dev'/'debug' or 'release'")
if release is not None and release not in {"0", "1", "false", "true", "no", "yes"}:
raise ValueError("fbuild-release must be a boolean value")

requested_release = (
profile == "release"
if profile is not None
else release in {"1", "true", "yes"}
if release is not None
else None
)
if requested_release is None:
yield
return

previous = os.environ.get("FBUILD_BUILD_RELEASE")
os.environ["FBUILD_BUILD_RELEASE"] = "1" if requested_release else "0"
try:
yield
finally:
if previous is None:
os.environ.pop("FBUILD_BUILD_RELEASE", None)
else:
os.environ["FBUILD_BUILD_RELEASE"] = previous


def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
with _profile_environment(config_settings):
return _setuptools.build_wheel(wheel_directory, config_settings, metadata_directory)


def build_editable(wheel_directory, config_settings=None, metadata_directory=None):
with _profile_environment(config_settings):
return _setuptools.build_editable(wheel_directory, config_settings, metadata_directory)


def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None):
with _profile_environment(config_settings):
return _setuptools.prepare_metadata_for_build_wheel(metadata_directory, config_settings)


def get_requires_for_build_wheel(config_settings=None):
with _profile_environment(config_settings):
return _setuptools.get_requires_for_build_wheel(config_settings)


def build_sdist(sdist_directory, config_settings=None):
with _profile_environment(config_settings):
return _setuptools.build_sdist(sdist_directory, config_settings)


def get_requires_for_build_sdist(config_settings=None):
with _profile_environment(config_settings):
return _setuptools.get_requires_for_build_sdist(config_settings)


def prepare_metadata_for_build_editable(metadata_directory, config_settings=None):
with _profile_environment(config_settings):
return _setuptools.prepare_metadata_for_build_editable(metadata_directory, config_settings)
11 changes: 11 additions & 0 deletions crates/fbuild-build-esp/src/esp32/esp32_linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,16 @@ impl Esp32Linker {
}
}

// Keep section-level dead-code elimination enabled even when the SDK
// supplies a complete `flags/ld_flags` file. The SDK flags replace
// the JSON fallback above, and older SDK packages do not all include
// `--gc-sections`. This is the important size guard for quick/no-LTO
// builds: every function/data section can still be removed when it is
// unreachable from the firmware roots.
if !flags.iter().any(|flag| flag == "-Wl,--gc-sections") {
flags.push("-Wl,--gc-sections".to_string());
}

flags
}

Expand Down Expand Up @@ -590,6 +600,7 @@ mod tests {
assert!(flags.contains(&"-nostartfiles".to_string()));
assert!(flags.contains(&"-u".to_string()));
assert!(flags.contains(&"app_main".to_string()));
assert!(flags.contains(&"-Wl,--gc-sections".to_string()));
// Profile link flags should NOT be present when SDK flags are used
assert!(!flags.contains(&"-flto=auto".to_string()));
}
Expand Down
12 changes: 12 additions & 0 deletions docs/getting-started/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ cd fbuild
pip install -e .
```

Source installs use Rust's fast dev profile by default (no Rust LTO), which
keeps local rebuilds quick. To explicitly build an optimized Rust wheel, pass
the PEP 517 backend setting:

```bash
pip install . --config-settings fbuild-profile=release
```

`pip install . -- --release` is not supported: pip does not forward arbitrary
arguments after `--` to a PEP 517 backend. The `fbuild-profile` setting (or
`FBUILD_BUILD_RELEASE=1`) is the supported release override.

## First Project

Create a project with a PlatformIO-compatible layout:
Expand Down
9 changes: 8 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,14 @@ cache-keys = [

[build-system]
requires = ["setuptools>=64"]
build-backend = "setuptools.build_meta"
build-backend = "build_backend"
backend-path = ["."]

# Local/source installs use Cargo's dev profile (no LTO) by default. To
# request an optimized Rust wheel through pip, use:
# pip install . --config-settings fbuild-profile=release
# A literal `pip install . -- --release` cannot be forwarded through the PEP
# 517 interface; `--config-settings` is the standard backend override.

[tool.setuptools]
packages = ["fbuild", "fbuild.api"]
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,8 @@ def _use_release_profile() -> bool:
3 via `[profile.dev.package."*"]`, only our own crates compile
unoptimized). Set `FBUILD_BUILD_RELEASE=1` to opt into a release
build when you actually want a fast binary (CI, packaging, perf
tests).
tests). PEP 517 callers can use `--config-settings fbuild-profile=release`;
`build_backend.py` translates that setting to this environment variable.
"""
return os.environ.get("FBUILD_BUILD_RELEASE", "").lower() in ("1", "true", "yes")

Expand Down
Loading