#!/usr/bin/ruby

# tool/update-deps assists you to update dependencies in common.mk.

# This script uses preprocessed source files (*.i) to extract
# dependencies.
# It is possible to generate *.i using gcc with -save-temps option as:
#
#   ./configure CFLAGS='-save-temps'
#   make all golf
#
# After that, tool/update-deps generate common.mk with up-to-date dependencies.
# Currently, the result is not perfect around version.o, compile.o, etc.
# So you must see each changes and incorporate right changes.
#
#   ./tool/update-deps > z
#   wdiff =(sed -e 's/\\$//' common.mk ) =(sed -e 's/\\$//' z) |less -j 5 -p '\{\+|\+\}|\[-|-\]'
#   vi common.mk

src = File.read("common.mk")

includes_macro = {}
src.scan(/^([A-Z_]+_H_INCLUDES)[ \t]*=(([^\\\n]|\\(.|\n))*)\n/) {
  name = $1
  vals = $2
  #STDERR.puts vals.inspect
  vals.gsub!(/\\\n/, ' ')
  vals.gsub!(/\{\$\(VPATH\)\}/, '')
  vals.gsub!(/thread_\$\(THREAD_MODEL\)/, 'thread_pthread')
  vals = vals.strip.split(/\s+/)
  includes_macro[name] = vals
  #STDERR.puts  [name, vals].inspect
}

begin
  again = false
  includes_macro.each  {|name, vals|
    vals.map! {|val|
      if /\A\$\((.*_H_INCLUDES)\)\z/ =~ val
        again = true
        includes_macro.fetch($1)
      else
        val
      end
    }
    vals.flatten!
  }
end while again

src.gsub!(/^([0-9a-z._]+)\.\$\(OBJEXT\):(.*\n(?:[ 	].*\n)*)/) {
  #STDERR.puts  $&.inspect
  matched = $&
  basename = $1
  deps = $2
  dd = deps.dup
  dd.gsub!(/\{\$\(VPATH\)\}/, '')
  dd.gsub!(/\\\n/, ' ')
  dd.gsub!(/thread_\$\(THREAD_MODEL\)/, 'thread_pthread')
  used_imacro = {}
  includes_macro.each {|k, v|
    if dd.sub!(/\$\(#{Regexp.escape k}\)/) { v.join(' ') }
      used_imacro[k] = true
    end
  }
  dd = dd.strip.split(/\s+/)
  if !File.file?("#{basename}.o")
    warn "#{basename}.o not found."
  else
    unless File.file? "#{basename}.i"
      puts "#{basename}.i not found."
      next
    end
    incs = []
    File.foreach("#{basename}.i") {|line|
      next unless /^# \d+ "([^"]*)"/ =~ line
      inc = $1
      next if %r{\A[/<]} =~ inc
      inc.sub!(%r{\A\./}, '')
      inc.sub!(%r{\Ainclude/ruby/}, '') or
      inc.sub!(%r{\Ainclude/}, '') or
      inc.sub!(%r{\A\.ext/include/[^/]+/ruby/}, '') or
      inc.sub!(%r{\Aenc/}, '') or
      inc.sub!(%r{\Amissing/}, '')
      #p inc
      incs << inc
    }
    incs.uniq!
    incs = incs.sort_by {|inc| [(dd.index(inc) || dd.length), incs.index(inc)] }
    add = incs - dd
    if !add.empty? || true
      if incs[0] != dd[0]
        raise "first file not matched: #{incs[0].inspect} v.s. #{dd[0].inspect}"
      end
      depline = "#{basename}.$(OBJEXT):"
      used_imacro.each_key {|k|
        if includes_macro[k].all? {|v| incs.include? v }
          im = "$(#{k})"
          incs.map! {|inc|
            if includes_macro[k].include? inc
              im0 = im
              im = nil
              im0
            else
              inc
            end
          }
          incs.compact!
        else
          needless = includes_macro[k].reject {|v| incs.include? v }
          STDERR.puts "#{basename}.$(OBJEXT) can't use #{k}. #{needless.join(' ')} is not used."
        end
      }

      incs.each {|inc|
        inc = inc.sub(/\Athread_pthread/, 'thread_$(THREAD_MODEL)')
        if /_INCLUDES\)\z/ =~ inc
          # use $(RUBY_H_INCLUDES) as is.
        elsif inc == 'revision.h'
          inc = '$(srcdir)/revision.h'
        else
          inc = "{$(VPATH)}#{inc}"
        end
        depline << " #{inc}"
      }
      lines = []
      while 72 < depline.length && depline.sub!(/\A(.{0,72}|.{72}.*?) /, '')
        lines << $&
      end
      lines << depline
      matched = lines.join("\\\n  ")
      matched << "\n"
    end
  end
  #STDERR.puts  matched.inspect
  matched
}

puts src

