auto generate msvc pch source file if none is provided by the user

pull/4998/head
Nicolas Schneider 6 years ago committed by Jussi Pakkanen
parent fcd608c131
commit ded0defc3f
  1. 5
      docs/markdown/Precompiled-headers.md
  2. 17
      mesonbuild/backend/backends.py
  3. 17
      mesonbuild/backend/ninjabackend.py
  4. 27
      mesonbuild/backend/vs2010backend.py
  5. 2
      mesonbuild/build.py
  6. 2
      test cases/common/13 pch/c/meson.build
  7. 5
      test cases/common/13 pch/c/pch/prog_pch.c
  8. 2
      test cases/common/13 pch/cpp/meson.build
  9. 5
      test cases/common/13 pch/cpp/pch/prog_pch.cc
  10. 2
      test cases/common/13 pch/generated/meson.build
  11. 5
      test cases/common/13 pch/generated/pch/prog_pch.c
  12. 1
      test cases/common/13 pch/meson.build
  13. 15
      test cases/common/13 pch/mixed/meson.build
  14. 1
      test cases/common/13 pch/mixed/pch/func_pch.c
  15. 1
      test cases/common/13 pch/mixed/pch/main_pch.cc
  16. 10
      test cases/common/13 pch/userDefined/meson.build
  17. 5
      test cases/common/13 pch/userDefined/pch/pch.c
  18. 1
      test cases/common/13 pch/userDefined/pch/pch.h
  19. 8
      test cases/common/13 pch/userDefined/prog.c
  20. 2
      test cases/common/13 pch/withIncludeDirectories/meson.build
  21. 5
      test cases/common/13 pch/withIncludeDirectories/pch/prog_pch.c

@ -75,8 +75,11 @@ executable('multilang', sources : srclist,
Using precompiled headers with MSVC
--
Since Meson version 0.50.0, precompiled headers with MSVC work just like
with GCC. Meson will automatically create the matching pch implementation
file for you.
MSVC is a bit trickier, because in addition to the header file, it
Before version 0.50.0, in addition to the header file, Meson
also requires a corresponding source file. If your header is called
`foo_pch.h`, the corresponding source file is usually called
`foo_pch.cpp` and it resides in the same `pch` subdirectory as the

@ -515,6 +515,23 @@ class Backend:
args += compiler.get_pch_use_args(pchpath, p[0])
return includeargs + args
def create_msvc_pch_implementation(self, target, lang, pch_header):
# We have to include the language in the file name, otherwise
# pch.c and pch.cpp will both end up as pch.obj in VS backends.
impl_name = 'meson_pch-%s.%s' % (lang, lang)
pch_rel_to_build = os.path.join(self.get_target_private_dir(target), impl_name)
# Make sure to prepend the build dir, since the working directory is
# not defined. Otherwise, we might create the file in the wrong path.
pch_file = os.path.join(self.build_dir, pch_rel_to_build)
os.makedirs(os.path.dirname(pch_file), exist_ok=True)
content = '#include "%s"' % os.path.basename(pch_header)
pch_file_tmp = pch_file + '.tmp'
with open(pch_file_tmp, 'w') as f:
f.write(content)
mesonlib.replace_if_different(pch_file, pch_file_tmp)
return pch_rel_to_build
@staticmethod
def escape_extra_args(compiler, args):
# No extra escaping/quoting needed when not running on Windows

@ -2252,22 +2252,28 @@ rule FORTRAN_DEP_HACK%s
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 MesonException('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)
if len(pch) == 1:
# Auto generate PCH.
source = self.create_msvc_pch_implementation(target, compiler.get_language(), pch[0])
pch_header_dir = os.path.dirname(os.path.join(self.build_to_src, target.get_source_subdir(), header))
commands += compiler.get_include_args(pch_header_dir, False)
else:
source = os.path.join(self.build_to_src, target.get_source_subdir(), pch[1])
just_name = os.path.basename(header)
(objname, pch_args) = compiler.gen_pch_args(just_name, source, dst)
commands += pch_args
commands += self._generate_single_compile(target, compiler)
commands += self.get_compile_debugfile_args(compiler, target, objname)
dep = dst + '.' + compiler.get_depfile_suffix()
return commands, dep, dst, [objname]
return commands, dep, dst, [objname], source
def generate_gcc_pch_command(self, target, compiler, pch):
commands = self._generate_single_compile(target, compiler)
@ -2296,8 +2302,7 @@ rule FORTRAN_DEP_HACK%s
raise InvalidArguments(msg)
compiler = target.compilers[lang]
if isinstance(compiler, VisualStudioCCompiler):
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)
(commands, dep, dst, objs, src) = self.generate_msvc_pch_command(target, compiler, pch)
extradep = os.path.join(self.build_to_src, target.get_source_subdir(), pch[0])
elif compiler.id == 'intel':
# Intel generates on target generation

@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
import os
import pickle
import xml.dom.minidom
@ -1035,13 +1036,18 @@ class Vs2010Backend(backends.Backend):
continue
pch_node.text = 'Use'
if compiler.id == 'msvc':
if len(pch) != 2:
raise MesonException('MSVC requires one header and one source to produce precompiled headers.')
pch_sources[lang] = [pch[0], pch[1], lang]
if len(pch) == 1:
# Auto generate PCH.
src = os.path.join(down, self.create_msvc_pch_implementation(target, lang, pch[0]))
pch_header_dir = os.path.dirname(os.path.join(proj_to_src_dir, pch[0]))
else:
src = os.path.join(proj_to_src_dir, pch[1])
pch_header_dir = None
pch_sources[lang] = [pch[0], src, lang, pch_header_dir]
else:
# I don't know whether its relevant but let's handle other compilers
# used with a vs backend
pch_sources[lang] = [pch[0], None, lang]
pch_sources[lang] = [pch[0], None, lang, None]
if len(pch_sources) == 1:
# If there is only 1 language with precompiled headers, we can use it for the entire project, which
# is cleaner than specifying it for each source file.
@ -1205,14 +1211,19 @@ class Vs2010Backend(backends.Backend):
self.add_preprocessor_defines(lang, inc_cl, file_defines)
self.add_include_dirs(lang, inc_cl, file_inc_dirs)
for lang in pch_sources:
header, impl, suffix = pch_sources[lang]
impl = pch_sources[lang][1]
if impl:
relpath = os.path.join(proj_to_src_dir, impl)
inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=relpath)
inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=impl)
self.create_pch(pch_sources, lang, inc_cl)
self.add_additional_options(lang, inc_cl, file_args)
self.add_preprocessor_defines(lang, inc_cl, file_defines)
self.add_include_dirs(lang, inc_cl, file_inc_dirs)
pch_header_dir = pch_sources[lang][3]
if pch_header_dir:
inc_dirs = copy.deepcopy(file_inc_dirs)
inc_dirs[lang] = [pch_header_dir] + inc_dirs[lang]
else:
inc_dirs = file_inc_dirs
self.add_include_dirs(lang, inc_cl, inc_dirs)
if self.has_objects(objects, additional_objects, gen_objs):
inc_objs = ET.SubElement(root, 'ItemGroup')

@ -1096,6 +1096,8 @@ You probably should put it in link_with instead.''')
if (os.path.dirname(pchlist[0]) != os.path.dirname(pchlist[1])):
raise InvalidArguments('PCH files must be stored in the same folder.')
mlog.warning('PCH source files are deprecated, only a single header file should be used.')
elif len(pchlist) > 2:
raise InvalidArguments('PCH definition may have a maximum of 2 files.')
for f in pchlist:

@ -5,4 +5,4 @@ if cc_id == 'lcc'
endif
exe = executable('prog', 'prog.c',
c_pch : ['pch/prog_pch.c', 'pch/prog.h'])
c_pch : 'pch/prog.h')

@ -1,5 +0,0 @@
#if !defined(_MSC_VER)
#error "This file is only for use with MSVC."
#endif
#include "prog.h"

@ -1 +1 @@
exe = executable('prog', 'prog.cc', cpp_pch : ['pch/prog.hh', 'pch/prog_pch.cc'])
exe = executable('prog', 'prog.cc', cpp_pch : 'pch/prog.hh')

@ -1,5 +0,0 @@
#if !defined(_MSC_VER)
#error "This file is only for use with MSVC."
#endif
#include "prog.hh"

@ -13,4 +13,4 @@ generated_generator = generator(find_program('gen_generator.py'),
arguments: ['@INPUT@', '@OUTPUT@'])
exe = executable('prog', 'prog.c', generated_customTarget, generated_generator.process('generated_generator.in'),
c_pch: ['pch/prog_pch.c', 'pch/prog.h'])
c_pch: 'pch/prog.h')

@ -1,5 +0,0 @@
#if !defined(_MSC_VER)
#error "This file is only for use with MSVC."
#endif
#include "prog.h"

@ -3,6 +3,7 @@ project('pch test', 'c', 'cpp')
subdir('c')
subdir('cpp')
subdir('generated')
subdir('userDefined')
subdir('withIncludeDirectories')
if meson.backend() == 'xcode'

@ -1,17 +1,6 @@
exe = executable(
'prog',
files('main.cc', 'func.c'),
c_pch : ['pch/func.h', 'pch/func_pch.c'],
cpp_pch : ['pch/main_pch.cc', 'pch/main.h'],
c_pch : ['pch/func.h'],
cpp_pch : ['pch/main.h'],
)
# test pch when only a header is given (not supported by msvc)
cc = meson.get_compiler('c')
if not ['msvc', 'clang-cl'].contains(cc.get_id())
exe2 = executable(
'prog2',
files('main.cc', 'func.c'),
c_pch : 'pch/func.h',
cpp_pch : 'pch/main.h',
)
endif

@ -0,0 +1,10 @@
cc = meson.get_compiler('c')
cc_id = cc.get_id()
# User supplied PCH implementation should override the auto
# generated one. PCH implementations are only supported for
# msvc and generally should not be used at all. Support for
# them is only kept for backwards compatibility.
if cc_id == 'msvc'
exe = executable('prog', 'prog.c', c_pch : ['pch/pch.h', 'pch/pch.c'])
endif

@ -0,0 +1,5 @@
#include "pch.h"
int foo() {
return 0;
}

@ -0,0 +1,8 @@
// No includes here, they need to come from the PCH
int main(int argc, char **argv) {
// Method is implemented in pch.c.
// This makes sure that we can properly handle user defined
// pch implementation files and not only auto-generated ones.
return foo();
}

@ -6,4 +6,4 @@ endif
exe = executable('prog', 'prog.c',
include_directories: 'include',
c_pch : ['pch/prog_pch.c', 'pch/prog.h'])
c_pch : 'pch/prog.h')

@ -1,5 +0,0 @@
#if !defined(_MSC_VER)
#error "This file is only for use with MSVC."
#endif
#include "prog.h"
Loading…
Cancel
Save