mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	* tool/update-deps: Use "make -p all miniruby ruby golf" to extract deendencies in makefiles. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@40246 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
		
			
				
	
	
		
			157 lines
		
	
	
	
		
			4.1 KiB
		
	
	
	
		
			Ruby
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			157 lines
		
	
	
	
		
			4.1 KiB
		
	
	
	
		
			Ruby
		
	
	
		
			Executable file
		
	
	
	
	
#!/usr/bin/ruby
 | 
						|
 | 
						|
# tool/update-deps verify makefile dependencies.
 | 
						|
 | 
						|
# Requirements:
 | 
						|
#   gcc 4.5 (for -save-temps=obj option)
 | 
						|
#   GNU make (for -p option)
 | 
						|
#
 | 
						|
# Usage:
 | 
						|
#   1. Compile ruby with -save-temps=obj option.
 | 
						|
#      Ex.  ./configure debugflags='-save-temps=obj -g' && make all golf
 | 
						|
#   2. Run tool/update-deps to show dependency problems.
 | 
						|
#      Ex.  ruby tool/update-deps
 | 
						|
 | 
						|
require 'pathname'
 | 
						|
require 'pp'
 | 
						|
 | 
						|
ENV['LC_ALL'] = 'C'
 | 
						|
 | 
						|
def read_make_deps(cwd)
 | 
						|
  dependencies = {}
 | 
						|
  make_p = `make -p all miniruby ruby golf 2> /dev/null`
 | 
						|
  dirstack = [cwd]
 | 
						|
  make_p.scan(%r{Entering directory `(.*)'|Leaving directory `(.*)'|^([/0-9a-zA-Z._-]+):(.*)}) {
 | 
						|
    if $1
 | 
						|
      enter_dir = Pathname($1)
 | 
						|
      #p [:enter, enter_dir]
 | 
						|
      dirstack.push enter_dir
 | 
						|
    elsif $2
 | 
						|
      leave_dir = Pathname($2)
 | 
						|
      #p [:leave, leave_dir]
 | 
						|
      if leave_dir != dirstack.last
 | 
						|
        warn "unexpected leave_dir : #{dirstack.last.inspect} != #{leave_dir.inspect}"
 | 
						|
      end
 | 
						|
      dirstack.pop
 | 
						|
    else
 | 
						|
      target = $3
 | 
						|
      deps = $4
 | 
						|
      deps = deps.scan(%r{[/0-9a-zA-Z._-]+})
 | 
						|
      next if /\.o\z/ !~ target.to_s
 | 
						|
      next if /\A\./ =~ target.to_s # skip rules such as ".c.o"
 | 
						|
      dependencies[dirstack.last + target] ||= []
 | 
						|
      dependencies[dirstack.last + target] |= deps.map {|dep| dirstack.last + dep }
 | 
						|
    end
 | 
						|
  }
 | 
						|
  dependencies
 | 
						|
end
 | 
						|
 | 
						|
#def guess_compiler_wd(filename, hint0)
 | 
						|
#  hint = hint0
 | 
						|
#  begin
 | 
						|
#    guess = hint + filename
 | 
						|
#    if guess.file?
 | 
						|
#      return hint
 | 
						|
#    end
 | 
						|
#    hint = hint.parent
 | 
						|
#  end while hint.to_s != '.'
 | 
						|
#  raise ArgumentError, "can not find #{filename} (hint: #{hint0})"
 | 
						|
#end
 | 
						|
 | 
						|
def read_single_actual_deps(path_i, cwd)
 | 
						|
  files = {}
 | 
						|
  path_i.each_line.with_index {|line, lineindex|
 | 
						|
    next if /\A\# \d+ "(.*)"/ !~ line
 | 
						|
    files[$1] = lineindex
 | 
						|
  }
 | 
						|
  # gcc emits {# 1 "/absolute/directory/of/the/source/file//"} at 2nd line.
 | 
						|
  compiler_wd = files.keys.find {|f| %r{\A/.*//\z} =~ f }
 | 
						|
  if compiler_wd
 | 
						|
    files.delete compiler_wd
 | 
						|
    compiler_wd = Pathname(compiler_wd.sub(%r{//\z}, ''))
 | 
						|
  else
 | 
						|
    raise "compiler working directory not found"
 | 
						|
  end
 | 
						|
  deps = []
 | 
						|
  files.each_key {|dep|
 | 
						|
    next if %r{\A<.*>\z} =~ dep # omit <command-line>, etc.
 | 
						|
    dep = Pathname(dep)
 | 
						|
    if dep.relative?
 | 
						|
      dep = compiler_wd + dep
 | 
						|
    end
 | 
						|
    if !dep.file?
 | 
						|
      warn "file not found: #{dep}"
 | 
						|
      next
 | 
						|
    end
 | 
						|
    next if !dep.to_s.start_with?(cwd.to_s) # omit system headers.
 | 
						|
    deps << dep
 | 
						|
  }
 | 
						|
  deps
 | 
						|
end
 | 
						|
 | 
						|
def read_actual_deps(cwd)
 | 
						|
  deps = {}
 | 
						|
  Pathname.glob('**/*.o').sort.each {|fn_o|
 | 
						|
    fn_i = fn_o.sub_ext('.i')
 | 
						|
    next if !fn_i.exist?
 | 
						|
    path_o = cwd + fn_o
 | 
						|
    path_i = cwd + fn_i
 | 
						|
    deps[path_o] = read_single_actual_deps(path_i, cwd)
 | 
						|
  }
 | 
						|
  deps
 | 
						|
end
 | 
						|
 | 
						|
def concentrate(dependencies, cwd)
 | 
						|
  deps = {}
 | 
						|
  dependencies.keys.sort.each {|target|
 | 
						|
    sources = dependencies[target]
 | 
						|
    target = target.relative_path_from(cwd)
 | 
						|
    sources = sources.map {|s| s.relative_path_from(cwd) }
 | 
						|
    if %r{\A\.\.(/|\z)} =~ target.to_s
 | 
						|
      warn "out of tree target: #{target}"
 | 
						|
      next
 | 
						|
    end
 | 
						|
    sources = sources.reject {|s|
 | 
						|
      if %r{\A\.\.(/|\z)} =~ s.to_s
 | 
						|
        warn "out of tree source: #{s}"
 | 
						|
        true
 | 
						|
      else
 | 
						|
        false
 | 
						|
      end
 | 
						|
    }
 | 
						|
    deps[target] = sources
 | 
						|
  }
 | 
						|
  deps
 | 
						|
end
 | 
						|
 | 
						|
def compare_deps(make_deps, actual_deps)
 | 
						|
  targets = actual_deps.keys.sort_by {|t|
 | 
						|
    ary = t.to_s.split(%r{/})
 | 
						|
    ary.map.with_index {|e, i| i == ary.length-1 ? [0, e] : [1, e] } # regular file first, directories last.
 | 
						|
  }
 | 
						|
  targets.each {|target|
 | 
						|
    actual_sources = actual_deps[target]
 | 
						|
    if !make_deps.has_key?(target)
 | 
						|
      warn "no makefile dependency for #{target}"
 | 
						|
    else
 | 
						|
      make_sources = make_deps[target]
 | 
						|
      lacks = actual_sources - make_sources
 | 
						|
      puts "#{target} lacks: #{lacks.join(" ")}" if !lacks.empty?
 | 
						|
      unused = make_sources - actual_sources
 | 
						|
      puts "#{target} unuse: #{unused.join(" ")}" if !unused.empty?
 | 
						|
    end
 | 
						|
  }
 | 
						|
end
 | 
						|
 | 
						|
def main
 | 
						|
  cwd = Pathname.pwd
 | 
						|
  make_deps = read_make_deps(cwd)
 | 
						|
  make_deps = concentrate(make_deps, cwd)
 | 
						|
  #pp make_deps
 | 
						|
  actual_deps = read_actual_deps(cwd)
 | 
						|
  actual_deps = concentrate(actual_deps, cwd)
 | 
						|
  #pp actual_deps
 | 
						|
  compare_deps(make_deps, actual_deps)
 | 
						|
end
 | 
						|
 | 
						|
main
 |