diff options
| author | Dylan Baker <dylan@pnwbakers.com> | 2025-11-03 11:04:35 -0800 |
|---|---|---|
| committer | Dylan Baker <dylan@pnwbakers.com> | 2025-11-12 08:14:37 -0800 |
| commit | 176c6d27e3e84f884df42cff6813cc5ca77f91c8 (patch) | |
| tree | 04ce87d3666b01ec8d949e5d4c3d0d0446ee51e2 | |
| parent | 4f1c618392972c935d83eb7939234b3b90479df5 (diff) | |
| download | meson-176c6d27e3e84f884df42cff6813cc5ca77f91c8.tar.gz | |
build: Use a tuple for pch data
This really isn't a list because it's not homogenous data, it's really
`tuple[str, str | None] | None`, but we're using list length to decide
what to do with it, and that makes for strict null issues, as an
accurate annotation would be `list[str | None]`, which would require a
lot of `is not None` checking. By using a tuple we don't need to keep
checking length, which is more expensive than null checking.
To ensure correctness I annotated some things in the VS backend
| -rw-r--r-- | mesonbuild/backend/backends.py | 15 | ||||
| -rw-r--r-- | mesonbuild/backend/ninjabackend.py | 22 | ||||
| -rw-r--r-- | mesonbuild/backend/vs2010backend.py | 22 | ||||
| -rw-r--r-- | mesonbuild/backend/xcodebackend.py | 4 | ||||
| -rw-r--r-- | mesonbuild/build.py | 19 | ||||
| -rw-r--r-- | mesonbuild/interpreter/interpreter.py | 2 | ||||
| -rw-r--r-- | mesonbuild/interpreter/type_checking.py | 18 |
7 files changed, 53 insertions, 49 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 76bd20932..7fba3f3fb 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -824,10 +824,11 @@ class Backend: # MSVC generate an object file for PCH if extobj.pch and self.target_uses_pch(extobj.target): for lang, pch in extobj.target.pch.items(): - compiler = extobj.target.compilers[lang] - if compiler.get_argument_syntax() == 'msvc': - objname = self.get_msvc_pch_objname(lang, pch) - result.append(os.path.join(targetdir, objname)) + if pch: + compiler = extobj.target.compilers[lang] + if compiler.get_argument_syntax() == 'msvc': + objname = self.get_msvc_pch_objname(lang, pch) + result.append(os.path.join(targetdir, objname)) # extobj could contain only objects and no sources if not sources: @@ -862,13 +863,13 @@ class Backend: args: T.List[str] = [] pchpath = self.get_target_private_dir(target) includeargs = compiler.get_include_args(pchpath, False) - p = target.get_pch(compiler.get_language()) + p = target.pch.get(compiler.get_language()) if p: args += compiler.get_pch_use_args(pchpath, p[0]) return includeargs + args - def get_msvc_pch_objname(self, lang: str, pch: T.List[str]) -> str: - if len(pch) == 1: + def get_msvc_pch_objname(self, lang: str, pch: T.Tuple[str, T.Optional[str]]) -> str: + if pch[1] is None: # Same name as in create_msvc_pch_implementation() below. return f'meson_pch-{lang}.obj' return os.path.splitext(pch[1])[0] + '.obj' diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 3510d19b4..90c61a26b 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3180,11 +3180,11 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) # Add MSVC debug file generation compile flags: /Fd /FS commands += self.get_compile_debugfile_args(compiler, target, rel_obj) - # PCH handling - if self.target_uses_pch(target): - pchlist = target.get_pch(compiler.language) + # PCH handling. We only support PCH for C and C++ + if compiler.language in {'c', 'cpp'} and target.has_pch() and self.target_uses_pch(target): + pchlist = target.pch[compiler.language] else: - pchlist = [] + pchlist = None if not pchlist: pch_dep = [] elif compiler.id == 'intel': @@ -3328,7 +3328,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) for lt in itertools.chain(target.link_targets, target.link_whole_targets) ] - def generate_msvc_pch_command(self, target, compiler, pch): + def generate_msvc_pch_command(self, target, compiler, pch: T.Tuple[str, T.Optional[str]]): header = pch[0] pchname = compiler.get_pch_name(header) dst = os.path.join(self.get_target_private_dir(target), pchname) @@ -3336,7 +3336,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) commands = [] commands += self.generate_basic_compiler_args(target, compiler) - if len(pch) == 1: + if pch[1] is None: # 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)) @@ -3355,7 +3355,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) return commands, dep, dst, link_objects, source - def generate_gcc_pch_command(self, target, compiler, pch): + def generate_gcc_pch_command(self, target, compiler, pch: str): commands = self._generate_single_compile(target, compiler) if pch.split('.')[-1] == 'h' and compiler.language == 'cpp': # Explicitly compile pch headers as C++. If Clang is invoked in C++ mode, it actually warns if @@ -3366,21 +3366,21 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) dep = dst + '.' + compiler.get_depfile_suffix() return commands, dep, dst, [] # Gcc does not create an object file during pch generation. - def generate_mwcc_pch_command(self, target, compiler, pch): + def generate_mwcc_pch_command(self, target, compiler, pch: str): commands = self._generate_single_compile(target, compiler) dst = os.path.join(self.get_target_private_dir(target), os.path.basename(pch) + '.' + compiler.get_pch_suffix()) dep = os.path.splitext(dst)[0] + '.' + compiler.get_depfile_suffix() return commands, dep, dst, [] # mwcc compilers do not create an object file during pch generation. - def generate_pch(self, target, header_deps=None): + def generate_pch(self, target: build.BuildTarget, header_deps=None): header_deps = header_deps if header_deps is not None else [] pch_objects = [] for lang in ['c', 'cpp']: - pch = target.get_pch(lang) + pch = target.pch[lang] if not pch: continue - if not has_path_sep(pch[0]) or not has_path_sep(pch[-1]): + if not has_path_sep(pch[0]) or (pch[1] and has_path_sep(pch[1])): msg = f'Precompiled header of {target.get_basename()!r} must not be in the same ' \ 'directory as source, please put it in a subdirectory.' raise InvalidArguments(msg) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 9c9f61259..1753ef002 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -820,23 +820,27 @@ class Vs2010Backend(backends.Backend): return 'masm' raise MesonException(f'Could not guess language from source file {src}.') - def add_pch(self, pch_sources, lang, inc_cl): + def add_pch(self, pch_sources: T.Dict[str, T.Tuple[str, T.Optional[str], str, T.Optional[str]]], + lang, inc_cl) -> None: if lang in pch_sources: self.use_pch(pch_sources, lang, inc_cl) - def create_pch(self, pch_sources, lang, inc_cl): + def create_pch(self, pch_sources: T.Dict[str, T.Tuple[str, T.Optional[str], str, T.Optional[str]]], + lang, inc_cl) -> None: pch = ET.SubElement(inc_cl, 'PrecompiledHeader') pch.text = 'Create' self.add_pch_files(pch_sources, lang, inc_cl) - def use_pch(self, pch_sources, lang, inc_cl): + def use_pch(self, pch_sources: T.Dict[str, T.Tuple[str, T.Optional[str], str, T.Optional[str]]], + lang, inc_cl) -> None: pch = ET.SubElement(inc_cl, 'PrecompiledHeader') pch.text = 'Use' header = self.add_pch_files(pch_sources, lang, inc_cl) pch_include = ET.SubElement(inc_cl, 'ForcedIncludeFiles') pch_include.text = header + ';%(ForcedIncludeFiles)' - def add_pch_files(self, pch_sources, lang, inc_cl): + def add_pch_files(self, pch_sources: T.Dict[str, T.Tuple[str, T.Optional[str], str, T.Optional[str]]], + lang, inc_cl) -> str: header = os.path.basename(pch_sources[lang][0]) pch_file = ET.SubElement(inc_cl, 'PrecompiledHeaderFile') # When USING PCHs, MSVC will not do the regular include @@ -1687,25 +1691,25 @@ class Vs2010Backend(backends.Backend): else: return False - pch_sources = {} + pch_sources: T.Dict[str, T.Tuple[str, T.Optional[str], str, T.Optional[str]]] = {} if self.target_uses_pch(target): for lang in ['c', 'cpp']: - pch = target.get_pch(lang) + pch = target.pch[lang] if not pch: continue if compiler.id == 'msvc': - if len(pch) == 1: + if pch[1] is None: # Auto generate PCH. src = os.path.join(proj_to_build_root, 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] + 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, None] + pch_sources[lang] = (pch[0], None, lang, None) previous_includes = [] if len(headers) + len(gen_hdrs) + len(target.extra_files) + len(pch_sources) > 0: diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 87e1309a6..95a62a02a 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -1802,9 +1802,7 @@ class XCodeBackend(backends.Backend): # Xcode uses GCC_PREFIX_HEADER which only allows one file per target/executable. Precompiling various header files and # applying a particular pch to each source file will require custom scripts (as a build phase) and build flags per each # file. Since Xcode itself already discourages precompiled headers in favor of modules we don't try much harder here. - pchs = target.get_pch('c') + target.get_pch('cpp') + target.get_pch('objc') + target.get_pch('objcpp') - # Make sure to use headers (other backends require implementation files like *.c *.cpp, etc; these should not be used here) - pchs = [pch for pch in pchs if pch.endswith('.h') or pch.endswith('.hh') or pch.endswith('hpp')] + pchs = [t[0] for t in [target.pch['c'], target.pch['cpp']] if t is not None] if pchs: if len(pchs) > 1: mlog.warning(f'Unsupported Xcode configuration: More than 1 precompiled header found "{pchs!s}". Target "{target.name}" might not compile correctly.') diff --git a/mesonbuild/build.py b/mesonbuild/build.py index c2b218955..4721df113 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -70,8 +70,8 @@ if T.TYPE_CHECKING: build_by_default: bool build_rpath: str - c_pch: T.List[str] - cpp_pch: T.List[str] + c_pch: T.Optional[T.Tuple[str, T.Optional[str]]] + cpp_pch: T.Optional[T.Tuple[str, T.Optional[str]]] d_debug: T.List[T.Union[str, int]] d_import_dirs: T.List[IncludeDirs] d_module_versions: T.List[T.Union[str, int]] @@ -807,7 +807,7 @@ class BuildTarget(Target): # The list of all files outputted by this target. Useful in cases such # as Vala which generates .vapi and .h besides the compiled output. self.outputs = [self.filename] - self.pch: T.Dict[str, T.List[str]] = {} + self.pch: T.Dict[str, T.Optional[T.Tuple[str, T.Optional[str]]]] = {} self.extra_args: T.DefaultDict[str, T.List[str]] = kwargs.get('language_args', defaultdict(list)) self.sources: T.List[File] = [] # If the same source is defined multiple times, use it only once. @@ -1266,8 +1266,8 @@ class BuildTarget(Target): self.raw_overrides = kwargs.get('override_options', {}) - self.add_pch('c', kwargs.get('c_pch', [])) - self.add_pch('cpp', kwargs.get('cpp_pch', [])) + self.pch['c'] = kwargs.get('c_pch') + self.pch['cpp'] = kwargs.get('cpp_pch') self.link_args = extract_as_list(kwargs, 'link_args') for i in self.link_args: @@ -1416,10 +1416,7 @@ class BuildTarget(Target): return self.install def has_pch(self) -> bool: - return bool(self.pch) - - def get_pch(self, language: str) -> T.List[str]: - return self.pch.get(language, []) + return any(x is not None for x in self.pch.values()) def get_include_dirs(self) -> T.List['IncludeDirs']: return self.include_dirs @@ -1590,10 +1587,6 @@ class BuildTarget(Target): else: mlog.warning(msg + ' This will fail in cross build.') - def add_pch(self, language: str, pchlist: T.List[str]) -> None: - if pchlist: - self.pch[language] = pchlist - def add_include_dirs(self, args: T.Sequence['IncludeDirs'], set_is_system: T.Optional[str] = None) -> None: ids: T.List['IncludeDirs'] = [] for a in args: diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 36647f32c..a3cdde04a 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3479,7 +3479,7 @@ class Interpreter(InterpreterBase, HoldableObject): self.check_for_jar_sources(sources, targetclass) kwargs['d_import_dirs'] = self.extract_incdirs(kwargs, 'd_import_dirs') missing: T.List[str] = [] - for each in itertools.chain(kwargs['c_pch'], kwargs['cpp_pch']): + for each in itertools.chain(kwargs['c_pch'] or [], kwargs['cpp_pch'] or []): if each is not None: if not os.path.isfile(os.path.join(self.environment.source_dir, self.subdir, each)): missing.append(os.path.join(self.subdir, each)) diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 5dc9127f3..6cd43b7c6 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -679,11 +679,19 @@ def _pch_feature_validator(args: T.List[str]) -> T.Iterable[FeatureCheckBase]: yield FeatureDeprecated('PCH source files', '0.50.0', 'Only a single header file should be used.') -def _pch_convertor(args: T.List[str]) -> T.List[str]: - # Flip so that we always have [header, src] - if len(args) == 2 and compilers.is_source(args[0]): - return [args[1], args[0]] - return args +def _pch_convertor(args: T.List[str]) -> T.Optional[T.Tuple[str, T.Optional[str]]]: + num_args = len(args) + + if num_args == 1: + return (args[0], None) + + if num_args == 2: + if compilers.is_source(args[0]): + # Flip so that we always have [header, src] + return (args[1], args[0]) + return (args[0], args[1]) + + return None _PCH_ARGS: KwargInfo[T.List[str]] = KwargInfo( |
