Commit Graph

2890 Commits

Author SHA1 Message Date
Macsen Casaus
1eacae7d5b Add TI C29 Clang compiler support (#8911)
Closes #8910 

Depends on https://github.com/compiler-explorer/infra/pull/2225

<!-- THIS COMMENT IS INVISIBLE IN THE FINAL PR, BUT FEEL FREE TO REMOVE
IT
Thanks for taking the time to improve CE. We really appreciate it.
Before opening the PR, please make sure that the tests & linter pass
their checks,
  by running `make check`.
In the best case scenario, you are also adding tests to back up your
changes,
  but don't sweat it if you don't. We can discuss them at a later date.
Feel free to append your name to the CONTRIBUTORS.md file
Thanks again, we really appreciate this!
-->

Co-authored-by: Macsen Casaus <m-casaus@ti.com>
Co-authored-by: mattgodbolt-molty <mattgodbolt-molty@users.noreply.github.com>
2026-07-21 15:34:47 -05:00
Matt Godbolt (bot acct)
07226d33bf Reapply extraBodyClass-driven branding, validating via the manifest in production (#8935)
Reapplies #8755, which was reverted in 463e3e70f after it broke the
staging deploy:

```
error: Top-level error (shutting down): Missing branding assets for extraBodyClass='staging'
in /infra/.deploy/static: favicon-staging.ico, site-logo-staging.svg
```

### What went wrong

`validateBrandingAssets` checked `staticPath` on the local filesystem.
That's correct for dev and local prod runs, but AWS deploys ship **two**
packages (`build-dist.sh`): the node app tarball (no `static/` at all)
and the static bundle, which goes to the CDN
(`staticUrl=https://static.ce-cdn.net/`). Production nodes never have
the branding files on disk, so any env with `extraBodyClass` set
(staging, beta, win\*) died at startup. Prod itself has an empty
`extraBodyClass`, which is why the check short-circuited there and the
bug only surfaced on the staging deploy.

### The fix (second commit)

The webpack manifest **does** ship with the node app, and lists every
asset copied from `public/` into the static bundle. So in production,
validate the derived `favicon-<class>.ico` / `site-logo-<class>.svg`
names against **manifest keys** instead of the filesystem; dev keeps the
on-disk check against `public/`. This preserves the fail-fast-on-typo
behaviour #8755 wanted, checking the thing that actually describes what
shipped to the CDN. If the manifest can't be loaded we're already on the
existing warn-and-fall-back handler, so validation is skipped rather
than fatal.

`setupStaticMiddleware` now takes the parsed manifest (loaded once in
`setupWebServer` via new `loadStaticManifest`) instead of reading it
itself.

### Verification

- Fresh **production** webpack build: all six env asset pairs (dev,
beta, staging, winprod, winstaging, wintest) appear as plain-name keys
in `manifest.json` (the win\* symlink placeholders are dereferenced on
copy).
- Booted the built `out/dist` server code in the exact deploy layout
(`staticUrl` set, nonexistent `staticPath`, `extraBodyClass=staging`):
boots cleanly and renders `favicon-staging.ico` +
`site-logo-staging.svg`; a mistyped class still fails startup with a
clear error.
- New regression tests pin both behaviours at the `setupWebServer` level
with a shipped-manifest-only layout.

⚠️ Merge timing: prod's empty `extraBodyClass` never exercises this path
— the real test is the next **staging** deploy, so this should merge
when someone (me) is ready to deploy staging and watch it.

cc @partouf — sorry again for the breakage; this validates against the
manifest rather than expecting the CDN bundle's files on local disk.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: mattgodbolt-molty <mattgodbolt-molty@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 21:37:49 +01:00
Partouf
463e3e70f0 Revert "Drive env branding from extraBodyClass alone (#8755)"
This reverts commit 13358939ce.
2026-07-19 23:55:46 +02:00
Jim McKeeth
d1aff27080 FPC: recognize AArch64 '// [n]' source-line comments (#8933) 2026-07-19 14:13:20 +02:00
Matt Godbolt
13358939ce Drive env branding from extraBodyClass alone (#8755)
The favicon selector and logo overlay used to be hard-coded switches
over the env name array (favicon) and a chained if/else in logo.pug.
Adding a new environment meant editing both, the favicon allowlist, and
the tests.

Convert both to a convention driven by extraBodyClass:
favicon-<class>.ico and site-logo-<class>.svg. logo.pug becomes one
unconditional overlay tag. getFaviconFilename takes the class string
directly.

Add validateBrandingAssets, called once during setupWebServer, which
throws if the configured assets are missing. Typos in extraBodyClass now
fail at startup with a clear error rather than serving a broken favicon.

isFaviconRequest becomes a regex matching favicon-*.ico so the log
filter no longer needs a per-env list (and previously didn't match
because req.path has a leading slash).

A new environment is now: drop two files in public/, set extraBodyClass
in the env properties file. No TS or pug changes.

---------

Co-authored-by: Matt Godbolt <mattgodbolt@hudson-trading.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-14 17:18:25 +01:00
Matt Godbolt
c425932877 Mask temp paths by the CE marker, not the live os.tmpdir() (#8920)
maskRootdir() built its strip-regex from this process's os.tmpdir(), so
it only masked paths whose root matched the current tmpdir. That is
fragile in two real cases:

- The path being masked is recorded when the compile runs and need not
share the masking process's tmpdir. Snapshots/fixtures that freeze
/tmp/... paths only mask on hosts where os.tmpdir() is /tmp (they fail
where it is e.g. /usr/tmp).
- On macOS os.tmpdir() returns /var/folders/... while real temp paths
often resolve via /private/var/folders/..., so masking silently missed.
- A configured temp root (temp dirs created outside os.tmpdir()) was
never matched at all.

Key the regex off the distinctive ce_temp_prefix marker instead,
matching whatever path segments precede it. This is tmpdir-independent
and unifies the Windows and non-Windows branches into one rule (also
masking embedded temp paths like `-I/tmp/.../include` on Windows, which
the old anchored branch missed).

Adds unit tests for maskRootdir (previously untested) covering /tmp,
/usr/tmp, macOS /private/var, Windows, the -I include case, and non-temp
paths left untouched.

<!-- THIS COMMENT IS INVISIBLE IN THE FINAL PR, BUT FEEL FREE TO REMOVE
IT
Thanks for taking the time to improve CE. We really appreciate it.
Before opening the PR, please make sure that the tests & linter pass
their checks,
  by running `make check`.
In the best case scenario, you are also adding tests to back up your
changes,
  but don't sweat it if you don't. We can discuss them at a later date.
Feel free to append your name to the CONTRIBUTORS.md file
Thanks again, we really appreciate this!
-->

---------

Co-authored-by: Matt Godbolt <mattgodbolt@hudson-trading.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-07-14 15:30:54 +01:00
Sirui Mu
7a6af91bc9 Enable AST output for Python (#8858)
This patch adds support for viewing the abstract syntax tree (AST) of
Python source code in Compiler Explorer, matching the existing AST
viewer feature for C/C++ (Clang AST).

- This patch adds a helper script `ast_dump.py` that parses and dumps
the AST for a Python source code file. `python -m ast` should also work,
but it's not available in old versions of Python.
- This patch adds an AST parser for Python which mimics the structure of
the C/C++ AST parser. It also updates the Python compiler to enable AST
output.
- All existing and new tests pass.

Assisted-by: GitHub Copilot / DeepSeek v4 Flash
Assisted-by: GitHub Copilot / DeepSeek v4 Pro

<!-- THIS COMMENT IS INVISIBLE IN THE FINAL PR, BUT FEEL FREE TO REMOVE
IT
Thanks for taking the time to improve CE. We really appreciate it.
Before opening the PR, please make sure that the tests & linter pass
their checks,
  by running `make check`.
In the best case scenario, you are also adding tests to back up your
changes,
  but don't sweat it if you don't. We can discuss them at a later date.
Feel free to append your name to the CONTRIBUTORS.md file
Thanks again, we really appreciate this!
-->

Co-authored-by: Matt Godbolt <matt@godbolt.org>
2026-07-14 08:46:10 +01:00
Cycle1337
ed127f3933 Fix compiler tool ID slimming for cached arrays (#8893) 2026-07-08 09:42:37 +02:00
moletteremi
ef9a1ce7c3 [CUDA] add scale's nvcc compiler (nvidia & amd backends) (#8849)
Hi !

This PR adds the [scale nvcc
compiler](https://docs.scale-lang.com/stable/) to the CE live site for
both AMD and Nvidia backends

It works locally and shows host asm, device LLVMIR and:

 - PTX ans SASS for Nvidia backend,
 - AMDGPU code for AMD backend.
 
There is a bug in scale 1.7.1 that prevents the compilation with both
`-S, -o` flags. As a result I had to make some temp workarounds.

For Nvidia backend, PTX is written in the device `.s` file. I run it
through `ptax` and `nvidasm` to get the SASS. To get the LLVMIR, I
decode the device `.bc` file with `llvm-dis`.

For AMD backend, the `.s` file is the AMDGPU code, and the LLVMIR is
obtained the same way.

I plan to make the `.ts` script closer to say `nvcc.ts` once the flag
bugs are fixed in scale.

Let me know if there is anything I missed for the live site integration.

infra PR: https://github.com/compiler-explorer/infra/pull/2191
Issue:
https://github.com/compiler-explorer/compiler-explorer/issues/8865

I acknowledge the use of generative AI to help drafting the code of this
PR.

---------

Co-authored-by: mattgodbolt-molty <mattgodbolt-molty@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 22:14:19 +01:00
Matt Godbolt (bot acct)
3d5a62fe6b Re-add CI check enforcing license header banners (#8883)
## What & why

We used to enforce the BSD-2-Clause license banner on every source file
via `eslint-plugin-header`. That check was lost when we migrated from
ESLint to Biome (#7033), which has no equivalent rule. This PR
reinstates it as a standalone node script, modelled on the existing
`etc/scripts/check-frontend-imports.js`.

### Why a script rather than a Biome rule
Biome 2.x has no built-in license/header rule and no plugin equivalent
to `eslint-plugin-header`; its experimental GritQL plugins aren't suited
to whole-file-prefix matching. A script needs zero new dependencies and
gives full control over scope and exemptions.

## The check (`etc/scripts/check-license-headers.js`)

A file "has an appropriate banner" if, ignoring an optional shebang, it
opens with a `// Copyright (c) …` line **and** contains the BSD-2-Clause
disclaimer body. The year and copyright holder are intentionally **not**
constrained — the tree legitimately has many holders (Compiler Explorer
Authors, Arm, Microsoft, HRT, individuals). The `(c)`/`(C)` marker is
matched case-insensitively.

**Scope:** `.ts/.js/.mjs/.cjs` under `lib/ static/ shared/ types/ test/
cypress/`.

**Exempt:** generated files (`lib/asm-docs/generated`), vendored
(`docenizer/vendor`), `.d.ts`, and three third-party ports that carry
their own upstream license — `static/ansi-to-html.ts` (MIT),
`lib/node-graceful.ts` (MIT), `shared/rison.ts` (Nanonid/rison port).

**Wired into:** CI (`test-and-deploy.yml`), `npm run check`, the `make
pre-commit` target, the husky pre-commit hook, and `lint-staged` (per
staged file).

Usage:
```
node ./etc/scripts/check-license-headers.js            # scan the tracked tree
node ./etc/scripts/check-license-headers.js <files...> # scan specific files (lint-staged)
```

## Backfill

The check surfaced **42 CE-authored files** missing the banner. This PR
backfills them all with the standard `Copyright (c) <year>, Compiler
Explorer Authors` banner, using each file's **git creation year**
(added-at-this-path, so no `--follow` rename artifacts).

## Verification

- `check-license-headers` → clean (was 42 failures)
- `biome check` on all source → no fixes needed (banner format matches
existing convention)
- `tsc` backend + frontend + tests → clean
- pre-commit gauntlet (lint, ts-check, related tests: 528 passed) ran on
commit

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: mattgodbolt-molty <mattgodbolt-molty@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 19:13:21 +01:00
Cedric
b0726beb5f Fix Triton MLIR source locations (#8718)
## Summary
- resolve nested named MLIR location aliases in the MLIR asm parser
- strip MLIR location metadata from displayed device output
- make Device Viewer source colours and hover linkage use the owning
source editor

## Testing
- npm run test -- test/asm-parser-tests.ts --reporter dot
- npm run ts-check:frontend
- npm run ts-check:backend
- npm run ts-check:tests
- npx biome check lib/parsers/asm-parser-mlir.ts
test/asm-parser-tests.ts static/panes/device-view.ts
- git diff --check -- lib/parsers/asm-parser-mlir.ts
test/asm-parser-tests.ts static/panes/device-view.ts
- pre-commit hook: npm run lint, npm run ts-check, vitest related passed

Note: a standalone npm run test-min run failed in unrelated
test/demangler-tests.ts Rust demangling expectations in this local
environment.

---------

Co-authored-by: Matt Godbolt <matt@godbolt.org>
2026-07-02 18:52:31 +01:00
hkalbasi
9b47e07d14 Add co2 language (#8842)
This PR adds [CO2](https://github.com/hkalbasi/co2) which is a language
backward compatible with C, with some Rust interop features that
compiles to Rust's MIR.

I added two compiler `co2rustc` and `co2cc`, and a tool `co2miri` which
is Miri for CO2. I added `co2cc` under C compilers, to make it possible
to view diff of assembly for a C code compiled with CO2 and clang or
gcc. `co2cc` is a CO2 frontend which accepts gcc-like flags, and can (or
at least should) compile almost every ISO compatible C23 code.
`co2rustc` accepts Rust like flags, and added under a new `CO2`
language.

Tested locally and it seems to work.

I need some help in setting up artifacts (which, if I understand
correctly, needs to happen in `infra` or `compiler-workflows`). I make
an artifact `co2-multicall` in my CI, which needs to get symlinked in
`co2cc`, `co2rustc` and `co2miri`, and it needs to run a simple project
with miri to create the miri sysroot. If you show me a similar project,
I will do the job.

Disclaimer: Written partially by LLM.

---------

Co-authored-by: Matt Godbolt <matt@godbolt.org>
2026-07-02 18:26:35 +01:00
Ofek
f7602b4360 Additional opt-pipeline optimization (#8876)
In an attempt to address #8583.
Also fix the opt-pipeline timing measurement.
2026-06-30 22:26:08 +03:00
Ofek
64b3ea2428 Optimizations in LLVM opt-pipeline (#8869)
3 Separate optimizations towards resolution of #8583:

1. (probably most substantial:) ``PrefixTree.replaceAll` did O(n^2)
allocations, causing substantial GC time:
```ts
const lineBit = line.substring(idxInOld);
const [oldValue, newValue] = this.findLongestMatch(lineBit);
```
For every character pos in a line this allocated a substring for the
entire remainder. Over one line that's O(n^2) bytes allocated, and
`processPassOutput` runs this on every line of every before/after IR
dump — which for an opt-pipeline run is enormous. This is a major
contributor to both the `replaceAll`/`processPassOutput` self-time and
the 20% GC.
Fix: make `findLongestMatch` accept a start offset and walk the existing
line string in place.

2. `JSON.stringify` large payloads once instead of twice, when caching
to S3

3. `processPassOutput` called `PrefixTree.replaceAll` which did a costly
build of replacement map but didn't use them. Now calls the new
`PrefixTree.replaceAllText`.



-------
The time measurements on the live site and on my machine differ
substantially. Before pursuing more aggressive optimizations I want to
check the impact of these, see if more work is needed.

One additional direction for (major) optimization: I think today each
pass dump is processed twice, once as 'before' and 2nd time as 'after'

---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-26 22:10:36 +03:00
Ofek
fa456a81f3 Fix #7870: use GCC pass-dump files to enumerate passes (#8826)
Following the discussion at #7870: `-fdump-passes` reports passes
enabled dynamically (e.g. loop2_unroll via `#pragma GCC unroll`) as OFF,
so they were missing from the pass dropdown although they were active.
Fixed by building the pass list from the dump files GCC actually wrote
instead.

While reworking this:
- Read all passes into a `passDumps` map shipped with the result, so
switching passes doesn't trigger a compile.
- Trim each dump to functions defined in the user's source, dropping
header/library functions; strip forced -lineno annotations from GIMPLE
(tree/IPA) dumps when Line Numbers is off, leaving RTL untouched.
- Exclude passes with empty output
- Add a GccDumpOutput type, backend unit tests and a cypress test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-20 16:06:25 +03:00
mert-kurttutan
1545df84e8 Tenstorrent support (#8803)
SFPI C++ is Tenstorrent’s C++ environment for writing custom SFPU/Tensix
kernel code with the `riscv-tt-elf-g++` toolchain.

  infra PRs: 
-
[compiler-explorer/infra#2164](https://github.com/compiler-explorer/infra/pull/2164)
-
[compiler-explorer/infra#2183](https://github.com/compiler-explorer/infra/pull/2183)

  Things this PR adds:
  - General support for SFPI C++
  - A dedicated SFPI compiler implementation
- SFPI config for local and Amazon-style `/opt/compiler-explorer`
installs
  - An SFPI example
  - Ability to compile for specific machines with mcpu flag

  Things not implemented:
  - Program execution
  - Binary output support

  Links:
  - Source code: https://github.com/tenstorrent/sfpi
  - Tenstorrent: https://tenstorrent.com/
2026-06-19 11:31:32 -04:00
Matt Godbolt (bot acct)
0cd84e0c3a Add OpenSearch shortcut for shortlinks (ce <id> in the address bar) (#8836)
## What

Adds
[OpenSearch](https://developer.mozilla.org/en-US/docs/Web/XML/Guides/OpenSearch)
support so browsers can register a `ce` keyword. Once registered, typing
`ce <shortlink>` in the address bar redirects to `/z/<shortlink>`.

## How

- **`views/opensearch.pug`** — an OpenSearch 1.1 description document.
- **`/search.xml` route** (`lib/app/server-config.ts`) — mirrors the
existing `/sitemap.xml` route; serves the doc with `Content-Type:
application/opensearchdescription+xml`.
- **`views/meta.pug`** — advertises it via `<link rel="search">` on the
main page (omitted on embed pages, where OpenSearch discovery doesn't
apply).

The search URL template (`…/z/{searchTerms}`) is built from the request
(`{protocol}://{host}{httpRoot}`, the same pattern as
`lib/storage/base.ts`), and the favicon uses the env-resolved
`staticRoot`/`faviconFilename`, so it's correct across
prod/beta/staging/local.

## Activation note

This is opt-in *discovery*, not automatic activation — browsers register
the engine after visiting the site, and the user typically activates the
`ce` keyword once (Chrome: Settings → Search engines → Site search;
Firefox surfaces it in the address bar). Chrome's auto-discovery is also
gated on a secure context, so this is best validated on an HTTPS
environment (e.g. staging/beta) rather than a plain-HTTP dev box.

## Testing

Verified against a local instance:
- `GET /search.xml` → `200`, correct content-type, well-formed XML,
`{searchTerms}` placeholder preserved, `<Url template>` absolute and
host-derived.
- `<link rel="search">` present on `/`, absent on `/e`.
- favicon URL resolves.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: mattgodbolt-molty <mattgodbolt-molty@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 12:01:51 +01:00
Lumi
938d42ae3f Add RazorForge language support (#8825)
This adds support for
[RazorForge](https://github.com/dj-lumiere/razorforge-suflae),
a precision-focused, ahead-of-time compiled language that lowers to LLVM
IR. It follows
  `docs/AddingALanguage.md`.

  ### What's included
- `lib/languages.ts` — language definition (`.rf`, Monaco mode
`razorforge`, disassembly `llvm-ir`)
  - `types/languages.interfaces.ts` — `razorforge` language key
- `lib/compilers/razorforge.ts` + `lib/compilers/_all.ts` — compiler
driver
- `static/modes/razorforge-mode.ts` + `static/modes/_all.ts` — Monarch
syntax highlighting
- `etc/config/razorforge.{defaults,amazon}.properties` — compiler config
  - `examples/razorforge/default.rf` — default example
  - `.github/labeler.yml` — `lang-razorforge` label

  ### Compiler behavior
RazorForge's CLI takes verbs + positional arguments only (all build
config lives in
`razorforge.toml`, not flags). The driver invokes the `build` verb,
which runs semantic
analysis and code generation and writes LLVM IR next to the source
(`example.rf` →
`example.ll`) without invoking `opt`/`clang`. The primary output is the
emitted LLVM IR,
  so `supportsIrView` is on and `supportsExecute` is off for now.

  ### Notes
  - Install recipe: compiler-explorer/infra#2177
  - No logo yet (`logoFilename: null`); happy to add one in a follow-up.
- Verified locally: `make`, `biome check`, `tsc`, and `vitest related`
all pass; the example
    compiles to valid LLVM IR with the released `v0.0.3-alpha` build.
2026-06-17 09:47:31 +01:00
Matt Godbolt (bot acct)
6774d762ce Construct builtin examples source explicitly at startup (#8828)
Follow-up to #8779 (which fixed #8601, `builtin.sourcePath` being
ignored). Two related changes:

### 1. Construct the builtin source explicitly at startup (restore
fail-fast)

#8779 made the builtin "Examples" source read its config lazily, on the
first `list()`/`load()`. That fixed correctness but moved the failure
mode: a misconfigured `sourcePath` (missing/unreadable dir) no longer
fails at startup. The server boots, passes healthchecks, takes traffic,
and then throws on the first request that opens Examples.

This restores fail-fast by constructing the source explicitly once
configuration is loaded:

- Replace the `builtin` module-level singleton with a `BuiltinSource`
class whose constructor scans the examples directory, plus a
`createBuiltinSource()` factory that reads `builtin.sourcePath`.
- `lib/sources/index.ts` exposes `createSources()` instead of a
top-level `sources` array (a top-level array would re-run config reads
at import time, reintroducing #8601).
- `initialiseApplication` constructs the sources after config load and
passes the `Source[]` to `ClientOptionsHandler` and
`setupControllersAndHandlers` (both already accept injected sources).

A bad `sourcePath` now throws during startup, before the instance
reports healthy. The lazy fix's correctness (config read after
`initialize()`) is preserved.

### 2. Single source of truth for the examples path

`GolangParser` independently re-read `('builtin', 'sourcePath',
'./examples/')` to locate `go/default.go`, duplicating where the
examples directory is defined. Export `getExamplesRoot()` from
`lib/sources/builtin.ts` and use it in both `createBuiltinSource()` and
`GolangParser`, so the config key and default live in one place.
Behaviour is unchanged (same value resolved); it removes the drift risk
if the key or default ever changes.

### Tests
Tests construct `BuiltinSource` directly with a fixture directory (no
import-order/`resetModules` dance), cover the unknown-example path,
assert fail-fast on a non-existent dir, and verify
`createBuiltinSource()` reads the configured path.

- `npx vitest --run test/sources/builtin-tests.ts
test/compilers/argument-parsers-tests.ts`
- `npm run ts-check`
- `npm run lint-check`

---------

Co-authored-by: mattgodbolt-molty <mattgodbolt-molty@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 22:38:23 +01:00
Puneet Dixit
6f9f064a84 Fix builtin sourcePath configuration (#8779)
## Summary

- defer scanning builtin examples until `list()` or `load()` is called
- refresh the cached examples if `builtin.sourcePath` changes
- add a regression test that imports the builtin source provider before
properties are initialized, then verifies a custom `sourcePath` is used

Closes #8601.

## Tests

- `npm run test -- --run test/sources/builtin-tests.ts --reporter=dot`
- `npm run test-min -- --run test/sources/builtin-tests.ts
--reporter=dot`
- `npm run ts-check`
- `npx biome check lib/sources/builtin.ts test/sources/builtin-tests.ts
--write`
- `npm run lint-check -- lib/sources/builtin.ts
test/sources/builtin-tests.ts`
- `git diff --check`

Co-authored-by: Deepak kudi <deepakkudi23@adsl-172-10-9-116.dsl.sndg02.sbcglobal.net>
2026-06-14 22:02:37 +01:00
Nerixyz
33bbd5792a clang-cl: Remove unused /Fm argument (#8792)
clang-cl doesn't write mapfiles like MSVC, so it doesn't support `/Fm`
and it used to report a warning:
```
clang-cl: warning: argument unused during compilation: '/FmC:\Windows\TEMP\compiler-explorer-compiler6Pv4en\output.s.exe.map' [-Wunused-command-line-argument]
```

To still get the function labels, we should use `llvm-objdump`. However,
that doesn't read the generated debug info file right now. I've opened
https://github.com/llvm/llvm-project/pull/201150 to fix this.
2026-06-14 21:53:04 +01:00
Nerixyz
13043fa989 clang-cl: fix IR/AST arguments (#8790)
When the user specified `/link` in the options or enabled "Link to
binary", clang-cl wouldn't generate IR or an AST.
Two changes fix this:
- Strip `/link` and all options after it. These are unused for IR and
AST generation anyway.
- Always specify `-c` for IR generation. For the AST, `-fsyntax-only` is
already specified in the base class.
2026-06-14 21:51:04 +01:00
Nerixyz
fab9e1f545 Use compiler's demangler type for LLVM IR (#8789)
The `LLVMIRDemangler` derived from `BaseDemangler`. It called the
demangler executable as-if it was `c++filt`. This would fail if the
demangler was `(llvm-)undname` (demangler for Microsoft names), because
it has a different output.

This PR refactors `LLVMIRDemangler` to take a demangler instance that it
calls into. `BaseDemangler` was extended to allow demangling a fixed set
of symbols and to skip applying the names. Both are needed for the IR
demangler. This way, the demangler instance can handle the details of
parsing the stdout and `LLVMIRDemangler` can provide the names and apply
the transforms.
2026-06-14 21:50:32 +01:00
Matt Godbolt (bot acct)
05f59881f7 Make --tmp-dir authoritative over inherited environment (#8819)
Fixes #8816.

- `setupTempDir()` exports the configured dir as `TMPDIR`, `TMP` *and*
`TEMP`. POSIX `os.tmpdir()` consults `TMPDIR` first, so the old TMP-only
export meant an inherited `TMPDIR` silently defeated `--tmp-dir`
(verified empirically; prod was protected only by `sudo env_reset` in
start.sh). Setting all three also covers native Windows (`TEMP` > `TMP`
there) and WSL, and means spawned tools reading any of the variables
agree.
- Restores the `os.tmpdir() !== tmpDir → throw` sanity check from
613d7f688 (#6052), lost in the #7681 split refactor.
- The startup log now prints `os.tmpdir()` — the value that actually
matters — instead of `TEMP || TMP`, which printed `undefined` on a
default Linux run.
- The `lib/temp.ts` exit hook was `process.on('exit', async ...)`: exit
handlers can't await, so it never removed anything. Replaced with a
synchronous `cleanupSync()` (tested).
- Test hygiene fix that the work surfaced: the temp-dir tests restored
the environment by reassigning `process.env` wholesale; a replaced
`process.env` is a plain object whose writes never reach the real
environment, while `os.tmpdir()` reads the real environ via `safeGetenv`
— so the suite silently leaked env state across tests (and into any test
running later in the same worker). Now saves/restores the individual
variables.

The WSL `%TEMP%`-discovery-failure path (`wsl-vc.ts` parsing garbage on
fallback, #8816 item 4) is deliberately untouched — Windows-specific and
unverifiable here.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: mattgodbolt-molty <mattgodbolt-molty@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-14 20:11:35 +01:00
Matt Godbolt (bot acct)
19ae5abcf3 Count queue jobs as completed when they settle, not when dequeued (#8821)
The last loose end from the incident issue —
`ce_compilation_queue_completed_total` incremented in a `finally` that
ran as soon as the job *returned its promise*, so it counted dequeues,
in lockstep with `ce_compilation_queue_dequeued_total`, and
`status().running` was almost always 0.

Completion is now counted when the job settles (fulfilled or rejected),
via a settlement callback rather than by awaiting in the wrapper. The
non-awaiting detail matters: my first attempt awaited `job()` so the
`finally` ran at settlement — and the existing "times out a job that
never settles" test immediately caught that this keeps `_running`
populated forever for a wedged job, reintroducing the exact
`busy`-forever wedge #8813 fixed. (A nice demonstration of that test
paying for itself.) With the callback approach, a never-settling job
correctly never counts as completed, so `dequeued − completed` now
exposes wedged/in-flight jobs — which would have made the original
incident visible directly in Grafana.

New test pins the semantics: counter unchanged while a job is running,
+1 once it settles.

Per discussion: no temp-dir sweeps of any kind (instances are replaced,
never restarted; and multiple CE processes may share a machine), so the
orphaned-dirs observation in #8811 is closed as won't-fix.

Closes #8811.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: mattgodbolt-molty <mattgodbolt-molty@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 23:02:35 +01:00
Matt Godbolt (bot acct)
51317dc13f Cap conan package extraction size; require http(s) package URLs (#8820)
Fixes #8817. Kept deliberately simple per discussion — the conan server
is CE's own infrastructure, so these are hygiene bounds, not attack
mitigations:

- A generous fixed 2GiB cap on the total declared size of extracted
files (tar-stream enforces entry bodies match their headers, so summing
`header.size` bounds bytes written). Real packages are tens to a few
hundred MiB; hitting this means a packaging error or a corrupt/bombed
archive, and the extraction rejects cleanly through the existing error
path.
- The package URL conan returns must be http(s). No redirect
restrictions (conan may legitimately hand out redirecting/presigned
URLs), no config plumbing.

Both paths tested (cap exercised by lowering the limit on the instance
under test; scheme via a `file://` URL).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: mattgodbolt-molty <mattgodbolt-molty@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 22:25:07 +01:00
Matt Godbolt (bot acct)
8e6033825a Fail healthcheck when temp filesystem free space is low (#8814)
Part of #8811 (production disk-full incident).

During the incident `/healthcheck` stayed green for hours while every
compilation failed with ENOSPC: the empty-job queue check passed (one of
two queue slots still worked), and the EFS health-file read doesn't
touch the root filesystem — so the load balancer never replaced the
instance.

This adds a free-space check on the filesystem where compilation temp
dirs are actually created: `lib/temp.ts` gains `getTempRoot()`, used
both by `temp.mkdir()` and the healthcheck, so the two can't drift
apart. (That resolves via `os.tmpdir()`; `--tmp-dir` flows into it as
`$TMP` via `setupTempDir()` — `/nosym/tmp` in prod.) Below the threshold
the healthcheck returns 500 and the load balancer replaces the instance
*before* hard failure. A `statfs` failure is itself treated as
unhealthy.

Configuration: `healthCheckMinFreeSpaceMiB` — required constructor
argument (no code default, per review); `amazon.properties` sets 2048
(at the incident's ~300MB/min fill rate that gives the LB several
minutes to act); the ceProps fallback is 100 for unconfigured/local
instances; 0 disables the check.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: mattgodbolt-molty <mattgodbolt-molty@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 22:01:53 +01:00
Matt Godbolt (bot acct)
a80b7eb210 Fix hang and uncaught exception when conan download is severed mid-stream (#8812)
Part of #8811 (production disk-full incident).

## The incident bug

If a conan library package download's connection died mid-transfer,
`downloadAndExtractPackage()`:

- threw an **uncaught exception** (`TypeError: terminated`): the error
was emitted on the `Readable.from(res.body)` wrapper, which had no
`'error'` listener — `.pipe()` neither attaches nor forwards one;
- **never settled its promise**: gunzip/tar saw neither an error nor an
end, so neither `resolve` nor `reject` was ever called. This wedged a
compilation queue slot forever, which permanently disabled temp dir
cleanup (it only runs when the queue is idle) and filled the disk on
prod.

The download path is rewritten around `stream.pipeline()`, which
propagates errors through every stage and guarantees settlement.
Per-entry file writes also go through `pipeline()`, so write errors
propagate (via `extract.destroy`) instead of being silently dropped, and
`next()` fires after the file is fully flushed rather than on stream
`'end'`.

## Extraction hardening (from review + an adversarial security pass)

- **Zip-slip guard anchored at the per-library extraction root**
(`downloadPath/<libId>`; plain `downloadPath` for `extractAllToRoot`): a
package can no longer write over a sibling library's files or the
compilation's own. The check is `path.relative`-based — immune to prefix
collisions (`/tmp/pkg` vs `/tmp/pkg-evil`) and to directories merely
*named* with leading dots.
- **Only regular-file entries are ever written**: directories, symlinks,
hardlinks and other types are drained and skipped, including malformed
entries (e.g. a directory claiming non-zero size) whose body could
otherwise land on disk as a file. CE never creates links of any kind;
prod additionally runs under the `/nosym/tmp` nosymfollow mount.
- **Zero-length files extract correctly** (the pre-pipeline code created
them as a side effect of an early `createWriteStream` — with a leaked
fd; the first pipeline version dropped them entirely).
- **Logs preserve stacks** (error objects passed to winston, not
interpolated) and the archive-controlled entry name is JSON-stringified.

Deliberately out of scope (filed as #8817): decompressed-size caps and
`packageUrl` scheme/redirect validation — defense-in-depth against our
own conan server, not blockers. See the review-convergence comment below
for vectors evaluated and rejected with rationale.

## Tests

`test/buildenvsetup-ceconan-tests.ts` (new), against a local HTTP
server:
- **Severed mid-stream download rejects rather than hanging** — against
the pre-fix code this reproduces the production failure exactly
(unhandled `TypeError: terminated` + timeout).
- Happy-path extraction including a zero-length file.
- 404 → rejection.
- Zip-slip: full escape and sibling-library escape are skipped; a
`..`-named directory inside the library root still extracts.
- A malformed sized-directory entry settles (no wedged promise) and
writes nothing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: mattgodbolt-molty <mattgodbolt-molty@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 22:01:24 +01:00
Matt Godbolt (bot acct)
9db0c4d28b Make compilation queue timeout effective; exit on uncaught exceptions (#8813)
Part of #8811 (production disk-full incident). Companion to #8812 — this
is the systemic backstop so no future never-settling job can wedge the
system.

Two changes:

**Queue timeout was dead config.** `enqueue()` passed `timeout:
undefined` to p-queue's `add()`; p-queue spreads per-call options over
its defaults, so this overrode and *disabled* the queue-wide timeout
configured from `compilationEnvTimeoutMs` (default 300s). A job whose
promise never settled therefore occupied a queue slot forever — and
since temp dir cleanup only runs when the queue is fully idle, one
wedged slot permanently disabled cleanup and filled the disk. Removing
the override makes the timeout effective: in p-queue v9 a timed-out task
rejects with `TimeoutError`, freeing the slot. (The timeout doesn't kill
underlying work — that remains the exec layer's job — but the system
makes progress again.)

**Uncaught exceptions now actually stop the process.** The handler set
`process.exitCode = 1` assuming the app would "exit naturally", but a
process with live server listeners never does: during the incident the
instance limped on half-dead for hours, passing healthchecks while every
compilation failed. Now it exits after a 1s delay (letting winston flush
its transports), and the load balancer replaces the instance.

The new queue test fails against the previous code: a never-settling job
is never rejected and `status().busy` stays true forever. Also adds
basic enqueue/nested-enqueue coverage.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: mattgodbolt-molty <mattgodbolt-molty@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 21:58:07 +01:00
Patrick Quist
c432fcc478 feat: lazy-load languages, compilers, libraries, and tools from API (#8549) 2026-06-05 15:54:27 +02:00
Francisco Giordano
99275f0f33 Fix Lean CFG parsing (#8783) 2026-06-04 23:41:19 +02:00
Patrick Quist
bbfffd064b Add FXC (D3DCompiler) compilers to HLSL (#8781) 2026-06-04 16:04:00 +02:00
leftibot
532b6c2b57 Fix #8695: [LANGUAGE REQUEST]: Add lua as a supported language (#8696)
Automated fix by @leftibot.

### What changed

> Fix #8695: add Lua as a supported language
> Add support for Lua via the reference PUC-Rio interpreter. Disassembly
is
> produced by `luac -l -l -p`, which writes a verbose bytecode listing
to
> stdout that the LuaCompiler captures and writes to the output file.
The
> class exposes overridable hooks (`resolveLuacExe`,
`getDisassemblyArgs`)
> so alternative implementations such as LuaJIT can plug in a different
> bytecode dumper without rewriting the compiler. Production config
ships
> five Lua releases (5.1.5, 5.2.4, 5.3.6, 5.4.7, 5.5.0) covering the
> actively used minor versions.

### Files
```
 etc/config/lua.amazon.properties   |  27 ++++++
 etc/config/lua.defaults.properties |   5 +
 examples/lua/default.lua           |   5 +
 lib/compilers/_all.ts              |   1 +
 lib/compilers/lua.ts               | 182 +++++++++++++++++++++++++++++++++++++
 lib/languages.ts                   |  11 +++
 test/lua-tests.ts                  | 138 ++++++++++++++++++++++++++++
 types/languages.interfaces.ts      |   1 +
 8 files changed, 370 insertions(+)
```

Closes #8695

_Triggered by @lefticus._

---------

Co-authored-by: leftibot <leftibot@users.noreply.github.com>
Co-authored-by: Matt Godbolt <matt@godbolt.org>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 22:45:23 -05:00
Markus Hoehnerbach
1e820f0511 Add CuTe DSL language support (#8715)
Add [CuTe
DSL](https://docs.nvidia.com/cutlass/latest/media/docs/pythonDSL/overview.html)
support to compiler-explorer. Tried to follow the existing Triton code,
wrapper has similar flags.

<img width="2289" height="894" alt="image"
src="https://github.com/user-attachments/assets/f4e89fcb-ca66-4d67-aace-e905693f0889"
/>

Infra: https://github.com/compiler-explorer/infra/pull/2123

---------

Co-authored-by: Matt Godbolt <matt@godbolt.org>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 22:16:57 -05:00
Rayan Salhab
5a674bbc5c Fix c2rust output location parsing (#8768)
## Summary

Fixes #8709.

This tightens the generic `file:line` parser so c2rust AST dump lines
like `CTypeId(42): Located {` are left as plain output instead of being
interpreted as source diagnostics.

## Test plan

- `npm test -- test/utils-tests.ts`
- `npm run lint-check -- lib/utils.ts test/utils-tests.ts`
- `npm run ts-check:tests`
- `npm run ts-check:backend`
- `git diff --check`

---------

Co-authored-by: cyphercodes <cyphercodes@users.noreply.github.com>
2026-06-03 21:25:50 -05:00
Nerixyz
bcd626f08f Windows: add llvm-pdbutil tool (#8763)
This adds `llvm-pdbutil`. It's similar to `llvm-dwarfdump` but for PDB
files (MS debug info).

Depends on https://github.com/compiler-explorer/infra/pull/2140.

Fixes #3941.
2026-06-03 21:09:43 -05:00
Francisco Giordano
a6cdc6a66d Add Lean (#8737)
Fixes
https://github.com/compiler-explorer/compiler-explorer/issues/5634.

Adds the Lean 4 language and compiler. 

> Lean is an open-source programming language and proof assistant that
enables correct, maintainable, and formally verified code

Since it was first requested in
https://github.com/compiler-explorer/compiler-explorer/issues/5634, it
has become increasingly relevant due to interest in AI-assisted theorem
proving, formalized mathematics, and verified software.

---

Lean compiles in two steps: first to C source code using the `lean`
executable, and then to assembly using the `leanc` compiler distributed
with Lean (I believe it's Clang).

The PR also includes a pane to visualize the emitted C source code that
was modelled after the panes for Rust and Haskell IRs and after the C
preprocessor pane for clang-format integration. Lean outputs mostly
unindented C code, so I enabled the formatter by default.

See companion infrastructure PR at
https://github.com/compiler-explorer/infra/pull/2130.



<img width="3024" height="1720" alt="image"
src="https://github.com/user-attachments/assets/5e7a1e91-e748-4599-8190-d3cb09fca503"
/>

---------

Co-authored-by: Matt Godbolt <matt@godbolt.org>
2026-06-02 12:19:29 -05:00
NoNaeAbC
e285d55130 Add glsl & slang asm compilation (#8573)
Adds the ability to compile glsl & slang to asm. Depends on
https://github.com/compiler-explorer/infra/pull/2038.

Co-authored-by: Matt Godbolt <matt@godbolt.org>
2026-06-01 21:46:34 -05:00
Steve
cbc17d85e5 Refine condition to skip emitting R2R lines (#8759)
Since .NET 11, R2R file is no longer limited to PE file. 
For example, it can now print `Emitting R2R Wasm file`, which bypassed
our asmline filter.

Loosen the text matching a bit by only recognizing `Emitting R2R` with a
tailing space.
2026-06-01 07:11:58 -05:00
Steve
cefdb2c34c Optimize .NET asm parser (#8711)
Optimize the .NET asm parser.

- Cap generic arity at `1024 * 1024` to avoid unbounded generic
instantiations
- Cache sorted source-mapping offsets per method and use binary search
for offset lookup so that we don't need to sort every time
- Avoid allocating an intermediate array for `INLRT` offset matches
- Use `Set` for labels to allow faster check
2026-05-30 14:37:10 -05:00
Ofek
e915dfc36f Fix #6744: properly handle gcc passes with a space in the name ("rtl … (#8753) 2026-05-28 23:02:47 +03:00
Ofek
1b2bb4706e Fix #8740: Remove outdated security check (#8752) 2026-05-28 20:45:19 +03:00
narpfel
bdcd800e7d [noscript] Fix generating shareable URL with storage to s3 (#8712)
Resolves #8701.

The `s3` storage backend requires an `express.Request` object to get the
request’s IP address, but the code passed `{} as express.Request`, which
led to an exception.

As a result, the fallback implementation was always used in prod, which
generates a link of the form `godbolt.org/#base64_encoded_clientstate`,
but `/#...` expects a `rison`-encoded string.
2026-05-28 20:06:32 +03:00
Ofek
f162fa265a Fix #8563: python bytecode basic-block parsing fixes (#8736)
Before:
<img width="1642" height="547" alt="image"
src="https://github.com/user-attachments/assets/ef37ebd5-cfe4-43a6-8ea0-c08dc32f0b8f"
/>


After:
<img width="1747" height="711" alt="image"
src="https://github.com/user-attachments/assets/99a7bcdb-9500-4095-a028-76e80b4c8e0f"
/>

---------
2026-05-23 15:25:52 +03:00
Ofek
41a59c0abb Fix optional-chain lint errors (#8735)
Biome started emitting 18 warnings like:
```
  ℹ Unsafe fix: Change to an optional chain.
  
    218 218 │   
    219 219 │       updateButtons() {
    220     │ - ········if·(!this.compiler·||·!this.compiler.optPipeline)·return;
        220 │ + ········if·(!this.compiler?.optPipeline)·return;
    221 221 │   
    222 222 │           const {supportedOptions, supportedFilters, initialOptionsState, initialFiltersState} =
```
This fixes them. Although the suggested fixes are labeled 'unsafe', in
these particular cases they in fact are.

Co-authored-by: Ofek Shilon <oshilon@speedata.io>
2026-05-23 14:47:16 +03:00
Steve
156f4b3542 Implement source mapping for .NET compilers (#8666)
This PR implements the source mapping for .NET compilers (C#, F#, VB.NET
and IL), supports CoreCLR, Crossgen2 and NativeAOT compilers.

- Add a parser that walks ECMA-335 metadata and Portable PDB sequence
points to build IL-offset-to-source mappings for .NET methods
- Load PDBs emitted by the .NET compiler after building the dll, and
then save the source mapping in the result
- Extend the .NET asm parser to match disassembly method signatures,
including generic types, generic methods, and inline root offsets, so
emitted asm lines inherit the correct source locations
- Use debug info emitted by the JIT `INLRT @ 0x##` as anchors to match
the offset

Showcase:


![image1](https://github.com/user-attachments/assets/5309fe70-be87-4f94-b70c-21bb91f745be)


![image2](https://github.com/user-attachments/assets/9d15741e-08ff-4f20-8a32-efa0d8307dea)
2026-05-10 09:15:36 -05:00
Matt Godbolt
5841cc57b7 Prep MCP endpoint for Anthropic connector submission (#8697)
## Summary

Anthropic's [connector review
criteria](https://claude.com/docs/connectors/building/review-criteria)
and [submission
requirements](https://claude.com/docs/connectors/building/submission)
call out two things our `/mcp` endpoint is missing today:

- every tool needs a `title` plus `readOnlyHint` / `destructiveHint`
annotation
- a public docs page covering setup and usage must exist by publish date

This PR adds both:

- **Tool annotations** on all 7 MCP tools (`compile`, `list_compilers`,
`list_languages`, `list_libraries`, `lookup_asm_instruction`,
`generate_short_url`, `get_shortlink_info`). All read-only tools get
`readOnlyHint: true`. `generate_short_url` is marked `readOnlyHint:
false` + `destructiveHint: false` + `idempotentHint: true` (it's
additive and the storage layer dedupes by config hash). Every tool gets
`openWorldHint: false` since none reach out to third-party services.
- **`docs/MCP.md`** describing the endpoint URL, transport, tool
catalogue, and a Claude Code setup line.

A judgement call worth flagging: `compile` is annotated `readOnlyHint:
true` even though `execute=true` runs user code. The CE service is
stateless from the connector's point of view and sandbox effects don't
escape the call, so the hint matches the spirit of the annotation, but
we may want to revisit if Anthropic pushes back during review.

Two known gaps not addressed here, that I'd like to discuss separately
before submission:

- **Origin-header validation** — required by the submission doc as
DNS-rebinding mitigation. The threat model mostly applies to
localhost-bound desktop servers; we're a public HTTPS service with no
auth, so the public REST API's `Allow-Origin: *` posture is consistent.
Suggest asking Anthropic to confirm exemption, or add an allowlist with
a missing-Origin pass-through (Claude Code currently sends none).
- **Submission form prep** — name/tagline/description/screenshots/logo,
and a pass through MCP Inspector. Operational, not code.

## Test plan

- [x] `npm run ts-check`
- [x] `npm run lint`
- [x] `npm run test -- --run mcp` (78 tests pass)
- [x] pre-commit hook runs clean
- [ ] manual smoke test from a fresh Claude Code MCP install (`claude
mcp add --transport http compiler-explorer https://godbolt.org/mcp` once
deployed)
- [ ] confirm tool annotations show up correctly in MCP Inspector

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-05-09 17:37:14 -05:00
Matt Godbolt (bot acct)
8891949f71 MCP: list_libraries version-string match + HPPA/Go cross-compiler instructionSet retags (#8688)
## Summary

Three related fixes prompted by driving the deployed MCP endpoint after
#8644 / #8685 merged:

1. **`list_libraries` match also searches version strings.** The natural
LLM query "find Boost 1.88" returned zero results —
`list_libraries({language:"c++", match:"boost 1.88"})` only matched
against `{id, name}`, so the version token never matched anything.

2. **HPPA + Go cross-compiler `instructionSet` retags.** Discovered via
`list_compilers(match="gcc 16", instructionSet="amd64")` returning
*only* `hppag1610` (HPPA cross-compiler) and missing plain `g161`
(x86-64 GCC). Two layered causes — HPPA had no entry in
`InstructionSetsList`, so the heuristic in `lib/instructionsets.ts`
defaulted HPPA cross-compilers to `amd64`; and 12 Go cross-compiler
groups (`386gl`, `arm32gl`, `arm64gl`, `mips*gl`, `ppc64*gl`, `riscvgl`,
`s390xgl`, `wasmgl`) had never been tagged at all.

3. **Defensive bucketing in MCP `pickLatest`.** Belt-and-braces fix:
bucket key is now `(lang, instructionSet, group)` so different compiler
families that happen to share an arch tag can't fight for the same
`latestPerMajor` slot.

## Changes

- `lib/mcp/tools/libraries.ts` — extend `applyMatch` haystack to include
`versions[].version` (human form, e.g. `"1.88.0"`) and `versions[].id`
(e.g. `"188"`).
- `lib/mcp/tools/compilers.ts` — `pickLatest` bucket key now includes
`c.group`.
- `types/instructionsets.ts` + `lib/instructionsets.ts` — new `'hppa'`
entry.
-
`etc/config/{ada,c++,c,d,fortran,gimple,objc++,objc}.amazon.properties`
—
`group.<gnathppa|gcchppa|cgcchppa|gdchppa|gimplehppa|objcppgcchppa|objchppa>.instructionSet=hppa`.
- `etc/config/go.amazon.properties` — explicit `instructionSet=` for all
13 Go arch groups (12 cross + amd64gl for symmetry).
- `test/mcp/mcp-tests.ts` — covers the bucketing-by-group case.
- `test/instructionsets-tests.ts` — covers the hppa target-string
branch.

## Follow-up

#8690 — drop the `lib/instructionsets.ts` path-based heuristic entirely
in favour of required `instructionSet` properties (the path branch is
Amazon-install-specific and silently mistags compilers on any non-Amazon
layout).

## Test plan

- [x] `npm run test -- --run mcp` — passes (existing + 2 new)
- [x] `npm run test -- --run instructionsets` — passes (existing + 1
new)
- [x] `npm run test:props` — 90/90 pass
- [x] `make pre-commit` — exits 0
- [ ] Drive the staging endpoint with `list_libraries({language:"c++",
match:"boost 1.88"})` and confirm Boost is returned with its versions.
- [ ] Drive the staging endpoint with `list_compilers({match:"gcc 16",
instructionSet:"amd64", latestPerMajor:true})` and confirm `g161`
(x86-64) appears and `hppag1610` does not.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: mattgodbolt-molty <mattgodbolt-molty@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 17:14:30 -05:00
Matt Godbolt
312846230a Add built-in MCP endpoint for LLM tool integration (#8644)
Expose Compiler Explorer's compile, list, shortlink and asm-docs APIs
via a Model Context Protocol (MCP) endpoint mounted at `/mcp`. This lets
MCP-aware clients (Claude, etc.) drive CE directly as a tool.

## Tools exposed at `/mcp`

- **`compile`** — compile source and return assembly / stdout / stderr,
with optional execution.
- `compiler` is **optional**; falls back to the language's
`defaultCompiler` from `list_languages` ("compile this hello world in
C++" is one call).
- `libraries[].version` accepts **either** the version id (`"188"`) or
the human form (`"1.88.0"`) — both work.
- When `execute: true` and the build fails,
`buildResult.stdout`/`stderr` carry the real compiler diagnostics with
**ANSI codes stripped** so an LLM caller sees clean text.
- Caps: `maxAsmLines` / `maxStdoutLines` / `maxStderrLines` with
truncation flags + total counts.
- **`list_compilers`** — with `language`, `instructionSet` (closed enum
from `InstructionSetsList`), `match` (case-insensitive AND-of-tokens;
numeric/dotted-version tokens treated as version-prefix), `lean: true`,
`maxResults`, `latestPerMajor: true`, and `includeExperimental: true`.
- Hard cap of 200 entries on lean responses with a refinement hint —
prevents the unfiltered call from overflowing.
- Each entry exposes `releaseTrack` (`stable | nightly | prerelease |
experimental`) and `supportsExecute` / `supportsBinary`.
- **`list_libraries`** — with `match`, `lean`, `maxResults` (same
lean-cap behaviour).
- **`list_languages`** — minimal listing including `defaultCompiler` and
`compilerCount` per language.
- **`generate_short_url`** — returns `{url}`. Library versions are
normalised before saving.
- **`get_shortlink_info`** — returns saved sessions in the **same shape
`compile` accepts** (`{compiler, options, libraries:[{id, version}]}`)
for direct round-tripping. Multi-pane shortlinks (executors,
conformance, CMake trees) are flattened to the basic compile inputs.
- **`lookup_asm_instruction`** — `instruction_set` is a closed enum
derived from the registered providers (no hand-listed enum values; one
source of truth in `lib/asm-docs/`).

## Implementation

- New `lib/mcp/` module wiring `@modelcontextprotocol/sdk` into the
existing Express router via `StreamableHTTPServerTransport` (stateless
mode — one server per request).
- `lib/mcp/utils.ts`: tokenised `match` with version-prefix matching for
numeric/dotted-version tokens (so `"gcc 14.1"` matches `"gcc 14.1"` and
`"gcc 14.1.0"` but NOT `"gcc 14.10"` or `"gcc 14.0.1"`); `applyCap` with
both per-call lean degradation and an absolute hard cap; `truncateLines`
strips ANSI escapes via the existing `filterEscapeSequences` helper from
`lib/utils.ts`.
- `lib/mcp/library-utils.ts`: `normaliseLibraryVersion` and
`normaliseRequestLibraries` — single source of truth for "accept id or
human version" semantics, used by both `compile` and
`generate_short_url`.
- Schema descriptions are tight (LLM context cost matters) and derive
closed-set enums programmatically from `InstructionSetsList`,
`RELEASE_TRACKS`, and a new `availableAsmDocsKeys` export — no
hand-listed values that can rot.
- Refactor `StorageBase` static helpers (`encodeBuffer`, `isCleanText`,
`getSafeHash`) to module-level functions with type-checked input so MCP
tools can build shortlink hashes without instantiating a storage
backend.
- Expose `ApiHandler.compileHandler` and split out
`getAvailableLanguages()` so MCP can reuse the same code paths the REST
API uses; new `ApiHandler.getDefaultCompilerFor()` for the
compile-default-compiler resolution.
- Browser-friendly CORS on `/mcp`: OPTIONS preflight advertises
`Access-Control-Allow-Methods: POST, OPTIONS` (the shared `cors`
middleware doesn't set Methods); 405 responses on other verbs use the
same Allow header.
- `docs/API.md`: clarify that `/api/shortener` requires a JSON object
body (the prior docs implied but didn't state it).

## Tester feedback addressed

A Claude tester drove the staging deployment through several rounds;
full thread in PR comments. Round-by-round refinements:

- Compile diagnostics surfaced on execute-mode build failures (the
original "silent `Build failed` with empty stderr" bug).
- `execute: true` schema description rewritten to reflect the actual
behaviour.
- Library `version` accepts both forms; clean errors when neither
matches with a sample of available versions.
- `latestPerMajor` rebuilt on top of the `releaseTrack` field added in
#8685, with `includeExperimental` opt-in for c++ proposal forks.
- Lean mode (`lean: true`) for catalog browsing, plus a hard 200-item
cap so even unfiltered calls don't overflow the host.
- Tokenised `match` with version-prefix semantics for
numeric/dotted-version tokens. **Behaviour change** vs the old
`/api/compilers?fields=...` text matching: bare numeric tokens are now
treated as version segments — `"2024"` no longer substring-matches
inside `"v2024beta"`, and `"14.1"` no longer wrongly matches `"14.0.1"`.
Strict improvements but worth a release-note line for callers depending
on the prior loose behaviour.
- ANSI escape code stripping from compile output.
- `instructionSet` as a structured filter (instead of relying on `match`
strings).
- `supportsExecute` / `supportsBinary` on `list_compilers` so an agent
knows whether `execute: true` will work without trying.
- `compilerCount` per language so an agent can tell well-stocked vs
niche languages at a glance.
- Compiler-not-found / library-not-found errors point at the right
`list_*` tool.

## Depends on

#8685 (releaseTrack metadata on `CompilerInfo`) — merged.

## Test plan

- [x] `npm run test -- --run mcp release-track` — all pass (78 + 22)
- [x] `npm run test-min` — full minus expensive, all green
- [x] `make pre-commit` — exits 0
- [x] Multi-round driving on staging via the live MCP endpoint,
including: default-compiler hello-world (no compiler arg), human-form
library version (`"1.88.0"`), broken-compile-with-execute (verifies
buildResult), compile+run with stdin, compile+library Boost 1.90,
parallel `-O0` vs `-O3` diff, `list_compilers latestPerMajor` for
c++/rust/go/csharp, `list_libraries match boost/fmt/json`,
`lookup_asm_instruction MOV amd64`, full `generate_short_url` →
`get_shortlink_info` → re-`compile` round-trip with library
normalisation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Matt Godbolt <mattgodbolt@hudson-trading.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: mattgodbolt-molty <mattgodbolt-molty@users.noreply.github.com>
2026-05-08 10:50:09 -04:00
Matt Godbolt (bot acct)
37f73b9b67 Add releaseTrack metadata to CompilerInfo (#8685)
## Summary

Adds a structured `releaseTrack: 'stable' | 'nightly' | 'prerelease' |
'experimental'` field on `CompilerInfo` so consumers can distinguish
release tracks that aren't currently separable from the existing
`isSemVer` / `isNightly` / `semver` fields.

Today every compiler with a non-numeric semver and `isNightly=true`
looks the same from the outside: Rust nightly, Rust beta, gcc snapshot,
and gcc's various experimental forks (`gcontracts-trunk`,
`gcc-modules-trunk`, `glambda-p2034-trunk`, ...) all share the same
shape. Anything that wants to ask "what's the canonical newest build of
language X" — a UI badge, the upcoming MCP `latestPerMajor` knob
(#8644), etc. — has no way to tell those apart.

## Implementation

- **Heuristic** in `lib/release-track.ts` driven by `asSafeVer` + the
existing `isSemVer`/`isNightly` flags. Real semvers with a prerelease
segment (e.g. micropython `1.28.0-preview`) are flagged `prerelease`;
semvers containing `trunk`/`main` and the literal `nightly` tag go to
`nightly`; bare `beta`/`alpha`/`rc` tags go to `prerelease`; `isNightly`
with anything else (the c++ experimental forks) goes to `experimental`;
everything else stays `stable`.
- **Override:** `compiler.releaseTrack=` / `group.releaseTrack=` in
`.properties` for cases the heuristic can't reach from structural
fields. Used here for `rustccggcc-master` / `mrustc-master`, where
"master" lives in the compiler id but not the semver field.
- **Backfill** in `loadPrediscovered()` so cached discovery JSON written
before this field existed doesn't break, and so a hand-edited invalid
value gets re-inferred rather than violating the type contract.
- **Tests:** 18 unit tests covering each rule + edge cases (whitespace,
mixed case, prerelease segments) and the `isReleaseTrack` type guard.

## API exposure

`releaseTrack` is on `CompilerInfo` so it appears via
`/api/compilers?fields=all`. It is **not** in the default field set, so
existing API responses are unchanged. UI consumers can opt in.

## Why this is a separate PR

This started as a piece of #8644 (MCP endpoint) — the MCP
`list_compilers` `latestPerMajor` knob needs this distinction to give
clean answers ("newest GCC" should not return a sea of experimental
forks). Rather than hack around the missing metadata in just the MCP
layer, the metadata belongs at the source where other features (UI
badging, etc.) can consume it. #8644 will rebase on top of this.

## Test plan

- [ ] `npm run test -- --run release-track` — 18/18 pass
- [ ] `npm run test:props` — 90/90 pass
- [ ] `make pre-commit` — exits 0 (pre-existing warnings only)
- [ ] Manual sanity check on staging:
`/api/compilers/c%2B%2B?fields=all` returns `releaseTrack` for each
compiler

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: mattgodbolt-molty <mattgodbolt-molty@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 19:26:03 -04:00