summaryrefslogtreecommitdiff
path: root/import/omnibus.to_chunk
blob: c086381e193475d35a2dcbd9fdac9e341e362fe2 (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
139
140
141
142
143
#!/usr/bin/env ruby
#
# Create a chunk morphology to integrate Omnibus software in Baserock
#
# 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 'omnibus'

require 'optparse'
require 'rubygems/commands/install_command'
require 'shellwords'

require_relative 'importer_base'

# This DEFINITIONS#PROJECT thing is a bit shit. Make main.py smarter about
# being able to pass extra arguments to import extensions instead of forcing
# everything into two arguments.
BANNER = "Usage: omnibus.to_chunk DEFINITIONS_DIR#PROJECT_NAME SOFTWARE_NAME"

DESCRIPTION = <<-END
Generate a .morph file for a given Omnibus software component.
END

class Omnibus::Builder
  # It's possible to use `gem install` in build commands, which is a great
  # way of subverting the dependency tracking Omnibus provides. It's done
  # in `omnibus-chef/config/software/chefdk.rb`, for example.
  #
  # To handle this, here we extend the class that executes the build commands
  # to detect when `gem install` is run. It uses the Gem library to turn the
  # commandline back into a Bundler::Dependency object that we can use.

  class GemInstallCommandParser < Gem::Commands::InstallCommand
    def dependency_list_from_commandline(args)
      handle_options args
      options[:args].collect do |gem_name|
        Bundler::Dependency.new(gem_name, options[:version])
      end
    end
  end

  def gem(command, options = {})
    # This function implements the 'gem' function in the build-commands DSL.
    if command.match /^install/
      parser = GemInstallCommandParser.new
      args = Shellwords.split(command).drop(1)
      gems = parser.dependency_list_from_commandline(args)
      manually_installed_rubygems.concat gems
    end
  end

  def manually_installed_rubygems
    @manually_installed_rubygems ||= []
  end
end

class OmnibusChunkMorphologyGenerator < Importer::Base
  def parse_options(arguments)
    opts = create_option_parser(BANNER, DESCRIPTION)

    parsed_arguments = opts.parse!(arguments)

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

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

  def deps_for_software(software)
    deps = Hash.new
    software.dependencies.each do |dep|
      deps[dep] = 0
    end
    software.builder.manually_installed_rubygems.each do |dep|
      deps[dep.name] = dep.requirement.to_s
    end
    deps
  end

  def generate_chunk_morph_for_software(software)
    omnibus_deps = Hash[software.dependencies.collect do |dep|
      [dep, 0]
    end]
    rubygems_deps = Hash[software.builder.manually_installed_rubygems.collect do |dep|
      [dep.name, dep.requirement.to_s]
    end]
    {
      "name" => software.name,
      "kind" => "chunk",
      # Possibly this tool should look at software.build and
      # generate suitable configure, build and install-commands.
      # For now: don't bother!

      # FIXME: are these build or runtime dependencies? We'll assume both.
      "x-build-dependencies-omnibus" => omnibus_deps,
      "x-runtime-dependencies-omnibus" => omnibus_deps,

      "x-build-dependencies-rubygems" => {},
      "x-runtime-dependencies-rubygems" => rubygems_deps,
    }
  end

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

    log.info("Creating chunk morph for #{software_name} from project " +
             "#{project_name}, defined in #{project_dir}")
    log.debug("Running in: #{Dir.getwd}")

    project = Omnibus::Project.load(project_name)

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

    morph = generate_chunk_morph_for_software(software)

    write_morph(STDOUT, morph)
  end
end

OmnibusChunkMorphologyGenerator.new.run