ninja backend: simplify generation of phony targets

Every phony target has a special indirection rule created because ninja
is bad at deleting generated outputs and tries to delete phony outputs
too.

Instead of invoking this as a separate helper post-creation function to
create the alias, wrap NinjaBuildElement and create it behind the
scenes. This simplifies target naming and means one less line at every
single use site.
This commit is contained in:
Eli Schwartz 2022-05-26 22:33:52 -04:00
parent 40c69630df
commit dd2f1c4c57
No known key found for this signature in database
GPG Key ID: CEB167EFB5722BD6
1 changed files with 33 additions and 64 deletions

View File

@ -434,18 +434,23 @@ class NinjaBackend(backends.Backend):
self.introspection_data = {}
self.created_llvm_ir_rule = PerMachine(False, False)
def create_target_alias(self, to_target):
# We need to use aliases for targets that might be used as directory
# names to workaround a Ninja bug that breaks `ninja -t clean`.
# This is used for 'reserved' targets such as 'test', 'install',
# 'benchmark', etc, and also for RunTargets.
# https://github.com/mesonbuild/meson/issues/1644
if not to_target.startswith('meson-'):
raise AssertionError(f'Invalid usage of create_target_alias with {to_target!r}')
from_target = to_target[len('meson-'):]
elem = NinjaBuildElement(self.all_outputs, from_target, 'phony', to_target)
def create_phony_target(self, all_outputs, dummy_outfile, rulename, phony_infilename, implicit_outs=None):
'''
We need to use aliases for targets that might be used as directory
names to workaround a Ninja bug that breaks `ninja -t clean`.
This is used for 'reserved' targets such as 'test', 'install',
'benchmark', etc, and also for RunTargets.
https://github.com/mesonbuild/meson/issues/1644
'''
if dummy_outfile.startswith('meson-'):
raise AssertionError(f'Invalid usage of create_phony_target with {dummy_outfile!r}')
to_name = f'meson-{dummy_outfile}'
elem = NinjaBuildElement(all_outputs, dummy_outfile, 'phony', to_name)
self.add_build(elem)
return NinjaBuildElement(all_outputs, to_name, rulename, phony_infilename, implicit_outs)
def detect_vs_dep_prefix(self, tempfilename):
'''VS writes its dependency in a locale dependent format.
Detect the search prefix to use.'''
@ -1050,13 +1055,10 @@ class NinjaBackend(backends.Backend):
env=target_env,
verbose=True)
cmd_type = f' (wrapped by meson {reason})' if reason else ''
internal_target_name = f'meson-{target_name}'
elem = NinjaBuildElement(self.all_outputs, internal_target_name, 'CUSTOM_COMMAND', [])
elem = self.create_phony_target(self.all_outputs, target_name, 'CUSTOM_COMMAND', [])
elem.add_item('COMMAND', meson_exe_cmd)
elem.add_item('description', f'Running external command {target.name}{cmd_type}')
elem.add_item('pool', 'console')
# Alias that runs the target defined above with the name the user specified
self.create_target_alias(internal_target_name)
deps = self.unwrap_dep_list(target)
deps += self.get_custom_target_depend_files(target)
elem.add_dep(deps)
@ -1084,55 +1086,43 @@ class NinjaBackend(backends.Backend):
(['--use_llvm_cov'] if use_llvm_cov else []))
def generate_coverage_rules(self, gcovr_exe: T.Optional[str], gcovr_version: T.Optional[str]):
e = NinjaBuildElement(self.all_outputs, 'meson-coverage', 'CUSTOM_COMMAND', 'PHONY')
e = self.create_phony_target(self.all_outputs, 'coverage', 'CUSTOM_COMMAND', 'PHONY')
self.generate_coverage_command(e, [])
e.add_item('description', 'Generates coverage reports')
self.add_build(e)
# Alias that runs the target defined above
self.create_target_alias('meson-coverage')
self.generate_coverage_legacy_rules(gcovr_exe, gcovr_version)
def generate_coverage_legacy_rules(self, gcovr_exe: T.Optional[str], gcovr_version: T.Optional[str]):
e = NinjaBuildElement(self.all_outputs, 'meson-coverage-html', 'CUSTOM_COMMAND', 'PHONY')
e = self.create_phony_target(self.all_outputs, 'coverage-html', 'CUSTOM_COMMAND', 'PHONY')
self.generate_coverage_command(e, ['--html'])
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')
if gcovr_exe:
e = NinjaBuildElement(self.all_outputs, 'meson-coverage-xml', 'CUSTOM_COMMAND', 'PHONY')
e = self.create_phony_target(self.all_outputs, 'coverage-xml', 'CUSTOM_COMMAND', 'PHONY')
self.generate_coverage_command(e, ['--xml'])
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')
e = self.create_phony_target(self.all_outputs, 'coverage-text', 'CUSTOM_COMMAND', 'PHONY')
self.generate_coverage_command(e, ['--text'])
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')
if mesonlib.version_compare(gcovr_version, '>=4.2'):
e = NinjaBuildElement(self.all_outputs, 'meson-coverage-sonarqube', 'CUSTOM_COMMAND', 'PHONY')
e = self.create_phony_target(self.all_outputs, 'coverage-sonarqube', 'CUSTOM_COMMAND', 'PHONY')
self.generate_coverage_command(e, ['--sonarqube'])
e.add_item('description', 'Generates Sonarqube XML coverage report')
self.add_build(e)
# Alias that runs the target defined above
self.create_target_alias('meson-coverage-sonarqube')
def generate_install(self):
self.create_install_data_files()
elem = NinjaBuildElement(self.all_outputs, 'meson-install', 'CUSTOM_COMMAND', 'PHONY')
elem = self.create_phony_target(self.all_outputs, 'install', 'CUSTOM_COMMAND', 'PHONY')
elem.add_dep('all')
elem.add_item('DESC', 'Installing files.')
elem.add_item('COMMAND', self.environment.get_build_command() + ['install', '--no-rebuild'])
elem.add_item('pool', 'console')
self.add_build(elem)
# Alias that runs the target defined above
self.create_target_alias('meson-install')
def generate_tests(self):
self.serialize_tests()
@ -1141,25 +1131,21 @@ class NinjaBackend(backends.Backend):
cmd += ['--no-stdsplit']
if self.environment.coredata.get_option(OptionKey('errorlogs')):
cmd += ['--print-errorlogs']
elem = NinjaBuildElement(self.all_outputs, 'meson-test', 'CUSTOM_COMMAND', ['all', 'PHONY'])
elem = self.create_phony_target(self.all_outputs, 'test', 'CUSTOM_COMMAND', ['all', 'PHONY'])
elem.add_item('COMMAND', cmd)
elem.add_item('DESC', 'Running all tests.')
elem.add_item('pool', 'console')
self.add_build(elem)
# Alias that runs the above-defined meson-test target
self.create_target_alias('meson-test')
# And then benchmarks.
cmd = self.environment.get_build_command(True) + [
'test', '--benchmark', '--logbase',
'benchmarklog', '--num-processes=1', '--no-rebuild']
elem = NinjaBuildElement(self.all_outputs, 'meson-benchmark', 'CUSTOM_COMMAND', ['all', 'PHONY'])
elem = self.create_phony_target(self.all_outputs, 'benchmark', 'CUSTOM_COMMAND', ['all', 'PHONY'])
elem.add_item('COMMAND', cmd)
elem.add_item('DESC', 'Running benchmark suite.')
elem.add_item('pool', 'console')
self.add_build(elem)
# Alias that runs the above-defined meson-benchmark target
self.create_target_alias('meson-benchmark')
def generate_rules(self):
self.rules = []
@ -3170,33 +3156,27 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
mlog.debug("Library versioning disabled because we do not have symlink creation privileges.")
def generate_custom_target_clean(self, trees: T.List[str]) -> str:
e = NinjaBuildElement(self.all_outputs, 'meson-clean-ctlist', 'CUSTOM_COMMAND', 'PHONY')
e = self.create_phony_target(self.all_outputs, 'clean-ctlist', 'CUSTOM_COMMAND', 'PHONY')
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')
self.add_build(e)
# Alias that runs the target defined above
self.create_target_alias('meson-clean-ctlist')
# Write out the data file passed to the script
with open(d_file, 'wb') as ofile:
pickle.dump(d, ofile)
return 'clean-ctlist'
def generate_gcov_clean(self):
gcno_elem = NinjaBuildElement(self.all_outputs, 'meson-clean-gcno', 'CUSTOM_COMMAND', 'PHONY')
gcno_elem = self.create_phony_target(self.all_outputs, 'clean-gcno', 'CUSTOM_COMMAND', 'PHONY')
gcno_elem.add_item('COMMAND', mesonlib.get_meson_command() + ['--internal', 'delwithsuffix', '.', 'gcno'])
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')
gcda_elem = NinjaBuildElement(self.all_outputs, 'meson-clean-gcda', 'CUSTOM_COMMAND', 'PHONY')
gcda_elem = self.create_phony_target(self.all_outputs, 'clean-gcda', 'CUSTOM_COMMAND', 'PHONY')
gcda_elem.add_item('COMMAND', mesonlib.get_meson_command() + ['--internal', 'delwithsuffix', '.', 'gcda'])
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')
def get_user_option_args(self):
cmds = []
@ -3209,13 +3189,11 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
return sorted(cmds)
def generate_dist(self):
elem = NinjaBuildElement(self.all_outputs, 'meson-dist', 'CUSTOM_COMMAND', 'PHONY')
elem = self.create_phony_target(self.all_outputs, 'dist', 'CUSTOM_COMMAND', 'PHONY')
elem.add_item('DESC', 'Creating source packages')
elem.add_item('COMMAND', self.environment.get_build_command() + ['dist'])
elem.add_item('pool', 'console')
self.add_build(elem)
# Alias that runs the target defined above
self.create_target_alias('meson-dist')
def generate_scanbuild(self):
if not environment.detect_scanbuild():
@ -3225,12 +3203,10 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
cmd = self.environment.get_build_command() + \
['--internal', 'scanbuild', self.environment.source_dir, self.environment.build_dir] + \
self.environment.get_build_command() + self.get_user_option_args()
elem = NinjaBuildElement(self.all_outputs, 'meson-scan-build', 'CUSTOM_COMMAND', 'PHONY')
elem = self.create_phony_target(self.all_outputs, 'scan-build', 'CUSTOM_COMMAND', 'PHONY')
elem.add_item('COMMAND', cmd)
elem.add_item('pool', 'console')
self.add_build(elem)
# Alias that runs the target defined above
self.create_target_alias('meson-scan-build')
def generate_clangtool(self, name, extra_arg=None):
target_name = 'clang-' + name
@ -3248,11 +3224,10 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
cmd = self.environment.get_build_command() + \
['--internal', 'clang' + name, self.environment.source_dir, self.environment.build_dir] + \
extra_args
elem = NinjaBuildElement(self.all_outputs, 'meson-' + target_name, 'CUSTOM_COMMAND', 'PHONY')
elem = self.create_phony_target(self.all_outputs, target_name, 'CUSTOM_COMMAND', 'PHONY')
elem.add_item('COMMAND', cmd)
elem.add_item('pool', 'console')
self.add_build(elem)
self.create_target_alias('meson-' + target_name)
def generate_clangformat(self):
if not environment.detect_clangformat():
@ -3276,12 +3251,10 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
return
cmd = self.environment.get_build_command() + \
['--internal', 'tags', tool, self.environment.source_dir]
elem = NinjaBuildElement(self.all_outputs, 'meson-' + target_name, 'CUSTOM_COMMAND', 'PHONY')
elem = self.create_phony_target(self.all_outputs, target_name, 'CUSTOM_COMMAND', 'PHONY')
elem.add_item('COMMAND', cmd)
elem.add_item('pool', 'console')
self.add_build(elem)
# Alias that runs the target defined above
self.create_target_alias('meson-' + target_name)
# For things like scan-build and other helper tools we might have.
def generate_utils(self):
@ -3292,12 +3265,10 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
self.generate_tags('ctags', 'ctags')
self.generate_tags('cscope', 'cscope')
cmd = self.environment.get_build_command() + ['--internal', 'uninstall']
elem = NinjaBuildElement(self.all_outputs, 'meson-uninstall', 'CUSTOM_COMMAND', 'PHONY')
elem = self.create_phony_target(self.all_outputs, 'uninstall', 'CUSTOM_COMMAND', 'PHONY')
elem.add_item('COMMAND', cmd)
elem.add_item('pool', 'console')
self.add_build(elem)
# Alias that runs the target defined above
self.create_target_alias('meson-uninstall')
def generate_ending(self):
targetlist = []
@ -3309,11 +3280,9 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
elem = NinjaBuildElement(self.all_outputs, 'all', 'phony', targetlist)
self.add_build(elem)
elem = NinjaBuildElement(self.all_outputs, 'meson-clean', 'CUSTOM_COMMAND', 'PHONY')
elem = self.create_phony_target(self.all_outputs, 'clean', 'CUSTOM_COMMAND', 'PHONY')
elem.add_item('COMMAND', self.ninja_command + ['-t', 'clean'])
elem.add_item('description', 'Cleaning')
# Alias that runs the above-defined meson-clean target
self.create_target_alias('meson-clean')
# If we have custom targets in this project, add all their outputs to
# the list that is passed to the `cleantrees.py` script. The script