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, included

Constructor Details

#initialize(software) ⇒ Builder

Create a new builder object for evaluation.

Parameters:

  • software (Software)

    the software definition that created this builder



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

def initialize(software)
  @software = software
end

Instance Attribute Details

#softwareSoftware (readonly)

Returns the software definition that created this builder

Returns:

  • (Software)

    the software definition that created this builder



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

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'

Parameters:

  • command (String)

    the command to execute

  • options (Hash) (defaults to: {})

    a list of options to pass to the Mixlib::ShellOut instance when it is executed



347
348
349
350
351
352
353
354
355
356
357
# File 'lib/omnibus/builder.rb', line 347

def appbundle(app_name, options = {})
  build_commands << BuildCommand.new("appbundle `#{app_name}'") do
    bin_dir            = "#{install_dir}/bin"
    appbundler_bin     = embedded_bin('appbundler')

    # Ensure the main bin dir exists
    FileUtils.mkdir_p(bin_dir)

    shellout!("#{appbundler_bin} '#{Omnibus::Config.source_dir}/#{app_name}' '#{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

Parameters:

  • command (String)

    the command to execute

  • options (Hash)

    a list of options to pass to the Mixlib::ShellOut instance when it is executed



396
397
398
# File 'lib/omnibus/builder.rb', line 396

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.



655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
# File 'lib/omnibus/builder.rb', line 655

def build
  log.info(log_key) { 'Starting build' }
  shasum # ensure shashum is calculated before build since the build can alter the shasum
  log.internal(log_key) { "Cached builder checksum before build: #{shasum}"}
  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'

Parameters:

  • command (String)

    the command to execute

  • options (Hash) (defaults to: {})

    a list of options to pass to the Mixlib::ShellOut instance when it is executed



327
328
329
330
331
332
# File 'lib/omnibus/builder.rb', line 327

def bundle(command, options = {})
  build_commands << BuildCommand.new("bundle `#{command}'") do
    bin = 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' }

Parameters:

  • command (String)

    the command to execute

  • options (Hash) (defaults to: {})

    a list of options to pass to the Mixlib::ShellOut instance when it is executed



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

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

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

#configure(*args) ⇒ void

This method returns an undefined value.

Run a prexisting “./configure” script that was generated by autotools. On windows, this will run configure within an msys bash shell with the given arguments. –host is also set on your behalf based on windows_arch. A default prefix of “#install_bin/embedded” is appended.

On POSIX systems, this results in:

./configure --prefix=/path/to/embedded

On Windows 64-bit, this results in:

./configure --host=x86_64-w64-mingw32 --prefix=C:/path/to/embedded

Note that the windows case uses a windows compabile path with forward slashes - not an msys path. Ensure that the final Makefile is happy with this and doesn't perform path gymnastics on it. Don't pass \ paths unless you particularly enjoy discovering exactly home many times configure and the Makefile it generates sends your path back and forth through bash/sh, mingw32 native binaries and msys binaries and how many backslashes it takes for you to quit software development.

The path to configure must be a “unix-y” path for both windows and posix as this path is run under an msys bash shell on windows. Prefer relative paths lest you incur the wrath of the msys path gods for they are not kind, just or benevolent.

Examples:

With no arguments

configure

With custom arguments

configure '--enable-shared'

With custom location of configure bin

configure '--enable-shared', bin: '../foo/configure'

Parameters:

  • command (String)

    the command to execute

  • options (Hash)

    a list of options to pass to the Mixlib::ShellOut instance when it is executed



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/omnibus/builder.rb', line 163

def configure(*args)
  options = args.last.is_a?(Hash) ? args.pop : {}

  configure = options.delete(:bin) || './configure'
  configure_cmd = [configure]

  # Pass the host platform as well. msys is configured for 32-bits even
  # if the actual installed compiler has 64-bit support.
  configure_cmd << '--host=x86_64-w64-mingw32' if windows? && !windows_arch_i386?

  # Accept a prefix override if provided. Can be set to '' to suppress
  # this functionality.
  prefix = options.delete(:prefix) || "#{install_dir}/embedded"
  configure_cmd << "--prefix=#{prefix}" if prefix && prefix != ''

  configure_cmd.concat args
  configure_cmd = configure_cmd.join(' ').strip

  options[:in_msys_bash] = true
  command(configure_cmd, options)
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.

Parameters:

  • source (String)

    the path on disk to copy from

  • destination (String)

    the path on disk to copy to

  • directory (String)

    the name or path of the directory to create

  • options (Hash) (defaults to: {})

    the list of options to pass to the underlying FileUtils call



546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
# File 'lib/omnibus/builder.rb', line 546

def copy(source, destination, options = {})
  command = "copy `#{source}' to `#{destination}'"
  build_commands << BuildCommand.new(command) do
    Dir.chdir(software.project_dir) do
      files = FileSyncer.glob(source)
      if files.empty?
        log.warn(log_key) {"no matched files for glob #{command}"}
      else
        files.each do |file|
          FileUtils.cp_r(file, destination, options)
        end
      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.

Parameters:

  • path (String)

    the path of the file to delete

  • directory (String)

    the name or path of the directory to create

  • options (Hash) (defaults to: {})

    the list of options to pass to the underlying FileUtils call



523
524
525
526
527
528
529
530
531
# File 'lib/omnibus/builder.rb', line 523

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'

Parameters:

  • options (Hash) (defaults to: {})

    the list of options

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)



429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
# File 'lib/omnibus/builder.rb', line 429

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'

Parameters:

  • command (String)

    the command to execute

  • options (Hash) (defaults to: {})

    a list of options to pass to the Mixlib::ShellOut instance when it is executed



307
308
309
310
311
312
# File 'lib/omnibus/builder.rb', line 307

def gem(command, options = {})
  build_commands << BuildCommand.new("gem `#{command}'") do
    bin = 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

Parameters:

  • source (String)

    the path on disk to link from

  • destination (String)

    the path on disk to link to

  • directory (String)

    the name or path of the directory to create

  • options (Hash) (defaults to: {})

    the list of options to pass to the underlying FileUtils call



604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
# File 'lib/omnibus/builder.rb', line 604

def link(source, destination, options = {})
  command = "link `#{source}' to `#{destination}'"
  build_commands << BuildCommand.new(command) do
    Dir.chdir(software.project_dir) do
      files = FileSyncer.glob(source)
      if files.empty?
        log.warn(log_key) {"no matched files for glob #{command}"}
      else
        files.each do |file|
          FileUtils.ln_s(file, destination, options)
        end
      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.

On windows you need to have the msys-base package (or some equivalent) before you can invoke this.

Examples:

With no arguments

make

With arguments

make 'install'

With custom make bin

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

Parameters:

  • command (String)

    the command to execute

  • options (Hash)

    a list of options to pass to the Mixlib::ShellOut instance when it is executed



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/omnibus/builder.rb', line 110

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

  make = options.delete(:bin) ||
    # Prefer gmake on non-windows environments.
    if !windows? && Omnibus.which('gmake')
      env = options.delete(:env) || {}
      env = { 'MAKE' => 'gmake' }.merge(env)
      options[:env] = env
      'gmake'
    else
      'make'
    end

  options[:in_msys_bash] = true
  make_cmd = ([make] + args).join(' ').strip
  command(make_cmd, 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.

Parameters:

  • directory (String)

    the name or path of the directory to create

  • options (Hash) (defaults to: {})

    the list of options to pass to the underlying FileUtils call



481
482
483
484
485
486
487
# File 'lib/omnibus/builder.rb', line 481

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.

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

Parameters:

  • source (String)

    the path on disk to move from

  • destination (String)

    the path on disk to move to

  • directory (String)

    the name or path of the directory to create

  • options (Hash) (defaults to: {})

    the list of options to pass to the underlying FileUtils call



575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
# File 'lib/omnibus/builder.rb', line 575

def move(source, destination, options = {})
  command = "move `#{source}' to `#{destination}'"
  build_commands << BuildCommand.new(command) do
    Dir.chdir(software.project_dir) do
      files = FileSyncer.glob(source)
      if files.empty?
        log.warn(log_key) {"no matched files for glob #{command}"}
      else
        files.each do |file|
          FileUtils.mv(file, destination, options)
        end
      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).

On windows, you must have the the patch package installed before you can invoke this.

Examples:

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

Parameters:

  • options (Hash) (defaults to: {})

    the list of options

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



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/omnibus/builder.rb', line 211

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

  # Using absolute paths to the patch when invoking patch from within msys
  # is going to end is tears and table-flips. Use relative paths instead.
  # It's windows - we don't reasonably expect symlinks to show up any-time
  # soon and if you're using junction points, you're on your own.
  clean_patch_path = patch_path
  if windows?
    clean_patch_path = Pathname.new(patch_path).relative_path_from(
      Pathname.new(software.project_dir)).to_s
  end

  if target
    patch_cmd = "cat #{clean_patch_path} | patch -p#{plevel} #{target}"
  else
    patch_cmd = "patch -p#{plevel} -i #{clean_patch_path}"
  end

  patches << patch_path
  options[:in_msys_bash] = true
  build_commands << BuildCommand.new("Apply patch `#{source}'") do
    shellout!(patch_cmd, 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'

Parameters:

  • command (String)

    the command to execute

  • options (Hash) (defaults to: {})

    a list of options to pass to the Mixlib::ShellOut instance when it is executed



370
371
372
373
374
375
# File 'lib/omnibus/builder.rb', line 370

def rake(command, options = {})
  build_commands << BuildCommand.new("rake `#{command}'") do
    bin = 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'

Parameters:

  • command (String)

    the command to execute

  • options (Hash) (defaults to: {})

    a list of options to pass to the Mixlib::ShellOut instance when it is executed



290
291
292
293
294
295
# File 'lib/omnibus/builder.rb', line 290

def ruby(command, options = {})
  build_commands << BuildCommand.new("ruby `#{command}'") do
    bin = 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

Returns:

  • (String)


685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
# File 'lib/omnibus/builder.rb', line 685

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'

Parameters:

  • source (String)

    the path on disk to sync from

  • destination (String)

    the path on disk to sync to

Options Hash (options):

  • :exclude (String, Array<String>)

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

Returns:

  • (true)

Raises:

  • ArgumentError if the source parameter is not a directory



630
631
632
633
634
635
636
# File 'lib/omnibus/builder.rb', line 630

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.

Parameters:

  • file (String)

    the path of the file to touch

  • directory (String)

    the name or path of the directory to create

  • options (Hash) (defaults to: {})

    the list of options to pass to the underlying FileUtils call



500
501
502
503
504
505
506
507
508
509
# File 'lib/omnibus/builder.rb', line 500

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"

Parameters:

  • pieces (String, Array<String>)

    the pieces of the path to join and fix

Returns:

  • (String)

    the path with applied changes



265
266
267
# File 'lib/omnibus/builder.rb', line 265

def windows_safe_path(*pieces)
  super
end

#workersObject

The maximum number of workers suitable for this system.

See Also:

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


251
252
253
# File 'lib/omnibus/builder.rb', line 251

def workers
  Config.workers
end