/* * Copyright 2014-present Facebook, Inc. * * 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.facebook.buck.apple; import com.facebook.buck.cxx.CxxDescriptionEnhancer; import com.facebook.buck.cxx.CxxPlatform; import com.facebook.buck.cxx.FrameworkDependencies; import com.facebook.buck.cxx.LinkerMapMode; import com.facebook.buck.cxx.StripStyle; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.BuildTargets; import com.facebook.buck.model.Flavor; import com.facebook.buck.model.FlavorDomain; import com.facebook.buck.model.Flavored; import com.facebook.buck.model.InternalFlavor; import com.facebook.buck.parser.NoSuchBuildTargetException; import com.facebook.buck.rules.BuildRuleParams; import com.facebook.buck.rules.BuildRuleResolver; import com.facebook.buck.rules.CellPathResolver; import com.facebook.buck.rules.CommonDescriptionArg; import com.facebook.buck.rules.Description; import com.facebook.buck.rules.HasDeclaredDeps; import com.facebook.buck.rules.HasTests; import com.facebook.buck.rules.Hint; import com.facebook.buck.rules.ImplicitDepsInferringDescription; import com.facebook.buck.rules.MetadataProvidingDescription; import com.facebook.buck.rules.TargetGraph; import com.facebook.buck.util.immutables.BuckStyleImmutable; import com.facebook.buck.versions.Version; import com.google.common.base.Predicates; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import java.util.Optional; import org.immutables.value.Value; public class AppleBundleDescription implements Description<AppleBundleDescriptionArg>, Flavored, ImplicitDepsInferringDescription<AppleBundleDescription.AbstractAppleBundleDescriptionArg>, MetadataProvidingDescription<AppleBundleDescriptionArg> { public static final ImmutableSet<Flavor> SUPPORTED_LIBRARY_FLAVORS = ImmutableSet.of(CxxDescriptionEnhancer.STATIC_FLAVOR, CxxDescriptionEnhancer.SHARED_FLAVOR); public static final Flavor WATCH_OS_FLAVOR = InternalFlavor.of("watchos-armv7k"); public static final Flavor WATCH_SIMULATOR_FLAVOR = InternalFlavor.of("watchsimulator-i386"); private static final Flavor WATCH = InternalFlavor.of("watch"); private final AppleBinaryDescription appleBinaryDescription; private final AppleLibraryDescription appleLibraryDescription; private final FlavorDomain<CxxPlatform> cxxPlatformFlavorDomain; private final FlavorDomain<AppleCxxPlatform> appleCxxPlatformsFlavorDomain; private final CxxPlatform defaultCxxPlatform; private final CodeSignIdentityStore codeSignIdentityStore; private final ProvisioningProfileStore provisioningProfileStore; private final AppleConfig appleConfig; public AppleBundleDescription( AppleBinaryDescription appleBinaryDescription, AppleLibraryDescription appleLibraryDescription, FlavorDomain<CxxPlatform> cxxPlatformFlavorDomain, FlavorDomain<AppleCxxPlatform> appleCxxPlatformsFlavorDomain, CxxPlatform defaultCxxPlatform, CodeSignIdentityStore codeSignIdentityStore, ProvisioningProfileStore provisioningProfileStore, AppleConfig appleConfig) { this.appleBinaryDescription = appleBinaryDescription; this.appleLibraryDescription = appleLibraryDescription; this.cxxPlatformFlavorDomain = cxxPlatformFlavorDomain; this.appleCxxPlatformsFlavorDomain = appleCxxPlatformsFlavorDomain; this.defaultCxxPlatform = defaultCxxPlatform; this.codeSignIdentityStore = codeSignIdentityStore; this.provisioningProfileStore = provisioningProfileStore; this.appleConfig = appleConfig; } @Override public Class<AppleBundleDescriptionArg> getConstructorArgType() { return AppleBundleDescriptionArg.class; } @Override public Optional<ImmutableSet<FlavorDomain<?>>> flavorDomains() { ImmutableSet.Builder<FlavorDomain<?>> builder = ImmutableSet.builder(); ImmutableSet<FlavorDomain<?>> localDomains = ImmutableSet.of(AppleDebugFormat.FLAVOR_DOMAIN, AppleDescriptions.INCLUDE_FRAMEWORKS); builder.addAll(localDomains); appleLibraryDescription.flavorDomains().ifPresent(domains -> builder.addAll(domains)); appleBinaryDescription.flavorDomains().ifPresent(domains -> builder.addAll(domains)); return Optional.of(builder.build()); } @Override public boolean hasFlavors(final ImmutableSet<Flavor> flavors) { if (appleLibraryDescription.hasFlavors(flavors)) { return true; } ImmutableSet.Builder<Flavor> flavorBuilder = ImmutableSet.builder(); for (Flavor flavor : flavors) { if (AppleDebugFormat.FLAVOR_DOMAIN.getFlavors().contains(flavor)) { continue; } if (AppleDescriptions.INCLUDE_FRAMEWORKS.getFlavors().contains(flavor)) { continue; } flavorBuilder.add(flavor); } return appleBinaryDescription.hasFlavors(flavorBuilder.build()); } @Override public AppleBundle createBuildRule( TargetGraph targetGraph, BuildRuleParams params, BuildRuleResolver resolver, CellPathResolver cellRoots, AppleBundleDescriptionArg args) throws NoSuchBuildTargetException { AppleDebugFormat flavoredDebugFormat = AppleDebugFormat.FLAVOR_DOMAIN .getValue(params.getBuildTarget()) .orElse(appleConfig.getDefaultDebugInfoFormatForBinaries()); if (!params.getBuildTarget().getFlavors().contains(flavoredDebugFormat.getFlavor())) { return (AppleBundle) resolver.requireRule( params.getBuildTarget().withAppendedFlavors(flavoredDebugFormat.getFlavor())); } if (!AppleDescriptions.INCLUDE_FRAMEWORKS.getValue(params.getBuildTarget()).isPresent()) { return (AppleBundle) resolver.requireRule( params .getBuildTarget() .withAppendedFlavors(AppleDescriptions.NO_INCLUDE_FRAMEWORKS_FLAVOR)); } return AppleDescriptions.createAppleBundle( cxxPlatformFlavorDomain, defaultCxxPlatform, appleCxxPlatformsFlavorDomain, targetGraph, params, resolver, codeSignIdentityStore, provisioningProfileStore, args.getBinary(), args.getExtension(), args.getProductName(), args.getInfoPlist(), args.getInfoPlistSubstitutions(), args.getDeps(), args.getTests(), flavoredDebugFormat, appleConfig.useDryRunCodeSigning(), appleConfig.cacheBundlesAndPackages()); } /** * Propagate the bundle's platform, debug symbol and strip flavors to its dependents which are * other bundles (e.g. extensions) */ @Override public void findDepsForTargetFromConstructorArgs( BuildTarget buildTarget, CellPathResolver cellRoots, AbstractAppleBundleDescriptionArg constructorArg, ImmutableCollection.Builder<BuildTarget> extraDepsBuilder, ImmutableCollection.Builder<BuildTarget> targetGraphOnlyDepsBuilder) { if (!cxxPlatformFlavorDomain.containsAnyOf(buildTarget.getFlavors())) { buildTarget = BuildTarget.builder(buildTarget) .addAllFlavors(ImmutableSet.of(defaultCxxPlatform.getFlavor())) .build(); } Optional<MultiarchFileInfo> fatBinaryInfo = MultiarchFileInfos.create(appleCxxPlatformsFlavorDomain, buildTarget); CxxPlatform cxxPlatform; if (fatBinaryInfo.isPresent()) { AppleCxxPlatform appleCxxPlatform = fatBinaryInfo.get().getRepresentativePlatform(); cxxPlatform = appleCxxPlatform.getCxxPlatform(); } else { cxxPlatform = ApplePlatforms.getCxxPlatformForBuildTarget( cxxPlatformFlavorDomain, defaultCxxPlatform, buildTarget); } String platformName = cxxPlatform.getFlavor().getName(); final Flavor actualWatchFlavor; if (ApplePlatform.isSimulator(platformName)) { actualWatchFlavor = WATCH_SIMULATOR_FLAVOR; } else if (platformName.startsWith(ApplePlatform.IPHONEOS.getName()) || platformName.startsWith(ApplePlatform.WATCHOS.getName())) { actualWatchFlavor = WATCH_OS_FLAVOR; } else { actualWatchFlavor = InternalFlavor.of(platformName); } FluentIterable<BuildTarget> depsExcludingBinary = FluentIterable.from(constructorArg.getDeps()) .filter(Predicates.not(constructorArg.getBinary()::equals)); // Propagate platform flavors. Need special handling for watch to map the pseudo-flavor // watch to the actual watch platform (simulator or device) so can't use // BuildTargets.propagateFlavorsInDomainIfNotPresent() { FluentIterable<BuildTarget> targetsWithPlatformFlavors = depsExcludingBinary.filter(BuildTargets.containsFlavors(cxxPlatformFlavorDomain)); FluentIterable<BuildTarget> targetsWithoutPlatformFlavors = depsExcludingBinary.filter( Predicates.not(BuildTargets.containsFlavors(cxxPlatformFlavorDomain))); FluentIterable<BuildTarget> watchTargets = targetsWithoutPlatformFlavors .filter(BuildTargets.containsFlavor(WATCH)) .transform( input -> BuildTarget.builder(input.withoutFlavors(WATCH)) .addFlavors(actualWatchFlavor) .build()); targetsWithoutPlatformFlavors = targetsWithoutPlatformFlavors.filter(Predicates.not(BuildTargets.containsFlavor(WATCH))); // Gather all the deps now that we've added platform flavors to everything. depsExcludingBinary = targetsWithPlatformFlavors .append(watchTargets) .append( BuildTargets.propagateFlavorDomains( buildTarget, ImmutableSet.of(cxxPlatformFlavorDomain), targetsWithoutPlatformFlavors)); } // Propagate some flavors depsExcludingBinary = BuildTargets.propagateFlavorsInDomainIfNotPresent( StripStyle.FLAVOR_DOMAIN, buildTarget, depsExcludingBinary); depsExcludingBinary = BuildTargets.propagateFlavorsInDomainIfNotPresent( AppleDebugFormat.FLAVOR_DOMAIN, buildTarget, depsExcludingBinary); depsExcludingBinary = BuildTargets.propagateFlavorsInDomainIfNotPresent( LinkerMapMode.FLAVOR_DOMAIN, buildTarget, depsExcludingBinary); if (fatBinaryInfo.isPresent()) { depsExcludingBinary = depsExcludingBinary.append( fatBinaryInfo .get() .getRepresentativePlatform() .getCodesignProvider() .getParseTimeDeps()); } else { depsExcludingBinary = depsExcludingBinary.append( appleCxxPlatformsFlavorDomain .getValue(buildTarget) .map(platform -> platform.getCodesignProvider().getParseTimeDeps()) .orElse(ImmutableSet.of())); } extraDepsBuilder.addAll(depsExcludingBinary); } @Override public <U> Optional<U> createMetadata( BuildTarget buildTarget, BuildRuleResolver resolver, AppleBundleDescriptionArg args, Optional<ImmutableMap<BuildTarget, Version>> selectedVersions, Class<U> metadataClass) throws NoSuchBuildTargetException { if (metadataClass.isAssignableFrom(FrameworkDependencies.class)) { // Bundles should be opaque to framework dependencies. return Optional.empty(); } return resolver.requireMetadata(args.getBinary(), metadataClass); } @BuckStyleImmutable @Value.Immutable interface AbstractAppleBundleDescriptionArg extends CommonDescriptionArg, HasAppleBundleFields, HasDeclaredDeps, HasTests { BuildTarget getBinary(); @Override @Hint(isDep = false) @Value.NaturalOrder ImmutableSortedSet<BuildTarget> getDeps(); } }