An open API service indexing awesome lists of open source software.

https://github.com/marcoroth/rake-cross-compiler

Cross-compile Ruby native extensions using Zig as a drop-in cross-compiler.
https://github.com/marcoroth/rake-cross-compiler

Last synced: 6 months ago
JSON representation

Cross-compile Ruby native extensions using Zig as a drop-in cross-compiler.

Awesome Lists containing this project

README

          

# rake-cross-compiler

Cross-compile Ruby native extensions using [Zig](https://ziglang.org/) as a drop-in C/C++ cross-compiler.

A lightweight alternative to [rake-compiler-dock](https://github.com/rake-compiler/rake-compiler-dock) that doesn't require Docker.

## Why?

Building native Ruby gems for multiple platforms traditionally requires:
- Docker containers with pre-configured cross-compilation toolchains
- Heavy disk usage and slow container startup times
- Complex setup for each target platform

**rake-cross-compiler** uses Zig's built-in cross-compilation capabilities instead:
- No Docker required
- Single `zig` binary handles all targets
- Cross-compile from any OS to Linux, macOS, and Windows
- Vendored Ruby headers for all supported platforms and Ruby versions

## Installation

Install the gem:

```bash
gem install rake-cross-compiler
```

Or add to your Gemfile:

```ruby
gem "rake-cross-compiler"
```

You'll also need [Zig](https://ziglang.org/download/) installed (version 0.13.0 or later). If Zig is not installed, rake-cross-compiler can auto-download it.

## Usage

### Basic Usage

```ruby
require "rake_cross_compiler"
```

**Cross-compile for Linux x86_64**
```ruby
Rake::Cross::Compiler.sh("bundle && rake compile", platform: "x86_64-linux")
```

**Or use the convenience alias**
```ruby
RakeCrossCompiler.sh("bundle && rake compile", platform: "aarch64-linux")
```

### Multiple Platforms

```ruby
RakeCrossCompiler.sh("rake native gem", platform: "x86_64-linux aarch64-linux arm64-darwin")
```

### In a Rakefile (Recommended)

The easiest integration with rake-compiler in `Rakefile`:

```ruby
require "rake/extensiontask"
require "rake_cross_compiler"

PLATFORMS = %w[
x86_64-linux-gnu
aarch64-linux-gnu
x86_64-linux-musl
aarch64-linux-musl
arm64-darwin
x86_64-darwin
].freeze

Rake::ExtensionTask.new("my_extension") do |ext|
ext.cross_compile = true
ext.cross_platform = PLATFORMS
end

namespace :gem do
task :prepare do
spec = Gem::Specification.load("my_gem.gemspec")

# Set RUBY_CC_VERSION based on gemspec and available platforms
Rake::Cross::Compiler.set_ruby_cc_version(spec.required_ruby_version, platforms: PLATFORMS)

# Set up cross-Ruby configurations for rake-compiler
Rake::Cross::Compiler.setup_cross_ruby(platforms: PLATFORMS)
end

PLATFORMS.each do |platform|
desc "Build native gem for #{platform}"
task platform => :prepare do
Rake::Cross::Compiler.sh(
"bundle --local && rake native:#{platform} gem",
platform: platform
)
end
end

desc "Build all native gems"
task :native => PLATFORMS
end
```

Then build gems:

```bash
bundle exec rake gem:prepare # Set up cross-ruby configs
bundle exec rake gem:x86_64-linux-gnu # Build for one platform
bundle exec rake gem:native # Build for all platforms
```

### Error Handling

```ruby
RakeCrossCompiler.sh("rake compile", platform: "x86_64-linux") do |ok, status|
if ok
puts "Build succeeded!"
else
puts "Build failed with status: #{status.exitstatus}"
end
end
```

### Getting Environment Variables

For manual integration or debugging:

```ruby
env = RakeCrossCompiler.env_for("x86_64-linux")
# => {
# "CC" => "zig cc -target x86_64-linux-gnu",
# "CXX" => "zig c++ -target x86_64-linux-gnu",
# "AR" => "zig ar",
# "RANLIB" => "zig ranlib",
# ...
# }
```

### With Extra Compiler Flags

```ruby
RakeCrossCompiler.sh(
"rake compile",
platform: "x86_64-linux",
cflags: ["-O2", "-Wall"],
ldflags: ["-lm"]
)
```

## Supported Platforms

| Ruby Platform | Zig Target |
|---------------|------------|
| `x86_64-linux` | `x86_64-linux-gnu` |
| `x86_64-linux-gnu` | `x86_64-linux-gnu` |
| `x86_64-linux-musl` | `x86_64-linux-musl` |
| `aarch64-linux` | `aarch64-linux-gnu` |
| `aarch64-linux-gnu` | `aarch64-linux-gnu` |
| `aarch64-linux-musl` | `aarch64-linux-musl` |
| `x86-linux` | `x86-linux-gnu` |
| `x86-linux-gnu` | `x86-linux-gnu` |
| `x86-linux-musl` | `x86-linux-musl` |
| `arm-linux` | `arm-linux-gnueabihf` |
| `arm-linux-gnu` | `arm-linux-gnueabihf` |
| `arm-linux-musl` | `arm-linux-musleabihf` |
| `x86_64-darwin` | `x86_64-macos` |
| `arm64-darwin` | `aarch64-macos` |
| `x64-mingw32` | `x86_64-windows-gnu` |
| `x64-mingw-ucrt` | `x86_64-windows-gnu` |
| `x86-mingw32` | `x86-windows-gnu` |

### Supported Ruby Versions

Vendored headers are available for:
- Ruby 3.0.0, 3.1.0, 3.2.0, 3.3.0, 3.4.0, 4.0.0

Note: Not all Ruby versions are available for all platforms. For example, `arm64-darwin` only has Ruby 3.1.0+. The gem automatically detects which versions are available for your target platforms.

## API Reference

### `Rake::Cross::Compiler.sh(cmd, **options)`

Run a shell command with cross-compilation environment.

```ruby
Rake::Cross::Compiler.sh(
"rake compile",
platform: "x86_64-linux",
verbose: true,
cflags: ["-O2"],
ldflags: ["-lm"],
env: { "DEBUG" => "1" },
ruby_cc_version: "3.3.0:3.4.0",
check_zig: true
)
```

Options:
- `platform:` - Target platform(s), space-separated (default: `"x86_64-linux"`)
- `verbose:` - Print command info (default: follows Rake's verbose flag)
- `cflags:` - Additional CFLAGS as array
- `ldflags:` - Additional LDFLAGS as array
- `env:` - Additional environment variables as hash
- `ruby_cc_version:` - RUBY_CC_VERSION value
- `check_zig:` - Verify Zig installation (default: `true`)

### `Rake::Cross::Compiler.exec(*args, **options)`

Run a command directly (without shell) with cross-compilation environment. Same options as `sh`.

### `Rake::Cross::Compiler.env_for(platform, cflags: [], ldflags: [])`

Get environment variables hash for a platform.

### `Rake::Cross::Compiler.setup_cross_ruby(platforms:, ruby_versions: nil)`

Set up cross-Ruby configurations for rake-compiler. Creates `rbconfig.rb` stubs in `~/.rake-compiler`.

```ruby
Rake::Cross::Compiler.setup_cross_ruby(
platforms: %w[x86_64-linux-gnu arm64-darwin],
ruby_versions: %w[3.3.0 3.4.0] # optional, auto-detected if nil
)
```

### `Rake::Cross::Compiler.ruby_cc_version(*requirements, platforms: nil)`

Get a RUBY_CC_VERSION string matching the given requirements.

```ruby
Rake::Cross::Compiler.ruby_cc_version(">= 3.1.0")
# => "4.0.0:3.4.0:3.3.0:3.2.0:3.1.0"

spec = Gem::Specification.load("my.gemspec")
Rake::Cross::Compiler.ruby_cc_version(spec.required_ruby_version, platforms: PLATFORMS)
# => "4.0.0:3.4.0:3.3.0:3.2.0:3.1.0"
```

### `Rake::Cross::Compiler.set_ruby_cc_version(*requirements, platforms: nil)`

Set the `RUBY_CC_VERSION` environment variable.

```ruby
Rake::Cross::Compiler.set_ruby_cc_version(">= 3.1.0", platforms: PLATFORMS)
```

### `Rake::Cross::Compiler.available_ruby_versions(platforms: nil, common_only: false)`

Get available Ruby versions from vendored headers.

```ruby
Rake::Cross::Compiler.available_ruby_versions
# => ["3.0.0", "3.1.0", "3.2.0", "3.3.0", "3.4.0", "4.0.0"]

# Only versions available on ALL platforms
Rake::Cross::Compiler.available_ruby_versions(platforms: PLATFORMS, common_only: true)
# => ["3.1.0", "3.2.0", "3.3.0", "3.4.0", "4.0.0"]
```

### `Rake::Cross::Compiler.supported_platforms`

List all supported Ruby platforms.

### `Rake::Cross::Compiler.zig_target_for(ruby_platform)`

Get the Zig target triple for a Ruby platform name.

### `Rake::Cross::Compiler.zig_available?`

Check if Zig is installed.

### `Rake::Cross::Compiler.zig_version`

Get the installed Zig version.

## GitHub Actions

Example workflow for building native gems:

```yaml
name: Build Gems

on:
release:
types: [published]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.4'
bundler-cache: true

- uses: mlugg/setup-zig@v1
with:
version: 0.13.0

- name: Setup Cross-Ruby Configs
run: bundle exec rake gem:prepare

- name: Build source gem
run: bundle exec rake build

- name: Build native gems
run: bundle exec rake gem:native

- uses: actions/upload-artifact@v4
with:
name: gems
path: pkg/*.gem
```

## Environment Variables

- `ZIG` - Path to Zig executable (default: `zig`)
- `RAKE_CROSS_COMPILER_PLATFORM` - Default target platform
- `RUBY_CC_VERSION` - Ruby versions for cross-compilation (e.g., `3.3.0:3.4.0`)

## Limitations

1. **Platform-specific code** - Extensions with platform-specific assembly or intrinsics may need additional work.

2. **Windows targets** - Windows cross-compilation is supported but less tested than Linux/macOS.

## Development

```bash
bundle install
bundle exec rake test
bundle exec steep check
```

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/marcoroth/rake-cross-compiler.

## License

The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).

## See Also

- [Zig](https://ziglang.org/) - The compiler powering cross-compilation
- [rake-compiler](https://github.com/rake-compiler/rake-compiler) - Build native extensions
- [rake-compiler-dock](https://github.com/rake-compiler/rake-compiler-dock) - Docker-based cross-compilation