Merge pull request #10916 from xclaesse/preprocess

Add cc.preprocess() method
pull/10945/head
Jussi Pakkanen 2 years ago committed by GitHub
commit 4c2b64188d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      docs/markdown/snippets/preprocess.md
  2. 22
      docs/yaml/objects/compiler.yaml
  3. 29
      mesonbuild/backend/backends.py
  4. 35
      mesonbuild/backend/ninjabackend.py
  5. 19
      mesonbuild/backend/vs2010backend.py
  6. 43
      mesonbuild/build.py
  7. 12
      mesonbuild/compilers/compilers.py
  8. 18
      mesonbuild/compilers/mixins/clike.py
  9. 6
      mesonbuild/compilers/mixins/gnu.py
  10. 5
      mesonbuild/compilers/mixins/visualstudio.py
  11. 38
      mesonbuild/interpreter/compiler.py
  12. 3
      test cases/common/255 preprocess/bar.c
  13. 1
      test cases/common/255 preprocess/foo.c
  14. 2
      test cases/common/255 preprocess/foo.h
  15. 15
      test cases/common/255 preprocess/meson.build
  16. 3
      test cases/common/255 preprocess/src/file.map.in
  17. 3
      test cases/common/255 preprocess/src/meson.build

@ -0,0 +1,14 @@
## New method to preprocess source files
Compiler object has a new `preprocess()` method. It is supported by all C/C++
compilers. It preprocess sources without compiling them.
The preprocessor will receive the same arguments (include directories, defines,
etc) as with normal compilation. That includes for example args added with
`add_project_arguments()`, or on the command line with `-Dc_args=-DFOO`.
```meson
cc = meson.get_compiler('c')
pp_files = cc.preprocess('foo.c', 'bar.c', output: '@PLAINNAME@')
exe = executable('app', pp_files)
```

@ -586,3 +586,25 @@ methods:
gcc or msvc, but use the same argument syntax as one of those two compilers gcc or msvc, but use the same argument syntax as one of those two compilers
such as clang or icc, especially when they use different syntax on different such as clang or icc, especially when they use different syntax on different
operating systems. operating systems.
- name: preprocess
returns: list[custom_idx]
since: 0.64.0
description: |
Preprocess a list of source files but do not compile them. The preprocessor
will receive the same arguments (include directories, defines, etc) as with
normal compilation. That includes for example args added with
`add_project_arguments()`, or on the command line with `-Dc_args=-DFOO`.
varargs_inherit: _build_target_base
kwargs:
output:
type: str
description: |
Template for name of preprocessed files: `@PLAINNAME@` is replaced by
the source filename and `@BASENAME@` is replaced by the source filename
without its extension.
compile_args:
type: list[str]
description: |
Extra flags to pass to the preprocessor

@ -50,6 +50,8 @@ if T.TYPE_CHECKING:
from typing_extensions import TypedDict from typing_extensions import TypedDict
_ALL_SOURCES_TYPE = T.List[T.Union[File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]]
class TargetIntrospectionData(TypedDict): class TargetIntrospectionData(TypedDict):
language: str language: str
@ -792,6 +794,8 @@ class Backend:
def object_filename_from_source(self, target: build.BuildTarget, source: 'FileOrString') -> str: def object_filename_from_source(self, target: build.BuildTarget, source: 'FileOrString') -> str:
assert isinstance(source, mesonlib.File) assert isinstance(source, mesonlib.File)
if isinstance(target, build.CompileTarget):
return target.sources_map[source]
build_dir = self.environment.get_build_dir() build_dir = self.environment.get_build_dir()
rel_src = source.rel_to_builddir(self.build_to_src) rel_src = source.rel_to_builddir(self.build_to_src)
@ -1862,3 +1866,28 @@ class Backend:
else: else:
env.prepend('PATH', list(extra_paths)) env.prepend('PATH', list(extra_paths))
return env return env
def compiler_to_generator(self, target: build.BuildTarget,
compiler: 'Compiler',
sources: _ALL_SOURCES_TYPE,
output_templ: str) -> build.GeneratedList:
'''
Some backends don't support custom compilers. This is a convenience
method to convert a Compiler to a Generator.
'''
exelist = compiler.get_exelist()
exe = programs.ExternalProgram(exelist[0])
args = exelist[1:]
# FIXME: There are many other args missing
commands = self.generate_basic_compiler_args(target, compiler)
commands += compiler.get_dependency_gen_args('@OUTPUT@', '@DEPFILE@')
commands += compiler.get_output_args('@OUTPUT@')
commands += compiler.get_compile_only_args() + ['@INPUT@']
commands += self.get_source_dir_include_args(target, compiler)
commands += self.get_build_dir_include_args(target, compiler)
generator = build.Generator(exe, args + commands.to_native(), [output_templ], depfile='@PLAINNAME@.d')
return generator.process_files(sources, self.interpreter)
def compile_target_to_generator(self, target: build.CompileTarget) -> build.GeneratedList:
all_sources = T.cast('_ALL_SOURCES_TYPE', target.sources) + T.cast('_ALL_SOURCES_TYPE', target.generated)
return self.compiler_to_generator(target, target.compiler, all_sources, target.output_templ)

@ -666,10 +666,10 @@ class NinjaBackend(backends.Backend):
# TODO: Rather than an explicit list here, rules could be marked in the # TODO: Rather than an explicit list here, rules could be marked in the
# rule store as being wanted in compdb # rule store as being wanted in compdb
for for_machine in MachineChoice: for for_machine in MachineChoice:
for lang in self.environment.coredata.compilers[for_machine]: for compiler in self.environment.coredata.compilers[for_machine].values():
rules += [f"{rule}{ext}" for rule in [self.get_compiler_rule_name(lang, for_machine)] rules += [f"{rule}{ext}" for rule in [self.compiler_to_rule_name(compiler)]
for ext in ['', '_RSP']] for ext in ['', '_RSP']]
rules += [f"{rule}{ext}" for rule in [self.get_pch_rule_name(lang, for_machine)] rules += [f"{rule}{ext}" for rule in [self.compiler_to_pch_rule_name(compiler)]
for ext in ['', '_RSP']] for ext in ['', '_RSP']]
compdb_options = ['-x'] if mesonlib.version_compare(self.ninja_version, '>=1.9') else [] compdb_options = ['-x'] if mesonlib.version_compare(self.ninja_version, '>=1.9') else []
ninja_compdb = self.ninja_command + ['-t', 'compdb'] + compdb_options + rules ninja_compdb = self.ninja_command + ['-t', 'compdb'] + compdb_options + rules
@ -986,6 +986,9 @@ class NinjaBackend(backends.Backend):
obj_list.append(o) obj_list.append(o)
compiled_sources.append(s) compiled_sources.append(s)
source2object[s] = o source2object[s] = o
if isinstance(target, build.CompileTarget):
# Skip the link stage for this special type of target
return
linker, stdlib_args = self.determine_linker_and_stdlib_args(target) linker, stdlib_args = self.determine_linker_and_stdlib_args(target)
if isinstance(target, build.StaticLibrary) and target.prelink: if isinstance(target, build.StaticLibrary) and target.prelink:
final_obj_list = self.generate_prelink(target, obj_list) final_obj_list = self.generate_prelink(target, obj_list)
@ -1426,7 +1429,7 @@ class NinjaBackend(backends.Backend):
commands += self.build.get_project_args(compiler, target.subproject, target.for_machine) commands += self.build.get_project_args(compiler, target.subproject, target.for_machine)
commands += self.build.get_global_args(compiler, target.for_machine) commands += self.build.get_global_args(compiler, target.for_machine)
elem = NinjaBuildElement(self.all_outputs, outputs, self.get_compiler_rule_name('cs', target.for_machine), rel_srcs + generated_rel_srcs) elem = NinjaBuildElement(self.all_outputs, outputs, self.compiler_to_rule_name(compiler), rel_srcs + generated_rel_srcs)
elem.add_dep(deps) elem.add_dep(deps)
elem.add_item('ARGS', commands) elem.add_item('ARGS', commands)
self.add_build(elem) self.add_build(elem)
@ -1959,7 +1962,7 @@ class NinjaBackend(backends.Backend):
getattr(target, 'rust_crate_type', '') == 'procmacro', getattr(target, 'rust_crate_type', '') == 'procmacro',
output, project_deps) output, project_deps)
compiler_name = self.get_compiler_rule_name('rust', target.for_machine) compiler_name = self.compiler_to_rule_name(rustc)
element = NinjaBuildElement(self.all_outputs, target_name, compiler_name, main_rust_file) element = NinjaBuildElement(self.all_outputs, target_name, compiler_name, main_rust_file)
if orderdeps: if orderdeps:
element.add_orderdep(orderdeps) element.add_orderdep(orderdeps)
@ -1978,20 +1981,16 @@ class NinjaBackend(backends.Backend):
return PerMachine('_FOR_BUILD', '')[for_machine] return PerMachine('_FOR_BUILD', '')[for_machine]
@classmethod @classmethod
def get_compiler_rule_name(cls, lang: str, for_machine: MachineChoice) -> str: def get_compiler_rule_name(cls, lang: str, for_machine: MachineChoice, mode: str = 'COMPILER') -> str:
return '{}_COMPILER{}'.format(lang, cls.get_rule_suffix(for_machine)) return f'{lang}_{mode}{cls.get_rule_suffix(for_machine)}'
@classmethod
def get_pch_rule_name(cls, lang: str, for_machine: MachineChoice) -> str:
return '{}_PCH{}'.format(lang, cls.get_rule_suffix(for_machine))
@classmethod @classmethod
def compiler_to_rule_name(cls, compiler: Compiler) -> str: def compiler_to_rule_name(cls, compiler: Compiler) -> str:
return cls.get_compiler_rule_name(compiler.get_language(), compiler.for_machine) return cls.get_compiler_rule_name(compiler.get_language(), compiler.for_machine, compiler.mode)
@classmethod @classmethod
def compiler_to_pch_rule_name(cls, compiler: Compiler) -> str: def compiler_to_pch_rule_name(cls, compiler: Compiler) -> str:
return cls.get_pch_rule_name(compiler.get_language(), compiler.for_machine) return cls.get_compiler_rule_name(compiler.get_language(), compiler.for_machine, 'PCH')
def swift_module_file_name(self, target): def swift_module_file_name(self, target):
return os.path.join(self.get_target_private_dir(target), return os.path.join(self.get_target_private_dir(target),
@ -2090,7 +2089,7 @@ class NinjaBackend(backends.Backend):
objects.append(oname) objects.append(oname)
rel_objects.append(os.path.join(self.get_target_private_dir(target), oname)) rel_objects.append(os.path.join(self.get_target_private_dir(target), oname))
rulename = self.get_compiler_rule_name('swift', target.for_machine) rulename = self.compiler_to_rule_name(swiftc)
# Swiftc does not seem to be able to emit objects and module files in one go. # Swiftc does not seem to be able to emit objects and module files in one go.
elem = NinjaBuildElement(self.all_outputs, rel_objects, rulename, abssrc) elem = NinjaBuildElement(self.all_outputs, rel_objects, rulename, abssrc)
@ -2099,9 +2098,7 @@ class NinjaBackend(backends.Backend):
elem.add_item('ARGS', compile_args + header_imports + abs_generated + module_includes) elem.add_item('ARGS', compile_args + header_imports + abs_generated + module_includes)
elem.add_item('RUNDIR', rundir) elem.add_item('RUNDIR', rundir)
self.add_build(elem) self.add_build(elem)
elem = NinjaBuildElement(self.all_outputs, out_module_name, elem = NinjaBuildElement(self.all_outputs, out_module_name, rulename, abssrc)
self.get_compiler_rule_name('swift', target.for_machine),
abssrc)
elem.add_dep(in_module_files + rel_generated) 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('ARGS', compile_args + abs_generated + module_includes + swiftc.get_mod_gen_args())
elem.add_item('RUNDIR', rundir) elem.add_item('RUNDIR', rundir)
@ -2312,7 +2309,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
crstr = self.get_rule_suffix(compiler.for_machine) crstr = self.get_rule_suffix(compiler.for_machine)
if langname == 'fortran': if langname == 'fortran':
self.generate_fortran_dep_hack(crstr) self.generate_fortran_dep_hack(crstr)
rule = self.get_compiler_rule_name(langname, compiler.for_machine) rule = self.compiler_to_rule_name(compiler)
depargs = NinjaCommandArg.list(compiler.get_dependency_gen_args('$out', '$DEPFILE'), Quoting.none) depargs = NinjaCommandArg.list(compiler.get_dependency_gen_args('$out', '$DEPFILE'), Quoting.none)
command = compiler.get_exelist() command = compiler.get_exelist()
args = ['$ARGS'] + depargs + NinjaCommandArg.list(compiler.get_output_args('$out'), Quoting.none) + compiler.get_compile_only_args() + ['$in'] args = ['$ARGS'] + depargs + NinjaCommandArg.list(compiler.get_output_args('$out'), Quoting.none) + compiler.get_compile_only_args() + ['$in']
@ -2368,6 +2365,8 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
self.generate_llvm_ir_compile_rule(compiler) self.generate_llvm_ir_compile_rule(compiler)
self.generate_compile_rule_for(langname, compiler) self.generate_compile_rule_for(langname, compiler)
self.generate_pch_rule_for(langname, compiler) self.generate_pch_rule_for(langname, compiler)
for mode in compiler.get_modes():
self.generate_compile_rule_for(langname, mode)
def generate_generator_list_rules(self, target): def generate_generator_list_rules(self, target):
# CustomTargets have already written their rules and # CustomTargets have already written their rules and

@ -683,6 +683,23 @@ class Vs2010Backend(backends.Backend):
self.add_target_deps(root, target) self.add_target_deps(root, target)
self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname)
def gen_compile_target_vcxproj(self, target, ofname, guid):
if target.for_machine is MachineChoice.BUILD:
platform = self.build_platform
else:
platform = self.platform
(root, type_config) = self.create_basic_project(target.name,
temp_dir=target.get_id(),
guid=guid,
target_platform=platform)
ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.targets')
target.generated = [self.compile_target_to_generator(target)]
target.sources = []
self.generate_custom_generator_commands(target, root)
self.add_regen_dependency(root)
self.add_target_deps(root, target)
self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname)
@classmethod @classmethod
def lang_from_source_file(cls, src): def lang_from_source_file(cls, src):
ext = src.split('.')[-1] ext = src.split('.')[-1]
@ -876,6 +893,8 @@ class Vs2010Backend(backends.Backend):
return self.gen_custom_target_vcxproj(target, ofname, guid) return self.gen_custom_target_vcxproj(target, ofname, guid)
elif isinstance(target, build.RunTarget): elif isinstance(target, build.RunTarget):
return self.gen_run_target_vcxproj(target, ofname, guid) return self.gen_run_target_vcxproj(target, ofname, guid)
elif isinstance(target, build.CompileTarget):
return self.gen_compile_target_vcxproj(target, ofname, guid)
else: else:
raise MesonException(f'Unknown target type for {target.get_basename()}') raise MesonException(f'Unknown target type for {target.get_basename()}')
# Prefix to use to access the build root from the vcxproj dir # Prefix to use to access the build root from the vcxproj dir

@ -2583,6 +2583,44 @@ class CustomTarget(Target, CommandBase):
def __len__(self) -> int: def __len__(self) -> int:
return len(self.outputs) return len(self.outputs)
class CompileTarget(BuildTarget):
'''
Target that only compile sources without linking them together.
It can be used as preprocessor, or transpiler.
'''
typename = 'compile'
def __init__(self,
name: str,
subdir: str,
subproject: str,
environment: environment.Environment,
sources: T.List[File],
output_templ: str,
compiler: Compiler,
kwargs):
compilers = {compiler.get_language(): compiler}
super().__init__(name, subdir, subproject, compiler.for_machine,
sources, None, [], environment, compilers, kwargs)
self.filename = name
self.compiler = compiler
self.output_templ = output_templ
self.outputs = []
for f in sources:
plainname = os.path.basename(f.fname)
basename = os.path.splitext(plainname)[0]
self.outputs.append(output_templ.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname))
self.sources_map = dict(zip(sources, self.outputs))
def type_suffix(self) -> str:
return "@compile"
@property
def is_unity(self) -> bool:
return False
class RunTarget(Target, CommandBase): class RunTarget(Target, CommandBase):
typename = 'run' typename = 'run'
@ -2707,7 +2745,7 @@ class CustomTargetIndex(HoldableObject):
typename: T.ClassVar[str] = 'custom' typename: T.ClassVar[str] = 'custom'
target: CustomTarget target: T.Union[CustomTarget, CompileTarget]
output: str output: str
def __post_init__(self) -> None: def __post_init__(self) -> None:
@ -2718,8 +2756,7 @@ class CustomTargetIndex(HoldableObject):
return f'{self.target.name}[{self.output}]' return f'{self.target.name}[{self.output}]'
def __repr__(self): def __repr__(self):
return '<CustomTargetIndex: {!r}[{}]>'.format( return '<CustomTargetIndex: {!r}[{}]>'.format(self.target, self.output)
self.target, self.target.get_outputs().index(self.output))
def get_outputs(self) -> T.List[str]: def get_outputs(self) -> T.List[str]:
return [self.output] return [self.output]

@ -494,6 +494,7 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
language: str language: str
id: str id: str
warn_args: T.Dict[str, T.List[str]] warn_args: T.Dict[str, T.List[str]]
mode: str = 'COMPILER'
def __init__(self, exelist: T.List[str], version: str, def __init__(self, exelist: T.List[str], version: str,
for_machine: MachineChoice, info: 'MachineInfo', for_machine: MachineChoice, info: 'MachineInfo',
@ -513,6 +514,7 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
self.linker = linker self.linker = linker
self.info = info self.info = info
self.is_cross = is_cross self.is_cross = is_cross
self.modes: T.List[Compiler] = []
def __repr__(self) -> str: def __repr__(self) -> str:
repr_str = "<{0}: v{1} `{2}`>" repr_str = "<{0}: v{1} `{2}`>"
@ -531,6 +533,9 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
def get_id(self) -> str: def get_id(self) -> str:
return self.id return self.id
def get_modes(self) -> T.List[Compiler]:
return self.modes
def get_linker_id(self) -> str: def get_linker_id(self) -> str:
# There is not guarantee that we have a dynamic linker instance, as # There is not guarantee that we have a dynamic linker instance, as
# some languages don't have separate linkers and compilers. In those # some languages don't have separate linkers and compilers. In those
@ -1050,6 +1055,9 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
def get_preprocess_only_args(self) -> T.List[str]: def get_preprocess_only_args(self) -> T.List[str]:
raise EnvironmentException('This compiler does not have a preprocessor') raise EnvironmentException('This compiler does not have a preprocessor')
def get_preprocess_to_file_args(self) -> T.List[str]:
return self.get_preprocess_only_args()
def get_default_include_dirs(self) -> T.List[str]: def get_default_include_dirs(self) -> T.List[str]:
# TODO: This is a candidate for returning an immutable list # TODO: This is a candidate for returning an immutable list
return [] return []
@ -1290,6 +1298,10 @@ class Compiler(HoldableObject, metaclass=abc.ABCMeta):
def needs_static_linker(self) -> bool: def needs_static_linker(self) -> bool:
raise NotImplementedError(f'There is no static linker for {self.language}') raise NotImplementedError(f'There is no static linker for {self.language}')
def get_preprocessor(self) -> Compiler:
"""Get compiler's preprocessor.
"""
raise EnvironmentException(f'{self.get_id()} does not support preprocessor')
def get_global_options(lang: str, def get_global_options(lang: str,
comp: T.Type[Compiler], comp: T.Type[Compiler],

@ -27,6 +27,7 @@ import itertools
import os import os
import re import re
import subprocess import subprocess
import copy
import typing as T import typing as T
from pathlib import Path from pathlib import Path
@ -145,6 +146,8 @@ class CLikeCompiler(Compiler):
self.exe_wrapper = None self.exe_wrapper = None
else: else:
self.exe_wrapper = exe_wrapper self.exe_wrapper = exe_wrapper
# Lazy initialized in get_preprocessor()
self.preprocessor: T.Optional[Compiler] = None
def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> CLikeCompilerArgs: def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> CLikeCompilerArgs:
# This is correct, mypy just doesn't understand co-operative inheritance # This is correct, mypy just doesn't understand co-operative inheritance
@ -1328,3 +1331,18 @@ class CLikeCompiler(Compiler):
def get_disable_assert_args(self) -> T.List[str]: def get_disable_assert_args(self) -> T.List[str]:
return ['-DNDEBUG'] return ['-DNDEBUG']
@functools.lru_cache(maxsize=None)
def can_compile(self, src: 'mesonlib.FileOrString') -> bool:
# Files we preprocess can be anything, e.g. .in
if self.mode == 'PREPROCESSOR':
return True
return super().can_compile(src)
def get_preprocessor(self) -> Compiler:
if not self.preprocessor:
self.preprocessor = copy.copy(self)
self.preprocessor.exelist = self.exelist + self.get_preprocess_to_file_args()
self.preprocessor.mode = 'PREPROCESSOR'
self.modes.append(self.preprocessor)
return self.preprocessor

@ -318,6 +318,12 @@ class GnuLikeCompiler(Compiler, metaclass=abc.ABCMeta):
def get_coverage_args(self) -> T.List[str]: def get_coverage_args(self) -> T.List[str]:
return ['--coverage'] return ['--coverage']
def get_preprocess_to_file_args(self) -> T.List[str]:
# We want to allow preprocessing files with any extension, such as
# foo.c.in. In that case we need to tell GCC/CLANG to treat them as
# assembly file.
return self.get_preprocess_only_args() + ['-x', 'assembler-with-cpp']
class GnuCompiler(GnuLikeCompiler): class GnuCompiler(GnuLikeCompiler):
""" """

@ -159,6 +159,9 @@ class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
def get_preprocess_only_args(self) -> T.List[str]: def get_preprocess_only_args(self) -> T.List[str]:
return ['/EP'] return ['/EP']
def get_preprocess_to_file_args(self) -> T.List[str]:
return ['/EP', '/P']
def get_compile_only_args(self) -> T.List[str]: def get_compile_only_args(self) -> T.List[str]:
return ['/c'] return ['/c']
@ -173,6 +176,8 @@ class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
return ['/fsanitize=address'] return ['/fsanitize=address']
def get_output_args(self, target: str) -> T.List[str]: def get_output_args(self, target: str) -> T.List[str]:
if self.mode == 'PREPROCESSOR':
return ['/Fi' + target]
if target.endswith('.exe'): if target.endswith('.exe'):
return ['/Fe' + target] return ['/Fe' + target]
return ['/Fo' + target] return ['/Fo' + target]

@ -4,6 +4,7 @@
import enum import enum
import functools import functools
import os
import typing as T import typing as T
from .. import build from .. import build
@ -79,6 +80,11 @@ if T.TYPE_CHECKING:
header_prefix: str header_prefix: str
header_required: T.Union[bool, coredata.UserFeatureOption] header_required: T.Union[bool, coredata.UserFeatureOption]
class PreprocessKW(TypedDict):
output: str
compile_args: T.List[str]
include_directories: T.List[build.IncludeDirs]
class _TestMode(enum.Enum): class _TestMode(enum.Enum):
@ -184,6 +190,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
'first_supported_link_argument': self.first_supported_link_argument_method, 'first_supported_link_argument': self.first_supported_link_argument_method,
'symbols_have_underscore_prefix': self.symbols_have_underscore_prefix_method, 'symbols_have_underscore_prefix': self.symbols_have_underscore_prefix_method,
'get_argument_syntax': self.get_argument_syntax_method, 'get_argument_syntax': self.get_argument_syntax_method,
'preprocess': self.preprocess_method,
}) })
@property @property
@ -734,3 +741,34 @@ class CompilerHolder(ObjectHolder['Compiler']):
@noKwargs @noKwargs
def get_argument_syntax_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: def get_argument_syntax_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return self.compiler.get_argument_syntax() return self.compiler.get_argument_syntax()
@FeatureNew('compiler.preprocess', '0.64.0')
@typed_pos_args('compiler.preprocess', varargs=(mesonlib.File, str), min_varargs=1)
@typed_kwargs(
'compiler.preprocess',
KwargInfo('output', str, default='@PLAINNAME@.i'),
KwargInfo('compile_args', ContainerTypeInfo(list, str), listify=True, default=[]),
_INCLUDE_DIRS_KW,
)
def preprocess_method(self, args: T.Tuple[T.List['mesonlib.FileOrString']], kwargs: 'PreprocessKW') -> T.List[build.CustomTargetIndex]:
compiler = self.compiler.get_preprocessor()
sources = self.interpreter.source_strings_to_files(args[0])
tg_kwargs = {
f'{self.compiler.language}_args': kwargs['compile_args'],
'build_by_default': False,
'include_directories': kwargs['include_directories'],
}
tg = build.CompileTarget(
'preprocessor',
self.interpreter.subdir,
self.subproject,
self.environment,
sources,
kwargs['output'],
compiler,
tg_kwargs)
self.interpreter.add_target(tg.name, tg)
# Expose this target as list of its outputs, so user can pass them to
# other targets, list outputs, etc.
private_dir = os.path.relpath(self.interpreter.backend.get_target_private_dir(tg), self.interpreter.subdir)
return [build.CustomTargetIndex(tg, os.path.join(private_dir, o)) for o in tg.outputs]

@ -0,0 +1,3 @@
int bar(void) {
return BAR;
}

@ -0,0 +1,2 @@
int bar(void);
int main(void) { return FOO + bar(); }

@ -0,0 +1,15 @@
project('preprocess', 'c')
cc = meson.get_compiler('c')
add_project_arguments(['-DFOO=0', '-DBAR=0'], language: 'c')
pp_files = cc.preprocess('foo.c', 'bar.c', output: '@PLAINNAME@')
foreach f : pp_files
message(f.full_path())
endforeach
subdir('src')
test('test-foo', executable('app', pp_files, link_depends: file_map))

@ -0,0 +1,3 @@
file_map = cc.preprocess('file.map.in',
output: '@BASENAME@',
)
Loading…
Cancel
Save