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.

pull/15/head
Jussi Pakkanen 12 years ago
parent 81b478e031
commit a1f4bf1124
  1. 69
      backends.py
  2. 22
      environment.py
  3. 38
      interpreter.py
  4. 3
      test cases/common/13 pch/meson.build
  5. 4
      test cases/common/13 pch/prog.c

@ -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)

@ -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'

@ -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:

@ -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'])

@ -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");
}

Loading…
Cancel
Save