class MakeMakefile::Depend
Generates dependency rules for Ruby and C extension sources.
An extension project can update the autogenerated section of its depend file while running extconf.rb:
ruby extconf.rb --update-depend
The depend file must contain these markers:
# AUTOGENERATED DEPENDENCIES START # AUTOGENERATED DEPENDENCIES END
MakeMakefile::Depend scans each source and the headers it includes without invoking a compiler. When updating an extension project, files outside the project tree, such as system and installed Ruby headers, are omitted. Use --depend-root=DIR when the project root cannot be detected from a .git directory.
Attributes
Source tree root used to resolve dependency paths.
Public Class Methods
Source
# File lib/mkmf/depend.rb, line 246 def find_root(path) path = File.expand_path(path) root = path loop do return path if File.exist?(File.join(path, ".git")) parent = File.dirname(path) break if parent == path path = parent end root end
Finds the nearest ancestor of path containing .git.
Returns path itself when no repository root can be found.
# File lib/mkmf/depend.rb, line 233 def main(argv = ARGV, err: $stderr) options, inputs = parse_options(argv) rescue OptionParser::ParseError => error err.puts "#{File.basename($0)}: #{error.message}" err.puts "Try '#{File.basename($0)} --help' for more information." false else execute(inputs, **options) end
Runs the command-line interface with argv.
Returns whether the requested update or check succeeded.
Source
# File lib/mkmf/depend.rb, line 334 def initialize(root: DEFAULT_ROOT) @root = File.expand_path(root) @make_variables = {} @dependency_declarations = {} @dependency_targets = {} @dependency_contents = {} end
Creates a dependency updater for root.
Private Class Methods
# File lib/mkmf/depend.rb, line 328 def execute(inputs, root: DEFAULT_ROOT, **options) new(root: root).run(inputs, **options) end
Creates an updater and runs it with the parsed command-line options.
Source
# File lib/mkmf/depend.rb, line 261 def parse_options(argv) options = {} make_variables = {} output = nil select_mode = proc do |mode| options[:mode] = mode end parser = OptionParser.new do |opts| opts.banner = "Usage: #{File.basename($0)} [options] [VAR=value ...] [files]" opts.separator "" opts.separator "Input selection:" opts.on("--root=DIR", "source tree root") {|value| options[:root] = value} opts.on( "--thread-model=MODEL", String, "thread model (same as THREAD_MODEL=MODEL)", ) do |value| make_variables["THREAD_MODEL"] = value end opts.on( "--scope=SCOPE", [:all, :core, :extensions], "dependency files to process (all, core, extensions)", ) do |value| options[:scope] = value end opts.separator "" opts.separator "Update mode (last one wins; default: stdout):" opts.on("--output=DIR", "write dependencies under DIR") do |value| select_mode[:output] output = value end opts.on("--inplace", "update dependency files in place") do select_mode[:inplace] end opts.on("--check", "check dependency files without updating") do select_mode[:check] end opts.separator "" opts.separator "Output options:" opts.on("--nmake", "emit NMake dependency rules") do options[:nmake] = true end opts.on("--sources", "emit source mappings") do options[:sources] = true end opts.on("-v", "--verbose", "show scanned sources") do options[:verbose] = true end end inputs = [] rest = parser.order(argv) do |arg| if /\A([A-Za-z_]\w*)=(.*)\z/m =~ arg make_variables[$1] = $2 else inputs << arg end end inputs.concat(rest) options[:make_variables] = make_variables unless make_variables.empty? options[:output] = output if options[:mode] == :output [options, inputs] end
Parses command-line arguments into runner options and input paths.
Public Instance Methods
# File lib/mkmf/depend.rb, line 979 def run(inputs = ARGV, out: $stdout, err: $stderr, mode: :stdout, output: nil, make_variables: {}, nmake: false, sources: false, scope: nil, thread_model: nil, verbose: false) case mode when :output raise ArgumentError, "output directory is missing" unless output when :stdout, :inplace, :check raise ArgumentError, "output requires output mode" if output else raise ArgumentError, "unknown update mode: #{mode.inspect}" end unless thread_model.nil? if thread_model.empty? raise ArgumentError, "thread model must not be empty" end make_variables = make_variables.merge("THREAD_MODEL" => thread_model) end select_make_variables(make_variables) if scope inputs = dependency_files(scope).map {|file| File.join(@root, file)} end output = File.expand_path(output) if output changed = false inputs.each do |input| if input.end_with?(".c", ".y") out.puts makedepend(input) else deps = dependency_file_content(input) || File.read(input) dependency_declarations(input, content: deps) dependency_targets(input, deps) match = MARK_SECTION.match(deps) next unless match raise "missing #{MARK_END} in #{input}" unless match.begin(1) current_rules = match[0] expected = update_deps( current_rules, input, group: !nmake, verbose: verbose, ) expected = minimize_deps(expected, input) if sources updated = match.pre_match + expected + match.post_match if mode == :output updated = normalize_dependency_rules(updated) unless nmake replace_file(File.join(output, relative_source(input)), updated) elsif same_dependency_rules?(current_rules, expected) next elsif mode == :inplace replace_file(input, updated) changed = true elsif mode == :check report_outdated_dependencies(input, current_rules, expected, err: err) changed = true else out.puts updated changed = true end end end if mode == :check && changed options = " --sources" if sources err.puts "\nupdate with:" err.puts " ruby tool/mkdepend.rb --scope=all#{options} --inplace" end mode != :check || !changed end
Processes dependency inputs according to the selected update mode.
make_variables supplies values for Make variables in declarations. thread_model is a shortcut for its THREAD_MODEL entry.
Returns false only when check mode finds an outdated dependency file.
# File lib/mkmf/depend.rb, line 904 def update_extension(input, source_map, make_variables: {}, nmake: false, verbose: false) select_make_variables(make_variables) input = File.expand_path(input) deps = File.read(input) if File.file?(input) match = MARK_SECTION.match(deps) if deps unless match raise "#{input}: no autogenerated dependency section" end raise "missing #{MARK_END} in #{input}" unless match.begin(1) dependency_declarations(input, content: deps) dependency_targets(input, deps) generated = [] source_map.each do |target, source| source = relative_source(source) warn "dependencies for #{source}:" if verbose makedepend( source, generated, target: target, input: input, project: true ) end manual = match.pre_match + match.post_match generated = remove_manual_dependencies(generated.join, manual) expected = compact_dependencies(generated, group: !nmake) updated = match.pre_match + expected + match.post_match return false if same_dependency_rules?(match[0], expected) replace_file(input, updated) true end
Updates the marked dependency section in extension file input.
source_map maps object targets to source paths. make_variables supplies values of Make variables used by manual dependency rules. Returns true when the file changed and false when its dependencies were already current.
Private Instance Methods
# File lib/mkmf/depend.rb, line 730 def compact_dependencies(rules, group: true) return rules.lines.uniq.sort.join unless group grouped = Hash.new {|hash, dependency| hash[dependency] = []} lines = [] rules.each_line do |line| unless /\A(\S+):\s+(\S+)\s*\z/ =~ line lines << line next end target, dependency = $1, $2 if dependency.end_with?(".c", ".y") lines << "#{target}: #{dependency}\n" else grouped[dependency] << target end end grouped.each do |dependency, targets| targets.uniq.sort.each_slice(8) do |slice| lines << "#{slice.join(' ')}: #{dependency}\n" end end lines.uniq.sort.join end
Sorts and deduplicates rules, optionally grouping targets by dependency.
# File lib/mkmf/depend.rb, line 505 def declaration(map, name, input = nil) candidates = [name] if input dir = File.dirname(relative_source(input)) candidates << name.delete_prefix(dir + "/") unless dir == "." end key = candidates.uniq.find {|candidate| map.key?(candidate)} [key, map[key]] if key end
Finds the declaration for name, accounting for an input-relative name.
# File lib/mkmf/depend.rb, line 465 def dependency_declarations(input = nil, source: nil, content: nil) input ||= dependency_input(source) input = relative_source(input || "depend") @dependency_declarations[input] ||= begin if input == "depend" path = File.join(@root, input) parse_dependency_declarations(path, dependency_file_content(path, content)) else global = dependency_declarations(File.join(@root, "depend")) path = File.absolute_path?(input) ? input : File.join(@root, input) local = parse_dependency_declarations( path, dependency_file_content(path, content), ) Declarations.new( global.scan.merge(local.scan), global.dependencies.merge(local.dependencies), global.defines.merge(local.defines), global.undefines.merge(local.undefines), ) end end end
Returns merged global and file-local declarations for an input or source.
# File lib/mkmf/depend.rb, line 453 def dependency_file_content(path, content = nil) path = File.expand_path(path, @root) if content @dependency_contents[path] = content elsif @dependency_contents.key?(path) @dependency_contents[path] elsif File.file?(path) @dependency_contents[path] = File.read(path) end end
Reads and caches a dependency file, or caches supplied content.
# File lib/mkmf/depend.rb, line 797 def dependency_files(scope = :all) Dir.chdir(@root) do files = case scope when :all %w[depend enc/depend].concat(Dir.glob("ext/**/depend")) when :core %w[depend enc/depend] when :extensions Dir.glob("ext/**/depend") else raise ArgumentError, "unknown dependency scope: #{scope}" end files.select do |file| next false unless File.file?(file) content = File.read(file) next false unless content.include?(MARK_START) dependency_file_content(file, content) true end end end
Returns marked dependency files selected by scope.
Source
# File lib/mkmf/depend.rb, line 489 def dependency_input(source) return "depend" unless source source = relative_source(source) if source.start_with?("ext/") dir = File.dirname(source) until dir == "." || dir == "ext" return File.join(dir, "depend") if File.file?(File.join(@root, dir, "depend")) dir = File.dirname(dir) end elsif source.start_with?("enc/") return "enc/depend" end "depend" end
Returns the dependency file that governs source.
Source
# File lib/mkmf/depend.rb, line 756 def dependency_pairs(rules) rules = normalize_dependency_rules(rules) rules.each_line.each_with_object(Set.new) do |line, pairs| next unless /\A(\S+(?:\s+\S+)*):\s*(.*?)\s*\z/ =~ line targets = $1.split dependencies = expand_dependency_variables($2.split).map do |dependency| normalize_dependency_rules(dependency) end targets.product(dependencies) {|pair| pairs << pair} end end
Returns target and dependency pairs from Make rules.
# File lib/mkmf/depend.rb, line 672 def dependency_scanner(src, declarations, input) macros = {} macros["RUBY_EXTCONF_H"] = "extconf.h" if src.start_with?("ext/") defined = declaration(declarations.defines, src, input)&.last || [] undefined = %w[__cplusplus] if undefined_macros = declaration(declarations.undefines, src, input) undefined.concat(undefined_macros[1]) end Scanner.new( root: @root, include_dirs: [ @root, File.join(@root, "include"), File.join(@root, "prism"), File.dirname(src), ], macros: macros, targets: dependency_targets(input), aliases: declarations.scan, dependencies: declarations.dependencies, defined: defined, undefined: undefined, ) end
Builds a Scanner configured by the declarations governing src.
# File lib/mkmf/depend.rb, line 664 def dependency_source?(source, input = nil) declarations = dependency_declarations(input, source: source) declaration(declarations.scan, source, input) || declaration(declarations.dependencies, source, input) || dependency_target(source, input || dependency_input(source)) end
Returns whether source is declared as generated or otherwise resolvable.
# File lib/mkmf/depend.rb, line 841 def dependency_source_map(rules, input) dependency_source_rules(rules, input).each_with_object({}) do |(target, data), result| result[target] = data[1] end end
Returns an object-target to source-path mapping extracted from rules.
# File lib/mkmf/depend.rb, line 820 def dependency_source_rules(rules, input) input = relative_source(input) srcs = {} rules.each_line do |line| next unless %r[\A([-.\w/]+)\.(?:\$\(OBJEXT\)|o):\s+(.+?)\s*\z] =~ line target, dependency = $1, $2 next unless src = dependency[%r{[-.\w/]+\.[cy]\z}] src = src.delete_prefix("/") source_name = File.basename(src, ".*") target_name = File.basename(target) next unless target_name == source_name || target_name.end_with?("-#{source_name}") local = input.start_with?("ext/") && !dependency.include?("$(top_srcdir)") src = File.join(File.dirname(input), src) if local && !src.start_with?("ext/") rank = src.end_with?(".y") ? 2 : local ? 1 : 0 current = srcs[target] srcs[target] = [rank, src, line] if !current || rank >= current.first end srcs.sort.to_h end
Extracts the best source rule for each object target in rules.
# File lib/mkmf/depend.rb, line 848 def dependency_sources(rules, input) dependency_source_map(rules, input).values end
Returns the source paths identified by rules.
# File lib/mkmf/depend.rb, line 536 def dependency_target(name, input) input = relative_source(input) targets = dependency_targets(input) candidates = [name] dir = File.dirname(input) candidates << name.delete_prefix(dir + "/") unless dir == "." candidates.uniq.find {|candidate| targets.include?(candidate)} end
Finds the declared target matching name in input.
# File lib/mkmf/depend.rb, line 516 def dependency_targets(input, content = nil) input = relative_source(input) @dependency_targets[input] ||= begin targets = [] path = File.absolute_path?(input) ? input : File.join(@root, input) content = dependency_file_content(path, content) if content content.each_line do |line| if line.start_with?(MARK_START)..line.start_with?(MARK_END) next elsif target = line[/\A[^\s#][^:=]*?(?=\s*:(?!=))/] targets.concat(target.split) end end end targets.uniq end end
Returns generated targets declared outside the marked section of input.
# File lib/mkmf/depend.rb, line 657 def dependency_vpath(input, source) return if source.start_with?("enc/") && (!input || relative_source(input) == "enc/depend") "{$(VPATH)}" end
Returns the VPATH prefix used for dependencies of source.
# File lib/mkmf/depend.rb, line 593 def depends(files, vpath, source: nil, input: nil, declarations: nil, project: false) declaration_input = input || dependency_input(source) declarations ||= dependency_declarations(declaration_input, source: source) extension_dir = File.dirname(source) if source&.start_with?("ext/") project_dir = File.dirname(source) if project expand = lambda do |file, expanded| if !expanded.include?(file) && (dependencies = declaration(declarations.dependencies, file, declaration_input)) dependencies[1].flat_map {|dep| expand.call(dep, expanded | [file])} else file end end files = files.flat_map {|file| expand.call(file, [])} files.each_with_object([]) do |file, deps| file = relative_source(file) dep = if file.start_with?('$(', '{$(') file elsif target = dependency_target(file, declaration_input) target elsif project_dir project_dependency(file, project_dir) elsif extension_dir extension_dependency(file, extension_dir) else case file when %r[\Aenc/unicode/[\d.]+/] "$(UNICODE_HDR_DIR)/#$'" when 'encindex.h', 'transcode_data.h', /\Areg(?!ex\b)\w+\.h\z/ "#{vpath || '$(top_srcdir)/'}#{file}" when /\.e?rb\z/ when %r[\Atool/] "$(top_srcdir)/#{file}" when %r[\Ainclude/\Kruby(?:/ruby|/version)?\.h\z] "$(hdrdir)/#$&" when %r[\A(?:internal|prism)\/] "$(top_srcdir)/#{file}" when %r[\Accan/] "$(CCAN_DIR)/#$'" when %r[\Aenc/] "#{vpath}#{file}" when %r[\Ainclude/ruby/], %r[\Amissing/] "#{vpath}#$'" else "#{vpath}#{file}" end end deps << dep if dep end.uniq.sort end
Converts scanned files into sorted Make dependencies for source.
# File lib/mkmf/depend.rb, line 374 def expand_dependency_variables(dependencies) dependencies.flat_map do |dependency| if /\A\$\((\w+)\)\z/ =~ dependency && (values = @make_variables[$1]) values.flat_map(&:split) else expand_make_variables(dependency) end end end
Expands known Make variables that stand for dependency lists.
# File lib/mkmf/depend.rb, line 356 def expand_make_variables(value, defaults: {}) value.gsub(/\$\((\w+)\)/) do |variable| name = $1 values = if @make_variables.key?(name) @make_variables[name] elsif defaults.key?(name) Array(defaults[name]) else next variable end unless values.one? raise ArgumentError, "#{name} is not a scalar Make variable" end values.first.to_s end end
Expands known Make variables embedded in value.
# File lib/mkmf/depend.rb, line 388 def expand_scan_declaration(name, source) source_parts = source.split(/(\*\*|\*)/) captures = source_parts.count {|part| part == "*" || part == "**"} return unless captures == name.scan(/\*\*|\*/).size return {name => source} if captures.zero? pattern = source_parts.map do |part| case part when "**" then "(.*)" when "*" then "([^/]*)" else Regexp.escape(part) end end.join pattern = /\A#{pattern}\z/ Dir.glob(File.join(@root, source)).sort.each_with_object({}) do |path, map| path = relative_source(path) match = pattern.match(path) or next values = match.captures generated = name.gsub(/\*\*|\*/) {values.shift} map[generated] = path end end
Expands matching wildcards in a scan declaration.
Returns a generated-name to source-path mapping.
# File lib/mkmf/depend.rb, line 554 def extension_dependency(file, source_dir) case file when %r[\A(?:#{Regexp.escape(source_dir)}/)?extconf\.h\z] "$(RUBY_EXTCONF_H)" when 'config.h' "$(arch_hdrdir)/ruby/config.h" when %r[\Ainclude/] "$(hdrdir)/#$'" when 'probes.h' else if file.start_with?(prefix = source_dir + "/") file.delete_prefix(prefix) elsif file.start_with?(prefix = File.dirname(source_dir) + "/") "$(srcdir)/../#{file.delete_prefix(prefix)}" elsif !file.include?("/") && File.file?(File.join(@root, source_dir, file)) file elsif /\.e?rb\z/.match?(file) nil else "$(top_srcdir)/#{file}" end end end
Converts an extension dependency to the Make variable path it requires.
# File lib/mkmf/depend.rb, line 698 def makedepend(src, out = [], target: nil, input: nil, project: false) src = relative_source(src) declaration_input = input || dependency_input(src) declarations = dependency_declarations(declaration_input, source: src) vpath = dependency_vpath(input, src) scanner = dependency_scanner(src, declarations, declaration_input) scan_source = if mapping = declaration(declarations.scan, src, declaration_input) mapping[1] else src end files = scanner.scan(scan_source) if dependencies = declaration(declarations.dependencies, src, declaration_input) files.concat(dependencies[1]) end files << src if scan_source != src files << src.sub(/\.y\z/, ".c") if src.end_with?(".y") obj = if target src.start_with?("ext/") ? "#{target}.o" : "#{target}.$(OBJEXT)" else object_name(src) end depends(files, vpath, source: src, input: input, declarations: declarations, project: project).each do |dep| out << "#{obj}: #{dep}\n" end out end
Appends Make dependency rules for src to out and returns out.
Source
# File lib/mkmf/depend.rb, line 864 def minimize_deps(rules, input) sources = dependency_source_rules(rules, input) targets = rules.scan(%r[^([-\.\w/]+)\.(?:\$\(OBJEXT\)|o):]).flatten.uniq unless (missing = targets - sources.keys).empty? raise "no source files for #{missing.join(', ')} in #{input}" end sources.each_value {|_, src| resolve_dependency_source(src, input)} minimal = sources.values.map(&:last) if relative_source(input).start_with?("ext/") rules.each_line do |line| minimal << line if /\A.*:\s+\$\(srcdir\)\//.match?(line) end end minimal.uniq.sort.join end
Reduces rules to source mappings needed to regenerate dependencies.
# File lib/mkmf/depend.rb, line 780 def normalize_dependency_rules(rules) rules.gsub(/\{(?:\.;)?\$\(VPATH\)\}/, '') end
Removes VPATH markers that are unnecessary in build-directory output.
Source
# File lib/mkmf/depend.rb, line 647 def object_name(src) stem = src.sub(/\.[cy]\z/, "") if src.start_with?("ext/") "#{File.basename(stem)}.o" else "#{stem.delete_prefix('missing/')}.$(OBJEXT)" end end
Returns the Make object name for source path src.
# File lib/mkmf/depend.rb, line 412 def parse_dependency_declarations(path, content = nil) declarations = Declarations.new({}, {}, {}, {}) content ||= File.read(path) if File.file?(path) return declarations unless content lineno = 0 content.each_line do |line| lineno += 1 case line[/\A#\s*mkdepend:\s*\K.*/] when nil next when /\Ascan\s+(\S+)\s*=>\s*(\S+)\s*\z/ scan = expand_scan_declaration($1, $2) unless scan && (!scan.empty? || !$2.include?("*")) raise "#{path}:#{lineno}: invalid mkdepend declaration: #{line.strip}" end declarations.scan.update(scan) when /\Adepends\s+(\S+)\s*=>\s*(.+?)\s*\z/ name, dependencies = $1, $2.split if declarations.dependencies.key?(name) raise "#{path}:#{lineno}: duplicate mkdepend declaration: #{name}" end declarations.dependencies[name] = dependencies when /\Adefine\s+(\S+)\s*=>\s*(.+?)\s*\z/ declarations.defines[$1] = $2.split when /\Aundef\s+(\S+)\s*=>\s*(.+?)\s*\z/ declarations.undefines[$1] = $2.split else raise "#{path}:#{lineno}: invalid mkdepend declaration: #{line.strip}" end end declarations.scan.transform_values! do |source| expand_make_variables(source, defaults: {"THREAD_MODEL" => "pthread"}) end declarations.dependencies.transform_values! do |dependencies| expand_dependency_variables(dependencies) end declarations end
Parses mkdepend declarations from path or the supplied content.
# File lib/mkmf/depend.rb, line 579 def project_dependency(file, source_dir) source_dir = File.join(@root, source_dir) path = File.expand_path(file, @root) prefix = @root + File::SEPARATOR return unless path.start_with?(prefix) dependency = Pathname.new(path).relative_path_from( Pathname.new(source_dir) ).to_s.tr(File::SEPARATOR, "/") return dependency unless dependency.start_with?("../") "$(srcdir)/#{dependency}" end
Converts a standalone project’s file to a dependency relative to its source.
Source
# File lib/mkmf/depend.rb, line 546 def relative_source(path) expanded = File.expand_path(path) expanded = File.expand_path(path, @root) unless File.exist?(expanded) prefix = @root + File::SEPARATOR expanded.start_with?(prefix) ? expanded.delete_prefix(prefix) : path end
Makes path relative to root when it refers to a source-tree file.
# File lib/mkmf/depend.rb, line 770 def remove_manual_dependencies(generated, manual_rules) manual = dependency_pairs(manual_rules) generated.each_line.reject do |line| normalized = normalize_dependency_rules(line) /\A(\S+):\s+(\S+)\s*\z/ =~ normalized && manual.include?([$1, $2]) end.join end
Removes generated dependencies already covered by manual_rules.
Source
# File lib/mkmf/depend.rb, line 938 def replace_file(path, content) dir = File.dirname(path) FileUtils.mkdir_p(dir) mode = File.exist?(path) ? File.stat(path).mode & 0777 : 0644 Tempfile.create([File.basename(path), ".tmp"], dir) do |file| file.binmode file.write(content) file.flush file.chmod(mode) file.close File.rename(file.path, path) end end
Atomically replaces path with content, preserving its file mode.
# File lib/mkmf/depend.rb, line 953 def report_outdated_dependencies(input, current, expected, err: $stderr) current_lines = current.lines expected_lines = expected.lines label = relative_source(input) err.puts "#{label}: dependencies are outdated" if current_lines.size <= 20 err.puts " current dependency rules:" current_lines.each {|line| err.puts " #{line.chomp}"} else current_word = current_lines.one? ? "rule" : "rules" expected_word = expected_lines.one? ? "source rule" : "source rules" err.puts " current section has #{current_lines.size} #{current_word}; " \ "expected #{expected_lines.size} #{expected_word}" end err.puts " expected source rules:" expected_lines.each {|line| err.puts " #{line.chomp}"} end
Reports how dependency rules in input differ from expected.
# File lib/mkmf/depend.rb, line 853 def resolve_dependency_source(src, input) return src if File.file?(File.join(@root, src)) missing = "missing/#{src}" return missing if File.file?(File.join(@root, missing)) return src if dependency_source?(src, input) raise "source file not found: #{src} in #{input}" end
Resolves src to an existing, missing-library, or declared source path.
# File lib/mkmf/depend.rb, line 785 def same_dependency_rules?(left, right) normalize_dependency_rules(left) == normalize_dependency_rules(right) end
Returns whether two rule sets differ only by removable VPATH markers.
# File lib/mkmf/depend.rb, line 345 def select_make_variables(make_variables) make_variables = make_variables.to_h do |name, value| [name.to_s, Array(value).map(&:to_s)] end return if @make_variables == make_variables @make_variables = make_variables @dependency_declarations.clear end
Selects known Make variable values used during dependency generation.
# File lib/mkmf/depend.rb, line 881 def update_deps(rules, input, group: true, verbose: false) sources = dependency_source_map(rules, input) targets = rules.scan(%r[^([-.\w/]+)\.(?:\$\(OBJEXT\)|o):]).flatten.uniq unless (missing = targets - sources.keys).empty? raise "no source files for #{missing.join(', ')} in #{input}" end generated = [] sources.each do |target, src| src = resolve_dependency_source(src, input) warn "dependencies for #{src}:" if verbose makedepend(src, generated, target: target, input: input) end compact_dependencies(generated.join, group: group) end
Regenerates dependency rules for the sources identified in input.