# class PackageConfig

### Constants

IDENTIFIER_RE
SEPARATOR

### Attributes

msvc_syntax[RW]
name[R]
paths[R]

### Public Class Methods

clear_configure_args_cache() click to toggle source

    # File lib/pkg-config.rb, line 168
    def clear_configure_args_cache
      @native_pkg_config = nil
      @native_pkg_config_prefix = nil
      @default_path = nil
      @custom_override_variables = nil
    end

custom_override_variables() click to toggle source

    # File lib/pkg-config.rb, line 164
    def custom_override_variables
      @custom_override_variables ||= with_config("override-variables", "")
    end

default_path() click to toggle source

    # File lib/pkg-config.rb, line 159
    def default_path
      @default_path ||= compute_default_path
    end

native_pkg_config() click to toggle source

    # File lib/pkg-config.rb, line 149
    def native_pkg_config
      @native_pkg_config ||= guess_native_pkg_config
    end

native_pkg_config_prefix() click to toggle source

    # File lib/pkg-config.rb, line 154
    def native_pkg_config_prefix
      @native_pkg_config_prefix ||= compute_native_pkg_config_prefix
    end

new(name, options={}) click to toggle source

    # File lib/pkg-config.rb, line 393
    def initialize(name, options={})
      if Pathname(name).absolute?
        @pc_path = name
        @path_position = 0
        @name = File.basename(@pc_path, ".*")
      else
        @pc_path = nil
        @path_position = nil
        @name = name
      end
      @options = options
      @paths = [
        @options[:path],
        self.class.default_path,
      ].compact.join(SEPARATOR).split(SEPARATOR)
      @paths.unshift(*(@options[:paths] || []))
      @paths = normalize_paths(@paths)
      @msvc_syntax = @options[:msvc_syntax]
      @variables = @declarations = nil
      override_variables = self.class.custom_override_variables
      @override_variables = parse_override_variables(override_variables)
      default_override_variables = @options[:override_variables] || {}
      @override_variables = default_override_variables.merge(@override_variables)
    end

### Private Class Methods

compute_default_path() click to toggle source

    # File lib/pkg-config.rb, line 266
    def compute_default_path
      default_paths = nil
      if native_pkg_config
        pc_path = run_command(native_pkg_config.to_s,
                              "--variable=pc_path",
                              "pkg-config")
        if pc_path
          default_paths = pc_path.strip.split(SEPARATOR)
          default_paths = nil if default_paths.empty?
        end
      end
      if default_paths.nil?
        arch_depended_path = Dir.glob("/usr/lib/*/pkgconfig")
        default_paths = []
        pkg_config_prefix = native_pkg_config_prefix
        if pkg_config_prefix
          pkg_config_arch_depended_paths =
            Dir.glob((pkg_config_prefix + "lib/*/pkgconfig").to_s)
          default_paths.concat(pkg_config_arch_depended_paths)
          default_paths << (pkg_config_prefix + "lib64/pkgconfig").to_s
          default_paths << (pkg_config_prefix + "libx32/pkgconfig").to_s
          default_paths << (pkg_config_prefix + "lib/pkgconfig").to_s
          default_paths << (pkg_config_prefix + "libdata/pkgconfig").to_s
          default_paths << (pkg_config_prefix + "share/pkgconfig").to_s
        end
        conda_prefix = ENV["CONDA_PREFIX"]
        if conda_prefix
          default_paths << File.join(conda_prefix, "lib", "pkgconfig")
          default_paths << File.join(conda_prefix, "share", "pkgconfig")
        end
        default_paths << "/usr/local/lib64/pkgconfig"
        default_paths << "/usr/local/libx32/pkgconfig"
        default_paths << "/usr/local/lib/pkgconfig"
        default_paths << "/usr/local/libdata/pkgconfig"
        default_paths << "/usr/local/share/pkgconfig"
        default_paths << "/opt/local/lib/pkgconfig"
        default_paths.concat(arch_depended_path)
        default_paths << "/usr/lib64/pkgconfig"
        default_paths << "/usr/libx32/pkgconfig"
        default_paths << "/usr/lib/pkgconfig"
        default_paths << "/usr/libdata/pkgconfig"
        default_paths << "/usr/X11R6/lib/pkgconfig"
        default_paths << "/usr/X11R6/share/pkgconfig"
        default_paths << "/usr/X11/lib/pkgconfig"
        default_paths << "/opt/X11/lib/pkgconfig"
        default_paths << "/usr/share/pkgconfig"
      end
      if Object.const_defined?(:RubyInstaller)
        mingw_bin_path = RubyInstaller::Runtime.msys2_installation.mingw_bin_path
        mingw_pkgconfig_path = Pathname.new(mingw_bin_path) + "../lib/pkgconfig"
        default_paths.unshift(mingw_pkgconfig_path.cleanpath.to_s)
      end
      libdir = ENV["PKG_CONFIG_LIBDIR"]
      default_paths.unshift(libdir) if libdir

      paths = []
      if /-darwin\d[\d\.]*\z/ =~ RUBY_PLATFORM and
        /\A(\d+\.\d+)/ =~ run_command("sw_vers", "-productVersion")
        mac_os_version = $1
        homebrew_repository_candidates = []
        if pkg_config_prefix
          brew_path = pkg_config_prefix + "bin" + "brew"
          if brew_path.exist?
            homebrew_repository = run_command(brew_path.to_s, "--repository")
            if homebrew_repository
              homebrew_repository_candidates <<
                Pathname.new(homebrew_repository.strip)
            end
          else
            homebrew_repository_candidates << pkg_config_prefix + "Homebrew"
            homebrew_repository_candidates << pkg_config_prefix
          end
        end
        brew = search_executable_from_path("brew")
        if brew
          homebrew_repository = run_command("brew", "--repository")
          if homebrew_repository
            homebrew_repository_candidates <<
              Pathname(homebrew_repository.strip)
          end
          homebrew_prefix = run_command("brew", "--prefix")
          if homebrew_prefix
            homebrew_repository_candidates <<
              Pathname(homebrew_prefix.strip)
          end
        end
        homebrew_repository_candidates.uniq.each do |candidate|
          mac_pkgconfig_base_path =
            candidate + "Library/Homebrew/os/mac/pkgconfig"
          mac_pkgconfig_path = mac_pkgconfig_base_path + mac_os_version
          unless mac_pkgconfig_path.exist?
            mac_pkgconfig_path =
              mac_pkgconfig_base_path + mac_os_version.gsub(/\.\d+\z/, "")
          end
          paths << mac_pkgconfig_path.to_s if mac_pkgconfig_path.exist?

          pkgconfig_path = candidate + "lib/pkgconfig"
          paths << pkgconfig_path.to_s if pkgconfig_path.exist?
        end
      end
      paths.concat(default_paths)
      [
        with_config("pkg-config-path") || ENV["PKG_CONFIG_PATH"],
        *paths,
      ].compact.join(SEPARATOR)
    end

compute_native_pkg_config_prefix() click to toggle source

    # File lib/pkg-config.rb, line 251
    def compute_native_pkg_config_prefix
      pkg_config = native_pkg_config
      return nil unless pkg_config.absolute?
      return nil unless pkg_config.exist?

      pkg_config_prefix = pkg_config.parent.parent
      if File::ALT_SEPARATOR
        normalized_pkg_config_prefix =
          pkg_config_prefix.to_s.split(File::ALT_SEPARATOR).join(File::SEPARATOR)
        Pathname(normalized_pkg_config_prefix)
      else
        pkg_config_prefix
      end
    end

guess_native_pkg_config() click to toggle source

    # File lib/pkg-config.rb, line 184
    def guess_native_pkg_config
      exeext = RbConfig::CONFIG["EXEEXT"]
      candidates = [
        with_config("pkg-config"),
        ENV["PKG_CONFIG"],
        "pkgconf#{exeext}",
        "pkg-config#{exeext}",
      ].compact
      candidates.each do |pkg_config|
        pkg_config = Pathname.new(pkg_config)
        return pkg_config if pkg_config.absolute? and pkg_config.exist?
        unless pkg_config.absolute?
          found_pkg_config = search_executable_from_path(pkg_config)
          return found_pkg_config if found_pkg_config
        end
        unless pkg_config.absolute?
          found_pkg_config = search_pkg_config_by_dln_find_exe(pkg_config)
          return found_pkg_config if found_pkg_config
        end
      end
      Pathname.new(candidates[0])
    end

run_command(*command_line) click to toggle source

    # File lib/pkg-config.rb, line 373
    def run_command(*command_line)
      IO.pipe do |input, output|
        begin
          pid = spawn(*command_line,
                      out: output,
                      err: File::NULL)
          output.close
          _, status = Process.waitpid2(pid)
          return nil unless status.success?
          input.read
        rescue SystemCallError
          nil
        end
      end
    end

search_executable_from_path(name) click to toggle source

    # File lib/pkg-config.rb, line 207
    def search_executable_from_path(name)
      (ENV["PATH"] || "").split(SEPARATOR).each do |path|
        try_name = Pathname(path) + name
        return try_name if try_name.executable?
      end
      nil
    end

search_pkg_config_by_dln_find_exe(pkg_config) click to toggle source

    # File lib/pkg-config.rb, line 215
    def search_pkg_config_by_dln_find_exe(pkg_config)
      begin
        require "dl/import"
      rescue LoadError
        return nil
      end
      dln = Module.new
      dln.module_eval do
        if DL.const_defined?(:Importer)
          extend DL::Importer
        else
          extend DL::Importable
        end
        begin
          dlload RbConfig::CONFIG["LIBRUBY"]
        rescue RuntimeError
          return nil if $!.message == "unknown error"
          return nil if /: image not found\z/ =~ $!.message
          raise
        rescue DL::DLError
          return nil
        end
        begin
          extern "const char *dln_find_exe(const char *, const char *)"
        rescue DL::DLError
          return nil
        end
      end
      path = dln.dln_find_exe(pkg_config.to_s, nil)
      if path.nil? or path.size.zero?
        nil
      else
        Pathname(path.to_s)
      end
    end

with_config(config, default=nil) click to toggle source
Calls superclass method

    # File lib/pkg-config.rb, line 176
    def with_config(config, default=nil)
      if defined?(super)
        super
      else
        default
      end
    end

### Public Instance Methods

cflags() click to toggle source

    # File lib/pkg-config.rb, line 438
    def cflags
      path_flags, other_flags = collect_cflags
      (path_flags + other_flags).join(" ")
    end

cflags_only_I() click to toggle source

    # File lib/pkg-config.rb, line 443
    def cflags_only_I
      collect_cflags[0].join(" ")
    end

cflags_only_other() click to toggle source

    # File lib/pkg-config.rb, line 447
    def cflags_only_other
      collect_cflags[1].join(" ")
    end

declaration(name) click to toggle source

    # File lib/pkg-config.rb, line 488
    def declaration(name)
      parse_pc if @declarations.nil?
      expand_value(@declarations[name])
    end

description() click to toggle source

    # File lib/pkg-config.rb, line 479
    def description
      declaration("Description")
    end

eql?(other) click to toggle source

    # File lib/pkg-config.rb, line 418
    def eql?(other)
      other.is_a?(self.class) and @name == other.name
    end

exist?() click to toggle source

    # File lib/pkg-config.rb, line 426
    def exist?
      not pc_path.nil?
    end

hash() click to toggle source

    # File lib/pkg-config.rb, line 422
    def hash
      @name.hash
    end

libs() click to toggle source

    # File lib/pkg-config.rb, line 451
    def libs
      collect_libs.join(" ")
    end

libs_only_L() click to toggle source

    # File lib/pkg-config.rb, line 465
    def libs_only_L
      collect_libs.find_all do |arg|
        if @msvc_syntax
          arg.start_with?("/libpath:")
        else
          arg.start_with?("-L")
        end
      end.join(" ")
    end

libs_only_l() click to toggle source

    # File lib/pkg-config.rb, line 455
    def libs_only_l
      collect_libs.find_all do |arg|
        if @msvc_syntax
          arg.end_with?(".lib")
        else
          arg.start_with?("-l")
        end
      end.join(" ")
    end

pc_path() click to toggle source

    # File lib/pkg-config.rb, line 493
    def pc_path
      if @pc_path
        return @pc_path if File.exist?(@pc_path)
      else
        @paths.each_with_index do |path, i|
          _pc_path = File.join(path, "#{@name}.pc")
          if File.exist?(_pc_path)
            @path_position = i + 1
            return _pc_path
          end
        end
      end
      nil
    end

requires() click to toggle source

    # File lib/pkg-config.rb, line 430
    def requires
      parse_requires(declaration("Requires"))
    end

requires_private() click to toggle source

    # File lib/pkg-config.rb, line 434
    def requires_private
      parse_requires(declaration("Requires.private"))
    end

variable(name) click to toggle source

    # File lib/pkg-config.rb, line 483
    def variable(name)
      parse_pc if @variables.nil?
      expand_value(@override_variables[name] || @variables[name])
    end

version() click to toggle source

    # File lib/pkg-config.rb, line 475
    def version
      declaration("Version")
    end

### Protected Instance Methods

collect_requires() { |self| ... } click to toggle source

    # File lib/pkg-config.rb, line 513
    def collect_requires(&block)
      dependencies = {}
      pending_packages = yield(self).collect {|name| self.class.new(name, @options)}
      until pending_packages.empty?
        package = pending_packages.shift
        next if dependencies.key?(package)
        dependencies[package] = true
        targets = yield(package)
        targets.each do |name|
          require_package = self.class.new(name, @options)
          pending_packages.push(require_package)
        end
      end
      dependencies.keys
    end

path_position() click to toggle source

    # File lib/pkg-config.rb, line 509
    def path_position
      @path_position
    end

### Private Instance Methods

all_required_packages() click to toggle source

    # File lib/pkg-config.rb, line 692
    def all_required_packages
      collect_requires do |package|
        package.requires_private + package.requires
      end
    end

collect_cflags() click to toggle source

    # File lib/pkg-config.rb, line 530
    def collect_cflags
      target_packages = [self, *all_required_packages]
      cflags_set = []
      target_packages.each do |package|
        cflags_set << package.declaration("Cflags")
      end
      all_cflags = normalize_cflags(Shellwords.split(cflags_set.join(" ")))
      path_flags, other_flags = all_cflags.partition {|flag| /\A-I/ =~ flag}
      path_flags = path_flags.collect {|flag| normalize_path_flag(flag, "-I")}
      path_flags = path_flags.reject do |flag|
        flag == "-I/usr/include"
      end
      path_flags = path_flags.uniq
      if @msvc_syntax
        path_flags = path_flags.collect do |flag|
          flag.gsub(/\A-I/, "/I")
        end
      end
      [path_flags, other_flags]
    end

collect_libs() click to toggle source

    # File lib/pkg-config.rb, line 582
    def collect_libs
      target_packages = [*required_packages, self]
      libs_set = []
      target_packages.each do |package|
        libs_set << package.declaration("Libs")
      end
      flags = split_lib_flags(libs_set.join(" "))
      flags = flags.collect do |flag|
        flag = normalize_path_flag(flag, "-L") if flag.start_with?("-L")
        flag
      end
      flags = flags.reject do |flag|
        /\A-L\/usr\/lib(?:64|x32)?\z/ =~ flag
      end
      flags = flags.uniq
      if @msvc_syntax
        flags = flags.collect do |flag|
          if flag.start_with?("-L")
            flag.gsub(/\A-L/, "/libpath:")
          elsif flag.start_with?("-l")
            "#{flag[2..-1]}.lib"
          else
            flag
          end
        end
      end
      flags
    end

expand_value(value) click to toggle source

    # File lib/pkg-config.rb, line 679
    def expand_value(value)
      return nil if value.nil?
      value.gsub(/\$\{(#{IDENTIFIER_RE})\}/) do
        variable($1)
      end
    end

normalize_cflags(cflags) click to toggle source

    # File lib/pkg-config.rb, line 565
    def normalize_cflags(cflags)
      normalized_cflags = []
      enumerator = cflags.to_enum
      begin
        loop do
          cflag = enumerator.next
          normalized_cflags << cflag.dup
          case cflag
          when "-I"
            normalized_cflags.last << enumerator.next
          end
        end
      rescue StopIteration
      end
      normalized_cflags
    end

normalize_path_flag(path_flag, flag_option) click to toggle source

    # File lib/pkg-config.rb, line 551
    def normalize_path_flag(path_flag, flag_option)
      return path_flag unless /-mingw(?:32|-ucrt)\z/ === RUBY_PLATFORM

      pkg_config_prefix = self.class.native_pkg_config_prefix
      return path_flag unless pkg_config_prefix

      mingw_dir = pkg_config_prefix.basename.to_s
      path = path_flag.sub(/\A#{Regexp.escape(flag_option)}/, "")
      path = path.sub(/\A\/#{Regexp.escape(mingw_dir)}/i) do
        pkg_config_prefix.to_s
      end
      "#{flag_option}#{path}"
    end

normalize_paths(paths) click to toggle source

    # File lib/pkg-config.rb, line 698
    def normalize_paths(paths)
      paths.reject do |path|
        path.empty? or !File.exist?(path)
      end
    end

parse_override_variables(override_variables) click to toggle source

    # File lib/pkg-config.rb, line 670
    def parse_override_variables(override_variables)
      variables = {}
      override_variables.split(",").each do |variable|
        name, value = variable.split("=", 2)
        variables[name] = value
      end
      variables
    end

parse_pc() click to toggle source

    # File lib/pkg-config.rb, line 635
    def parse_pc
      raise NotFoundError, ".pc doesn't exist: <#{@name}>" unless exist?
      @variables = {}
      @declarations = {}
      File.open(pc_path) do |input|
        current_line = +""
        input.each_line do |line|
          if line.dup.force_encoding("UTF-8").valid_encoding?
            line.force_encoding("UTF-8")
          end
          line = line.gsub(/#.*/, "").strip
          if line.end_with?("\\")
            current_line += line[0..-2]
            next
          end
          current_line += line
          case current_line
          when /^(#{IDENTIFIER_RE})\s*=\s*/
            match = Regexp.last_match
            @variables[match[1]] = match.post_match.strip
          when /^(#{IDENTIFIER_RE})\s*:\s*/
            match = Regexp.last_match
            @declarations[match[1]] = match.post_match.strip
          end
          current_line = +""
        end
      end
    end

parse_requires(requires) click to toggle source

    # File lib/pkg-config.rb, line 664
    def parse_requires(requires)
      return [] if requires.nil?
      requires_without_version = requires.gsub(/(?:<|>|<=|>=|=)\s*[\d.a-zA-Z_-]+\s*/, "")
      requires_without_version.split(/[,\s]+/)
    end

required_packages() click to toggle source

    # File lib/pkg-config.rb, line 686
    def required_packages
      collect_requires do |package|
        package.requires
      end
    end

split_lib_flags(libs_command_line) click to toggle source

    # File lib/pkg-config.rb, line 611
    def split_lib_flags(libs_command_line)
      all_flags = {}
      flags = []
      in_option = false
      libs_command_line.gsub(/-([Ll]) /, "\\1").split.each do |arg|
        if in_option
          flags << arg
          in_option = false
        else
          case arg
          when /-[lL]/
            next if all_flags.key?(arg)
            all_flags[arg] = true
            flags << arg
            in_option = true
          else
            flags << arg
          end
        end
      end
      flags
    end