CPUs and Operating Systems
I have been building a tool called Miruri.
The basic idea is simple:
Give it an existing software project and a target platform, and let it handle as much of the porting process as possible.
Repository:
https://github.com/yuna-r/miruri
It is still an alpha project, but it has reached the point where it can already do some surprisingly non-trivial things.
For example, I have used it to:
- port Intel SSE code to ARM NEON
- build Linux ARM64 binaries from an Apple Silicon Mac
- build an Autotools terminal application for macOS
- take a Linux/GNOME Python + GTK application and make it run as a macOS
.app
That last one surprised me too.
What Miruri is trying to solve
Cross-compiling a clean, portable C project is usually not that difficult.
Real projects are different.
They contain things like:
x86 intrinsics
Windows APIs
Linux-specific APIs
architecture-specific compiler flags
platform GUI frameworks
graphics APIs
audio backends
plugins
generated files
custom build scripts
external SDK assumptions
A compiler can tell you that something failed.
It usually cannot tell you how the architecture of the project should change to make another platform a first-class target.
Miruri tries to operate at that level.
Instead of thinking only in terms of:
source file
→ compiler
→ binary
it treats porting more like:
existing project
↓
analyze platform assumptions
↓
construct target requirements
↓
choose a migration strategy
↓
modify an isolated copy
↓
build
↓
inspect artifacts
Basic usage
Miruri itself is written in Go.
Building it is intentionally simple:
git clone https://github.com/yuna-r/miruri.git
cd miruri
go build -o bin/miruri ./cmd/miruri
Check the local environment:
./bin/miruri doctor
List targets:
./bin/miruri targets
Then build a project for a target:
./bin/miruri build \
--target macos-arm64 \
path/to/project
For projects that actually require source changes:
./bin/miruri port \
--target macos-arm64 \
path/to/project
The port command allows broader source and build-system modifications.
It does not edit the original repository
One design decision I made early was that Miruri should not directly mutate the source repository being ported.
The flow is closer to:
original repository
↓
isolated working copy
↓
analysis and modifications
↓
build
↓
artifact collection
This matters a lot once automated repair is involved.
Compiler output, generated files, build caches, object files, and experimental changes should not leak back into the original source tree.
Accepted source changes can instead be represented as a patch.
AI is a repair engine, not the source of truth
Miruri can use Codex CLI for source-level repair.
But I did not want the system to become:
paste compiler error into AI
→ trust whatever comes back
The intended relationship is:
AI proposes changes
compiler validates syntax and ABI assumptions
linker validates symbol resolution
artifact inspection validates output architecture
A typical flow is:
analyze
↓
build
↓
failure
↓
extract diagnostics
↓
repair isolated source
↓
build again
↓
inspect artifact
For larger platform ports, the repair may involve more than changing a broken line.
It can add:
- platform backends
- compatibility code
- conditional build logic
- new entry points
- GUI adapters
- resource definitions
Linux sysroots are automatically provisioned
One annoying part of cross-compilation is building the target sysroot.
For example, compiling Linux ARM64 software from macOS may require:
headers
libc
crt objects
libgcc runtime
target libraries
multiarch paths
Miruri can provision this automatically.
For example:
./bin/miruri build \
--target linux-arm64 \
path/to/project
does not require manually supplying --sysroot.
Under the hood, Miruri can retrieve a matching OCI root filesystem and use it as build data.
It does not execute the container image.
The process is roughly:
OCI manifest
↓
select architecture
↓
download layers
↓
verify SHA-256 digests
↓
extract root filesystem
↓
validate toolchain/runtime files
↓
use as Clang sysroot
No Docker daemon or QEMU is required for this part.
The result is cached by content digest.
You can also prefetch it:
./bin/miruri sysroot ensure \
--target linux-arm64
and inspect cached sysroots:
./bin/miruri sysroot list
Offline builds can reuse them:
./bin/miruri build \
--target linux-arm64 \
--offline \
path/to/project
Build-system detection
Real-world OSS immediately forced me to support more than CMake.
Miruri currently understands:
CMake
Meson
Autotools
Make
Examples:
CMakeLists.txt
→ CMake
meson.build
→ Meson
configure.ac / configure.in
→ Autotools
For an Autotools Git checkout without a generated configure script, the flow can become:
configure.ac
↓
autoreconf
↓
configure
↓
make
Meson can also be managed by Miruri when it is missing from the host.
This grew organically from testing actual open-source projects.
Real-world test 1: nudoku
One project I tested was nudoku, a terminal Sudoku application.
It uses Autotools.
At first Miruri simply stopped with:
detected: autotools
no supported build system
So I added Autotools support.
After that, the same project could be built for Apple Silicon macOS.
That was the first point where Miruri started to feel less like a cross-compiler wrapper and more like a porting system.
Real-world test 2: a Linux GNOME GUI application on macOS
Then I tried something much more unreasonable.
I used Drawing, a Python + GTK drawing application designed for the GNOME/Linux desktop.
I ran:
./bin/miruri port \
--target macos-arm64 \
--codex-mode port \
~/src/drawing
This exposed one missing feature after another.
The process looked roughly like this:
Meson project
↓
Miruri did not support Meson
↓
add Meson adapter
Meson missing on host
↓
add managed Meson runtime
project produces no native executable
↓
support interpreted install artifacts
Linux install tree is not a macOS app
↓
add .app packaging
Linux Python launcher assumptions fail on macOS
↓
add compatibility rewrites
PyGObject runtime not visible
↓
resolve Homebrew Python runtime
bundle entry point shadows Python package
↓
fix package resolution
GTK GSettings schemas not visible
↓
adjust macOS GTK runtime environment
Eventually the Linux application opened on macOS as a real GUI application.
The GTK interface rendered correctly.
I could use:
- menus
- canvas drawing
- text tools
- fonts
- checkboxes
- radio buttons
- Japanese UI
- mouse interaction
Seeing a GNOME-oriented application show up as a macOS window after going through that pipeline was a fun moment.
Intel SSE to ARM NEON
Another experiment involved an intentionally x86-only C project.
The original source directly used:
#include <xmmintrin.h>
and operations such as:
_mm_loadu_ps
_mm_add_ps
_mm_mul_ps
_mm_shuffle_ps
_mm_storeu_ps
Miruri identified the architecture dependency and the resulting code gained separate backends:
#if defined(__SSE__)
/* x86 */
#elif defined(__aarch64__)
/* ARM NEON */
#else
/* scalar */
#endif
For example:
_mm_add_ps(...)
became:
vaddq_f32(...)
and:
_mm_loadu_ps(...)
became:
vld1q_f32(...)
The same source tree could then produce:
Linux ARM64
→ ELF AArch64
macOS ARM64
→ Mach-O arm64
Compiler flags such as -msse also had to be made architecture-aware.
This is exactly the kind of issue Miruri is intended to find: architecture assumptions exist outside the C file too.
Project Graph and Target Contract
Internally, I am moving toward representing a project as capabilities rather than just files.
Conceptually:
Existing Project
│
▼
Project Graph
│
├── sources
├── build steps
├── resources
├── plugins
├── shaders
└── platform capabilities
│
▼
Target Contract
│
▼
Strategy Planner
│
├── native rebuild
├── source rewrite
├── compatibility layer
├── generated adapter
└── unresolved blocker
│
▼
Artifact Builder
For example:
#include <xmmintrin.h>
can be classified as a capability such as:
cpu.x86.intrinsics
A Windows-only GUI dependency could similarly become:
platform.windows.gui
This makes it possible to reason about a port at a higher level than individual compiler errors.
Artifact inspection matters
A successful compiler exit code is not enough.
Miruri inspects outputs such as:
ELF
Mach-O
PE
static archives
and verifies their architecture.
If the target is:
macOS ARM64
and the resulting Mach-O is actually x86_64, that should not be reported as a successful port.
Build results can also include metadata such as:
analysis.json
plan.json
build.log
manifest.json
sysroot.lock.json
so that the generated artifact has some provenance.
Cross-target artifacts are not automatically executed
Miruri currently avoids automatically running foreign target binaries during the porting process.
For example, when creating an ARM64 Linux ELF on macOS, it stops at:
compile
↓
link
↓
artifact inspection
instead of automatically launching it under an emulator.
That is intentional.
Once automatic execution is introduced, results can become dependent on:
- emulator behavior
- kernel behavior
- drivers
- graphics stack
- audio stack
- target services
I currently prefer to keep artifact production and target-runtime validation as separate stages.
What Miruri still cannot do
This is very much an alpha project.
There are many hard problems left.
Some examples:
Direct3D ↔ Vulkan ↔ Metal
shader translation
complex proprietary SDKs
driver dependencies
audio backend translation
full dependency resolution
semantic equivalence testing
bit-exact SIMD verification
production-quality standalone GUI packaging
Automatically making something compile is much easier than proving that the new implementation behaves exactly like the old one.
That is especially true for numerical and SIMD-heavy software.
The development process has been surprisingly fun
One thing I have enjoyed is testing Miruri with real open-source projects instead of only synthetic fixtures.
A fixture tends to fail in the way you expected.
Real software fails in ways you did not think about.
The development history has looked something like:
CMake works
↓
try real Autotools project
↓
add Autotools
try Meson project
↓
add Meson
try interpreted GUI app
↓
add staged install artifacts
try Linux GTK application on macOS
↓
add macOS application packaging
try x86 intrinsics
↓
add architecture-level porting logic
Each project reveals another assumption hidden inside the tool itself.
That has probably been the most useful form of testing.
Where I want to take it
Some areas I want to explore next:
SSE / AVX / AVX2 / AVX-512
→ NEON / SVE / SVE2
x86
→ RISC-V
x86
→ POWER
Windows GUI
→ macOS / Linux GUI
Direct3D
→ Vulkan / Metal
I am also interested in making semantic validation a much larger part of the system.
The hard part of automated porting is not generating different code.
The hard part is establishing that the different code still means the same thing.
Summary
Miruri is an experiment in automating software porting across CPUs and operating systems.
Repository:
https://github.com/yuna-r/miruri
At the moment it has infrastructure for:
CMake
Meson
Autotools
Make
Linux
macOS
Windows
x86_64
ARM64
RISC-V
POWER
and I have already used it for experiments including:
Intel SSE
→ ARM NEON
Autotools application
→ Apple Silicon macOS
and the slightly ridiculous one:
Linux/GNOME Python + GTK application
→ macOS .app
→ working GUI
There is still a lot to build, but the main question behind the project is simple:
How much of real software porting can we turn into a repeatable automated process?
That is what I am trying to find out.
Top comments (0)