mirror of
https://github.com/ruby/ruby.git
synced 2026-10-29 00:46:26 +08:00
A relative name was expanded against the current directory first, so a build directory nested inside the source tree made generated files such as ext/ripper/ripper.y look like source-tree files, and the extension's own depend file was no longer found. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1287 lines
41 KiB
Ruby
1287 lines
41 KiB
Ruby
require 'test/unit'
|
|
require 'tmpdir'
|
|
require 'stringio'
|
|
require_relative '../../lib/mkmf/depend'
|
|
|
|
TOP_SRCDIR = File.expand_path("../..", __dir__)
|
|
|
|
TestDepend = Class.new(MakeMakefile::Depend) do
|
|
public(*MakeMakefile::Depend.private_instance_methods(false))
|
|
|
|
class << self
|
|
public :parse_options
|
|
end
|
|
end
|
|
|
|
class TestMkdepend < Test::Unit::TestCase
|
|
MARK_START = MakeMakefile::Depend.const_get(:MARK_START, false)
|
|
MARK_END = MakeMakefile::Depend.const_get(:MARK_END, false)
|
|
Scanner = MakeMakefile::Depend.const_get(:Scanner, false)
|
|
|
|
def mkdepend
|
|
@mkdepend ||= TestDepend.new
|
|
end
|
|
|
|
def test_parse_options
|
|
options, inputs = TestDepend.parse_options(%w[
|
|
--root=src THREAD_MODEL=pthread --thread-model=win32 --output=.deps
|
|
--scope=extensions --nmake --sources --verbose depend
|
|
])
|
|
|
|
assert_equal(
|
|
{
|
|
root: "src",
|
|
make_variables: {"THREAD_MODEL" => "win32"},
|
|
output: ".deps",
|
|
mode: :output,
|
|
scope: :extensions,
|
|
nmake: true,
|
|
sources: true,
|
|
verbose: true,
|
|
},
|
|
options,
|
|
)
|
|
assert_equal(["depend"], inputs)
|
|
end
|
|
|
|
def test_parse_options_accepts_make_variable_assignments
|
|
options, inputs = TestDepend.parse_options(
|
|
["LOCAL_HDRS=", "--thread-model=win32", "THREAD_MODEL=pthread", "depend"],
|
|
)
|
|
|
|
assert_equal(
|
|
{
|
|
make_variables: {
|
|
"LOCAL_HDRS" => "",
|
|
"THREAD_MODEL" => "pthread",
|
|
},
|
|
},
|
|
options,
|
|
)
|
|
assert_equal(["depend"], inputs)
|
|
end
|
|
|
|
def test_parse_options_rejects_empty_thread_model
|
|
assert_raise(OptionParser::InvalidArgument) do
|
|
TestDepend.parse_options(["--thread-model="])
|
|
end
|
|
end
|
|
|
|
def test_parse_options_rejects_unknown_options
|
|
assert_raise(OptionParser::InvalidOption) do
|
|
TestDepend.parse_options(["--unknown"])
|
|
end
|
|
end
|
|
|
|
def test_parse_options_rejects_unknown_scope
|
|
assert_raise(OptionParser::InvalidArgument) do
|
|
TestDepend.parse_options(["--scope=unknown"])
|
|
end
|
|
end
|
|
|
|
def test_parse_options_selects_update_mode
|
|
assert_equal(
|
|
{mode: :inplace},
|
|
TestDepend.parse_options(["--inplace"]).first,
|
|
)
|
|
assert_equal(
|
|
{mode: :check},
|
|
TestDepend.parse_options(["--check"]).first,
|
|
)
|
|
end
|
|
|
|
def test_parse_options_uses_last_update_mode
|
|
options, = TestDepend.parse_options(["--output=.deps", "--check"])
|
|
assert_equal({mode: :check}, options)
|
|
|
|
options, = TestDepend.parse_options(["--inplace", "--output=.deps"])
|
|
assert_equal({mode: :output, output: ".deps"}, options)
|
|
end
|
|
|
|
def test_help_explains_update_mode_precedence
|
|
stdout, $stdout = $stdout, StringIO.new
|
|
exit_status = assert_raise(SystemExit) do
|
|
TestDepend.parse_options(["--help"])
|
|
end
|
|
assert_true(exit_status.success?)
|
|
help = $stdout.string
|
|
|
|
assert_include(help, "[VAR=value ...]")
|
|
assert_include(help, "same as THREAD_MODEL=MODEL")
|
|
assert_include(
|
|
help,
|
|
"Update mode (last one wins; default: stdout):",
|
|
)
|
|
output = help.index("--output=DIR")
|
|
inplace = help.index("--inplace")
|
|
check = help.index("--check")
|
|
assert_operator(output, :<, inplace)
|
|
assert_operator(inplace, :<, check)
|
|
ensure
|
|
$stdout = stdout
|
|
end
|
|
|
|
def test_scanner_follows_all_possible_project_headers
|
|
Dir.mktmpdir('mkdepend-scanner') do |dir|
|
|
File.write(File.join(dir, 'main.c'), <<~C)
|
|
#include "local.h"
|
|
#if FEATURE
|
|
# include "left.h"
|
|
#else
|
|
# include "right.h"
|
|
#endif
|
|
#if 0 && FEATURE
|
|
# include "never.h"
|
|
#endif
|
|
#include GENERATED_HEADER
|
|
#include "generated.rbinc"
|
|
#include "aliased.h"
|
|
#include "path/parse.h"
|
|
#ifdef DISABLED
|
|
# include "never_disabled.h"
|
|
#endif
|
|
#ifndef ENABLED
|
|
# include "never_enabled.h"
|
|
#endif
|
|
#include <external.h>
|
|
C
|
|
File.binwrite(File.join(dir, 'local.h'),
|
|
"/* \xff */\n#include \"nested.h\"\n".b)
|
|
%w[left.h right.h never.h never_disabled.h never_enabled.h nested.h].each do |header|
|
|
File.write(File.join(dir, header), "")
|
|
end
|
|
File.write(File.join(dir, 'aliased.h.erb'), "#include \"nested.h\"\n")
|
|
Dir.mkdir(File.join(dir, 'path'))
|
|
File.write(File.join(dir, 'path/parse.h'), "#include \"../path_nested.h\"\n")
|
|
File.write(File.join(dir, 'path_nested.h'), "")
|
|
|
|
scanner = Scanner.new(
|
|
root: dir,
|
|
include_dirs: [dir],
|
|
macros: {'GENERATED_HEADER' => 'generated.h'},
|
|
targets: %w[parse.h],
|
|
aliases: {'aliased.h' => 'aliased.h.erb'},
|
|
dependencies: {'generated.h' => %w[virtual.h]},
|
|
defined: %w[ENABLED],
|
|
undefined: %w[DISABLED],
|
|
)
|
|
|
|
assert_equal(
|
|
%w[aliased.h generated.rbinc left.h local.h main.c nested.h path/parse.h path_nested.h right.h virtual.h],
|
|
scanner.scan('main.c'),
|
|
)
|
|
end
|
|
end
|
|
|
|
def test_scanner_prefers_quoted_path
|
|
Dir.mktmpdir('mkdepend-scanner') do |dir|
|
|
root = File.join(dir, 'src')
|
|
source_dir = File.join(root, 'ext/-test-/float')
|
|
missing_dir = File.join(root, 'missing')
|
|
outer_missing_dir = File.join(dir, 'missing')
|
|
[source_dir, missing_dir, outer_missing_dir].each do |path|
|
|
FileUtils.mkdir_p(path)
|
|
end
|
|
File.write(
|
|
File.join(source_dir, 'nextafter.c'),
|
|
"#include \"../../../missing/nextafter.c\"\n",
|
|
)
|
|
File.write(
|
|
File.join(missing_dir, 'nextafter.c'),
|
|
"#include \"right.h\"\n",
|
|
)
|
|
File.write(File.join(missing_dir, 'right.h'), '')
|
|
File.write(
|
|
File.join(outer_missing_dir, 'nextafter.c'),
|
|
"#include \"wrong.h\"\n",
|
|
)
|
|
File.write(File.join(outer_missing_dir, 'wrong.h'), '')
|
|
|
|
scanner = Scanner.new(
|
|
root: root,
|
|
include_dirs: [root],
|
|
)
|
|
|
|
assert_equal(
|
|
%w[ext/-test-/float/nextafter.c missing/nextafter.c missing/right.h],
|
|
scanner.scan('ext/-test-/float/nextafter.c'),
|
|
)
|
|
end
|
|
end
|
|
|
|
def test_scanners_share_source_file_cache
|
|
Dir.mktmpdir('mkdepend-scanner') do |dir|
|
|
%w[first.c second.c].each do |source|
|
|
File.write(File.join(dir, source), "#include \"common.h\"\n")
|
|
end
|
|
header = File.join(dir, 'common.h')
|
|
File.write(header, "#include \"nested.h\"\n")
|
|
File.write(File.join(dir, 'nested.h'), "")
|
|
|
|
cache = {}
|
|
opened = Hash.new(0)
|
|
original_open = File.method(:open)
|
|
File.define_singleton_method(:open) do |path, *args, **kwargs, &block|
|
|
opened[File.expand_path(path)] += 1
|
|
original_open.call(path, *args, **kwargs, &block)
|
|
end
|
|
|
|
%w[first.c second.c].each do |source|
|
|
scanner = Scanner.new(root: dir, include_dirs: [dir], cache: cache)
|
|
assert_include(scanner.scan(source), 'common.h')
|
|
end
|
|
assert_equal(1, opened[header])
|
|
ensure
|
|
File.define_singleton_method(:open, original_open) if original_open
|
|
end
|
|
end
|
|
|
|
def test_scanner_joins_continued_directives
|
|
Dir.mktmpdir('mkdepend-scanner') do |dir|
|
|
File.write(File.join(dir, 'main.c'), <<~'C')
|
|
#include \
|
|
"continued.h"
|
|
C
|
|
File.write(File.join(dir, 'continued.h'), "")
|
|
|
|
scanner = Scanner.new(root: dir, include_dirs: [dir])
|
|
assert_include(scanner.scan('main.c'), 'continued.h')
|
|
end
|
|
end
|
|
|
|
def test_scanner_ignores_template_include
|
|
Dir.mktmpdir('mkdepend-scanner') do |dir|
|
|
File.write(File.join(dir, 'main.c'), "#include <<%= header %>>\n")
|
|
|
|
scanner = Scanner.new(root: dir, include_dirs: [dir])
|
|
|
|
assert_equal(%w[main.c], scanner.scan('main.c'))
|
|
assert_empty(scanner.unresolved)
|
|
end
|
|
end
|
|
|
|
def test_project_internal_headers_precede_public_headers
|
|
Dir.mktmpdir('mkdepend-scanner') do |root|
|
|
%w[ext/example internal include/ruby/internal].each do |dir|
|
|
FileUtils.mkdir_p(File.join(root, dir))
|
|
end
|
|
File.write(
|
|
File.join(root, 'ext/example/example.c'),
|
|
"#include \"internal/gc.h\"\n",
|
|
)
|
|
File.write(
|
|
File.join(root, 'internal/gc.h'),
|
|
"#include \"project.h\"\n",
|
|
)
|
|
File.write(File.join(root, 'internal/project.h'), '')
|
|
File.write(
|
|
File.join(root, 'include/ruby/internal/gc.h'),
|
|
"#include \"public.h\"\n",
|
|
)
|
|
File.write(File.join(root, 'include/ruby/internal/public.h'), '')
|
|
File.write(File.join(root, 'depend'), "#{MARK_START}\n#{MARK_END}\n")
|
|
File.write(
|
|
File.join(root, 'ext/example/depend'),
|
|
"#{MARK_START}\nexample.o: example.c\n#{MARK_END}\n",
|
|
)
|
|
|
|
rules = TestDepend.new(root: root).makedepend('ext/example/example.c').join
|
|
assert_include(rules, 'example.o: $(top_srcdir)/internal/gc.h')
|
|
assert_include(rules, 'example.o: $(top_srcdir)/internal/project.h')
|
|
assert_not_include(rules, 'public.h')
|
|
end
|
|
end
|
|
|
|
def test_virtual_dependencies_are_declared
|
|
declarations = mkdepend.dependency_declarations('depend')
|
|
assert_equal(
|
|
%w[include/ruby.h include/ruby/version.h config.h],
|
|
declarations.dependencies['config.h'],
|
|
)
|
|
assert_equal(
|
|
declarations.dependencies['config.h'],
|
|
declarations.dependencies['ruby/config.h'],
|
|
)
|
|
assert_equal(%w[probes.h probes.dmyh], declarations.dependencies['probes.h'])
|
|
assert_equal(
|
|
'prism/templates/src/node.c.erb',
|
|
declarations.scan['prism/node.c'],
|
|
)
|
|
assert_equal(
|
|
'prism/templates/include/prism/ast.h.erb',
|
|
declarations.scan['prism/ast.h'],
|
|
)
|
|
assert_equal(
|
|
'prism/templates/include/prism/internal/diagnostic.h.erb',
|
|
declarations.scan['prism/internal/diagnostic.h'],
|
|
)
|
|
assert_equal('thread_pthread.h', declarations.scan['THREAD_IMPL_H'])
|
|
assert_equal('thread_pthread.c', declarations.scan['THREAD_IMPL_SRC'])
|
|
assert_equal(
|
|
%w[thread_$(THREAD_MODEL).h],
|
|
declarations.dependencies['THREAD_IMPL_H'],
|
|
)
|
|
assert_equal(
|
|
%w[thread_$(THREAD_MODEL).c],
|
|
declarations.dependencies['THREAD_IMPL_SRC'],
|
|
)
|
|
assert_equal(
|
|
%w[{$(VPATH)}$(COROUTINE_H)],
|
|
declarations.dependencies['COROUTINE_H'],
|
|
)
|
|
assert_equal(%w[RIPPER], declarations.undefines['parse.y'])
|
|
ripper = mkdepend.dependency_declarations('ext/ripper/depend')
|
|
assert_equal(%w[RIPPER], ripper.defines['ripper.y'])
|
|
assert_equal(
|
|
%w[eventids1.h {$(VPATH)}eventids1.c],
|
|
ripper.dependencies['eventids1.c'],
|
|
)
|
|
assert_include(
|
|
mkdepend.dependency_targets('ext/socket/depend'),
|
|
'constdefs.h',
|
|
)
|
|
assert_include(
|
|
mkdepend.dependency_targets('ext/ripper/depend'),
|
|
'eventids1.h',
|
|
)
|
|
assert_path_not_exist(File.expand_path('../mkdepend', __dir__))
|
|
end
|
|
|
|
def test_scan_patterns_are_expanded_from_selected_root
|
|
Dir.mktmpdir('mkdepend-prism') do |dir|
|
|
FileUtils.mkdir_p(File.join(dir, 'prism/templates/src'))
|
|
FileUtils.mkdir_p(File.join(dir, 'prism/templates/ext/prism'))
|
|
FileUtils.mkdir_p(File.join(dir, 'prism/templates/include/prism/internal'))
|
|
FileUtils.mkdir_p(File.join(dir, 'enc/trans'))
|
|
File.write(File.join(dir, 'prism/templates/src/node.c.erb'), '')
|
|
File.write(File.join(dir, 'prism/templates/ext/prism/api.c.erb'), '')
|
|
File.write(File.join(dir, 'prism/templates/include/prism/ast.h.erb'), '')
|
|
File.write(
|
|
File.join(dir, 'prism/templates/include/prism/internal/node.h.erb'),
|
|
'',
|
|
)
|
|
File.write(File.join(dir, 'enc/trans/example.trans'), '')
|
|
File.write(File.join(dir, 'depend'), <<~DEPEND)
|
|
# mkdepend: scan prism/*.c => prism/templates/src/*.c.erb
|
|
# mkdepend: scan prism/*.c => prism/templates/ext/prism/*.c.erb
|
|
# mkdepend: scan **/*.h => prism/templates/include/**/*.h.erb
|
|
# mkdepend: scan enc/trans/*.c => enc/trans/*.trans
|
|
DEPEND
|
|
|
|
mkdepend = TestDepend.new(root: dir)
|
|
assert_equal(
|
|
{
|
|
'enc/trans/example.c' => 'enc/trans/example.trans',
|
|
'prism/api.c' => 'prism/templates/ext/prism/api.c.erb',
|
|
'prism/ast.h' => 'prism/templates/include/prism/ast.h.erb',
|
|
'prism/internal/node.h' =>
|
|
'prism/templates/include/prism/internal/node.h.erb',
|
|
'prism/node.c' => 'prism/templates/src/node.c.erb',
|
|
},
|
|
mkdepend.dependency_declarations('depend').scan,
|
|
)
|
|
end
|
|
end
|
|
|
|
def test_dependency_declarations_control_scanning_and_output
|
|
Dir.mktmpdir('mkdepend-declarations') do |dir|
|
|
FileUtils.mkdir_p(File.join(dir, 'ext/example'))
|
|
File.write(File.join(dir, 'depend'), <<~DEPEND)
|
|
# mkdepend: scan aliased.h => alias.h.tmpl
|
|
# mkdepend: depends virtual.h => first.h second.h
|
|
#{MARK_START}
|
|
#{MARK_END}
|
|
DEPEND
|
|
File.write(File.join(dir, 'alias.h.tmpl'), "#include \"nested.h\"\n")
|
|
File.write(File.join(dir, 'nested.h'), '')
|
|
File.write(File.join(dir, 'template.c'), <<~C)
|
|
#include "aliased.h"
|
|
#include "generated.h"
|
|
#include "virtual.h"
|
|
#include "vpath.inc"
|
|
#include "selected.inc"
|
|
C
|
|
input = File.join(dir, 'ext/example/depend')
|
|
File.write(input, <<~DEPEND)
|
|
# mkdepend: scan generated.c => template.c
|
|
# mkdepend: depends vpath.inc => {$(VPATH)}vpath.inc
|
|
# mkdepend: depends selected.inc => $(SELECTED_HEADER)
|
|
generated.h: template.c
|
|
#{MARK_START}
|
|
generated.o: generated.c
|
|
#{MARK_END}
|
|
DEPEND
|
|
|
|
output = File.join(dir, '.deps')
|
|
mkdepend = TestDepend.new(root: dir)
|
|
assert_true(
|
|
mkdepend.run([input], mode: :output, output: output, nmake: true),
|
|
)
|
|
generated = File.read(File.join(output, 'ext/example/depend'))
|
|
assert_include(generated, 'generated.o: generated.c')
|
|
assert_include(generated, 'generated.o: generated.h')
|
|
assert_include(generated, 'generated.o: {$(VPATH)}vpath.inc')
|
|
assert_include(generated, 'generated.o: $(SELECTED_HEADER)')
|
|
assert_include(generated, 'generated.o: $(top_srcdir)/first.h')
|
|
assert_include(generated, 'generated.o: $(top_srcdir)/second.h')
|
|
assert_include(generated, 'generated.o: $(top_srcdir)/nested.h')
|
|
end
|
|
end
|
|
|
|
def test_invalid_dependency_declaration
|
|
Dir.mktmpdir('mkdepend-declarations') do |dir|
|
|
path = File.join(dir, 'depend')
|
|
[
|
|
'# mkdepend: depends ignored.h =>',
|
|
'# mkdepend: generated generated.h',
|
|
'# mkdepend: generated generated.h => $(GENERATED_HEADER)',
|
|
'# mkdepend: unknown header.h',
|
|
'# mkdepend: scan generated/*.c => template.c.erb',
|
|
'# mkdepend: scan generated/*.c => missing/*.c.erb',
|
|
].each do |declaration|
|
|
File.write(path, "manual: rule\n#{declaration}\n")
|
|
error = assert_raise(RuntimeError) do
|
|
TestDepend.new(root: dir).parse_dependency_declarations(path)
|
|
end
|
|
assert_equal(
|
|
"#{path}:2: invalid mkdepend declaration: #{declaration}",
|
|
error.message,
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_duplicate_dependency_declaration
|
|
Dir.mktmpdir('mkdepend-declarations') do |dir|
|
|
path = File.join(dir, 'depend')
|
|
File.write(path, <<~DEPEND)
|
|
# mkdepend: depends generated.c => generated.h
|
|
# mkdepend: depends generated.c => generated.inc
|
|
DEPEND
|
|
error = assert_raise(RuntimeError) do
|
|
TestDepend.new(root: dir).parse_dependency_declarations(path)
|
|
end
|
|
assert_equal(
|
|
"#{path}:2: duplicate mkdepend declaration: generated.c",
|
|
error.message,
|
|
)
|
|
end
|
|
end
|
|
|
|
def test_dependency_metadata_uses_passed_content
|
|
Dir.mktmpdir('mkdepend-declarations') do |dir|
|
|
path = File.join(dir, 'missing-depend')
|
|
content = <<~DEPEND
|
|
# mkdepend: depends generated.h => $(GENERATED_HEADER)
|
|
generated.h: template.h
|
|
#{MARK_START}
|
|
generated.o: generated.c
|
|
#{MARK_END}
|
|
DEPEND
|
|
declarations = TestDepend.new(root: dir).
|
|
parse_dependency_declarations(path, content)
|
|
assert_equal(%w[$(GENERATED_HEADER)], declarations.dependencies['generated.h'])
|
|
assert_equal(
|
|
%w[generated.h],
|
|
TestDepend.new(root: dir).dependency_targets(path, content),
|
|
)
|
|
end
|
|
end
|
|
|
|
def test_dependency_files
|
|
files = mkdepend.dependency_files
|
|
assert_include(files, 'depend')
|
|
assert_include(files, 'enc/depend')
|
|
assert_include(files, 'ext/date/depend')
|
|
assert(files.all? {|file| File.read(File.join(TOP_SRCDIR, file)).include?(MARK_START)})
|
|
end
|
|
|
|
def test_dependency_files_caches_contents
|
|
Dir.mktmpdir('mkdepend-files') do |dir|
|
|
path = File.join(dir, 'depend')
|
|
ignored = File.join(dir, 'ext/example/depend')
|
|
FileUtils.mkdir_p(File.dirname(ignored))
|
|
content = "#{MARK_START}\n#{MARK_END}\n"
|
|
File.write(path, content)
|
|
File.write(ignored, "manual: rule\n")
|
|
mkdepend = TestDepend.new(root: dir)
|
|
|
|
assert_equal(%w[depend], mkdepend.dependency_files)
|
|
File.unlink(path)
|
|
File.unlink(ignored)
|
|
assert_equal(content, mkdepend.dependency_file_content(path))
|
|
assert_nil(mkdepend.dependency_file_content(ignored))
|
|
end
|
|
end
|
|
|
|
def test_dependency_files_can_select_core_files
|
|
Dir.mktmpdir('mkdepend-files') do |dir|
|
|
%w[depend enc/depend ext/example/depend].each do |file|
|
|
path = File.join(dir, file)
|
|
FileUtils.mkdir_p(File.dirname(path))
|
|
File.write(path, "#{MARK_START}\n#{MARK_END}\n")
|
|
end
|
|
|
|
mkdepend = TestDepend.new(root: dir)
|
|
assert_equal(%w[depend enc/depend], mkdepend.dependency_files(:core))
|
|
assert_equal(
|
|
%w[depend enc/depend ext/example/depend],
|
|
mkdepend.dependency_files(:all),
|
|
)
|
|
assert_equal(
|
|
%w[ext/example/depend],
|
|
mkdepend.dependency_files(:extensions),
|
|
)
|
|
end
|
|
end
|
|
|
|
def test_run_selects_extension_files
|
|
Dir.mktmpdir('mkdepend-files') do |root|
|
|
%w[depend ext/example/depend].each do |file|
|
|
path = File.join(root, file)
|
|
FileUtils.mkdir_p(File.dirname(path))
|
|
File.write(path, "#{MARK_START}\n#{MARK_END}\n")
|
|
end
|
|
|
|
Dir.mktmpdir('mkdepend-output') do |output|
|
|
assert_true(
|
|
MakeMakefile::Depend.main([
|
|
"--root=#{root}", "--scope=extensions", "--output=#{output}",
|
|
]),
|
|
)
|
|
assert_false(File.exist?(File.join(output, 'depend')))
|
|
assert_true(File.exist?(File.join(output, 'ext/example/depend')))
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_thread_model_is_resolved_for_extension_dependencies
|
|
Dir.mktmpdir('mkdepend-thread-model') do |root|
|
|
FileUtils.mkdir_p(File.join(root, 'ext/example'))
|
|
File.write(File.join(root, 'depend'), <<~DEPEND)
|
|
# mkdepend: scan THREAD_IMPL_H => thread_$(THREAD_MODEL).h
|
|
# mkdepend: depends THREAD_IMPL_H => thread_$(THREAD_MODEL).h
|
|
#{MARK_START}
|
|
#{MARK_END}
|
|
DEPEND
|
|
File.write(File.join(root, 'thread_pthread.h'), '')
|
|
File.write(
|
|
File.join(root, 'thread_win32.h'),
|
|
"#include \"win32_only.h\"\n",
|
|
)
|
|
File.write(File.join(root, 'win32_only.h'), '')
|
|
File.write(
|
|
File.join(root, 'ext/example/example.c'),
|
|
"#include THREAD_IMPL_H\n",
|
|
)
|
|
File.write(File.join(root, 'ext/example/depend'), <<~DEPEND)
|
|
#{MARK_START}
|
|
example.o: example.c
|
|
#{MARK_END}
|
|
DEPEND
|
|
|
|
mkdepend = TestDepend.new(root: root)
|
|
[
|
|
{make_variables: {"THREAD_MODEL" => "win32"}},
|
|
{thread_model: "win32"},
|
|
].each do |options|
|
|
out = StringIO.new
|
|
mkdepend.run(
|
|
[File.join(root, 'ext/example/example.c')],
|
|
out: out,
|
|
**options,
|
|
)
|
|
rules = out.string
|
|
assert_include(rules, 'example.o: $(top_srcdir)/thread_win32.h')
|
|
assert_include(rules, 'example.o: $(top_srcdir)/win32_only.h')
|
|
assert_not_include(rules, 'THREAD_MODEL')
|
|
assert_not_include(rules, 'thread_pthread.h')
|
|
end
|
|
|
|
out = StringIO.new
|
|
mkdepend.run([File.join(root, 'ext/example/example.c')], out: out)
|
|
assert_include(
|
|
out.string,
|
|
'example.o: $(top_srcdir)/thread_$(THREAD_MODEL).h',
|
|
)
|
|
assert_not_include(out.string, 'thread_win32.h')
|
|
end
|
|
end
|
|
|
|
def test_unicode_header
|
|
assert_equal(
|
|
['$(UNICODE_HDR_DIR)/name2ctype.h'],
|
|
mkdepend.depends(['enc/unicode/15.0.0/name2ctype.h'], nil),
|
|
)
|
|
end
|
|
|
|
def test_vpath_for_dependency_file
|
|
assert_equal('{$(VPATH)}', mkdepend.dependency_vpath('depend', 'enc/ascii.c'))
|
|
assert_nil(mkdepend.dependency_vpath('enc/depend', 'enc/ascii.c'))
|
|
assert_equal('{$(VPATH)}', mkdepend.dependency_vpath('depend', 'array.c'))
|
|
assert_equal(['{$(VPATH)}enc/ascii.c'], mkdepend.depends(['enc/ascii.c'], '{$(VPATH)}'))
|
|
assert_equal(['enc/ascii.c'], mkdepend.depends(['enc/ascii.c'], nil))
|
|
assert_equal(
|
|
['{$(VPATH)}thread_pthread.h'],
|
|
mkdepend.depends(['thread_pthread.h'], '{$(VPATH)}'),
|
|
)
|
|
end
|
|
|
|
def test_ruby_sources_are_not_headers
|
|
assert_empty(mkdepend.depends(%w[array.rb array.rbinc], '{$(VPATH)}') & ['{$(VPATH)}array.rb'])
|
|
end
|
|
|
|
def test_tool_sources
|
|
assert_equal(
|
|
%w[
|
|
$(top_srcdir)/tool/dump_ast.c
|
|
$(top_srcdir)/tool/mkdepend/yaml.h
|
|
],
|
|
mkdepend.depends(%w[tool/dump_ast.c tool/mkdepend/yaml.h], '{$(VPATH)}'),
|
|
)
|
|
end
|
|
|
|
def test_dependency_sources
|
|
assert_equal(
|
|
%w[array.c parse.y],
|
|
mkdepend.dependency_sources(<<~RULES, 'depend'),
|
|
array.$(OBJEXT): {$(VPATH)}array.c
|
|
parse.$(OBJEXT): {$(VPATH)}parse.c
|
|
parse.$(OBJEXT): {$(VPATH)}parse.y
|
|
RULES
|
|
)
|
|
assert_equal(
|
|
%w[enc/ascii.c],
|
|
mkdepend.dependency_sources(<<~RULES, 'enc/depend'),
|
|
enc/ascii.$(OBJEXT): enc/ascii.c
|
|
RULES
|
|
)
|
|
assert_equal(
|
|
%w[ext/date/date_core.c],
|
|
mkdepend.dependency_sources(<<~RULES, 'ext/date/depend'),
|
|
date_core.o: date_core.c
|
|
RULES
|
|
)
|
|
assert_equal(
|
|
%w[ext/-test-/float/nextafter.c],
|
|
mkdepend.dependency_sources(<<~RULES, 'ext/-test-/float/depend'),
|
|
nextafter.o: $(top_srcdir)/missing/nextafter.c
|
|
nextafter.o: nextafter.c
|
|
RULES
|
|
)
|
|
assert_equal(
|
|
%w[ext/-test-/load/dot.dot/dot.dot.c],
|
|
mkdepend.dependency_sources(<<~RULES, 'ext/-test-/load/dot.dot/depend'),
|
|
dot.dot.o: dot.dot.c
|
|
RULES
|
|
)
|
|
assert_equal(
|
|
%w[prism/api_node.c],
|
|
mkdepend.dependency_sources(<<~RULES, 'depend'),
|
|
prism/api_node.$(OBJEXT): $(top_srcdir)/prism/api_node.c
|
|
RULES
|
|
)
|
|
assert_equal(
|
|
{'dump_ast-dump_ast' => 'tool/dump_ast.c'},
|
|
mkdepend.dependency_source_map(<<~RULES, 'depend'),
|
|
dump_ast-dump_ast.$(OBJEXT): $(top_srcdir)/tool/dump_ast.c
|
|
RULES
|
|
)
|
|
end
|
|
|
|
def test_extension_headers
|
|
assert_equal(
|
|
[
|
|
'$(RUBY_EXTCONF_H)',
|
|
'$(arch_hdrdir)/ruby/config.h',
|
|
'$(hdrdir)/ruby.h',
|
|
'$(hdrdir)/ruby/internal/intern/parse.h',
|
|
'$(hdrdir)/ruby/internal/value.h',
|
|
'$(hdrdir)/ruby/version.h',
|
|
'$(top_srcdir)/internal.h',
|
|
'$(top_srcdir)/internal/parse.h',
|
|
'date_tmx.h',
|
|
'{$(VPATH)}probes.dmyh',
|
|
],
|
|
mkdepend.depends(
|
|
%w[
|
|
extconf.h
|
|
config.h
|
|
include/ruby/internal/intern/parse.h
|
|
include/ruby/internal/value.h
|
|
internal.h
|
|
internal/parse.h
|
|
probes.h
|
|
ext/date/date_tmx.h
|
|
],
|
|
nil,
|
|
source: 'ext/date/date_core.c',
|
|
),
|
|
)
|
|
assert_equal(
|
|
['$(srcdir)/../digest.h'],
|
|
mkdepend.depends(['ext/digest/md5/../digest.h'], nil, source: 'ext/digest/md5/md5.c'),
|
|
)
|
|
assert_equal(
|
|
['{$(VPATH)}ripper.c'],
|
|
mkdepend.depends(['ext/ripper/ripper.c'], nil, source: 'ext/ripper/ripper.y'),
|
|
)
|
|
assert_equal(
|
|
['$(top_srcdir)/thread_$(THREAD_MODEL).h'],
|
|
mkdepend.depends(['THREAD_IMPL_H'], nil, source: 'ext/coverage/coverage.c'),
|
|
)
|
|
assert_equal(
|
|
['{$(VPATH)}probes.dmyh'],
|
|
mkdepend.depends(%w[probes.h probes.dmyh], nil, source: 'ext/ripper/ripper.y'),
|
|
)
|
|
end
|
|
|
|
def test_object_name
|
|
assert_equal('array.$(OBJEXT)', mkdepend.object_name('array.c'))
|
|
assert_equal('enc/utf_8.$(OBJEXT)', mkdepend.object_name('enc/utf_8.c'))
|
|
assert_equal('date_core.o', mkdepend.object_name('ext/date/date_core.c'))
|
|
assert_equal('ripper.o', mkdepend.object_name('ext/ripper/ripper.y'))
|
|
assert_equal('ffs.$(OBJEXT)', mkdepend.object_name('missing/ffs.c'))
|
|
end
|
|
|
|
def test_parse_and_ripper_conditions
|
|
assert_not_include(mkdepend.makedepend('parse.y').join, 'eventids1.h')
|
|
assert_include(mkdepend.makedepend('ext/ripper/ripper.y').join, 'eventids1.h')
|
|
end
|
|
|
|
def test_generated_headers_are_dependencies
|
|
unicode = mkdepend.makedepend('enc/unicode.c').join
|
|
assert_include(unicode, '$(UNICODE_HDR_DIR)/casefold.h')
|
|
assert_include(unicode, '$(UNICODE_HDR_DIR)/name2ctype.h')
|
|
assert_include(mkdepend.makedepend('loadpath.c').join, '{$(VPATH)}verconf.h')
|
|
assert_include(mkdepend.makedepend('ruby-runner.c').join, '{$(VPATH)}ruby-runner.h')
|
|
end
|
|
|
|
def test_compact_dependencies
|
|
rules = <<~RULES
|
|
array.$(OBJEXT): common.h
|
|
hash.$(OBJEXT): common.h
|
|
array.$(OBJEXT): array.c
|
|
hash.$(OBJEXT): hash.c
|
|
RULES
|
|
assert_equal(<<~DEPEND, mkdepend.compact_dependencies(rules))
|
|
array.$(OBJEXT) hash.$(OBJEXT): common.h
|
|
array.$(OBJEXT): array.c
|
|
hash.$(OBJEXT): hash.c
|
|
DEPEND
|
|
end
|
|
|
|
def test_compact_dependencies_keeps_separate_targets_for_nmake
|
|
rules = <<~RULES
|
|
array.o: common.h
|
|
hash.o: common.h
|
|
array.o: array.c
|
|
hash.o: hash.c
|
|
RULES
|
|
assert_equal(<<~DEPEND, mkdepend.compact_dependencies(rules, group: false))
|
|
array.o: array.c
|
|
array.o: common.h
|
|
hash.o: common.h
|
|
hash.o: hash.c
|
|
DEPEND
|
|
end
|
|
|
|
def test_minimize_deps_keeps_only_preferred_source_rules
|
|
rules = <<~RULES
|
|
array.$(OBJEXT): common.h
|
|
array.$(OBJEXT): {$(VPATH)}array.c
|
|
parse.$(OBJEXT): {$(VPATH)}parse.c
|
|
parse.$(OBJEXT): {$(VPATH)}parse.y
|
|
hash.$(OBJEXT): common.h
|
|
hash.$(OBJEXT): hash.c
|
|
RULES
|
|
assert_equal(<<~DEPEND, mkdepend.minimize_deps(rules, 'depend'))
|
|
array.$(OBJEXT): {$(VPATH)}array.c
|
|
hash.$(OBJEXT): hash.c
|
|
parse.$(OBJEXT): {$(VPATH)}parse.y
|
|
DEPEND
|
|
end
|
|
|
|
def test_minimize_deps_keeps_upstream_dependencies
|
|
rules = <<~RULES
|
|
generator.o: $(hdrdir)/ruby.h
|
|
generator.o: $(srcdir)/../json.h
|
|
generator.o: generator.c
|
|
RULES
|
|
expected = <<~DEPEND
|
|
generator.o: $(srcdir)/../json.h
|
|
generator.o: generator.c
|
|
DEPEND
|
|
assert_equal(
|
|
expected,
|
|
mkdepend.minimize_deps(rules, 'ext/json/generator/depend'),
|
|
)
|
|
end
|
|
|
|
def test_sources_regenerates_upstream_dependencies
|
|
Dir.mktmpdir('mkdepend') do |root|
|
|
source_dir = File.join(root, 'ext/example')
|
|
FileUtils.mkdir_p(source_dir)
|
|
File.write(File.join(root, 'ext/current.h'), '')
|
|
File.write(File.join(source_dir, 'example.c'), <<~SOURCE)
|
|
#include "../current.h"
|
|
SOURCE
|
|
depend = File.join(source_dir, 'depend')
|
|
File.write(depend, <<~DEPEND)
|
|
#{MARK_START}
|
|
example.o: $(srcdir)/../stale.h
|
|
example.o: example.c
|
|
#{MARK_END}
|
|
DEPEND
|
|
|
|
TestDepend.new(root: root).run(
|
|
[depend], sources: true, mode: :inplace,
|
|
)
|
|
|
|
assert_equal(<<~DEPEND, File.read(depend))
|
|
#{MARK_START}
|
|
example.o: $(srcdir)/../current.h
|
|
example.o: example.c
|
|
#{MARK_END}
|
|
DEPEND
|
|
end
|
|
end
|
|
|
|
def test_minimize_deps_preserves_text_outside_markers
|
|
content = +<<~DEPEND
|
|
manual: rule
|
|
#{MARK_START}
|
|
array.$(OBJEXT): common.h
|
|
array.$(OBJEXT): {$(VPATH)}array.c
|
|
#{MARK_END}
|
|
upstream: rule
|
|
DEPEND
|
|
Dir.mktmpdir('mkdepend') do |dir|
|
|
path = File.join(dir, 'depend')
|
|
File.write(path, content)
|
|
|
|
assert_false(
|
|
mkdepend.run(
|
|
[path], sources: true, mode: :check, err: StringIO.new,
|
|
),
|
|
)
|
|
assert_true(mkdepend.run([path], sources: true, mode: :inplace))
|
|
assert_equal(<<~DEPEND, File.read(path))
|
|
manual: rule
|
|
#{MARK_START}
|
|
array.$(OBJEXT): {$(VPATH)}array.c
|
|
#{MARK_END}
|
|
upstream: rule
|
|
DEPEND
|
|
end
|
|
end
|
|
|
|
def test_update_extension_uses_explicit_source_map
|
|
Dir.mktmpdir('mkdepend-extension') do |root|
|
|
source_dir = File.join(root, 'ext/example')
|
|
FileUtils.mkdir_p(source_dir)
|
|
FileUtils.mkdir_p(File.join(root, 'include'))
|
|
File.write(File.join(root, 'ext/shared.h'), '')
|
|
File.write(File.join(root, 'include/project.h'), '')
|
|
File.write(File.join(source_dir, 'example.c'), <<~SOURCE)
|
|
#include "../shared.h"
|
|
#include "../../include/project.h"
|
|
SOURCE
|
|
depend = File.join(source_dir, 'depend')
|
|
File.write(depend, <<~DEPEND)
|
|
#{MARK_START}
|
|
stale.o: stale.c
|
|
#{MARK_END}
|
|
DEPEND
|
|
|
|
MakeMakefile::Depend.new(root: root).update_extension(
|
|
depend,
|
|
{'example' => File.join(source_dir, 'example.c')},
|
|
)
|
|
|
|
assert_equal(<<~DEPEND, File.read(depend))
|
|
#{MARK_START}
|
|
example.o: $(srcdir)/../../include/project.h
|
|
example.o: $(srcdir)/../shared.h
|
|
example.o: example.c
|
|
#{MARK_END}
|
|
DEPEND
|
|
end
|
|
end
|
|
|
|
def test_update_extension_expands_make_variables_in_manual_rules
|
|
Dir.mktmpdir('mkdepend-extension') do |root|
|
|
source_dir = File.join(root, 'ext/example')
|
|
FileUtils.mkdir_p(source_dir)
|
|
File.write(File.join(source_dir, 'local.h'), '')
|
|
File.write(
|
|
File.join(source_dir, 'example.c'),
|
|
"#include \"local.h\"\n",
|
|
)
|
|
depend = File.join(source_dir, 'depend')
|
|
File.write(depend, <<~DEPEND)
|
|
example.o: $(LOCAL_HDRS)
|
|
|
|
#{MARK_START}
|
|
stale.o: stale.c
|
|
#{MARK_END}
|
|
DEPEND
|
|
|
|
MakeMakefile::Depend.new(root: root).update_extension(
|
|
depend,
|
|
{'example' => File.join(source_dir, 'example.c')},
|
|
make_variables: {"LOCAL_HDRS" => ["{.;$(VPATH)}local.h"]},
|
|
)
|
|
|
|
assert_equal(<<~DEPEND, File.read(depend))
|
|
example.o: $(LOCAL_HDRS)
|
|
|
|
#{MARK_START}
|
|
example.o: example.c
|
|
example.o: local.h
|
|
#{MARK_END}
|
|
DEPEND
|
|
end
|
|
end
|
|
|
|
def test_update_extension_requires_markers
|
|
Dir.mktmpdir('mkdepend-extension') do |root|
|
|
source_dir = File.join(root, 'ext/example')
|
|
FileUtils.mkdir_p(source_dir)
|
|
File.write(File.join(source_dir, 'example.c'), '')
|
|
depend = File.join(source_dir, 'depend')
|
|
File.write(depend, "example.o: example.c\n")
|
|
|
|
error = assert_raise(RuntimeError) do
|
|
MakeMakefile::Depend.new(root: root).update_extension(
|
|
depend,
|
|
{'example' => File.join(source_dir, 'example.c')},
|
|
)
|
|
end
|
|
assert_include(error.message, "no autogenerated dependency section")
|
|
end
|
|
end
|
|
|
|
def test_relative_source
|
|
assert_equal('array.c', mkdepend.relative_source(File.join(TOP_SRCDIR, 'array.c')))
|
|
assert_equal(
|
|
'array.c',
|
|
mkdepend.relative_source(File.join(TOP_SRCDIR, '.+aarch64-darwin', '..', 'array.c')),
|
|
)
|
|
end
|
|
|
|
def test_replace_file_without_leftovers
|
|
Dir.mktmpdir('mkdepend') do |dir|
|
|
path = File.join(dir, 'depend')
|
|
File.write(path, <<~DEPEND)
|
|
#{MARK_START}
|
|
array.$(OBJEXT): {$(VPATH)}array.c
|
|
#{MARK_END}
|
|
DEPEND
|
|
File.chmod(0644, path)
|
|
content = File.read(path)
|
|
|
|
assert_true(mkdepend.run([path], mode: :inplace))
|
|
|
|
assert_include(File.read(path), 'array.$(OBJEXT): $(hdrdir)/ruby/ruby.h')
|
|
assert_equal(0644, File.stat(path).mode & 0777)
|
|
assert_equal(%w[depend], Dir.children(dir))
|
|
end
|
|
end
|
|
|
|
def test_run_writes_expanded_dependency_to_output_directory
|
|
Dir.mktmpdir('mkdepend-output') do |dir|
|
|
source = File.join(TOP_SRCDIR, 'ext/date/depend')
|
|
output = File.join(dir, 'build-deps')
|
|
original = File.read(source)
|
|
|
|
assert_true(mkdepend.run([source], mode: :output, output: output))
|
|
generated = File.read(File.join(output, 'ext/date/depend'))
|
|
assert_include(generated, 'date_core.o: date_core.c')
|
|
assert_match(/date_core\.o.*: \$\(hdrdir\)\/ruby\/ruby\.h/, generated)
|
|
assert_not_include(generated, '{$(VPATH)}')
|
|
assert_equal(original, File.read(source))
|
|
end
|
|
end
|
|
|
|
def test_run_reuses_unchanged_output_dependencies
|
|
Dir.mktmpdir('mkdepend-input') do |root|
|
|
File.write(File.join(root, 'one.c'), "#include \"common.h\"\n")
|
|
header = File.join(root, 'common.h')
|
|
File.write(header, "")
|
|
input = File.join(root, 'depend')
|
|
File.write(input, <<~DEPEND)
|
|
#{MARK_START}
|
|
one.$(OBJEXT): one.c
|
|
#{MARK_END}
|
|
DEPEND
|
|
output = File.join(root, '.deps')
|
|
destination = File.join(output, 'depend')
|
|
|
|
updater = TestDepend.new(root: root)
|
|
assert_true(updater.run([input], mode: :output, output: output))
|
|
assert_path_exist(destination + '.mkdepend')
|
|
|
|
opened = []
|
|
original_open = File.method(:open)
|
|
File.define_singleton_method(:open) do |path, *args, **kwargs, &block|
|
|
opened << File.expand_path(path)
|
|
original_open.call(path, *args, **kwargs, &block)
|
|
end
|
|
updater = TestDepend.new(root: root)
|
|
assert_true(updater.run([input], mode: :output, output: output))
|
|
assert_empty(opened & [File.join(root, 'one.c'), header])
|
|
|
|
future = Time.now + 2
|
|
File.utime(future, future, header)
|
|
updater = TestDepend.new(root: root)
|
|
assert_true(updater.run([input], mode: :output, output: output))
|
|
assert_include(opened, header)
|
|
ensure
|
|
File.define_singleton_method(:open, original_open) if original_open
|
|
end
|
|
end
|
|
|
|
def test_run_invalidates_output_when_missing_header_appears
|
|
Dir.mktmpdir('mkdepend-input') do |root|
|
|
File.write(File.join(root, 'one.c'), "#include \"appeared.h\"\n")
|
|
input = File.join(root, 'depend')
|
|
File.write(input, <<~DEPEND)
|
|
#{MARK_START}
|
|
one.$(OBJEXT): one.c
|
|
#{MARK_END}
|
|
DEPEND
|
|
output = File.join(root, '.deps')
|
|
destination = File.join(output, 'depend')
|
|
updater = TestDepend.new(root: root)
|
|
|
|
assert_true(updater.run([input], mode: :output, output: output))
|
|
assert_not_include(File.read(destination), 'appeared.h')
|
|
|
|
File.write(File.join(root, 'appeared.h'), "")
|
|
assert_true(updater.run([input], mode: :output, output: output))
|
|
assert_include(File.read(destination), 'one.$(OBJEXT): appeared.h')
|
|
end
|
|
end
|
|
|
|
def test_run_reuses_output_with_non_directory_include_prefix
|
|
Dir.mktmpdir('mkdepend-input') do |root|
|
|
File.write(File.join(root, 'ruby'), "")
|
|
File.write(File.join(root, 'one.c'), "#include \"ruby/assert.h\"\n")
|
|
input = File.join(root, 'depend')
|
|
File.write(input, <<~DEPEND)
|
|
#{MARK_START}
|
|
one.$(OBJEXT): one.c
|
|
#{MARK_END}
|
|
DEPEND
|
|
output = File.join(root, '.deps')
|
|
updater = TestDepend.new(root: root)
|
|
|
|
assert_true(updater.run([input], mode: :output, output: output))
|
|
updater = TestDepend.new(root: root)
|
|
assert_true(updater.run([input], mode: :output, output: output))
|
|
end
|
|
end
|
|
|
|
def test_run_preserves_vpath_notation_for_nmake_output
|
|
Dir.mktmpdir('mkdepend-output') do |dir|
|
|
source = File.join(TOP_SRCDIR, 'ext/-test-/thread/id/depend')
|
|
|
|
assert_true(
|
|
mkdepend.run([source], mode: :output, output: dir, nmake: true),
|
|
)
|
|
generated = File.read(File.join(dir, 'ext/-test-/thread/id/depend'))
|
|
assert_include(generated, '{$(VPATH)}id.c')
|
|
end
|
|
end
|
|
|
|
def test_run_removes_vpath_notation_from_build_output
|
|
Dir.mktmpdir('mkdepend-output') do |dir|
|
|
source = File.join(TOP_SRCDIR, 'ext/-test-/thread/id/depend')
|
|
|
|
assert_true(mkdepend.run([source], mode: :output, output: dir))
|
|
generated = File.read(File.join(dir, 'ext/-test-/thread/id/depend'))
|
|
assert_include(generated, 'id.o: id.c')
|
|
assert_not_include(generated, '{$(VPATH)}')
|
|
end
|
|
end
|
|
|
|
def test_run_from_build_directory_keeps_generated_dependency_names
|
|
Dir.mktmpdir('mkdepend-builddir') do |dir|
|
|
File.write(File.join(dir, 'builtin.c'), <<~SOURCE)
|
|
#include "builtin_binary.rbbin"
|
|
SOURCE
|
|
input = File.join(dir, 'depend')
|
|
File.write(input, <<~DEPEND)
|
|
#{MARK_START}
|
|
builtin.$(OBJEXT): {$(VPATH)}builtin.c
|
|
#{MARK_END}
|
|
DEPEND
|
|
build = File.join(dir, '.build')
|
|
FileUtils.mkdir_p(build)
|
|
File.write(File.join(build, 'builtin_binary.rbbin'), '')
|
|
output = File.join(build, '.deps')
|
|
|
|
mkdepend = TestDepend.new(root: dir)
|
|
Dir.chdir(build) do
|
|
assert_true(mkdepend.run([input], mode: :output, output: output))
|
|
end
|
|
generated = File.read(File.join(output, 'depend'))
|
|
assert_include(generated, "builtin.$(OBJEXT): builtin_binary.rbbin\n")
|
|
assert_not_include(generated, '.build/builtin_binary.rbbin')
|
|
end
|
|
end
|
|
|
|
def test_build_directory_does_not_shadow_source_tree_names
|
|
Dir.mktmpdir('mkdepend-builddir') do |root|
|
|
FileUtils.mkdir_p(File.join(root, 'ext/example'))
|
|
File.write(File.join(root, 'ext/example/depend'), <<~DEPEND)
|
|
# mkdepend: depends example.c => {$(VPATH)}example.c
|
|
DEPEND
|
|
build = File.join(root, 'build')
|
|
FileUtils.mkdir_p(File.join(build, 'ext/example'))
|
|
File.write(File.join(build, 'ext/example/example.c'), '')
|
|
|
|
mkdepend = TestDepend.new(root: root)
|
|
Dir.chdir(build) do
|
|
assert_equal(
|
|
'ext/example/depend',
|
|
mkdepend.dependency_input('ext/example/example.c'),
|
|
)
|
|
assert_equal(
|
|
['{$(VPATH)}example.c'],
|
|
mkdepend.depends(%w[ext/example/example.c], nil,
|
|
source: 'ext/example/example.c'),
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_normalize_dependency_rules_removes_vpath_search
|
|
assert_equal(
|
|
"one.h two.h\n",
|
|
mkdepend.normalize_dependency_rules(
|
|
"{$(VPATH)}one.h {.;$(VPATH)}two.h\n",
|
|
),
|
|
)
|
|
end
|
|
|
|
def test_run_writes_unchanged_dependencies_to_output_directory
|
|
Dir.mktmpdir('mkdepend-input') do |input_dir|
|
|
Dir.mktmpdir('mkdepend-output') do |output|
|
|
mkdepend = TestDepend.new(root: input_dir)
|
|
input = File.join(input_dir, 'depend')
|
|
File.write(File.join(input_dir, 'array.c'), '')
|
|
rules = "array.$(OBJEXT): {$(VPATH)}array.c\n"
|
|
File.write(input, "#{MARK_START}\n#{rules}#{MARK_END}\n")
|
|
destination = File.join(output, 'depend')
|
|
|
|
assert_true(mkdepend.run([input], mode: :output, output: output))
|
|
assert_equal(
|
|
mkdepend.normalize_dependency_rules(File.read(input)),
|
|
File.read(destination),
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_file_without_markers_is_not_updated
|
|
Dir.mktmpdir('mkdepend') do |dir|
|
|
path = File.join(dir, 'depend')
|
|
content = "array.$(OBJEXT): {$(VPATH)}array.c\n"
|
|
File.write(path, content)
|
|
|
|
assert_true(mkdepend.run([path], mode: :inplace))
|
|
assert_equal(content, File.read(path))
|
|
end
|
|
end
|
|
|
|
def test_unterminated_dependency_section_is_rejected
|
|
content = +<<~DEPEND
|
|
#{MARK_START}
|
|
array.$(OBJEXT): {$(VPATH)}array.c
|
|
DEPEND
|
|
|
|
[content, MARK_START].each do |deps|
|
|
Dir.mktmpdir('mkdepend') do |dir|
|
|
path = File.join(dir, 'depend')
|
|
File.write(path, deps)
|
|
error = assert_raise(RuntimeError) {mkdepend.run([path])}
|
|
assert_include(error.message, "missing #{MARK_END}")
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_object_rules_without_a_source_are_rejected
|
|
content = +<<~DEPEND
|
|
#{MARK_START}
|
|
unknown.o: unknown.h
|
|
#{MARK_END}
|
|
DEPEND
|
|
Dir.mktmpdir('mkdepend') do |dir|
|
|
path = File.join(dir, 'depend')
|
|
File.write(path, content)
|
|
error = assert_raise(RuntimeError) {mkdepend.run([path])}
|
|
assert_include(error.message, 'no source files')
|
|
end
|
|
end
|
|
|
|
def test_missing_dependency_source_is_rejected
|
|
rules = <<~DEPEND
|
|
unknown.o: unknown.c
|
|
DEPEND
|
|
|
|
error = assert_raise(RuntimeError) do
|
|
mkdepend.minimize_deps(rules, 'ext/test/depend')
|
|
end
|
|
assert_include(error.message, 'source file not found: ext/test/unknown.c')
|
|
|
|
error = assert_raise(RuntimeError) do
|
|
mkdepend.update_deps(rules, 'ext/test/depend')
|
|
end
|
|
assert_include(error.message, 'source file not found: ext/test/unknown.c')
|
|
end
|
|
|
|
def test_vpath_notation_does_not_affect_dependency_comparison
|
|
input = 'ext/-test-/thread/id/depend'
|
|
expected = mkdepend.compact_dependencies(
|
|
mkdepend.makedepend('ext/-test-/thread/id/id.c', input: input).join,
|
|
)
|
|
plain = expected.gsub('{$(VPATH)}', '')
|
|
|
|
assert_true(mkdepend.same_dependency_rules?(expected, plain))
|
|
assert_true(mkdepend.same_dependency_rules?(plain, expected))
|
|
|
|
assert_true(mkdepend.same_dependency_rules?(plain, mkdepend.update_deps(plain, input)))
|
|
end
|
|
|
|
def test_outdated_dependency_report_shows_small_rule_changes
|
|
current = <<~RULES
|
|
array.$(OBJEXT): common.h
|
|
array.$(OBJEXT): {$(VPATH)}array.c
|
|
RULES
|
|
expected = "array.$(OBJEXT): {$(VPATH)}array.c\n"
|
|
err = StringIO.new
|
|
|
|
mkdepend.report_outdated_dependencies('depend', current, expected, err: err)
|
|
|
|
assert_include(err.string, 'depend: dependencies are outdated')
|
|
assert_include(err.string, 'current dependency rules:')
|
|
assert_include(err.string, 'array.$(OBJEXT): common.h')
|
|
assert_include(err.string, 'expected source rules:')
|
|
end
|
|
|
|
def test_outdated_dependency_report_summarizes_large_rule_changes
|
|
headers = 25.times.map {|i| "array.$(OBJEXT): header#{i}.h\n"}.join
|
|
current = headers + "array.$(OBJEXT): {$(VPATH)}array.c\n"
|
|
expected = "array.$(OBJEXT): {$(VPATH)}array.c\n"
|
|
err = StringIO.new
|
|
|
|
mkdepend.report_outdated_dependencies('depend', current, expected, err: err)
|
|
|
|
assert_include(err.string, 'current section has 26 rules; expected 1 source rule')
|
|
assert_not_include(err.string, 'header0.h')
|
|
assert_include(err.string, expected.strip)
|
|
end
|
|
end
|