diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2016-01-23 20:02:52 +0200 |
---|---|---|
committer | Jussi Pakkanen <jpakkane@gmail.com> | 2016-01-23 20:02:52 +0200 |
commit | 7435df8399a9580441f3940b35543ee2933a7b12 (patch) | |
tree | d34e467b180deb2622f75f2fcc182b405bcc3af5 /mesonbuild/backend | |
parent | d6b205314618052e154901fa89579dc04a114011 (diff) | |
download | meson-7435df8399a9580441f3940b35543ee2933a7b12.zip meson-7435df8399a9580441f3940b35543ee2933a7b12.tar.gz meson-7435df8399a9580441f3940b35543ee2933a7b12.tar.bz2 |
Moved backends to their own module.
Diffstat (limited to 'mesonbuild/backend')
-rw-r--r-- | mesonbuild/backend/__init__.py | 0 | ||||
-rw-r--r-- | mesonbuild/backend/backends.py | 423 | ||||
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 1828 | ||||
-rw-r--r-- | mesonbuild/backend/vs2010backend.py | 646 | ||||
-rw-r--r-- | mesonbuild/backend/xcodebackend.py | 776 |
5 files changed, 3673 insertions, 0 deletions
diff --git a/mesonbuild/backend/__init__.py b/mesonbuild/backend/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/mesonbuild/backend/__init__.py diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py new file mode 100644 index 0000000..cab3b8f --- /dev/null +++ b/mesonbuild/backend/backends.py @@ -0,0 +1,423 @@ +# 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. + +import os, pickle, re +from .. import build +from .. import dependencies +from .. import mesonlib +import json +from ..coredata import MesonException + +class InstallData(): + def __init__(self, source_dir, build_dir, prefix, depfixer): + self.source_dir = source_dir + self.build_dir= build_dir + self.prefix = prefix + self.targets = [] + self.depfixer = depfixer + self.headers = [] + self.man = [] + self.data = [] + self.po_package_name = '' + self.po = [] + self.install_scripts = [] + self.install_subdirs = [] + +class TestSerialisation: + def __init__(self, name, suite, fname, is_cross, exe_wrapper, is_parallel, cmd_args, env, + should_fail, valgrind_args, timeout, workdir, extra_paths): + self.name = name + self.suite = suite + self.fname = fname + self.is_cross = is_cross + self.exe_runner = exe_wrapper + self.is_parallel = is_parallel + self.cmd_args = cmd_args + self.env = env + self.should_fail = should_fail + self.valgrind_args = valgrind_args + self.timeout = timeout + self.workdir = workdir + self.extra_paths = extra_paths + +# This class contains the basic functionality that is needed by all backends. +# Feel free to move stuff in and out of it as you see fit. +class Backend(): + def __init__(self, build): + self.build = build + self.environment = build.environment + self.processed_targets = {} + self.dep_rules = {} + self.build_to_src = os.path.relpath(self.environment.get_source_dir(), + self.environment.get_build_dir()) + for t in self.build.targets: + priv_dirname = self.get_target_private_dir_abs(t) + os.makedirs(priv_dirname, exist_ok=True) + + def get_compiler_for_lang(self, lang): + for i in self.build.compilers: + if i.language == lang: + return i + raise RuntimeError('No compiler for language ' + lang) + + def get_compiler_for_source(self, src): + for i in self.build.compilers: + if i.can_compile(src): + return i + if isinstance(src, mesonlib.File): + src = src.fname + raise RuntimeError('No specified compiler can handle file ' + src) + + def get_target_filename(self, target): + targetdir = self.get_target_dir(target) + fname = target.get_filename() + if isinstance(fname, list): + fname = fname[0] # HORROR, HORROR! Fix this. + filename = os.path.join(targetdir, fname) + return filename + + def get_target_dir(self, target): + if self.environment.coredata.get_builtin_option('layout') == 'mirror': + dirname = target.get_subdir() + else: + dirname = 'meson-out' + return dirname + + def get_target_private_dir(self, target): + dirname = os.path.join(self.get_target_dir(target), target.get_basename() + target.type_suffix()) + return dirname + + def get_target_private_dir_abs(self, target): + dirname = os.path.join(self.environment.get_build_dir(), self.get_target_private_dir(target)) + return dirname + + def generate_unity_files(self, target, unity_src): + langlist = {} + abs_files = [] + result = [] + for src in unity_src: + comp = self.get_compiler_for_source(src) + language = comp.get_language() + suffix = '.' + comp.get_default_suffix() + if language not in langlist: + outfilename = os.path.join(self.get_target_private_dir_abs(target), target.name + '-unity' + suffix) + outfileabs = os.path.join(self.environment.get_build_dir(), outfilename) + outfileabs_tmp = outfileabs + '.tmp' + abs_files.append(outfileabs) + outfile = open(outfileabs_tmp, 'w') + langlist[language] = outfile + result.append(outfilename) + ofile = langlist[language] + ofile.write('#include<%s>\n' % src) + [x.close() for x in langlist.values()] + [mesonlib.replace_if_different(x, x + '.tmp') for x in abs_files] + return result + + def relpath(self, todir, fromdir): + return os.path.relpath(os.path.join('dummyprefixdir', todir),\ + os.path.join('dummyprefixdir', fromdir)) + + def flatten_object_list(self, target, proj_dir_to_build_root=''): + obj_list = [] + for obj in target.get_objects(): + if isinstance(obj, str): + o = os.path.join(proj_dir_to_build_root, + self.build_to_src, target.get_subdir(), obj) + obj_list.append(o) + elif isinstance(obj, build.ExtractedObjects): + obj_list += self.determine_ext_objs(obj, proj_dir_to_build_root) + else: + raise MesonException('Unknown data type in object list.') + return obj_list + + def serialise_tests(self): + test_data = os.path.join(self.environment.get_scratch_dir(), 'meson_test_setup.dat') + datafile = open(test_data, 'wb') + self.write_test_file(datafile) + datafile.close() + benchmark_data = os.path.join(self.environment.get_scratch_dir(), 'meson_benchmark_setup.dat') + datafile = open(benchmark_data, 'wb') + self.write_benchmark_file(datafile) + datafile.close() + + def has_source_suffix(self, target, suffix): + for s in target.get_sources(): + if s.endswith(suffix): + return True + return False + + def has_vala(self, target): + return self.has_source_suffix(target, '.vala') + + def has_rust(self, target): + return self.has_source_suffix(target, '.rs') + + def has_cs(self, target): + return self.has_source_suffix(target, '.cs') + + def has_swift(self, target): + return self.has_source_suffix(target, '.swift') + + def determine_linker(self, target, src): + if isinstance(target, build.StaticLibrary): + return self.build.static_linker + if len(self.build.compilers) == 1: + return self.build.compilers[0] + # Currently a bit naive. C++ must + # be linked with a C++ compiler, but + # otherwise we don't care. This will + # become trickier if and when Fortran + # and the like become supported. + cpp = None + for c in self.build.compilers: + if c.get_language() == 'cpp': + cpp = c + break + if cpp is not None: + for s in src: + if c.can_compile(s): + return cpp + for c in self.build.compilers: + if c.get_language() != 'vala': + return c + raise RuntimeError('Unreachable code') + + def determine_ext_objs(self, extobj, proj_dir_to_build_root=''): + result = [] + targetdir = self.get_target_private_dir(extobj.target) + suffix = '.' + self.environment.get_object_suffix() + for osrc in extobj.srclist: + osrc_base = osrc.fname + if not self.source_suffix_in_objs: + osrc_base = '.'.join(osrc.split('.')[:-1]) + # If extracting in a subproject, the subproject + # name gets duplicated in the file name. + pathsegs = osrc.subdir.split(os.sep) + if pathsegs[0] == 'subprojects': + pathsegs = pathsegs[2:] + fixedpath = os.sep.join(pathsegs) + objbase = osrc.fname.replace('/', '_').replace('\\', '_') + objname = os.path.join(proj_dir_to_build_root, + targetdir, os.path.basename(objbase) + suffix) + result.append(objname) + return result + + def get_pch_include_args(self, compiler, target): + args = [] + pchpath = self.get_target_private_dir(target) + includeargs = compiler.get_include_args(pchpath, False) + for lang in ['c', 'cpp']: + p = target.get_pch(lang) + if len(p) == 0: + continue + if compiler.can_compile(p[-1]): + header = p[0] + args += compiler.get_pch_use_args(pchpath, header) + if len(args) > 0: + args = includeargs + args + return args + + def generate_basic_compiler_args(self, target, compiler): + commands = [] + commands += compiler.get_always_args() + if self.environment.coredata.get_builtin_option('buildtype') != 'plain': + commands += compiler.get_warn_args(self.environment.coredata.get_builtin_option('warning_level')) + commands += compiler.get_option_compile_args(self.environment.coredata.compiler_options) + commands += self.build.get_global_args(compiler) + commands += self.environment.coredata.external_args[compiler.get_language()] + commands += target.get_extra_args(compiler.get_language()) + commands += compiler.get_buildtype_args(self.environment.coredata.get_builtin_option('buildtype')) + if self.environment.coredata.get_builtin_option('coverage'): + commands += compiler.get_coverage_args() + if self.environment.coredata.get_builtin_option('werror'): + commands += compiler.get_werror_args() + if isinstance(target, build.SharedLibrary): + commands += compiler.get_pic_args() + for dep in target.get_external_deps(): + commands += dep.get_compile_args() + if isinstance(target, build.Executable): + commands += dep.get_exe_args() + + # Fortran requires extra include directives. + if compiler.language == 'fortran': + for lt in target.link_targets: + priv_dir = os.path.join(self.get_target_dir(lt), lt.get_basename() + lt.type_suffix()) + incflag = compiler.get_include_args(priv_dir, False) + commands += incflag + return commands + + def build_target_link_arguments(self, compiler, deps): + args = [] + for d in deps: + if not isinstance(d, build.StaticLibrary) and\ + not isinstance(d, build.SharedLibrary): + raise RuntimeError('Tried to link with a non-library target "%s".' % d.get_basename()) + fname = self.get_target_filename(d) + if compiler.id == 'msvc': + if fname.endswith('dll'): + fname = fname[:-3] + 'lib' + args.append(fname) + # If you have executable e that links to shared lib s1 that links to shared library s2 + # you have to specify s2 as well as s1 when linking e even if e does not directly use + # s2. Gcc handles this case fine but Clang does not for some reason. Thus we need to + # explictly specify all libraries every time. + args += self.build_target_link_arguments(compiler, d.get_dependencies()) + return args + + def determine_windows_extra_paths(self, target): + '''On Windows there is no such thing as an rpath. + We must determine all locations of DLLs that this exe + links to and return them so they can be used in unit + tests.''' + if not isinstance(target, build.Executable): + return [] + prospectives = target.get_transitive_link_deps() + result = [] + for ld in prospectives: + if ld == '' or ld == '.': + continue + dirseg = os.path.join(self.environment.get_build_dir(), self.get_target_dir(ld)) + if dirseg not in result: + result.append(dirseg) + return result + + def write_benchmark_file(self, datafile): + self.write_test_serialisation(self.build.get_benchmarks(), datafile) + + def write_test_file(self, datafile): + self.write_test_serialisation(self.build.get_tests(), datafile) + + def write_test_serialisation(self, tests, datafile): + arr = [] + for t in tests: + exe = t.get_exe() + if isinstance(exe, dependencies.ExternalProgram): + fname = exe.fullpath + else: + fname = [os.path.join(self.environment.get_build_dir(), self.get_target_filename(t.get_exe()))] + is_cross = self.environment.is_cross_build() and self.environment.cross_info.need_cross_compiler() + if is_cross: + exe_wrapper = self.environment.cross_info.config['binaries'].get('exe_wrapper', None) + else: + exe_wrapper = None + if mesonlib.is_windows(): + extra_paths = self.determine_windows_extra_paths(exe) + else: + extra_paths = [] + cmd_args = [] + for a in t.cmd_args: + if isinstance(a, mesonlib.File): + a = os.path.join(self.environment.get_build_dir(), a.rel_to_builddir(self.build_to_src)) + cmd_args.append(a) + ts = TestSerialisation(t.get_name(), t.suite, fname, is_cross, exe_wrapper, + t.is_parallel, cmd_args, t.env, t.should_fail, t.valgrind_args, + t.timeout, t.workdir, extra_paths) + arr.append(ts) + pickle.dump(arr, datafile) + + + def generate_depmf_install(self, d): + if self.build.dep_manifest_name is None: + return + ifilename = os.path.join(self.environment.get_build_dir(), 'depmf.json') + ofilename = os.path.join(self.environment.get_prefix(), self.build.dep_manifest_name) + mfobj = {'type': 'dependency manifest', + 'version': '1.0'} + mfobj['projects'] = self.build.dep_manifest + open(ifilename, 'w').write(json.dumps(mfobj)) + d.data.append([ifilename, ofilename]) + + def get_regen_filelist(self): + '''List of all files whose alteration means that the build + definition needs to be regenerated.''' + deps = [os.path.join(self.build_to_src, df) \ + for df in self.interpreter.get_build_def_files()] + if self.environment.is_cross_build(): + deps.append(os.path.join(self.build_to_src, + self.environment.coredata.cross_file)) + deps.append('meson-private/coredata.dat') + if os.path.exists(os.path.join(self.environment.get_source_dir(), 'meson_options.txt')): + deps.append(os.path.join(self.build_to_src, 'meson_options.txt')) + for sp in self.build.subprojects.keys(): + fname = os.path.join(self.environment.get_source_dir(), sp, 'meson_options.txt') + if os.path.isfile(fname): + deps.append(os.path.join(self.build_to_src, sp, 'meson_options.txt')) + return deps + + def exe_object_to_cmd_array(self, exe): + if self.environment.is_cross_build() and \ + isinstance(exe, build.BuildTarget) and exe.is_cross: + if 'exe_wrapper' not in self.environment.cross_info: + s = 'Can not use target %s as a generator because it is cross-built\n' + s += 'and no exe wrapper is defined. You might want to set it to native instead.' + s = s % exe.name + raise MesonException(s) + if isinstance(exe, build.BuildTarget): + exe_arr = [os.path.join(self.environment.get_build_dir(), self.get_target_filename(exe))] + else: + exe_arr = exe.get_command() + return exe_arr + + def eval_custom_target_command(self, target, absolute_paths=False): + ofilenames = [os.path.join(self.get_target_dir(target), i) for i in target.output] + srcs = [] + outdir = self.get_target_dir(target) + # Many external programs fail on empty arguments. + if outdir == '': + outdir = '.' + if absolute_paths: + outdir = os.path.join(self.environment.get_build_dir(), outdir) + for i in target.sources: + if isinstance(i, str): + fname = os.path.join(self.build_to_src, target.subdir, i) + else: + fname = i.rel_to_builddir(self.build_to_src) + if absolute_paths: + fname = os.path.join(self.environment.get_build_dir(), fname) + srcs.append(fname) + cmd = [] + for i in target.command: + if isinstance(i, build.Executable): + cmd += self.exe_object_to_cmd_array(i) + continue + if isinstance(i, build.CustomTarget): + # GIR scanner will attempt to execute this binary but + # it assumes that it is in path, so always give it a full path. + tmp = i.get_filename()[0] + i = os.path.join(self.get_target_dir(i), tmp) + for (j, src) in enumerate(srcs): + i = i.replace('@INPUT%d@' % j, src) + for (j, res) in enumerate(ofilenames): + i = i.replace('@OUTPUT%d@' % j, res) + if i == '@INPUT@': + cmd += srcs + elif i == '@OUTPUT@': + cmd += ofilenames + else: + if '@OUTDIR@' in i: + i = i.replace('@OUTDIR@', outdir) + elif '@PRIVATE_OUTDIR_' in i: + match = re.search('@PRIVATE_OUTDIR_(ABS_)?([-a-zA-Z0-9.@:]*)@', i) + source = match.group(0) + if match.group(1) is None and not absolute_paths: + lead_dir = '' + else: + lead_dir = self.environment.get_build_dir() + target_id = match.group(2) + i = i.replace(source, + os.path.join(lead_dir, + outdir)) + cmd.append(i) + cmd = [i.replace('\\', '/') for i in cmd] + return (srcs, ofilenames, cmd) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py new file mode 100644 index 0000000..e748def --- /dev/null +++ b/mesonbuild/backend/ninjabackend.py @@ -0,0 +1,1828 @@ +# 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): + vala_output_files = self.generate_vala_compile(target, outfile) + gen_src_deps += vala_output_files + 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. + if self.environment.is_header(src): + header_deps.append(src) + else: + 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 = [sys.executable, self.environment.get_build_command(), '--internal', 'commandrunner'] + 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 = 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 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 determine_dep_vapis(self, target): + result = [] + for dep in target.link_targets: + for i in dep.sources: + if hasattr(i, 'fname'): + i = i.fname + if i.endswith('vala'): + vapiname = os.path.splitext(os.path.split(i)[1])[0] + '.vapi' + fullname = os.path.join(self.get_target_dir(dep), vapiname) + result.append(fullname) + break + return result + + 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'] + (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 = [] + vala_input_files = [] + for s in src: + if s.endswith('.vala'): + vala_input_files.append(s.rel_to_builddir(self.build_to_src)) + if len(src) == 0: + raise InvalidArguments('Vala library has no Vala source files.') + namebase = os.path.splitext(os.path.split(src[0].fname)[1])[0] + base_h = namebase + '.h' + base_vapi = namebase + '.vapi' + hname = os.path.normpath(os.path.join(self.get_target_dir(target), base_h)) + vapiname = os.path.normpath(os.path.join(self.get_target_dir(target), base_vapi)) + + generated_c_files = [] + outputs = [vapiname] + args = ['-d', self.get_target_private_dir(target)] + args += ['-C']#, '-o', cname] + if not isinstance(target, build.Executable): + outputs.append(hname) + args += ['-H', hname] + args += ['--library=' + target.name] + args += ['--vapi=' + os.path.join('..', base_vapi)] + for src in vala_input_files: + namebase = os.path.splitext(os.path.split(src)[1])[0] + '.c' + full_c = os.path.join(self.get_target_private_dir(target), namebase) + generated_c_files.append(full_c) + outputs.append(full_c) + 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] + 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) + dependency_vapis = self.determine_dep_vapis(target) + extra_dep_files += dependency_vapis + args += extra_args + args += dependency_vapis + element = NinjaBuildElement(outputs, + valac.get_language() + '_COMPILER', + vala_input_files + vapi_src) + element.add_item('ARGS', args) + element.add_dep(extra_dep_files) + element.write(outfile) + self.check_outputs(element) + return generated_c_files + + 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. + outfile.write(rule) + outfile.write(command) + outfile.write(description) + outfile.write(restat) + 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, + self.environment.get_build_command(), + '--internal', + 'dirchanger', + '$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) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py new file mode 100644 index 0000000..c9fe09f --- /dev/null +++ b/mesonbuild/backend/vs2010backend.py @@ -0,0 +1,646 @@ +# Copyright 2014-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. + +import os, sys +import pickle +from . import backends +from .. import build +from .. import dependencies +from .. import mlog +import xml.etree.ElementTree as ET +import xml.dom.minidom +from ..coredata import MesonException + +class RegenInfo(): + def __init__(self, source_dir, build_dir, depfiles, solutionfile): + self.source_dir = source_dir + self.build_dir = build_dir + self.depfiles = depfiles + self.solutionfile = solutionfile + +class Vs2010Backend(backends.Backend): + def __init__(self, build): + super().__init__(build) + self.project_file_version = '10.0.30319.1' + # foo.c compiles to foo.obj, not foo.c.obj + self.source_suffix_in_obj = False + + def generate_custom_generator_commands(self, target, parent_node): + idgroup = ET.SubElement(parent_node, 'ItemDefinitionGroup') + all_output_files = [] + for genlist in target.get_generated_sources(): + if isinstance(genlist, build.CustomTarget): + all_output_files += [os.path.join(self.get_target_dir(genlist), i) for i in genlist.output] + else: + generator = genlist.get_generator() + exe = generator.get_exe() + infilelist = genlist.get_infilelist() + outfilelist = genlist.get_outfilelist() + if isinstance(exe, build.BuildTarget): + exe_file = os.path.join(self.environment.get_build_dir(), self.get_target_filename(exe)) + else: + exe_file = exe.get_command() + base_args = generator.get_arglist() + for i in range(len(infilelist)): + if len(infilelist) == len(outfilelist): + sole_output = os.path.join(self.get_target_private_dir(target), outfilelist[i]) + else: + sole_output = '' + curfile = infilelist[i] + infilename = os.path.join(self.environment.get_source_dir(), curfile) + outfiles = genlist.get_outputs_for(curfile) + outfiles = [os.path.join(self.get_target_private_dir(target), of) for of in outfiles] + all_output_files += outfiles + args = [x.replace("@INPUT@", infilename).replace('@OUTPUT@', sole_output)\ + for x in base_args] + args = [x.replace("@SOURCE_DIR@", self.environment.get_source_dir()).replace("@BUILD_DIR@", self.get_target_private_dir(target)) + for x in args] + fullcmd = [exe_file] + args + cbs = ET.SubElement(idgroup, 'CustomBuildStep') + ET.SubElement(cbs, 'Command').text = ' '.join(self.special_quote(fullcmd)) + ET.SubElement(cbs, 'Inputs').text = infilename + ET.SubElement(cbs, 'Outputs').text = ';'.join(outfiles) + ET.SubElement(cbs, 'Message').text = 'Generating sources from %s.' % infilename + pg = ET.SubElement(parent_node, 'PropertyGroup') + ET.SubElement(pg, 'CustomBuildBeforeTargets').text = 'ClCompile' + return all_output_files + + def generate(self, interp): + self.interpreter = interp + self.platform = 'Win32' + self.buildtype = self.environment.coredata.get_builtin_option('buildtype') + sln_filename = os.path.join(self.environment.get_build_dir(), self.build.project_name + '.sln') + projlist = self.generate_projects() + self.gen_testproj('RUN_TESTS', os.path.join(self.environment.get_build_dir(), 'RUN_TESTS.vcxproj')) + self.gen_regenproj('REGEN', os.path.join(self.environment.get_build_dir(), 'REGEN.vcxproj')) + self.generate_solution(sln_filename, projlist) + self.generate_regen_info(sln_filename) + open(os.path.join(self.environment.get_scratch_dir(), 'regen.stamp'), 'wb') + rulefile = os.path.join(self.environment.get_scratch_dir(), 'regen.rule') + if not os.path.exists(rulefile): + open(rulefile, 'w').write("# For some reason this needs to be here.") + + def generate_regen_info(self, sln_filename): + deps = self.get_regen_filelist() + regeninfo = RegenInfo(self.environment.get_source_dir(), + self.environment.get_build_dir(), + deps, + sln_filename) + pickle.dump(regeninfo, open(os.path.join(self.environment.get_scratch_dir(), 'regeninfo.dump'), 'wb')) + + def get_obj_target_deps(self, obj_list): + result = {} + for o in obj_list: + if isinstance(o, build.ExtractedObjects): + result[o.target.get_basename()] = True + return result.keys() + + def determine_deps(self, p): + all_deps = {} + target = self.build.targets[p[0]] + if isinstance(target, build.CustomTarget): + for d in target.dependencies: + all_deps[d.get_id()] = True + return all_deps + if isinstance(target, build.RunTarget): + for d in [target.command] + target.args: + if isinstance(d, build.BuildTarget): + all_deps[d.get_id()] = True + return all_deps + for ldep in target.link_targets: + all_deps[ldep.get_id()] = True + for objdep in self.get_obj_target_deps(target.objects): + all_deps[objdep] = True + for gendep in target.generated: + if isinstance(gendep, build.CustomTarget): + all_deps[gendep.get_id()] = True + else: + gen_exe = gendep.generator.get_exe() + if isinstance(gen_exe, build.Executable): + all_deps[gen_exe.get_id()] = True + return all_deps + + def generate_solution(self, sln_filename, projlist): + ofile = open(sln_filename, 'w') + ofile.write('Microsoft Visual Studio Solution File, Format Version 11.00\n') + ofile.write('# Visual Studio 2010\n') + prj_templ = prj_line = 'Project("{%s}") = "%s", "%s", "{%s}"\n' + for p in projlist: + prj_line = prj_templ % (self.environment.coredata.guid, p[0], p[1], p[2]) + ofile.write(prj_line) + all_deps = self.determine_deps(p) + ofile.write('\tProjectSection(ProjectDependencies) = postProject\n') + regen_guid = self.environment.coredata.regen_guid + ofile.write('\t\t{%s} = {%s}\n' % (regen_guid, regen_guid)) + for dep in all_deps.keys(): + guid = self.environment.coredata.target_guids[dep] + ofile.write('\t\t{%s} = {%s}\n' % (guid, guid)) + ofile.write('EndProjectSection\n') + ofile.write('EndProject\n') + test_line = prj_templ % (self.environment.coredata.guid, + 'RUN_TESTS', 'RUN_TESTS.vcxproj', self.environment.coredata.test_guid) + ofile.write(test_line) + ofile.write('EndProject\n') + regen_line = prj_templ % (self.environment.coredata.guid, + 'REGEN', 'REGEN.vcxproj', self.environment.coredata.regen_guid) + ofile.write(regen_line) + ofile.write('EndProject\n') + ofile.write('Global\n') + ofile.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n') + ofile.write('\t\t%s|%s = %s|%s\n' % (self.buildtype, self.platform, self.buildtype, self.platform)) + ofile.write('\tEndGlobalSection\n') + ofile.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n') + ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' % + (self.environment.coredata.regen_guid, self.buildtype, self.platform, + self.buildtype, self.platform)) + ofile.write('\t\t{%s}.%s|%s.Build.0 = %s|%s\n' % + (self.environment.coredata.regen_guid, self.buildtype, self.platform, + self.buildtype, self.platform)) + for p in projlist: + ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' % + (p[2], self.buildtype, self.platform, + self.buildtype, self.platform)) + if not isinstance(self.build.targets[p[0]], build.RunTarget): + ofile.write('\t\t{%s}.%s|%s.Build.0 = %s|%s\n' % + (p[2], self.buildtype, self.platform, + self.buildtype, self.platform)) + ofile.write('\t\t{%s}.%s|%s.ActiveCfg = %s|%s\n' % + (self.environment.coredata.test_guid, self.buildtype, self.platform, + self.buildtype, self.platform)) + ofile.write('\tEndGlobalSection\n') + ofile.write('\tGlobalSection(SolutionProperties) = preSolution\n') + ofile.write('\t\tHideSolutionNode = FALSE\n') + ofile.write('\tEndGlobalSection\n') + ofile.write('EndGlobal\n') + + def generate_projects(self): + projlist = [] + comp = None + for l, c in self.environment.coredata.compilers.items(): + if l == 'c' or l == 'cpp': + comp = c + break + if comp is None: + raise RuntimeError('C and C++ compilers missing.') + for name, target in self.build.targets.items(): + outdir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target)) + fname = name + '.vcxproj' + relname = os.path.join(target.subdir, fname) + projfile = os.path.join(outdir, fname) + uuid = self.environment.coredata.target_guids[name] + self.gen_vcxproj(target, projfile, uuid, comp) + projlist.append((name, relname, uuid)) + return projlist + + def split_sources(self, srclist): + sources = [] + headers = [] + for i in srclist: + if self.environment.is_header(i): + headers.append(i) + else: + sources.append(i) + return (sources, headers) + + def target_to_build_root(self, target): + if target.subdir == '': + return '' + + directories = os.path.split(target.subdir) + directories = list(filter(bool,directories)) #Filter out empty strings + + return '/'.join(['..']*len(directories)) + + def special_quote(self, arr): + return ['"%s"' % i for i in arr] + + def create_basic_crap(self, target): + project_name = target.name + root = ET.Element('Project', {'DefaultTargets' : "Build", + 'ToolsVersion' : '4.0', + 'xmlns' : 'http://schemas.microsoft.com/developer/msbuild/2003'}) + confitems = ET.SubElement(root, 'ItemGroup', {'Label' : 'ProjectConfigurations'}) + prjconf = ET.SubElement(confitems, 'ProjectConfiguration', + {'Include' : self.buildtype + '|' + self.platform}) + p = ET.SubElement(prjconf, 'Configuration') + p.text= self.buildtype + pl = ET.SubElement(prjconf, 'Platform') + pl.text = self.platform + globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals') + guidelem = ET.SubElement(globalgroup, 'ProjectGuid') + guidelem.text = self.environment.coredata.test_guid + kw = ET.SubElement(globalgroup, 'Keyword') + kw.text = self.platform + 'Proj' + p = ET.SubElement(globalgroup, 'Platform') + p.text= self.platform + pname= ET.SubElement(globalgroup, 'ProjectName') + pname.text = project_name + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.Default.props') + type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration') + ET.SubElement(type_config, 'ConfigurationType') + ET.SubElement(type_config, 'CharacterSet').text = 'MultiByte' + ET.SubElement(type_config, 'UseOfMfc').text = 'false' + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.props') + direlem = ET.SubElement(root, 'PropertyGroup') + fver = ET.SubElement(direlem, '_ProjectFileVersion') + fver.text = self.project_file_version + outdir = ET.SubElement(direlem, 'OutDir') + outdir.text = '.\\' + intdir = ET.SubElement(direlem, 'IntDir') + intdir.text = 'test-temp\\' + tname = ET.SubElement(direlem, 'TargetName') + tname.text = target.name + return root + + def gen_run_target_vcxproj(self, target, ofname, guid): + root = self.create_basic_crap(target) + action = ET.SubElement(root, 'ItemDefinitionGroup') + customstep = ET.SubElement(action, 'PostBuildEvent') + cmd_raw = [target.command] + target.args + cmd = [sys.executable, os.path.join(self.environment.get_script_dir(), 'commandrunner.py'), + self.environment.get_build_dir(), self.environment.get_source_dir(), + self.get_target_dir(target)] + for i in cmd_raw: + if isinstance(i, build.BuildTarget): + cmd.append(os.path.join(self.environment.get_build_dir(), self.get_target_filename(i))) + elif isinstance(i, dependencies.ExternalProgram): + cmd += i.fullpath + else: + cmd.append(i) + cmd_templ = '''"%s" '''*len(cmd) + ET.SubElement(customstep, 'Command').text = cmd_templ % tuple(cmd) + ET.SubElement(customstep, 'Message').text = 'Running custom command.' + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets') + tree = ET.ElementTree(root) + tree.write(ofname, encoding='utf-8', xml_declaration=True) + + def gen_custom_target_vcxproj(self, target, ofname, guid): + root = self.create_basic_crap(target) + action = ET.SubElement(root, 'ItemDefinitionGroup') + customstep = ET.SubElement(action, 'CustomBuildStep') + (srcs, ofilenames, cmd) = self.eval_custom_target_command(target, True) + cmd_templ = '''"%s" '''*len(cmd) + ET.SubElement(customstep, 'Command').text = cmd_templ % tuple(cmd) + ET.SubElement(customstep, 'Outputs').text = ';'.join([os.path.join(self.environment.get_build_dir(), i)\ + for i in ofilenames]) + ET.SubElement(customstep, 'Inputs').text = ';'.join([os.path.join(self.environment.get_build_dir(), i) \ + for i in srcs]) + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets') + tree = ET.ElementTree(root) + tree.write(ofname, encoding='utf-8', xml_declaration=True) + + def gen_vcxproj(self, target, ofname, guid, compiler): + mlog.debug('Generating vcxproj %s.' % target.name) + entrypoint = 'WinMainCRTStartup' + subsystem = 'Windows' + if isinstance(target, build.Executable): + conftype = 'Application' + if not target.gui_app: + subsystem = 'Console' + entrypoint = 'mainCRTStartup' + elif isinstance(target, build.StaticLibrary): + conftype = 'StaticLibrary' + elif isinstance(target, build.SharedLibrary): + conftype = 'DynamicLibrary' + entrypoint = '_DllMainCrtStartup' + elif isinstance(target, build.CustomTarget): + return self.gen_custom_target_vcxproj(target, ofname, guid) + elif isinstance(target, build.RunTarget): + return self.gen_run_target_vcxproj(target, ofname, guid) + else: + raise MesonException('Unknown target type for %s' % target.get_basename()) + down = self.target_to_build_root(target) + proj_to_src_root = os.path.join(down, self.build_to_src) + proj_to_src_dir = os.path.join(proj_to_src_root, target.subdir) + (sources, headers) = self.split_sources(target.sources) + buildtype = self.buildtype + project_name = target.name + target_name = target.name + root = ET.Element('Project', {'DefaultTargets' : "Build", + 'ToolsVersion' : '4.0', + 'xmlns' : 'http://schemas.microsoft.com/developer/msbuild/2003'}) + confitems = ET.SubElement(root, 'ItemGroup', {'Label' : 'ProjectConfigurations'}) + prjconf = ET.SubElement(confitems, 'ProjectConfiguration', + {'Include' : self.buildtype + '|' + self.platform}) + p = ET.SubElement(prjconf, 'Configuration') + p.text= buildtype + pl = ET.SubElement(prjconf, 'Platform') + pl.text = self.platform + globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals') + guidelem = ET.SubElement(globalgroup, 'ProjectGuid') + guidelem.text = guid + kw = ET.SubElement(globalgroup, 'Keyword') + kw.text = self.platform + 'Proj' + ns = ET.SubElement(globalgroup, 'RootNamespace') + ns.text = target_name + p = ET.SubElement(globalgroup, 'Platform') + p.text= self.platform + pname= ET.SubElement(globalgroup, 'ProjectName') + pname.text = project_name + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.Default.props') + type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration') + ET.SubElement(type_config, 'ConfigurationType').text = conftype + ET.SubElement(type_config, 'CharacterSet').text = 'MultiByte' + ET.SubElement(type_config, 'WholeProgramOptimization').text = 'false' + ET.SubElement(type_config, 'UseDebugLibraries').text = 'true' + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.props') + generated_files = self.generate_custom_generator_commands(target, root) + (gen_src, gen_hdrs) = self.split_sources(generated_files) + direlem = ET.SubElement(root, 'PropertyGroup') + fver = ET.SubElement(direlem, '_ProjectFileVersion') + fver.text = self.project_file_version + outdir = ET.SubElement(direlem, 'OutDir') + outdir.text = '.\\' + intdir = ET.SubElement(direlem, 'IntDir') + intdir.text = os.path.join(self.get_target_dir(target), target.get_basename() + '.dir') + '\\' + tname = ET.SubElement(direlem, 'TargetName') + tname.text = target_name + inclinc = ET.SubElement(direlem, 'LinkIncremental') + inclinc.text = 'true' + + compiles = ET.SubElement(root, 'ItemDefinitionGroup') + clconf = ET.SubElement(compiles, 'ClCompile') + opt = ET.SubElement(clconf, 'Optimization') + opt.text = 'disabled' + inc_dirs = [proj_to_src_dir, self.get_target_private_dir(target)] + cur_dir = target.subdir + if cur_dir == '': + cur_dir= '.' + inc_dirs.append(cur_dir) + extra_args = [] + # SUCKS, VS can not handle per-language type flags, so just use + # them all. + extra_args += compiler.get_buildtype_args(self.buildtype) + for l in self.environment.coredata.external_args.values(): + for a in l: + extra_args.append(a) + for l in self.build.global_args.values(): + for a in l: + extra_args.append(a) + for l in target.extra_args.values(): + for a in l: + extra_args.append(a) + # FIXME all the internal flags of VS (optimization etc) are represented + # by their own XML elements. In theory we should split all flags to those + # that have an XML element and those that don't and serialise them + # properly. This is a crapton of work for no real gain, so just dump them + # here. + extra_args = compiler.get_option_compile_args(self.environment.coredata.compiler_options) + if len(extra_args) > 0: + extra_args.append('%(AdditionalOptions)') + ET.SubElement(clconf, "AdditionalOptions").text = ' '.join(extra_args) + for d in target.include_dirs: + for i in d.incdirs: + curdir = os.path.join(d.curdir, i) + inc_dirs.append(self.relpath(curdir, target.subdir)) # build dir + inc_dirs.append(os.path.join(proj_to_src_root, curdir)) # src dir + inc_dirs.append('%(AdditionalIncludeDirectories)') + ET.SubElement(clconf, 'AdditionalIncludeDirectories').text = ';'.join(inc_dirs) + preproc = ET.SubElement(clconf, 'PreprocessorDefinitions') + rebuild = ET.SubElement(clconf, 'MinimalRebuild') + rebuild.text = 'true' + rtlib = ET.SubElement(clconf, 'RuntimeLibrary') + rtlib.text = 'MultiThreadedDebugDLL' + funclink = ET.SubElement(clconf, 'FunctionLevelLinking') + funclink.text = 'true' + pch = ET.SubElement(clconf, 'PrecompiledHeader') + warnings = ET.SubElement(clconf, 'WarningLevel') + warnings.text = 'Level3' + debinfo = ET.SubElement(clconf, 'DebugInformationFormat') + debinfo.text = 'EditAndContinue' + resourcecompile = ET.SubElement(compiles, 'ResourceCompile') + ET.SubElement(resourcecompile, 'PreprocessorDefinitions') + link = ET.SubElement(compiles, 'Link') + # Put all language args here, too. + extra_link_args = compiler.get_option_link_args(self.environment.coredata.compiler_options) + extra_link_args += compiler.get_buildtype_linker_args(self.buildtype) + for l in self.environment.coredata.external_link_args.values(): + for a in l: + extra_link_args.append(a) + for l in target.link_args: + for a in l: + extra_link_args.append(a) + if len(extra_args) > 0: + extra_args.append('%(AdditionalOptions)') + ET.SubElement(link, "AdditionalOptions").text = ' '.join(extra_args) + + additional_links = [] + for t in target.link_targets: + lobj = self.build.targets[t.get_id()] + rel_path = self.relpath(lobj.subdir, target.subdir) + linkname = os.path.join(rel_path, lobj.get_import_filename()) + additional_links.append(linkname) + for o in self.flatten_object_list(target, down): + assert(isinstance(o, str)) + additional_links.append(o) + if len(additional_links) > 0: + additional_links.append('%(AdditionalDependencies)') + ET.SubElement(link, 'AdditionalDependencies').text = ';'.join(additional_links) + ofile = ET.SubElement(link, 'OutputFile') + ofile.text = '$(OutDir)%s' % target.get_filename() + addlibdir = ET.SubElement(link, 'AdditionalLibraryDirectories') + addlibdir.text = '%(AdditionalLibraryDirectories)' + subsys = ET.SubElement(link, 'SubSystem') + subsys.text = subsystem + gendeb = ET.SubElement(link, 'GenerateDebugInformation') + gendeb.text = 'true' + if isinstance(target, build.SharedLibrary): + ET.SubElement(link, 'ImportLibrary').text = target.get_import_filename() + pdb = ET.SubElement(link, 'ProgramDataBaseFileName') + pdb.text = '$(OutDir}%s.pdb' % target_name + if isinstance(target, build.Executable): + ET.SubElement(link, 'EntryPointSymbol').text = entrypoint + targetmachine = ET.SubElement(link, 'TargetMachine') + targetmachine.text = 'MachineX86' + + if len(headers) + len(gen_hdrs) > 0: + inc_hdrs = ET.SubElement(root, 'ItemGroup') + for h in headers: + relpath = h.rel_to_builddir(proj_to_src_root) + ET.SubElement(inc_hdrs, 'CLInclude', Include=relpath) + for h in gen_hdrs: + if isinstance(h, str): + relpath = h + else: + relpath = h.rel_to_builddir(proj_to_src_root) + ET.SubElement(inc_hdrs, 'CLInclude', Include = relpath) + if len(sources) + len(gen_src) > 0: + inc_src = ET.SubElement(root, 'ItemGroup') + for s in sources: + relpath = s.rel_to_builddir(proj_to_src_root) + ET.SubElement(inc_src, 'CLCompile', Include=relpath) + for s in gen_src: + relpath = self.relpath(s, target.subdir) + ET.SubElement(inc_src, 'CLCompile', Include=relpath) + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets') + # Reference the regen target. + ig = ET.SubElement(root, 'ItemGroup') + pref = ET.SubElement(ig, 'ProjectReference', Include=os.path.join(self.environment.get_build_dir(), 'REGEN.vcxproj')) + ET.SubElement(pref, 'Project').text = self.environment.coredata.regen_guid + tree = ET.ElementTree(root) + tree.write(ofname, encoding='utf-8', xml_declaration=True) + # ElementTree can not do prettyprinting so do it manually + doc = xml.dom.minidom.parse(ofname) + open(ofname, 'w').write(doc.toprettyxml()) + # World of horror! Python insists on not quoting quotes and + # fixing the escaped " into &quot; whereas MSVS + # requires quoted but not fixed elements. Enter horrible hack. + txt = open(ofname, 'r').read() + open(ofname, 'w').write(txt.replace('&quot;', '"')) + + def gen_regenproj(self, project_name, ofname): + root = ET.Element('Project', {'DefaultTargets': 'Build', + 'ToolsVersion' : '4.0', + 'xmlns' : 'http://schemas.microsoft.com/developer/msbuild/2003'}) + confitems = ET.SubElement(root, 'ItemGroup', {'Label' : 'ProjectConfigurations'}) + prjconf = ET.SubElement(confitems, 'ProjectConfiguration', + {'Include' : self.buildtype + '|' + self.platform}) + p = ET.SubElement(prjconf, 'Configuration') + p.text= self.buildtype + pl = ET.SubElement(prjconf, 'Platform') + pl.text = self.platform + globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals') + guidelem = ET.SubElement(globalgroup, 'ProjectGuid') + guidelem.text = self.environment.coredata.test_guid + kw = ET.SubElement(globalgroup, 'Keyword') + kw.text = self.platform + 'Proj' + p = ET.SubElement(globalgroup, 'Platform') + p.text = self.platform + pname= ET.SubElement(globalgroup, 'ProjectName') + pname.text = project_name + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.Default.props') + type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration') + ET.SubElement(type_config, 'ConfigurationType').text = "Utility" + ET.SubElement(type_config, 'CharacterSet').text = 'MultiByte' + ET.SubElement(type_config, 'UseOfMfc').text = 'false' + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.props') + direlem = ET.SubElement(root, 'PropertyGroup') + fver = ET.SubElement(direlem, '_ProjectFileVersion') + fver.text = self.project_file_version + outdir = ET.SubElement(direlem, 'OutDir') + outdir.text = '.\\' + intdir = ET.SubElement(direlem, 'IntDir') + intdir.text = 'test-temp\\' + tname = ET.SubElement(direlem, 'TargetName') + tname.text = project_name + + action = ET.SubElement(root, 'ItemDefinitionGroup') + midl = ET.SubElement(action, 'Midl') + ET.SubElement(midl, "AdditionalIncludeDirectories").text = '%(AdditionalIncludeDirectories)' + ET.SubElement(midl, "OutputDirectory").text = '$(IntDir)' + ET.SubElement(midl, 'HeaderFileName').text = '%(Filename).h' + ET.SubElement(midl, 'TypeLibraryName').text = '%(Filename).tlb' + ET.SubElement(midl, 'InterfaceIdentifierFilename').text = '%(Filename)_i.c' + ET.SubElement(midl, 'ProxyFileName').text = '%(Filename)_p.c' + regen_command = [sys.executable, + self.environment.get_build_command(), + '--internal', + 'regencheck'] + private_dir = self.environment.get_scratch_dir() + cmd_templ = '''setlocal +"%s" "%s" +if %%errorlevel%% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %%errorlevel%% & goto :cmDone +:cmErrorLevel +exit /b %%1 +:cmDone +if %%errorlevel%% neq 0 goto :VCEnd''' + igroup = ET.SubElement(root, 'ItemGroup') + custombuild = ET.SubElement(igroup, 'CustomBuild', Include='meson-private/regen.rule') + message = ET.SubElement(custombuild, 'Message') + message.text = 'Checking whether solution needs to be regenerated.' + ET.SubElement(custombuild, 'Command').text = cmd_templ % \ + ('" "'.join(regen_command), private_dir) + ET.SubElement(custombuild, 'Outputs').text = os.path.join(self.environment.get_scratch_dir(), 'regen.stamp') + deps = self.get_regen_filelist() + depstr = ';'.join([os.path.join(self.environment.get_source_dir(), d) for d in deps]) + ET.SubElement(custombuild, 'AdditionalInputs').text = depstr + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets') + ET.SubElement(root, 'ImportGroup', Label='ExtensionTargets') + tree = ET.ElementTree(root) + tree.write(ofname, encoding='utf-8', xml_declaration=True) + + def gen_testproj(self, target_name, ofname): + project_name = target_name + root = ET.Element('Project', {'DefaultTargets' : "Build", + 'ToolsVersion' : '4.0', + 'xmlns' : 'http://schemas.microsoft.com/developer/msbuild/2003'}) + confitems = ET.SubElement(root, 'ItemGroup', {'Label' : 'ProjectConfigurations'}) + prjconf = ET.SubElement(confitems, 'ProjectConfiguration', + {'Include' : self.buildtype + '|' + self.platform}) + p = ET.SubElement(prjconf, 'Configuration') + p.text= self.buildtype + pl = ET.SubElement(prjconf, 'Platform') + pl.text = self.platform + globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals') + guidelem = ET.SubElement(globalgroup, 'ProjectGuid') + guidelem.text = self.environment.coredata.test_guid + kw = ET.SubElement(globalgroup, 'Keyword') + kw.text = self.platform + 'Proj' + p = ET.SubElement(globalgroup, 'Platform') + p.text= self.platform + pname= ET.SubElement(globalgroup, 'ProjectName') + pname.text = project_name + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.Default.props') + type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration') + ET.SubElement(type_config, 'ConfigurationType') + ET.SubElement(type_config, 'CharacterSet').text = 'MultiByte' + ET.SubElement(type_config, 'UseOfMfc').text = 'false' + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.props') + direlem = ET.SubElement(root, 'PropertyGroup') + fver = ET.SubElement(direlem, '_ProjectFileVersion') + fver.text = self.project_file_version + outdir = ET.SubElement(direlem, 'OutDir') + outdir.text = '.\\' + intdir = ET.SubElement(direlem, 'IntDir') + intdir.text = 'test-temp\\' + tname = ET.SubElement(direlem, 'TargetName') + tname.text = target_name + + action = ET.SubElement(root, 'ItemDefinitionGroup') + midl = ET.SubElement(action, 'Midl') + ET.SubElement(midl, "AdditionalIncludeDirectories").text = '%(AdditionalIncludeDirectories)' + ET.SubElement(midl, "OutputDirectory").text = '$(IntDir)' + ET.SubElement(midl, 'HeaderFileName').text = '%(Filename).h' + ET.SubElement(midl, 'TypeLibraryName').text = '%(Filename).tlb' + ET.SubElement(midl, 'InterfaceIdentifierFilename').text = '%(Filename)_i.c' + ET.SubElement(midl, 'ProxyFileName').text = '%(Filename)_p.c' + postbuild = ET.SubElement(action, 'PostBuildEvent') + ET.SubElement(postbuild, 'Message') + test_data = os.path.join(self.environment.get_scratch_dir(), 'meson_test_setup.dat') + test_command = [sys.executable, + self.environment.get_build_command(), + '--internal', + 'test'] + cmd_templ = '''setlocal +"%s" "%s" +if %%errorlevel%% neq 0 goto :cmEnd +:cmEnd +endlocal & call :cmErrorLevel %%errorlevel%% & goto :cmDone +:cmErrorLevel +exit /b %%1 +:cmDone +if %%errorlevel%% neq 0 goto :VCEnd''' + ET.SubElement(postbuild, 'Command').text =\ + cmd_templ % ('" "'.join(test_command), test_data) + ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets') + tree = ET.ElementTree(root) + tree.write(ofname, encoding='utf-8', xml_declaration=True) + datafile = open(test_data, 'wb') + self.serialise_tests() + datafile.close() + # ElementTree can not do prettyprinting so do it manually + #doc = xml.dom.minidom.parse(ofname) + #open(ofname, 'w').write(doc.toprettyxml()) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py new file mode 100644 index 0000000..d874be9 --- /dev/null +++ b/mesonbuild/backend/xcodebackend.py @@ -0,0 +1,776 @@ +# Copyright 2014-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 build +from .. import mesonlib +import uuid, os, sys + +from ..coredata import MesonException + +class XCodeBackend(backends.Backend): + def __init__(self, build): + super().__init__(build) + self.project_uid = self.environment.coredata.guid.replace('-', '')[:24] + self.project_conflist = self.gen_id() + self.indent = ' ' + self.indent_level = 0 + self.xcodetypemap = {'c' : 'sourcecode.c.c', + 'a' : 'archive.ar', + 'cc': 'sourcecode.cpp.cpp', + 'cxx' : 'sourcecode.cpp.cpp', + 'cpp' : 'sourcecode.cpp.cpp', + 'c++' : 'sourcecode.cpp.cpp', + 'm' : 'sourcecode.c.objc', + 'mm' : 'sourcecode.cpp.objcpp', + 'h' : 'sourcecode.c.h', + 'hpp' : 'sourcecode.cpp.h', + 'hxx' : 'sourcecode.cpp.h', + 'hh' : 'sourcecode.cpp.hh', + 'inc' : 'sourcecode.c.h', + 'dylib' : 'compiled.mach-o.dylib', + 'o' : 'compiled.mach-o.objfile',} + self.maingroup_id = self.gen_id() + self.all_id = self.gen_id() + self.all_buildconf_id = self.gen_id() + self.buildtypes = ['debug'] + self.test_id = self.gen_id() + self.test_command_id = self.gen_id() + self.test_buildconf_id = self.gen_id() + + def gen_id(self): + return str(uuid.uuid4()).upper().replace('-', '')[:24] + + def get_target_dir(self, target): + dirname = os.path.join(target.get_subdir(), self.environment.coredata.get_builtin_option('buildtype')) + os.makedirs(os.path.join(self.environment.get_build_dir(), dirname), exist_ok=True) + return dirname + + def write_line(self, text): + self.ofile.write(self.indent*self.indent_level + text) + if not text.endswith('\n'): + self.ofile.write('\n') + + def generate(self, interp): + self.interpreter = interp + self.serialise_tests() + self.generate_filemap() + self.generate_buildmap() + self.generate_buildstylemap() + self.generate_build_phase_map() + self.generate_build_configuration_map() + self.generate_build_configurationlist_map() + self.generate_project_configurations_map() + self.generate_buildall_configurations_map() + self.generate_test_configurations_map() + self.generate_native_target_map() + self.generate_source_phase_map() + self.generate_target_dependency_map() + self.generate_pbxdep_map() + self.generate_containerproxy_map() + self.proj_dir = os.path.join(self.environment.get_build_dir(), self.build.project_name + '.xcodeproj') + os.makedirs(self.proj_dir, exist_ok=True) + self.proj_file = os.path.join(self.proj_dir, 'project.pbxproj') + self.ofile = open(self.proj_file, 'w') + self.generate_prefix() + self.generate_pbx_aggregate_target() + self.generate_pbx_build_file() + self.generate_pbx_build_style() + self.generate_pbx_container_item_proxy() + self.generate_pbx_file_reference() + self.generate_pbx_group() + self.generate_pbx_native_target() + self.generate_pbx_project() + self.generate_pbx_shell_build_phase() + self.generate_pbx_sources_build_phase() + self.generate_pbx_target_dependency() + self.generate_xc_build_configuration() + self.generate_xc_configurationList() + self.generate_suffix() + + def get_xcodetype(self, fname): + return self.xcodetypemap[fname.split('.')[-1]] + + def generate_filemap(self): + self.filemap = {} # Key is source file relative to src root. + self.target_filemap = {} + for name, t in self.build.targets.items(): + for s in t.sources: + if isinstance(s, mesonlib.File): + s = os.path.join(s.subdir, s.fname) + self.filemap[s] = self.gen_id() + for o in t.objects: + if isinstance(o, str): + o = os.path.join(t.subdir, o) + self.filemap[o] = self.gen_id() + self.target_filemap[name] = self.gen_id() + + def generate_buildmap(self): + self.buildmap = {} + for t in self.build.targets.values(): + for s in t.sources: + s = os.path.join(s.subdir, s.fname) + self.buildmap[s] = self.gen_id() + for o in t.objects: + o = os.path.join(t.subdir, o) + if isinstance(o, str): + self.buildmap[o] = self.gen_id() + + def generate_buildstylemap(self): + self.buildstylemap = {'debug' : self.gen_id()} + + def generate_build_phase_map(self): + self.buildphasemap = {} + for t in self.build.targets: + self.buildphasemap[t] = self.gen_id() + + def generate_build_configuration_map(self): + self.buildconfmap = {} + for t in self.build.targets: + bconfs = {'debug' : self.gen_id()} + self.buildconfmap[t] = bconfs + + def generate_project_configurations_map(self): + self.project_configurations = {'debug' : self.gen_id()} + + def generate_buildall_configurations_map(self): + self.buildall_configurations = {'debug' : self.gen_id()} + + def generate_test_configurations_map(self): + self.test_configurations = {'debug' : self.gen_id()} + + def generate_build_configurationlist_map(self): + self.buildconflistmap = {} + for t in self.build.targets: + self.buildconflistmap[t] = self.gen_id() + + def generate_native_target_map(self): + self.native_targets = {} + for t in self.build.targets: + self.native_targets[t] = self.gen_id() + + def generate_target_dependency_map(self): + self.target_dependency_map = {} + for tname, t in self.build.targets.items(): + for target in t.link_targets: + self.target_dependency_map[(tname, target.get_basename())] = self.gen_id() + + def generate_pbxdep_map(self): + self.pbx_dep_map = {} + for t in self.build.targets: + self.pbx_dep_map[t] = self.gen_id() + + def generate_containerproxy_map(self): + self.containerproxy_map = {} + for t in self.build.targets: + self.containerproxy_map[t] = self.gen_id() + + def generate_source_phase_map(self): + self.source_phase = {} + for t in self.build.targets: + self.source_phase[t] = self.gen_id() + + def generate_pbx_aggregate_target(self): + self.ofile.write('\n/* Begin PBXAggregateTarget section */\n') + self.write_line('%s /* ALL_BUILD */ = {' % self.all_id) + self.indent_level+=1 + self.write_line('isa = PBXAggregateTarget;') + self.write_line('buildConfigurationList = %s;' % self.all_buildconf_id) + self.write_line('buildPhases = (') + self.write_line(');') + self.write_line('dependencies = (') + self.indent_level+=1 + for t in self.build.targets: + self.write_line('%s /* PBXTargetDependency */,' % self.pbx_dep_map[t]) + self.indent_level-=1 + self.write_line(');') + self.write_line('name = ALL_BUILD;') + self.write_line('productName = ALL_BUILD;') + self.indent_level-=1 + self.write_line('};') + self.write_line('%s /* RUN_TESTS */ = {' % self.test_id) + self.indent_level +=1 + self.write_line('isa = PBXAggregateTarget;') + self.write_line('buildConfigurationList = %s;' % self.test_buildconf_id) + self.write_line('buildPhases = (') + self.indent_level+=1 + self.write_line('%s /* test run command */,' % self.test_command_id) + self.indent_level-=1 + self.write_line(');') + self.write_line('dependencies = (') + self.write_line(');') + self.write_line('name = RUN_TESTS;') + self.write_line('productName = RUN_TESTS;') + self.indent_level-=1 + self.write_line('};') + self.ofile.write('/* End PBXAggregateTarget section */\n') + + def generate_pbx_build_file(self): + self.ofile.write('\n/* Begin PBXBuildFile section */\n') + templ = '%s /* %s */ = { isa = PBXBuildFile; fileRef = %s /* %s */; settings = { COMPILER_FLAGS = "%s"; }; };\n' + otempl = '%s /* %s */ = { isa = PBXBuildFile; fileRef = %s /* %s */;};\n' + for t in self.build.targets.values(): + for s in t.sources: + if isinstance(s, str): + s = os.path.join(t.subdir, s) + idval = self.buildmap[s] + fullpath = os.path.join(self.environment.get_source_dir(), s) + fileref = self.filemap[s] + fullpath2 = fullpath + compiler_args = '' + self.ofile.write(templ % (idval, fullpath, fileref, fullpath2, compiler_args)) + for o in t.objects: + o = os.path.join(t.subdir, o) + idval = self.buildmap[o] + fileref = self.filemap[o] + fullpath = os.path.join(self.environment.get_source_dir(), o) + fullpath2 = fullpath + self.ofile.write(otempl % (idval, fullpath, fileref, fullpath2)) + self.ofile.write('/* End PBXBuildFile section */\n') + + def generate_pbx_build_style(self): + self.ofile.write('\n/* Begin PBXBuildStyle section */\n') + for name, idval in self.buildstylemap.items(): + self.write_line('%s /* %s */ = {\n' % (idval, name)) + self.indent_level += 1 + self.write_line('isa = PBXBuildStyle;\n') + self.write_line('buildSettings = {\n') + self.indent_level += 1 + self.write_line('COPY_PHASE_STRIP = NO;\n') + self.indent_level -= 1 + self.write_line('};\n') + self.write_line('name = %s;\n' % name) + self.indent_level -= 1 + self.write_line('};\n') + self.ofile.write('/* End PBXBuildStyle section */\n') + + def generate_pbx_container_item_proxy(self): + self.ofile.write('\n/* Begin PBXContainerItemProxy section */\n') + for t in self.build.targets: + self.write_line('%s /* PBXContainerItemProxy */ = {' % self.containerproxy_map[t]) + self.indent_level += 1 + self.write_line('isa = PBXContainerItemProxy;') + self.write_line('containerPortal = %s /* Project object */;' % self.project_uid) + self.write_line('proxyType = 1;') + self.write_line('remoteGlobalIDString = %s;' % self.native_targets[t]) + self.write_line('remoteInfo = %s;' % t) + self.indent_level-=1 + self.write_line('};') + self.ofile.write('/* End PBXContainerItemProxy section */\n') + + def generate_pbx_file_reference(self): + self.ofile.write('\n/* Begin PBXFileReference section */\n') + src_templ = '%s /* %s */ = { isa = PBXFileReference; explicitFileType = "%s"; fileEncoding = 4; name = "%s"; path = "%s"; sourceTree = SOURCE_ROOT; };\n' + for fname, idval in self.filemap.items(): + fullpath = os.path.join(self.environment.get_source_dir(), fname) + xcodetype = self.get_xcodetype(fname) + name = os.path.split(fname)[-1] + path = fname + self.ofile.write(src_templ % (idval, fullpath, xcodetype, name, path)) + target_templ = '%s /* %s */ = { isa = PBXFileReference; explicitFileType = "%s"; path = %s; refType = %d; sourceTree = BUILT_PRODUCTS_DIR; };\n' + for tname, idval in self.target_filemap.items(): + t = self.build.targets[tname] + fname = t.get_filename() + reftype = 0 + if isinstance(t, build.Executable): + typestr = 'compiled.mach-o.executable' + path = t.get_filename() + elif isinstance(t, build.SharedLibrary): + # OSX has a completely different shared library + # naming scheme so do this manually. + typestr = self.get_xcodetype('dummy.dylib') + path = t.get_osx_filename() + else: + typestr = self.get_xcodetype(fname) + path = '"%s"' % t.get_filename() + self.ofile.write(target_templ % (idval, tname, typestr, path, reftype)) + self.ofile.write('/* End PBXFileReference section */\n') + + def generate_pbx_group(self): + groupmap = {} + target_src_map = {} + for t in self.build.targets: + groupmap[t] = self.gen_id() + target_src_map[t] = self.gen_id() + self.ofile.write('\n/* Begin PBXGroup section */\n') + sources_id = self.gen_id() + resources_id = self.gen_id() + products_id = self.gen_id() + self.write_line('%s = {' % self.maingroup_id) + self.indent_level+=1 + self.write_line('isa = PBXGroup;') + self.write_line('children = (') + self.indent_level+=1 + self.write_line('%s /* Sources */,' % sources_id) + self.write_line('%s /* Resources */,' % resources_id) + self.write_line('%s /* Products */,' % products_id) + self.indent_level-=1 + self.write_line(');') + self.write_line('sourceTree = "<group>";') + self.indent_level -= 1 + self.write_line('};') + + # Sources + self.write_line('%s /* Sources */ = {' % sources_id) + self.indent_level+=1 + self.write_line('isa = PBXGroup;') + self.write_line('children = (') + self.indent_level+=1 + for t in self.build.targets: + self.write_line('%s /* %s */,' % (groupmap[t], t)) + self.indent_level-=1 + self.write_line(');') + self.write_line('name = Sources;') + self.write_line('sourcetree = "<group>";') + self.indent_level-=1 + self.write_line('};') + + self.write_line('%s /* Resources */ = {' % resources_id) + self.indent_level+=1 + self.write_line('isa = PBXGroup;') + self.write_line('children = (') + self.write_line(');') + self.write_line('name = Resources;') + self.write_line('sourceTree = "<group>";') + self.indent_level-=1 + self.write_line('};') + + # Targets + for t in self.build.targets: + self.write_line('%s /* %s */ = {' % (groupmap[t], t)) + self.indent_level+=1 + self.write_line('isa = PBXGroup;') + self.write_line('children = (') + self.indent_level+=1 + self.write_line('%s /* Source files */,' % target_src_map[t]) + self.indent_level-=1 + self.write_line(');') + self.write_line('name = %s;' % t) + self.write_line('sourceTree = "<group>";') + self.indent_level-=1 + self.write_line('};') + self.write_line('%s /* Source files */ = {' % target_src_map[t]) + self.indent_level+=1 + self.write_line('isa = PBXGroup;') + self.write_line('children = (') + self.indent_level+=1 + for s in self.build.targets[t].sources: + s = os.path.join(s.subdir, s.fname) + if isinstance(s, str): + self.write_line('%s /* %s */,' % (self.filemap[s], s)) + for o in self.build.targets[t].objects: + o = os.path.join(self.build.targets[t].subdir, o) + self.write_line('%s /* %s */,' % (self.filemap[o], o)) + self.indent_level-=1 + self.write_line(');') + self.write_line('name = "Source files";') + self.write_line('sourceTree = "<group>";') + self.indent_level-=1 + self.write_line('};') + + # And finally products + self.write_line('%s /* Products */ = {' % products_id) + self.indent_level+=1 + self.write_line('isa = PBXGroup;') + self.write_line('children = (') + self.indent_level+=1 + for t in self.build.targets: + self.write_line('%s /* %s */,' % (self.target_filemap[t], t)) + self.indent_level-=1 + self.write_line(');') + self.write_line('name = Products;') + self.write_line('sourceTree = "<group>";') + self.indent_level-=1 + self.write_line('};') + self.ofile.write('/* End PBXGroup section */\n') + + def generate_pbx_native_target(self): + self.ofile.write('\n/* Begin PBXNativeTarget section */\n') + for tname, idval in self.native_targets.items(): + t = self.build.targets[tname] + self.write_line('%s /* %s */ = {' % (idval, tname)) + self.indent_level+=1 + self.write_line('isa = PBXNativeTarget;') + self.write_line('buildConfigurationList = %s /* Build configuration list for PBXNativeTarget "%s" */;'\ + % (self.buildconflistmap[tname], tname)) + self.write_line('buildPhases = (') + self.indent_level+=1 + self.write_line('%s /* Sources */,' % self.buildphasemap[tname]) + self.indent_level-=1 + self.write_line(');') + self.write_line('buildRules = (') + self.write_line(');') + self.write_line('dependencies = (') + self.indent_level+=1 + for lt in self.build.targets[tname].link_targets: + # NOT DOCUMENTED, may need to make different links + # to same target have different targetdependency item. + idval = self.pbx_dep_map[lt.get_basename()] + self.write_line('%s /* PBXTargetDependency */,' % idval) + self.indent_level -=1 + self.write_line(");") + self.write_line('name = %s;' % tname) + self.write_line('productName = %s;' % tname) + self.write_line('productReference = %s /* %s */;' % (self.target_filemap[tname], tname)) + if isinstance(t, build.Executable): + typestr = 'com.apple.product-type.tool' + elif isinstance(t, build.StaticLibrary): + typestr = 'com.apple.product-type.library.static' + elif isinstance(t, build.SharedLibrary): + typestr = 'com.apple.product-type.library.dynamic' + else: + raise MesonException('Unknown target type for %s' % tname) + self.write_line('productType = "%s";' % typestr) + self.indent_level-=1 + self.write_line('};') + self.ofile.write('/* End PBXNativeTarget section */\n') + + def generate_pbx_project(self): + self.ofile.write('\n/* Begin PBXProject section */\n') + self.write_line('%s /* Project object */ = {' % self.project_uid) + self.indent_level += 1 + self.write_line('isa = PBXProject;') + self.write_line('attributes = {') + self.indent_level += 1 + self.write_line('BuildIndependentTargetsInParallel = YES;') + self.indent_level -= 1 + self.write_line('};') + conftempl = 'buildConfigurationList = %s /* build configuration list for PBXProject "%s"*/;' + self.write_line(conftempl % (self.project_conflist, self.build.project_name)) + self.write_line('buildSettings = {') + self.write_line('};') + self.write_line('buildStyles = (') + self.indent_level += 1 + for name, idval in self.buildstylemap.items(): + self.write_line('%s /* %s */,' % (idval, name)) + self.indent_level -= 1 + self.write_line(');') + self.write_line('compatibilityVersion = "Xcode 3.2";') + self.write_line('hasScannedForEncodings = 0;') + self.write_line('mainGroup = %s;' % self.maingroup_id) + self.write_line('projectDirPath = "%s";' % self.build_to_src) + self.write_line('projectRoot = "";') + self.write_line('targets = (') + self.indent_level += 1 + self.write_line('%s /* ALL_BUILD */,' % self.all_id) + self.write_line('%s /* RUN_TESTS */,' % self.test_id) + for t in self.build.targets: + self.write_line('%s /* %s */,' % (self.native_targets[t], t)) + self.indent_level -= 1 + self.write_line(');') + self.indent_level -= 1 + self.write_line('};') + self.ofile.write('/* End PBXProject section */\n') + + def generate_pbx_shell_build_phase(self): + self.ofile.write('\n/* Begin PBXShellScriptBuildPhase section */\n') + self.write_line('%s = {' % self.test_command_id) + self.indent_level += 1 + self.write_line('isa = PBXShellScriptBuildPhase;') + self.write_line('buildActionMask = 2147483647;') + self.write_line('files = (') + self.write_line(');') + self.write_line('inputPaths = (') + self.write_line(');') + self.write_line('outputPaths = (') + self.write_line(');') + self.write_line('runOnlyForDeploymentPostprocessing = 0;') + self.write_line('shellPath = /bin/sh;') + script_root = self.environment.get_script_dir() + test_script = os.path.join(script_root, 'meson_test.py') + test_data = os.path.join(self.environment.get_scratch_dir(), 'meson_test_setup.dat') + cmd = [sys.executable, test_script, test_data, '--wd', self.environment.get_build_dir()] + cmdstr = ' '.join(["'%s'" % i for i in cmd]) + self.write_line('shellScript = "%s";' % cmdstr) + self.write_line('showEnvVarsInLog = 0;') + self.indent_level-=1 + self.write_line('};') + self.ofile.write('/* End PBXShellScriptBuildPhase section */\n') + + def generate_pbx_sources_build_phase(self): + self.ofile.write('\n/* Begin PBXSourcesBuildPhase section */\n') + for name, phase_id in self.source_phase.items(): + self.write_line('%s /* Sources */ = {' % self.buildphasemap[name]) + self.indent_level+=1 + self.write_line('isa = PBXSourcesBuildPhase;') + self.write_line('buildActionMask = 2147483647;') + self.write_line('files = (') + self.indent_level+=1 + for s in self.build.targets[name].sources: + s = os.path.join(s.subdir, s.fname) + if not self.environment.is_header(s): + self.write_line('%s /* %s */,' % (self.buildmap[s], os.path.join(self.environment.get_source_dir(), s))) + self.indent_level-=1 + self.write_line(');') + self.write_line('runOnlyForDeploymentPostprocessing = 0;') + self.indent_level-=1 + self.write_line('};') + self.ofile.write('/* End PBXSourcesBuildPhase section */\n') + + def generate_pbx_target_dependency(self): + self.ofile.write('\n/* Begin PBXTargetDependency section */\n') + for t in self.build.targets: + idval = self.pbx_dep_map[t] # VERIFY: is this correct? + self.write_line('%s /* PBXTargetDependency */ = {' % idval) + self.indent_level += 1 + self.write_line('isa = PBXTargetDependency;') + self.write_line('target = %s /* %s */;' % (self.native_targets[t], t)) + self.write_line('targetProxy = %s /* PBXContainerItemProxy */;' % self.containerproxy_map[t]) + self.indent_level-=1 + self.write_line('};') + self.ofile.write('/* End PBXTargetDependency section */\n') + + def generate_xc_build_configuration(self): + self.ofile.write('\n/* Begin XCBuildConfiguration section */\n') + # First the setup for the toplevel project. + for buildtype in self.buildtypes: + self.write_line('%s /* %s */ = {' % (self.project_configurations[buildtype], buildtype)) + self.indent_level+=1 + self.write_line('isa = XCBuildConfiguration;') + self.write_line('buildSettings = {') + self.indent_level+=1 + self.write_line('ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";') + self.write_line('ONLY_ACTIVE_ARCH = YES;') + self.write_line('SDKROOT = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk";') + self.write_line('SYMROOT = "%s/build";' % self.environment.get_build_dir()) + self.indent_level-=1 + self.write_line('};') + self.write_line('name = %s;' % buildtype) + self.indent_level-=1 + self.write_line('};') + + # Then the all target. + for buildtype in self.buildtypes: + self.write_line('%s /* %s */ = {' % (self.buildall_configurations[buildtype], buildtype)) + self.indent_level+=1 + self.write_line('isa = XCBuildConfiguration;') + self.write_line('buildSettings = {') + self.indent_level += 1 + self.write_line('COMBINE_HIDPI_IMAGES = YES;') + self.write_line('GCC_GENERATE_DEBUGGING_SYMBOLS = NO;') + self.write_line('GCC_INLINES_ARE_PRIVATE_EXTERN = NO;') + self.write_line('GCC_OPTIMIZATION_LEVEL = 0;') + self.write_line('GCC_PREPROCESSOR_DEFINITIONS = ("");') + self.write_line('GCC_SYMBOLS_PRIVATE_EXTERN = NO;') + self.write_line('INSTALL_PATH = "";') + self.write_line('OTHER_CFLAGS = " ";') + self.write_line('OTHER_LDFLAGS = " ";') + self.write_line('OTHER_REZFLAGS = "";') + self.write_line('PRODUCT_NAME = ALL_BUILD;') + self.write_line('SECTORDER_FLAGS = "";') + self.write_line('SYMROOT = "%s";' % self.environment.get_build_dir()) + self.write_line('USE_HEADERMAP = NO;') + self.write_line('WARNING_CFLAGS = ("-Wmost", "-Wno-four-char-constants", "-Wno-unknown-pragmas", );') + self.indent_level-=1 + self.write_line('};') + self.write_line('name = %s;' % buildtype) + self.indent_level-=1 + self.write_line('};') + + # Then the test target. + for buildtype in self.buildtypes: + self.write_line('%s /* %s */ = {' % (self.test_configurations[buildtype], buildtype)) + self.indent_level+=1 + self.write_line('isa = XCBuildConfiguration;') + self.write_line('buildSettings = {') + self.indent_level += 1 + self.write_line('COMBINE_HIDPI_IMAGES = YES;') + self.write_line('GCC_GENERATE_DEBUGGING_SYMBOLS = NO;') + self.write_line('GCC_INLINES_ARE_PRIVATE_EXTERN = NO;') + self.write_line('GCC_OPTIMIZATION_LEVEL = 0;') + self.write_line('GCC_PREPROCESSOR_DEFINITIONS = ("");') + self.write_line('GCC_SYMBOLS_PRIVATE_EXTERN = NO;') + self.write_line('INSTALL_PATH = "";') + self.write_line('OTHER_CFLAGS = " ";') + self.write_line('OTHER_LDFLAGS = " ";') + self.write_line('OTHER_REZFLAGS = "";') + self.write_line('PRODUCT_NAME = RUN_TESTS;') + self.write_line('SECTORDER_FLAGS = "";') + self.write_line('SYMROOT = "%s";' % self.environment.get_build_dir()) + self.write_line('USE_HEADERMAP = NO;') + self.write_line('WARNING_CFLAGS = ("-Wmost", "-Wno-four-char-constants", "-Wno-unknown-pragmas", );') + self.indent_level-=1 + self.write_line('};') + self.write_line('name = %s;' % buildtype) + self.indent_level-=1 + self.write_line('};') + + # Now finally targets. + langnamemap = {'c' : 'C', 'cpp' : 'CPLUSPLUS', 'objc' : 'OBJC', 'objcpp' : 'OBJCPLUSPLUS'} + for target_name, target in self.build.targets.items(): + for buildtype in self.buildtypes: + dep_libs = [] + links_dylib = False + headerdirs = [] + for d in target.include_dirs: + for sd in d.incdirs: + cd = os.path.join(d.curdir, sd) + headerdirs.append(os.path.join(self.environment.get_source_dir(), cd)) + headerdirs.append(os.path.join(self.environment.get_build_dir(), cd)) + for l in target.link_targets: + abs_path = os.path.join(self.environment.get_build_dir(), + l.subdir, buildtype, l.get_osx_filename()) + dep_libs.append("'%s'" % abs_path) + if isinstance(l, build.SharedLibrary): + links_dylib = True + if links_dylib: + dep_libs = ['-Wl,-search_paths_first', '-Wl,-headerpad_max_install_names'] + dep_libs + dylib_version = None + if isinstance(target, build.SharedLibrary): + ldargs = ['-dynamiclib', '-Wl,-headerpad_max_install_names'] + dep_libs + install_path = os.path.join(self.environment.get_build_dir(), target.subdir, buildtype) + dylib_version = target.version + else: + ldargs = dep_libs + install_path = '' + if dylib_version is not None: + product_name = target_name + '.' + dylib_version + else: + product_name = target_name + ldargs += target.link_args + ldstr = ' '.join(ldargs) + valid = self.buildconfmap[target_name][buildtype] + langargs = {} + for lang in self.environment.coredata.compilers: + if lang not in langnamemap: + continue + gargs = self.build.global_args.get(lang, []) + targs = target.get_extra_args(lang) + args = gargs + targs + if len(args) > 0: + langargs[langnamemap[lang]] = args + symroot = os.path.join(self.environment.get_build_dir(), target.subdir) + self.write_line('%s /* %s */ = {' % (valid, buildtype)) + self.indent_level+=1 + self.write_line('isa = XCBuildConfiguration;') + self.write_line('buildSettings = {') + self.indent_level += 1 + self.write_line('COMBINE_HIDPI_IMAGES = YES;') + if dylib_version is not None: + self.write_line('DYLIB_CURRENT_VERSION = "%s";' % dylib_version) + self.write_line('EXECUTABLE_PREFIX = "%s";' % target.prefix) + if target.suffix == '': + suffix = '' + else: + suffix = '.' + target.suffix + self.write_line('EXECUTABLE_SUFFIX = "%s";' % suffix) + self.write_line('GCC_GENERATE_DEBUGGING_SYMBOLS = YES;') + self.write_line('GCC_INLINES_ARE_PRIVATE_EXTERN = NO;') + self.write_line('GCC_OPTIMIZATION_LEVEL = 0;') + self.write_line('GCC_PREPROCESSOR_DEFINITIONS = ("");') + self.write_line('GCC_SYMBOLS_PRIVATE_EXTERN = NO;') + if len(headerdirs) > 0: + quotedh = ','.join(['"\\"%s\\""' % i for i in headerdirs]) + self.write_line('HEADER_SEARCH_PATHS=(%s);' % quotedh) + self.write_line('INSTALL_PATH = "%s";' % install_path) + self.write_line('LIBRARY_SEARCH_PATHS = "";') + if isinstance(target, build.SharedLibrary): + self.write_line('LIBRARY_STYLE = DYNAMIC;') + for langname, args in langargs.items(): + argstr = ' '.join(args) + self.write_line('OTHER_%sFLAGS = "%s";' % (langname, argstr)) + self.write_line('OTHER_LDFLAGS = "%s";' % ldstr) + self.write_line('OTHER_REZFLAGS = "";') + self.write_line('PRODUCT_NAME = %s;' % product_name) + self.write_line('SECTORDER_FLAGS = "";') + self.write_line('SYMROOT = "%s";' % symroot) + self.write_line('USE_HEADERMAP = NO;') + self.write_line('WARNING_CFLAGS = ("-Wmost", "-Wno-four-char-constants", "-Wno-unknown-pragmas", );') + self.indent_level-=1 + self.write_line('};') + self.write_line('name = %s;' % buildtype) + self.indent_level-=1 + self.write_line('};') + self.ofile.write('/* End XCBuildConfiguration section */\n') + + def generate_xc_configurationList(self): + self.ofile.write('\n/* Begin XCConfigurationList section */\n') + self.write_line('%s /* Build configuration list for PBXProject "%s" */ = {' % (self.project_conflist, self.build.project_name)) + self.indent_level+=1 + self.write_line('isa = XCConfigurationList;') + self.write_line('buildConfigurations = (') + self.indent_level+=1 + for buildtype in self.buildtypes: + self.write_line('%s /* %s */,' % (self.project_configurations[buildtype], buildtype)) + self.indent_level-=1 + self.write_line(');') + self.write_line('defaultConfigurationIsVisible = 0;') + self.write_line('defaultConfigurationName = debug;') + self.indent_level-=1 + self.write_line('};') + + # Now the all target + self.write_line('%s /* Build configuration list for PBXAggregateTarget "ALL_BUILD" */ = {' % self.all_buildconf_id) + self.indent_level+=1 + self.write_line('isa = XCConfigurationList;') + self.write_line('buildConfigurations = (') + self.indent_level+=1 + for buildtype in self.buildtypes: + self.write_line('%s /* %s */,' % (self.buildall_configurations[buildtype], buildtype)) + self.indent_level-=1 + self.write_line(');') + self.write_line('defaultConfigurationIsVisible = 0;') + self.write_line('defaultConfigurationName = debug;') + self.indent_level-=1 + self.write_line('};') + + # Test target + self.write_line('%s /* Build configuration list for PBXAggregateTarget "ALL_BUILD" */ = {' % self.test_buildconf_id) + self.indent_level+=1 + self.write_line('isa = XCConfigurationList;') + self.write_line('buildConfigurations = (') + self.indent_level+=1 + for buildtype in self.buildtypes: + self.write_line('%s /* %s */,' % (self.test_configurations[buildtype], buildtype)) + self.indent_level-=1 + self.write_line(');') + self.write_line('defaultConfigurationIsVisible = 0;') + self.write_line('defaultConfigurationName = debug;') + self.indent_level-=1 + self.write_line('};') + + for target_name in self.build.targets: + listid = self.buildconflistmap[target_name] + self.write_line('%s /* Build configuration list for PBXNativeTarget "%s" */ = {' % (listid, target_name)) + self.indent_level += 1 + self.write_line('isa = XCConfigurationList;') + self.write_line('buildConfigurations = (') + self.indent_level += 1 + typestr = 'debug' + idval = self.buildconfmap[target_name][typestr] + self.write_line('%s /* %s */,' % (idval, typestr)) + self.indent_level -= 1 + self.write_line(');') + self.write_line('defaultConfigurationIsVisible = 0;') + self.write_line('defaultConfigurationName = %s;' % typestr) + self.indent_level -= 1 + self.write_line('};') + self.ofile.write('/* End XCConfigurationList section */\n') + + def generate_prefix(self): + self.ofile.write('// !$*UTF8*$!\n{\n') + self.indent_level += 1 + self.write_line('archiveVersion = 1;\n') + self.write_line('classes = {\n') + self.write_line('};\n') + self.write_line('objectVersion = 46;\n') + self.write_line('objects = {\n') + self.indent_level += 1 + + def generate_suffix(self): + self.indent_level -= 1 + self.write_line('};\n') + self.write_line('rootObject = ' + self.project_uid + ';') + self.indent_level -= 1 + self.write_line('}\n') |