diff options
Diffstat (limited to 'lib/rubygems/specification.rb')
-rw-r--r-- | lib/rubygems/specification.rb | 256 |
1 files changed, 123 insertions, 133 deletions
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index 08614cd9eb..596f6ff69f 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -5,10 +5,13 @@ # See LICENSE.txt for permissions. #++ + require 'rubygems/version' require 'rubygems/requirement' require 'rubygems/platform' require 'rubygems/deprecate' +require 'rubygems/basic_specification' +require 'rubygems/stub_specification' # :stopdoc: # date.rb can't be loaded for `make install` due to miniruby @@ -45,7 +48,7 @@ class Date; end # # s.metadata = { "bugtracker" => "http://somewhere.com/blah" } -class Gem::Specification +class Gem::Specification < Gem::BasicSpecification # REFACTOR: Consider breaking out this version stuff into a separate # module. There's enough special stuff around it that it may justify @@ -107,6 +110,10 @@ class Gem::Specification today = Time.now.utc TODAY = Time.utc(today.year, today.month, today.day) + LOAD_CACHE = {} + + private_constant :LOAD_CACHE if defined? private_constant + # :startdoc: ## @@ -156,6 +163,17 @@ class Gem::Specification :version => nil, } + Dupable = { } + + @@default_value.each do |k,v| + case v + when Time, Numeric, Symbol, true, false, nil + Dupable[k] = false + else + Dupable[k] = true + end + end + @@attributes = @@default_value.keys.sort_by { |s| s.to_s } @@array_attributes = @@default_value.reject { |k,v| v != [] }.keys @@nil_attributes, @@non_nil_attributes = @@default_value.keys.partition { |k| @@ -584,11 +602,6 @@ class Gem::Specification attr_writer :default_executable ## - # Path this gemspec was loaded from. This attribute is not persisted. - - attr_reader :loaded_from - - ## # Allows deinstallation of gems with legacy platforms. attr_writer :original_platform # :nodoc: @@ -615,58 +628,68 @@ class Gem::Specification attr_accessor :specification_version - class << self - def default_specifications_dir - File.join(Gem.default_dir, "specifications", "default") + def self._all # :nodoc: + unless defined?(@@all) && @@all then + @@all = stubs.map(&:to_spec) + + # After a reset, make sure already loaded specs + # are still marked as activated. + specs = {} + Gem.loaded_specs.each_value{|s| specs[s] = true} + @@all.each{|s| s.activated = true if specs[s]} + + _resort!(@@all) end + @@all + end - def each_spec(search_dirs) # :nodoc: - search_dirs.each { |dir| - Dir[File.join(dir, "*.gemspec")].each { |path| - spec = Gem::Specification.load path.untaint - # #load returns nil if the spec is bad, so we just ignore - # it at this stage - yield(spec) if spec - } - } + def self._clear_load_cache # :nodoc: + LOAD_CACHE.clear + end + + # :nodoc: + def self.each_gemspec(dirs) + dirs.each do |dir| + Dir[File.join(dir, "*.gemspec")].each do |path| + yield path.untaint + end end + end - def each_default(&block) # :nodoc: - each_spec([default_specifications_dir], - &block) + # :nodoc: + def self.each_stub(dirs) + each_gemspec(dirs) do |path| + stub = Gem::StubSpecification.new(path) + yield stub if stub.valid? end + end - def each_normal(&block) # :nodoc: - each_spec(dirs, &block) + # :nodoc: + def self.each_spec(dirs) + each_gemspec(dirs) do |path| + spec = self.load path + yield spec if spec end end - def self._all # :nodoc: - unless defined?(@@all) && @@all then + ## + # Returns a Gem::StubSpecification for every installed gem - specs = {} - each_default do |spec| - specs[spec.full_name] ||= spec + def self.stubs + @@stubs ||= begin + stubs = {} + each_stub([default_specifications_dir] + dirs) do |stub| + stubs[stub.full_name] ||= stub end - each_normal do |spec| - specs[spec.full_name] ||= spec - end - - @@all = specs.values - - # After a reset, make sure already loaded specs - # are still marked as activated. - specs = {} - Gem.loaded_specs.each_value{|s| specs[s] = true} - @@all.each{|s| s.activated = true if specs[s]} - _resort! + stubs = stubs.values + _resort!(stubs) + stubs end - @@all end - def self._resort! # :nodoc: - @@all.sort! { |a, b| + def self._resort!(specs) # :nodoc: + specs.sort! { |a, b| names = a.name <=> b.name next names if names.nonzero? b.version <=> a.version @@ -677,7 +700,9 @@ class Gem::Specification # Loads the default specifications. It should be called only once. def self.load_defaults - each_default do |spec| + each_spec([default_specifications_dir]) do |spec| + # #load returns nil if the spec is bad, so we just ignore + # it at this stage Gem.register_default_spec(spec) end end @@ -700,7 +725,9 @@ class Gem::Specification return if _all.include? spec _all << spec - _resort! + stubs << spec + _resort!(_all) + _resort!(stubs) end ## @@ -843,9 +870,10 @@ class Gem::Specification # amongst the specs that are not activated. def self.find_inactive_by_path path - self.find { |spec| - spec.contains_requirable_file? path unless spec.activated? + stub = stubs.find { |s| + s.contains_requirable_file? path unless s.activated? } + stub && stub.to_spec end ## @@ -937,6 +965,9 @@ class Gem::Specification file = file.dup.untaint return unless File.file?(file) + spec = LOAD_CACHE[file] + return spec if spec + code = if defined? Encoding File.read file, :mode => 'r:UTF-8:-' else @@ -950,6 +981,7 @@ class Gem::Specification if Gem::Specification === spec spec.loaded_from = file.to_s + LOAD_CACHE[file] = spec return spec end @@ -1013,6 +1045,7 @@ class Gem::Specification raise "wtf: #{spec.full_name} not in #{all_names.inspect}" unless _all.include? spec _all.delete spec + stubs.delete_if { |s| s.full_name == spec.full_name } end ## @@ -1037,6 +1070,8 @@ class Gem::Specification @@dirs = nil Gem.pre_reset_hooks.each { |hook| hook.call } @@all = nil + @@stubs = nil + _clear_load_cache unresolved = unresolved_deps unless unresolved.empty? then w = "W" + "ARN" @@ -1281,20 +1316,6 @@ class Gem::Specification end ## - # Returns the full path to the base gem directory. - # - # eg: /usr/local/lib/ruby/gems/1.8 - - def base_dir - return Gem.dir unless loaded_from - @base_dir ||= if default_gem? then - File.dirname File.dirname File.dirname loaded_from - else - File.dirname File.dirname loaded_from - end - end - - ## # Returns the full path to installed gem's bin directory. # # NOTE: do not confuse this with +bindir+, which is just 'bin', not @@ -1368,19 +1389,6 @@ class Gem::Specification end ## - # Return true if this spec can require +file+. - - def contains_requirable_file? file - root = full_gem_path - suffixes = Gem.suffixes - - require_paths.any? do |lib| - base = "#{root}/#{lib}/#{file}" - suffixes.any? { |suf| File.file? "#{base}#{suf}" } - end - end - - ## # The date this gem was created. Lazily defaults to TODAY. def date @@ -1623,35 +1631,14 @@ class Gem::Specification spec end - ## - # The full path to the gem (install path + full name). - - def full_gem_path - # TODO: This is a heavily used method by gems, so we'll need - # to aleast just alias it to #gem_dir rather than remove it. - - # TODO: also, shouldn't it default to full_name if it hasn't been written? - return @full_gem_path if defined?(@full_gem_path) && @full_gem_path - - @full_gem_path = File.expand_path File.join(gems_dir, full_name) - @full_gem_path.untaint - - return @full_gem_path if File.directory? @full_gem_path - - @full_gem_path = File.expand_path File.join(gems_dir, original_name) + # :nodoc: + def find_full_gem_path + super || File.expand_path(File.join(gems_dir, original_name)) end - - ## - # Returns the full name (name-version) of this Gem. Platform information - # is included (name-version-platform) if it is specified and not the - # default Ruby platform. + private :find_full_gem_path def full_name - @full_name ||= if platform == Gem::Platform::RUBY or platform.nil? then - "#{@name}-#{@version}".untaint - else - "#{@name}-#{@version}-#{platform}".untaint - end + @full_name ||= super end ## @@ -1663,15 +1650,6 @@ class Gem::Specification end ## - # Returns the full path to the gems directory containing this spec's - # gem directory. eg: /usr/local/lib/ruby/1.8/gems - - def gems_dir - # TODO: this logic seems terribly broken, but tests fail if just base_dir - @gems_dir ||= File.join(loaded_from && base_dir || Gem.dir, "gems") - end - - ## # Deprecated and ignored, defaults to true. # # Formerly used to indicate this gem was RDoc-capable. @@ -1703,9 +1681,7 @@ class Gem::Specification # :startdoc: def hash # :nodoc: - @@attributes.inject(0) { |hash_code, (name, _)| - hash_code ^ self.send(name).hash - } + name.hash ^ version.hash end def init_with coder # :nodoc: @@ -1720,7 +1696,7 @@ class Gem::Specification def initialize name = nil, version = nil @loaded = false @activated = false - @loaded_from = nil + self.loaded_from = nil @original_platform = nil @@nil_attributes.each do |key| @@ -1729,11 +1705,7 @@ class Gem::Specification @@non_nil_attributes.each do |key| default = default_value(key) - value = case default - when Time, Numeric, Symbol, true, false, nil then default - else default.dup - end - + value = Dupable[key] ? default.dup : default instance_variable_set "@#{key}", value end @@ -1828,28 +1800,31 @@ class Gem::Specification @licenses ||= [] end - ## - # Set the location a Specification was loaded from. +obj+ is converted - # to a String. + def filename= path + super - def loaded_from= path - @loaded_from = path.to_s - - # reset everything @loaded_from depends upon - @base_dir = nil @bin_dir = nil @cache_dir = nil @cache_file = nil @doc_dir = nil - @full_gem_path = nil @gem_dir = nil - @gems_dir = nil @ri_dir = nil @spec_dir = nil @spec_file = nil end ## + # Path this gemspec was loaded from. This attribute is not persisted. + + alias loaded_from filename + + ## + # Set the location a Specification was loaded from. +obj+ is converted + # to a String. + + alias loaded_from= filename= + + ## # Sets the rubygems_version to the current RubyGems version. def mark_version @@ -1878,6 +1853,11 @@ class Gem::Specification end end + # Prevent ruby hitting spec.method_missing when [[spec]].flatten is called + def to_ary # :nodoc: + nil + end + ## # Normalize the list of files so that: # * All file lists have redundancies removed. @@ -2094,6 +2074,13 @@ class Gem::Specification end ## + # Used by Gem::DependencyResolver to order Gem::Specification objects + + def source # :nodoc: + self + end + + ## # Returns the full path to the directory containing this spec's # gemspec file. eg: /usr/local/lib/ruby/gems/1.8/specifications @@ -2172,6 +2159,7 @@ class Gem::Specification mark_version result = [] result << "# -*- encoding: utf-8 -*-" + result << "#{Gem::StubSpecification::PREFIX}#{name} #{version} #{platform} #{require_paths.join("\0")}" result << nil result << "Gem::Specification.new do |s|" @@ -2259,6 +2247,13 @@ class Gem::Specification "#<Gem::Specification name=#{@name} version=#{@version}>" end + ## + # Returns self + + def to_spec + self + end + def to_yaml(opts = {}) # :nodoc: if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck? then # Because the user can switch the YAML engine behind our @@ -2559,11 +2554,6 @@ class Gem::Specification end end - def default_gem? - loaded_from && - File.dirname(loaded_from) == self.class.default_specifications_dir - end - extend Gem::Deprecate # TODO: |