diff --git a/.github/labeler.yml b/.github/labeler.yml index 3846a6b5..e1658383 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -9,7 +9,3 @@ ruby: ruby_h_to_go: - changed-files: - any-glob-to-any-file: "_tools/ruby_h_to_go/**/*" - -patch_for_go_gem: - - changed-files: - - any-glob-to-any-file: "_tools/patch_for_go_gem/**/*" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e3bbb026..99c59880 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,6 @@ on: - ".github/pages.yml" - ".github/release.yml" - ".github/workflows/labeler.yml" - - ".github/workflows/patch_for_go_gem.yml" - ".github/workflows/rbs-collection-updater.yml" - "_benchmark/**" diff --git a/.github/workflows/patch_for_go_gem.yml b/.github/workflows/patch_for_go_gem.yml deleted file mode 100644 index 294b49f1..00000000 --- a/.github/workflows/patch_for_go_gem.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: patch_for_go_gem - -on: - push: - branches: - - main - pull_request: - types: - - opened - - synchronize - - reopened - paths: - - ".github/workflows/patch_for_go_gem.yml" - - "_tools/patch_for_go_gem/**" - - "Gemfile" - - "Gemfile.lock" - -defaults: - run: - working-directory: _tools/patch_for_go_gem/ - -jobs: - generate-matrix: - runs-on: ubuntu-latest - - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} - - steps: - - uses: actions/checkout@v5 - - - id: set-matrix - run: echo "matrix=$(cat matrix.json | jq -c)" >> $GITHUB_OUTPUT - working-directory: .github/workflows/ - - test: - name: "test (Go ${{ matrix.go }}, Ruby ${{ matrix.ruby }})" - - needs: - - generate-matrix - - runs-on: ubuntu-latest - - strategy: - fail-fast: false - - matrix: ${{ fromJSON(needs.generate-matrix.outputs.matrix) }} - - steps: - - uses: actions/checkout@v5 - - - uses: actions/setup-go@v6 - with: - go-version: ${{ matrix.go }} - - - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - bundler-cache: true - - - run: bundle exec rspec - - - name: Slack Notification (not success) - uses: act10ns/slack@v2 - if: "! success()" - continue-on-error: true - with: - status: ${{ job.status }} - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - matrix: ${{ toJson(matrix) }} - - notify: - needs: - - test - - runs-on: ubuntu-latest - - steps: - - name: Slack Notification (success) - uses: act10ns/slack@v2 - if: always() - continue-on-error: true - with: - status: ${{ job.status }} - webhook-url: ${{ secrets.SLACK_WEBHOOK }} diff --git a/README.md b/README.md index 0560eb33..b0a08902 100644 --- a/README.md +++ b/README.md @@ -23,20 +23,11 @@ See [.github/workflows/matrix.json](.github/workflows/matrix.json) for details ## Getting started -At first, patch to make a gem into a Go gem right after `bundle gem` +Run followings -See [_tools/patch_for_go_gem/](_tools/patch_for_go_gem/) - -Please also add the following depending on the CI you are using. - -### GitHub Actions -e.g. - -```yml -- uses: actions/setup-go@v5 - with: - go-version-file: ext/GEM_NAME/go.mod -``` +1. `bundle gem --ext=go` **(Requires bundler v4.0.0.beta1+)** +2. `bundle install` +3. `bundle exec rake go:mod_tidy` ## Implementing Ruby methods in Go For example, consider the following Ruby method implemented in Go diff --git a/Rakefile b/Rakefile index ddaf280b..36c90aa2 100644 --- a/Rakefile +++ b/Rakefile @@ -25,6 +25,6 @@ task :changelog, [:before, :after] do |_, params| sh "ruby _tools/changelog_generator/changelog_generator.rb #{args.join(" ")}" end -task build_all: %w[ruby:build_all go:build_all go_gem:test ruby_h_to_go:test patch_for_go_gem:test] +task build_all: %w[ruby:build_all go:build_all go_gem:test ruby_h_to_go:test] task default: :build_all diff --git a/_gem/README.md b/_gem/README.md index 6bc26503..0291b699 100644 --- a/_gem/README.md +++ b/_gem/README.md @@ -10,10 +10,6 @@ Add below. spec.add_dependency "go_gem" ``` -See below for details. - -[/_tools/patch_for_go_gem/](/_tools/patch_for_go_gem/) - ## Usage ### [`create_go_makefile`](https://ruby-go-gem.github.io/go-gem-wrapper/GoGem/Mkmf.html#create_go_makefile-instance_method) `create_go_makefile` is an extension of `create_makefile`. diff --git a/_tasks/patch_for_go_gem.rake b/_tasks/patch_for_go_gem.rake deleted file mode 100644 index 6f3382db..00000000 --- a/_tasks/patch_for_go_gem.rake +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -namespace :patch_for_go_gem do - desc "Run _tools/patch_for_go_gem test" - task :test do - Dir.chdir(File.join(repo_root, "_tools", "patch_for_go_gem")) do - sh "rspec" - end - end -end diff --git a/_tools/patch_for_go_gem/.gitignore b/_tools/patch_for_go_gem/.gitignore deleted file mode 100644 index e3200e0f..00000000 --- a/_tools/patch_for_go_gem/.gitignore +++ /dev/null @@ -1,56 +0,0 @@ -*.gem -*.rbc -/.config -/coverage/ -/InstalledFiles -/pkg/ -/spec/reports/ -/spec/examples.txt -/test/tmp/ -/test/version_tmp/ -/tmp/ - -# Used by dotenv library to load environment variables. -# .env - -# Ignore Byebug command history file. -.byebug_history - -## Specific to RubyMotion: -.dat* -.repl_history -build/ -*.bridgesupport -build-iPhoneOS/ -build-iPhoneSimulator/ - -## Specific to RubyMotion (use of CocoaPods): -# -# We recommend against adding the Pods directory to your .gitignore. However -# you should judge for yourself, the pros and cons are mentioned at: -# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control -# -# vendor/Pods/ - -## Documentation cache and generated files: -/.yardoc/ -/_yardoc/ -/doc/ -/rdoc/ - -## Environment normalization: -/.bundle/ -/vendor/bundle -/lib/bundler/man/ - -# for a library or gem, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# Gemfile.lock -# .ruby-version -# .ruby-gemset - -# unless supporting rvm < 1.11.0 or doing something fancy, ignore this: -.rvmrc - -# Used by RuboCop. Remote config files pulled in from inherit_from directive. -# .rubocop-https?--* diff --git a/_tools/patch_for_go_gem/.rspec b/_tools/patch_for_go_gem/.rspec deleted file mode 100644 index 5be63fcb..00000000 --- a/_tools/patch_for_go_gem/.rspec +++ /dev/null @@ -1,2 +0,0 @@ ---require spec_helper ---format documentation diff --git a/_tools/patch_for_go_gem/README.md b/_tools/patch_for_go_gem/README.md deleted file mode 100644 index eb758bff..00000000 --- a/_tools/patch_for_go_gem/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# patch_for_go_gem -Patch to make a gem into a Go gem right after `bundle gem` - -> [!NOTE] -> This is a temporary tool until `bundle gem --ext=go` is added to the bundler -> -> c.f. https://github.com/rubygems/rubygems/pull/8183 - -## Requirements -* Go 1.23+ -* Ruby 3.3+ - -## Usage -1. Download [patch_for_go_gem.rb](patch_for_go_gem.rb) -2. Run `bundle gem --ext=c` - * The other options for `bundle gem` are optional -3. Run `ruby patch_for_go_gem.rb --file /path/to/GEM_NAME.gemspec --dry-run` -4. Run `ruby patch_for_go_gem.rb --file /path/to/GEM_NAME.gemspec` -5. `cd` to the same location as `ext/GEM_NAME/go.mod` -6. Run `go get -u github.com/ruby-go-gem/go-gem-wrapper@latest` diff --git a/_tools/patch_for_go_gem/patch_for_go_gem.rb b/_tools/patch_for_go_gem/patch_for_go_gem.rb deleted file mode 100644 index 6b6af93b..00000000 --- a/_tools/patch_for_go_gem/patch_for_go_gem.rb +++ /dev/null @@ -1,199 +0,0 @@ -# frozen_string_literal: true - -require "optparse" - -gemspec_file = nil -dry_run = false - -opt = OptionParser.new -opt.on("-f", "--file=GEMSPEC_FILE") { |v| gemspec_file = v } -opt.on("--dry-run") { |v| dry_run = v } - -opt.parse!(ARGV) - -raise "--file is required" unless gemspec_file -raise "#{gemspec_file} isn't gemspec" unless File.extname(gemspec_file) == ".gemspec" -raise "#{gemspec_file} isn't found" unless File.exist?(gemspec_file) - -# Patch to make a gem into a Go gem right after `bundle gem` -class GemPatcher # rubocop:disable Metrics/ClassLength - attr_reader :gemspec_file - - # @param gemspec_file [String] - # @param dry_run [Boolean] - def initialize(gemspec_file:, dry_run:) - @gemspec_file = gemspec_file - @dry_run = dry_run - end - - def perform - create_gem_name_go - create_go_mod - update_gem_name_c - update_extconf_rb - update_gemspec - end - - private - - # @return [Boolean] - def dry_run? - @dry_run - end - - # @return [String] path to ext dir. (e.g. /path/to/gem_name/ext/gem_name) - def ext_dir - File.join(File.absolute_path(File.dirname(gemspec_file)), "ext", gem_name) - end - - # @return [String] - def gem_name - File.basename(gemspec_file, ".gemspec") - end - - # @return [String] - def module_name - snake_to_camel(gem_name) - end - - # @param str [String] - # @return [String] - def snake_to_camel(str) - str.split("_").map(&:capitalize).join.gsub(/(?<=\d)([a-z])/) { _1.upcase } # rubocop:disable Style/SymbolProc - end - - # Create .go - def create_gem_name_go - gem_name_go_path = File.join(ext_dir, "#{gem_name}.go") - - return if File.exist?(gem_name_go_path) - - content = <<~GO - package main - - /* - #include "#{gem_name}.h" - */ - import "C" - - import ( - \t"github.com/ruby-go-gem/go-gem-wrapper/ruby" - ) - - //export Init_#{gem_name} - func Init_#{gem_name}() { - \trb_m#{module_name} := ruby.RbDefineModule("#{module_name}") - } - - func main() { - } - GO - - save_file(file_path: gem_name_go_path, content:) - end - - def create_go_mod - go_mod_path = File.join(ext_dir, "go.mod") - - return if File.exist?(go_mod_path) - - `go version` =~ /go version go([.\d]+)/ - go_version = ::Regexp.last_match(1) - - raise "go isn't found in PATH" unless go_version - - content = <<~GO - module github.com/username/#{gem_name} - - go #{go_version} - GO - - save_file(file_path: go_mod_path, content:) - end - - def update_gem_name_c - gem_name_c_path = File.join(ext_dir, "#{gem_name}.c") - - content = File.read(gem_name_c_path) - - return if content.include?('#include "_cgo_export.h"') - - content = <<~C - #include "#{gem_name}.h" - #include "_cgo_export.h" - C - - save_file(file_path: gem_name_c_path, content:) - end - - def update_extconf_rb - extconf_rb_path = File.join(ext_dir, "extconf.rb") - - content = File.read(extconf_rb_path) - - unless content.include?(%(require "go_gem/mkmf")) - content.gsub!(<<~RUBY, <<~RUBY) - require "mkmf" - RUBY - require "mkmf" - require "go_gem/mkmf" - RUBY - end - - unless content.include?(%(create_go_makefile("#{gem_name}/#{gem_name}"))) - content.gsub!(<<~RUBY, <<~RUBY) - create_makefile("#{gem_name}/#{gem_name}") - RUBY - create_go_makefile("#{gem_name}/#{gem_name}") - RUBY - end - - save_file(file_path: extconf_rb_path, content:) - end - - def update_gemspec - content = File.read(gemspec_file) - - return if content.include?(%(.add_dependency "go_gem")) || content.include?(%(.add_runtime_dependency "go_gem")) - - content =~ /Gem::Specification\.new\s+do\s+\|(.+)\|/ - spec_var_name = ::Regexp.last_match(1) - - content.gsub!(/^end\n/, <<~RUBY) - #{spec_var_name}.add_dependency "go_gem" - end - RUBY - - save_file(file_path: gemspec_file, content:) - end - - # @param file_path [String] - # @param content [String] - def save_file(file_path:, content:) - is_updated = File.exist?(file_path) - if is_updated - before_content = File.read(file_path) - return if content == before_content - end - - if dry_run? - if is_updated - puts "[INFO] #{file_path} will be updated (dry-run)" - else - puts "[INFO] #{file_path} will be created (dry-run)" - end - - return - end - - File.binwrite(file_path, content) - - if is_updated - puts "[INFO] #{file_path} is updated" - else - puts "[INFO] #{file_path} is created" - end - end -end - -GemPatcher.new(gemspec_file:, dry_run:).perform diff --git a/_tools/patch_for_go_gem/spec/patch_for_go_gem_spec.rb b/_tools/patch_for_go_gem/spec/patch_for_go_gem_spec.rb deleted file mode 100644 index 22579198..00000000 --- a/_tools/patch_for_go_gem/spec/patch_for_go_gem_spec.rb +++ /dev/null @@ -1,108 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe "patch_for_go_gem.rb" do - gem_name = "new_gem" - - before(:all) do - @temp_dir = Dir.mktmpdir - - Dir.chdir(@temp_dir) do - sh "bundle gem #{gem_name} --ext=c" - sh "ruby #{File.join(src_dir, "patch_for_go_gem.rb")} --file=#{File.join(gem_name, "#{gem_name}.gemspec")}" - - if ENV["CI"] - [ - "#{gem_name}.c", - "#{gem_name}.go", - "go.mod", - "extconf.rb", - ].each do |file_name| - file_path = File.join(gem_name, "ext", gem_name, file_name) - puts "----------------------------------------" - puts file_path - puts "----------------------------------------" - puts File.read(file_path) - puts "" - end - end - end - end - - # @param command [String] - def sh(command) - puts "$ #{command}" - system command, exception: true - end - - after(:all) do - FileUtils.remove_entry_secure(@temp_dir) if @temp_dir && Dir.exist?(@temp_dir) - end - - around do |example| - Dir.chdir(@temp_dir) do - example.run - end - end - - describe file(File.join(gem_name, "ext", gem_name, "#{gem_name}.c")) do - it { should be_file } - it { should exist } - its(:content) { should match(/^#include "_cgo_export.h"$/) } - its(:content) { should_not match(/VALUE\s*rb/) } - its(:content) { should_not include "RUBY_FUNC_EXPORTED void" } - its(:content) { should_not match(/Init_.+\(void\)/) } - its(:content) { should_not match(/rb_m.+\s*=\s*rb_define_module\(".+"\);/) } - end - - describe file(File.join(gem_name, "ext", gem_name, "#{gem_name}.go")) do - it { should be_file } - it { should exist } - - let(:content) do - <<~GO - package main - - /* - #include "#{gem_name}.h" - */ - import "C" - - import ( - \t"github.com/ruby-go-gem/go-gem-wrapper/ruby" - ) - GO - end - - its(:content) { should be_start_with content } - end - - describe file(File.join(gem_name, "ext", gem_name, "go.mod")) do - it { should be_file } - it { should exist } - - let(:content) do - <<~GO - module github.com/username/#{gem_name} - - GO - end - - its(:content) { should be_start_with content } - end - - describe file(File.join(gem_name, "ext", gem_name, "extconf.rb")) do - it { should be_file } - it { should exist } - - its(:content) { should include %(require "go_gem/mkmf") } - its(:content) { should include %(create_go_makefile("#{gem_name}/#{gem_name}")) } - its(:content) { should_not include %(create_makefile("#{gem_name}/#{gem_name}")) } - end - - describe file(File.join(gem_name, "#{gem_name}.gemspec")) do - it { should be_file } - it { should exist } - - its(:content) { should include %(spec.add_dependency "go_gem") } - end -end diff --git a/_tools/patch_for_go_gem/spec/spec_helper.rb b/_tools/patch_for_go_gem/spec/spec_helper.rb deleted file mode 100644 index 1ac3aa94..00000000 --- a/_tools/patch_for_go_gem/spec/spec_helper.rb +++ /dev/null @@ -1,118 +0,0 @@ -# frozen_string_literal: true - -# This file was generated by the `rspec --init` command. Conventionally, all -# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. -# The generated `.rspec` file contains `--require spec_helper` which will cause -# this file to always be loaded, without a need to explicitly require it in any -# files. -# -# Given that it is always loaded, you are encouraged to keep this file as -# light-weight as possible. Requiring heavyweight dependencies from this file -# will add to the boot time of your test suite on EVERY test run, even for an -# individual file that may not need all of that loaded. Instead, consider making -# a separate helper file that requires the additional dependencies and performs -# the additional setup, and require it from the spec files that actually need -# it. -# -# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration - -require "tmpdir" -require "serverspec" - -set :backend, :exec - -RSpec.configure do |config| - # rspec-expectations config goes here. You can use an alternate - # assertion/expectation library such as wrong or the stdlib/minitest - # assertions if you prefer. - config.expect_with :rspec do |expectations| - # This option will default to `true` in RSpec 4. It makes the `description` - # and `failure_message` of custom matchers include text for helper methods - # defined using `chain`, e.g.: - # be_bigger_than(2).and_smaller_than(4).description - # # => "be bigger than 2 and smaller than 4" - # ...rather than: - # # => "be bigger than 2" - expectations.include_chain_clauses_in_custom_matcher_descriptions = true - end - - # rspec-mocks config goes here. You can use an alternate test double - # library (such as bogus or mocha) by changing the `mock_with` option here. - config.mock_with :rspec do |mocks| - # Prevents you from mocking or stubbing a method that does not exist on - # a real object. This is generally recommended, and will default to - # `true` in RSpec 4. - mocks.verify_partial_doubles = true - end - - # This option will default to `:apply_to_host_groups` in RSpec 4 (and will - # have no way to turn it off -- the option exists only for backwards - # compatibility in RSpec 3). It causes shared context metadata to be - # inherited by the metadata hash of host groups and examples, rather than - # triggering implicit auto-inclusion in groups with matching metadata. - config.shared_context_metadata_behavior = :apply_to_host_groups - - # The settings below are suggested to provide a good initial experience - # with RSpec, but feel free to customize to your heart's content. - # # This allows you to limit a spec run to individual examples or groups - # # you care about by tagging them with `:focus` metadata. When nothing - # # is tagged with `:focus`, all examples get run. RSpec also provides - # # aliases for `it`, `describe`, and `context` that include `:focus` - # # metadata: `fit`, `fdescribe` and `fcontext`, respectively. - # config.filter_run_when_matching :focus - # - # # Allows RSpec to persist some state between runs in order to support - # # the `--only-failures` and `--next-failure` CLI options. We recommend - # # you configure your source control system to ignore this file. - # config.example_status_persistence_file_path = "spec/examples.txt" - # - # # Limits the available syntax to the non-monkey patched syntax that is - # # recommended. For more details, see: - # # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/ - # config.disable_monkey_patching! - # - # # This setting enables warnings. It's recommended, but in some cases may - # # be too noisy due to issues in dependencies. - # config.warnings = true - # - # # Many RSpec users commonly either run the entire suite or an individual - # # file, and it's useful to allow more verbose output when running an - # # individual spec file. - # if config.files_to_run.one? - # # Use the documentation formatter for detailed output, - # # unless a formatter has already been configured - # # (e.g. via a command-line flag). - # config.default_formatter = "doc" - # end - # - # # Print the 10 slowest examples and example groups at the - # # end of the spec run, to help surface which specs are running - # # particularly slow. - # config.profile_examples = 10 - # - # # Run specs in random order to surface order dependencies. If you find an - # # order dependency and want to debug it, you can fix the order by providing - # # the seed, which is printed after each run. - # # --seed 1234 - # config.order = :random - # - # # Seed global randomization in this process using the `--seed` CLI option. - # # Setting this allows you to use `--seed` to deterministically reproduce - # # test failures related to randomization by passing the same `--seed` value - # # as the one that triggered the failure. - # Kernel.srand config.seed - - config.define_derived_metadata do |meta| - meta[:aggregate_failures] = true - end -end - -# @return [Pathname] -def spec_dir - __dir__ -end - -# @return [Pathname] -def src_dir - File.expand_path(File.join(spec_dir, "..")) -end