diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2020-02-23 20:23:03 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-23 20:23:03 +0200 |
commit | 9c604320a0cd47a5b22b52e157d43b73b0533e82 (patch) | |
tree | 0e90134be40901fcbea20faf36c539b474b80683 | |
parent | ee94cb6c15732c8b5f329164af7f2b0a63f4fa6d (diff) | |
parent | 04e89d0867e358df347dbc8cb91e6df7bc365d9a (diff) | |
download | meson-9c604320a0cd47a5b22b52e157d43b73b0533e82.zip meson-9c604320a0cd47a5b22b52e157d43b73b0533e82.tar.gz meson-9c604320a0cd47a5b22b52e157d43b73b0533e82.tar.bz2 |
Merge pull request #6637 from mesonbuild/nirbheek/implement-symbolextractor-windows
Implement symbolextractor on windows + some cleanups/fixes
-rw-r--r-- | ci/azure-steps.yml | 4 | ||||
-rw-r--r-- | docs/markdown/snippets/ninja_version_bump.md | 10 | ||||
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 89 | ||||
-rw-r--r-- | mesonbuild/compilers/compilers.py | 3 | ||||
-rw-r--r-- | mesonbuild/environment.py | 4 | ||||
-rw-r--r-- | mesonbuild/linkers.py | 15 | ||||
-rw-r--r-- | mesonbuild/scripts/symbolextractor.py | 229 | ||||
-rw-r--r-- | mesonbuild/templates/cpptemplates.py | 3 | ||||
-rw-r--r-- | mesonbuild/templates/ctemplates.py | 3 | ||||
-rw-r--r-- | mesonbuild/templates/cudatemplates.py | 3 | ||||
-rw-r--r-- | mesonbuild/templates/dlangtemplates.py | 3 | ||||
-rw-r--r-- | mesonbuild/templates/fortrantemplates.py | 3 | ||||
-rw-r--r-- | mesonbuild/templates/javatemplates.py | 1 | ||||
-rw-r--r-- | mesonbuild/templates/objcpptemplates.py | 3 | ||||
-rw-r--r-- | mesonbuild/templates/objctemplates.py | 3 | ||||
-rw-r--r-- | mesonbuild/templates/rusttemplates.py | 3 | ||||
-rwxr-xr-x | run_tests.py | 18 | ||||
-rwxr-xr-x | run_unittests.py | 82 | ||||
-rw-r--r-- | test cases/common/149 recursive linking/3rdorderdeps/meson.build | 2 |
19 files changed, 375 insertions, 106 deletions
diff --git a/ci/azure-steps.yml b/ci/azure-steps.yml index fa28046..8ec219e 100644 --- a/ci/azure-steps.yml +++ b/ci/azure-steps.yml @@ -114,8 +114,8 @@ steps: $env:Path = $origPath # install llvm for clang-cl builds - DownloadFile -Source 'http://releases.llvm.org/7.0.0/LLVM-7.0.0-win64.exe' -Destination LLVM-7.0.0-win64.exe - Start-Process .\LLVM-7.0.0-win64.exe -ArgumentList '/S' -Wait + DownloadFile -Source 'http://releases.llvm.org/9.0.0/LLVM-9.0.0-win64.exe' -Destination LLVM-9.0.0-win64.exe + Start-Process .\LLVM-9.0.0-win64.exe -ArgumentList '/S' -Wait $env:Path = "C:\Program Files\LLVM\bin;$env:Path" $env:CC = "clang-cl" $env:CXX = "clang-cl" diff --git a/docs/markdown/snippets/ninja_version_bump.md b/docs/markdown/snippets/ninja_version_bump.md new file mode 100644 index 0000000..9c5f6e8 --- /dev/null +++ b/docs/markdown/snippets/ninja_version_bump.md @@ -0,0 +1,10 @@ +## Ninja version requirement bumped to 1.7 + +Meson now uses the [Implicit outputs](https://ninja-build.org/manual.html#ref_outputs) +feature of Ninja for some types of targets that have multiple outputs which may +not be listed on the command-line. This feature requires Ninja 1.7+. + +Note that the latest version of [Ninja available in Ubuntu 16.04](https://packages.ubuntu.com/search?keywords=ninja-build&searchon=names&suite=xenial-backports§ion=all) +(the oldest Ubuntu LTS at the time of writing) is 1.7.1. If your distro does +not ship with a new-enough Ninja, you can download the latest release from +Ninja's GitHub page: https://github.com/ninja-build/ninja/releases diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index bcb916c..5f5de0a 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -120,7 +120,8 @@ class NinjaRule: outfile.write('\n') class NinjaBuildElement: - def __init__(self, all_outputs, outfilenames, rule, infilenames): + def __init__(self, all_outputs, outfilenames, rule, infilenames, implicit_outs=None): + self.implicit_outfilenames = implicit_outs or [] if isinstance(outfilenames, str): self.outfilenames = [outfilenames] else: @@ -155,9 +156,12 @@ class NinjaBuildElement: def write(self, outfile): self.check_outputs() - line = 'build %s: %s %s' % (' '.join([ninja_quote(i, True) for i in self.outfilenames]), - self.rule, - ' '.join([ninja_quote(i, True) for i in self.infilenames])) + ins = ' '.join([ninja_quote(i, True) for i in self.infilenames]) + outs = ' '.join([ninja_quote(i, True) for i in self.outfilenames]) + implicit_outs = ' '.join([ninja_quote(i, True) for i in self.implicit_outfilenames]) + if implicit_outs: + implicit_outs = ' | ' + implicit_outs + line = 'build {}{}: {} {}'.format(outs, implicit_outs, self.rule, ins) if len(self.deps) > 0: line += ' | ' + ' '.join([ninja_quote(x, True) for x in self.deps]) if len(self.orderdeps) > 0: @@ -664,7 +668,7 @@ int dummy; (srcs, ofilenames, cmd) = self.eval_custom_target_command(target) deps = self.unwrap_dep_list(target) deps += self.get_custom_target_depend_files(target) - desc = 'Generating {0} with a {1} command.' + desc = 'Generating {0} with a {1} command' if target.build_always_stale: deps.append('PHONY') if target.depfile is None: @@ -760,7 +764,7 @@ int dummy; target_name = 'meson-{}'.format(self.build_run_target_name(target)) elem = NinjaBuildElement(self.all_outputs, target_name, 'CUSTOM_COMMAND', []) elem.add_item('COMMAND', cmd) - elem.add_item('description', 'Running external command %s.' % target.name) + elem.add_item('description', 'Running external command %s' % target.name) elem.add_item('pool', 'console') # Alias that runs the target defined above with the name the user specified self.create_target_alias(target_name) @@ -785,7 +789,7 @@ int dummy; def generate_coverage_rules(self): e = NinjaBuildElement(self.all_outputs, 'meson-coverage', 'CUSTOM_COMMAND', 'PHONY') self.generate_coverage_command(e, []) - e.add_item('description', 'Generates coverage reports.') + e.add_item('description', 'Generates coverage reports') self.add_build(e) # Alias that runs the target defined above self.create_target_alias('meson-coverage') @@ -794,21 +798,21 @@ int dummy; def generate_coverage_legacy_rules(self): e = NinjaBuildElement(self.all_outputs, 'meson-coverage-xml', 'CUSTOM_COMMAND', 'PHONY') self.generate_coverage_command(e, ['--xml']) - e.add_item('description', 'Generates XML coverage report.') + e.add_item('description', 'Generates XML coverage report') self.add_build(e) # Alias that runs the target defined above self.create_target_alias('meson-coverage-xml') e = NinjaBuildElement(self.all_outputs, 'meson-coverage-text', 'CUSTOM_COMMAND', 'PHONY') self.generate_coverage_command(e, ['--text']) - e.add_item('description', 'Generates text coverage report.') + e.add_item('description', 'Generates text coverage report') self.add_build(e) # Alias that runs the target defined above self.create_target_alias('meson-coverage-text') e = NinjaBuildElement(self.all_outputs, 'meson-coverage-html', 'CUSTOM_COMMAND', 'PHONY') self.generate_coverage_command(e, ['--html']) - e.add_item('description', 'Generates HTML coverage report.') + e.add_item('description', 'Generates HTML coverage report') self.add_build(e) # Alias that runs the target defined above self.create_target_alias('meson-coverage-html') @@ -975,7 +979,7 @@ int dummy; ofilename = os.path.join(self.get_target_private_dir(target), ofilebase) elem = NinjaBuildElement(self.all_outputs, ofilename, "CUSTOM_COMMAND", rel_sourcefile) elem.add_item('COMMAND', ['resgen', rel_sourcefile, ofilename]) - elem.add_item('DESC', 'Compiling resource %s.' % rel_sourcefile) + elem.add_item('DESC', 'Compiling resource %s' % rel_sourcefile) self.add_build(elem) deps.append(ofilename) a = '-resource:' + ofilename @@ -1069,7 +1073,7 @@ int dummy; def generate_java_link(self): rule = 'java_LINKER' command = ['jar', '$ARGS'] - description = 'Creating JAR $out.' + description = 'Creating JAR $out' self.add_rule(NinjaRule(rule, command, [], description)) def determine_dep_vapis(self, target): @@ -1550,7 +1554,7 @@ int dummy; cmdlist += static_linker.get_exelist() cmdlist += ['$LINK_ARGS'] cmdlist += static_linker.get_output_args('$out') - description = 'Linking static target $out.' + description = 'Linking static target $out' if num_pools > 0: pool = 'pool = link_pool' else: @@ -1572,7 +1576,7 @@ int dummy; rule = '%s_LINKER%s' % (langname, self.get_rule_suffix(for_machine)) command = compiler.get_linker_exelist() args = ['$ARGS'] + compiler.get_linker_output_args('$out') + ['$in', '$LINK_ARGS'] - description = 'Linking target $out.' + description = 'Linking target $out' if num_pools > 0: pool = 'pool = link_pool' else: @@ -1584,11 +1588,13 @@ int dummy; args = [ninja_quote(quote_func(x)) for x in self.environment.get_build_command()] + \ ['--internal', 'symbolextractor', + ninja_quote(quote_func(self.environment.get_build_dir())), '$in', + '$IMPLIB', '$out'] symrule = 'SHSYM' symcmd = args + ['$CROSS'] - syndesc = 'Generating symbol file $out.' + syndesc = 'Generating symbol file $out' synstat = 'restat = 1' self.add_rule(NinjaRule(symrule, symcmd, [], syndesc, extra=synstat)) @@ -1596,7 +1602,7 @@ int dummy; rule = self.compiler_to_rule_name(compiler) invoc = [ninja_quote(i) for i in compiler.get_exelist()] command = invoc + ['$ARGS', '$in'] - description = 'Compiling Java object $in.' + description = 'Compiling Java object $in' self.add_rule(NinjaRule(rule, command, [], description)) def generate_cs_compile_rule(self, compiler): @@ -1604,7 +1610,7 @@ int dummy; invoc = [ninja_quote(i) for i in compiler.get_exelist()] command = invoc args = ['$ARGS', '$in'] - description = 'Compiling C Sharp target $out.' + description = 'Compiling C Sharp target $out' self.add_rule(NinjaRule(rule, command, args, description, rspable=mesonlib.is_windows())) @@ -1612,14 +1618,14 @@ int dummy; rule = self.compiler_to_rule_name(compiler) invoc = [ninja_quote(i) for i in compiler.get_exelist()] command = invoc + ['$ARGS', '$in'] - description = 'Compiling Vala source $in.' + description = 'Compiling Vala source $in' self.add_rule(NinjaRule(rule, command, [], description, extra='restat = 1')) def generate_rust_compile_rules(self, compiler): rule = self.compiler_to_rule_name(compiler) invoc = [ninja_quote(i) for i in compiler.get_exelist()] command = invoc + ['$ARGS', '$in'] - description = 'Compiling Rust source $in.' + description = 'Compiling Rust source $in' depfile = '$targetdep' depstyle = 'gcc' self.add_rule(NinjaRule(rule, command, [], description, deps=depstyle, @@ -1634,7 +1640,7 @@ int dummy; ] invoc = full_exe + [ninja_quote(i) for i in compiler.get_exelist()] command = invoc + ['$ARGS', '$in'] - description = 'Compiling Swift source $in.' + description = 'Compiling Swift source $in' self.add_rule(NinjaRule(rule, command, [], description)) def generate_fortran_dep_hack(self, crstr): @@ -1654,7 +1660,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) rule = self.get_compiler_rule_name('llvm_ir', compiler.for_machine) command = [ninja_quote(i) for i in compiler.get_exelist()] args = ['$ARGS'] + compiler.get_output_args('$out') + compiler.get_compile_only_args() + ['$in'] - description = 'Compiling LLVM IR object $in.' + description = 'Compiling LLVM IR object $in' self.add_rule(NinjaRule(rule, command, args, description, rspable=compiler.can_linker_accept_rsp())) self.created_llvm_ir_rule[compiler.for_machine] = True @@ -1691,7 +1697,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) command = [ninja_quote(i) for i in compiler.get_exelist()] args = ['$ARGS'] + quoted_depargs + compiler.get_output_args('$out') + compiler.get_compile_only_args() + ['$in'] - description = 'Compiling %s object $out.' % compiler.get_display_language() + description = 'Compiling %s object $out' % compiler.get_display_language() if isinstance(compiler, VisualStudioLikeCompiler): deps = 'msvc' depfile = None @@ -1718,7 +1724,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) else: output = compiler.get_output_args('$out') command = compiler.get_exelist() + ['$ARGS'] + quoted_depargs + output + compiler.get_compile_only_args() + ['$in'] - description = 'Precompiling header $in.' + description = 'Precompiling header $in' if isinstance(compiler, VisualStudioLikeCompiler): deps = 'msvc' depfile = None @@ -1946,6 +1952,9 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) else: return compiler.get_compile_debugfile_args(objfile, pch=False) + def get_link_debugfile_name(self, linker, target, outname): + return linker.get_link_debugfile_name(outname) + def get_link_debugfile_args(self, linker, target, outname): return linker.get_link_debugfile_args(outname) @@ -2300,12 +2309,17 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) self.add_build(elem) return pch_objects + def get_target_shsym_filename(self, target): + # Always name the .symbols file after the primary build output because it always exists + targetdir = self.get_target_private_dir(target) + return os.path.join(targetdir, target.get_filename() + '.symbols') + def generate_shsym(self, target): - target_name = target.get_filename() target_file = self.get_target_filename(target) - targetdir = self.get_target_private_dir(target) - symname = os.path.join(targetdir, target_name + '.symbols') + symname = self.get_target_shsym_filename(target) elem = NinjaBuildElement(self.all_outputs, symname, 'SHSYM', target_file) + # The library we will actually link to, which is an import library on Windows (not the DLL) + elem.add_item('IMPLIB', self.get_target_filename_for_linking(target)) if self.environment.is_cross_build(): elem.add_item('CROSS', '--cross-host=' + self.environment.machines[target.for_machine].system) self.add_build(elem) @@ -2318,6 +2332,9 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) return [] return linker.get_no_stdlib_link_args() + def get_import_filename(self, target): + return os.path.join(self.get_target_dir(target), target.import_filename) + def get_target_type_link_args(self, target, linker): commands = [] if isinstance(target, build.Executable): @@ -2328,7 +2345,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) commands += linker.gen_export_dynamic_link_args(self.environment) # If implib, and that's significant on this platform (i.e. Windows using either GCC or Visual Studio) if target.import_filename: - commands += linker.gen_import_library_args(os.path.join(self.get_target_dir(target), target.import_filename)) + commands += linker.gen_import_library_args(self.get_import_filename(target)) if target.pie: commands += linker.get_pie_link_args() elif isinstance(target, build.SharedLibrary): @@ -2349,7 +2366,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) commands += linker.gen_vs_module_defs_args(target.vs_module_defs.rel_to_builddir(self.build_to_src)) # This is only visited when building for Windows using either GCC or Visual Studio if target.import_filename: - commands += linker.gen_import_library_args(os.path.join(self.get_target_dir(target), target.import_filename)) + commands += linker.gen_import_library_args(self.get_import_filename(target)) elif isinstance(target, build.StaticLibrary): commands += linker.get_std_link_args() else: @@ -2448,6 +2465,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) def generate_link(self, target, outname, obj_list, linker, extra_args=None, stdlib_args=None): extra_args = extra_args if extra_args is not None else [] stdlib_args = stdlib_args if stdlib_args is not None else [] + implicit_outs = [] if isinstance(target, build.StaticLibrary): linker_base = 'STATIC' else: @@ -2483,6 +2501,9 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) # Add /DEBUG and the pdb filename when using MSVC if self.get_option_for_target('debug', target): commands += self.get_link_debugfile_args(linker, target, outname) + debugfile = self.get_link_debugfile_name(linker, target, outname) + if debugfile is not None: + implicit_outs += [debugfile] # Add link args specific to this BuildTarget type, such as soname args, # PIC, import library generation, etc. commands += self.get_target_type_link_args(target, linker) @@ -2571,14 +2592,14 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) dep_targets.extend([self.get_dependency_filename(t) for t in dependencies]) dep_targets.extend([self.get_dependency_filename(t) for t in target.link_depends]) - elem = NinjaBuildElement(self.all_outputs, outname, linker_rule, obj_list) + elem = NinjaBuildElement(self.all_outputs, outname, linker_rule, obj_list, implicit_outs=implicit_outs) elem.add_dep(dep_targets + custom_target_libraries) elem.add_item('LINK_ARGS', commands) return elem def get_dependency_filename(self, t): if isinstance(t, build.SharedLibrary): - return os.path.join(self.get_target_private_dir(t), t.get_filename() + '.symbols') + return self.get_target_shsym_filename(t) elif isinstance(t, mesonlib.File): if t.is_built: return t.relative_name() @@ -2607,7 +2628,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) d = CleanTrees(self.environment.get_build_dir(), trees) d_file = os.path.join(self.environment.get_scratch_dir(), 'cleantrees.dat') e.add_item('COMMAND', self.environment.get_build_command() + ['--internal', 'cleantrees', d_file]) - e.add_item('description', 'Cleaning custom target directories.') + e.add_item('description', 'Cleaning custom target directories') self.add_build(e) # Alias that runs the target defined above self.create_target_alias('meson-clean-ctlist') @@ -2621,7 +2642,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) script_root = self.environment.get_script_dir() clean_script = os.path.join(script_root, 'delwithsuffix.py') gcno_elem.add_item('COMMAND', mesonlib.python_command + [clean_script, '.', 'gcno']) - gcno_elem.add_item('description', 'Deleting gcno files.') + gcno_elem.add_item('description', 'Deleting gcno files') self.add_build(gcno_elem) # Alias that runs the target defined above self.create_target_alias('meson-clean-gcno') @@ -2630,7 +2651,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) script_root = self.environment.get_script_dir() clean_script = os.path.join(script_root, 'delwithsuffix.py') gcda_elem.add_item('COMMAND', mesonlib.python_command + [clean_script, '.', 'gcda']) - gcda_elem.add_item('description', 'Deleting gcda files.') + gcda_elem.add_item('description', 'Deleting gcda files') self.add_build(gcda_elem) # Alias that runs the target defined above self.create_target_alias('meson-clean-gcda') @@ -2741,7 +2762,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) elem = NinjaBuildElement(self.all_outputs, 'meson-clean', 'CUSTOM_COMMAND', 'PHONY') elem.add_item('COMMAND', [self.ninja_command, '-t', 'clean']) - elem.add_item('description', 'Cleaning.') + elem.add_item('description', 'Cleaning') # Alias that runs the above-defined meson-clean target self.create_target_alias('meson-clean') diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 2f1e104..37cf2e0 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -982,6 +982,9 @@ class Compiler: def get_compile_debugfile_args(self, rel_obj, **kwargs): return [] + def get_link_debugfile_name(self, targetfile: str) -> str: + return self.linker.get_debugfile_name(targetfile) + def get_link_debugfile_args(self, targetfile: str) -> T.List[str]: return self.linker.get_debugfile_args(targetfile) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index eb626b0..46bbea0 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -141,11 +141,11 @@ def find_coverage_tools(): return gcovr_exe, gcovr_new_rootdir, lcov_exe, genhtml_exe -def detect_ninja(version: str = '1.5', log: bool = False) -> str: +def detect_ninja(version: str = '1.7', log: bool = False) -> str: r = detect_ninja_command_and_version(version, log) return r[0] if r else None -def detect_ninja_command_and_version(version: str = '1.5', log: bool = False) -> (str, str): +def detect_ninja_command_and_version(version: str = '1.7', log: bool = False) -> (str, str): env_ninja = os.environ.get('NINJA', None) for n in [env_ninja] if env_ninja else ['ninja', 'ninja-build', 'samu']: try: diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py index 84eb359..489525b 100644 --- a/mesonbuild/linkers.py +++ b/mesonbuild/linkers.py @@ -75,6 +75,9 @@ class StaticLinker: def native_args_to_unix(cls, args: T.List[str]) -> T.List[str]: return args[:] + def get_link_debugfile_name(self, targetfile: str) -> str: + return None + def get_link_debugfile_args(self, targetfile: str) -> T.List[str]: # Static libraries do not have PDB files return [] @@ -305,6 +308,10 @@ class DynamicLinker(metaclass=abc.ABCMeta): m = 'Language {} does not support has_multi_link_arguments.' raise mesonlib.EnvironmentException(m.format(self.id)) + def get_debugfile_name(self, targetfile: str) -> str: + '''Name of debug file written out (see below)''' + return None + def get_debugfile_args(self, targetfile: str) -> T.List[str]: """Some compilers (MSVC) write debug into a separate file. @@ -842,10 +849,12 @@ class VisualStudioLikeLinkerMixin: def get_std_shared_lib_args(self) -> T.List[str]: return self._apply_prefix('/DLL') + def get_debugfile_name(self, targetfile: str) -> str: + basename = targetfile.rsplit('.', maxsplit=1)[0] + return basename + '.pdb' + def get_debugfile_args(self, targetfile: str) -> T.List[str]: - pdbarr = targetfile.split('.')[:-1] - pdbarr += ['pdb'] - return self._apply_prefix(['/DEBUG', '/PDB:' + '.'.join(pdbarr)]) + return self._apply_prefix(['/DEBUG', '/PDB:' + self.get_debugfile_name(targetfile)]) def get_link_whole_for(self, args: T.List[str]) -> T.List[str]: # Only since VS2015 diff --git a/mesonbuild/scripts/symbolextractor.py b/mesonbuild/scripts/symbolextractor.py index 410cb33..65b2189 100644 --- a/mesonbuild/scripts/symbolextractor.py +++ b/mesonbuild/scripts/symbolextractor.py @@ -20,8 +20,10 @@ # This file is basically a reimplementation of # http://cgit.freedesktop.org/libreoffice/core/commit/?id=3213cd54b76bc80a6f0516aac75a48ff3b2ad67c +import typing as T import os, sys from .. import mesonlib +from .. import mlog from ..mesonlib import Popen_safe import argparse @@ -31,12 +33,15 @@ parser.add_argument('--cross-host', default=None, dest='cross_host', help='cross compilation host platform') parser.add_argument('args', nargs='+') -def dummy_syms(outfilename): +TOOL_WARNING_FILE = None +RELINKING_WARNING = 'Relinking will always happen on source changes.' + +def dummy_syms(outfilename: str): """Just touch it so relinking happens always.""" with open(outfilename, 'w'): pass -def write_if_changed(text, outfilename): +def write_if_changed(text: str, outfilename: str): try: with open(outfilename, 'r') as f: oldtext = f.read() @@ -47,27 +52,62 @@ def write_if_changed(text, outfilename): with open(outfilename, 'w') as f: f.write(text) -def linux_syms(libfilename, outfilename): - evar = 'READELF' - if evar in os.environ: - readelfbin = os.environ[evar].strip() - else: - readelfbin = 'readelf' - evar = 'NM' +def print_tool_warning(tool: list, msg: str, stderr: str = None): + global TOOL_WARNING_FILE + if os.path.exists(TOOL_WARNING_FILE): + return + if len(tool) == 1: + tool = tool[0] + m = '{!r} {}. {}'.format(tool, msg, RELINKING_WARNING) + if stderr: + m += '\n' + stderr + mlog.warning(m) + # Write it out so we don't warn again + with open(TOOL_WARNING_FILE, 'w'): + pass + +def get_tool(name: str) -> T.List[str]: + evar = name.upper() if evar in os.environ: - nmbin = os.environ[evar].strip() - else: - nmbin = 'nm' - pe, output = Popen_safe([readelfbin, '-d', libfilename])[0:2] - if pe.returncode != 0: - raise RuntimeError('Readelf does not work') + import shlex + return shlex.split(os.environ[evar]) + return [name] + +def call_tool(name: str, args: T.List[str], **kwargs) -> str: + tool = get_tool(name) + try: + p, output, e = Popen_safe(tool + args, **kwargs) + except FileNotFoundError: + print_tool_warning(tool, 'not found') + return None + if p.returncode != 0: + print_tool_warning(tool, 'does not work', e) + return None + return output + +def call_tool_nowarn(tool: T.List[str], **kwargs) -> T.Tuple[str, str]: + try: + p, output, e = Popen_safe(tool, **kwargs) + except FileNotFoundError: + return None, '{!r} not found\n'.format(tool[0]) + if p.returncode != 0: + return None, e + return output, None + +def linux_syms(libfilename: str, outfilename: str): + # Get the name of the library + output = call_tool('readelf', ['-d', libfilename]) + if not output: + dummy_syms(outfilename) + return result = [x for x in output.split('\n') if 'SONAME' in x] assert(len(result) <= 1) - pnm, output = Popen_safe([nmbin, '--dynamic', '--extern-only', - '--defined-only', '--format=posix', - libfilename])[0:2] - if pnm.returncode != 0: - raise RuntimeError('nm does not work.') + # Get a list of all symbols exported + output = call_tool('nm', ['--dynamic', '--extern-only', '--defined-only', + '--format=posix', libfilename]) + if not output: + dummy_syms(outfilename) + return for line in output.split('\n'): if not line: continue @@ -78,44 +118,157 @@ def linux_syms(libfilename, outfilename): result += [' '.join(entry)] write_if_changed('\n'.join(result) + '\n', outfilename) -def osx_syms(libfilename, outfilename): - pe, output = Popen_safe(['otool', '-l', libfilename])[0:2] - if pe.returncode != 0: - raise RuntimeError('Otool does not work.') +def osx_syms(libfilename: str, outfilename: str): + # Get the name of the library + output = call_tool('otool', ['-l', libfilename]) + if not output: + dummy_syms(outfilename) + return arr = output.split('\n') for (i, val) in enumerate(arr): if 'LC_ID_DYLIB' in val: match = i break result = [arr[match + 2], arr[match + 5]] # Libreoffice stores all 5 lines but the others seem irrelevant. - pnm, output = Popen_safe(['nm', '-g', '-P', libfilename])[0:2] - if pnm.returncode != 0: - raise RuntimeError('nm does not work.') - result += [' '.join(x.split()[0:2]) for x in output.split('\n') if x and not x.endswith('U')] + # Get a list of all symbols exported + output = call_tool('nm', ['--extern-only', '--defined-only', + '--format=posix', libfilename]) + if not output: + dummy_syms(outfilename) + return + result += [' '.join(x.split()[0:2]) for x in output.split('\n')] + write_if_changed('\n'.join(result) + '\n', outfilename) + +def cygwin_syms(impfilename: str, outfilename: str): + # Get the name of the library + output = call_tool('dlltool', ['-I', impfilename]) + if not output: + dummy_syms(outfilename) + return + result = [output] + # Get the list of all symbols exported + output = call_tool('nm', ['--extern-only', '--defined-only', + '--format=posix', impfilename]) + if not output: + dummy_syms(outfilename) + return + for line in output.split('\n'): + if ' T ' not in line: + continue + result.append(line.split(maxsplit=1)[0]) + write_if_changed('\n'.join(result) + '\n', outfilename) + +def _get_implib_dllname(impfilename: str) -> T.Tuple[T.List[str], str]: + all_stderr = '' + # First try lib.exe, which is provided by MSVC. Then llvm-lib.exe, by LLVM + # for clang-cl. + # + # We cannot call get_tool on `lib` because it will look at the `LIB` env + # var which is the list of library paths MSVC will search for import + # libraries while linking. + for lib in (['lib'], get_tool('llvm-lib')): + output, e = call_tool_nowarn(lib + ['-list', impfilename]) + if output: + # The output is a list of DLLs that each symbol exported by the import + # library is available in. We only build import libraries that point to + # a single DLL, so we can pick any of these. Pick the last one for + # simplicity. Also skip the last line, which is empty. + return output.split('\n')[-2:-1], None + all_stderr += e + # Next, try dlltool.exe which is provided by MinGW + output, e = call_tool_nowarn(get_tool('dlltool') + ['-I', impfilename]) + if output: + return [output], None + all_stderr += e + return ([], all_stderr) + +def _get_implib_exports(impfilename: str) -> T.Tuple[T.List[str], str]: + all_stderr = '' + # Force dumpbin.exe to use en-US so we can parse its output + env = os.environ.copy() + env['VSLANG'] = '1033' + output, e = call_tool_nowarn(get_tool('dumpbin') + ['-exports', impfilename], env=env) + if output: + lines = output.split('\n') + start = lines.index('File Type: LIBRARY') + end = lines.index(' Summary') + return lines[start:end], None + all_stderr += e + # Next, try llvm-nm.exe provided by LLVM, then nm.exe provided by MinGW + for nm in ('llvm-nm', 'nm'): + output, e = call_tool_nowarn(get_tool(nm) + ['--extern-only', '--defined-only', + '--format=posix', impfilename]) + if output: + result = [] + for line in output.split('\n'): + if ' T ' not in line or line.startswith('.text'): + continue + result.append(line.split(maxsplit=1)[0]) + return result, None + all_stderr += e + return ([], all_stderr) + +def windows_syms(impfilename: str, outfilename: str): + # Get the name of the library + result, e = _get_implib_dllname(impfilename) + if not result: + print_tool_warning('lib, llvm-lib, dlltool', 'do not work or were not found', e) + dummy_syms(outfilename) + return + # Get a list of all symbols exported + symbols, e = _get_implib_exports(impfilename) + if not symbols: + print_tool_warning('dumpbin, llvm-nm, nm', 'do not work or were not found', e) + dummy_syms(outfilename) + return + result += symbols write_if_changed('\n'.join(result) + '\n', outfilename) -def gen_symbols(libfilename, outfilename, cross_host): +def gen_symbols(libfilename: str, impfilename: str, outfilename: str, cross_host: str): if cross_host is not None: - # In case of cross builds just always relink. - # In theory we could determine the correct - # toolset but there are more important things - # to do. + # In case of cross builds just always relink. In theory we could + # determine the correct toolset, but we would need to use the correct + # `nm`, `readelf`, etc, from the cross info which requires refactoring. dummy_syms(outfilename) elif mesonlib.is_linux(): linux_syms(libfilename, outfilename) elif mesonlib.is_osx(): osx_syms(libfilename, outfilename) + elif mesonlib.is_windows(): + if os.path.isfile(impfilename): + windows_syms(impfilename, outfilename) + else: + # No import library. Not sure how the DLL is being used, so just + # rebuild everything that links to it every time. + dummy_syms(outfilename) + elif mesonlib.is_cygwin(): + if os.path.isfile(impfilename): + cygwin_syms(impfilename, outfilename) + else: + # No import library. Not sure how the DLL is being used, so just + # rebuild everything that links to it every time. + dummy_syms(outfilename) else: + if not os.path.exists(TOOL_WARNING_FILE): + mlog.warning('Symbol extracting has not been implemented for this ' + 'platform. ' + RELINKING_WARNING) + # Write it out so we don't warn again + with open(TOOL_WARNING_FILE, 'w'): + pass dummy_syms(outfilename) def run(args): + global TOOL_WARNING_FILE options = parser.parse_args(args) - if len(options.args) != 2: - print('symbolextractor.py <shared library file> <output file>') + if len(options.args) != 4: + print('symbolextractor.py <shared library file> <import library> <output file>') sys.exit(1) - libfile = options.args[0] - outfile = options.args[1] - gen_symbols(libfile, outfile, options.cross_host) + privdir = os.path.join(options.args[0], 'meson-private') + TOOL_WARNING_FILE = os.path.join(privdir, 'symbolextractor_tool_warning_printed') + libfile = options.args[1] + impfile = options.args[2] # Only used on Windows + outfile = options.args[3] + gen_symbols(libfile, impfile, outfile, options.cross_host) return 0 if __name__ == '__main__': diff --git a/mesonbuild/templates/cpptemplates.py b/mesonbuild/templates/cpptemplates.py index 5bff67b..f664e42 100644 --- a/mesonbuild/templates/cpptemplates.py +++ b/mesonbuild/templates/cpptemplates.py @@ -153,6 +153,7 @@ def create_lib_cpp_sample(project_name, version): lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower()) uppercase_token = lowercase_token.upper() class_name = uppercase_token[0] + lowercase_token[1:] + test_exe_name = lowercase_token + '_test' namespace = lowercase_token lib_hpp_name = lowercase_token + '.hpp' lib_cpp_name = lowercase_token + '.cpp' @@ -165,7 +166,7 @@ def create_lib_cpp_sample(project_name, version): 'header_file': lib_hpp_name, 'source_file': lib_cpp_name, 'test_source_file': test_cpp_name, - 'test_exe_name': lowercase_token, + 'test_exe_name': test_exe_name, 'project_name': project_name, 'lib_name': lowercase_token, 'test_name': lowercase_token, diff --git a/mesonbuild/templates/ctemplates.py b/mesonbuild/templates/ctemplates.py index f46f054..64686c8 100644 --- a/mesonbuild/templates/ctemplates.py +++ b/mesonbuild/templates/ctemplates.py @@ -134,6 +134,7 @@ def create_lib_c_sample(project_name, version): lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower()) uppercase_token = lowercase_token.upper() function_name = lowercase_token[0:3] + '_func' + test_exe_name = lowercase_token + '_test' lib_h_name = lowercase_token + '.h' lib_c_name = lowercase_token + '.c' test_c_name = lowercase_token + '_test.c' @@ -144,7 +145,7 @@ def create_lib_c_sample(project_name, version): 'header_file': lib_h_name, 'source_file': lib_c_name, 'test_source_file': test_c_name, - 'test_exe_name': lowercase_token, + 'test_exe_name': test_exe_name, 'project_name': project_name, 'lib_name': lowercase_token, 'test_name': lowercase_token, diff --git a/mesonbuild/templates/cudatemplates.py b/mesonbuild/templates/cudatemplates.py index d083fe8..cc0782c 100644 --- a/mesonbuild/templates/cudatemplates.py +++ b/mesonbuild/templates/cudatemplates.py @@ -153,6 +153,7 @@ def create_lib_cuda_sample(project_name, version): lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower()) uppercase_token = lowercase_token.upper() class_name = uppercase_token[0] + lowercase_token[1:] + test_exe_name = lowercase_token + '_test' namespace = lowercase_token lib_h_name = lowercase_token + '.h' lib_cuda_name = lowercase_token + '.cu' @@ -165,7 +166,7 @@ def create_lib_cuda_sample(project_name, version): 'header_file': lib_h_name, 'source_file': lib_cuda_name, 'test_source_file': test_cuda_name, - 'test_exe_name': lowercase_token, + 'test_exe_name': test_exe_name, 'project_name': project_name, 'lib_name': lowercase_token, 'test_name': lowercase_token, diff --git a/mesonbuild/templates/dlangtemplates.py b/mesonbuild/templates/dlangtemplates.py index 124634c..265e3d5 100644 --- a/mesonbuild/templates/dlangtemplates.py +++ b/mesonbuild/templates/dlangtemplates.py @@ -113,6 +113,7 @@ def create_lib_d_sample(project_name, version): lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower()) uppercase_token = lowercase_token.upper() function_name = lowercase_token[0:3] + '_func' + test_exe_name = lowercase_token + '_test' lib_m_name = lowercase_token lib_d_name = lowercase_token + '.d' test_d_name = lowercase_token + '_test.d' @@ -123,7 +124,7 @@ def create_lib_d_sample(project_name, version): 'module_file': lib_m_name, 'source_file': lib_d_name, 'test_source_file': test_d_name, - 'test_exe_name': lowercase_token, + 'test_exe_name': test_exe_name, 'project_name': project_name, 'lib_name': lowercase_token, 'test_name': lowercase_token, diff --git a/mesonbuild/templates/fortrantemplates.py b/mesonbuild/templates/fortrantemplates.py index 3bf1b74..b784fda 100644 --- a/mesonbuild/templates/fortrantemplates.py +++ b/mesonbuild/templates/fortrantemplates.py @@ -111,6 +111,7 @@ def create_lib_fortran_sample(project_name, version): lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower()) uppercase_token = lowercase_token.upper() function_name = lowercase_token[0:3] + '_func' + test_exe_name = lowercase_token + '_test' lib_fortran_name = lowercase_token + '.f90' test_fortran_name = lowercase_token + '_test.f90' kwargs = {'utoken': uppercase_token, @@ -119,7 +120,7 @@ def create_lib_fortran_sample(project_name, version): 'function_name': function_name, 'source_file': lib_fortran_name, 'test_source_file': test_fortran_name, - 'test_exe_name': lowercase_token, + 'test_exe_name': test_exe_name, 'project_name': project_name, 'lib_name': lowercase_token, 'test_name': lowercase_token, diff --git a/mesonbuild/templates/javatemplates.py b/mesonbuild/templates/javatemplates.py index e8a8c15..012823a 100644 --- a/mesonbuild/templates/javatemplates.py +++ b/mesonbuild/templates/javatemplates.py @@ -118,7 +118,6 @@ def create_lib_java_sample(project_name, version): 'class_name': class_name, 'source_file': lib_java_name, 'test_source_file': test_java_name, - 'test_exe_name': lowercase_token, 'project_name': project_name, 'lib_name': lowercase_token, 'test_name': lowercase_token, diff --git a/mesonbuild/templates/objcpptemplates.py b/mesonbuild/templates/objcpptemplates.py index 329a568..2d71573 100644 --- a/mesonbuild/templates/objcpptemplates.py +++ b/mesonbuild/templates/objcpptemplates.py @@ -134,6 +134,7 @@ def create_lib_objcpp_sample(project_name, version): lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower()) uppercase_token = lowercase_token.upper() function_name = lowercase_token[0:3] + '_func' + test_exe_name = lowercase_token + '_test' lib_h_name = lowercase_token + '.h' lib_objcpp_name = lowercase_token + '.mm' test_objcpp_name = lowercase_token + '_test.mm' @@ -144,7 +145,7 @@ def create_lib_objcpp_sample(project_name, version): 'header_file': lib_h_name, 'source_file': lib_objcpp_name, 'test_source_file': test_objcpp_name, - 'test_exe_name': lowercase_token, + 'test_exe_name': test_exe_name, 'project_name': project_name, 'lib_name': lowercase_token, 'test_name': lowercase_token, diff --git a/mesonbuild/templates/objctemplates.py b/mesonbuild/templates/objctemplates.py index db89c28..73791f5 100644 --- a/mesonbuild/templates/objctemplates.py +++ b/mesonbuild/templates/objctemplates.py @@ -134,6 +134,7 @@ def create_lib_objc_sample(project_name, version): lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower()) uppercase_token = lowercase_token.upper() function_name = lowercase_token[0:3] + '_func' + test_exe_name = lowercase_token + '_test' lib_h_name = lowercase_token + '.h' lib_objc_name = lowercase_token + '.m' test_objc_name = lowercase_token + '_test.m' @@ -144,7 +145,7 @@ def create_lib_objc_sample(project_name, version): 'header_file': lib_h_name, 'source_file': lib_objc_name, 'test_source_file': test_objc_name, - 'test_exe_name': lowercase_token, + 'test_exe_name': test_exe_name, 'project_name': project_name, 'lib_name': lowercase_token, 'test_name': lowercase_token, diff --git a/mesonbuild/templates/rusttemplates.py b/mesonbuild/templates/rusttemplates.py index 848dfc0..ab8ecbd 100644 --- a/mesonbuild/templates/rusttemplates.py +++ b/mesonbuild/templates/rusttemplates.py @@ -82,6 +82,7 @@ def create_lib_rust_sample(project_name, version): lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower()) uppercase_token = lowercase_token.upper() function_name = lowercase_token[0:3] + '_func' + test_exe_name = lowercase_token + '_test' lib_crate_name = lowercase_token lib_rs_name = lowercase_token + '.rs' test_rs_name = lowercase_token + '_test.rs' @@ -92,7 +93,7 @@ def create_lib_rust_sample(project_name, version): 'crate_file': lib_crate_name, 'source_file': lib_rs_name, 'test_source_file': test_rs_name, - 'test_exe_name': lowercase_token, + 'test_exe_name': test_exe_name, 'project_name': project_name, 'lib_name': lowercase_token, 'test_name': lowercase_token, diff --git a/run_tests.py b/run_tests.py index 535c792..3237e85 100755 --- a/run_tests.py +++ b/run_tests.py @@ -45,17 +45,15 @@ if 'CI' in os.environ: NINJA_CMD = 'ninja' else: # Look for 1.9 to see if https://github.com/ninja-build/ninja/issues/1219 - # is fixed, else require 1.6 for -w dupbuild=err - for v in ('1.9', '1.6'): - NINJA_CMD = detect_ninja(v) - if NINJA_CMD is not None: - if mesonlib.version_compare(v, '>=1.9'): - NINJA_1_9_OR_NEWER = True - else: - mlog.warning('Found ninja <1.9, tests will run slower', once=True) - break + # is fixed + NINJA_CMD = detect_ninja('1.9') + if NINJA_CMD is not None: + NINJA_1_9_OR_NEWER = True + else: + mlog.warning('Found ninja <1.9, tests will run slower', once=True) + NINJA_CMD = detect_ninja() if NINJA_CMD is None: - raise RuntimeError('Could not find Ninja v1.6 or newer') + raise RuntimeError('Could not find Ninja v1.7 or newer') def guess_backend(backend, msbuild_exe: str): # Auto-detect backend if unspecified diff --git a/run_unittests.py b/run_unittests.py index 033647c..aa27a1d 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -809,6 +809,7 @@ class InternalTests(unittest.TestCase): env.machines.host.system = 'windows' self._test_all_naming(cc, env, patterns, 'windows-mingw') + @skipIfNoPkgconfig def test_pkgconfig_parse_libs(self): ''' Unit test for parsing of pkg-config output to search for libraries @@ -1700,20 +1701,43 @@ class BasePlatformTests(unittest.TestCase): path_basename = PurePath(path).parts[-1] self.assertEqual(PurePath(path_basename), PurePath(basename), msg) + def assertReconfiguredBuildIsNoop(self): + 'Assert that we reconfigured and then there was nothing to do' + ret = self.build() + self.assertIn('The Meson build system', ret) + if self.backend is Backend.ninja: + for line in ret.split('\n'): + if line in self.no_rebuild_stdout: + break + else: + raise AssertionError('build was reconfigured, but was not no-op') + elif self.backend is Backend.vs: + # Ensure that some target said that no rebuild was done + # XXX: Note CustomBuild did indeed rebuild, because of the regen checker! + self.assertIn('ClCompile:\n All outputs are up-to-date.', ret) + self.assertIn('Link:\n All outputs are up-to-date.', ret) + # Ensure that no targets were built + self.assertNotRegex(ret, re.compile('ClCompile:\n [^\n]*cl', flags=re.IGNORECASE)) + self.assertNotRegex(ret, re.compile('Link:\n [^\n]*link', flags=re.IGNORECASE)) + elif self.backend is Backend.xcode: + raise unittest.SkipTest('Please help us fix this test on the xcode backend') + else: + raise RuntimeError('Invalid backend: {!r}'.format(self.backend.name)) + def assertBuildIsNoop(self): ret = self.build() if self.backend is Backend.ninja: self.assertIn(ret.split('\n')[-2], self.no_rebuild_stdout) elif self.backend is Backend.vs: - # Ensure that some target said that no rebuild was done + # Ensure that some target of each type said that no rebuild was done + # We always have at least one CustomBuild target for the regen checker self.assertIn('CustomBuild:\n All outputs are up-to-date.', ret) self.assertIn('ClCompile:\n All outputs are up-to-date.', ret) self.assertIn('Link:\n All outputs are up-to-date.', ret) # Ensure that no targets were built - clre = re.compile('ClCompile:\n [^\n]*cl', flags=re.IGNORECASE) - linkre = re.compile('Link:\n [^\n]*link', flags=re.IGNORECASE) - self.assertNotRegex(ret, clre) - self.assertNotRegex(ret, linkre) + self.assertNotRegex(ret, re.compile('CustomBuild:\n [^\n]*cl', flags=re.IGNORECASE)) + self.assertNotRegex(ret, re.compile('ClCompile:\n [^\n]*cl', flags=re.IGNORECASE)) + self.assertNotRegex(ret, re.compile('Link:\n [^\n]*link', flags=re.IGNORECASE)) elif self.backend is Backend.xcode: raise unittest.SkipTest('Please help us fix this test on the xcode backend') else: @@ -1732,6 +1756,33 @@ class BasePlatformTests(unittest.TestCase): else: raise RuntimeError('Invalid backend: {!r}'.format(self.backend.name)) + @staticmethod + def get_target_from_filename(filename): + base = os.path.splitext(filename)[0] + if base.startswith(('lib', 'cyg')): + return base[3:] + return base + + def assertBuildRelinkedOnlyTarget(self, target): + ret = self.build() + if self.backend is Backend.ninja: + linked_targets = [] + for line in ret.split('\n'): + if 'Linking target' in line: + fname = line.rsplit('target ')[-1] + linked_targets.append(self.get_target_from_filename(fname)) + self.assertEqual(linked_targets, [target]) + elif self.backend is Backend.vs: + # Ensure that this target was rebuilt + linkre = re.compile(r'Link:\n [^\n]*link.exe[^\n]*/OUT:".\\([^"]*)"', flags=re.IGNORECASE) + matches = linkre.findall(ret) + self.assertEqual(len(matches), 1, msg=matches) + self.assertEqual(self.get_target_from_filename(matches[0]), target) + elif self.backend is Backend.xcode: + raise unittest.SkipTest('Please help us fix this test on the xcode backend') + else: + raise RuntimeError('Invalid backend: {!r}'.format(self.backend.name)) + def assertPathExists(self, path): m = 'Path {!r} should exist'.format(path) self.assertTrue(os.path.exists(path), msg=m) @@ -2535,6 +2586,23 @@ class AllPlatformTests(BasePlatformTests): meson_exe_dat2 = glob(os.path.join(self.privatedir, 'meson_exe*.dat')) self.assertListEqual(meson_exe_dat1, meson_exe_dat2) + def test_noop_changes_cause_no_rebuilds(self): + ''' + Test that no-op changes to the build files such as mtime do not cause + a rebuild of anything. + ''' + testdir = os.path.join(self.common_test_dir, '6 linkshared') + self.init(testdir) + self.build() + # Immediately rebuilding should not do anything + self.assertBuildIsNoop() + # Changing mtime of meson.build should not rebuild anything + self.utime(os.path.join(testdir, 'meson.build')) + self.assertReconfiguredBuildIsNoop() + # Changing mtime of libefile.c should rebuild the library, but not relink the executable + self.utime(os.path.join(testdir, 'libfile.c')) + self.assertBuildRelinkedOnlyTarget('mylib') + def test_source_changes_cause_rebuild(self): ''' Test that changes to sources and headers cause rebuilds, but not @@ -2548,7 +2616,7 @@ class AllPlatformTests(BasePlatformTests): self.assertBuildIsNoop() # Changing mtime of header.h should rebuild everything self.utime(os.path.join(testdir, 'header.h')) - self.assertRebuiltTarget('prog') + self.assertBuildRelinkedOnlyTarget('prog') def test_custom_target_changes_cause_rebuild(self): ''' @@ -2564,7 +2632,7 @@ class AllPlatformTests(BasePlatformTests): # Changing mtime of these should rebuild everything for f in ('input.def', 'makeheader.py', 'somefile.txt'): self.utime(os.path.join(testdir, f)) - self.assertRebuiltTarget('prog') + self.assertBuildRelinkedOnlyTarget('prog') def test_source_generator_program_cause_rebuild(self): ''' diff --git a/test cases/common/149 recursive linking/3rdorderdeps/meson.build b/test cases/common/149 recursive linking/3rdorderdeps/meson.build index d4ef745..4c5ac73 100644 --- a/test cases/common/149 recursive linking/3rdorderdeps/meson.build +++ b/test cases/common/149 recursive linking/3rdorderdeps/meson.build @@ -41,7 +41,7 @@ foreach dep2 : ['sh', 'st'] main_c = configure_file(input : 'main.c.in', output : name + '-main.c', configuration : cdata) - dep3_bin = executable(name, main_c, link_with : dep3_lib, + dep3_bin = executable(name + '_test', main_c, link_with : dep3_lib, c_args : build_args) test(name + 'test', dep3_bin) endforeach |