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 .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
.squad/agents/*/history.md merge=union
.squad/log/** merge=union
.squad/orchestration-log/** merge=union
*.sh text eol=lf
53 changes: 51 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ jobs:
# ─────────────────────────────────────────────
# Backend-specific distribution builds (one Cargo feature each)
#
# The published packages are single-backend builds: exactly one backend
# feature, stamped with OSCAN_DISTRIBUTION_BACKEND. Those cfg
# Published slim packages are single-backend builds: exactly one backend
# feature, stamped with the legacy OSCAN_DISTRIBUTION_BACKEND input. Those cfg
# configurations compile different code from the default all-backends
# build, so they are tested here as first-class configurations rather
# than only being discovered at release time.
Expand Down Expand Up @@ -83,6 +83,52 @@ jobs:
grep -qx "default-backend: ${{ matrix.backend }}" <<<"$metadata"
grep -qx "distribution: ${{ matrix.backend }}" <<<"$metadata"

# The full package is a strict distribution too, not an unstamped development
# build: all backends are present, LLVM is deterministic, and packaged lookup
# remains enabled.
full-distribution:
name: Linux full-profile distribution build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
submodules: recursive

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable

- name: Cache cargo dependencies
uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: linux-full-profile-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: linux-full-profile-cargo-

- name: Test the full profile contract and CLI
env:
OSCAN_DISTRIBUTION_PROFILE: full
OSCAN_DEFAULT_BACKEND: llvm
run: |
cargo test backend::select::tests
cargo test --test cli_help

- name: The built compiler reports the full profile
env:
OSCAN_DISTRIBUTION_PROFILE: full
OSCAN_DEFAULT_BACKEND: llvm
shell: bash
run: |
set -euo pipefail
cargo build --release
metadata="$(./target/release/oscan --version)"
echo "$metadata"
grep -qx "backends: llvm, cranelift, c" <<<"$metadata"
grep -qx "default-backend: llvm" <<<"$metadata"
grep -qx "distribution: full" <<<"$metadata"

# ─────────────────────────────────────────────
# Verify generated docs are up to date
# ─────────────────────────────────────────────
Expand Down Expand Up @@ -144,6 +190,9 @@ jobs:
- name: Run unit tests (53 tests)
run: cargo test

- name: Archive installer coexistence tests
run: python -m unittest scripts.test_installers.WindowsInstallerCoexistenceTests

- name: Integration tests — positive (freestanding)
shell: bash
run: |
Expand Down
125 changes: 63 additions & 62 deletions .github/workflows/release.yml

Large diffs are not rendered by default.

45 changes: 27 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,18 @@ It is designed to be approachable for people and reliable for AI coding tools.

Download the latest release from
[GitHub Releases](https://github.com/lucabol/Oscan/releases). Choose the
**LLVM package** unless you specifically need the Cranelift or C backend.
**LLVM slim package** for the smallest recommended install, or choose **full**
to use LLVM, Cranelift, and C from one compiler with `--backend`.

| Platform | Recommended download | Notes |
|---|---|---|
| Windows x86_64 | `oscan-vX.Y.Z-windows-x86_64-llvm.msi` or `oscan-vX.Y.Z-windows-x86_64-llvm.zip` | The MSI is the simplest option |
| Linux x86_64 | `oscan-vX.Y.Z-linux-x86_64-llvm.tar.xz` | Requires glibc 2.34 or newer |
| Windows x86_64 | `oscan-vX.Y.Z-windows-x86_64-llvm.msi` or `oscan-vX.Y.Z-windows-x86_64-llvm.zip` | LLVM remains the transition default; `-full.zip` includes all backends |
| Linux x86_64 | `oscan-vX.Y.Z-linux-x86_64-llvm.tar.xz` | `-full.tar.xz` includes all backends; LLVM requires glibc 2.34+ |
| macOS x86_64 | `oscan-vX.Y.Z-macos-x86_64-c.tar.gz` | Requires Xcode Command Line Tools |

Windows and Linux also provide `cranelift` and `c` packages. Each release
Windows and Linux provide `full`, `llvm`, `cranelift`, and `c` profiles. Full
contains one backend-neutral compiler; the others are smaller slim packages.
Each release
includes `SHA256SUMS`; keep the downloaded file's original name and verify it
before installing. The [installation guide](docs/guide.md#installation) has
step-by-step verification, upgrade, and uninstall instructions.
Expand All @@ -51,33 +54,38 @@ Install the latest recommended package:
iwr -useb https://raw.githubusercontent.com/lucabol/Oscan/master/scripts/install-latest.ps1 | iex
```

To select a backend explicitly:
Install full, or select a slim package explicitly:

```powershell
iwr -useb https://raw.githubusercontent.com/lucabol/Oscan/master/scripts/install-latest.ps1 -OutFile install-latest.ps1
.\install-latest.ps1 -Profile full -SetDefault
.\install-latest.ps1 -Backend llvm
.\install-latest.ps1 -Backend cranelift
.\install-latest.ps1 -Backend c
```

Open a new terminal after installation, then check:
Archive profiles coexist as `oscan-full`, `oscan-llvm`, `oscan-cranelift`, and
`oscan-c`. The first archive install safely creates `oscan`; later installs
preserve that selection unless `-SetDefault` is passed. Open a new terminal,
then check:

```powershell
oscan --version
```

### Linux

Download the recommended archive and `SHA256SUMS`, verify the archive, extract
it, and run the included installer:
Download an archive and `SHA256SUMS`, verify it, extract it, and run the
included installer. This example installs full and selects it as the default:

```bash
tar xf oscan-vX.Y.Z-linux-x86_64-llvm.tar.xz
./oscan-vX.Y.Z-linux-x86_64-llvm/install.sh
tar xf oscan-vX.Y.Z-linux-x86_64-full.tar.xz
./oscan-vX.Y.Z-linux-x86_64-full/install.sh --set-default
oscan --version
oscan app.osc --backend cranelift
```

On Debian or Ubuntu, the LLVM package also needs:
On Debian or Ubuntu, the LLVM and full packages also need:

```bash
sudo apt-get install libedit2 libffi8 libxml2 libz3-4 libzstd1 zlib1g
Expand All @@ -93,8 +101,9 @@ xcode-select --install
tar xf oscan-vX.Y.Z-macos-x86_64-c.tar.gz
```

Copy `oscan` to a directory in your `PATH`, such as `/usr/local/bin`, then run
`oscan --version`.
Run the included `install.sh` to create `oscan-c` without disturbing another
profile, then run `oscan-c --version`. Pass `--set-default` only when the plain
`oscan` command should select this profile.

### Build from source

Expand Down Expand Up @@ -188,9 +197,9 @@ small `wc`-style command-line program step by step.
| `oscan app.osc --debuginfo line-tables` | Keep Oscan source locations for debugging |
| `oscan --help` | Show every option |

A release package contains one backend. If you request another one, Oscan tells
you which package to install. `--opt-level size|speed` applies to LLVM and
Cranelift generated code; the default is `size`.
Full contains every backend; slim packages contain one and identify the full or
matching slim package when another is requested. `--opt-level size|speed`
applies to LLVM and Cranelift generated code; the default is `size`.

Debug information is opt-in: `--debuginfo none` is the default, while
`--debuginfo line-tables` enables source breakpoints, stepping, stack
Expand Down Expand Up @@ -240,8 +249,8 @@ See the [full built-in function reference](docs/builtins.md) for signatures and
`free` calls or a garbage collector.
- **Targets:** release and cross-compilation support varies by platform and
backend.
- **Distribution:** each release package contains one backend and its required
support files.
- **Distribution:** full contains all backends; coexistence-safe slim profiles
contain one backend and its required support files.

See [Compiler Technical Details](docs/technical-details.md) for backend
selection, package layouts, runtime and linker behavior, supported targets,
Expand Down
56 changes: 34 additions & 22 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,27 @@ use sha2::{Digest, Sha256};

fn main() {
stamp_git_version();
stamp_distribution_backend();
stamp_distribution_config();
generate_native_link_assets();
configure_inprocess_toolchain();
generate_inprocess_link_assets();
}

// ---------------------------------------------------------------------------
// Backend-specific distribution builds.
// Packaged distribution builds.
//
// `OSCAN_DISTRIBUTION_BACKEND=llvm|cranelift|c` marks a build as *the*
// packaged compiler for one backend: the resulting `oscan` defaults to that
// backend deterministically instead of probing host capabilities, and every
// other backend is compiled out (`--no-default-features --features
// backend-<name>`). Leaving it unset is the ordinary development build: all
// enabled features, existing capability-based default behavior.
// `OSCAN_DISTRIBUTION_PROFILE=full|llvm|cranelift|c` identifies the package,
// while `OSCAN_DEFAULT_BACKEND=llvm|cranelift|c` independently fixes its
// implicit backend. `OSCAN_DISTRIBUTION_BACKEND` remains a compatibility input
// for legacy slim builds and expands to matching profile/default values.
//
// The rules themselves live in `src/backend/distribution_contract.rs`,
// shared verbatim with the compiler (which reads the stamp and unit-tests
// the rules) so the two can never drift apart. Validation happens here, at
// build time, so a mismatched pair — a stamp naming a disabled backend, or
// a stamp on a build that still contains every backend — fails the build
// with a named reason instead of producing an artifact whose name promises
// something it is not.
// with a named reason instead of producing an artifact whose profile/default
// metadata promises something it is not.
// ---------------------------------------------------------------------------

mod distribution_contract {
Expand All @@ -48,7 +46,9 @@ const BACKEND_FEATURES: [(&str, &str); 3] = [
("c", "CARGO_FEATURE_BACKEND_C"),
];

fn stamp_distribution_backend() {
fn stamp_distribution_config() {
println!("cargo:rerun-if-env-changed=OSCAN_DISTRIBUTION_PROFILE");
println!("cargo:rerun-if-env-changed=OSCAN_DEFAULT_BACKEND");
println!("cargo:rerun-if-env-changed=OSCAN_DISTRIBUTION_BACKEND");
println!("cargo:rerun-if-changed=src/backend/distribution_contract.rs");

Expand All @@ -57,17 +57,29 @@ fn stamp_distribution_backend() {
.filter(|(_, feature)| std::env::var_os(feature).is_some())
.map(|(name, _)| *name)
.collect();
let raw = std::env::var("OSCAN_DISTRIBUTION_BACKEND").unwrap_or_default();
let stamp = distribution_contract::validate_distribution_stamp(&raw, &enabled)
.unwrap_or_else(|reason| panic!("{reason}"));

// Always stamped (empty means "not a distribution build") so the
// compiler can read it with a plain `env!` rather than an `option_env!`
// whose absence would be indistinguishable from a stale build.
println!(
"cargo:rustc-env=OSCAN_DISTRIBUTION_BACKEND={}",
stamp.unwrap_or_default()
);
let profile_raw = std::env::var("OSCAN_DISTRIBUTION_PROFILE").unwrap_or_default();
let default_raw = std::env::var("OSCAN_DEFAULT_BACKEND").unwrap_or_default();
let legacy_raw = std::env::var("OSCAN_DISTRIBUTION_BACKEND").unwrap_or_default();
let config = distribution_contract::validate_distribution_config(
&profile_raw,
&default_raw,
&legacy_raw,
&enabled,
)
.unwrap_or_else(|reason| panic!("{reason}"));

let profile = config.profile.as_deref().unwrap_or("");
let default_backend = config.default_backend.as_deref().unwrap_or("");
let legacy_backend = match profile {
"llvm" | "cranelift" | "c" => profile,
_ => "",
};

// Always stamp every normalized value. Empty means "not configured", and
// lets the compiler use env! without stale option_env! ambiguity.
println!("cargo:rustc-env=OSCAN_DISTRIBUTION_PROFILE={profile}");
println!("cargo:rustc-env=OSCAN_DEFAULT_BACKEND={default_backend}");
println!("cargo:rustc-env=OSCAN_DISTRIBUTION_BACKEND={legacy_backend}");
}

fn stamp_git_version() {
Expand Down
Loading
Loading