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
53 changes: 53 additions & 0 deletions .github/actions/python-tools/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Python tools
description: >-
Puts a venv of pinned Python tools on the path, restored whole from the
Actions cache when the runner, the interpreter and the requirements match,
so a job with a hit runs no pip. Expects setup-python to have run and
scripts/ci/python_tools.py checked out.

inputs:
requirements:
description: >-
pip requirements, one to a line. Pin each: the venv is reused until this
text or the interpreter changes, so an unpinned one, or anything one
depends on, stays at the version it first got
required: true

runs:
using: composite
steps:
- name: Key the Python tools
id: key
shell: bash
env:
REQUIREMENTS: ${{ inputs.requirements }}
run: python scripts/ci/python_tools.py key

- name: Restore the Python tools
id: restore
uses: actions/cache/restore@v6
with:
path: ${{ steps.key.outputs.venv }}
key: ${{ steps.key.outputs.key }}

- name: Install the Python tools
if: steps.restore.outputs.cache-hit != 'true'
shell: bash
env:
REQUIREMENTS: ${{ inputs.requirements }}
run: python scripts/ci/python_tools.py build

# As soon as it is built rather than when the job ends, which a failed
# job does not reach.
- name: Save the Python tools
if: steps.restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v6
with:
path: ${{ steps.key.outputs.venv }}
key: ${{ steps.key.outputs.key }}

- name: Put the Python tools on the path
shell: bash
env:
REQUIREMENTS: ${{ inputs.requirements }}
run: python scripts/ci/python_tools.py activate
74 changes: 50 additions & 24 deletions .github/actions/setup-build/action.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
name: Set up the build
description: >-
Installs the host packages, Python tools and vcpkg that configuring the
viewer needs, and adds the R2 vcpkg binary cache when asked to. Expects the
repository checked out with its submodules and VCPKG_ROOT set.
Installs the host packages, Python tools, Rust crates and vcpkg that
configuring the viewer needs, and adds the R2 vcpkg binary cache when asked
to. Expects the repository checked out with its submodules and VCPKG_ROOT
set.

inputs:
msvc-environment:
Expand Down Expand Up @@ -99,26 +100,31 @@ runs:
with:
python-version: "3.14"

# Linux builds with the CMake floor, so the floor is a floor; the Visual
# Studio 2026 generator needs a newer one. Both are pinned: vcpkg builds
# the ports with this CMake and hashes its version into every package
# ABI, so a CMake release on PyPI would otherwise miss the whole binary
# cache until a protected build filled it again.
- name: Install python dependencies
shell: bash
run: |
if [[ "$RUNNER_OS" == "Linux" ]]
then pip3 install llsd cmake==4.0.0 ninja==1.13.2
else pip3 install llsd cmake==4.4.3 ninja==1.13.2
fi

# What a Developer Command Prompt has, for the runner's architecture,
# but Visual Studio's CMake, Ninja and vcpkg.
- name: MSVC environment
if: runner.os == 'Windows' && inputs.msvc-environment == 'true'
shell: pwsh
run: python scripts/ci/msvc_environment.py

# After the MSVC environment, so nothing it puts on the path comes ahead
# of these. Linux builds with the CMake floor, so the floor is a floor;
# the Visual Studio 2026 generator needs a newer one. Both are pinned:
# vcpkg builds the ports with this CMake and hashes its version into
# every package ABI, so a CMake release on PyPI would otherwise miss the
# whole binary cache until a protected build filled it again. llsd is
# for the tests that spawn a Python peer, sentry-cli uploads the debug
# files.
- name: Python tools
uses: ./.github/actions/python-tools
with:
requirements: |
cmake==4.0.0; sys_platform == "linux"
cmake==4.4.4; sys_platform != "linux"
ninja==1.13.2
llsd==1.2.4
sentry-cli==3.8.0

- name: Bootstrap vcpkg Windows
if: runner.os == 'Windows'
shell: pwsh
Expand All @@ -129,16 +135,36 @@ runs:
shell: bash
run: ./vcpkg/bootstrap-vcpkg.sh

# Ports built with cargo (Velopack) fetch their crates here. Keyed on the
# manifest and the registry baseline, which decide the crate versions.
- name: Cache Rust dependencies
uses: actions/cache@v6
# Corrosion builds velopack_libc where Velopack ships, Windows and macOS,
# from the crates indra/rust/Cargo.lock names. They come from the cache:
# only what cargo downloads, the index and the .crate archives, as it
# unpacks the sources itself and the unpacked tree would be most of what
# a restore costs on Windows. On a miss they are fetched and saved at
# once, not when the job ends: the rows that build no viewer never run
# cargo, and would save an empty registry under the key.
- name: Restore the Rust crates
id: crates
if: runner.os != 'Linux'
uses: actions/cache/restore@v6
with:
path: |
~/.cargo/registry/index/
~/.cargo/registry/cache/
key: cargo-${{ runner.os }}-${{ hashFiles('indra/rust/Cargo.lock') }}

- name: Fetch the Rust crates
if: runner.os != 'Linux' && steps.crates.outputs.cache-hit != 'true'
shell: bash
run: cargo fetch --locked --manifest-path indra/rust/Cargo.toml

- name: Save the Rust crates
if: runner.os != 'Linux' && steps.crates.outputs.cache-hit != 'true'
uses: actions/cache/save@v6
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-cargo-${{ hashFiles('indra/vcpkg.json', 'indra/vcpkg-configuration.json') }}
restore-keys: ${{ runner.os }}-cargo-
~/.cargo/registry/index/
~/.cargo/registry/cache/
key: ${{ steps.crates.outputs.cache-primary-key }}

- name: Install AWS CLI on macOS
if: inputs.r2-cache-mode != '' && runner.os == 'macOS'
Expand Down
29 changes: 23 additions & 6 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -515,8 +515,7 @@ jobs:
exit 1
fi

pip3 install sentry-cli==3.7.0

# sentry-cli is one of the setup's Python tools.
cmake --build "$BUILD_DIRECTORY" --config Release --target symbols
sentry-cli debug-files upload --include-sources --wait "$BUILD_DIRECTORY/symbols/Release"

Expand Down Expand Up @@ -546,6 +545,9 @@ jobs:
runs-on: windows-2025-vs2026
env:
SIGN_PACKAGES: ${{ github.event_name != 'pull_request' }}
# The .NET tools restore into a folder of their own, so the cache holds
# them and not whatever packages the image carries.
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
steps:
# The app and its metadata come from the build's artifacts; the tree
# contributes only the .NET tool manifest.
Expand All @@ -557,10 +559,14 @@ jobs:
sparse-checkout: /dotnet-tools.json
sparse-checkout-cone-mode: false

# vpk is a 147 MB package: it comes from the cache, keyed on the tool
# manifest, rather than from nuget.org.
- name: Setup .NET for Velopack
uses: actions/setup-dotnet@v6
with:
dotnet-version: '10.x'
cache: true
cache-dependency-path: dotnet-tools.json

- name: Install Velopack CLI and AzureSignTool
run: dotnet tool restore
Expand Down Expand Up @@ -672,17 +678,21 @@ jobs:
env:
DEVELOPER_DIR: "/Applications/Xcode.app/Contents/Developer"
SIGN_PACKAGES: ${{ github.event_name != 'pull_request' }}
# As in the Windows packaging job.
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
steps:
# The app and its metadata come from the build's artifacts; the tree
# contributes the .NET tool manifest, the signing script, the
# entitlements and the disk image's layout.
# contributes the .NET tool manifest, the Python tools' action, the
# signing script, the entitlements and the disk image's layout.
- name: Checkout code
uses: actions/checkout@v7
with:
persist-credentials: false
ref: ${{ github.event.pull_request.head.sha || github.sha }}
sparse-checkout: |
/dotnet-tools.json
/.github/actions/python-tools/
/scripts/ci/python_tools.py
/indra/cmake/ViewerCodeSign.cmake
/indra/newview/slplugin.entitlements
/indra/newview/installers/darwin/
Expand All @@ -697,6 +707,8 @@ jobs:
uses: actions/setup-dotnet@v6
with:
dotnet-version: '10.x'
cache: true
cache-dependency-path: dotnet-tools.json

- name: Install Velopack CLI
run: dotnet tool restore
Expand All @@ -706,9 +718,14 @@ jobs:
with:
python-version: "3.14"

# The badge_icons extra badges the volume icon through Quartz.
# CMake runs the signing script; the badge_icons extra badges the
# volume icon through Quartz.
- name: Install CMake and dmgbuild
run: pip install cmake "dmgbuild[badge_icons]==1.6.7"
uses: ./.github/actions/python-tools
with:
requirements: |
cmake==4.4.4
dmgbuild[badge_icons]==1.6.7

- name: Fetch macOS app
uses: actions/download-artifact@v8
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ indra/.distcc
indra/out/*

indra/packages/*
indra/rust/target/
build-*/
indra/CMakeFiles
indra/build-vc[0-9]*
Expand Down
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ CMake with vcpkg for dependency management. The source root for CMake is `indra/
### Prerequisites

- CMake 4.0+, Visual Studio 2022/2026 (Windows) or Xcode (macOS) or GCC/Clang+Ninja (Linux)
- Rust and the .NET SDK (`dotnet tool restore`) only for Velopack installers
- Rust only for the Velopack update client (`AL_USE_VELOPACK`, Windows and macOS), whose C API Corrosion builds from `indra/rust`
- The .NET SDK (`dotnet tool restore`) only for Velopack installers
- Python 3 with `llsd` (`pip install -r requirements.txt`) only for the four tests that spawn a Python peer; without it they are registered disabled
- Python 3 with `PyYAML` and `llsd`, plus bison and flex, only for LL's LSL compiler (`indra/lscript`, a test-only reference); without them it is left out

Expand Down
1 change: 1 addition & 0 deletions doc/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
- **media_plugins** - Out-of-process media plugin implementations (CEF, VLC, GStreamer)
- **alscript** - Script Studio's language services, free of UI (depends on llcommon, llmath, Tailslide and SLua's Luau). One folder a part, each on the include path: `core/` (problems, symbols, the lexicon, messages, weights, the formatter), `lint/` (the studio's lints and fixes), `lsl/` (the LSL analyzer over Tailslide, the SLua converter), `lsl/optimizer/` (the LSL optimizer, a pass a file, sharing `allsloptimizerpass.h`), `luau/` (the SLua analyzer) and `preprocessor/` (the preprocessor and its includes)
- **lscript** - LL's LSL compiler and LSO VM, restored as a test-only reference for the optimizer and the weigher; built with tests, never linked into the viewer
- **rust** - The Rust the viewer builds: one Cargo workspace and lock file, imported by Corrosion, a vcpkg port. It holds velopack_libc, Velopack's C API over the `velopack` crate from crates.io, its sources and headers vendored from the release with one buffer fix marked Alchemy (`cmake/Velopack.cmake`): a DLL on Windows, a static library on macOS, the release build for every configuration
- **newview** - The main viewer application. Contains all viewer-specific logic: the rendering pipeline, scene graph, UI floaters/panels, settings, object/avatar systems, and the application entry point. Links against all libraries above plus third-party: Tracy, OpenXR, Discord SDK, NVAPI, TinyEXR, Velopack

### Application Lifecycle
Expand Down
32 changes: 14 additions & 18 deletions doc/BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ Every platform needs a C++ toolchain plus:

- **CMake** 4.0+
- **Git**
- **Rust** and **.NET SDK** — only for Velopack installers (`-DAL_USE_VELOPACK=ON`)
- **Rust** — only for the Velopack update client (`-DAL_USE_VELOPACK=ON`, Windows and macOS), whose C API the build compiles from `indra/rust`
- **.NET SDK** — only for Velopack installers
- **Python** 3 — only for the tests that spawn a Python peer (see [Running tests](#running-tests))

Install commands are platform-specific; see below.
Expand All @@ -33,7 +34,7 @@ Install the following:
- [Visual Studio 2026](https://visualstudio.microsoft.com/vs/community/) — select the **Desktop development with C++** workload
- [CMake](https://cmake.org/download/) 4.0+
- [Git for Windows](https://git-scm.com/install/windows)
- [Rust](https://rust-lang.org/tools/install/) — run `rustup-init.exe` and accept defaults (packaging only)
- [Rust](https://rust-lang.org/tools/install/) — run `rustup-init.exe` and accept defaults (Velopack only)
- [.NET SDK](https://dotnet.microsoft.com/en-us/download) (packaging only)

Sanity-check in a fresh terminal:
Expand All @@ -54,10 +55,11 @@ brew install git cmake zip unzip curl pkgconf automake autoconf autoconf-archive
gettext libtool rustup dotnet
```

Initialize the Rust toolchain (packaging only):
Put Homebrew's rustup on the path and install a stable toolchain (Velopack only). The formula is keg-only and no longer provides `rustup-init`; add the `export` to your shell profile too:

```
rustup-init -y
export PATH="$(brew --prefix rustup)/bin:$PATH"
rustup default stable
```

### Linux
Expand All @@ -70,7 +72,7 @@ Install system packages for your distro:
```
sudo pacman -Syu automake autoconf autoconf-archive base-devel cmake fontconfig git glib2-devel \
gstreamer gst-plugins-base-libs ninja libglvnd libtool libvlc libx11 pkgconf python \
wayland dotnet-sdk rustup zip nasm
wayland dotnet-sdk zip nasm
```

</details>
Expand All @@ -88,7 +90,7 @@ sudo apt install \
libthai-dev libtool libudev-dev libunwind-dev liburing-dev libvlc-dev libwayland-dev \
libx11-dev libxcursor-dev libxext-dev libxfixes-dev libxft-dev libxi-dev libxinerama-dev \
libxkbcommon-dev libxrandr-dev libxss-dev libxtst-dev linux-libc-dev ninja-build \
pkgconf tar tex-common texinfo unzip zip dotnet-sdk-10.0 rustup nasm
pkgconf tar tex-common texinfo unzip zip dotnet-sdk-10.0 nasm
```

</details>
Expand All @@ -106,7 +108,7 @@ sudo apt install \
libthai-dev libtool libudev-dev libunwind-dev liburing-dev libvlc-dev libwayland-dev \
libx11-dev libxcursor-dev libxext-dev libxfixes-dev libxft-dev libxi-dev libxinerama-dev \
libxkbcommon-dev libxrandr-dev libxss-dev libxtst-dev linux-libc-dev ninja-build \
pkgconf tar tex-common texinfo unzip zip dotnet-sdk-10.0 rustup nasm
pkgconf tar tex-common texinfo unzip zip dotnet-sdk-10.0 nasm
```

</details>
Expand All @@ -120,7 +122,7 @@ sudo apt install \
sudo dnf group install "Development Tools"
sudo dnf install cmake fontconfig-devel git glib2-devel gstreamer1-devel \
gstreamer1-plugins-base-devel libX11-devel libglvnd-devel \
ninja-build python3 vlc-devel wayland-devel dotnet-sdk-10.0 rustup
ninja-build python3 vlc-devel wayland-devel dotnet-sdk-10.0
```

You may need to enable EPEL first: `sudo dnf install epel-release`
Expand All @@ -131,7 +133,7 @@ You may need to enable EPEL first: `sudo dnf install epel-release`
sudo dnf install @development-tools @c-development cmake fontconfig-devel git glib-devel \
gstreamer1-devel gstreamer1-plugins-base-devel libX11-devel \
libglvnd-devel ninja-build python3 vlc-devel \
wayland-devel dotnet-sdk-10.0 rustup perl-IPC-Cmd perl-FindBin perl-Time-Piece \
wayland-devel dotnet-sdk-10.0 perl-IPC-Cmd perl-FindBin perl-Time-Piece \
autoconf-archive perl-open libXcursor-devel wayland-protocols-devel dbus-devel \
ibus-devel mesa-libGLU-devel libxkbcommon-devel mesa-libEGL-devel mesa-libGL-devel \
libXtst-devel libXrandr-devel pipewire-devel pulseaudio-libs-devel alsa-lib-devel \
Expand All @@ -154,12 +156,6 @@ sudo zypper install cmake fontconfig-devel git glib2-devel gstreamer-devel \

</details>

Initialize a stable Rust toolchain (packaging only):

```
rustup default stable
```

## Clone and bootstrap

Alchemy vendors the [Dullahan](https://github.com/AlchemyViewer/dullahan) CEF wrapper — used by the in-world web media plugin — as a git submodule under `indra/dullahan`. It builds from source as part of the tree, so the submodule must be present before you configure. Clone with `--recurse-submodules`:
Expand Down Expand Up @@ -521,15 +517,15 @@ Velopack needs the `vpk` .NET tool. Install it once per clone:
dotnet tool restore
```

### Rust / `cargo` missing during packaging
### `rustc` not found when configuring

Velopack invokes `cargo`. Install a stable Rust toolchain:
With `AL_USE_VELOPACK=ON` the build compiles Velopack's C API, `indra/rust/velopack_libc`, with cargo through [Corrosion](https://github.com/corrosion-rs/corrosion), which looks for `rustc` on the path and in `~/.cargo/bin`. Install a stable toolchain:

```
rustup default stable
```

Only needed with `AL_USE_VELOPACK=ON`.
On macOS, Homebrew's rustup keeps `rustc` and `cargo` in `$(brew --prefix rustup)/bin`, which must be on the path. A target the toolchain lacks, such as `x86_64-apple-darwin` for an x86_64 build on Apple silicon, is added with rustup while configuring; `-DRust_RUSTUP_INSTALL_MISSING_TARGET=OFF` stops that.

### Warnings fail the build

Expand Down
1 change: 1 addition & 0 deletions indra/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ set(
SDL3
Sentry
Tracy
Velopack
WebRTC
)

Expand Down
1 change: 0 additions & 1 deletion indra/cmake/Dependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ al_import(ll::fontconfig PACKAGE Fontconfig TARGETS Fontconfig
al_import(ll::faudio PACKAGE FAudio CONFIG TARGETS FAudio::FAudio DEFINES LL_FAUDIO=1 WHEN AL_USE_FAUDIO)
al_import(ll::openal PACKAGE OpenAL CONFIG TARGETS OpenAL::OpenAL DEFINES LL_OPENAL=1 WHEN AL_USE_OPENAL)
al_import(ll::openxr PACKAGE OpenXR CONFIG TARGETS OpenXR::headers OpenXR::openxr_loader WHEN AL_USE_OPENXR)
al_import(ll::velopack PACKAGE unofficial-velopack CONFIG TARGETS unofficial::velopack::velopack DEFINES LL_VELOPACK=1 WHEN AL_USE_VELOPACK)
al_import(ll::nsspellchecker LINK "-framework AppKit" "-framework Foundation" WHEN AL_USE_NSSPELLCHECKER)
al_import(ll::winspellcheck LINK ole32 WHEN AL_USE_WINSPELLCHECK)
# gersemi: on
Loading
Loading