From 1b2109f629a2503eef4360e050aa52e0d9c8050b Mon Sep 17 00:00:00 2001 From: akr Date: Thu, 20 Nov 2014 14:18:37 +0000 Subject: * tool/update-deps: Insert all dependencies found by compiler. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@48514 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- tool/update-deps | 286 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 168 insertions(+), 118 deletions(-) (limited to 'tool') diff --git a/tool/update-deps b/tool/update-deps index f98707a674..3ab69dd1a9 100755 --- a/tool/update-deps +++ b/tool/update-deps @@ -25,6 +25,7 @@ require 'optparse' require 'stringio' require 'pathname' +require 'open3' require 'pp' ENV['LC_ALL'] = 'C' @@ -34,6 +35,9 @@ $opt_a = false $opt_actual_fix = false $i_not_found = false +DEPENDENCIES_SECTION_START_MARK = "\# AUTOGENERATED DEPENDENCIES START\n" +DEPENDENCIES_SECTION_END_MARK = "\# AUTOGENERATED DEPENDENCIES END\n" + def optionparser op = OptionParser.new op.banner = 'Usage: ruby tool/update-deps' @@ -45,7 +49,11 @@ end def read_make_deps(cwd) dependencies = {} - make_p = `make -p all miniruby ruby golf 2> /dev/null` + make_p, make_p_stderr, make_p_status = Open3.capture3("make -p all miniruby ruby golf") + if !make_p_status.success? + puts make_p_stderr + raise "make failed" + end dirstack = [cwd] curdir = nil make_p.scan(%r{Entering\ directory\ ['`](.*)'| @@ -108,7 +116,7 @@ end # raise ArgumentError, "can not find #{filename} (hint: #{hint0})" #end -def read_single_actual_deps(path_i, cwd) +def read_single_cc_deps(path_i, cwd) files = {} path_i.each_line.with_index {|line, lineindex| next if /\A\# \d+ "(.*)"/ !~ line @@ -130,7 +138,7 @@ def read_single_actual_deps(path_i, cwd) dep = compiler_wd + dep end if !dep.file? - warn "file not found: #{dep}" + warn "warning: file not found: #{dep}" next end next if !dep.to_s.start_with?(cwd.to_s) # omit system headers. @@ -139,18 +147,18 @@ def read_single_actual_deps(path_i, cwd) deps end -def read_actual_deps(cwd) +def read_cc_deps(cwd) deps = {} Pathname.glob('**/*.o').sort.each {|fn_o| fn_i = fn_o.sub_ext('.i') if !fn_i.exist? - warn "not found: #{fn_i}" + warn "warning: not found: #{fn_i}" $i_not_found = true next end path_o = cwd + fn_o path_i = cwd + fn_i - deps[path_o] = read_single_actual_deps(path_i, cwd) + deps[path_o] = read_single_cc_deps(path_i, cwd) } deps end @@ -165,12 +173,12 @@ def concentrate(dependencies, cwd) rel } if %r{\A\.\.(/|\z)} =~ target.to_s - warn "out of tree target: #{target}" + warn "warning: out of tree target: #{target}" next end sources = sources.reject {|s| if %r{\A\.\.(/|\z)} =~ s.to_s - warn "out of tree source: #{s}" + warn "warning: out of tree source: #{s}" true else false @@ -226,7 +234,7 @@ def in_makefile(target, source) ["enc/depend", target2, source2] when %r{\Aext/} unless File.exist?("#{File.dirname(target)}/extconf.rb") - warn "not found: #{File.dirname(target)}/extconf.rb" + warn "warning: not found: #{File.dirname(target)}/extconf.rb" end target2 = File.basename(target) case source @@ -235,10 +243,10 @@ def in_makefile(target, source) when %r{\A\.ext/include/[^/]+/ruby/} then source2 = "$(arch_hdrdir)/ruby/#{$'}" when %r{\A#{Regexp.escape File.dirname(target)}/extconf\.h\z} then source2 = "$(RUBY_EXTCONF_H)" when %r{\A#{Regexp.escape File.dirname(target)}/} then source2 = $' - when 'id.h' then source2 = '{$(VPATH)}id.h' - when 'parse.h' then source2 = '{$(VPATH)}parse.h' - when 'lex.c' then source2 = '{$(VPATH)}lex.c' - when 'probes.h' then source2 = '{$(VPATH)}probes.h' + when 'id.h' then source2 = '$(topdir)/id.h' + when 'parse.h' then source2 = '$(topdir)/parse.h' + when 'lex.c' then source2 = '$(topdir)/lex.c' + when 'probes.h' then source2 = '$(topdir)/probes.h' else source2 = "$(top_srcdir)/#{source}" end ["#{File.dirname(target)}/depend", target2, source2] @@ -247,144 +255,186 @@ def in_makefile(target, source) end end -def compare_deps(make_deps, actual_deps, out=$stdout) - targets = sort_paths(actual_deps.keys) - 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] - sort_paths(actual_sources | make_sources).each {|source| - makefile, target2, source2 = in_makefile(target, source) - lines = begin - File.readlines(makefile) - rescue Errno::ENOENT - [] - end - #depline = "#{target2}: #{source2} \# #{target}: #{source}\n" - depline = "#{target2}: #{source2}\n" - if !make_sources.include?(source) - out.puts "add #{makefile} : #{depline}" - elsif !actual_sources.include?(source) - if lines.include? depline - out.puts "delL #{makefile} : #{depline}" # delL stands for del line - else - out.puts "delP #{makefile} : #{depline}" # delP stands for del prerequisite - end - else - if $opt_a - if lines.include? depline - out.puts "okL #{makefile} : #{depline}" # okL stands for ok line - else - out.puts "okP #{makefile} : #{depline}" # okP stands for ok prerequisite - end - end - end - } - end +def show_deps(tag, deps) + targets = sort_paths(deps.keys) + targets.each {|t| + sources = sort_paths(deps[t]) + sources.each {|s| + puts "#{tag} #{t}: #{s}" + } } end -def main_show(out=$stdout) +def detect_dependencies(out=$stdout) 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) - #pp actual_deps - actual_deps = concentrate(actual_deps, cwd) - #pp actual_deps - compare_deps(make_deps, actual_deps, out) + cc_deps = read_cc_deps(cwd) + #pp cc_deps + cc_deps = concentrate(cc_deps, cwd) + #pp cc_deps + return make_deps, cc_deps end -def extract_deplines(problems) - adds = {} - dels = {} - problems.each_line {|line| - case line - when /\Aadd (\S+) : (\S.*\n)\z/ - (adds[$1] ||= []) << $2 - when /\AdelL (\S+) : (\S.*\n)\z/ - (dels[$1] ||= []) << $2 - when /\AdelP (\S+) : (\S.*\n)\z/ - (dels[$1] ||= []) << $2 - when /\AokL (\S+) : (\S.*\n)\z/ - when /\AokP (\S+) : (\S.*\n)\z/ - (adds[$1] ||= []) << $2 - end +def compare_deps(make_deps, cc_deps, out=$stdout) + targets = make_deps.keys | cc_deps.keys + + makefiles = {} + + make_lines_hash = {} + make_deps.each {|t, sources| + sources.each {|s| + makefile, t2, s2 = in_makefile(t, s) + makefiles[makefile] = true + make_lines_hash[makefile] ||= Hash.new(false) + make_lines_hash[makefile]["#{t2}: #{s2}"] = true + } } - return adds, dels -end -DEPENDENCIES_SECTION_START_MARK = "\# AUTOGENERATED DEPENDENCIES START\n" -DEPENDENCIES_SECTION_END_MARK = "\# AUTOGENERATED DEPENDENCIES END\n" + cc_lines_hash = {} + cc_deps.each {|t, sources| + sources.each {|s| + makefile, t2, s2 = in_makefile(t, s) + makefiles[makefile] = true + cc_lines_hash[makefile] ||= Hash.new(false) + cc_lines_hash[makefile]["#{t2}: #{s2}"] = true + } + } -def main_actual_fix(problems) - adds, dels = extract_deplines(problems) - (adds.keys | dels.keys).sort.each {|makefile| + makefiles.keys.sort.each {|makefile| + cc_lines = cc_lines_hash[makefile] || Hash.new(false) + make_lines = make_lines_hash[makefile] || Hash.new(false) content = begin File.read(makefile) rescue Errno::ENOENT '' end - if /^#{Regexp.escape DEPENDENCIES_SECTION_START_MARK}((?:.*\n)*)#{Regexp.escape DEPENDENCIES_SECTION_END_MARK}/ =~ content + if /^#{Regexp.escape DEPENDENCIES_SECTION_START_MARK} + ((?:.*\n)*) + #{Regexp.escape DEPENDENCIES_SECTION_END_MARK}/x =~ content pre_post_part = [$`, $'] - lines = $1.lines.to_a + current_lines = Hash.new(false) + $1.each_line {|line| current_lines[line.chomp] = true } + (cc_lines.keys | current_lines.keys | make_lines.keys).sort.each {|line| + status = [cc_lines[line], current_lines[line], make_lines[line]] + case status + when [true, true, true] + # no problem + when [true, true, false] + out.puts "warning #{makefile} : #{line} (make doesn't detect written dependency)" + when [true, false, true] + out.puts "add_auto #{makefile} : #{line} (harmless)" # This is automatically updatable. + when [true, false, false] + out.puts "add_auto #{makefile} : #{line} (harmful)" # This is automatically updatable. + when [false, true, true] + out.puts "del_cc #{makefile} : #{line}" # Not automatically updatable because build on other OS may need the dependency. + when [false, true, false] + out.puts "del_cc #{makefile} : #{line} (Curious. make doesn't detect this dependency.)" # Not automatically updatable because build on other OS may need the dependency. + when [false, false, true] + out.puts "del_make #{makefile} : #{line}" # Not automatically updatable because the dependency is written manually. + else + raise "unexpected status: #{status.inspect}" + end + } else - pre_post_part = nil - lines = [] + (cc_lines.keys | make_lines.keys).sort.each {|line| + status = [cc_lines[line], make_lines[line]] + case status + when [true, true] + # no problem + when [true, false] + out.puts "add_manual #{makefile} : #{line}" # Not automatically updatable because makefile has no section to update automatically. + when [false, true] + out.puts "del_manual #{makefile} : #{line}" # Not automatically updatable because makefile has no section to update automatically. + else + raise "unexpected status: #{status.inspect}" + end + } end + } +end - lines_original = lines.dup +def main_show(out=$stdout) + make_deps, cc_deps = detect_dependencies(out) + compare_deps(make_deps, cc_deps, out) +end - if dels[makefile] - lines -= dels[makefile] - end - if adds[makefile] - lines.concat(adds[makefile] - lines) +def extract_deplines(problems) + adds = {} + others = {} + problems.each_line {|line| + case line + when /\Aadd_auto (\S+) : ((\S+): (\S+))/ + (adds[$1] ||= []) << [line, "#{$2}\n"] + when /\A(?:del_cc|del_make|add_manual|del_manual|warning) (\S+) : / + (others[$1] ||= []) << line + else + raise "unexpected line: #{line.inspect}" end + } + return adds, others +end - if lines == lines_original - next +def main_actual_fix(problems) + adds, others = extract_deplines(problems) + (adds.keys | others.keys).sort.each {|makefile| + content = begin + File.read(makefile) + rescue Errno::ENOENT + nil end - lines.sort! - - if pre_post_part - new_content = [ - pre_post_part.first, - DEPENDENCIES_SECTION_START_MARK, - *lines, - DEPENDENCIES_SECTION_END_MARK, - pre_post_part.last - ].join - tmp_makefile = "#{makefile}.new#{$$}" - File.write(tmp_makefile, new_content) - File.rename tmp_makefile, makefile - puts "modified: #{makefile}" + if content && + /^#{Regexp.escape DEPENDENCIES_SECTION_START_MARK} + ((?:.*\n)*) + #{Regexp.escape DEPENDENCIES_SECTION_END_MARK}/x =~ content + pre_dep_post = [$`, $1, $'] else + pre_dep_post = nil + end + + if pre_dep_post && adds[makefile] + pre_lines, dep_lines, post_lines = pre_dep_post + dep_lines = dep_lines.lines.to_a + add_lines = adds[makefile].map(&:last) + new_lines = (dep_lines | add_lines).sort.uniq new_content = [ + pre_lines, DEPENDENCIES_SECTION_START_MARK, - *lines, + *new_lines, DEPENDENCIES_SECTION_END_MARK, + post_lines ].join - if !File.exist?(makefile) - if !lines.empty? - File.open(makefile, 'w') {|f| - f.print new_content - } - puts "created: #{makefile}" - end + if content != new_content + puts "modified: #{makefile}" + tmp_makefile = "#{makefile}.new.#{$$}" + File.write(tmp_makefile, new_content) + File.rename tmp_makefile, makefile + (add_lines - lines).each {|line| puts " added #{line}" } else + puts "not modified: #{makefile}" + end + if others[makefile] + others[makefile].each {|line| puts " #{line}" } + end + else + if pre_dep_post + puts "no addtional lines: #{makefile}" + elsif content puts "no dependencies section: #{makefile}" - (lines_original - lines).each {|line| - puts " del: #{line}" - } - (lines - lines_original).each {|line| - puts " add: #{line}" - } + else + puts "no makefile: #{makefile}" + end + if adds[makefile] + puts " warning: dependencies section was exist at previous phase." + end + if adds[makefile] + adds[makefile].map(&:first).each {|line| puts " #{line}" } + end + if others[makefile] + others[makefile].each {|line| puts " #{line}" } end end } @@ -410,5 +460,5 @@ end run if $i_not_found - warn "missing *.i files, see help in #$0 and ensure ccache is disabled" + warn "warning: missing *.i files, see help in #$0 and ensure ccache is disabled" end -- cgit v1.2.3