class MiniPortile
Constants
- DEFAULT_TIMEOUT
- KEYRING_NAME
- TAR_EXECUTABLES
- VERSION
Attributes
Public Class Methods
Source
# File lib/mini_portile2/mini_portile.rb, line 55 def self.darwin? target_os =~ /darwin/ end
Source
# File lib/mini_portile2/mini_portile.rb, line 59 def self.freebsd? target_os =~ /freebsd/ end
Source
# File lib/mini_portile2/mini_portile.rb, line 67 def self.linux? target_os =~ /linux/ end
Source
# File lib/mini_portile2/mini_portile.rb, line 46 def self.mingw? target_os =~ /mingw/ end
GNU MinGW compiled Ruby?
Source
# File lib/mini_portile2/mini_portile.rb, line 51 def self.mswin? target_os =~ /mswin/ end
MS Visual-C compiled Ruby?
Source
# File lib/mini_portile2/mini_portile.rb, line 83 def self.native_path(path) path = File.expand_path(path) if File::ALT_SEPARATOR path.tr(File::SEPARATOR, File::ALT_SEPARATOR) else path end end
Source
# File lib/mini_portile2/mini_portile.rb, line 101 def initialize(name, version, **kwargs) @name = name @version = version @target = 'ports' @files = [] @patch_files = [] @log_files = {} @logger = kwargs[:logger] || STDOUT @source_directory = nil @cc_command = kwargs[:cc_command] || kwargs[:gcc_command] @cxx_command = kwargs[:cxx_command] @make_command = kwargs[:make_command] @open_timeout = kwargs[:open_timeout] || DEFAULT_TIMEOUT @read_timeout = kwargs[:read_timeout] || DEFAULT_TIMEOUT @original_host = @host = detect_host end
Source
# File lib/mini_portile2/mini_portile.rb, line 63 def self.openbsd? target_os =~ /openbsd/ end
Source
# File lib/mini_portile2/mini_portile.rb, line 92 def self.posix_path(path) path = File.expand_path(path) if File::ALT_SEPARATOR "/" + path.tr(File::ALT_SEPARATOR, File::SEPARATOR).tr(":", File::SEPARATOR) else path end end
Source
# File lib/mini_portile2/mini_portile.rb, line 71 def self.solaris? target_os =~ /solaris/ end
Source
# File lib/mini_portile2/mini_portile.rb, line 79 def self.target_cpu RbConfig::CONFIG['target_cpu'] end
Source
# File lib/mini_portile2/mini_portile.rb, line 75 def self.target_os RbConfig::CONFIG['target_os'] end
Source
# File lib/mini_portile2/mini_portile.rb, line 41 def self.windows? target_os =~ /mswin|mingw/ end
Public Instance Methods
Source
# File lib/mini_portile2/mini_portile.rb, line 244 def activate vars = { 'PATH' => File.join(port_path, 'bin'), 'CPATH' => include_path, 'LIBRARY_PATH' => lib_path, }.reject { |env, path| !File.directory?(path) } output "Activating #{@name} #{@version} (from #{port_path})..." vars.each do |var, path| full_path = native_path(path) # 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 = native_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
Source
# File lib/mini_portile2/mini_portile.rb, line 145 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}... " Dir.mktmpdir do |tmp_git_dir| execute('patch', ["git", "--git-dir=#{tmp_git_dir}", "--work-tree=.", "apply", "--whitespace=warn", file], :initial_message => false) end } 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
Source
# File lib/mini_portile2/mini_portile.rb, line 376 def cc_cmd (ENV["CC"] || @cc_command || RbConfig::CONFIG["CC"] || "gcc").dup end
Source
# File lib/mini_portile2/mini_portile.rb, line 194 def compile execute('compile', make_cmd) end
Source
# File lib/mini_portile2/mini_portile.rb, line 179 def configure return if configured? FileUtils.mkdir_p(tmp_path) cache_file = File.join(tmp_path, 'configure.options_cache') File.open(cache_file, "w") { |f| f.write computed_options.to_s } command = Array(File.join((source_directory || "."), "configure")) if RUBY_PLATFORM=~/mingw|mswin/ # Windows doesn't recognize the shebang. command.unshift("sh") end execute('configure', command + computed_options, altlog: "config.log") end
Source
# File lib/mini_portile2/mini_portile.rb, line 175 def configure_options @configure_options ||= configure_defaults end
Source
# File lib/mini_portile2/mini_portile.rb, line 211 def configured? configure = File.join((source_directory || work_path), 'configure') makefile = File.join(work_path, 'Makefile') cache_file = File.join(tmp_path, 'configure.options_cache') stored_options = File.exist?(cache_file) ? File.read(cache_file) : "" current_options = computed_options.to_s (current_options == stored_options) && newer?(makefile, configure) end
Source
# File lib/mini_portile2/mini_portile.rb, line 229 def cook if source_directory prepare_build_directory else download unless downloaded? extract patch end configure unless configured? compile install unless installed? return true end
Source
# File lib/mini_portile2/mini_portile.rb, line 381 def cxx_cmd (ENV["CXX"] || @cxx_command || RbConfig::CONFIG["CXX"] || "g++").dup end
Source
# File lib/mini_portile2/mini_portile.rb, line 131 def download files_hashs.each do |file| download_file(file[:url], file[:local_path]) verify_file(file) end end
Source
# File lib/mini_portile2/mini_portile.rb, line 203 def downloaded? missing = files_hashs.detect do |file| !File.exist?(file[:local_path]) end missing ? false : true end
Source
# File lib/mini_portile2/mini_portile.rb, line 138 def extract files_hashs.each do |file| verify_file(file) extract_file(file[:local_path], tmp_path) end end
Source
# File lib/mini_portile2/mini_portile.rb, line 368 def include_path File.join(path, "include") end
Source
# File lib/mini_portile2/mini_portile.rb, line 198 def install return if installed? execute('install', %Q(#{make_cmd} install)) end
Source
# File lib/mini_portile2/mini_portile.rb, line 222 def installed? makefile = File.join(work_path, 'Makefile') target_dir = Dir.glob("#{port_path}/*").find { |d| File.directory?(d) } newer?(target_dir, makefile) end
Source
# File lib/mini_portile2/mini_portile.rb, line 372 def lib_path File.join(path, "lib") end
Source
# File lib/mini_portile2/mini_portile.rb, line 385 def make_cmd (ENV["MAKE"] || @make_command || ENV["make"] || "make").dup end
Source
# File lib/mini_portile2/mini_portile.rb, line 283 def mkmf_config(pkg: nil, dir: nil, static: nil) require "mkmf" if pkg dir ||= File.join(lib_path, "pkgconfig") pcfile = File.join(dir, "#{pkg}.pc") unless File.exist?(pcfile) raise ArgumentError, "pkg-config file '#{pcfile}' does not exist" end output "Configuring MakeMakefile for #{File.basename(pcfile)} (in #{File.dirname(pcfile)})\n" # on macos, pkg-config will not return --cflags without this ENV["PKG_CONFIG_ALLOW_SYSTEM_CFLAGS"] = "t" # append to PKG_CONFIG_PATH as we go, so later pkg-config files can depend on earlier ones ENV["PKG_CONFIG_PATH"] = [ENV["PKG_CONFIG_PATH"], dir].compact.join(File::PATH_SEPARATOR) incflags = minimal_pkg_config(pcfile, "cflags-only-I") cflags = minimal_pkg_config(pcfile, "cflags-only-other") if static ldflags = minimal_pkg_config(pcfile, "libs-only-L", "static") libflags = minimal_pkg_config(pcfile, "libs-only-l", "static") else ldflags = minimal_pkg_config(pcfile, "libs-only-L") libflags = minimal_pkg_config(pcfile, "libs-only-l") end else output "Configuring MakeMakefile for #{@name} #{@version} (from #{path})\n" lib_name = name.sub(/\Alib/, "") # TODO: use delete_prefix when we no longer support ruby 2.4 incflags = Dir.exist?(include_path) ? "-I#{include_path}" : "" cflags = "" ldflags = Dir.exist?(lib_path) ? "-L#{lib_path}" : "" libflags = Dir.exist?(lib_path) ? "-l#{lib_name}" : "" end if static libdir = lib_path if pcfile pcfile_libdir = minimal_pkg_config(pcfile, "variable=libdir").strip libdir = pcfile_libdir unless pcfile_libdir.empty? end # # keep track of the libraries we're statically linking against, and fix up ldflags and # libflags to make sure we link statically against the recipe's libaries. # # this avoids the unintentionally dynamically linking against system libraries, and makes sure # that if multiple pkg-config files reference each other that we are able to intercept flags # from dependent packages that reference the static archive. # $MINI_PORTILE_STATIC_LIBS[static] = libdir static_ldflags = $MINI_PORTILE_STATIC_LIBS.values.map { |v| "-L#{v}" } static_libflags = $MINI_PORTILE_STATIC_LIBS.keys.map { |v| "-l#{v}" } # remove `-L#{libdir}` and `-lfoo`. we don't need them since we link against the static # archive using the full path. ldflags = ldflags.shellsplit.reject { |f| static_ldflags.include?(f) }.shelljoin libflags = libflags.shellsplit.reject { |f| static_libflags.include?(f) }.shelljoin # prepend the full path to the static archive to the linker flags static_archive = File.join(libdir, "lib#{static}.#{$LIBEXT}") libflags = [static_archive, libflags].join(" ").strip end # prefer this package by prepending to search paths and library flags # # convert the ldflags into a list of directories and append to $LIBPATH (instead of just using # $LDFLAGS) to ensure we get the `-Wl,-rpath` linker flag for re-finding shared libraries. $INCFLAGS = [incflags, $INCFLAGS].join(" ").strip libpaths = ldflags.shellsplit.map { |f| f.sub(/\A-L/, "") } $LIBPATH = libpaths | $LIBPATH $libs = [libflags, $libs].join(" ").strip # prefer this package's compiler flags by appending them to the command line $CFLAGS = [$CFLAGS, cflags].join(" ").strip $CXXFLAGS = [$CXXFLAGS, cflags].join(" ").strip end
pkg: the pkg-config file name (without the .pc extension) dir: inject the directory path for the pkg-config file (probably only useful for tests) static: the name of the static library archive (without the “lib” prefix or the file extension), or nil for dynamic linking
we might be able to be terribly clever and infer the name of the static archive file, but unfortunately projects have so much freedom in what they can report (for name, for libs, etc.) that it feels unreliable to try to do so, so I’m preferring to just have the developer make it explicit.
Source
# File lib/mini_portile2/mini_portile.rb, line 168 def patch @patch_files.each do |full_path| next unless File.exist?(full_path) apply_patch(full_path) end end
Source
# File lib/mini_portile2/mini_portile.rb, line 364 def path File.expand_path(port_path) end
Source
# File lib/mini_portile2/mini_portile.rb, line 124 def prepare_build_directory raise "source_directory is not set" if source_directory.nil? output "Building #{@name} from source at '#{source_directory}'" FileUtils.mkdir_p(File.join(tmp_path, [name, version].join("-"))) FileUtils.rm_rf(port_path) # make sure we always re-install end
Source
# File lib/mini_portile2/mini_portile.rb, line 120 def source_directory=(path) @source_directory = posix_path(path) end
Private Instance Methods
Source
# File lib/mini_portile2/mini_portile.rb, line 407 def archives_path "#{@target}/archives" end
Source
# File lib/mini_portile2/mini_portile.rb, line 427 def computed_options [ configure_options, # customized or default options configure_prefix, # installation target ].flatten end
Source
# File lib/mini_portile2/mini_portile.rb, line 415 def configure_defaults [ "--host=#{@host}", # build for specific target (host) "--enable-static", # build static library "--disable-shared" # disable generation of shared object ] end
Source
# File lib/mini_portile2/mini_portile.rb, line 423 def configure_prefix "--prefix=#{File.expand_path(port_path)}" end
Source
# File lib/mini_portile2/mini_portile.rb, line 529 def detect_host return @detect_host if defined?(@detect_host) begin ENV["LC_ALL"], old_lc_all = "C", ENV["LC_ALL"] output = `#{gcc_cmd} -v 2>&1` if m = output.match(/^Target\: (.*)$/) @detect_host = m[1] else @detect_host = nil end @detect_host ensure ENV["LC_ALL"] = old_lc_all end end
Source
# File lib/mini_portile2/mini_portile.rb, line 655 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
Slighly modified from RubyInstaller uri_ext, Rubinius configure and adaptations of Wayne’s RailsInstaller
Source
# File lib/mini_portile2/mini_portile.rb, line 728 def download_file_file(uri, full_path) FileUtils.mkdir_p File.dirname(full_path) FileUtils.cp uri.path, full_path end
Source
# File lib/mini_portile2/mini_portile.rb, line 733 def download_file_ftp(uri, full_path) require "net/ftp" filename = File.basename(uri.path) with_tempfile(filename, full_path) do |temp_file| 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] }, :open_timeout => @open_timeout, :read_timeout => @read_timeout, } 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 LoadError raise LoadError, "Ruby #{RUBY_VERSION} does not provide the net-ftp gem, please add it as a dependency if you need to use FTP" rescue Net::FTPError return false end
Source
# File lib/mini_portile2/mini_portile.rb, line 674 def download_file_http(url, full_path, count = 3) filename = File.basename(full_path) with_tempfile(filename, full_path) do |temp_file| total = 0 params = { "Accept-Encoding" => 'identity', :content_length_proc => lambda{|length| total = length }, :progress_proc => lambda{|bytes| if total new_progress = (bytes * 100) / total message "\rDownloading %s (%3d%%) " % [filename, new_progress] else # Content-Length is unavailable because Transfer-Encoding is chunked message "\rDownloading %s " % [filename] end }, :open_timeout => @open_timeout, :read_timeout => @read_timeout, } 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 count = count - 1 @logger.puts "#{count} retrie(s) left for #{filename} (#{e.message})" if count > 0 sleep 1 return download_file_http(url, full_path, count) end output e.message return false end end end
Source
# File lib/mini_portile2/mini_portile.rb, line 585 def execute(action, command, command_opts={}) opt_message = command_opts.fetch(:initial_message, true) opt_debug = command_opts.fetch(:debug, false) opt_cd = command_opts.fetch(:cd) { work_path } opt_env = command_opts.fetch(:env) { Hash.new } opt_altlog = command_opts.fetch(:altlog, nil) log_out = log_file(action) Dir.chdir(opt_cd) do output "DEBUG: env is #{opt_env.inspect}" if opt_debug output "DEBUG: command is #{command.inspect}" if opt_debug message "Running '#{action}' for #{@name} #{@version}... " if opt_message if Process.respond_to?(:spawn) && ! RbConfig.respond_to?(:java) options = {[:out, :err]=>[log_out, "a"]} output "DEBUG: options are #{options.inspect}" if opt_debug args = [opt_env, command, options].flatten pid = spawn(*args) Process.wait(pid) else env_args = opt_env.map { |k,v| "#{k}=#{v}".shellescape }.join(" ") c = if command.kind_of?(Array) command.map(&:shellescape).join(" ") else command end redirected = %Q{env #{env_args} #{c} > #{log_out.shellescape} 2>&1} output "DEBUG: final command is #{redirected.inspect}" if opt_debug system redirected end if $?.success? output "OK" return true else output "ERROR. Please review logs to see what happened:\n" [log_out, opt_altlog].compact.each do |log| next unless File.exist?(log) output("----- contents of '#{log}' -----") output(File.read(log)) output("----- end of file -----") end raise "Failed to complete #{action} task" end end end
command could be an array of args, or one string containing a command passed to the shell. See Process.spawn for more information.
Source
# File lib/mini_portile2/mini_portile.rb, line 575 def extract_file(file, target) filename = File.basename(file) FileUtils.mkdir_p target message "Extracting #{filename} into #{target}... " execute('extract', tar_command(file, target) , {:cd => Dir.pwd, :initial_message => false}) end
Source
# File lib/mini_portile2/mini_portile.rb, line 434 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
Source
# File lib/mini_portile2/mini_portile.rb, line 505 def log_file(action) @log_files[action] ||= File.expand_path("#{action}.log", tmp_path).tap { |file| File.unlink(file) if File.exist?(file) } end
Source
# File lib/mini_portile2/mini_portile.rb, line 642 def message(text) @logger.print text @logger.flush end
print out a message with the logger
Source
# File lib/mini_portile2/mini_portile.rb, line 782 def minimal_pkg_config(pkg, *pcoptions) if pcoptions.empty? raise ArgumentError, "no pkg-config options are given" end if ($PKGCONFIG ||= (pkgconfig = MakeMakefile.with_config("pkg-config") {MakeMakefile.config_string("PKG_CONFIG") || "pkg-config"}) && MakeMakefile.find_executable0(pkgconfig) && pkgconfig) pkgconfig = $PKGCONFIG else raise RuntimeError, "pkg-config is not found" end pcoptions = Array(pcoptions).map { |o| "--#{o}" } response = IO.popen([pkgconfig, *pcoptions, pkg], err:[:child, :out], &:read) raise RuntimeError, response unless $?.success? response.strip end
this minimal version of pkg_config is based on ruby 29dc9378 (2023-01-09)
specifically with the fix from b90e56e6 to support multiple pkg-config options, and removing code paths that aren’t helpful for mini-portile’s use case of parsing pc files.
Source
# File lib/mini_portile2/mini_portile.rb, line 391 def native_path(path) MiniPortile.native_path(path) end
Source
# File lib/mini_portile2/mini_portile.rb, line 633 def newer?(target, checkpoint) if (target && File.exist?(target)) && (checkpoint && File.exist?(checkpoint)) File.mtime(target) > File.mtime(checkpoint) else false end end
Source
# File lib/mini_portile2/mini_portile.rb, line 648 def output(text = "") @logger.puts text @logger.flush end
print out a message using the logger but return to a new line
Source
# File lib/mini_portile2/mini_portile.rb, line 403 def port_path "#{@target}/#{@host}/#{@name}/#{@version}" end
Source
# File lib/mini_portile2/mini_portile.rb, line 395 def posix_path(path) MiniPortile.posix_path(path) end
Source
# File lib/mini_portile2/mini_portile.rb, line 561 def tar_command(file, target) case File.extname(file) when '.gz', '.tgz' [tar_exe, 'xzf', file, '-C', target] when '.bz2', '.tbz2' [tar_exe, 'xjf', file, '-C', target] when '.xz' # NOTE: OpenBSD's tar command does not support the -J option "#{xzcat_exe.shellescape} #{file.shellescape} | #{tar_exe.shellescape} xf - -C #{target.shellescape}" else [tar_exe, 'xf', file, '-C', target] end end
Source
# File lib/mini_portile2/mini_portile.rb, line 549 def tar_exe @@tar_exe ||= begin TAR_EXECUTABLES.find { |c| which(c) } or raise("tar not found - please make sure that one of the following commands is in the PATH: #{TAR_EXECUTABLES.join(", ")}") end end
Source
# File lib/mini_portile2/mini_portile.rb, line 399 def tmp_path "tmp/#{@host}/ports/#{@name}/#{@version}" end
Source
# File lib/mini_portile2/mini_portile.rb, line 454 def verify_file(file) if file.has_key?(:gpg) gpg = file[:gpg] signature_url = gpg[:signature_url] || "#{file[:url]}.asc" signature_file = file[:local_path] + ".asc" # download the signature file download_file(signature_url, signature_file) gpg_exe = which('gpg2') || which('gpg') || raise("Neither GPG nor GPG2 is installed") # import the key into our own keyring gpg_error = nil gpg_status = Open3.popen3(gpg_exe, "--status-fd", "1", "--no-default-keyring", "--keyring", KEYRING_NAME, "--import") do |gpg_in, gpg_out, gpg_err, _thread| gpg_in.write gpg[:key] gpg_in.close gpg_error = gpg_err.read gpg_out.read end key_ids = gpg_status.scan(/\[GNUPG:\] IMPORT_OK \d+ (?<key_id>[0-9a-f]+)/i).map(&:first) raise "invalid gpg key provided:\n#{gpg_error}" if key_ids.empty? begin # verify the signature against our keyring gpg_status, gpg_error, _status = Open3.capture3(gpg_exe, "--status-fd", "1", "--no-default-keyring", "--keyring", KEYRING_NAME, "--verify", signature_file, file[:local_path]) raise "signature mismatch:\n#{gpg_error}" unless gpg_status.match(/^\[GNUPG:\] VALIDSIG/) ensure # remove the key from our keyring key_ids.each do |key_id| IO.popen([gpg_exe, "--batch", "--yes", "--no-default-keyring", "--keyring", KEYRING_NAME, "--delete-keys", key_id], &:read) raise "unable to delete the imported key" unless $?.exitstatus==0 end end else 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 end
Source
# File lib/mini_portile2/mini_portile.rb, line 518 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
From: stackoverflow.com/a/5471032/7672 Thanks, Mislav!
Cross-platform way of finding an executable in the $PATH.
which('ruby') #=> /usr/bin/ruby
Source
# File lib/mini_portile2/mini_portile.rb, line 766 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
Source
# File lib/mini_portile2/mini_portile.rb, line 411 def work_path Dir.glob("#{tmp_path}/*").find { |d| File.directory?(d) } end
Source
# File lib/mini_portile2/mini_portile.rb, line 557 def xzcat_exe @@xzcat_exe ||= which("xzcat") ? "xzcat" : raise("xzcat not found") end