Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
226 changes: 169 additions & 57 deletions mesonbuild/backend/ninjabackend.py

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions mesonbuild/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -1704,6 +1704,9 @@ def get_used_stdlib_args(self, link_language: str) -> T.List[str]:
stdlib_args.extend(all_compilers[dl].language_stdlib_only_link_flags(self.environment))
return stdlib_args

def uses_cpp(self) -> bool:
return 'cpp' in self.compilers

def uses_rust(self) -> bool:
return 'rust' in self.compilers

Expand Down
115 changes: 113 additions & 2 deletions mesonbuild/compilers/cpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,22 @@
from __future__ import annotations

import functools
import os
import os.path
import re
import typing as T

from .. import options
from .. import mlog
from ..mesonlib import MesonException, version_compare
from ..mesonlib import (File, MesonException, MesonBugException, Popen_safe_logged,
version_compare)

from .compilers import (
gnu_winlibs,
msvc_winlibs,
Compiler,
CompileCheckMode,
CompileResult
)
from .c_function_attributes import CXX_FUNC_ATTRIBUTES, C_FUNC_ATTRIBUTES
from .mixins.apple import AppleCompilerMixin, AppleCPPStdsMixin
Expand Down Expand Up @@ -88,7 +92,60 @@ def get_no_stdlib_link_args(self) -> T.List[str]:

def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
code = 'class breakCCompiler;int main(void) { return 0; }\n'
return self._sanity_check_impl(work_dir, environment, 'sanitycheckcpp.cc', code)
self._sanity_check_impl(work_dir, environment, 'sanitycheckcpp.cc', code)
if environment.coredata.optstore.get_value('cpp_import_std'):
self._import_cpp_std_sanity_check(work_dir, environment)

def compile_import_std_module(self,
env: 'Environment',
code: File):
cpp_std = env.coredata.optstore.get_value('cpp_std')
srcname = code.fname
# Construct the compiler command-line
commands = self.compiler_args()
commands.append(f"-std={cpp_std}")
commands.extend(['-Wno-reserved-identifier', '-Wno-reserved-module-identifier'])
commands.append("--precompile")

all_lists_to_add = [self.get_always_args(), self.get_debug_args(env.coredata.optstore.get_value('buildtype') == 'debug'),
self.get_assert_args(disable=env.coredata.optstore.get_value('b_ndebug') in ['if-release', 'true'],
env=env)]
for args_list in all_lists_to_add:
for arg in args_list:
commands.append(arg)
commands.append(srcname)
tmpdirname = env.build_dir

# Preprocess mode outputs to stdout, so no output args
print(f"***{self.get_exelist()}")
output = f'std{self.get_cpp20_module_bmi_extension()}'
commands += self.get_output_args(output)
no_ccache = True
os_env = os.environ.copy()
os_env['LC_ALL'] = 'C'
os_env['CCACHE_DISABLE'] = '1'
command_list = self.get_exelist(ccache=not no_ccache) + commands.to_native()
p, stdo, stde = Popen_safe_logged(command_list, msg="Command line for compiling 'import std' feature", cwd=tmpdirname, env=os_env)
if p.returncode != 0:
raise MesonException("Could not compile library for use with 'import std'")

def get_import_std_lib_source_args(self, env: Environment) -> T.List[str]:
raise MesonException("Your compiler does not support 'import std' feature or it has not been implemented")

def get_import_std_lib_source_file(self) -> str:
raise MesonException("Your compiler does not support 'import std' feature or it has not been implemented")

def get_cpp20_module_bmi_extension(self) -> str:
raise MesonException("Your compiler does not support 'import std' feature or it has not been implemented")

def get_import_std_compile_args(self, environment: 'Environment') -> T.List[str]:
raise MesonException("Your compiler does not support 'import std' feature or it has not been implemented")

def check_cpp_import_std_support(self):
raise MesonException("Your compiler does not support 'import std' feature or it has not been implemented")

def _import_cpp_std_sanity_check(self, work_dir: str, environment: 'Environment') -> None:
self.check_cpp_import_std_support()

def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]:
# -fpermissive allows non-conforming code to compile which is necessary
Expand Down Expand Up @@ -174,9 +231,13 @@ def _find_best_cpp_std(self, cpp_std: str) -> str:
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = super().get_options()
key = self.form_compileropt_key('std')
import_std_key = self.form_compileropt_key('import_std')

opts.update({
key: options.UserStdOption('cpp', ALL_STDS),
import_std_key: options.UseImportStd('cpp')
})

return opts


Expand Down Expand Up @@ -235,6 +296,36 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_
'3': default_warn_args + ['-Wextra', '-Wpedantic'],
'everything': ['-Weverything']}

def check_cpp_import_std_support(self):
if int(self.version.split('.')[0]) < 17:
raise MesonException('Your compiler does not support import std feature. Clang support starts at version >= 17')

def get_import_std_compile_args(self, env: 'Environment') -> T.List[str]:
bmi_path = f'{env.get_build_dir()}/std{self.get_cpp20_module_bmi_extension()}'
return [f'-fmodule-file=std={bmi_path}']

def get_cpp20_module_bmi_extension(self) -> str:
return '.pcm'

def get_import_std_lib_source_args(self, env: Environment) -> T.List[str]:
cpp_std = env.coredata.optstore.get_value('cpp_std')
return [f'-std={cpp_std}',
'-Wno-reserved-identifier',
'-Wno-reserved-module-identifier',
'--precompile']

llvm_dir_re = re.compile(r'(/\D*/(?:\.?\d+)+)/.*')

def get_import_std_lib_source_file(self) -> str:
dirs = [d for d in self.get_preprocessor().get_default_include_dirs() if 'llvm' in d and not '..' in d]
for d in dirs:
if m := type(self).llvm_dir_re.match(d):
break
if not m:
raise MesonBugException('Could not find import std lib source file. This should work')
llvm_dir = str(m[1])
return f'{llvm_dir}/share/libc++/v1/std.cppm'

def get_options(self) -> 'MutableKeyedOptionDictType':
opts = super().get_options()

Expand Down Expand Up @@ -460,6 +551,26 @@ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_
self.supported_warn_args(gnu_common_warning_args) +
self.supported_warn_args(gnu_cpp_warning_args))}

def get_import_std_lib_source_args(self, env: Environment) -> T.List[str]:
cpp_std = env.coredata.optstore.get_value('cpp_std')
return [f"-std={cpp_std}", '-fmodules', '-fsearch-include-path', '-fmodule-only'] + self.get_compile_only_args()

def get_import_std_lib_source_file(self) -> str:
std_lib_dir = [d for d in self.get_preprocessor().get_default_include_dirs()
if "c++" in d and '..' not in d and d[-1].isdigit()][0]
return f'{std_lib_dir}/bits/std.cc'

def get_cpp20_module_bmi_extension(self) -> str:
return '.gcm'

def get_import_std_compile_args(self, env: 'Environment'):
bmi_path = f'{env.get_build_dir()}/std{self.get_cpp20_module_bmi_extension()}'
return ['-fmodules']

def check_cpp_import_std_support(self):
if int(self.version.split('.')[0]) < 15:
raise MesonException('Your compiler does not support import std feature. GCC support starts at version >= 15')

def get_options(self) -> 'MutableKeyedOptionDictType':
opts = super().get_options()

Expand Down
3 changes: 3 additions & 0 deletions mesonbuild/compilers/mixins/clike.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@ def _sanity_check_impl(self, work_dir: str, environment: 'Environment',
mode = CompileCheckMode.COMPILE
cargs, largs = self._get_basic_compiler_args(environment, mode)
extra_flags = cargs + self.linker_to_compiler_args(largs)
if environment.coredata.optstore.get_value('cpp_import_std'):
# extra_flags.extend(self.get_import_std)
pass

# Is a valid executable output for all toolchains and platforms
binname += '.exe'
Expand Down
53 changes: 53 additions & 0 deletions mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,54 @@ def __init__(
self.compilers: PerMachine[T.Dict[str, 'compilers.Compiler']] = PerMachine({}, {})
self.parse_project()
self._redetect_machines()
self._cpp_import_std_bmi_dep = None

if self.coredata.optstore.get_value('cpp_import_std') and self.subproject == "":
self._cpp_import_std_bmi_dep = self._create_cpp_import_std_dep(self.environment)

def _create_cpp_import_std_dep(self, env: environment.Environment):
compiler_to_use: T.Optional[compilers.cpp.CPPCompiler] = None
for comp_lang, compiler in self.compilers.host.items():
if comp_lang == 'cpp':
compiler_to_use = T.cast(compilers.cpp.CPPCompiler, compiler)
if not compiler_to_use:
raise MesonException('cpp_import_std option is set to true but no cpp compiler could be found.'
' Enable cpp language in your project to use this feature.')
# Construct the compiler command-line
commands = compiler_to_use.compiler_args()
commands.extend(compiler_to_use.get_import_std_lib_source_args(self.environment))
all_lists_to_add = [compiler_to_use.get_always_args(), compiler_to_use.get_debug_args(env.coredata.optstore.get_value('buildtype') == 'debug'),
compiler_to_use.get_assert_args(disable=env.coredata.optstore.get_value('b_ndebug') in ['if-release', 'true'],
env=env)]
for args_list in all_lists_to_add:
for arg in args_list:
commands.append(arg)
commands.append("-o")
commands.append("@OUTPUT@")
commands.append("@INPUT@")
no_ccache = True
command_list = compiler_to_use.get_exelist(ccache=not no_ccache) + commands.to_native()
tgt = build.CustomTarget('',
'', '', self.environment, command_list,
sources=[compiler_to_use.get_import_std_lib_source_file()],
outputs=[f'std{compiler_to_use.get_cpp20_module_bmi_extension()}'])
self.add_target('_cpp_import_std_bmi', tgt)
bmi_dep = dependencies.InternalDependency(
version='0.0',
incdirs=[],
compile_args=compiler_to_use.get_import_std_compile_args(self.environment),
# compile_args=[],
link_args=[],
libraries=[],
whole_libraries=[],
sources=[tgt],
extra_files=[],
ext_deps=[],
variables=[],
d_module_versions=[],
d_import_dirs=[],
objects=[])
return bmi_dep

def __getnewargs_ex__(self) -> T.Tuple[T.Tuple[object], T.Dict[str, object]]:
raise MesonBugException('This class is unpicklable')
Expand Down Expand Up @@ -3411,6 +3459,7 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs
if targetclass not in {build.Executable, build.SharedLibrary, build.SharedModule, build.StaticLibrary, build.Jar}:
mlog.debug('Unknown target type:', str(targetclass))
raise RuntimeError('Unreachable code')

self.__process_language_args(kwargs)
if targetclass is build.StaticLibrary:
for lang in compilers.all_languages - {'java'}:
Expand Down Expand Up @@ -3492,6 +3541,10 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs

target = targetclass(name, self.subdir, self.subproject, for_machine, srcs, struct, objs,
self.environment, self.compilers[for_machine], kwargs)
if target.uses_cpp():
if self.coredata.optstore.get_value('cpp_import_std') and self.subproject == '':
target.add_deps([self._cpp_import_std_bmi_dep])

if objs and target.uses_rust():
FeatureNew.single_use('objects in Rust targets', '1.8.0', self.subproject)

Expand Down
6 changes: 6 additions & 0 deletions mesonbuild/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,12 @@ def choices_are_different(a: _U, b: _U) -> bool:
return False


class UseImportStd(UserBooleanOption):
def __init__(self, lang):
self.lang = lang.lower()
opt_name =f'{self.lang}_import_std'
super().__init__(opt_name, 'Whether to use import std; module in your targets', False)

class UserStdOption(UserComboOption):
'''
UserOption specific to c_std and cpp_std options. User can set a list of
Expand Down
38 changes: 34 additions & 4 deletions mesonbuild/scripts/depscan.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import pickle
import re
import typing as T
import subprocess as sp

if T.TYPE_CHECKING:
from typing_extensions import Literal, TypedDict, NotRequired
Expand Down Expand Up @@ -201,8 +202,37 @@ def scan(self) -> int:

return 0


class CppDependenciesScanner:
pass

class ClangDependencyScanner(CppDependenciesScanner):
def __init__(self, compilation_db_file, json_output_file, dd_output_file=None):
self.compilation_db_file = compilation_db_file
self.output_file = output_file
self.dd_output_file = dd_output_file

def scan(self):
try:
r = sp.run(["/usr/local/Cellar/llvm/20.1.1/bin/clang-scan-deps",
"-format=p1689",
"-compilation-database", self.compilation_db_file],
capture_output=True,
check=True)
print(r.stdout)
#json.loads(r.stdout)
return 0
except sp.SubprocessError:
return 1
except sp.TimeoutExpired:
return 2


def run(args: T.List[str]) -> int:
assert len(args) == 2, 'got wrong number of arguments!'
outfile, pickle_file = args
scanner = DependencyScanner(pickle_file, outfile)
return scanner.scan()
assert len(args) > 2, 'At least <compilation_db> and <json_output-file> arguments'
comp_db, json_output, dd_output = args
ClangDependencyScanner(compilation_db_file, output_file)
# assert len(args) == 2, 'got wrong number of arguments!'
# outfile, pickle_file = args
# scanner = DependencyScanner(pickle_file, outfile)
# return scanner.scan()
Loading
Loading