/** Returns 0, 0 for an unknown line */ @Override Pair<Integer, Integer> getOffsetsForLine(int line) { for (int ii = 0, len = table.size(); ii < len; ii++) { if (table.get(ii).line == line) { return Pair.of( table.get(ii).offset, ii < len - 1 ? table.get(ii + 1).offset : bufferLength); } } return Pair.of(0, 0); }
/** * Creates a {@link MiddlemanType#AGGREGATING_MIDDLEMAN aggregating} middleman. * * @param owner the owner of the action that will be created; must not be null * @param purpose the purpose for which this middleman is created. This should be a string which * is suitable for use as a filename. A single rule may have many middlemen with distinct * purposes. * @param inputs the set of artifacts for which the created artifact is to be the middleman. * @param middlemanDir the directory in which to place the middleman. * @return null iff {@code inputs} is empty; the single element of {@code inputs} if there's only * one; a new aggregating middleman for the {@code inputs} otherwise */ public Artifact createAggregatingMiddleman( ActionOwner owner, String purpose, Iterable<Artifact> inputs, Root middlemanDir) { if (hasExactlyOneInput(inputs)) { // Optimization: No middleman for just one input. return Iterables.getOnlyElement(inputs); } Pair<Artifact, Action> result = createMiddleman( owner, Label.print(owner.getLabel()), purpose, inputs, middlemanDir, MiddlemanType.AGGREGATING_MIDDLEMAN); return result == null ? null : result.getFirst(); }
/** * Add a map of generated source or header Artifact to an output Artifact after grepping the * file for include statements. */ public Builder addPregreppedHeaderMap(Map<Artifact, Artifact> pregrepped) { addCompilationPrerequisites(pregrepped.values()); for (Map.Entry<Artifact, Artifact> entry : pregrepped.entrySet()) { this.pregreppedHdrs.add(Pair.of(entry.getKey(), entry.getValue())); } return this; }
@Override Pair<Integer, Integer> getOffsetsForLine(int line) { if (line <= 0 || line >= linestart.length) { throw new IllegalArgumentException("Illegal line: " + line); } return Pair.of( linestart[line], line < linestart.length - 1 ? linestart[line + 1] : bufferLength); }
/** * Adds a header to {@code publicHeaders} and in case header processing is switched on for the * file type also to compilationUnitSources. */ private void addHeader(Artifact header, Label label) { boolean isHeader = CppFileTypes.CPP_HEADER.matches(header.getExecPath()); boolean isTextualInclude = CppFileTypes.CPP_TEXTUAL_INCLUDE.matches(header.getExecPath()); publicHeaders.add(header); if (isTextualInclude || !isHeader || !shouldProcessHeaders()) { return; } compilationUnitSources.add(Pair.of(header, label)); }
/** * Extracts the repository name from a PathFragment that was created with {@code * PackageIdentifier.getPathFragment}. * * @return a {@code Pair} of the extracted repository name and the path fragment with stripped of * "external/"-prefix and repository name, or null if none was found or the repository name * was invalid. */ public static Pair<RepositoryName, PathFragment> fromPathFragment(PathFragment path) { if (path.segmentCount() < 2 || !path.getSegment(0).equals(Label.EXTERNAL_PATH_PREFIX)) { return null; } try { RepositoryName repoName = RepositoryName.create("@" + path.getSegment(1)); PathFragment subPath = path.subFragment(2, path.segmentCount()); return Pair.of(repoName, subPath); } catch (LabelSyntaxException e) { return null; } }
/** * Adds a source to {@code compilationUnitSources} if it is a compiled file type (including * parsed/preprocessed header) and to {@code privateHeaders} if it is a header. */ private void addSource(Artifact source, Label label) { boolean isHeader = CppFileTypes.CPP_HEADER.matches(source.getExecPath()); boolean isTextualInclude = CppFileTypes.CPP_TEXTUAL_INCLUDE.matches(source.getExecPath()); boolean isCompiledSource = SOURCE_TYPES.matches(source.getExecPathString()); if (isHeader || isTextualInclude) { privateHeaders.add(source); } if (isTextualInclude || !isCompiledSource || (isHeader && !shouldProcessHeaders())) { return; } compilationUnitSources.add(Pair.of(source, label)); }
/** * Splits the link command-line into a part to be written to a parameter file, and the remaining * actual command line to be executed (which references the parameter file). Should only be used * if getParamFile() is not null. * * @throws IllegalStateException if the command-line cannot be split */ @VisibleForTesting final Pair<List<String>, List<String>> splitCommandline() { List<String> args = getRawLinkArgv(); if (linkTargetType.isStaticLibraryLink()) { // Ar link commands can also generate huge command lines. List<String> paramFileArgs = args.subList(1, args.size()); List<String> commandlineArgs = new ArrayList<>(); commandlineArgs.add(args.get(0)); commandlineArgs.add("@" + paramFile.getExecPath().getPathString()); return Pair.of(commandlineArgs, paramFileArgs); } else { // Gcc link commands tend to generate humongous commandlines for some targets, which may // not fit on some remote execution machines. To work around this we will employ the help of // a parameter file and pass any linker options through it. List<String> paramFileArgs = new ArrayList<>(); List<String> commandlineArgs = new ArrayList<>(); extractArgumentsForParamFile(args, commandlineArgs, paramFileArgs); commandlineArgs.add("-Wl,@" + paramFile.getExecPath().getPathString()); return Pair.of(commandlineArgs, paramFileArgs); } }
/** * Creates both normal and scheduling middlemen. * * <p>Note: there's no need to synchronize this method; the only use of a field is via a call to * another synchronized method (getArtifact()). * * @return null iff {@code inputs} is null or empty; the middleman file and the middleman action * otherwise */ private Pair<Artifact, Action> createMiddleman( ActionOwner owner, String middlemanName, String purpose, Iterable<Artifact> inputs, Root middlemanDir, MiddlemanType middlemanType) { if (inputs == null || Iterables.isEmpty(inputs)) { return null; } Artifact stampFile = getStampFileArtifact(middlemanName, purpose, middlemanDir); Action action = new MiddlemanAction(owner, inputs, stampFile, purpose, middlemanType); actionRegistry.registerAction(action); return Pair.of(stampFile, action); }
@Test public void testSetGlobPaths() throws Exception { // This pattern matches no files. String pattern = "fake*.java"; assertThat(cache.getKeySet()).doesNotContain(pattern); List<String> results = cache.getGlob(pattern, false); assertThat(cache.getKeySet()).contains(Pair.of(pattern, false)); assertThat(results).isEmpty(); cache.setGlobPaths( pattern, false, Futures.<List<Path>>immediateFuture( Lists.newArrayList( scratch.resolve("isolated/fake.txt"), scratch.resolve("isolated/fake.py")))); assertThat(cache.getGlob(pattern, false)).containsExactly("fake.py", "fake.txt"); }
@Override public ConfiguredTarget create(RuleContext ruleContext) throws RuleErrorException, InterruptedException { TransitiveInfoCollection lipoContextCollector = ruleContext.getPrerequisite(":lipo_context_collector", Mode.DONT_CHECK); if (lipoContextCollector != null && lipoContextCollector.getProvider(LipoContextProvider.class) == null) { ruleContext.ruleError("--lipo_context must point to a cc_binary or a cc_test rule"); return null; } CppConfiguration cppConfiguration = Preconditions.checkNotNull(ruleContext.getFragment(CppConfiguration.class)); Path fdoZip = ruleContext.getConfiguration().getCompilationMode() == CompilationMode.OPT ? cppConfiguration.getFdoZip() : null; SkyKey fdoKey = FdoSupportValue.key( cppConfiguration.getLipoMode(), fdoZip, cppConfiguration.getFdoInstrument()); SkyFunction.Environment skyframeEnv = ruleContext.getAnalysisEnvironment().getSkyframeEnv(); FdoSupportValue fdoSupport; try { fdoSupport = (FdoSupportValue) skyframeEnv.getValueOrThrow(fdoKey, FdoException.class, IOException.class); } catch (FdoException | IOException e) { ruleContext.ruleError("cannot initialize FDO: " + e.getMessage()); return null; } if (skyframeEnv.valuesMissing()) { return null; } final Label label = ruleContext.getLabel(); final NestedSet<Artifact> crosstool = ruleContext .getPrerequisite("all_files", Mode.HOST) .getProvider(FileProvider.class) .getFilesToBuild(); final NestedSet<Artifact> crosstoolMiddleman = getFiles(ruleContext, "all_files"); final NestedSet<Artifact> compile = getFiles(ruleContext, "compiler_files"); final NestedSet<Artifact> strip = getFiles(ruleContext, "strip_files"); final NestedSet<Artifact> objcopy = getFiles(ruleContext, "objcopy_files"); final NestedSet<Artifact> link = getFiles(ruleContext, "linker_files"); final NestedSet<Artifact> dwp = getFiles(ruleContext, "dwp_files"); final NestedSet<Artifact> libcLink = inputsForLibc(ruleContext); String purposePrefix = Actions.escapeLabel(label) + "_"; String runtimeSolibDirBase = "_solib_" + "_" + Actions.escapeLabel(label); final PathFragment runtimeSolibDir = ruleContext.getConfiguration().getBinFragment().getRelative(runtimeSolibDirBase); // Static runtime inputs. TransitiveInfoCollection staticRuntimeLibDep = selectDep(ruleContext, "static_runtime_libs", cppConfiguration.getStaticRuntimeLibsLabel()); final NestedSet<Artifact> staticRuntimeLinkInputs; final Artifact staticRuntimeLinkMiddleman; if (cppConfiguration.supportsEmbeddedRuntimes()) { staticRuntimeLinkInputs = staticRuntimeLibDep.getProvider(FileProvider.class).getFilesToBuild(); } else { staticRuntimeLinkInputs = NestedSetBuilder.emptySet(Order.STABLE_ORDER); } if (!staticRuntimeLinkInputs.isEmpty()) { NestedSet<Artifact> staticRuntimeLinkMiddlemanSet = CompilationHelper.getAggregatingMiddleman( ruleContext, purposePrefix + "static_runtime_link", staticRuntimeLibDep); staticRuntimeLinkMiddleman = staticRuntimeLinkMiddlemanSet.isEmpty() ? null : Iterables.getOnlyElement(staticRuntimeLinkMiddlemanSet); } else { staticRuntimeLinkMiddleman = null; } Preconditions.checkState( (staticRuntimeLinkMiddleman == null) == staticRuntimeLinkInputs.isEmpty()); // Dynamic runtime inputs. TransitiveInfoCollection dynamicRuntimeLibDep = selectDep( ruleContext, "dynamic_runtime_libs", cppConfiguration.getDynamicRuntimeLibsLabel()); final NestedSet<Artifact> dynamicRuntimeLinkInputs; final Artifact dynamicRuntimeLinkMiddleman; if (cppConfiguration.supportsEmbeddedRuntimes()) { NestedSetBuilder<Artifact> dynamicRuntimeLinkInputsBuilder = NestedSetBuilder.stableOrder(); for (Artifact artifact : dynamicRuntimeLibDep.getProvider(FileProvider.class).getFilesToBuild()) { if (CppHelper.SHARED_LIBRARY_FILETYPES.matches(artifact.getFilename())) { dynamicRuntimeLinkInputsBuilder.add( SolibSymlinkAction.getCppRuntimeSymlink( ruleContext, artifact, runtimeSolibDirBase, ruleContext.getConfiguration())); } else { dynamicRuntimeLinkInputsBuilder.add(artifact); } } dynamicRuntimeLinkInputs = dynamicRuntimeLinkInputsBuilder.build(); } else { dynamicRuntimeLinkInputs = NestedSetBuilder.emptySet(Order.STABLE_ORDER); } if (!dynamicRuntimeLinkInputs.isEmpty()) { List<Artifact> dynamicRuntimeLinkMiddlemanSet = CppHelper.getAggregatingMiddlemanForCppRuntimes( ruleContext, purposePrefix + "dynamic_runtime_link", dynamicRuntimeLibDep, runtimeSolibDirBase, ruleContext.getConfiguration()); dynamicRuntimeLinkMiddleman = dynamicRuntimeLinkMiddlemanSet.isEmpty() ? null : Iterables.getOnlyElement(dynamicRuntimeLinkMiddlemanSet); } else { dynamicRuntimeLinkMiddleman = null; } Preconditions.checkState( (dynamicRuntimeLinkMiddleman == null) == dynamicRuntimeLinkInputs.isEmpty()); CppCompilationContext.Builder contextBuilder = new CppCompilationContext.Builder(ruleContext); CppModuleMap moduleMap = createCrosstoolModuleMap(ruleContext); if (moduleMap != null) { contextBuilder.setCppModuleMap(moduleMap); } final CppCompilationContext context = contextBuilder.build(); boolean supportsParamFiles = ruleContext.attributes().get("supports_param_files", BOOLEAN); boolean supportsHeaderParsing = ruleContext.attributes().get("supports_header_parsing", BOOLEAN); NestedSetBuilder<Pair<String, String>> coverageEnvironment = NestedSetBuilder.compileOrder(); coverageEnvironment.add( Pair.of("COVERAGE_GCOV_PATH", cppConfiguration.getGcovExecutable().getPathString())); if (cppConfiguration.getFdoInstrument() != null) { coverageEnvironment.add( Pair.of("FDO_DIR", cppConfiguration.getFdoInstrument().getPathString())); } CcToolchainProvider provider = new CcToolchainProvider( cppConfiguration, crosstool, fullInputsForCrosstool(ruleContext, crosstoolMiddleman), compile, strip, objcopy, fullInputsForLink(ruleContext, link), dwp, libcLink, staticRuntimeLinkInputs, staticRuntimeLinkMiddleman, dynamicRuntimeLinkInputs, dynamicRuntimeLinkMiddleman, runtimeSolibDir, context, supportsParamFiles, supportsHeaderParsing, getBuildVariables(ruleContext), getBuiltinIncludes(ruleContext), coverageEnvironment.build()); RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(ruleContext) .add(CcToolchainProvider.class, provider) .add(FdoSupportProvider.class, new FdoSupportProvider(fdoSupport.getFdoSupport())) .setFilesToBuild(new NestedSetBuilder<Artifact>(Order.STABLE_ORDER).build()) .add(RunfilesProvider.class, RunfilesProvider.simple(Runfiles.EMPTY)); // If output_license is specified on the cc_toolchain rule, override the transitive licenses // with that one. This is necessary because cc_toolchain is used in the target configuration, // but it is sort-of-kind-of a tool, but various parts of it are linked into the output... // ...so we trust the judgment of the author of the cc_toolchain rule to figure out what // licenses should be propagated to C++ targets. License outputLicense = ruleContext.getRule().getToolOutputLicense(ruleContext.attributes()); if (outputLicense != null && outputLicense != License.NO_LICENSE) { final NestedSet<TargetLicense> license = NestedSetBuilder.create( Order.STABLE_ORDER, new TargetLicense(ruleContext.getLabel(), outputLicense)); LicensesProvider licensesProvider = new LicensesProvider() { @Override public NestedSet<TargetLicense> getTransitiveLicenses() { return license; } }; builder.add(LicensesProvider.class, licensesProvider); } return builder.build(); }
@Test public void testGetKeySet() throws Exception { assertThat(cache.getKeySet()).isEmpty(); cache.getGlob("*.java"); assertThat(cache.getKeySet()).containsExactly(Pair.of("*.java", false)); cache.getGlob("*.java"); assertThat(cache.getKeySet()).containsExactly(Pair.of("*.java", false)); cache.getGlob("*.js"); assertThat(cache.getKeySet()).containsExactly(Pair.of("*.java", false), Pair.of("*.js", false)); cache.getGlob("*.java", true); assertThat(cache.getKeySet()) .containsExactly(Pair.of("*.java", false), Pair.of("*.js", false), Pair.of("*.java", true)); try { cache.getGlob("invalid?"); fail("Expected an invalid regex exception"); } catch (BadGlobException expected) { } assertThat(cache.getKeySet()) .containsExactly(Pair.of("*.java", false), Pair.of("*.js", false), Pair.of("*.java", true)); cache.getGlob("foo/first.*"); assertThat(cache.getKeySet()) .containsExactly( Pair.of("*.java", false), Pair.of("*.java", true), Pair.of("*.js", false), Pair.of("foo/first.*", false)); }