Skip to content

fix: unblock main CI — dead-stripped symbol + cargo-test staticlib build + oversized link/mod.rs#5406

Merged
proggeramlug merged 1 commit into
mainfrom
chore/split-link-mod-file-size
Jun 18, 2026
Merged

fix: unblock main CI — dead-stripped symbol + cargo-test staticlib build + oversized link/mod.rs#5406
proggeramlug merged 1 commit into
mainfrom
chore/split-link-mod-file-size

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes three pre-existing CI fragilities on main that were turning required checks red for PRs (including #5402). Bundled because all are "make main's CI green again."

0. cargo-test never builds the runtime staticlib (root cause of the "Could not find" failures)

The cargo-test job runs cargo test -p perry-runtime, which builds only lib/bin/test targets — not the staticlib crate-type — so libperry_runtime.a / libperry_stdlib.a are never produced by the job and only exist when restored from the rust-cache. Integration tests that compile with PERRY_NO_AUTO_OPTIMIZE=1 (e.g. functional_batch2_regressions) link the prebuilt archive directly, so the moment a PR touches perry-runtime/perry-stdlib the cached staticlib is invalidated, cargo never rebuilds it, and those tests fail with Could not find libperry_runtime.a. Fix: add an explicit cargo build -p perry-runtime -p perry-stdlib to the cargo-test job before the integration-test loop. (Verified locally: cargo test -p perry-runtime --no-run does not produce the .a; cargo build -p perry-runtime does.)

1. dead-stripped runtime symbol → undefined symbol at link

js_array_numeric_value_to_raw_f64 (added by #5291) is #[no_mangle] but called only from generated machine code, so without a #[used] anchor the linker dead-strips it from libperry_runtime.aUndefined symbols: _js_array_numeric_value_to_raw_f64. Fix: add the #[used] keepalive anchor.

2. link/mod.rs over the file-size gate (lint)

Crossed 2000 lines (2132) after #5400. Split the ~1770-line build_and_run_link orchestrator into link/build_and_run.rs (mod.rs → 357, new file → 1785; pure mechanical move).

Verification

  • File-size gate OK; cargo fmt --check clean.
  • functional_batch2_regressions 5/5 pass locally with the #[used] fix; 599 perry bin tests pass; hello-world compile+link round-trips.
  • The workflow change applies to this PR's own run (PR workflows run from the head branch).

Summary by CodeRabbit

  • Chores
    • Bumped version to 0.5.1184 (including changelog and metadata updates).
  • Bug Fixes
    • Improved CI reliability by ensuring runtime/stdlib static libraries are always built in the cargo-test job.
    • Fixed intermittent link-time failures caused by a dead-stripped runtime symbol.
    • Resolved lint/build gating failures by reorganizing the executable link step to fit file-size limits.
  • Tests
    • Confirmed unit tests pass, including link response-file and compile+link round-trip scenarios.

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 64ec2b68-2222-4ad9-b35c-74f13517b9b3

📥 Commits

Reviewing files that changed from the base of the PR and between 0534e25 and bffb4ac.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • .github/workflows/test.yml
  • CHANGELOG.md
  • CLAUDE.md
  • Cargo.toml
  • crates/perry-runtime/src/array/header.rs
  • crates/perry/src/commands/compile/link/build_and_run.rs
  • crates/perry/src/commands/compile/link/mod.rs
✅ Files skipped from review due to trivial changes (2)
  • CLAUDE.md
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • Cargo.toml
  • crates/perry-runtime/src/array/header.rs
  • crates/perry/src/commands/compile/link/mod.rs
  • crates/perry/src/commands/compile/link/build_and_run.rs

📝 Walkthrough

Walkthrough

Three CI breakages are fixed: a CI workflow update ensures libperry_runtime.a and libperry_stdlib.a are always built even when cache invalidates; a #[used] keepalive anchor prevents linker dead-stripping of js_array_numeric_value_to_raw_f64 during cold builds; and build_and_run_link (a ~1788-line executable-link orchestrator) is extracted from link/mod.rs into a new sibling file to satisfy file-size constraints. Version metadata is updated to v0.5.1184.

Changes

CI unblock fixes and module extraction

Layer / File(s) Summary
CI workflow explicit staticlib build
.github/workflows/test.yml
Adds explicit cargo build -p perry-runtime -p perry-stdlib step in the cargo-test job to ensure libperry_runtime.a and libperry_stdlib.a exist deterministically even when cache does not contain them, preventing integration test link failures.
Runtime symbol keepalive anchor
crates/perry-runtime/src/array/header.rs
Adds a #[used] static KEEP_JS_ARRAY_NUMERIC_VALUE_TO_RAW_F64 that references the extern js_array_numeric_value_to_raw_f64 function, preventing linker dead-stripping when code references it indirectly.
Link module extraction and orchestration refactoring
crates/perry/src/commands/compile/link/mod.rs, crates/perry/src/commands/compile/link/build_and_run.rs
Declares mod build_and_run in mod.rs, adds pub(super) use build_and_run::build_and_run_link re-export, and removes the inline ~1778-line function body. build_and_run.rs contains the full pub(crate) fn build_and_run_link implementation: lock-gate verification, platform detection, linker command assembly, executable-entry rewriting for ios-game-loop, HarmonyOS loose object forwarding, dead-code stripping per platform, object/stdlib/runtime ordering with Windows output formatting, plugin symbol retention, per-platform system libraries, UI archive linking with pkg-config fallbacks, native library build-and-link loop, watchOS Swift compilation, macOS Info.plist embedding, link-cache preparation, optional response-file rewrite, and linker execution.
Version bump and changelog
Cargo.toml, CLAUDE.md, CHANGELOG.md
workspace.package.version incremented to 0.5.1184; Current Version line updated in CLAUDE.md; CHANGELOG.md entry added documenting the runtime keepalive fix, module extraction rationale, and CI workflow staticlib build change.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant build_and_run_link
  participant select_linker_command
  participant cargo_build as cargo build
  participant pkg_config
  participant Linker

  Caller->>build_and_run_link: args_input, ctx, target, obj_paths, …
  build_and_run_link->>build_and_run_link: verify perry.lock gate
  build_and_run_link->>select_linker_command: compute platform booleans
  select_linker_command-->>build_and_run_link: base linker command
  build_and_run_link->>build_and_run_link: append objects, stdlib/runtime, platform system libs, UI archive
  loop each native_library manifest
    build_and_run_link->>cargo_build: sandboxed cargo build (tier3/Android/HarmonyOS env)
    cargo_build-->>build_and_run_link: native archive path
    build_and_run_link->>pkg_config: GTK4/GStreamer/WebKit flags (Linux UI)
    pkg_config-->>build_and_run_link: linker flags
  end
  build_and_run_link->>build_and_run_link: embed Info.plist, prepare link-cache, optional response-file rewrite
  build_and_run_link->>Linker: execute final linker command
  Linker-->>build_and_run_link: exit status
  build_and_run_link-->>Caller: Ok(LinkCacheStatus) or Err
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 Three bugs plagued the CI's domain,
A symbol lost, a cache in vain, a file too plain.
With #[used] anchored and modules refine,
Staticlibs built and workflows align.
Version bumped to v0.5.1184 with care,
The pipeline breathes freely through the air! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description violates explicit repository guidelines by modifying CHANGELOG.md and CLAUDE.md, which the template explicitly forbids (maintainers handle these at merge time). Remove CHANGELOG.md and CLAUDE.md edits from this PR; the maintainer will fold version bumps and changelog entries at merge time per CONTRIBUTING.md.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title concisely summarizes three CI fixes: dead-stripped symbol, cargo-test staticlib build, and oversized link/mod.rs module—accurately capturing the main changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/split-link-mod-file-size

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CHANGELOG.md`:
- Around line 1-27: The CHANGELOG.md file has been modified in this PR, but
external contributor PRs must not touch CHANGELOG.md according to the
repository's coding guidelines. Remove all changes to CHANGELOG.md from this PR
using git (e.g., git checkout HEAD -- CHANGELOG.md or by unstaging it), as the
maintainer will add the version block and write the changelog entry at merge
time as part of the release workflow.

In `@crates/perry/src/commands/compile/link/build_and_run.rs`:
- Around line 181-182: The issue is that when name_str equals triple, the root
path construction at lines 204-205 is incorrectly joining the triple again,
resulting in target/<triple>/<triple> instead of target/<triple>. To fix this,
modify the logic so that when name_str == triple (the HarmonyOS fallback case),
the root path is set to target/<triple> without joining the triple component
again. The fix involves adjusting the condition or the path construction logic
to distinguish between the perry-auto- prefix case and the exact triple match
case, ensuring the correct build directory path is used for locating the
required .o inputs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c066ce43-85cf-4243-b252-10ac3ffb1423

📥 Commits

Reviewing files that changed from the base of the PR and between 54ebb6c and 709fc36.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • CHANGELOG.md
  • CLAUDE.md
  • Cargo.toml
  • crates/perry/src/commands/compile/link/build_and_run.rs
  • crates/perry/src/commands/compile/link/mod.rs

Comment thread CHANGELOG.md Outdated
Comment on lines +1 to +27
## v0.5.1184 — chore(link): extract build_and_run_link into its own module (file-size gate)

`crates/perry/src/commands/compile/link/mod.rs` crossed the 2000-line file-size
gate (2132 lines) after #5400 grew the Windows response-file path, turning the
`lint` job red for every PR branched off main. Split the single ~1770-line
`build_and_run_link` orchestrator (the only oversized item) into a sibling
`link/build_and_run.rs`, leaving mod.rs at 357 lines and the new file at 1785.

Pure mechanical move, no behavior change:

- The function moved verbatim; `use super::*` pulls in every helper that stays in
the parent `link` module (the `NativeBackendLinkMetadata` selection, the
`resolve_optional_framework_dir` / `find_project_root_for` /
`rewrite_link_with_response_file` / `quote_response_arg` / `response_file_contents`
helpers, and the sibling-module re-exports).
- `build_and_run_link` is now `pub(crate)` (was `pub(super)`) so mod.rs can
re-export it to the parent `compile` module via
`pub(super) use build_and_run::build_and_run_link;`.
- The five fully-qualified paths that reached into the `compile` module
(`library_search::`, `sandbox_buildrs::`, `optimized_libs::`,
`run_lock_verify_for_compile`, `find_perry_workspace_root`) became
`super::super::…` from the new two-level-deep module.

All 599 perry bin unit tests pass (including the link `response_file_tests` /
`native_package_selection_tests` / `optional_framework_dir_tests`); a hello-world
compile+link round-trips through the moved driver.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Drop this changelog entry from the PR.

As per coding guidelines, external contributor PRs must not touch CHANGELOG.md; the maintainer adds the version block at merge time. Keeping it here conflicts with the repo’s release workflow.

As per coding guidelines, “External contributor PRs must NOT touch [workspace.package] version in Cargo.toml, the **Current Version:** line in CLAUDE.md, or CHANGELOG.md; the maintainer bumps the version and writes the changelog entry at merge time.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CHANGELOG.md` around lines 1 - 27, The CHANGELOG.md file has been modified in
this PR, but external contributor PRs must not touch CHANGELOG.md according to
the repository's coding guidelines. Remove all changes to CHANGELOG.md from this
PR using git (e.g., git checkout HEAD -- CHANGELOG.md or by unstaging it), as
the maintainer will add the version block and write the changelog entry at merge
time as part of the release workflow.

Sources: Coding guidelines, Learnings

Comment on lines +181 to +182
if name_str.starts_with("perry-auto-") || name_str == triple {
roots.push(entry.path());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

HarmonyOS fallback root composes an invalid build/ path

When name_str == triple, root is set to target/<triple>, then root.join(triple) produces target/<triple>/<triple>/.... That misses the real non-auto Cargo build dir and can drop required .o inputs.

Suggested fix
-                    if name_str.starts_with("perry-auto-") || name_str == triple {
-                        roots.push(entry.path());
-                    }
+                    if name_str.starts_with("perry-auto-") {
+                        roots.push(entry.path());
+                    } else if name_str == triple {
+                        // Non-auto layout lives under target/<triple>/release/build,
+                        // so use target/ as the root before appending <triple>.
+                        roots.push(std::path::PathBuf::from("target"));
+                    }

Also applies to: 204-205

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/perry/src/commands/compile/link/build_and_run.rs` around lines 181 -
182, The issue is that when name_str equals triple, the root path construction
at lines 204-205 is incorrectly joining the triple again, resulting in
target/<triple>/<triple> instead of target/<triple>. To fix this, modify the
logic so that when name_str == triple (the HarmonyOS fallback case), the root
path is set to target/<triple> without joining the triple component again. The
fix involves adjusting the condition or the path construction logic to
distinguish between the perry-auto- prefix case and the exact triple match case,
ensuring the correct build directory path is used for locating the required .o
inputs.

@proggeramlug proggeramlug force-pushed the chore/split-link-mod-file-size branch from 709fc36 to 0534e25 Compare June 18, 2026 13:11
@proggeramlug proggeramlug changed the title chore(link): extract build_and_run_link into its own module (file-size gate) fix: unblock main CI — retain dead-stripped js_array_numeric_value_to_raw_f64 + split oversized link/mod.rs Jun 18, 2026
…oversized link/mod.rs

Three pre-existing CI fragilities on main turning required checks red for PRs:

0. cargo-test runs 'cargo test -p perry-runtime', which never builds the
   staticlib crate-type, so libperry_runtime.a/libperry_stdlib.a only exist from
   cache. A PR touching perry-runtime invalidates the cached staticlib and cargo
   never rebuilds it -> PERRY_NO_AUTO_OPTIMIZE=1 tests fail with 'Could not find
   libperry_runtime.a'. Fix: add 'cargo build -p perry-runtime -p perry-stdlib'
   to the cargo-test job before the integration-test loop.

1. js_array_numeric_value_to_raw_f64 (#5291) is #[no_mangle] but called only
   from generated code, so the linker dead-strips it from libperry_runtime.a ->
   undefined symbol at link. Fix: #[used] keepalive anchor.

2. link/mod.rs crossed the 2000-line file-size gate (2132) after #5400. Split
   the ~1770-line build_and_run_link orchestrator into link/build_and_run.rs.
@proggeramlug proggeramlug force-pushed the chore/split-link-mod-file-size branch from 0534e25 to bffb4ac Compare June 18, 2026 13:47
@proggeramlug proggeramlug changed the title fix: unblock main CI — retain dead-stripped js_array_numeric_value_to_raw_f64 + split oversized link/mod.rs fix: unblock main CI — dead-stripped symbol + cargo-test staticlib build + oversized link/mod.rs Jun 18, 2026
@proggeramlug proggeramlug merged commit 7497483 into main Jun 18, 2026
15 checks passed
@proggeramlug proggeramlug deleted the chore/split-link-mod-file-size branch June 18, 2026 15:16
nglmercer pushed a commit to nglmercer/perry that referenced this pull request Jun 18, 2026
…lict in compile/link/mod.rs

Brings main's PerryTS#5400 (Windows response-file link) + PerryTS#5406/PerryTS#5408/PerryTS#5410/PerryTS#5412 codegen
split + PerryTS#5402 require Tier 2 + PerryTS#5414 unknown builtin-namespace fix into the
Windows plugin support branch. The conflict in compile/link/mod.rs is
structural: main extracted the orchestrator into link/build_and_run.rs
while the branch kept it inline. Resolved by taking main's refactor and
re-applying the Windows plugin support on top:

- compile/link/mod.rs: add PLUGIN_HOST_SYMBOLS const (the runtime + plugin
  export list — single source of truth for the macOS -u force-keep and
  the Windows .def file) plus use std::io::Write for the .def writer.

- compile/link/build_and_run.rs: convert the if ctx.needs_plugins &&
  !is_windows block to if ctx.needs_plugins with platform branches.
  The Windows branch writes a per-build perry_plugin_host_<pid>.def
  using the NAME directive (not LIBRARY — that one tells link.exe
  the output is a DLL and breaks the host .exe) and passes
  /DEF:<path> to the linker. PLUGIN_HOST_SYMBOLS replaces the inline
  
untime_syms array on macOS too.

- compile.rs: dylib link path on Windows now uses lld-link with
  /FORCE:UNRESOLVED (the .def file lists plugin_activate /
  perry_plugin_abi_version / plugin_deactivate so lld-link's empty
  default export table doesn't break GetProcAddress in the host) and
  writes a per-build .def file with the same three symbols. Lifts the
  pre-existing 'use link.exe' out for the LLVM-friendly linker.

- codegen/entry.rs: re-apply the dylib plugin ABI shim block (emits
  perry_plugin_abi_version, plugin_activate, and the optional
  plugin_deactivate) on top of main's refactor of compile_module_entry.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant