Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support for GEM_HOME_SHARED #596

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 21 additions & 0 deletions lib/rubygems.rb
Expand Up @@ -306,6 +306,20 @@ def self.bindir(install_dir=Gem.dir)
Gem.default_bindir
end

##
# The path where gem cache is hold.

def self.cachedir(install_dir=Gem.dir)
# TODO: move to Gem::Dirs
if install_dir.to_s == Gem.user_dir.to_s
File.join Gem.shared_user_dir, 'cache'
elsif Gem.shareddir
File.join Gem.shareddir, 'cache'
else
File.join install_dir, 'cache'
end
end

##
# Reset the +dir+ and +path+ values. The next time +dir+ or +path+
# is requested, the values will be calculated from scratch. This is
Expand Down Expand Up @@ -382,6 +396,11 @@ def self.dir
paths.home
end

def self.shareddir
# TODO: raise "no"
paths.home_shared
end

def self.path
# TODO: raise "no"
paths.path
Expand All @@ -402,6 +421,8 @@ def self.spec_cache_dir

def self.ensure_gem_subdirectories dir = Gem.dir, mode = nil
ensure_subdirectories(dir, mode, REPOSITORY_SUBDIRECTORIES)
ensure_subdirectories(Gem.shared_user_dir, mode, %w[cache]) if dir == Gem.user_dir # :user_install
ensure_subdirectories(Gem.shareddir, mode, %w[cache]) if Gem.shareddir # GEM_HOME_SHARED
end

##
Expand Down
2 changes: 1 addition & 1 deletion lib/rubygems/commands/cleanup_command.rb
Expand Up @@ -146,7 +146,7 @@ def uninstall_dep spec
:version => "= #{spec.version}",
}

uninstall_options[:user_install] = Gem.user_dir == spec.base_dir
uninstall_options[:user_install] = ((Gem.user_dir == spec.base_dir) or (Gem.shared_user_dir == spec.base_dir))

uninstaller = Gem::Uninstaller.new spec.name, uninstall_options

Expand Down
12 changes: 10 additions & 2 deletions lib/rubygems/defaults.rb
Expand Up @@ -67,6 +67,14 @@ def self.user_dir
File.join parts
end

##
# Path for shared gems in the user's home directory

def self.shared_user_dir
parts = [Gem.user_home, '.gem', 'shared']
File.join parts
end

##
# How String Gem paths should be split. Overridable for esoteric platforms.

Expand All @@ -79,9 +87,9 @@ def self.path_separator

def self.default_path
if Gem.user_home && File.exist?(Gem.user_home) then
[user_dir, default_dir]
[ user_dir, shared_user_dir, default_dir ]
else
[default_dir]
[ default_dir ]
end
end

Expand Down
50 changes: 42 additions & 8 deletions lib/rubygems/installer.rb
Expand Up @@ -109,11 +109,7 @@ def initialize(gem, options={})

@package.security_policy = @security_policy

if options[:user_install] and not options[:unpack] then
@gem_home = Gem.user_dir
@bin_dir = Gem.bindir gem_home unless options[:bin_dir]
check_that_user_bin_dir_is_in_path
end
early_check_user_install
end

##
Expand Down Expand Up @@ -769,6 +765,42 @@ def dir
gem_dir.to_s
end

##
# For initialization to detect :user_install,
# can be changed in try_to_share_gems_location

def early_check_user_install
if options[:user_install] and not options[:unpack] then
@gem_home = Gem.user_dir
@bin_dir = Gem.bindir gem_home unless options[:bin_dir]
end
end

##
# Detect if gems can and should be shared between rubies

def try_to_share_gems_location
if options[:user_install] and not options[:unpack] then

if spec.can_be_shared?(minimal_shared_gem_version_required) then
@gem_home = Gem.shared_user_dir
@bin_dir = Gem.bindir gem_home unless options[:bin_dir]
end
check_that_user_bin_dir_is_in_path

elsif Gem.shareddir and not options[:unpack] then
if spec.can_be_shared?(minimal_shared_gem_version_required) then
@gem_home = Gem.shareddir
@bin_dir = Gem.bindir gem_home unless options[:bin_dir]
check_that_user_bin_dir_is_in_path
end
end
end

def minimal_shared_gem_version_required
@minimal_required_version ||= Gem::Version.new('1.8.7')
end

##
# Performs various checks before installing the gem such as the install
# repository is writable and its directories exist, required ruby and
Expand All @@ -779,15 +811,17 @@ def dir
# The dependent check will be skipped this install is ignoring dependencies.

def pre_install_checks
verify_gem_home options[:unpack]

# If we're forcing the install then disable security unless the security
# policy says that we only install signed gems.
@security_policy = nil if
@force and @security_policy and not @security_policy.only_signed

ensure_loadable_spec

try_to_share_gems_location

verify_gem_home options[:unpack]

if options[:install_as_default]
Gem.ensure_default_gem_subdirectories gem_home
else
Expand Down Expand Up @@ -827,7 +861,7 @@ def write_build_info_file
# Writes the .gem file to the cache directory

def write_cache_file
cache_file = File.join gem_home, 'cache', spec.file_name
cache_file = File.join Gem.cachedir(gem_home), spec.file_name

FileUtils.cp @gem, cache_file unless File.exist? cache_file
end
Expand Down
2 changes: 1 addition & 1 deletion lib/rubygems/installer_test_case.rb
Expand Up @@ -105,7 +105,7 @@ def setup
@user_gem = @user_spec.cache_file

@installer = util_installer @spec, @gemhome
@user_installer = util_installer @user_spec, Gem.user_dir, :user
@user_installer = util_installer @user_spec, Gem.shared_user_dir, :user
end

def util_gem_bindir spec = @spec # :nodoc:
Expand Down
16 changes: 16 additions & 0 deletions lib/rubygems/path_support.rb
Expand Up @@ -8,6 +8,10 @@ class Gem::PathSupport
# The default system path for managing Gems.
attr_reader :home

##
# The shared Gems path, only works together with GEM_HOME.
attr_reader :home_shared

##
# Array of paths to search for Gems.
attr_reader :path
Expand All @@ -31,6 +35,10 @@ def initialize(env=ENV)
@home = @home.gsub(File::ALT_SEPARATOR, File::SEPARATOR)
end

if env["GEM_HOME"] || ENV["GEM_HOME"]
@home_shared = env["GEM_HOME_SHARED"] || ENV["GEM_HOME_SHARED"]
end

self.path = env["GEM_PATH"] || ENV["GEM_PATH"]

@spec_cache_dir =
Expand All @@ -49,6 +57,13 @@ def home=(home)
@home = home.to_s
end

##
# Set the Gem home shared directory (as reported by Gem.shareddir).

def home_shared=(home_shared)
@home_shared = home_shared.nil? ? nil : home_shared.to_s
end

##
# Set the Gem search path (as reported by Gem.path).

Expand All @@ -74,6 +89,7 @@ def path=(gpaths)
end

gem_path << @home
gem_path << @home_shared if @home_shared
else
gem_path = Gem.default_path + [@home]

Expand Down
4 changes: 3 additions & 1 deletion lib/rubygems/remote_fetcher.rb
Expand Up @@ -121,10 +121,12 @@ def download(spec, source_uri, install_dir = Gem.dir)
cache_dir =
if Dir.pwd == install_dir then # see fetch_command
install_dir
elsif Gem.shareddir then
File.join Gem.shareddir, "cache"
elsif File.writable? install_dir then
File.join install_dir, "cache"
else
File.join Gem.user_dir, "cache"
File.join Gem.shared_user_dir, "cache"
end

gem_file_name = File.basename spec.cache_file
Expand Down
14 changes: 13 additions & 1 deletion lib/rubygems/specification.rb
Expand Up @@ -1358,12 +1358,24 @@ def build_info_file
File.join build_info_dir, "#{full_name}.info"
end

##
# True if this gem:
# - satisfies the given version
# - has platform ruby
# - has no extensions

def can_be_shared?(minimal_required_version)
Gem::Requirement.new(required_ruby_version).
satisfied_by?(minimal_required_version) &&
platform == 'ruby' && extensions.empty?
end

##
# Returns the full path to the cache directory containing this
# spec's cached gem.

def cache_dir
@cache_dir ||= File.join base_dir, "cache"
@cache_dir ||= Gem.cachedir(base_dir)
end

##
Expand Down
58 changes: 46 additions & 12 deletions lib/rubygems/uninstaller.rb
Expand Up @@ -28,10 +28,21 @@ class Gem::Uninstaller
attr_reader :bin_dir

##
# The gem repository the gem will be installed into
# The user path the gems were installed into,
# can be empty - defaults to gem_home

attr_reader :install_dir

##
# The gem repository the platform gems were installed into

attr_reader :gem_home

##
# The gem repository the shared gems were installed into

attr_reader :gem_home_shared

##
# The Gem::Specification for the gem being uninstalled, only set during
# #uninstall_gem
Expand All @@ -45,7 +56,9 @@ def initialize(gem, options = {})
# TODO document the valid options
@gem = gem
@version = options[:version] || Gem::Requirement.default
@gem_home = File.expand_path(options[:install_dir] || Gem.dir)
@install_dir = options[:install_dir] ? File.expand_path(options[:install_dir]) : nil
@gem_home = File.expand_path(Gem.dir)
@gem_home_shared = Gem.shareddir ? File.expand_path(Gem.shareddir) : ""
@force_executables = options[:executables]
@force_all = options[:all]
@force_ignore = options[:ignore]
Expand All @@ -68,6 +81,14 @@ def initialize(gem, options = {})
@user_install = options[:user_install] unless options[:install_dir]
end

def gem_in_uninstallable_path(base_dir)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restructure this method please!

(
install_dir ? install_dir == base_dir : ( @gem_home == base_dir or @gem_home_shared == base_dir )
) or (
@user_install and (Gem.user_dir == base_dir or Gem.shared_user_dir == base_dir)
)
end

##
# Performs the uninstall of the gem. This removes the spec, the Gem
# directory, and the cached .gem file.
Expand All @@ -92,8 +113,7 @@ def uninstall
end

list, other_repo_specs = list.partition do |spec|
@gem_home == spec.base_dir or
(@user_install and spec.base_dir == Gem.user_dir)
gem_in_uninstallable_path(spec.base_dir)
end

if list.empty? then
Expand Down Expand Up @@ -234,14 +254,7 @@ def remove_all(list)
# uninstalled a gem, it is removed from that list.

def remove(spec)
unless path_ok?(@gem_home, spec) or
(@user_install and path_ok?(Gem.user_dir, spec)) then
e = Gem::GemNotInHomeException.new \
"Gem is not installed in directory #{@gem_home}"
e.spec = spec

raise e
end
paths_ok_or_raise(spec)

raise Gem::FilePermissionError, spec.base_dir unless
File.writable?(spec.base_dir)
Expand Down Expand Up @@ -274,6 +287,27 @@ def remove(spec)
Gem::Specification.remove_spec spec
end

##
# Confirm spec is one of paths that it can be uninstalled from

def paths_ok_or_raise(spec)
unless (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please restructure this conditional!

@install_dir ? path_ok?(@install_dir, spec) : (
path_ok?(@gem_home, spec) or path_ok?(@gem_home_shared, spec)
)
) or (
@user_install and (
path_ok?(Gem.user_dir, spec) or path_ok?(Gem.shared_user_dir, spec)
)
) then
e = Gem::GemNotInHomeException.new \
"Gem is not installed in directory #{@install_dir ? @install_dir : "#{@gem_home_shared} or #{@gem_home}"}"
e.spec = spec

raise e
end
end

##
# Is +spec+ in +gem_dir+?

Expand Down
2 changes: 0 additions & 2 deletions test/rubygems/test_gem.rb
Expand Up @@ -476,7 +476,6 @@ def test_self_path_ENV_PATH

def test_self_path_duplicate
Gem.clear_paths
util_ensure_gem_dirs
dirs = @additional + [@gemhome] + [File.join(@tempdir, 'a')]

ENV['GEM_HOME'] = @gemhome
Expand All @@ -491,7 +490,6 @@ def test_self_path_duplicate
def test_self_path_overlap
Gem.clear_paths

util_ensure_gem_dirs
ENV['GEM_HOME'] = @gemhome
ENV['GEM_PATH'] = @additional.join(File::PATH_SEPARATOR)

Expand Down
4 changes: 2 additions & 2 deletions test/rubygems/test_gem_commands_cleanup_command.rb
Expand Up @@ -81,7 +81,7 @@ def test_execute_all_user
@a_1_1 = quick_spec 'a', '1.1'
@a_1_1 = install_gem_user @a_1_1 # pick up user install path

Gem::Specification.dirs = [Gem.dir, Gem.user_dir]
Gem::Specification.dirs = [Gem.dir, Gem.shared_user_dir]

assert_path_exists @a_1.gem_dir
assert_path_exists @a_1_1.gem_dir
Expand All @@ -100,7 +100,7 @@ def test_execute_all_user_no_sudo
@a_1_1 = quick_spec 'a', '1.1'
@a_1_1 = install_gem_user @a_1_1 # pick up user install path

Gem::Specification.dirs = [Gem.dir, Gem.user_dir]
Gem::Specification.dirs = [Gem.dir, Gem.shared_user_dir]

assert_path_exists @a_1.gem_dir
assert_path_exists @a_1_1.gem_dir
Expand Down