class MiniPortile

Constants

VERSION

Attributes

configure_options[W]
files[RW]
host[RW]
logger[RW]
name[R]
original_host[R]
patch_files[RW]
target[RW]
version[R]

Public Class Methods

new(name, version) click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 36
def initialize(name, version)
  @name = name
  @version = version
  @target = 'ports'
  @files = []
  @patch_files = []
  @log_files = {}
  @logger = STDOUT

  @original_host = @host = detect_host
end

Public Instance Methods

activate() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 156
def activate
  lib_path = File.join(port_path, "lib")
  vars = {
    'PATH'          => File.join(port_path, 'bin'),
    'CPATH'         => File.join(port_path, 'include'),
    'LIBRARY_PATH'  => lib_path
  }.reject { |env, path| !File.directory?(path) }

  output "Activating #{@name} #{@version} (from #{port_path})..."
  vars.each do |var, path|
    full_path = File.expand_path(path)

    # turn into a valid Windows path (if required)
    full_path.gsub!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR

    # save current variable value
    old_value = ENV[var] || ''

    unless old_value.include?(full_path)
      ENV[var] = "#{full_path}#{File::PATH_SEPARATOR}#{old_value}"
    end
  end

  # rely on LDFLAGS when cross-compiling
  if File.exist?(lib_path) && (@host != @original_host)
    full_path = File.expand_path(lib_path)

    old_value = ENV.fetch("LDFLAGS", "")

    unless old_value.include?(full_path)
      ENV["LDFLAGS"] = "-L#{full_path} #{old_value}".strip
    end
  end
end
apply_patch(patch_file) click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 61
def apply_patch(patch_file)
  (
    # Not a class variable because closures will capture self.
    @apply_patch ||=
    case
    when which('git')
      lambda { |file|
        message "Running git apply with #{file}... "
        # By --work-tree=. git-apply uses the current directory as
        # the project root and will not search upwards for .git.
        execute('patch', ["git", "--work-tree=.", "apply", "--whitespace=warn", file], :initial_message => false)
      }
    when which('patch')
      lambda { |file|
        message "Running patch with #{file}... "
        execute('patch', ["patch", "-p1", "-i", file], :initial_message => false)
      }
    else
      raise "Failed to complete patch task; patch(1) or git(1) is required."
    end
  ).call(patch_file)
end
compile() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 110
def compile
  execute('compile', make_cmd)
end
configure() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 95
def configure
  return if configured?

  md5_file = File.join(tmp_path, 'configure.md5')
  digest   = Digest::MD5.hexdigest(computed_options.to_s)
  File.open(md5_file, "w") { |f| f.write digest }

  if RUBY_PLATFORM=~/mingw|mswin/
    # Windows doesn't recognize the shebang.
    execute('configure', %w(sh ./configure) + computed_options)
  else
    execute('configure', %w(./configure) + computed_options)
  end
end
configure_options() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 91
def configure_options
  @configure_options ||= configure_defaults
end
configured?() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 127
def configured?
  configure = File.join(work_path, 'configure')
  makefile  = File.join(work_path, 'Makefile')
  md5_file  = File.join(tmp_path, 'configure.md5')

  stored_md5  = File.exist?(md5_file) ? File.read(md5_file) : ""
  current_md5 = Digest::MD5.hexdigest(computed_options.to_s)

  (current_md5 == stored_md5) && newer?(makefile, configure)
end
cook() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 145
def cook
  download unless downloaded?
  extract
  patch
  configure unless configured?
  compile
  install unless installed?

  return true
end
download() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 48
def download
  files_hashs.each do |file|
    download_file(file[:url], file[:local_path])
    verify_file(file)
  end
end
downloaded?() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 119
def downloaded?
  missing = files_hashs.detect do |file|
    !File.exist?(file[:local_path])
  end

  missing ? false : true
end
extract() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 55
def extract
  files_hashs.each do |file|
    extract_file(file[:local_path], tmp_path)
  end
end
install() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 114
def install
  return if installed?
  execute('install', %Q(#{make_cmd} install))
end
installed?() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 138
def installed?
  makefile  = File.join(work_path, 'Makefile')
  target_dir = Dir.glob("#{port_path}/*").find { |d| File.directory?(d) }

  newer?(target_dir, makefile)
end
patch() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 84
def patch
  @patch_files.each do |full_path|
    next unless File.exist?(full_path)
    apply_patch(full_path)
  end
end
path() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 191
def path
  File.expand_path(port_path)
end

Private Instance Methods

archives_path() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 205
def archives_path
  "#{@target}/archives"
end
computed_options() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 225
def computed_options
  [
    configure_options,     # customized or default options
    configure_prefix,      # installation target
  ].flatten
end
configure_defaults() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 213
def configure_defaults
  [
    "--host=#{@host}",    # build for specific target (host)
    "--enable-static",    # build static library
    "--disable-shared"    # disable generation of shared object
  ]
end
configure_prefix() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 221
def configure_prefix
  "--prefix=#{File.expand_path(port_path)}"
end
detect_host() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 309
def detect_host
  return @detect_host if defined?(@detect_host)

  begin
    ENV["LC_ALL"], old_lc_all = "C", ENV["LC_ALL"]

    output = %x`#{gcc_cmd} -v 2>&1`
    if m = output.match(/^Target\: (.*)$/)
      @detect_host = m[1]
    end

    @detect_host
  ensure
    ENV["LC_ALL"] = old_lc_all
  end
end
download_file(url, full_path, count = 3) click to toggle source

Slighly modified from RubyInstaller uri_ext, Rubinius configure and adaptations of Wayne's RailsInstaller

# File lib/mini_portile2/mini_portile.rb, line 393
def download_file(url, full_path, count = 3)
  return if File.exist?(full_path)
  uri = URI.parse(url)

  case uri.scheme.downcase
  when /ftp/
    download_file_ftp(uri, full_path)
  when /http|https/
    download_file_http(url, full_path, count)
  when /file/
    download_file_file(uri, full_path)
  else
    raise ArgumentError.new("Unsupported protocol for #{url}")
  end
rescue Exception => e
  File.unlink full_path if File.exist?(full_path)
  raise e
end
download_file_file(uri, full_path) click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 453
def download_file_file(uri, full_path)
  FileUtils.mkdir_p File.dirname(full_path)
  FileUtils.cp uri.path, full_path
end
download_file_ftp(uri, full_path) click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 458
def download_file_ftp(uri, full_path)
  filename = File.basename(uri.path)
  with_tempfile(filename, full_path) do |temp_file|
    progress = 0
    total = 0
    params = {
      :content_length_proc => lambda{|length| total = length },
      :progress_proc => lambda{|bytes|
        new_progress = (bytes * 100) / total
        message "\rDownloading %s (%3d%%) " % [filename, new_progress]
        progress = new_progress
      }
    }
    if ENV["ftp_proxy"]
      _, userinfo, _p_host, _p_port = URI.split(ENV['ftp_proxy'])
      if userinfo
        proxy_user, proxy_pass = userinfo.split(/:/).map{|s| CGI.unescape(s) }
        params[:proxy_http_basic_authentication] =
          [ENV['ftp_proxy'], proxy_user, proxy_pass]
      end
    end
    OpenURI.open_uri(uri, 'rb', params) do |io|
      temp_file << io.read
    end
    output
  end
rescue Net::FTPError
  return false
end
download_file_http(url, full_path, count = 3) click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 412
def download_file_http(url, full_path, count = 3)
  filename = File.basename(full_path)
  with_tempfile(filename, full_path) do |temp_file|
    progress = 0
    total = 0
    params = {
      "Accept-Encoding" => 'identity',
      :content_length_proc => lambda{|length| total = length },
      :progress_proc => lambda{|bytes|
        new_progress = (bytes * 100) / total
        message "\rDownloading %s (%3d%%) " % [filename, new_progress]
        progress = new_progress
      }
    }
    proxy_uri = URI.parse(url).scheme.downcase == 'https' ?
                ENV["https_proxy"] :
                ENV["http_proxy"]
    if proxy_uri
      _, userinfo, _p_host, _p_port = URI.split(proxy_uri)
      if userinfo
        proxy_user, proxy_pass = userinfo.split(/:/).map{|s| CGI.unescape(s) }
        params[:proxy_http_basic_authentication] =
          [proxy_uri, proxy_user, proxy_pass]
      end
    end
    begin
      OpenURI.open_uri(url, 'rb', params) do |io|
        temp_file << io.read
      end
      output
    rescue OpenURI::HTTPRedirect => redirect
      raise "Too many redirections for the original URL, halting." if count <= 0
      count = count - 1
      return download_file(redirect.url, full_path, count - 1)
    rescue => e
      output e.message
      return false
    end
  end
end
execute(action, command, options={}) click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 334
def execute(action, command, options={})
  log_out    = log_file(action)

  Dir.chdir (options.fetch(:cd){ work_path }) do
    if options.fetch(:initial_message){ true }
      message "Running '#{action}' for #{@name} #{@version}... "
    end

    if Process.respond_to?(:spawn) && ! RbConfig.respond_to?(:java)
      args = [command].flatten + [{[:out, :err]=>[log_out, "a"]}]
      pid = spawn(*args)
      Process.wait(pid)
    else
      redirected = if command.kind_of?(Array)
                     %Q{#{command.map(&:shellescape).join(" ")} > #{log_out.shellescape} 2>&1}
                   else
                     %Q{#{command} > #{log_out.shellescape} 2>&1}
                   end
      system redirected
    end

    if $?.success?
      output "OK"
      return true
    else
      if File.exist? log_out
        output "ERROR, review '#{log_out}' to see what happened. Last lines are:"
        output("=" * 72)
        log_lines = File.readlines(log_out)
        output(log_lines[-[log_lines.length, 20].min .. -1])
        output("=" * 72)
      end
      raise "Failed to complete #{action} task"
    end
  end
end
extract_file(file, target) click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 326
def extract_file(file, target)
  filename = File.basename(file)
  FileUtils.mkdir_p target

  message "Extracting #{filename} into #{target}... "
  execute('extract', [tar_exe, "#{tar_compression_switch(filename)}xf", file, "-C", target], {:cd => Dir.pwd, :initial_message => false})
end
files_hashs() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 232
def files_hashs
  @files.map do |file|
    hash = case file
    when String
      { :url => file }
    when Hash
      file.dup
    else
      raise ArgumentError, "files must be an Array of Stings or Hashs"
    end

    url = hash.fetch(:url){ raise ArgumentError, "no url given" }
    filename = File.basename(url)
    hash[:local_path] = File.join(archives_path, filename)
    hash
  end
end
gcc_cmd() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 498
def gcc_cmd
  cc = ENV["CC"] || RbConfig::CONFIG["CC"] || "gcc"
  return cc.dup
end
log_file(action) click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 264
def log_file(action)
  @log_files[action] ||=
    File.expand_path("#{action}.log", tmp_path).tap { |file|
      File.unlink(file) if File.exist?(file)
    }
end
make_cmd() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 503
def make_cmd
  m = ENV['MAKE'] || ENV['make'] || 'make'
  return m.dup
end
message(text) click to toggle source

print out a message with the logger

# File lib/mini_portile2/mini_portile.rb, line 380
def message(text)
  @logger.print text
  @logger.flush
end
newer?(target, checkpoint) click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 371
def newer?(target, checkpoint)
  if (target && File.exist?(target)) && (checkpoint && File.exist?(checkpoint))
    File.mtime(target) > File.mtime(checkpoint)
  else
    false
  end
end
output(text = "") click to toggle source

print out a message using the logger but return to a new line

# File lib/mini_portile2/mini_portile.rb, line 386
def output(text = "")
  @logger.puts text
  @logger.flush
end
port_path() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 201
def port_path
  "#{@target}/#{@host}/#{@name}/#{@version}"
end
tar_compression_switch(filename) click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 279
def tar_compression_switch(filename)
  case File.extname(filename)
    when '.gz', '.tgz'
      'z'
    when '.bz2', '.tbz2'
      'j'
    when '.Z'
      'Z'
    else
      ''
  end
end
tar_exe() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 271
def tar_exe
  @@tar_exe ||= begin
    %w[gtar bsdtar tar basic-bsdtar].find { |c|
      which(c)
    }
  end
end
tmp_path() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 197
def tmp_path
  "tmp/#{@host}/ports/#{@name}/#{@version}"
end
verify_file(file) click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 250
def verify_file(file)
  digest = case
    when exp=file[:sha256] then Digest::SHA256
    when exp=file[:sha1] then Digest::SHA1
    when exp=file[:md5] then Digest::MD5
  end
  if digest
    is = digest.file(file[:local_path]).hexdigest
    unless is == exp.downcase
      raise "Downloaded file '#{file[:local_path]}' has wrong hash: expected: #{exp} is: #{is}"
    end
  end
end
which(cmd) click to toggle source

From: stackoverflow.com/a/5471032/7672 Thanks, Mislav!

Cross-platform way of finding an executable in the $PATH.

which('ruby') #=> /usr/bin/ruby
# File lib/mini_portile2/mini_portile.rb, line 298
def which(cmd)
  exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
  ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
    exts.each { |ext|
      exe = File.join(path, "#{cmd}#{ext}")
      return exe if File.executable? exe
    }
  end
  return nil
end
with_tempfile(filename, full_path) { |temp_file| ... } click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 488
def with_tempfile(filename, full_path)
  temp_file = Tempfile.new("download-#{filename}")
  temp_file.binmode
  yield temp_file
  temp_file.close
  File.unlink full_path if File.exist?(full_path)
  FileUtils.mkdir_p File.dirname(full_path)
  FileUtils.mv temp_file.path, full_path, :force => true
end
work_path() click to toggle source
# File lib/mini_portile2/mini_portile.rb, line 209
def work_path
  Dir.glob("#{tmp_path}/*").find { |d| File.directory?(d) }
end