/** * Registers actions required to build an application. This includes any {@link * BundleSupport#registerActions(ObjcProvider) bundle} and bundle merge actions, signing this * application if appropriate and combining several single-architecture binaries into one * multi-architecture binary. * * @return this application support * @throws InterruptedException */ ReleaseBundlingSupport registerActions() throws InterruptedException { bundleSupport.registerActions(objcProvider); registerCombineArchitecturesAction(); registerTransformAndCopyBreakpadFilesAction(); registerSwiftStdlibActionsIfNecessary(); AppleConfiguration appleConfiguration = ruleContext.getFragment(AppleConfiguration.class); Artifact ipaOutput = ruleContext.getImplicitOutputArtifact(IPA); Artifact maybeSignedIpa; if (appleConfiguration.getBundlingPlatform() == Platform.IOS_SIMULATOR) { maybeSignedIpa = ipaOutput; } else if (attributes.provisioningProfile() == null) { throw new IllegalStateException(DEVICE_NO_PROVISIONING_PROFILE); } else { maybeSignedIpa = registerBundleSigningActions(ipaOutput); } registerEmbedLabelPlistAction(); registerEnvironmentPlistAction(); registerAutomaticPlistAction(); if (ObjcRuleClasses.useLaunchStoryboard(ruleContext)) { registerLaunchStoryboardPlistAction(); } BundleMergeControlBytes bundleMergeControlBytes = new BundleMergeControlBytes( bundling, maybeSignedIpa, appleConfiguration, bundleSupport.targetDeviceFamilies()); registerBundleMergeActions( maybeSignedIpa, bundling.getBundleContentArtifacts(), bundleMergeControlBytes); return this; }
@Override public void registerActions(RuleContext ruleContext, AndroidSemantics semantics) { Preconditions.checkNotNull( apkName, "APK name must be set to create progress messages for APK actions."); if (signedApk != null) { Artifact intermediateUnsignedApk = unsignedApk; if (intermediateUnsignedApk == null) { // If the caller did not request an unsigned APK, we still need to construct one so that // we can sign it. So we make up an intermediate artifact. intermediateUnsignedApk = AndroidBinary.getDxArtifact(ruleContext, "unsigned_" + signedApk.getFilename()); } ruleContext.registerAction( buildApk(ruleContext, intermediateUnsignedApk, null, "Generating unsigned " + apkName)); Artifact apkToSign = intermediateUnsignedApk; if (zipalignApk) { apkToSign = AndroidBinary.getDxArtifact(ruleContext, "zipaligned_" + signedApk.getFilename()); ruleContext.registerAction(zipalignApk(ruleContext, intermediateUnsignedApk, apkToSign)); } ruleContext.registerAction( signApk( ruleContext, semantics.getApkDebugSigningKey(ruleContext), apkToSign, signedApk)); } else if (unsignedApk != null) { ruleContext.registerAction( buildApk(ruleContext, unsignedApk, null, "Generating unsigned " + apkName)); } }
/** * Returns the xcode version number corresponding to the {@code --xcode_version} flag, if there is * an available {@code xcode_version} target which recognizes the flag value as either an official * version or an alias. Returns null if no such target is found. */ @Nullable private DottedVersion resolveExplicitlyDefinedVersion(RuleContext ruleContext) { AppleConfiguration configuration = ruleContext.getFragment(AppleConfiguration.class); Optional<DottedVersion> versionOverrideFlag = configuration.getXcodeVersionOverrideFlag(); if (versionOverrideFlag.isPresent()) { // The version override flag is not necessarily an actual version - it may be a version // alias. DottedVersion explicitVerison = aliasesToVersionMap(ruleContext).get(versionOverrideFlag.get().toString()); if (explicitVerison != null) { return explicitVerison; } } else { // No override specified. Use default. XcodeVersionProvider defaultProvider = ruleContext.getPrerequisite( XcodeConfigRule.DEFAULT_ATTR_NAME, Mode.TARGET, XcodeVersionProvider.class); if (defaultProvider != null) { return defaultProvider.getVersion(); } } boolean requireDefinedVersions = ruleContext.attributes().get(XcodeConfigRule.REQUIRE_DEFINED_VERSIONS_ATTR_NAME, BOOLEAN); if (requireDefinedVersions) { ruleContext.ruleError( "xcode version config required an explicitly defined version, but none was available"); } return null; }
private void registerBundleMergeActions( Artifact ipaUnsigned, NestedSet<Artifact> bundleContentArtifacts, BundleMergeControlBytes controlBytes) { Artifact bundleMergeControlArtifact = ObjcRuleClasses.artifactByAppendingToBaseName(ruleContext, ".ipa-control"); ruleContext.registerAction( new BinaryFileWriteAction( ruleContext.getActionOwner(), bundleMergeControlArtifact, controlBytes, /*makeExecutable=*/ false)); ruleContext.registerAction( new SpawnAction.Builder() .setMnemonic("IosBundle") .setProgressMessage("Bundling iOS application: " + ruleContext.getLabel()) .setExecutable(attributes.bundleMergeExecutable()) .addInputArgument(bundleMergeControlArtifact) .addTransitiveInputs(bundleContentArtifacts) .addOutput(ipaUnsigned) .setVerboseFailuresAndSubcommandsInEnv() .build(ruleContext)); }
/** 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; }
@Override public void registerActions(RuleContext ruleContext, AndroidSemantics semantics) { Preconditions.checkNotNull( apkName, "APK name must be set to create progress messages for APK actions."); if (unsignedApk != null) { ruleContext.registerAction( buildApk(ruleContext, unsignedApk, null, "Generating unsigned " + apkName)); } if (signedApk != null) { // Legacy signing destroys zip aligning, so if zip aligning is requested we build an // intermediate APK that is signed but not zip aligned then zip align it. If zip aligning // is not requested then the output of the buildApk step is the final apk. Artifact intermediateSignedApk; if (zipalignApk) { intermediateSignedApk = AndroidBinary.getDxArtifact(ruleContext, "signed_" + signedApk.getFilename()); } else { intermediateSignedApk = signedApk; } ruleContext.registerAction( buildApk( ruleContext, intermediateSignedApk, semantics.getApkDebugSigningKey(ruleContext), "Generating signed " + apkName)); if (zipalignApk) { ruleContext.registerAction(zipalignApk(ruleContext, intermediateSignedApk, signedApk)); } } }
private static Collection<Artifact> getTargetListAttribute( RuleContext ruleContext, String attributeName) { return (ruleContext.attributes().has(attributeName, BuildType.LABEL_LIST) && ruleContext.getAttributeMode(attributeName) == Mode.TARGET) ? ruleContext.getPrerequisiteArtifacts(attributeName, Mode.TARGET).list() : ImmutableList.<Artifact>of(); }
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; }
private static void validateManifest(RuleContext ruleContext) throws RuleErrorException { if (ruleContext.getPrerequisiteArtifact("manifest", Mode.TARGET) == null) { ruleContext.attributeError( "manifest", "manifest is required when resource_files or assets are defined."); throw new RuleErrorException(); } }
/** * 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())); }
public ApplicationManifest addStubApplication(RuleContext ruleContext) { Artifact stubManifest = ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.STUB_APPLICATON_MANIFEST); SpawnAction.Builder builder = new SpawnAction.Builder() .setExecutable(ruleContext.getExecutablePrerequisite("$stubify_manifest", Mode.HOST)) .setProgressMessage("Injecting stub application") .setMnemonic("InjectStubApplication") .addArgument("--input_manifest") .addInputArgument(manifest) .addArgument("--output_manifest") .addOutputArgument(stubManifest) .addArgument("--output_datafile") .addOutputArgument( ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.STUB_APPLICATION_DATA)); String overridePackage = getOverridePackage(ruleContext); if (overridePackage != null) { builder.addArgument("--override_package"); builder.addArgument(overridePackage); } ruleContext.registerAction(builder.build(ruleContext)); return new ApplicationManifest(stubManifest); }
public ApplicationManifest createSplitManifest( RuleContext ruleContext, String splitName, boolean hasCode) { // aapt insists that manifests be called AndroidManifest.xml, even though they have to be // explicitly designated as manifests on the command line Artifact result = AndroidBinary.getDxArtifact(ruleContext, "split_" + splitName + "/AndroidManifest.xml"); SpawnAction.Builder builder = new SpawnAction.Builder() .setExecutable( ruleContext.getExecutablePrerequisite("$build_split_manifest", Mode.HOST)) .setProgressMessage("Creating manifest for split " + splitName) .setMnemonic("AndroidBuildSplitManifest") .addArgument("--main_manifest") .addInputArgument(manifest) .addArgument("--split_manifest") .addOutputArgument(result) .addArgument("--split") .addArgument(splitName) .addArgument(hasCode ? "--hascode" : "--nohascode"); String overridePackage = getOverridePackage(ruleContext); if (overridePackage != null) { builder.addArgument("--override_package").addArgument(overridePackage); } ruleContext.registerAction(builder.build(ruleContext)); return new ApplicationManifest(result); }
private ImmutableList<Artifact> generatedOutputArtifacts(FileType newFileType) { ImmutableList.Builder<Artifact> builder = new ImmutableList.Builder<>(); for (Artifact protoFile : getFilteredProtoSources()) { String protoFileName = FileSystemUtils.removeExtension(protoFile.getFilename()); String generatedOutputName; if (attributes.outputsCpp()) { generatedOutputName = protoFileName; } else if (usesProtobufLibrary()) { // The protobuf library generates filenames with some slight modifications. generatedOutputName = generateProtobufFilename(protoFileName); } else { String lowerUnderscoreBaseName = protoFileName.replace('-', '_').toLowerCase(); generatedOutputName = LOWER_UNDERSCORE.to(UPPER_CAMEL, lowerUnderscoreBaseName); } PathFragment generatedFilePath = new PathFragment( protoFile.getRootRelativePath().getParentDirectory(), new PathFragment(generatedOutputName)); PathFragment outputFile = FileSystemUtils.appendExtension(generatedFilePath, newFileType.getExtensions().get(0)); if (outputFile != null) { builder.add( ruleContext.getUniqueDirectoryArtifact( UNIQUE_DIRECTORY_NAME, outputFile, ruleContext.getBinOrGenfilesDirectory())); } } return builder.build(); }
/** Registers an action to generate a runner script based on a template. */ ReleaseBundlingSupport registerGenerateRunnerScriptAction( Artifact runnerScript, Artifact ipaInput) { ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext); String escapedSimDevice = ShellUtils.shellEscape(objcConfiguration.getIosSimulatorDevice()); String escapedSdkVersion = ShellUtils.shellEscape(objcConfiguration.getIosSimulatorVersion()); ImmutableList<Substitution> substitutions = ImmutableList.of( Substitution.of("%app_name%", ruleContext.getLabel().getName()), Substitution.of("%ipa_file%", ipaInput.getRootRelativePath().getPathString()), Substitution.of("%sim_device%", escapedSimDevice), Substitution.of("%sdk_version%", escapedSdkVersion), Substitution.of("%iossim%", attributes.iossim().getRootRelativePath().getPathString()), Substitution.of( "%std_redirect_dylib_path%", attributes.stdRedirectDylib().getRootRelativePath().getPathString())); ruleContext.registerAction( new TemplateExpansionAction( ruleContext.getActionOwner(), attributes.runnerScriptTemplate(), runnerScript, substitutions, true)); return this; }
/** Registers an action to copy Swift standard library dylibs into app bundle. */ private void registerSwiftStdlibActionsIfNecessary() { if (!objcProvider.is(USES_SWIFT)) { return; } AppleConfiguration appleConfiguration = ruleContext.getFragment(AppleConfiguration.class); CustomCommandLine.Builder commandLine = CustomCommandLine.builder() .addPath(intermediateArtifacts.swiftFrameworksFileZip().getExecPath()) .add("--platform") .add(AppleToolchain.swiftPlatform(appleConfiguration)) .addExecPath("--scan-executable", intermediateArtifacts.combinedArchitectureBinary()); ruleContext.registerAction( ObjcRuleClasses.spawnXcrunActionBuilder(ruleContext) .setMnemonic("SwiftStdlibCopy") .setExecutable(attributes.swiftStdlibToolWrapper()) .setCommandLine(commandLine.build()) .addOutput(intermediateArtifacts.swiftFrameworksFileZip()) .addInput(intermediateArtifacts.combinedArchitectureBinary()) // TODO(dmaclach): Adding realpath and xcrunwrapper should not be required once // https://github.com/google/bazel/issues/285 is fixed. .addInput(attributes.realpath()) .addInput(CompilationSupport.xcrunwrapper(ruleContext).getExecutable()) .build(ruleContext)); }
public CppModel(RuleContext ruleContext, CppSemantics semantics) { this.ruleContext = Preconditions.checkNotNull(ruleContext); this.semantics = semantics; configuration = ruleContext.getConfiguration(); cppConfiguration = ruleContext.getFragment(CppConfiguration.class); features = CppHelper.getToolchain(ruleContext).getFeatures(); }
/** * Validity-checks that no group has its environment referenced in both the "compatible with" * and restricted to" attributes. Returns true if all is good, returns false and reports * appropriate errors if there are any problems. */ boolean validateEnvironmentSpecifications() { ImmutableCollection<EnvironmentGroup> restrictionGroups = restrictionEnvironments.getGroups(); boolean hasErrors = false; for (EnvironmentGroup group : compatibilityEnvironments.getGroups()) { if (restrictionGroups.contains(group)) { // To avoid error-spamming the user, when we find a conflict we only report one example // environment from each attribute for that group. Label compatibilityEnv = compatibilityEnvironments.getEnvironments(group).iterator().next(); Label restrictionEnv = restrictionEnvironments.getEnvironments(group).iterator().next(); if (compatibilityEnv.equals(restrictionEnv)) { ruleContext.attributeError( compatibilityAttr, compatibilityEnv + " cannot appear both here and in " + restrictionAttr); } else { ruleContext.attributeError( compatibilityAttr, compatibilityEnv + " and " + restrictionEnv + " belong to the same environment group. They should be declared " + "together either here or in " + restrictionAttr); } hasErrors = true; } } return !hasErrors; }
/** * The artifact for the .o file that should be generated when compiling the {@code source} * artifact. */ public Artifact objFile(Artifact source) { if (source.isTreeArtifact()) { PathFragment rootRelativePath = source.getRootRelativePath().replaceName("obj_files"); return ruleContext.getTreeArtifact(rootRelativePath, ruleContext.getBinOrGenfilesDirectory()); } else { return inUniqueObjsDir(source, ".o"); } }
private void registerAutomaticPlistAction() { ruleContext.registerAction( new FileWriteAction( ruleContext.getActionOwner(), getGeneratedAutomaticPlist(), automaticEntries().toASCIIPropertyList(), /*makeExecutable=*/ false)); }
/** Returns the options file, or null if it was not specified. */ @Nullable Artifact getOptionsFile() { if (ruleContext.attributes().has(ObjcProtoLibraryRule.OPTIONS_FILE_ATTR, LABEL)) { return ruleContext.getPrerequisiteArtifact( ObjcProtoLibraryRule.OPTIONS_FILE_ATTR, Mode.HOST); } return null; }
private void registerProtoInputListFileAction() { ruleContext.registerAction( new FileWriteAction( ruleContext.getActionOwner(), getProtoInputListFile(), getProtoInputListFileContents(), false)); }
/** * Returns the list of proto files that were added directly into the deps attributes. This way * of specifying the protos is deprecated, and displays a warning when the target does so. */ private ImmutableList<Artifact> getProtoDepsFiles() { PrerequisiteArtifacts prerequisiteArtifacts = ruleContext.getPrerequisiteArtifacts("deps", Mode.TARGET); ImmutableList<Artifact> protos = prerequisiteArtifacts.filter(FileType.of(".proto")).list(); if (!protos.isEmpty()) { ruleContext.attributeWarning("deps", FILES_DEPRECATED_WARNING); } return protos; }
@Nullable Artifact provisioningProfile() { Artifact explicitProvisioningProfile = ruleContext.getPrerequisiteArtifact("provisioning_profile", Mode.TARGET); if (explicitProvisioningProfile != null) { return explicitProvisioningProfile; } return ruleContext.getPrerequisiteArtifact(":default_provisioning_profile", Mode.TARGET); }
private PathFragment getWorkspaceRelativeOutputDir() { // Generate sources in a package-and-rule-scoped directory; adds both the // package-and-rule-scoped directory and the header-containing-directory to the include path // of dependers. PathFragment rootRelativeOutputDir = ruleContext.getUniqueDirectory(UNIQUE_DIRECTORY_NAME); return new PathFragment( ruleContext.getBinOrGenfilesDirectory().getExecPath(), rootRelativeOutputDir); }
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); }
private static void validateAssetsAndAssetsDir(RuleContext ruleContext) throws RuleErrorException { if (ruleContext.attributes().isAttributeValueExplicitlySpecified("assets") ^ ruleContext.attributes().isAttributeValueExplicitlySpecified("assets_dir")) { ruleContext.ruleError( "'assets' and 'assets_dir' should be either both empty or both non-empty"); throw new RuleErrorException(); } }
/** * 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; }
private TransitiveInfoCollection selectDep( RuleContext ruleContext, String attribute, Label label) { for (TransitiveInfoCollection dep : ruleContext.getPrerequisites(attribute, Mode.TARGET)) { if (dep.getLabel().equals(label)) { return dep; } } return ruleContext.getPrerequisites(attribute, Mode.TARGET).get(0); }
private CppModuleMap createCrosstoolModuleMap(RuleContext ruleContext) { if (ruleContext.getPrerequisite("module_map", Mode.HOST) == null) { return null; } Artifact moduleMapArtifact = ruleContext.getPrerequisiteArtifact("module_map", Mode.HOST); if (moduleMapArtifact == null) { return null; } return new CppModuleMap(moduleMapArtifact, "crosstool"); }
/** Returns the list of portable proto filters. */ ImmutableList<Artifact> getPortableProtoFilters() { if (ruleContext .attributes() .has(ObjcProtoLibraryRule.PORTABLE_PROTO_FILTERS_ATTR, LABEL_LIST)) { return ruleContext .getPrerequisiteArtifacts(ObjcProtoLibraryRule.PORTABLE_PROTO_FILTERS_ATTR, Mode.HOST) .list(); } return ImmutableList.of(); }