summaryrefslogtreecommitdiff
path: root/lib/bundler/manifest.rb
blob: 690e419155b83a208818e7f5c09e368121abb1ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
require "rubygems/source_index"

module Bundler
  class VersionConflict < StandardError; end

  class Manifest
    attr_reader :sources, :dependencies, :path

    def initialize(sources, dependencies, bindir, repository_path, rubygems, system_gems)
      sources.map! {|s| s.is_a?(URI) ? s : URI.parse(s) }
      @sources      = sources
      @dependencies = dependencies
      @bindir       = bindir
      @repository   = Repository.new(repository_path)
      @rubygems     = rubygems
      @system_gems  = system_gems
    end

    def install(update)
      fetch(update)
      @repository.install_cached_gems(:bin_dir => @bindir || @repository.path.join("bin"))
      cleanup_removed_gems
      create_environment_files(@repository.path.join("environments"))
      Bundler.logger.info "Done."
    end

    def activate(environment = "default")
      require @repository.path.join("environments", "#{environment}.rb")
    end

    def require_all
      dependencies.each do |dep|
        dep.require_as.each {|file| require file }
      end
    end

    def gems_for(environment = nil)
      deps     = dependencies
      deps     = deps.select { |d| d.in?(environment) } if environment
      deps     = deps.map { |d| d.to_gem_dependency }
      Resolver.resolve(deps, @repository.source_index)
    end
    alias gems gems_for

    def environments
      envs = dependencies.map {|dep| Array(dep.only) + Array(dep.except) }.flatten
      envs << "default"
    end

  private

    def fetch(update)
      return unless update || !all_gems_installed?

      finder = Finder.new(*sources)
      unless bundle = finder.resolve(*gem_dependencies)
        gems = @dependencies.map {|d| "  #{d.to_s}" }.join("\n")
        raise VersionConflict, "No compatible versions could be found for:\n#{gems}"
      end

      bundle.download(@repository.path)
    end

    def gem_dependencies
      @gem_dependencies ||= dependencies.map { |d| d.to_gem_dependency }
    end

    def all_gems_installed?
      downloaded_gems = {}

      Dir[@repository.path.join("cache", "*.gem")].each do |file|
        file =~ /\/([^\/]+)-([\d\.]+)\.gem$/
        name, version = $1, $2
        downloaded_gems[name] = Gem::Version.new(version)
      end

      gem_dependencies.all? do |dep|
        downloaded_gems[dep.name] &&
        dep.version_requirements.satisfied_by?(downloaded_gems[dep.name])
      end
    end

    def cleanup_removed_gems
      glob = gems.map { |g| g.full_name }.join(',')
      base = @repository.path.join("{cache,specifications,gems}")

      (Dir[base.join("*")] - Dir[base.join("{#{glob}}{.gemspec,.gem,}")]).each do |file|
        Bundler.logger.info "Deleting gem: #{File.basename(file, ".gem")}" if File.basename(file) =~ /\.gem$/
        FileUtils.rm_rf(file)
      end

      glob = gems.map { |g| g.executables }.flatten.join(',')
      (Dir[@bindir.join("*")] - Dir[@bindir.join("{#{glob}}")]).each do |file|
        Bundler.logger.info "Deleting bin file: #{File.basename(file)}"
        FileUtils.rm_rf(file)
      end
    end

    def create_environment_files(path)
      FileUtils.mkdir_p(path)
      environments.each do |environment|
        specs = gems_for(environment)
        files = spec_files_for_specs(specs, path)
        load_paths = load_paths_for_specs(specs)
        create_environment_file(path, environment, files, load_paths)
      end
    end

    def create_environment_file(path, environment, spec_files, load_paths)
      File.open(path.join("#{environment}.rb"), "w") do |file|
        template = File.read(File.join(File.dirname(__FILE__), "templates", "environment.rb"))
        erb = ERB.new(template)
        file.puts erb.result(binding)
      end
    end

    def load_paths_for_specs(specs)
      load_paths = []
      specs.each do |spec|
        load_paths << File.join(spec.full_gem_path, spec.bindir) if spec.bindir
        spec.require_paths.each do |path|
          load_paths << File.join(spec.full_gem_path, path)
        end
      end
      load_paths
    end

    def spec_files_for_specs(specs, path)
      files = {}
      specs.each do |s|
        files[s.name] = path.join("..", "specifications", "#{s.full_name}.gemspec").expand_path
      end
      files
    end
  end
end