bundler 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bundler might be problematic. Click here for more details.
- data/README.markdown +5 -0
- data/Rakefile +20 -11
- data/lib/bundler.rb +2 -3
- data/lib/bundler/cli.rb +3 -3
- data/lib/bundler/{runtime/dependency.rb → dependency.rb} +4 -4
- data/lib/bundler/dsl.rb +76 -28
- data/lib/bundler/environment.rb +115 -0
- data/lib/bundler/gem_bundle.rb +1 -1
- data/lib/bundler/gem_ext.rb +7 -6
- data/lib/bundler/repository.rb +106 -71
- data/lib/bundler/repository/directory_repository.rb +46 -0
- data/lib/bundler/repository/gem_repository.rb +108 -0
- data/lib/bundler/resolver.rb +33 -7
- data/lib/bundler/source.rb +112 -13
- data/lib/bundler/templates/environment.erb +48 -5
- metadata +6 -6
- data/lib/bundler/manifest.rb +0 -136
- data/lib/bundler/manifest_file.rb +0 -89
- data/lib/bundler/runtime/dsl.rb +0 -44
@@ -0,0 +1,46 @@
|
|
1
|
+
module Bundler
|
2
|
+
class Repository
|
3
|
+
class Directory
|
4
|
+
attr_reader :path, :bindir
|
5
|
+
|
6
|
+
def initialize(path, bindir)
|
7
|
+
@path = path
|
8
|
+
@bindir = bindir
|
9
|
+
|
10
|
+
FileUtils.mkdir_p(path.to_s)
|
11
|
+
end
|
12
|
+
|
13
|
+
def source_index
|
14
|
+
index = Gem::SourceIndex.from_gems_in(@path.join("specifications"))
|
15
|
+
index.each { |n, spec| spec.loaded_from = @path.join("specifications", "#{spec.full_name}.gemspec") }
|
16
|
+
index
|
17
|
+
end
|
18
|
+
|
19
|
+
def gems
|
20
|
+
source_index.gems.values
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_spec(spec)
|
24
|
+
destination = path.join('specifications')
|
25
|
+
destination.mkdir unless destination.exist?
|
26
|
+
|
27
|
+
File.open(destination.join("#{spec.full_name}.gemspec"), 'w') do |f|
|
28
|
+
f.puts spec.to_ruby
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def download_path_for
|
33
|
+
@path.join("dirs")
|
34
|
+
end
|
35
|
+
|
36
|
+
# Checks whether a gem is installed
|
37
|
+
def expand(options)
|
38
|
+
# raise NotImplementedError
|
39
|
+
end
|
40
|
+
|
41
|
+
def cleanup(gems)
|
42
|
+
# raise NotImplementedError
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Bundler
|
2
|
+
class Repository
|
3
|
+
class Gems
|
4
|
+
attr_reader :path, :bindir
|
5
|
+
|
6
|
+
def initialize(path, bindir)
|
7
|
+
@path = path
|
8
|
+
@bindir = bindir
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns the source index for all gems installed in the
|
12
|
+
# repository
|
13
|
+
def source_index
|
14
|
+
index = Gem::SourceIndex.from_gems_in(@path.join("specifications"))
|
15
|
+
index.each { |n, spec| spec.loaded_from = @path.join("specifications", "#{spec.full_name}.gemspec") }
|
16
|
+
index
|
17
|
+
end
|
18
|
+
|
19
|
+
def gems
|
20
|
+
source_index.gems.values
|
21
|
+
end
|
22
|
+
|
23
|
+
# Checks whether a gem is installed
|
24
|
+
def expand(options)
|
25
|
+
cached_gems.each do |name, version|
|
26
|
+
unless installed?(name, version)
|
27
|
+
install_cached_gem(name, version, options)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def cleanup(gems)
|
33
|
+
glob = gems.map { |g| g.full_name }.join(',')
|
34
|
+
base = path.join("{cache,specifications,gems}")
|
35
|
+
|
36
|
+
(Dir[base.join("*")] - Dir[base.join("{#{glob}}{.gemspec,.gem,}")]).each do |file|
|
37
|
+
if File.basename(file) =~ /\.gem$/
|
38
|
+
name = File.basename(file, '.gem')
|
39
|
+
Bundler.logger.info "Deleting gem: #{name}"
|
40
|
+
end
|
41
|
+
FileUtils.rm_rf(file)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_spec(spec)
|
46
|
+
raise NotImplementedError
|
47
|
+
end
|
48
|
+
|
49
|
+
def download_path_for
|
50
|
+
path
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def cache_path
|
56
|
+
@path.join("cache")
|
57
|
+
end
|
58
|
+
|
59
|
+
def cache_files
|
60
|
+
Dir[cache_path.join("*.gem")]
|
61
|
+
end
|
62
|
+
|
63
|
+
def cached_gems
|
64
|
+
cache_files.map do |f|
|
65
|
+
full_name = File.basename(f).gsub(/\.gem$/, '')
|
66
|
+
full_name.split(/-(?=[^-]+$)/)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def spec_path
|
71
|
+
@path.join("specifications")
|
72
|
+
end
|
73
|
+
|
74
|
+
def spec_files
|
75
|
+
Dir[spec_path.join("*.gemspec")]
|
76
|
+
end
|
77
|
+
|
78
|
+
def gem_path
|
79
|
+
@path.join("gems")
|
80
|
+
end
|
81
|
+
|
82
|
+
def gem_paths
|
83
|
+
Dir[gem_path.join("*")]
|
84
|
+
end
|
85
|
+
|
86
|
+
def installed?(name, version)
|
87
|
+
spec_files.any? { |g| File.basename(g) == "#{name}-#{version}.gemspec" } &&
|
88
|
+
gem_paths.any? { |g| File.basename(g) == "#{name}-#{version}" }
|
89
|
+
end
|
90
|
+
|
91
|
+
def install_cached_gem(name, version, options = {})
|
92
|
+
cached_gem = cache_path.join("#{name}-#{version}.gem")
|
93
|
+
# TODO: Add a warning if cached_gem is not a file
|
94
|
+
if cached_gem.file?
|
95
|
+
Bundler.logger.info "Installing #{name}-#{version}.gem"
|
96
|
+
installer = Gem::Installer.new(cached_gem.to_s, options.merge(
|
97
|
+
:install_dir => @path,
|
98
|
+
:ignore_dependencies => true,
|
99
|
+
:env_shebang => true,
|
100
|
+
:wrappers => true,
|
101
|
+
:bin_dir => @bindir
|
102
|
+
))
|
103
|
+
installer.install
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/lib/bundler/resolver.rb
CHANGED
@@ -20,6 +20,7 @@ end
|
|
20
20
|
|
21
21
|
module Bundler
|
22
22
|
class GemNotFound < StandardError; end
|
23
|
+
class VersionConflict < StandardError; end
|
23
24
|
|
24
25
|
class Resolver
|
25
26
|
|
@@ -35,21 +36,39 @@ module Bundler
|
|
35
36
|
# ==== Returns
|
36
37
|
# <GemBundle>,nil:: If the list of dependencies can be resolved, a
|
37
38
|
# collection of gemspecs is returned. Otherwise, nil is returned.
|
38
|
-
def self.resolve(requirements,
|
39
|
+
def self.resolve(requirements, sources)
|
39
40
|
Bundler.logger.info "Calculating dependencies..."
|
40
41
|
|
41
|
-
resolver = new(
|
42
|
+
resolver = new(sources)
|
42
43
|
result = catch(:success) do
|
43
44
|
resolver.resolve(requirements, {})
|
45
|
+
output = resolver.errors.inject("") do |o, (conflict, (origin, requirement))|
|
46
|
+
o << " Conflict on: #{conflict.inspect}:\n"
|
47
|
+
o << " * #{conflict} (#{origin.version}) activated by #{origin.required_by.first}\n"
|
48
|
+
o << " * #{requirement} required by #{requirement.required_by.first}\n"
|
49
|
+
o << " All possible versions of origin requirements conflict."
|
50
|
+
end
|
51
|
+
raise VersionConflict, "No compatible versions could be found for required dependencies:\n #{output}"
|
44
52
|
nil
|
45
53
|
end
|
46
54
|
result && GemBundle.new(result.values)
|
47
55
|
end
|
48
56
|
|
49
|
-
def initialize(
|
57
|
+
def initialize(sources)
|
50
58
|
@errors = {}
|
51
59
|
@stack = []
|
52
|
-
@
|
60
|
+
@specs = Hash.new { |h,k| h[k] = {} }
|
61
|
+
@cache = {}
|
62
|
+
@index = {}
|
63
|
+
|
64
|
+
sources.reverse_each do |source|
|
65
|
+
source.gems.values.each do |spec|
|
66
|
+
# TMP HAX FOR OPTZ
|
67
|
+
spec.source = source
|
68
|
+
next unless Gem::Platform.match(spec.platform)
|
69
|
+
@specs[spec.name][spec.version] = spec
|
70
|
+
end
|
71
|
+
end
|
53
72
|
end
|
54
73
|
|
55
74
|
def resolve(reqs, activated)
|
@@ -61,7 +80,7 @@ module Bundler
|
|
61
80
|
# Easiest to resolve is defined by: Is this gem already activated? Otherwise,
|
62
81
|
# check the number of child dependencies this requirement has.
|
63
82
|
reqs = reqs.sort_by do |req|
|
64
|
-
activated[req.name] ? 0 :
|
83
|
+
activated[req.name] ? 0 : search(req).size
|
65
84
|
end
|
66
85
|
|
67
86
|
activated = activated.dup
|
@@ -77,7 +96,7 @@ module Bundler
|
|
77
96
|
# the remaining requirements.
|
78
97
|
resolve(reqs, activated)
|
79
98
|
else
|
80
|
-
@errors[existing.name] =
|
99
|
+
@errors[existing.name] = [existing, current]
|
81
100
|
# Since the current requirement conflicts with an activated gem, we need
|
82
101
|
# to backtrack to the current requirement's parent and try another version
|
83
102
|
# of it (maybe the current requirement won't be present anymore). If the
|
@@ -99,7 +118,7 @@ module Bundler
|
|
99
118
|
# Fetch all gem versions matching the requirement
|
100
119
|
#
|
101
120
|
# TODO: Warn / error when no matching versions are found.
|
102
|
-
matching_versions =
|
121
|
+
matching_versions = search(current)
|
103
122
|
|
104
123
|
if matching_versions.empty?
|
105
124
|
if current.required_by.empty?
|
@@ -159,5 +178,12 @@ module Bundler
|
|
159
178
|
retval
|
160
179
|
end
|
161
180
|
|
181
|
+
def search(dependency)
|
182
|
+
@cache[dependency.hash] ||= begin
|
183
|
+
@specs[dependency.name].values.select do |spec|
|
184
|
+
dependency =~ spec
|
185
|
+
end.sort_by {|s| s.version }
|
186
|
+
end
|
187
|
+
end
|
162
188
|
end
|
163
189
|
end
|
data/lib/bundler/source.rb
CHANGED
@@ -2,14 +2,19 @@ module Bundler
|
|
2
2
|
# Represents a source of rubygems. Initially, this is only gem repositories, but
|
3
3
|
# eventually, this will be git, svn, HTTP
|
4
4
|
class Source
|
5
|
+
attr_accessor :tmp_path
|
6
|
+
end
|
7
|
+
|
8
|
+
class GemSource < Source
|
5
9
|
attr_reader :uri
|
6
10
|
|
7
|
-
def initialize(
|
8
|
-
@uri =
|
11
|
+
def initialize(options)
|
12
|
+
@uri = options[:uri]
|
13
|
+
@uri = URI.parse(@uri) unless @uri.is_a?(URI)
|
9
14
|
raise ArgumentError, "The source must be an absolute URI" unless @uri.absolute?
|
10
15
|
end
|
11
16
|
|
12
|
-
def
|
17
|
+
def gems
|
13
18
|
@specs ||= fetch_specs
|
14
19
|
end
|
15
20
|
|
@@ -21,9 +26,18 @@ module Bundler
|
|
21
26
|
@uri.to_s
|
22
27
|
end
|
23
28
|
|
24
|
-
|
29
|
+
class RubygemsRetardation < StandardError; end
|
30
|
+
|
31
|
+
def download(spec, repository)
|
25
32
|
Bundler.logger.info "Downloading #{spec.full_name}.gem"
|
26
|
-
|
33
|
+
|
34
|
+
destination = repository.download_path_for(:gem)
|
35
|
+
|
36
|
+
unless destination.writable?
|
37
|
+
raise RubygemsRetardation
|
38
|
+
end
|
39
|
+
|
40
|
+
Gem::RemoteFetcher.fetcher.download(spec, uri, repository.download_path_for(:gem))
|
27
41
|
end
|
28
42
|
|
29
43
|
private
|
@@ -35,17 +49,102 @@ module Bundler
|
|
35
49
|
inflated = Gem.inflate deflated
|
36
50
|
|
37
51
|
index = Marshal.load(inflated)
|
38
|
-
|
52
|
+
index.gems
|
53
|
+
rescue Gem::RemoteFetcher::FetchError => e
|
54
|
+
raise ArgumentError, "#{to_s} is not a valid source: #{e.message}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class DirectorySource < Source
|
59
|
+
def initialize(options)
|
60
|
+
@name = options[:name]
|
61
|
+
@version = options[:version]
|
62
|
+
@location = options[:location]
|
63
|
+
@require_paths = options[:require_paths] || %w(lib)
|
64
|
+
end
|
65
|
+
|
66
|
+
def gems
|
67
|
+
@gems ||= begin
|
68
|
+
specs = {}
|
69
|
+
|
70
|
+
# Find any gemspec files in the directory and load those specs
|
71
|
+
Dir[@location.join('**', '*.gemspec')].each do |file|
|
72
|
+
path = Pathname.new(file).relative_path_from(@location).dirname
|
73
|
+
spec = eval(File.read(file))
|
74
|
+
spec.require_paths.map! { |p| path.join(p) }
|
75
|
+
specs[spec.full_name] = spec
|
76
|
+
end
|
39
77
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
78
|
+
# If a gemspec for the dependency was not found, add it to the list
|
79
|
+
if specs.keys.grep(/^#{Regexp.escape(@name)}/).empty?
|
80
|
+
case
|
81
|
+
when @version.nil?
|
82
|
+
raise ArgumentError, "If you use :at, you must specify the gem" \
|
83
|
+
"and version you wish to stand in for"
|
84
|
+
when !Gem::Version.correct?(@version)
|
85
|
+
raise ArgumentError, "If you use :at, you must specify a gem and" \
|
86
|
+
"version. You specified #{@version} for the version"
|
87
|
+
end
|
88
|
+
|
89
|
+
default = Gem::Specification.new do |s|
|
90
|
+
s.name = @name
|
91
|
+
s.version = Gem::Version.new(@version) if @version
|
92
|
+
end
|
93
|
+
specs[default.full_name] = default
|
94
|
+
end
|
95
|
+
|
96
|
+
specs
|
44
97
|
end
|
98
|
+
end
|
45
99
|
|
46
|
-
|
47
|
-
|
48
|
-
|
100
|
+
def ==(other)
|
101
|
+
# TMP HAX
|
102
|
+
other.is_a?(DirectorySource)
|
103
|
+
end
|
104
|
+
|
105
|
+
def to_s
|
106
|
+
"#{@name} (#{@version}) Located at: '#{@location}'"
|
107
|
+
end
|
108
|
+
|
109
|
+
def download(spec, repository)
|
110
|
+
spec.require_paths.map! { |p| File.join(@location, p) }
|
111
|
+
repository.add_spec(:directory, spec)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class GitSource < DirectorySource
|
116
|
+
def initialize(options)
|
117
|
+
super
|
118
|
+
@uri = options[:uri]
|
119
|
+
@ref = options[:ref]
|
120
|
+
@branch = options[:branch]
|
121
|
+
end
|
122
|
+
|
123
|
+
def gems
|
124
|
+
FileUtils.mkdir_p(tmp_path.join("gitz"))
|
125
|
+
|
126
|
+
# TMP HAX to get the *.gemspec reading to work
|
127
|
+
@location = tmp_path.join("gitz", @name)
|
128
|
+
|
129
|
+
Bundler.logger.info "Cloning git repository at: #{@uri}"
|
130
|
+
`git clone #{@uri} #{@location} --no-hardlinks`
|
131
|
+
|
132
|
+
if @ref
|
133
|
+
Dir.chdir(@location) { `git checkout #{@ref}` }
|
134
|
+
elsif @branch && @branch != "master"
|
135
|
+
Dir.chdir(@location) { `git checkout origin/#{@branch}` }
|
136
|
+
end
|
137
|
+
super
|
138
|
+
end
|
139
|
+
|
140
|
+
def download(spec, repository)
|
141
|
+
dest = repository.download_path_for(:directory).join(@name)
|
142
|
+
spec.require_paths.map! { |p| File.join(dest, p) }
|
143
|
+
repository.add_spec(:directory, spec)
|
144
|
+
if spec.name == @name
|
145
|
+
FileUtils.mkdir_p(dest.dirname)
|
146
|
+
FileUtils.mv(tmp_path.join("gitz", spec.name), dest)
|
147
|
+
end
|
49
148
|
end
|
50
149
|
end
|
51
150
|
end
|
@@ -1,7 +1,8 @@
|
|
1
|
+
# DO NOT MODIFY THIS FILE
|
1
2
|
module Bundler
|
2
3
|
dir = File.dirname(__FILE__)
|
3
4
|
|
4
|
-
<% unless
|
5
|
+
<% unless options[:system_gems] -%>
|
5
6
|
ENV["GEM_HOME"] = dir
|
6
7
|
ENV["GEM_PATH"] = dir
|
7
8
|
<% end -%>
|
@@ -9,12 +10,12 @@ module Bundler
|
|
9
10
|
ENV["RUBYOPT"] = "-r#{__FILE__} #{ENV["RUBYOPT"]}"
|
10
11
|
|
11
12
|
<% load_paths.each do |load_path| -%>
|
12
|
-
$LOAD_PATH.unshift "#{dir}/<%= load_path %>"
|
13
|
+
$LOAD_PATH.unshift File.expand_path("#{dir}/<%= load_path %>")
|
13
14
|
<% end -%>
|
14
15
|
|
15
16
|
@gemfile = "#{dir}/<%= filename %>"
|
16
17
|
|
17
|
-
<% if
|
18
|
+
<% if options[:rubygems] -%>
|
18
19
|
require "rubygems"
|
19
20
|
|
20
21
|
@bundled_specs = {}
|
@@ -37,10 +38,52 @@ module Bundler
|
|
37
38
|
add_specs_to_index
|
38
39
|
<% end -%>
|
39
40
|
|
40
|
-
|
41
|
+
def self.require_env(env = nil)
|
42
|
+
context = Class.new do
|
43
|
+
def initialize(env) @env = env && env.to_s ; end
|
44
|
+
def method_missing(*) ; end
|
45
|
+
def only(env)
|
46
|
+
old, @only = @only, _combine_onlys(env)
|
47
|
+
yield
|
48
|
+
@only = old
|
49
|
+
end
|
50
|
+
def except(env)
|
51
|
+
old, @except = @except, _combine_excepts(env)
|
52
|
+
yield
|
53
|
+
@except = old
|
54
|
+
end
|
55
|
+
def gem(name, *args)
|
56
|
+
opt = args.last || {}
|
57
|
+
only = _combine_onlys(opt[:only] || opt["only"])
|
58
|
+
except = _combine_excepts(opt[:except] || opt["except"])
|
59
|
+
files = opt[:require_as] || opt["require_as"] || name
|
60
|
+
|
61
|
+
return unless !only || only.any? {|e| e == @env }
|
62
|
+
return if except && except.any? {|e| e == @env }
|
63
|
+
|
64
|
+
files.each { |f| require f }
|
65
|
+
yield if block_given?
|
66
|
+
true
|
67
|
+
end
|
68
|
+
private
|
69
|
+
def _combine_onlys(only)
|
70
|
+
return @only unless only
|
71
|
+
only = [only].flatten.compact.uniq.map { |o| o.to_s }
|
72
|
+
only &= @only if @only
|
73
|
+
only
|
74
|
+
end
|
75
|
+
def _combine_excepts(except)
|
76
|
+
return @except unless except
|
77
|
+
except = [except].flatten.compact.uniq.map { |o| o.to_s }
|
78
|
+
except |= @except if @except
|
79
|
+
except
|
80
|
+
end
|
81
|
+
end
|
82
|
+
context.new(env && env.to_s).instance_eval(File.read(@gemfile))
|
83
|
+
end
|
41
84
|
end
|
42
85
|
|
43
|
-
<% if
|
86
|
+
<% if options[:rubygems] -%>
|
44
87
|
module Gem
|
45
88
|
def source_index.refresh!
|
46
89
|
super
|