class MiniPortile

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_portile.rb, line 14
def initialize(name, version)
  @name = name
  @version = version
  @target = 'ports'
  @files = []
  @patch_files = []
  @logger = STDOUT

  @original_host = @host = detect_host
end

Public Instance Methods

activate() click to toggle source
# File lib/mini_portile.rb, line 116
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
compile() click to toggle source
# File lib/mini_portile.rb, line 69
def compile
  execute('compile', make_cmd)
end
configure() click to toggle source
# File lib/mini_portile.rb, line 59
def configure
  return if configured?

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

  execute('configure', %Q(sh configure #{computed_options}))
end
configure_options() click to toggle source
# File lib/mini_portile.rb, line 55
def configure_options
  @configure_options ||= configure_defaults
end
configured?() click to toggle source
# File lib/mini_portile.rb, line 87
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)

  (current_md5 == stored_md5) && newer?(makefile, configure)
end
cook() click to toggle source
# File lib/mini_portile.rb, line 105
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_portile.rb, line 25
def download
  @files.each do |url|
    filename = File.basename(url)
    download_file(url, File.join(archives_path, filename))
  end
end
downloaded?() click to toggle source
# File lib/mini_portile.rb, line 78
def downloaded?
  missing = @files.detect do |url|
    filename = File.basename(url)
    !File.exist?(File.join(archives_path, filename))
  end

  missing ? false : true
end
extract() click to toggle source
# File lib/mini_portile.rb, line 32
def extract
  @files.each do |url|
    filename = File.basename(url)
    extract_file(File.join(archives_path, filename), tmp_path)
  end
end
install() click to toggle source
# File lib/mini_portile.rb, line 73
def install
  return if installed?
  execute('install', %Q(#{make_cmd} install))
end
installed?() click to toggle source
# File lib/mini_portile.rb, line 98
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_portile.rb, line 39
def patch
  # Set GIT_DIR while appying patches to work around
  # git-apply doing nothing when started within another
  # git directory.
  ENV['GIT_DIR'], old_git = '.', ENV['GIT_DIR']
  begin
    @patch_files.each do |full_path|
      next unless File.exists?(full_path)
      output "Running git apply with #{full_path}..."
      execute('patch', %Q(git apply #{full_path}))
    end
  ensure
    ENV['GIT_DIR'] = old_git
  end
end
path() click to toggle source
# File lib/mini_portile.rb, line 151
def path
  File.expand_path(port_path)
end

Private Instance Methods

archives_path() click to toggle source
# File lib/mini_portile.rb, line 165
def archives_path
  "#{@target}/archives"
end
computed_options() click to toggle source
# File lib/mini_portile.rb, line 185
def computed_options
  [
    configure_options,     # customized or default options
    configure_prefix,      # installation target
  ].flatten.join(' ')
end
configure_defaults() click to toggle source
# File lib/mini_portile.rb, line 173
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_portile.rb, line 181
def configure_prefix
  "--prefix=#{File.expand_path(port_path)}"
end
detect_host() click to toggle source
# File lib/mini_portile.rb, line 234
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_portile.rb, line 306
def download_file(url, full_path, count = 3)
  return if File.exist?(full_path)
  uri = URI.parse(url)
  begin
    case uri.scheme.downcase
    when /ftp/
      download_file_ftp(uri, full_path)
    when /http|https/
      download_file_http(url, full_path, count)
    end
  rescue Exception => e
    File.unlink full_path if File.exists?(full_path)
    output "ERROR: #{e.message}"
    raise "Failed to complete download task"
  end
end
download_file_ftp(uri, full_path) click to toggle source
# File lib/mini_portile.rb, line 389
def download_file_ftp(uri, full_path)
  filename = File.basename(uri.path)
  with_tempfile(filename, full_path) do |temp_file|
    size = 0
    progress = 0
    Net::FTP.open(uri.host, uri.user, uri.password) do |ftp|
      ftp.passive = true
      ftp.login
      remote_dir = File.dirname(uri.path)
      ftp.chdir(remote_dir) unless remote_dir == '.'
      total = ftp.size(filename)
      ftp.getbinaryfile(filename, temp_file.path, 8192) do |chunk|
        # Ruby 1.8.7 already wrote the chunk into the file
        unless RUBY_VERSION < "1.9"
          temp_file << chunk
        end

        size += chunk.size
        new_progress = (size * 100) / total
        unless new_progress == progress
          message "\rDownloading %s (%3d%%) " % [filename, new_progress]
        end
        progress = new_progress
      end
    end
    output
  end
end
download_file_http(url, full_path, count = 3) click to toggle source
# File lib/mini_portile.rb, line 323
def download_file_http(url, full_path, count = 3)
  filename = File.basename(full_path)
  uri = URI.parse(url)

  if ENV['http_proxy']
    _, userinfo, p_host, p_port = URI.split(ENV['http_proxy'])
    proxy_user, proxy_pass = userinfo.split(/:/) if userinfo
    http = Net::HTTP.new(uri.host, uri.port, p_host, p_port, proxy_user, proxy_pass)
  else
    http = Net::HTTP.new(uri.host, uri.port)

    if URI::HTTPS === uri
      http.use_ssl = true
      http.verify_mode = OpenSSL::SSL::VERIFY_PEER

      store = OpenSSL::X509::Store.new

      # Auto-include system-provided certificates
      store.set_default_paths

      if ENV.has_key?("SSL_CERT_FILE") && File.exist?(ENV["SSL_CERT_FILE"])
        store.add_file ENV["SSL_CERT_FILE"]
      end

      http.cert_store = store
    end
  end

  message "Downloading #{filename} "
  http.start do |h|
    h.request_get(uri.path, 'Accept-Encoding' => 'identity') do |response|
      case response
      when Net::HTTPNotFound
        output "404 - Not Found"
        return false

      when Net::HTTPClientError
        output "Error: Client Error: #{response.inspect}"
        return false

      when Net::HTTPRedirection
        raise "Too many redirections for the original URL, halting." if count <= 0
        url = response["location"]
        return download_file(url, full_path, count - 1)

      when Net::HTTPOK
        return with_tempfile(filename, full_path) do |temp_file|
          size = 0
          progress = 0
          total = response.header["Content-Length"].to_i
          response.read_body do |chunk|
            temp_file << chunk
            size += chunk.size
            new_progress = (size * 100) / total
            unless new_progress == progress
              message "\rDownloading %s (%3d%%) " % [filename, new_progress]
            end
            progress = new_progress
          end
          output
        end
      end
    end
  end
end
execute(action, command) click to toggle source
# File lib/mini_portile.rb, line 266
def execute(action, command)
  log        = log_file(action)
  log_out    = File.expand_path(log)
  redirected = command << " >#{log_out} 2>&1"

  Dir.chdir work_path do
    message "Running '#{action}' for #{@name} #{@version}... "
    system redirected
    if $?.success?
      output "OK"
      return true
    else
      output "ERROR, review '#{log_out}' to see what happened."
      raise "Failed to complete #{action} task"
    end
  end
end
extract_file(file, target) click to toggle source
# File lib/mini_portile.rb, line 251
def extract_file(file, target)
  filename = File.basename(file)
  FileUtils.mkdir_p target

  message "Extracting #{filename} into #{target}... "
  result = %x`#{tar_exe} #{tar_compression_switch(filename)}xf "#{file}" -C "#{target}" 2>&1`
  if $?.success?
    output "OK"
  else
    output "ERROR"
    output result
    raise "Failed to complete extract task"
  end
end
gcc_cmd() click to toggle source
# File lib/mini_portile.rb, line 428
def gcc_cmd
  cc = ENV["CC"] || RbConfig::CONFIG["CC"] || "gcc"
  return cc.dup
end
log_file(action) click to toggle source
# File lib/mini_portile.rb, line 192
def log_file(action)
  File.join(tmp_path, "#{action}.log")
end
make_cmd() click to toggle source
# File lib/mini_portile.rb, line 433
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_portile.rb, line 293
def message(text)
  @logger.print text
  @logger.flush
end
newer?(target, checkpoint) click to toggle source
# File lib/mini_portile.rb, line 284
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_portile.rb, line 299
def output(text = "")
  @logger.puts text
  @logger.flush
end
port_path() click to toggle source
# File lib/mini_portile.rb, line 161
def port_path
  "#{@target}/#{@host}/#{@name}/#{@version}"
end
tar_compression_switch(filename) click to toggle source
# File lib/mini_portile.rb, line 204
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_portile.rb, line 196
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_portile.rb, line 157
def tmp_path
  "tmp/#{@host}/ports/#{@name}/#{@version}"
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_portile.rb, line 223
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_portile.rb, line 418
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.exists?(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_portile.rb, line 169
def work_path
  Dir.glob("#{tmp_path}/*").find { |d| File.directory?(d) }
end