Skip to content

Commit

Permalink
[Apple] Add support to AppClips (facebook#2552)
Browse files Browse the repository at this point in the history
* Add app clip support

* Add integration test

* fix target name at Integration test

* fix typo

* Fix bundle ProductType

Co-authored-by: Lucas Marçal <[email protected]>
  • Loading branch information
Lcsmarcal and Lucas Marçal authored Oct 20, 2020
1 parent 32440f9 commit ae2347e
Show file tree
Hide file tree
Showing 24 changed files with 300 additions and 7 deletions.
3 changes: 2 additions & 1 deletion src/com/facebook/buck/apple/AppleBinaryDescription.java
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,8 @@ private BuildRule createBundleBuildRule(
swiftBuckConfig.getUseLipoThin(),
cxxBuckConfig.shouldCacheStrip(),
appleConfig.useEntitlementsWhenAdhocCodeSigning(),
Predicates.alwaysTrue());
Predicates.alwaysTrue(),
Optional.empty());
}

private BuildRule createBinary(
Expand Down
9 changes: 8 additions & 1 deletion src/com/facebook/buck/apple/AppleBundle.java
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ public class AppleBundle extends AbstractBuildRule
private final Duration codesignTimeout;
private final BuildRuleParams buildRuleParams;
private BuildableSupport.DepsSupplier depsSupplier;
private final Optional<Boolean> isAppClip;

AppleBundle(
BuildTarget buildTarget,
Expand Down Expand Up @@ -224,7 +225,8 @@ public class AppleBundle extends AbstractBuildRule
Duration codesignTimeout,
boolean copySwiftStdlibToFrameworks,
boolean useLipoThin,
boolean useEntitlementsWhenAdhocCodeSigning) {
boolean useEntitlementsWhenAdhocCodeSigning,
Optional<Boolean> isAppClip) {
super(buildTarget, projectFilesystem);
this.buildRuleParams = params;
this.extension =
Expand Down Expand Up @@ -301,6 +303,7 @@ public class AppleBundle extends AbstractBuildRule
this.useLipoThin = useLipoThin;
this.useEntitlementsWhenAdhocCodeSigning = useEntitlementsWhenAdhocCodeSigning;
this.depsSupplier = BuildableSupport.buildDepsSupplier(this, graphBuilder);
this.isAppClip = isAppClip;
}

public static String getBinaryName(BuildTarget buildTarget, Optional<String> productName) {
Expand Down Expand Up @@ -991,6 +994,10 @@ private ImmutableMap<String, NSObject> getInfoPlistAdditionalKeys() {
return keys.build();
}

public Boolean getIsAppClip() {
return isAppClip.orElse(false);
}

@Override
public boolean isTestedBy(BuildTarget testRule) {
if (tests.contains(testRule)) {
Expand Down
3 changes: 2 additions & 1 deletion src/com/facebook/buck/apple/AppleBundleDescription.java
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ public AppleBundle createBuildRule(
swiftBuckConfig.getUseLipoThin(),
cxxBuckConfig.shouldCacheStrip(),
appleConfig.useEntitlementsWhenAdhocCodeSigning(),
resourceFilter);
resourceFilter,
args.getIsAppClip());
}

/**
Expand Down
3 changes: 3 additions & 0 deletions src/com/facebook/buck/apple/AppleBundleDestination.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
public enum AppleBundleDestination {
RESOURCES,
FRAMEWORKS,
APPCLIPS,
EXECUTABLES,
PLUGINS,
XPCSERVICES;
Expand All @@ -43,6 +44,8 @@ public Path getPath(AppleBundleDestinations destinations) {
return destinations.getResourcesPath();
case EXECUTABLES:
return destinations.getExecutablesPath();
case APPCLIPS:
return destinations.getAppClipsPath();
case FRAMEWORKS:
return destinations.getFrameworksPath();
case PLUGINS:
Expand Down
7 changes: 7 additions & 0 deletions src/com/facebook/buck/apple/AppleBundleDestinations.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ abstract class AppleBundleDestinations implements AddsToRuleKey {
@AddToRuleKey(stringify = true)
public abstract Path getExecutablesPath();

@AddToRuleKey(stringify = true)
public abstract Path getAppClipsPath();

@AddToRuleKey(stringify = true)
public abstract Path getFrameworksPath();

Expand Down Expand Up @@ -62,6 +65,7 @@ abstract class AppleBundleDestinations implements AddsToRuleKey {
OSX_CONTENTS_PATH,
OSX_CONTENTS_PATH.resolve("Resources"),
OSX_CONTENTS_PATH.resolve("MacOS"),
OSX_CONTENTS_PATH,
OSX_CONTENTS_PATH.resolve("Frameworks"),
OSX_CONTENTS_PATH.resolve("PlugIns"),
OSX_CONTENTS_PATH,
Expand All @@ -76,6 +80,7 @@ abstract class AppleBundleDestinations implements AddsToRuleKey {
OSX_FRAMEWORK_CONTENTS_PATH.resolve("Resources"),
OSX_FRAMEWORK_CONTENTS_PATH.resolve("Resources"),
OSX_FRAMEWORK_CONTENTS_PATH,
OSX_FRAMEWORK_CONTENTS_PATH,
OSX_FRAMEWORK_CONTENTS_PATH.resolve("Frameworks"),
OSX_FRAMEWORK_CONTENTS_PATH,
OSX_FRAMEWORK_CONTENTS_PATH,
Expand All @@ -90,6 +95,7 @@ abstract class AppleBundleDestinations implements AddsToRuleKey {
IOS_CONTENTS_PATH,
IOS_CONTENTS_PATH,
IOS_CONTENTS_PATH,
IOS_CONTENTS_PATH.resolve("AppClips"),
IOS_CONTENTS_PATH.resolve("Frameworks"),
IOS_CONTENTS_PATH.resolve("PlugIns"),
IOS_CONTENTS_PATH.resolve("Watch"),
Expand All @@ -104,6 +110,7 @@ abstract class AppleBundleDestinations implements AddsToRuleKey {
IOS_FRAMEWORK_CONTENTS_PATH,
IOS_FRAMEWORK_CONTENTS_PATH,
IOS_FRAMEWORK_CONTENTS_PATH,
IOS_FRAMEWORK_CONTENTS_PATH,
IOS_FRAMEWORK_CONTENTS_PATH.resolve("Frameworks"),
IOS_FRAMEWORK_CONTENTS_PATH,
IOS_FRAMEWORK_CONTENTS_PATH,
Expand Down
9 changes: 7 additions & 2 deletions src/com/facebook/buck/apple/AppleDescriptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -675,7 +675,8 @@ static AppleBundle createAppleBundle(
boolean useLipoThin,
boolean cacheStrips,
boolean useEntitlementsWhenAdhocCodeSigning,
Predicate<BuildTarget> filter) {
Predicate<BuildTarget> filter,
Optional<Boolean> isAppClip) {
AppleCxxPlatform appleCxxPlatform =
ApplePlatforms.getAppleCxxPlatformForBuildTarget(
graphBuilder,
Expand Down Expand Up @@ -918,7 +919,8 @@ static AppleBundle createAppleBundle(
codesignTimeout,
copySwiftStdlibToFrameworks,
useLipoThin,
useEntitlementsWhenAdhocCodeSigning);
useEntitlementsWhenAdhocCodeSigning,
isAppClip);
}

/**
Expand Down Expand Up @@ -1100,6 +1102,9 @@ private static ImmutableMap<SourcePath, String> collectFirstLevelAppleDependency
destinationPath = destinations.getWatchAppPath();
} else if (appleBundle.isLegacyWatchApp()) {
destinationPath = destinations.getResourcesPath();
}
else if (appleBundle.getIsAppClip()) {
destinationPath = destinations.getAppClipsPath();
} else {
destinationPath = destinations.getPlugInsPath();
}
Expand Down
3 changes: 2 additions & 1 deletion src/com/facebook/buck/apple/AppleLibraryDescription.java
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,8 @@ private <A extends AbstractAppleLibraryDescriptionArg> BuildRule createFramework
swiftBuckConfig.getUseLipoThin(),
cxxBuckConfig.shouldCacheStrip(),
appleConfig.useEntitlementsWhenAdhocCodeSigning(),
Predicates.alwaysTrue());
Predicates.alwaysTrue(),
Optional.empty());
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/com/facebook/buck/apple/AppleTestDescription.java
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,8 @@ public BuildRule createBuildRule(
swiftBuckConfig.getUseLipoThin(),
cxxBuckConfig.shouldCacheStrip(),
appleConfig.useEntitlementsWhenAdhocCodeSigning(),
Predicates.alwaysTrue())));
Predicates.alwaysTrue(),
Optional.empty())));

Optional<SourcePath> xctool =
getXctool(projectFilesystem, params, targetConfiguration, graphBuilder);
Expand Down
2 changes: 2 additions & 0 deletions src/com/facebook/buck/apple/HasAppleBundleFields.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public interface HasAppleBundleFields {

Optional<String> getXcodeProductType();

Optional<Boolean> getIsAppClip();

ImmutableMap<String, String> getInfoPlistSubstitutions();

@Value.Default
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public enum Destination {
WRAPPER(1),
EXECUTABLES(6),
RESOURCES(7),
APPCLIPS(16),
FRAMEWORKS(10),
SHARED_FRAMEWORKS(11),
SHARED_SUPPORT(12),
Expand Down
3 changes: 3 additions & 0 deletions src/com/facebook/buck/apple/xcode/xcodeproj/ProductTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ public final class ProductTypes {
ProductType.of("com.apple.product-type.framework.static");
public static final ProductType APPLICATION =
ProductType.of("com.apple.product-type.application");
public static final ProductType APP_CLIP =
ProductType.of("com.apple.product-type.application.on-demand-install-capable");
public static final ProductType WATCH_APPLICATION =

ProductType.of("com.apple.product-type.application.watchapp2");
public static final ProductType UNIT_TEST =
ProductType.of("com.apple.product-type.bundle.unit-test");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,8 @@ private PBXCopyFilesBuildPhase.Destination pbxCopyPhaseDestination(
switch (destination) {
case FRAMEWORKS:
return PBXCopyFilesBuildPhase.Destination.FRAMEWORKS;
case APPCLIPS:
return PBXCopyFilesBuildPhase.Destination.APPCLIPS;
case EXECUTABLES:
return PBXCopyFilesBuildPhase.Destination.EXECUTABLES;
case RESOURCES:
Expand Down
23 changes: 23 additions & 0 deletions src/com/facebook/buck/features/apple/project/ProjectGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -3394,6 +3394,11 @@ private Optional<CopyFilePhaseDestinationSpec> getDestinationSpec(TargetNode<?>
CopyFilePhaseDestinationSpec.of(
PBXCopyFilesBuildPhase.Destination.PRODUCTS,
Optional.of("$(CONTENTS_FOLDER_PATH)/Watch")));
} else if (isAppClipApplicationNode(targetNode)) {
return Optional.of(
CopyFilePhaseDestinationSpec.of(
PBXCopyFilesBuildPhase.Destination.APPCLIPS,
Optional.of("$(CONTENTS_FOLDER_PATH)/AppClips")));
} else {
return Optional.of(
CopyFilePhaseDestinationSpec.of(PBXCopyFilesBuildPhase.Destination.EXECUTABLES));
Expand Down Expand Up @@ -4714,6 +4719,9 @@ private ProductType bundleToTargetProductType(
}
} else if (binaryNode.getDescription() instanceof AppleBinaryDescription) {
if (extension == AppleBundleExtension.APP) {
if (targetNode.getConstructorArg().getIsAppClip().orElse(false)) {
return ProductTypes.APP_CLIP;
}
return ProductTypes.APPLICATION;
}
} else if (binaryNode.getDescription() instanceof AppleTestDescription) {
Expand Down Expand Up @@ -4871,6 +4879,21 @@ private static boolean isWatchApplicationNode(TargetNode<?> targetNode) {
return false;
}

/**
* Determines if a target node is for AppClip application
*
* @param targetNode A target node
* @return If the given target node is for an AppClip application
*/
private static boolean isAppClipApplicationNode(TargetNode<?> targetNode) {
if (targetNode.getDescription() instanceof AppleBundleDescription) {
AppleBundleDescriptionArg arg = (AppleBundleDescriptionArg) targetNode.getConstructorArg();
return arg.getXcodeProductType()
.equals(Optional.of(ProductTypes.APP_CLIP.getIdentifier()));
}
return false;
}

private Optional<SourcePath> getPrefixHeaderSourcePath(CxxLibraryDescription.CommonArg arg) {
// The prefix header could be stored in either the `prefix_header` or the `precompiled_header`
// field. Use either, but prefer the prefix_header.
Expand Down
26 changes: 26 additions & 0 deletions test/com/facebook/buck/apple/AppleBundleIntegrationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1163,6 +1163,32 @@ public void watchApplicationBundle() throws IOException {
assertTrue(Files.exists(watchAppPath.resolve("Interface.plist")));
}

@Test
public void appClipApplicationBundle() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "app_bundle_with_appclip", tmp);
workspace.setUp();

BuildTarget target = BuildTargetFactory.newInstance("//:ExampleApp#iphonesimulator-x86_64");
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();

Path appPath =
workspace.getPath(
BuildTargetPaths.getGenPath(
filesystem,
target.withAppendedFlavors(
AppleDebugFormat.DWARF_AND_DSYM.getFlavor(),
AppleDescriptions.NO_INCLUDE_FRAMEWORKS_FLAVOR),
"%s")
.resolve(target.getShortName() + ".app"));
assertTrue(Files.exists(appPath.resolve("ExampleApp")));
assertTrue(Files.exists(appPath.resolve("Info.plist")));

Path appClipPath = appPath.resolve("AppClips/Clip.app/");
assertTrue(Files.exists(appClipPath.resolve("Clip")));
assertTrue(Files.exists(appClipPath.resolve("Info.plist")));
}

@Test
public void copiesFrameworkBundleIntoFrameworkDirectory() throws Exception {
assumeTrue(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// AppDelegate.swift
// AppClipApp
//
// Created by Lucas Marcal on 08/10/20.
//

import UIKit

class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = ViewController()
self.window?.makeKeyAndVisible()
return true
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>EN</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.buck.test.appclipapp</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// ViewController.swift
// AppClipApp
//
// Created by Lucas Marcal on 08/10/20.
//

import UIKit

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .yellow
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Foundation
import UIKit

let argc = CommandLine.argc
let argv = UnsafeMutableRawPointer(CommandLine.unsafeArgv).bindMemory(to: UnsafeMutablePointer<Int8>.self, capacity: Int(CommandLine.argc))

UIApplicationMain(argc, argv, nil, NSStringFromClass(AppDelegate.self))
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
apple_bundle(
name = "ExampleApp",
visibility = ["PUBLIC"],
extension = "app",
binary = ":ExampleAppBinary#iphonesimulator-x86_64",
product_name = "ExampleApp",
info_plist = "AppClipApp/Info.plist",
deps = ["//Clip:Clip"]
)

apple_binary(
name = "ExampleAppBinary",
visibility = ["PUBLIC"],
swift_version = "5",
target_sdk_version = "14.0",
srcs = glob(["AppClipApp/*.swift",])
)
Loading

0 comments on commit ae2347e

Please sign in to comment.