2011-06-17 19:15:47 -04:00
#!/usr/bin/ruby
2013-04-11 08:03:23 -04:00
# tool/update-deps verify makefile dependencies.
2011-06-17 19:15:47 -04:00
2013-04-11 08:03:23 -04:00
# Requirements:
# gcc 4.5 (for -save-temps=obj option)
# GNU make (for -p option)
2011-06-17 19:15:47 -04:00
#
2014-11-16 04:22:43 -05:00
# Warning: ccache (and similar tools) must be disabled for
# -save-temps=obj to work properly.
#
2013-04-11 08:03:23 -04:00
# 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.
2018-09-16 21:31:06 -04:00
# Ex. ./ruby tool/update-deps
2014-11-15 00:15:49 -05:00
# 3. Use --fix to fix makefiles.
2018-09-16 21:31:06 -04:00
# Ex. ./ruby tool/update-deps --fix
2014-11-15 00:15:49 -05:00
#
# Other usages:
# * Fix makefiles using previously detected dependency problems
# Ex. ruby tool/update-deps --actual-fix [file]
2021-01-05 09:13:53 -05:00
# "ruby tool/update-deps --fix" is the same as "ruby tool/update-deps | ruby tool/update-deps --actual-fix".
2011-06-17 19:15:47 -04:00
2014-11-15 00:15:49 -05:00
require 'optparse'
require 'stringio'
2013-04-11 08:03:23 -04:00
require 'pathname'
2014-11-20 09:18:37 -05:00
require 'open3'
2013-04-11 08:03:23 -04:00
require 'pp'
2011-06-17 19:15:47 -04:00
2016-07-02 17:01:04 -04:00
# When out-of-place build, files may be built in source directory or
2014-11-27 07:51:04 -05:00
# build directory.
# Some files are always built in the source directory.
# Some files are always built in the build directory.
# Some files are built in the source directory for tarball but build directory for repository (svn).
=begin
How to build test directories.
VER=2.2.0
REV=48577
tar xf ruby-$VER-r$REV.tar.xz
cp -a ruby-$VER-r$REV tarball_source_dir_original
mv ruby-$VER-r$REV tarball_source_dir_after_build
2018-07-28 06:00:27 -04:00
svn co -q -r$REV https://svn.ruby-lang.org/repos/ruby/trunk ruby
2014-11-27 07:51:04 -05:00
(cd ruby; autoconf)
cp -a ruby repo_source_dir_original
mv ruby repo_source_dir_after_build
mkdir tarball_build_dir repo_build_dir tarball_install_dir repo_install_dir
(cd tarball_build_dir; ../tarball_source_dir_after_build/configure --prefix=$(cd ../tarball_install_dir; pwd) && make all golf install) > tarball.log 2>&1
(cd repo_build_dir; ../repo_source_dir_after_build/configure --prefix=$(cd ../repo_install_dir; pwd) && make all golf install) > repo.log 2>&1
ruby -rpp -rfind -e '
ds = %w[
repo_source_dir_original
repo_source_dir_after_build
repo_build_dir
tarball_source_dir_original
tarball_source_dir_after_build
tarball_build_dir
]
files = {}
ds.each {|d|
files[d] = {}
Dir.chdir(d) { Find.find(".") {|f| files[d][f] = true if %r{\.(c|h|inc|dmyh)\z} =~ f } }
}
result = {}
files_union = files.values.map {|h| h.keys }.flatten.uniq.sort
files_union.each {|f|
k = files.map {|d,h| h[f] ? d : nil }.compact.sort
next if k == %w[repo_source_dir_after_build repo_source_dir_original tarball_source_dir_after_build tarball_source_dir_original]
next if k == %w[repo_build_dir tarball_build_dir] && File.basename(f) == "extconf.h"
result[k] ||= []
result[k] << f
}
result.each {|k,v|
k.each {|d|
puts d
}
v.each {|f|
puts " " + f.sub(%r{\A\./}, "")
}
puts
}
' | tee compare.log
=end
# Files built in the source directory.
# They can be referenced as $(top_srcdir)/filename.
# % ruby -e 'def g(d) Dir.chdir(d) { Dir["**/*.{c,h,inc,dmyh}"] } end; puts((g("repo_source_dir_after_build") - g("repo_source_dir_original")).sort)'
FILES_IN_SOURCE_DIRECTORY = %w[
revision.h
]
# Files built in the build directory (except extconf.h).
# They can be referenced as $(topdir)/filename.
# % ruby -e 'def g(d) Dir.chdir(d) { Dir["**/*.{c,h,inc,dmyh}"] } end; puts(g("tarball_build_dir").reject {|f| %r{/extconf.h\z} =~ f }.sort)'
FILES_IN_BUILD_DIRECTORY = %w[
2014-11-21 04:12:56 -05:00
encdb.h
2014-11-27 07:51:04 -05:00
ext/etc/constdefs.h
ext/socket/constdefs.c
ext/socket/constdefs.h
probes.h
transdb.h
verconf.h
]
# They are built in the build directory if the source is obtained from the repository.
# However they are pre-built for tarball and they exist in the source directory extracted from the tarball.
# % ruby -e 'def g(d) Dir.chdir(d) { Dir["**/*.{c,h,inc,dmyh}"] } end; puts((g("repo_build_dir") & g("tarball_source_dir_original")).sort)'
FILES_NEED_VPATH = %w[
ext/rbconfig/sizeof/sizes.c
ext/ripper/eventids1.c
ext/ripper/eventids2table.c
ext/ripper/ripper.c
golf_prelude.c
2014-11-21 04:12:56 -05:00
id.c
id.h
2014-11-27 07:51:04 -05:00
insns.inc
insns_info.inc
known_errors.inc
2014-11-21 04:12:56 -05:00
lex.c
miniprelude.c
2022-09-05 00:53:46 -04:00
mjit_compile_attr.inc
2014-11-21 04:12:56 -05:00
newline.c
2014-11-27 07:51:04 -05:00
node_name.inc
opt_sc.inc
optinsn.inc
optunifs.inc
2014-11-21 04:12:56 -05:00
parse.c
parse.h
2014-11-27 07:51:04 -05:00
probes.dmyh
vm.inc
vmtc.inc
enc/trans/big5.c
enc/trans/chinese.c
enc/trans/emoji.c
enc/trans/emoji_iso2022_kddi.c
enc/trans/emoji_sjis_docomo.c
enc/trans/emoji_sjis_kddi.c
enc/trans/emoji_sjis_softbank.c
enc/trans/escape.c
enc/trans/gb18030.c
enc/trans/gbk.c
enc/trans/iso2022.c
enc/trans/japanese.c
enc/trans/japanese_euc.c
enc/trans/japanese_sjis.c
enc/trans/korean.c
enc/trans/single_byte.c
enc/trans/utf8_mac.c
enc/trans/utf_16_32.c
2014-11-21 04:12:56 -05:00
]
2014-11-27 07:51:04 -05:00
# Multiple files with same filename.
2014-11-21 04:12:56 -05:00
# It is not good idea to refer them using VPATH.
2014-12-19 08:34:37 -05:00
# Files in FILES_SAME_NAME_INC is referenced using $(hdrdir).
# Files in FILES_SAME_NAME_TOP is referenced using $(top_srcdir).
FILES_SAME_NAME_INC = %w[
2019-02-07 21:46:22 -05:00
include/ruby.h
2014-11-21 04:12:56 -05:00
include/ruby/ruby.h
include/ruby/version.h
]
2011-06-17 19:15:47 -04:00
2014-12-19 08:34:37 -05:00
FILES_SAME_NAME_TOP = %w[
version.h
]
2014-11-27 07:51:04 -05:00
# Other source files exist in the source directory.
2014-11-21 04:12:56 -05:00
def in_makefile(target, source)
target = target.to_s
source = source.to_s
case target
2018-12-03 08:12:34 -05:00
when %r{\A[^/]*\z}, %r{\Acoroutine/}
2014-11-21 04:12:56 -05:00
target2 = "#{target.sub(/\.o\z/, '.$(OBJEXT)')}"
case source
2014-11-27 07:51:04 -05:00
when *FILES_IN_SOURCE_DIRECTORY then source2 = "$(top_srcdir)/#{source}"
when *FILES_IN_BUILD_DIRECTORY then source2 = "{$(VPATH)}#{source}" # VPATH is not used now but it may changed in future.
2014-11-21 04:12:56 -05:00
when *FILES_NEED_VPATH then source2 = "{$(VPATH)}#{source}"
2014-12-19 08:34:37 -05:00
when *FILES_SAME_NAME_INC then source2 = "$(hdrdir)/#{source.sub(%r{\Ainclude/},'')}"
when *FILES_SAME_NAME_TOP then source2 = "$(top_srcdir)/#{source}"
2014-11-21 04:12:56 -05:00
when 'thread_pthread.c' then source2 = '{$(VPATH)}thread_$(THREAD_MODEL).c'
when 'thread_pthread.h' then source2 = '{$(VPATH)}thread_$(THREAD_MODEL).h'
when %r{\A[^/]*\z} then source2 = "{$(VPATH)}#{File.basename source}"
when %r{\A\.ext/include/[^/]+/ruby/} then source2 = "{$(VPATH)}#{$'}"
when %r{\Ainclude/ruby/} then source2 = "{$(VPATH)}#{$'}"
when %r{\Aenc/} then source2 = "{$(VPATH)}#{$'}"
when %r{\Amissing/} then source2 = "{$(VPATH)}#{$'}"
when %r{\Accan/} then source2 = "$(CCAN_DIR)/#{$'}"
when %r{\Adefs/} then source2 = "{$(VPATH)}#{source}"
2018-12-03 08:12:34 -05:00
when %r{\Acoroutine/} then source2 = "{$(VPATH)}$(COROUTINE_H)"
2014-11-21 04:12:56 -05:00
else source2 = "$(top_srcdir)/#{source}"
end
["common.mk", target2, source2]
when %r{\Aenc/}
target2 = "#{target.sub(/\.o\z/, '.$(OBJEXT)')}"
case source
2014-11-27 07:51:04 -05:00
when *FILES_IN_SOURCE_DIRECTORY then source2 = "$(top_srcdir)/#{source}"
when *FILES_IN_BUILD_DIRECTORY then source2 = source
2014-11-21 04:12:56 -05:00
when *FILES_NEED_VPATH then source2 = source
2014-12-19 08:34:37 -05:00
when *FILES_SAME_NAME_INC then source2 = "$(hdrdir)/#{source.sub(%r{\Ainclude/},'')}"
when *FILES_SAME_NAME_TOP then source2 = "$(top_srcdir)/#{source}"
2014-11-21 04:12:56 -05:00
when %r{\A\.ext/include/[^/]+/ruby/} then source2 = $'
when %r{\Ainclude/ruby/} then source2 = $'
2019-02-12 07:15:30 -05:00
when %r{\Aenc/unicode/[\d.]+/} then source2 = '$(UNICODE_HDR_DIR)/' + $'
2014-11-21 04:12:56 -05:00
when %r{\Aenc/} then source2 = source
else source2 = "$(top_srcdir)/#{source}"
end
["enc/depend", target2, source2]
when %r{\Aext/}
2019-02-14 00:42:14 -05:00
targetdir = File.dirname(target)
unless File.exist?("#{targetdir}/extconf.rb")
warn "warning: not found: #{targetdir}/extconf.rb"
2014-11-21 04:12:56 -05:00
end
target2 = File.basename(target)
2014-11-27 07:51:04 -05:00
relpath = Pathname(source).relative_path_from(Pathname(target).dirname).to_s
2014-11-21 04:12:56 -05:00
case source
2014-11-27 07:51:04 -05:00
when *FILES_IN_SOURCE_DIRECTORY then source2 = "$(top_srcdir)/#{source}"
when *FILES_IN_BUILD_DIRECTORY then source2 = relpath
when *FILES_NEED_VPATH then source2 = "{$(VPATH)}#{File.basename source}"
2014-12-19 08:34:37 -05:00
when *FILES_SAME_NAME_INC then source2 = "$(hdrdir)/#{source.sub(%r{\Ainclude/},'')}"
when *FILES_SAME_NAME_TOP then source2 = "$(top_srcdir)/#{source}"
2014-11-21 04:12:56 -05:00
when %r{\A\.ext/include/[^/]+/ruby/} then source2 = "$(arch_hdrdir)/ruby/#{$'}"
when %r{\Ainclude/} then source2 = "$(hdrdir)/#{$'}"
2019-02-14 00:42:14 -05:00
when %r{\A#{Regexp.escape targetdir}/extconf\.h\z} then source2 = "$(RUBY_EXTCONF_H)"
when %r{\A#{Regexp.escape targetdir}/} then source2 = $'
when %r{\A#{Regexp.escape File.dirname(targetdir)}/} then source2 = "$(srcdir)/../#{$'}"
2014-11-21 04:12:56 -05:00
else source2 = "$(top_srcdir)/#{source}"
end
["#{File.dirname(target)}/depend", target2, source2]
else
raise "unexpected target: #{target}"
end
end
2014-11-15 00:15:49 -05:00
2014-11-20 09:18:37 -05:00
DEPENDENCIES_SECTION_START_MARK = "\# AUTOGENERATED DEPENDENCIES START\n"
DEPENDENCIES_SECTION_END_MARK = "\# AUTOGENERATED DEPENDENCIES END\n"
2014-11-21 04:12:56 -05:00
def init_global
ENV['LC_ALL'] = 'C'
2018-11-18 18:29:40 -05:00
if mkflag0 = ENV['GNUMAKEFLAGS'] and (mkflag = mkflag0.sub(/(\A|\s+)-j\d*(?=\s+|\z)/, '')) != mkflag0
2018-01-09 01:55:54 -05:00
mkflag.strip!
ENV['GNUMAKEFLAGS'] = mkflag
end
2014-11-21 04:12:56 -05:00
$opt_fix = false
$opt_a = false
$opt_actual_fix = false
$i_not_found = false
end
2014-11-15 00:15:49 -05:00
def optionparser
op = OptionParser.new
op.banner = 'Usage: ruby tool/update-deps'
op.def_option('-a', 'show valid dependencies') { $opt_a = true }
op.def_option('--fix') { $opt_fix = true }
op.def_option('--actual-fix') { $opt_actual_fix = true }
op
end
2013-04-11 08:03:23 -04:00
def read_make_deps(cwd)
dependencies = {}
2020-09-05 10:15:59 -04:00
make_p, make_p_stderr, make_p_status = Open3.capture3("make -p all miniruby exe/ruby golf")
2014-11-27 09:42:05 -05:00
File.open('update-deps.make.out.log', 'w') {|f| f.print make_p }
File.open('update-deps.make.err.log', 'w') {|f| f.print make_p_stderr }
2014-11-20 09:18:37 -05:00
if !make_p_status.success?
puts make_p_stderr
raise "make failed"
end
2013-04-11 08:03:23 -04:00
dirstack = [cwd]
2014-11-14 03:37:47 -05:00
curdir = nil
2014-11-15 00:15:49 -05:00
make_p.scan(%r{Entering\ directory\ ['`](.*)'|
^\#\ (GNU\ Make)\ |
^CURDIR\ :=\ (.*)|
^([/0-9a-zA-Z._-]+):(.*)\n((?:\#.*\n)*)|
^\#\ (Finished\ Make\ data\ base\ on)\ |
Leaving\ directory\ ['`](.*)'}x) {
2014-11-14 03:37:47 -05:00
directory_enter = $1
data_base_start = $2
data_base_curdir = $3
rule_target = $4
rule_sources = $5
2014-11-15 00:15:49 -05:00
rule_desc = $6
data_base_end = $7
directory_leave = $8
2014-11-14 03:37:47 -05:00
#p $~
if directory_enter
enter_dir = Pathname(directory_enter)
2013-04-11 08:03:23 -04:00
#p [:enter, enter_dir]
dirstack.push enter_dir
2014-11-14 03:37:47 -05:00
elsif data_base_start
curdir = nil
elsif data_base_curdir
curdir = Pathname(data_base_curdir)
2014-11-15 00:15:49 -05:00
elsif rule_target && rule_sources && rule_desc &&
/Modification time never checked/ !~ rule_desc # This pattern match eliminates rules which VPATH is not expanded.
2014-11-14 03:37:47 -05:00
target = rule_target
deps = rule_sources
deps = deps.scan(%r{[/0-9a-zA-Z._-]+})
2019-08-09 11:02:17 -04:00
deps.delete_if {|dep| /\.time\z/ =~ dep} # skip timestamp
2014-11-14 03:37:47 -05:00
next if /\.o\z/ !~ target.to_s
2022-05-11 06:26:43 -04:00
next if /\.bundle\// =~ target.to_s
2014-11-14 03:37:47 -05:00
next if /\A\./ =~ target.to_s # skip rules such as ".c.o"
#p [curdir, target, deps]
2014-11-15 00:15:49 -05:00
dir = curdir || dirstack.last
dependencies[dir + target] ||= []
dependencies[dir + target] |= deps.map {|dep| dir + dep }
2014-11-14 03:37:47 -05:00
elsif data_base_end
curdir = nil
elsif directory_leave
leave_dir = Pathname(directory_leave)
2013-04-11 08:03:23 -04:00
#p [:leave, leave_dir]
if leave_dir != dirstack.last
warn "unexpected leave_dir : #{dirstack.last.inspect} != #{leave_dir.inspect}"
2011-06-17 19:15:47 -04:00
end
2013-04-11 08:03:23 -04:00
dirstack.pop
end
2011-06-17 19:15:47 -04:00
}
2013-04-11 08:03:23 -04:00
dependencies
end
2011-06-17 19:15:47 -04:00
2013-04-11 08:03:23 -04:00
#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
2014-11-20 09:18:37 -05:00
def read_single_cc_deps(path_i, cwd)
2013-04-11 08:03:23 -04:00
files = {}
2016-07-06 01:37:13 -04:00
compiler_wd = nil
path_i.each_line {|line|
2013-04-11 08:03:23 -04:00
next if /\A\# \d+ "(.*)"/ !~ line
2016-07-06 01:37:13 -04:00
dep = $1
next if %r{\A<.*>\z} =~ dep # omit <command-line>, etc.
2019-02-12 07:31:01 -05:00
next if /\.erb\z/ =~ dep
2016-07-06 01:37:13 -04:00
compiler_wd ||= dep
files[dep] = true
2011-06-17 19:15:47 -04:00
}
2013-04-11 08:03:23 -04:00
# gcc emits {# 1 "/absolute/directory/of/the/source/file//"} at 2nd line.
2016-07-06 01:37:13 -04:00
if %r{\A/.*//\z} =~ compiler_wd
2013-04-11 08:03:23 -04:00
files.delete compiler_wd
compiler_wd = Pathname(compiler_wd.sub(%r{//\z}, ''))
2016-07-06 01:37:13 -04:00
elsif !(compiler_wd = yield)
2015-06-14 09:35:05 -04:00
raise "compiler working directory not found: #{path_i}"
2013-04-11 08:03:23 -04:00
end
deps = []
files.each_key {|dep|
dep = Pathname(dep)
if dep.relative?
dep = compiler_wd + dep
end
if !dep.file?
2014-11-20 09:18:37 -05:00
warn "warning: file not found: #{dep}"
2011-06-17 19:15:47 -04:00
next
end
2013-04-11 08:03:23 -04:00
next if !dep.to_s.start_with?(cwd.to_s) # omit system headers.
deps << dep
}
2019-11-18 09:14:13 -05:00
if deps.include?(cwd + "probes.h")
deps << (cwd + "probes.dmyh")
end
2013-04-11 08:03:23 -04:00
deps
end
2011-06-17 19:15:47 -04:00
2014-11-20 09:18:37 -05:00
def read_cc_deps(cwd)
2013-04-11 08:03:23 -04:00
deps = {}
Pathname.glob('**/*.o').sort.each {|fn_o|
fn_i = fn_o.sub_ext('.i')
2014-11-15 00:15:49 -05:00
if !fn_i.exist?
2018-12-03 08:12:34 -05:00
next if fn_o.sub_ext('.S').exist?
2014-11-20 09:18:37 -05:00
warn "warning: not found: #{fn_i}"
2014-11-16 04:22:43 -05:00
$i_not_found = true
2014-11-15 00:15:49 -05:00
next
end
2013-04-11 08:03:23 -04:00
path_o = cwd + fn_o
path_i = cwd + fn_i
2016-07-06 01:37:13 -04:00
deps[path_o] = read_single_cc_deps(path_i, cwd) do
if fn_o.to_s.start_with?("enc/")
cwd
else
path_i.parent
end
end
2013-04-11 08:03:23 -04:00
}
deps
end
def concentrate(dependencies, cwd)
deps = {}
dependencies.keys.sort.each {|target|
sources = dependencies[target]
target = target.relative_path_from(cwd)
2014-11-14 03:37:47 -05:00
sources = sources.map {|s|
rel = s.relative_path_from(cwd)
rel
}
2013-04-11 08:03:23 -04:00
if %r{\A\.\.(/|\z)} =~ target.to_s
2014-11-20 09:18:37 -05:00
warn "warning: out of tree target: #{target}"
2013-04-11 08:03:23 -04:00
next
end
sources = sources.reject {|s|
if %r{\A\.\.(/|\z)} =~ s.to_s
2014-11-20 09:18:37 -05:00
warn "warning: out of tree source: #{s}"
2013-04-11 08:03:23 -04:00
true
2016-07-06 01:37:14 -04:00
elsif %r{/\.time\z} =~ s.to_s
true
2013-04-11 08:03:23 -04:00
else
false
2011-06-17 19:15:47 -04:00
end
2013-04-11 08:03:23 -04:00
}
deps[target] = sources
}
deps
end
2014-11-15 00:15:49 -05:00
def sort_paths(paths)
paths.sort_by {|t|
2013-04-11 08:03:23 -04:00
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.
}
2014-11-15 00:15:49 -05:00
end
2014-11-20 09:18:37 -05:00
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}"
}
2013-04-11 08:03:23 -04:00
}
end
2011-06-17 19:15:47 -04:00
2014-11-20 09:18:37 -05:00
def detect_dependencies(out=$stdout)
2013-04-11 08:03:23 -04:00
cwd = Pathname.pwd
make_deps = read_make_deps(cwd)
2014-11-14 03:37:47 -05:00
#pp make_deps
2013-04-11 08:03:23 -04:00
make_deps = concentrate(make_deps, cwd)
#pp make_deps
2014-11-20 09:18:37 -05:00
cc_deps = read_cc_deps(cwd)
#pp cc_deps
cc_deps = concentrate(cc_deps, cwd)
#pp cc_deps
return make_deps, cc_deps
2014-11-15 00:15:49 -05:00
end
2014-11-20 09:18:37 -05:00
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
}
2014-11-15 00:15:49 -05:00
}
2014-11-20 09:18:37 -05:00
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
}
}
2014-11-15 06:30:59 -05:00
2014-11-20 09:18:37 -05:00
makefiles.keys.sort.each {|makefile|
cc_lines = cc_lines_hash[makefile] || Hash.new(false)
make_lines = make_lines_hash[makefile] || Hash.new(false)
2014-11-15 06:30:59 -05:00
content = begin
File.read(makefile)
2014-11-15 00:15:49 -05:00
rescue Errno::ENOENT
2014-11-15 06:30:59 -05:00
''
end
2014-11-20 09:18:37 -05:00
if /^#{Regexp.escape DEPENDENCIES_SECTION_START_MARK}
((?:.*\n)*)
#{Regexp.escape DEPENDENCIES_SECTION_END_MARK}/x =~ content
2014-11-15 06:30:59 -05:00
pre_post_part = [$`, $']
2014-11-20 09:18:37 -05:00
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
}
2014-11-15 06:30:59 -05:00
else
2014-11-20 09:18:37 -05:00
(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
}
2014-11-15 00:15:49 -05:00
end
2014-11-20 09:18:37 -05:00
}
end
2014-11-15 06:30:59 -05:00
2021-04-05 20:33:11 -04:00
def prepare_build
unless File.exist?("Makefile")
if File.exist?("autogen.sh")
system("./autogen.sh")
elsif !File.exist?("configure")
system("autoreconf", "-i", "-s")
end
system("./configure", "-q", "--enable-load-relative", "--prefix=/.",
"--disable-install-doc", "debugflags=-save-temps=obj -g")
end
end
2014-11-20 09:18:37 -05:00
def main_show(out=$stdout)
2021-04-05 20:33:11 -04:00
prepare_build
2014-11-20 09:18:37 -05:00
make_deps, cc_deps = detect_dependencies(out)
compare_deps(make_deps, cc_deps, out)
end
2014-11-15 06:30:59 -05:00
2014-11-20 09:18:37 -05:00
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}"
2014-11-15 00:15:49 -05:00
end
2014-11-20 09:18:37 -05:00
}
return adds, others
end
2014-11-15 06:30:59 -05:00
2014-11-20 09:18:37 -05:00
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
2014-11-15 06:30:59 -05:00
end
2014-11-20 09:18:37 -05:00
if content &&
/^#{Regexp.escape DEPENDENCIES_SECTION_START_MARK}
((?:.*\n)*)
#{Regexp.escape DEPENDENCIES_SECTION_END_MARK}/x =~ content
pre_dep_post = [$`, $1, $']
2014-11-15 06:30:59 -05:00
else
2014-11-20 09:18:37 -05:00
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
2014-11-15 06:30:59 -05:00
new_content = [
2014-11-20 09:18:37 -05:00
pre_lines,
2014-11-15 06:30:59 -05:00
DEPENDENCIES_SECTION_START_MARK,
2014-11-20 09:18:37 -05:00
*new_lines,
2014-11-15 06:30:59 -05:00
DEPENDENCIES_SECTION_END_MARK,
2014-11-20 09:18:37 -05:00
post_lines
2014-11-15 06:30:59 -05:00
].join
2014-11-20 09:18:37 -05:00
if content != new_content
puts "modified: #{makefile}"
tmp_makefile = "#{makefile}.new.#{$$}"
File.write(tmp_makefile, new_content)
File.rename tmp_makefile, makefile
2014-11-20 09:27:44 -05:00
(add_lines - dep_lines).each {|line| puts " added #{line}" }
2014-11-15 06:30:59 -05:00
else
2014-11-20 09:18:37 -05:00
puts "not modified: #{makefile}"
end
if others[makefile]
others[makefile].each {|line| puts " #{line}" }
end
else
if pre_dep_post
2015-04-19 23:45:35 -04:00
puts "no additional lines: #{makefile}"
2014-11-20 09:18:37 -05:00
elsif content
2014-11-15 06:30:59 -05:00
puts "no dependencies section: #{makefile}"
2014-11-20 09:18:37 -05:00
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}" }
2014-11-15 06:30:59 -05:00
end
2014-11-15 00:15:49 -05:00
end
}
end
def main_fix
problems = StringIO.new
main_show(problems)
2014-11-15 06:30:59 -05:00
main_actual_fix(problems.string)
2014-11-15 00:15:49 -05:00
end
def run
op = optionparser
op.parse!(ARGV)
if $opt_actual_fix
main_actual_fix(ARGF.read)
elsif $opt_fix
main_fix
else
main_show
end
2013-04-11 08:03:23 -04:00
end
2011-06-17 19:15:47 -04:00
2014-11-21 04:12:56 -05:00
init_global
2014-11-15 00:15:49 -05:00
run
2014-11-16 04:22:43 -05:00
if $i_not_found
2014-11-20 09:18:37 -05:00
warn "warning: missing *.i files, see help in #$0 and ensure ccache is disabled"
2014-11-16 04:22:43 -05:00
end