diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/ArtifactCategory.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/ArtifactCategory.java index ca2a2b1552eca5..b129c111acaac2 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/ArtifactCategory.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/ArtifactCategory.java @@ -32,6 +32,10 @@ public enum ArtifactCategory { OBJECT_FILE("", ".o", ".obj"), PIC_OBJECT_FILE("", ".pic.o"), CPP_MODULE("", ".pcm"), + CPP20_MODULES_INFO("", ".CXXModules.json"), + CPP20_MODULE_DEP("", ".ddi"), + CPP20_MODULE_MAP("", ".modmap"), + CPP20_MODULE_MAP_INPUT("", ".modmap.input"), GENERATED_ASSEMBLY("", ".s", ".asm"), PROCESSED_HEADER("", ".processed"), GENERATED_HEADER("", ".h"), diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/BUILD b/src/main/java/com/google/devtools/build/lib/rules/cpp/BUILD index 209f89eec20054..ffdfcc96dc6bd2 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/BUILD +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/BUILD @@ -128,6 +128,7 @@ java_library( "//third_party:caffeine", "//third_party:checker_framework_annotations", "//third_party:flogger", + "//third_party:gson", "//third_party:guava", "//third_party:jsr305", "//third_party/protobuf:protobuf_java", diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java index a30c8b3a839b94..c545a59bb1c950 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java @@ -76,6 +76,9 @@ public final class CcCommon implements StarlarkValue { CppActionNames.CPP_HEADER_PARSING, CppActionNames.CPP_MODULE_COMPILE, CppActionNames.CPP_MODULE_CODEGEN, + CppActionNames.CPP20_DEPS_SCANNING, + CppActionNames.CPP20_MODULE_COMPILE, + CppActionNames.CPP20_MODULE_CODEGEN, CppActionNames.ASSEMBLE, CppActionNames.PREPROCESS_ASSEMBLE, CppActionNames.CLIF_MATCH, diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationContext.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationContext.java index f071d1a47508d5..1ce4c46bae1624 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationContext.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationContext.java @@ -79,6 +79,11 @@ public final class CcCompilationContext implements CcCompilationContextApi transitiveModules; private final NestedSet transitivePicModules; + private final NestedSet pcmFiles; + private final NestedSet picPcmFiles; + private final NestedSet modulesInfoFiles; + private final NestedSet picModulesInfoFiles; + private final CppModuleMap cppModuleMap; private final boolean propagateModuleMapAsActionInput; @@ -111,6 +116,10 @@ private CcCompilationContext( HeaderInfo headerInfo, NestedSet transitiveModules, NestedSet transitivePicModules, + NestedSet pcmFiles, + NestedSet picPcmFiles, + NestedSet modulesInfoFiles, + NestedSet picModulesInfoFiles, ImmutableList directModuleMaps, ImmutableList exportingModuleMaps, CppModuleMap cppModuleMap, @@ -125,6 +134,10 @@ private CcCompilationContext( this.headerInfo = headerInfo; this.transitiveModules = transitiveModules; this.transitivePicModules = transitivePicModules; + this.pcmFiles = pcmFiles; + this.picPcmFiles = picPcmFiles; + this.modulesInfoFiles = modulesInfoFiles; + this.picModulesInfoFiles = picModulesInfoFiles; this.cppModuleMap = cppModuleMap; this.nonCodeInputs = nonCodeInputs; this.compilationPrerequisites = compilationPrerequisites; @@ -550,6 +563,14 @@ public NestedSet getTransitiveModules(boolean usePic) { return usePic ? transitivePicModules : transitiveModules; } + public NestedSet getPcmFiles(boolean usePic) { + return usePic ? picPcmFiles: pcmFiles; + } + + public NestedSet getModulesInfoFiles(boolean usePic) { + return usePic ? picModulesInfoFiles : modulesInfoFiles; + } + @Override public Depset getStarlarkTransitiveModules(boolean usePic, StarlarkThread thread) throws EvalException { @@ -627,6 +648,44 @@ ImmutableList getNonTransitiveDefines() { return commandLineCcCompilationContext.localDefines; } + public static CcCompilationContext createWithCpp20Modules( + CcCompilationContext ccCompilationContext, + NestedSet pcmFiles, + NestedSet picPcmFiles, + ImmutableList modulesInfoFiles, + ImmutableList picModulesInfoFiles) { + var pcmFilesBuilder = NestedSetBuilder + .fromNestedSet(ccCompilationContext.pcmFiles) + .addTransitive(pcmFiles); + var picPcmFilesBuilder = NestedSetBuilder + .fromNestedSet(ccCompilationContext.picPcmFiles) + .addTransitive(picPcmFiles); + var modulesInfoFilesBuilder = NestedSetBuilder + .fromNestedSet(ccCompilationContext.modulesInfoFiles) + .addAll(modulesInfoFiles); + var picModulesInfoFilesBuilder = NestedSetBuilder + .fromNestedSet(ccCompilationContext.picModulesInfoFiles) + .addAll(picModulesInfoFiles); + return new CcCompilationContext( + ccCompilationContext.commandLineCcCompilationContext, + ccCompilationContext.compilationPrerequisites, + ccCompilationContext.declaredIncludeSrcs, + ccCompilationContext.nonCodeInputs, + ccCompilationContext.headerInfo, + ccCompilationContext.transitiveModules, + ccCompilationContext.transitivePicModules, + pcmFilesBuilder.build(), + picPcmFilesBuilder.build(), + modulesInfoFilesBuilder.build(), + picModulesInfoFilesBuilder.build(), + ccCompilationContext.directModuleMaps, + ccCompilationContext.exportingModuleMaps, + ccCompilationContext.cppModuleMap, + ccCompilationContext.propagateModuleMapAsActionInput, + ccCompilationContext.virtualToOriginalHeaders, + ccCompilationContext.headerTokens); + } + /** * Returns a {@code CcCompilationContext} that is based on a given {@code CcCompilationContext}, * with {@code extraHeaderTokens} added to the header tokens. @@ -644,6 +703,10 @@ public static CcCompilationContext createWithExtraHeaderTokens( ccCompilationContext.headerInfo, ccCompilationContext.transitiveModules, ccCompilationContext.transitivePicModules, + ccCompilationContext.pcmFiles, + ccCompilationContext.picPcmFiles, + ccCompilationContext.modulesInfoFiles, + ccCompilationContext.picModulesInfoFiles, ccCompilationContext.directModuleMaps, ccCompilationContext.exportingModuleMaps, ccCompilationContext.cppModuleMap, @@ -765,6 +828,10 @@ private void mergeDependentCcCompilationContext( TransitiveSetHelper allDefines, NestedSetBuilder transitiveModules, NestedSetBuilder transitivePicModules, + NestedSetBuilder pcmFiles, + NestedSetBuilder picPcmFiles, + NestedSetBuilder modulesInfoFiles, + NestedSetBuilder picModulesInfoFiles, Set directModuleMaps) { Preconditions.checkNotNull(otherCcCompilationContext); compilationPrerequisites.addTransitive( @@ -785,6 +852,11 @@ private void mergeDependentCcCompilationContext( addIfNotNull(transitivePicModules, otherCcCompilationContext.headerInfo.picHeaderModule); addIfNotNull(transitivePicModules, otherCcCompilationContext.headerInfo.separatePicModule); + pcmFiles.addTransitive(otherCcCompilationContext.pcmFiles); + picPcmFiles.addTransitive(otherCcCompilationContext.picPcmFiles); + modulesInfoFiles.addTransitive(otherCcCompilationContext.modulesInfoFiles); + picModulesInfoFiles.addTransitive(otherCcCompilationContext.picModulesInfoFiles); + nonCodeInputs.addTransitive(otherCcCompilationContext.nonCodeInputs); // All module maps of direct dependencies are inputs to the current compile independently of @@ -842,6 +914,10 @@ private void mergeDependentCcCompilationContexts( TransitiveSetHelper allDefines, NestedSetBuilder transitiveModules, NestedSetBuilder transitivePicModules, + NestedSetBuilder pcmFiles, + NestedSetBuilder picPcmFiles, + NestedSetBuilder modulesInfoFiles, + NestedSetBuilder picModulesInfoFiles, Set directModuleMaps, Set exportingModuleMaps) { for (CcCompilationContext ccCompilationContext : @@ -851,6 +927,10 @@ private void mergeDependentCcCompilationContexts( allDefines, transitiveModules, transitivePicModules, + pcmFiles, + picPcmFiles, + modulesInfoFiles, + picModulesInfoFiles, directModuleMaps); } @@ -1079,6 +1159,10 @@ public CcCompilationContext build() { TransitiveSetHelper allDefines = new TransitiveSetHelper<>(); NestedSetBuilder transitiveModules = NestedSetBuilder.stableOrder(); NestedSetBuilder transitivePicModules = NestedSetBuilder.stableOrder(); + NestedSetBuilder pcmFiles = NestedSetBuilder.stableOrder(); + NestedSetBuilder picPcmFiles = NestedSetBuilder.stableOrder(); + NestedSetBuilder modulesInfoFiles = NestedSetBuilder.stableOrder(); + NestedSetBuilder picModulesInfoFiles = NestedSetBuilder.stableOrder(); Set directModuleMaps = new LinkedHashSet<>(); Set exportingModuleMaps = new LinkedHashSet<>(); mergeDependentCcCompilationContexts( @@ -1087,6 +1171,10 @@ public CcCompilationContext build() { allDefines, transitiveModules, transitivePicModules, + pcmFiles, + picPcmFiles, + modulesInfoFiles, + picModulesInfoFiles, directModuleMaps, exportingModuleMaps); @@ -1110,6 +1198,10 @@ public CcCompilationContext build() { headerInfo, transitiveModules.build(), transitivePicModules.build(), + pcmFiles.build(), + picPcmFiles.build(), + modulesInfoFiles.build(), + picModulesInfoFiles.build(), ImmutableList.copyOf(directModuleMaps), ImmutableList.copyOf(exportingModuleMaps), cppModuleMap, diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java index 54202e5e0f2b9e..7c0c6baf652012 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java @@ -34,6 +34,7 @@ import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; +import com.google.devtools.build.lib.collect.nestedset.Order; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; import com.google.devtools.build.lib.rules.cpp.CcCommon.CoptsFilter; @@ -58,6 +59,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.Nullable; import net.starlark.java.eval.EvalException; import net.starlark.java.eval.Starlark; @@ -269,6 +271,7 @@ public CcCompilationContext getCcCompilationContext() { private final List additionalExportedHeaders = new ArrayList<>(); private final List additionalCppModuleMaps = new ArrayList<>(); private final LinkedHashMap compilationUnitSources = new LinkedHashMap<>(); + private final LinkedHashMap moduleInterfaceSources = new LinkedHashMap<>(); private ImmutableList copts = ImmutableList.of(); private CoptsFilter coptsFilter = CoptsFilter.alwaysPasses(); private final Set defines = new LinkedHashSet<>(); @@ -518,6 +521,27 @@ public CcCompilationHelper addSources(Artifact... sources) { return addSources(Arrays.asList(sources)); } + @CanIgnoreReturnValue + public CcCompilationHelper addModuleInterfaceSources(Collection sources) { + for (Artifact source : sources) { + addModuleInterfaceSource(source, label); + } + return this; + } + + @CanIgnoreReturnValue + public CcCompilationHelper addModuleInterfaceSources(Iterable> sources) { + for (Pair source : sources) { + addModuleInterfaceSource(source.first, source.second); + } + return this; + } + + private void addModuleInterfaceSource(Artifact source, Label label) { + Preconditions.checkNotNull(featureConfiguration); + moduleInterfaceSources.put(source, CppSource.create(source, label, CppSource.Type.SOURCE)); + } + /** Add the corresponding files as non-header, non-source input files. */ @CanIgnoreReturnValue public CcCompilationHelper addAdditionalInputs(Collection inputs) { @@ -841,8 +865,21 @@ public CompilationInfo compile(RuleContext ruleContext) // Create compile actions (both PIC and no-PIC). try { - CcCompilationOutputs ccOutputs = createCcCompileActions(); - + CcCompilationOutputs ccOutputs; + if (featureConfiguration.isEnabled(CppRuleClasses.CPP20_MODULE)) { + // Handle C++20 Module compile + ccOutputs = createCcCompileActionsWithCpp20Module(); + publicCompilationContext = + CcCompilationContext.createWithCpp20Modules( + publicCompilationContext, + ccOutputs.getPcmFiles(false), + ccOutputs.getPcmFiles(true), + ccOutputs.getModulesInfoFiles(false), + ccOutputs.getModulesInfoFiles(true)); + } else { + // Create compile actions (both PIC and no-PIC). + ccOutputs = createCcCompileActions(); + } if (cppConfiguration.processHeadersInDependencies()) { return new CompilationInfo( CcCompilationContext.createWithExtraHeaderTokens( @@ -968,6 +1005,429 @@ private ImmutableSet getSourceArtifactsByType( return result.build(); } + private CcCompilationOutputs createCcCompileActionsWithCpp20Module() + throws RuleErrorException, EvalException, InterruptedException { + Preconditions.checkState( + featureConfiguration.isEnabled(CppRuleClasses.CPP20_MODULE), + "to use C++20 Modules, the feature cpp20_module must be enabled"); + Preconditions.checkNotNull(ccCompilationContext); + CcCompilationOutputs.Builder result = CcCompilationOutputs.builder(); + // merge module interfaces and ordinary sources + Map sourcesMap = new LinkedHashMap<>(); + sourcesMap.putAll(compilationUnitSources); + sourcesMap.putAll(moduleInterfaceSources); + ImmutableMap outputNameMap = + calculateOutputNameMapByType(sourcesMap, /* prefixDir= */ null); + if (generateNoPicAction) { + createCcCompileActionsWithCpp20ModuleHelper(result, /* usePic= */ false, outputNameMap); + } + if (generatePicAction) { + createCcCompileActionsWithCpp20ModuleHelper(result, /* usePic= */ true, outputNameMap); + } + return result.build(); + } + + private void createCcCompileActionsWithCpp20ModuleHelper( + CcCompilationOutputs.Builder result, + boolean usePic, + ImmutableMap outputNameMap) + throws RuleErrorException, EvalException, InterruptedException { + NestedSetBuilder pcmSetBuilder = NestedSetBuilder.stableOrder(); + NestedSetBuilder ddiSetBuilder = NestedSetBuilder.stableOrder(); + ImmutableList.Builder> pcmAndDdiPairListBuilder = + ImmutableList.builder(); + + // declare .CXXModules.json forward + // all modules information is put here + String modulesInfoFileName = label.getName(); + if (usePic) { + modulesInfoFileName = + CppHelper.getArtifactNameForCategory( + ccToolchain, ArtifactCategory.PIC_FILE, modulesInfoFileName); + } + Artifact modulesInfoFile = + CppHelper.getCompileOutputArtifact( + actionConstructionContext, + label, + CppHelper.getArtifactNameForCategory( + ccToolchain, ArtifactCategory.CPP20_MODULES_INFO, modulesInfoFileName), + configuration); + if (usePic) { + result.addPicModulesInfoFile(modulesInfoFile); + } else { + result.addModulesInfoFile(modulesInfoFile); + } + // the builder list contains + // 1. compile c++ source (e.g. .cc -> .o) compilationUnitSources.size() + // 2. compile c++ module (e.g. .cppm -> .pcm) moduleInterfaceSources.size() + // 3. compile c++ module (e.g. .cpm -> .o) moduleInterfaceSources.size() + List builderList = + new ArrayList<>(compilationUnitSources.size() + 2 * moduleInterfaceSources.size()); + + createCppCompileActionBuilder( + result, + usePic, + outputNameMap, + pcmSetBuilder, + ddiSetBuilder, + pcmAndDdiPairListBuilder, + modulesInfoFile, + builderList, + compilationUnitSources, + /* isModuleInterface= */ false); + createCppCompileActionBuilder( + result, + usePic, + outputNameMap, + pcmSetBuilder, + ddiSetBuilder, + pcmAndDdiPairListBuilder, + modulesInfoFile, + builderList, + moduleInterfaceSources, + /* isModuleInterface= */ true); + // create .CXXModules.json + var modulesInfoAction = + new Cpp20ModulesInfoAction( + actionConstructionContext.getActionOwner(), + ddiSetBuilder.build(), + pcmAndDdiPairListBuilder.build(), + ccCompilationContext.getModulesInfoFiles(usePic), + modulesInfoFile); + actionConstructionContext.registerAction(modulesInfoAction); + var depPcmFiles = ccCompilationContext.getPcmFiles(usePic); + if (depPcmFiles != null) { + pcmSetBuilder.addTransitive(depPcmFiles); + } + var pcmFiles = pcmSetBuilder.build(); + for (CppCompileActionBuilder builder : builderList) { + builder.setPcmFiles(pcmFiles); + CppCompileAction cppCompileAction = builder.buildOrThrowRuleError(ruleErrorConsumer); + actionConstructionContext.registerAction(cppCompileAction); + } + } + + private void createCppCompileActionBuilder( + CcCompilationOutputs.Builder result, + boolean usePic, + ImmutableMap outputNameMap, + NestedSetBuilder pcmSetBuilder, + NestedSetBuilder ddiSetBuilder, + ImmutableList.Builder> pcmAndDdiPairListBuilder, + Artifact modulesInfoFile, + List builderList, + Map sourceMap, + boolean isModuleInterface) + throws RuleErrorException, EvalException, InterruptedException { + for (CppSource source : sourceMap.values()) { + CppCompileActionBuilder builder; + Label sourceLabel = source.getLabel(); + Artifact sourceArtifact = source.getSource(); + PathFragment sourcePath = sourceArtifact.getExecPath(); + String outputName = outputNameMap.get(sourceArtifact); + if (usePic) { + outputName = + CppHelper.getArtifactNameForCategory( + ccToolchain, ArtifactCategory.PIC_FILE, outputName); + } + builder = initializeCompileAction(sourceArtifact); + // declare .d file forward + // reuse .d file produced by scan dependencies (c++20-deps-scanning) + Artifact dotdFile; + if (builder.dotdFilesEnabled() + && builder.useDotdFile(sourceArtifact)) { + String dotdFileName = CppHelper.getArtifactNameForCategory( + ccToolchain, ArtifactCategory.INCLUDED_FILE_LIST, outputName); + dotdFile = + CppHelper.getCompileOutputArtifact( + actionConstructionContext, label, dotdFileName, configuration); + } else { + dotdFile = null; + } + // scan dependencies + // both module source files and ordinary c++ source files are needed + // others skip (e.g. C or assembler) + if (isModuleInterface || CppFileTypes.CPP_SOURCE.matches(sourcePath)) { + // dependencies information are put in .ddi file + // the format is https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1689r5.html + Artifact ddiFile = + CppHelper.getCompileOutputArtifact( + actionConstructionContext, + label, + CppHelper.getArtifactNameForCategory( + ccToolchain, ArtifactCategory.CPP20_MODULE_DEP, outputName), + configuration); + ddiSetBuilder.add(ddiFile); + // all -fmodule-file== flags are put in .modmap file + var modmapFile = + CppHelper.getCompileOutputArtifact( + actionConstructionContext, + label, + CppHelper.getArtifactNameForCategory( + ccToolchain, ArtifactCategory.CPP20_MODULE_MAP, outputName), + configuration); + // all path/to/bmi are put in .modmap.input file, + // which is convenient to get all bmi in CppCompileAction + var modmapInputFile = + CppHelper.getCompileOutputArtifact( + actionConstructionContext, + label, + CppHelper.getArtifactNameForCategory( + ccToolchain, ArtifactCategory.CPP20_MODULE_MAP_INPUT, outputName), + configuration); + Cpp20ModuleDepMapAction cpp20ModuleDepMapAction = + new Cpp20ModuleDepMapAction( + actionConstructionContext.getActionOwner(), + ddiFile, + modulesInfoFile, + modmapFile, + modmapInputFile); + actionConstructionContext.registerAction(cpp20ModuleDepMapAction); + + if (isModuleInterface) { + // use two-phase compilation + // e.g. + // 1. .cppm -> .pcm + // 2. .pcm -> .o + // echo phase need one builder + // so the builder will change + // and the new builder return + builder = + createCpp20ModuleCompileActionBuilder( + builder, + sourceLabel, + outputName, + result, + sourceArtifact, + usePic, + pcmSetBuilder, + pcmAndDdiPairListBuilder, + builderList, + ddiFile, + dotdFile, + modmapFile, + modmapInputFile); + } else { + createCompileActionBuilder( + builder, + sourceLabel, + outputName, + result, + sourceArtifact, + usePic, + ddiFile, + dotdFile, + modmapFile, + /* isCpp20Module= */ false); + } + builder.setModmapFile(modmapFile); + builder.setModmapInputFile(modmapInputFile); + } else { + createCompileActionBuilder( + builder, + sourceLabel, + outputName, + result, + sourceArtifact, + usePic, + /* ddiFile= */ null, + dotdFile, + /* modmapFile= */ null, + /* isCpp20Module= */ false); + } + semantics.finalizeCompileActionBuilder( + configuration, featureConfiguration, builder); + builderList.add(builder); + } + } + + private CppCompileActionBuilder createCpp20ModuleCompileActionBuilder( + CppCompileActionBuilder moduleCompileBuilder, + Label sourceLabel, + String outputName, + CcCompilationOutputs.Builder result, + Artifact sourceArtifact, + boolean usePic, + NestedSetBuilder pcmSetBuilder, + ImmutableList.Builder> pcmAndDdiPairListBuilder, + List builderList, + Artifact ddiFile, + Artifact dotdFile, + Artifact modmapFile, + Artifact modmapInputFile) + throws RuleErrorException, EvalException, InterruptedException { + Artifact diagnosticsFile; + var outputCategory = ArtifactCategory.CPP_MODULE; + if (moduleCompileBuilder.serializedDiagnosticsFilesEnabled()) { + String diagnosticsFileName = + CppHelper.getDiagnosticsFileName(ccToolchain, outputCategory, outputName); + diagnosticsFile = + CppHelper.getCompileOutputArtifact( + actionConstructionContext, label, diagnosticsFileName, configuration); + } else { + diagnosticsFile = null; + } + String pcmFileName = + CppHelper.getArtifactNameForCategory(ccToolchain, outputCategory, outputName); + PathFragment objectDir = + CppHelper.getObjDirectory(label, configuration.isSiblingRepositoryLayout()); + // the DerivedArtifact type is required when using restart mechanism + Artifact.DerivedArtifact pcmFile = + actionConstructionContext.getDerivedArtifact( + objectDir.getRelative(pcmFileName), + configuration.getBinDirectory(label.getRepository())); + pcmSetBuilder.add(pcmFile); + pcmAndDdiPairListBuilder.add(Pair.of(pcmFile, ddiFile)); + if (usePic) { + result.addPicPcmFile(pcmFile); + } else { + result.addPcmFile(pcmFile); + } + moduleCompileBuilder.setOutputs(pcmFile, dotdFile, diagnosticsFile); + moduleCompileBuilder.setModmapFile(modmapFile); + moduleCompileBuilder.setModmapInputFile(modmapInputFile); + moduleCompileBuilder.setActionName(CppActionNames.CPP20_MODULE_COMPILE); + PathFragment ccRelativeName = sourceArtifact.getRootRelativePath(); + var variables = + setupCompileBuildVariables( + moduleCompileBuilder, + sourceLabel, + usePic, + /* needsFdoBuildVariables= */ ccRelativeName != null, + cppModuleMap, + /* enableCoverage= */ false, + null, + false, + null, + null, + /* additionalBuildVariables= */ ImmutableMap.of( + CompileBuildVariables.CPP20_MODMAP_FILE.getVariableName(), + modmapFile.getExecPathString())); + moduleCompileBuilder.setVariables(variables); + semantics.finalizeCompileActionBuilder( + configuration, featureConfiguration, moduleCompileBuilder); + builderList.add(moduleCompileBuilder); + createScanDepsAction(variables, sourceArtifact, ddiFile, dotdFile); + // after compile module interface file to pcm file + // we compile pcm file to object file + var moduleCodegenBuilder = initializeCompileAction(pcmFile); + moduleCodegenBuilder.addMandatoryInputs(List.of(sourceArtifact)); + createCompileActionBuilder( + moduleCodegenBuilder, + sourceLabel, + outputName, + result, + pcmFile, + usePic, + /* ddiFile= */ null, + dotdFile, + modmapFile, + /* isCpp20Module= */ true); + return moduleCodegenBuilder; + } + + private void createCompileActionBuilder( + CppCompileActionBuilder builder, + Label sourceLabel, + String outputName, + CcCompilationOutputs.Builder result, + Artifact sourceArtifact, + boolean usePic, + Artifact ddiFile, + Artifact dotdFile, + Artifact modmapFile, + boolean isCpp20Module) + throws RuleErrorException, EvalException, InterruptedException { + if (isCpp20Module) { + builder.setActionName(CppActionNames.CPP20_MODULE_CODEGEN); + } else { + // do nothing + // fallback to default action name + } + boolean enableCoverage = isCodeCoverageEnabled; + boolean generateDwo = + ccToolchain.shouldCreatePerObjectDebugInfo(featureConfiguration, cppConfiguration); + boolean bitcodeOutput = + featureConfiguration.isEnabled(CppRuleClasses.THIN_LTO) + && CppFileTypes.LTO_SOURCE.matches(sourceArtifact.getFilename()); + PathFragment ccRelativeName = sourceArtifact.getRootRelativePath(); + + Artifact outputFile = CppHelper.getCompileOutputArtifact( + actionConstructionContext, + label, + CppHelper.getArtifactNameForCategory(ccToolchain, ArtifactCategory.OBJECT_FILE, outputName), + configuration + ); + Artifact diagnosticsFile; + if (builder.serializedDiagnosticsFilesEnabled()) { + String diagnosticsFileName = + CppHelper.getDiagnosticsFileName(ccToolchain, ArtifactCategory.OBJECT_FILE, outputName); + diagnosticsFile = + CppHelper.getCompileOutputArtifact( + actionConstructionContext, label, diagnosticsFileName, configuration); + } + else { + diagnosticsFile = null; + } + builder.setOutputs(outputFile, dotdFile, diagnosticsFile); + String gcnoFileName = + CppHelper.getArtifactNameForCategory( + ccToolchain, ArtifactCategory.COVERAGE_DATA_FILE, outputName); + Artifact gcnoFile = + enableCoverage + ? CppHelper.getCompileOutputArtifact( + actionConstructionContext, label, gcnoFileName, configuration) + : null; + Artifact dwoFile = generateDwo && !bitcodeOutput ? getDwoFile(outputFile) : null; + Artifact ltoIndexingFile = bitcodeOutput ? getLtoIndexingFile(outputFile) : null; + // compile arguments are produced by the same CompileBuildVariables + var variables = + setupCompileBuildVariables( + builder, + sourceLabel, + /* usePic= */ usePic, + /* needsFdoBuildVariables= */ ccRelativeName != null, + cppModuleMap, + /* enableCoverage= */ false, + gcnoFile, + generateDwo, + dwoFile, + ltoIndexingFile, + /* additionalBuildVariables= */ modmapFile == null + ? ImmutableMap.of() + : ImmutableMap.of( + CompileBuildVariables.CPP20_MODMAP_FILE.getVariableName(), + modmapFile.getExecPathString())); + + if (ddiFile != null) { + createScanDepsAction(variables, sourceArtifact, ddiFile, dotdFile); + } + builder.setVariables(variables); + builder.setGcnoFile(gcnoFile); + builder.setDwoFile(dwoFile); + builder.setLtoIndexingFile(ltoIndexingFile); + + result.addTemps( + createTempsActions( + sourceArtifact, sourceLabel, outputName, builder, usePic, ccRelativeName)); + if (usePic) { + result.addPicObjectFile(outputFile); + if (dwoFile != null) { + result.addPicDwoFile(dwoFile); + } + if (gcnoFile != null) { + result.addPicGcnoFile(gcnoFile); + } + } else { + result.addObjectFile(outputFile); + if (dwoFile != null) { + result.addDwoFile(dwoFile); + } + if (gcnoFile != null) { + result.addGcnoFile(gcnoFile); + } + } + } /** * Constructs the C++ compiler actions. It generally creates one action for every specified source * file. It takes into account coverage, and PIC, in addition to using the settings specified on @@ -975,6 +1435,9 @@ private ImmutableSet getSourceArtifactsByType( */ private CcCompilationOutputs createCcCompileActions() throws RuleErrorException, EvalException, InterruptedException { + Preconditions.checkState( + moduleInterfaceSources.isEmpty(), + "to use C++20 Modules, the feature cpp20_module must be enabled"); CcCompilationOutputs.Builder result = CcCompilationOutputs.builder(); Preconditions.checkNotNull(ccCompilationContext); @@ -1494,6 +1957,22 @@ private void createHeaderAction( result.addHeaderTokenFile(tokenFile); } + private void createScanDepsAction( + CcToolchainVariables variables, Artifact sourceArtifact, Artifact ddiFile, Artifact dotdFile) + throws RuleErrorException, EvalException { + var scanDepsBuilder = initializeCompileAction(sourceArtifact); + scanDepsBuilder.setActionName(CppActionNames.CPP20_DEPS_SCANNING); + scanDepsBuilder.setOutputs(ddiFile, dotdFile, null); + // only c++20-deps-scanning add .d file + var buildVariables = CcToolchainVariables.builder(variables) + .addStringVariable(CompileBuildVariables.DEPENDENCY_FILE.getVariableName(), dotdFile.getExecPathString()); + scanDepsBuilder.setVariables(buildVariables.build()); + semantics.finalizeCompileActionBuilder( + configuration, featureConfiguration, scanDepsBuilder); + var scanDepsAction = scanDepsBuilder.buildOrThrowRuleError(ruleErrorConsumer); + actionConstructionContext.registerAction(scanDepsAction); + } + private ImmutableList createModuleAction( CcCompilationOutputs.Builder result, CppModuleMap cppModuleMap) throws RuleErrorException, EvalException, InterruptedException { @@ -1843,4 +2322,8 @@ private ImmutableList createTempsActions( return ImmutableList.of(dAction.getPrimaryOutput(), sdAction.getPrimaryOutput()); } + private NestedSet getModuleInterfaceSourceFiles() { + var moduleInterfaceFiles = moduleInterfaceSources.values().stream().map(CppSource::getSource).collect(Collectors.toList()); + return NestedSetBuilder.wrap(Order.STABLE_ORDER, moduleInterfaceFiles); + } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java index b65048cb29cd62..34702095b3dcb8 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java @@ -44,6 +44,18 @@ public class CcCompilationOutputs implements CcCompilationOutputsApi { */ private final ImmutableList picObjectFiles; + /** + * All .pcm files built by the target. + */ + private final NestedSet pcmFiles; + + /** + * All .pic.pcm files built by the target. + */ + private final NestedSet picPcmFiles; + + private final ImmutableList modulesInfoFiles; + private final ImmutableList picModulesInfoFiles; /** * Maps all .o bitcode files coming from a ThinLTO C(++) compilation under our control to * information needed by the LTO indexing and backend steps. @@ -82,6 +94,10 @@ public class CcCompilationOutputs implements CcCompilationOutputsApi { private CcCompilationOutputs( ImmutableList objectFiles, ImmutableList picObjectFiles, + NestedSet pcmFiles, + NestedSet picPcmFiles, + ImmutableList modulesInfoFiles, + ImmutableList picModulesInfoFiles, LtoCompilationContext ltoCompilationContext, ImmutableList dwoFiles, ImmutableList picDwoFiles, @@ -92,6 +108,10 @@ private CcCompilationOutputs( ImmutableList moduleFiles) { this.objectFiles = objectFiles; this.picObjectFiles = picObjectFiles; + this.pcmFiles = pcmFiles; + this.picPcmFiles = picPcmFiles; + this.modulesInfoFiles = modulesInfoFiles; + this.picModulesInfoFiles = picModulesInfoFiles; this.ltoCompilationContext = ltoCompilationContext; this.dwoFiles = dwoFiles; this.picDwoFiles = picDwoFiles; @@ -111,6 +131,19 @@ public ImmutableList getObjectFiles(boolean usePic) { return usePic ? picObjectFiles : objectFiles; } + /** + * Returns an unmodifiable view of the .pcm or .pic.pcm files set. + * + * @param usePic whether to return .pic.pcm files + */ + public NestedSet getPcmFiles(boolean usePic) { + return usePic ? picPcmFiles : pcmFiles; + } + + public ImmutableList getModulesInfoFiles(boolean usePic) { + return usePic ? picModulesInfoFiles : modulesInfoFiles; + } + @Override public Sequence getStarlarkObjects() throws EvalException { return StarlarkList.immutableCopyOf(getObjectFiles(/* usePic= */ false)); @@ -244,6 +277,10 @@ public static Builder builder() { public static final class Builder { private final Set objectFiles = new LinkedHashSet<>(); private final Set picObjectFiles = new LinkedHashSet<>(); + private final NestedSetBuilder pcmFiles = NestedSetBuilder.stableOrder(); + private final NestedSetBuilder picPcmFiles = NestedSetBuilder.stableOrder(); + private final Set modulesInfoFiles = new LinkedHashSet<>(); + private final Set picModulesInfoFiles = new LinkedHashSet<>(); private final LtoCompilationContext.Builder ltoCompilationContext = new LtoCompilationContext.Builder(); private final Set dwoFiles = new LinkedHashSet<>(); @@ -262,6 +299,10 @@ public CcCompilationOutputs build() { return new CcCompilationOutputs( ImmutableList.copyOf(objectFiles), ImmutableList.copyOf(picObjectFiles), + pcmFiles.build(), + picPcmFiles.build(), + ImmutableList.copyOf(modulesInfoFiles), + ImmutableList.copyOf(picModulesInfoFiles), ltoCompilationContext.build(), ImmutableList.copyOf(dwoFiles), ImmutableList.copyOf(picDwoFiles), @@ -276,6 +317,10 @@ public CcCompilationOutputs build() { public Builder merge(CcCompilationOutputs outputs) { this.objectFiles.addAll(outputs.objectFiles); this.picObjectFiles.addAll(outputs.picObjectFiles); + this.pcmFiles.addTransitive(outputs.pcmFiles); + this.picPcmFiles.addTransitive(outputs.picPcmFiles); + this.modulesInfoFiles.addAll(outputs.modulesInfoFiles); + this.picModulesInfoFiles.addAll(outputs.picModulesInfoFiles); this.dwoFiles.addAll(outputs.dwoFiles); this.picDwoFiles.addAll(outputs.picDwoFiles); this.gcnoFiles.addAll(outputs.gcnoFiles); @@ -298,6 +343,20 @@ public Builder addObjectFile(Artifact artifact) { return this; } + /** Adds a pcm file. */ + @CanIgnoreReturnValue + public Builder addPcmFile(Artifact.DerivedArtifact artifact) { + pcmFiles.add(artifact); + return this; + } + + /** Adds a modules info file. */ + @CanIgnoreReturnValue + public Builder addModulesInfoFile(Artifact artifact) { + modulesInfoFiles.add(artifact); + return this; + } + @CanIgnoreReturnValue public Builder addObjectFiles(Iterable artifacts) { for (Artifact artifact : artifacts) { @@ -315,6 +374,19 @@ public Builder addPicObjectFile(Artifact artifact) { return this; } + /** Adds a pic pcm file. */ + @CanIgnoreReturnValue + public Builder addPicPcmFile(Artifact.DerivedArtifact artifact) { + picPcmFiles.add(artifact); + return this; + } + /** Adds a pic modules info file. */ + @CanIgnoreReturnValue + public Builder addPicModulesInfoFile(Artifact artifact) { + picModulesInfoFiles.add(artifact); + return this; + } + @CanIgnoreReturnValue public Builder addLtoBitcodeFile( Artifact fullBitcode, Artifact ltoIndexingBitcode, ImmutableList copts) { diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java index 8954f9006aa47f..0055ceaa929bd7 100755 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java @@ -1189,6 +1189,7 @@ public CcToolchainConfigInfo ccToolchainConfigInfoFromStarlark( String linkerToolPath = "DUMMY_LINKER_TOOL"; String arToolPath = "DUMMY_AR_TOOL"; String stripToolPath = "DUMMY_STRIP_TOOL"; + String depsScannerToolPath = "DUMMY_DEPS_SCANNER_TOOL"; for (Pair tool : toolPathList) { if (tool.first.equals(CppConfiguration.Tool.GCC.getNamePart())) { gccToolPath = tool.second; @@ -1207,6 +1208,9 @@ public CcToolchainConfigInfo ccToolchainConfigInfoFromStarlark( if (tool.first.equals(CppConfiguration.Tool.STRIP.getNamePart())) { stripToolPath = tool.second; } + if (tool.first.equals(CppConfiguration.Tool.DEPS_SCANNER.getNamePart())) { + depsScannerToolPath = tool.second; + } } ImmutableList.Builder legacyFeaturesBuilder = ImmutableList.builder(); @@ -1268,6 +1272,7 @@ public CcToolchainConfigInfo ccToolchainConfigInfoFromStarlark( gccToolPath, arToolPath, stripToolPath, + depsScannerToolPath, /* supportsInterfaceSharedLibraries= */ false, actionConfigNames)) { legacyActionConfigBuilder.add(new ActionConfig(actionConfig)); @@ -2023,6 +2028,7 @@ public Tuple compile( FeatureConfigurationForStarlark starlarkFeatureConfiguration, Info starlarkCcToolchainProvider, Sequence sourcesUnchecked, // expected + Sequence moduleInterfacesUnchecked, // expected Sequence publicHeadersUnchecked, // expected Sequence privateHeadersUnchecked, // expected Object textualHeadersStarlarkObject, @@ -2143,23 +2149,28 @@ public Tuple compile( configuration.getFragment(CppConfiguration.class))); boolean tuple = (!sourcesUnchecked.isEmpty() && sourcesUnchecked.get(0) instanceof Tuple) + || (!moduleInterfacesUnchecked.isEmpty() && moduleInterfacesUnchecked.get(0) instanceof Tuple) || (!publicHeadersUnchecked.isEmpty() && publicHeadersUnchecked.get(0) instanceof Tuple) || (!privateHeadersUnchecked.isEmpty() && privateHeadersUnchecked.get(0) instanceof Tuple); if (tuple) { ImmutableList> sources = convertSequenceTupleToPair(sourcesUnchecked); + ImmutableList> moduleInterfaces = convertSequenceTupleToPair(moduleInterfacesUnchecked); ImmutableList> publicHeaders = convertSequenceTupleToPair(publicHeadersUnchecked); ImmutableList> privateHeaders = convertSequenceTupleToPair(privateHeadersUnchecked); - helper.addPublicHeaders(publicHeaders).addPrivateHeaders(privateHeaders).addSources(sources); + helper.addPublicHeaders(publicHeaders).addPrivateHeaders(privateHeaders).addSources(sources) + .addModuleInterfaceSources(moduleInterfaces); } else { List sources = Sequence.cast(sourcesUnchecked, Artifact.class, "srcs"); + List moduleInterfaces = Sequence.cast(moduleInterfacesUnchecked, Artifact.class, "module_interfaces"); List publicHeaders = Sequence.cast(publicHeadersUnchecked, Artifact.class, "public_hdrs"); List privateHeaders = Sequence.cast(privateHeadersUnchecked, Artifact.class, "private_hdrs"); - helper.addPublicHeaders(publicHeaders).addPrivateHeaders(privateHeaders).addSources(sources); + helper.addPublicHeaders(publicHeaders).addPrivateHeaders(privateHeaders).addSources(sources) + .addModuleInterfaceSources(moduleInterfaces); } List includes = diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileBuildVariables.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileBuildVariables.java index ed1cdf2eb9d6fa..a34f4cd3ee6a08 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileBuildVariables.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileBuildVariables.java @@ -77,6 +77,8 @@ public enum CompileBuildVariables { * @see CcCompilationContext#getFrameworkIncludeDirs(). */ FRAMEWORK_PATHS("framework_include_paths"), + /** Variable for the c++20 module map file name. */ + CPP20_MODMAP_FILE("cpp20_modmap_file"), /** Variable for the module map file name. */ MODULE_MAP_FILE("module_map_file"), /** Variable for the dependent module map file name. */ diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/Cpp20ModuleDepMapAction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/Cpp20ModuleDepMapAction.java new file mode 100644 index 00000000000000..c830c55d280564 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/Cpp20ModuleDepMapAction.java @@ -0,0 +1,222 @@ +// Copyright 2014 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.rules.cpp; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.devtools.build.lib.actions.*; +import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander; +import com.google.devtools.build.lib.analysis.actions.DeterministicWriter; +import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; +import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; +import com.google.devtools.build.lib.server.FailureDetails; +import com.google.devtools.build.lib.util.Fingerprint; +import com.google.devtools.build.lib.vfs.FileSystemUtils; +import com.google.devtools.build.lib.vfs.Path; + +import javax.annotation.Nullable; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * Creates C++20 module map (.modmap) file. + * 1. the modules needed for compiling C++ Modules or ordinary sources + * are provided by the ddi file + * 2. all modules information provided by the modules info file + * 3. the output file .modmap collect all modules, including indirect dependencies + * e.g. a -> b -> c -> d + * the ddi file only record module a require module b + * However, in .modmap file, we need to put all modules (b, c, d) in it + * the format of .modmap file is + * -fmodule-file== + * -fmodule-file== + * ... + * 4. the output file .modmap.input collects all module files' paths + * it is convenient for CppCompileAction to handle dynamic modules input with the .modmap.input file + * otherwise, we need to parse the .modmap file and get all modules' path in CppCompileAction + * the format of .modmap file is + * + * + * ... + */ +@Immutable +public final class Cpp20ModuleDepMapAction extends AbstractAction { + private static final String GUID = "b02b043a-6c4e-4eeb-992a-09769b366fdd"; + Artifact ddiFile; + Artifact modulesInfoFile; + Artifact modmapFile; + Artifact modmapInputFile; + + public Cpp20ModuleDepMapAction( + ActionOwner owner, + Artifact ddiFile, + Artifact modulesInfoFile, + Artifact modmapFile, + Artifact modmapInputFile) { + super( + owner, + NestedSetBuilder.stableOrder().add(ddiFile).add(modulesInfoFile).build(), + ImmutableSet.of(modmapFile, modmapInputFile)); + this.ddiFile = ddiFile; + this.modulesInfoFile = modulesInfoFile; + this.modmapFile = modmapFile; + this.modmapInputFile = modmapInputFile; + } + + @Override + public ActionResult execute(ActionExecutionContext actionExecutionContext) + throws ActionExecutionException, InterruptedException { + try { + Map moduleMap = computeModuleMap(actionExecutionContext); + + var result1 = + writeOutputToFile( + actionExecutionContext, + out -> { + OutputStreamWriter content = + new OutputStreamWriter(out, StandardCharsets.ISO_8859_1); + for (Map.Entry entry : moduleMap.entrySet()) { + String moduleName = entry.getKey(); + String modulePath = entry.getValue(); + content.append("-f"); + content.append("module-file="); + content.append(moduleName); + content.append("="); + content.append(modulePath); + content.append('\n'); + } + content.flush(); + }, + modmapFile); + var result2 = + writeOutputToFile( + actionExecutionContext, + out -> { + OutputStreamWriter content = + new OutputStreamWriter(out, StandardCharsets.ISO_8859_1); + for (String modulePath : moduleMap.values()) { + content.append(modulePath); + content.append('\n'); + } + content.flush(); + }, + modmapInputFile); + var builder = new ImmutableList.Builder(); + builder.addAll(result1); + builder.addAll(result2); + return ActionResult.create(builder.build()); + } catch (ExecException e) { + throw ActionExecutionException.fromExecException(e, this); + } + } + + private Map computeModuleMap(ActionExecutionContext ctx) throws ExecException { + String ddi; + try { + ddi = FileSystemUtils.readContent(ctx.getInputPath(ddiFile), Charset.defaultCharset()); + } catch (IOException e) { + throw new EnvironmentalExecException( + e, + createFailureDetail( + String.format("read ddi file fail: %s", ddiFile.getExecPathString()), + FailureDetails.CppCompile.Code.DDI_FILE_READ_FAILURE)); + } + var moduleDep = Cpp20ModuleHelper.parseScanResult(ddi); + String modules; + try { + modules = + FileSystemUtils.readContent(ctx.getInputPath(modulesInfoFile), Charset.defaultCharset()); + } catch (IOException e) { + throw new EnvironmentalExecException( + e, + createFailureDetail( + String.format("read module info file fail: %s", modulesInfoFile.getExecPathString()), + FailureDetails.CppCompile.Code.MODULES_INFO_FILE_READ_FAILURE)); + } + + var cxxModules = Cpp20ModuleHelper.Cpp20ModulesInfo.fromJSON(modules); + Set moduleNameSet = new HashSet<>(); + Queue requireModuleQueue = new ArrayDeque<>(); + if (moduleDep.getRequireModules() != null) { + requireModuleQueue.addAll(moduleDep.getRequireModules()); + } + while (!requireModuleQueue.isEmpty()) { + String requireModuleName = requireModuleQueue.poll(); + moduleNameSet.add(requireModuleName); + var deps = cxxModules.getRequireModules(requireModuleName); + for (String dep : deps) { + if (moduleNameSet.contains(dep)) { + continue; + } + requireModuleQueue.add(dep); + } + } + Map moduleMap = new HashMap<>(moduleNameSet.size()); + for (String moduleName : moduleNameSet) { + String modulePath = cxxModules.getModulePath(moduleName); + if (modulePath == null) { + continue; + } + moduleMap.put(moduleName, modulePath); + } + return moduleMap; + } + + private ImmutableList writeOutputToFile( + ActionExecutionContext actionExecutionContext, + DeterministicWriter deterministicWriter, + Artifact output) + throws ExecException { + actionExecutionContext.getEventHandler().post(new RunningActionEvent(this, "local")); + Path outputPath = actionExecutionContext.getInputPath(output); + try { + try (OutputStream out = new BufferedOutputStream(outputPath.getOutputStream())) { + deterministicWriter.writeOutputFile(out); + } + } catch (IOException e) { + throw new EnvironmentalExecException( + e, FailureDetails.Execution.Code.FILE_WRITE_IO_EXCEPTION); + } + return ImmutableList.of(); + } + + @Override + public String getMnemonic() { + return "Cpp20ModuleDepMap"; + } + + @Override + protected void computeKey( + ActionKeyContext actionKeyContext, + @Nullable ArtifactExpander artifactExpander, + Fingerprint fp) { + fp.addString(GUID); + fp.addString(ddiFile.getExecPathString()); + fp.addString(modulesInfoFile.getExecPathString()); + fp.addString(modmapFile.getExecPathString()); + } + + private static FailureDetails.FailureDetail createFailureDetail( + String message, FailureDetails.CppCompile.Code detailedCode) { + return FailureDetails.FailureDetail.newBuilder() + .setMessage(message) + .setCppCompile(FailureDetails.CppCompile.newBuilder().setCode(detailedCode)) + .build(); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/Cpp20ModuleHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/Cpp20ModuleHelper.java new file mode 100644 index 00000000000000..fa0cbdf3915497 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/Cpp20ModuleHelper.java @@ -0,0 +1,277 @@ +package com.google.devtools.build.lib.rules.cpp; + +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.HashMap; + +/** + * to parse JSON content with format + * p1689 + * when discovering dependencies. + * all related class are put here + * for example, + * + * { + * "revision": 0, + * "rules": [ + * { + * "primary-output": "path/to/a.pcm", + * "provides": [ + * { + * "is-interface": true, + * "logical-name": "a", + * "source-path": "path/to/a.cppm" + * } + * ], + * "requires": [ + * { + * "logical-name": "b" + * } + * ] + * } + * ], + * "version": 1 + * } + * + * + * + */ +public class Cpp20ModuleHelper { + static class Require { + @SerializedName("logical-name") + private String logicalName; + + @SerializedName("source-path") + private String sourcePath; + + public String getLogicalName() { + return logicalName; + } + + public void setLogicalName(String logicalName) { + this.logicalName = logicalName; + } + + public String getSourcePath() { + return sourcePath; + } + + public void setSourcePath(String sourcePath) { + this.sourcePath = sourcePath; + } + } + + static class Provide { + @SerializedName("is-interface") + private Boolean interfaceModule; + @SerializedName("logical-name") + private String logicalName; + @SerializedName("source-path") + private String sourcePath; + + @Override + public String toString() { + Gson gson = new Gson(); + return gson.toJson(this); + } + + public Boolean getInterfaceModule() { + return interfaceModule; + } + + public void setInterfaceModule(Boolean interfaceModule) { + this.interfaceModule = interfaceModule; + } + + public String getLogicalName() { + return logicalName; + } + + public void setLogicalName(String logicalName) { + this.logicalName = logicalName; + } + + public String getSourcePath() { + return sourcePath; + } + + public void setSourcePath(String sourcePath) { + this.sourcePath = sourcePath; + } + } + + static class Rule { + @SerializedName("primary-output") + private String primaryOutput; + private List provides; + private List requires; + + @Override + public String toString() { + Gson gson = new Gson(); + return gson.toJson(this); + } + + public String getPrimaryOutput() { + return primaryOutput; + } + + public void setPrimaryOutput(String primaryOutput) { + this.primaryOutput = primaryOutput; + } + + public List getProvides() { + return provides; + } + + public void setProvides(List provides) { + this.provides = provides; + } + + public List getRequires() { + return requires; + } + + public void setRequires(List requires) { + this.requires = requires; + } + } + + static class Cpp20ModuleScanDepsResult { + private Integer revision; + private String version; + private List rules; + + @Override + public String toString() { + Gson gson = new Gson(); + return gson.toJson(this); + } + + public Integer getRevision() { + return revision; + } + + public void setRevision(Integer revision) { + this.revision = revision; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public List getRules() { + return rules; + } + + public void setRules(List rules) { + this.rules = rules; + } + } + + static class ModuleDep { + private boolean needProduceBMI = false; + private String moduleName; + private List requireModules; + + public boolean isNeedProduceBMI() { + return needProduceBMI; + } + + public void setNeedProduceBMI(boolean needProduceBMI) { + this.needProduceBMI = needProduceBMI; + } + + public String getModuleName() { + return moduleName; + } + + public void setModuleName(String moduleName) { + this.moduleName = moduleName; + } + + public List getRequireModules() { + return requireModules; + } + + public void setRequireModules(List requireModules) { + this.requireModules = requireModules; + } + } + + static class Cpp20ModulesInfo { + Cpp20ModulesInfo() { + modules = new HashMap<>(); + usages = new HashMap<>(); + } + void merge(Cpp20ModulesInfo other) { + this.modules.putAll(other.modules); + this.usages.putAll(other.usages); + } + private final Map modules; + private final Map> usages; + + public List getRequireModules(String moduleName) { + return usages.getOrDefault(moduleName, List.of()); + } + public void addRequireModule(String moduleName, String requireModuleName) { + usages.computeIfAbsent(moduleName, it -> new ArrayList<>()); + usages.get(moduleName).add(requireModuleName); + } + + public String getModulePath(String moduleName) { + return modules.getOrDefault(moduleName, null); + } + public void putModulePath(String moduleName, String modulePath) { + modules.put(moduleName, modulePath); + } + static Cpp20ModulesInfo fromJSON(String json) { + Gson gson = new Gson(); + return gson.fromJson(json, Cpp20ModulesInfo.class); + } + } + static ModuleDep parseScanResult(String out) { + Gson gson = new Gson(); + Cpp20ModuleScanDepsResult dep = gson.fromJson(out, Cpp20ModuleScanDepsResult.class); + if (dep == null) { + throw new RuntimeException("call clang-scan-deps error"); + } + if (dep.getRules() == null) { + throw new RuntimeException("call clang-scan-deps error"); + } + if (dep.getRules().size() != 1) { + throw new RuntimeException("expect only 1 rule, but got " + dep.getRules()); + } + ModuleDep moduleDep = new ModuleDep(); + Rule rule = dep.getRules().get(0); + if (rule.getProvides() != null && !rule.getProvides().isEmpty()) { + if (rule.getProvides().size() != 1) { + throw new RuntimeException("expect only 1 rule, but got " + rule.getProvides()); + } + Provide provide = rule.getProvides().get(0); + moduleDep.setModuleName(provide.getLogicalName()); + moduleDep.setNeedProduceBMI(true); + } + if (rule.getRequires() != null) { + var requireModules = new ArrayList(rule.getRequires().size()); + for (int i = 0; i < rule.getRequires().size(); i++) { + Require require = rule.getRequires().get(i); + requireModules.add(require.getLogicalName()); + } + moduleDep.setRequireModules(requireModules); + } + return moduleDep; + } + public static boolean isCpp20ModuleCompilationAction(String actionName) { + return CppActionNames.CPP20_MODULE_COMPILE.equals(actionName) + || CppActionNames.CPP20_MODULE_CODEGEN.equals(actionName); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/Cpp20ModulesInfoAction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/Cpp20ModulesInfoAction.java new file mode 100644 index 00000000000000..332a7d61c16b61 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/Cpp20ModulesInfoAction.java @@ -0,0 +1,132 @@ +// Copyright 2014 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.rules.cpp; + +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.lib.actions.ActionExecutionContext; +import com.google.devtools.build.lib.actions.ActionKeyContext; +import com.google.devtools.build.lib.actions.ActionOwner; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander; +import com.google.devtools.build.lib.analysis.actions.AbstractFileWriteAction; +import com.google.devtools.build.lib.analysis.actions.DeterministicWriter; +import com.google.devtools.build.lib.collect.nestedset.NestedSet; +import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; +import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; +import com.google.devtools.build.lib.util.Fingerprint; +import com.google.devtools.build.lib.util.Pair; +import com.google.devtools.build.lib.vfs.FileSystemUtils; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import javax.annotation.Nullable; +import java.io.OutputStreamWriter; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * Creates C++20 Modules info file (.CXXModules.json) + * e.g. + * { + * "modules": { + * "a": "path/to/bmi/of/module/a", + * "b": "path/to/bmi/of/module/b", + * }, + * "usages": { + * "a": ["b"] + * } + * } + * Each target has only one to record all modules information + * 1. modules: the map of module-name -> module bmi + * 2. usages: the direct dependencies of each module + */ +@Immutable +public final class Cpp20ModulesInfoAction extends AbstractFileWriteAction { + + private static final String GUID = "dd122dd8-72c2-4c98-b343-fac327adb923"; + NestedSet ddiFiles; + ImmutableList> pcmAndDdiPairList; + NestedSet modulesInfoFiles; + public Cpp20ModulesInfoAction( + ActionOwner owner, + NestedSet ddiFiles, + ImmutableList> pcmAndDdiPairList, + NestedSet modulesInfoFiles, + Artifact modulesInfoFile + ) { + super( + owner, + collectInput(ddiFiles, modulesInfoFiles), + modulesInfoFile, + /*makeExecutable=*/ true); + this.ddiFiles = ddiFiles; + this.pcmAndDdiPairList = pcmAndDdiPairList; + this.modulesInfoFiles = modulesInfoFiles; + } + private static NestedSet collectInput(NestedSet ddiFiles, NestedSet modulesInfoFiles) { + return NestedSetBuilder.stableOrder() + .addTransitive(ddiFiles) + .addTransitive(modulesInfoFiles) + .build(); + } + @Override + public DeterministicWriter newDeterministicWriter(ActionExecutionContext ctx) { + return out -> { + var cxxModules = new Cpp20ModuleHelper.Cpp20ModulesInfo(); + OutputStreamWriter content = new OutputStreamWriter(out, StandardCharsets.ISO_8859_1); + for (Pair pair : pcmAndDdiPairList) { + Artifact pcmFile = pair.first; + Artifact ddiFile = pair.second; + String s = FileSystemUtils.readContent(ctx.getInputPath(ddiFile), Charset.defaultCharset()); + var moduleDep = Cpp20ModuleHelper.parseScanResult(s); + if (moduleDep.isNeedProduceBMI() && pcmFile != null) { + cxxModules.putModulePath(moduleDep.getModuleName(), pcmFile.getExecPathString()); + } + if (moduleDep.getRequireModules()!=null) { + for (String requireModule : moduleDep.getRequireModules()) { + cxxModules.addRequireModule(moduleDep.getModuleName(), requireModule); + } + } + } + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + for (Artifact depModulesInfoFile : modulesInfoFiles.toList()) { + String modulesInfoContent = FileSystemUtils.readContent(ctx.getInputPath(depModulesInfoFile), Charset.defaultCharset()); + Cpp20ModuleHelper.Cpp20ModulesInfo depCpp20ModulesInfo = Cpp20ModuleHelper.Cpp20ModulesInfo.fromJSON(modulesInfoContent); + cxxModules.merge(depCpp20ModulesInfo); + } + String json = gson.toJson(cxxModules); + content.append(json); + content.flush(); + }; + } + + @Override + public String getMnemonic() { + return "Cpp20ModulesInfo"; + } + + @Override + protected void computeKey( + ActionKeyContext actionKeyContext, + @Nullable ArtifactExpander artifactExpander, + Fingerprint fp) { + fp.addString(GUID); + ImmutableList ddiFileList = ddiFiles.toList(); + fp.addInt(ddiFileList.size()); + for (Artifact artifact : ddiFileList) { + fp.addString(artifact.getExecPathString()); + } + } + +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppActionConfigs.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppActionConfigs.java index 77e1b89162dc69..df242c7f52f302 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppActionConfigs.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppActionConfigs.java @@ -72,6 +72,9 @@ public static ImmutableList getLegacyFeatures( " action: 'c++-header-parsing'", " action: 'c++-module-compile'", " action: 'c++-module-codegen'", + " action: 'c++20-deps-scanning'", + " action: 'c++20-module-compile'", + " action: 'c++20-module-codegen'", " action: 'lto-backend'", " action: 'clif-match'", " flag_group {", @@ -102,7 +105,9 @@ public static ImmutableList getLegacyFeatures( " action: 'preprocess-assemble'", " action: 'c-compile'", " action: 'c++-compile'", - " action: 'c++-module-compile'", + " action: 'c++-module-codegen'", + " action: 'c++20-deps-scanning'", + " action: 'c++20-module-codegen'", " action: 'objc-compile'", " action: 'objc++-compile'", " action: 'c++-header-parsing'", @@ -133,6 +138,9 @@ public static ImmutableList getLegacyFeatures( " action: 'c++-compile'", " action: 'c++-module-codegen'", " action: 'c++-module-compile'", + " action: 'c++20-deps-scanning'", + " action: 'c++20-module-compile'", + " action: 'c++20-module-codegen'", " flag_group {", " expand_if_all_available: 'output_file'", " flag: '-frandom-seed=%{output_file}'", @@ -154,6 +162,9 @@ public static ImmutableList getLegacyFeatures( " action: 'c++-compile'", " action: 'c++-module-codegen'", " action: 'c++-module-compile'", + " action: 'c++20-deps-scanning'", + " action: 'c++20-module-compile'", + " action: 'c++20-module-codegen'", " flag_group {", " expand_if_all_available: 'pic'", " flag: '-fPIC'", @@ -172,6 +183,7 @@ public static ImmutableList getLegacyFeatures( " action: 'c-compile'", " action: 'c++-compile'", " action: 'c++-module-codegen'", + " action: 'c++20-module-compile'", " flag_group {", " expand_if_all_available: 'per_object_debug_info_file'", " flag: '-gsplit-dwarf'", @@ -193,6 +205,8 @@ public static ImmutableList getLegacyFeatures( " action: 'c++-compile'", " action: 'c++-header-parsing'", " action: 'c++-module-compile'", + " action: 'c++20-deps-scanning'", + " action: 'c++20-module-compile'", " action: 'clif-match'", " flag_group {", " iterate_over: 'preprocessor_defines'", @@ -214,6 +228,8 @@ public static ImmutableList getLegacyFeatures( " action: 'c++-compile'", " action: 'c++-header-parsing'", " action: 'c++-module-compile'", + " action: 'c++20-deps-scanning'", + " action: 'c++20-module-compile'", " action: 'clif-match'", " action: 'objc-compile'", " action: 'objc++-compile'", @@ -239,6 +255,8 @@ public static ImmutableList getLegacyFeatures( " action: 'c++-compile'", " action: 'c++-header-parsing'", " action: 'c++-module-compile'", + " action: 'c++20-deps-scanning'", + " action: 'c++20-module-compile'", " action: 'clif-match'", " action: 'objc-compile'", " action: 'objc++-compile'", @@ -956,6 +974,7 @@ public static ImmutableList getLegacyFeatures( " action: 'c-compile'", " action: 'c++-compile'", " action: 'c++-module-compile'", + " action: 'c++20-module-compile'", " action: 'objc-compile'", " action: 'objc++-compile'", " flag_group {", @@ -989,6 +1008,7 @@ public static ImmutableList getLegacyFeatures( " action: 'c-compile'", " action: 'c++-compile'", " action: 'c++-module-compile'", + " action: 'c++20-module-compile'", " action: 'objc-compile'", " action: 'objc++-compile'", " action: 'objc-executable'", @@ -1029,6 +1049,7 @@ public static ImmutableList getLegacyActionConfigs( String gccToolPath, String arToolPath, String stripToolPath, + String depsScannerToolPath, boolean supportsInterfaceSharedLibraries, ImmutableSet existingActionConfigNames) { try { @@ -1186,6 +1207,59 @@ public static ImmutableList getLegacyActionConfigs( " implies: 'compiler_input_flags'", " implies: 'compiler_output_flags'"))); } + + if (!existingActionConfigNames.contains(CppActionNames.CPP20_DEPS_SCANNING)) { + actionConfigBuilder.add( + getActionConfig( + Joiner.on("\n") + .join( + " config_name: 'c++20-deps-scanning'", + " action_name: 'c++20-deps-scanning'", + " tool {", + " tool_path: '" + depsScannerToolPath + "'", + " }", + " implies: 'legacy_compile_flags'", + " implies: 'user_compile_flags'", + " implies: 'sysroot'", + " implies: 'unfiltered_compile_flags'", + " implies: 'compiler_input_flags'", + " implies: 'compiler_output_flags'"))); + } + if (!existingActionConfigNames.contains(CppActionNames.CPP20_MODULE_COMPILE)) { + actionConfigBuilder.add( + getActionConfig( + Joiner.on("\n") + .join( + " config_name: 'c++20-module-compile'", + " action_name: 'c++20-module-compile'", + " tool {", + " tool_path: '" + gccToolPath + "'", + " }", + " implies: 'cpp20_module_compile'", + " implies: 'legacy_compile_flags'", + " implies: 'user_compile_flags'", + " implies: 'sysroot'", + " implies: 'unfiltered_compile_flags'", + " implies: 'compiler_input_flags'", + " implies: 'compiler_output_flags'"))); + } + if (!existingActionConfigNames.contains(CppActionNames.CPP20_MODULE_CODEGEN)) { + actionConfigBuilder.add( + getActionConfig( + Joiner.on("\n") + .join( + " config_name: 'c++20-module-codegen'", + " action_name: 'c++20-module-codegen'", + " tool {", + " tool_path: '" + gccToolPath + "'", + " }", + " implies: 'legacy_compile_flags'", + " implies: 'user_compile_flags'", + " implies: 'sysroot'", + " implies: 'unfiltered_compile_flags'", + " implies: 'compiler_input_flags'", + " implies: 'compiler_output_flags'"))); + } if (!existingActionConfigNames.contains(CppActionNames.CPP_LINK_EXECUTABLE)) { actionConfigBuilder.add( getActionConfig( @@ -1417,6 +1491,9 @@ public static ImmutableList getFeaturesToAppearLastInFeature " action: 'c++-header-parsing'", " action: 'c++-module-compile'", " action: 'c++-module-codegen'", + " action: 'c++20-deps-scanning'", + " action: 'c++20-module-compile'", + " action: 'c++20-module-codegen'", " action: 'lto-backend'", " action: 'clif-match'", " flag_group {", @@ -1440,6 +1517,8 @@ public static ImmutableList getFeaturesToAppearLastInFeature " action: 'c++-compile'", " action: 'c++-header-parsing'", " action: 'c++-module-compile'", + " action: 'c++20-deps-scanning'", + " action: 'c++20-module-compile'", " action: 'c++-link-executable'", " action: 'c++-link-dynamic-library'", " action: 'c++-link-nodeps-dynamic-library'", @@ -1454,6 +1533,48 @@ public static ImmutableList getFeaturesToAppearLastInFeature " }", " }"))); } + if (!existingFeatureNames.contains("cpp20_module")) { + featureBuilder.add( + getFeature( + Joiner.on("\n") + .join( + " name: 'cpp20_module'", + " enabled: false" + ))); + } + if (!existingFeatureNames.contains("cpp20_modmap_file")) { + featureBuilder.add( + getFeature( + Joiner.on("\n") + .join( + " name: 'cpp20_modmap_file'", + " enabled: true", + " flag_set {", + " action: 'c++-compile'", + " action: 'c++20-module-compile'", + " action: 'c++20-module-codegen'", + " flag_group {", + " expand_if_all_available: 'cpp20_modmap_file'", + " flag: '@%{cpp20_modmap_file}'", + " }", + " }"))); + } + if (!existingFeatureNames.contains("cpp20_module_compile")) { + featureBuilder.add( + getFeature( + Joiner.on("\n") + .join( + " name: 'cpp20_module_compile'", + " enabled: true", + " flag_set {", + " action: 'c++20-module-compile'", + " flag_group {", + " flag: '--precompile'", + " flag: '-x'", + " flag: 'c++-module'", + " }", + " }"))); + } // unfiltered_compile_flags contain system include paths. These must be added // after the user provided options (present in legacy_compile_flags build // variable above), otherwise users adding include paths will not pick up their own @@ -1474,6 +1595,9 @@ public static ImmutableList getFeaturesToAppearLastInFeature " action: 'c++-header-parsing'", " action: 'c++-module-compile'", " action: 'c++-module-codegen'", + " action: 'c++20-deps-scanning'", + " action: 'c++20-module-compile'", + " action: 'c++20-module-codegen'", " action: 'lto-backend'", " action: 'clif-match'", " flag_group {", @@ -1483,6 +1607,24 @@ public static ImmutableList getFeaturesToAppearLastInFeature " }", " }"))); } + if (!existingFeatureNames.contains("cpp20_modmap_file")) { + String compileModmapFile = " flag: '@%{cpp20_modmap_file}'"; + featureBuilder.add( + getFeature( + Joiner.on("\n") + .join( + " name: 'compile_modmap_file'", + " enabled: true", + " flag_set {", + " action: 'c++-compile'", + " action: 'c++20-module-compile'", + " action: 'c++20-module-codegen'", + " flag_group {", + " expand_if_all_available: 'cpp20_modmap_file'", + compileModmapFile, + " }", + " }"))); + } if (!existingFeatureNames.contains("linker_param_file")) { String dynamicLibraryParamFile = " flag: '@%{linker_param_file}'"; featureBuilder.add( @@ -1525,6 +1667,9 @@ public static ImmutableList getFeaturesToAppearLastInFeature " action: 'linkstamp-compile'", " action: 'c++-module-compile'", " action: 'c++-module-codegen'", + " action: 'c++20-deps-scanning'", + " action: 'c++20-module-compile'", + " action: 'c++20-module-codegen'", " action: 'objc-compile'", " action: 'objc++-compile'", " action: 'c++-header-parsing'", @@ -1552,6 +1697,9 @@ public static ImmutableList getFeaturesToAppearLastInFeature " action: 'linkstamp-compile'", " action: 'c++-module-compile'", " action: 'c++-module-codegen'", + " action: 'c++20-deps-scanning'", + " action: 'c++20-module-compile'", + " action: 'c++20-module-codegen'", " action: 'objc-compile'", " action: 'objc++-compile'", " action: 'c++-header-parsing'", diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppActionNames.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppActionNames.java index ab4881340abd16..fc7ba2c31dc0c7 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppActionNames.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppActionNames.java @@ -36,6 +36,11 @@ public class CppActionNames { public static final String OBJCPP_COMPILE = "objc++-compile"; /** A string constant for the c++ header parsing. */ public static final String CPP_HEADER_PARSING = "c++-header-parsing"; + /** A string constant for the c++20 modules deps scanning */ + public static final String CPP20_DEPS_SCANNING = "c++20-deps-scanning"; + /** A string constant for the c++20 module compile action. */ + public static final String CPP20_MODULE_COMPILE = "c++20-module-compile"; + public static final String CPP20_MODULE_CODEGEN = "c++20-module-codegen"; /** * A string constant for the c++ module compilation action. Note: currently we don't support C * module compilation. diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java index 534ed1ada2452a..c7efba5d2cd94d 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java @@ -94,14 +94,17 @@ import com.google.devtools.build.skyframe.SkyframeLookupResult; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; +import java.util.Objects; import java.util.List; import java.util.Map; +import java.util.HashMap; import java.util.Set; import java.util.UUID; import java.util.function.Predicate; @@ -120,6 +123,7 @@ public class CppCompileAction extends AbstractAction implements IncludeScannable @VisibleForTesting static final String OBJC_COMPILE_MNEMONIC = "ObjcCompile"; @Nullable private final Artifact gcnoFile; + @Nullable private final Artifact dotdFile; private final Artifact sourceFile; private final BuildConfigurationValue configuration; private final NestedSet mandatoryInputs; @@ -173,6 +177,7 @@ public class CppCompileAction extends AbstractAction implements IncludeScannable * first. */ private Set usedModules; + private Set usedCpp20Modules; private boolean inputsDiscovered = false; @@ -193,6 +198,8 @@ public class CppCompileAction extends AbstractAction implements IncludeScannable private ParamFileActionInput paramFileActionInput; private PathFragment paramFilePath; + private final NestedSet pcmFiles; + private final Artifact modmapInputFile; /** * Creates a new action to compile C/C++ source files. * @@ -248,7 +255,10 @@ public class CppCompileAction extends AbstractAction implements IncludeScannable CppSemantics cppSemantics, ImmutableList builtInIncludeDirectories, @Nullable Artifact grepIncludes, - ImmutableList additionalOutputs) { + ImmutableList additionalOutputs, + NestedSet pcmFiles, + Artifact modmapInputFile + ) { super( owner, mandatoryInputs, @@ -259,8 +269,11 @@ public class CppCompileAction extends AbstractAction implements IncludeScannable gcnoFile, dwoFile, ltoIndexingFile, - additionalOutputs)); + additionalOutputs, + featureConfiguration, + actionName)); this.gcnoFile = gcnoFile; + this.dotdFile = dotdFile; this.sourceFile = sourceFile; this.shareable = shareable; this.configuration = configuration; @@ -315,7 +328,13 @@ public class CppCompileAction extends AbstractAction implements IncludeScannable if (separateModule != null && !separateModule.equals(getPrimaryOutput())) { allowedDerivedInputsBuilder.add(separateModule); } + if (pcmFiles != null) { + allowedDerivedInputsBuilder.addTransitive(pcmFiles); + } allowedDerivedInputs = allowedDerivedInputsBuilder.build(); + + this.pcmFiles = pcmFiles; + this.modmapInputFile = modmapInputFile; } private static ImmutableSet collectOutputs( @@ -325,7 +344,10 @@ private static ImmutableSet collectOutputs( @Nullable Artifact gcnoFile, @Nullable Artifact dwoFile, @Nullable Artifact ltoIndexingFile, - ImmutableList additionalOutputs) { + ImmutableList additionalOutputs, + FeatureConfiguration featureConfiguration, + String actionName + ) { ImmutableSet.Builder outputs = ImmutableSet.builder(); outputs.add(outputFile); if (gcnoFile != null) { @@ -333,7 +355,26 @@ private static ImmutableSet collectOutputs( } outputs.addAll(additionalOutputs); if (dotdFile != null) { - outputs.add(dotdFile); + if (featureConfiguration.isEnabled(CppRuleClasses.CPP20_MODULE)) { + switch (actionName) { + // if cpp20_module enabled, only c++20-deps-scanning will produce .d file + // other actions will reuse the .d file from c++20-deps-scanning + case CppActionNames.CPP_COMPILE: + case CppActionNames.CPP20_MODULE_COMPILE: + case CppActionNames.CPP20_MODULE_CODEGEN: + { + break; + } + case CppActionNames.CPP20_DEPS_SCANNING: + // other source files. e.g. c + default: { + outputs.add(dotdFile); + } + } + } + else { + outputs.add(dotdFile); + } } if (diagnosticsFile != null) { outputs.add(diagnosticsFile); @@ -435,7 +476,8 @@ private void clearAdditionalInputs() { @Override public boolean discoversInputs() { - return shouldScanIncludes || getDotdFile() != null || shouldParseShowIncludes(); + return Cpp20ModuleHelper.isCpp20ModuleCompilationAction(actionName) || + shouldScanIncludes || getDotdFile() != null || shouldParseShowIncludes(); } @Override @@ -502,7 +544,9 @@ public void prepareInputDiscovery() { @Override public NestedSet discoverInputs(ActionExecutionContext actionExecutionContext) throws ActionExecutionException, InterruptedException { - Preconditions.checkArgument(!sourceFile.isFileType(CppFileTypes.CPP_MODULE)); + if (!Cpp20ModuleHelper.isCpp20ModuleCompilationAction(actionName)) { + Preconditions.checkArgument(!sourceFile.isFileType(CppFileTypes.CPP_MODULE)); + } if (additionalInputs == null) { List options; @@ -516,6 +560,10 @@ public NestedSet discoverInputs(ActionExecutionContext actionExecution DetailedExitCode code = createDetailedExitCode(message, Code.COMMAND_GENERATION_FAILURE); throw new ActionExecutionException(message, this, /*catastrophe=*/ false, code); } + usedCpp20Modules = computeUsedCpp20Modules(actionExecutionContext); + if (usedCpp20Modules == null) { + return null; + } commandLineKey = computeCommandLineKey(options); ImmutableList systemIncludeDirs = getSystemIncludeDirs(options); boolean siblingLayout = @@ -531,6 +579,7 @@ public NestedSet discoverInputs(ActionExecutionContext actionExecution additionalInputs = NestedSetBuilder.fromNestedSet(ccCompilationContext.getDeclaredIncludeSrcs()) .addTransitive(additionalPrunableHeaders) + .addAll(usedCpp20Modules) .build(); if (needsIncludeValidation) { verifyActionIncludePaths(systemIncludeDirs, siblingLayout); @@ -603,7 +652,8 @@ public NestedSet discoverInputs(ActionExecutionContext actionExecution additionalInputs = NestedSetBuilder.fromNestedSet(additionalInputs).addTransitive(discoveredModules).build(); - if (getPrimaryOutput().isFileType(CppFileTypes.CPP_MODULE)) { + if (getPrimaryOutput().isFileType(CppFileTypes.CPP_MODULE) + && !Cpp20ModuleHelper.isCpp20ModuleCompilationAction(actionName)) { this.discoveredModules = discoveredModules; } usedModules = null; @@ -660,7 +710,7 @@ public NestedSet getDiscoveredModules() { /** Returns the path where the compiler should put the discovered dependency information. */ public Artifact getDotdFile() { - return compileCommandLine.getDotdFile(); + return dotdFile; } public CcCompilationContext getCcCompilationContext() { @@ -1167,7 +1217,8 @@ public NestedSet getAllowedDerivedInputs() { @Override public synchronized void updateInputs(NestedSet inputs) { super.updateInputs(inputs); - if (getPrimaryOutput().isFileType(CppFileTypes.CPP_MODULE)) { + if (getPrimaryOutput().isFileType(CppFileTypes.CPP_MODULE) + && !Cpp20ModuleHelper.isCpp20ModuleCompilationAction(actionName)) { discoveredModules = NestedSetBuilder.wrap( Order.STABLE_ORDER, @@ -1178,10 +1229,14 @@ public synchronized void updateInputs(NestedSet inputs) { @Override protected String getRawProgressMessage() { - return (actionName.equals(CppActionNames.CPP_HEADER_ANALYSIS) - ? "Header analysis for " - : "Compiling ") - + getSourceFile().prettyPrint(); + switch (actionName) { + case CppActionNames.CPP_HEADER_ANALYSIS: + return "Header analysis for " + getSourceFile().prettyPrint(); + case CppActionNames.CPP20_DEPS_SCANNING: + return "Deps scanning for " + getSourceFile().prettyPrint(); + default: + return "Compiling " + getSourceFile().prettyPrint(); + } } /** Returns explicitly listed header files. */ @@ -1429,6 +1484,12 @@ public ActionResult execute(ActionExecutionContext actionExecutionContext) siblingRepositoryLayout); dotDContents = null; // Garbage collect in-memory .d contents. + if (usedCpp20Modules != null) { + discoveredInputs = NestedSetBuilder.stableOrder() + .addAll(usedCpp20Modules) + .addTransitive(discoveredInputs) + .build(); + } updateActionInputs(discoveredInputs); // hdrs_check: This cannot be switched off for C++ build actions, @@ -1492,6 +1553,64 @@ private boolean shouldParseShowIncludes() { return featureConfiguration.isEnabled(CppRuleClasses.PARSE_SHOWINCLUDES); } + /** + * Dynamic dependencies handle for C++20 Modules + * We use restart mechanism to ensure all required module files have been generated. + */ + private Set computeUsedCpp20Modules(ActionExecutionContext actionExecutionContext) + throws ActionExecutionException, InterruptedException { + // if cpp20_module not enable, skip + if (!featureConfiguration.isEnabled(CppRuleClasses.CPP20_MODULE)) { + return Set.of(); + } + // c++20-deps-scanning use source file and header files only + if (Objects.equals(CppActionNames.CPP20_DEPS_SCANNING, actionName)) { + return Set.of(); + } + if (!Cpp20ModuleHelper.isCpp20ModuleCompilationAction(actionName) + && !CppFileTypes.CPP_SOURCE.matches(sourceFile.getExecPath())) { + return Set.of(); + } + Set usedModules = new HashSet<>(); + ImmutableList modulePathList; + try { + modulePathList = FileSystemUtils.readLines( + actionExecutionContext.getInputPath(modmapInputFile), Charset.defaultCharset()); + } catch (IOException e) { + String message = + String.format( + "failed to read modmap input: %s", + modmapInputFile.getExecPathString()); + DetailedExitCode code = createDetailedExitCode(message, Code.MODMAP_INPUT_FILE_READ_FAILURE); + throw new ActionExecutionException(message, this, /*catastrophe=*/ false, code); + } + var pcmFileSet = pcmFiles.toSet(); + Map pcmFileMap = new HashMap<>(pcmFileSet.size()); + for (DerivedArtifact pcmFile : pcmFileSet) { + pcmFileMap.put(pcmFile.getExecPathString(), pcmFile); + } + for (String modulePath : modulePathList) { + if (pcmFileMap.containsKey(modulePath)) { + usedModules.add(pcmFileMap.get(modulePath)); + } + } + // use restart mechanism + var env = actionExecutionContext.getEnvironmentForDiscoveringInputs(); + var skyKeys = Collections2.transform(usedModules, DerivedArtifact::getGeneratingActionKey); + var actionExecutionValues = env.getValuesAndExceptions(skyKeys); + if (env.valuesMissing()) { + return null; + } + for (DerivedArtifact module: usedModules) { + Preconditions.checkState(module.isFileType(CppFileTypes.CPP_MODULE), "Non-module? %s", module); + var skyValue = actionExecutionValues.get(module.getGeneratingActionKey()); + if (skyValue == null) { + return null; + } + } + return usedModules; + } + Spawn createSpawn(Path execRoot, Map clientEnv) throws ActionExecutionException { // Intentionally not adding {@link CppCompileAction#inputsForInvalidation}, those are not needed // for execution. @@ -1549,10 +1668,19 @@ Spawn createSpawn(Path execRoot, Map clientEnv) throws ActionExe } try { + var envOld = getEffectiveEnvironment(clientEnv); + Map environment = Maps.newLinkedHashMapWithExpectedSize(envOld.size() + 1); + environment.putAll(envOld); + if (Objects.equals(CppActionNames.CPP20_DEPS_SCANNING, actionName)) { + // export DEPS_SCANNER_OUTPUT_FILE + // redirect clang-scan-deps output to outputFile + // due to clang-scan-deps has no option like `-o` to specify the output file + environment.put("DEPS_SCANNER_OUTPUT_FILE", getPrimaryOutput().getExecPathString()); + } return new SimpleSpawn( this, ImmutableList.copyOf(getArguments()), - getEffectiveEnvironment(clientEnv), + ImmutableMap.copyOf(environment), executionInfo.buildOrThrow(), /* filesetMappings= */ ImmutableMap.of(), inputs, @@ -1761,6 +1889,8 @@ static String actionNameToMnemonic( : CPP_COMPILE_MNEMONIC + suffix; case CppActionNames.CPP_HEADER_ANALYSIS: return "CppHeaderAnalysis"; + case CppActionNames.CPP20_DEPS_SCANNING: + return "CppDepsScanning"; default: return CPP_COMPILE_MNEMONIC; } diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java index 6234a14b47ccdc..b1fae79b0f0887 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java @@ -52,6 +52,9 @@ public final class CppCompileActionBuilder { private Artifact sourceFile; private final NestedSetBuilder mandatoryInputsBuilder; private Artifact outputFile; + private Artifact modmapFile; + private Artifact modmapInputFile; + private NestedSet pcmFiles; private Artifact dwoFile; private Artifact ltoIndexingFile; private Artifact dotdFile; @@ -129,6 +132,9 @@ public CppCompileActionBuilder(CppCompileActionBuilder other) { this.ccToolchain = other.ccToolchain; this.actionName = other.actionName; this.additionalOutputs = other.additionalOutputs; + this.pcmFiles = other.pcmFiles; + this.modmapFile = other.modmapFile; + this.modmapInputFile = other.modmapInputFile; } public CppCompileActionBuilder setSourceFile(Artifact sourceFile) { @@ -287,35 +293,40 @@ public CppCompileAction buildAndVerify() throws UnconfiguredActionConfigExceptio actionName, featureConfiguration, cppConfiguration.useCppCompileHeaderMnemonic())); // Copying the collections is needed to make the builder reusable. - return new CppCompileAction( - owner, - featureConfiguration, - variables, - sourceFile, - configuration, - shareable, - shouldScanIncludes, - usePic, - useHeaderModules, - realMandatoryInputs, - realMandatorySpawnInputs, - getBuiltinIncludeFiles(), - prunableHeaders, - outputFile, - dotdFile, - diagnosticsFile, - gcnoFile, - dwoFile, - ltoIndexingFile, - ccCompilationContext, - coptsFilter, - ImmutableList.copyOf(additionalIncludeScanningRoots), - ImmutableMap.copyOf(executionInfo), - actionName, - cppSemantics, - getBuiltinIncludeDirectories(), - ccToolchain.getGrepIncludes(), - additionalOutputs); + CppCompileAction action; + action = + new CppCompileAction( + owner, + featureConfiguration, + variables, + sourceFile, + configuration, + shareable, + shouldScanIncludes, + usePic, + useHeaderModules, + realMandatoryInputs, + realMandatorySpawnInputs, + getBuiltinIncludeFiles(), + prunableHeaders, + outputFile, + dotdFile, + diagnosticsFile, + gcnoFile, + dwoFile, + ltoIndexingFile, + ccCompilationContext, + coptsFilter, + ImmutableList.copyOf(additionalIncludeScanningRoots), + ImmutableMap.copyOf(executionInfo), + actionName, + cppSemantics, + getBuiltinIncludeDirectories(), + ccToolchain.getGrepIncludes(), + additionalOutputs, + pcmFiles, + modmapInputFile); + return action; } private ImmutableList getBuiltinIncludeFiles() throws EvalException { @@ -354,6 +365,19 @@ NestedSet buildMandatoryInputs() throws EvalException { realMandatoryInputsBuilder.addTransitive(ccCompilationContext.getDeclaredIncludeSrcs()); realMandatoryInputsBuilder.addTransitive(additionalPrunableHeaders); } + if (CppActionNames.CPP20_DEPS_SCANNING.equals(actionName)) { + // scan deps, do nothing + } + else if (Cpp20ModuleHelper.isCpp20ModuleCompilationAction(actionName) + || CppFileTypes.CPP_SOURCE.matches(sourceFile.getExecPath())) { + // C++20 module compile and codegen + // or C++ source compile + if (featureConfiguration.isEnabled(CppRuleClasses.CPP20_MODULE)) { + Preconditions.checkNotNull(modmapFile); + Preconditions.checkNotNull(modmapInputFile); + realMandatoryInputsBuilder.add(modmapFile).add(modmapInputFile); + } + } return realMandatoryInputsBuilder.build(); } @@ -616,6 +640,23 @@ public CppCompileActionBuilder setAdditionalPrunableHeaders( return this; } + @CanIgnoreReturnValue + public CppCompileActionBuilder setModmapFile(Artifact modmapFile) { + this.modmapFile = modmapFile; + return this; + } + + @CanIgnoreReturnValue + public CppCompileActionBuilder setModmapInputFile(Artifact modmapInputFile) { + this.modmapInputFile = modmapInputFile; + return this; + } + + @CanIgnoreReturnValue + public CppCompileActionBuilder setPcmFiles(NestedSet pcmFiles) { + this.pcmFiles = pcmFiles; + return this; + } ImmutableList getBuiltinIncludeDirectories() throws EvalException { return ccToolchain.getBuiltInIncludeDirectories(); } diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java index 2a316f21d8e8eb..bac5e1319746bf 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java @@ -76,6 +76,7 @@ public enum Tool { OBJDUMP("objdump"), STRIP("strip"), DWP("dwp"), + DEPS_SCANNER("deps-scanner"), LLVM_PROFDATA("llvm-profdata"); private final String namePart; diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java index fc5e215ae97801..6d5d16a0013685 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java @@ -107,6 +107,8 @@ public static ToolchainTypeRequirement ccToolchainTypeRequirement(RuleDefinition */ public static final String MODULE_MAPS = "module_maps"; + public static final String CPP20_MODULE = "cpp20_module"; + /** * A string constant for the random_seed feature. This is used by gcc and Clangfor the * randomization of symbol names that are in the anonymous namespace but have external linkage. diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CcModuleApi.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CcModuleApi.java index aaea454242da3f..a84eda38911140 100755 --- a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CcModuleApi.java +++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CcModuleApi.java @@ -110,6 +110,12 @@ default void compilerFlagExists() {} positional = false, named = true, defaultValue = "[]"), + @Param( + name = "module_interfaces", + doc = "The list of module interfaces source files to be compiled.", + positional = false, + named = true, + defaultValue = "[]"), @Param( name = "public_hdrs", doc = @@ -373,6 +379,7 @@ Tuple compile( FeatureConfigurationT starlarkFeatureConfiguration, Info starlarkCcToolchainProvider, Sequence sourcesUnchecked, // expected + Sequence moduleInterfacesUnchecked, // expected Sequence publicHeadersUnchecked, // expected Sequence privateHeadersUnchecked, // expected Object textualHeadersStarlarkObject, diff --git a/src/main/protobuf/failure_details.proto b/src/main/protobuf/failure_details.proto index 30cb43c5847e8d..c645d78a74b76b 100644 --- a/src/main/protobuf/failure_details.proto +++ b/src/main/protobuf/failure_details.proto @@ -1080,6 +1080,9 @@ message CppCompile { D_FILE_PARSE_FAILURE = 9 [(metadata) = { exit_code: 1 }]; COVERAGE_NOTES_CREATION_FAILURE = 10 [(metadata) = { exit_code: 1 }]; MODULE_EXPANSION_MISSING_DATA = 11 [(metadata) = { exit_code: 1 }]; + DDI_FILE_READ_FAILURE = 12 [(metadata) = { exit_code: 36 }]; + MODULES_INFO_FILE_READ_FAILURE = 13 [(metadata) = { exit_code: 36 }]; + MODMAP_INPUT_FILE_READ_FAILURE = 14 [(metadata) = { exit_code: 36 }]; } Code code = 1; diff --git a/src/main/starlark/builtins_bzl/common/cc/action_names.bzl b/src/main/starlark/builtins_bzl/common/cc/action_names.bzl index 620ac7ffd7becb..c66383459ffde3 100644 --- a/src/main/starlark/builtins_bzl/common/cc/action_names.bzl +++ b/src/main/starlark/builtins_bzl/common/cc/action_names.bzl @@ -35,6 +35,13 @@ CPP_MODULE_CODEGEN_ACTION_NAME = "c++-module-codegen" # Name of the C++ header parsing action. CPP_HEADER_PARSING_ACTION_NAME = "c++-header-parsing" +# Name of the C++ deps scanning action. +CPP20_DEPS_SCANNING_ACTION_NAME = "c++20-deps-scanning" + +# Name of the C++20 module compile action. +CPP20_MODULE_COMPILE_ACTION_NAME = "c++20-module-compile" +CPP20_MODULE_CODEGEN_ACTION_NAME = "c++20-module-codegen" + # Name of the C++ module compile action. CPP_MODULE_COMPILE_ACTION_NAME = "c++-module-compile" @@ -102,6 +109,9 @@ ACTION_NAMES = struct( cc_flags_make_variable = CC_FLAGS_MAKE_VARIABLE_ACTION_NAME, cpp_module_codegen = CPP_MODULE_CODEGEN_ACTION_NAME, cpp_header_parsing = CPP_HEADER_PARSING_ACTION_NAME, + cpp20_deps_scanning = CPP20_DEPS_SCANNING_ACTION_NAME, + cpp20_module_compile = CPP20_MODULE_COMPILE_ACTION_NAME, + cpp20_module_codegen = CPP20_MODULE_CODEGEN_ACTION_NAME, cpp_module_compile = CPP_MODULE_COMPILE_ACTION_NAME, assemble = ASSEMBLE_ACTION_NAME, preprocess_assemble = PREPROCESS_ASSEMBLE_ACTION_NAME, diff --git a/src/main/starlark/builtins_bzl/common/cc/attrs.bzl b/src/main/starlark/builtins_bzl/common/cc/attrs.bzl index c7e37177d0840a..30044ebbbe614c 100644 --- a/src/main/starlark/builtins_bzl/common/cc/attrs.bzl +++ b/src/main/starlark/builtins_bzl/common/cc/attrs.bzl @@ -84,6 +84,22 @@ using the C/C++ compiler.

""", ), + "module_interfaces": attr.label_list( + allow_files = True, + flags = ["DIRECT_COMPILE_TIME_INPUT"], + doc = """ +The list of files are regarded as C++20 Modules Interface. + +

+C++ Standard has no restriction about module interface file extension +

    +
  • Clang use cppm
  • +
  • GCC can use any source file extension
  • +
  • MSVC use ixx
  • +
+

+ """, + ), "data": attr.label_list( allow_files = True, flags = ["SKIP_CONSTRAINTS_OVERRIDE"], diff --git a/src/main/starlark/builtins_bzl/common/cc/cc_binary.bzl b/src/main/starlark/builtins_bzl/common/cc/cc_binary.bzl index 743c70834658b3..eebeb62cd4e1a1 100644 --- a/src/main/starlark/builtins_bzl/common/cc/cc_binary.bzl +++ b/src/main/starlark/builtins_bzl/common/cc/cc_binary.bzl @@ -486,6 +486,9 @@ def cc_binary_impl(ctx, additional_linkopts, force_linkstatic = False): requested_features = features, unsupported_features = disabled_features, ) + + cc_helper.check_cpp20_module(ctx, feature_configuration) + all_deps = ctx.attr.deps + semantics.get_cc_runtimes(ctx, _is_link_shared(ctx)) compilation_context_deps = [dep[CcInfo].compilation_context for dep in all_deps if CcInfo in dep] @@ -507,6 +510,7 @@ def cc_binary_impl(ctx, additional_linkopts, force_linkstatic = False): public_hdrs = cc_helper.get_public_hdrs(ctx), copts_filter = cc_helper.copts_filter(ctx, additional_make_variable_substitutions), srcs = cc_helper.get_srcs(ctx), + module_interfaces = cc_helper.get_module_interfaces(ctx), compilation_contexts = compilation_context_deps, code_coverage_enabled = cc_helper.is_code_coverage_enabled(ctx = ctx), ) diff --git a/src/main/starlark/builtins_bzl/common/cc/cc_common.bzl b/src/main/starlark/builtins_bzl/common/cc/cc_common.bzl index bdd40fa01303db..5be38f6e077c01 100644 --- a/src/main/starlark/builtins_bzl/common/cc/cc_common.bzl +++ b/src/main/starlark/builtins_bzl/common/cc/cc_common.bzl @@ -657,6 +657,7 @@ def _compile( cc_toolchain, name, srcs = [], + module_interfaces = [], public_hdrs = [], private_hdrs = [], textual_hdrs = [], @@ -727,7 +728,7 @@ def _compile( if non_compilation_additional_inputs == _UNBOUND: non_compilation_additional_inputs = [] - has_tuple = _check_all_sources_contain_tuples_or_none_of_them([srcs, private_hdrs, public_hdrs]) + has_tuple = _check_all_sources_contain_tuples_or_none_of_them([srcs, module_interfaces, private_hdrs, public_hdrs]) if has_tuple: cc_common_internal.check_private_api(allowlist = _PRIVATE_STARLARKIFICATION_ALLOWLIST) @@ -737,6 +738,7 @@ def _compile( cc_toolchain = cc_toolchain, name = name, srcs = srcs, + module_interfaces = module_interfaces, public_hdrs = public_hdrs, private_hdrs = private_hdrs, textual_hdrs = textual_hdrs, diff --git a/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl b/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl index 78a47e931191dc..26423e44992938 100644 --- a/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl +++ b/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl @@ -944,16 +944,11 @@ def _map_to_list(m): result.append((k, v)) return result -# Returns a list of (Artifact, Label) tuples. Each tuple represents an input source -# file and the label of the rule that generates it (or the label of the source file itself if it -# is an input file). -def _get_srcs(ctx): - if not hasattr(ctx.attr, "srcs"): - return [] - # "srcs" attribute is a LABEL_LIST in cc_rules, which might also contain files. +# "srcs" attribute is a LABEL_LIST in cc_rules, which might also contain files. +def _calculate_artifact_label_map(srcs, attr): artifact_label_map = {} - for src in ctx.attr.srcs: + for src in srcs: if DefaultInfo in src: for artifact in src[DefaultInfo].files.to_list(): if "." + artifact.extension not in CC_HEADER: @@ -962,8 +957,43 @@ def _get_srcs(ctx): if old_label != None and not _are_labels_equal(old_label, src.label) and "." + artifact.extension in CC_AND_OBJC: fail( "Artifact '{}' is duplicated (through '{}' and '{}')".format(artifact, old_label, src), - attr = "srcs", + attr = attr, ) + return artifact_label_map +# Returns a list of (Artifact, Label) tuples. Each tuple represents an input source +# file and the label of the rule that generates it (or the label of the source file itself if it +# is an input file). +def _get_srcs(ctx): + if not hasattr(ctx.attr, "srcs"): + return [] + artifact_label_map = _calculate_artifact_label_map(ctx.attr.srcs, "srcs") + return _map_to_list(artifact_label_map) + +# Returns a list of (Artifact, Label) tuples. Each tuple represents an input source +# file and the label of the rule that generates it (or the label of the source file itself if it +# is an input file). +def _get_module_interfaces(ctx): + if not hasattr(ctx.attr, "module_interfaces"): + return [] + srcs_artifact_label_map = _calculate_artifact_label_map(ctx.attr.srcs, "srcs") + + artifact_label_map = {} + for src in ctx.attr.module_interfaces: + if DefaultInfo in src: + for artifact in src[DefaultInfo].files.to_list(): + old_label = artifact_label_map.get(artifact, None) + artifact_label_map[artifact] = src.label + if old_label != None and not _are_labels_equal(old_label, src.label): + fail( + "Artifact '{}' is duplicated (through '{}' and '{}')".format(artifact.path, old_label, src), + attr = "module_interfaces", + ) + label_from_srcs = srcs_artifact_label_map.get(artifact, None) + if label_from_srcs != None: + fail( + "Artifact '{}' is duplicated (through '{}' and '{}')".format(artifact.path, label_from_srcs, src), + attr = "module_interfaces", + ) return _map_to_list(artifact_label_map) # Returns a list of (Artifact, Label) tuples. Each tuple represents an input source @@ -1175,6 +1205,12 @@ def _should_use_pic(ctx, cc_toolchain, feature_configuration): cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "prefer_pic_for_opt_binaries") ) ) +def _check_cpp20_module(ctx, feature_configuration): + if len(ctx.files.module_interfaces) > 0 and not cc_common.is_enabled( + feature_configuration = feature_configuration, + feature_name = "cpp20_module", + ): + fail("to use C++20 Modules, the feature cpp20_module must be enabled") cc_helper = struct( CPP_TOOLCHAIN_TYPE = _CPP_TOOLCHAIN_TYPE, @@ -1225,6 +1261,7 @@ cc_helper = struct( get_local_defines_for_runfiles_lookup = _get_local_defines_for_runfiles_lookup, are_labels_equal = _are_labels_equal, get_srcs = _get_srcs, + get_module_interfaces = _get_module_interfaces, get_private_hdrs = _get_private_hdrs, get_public_hdrs = _get_public_hdrs, report_invalid_options = _report_invalid_options, @@ -1242,4 +1279,5 @@ cc_helper = struct( package_source_root = _package_source_root, tokenize = _tokenize, should_use_pic = _should_use_pic, + check_cpp20_module = _check_cpp20_module, ) diff --git a/src/main/starlark/builtins_bzl/common/cc/cc_library.bzl b/src/main/starlark/builtins_bzl/common/cc/cc_library.bzl index 7295719955e6dd..1ded2172d05f97 100755 --- a/src/main/starlark/builtins_bzl/common/cc/cc_library.bzl +++ b/src/main/starlark/builtins_bzl/common/cc/cc_library.bzl @@ -37,6 +37,8 @@ def _cc_library_impl(ctx): unsupported_features = ctx.disabled_features, ) + cc_helper.check_cpp20_module(ctx, feature_configuration) + precompiled_files = cc_helper.build_precompiled_files(ctx = ctx) semantics.validate_attributes(ctx = ctx) @@ -63,6 +65,7 @@ def _cc_library_impl(ctx): copts_filter = cc_helper.copts_filter(ctx, additional_make_variable_substitutions), purpose = "cc_library-compile", srcs = cc_helper.get_srcs(ctx), + module_interfaces = cc_helper.get_module_interfaces(ctx), private_hdrs = cc_helper.get_private_hdrs(ctx), public_hdrs = cc_helper.get_public_hdrs(ctx), code_coverage_enabled = cc_helper.is_code_coverage_enabled(ctx), diff --git a/src/test/java/com/google/devtools/build/lib/analysis/mock/BUILD b/src/test/java/com/google/devtools/build/lib/analysis/mock/BUILD index bf38cae77bacdb..2a886b4a63220d 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/mock/BUILD +++ b/src/test/java/com/google/devtools/build/lib/analysis/mock/BUILD @@ -25,6 +25,7 @@ java_library( ], resources = [ "cc_toolchain_config.bzl", + "action_names.bzl", "//tools/build_defs/cc:action_names.bzl", "//tools/cpp:cc_toolchain_config_lib.bzl", ], diff --git a/src/test/java/com/google/devtools/build/lib/analysis/mock/action_names.bzl b/src/test/java/com/google/devtools/build/lib/analysis/mock/action_names.bzl new file mode 100644 index 00000000000000..aa5e4828cc4aab --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/analysis/mock/action_names.bzl @@ -0,0 +1,131 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Constants for action names used for C++ rules.""" + +# Name for the C compilation action. +C_COMPILE_ACTION_NAME = "c-compile" + +# Name of the C++ compilation action. +CPP_COMPILE_ACTION_NAME = "c++-compile" + +# Name of the linkstamp-compile action. +LINKSTAMP_COMPILE_ACTION_NAME = "linkstamp-compile" + +# Name of the action used to compute CC_FLAGS make variable. +CC_FLAGS_MAKE_VARIABLE_ACTION_NAME = "cc-flags-make-variable" + +# Name of the C++ module codegen action. +CPP_MODULE_CODEGEN_ACTION_NAME = "c++-module-codegen" + +# Name of the C++ header parsing action. +CPP_HEADER_PARSING_ACTION_NAME = "c++-header-parsing" + +# Name of the C++ deps scanning action. +CPP20_DEPS_SCANNING_ACTION_NAME = "c++20-deps-scanning" + +# Name of the C++ module compile action. +CPP20_MODULE_COMPILE_ACTION_NAME = "c++20-module-compile" +CPP20_MODULE_CODEGEN_ACTION_NAME = "c++20-module-codegen" + +# Name of the C++ module compile action. +CPP_MODULE_COMPILE_ACTION_NAME = "c++-module-compile" + +# Name of the assembler action. +ASSEMBLE_ACTION_NAME = "assemble" + +# Name of the assembly preprocessing action. +PREPROCESS_ASSEMBLE_ACTION_NAME = "preprocess-assemble" + +LLVM_COV = "llvm-cov" + +# Name of the action producing ThinLto index. +LTO_INDEXING_ACTION_NAME = "lto-indexing" + +# Name of the action producing ThinLto index for executable. +LTO_INDEX_FOR_EXECUTABLE_ACTION_NAME = "lto-index-for-executable" + +# Name of the action producing ThinLto index for dynamic library. +LTO_INDEX_FOR_DYNAMIC_LIBRARY_ACTION_NAME = "lto-index-for-dynamic-library" + +# Name of the action producing ThinLto index for nodeps dynamic library. +LTO_INDEX_FOR_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME = "lto-index-for-nodeps-dynamic-library" + +# Name of the action compiling lto bitcodes into native objects. +LTO_BACKEND_ACTION_NAME = "lto-backend" + +# Name of the link action producing executable binary. +CPP_LINK_EXECUTABLE_ACTION_NAME = "c++-link-executable" + +# Name of the link action producing dynamic library. +CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME = "c++-link-dynamic-library" + +# Name of the link action producing dynamic library that doesn't include it's +# transitive dependencies. +CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME = "c++-link-nodeps-dynamic-library" + +# Name of the archiving action producing static library. +CPP_LINK_STATIC_LIBRARY_ACTION_NAME = "c++-link-static-library" + +# Name of the action stripping the binary. +STRIP_ACTION_NAME = "strip" + +# A string constant for the objc compilation action. +OBJC_COMPILE_ACTION_NAME = "objc-compile" + +# A string constant for the objc++ compile action. +OBJCPP_COMPILE_ACTION_NAME = "objc++-compile" + +# A string constant for the objc executable link action. +OBJC_EXECUTABLE_ACTION_NAME = "objc-executable" + +# A string constant for the objc fully-link link action. +OBJC_FULLY_LINK_ACTION_NAME = "objc-fully-link" + +# A string constant for the clif actions. +CLIF_MATCH_ACTION_NAME = "clif-match" + +# A string constant for the obj copy actions. +OBJ_COPY_ACTION_NAME = "objcopy_embed_data" + +ACTION_NAMES = struct( + c_compile = C_COMPILE_ACTION_NAME, + cpp_compile = CPP_COMPILE_ACTION_NAME, + linkstamp_compile = LINKSTAMP_COMPILE_ACTION_NAME, + cc_flags_make_variable = CC_FLAGS_MAKE_VARIABLE_ACTION_NAME, + cpp_module_codegen = CPP_MODULE_CODEGEN_ACTION_NAME, + cpp_header_parsing = CPP_HEADER_PARSING_ACTION_NAME, + cpp20_deps_scanning = CPP20_DEPS_SCANNING_ACTION_NAME, + cpp20_module_compile = CPP20_MODULE_COMPILE_ACTION_NAME, + cpp20_module_codegen = CPP20_MODULE_CODEGEN_ACTION_NAME, + cpp_module_compile = CPP_MODULE_COMPILE_ACTION_NAME, + assemble = ASSEMBLE_ACTION_NAME, + preprocess_assemble = PREPROCESS_ASSEMBLE_ACTION_NAME, + llvm_cov = LLVM_COV, + lto_indexing = LTO_INDEXING_ACTION_NAME, + lto_backend = LTO_BACKEND_ACTION_NAME, + lto_index_for_executable = LTO_INDEX_FOR_EXECUTABLE_ACTION_NAME, + lto_index_for_dynamic_library = LTO_INDEX_FOR_DYNAMIC_LIBRARY_ACTION_NAME, + lto_index_for_nodeps_dynamic_library = LTO_INDEX_FOR_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME, + cpp_link_executable = CPP_LINK_EXECUTABLE_ACTION_NAME, + cpp_link_dynamic_library = CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME, + cpp_link_nodeps_dynamic_library = CPP_LINK_NODEPS_DYNAMIC_LIBRARY_ACTION_NAME, + cpp_link_static_library = CPP_LINK_STATIC_LIBRARY_ACTION_NAME, + strip = STRIP_ACTION_NAME, + objc_compile = OBJC_COMPILE_ACTION_NAME, + objc_executable = OBJC_EXECUTABLE_ACTION_NAME, + objc_fully_link = OBJC_FULLY_LINK_ACTION_NAME, + objcpp_compile = OBJCPP_COMPILE_ACTION_NAME, + clif_match = CLIF_MATCH_ACTION_NAME, + objcopy_embed_data = OBJ_COPY_ACTION_NAME, +) diff --git a/src/test/java/com/google/devtools/build/lib/analysis/mock/cc_toolchain_config.bzl b/src/test/java/com/google/devtools/build/lib/analysis/mock/cc_toolchain_config.bzl index 7e831624f186d7..1e0689e8fdf7f8 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/mock/cc_toolchain_config.bzl +++ b/src/test/java/com/google/devtools/build/lib/analysis/mock/cc_toolchain_config.bzl @@ -32,6 +32,7 @@ load( ) _FEATURE_NAMES = struct( + cpp20_module = "cpp20_module", generate_pdb_file = "generate_pdb_file", no_legacy_features = "no_legacy_features", do_not_split_linking_cmdline = "do_not_split_linking_cmdline", @@ -120,6 +121,34 @@ _FEATURE_NAMES = struct( generate_linkmap = "generate_linkmap", ) + # Tell bazel we support C++20 Modules now +_cpp20_module = feature( + name = "cpp20_module", + # set default value to False + # to enable the feature + # use --features=cpp20_module + # or add cpp20_module to features attr + enabled = False, + ) +_cpp20_modmap_file_feature = feature( + name = "cpp20_modmap_file", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp20_module_compile, + ACTION_NAMES.cpp20_module_codegen, + ], + flag_groups = [ + flag_group( + flags = ["@%{cpp20_modmap_file}"], + expand_if_available = "cpp20_modmap_file", + ), + ], + ), + ], + enabled = True, + ) _no_copts_tokenization_feature = feature(name = _FEATURE_NAMES.no_copts_tokenization) _disable_pbh_feature = feature(name = _FEATURE_NAMES.disable_pbh) @@ -147,6 +176,7 @@ _define_with_space = feature( ACTION_NAMES.cpp_compile, ACTION_NAMES.linkstamp_compile, ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp20_deps_scanning, ACTION_NAMES.cpp_module_compile, ACTION_NAMES.cpp_module_codegen, ACTION_NAMES.clif_match, @@ -457,6 +487,7 @@ _user_compile_flags_feature = feature( ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile, ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp20_deps_scanning, ACTION_NAMES.cpp_module_compile, ACTION_NAMES.cpp_module_codegen, ACTION_NAMES.lto_backend, @@ -1330,6 +1361,7 @@ _generate_linkmap_feature = feature( ) _feature_name_to_feature = { + _FEATURE_NAMES.cpp20_module: _cpp20_module, _FEATURE_NAMES.no_legacy_features: _no_legacy_features_feature, _FEATURE_NAMES.do_not_split_linking_cmdline: _do_not_split_linking_cmdline_feature, _FEATURE_NAMES.supports_dynamic_linker: _supports_dynamic_linker_feature, @@ -1511,6 +1543,7 @@ def _impl(ctx): ACTION_NAMES.linkstamp_compile, ACTION_NAMES.cpp_compile, ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp20_deps_scanning, ACTION_NAMES.cpp_module_compile, ACTION_NAMES.cpp_module_codegen, ACTION_NAMES.lto_backend, @@ -1592,6 +1625,10 @@ def _impl(ctx): name = "llvm-profdata", path = "/usr/bin/mock-llvm-profdata", ), + tool_path( + name = "deps-scanner", + path = "/usr/bin/mock-deps-scanner" + ) ] else: tool_paths = [_get_tool_path(name, path) for name, path in ctx.attr.tool_paths.items()] diff --git a/src/test/java/com/google/devtools/build/lib/bazel/rules/android/AndroidNdkRepositoryTest.java b/src/test/java/com/google/devtools/build/lib/bazel/rules/android/AndroidNdkRepositoryTest.java index b4eab52c34bb5e..29e31a4fbd7e27 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/rules/android/AndroidNdkRepositoryTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/rules/android/AndroidNdkRepositoryTest.java @@ -61,11 +61,6 @@ public void setup() throws Exception { CcToolchainConfig.builder() .withToolchainTargetConstraints("@platforms//os:android", "@platforms//cpu:armv7")); scratch.overwriteFile("embedded_tools/tools/build_defs/cc/BUILD"); - scratch.overwriteFile( - "embedded_tools/tools/build_defs/cc/action_names.bzl", - ResourceLoader.readFromResources( - TestConstants.RULES_CC_REPOSITORY_EXECROOT + "cc/action_names.bzl")); - scratch.overwriteFile( "embedded_tools/tools/cpp/cc_toolchain_config_lib.bzl", ResourceLoader.readFromResources( diff --git a/src/test/java/com/google/devtools/build/lib/bazel/rules/android/ndkcrosstools/AndroidNdkCrosstoolsTest.java b/src/test/java/com/google/devtools/build/lib/bazel/rules/android/ndkcrosstools/AndroidNdkCrosstoolsTest.java index dd3f6009f7d912..e5f3ddc7978fb5 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/rules/android/ndkcrosstools/AndroidNdkCrosstoolsTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/rules/android/ndkcrosstools/AndroidNdkCrosstoolsTest.java @@ -163,7 +163,8 @@ public void testPathsExist() throws Exception { // TODO(tmsriram): Not all crosstools contain llvm-profdata tool yet, remove // the check once llvm-profdata becomes always available. if (toolpath.getPath().contains("llvm-profdata") - || toolpath.getPath().contains("llvm-cov")) { + || toolpath.getPath().contains("llvm-cov") + || toolpath.getPath().contains("deps-scanner")) { continue; } assertThat(ndkFiles).contains(toolpath.getPath()); diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockAndroidSupport.java b/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockAndroidSupport.java index 619aa4ff8e6d85..622c88ee9cfd7c 100644 --- a/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockAndroidSupport.java +++ b/src/test/java/com/google/devtools/build/lib/packages/util/BazelMockAndroidSupport.java @@ -58,6 +58,7 @@ public static CcToolchainConfig.Builder x86Config() { Pair.of("objcopy", "x86/bin/i686-linux-android-objcopy"), Pair.of("objdump", "x86/bin/i686-linux-android-objdump"), Pair.of("strip", "x86/bin/i686-linux-android-strip"), + Pair.of("deps-scanner", "x86/bin/i686-linux-android-deps-scanner"), Pair.of("ld-bfd", "x86/bin/i686-linux-android-ld.bfd"), Pair.of("ld-gold", "x86/bin/i686-linux-android-ld.gold")) .withToolchainTargetConstraints( @@ -86,6 +87,7 @@ public static CcToolchainConfig.Builder armeabiV7a() { Pair.of("objcopy", "arm/bin/arm-linux-androideabi-objcopy"), Pair.of("objdump", "arm/bin/arm-linux-androideabi-objdump"), Pair.of("strip", "arm/bin/arm-linux-androideabi-strip"), + Pair.of("deps-scanner", "arm/bin/arm-linux-androideabi-deps-scanner"), Pair.of("ld-bfd", "arm/bin/arm-linux-androideabi-ld.bfd"), Pair.of("ld-gold", "arm/bin/arm-linux-androideabi-ld.gold")) .withToolchainTargetConstraints( diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/MockCcSupport.java b/src/test/java/com/google/devtools/build/lib/packages/util/MockCcSupport.java index dd8361e4207934..0cf5bbaa0201ee 100644 --- a/src/test/java/com/google/devtools/build/lib/packages/util/MockCcSupport.java +++ b/src/test/java/com/google/devtools/build/lib/packages/util/MockCcSupport.java @@ -271,10 +271,19 @@ protected void setupRulesCc(MockToolsConfig config) throws IOException { TestConstants.TOOLS_REPOSITORY_SCRATCH + "tools/cpp/cc_toolchain_config_lib.bzl", ResourceLoader.readFromResources( TestConstants.RULES_CC_REPOSITORY_EXECROOT + "cc/cc_toolchain_config_lib.bzl")); + // to support C++20 Modules + // many action names have been added + // However, the rules_cc is not update now, resulting test failed. + // e.g. Error: 'struct' value has no field or method 'cpp20_deps_scanning' + // to fix the error, another action_names.bzl is created for testing + // Attention: keep the file content same as + // src/main/starlark/builtins_bzl/common/cc/action_names.bzl config.overwrite( TestConstants.TOOLS_REPOSITORY_SCRATCH + "tools/build_defs/cc/action_names.bzl", + readCcActionNamesFile() + ); ResourceLoader.readFromResources( - TestConstants.RULES_CC_REPOSITORY_EXECROOT + "cc/action_names.bzl")); + TestConstants.RULES_CC_REPOSITORY_EXECROOT + "cc/action_names.bzl"); config.create( TestConstants.RULES_CC_REPOSITORY_EXECROOT + "BUILD", "genrule(name='license', cmd='exit 0', outs=['dummy_license'])"); @@ -341,6 +350,11 @@ protected String readCcToolchainConfigFile() throws IOException { "com/google/devtools/build/lib/analysis/mock/cc_toolchain_config.bzl"); } + protected String readCcActionNamesFile() throws IOException { + return ResourceLoader.readFromResources( + "com/google/devtools/build/lib/analysis/mock/action_names.bzl"); + } + public abstract Label getMockCrosstoolLabel(); protected abstract ImmutableList getCrosstoolArchs(); diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/mock/osx_cc_toolchain_config.bzl b/src/test/java/com/google/devtools/build/lib/packages/util/mock/osx_cc_toolchain_config.bzl index d102d5a2606e0b..8ddc35f5eca7ec 100644 --- a/src/test/java/com/google/devtools/build/lib/packages/util/mock/osx_cc_toolchain_config.bzl +++ b/src/test/java/com/google/devtools/build/lib/packages/util/mock/osx_cc_toolchain_config.bzl @@ -7277,6 +7277,7 @@ def _impl(ctx): tool_path(name = "strip", path = "/strip"), tool_path(name = "dwp", path = "/usr/bin/dwp"), tool_path(name = "objdump", path = "/usr/bin/objdump"), + tool_path(name = "deps-scanner", path = "/usr/bin/deps-scanner"), ] elif (ctx.attr.cpu == "ios_arm64" or ctx.attr.cpu == "ios_armv7"): @@ -7290,6 +7291,7 @@ def _impl(ctx): tool_path(name = "strip", path = "ios/strip"), tool_path(name = "dwp", path = "/usr/bin/dwp"), tool_path(name = "objdump", path = "/usr/bin/objdump"), + tool_path(name = "deps-scanner", path = "/usr/bin/deps-scanner"), ] elif (ctx.attr.cpu == "ios_i386" or ctx.attr.cpu == "ios_x86_64"): @@ -7303,6 +7305,7 @@ def _impl(ctx): tool_path(name = "strip", path = "iossim/strip"), tool_path(name = "dwp", path = "/usr/bin/dwp"), tool_path(name = "objdump", path = "/usr/bin/objdump"), + tool_path(name = "deps-scanner", path = "/usr/bin/deps-scanner"), ] elif (ctx.attr.cpu == "darwin_x86_64"): tool_paths = [ @@ -7315,6 +7318,7 @@ def _impl(ctx): tool_path(name = "strip", path = "mac/strip"), tool_path(name = "dwp", path = "/usr/bin/dwp"), tool_path(name = "objdump", path = "/usr/bin/objdump"), + tool_path(name = "deps-scanner", path = "/usr/bin/deps-scanner"), ] elif (ctx.attr.cpu == "tvos_arm64"): tool_paths = [ @@ -7327,6 +7331,7 @@ def _impl(ctx): tool_path(name = "strip", path = "tvos/strip"), tool_path(name = "dwp", path = "/usr/bin/dwp"), tool_path(name = "objdump", path = "/usr/bin/objdump"), + tool_path(name = "deps-scanner", path = "/usr/bin/deps-scanner"), ] elif (ctx.attr.cpu == "tvos_x86_64"): tool_paths = [ @@ -7339,6 +7344,7 @@ def _impl(ctx): tool_path(name = "strip", path = "tvsim/strip"), tool_path(name = "dwp", path = "/usr/bin/dwp"), tool_path(name = "objdump", path = "/usr/bin/objdump"), + tool_path(name = "deps-scanner", path = "/usr/bin/deps-scanner"), ] elif (ctx.attr.cpu == "watchos_armv7k" or ctx.attr.cpu == "watchos_arm64_32"): @@ -7352,6 +7358,7 @@ def _impl(ctx): tool_path(name = "strip", path = "watchos/strip"), tool_path(name = "dwp", path = "/usr/bin/dwp"), tool_path(name = "objdump", path = "/usr/bin/objdump"), + tool_path(name = "deps-scanner", path = "/usr/bin/deps-scanner"), ] elif (ctx.attr.cpu == "watchos_i386" or ctx.attr.cpu == "watchos_x86_64"): @@ -7365,6 +7372,7 @@ def _impl(ctx): tool_path(name = "strip", path = "watchsim/strip"), tool_path(name = "dwp", path = "/usr/bin/dwp"), tool_path(name = "objdump", path = "/usr/bin/objdump"), + tool_path(name = "deps-scanner", path = "/usr/bin/deps-scanner"), ] else: tool_paths = [] diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD b/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD index a4eece61b81b1e..9ea9c7d75777c3 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD @@ -742,3 +742,19 @@ java_test( "//third_party:truth", ], ) + +java_test( + name = "Cpp20ModuleCompileTest", + srcs = ["Cpp20ModuleCompileTest.java"], + deps = [ + "//src/main/java/com/google/devtools/build/lib/actions:artifacts", + "//src/main/java/com/google/devtools/build/lib/analysis:analysis_cluster", + "//src/main/java/com/google/devtools/build/lib/analysis:configured_target", + "//src/main/java/com/google/devtools/build/lib/rules/cpp", + "//src/test/java/com/google/devtools/build/lib/actions/util", + "//src/test/java/com/google/devtools/build/lib/analysis/util", + "//src/test/java/com/google/devtools/build/lib/packages:testutil", + "//third_party:junit4", + "@io_bazel//third_party:truth", + ], +) diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProviderTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProviderTest.java index edbd830b55df26..983bd9823e6796 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProviderTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainProviderTest.java @@ -169,6 +169,7 @@ def _impl(ctx): tool_path(name = "objdump", path = "some/objdump"), tool_path(name = "strip", path = "some/strip"), tool_path(name = "dwp", path = "some/dwp"), + tool_path(name = "deps-scanner", path = "some/deps-scanner"), ], cc_target_os = "os", builtin_sysroot = "sysroot", @@ -249,7 +250,8 @@ public void testGcovToolNotDefined() throws Exception { Pair.of("ld", "ld"), Pair.of("nm", "nm"), Pair.of("objdump", "objdump"), - Pair.of("strip", "strip")) + Pair.of("strip", "strip"), + Pair.of("deps-scanner", "deps-scanner")) .build() .getCcToolchainConfigRule()); analysisMock.ccSupport().setupCcToolchainConfig(mockToolsConfig, CcToolchainConfig.builder()); @@ -293,7 +295,8 @@ public void testGcovToolDefined() throws Exception { Pair.of("ld", "ld"), Pair.of("nm", "nm"), Pair.of("objdump", "objdump"), - Pair.of("strip", "strip")) + Pair.of("strip", "strip"), + Pair.of("deps-scanner", "deps-scanner")) .build() .getCcToolchainConfigRule()); analysisMock.ccSupport().setupCcToolchainConfig(mockToolsConfig, CcToolchainConfig.builder()); @@ -322,7 +325,8 @@ public void testGcovNotDefined() throws Exception { Pair.of("ld", "ld"), Pair.of("nm", "nm"), Pair.of("objdump", "objdump"), - Pair.of("strip", "strip")); + Pair.of("strip", "strip"), + Pair.of("deps-scanner", "deps-scanner")); scratch.file( "a/BUILD", "load(':cc_toolchain_config.bzl', 'cc_toolchain_config')", @@ -412,7 +416,8 @@ public void testLlvmCoverageToolsDefined() throws Exception { Pair.of("llvm-profdata", "path-to-llvm-profdata"), Pair.of("nm", "nm"), Pair.of("objdump", "objdump"), - Pair.of("strip", "strip")); + Pair.of("strip", "strip"), + Pair.of("deps-scanner", "deps-scanner")); scratch.file( "a/BUILD", "load(':cc_toolchain_config.bzl', 'cc_toolchain_config')", @@ -467,7 +472,8 @@ public void testLlvmCoverageToolsNotDefined() throws Exception { Pair.of("ld", "ld"), Pair.of("nm", "nm"), Pair.of("objdump", "objdump"), - Pair.of("strip", "strip")); + Pair.of("strip", "strip"), + Pair.of("deps-scanner", "deps-scanner")); scratch.file( "a/BUILD", "load(':cc_toolchain_config.bzl', 'cc_toolchain_config')", @@ -575,8 +581,9 @@ public void testConfigWithMissingToolDefs() throws Exception { Pair.of("gcov", "gcov"), Pair.of("ld", "ld"), Pair.of("nm", "nm"), - Pair.of("objdump", "objdump") + Pair.of("objdump", "objdump"), // Pair.of("strip", "strip") + Pair.of("deps-scanner", "deps-scanner") ) .build() .getCcToolchainConfigRule()); diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainTest.java index 8af3d00ddb912a..899ed0020157da 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainTest.java @@ -742,6 +742,7 @@ def _impl(ctx): tool_path(name = "strip", path = "/some/path"), tool_path(name = "dwp", path = "/some/path"), tool_path(name = "llvm_profdata", path = "/some/path"), + tool_path(name = "deps-scanner", path = "/some/path"), ], cc_target_os = "os", builtin_sysroot = "sysroot", diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/Cpp20ModuleCompileTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/Cpp20ModuleCompileTest.java new file mode 100644 index 00000000000000..b80043a985fea0 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/Cpp20ModuleCompileTest.java @@ -0,0 +1,158 @@ +package com.google.devtools.build.lib.rules.cpp; +import static com.google.common.truth.Truth.assertThat; +import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.baseArtifactNames; + +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.lib.analysis.OutputGroupInfo; +import com.google.devtools.build.lib.analysis.actions.SpawnAction; +import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; +import com.google.devtools.build.lib.packages.util.Crosstool; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class Cpp20ModuleCompileTest extends BuildViewTestCase { + private void enableCpp20Module() throws Exception { + getAnalysisMock().ccSupport().setupCcToolchainConfig( + mockToolsConfig, Crosstool.CcToolchainConfig.builder().withFeatures( + CppRuleClasses.CPP20_MODULE)); + } + // if we use module_interfaces + // we need to enable cpp20_module feature + @Test + public void testCpp20ModuleFeatureNotEnabled1() throws Exception { + reporter.removeHandler(failFastHandler); + scratch.file("foo/BUILD", + "cc_library(name='foo', module_interfaces=['foo.cppm'])"); + scratch.file("foo/foo.cppm", "foo"); + getConfiguredTarget("//foo:foo"); + assertContainsEvent( + "to use C++20 Modules, the feature cpp20_module must be enabled"); + } + + // when use C++20 Modules, the compile model changed + // to keep backward-compatibility + // the feature cpp20_module is not enabled by default + // we need to enable the feature in rule attr features + @Test + public void testCpp20ModuleFeatureNotEnabled2() throws Exception { + enableCpp20Module(); + reporter.removeHandler(failFastHandler); + scratch.file("foo/BUILD", + "cc_library(name='foo', module_interfaces=['foo.cppm'])"); + scratch.file("foo/foo.cppm", "foo"); + getConfiguredTarget("//foo:foo"); + assertContainsEvent( + "to use C++20 Modules, the feature cpp20_module must be enabled"); + } + + // add the feature cpp20_module to toolchain + // add the feature cpp20_module to rule attr features + // now we can play with C++20 Modules + @Test + public void testCpp20ModuleFeatureEnabled() throws Exception { + enableCpp20Module(); + reporter.removeHandler(failFastHandler); + scratch.file( + "foo/BUILD", + "cc_library(name='foo', module_interfaces=['foo.cppm'], features=['cpp20_module'])"); + scratch.file("foo/foo.cppm", "foo"); + getConfiguredTarget("//foo:foo"); + assertDoesNotContainEvent( + "to use C++20 Modules, the feature cpp20_module must be enabled"); + } + + // module_interfaces cannot have two same .cc files + @Test + public void testSameCcFileTwice1() throws Exception { + enableCpp20Module(); + scratch.file( + "a/BUILD", + "cc_library(name='a', module_interfaces=['a1', 'a2'], features=['cpp20_module'])", + "filegroup(name='a1', srcs=['a.cc'])", + "filegroup(name='a2', srcs=['a.cc'])"); + reporter.removeHandler(failFastHandler); + getConfiguredTarget("//a:a"); + assertContainsEvent("Artifact 'a/a.cc' is duplicated"); + } + + // if one file has already in srcs, + // it can not put to module_interfaces + // due to module_interfaces is a special srcs that produce bmi files + @Test + public void testSameCcFileTwice2() throws Exception { + enableCpp20Module(); + scratch.file( + "a/BUILD", + "cc_library(name='a', srcs=['a1'], module_interfaces=['a2'], features=['cpp20_module'])", + "filegroup(name='a1', srcs=['a.cc'])", + "filegroup(name='a2', srcs=['a.cc'])"); + reporter.removeHandler(failFastHandler); + getConfiguredTarget("//a:a"); + assertContainsEvent("Artifact 'a/a.cc' is duplicated"); + } + @Test + public void testObjectFile() throws Exception { + enableCpp20Module(); + useConfiguration("--cpu=k8"); + ConfiguredTarget archiveInSrcsTest = scratchConfiguredTarget( + "cc_in_module_interfaces", "cc_in_module_interfaces_test", + "cc_test(name = 'cc_in_module_interfaces_test',", + " module_interfaces = ['foo.cc'],", + " features = ['cpp20_module'],", ")"); + List artifactNames = + baseArtifactNames(getLinkerInputs(archiveInSrcsTest)); + assertThat(artifactNames).contains("foo.o"); + } + + private Iterable getLinkerInputs(ConfiguredTarget target) { + Artifact executable = getExecutable(target); + SpawnAction linkAction = (SpawnAction)getGeneratingAction(executable); + return linkAction.getInputs().toList(); + } + + // use --precompile to compile .cppm to .pcm + @Test + public void testModuleCompileOption() throws Exception { + enableCpp20Module(); + useConfiguration("--cpu=k8"); + scratch.file("a/BUILD", "cc_library(", "name='foo', ", + "features = ['cpp20_module'], ", + "module_interfaces=['foo.cppm']", ")"); + ConfiguredTarget target = getConfiguredTarget("//a:foo"); + List compilationSteps = + actionsTestUtil().findTransitivePrerequisitesOf( + getFilesToBuild(target).toList().get(0), CppCompileAction.class); + assertThat(compilationSteps.get(1).getArguments()).contains("--precompile"); + } + + @Test + public void testModuleCompileOnly() throws Exception { + enableCpp20Module(); + useConfiguration("--cpu=k8"); + scratch.file("a/BUILD", "cc_library(", "name='foo', ", + "features = ['cpp20_module'], ", + "module_interfaces=['foo.cppm']", ")"); + ConfiguredTarget target = getConfiguredTarget("//a:foo"); + var outputs = + getOutputGroup(target, OutputGroupInfo.FILES_TO_COMPILE).toList(); + assertThat(outputs).isNotEmpty(); + + assertThat(getArtifactByExecPathSuffix(target, "/foo.o")).isNotNull(); + assertThat(getArtifactByExecPathSuffix(target, "/foo.pcm.o")).isNull(); + } + + protected static Artifact getArtifactByExecPathSuffix(ConfiguredTarget target, + String path) { + for (Artifact artifact : + getOutputGroup(target, OutputGroupInfo.FILES_TO_COMPILE).toList()) { + if (artifact.getExecPathString().endsWith(path)) { + return artifact; + } + } + return null; + } +} diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java index 9c2185e4d6133d..a8417a38e2234e 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java @@ -163,6 +163,7 @@ private static FeatureConfiguration getMockFeatureConfiguration( "gcc_tool", "ar_tool", "strip_tool", + "deps_scanner_tool", /* supportsInterfaceSharedLibraries= */ false, /* existingActionConfigNames= */ ImmutableSet.of()); diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLineTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLineTest.java index a745afc5409a91..02e0451aef5758 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLineTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLineTest.java @@ -91,6 +91,7 @@ private static FeatureConfiguration getMockFeatureConfiguration() throws Excepti "MOCK_GCC_TOOL", "MOCK_AR_TOOL", "MOCK_STRIP_TOOL", + "MOCK_DEPS_SCANNER_TOOL", /* supportsInterfaceSharedLibraries= */ false, /* existingActionConfigNames= */ ImmutableSet.of()); diff --git a/tools/build_defs/cc/action_names.bzl b/tools/build_defs/cc/action_names.bzl index f377ecafb19660..aa5e4828cc4aab 100644 --- a/tools/build_defs/cc/action_names.bzl +++ b/tools/build_defs/cc/action_names.bzl @@ -31,6 +31,13 @@ CPP_MODULE_CODEGEN_ACTION_NAME = "c++-module-codegen" # Name of the C++ header parsing action. CPP_HEADER_PARSING_ACTION_NAME = "c++-header-parsing" +# Name of the C++ deps scanning action. +CPP20_DEPS_SCANNING_ACTION_NAME = "c++20-deps-scanning" + +# Name of the C++ module compile action. +CPP20_MODULE_COMPILE_ACTION_NAME = "c++20-module-compile" +CPP20_MODULE_CODEGEN_ACTION_NAME = "c++20-module-codegen" + # Name of the C++ module compile action. CPP_MODULE_COMPILE_ACTION_NAME = "c++-module-compile" @@ -98,6 +105,9 @@ ACTION_NAMES = struct( cc_flags_make_variable = CC_FLAGS_MAKE_VARIABLE_ACTION_NAME, cpp_module_codegen = CPP_MODULE_CODEGEN_ACTION_NAME, cpp_header_parsing = CPP_HEADER_PARSING_ACTION_NAME, + cpp20_deps_scanning = CPP20_DEPS_SCANNING_ACTION_NAME, + cpp20_module_compile = CPP20_MODULE_COMPILE_ACTION_NAME, + cpp20_module_codegen = CPP20_MODULE_CODEGEN_ACTION_NAME, cpp_module_compile = CPP_MODULE_COMPILE_ACTION_NAME, assemble = ASSEMBLE_ACTION_NAME, preprocess_assemble = PREPROCESS_ASSEMBLE_ACTION_NAME, diff --git a/tools/cpp/BUILD.tpl b/tools/cpp/BUILD.tpl index f99995d2b923eb..a8ab542745f7b6 100644 --- a/tools/cpp/BUILD.tpl +++ b/tools/cpp/BUILD.tpl @@ -54,6 +54,11 @@ filegroup( srcs = ["cc_wrapper.sh"], ) +filegroup( + name = "deps_scanner_wrapper", + srcs = ["deps_scanner_wrapper.sh"], +) + filegroup( name = "compiler_deps", srcs = glob(["extra_tools/**"], allow_empty = True) + [%{cc_compiler_deps}], diff --git a/tools/cpp/armeabi_cc_toolchain_config.bzl b/tools/cpp/armeabi_cc_toolchain_config.bzl index 72ef48ae6d6dfc..3d518999c56ecd 100644 --- a/tools/cpp/armeabi_cc_toolchain_config.bzl +++ b/tools/cpp/armeabi_cc_toolchain_config.bzl @@ -53,6 +53,7 @@ def _impl(ctx): tool_path(name = "objcopy", path = "/bin/false"), tool_path(name = "objdump", path = "/bin/false"), tool_path(name = "strip", path = "/bin/false"), + tool_path(name = "deps-scanner", path = "/bin/false"), ] return cc_common.create_cc_toolchain_config_info( diff --git a/tools/cpp/clang_deps_scanner_wrapper.sh.tpl b/tools/cpp/clang_deps_scanner_wrapper.sh.tpl new file mode 100644 index 00000000000000..63a14ed6761451 --- /dev/null +++ b/tools/cpp/clang_deps_scanner_wrapper.sh.tpl @@ -0,0 +1,52 @@ +#!/bin/bash +# +# Ship the environment to the C++ action +# +set -eu + +# Set-up the environment +%{env} + +# Call the C++ compiler +%{deps_scanner} -format=p1689 -- %{cc} "$@" > $DEPS_SCANNER_OUTPUT_FILE + +# workaround for clang-scan-deps BUG +# the .d file generated by clang-scan-deps is inconsistent with the one generated by clang. +# the source file path in .d file is absolute path, and Bazel regards the source file as extra dependency. +# see: https://github.com/llvm/llvm-project/issues/69439 +# use two functions handle Linux and MacOS +# the difference is as follows +# 1. how to get string length +# 2. how to use sed to modify .d file +fix_bash() { +output_file_path=$(readlink -f $DEPS_SCANNER_OUTPUT_FILE) +output_file_length=$(expr length $output_file_path) +output_file_short_length=$(expr length $DEPS_SCANNER_OUTPUT_FILE) +current_path_length=$(expr $output_file_length - $output_file_short_length) +current_path=${output_file_path:0:$current_path_length} +dotd_file_length=$(expr $output_file_short_length - 2) +dotd_file_path=${DEPS_SCANNER_OUTPUT_FILE:0:$dotd_file_length} +sed_arg="s|$current_path||g" +sed -i $sed_arg $dotd_file_path +} +fix_zsh() { +output_file_path=$(readlink -f $DEPS_SCANNER_OUTPUT_FILE) +output_file_length=${#output_file_path} +output_file_short_length=${#DEPS_SCANNER_OUTPUT_FILE} +current_path_length=$(expr $output_file_length - $output_file_short_length) +current_path=${output_file_path:0:$current_path_length} +dotd_file_length=$(expr $output_file_short_length - 2) +dotd_file_path=${DEPS_SCANNER_OUTPUT_FILE:0:$dotd_file_length} +sed_arg="s|$current_path||g" +sed -i '' $sed_arg $dotd_file_path +} + +system_type=$(uname -s) +if [ "$system_type" = "Linux" ]; then + fix_bash +elif [ "$system_type" = "Darwin" ]; then + fix_zsh +else + echo "The system is not supported now: $system_type" + exit 1 +fi diff --git a/tools/cpp/gcc_deps_scanner_wrapper.sh.tpl b/tools/cpp/gcc_deps_scanner_wrapper.sh.tpl new file mode 100644 index 00000000000000..2893e5bc4f7667 --- /dev/null +++ b/tools/cpp/gcc_deps_scanner_wrapper.sh.tpl @@ -0,0 +1,12 @@ +#!/bin/bash +# +# Ship the environment to the C++ action +# +set -eu + +# Set-up the environment +%{env} + +# Call the C++ compiler +echo "gcc not supported now" +exit 1 diff --git a/tools/cpp/unix_cc_configure.bzl b/tools/cpp/unix_cc_configure.bzl index 3f162bdeb12551..09f169027b5a77 100644 --- a/tools/cpp/unix_cc_configure.bzl +++ b/tools/cpp/unix_cc_configure.bzl @@ -91,6 +91,7 @@ def _get_tool_paths(repository_ctx, overriden_tools): "objcopy", "objdump", "strip", + "deps-scanner", ] }.items()) @@ -330,6 +331,8 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overriden_tools): "@bazel_tools//tools/cpp:unix_cc_toolchain_config.bzl", "@bazel_tools//tools/cpp:linux_cc_wrapper.sh.tpl", "@bazel_tools//tools/cpp:osx_cc_wrapper.sh.tpl", + "@bazel_tools//tools/cpp:clang_deps_scanner_wrapper.sh.tpl", + "@bazel_tools//tools/cpp:gcc_deps_scanner_wrapper.sh.tpl", ]) repository_ctx.symlink( @@ -385,6 +388,8 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overriden_tools): if darwin: overriden_tools["gcc"] = "cc_wrapper.sh" overriden_tools["ar"] = _find_generic(repository_ctx, "libtool", "LIBTOOL", overriden_tools) + + overriden_tools["deps-scanner"] = "deps_scanner_wrapper.sh" auto_configure_warning_maybe(repository_ctx, "CC used: " + str(cc)) tool_paths = _get_tool_paths(repository_ctx, overriden_tools) @@ -409,6 +414,24 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overriden_tools): "%{env}": escape_string(get_env(repository_ctx)), }, ) + deps_scanner_wrapper_src = ( + "@bazel_tools//tools/cpp:clang_deps_scanner_wrapper.sh.tpl" if is_clang else "@bazel_tools//tools/cpp:gcc_deps_scanner_wrapper.sh.tpl" + ) + deps_scanner = "" + if is_clang: + cc_str = str(cc) + path_arr = cc_str.split("/")[:-1] + path_arr.append("clang-scan-deps") + deps_scanner = "/".join(path_arr) + repository_ctx.template( + "deps_scanner_wrapper.sh", + paths[deps_scanner_wrapper_src], + { + "%{cc}": escape_string(str(cc)), + "%{deps_scanner}": escape_string(deps_scanner), + "%{env}": escape_string(get_env(repository_ctx)), + }, + ) conly_opts = split_escaped(get_env_var( repository_ctx, @@ -553,6 +576,7 @@ def configure_unix_toolchain(repository_ctx, cpu_value, overriden_tools): "%{cc_compiler_deps}": get_starlark_list([ ":builtin_include_directory_paths", ":cc_wrapper", + ":deps_scanner_wrapper", ]), "%{compiler}": escape_string(get_env_var( repository_ctx, diff --git a/tools/cpp/unix_cc_toolchain_config.bzl b/tools/cpp/unix_cc_toolchain_config.bzl index a361e7b02e2ef6..9be877f2a29315 100644 --- a/tools/cpp/unix_cc_toolchain_config.bzl +++ b/tools/cpp/unix_cc_toolchain_config.bzl @@ -146,6 +146,9 @@ all_compile_actions = [ ACTION_NAMES.cpp_header_parsing, ACTION_NAMES.cpp_module_compile, ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.cpp20_deps_scanning, + ACTION_NAMES.cpp20_module_compile, + ACTION_NAMES.cpp20_module_codegen, ACTION_NAMES.clif_match, ACTION_NAMES.lto_backend, ] @@ -156,6 +159,9 @@ all_cpp_compile_actions = [ ACTION_NAMES.cpp_header_parsing, ACTION_NAMES.cpp_module_compile, ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.cpp20_deps_scanning, + ACTION_NAMES.cpp20_module_compile, + ACTION_NAMES.cpp20_module_codegen, ACTION_NAMES.clif_match, ] @@ -166,6 +172,8 @@ preprocessor_compile_actions = [ ACTION_NAMES.preprocess_assemble, ACTION_NAMES.cpp_header_parsing, ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp20_deps_scanning, + ACTION_NAMES.cpp20_module_compile, ACTION_NAMES.clif_match, ] @@ -176,6 +184,7 @@ codegen_compile_actions = [ ACTION_NAMES.assemble, ACTION_NAMES.preprocess_assemble, ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.cpp20_module_codegen, ACTION_NAMES.lto_backend, ] @@ -386,6 +395,9 @@ def _impl(ctx): ACTION_NAMES.cpp_header_parsing, ACTION_NAMES.cpp_module_compile, ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.cpp20_deps_scanning, + ACTION_NAMES.cpp20_module_compile, + ACTION_NAMES.cpp20_module_codegen, ACTION_NAMES.lto_backend, ACTION_NAMES.clif_match, ] + all_link_actions + lto_index_actions, @@ -500,6 +512,8 @@ def _impl(ctx): ACTION_NAMES.cpp_compile, ACTION_NAMES.cpp_module_codegen, ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp20_module_compile, + ACTION_NAMES.cpp20_module_codegen, ], flag_groups = [ flag_group(flags = ["-fPIC"], expand_if_available = "pic"), @@ -519,6 +533,7 @@ def _impl(ctx): ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile, ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.cpp20_module_codegen, ], flag_groups = [ flag_group( @@ -542,6 +557,9 @@ def _impl(ctx): ACTION_NAMES.cpp_compile, ACTION_NAMES.cpp_header_parsing, ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp20_deps_scanning, + ACTION_NAMES.cpp20_module_compile, + ACTION_NAMES.cpp20_module_codegen, ACTION_NAMES.clif_match, ], flag_groups = [ @@ -698,6 +716,9 @@ def _impl(ctx): ACTION_NAMES.cpp_compile, ACTION_NAMES.cpp_module_codegen, ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp20_deps_scanning, + ACTION_NAMES.cpp20_module_compile, + ACTION_NAMES.cpp20_module_codegen, ], flag_groups = [ flag_group( @@ -721,6 +742,8 @@ def _impl(ctx): ACTION_NAMES.cpp_compile, ACTION_NAMES.cpp_header_parsing, ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp20_deps_scanning, + ACTION_NAMES.cpp20_module_compile, ACTION_NAMES.clif_match, ACTION_NAMES.objc_compile, ACTION_NAMES.objcpp_compile, @@ -792,6 +815,8 @@ def _impl(ctx): ACTION_NAMES.cpp_compile, ACTION_NAMES.cpp_header_parsing, ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp20_deps_scanning, + ACTION_NAMES.cpp20_module_compile, ACTION_NAMES.clif_match, ACTION_NAMES.objc_compile, ACTION_NAMES.objcpp_compile, @@ -825,6 +850,9 @@ def _impl(ctx): ACTION_NAMES.cpp_compile, ACTION_NAMES.cpp_header_parsing, ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp20_deps_scanning, + ACTION_NAMES.cpp20_module_compile, + ACTION_NAMES.cpp20_module_codegen, ACTION_NAMES.clif_match, ACTION_NAMES.objc_compile, ACTION_NAMES.objcpp_compile, @@ -1158,6 +1186,8 @@ def _impl(ctx): ACTION_NAMES.objc_compile, ACTION_NAMES.objcpp_compile, ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp20_deps_scanning, + ACTION_NAMES.cpp20_module_compile, ACTION_NAMES.clif_match, ], flag_groups = [ @@ -1183,6 +1213,7 @@ def _impl(ctx): ACTION_NAMES.objc_compile, ACTION_NAMES.objcpp_compile, ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp20_deps_scanning, ACTION_NAMES.clif_match, ], flag_groups = [ @@ -1427,11 +1458,42 @@ def _impl(ctx): ], ) + # Tell bazel we support C++20 Modules now + cpp20_module = feature( + name = "cpp20_module", + # set default value to False + # to enable the feature + # use --features=cpp20_module + # or add cpp20_module to features attr + enabled = False, + ) + cpp20_modmap_file_feature = feature( + name = "cpp20_modmap_file", + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.cpp_compile, + ACTION_NAMES.cpp20_module_compile, + ACTION_NAMES.cpp20_module_codegen, + ], + flag_groups = [ + flag_group( + flags = ["@%{cpp20_modmap_file}"], + expand_if_available = "cpp20_modmap_file", + ), + ], + ), + ], + enabled = True, + ) + # TODO(#8303): Mac crosstool should also declare every feature. if is_linux: # Linux artifact name patterns are the default. artifact_name_patterns = [] features = [ + cpp20_module, + cpp20_modmap_file_feature, dependency_file_feature, serialized_diagnostics_file_feature, random_seed_feature, @@ -1499,6 +1561,8 @@ def _impl(ctx): ), ] features = [ + cpp20_module, + cpp20_modmap_file_feature, macos_minimum_os_feature, macos_default_link_flags_feature, libtool_feature,