bundler 0.9.26 → 1.0.0.beta.1
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/CHANGELOG.md +7 -0
- data/README.md +3 -1
- data/ROADMAP.md +19 -34
- data/TODO.md +16 -0
- data/lib/bundler.rb +87 -41
- data/lib/bundler/cli.rb +115 -73
- data/lib/bundler/definition.rb +203 -34
- data/lib/bundler/dependency.rb +77 -0
- data/lib/bundler/dsl.rb +57 -16
- data/lib/bundler/environment.rb +13 -109
- data/lib/bundler/graph.rb +130 -0
- data/lib/bundler/index.rb +19 -41
- data/lib/bundler/installer.rb +22 -58
- data/lib/bundler/lazy_specification.rb +67 -0
- data/lib/bundler/lockfile_parser.rb +100 -0
- data/lib/bundler/remote_specification.rb +2 -1
- data/lib/bundler/resolver.rb +185 -45
- data/lib/bundler/rubygems_ext.rb +105 -6
- data/lib/bundler/runtime.rb +24 -82
- data/lib/bundler/settings.rb +9 -5
- data/lib/bundler/setup.rb +6 -14
- data/lib/bundler/shared_helpers.rb +2 -25
- data/lib/bundler/source.rb +306 -132
- data/lib/bundler/spec_set.rb +77 -17
- data/lib/bundler/templates/Executable +28 -0
- data/lib/bundler/vendor/thor.rb +31 -4
- data/lib/bundler/vendor/thor/invocation.rb +1 -1
- data/lib/bundler/vendor/thor/parser/arguments.rb +14 -3
- data/lib/bundler/vendor/thor/parser/options.rb +0 -5
- data/lib/bundler/vendor/thor/shell/basic.rb +28 -0
- data/lib/bundler/vendor/thor/task.rb +6 -6
- data/lib/bundler/vendor/thor/util.rb +13 -14
- data/lib/bundler/vendor/thor/version.rb +1 -1
- data/lib/bundler/version.rb +1 -1
- metadata +11 -5
- data/lib/bundler/templates/environment.erb +0 -91
data/lib/bundler/environment.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'erb'
|
2
|
-
|
3
1
|
module Bundler
|
4
2
|
class Environment
|
5
3
|
attr_reader :root
|
@@ -9,130 +7,36 @@ module Bundler
|
|
9
7
|
@definition = definition
|
10
8
|
end
|
11
9
|
|
10
|
+
# TODO: Remove this method. It's used in cli.rb still
|
12
11
|
def index
|
13
|
-
@index
|
14
|
-
idx.use runtime_gems
|
15
|
-
idx.use Index.cached_gems
|
16
|
-
end
|
12
|
+
@definition.index
|
17
13
|
end
|
18
14
|
|
19
15
|
def requested_specs
|
20
|
-
@requested_specs
|
21
|
-
groups = @definition.groups - Bundler.settings.without
|
22
|
-
groups.map! { |g| g.to_sym }
|
23
|
-
groups.any? ? specs_for(groups) : []
|
24
|
-
end
|
16
|
+
@definition.requested_specs
|
25
17
|
end
|
26
18
|
|
27
19
|
def specs
|
28
|
-
@specs
|
20
|
+
@definition.specs
|
29
21
|
end
|
30
22
|
|
31
|
-
|
32
|
-
|
33
|
-
def sources
|
34
|
-
@definition.sources
|
23
|
+
def dependencies
|
24
|
+
@definition.dependencies
|
35
25
|
end
|
36
26
|
|
37
|
-
def
|
38
|
-
@
|
39
|
-
sources.each do |s|
|
40
|
-
i.use s.local_specs if s.respond_to?(:local_specs)
|
41
|
-
end
|
42
|
-
|
43
|
-
i.use Index.installed_gems
|
44
|
-
end
|
27
|
+
def current_dependencies
|
28
|
+
@definition.current_dependencies
|
45
29
|
end
|
46
30
|
|
47
|
-
def
|
48
|
-
|
49
|
-
|
50
|
-
next unless dep.source && dep.source.respond_to?(type)
|
51
|
-
source_requirements[dep.name] = dep.source.send(type)
|
31
|
+
def lock
|
32
|
+
File.open(root.join('Gemfile.lock'), 'w') do |f|
|
33
|
+
f.puts @definition.to_lock
|
52
34
|
end
|
53
|
-
|
54
|
-
# Run a resolve against the locally available gems
|
55
|
-
Resolver.resolve(actual_dependencies, index, source_requirements)
|
56
35
|
end
|
57
36
|
|
58
|
-
def
|
59
|
-
|
37
|
+
def update(*gems)
|
38
|
+
# Nothing
|
60
39
|
end
|
61
40
|
|
62
|
-
def resolve_remotely
|
63
|
-
raise NotImplementedError
|
64
|
-
end
|
65
|
-
|
66
|
-
def specs_for(groups)
|
67
|
-
deps = dependencies.select { |d| (d.groups & groups).any? }
|
68
|
-
# deps.any? ? specs.for(deps) : specs
|
69
|
-
specs.for(deps)
|
70
|
-
end
|
71
|
-
|
72
|
-
# ==== Locking
|
73
|
-
|
74
|
-
def locked?
|
75
|
-
File.exist?("#{root}/Gemfile.lock")
|
76
|
-
end
|
77
|
-
|
78
|
-
def write_rb_lock
|
79
|
-
shared_helpers = File.read(File.expand_path("../shared_helpers.rb", __FILE__))
|
80
|
-
template = File.read(File.expand_path("../templates/environment.erb", __FILE__))
|
81
|
-
erb = ERB.new(template, nil, '-')
|
82
|
-
Bundler.env_file.dirname.mkpath
|
83
|
-
File.open(Bundler.env_file, 'w') do |f|
|
84
|
-
f.puts erb.result(binding)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def gemfile_fingerprint
|
89
|
-
Digest::SHA1.hexdigest(File.read("#{root}/Gemfile"))
|
90
|
-
end
|
91
|
-
|
92
|
-
def specs_for_lock_file
|
93
|
-
requested_specs.map do |s|
|
94
|
-
hash = {
|
95
|
-
:name => s.name,
|
96
|
-
:load_paths => s.load_paths
|
97
|
-
}
|
98
|
-
if s.respond_to?(:relative_loaded_from) && s.relative_loaded_from
|
99
|
-
hash[:virtual_spec] = s.to_ruby
|
100
|
-
end
|
101
|
-
hash[:loaded_from] = s.loaded_from.to_s
|
102
|
-
hash
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def autorequires_for_groups(*groups)
|
107
|
-
groups.map! { |g| g.to_sym }
|
108
|
-
autorequires = Hash.new { |h,k| h[k] = [] }
|
109
|
-
|
110
|
-
ordered_deps = []
|
111
|
-
specs = groups.any? ? specs_for(groups) : requested_specs
|
112
|
-
specs.each do |g|
|
113
|
-
dep = @definition.dependencies.find{|d| d.name == g.name }
|
114
|
-
ordered_deps << dep if dep && !ordered_deps.include?(dep)
|
115
|
-
end
|
116
|
-
|
117
|
-
ordered_deps.each do |dep|
|
118
|
-
dep.groups.each do |group|
|
119
|
-
# If there is no autorequire, then rescue from
|
120
|
-
# autorequiring the gems name
|
121
|
-
if dep.autorequire
|
122
|
-
dep.autorequire.each do |file|
|
123
|
-
autorequires[group] << [file, true]
|
124
|
-
end
|
125
|
-
else
|
126
|
-
autorequires[group] << [dep.name, false]
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
if groups.empty?
|
132
|
-
autorequires
|
133
|
-
else
|
134
|
-
groups.inject({}) { |h,g| h[g] = autorequires[g]; h }
|
135
|
-
end
|
136
|
-
end
|
137
41
|
end
|
138
42
|
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Bundler
|
2
|
+
class Graph
|
3
|
+
|
4
|
+
USER_OPTIONS = {:style => 'filled, bold', :fillcolor => '#cccccc'}.freeze
|
5
|
+
|
6
|
+
def initialize(env)
|
7
|
+
@env = env
|
8
|
+
end
|
9
|
+
|
10
|
+
def nodes
|
11
|
+
populate
|
12
|
+
@nodes
|
13
|
+
end
|
14
|
+
|
15
|
+
def groups
|
16
|
+
populate
|
17
|
+
@groups
|
18
|
+
end
|
19
|
+
|
20
|
+
def viz(output_file, show_gem_versions = false, show_dependency_requirements = false)
|
21
|
+
require 'graphviz'
|
22
|
+
populate
|
23
|
+
|
24
|
+
graph_viz = GraphViz::new('Gemfile', {:concentrate => true, :dpi => 65 } )
|
25
|
+
graph_viz.edge[:fontname] = graph_viz.node[:fontname] = 'Arial, Helvetica, SansSerif'
|
26
|
+
graph_viz.edge[:fontsize] = 12
|
27
|
+
|
28
|
+
viz_nodes = {}
|
29
|
+
|
30
|
+
# populate all of the nodes
|
31
|
+
nodes.each do |name, node|
|
32
|
+
label = name.dup
|
33
|
+
label << "\n#{node.version}" if show_gem_versions
|
34
|
+
options = { :label => label }
|
35
|
+
options.merge!( USER_OPTIONS ) if node.is_user
|
36
|
+
viz_nodes[name] = graph_viz.add_node( name, options )
|
37
|
+
end
|
38
|
+
|
39
|
+
group_nodes = {}
|
40
|
+
@groups.each do |name, dependencies|
|
41
|
+
group_nodes[name] = graph_viz.add_node(name.to_s, { :shape => 'folder', :fontsize => 16 }.merge(USER_OPTIONS))
|
42
|
+
dependencies.each do |dependency|
|
43
|
+
options = { :weight => 2 }
|
44
|
+
if show_dependency_requirements && (dependency.requirement.to_s != ">= 0")
|
45
|
+
options[:label] = dependency.requirement.to_s
|
46
|
+
end
|
47
|
+
graph_viz.add_edge( group_nodes[name], viz_nodes[dependency.name], options )
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
@groups.keys.select { |group| group != :default }.each do |group|
|
52
|
+
graph_viz.add_edge( group_nodes[group], group_nodes[:default], { :weight => 2 } )
|
53
|
+
end
|
54
|
+
|
55
|
+
viz_nodes.each do |name, node|
|
56
|
+
nodes[name].dependencies.each do |dependency|
|
57
|
+
options = { }
|
58
|
+
if nodes[dependency.name].is_user
|
59
|
+
options[:constraint] = false
|
60
|
+
end
|
61
|
+
if show_dependency_requirements && (dependency.requirement.to_s != ">= 0")
|
62
|
+
options[:label] = dependency.requirement.to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
graph_viz.add_edge( node, viz_nodes[dependency.name], options )
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
graph_viz.output( :png => output_file )
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def populate
|
75
|
+
return if @populated
|
76
|
+
|
77
|
+
# hash of name => GraphNode
|
78
|
+
@nodes = {}
|
79
|
+
@groups = {}
|
80
|
+
|
81
|
+
# Populate @nodes
|
82
|
+
@env.specs.each { |spec| @nodes[spec.name] = GraphNode.new(spec.name, spec.version) }
|
83
|
+
|
84
|
+
# For gems in Gemfile, add details
|
85
|
+
@env.current_dependencies.each do |dependency|
|
86
|
+
node = @nodes[dependency.name]
|
87
|
+
node.is_user = true
|
88
|
+
|
89
|
+
dependency.groups.each do |group|
|
90
|
+
if @groups.has_key? group
|
91
|
+
group = @groups[group]
|
92
|
+
else
|
93
|
+
group = @groups[group] = []
|
94
|
+
end
|
95
|
+
group << dependency
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# walk though a final time and add edges
|
100
|
+
@env.specs.each do |spec|
|
101
|
+
|
102
|
+
from = @nodes[spec.name]
|
103
|
+
spec.runtime_dependencies.each do |dependency|
|
104
|
+
from.dependencies << dependency
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
@nodes.freeze
|
110
|
+
@groups.freeze
|
111
|
+
@populated = true
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
# Add version info
|
117
|
+
class GraphNode
|
118
|
+
|
119
|
+
def initialize(name, version)
|
120
|
+
@name = name
|
121
|
+
@version = version
|
122
|
+
@is_user = false
|
123
|
+
@dependencies = []
|
124
|
+
end
|
125
|
+
|
126
|
+
attr_reader :name, :dependencies, :version
|
127
|
+
attr_accessor :is_user
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
data/lib/bundler/index.rb
CHANGED
@@ -6,43 +6,6 @@ module Bundler
|
|
6
6
|
i
|
7
7
|
end
|
8
8
|
|
9
|
-
def self.installed_gems
|
10
|
-
build do |idx|
|
11
|
-
idx.use bundler_gems
|
12
|
-
idx.use system_gems
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.bundler_gems
|
17
|
-
Source::BundlerGems.new.specs
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.system_gems
|
21
|
-
Source::SystemGems.new.specs
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.cached_gems
|
25
|
-
build do |idx|
|
26
|
-
idx.use application_cached_gems
|
27
|
-
idx.use system_cached_gems
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.application_cached_gems
|
32
|
-
path = "#{Bundler.root}/vendor/cache"
|
33
|
-
if File.directory?(path)
|
34
|
-
from_cached_specs(path)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.system_cached_gems
|
39
|
-
from_cached_specs("#{Bundler.bundle_path}/cache")
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.from_cached_specs(path)
|
43
|
-
Source::GemCache.new("path" => path).specs
|
44
|
-
end
|
45
|
-
|
46
9
|
def initialize
|
47
10
|
@cache = {}
|
48
11
|
@specs = Hash.new { |h,k| h[k] = [] }
|
@@ -62,12 +25,26 @@ module Bundler
|
|
62
25
|
|
63
26
|
def search(query)
|
64
27
|
case query
|
65
|
-
when Gem::Specification, RemoteSpecification then search_by_spec(query)
|
28
|
+
when Gem::Specification, RemoteSpecification, LazySpecification then search_by_spec(query)
|
66
29
|
when String then @specs[query]
|
67
30
|
else search_by_dependency(query)
|
68
31
|
end
|
69
32
|
end
|
70
33
|
|
34
|
+
def search_for_all_platforms(dependency)
|
35
|
+
specs = @specs[dependency.name]
|
36
|
+
|
37
|
+
wants_prerelease = dependency.requirement.prerelease?
|
38
|
+
only_prerelease = specs.all? {|spec| spec.version.prerelease? }
|
39
|
+
found = specs.select { |spec| dependency =~ spec }
|
40
|
+
|
41
|
+
unless wants_prerelease || only_prerelease
|
42
|
+
found.reject! { |spec| spec.version.prerelease? }
|
43
|
+
end
|
44
|
+
|
45
|
+
found.sort_by {|s| [s.version, s.platform.to_s == 'ruby' ? "\0" : s.platform.to_s] }
|
46
|
+
end
|
47
|
+
|
71
48
|
def sources
|
72
49
|
@specs.values.map do |specs|
|
73
50
|
specs.map{|s| s.source.class }
|
@@ -105,7 +82,9 @@ module Bundler
|
|
105
82
|
private
|
106
83
|
|
107
84
|
def search_by_spec(spec)
|
108
|
-
@specs[spec.name].select
|
85
|
+
@specs[spec.name].select do |s|
|
86
|
+
s.version == spec.version && Gem::Platform.new(s.platform) == Gem::Platform.new(spec.platform)
|
87
|
+
end
|
109
88
|
end
|
110
89
|
|
111
90
|
def search_by_dependency(dependency)
|
@@ -114,7 +93,7 @@ module Bundler
|
|
114
93
|
|
115
94
|
wants_prerelease = dependency.requirement.prerelease?
|
116
95
|
only_prerelease = specs.all? {|spec| spec.version.prerelease? }
|
117
|
-
found = specs.select { |spec| dependency =~ spec }
|
96
|
+
found = specs.select { |spec| dependency =~ spec && Gem::Platform.match(spec.platform) }
|
118
97
|
|
119
98
|
unless wants_prerelease || only_prerelease
|
120
99
|
found.reject! { |spec| spec.version.prerelease? }
|
@@ -123,6 +102,5 @@ module Bundler
|
|
123
102
|
found.sort_by {|s| [s.version, s.platform.to_s == 'ruby' ? "\0" : s.platform.to_s] }
|
124
103
|
end
|
125
104
|
end
|
126
|
-
|
127
105
|
end
|
128
106
|
end
|
data/lib/bundler/installer.rb
CHANGED
@@ -2,16 +2,22 @@ require 'rubygems/dependency_installer'
|
|
2
2
|
|
3
3
|
module Bundler
|
4
4
|
class Installer < Environment
|
5
|
-
def self.install(root, definition, options)
|
6
|
-
new(root, definition)
|
5
|
+
def self.install(root, definition, options = {})
|
6
|
+
installer = new(root, definition)
|
7
|
+
installer.run(options)
|
8
|
+
installer
|
7
9
|
end
|
8
10
|
|
9
11
|
def run(options)
|
10
|
-
if
|
12
|
+
if dependencies.empty?
|
11
13
|
Bundler.ui.warn "The Gemfile specifies no dependencies"
|
12
14
|
return
|
13
15
|
end
|
14
16
|
|
17
|
+
# Since we are installing, we can resolve the definition
|
18
|
+
# using remote specs
|
19
|
+
@definition.resolve_remotely!
|
20
|
+
|
15
21
|
# Ensure that BUNDLE_PATH exists
|
16
22
|
FileUtils.mkdir_p(Bundler.bundle_path)
|
17
23
|
|
@@ -21,72 +27,30 @@ module Bundler
|
|
21
27
|
specs.each do |spec|
|
22
28
|
spec.source.fetch(spec) if spec.source.respond_to?(:fetch)
|
23
29
|
|
24
|
-
unless requested_specs.include?(spec)
|
25
|
-
|
26
|
-
|
27
|
-
end
|
30
|
+
# unless requested_specs.include?(spec)
|
31
|
+
# Bundler.ui.debug " * Not in requested group; skipping."
|
32
|
+
# next
|
33
|
+
# end
|
28
34
|
|
29
|
-
if [Source::Rubygems, Source::GemCache].include?(spec.source.class)
|
30
|
-
Bundler.ui.info "Installing #{spec.name} (#{spec.version}) from #{spec.source} "
|
31
|
-
else
|
32
|
-
Bundler.ui.info "Using #{spec.name} (#{spec.version}) from #{spec.source} "
|
33
|
-
end
|
34
35
|
spec.source.install(spec)
|
35
36
|
Bundler.ui.info ""
|
37
|
+
generate_bundler_executable_stubs(spec)
|
38
|
+
FileUtils.rm_rf(Bundler.tmp)
|
36
39
|
end
|
37
40
|
|
38
|
-
|
39
|
-
write_rb_lock
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def dependencies
|
44
|
-
@definition.dependencies
|
45
|
-
end
|
46
|
-
|
47
|
-
def actual_dependencies
|
48
|
-
@definition.actual_dependencies
|
41
|
+
lock
|
49
42
|
end
|
50
43
|
|
51
44
|
private
|
52
45
|
|
53
|
-
def
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
# Simple logic for now. Can improve later.
|
60
|
-
specs.length == actual_dependencies.length && specs
|
61
|
-
rescue GemNotFound, PathError => e
|
62
|
-
nil
|
63
|
-
end
|
64
|
-
|
65
|
-
def resolve_remotely
|
66
|
-
resolve(:specs, remote_index)
|
67
|
-
end
|
68
|
-
|
69
|
-
def ambiguous?(dep)
|
70
|
-
dep.requirement.requirements.any? { |op,_| op != '=' }
|
71
|
-
end
|
72
|
-
|
73
|
-
def remote_index
|
74
|
-
@remote_index ||= Index.build do |idx|
|
75
|
-
rubygems, other = sources.partition { |s| Source::Rubygems === s }
|
76
|
-
|
77
|
-
other.each do |source|
|
78
|
-
Bundler.ui.debug "Source: Processing index"
|
79
|
-
idx.use source.specs
|
80
|
-
end
|
81
|
-
|
82
|
-
idx.use Index.installed_gems
|
83
|
-
idx.use Index.cached_gems
|
84
|
-
|
85
|
-
rubygems.each do |source|
|
86
|
-
Bundler.ui.debug "Source: Processing index"
|
87
|
-
idx.use source.specs
|
46
|
+
def generate_bundler_executable_stubs(spec)
|
47
|
+
spec.executables.each do |executable|
|
48
|
+
next if executable == "bundle"
|
49
|
+
File.open "#{Bundler.bin_path}/#{executable}", 'w', 0755 do |f|
|
50
|
+
f.puts File.read(File.expand_path('../templates/Executable', __FILE__))
|
88
51
|
end
|
89
52
|
end
|
90
53
|
end
|
54
|
+
|
91
55
|
end
|
92
56
|
end
|