123456789_123456789_123456789_123456789_123456789_

Class: MakeMakefile::Depend::Scanner

Do not use. This class is for internal use only.
Relationships & Source Files
Inherits: Object
Defined in: lib/mkmf/depend.rb

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(root:, include_dirs:, quote_dirs: [], macros: {}, targets: [], aliases: {}, dependencies: {}, defined: [], undefined: [], cache: {}) ⇒ Scanner

[ GitHub ]

  
# File 'lib/mkmf/depend.rb', line 75

def initialize(root:, include_dirs:, quote_dirs: [], macros: {},
               targets: [], aliases: {}, dependencies: {}, defined: [],
               undefined: [], cache: {})
  @root = File.expand_path(root)
  @include_dirs = include_dirs.map {|dir| File.expand_path(dir, @root)}
  @quote_dirs = quote_dirs.map {|dir| File.expand_path(dir, @root)}
  @macros = macros
  @targets = targets.to_set
  @aliases = aliases
  @dependencies = dependencies
  @defined = defined.to_set
  @undefined = undefined.to_set
  @accessed = Set.new
  @unresolved = Set.new
  @cache = cache
end

Instance Attribute Details

#accessed (readonly)

[ GitHub ]

  
# File 'lib/mkmf/depend.rb', line 73

attr_reader :accessed, :unresolved

#unresolved (readonly)

[ GitHub ]

  
# File 'lib/mkmf/depend.rb', line 73

attr_reader :accessed, :unresolved

Instance Method Details

#expression_condition(expression) (private)

[ GitHub ]

  
# File 'lib/mkmf/depend.rb', line 176

def expression_condition(expression)
  case expression.sub(%r{/\*.*}, '').strip
  when /\A0+[LU]*(?:\z|\s*&&)/i
    return false
  when /\A1+[LU]*(?:\z|\s*\|\|)/i
    return true
  when /\A(!?)\s*defined\s*(?:\(\s*)?(\w+)/
    condition = symbol_condition($2)
    return condition if condition == :all
    return $1.empty? ? condition : !condition
  when /\A(!?)(\w+)\z/
    condition = symbol_condition($2)
    return condition if condition == :all
    return $1.empty? ? condition : !condition
  end
  :all
end

#relative(path) (private)

[ GitHub ]

  
# File 'lib/mkmf/depend.rb', line 244

def relative(path)
  prefix = @root + File::SEPARATOR
  path.start_with?(prefix) ? path.delete_prefix(prefix) : path
end

#resolve(name, current, quoted) (private)

[ GitHub ]

  
# File 'lib/mkmf/depend.rb', line 232

def resolve(name, current, quoted)
  dirs = []
  dirs << File.dirname(current) if quoted
  dirs.concat(@quote_dirs) if quoted
  dirs.concat(@include_dirs)
  dirs.each do |dir|
    path = File.expand_path(name, dir)
    return path if source_directives(path)
  end
  nil
end

#scan(source)

[ GitHub ]

  
# File 'lib/mkmf/depend.rb', line 92

def scan(source)
  dependencies = Set.new
  visit(File.expand_path(source, @root), dependencies, Set.new)
  dependencies.to_a.sort
end

#scan_include(argument, current, dependencies, visited) (private)

[ GitHub ]

  
# File 'lib/mkmf/depend.rb', line 194

def scan_include(argument, current, dependencies, visited)
  case argument.sub(%r{//.*\z}, '').strip
  when /\A([<"])([^<>"]+)[>"]/
    quoted = $1 == '"'
    name = $2
  when /\A([A-Za-z_]\w*)/
    name = $1
    unless @aliases.key?(name) || @dependencies.key?(name) ||
        @targets.include?(name)
      name = @macros[name]
    end
    quoted = true
  end
  return unless name

  if alias_path = @aliases[name]
    if virtual = @dependencies[name]
      dependencies.merge(virtual)
    else
      dependencies.add(name)
    end
    visit(File.expand_path(alias_path, @root), dependencies, visited, record: false)
    return
  end

  if virtual = @dependencies[name]
    dependencies.merge(virtual)
  elsif name.end_with?(".inc", ".rbinc", ".rbbin")
    dependencies.add(name)
  elsif path = resolve(name, current, quoted)
    visit(path, dependencies, visited)
  elsif @targets.include?(name)
    dependencies.add(name)
  else
    @unresolved.add(name)
  end
end

#source_directives(path) (private)

[ GitHub ]

  
# File 'lib/mkmf/depend.rb', line 142

def source_directives(path)
  path = File.expand_path(path)
  @accessed.add(path)
  return @cache[path] if @cache.key?(path)

  @cache[path] = if File.file?(path)
    File.open(path, "rb") do |file|
      directives = []
      logical_line = "".b
      file.each_line do |line|
        logical_line << line
        next if logical_line.sub!(/\\\r?\n\z/, "")

        if directive = logical_line[/\A\s*#\s*\K.*/]
          directives << directive
        end
        logical_line.clear
      end
      unless logical_line.empty?
        if directive = logical_line[/\A\s*#\s*\K.*/]
          directives << directive
        end
      end
      directives
    end
  end
end

#symbol_condition(symbol) (private)

[ GitHub ]

  
# File 'lib/mkmf/depend.rb', line 170

def symbol_condition(symbol)
  return true if @defined.include?(symbol)
  return false if @undefined.include?(symbol)
  :all
end

#visit(path, dependencies, visited, record: true) (private)

[ GitHub ]

  
# File 'lib/mkmf/depend.rb', line 100

def visit(path, dependencies, visited, record: true)
  path = File.expand_path(path)
  return if visited.include?(path)
  directives = source_directives(path)
  return unless directives
  visited.add(path)
  dependencies.add(relative(path)) if record

  conditions = []
  directives.each do |directive|
    case directive
    when /\Aifdef\s(\w)/
      conditions << symbol_condition($1)
    when /\Aifndef\s(\w)/
      condition = symbol_condition($1)
      conditions << (condition == :all ? :all : !condition)
    when /\Aif\s(.)/
      conditions << expression_condition($1)
    when /\Aelif\s(.)/
      conditions[-1] = if conditions[-1] == true
        false
      elsif conditions[-1] == false
        expression_condition($1)
      else
        :all
      end
    when /\Aelse\b/
      conditions[-1] = !conditions[-1] if [true, false].include?(conditions[-1])
    when /\Aendif\b/
      conditions.pop
    else
      next if conditions.include?(false)
      case directive
      when /\A(?:line\s)?\d\s"([^"<>])"/
        dependencies.add($1.delete_prefix("./"))
      when /\Ainclude(?:_next)?\s(.)/
        scan_include($1, path, dependencies, visited)
      end
    end
  end
end