Skip to content

build: add ARM64 architecture support to devcontainer - #14008

Merged
kgryte merged 1 commit into
stdlib-js:developfrom
anandkaranubc:build/devcontainer-arm64-support
Aug 8, 2026
Merged

build: add ARM64 architecture support to devcontainer#14008
kgryte merged 1 commit into
stdlib-js:developfrom
anandkaranubc:build/devcontainer-arm64-support

Conversation

@anandkaranubc

@anandkaranubc anandkaranubc commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Resolves #4934, stdlib-js/metr-issue-tracker#1123

Description

What is the purpose of this pull request?

This pull request:

  • adds ARM64 (aarch64) architecture support to the devcontainer
  • fixes two toolchain issues that block ARM64 usage
  • updates documentation to reflect ARM64 support

Related Issues

Does this pull request have any related issues?

This pull request has the following related issues:

Questions

Any questions for reviewers of this pull request?

No.

Other

Any other information relevant to this pull request? This may include screenshots, references, and/or implementation notes.

Analysis

The original issue was filed against the universal:2 image, which has no ARM64 manifest. PR #8600 (Dec 2025) switched the image to javascript-node:1-22-bookworm, which ships both linux/amd64 and linux/arm64. So the base image blocker is already resolved.

The remaining problems were found by building the devcontainer on an Apple Silicon Mac and testing every tool:

Problem 1: GCC 16 and binutils version mismatch

The r-apt devcontainer feature upgrades the base system from Debian bookworm to testing, which installs GCC 16. GCC 16 was built with HAVE_AS_AEABI_BUILD_ATTRIBUTES=1 (it detected support for AArch64 AEABI build attributes in its build-time assembler), but the container's as is from an older binutils that does not understand .aeabi_subsection and .aeabi_attribute directives. This causes every native compilation to fail on ARM64: npm install (@kaciras/deasync node-gyp build), make install-node-addons, make benchmark-c, and gfortran.

Reference: https://gcc.gnu.org/pipermail/gcc-patches/2024-October/666180.html

Problem 2: shellcheck wrong architecture

The devcontainers-extra/features/shellcheck feature uses assetRegex='shellcheck-.*.tar.xz$' to match release assets, with no architecture filtering. On ARM64 it installs a RISC-V binary instead of the linux.aarch64 one. The binary appears to work only because Docker Desktop has QEMU binfmt emulation, but it is not native and would fail in environments without emulation.

Approaches considered

  1. Fix in post-create only (chosen): Add architecture detection to the post-create script. On ARM64, pin gcc/g++/gfortran back to version 12 via update-alternatives and replace the shellcheck binary with the correct release. On x86-64, nothing changes.

  2. Separate ARM64 devcontainer.json: Create a second config under .devcontainer/arm64/. Rejected because the base image already supports both architectures, so a second config adds maintenance burden for no gain.

  3. Replace r-apt with r-rig for everyone: Would avoid the gcc upgrade entirely, but changes the working x86-64 setup and needs separate testing.

Changes

  • .devcontainer/post-create: Architecture detection. On aarch64, pin gcc/g++/gfortran to version 12 before make install, and replace the shellcheck binary after feature install.
  • tools/make/lib/install/shellcheck.mk: Add a linux.aarch64 URL branch for ARM64 Linux, update the darwin FIXME comment to be more specific.
  • deps/checksums/shellcheck_v0_8_0_linux_aarch64_tar_xz/sha256: Checksum for the linux.aarch64 shellcheck release.
  • docs/contributing/setting_up_a_devcontainer.md: Remove the "does not support ARM64" note, add "supports both x86-64 and ARM64".
  • docs/contributing/FAQ.md: Same doc update, remove the dead [devcontainer-issue] link.

Verification

Tested on Apple Silicon (M3, Docker Desktop, aarch64). After fixes:

Tool Version Architecture
node v22.16.0 arm64
gcc 12.5.0 aarch64
g++ 12.5.0 aarch64
gfortran 12.5.0 aarch64
shellcheck 0.11.0 ARM aarch64
R 4.6.1 aarch64
julia 1.12.6 aarch64
pandoc 3.10.1 aarch64
python 3.11.2 aarch64

Additionally verified:

  • Native addon build (make install-node-addons): passes
  • Native addon tests (test.native.js): 10/10 pass
  • C benchmarks (make benchmark-c): ~262M ops/sec
  • C examples (make examples-c): compile and run correctly
  • Fortran compilation: works
  • shellcheck lint: works

Checklist

Please ensure the following tasks are completed before submitting this pull request.

AI Assistance

When authoring the changes proposed in this PR, did you use any kind of AI assistance?

  • Yes
  • No

If you answered "yes" above, how did you use AI assistance?

  • Code generation (e.g., when writing an implementation or fixing a bug)
  • Test/benchmark generation
  • Documentation (including examples)
  • Research and understanding

Disclosure

If you answered "yes" to using AI assistance, please provide a short disclosure indicating how you used AI assistance.

Claude Code was used for the ARM64 investigation (building the devcontainer, probing tool architectures, diagnosing the GCC/binutils mismatch and shellcheck wrong-arch issue), writing the post-create fixups, and updating documentation.


@stdlib-js/reviewers

Closes: stdlib-js#4934

---
type: pre_commit_static_analysis_report
description: Results of running static analysis checks when committing changes.
report:
  - task: lint_filenames
    status: passed
  - task: lint_editorconfig
    status: passed
  - task: lint_markdown_pkg_readmes
    status: na
  - task: lint_markdown_docs
    status: passed
  - task: lint_markdown
    status: na
  - task: lint_package_json
    status: na
  - task: lint_repl_help
    status: na
  - task: lint_javascript_src
    status: na
  - task: lint_javascript_cli
    status: na
  - task: lint_javascript_examples
    status: na
  - task: lint_javascript_tests
    status: na
  - task: lint_javascript_benchmarks
    status: na
  - task: lint_python
    status: na
  - task: lint_r
    status: na
  - task: lint_c_src
    status: na
  - task: lint_c_examples
    status: na
  - task: lint_c_benchmarks
    status: na
  - task: lint_c_tests_fixtures
    status: na
  - task: lint_shell
    status: na
  - task: lint_typescript_declarations
    status: passed
  - task: lint_typescript_tests
    status: na
  - task: lint_license_headers
    status: passed
---
@anandkaranubc
anandkaranubc requested a review from a team August 7, 2026 09:01
@stdlib-bot stdlib-bot added the Needs Review A pull request which needs code review. label Aug 7, 2026
@anandkaranubc

Copy link
Copy Markdown
Contributor Author

Context

The original issue (#4934) was filed because mcr.microsoft.com/devcontainers/universal:2 has no ARM64 manifest. PR #8600 (Dec 2025) switched the image to javascript-node:1-22-bookworm, which ships both linux/amd64 and linux/arm64. That resolves the base image blocker, but two toolchain problems remain.

Problem 1: GCC/binutils version mismatch on ARM64

The r-apt devcontainer feature upgrades the base system from Debian bookworm to testing. This installs GCC 16, which was built with HAVE_AS_AEABI_BUILD_ATTRIBUTES=1. That means GCC 16 emits .aeabi_subsection and .aeabi_attribute assembler directives for AArch64. But the container's assembler (from an older binutils) does not understand those directives, so every compilation fails with:

Error: unknown pseudo-op: `.aeabi_attribute'
Error: unknown pseudo-op: `.aeabi_subsection'

This breaks npm install (the @kaciras/deasync native addon), make install-node-addons, make benchmark-c, make examples-c, and gfortran. Pinning gcc/g++/gfortran back to version 12 via update-alternatives fixes all of these.

Reference for the AEABI build attributes feature: https://gcc.gnu.org/pipermail/gcc-patches/2024-October/666180.html

Problem 2: shellcheck wrong architecture

The devcontainers-extra/features/shellcheck uses assetRegex='shellcheck-.*.tar.xz$' to match GitHub release assets, with no architecture filtering. The v0.11.0 release has six matching assets:

shellcheck-v0.11.0.darwin.aarch64.tar.xz
shellcheck-v0.11.0.darwin.x86_64.tar.xz
shellcheck-v0.11.0.linux.aarch64.tar.xz
shellcheck-v0.11.0.linux.armv6hf.tar.xz
shellcheck-v0.11.0.linux.riscv64.tar.xz
shellcheck-v0.11.0.linux.x86_64.tar.xz

On ARM64, the feature picks the RISC-V binary. It installs without error and appears to run only because Docker Desktop registers QEMU binfmt handlers. On amd64, it correctly picks x86_64. The post-create script replaces the wrong binary with the correct linux.aarch64 release.

Approaches considered

  1. Fix in post-create only (chosen): Smallest diff. On ARM64, pin gcc/g++/gfortran to version 12 and replace shellcheck. On x86-64, nothing changes.

  2. Separate ARM64 devcontainer.json: The base image already handles both architectures, so a second config file adds maintenance cost for no benefit.

  3. Replace r-apt with r-rig for everyone: Would avoid the gcc upgrade, but changes the working x86-64 setup and needs separate testing.

What was verified (Apple Silicon M3)

  • All 9 toolchain binaries are native aarch64 after fixes
  • Native addon build and tests: 10/10 pass
  • C benchmarks: ~262M ops/sec on abs
  • C examples: compile and run
  • Fortran compilation: works
  • shellcheck: native aarch64, lints correctly

Additional fix: shellcheck.mk

The shellcheck.mk makefile hardcoded linux.x86_64 for all Linux platforms. Since shellcheck v0.8.0 (the pinned version) ships a linux.aarch64 tarball, this PR adds the ARM64 URL branch and the corresponding checksum.

@anandkaranubc

Copy link
Copy Markdown
Contributor Author

Follow-up: darwin arm64 support for make install-deps-shellcheck

The upstream issue for darwin arm64 shellcheck binaries (koalaman/shellcheck#2714) is now closed. Shellcheck ships darwin.aarch64 tarballs starting from v0.10.0.

This means the following lines are outdated and can be fixed in a follow-up PR:

1. The TODO on line 26 says darwin arm64 binaries are not provided. They are now.

	# TODO: add darwin.aarch64 support. The upstream issue (https://github.com/koalaman/shellcheck/issues/2714) is now resolved and darwin.aarch64 binaries are available. This requires bumping the pinned version, adding the darwin.aarch64 URL branch, and adding the corresponding checksum.

2. The darwin URL on line 27 hardcodes x86_64. A follow-up would add an arch branch similar to what this PR does for linux on lines 29-33.

3. The FIXME on line 55 falls back to system shellcheck on darwin arm64. Once the URL is fixed, this workaround can be removed.

The follow-up would need to bump DEPS_SHELLCHECK_VERSION from 0.8.0 (which does not have darwin.aarch64) to at least 0.10.0, add the darwin.aarch64 URL branch, compute the checksum, and add it under deps/checksums/.

@anandkaranubc anandkaranubc added Bug Something isn't working. METR Pull request associated with the METR project. labels Aug 7, 2026

@kgryte kgryte left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. Let's go ahead and get this in so that folks can test it out.

@kgryte
kgryte merged commit 4982836 into stdlib-js:develop Aug 8, 2026
66 checks passed
@kgryte kgryte removed the Needs Review A pull request which needs code review. label Aug 8, 2026
@anandkaranubc
anandkaranubc deleted the build/devcontainer-arm64-support branch August 8, 2026 03:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something isn't working. METR Pull request associated with the METR project.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[RFC]: add devcontainer support for arm64 architectures

3 participants