Class: Omnibus::Builder

Inherits:
Object
  • Object
show all
Includes:
Cleanroom, Digestable, Instrumentation, Logging, Templating, Util
Defined in:
lib/omnibus/builder.rb

Defined Under Namespace

Classes: BuildCommand

Constant Summary

Constants included from Util

Util::SHELLOUT_OPTIONS

Instance Attribute Summary collapse

System DSL methods collapse

Ruby DSL methods collapse

File system DSL methods collapse

Public API collapse

Instance Method Summary collapse

Methods included from Util

#copy_file, #create_directory, #create_file, #create_link, included, #remove_directory, #remove_file, #shellout

Methods included from Templating

included, #render_template

Methods included from Logging

included

Methods included from Instrumentation

#measure

Methods included from Digestable

#digest, #digest_directory

Constructor Details

#initialize(software) ⇒ Builder

Create a new builder object for evaluation.


56
57
58
# File 'lib/omnibus/builder.rb', line 56

def initialize(software)
  @software = software
end

Instance Attribute Details

#softwareSoftware (readonly)


48
49
50
# File 'lib/omnibus/builder.rb', line 48

def software
  @software
end

Instance Method Details

#appbundle(app_name, options = {}) ⇒ void

This method returns an undefined value.

Execute the given appbundler command against the embedded Ruby's appbundler. This command assumes the appbundle gem is installed and in the embedded Ruby. You should add a dependency on the appbundler software definition if you want to use this command.

Examples:

appbundle 'chef'

276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/omnibus/builder.rb', line 276

def appbundle(app_name, options = {})
  build_commands << BuildCommand.new("appbundle `#{app_name}'") do
    bin_dir            = "#{install_dir}/bin"
    embedded_apps_root = "#{install_dir}/embedded/apps"
    embedded_app_dir   = "#{embedded_apps_root}/#{app_name}"
    gemfile_lock       = "#{embedded_app_dir}/Gemfile.lock"
    appbundler_bin     = windows_safe_path("#{install_dir}/embedded/bin/appbundler")

    # Ensure the main bin dir exists
    FileUtils.mkdir_p(bin_dir)
    # Ensure the embedded app directory exists
    FileUtils.mkdir_p(embedded_apps_root)
    # Copy the application code into place
    FileUtils.cp_r("#{Omnibus::Config.source_dir}/#{app_name}", embedded_apps_root)
    # Delete any top-level `.git` directory
    FileUtils.rm_rf("#{embedded_app_dir}/.git")

    # Prepare the environment
    options[:env] ||= {}
    env = with_embedded_path || {}
    env["BUNDLE_GEMFILE"] = gemfile_lock
    options[:env].merge!(env)

    shellout!("#{appbundler_bin} '#{embedded_app_dir}' '#{bin_dir}'", options)
  end
end

#block(name = '<Dynamic Ruby block>', &proc) ⇒ void

This method returns an undefined value.

Execute the given Ruby block at runtime. The block is captured as-is and no validation is performed. As a general rule, you should avoid this method unless you know what you are doing.

Examples:

block do
  # Some complex operation
end
block 'Named operation' do
  # The above name can be used in log output to identify the operation
end

340
341
342
# File 'lib/omnibus/builder.rb', line 340

def block(name = '<Dynamic Ruby block>', &proc)
  build_commands << BuildCommand.new(name, &proc)
end

#buildvoid

This method returns an undefined value.

Execute all the BuildCommand instances, in order, for this builder.


581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
# File 'lib/omnibus/builder.rb', line 581

def build
  log.info(log_key) { 'Starting build' }

  if software.overridden?
    log.info(log_key) do
      "Version overridden from #{software.default_version} to "\
      "#{software.version}"
    end
  end

  measure("Build #{software.name}") do
    build_commands.each do |command|
      execute(command)
    end
  end

  log.info(log_key) { 'Finished build' }
end

#bundle(command, options = {}) ⇒ void

This method returns an undefined value.

Execute the given bundle command against the embedded Ruby's bundler. This command assumes the bundler gem is installed and in the embedded Ruby. You should add a dependency on the bundler software definition if you want to use this command.

Examples:

bundle 'install'

256
257
258
259
260
261
# File 'lib/omnibus/builder.rb', line 256

def bundle(command, options = {})
  build_commands << BuildCommand.new("bundle `#{command}'") do
    bin = windows_safe_path("#{install_dir}/embedded/bin/bundle")
    shellout!("#{bin} #{command}", options)
  end
end

#command(command, options = {}) ⇒ void

This method returns an undefined value.

Execute the given command string or command arguments.

Examples:

command 'make install', env: { 'PATH' => '/my/custom/path' }

80
81
82
83
84
85
86
# File 'lib/omnibus/builder.rb', line 80

def command(command, options = {})
  warn_for_shell_commands(command)

  build_commands << BuildCommand.new("Execute: `#{command}'") do
    shellout!(command, options)
  end
end

#copy(source, destination, options = {}) ⇒ void

This method returns an undefined value.

Copy the given source to the destination. This method accepts a single file or a file pattern to match.


490
491
492
493
494
495
496
497
498
# File 'lib/omnibus/builder.rb', line 490

def copy(source, destination, options = {})
  build_commands << BuildCommand.new("copy `#{source}' to `#{destination}'") do
    Dir.chdir(software.project_dir) do
      FileSyncer.glob(source).each do |file|
        FileUtils.cp_r(file, destination, options)
      end
    end
  end
end

#delete(path, options = {}) ⇒ void

This method returns an undefined value.

Delete the given file or directory on the system. This method uses the equivalent of rm -rf, so you may pass in a specific file or a glob of files.


467
468
469
470
471
472
473
474
475
# File 'lib/omnibus/builder.rb', line 467

def delete(path, options = {})
  build_commands << BuildCommand.new("delete `#{path}'") do
    Dir.chdir(software.project_dir) do
      FileSyncer.glob(path).each do |file|
        FileUtils.rm_rf(file, options)
      end
    end
  end
end

#erb(options = {}) ⇒ void

This method returns an undefined value.

Render the erb template by the given name. This method will search all possible locations for an erb template (such as Config#software_gems).

Examples:

erb source: 'example.erb',
    dest:   '/path/on/disk/to/render'
erb source: 'example.erb',
    dest:   '/path/on/disk/to/render',
    vars:   { foo: 'bar' },
    mode:   '0755'

Options Hash (options):

  • :source (String)

    the name of the patch to apply

  • :dest (String)

    the path on disk where the erb should be rendered

  • :vars (Hash)

    the list of variables to pass to the ERB rendering

  • :mode (String)

    the file mode for the rendered template (default varies by system)


373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/omnibus/builder.rb', line 373

def erb(options = {})
  source = options.delete(:source)
  dest   = options.delete(:dest)
  mode   = options.delete(:mode) || 0644
  vars   = options.delete(:vars) || {}

  raise "Missing required option `:source'!" unless source
  raise "Missing required option `:dest'!"   unless dest

  locations, source_path = find_file('config/templates', source)

  unless source_path
    raise MissingTemplate.new(source, locations)
  end

  erbs << source_path

  block "Render erb `#{source}'" do
    render_template(source_path,
      destination: dest,
      mode:        mode,
      variables:   vars,
    )
  end
end

#gem(command, options = {}) ⇒ void

This method returns an undefined value.

Execute the given Rubygem command against the embedded Rubygems.

Examples:

gem 'install chef'

236
237
238
239
240
241
# File 'lib/omnibus/builder.rb', line 236

def gem(command, options = {})
  build_commands << BuildCommand.new("gem `#{command}'") do
    bin = windows_safe_path("#{install_dir}/embedded/bin/gem")
    shellout!("#{bin} #{command}", options)
  end
end

This method returns an undefined value.

Link the given source to the destination. This method accepts a single file or a file pattern to match


536
537
538
539
540
541
542
543
544
# File 'lib/omnibus/builder.rb', line 536

def link(source, destination, options = {})
  build_commands << BuildCommand.new("link `#{source}' to `#{destination}'") do
    Dir.chdir(software.project_dir) do
      FileSyncer.glob(source).each do |file|
        FileUtils.ln_s(file, destination, options)
      end
    end
  end
end

#make(*args) ⇒ void

This method returns an undefined value.

Execute the given make command. When present, this method will prefer the use of gmake over make. If applicable, this method will also set the `MAKE=gmake` environment variable when gmake is to be preferred.

Examples:

With no arguments

make

With arguments

make 'install'

With custom make bin

make 'install', bin: '/path/to/custom/make'

106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/omnibus/builder.rb', line 106

def make(*args)
  options = args.last.is_a?(Hash) ? args.pop : {}
  command = args.join(' ')

  make = if makebin = options.delete(:bin)
           makebin
         elsif Omnibus.which('gmake')
           env = options.delete(:env) || {}
           env = { 'MAKE' => 'gmake' }.merge(env)
           options[:env] = env
           'gmake'
         else
           'make'
         end

  command("#{make} #{command}".strip, options)
end

#mkdir(directory, options = {}) ⇒ void

This method returns an undefined value.

Make a directory at runtime. This method uses the equivalent of mkdir -p under the covers.


425
426
427
428
429
430
431
# File 'lib/omnibus/builder.rb', line 425

def mkdir(directory, options = {})
  build_commands << BuildCommand.new("mkdir `#{directory}'") do
    Dir.chdir(software.project_dir) do
      FileUtils.mkdir_p(directory, options)
    end
  end
end

#move(source, destination, options = {}) ⇒ void

This method returns an undefined value.

Copy the given source to the destination. This method accepts a single file or a file pattern to match


513
514
515
516
517
518
519
520
521
# File 'lib/omnibus/builder.rb', line 513

def move(source, destination, options = {})
  build_commands << BuildCommand.new("move `#{source}' to `#{destination}'") do
    Dir.chdir(software.project_dir) do
      FileSyncer.glob(source).each do |file|
        FileUtils.mv(file, destination, options)
      end
    end
  end
end

#patch(options = {}) ⇒ void

This method returns an undefined value.

Apply the patch by the given name. This method will search all possible locations for a patch (such as Config#software_gems).

Examples:

patch source: 'ncurses-clang.patch'
patch source: 'patch-ad', plevel: 0

Options Hash (options):

  • :source (String)

    the name of the patch to apply

  • :plevel (Fixnum)

    the level to apply the patch

  • :target (String)

    the destination to apply the patch


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/omnibus/builder.rb', line 147

def patch(options = {})
  source = options.delete(:source)
  plevel = options.delete(:plevel) || 1
  target = options.delete(:target)

  locations, patch_path = find_file('config/patches', source)

  unless patch_path
    raise MissingPatch.new(source, locations)
  end

  # Apply patches nicely on Windows
  patch_path = windows_safe_path(patch_path)

  if target
    command = "cat #{patch_path} | patch -p#{plevel} #{target}"
  else
    command = "patch -d #{software.project_dir} -p#{plevel} -i #{patch_path}"
  end

  patches << patch_path

  build_commands << BuildCommand.new("Apply patch `#{source}'") do
    shellout!(command, options)
  end
end

#rake(command, options = {}) ⇒ void

This method returns an undefined value.

Execute the given Rake command against the embedded Ruby's rake. This command assumes the rake gem has been installed.

Examples:

rake 'test'

314
315
316
317
318
319
# File 'lib/omnibus/builder.rb', line 314

def rake(command, options = {})
  build_commands << BuildCommand.new("rake `#{command}'") do
    bin = windows_safe_path("#{install_dir}/embedded/bin/rake")
    shellout!("#{bin} #{command}", options)
  end
end

#ruby(command, options = {}) ⇒ void

This method returns an undefined value.

Execute the given Ruby command or script against the embedded Ruby.

Examples:

ruby 'setup.rb'

219
220
221
222
223
224
# File 'lib/omnibus/builder.rb', line 219

def ruby(command, options = {})
  build_commands << BuildCommand.new("ruby `#{command}'") do
    bin = windows_safe_path("#{install_dir}/embedded/bin/ruby")
    shellout!("#{bin} #{command}", options)
  end
end

#shasumString

The shasum for this builder object. The shasum is calculated using the following:

- The descriptions of all {BuildCommand} objects
- The digest of all patch files on disk
- The digest of all erb files on disk

610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
# File 'lib/omnibus/builder.rb', line 610

def shasum
  @shasum ||= begin
    digest = Digest::SHA256.new

    build_commands.each do |build_command|
      update_with_string(digest, build_command.description)
    end

    patches.each do |patch_path|
      update_with_file_contents(digest, patch_path)
    end

    erbs.each do |erb_path|
      update_with_file_contents(digest, erb_path)
    end

    digest.hexdigest
  end
end

#sync(source, destination, options = {}) ⇒ true

Copy the files from source to destination, while removing any files in destination that are not present in source.

The method accepts an optional :exclude parameter to ignore files and folders that match the given pattern(s). Note the exclude pattern behaves on paths relative to the given source. If you want to exclude a nested directory, you will need to use something like **/directory.

Examples:

sync "#{project_dir}/**/*.rb", "#{install_dir}/ruby_files"
sync project_dir, "#{install_dir}/files", exclude: '.git'

Options Hash (options):

  • :exclude (String, Array<String>)

    a file, folder, or globbing pattern of files to ignore when syncing

Raises:

  • ArgumentError if the source parameter is not a directory


556
557
558
559
560
561
562
# File 'lib/omnibus/builder.rb', line 556

def sync(source, destination, options = {})
  build_commands << BuildCommand.new("sync `#{source}' to `#{destination}'") do
    Dir.chdir(software.project_dir) do
      FileSyncer.sync(source, destination, options)
    end
  end
end

#touch(file, options = {}) ⇒ void

This method returns an undefined value.

Touch the given filepath at runtime. This method will also ensure the containing directory exists first.


444
445
446
447
448
449
450
451
452
453
# File 'lib/omnibus/builder.rb', line 444

def touch(file, options = {})
  build_commands << BuildCommand.new("touch `#{file}'") do
    Dir.chdir(software.project_dir) do
      parent = File.dirname(file)
      FileUtils.mkdir_p(parent) unless File.directory?(parent)

      FileUtils.touch(file, options)
    end
  end
end

#windows_safe_path(*pieces) ⇒ String

Convert the given path to be appropiate for shelling out on Windows.

Most internal Ruby methods will handle this automatically, but the command method is unable to do so.

Examples:

command "#{windows_safe_path(install_dir)}\\embedded\\bin\\gem"

194
195
196
# File 'lib/omnibus/builder.rb', line 194

def windows_safe_path(*pieces)
  super
end

#workersObject

The maximum number of workers suitable for this system.

See Also:

  • Omnibus::Builder.(Config(Config#workers)

180
181
182
# File 'lib/omnibus/builder.rb', line 180

def workers
  Config.workers
end