/** * Generates an empty manifest for a rule that does not directly specify resources. * * <p><strong>Note:</strong> This generated manifest can then be used as the primary manifest when * merging with dependencies. * * @return the generated ApplicationManifest */ public static ApplicationManifest generatedManifest(RuleContext ruleContext) { Artifact generatedManifest = ruleContext.getUniqueDirectoryArtifact( ruleContext.getRule().getName() + "_generated", new PathFragment("AndroidManifest.xml"), ruleContext.getBinOrGenfilesDirectory()); String manifestPackage; if (ruleContext.attributes().isAttributeValueExplicitlySpecified("custom_package")) { manifestPackage = ruleContext.attributes().get("custom_package", Type.STRING); } else { manifestPackage = JavaUtil.getJavaFullClassname(ruleContext.getRule().getPackage().getNameFragment()); } String contents = Joiner.on("\n") .join( "<?xml version=\"1.0\" encoding=\"utf-8\"?>", "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"", " package=\"" + manifestPackage + "\">", " <application>", " </application>", "</manifest>"); ruleContext .getAnalysisEnvironment() .registerAction( new FileWriteAction( ruleContext.getActionOwner(), generatedManifest, contents, false /* makeExecutable */)); return new ApplicationManifest(generatedManifest); }
/** * Create a Rule Configured Target from the ruleContext and the ruleImplementation. The * registeredProviderTypes map indicates which keys in structs returned by skylark rules should be * interpreted as native TransitiveInfoProvider instances of type (map value). */ public static ConfiguredTarget buildRule( RuleContext ruleContext, BaseFunction ruleImplementation, Map<String, Class<? extends TransitiveInfoProvider>> registeredProviderTypes) throws InterruptedException { String expectFailure = ruleContext.attributes().get("expect_failure", Type.STRING); try (Mutability mutability = Mutability.create("configured target")) { SkylarkRuleContext skylarkRuleContext = new SkylarkRuleContext(ruleContext, Kind.RULE); Environment env = Environment.builder(mutability) .setSkylark() .setCallerLabel(ruleContext.getLabel()) .setGlobals( ruleContext .getRule() .getRuleClassObject() .getRuleDefinitionEnvironment() .getGlobals()) .setEventHandler(ruleContext.getAnalysisEnvironment().getEventHandler()) .build(); // NB: loading phase functions are not available: this is analysis already, // so we do *not* setLoadingPhase(). Object target = ruleImplementation.call( ImmutableList.<Object>of(skylarkRuleContext), ImmutableMap.<String, Object>of(), /*ast=*/ null, env); if (ruleContext.hasErrors()) { return null; } else if (!(target instanceof SkylarkClassObject) && target != Runtime.NONE && !(target instanceof Iterable)) { ruleContext.ruleError( String.format( "Rule should return a return a struct or a list, but got %s", SkylarkType.typeOf(target))); return null; } else if (!expectFailure.isEmpty()) { ruleContext.ruleError("Expected failure not found: " + expectFailure); return null; } ConfiguredTarget configuredTarget = createTarget(ruleContext, target, registeredProviderTypes); SkylarkProviderValidationUtil.checkOrphanArtifacts(ruleContext); return configuredTarget; } catch (EvalException e) { addRuleToStackTrace(e, ruleContext.getRule(), ruleImplementation); // If the error was expected, return an empty target. if (!expectFailure.isEmpty() && getMessageWithoutStackTrace(e).matches(expectFailure)) { return new com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder(ruleContext) .add(RunfilesProvider.class, RunfilesProvider.EMPTY) .build(); } ruleContext.ruleError("\n" + e.print()); return null; } }
private Artifact scopedArtifact(PathFragment scopeRelative, boolean inGenfiles) { Root root = inGenfiles ? buildConfiguration.getGenfilesDirectory(ruleContext.getRule().getRepository()) : buildConfiguration.getBinDirectory(ruleContext.getRule().getRepository()); // The path of this artifact will be RULE_PACKAGE/SCOPERELATIVE return ruleContext.getPackageRelativeArtifact(scopeRelative, root); }
TestTargetExecutionSettings( RuleContext ruleContext, RunfilesSupport runfiles, Artifact executable, Artifact instrumentedFileManifest, int shards) { Preconditions.checkArgument(TargetUtils.isTestRule(ruleContext.getRule())); Preconditions.checkArgument(shards >= 0); BuildConfiguration config = ruleContext.getConfiguration(); List<String> targetArgs = runfiles.getArgs(); testArguments = targetArgs.isEmpty() ? config.getTestArguments() : ImmutableList.copyOf(Iterables.concat(targetArgs, config.getTestArguments())); totalShards = shards; runUnder = config.getRunUnder(); runUnderExecutable = getRunUnderExecutable(ruleContext); this.testFilter = config.getTestFilter(); this.executable = executable; this.runfilesManifest = runfiles.getRunfilesManifest(); this.runfilesInputManifest = runfiles.getRunfilesInputManifest(); this.instrumentedFileManifest = instrumentedFileManifest; }
/** * Creates a middleman for the compilation prerequisites. * * @return the middleman or null if there are no prerequisites */ private Artifact createMiddleman(ActionOwner owner, MiddlemanFactory middlemanFactory) { if (compilationPrerequisites.isEmpty()) { return null; } // Compilation prerequisites gathered in the compilationPrerequisites // must be generated prior to executing C++ compilation step that depends // on them (since these prerequisites include all potential header files, etc // that could be referenced during compilation). So there is a definite need // to ensure scheduling edge dependency. However, those prerequisites should // have no effect on the decision whether C++ compilation should happen in // the first place - only CppCompileAction outputs (*.o and *.d files) and // all files referenced by the *.d file should be used to make that decision. // If this action was never executed, then *.d file would be missing, forcing // compilation to occur. If *.d file is present and has not changed then the // only reason that would force us to re-compile would be change in one of // the files referenced by the *.d file, since no other files participated // in the compilation. We also need to propagate errors through this // dependency link. So we use an error propagating middleman. // Such middleman will be ignored by the dependency checker yet will still // represent an edge in the action dependency graph - forcing proper execution // order and error propagation. return middlemanFactory.createErrorPropagatingMiddleman( owner, ruleContext.getLabel().toString(), purpose, ImmutableList.copyOf(compilationPrerequisites), ruleContext .getConfiguration() .getMiddlemanDirectory(ruleContext.getRule().getRepository())); }
/** Returns true iff code coverage is enabled for the given target. */ private boolean isCodeCoverageEnabled() { if (configuration.isCodeCoverageEnabled()) { // If rule is matched by the instrumentation filter, enable instrumentation if (InstrumentedFilesCollector.shouldIncludeLocalSources(ruleContext)) { return true; } // At this point the rule itself is not matched by the instrumentation filter. However, we // might still want to instrument C++ rules if one of the targets listed in "deps" is // instrumented and, therefore, can supply header files that we would want to collect code // coverage for. For example, think about cc_test rule that tests functionality defined in a // header file that is supplied by the cc_library. // // Note that we only check direct prerequisites and not the transitive closure. This is done // for two reasons: // a) It is a good practice to declare libraries which you directly rely on. Including headers // from a library hidden deep inside the transitive closure makes build dependencies less // readable and can lead to unexpected breakage. // b) Traversing the transitive closure for each C++ compile action would require more complex // implementation (with caching results of this method) to avoid O(N^2) slowdown. if (ruleContext.getRule().isAttrDefined("deps", BuildType.LABEL_LIST)) { for (TransitiveInfoCollection dep : ruleContext.getPrerequisites("deps", Mode.TARGET)) { if (dep.getProvider(CppCompilationContext.class) != null && InstrumentedFilesCollector.shouldIncludeLocalSources(configuration, dep)) { return true; } } } } return false; }
/** * Returns a derived artifact in the bin directory obtained by appending some extension to the * main label name; the result artifact is placed in a unique "entitlements" directory. For * example, if this artifact is for a target Foo with extension ".extension", the result artifact * will be located at {target_base_path}/entitlements/Foo.extension. */ public Artifact appendExtensionForEntitlementArtifact(String extension) { PathFragment entitlementsDirectory = ruleContext.getUniqueDirectory("entitlements"); Artifact artifact = ruleContext.getDerivedArtifact( entitlementsDirectory.replaceName( addOutputPrefix(entitlementsDirectory.getBaseName(), extension)), buildConfiguration.getBinDirectory(ruleContext.getRule().getRepository())); return artifact; }
/** * Returns the same set of artifacts as createMiddleman() would, but without actually creating * middlemen. */ private Artifact getMiddlemanArtifact(MiddlemanFactory middlemanFactory) { if (compilationPrerequisites.isEmpty()) { return null; } return middlemanFactory.getErrorPropagatingMiddlemanArtifact( ruleContext.getLabel().toString(), purpose, ruleContext .getConfiguration() .getMiddlemanDirectory(ruleContext.getRule().getRepository())); }
public static String getJavaPackage(RuleContext ruleContext) { if (ruleContext.attributes().isAttributeValueExplicitlySpecified("custom_package")) { return ruleContext.attributes().get("custom_package", Type.STRING); } else { PathFragment nameFragment = ruleContext.getRule().getPackage().getNameFragment(); String packageName = JavaUtil.getJavaFullClassname(nameFragment); if (packageName != null) { return packageName; } else { // This is a workaround for libraries that don't follow the standard Bazel package format return nameFragment.getPathString().replace('/', '.'); } } }
/** * Adds environments specified by the given attribute to the set of supported environments and * returns the environments added. * * <p>If this rule doesn't have the given attributes, returns an empty set. */ private EnvironmentCollection collectEnvironments( String attrName, EnvironmentCollection.Builder supportedEnvironments) { if (!ruleContext.getRule().isAttrDefined(attrName, BuildType.LABEL_LIST)) { return EnvironmentCollection.EMPTY; } EnvironmentCollection.Builder environments = new EnvironmentCollection.Builder(); for (TransitiveInfoCollection envTarget : ruleContext.getPrerequisites(attrName, RuleConfiguredTarget.Mode.DONT_CHECK)) { EnvironmentWithGroup envInfo = resolveEnvironment(envTarget); environments.put(envInfo.group(), envInfo.environment()); supportedEnvironments.put(envInfo.group(), envInfo.environment()); } return environments.build(); }
/** * Returns the rule class defaults specified for this rule, or null if there are no such defaults. */ @Nullable private static EnvironmentCollector maybeGetRuleClassDefaults(RuleContext ruleContext) { Rule rule = ruleContext.getRule(); String restrictionAttr = RuleClass.DEFAULT_RESTRICTED_ENVIRONMENT_ATTR; String compatibilityAttr = RuleClass.DEFAULT_COMPATIBLE_ENVIRONMENT_ATTR; if (rule.isAttrDefined(restrictionAttr, BuildType.LABEL_LIST) || rule.isAttrDefined(compatibilityAttr, BuildType.LABEL_LIST)) { return new EnvironmentCollector( ruleContext, restrictionAttr, compatibilityAttr, new GroupDefaultsProvider()); } else { return null; } }
public ApplicationManifest mergeWith( RuleContext ruleContext, Iterable<ResourceContainer> resourceContainers) { if (!Iterables.isEmpty(getMergeeManifests(resourceContainers))) { Iterable<Artifact> exportedManifests = getMergeeManifests(resourceContainers); Artifact outputManifest = ruleContext.getUniqueDirectoryArtifact( ruleContext.getRule().getName() + "_merged", "AndroidManifest.xml", ruleContext.getBinOrGenfilesDirectory()); AndroidManifestMergeHelper.createMergeManifestAction( ruleContext, getManifest(), exportedManifests, ImmutableList.of("all"), outputManifest); return new ApplicationManifest(outputManifest); } return this; }
/** Create a Rule Configured Target from the ruleContext and the ruleImplementation. */ public static ConfiguredTarget buildRule( RuleContext ruleContext, BaseFunction ruleImplementation) { String expectFailure = ruleContext.attributes().get("expect_failure", Type.STRING); try { SkylarkRuleContext skylarkRuleContext = new SkylarkRuleContext(ruleContext); SkylarkEnvironment env = ruleContext .getRule() .getRuleClassObject() .getRuleDefinitionEnvironment() .cloneEnv(ruleContext.getAnalysisEnvironment().getEventHandler()); // Collect the symbols to disable statically and pass at the next call, so we don't need to // clone the RuleDefinitionEnvironment. env.disableOnlyLoadingPhaseObjects(); Object target = ruleImplementation.call( ImmutableList.<Object>of(skylarkRuleContext), ImmutableMap.<String, Object>of(), null, env); if (ruleContext.hasErrors()) { return null; } else if (!(target instanceof SkylarkClassObject) && target != Environment.NONE) { ruleContext.ruleError("Rule implementation doesn't return a struct"); return null; } else if (!expectFailure.isEmpty()) { ruleContext.ruleError("Expected failure not found: " + expectFailure); return null; } ConfiguredTarget configuredTarget = createTarget(ruleContext, target); checkOrphanArtifacts(ruleContext); return configuredTarget; } catch (InterruptedException e) { ruleContext.ruleError(e.getMessage()); return null; } catch (EvalException e) { // If the error was expected, return an empty target. if (!expectFailure.isEmpty() && e.getMessage().matches(expectFailure)) { return new com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder(ruleContext) .add(RunfilesProvider.class, RunfilesProvider.EMPTY) .build(); } ruleContext.ruleError("\n" + e.print()); return null; } }
private static Action[] makePackageManifestAction( RuleContext ruleContext, Artifact packageManifest, Collection<Artifact> sourceFiles) { return new SpawnAction.Builder() .addInputs(sourceFiles) .addOutput(packageManifest) .setExecutable(ruleContext.getExecutablePrerequisite("$packageParser", Mode.HOST)) .setCommandLine( CustomCommandLine.builder() .addExecPath("--output_manifest", packageManifest) .addJoinStrings("--sources", ":", toSerializedArtifactLocations(sourceFiles)) .build()) .useParameterFile(ParameterFileType.SHELL_QUOTED) .setProgressMessage("Parsing java package strings for " + ruleContext.getRule()) .setMnemonic("JavaPackageManifest") .build(ruleContext); }
private JavaCompilationHelper initAttributes( JavaTargetAttributes.Builder attributes, JavaSemantics semantics) { JavaCompilationHelper helper = new JavaCompilationHelper(ruleContext, semantics, javaCommon.getJavacOpts(), attributes); helper.addLibrariesToAttributes(javaCommon.targetsTreatedAsDeps(ClasspathType.COMPILE_ONLY)); helper.addProvidersToAttributes( JavaCommon.compilationArgsFromSources(ruleContext), asNeverLink); attributes.setStrictJavaDeps(getStrictAndroidDeps()); attributes.setRuleKind(ruleContext.getRule().getRuleClass()); attributes.setTargetLabel(ruleContext.getLabel()); JavaCommon.validateConstraint( ruleContext, "android", javaCommon.targetsTreatedAsDeps(ClasspathType.BOTH)); ruleContext.checkSrcsSamePackage(true); return helper; }
private static Artifact getExecutable(RuleContext ruleContext, Object target) throws EvalException { Artifact executable = ruleContext.getRule().getRuleClassObject().outputsDefaultExecutable() // This doesn't actually create a new Artifact just returns the one // created in SkylarkruleContext. ? ruleContext.createOutputArtifact() : null; if (target instanceof SkylarkClassObject) { SkylarkClassObject struct = (SkylarkClassObject) target; if (struct.getValue("executable") != null) { // We need this because of genrule.bzl. This overrides the default executable. executable = cast("executable", struct, Artifact.class, struct.getCreationLoc()); } } return executable; }
@Override public ConfiguredTarget create(RuleContext ruleContext) throws InterruptedException { RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(ruleContext) .addProvider( RuleInfo.class, new RuleInfo(collectAspectData("rule " + ruleContext.getLabel(), ruleContext))) .setFilesToBuild(NestedSetBuilder.<Artifact>create(Order.STABLE_ORDER)) .setRunfilesSupport(null, null) .add(RunfilesProvider.class, RunfilesProvider.simple(Runfiles.EMPTY)); if (ruleContext.getRule().getRuleClassObject().getName().equals("honest")) { builder.addProvider(RequiredProvider.class, new RequiredProvider()); } return builder.build(); }
private Iterable<CppModuleMap> collectModuleMaps() { // Cpp module maps may be null for some rules. We filter the nulls out at the end. List<CppModuleMap> result = new ArrayList<>(); Iterables.addAll(result, Iterables.transform(deps, CPP_DEPS_TO_MODULES)); if (ruleContext.getRule().getAttributeDefinition(":stl") != null) { CppCompilationContext stl = ruleContext.getPrerequisite(":stl", Mode.TARGET, CppCompilationContext.class); if (stl != null) { result.add(stl.getCppModuleMap()); } } CcToolchainProvider toolchain = CppHelper.getToolchain(ruleContext); if (toolchain != null) { result.add(toolchain.getCppCompilationContext().getCppModuleMap()); } return Iterables.filter(result, Predicates.<CppModuleMap>notNull()); }
/** Returns all dependencies that should be constraint-checked against the current rule. */ private static Iterable<TransitiveInfoCollection> getConstraintCheckedDependencies( RuleContext ruleContext) { Set<TransitiveInfoCollection> depsToCheck = new LinkedHashSet<>(); AttributeMap attributes = ruleContext.attributes(); for (String attr : attributes.getAttributeNames()) { Attribute attrDef = attributes.getAttributeDefinition(attr); Type<?> attrType = attributes.getAttributeType(attr); // TODO(bazel-team): support a user-definable API for choosing which attributes are checked if (!attrDef.checkConstraintsOverride()) { if ((attrType != BuildType.LABEL && attrType != BuildType.LABEL_LIST) || RuleClass.isConstraintAttribute(attr) || attr.equals("visibility") // Use the same implicit deps check that query uses. This facilitates running queries to // determine exactly which rules need to be constraint-annotated for depot migrations. || !Rule.NO_IMPLICIT_DEPS.apply(ruleContext.getRule(), attrDef) // We can't identify host deps by calling BuildConfiguration.isHostConfiguration() // because --nodistinct_host_configuration subverts that call. || attrDef.getConfigurationTransition() == Attribute.ConfigurationTransition.HOST) { continue; } } for (TransitiveInfoCollection dep : ruleContext.getPrerequisites(attr, RuleConfiguredTarget.Mode.DONT_CHECK)) { // Output files inherit the environment spec of their generating rule. if (dep instanceof OutputFileConfiguredTarget) { // Note this reassignment means constraint violation errors reference the generating // rule, not the file. This makes the source of the environmental mismatch more clear. dep = ((OutputFileConfiguredTarget) dep).getGeneratingRule(); } // Input files don't support environments. We may subsequently opt them into constraint // checking, but for now just pass them by. if (dep.getProvider(SupportedEnvironmentsProvider.class) != null) { depsToCheck.add(dep); } } } return depsToCheck; }
@Override public ConfiguredAspect create( ConfiguredTarget base, RuleContext ruleContext, AspectParameters parameters) { ConfiguredAspect.Builder builder = new Builder(NAME, ruleContext); AndroidStudioInfoFilesProvider.Builder providerBuilder = new AndroidStudioInfoFilesProvider.Builder(); RuleIdeInfo.Kind ruleKind = getRuleKind(ruleContext.getRule(), base); DependenciesResult dependenciesResult = processDependencies(base, ruleContext, providerBuilder); AndroidStudioInfoFilesProvider provider = createIdeBuildArtifact(base, ruleContext, ruleKind, dependenciesResult, providerBuilder); builder .addOutputGroup(IDE_INFO, provider.getIdeInfoFiles()) .addOutputGroup(IDE_INFO_TEXT, provider.getIdeInfoTextFiles()) .addOutputGroup(IDE_RESOLVE, provider.getIdeResolveFiles()) .addProvider(AndroidStudioInfoFilesProvider.class, provider); return builder.build(); }
@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(); }
private AndroidStudioInfoFilesProvider createIdeBuildArtifact( ConfiguredTarget base, RuleContext ruleContext, Kind ruleKind, DependenciesResult dependenciesResult, AndroidStudioInfoFilesProvider.Builder providerBuilder) { Artifact ideInfoFile = derivedArtifact(base, ruleContext, ASWB_BUILD_SUFFIX); Artifact ideInfoTextFile = derivedArtifact(base, ruleContext, ASWB_BUILD_TEXT_SUFFIX); providerBuilder.ideInfoFilesBuilder().add(ideInfoFile); providerBuilder.ideInfoTextFilesBuilder().add(ideInfoTextFile); NestedSetBuilder<Artifact> ideResolveArtifacts = providerBuilder.ideResolveFilesBuilder(); RuleIdeInfo.Builder outputBuilder = RuleIdeInfo.newBuilder(); outputBuilder.setLabel(base.getLabel().toString()); outputBuilder.setBuildFile( ruleContext.getRule().getPackage().getBuildFile().getPath().toString()); outputBuilder.setBuildFileArtifactLocation( makeArtifactLocation(ruleContext.getRule().getPackage())); if (ruleKind != Kind.UNRECOGNIZED) { outputBuilder.setKind(ruleKind); } outputBuilder.setKindString(ruleContext.getRule().getRuleClass()); // Java rules JavaRuleOutputJarsProvider outputJarsProvider = base.getProvider(JavaRuleOutputJarsProvider.class); if (outputJarsProvider != null) { Artifact packageManifest = createPackageManifest(base, ruleContext); if (packageManifest != null) { providerBuilder.ideInfoFilesBuilder().add(packageManifest); ruleContext.registerAction( makePackageManifestAction(ruleContext, packageManifest, getJavaSources(ruleContext))); } JavaRuleIdeInfo javaRuleIdeInfo = makeJavaRuleIdeInfo( base, ruleContext, outputJarsProvider, ideResolveArtifacts, packageManifest); outputBuilder.setJavaRuleIdeInfo(javaRuleIdeInfo); } // C rules CppCompilationContext cppCompilationContext = base.getProvider(CppCompilationContext.class); if (cppCompilationContext != null) { CRuleIdeInfo cRuleIdeInfo = makeCRuleIdeInfo(base, ruleContext, cppCompilationContext); outputBuilder.setCRuleIdeInfo(cRuleIdeInfo); } // CCToolchain rule CppConfiguration cppConfiguration = getCppConfiguration(base); if (cppConfiguration != null) { CToolchainIdeInfo cToolchainIdeInfo = makeCToolchainIdeInfo(ruleContext, cppConfiguration); if (cToolchainIdeInfo != null) { outputBuilder.setCToolchainIdeInfo(cToolchainIdeInfo); } } // Android rules AndroidIdeInfoProvider androidIdeInfoProvider = base.getProvider(AndroidIdeInfoProvider.class); if (androidIdeInfoProvider != null) { outputBuilder.setAndroidRuleIdeInfo( makeAndroidRuleIdeInfo( base, androidIdeInfoProvider, dependenciesResult, ideResolveArtifacts)); } AndroidStudioInfoFilesProvider provider = providerBuilder.build(); outputBuilder.addAllDependencies(transform(dependenciesResult.deps, LABEL_TO_STRING)); outputBuilder.addAllRuntimeDeps(transform(dependenciesResult.runtimeDeps, LABEL_TO_STRING)); outputBuilder.addAllTags(base.getTarget().getAssociatedRule().getRuleTags()); final RuleIdeInfo ruleIdeInfo = outputBuilder.build(); ruleContext.registerAction( makeProtoWriteAction(ruleContext.getActionOwner(), ruleIdeInfo, ideInfoFile)); ruleContext.registerAction( makeProtoTextWriteAction(ruleContext.getActionOwner(), ruleIdeInfo, ideInfoTextFile)); return provider; }
private static ConfiguredTarget addStructFieldsAndBuild( RuleContext ruleContext, RuleConfiguredTargetBuilder builder, Object target, Artifact executable, Map<String, Class<? extends TransitiveInfoProvider>> registeredProviderTypes) throws EvalException { Location loc = null; Runfiles statelessRunfiles = null; Runfiles dataRunfiles = null; Runfiles defaultRunfiles = null; if (target instanceof SkylarkClassObject) { SkylarkClassObject struct = (SkylarkClassObject) target; loc = struct.getCreationLoc(); for (String key : struct.getKeys()) { if (key.equals("files")) { // If we specify files_to_build we don't have the executable in it by default. builder.setFilesToBuild( cast("files", struct, SkylarkNestedSet.class, Artifact.class, loc) .getSet(Artifact.class)); } else if (key.equals("runfiles")) { statelessRunfiles = cast("runfiles", struct, Runfiles.class, loc); } else if (key.equals("data_runfiles")) { dataRunfiles = cast("data_runfiles", struct, Runfiles.class, loc); } else if (key.equals("default_runfiles")) { defaultRunfiles = cast("default_runfiles", struct, Runfiles.class, loc); } else if (key.equals("output_groups")) { addOutputGroups(struct.getValue(key), loc, builder); } else if (key.equals("instrumented_files")) { SkylarkClassObject insStruct = cast("instrumented_files", struct, SkylarkClassObject.class, loc); Location insLoc = insStruct.getCreationLoc(); FileTypeSet fileTypeSet = FileTypeSet.ANY_FILE; if (insStruct.getKeys().contains("extensions")) { @SuppressWarnings("unchecked") List<String> exts = cast("extensions", insStruct, SkylarkList.class, String.class, insLoc); if (exts.isEmpty()) { fileTypeSet = FileTypeSet.NO_FILE; } else { FileType[] fileTypes = new FileType[exts.size()]; for (int i = 0; i < fileTypes.length; i++) { fileTypes[i] = FileType.of(exts.get(i)); } fileTypeSet = FileTypeSet.of(fileTypes); } } List<String> dependencyAttributes = Collections.emptyList(); if (insStruct.getKeys().contains("dependency_attributes")) { dependencyAttributes = cast("dependency_attributes", insStruct, SkylarkList.class, String.class, insLoc); } List<String> sourceAttributes = Collections.emptyList(); if (insStruct.getKeys().contains("source_attributes")) { sourceAttributes = cast("source_attributes", insStruct, SkylarkList.class, String.class, insLoc); } InstrumentationSpec instrumentationSpec = new InstrumentationSpec(fileTypeSet) .withSourceAttributes(sourceAttributes.toArray(new String[0])) .withDependencyAttributes(dependencyAttributes.toArray(new String[0])); InstrumentedFilesProvider instrumentedFilesProvider = InstrumentedFilesCollector.collect( ruleContext, instrumentationSpec, InstrumentedFilesCollector.NO_METADATA_COLLECTOR, Collections.<Artifact>emptySet()); builder.addProvider(InstrumentedFilesProvider.class, instrumentedFilesProvider); } else if (registeredProviderTypes.containsKey(key)) { Class<? extends TransitiveInfoProvider> providerType = registeredProviderTypes.get(key); TransitiveInfoProvider provider = cast(key, struct, providerType, loc); builder.addProvider(providerType, provider); } else if (key.equals("providers")) { Iterable iterable = cast(key, struct, Iterable.class, loc); for (Object o : iterable) { SkylarkClassObject declaredProvider = SkylarkType.cast( o, SkylarkClassObject.class, loc, "The value of 'providers' should be a sequence of declared providers"); builder.addSkylarkDeclaredProvider(declaredProvider, loc); } } else if (!key.equals("executable")) { // We handled executable already. builder.addSkylarkTransitiveInfo(key, struct.getValue(key), loc); } } } else if (target instanceof Iterable) { loc = ruleContext.getRule().getRuleClassObject().getConfiguredTargetFunction().getLocation(); for (Object o : (Iterable) target) { SkylarkClassObject declaredProvider = SkylarkType.cast( o, SkylarkClassObject.class, loc, "A return value of rule implementation function should be " + "a sequence of declared providers"); Location creationLoc = declaredProvider.getCreationLocOrNull(); builder.addSkylarkDeclaredProvider( declaredProvider, creationLoc != null ? creationLoc : loc); } } if ((statelessRunfiles != null) && (dataRunfiles != null || defaultRunfiles != null)) { throw new EvalException( loc, "Cannot specify the provider 'runfiles' " + "together with 'data_runfiles' or 'default_runfiles'"); } if (statelessRunfiles == null && dataRunfiles == null && defaultRunfiles == null) { // No runfiles specified, set default statelessRunfiles = Runfiles.EMPTY; } RunfilesProvider runfilesProvider = statelessRunfiles != null ? RunfilesProvider.simple(merge(statelessRunfiles, executable, ruleContext)) : RunfilesProvider.withData( // The executable doesn't get into the default runfiles if we have runfiles states. // This is to keep skylark genrule consistent with the original genrule. defaultRunfiles != null ? defaultRunfiles : Runfiles.EMPTY, dataRunfiles != null ? dataRunfiles : Runfiles.EMPTY); builder.addProvider(RunfilesProvider.class, runfilesProvider); Runfiles computedDefaultRunfiles = runfilesProvider.getDefaultRunfiles(); // This works because we only allowed to call a rule *_test iff it's a test type rule. boolean testRule = TargetUtils.isTestRuleName(ruleContext.getRule().getRuleClass()); if (testRule && computedDefaultRunfiles.isEmpty()) { throw new EvalException(loc, "Test rules have to define runfiles"); } if (executable != null || testRule) { RunfilesSupport runfilesSupport = computedDefaultRunfiles.isEmpty() ? null : RunfilesSupport.withExecutable(ruleContext, computedDefaultRunfiles, executable); builder.setRunfilesSupport(runfilesSupport, executable); } if (ruleContext.getRule().getRuleClassObject().isSkylarkTestable()) { SkylarkClassObject actions = ActionsProvider.create(ruleContext.getAnalysisEnvironment().getRegisteredActions()); builder.addSkylarkDeclaredProvider(actions, loc); } try { return builder.build(); } catch (IllegalArgumentException e) { throw new EvalException(loc, e.getMessage()); } }
private DependenciesResult processDependencies( ConfiguredTarget base, RuleContext ruleContext, AndroidStudioInfoFilesProvider.Builder providerBuilder) { // Calculate direct dependencies ImmutableList.Builder<TransitiveInfoCollection> directDepsBuilder = ImmutableList.builder(); for (PrerequisiteAttr prerequisiteAttr : PREREQUISITE_ATTRS) { if (ruleContext.attributes().has(prerequisiteAttr.name, prerequisiteAttr.type)) { directDepsBuilder.addAll(ruleContext.getPrerequisites(prerequisiteAttr.name, Mode.TARGET)); } } List<TransitiveInfoCollection> directDeps = directDepsBuilder.build(); // Add exports from direct dependencies NestedSetBuilder<Label> dependenciesBuilder = NestedSetBuilder.stableOrder(); for (AndroidStudioInfoFilesProvider depProvider : AnalysisUtils.getProviders(directDeps, AndroidStudioInfoFilesProvider.class)) { dependenciesBuilder.addTransitive(depProvider.getExportedDeps()); } for (TransitiveInfoCollection dep : directDeps) { dependenciesBuilder.add(dep.getLabel()); } NestedSet<Label> dependencies = dependenciesBuilder.build(); // Propagate my own exports JavaExportsProvider javaExportsProvider = base.getProvider(JavaExportsProvider.class); if (javaExportsProvider != null) { providerBuilder .exportedDepsBuilder() .addTransitive(javaExportsProvider.getTransitiveExports()); } // android_library without sources exports all its deps if (ruleContext.getRule().getRuleClass().equals("android_library")) { JavaSourceInfoProvider sourceInfoProvider = base.getProvider(JavaSourceInfoProvider.class); boolean hasSources = sourceInfoProvider != null && !sourceInfoProvider.getSourceFiles().isEmpty(); if (!hasSources) { for (TransitiveInfoCollection dep : directDeps) { providerBuilder.exportedDepsBuilder().add(dep.getLabel()); } } } // runtime_deps List<? extends TransitiveInfoCollection> runtimeDeps = ImmutableList.of(); NestedSetBuilder<Label> runtimeDepsBuilder = NestedSetBuilder.stableOrder(); if (ruleContext.attributes().has("runtime_deps", BuildType.LABEL_LIST)) { runtimeDeps = ruleContext.getPrerequisites("runtime_deps", Mode.TARGET); for (TransitiveInfoCollection dep : runtimeDeps) { runtimeDepsBuilder.add(dep.getLabel()); } } // resources @Nullable TransitiveInfoCollection resources = ruleContext.attributes().has("resources", BuildType.LABEL) ? ruleContext.getPrerequisite("resources", Mode.TARGET) : null; // Propagate providers from all prerequisites (deps + runtime_deps) ImmutableList.Builder<TransitiveInfoCollection> prerequisitesBuilder = ImmutableList.builder(); prerequisitesBuilder.addAll(directDeps); prerequisitesBuilder.addAll(runtimeDeps); if (resources != null) { prerequisitesBuilder.add(resources); } List<TransitiveInfoCollection> prerequisites = prerequisitesBuilder.build(); for (AndroidStudioInfoFilesProvider depProvider : AnalysisUtils.getProviders(prerequisites, AndroidStudioInfoFilesProvider.class)) { providerBuilder.ideInfoFilesBuilder().addTransitive(depProvider.getIdeInfoFiles()); providerBuilder.ideInfoTextFilesBuilder().addTransitive(depProvider.getIdeInfoTextFiles()); providerBuilder.ideResolveFilesBuilder().addTransitive(depProvider.getIdeResolveFiles()); } return new DependenciesResult( dependencies, runtimeDepsBuilder.build(), resources != null ? resources.getLabel() : null); }
/** Create the C++ compile and link actions, and the corresponding C++-related providers. */ public Info build() { // Fail early if there is no lipo context collector on the rule - otherwise we end up failing // in lipo optimization. Preconditions.checkState( // 'cc_inc_library' rules do not compile, and thus are not affected by LIPO. ruleContext.getRule().getRuleClass().equals("cc_inc_library") || ruleContext.getRule().isAttrDefined(":lipo_context_collector", BuildType.LABEL)); if (checkDepsGenerateCpp) { for (LanguageDependentFragment dep : AnalysisUtils.getProviders(deps, LanguageDependentFragment.class)) { LanguageDependentFragment.Checker.depSupportsLanguage( ruleContext, dep, CppRuleClasses.LANGUAGE); } } CppModel model = initializeCppModel(); CppCompilationContext cppCompilationContext = initializeCppCompilationContext(model); model.setContext(cppCompilationContext); boolean compileHeaderModules = featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULES); Preconditions.checkState( !compileHeaderModules || cppCompilationContext.getCppModuleMap() != null, "All cc rules must support module maps."); // Create compile actions (both PIC and non-PIC). CcCompilationOutputs ccOutputs = model.createCcCompileActions(); if (!objectFiles.isEmpty() || !picObjectFiles.isEmpty()) { // Merge the pre-compiled object files into the compiler outputs. ccOutputs = new CcCompilationOutputs.Builder() .merge(ccOutputs) .addLTOBitcodeFile(ccOutputs.getLtoBitcodeFiles()) .addObjectFiles(objectFiles) .addPicObjectFiles(picObjectFiles) .build(); } // Create link actions (only if there are object files or if explicitly requested). CcLinkingOutputs ccLinkingOutputs = CcLinkingOutputs.EMPTY; if (emitLinkActionsIfEmpty || !ccOutputs.isEmpty()) { // On some systems, the linker gives an error message if there are no input files. Even with // the check above, this can still happen if there is a .nopic.o or .o files in srcs, but no // other files. To fix that, we'd have to check for each link action individually. // // An additional pre-existing issue is that the header check tokens are dropped if we don't // generate any link actions, effectively disabling header checking in some cases. if (linkType.isStaticLibraryLink()) { // TODO(bazel-team): This can't create the link action for a cc_binary yet. ccLinkingOutputs = model.createCcLinkActions(ccOutputs); } } CcLinkingOutputs originalLinkingOutputs = ccLinkingOutputs; if (!(staticLibraries.isEmpty() && picStaticLibraries.isEmpty() && dynamicLibraries.isEmpty())) { // Merge the pre-compiled libraries (static & dynamic) into the linker outputs. ccLinkingOutputs = new CcLinkingOutputs.Builder() .merge(ccLinkingOutputs) .addStaticLibraries(staticLibraries) .addPicStaticLibraries(picStaticLibraries) .addDynamicLibraries(dynamicLibraries) .addExecutionDynamicLibraries(dynamicLibraries) .build(); } DwoArtifactsCollector dwoArtifacts = DwoArtifactsCollector.transitiveCollector(ccOutputs, deps); Runfiles cppStaticRunfiles = collectCppRunfiles(ccLinkingOutputs, true); Runfiles cppSharedRunfiles = collectCppRunfiles(ccLinkingOutputs, false); // By very careful when adding new providers here - it can potentially affect a lot of rules. // We should consider merging most of these providers into a single provider. Map<Class<? extends TransitiveInfoProvider>, TransitiveInfoProvider> providers = new LinkedHashMap<>(); providers.put( CppRunfilesProvider.class, new CppRunfilesProvider(cppStaticRunfiles, cppSharedRunfiles)); providers.put(CppCompilationContext.class, cppCompilationContext); providers.put( CppDebugFileProvider.class, new CppDebugFileProvider( dwoArtifacts.getDwoArtifacts(), dwoArtifacts.getPicDwoArtifacts())); providers.put(TransitiveLipoInfoProvider.class, collectTransitiveLipoInfo(ccOutputs)); Map<String, NestedSet<Artifact>> outputGroups = new TreeMap<>(); outputGroups.put(OutputGroupProvider.TEMP_FILES, getTemps(ccOutputs)); if (emitCompileProviders) { boolean isLipoCollector = ruleContext.getFragment(CppConfiguration.class).isLipoContextCollector(); boolean processHeadersInDependencies = ruleContext.getFragment(CppConfiguration.class).processHeadersInDependencies(); boolean usePic = CppHelper.usePic(ruleContext, false); outputGroups.put( OutputGroupProvider.FILES_TO_COMPILE, ccOutputs.getFilesToCompile(isLipoCollector, processHeadersInDependencies, usePic)); outputGroups.put( OutputGroupProvider.COMPILATION_PREREQUISITES, CcCommon.collectCompilationPrerequisites(ruleContext, cppCompilationContext)); } // TODO(bazel-team): Maybe we can infer these from other data at the places where they are // used. if (emitCcNativeLibrariesProvider) { providers.put( CcNativeLibraryProvider.class, new CcNativeLibraryProvider(collectNativeCcLibraries(ccLinkingOutputs))); } providers.put( CcExecutionDynamicLibrariesProvider.class, collectExecutionDynamicLibraryArtifacts(ccLinkingOutputs.getExecutionDynamicLibraries())); boolean forcePic = ruleContext.getFragment(CppConfiguration.class).forcePic(); if (emitCcSpecificLinkParamsProvider) { providers.put( CcSpecificLinkParamsProvider.class, new CcSpecificLinkParamsProvider( createCcLinkParamsStore(ccLinkingOutputs, cppCompilationContext, forcePic))); } else { providers.put( CcLinkParamsProvider.class, new CcLinkParamsProvider( createCcLinkParamsStore(ccLinkingOutputs, cppCompilationContext, forcePic))); } return new Info( providers, outputGroups, ccOutputs, ccLinkingOutputs, originalLinkingOutputs, cppCompilationContext); }
private static ConfiguredTarget addStructFields( RuleContext ruleContext, RuleConfiguredTargetBuilder builder, Object target, Artifact executable) throws EvalException { Location loc = null; Runfiles statelessRunfiles = null; Runfiles dataRunfiles = null; Runfiles defaultRunfiles = null; if (target instanceof SkylarkClassObject) { SkylarkClassObject struct = (SkylarkClassObject) target; loc = struct.getCreationLoc(); for (String key : struct.getKeys()) { if (key.equals("files")) { // If we specify files_to_build we don't have the executable in it by default. builder.setFilesToBuild( cast("files", struct, SkylarkNestedSet.class, Artifact.class, loc) .getSet(Artifact.class)); } else if (key.equals("runfiles")) { statelessRunfiles = cast("runfiles", struct, Runfiles.class, loc); } else if (key.equals("data_runfiles")) { dataRunfiles = cast("data_runfiles", struct, Runfiles.class, loc); } else if (key.equals("default_runfiles")) { defaultRunfiles = cast("default_runfiles", struct, Runfiles.class, loc); } else if (!key.equals("executable")) { // We handled executable already. builder.addSkylarkTransitiveInfo(key, struct.getValue(key), loc); } } } if ((statelessRunfiles != null) && (dataRunfiles != null || defaultRunfiles != null)) { throw new EvalException( loc, "Cannot specify the provider 'runfiles' " + "together with 'data_runfiles' or 'default_runfiles'"); } if (statelessRunfiles == null && dataRunfiles == null && defaultRunfiles == null) { // No runfiles specified, set default statelessRunfiles = Runfiles.EMPTY; } RunfilesProvider runfilesProvider = statelessRunfiles != null ? RunfilesProvider.simple(merge(statelessRunfiles, executable)) : RunfilesProvider.withData( // The executable doesn't get into the default runfiles if we have runfiles states. // This is to keep skylark genrule consistent with the original genrule. defaultRunfiles != null ? defaultRunfiles : Runfiles.EMPTY, dataRunfiles != null ? dataRunfiles : Runfiles.EMPTY); builder.addProvider(RunfilesProvider.class, runfilesProvider); Runfiles computedDefaultRunfiles = runfilesProvider.getDefaultRunfiles(); // This works because we only allowed to call a rule *_test iff it's a test type rule. boolean testRule = TargetUtils.isTestRuleName(ruleContext.getRule().getRuleClass()); if (testRule && computedDefaultRunfiles.isEmpty()) { throw new EvalException(loc, "Test rules have to define runfiles"); } if (executable != null || testRule) { RunfilesSupport runfilesSupport = computedDefaultRunfiles.isEmpty() ? null : RunfilesSupport.withExecutable(ruleContext, computedDefaultRunfiles, executable); builder.setRunfilesSupport(runfilesSupport, executable); } try { return builder.build(); } catch (IllegalArgumentException e) { throw new EvalException(loc, e.getMessage()); } }