shuriken: Copy source file to build dir for "import std"

Problem with the previous approach: CLion wouldn't recognize the std module, unless it's included in the compdb (but then /usr/include/c++/.../bits is listed as part of the project -.-)
This commit is contained in:
Lexi / Zoe 2026-05-16 01:17:43 +02:00
parent ae1df9bc8b
commit 5a53457817
Signed by: binaryDiv
GPG key ID: F8D4956E224DA232

View file

@ -497,7 +497,7 @@ class ShurikenCli:
CompileTargetCpp = namedtuple('CompileTargetCpp', ['src', 'obj'])
CompileTargetCppm = namedtuple('CompileTargetCppm', ['src', 'obj', 'pcm'])
CompileTargetStdModule = namedtuple('CompileTargetStdModule', ['src', 'pcm'])
CompileTargetStdModule = namedtuple('CompileTargetStdModule', ['original_src', 'generated_src', 'pcm'])
class NinjaFileGenerator:
@ -552,7 +552,8 @@ class NinjaFileGenerator:
std_module_sources = std_module_source_finder.find_module_sources(['std'])
for module_name, module_src_path in std_module_sources.items():
self.compile_targets_std_module.append(CompileTargetStdModule(
src=str(module_src_path),
original_src=str(module_src_path),
generated_src=f"$builddir/generated/stdlib/{module_name}.cppm",
pcm=f"$builddir/pcm/{module_name}.pcm",
))
@ -589,6 +590,10 @@ linker_args = {target_config.merged_linker_args}
# -- RULES
# Rule to copy an arbitrary file (or multiple) from in to out
rule copy
command = cp $in $out
# Rule to compile a .cpp file to a .o file
rule cpp
command = {target_config.cpp_compiler} $cpp_flags -c $in -o $out
@ -607,7 +612,7 @@ rule link
# Rule to generate a compilation database (JSON file) from the build targets defined in a Ninja file
rule generate_compdb
command = ninja -f $in -t compdb cpp cppm > $out
command = ninja -f $in -t compdb cpp cppm std_module > $out
# Rule to generate a Ninja dyndep file for C++ module dependencies based on a compilation database
rule generate_dyndeps
@ -617,7 +622,7 @@ rule generate_dyndeps
# -- STATIC BUILD TARGETS
# Generate compilation database from default target Ninja file
build $compdb_file: generate_compdb {self.config.get_ninja_file_path(config.default_target)}
build $compdb_file: generate_compdb {self.config.get_ninja_file_path(config.default_target)} | generate_source_files
# Shortcut alias to generate compilation database
build compdb: phony $compdb_file
@ -632,11 +637,18 @@ build $dyndep_file: generate_dyndeps $compdb_file
build $builddir/{target_config.output_file}: link {' '.join(self.link_target_obj_files)}
default $builddir/{target_config.output_file}
# Phony target that depends on all generated source files
build generate_source_files: phony {self.list_generated_source_files()}
{self.render_compile_targets()}
# End of build.{target}.ninja
""".strip()
def list_generated_source_files(self) -> str:
generated_files = [target.generated_src for target in self.compile_targets_std_module]
return ' '.join(generated_files)
def render_compile_targets(self) -> str:
rendered_targets = list(map(self.render_compile_target_std_module, self.compile_targets_std_module))
rendered_targets += list(map(self.render_compile_target_cpp, self.compile_targets_cpp))
@ -661,7 +673,9 @@ build {target.obj} | {target.pcm}: cppm {target.src} || $dyndep_file
@staticmethod
def render_compile_target_std_module(target: CompileTargetStdModule) -> str:
return f"""
build {target.pcm}: std_module {target.src} || $dyndep_file
build {target.generated_src}: copy {target.original_src}
build {target.pcm}: std_module {target.generated_src} || $dyndep_file
dyndep = $dyndep_file
""".strip()
@ -683,10 +697,6 @@ class BuildDependencyParser:
# Map of module names to PCM file paths
module_map: dict[str, Path] = {}
# Add standard library modules to module map (if "import std" support is enabled)
if self.config.enable_import_std:
module_map['std'] = Path(target_build_dir, 'pcm', 'std.pcm')
# Validate module name and path convention, construct map of module names to PCM file paths
for rule in parsed_p1689['rules']:
provides = rule.get('provides', [])
@ -697,20 +707,16 @@ class BuildDependencyParser:
module_name: str = provides[0]['logical-name']
assert module_name not in module_map, f'Module "{module_name}" is provided more than once: {rule}'
expected_source_path = str(Path(source_dir, module_name.replace('.', '/') + '.cppm'))
assert provides[0]['source-path'] == expected_source_path, \
f"Module name does not match source path: {rule}"
if module_name != 'std':
expected_source_path = str(Path(source_dir, module_name.replace('.', '/') + '.cppm'))
assert provides[0]['source-path'] == expected_source_path, \
f"Module name does not match source path: {rule}"
module_map[module_name] = Path(target_build_dir, 'pcm', module_name + '.pcm')
# Collect all module dependencies to generate dyndeps
build_dependencies: dict[Path, list[Path]] = {}
# If "import std" support is enabled, we need to add std to the build dependencies, even though it doesn't have
# any dependencies itself. This is needed, otherwise the module won't be built automatically.
if self.config.enable_import_std:
build_dependencies[target_build_dir / 'pcm/std.pcm'] = []
# Parse rules and get module dependencies
for rule in parsed_p1689['rules']:
# Compilation database contains "build/{default_target}" paths, we need to map them to "build/{target}"