/* * Copyright 2015-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 static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.BuildTargetFactory; import com.facebook.buck.model.BuildTargets; import com.facebook.buck.testutil.TestConsole; import com.facebook.buck.testutil.integration.ProjectWorkspace; import com.facebook.buck.testutil.integration.TemporaryPaths; import com.facebook.buck.testutil.integration.TestDataHelper; import com.facebook.buck.testutil.integration.ZipInspector; import com.facebook.buck.util.DefaultProcessExecutor; import com.facebook.buck.util.ProcessExecutor; import com.facebook.buck.util.ProcessExecutorParams; import com.facebook.buck.util.environment.Platform; import com.facebook.buck.zip.Unzip; import com.google.common.collect.ImmutableList; import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.EnumSet; import java.util.Optional; import java.util.Set; import org.junit.Before; import org.junit.Rule; import org.junit.Test; public class BuiltinApplePackageIntegrationTest { @Rule public TemporaryPaths tmp = new TemporaryPaths(); private ProjectFilesystem filesystem; @Before public void setUp() throws InterruptedException { filesystem = new ProjectFilesystem(tmp.getRoot()); } private static boolean isDirEmpty(final Path directory) throws IOException { try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(directory)) { return !dirStream.iterator().hasNext(); } } @Test public void packageHasProperStructure() throws IOException, InterruptedException { assumeTrue(Platform.detect() == Platform.MACOS); ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario( this, "simple_application_bundle_no_debug", tmp); workspace.setUp(); workspace.enableDirCache(); BuildTarget appTarget = BuildTargetFactory.newInstance("//:DemoApp#no-debug,no-include-frameworks"); workspace .runBuckCommand("build", appTarget.getUnflavoredBuildTarget().getFullyQualifiedName()) .assertSuccess(); workspace.getBuildLog().assertTargetBuiltLocally(appTarget.getFullyQualifiedName()); workspace.runBuckCommand("clean").assertSuccess(); BuildTarget packageTarget = BuildTargetFactory.newInstance("//:DemoAppPackage"); workspace.runBuckCommand("build", packageTarget.getFullyQualifiedName()).assertSuccess(); workspace.getBuildLog().assertTargetWasFetchedFromCache(appTarget.getFullyQualifiedName()); workspace.getBuildLog().assertTargetBuiltLocally(packageTarget.getFullyQualifiedName()); Path templateDir = TestDataHelper.getTestDataScenario(this, "simple_application_bundle_no_debug"); ZipInspector zipInspector = new ZipInspector( workspace.getPath(BuildTargets.getGenPath(filesystem, packageTarget, "%s.ipa"))); zipInspector.assertFileExists("Payload/DemoApp.app/DemoApp"); zipInspector.assertFileDoesNotExist("WatchKitSupport"); zipInspector.assertFileDoesNotExist("WatchKitSupport2"); zipInspector.assertFileContents( "Payload/DemoApp.app/PkgInfo", new String( Files.readAllBytes( templateDir.resolve("DemoApp_output.expected/DemoApp.app/PkgInfo.expected")), UTF_8)); } @Test public void packageHasProperStructureForSwift() throws IOException { assumeTrue(Platform.detect() == Platform.MACOS); ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario( this, "simple_application_bundle_swift_no_debug", tmp); workspace.setUp(); workspace.enableDirCache(); BuildTarget packageTarget = BuildTargetFactory.newInstance("//:DemoAppPackage"); workspace.runBuckCommand("build", packageTarget.getFullyQualifiedName()).assertSuccess(); workspace.getBuildLog().assertTargetBuiltLocally(packageTarget.getFullyQualifiedName()); ZipInspector zipInspector = new ZipInspector( workspace.getPath(BuildTargets.getGenPath(filesystem, packageTarget, "%s.ipa"))); zipInspector.assertFileExists("SwiftSupport/iphonesimulator/libswiftCore.dylib"); } @Test public void swiftSupportIsOnlyAddedIfPackageContainsSwiftCode() throws IOException { assumeTrue(Platform.detect() == Platform.MACOS); ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario( this, "simple_application_bundle_no_debug", tmp); workspace.setUp(); workspace.enableDirCache(); BuildTarget packageTarget = BuildTargetFactory.newInstance("//:DemoAppPackage"); workspace.runBuckCommand("build", packageTarget.getFullyQualifiedName()).assertSuccess(); workspace.getBuildLog().assertTargetBuiltLocally(packageTarget.getFullyQualifiedName()); ZipInspector zipInspector = new ZipInspector( workspace.getPath(BuildTargets.getGenPath(filesystem, packageTarget, "%s.ipa"))); zipInspector.assertFileDoesNotExist("SwiftSupport"); } @Test public void packageHasProperStructureForWatch20() throws IOException, InterruptedException { assumeTrue(Platform.detect() == Platform.MACOS); ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "watch_application_bundle", tmp); workspace.setUp(); workspace.addBuckConfigLocalOption("apple", "watchsimulator_target_sdk_version", "2.0"); packageHasProperStructureForWatchHelper(workspace, true); } @Test public void packageHasProperStructureForWatch21() throws IOException, InterruptedException { assumeTrue(Platform.detect() == Platform.MACOS); ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "watch_application_bundle", tmp); workspace.setUp(); workspace.addBuckConfigLocalOption("apple", "watchsimulator_target_sdk_version", "2.1"); packageHasProperStructureForWatchHelper(workspace, false); } private void packageHasProperStructureForWatchHelper( ProjectWorkspace workspace, boolean shouldHaveStubInsideBundle) throws IOException, InterruptedException { BuildTarget packageTarget = BuildTargetFactory.newInstance("//:DemoAppPackage"); workspace.runBuckCommand("build", packageTarget.getFullyQualifiedName()).assertSuccess(); Path destination = workspace.getDestPath(); Unzip.extractZipFile( workspace.getPath(BuildTargets.getGenPath(filesystem, packageTarget, "%s.ipa")), destination, Unzip.ExistingFileMode.OVERWRITE_AND_CLEAN_DIRECTORIES); Path stubOutsideBundle = destination.resolve("WatchKitSupport2/WK"); assertTrue(Files.isExecutable(stubOutsideBundle)); assertTrue(Files.isDirectory(destination.resolve("Symbols"))); assertTrue(isDirEmpty(destination.resolve("Symbols"))); if (shouldHaveStubInsideBundle) { Path stubInsideBundle = destination.resolve("Payload/DemoApp.app/Watch/DemoWatchApp.app/_WatchKitStub/WK"); assertTrue(Files.exists(stubInsideBundle)); assertEquals( new String(Files.readAllBytes(stubInsideBundle)), new String(Files.readAllBytes(stubOutsideBundle))); } } @Test public void packageHasProperStructureForLegacyWatch() throws IOException, InterruptedException { assumeTrue(Platform.detect() == Platform.MACOS); ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario( this, "legacy_watch_application_bundle", tmp); workspace.setUp(); BuildTarget packageTarget = BuildTargetFactory.newInstance("//:DemoAppPackage"); workspace.runBuckCommand("build", packageTarget.getFullyQualifiedName()).assertSuccess(); Path destination = workspace.getDestPath(); Unzip.extractZipFile( workspace.getPath(BuildTargets.getGenPath(filesystem, packageTarget, "%s.ipa")), destination, Unzip.ExistingFileMode.OVERWRITE_AND_CLEAN_DIRECTORIES); Path stub = destination.resolve("WatchKitSupport/WK"); assertTrue(Files.isExecutable(stub)); assertFalse(Files.isDirectory(destination.resolve("Symbols"))); } @Test public void packageSupportsFatBinaries() throws IOException, InterruptedException { assumeTrue(Platform.detect() == Platform.MACOS); ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario( this, "simple_application_bundle_no_debug", tmp); workspace.setUp(); BuildTarget packageTarget = BuildTargetFactory.newInstance( "//:DemoAppPackage#iphonesimulator-i386,iphonesimulator-x86_64"); workspace.runBuckCommand("build", packageTarget.getFullyQualifiedName()).assertSuccess(); Unzip.extractZipFile( workspace.getPath(BuildTargets.getGenPath(filesystem, packageTarget, "%s.ipa")), workspace.getDestPath(), Unzip.ExistingFileMode.OVERWRITE_AND_CLEAN_DIRECTORIES); ProcessExecutor executor = new DefaultProcessExecutor(new TestConsole()); ProcessExecutorParams processExecutorParams = ProcessExecutorParams.builder() .setCommand( ImmutableList.of( "lipo", "-info", workspace.getDestPath().resolve("Payload/DemoApp.app/DemoApp").toString())) .build(); // Specify that stdout is expected, or else output may be wrapped in Ansi escape chars. Set<ProcessExecutor.Option> options = EnumSet.of(ProcessExecutor.Option.EXPECTING_STD_OUT, ProcessExecutor.Option.IS_SILENT); ProcessExecutor.Result result = executor.launchAndExecute( processExecutorParams, options, /* stdin */ Optional.empty(), /* timeOutMs */ Optional.empty(), /* timeOutHandler */ Optional.empty()); assertEquals(result.getExitCode(), 0); assertTrue(result.getStdout().isPresent()); String output = result.getStdout().get(); assertTrue(output.contains("i386")); assertTrue(output.contains("x86_64")); } @Test public void testDisablingPackageCaching() throws IOException { assumeTrue(Platform.detect() == Platform.MACOS); ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario( this, "simple_application_bundle_no_debug", tmp); workspace.setUp(); workspace.enableDirCache(); workspace .runBuckBuild("-c", "apple.cache_bundles_and_packages=false", "//:DemoAppPackage") .assertSuccess(); workspace.runBuckCommand("clean"); workspace .runBuckBuild("-c", "apple.cache_bundles_and_packages=false", "//:DemoAppPackage") .assertSuccess(); workspace.getBuildLog().assertTargetBuiltLocally("//:DemoAppPackage"); } }