Permalink
Switch branches/tags
0-8-stable 0-9-stable 1-0-stable 1-1-stable 1-2-stable 1-3-stable 1-5-stable 1-6-stable 1-7-stable 1-8-stable 1-9-stable 1-10-stable 1-11-stable 1-12-stable 1-13-stable 1-14-stable 1-15-stable 1-16-stable 1-17-stable 1-99-dev 1_13_2_changelog_edit 2-0-dev-archived 2-0-dev 2-0-stable 171 3982-clean-rubylib 4064 aa-implicit-ruby-versions aa-revert-4998 auto autobundle bump_ruby_2.4_in_travis checksum clean_local_test_run colby/bundler-2-docs colby/bundler-2-install-doc colby/remove-bundler-init-default-gemsrb colby/spec-system-bundler colby/test-6689 colby/travis-build-stages colby/update-rubocop-ruby-version config_location default-travis-emails dont_run_git_commands_during_runtime dwr-metadata execless fetchers_fetchers_everywhere fix_missing_gem_dir further_disablement gem_tasks gemfile_hashing gh-pages greysteil/print-errors-to-stderr homu-tmp indirect/deprecation-with-warnings indirect/playbooks issue-3671 master modify_gem_build_warning_spec molinillo new-index old-gem-version parallel_threads platforms refactor-resolver remove-bundle_ruby remove-old-ruby remove-self-hosted-yaml remove_gem_build_warning_exception removing_the_spec_gil resolve-running-ruby retry_network rgb-manpage-additions rubygems22_exts rubymorillo-patch-1 seg-binstubs-shebang seg-bundle-gem-github-source seg-bundle-gem-invalid-constant seg-default-command seg-definition-from-lockfile seg-definition-to-ruby seg-deprecate-github-git-source seg-downloader-error seg-dsl-force-version seg-error-report seg-hot-mess-in-proc-specs seg-implicitly-lock-ruby seg-in-process-tests seg-install-if-eval seg-json-gemfile seg-lockfile-corrupted-no-deadlock seg-no-warning-when-updating-sha-not-version seg-package-all-platforms-without seg-package-install-no-git seg-parallel-travis seg-release-no-changes seg-release-tasks seg-remove-fakeweb seg-split-gem-download-from-install seg-travis-run-against-setup-rg segiddins/bundle-gem-rubocop-option segiddins/bundle-gem-version-file segiddins/fail-gracefully-when-resetting-to-rev-fails segiddins/fix-bundle-exec-bundle-install-git-extension segiddins/gemspec-force-ruby-platform segiddins/log-everything-in-tmp segiddins/sys-install-command segiddins/updater-use-more-filesystem-access sequential_fallback sources spec-for-3896 staging test_default_gems travis-docker try unsupported_sources update-rubocop user_agent user_agent2
Nothing to show
Find file Copy path
323 lines (266 sloc) 11 KB
# frozen_string_literal: true
module Bundler
class Runtime
include SharedHelpers
def initialize(root, definition)
@root = root
@definition = definition
end
def setup(*groups)
@definition.ensure_equivalent_gemfile_and_lockfile if Bundler.frozen_bundle?
groups.map!(&:to_sym)
# Has to happen first
clean_load_path
specs = groups.any? ? @definition.specs_for(groups) : requested_specs
SharedHelpers.set_bundle_environment
Bundler.rubygems.replace_entrypoints(specs)
# Activate the specs
load_paths = specs.map do |spec|
unless spec.loaded_from
raise GemNotFound, "#{spec.full_name} is missing. Run `bundle install` to get it."
end
check_for_activated_spec!(spec)
Bundler.rubygems.mark_loaded(spec)
spec.load_paths.reject {|path| $LOAD_PATH.include?(path) }
end.reverse.flatten
# See Gem::Specification#add_self_to_load_path (since RubyGems 1.8)
if insert_index = Bundler.rubygems.load_path_insert_index
# Gem directories must come after -I and ENV['RUBYLIB']
$LOAD_PATH.insert(insert_index, *load_paths)
else
# We are probably testing in core, -I and RUBYLIB don't apply
$LOAD_PATH.unshift(*load_paths)
end
setup_manpath
lock(:preserve_unknown_sections => true)
self
end
REQUIRE_ERRORS = [
/^no such file to load -- (.+)$/i,
/^Missing \w+ (?:file\s*)?([^\s]+.rb)$/i,
/^Missing API definition file in (.+)$/i,
/^cannot load such file -- (.+)$/i,
/^dlopen\([^)]*\): Library not loaded: (.+)$/i,
].freeze
def require(*groups)
groups.map!(&:to_sym)
groups = [:default] if groups.empty?
@definition.dependencies.each do |dep|
# Skip the dependency if it is not in any of the requested groups, or
# not for the current platform, or doesn't match the gem constraints.
next unless (dep.groups & groups).any? && dep.should_include?
required_file = nil
begin
# Loop through all the specified autorequires for the
# dependency. If there are none, use the dependency's name
# as the autorequire.
Array(dep.autorequire || dep.name).each do |file|
# Allow `require: true` as an alias for `require: <name>`
file = dep.name if file == true
required_file = file
begin
Kernel.require file
rescue RuntimeError => e
raise e if e.is_a?(LoadError) # we handle this a little later
raise Bundler::GemRequireError.new e,
"There was an error while trying to load the gem '#{file}'."
end
end
rescue LoadError => e
REQUIRE_ERRORS.find {|r| r =~ e.message }
raise if dep.autorequire || $1 != required_file
if dep.autorequire.nil? && dep.name.include?("-")
begin
namespaced_file = dep.name.tr("-", "/")
Kernel.require namespaced_file
rescue LoadError => e
REQUIRE_ERRORS.find {|r| r =~ e.message }
raise if $1 != namespaced_file
end
end
end
end
end
def self.definition_method(meth)
define_method(meth) do
raise ArgumentError, "no definition when calling Runtime##{meth}" unless @definition
@definition.send(meth)
end
end
private_class_method :definition_method
definition_method :requested_specs
definition_method :specs
definition_method :dependencies
definition_method :current_dependencies
definition_method :requires
def lock(opts = {})
return if @definition.nothing_changed? && !@definition.unlocking?
@definition.lock(Bundler.default_lockfile, opts[:preserve_unknown_sections])
end
alias_method :gems, :specs
def cache(custom_path = nil)
cache_path = Bundler.app_cache(custom_path)
SharedHelpers.filesystem_access(cache_path) do |p|
FileUtils.mkdir_p(p)
end unless File.exist?(cache_path)
Bundler.ui.info "Updating files in #{Bundler.settings.app_cache_path}"
specs_to_cache = Bundler.settings[:cache_all_platforms] ? @definition.resolve.materialized_for_all_platforms : specs
specs_to_cache.each do |spec|
next if spec.name == "bundler"
next if spec.source.is_a?(Source::Gemspec)
spec.source.send(:fetch_gem, spec) if Bundler.settings[:cache_all_platforms] && spec.source.respond_to?(:fetch_gem, true)
spec.source.cache(spec, custom_path) if spec.source.respond_to?(:cache)
end
Dir[cache_path.join("*/.git")].each do |git_dir|
FileUtils.rm_rf(git_dir)
FileUtils.touch(File.expand_path("../.bundlecache", git_dir))
end
prune_cache(cache_path) unless Bundler.settings[:no_prune]
end
def prune_cache(cache_path)
SharedHelpers.filesystem_access(cache_path) do |p|
FileUtils.mkdir_p(p)
end unless File.exist?(cache_path)
resolve = @definition.resolve
prune_gem_cache(resolve, cache_path)
prune_git_and_path_cache(resolve, cache_path)
end
def clean(dry_run = false)
gem_bins = Dir["#{Gem.dir}/bin/*"]
git_dirs = Dir["#{Gem.dir}/bundler/gems/*"]
git_cache_dirs = Dir["#{Gem.dir}/cache/bundler/git/*"]
gem_dirs = Dir["#{Gem.dir}/gems/*"]
gem_files = Dir["#{Gem.dir}/cache/*.gem"]
gemspec_files = Dir["#{Gem.dir}/specifications/*.gemspec"]
extension_dirs = Dir["#{Gem.dir}/extensions/*/*/*"]
spec_gem_paths = []
# need to keep git sources around
spec_git_paths = @definition.spec_git_paths
spec_git_cache_dirs = []
spec_gem_executables = []
spec_cache_paths = []
spec_gemspec_paths = []
spec_extension_paths = []
specs.each do |spec|
spec_gem_paths << spec.full_gem_path
# need to check here in case gems are nested like for the rails git repo
md = %r{(.+bundler/gems/.+-[a-f0-9]{7,12})}.match(spec.full_gem_path)
spec_git_paths << md[1] if md
spec_gem_executables << spec.executables.collect do |executable|
e = "#{Bundler.rubygems.gem_bindir}/#{executable}"
[e, "#{e}.bat"]
end
spec_cache_paths << spec.cache_file
spec_gemspec_paths << spec.spec_file
spec_extension_paths << spec.extension_dir if spec.respond_to?(:extension_dir)
spec_git_cache_dirs << spec.source.cache_path.to_s if spec.source.is_a?(Bundler::Source::Git)
end
spec_gem_paths.uniq!
spec_gem_executables.flatten!
stale_gem_bins = gem_bins - spec_gem_executables
stale_git_dirs = git_dirs - spec_git_paths - ["#{Gem.dir}/bundler/gems/extensions"]
stale_git_cache_dirs = git_cache_dirs - spec_git_cache_dirs
stale_gem_dirs = gem_dirs - spec_gem_paths
stale_gem_files = gem_files - spec_cache_paths
stale_gemspec_files = gemspec_files - spec_gemspec_paths
stale_extension_dirs = extension_dirs - spec_extension_paths
removed_stale_gem_dirs = stale_gem_dirs.collect {|dir| remove_dir(dir, dry_run) }
removed_stale_git_dirs = stale_git_dirs.collect {|dir| remove_dir(dir, dry_run) }
output = removed_stale_gem_dirs + removed_stale_git_dirs
unless dry_run
stale_files = stale_gem_bins + stale_gem_files + stale_gemspec_files
stale_files.each do |file|
SharedHelpers.filesystem_access(File.dirname(file)) do |_p|
FileUtils.rm(file) if File.exist?(file)
end
end
stale_dirs = stale_git_cache_dirs + stale_extension_dirs
stale_dirs.each do |stale_dir|
SharedHelpers.filesystem_access(stale_dir) do |dir|
FileUtils.rm_rf(dir) if File.exist?(dir)
end
end
end
output
end
private
def prune_gem_cache(resolve, cache_path)
cached = Dir["#{cache_path}/*.gem"]
cached = cached.delete_if do |path|
spec = Bundler.rubygems.spec_from_gem path
resolve.any? do |s|
s.name == spec.name && s.version == spec.version && !s.source.is_a?(Bundler::Source::Git)
end
end
if cached.any?
Bundler.ui.info "Removing outdated .gem files from #{Bundler.settings.app_cache_path}"
cached.each do |path|
Bundler.ui.info " * #{File.basename(path)}"
File.delete(path)
end
end
end
def prune_git_and_path_cache(resolve, cache_path)
cached = Dir["#{cache_path}/*/.bundlecache"]
cached = cached.delete_if do |path|
name = File.basename(File.dirname(path))
resolve.any? do |s|
source = s.source
source.respond_to?(:app_cache_dirname) && source.app_cache_dirname == name
end
end
if cached.any?
Bundler.ui.info "Removing outdated git and path gems from #{Bundler.settings.app_cache_path}"
cached.each do |path|
path = File.dirname(path)
Bundler.ui.info " * #{File.basename(path)}"
FileUtils.rm_rf(path)
end
end
end
def setup_manpath
# Add man/ subdirectories from activated bundles to MANPATH for man(1)
manuals = $LOAD_PATH.map do |path|
man_subdir = path.sub(/lib$/, "man")
man_subdir unless Dir[man_subdir + "/man?/"].empty?
end.compact
return if manuals.empty?
Bundler::SharedHelpers.set_env "MANPATH", manuals.concat(
ENV["MANPATH"].to_s.split(File::PATH_SEPARATOR)
).uniq.join(File::PATH_SEPARATOR)
end
def remove_dir(dir, dry_run)
full_name = Pathname.new(dir).basename.to_s
parts = full_name.split("-")
name = parts[0..-2].join("-")
version = parts.last
output = "#{name} (#{version})"
if dry_run
Bundler.ui.info "Would have removed #{output}"
else
Bundler.ui.info "Removing #{output}"
FileUtils.rm_rf(dir)
end
output
end
def check_for_activated_spec!(spec)
return unless activated_spec = Bundler.rubygems.loaded_specs(spec.name)
return if activated_spec.version == spec.version
suggestion = if Bundler.rubygems.spec_default_gem?(activated_spec)
"Since #{spec.name} is a default gem, you can either remove your dependency on it" \
" or try updating to a newer version of bundler that supports #{spec.name} as a default gem."
else
"Prepending `bundle exec` to your command may solve this."
end
e = Gem::LoadError.new "You have already activated #{activated_spec.name} #{activated_spec.version}, " \
"but your Gemfile requires #{spec.name} #{spec.version}. #{suggestion}"
e.name = spec.name
if e.respond_to?(:requirement=)
e.requirement = Gem::Requirement.new(spec.version.to_s)
else
e.version_requirement = Gem::Requirement.new(spec.version.to_s)
end
raise e
end
end
end