Class: Omnibus::Builder
- Inherits:
-
Object
- Object
- Omnibus::Builder
- Includes:
- Cleanroom, Digestable, Instrumentation, Logging, Templating, Util
- Defined in:
- lib/omnibus/builder.rb
Defined Under Namespace
Classes: BuildCommand
Constant Summary
Constants included from Util
Instance Attribute Summary collapse
-
#software ⇒ Software
readonly
The software definition that created this builder.
System DSL methods collapse
-
#command(command, options = {}) ⇒ void
Execute the given command string or command arguments.
-
#compiler_safe_path(*pieces) ⇒ String
Convert the given path to be appropriate for usage with the given compiler.
-
#configure(*args) ⇒ void
Run a prexisting “./configure” script that was generated by autotools.
-
#make(*args) ⇒ void
Execute the given make command.
-
#patch(options = {}) ⇒ void
Apply the patch by the given name.
-
#windows_safe_path(*pieces) ⇒ String
Convert the given path to be appropiate for shelling out on Windows.
-
#workers ⇒ Object
The maximum number of workers suitable for this system.
Go DSL methods collapse
-
#go(command, options = {}) ⇒ void
Execute the given Go command or script against the embedded Go.
Ruby DSL methods collapse
-
#appbundle(software_name, options = {}) ⇒ void
Execute the given appbundler command against the embedded Ruby’s appbundler.
-
#block(name = "<Dynamic Ruby block>", &proc) ⇒ void
Execute the given Ruby block at runtime.
-
#bundle(command, options = {}) ⇒ void
Execute the given bundle command against the embedded Ruby’s bundler.
-
#erb(options = {}) ⇒ void
Render the erb template by the given name.
-
#gem(command, options = {}) ⇒ void
Execute the given Rubygem command against the embedded Rubygems.
-
#rake(command, options = {}) ⇒ void
Execute the given Rake command against the embedded Ruby’s rake.
-
#ruby(command, options = {}) ⇒ void
Execute the given Ruby command or script against the embedded Ruby.
File system DSL methods collapse
-
#copy(source, destination, options = {}) ⇒ void
Copy the given source to the destination.
-
#delete(path, options = {}) ⇒ void
Delete the given file or directory on the system.
-
#link(source, destination, options = {}) ⇒ void
Link the given source to the destination.
-
#mkdir(directory, options = {}) ⇒ void
Make a directory at runtime.
-
#move(source, destination, options = {}) ⇒ void
Move the given source to the destination.
-
#strip(path) ⇒ void
Strip symbols from the given file or directory on the system.
-
#sync(source, destination, options = {}) ⇒ true
Copy the files from
source
todestination
, while removing any files indestination
that are not present insource
. -
#touch(file, options = {}) ⇒ void
Touch the given filepath at runtime.
-
#update_config_guess(target: ".", install: %i{config_guess config_sub})) ⇒ Object
Helper method to update config_guess in the software’s source directory.
Public API collapse
-
#build ⇒ void
Execute all the BuildCommand instances, in order, for this builder.
-
#shasum ⇒ String
The shasum for this builder object.
Instance Method Summary collapse
-
#initialize(software) ⇒ Builder
constructor
Create a new builder object for evaluation.
Methods included from Util
#copy_file, #create_directory, #create_file, #create_link, included, #path_key, #remove_directory, #remove_file, #retry_block, #shellout
Methods included from Templating
included, #render_template, #render_template_content
Methods included from Logging
Methods included from Instrumentation
Methods included from Digestable
#digest, #digest_directory, included
Constructor Details
#initialize(software) ⇒ Builder
Create a new builder object for evaluation.
57 58 59 |
# File 'lib/omnibus/builder.rb', line 57 def initialize(software) @software = software end |
Instance Attribute Details
#software ⇒ Software (readonly)
Returns 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(software_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.
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 |
# File 'lib/omnibus/builder.rb', line 411 def appbundle(software_name, = {}) build_commands << BuildCommand.new("appbundle `#{software_name}'") do bin_dir = "#{install_dir}/bin" appbundler_bin = ("appbundler") lockdir = .delete(:lockdir) gem = .delete(:gem) without = .delete(:without) extra_bin_files = .delete(:extra_bin_files) lockdir ||= begin app_software = project.softwares.find do |p| p.name == software_name end if app_software.nil? raise "could not find software definition for #{software_name}, add a dependency to it, or pass a lockdir argument to appbundle command." end app_software.project_dir end command = [ appbundler_bin, "'#{lockdir}'", "'#{bin_dir}'" ] # This option is almost entirely for support of ChefDK and enables transitive gemfile lock construction in order # to be able to decouple the dev gems for all the different components of ChefDK. AKA: don't use it outside of # ChefDK. You should also explicitly specify the lockdir when going down this road. command << [ "'#{gem}'" ] if gem # FIXME: appbundler lacks support for this argument when not also specifying the gem (2-arg appbundling lacks support) # (if you really need this bug fixed, though, fix it in appbundler, don't try using the 3-arg version to try to # get `--without` support, you will likely wind up going down a sad path). command << [ "--without", without.join(",") ] unless without.nil? command << [ "--extra-bin-files", extra_bin_files.join(",") ] unless extra_bin_files.nil? || extra_bin_files.empty? # Ensure the main bin dir exists FileUtils.mkdir_p(bin_dir) shellout!(command.join(" "), ) 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.
491 492 493 |
# File 'lib/omnibus/builder.rb', line 491 def block(name = "<Dynamic Ruby block>", &proc) build_commands << BuildCommand.new(name, &proc) end |
#build ⇒ void
This method returns an undefined value.
Execute all the BuildCommand instances, in order, for this builder.
805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 |
# File 'lib/omnibus/builder.rb', line 805 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 || "n/a"} 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.
387 388 389 390 391 392 |
# File 'lib/omnibus/builder.rb', line 387 def bundle(command, = {}) build_commands << BuildCommand.new("bundle `#{command}'") do bin = ("bundle") shellout!("#{bin} #{command}", **) end end |
#command(command, options = {}) ⇒ void
This method returns an undefined value.
Execute the given command string or command arguments.
81 82 83 84 85 86 87 |
# File 'lib/omnibus/builder.rb', line 81 def command(command, = {}) warn_for_shell_commands(command) build_commands << BuildCommand.new("Execute: `#{command}'") do shellout!(command, **) end end |
#compiler_safe_path(*pieces) ⇒ String
Convert the given path to be appropriate for usage with the given compiler
Some compilers require paths to be formatted in certain ways. This helper takes in the standard Omnibus-style path and ensures that it is passed correctly.
290 291 292 |
# File 'lib/omnibus/builder.rb', line 290 def compiler_safe_path(*pieces) super 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. –build is also set on your behalf based on windows_arch. A default prefix of “#install_bin/embedded” is appended. It is important to set –build rather than –host because by default, –build also sets –host but it doesn’t trigger “cross-compilation” mode in most configure scripts. Triggering this mode can confuse certain software projects like Ruby which depend on the build platform in its mkmf scripts.
On POSIX systems, this results in:
./configure --prefix=/path/to/embedded
On Windows 64-bit, this results in:
./configure --build=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 how 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.
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/omnibus/builder.rb', line 167 def configure(*args) = args.last.is_a?(Hash) ? args.pop : {} configure = .delete(:bin) || "./configure" configure_cmd = [configure] # Pass the host platform as well. Different versions of config.guess # arrive at differently terrible wild ass guesses for what MSYSTEM=MINGW64 # means. This can be anything from x86_64-pc-mingw64 to i686-pc-mingw32 # which doesn't even make any sense... if windows? platform = windows_arch_i386? ? "i686-w64-mingw32" : "x86_64-w64-mingw32" configure_cmd << "--build=#{platform}" end # Accept a prefix override if provided. Can be set to '' to suppress # this functionality. prefix = .delete(:prefix) || "#{install_dir}/embedded" configure_cmd << "--prefix=#{prefix}" if prefix && prefix != "" configure_cmd.concat args configure_cmd = configure_cmd.join(" ").strip [:in_msys_bash] = true command(configure_cmd, ) 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.
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 |
# File 'lib/omnibus/builder.rb', line 663 def copy(source, destination, = {}) 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, **) 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.
617 618 619 620 621 622 623 624 625 |
# File 'lib/omnibus/builder.rb', line 617 def delete(path, = {}) build_commands << BuildCommand.new("delete `#{path}'") do Dir.chdir(software.project_dir) do FileSyncer.glob(path).each do |file| FileUtils.rm_rf(file, **) 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).
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 |
# File 'lib/omnibus/builder.rb', line 524 def erb( = {}) source = .delete(:source) dest = .delete(:dest) mode = .delete(:mode) || 0644 vars = .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.
367 368 369 370 371 372 |
# File 'lib/omnibus/builder.rb', line 367 def gem(command, = {}) build_commands << BuildCommand.new("gem `#{command}'") do bin = ("gem") shellout!("#{bin} #{command}", **) end end |
#go(command, options = {}) ⇒ void
This method returns an undefined value.
Execute the given Go command or script against the embedded Go.
315 316 317 318 319 320 321 322 323 324 325 326 327 |
# File 'lib/omnibus/builder.rb', line 315 def go(command, = {}) build_commands << BuildCommand.new("go `#{command}'") do bin = windows? ? windows_safe_path("#{install_dir}/embedded/go/bin/go") : ("go") # Check if we are building a go binary and then check if we are on # Red Hat or CentOS so we build the binary properly with a build-id if command.start_with?("build", " build") && rhel? command << " -ldflags=-linkmode=external" end shellout!("#{bin} #{command}", ) end end |
#link(source, destination, options = {}) ⇒ void
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
721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 |
# File 'lib/omnibus/builder.rb', line 721 def link(source, destination, = {}) command = "link `#{source}' to `#{destination}'" build_commands << BuildCommand.new(command) do Dir.chdir(software.project_dir) do if .delete(:unchecked) FileUtils.ln_s(source, destination, **) else 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, **) end 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.
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) = args.last.is_a?(Hash) ? args.pop : {} make = .delete(:bin) || # Prefer gmake on non-windows environments. if !windows? && Omnibus.which("gmake") env = .delete(:env) || {} env = { "MAKE" => "gmake" }.merge(env) [:env] = env "gmake" else "make" end [:in_msys_bash] = true make_cmd = ([make] + args).join(" ").strip command(make_cmd, ) 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.
575 576 577 578 579 580 581 |
# File 'lib/omnibus/builder.rb', line 575 def mkdir(directory, = {}) build_commands << BuildCommand.new("mkdir `#{directory}'") do Dir.chdir(software.project_dir) do FileUtils.mkdir_p(directory, **) 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
692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 |
# File 'lib/omnibus/builder.rb', line 692 def move(source, destination, = {}) 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, **) 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.
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/omnibus/builder.rb', line 220 def patch( = {}) source = .delete(:source) plevel = .delete(:plevel) || 1 target = .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 [:in_msys_bash] = true build_commands << BuildCommand.new("Apply patch `#{source}'") do shellout!(patch_cmd, **) 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.
465 466 467 468 469 470 |
# File 'lib/omnibus/builder.rb', line 465 def rake(command, = {}) build_commands << BuildCommand.new("rake `#{command}'") do bin = ("rake") shellout!("#{bin} #{command}", **) end end |
#ruby(command, options = {}) ⇒ void
This method returns an undefined value.
Execute the given Ruby command or script against the embedded Ruby.
350 351 352 353 354 355 |
# File 'lib/omnibus/builder.rb', line 350 def ruby(command, = {}) build_commands << BuildCommand.new("ruby `#{command}'") do bin = ("ruby") shellout!("#{bin} #{command}", **) end end |
#shasum ⇒ String
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
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 |
# File 'lib/omnibus/builder.rb', line 835 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 |
#strip(path) ⇒ void
This method returns an undefined value.
Strip symbols from the given file or directory on the system. This method uses find and passes the matched files to strip through xargs, ignoring errors. So one may pass in a specific file/directory or a glob of files.
638 639 640 641 642 643 644 645 646 647 648 |
# File 'lib/omnibus/builder.rb', line 638 def strip(path) regexp_ends = ".*(" + IGNORED_ENDINGS.map { |e| e.gsub(/\./, '\.') }.join("|") + ")$" regexp_patterns = IGNORED_SUBSTRINGS.map { |e| ".*" + e.gsub(%r{/}, '\/') + ".*" }.join("|") regexp = regexp_ends + "|" + regexp_patterns # Do not actually care if strip runs on non-strippable file, as its a no-op. Hence the `|| true` appended. # Do want to avoid stripping files unneccessarily so as not to slow down build process. find_command = "find #{path}/ -type f -regextype posix-extended ! -regex \"#{regexp}\" | xargs strip || true" = { in_msys_bash: true } command(find_command, ) 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.
751 752 753 754 755 756 757 |
# File 'lib/omnibus/builder.rb', line 751 def sync(source, destination, = {}) build_commands << BuildCommand.new("sync `#{source}' to `#{destination}'") do Dir.chdir(software.project_dir) do FileSyncer.sync(source, destination, **) 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.
594 595 596 597 598 599 600 601 602 603 |
# File 'lib/omnibus/builder.rb', line 594 def touch(file, = {}) 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, **) end end end |
#update_config_guess(target: ".", install: %i{config_guess config_sub})) ⇒ Object
Helper method to update config_guess in the software’s source directory. You should add a dependency on the config_guess
software definition if you want to use this command.
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 |
# File 'lib/omnibus/builder.rb', line 771 def update_config_guess(target: ".", install: %i{config_guess config_sub}) build_commands << BuildCommand.new("update_config_guess `target: #{target} install: #{install.inspect}'") do config_guess_dir = "#{install_dir}/embedded/lib/config_guess" %w{config.guess config.sub}.each do |c| unless File.exist?(File.join(config_guess_dir, c)) raise "Can not find #{c}. Make sure you add a dependency on 'config_guess' in your software definition" end end destination = File.join(software.project_dir, target) FileUtils.mkdir_p(destination) FileUtils.cp_r("#{config_guess_dir}/config.guess", destination) if install.include? :config_guess FileUtils.cp_r("#{config_guess_dir}/config.sub", destination) if install.include? :config_sub 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.
275 276 277 |
# File 'lib/omnibus/builder.rb', line 275 def windows_safe_path(*pieces) super end |