aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/backend/ninjabackend.py
diff options
context:
space:
mode:
authorJon Turney <jon.turney@dronecode.org.uk>2018-11-19 13:59:04 +0000
committerJon Turney <jon.turney@dronecode.org.uk>2019-04-27 15:39:32 +0100
commit1303389700e91057316560ab1a4f3071ca2959a8 (patch)
treeed25de8e3a4f0581891fad212f3f5ec8e0dc3086 /mesonbuild/backend/ninjabackend.py
parent7bc1db2d7b05836545ef5a9452383a9940c0348c (diff)
downloadmeson-1303389700e91057316560ab1a4f3071ca2959a8.zip
meson-1303389700e91057316560ab1a4f3071ca2959a8.tar.gz
meson-1303389700e91057316560ab1a4f3071ca2959a8.tar.bz2
ninja: Store rules
Store the rules and then write them all out, rather than writing them out as we go. Store the rule broken down into parts which do and don't go into rspfile, so we can construct either a rsp or non-rsp version of the rule.
Diffstat (limited to 'mesonbuild/backend/ninjabackend.py')
-rw-r--r--mesonbuild/backend/ninjabackend.py361
1 files changed, 181 insertions, 180 deletions
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index f1527da..8c856b2 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -63,6 +63,48 @@ Please report this error with a test case to the Meson bug tracker.''' % text
raise MesonException(errmsg)
return text
+class NinjaComment:
+ def __init__(self, comment):
+ self.comment = comment
+
+ def write(self, outfile):
+ for l in self.comment.split('\n'):
+ outfile.write('# ')
+ outfile.write(l)
+ outfile.write('\n')
+ outfile.write('\n')
+
+class NinjaRule:
+ def __init__(self, rule, command, args, description,
+ rspable = False, deps = None, depfile = None, extra = None):
+ self.name = rule
+ self.command = command # includes args which never go into a rspfile
+ self.args = args # args which will go into a rspfile, if used
+ self.description = description
+ self.deps = deps # depstyle 'gcc' or 'msvc'
+ self.depfile = depfile
+ self.extra = extra
+ self.rspable = rspable # if a rspfile can be used
+
+ def write(self, outfile):
+ outfile.write('rule %s\n' % self.name)
+ if self.rspable:
+ outfile.write(' command = %s @$out.rsp\n' % self.command)
+ outfile.write(' rspfile = $out.rsp\n')
+ outfile.write(' rspfile_content = %s\n' % self.args)
+ else:
+ outfile.write(' command = %s %s\n' % (self.command, self.args))
+ if self.deps:
+ outfile.write(' deps = %s\n' % self.deps)
+ if self.depfile:
+ outfile.write(' depfile = %s\n' % self.depfile)
+ outfile.write(' description = %s\n' % self.description)
+ if self.extra:
+ for l in self.extra.split('\n'):
+ outfile.write(' ')
+ outfile.write(l)
+ outfile.write('\n')
+ outfile.write('\n')
class NinjaBuildElement:
def __init__(self, all_outputs, outfilenames, rule, infilenames):
@@ -230,6 +272,7 @@ int dummy;
with self.detect_vs_dep_prefix(tempfilename) as outfile:
self.generate_rules(outfile)
+ self.write_rules(outfile)
self.generate_phony(outfile)
outfile.write('# Build rules for targets\n\n')
for t in self.build.get_targets().values():
@@ -781,36 +824,43 @@ int dummy;
self.create_target_alias('meson-benchmark', outfile)
def generate_rules(self, outfile):
- outfile.write('# Rules for compiling.\n\n')
+ self.rules = []
+
+ self.add_rule_comment(NinjaComment('Rules for compiling.'))
self.generate_compile_rules(outfile)
- outfile.write('# Rules for linking.\n\n')
+ self.add_rule_comment(NinjaComment('Rules for linking.'))
if self.environment.is_cross_build():
self.generate_static_link_rules(True, outfile)
self.generate_static_link_rules(False, outfile)
self.generate_dynamic_link_rules(outfile)
- outfile.write('# Other rules\n\n')
- outfile.write('rule CUSTOM_COMMAND\n')
- outfile.write(' command = $COMMAND\n')
- outfile.write(' description = $DESC\n')
- outfile.write(' restat = 1\n\n')
+ self.add_rule_comment(NinjaComment('Other rules'))
# Ninja errors out if you have deps = gcc but no depfile, so we must
# have two rules for custom commands.
- outfile.write('rule CUSTOM_COMMAND_DEP\n')
- outfile.write(' command = $COMMAND\n')
- outfile.write(' description = $DESC\n')
- outfile.write(' deps = gcc\n')
- outfile.write(' depfile = $DEPFILE\n')
- outfile.write(' restat = 1\n\n')
- outfile.write('rule REGENERATE_BUILD\n')
+ self.add_rule(NinjaRule('CUSTOM_COMMAND', '$COMMAND', '', '$DESC',
+ extra='restat = 1'))
+ self.add_rule(NinjaRule('CUSTOM_COMMAND_DEP', '$COMMAND', '', '$DESC',
+ deps='gcc', depfile='$DEPFILE',
+ extra='restat = 1'))
+
c = [ninja_quote(quote_func(x)) for x in self.environment.get_build_command()] + \
['--internal',
'regenerate',
ninja_quote(quote_func(self.environment.get_source_dir())),
ninja_quote(quote_func(self.environment.get_build_dir()))]
- outfile.write(" command = " + ' '.join(c) + ' --backend ninja\n')
- outfile.write(' description = Regenerating build files.\n')
- outfile.write(' generator = 1\n\n')
- outfile.write('\n')
+ self.add_rule(NinjaRule('REGENERATE_BUILD',
+ ' '.join(c) + ' --backend ninja', '',
+ 'Regenerating build files.',
+ extra='generator = 1'))
+
+ def add_rule_comment(self, comment):
+ self.rules.append(comment)
+
+ def add_rule(self, rule):
+ self.rules.append(rule)
+
+ def write_rules(self, outfile):
+ for r in self.rules:
+ r.write(outfile)
def generate_phony(self, outfile):
outfile.write('# Phony build target, always out of date\n')
@@ -976,13 +1026,10 @@ int dummy;
return plain_class_path
def generate_java_link(self, outfile):
- rule = 'rule java_LINKER\n'
- command = ' command = jar $ARGS\n'
- description = ' description = Creating JAR $out.\n'
- outfile.write(rule)
- outfile.write(command)
- outfile.write(description)
- outfile.write('\n')
+ rule = 'java_LINKER'
+ command = 'jar $ARGS'
+ description = 'Creating JAR $out.'
+ self.add_rule(NinjaRule(rule, command, '', description))
def determine_dep_vapis(self, target):
"""
@@ -1427,14 +1474,10 @@ int dummy;
crstr = ''
if static_linker is None:
return
- rule = 'rule STATIC%s_LINKER\n' % crstr
- if static_linker.can_linker_accept_rsp():
- command_template = ''' command = {executable} $LINK_ARGS {output_args} @$out.rsp
- rspfile = $out.rsp
- rspfile_content = $in
-'''
- else:
- command_template = ' command = {executable} $LINK_ARGS {output_args} $in\n'
+ rule = 'STATIC%s_LINKER' % crstr
+ command_template = '{executable} $LINK_ARGS {output_args}'
+ args = '$in'
+
cmdlist = []
# FIXME: Must normalize file names with pathlib.Path before writing
# them out to fix this properly on Windows. See:
@@ -1446,15 +1489,19 @@ int dummy;
# it exists. https://github.com/mesonbuild/meson/issues/1355
cmdlist = [execute_wrapper, rmfile_prefix.format('$out')]
cmdlist += static_linker.get_exelist()
- command = command_template.format(
- executable=' '.join(cmdlist),
- output_args=' '.join(static_linker.get_output_args('$out')))
- description = ' description = Linking static target $out.\n\n'
- outfile.write(rule)
- outfile.write(command)
+ subst = {
+ 'executable': ' '.join(cmdlist),
+ 'output_args': ' '.join(static_linker.get_output_args('$out'))
+ }
+ command = command_template.format(**subst)
+ description = 'Linking static target $out.'
if num_pools > 0:
- outfile.write(' pool = link_pool\n')
- outfile.write(description)
+ pool = 'pool = link_pool'
+ else:
+ pool = None
+ self.add_rule(NinjaRule(rule, command, args, description,
+ rspable=static_linker.can_linker_accept_rsp(),
+ extra=pool))
def generate_dynamic_link_rules(self, outfile):
num_pools = self.environment.coredata.backend_options['backend_max_links'].value
@@ -1473,101 +1520,73 @@ int dummy;
crstr = ''
if is_cross:
crstr = '_CROSS'
- rule = 'rule %s%s_LINKER\n' % (langname, crstr)
- if compiler.can_linker_accept_rsp():
- command_template = ''' command = {executable} @$out.rsp
- rspfile = $out.rsp
- rspfile_content = $ARGS {output_args} $in $LINK_ARGS
-'''
- else:
- command_template = ' command = {executable} $ARGS {output_args} $in $LINK_ARGS\n'
- command = command_template.format(
- executable=' '.join(compiler.get_linker_exelist()),
- output_args=' '.join(compiler.get_linker_output_args('$out'))
- )
- description = ' description = Linking target $out.\n'
- outfile.write(rule)
- outfile.write(command)
+ rule = '%s%s_LINKER' % (langname, crstr)
+ command_template = '{executable}'
+ args_template = '$ARGS {output_args} $in $LINK_ARGS'
+ subst = {
+ 'executable': ' '.join(compiler.get_linker_exelist()),
+ 'output_args': ' '.join(compiler.get_linker_output_args('$out')),
+ }
+ command = command_template.format(**subst)
+ args = args_template.format(**subst)
+ description = 'Linking target $out.'
if num_pools > 0:
- outfile.write(' pool = link_pool\n')
- outfile.write(description)
- outfile.write('\n')
- outfile.write('\n')
+ pool = 'pool = link_pool'
+ else:
+ pool = None
+ self.add_rule(NinjaRule(rule, command, args, description,
+ rspable=compiler.can_linker_accept_rsp(),
+ extra=pool))
+
args = [ninja_quote(quote_func(x)) for x in self.environment.get_build_command()] + \
['--internal',
'symbolextractor',
'$in',
'$out']
- symrule = 'rule SHSYM\n'
- symcmd = ' command = ' + ' '.join(args) + ' $CROSS\n'
- synstat = ' restat = 1\n'
- syndesc = ' description = Generating symbol file $out.\n'
- outfile.write(symrule)
- outfile.write(symcmd)
- outfile.write(synstat)
- outfile.write(syndesc)
- outfile.write('\n')
+ symrule = 'SHSYM'
+ symcmd = ' '.join(args) + ' $CROSS'
+ syndesc = 'Generating symbol file $out.'
+ synstat = 'restat = 1'
+ self.add_rule(NinjaRule(symrule, symcmd, '', syndesc, extra=synstat))
def generate_java_compile_rule(self, compiler, outfile):
- rule = 'rule %s_COMPILER\n' % compiler.get_language()
+ rule = '%s_COMPILER' % compiler.get_language()
invoc = ' '.join([ninja_quote(i) for i in compiler.get_exelist()])
- command = ' command = %s $ARGS $in\n' % invoc
- description = ' description = Compiling Java object $in.\n'
- outfile.write(rule)
- outfile.write(command)
- outfile.write(description)
- outfile.write('\n')
+ command = '%s $ARGS $in' % invoc
+ description = 'Compiling Java object $in.'
+ self.add_rule(NinjaRule(rule, command, '', description))
def generate_cs_compile_rule(self, compiler, outfile):
- rule = 'rule %s_COMPILER\n' % compiler.get_language()
+ rule = '%s_COMPILER' % compiler.get_language()
invoc = ' '.join([ninja_quote(i) for i in compiler.get_exelist()])
-
- if mesonlib.is_windows():
- command = ''' command = {executable} @$out.rsp
- rspfile = $out.rsp
- rspfile_content = $ARGS $in
-'''.format(executable=invoc)
- else:
- command = ' command = %s $ARGS $in\n' % invoc
-
- description = ' description = Compiling C Sharp target $out.\n'
- outfile.write(rule)
- outfile.write(command)
- outfile.write(description)
- outfile.write('\n')
+ command = '%s' % invoc
+ args = '$ARGS $in'
+ description = 'Compiling C Sharp target $out.'
+ self.add_rule(NinjaRule(rule, command, args, description,
+ rspable=mesonlib.is_windows()))
def generate_vala_compile_rules(self, compiler, outfile):
- rule = 'rule %s_COMPILER\n' % compiler.get_language()
+ rule = '%s_COMPILER' % compiler.get_language()
invoc = ' '.join([ninja_quote(i) for i in compiler.get_exelist()])
- command = ' command = %s $ARGS $in\n' % invoc
- description = ' description = Compiling Vala source $in.\n'
- restat = ' restat = 1\n' # ValaC does this always to take advantage of it.
- outfile.write(rule)
- outfile.write(command)
- outfile.write(description)
- outfile.write(restat)
- outfile.write('\n')
+ command = '%s $ARGS $in' % invoc
+ description = 'Compiling Vala source $in.'
+ self.add_rule(NinjaRule(rule, command, '', description, extra='restat = 1'))
def generate_rust_compile_rules(self, compiler, outfile, is_cross):
crstr = ''
if is_cross:
crstr = '_CROSS'
- rule = 'rule %s%s_COMPILER\n' % (compiler.get_language(), crstr)
+ rule = '%s%s_COMPILER' % (compiler.get_language(), crstr)
invoc = ' '.join([ninja_quote(i) for i in compiler.get_exelist()])
- command = ' command = %s $ARGS $in\n' % invoc
- description = ' description = Compiling Rust source $in.\n'
- depfile = ' depfile = $targetdep\n'
-
- depstyle = ' deps = gcc\n'
- outfile.write(rule)
- outfile.write(command)
- outfile.write(description)
- outfile.write(depfile)
- outfile.write(depstyle)
- outfile.write('\n')
+ command = '%s $ARGS $in' % invoc
+ description = 'Compiling Rust source $in.'
+ depfile = '$targetdep'
+ depstyle = 'gcc'
+ self.add_rule(NinjaRule(rule, command, '', description, deps=depstyle,
+ depfile=depfile))
def generate_swift_compile_rules(self, compiler, outfile):
- rule = 'rule %s_COMPILER\n' % compiler.get_language()
+ rule = '%s_COMPILER' % compiler.get_language()
full_exe = [ninja_quote(x) for x in self.environment.get_build_command()] + [
'--internal',
'dirchanger',
@@ -1575,49 +1594,37 @@ int dummy;
]
invoc = (' '.join(full_exe) + ' ' +
' '.join(ninja_quote(i) for i in compiler.get_exelist()))
- command = ' command = %s $ARGS $in\n' % invoc
- description = ' description = Compiling Swift source $in.\n'
- outfile.write(rule)
- outfile.write(command)
- outfile.write(description)
- outfile.write('\n')
+ command = '%s $ARGS $in' % invoc
+ description = 'Compiling Swift source $in.'
+ self.add_rule(NinjaRule(rule, command, '', description))
def generate_fortran_dep_hack(self, outfile, crstr):
+ rule = 'FORTRAN_DEP_HACK%s' % (crstr)
if mesonlib.is_windows():
cmd = 'cmd /C ""'
else:
cmd = 'true'
- template = '''# Workaround for these issues:
-# https://groups.google.com/forum/#!topic/ninja-build/j-2RfBIOd_8
-# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485
-rule FORTRAN_DEP_HACK%s
- command = %s
- description = Dep hack
- restat = 1
-
-'''
- outfile.write(template % (crstr, cmd))
+ self.add_rule_comment(NinjaComment('''Workaround for these issues:
+https://groups.google.com/forum/#!topic/ninja-build/j-2RfBIOd_8
+https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
+ self.add_rule(NinjaRule(rule, cmd, '', 'Dep hack', extra='restat = 1'))
def generate_llvm_ir_compile_rule(self, compiler, is_cross, outfile):
if getattr(self, 'created_llvm_ir_rule', False):
return
- rule = 'rule llvm_ir{}_COMPILER\n'.format('_CROSS' if is_cross else '')
- if compiler.can_linker_accept_rsp():
- command_template = ' command = {executable} @$out.rsp\n' \
- ' rspfile = $out.rsp\n' \
- ' rspfile_content = $ARGS {output_args} {compile_only_args} $in\n'
- else:
- command_template = ' command = {executable} $ARGS {output_args} {compile_only_args} $in\n'
- command = command_template.format(
- executable=' '.join([ninja_quote(i) for i in compiler.get_exelist()]),
- output_args=' '.join(compiler.get_output_args('$out')),
- compile_only_args=' '.join(compiler.get_compile_only_args())
- )
- description = ' description = Compiling LLVM IR object $in.\n'
- outfile.write(rule)
- outfile.write(command)
- outfile.write(description)
- outfile.write('\n')
+ rule = 'llvm_ir{}_COMPILER'.format('_CROSS' if is_cross else '')
+ command_template = '{executable}'
+ args_template = '$ARGS {output_args} {compile_only_args} $in'
+ subst = {
+ 'executable': ' '.join([ninja_quote(i) for i in compiler.get_exelist()]),
+ 'output_args': ' '.join(compiler.get_output_args('$out')),
+ 'compile_only_args': ' '.join(compiler.get_compile_only_args()),
+ }
+ command = command_template.format(**subst)
+ args = args_template.format(**subst)
+ 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 = True
def generate_compile_rule_for(self, langname, compiler, is_cross, outfile):
@@ -1646,7 +1653,7 @@ rule FORTRAN_DEP_HACK%s
crstr = ''
if langname == 'fortran':
self.generate_fortran_dep_hack(outfile, crstr)
- rule = 'rule %s%s_COMPILER\n' % (langname, crstr)
+ rule = '%s%s_COMPILER' % (langname, crstr)
depargs = compiler.get_dependency_gen_args('$out', '$DEPFILE')
quoted_depargs = []
for d in depargs:
@@ -1654,30 +1661,26 @@ rule FORTRAN_DEP_HACK%s
d = quote_func(d)
quoted_depargs.append(d)
- if compiler.can_linker_accept_rsp():
- command_template = ''' command = {executable} @$out.rsp
- rspfile = $out.rsp
- rspfile_content = $ARGS {dep_args} {output_args} {compile_only_args} $in
-'''
- else:
- command_template = ' command = {executable} $ARGS {dep_args} {output_args} {compile_only_args} $in\n'
- command = command_template.format(
- executable=' '.join([ninja_quote(i) for i in compiler.get_exelist()]),
- dep_args=' '.join(quoted_depargs),
- output_args=' '.join(compiler.get_output_args('$out')),
- compile_only_args=' '.join(compiler.get_compile_only_args())
- )
- description = ' description = Compiling %s object $out.\n' % compiler.get_display_language()
+ command_template = '{executable}'
+ args_template = '$ARGS {dep_args} {output_args} {compile_only_args} $in'
+ subst = {
+ 'executable': ' '.join([ninja_quote(i) for i in compiler.get_exelist()]),
+ 'dep_args': ' '.join(quoted_depargs),
+ 'output_args': ' '.join(compiler.get_output_args('$out')),
+ 'compile_only_args': ' '.join(compiler.get_compile_only_args()),
+ }
+ command = command_template.format(**subst)
+ args = args_template.format(**subst)
+ description = 'Compiling %s object $out.' % compiler.get_display_language()
if isinstance(compiler, VisualStudioCCompiler):
- deps = ' deps = msvc\n'
+ deps = 'msvc'
+ depfile = None
else:
- deps = ' deps = gcc\n'
- deps += ' depfile = $DEPFILE\n'
- outfile.write(rule)
- outfile.write(command)
- outfile.write(deps)
- outfile.write(description)
- outfile.write('\n')
+ deps = 'gcc'
+ depfile = '$DEPFILE'
+ self.add_rule(NinjaRule(rule, command, args, description,
+ rspable=compiler.can_linker_accept_rsp(),
+ deps=deps, depfile=depfile))
def generate_pch_rule_for(self, langname, compiler, is_cross, outfile):
if langname != 'c' and langname != 'cpp':
@@ -1686,7 +1689,7 @@ rule FORTRAN_DEP_HACK%s
crstr = '_CROSS'
else:
crstr = ''
- rule = 'rule %s%s_PCH\n' % (langname, crstr)
+ rule = '%s%s_PCH' % (langname, crstr)
depargs = compiler.get_dependency_gen_args('$out', '$DEPFILE')
quoted_depargs = []
@@ -1698,23 +1701,21 @@ rule FORTRAN_DEP_HACK%s
output = ''
else:
output = ' '.join(compiler.get_output_args('$out'))
- command = " command = {executable} $ARGS {dep_args} {output_args} {compile_only_args} $in\n".format(
+ command = "{executable} $ARGS {dep_args} {output_args} {compile_only_args} $in".format(
executable=' '.join(compiler.get_exelist()),
dep_args=' '.join(quoted_depargs),
output_args=output,
compile_only_args=' '.join(compiler.get_compile_only_args())
)
- description = ' description = Precompiling header %s.\n' % '$in'
+ description = 'Precompiling header $in.'
if isinstance(compiler, VisualStudioCCompiler):
- deps = ' deps = msvc\n'
+ deps = 'msvc'
+ depfile = None
else:
- deps = ' deps = gcc\n'
- deps += ' depfile = $DEPFILE\n'
- outfile.write(rule)
- outfile.write(command)
- outfile.write(deps)
- outfile.write(description)
- outfile.write('\n')
+ deps = 'gcc'
+ depfile = '$DEPFILE'
+ self.add_rule(NinjaRule(rule, command, '', description, deps=deps,
+ depfile=depfile))
def generate_compile_rules(self, outfile):
for langname, compiler in self.build.compilers.items():