Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Manifest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ bundler/lib/bundler/templates/newgem/bin/setup.tt
bundler/lib/bundler/templates/newgem/circleci/config.yml.tt
bundler/lib/bundler/templates/newgem/exe/newgem.tt
bundler/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt
bundler/lib/bundler/templates/newgem/ext/newgem/build.rs.tt
bundler/lib/bundler/templates/newgem/ext/newgem/extconf-c.rb.tt
bundler/lib/bundler/templates/newgem/ext/newgem/extconf-go.rb.tt
bundler/lib/bundler/templates/newgem/ext/newgem/extconf-rust.rb.tt
Expand All @@ -229,6 +230,7 @@ bundler/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt
bundler/lib/bundler/templates/newgem/ext/newgem/newgem.go.tt
bundler/lib/bundler/templates/newgem/ext/newgem/newgem.h.tt
bundler/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt
bundler/lib/bundler/templates/newgem/github/workflows/build-gems.yml.tt
bundler/lib/bundler/templates/newgem/github/workflows/main.yml.tt
bundler/lib/bundler/templates/newgem/gitignore.tt
bundler/lib/bundler/templates/newgem/gitlab-ci.yml.tt
Expand Down
4 changes: 4 additions & 0 deletions bundler/lib/bundler/cli/gem.rb
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ def run
case config[:ci]
when "github"
templates.merge!("github/workflows/main.yml.tt" => ".github/workflows/main.yml")
if extension == "rust"
templates.merge!("github/workflows/build-gems.yml.tt" => ".github/workflows/build-gems.yml")
end
config[:ignore_paths] << ".github/"
when "gitlab"
templates.merge!("gitlab-ci.yml.tt" => ".gitlab-ci.yml")
Expand Down Expand Up @@ -228,6 +231,7 @@ def run
templates.merge!(
"Cargo.toml.tt" => "Cargo.toml",
"ext/newgem/Cargo.toml.tt" => "ext/#{name}/Cargo.toml",
"ext/newgem/build.rs.tt" => "ext/#{name}/build.rs",
"ext/newgem/extconf-rust.rb.tt" => "ext/#{name}/extconf.rb",
"ext/newgem/src/lib.rs.tt" => "ext/#{name}/src/lib.rs",
)
Expand Down
6 changes: 6 additions & 0 deletions bundler/lib/bundler/templates/newgem/Cargo.toml.tt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@
[workspace]
members = ["./ext/<%= config[:name] %>"]
resolver = "2"

[profile.release]
# By default, debug symbols are stripped from the final binary which makes it
# harder to debug if something goes wrong. It's recommended to keep debug
# symbols in the release build so that you can debug the final binary if needed.
debug = true
7 changes: 7 additions & 0 deletions bundler/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@ crate-type = ["cdylib"]

[dependencies]
magnus = { version = "0.8.2" }
rb-sys = { version = "0.9", features = ["stable-api-compiled-fallback"] }

[build-dependencies]
rb-sys-env = "0.2.2"

[dev-dependencies]
rb-sys-test-helpers = { version = "0.2.2" }
5 changes: 5 additions & 0 deletions bundler/lib/bundler/templates/newgem/ext/newgem/build.rs.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
let _ = rb_sys_env::activate()?;

Ok(())
}
15 changes: 13 additions & 2 deletions bundler/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use magnus::{function, prelude::*, Error, Ruby};

fn hello(subject: String) -> String {
format!("Hello from Rust, {subject}!")
pub fn hello(subject: String) -> String {
format!("Hello {subject}, from Rust!")
}

#[magnus::init]
Expand All @@ -10,3 +10,14 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
module.define_singleton_method("hello", function!(hello, 1))?;
Ok(())
}

#[cfg(test)]
mod tests {
use rb_sys_test_helpers::ruby_test;
use super::hello;

#[ruby_test]
fn test_hello() {
assert_eq!("Hello world, from Rust!", hello("world".to_string()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
name: Build gems

on:
push:
tags:
- "v*"
- "cross-gem/*"
workflow_dispatch:

jobs:
ci-data:
runs-on: ubuntu-latest
outputs:
result: ${{ steps.fetch.outputs.result }}
steps:
- uses: oxidize-rb/actions/fetch-ci-data@v1
id: fetch
with:
supported-ruby-platforms: |
exclude: ["arm-linux", "x64-mingw32"]
stable-ruby-versions: |
exclude: ["head"]

source-gem:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

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

- name: Build gem
run: bundle exec rake build

- uses: actions/upload-artifact@v3
Copy link

Choose a reason for hiding this comment

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

with:
name: source-gem
path: pkg/*.gem

cross-gem:
name: Compile native gem for ${{ matrix.platform }}
runs-on: ubuntu-latest
needs: ci-data
strategy:
matrix:
platform: ${{ fromJSON(needs.ci-data.outputs.result).supported-ruby-platforms }}
steps:
- uses: actions/checkout@v4

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

- uses: oxidize-rb/actions/cross-gem@v1
id: cross-gem
with:
platform: ${{ matrix.platform }}
ruby-versions: ${{ join(fromJSON(needs.ci-data.outputs.result).stable-ruby-versions, ',') }}

- uses: actions/upload-artifact@v3
with:
name: cross-gem
path: ${{ steps.cross-gem.outputs.gem-path }}
9 changes: 8 additions & 1 deletion bundler/lib/bundler/templates/newgem/lib/newgem.rb.tt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@

require_relative "<%= File.basename(config[:namespaced_path]) %>/version"
<%- if config[:ext] -%>
require_relative "<%= File.basename(config[:namespaced_path]) %>/<%= config[:underscored_name] %>"
# Attempt to load a versioned extension based on the Ruby version.
Copy link
Member

Choose a reason for hiding this comment

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

Can you remove these changes from template? That workaround is only for fat gem, not generally usage.

# Fall back to loading the non-versioned extension if version-specific loading fails.
begin
RUBY_VERSION =~ /(\d+\.\d+)/
require "#{Regexp.last_match(1)}/<%= File.basename(config[:namespaced_path]) %>/<%= config[:underscored_name] %>"
rescue LoadError
require "<%= File.basename(config[:namespaced_path]) %>/<%= config[:underscored_name] %>"
end
<%- end -%>

<%- config[:constant_array].each_with_index do |c, i| -%>
Expand Down
4 changes: 0 additions & 4 deletions bundler/lib/bundler/templates/newgem/newgem.gemspec.tt
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ Gem::Specification.new do |spec|
spec.license = "MIT"
<%- end -%>
spec.required_ruby_version = ">= <%= config[:required_ruby_version] %>"
<%- if config[:ext] == 'rust' -%>
spec.required_rubygems_version = ">= <%= config[:rust_builder_required_rubygems_version] %>"
<%- end -%>

spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = "<%= config[:source_code_uri] %>"
Expand Down
8 changes: 8 additions & 0 deletions bundler/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ RSpec.describe <%= config[:constant_name] %> do
expect(<%= config[:constant_name] %>::VERSION).not_to be nil
end

<%- if config[:ext] == 'rust' -%>
it "can call into Rust" do
result = <%= config[:constant_name] %>.hello("world")

expect(result).to be("Hello earth, from Rust!")
Copy link

Choose a reason for hiding this comment

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

This needs eq as these will be different objects I think

     Failure/Error: expect(result).to be("Hello earth, from Rust!")
     
       expected #<String:1640> => "Hello earth, from Rust!"
            got #<String:1648> => "Hello earth, from Rust!"
     
       Compared using equal?, which compares object identity,
       but expected and actual are not the same object. Use
       `expect(actual).to eq(expected)` if you don't care about
       object identity in this example.
Suggested change
expect(result).to be("Hello earth, from Rust!")
expect(result).to eq("Hello earth, from Rust!")
Suggested change
expect(result).to be("Hello earth, from Rust!")
expect(result).to eq("Hello world, from Rust!")

end
<%- else -%>
it "does something useful" do
expect(false).to eq(true)
end
<%- end -%>
end
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ class <%= config[:minitest_constant_name] %> < Minitest::Test
refute_nil ::<%= config[:constant_name] %>::VERSION
end

<%- if config[:ext] == 'rust' -%>
def test_hello_world
assert_equal "Hello earth, from Rust!", <%= config[:constant_name] %>.hello("world")
end
<%- else -%>
def test_it_does_something_useful
assert false
end
<%- end -%>
end
29 changes: 26 additions & 3 deletions bundler/spec/commands/newgem_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1726,12 +1726,11 @@ def create_temporary_dir(dir)
expect(bundled_app("#{gem_name}/ext/#{gem_name}/Cargo.toml")).to exist
expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb")).to exist
expect(bundled_app("#{gem_name}/ext/#{gem_name}/src/lib.rs")).to exist
expect(bundled_app("#{gem_name}/ext/#{gem_name}/build.rs")).to exist
end

it "includes rake-compiler, rb_sys gems and required_rubygems_version constraint" do
it "includes rake-compiler constraint" do
expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rake-compiler"')
expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include('spec.add_dependency "rb_sys"')
Copy link
Member

Choose a reason for hiding this comment

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

Why did you remove this?

expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include('spec.required_rubygems_version = ">= ')
end

it "depends on compile task for build" do
Expand All @@ -1754,6 +1753,30 @@ def create_temporary_dir(dir)

expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
end

it "configures the crate such that `cargo test` works", :ruby_repo, :mri_only do
env = setup_rust_env
gem_path = bundled_app(gem_name)
result = sys_exec("cargo test", env: env, dir: gem_path)

expect(result).to include("1 passed")
end

def setup_rust_env
skip "rust toolchain of mingw is broken" if RUBY_PLATFORM.match?("mingw")

env = {
"CARGO_HOME" => ENV.fetch("CARGO_HOME", File.join(ENV["HOME"], ".cargo")),
"RUSTUP_HOME" => ENV.fetch("RUSTUP_HOME", File.join(ENV["HOME"], ".rustup")),
"RUSTUP_TOOLCHAIN" => ENV.fetch("RUSTUP_TOOLCHAIN", "stable"),
}

system(env, "cargo", "-V", out: IO::NULL, err: [:child, :out])
skip "cargo not present" unless $?.success?
# Hermetic Cargo setup
RbConfig::CONFIG.each {|k, v| env["RBCONFIG_#{k}"] = v }
env
end
end

context "--ext parameter set with go" do
Expand Down
1 change: 1 addition & 0 deletions bundler/spec/support/filters.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def inspect
config.filter_run_excluding jruby_only: RUBY_ENGINE != "jruby"
config.filter_run_excluding truffleruby_only: RUBY_ENGINE != "truffleruby"
config.filter_run_excluding man: Gem.win_platform?
config.filter_run_excluding mri_only: RUBY_ENGINE != "ruby"

config.filter_run_when_matching :focus unless ENV["CI"]

Expand Down