diff options
author | Hiroshi SHIBATA <hsbt@ruby-lang.org> | 2022-05-02 19:42:15 +0900 |
---|---|---|
committer | nagachika <nagachika@ruby-lang.org> | 2022-05-18 10:02:42 +0900 |
commit | 8ba584ff3c085251865c11c5d7deef0ddfc6d0ff (patch) | |
tree | 7574439a57c7ad4f9ee7decf7f8272f48e7fc343 /lib | |
parent | ec5dae0d816261719b0b2f9595c072701c7036e0 (diff) |
Merge RubyGems-3.3.11 and Bundler-2.3.11
Diffstat (limited to 'lib')
26 files changed, 381 insertions, 44 deletions
diff --git a/lib/bundler.rb b/lib/bundler.rb index 0025dee720..47b352177d 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -370,7 +370,7 @@ EOF if env.key?("RUBYLIB") rubylib = env["RUBYLIB"].split(File::PATH_SEPARATOR) - rubylib.delete(File.expand_path("..", __FILE__)) + rubylib.delete(__dir__) env["RUBYLIB"] = rubylib.join(File::PATH_SEPARATOR) end diff --git a/lib/bundler/build_metadata.rb b/lib/bundler/build_metadata.rb index 0846e82e06..8bffb2fae7 100644 --- a/lib/bundler/build_metadata.rb +++ b/lib/bundler/build_metadata.rb @@ -27,7 +27,7 @@ module Bundler # If Bundler has been installed without its .git directory and without a # commit instance variable then we can't determine its commits SHA. - git_dir = File.join(File.expand_path("../../../..", __FILE__), ".git") + git_dir = File.expand_path("../../../.git", __dir__) if File.directory?(git_dir) return @git_commit_sha = Dir.chdir(git_dir) { `git rev-parse --short HEAD`.strip.freeze } end diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 922ba469a4..e1c284130b 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -610,7 +610,7 @@ module Bundler private :gem def self.source_root - File.expand_path(File.join(File.dirname(__FILE__), "templates")) + File.expand_path("templates", __dir__) end desc "clean [OPTIONS]", "Cleans up unused gems in your bundler directory", :hide => true diff --git a/lib/bundler/cli/info.rb b/lib/bundler/cli/info.rb index 38bc008cb5..0545ce8c75 100644 --- a/lib/bundler/cli/info.rb +++ b/lib/bundler/cli/info.rb @@ -47,7 +47,7 @@ module Bundler def print_gem_path(spec) name = spec.name if name == "bundler" - path = File.expand_path("../../../..", __FILE__) + path = File.expand_path("../../..", __dir__) else path = spec.full_gem_path if spec.deleted_gem? diff --git a/lib/bundler/cli/init.rb b/lib/bundler/cli/init.rb index d851d02d42..e4f8229c48 100644 --- a/lib/bundler/cli/init.rb +++ b/lib/bundler/cli/init.rb @@ -32,7 +32,7 @@ module Bundler file << spec.to_gemfile end else - FileUtils.cp(File.expand_path("../../templates/#{gemfile}", __FILE__), gemfile) + FileUtils.cp(File.expand_path("../templates/#{gemfile}", __dir__), gemfile) end puts "Writing new #{gemfile} to #{SharedHelpers.pwd}/#{gemfile}" diff --git a/lib/bundler/cli/show.rb b/lib/bundler/cli/show.rb index 5eaaba1ef7..2df13db1fa 100644 --- a/lib/bundler/cli/show.rb +++ b/lib/bundler/cli/show.rb @@ -18,7 +18,7 @@ module Bundler if gem_name if gem_name == "bundler" - path = File.expand_path("../../../..", __FILE__) + path = File.expand_path("../../..", __dir__) else spec = Bundler::CLI::Common.select_spec(gem_name, :regex_match) return unless spec diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb index 61bf1e06d4..ca9df4a21e 100644 --- a/lib/bundler/installer.rb +++ b/lib/bundler/installer.rb @@ -119,7 +119,7 @@ module Bundler relative_gemfile_path = relative_gemfile_path ruby_command = Thor::Util.ruby_command ruby_command = ruby_command - template_path = File.expand_path("../templates/Executable", __FILE__) + template_path = File.expand_path("templates/Executable", __dir__) if spec.name == "bundler" template_path += ".bundler" spec.executables = %(bundle) @@ -172,7 +172,7 @@ module Bundler end standalone_path = Bundler.root.join(path).relative_path_from(bin_path) standalone_path = standalone_path - template = File.read(File.expand_path("../templates/Executable.standalone", __FILE__)) + template = File.read(File.expand_path("templates/Executable.standalone", __dir__)) ruby_command = Thor::Util.ruby_command ruby_command = ruby_command diff --git a/lib/bundler/man/bundle-config.1 b/lib/bundler/man/bundle-config.1 index c84a2b106e..d657910e83 100644 --- a/lib/bundler/man/bundle-config.1 +++ b/lib/bundler/man/bundle-config.1 @@ -36,13 +36,13 @@ Executing \fBbundle config list\fR with will print a list of all bundler configu Executing \fBbundle config get <name>\fR will print the value of that configuration setting, and where it was set\. . .P -Executing \fBbundle config set <name> <value>\fR will set that configuration to the value specified for all bundles executed as the current user\. The configuration will be stored in \fB~/\.bundle/config\fR\. If \fIname\fR already is set, \fIname\fR will be overridden and user will be warned\. +Executing \fBbundle config set <name> <value>\fR defaults to setting \fBlocal\fR configuration if executing from within a local application, otherwise it will set \fBglobal\fR configuration\. See \fB\-\-local\fR and \fB\-\-global\fR options below\. . .P -Executing \fBbundle config set \-\-global <name> <value>\fR works the same as above\. +Executing \fBbundle config set \-\-local <name> <value>\fR will set that configuration in the directory for the local application\. The configuration will be stored in \fB<project_root>/\.bundle/config\fR\. If \fBBUNDLE_APP_CONFIG\fR is set, the configuration will be stored in \fB$BUNDLE_APP_CONFIG/config\fR\. . .P -Executing \fBbundle config set \-\-local <name> <value>\fR will set that configuration in the directory for the local application\. The configuration will be stored in \fB<project_root>/\.bundle/config\fR\. If \fBBUNDLE_APP_CONFIG\fR is set, the configuration will be stored in \fB$BUNDLE_APP_CONFIG/config\fR\. +Executing \fBbundle config set \-\-global <name> <value>\fR will set that configuration to the value specified for all bundles executed as the current user\. The configuration will be stored in \fB~/\.bundle/config\fR\. If \fIname\fR already is set, \fIname\fR will be overridden and user will be warned\. . .P Executing \fBbundle config unset <name>\fR will delete the configuration in both local and global sources\. diff --git a/lib/bundler/man/bundle-config.1.ronn b/lib/bundler/man/bundle-config.1.ronn index e0533e05be..7d1cb271a9 100644 --- a/lib/bundler/man/bundle-config.1.ronn +++ b/lib/bundler/man/bundle-config.1.ronn @@ -23,18 +23,20 @@ was set. Executing `bundle config get <name>` will print the value of that configuration setting, and where it was set. -Executing `bundle config set <name> <value>` will set that configuration to the -value specified for all bundles executed as the current user. The configuration -will be stored in `~/.bundle/config`. If <name> already is set, <name> will be -overridden and user will be warned. - -Executing `bundle config set --global <name> <value>` works the same as above. +Executing `bundle config set <name> <value>` defaults to setting `local` +configuration if executing from within a local application, otherwise it will +set `global` configuration. See `--local` and `--global` options below. Executing `bundle config set --local <name> <value>` will set that configuration in the directory for the local application. The configuration will be stored in `<project_root>/.bundle/config`. If `BUNDLE_APP_CONFIG` is set, the configuration will be stored in `$BUNDLE_APP_CONFIG/config`. +Executing `bundle config set --global <name> <value>` will set that +configuration to the value specified for all bundles executed as the current +user. The configuration will be stored in `~/.bundle/config`. If <name> already +is set, <name> will be overridden and user will be warned. + Executing `bundle config unset <name>` will delete the configuration in both local and global sources. diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb index 8b476c9135..ffdbdee503 100644 --- a/lib/bundler/shared_helpers.rb +++ b/lib/bundler/shared_helpers.rb @@ -274,10 +274,10 @@ module Bundler def set_bundle_variables # bundler exe & lib folders have same root folder, typical gem installation - exe_file = File.expand_path("../../../exe/bundle", __FILE__) + exe_file = File.expand_path("../../exe/bundle", __dir__) # for Ruby core repository testing - exe_file = File.expand_path("../../../libexec/bundle", __FILE__) unless File.exist?(exe_file) + exe_file = File.expand_path("../../libexec/bundle", __dir__) unless File.exist?(exe_file) # bundler is a default gem, exe path is separate exe_file = Bundler.rubygems.bin_path("bundler", "bundle", VERSION) unless File.exist?(exe_file) @@ -309,7 +309,7 @@ module Bundler end def bundler_ruby_lib - resolve_path File.expand_path("../..", __FILE__) + File.expand_path("..", __dir__) end def clean_load_path @@ -325,7 +325,7 @@ module Bundler def resolve_path(path) expanded = File.expand_path(path) - return expanded unless File.respond_to?(:realpath) && File.exist?(expanded) + return expanded unless File.exist?(expanded) File.realpath(expanded) end diff --git a/lib/bundler/source/metadata.rb b/lib/bundler/source/metadata.rb index 8bdbaa5527..524afd750a 100644 --- a/lib/bundler/source/metadata.rb +++ b/lib/bundler/source/metadata.rb @@ -22,7 +22,7 @@ module Bundler s.summary = "The best way to manage your application's dependencies" s.executables = %w[bundle] # can't point to the actual gemspec or else the require paths will be wrong - s.loaded_from = File.expand_path("..", __FILE__) + s.loaded_from = __dir__ end if local_spec = Bundler.rubygems.find_bundler(VERSION) diff --git a/lib/bundler/templates/Executable b/lib/bundler/templates/Executable index 3e8d5b317a..f6487e3c89 100644 --- a/lib/bundler/templates/Executable +++ b/lib/bundler/templates/Executable @@ -8,11 +8,9 @@ # this file is here to facilitate running it. # -require "pathname" -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../<%= relative_gemfile_path %>", - Pathname.new(__FILE__).realpath) +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("<%= relative_gemfile_path %>", __dir__) -bundle_binstub = File.expand_path("../bundle", __FILE__) +bundle_binstub = File.expand_path("bundle", __dir__) if File.file?(bundle_binstub) if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/ diff --git a/lib/bundler/templates/Executable.bundler b/lib/bundler/templates/Executable.bundler index 6bb5c51090..51650c7664 100644 --- a/lib/bundler/templates/Executable.bundler +++ b/lib/bundler/templates/Executable.bundler @@ -41,7 +41,7 @@ m = Module.new do gemfile = ENV["BUNDLE_GEMFILE"] return gemfile if gemfile && !gemfile.empty? - File.expand_path("../<%= relative_gemfile_path %>", __FILE__) + File.expand_path("<%= relative_gemfile_path %>", __dir__) end def lockfile diff --git a/lib/bundler/templates/Executable.standalone b/lib/bundler/templates/Executable.standalone index 4bf0753f44..d591e3fc04 100644 --- a/lib/bundler/templates/Executable.standalone +++ b/lib/bundler/templates/Executable.standalone @@ -6,9 +6,7 @@ # this file is here to facilitate running it. # -require "pathname" -path = Pathname.new(__FILE__) -$:.unshift File.expand_path "../<%= standalone_path %>", path.realpath +$:.unshift File.expand_path "<%= standalone_path %>", __dir__ require "bundler/setup" -load File.expand_path "../<%= executable_path %>", path.realpath +load File.expand_path "<%= executable_path %>", __dir__ diff --git a/lib/bundler/templates/newgem/github/workflows/main.yml.tt b/lib/bundler/templates/newgem/github/workflows/main.yml.tt index 6570d177af..1ff4b58b7b 100644 --- a/lib/bundler/templates/newgem/github/workflows/main.yml.tt +++ b/lib/bundler/templates/newgem/github/workflows/main.yml.tt @@ -17,7 +17,7 @@ jobs: - '<%= RUBY_VERSION %>' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: diff --git a/lib/bundler/templates/newgem/newgem.gemspec.tt b/lib/bundler/templates/newgem/newgem.gemspec.tt index 546a28b78a..ceb2e9b28d 100644 --- a/lib/bundler/templates/newgem/newgem.gemspec.tt +++ b/lib/bundler/templates/newgem/newgem.gemspec.tt @@ -24,7 +24,7 @@ Gem::Specification.new do |spec| # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. - spec.files = Dir.chdir(File.expand_path(__dir__)) do + spec.files = Dir.chdir(__dir__) do `git ls-files -z`.split("\x0").reject do |f| (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)}) end diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb index 6a5af6c296..f8d69d73e6 100644 --- a/lib/bundler/version.rb +++ b/lib/bundler/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module Bundler - VERSION = "2.3.10".freeze + VERSION = "2.3.11".freeze def self.bundler_major_version @bundler_major_version ||= VERSION.split(".").first.to_i diff --git a/lib/rubygems.rb b/lib/rubygems.rb index 68c0781a50..a99281fef5 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -8,7 +8,7 @@ require 'rbconfig' module Gem - VERSION = "3.3.10".freeze + VERSION = "3.3.11".freeze end # Must be first since it unloads the prelude from 1.9.2 @@ -112,7 +112,7 @@ require_relative 'rubygems/errors' # -The RubyGems Team module Gem - RUBYGEMS_DIR = File.dirname File.expand_path(__FILE__) + RUBYGEMS_DIR = __dir__ # Taint support is deprecated in Ruby 2.7. # This allows switching ".untaint" to ".tap(&Gem::UNTAINT)", diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb index 01714f0342..7b6890013c 100644 --- a/lib/rubygems/commands/setup_command.rb +++ b/lib/rubygems/commands/setup_command.rb @@ -341,7 +341,7 @@ By default, this RubyGems will install gem as: fake_spec = Gem::Specification.new 'rubygems', Gem::VERSION def fake_spec.full_gem_path - File.expand_path '../../../..', __FILE__ + File.expand_path '../../..', __dir__ end generate_ri = options[:document].include? 'ri' diff --git a/lib/rubygems/ext.rb b/lib/rubygems/ext.rb index bdd5bd9d82..59fd830437 100644 --- a/lib/rubygems/ext.rb +++ b/lib/rubygems/ext.rb @@ -16,3 +16,4 @@ require_relative 'ext/configure_builder' require_relative 'ext/ext_conf_builder' require_relative 'ext/rake_builder' require_relative 'ext/cmake_builder' +require_relative 'ext/cargo_builder' diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb index 6d32be6515..d9aa68c067 100644 --- a/lib/rubygems/ext/builder.rb +++ b/lib/rubygems/ext/builder.rb @@ -123,6 +123,9 @@ class Gem::Ext::Builder Gem::Ext::RakeBuilder when /CMakeLists.txt/ then Gem::Ext::CmakeBuilder + when /Cargo.toml/ then + # We use the spec name here to ensure we invoke the correct init function later + Gem::Ext::CargoBuilder.new(@spec) else build_error("No builder for extension '#{extension}'") end diff --git a/lib/rubygems/ext/cargo_builder.rb b/lib/rubygems/ext/cargo_builder.rb new file mode 100644 index 0000000000..4c16063224 --- /dev/null +++ b/lib/rubygems/ext/cargo_builder.rb @@ -0,0 +1,305 @@ +# frozen_string_literal: true + +# This class is used by rubygems to build Rust extensions. It is a thin-wrapper +# over the `cargo rustc` command which takes care of building Rust code in a way +# that Ruby can use. +class Gem::Ext::CargoBuilder < Gem::Ext::Builder + attr_reader :spec + + def initialize(spec) + @spec = spec + end + + def build(_extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd) + require "rubygems/command" + require "fileutils" + require "shellwords" + + build_crate(dest_path, results, args, cargo_dir) + ext_path = rename_cdylib_for_ruby_compatibility(dest_path) + finalize_directory(ext_path, dest_path, lib_dir, cargo_dir) + results + end + + private + + def build_crate(dest_path, results, args, cargo_dir) + manifest = File.join(cargo_dir, "Cargo.toml") + + given_ruby_static = ENV["RUBY_STATIC"] + + ENV["RUBY_STATIC"] = "true" if ruby_static? && !given_ruby_static + + cargo = ENV.fetch("CARGO", "cargo") + + cmd = [] + cmd += [cargo, "rustc"] + cmd += ["--target-dir", dest_path] + cmd += ["--manifest-path", manifest] + cmd += ["--lib", "--release", "--locked"] + cmd += ["--"] + cmd += [*cargo_rustc_args(dest_path)] + cmd += Gem::Command.build_args + cmd += args + + self.class.run cmd, results, self.class.class_name, cargo_dir + results + ensure + ENV["RUBY_STATIC"] = given_ruby_static + end + + def cargo_rustc_args(dest_dir) + [ + *linker_args, + *mkmf_libpath, + *rustc_dynamic_linker_flags(dest_dir), + *rustc_lib_flags(dest_dir), + *platform_specific_rustc_args(dest_dir), + *debug_flags, + ] + end + + def platform_specific_rustc_args(dest_dir, flags = []) + if mingw_target? + # On mingw platforms, mkmf adds libruby to the linker flags + flags += libruby_args(dest_dir) + + # Make sure ALSR is used on mingw + # see https://github.com/rust-lang/rust/pull/75406/files + flags += ["-C", "link-arg=-Wl,--dynamicbase"] + flags += ["-C", "link-arg=-Wl,--disable-auto-image-base"] + + # If the gem is installed on a host with build tools installed, but is + # run on one that isn't the missing libraries will cause the extension + # to fail on start. + flags += ["-C", "link-arg=-static-libgcc"] + end + + flags + end + + # We want to use the same linker that Ruby uses, so that the linker flags from + # mkmf work properly. + def linker_args + # Have to handle CC="cl /nologo" on mswin + cc_flag = Shellwords.split(makefile_config("CC")) + linker = cc_flag.shift + link_args = cc_flag.flat_map {|a| ["-C", "link-arg=#{a}"] } + + ["-C", "linker=#{linker}", *link_args] + end + + def libruby_args(dest_dir) + libs = makefile_config(ruby_static? ? "LIBRUBYARG_STATIC" : "LIBRUBYARG_SHARED") + raw_libs = Shellwords.split(libs) + raw_libs.flat_map {|l| ldflag_to_link_modifier(l, dest_dir) } + end + + def ruby_static? + return true if %w[1 true].include?(ENV["RUBY_STATIC"]) + + makefile_config("ENABLE_SHARED") == "no" + end + + # Ruby expects the dylib to follow a file name convention for loading + def rename_cdylib_for_ruby_compatibility(dest_path) + dylib_path = validate_cargo_build!(dest_path) + dlext_name = "#{spec.name}.#{makefile_config("DLEXT")}" + new_name = dylib_path.gsub(File.basename(dylib_path), dlext_name) + FileUtils.cp(dylib_path, new_name) + new_name + end + + def validate_cargo_build!(dir) + prefix = so_ext == "dll" ? "" : "lib" + dylib_path = File.join(dir, "release", "#{prefix}#{cargo_crate_name}.#{so_ext}") + + raise DylibNotFoundError, dir unless File.exist?(dylib_path) + + dylib_path + end + + def cargo_crate_name + spec.metadata.fetch('cargo_crate_name', spec.name).tr('-', '_') + end + + def rustc_dynamic_linker_flags(dest_dir) + split_flags("DLDFLAGS") + .map {|arg| maybe_resolve_ldflag_variable(arg, dest_dir) } + .compact + .flat_map {|arg| ldflag_to_link_modifier(arg, dest_dir) } + end + + def rustc_lib_flags(dest_dir) + split_flags("LIBS").flat_map {|arg| ldflag_to_link_modifier(arg, dest_dir) } + end + + def split_flags(var) + Shellwords.split(RbConfig::CONFIG.fetch(var, "")) + end + + def ldflag_to_link_modifier(arg, dest_dir) + flag = arg[0..1] + val = arg[2..-1] + + case flag + when "-L" then ["-L", "native=#{val}"] + when "-l" then ["-l", val.to_s] + when "-F" then ["-l", "framework=#{val}"] + else ["-C", "link_arg=#{arg}"] + end + end + + def link_flag(link_name) + # These are provided by the CRT with MSVC + # @see https://github.com/rust-lang/pkg-config-rs/blob/49a4ac189aafa365167c72e8e503565a7c2697c2/src/lib.rs#L622 + return [] if msvc_target? && ["m", "c", "pthread"].include?(link_name) + + if link_name.include?("ruby") + # Specify the lib kind and give it the name "ruby" for linking + kind = ruby_static? ? "static" : "dylib" + + ["-l", "#{kind}=ruby:#{link_name}"] + else + ["-l", link_name] + end + end + + def msvc_target? + makefile_config("target_os").include?("msvc") + end + + def darwin_target? + makefile_config("target_os").include?("darwin") + end + + def mingw_target? + makefile_config("target_os").include?("mingw") + end + + def win_target? + target_platform = RbConfig::CONFIG["target_os"] + !!Gem::WIN_PATTERNS.find {|r| target_platform =~ r } + end + + # Intepolate substition vars in the arg (i.e. $(DEFFILE)) + def maybe_resolve_ldflag_variable(input_arg, dest_dir) + str = input_arg.gsub(/\$\((\w+)\)/) do |var_name| + case var_name + # On windows, it is assumed that mkmf has setup an exports file for the + # extension, so we have to to create one ourselves. + when "DEFFILE" + write_deffile(dest_dir) + else + RbConfig::CONFIG[var_name] + end + end.strip + + str == "" ? nil : str + end + + def write_deffile(dest_dir) + deffile_path = File.join(dest_dir, "#{spec.name}-#{RbConfig::CONFIG["arch"]}.def") + export_prefix = makefile_config("EXPORT_PREFIX") || "" + + File.open(deffile_path, "w") do |f| + f.puts "EXPORTS" + f.puts "#{export_prefix.strip}Init_#{spec.name}" + end + + deffile_path + end + + # We have to basically reimplement RbConfig::CONFIG['SOEXT'] here to support + # Ruby < 2.5 + # + # @see https://github.com/ruby/ruby/blob/c87c027f18c005460746a74c07cd80ee355b16e4/configure.ac#L3185 + def so_ext + return RbConfig::CONFIG["SOEXT"] if RbConfig::CONFIG.key?("SOEXT") + + if win_target? + "dll" + elsif darwin_target? + "dylib" + else + "so" + end + end + + # Corresponds to $(LIBPATH) in mkmf + def mkmf_libpath + ["-L", "native=#{makefile_config("libdir")}"] + end + + def makefile_config(var_name) + val = RbConfig::MAKEFILE_CONFIG[var_name] + + return unless val + + RbConfig.expand(val.dup) + end + + # Good balance between binary size and debugability + def debug_flags + ["-C", "debuginfo=1"] + end + + # Copied from ExtConfBuilder + def finalize_directory(ext_path, dest_path, lib_dir, extension_dir) + require "fileutils" + require "tempfile" + + begin + tmp_dest = Dir.mktmpdir(".gem.", extension_dir) + + # Some versions of `mktmpdir` return absolute paths, which will break make + # if the paths contain spaces. However, on Ruby 1.9.x on Windows, relative + # paths cause all C extension builds to fail. + # + # As such, we convert to a relative path unless we are using Ruby 1.9.x on + # Windows. This means that when using Ruby 1.9.x on Windows, paths with + # spaces do not work. + # + # Details: https://github.com/rubygems/rubygems/issues/977#issuecomment-171544940 + tmp_dest_relative = get_relative_path(tmp_dest.clone, extension_dir) + + if tmp_dest_relative + full_tmp_dest = File.join(extension_dir, tmp_dest_relative) + + # TODO: remove in RubyGems 3 + if Gem.install_extension_in_lib && lib_dir + FileUtils.mkdir_p lib_dir + FileUtils.cp_r ext_path, lib_dir, remove_destination: true + end + + FileUtils::Entry_.new(full_tmp_dest).traverse do |ent| + destent = ent.class.new(dest_path, ent.rel) + destent.exist? || FileUtils.mv(ent.path, destent.path) + end + end + ensure + FileUtils.rm_rf tmp_dest if tmp_dest + end + end + + def get_relative_path(path, base) + path[0..base.length - 1] = "." if path.start_with?(base) + path + end + + # Error raised when no cdylib artifact was created + class DylibNotFoundError < StandardError + def initialize(dir) + files = Dir.glob(File.join(dir, "**", "*")).map {|f| "- #{f}" }.join "\n" + + super <<~MSG + Dynamic library not found for Rust extension (in #{dir}) + + Make sure you set "crate-type" in Cargo.toml to "cdylib" + + Found files: + #{files} + MSG + end + end +end diff --git a/lib/rubygems/ext/ext_conf_builder.rb b/lib/rubygems/ext/ext_conf_builder.rb index 3ca3463615..3e8aa950c9 100644 --- a/lib/rubygems/ext/ext_conf_builder.rb +++ b/lib/rubygems/ext/ext_conf_builder.rb @@ -39,7 +39,7 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder # workaround for https://github.com/oracle/truffleruby/issues/2115 siteconf_path = RUBY_ENGINE == "truffleruby" ? siteconf.path.dup : siteconf.path require "shellwords" - cmd = Gem.ruby.shellsplit << "-I" << File.expand_path("../../..", __FILE__) << + cmd = Gem.ruby.shellsplit << "-I" << File.expand_path('../..', __dir__) << "-r" << get_relative_path(siteconf_path, extension_dir) << File.basename(extension) cmd.push(*args) diff --git a/lib/rubygems/gemcutter_utilities.rb b/lib/rubygems/gemcutter_utilities.rb index 0968e1a6f9..7cb5fe9448 100644 --- a/lib/rubygems/gemcutter_utilities.rb +++ b/lib/rubygems/gemcutter_utilities.rb @@ -163,12 +163,14 @@ module Gem::GemcutterUtilities key_name = get_key_name(scope) scope_params = get_scope_params(scope) + mfa_params = get_mfa_params(email, password) + all_params = scope_params.merge(mfa_params) response = rubygems_api_request(:post, "api/v1/api_key", sign_in_host, scope: scope) do |request| request.basic_auth email, password request["OTP"] = otp if otp - request.body = URI.encode_www_form({ name: key_name }.merge(scope_params)) + request.body = URI.encode_www_form({ name: key_name }.merge(all_params)) end with_response response do |resp| @@ -219,7 +221,7 @@ module Gem::GemcutterUtilities # +response+ text and no otp provided by options. def set_api_key(host, key) - if host == Gem::DEFAULT_HOST + if default_host? Gem.configuration.rubygems_api_key = key else Gem.configuration.set_api_key host, key @@ -243,7 +245,7 @@ module Gem::GemcutterUtilities end def pretty_host(host) - if Gem::DEFAULT_HOST == host + if default_host? 'RubyGems.org' else host @@ -258,8 +260,8 @@ module Gem::GemcutterUtilities else say "Please select scopes you want to enable for the API key (y/n)" API_SCOPES.each do |scope| - selected = ask "#{scope} [y/N]: " - scope_params[scope] = true if selected =~ /^[yY](es)?$/ + selected = ask_yes_no("#{scope}", false) + scope_params[scope] = true if selected end say "\n" end @@ -267,6 +269,33 @@ module Gem::GemcutterUtilities scope_params end + def default_host? + self.host == Gem::DEFAULT_HOST + end + + def get_mfa_params(email, password) + return {} unless default_host? + + mfa_level = get_user_mfa_level(email, password) + params = {} + if mfa_level == "ui_only" || mfa_level == "ui_and_gem_signin" + selected = ask_yes_no("Would you like to enable MFA for this key? (strongly recommended)") + params["mfa"] = true if selected + end + params + end + + def get_user_mfa_level(email, password) + response = rubygems_api_request(:get, "api/v1/profile/me.yaml") do |request| + request.basic_auth email, password + end + + with_response response do |resp| + body = Gem::SafeYAML.load clean_text(resp.body) + body["mfa"] + end + end + def get_key_name(scope) hostname = Socket.gethostname || "unknown-host" user = ENV["USER"] || ENV["USERNAME"] || "unknown-user" diff --git a/lib/rubygems/request.rb b/lib/rubygems/request.rb index d6100c914b..136b154bee 100644 --- a/lib/rubygems/request.rb +++ b/lib/rubygems/request.rb @@ -39,7 +39,7 @@ class Gem::Request def cert_files; @connection_pool.cert_files; end def self.get_cert_files - pattern = File.expand_path("./ssl_certs/*/*.pem", File.dirname(__FILE__)) + pattern = File.expand_path("./ssl_certs/*/*.pem", __dir__) Dir.glob(pattern) end diff --git a/lib/rubygems/source/git.rb b/lib/rubygems/source/git.rb index cda5aa8073..8d5dc1f69d 100644 --- a/lib/rubygems/source/git.rb +++ b/lib/rubygems/source/git.rb @@ -102,6 +102,7 @@ class Gem::Source::Git < Gem::Source success = system @git, 'reset', '--quiet', '--hard', rev_parse if @need_submodules + require "open3" _, status = Open3.capture2e(@git, 'submodule', 'update', '--quiet', '--init', '--recursive') success &&= status.success? |