From 409947296956843704bc60bd3401cac6a95bcd9b Mon Sep 17 00:00:00 2001 From: Konstantin Gredeskoul Date: Tue, 31 Dec 2019 02:14:37 -0800 Subject: [PATCH 01/19] Making various tweaks to support monorepo * We can't disable gems because Bundler fails to work at all * Changing the default of `bundle_install` to `ruby_bundle`, but keeping `bundle_install` as an alias. * Adding code to set bundler config instead of passing command line options, which have been deprecated and are now causing errors with Bundler 2.1.2 --- ruby/defs.bzl | 5 +- ruby/private/bundle.bzl | 5 +- ruby/private/bundle/bundle.bzl | 113 ++++++++++++++---- .../private/toolchains/repository_context.bzl | 2 +- 4 files changed, 97 insertions(+), 28 deletions(-) diff --git a/ruby/defs.bzl b/ruby/defs.bzl index ec111b8..bd1476c 100644 --- a/ruby/defs.bzl +++ b/ruby/defs.bzl @@ -13,11 +13,12 @@ load( ) load( "@bazelruby_ruby_rules//ruby/private:bundle.bzl", - _bundle_install = "bundle_install", + _ruby_bundle = "ruby_bundle", ) ruby_toolchain = _toolchain ruby_library = _library ruby_binary = _binary ruby_test = _test -bundle_install = _bundle_install +bundle_install = _ruby_bundle +ruby_bundle = _ruby_bundle diff --git a/ruby/private/bundle.bzl b/ruby/private/bundle.bzl index 636fb6b..292d6e1 100644 --- a/ruby/private/bundle.bzl +++ b/ruby/private/bundle.bzl @@ -1,6 +1,7 @@ load( "//ruby/private/bundle:bundle.bzl", - _bundle_install = "bundle_install", + _ruby_bundle = "ruby_bundle", ) -bundle_install = _bundle_install +bundle_install = _ruby_bundle +ruby_bundle = _ruby_bundle diff --git a/ruby/private/bundle/bundle.bzl b/ruby/private/bundle/bundle.bzl index ad2da0b..121780d 100644 --- a/ruby/private/bundle/bundle.bzl +++ b/ruby/private/bundle/bundle.bzl @@ -1,8 +1,70 @@ load("//ruby/private:constants.bzl", "RULES_RUBY_WORKSPACE_NAME") -def install_bundler(ctx, interpreter, install_bundler, dest, version): +BUNDLE_INSTALL_PATH = "lib" +BUNDLE_BINARY = "bundler/exe/bundler" + +# TODO: do not hard-code this +GEM_HOME = "lib/ruby/2.5.0/" + +def run_bundler(ctx, interpreter, environment, extra_args): + # Now we are running bundle install + args = [ + interpreter, # ruby + "--enable=gems", # bundler must run with rubygems enabled + "-I", + ".", + "-I", # Used to tell Ruby where to load the library scripts + "lib", # Add vendor/bundle to the list of resolvers + BUNDLE_BINARY, # our binary + ] + extra_args + + # print("running bundler with args\n", args) + + result = ctx.execute( + args, + quiet = False, + ) + + return result + +# Sets local bundler config values +def set_bundler_config(ctx, interpreter, environment): + # Bundler is deprecating various flags in favor of the configuration. + # HOWEVER — for reasons I can't explain, Bazel runs "bundle install" *prior* + # to setting these flags. So the flags are then useless until we can force the + # order and ensure that Bazel first downloads Bundler, then sets config, then + # runs bundle install. Until then, it's a wild west out here. + # + # Set local configuration options for bundler + bundler_config = { + "binstubs": "bin", + "deployment": "'true'", + "standalone": "'true'", + "frozen": "'true'", + "without": "development,test", + "path": "lib", + "jobs": "20", + } + + for option, value in [(option, value) for option, value in bundler_config.items()]: + args = [ + "config", + "--local", + option, + value, + ] + + result = run_bundler(ctx, interpreter, environment, args) + if result.return_code: + message = "Failed to set bundle config {} to {}: {}".format( + option, + value, + result.stderr, + ) + fail(message) + +def install_bundler(ctx, interpreter, install_bundler, dest, version, environment): args = [interpreter, install_bundler, version, dest] - environment = {"RUBYOPT": "--disable-gems"} result = ctx.execute(args, environment = environment) if result.return_code: @@ -22,34 +84,39 @@ def bundle_install_impl(ctx): if ctx.attr.gemspec: ctx.symlink(ctx.attr.gemspec, ctx.path(ctx.attr.gemspec).basename) + environment = {"RUBYOPT": "--enable-gems", "GEM_HOME": GEM_HOME} + ruby = ctx.attr.ruby_interpreter - interpreter_path = ctx.path(ruby) + interpreter = ctx.path(ruby) install_bundler( ctx, - interpreter_path, + interpreter, "install_bundler.rb", "bundler", ctx.attr.version, + environment, ) + bundler = Label("//:" + BUNDLE_BINARY) - bundler = Label("//:bundler/exe/bundler") + # Set Bundler config in the .bundle/config file + set_bundler_config( + ctx, + interpreter, + environment, + ) - # Install the Gems into the workspace - args = [ - ctx.path(ruby), # ruby - "--disable-gems", # prevent the addition of gem installation directories to the default load path - "-I", # Used to tell Ruby where to load the library scripts - "bundler/lib", - "bundler/exe/bundler", # run - "install", # > bundle install - "--deployment", # In the deployment mode, gems are dumped to --path and frozen; also .bundle/config file is created - "--standalone", # Makes a bundle that can work without depending on Rubygems or Bundler at runtime. - "--frozen", # Do not allow the Gemfile.lock to be updated after this install. - "--binstubs=bin", # Creates a directory and place any executables from the gem there. - "--path=lib", # The location to install the specified gems to. - ] - result = ctx.execute(args, quiet = False) + result = run_bundler( + ctx, + interpreter, + environment, + [ + "install", # > bundle install + "--standalone", # Makes a bundle that can work without depending on Rubygems or Bundler at runtime. + "--binstubs=bin", # Creates a directory and place any executables from the gem there. + "--path={}".format(BUNDLE_INSTALL_PATH), # The location to install the specified gems to. + ], + ) if result.return_code: fail("Failed to install gems: %s%s" % (result.stdout, result.stderr)) @@ -57,7 +124,7 @@ def bundle_install_impl(ctx): # Create the BUILD file to expose the gems to the WORKSPACE args = [ ctx.path(ruby), # ruby interpreter - "--disable-gems", # prevent the addition of gem installation directories to the default load path + "--enable=gems", # prevent the addition of gem installation directories to the default load path "-I", # -I lib (adds this folder to $LOAD_PATH where ruby searchesf for things) "bundler/lib", "create_bundle_build_file.rb", # The template used to created bundle file @@ -81,7 +148,7 @@ def bundle_install_impl(ctx): }, ) -bundle_install = repository_rule( +ruby_bundle = repository_rule( implementation = bundle_install_impl, attrs = { "ruby_sdk": attr.string( @@ -98,7 +165,7 @@ bundle_install = repository_rule( allow_single_file = True, ), "version": attr.string( - default = "2.0.2", + default = "2.1.2", ), "gemspec": attr.label( allow_single_file = True, diff --git a/ruby/private/toolchains/repository_context.bzl b/ruby/private/toolchains/repository_context.bzl index b82c922..b151531 100644 --- a/ruby/private/toolchains/repository_context.bzl +++ b/ruby/private/toolchains/repository_context.bzl @@ -9,7 +9,7 @@ def _eval_ruby(ruby, script, options = None): arguments.extend(options) arguments.extend(["-e", script]) - environment = {"RUBYOPT": "--disable-gems"} + environment = {"RUBYOPT": "--enable=gems"} result = ruby._ctx.execute(arguments, environment = environment) if result.return_code: From 10a116c043ce31b337c24854b84d74437d657c2a Mon Sep 17 00:00:00 2001 From: Konstantin Gredeskoul Date: Tue, 31 Dec 2019 04:07:21 -0800 Subject: [PATCH 02/19] Fixing CI and tests --- .rubocop.yml | 2 +- examples/simple_script/.rubocop.yml | 35 ++++++++++++--- examples/simple_script/BUILD | 38 ++++++++++++++-- examples/simple_script/Gemfile | 1 + examples/simple_script/Gemfile.lock | 6 ++- examples/simple_script/lib/foo.rb | 29 ++++++++++-- examples/simple_script/script.rb | 8 ++-- examples/simple_script/spec/lib/foo_spec.rb | 26 +++++++++++ examples/simple_script/spec/script_spec.rb | 31 +------------ examples/simple_script/spec/spec_helper.rb | 44 +++++++++++++++++++ ruby/private/bundle/bundle.bzl | 24 ++++++++-- .../bundle/create_bundle_build_file.rb | 29 +++++++++--- ruby/private/bundle/install_bundler.rb | 2 + 13 files changed, 216 insertions(+), 59 deletions(-) create mode 100644 examples/simple_script/spec/lib/foo_spec.rb create mode 100644 examples/simple_script/spec/spec_helper.rb diff --git a/.rubocop.yml b/.rubocop.yml index f4a96ee..34955ae 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -6,7 +6,7 @@ AllCops: - "**/rubocop" - "examples/**/*" - "bazel-*/**/*" - - "**/external/**/*" + - "external/**/*" - "**/vendor/bundle/**/*" diff --git a/examples/simple_script/.rubocop.yml b/examples/simple_script/.rubocop.yml index 461aed5..dc53049 100644 --- a/examples/simple_script/.rubocop.yml +++ b/examples/simple_script/.rubocop.yml @@ -1,11 +1,36 @@ -inherit_from: .relaxed-rubocop-2.4.yml +inherit_from: ~/.relaxed-rubocop-2.4.yml AllCops: - TargetRubyVersion: 2.6.5 + TargetRubyVersion: 2.6 + UseCache: true + DefaultFormatter: progress + DisplayStyleGuide: true + DisplayCopNames: true Exclude: - - rubocop - - bazel-*/**/* - - external/**/* + - "external*/**/*" + - "bazel-*/**/*" + - "**/examples/**/*" + - "**/BUILD" + - "**/*.bazel" + - "**/*.bzl" + - "**/rubocop" - "**/vendor/bundle/**/*" + Include: + - '**/*.rb' + - '**/*.gemfile' + - '**/*.gemspec' + - '**/*.rake' + - '**/*.ru' + - '**/Gemfile' + - '**/Rakefile' +Layout/HashAlignment: + Enabled: true + EnforcedColonStyle: table + +Style/Dir: + Enabled: false +# In Bazel we want to use __FILE__ because __dir__points to the actual sources +Style/ExpandPathArguments: + Enabled: false diff --git a/examples/simple_script/BUILD b/examples/simple_script/BUILD index 7500d6d..56dd4d3 100644 --- a/examples/simple_script/BUILD +++ b/examples/simple_script/BUILD @@ -3,32 +3,59 @@ package(default_visibility = ["//:__subpackages__"]) load( "@bazelruby_ruby_rules//ruby:defs.bzl", "ruby_binary", + "ruby_library", "ruby_test", ) +ruby_library( + name = "script.lib", + srcs = [ + "script.rb", + "//lib:foo", + ], + deps = [ + "@bundle//:awesome_print", + "@bundle//:colored2", + ], +) + ruby_binary( - name = "default_bin", + name = "bin", srcs = ["script.rb"], main = "script.rb", deps = [ + ":script.lib", "//lib:foo", "@bundle//:awesome_print", + "@bundle//:colored2", ], ) ruby_test( - name = "script_spec", + name = "rspec", timeout = "short", srcs = [ "script.rb", + "spec/lib/foo_spec.rb", "spec/script_spec.rb", + "spec/spec_helper.rb", + "@bundle//:bin/rspec", + ], + args = [ + "spec", + ], + includes = [ + ".", + "lib", + "spec", ], - main = "spec/script_spec.rb", - rubyopt = ["-rrspec/autorun"], # require autorun because it is needed + main = "spec/spec_helper.rb", deps = [ "//lib:foo", "@bundle//:awesome_print", + "@bundle//:colored2", "@bundle//:rspec", + "@bundle//:rspec-its", ], ) @@ -39,7 +66,9 @@ ruby_binary( ".relaxed-rubocop-2.4.yml", ".rubocop.yml", "script.rb", + "spec/lib/foo_spec.rb", "spec/script_spec.rb", + "spec/spec_helper.rb", "@bundle//:bin/rubocop", ], args = [ @@ -50,6 +79,7 @@ ruby_binary( ], main = "@bundle//:bin/rubocop", deps = [ + "//lib:foo", "@bundle//:rubocop", ], ) diff --git a/examples/simple_script/Gemfile b/examples/simple_script/Gemfile index 912b976..d677fb5 100644 --- a/examples/simple_script/Gemfile +++ b/examples/simple_script/Gemfile @@ -5,4 +5,5 @@ source 'https://rubygems.org' gem 'awesome_print' gem 'colored2' gem 'rspec', '~> 3.7.0' +gem 'rspec-its' gem 'rubocop', '~> 0.78.0' diff --git a/examples/simple_script/Gemfile.lock b/examples/simple_script/Gemfile.lock index 5c2bd87..f49663c 100644 --- a/examples/simple_script/Gemfile.lock +++ b/examples/simple_script/Gemfile.lock @@ -19,6 +19,9 @@ GEM rspec-expectations (3.7.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.7.0) + rspec-its (1.3.0) + rspec-core (>= 3.0.0) + rspec-expectations (>= 3.0.0) rspec-mocks (3.7.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.7.0) @@ -40,7 +43,8 @@ DEPENDENCIES awesome_print colored2 rspec (~> 3.7.0) + rspec-its rubocop (~> 0.78.0) BUNDLED WITH - 2.1.0 + 2.1.2 diff --git a/examples/simple_script/lib/foo.rb b/examples/simple_script/lib/foo.rb index f4e6243..eff4466 100644 --- a/examples/simple_script/lib/foo.rb +++ b/examples/simple_script/lib/foo.rb @@ -1,7 +1,30 @@ # frozen_string_literal: true -module Foo - def self.aha - "aha" +class Foo + class << self + def yell_aha + puts aha + end + + def aha + 'You said, aha?' + end + + def rot13(value) + return nil unless value.is_a?(String) + + value.tr('abcdefghijklmnopqrstuvwxyz', 'nopqrstuvwxyzabcdefghijklm') + end + end + + attr_reader :goo, :foo + + def initialize(goo) + @goo = goo + @foo = transform(goo) + end + + def transform(incoming = goo) + Foo.rot13(incoming) end end diff --git a/examples/simple_script/script.rb b/examples/simple_script/script.rb index bc1e0ac..e4853dd 100644 --- a/examples/simple_script/script.rb +++ b/examples/simple_script/script.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true +require 'colored2' require 'openssl' -require 'lib/foo' require 'awesome_print' +require_relative 'lib/foo' + def oss_rand OpenSSL::BN.rand(512).to_s end puts Foo.aha + ' ' + oss_rand -puts $LOAD_PATH - -ap Class +ap Class.to_s diff --git a/examples/simple_script/spec/lib/foo_spec.rb b/examples/simple_script/spec/lib/foo_spec.rb new file mode 100644 index 0000000..d65f333 --- /dev/null +++ b/examples/simple_script/spec/lib/foo_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require_relative '../spec_helper' +require_relative '../../lib/foo' + +RSpec.describe Foo do + let(:goo) { 'Green slime was dripping down his throat into his lapdomen...' } + subject(:foo) { Foo.new(goo) } + + context 'without the aha' do + before { allow(Foo).to receive(:yell_aha).and_return('tiny dongle') } + + its(:goo) { should eq goo } + its(:transform) { should_not eq goo } + + # Some rot13 old school encryption :) + its(:transform) { should eq 'Gerra fyvzr jnf qevccvat qbja uvf guebng vagb uvf yncqbzra...' } + end + + context 'aha' do + it 'should print aha' do + expect(Foo).to receive(:puts).with('You said, aha?').and_return(nil) + Foo.yell_aha + end + end +end diff --git a/examples/simple_script/spec/script_spec.rb b/examples/simple_script/spec/script_spec.rb index cb0de75..d246cac 100644 --- a/examples/simple_script/spec/script_spec.rb +++ b/examples/simple_script/spec/script_spec.rb @@ -1,35 +1,6 @@ # frozen_string_literal: true -RSpec.configure do |config| - config.expect_with :rspec do |expectations| - expectations.include_chain_clauses_in_custom_matcher_descriptions = true - end - - config.mock_with :rspec do |mocks| - mocks.verify_partial_doubles = true - end - - config.shared_context_metadata_behavior = :apply_to_host_groups - - config.warnings = true - config.filter_run_when_matching :focus - # config.disable_monkey_patching! - config.order = :random - Kernel.srand config.seed -end - -# Sets HOME here because: -# If otherwise, it causes a runtime failure with the following steps. -# 1. RSpec::Core::ConfigurationOptions.global_options_file raises an exception -# because $HOME is not set in the sandbox environment of Bazel -# 2. the rescue clause calls RSpec::Support.#warning -# 3. #warning calls #warn_with -# 4. #warn_with tries to lookup the first caller which is not a part of RSpec. -# But all the call stack entires are about RSpec at this time because -# it is invoked by rpsec/autorun. So #warn_with raises an exception -# 5. The process fails with an unhandled exception. -ENV['HOME'] ||= '/' - +require 'spec_helper' require_relative '../script' describe 'oss_rand' do diff --git a/examples/simple_script/spec/spec_helper.rb b/examples/simple_script/spec/spec_helper.rb new file mode 100644 index 0000000..e8a4d70 --- /dev/null +++ b/examples/simple_script/spec/spec_helper.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'rspec' +require 'rspec/its' +require 'awesome_print' +require 'colored2' + +# frozen_string_literal: true +RSpec.configure do |config| + config.expect_with :rspec do |expectations| + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + config.mock_with :rspec do |mocks| + mocks.verify_partial_doubles = true + end + + config.shared_context_metadata_behavior = :apply_to_host_groups + + config.warnings = true + config.filter_run_when_matching :focus + # config.disable_monkey_patching! + config.order = :random + Kernel.srand config.seed +end + +if $0 == 'rspec' + require_relative '../script' + require_relative '../lib/foo' + require_relative './script_spec' + require_relative './lib/foo_spec' +end + +# Sets HOME here because: +# If otherwise, it causes a runtime failure with the following steps. +# 1. RSpec::Core::ConfigurationOptions.global_options_file raises an exception +# because $HOME is not set in the sandbox environment of Bazel +# 2. the rescue clause calls RSpec::Support.#warning +# 3. #warning calls #warn_with +# 4. #warn_with tries to lookup the first caller which is not a part of RSpec. +# But all the call stack entires are about RSpec at this time because +# it is invoked by rpsec/autorun. So #warn_with raises an exception +# 5. The process fails with an unhandled exception. +# ENV['HOME'] ||= '/' diff --git a/ruby/private/bundle/bundle.bzl b/ruby/private/bundle/bundle.bzl index 121780d..46bc314 100644 --- a/ruby/private/bundle/bundle.bzl +++ b/ruby/private/bundle/bundle.bzl @@ -6,6 +6,26 @@ BUNDLE_BINARY = "bundler/exe/bundler" # TODO: do not hard-code this GEM_HOME = "lib/ruby/2.5.0/" +def upgrade_bundler(ctx, interpreter, environment): + # Now we are running bundle install + args = [ + interpreter, # ruby + "--enable=gems", # bundler must run with rubygems enabled + "", + ".", + "-I", # Used to tell Ruby where to load the library scripts + "lib", # Add vendor/bundle to the list of resolvers + BUNDLE_BINARY, # our binary + ] + extra_args + + # print("running bundler with args\n", args) + + result = ctx.execute( + args, + quiet = False, + ) + + return result def run_bundler(ctx, interpreter, environment, extra_args): # Now we are running bundle install args = [ @@ -80,10 +100,6 @@ def bundle_install_impl(ctx): ctx.symlink(ctx.attr._create_bundle_build_file, "create_bundle_build_file.rb") ctx.symlink(ctx.attr._install_bundler, "install_bundler.rb") - # TODO(kig) Make Gemspec reference from Gemfile actually work - if ctx.attr.gemspec: - ctx.symlink(ctx.attr.gemspec, ctx.path(ctx.attr.gemspec).basename) - environment = {"RUBYOPT": "--enable-gems", "GEM_HOME": GEM_HOME} ruby = ctx.attr.ruby_interpreter diff --git a/ruby/private/bundle/create_bundle_build_file.rb b/ruby/private/bundle/create_bundle_build_file.rb index 4470418..2e54122 100644 --- a/ruby/private/bundle/create_bundle_build_file.rb +++ b/ruby/private/bundle/create_bundle_build_file.rb @@ -49,14 +49,29 @@ require "bundler" require 'json' +# This method scans the contents of the Gemfile.lock and if it finds BUNDLED WITH +# it strips that line + the line below it, so that any version of bundler would work. +def remove_bundler_version!(gemfile_lock_file) + contents = File.read(gemfile_lock_file) + unless contents !~ /BUNDLED WITH/ + temp_lock_file = "#{gemfile_lock_file}.no-bundle-version" + system %(sed -n '/BUNDLED WITH/q;p' "#{gemfile_lock_file}" > #{temp_lock_file}) + ::FileUtils.rm_f(gemfile_lock_file) if File.symlink?(gemfile_lock_file) # it's just a symlink + ::FileUtils.move(temp_lock_file, gemfile_lock_file, force: true) + end +end + def create_bundle_build_file(build_out_file, lock_file, repo_name, excludes, workspace_name) # TODO: properly calculate path/ruby version here # ruby_version = RUBY_VERSION # doesnt work because verion is 2.5.5 path is 2.5.0 ruby_version = "*" template_out = TEMPLATE.gsub("{workspace_name}", workspace_name) - .gsub("{repo_name}", repo_name) - .gsub("{ruby_version}", ruby_version) + .gsub("{repo_name}", repo_name) + .gsub("{ruby_version}", ruby_version) + + # strip bundler version so we can process this file + remove_bunder_version!(lock_file) # Append to the end specific gem libraries and dependencies bundle = Bundler::LockfileParser.new(Bundler.read_file(lock_file)) @@ -70,11 +85,11 @@ def create_bundle_build_file(build_out_file, lock_file, repo_name, excludes, wor exclude_array += ["**/* *.*", "**/* */*"] template_out += GEM_TEMPLATE.gsub("{exclude}", exclude_array.to_s) - .gsub("{name}", spec.name) - .gsub("{version}", spec.version.to_s) - .gsub("{deps}", deps.to_s) - .gsub("{repo_name}", repo_name) - .gsub("{ruby_version}", ruby_version) + .gsub("{name}", spec.name) + .gsub("{version}", spec.version.to_s) + .gsub("{deps}", deps.to_s) + .gsub("{repo_name}", repo_name) + .gsub("{ruby_version}", ruby_version) } # Write the actual BUILD file diff --git a/ruby/private/bundle/install_bundler.rb b/ruby/private/bundle/install_bundler.rb index ff4a3d9..24905bb 100644 --- a/ruby/private/bundle/install_bundler.rb +++ b/ruby/private/bundle/install_bundler.rb @@ -16,6 +16,8 @@ def unpack_gem(name, version, dest = Dir.pwd) downloaded = File.join(dir, "#{name}-#{version}.gem") Gem::Package.new(downloaded).extract_files dest } + + `gem install bundler --no-doc` end def main From ea12e88e325c854b7e6f0f1504abc32346597468 Mon Sep 17 00:00:00 2001 From: Konstantin Gredeskoul Date: Tue, 31 Dec 2019 16:37:04 -0800 Subject: [PATCH 03/19] Introducing ruby_rspec_test and a bunch of others * refactoring rules * moving some definitions to constants --- .circleci/config.yml | 16 +- WORKSPACE | 8 +- bin/test-suite | 3 +- examples/simple_script/BUILD | 40 +++- examples/simple_script/WORKSPACE | 12 +- examples/simple_script/script.rb | 2 - ruby/defs.bzl | 5 + ruby/private/binary.bzl | 55 ++--- ruby/private/bundle/bundle.bzl | 204 +++++++----------- .../bundle/create_bundle_build_file.rb | 3 +- ruby/private/bundle/install_bundler.rb | 13 +- ruby/private/constants.bzl | 92 ++++++++ ruby/private/providers.bzl | 9 + ruby/private/rspec.bzl | 28 +++ ruby/private/tools/deprecations.bzl | 12 ++ 15 files changed, 317 insertions(+), 185 deletions(-) mode change 100644 => 100755 ruby/private/bundle/create_bundle_build_file.rb mode change 100644 => 100755 ruby/private/bundle/install_bundler.rb create mode 100644 ruby/private/rspec.bzl create mode 100644 ruby/private/tools/deprecations.bzl diff --git a/.circleci/config.yml b/.circleci/config.yml index 6bbf76d..7b2a76a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -68,17 +68,25 @@ jobs: - run: name: "Bazel Simple Script Example Build" command: | - cd examples/simple_script && bazel ${BAZEL_OPTS} build ${BAZEL_BUILD_OPTS} -- //... + cd examples/simple_script && bazel ${BAZEL_OPTS} build ${BAZEL_BUILD_OPTS} -- :all + + - run: + name: "Bazel Simple Script Example Rubocop Check" + command: | + cd examples/simple_script && bazel ${BAZEL_OPTS} run ${BAZEL_BUILD_OPTS} -- :rubocop || true - run: name: "Bazel Simple Script Example Test" command: | - cd examples/simple_script && bazel ${BAZEL_OPTS} test ${BAZEL_BUILD_OPTS} ${BAZEL_TEST_OPTS} -- //... + cd examples/simple_script + bazel ${BAZEL_OPTS} test -s --verbose_failures --sandbox_debug \ + --test_output=streamed --test_verbose_timeout_warnings :all-specs - run: - name: "Bazel Simple Script Example Rubocop Check" + name: "Print output log" command: | - cd examples/simple_script && bazel ${BAZEL_OPTS} run ${BAZEL_BUILD_OPTS} -- :rubocop + cat /home/circleci/.cache/bazel/_bazel_circleci/86ef9d8d260271208b18f4c01debfa13/execroot/bazelruby_ruby_rules_example/bazel-out/k8-fastbuild/testlogs/all-specs/test.log + buildifier: <<: *bazel_defaults diff --git a/WORKSPACE b/WORKSPACE index 825f82a..429a304 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,11 +1,9 @@ workspace(name = "bazelruby_ruby_rules") -load("@//ruby:deps.bzl", "ruby_register_toolchains", "ruby_rules_dependencies") +load("@//ruby/private:dependencies.bzl", "ruby_rules_dependencies") ruby_rules_dependencies() -ruby_register_toolchains() - load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") bazel_skylib_workspace() @@ -14,6 +12,10 @@ load("@bazel_skylib//lib:versions.bzl", "versions") versions.check("1.2.1") +load("@//ruby:deps.bzl", "ruby_register_toolchains") + +ruby_register_toolchains() + local_repository( name = "bazelruby_ruby_rules_ruby_tests_testdata_another_workspace", path = "ruby/tests/testdata/another_workspace", diff --git a/bin/test-suite b/bin/test-suite index abaf6c2..bb4c762 100755 --- a/bin/test-suite +++ b/bin/test-suite @@ -148,7 +148,8 @@ test::simple-script() { __test::exec simple-script " cd examples/simple_script ${Bazel__BuildSteps} - bazel ${BAZEL_OPTS} run ${BAZEL_BUILD_OPTS} -- :rubocop ; + bazel ${BAZEL_OPTS} build ${BAZEL_BUILD_OPTS} :all; + bazel ${BAZEL_OPTS} test ${BAZEL_BUILD_OPTS} :all-specs " } diff --git a/examples/simple_script/BUILD b/examples/simple_script/BUILD index 56dd4d3..1b4897f 100644 --- a/examples/simple_script/BUILD +++ b/examples/simple_script/BUILD @@ -4,6 +4,7 @@ load( "@bazelruby_ruby_rules//ruby:defs.bzl", "ruby_binary", "ruby_library", + "ruby_rspec_test", "ruby_test", ) @@ -32,17 +33,19 @@ ruby_binary( ) ruby_test( - name = "rspec", + name = "all-specs", timeout = "short", - srcs = [ + srcs = glob([ "script.rb", - "spec/lib/foo_spec.rb", - "spec/script_spec.rb", - "spec/spec_helper.rb", - "@bundle//:bin/rspec", - ], + "spec/**/*.rb", + "lib/**/*.rb", + ]), args = [ - "spec", + "--format documentation", + "--force-color", + "spec/spec_helper.rb", + "spec/script_spec.rb", + "spec/lib/foo_spec.rb", ], includes = [ ".", @@ -50,6 +53,26 @@ ruby_test( "spec", ], main = "spec/spec_helper.rb", + rubyopt = ["-rrspec/autorun"], + deps = [ + "//lib:foo", + "@bundle//:awesome_print", + "@bundle//:colored2", + "@bundle//:rspec", + "@bundle//:rspec-its", + ], +) + +ruby_rspec_test( + name = "all-rspecs", + srcs = [ + "script.rb", + "spec/lib/foo_spec.rb", + "spec/script_spec.rb", + "spec/spec_helper.rb", + ], + rubyopt = ["-rrspec/autorun"], + spec_target = "spec/script_spec.rb", deps = [ "//lib:foo", "@bundle//:awesome_print", @@ -59,7 +82,6 @@ ruby_test( ], ) -# TODO: not the best way of defining this, should make `rubocop` rule like `buildifier` ruby_binary( name = "rubocop", srcs = [ diff --git a/examples/simple_script/WORKSPACE b/examples/simple_script/WORKSPACE index ecb83d2..e7ba9ae 100644 --- a/examples/simple_script/WORKSPACE +++ b/examples/simple_script/WORKSPACE @@ -8,19 +8,23 @@ local_repository( ) load( - "@bazelruby_ruby_rules//ruby:deps.bzl", - "ruby_register_toolchains", + "@bazelruby_ruby_rules//ruby/private:dependencies.bzl", "ruby_rules_dependencies", ) ruby_rules_dependencies() -ruby_register_toolchains(version = "2.6.5") - load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") bazel_skylib_workspace() +load( + "@bazelruby_ruby_rules//ruby:deps.bzl", + "ruby_register_toolchains", +) + +ruby_register_toolchains(version = "2.6.5") + load("@bazelruby_ruby_rules//ruby:defs.bzl", "bundle_install") bundle_install( diff --git a/examples/simple_script/script.rb b/examples/simple_script/script.rb index e4853dd..cc3d793 100644 --- a/examples/simple_script/script.rb +++ b/examples/simple_script/script.rb @@ -11,5 +11,3 @@ def oss_rand end puts Foo.aha + ' ' + oss_rand - -ap Class.to_s diff --git a/ruby/defs.bzl b/ruby/defs.bzl index bd1476c..7052a41 100644 --- a/ruby/defs.bzl +++ b/ruby/defs.bzl @@ -15,10 +15,15 @@ load( "@bazelruby_ruby_rules//ruby/private:bundle.bzl", _ruby_bundle = "ruby_bundle", ) +load( + "@bazelruby_ruby_rules//ruby/private:rspec.bzl", + _ruby_rspec_test = "ruby_rspec_test", +) ruby_toolchain = _toolchain ruby_library = _library ruby_binary = _binary ruby_test = _test +ruby_rspec_test = _ruby_rspec_test bundle_install = _ruby_bundle ruby_bundle = _ruby_bundle diff --git a/ruby/private/binary.bzl b/ruby/private/binary.bzl index 1f20d4a..aeb3fbd 100644 --- a/ruby/private/binary.bzl +++ b/ruby/private/binary.bzl @@ -1,5 +1,4 @@ -load(":constants.bzl", "TOOLCHAIN_TYPE_NAME") -load(":providers.bzl", "RubyLibrary") +load(":constants.bzl", "RUBY_ATTRS", "TOOLCHAIN_TYPE_NAME") load( "//ruby/private/tools:deps.bzl", _transitive_deps = "transitive_deps", @@ -11,14 +10,19 @@ def _to_manifest_path(ctx, file): else: return ("%s/%s" % (ctx.workspace_name, file.short_path)) -def _ruby_binary_impl(ctx): +# Having this function allows us to override otherwise frozen attributes +# such as main, srcs and deps. We use this in ruby_rspec_test rule by +# adding rspec as a main, and sources, and rspec gem as a dependency. +# +# There could be similar situations in the future where we might want +# to create a rule (eg, rubocop) that does exactly the same. +def ruby_binary_macro(ctx, main, srcs, deps, args): sdk = ctx.toolchains[TOOLCHAIN_TYPE_NAME].ruby_runtime interpreter = sdk.interpreter[DefaultInfo].files_to_run.executable - main = ctx.file.main if not main: expected_name = "%s.rb" % ctx.attr.name - for f in ctx.attr.srcs: + for f in srcs: if f.label.name == expected_name: main = f.files.to_list()[0] break @@ -30,6 +34,7 @@ def _ruby_binary_impl(ctx): ) executable = ctx.actions.declare_file(ctx.attr.name) + deps = _transitive_deps( ctx, extra_files = [executable], @@ -55,41 +60,25 @@ def _ruby_binary_impl(ctx): data_runfiles = deps.data_files, )] -_ATTRS = { - "srcs": attr.label_list( - allow_files = True, - ), - "deps": attr.label_list( - providers = [RubyLibrary], - ), - "includes": attr.string_list(), - "rubyopt": attr.string_list(), - "data": attr.label_list( - allow_files = True, - ), - "main": attr.label( - allow_single_file = True, - ), - "_wrapper_template": attr.label( - allow_single_file = True, - default = "binary_wrapper.tpl", - ), - "_misc_deps": attr.label_list( - allow_files = True, - default = ["@bazel_tools//tools/bash/runfiles"], - ), -} +def ruby_binary_impl(ctx): + return ruby_binary_macro( + ctx, + ctx.file.main, + ctx.attr.srcs, + ctx.attr.deps, + ctx.attr.args, + ) ruby_binary = rule( - implementation = _ruby_binary_impl, - attrs = _ATTRS, + implementation = ruby_binary_impl, + attrs = RUBY_ATTRS, executable = True, toolchains = [TOOLCHAIN_TYPE_NAME], ) ruby_test = rule( - implementation = _ruby_binary_impl, - attrs = _ATTRS, + implementation = ruby_binary_impl, + attrs = RUBY_ATTRS, test = True, toolchains = [TOOLCHAIN_TYPE_NAME], ) diff --git a/ruby/private/bundle/bundle.bzl b/ruby/private/bundle/bundle.bzl index 46bc314..ee12cd8 100644 --- a/ruby/private/bundle/bundle.bzl +++ b/ruby/private/bundle/bundle.bzl @@ -1,54 +1,40 @@ -load("//ruby/private:constants.bzl", "RULES_RUBY_WORKSPACE_NAME") +load( + "//ruby/private:constants.bzl", + "BUNDLE_ATTRS", + "RULES_RUBY_WORKSPACE_NAME", +) +load("//ruby/private:providers.bzl", "RubyRuntimeContext") +load("//ruby/private/tools:deprecations.bzl", "deprecated_attribute") BUNDLE_INSTALL_PATH = "lib" BUNDLE_BINARY = "bundler/exe/bundler" +SCRIPT_INSTALL_GEM = "install_bundler.rb" +SCRIPT_BUILD_FILE_GENERATOR = "create_bundle_build_file.rb" -# TODO: do not hard-code this -GEM_HOME = "lib/ruby/2.5.0/" - -def upgrade_bundler(ctx, interpreter, environment): +def run_bundler(runtime_ctx, extra_args): # Now we are running bundle install args = [ - interpreter, # ruby - "--enable=gems", # bundler must run with rubygems enabled - "", - ".", - "-I", # Used to tell Ruby where to load the library scripts - "lib", # Add vendor/bundle to the list of resolvers - BUNDLE_BINARY, # our binary - ] + extra_args - - # print("running bundler with args\n", args) - - result = ctx.execute( - args, - quiet = False, - ) - - return result -def run_bundler(ctx, interpreter, environment, extra_args): - # Now we are running bundle install - args = [ - interpreter, # ruby + runtime_ctx.interpreter, # ruby "--enable=gems", # bundler must run with rubygems enabled "-I", ".", "-I", # Used to tell Ruby where to load the library scripts - "lib", # Add vendor/bundle to the list of resolvers + BUNDLE_INSTALL_PATH, # Add vendor/bundle to the list of resolvers BUNDLE_BINARY, # our binary ] + extra_args # print("running bundler with args\n", args) - result = ctx.execute( + result = runtime_ctx.ctx.execute( args, quiet = False, + environment = runtime_ctx.environment, ) return result # Sets local bundler config values -def set_bundler_config(ctx, interpreter, environment): +def set_bundler_config(runtime_ctx): # Bundler is deprecating various flags in favor of the configuration. # HOWEVER — for reasons I can't explain, Bazel runs "bundle install" *prior* # to setting these flags. So the flags are then useless until we can force the @@ -67,14 +53,9 @@ def set_bundler_config(ctx, interpreter, environment): } for option, value in [(option, value) for option, value in bundler_config.items()]: - args = [ - "config", - "--local", - option, - value, - ] - - result = run_bundler(ctx, interpreter, environment, args) + args = ["config", "--local", value, option] + + result = run_bundler(runtime_ctx, args) if result.return_code: message = "Failed to set bundle config {} to {}: {}".format( option, @@ -83,49 +64,37 @@ def set_bundler_config(ctx, interpreter, environment): ) fail(message) -def install_bundler(ctx, interpreter, install_bundler, dest, version, environment): - args = [interpreter, install_bundler, version, dest] - - result = ctx.execute(args, environment = environment) +def install_pure_ruby_gem(runtime_ctx, gem_name, gem_version, folder): + # USAGE: ./install_bundler.rb gem-name gem-version destination-folder + args = [ + runtime_ctx.interpreter, + SCRIPT_INSTALL_GEM, + gem_name, + gem_version, + folder, + ] + result = runtime_ctx.ctx.execute(args, environment = runtime_ctx.environment) if result.return_code: - message = "Failed to evaluate ruby snippet with {}: {}".format( - interpreter, + message = "Failed to install gem {}-{} to {} with {}: {}".format( + gem_name, + gem_version, + folder, + runtime_ctx.interpreter, result.stderr, ) fail(message) -def bundle_install_impl(ctx): - ctx.symlink(ctx.attr.gemfile, "Gemfile") - ctx.symlink(ctx.attr.gemfile_lock, "Gemfile.lock") - ctx.symlink(ctx.attr._create_bundle_build_file, "create_bundle_build_file.rb") - ctx.symlink(ctx.attr._install_bundler, "install_bundler.rb") - - environment = {"RUBYOPT": "--enable-gems", "GEM_HOME": GEM_HOME} - - ruby = ctx.attr.ruby_interpreter - interpreter = ctx.path(ruby) - - install_bundler( - ctx, - interpreter, - "install_bundler.rb", +def install_bundler(runtime_ctx, bundler_version): + return install_pure_ruby_gem( + runtime_ctx, + "bundler", + bundler_version, "bundler", - ctx.attr.version, - environment, - ) - bundler = Label("//:" + BUNDLE_BINARY) - - # Set Bundler config in the .bundle/config file - set_bundler_config( - ctx, - interpreter, - environment, ) +def bundle_install(runtime_ctx): result = run_bundler( - ctx, - interpreter, - environment, + runtime_ctx, [ "install", # > bundle install "--standalone", # Makes a bundle that can work without depending on Rubygems or Bundler at runtime. @@ -135,72 +104,59 @@ def bundle_install_impl(ctx): ) if result.return_code: - fail("Failed to install gems: %s%s" % (result.stdout, result.stderr)) + fail("bundle install failed: %s%s" % (result.stdout, result.stderr)) +def generate_bundle_build_file(runtime_ctx): # Create the BUILD file to expose the gems to the WORKSPACE args = [ - ctx.path(ruby), # ruby interpreter + runtime_ctx.interpreter, # ruby interpreter "--enable=gems", # prevent the addition of gem installation directories to the default load path "-I", # -I lib (adds this folder to $LOAD_PATH where ruby searchesf for things) "bundler/lib", - "create_bundle_build_file.rb", # The template used to created bundle file + SCRIPT_BUILD_FILE_GENERATOR, # The template used to created bundle file "BUILD.bazel", # Bazel build file (can be empty) "Gemfile.lock", # Gemfile.lock where we list all direct and transitive dependencies - ctx.name, # Name of the target - repr(ctx.attr.excludes), + runtime_ctx.ctx.name, # Name of the target + repr(runtime_ctx.ctx.attr.excludes), RULES_RUBY_WORKSPACE_NAME, ] - result = ctx.execute(args, quiet = False) + result = runtime_ctx.ctx.execute(args, quiet = False) if result.return_code: - fail("Failed to create build file: %s%s" % (result.stdout, result.stderr)) - - ctx.template( - "BUILD.bazel", - ctx.attr._buildfile_template, - substitutions = { - "{repo_name}": ctx.name, - "{workspace_name}": RULES_RUBY_WORKSPACE_NAME, - }, - ) + fail("build file generation failed: %s%s" % (result.stdout, result.stderr)) + +def bundle_install_impl(ctx): + ctx.symlink(ctx.attr.gemfile, "Gemfile") + ctx.symlink(ctx.attr.gemfile_lock, "Gemfile.lock") + ctx.symlink(ctx.attr._create_bundle_build_file, SCRIPT_BUILD_FILE_GENERATOR) + ctx.symlink(ctx.attr._install_bundler, SCRIPT_INSTALL_GEM) + + # version is too generic for this operation + deprecated_attribute(ctx, "version", "bundler_version") + + # Setup this provider that we pass around between functions for convenience + runtime_ctx = RubyRuntimeContext( + ctx = ctx, + interpreter = ctx.path(ctx.attr.ruby_interpreter), + environment = {"RUBYOPT": "--enable-gems"}, + ) + + # 1. Install the right version of the Bundler Gem + install_bundler(runtime_ctx, ctx.attr.bundler_version) + + # Create label for the Bundler executable + bundler = Label("//:" + BUNDLE_BINARY) + + # 2. Set Bundler config in the .bundle/config file + set_bundler_config(runtime_ctx) + + # 3. Run bundle install + bundle_install(runtime_ctx) + + # 4. Generate the BUILD file for the bundle + generate_bundle_build_file(runtime_ctx) ruby_bundle = repository_rule( implementation = bundle_install_impl, - attrs = { - "ruby_sdk": attr.string( - default = "@org_ruby_lang_ruby_toolchain", - ), - "ruby_interpreter": attr.label( - default = "@org_ruby_lang_ruby_toolchain//:ruby", - ), - "gemfile": attr.label( - allow_single_file = True, - mandatory = True, - ), - "gemfile_lock": attr.label( - allow_single_file = True, - ), - "version": attr.string( - default = "2.1.2", - ), - "gemspec": attr.label( - allow_single_file = True, - ), - "excludes": attr.string_list_dict( - doc = "List of glob patterns per gem to be excluded from the library", - ), - "_install_bundler": attr.label( - default = "%s//ruby/private/bundle:install_bundler.rb" % ( - RULES_RUBY_WORKSPACE_NAME - ), - allow_single_file = True, - ), - "_create_bundle_build_file": attr.label( - default = "%s//ruby/private/bundle:create_bundle_build_file.rb" % ( - RULES_RUBY_WORKSPACE_NAME - ), - doc = "Creates the BUILD file", - allow_single_file = True, - ), - }, + attrs = BUNDLE_ATTRS, ) diff --git a/ruby/private/bundle/create_bundle_build_file.rb b/ruby/private/bundle/create_bundle_build_file.rb old mode 100644 new mode 100755 index 2e54122..c1af65d --- a/ruby/private/bundle/create_bundle_build_file.rb +++ b/ruby/private/bundle/create_bundle_build_file.rb @@ -1,3 +1,4 @@ +#!/usr/bin/env ruby # frozen_string_literal: true TEMPLATE = 'load( @@ -71,7 +72,7 @@ def create_bundle_build_file(build_out_file, lock_file, repo_name, excludes, wor .gsub("{ruby_version}", ruby_version) # strip bundler version so we can process this file - remove_bunder_version!(lock_file) + remove_bundler_version!(lock_file) # Append to the end specific gem libraries and dependencies bundle = Bundler::LockfileParser.new(Bundler.read_file(lock_file)) diff --git a/ruby/private/bundle/install_bundler.rb b/ruby/private/bundle/install_bundler.rb old mode 100644 new mode 100755 index 24905bb..e08bf16 --- a/ruby/private/bundle/install_bundler.rb +++ b/ruby/private/bundle/install_bundler.rb @@ -1,5 +1,6 @@ +#!/usr/bin/env ruby # frozen_string_literal: true - +# require 'rubygems' require 'rubygems/name_tuple' require 'rubygems/package' @@ -21,9 +22,13 @@ def unpack_gem(name, version, dest = Dir.pwd) end def main - version = ARGV[0] - dir = ARGV[1] || Dir.pwd - unpack_gem('bundler', version, dir) + gem_name, gem_version, dir, _ = *ARGV + dir ||= Dir.pwd + unless gem_name && gem_version + puts "USAGE: #{$0} gem-name gem-version destination-folder" + exit 1 + end + unpack_gem(gem_name, gem_version, dir) end if $0 == __FILE__ diff --git a/ruby/private/constants.bzl b/ruby/private/constants.bzl index 0363c2f..4983be9 100644 --- a/ruby/private/constants.bzl +++ b/ruby/private/constants.bzl @@ -1,2 +1,94 @@ +load(":providers.bzl", "RubyLibrary") +load("@bazel_skylib//lib:dicts.bzl", "dicts") + RULES_RUBY_WORKSPACE_NAME = "@bazelruby_ruby_rules" TOOLCHAIN_TYPE_NAME = "%s//ruby:toolchain_type" % RULES_RUBY_WORKSPACE_NAME +DEFAULT_BUNDLER_VERSION = "2.1.2" + +RUBY_ATTRS = { + "srcs": attr.label_list( + allow_files = True, + ), + "deps": attr.label_list( + providers = [RubyLibrary], + ), + "includes": attr.string_list(), + "rubyopt": attr.string_list(), + "data": attr.label_list( + allow_files = True, + ), + "main": attr.label( + allow_single_file = True, + ), + "_wrapper_template": attr.label( + allow_single_file = True, + default = "binary_wrapper.tpl", + ), + "_misc_deps": attr.label_list( + allow_files = True, + default = ["@bazel_tools//tools/bash/runfiles"], + ), +} + +_RSPEC_ATTRS = { + "bundle": attr.string( + default = "@bundle//", + doc = "Name of the bundle where the rspec gem can be found, eg @bundle//", + ), + "rspec_args": attr.string_list( + default = ["--format documentation", "--force-color", "-p2"], + doc = "Arguments passed to rspec executable", + ), + "spec_executable": attr.label( + default = "@bundle//:bin/rspec", + allow_single_file = True, + doc = "RSpec executable", + ), + "spec_target": attr.label( + allow_single_file = True, + doc = "Directory or local file to tests", + ), +} + +RSPEC_ATTRS = dicts.add(RUBY_ATTRS, _RSPEC_ATTRS) + +BUNDLE_ATTRS = { + "ruby_sdk": attr.string( + default = "@org_ruby_lang_ruby_toolchain", + ), + "ruby_interpreter": attr.label( + default = "@org_ruby_lang_ruby_toolchain//:ruby", + ), + "gemfile": attr.label( + allow_single_file = True, + mandatory = True, + ), + "gemfile_lock": attr.label( + allow_single_file = True, + ), + "version": attr.string( + mandatory = False, + ), + "bundler_version": attr.string( + default = DEFAULT_BUNDLER_VERSION, + ), + "gemspec": attr.label( + allow_single_file = True, + ), + "excludes": attr.string_list_dict( + doc = "List of glob patterns per gem to be excluded from the library", + ), + "_install_bundler": attr.label( + default = "%s//ruby/private/bundle:install_bundler.rb" % ( + RULES_RUBY_WORKSPACE_NAME + ), + allow_single_file = True, + ), + "_create_bundle_build_file": attr.label( + default = "%s//ruby/private/bundle:create_bundle_build_file.rb" % ( + RULES_RUBY_WORKSPACE_NAME + ), + doc = "Creates the BUILD file", + allow_single_file = True, + ), +} diff --git a/ruby/private/providers.bzl b/ruby/private/providers.bzl index 2101bdd..ee032cd 100644 --- a/ruby/private/providers.bzl +++ b/ruby/private/providers.bzl @@ -5,3 +5,12 @@ RubyLibrary = provider( "rubyopt", ], ) + +RubyRuntimeContext = provider( + doc = "Carries info required to execute Ruby Scripts", + fields = [ + "ctx", + "interpreter", + "environment", + ], +) diff --git a/ruby/private/rspec.bzl b/ruby/private/rspec.bzl new file mode 100644 index 0000000..b5b2899 --- /dev/null +++ b/ruby/private/rspec.bzl @@ -0,0 +1,28 @@ +load(":constants.bzl", "RSPEC_ATTRS", "TOOLCHAIN_TYPE_NAME") +load(":binary.bzl", "ruby_binary_macro") + +def _ruby_rspec(ctx): + bundle = ctx.attr.bundle + + rspec_executable = ctx.file.spec_executable + rspec_gem = Label("%s:rspec" % (bundle)) + args = ctx.attr.rspec_args + ctx.attr.args + + if ctx.attr.spec_target: + spec_file = ctx.file.spec_target + args.append(spec_file.path) + + return ruby_binary_macro( + ctx, + rspec_executable, + ctx.attr.srcs + [rspec_executable], + ctx.attr.deps + [rspec_gem], + args, + ) + +ruby_rspec_test = rule( + implementation = _ruby_rspec, + attrs = RSPEC_ATTRS, + test = True, + toolchains = [TOOLCHAIN_TYPE_NAME], +) diff --git a/ruby/private/tools/deprecations.bzl b/ruby/private/tools/deprecations.bzl new file mode 100644 index 0000000..b77f3af --- /dev/null +++ b/ruby/private/tools/deprecations.bzl @@ -0,0 +1,12 @@ +def deprecated_attribute( + ctx, + old_attribute_name, + new_attribute_name = None, + action = None): + if getattr(ctx.attr, old_attribute_name) != None: + if action != None: + print("Attribute \"%s\" is deprecated — \"%s\"" % (old_attribute_name, action)) + elif new_attribute_name != None: + print("Attribute \"%s\" is deprecated in favor of \"%s\"" % (old_attribute_name, new_attribute_name)) + else: + print("Attribute \"%s\" is deprecated. Please do not use it." % (old_attribute_name)) From 3242b0aef0096e142d7f1a7fc15d34e9720a3543 Mon Sep 17 00:00:00 2001 From: Konstantin Gredeskoul Date: Thu, 2 Jan 2020 14:55:12 -0800 Subject: [PATCH 04/19] Working ruby_rspec macro + rule; tests should pass --- .circleci/config.yml | 27 +--- README.md | 86 +++++++++-- bin/test-suite | 5 +- examples/simple_script/.rubocop.yml | 2 +- examples/simple_script/BUILD | 107 ------------- examples/simple_script/BUILD.bazel | 141 ++++++++++++++++++ examples/simple_script/spec/spec_helper.rb | 32 ++-- ruby/defs.bzl | 2 + ruby/deps.bzl | 5 +- ruby/private/binary.bzl | 13 +- ruby/private/bundle/bundle.bzl | 39 +++-- .../bundle/create_bundle_build_file.rb | 102 ++++++++----- ruby/private/bundle/install_bundler.rb | 8 +- ruby/private/constants.bzl | 18 +-- ruby/private/rspec.bzl | 64 ++++++-- ruby/tests/BUILD.bazel | 11 ++ 16 files changed, 406 insertions(+), 256 deletions(-) delete mode 100644 examples/simple_script/BUILD create mode 100644 examples/simple_script/BUILD.bazel diff --git a/.circleci/config.yml b/.circleci/config.yml index 7b2a76a..868e720 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -61,32 +61,9 @@ jobs: /usr/bin/env bash bin/setup - run: - name: "Show Bazel Info" + name: "Bazel Build & Test Example" command: | - /usr/bin/env bash bin/test-suite bazel-info - - - run: - name: "Bazel Simple Script Example Build" - command: | - cd examples/simple_script && bazel ${BAZEL_OPTS} build ${BAZEL_BUILD_OPTS} -- :all - - - run: - name: "Bazel Simple Script Example Rubocop Check" - command: | - cd examples/simple_script && bazel ${BAZEL_OPTS} run ${BAZEL_BUILD_OPTS} -- :rubocop || true - - - run: - name: "Bazel Simple Script Example Test" - command: | - cd examples/simple_script - bazel ${BAZEL_OPTS} test -s --verbose_failures --sandbox_debug \ - --test_output=streamed --test_verbose_timeout_warnings :all-specs - - - run: - name: "Print output log" - command: | - cat /home/circleci/.cache/bazel/_bazel_circleci/86ef9d8d260271208b18f4c01debfa13/execroot/bazelruby_ruby_rules_example/bazel-out/k8-fastbuild/testlogs/all-specs/test.log - + /usr/bin/env bash bin/test-suite simple-script buildifier: <<: *bazel_defaults diff --git a/README.md b/README.md index 7804192..6cfaee6 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ -* [Status:](#status) * [Usage](#usage) + * [WORKSPACE File](#workspace-file) + * [BUILD.bazel files](#buildbazel-files) * [Rules](#rules) * [ruby_library](#ruby_library) * [ruby_binary](#ruby_binary) * [ruby_test](#ruby_test) - * [bundle_install](#bundle_install) + * [ruby_bundle](#ruby_bundle) * [What's coming next](#whats-coming-next) * [Contributing](#contributing) * [Setup](#setup) @@ -35,14 +36,14 @@ Ruby rules for [Bazel](https://bazel.build). -## Status: +** Current Status:** *Work in progress.* -**Work in progress.** - -We have a guide on [Building your first Ruby Project](https://github.com/bazelruby/rules_ruby/wiki/Build-your-ruby-project) on the Wiki. We encourage you to check it out. +Note: we have a short guide on [Building your first Ruby Project](https://github.com/bazelruby/rules_ruby/wiki/Build-your-ruby-project) on the Wiki. We encourage you to check it out. ## Usage +### `WORKSPACE` File + Add `ruby_rules_dependencies` and `ruby_register_toolchains` into your `WORKSPACE` file. ```python @@ -65,6 +66,33 @@ ruby_rules_dependencies() ruby_register_toolchains() ``` +Next, add any external Gem dependencies you may have via `ruby_bundle` command. +The name of the bundle becomes a reference to this particular Gemfile.lock. + +Install external gems that can be later referenced as `@//:`, +and the executables from each gem can be accessed as `@` +for instance, `@bundle//:bin/rubocop`. + +You can install more than one bundle per WORKSPACE, but that's not recommended. + +```python +ruby_bundle( + name = "bundle", + gemfile = ":Gemfile", + gemfile_lock = ":Gemfile.lock", + bundler_version = "2.1.2", +) + +ruby_bundle( + name = "bundle_app_shopping", + gemfile = "//apps/shopping:Gemfile", + gemfile_lock = "//apps/shopping:Gemfile.lock", + bundler_version = "2.1.2", +) +``` + +### `BUILD.bazel` files + Add `ruby_library`, `ruby_binary` or `ruby_test` into your `BUILD.bazel` files. ```python @@ -73,12 +101,18 @@ load( "ruby_binary", "ruby_library", "ruby_test", + "ruby_rspec", ) ruby_library( name = "foo", - srcs = ["lib/foo.rb"], + srcs = glob(["lib/**/*.rb"]), includes = ["lib"], + deps = [ + "@bundle//:activesupport", + "@bundle//:awesome_print", + "@bundle//:rubocop", + ] ) ruby_binary( @@ -92,6 +126,14 @@ ruby_test( srcs = ["test/foo_test.rb"], deps = [":foo"], ) + +ruby_rspec( + name = "foo_spec", + specs = glob(["spec/**/*.rb]), + rspec_args = { "--format": "progress" }, + deps = [":foo"] +} + ``` ## Rules @@ -338,7 +380,7 @@ ruby_test(name, deps, srcs, data, main, compatible_with, deprecation, distribs, -### `bundle_install` +### `ruby_bundle` Installs gems with Bundler, and make them available as a `ruby_library`. @@ -361,9 +403,9 @@ ruby_rules_dependencies() ruby_register_toolchains() -load("@bazelruby_ruby_rules//ruby:defs.bzl", "bundle_install") +load("@bazelruby_ruby_rules//ruby:defs.bzl", "ruby_bundle") -bundle_install( +ruby_bundle( name = "gems", gemfile = "//:Gemfile", gemfile_lock = "//:Gemfile.lock", @@ -376,12 +418,24 @@ Example: `lib/BUILD.bazel`: ruby_library( name = "foo", srcs = ["foo.rb"], - deps = ["@gems//:libs"], + deps = ["@gems//:all"], +) +``` + +Or declare many gems in your `Gemfile`, and only use some of them in each ruby library: + +```python +ruby_binary( + name = "rubocop", + srcs = [":foo", ".rubocop.yml"], + args = ["-P", "-D", "-c" ".rubocop.yml"], + main = "@gems//:bin/rubocop", + deps = ["@gems//:rubocop"], ) ```
-bundle_install(name, gemfile, gemfile_lock)
+ruby_bundle(name, gemfile, gemfile_lock, bundler_version = "2.1.2")
 
@@ -418,6 +472,14 @@ bundle_install(name, gemfile, gemfile_lock)

NOTE: This rule never updates the Gemfile.lock. It is your responsibility to generate/update Gemfile.lock

+ + + +
bundler_version + String, optional +

The Version of Bundler to use. Defaults to 2.1.2.

+

NOTE: This rule never updates the Gemfile.lock. It is your responsibility to generate/update Gemfile.lock

+
diff --git a/bin/test-suite b/bin/test-suite index bb4c762..6b489ea 100755 --- a/bin/test-suite +++ b/bin/test-suite @@ -148,8 +148,9 @@ test::simple-script() { __test::exec simple-script " cd examples/simple_script ${Bazel__BuildSteps} - bazel ${BAZEL_OPTS} build ${BAZEL_BUILD_OPTS} :all; - bazel ${BAZEL_OPTS} test ${BAZEL_BUILD_OPTS} :all-specs + echo run :bin; bazel ${BAZEL_OPTS} run ${BAZEL_BUILD_OPTS} :bin + echo run :rubocop; bazel ${BAZEL_OPTS} run ${BAZEL_BUILD_OPTS} :rubocop + " } diff --git a/examples/simple_script/.rubocop.yml b/examples/simple_script/.rubocop.yml index dc53049..7b653d9 100644 --- a/examples/simple_script/.rubocop.yml +++ b/examples/simple_script/.rubocop.yml @@ -1,4 +1,4 @@ -inherit_from: ~/.relaxed-rubocop-2.4.yml +inherit_from: .relaxed-rubocop-2.4.yml AllCops: TargetRubyVersion: 2.6 diff --git a/examples/simple_script/BUILD b/examples/simple_script/BUILD deleted file mode 100644 index 1b4897f..0000000 --- a/examples/simple_script/BUILD +++ /dev/null @@ -1,107 +0,0 @@ -package(default_visibility = ["//:__subpackages__"]) - -load( - "@bazelruby_ruby_rules//ruby:defs.bzl", - "ruby_binary", - "ruby_library", - "ruby_rspec_test", - "ruby_test", -) - -ruby_library( - name = "script.lib", - srcs = [ - "script.rb", - "//lib:foo", - ], - deps = [ - "@bundle//:awesome_print", - "@bundle//:colored2", - ], -) - -ruby_binary( - name = "bin", - srcs = ["script.rb"], - main = "script.rb", - deps = [ - ":script.lib", - "//lib:foo", - "@bundle//:awesome_print", - "@bundle//:colored2", - ], -) - -ruby_test( - name = "all-specs", - timeout = "short", - srcs = glob([ - "script.rb", - "spec/**/*.rb", - "lib/**/*.rb", - ]), - args = [ - "--format documentation", - "--force-color", - "spec/spec_helper.rb", - "spec/script_spec.rb", - "spec/lib/foo_spec.rb", - ], - includes = [ - ".", - "lib", - "spec", - ], - main = "spec/spec_helper.rb", - rubyopt = ["-rrspec/autorun"], - deps = [ - "//lib:foo", - "@bundle//:awesome_print", - "@bundle//:colored2", - "@bundle//:rspec", - "@bundle//:rspec-its", - ], -) - -ruby_rspec_test( - name = "all-rspecs", - srcs = [ - "script.rb", - "spec/lib/foo_spec.rb", - "spec/script_spec.rb", - "spec/spec_helper.rb", - ], - rubyopt = ["-rrspec/autorun"], - spec_target = "spec/script_spec.rb", - deps = [ - "//lib:foo", - "@bundle//:awesome_print", - "@bundle//:colored2", - "@bundle//:rspec", - "@bundle//:rspec-its", - ], -) - -ruby_binary( - name = "rubocop", - srcs = [ - ".relaxed-rubocop-2.4.yml", - ".rubocop.yml", - "script.rb", - "spec/lib/foo_spec.rb", - "spec/script_spec.rb", - "spec/spec_helper.rb", - "@bundle//:bin/rubocop", - ], - args = [ - "-c", - ".rubocop.yml", - "-P", - "-D", - ], - main = "@bundle//:bin/rubocop", - deps = [ - "//lib:foo", - "@bundle//:rubocop", - ], -) diff --git a/examples/simple_script/BUILD.bazel b/examples/simple_script/BUILD.bazel new file mode 100644 index 0000000..89eac23 --- /dev/null +++ b/examples/simple_script/BUILD.bazel @@ -0,0 +1,141 @@ +package(default_visibility = ["//:__subpackages__"]) + +load( + "@bazelruby_ruby_rules//ruby:defs.bzl", + "ruby_binary", + "ruby_library", + "ruby_rspec", + "ruby_test", +) + +filegroup( + name = "sources", + srcs = glob([ + "script.rb", + "lib/*.rb", + ]), +) + +filegroup( + name = "spec_sources", + srcs = glob([ + "spec/**/*.rb", + ]), +) + +ruby_library( + name = "lib", + srcs = [ + ":sources", + ":spec_sources", + ], + deps = [ + "@bundle//:awesome_print", + "@bundle//:colored2", + ], +) + +ruby_binary( + name = "bin", + srcs = ["script.rb"], + main = "script.rb", + deps = [ + ":lib", + "//lib:foo", + "@bundle//:awesome_print", + "@bundle//:colored2", + ], +) + +# This is an example of the RSpec definition that uses autorun +# and points to spec_helper as the main spec file. It specifies +# which specs to run using the args. +ruby_test( + name = "rspec-autorun", + timeout = "short", + srcs = [ + ":sources", + ":spec_sources", + ], + args = [ + "--format documentation", + "--force-color", + ] + glob([ + "spec/**/*.rb", + ]), + main = "spec/spec_helper.rb", + rubyopt = ["-rrspec/autorun"], + deps = [ + "@bundle//:awesome_print", + "@bundle//:colored2", + "@bundle//:rspec", + "@bundle//:rspec-its", + ], +) + +# This is a similar example, except instead of using rubyopt to load +# rspec, we execute rspec executable located in the bin folder under +# the bundle, accessible via @bundle//:bin/rspec +ruby_test( + name = "rspec-binary", + timeout = "short", + srcs = [ + ":sources", + ":spec_sources", + "@bundle//:bin/rspec", + ], + args = [ + "--format documentation", + "--force-color", + ] + glob([ + "spec/**/*.rb", + ]), + main = "@bundle//:bin/rspec", + deps = [ + "@bundle//:awesome_print", + "@bundle//:colored2", + "@bundle//:rspec", + "@bundle//:rspec-its", + ], +) + +# Finally, this is the short version of the same thing, expressed +# via the ruby_rspec_test rule that does what the above example +# shows but encapsulated in the rule itself. It adds rspec and rspec-its +# gems to the dependency list, executes bin/rspec and passes spec_targets +# as arguments to rspec. +ruby_rspec( + name = "ruby-rspec-test", + srcs = [ + ":sources", + ":spec_sources", + ], + specs = glob([ + "spec/**/*.rb", + ]), + deps = [ + "@bundle//:awesome_print", + "@bundle//:colored2", + ], +) + +ruby_binary( + name = "rubocop", + srcs = [ + ".relaxed-rubocop-2.4.yml", + ".rubocop.yml", + "@bundle//:bin/rubocop", + ], + args = [ + "-c", + ".rubocop.yml", + "-P", + "-D", + ], + main = "@bundle//:bin/rubocop", + deps = [ + ":lib", + "//lib:foo", + "@bundle//:rubocop", + ], +) diff --git a/examples/simple_script/spec/spec_helper.rb b/examples/simple_script/spec/spec_helper.rb index e8a4d70..b0866bf 100644 --- a/examples/simple_script/spec/spec_helper.rb +++ b/examples/simple_script/spec/spec_helper.rb @@ -1,5 +1,18 @@ # frozen_string_literal: true +# Sets HOME here because: +# If otherwise, it causes a runtime failure with the following steps. +# 1. RSpec::Core::ConfigurationOptions.global_options_file raises an exception +# because $HOME is not set in the sandbox environment of Bazel +# 2. the rescue clause calls RSpec::Support.#warning +# 3. #warning calls #warn_with +# 4. #warn_with tries to lookup the first caller which is not a part of RSpec. +# But all the call stack entires are about RSpec at this time because +# it is invoked by rpsec/autorun. So #warn_with raises an exception +# 5. The process fails with an unhandled exception. + +ENV['HOME'] ||= '/' + require 'rspec' require 'rspec/its' require 'awesome_print' @@ -23,22 +36,3 @@ config.order = :random Kernel.srand config.seed end - -if $0 == 'rspec' - require_relative '../script' - require_relative '../lib/foo' - require_relative './script_spec' - require_relative './lib/foo_spec' -end - -# Sets HOME here because: -# If otherwise, it causes a runtime failure with the following steps. -# 1. RSpec::Core::ConfigurationOptions.global_options_file raises an exception -# because $HOME is not set in the sandbox environment of Bazel -# 2. the rescue clause calls RSpec::Support.#warning -# 3. #warning calls #warn_with -# 4. #warn_with tries to lookup the first caller which is not a part of RSpec. -# But all the call stack entires are about RSpec at this time because -# it is invoked by rpsec/autorun. So #warn_with raises an exception -# 5. The process fails with an unhandled exception. -# ENV['HOME'] ||= '/' diff --git a/ruby/defs.bzl b/ruby/defs.bzl index 7052a41..abc31c5 100644 --- a/ruby/defs.bzl +++ b/ruby/defs.bzl @@ -17,6 +17,7 @@ load( ) load( "@bazelruby_ruby_rules//ruby/private:rspec.bzl", + _ruby_rspec = "ruby_rspec", _ruby_rspec_test = "ruby_rspec_test", ) @@ -25,5 +26,6 @@ ruby_library = _library ruby_binary = _binary ruby_test = _test ruby_rspec_test = _ruby_rspec_test +ruby_rspec = _ruby_rspec bundle_install = _ruby_bundle ruby_bundle = _ruby_bundle diff --git a/ruby/deps.bzl b/ruby/deps.bzl index 4623094..9b1455f 100644 --- a/ruby/deps.bzl +++ b/ruby/deps.bzl @@ -1,12 +1,13 @@ # Repository rules load( "@bazelruby_ruby_rules//ruby/private:dependencies.bzl", - _rules_dependencies = "ruby_rules_dependencies", + _load_ruby_rules_dependencies = "ruby_rules_dependencies", ) load( "@bazelruby_ruby_rules//ruby/private:sdk.bzl", _register_toolchains = "ruby_register_toolchains", ) -ruby_rules_dependencies = _rules_dependencies +load_ruby_rules_dependencies = _load_ruby_rules_dependencies + ruby_register_toolchains = _register_toolchains diff --git a/ruby/private/binary.bzl b/ruby/private/binary.bzl index aeb3fbd..6d34b48 100644 --- a/ruby/private/binary.bzl +++ b/ruby/private/binary.bzl @@ -16,7 +16,7 @@ def _to_manifest_path(ctx, file): # # There could be similar situations in the future where we might want # to create a rule (eg, rubocop) that does exactly the same. -def ruby_binary_macro(ctx, main, srcs, deps, args): +def ruby_binary_macro(ctx, main, srcs): sdk = ctx.toolchains[TOOLCHAIN_TYPE_NAME].ruby_runtime interpreter = sdk.interpreter[DefaultInfo].files_to_run.executable @@ -54,19 +54,18 @@ def ruby_binary_macro(ctx, main, srcs, deps, args): }, ) - return [DefaultInfo( + info = DefaultInfo( executable = executable, - default_runfiles = deps.default_files, - data_runfiles = deps.data_files, - )] + runfiles = deps.default_files.merge(deps.data_files), + ) + + return [info] def ruby_binary_impl(ctx): return ruby_binary_macro( ctx, ctx.file.main, ctx.attr.srcs, - ctx.attr.deps, - ctx.attr.args, ) ruby_binary = rule( diff --git a/ruby/private/bundle/bundle.bzl b/ruby/private/bundle/bundle.bzl index ee12cd8..6f1b323 100644 --- a/ruby/private/bundle/bundle.bzl +++ b/ruby/private/bundle/bundle.bzl @@ -6,12 +6,17 @@ load( load("//ruby/private:providers.bzl", "RubyRuntimeContext") load("//ruby/private/tools:deprecations.bzl", "deprecated_attribute") -BUNDLE_INSTALL_PATH = "lib" +BUNDLE_BIN_PATH = "bin" +BUNDLE_PATH = "lib" BUNDLE_BINARY = "bundler/exe/bundler" SCRIPT_INSTALL_GEM = "install_bundler.rb" SCRIPT_BUILD_FILE_GENERATOR = "create_bundle_build_file.rb" -def run_bundler(runtime_ctx, extra_args): +# Runs bundler with arbitrary arguments +# +# eg: run_bundler(runtime_ctx, [ "lock", " --gemfile", "Gemfile.rails5" ]) +# +def run_bundler(runtime_ctx, bundler_arguments): # Now we are running bundle install args = [ runtime_ctx.interpreter, # ruby @@ -19,22 +24,25 @@ def run_bundler(runtime_ctx, extra_args): "-I", ".", "-I", # Used to tell Ruby where to load the library scripts - BUNDLE_INSTALL_PATH, # Add vendor/bundle to the list of resolvers + BUNDLE_PATH, # Add vendor/bundle to the list of resolvers BUNDLE_BINARY, # our binary - ] + extra_args + ] + bundler_arguments - # print("running bundler with args\n", args) + # print("Bundler Command:\n\n", args) - result = runtime_ctx.ctx.execute( + return runtime_ctx.ctx.execute( args, quiet = False, environment = runtime_ctx.environment, ) - return result - -# Sets local bundler config values -def set_bundler_config(runtime_ctx): +# +# Sets local bundler config values by calling +# +# $ bundler config --local | --global config-option config-value +# +# @config_category can be either 'local' or 'global' +def set_bundler_config(runtime_ctx, config_category = "local"): # Bundler is deprecating various flags in favor of the configuration. # HOWEVER — for reasons I can't explain, Bazel runs "bundle install" *prior* # to setting these flags. So the flags are then useless until we can force the @@ -43,17 +51,17 @@ def set_bundler_config(runtime_ctx): # # Set local configuration options for bundler bundler_config = { - "binstubs": "bin", + "binstubs": BUNDLE_BIN_PATH, "deployment": "'true'", "standalone": "'true'", "frozen": "'true'", "without": "development,test", - "path": "lib", + "path": BUNDLE_PATH, "jobs": "20", } for option, value in [(option, value) for option, value in bundler_config.items()]: - args = ["config", "--local", value, option] + args = ["config", "--%s" % (config_category), option, value] result = run_bundler(runtime_ctx, args) if result.return_code: @@ -98,8 +106,8 @@ def bundle_install(runtime_ctx): [ "install", # > bundle install "--standalone", # Makes a bundle that can work without depending on Rubygems or Bundler at runtime. - "--binstubs=bin", # Creates a directory and place any executables from the gem there. - "--path={}".format(BUNDLE_INSTALL_PATH), # The location to install the specified gems to. + "--binstubs={}".format(BUNDLE_BIN_PATH), # Creates a directory and place any executables from the gem there. + "--path={}".format(BUNDLE_PATH), # The location to install the specified gems to. ], ) @@ -108,6 +116,7 @@ def bundle_install(runtime_ctx): def generate_bundle_build_file(runtime_ctx): # Create the BUILD file to expose the gems to the WORKSPACE + # USAGE: ./create_bundle_build_file.rb BUILD.bazel Gemfile.lock repo-name [excludes-json] workspace-name args = [ runtime_ctx.interpreter, # ruby interpreter "--enable=gems", # prevent the addition of gem installation directories to the default load path diff --git a/ruby/private/bundle/create_bundle_build_file.rb b/ruby/private/bundle/create_bundle_build_file.rb index c1af65d..a06d341 100755 --- a/ruby/private/bundle/create_bundle_build_file.rb +++ b/ruby/private/bundle/create_bundle_build_file.rb @@ -8,11 +8,23 @@ package(default_visibility = ["//visibility:public"]) -filegroup( - name = "binstubs", - srcs = glob(["bin/**/*"]), - data = [":libs"], -) +#exports_files( +# {binaries}, +# visibility = ["//visibility:public"], +#) +# +#filegroup( +# name = "libs", +# srcs = glob(["lib/**/*"]), +# data = [":libs"], +#) +# +# +#filegroup( +# name = "binstubs", +# srcs = glob(["bin/**/*"]), +# data = [":libs"], +#) ruby_library( name = "bundler_setup", @@ -39,6 +51,7 @@ srcs = glob( include = [ "lib/ruby/{ruby_version}/gems/{name}-{version}/**/*", + "bin/*" ], exclude = {exclude}, ), @@ -47,29 +60,44 @@ ) ' -require "bundler" +BIN_TEMPLATE = ' +exports_files( + {binaries}, + visibility = ["//visibility:public"], +) +' + +require 'bundler' require 'json' +require 'stringio' # This method scans the contents of the Gemfile.lock and if it finds BUNDLED WITH # it strips that line + the line below it, so that any version of bundler would work. def remove_bundler_version!(gemfile_lock_file) contents = File.read(gemfile_lock_file) - unless contents !~ /BUNDLED WITH/ - temp_lock_file = "#{gemfile_lock_file}.no-bundle-version" - system %(sed -n '/BUNDLED WITH/q;p' "#{gemfile_lock_file}" > #{temp_lock_file}) - ::FileUtils.rm_f(gemfile_lock_file) if File.symlink?(gemfile_lock_file) # it's just a symlink - ::FileUtils.move(temp_lock_file, gemfile_lock_file, force: true) - end + return unless contents =~ /BUNDLED WITH/ + + temp_lock_file = "#{gemfile_lock_file}.no-bundle-version" + system %(sed -n '/BUNDLED WITH/q;p' "#{gemfile_lock_file}" > #{temp_lock_file}) + ::FileUtils.rm_f(gemfile_lock_file) if File.symlink?(gemfile_lock_file) # it's just a symlink + ::FileUtils.move(temp_lock_file, gemfile_lock_file, force: true) +end + +def ruby_version(ruby_version = RUBY_VERSION) + @ruby_version ||= (ruby_version.split('.')[0..1] << 0).join('.') end def create_bundle_build_file(build_out_file, lock_file, repo_name, excludes, workspace_name) - # TODO: properly calculate path/ruby version here - # ruby_version = RUBY_VERSION # doesnt work because verion is 2.5.5 path is 2.5.0 - ruby_version = "*" + template_out = StringIO.new + + bin_folder = File.expand_path('bin', __dir__) + binaries = Dir.glob("#{bin_folder}/*").map { |binary| 'bin/' + File.basename(binary) if File.executable?(binary) } - template_out = TEMPLATE.gsub("{workspace_name}", workspace_name) - .gsub("{repo_name}", repo_name) - .gsub("{ruby_version}", ruby_version) + template_out.puts TEMPLATE + .gsub('{workspace_name}', workspace_name) + .gsub('{repo_name}', repo_name) + .gsub('{ruby_version}', ruby_version) + .gsub('{binaries}', binaries.to_s) # strip bundler version so we can process this file remove_bundler_version!(lock_file) @@ -79,40 +107,34 @@ def create_bundle_build_file(build_out_file, lock_file, repo_name, excludes, wor bundle.specs.each { |spec| deps = spec.dependencies.map(&:name) - deps += [":bundler_setup"] + deps += [':bundler_setup'] exclude_array = excludes[spec.name] || [] # We want to exclude files and folder with spaces in them - exclude_array += ["**/* *.*", "**/* */*"] - - template_out += GEM_TEMPLATE.gsub("{exclude}", exclude_array.to_s) - .gsub("{name}", spec.name) - .gsub("{version}", spec.version.to_s) - .gsub("{deps}", deps.to_s) - .gsub("{repo_name}", repo_name) - .gsub("{ruby_version}", ruby_version) + exclude_array += ['**/* *.*', '**/* */*'] + + template_out.puts GEM_TEMPLATE + .gsub('{exclude}', exclude_array.to_s) + .gsub('{name}', spec.name) + .gsub('{version}', spec.version.to_s) + .gsub('{deps}', deps.to_s) + .gsub('{repo_name}', repo_name) + .gsub('{ruby_version}', ruby_version) } - # Write the actual BUILD file - ::File.open(build_out_file, 'w') { |f| - f.puts template_out - } + ::File.open(build_out_file, 'w') { |f| f.puts template_out.string } + + #::File.open(File.dirname(build_out_file) + '/bin/BUILD.bazel', 'w') { |f| f.puts BIN_TEMPLATE.gsub('{binaries}', binaries.to_s) } end # ruby ./create_bundle_build_file.rb "BUILD.bazel" "Gemfile.lock" "repo_name" "[]" "wsp_name" if $0 == __FILE__ if ARGV.length != 5 - fmt.Println("BUILD FILE ARGS not 5") + warn("USAGE: #{$0} BUILD.bazel Gemfile.lock repo-name [excludes-json] workspace-name") exit(1) end - build_out_file = ARGV[0] - lock_file = ARGV[1] - repo_name = ARGV[2] - - excludes = JSON.parse(ARGV[3]) - - workspace_name = ARGV[4] + build_out_file, lock_file, repo_name, excludes, workspace_name, * = *ARGV - create_bundle_build_file(build_out_file, lock_file, repo_name, excludes, workspace_name) + create_bundle_build_file(build_out_file, lock_file, repo_name, JSON.parse(excludes), workspace_name) end diff --git a/ruby/private/bundle/install_bundler.rb b/ruby/private/bundle/install_bundler.rb index e08bf16..ab6a94c 100755 --- a/ruby/private/bundle/install_bundler.rb +++ b/ruby/private/bundle/install_bundler.rb @@ -1,6 +1,6 @@ -#!/usr/bin/env ruby +#!/usr/bin/env ruby # frozen_string_literal: true -# + require 'rubygems' require 'rubygems/name_tuple' require 'rubygems/package' @@ -22,12 +22,12 @@ def unpack_gem(name, version, dest = Dir.pwd) end def main - gem_name, gem_version, dir, _ = *ARGV + gem_name, gem_version, dir, = *ARGV dir ||= Dir.pwd unless gem_name && gem_version puts "USAGE: #{$0} gem-name gem-version destination-folder" exit 1 - end + end unpack_gem(gem_name, gem_version, dir) end diff --git a/ruby/private/constants.bzl b/ruby/private/constants.bzl index 4983be9..8b41925 100644 --- a/ruby/private/constants.bzl +++ b/ruby/private/constants.bzl @@ -3,7 +3,11 @@ load("@bazel_skylib//lib:dicts.bzl", "dicts") RULES_RUBY_WORKSPACE_NAME = "@bazelruby_ruby_rules" TOOLCHAIN_TYPE_NAME = "%s//ruby:toolchain_type" % RULES_RUBY_WORKSPACE_NAME + DEFAULT_BUNDLER_VERSION = "2.1.2" +DEFAULT_RSPEC_ARGS = {"--format": "documentation", "--force-color": None} +DEFAULT_RSPEC_GEMS = ["rspec", "rspec-its"] +DEFAULT_BUNDLE_NAME = "@bundle//" RUBY_ATTRS = { "srcs": attr.label_list( @@ -32,21 +36,17 @@ RUBY_ATTRS = { _RSPEC_ATTRS = { "bundle": attr.string( - default = "@bundle//", + default = DEFAULT_BUNDLE_NAME, doc = "Name of the bundle where the rspec gem can be found, eg @bundle//", ), "rspec_args": attr.string_list( - default = ["--format documentation", "--force-color", "-p2"], + default = [], doc = "Arguments passed to rspec executable", ), - "spec_executable": attr.label( - default = "@bundle//:bin/rspec", - allow_single_file = True, - doc = "RSpec executable", - ), - "spec_target": attr.label( + "rspec_executable": attr.label( + default = "%s:bin/rspec" % (DEFAULT_BUNDLE_NAME), allow_single_file = True, - doc = "Directory or local file to tests", + doc = "RSpec Executable Label", ), } diff --git a/ruby/private/rspec.bzl b/ruby/private/rspec.bzl index b5b2899..d68e652 100644 --- a/ruby/private/rspec.bzl +++ b/ruby/private/rspec.bzl @@ -1,27 +1,65 @@ -load(":constants.bzl", "RSPEC_ATTRS", "TOOLCHAIN_TYPE_NAME") +load( + ":constants.bzl", + "DEFAULT_BUNDLE_NAME", + "DEFAULT_RSPEC_ARGS", + "DEFAULT_RSPEC_GEMS", + "RSPEC_ATTRS", + "TOOLCHAIN_TYPE_NAME", +) load(":binary.bzl", "ruby_binary_macro") +load("@bazel_skylib//lib:dicts.bzl", "dicts") -def _ruby_rspec(ctx): - bundle = ctx.attr.bundle +def ruby_rspec( + name, + srcs = [], + specs = [], + deps = [], + size = "small", + rspec_args = {}, + bundle = DEFAULT_BUNDLE_NAME, + visibility = None, + **kwargs): + args_list = [] + + args_dict = dicts.add(DEFAULT_RSPEC_ARGS, rspec_args) + + # We pass the respec_args as a dictionary so that you can overwrite + # the default rspec arguments with custom ones. + for option, value in [(option, value) for option, value in args_dict.items()]: + if value != None: + args_list.append("%s %s" % (option, value)) + else: + args_list.append("%s" % (option)) + + args_list += specs - rspec_executable = ctx.file.spec_executable - rspec_gem = Label("%s:rspec" % (bundle)) - args = ctx.attr.rspec_args + ctx.attr.args + rspec_gems = ["%s:%s" % (bundle, gem) for gem in DEFAULT_RSPEC_GEMS] + + deps += rspec_gems + + ruby_rspec_test( + name = name, + visibility = visibility, + args = args_list, + srcs = srcs + specs, + deps = deps, + size = size, + **kwargs + ) + +def _ruby_rspec_test_impl(ctx): + bundle = ctx.attr.bundle - if ctx.attr.spec_target: - spec_file = ctx.file.spec_target - args.append(spec_file.path) + rspec_executable = ctx.file.rspec_executable return ruby_binary_macro( ctx, rspec_executable, - ctx.attr.srcs + [rspec_executable], - ctx.attr.deps + [rspec_gem], - args, + ctx.attr.srcs, ) ruby_rspec_test = rule( - implementation = _ruby_rspec, + implementation = _ruby_rspec_test_impl, attrs = RSPEC_ATTRS, test = True, toolchains = [TOOLCHAIN_TYPE_NAME], diff --git a/ruby/tests/BUILD.bazel b/ruby/tests/BUILD.bazel index a28e195..247e44a 100644 --- a/ruby/tests/BUILD.bazel +++ b/ruby/tests/BUILD.bazel @@ -1,3 +1,4 @@ +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") load( "//ruby:defs.bzl", "ruby_binary", @@ -9,6 +10,7 @@ load("@io_bazel_rules_docker//container:container.bzl", "container_image") # Checks if args are correctly passed to the ruby script. ruby_test( name = "args_check_ruby_test", + size = "small", srcs = ["args_check.rb"], args = [ "foo", @@ -26,6 +28,7 @@ ruby_binary( # Checks if a ruby_binary is a valid src in sh_* rules sh_test( name = "args_check_sh_test", + size = "small", srcs = ["args_check"], args = [ "foo", @@ -36,6 +39,7 @@ sh_test( ruby_test( name = "include_order_check", + size = "small", srcs = ["include_order_check.rb"], deps = [ "//ruby/tests/testdata:a", @@ -48,6 +52,7 @@ ruby_test( # Tests if :ruby_bin can run in sh_binary sh_test( name = "runtime_run_ruby_test", + size = "small", srcs = ["runtime_run_ruby_test.sh"], args = [ "$(location args_check.rb)", @@ -82,6 +87,7 @@ genrule( sh_test( name = "genrule_run_ruby_test", + size = "small", srcs = ["genrules_run_ruby_test.sh"], ) @@ -123,6 +129,7 @@ sh_binary( # runfiles resolution test (case 4a) ruby_test( name = "load_path_in_runfiles_test_4a", + size = "small", srcs = ["load_path_in_runfiles_test.rb"], main = "load_path_in_runfiles_test.rb", deps = [ @@ -134,6 +141,7 @@ ruby_test( # runfiles resolution test (case 4b) sh_test( name = "load_path_in_runfiles_test_4b", + size = "small", srcs = ["load_path_in_runfiles_test_3.sh"], data = [":load_path_in_runfiles_sh_binary"], ) @@ -151,6 +159,7 @@ genrule( # runfiles resolution test (case 5a) sh_test( name = "load_path_in_runfiles_test_5a", + size = "small", srcs = ["load_path_in_runfiles_test_5a.sh"], ) @@ -220,6 +229,7 @@ config_setting( ruby_test( name = "ext_test", + size = "small", srcs = ["ext_test.rb"], data = select({ ":requires_bundle": ["example_ext.bundle"], @@ -256,6 +266,7 @@ container_image( sh_test( name = "load_path_in_runfiles_container_test", + size = "small", srcs = ["container_test.sh"], args = [ "$(location :load_path_in_runfiles_container_image)", From a596328ce4df9f82341542fcbbb9afaafe5e160d Mon Sep 17 00:00:00 2001 From: Konstantin Gredeskoul Date: Thu, 2 Jan 2020 22:07:17 -0800 Subject: [PATCH 05/19] Refactor download_gem.rb and build file generator --- .rubocop.yml | 32 +- README.md | 6 +- WORKSPACE | 4 +- examples/simple_script/BUILD.bazel | 20 +- examples/simple_script/WORKSPACE | 16 +- examples/simple_script/spec/spec_helper.rb | 1 - ruby/deps.bzl | 4 +- ruby/private/bundle/bundle.bzl | 47 ++- .../bundle/create_bundle_build_file.rb | 331 ++++++++++++------ .../{install_bundler.rb => download_gem.rb} | 8 +- ruby/private/constants.bzl | 23 +- ruby/private/rspec.bzl | 28 +- ruby/tests/BUILD.bazel | 5 +- 13 files changed, 351 insertions(+), 174 deletions(-) rename ruby/private/bundle/{install_bundler.rb => download_gem.rb} (86%) diff --git a/.rubocop.yml b/.rubocop.yml index 34955ae..0d331a5 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,12 +1,36 @@ inherit_from: .relaxed-rubocop-2.4.yml AllCops: - TargetRubyVersion: 2.6.5 + TargetRubyVersion: 2.6 + UseCache: true + DefaultFormatter: progress + DisplayStyleGuide: true + DisplayCopNames: true Exclude: - - "**/rubocop" - - "examples/**/*" + - "external*/**/*" - "bazel-*/**/*" - - "external/**/*" + - "**/examples/**/*" + - "**/BUILD" + - "**/*.bazel" + - "**/*.bzl" + - "**/rubocop" - "**/vendor/bundle/**/*" + Include: + - '**/*.rb' + - '**/*.gemfile' + - '**/*.gemspec' + - '**/*.rake' + - '**/*.ru' + - '**/Gemfile' + - '**/Rakefile' +Layout/HashAlignment: + Enabled: true + EnforcedColonStyle: table + +Style/Dir: + Enabled: false +Layout/MultilineMethodCallIndentation: + Enabled: true + EnforcedStyle: indented_relative_to_receiver diff --git a/README.md b/README.md index 6cfaee6..e397e20 100644 --- a/README.md +++ b/README.md @@ -122,14 +122,14 @@ ruby_binary( ) ruby_test( - name = "foo_test", + name = "foo-test", srcs = ["test/foo_test.rb"], deps = [":foo"], ) ruby_rspec( - name = "foo_spec", - specs = glob(["spec/**/*.rb]), + name = "foo-spec", + specs = glob(["spec/**/*.rb"]), rspec_args = { "--format": "progress" }, deps = [":foo"] } diff --git a/WORKSPACE b/WORKSPACE index 429a304..450bed6 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,6 +1,6 @@ workspace(name = "bazelruby_ruby_rules") -load("@//ruby/private:dependencies.bzl", "ruby_rules_dependencies") +load("@//ruby:deps.bzl", "ruby_rules_dependencies") ruby_rules_dependencies() @@ -97,10 +97,10 @@ load("@bazelruby_ruby_rules//ruby:defs.bzl", "bundle_install") bundle_install( name = "bundle", + bundler_version = "2.1.2", excludes = { "mini_portile": ["test/**/*"], }, gemfile = "//:Gemfile", gemfile_lock = "//:Gemfile.lock", - version = "2.0.2", ) diff --git a/examples/simple_script/BUILD.bazel b/examples/simple_script/BUILD.bazel index 89eac23..0c82763 100644 --- a/examples/simple_script/BUILD.bazel +++ b/examples/simple_script/BUILD.bazel @@ -47,6 +47,17 @@ ruby_binary( ], ) +ruby_binary( + name = "bin-all", + srcs = ["script.rb"], + main = "script.rb", + deps = [ + ":lib", + "//lib:foo", + "@bundle//:gems", + ], +) + # This is an example of the RSpec definition that uses autorun # and points to spec_helper as the main spec file. It specifies # which specs to run using the args. @@ -87,9 +98,8 @@ ruby_test( args = [ "--format documentation", "--force-color", - ] + glob([ - "spec/**/*.rb", - ]), + "spec", + ], main = "@bundle//:bin/rspec", deps = [ "@bundle//:awesome_print", @@ -110,6 +120,10 @@ ruby_rspec( ":sources", ":spec_sources", ], + rspec_args = { + # NOTE: the output is only visible with --test_output=streamed flag + "--format": "progress", # this is how we can override rspec output format + }, specs = glob([ "spec/**/*.rb", ]), diff --git a/examples/simple_script/WORKSPACE b/examples/simple_script/WORKSPACE index e7ba9ae..9883f02 100644 --- a/examples/simple_script/WORKSPACE +++ b/examples/simple_script/WORKSPACE @@ -8,26 +8,18 @@ local_repository( ) load( - "@bazelruby_ruby_rules//ruby/private:dependencies.bzl", + "@bazelruby_ruby_rules//ruby:deps.bzl", + "ruby_register_toolchains", "ruby_rules_dependencies", ) ruby_rules_dependencies() -load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") - -bazel_skylib_workspace() - -load( - "@bazelruby_ruby_rules//ruby:deps.bzl", - "ruby_register_toolchains", -) - ruby_register_toolchains(version = "2.6.5") -load("@bazelruby_ruby_rules//ruby:defs.bzl", "bundle_install") +load("@bazelruby_ruby_rules//ruby:defs.bzl", "ruby_bundle") -bundle_install( +ruby_bundle( name = "bundle", excludes = { "mini_portile": ["test/**/*"], diff --git a/examples/simple_script/spec/spec_helper.rb b/examples/simple_script/spec/spec_helper.rb index b0866bf..65d24b0 100644 --- a/examples/simple_script/spec/spec_helper.rb +++ b/examples/simple_script/spec/spec_helper.rb @@ -18,7 +18,6 @@ require 'awesome_print' require 'colored2' -# frozen_string_literal: true RSpec.configure do |config| config.expect_with :rspec do |expectations| expectations.include_chain_clauses_in_custom_matcher_descriptions = true diff --git a/ruby/deps.bzl b/ruby/deps.bzl index 9b1455f..a520bfc 100644 --- a/ruby/deps.bzl +++ b/ruby/deps.bzl @@ -1,13 +1,13 @@ # Repository rules load( "@bazelruby_ruby_rules//ruby/private:dependencies.bzl", - _load_ruby_rules_dependencies = "ruby_rules_dependencies", + _ruby_rules_dependencies = "ruby_rules_dependencies", ) load( "@bazelruby_ruby_rules//ruby/private:sdk.bzl", _register_toolchains = "ruby_register_toolchains", ) -load_ruby_rules_dependencies = _load_ruby_rules_dependencies +ruby_rules_dependencies = _ruby_rules_dependencies ruby_register_toolchains = _register_toolchains diff --git a/ruby/private/bundle/bundle.bzl b/ruby/private/bundle/bundle.bzl index 6f1b323..9c56e57 100644 --- a/ruby/private/bundle/bundle.bzl +++ b/ruby/private/bundle/bundle.bzl @@ -1,22 +1,21 @@ load( "//ruby/private:constants.bzl", "BUNDLE_ATTRS", + "BUNDLE_BINARY", + "BUNDLE_BIN_PATH", + "BUNDLE_PATH", "RULES_RUBY_WORKSPACE_NAME", + "SCRIPT_BUILD_FILE_GENERATOR", + "SCRIPT_INSTALL_GEM", ) load("//ruby/private:providers.bzl", "RubyRuntimeContext") load("//ruby/private/tools:deprecations.bzl", "deprecated_attribute") -BUNDLE_BIN_PATH = "bin" -BUNDLE_PATH = "lib" -BUNDLE_BINARY = "bundler/exe/bundler" -SCRIPT_INSTALL_GEM = "install_bundler.rb" -SCRIPT_BUILD_FILE_GENERATOR = "create_bundle_build_file.rb" - # Runs bundler with arbitrary arguments -# # eg: run_bundler(runtime_ctx, [ "lock", " --gemfile", "Gemfile.rails5" ]) -# def run_bundler(runtime_ctx, bundler_arguments): + #print("BUNDLE RUN", bundler_arguments) + # Now we are running bundle install args = [ runtime_ctx.interpreter, # ruby @@ -39,7 +38,7 @@ def run_bundler(runtime_ctx, bundler_arguments): # # Sets local bundler config values by calling # -# $ bundler config --local | --global config-option config-value +# $ bundle config --local | --global config-option config-value # # @config_category can be either 'local' or 'global' def set_bundler_config(runtime_ctx, config_category = "local"): @@ -51,16 +50,15 @@ def set_bundler_config(runtime_ctx, config_category = "local"): # # Set local configuration options for bundler bundler_config = { - "binstubs": BUNDLE_BIN_PATH, - "deployment": "'true'", - "standalone": "'true'", - "frozen": "'true'", + "deployment": "true", + "standalone": "true", + "frozen": "true", "without": "development,test", "path": BUNDLE_PATH, "jobs": "20", } - for option, value in [(option, value) for option, value in bundler_config.items()]: + for option, value in bundler_config.items(): args = ["config", "--%s" % (config_category), option, value] result = run_bundler(runtime_ctx, args) @@ -72,6 +70,13 @@ def set_bundler_config(runtime_ctx, config_category = "local"): ) fail(message) + # The new way to generate binstubs is via the binstubs command, not config option. + return run_bundler(runtime_ctx, ["binstubs", "--path", BUNDLE_BIN_PATH]) + +# This function is called "pure_ruby" because it downloads and unpacks the gem +# file into a given folder, which for gems without C-extensions is the same +# as install. To support gems that have C-extensions, the Ruby file install_gem.rb +# will need to be modified to use Gem::Installer.at(path).install(gem) API. def install_pure_ruby_gem(runtime_ctx, gem_name, gem_version, folder): # USAGE: ./install_bundler.rb gem-name gem-version destination-folder args = [ @@ -104,7 +109,7 @@ def bundle_install(runtime_ctx): result = run_bundler( runtime_ctx, [ - "install", # > bundle install + "install", # bundle install "--standalone", # Makes a bundle that can work without depending on Rubygems or Bundler at runtime. "--binstubs={}".format(BUNDLE_BIN_PATH), # Creates a directory and place any executables from the gem there. "--path={}".format(BUNDLE_PATH), # The location to install the specified gems to. @@ -120,7 +125,7 @@ def generate_bundle_build_file(runtime_ctx): args = [ runtime_ctx.interpreter, # ruby interpreter "--enable=gems", # prevent the addition of gem installation directories to the default load path - "-I", # -I lib (adds this folder to $LOAD_PATH where ruby searchesf for things) + "-I", # -I lib (adds this folder to $LOAD_PATH where ruby searches for things) "bundler/lib", SCRIPT_BUILD_FILE_GENERATOR, # The template used to created bundle file "BUILD.bazel", # Bazel build file (can be empty) @@ -134,7 +139,7 @@ def generate_bundle_build_file(runtime_ctx): if result.return_code: fail("build file generation failed: %s%s" % (result.stdout, result.stderr)) -def bundle_install_impl(ctx): +def _ruby_bundle_impl(ctx): ctx.symlink(ctx.attr.gemfile, "Gemfile") ctx.symlink(ctx.attr.gemfile_lock, "Gemfile.lock") ctx.symlink(ctx.attr._create_bundle_build_file, SCRIPT_BUILD_FILE_GENERATOR) @@ -142,6 +147,10 @@ def bundle_install_impl(ctx): # version is too generic for this operation deprecated_attribute(ctx, "version", "bundler_version") + if ctx.attr.bundler_version: + bundler_version = ctx.attr.bundler_version + else: + bundler_version = ctx.attr.version # Setup this provider that we pass around between functions for convenience runtime_ctx = RubyRuntimeContext( @@ -151,7 +160,7 @@ def bundle_install_impl(ctx): ) # 1. Install the right version of the Bundler Gem - install_bundler(runtime_ctx, ctx.attr.bundler_version) + install_bundler(runtime_ctx, bundler_version) # Create label for the Bundler executable bundler = Label("//:" + BUNDLE_BINARY) @@ -166,6 +175,6 @@ def bundle_install_impl(ctx): generate_bundle_build_file(runtime_ctx) ruby_bundle = repository_rule( - implementation = bundle_install_impl, + implementation = _ruby_bundle_impl, attrs = BUNDLE_ATTRS, ) diff --git a/ruby/private/bundle/create_bundle_build_file.rb b/ruby/private/bundle/create_bundle_build_file.rb index a06d341..a23cd6f 100755 --- a/ruby/private/bundle/create_bundle_build_file.rb +++ b/ruby/private/bundle/create_bundle_build_file.rb @@ -1,112 +1,224 @@ #!/usr/bin/env ruby # frozen_string_literal: true -TEMPLATE = 'load( - "{workspace_name}//ruby:defs.bzl", - "ruby_library", -) - -package(default_visibility = ["//visibility:public"]) - -#exports_files( -# {binaries}, -# visibility = ["//visibility:public"], -#) -# -#filegroup( -# name = "libs", -# srcs = glob(["lib/**/*"]), -# data = [":libs"], -#) -# -# -#filegroup( -# name = "binstubs", -# srcs = glob(["bin/**/*"]), -# data = [":libs"], -#) - -ruby_library( - name = "bundler_setup", - srcs = ["lib/bundler/setup.rb"], - visibility = ["//visibility:private"], -) - -ruby_library( - name = "bundler", - srcs = glob( - include = [ - "bundler/**/*", - ], - ), - rubyopt = ["-r${RUNFILES_DIR}/{repo_name}/lib/bundler/setup.rb"], -) - -# PULL EACH GEM INDIVIDUALLY -' - -GEM_TEMPLATE = ' -ruby_library( - name = "{name}", - srcs = glob( - include = [ - "lib/ruby/{ruby_version}/gems/{name}-{version}/**/*", - "bin/*" - ], - exclude = {exclude}, - ), - deps = {deps}, - rubyopt = ["-r${RUNFILES_DIR}/{repo_name}/lib/bundler/setup.rb"], -) -' - -BIN_TEMPLATE = ' -exports_files( - {binaries}, - visibility = ["//visibility:public"], -) -' +TEMPLATE = <<~MAIN_TEMPLATE + load( + "{workspace_name}//ruby:defs.bzl", + "ruby_library", + ) + + package(default_visibility = ["//visibility:public"]) + + ruby_library( + name = "bundler_setup", + srcs = ["lib/bundler/setup.rb"], + visibility = ["//visibility:private"], + ) + + ruby_library( + name = "bundler", + srcs = glob( + include = [ + "bundler/**/*", + ], + ), + rubyopt = ["{bundler_setup}"], + ) + + # PULL EACH GEM INDIVIDUALLY +MAIN_TEMPLATE + +GEM_TEMPLATE = <<~GEM_TEMPLATE + ruby_library( + name = "{name}", + srcs = glob( + include = [ + "lib/ruby/{ruby_version}/gems/{name}-{version}/**/*", + "bin/*" + ], + exclude = {exclude}, + ), + deps = {deps}, + includes = ["lib/ruby/{ruby_version}/gems/{name}-{version}/lib"], + rubyopt = ["{bundler_setup}"], + ) +GEM_TEMPLATE + +ALL_GEMS = <<~ALL_GEMS + ruby_library( + name = "gems", + srcs = glob( + {gems_lib_files}, + ), + includes = {gems_lib_paths}, + rubyopt = ["{bundler_setup}"], + ) +ALL_GEMS + +GEM_LIB_PATH = ->(ruby_version, gem_name, gem_version) do + "lib/ruby/#{ruby_version}/gems/#{gem_name}-#{gem_version}/lib" +end require 'bundler' require 'json' require 'stringio' +require 'fileutils' +require 'tempfile' + +# colorization +class String + def colorize(color_code) + "\e[#{color_code}m#{self}\e[0m" + end + + # @formatter:off + def red; colorize(31); end -# This method scans the contents of the Gemfile.lock and if it finds BUNDLED WITH -# it strips that line + the line below it, so that any version of bundler would work. -def remove_bundler_version!(gemfile_lock_file) - contents = File.read(gemfile_lock_file) - return unless contents =~ /BUNDLED WITH/ + def green; colorize(32); end - temp_lock_file = "#{gemfile_lock_file}.no-bundle-version" - system %(sed -n '/BUNDLED WITH/q;p' "#{gemfile_lock_file}" > #{temp_lock_file}) - ::FileUtils.rm_f(gemfile_lock_file) if File.symlink?(gemfile_lock_file) # it's just a symlink - ::FileUtils.move(temp_lock_file, gemfile_lock_file, force: true) + def yellow; colorize(33); end + + def blue; colorize(34); end + + def pink; colorize(35); end + + def light_blue; colorize(36); end + + def orange; colorize(41); end + # @formatter:on end -def ruby_version(ruby_version = RUBY_VERSION) - @ruby_version ||= (ruby_version.split('.')[0..1] << 0).join('.') +class Buildifier + attr_reader :build_file, :output_file + + # @formatter:off + class BuildifierError < StandardError; end + class BuildifierNotFoundError < BuildifierError; end + class BuildifierFailedError < BuildifierError; end + class BuildifierNoBuildFileError < BuildifierError; end + # @formatter:on + + def initialize(build_file) + @build_file = build_file + + # For capturing buildifier output + @output_file = ::Tempfile.new("/tmp/#{File.dirname(File.absolute_path(build_file))}/#{build_file}.stdout").path + end + + def buildify! + raise BuildifierNoBuildFileError, 'Can\'t find the BUILD file' unless File.exist?(build_file) + + # see if we can find buildifier on the filesystem + buildifier = `bash -c 'command -v buildifier'`.strip + + raise BuildifierNotFoundError, 'Can\'t find buildifier' unless buildifier && File.executable?(buildifier) + + command = "#{buildifier} -v #{File.absolute_path(build_file)}" + system("/usr/bin/env bash -c '#{command} 1>#{output_file} 2>&1'") + code = $? + + output = File.read(output_file).strip.gsub(Dir.pwd, '.').yellow + begin + FileUtils.rm_f(output_file) + rescue StandardError + nil + end + + if code == 0 + puts 'Buildifier gave 👍 '.green + (output ? " and said: #{output}" : '') + else + raise BuildifierFailedError, + 'Generated BUILD file failed buildifier, with error — '.red + "\n\n" + + File.read(output_file).yellow + end + end end -def create_bundle_build_file(build_out_file, lock_file, repo_name, excludes, workspace_name) - template_out = StringIO.new +class BundleBuildFileGenerator + attr_reader :workspace_name, + :repo_name, + :build_file, + :gemfile_lock, + :excludes, + :ruby_version + + def initialize(workspace_name:, + repo_name:, + build_file: 'BUILD.bazel', + gemfile_lock: 'Gemfile.lock', + excludes: nil) + @workspace_name = workspace_name + @repo_name = repo_name + @build_file = build_file + @gemfile_lock = gemfile_lock + @excludes = excludes + # This attribute returns 0 as the third minor version number, which happens to be + # what Ruby uses in the PATH to gems, eg. ruby 2.6.5 would have a folder called + # ruby/2.6.0/gems for all minor versions of 2.6.* + @ruby_version ||= (RUBY_VERSION.split('.')[0..1] << 0).join('.') + end + + def generate! + # when we append to a string many times, using StringIO is more efficient. + template_out = StringIO.new + + # In Bazel we want to use __FILE__ because __dir__points to the actual sources, and we are + # using symlinks here. + # + # rubocop:disable Style/ExpandPathArguments + bin_folder = File.expand_path('../bin', __FILE__) + binaries = Dir.glob("#{bin_folder}/*").map do |binary| + 'bin/' + File.basename(binary) if File.executable?(binary) + end + # rubocop:enable Style/ExpandPathArguments + + template_out.puts TEMPLATE + .gsub('{workspace_name}', workspace_name) + .gsub('{repo_name}', repo_name) + .gsub('{ruby_version}', ruby_version) + .gsub('{binaries}', binaries.to_s) + .gsub('{bundler_setup}', bundler_setup_require) + + # strip bundler version so we can process this file + remove_bundler_version! + # Append to the end specific gem libraries and dependencies + bundle = Bundler::LockfileParser.new(Bundler.read_file(gemfile_lock)) + gem_lib_paths = [] + bundle.specs.each { |spec| register_gem(spec, template_out, gem_lib_paths) } + + template_out.puts ALL_GEMS + .gsub('{gems_lib_files}', gem_lib_paths.map { |p| "#{p}/**/*.rb" }.to_s) + .gsub('{gems_lib_paths}', gem_lib_paths.to_s) + .gsub('{bundler_setup}', bundler_setup_require) - bin_folder = File.expand_path('bin', __dir__) - binaries = Dir.glob("#{bin_folder}/*").map { |binary| 'bin/' + File.basename(binary) if File.executable?(binary) } + ::File.open(build_file, 'w') { |f| f.puts template_out.string } + end + + private + + def bundler_setup_require + @bundler_setup_require ||= "-r#{runfiles_path('lib/bundler/setup.rb')}" + end - template_out.puts TEMPLATE - .gsub('{workspace_name}', workspace_name) - .gsub('{repo_name}', repo_name) - .gsub('{ruby_version}', ruby_version) - .gsub('{binaries}', binaries.to_s) + def runfiles_path(path) + "${RUNFILES_DIR}/#{repo_name}/#{path}" + end - # strip bundler version so we can process this file - remove_bundler_version!(lock_file) + # This method scans the contents of the Gemfile.lock and if it finds BUNDLED WITH + # it strips that line + the line below it, so that any version of bundler would work. + def remove_bundler_version! + contents = File.read(gemfile_lock) + return unless contents =~ /BUNDLED WITH/ - # Append to the end specific gem libraries and dependencies - bundle = Bundler::LockfileParser.new(Bundler.read_file(lock_file)) + temp_gemfile_lock = "#{gemfile_lock}.no-bundle-version" + system %(sed -n '/BUNDLED WITH/q;p' "#{gemfile_lock}" > #{temp_gemfile_lock}) + ::FileUtils.rm_f(gemfile_lock) if File.symlink?(gemfile_lock) # it's just a symlink + ::FileUtils.move(temp_gemfile_lock, gemfile_lock, force: true) + end - bundle.specs.each { |spec| - deps = spec.dependencies.map(&:name) + def register_gem(spec, template_out, gem_lib_paths) + gem_lib_paths << GEM_LIB_PATH[ruby_version, spec.name, spec.version] + deps = spec.dependencies.map { |d| ":#{d.name}" } deps += [':bundler_setup'] exclude_array = excludes[spec.name] || [] @@ -114,27 +226,36 @@ def create_bundle_build_file(build_out_file, lock_file, repo_name, excludes, wor exclude_array += ['**/* *.*', '**/* */*'] template_out.puts GEM_TEMPLATE - .gsub('{exclude}', exclude_array.to_s) - .gsub('{name}', spec.name) - .gsub('{version}', spec.version.to_s) - .gsub('{deps}', deps.to_s) - .gsub('{repo_name}', repo_name) - .gsub('{ruby_version}', ruby_version) - } - - ::File.open(build_out_file, 'w') { |f| f.puts template_out.string } - - #::File.open(File.dirname(build_out_file) + '/bin/BUILD.bazel', 'w') { |f| f.puts BIN_TEMPLATE.gsub('{binaries}', binaries.to_s) } + .gsub('{exclude}', exclude_array.to_s) + .gsub('{name}', spec.name) + .gsub('{version}', spec.version.to_s) + .gsub('{deps}', deps.to_s) + .gsub('{repo_name}', repo_name) + .gsub('{ruby_version}', ruby_version) + .gsub('{bundler_setup}', bundler_setup_require) + end end # ruby ./create_bundle_build_file.rb "BUILD.bazel" "Gemfile.lock" "repo_name" "[]" "wsp_name" if $0 == __FILE__ if ARGV.length != 5 - warn("USAGE: #{$0} BUILD.bazel Gemfile.lock repo-name [excludes-json] workspace-name") + warn("USAGE: #{$0} BUILD.bazel Gemfile.lock repo-name [excludes-json] workspace-name".orange) exit(1) end - build_out_file, lock_file, repo_name, excludes, workspace_name, * = *ARGV + build_file, gemfile_lock, repo_name, excludes, workspace_name, * = *ARGV - create_bundle_build_file(build_out_file, lock_file, repo_name, JSON.parse(excludes), workspace_name) + BundleBuildFileGenerator.new(build_file: build_file, + gemfile_lock: gemfile_lock, + repo_name: repo_name, + excludes: JSON.parse(excludes), + workspace_name: workspace_name) + .generate! + + begin + Buildifier.new(build_file).buildify! + puts("Buildifier successful on file #{build_file} ") + rescue Buildifier::BuildifierError => e + warn("ERROR running buildifier on the generated build file [#{build_file}] ➔ \n#{e.message.orange}") + end end diff --git a/ruby/private/bundle/install_bundler.rb b/ruby/private/bundle/download_gem.rb similarity index 86% rename from ruby/private/bundle/install_bundler.rb rename to ruby/private/bundle/download_gem.rb index ab6a94c..780c968 100755 --- a/ruby/private/bundle/install_bundler.rb +++ b/ruby/private/bundle/download_gem.rb @@ -12,13 +12,11 @@ def unpack_gem(name, version, dest = Dir.pwd) source = Gem::Source.new('https://rubygems.org') spec = source.fetch_spec Gem::NameTuple.new(name, version) - Dir.mktmpdir { |dir| + Dir.mktmpdir do |dir| Dir.chdir(dir) { source.download(spec) } downloaded = File.join(dir, "#{name}-#{version}.gem") - Gem::Package.new(downloaded).extract_files dest - } - - `gem install bundler --no-doc` + Gem::Package.new(downloaded).extract_files(dest) + end end def main diff --git a/ruby/private/constants.bzl b/ruby/private/constants.bzl index 8b41925..3505735 100644 --- a/ruby/private/constants.bzl +++ b/ruby/private/constants.bzl @@ -1,5 +1,4 @@ load(":providers.bzl", "RubyLibrary") -load("@bazel_skylib//lib:dicts.bzl", "dicts") RULES_RUBY_WORKSPACE_NAME = "@bazelruby_ruby_rules" TOOLCHAIN_TYPE_NAME = "%s//ruby:toolchain_type" % RULES_RUBY_WORKSPACE_NAME @@ -9,6 +8,13 @@ DEFAULT_RSPEC_ARGS = {"--format": "documentation", "--force-color": None} DEFAULT_RSPEC_GEMS = ["rspec", "rspec-its"] DEFAULT_BUNDLE_NAME = "@bundle//" +BUNDLE_BIN_PATH = "bin" +BUNDLE_PATH = "lib" +BUNDLE_BINARY = "bundler/exe/bundler" + +SCRIPT_INSTALL_GEM = "download_gem.rb" +SCRIPT_BUILD_FILE_GENERATOR = "create_bundle_build_file.rb" + RUBY_ATTRS = { "srcs": attr.label_list( allow_files = True, @@ -50,7 +56,10 @@ _RSPEC_ATTRS = { ), } -RSPEC_ATTRS = dicts.add(RUBY_ATTRS, _RSPEC_ATTRS) +RSPEC_ATTRS = {} + +RSPEC_ATTRS.update(RUBY_ATTRS) +RSPEC_ATTRS.update(_RSPEC_ATTRS) BUNDLE_ATTRS = { "ruby_sdk": attr.string( @@ -79,14 +88,16 @@ BUNDLE_ATTRS = { doc = "List of glob patterns per gem to be excluded from the library", ), "_install_bundler": attr.label( - default = "%s//ruby/private/bundle:install_bundler.rb" % ( - RULES_RUBY_WORKSPACE_NAME + default = "%s//ruby/private/bundle:%s" % ( + RULES_RUBY_WORKSPACE_NAME, + SCRIPT_INSTALL_GEM, ), allow_single_file = True, ), "_create_bundle_build_file": attr.label( - default = "%s//ruby/private/bundle:create_bundle_build_file.rb" % ( - RULES_RUBY_WORKSPACE_NAME + default = "%s//ruby/private/bundle:%s" % ( + RULES_RUBY_WORKSPACE_NAME, + SCRIPT_BUILD_FILE_GENERATOR, ), doc = "Creates the BUILD file", allow_single_file = True, diff --git a/ruby/private/rspec.bzl b/ruby/private/rspec.bzl index d68e652..334d9c3 100644 --- a/ruby/private/rspec.bzl +++ b/ruby/private/rspec.bzl @@ -7,21 +7,31 @@ load( "TOOLCHAIN_TYPE_NAME", ) load(":binary.bzl", "ruby_binary_macro") -load("@bazel_skylib//lib:dicts.bzl", "dicts") def ruby_rspec( name, - srcs = [], - specs = [], - deps = [], + srcs, + specs, + deps = None, size = "small", - rspec_args = {}, + rspec_args = None, # This is expected to be a dictionary bundle = DEFAULT_BUNDLE_NAME, visibility = None, **kwargs): + if specs == None: + specs = [] + + if srcs == None: + srcs = [] + + if rspec_args == None: + rspec_args = {} + args_list = [] - args_dict = dicts.add(DEFAULT_RSPEC_ARGS, rspec_args) + args_dict = {} + args_dict.update(DEFAULT_RSPEC_ARGS) + args_dict.update(rspec_args) # We pass the respec_args as a dictionary so that you can overwrite # the default rspec arguments with custom ones. @@ -48,13 +58,9 @@ def ruby_rspec( ) def _ruby_rspec_test_impl(ctx): - bundle = ctx.attr.bundle - - rspec_executable = ctx.file.rspec_executable - return ruby_binary_macro( ctx, - rspec_executable, + ctx.file.rspec_executable, ctx.attr.srcs, ) diff --git a/ruby/tests/BUILD.bazel b/ruby/tests/BUILD.bazel index 247e44a..d48f6be 100644 --- a/ruby/tests/BUILD.bazel +++ b/ruby/tests/BUILD.bazel @@ -5,7 +5,10 @@ load( "ruby_test", ) load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar") -load("@io_bazel_rules_docker//container:container.bzl", "container_image") +load( + "@io_bazel_rules_docker//container:container.bzl", + "container_image", +) # Checks if args are correctly passed to the ruby script. ruby_test( From 27bcdd09276fb1152b727a79cfb354b26259cf50 Mon Sep 17 00:00:00 2001 From: Konstantin Gredeskoul Date: Sun, 5 Jan 2020 17:17:04 -0800 Subject: [PATCH 06/19] This PR refactors bundle steps and :bin generation --- examples/simple_script/BUILD.bazel | 11 +- ruby/private/binary_wrapper.tpl | 2 +- ruby/private/bundle/bundle.bzl | 65 ++++++----- .../bundle/create_bundle_build_file.rb | 108 ++++++++++++------ ruby/private/rspec.bzl | 1 + ruby/tests/BUILD.bazel | 2 +- ruby/tests/container_test.sh | 9 +- 7 files changed, 120 insertions(+), 78 deletions(-) diff --git a/examples/simple_script/BUILD.bazel b/examples/simple_script/BUILD.bazel index 0c82763..94bd0b1 100644 --- a/examples/simple_script/BUILD.bazel +++ b/examples/simple_script/BUILD.bazel @@ -54,7 +54,7 @@ ruby_binary( deps = [ ":lib", "//lib:foo", - "@bundle//:gems", + "@bundle//:bin", ], ) @@ -77,10 +77,7 @@ ruby_test( main = "spec/spec_helper.rb", rubyopt = ["-rrspec/autorun"], deps = [ - "@bundle//:awesome_print", - "@bundle//:colored2", - "@bundle//:rspec", - "@bundle//:rspec-its", + "@bundle//:gems", ], ) @@ -103,6 +100,7 @@ ruby_test( main = "@bundle//:bin/rspec", deps = [ "@bundle//:awesome_print", + "@bundle//:bin", "@bundle//:colored2", "@bundle//:rspec", "@bundle//:rspec-its", @@ -138,7 +136,6 @@ ruby_binary( srcs = [ ".relaxed-rubocop-2.4.yml", ".rubocop.yml", - "@bundle//:bin/rubocop", ], args = [ "-c", @@ -150,6 +147,6 @@ ruby_binary( deps = [ ":lib", "//lib:foo", - "@bundle//:rubocop", + "@bundle//:bin", ], ) diff --git a/ruby/private/binary_wrapper.tpl b/ruby/private/binary_wrapper.tpl index bfedfff..70e1ce7 100644 --- a/ruby/private/binary_wrapper.tpl +++ b/ruby/private/binary_wrapper.tpl @@ -98,7 +98,7 @@ def main(args) loadpaths = create_loadpath_entries(custom_loadpaths, runfiles) loadpaths += get_repository_imports(runfiles) loadpaths += ENV['RUBYLIB'].split(':') if ENV.key?('RUBYLIB') - ENV['RUBYLIB'] = loadpaths.join(':') + ENV['RUBYLIB'] = loadpaths.sort.uniq.join(':') runfiles_envkey, runfiles_envvalue = runfiles_envvar(runfiles) ENV[runfiles_envkey] = runfiles_envvalue if runfiles_envkey diff --git a/ruby/private/bundle/bundle.bzl b/ruby/private/bundle/bundle.bzl index 9c56e57..afe51ce 100644 --- a/ruby/private/bundle/bundle.bzl +++ b/ruby/private/bundle/bundle.bzl @@ -13,19 +13,23 @@ load("//ruby/private/tools:deprecations.bzl", "deprecated_attribute") # Runs bundler with arbitrary arguments # eg: run_bundler(runtime_ctx, [ "lock", " --gemfile", "Gemfile.rails5" ]) -def run_bundler(runtime_ctx, bundler_arguments): - #print("BUNDLE RUN", bundler_arguments) - +def run_bundler(runtime_ctx, bundler_arguments, previous_result): # Now we are running bundle install + bundler_command = bundler_arguments[0] + bundler_args = [] + + # add --verbose to all commands except install + if bundler_command != "install": + bundler_args += ["--verbose"] + + bundler_args += bundler_arguments[1:] + args = [ runtime_ctx.interpreter, # ruby - "--enable=gems", # bundler must run with rubygems enabled - "-I", - ".", "-I", # Used to tell Ruby where to load the library scripts BUNDLE_PATH, # Add vendor/bundle to the list of resolvers BUNDLE_BINARY, # our binary - ] + bundler_arguments + ] + [bundler_command] + bundler_args # print("Bundler Command:\n\n", args) @@ -41,7 +45,7 @@ def run_bundler(runtime_ctx, bundler_arguments): # $ bundle config --local | --global config-option config-value # # @config_category can be either 'local' or 'global' -def set_bundler_config(runtime_ctx, config_category = "local"): +def set_bundler_config(runtime_ctx, previous_result, config_category = "local"): # Bundler is deprecating various flags in favor of the configuration. # HOWEVER — for reasons I can't explain, Bazel runs "bundle install" *prior* # to setting these flags. So the flags are then useless until we can force the @@ -52,16 +56,20 @@ def set_bundler_config(runtime_ctx, config_category = "local"): bundler_config = { "deployment": "true", "standalone": "true", + "force": "false", + "redownload": "false", "frozen": "true", - "without": "development,test", "path": BUNDLE_PATH, "jobs": "20", + "shebang": runtime_ctx.interpreter, } - for option, value in bundler_config.items(): - args = ["config", "--%s" % (config_category), option, value] + last_result = previous_result - result = run_bundler(runtime_ctx, args) + for option, value in bundler_config.items(): + args = ["config", "set", "--%s" % (config_category), option, value] + result = run_bundler(runtime_ctx, args, last_result) + last_result = result if result.return_code: message = "Failed to set bundle config {} to {}: {}".format( option, @@ -70,8 +78,7 @@ def set_bundler_config(runtime_ctx, config_category = "local"): ) fail(message) - # The new way to generate binstubs is via the binstubs command, not config option. - return run_bundler(runtime_ctx, ["binstubs", "--path", BUNDLE_BIN_PATH]) + return last_result # This function is called "pure_ruby" because it downloads and unpacks the gem # file into a given folder, which for gems without C-extensions is the same @@ -96,6 +103,8 @@ def install_pure_ruby_gem(runtime_ctx, gem_name, gem_version, folder): result.stderr, ) fail(message) + else: + return result def install_bundler(runtime_ctx, bundler_version): return install_pure_ruby_gem( @@ -105,21 +114,26 @@ def install_bundler(runtime_ctx, bundler_version): "bundler", ) -def bundle_install(runtime_ctx): +def bundle_install(runtime_ctx, previous_result): result = run_bundler( runtime_ctx, [ - "install", # bundle install - "--standalone", # Makes a bundle that can work without depending on Rubygems or Bundler at runtime. - "--binstubs={}".format(BUNDLE_BIN_PATH), # Creates a directory and place any executables from the gem there. - "--path={}".format(BUNDLE_PATH), # The location to install the specified gems to. + "install", + "--binstubs={}".format(BUNDLE_BIN_PATH), + "--path={}".format(BUNDLE_PATH), + "--deployment", + "--standalone", + "--frozen", ], + previous_result, ) if result.return_code: fail("bundle install failed: %s%s" % (result.stdout, result.stderr)) + else: + return result -def generate_bundle_build_file(runtime_ctx): +def generate_bundle_build_file(runtime_ctx, previous_result): # Create the BUILD file to expose the gems to the WORKSPACE # USAGE: ./create_bundle_build_file.rb BUILD.bazel Gemfile.lock repo-name [excludes-json] workspace-name args = [ @@ -160,19 +174,16 @@ def _ruby_bundle_impl(ctx): ) # 1. Install the right version of the Bundler Gem - install_bundler(runtime_ctx, bundler_version) - - # Create label for the Bundler executable - bundler = Label("//:" + BUNDLE_BINARY) + result = install_bundler(runtime_ctx, bundler_version) # 2. Set Bundler config in the .bundle/config file - set_bundler_config(runtime_ctx) + result = set_bundler_config(runtime_ctx, result) # 3. Run bundle install - bundle_install(runtime_ctx) + result = bundle_install(runtime_ctx, result) # 4. Generate the BUILD file for the bundle - generate_bundle_build_file(runtime_ctx) + generate_bundle_build_file(runtime_ctx, result) ruby_bundle = repository_rule( implementation = _ruby_bundle_impl, diff --git a/ruby/private/bundle/create_bundle_build_file.rb b/ruby/private/bundle/create_bundle_build_file.rb index a23cd6f..9854fe8 100755 --- a/ruby/private/bundle/create_bundle_build_file.rb +++ b/ruby/private/bundle/create_bundle_build_file.rb @@ -1,7 +1,7 @@ #!/usr/bin/env ruby # frozen_string_literal: true -TEMPLATE = <<~MAIN_TEMPLATE +BUILD_HEADER = <<~MAIN_TEMPLATE load( "{workspace_name}//ruby:defs.bzl", "ruby_library", @@ -33,30 +33,33 @@ name = "{name}", srcs = glob( include = [ - "lib/ruby/{ruby_version}/gems/{name}-{version}/**/*", - "bin/*" + ".bundle/config", + "{gem_lib_files}", + {gem_binaries} ], exclude = {exclude}, ), deps = {deps}, includes = ["lib/ruby/{ruby_version}/gems/{name}-{version}/lib"], - rubyopt = ["{bundler_setup}"], ) GEM_TEMPLATE ALL_GEMS = <<~ALL_GEMS ruby_library( name = "gems", - srcs = glob( - {gems_lib_files}, - ), + srcs = glob([{gems_lib_files}]) + glob(["bin/*"]), includes = {gems_lib_paths}, - rubyopt = ["{bundler_setup}"], + ) + + ruby_library( + name = "bin", + srcs = glob(["bin/*"]), + deps = {gems_with_binaries} ) ALL_GEMS -GEM_LIB_PATH = ->(ruby_version, gem_name, gem_version) do - "lib/ruby/#{ruby_version}/gems/#{gem_name}-#{gem_version}/lib" +GEM_PATH = ->(ruby_version, gem_name, gem_version) do + "lib/ruby/#{ruby_version}/gems/#{gem_name}-#{gem_version}" end require 'bundler' @@ -84,7 +87,7 @@ def pink; colorize(35); end def light_blue; colorize(36); end - def orange; colorize(41); end + def orange; colorize(52); end # @formatter:on end @@ -117,6 +120,8 @@ def buildify! system("/usr/bin/env bash -c '#{command} 1>#{output_file} 2>&1'") code = $? + return unless File.exist?(output_file) + output = File.read(output_file).strip.gsub(Dir.pwd, '.').yellow begin FileUtils.rm_f(output_file) @@ -129,7 +134,7 @@ def buildify! else raise BuildifierFailedError, 'Generated BUILD file failed buildifier, with error — '.red + "\n\n" + - File.read(output_file).yellow + output.yellow end end end @@ -142,6 +147,10 @@ class BundleBuildFileGenerator :excludes, :ruby_version + DEFAULT_EXCLUDES = ['**/* *.*', '**/* */*'].freeze + + EXCLUDED_EXECUTABLES = %w(console setup).freeze + def initialize(workspace_name:, repo_name:, build_file: 'BUILD.bazel', @@ -161,35 +170,31 @@ def initialize(workspace_name:, def generate! # when we append to a string many times, using StringIO is more efficient. template_out = StringIO.new - - # In Bazel we want to use __FILE__ because __dir__points to the actual sources, and we are - # using symlinks here. - # - # rubocop:disable Style/ExpandPathArguments - bin_folder = File.expand_path('../bin', __FILE__) - binaries = Dir.glob("#{bin_folder}/*").map do |binary| - 'bin/' + File.basename(binary) if File.executable?(binary) - end - # rubocop:enable Style/ExpandPathArguments - - template_out.puts TEMPLATE + template_out.puts BUILD_HEADER .gsub('{workspace_name}', workspace_name) .gsub('{repo_name}', repo_name) .gsub('{ruby_version}', ruby_version) - .gsub('{binaries}', binaries.to_s) .gsub('{bundler_setup}', bundler_setup_require) # strip bundler version so we can process this file remove_bundler_version! + # Append to the end specific gem libraries and dependencies bundle = Bundler::LockfileParser.new(Bundler.read_file(gemfile_lock)) gem_lib_paths = [] - bundle.specs.each { |spec| register_gem(spec, template_out, gem_lib_paths) } + gems_binaries = {} # gem-name => [ gem's binaries ], ... + gems = bundle.specs.map(&:name) + + bundle.specs.each { |spec| register_gem(spec, template_out, gem_lib_paths, gems_binaries) } template_out.puts ALL_GEMS - .gsub('{gems_lib_files}', gem_lib_paths.map { |p| "#{p}/**/*.rb" }.to_s) + .gsub('{gems_lib_files}', to_flat_string(gem_lib_paths.map { |p| "#{p}/**/*" })) + .gsub('{gems_binaries}', gems_binaries.values.flatten.to_s) .gsub('{gems_lib_paths}', gem_lib_paths.to_s) .gsub('{bundler_setup}', bundler_setup_require) + .gsub('{gems_deps}', gems.map { |g| ":#{g}" }.to_s) + .gsub('{gems_with_binaries}', gems_binaries.keys.map { |g| ":#{g}" }.to_s) + .gsub('{exclude}', DEFAULT_EXCLUDES.to_s) ::File.open(build_file, 'w') { |f| f.puts template_out.string } end @@ -216,17 +221,23 @@ def remove_bundler_version! ::FileUtils.move(temp_gemfile_lock, gemfile_lock, force: true) end - def register_gem(spec, template_out, gem_lib_paths) - gem_lib_paths << GEM_LIB_PATH[ruby_version, spec.name, spec.version] + def register_gem(spec, template_out, gem_lib_paths, gems_binaries) + gem_path = GEM_PATH[ruby_version, spec.name, spec.version] + gem_lib_paths << gem_lib_path = gem_path + '/lib' + + # paths to search for executables + gem_binaries = find_gems_binaries(gem_path) + gems_binaries[spec.name] = gem_binaries unless gem_binaries.nil? || gem_binaries.empty? + deps = spec.dependencies.map { |d| ":#{d.name}" } - deps += [':bundler_setup'] - exclude_array = excludes[spec.name] || [] - # We want to exclude files and folder with spaces in them - exclude_array += ['**/* *.*', '**/* */*'] + warn("registering gem #{spec.name} with binaries: #{gem_binaries}") if gems_binaries.key?(spec.name) template_out.puts GEM_TEMPLATE - .gsub('{exclude}', exclude_array.to_s) + .gsub('{gem_lib_path}', gem_lib_path) + .gsub('{gem_lib_files}', gem_lib_path + '/**/*') + .gsub('{gem_binaries}', to_flat_string(gem_binaries)) + .gsub('{exclude}', exclude_array(spec.name).to_s) .gsub('{name}', spec.name) .gsub('{version}', spec.version.to_s) .gsub('{deps}', deps.to_s) @@ -234,6 +245,30 @@ def register_gem(spec, template_out, gem_lib_paths) .gsub('{ruby_version}', ruby_version) .gsub('{bundler_setup}', bundler_setup_require) end + + def find_gems_binaries(gem_path) + gem_bin_paths = %W(#{gem_path}/bin #{gem_path}/exe) + + gem_bin_paths + .map do |bin_path| + Dir # grab all files under bin/ and exe/ inside the gem folder + .glob("#{bin_path}/*") # convert to File object + .map { |b| f = File.new(b); File.executable?(f) ? f : nil } + .compact # remove non-executables, take basename, minus binary defaults + .map { |f| File.basename(f.path) } - EXCLUDED_EXECUTABLES # that bundler installs with bundle gem e - warn("ERROR running buildifier on the generated build file [#{build_file}] ➔ \n#{e.message.orange}") + warn("ERROR running buildifier on the generated build file [#{build_file}] ➔ #{e.message.orange}") end end diff --git a/ruby/private/rspec.bzl b/ruby/private/rspec.bzl index 334d9c3..e86603b 100644 --- a/ruby/private/rspec.bzl +++ b/ruby/private/rspec.bzl @@ -46,6 +46,7 @@ def ruby_rspec( rspec_gems = ["%s:%s" % (bundle, gem) for gem in DEFAULT_RSPEC_GEMS] deps += rspec_gems + deps += ["%s:bin" % bundle] ruby_rspec_test( name = name, diff --git a/ruby/tests/BUILD.bazel b/ruby/tests/BUILD.bazel index d48f6be..f1fe900 100644 --- a/ruby/tests/BUILD.bazel +++ b/ruby/tests/BUILD.bazel @@ -111,7 +111,7 @@ sh_test( # 6a) case 3 in the context of a genrule # # Also all of the cases above must correctly configure environment variables -# so that their subprocesses whose binaries are generated by Bazel can run with +# so that their sub-processes with binaries generated by Bazel can run with # their runfiles. ruby_binary( diff --git a/ruby/tests/container_test.sh b/ruby/tests/container_test.sh index 35326ac..bb827c2 100755 --- a/ruby/tests/container_test.sh +++ b/ruby/tests/container_test.sh @@ -7,15 +7,16 @@ if [[ $# -lt 2 ]]; then exit 1 fi -# check if we are running inside a Docker container, and skip this test if so. -if [[ -n "$(docker info 2>/dev/null| grep 'Cannot connect')" ]]; then +# check if we are running without access to Docker Server (eg, on CI +# within its own Docker container) and if so — skip this test. +if [[ -n "$(docker info 2>/dev/null | grep 'Cannot connect')" ]]; then echo "No Docker runtime detected, skipping tests." exit 0 else CONTAINER_IMAGE_LOADER="$1" CONTAINER_IMAGE_NAME="$2" - if [[ -n $(command -v ${CONTAINER_IMAGE_LOADER}) ]] ; then + if [[ -n $(command -v ${CONTAINER_IMAGE_LOADER}) ]]; then ${CONTAINER_IMAGE_LOADER} docker run "${CONTAINER_IMAGE_NAME}" else @@ -23,5 +24,3 @@ else exit 2 fi fi - - From ecf0fc5d1e065c7027474598cd3d3439c42a2702 Mon Sep 17 00:00:00 2001 From: Konstantin Gredeskoul Date: Thu, 16 Jan 2020 14:06:23 -0800 Subject: [PATCH 07/19] Fixing bad naming by replacing gems_.. with bundle_... --- examples/simple_script/.ruby-version | 2 +- examples/simple_script/WORKSPACE | 2 +- .../bundle/create_bundle_build_file.rb | 38 +++++++++---------- ruby/tests/container_test.sh | 2 +- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/examples/simple_script/.ruby-version b/examples/simple_script/.ruby-version index 6816713..57cf282 100644 --- a/examples/simple_script/.ruby-version +++ b/examples/simple_script/.ruby-version @@ -1 +1 @@ -2.6.5 \ No newline at end of file +2.6.5 diff --git a/examples/simple_script/WORKSPACE b/examples/simple_script/WORKSPACE index 9883f02..b57a0eb 100644 --- a/examples/simple_script/WORKSPACE +++ b/examples/simple_script/WORKSPACE @@ -21,10 +21,10 @@ load("@bazelruby_ruby_rules//ruby:defs.bzl", "ruby_bundle") ruby_bundle( name = "bundle", + bundler_version = "2.1.2", excludes = { "mini_portile": ["test/**/*"], }, gemfile = "//:Gemfile", gemfile_lock = "//:Gemfile.lock", - version = "2.0.2", ) diff --git a/ruby/private/bundle/create_bundle_build_file.rb b/ruby/private/bundle/create_bundle_build_file.rb index 9854fe8..ac95acf 100755 --- a/ruby/private/bundle/create_bundle_build_file.rb +++ b/ruby/private/bundle/create_bundle_build_file.rb @@ -47,14 +47,14 @@ ALL_GEMS = <<~ALL_GEMS ruby_library( name = "gems", - srcs = glob([{gems_lib_files}]) + glob(["bin/*"]), - includes = {gems_lib_paths}, + srcs = glob([{bundle_lib_files}]) + glob(["bin/*"]), + includes = {bundle_lib_paths}, ) ruby_library( name = "bin", srcs = glob(["bin/*"]), - deps = {gems_with_binaries} + deps = {bundle_with_binaries} ) ALL_GEMS @@ -180,20 +180,20 @@ def generate! remove_bundler_version! # Append to the end specific gem libraries and dependencies - bundle = Bundler::LockfileParser.new(Bundler.read_file(gemfile_lock)) - gem_lib_paths = [] - gems_binaries = {} # gem-name => [ gem's binaries ], ... - gems = bundle.specs.map(&:name) + bundle = Bundler::LockfileParser.new(Bundler.read_file(gemfile_lock)) + bundle_lib_paths = [] + bundle_binaries = {} # gem-name => [ gem's binaries ], ... + gems = bundle.specs.map(&:name) - bundle.specs.each { |spec| register_gem(spec, template_out, gem_lib_paths, gems_binaries) } + bundle.specs.each { |spec| register_gem(spec, template_out, bundle_lib_paths, bundle_binaries) } template_out.puts ALL_GEMS - .gsub('{gems_lib_files}', to_flat_string(gem_lib_paths.map { |p| "#{p}/**/*" })) - .gsub('{gems_binaries}', gems_binaries.values.flatten.to_s) - .gsub('{gems_lib_paths}', gem_lib_paths.to_s) + .gsub('{bundle_lib_files}', to_flat_string(bundle_lib_paths.map { |p| "#{p}/**/*" })) + .gsub('{bundle_with_binaries}', bundle_binaries.keys.map { |g| ":#{g}" }.to_s) + .gsub('{bundle_binaries}', bundle_binaries.values.flatten.to_s) + .gsub('{bundle_lib_paths}', bundle_lib_paths.to_s) .gsub('{bundler_setup}', bundler_setup_require) - .gsub('{gems_deps}', gems.map { |g| ":#{g}" }.to_s) - .gsub('{gems_with_binaries}', gems_binaries.keys.map { |g| ":#{g}" }.to_s) + .gsub('{bundle_deps}', gems.map { |g| ":#{g}" }.to_s) .gsub('{exclude}', DEFAULT_EXCLUDES.to_s) ::File.open(build_file, 'w') { |f| f.puts template_out.string } @@ -221,17 +221,17 @@ def remove_bundler_version! ::FileUtils.move(temp_gemfile_lock, gemfile_lock, force: true) end - def register_gem(spec, template_out, gem_lib_paths, gems_binaries) + def register_gem(spec, template_out, bundle_lib_paths, bundle_binaries) gem_path = GEM_PATH[ruby_version, spec.name, spec.version] - gem_lib_paths << gem_lib_path = gem_path + '/lib' + bundle_lib_paths << gem_lib_path = gem_path + '/lib' # paths to search for executables - gem_binaries = find_gems_binaries(gem_path) - gems_binaries[spec.name] = gem_binaries unless gem_binaries.nil? || gem_binaries.empty? + gem_binaries = find_bundle_binaries(gem_path) + bundle_binaries[spec.name] = gem_binaries unless gem_binaries.nil? || gem_binaries.empty? deps = spec.dependencies.map { |d| ":#{d.name}" } - warn("registering gem #{spec.name} with binaries: #{gem_binaries}") if gems_binaries.key?(spec.name) + warn("registering gem #{spec.name} with binaries: #{gem_binaries}") if bundle_binaries.key?(spec.name) template_out.puts GEM_TEMPLATE .gsub('{gem_lib_path}', gem_lib_path) @@ -246,7 +246,7 @@ def register_gem(spec, template_out, gem_lib_paths, gems_binaries) .gsub('{bundler_setup}', bundler_setup_require) end - def find_gems_binaries(gem_path) + def find_bundle_binaries(gem_path) gem_bin_paths = %W(#{gem_path}/bin #{gem_path}/exe) gem_bin_paths diff --git a/ruby/tests/container_test.sh b/ruby/tests/container_test.sh index bb827c2..c19180a 100755 --- a/ruby/tests/container_test.sh +++ b/ruby/tests/container_test.sh @@ -9,7 +9,7 @@ fi # check if we are running without access to Docker Server (eg, on CI # within its own Docker container) and if so — skip this test. -if [[ -n "$(docker info 2>/dev/null | grep 'Cannot connect')" ]]; then +if [[ -z $(command -v docker) || -n "$(docker info 2>/dev/null | grep 'Cannot connect')" ]]; then echo "No Docker runtime detected, skipping tests." exit 0 else From 3e4e871752c3c8a419bbad3810b348a096a28358 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Feb 2020 13:15:43 +0000 Subject: [PATCH 08/19] Bump nokogiri from 1.10.7 to 1.10.8 in /examples/simple_rails_api Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.10.7 to 1.10.8. - [Release notes](https://github.com/sparklemotion/nokogiri/releases) - [Changelog](https://github.com/sparklemotion/nokogiri/blob/master/CHANGELOG.md) - [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.10.7...v1.10.8) Signed-off-by: dependabot[bot] --- examples/simple_rails_api/Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/simple_rails_api/Gemfile.lock b/examples/simple_rails_api/Gemfile.lock index 1432e0b..9c9fca8 100644 --- a/examples/simple_rails_api/Gemfile.lock +++ b/examples/simple_rails_api/Gemfile.lock @@ -86,7 +86,7 @@ GEM minitest (5.13.0) msgpack (1.3.1) nio4r (2.5.2) - nokogiri (1.10.7) + nokogiri (1.10.8) mini_portile2 (~> 2.4.0) puma (4.3.1) nio4r (~> 2.0) From ab99ed69ee59d654191bd6ac65b7313082cbe7ef Mon Sep 17 00:00:00 2001 From: Konstantin Gredeskoul Date: Thu, 27 Feb 2020 03:48:08 -0800 Subject: [PATCH 09/19] Upgrade Bazel and Bazelist; add ruby 2.7.0 --- .bazelversion | 2 +- .circleci/Dockerfile | 6 +- .circleci/build.sh | 17 ++- .circleci/config.yml | 2 +- .ruby-version | 1 - Gemfile.lock | 12 +- WORKSPACE | 4 +- bin/deps | 2 +- bin/test-suite | 2 +- examples/simple_rails_api/.ruby-version | 2 +- examples/simple_rails_api/Gemfile | 2 - examples/simple_rails_api/Gemfile.lock | 135 +++++++++++------------ examples/simple_rails_api/WORKSPACE | 2 +- examples/simple_script/.ruby-version | 1 - examples/simple_script/Gemfile.lock | 4 +- examples/simple_script/WORKSPACE | 2 +- ruby/private/sdk.bzl | 2 +- ruby/private/toolchains/ruby_runtime.bzl | 8 +- 18 files changed, 105 insertions(+), 101 deletions(-) delete mode 100644 .ruby-version delete mode 100644 examples/simple_script/.ruby-version diff --git a/.bazelversion b/.bazelversion index 227cea2..3e3c2f1 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -2.0.0 +2.1.1 diff --git a/.circleci/Dockerfile b/.circleci/Dockerfile index 1419792..155b09c 100644 --- a/.circleci/Dockerfile +++ b/.circleci/Dockerfile @@ -1,7 +1,7 @@ # # rules_ruby circleci Docker file. # -FROM ruby:2.6.5-stretch +FROM ruby:2.7.0 # make Apt non-interactive RUN echo 'APT::Get::Assume-Yes "true";' > /etc/apt/apt.conf.d/90circleci \ @@ -90,9 +90,9 @@ RUN groupadd --gid 3434 circleci \ RUN apt-get update && apt-get upgrade -RUN apt-get install -y openjdk-8-jdk python2.7 python3 golang-go +RUN apt-get install -y openjdk-11-jdk python2.7 python3 golang-go -RUN curl -L -o /usr/bin/bazel https://github.com/bazelbuild/bazelisk/releases/download/v1.0/bazelisk-linux-amd64 \ +RUN curl -L -o /usr/bin/bazel https://github.com/bazelbuild/bazelisk/releases/download/v1.3.0/bazelisk-linux-amd64 \ && sudo chmod +x /usr/bin/bazel USER circleci diff --git a/.circleci/build.sh b/.circleci/build.sh index 819d7b5..b775eea 100755 --- a/.circleci/build.sh +++ b/.circleci/build.sh @@ -1,7 +1,18 @@ #!/usr/bin/env bash -set -ex +set -e -docker build . -t bazelruby/ruby-2.6.5 -docker push bazelruby/ruby-2.6.5 +[[ -d .circleci ]] && cd .circleci + +RUBY_VERSION=$(cat ../.ruby-version) + +echo +echo "Ruby version is $RUBY_VERSION" +echo + +set -x + +docker build . -t bazelruby/ruby-$RUBY_VERSION + +docker push bazelruby/ruby-$RUBY_VERSION diff --git a/.circleci/config.yml b/.circleci/config.yml index 868e720..675f65b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,7 +5,7 @@ jobs: working_directory: /home/circleci/repo resource_class: medium docker: - - image: bazelruby/ruby-2.6.5 + - image: bazelruby/ruby-2.7.0 environment: PATH: "/usr/local/bin:/usr/bin:/sbin:/opt/bin:/home/circleci/repo/bin:/bin:/sbin:/usr/sbin" BUNDLE_PATH: /home/circleci/.bundle_cache diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 57cf282..0000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.6.5 diff --git a/Gemfile.lock b/Gemfile.lock index f5e28f7..3080887 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,18 +4,20 @@ GEM ast (2.4.0) jaro_winkler (1.5.4) parallel (1.19.1) - parser (2.6.5.0) + parser (2.7.0.3) ast (~> 2.4.0) rainbow (3.0.0) - rubocop (0.78.0) + rexml (3.2.4) + rubocop (0.80.0) jaro_winkler (~> 1.5.1) parallel (~> 1.10) - parser (>= 2.6) + parser (>= 2.7.0.1) rainbow (>= 2.2.2, < 4.0) + rexml ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 1.7) ruby-progressbar (1.10.1) - unicode-display_width (1.6.0) + unicode-display_width (1.6.1) PLATFORMS ruby @@ -24,4 +26,4 @@ DEPENDENCIES rubocop (~> 0.78) BUNDLED WITH - 2.0.2 + 2.1.2 diff --git a/WORKSPACE b/WORKSPACE index 450bed6..dca9226 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,6 +1,6 @@ workspace(name = "bazelruby_ruby_rules") -load("@//ruby:deps.bzl", "ruby_rules_dependencies") +load("@//ruby:deps.bzl", "ruby_register_toolchains", "ruby_rules_dependencies") ruby_rules_dependencies() @@ -12,8 +12,6 @@ load("@bazel_skylib//lib:versions.bzl", "versions") versions.check("1.2.1") -load("@//ruby:deps.bzl", "ruby_register_toolchains") - ruby_register_toolchains() local_repository( diff --git a/bin/deps b/bin/deps index fc36cbd..9244cc6 100755 --- a/bin/deps +++ b/bin/deps @@ -27,7 +27,7 @@ __version::detect() { # Application Constants export RulesRuby__RulesVersion=$(__version::detect .rules_version) -export RulesRuby__RubyVersion=$(__version::detect .ruby_version) +export RulesRuby__RubyVersion=2.7.0 export RulesRuby__BazelVersion=$(__version::detect .bazelversion) bazel-sha() { diff --git a/bin/test-suite b/bin/test-suite index 6b489ea..ebe43b7 100755 --- a/bin/test-suite +++ b/bin/test-suite @@ -12,7 +12,7 @@ export PATH="${HOME}/.rbenv/bin:${HOME}/.rbenv/shims:/usr/local/bin:/usr/bin:/bi export BAZEL_OPTS="--host_jvm_args=-Xmx500m --host_jvm_args=-Xms500m" export BAZEL_BUILD_OPTS="--curses=no --verbose_failures -j 15 --show_progress_rate_limit 0 " export BAZEL_TEST_OPTS="--verbose_failures --test_output=streamed --test_verbose_timeout_warnings " -export RUBY_VERSION=$(cat .ruby-version) +export RUBY_VERSION=2.7.0 export BashMatic__Expr=" [[ -f ${SHELL_INIT} ]] && source ${SHELL_INIT} diff --git a/examples/simple_rails_api/.ruby-version b/examples/simple_rails_api/.ruby-version index 57cf282..24ba9a3 100644 --- a/examples/simple_rails_api/.ruby-version +++ b/examples/simple_rails_api/.ruby-version @@ -1 +1 @@ -2.6.5 +2.7.0 diff --git a/examples/simple_rails_api/Gemfile b/examples/simple_rails_api/Gemfile index af10772..8f98992 100644 --- a/examples/simple_rails_api/Gemfile +++ b/examples/simple_rails_api/Gemfile @@ -1,8 +1,6 @@ source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } -ruby '2.6.5' - # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '~> 6.0.2' # Use sqlite3 as the database for Active Record diff --git a/examples/simple_rails_api/Gemfile.lock b/examples/simple_rails_api/Gemfile.lock index 9c9fca8..c89e612 100644 --- a/examples/simple_rails_api/Gemfile.lock +++ b/examples/simple_rails_api/Gemfile.lock @@ -1,72 +1,72 @@ GEM remote: https://rubygems.org/ specs: - actioncable (6.0.2) - actionpack (= 6.0.2) + actioncable (6.0.2.1) + actionpack (= 6.0.2.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (6.0.2) - actionpack (= 6.0.2) - activejob (= 6.0.2) - activerecord (= 6.0.2) - activestorage (= 6.0.2) - activesupport (= 6.0.2) + actionmailbox (6.0.2.1) + actionpack (= 6.0.2.1) + activejob (= 6.0.2.1) + activerecord (= 6.0.2.1) + activestorage (= 6.0.2.1) + activesupport (= 6.0.2.1) mail (>= 2.7.1) - actionmailer (6.0.2) - actionpack (= 6.0.2) - actionview (= 6.0.2) - activejob (= 6.0.2) + actionmailer (6.0.2.1) + actionpack (= 6.0.2.1) + actionview (= 6.0.2.1) + activejob (= 6.0.2.1) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (6.0.2) - actionview (= 6.0.2) - activesupport (= 6.0.2) - rack (~> 2.0) + actionpack (6.0.2.1) + actionview (= 6.0.2.1) + activesupport (= 6.0.2.1) + rack (~> 2.0, >= 2.0.8) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.0.2) - actionpack (= 6.0.2) - activerecord (= 6.0.2) - activestorage (= 6.0.2) - activesupport (= 6.0.2) + actiontext (6.0.2.1) + actionpack (= 6.0.2.1) + activerecord (= 6.0.2.1) + activestorage (= 6.0.2.1) + activesupport (= 6.0.2.1) nokogiri (>= 1.8.5) - actionview (6.0.2) - activesupport (= 6.0.2) + actionview (6.0.2.1) + activesupport (= 6.0.2.1) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (6.0.2) - activesupport (= 6.0.2) + activejob (6.0.2.1) + activesupport (= 6.0.2.1) globalid (>= 0.3.6) - activemodel (6.0.2) - activesupport (= 6.0.2) - activerecord (6.0.2) - activemodel (= 6.0.2) - activesupport (= 6.0.2) - activestorage (6.0.2) - actionpack (= 6.0.2) - activejob (= 6.0.2) - activerecord (= 6.0.2) + activemodel (6.0.2.1) + activesupport (= 6.0.2.1) + activerecord (6.0.2.1) + activemodel (= 6.0.2.1) + activesupport (= 6.0.2.1) + activestorage (6.0.2.1) + actionpack (= 6.0.2.1) + activejob (= 6.0.2.1) + activerecord (= 6.0.2.1) marcel (~> 0.3.1) - activesupport (6.0.2) + activesupport (6.0.2.1) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) zeitwerk (~> 2.2) - bootsnap (1.4.5) + bootsnap (1.4.6) msgpack (~> 1.0) builder (3.2.4) - byebug (11.0.1) - concurrent-ruby (1.1.5) - crass (1.0.5) + byebug (11.1.1) + concurrent-ruby (1.1.6) + crass (1.0.6) erubi (1.9.0) - ffi (1.11.3) + ffi (1.12.2) globalid (0.4.2) activesupport (>= 4.2.0) - i18n (1.7.0) + i18n (1.8.2) concurrent-ruby (~> 1.0) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) @@ -80,48 +80,48 @@ GEM marcel (0.3.3) mimemagic (~> 0.3.2) method_source (0.9.2) - mimemagic (0.3.3) + mimemagic (0.3.4) mini_mime (1.0.2) mini_portile2 (2.4.0) - minitest (5.13.0) - msgpack (1.3.1) + minitest (5.14.0) + msgpack (1.3.3) nio4r (2.5.2) nokogiri (1.10.8) mini_portile2 (~> 2.4.0) puma (4.3.1) nio4r (~> 2.0) - rack (2.0.8) + rack (2.2.2) rack-test (1.1.0) rack (>= 1.0, < 3) - rails (6.0.2) - actioncable (= 6.0.2) - actionmailbox (= 6.0.2) - actionmailer (= 6.0.2) - actionpack (= 6.0.2) - actiontext (= 6.0.2) - actionview (= 6.0.2) - activejob (= 6.0.2) - activemodel (= 6.0.2) - activerecord (= 6.0.2) - activestorage (= 6.0.2) - activesupport (= 6.0.2) + rails (6.0.2.1) + actioncable (= 6.0.2.1) + actionmailbox (= 6.0.2.1) + actionmailer (= 6.0.2.1) + actionpack (= 6.0.2.1) + actiontext (= 6.0.2.1) + actionview (= 6.0.2.1) + activejob (= 6.0.2.1) + activemodel (= 6.0.2.1) + activerecord (= 6.0.2.1) + activestorage (= 6.0.2.1) + activesupport (= 6.0.2.1) bundler (>= 1.3.0) - railties (= 6.0.2) + railties (= 6.0.2.1) sprockets-rails (>= 2.0.0) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) rails-html-sanitizer (1.3.0) loofah (~> 2.3) - railties (6.0.2) - actionpack (= 6.0.2) - activesupport (= 6.0.2) + railties (6.0.2.1) + actionpack (= 6.0.2.1) + activesupport (= 6.0.2.1) method_source rake (>= 0.8.7) thor (>= 0.20.3, < 2.0) rake (13.0.1) rb-fsevent (0.10.3) - rb-inotify (0.10.0) + rb-inotify (0.10.1) ffi (~> 1.0) ruby_dep (1.5.0) spring (2.1.0) @@ -135,10 +135,10 @@ GEM actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) - sqlite3 (1.4.1) - thor (1.0.0) + sqlite3 (1.4.2) + thor (1.0.1) thread_safe (0.3.6) - tzinfo (1.2.5) + tzinfo (1.2.6) thread_safe (~> 0.1) websocket-driver (0.7.1) websocket-extensions (>= 0.1.0) @@ -159,8 +159,5 @@ DEPENDENCIES sqlite3 (~> 1.4) tzinfo-data -RUBY VERSION - ruby 2.6.5p114 - BUNDLED WITH - 2.0.2 + 2.1.2 diff --git a/examples/simple_rails_api/WORKSPACE b/examples/simple_rails_api/WORKSPACE index d4e4f2c..daef2e1 100644 --- a/examples/simple_rails_api/WORKSPACE +++ b/examples/simple_rails_api/WORKSPACE @@ -15,7 +15,7 @@ load( ruby_rules_dependencies() -ruby_register_toolchains(version = "2.6.5") +ruby_register_toolchains(version = "2.7.0") load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") diff --git a/examples/simple_script/.ruby-version b/examples/simple_script/.ruby-version deleted file mode 100644 index 57cf282..0000000 --- a/examples/simple_script/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.6.5 diff --git a/examples/simple_script/Gemfile.lock b/examples/simple_script/Gemfile.lock index f49663c..c2b42fd 100644 --- a/examples/simple_script/Gemfile.lock +++ b/examples/simple_script/Gemfile.lock @@ -7,7 +7,7 @@ GEM diff-lcs (1.3) jaro_winkler (1.5.4) parallel (1.19.1) - parser (2.6.5.0) + parser (2.7.0.3) ast (~> 2.4.0) rainbow (3.0.0) rspec (3.7.0) @@ -34,7 +34,7 @@ GEM ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 1.7) ruby-progressbar (1.10.1) - unicode-display_width (1.6.0) + unicode-display_width (1.6.1) PLATFORMS ruby diff --git a/examples/simple_script/WORKSPACE b/examples/simple_script/WORKSPACE index b57a0eb..bd722d6 100644 --- a/examples/simple_script/WORKSPACE +++ b/examples/simple_script/WORKSPACE @@ -15,7 +15,7 @@ load( ruby_rules_dependencies() -ruby_register_toolchains(version = "2.6.5") +ruby_register_toolchains(version = "2.7.0") load("@bazelruby_ruby_rules//ruby:defs.bzl", "ruby_bundle") diff --git a/ruby/private/sdk.bzl b/ruby/private/sdk.bzl index 12b5046..5fddabd 100644 --- a/ruby/private/sdk.bzl +++ b/ruby/private/sdk.bzl @@ -6,7 +6,7 @@ load( def ruby_register_toolchains(version = "host"): """Registers ruby toolchains in the WORKSPACE file.""" - supported_versions = ["host", "2.6.3", "2.6.5"] + supported_versions = ["host", "2.6.3", "2.6.5", "2.7.0"] if version in supported_versions: _ruby_runtime( name = "org_ruby_lang_ruby_toolchain", diff --git a/ruby/private/toolchains/ruby_runtime.bzl b/ruby/private/toolchains/ruby_runtime.bzl index 875f6d3..48550eb 100644 --- a/ruby/private/toolchains/ruby_runtime.bzl +++ b/ruby/private/toolchains/ruby_runtime.bzl @@ -4,9 +4,9 @@ load("//ruby/private/toolchains:repository_context.bzl", "ruby_repository_contex def _install_ruby_version(ctx, version): print("download and extract ruby-build") ctx.download_and_extract( - url = "https://github.com/rbenv/ruby-build/archive/v20191205.tar.gz", - sha256 = "d8ffe806a215b3afacead72e766f293ce380c78a143911b84cdb5f33e20a5284", - stripPrefix = "ruby-build-20191205", + url = "https://github.com/rbenv/ruby-build/archive/v20200224.tar.gz", + sha256 = "dc3799a1c784c9a0f214a3c0c861a0bb798cd40ee2df49bce95b4c95adf6fc79", + stripPrefix = "ruby-build-20200224", ) install_path = "./build" @@ -39,7 +39,7 @@ def _relativate(path): def _list_libdirs(ruby): """List the LOAD_PATH of the ruby""" - paths = ruby.eval(ruby, 'print $:.join("\\n")') + paths = ruby.eval(ruby, "print $:.join(\"\\n\")") paths = sorted(paths.split("\n")) rel_paths = [_relativate(path) for path in paths] return (paths, rel_paths) From 65009d44b44e86fe5367533f52c1e658c7e8f4a2 Mon Sep 17 00:00:00 2001 From: Konstantin Gredeskoul Date: Thu, 27 Feb 2020 23:57:16 -0800 Subject: [PATCH 10/19] Merging Coinbase changes selectively --- .circleci/.bazelrc | 21 +++- WORKSPACE | 4 +- examples/simple_script/BUILD.bazel | 108 +++++------------- examples/simple_script/README.md | 2 +- examples/simple_script/script.rb | 1 - examples/simple_script/spec/script_spec.rb | 2 +- ruby/defs.bzl | 14 ++- ruby/private/binary.bzl | 14 +++ ruby/private/binary_wrapper.tpl | 2 + .../bundle/create_bundle_build_file.rb | 3 + ruby/private/rubocop/BUILD.bazel | 6 + ruby/private/rubocop/def.bzl | 20 ++++ ruby/private/rubocop/runner.sh.tpl | 5 + 13 files changed, 109 insertions(+), 93 deletions(-) create mode 100644 ruby/private/rubocop/BUILD.bazel create mode 100644 ruby/private/rubocop/def.bzl create mode 100644 ruby/private/rubocop/runner.sh.tpl diff --git a/.circleci/.bazelrc b/.circleci/.bazelrc index 4a379b6..9e3e7ed 100644 --- a/.circleci/.bazelrc +++ b/.circleci/.bazelrc @@ -1,6 +1,17 @@ -build --verbose_failures --spawn_strategy=standalone --strategy=Genrule=standalone --show_timestamps --show_progress --show_progress_rate_limit 0.5 -test --spawn_strategy=standalone +# Common options common --color=yes -startup --max_idle_secs=1 -test --verbose_failures --test_output=errors --test_verbose_timeout_warnings --show_progress --show_progress_rate_limit 0.5 -run --verbose_failures --show_progress --show_progress_rate_limit 0.5 +common --verbose_failures +common --show_progress +common --show_progress_rate_limit=0.5 + +# Build options +build --spawn_strategy=standalone +build --strategy=Genrule=standalone +build --show_timestamps +build --curses=no +build --jobs=10 + +# Test options +test --spawn_strategy=standalone +test --test_output=all +test --test_verbose_timeout_warnings diff --git a/WORKSPACE b/WORKSPACE index dca9226..2465f8d 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -91,9 +91,9 @@ container_pull( repository = "library/ruby", ) -load("@bazelruby_ruby_rules//ruby:defs.bzl", "bundle_install") +load("@bazelruby_ruby_rules//ruby:defs.bzl", "ruby_bundle") -bundle_install( +ruby_bundle( name = "bundle", bundler_version = "2.1.2", excludes = { diff --git a/examples/simple_script/BUILD.bazel b/examples/simple_script/BUILD.bazel index 94bd0b1..e9e382e 100644 --- a/examples/simple_script/BUILD.bazel +++ b/examples/simple_script/BUILD.bazel @@ -1,107 +1,43 @@ -package(default_visibility = ["//:__subpackages__"]) - load( "@bazelruby_ruby_rules//ruby:defs.bzl", "ruby_binary", - "ruby_library", "ruby_rspec", + "ruby_rubocop", "ruby_test", ) -filegroup( - name = "sources", - srcs = glob([ - "script.rb", - "lib/*.rb", - ]), -) - -filegroup( - name = "spec_sources", - srcs = glob([ - "spec/**/*.rb", - ]), -) - -ruby_library( - name = "lib", - srcs = [ - ":sources", - ":spec_sources", - ], - deps = [ - "@bundle//:awesome_print", - "@bundle//:colored2", - ], -) +package(default_visibility = ["//:__subpackages__"]) ruby_binary( name = "bin", srcs = ["script.rb"], main = "script.rb", deps = [ - ":lib", "//lib:foo", "@bundle//:awesome_print", - "@bundle//:colored2", - ], -) - -ruby_binary( - name = "bin-all", - srcs = ["script.rb"], - main = "script.rb", - deps = [ - ":lib", - "//lib:foo", - "@bundle//:bin", ], ) # This is an example of the RSpec definition that uses autorun # and points to spec_helper as the main spec file. It specifies # which specs to run using the args. + ruby_test( - name = "rspec-autorun", + name = "all-specs", timeout = "short", srcs = [ - ":sources", - ":spec_sources", - ], - args = [ - "--format documentation", - "--force-color", + "script.rb", + "//lib:foo", ] + glob([ "spec/**/*.rb", ]), - main = "spec/spec_helper.rb", - rubyopt = ["-rrspec/autorun"], - deps = [ - "@bundle//:gems", - ], -) - -# This is a similar example, except instead of using rubyopt to load -# rspec, we execute rspec executable located in the bin folder under -# the bundle, accessible via @bundle//:bin/rspec -ruby_test( - name = "rspec-binary", - timeout = "short", - srcs = [ - ":sources", - ":spec_sources", - "@bundle//:bin/rspec", - ], args = [ - "--format documentation", - "--force-color", "spec", ], main = "@bundle//:bin/rspec", deps = [ "@bundle//:awesome_print", "@bundle//:bin", - "@bundle//:colored2", "@bundle//:rspec", "@bundle//:rspec-its", ], @@ -115,8 +51,8 @@ ruby_test( ruby_rspec( name = "ruby-rspec-test", srcs = [ - ":sources", - ":spec_sources", + "script.rb", + "//lib:foo", ], rspec_args = { # NOTE: the output is only visible with --test_output=streamed flag @@ -127,26 +63,34 @@ ruby_rspec( ]), deps = [ "@bundle//:awesome_print", - "@bundle//:colored2", ], ) ruby_binary( - name = "rubocop", + name = "rubocop-bin", srcs = [ - ".relaxed-rubocop-2.4.yml", - ".rubocop.yml", - ], + "script.rb", + "//lib:foo", + ] + glob([ + "spec/**/*.rb", + ]), args = [ - "-c", - ".rubocop.yml", - "-P", - "-D", + "-- *.rb spec/*.rb lib/*.rb -a", ], main = "@bundle//:bin/rubocop", deps = [ - ":lib", "//lib:foo", "@bundle//:bin", ], ) + +# Rubocop rule +# To check +# bazel run rubocop -- -a +ruby_rubocop( + name = "rubocop", + bin = "@bundle//:bin/rubocop", + deps = [ + "@bundle//:rubocop", + ], +) diff --git a/examples/simple_script/README.md b/examples/simple_script/README.md index d4d2040..edc2820 100644 --- a/examples/simple_script/README.md +++ b/examples/simple_script/README.md @@ -15,5 +15,5 @@ bundle lock --update Run rubocop with: ``` -bazel run :rubocop -- $(pwd)/* -a +bazel run //:rubocop ``` \ No newline at end of file diff --git a/examples/simple_script/script.rb b/examples/simple_script/script.rb index cc3d793..308391e 100644 --- a/examples/simple_script/script.rb +++ b/examples/simple_script/script.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require 'colored2' require 'openssl' require 'awesome_print' diff --git a/examples/simple_script/spec/script_spec.rb b/examples/simple_script/spec/script_spec.rb index d246cac..458b928 100644 --- a/examples/simple_script/spec/script_spec.rb +++ b/examples/simple_script/spec/script_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -require_relative '../script' +require 'script' describe 'oss_rand' do it 'generates a String' do diff --git a/ruby/defs.bzl b/ruby/defs.bzl index abc31c5..6fd9f84 100644 --- a/ruby/defs.bzl +++ b/ruby/defs.bzl @@ -20,6 +20,10 @@ load( _ruby_rspec = "ruby_rspec", _ruby_rspec_test = "ruby_rspec_test", ) +load( + "@bazelruby_ruby_rules//ruby/private/rubocop:def.bzl", + _rubocop = "rubocop", +) ruby_toolchain = _toolchain ruby_library = _library @@ -27,5 +31,13 @@ ruby_binary = _binary ruby_test = _test ruby_rspec_test = _ruby_rspec_test ruby_rspec = _ruby_rspec -bundle_install = _ruby_bundle ruby_bundle = _ruby_bundle +ruby_rubocop = _rubocop + +rb_toolchain = _toolchain +rb_library = _library +rb_binary = _binary +rb_test = _test +rb_rspec = _ruby_rspec +rb_bundle = _ruby_bundle +rb_rubocop = _rubocop diff --git a/ruby/private/binary.bzl b/ruby/private/binary.bzl index 6d34b48..a5d0f70 100644 --- a/ruby/private/binary.bzl +++ b/ruby/private/binary.bzl @@ -10,6 +10,17 @@ def _to_manifest_path(ctx, file): else: return ("%s/%s" % (ctx.workspace_name, file.short_path)) +def _get_gem_path(incpaths): + """ + incpaths is a list of `/lib/ruby//gems/-/lib` + The gem_path is `/lib/ruby/` so we can go from an incpath to the + gem_path pretty easily without much additional work. + """ + if len(incpaths) == 0: + return "" + incpath = incpaths[0] + return incpath.rsplit("/", 3)[0] + # Having this function allows us to override otherwise frozen attributes # such as main, srcs and deps. We use this in ruby_rspec_test rule by # adding rspec as a main, and sources, and rspec gem as a dependency. @@ -41,6 +52,8 @@ def ruby_binary_macro(ctx, main, srcs): extra_deps = ctx.attr._misc_deps, ) + gem_path = _get_gem_path(deps.incpaths.to_list()) + rubyopt = reversed(deps.rubyopt.to_list()) ctx.actions.expand_template( @@ -51,6 +64,7 @@ def ruby_binary_macro(ctx, main, srcs): "{rubyopt}": repr(rubyopt), "{main}": repr(_to_manifest_path(ctx, main)), "{interpreter}": _to_manifest_path(ctx, interpreter), + "{gem_path}": gem_path, }, ) diff --git a/ruby/private/binary_wrapper.tpl b/ruby/private/binary_wrapper.tpl index 70e1ce7..e7a008f 100644 --- a/ruby/private/binary_wrapper.tpl +++ b/ruby/private/binary_wrapper.tpl @@ -103,6 +103,8 @@ def main(args) runfiles_envkey, runfiles_envvalue = runfiles_envvar(runfiles) ENV[runfiles_envkey] = runfiles_envvalue if runfiles_envkey + ENV["GEM_PATH"] = File.join(runfiles, "{gem_path}") if "{gem_path}" + ruby_program = find_ruby_binary main = {main} diff --git a/ruby/private/bundle/create_bundle_build_file.rb b/ruby/private/bundle/create_bundle_build_file.rb index ac95acf..8ed8298 100755 --- a/ruby/private/bundle/create_bundle_build_file.rb +++ b/ruby/private/bundle/create_bundle_build_file.rb @@ -35,10 +35,12 @@ include = [ ".bundle/config", "{gem_lib_files}", + "lib/ruby/{ruby_version}/specifications/{name}-{version}.gemspec", {gem_binaries} ], exclude = {exclude}, ), + rubyopt = ["{bundler_setup}"], deps = {deps}, includes = ["lib/ruby/{ruby_version}/gems/{name}-{version}/lib"], ) @@ -49,6 +51,7 @@ name = "gems", srcs = glob([{bundle_lib_files}]) + glob(["bin/*"]), includes = {bundle_lib_paths}, + rubyopt = ["{bundler_setup}"], ) ruby_library( diff --git a/ruby/private/rubocop/BUILD.bazel b/ruby/private/rubocop/BUILD.bazel new file mode 100644 index 0000000..94ca1ed --- /dev/null +++ b/ruby/private/rubocop/BUILD.bazel @@ -0,0 +1,6 @@ +package(default_visibility = ["//ruby/private:__pkg__"]) + +exports_files( + ["runner.sh.tpl"], + visibility = ["//visibility:public"], +) diff --git a/ruby/private/rubocop/def.bzl b/ruby/private/rubocop/def.bzl new file mode 100644 index 0000000..71a37f6 --- /dev/null +++ b/ruby/private/rubocop/def.bzl @@ -0,0 +1,20 @@ +load("@bazelruby_ruby_rules//ruby/private:binary.bzl", "ruby_binary") + +# This wraps an rb_binary in a script that is executed from the workspace folder +def rubocop(name, bin, deps): + bin_name = name + "-ruby" + ruby_binary( + name = bin_name, + main = bin, + deps = deps, + ) + + runner = "@bazelruby_ruby_rules//ruby/private/rubocop:runner.sh.tpl" + native.genrule( + name = name, + tools = [bin_name], + srcs = [runner], + executable = True, + outs = [name + ".sh"], + cmd = "sed \"s~{{BIN}}~$(location %s)~g\" $(location %s) > \"$@\"" % (bin_name, runner), + ) diff --git a/ruby/private/rubocop/runner.sh.tpl b/ruby/private/rubocop/runner.sh.tpl new file mode 100644 index 0000000..d0f3d52 --- /dev/null +++ b/ruby/private/rubocop/runner.sh.tpl @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +( + cd $BUILD_WORKSPACE_DIRECTORY && \ + {{BIN}} $@ +) From 08847a2aaaeb9b595749d7f6e2e8a11466ade3f6 Mon Sep 17 00:00:00 2001 From: Fredrik Eckardt Date: Tue, 4 Feb 2020 22:08:32 +0000 Subject: [PATCH 11/19] Merging Coinbase PR: https://github.com/coinbase/rules_ruby/pull/10 Feature: Add rb_gem and rb_gemspec --- README.md | 80 ++++++++++++++++++++++- WORKSPACE | 4 ++ bin/test-suite | 9 ++- examples/example_gem/BUILD | 16 +++++ examples/example_gem/WORKSPACE | 22 +++++++ examples/example_gem/lib/BUILD | 12 ++++ examples/example_gem/lib/example_gem.rb | 5 ++ examples/example_gem/lib/foo/BUILD | 11 ++++ examples/example_gem/lib/foo/bar.rb | 9 +++ ruby/defs.bzl | 13 ++++ ruby/private/BUILD.bazel | 5 +- ruby/private/dependencies.bzl | 11 ++++ ruby/private/gem.bzl | 31 +++++++++ ruby/private/gemspec.bzl | 87 +++++++++++++++++++++++++ ruby/private/gemspec_template.tpl | 10 +++ ruby/private/providers.bzl | 8 +++ 16 files changed, 330 insertions(+), 3 deletions(-) create mode 100644 examples/example_gem/BUILD create mode 100644 examples/example_gem/WORKSPACE create mode 100644 examples/example_gem/lib/BUILD create mode 100644 examples/example_gem/lib/example_gem.rb create mode 100644 examples/example_gem/lib/foo/BUILD create mode 100644 examples/example_gem/lib/foo/bar.rb create mode 100644 ruby/private/gem.bzl create mode 100644 ruby/private/gemspec.bzl create mode 100644 ruby/private/gemspec_template.tpl diff --git a/README.md b/README.md index e397e20..5827ad2 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ * [ruby_binary](#ruby_binary) * [ruby_test](#ruby_test) * [ruby_bundle](#ruby_bundle) + * [ruby_gem](#rb_gem) * [What's coming next](#whats-coming-next) * [Contributing](#contributing) * [Setup](#setup) @@ -483,11 +484,88 @@ ruby_bundle(name, gemfile, gemfile_lock, bundler_version = "2.1.2") +## rb_gem +Used to generate a zipped gem containing its srcs, dependencies and a gemspec. + +
+rb_gem(name, gem_name, version, srcs, authors, deps, data, includes)
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Attributes
name + Name, required +

A unique name for this rule.

+
gem_name + Name of the gem, required +

The name of the gem to be generated.

+
version + Label, required +

+ The version of the gem. Is used to name the output file, + which becomes name-version.zip, and also + included in the Gemspec. +

+
authors + List of Strings, required +

+ List of human readable names of the gem authors. + Required to generate a valid gemspec. +

+
srcs + List of Labels, optional +

+ List of .rb files. +

+

At least srcs or deps must be present

+
deps + List of labels, optional +

+ List of targets that are required by the srcs Ruby + files. +

+

At least srcs or deps must be present

+
+ ## What's coming next 1. Building native extensions in gems with Bazel 2. Using a specified version of Ruby. -3. Building and releasing your gems with Bazel +3. Releasing your gems with Bazel ## Contributing diff --git a/WORKSPACE b/WORKSPACE index 2465f8d..d31dee8 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -102,3 +102,7 @@ ruby_bundle( gemfile = "//:Gemfile", gemfile_lock = "//:Gemfile.lock", ) + +load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies") + +rules_pkg_dependencies() diff --git a/bin/test-suite b/bin/test-suite index ebe43b7..54a6227 100755 --- a/bin/test-suite +++ b/bin/test-suite @@ -147,10 +147,17 @@ test::buildifier() { test::simple-script() { __test::exec simple-script " cd examples/simple_script - ${Bazel__BuildSteps} + ${Bazel__BuildSteps} echo run :bin; bazel ${BAZEL_OPTS} run ${BAZEL_BUILD_OPTS} :bin echo run :rubocop; bazel ${BAZEL_OPTS} run ${BAZEL_BUILD_OPTS} :rubocop + " +} +# Builds and runs workspace inside examples/simple_script +test::example-gem() { + __test::exec example-gem " + cd examples/example_gem + echo bazel ${BAZEL_OPTS} build ...:all; bazel ${BAZEL_OPTS} build ...:all " } diff --git a/examples/example_gem/BUILD b/examples/example_gem/BUILD new file mode 100644 index 0000000..8fb980b --- /dev/null +++ b/examples/example_gem/BUILD @@ -0,0 +1,16 @@ +package(default_visibility = ["//:__subpackages__"]) + +load( + "@bazelruby_ruby_rules//ruby:defs.bzl", + "rb_gem", +) + +rb_gem( + name = "default_gem", + authors = ["Coinbase"], + gem_name = "example_gem", + version = "0.1.0", + deps = [ + "//lib:example_gem", + ], +) diff --git a/examples/example_gem/WORKSPACE b/examples/example_gem/WORKSPACE new file mode 100644 index 0000000..3b377a5 --- /dev/null +++ b/examples/example_gem/WORKSPACE @@ -0,0 +1,22 @@ +workspace(name = "bazelruby_ruby_rules_example_gem") + +# Importing rules_ruby from the parent directory for developing +# rules_ruby itself... +local_repository( + name = "bazelruby_ruby_rules", + path = "../..", +) + +load( + "@bazelruby_ruby_rules//ruby:deps.bzl", + "ruby_register_toolchains", + "ruby_rules_dependencies", +) + +ruby_rules_dependencies() + +ruby_register_toolchains() + +load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") + +bazel_skylib_workspace() diff --git a/examples/example_gem/lib/BUILD b/examples/example_gem/lib/BUILD new file mode 100644 index 0000000..e98ee71 --- /dev/null +++ b/examples/example_gem/lib/BUILD @@ -0,0 +1,12 @@ +package(default_visibility = ["//:__subpackages__"]) + +load( + "@bazelruby_ruby_rules//ruby:defs.bzl", + "rb_library", +) + +rb_library( + name = "example_gem", + srcs = ["example_gem.rb"], + deps = ["//lib/foo:default_library"], +) diff --git a/examples/example_gem/lib/example_gem.rb b/examples/example_gem/lib/example_gem.rb new file mode 100644 index 0000000..f41ee6f --- /dev/null +++ b/examples/example_gem/lib/example_gem.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require_relative 'foo/bar' + +puts 'Foo' diff --git a/examples/example_gem/lib/foo/BUILD b/examples/example_gem/lib/foo/BUILD new file mode 100644 index 0000000..d08a459 --- /dev/null +++ b/examples/example_gem/lib/foo/BUILD @@ -0,0 +1,11 @@ +package(default_visibility = ["//:__subpackages__"]) + +load( + "@bazelruby_ruby_rules//ruby:defs.bzl", + "rb_library", +) + +rb_library( + name = "default_library", + srcs = ["bar.rb"], +) diff --git a/examples/example_gem/lib/foo/bar.rb b/examples/example_gem/lib/foo/bar.rb new file mode 100644 index 0000000..6c6db65 --- /dev/null +++ b/examples/example_gem/lib/foo/bar.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Foo + class Bar + def call + puts 'Hello World' + end + end +end diff --git a/ruby/defs.bzl b/ruby/defs.bzl index 6fd9f84..f4716e2 100644 --- a/ruby/defs.bzl +++ b/ruby/defs.bzl @@ -24,8 +24,17 @@ load( "@bazelruby_ruby_rules//ruby/private/rubocop:def.bzl", _rubocop = "rubocop", ) +load( + "@bazelruby_ruby_rules//ruby/private:gemspec.bzl", + _gemspec = "rb_gemspec", +) +load( + "@bazelruby_ruby_rules//ruby/private:gem.bzl", + _gem = "rb_gem", +) ruby_toolchain = _toolchain + ruby_library = _library ruby_binary = _binary ruby_test = _test @@ -33,6 +42,8 @@ ruby_rspec_test = _ruby_rspec_test ruby_rspec = _ruby_rspec ruby_bundle = _ruby_bundle ruby_rubocop = _rubocop +ruby_gemspec = _gemspec +ruby_gem = _gem rb_toolchain = _toolchain rb_library = _library @@ -41,3 +52,5 @@ rb_test = _test rb_rspec = _ruby_rspec rb_bundle = _ruby_bundle rb_rubocop = _rubocop +rb_gemspec = _gemspec +rb_gem = _gem diff --git a/ruby/private/BUILD.bazel b/ruby/private/BUILD.bazel index 4353062..cb0b35a 100644 --- a/ruby/private/BUILD.bazel +++ b/ruby/private/BUILD.bazel @@ -1,4 +1,7 @@ exports_files( - ["binary_wrapper.tpl"], + [ + "binary_wrapper.tpl", + "gemspec_template.tpl", + ], visibility = ["//visibility:public"], ) diff --git a/ruby/private/dependencies.bzl b/ruby/private/dependencies.bzl index 397a6eb..150754b 100644 --- a/ruby/private/dependencies.bzl +++ b/ruby/private/dependencies.bzl @@ -1,4 +1,5 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") def ruby_rules_dependencies(): if "bazel_skylib" not in native.existing_rules(): @@ -10,3 +11,13 @@ def ruby_rules_dependencies(): ], sha256 = "97e70364e9249702246c0e9444bccdc4b847bed1eb03c5a3ece4f83dfe6abc44", ) + + if "rules_pkg" not in native.existing_rules(): + # Use Grahams improved rules_zip version until google merges it into mainline. + # https://github.com/bazelbuild/rules_pkg/pull/127 + http_archive( + name = "rules_pkg", + url = "https://github.com/grahamjenson/rules_pkg/archive/3e0cd514ad1cdd2d23ab3d427d34436f75060018.zip", + sha256 = "85e26971904cbb387688bd2a9e87c105f7cd7d986dc1b96bb1391924479c5ef6", + strip_prefix = "rules_pkg-3e0cd514ad1cdd2d23ab3d427d34436f75060018/pkg", + ) diff --git a/ruby/private/gem.bzl b/ruby/private/gem.bzl new file mode 100644 index 0000000..9108192 --- /dev/null +++ b/ruby/private/gem.bzl @@ -0,0 +1,31 @@ +load( + ":gemspec.bzl", + _rb_gemspec = "rb_gemspec", +) +load( + "@rules_pkg//:pkg.bzl", + "pkg_zip", +) + +def rb_gem(name, version, gem_name, srcs = [], **kwargs): + _zip_name = "%s-%s" % (gem_name, version) + _gemspec_name = name + "_gemspec" + + _rb_gemspec( + name = _gemspec_name, + gem_name = gem_name, + version = version, + **kwargs + ) + + pkg_zip( + name = _zip_name, + srcs = srcs + [":" + _gemspec_name], + strip_prefix = "./", + ) + + native.alias( + name = name, + actual = ":" + _zip_name, + visibility = ["//visibility:public"], + ) diff --git a/ruby/private/gemspec.bzl b/ruby/private/gemspec.bzl new file mode 100644 index 0000000..ee7bdac --- /dev/null +++ b/ruby/private/gemspec.bzl @@ -0,0 +1,87 @@ +load( + "//ruby/private/tools:deps.bzl", + _transitive_deps = "transitive_deps", +) +load( + "//ruby/private:providers.bzl", + "RubyGem", + "RubyLibrary", +) + +def _get_transitive_srcs(srcs, deps): + for dep in deps: + print(dep[RubyLibrary].transitive_ruby_srcs) + + return depset( + srcs, + transitive = [dep[RubyLibrary].transitive_ruby_srcs for dep in deps], + ) + +def _unique_elems(list): + _out = [] + _prev = None + for elem in sorted(list): + if _prev != elem: + _out.append(elem) + + return _out + +def _rb_gem_impl(ctx): + gemspec = ctx.actions.declare_file("%s.gemspec" % ctx.attr.gem_name) + + _ruby_files = [] + _require_paths = [] + for file in _get_transitive_srcs([], ctx.attr.deps).to_list(): + _ruby_files.append(file.short_path) + _require_paths.append(file.dirname) + + _require_paths = _unique_elems(_require_paths) # Set is not supported in Starlark + + ctx.actions.expand_template( + template = ctx.file._gemspec_template, + output = gemspec, + substitutions = { + "{name}": "\"%s\"" % ctx.label.name, + "{srcs}": repr(_ruby_files), + "{authors}": repr(ctx.attr.authors), + "{version}": ctx.attr.version, + "{require_paths}": repr(_require_paths), + }, + ) + + return [ + DefaultInfo(files = _get_transitive_srcs([gemspec], ctx.attr.deps)), + RubyGem( + ctx = ctx, + version = ctx.attr.version, + ), + ] + +_ATTRS = { + "version": attr.string( + default = "0.0.1", + ), + "authors": attr.string_list(), + "deps": attr.label_list( + allow_files = True, + ), + "data": attr.label_list( + allow_files = True, + ), + "_gemspec_template": attr.label( + allow_single_file = True, + default = "gemspec_template.tpl", + ), + "gem_name": attr.string(), + "srcs": attr.label_list( + allow_files = True, + default = [], + ), + "require_paths": attr.string_list(), +} + +rb_gemspec = rule( + implementation = _rb_gem_impl, + attrs = _ATTRS, + provides = [DefaultInfo, RubyGem], +) diff --git a/ruby/private/gemspec_template.tpl b/ruby/private/gemspec_template.tpl new file mode 100644 index 0000000..1c65d0b --- /dev/null +++ b/ruby/private/gemspec_template.tpl @@ -0,0 +1,10 @@ +Gem::Specification.new do |s| + s.name = {name} + s.summary = {name} + s.authors = {authors} + s.version = {version} + s.files = {srcs} + s.require_paths = {require_paths} + + s.add_dependency('csf') +end \ No newline at end of file diff --git a/ruby/private/providers.bzl b/ruby/private/providers.bzl index ee032cd..10f466c 100644 --- a/ruby/private/providers.bzl +++ b/ruby/private/providers.bzl @@ -14,3 +14,11 @@ RubyRuntimeContext = provider( "environment", ], ) + +RubyGem = provider( + doc = "Carries info required to package a ruby gem", + fields = [ + "ctx", + "version", + ], +) From ad2850b7d6f15606229612c962d6b05816f5f4d6 Mon Sep 17 00:00:00 2001 From: ClareCat Date: Tue, 11 Feb 2020 11:42:48 -0800 Subject: [PATCH 12/19] Add force_gem_pristine attr to rb_binary (#11) * Add gem pristine functionality to unblock gems * Run buildifier, move ruby attrs --- ruby/private/binary.bzl | 4 ++++ ruby/private/binary_wrapper.tpl | 19 +++++++++++++++++++ .../bundle/create_bundle_build_file.rb | 7 +++++++ ruby/private/constants.bzl | 3 +++ 4 files changed, 33 insertions(+) diff --git a/ruby/private/binary.bzl b/ruby/private/binary.bzl index a5d0f70..4a38054 100644 --- a/ruby/private/binary.bzl +++ b/ruby/private/binary.bzl @@ -54,6 +54,8 @@ def ruby_binary_macro(ctx, main, srcs): gem_path = _get_gem_path(deps.incpaths.to_list()) + gems_to_pristine = ctx.attr.force_gem_pristine + rubyopt = reversed(deps.rubyopt.to_list()) ctx.actions.expand_template( @@ -65,6 +67,8 @@ def ruby_binary_macro(ctx, main, srcs): "{main}": repr(_to_manifest_path(ctx, main)), "{interpreter}": _to_manifest_path(ctx, interpreter), "{gem_path}": gem_path, + "{should_gem_pristine}": str(len(gems_to_pristine) > 0).lower(), + "{gems_to_pristine}": " ".join(gems_to_pristine), }, ) diff --git a/ruby/private/binary_wrapper.tpl b/ruby/private/binary_wrapper.tpl index e7a008f..159f509 100644 --- a/ruby/private/binary_wrapper.tpl +++ b/ruby/private/binary_wrapper.tpl @@ -91,6 +91,13 @@ def find_ruby_binary ) end +def find_gem_binary + File.join( + RbConfig::CONFIG['bindir'], + 'gem', + ) +end + def main(args) custom_loadpaths = {loadpaths} runfiles = find_runfiles @@ -104,6 +111,7 @@ def main(args) ENV[runfiles_envkey] = runfiles_envvalue if runfiles_envkey ENV["GEM_PATH"] = File.join(runfiles, "{gem_path}") if "{gem_path}" + ENV["GEM_HOME"] = File.join(runfiles, "{gem_path}") if "{gem_path}" ruby_program = find_ruby_binary @@ -119,6 +127,17 @@ def main(args) end end end + + # This is a jank hack because some of our gems are having issues with how + # they are being installed. Most gems are fine, but this fixes the ones that + # aren't. Put it here instead of in the library because we want to fix the + # underlying issue and then tear this out. + if {should_gem_pristine} then + gem_program = find_gem_binary + puts "Running pristine on {gems_to_pristine}" + system(gem_program + " pristine {gems_to_pristine}") + end + exec(ruby_program, *rubyopt, main, *args) # TODO(yugui) Support windows end diff --git a/ruby/private/bundle/create_bundle_build_file.rb b/ruby/private/bundle/create_bundle_build_file.rb index 8ed8298..a7012b9 100755 --- a/ruby/private/bundle/create_bundle_build_file.rb +++ b/ruby/private/bundle/create_bundle_build_file.rb @@ -33,10 +33,17 @@ name = "{name}", srcs = glob( include = [ +<<<<<<< HEAD ".bundle/config", "{gem_lib_files}", "lib/ruby/{ruby_version}/specifications/{name}-{version}.gemspec", {gem_binaries} +======= + "lib/ruby/{ruby_version}/gems/{name}-{version}*/**", + "lib/ruby/{ruby_version}/specifications/{name}-{version}*.gemspec", + "lib/ruby/{ruby_version}/cache/{name}-{version}*.gem", + "bin/*" +>>>>>>> eeab881... Add force_gem_pristine attr to rb_binary (#11) ], exclude = {exclude}, ), diff --git a/ruby/private/constants.bzl b/ruby/private/constants.bzl index 3505735..d3d43e4 100644 --- a/ruby/private/constants.bzl +++ b/ruby/private/constants.bzl @@ -30,6 +30,9 @@ RUBY_ATTRS = { "main": attr.label( allow_single_file = True, ), + "force_gem_pristine": attr.string_list( + doc = "Jank hack. Run gem pristine on some gems that don't handle symlinks well", + ), "_wrapper_template": attr.label( allow_single_file = True, default = "binary_wrapper.tpl", From cc2415f5ebf359d258df3346d8239dd2f7a74e15 Mon Sep 17 00:00:00 2001 From: Konstantin Gredeskoul Date: Fri, 28 Feb 2020 17:28:39 -0800 Subject: [PATCH 13/19] Fixing merge conflict + some additional fixes (see below) Also: * removing `max_idle_sec` to keep Bazel running persistently * adding `example_gem` to CircleCI and Travis CI * removing `bundler/setup.rb` rubyopt, because it's responsible for loading the entire bundle when we want individual gems. --- .circleci/.bazelrc | 2 +- .circleci/config.yml | 24 ++++++++++++++++--- .travis.yml | 4 ++-- bin/linter | 2 +- bin/test-suite | 9 +++---- .../bundle/create_bundle_build_file.rb | 10 -------- 6 files changed, 30 insertions(+), 21 deletions(-) diff --git a/.circleci/.bazelrc b/.circleci/.bazelrc index 9e3e7ed..3d8914d 100644 --- a/.circleci/.bazelrc +++ b/.circleci/.bazelrc @@ -1,6 +1,5 @@ # Common options common --color=yes -common --verbose_failures common --show_progress common --show_progress_rate_limit=0.5 @@ -15,3 +14,4 @@ build --jobs=10 test --spawn_strategy=standalone test --test_output=all test --test_verbose_timeout_warnings +test --verbose_failures diff --git a/.circleci/config.yml b/.circleci/config.yml index 675f65b..b9e9884 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,7 +9,7 @@ jobs: environment: PATH: "/usr/local/bin:/usr/bin:/sbin:/opt/bin:/home/circleci/repo/bin:/bin:/sbin:/usr/sbin" BUNDLE_PATH: /home/circleci/.bundle_cache - BAZEL_OPTS: "--max_idle_secs=1 --host_jvm_args=-Xmx500m --host_jvm_args=-Xms500m" + BAZEL_OPTS: "--host_jvm_args=-Xmx500m --host_jvm_args=-Xms500m" BAZEL_BUILD_OPTS: "--curses=no --verbose_failures --jobs 10" BAZEL_TEST_OPTS: "--verbose_failures --test_output=streamed --test_verbose_timeout_warnings " @@ -48,7 +48,7 @@ jobs: command: | /usr/bin/env bash bin/test-suite workspace - bazel_build_examples: + bazel_example_script: <<: *bazel_defaults steps: @@ -65,6 +65,23 @@ jobs: command: | /usr/bin/env bash bin/test-suite simple-script + bazel_example_gem: + <<: *bazel_defaults + + steps: + - checkout + + - run: + name: "Install ~/.bazelrc and run setup" + command: | + cp .circleci/.bazelrc ${HOME} + /usr/bin/env bash bin/setup + + - run: + name: "Bazel Build & Test Example" + command: | + /usr/bin/env bash bin/test-suite example-gem + buildifier: <<: *bazel_defaults @@ -91,5 +108,6 @@ workflows: rules_ruby: jobs: - bazel_build_workspace - - bazel_build_examples + - bazel_example_gem + - bazel_example_script - buildifier diff --git a/.travis.yml b/.travis.yml index d212be2..3363cb2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,6 @@ env: - CI=true - BUNDLE_PATH="${HOME}/.bundle/gems" - PATH="${HOME}/.rbenv/bin:${HOME}/.rbenv/shims:/usr/local/bin:/usr/bin:/bin:/sbin:/usr/sbin:/opt/local/bin:${PATH}" - - BAZEL_OPTS="--max_idle_secs=1 --host_jvm_args=-Xmx1200m --host_jvm_args=-Xms1200m" - - BAZEL_BUILD_OPTS="--curses=no --verbose_failures -j 30 --show_progress_rate_limit 0.5" + - BAZEL_OPTS="--host_jvm_args=-Xmx1200m --host_jvm_args=-Xms1200m" + - BAZEL_BUILD_OPTS="--curses=no --verbose_failures -j 10 --show_progress_rate_limit 0.5" - BAZEL_TEST_OPTS="--verbose_failures --test_output=streamed --test_verbose_timeout_warnings" diff --git a/bin/linter b/bin/linter index d44ae69..fa7ddfa 100755 --- a/bin/linter +++ b/bin/linter @@ -104,7 +104,7 @@ lint::all() { hr echo info "ACTION: ${bldylw}Please add any respective files to the commit and retry." - retrn 1 + return 1 else echo success "No changes detected, changes passed linter inspection." diff --git a/bin/test-suite b/bin/test-suite index 54a6227..108b884 100755 --- a/bin/test-suite +++ b/bin/test-suite @@ -1,5 +1,5 @@ #!/usr/bin/env bash - +# vim: ft=bash set -e # shellcheck disable=SC1091 @@ -10,8 +10,8 @@ export BUNDLE_PATH="${BUNDLE_PATH:-${HOME}/.bundle/gems}" export PATH="${HOME}/.rbenv/bin:${HOME}/.rbenv/shims:/usr/local/bin:/usr/bin:/bin:/sbin:/usr/sbin:/opt/local/bin" export BAZEL_OPTS="--host_jvm_args=-Xmx500m --host_jvm_args=-Xms500m" -export BAZEL_BUILD_OPTS="--curses=no --verbose_failures -j 15 --show_progress_rate_limit 0 " -export BAZEL_TEST_OPTS="--verbose_failures --test_output=streamed --test_verbose_timeout_warnings " +export BAZEL_BUILD_OPTS="--curses=no --verbose_failures -j 30 --progress_report_interval=2" +export BAZEL_TEST_OPTS="--verbose_failures --test_verbose_timeout_warnings --verbose_explanations" export RUBY_VERSION=2.7.0 export BashMatic__Expr=" @@ -187,9 +187,10 @@ test::all() { test::bazel-info test::workspace test::simple-script - test::buildifier + test::example-gem test::rubocop test::rspec + test::buildifier } test-suite::setup() { diff --git a/ruby/private/bundle/create_bundle_build_file.rb b/ruby/private/bundle/create_bundle_build_file.rb index a7012b9..b283b40 100755 --- a/ruby/private/bundle/create_bundle_build_file.rb +++ b/ruby/private/bundle/create_bundle_build_file.rb @@ -22,7 +22,6 @@ "bundler/**/*", ], ), - rubyopt = ["{bundler_setup}"], ) # PULL EACH GEM INDIVIDUALLY @@ -33,21 +32,13 @@ name = "{name}", srcs = glob( include = [ -<<<<<<< HEAD ".bundle/config", "{gem_lib_files}", "lib/ruby/{ruby_version}/specifications/{name}-{version}.gemspec", {gem_binaries} -======= - "lib/ruby/{ruby_version}/gems/{name}-{version}*/**", - "lib/ruby/{ruby_version}/specifications/{name}-{version}*.gemspec", - "lib/ruby/{ruby_version}/cache/{name}-{version}*.gem", - "bin/*" ->>>>>>> eeab881... Add force_gem_pristine attr to rb_binary (#11) ], exclude = {exclude}, ), - rubyopt = ["{bundler_setup}"], deps = {deps}, includes = ["lib/ruby/{ruby_version}/gems/{name}-{version}/lib"], ) @@ -58,7 +49,6 @@ name = "gems", srcs = glob([{bundle_lib_files}]) + glob(["bin/*"]), includes = {bundle_lib_paths}, - rubyopt = ["{bundler_setup}"], ) ruby_library( From 5a81b112c04d0c3ed3ae813aba519d1427eae104 Mon Sep 17 00:00:00 2001 From: Konstantin Gredeskoul Date: Sat, 29 Feb 2020 01:01:23 -0800 Subject: [PATCH 14/19] Adding method Dir.children when it's not found. This is a workaround for this issue: https://github.com/bazelruby/rules_ruby/issues/63 A proper fix would be to set PATH so that system ruby is never used. --- ruby/private/binary_wrapper.tpl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ruby/private/binary_wrapper.tpl b/ruby/private/binary_wrapper.tpl index 159f509..6adb2a0 100644 --- a/ruby/private/binary_wrapper.tpl +++ b/ruby/private/binary_wrapper.tpl @@ -20,6 +20,14 @@ require 'rbconfig' +# Ruby 2.4 and older does not have +.children+ +# So we define it. +unless Dir.respond_to?(:children) + Dir.define_method :children do |dir| + Dir.entries(dir).reject { |entry| %w(. ..).include?(entry) } + end +end + def find_runfiles stub_filename = File.absolute_path($0) runfiles = "#{stub_filename}.runfiles" From 631fa4bc9b433c9044308364348ea15da99a94c4 Mon Sep 17 00:00:00 2001 From: Konstantin Gredeskoul Date: Sat, 29 Feb 2020 02:34:46 -0800 Subject: [PATCH 15/19] Updating README --- README.md | 192 ++++++++++++++++++++++++++++------ docs/img/status-not-ready.svg | 1 + docs/img/status-ready.svg | 1 + docs/img/status-wait.svg | 1 + 4 files changed, 165 insertions(+), 30 deletions(-) create mode 100644 docs/img/status-not-ready.svg create mode 100644 docs/img/status-ready.svg create mode 100644 docs/img/status-wait.svg diff --git a/README.md b/README.md index 5827ad2..ba98a77 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,20 @@ +* [Rules Development Status](#rules-development-status) * [Usage](#usage) - * [WORKSPACE File](#workspace-file) - * [BUILD.bazel files](#buildbazel-files) + * [`WORKSPACE` File](#workspace-file) + * [`BUILD.bazel` files](#buildbazel-files) * [Rules](#rules) - * [ruby_library](#ruby_library) - * [ruby_binary](#ruby_binary) - * [ruby_test](#ruby_test) - * [ruby_bundle](#ruby_bundle) - * [ruby_gem](#rb_gem) + * [`ruby_library`](#ruby_library) + * [`ruby_binary`](#ruby_binary) + * [`ruby_test`](#ruby_test) + * [`ruby_bundle`](#ruby_bundle) + * [Limitations](#limitations) + * [Conventions](#conventions) + * [Example: `WORKSPACE`:](#example-workspace) + * [Example: `lib/BUILD.bazel`:](#example-libbuildbazel) + * [`ruby_rspec`](#ruby_rspec) + * [`ruby_gem`](#ruby_gem) * [What's coming next](#whats-coming-next) * [Contributing](#contributing) * [Setup](#setup) @@ -27,17 +33,22 @@ | Build | Status | |---------: |--------------------------------------------------------------------------------------------------------------------------------------------------- | -| CircleCI Develop: | [![CircleCI](https://circleci.com/gh/bazelruby/rules_ruby/tree/develop.svg?style=svg)](https://circleci.com/gh/bazelruby/rules_ruby/tree/develop) | -| CircleCI Default: | [![CircleCI](https://circleci.com/gh/bazelruby/rules_ruby.svg?style=svg)](https://circleci.com/gh/bazelruby/rules_ruby) | -| Develop: | [![Build Status](https://travis-ci.org/bazelruby/rules_ruby.svg?branch=develop)](https://travis-ci.org/bazelruby/rules_ruby) | -| Master: | [![Build Status](https://travis-ci.org/bazelruby/rules_ruby.svg?branch=master)](https://travis-ci.org/bazelruby/rules_ruby) | +| CircleCI | [![CircleCI](https://circleci.com/gh/bazelruby/rules_ruby/tree/develop.svg?style=svg)](https://circleci.com/gh/bazelruby/rules_ruby/tree/develop) | +| TravisCI | [![Build Status](https://travis-ci.org/bazelruby/rules_ruby.svg?branch=develop)](https://travis-ci.org/bazelruby/rules_ruby) | # Rules Ruby -Ruby rules for [Bazel](https://bazel.build). +This is the README for Ruby Rules for the [Bazel Build](https://bazel.build) system. + +## Rules Development Status + +| **Readiness** | **Types of Applications** | +|:-------------------------------------------------------------------------------| :----------| +| ![Ready](docs/img/status-ready.svg) | ruby apps, ruby gems, micro-services, ideally in a mono-repo | +| ![Wait](docs/img/status-wait.svg) | medium-sized Ruby on Rails apps, ideally in a mono-repo | +| ![Not Ready](docs/img/status-not-ready.svg) | complex Ruby on Rails monoliths, single-repo | -** Current Status:** *Work in progress.* Note: we have a short guide on [Building your first Ruby Project](https://github.com/bazelruby/rules_ruby/wiki/Build-your-ruby-project) on the Wiki. We encourage you to check it out. @@ -45,26 +56,26 @@ Note: we have a short guide on [Building your first Ruby Project](https://github ### `WORKSPACE` File -Add `ruby_rules_dependencies` and `ruby_register_toolchains` into your `WORKSPACE` file. +Add `ruby_rules_dependencies` and `ruby_rules_toolchains` into your `WORKSPACE` file. ```python # To get the latest, grab the 'develop' branch. git_repository( - name = "bazelruby_ruby_rules", + name = "bazelruby_rules_ruby", remote = "https://github.com/bazelruby/rules_ruby.git", branch = "develop", ) load( - "@bazelruby_ruby_rules//ruby:deps.bzl", - "ruby_register_toolchains", + "@bazelruby_rules_ruby//ruby:deps.bzl", + "ruby_rules_toolchains", "ruby_rules_dependencies", ) ruby_rules_dependencies() -ruby_register_toolchains() +ruby_rules_toolchains() ``` Next, add any external Gem dependencies you may have via `ruby_bundle` command. @@ -98,7 +109,7 @@ Add `ruby_library`, `ruby_binary` or `ruby_test` into your `BUILD.bazel` files. ```python load( - "@bazelruby_ruby_rules//ruby:defs.bzl", + "@bazelruby_rules_ruby//ruby:defs.bzl", "ruby_binary", "ruby_library", "ruby_test", @@ -139,10 +150,13 @@ ruby_rspec( ## Rules +> NOTE: this diagram is slightly outdated. + The following diagram attempts to capture the implementation behind `ruby_library` that depends on the result of `bundle install`, and a `ruby_binary` that depends on both: ![Ruby Rules](docs/img/ruby_rules.png) +---- ### `ruby_library` @@ -219,6 +233,8 @@ ruby_library(name, deps, srcs, data, compatible_with, deprecation, distribs, fea +---- + ### `ruby_binary`
@@ -300,6 +316,8 @@ ruby_binary(name, deps, srcs, data, main, compatible_with, deprecation, distribs
   
 
 
+----
+
 ### `ruby_test`
 
 
@@ -381,39 +399,57 @@ ruby_test(name, deps, srcs, data, main, compatible_with, deprecation, distribs,
   
 
 
+----
+
 ### `ruby_bundle`
 
-Installs gems with Bundler, and make them available as a `ruby_library`.
+**NOTE: This is a repository rule, and can only be used in a `WORKSPACE` file.**
+
+This rule installs gems defined in a Gemfile using Bundler, and exports individual gems from the bundle, as well as the entire bundle, available as a `ruby_library` that can be depended upon from other targets.
 
-Example: `WORKSPACE`:
+#### Limitations
+
+Installing using a `Gemfile` that uses the `gemspec` keyword is not currently supported.
+
+#### Conventions
+
+`ruby_bundle` creates several targets that can be used downstream. In the examples below we assume that your `ruby_bundle` has a name `app_bundle`:
+
+ * `@app_bundle//:bundler` — references just the Bundler from the bundle.
+ * `@app_bundle//:gems` — references *all* gems in the bundle (i.e. "the entire bundle").
+ * `@app_bundle//:gem-name` — references *just the specified* gem in the bundle, eg. `@app_bundle//:awesome_print`.
+ * `@app_bundle//:bin` — references to all installed executables from this bundle, with individual executables accessible via eg. `@app_bundle//:bin/rubocop`
+
+##### Example: `WORKSPACE`:
 
 ```python
 git_repository(
-    name = "bazelruby_ruby_rules",
+    name = "bazelruby_rules_ruby",
     remote = "https://github.com/bazelruby/rules_ruby.git",
     tag = "v0.1.0",
 )
 
 load(
-    "@bazelruby_ruby_rules//ruby:deps.bzl",
-    "ruby_register_toolchains",
+    "@bazelruby_rules_ruby//ruby:deps.bzl",
+    "ruby_rules_toolchains",
     "ruby_rules_dependencies",
 )
 
 ruby_rules_dependencies()
 
-ruby_register_toolchains()
+ruby_rules_toolchains()
 
-load("@bazelruby_ruby_rules//ruby:defs.bzl", "ruby_bundle")
+load("@bazelruby_rules_ruby//ruby:defs.bzl", "ruby_bundle")
 
 ruby_bundle(
+    bundler_version = '2.1.2',
     name = "gems",
     gemfile = "//:Gemfile",
     gemfile_lock = "//:Gemfile.lock",
 )
 ```
 
-Example: `lib/BUILD.bazel`:
+##### Example: `lib/BUILD.bazel`:
 
 ```python
 ruby_library(
@@ -484,11 +520,105 @@ ruby_bundle(name, gemfile, gemfile_lock, bundler_version = "2.1.2")
   
 
 
-## rb_gem
+----
+
+### `ruby_rspec`
+
+
+ruby_rspec(name, deps, srcs, data, main, rspec_args, bundle, compatible_with, deprecation, distribs, features, licenses, restricted_to, tags, testonly, toolchains, visibility, args, size, timeout, flaky, local, shard_count)
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Attributes
name + Name, required +

A unique name for this rule.

+
srcs + List of Labels, required +

+ List of .rb files. +

+
deps + List of labels, optional +

+ List of targets that are required by the srcs Ruby + files. +

+
main + Label, optional +

The entrypoint file. It must be also in srcs.

+

If not specified, $(NAME).rb where $(NAME) is the name of this rule.

+
rspec_args + List of strings, optional +

Command line arguments to the rspec binary, eg ["--progress", "-p2", "-b"]

+

If not specified, the default arguments defined in `constants.bzl` are used: --format=documentation --force-color.

+
includes + List of strings, optional +

+ List of paths to be added to $LOAD_PATH at runtime. + The paths must be relative to the the workspace which this rule belongs to. +

+
rubyopt + List of strings, optional +

+ List of options to be passed to the Ruby interpreter at runtime. +

+

+ NOTE: -I option should usually go to includes attribute. +

+
And other common attributes
+ +---- + +### `ruby_gem` + Used to generate a zipped gem containing its srcs, dependencies and a gemspec.
-rb_gem(name, gem_name, version, srcs, authors, deps, data, includes)
+ruby_gem(name, gem_name, gem_version, srcs, authors, deps, data, includes)
 
@@ -517,7 +647,7 @@ rb_gem(name, gem_name, version, srcs, authors, deps, data, includes) - +
versiongem_version Label, required

@@ -696,3 +826,5 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + + diff --git a/docs/img/status-not-ready.svg b/docs/img/status-not-ready.svg new file mode 100644 index 0000000..3a86de9 --- /dev/null +++ b/docs/img/status-not-ready.svg @@ -0,0 +1 @@ + Development StatusDevelopment StatusNot ReadyNot Ready \ No newline at end of file diff --git a/docs/img/status-ready.svg b/docs/img/status-ready.svg new file mode 100644 index 0000000..fcb75e3 --- /dev/null +++ b/docs/img/status-ready.svg @@ -0,0 +1 @@ + Development StatusDevelopment StatusReadyReady \ No newline at end of file diff --git a/docs/img/status-wait.svg b/docs/img/status-wait.svg new file mode 100644 index 0000000..5bd705b --- /dev/null +++ b/docs/img/status-wait.svg @@ -0,0 +1 @@ + Development StatusDevelopment StatusWaitWait \ No newline at end of file From 20a67ec256904d5ef02137b68638f7d1fc99a2b6 Mon Sep 17 00:00:00 2001 From: Konstantin Gredeskoul Date: Sat, 29 Feb 2020 18:09:56 -0800 Subject: [PATCH 16/19] Renaming the repo to @bazelruby_rules_ruby * Also renaming all ruby_rules_ to rules_ruby_ --- README.md | 40 +++++++++---------- WORKSPACE | 10 ++--- examples/example_gem/BUILD | 6 +-- examples/example_gem/WORKSPACE | 10 ++--- examples/example_gem/lib/BUILD | 6 +-- examples/example_gem/lib/foo/BUILD | 6 +-- examples/simple_rails_api/BUILD | 7 ++-- examples/simple_rails_api/WORKSPACE | 12 +++--- examples/simple_script/BUILD.bazel | 2 +- examples/simple_script/WORKSPACE | 12 +++--- examples/simple_script/lib/BUILD | 6 +-- ruby/defs.bzl | 16 ++++---- ruby/deps.bzl | 8 ++-- ruby/private/constants.bzl | 2 +- ruby/private/dependencies.bzl | 3 +- ruby/private/rubocop/def.bzl | 4 +- ruby/private/sdk.bzl | 2 +- ruby/tests/BUILD.bazel | 10 ++--- ruby/tests/load_path_in_runfiles_test.rb | 2 +- .../testdata/another_workspace/WORKSPACE | 4 +- .../another_workspace/baz/qux/BUILD.bazel | 4 +- 21 files changed, 85 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index ba98a77..1afc991 100644 --- a/README.md +++ b/README.md @@ -43,11 +43,11 @@ This is the README for Ruby Rules for the [Bazel Build](https://bazel.build) sys ## Rules Development Status -| **Readiness** | **Types of Applications** | -|:-------------------------------------------------------------------------------| :----------| -| ![Ready](docs/img/status-ready.svg) | ruby apps, ruby gems, micro-services, ideally in a mono-repo | -| ![Wait](docs/img/status-wait.svg) | medium-sized Ruby on Rails apps, ideally in a mono-repo | -| ![Not Ready](docs/img/status-not-ready.svg) | complex Ruby on Rails monoliths, single-repo | +| **Readiness** | **Types of Applications** | +| :------------------------------------------ | :----------------------------------------------------------- | +| ![Ready](docs/img/status-ready.svg) | ruby apps, ruby gems, micro-services, ideally in a mono-repo | +| ![Wait](docs/img/status-wait.svg) | medium-sized Ruby on Rails apps, ideally in a mono-repo | +| ![Not Ready](docs/img/status-not-ready.svg) | complex Ruby on Rails monoliths, single-repo | Note: we have a short guide on [Building your first Ruby Project](https://github.com/bazelruby/rules_ruby/wiki/Build-your-ruby-project) on the Wiki. We encourage you to check it out. @@ -56,26 +56,26 @@ Note: we have a short guide on [Building your first Ruby Project](https://github ### `WORKSPACE` File -Add `ruby_rules_dependencies` and `ruby_rules_toolchains` into your `WORKSPACE` file. +Add `rules_ruby_dependencies` and `rules_ruby_toolchains` into your `WORKSPACE` file. ```python # To get the latest, grab the 'develop' branch. git_repository( - name = "bazelruby_rules_ruby", + name = "bazelrules_ruby_ruby", remote = "https://github.com/bazelruby/rules_ruby.git", branch = "develop", ) load( - "@bazelruby_rules_ruby//ruby:deps.bzl", - "ruby_rules_toolchains", - "ruby_rules_dependencies", + "@bazelrules_ruby_ruby//ruby:deps.bzl", + "rules_ruby_toolchains", + "rules_ruby_dependencies", ) -ruby_rules_dependencies() +rules_ruby_dependencies() -ruby_rules_toolchains() +rules_ruby_toolchains() ``` Next, add any external Gem dependencies you may have via `ruby_bundle` command. @@ -109,7 +109,7 @@ Add `ruby_library`, `ruby_binary` or `ruby_test` into your `BUILD.bazel` files. ```python load( - "@bazelruby_rules_ruby//ruby:defs.bzl", + "@bazelrules_ruby_ruby//ruby:defs.bzl", "ruby_binary", "ruby_library", "ruby_test", @@ -424,22 +424,22 @@ Installing using a `Gemfile` that uses the `gemspec` keyword is not currently su ```python git_repository( - name = "bazelruby_rules_ruby", + name = "bazelrules_ruby_ruby", remote = "https://github.com/bazelruby/rules_ruby.git", tag = "v0.1.0", ) load( - "@bazelruby_rules_ruby//ruby:deps.bzl", - "ruby_rules_toolchains", - "ruby_rules_dependencies", + "@bazelrules_ruby_ruby//ruby:deps.bzl", + "rules_ruby_toolchains", + "rules_ruby_dependencies", ) -ruby_rules_dependencies() +rules_ruby_dependencies() -ruby_rules_toolchains() +rules_ruby_toolchains() -load("@bazelruby_rules_ruby//ruby:defs.bzl", "ruby_bundle") +load("@bazelrules_ruby_ruby//ruby:defs.bzl", "ruby_bundle") ruby_bundle( bundler_version = '2.1.2', diff --git a/WORKSPACE b/WORKSPACE index d31dee8..9e255a2 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,8 +1,8 @@ -workspace(name = "bazelruby_ruby_rules") +workspace(name = "bazelrules_ruby_ruby") -load("@//ruby:deps.bzl", "ruby_register_toolchains", "ruby_rules_dependencies") +load("@//ruby:deps.bzl", "ruby_register_toolchains", "rules_ruby_dependencies") -ruby_rules_dependencies() +rules_ruby_dependencies() load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") @@ -15,7 +15,7 @@ versions.check("1.2.1") ruby_register_toolchains() local_repository( - name = "bazelruby_ruby_rules_ruby_tests_testdata_another_workspace", + name = "bazelrules_ruby_ruby_ruby_tests_testdata_another_workspace", path = "ruby/tests/testdata/another_workspace", ) @@ -91,7 +91,7 @@ container_pull( repository = "library/ruby", ) -load("@bazelruby_ruby_rules//ruby:defs.bzl", "ruby_bundle") +load("@bazelrules_ruby_ruby//ruby:defs.bzl", "ruby_bundle") ruby_bundle( name = "bundle", diff --git a/examples/example_gem/BUILD b/examples/example_gem/BUILD index 8fb980b..6ff3eb0 100644 --- a/examples/example_gem/BUILD +++ b/examples/example_gem/BUILD @@ -1,10 +1,10 @@ -package(default_visibility = ["//:__subpackages__"]) - load( - "@bazelruby_ruby_rules//ruby:defs.bzl", + "@bazelrules_ruby_ruby//ruby:defs.bzl", "rb_gem", ) +package(default_visibility = ["//:__subpackages__"]) + rb_gem( name = "default_gem", authors = ["Coinbase"], diff --git a/examples/example_gem/WORKSPACE b/examples/example_gem/WORKSPACE index 3b377a5..0933a25 100644 --- a/examples/example_gem/WORKSPACE +++ b/examples/example_gem/WORKSPACE @@ -1,19 +1,19 @@ -workspace(name = "bazelruby_ruby_rules_example_gem") +workspace(name = "bazelrules_ruby_ruby_example_gem") # Importing rules_ruby from the parent directory for developing # rules_ruby itself... local_repository( - name = "bazelruby_ruby_rules", + name = "bazelrules_ruby_ruby", path = "../..", ) load( - "@bazelruby_ruby_rules//ruby:deps.bzl", + "@bazelrules_ruby_ruby//ruby:deps.bzl", "ruby_register_toolchains", - "ruby_rules_dependencies", + "rules_ruby_dependencies", ) -ruby_rules_dependencies() +rules_ruby_dependencies() ruby_register_toolchains() diff --git a/examples/example_gem/lib/BUILD b/examples/example_gem/lib/BUILD index e98ee71..cff539f 100644 --- a/examples/example_gem/lib/BUILD +++ b/examples/example_gem/lib/BUILD @@ -1,10 +1,10 @@ -package(default_visibility = ["//:__subpackages__"]) - load( - "@bazelruby_ruby_rules//ruby:defs.bzl", + "@bazelrules_ruby_ruby//ruby:defs.bzl", "rb_library", ) +package(default_visibility = ["//:__subpackages__"]) + rb_library( name = "example_gem", srcs = ["example_gem.rb"], diff --git a/examples/example_gem/lib/foo/BUILD b/examples/example_gem/lib/foo/BUILD index d08a459..d28e3e7 100644 --- a/examples/example_gem/lib/foo/BUILD +++ b/examples/example_gem/lib/foo/BUILD @@ -1,10 +1,10 @@ -package(default_visibility = ["//:__subpackages__"]) - load( - "@bazelruby_ruby_rules//ruby:defs.bzl", + "@bazelrules_ruby_ruby//ruby:defs.bzl", "rb_library", ) +package(default_visibility = ["//:__subpackages__"]) + rb_library( name = "default_library", srcs = ["bar.rb"], diff --git a/examples/simple_rails_api/BUILD b/examples/simple_rails_api/BUILD index 686a8a0..82fca5c 100644 --- a/examples/simple_rails_api/BUILD +++ b/examples/simple_rails_api/BUILD @@ -1,11 +1,10 @@ -package(default_visibility = ["//:__subpackages__"]) - load( - "@bazelruby_ruby_rules//ruby:defs.bzl", + "@bazelrules_ruby_ruby//ruby:defs.bzl", "ruby_binary", - "ruby_test", ) +package(default_visibility = ["//:__subpackages__"]) + ruby_binary( name = "server", srcs = glob( diff --git a/examples/simple_rails_api/WORKSPACE b/examples/simple_rails_api/WORKSPACE index daef2e1..84b4840 100644 --- a/examples/simple_rails_api/WORKSPACE +++ b/examples/simple_rails_api/WORKSPACE @@ -1,19 +1,19 @@ -workspace(name = "bazelruby_ruby_rules_example") +workspace(name = "bazelrules_ruby_ruby_example") # Importing rules_ruby from the parent directory for developing # rules_ruby itself... local_repository( - name = "bazelruby_ruby_rules", + name = "bazelrules_ruby_ruby", path = "../..", ) load( - "@bazelruby_ruby_rules//ruby:deps.bzl", + "@bazelrules_ruby_ruby//ruby:deps.bzl", "ruby_register_toolchains", - "ruby_rules_dependencies", + "rules_ruby_dependencies", ) -ruby_rules_dependencies() +rules_ruby_dependencies() ruby_register_toolchains(version = "2.7.0") @@ -21,7 +21,7 @@ load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") bazel_skylib_workspace() -load("@bazelruby_ruby_rules//ruby:defs.bzl", "bundle_install") +load("@bazelrules_ruby_ruby//ruby:defs.bzl", "bundle_install") bundle_install( name = "bundle", diff --git a/examples/simple_script/BUILD.bazel b/examples/simple_script/BUILD.bazel index e9e382e..078de0f 100644 --- a/examples/simple_script/BUILD.bazel +++ b/examples/simple_script/BUILD.bazel @@ -1,5 +1,5 @@ load( - "@bazelruby_ruby_rules//ruby:defs.bzl", + "@bazelrules_ruby_ruby//ruby:defs.bzl", "ruby_binary", "ruby_rspec", "ruby_rubocop", diff --git a/examples/simple_script/WORKSPACE b/examples/simple_script/WORKSPACE index bd722d6..bc4d0b7 100644 --- a/examples/simple_script/WORKSPACE +++ b/examples/simple_script/WORKSPACE @@ -1,23 +1,23 @@ -workspace(name = "bazelruby_ruby_rules_example") +workspace(name = "bazelrules_ruby_ruby_example") # Importing rules_ruby from the parent directory for developing # rules_ruby itself... local_repository( - name = "bazelruby_ruby_rules", + name = "bazelrules_ruby_ruby", path = "../..", ) load( - "@bazelruby_ruby_rules//ruby:deps.bzl", + "@bazelrules_ruby_ruby//ruby:deps.bzl", "ruby_register_toolchains", - "ruby_rules_dependencies", + "rules_ruby_dependencies", ) -ruby_rules_dependencies() +rules_ruby_dependencies() ruby_register_toolchains(version = "2.7.0") -load("@bazelruby_ruby_rules//ruby:defs.bzl", "ruby_bundle") +load("@bazelrules_ruby_ruby//ruby:defs.bzl", "ruby_bundle") ruby_bundle( name = "bundle", diff --git a/examples/simple_script/lib/BUILD b/examples/simple_script/lib/BUILD index 46da775..9721cb8 100644 --- a/examples/simple_script/lib/BUILD +++ b/examples/simple_script/lib/BUILD @@ -1,10 +1,10 @@ -package(default_visibility = ["//:__subpackages__"]) - load( - "@bazelruby_ruby_rules//ruby:defs.bzl", + "@bazelrules_ruby_ruby//ruby:defs.bzl", "ruby_library", ) +package(default_visibility = ["//:__subpackages__"]) + ruby_library( name = "foo", srcs = ["foo.rb"], diff --git a/ruby/defs.bzl b/ruby/defs.bzl index f4716e2..1e386a5 100644 --- a/ruby/defs.bzl +++ b/ruby/defs.bzl @@ -1,35 +1,35 @@ load( - "@bazelruby_ruby_rules//ruby/private:toolchain.bzl", + "@bazelrules_ruby_ruby//ruby/private:toolchain.bzl", _toolchain = "ruby_toolchain", ) load( - "@bazelruby_ruby_rules//ruby/private:library.bzl", + "@bazelrules_ruby_ruby//ruby/private:library.bzl", _library = "ruby_library", ) load( - "@bazelruby_ruby_rules//ruby/private:binary.bzl", + "@bazelrules_ruby_ruby//ruby/private:binary.bzl", _binary = "ruby_binary", _test = "ruby_test", ) load( - "@bazelruby_ruby_rules//ruby/private:bundle.bzl", + "@bazelrules_ruby_ruby//ruby/private:bundle.bzl", _ruby_bundle = "ruby_bundle", ) load( - "@bazelruby_ruby_rules//ruby/private:rspec.bzl", + "@bazelrules_ruby_ruby//ruby/private:rspec.bzl", _ruby_rspec = "ruby_rspec", _ruby_rspec_test = "ruby_rspec_test", ) load( - "@bazelruby_ruby_rules//ruby/private/rubocop:def.bzl", + "@bazelrules_ruby_ruby//ruby/private/rubocop:def.bzl", _rubocop = "rubocop", ) load( - "@bazelruby_ruby_rules//ruby/private:gemspec.bzl", + "@bazelrules_ruby_ruby//ruby/private:gemspec.bzl", _gemspec = "rb_gemspec", ) load( - "@bazelruby_ruby_rules//ruby/private:gem.bzl", + "@bazelrules_ruby_ruby//ruby/private:gem.bzl", _gem = "rb_gem", ) diff --git a/ruby/deps.bzl b/ruby/deps.bzl index a520bfc..e1899f9 100644 --- a/ruby/deps.bzl +++ b/ruby/deps.bzl @@ -1,13 +1,13 @@ # Repository rules load( - "@bazelruby_ruby_rules//ruby/private:dependencies.bzl", - _ruby_rules_dependencies = "ruby_rules_dependencies", + "@bazelrules_ruby_ruby//ruby/private:dependencies.bzl", + _rules_ruby_dependencies = "rules_ruby_dependencies", ) load( - "@bazelruby_ruby_rules//ruby/private:sdk.bzl", + "@bazelrules_ruby_ruby//ruby/private:sdk.bzl", _register_toolchains = "ruby_register_toolchains", ) -ruby_rules_dependencies = _ruby_rules_dependencies +rules_ruby_dependencies = _rules_ruby_dependencies ruby_register_toolchains = _register_toolchains diff --git a/ruby/private/constants.bzl b/ruby/private/constants.bzl index d3d43e4..32ee05c 100644 --- a/ruby/private/constants.bzl +++ b/ruby/private/constants.bzl @@ -1,6 +1,6 @@ load(":providers.bzl", "RubyLibrary") -RULES_RUBY_WORKSPACE_NAME = "@bazelruby_ruby_rules" +RULES_RUBY_WORKSPACE_NAME = "@bazelrules_ruby_ruby" TOOLCHAIN_TYPE_NAME = "%s//ruby:toolchain_type" % RULES_RUBY_WORKSPACE_NAME DEFAULT_BUNDLER_VERSION = "2.1.2" diff --git a/ruby/private/dependencies.bzl b/ruby/private/dependencies.bzl index 150754b..883bcf2 100644 --- a/ruby/private/dependencies.bzl +++ b/ruby/private/dependencies.bzl @@ -1,7 +1,6 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") -def ruby_rules_dependencies(): +def rules_ruby_dependencies(): if "bazel_skylib" not in native.existing_rules(): http_archive( name = "bazel_skylib", diff --git a/ruby/private/rubocop/def.bzl b/ruby/private/rubocop/def.bzl index 71a37f6..5bb5215 100644 --- a/ruby/private/rubocop/def.bzl +++ b/ruby/private/rubocop/def.bzl @@ -1,4 +1,4 @@ -load("@bazelruby_ruby_rules//ruby/private:binary.bzl", "ruby_binary") +load("@bazelrules_ruby_ruby//ruby/private:binary.bzl", "ruby_binary") # This wraps an rb_binary in a script that is executed from the workspace folder def rubocop(name, bin, deps): @@ -9,7 +9,7 @@ def rubocop(name, bin, deps): deps = deps, ) - runner = "@bazelruby_ruby_rules//ruby/private/rubocop:runner.sh.tpl" + runner = "@bazelrules_ruby_ruby//ruby/private/rubocop:runner.sh.tpl" native.genrule( name = name, tools = [bin_name], diff --git a/ruby/private/sdk.bzl b/ruby/private/sdk.bzl index 5fddabd..ba178cd 100644 --- a/ruby/private/sdk.bzl +++ b/ruby/private/sdk.bzl @@ -1,5 +1,5 @@ load( - "@bazelruby_ruby_rules//ruby/private/toolchains:ruby_runtime.bzl", + "@bazelrules_ruby_ruby//ruby/private/toolchains:ruby_runtime.bzl", _ruby_runtime = "ruby_runtime", ) diff --git a/ruby/tests/BUILD.bazel b/ruby/tests/BUILD.bazel index f1fe900..58d7b7e 100644 --- a/ruby/tests/BUILD.bazel +++ b/ruby/tests/BUILD.bazel @@ -120,7 +120,7 @@ ruby_binary( main = "load_path_in_runfiles_test.rb", deps = [ "//ruby/tests/testdata:g", - "@bazelruby_ruby_rules_ruby_tests_testdata_another_workspace//baz/qux:j", + "@bazelrules_ruby_ruby_ruby_tests_testdata_another_workspace//baz/qux:j", ], ) @@ -137,7 +137,7 @@ ruby_test( main = "load_path_in_runfiles_test.rb", deps = [ "//ruby/tests/testdata:g", - "@bazelruby_ruby_rules_ruby_tests_testdata_another_workspace//baz/qux:j", + "@bazelrules_ruby_ruby_ruby_tests_testdata_another_workspace//baz/qux:j", ], ) @@ -250,13 +250,13 @@ pkg_tar( include_runfiles = True, package_dir = "/app", remap_paths = { - "ruby": "load_path_in_runfiles.runfiles/bazelruby_ruby_rules/ruby", + "ruby": "load_path_in_runfiles.runfiles/bazelrules_ruby_ruby/ruby", ".": "load_path_in_runfiles.runfiles/", }, strip_prefix = "dummy", symlinks = { - "/app/load_path_in_runfiles.runfiles/bazelruby_ruby_rules/external": "/app/load_path_in_runfiles.runfiles", - "/app/load_path_in_runfiles": "/app/load_path_in_runfiles.runfiles/bazelruby_ruby_rules/ruby/tests/load_path_in_runfiles", + "/app/load_path_in_runfiles.runfiles/bazelrules_ruby_ruby/external": "/app/load_path_in_runfiles.runfiles", + "/app/load_path_in_runfiles": "/app/load_path_in_runfiles.runfiles/bazelrules_ruby_ruby/ruby/tests/load_path_in_runfiles", }, ) diff --git a/ruby/tests/load_path_in_runfiles_test.rb b/ruby/tests/load_path_in_runfiles_test.rb index cbc0b02..f36ef74 100644 --- a/ruby/tests/load_path_in_runfiles_test.rb +++ b/ruby/tests/load_path_in_runfiles_test.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true require 'ruby/tests/testdata/foo/g' -require 'external/bazelruby_ruby_rules_ruby_tests_testdata_another_workspace/baz/qux/j' +require 'external/bazelrules_ruby_ruby_ruby_tests_testdata_another_workspace/baz/qux/j' [g, j] diff --git a/ruby/tests/testdata/another_workspace/WORKSPACE b/ruby/tests/testdata/another_workspace/WORKSPACE index 25cbfaa..50a1b56 100644 --- a/ruby/tests/testdata/another_workspace/WORKSPACE +++ b/ruby/tests/testdata/another_workspace/WORKSPACE @@ -1,5 +1,5 @@ -workspace(name = "bazelruby_ruby_rules_ruby_tests_testdata_another_workspace") +workspace(name = "bazelrules_ruby_ruby_ruby_tests_testdata_another_workspace") -load("@bazelruby_ruby_rules//ruby:defs.bzl", "ruby_register_toolchains") +load("@bazelrules_ruby_ruby//ruby:defs.bzl", "ruby_register_toolchains") ruby_register_toolchains() diff --git a/ruby/tests/testdata/another_workspace/baz/qux/BUILD.bazel b/ruby/tests/testdata/another_workspace/baz/qux/BUILD.bazel index 88a9927..fa86829 100644 --- a/ruby/tests/testdata/another_workspace/baz/qux/BUILD.bazel +++ b/ruby/tests/testdata/another_workspace/baz/qux/BUILD.bazel @@ -1,6 +1,6 @@ -package(default_visibility = ["//visibility:public"]) +load("@bazelrules_ruby_ruby//ruby:defs.bzl", "ruby_library") -load("@bazelruby_ruby_rules//ruby:defs.bzl", "ruby_library") +package(default_visibility = ["//visibility:public"]) ruby_library( name = "j", From ee1ffdfbf89fedcdd5b812a8e87146351d9f2472 Mon Sep 17 00:00:00 2001 From: Konstantin Gredeskoul Date: Sat, 29 Feb 2020 20:33:52 -0800 Subject: [PATCH 17/19] Fixing project name + enhancement to gemspec/gem --- README.md | 12 +- WORKSPACE | 12 +- examples/example_gem/BUILD | 16 -- examples/example_gem/BUILD.bazel | 35 ++++ examples/example_gem/WORKSPACE | 10 +- examples/example_gem/lib/BUILD | 6 +- examples/example_gem/lib/foo/BUILD | 6 +- examples/simple_rails_api/BUILD | 2 +- examples/simple_rails_api/WORKSPACE | 14 +- examples/simple_script/BUILD.bazel | 2 +- examples/simple_script/WORKSPACE | 12 +- examples/simple_script/lib/BUILD | 2 +- ruby/defs.bzl | 44 ++--- ruby/deps.bzl | 9 +- ruby/private/BUILD.bazel | 1 - ruby/private/bundle.bzl | 7 - ruby/private/bundle/{bundle.bzl => def.bzl} | 4 +- ruby/private/constants.bzl | 44 ++++- ruby/private/gem.bzl | 31 ---- ruby/private/gemspec.bzl | 87 --------- ruby/private/gemspec/BUILD.bazel | 9 + ruby/private/gemspec/def.bzl | 170 ++++++++++++++++++ ruby/private/gemspec/gemspec_template.tpl | 25 +++ ruby/private/gemspec/readme_template.tpl | 21 +++ ruby/private/gemspec_template.tpl | 10 -- ruby/private/providers.bzl | 10 +- ruby/private/rubocop/def.bzl | 4 +- ruby/private/sdk.bzl | 6 +- ruby/private/toolchains/ruby_runtime.bzl | 2 +- ruby/tests/BUILD.bazel | 10 +- ruby/tests/load_path_in_runfiles_test.rb | 2 +- .../testdata/another_workspace/WORKSPACE | 6 +- .../another_workspace/baz/qux/BUILD.bazel | 2 +- 33 files changed, 389 insertions(+), 244 deletions(-) delete mode 100644 examples/example_gem/BUILD create mode 100644 examples/example_gem/BUILD.bazel delete mode 100644 ruby/private/bundle.bzl rename ruby/private/bundle/{bundle.bzl => def.bzl} (98%) delete mode 100644 ruby/private/gem.bzl delete mode 100644 ruby/private/gemspec.bzl create mode 100644 ruby/private/gemspec/BUILD.bazel create mode 100644 ruby/private/gemspec/def.bzl create mode 100644 ruby/private/gemspec/gemspec_template.tpl create mode 100644 ruby/private/gemspec/readme_template.tpl delete mode 100644 ruby/private/gemspec_template.tpl diff --git a/README.md b/README.md index 1afc991..aae6a46 100644 --- a/README.md +++ b/README.md @@ -62,13 +62,13 @@ Add `rules_ruby_dependencies` and `rules_ruby_toolchains` into your `WORKSPACE` # To get the latest, grab the 'develop' branch. git_repository( - name = "bazelrules_ruby_ruby", + name = "bazelruby_rules_ruby", remote = "https://github.com/bazelruby/rules_ruby.git", branch = "develop", ) load( - "@bazelrules_ruby_ruby//ruby:deps.bzl", + "@bazelruby_rules_ruby//ruby:deps.bzl", "rules_ruby_toolchains", "rules_ruby_dependencies", ) @@ -109,7 +109,7 @@ Add `ruby_library`, `ruby_binary` or `ruby_test` into your `BUILD.bazel` files. ```python load( - "@bazelrules_ruby_ruby//ruby:defs.bzl", + "@bazelruby_rules_ruby//ruby:defs.bzl", "ruby_binary", "ruby_library", "ruby_test", @@ -424,13 +424,13 @@ Installing using a `Gemfile` that uses the `gemspec` keyword is not currently su ```python git_repository( - name = "bazelrules_ruby_ruby", + name = "bazelruby_rules_ruby", remote = "https://github.com/bazelruby/rules_ruby.git", tag = "v0.1.0", ) load( - "@bazelrules_ruby_ruby//ruby:deps.bzl", + "@bazelruby_rules_ruby//ruby:deps.bzl", "rules_ruby_toolchains", "rules_ruby_dependencies", ) @@ -439,7 +439,7 @@ rules_ruby_dependencies() rules_ruby_toolchains() -load("@bazelrules_ruby_ruby//ruby:defs.bzl", "ruby_bundle") +load("@bazelruby_rules_ruby//ruby:defs.bzl", "ruby_bundle") ruby_bundle( bundler_version = '2.1.2', diff --git a/WORKSPACE b/WORKSPACE index 9e255a2..4c1bdf1 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,6 +1,6 @@ -workspace(name = "bazelrules_ruby_ruby") +workspace(name = "bazelruby_rules_ruby") -load("@//ruby:deps.bzl", "ruby_register_toolchains", "rules_ruby_dependencies") +load("@//ruby:deps.bzl", "rules_ruby_dependencies", "rules_ruby_select_sdk") rules_ruby_dependencies() @@ -10,12 +10,12 @@ bazel_skylib_workspace() load("@bazel_skylib//lib:versions.bzl", "versions") -versions.check("1.2.1") +versions.check("2.1.1") -ruby_register_toolchains() +rules_ruby_select_sdk("2.7.0") local_repository( - name = "bazelrules_ruby_ruby_ruby_tests_testdata_another_workspace", + name = "bazelruby_rules_ruby_ruby_tests_testdata_another_workspace", path = "ruby/tests/testdata/another_workspace", ) @@ -91,7 +91,7 @@ container_pull( repository = "library/ruby", ) -load("@bazelrules_ruby_ruby//ruby:defs.bzl", "ruby_bundle") +load("@bazelruby_rules_ruby//ruby:defs.bzl", "ruby_bundle") ruby_bundle( name = "bundle", diff --git a/examples/example_gem/BUILD b/examples/example_gem/BUILD deleted file mode 100644 index 6ff3eb0..0000000 --- a/examples/example_gem/BUILD +++ /dev/null @@ -1,16 +0,0 @@ -load( - "@bazelrules_ruby_ruby//ruby:defs.bzl", - "rb_gem", -) - -package(default_visibility = ["//:__subpackages__"]) - -rb_gem( - name = "default_gem", - authors = ["Coinbase"], - gem_name = "example_gem", - version = "0.1.0", - deps = [ - "//lib:example_gem", - ], -) diff --git a/examples/example_gem/BUILD.bazel b/examples/example_gem/BUILD.bazel new file mode 100644 index 0000000..da599e8 --- /dev/null +++ b/examples/example_gem/BUILD.bazel @@ -0,0 +1,35 @@ +load( + "@bazelruby_rules_ruby//ruby:defs.bzl", + "ruby_gem", +) + +package(default_visibility = ["//:__subpackages__"]) + +ruby_gem( + name = "example_gem", + gem_author_emails = [ + "dev@coinbase.com", + "bazelruby@googlegroups.com", + ], + gem_authors = [ + "Coinbase", + "BazelRuby", + ], + gem_dependencies = { + "colored2": "", + "hashie": "", + }, + gem_description = "Example gem to demonstrate Bazel Gem packaging", + gem_development_dependencies = { + "rspec": "", + "rspec-its": "", + "rubocop": "", + }, + gem_homepage = "https://github.com/bazelruby/rules_ruby", + gem_name = "example-gem", + gem_summary = "Example gem to demonstrate Bazel Gem packaging", + gem_version = "0.1.0", + deps = [ + "//lib:example_gem", + ], +) diff --git a/examples/example_gem/WORKSPACE b/examples/example_gem/WORKSPACE index 0933a25..2fee5d4 100644 --- a/examples/example_gem/WORKSPACE +++ b/examples/example_gem/WORKSPACE @@ -1,21 +1,21 @@ -workspace(name = "bazelrules_ruby_ruby_example_gem") +workspace(name = "bazelruby_rules_ruby_example_gem") # Importing rules_ruby from the parent directory for developing # rules_ruby itself... local_repository( - name = "bazelrules_ruby_ruby", + name = "bazelruby_rules_ruby", path = "../..", ) load( - "@bazelrules_ruby_ruby//ruby:deps.bzl", - "ruby_register_toolchains", + "@bazelruby_rules_ruby//ruby:deps.bzl", "rules_ruby_dependencies", + "rules_ruby_select_sdk", ) rules_ruby_dependencies() -ruby_register_toolchains() +rules_ruby_select_sdk("2.7.0") load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") diff --git a/examples/example_gem/lib/BUILD b/examples/example_gem/lib/BUILD index cff539f..3239452 100644 --- a/examples/example_gem/lib/BUILD +++ b/examples/example_gem/lib/BUILD @@ -1,11 +1,11 @@ load( - "@bazelrules_ruby_ruby//ruby:defs.bzl", - "rb_library", + "@bazelruby_rules_ruby//ruby:defs.bzl", + "ruby_library", ) package(default_visibility = ["//:__subpackages__"]) -rb_library( +ruby_library( name = "example_gem", srcs = ["example_gem.rb"], deps = ["//lib/foo:default_library"], diff --git a/examples/example_gem/lib/foo/BUILD b/examples/example_gem/lib/foo/BUILD index d28e3e7..9e6e2fe 100644 --- a/examples/example_gem/lib/foo/BUILD +++ b/examples/example_gem/lib/foo/BUILD @@ -1,11 +1,11 @@ load( - "@bazelrules_ruby_ruby//ruby:defs.bzl", - "rb_library", + "@bazelruby_rules_ruby//ruby:defs.bzl", + "ruby_library", ) package(default_visibility = ["//:__subpackages__"]) -rb_library( +ruby_library( name = "default_library", srcs = ["bar.rb"], ) diff --git a/examples/simple_rails_api/BUILD b/examples/simple_rails_api/BUILD index 82fca5c..c53e54b 100644 --- a/examples/simple_rails_api/BUILD +++ b/examples/simple_rails_api/BUILD @@ -1,5 +1,5 @@ load( - "@bazelrules_ruby_ruby//ruby:defs.bzl", + "@bazelruby_rules_ruby//ruby:defs.bzl", "ruby_binary", ) diff --git a/examples/simple_rails_api/WORKSPACE b/examples/simple_rails_api/WORKSPACE index 84b4840..52d2999 100644 --- a/examples/simple_rails_api/WORKSPACE +++ b/examples/simple_rails_api/WORKSPACE @@ -1,29 +1,29 @@ -workspace(name = "bazelrules_ruby_ruby_example") +workspace(name = "bazelruby_rules_ruby_example") # Importing rules_ruby from the parent directory for developing # rules_ruby itself... local_repository( - name = "bazelrules_ruby_ruby", + name = "bazelruby_rules_ruby", path = "../..", ) load( - "@bazelrules_ruby_ruby//ruby:deps.bzl", - "ruby_register_toolchains", + "@bazelruby_rules_ruby//ruby:deps.bzl", "rules_ruby_dependencies", + "rules_ruby_select_sdk", ) rules_ruby_dependencies() -ruby_register_toolchains(version = "2.7.0") +rules_ruby_select_sdk(version = "2.7.0") load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") bazel_skylib_workspace() -load("@bazelrules_ruby_ruby//ruby:defs.bzl", "bundle_install") +load("@bazelruby_rules_ruby//ruby:defs.bzl", "ruby_bundle") -bundle_install( +ruby_bundle( name = "bundle", gemfile = "//:Gemfile", gemfile_lock = "//:Gemfile.lock", diff --git a/examples/simple_script/BUILD.bazel b/examples/simple_script/BUILD.bazel index 078de0f..bf0e803 100644 --- a/examples/simple_script/BUILD.bazel +++ b/examples/simple_script/BUILD.bazel @@ -1,5 +1,5 @@ load( - "@bazelrules_ruby_ruby//ruby:defs.bzl", + "@bazelruby_rules_ruby//ruby:defs.bzl", "ruby_binary", "ruby_rspec", "ruby_rubocop", diff --git a/examples/simple_script/WORKSPACE b/examples/simple_script/WORKSPACE index bc4d0b7..e99b80a 100644 --- a/examples/simple_script/WORKSPACE +++ b/examples/simple_script/WORKSPACE @@ -1,23 +1,23 @@ -workspace(name = "bazelrules_ruby_ruby_example") +workspace(name = "bazelruby_rules_ruby_example") # Importing rules_ruby from the parent directory for developing # rules_ruby itself... local_repository( - name = "bazelrules_ruby_ruby", + name = "bazelruby_rules_ruby", path = "../..", ) load( - "@bazelrules_ruby_ruby//ruby:deps.bzl", - "ruby_register_toolchains", + "@bazelruby_rules_ruby//ruby:deps.bzl", "rules_ruby_dependencies", + "rules_ruby_select_sdk", ) rules_ruby_dependencies() -ruby_register_toolchains(version = "2.7.0") +rules_ruby_select_sdk(version = "2.7.0") -load("@bazelrules_ruby_ruby//ruby:defs.bzl", "ruby_bundle") +load("@bazelruby_rules_ruby//ruby:defs.bzl", "ruby_bundle") ruby_bundle( name = "bundle", diff --git a/examples/simple_script/lib/BUILD b/examples/simple_script/lib/BUILD index 9721cb8..f125ff8 100644 --- a/examples/simple_script/lib/BUILD +++ b/examples/simple_script/lib/BUILD @@ -1,5 +1,5 @@ load( - "@bazelrules_ruby_ruby//ruby:defs.bzl", + "@bazelruby_rules_ruby//ruby:defs.bzl", "ruby_library", ) diff --git a/ruby/defs.bzl b/ruby/defs.bzl index 1e386a5..1777d38 100644 --- a/ruby/defs.bzl +++ b/ruby/defs.bzl @@ -1,56 +1,44 @@ load( - "@bazelrules_ruby_ruby//ruby/private:toolchain.bzl", + "@bazelruby_rules_ruby//ruby/private:toolchain.bzl", _toolchain = "ruby_toolchain", ) load( - "@bazelrules_ruby_ruby//ruby/private:library.bzl", + "@bazelruby_rules_ruby//ruby/private:library.bzl", _library = "ruby_library", ) load( - "@bazelrules_ruby_ruby//ruby/private:binary.bzl", + "@bazelruby_rules_ruby//ruby/private:binary.bzl", _binary = "ruby_binary", _test = "ruby_test", ) load( - "@bazelrules_ruby_ruby//ruby/private:bundle.bzl", - _ruby_bundle = "ruby_bundle", + "@bazelruby_rules_ruby//ruby/private/bundle:def.bzl", + _bundle = "bundle_install", + _ruby_bundle = "ruby_bundle_install", ) load( - "@bazelrules_ruby_ruby//ruby/private:rspec.bzl", - _ruby_rspec = "ruby_rspec", - _ruby_rspec_test = "ruby_rspec_test", + "@bazelruby_rules_ruby//ruby/private:rspec.bzl", + _rspec = "ruby_rspec", + _rspec_test = "ruby_rspec_test", ) load( - "@bazelrules_ruby_ruby//ruby/private/rubocop:def.bzl", + "@bazelruby_rules_ruby//ruby/private/rubocop:def.bzl", _rubocop = "rubocop", ) load( - "@bazelrules_ruby_ruby//ruby/private:gemspec.bzl", - _gemspec = "rb_gemspec", -) -load( - "@bazelrules_ruby_ruby//ruby/private:gem.bzl", - _gem = "rb_gem", + "@bazelruby_rules_ruby//ruby/private/gemspec:def.bzl", + _gem = "gem", + _gemspec = "gemspec", ) ruby_toolchain = _toolchain - ruby_library = _library ruby_binary = _binary ruby_test = _test -ruby_rspec_test = _ruby_rspec_test -ruby_rspec = _ruby_rspec +ruby_rspec_test = _rspec_test +ruby_rspec = _rspec ruby_bundle = _ruby_bundle +ruby_bundle_install = _bundle ruby_rubocop = _rubocop ruby_gemspec = _gemspec ruby_gem = _gem - -rb_toolchain = _toolchain -rb_library = _library -rb_binary = _binary -rb_test = _test -rb_rspec = _ruby_rspec -rb_bundle = _ruby_bundle -rb_rubocop = _rubocop -rb_gemspec = _gemspec -rb_gem = _gem diff --git a/ruby/deps.bzl b/ruby/deps.bzl index e1899f9..3a7c45b 100644 --- a/ruby/deps.bzl +++ b/ruby/deps.bzl @@ -1,13 +1,12 @@ # Repository rules load( - "@bazelrules_ruby_ruby//ruby/private:dependencies.bzl", + "@bazelruby_rules_ruby//ruby/private:dependencies.bzl", _rules_ruby_dependencies = "rules_ruby_dependencies", ) load( - "@bazelrules_ruby_ruby//ruby/private:sdk.bzl", - _register_toolchains = "ruby_register_toolchains", + "@bazelruby_rules_ruby//ruby/private:sdk.bzl", + _rules_ruby_select_sdk = "rules_ruby_select_sdk", ) rules_ruby_dependencies = _rules_ruby_dependencies - -ruby_register_toolchains = _register_toolchains +rules_ruby_select_sdk = _rules_ruby_select_sdk diff --git a/ruby/private/BUILD.bazel b/ruby/private/BUILD.bazel index cb0b35a..1c16e04 100644 --- a/ruby/private/BUILD.bazel +++ b/ruby/private/BUILD.bazel @@ -1,7 +1,6 @@ exports_files( [ "binary_wrapper.tpl", - "gemspec_template.tpl", ], visibility = ["//visibility:public"], ) diff --git a/ruby/private/bundle.bzl b/ruby/private/bundle.bzl deleted file mode 100644 index 292d6e1..0000000 --- a/ruby/private/bundle.bzl +++ /dev/null @@ -1,7 +0,0 @@ -load( - "//ruby/private/bundle:bundle.bzl", - _ruby_bundle = "ruby_bundle", -) - -bundle_install = _ruby_bundle -ruby_bundle = _ruby_bundle diff --git a/ruby/private/bundle/bundle.bzl b/ruby/private/bundle/def.bzl similarity index 98% rename from ruby/private/bundle/bundle.bzl rename to ruby/private/bundle/def.bzl index afe51ce..34128f9 100644 --- a/ruby/private/bundle/bundle.bzl +++ b/ruby/private/bundle/def.bzl @@ -20,7 +20,7 @@ def run_bundler(runtime_ctx, bundler_arguments, previous_result): # add --verbose to all commands except install if bundler_command != "install": - bundler_args += ["--verbose"] + bundler_args.append("--verbose") bundler_args += bundler_arguments[1:] @@ -185,7 +185,7 @@ def _ruby_bundle_impl(ctx): # 4. Generate the BUILD file for the bundle generate_bundle_build_file(runtime_ctx, result) -ruby_bundle = repository_rule( +ruby_bundle_install = repository_rule( implementation = _ruby_bundle_impl, attrs = BUNDLE_ATTRS, ) diff --git a/ruby/private/constants.bzl b/ruby/private/constants.bzl index 32ee05c..eb1880f 100644 --- a/ruby/private/constants.bzl +++ b/ruby/private/constants.bzl @@ -1,6 +1,6 @@ load(":providers.bzl", "RubyLibrary") -RULES_RUBY_WORKSPACE_NAME = "@bazelrules_ruby_ruby" +RULES_RUBY_WORKSPACE_NAME = "@bazelruby_rules_ruby" TOOLCHAIN_TYPE_NAME = "%s//ruby:toolchain_type" % RULES_RUBY_WORKSPACE_NAME DEFAULT_BUNDLER_VERSION = "2.1.2" @@ -106,3 +106,45 @@ BUNDLE_ATTRS = { allow_single_file = True, ), } + +GEMSPEC_ATTRS = { + "gem_name": attr.string(), + "gem_version": attr.string(default = "0.0.1"), + "gem_summary": attr.string(), + "gem_description": attr.string(), + "gem_homepage": attr.string(), + "gem_authors": attr.string_list(), + "gem_author_emails": attr.string_list(), + "gem_dependencies": attr.string_dict( + allow_empty = True, + doc = "Key value pairs of gem dependencies (name, version) where version can be None", + ), + "gem_development_dependencies": attr.string_dict( + allow_empty = True, + default = { + "rspec": "", + "rspec-its": "", + "rubocop": "", + }, + doc = "Key value pairs of gem dependencies (name, version) where version can be None", + ), + "srcs": attr.label_list( + allow_files = True, + default = [], + ), + "require_paths": attr.string_list(), + "deps": attr.label_list( + allow_files = True, + ), + "data": attr.label_list( + allow_files = True, + ), + "_gemspec_template": attr.label( + allow_single_file = True, + default = "%s//ruby/private/gemspec:gemspec_template.tpl" % RULES_RUBY_WORKSPACE_NAME, + ), + "_readme_template": attr.label( + allow_single_file = True, + default = "%s//ruby/private/gemspec:readme_template.tpl" % RULES_RUBY_WORKSPACE_NAME, + ), +} diff --git a/ruby/private/gem.bzl b/ruby/private/gem.bzl deleted file mode 100644 index 9108192..0000000 --- a/ruby/private/gem.bzl +++ /dev/null @@ -1,31 +0,0 @@ -load( - ":gemspec.bzl", - _rb_gemspec = "rb_gemspec", -) -load( - "@rules_pkg//:pkg.bzl", - "pkg_zip", -) - -def rb_gem(name, version, gem_name, srcs = [], **kwargs): - _zip_name = "%s-%s" % (gem_name, version) - _gemspec_name = name + "_gemspec" - - _rb_gemspec( - name = _gemspec_name, - gem_name = gem_name, - version = version, - **kwargs - ) - - pkg_zip( - name = _zip_name, - srcs = srcs + [":" + _gemspec_name], - strip_prefix = "./", - ) - - native.alias( - name = name, - actual = ":" + _zip_name, - visibility = ["//visibility:public"], - ) diff --git a/ruby/private/gemspec.bzl b/ruby/private/gemspec.bzl deleted file mode 100644 index ee7bdac..0000000 --- a/ruby/private/gemspec.bzl +++ /dev/null @@ -1,87 +0,0 @@ -load( - "//ruby/private/tools:deps.bzl", - _transitive_deps = "transitive_deps", -) -load( - "//ruby/private:providers.bzl", - "RubyGem", - "RubyLibrary", -) - -def _get_transitive_srcs(srcs, deps): - for dep in deps: - print(dep[RubyLibrary].transitive_ruby_srcs) - - return depset( - srcs, - transitive = [dep[RubyLibrary].transitive_ruby_srcs for dep in deps], - ) - -def _unique_elems(list): - _out = [] - _prev = None - for elem in sorted(list): - if _prev != elem: - _out.append(elem) - - return _out - -def _rb_gem_impl(ctx): - gemspec = ctx.actions.declare_file("%s.gemspec" % ctx.attr.gem_name) - - _ruby_files = [] - _require_paths = [] - for file in _get_transitive_srcs([], ctx.attr.deps).to_list(): - _ruby_files.append(file.short_path) - _require_paths.append(file.dirname) - - _require_paths = _unique_elems(_require_paths) # Set is not supported in Starlark - - ctx.actions.expand_template( - template = ctx.file._gemspec_template, - output = gemspec, - substitutions = { - "{name}": "\"%s\"" % ctx.label.name, - "{srcs}": repr(_ruby_files), - "{authors}": repr(ctx.attr.authors), - "{version}": ctx.attr.version, - "{require_paths}": repr(_require_paths), - }, - ) - - return [ - DefaultInfo(files = _get_transitive_srcs([gemspec], ctx.attr.deps)), - RubyGem( - ctx = ctx, - version = ctx.attr.version, - ), - ] - -_ATTRS = { - "version": attr.string( - default = "0.0.1", - ), - "authors": attr.string_list(), - "deps": attr.label_list( - allow_files = True, - ), - "data": attr.label_list( - allow_files = True, - ), - "_gemspec_template": attr.label( - allow_single_file = True, - default = "gemspec_template.tpl", - ), - "gem_name": attr.string(), - "srcs": attr.label_list( - allow_files = True, - default = [], - ), - "require_paths": attr.string_list(), -} - -rb_gemspec = rule( - implementation = _rb_gem_impl, - attrs = _ATTRS, - provides = [DefaultInfo, RubyGem], -) diff --git a/ruby/private/gemspec/BUILD.bazel b/ruby/private/gemspec/BUILD.bazel new file mode 100644 index 0000000..64bbe87 --- /dev/null +++ b/ruby/private/gemspec/BUILD.bazel @@ -0,0 +1,9 @@ +package(default_visibility = ["//ruby/private/gemspec:__pkg__"]) + +exports_files( + [ + "gemspec_template.tpl", + "readme_template.tpl", + ], + visibility = ["//visibility:public"], +) diff --git a/ruby/private/gemspec/def.bzl b/ruby/private/gemspec/def.bzl new file mode 100644 index 0000000..ae8cbe5 --- /dev/null +++ b/ruby/private/gemspec/def.bzl @@ -0,0 +1,170 @@ +load( + "//ruby/private:providers.bzl", + "RubyGem", + "RubyLibrary", +) +load( + "//ruby/private:constants.bzl", + "GEMSPEC_ATTRS", +) +load( + "@rules_pkg//:pkg.bzl", + "pkg_zip", +) + +def _get_transitive_srcs(srcs, deps): + for dep in deps: + print(dep[RubyLibrary].transitive_ruby_srcs) + + return depset( + srcs, + transitive = [dep[RubyLibrary].transitive_ruby_srcs for dep in deps], + ) + +def _unique_elems(list): + _out = [] + _prev = None + for elem in sorted(list): + if _prev != elem: + _out.append(elem) + + return _out + +# Converts gem name and optionally a version into a +# gemspec line "spec.add_[development_]dependency 'gem-name', [ 'gem-version' ]" +def _gem_dependency(name, version = "", development = False): + dependency_type = "spec.add_development_dependency" if development else "spec.add_dependency" + + output = "%s '%s'" % (dependency_type, name) + if version != "": + output += ", '%s'" % version + + return output + +# Converts gem name and optionally to a bullet list +def _markdown_gem_dependency(name, version = ""): + output = " * %s " % name + if version != "": + output += " (version %s) " % (version) + + return output + +def _markdown_ul(list = []): + return ("\n * " + "\n * ".join(list) + "\n") + +# Converts a dictionary (key = gem name, value = gem version or None) +# to a string to be inserted into the gemspec. +def _gem_dependencies(gem_dict = {}): + dependencies = [_gem_dependency(k, v) for k, v in gem_dict.items()] + return ("\n " + "\n ".join(dependencies)) + +# Converts a dictionary (key = gem name, value = gem version or None) +# to a string to be inserted into the gemspec. +def _markdown_gem_dependencies(gem_dict = {}, type = "Runtime"): + dependencies = [_markdown_gem_dependency(k, v) for k, v in gem_dict.items()] + output = "\n### %s Dependencies\n\n" % type + output += "\n " + "\n ".join(dependencies) + "\n\n" + return (output) + +def _gem_impl(ctx): + gemspec = ctx.actions.declare_file("%s.gemspec" % ctx.attr.gem_name) + gem_readme = ctx.actions.declare_file("README.md") + + _ruby_files = [] + _require_paths = [] + + for file in _get_transitive_srcs([], ctx.attr.deps).to_list(): + _ruby_files.append(file.short_path) + _require_paths.append(file.dirname) + + if len(_ruby_files) == 0: + _gem_sources = "`git ls-files -z`.split(\"\\x0\").reject { |f| f.match(/^(test|spec|features)\\//) }" + else: + _gem_sources = repr(_ruby_files) + + if ctx.attr.gem_homepage != "": + _gem_title = "[%s](%s)" % (ctx.attr.gem_name, ctx.attr.gem_homepage) + else: + _gem_title = "%s" % (ctx.attr.gem_name) + + ctx.actions.expand_template( + template = ctx.file._gemspec_template, + output = gemspec, + substitutions = { + "{gem_author_emails}": repr(ctx.attr.gem_author_emails), + "{gem_authors}": repr(ctx.attr.gem_authors), + "{gem_dependencies}": _gem_dependencies(ctx.attr.gem_dependencies), + "{gem_description}": ctx.attr.gem_description if ctx.attr.gem_description else ctx.attr.gem_summary, + "{gem_development_dependencies}": _gem_dependencies(ctx.attr.gem_development_dependencies), + "{gem_homepage}": ctx.attr.gem_homepage, + "{gem_name}": ctx.attr.gem_name, + "{gem_require_paths}": repr(["lib"]), + "{gem_sources}": _gem_sources, + "{gem_summary}": ctx.attr.gem_summary, + "{gem_version}": ctx.attr.gem_version, + }, + ) + + _dependencies = _markdown_gem_dependencies(ctx.attr.gem_dependencies, "Runtime") + _dependencies += _markdown_gem_dependencies(ctx.attr.gem_development_dependencies, "Development") + + ctx.actions.expand_template( + template = ctx.file._readme_template, + output = gem_readme, + substitutions = { + "{gem_authorship}": _markdown_ul(ctx.attr.gem_authors), + "{gem_dependencies}": _dependencies, + "{gem_description}": ctx.attr.gem_description if ctx.attr.gem_description else ctx.attr.gem_summary, + "{gem_name}": ctx.attr.gem_name, + "{gem_title}": _gem_title, + "{gem_summary}": ctx.attr.gem_summary, + "{gem_version}": ctx.attr.gem_version, + }, + ) + + return [ + DefaultInfo( + files = _get_transitive_srcs([gemspec, gem_readme], ctx.attr.deps), + ), + RubyGem( + ctx = ctx, + gem_author_emails = ctx.attr.gem_author_emails, + gem_authors = ctx.attr.gem_authors, + gem_dependencies = ctx.attr.gem_dependencies, + gem_description = ctx.attr.gem_description, + gem_development_dependencies = ctx.attr.gem_development_dependencies, + gem_homepage = ctx.attr.gem_homepage, + gem_name = ctx.attr.gem_name, + gem_summary = ctx.attr.gem_summary, + gem_version = ctx.attr.gem_version, + ), + ] + +gemspec = rule( + implementation = _gem_impl, + attrs = GEMSPEC_ATTRS, + provides = [DefaultInfo, RubyGem], +) + +def gem(name, gem_name, gem_version, srcs = [], **kwargs): + _zip_name = "%s-%s" % (gem_name, gem_version) + _gemspec_name = name + "_gemspec" + + gemspec( + name = _gemspec_name, + gem_name = gem_name, + gem_version = gem_version, + **kwargs + ) + + pkg_zip( + name = _zip_name, + srcs = srcs + [":" + _gemspec_name], + strip_prefix = "./", + ) + + native.alias( + name = name, + actual = ":" + _zip_name, + visibility = ["//visibility:public"], + ) diff --git a/ruby/private/gemspec/gemspec_template.tpl b/ruby/private/gemspec/gemspec_template.tpl new file mode 100644 index 0000000..ebe27a3 --- /dev/null +++ b/ruby/private/gemspec/gemspec_template.tpl @@ -0,0 +1,25 @@ +# vim: ft=ruby +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) + +Gem::Specification.new do |spec| + spec.name = "{gem_name}" + spec.version = "{gem_version}" + spec.summary = "{gem_summary}" + spec.description = "{gem_description}" + spec.homepage = "{gem_homepage}" + + spec.authors = {gem_authors} + spec.email = {gem_author_emails} + + spec.files = {gem_sources} + spec.bindir = 'exe' + spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } + spec.require_paths = {gem_require_paths} + + spec.required_ruby_version = '>= 2.3' + + {gem_dependencies} + + {gem_development_dependencies} +end diff --git a/ruby/private/gemspec/readme_template.tpl b/ruby/private/gemspec/readme_template.tpl new file mode 100644 index 0000000..1b867fb --- /dev/null +++ b/ruby/private/gemspec/readme_template.tpl @@ -0,0 +1,21 @@ +# {gem_title} (Version: {gem_version}) + +> NOTE: You are reading the auto-generated README for Gem {gem_name}. + +## Summary + +{gem_summary} + +## Description + +{gem_description} + +## Dependencies + +{gem_dependencies} + +## Author(s) + +Copyright © 2020 + +{gem_authorship} diff --git a/ruby/private/gemspec_template.tpl b/ruby/private/gemspec_template.tpl deleted file mode 100644 index 1c65d0b..0000000 --- a/ruby/private/gemspec_template.tpl +++ /dev/null @@ -1,10 +0,0 @@ -Gem::Specification.new do |s| - s.name = {name} - s.summary = {name} - s.authors = {authors} - s.version = {version} - s.files = {srcs} - s.require_paths = {require_paths} - - s.add_dependency('csf') -end \ No newline at end of file diff --git a/ruby/private/providers.bzl b/ruby/private/providers.bzl index 10f466c..80fb6ca 100644 --- a/ruby/private/providers.bzl +++ b/ruby/private/providers.bzl @@ -19,6 +19,14 @@ RubyGem = provider( doc = "Carries info required to package a ruby gem", fields = [ "ctx", - "version", + "gem_author_emails", + "gem_authors", + "gem_dependencies", + "gem_description", + "gem_development_dependencies", + "gem_homepage", + "gem_name", + "gem_summary", + "gem_version", ], ) diff --git a/ruby/private/rubocop/def.bzl b/ruby/private/rubocop/def.bzl index 5bb5215..0319906 100644 --- a/ruby/private/rubocop/def.bzl +++ b/ruby/private/rubocop/def.bzl @@ -1,4 +1,4 @@ -load("@bazelrules_ruby_ruby//ruby/private:binary.bzl", "ruby_binary") +load("@bazelruby_rules_ruby//ruby/private:binary.bzl", "ruby_binary") # This wraps an rb_binary in a script that is executed from the workspace folder def rubocop(name, bin, deps): @@ -9,7 +9,7 @@ def rubocop(name, bin, deps): deps = deps, ) - runner = "@bazelrules_ruby_ruby//ruby/private/rubocop:runner.sh.tpl" + runner = "@bazelruby_rules_ruby//ruby/private/rubocop:runner.sh.tpl" native.genrule( name = name, tools = [bin_name], diff --git a/ruby/private/sdk.bzl b/ruby/private/sdk.bzl index ba178cd..dc6c13c 100644 --- a/ruby/private/sdk.bzl +++ b/ruby/private/sdk.bzl @@ -1,9 +1,9 @@ load( - "@bazelrules_ruby_ruby//ruby/private/toolchains:ruby_runtime.bzl", + "@bazelruby_rules_ruby//ruby/private/toolchains:ruby_runtime.bzl", _ruby_runtime = "ruby_runtime", ) -def ruby_register_toolchains(version = "host"): +def rules_ruby_select_sdk(version = "host"): """Registers ruby toolchains in the WORKSPACE file.""" supported_versions = ["host", "2.6.3", "2.6.5", "2.7.0"] @@ -13,7 +13,7 @@ def ruby_register_toolchains(version = "host"): version = version, ) else: - fail("ruby_register_toolchains: unsupported ruby version '%s' not in '%s'" % (version, supported_versions)) + fail("rules_ruby_select_sdk: unsupported ruby version '%s' not in '%s'" % (version, supported_versions)) native.register_toolchains( "@org_ruby_lang_ruby_toolchain//:toolchain", diff --git a/ruby/private/toolchains/ruby_runtime.bzl b/ruby/private/toolchains/ruby_runtime.bzl index 48550eb..7fe18c2 100644 --- a/ruby/private/toolchains/ruby_runtime.bzl +++ b/ruby/private/toolchains/ruby_runtime.bzl @@ -57,7 +57,7 @@ def _install_ruby(ctx, ruby): ctx.symlink(ruby.interpreter_realpath, ruby.rel_interpreter_path) # Places the interpreter at a predictable place regardless of the actual binary name - # so that bundle_install can depend on it. + # so that ruby_bundle can depend on it. ctx.template( "ruby", ctx.attr._interpreter_wrapper_template, diff --git a/ruby/tests/BUILD.bazel b/ruby/tests/BUILD.bazel index 58d7b7e..d26dabe 100644 --- a/ruby/tests/BUILD.bazel +++ b/ruby/tests/BUILD.bazel @@ -120,7 +120,7 @@ ruby_binary( main = "load_path_in_runfiles_test.rb", deps = [ "//ruby/tests/testdata:g", - "@bazelrules_ruby_ruby_ruby_tests_testdata_another_workspace//baz/qux:j", + "@bazelruby_rules_ruby_ruby_tests_testdata_another_workspace//baz/qux:j", ], ) @@ -137,7 +137,7 @@ ruby_test( main = "load_path_in_runfiles_test.rb", deps = [ "//ruby/tests/testdata:g", - "@bazelrules_ruby_ruby_ruby_tests_testdata_another_workspace//baz/qux:j", + "@bazelruby_rules_ruby_ruby_tests_testdata_another_workspace//baz/qux:j", ], ) @@ -250,13 +250,13 @@ pkg_tar( include_runfiles = True, package_dir = "/app", remap_paths = { - "ruby": "load_path_in_runfiles.runfiles/bazelrules_ruby_ruby/ruby", + "ruby": "load_path_in_runfiles.runfiles/bazelruby_rules_ruby/ruby", ".": "load_path_in_runfiles.runfiles/", }, strip_prefix = "dummy", symlinks = { - "/app/load_path_in_runfiles.runfiles/bazelrules_ruby_ruby/external": "/app/load_path_in_runfiles.runfiles", - "/app/load_path_in_runfiles": "/app/load_path_in_runfiles.runfiles/bazelrules_ruby_ruby/ruby/tests/load_path_in_runfiles", + "/app/load_path_in_runfiles.runfiles/bazelruby_rules_ruby/external": "/app/load_path_in_runfiles.runfiles", + "/app/load_path_in_runfiles": "/app/load_path_in_runfiles.runfiles/bazelruby_rules_ruby/ruby/tests/load_path_in_runfiles", }, ) diff --git a/ruby/tests/load_path_in_runfiles_test.rb b/ruby/tests/load_path_in_runfiles_test.rb index f36ef74..9521827 100644 --- a/ruby/tests/load_path_in_runfiles_test.rb +++ b/ruby/tests/load_path_in_runfiles_test.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true require 'ruby/tests/testdata/foo/g' -require 'external/bazelrules_ruby_ruby_ruby_tests_testdata_another_workspace/baz/qux/j' +require 'external/bazelruby_rules_ruby_ruby_tests_testdata_another_workspace/baz/qux/j' [g, j] diff --git a/ruby/tests/testdata/another_workspace/WORKSPACE b/ruby/tests/testdata/another_workspace/WORKSPACE index 50a1b56..86ed1c1 100644 --- a/ruby/tests/testdata/another_workspace/WORKSPACE +++ b/ruby/tests/testdata/another_workspace/WORKSPACE @@ -1,5 +1,5 @@ -workspace(name = "bazelrules_ruby_ruby_ruby_tests_testdata_another_workspace") +workspace(name = "bazelruby_rules_ruby_ruby_tests_testdata_another_workspace") -load("@bazelrules_ruby_ruby//ruby:defs.bzl", "ruby_register_toolchains") +load("@bazelruby_rules_ruby//ruby:defs.bzl", "rules_ruby_select_sdk") -ruby_register_toolchains() +rules_ruby_select_sdk() diff --git a/ruby/tests/testdata/another_workspace/baz/qux/BUILD.bazel b/ruby/tests/testdata/another_workspace/baz/qux/BUILD.bazel index fa86829..89a2dfe 100644 --- a/ruby/tests/testdata/another_workspace/baz/qux/BUILD.bazel +++ b/ruby/tests/testdata/another_workspace/baz/qux/BUILD.bazel @@ -1,4 +1,4 @@ -load("@bazelrules_ruby_ruby//ruby:defs.bzl", "ruby_library") +load("@bazelruby_rules_ruby//ruby:defs.bzl", "ruby_library") package(default_visibility = ["//visibility:public"]) From b271ed2319157f4eb0631acc4038655b186ab729 Mon Sep 17 00:00:00 2001 From: Konstantin Gredeskoul Date: Sat, 29 Feb 2020 21:43:47 -0800 Subject: [PATCH 18/19] Enhanced ruby_gem with many more options; updated README --- README.md | 430 ++++++++++++++++------ examples/example_gem/BUILD.bazel | 14 +- ruby/private/bundle/def.bzl | 8 +- ruby/private/constants.bzl | 12 +- ruby/private/gemspec/def.bzl | 30 +- ruby/private/gemspec/gemspec_template.tpl | 2 +- ruby/private/gemspec/readme_template.tpl | 6 +- ruby/private/providers.bzl | 2 +- ruby/private/tools/deprecations.bzl | 12 - 9 files changed, 352 insertions(+), 164 deletions(-) delete mode 100644 ruby/private/tools/deprecations.bzl diff --git a/README.md b/README.md index aae6a46..04a9f17 100644 --- a/README.md +++ b/README.md @@ -56,10 +56,20 @@ Note: we have a short guide on [Building your first Ruby Project](https://github ### `WORKSPACE` File -Add `rules_ruby_dependencies` and `rules_ruby_toolchains` into your `WORKSPACE` file. +#### Load dependencies, select Ruby SDK and define one or more Bundles ```python -# To get the latest, grab the 'develop' branch. +workspace(name = "my_ruby_project") + +load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") +bazel_skylib_workspace() + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") + +#——————————————————————————————————————————————————————————————————————— +# To get the latest ruby rules, grab the 'develop' branch. +#——————————————————————————————————————————————————————————————————————— git_repository( name = "bazelruby_rules_ruby", @@ -69,45 +79,58 @@ git_repository( load( "@bazelruby_rules_ruby//ruby:deps.bzl", - "rules_ruby_toolchains", "rules_ruby_dependencies", + "rules_ruby_select_sdk", ) rules_ruby_dependencies() -rules_ruby_toolchains() -``` +#——————————————————————————————————————————————————————————————————————— +# Specify Ruby version — this will either build Ruby or use a local +# RBENV installation if the Ruby version matches. +#——————————————————————————————————————————————————————————————————————— -Next, add any external Gem dependencies you may have via `ruby_bundle` command. -The name of the bundle becomes a reference to this particular Gemfile.lock. +rules_ruby_select_sdk(version = "2.7.0") -Install external gems that can be later referenced as `@//:`, -and the executables from each gem can be accessed as `@` -for instance, `@bundle//:bin/rubocop`. +#——————————————————————————————————————————————————————————————————————— +# Now, load the ruby_bundle rule & install gems specified in the Gemfile +#——————————————————————————————————————————————————————————————————————— -You can install more than one bundle per WORKSPACE, but that's not recommended. +load( + "@bazelruby_rules_ruby//ruby:defs.bzl", + "ruby_bundle", +) -```python ruby_bundle( - name = "bundle", - gemfile = ":Gemfile", - gemfile_lock = ":Gemfile.lock", - bundler_version = "2.1.2", + name = "bundle", + excludes = { + "mini_portile": ["test/**/*"], + }, + gemfile = "//:Gemfile", + gemfile_lock = "//:Gemfile.lock", ) +# You can specify more than one bundle in the WORKSPACE file ruby_bundle( - name = "bundle_app_shopping", - gemfile = "//apps/shopping:Gemfile", - gemfile_lock = "//apps/shopping:Gemfile.lock", - bundler_version = "2.1.2", + name = "bundle_app_shopping", + gemfile = "//apps/shopping:Gemfile", + gemfile_lock = "//apps/shopping:Gemfile.lock", ) ``` -### `BUILD.bazel` files +### `BUILD.bazel` file(s) -Add `ruby_library`, `ruby_binary` or `ruby_test` into your `BUILD.bazel` files. +Any of the project `BUILD` files can now reference any gems included in the `Gemfile` referenced by the `ruby_bundle` rule, and defined in the project's `WORKSPACE` file. + +#### Define Ruby Executable, Library and an RSpec + +Add `ruby_library`, `ruby_binary`, `ruby_rspec` or `ruby_test` into your `BUILD.bazel` files. ```python +#——————————————————————————————————————————————————————————————————————— +# Define Ruby executable, test, spec and package a gem +#——————————————————————————————————————————————————————————————————————— + load( "@bazelruby_rules_ruby//ruby:defs.bzl", "ruby_binary", @@ -145,10 +168,52 @@ ruby_rspec( rspec_args = { "--format": "progress" }, deps = [":foo"] } +``` + +#### Package Ruby files as a Gem + +Use `ruby_gem` rule to package any number of ruby files or folders into a Ruby-Gem compatible ZIP archive. + +```python +load( + "@bazelruby_rules_ruby//ruby:defs.bzl", + "ruby_gem", +) + +ruby_gem( + name = "awesome-sauce-gem", # name of the build target + gem_name = "awesome-sauce", # name of the gem + gem_version = "0.1.0", + gem_summary = "Example gem to demonstrate Bazel Gem packaging", + gem_description = "Example gem to demonstrate Bazel Gem packaging", + gem_homepage = "https://github.com/bazelruby/rules_ruby", + gem_authors = [ + "BazelRuby", + "Konstantin Gredeskoul" + ], + gem_author_emails = [ + "bazelruby@googlegroups.com", + ], + gem_runtime_dependencies = { + "colored2": "~> 3.1.2", + "hashie": "", + }, + gem_development_dependencies = { + "rspec": "", + "rspec-its": "", + "rubocop": "", + }, + srcs = [ + glob("{bin,exe,lib,spec}/**/*.rb") + ], + deps = [ + "//lib:example_gem", + ], +) ``` -## Rules +### Rule Dependency Diagram > NOTE: this diagram is slightly outdated. @@ -156,13 +221,27 @@ The following diagram attempts to capture the implementation behind `ruby_librar ![Ruby Rules](docs/img/ruby_rules.png) ----- +## Rules ### `ruby_library` -

-ruby_library(name, deps, srcs, data, compatible_with, deprecation, distribs, features, licenses, restricted_to, tags, testonly, toolchains, visibility)
-
+```python +ruby_library( + name, + deps, + srcs, + data, + compatible_with, + deprecation, + distribs, + features, + licenses, + restricted_to, + tags, + testonly, + toolchains, + visibility) +``` @@ -233,13 +312,29 @@ ruby_library(name, deps, srcs, data, compatible_with, deprecation, distribs, fea
----- - ### `ruby_binary` -
-ruby_binary(name, deps, srcs, data, main, compatible_with, deprecation, distribs, features, licenses, restricted_to, tags, testonly, toolchains, visibility, args, output_licenses)
-
+```python +ruby_binary( + name, + deps, + srcs, + data, + main, + compatible_with, + deprecation, + distribs, + features, + licenses, + restricted_to, + tags, + testonly, + toolchains, + visibility, + args, + output_licenses +) +``` @@ -316,13 +411,31 @@ ruby_binary(name, deps, srcs, data, main, compatible_with, deprecation, distribs
----- - ### `ruby_test` -
-ruby_test(name, deps, srcs, data, main, compatible_with, deprecation, distribs, features, licenses, restricted_to, tags, testonly, toolchains, visibility, args, size, timeout, flaky, local, shard_count)
-
+```python +ruby_test( + name, + deps, + srcs, + data, + main, + compatible_with, + deprecation, + distribs, + features, + licenses, + restricted_to, + tags, + testonly, + toolchains, + visibility, + args, + size, + timeout, + flaky, + local, shard_count) +``` @@ -399,81 +512,24 @@ ruby_test(name, deps, srcs, data, main, compatible_with, deprecation, distribs,
----- - ### `ruby_bundle` **NOTE: This is a repository rule, and can only be used in a `WORKSPACE` file.** This rule installs gems defined in a Gemfile using Bundler, and exports individual gems from the bundle, as well as the entire bundle, available as a `ruby_library` that can be depended upon from other targets. -#### Limitations - -Installing using a `Gemfile` that uses the `gemspec` keyword is not currently supported. - -#### Conventions - -`ruby_bundle` creates several targets that can be used downstream. In the examples below we assume that your `ruby_bundle` has a name `app_bundle`: - - * `@app_bundle//:bundler` — references just the Bundler from the bundle. - * `@app_bundle//:gems` — references *all* gems in the bundle (i.e. "the entire bundle"). - * `@app_bundle//:gem-name` — references *just the specified* gem in the bundle, eg. `@app_bundle//:awesome_print`. - * `@app_bundle//:bin` — references to all installed executables from this bundle, with individual executables accessible via eg. `@app_bundle//:bin/rubocop` - -##### Example: `WORKSPACE`: - ```python -git_repository( - name = "bazelruby_rules_ruby", - remote = "https://github.com/bazelruby/rules_ruby.git", - tag = "v0.1.0", -) - -load( - "@bazelruby_rules_ruby//ruby:deps.bzl", - "rules_ruby_toolchains", - "rules_ruby_dependencies", -) - -rules_ruby_dependencies() - -rules_ruby_toolchains() - -load("@bazelruby_rules_ruby//ruby:defs.bzl", "ruby_bundle") - ruby_bundle( - bundler_version = '2.1.2', - name = "gems", - gemfile = "//:Gemfile", - gemfile_lock = "//:Gemfile.lock", -) -``` - -##### Example: `lib/BUILD.bazel`: - -```python -ruby_library( - name = "foo", - srcs = ["foo.rb"], - deps = ["@gems//:all"], + name, + gemfile, + gemfile_lock, + bundler_version = "2.1.2", + excludes = [], + ruby_sdk = "@org_ruby_lang_ruby_toolchain", + ruby_interpreter = "@org_ruby_lang_ruby_toolchain//:ruby", ) ``` -Or declare many gems in your `Gemfile`, and only use some of them in each ruby library: - -```python -ruby_binary( - name = "rubocop", - srcs = [":foo", ".rubocop.yml"], - args = ["-P", "-D", "-c" ".rubocop.yml"], - main = "@gems//:bin/rubocop", - deps = ["@gems//:rubocop"], -) -``` - -
-ruby_bundle(name, gemfile, gemfile_lock, bundler_version = "2.1.2")
-
@@ -520,13 +576,83 @@ ruby_bundle(name, gemfile, gemfile_lock, bundler_version = "2.1.2")
----- +#### Limitations + +Installing using a `Gemfile` that uses the `gemspec` keyword is not currently supported. + +#### Conventions + +`ruby_bundle` creates several targets that can be used downstream. In the examples below we assume that your `ruby_bundle` has a name `app_bundle`: + + * `@app_bundle//:bundler` — references just the Bundler from the bundle. + * `@app_bundle//:gems` — references *all* gems in the bundle (i.e. "the entire bundle"). + * `@app_bundle//:gem-name` — references *just the specified* gem in the bundle, eg. `@app_bundle//:awesome_print`. + * `@app_bundle//:bin` — references to all installed executables from this bundle, with individual executables accessible via eg. `@app_bundle//:bin/rubocop` + +#### `WORKSPACE`: + +```python +load("@bazelruby_rules_ruby//ruby:defs.bzl", "ruby_bundle") + +ruby_bundle( + name = "gems", + bundler_version = '2.1.2', + gemfile = "//:Gemfile", + gemfile_lock = "//:Gemfile.lock", +) +``` + +#### `BUILD.bazel`: + +```pythonj +# Reference the entire bundle with :gems + +ruby_library( + name = "foo", + srcs = ["foo.rb"], + deps = ["@gems//:gems"], +) + +# Or, reference specific gems from the bundle like so: + +ruby_binary( + name = "rubocop", + srcs = [":foo", ".rubocop.yml"], + args = ["-P", "-D", "-c" ".rubocop.yml"], + main = "@gems//:bin/rubocop", + deps = ["@gems//:rubocop"], +) +``` ### `ruby_rspec` -
-ruby_rspec(name, deps, srcs, data, main, rspec_args, bundle, compatible_with, deprecation, distribs, features, licenses, restricted_to, tags, testonly, toolchains, visibility, args, size, timeout, flaky, local, shard_count)
-
+```python +ruby_rspec( + name, + deps, + srcs, + data, + main, + rspec_args, + bundle, + compatible_with, + deprecation, + distribs, + features, + licenses, + restricted_to, + tags, + testonly, + toolchains, + visibility, + args, + size, + timeout, + flaky, + local, + shard_count +) +``` @@ -611,15 +737,30 @@ ruby_rspec(name, deps, srcs, data, main, rspec_args, bundle, compatible_with, de
----- - ### `ruby_gem` Used to generate a zipped gem containing its srcs, dependencies and a gemspec. -
-ruby_gem(name, gem_name, gem_version, srcs, authors, deps, data, includes)
-
+```python +ruby_gem( + name, + gem_name, + gem_version, + gem_summary, + gem_description, + gem_homepage, + gem_authors, + gem_author_emails, + gem_runtime_dependencies, + gem_development_dependencies, + require_paths = ["lib"], + srcs = srcs, + deps = deps, + data = data +) +``` + + @@ -635,7 +776,7 @@ ruby_gem(name, gem_name, gem_version, srcs, authors, deps, data, includes) @@ -645,11 +786,10 @@ ruby_gem(name, gem_name, gem_version, srcs, authors, deps, data, includes)

The name of the gem to be generated.

- - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
name Name, required -

A unique name for this rule.

+

A unique name for this build target.

gem_version - Label, required + String, optional

The version of the gem. Is used to name the output file, which becomes name-version.zip, and also @@ -658,7 +798,28 @@ ruby_gem(name, gem_name, gem_version, srcs, authors, deps, data, includes)

authorsgem_summary + String, optional +

One line summary of the gem purpose.

+
gem_description + String, required +

Single-line, paragraph-sized description text for the gem.

+
gem_homepage + String, optional +

Homepage URL of the gem.

+
gem_authors List of Strings, required

@@ -667,6 +828,15 @@ ruby_gem(name, gem_name, gem_version, srcs, authors, deps, data, includes)

gem_author_emails + List of Strings, optional +

+ List of email addresses of the authors. +

+
srcs @@ -688,6 +858,38 @@ ruby_gem(name, gem_name, gem_version, srcs, authors, deps, data, includes)

At least srcs or deps must be present

require_paths + List of Strings, optional +

+ List of paths to be added to the Ruby LOAD_PATH when using this gem. + Typically this value is just `lib` (which is also the default). +

+
gem_runtime_dependencies + String Dictionary, optional +

+ This is a dictionary where keys are gem names, and values are either an empty + string or a gem version specification. + For instance, the pessimistic version specifier ~> 3.0 means that all versions up to 4.0 are accepted. +

+
gem_development_dependencies + String Dictionary, optional +

+ Similar to the above, this specifies gems necessary for the development of the above gem, such as + testing gems, linters, code coverage and more. +

+
diff --git a/examples/example_gem/BUILD.bazel b/examples/example_gem/BUILD.bazel index da599e8..55948ee 100644 --- a/examples/example_gem/BUILD.bazel +++ b/examples/example_gem/BUILD.bazel @@ -7,6 +7,9 @@ package(default_visibility = ["//:__subpackages__"]) ruby_gem( name = "example_gem", + srcs = [ + "//lib:example_gem", + ], gem_author_emails = [ "dev@coinbase.com", "bazelruby@googlegroups.com", @@ -15,10 +18,6 @@ ruby_gem( "Coinbase", "BazelRuby", ], - gem_dependencies = { - "colored2": "", - "hashie": "", - }, gem_description = "Example gem to demonstrate Bazel Gem packaging", gem_development_dependencies = { "rspec": "", @@ -27,9 +26,10 @@ ruby_gem( }, gem_homepage = "https://github.com/bazelruby/rules_ruby", gem_name = "example-gem", + gem_runtime_dependencies = { + "colored2": "", + "hashie": "", + }, gem_summary = "Example gem to demonstrate Bazel Gem packaging", gem_version = "0.1.0", - deps = [ - "//lib:example_gem", - ], ) diff --git a/ruby/private/bundle/def.bzl b/ruby/private/bundle/def.bzl index 34128f9..d3fc880 100644 --- a/ruby/private/bundle/def.bzl +++ b/ruby/private/bundle/def.bzl @@ -9,7 +9,6 @@ load( "SCRIPT_INSTALL_GEM", ) load("//ruby/private:providers.bzl", "RubyRuntimeContext") -load("//ruby/private/tools:deprecations.bzl", "deprecated_attribute") # Runs bundler with arbitrary arguments # eg: run_bundler(runtime_ctx, [ "lock", " --gemfile", "Gemfile.rails5" ]) @@ -159,12 +158,7 @@ def _ruby_bundle_impl(ctx): ctx.symlink(ctx.attr._create_bundle_build_file, SCRIPT_BUILD_FILE_GENERATOR) ctx.symlink(ctx.attr._install_bundler, SCRIPT_INSTALL_GEM) - # version is too generic for this operation - deprecated_attribute(ctx, "version", "bundler_version") - if ctx.attr.bundler_version: - bundler_version = ctx.attr.bundler_version - else: - bundler_version = ctx.attr.version + bundler_version = ctx.attr.bundler_version # Setup this provider that we pass around between functions for convenience runtime_ctx = RubyRuntimeContext( diff --git a/ruby/private/constants.bzl b/ruby/private/constants.bzl index eb1880f..6600fc7 100644 --- a/ruby/private/constants.bzl +++ b/ruby/private/constants.bzl @@ -78,15 +78,9 @@ BUNDLE_ATTRS = { "gemfile_lock": attr.label( allow_single_file = True, ), - "version": attr.string( - mandatory = False, - ), "bundler_version": attr.string( default = DEFAULT_BUNDLER_VERSION, ), - "gemspec": attr.label( - allow_single_file = True, - ), "excludes": attr.string_list_dict( doc = "List of glob patterns per gem to be excluded from the library", ), @@ -115,7 +109,7 @@ GEMSPEC_ATTRS = { "gem_homepage": attr.string(), "gem_authors": attr.string_list(), "gem_author_emails": attr.string_list(), - "gem_dependencies": attr.string_dict( + "gem_runtime_dependencies": attr.string_dict( allow_empty = True, doc = "Key value pairs of gem dependencies (name, version) where version can be None", ), @@ -132,7 +126,9 @@ GEMSPEC_ATTRS = { allow_files = True, default = [], ), - "require_paths": attr.string_list(), + "require_paths": attr.string_list( + default = ["lib"], + ), "deps": attr.label_list( allow_files = True, ), diff --git a/ruby/private/gemspec/def.bzl b/ruby/private/gemspec/def.bzl index ae8cbe5..9560da8 100644 --- a/ruby/private/gemspec/def.bzl +++ b/ruby/private/gemspec/def.bzl @@ -33,7 +33,7 @@ def _unique_elems(list): # Converts gem name and optionally a version into a # gemspec line "spec.add_[development_]dependency 'gem-name', [ 'gem-version' ]" def _gem_dependency(name, version = "", development = False): - dependency_type = "spec.add_development_dependency" if development else "spec.add_dependency" + dependency_type = "spec.add_development_dependency" if development else "spec.add_runtime_dependency" output = "%s '%s'" % (dependency_type, name) if version != "": @@ -54,13 +54,13 @@ def _markdown_ul(list = []): # Converts a dictionary (key = gem name, value = gem version or None) # to a string to be inserted into the gemspec. -def _gem_dependencies(gem_dict = {}): +def _gem_runtime_dependencies(gem_dict = {}): dependencies = [_gem_dependency(k, v) for k, v in gem_dict.items()] return ("\n " + "\n ".join(dependencies)) # Converts a dictionary (key = gem name, value = gem version or None) # to a string to be inserted into the gemspec. -def _markdown_gem_dependencies(gem_dict = {}, type = "Runtime"): +def _markdown_gem_runtime_dependencies(gem_dict = {}, type = "Runtime"): dependencies = [_markdown_gem_dependency(k, v) for k, v in gem_dict.items()] output = "\n### %s Dependencies\n\n" % type output += "\n " + "\n ".join(dependencies) + "\n\n" @@ -93,9 +93,9 @@ def _gem_impl(ctx): substitutions = { "{gem_author_emails}": repr(ctx.attr.gem_author_emails), "{gem_authors}": repr(ctx.attr.gem_authors), - "{gem_dependencies}": _gem_dependencies(ctx.attr.gem_dependencies), + "{gem_runtime_dependencies}": _gem_runtime_dependencies(ctx.attr.gem_runtime_dependencies), "{gem_description}": ctx.attr.gem_description if ctx.attr.gem_description else ctx.attr.gem_summary, - "{gem_development_dependencies}": _gem_dependencies(ctx.attr.gem_development_dependencies), + "{gem_development_dependencies}": _gem_runtime_dependencies(ctx.attr.gem_development_dependencies), "{gem_homepage}": ctx.attr.gem_homepage, "{gem_name}": ctx.attr.gem_name, "{gem_require_paths}": repr(["lib"]), @@ -105,19 +105,19 @@ def _gem_impl(ctx): }, ) - _dependencies = _markdown_gem_dependencies(ctx.attr.gem_dependencies, "Runtime") - _dependencies += _markdown_gem_dependencies(ctx.attr.gem_development_dependencies, "Development") + _dependencies = _markdown_gem_runtime_dependencies(ctx.attr.gem_runtime_dependencies, "Runtime") + _dependencies += _markdown_gem_runtime_dependencies(ctx.attr.gem_development_dependencies, "Development") ctx.actions.expand_template( template = ctx.file._readme_template, output = gem_readme, substitutions = { "{gem_authorship}": _markdown_ul(ctx.attr.gem_authors), - "{gem_dependencies}": _dependencies, + "{gem_runtime_dependencies}": _dependencies, "{gem_description}": ctx.attr.gem_description if ctx.attr.gem_description else ctx.attr.gem_summary, "{gem_name}": ctx.attr.gem_name, - "{gem_title}": _gem_title, "{gem_summary}": ctx.attr.gem_summary, + "{gem_title}": _gem_title, "{gem_version}": ctx.attr.gem_version, }, ) @@ -130,7 +130,7 @@ def _gem_impl(ctx): ctx = ctx, gem_author_emails = ctx.attr.gem_author_emails, gem_authors = ctx.attr.gem_authors, - gem_dependencies = ctx.attr.gem_dependencies, + gem_runtime_dependencies = ctx.attr.gem_runtime_dependencies, gem_description = ctx.attr.gem_description, gem_development_dependencies = ctx.attr.gem_development_dependencies, gem_homepage = ctx.attr.gem_homepage, @@ -146,14 +146,20 @@ gemspec = rule( provides = [DefaultInfo, RubyGem], ) -def gem(name, gem_name, gem_version, srcs = [], **kwargs): +def gem( + name, + gem_name, + gem_version, + srcs, + **kwargs): _zip_name = "%s-%s" % (gem_name, gem_version) - _gemspec_name = name + "_gemspec" + _gemspec_name = name + ".gemspec" gemspec( name = _gemspec_name, gem_name = gem_name, gem_version = gem_version, + srcs = srcs, **kwargs ) diff --git a/ruby/private/gemspec/gemspec_template.tpl b/ruby/private/gemspec/gemspec_template.tpl index ebe27a3..7c0da02 100644 --- a/ruby/private/gemspec/gemspec_template.tpl +++ b/ruby/private/gemspec/gemspec_template.tpl @@ -19,7 +19,7 @@ Gem::Specification.new do |spec| spec.required_ruby_version = '>= 2.3' - {gem_dependencies} + {gem_runtime_dependencies} {gem_development_dependencies} end diff --git a/ruby/private/gemspec/readme_template.tpl b/ruby/private/gemspec/readme_template.tpl index 1b867fb..33b71d8 100644 --- a/ruby/private/gemspec/readme_template.tpl +++ b/ruby/private/gemspec/readme_template.tpl @@ -1,4 +1,6 @@ -# {gem_title} (Version: {gem_version}) +# {gem_title} + +## Version {gem_version} > NOTE: You are reading the auto-generated README for Gem {gem_name}. @@ -12,7 +14,7 @@ ## Dependencies -{gem_dependencies} +{gem_runtime_dependencies} ## Author(s) diff --git a/ruby/private/providers.bzl b/ruby/private/providers.bzl index 80fb6ca..33dbe1e 100644 --- a/ruby/private/providers.bzl +++ b/ruby/private/providers.bzl @@ -21,7 +21,7 @@ RubyGem = provider( "ctx", "gem_author_emails", "gem_authors", - "gem_dependencies", + "gem_runtime_dependencies", "gem_description", "gem_development_dependencies", "gem_homepage", diff --git a/ruby/private/tools/deprecations.bzl b/ruby/private/tools/deprecations.bzl deleted file mode 100644 index b77f3af..0000000 --- a/ruby/private/tools/deprecations.bzl +++ /dev/null @@ -1,12 +0,0 @@ -def deprecated_attribute( - ctx, - old_attribute_name, - new_attribute_name = None, - action = None): - if getattr(ctx.attr, old_attribute_name) != None: - if action != None: - print("Attribute \"%s\" is deprecated — \"%s\"" % (old_attribute_name, action)) - elif new_attribute_name != None: - print("Attribute \"%s\" is deprecated in favor of \"%s\"" % (old_attribute_name, new_attribute_name)) - else: - print("Attribute \"%s\" is deprecated. Please do not use it." % (old_attribute_name)) From 12d74cd2d5d25da199da713f80895b411f5cbe1c Mon Sep 17 00:00:00 2001 From: Konstantin Gredeskoul Date: Mon, 2 Mar 2020 16:07:47 -0800 Subject: [PATCH 19/19] Adding CHANGELOG and VERSION.yml file. Bump version to 0.3.0 --- CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++ VERSION.yml | 5 +++++ 2 files changed, 37 insertions(+) create mode 100644 CHANGELOG.md create mode 100644 VERSION.yml diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4e627bc --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,32 @@ +# 0.3.0 / 2020-03-02 + +[Total Changes since v0.2.0](https://github.com/bazelruby/rules_ruby/compare/v0.2.0...v0.3.0) + +**2,221 additions and 631 deletions in 71 changed files.** + +## Backwards Incompatible Changes + +* Main workspace has been renamed from `bazelruby_ruby_rules` to `bazelruby_rules_ruby` +* Global function `ruby_register_toolchains` has been renamed to `rules_ruby_select_sdk` +* Global function `ruby_rules_dependencies` has been renamed to `rules_ruby_dependencies` +* `bundle_install` has been removed in favor of `ruby_bundle`. + +## Other Changes + +* Introduced `ruby_gem` rule for packaging Ruby sources into a RubyGem-compatible zip file. Note, the resulting file has `.zip` extension. +* Introduced `ruby_rubocop` rule for running rubocop in analysis mode or auto-correcting mode. +* Added an example gem workspace under `examples/example-gem` +* Default ruby used is now 2.7.0. We also now allow 2.7.0 to be built by Bazel. +* Bazelisk has been updated to 1.3.0 +* Updated Bazel version from 2.0.0 to 2.1.0 +* Updated gem versions in the Gemfile +* Changed how the `ruby_bundle` pulls gem's folders into the bundle to include additional files. +* Many other small changes, for full list [please see the diff](https://github.com/bazelruby/rules_ruby/compare/v0.2.0...v0.3.0). + +# 0.2.0 / 2020-12-30 + +[Total Changes since v0.1.0](https://github.com/bazelruby/rules_ruby/compare/v0.1.0...v0.2.0) + +# 0.1.0 / 2019-11-20 + +* Initial migration from [Yugui](https://github.com/yugui) rules ruby. \ No newline at end of file diff --git a/VERSION.yml b/VERSION.yml new file mode 100644 index 0000000..f5ab055 --- /dev/null +++ b/VERSION.yml @@ -0,0 +1,5 @@ +rules_ruby: + version: 0.3.0 + + +