// Copyright 2016 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.objc; import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.BundlingRule.FAMILIES_ATTR; import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.ReleaseBundlingRule.APP_ICON_ATTR; import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.ReleaseBundlingRule.BUNDLE_ID_ATTR; import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.ReleaseBundlingRule.DEFAULT_PROVISIONING_PROFILE_ATTR; import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.ReleaseBundlingRule.LAUNCH_IMAGE_ATTR; import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.ReleaseBundlingRule.LAUNCH_STORYBOARD_ATTR; import static com.google.devtools.build.lib.rules.objc.ObjcRuleClasses.ReleaseBundlingRule.PROVISIONING_PROFILE_ATTR; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.analysis.RuleContext; 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.rules.objc.TargetDeviceFamily.InvalidFamilyNameException; import com.google.devtools.build.lib.rules.objc.TargetDeviceFamily.RepeatedFamilyNameException; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.util.Preconditions; import java.util.List; /** * Contains information regarding the creation of a released bundle such as an application or * extension. The information which generally includes app icons, launch image, targeted devices and * other data for potential signing is used to create a releasable bundle out of the bundle created * using {@link Bundling} object. * * @deprecated The native bundling rules have been deprecated. This class will be removed in the * future. */ @Deprecated @Immutable final class ReleaseBundling { static final class Builder { private Artifact ipaArtifact; private String bundleId; private String primaryBundleId; private String fallbackBundleId; private String appIcon; private String launchImage; private Artifact launchStoryboard; private Artifact provisioningProfile; private String provisioningProfileAttributeName; private final NestedSetBuilder<Artifact> infoplistInputs = NestedSetBuilder.stableOrder(); private Iterable<Artifact> infoPlistsFromRule; private ImmutableSet<TargetDeviceFamily> families; private IntermediateArtifacts intermediateArtifacts; private String artifactPrefix; private Artifact entitlements; private Artifact extraEntitlements; public Builder setIpaArtifact(Artifact ipaArtifact) { this.ipaArtifact = ipaArtifact; return this; } public Builder setBundleId(String bundleId) { this.bundleId = bundleId; return this; } public Builder setPrimaryBundleId(String primaryId) { this.primaryBundleId = primaryId; return this; } public Builder setFallbackBundleId(String fallbackId) { this.fallbackBundleId = fallbackId; return this; } public Builder setAppIcon(String appIcon) { this.appIcon = appIcon; return this; } public Builder setLaunchImage(String launchImage) { this.launchImage = launchImage; return this; } public Builder setLaunchStoryboard(Artifact launchStoryboard) { this.launchStoryboard = launchStoryboard; return this; } public Builder setProvisioningProfile(Artifact provisioningProfile) { this.provisioningProfile = provisioningProfile; return this; } public Builder setProvisioningProfileAttributeName(String provisioningProfileAttributeName) { this.provisioningProfileAttributeName = provisioningProfileAttributeName; return this; } public Builder addInfoplistInput(Artifact infoPlist) { this.infoplistInputs.add(infoPlist); return this; } public Builder addInfoplistInputs(Iterable<Artifact> infoplists) { this.infoplistInputs.addAll(infoplists); return this; } public Builder setInfoPlistsFromRule(Iterable<Artifact> infoPlistsFromRule) { this.infoPlistsFromRule = infoPlistsFromRule; return this; } public Builder setIntermediateArtifacts(IntermediateArtifacts intermediateArtifacts) { this.intermediateArtifacts = intermediateArtifacts; return this; } public Builder setTargetDeviceFamilies(ImmutableSet<TargetDeviceFamily> families) { this.families = families; return this; } public Builder setArtifactPrefix(String artifactPrefix) { this.artifactPrefix = artifactPrefix; return this; } public Builder setEntitlements(Artifact entitlements) { this.entitlements = entitlements; return this; } public ReleaseBundling build() { Preconditions.checkNotNull(intermediateArtifacts, "intermediateArtifacts"); Preconditions.checkNotNull(families, FAMILIES_ATTR); return new ReleaseBundling( ipaArtifact, bundleId, primaryBundleId, fallbackBundleId, appIcon, launchImage, launchStoryboard, provisioningProfile, provisioningProfileAttributeName, infoplistInputs.build(), infoPlistsFromRule, families, intermediateArtifacts, artifactPrefix, entitlements, extraEntitlements); } } /** * Returns a {@link ReleaseBundling} object constructed using the information available in given * context. */ public static ReleaseBundling releaseBundling(RuleContext ruleContext) throws InterruptedException { Preconditions.checkState(!Strings.isNullOrEmpty( ruleContext.attributes().get(BUNDLE_ID_ATTR, Type.STRING)), "requires a bundle_id value"); String primaryBundleId = null; String fallbackBundleId = null; Artifact provisioningProfile; if (ruleContext.attributes().isAttributeValueExplicitlySpecified(BUNDLE_ID_ATTR)) { primaryBundleId = ruleContext.attributes().get(BUNDLE_ID_ATTR, Type.STRING); } else { fallbackBundleId = ruleContext.attributes().get(BUNDLE_ID_ATTR, Type.STRING); } Artifact explicitProvisioningProfile = ruleContext.getPrerequisiteArtifact(PROVISIONING_PROFILE_ATTR, Mode.TARGET); if (explicitProvisioningProfile != null) { provisioningProfile = explicitProvisioningProfile; } else { provisioningProfile = ruleContext.getPrerequisiteArtifact(DEFAULT_PROVISIONING_PROFILE_ATTR, Mode.TARGET); } ImmutableSet<TargetDeviceFamily> families = null; List<String> rawFamilies = ruleContext.attributes().get(FAMILIES_ATTR, Type.STRING_LIST); try { families = ImmutableSet.copyOf(TargetDeviceFamily.fromNamesInRule(rawFamilies)); } catch (InvalidFamilyNameException | RepeatedFamilyNameException e) { families = ImmutableSet.of(); } if (families.isEmpty()) { ruleContext.attributeError(FAMILIES_ATTR, INVALID_FAMILIES_ERROR); } return new ReleaseBundling.Builder() .setIpaArtifact(ruleContext.getImplicitOutputArtifact(ReleaseBundlingSupport.IPA)) .setBundleId(ruleContext.attributes().get(BUNDLE_ID_ATTR, Type.STRING)) .setPrimaryBundleId(primaryBundleId) .setFallbackBundleId(fallbackBundleId) .setAppIcon(Strings.emptyToNull(ruleContext.attributes().get(APP_ICON_ATTR, Type.STRING))) .setLaunchImage(Strings.emptyToNull( ruleContext.attributes().get(LAUNCH_IMAGE_ATTR, Type.STRING))) .setLaunchStoryboard( ruleContext.getPrerequisiteArtifact(LAUNCH_STORYBOARD_ATTR, Mode.TARGET)) .setProvisioningProfile(provisioningProfile) .setProvisioningProfileAttributeName(PROVISIONING_PROFILE_ATTR) .setTargetDeviceFamilies(families) .setIntermediateArtifacts(ObjcRuleClasses.intermediateArtifacts(ruleContext)) .setEntitlements(ruleContext.getPrerequisiteArtifact("entitlements", Mode.TARGET)) .build(); } @VisibleForTesting static final String INVALID_FAMILIES_ERROR = "Expected one or two strings from the list 'iphone', 'ipad'"; private final Artifact ipaArtifact; private final String bundleId; private final String fallbackBundleId; private final String primaryBundleId; private final String appIcon; private final String launchImage; private final Artifact launchStoryboard; private final Artifact provisioningProfile; private final String provisioningProfileAttributeName; private final NestedSet<Artifact> infoplistInputs; private final ImmutableSet<TargetDeviceFamily> families; private final IntermediateArtifacts intermediateArtifacts; private final Iterable<Artifact> infoPlistsFromRule; private final String artifactPrefix; private final Artifact entitlements; private ReleaseBundling( Artifact ipaArtifact, String bundleId, String primaryBundleId, String fallbackBundleId, String appIcon, String launchImage, Artifact launchStoryboard, Artifact provisioningProfile, String provisioningProfileAttributeName, NestedSet<Artifact> infoplistInputs, Iterable<Artifact> infoPlistsFromRule, ImmutableSet<TargetDeviceFamily> families, IntermediateArtifacts intermediateArtifacts, String artifactPrefix, Artifact entitlements, Artifact extraEntitlements) { this.ipaArtifact = Preconditions.checkNotNull(ipaArtifact); this.bundleId = bundleId; this.primaryBundleId = primaryBundleId; this.fallbackBundleId = fallbackBundleId; this.appIcon = appIcon; this.launchImage = launchImage; this.launchStoryboard = launchStoryboard; this.provisioningProfile = provisioningProfile; this.provisioningProfileAttributeName = Preconditions.checkNotNull(provisioningProfileAttributeName); this.infoplistInputs = Preconditions.checkNotNull(infoplistInputs); this.infoPlistsFromRule = infoPlistsFromRule; this.families = Preconditions.checkNotNull(families); this.intermediateArtifacts = Preconditions.checkNotNull(intermediateArtifacts); this.artifactPrefix = artifactPrefix; this.entitlements = entitlements; } /** * Returns the {@link Artifact} containing the final ipa bundle. */ public Artifact getIpaArtifact() { return ipaArtifact; } /** * Returns the identifier of this bundle. */ public String getBundleId() { return bundleId; } /** * Returns primary bundle ID to use, can be null. */ public String getPrimaryBundleId() { return primaryBundleId; } /** * Returns fallback bundle ID to use when primary isn't set. */ public String getFallbackBundleId() { return fallbackBundleId; } /** * Returns the app icon name for this bundle, can be null. */ public String getAppIcon() { return appIcon; } /** * Returns the launch image name for this bundle, can be null. */ public String getLaunchImage() { return launchImage; } /** * Returns an {@link Artifact} containing launch storyboard for this bundle, can be null. */ public Artifact getLaunchStoryboard() { return launchStoryboard; } /** * Returns an {@link Artifact} containing provisioning profile used to sign this bundle, * can be null. */ public Artifact getProvisioningProfile() { return provisioningProfile; } /** * Returns the list of plists to be merged to final bundle. */ public NestedSet<Artifact> getInfoplistInputs() { return infoplistInputs; } /** * Returns the list of {@link TargetDeviceFamily} values this bundle is targeting. * If empty, the default values specified by {@link FAMILIES_ATTR} will be used. */ public ImmutableSet<TargetDeviceFamily> getTargetDeviceFamilies() { return families; } /** * Returns {@link IntermediateArtifacts} used to create this bundle. */ public IntermediateArtifacts getIntermediateArtifacts() { return intermediateArtifacts; } /** * Returns the name of the attribute which is used to specifiy the provisioning profile. */ public String getProvisioningProfileAttrName() { return provisioningProfileAttributeName; } /** * Adds any info plists specified in the given rule's {@code infoplists} attribute as inputs to * this bundle's {@code Info.plist} (which is merged from any such added plists plus some * additional information). */ public Iterable<Artifact> getInfoPlistsFromRule() { return infoPlistsFromRule; } /** * Returns the prefix to be added to all generated artifact names, can be null. This is useful * to disambiguate artifacts for multiple bundles created with different names withing same rule. */ public String getArtifactPrefix() { return artifactPrefix; } /** * Returns an {@link Artifact} containing the entitlements used to sign this bundle for * non-simulator builds; can be null. */ public Artifact getEntitlements() { return entitlements; } }