diff --git a/Rakefile b/Rakefile index 82636b36..ddaf280b 100644 --- a/Rakefile +++ b/Rakefile @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative "_gem/lib/go_gem/rake_task" + # @return [String] def repo_root __dir__ diff --git a/_gem/README.md b/_gem/README.md index 01ed7c4f..822ba0e1 100644 --- a/_gem/README.md +++ b/_gem/README.md @@ -33,6 +33,68 @@ require "go_gem/mkmf" # Append this create_go_makefile("example/example") ``` +### `GoGem::RakeTask` +Provides rake tasks for `go test` with CRuby + +#### Example (Without config) +```ruby +# Rakefile +require "go_gem/rake_task" + +GoGem::RakeTask.new("gem_name") +``` + +Following tasks are generated + +* `rake go:test` +* `rake go:testrace` +* `rake go:fmt` + +#### Example (With config) +```ruby +# Rakefile +require "go_gem/rake_task" + +GoGem::RakeTask.new("gem_name") do |t| + t.task_namespace = "go5" + t.go_bin_path = "/path/to/go" + t.go_test_args = "-mod=readonly" + t.target_dir = "/dir/to/go-mod/" +end +``` + +Following tasks are generated + +* `rake go5:test` +* `rake go5:testrace` +* `rake go5:fmt` + +#### Example (Add additional tasks) +```ruby +# Rakefile +require "go_gem/rake_task" + +go_task = GoGem::RakeTask.new("gem_name") + +namespace :go do + desc "Run golangci-lint" + task :lint do + go_task.within_target_dir do + sh "which golangci-lint" do |ok, _| + raise "golangci-lint isn't installed. See. https://golangci-lint.run/welcome/install/" unless ok + end + sh GoGem::RakeTask.build_env_vars, "golangci-lint run" + end + end +end +``` + +#### Available configurations +* `task_namespace` : task namespace (default: `:go`) +* `go_bin_path` : path to go binary (default: `"go"`) +* `go_test_args` : argument passed to `go test` (default: `"-mod=readonly -count=1"`) +* `target_dir` : directory when executing go commands. (default: `"ext/#{gem_name}"`) + ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. diff --git a/_gem/lib/go_gem/rake_task.rb b/_gem/lib/go_gem/rake_task.rb new file mode 100644 index 00000000..fac48924 --- /dev/null +++ b/_gem/lib/go_gem/rake_task.rb @@ -0,0 +1,172 @@ +# frozen_string_literal: true + +require "rake" +require "rake/tasklib" + +module GoGem + # Provides rake tasks for `go test` with CRuby + # + # @example Without config + # # Rakefile + # require "go_gem/rake_task" + # + # GoGem::RakeTask.new("gem_name") + # + # @example With config + # # Rakefile + # require "go_gem/rake_task" + # + # GoGem::RakeTask.new("gem_name") do |t| + # t.task_namespace = "go5" + # t.go_bin_path = "/path/to/go" + # t.go_test_args = "-mod=readonly" + # t.target_dir = "/dir/to/go-mod/" + # end + # + # @example additional tasks + # # Rakefile + # require "go_gem/rake_task" + # + # t = GoGem::RakeTask.new("gem_name") + # + # namespace :go do + # desc "Run golangci-lint" + # task :lint do + # t.within_target_dir do + # sh "which golangci-lint" do |ok, _| + # raise "golangci-lint isn't installed. See. https://golangci-lint.run/welcome/install/" unless ok + # end + # sh GoGem::RakeTask.build_env_vars, "golangci-lint run" + # end + # end + # end + class RakeTask < ::Rake::TaskLib + DEFAULT_TASK_NAMESPACE = :go + + DEFAULT_GO_BIN_PATH = "go" + + DEFAULT_GO_TEST_ARGS = "-mod=readonly -count=1" + + # @!attribute [r] gem_name + # @return [String] + attr_reader :gem_name + + # @!attribute task_namespace + # @return [Symbol,String] task namespace (default: `:go`) + attr_accessor :task_namespace + + # @!attribute go_bin_path + # @return [String] path to go binary (default: `"go"`) + attr_accessor :go_bin_path + + # @!attribute go_test_args + # @return [String] argument passed to `go test` (default: `"-mod=readonly -count=1"`) + attr_accessor :go_test_args + + # @!attribute cwd + # @return [String] directory when executing go commands. (default: `"ext/#{gem_name}"`) + attr_accessor :target_dir + + # @param gem_name [String] + # @yield configuration of {RakeTask} + # @yieldparam t [RakeTask] + def initialize(gem_name) + super() + + @gem_name = gem_name + + @task_namespace = DEFAULT_TASK_NAMESPACE + @go_bin_path = DEFAULT_GO_BIN_PATH + @go_test_args = DEFAULT_GO_TEST_ARGS + @target_dir = ext_dir + + yield(self) if block_given? + + namespace(task_namespace) do + define_go_test_task + define_go_testrace_task + define_go_fmt_task + end + end + + # Generate environment variables to build go programs in the Go gem + # + # @return [Hash] + def self.build_env_vars + ldflags = "-L#{RbConfig::CONFIG["libdir"]} -l#{RbConfig::CONFIG["RUBY_SO_NAME"]}" + + case `#{RbConfig::CONFIG["CC"]} --version` # rubocop:disable Lint/LiteralAsCondition + when /Free Software Foundation/ + ldflags << " -Wl,--unresolved-symbols=ignore-all" + when /clang/ + ldflags << " -undefined dynamic_lookup" + end + + cflags = [ + RbConfig::CONFIG["CFLAGS"], + "-I#{RbConfig::CONFIG["rubyarchhdrdir"]}", + "-I#{RbConfig::CONFIG["rubyhdrdir"]}", + ].join(" ") + + # FIXME: Workaround for Ubuntu (GitHub Actions) + if RUBY_PLATFORM =~ /linux/i + cflags.gsub!("-Wno-self-assign", "") + cflags.gsub!("-Wno-parentheses-equality", "") + cflags.gsub!("-Wno-constant-logical-operand", "") + cflags.gsub!("-Wsuggest-attribute=format", "") + cflags.gsub!("-Wold-style-definition", "") + cflags.gsub!("-Wsuggest-attribute=noreturn", "") + ldflags.gsub!("-Wl,--unresolved-symbols=ignore-all", "") + end + + ld_library_path = RbConfig::CONFIG["libdir"] + + { + "CGO_CFLAGS" => cflags, + "CGO_LDFLAGS" => ldflags, + "LD_LIBRARY_PATH" => ld_library_path, + } + end + + # @yield + def within_target_dir + Dir.chdir(target_dir) do # rubocop:disable Style/ExplicitBlockArgument + yield + end + end + + # @return [String] + def ext_dir + File.join("ext", gem_name) + end + + private + + def define_go_test_task + desc "Run #{go_bin_path} test" + task(:test) do + within_target_dir do + sh RakeTask.build_env_vars, "#{go_bin_path} test #{go_test_args} ./..." + end + end + end + + def define_go_testrace_task + desc "Run #{go_bin_path} test -race" + task(:testrace) do + within_target_dir do + sh RakeTask.build_env_vars, "#{go_bin_path} test #{go_test_args} -race ./..." + end + end + end + + def define_go_fmt_task + desc "Run #{go_bin_path} fmt" + task(:fmt) do + within_target_dir do + sh "#{go_bin_path} fmt ./..." + end + end + end + end +end diff --git a/_gem/sig/go_gem/rake_task.rbs b/_gem/sig/go_gem/rake_task.rbs new file mode 100644 index 00000000..a3c8d349 --- /dev/null +++ b/_gem/sig/go_gem/rake_task.rbs @@ -0,0 +1,45 @@ +module GoGem + class RakeTask < ::Rake::TaskLib + @gem_name: String + + @task_namespace: Symbol | String + + @go_bin_path: String + + @go_test_args: String + + @target_dir: String + + DEFAULT_TASK_NAMESPACE: Symbol + + DEFAULT_GO_BIN_PATH: String + + DEFAULT_GO_TEST_ARGS: String + + attr_reader gem_name: String + + attr_accessor task_namespace: Symbol | String + + attr_accessor go_bin_path: String + + attr_accessor go_test_args: String + + attr_accessor target_dir: String + + def initialize: (String gem_name) ?{ (RakeTask) -> void } -> void + + def self.build_env_vars: () -> { "CGO_CFLAGS" => String, "CGO_LDFLAGS" => String, "LD_LIBRARY_PATH" => String } + + private + + def define_go_test_task: () -> void + + def define_go_testrace_task: () -> void + + def define_go_fmt_task: () -> void + + def within_target_dir: () { () -> void } -> void + + def ext_dir: () -> String + end +end diff --git a/_gem/sig/non-gemify/rake.rbs b/_gem/sig/non-gemify/rake.rbs new file mode 100644 index 00000000..4ce409d3 --- /dev/null +++ b/_gem/sig/non-gemify/rake.rbs @@ -0,0 +1,4 @@ +module FileUtils + def sh: (Hash[String, String] env, *String cmd, **untyped options) ?{ (bool, Process::Status) -> void } -> void + | ... +end diff --git a/_gem/spec/go_gem/rake_task_spec.rb b/_gem/spec/go_gem/rake_task_spec.rb new file mode 100644 index 00000000..fdc02f13 --- /dev/null +++ b/_gem/spec/go_gem/rake_task_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +RSpec.describe GoGem::RakeTask do + before { Rake::Task.clear } + + after { Rake::Task.clear } + + describe "defining tasks" do + context "with default params" do + let(:gem_name) { "my_gem" } + + subject do + GoGem::RakeTask.new(gem_name) + Rake::Task + end + + it { should be_task_defined("go:test") } + it { should be_task_defined("go:testrace") } + it { should be_task_defined("go:fmt") } + + describe "Add additional tasks" do + include Rake::DSL + + subject do + t = GoGem::RakeTask.new(gem_name) + + namespace :go do + task :test2 do + t.within_target_dir do + sh "go test" + end + end + end + + Rake::Task + end + + it { should be_task_defined("go:test2") } + end + end + + context "with params" do + let(:gem_name) { "my_gem" } + + subject do + GoGem::RakeTask.new(gem_name) do |config| + config.task_namespace = :go5 + end + Rake::Task + end + + it { should be_task_defined("go5:test") } + it { should be_task_defined("go5:testrace") } + it { should be_task_defined("go5:fmt") } + end + end +end diff --git a/_gem/spec/spec_helper.rb b/_gem/spec/spec_helper.rb index 85093bec..8d245f6f 100644 --- a/_gem/spec/spec_helper.rb +++ b/_gem/spec/spec_helper.rb @@ -2,6 +2,7 @@ require "go_gem" require "go_gem/mkmf" +require "go_gem/rake_task" require "tmpdir" require "serverspec" diff --git a/_tasks/go.rake b/_tasks/go.rake index b0401bf9..cc5e28f2 100644 --- a/_tasks/go.rake +++ b/_tasks/go.rake @@ -1,60 +1,19 @@ # frozen_string_literal: true -# @return [Hash] -def env_vars - ldflags = "-L#{RbConfig::CONFIG["libdir"]} -l#{RbConfig::CONFIG["RUBY_SO_NAME"]}" - - case `#{RbConfig::CONFIG["CC"]} --version` # rubocop:disable Lint/LiteralAsCondition - when /Free Software Foundation/ - ldflags << " -Wl,--unresolved-symbols=ignore-all" - when /clang/ - ldflags << " -undefined dynamic_lookup" - end - - cflags = "#{RbConfig::CONFIG["CFLAGS"]} -I#{RbConfig::CONFIG["rubyarchhdrdir"]} -I#{RbConfig::CONFIG["rubyhdrdir"]}" - - # FIXME: Workaround for GitHub Actions - if ENV["GITHUB_ACTIONS"] - cflags.gsub!("-Wno-self-assign", "") - cflags.gsub!("-Wno-parentheses-equality", "") - cflags.gsub!("-Wno-constant-logical-operand", "") - cflags.gsub!("-Wsuggest-attribute=format", "") - cflags.gsub!("-Wold-style-definition", "") - cflags.gsub!("-Wsuggest-attribute=noreturn", "") - ldflags.gsub!("-Wl,--unresolved-symbols=ignore-all", "") - end - - ld_library_path = RbConfig::CONFIG["libdir"] - - { - "CGO_CFLAGS" => cflags, - "CGO_LDFLAGS" => ldflags, - "LD_LIBRARY_PATH" => ld_library_path, - } +go_task = GoGem::RakeTask.new("") do |t| + t.target_dir = repo_root + t.go_test_args = "#{GoGem::RakeTask::DEFAULT_GO_TEST_ARGS} #{ENV["GO_TEST_ARGS"]}" end namespace :go do - desc "Run go test" - task :test do - sh env_vars, "go test -mod=readonly -count=1 #{ENV["GO_TEST_ARGS"]} ./..." - end - - desc "Run go test -race" - task :testrace do - sh env_vars, "go test -mod=readonly -count=1 #{ENV["GO_TEST_ARGS"]} -race ./..." - end - - desc "Run go fmt" - task :fmt do - sh "go fmt ./..." - end - desc "Run golangci-lint" task :lint do - sh "which golangci-lint" do |ok, _| - raise "golangci-lint isn't installed. See. https://golangci-lint.run/welcome/install/" unless ok + go_task.within_target_dir do + sh "which golangci-lint" do |ok, _| + raise "golangci-lint isn't installed. See. https://golangci-lint.run/welcome/install/" unless ok + end + sh GoGem::RakeTask.build_env_vars, "golangci-lint run" end - sh env_vars, "golangci-lint run" end desc "Run all build tasks in go"