mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
a73d8cd103
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@48427 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
177 lines
4.7 KiB
Ruby
Executable file
177 lines
4.7 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]
|
|
curdir = nil
|
|
make_p.scan(%r{Entering directory ['`](.*)'|^\# (GNU Make) |^CURDIR := (.*)|^([/0-9a-zA-Z._-]+):(.*)|^# (Finished Make data base on) |Leaving directory ['`](.*)'}) {
|
|
directory_enter = $1
|
|
data_base_start = $2
|
|
data_base_curdir = $3
|
|
rule_target = $4
|
|
rule_sources = $5
|
|
data_base_end = $6
|
|
directory_leave = $7
|
|
#p $~
|
|
if directory_enter
|
|
enter_dir = Pathname(directory_enter)
|
|
#p [:enter, enter_dir]
|
|
dirstack.push enter_dir
|
|
elsif data_base_start
|
|
curdir = nil
|
|
elsif data_base_curdir
|
|
curdir = Pathname(data_base_curdir)
|
|
elsif rule_target && rule_sources
|
|
target = rule_target
|
|
deps = rule_sources
|
|
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"
|
|
#p [curdir, target, deps]
|
|
dependencies[(curdir||dirstack.last) + target] ||= []
|
|
dependencies[(curdir||dirstack.last) + target] |= deps.map {|dep| (curdir||dirstack.last) + dep }
|
|
elsif data_base_end
|
|
curdir = nil
|
|
elsif directory_leave
|
|
leave_dir = Pathname(directory_leave)
|
|
#p [:leave, leave_dir]
|
|
if leave_dir != dirstack.last
|
|
warn "unexpected leave_dir : #{dirstack.last.inspect} != #{leave_dir.inspect}"
|
|
end
|
|
dirstack.pop
|
|
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|
|
|
rel = s.relative_path_from(cwd)
|
|
rel
|
|
}
|
|
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)
|
|
#pp make_deps
|
|
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
|