aboutsummaryrefslogtreecommitdiff
path: root/meson/ninjabackend.py
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2016-01-16 17:35:29 +0200
committerJussi Pakkanen <jpakkane@gmail.com>2016-01-16 17:35:29 +0200
commit23b98cd6e66c6ae0f070e28e0f8b1566c0b5e585 (patch)
treee349597556abe3d22578cfb1f9529f4626ceb5aa /meson/ninjabackend.py
parent1510522b1b9970376a1e1cc5f39e00d8749ec19a (diff)
downloadmeson-23b98cd6e66c6ae0f070e28e0f8b1566c0b5e585.zip
meson-23b98cd6e66c6ae0f070e28e0f8b1566c0b5e585.tar.gz
meson-23b98cd6e66c6ae0f070e28e0f8b1566c0b5e585.tar.bz2
Renamed meson package to mesonbuild so that we can have a script named meson in the same toplevel dir.
Diffstat (limited to 'meson/ninjabackend.py')
-rw-r--r--meson/ninjabackend.py1819
1 files changed, 0 insertions, 1819 deletions
diff --git a/meson/ninjabackend.py b/meson/ninjabackend.py
deleted file mode 100644
index 36c5ce9..0000000
--- a/meson/ninjabackend.py
+++ /dev/null
@@ -1,1819 +0,0 @@
-# Copyright 2012-2016 The Meson development team
-
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-
-# http://www.apache.org/licenses/LICENSE-2.0
-
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from . import backends
-from . import environment, mesonlib
-from . import build
-from . import mlog
-from . import dependencies
-from .mesonlib import File
-from .backends import InstallData
-from .build import InvalidArguments
-from .coredata import MesonException
-import os, sys, pickle, re
-import subprocess, shutil
-
-if mesonlib.is_windows():
- quote_char = '"'
- execute_wrapper = 'cmd /c'
-else:
- quote_char = "'"
- execute_wrapper = ''
-
-def ninja_quote(text):
- return text.replace(' ', '$ ').replace(':', '$:')
-
-class RawFilename():
- def __init__(self, fname):
- self.fname = fname
-
- def split(self, c):
- return self.fname.split(c)
-
- def startswith(self, s):
- return self.fname.startswith(s)
-
-class NinjaBuildElement():
- def __init__(self, outfilenames, rule, infilenames):
- if isinstance(outfilenames, str):
- self.outfilenames = [outfilenames]
- else:
- self.outfilenames = outfilenames
- assert(isinstance(rule, str))
- self.rule = rule
- if isinstance(infilenames, str):
- self.infilenames = [infilenames]
- else:
- self.infilenames = infilenames
- self.deps = []
- self.orderdeps = []
- self.elems = []
-
- def add_dep(self, dep):
- if isinstance(dep, list):
- self.deps += dep
- else:
- self.deps.append(dep)
-
- def add_orderdep(self, dep):
- if isinstance(dep, list):
- self.orderdeps += dep
- else:
- self.orderdeps.append(dep)
-
- def add_item(self, name, elems):
- if isinstance(elems, str):
- elems = [elems]
- self.elems.append((name, elems))
-
- def write(self, outfile):
- line = 'build %s: %s %s' % (' '.join([ninja_quote(i) for i in self.outfilenames]),\
- self.rule,
- ' '.join([ninja_quote(i) for i in self.infilenames]))
- if len(self.deps) > 0:
- line += ' | ' + ' '.join([ninja_quote(x) for x in self.deps])
- if len(self.orderdeps) > 0:
- line += ' || ' + ' '.join([ninja_quote(x) for x in self.orderdeps])
- line += '\n'
- # This is the only way I could find to make this work on all
- # platforms including Windows command shell. Slash is a dir separator
- # on Windows, too, so all characters are unambiguous and, more importantly,
- # do not require quoting.
- line = line.replace('\\', '/')
- outfile.write(line)
-
- for e in self.elems:
- (name, elems) = e
- should_quote = True
- if name == 'DEPFILE' or name == 'DESC' or name == 'pool':
- should_quote = False
- line = ' %s = ' % name
- q_templ = quote_char + "%s" + quote_char
- noq_templ = "%s"
- newelems = []
- for i in elems:
- if not should_quote or i == '&&': # Hackety hack hack
- templ = noq_templ
- else:
- templ = q_templ
- i = i.replace('\\', '\\\\')
- if quote_char == '"':
- i = i.replace('"', '\\"')
- newelems.append(templ % ninja_quote(i))
- line += ' '.join(newelems)
- line += '\n'
- outfile.write(line)
- outfile.write('\n')
-
-class NinjaBackend(backends.Backend):
-
- def __init__(self, build):
- super().__init__(build)
- self.source_suffix_in_objs = True
- self.ninja_filename = 'build.ninja'
- self.fortran_deps = {}
- self.all_outputs = {}
-
- def check_outputs(self, elem):
- for n in elem.outfilenames:
- if n in self.all_outputs:
- raise MesonException('Multiple producers for Ninja target "%s". Please rename your targets.' % n)
- self.all_outputs[n] = True
-
- def detect_vs_dep_prefix(self, outfile, tempfilename):
- '''VS writes its dependency in a locale dependent format.
- Detect the search prefix to use.'''
- if shutil.which('cl') is None:
- return outfile
- outfile.close()
- open(os.path.join(self.environment.get_scratch_dir(), 'incdetect.c'),
- 'w').write('''#include<stdio.h>
-int dummy;
-''')
-
- pc = subprocess.Popen(['cl', '/showIncludes', '/c', 'incdetect.c'],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- cwd=self.environment.get_scratch_dir())
-
- (stdo, _) = pc.communicate()
-
- for line in stdo.split(b'\r\n'):
- if line.endswith(b'stdio.h'):
- matchstr = b':'.join(line.split(b':')[0:2]) + b':'
- binfile = open(tempfilename, 'ab')
- binfile.write(b'msvc_deps_prefix = ' + matchstr + b'\r\n')
- binfile.close()
- return open(tempfilename, 'a')
- raise MesonException('Could not determine vs dep dependency prefix string.')
-
- def generate(self, interp):
- self.interpreter = interp
- outfilename = os.path.join(self.environment.get_build_dir(), self.ninja_filename)
- tempfilename = outfilename + '~'
- outfile = open(tempfilename, 'w')
- outfile.write('# This is the build file for project "%s"\n' % self.build.get_project())
- outfile.write('# It is autogenerated by the Meson build system.\n')
- outfile.write('# Do not edit by hand.\n\n')
- outfile.write('ninja_required_version = 1.5.1\n\n')
- outfile = self.detect_vs_dep_prefix(outfile, tempfilename)
- self.generate_rules(outfile)
- self.generate_phony(outfile)
- outfile.write('# Build rules for targets\n\n')
- [self.generate_target(t, outfile) for t in self.build.get_targets().values()]
- if len(self.build.pot) > 0:
- outfile.write('# Build rules for localisation.\n\n')
- self.generate_po(outfile)
- outfile.write('# Test rules\n\n')
- self.generate_tests(outfile)
- outfile.write('# Install rules\n\n')
- self.generate_install(outfile)
- if self.environment.coredata.get_builtin_option('coverage'):
- outfile.write('# Coverage rules\n\n')
- self.generate_coverage_rules(outfile)
- outfile.write('# Suffix\n\n')
- self.generate_ending(outfile)
- # Only ovewrite the old build file after the new one has been
- # fully created.
- outfile.close()
- os.replace(tempfilename, outfilename)
- self.generate_compdb()
-
- # http://clang.llvm.org/docs/JSONCompilationDatabase.html
- def generate_compdb(self):
- ninja_exe = environment.detect_ninja()
- builddir = self.environment.get_build_dir()
- jsondb = subprocess.check_output([ninja_exe, '-t', 'compdb', 'c_COMPILER', 'cpp_COMPILER'], cwd=builddir)
- open(os.path.join(builddir, 'compile_commands.json'), 'wb').write(jsondb)
-
- # Get all generated headers. Any source file might need them so
- # we need to add an order dependency to them.
- def get_generated_headers(self, target):
- header_deps = []
- for gensource in target.get_generated_sources():
- if isinstance(gensource, build.CustomTarget):
- continue
- for src in gensource.get_outfilelist():
- if self.environment.is_header(src):
- header_deps.append(os.path.join(self.get_target_private_dir(target), src))
- for dep in target.link_targets:
- if isinstance(dep, (build.StaticLibrary, build.SharedLibrary)):
- header_deps += self.get_generated_headers(dep)
- return header_deps
-
- def generate_target(self, target, outfile):
- if isinstance(target, build.CustomTarget):
- self.generate_custom_target(target, outfile)
- if isinstance(target, build.RunTarget):
- self.generate_run_target(target, outfile)
- name = target.get_id()
- gen_src_deps = []
- if name in self.processed_targets:
- return
- if isinstance(target, build.Jar):
- self.generate_jar_target(target, outfile)
- return
- if 'rust' in self.environment.coredata.compilers.keys() and self.has_rust(target):
- self.generate_rust_target(target, outfile)
- return
- if 'cs' in self.environment.coredata.compilers.keys() and self.has_cs(target):
- self.generate_cs_target(target, outfile)
- return
- if 'vala' in self.environment.coredata.compilers.keys() and self.has_vala(target):
- gen_src_deps += self.generate_vala_compile(target, outfile)
- if 'swift' in self.environment.coredata.compilers.keys() and self.has_swift(target):
- self.generate_swift_target(target, outfile)
- return
- self.scan_fortran_module_outputs(target)
- # The following deals with C/C++ compilation.
- (gen_src, gen_other_deps) = self.process_dep_gens(outfile, target)
- gen_src_deps += gen_src
- self.process_target_dependencies(target, outfile)
- self.generate_custom_generator_rules(target, outfile)
- outname = self.get_target_filename(target)
- obj_list = []
- use_pch = self.environment.coredata.get_builtin_option('use_pch')
- is_unity = self.environment.coredata.get_builtin_option('unity')
- if use_pch and target.has_pch():
- pch_objects = self.generate_pch(target, outfile)
- else:
- pch_objects = []
- header_deps = gen_other_deps
- unity_src = []
- unity_deps = [] # Generated sources that must be built before compiling a Unity target.
- header_deps += self.get_generated_headers(target)
- for gensource in target.get_generated_sources():
- if isinstance(gensource, build.CustomTarget):
- for src in gensource.output:
- src = os.path.join(self.get_target_dir(gensource), src)
- if self.environment.is_source(src) and not self.environment.is_header(src):
- if is_unity:
- unity_deps.append(os.path.join(self.environment.get_build_dir(), RawFilename(src)))
- else:
- obj_list.append(self.generate_single_compile(target, outfile, RawFilename(src), True,
- header_deps))
- elif self.environment.is_object(src):
- obj_list.append(src)
- elif self.environment.is_library(src):
- pass
- else:
- # Assume anything not specifically a source file is a header. This is because
- # people generate files with weird suffixes (.inc, .fh) that they then include
- # in their source files.
- header_deps.append(RawFilename(src))
- else:
- for src in gensource.get_outfilelist():
- if self.environment.is_object(src):
- obj_list.append(os.path.join(self.get_target_private_dir(target), src))
- elif not self.environment.is_header(src):
- if is_unity:
- if self.has_dir_part(src):
- rel_src = src
- else:
- rel_src = os.path.join(self.get_target_private_dir(target), src)
- unity_deps.append(rel_src)
- abs_src = os.path.join(self.environment.get_build_dir(), rel_src)
- unity_src.append(abs_src)
- else:
- obj_list.append(self.generate_single_compile(target, outfile, src, True,
- header_deps=header_deps))
- src_list = []
- for src in gen_src_deps:
- src_list.append(src)
- if is_unity:
- unity_src.append(os.path.join(self.environment.get_build_dir(), src))
- header_deps.append(src)
- else:
- # Generated targets are ordered deps because the must exist
- # before the sources compiling them are used. After the first
- # compile we get precise dependency info from dep files.
- # This should work in all cases. If it does not, then just
- # move them from orderdeps to proper deps.
- obj_list.append(self.generate_single_compile(target, outfile, src, True, [], header_deps))
- for src in target.get_sources():
- if src.endswith('.vala'):
- continue
- if not self.environment.is_header(src):
- src_list.append(src)
- if is_unity:
- abs_src = os.path.join(self.environment.get_build_dir(),
- src.rel_to_builddir(self.build_to_src))
- unity_src.append(abs_src)
- else:
- obj_list.append(self.generate_single_compile(target, outfile, src, False, [], header_deps))
- obj_list += self.flatten_object_list(target)
- if is_unity:
- for src in self.generate_unity_files(target, unity_src):
- obj_list.append(self.generate_single_compile(target, outfile, src, True, unity_deps + header_deps))
- linker = self.determine_linker(target, src_list)
- elem = self.generate_link(target, outfile, outname, obj_list, linker, pch_objects)
- self.generate_shlib_aliases(target, self.get_target_dir(target))
- elem.write(outfile)
- self.processed_targets[name] = True
-
- def process_target_dependencies(self, target, outfile):
- for t in target.get_dependencies():
- tname = t.get_basename() + t.type_suffix()
- if not tname in self.processed_targets:
- self.generate_target(t, outfile)
-
- def generate_custom_target(self, target, outfile):
- (srcs, ofilenames, cmd) = self.eval_custom_target_command(target)
- deps = []
- for i in target.get_dependencies():
- # FIXME, should not grab element at zero but rather expand all.
- if isinstance(i, list):
- i = i[0]
- fname = i.get_filename()
- if isinstance(fname, list):
- fname = fname[0]
- deps.append(os.path.join(self.get_target_dir(i), fname))
- if target.build_always:
- deps.append('PHONY')
- elem = NinjaBuildElement(ofilenames, 'CUSTOM_COMMAND', srcs)
- for i in target.depend_files:
- if isinstance(i, mesonlib.File):
- deps.append(i.rel_to_builddir(self.build_to_src))
- else:
- deps.append(os.path.join(self.build_to_src, i))
- elem.add_dep(deps)
- for d in target.extra_depends:
- tmp = d.get_filename()
- if not isinstance(tmp, list):
- tmp = [tmp]
- for fname in tmp:
- elem.add_dep(os.path.join(self.get_target_dir(d), fname))
-
- elem.add_item('COMMAND', cmd)
- elem.add_item('description', 'Generating %s with a custom command.' % target.name)
- elem.write(outfile)
- self.check_outputs(elem)
- self.processed_targets[target.name + target.type_suffix()] = True
-
- def generate_run_target(self, target, outfile):
- runnerscript = os.path.join(self.environment.get_script_dir(), 'commandrunner.py')
- deps = []
- arg_strings = []
- for i in target.args:
- if isinstance(i, str):
- arg_strings.append(i)
- elif isinstance(i, (build.BuildTarget, build.CustomTarget)):
- relfname = self.get_target_filename(i)
- deps.append(relfname)
- arg_strings.append(os.path.join(self.environment.get_build_dir(), relfname))
- else:
- mlog.debug(str(i))
- raise MesonException('Unreachable code in generate_run_target.')
- elem = NinjaBuildElement(target.name, 'CUSTOM_COMMAND', deps)
- cmd = [sys.executable, runnerscript, self.environment.get_source_dir(), self.environment.get_build_dir(), target.subdir]
- texe = target.command
- try:
- texe = texe.held_object
- except AttributeError:
- pass
- if isinstance(texe, build.Executable):
- abs_exe = os.path.join(self.environment.get_build_dir(), self.get_target_filename(texe))
- deps.append(self.get_target_filename(texe))
- if self.environment.is_cross_build() \
- and self.environment.cross_info.config['binaries'].get('exe_wrapper', None) is not None:
- cmd += [self.environment.cross_info.config['binaries']['exe_wrapper']]
- cmd.append(abs_exe)
- else:
- cmd.append(target.command)
- cmd += arg_strings
- elem.add_item('COMMAND', cmd)
- elem.add_item('description', 'Running external command %s.' % target.name)
- elem.add_item('pool', 'console')
- elem.write(outfile)
- self.check_outputs(elem)
- self.processed_targets[target.name + target.type_suffix()] = True
-
- def generate_po(self, outfile):
- for p in self.build.pot:
- (packagename, languages, subdir) = p
- input_file = os.path.join(subdir, 'POTFILES')
- elem = NinjaBuildElement('pot', 'GEN_POT', [])
- elem.add_item('PACKAGENAME', packagename)
- elem.add_item('OUTFILE', packagename + '.pot')
- elem.add_item('FILELIST', os.path.join(self.environment.get_source_dir(), input_file))
- elem.add_item('OUTDIR', os.path.join(self.environment.get_source_dir(), subdir))
- elem.write(outfile)
- self.check_outputs(elem)
- for l in languages:
- infile = os.path.join(self.environment.get_source_dir(), subdir, l + '.po')
- outfilename = os.path.join(subdir, l + '.gmo')
- lelem = NinjaBuildElement(outfilename, 'GEN_GMO', infile)
- lelem.add_item('INFILE', infile)
- lelem.add_item('OUTFILE', outfilename)
- lelem.write(outfile)
- self.check_outputs(lelem)
-
- def generate_coverage_rules(self, outfile):
- (gcovr_exe, lcov_exe, genhtml_exe) = environment.find_coverage_tools()
- added_rule = False
- if gcovr_exe:
- added_rule = True
- elem = NinjaBuildElement('coverage-xml', 'CUSTOM_COMMAND', '')
- elem.add_item('COMMAND', [gcovr_exe, '-x', '-r', self.environment.get_build_dir(),\
- '-o', os.path.join(self.environment.get_log_dir(), 'coverage.xml')])
- elem.add_item('DESC', 'Generating XML coverage report.')
- elem.write(outfile)
- elem = NinjaBuildElement('coverage-text', 'CUSTOM_COMMAND', '')
- elem.add_item('COMMAND', [gcovr_exe, '-r', self.environment.get_build_dir(),\
- '-o', os.path.join(self.environment.get_log_dir(), 'coverage.txt')])
- elem.add_item('DESC', 'Generating text coverage report.')
- elem.write(outfile)
- self.check_outputs(elem)
- if lcov_exe and genhtml_exe:
- added_rule = True
- phony_elem = NinjaBuildElement('coverage-html', 'phony', 'coveragereport/index.html')
- phony_elem.write(outfile)
-
- elem = NinjaBuildElement('coveragereport/index.html', 'CUSTOM_COMMAND', '')
- command = [lcov_exe, '--directory', self.environment.get_build_dir(),\
- '--capture', '--output-file', 'coverage.info', '--no-checksum',\
- '&&', genhtml_exe, '--prefix', self.environment.get_build_dir(),\
- '--output-directory', self.environment.get_log_dir(), '--title', 'Code coverage',\
- '--legend', '--show-details', 'coverage.info']
- elem.add_item('COMMAND', command)
- elem.add_item('DESC', 'Generating HTML coverage report.')
- self.check_outputs(elem)
- elem.write(outfile)
- if not added_rule:
- mlog.log(mlog.red('Warning:'), 'coverage requested but neither gcovr nor lcov/genhtml found.')
-
- def generate_install(self, outfile):
- script_root = self.environment.get_script_dir()
- install_script = os.path.join(script_root, 'meson_install.py')
- install_data_file = os.path.join(self.environment.get_scratch_dir(), 'install.dat')
- depfixer = [sys.executable, self.environment.get_build_command(), '--internal', 'depfixer']
- d = InstallData(self.environment.get_source_dir(),
- self.environment.get_build_dir(),
- self.environment.get_prefix(), depfixer)
- elem = NinjaBuildElement('install', 'CUSTOM_COMMAND', 'PHONY')
- elem.add_dep('all')
- elem.add_item('DESC', 'Installing files.')
- elem.add_item('COMMAND', [sys.executable, self.environment.get_build_command(), '--internal', 'install', install_data_file])
- elem.add_item('pool', 'console')
- self.generate_depmf_install(d)
- self.generate_target_install(d)
- self.generate_header_install(d)
- self.generate_man_install(d)
- self.generate_data_install(d)
- self.generate_po_install(d, elem)
- self.generate_custom_install_script(d)
- self.generate_subdir_install(d)
- elem.write(outfile)
- self.check_outputs(elem)
-
- ofile = open(install_data_file, 'wb')
- pickle.dump(d, ofile)
-
- def generate_po_install(self, d, elem):
- for p in self.build.pot:
- (package_name, languages, subdir) = p
- # FIXME: assumes only one po package per source
- d.po_package_name = package_name
- for lang in languages:
- rel_src = os.path.join(subdir, lang + '.gmo')
- src_file = os.path.join(self.environment.get_build_dir(), rel_src)
- d.po.append((src_file, self.environment.coredata.get_builtin_option('localedir'), lang))
- elem.add_dep(rel_src)
-
- def generate_target_install(self, d):
- libdir = self.environment.get_libdir()
- bindir = self.environment.get_bindir()
-
- should_strip = self.environment.coredata.get_builtin_option('strip')
- for t in self.build.get_targets().values():
- if t.should_install():
- outdir = t.get_custom_install_dir()
- if outdir is None:
- if isinstance(t, build.Executable):
- outdir = bindir
- else:
- outdir = libdir
- i = [self.get_target_filename(t), outdir, t.get_aliaslist(),\
- should_strip, t.install_rpath]
- d.targets.append(i)
-
- def generate_custom_install_script(self, d):
- d.install_scripts = self.build.install_scripts
-
- def generate_header_install(self, d):
- incroot = self.environment.get_includedir()
- headers = self.build.get_headers()
-
- for h in headers:
- outdir = h.get_custom_install_dir()
- if outdir is None:
- outdir = os.path.join(incroot, h.get_install_subdir())
- for f in h.get_sources():
- abspath = os.path.join(self.environment.get_source_dir(), h.get_source_subdir(), f)
- i = [abspath, outdir]
- d.headers.append(i)
-
- def generate_man_install(self, d):
- manroot = self.environment.get_mandir()
- man = self.build.get_man()
- for m in man:
- for f in m.get_sources():
- num = f.split('.')[-1]
- subdir = m.get_custom_install_dir()
- if subdir is None:
- subdir = os.path.join(manroot, 'man' + num)
- srcabs = os.path.join(self.environment.get_source_dir(), m.get_source_subdir(), f)
- dstabs = os.path.join(subdir, f + '.gz')
- i = [srcabs, dstabs]
- d.man.append(i)
-
- def generate_data_install(self, d):
- data = self.build.get_data()
- for de in data:
- assert(isinstance(de, build.Data))
- subdir = de.install_dir
- for f in de.sources:
- if de.in_sourcetree:
- srcprefix = self.environment.get_source_dir()
- else:
- srcprefix = self.environment.get_build_dir()
- srcabs = os.path.join(srcprefix, de.source_subdir, f)
- dstabs = os.path.join(subdir, f)
- i = [srcabs, dstabs]
- d.data.append(i)
-
- def generate_subdir_install(self, d):
- for sd in self.build.get_install_subdirs():
- src_dir = os.path.join(self.environment.get_source_dir(), sd.source_subdir, sd.installable_subdir)
- dst_dir = os.path.join(self.environment.get_prefix(), sd.install_dir)
- d.install_subdirs.append([src_dir, dst_dir])
-
- def write_test_suite_targets(self, cmd, outfile):
- suites = {}
- for t in self.build.get_tests():
- for s in t.suite:
- suites[s] = True
- suites = list(suites.keys())
- suites.sort()
- for s in suites:
- if s == '':
- visible_name = 'for top level tests'
- else:
- visible_name = s
- elem = NinjaBuildElement('test-' + s, 'CUSTOM_COMMAND', ['all', 'PHONY'])
- elem.add_item('COMMAND', cmd + ['--suite=' + s])
- elem.add_item('DESC', 'Running test suite %s.' % visible_name)
- elem.add_item('pool', 'console')
- elem.write(outfile)
- self.check_outputs(elem)
-
- def generate_tests(self, outfile):
- self.serialise_tests()
- valgrind = environment.find_valgrind()
- script_root = self.environment.get_script_dir()
- test_data = os.path.join(self.environment.get_scratch_dir(), 'meson_test_setup.dat')
- cmd = [sys.executable, self.environment.get_build_command(), '--internal', 'test', test_data]
- elem = NinjaBuildElement('test', 'CUSTOM_COMMAND', ['all', 'PHONY'])
- elem.add_item('COMMAND', cmd)
- elem.add_item('DESC', 'Running all tests.')
- elem.add_item('pool', 'console')
- elem.write(outfile)
- self.check_outputs(elem)
- self.write_test_suite_targets(cmd, outfile)
-
- if valgrind:
- velem = NinjaBuildElement('test-valgrind', 'CUSTOM_COMMAND', ['all', 'PHONY'])
- velem.add_item('COMMAND', cmd + ['--wrapper=' + valgrind])
- velem.add_item('DESC', 'Running test suite under Valgrind.')
- velem.add_item('pool', 'console')
- velem.write(outfile)
- self.check_outputs(velem)
-
- # And then benchmarks.
- benchmark_script = os.path.join(script_root, 'meson_benchmark.py')
- benchmark_data = os.path.join(self.environment.get_scratch_dir(), 'meson_benchmark_setup.dat')
- cmd = [sys.executable, self.environment.get_build_command(), '--internal', 'benchmark', benchmark_data]
- elem = NinjaBuildElement('benchmark', 'CUSTOM_COMMAND', ['all', 'PHONY'])
- elem.add_item('COMMAND', cmd)
- elem.add_item('DESC', 'Running benchmark suite.')
- elem.add_item('pool', 'console')
- elem.write(outfile)
- self.check_outputs(elem)
-
- def generate_rules(self, outfile):
- outfile.write('# Rules for compiling.\n\n')
- self.generate_compile_rules(outfile)
- outfile.write('# Rules for linking.\n\n')
- 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')
- outfile.write('rule REGENERATE_BUILD\n')
- c = (quote_char + ninja_quote(sys.executable) + quote_char,
- quote_char + ninja_quote(self.environment.get_build_command()) + quote_char,
- '--internal',
- 'regenerate',
- quote_char + ninja_quote(self.environment.get_source_dir()) + quote_char,
- quote_char + ninja_quote(self.environment.get_build_dir()) + quote_char)
- outfile.write(" command = %s %s %s %s %s %s --backend ninja\n" % c)
- outfile.write(' description = Regenerating build files\n')
- outfile.write(' generator = 1\n\n')
- if len(self.build.pot) > 0:
- self.generate_gettext_rules(outfile)
- outfile.write('\n')
-
- def generate_gettext_rules(self, outfile):
- rule = 'rule GEN_POT\n'
- command = " command = xgettext --package-name=$PACKAGENAME -p $OUTDIR -f $FILELIST -D '%s' -k_ -o $OUTFILE\n" % \
- self.environment.get_source_dir()
- desc = " description = Creating pot file for package $PACKAGENAME.\n"
- outfile.write(rule)
- outfile.write(command)
- outfile.write(desc)
- outfile.write('\n')
- rule = 'rule GEN_GMO\n'
- command = ' command = msgfmt $INFILE -o $OUTFILE\n'
- desc = ' description = Generating gmo file $OUTFILE\n'
- outfile.write(rule)
- outfile.write(command)
- outfile.write(desc)
- outfile.write('\n')
-
- def generate_phony(self, outfile):
- outfile.write('# Phony build target, always out of date\n')
- outfile.write('build PHONY: phony\n')
- outfile.write('\n')
-
- def generate_jar_target(self, target, outfile):
- fname = target.get_filename()
- subdir = target.get_subdir()
- outname_rel = os.path.join(self.get_target_dir(target), fname)
- src_list = target.get_sources()
- class_list = []
- compiler = self.get_compiler_for_source(src_list[0])
- assert(compiler.get_language() == 'java')
- c = 'c'
- m = ''
- e = ''
- f = 'f'
- main_class = target.get_main_class()
- if main_class != '':
- e = 'e'
- for src in src_list:
- plain_class_path = self.generate_single_java_compile(src, target, compiler, outfile)
- class_list.append(plain_class_path)
- class_dep_list = [os.path.join(self.get_target_private_dir(target), i) for i in class_list]
- jar_rule = 'java_LINKER'
- commands = [c+m+e+f]
- if e != '':
- commands.append(main_class)
- commands.append(self.get_target_filename(target))
- for cls in class_list:
- commands += ['-C', self.get_target_private_dir(target), cls]
- elem = NinjaBuildElement(outname_rel, jar_rule, [])
- elem.add_dep(class_dep_list)
- elem.add_item('ARGS', commands)
- elem.write(outfile)
- self.check_outputs(elem)
-
- def generate_cs_resource_tasks(self, target, outfile):
- args = []
- deps = []
- for r in target.resources:
- rel_sourcefile = os.path.join(self.build_to_src, target.subdir, r)
- if r.endswith('.resources'):
- a = '-resource:' + rel_sourcefile
- elif r.endswith('.txt') or r.endswith('.resx'):
- ofilebase = os.path.splitext(os.path.basename(r))[0] + '.resources'
- ofilename = os.path.join(self.get_target_private_dir(target), ofilebase)
- elem = NinjaBuildElement(ofilename, "CUSTOM_COMMAND", rel_sourcefile)
- elem.add_item('COMMAND', ['resgen', rel_sourcefile, ofilename])
- elem.add_item('DESC', 'Compiling resource %s.' % rel_sourcefile)
- elem.write(outfile)
- self.check_outputs(elem)
- deps.append(ofilename)
- a = '-resource:' + ofilename
- else:
- raise InvalidArguments('Unknown resource file %s.' % r)
- args.append(a)
- return (args, deps)
-
- def generate_cs_target(self, target, outfile):
- buildtype = self.environment.coredata.get_builtin_option('buildtype')
- fname = target.get_filename()
- outname_rel = os.path.join(self.get_target_dir(target), fname)
- src_list = target.get_sources()
- compiler = self.get_compiler_for_source(src_list[0])
- assert(compiler.get_language() == 'cs')
- rel_srcs = [s.rel_to_builddir(self.build_to_src) for s in src_list]
- deps = []
- commands = target.extra_args.get('cs', [])
- commands += compiler.get_buildtype_args(buildtype)
- if isinstance(target, build.Executable):
- commands.append('-target:exe')
- elif isinstance(target, build.SharedLibrary):
- commands.append('-target:library')
- else:
- raise MesonException('Unknown C# target type.')
- (resource_args, resource_deps) = self.generate_cs_resource_tasks(target, outfile)
- commands += resource_args
- deps += resource_deps
- commands += compiler.get_output_args(outname_rel)
- for l in target.link_targets:
- lname = os.path.join(self.get_target_dir(l), l.get_filename())
- commands += compiler.get_link_args(lname)
- deps.append(lname)
- if '-g' in commands:
- outputs = [outname_rel, outname_rel + '.mdb']
- else:
- outputs = [outname_rel]
- elem = NinjaBuildElement(outputs, 'cs_COMPILER', rel_srcs)
- elem.add_dep(deps)
- elem.add_item('ARGS', commands)
- self.check_outputs(elem)
- elem.write(outfile)
-
- def generate_single_java_compile(self, src, target, compiler, outfile):
- args = []
- args += compiler.get_buildtype_args(self.environment.coredata.get_builtin_option('buildtype'))
- args += compiler.get_output_args(self.get_target_private_dir(target))
- for i in target.include_dirs:
- for idir in i.get_incdirs():
- args += ['-sourcepath', os.path.join(self.build_to_src, i.curdir, idir)]
- rel_src = src.rel_to_builddir(self.build_to_src)
- plain_class_path = src.fname[:-4] + 'class'
- rel_obj = os.path.join(self.get_target_private_dir(target), plain_class_path)
- element = NinjaBuildElement(rel_obj, compiler.get_language() + '_COMPILER', rel_src)
- element.add_item('ARGS', args)
- element.write(outfile)
- self.check_outputs(element)
- 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')
-
- def generate_fastvapi_compile(self, target, valac, outfile):
- fastvapis = {}
- for s in target.get_sources():
- if not s.endswith('.vala'):
- continue
- vapibase = os.path.basename(s.fname)[:-4] + 'vapi'
- rel_vapi = os.path.join(self.get_target_private_dir(target), vapibase)
- args = ['--fast-vapi=' + rel_vapi]
- rel_s = s.rel_to_builddir(self.build_to_src)
- element = NinjaBuildElement(rel_vapi, valac.get_language() + '_COMPILER', rel_s)
- element.add_item('ARGS', args)
- element.write(outfile)
- self.check_outputs(element)
- fastvapis[s] = (vapibase, rel_vapi)
- return fastvapis
-
- def split_vala_sources(self, sources):
- src = []
- vapi_src = []
- for s in sources:
- if s.endswith('.vapi'):
- vapi_src.append(s)
- else:
- src.append(s)
- return (src, vapi_src)
-
- def generate_vala_compile(self, target, outfile):
- """Vala is compiled into C. Set up all necessary build steps here."""
- valac = self.environment.coredata.compilers['vala']
- fast_vapis = self.generate_fastvapi_compile(target, valac, outfile)
- generated_c = []
- (src, vapi_src) = self.split_vala_sources(target.get_sources())
- vapi_src = [x.rel_to_builddir(self.build_to_src) for x in vapi_src]
- extra_dep_files = []
- for s in src:
- if not s.endswith('.vala'):
- continue
- args = ['-d', self.get_target_private_dir(target)]
- sc = os.path.basename(s.fname)[:-4] + 'c'
- args += ['-C']
- vapi_order_deps = []
- for (sourcefile, vapi_info) in fast_vapis.items():
- if sourcefile == s:
- continue
- (vapibase, rel_vapi) = vapi_info
- args += ['--use-fast-vapi=' + rel_vapi]
- vapi_order_deps.append(rel_vapi)
- relsc = os.path.join(self.get_target_private_dir(target), sc)
- rel_s = s.rel_to_builddir(self.build_to_src)
- args += ['--deps', relsc + '.d']
- if self.environment.coredata.get_builtin_option('werror'):
- args += valac.get_werror_args()
- for d in target.external_deps:
- if isinstance(d, dependencies.PkgConfigDependency):
- if d.name == 'glib-2.0' and d.version_requirement is not None \
- and d.version_requirement.startswith(('>=', '==')):
- args += ['--target-glib', d.version_requirement[2:]]
- args += ['--pkg', d.name]
- args += vapi_src
- extra_args = []
-
- for a in target.extra_args.get('vala', []):
- if isinstance(a, File):
- relname = a.rel_to_builddir(self.build_to_src)
- extra_dep_files.append(relname)
- extra_args.append(relname)
- else:
- extra_args.append(a)
- args += extra_args
- generated_c += [relsc]
- element = NinjaBuildElement(relsc, valac.get_language() + '_COMPILER', rel_s)
- element.add_item('ARGS', args)
- element.add_orderdep(vapi_order_deps)
- element.add_dep(extra_dep_files)
- element.write(outfile)
- self.check_outputs(element)
- return generated_c
-
- def generate_rust_target(self, target, outfile):
- rustc = self.environment.coredata.compilers['rust']
- relsrc = []
- for i in target.get_sources():
- if not rustc.can_compile(i):
- raise InvalidArguments('Rust target %s contains a non-rust source file.' % target.get_basename())
- relsrc.append(i.rel_to_builddir(self.build_to_src))
- target_name = os.path.join(target.subdir, target.get_filename())
- args = ['--crate-type']
- if isinstance(target, build.Executable):
- cratetype = 'bin'
- elif isinstance(target, build.SharedLibrary):
- cratetype = 'rlib'
- elif isinstance(target, build.StaticLibrary):
- cratetype = 'rlib'
- else:
- raise InvalidArguments('Unknown target type for rustc.')
- args.append(cratetype)
- args += rustc.get_buildtype_args(self.environment.coredata.get_builtin_option('buildtype'))
- depfile = target.name + '.d'
- args += ['--out-dir', target.subdir]
- args += ['--emit', 'dep-info', '--emit', 'link']
- orderdeps = [os.path.join(t.subdir, t.get_filename()) for t in target.link_targets]
- linkdirs = {}
- for d in target.link_targets:
- linkdirs[d.subdir] = True
- for d in linkdirs.keys():
- if d == '':
- d = '.'
- args += ['-L', d]
- element = NinjaBuildElement(target_name, 'rust_COMPILER', relsrc)
- if len(orderdeps) > 0:
- element.add_orderdep(orderdeps)
- element.add_item('ARGS', args)
- element.add_item('targetdep', depfile)
- element.add_item('cratetype', cratetype)
- element.write(outfile)
- self.check_outputs(element)
-
- def swift_module_file_name(self, target):
- return os.path.join(self.get_target_private_dir(target),
- self.target_swift_modulename(target) + '.swiftmodule')
-
- def target_swift_modulename(self, target):
- return target.name
-
- def is_swift_target(self, target):
- for s in target.sources:
- if s.endswith('swift'):
- return True
- return False
-
- def determine_swift_dep_modules(self, target):
- result = []
- for l in target.link_targets:
- if self.is_swift_target(l):
- result.append(self.swift_module_file_name(l))
- return result
-
- def determine_swift_dep_dirs(self, target):
- result = []
- for l in target.link_targets:
- result.append(self.get_target_private_dir_abs(l))
- return result
-
- def get_swift_link_deps(self, target):
- result = []
- for l in target.link_targets:
- result.append(self.get_target_filename(l))
- return result
-
- def split_swift_generated_sources(self, target):
- all_srcs = []
- for genlist in target.get_generated_sources():
- if isinstance(genlist, build.CustomTarget):
- for ifile in genlist.get_filename():
- rel = os.path.join(self.get_target_dir(genlist), ifile)
- all_srcs.append(rel)
- else:
- for ifile in genlist.get_outfilelist():
- rel = os.path.join(self.get_target_private_dir(target), ifile)
- all_srcs.append(rel)
- srcs = []
- others = []
- for i in all_srcs:
- if i.endswith('.swift'):
- srcs.append(i)
- else:
- others.append(i)
- return (srcs, others)
-
- def generate_swift_target(self, target, outfile):
- module_name = self.target_swift_modulename(target)
- swiftc = self.environment.coredata.compilers['swift']
- abssrc = []
- abs_headers = []
- header_imports = []
- for i in target.get_sources():
- if swiftc.can_compile(i):
- relsrc = i.rel_to_builddir(self.build_to_src)
- abss = os.path.normpath(os.path.join(self.environment.get_build_dir(), relsrc))
- abssrc.append(abss)
- elif self.environment.is_header(i):
- relh = i.rel_to_builddir(self.build_to_src)
- absh = os.path.normpath(os.path.join(self.environment.get_build_dir(), relh))
- abs_headers.append(absh)
- header_imports += swiftc.get_header_import_args(absh)
- else:
- raise InvalidArguments('Swift target %s contains a non-swift source file.' % target.get_basename())
- os.makedirs(self.get_target_private_dir_abs(target), exist_ok=True)
- compile_args = swiftc.get_compile_only_args()
- compile_args += swiftc.get_module_args(module_name)
- link_args = swiftc.get_output_args(os.path.join(self.environment.get_build_dir(), self.get_target_filename(target)))
- rundir = self.get_target_private_dir(target)
- out_module_name = self.swift_module_file_name(target)
- in_module_files = self.determine_swift_dep_modules(target)
- abs_module_dirs = self.determine_swift_dep_dirs(target)
- module_includes = []
- for x in abs_module_dirs:
- module_includes += swiftc.get_include_args(x)
- link_deps = self.get_swift_link_deps(target)
- abs_link_deps = [os.path.join(self.environment.get_build_dir(), x) for x in link_deps]
- (rel_generated, _) = self.split_swift_generated_sources(target)
- abs_generated = [os.path.join(self.environment.get_build_dir(), x) for x in rel_generated]
- # We need absolute paths because swiftc needs to be invoked in a subdir
- # and this is the easiest way about it.
- objects = [] # Relative to swift invocation dir
- rel_objects = [] # Relative to build.ninja
- for i in abssrc + abs_generated:
- base = os.path.split(i)[1]
- oname = os.path.splitext(base)[0] + '.o'
- objects.append(oname)
- rel_objects.append(os.path.join(self.get_target_private_dir(target), oname))
-
- # Swiftc does not seem to be able to emit objects and module files in one go.
- elem = NinjaBuildElement(rel_objects,
- 'swift_COMPILER',
- abssrc)
- elem.add_dep(in_module_files + rel_generated)
- elem.add_dep(abs_headers)
- elem.add_item('ARGS', compile_args + header_imports + abs_generated + module_includes)
- elem.add_item('RUNDIR', rundir)
- elem.write(outfile)
- self.check_outputs(elem)
- elem = NinjaBuildElement(out_module_name,
- 'swift_COMPILER',
- abssrc)
- elem.add_dep(in_module_files + rel_generated)
- elem.add_item('ARGS', compile_args + abs_generated + module_includes + swiftc.get_mod_gen_args())
- elem.add_item('RUNDIR', rundir)
- elem.write(outfile)
- self.check_outputs(elem)
- if isinstance(target, build.StaticLibrary):
- elem = self.generate_link(target, outfile, self.get_target_filename(target),
- rel_objects, self.build.static_linker)
- elem.write(outfile)
- elif isinstance(target, build.Executable):
- elem = NinjaBuildElement(self.get_target_filename(target), 'swift_COMPILER', [])
- elem.add_dep(rel_objects)
- elem.add_dep(link_deps)
- elem.add_item('ARGS', link_args + swiftc.get_std_exe_link_args() + objects + abs_link_deps)
- elem.add_item('RUNDIR', rundir)
- elem.write(outfile)
- self.check_outputs(elem)
- else:
- raise MesonException('Swift supports only executable and static library targets.')
-
- def generate_static_link_rules(self, is_cross, outfile):
- if self.build.has_language('java'):
- if not is_cross:
- self.generate_java_link(outfile)
- if is_cross:
- if self.environment.cross_info.need_cross_compiler():
- static_linker = self.build.static_cross_linker
- else:
- static_linker = self.build.static_linker
- crstr = '_CROSS'
- else:
- static_linker = self.build.static_linker
- crstr = ''
- if static_linker is None:
- return
- rule = 'rule STATIC%s_LINKER\n' % crstr
- if mesonlib.is_windows():
- command_templ = ''' command = %s @$out.rsp
- rspfile = $out.rsp
- rspfile_content = $LINK_ARGS %s $in
-'''
- else:
- command_templ = ' command = %s $LINK_ARGS %s $in\n'
- command = command_templ %\
- (' '.join(static_linker.get_exelist()),
- ' '.join(static_linker.get_output_args('$out')))
- description = ' description = Static linking library $out\n\n'
- outfile.write(rule)
- outfile.write(command)
- outfile.write(description)
-
- def generate_dynamic_link_rules(self, outfile):
- ctypes = [(self.build.compilers, False)]
- if self.environment.is_cross_build():
- if self.environment.cross_info.need_cross_compiler():
- ctypes.append((self.build.cross_compilers, True))
- else:
- # Native compiler masquerades as the cross compiler.
- ctypes.append((self.build.compilers, True))
- else:
- ctypes.append((self.build.cross_compilers, True))
- for (complist, is_cross) in ctypes:
- for compiler in complist:
- langname = compiler.get_language()
- if langname == 'java' or langname == 'vala' or\
- langname == 'rust' or langname == 'cs':
- continue
- crstr = ''
- cross_args = []
- if is_cross:
- crstr = '_CROSS'
- try:
- cross_args = self.environment.cross_info.config['properties'][langname + '_link_args']
- except KeyError:
- pass
- rule = 'rule %s%s_LINKER\n' % (langname, crstr)
- if mesonlib.is_windows():
- command_template = ''' command = %s @$out.rsp
- rspfile = $out.rsp
- rspfile_content = %s $ARGS %s $in $LINK_ARGS $aliasing
-'''
- else:
- command_template = ' command = %s %s $ARGS %s $in $LINK_ARGS $aliasing\n'
- command = command_template % \
- (' '.join(compiler.get_linker_exelist()),\
- ' '.join(cross_args),\
- ' '.join(compiler.get_linker_output_args('$out')))
- description = ' description = Linking target $out'
- outfile.write(rule)
- outfile.write(command)
- outfile.write(description)
- outfile.write('\n')
- scriptdir = self.environment.get_script_dir()
- outfile.write('\n')
- symrule = 'rule SHSYM\n'
- symcmd = ' command = "%s" "%s" %s %s %s %s $CROSS\n' % (ninja_quote(sys.executable),
- self.environment.get_build_command(),
- '--internal',
- 'symbolextractor',
- '$in',
- '$out')
- 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')
-
- def generate_java_compile_rule(self, compiler, outfile):
- rule = 'rule %s_COMPILER\n' % 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')
-
- def generate_cs_compile_rule(self, compiler, outfile):
- rule = 'rule %s_COMPILER\n' % compiler.get_language()
- invoc = ' '.join([ninja_quote(i) for i in compiler.get_exelist()])
- command = ' command = %s $ARGS $in\n' % invoc
- description = ' description = Compiling cs target $out.\n'
- outfile.write(rule)
- outfile.write(command)
- outfile.write(description)
- outfile.write('\n')
-
- def generate_vala_compile_rules(self, compiler, outfile):
- rule = 'rule %s_COMPILER\n' % 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.
- depfile = ' depfile = $out.d\n'
- depstyle = ' deps = gcc\n'
- outfile.write(rule)
- outfile.write(command)
- outfile.write(description)
- outfile.write(restat)
- outfile.write(depfile)
- outfile.write(depstyle)
- outfile.write('\n')
-
- def generate_rust_compile_rules(self, compiler, outfile):
- rule = 'rule %s_COMPILER\n' % compiler.get_language()
- 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')
-
- def generate_swift_compile_rules(self, compiler, outfile):
- rule = 'rule %s_COMPILER\n' % compiler.get_language()
- full_exe = [sys.executable,
- os.path.join(self.environment.get_script_dir(), 'dirchanger.py'),
- '$RUNDIR'] + compiler.get_exelist()
- invoc = ' '.join([ninja_quote(i) for i in full_exe])
- 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')
-
- def generate_fortran_dep_hack(self, outfile):
- 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
- command = %s
- description = Dep hack
- restat = 1
-
-'''
- outfile.write(template % cmd)
-
- def generate_compile_rule_for(self, langname, compiler, qstr, is_cross, outfile):
- if langname == 'java':
- if not is_cross:
- self.generate_java_compile_rule(compiler, outfile)
- return
- if langname == 'cs':
- if not is_cross:
- self.generate_cs_compile_rule(compiler, outfile)
- return
- if langname == 'vala':
- if not is_cross:
- self.generate_vala_compile_rules(compiler, outfile)
- return
- if langname == 'rust':
- if not is_cross:
- self.generate_rust_compile_rules(compiler, outfile)
- return
- if langname == 'swift':
- if not is_cross:
- self.generate_swift_compile_rules(compiler, outfile)
- return
- if langname == 'fortran':
- self.generate_fortran_dep_hack(outfile)
- if is_cross:
- crstr = '_CROSS'
- else:
- crstr = ''
- rule = 'rule %s%s_COMPILER\n' % (langname, crstr)
- depargs = compiler.get_dependency_gen_args('$out', '$DEPFILE')
- quoted_depargs = []
- for d in depargs:
- if d != '$out' and d != '$in':
- d = qstr % d
- quoted_depargs.append(d)
- cross_args = []
- if is_cross:
- try:
- cross_args = self.environment.cross_info.config['properties'][langname + '_args']
- except KeyError:
- pass
- if mesonlib.is_windows():
- command_template = ''' command = %s @$out.rsp
- rspfile = $out.rsp
- rspfile_content = %s $ARGS %s %s %s $in
-'''
- else:
- command_template = ' command = %s %s $ARGS %s %s %s $in\n'
- command = command_template % \
- (' '.join(compiler.get_exelist()),\
- ' '.join(cross_args),
- ' '.join(quoted_depargs),\
- ' '.join(compiler.get_output_args('$out')),\
- ' '.join(compiler.get_compile_only_args()))
- description = ' description = Compiling %s object $out\n' % langname
- if compiler.get_id() == 'msvc':
- deps = ' deps = msvc\n'
- else:
- deps = ' deps = gcc\n'
- deps += ' depfile = $DEPFILE\n'
- outfile.write(rule)
- outfile.write(command)
- outfile.write(deps)
- outfile.write(description)
- outfile.write('\n')
-
- def generate_pch_rule_for(self, langname, compiler, qstr, is_cross, outfile):
- if langname != 'c' and langname != 'cpp':
- return
- if is_cross:
- crstr = '_CROSS'
- else:
- crstr = ''
- rule = 'rule %s%s_PCH\n' % (langname, crstr)
- depargs = compiler.get_dependency_gen_args('$out', '$DEPFILE')
- cross_args = []
- if is_cross:
- try:
- cross_args = self.environment.cross_info.config['properties'][langname + '_args']
- except KeyError:
- pass
-
- quoted_depargs = []
- for d in depargs:
- if d != '$out' and d != '$in':
- d = qstr % d
- quoted_depargs.append(d)
- if compiler.get_id() == 'msvc':
- output = ''
- else:
- output = ' '.join(compiler.get_output_args('$out'))
- command = " command = %s %s $ARGS %s %s %s $in\n" % \
- (' '.join(compiler.get_exelist()),\
- ' '.join(cross_args),\
- ' '.join(quoted_depargs),\
- output,\
- ' '.join(compiler.get_compile_only_args()))
- description = ' description = Precompiling header %s\n' % '$in'
- if compiler.get_id() == 'msvc':
- deps = ' deps = msvc\n'
- else:
- deps = ' deps = gcc\n'
- deps += ' depfile = $DEPFILE\n'
- outfile.write(rule)
- outfile.write(command)
- outfile.write(deps)
- outfile.write(description)
- outfile.write('\n')
-
- def generate_compile_rules(self, outfile):
- qstr = quote_char + "%s" + quote_char
- for compiler in self.build.compilers:
- langname = compiler.get_language()
- self.generate_compile_rule_for(langname, compiler, qstr, False, outfile)
- self.generate_pch_rule_for(langname, compiler, qstr, False, outfile)
- if self.environment.is_cross_build():
- # In case we are going a target-only build, make the native compilers
- # masquerade as cross compilers.
- if self.environment.cross_info.need_cross_compiler():
- cclist = self.build.cross_compilers
- else:
- cclist = self.build.compilers
- for compiler in cclist:
- langname = compiler.get_language()
- self.generate_compile_rule_for(langname, compiler, qstr, True, outfile)
- self.generate_pch_rule_for(langname, compiler, qstr, True, outfile)
- outfile.write('\n')
-
- def replace_outputs(self, args, private_dir, output_list):
- newargs = []
- regex = re.compile('@OUTPUT(\d+)@')
- for arg in args:
- m = regex.search(arg)
- while m is not None:
- index = int(m.group(1))
- src = '@OUTPUT%d@' % index
- arg = arg.replace(src, os.path.join(private_dir, output_list[index]))
- m = regex.search(arg)
- newargs.append(arg)
- return newargs
-
- def generate_custom_generator_rules(self, target, outfile):
- for genlist in target.get_generated_sources():
- if isinstance(genlist, build.CustomTarget):
- continue # Customtarget has already written its output rules
- generator = genlist.get_generator()
- exe = generator.get_exe()
- exe_arr = self.exe_object_to_cmd_array(exe)
- infilelist = genlist.get_infilelist()
- outfilelist = genlist.get_outfilelist()
- base_args = generator.get_arglist()
- extra_dependencies = [os.path.join(self.build_to_src, i) for i in genlist.extra_depends]
- for i in range(len(infilelist)):
- if len(generator.outputs) == 1:
- sole_output = os.path.join(self.get_target_private_dir(target), outfilelist[i])
- else:
- sole_output = ''
- curfile = infilelist[i]
- infilename = os.path.join(self.build_to_src, curfile)
- outfiles = genlist.get_outputs_for(curfile)
- outfiles = [os.path.join(self.get_target_private_dir(target), of) for of in outfiles]
- args = [x.replace("@INPUT@", infilename).replace('@OUTPUT@', sole_output)\
- for x in base_args]
- args = self.replace_outputs(args, self.get_target_private_dir(target), outfilelist)
- # We have consumed output files, so drop them from the list of remaining outputs.
- if sole_output == '':
- outfilelist = outfilelist[len(generator.outputs):]
- relout = self.get_target_private_dir(target)
- args = [x.replace("@SOURCE_DIR@", self.build_to_src).replace("@BUILD_DIR@", relout)
- for x in args]
- final_args = []
- for a in args:
- if a == '@EXTRA_ARGS@':
- final_args += genlist.get_extra_args()
- else:
- final_args.append(a)
- cmdlist = exe_arr + final_args
- elem = NinjaBuildElement(outfiles, 'CUSTOM_COMMAND', infilename)
- if len(extra_dependencies) > 0:
- elem.add_dep(extra_dependencies)
- elem.add_item('DESC', 'Generating $out')
- if isinstance(exe, build.BuildTarget):
- elem.add_dep(self.get_target_filename(exe))
- elem.add_item('COMMAND', cmdlist)
- elem.write(outfile)
- self.check_outputs(elem)
-
- def scan_fortran_module_outputs(self, target):
- compiler = None
- for c in self.build.compilers:
- if c.get_language() == 'fortran':
- compiler = c
- break
- if compiler is None:
- self.fortran_deps[target.get_basename()] = {}
- return
- modre = re.compile(r"\s*module\s+(\w+)", re.IGNORECASE)
- module_files = {}
- for s in target.get_sources():
- # FIXME, does not work for generated Fortran sources,
- # but those are really rare. I hope.
- if not compiler.can_compile(s):
- continue
- for line in open(os.path.join(self.environment.get_source_dir(), s.subdir, s.fname)):
- modmatch = modre.match(line)
- if modmatch is not None:
- modname = modmatch.group(1)
- if modname.lower() == 'procedure': # MODULE PROCEDURE construct
- continue
- if modname in module_files:
- raise InvalidArguments('Namespace collision: module %s defined in two files %s and %s.' %
- (modname, module_files[modname], s))
- module_files[modname] = s
- self.fortran_deps[target.get_basename()] = module_files
-
- def get_fortran_deps(self, compiler, src, target):
- mod_files = []
- usere = re.compile(r"\s*use\s+(\w+)", re.IGNORECASE)
- dirname = self.get_target_private_dir(target)
- tdeps= self.fortran_deps[target.get_basename()]
- for line in open(src):
- usematch = usere.match(line)
- if usematch is not None:
- usename = usematch.group(1)
- if usename not in tdeps:
- # The module is not provided by any source file. This is due to
- # a) missing file/typo/etc
- # b) using a module provided by the compiler, such as OpenMP
- # There's no easy way to tell which is which (that I know of)
- # so just ignore this and go on. Ideally we would print a
- # warning message to the user but this is a common occurrance,
- # which would lead to lots of distracting noise.
- continue
- mod_source_file = tdeps[usename]
- # Check if a source uses a module it exports itself.
- # Potential bug if multiple targets have a file with
- # the same name.
- if mod_source_file.fname == os.path.split(src)[1]:
- continue
- mod_name = compiler.module_name_to_filename(usematch.group(1))
- mod_files.append(os.path.join(dirname, mod_name))
- return mod_files
-
- def generate_single_compile(self, target, outfile, src, is_generated=False, header_deps=[], order_deps=[]):
- if(isinstance(src, str) and src.endswith('.h')):
- raise RuntimeError('Fug')
- if isinstance(src, RawFilename) and src.fname.endswith('.h'):
- raise RuntimeError('Fug')
- extra_orderdeps = []
- compiler = self.get_compiler_for_source(src)
- commands = self.generate_basic_compiler_args(target, compiler)
- commands += compiler.get_include_args(self.get_target_private_dir(target), False)
- curdir = target.get_subdir()
- tmppath = os.path.normpath(os.path.join(self.build_to_src, curdir))
- commands += compiler.get_include_args(tmppath, False)
- if curdir == '':
- curdir = '.'
- commands += compiler.get_include_args(curdir, False)
- for d in target.external_deps:
- if d.need_threads():
- commands += compiler.thread_flags()
- break
- if isinstance(src, RawFilename):
- rel_src = src.fname
- elif is_generated:
- if self.has_dir_part(src):
- rel_src = src
- else:
- rel_src = os.path.join(self.get_target_private_dir(target), src)
- abs_src = os.path.join(self.environment.get_source_dir(), rel_src)
- else:
- if isinstance(src, File):
- rel_src = src.rel_to_builddir(self.build_to_src)
- else:
- raise build.InvalidArguments('Invalid source type.')
- abs_src = os.path.join(self.environment.get_build_dir(), rel_src)
- if isinstance(src, RawFilename):
- src_filename = src.fname
- elif isinstance(src, File):
- src_filename = src.fname
- elif os.path.isabs(src):
- src_filename = os.path.basename(src)
- else:
- src_filename = src
- obj_basename = src_filename.replace('/', '_').replace('\\', '_')
- rel_obj = os.path.join(self.get_target_private_dir(target), obj_basename)
- rel_obj += '.' + self.environment.get_object_suffix()
- dep_file = compiler.depfile_for_object(rel_obj)
- if self.environment.coredata.get_builtin_option('use_pch'):
- pchlist = target.get_pch(compiler.language)
- else:
- pchlist = []
- if len(pchlist) == 0:
- pch_dep = []
- else:
- arr = []
- i = os.path.join(self.get_target_private_dir(target), compiler.get_pch_name(pchlist[0]))
- arr.append(i)
- pch_dep = arr
- for i in target.get_include_dirs():
- basedir = i.get_curdir()
- for d in i.get_incdirs():
- expdir = os.path.join(basedir, d)
- srctreedir = os.path.join(self.build_to_src, expdir)
- bargs = compiler.get_include_args(expdir, i.is_system)
- sargs = compiler.get_include_args(srctreedir, i.is_system)
- commands += bargs
- commands += sargs
- for d in i.get_extra_build_dirs():
- commands += compiler.get_include_args(d, i.is_system)
- custom_target_include_dirs = []
- for i in target.generated:
- if isinstance(i, build.CustomTarget):
- idir = self.get_target_dir(i)
- if idir not in custom_target_include_dirs:
- custom_target_include_dirs.append(idir)
- for i in custom_target_include_dirs:
- commands+= compiler.get_include_args(i, False)
- if self.environment.coredata.get_builtin_option('use_pch'):
- commands += self.get_pch_include_args(compiler, target)
- crstr = ''
- if target.is_cross:
- crstr = '_CROSS'
- compiler_name = '%s%s_COMPILER' % (compiler.get_language(), crstr)
- extra_deps = []
- if compiler.get_language() == 'fortran':
- extra_deps += self.get_fortran_deps(compiler, abs_src, target)
- # Dependency hack. Remove once multiple outputs in Ninja is fixed:
- # https://groups.google.com/forum/#!topic/ninja-build/j-2RfBIOd_8
- for modname, srcfile in self.fortran_deps[target.get_basename()].items():
- modfile = os.path.join(self.get_target_private_dir(target),
- compiler.module_name_to_filename(modname))
- if srcfile == src:
- depelem = NinjaBuildElement(modfile, 'FORTRAN_DEP_HACK', rel_obj)
- depelem.write(outfile)
- self.check_outputs(depelem)
- commands += compiler.get_module_outdir_args(self.get_target_private_dir(target))
-
- element = NinjaBuildElement(rel_obj, compiler_name, rel_src)
- for d in header_deps:
- if isinstance(d, RawFilename):
- d = d.fname
- elif not self.has_dir_part(d):
- d = os.path.join(self.get_target_private_dir(target), d)
- element.add_dep(d)
- for d in extra_deps:
- element.add_dep(d)
- for d in order_deps:
- if isinstance(d, RawFilename):
- d = d.fname
- elif not self.has_dir_part(d):
- d = os.path.join(self.get_target_private_dir(target), d)
- element.add_orderdep(d)
- element.add_orderdep(pch_dep)
- element.add_orderdep(extra_orderdeps)
- for i in self.get_fortran_orderdeps(target, compiler):
- element.add_orderdep(i)
- element.add_item('DEPFILE', dep_file)
- element.add_item('ARGS', commands)
- element.write(outfile)
- self.check_outputs(element)
- return rel_obj
-
- def has_dir_part(self, fname):
- return '/' in fname or '\\' in fname
-
- # Fortran is a bit weird (again). When you link against a library, just compiling a source file
- # requires the mod files that are output when single files are built. To do this right we would need to
- # scan all inputs and write out explicit deps for each file. That is stoo slow and too much effort so
- # instead just have an ordered dependendy on the library. This ensures all required mod files are created.
- # The real deps are then detected via dep file generation from the compiler. This breaks on compilers that
- # produce incorrect dep files but such is life.
- def get_fortran_orderdeps(self, target, compiler):
- if compiler.language != 'fortran':
- return []
- return [os.path.join(self.get_target_dir(lt), lt.get_filename()) for lt in target.link_targets]
-
- def generate_msvc_pch_command(self, target, compiler, pch):
- if len(pch) != 2:
- raise RuntimeError('MSVC requires one header and one source to produce precompiled headers.')
- header = pch[0]
- source = pch[1]
- pchname = compiler.get_pch_name(header)
- dst = os.path.join(self.get_target_private_dir(target), pchname)
-
- commands = []
- commands += self.generate_basic_compiler_args(target, compiler)
- just_name = os.path.split(header)[1]
- (objname, pch_args) = compiler.gen_pch_args(just_name, source, dst)
- commands += pch_args
- dep = dst + '.' + compiler.get_depfile_suffix()
- return (commands, dep, dst, [objname])
-
- def generate_gcc_pch_command(self, target, compiler, pch):
- commands = []
- commands += self.generate_basic_compiler_args(target, compiler)
- dst = os.path.join(self.get_target_private_dir(target),
- os.path.split(pch)[-1] + '.' + compiler.get_pch_suffix())
- dep = dst + '.' + compiler.get_depfile_suffix()
- return (commands, dep, dst, []) # Gcc does not create an object file during pch generation.
-
- def generate_pch(self, target, outfile):
- cstr = ''
- pch_objects = []
- if target.is_cross:
- cstr = '_CROSS'
- for lang in ['c', 'cpp']:
- pch = target.get_pch(lang)
- if len(pch) == 0:
- continue
- if '/' not in pch[0] or '/' not in pch[-1]:
- raise build.InvalidArguments('Precompiled header of "%s" must not be in the same directory as source, please put it in a subdirectory.' % target.get_basename())
- compiler = self.get_compiler_for_lang(lang)
- if compiler.id == 'msvc':
- src = os.path.join(self.build_to_src, target.get_source_subdir(), pch[-1])
- (commands, dep, dst, objs) = self.generate_msvc_pch_command(target, compiler, pch)
- extradep = os.path.join(self.build_to_src, target.get_source_subdir(), pch[0])
- else:
- src = os.path.join(self.build_to_src, target.get_source_subdir(), pch[0])
- (commands, dep, dst, objs) = self.generate_gcc_pch_command(target, compiler, pch[0])
- extradep = None
- pch_objects += objs
- rulename = compiler.get_language() + cstr + '_PCH'
- elem = NinjaBuildElement(dst, rulename, src)
- if extradep is not None:
- elem.add_dep(extradep)
- elem.add_item('ARGS', commands)
- elem.add_item('DEPFILE', dep)
- elem.write(outfile)
- self.check_outputs(elem)
- return pch_objects
-
- def generate_shsym(self, outfile, target):
- target_name = self.get_target_filename(target)
- targetdir = self.get_target_private_dir(target)
- symname = os.path.join(targetdir, target_name + '.symbols')
- elem = NinjaBuildElement(symname, 'SHSYM', target_name)
- if self.environment.is_cross_build() and self.environment.cross_info.need_cross_compiler():
- elem.add_item('CROSS', '--cross-host=' + self.environment.cross_info.config['host_machine']['system'])
- elem.write(outfile)
- self.check_outputs(elem)
-
- def generate_link(self, target, outfile, outname, obj_list, linker, extra_args=[]):
- if isinstance(target, build.StaticLibrary):
- linker_base = 'STATIC'
- else:
- linker_base = linker.get_language() # Fixme.
- if isinstance(target, build.SharedLibrary):
- self.generate_shsym(outfile, target)
- crstr = ''
- if target.is_cross:
- crstr = '_CROSS'
- linker_rule = linker_base + crstr + '_LINKER'
- abspath = os.path.join(self.environment.get_build_dir(), target.subdir)
- commands = []
- commands += linker.get_linker_always_args()
- commands += linker.get_buildtype_linker_args(self.environment.coredata.get_builtin_option('buildtype'))
- commands += linker.get_option_link_args(self.environment.coredata.compiler_options)
- if not(isinstance(target, build.StaticLibrary)):
- commands += self.environment.coredata.external_link_args[linker.get_language()]
- if isinstance(target, build.Executable):
- commands += linker.get_std_exe_link_args()
- elif isinstance(target, build.SharedLibrary):
- commands += linker.get_std_shared_lib_link_args()
- commands += linker.get_pic_args()
- if hasattr(target, 'soversion'):
- soversion = target.soversion
- else:
- soversion = None
- commands += linker.get_soname_args(target.name, abspath, soversion)
- elif isinstance(target, build.StaticLibrary):
- commands += linker.get_std_link_args()
- else:
- raise RuntimeError('Unknown build target type.')
- # Link arguments of static libraries are not put in the command line of
- # the library. They are instead appended to the command line where
- # the static library is used.
- if linker_base == 'STATIC':
- dependencies = []
- else:
- dependencies = target.get_dependencies()
- commands += self.build_target_link_arguments(linker, dependencies)
- for d in target.external_deps:
- if d.need_threads():
- commands += linker.thread_link_flags()
- if not isinstance(target, build.StaticLibrary):
- commands += target.link_args
- # External deps must be last because target link libraries may depend on them.
- if not(isinstance(target, build.StaticLibrary)):
- for dep in target.get_external_deps():
- commands += dep.get_link_args()
- for d in target.get_dependencies():
- if isinstance(d, build.StaticLibrary):
- for dep in d.get_external_deps():
- commands += dep.get_link_args()
- commands += linker.build_rpath_args(self.environment.get_build_dir(),\
- self.determine_rpath_dirs(target), target.install_rpath)
- if self.environment.coredata.get_builtin_option('coverage'):
- commands += linker.get_coverage_link_args()
- custom_target_libraries = self.get_custom_target_provided_libraries(target)
- commands += extra_args
- commands += custom_target_libraries
- commands = linker.unixtype_flags_to_native(commands)
- dep_targets = [self.get_dependency_filename(t) for t in dependencies]
- dep_targets += [os.path.join(self.environment.source_dir,
- target.subdir, t) for t in target.link_depends]
- elem = NinjaBuildElement(outname, linker_rule, obj_list)
- elem.add_dep(dep_targets + custom_target_libraries)
- elem.add_item('LINK_ARGS', commands)
- self.check_outputs(elem)
- return elem
-
- def get_custom_target_provided_libraries(self, target):
- libs = []
- for t in target.get_generated_sources():
- if not isinstance(t, build.CustomTarget):
- continue
- for f in t.output:
- if self.environment.is_library(f):
- libs.append(os.path.join(self.get_target_dir(t), f))
- return libs
-
- def determine_rpath_dirs(self, target):
- link_deps = target.get_all_link_deps()
- result = []
- for ld in link_deps:
- prospective = self.get_target_dir(ld)
- if not prospective in result:
- result.append(prospective)
- return result
-
- def get_dependency_filename(self, t):
- if isinstance(t, build.SharedLibrary):
- return os.path.join(self.get_target_private_dir(t), self.get_target_filename(t) + '.symbols')
- return self.get_target_filename(t)
-
- def generate_shlib_aliases(self, target, outdir):
- basename = target.get_filename()
- aliases = target.get_aliaslist()
- if not mesonlib.is_windows():
- for alias in aliases:
- aliasfile = os.path.join(self.environment.get_build_dir(), outdir, alias)
- try:
- os.remove(aliasfile)
- except Exception:
- pass
- os.symlink(basename, aliasfile)
- else:
- mlog.debug("Library versioning disabled because host does not support symlinks.")
-
- def generate_gcov_clean(self, outfile):
- gcno_elem = NinjaBuildElement('clean-gcno', 'CUSTOM_COMMAND', 'PHONY')
- script_root = self.environment.get_script_dir()
- clean_script = os.path.join(script_root, 'delwithsuffix.py')
- gcno_elem.add_item('COMMAND', [sys.executable, clean_script, '.', 'gcno'])
- gcno_elem.add_item('description', 'Deleting gcno files')
- gcno_elem.write(outfile)
- self.check_outputs(gcno_elem)
-
- gcda_elem = NinjaBuildElement('clean-gcda', 'CUSTOM_COMMAND', 'PHONY')
- script_root = self.environment.get_script_dir()
- clean_script = os.path.join(script_root, 'delwithsuffix.py')
- gcda_elem.add_item('COMMAND', [sys.executable, clean_script, '.', 'gcda'])
- gcda_elem.add_item('description', 'Deleting gcda files')
- gcda_elem.write(outfile)
- self.check_outputs(gcda_elem)
-
- def is_compilable_file(self, filename):
- if filename.endswith('.cpp') or\
- filename.endswith('.c') or\
- filename.endswith('.cxx') or\
- filename.endswith('.cc') or\
- filename.endswith('.C'):
- return True
- return False
-
- def process_dep_gens(self, outfile, target):
- src_deps = []
- other_deps = []
- for rule in self.dep_rules.values():
- srcs = target.get_original_kwargs().get(rule.src_keyword, [])
- if isinstance(srcs, str):
- srcs = [srcs]
- for src in srcs:
- plainname = os.path.split(src)[1]
- basename = plainname.split('.')[0]
- outname = rule.name_templ.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname)
- outfilename = os.path.join(self.get_target_private_dir(target), outname)
- infilename = os.path.join(self.build_to_src, target.get_source_subdir(), src)
- elem = NinjaBuildElement(outfilename, rule.name, infilename)
- elem.write(outfile)
- self.check_outputs(elem)
- if self.is_compilable_file(outfilename):
- src_deps.append(outfilename)
- else:
- other_deps.append(outfilename)
- return (src_deps, other_deps)
-
- def generate_ending(self, outfile):
- targetlist = [self.get_target_filename(t) for t in self.build.get_targets().values()\
- if not isinstance(t, build.RunTarget)]
-
- elem = NinjaBuildElement('all', 'phony', targetlist)
- elem.write(outfile)
- self.check_outputs(elem)
-
- default = 'default all\n\n'
- outfile.write(default)
-
- ninja_command = environment.detect_ninja()
- if ninja_command is None:
- raise MesonException('Could not detect ninja command')
- elem = NinjaBuildElement('clean', 'CUSTOM_COMMAND', 'PHONY')
- elem.add_item('COMMAND', [ninja_command, '-t', 'clean'])
- elem.add_item('description', 'Cleaning')
- if self.environment.coredata.get_builtin_option('coverage'):
- self.generate_gcov_clean(outfile)
- elem.add_dep('clean-gcda')
- elem.add_dep('clean-gcno')
- elem.write(outfile)
- self.check_outputs(elem)
-
- deps = self.get_regen_filelist()
- elem = NinjaBuildElement('build.ninja', 'REGENERATE_BUILD', deps)
- elem.add_item('pool', 'console')
- elem.write(outfile)
-
- elem = NinjaBuildElement(deps, 'phony', '')
- elem.write(outfile)
- self.check_outputs(elem)