From a1f4bf1124588fd59f8eed95f13ec445b236735b Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sat, 15 Jun 2013 00:37:14 +0300 Subject: [PATCH] Started work on MSVC precompiled headers. It does not work yet but I have been at it for so long that I want to just commit now because it at least does something close to the final result. --- backends.py | 69 ++++++++++++++++++++++------ environment.py | 22 +++++++-- interpreter.py | 38 +++++++++++---- test cases/common/13 pch/meson.build | 3 +- test cases/common/13 pch/prog.c | 4 ++ 5 files changed, 107 insertions(+), 29 deletions(-) diff --git a/backends.py b/backends.py index df7a2cb42..a9bb34c4e 100755 --- a/backends.py +++ b/backends.py @@ -112,6 +112,12 @@ class Backend(): self.build_to_src = os.path.relpath(self.environment.get_source_dir(), self.environment.get_build_dir()) + 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): @@ -171,10 +177,13 @@ class Backend(): args = [] pchpath = self.get_target_private_dir(target) includearg = compiler.get_include_arg(pchpath) - for p in target.get_pch(): - if compiler.can_compile(p): + for lang in ['c', 'cpp']: + p = target.get_pch(lang) + if len(p) == 0: + continue + if compiler.can_compile(p[-1]): args.append('-include') - args.append(os.path.split(p)[-1]) + args.append(os.path.split(p[0])[-1]) if len(args) > 0: args = [includearg] + args return args @@ -595,7 +604,7 @@ class NinjaBackend(Backend): rel_obj = os.path.join(self.get_target_private_dir(target), os.path.basename(src_filename)) rel_obj += '.' + self.environment.get_object_suffix() dep_file = rel_obj + '.' + compiler.get_depfile_suffix() - pchlist = target.get_pch() + pchlist = target.get_pch(compiler.language) if len(pchlist) == 0: pch_dep = [] else: @@ -628,22 +637,54 @@ class NinjaBackend(Backend): element.write(outfile) return rel_obj + 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] + chopped = os.path.split(header)[-1].split('.')[:-1] + chopped.append(compiler.get_pch_suffix()) + pchname = '.'.join(chopped) + dst = os.path.join(self.get_target_private_dir(target), pchname) + + commands = [] + commands += self.generate_basic_compiler_flags(target, compiler) + commands += compiler.gen_pch_args(header, source, dst) + + dep = dst + '.' + compiler.get_depfile_suffix() + return (commands, dep, dst) + + def generate_gcc_pch_command(self, target, compiler, pch): + commands = [] + commands += self.generate_basic_compiler_flags(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) + def generate_pch(self, target, outfile): - for pch in target.get_pch(): - if '/' not in pch: + 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 interpreter.InvalidArguments('Precompiled header of "%s" must not be in the same direcotory as source, please put it in a subdirectory.' % target.get_basename()) - compiler = self.get_compiler_for_source(pch) - commands = [] - commands += self.generate_basic_compiler_flags(target, compiler) - src = os.path.join(self.build_to_src, target.get_source_subdir(), pch) - 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() + compiler = self.get_compiler_for_lang(lang) + src = os.path.join(self.build_to_src, target.get_source_subdir(), pch[-1]) + if compiler.id == 'msvc': + (commands, dep, dst) = self.generate_msvc_pch_command(target, compiler, pch) + extradep = os.path.join(self.build_to_src, target.get_source_subdir(), pch[0]) + else: + (commands, dep, dst) = self.generate_gcc_pch_command(target, compiler, pch[0]) + extradep = None elem = NinjaBuildElement(dst, compiler.get_language() + '_COMPILER', src) + if extradep is not None: + elem.add_dep(extradep) elem.add_item('FLAGS', commands) elem.add_item('DEPFILE', dep) elem.write(outfile) - + def generate_shsym(self, outfile, target): target_name = self.get_target_filename(target) targetdir = self.get_target_private_dir(target) diff --git a/environment.py b/environment.py index 0212dbd31..9274651e3 100755 --- a/environment.py +++ b/environment.py @@ -169,8 +169,6 @@ int main(int argc, char **argv) { raise EnvironmentException('Could not run sizeof test binary.') return int(so.decode()) -cpp_suffixes = ['cc', 'cpp', 'cxx', 'hh', 'hpp', 'hxx'] - class CPPCompiler(CCompiler): def __init__(self, exelist): CCompiler.__init__(self, exelist) @@ -280,6 +278,9 @@ class VisualStudioCCompiler(CCompiler): def get_std_shared_lib_link_flags(self): return ['/DLL'] + def gen_pch_args(self, header, source, pchname): + return ['/Yc' + header, '/Fp' + pchname] + def sanity_check(self, work_dir): source_name = os.path.join(work_dir, 'sanitycheckc.c') binary_name = os.path.join(work_dir, 'sanitycheckc') @@ -504,6 +505,17 @@ def is_windows(): return platform.system().lower() == 'windows' header_suffixes = ['h', 'hh', 'hpp', 'hxx', 'H'] +cpp_suffixes = ['cc', 'cpp', 'cxx', 'hh', 'hpp', 'hxx'] +c_suffixes = ['c'] +clike_suffixes = c_suffixes + cpp_suffixes + +def is_header(fname): + suffix = fname.split('.')[-1] + return suffix in header_suffixes + +def is_source(fname): + suffix = fname.split('.')[-1] + return suffix in clike_suffixes class Environment(): private_dir = 'meson-private' @@ -573,8 +585,10 @@ class Environment(): return self.meson_script_file def is_header(self, fname): - suffix = fname.split('.')[-1] - return suffix in header_suffixes + return is_header(fname) + + def is_source(self, fname): + return is_source(fname) def detect_c_compiler(self): evar = 'CC' diff --git a/interpreter.py b/interpreter.py index 5469cec95..da53cc2e4 100755 --- a/interpreter.py +++ b/interpreter.py @@ -348,7 +348,7 @@ class BuildTarget(InterpreterObject): self.link_targets = [] self.filename = 'no_name' self.need_install = False - self.pch = [] + self.pch = {} self.extra_args = {} self.generated = [] self.process_sourcelist(sources) @@ -379,10 +379,14 @@ class BuildTarget(InterpreterObject): llist = [llist] for linktarget in llist: self.link(linktarget) - pchlist = kwargs.get('pch', []) - if not isinstance(pchlist, list): - pchlist = [pchlist] - self.add_pch(pchlist) + c_pchlist = kwargs.get('c_pch', []) + if not isinstance(c_pchlist, list): + c_pchlist = [c_pchlist] + self.add_pch('c', c_pchlist) + cpp_pchlist = kwargs.get('cpp_pch', []) + if not isinstance(cpp_pchlist, list): + cpp_pchlist = [cpp_pchlist] + self.add_pch('cpp', cpp_pchlist) clist = kwargs.get('c_args', []) if not isinstance(clist, list): clist = [clist] @@ -434,8 +438,11 @@ class BuildTarget(InterpreterObject): def has_pch(self): return len(self.pch) > 0 - def get_pch(self): - return self.pch + def get_pch(self, language): + try: + return self.pch[language] + except KeyError: + return[] def get_include_dirs(self): return self.include_dirs @@ -467,9 +474,20 @@ class BuildTarget(InterpreterObject): raise InvalidArguments('Generated source argument is not the output of a generator.') self.generated.append(g) - def add_pch(self, pchlist): - for a in pchlist: - self.pch.append(a) + def add_pch(self, language, pchlist): + if len(pchlist) == 0: + return + if len(pchlist) == 2: + if environment.is_header(pchlist[0]): + if not environment.is_source(pchlist[1]): + raise InterpreterException('PCH definition must contain one header and at most one source.') + elif environment.is_source(pchlist[0]): + if not environment.is_header(pchlist[1]): + raise InterpreterException('PCH definition must contain one header and at most one source.') + pchlist = [pchlist[1], pchlist[0]] + elif len(pchlist) > 2: + raise InterpreterException('PCH definition may have a maximum of 2 files.') + self.pch[language] = pchlist def add_include_dirs(self, args): for a in args: diff --git a/test cases/common/13 pch/meson.build b/test cases/common/13 pch/meson.build index 9632cc58d..9ed651242 100644 --- a/test cases/common/13 pch/meson.build +++ b/test cases/common/13 pch/meson.build @@ -1,3 +1,4 @@ project('pch test', 'c') -exe = executable('prog', 'prog.c', pch : 'pch/prog.h') +exe = executable('prog', 'prog.c', +c_pch : ['pch/prog_pch.c', 'pch/prog.h']) diff --git a/test cases/common/13 pch/prog.c b/test cases/common/13 pch/prog.c index 995bb3f83..23125f5f3 100644 --- a/test cases/common/13 pch/prog.c +++ b/test cases/common/13 pch/prog.c @@ -1,3 +1,7 @@ +#if defined(_MSC_VER) +#include"prog.pch" +#endif + void func() { fprintf(stdout, "This is a function that fails if stdio is not #included.\n"); }