summaryrefslogtreecommitdiff
path: root/baserockimport/exts/omnibus.find_deps
blob: 8fea31dd39c1158f62ba37a77e9a5a00eaccc8e6 (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
137
138
#!/usr/bin/env ruby
#
# Find dependencies for an Omnibus software component.
#
# Copyright (C) 2014  Codethink Limited
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

require 'bundler'

require_relative 'importer_base'
require_relative 'importer_omnibus_extensions'

BANNER = "Usage: omnibus.find_deps PROJECT_DIR PROJECT_NAME SOURCE_DIR SOFTWARE_NAME"

DESCRIPTION = <<-END
Calculate dependencies for a given Omnibus software component.
END

class OmnibusDependencyFinder < Importer::Base
  def initialize
    local_data = YAML.load_file(local_data_path("omnibus.yaml"))
    @dependency_blacklist = local_data['dependency-blacklist']
  end

  def parse_options(arguments)
    opts = create_option_parser(BANNER, DESCRIPTION)

    parsed_arguments = opts.parse!(arguments)

    if parsed_arguments.length != 4 and parsed_arguments.length != 5
      STDERR.puts "Expected 4 or 5 arguments, got #{parsed_arguments}."
      opts.parse(['-?'])
      exit 255
    end

    project_dir, project_name, source_dir, software_name, expected_version = \
      parsed_arguments
    # Not yet implemented
    #if expected_version != nil
    #  expected_version = Gem::Version.new(expected_version)
    #end
    [project_dir, project_name, source_dir, software_name, expected_version]
  end

  def resolve_rubygems_deps(requirements)
    return {} if requirements.empty?

    log.info('Resolving RubyGem requirements with Bundler')

    fake_gemfile = Bundler::Dsl.new
    fake_gemfile.source('https://rubygems.org')

    requirements.each do |dep|
      fake_gemfile.gem(dep.name, dep.requirement)
    end

    definition = fake_gemfile.to_definition('Gemfile.lock', true)
    resolved_specs = definition.resolve_remotely!

    Hash[resolved_specs.collect { |spec| [spec.name, spec.version.to_s]}]
  end

  def calculate_dependencies_for_software(project, software, source_dir)
    omnibus_deps = {}
    rubygems_deps = {}

    software.dependencies.each do |name|
      software = Omnibus::Software.load(project, name)
      if @dependency_blacklist.member? name
        log.info(
          "Not adding #{name} as a dependency as it is marked to be ignored.")
      elsif software.fetcher.instance_of?(Omnibus::PathFetcher)
        log.info(
          "Not adding #{name} as a dependency: it's installed from " +
          "a path which probably means that it is package configuration, not " +
          "a 3rd-party component to be imported.")
      elsif software.fetcher.instance_of?(Omnibus::NullFetcher)
        if software.builder.built_gemspec
          log.info(
            "Adding #{name} as a RubyGem dependency because it builds " +
            "#{software.builder.built_gemspec}")
          rubygems_deps[name] = software.version
        else
          log.info(
            "Not adding #{name} as a dependency: no sources listed.")
        end
      else
        omnibus_deps[name] = software.version
      end
    end

    gem_requirements = software.builder.manually_installed_rubygems
    rubygems_deps = resolve_rubygems_deps(gem_requirements)

    {
      "omnibus" => {
        # FIXME: are these build or runtime dependencies? We'll assume both.
        "build-dependencies" => omnibus_deps,
        "runtime-dependencies" => omnibus_deps,
      },
      "rubygems" => {
        "build-dependencies" => {},
        "runtime-dependencies" => rubygems_deps,
      }
    }
  end

  def run
    project_dir, project_name, source_dir, software_name = parse_options(ARGV)

    log.info("Calculating dependencies for #{software_name} from project " +
             "#{project_name}, defined in #{project_dir}")

    Dir.chdir(project_dir)

    project = Omnibus::Project.load(project_name)

    software = Omnibus::Software.load(@project, software_name)

    dependencies = calculate_dependencies_for_software(
      project, software, source_dir)
    write_dependencies(STDOUT, dependencies)
  end
end

OmnibusDependencyFinder.new.run