/*
* 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 static com.facebook.buck.cxx.CxxFlavorSanitizer.sanitize;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import com.facebook.buck.cxx.CxxDescriptionEnhancer;
import com.facebook.buck.cxx.CxxStrip;
import com.facebook.buck.cxx.LinkerMapMode;
import com.facebook.buck.cxx.StripStyle;
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.model.Flavor;
import com.facebook.buck.model.InternalFlavor;
import com.facebook.buck.testutil.MoreAsserts;
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.util.ProcessExecutor;
import com.facebook.buck.util.environment.Platform;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
public class AppleBinaryIntegrationTest {
@Rule public TemporaryPaths tmp = new TemporaryPaths();
private ProjectFilesystem filesystem;
@Before
public void setUp() throws InterruptedException {
assumeTrue(Platform.detect() == Platform.MACOS || Platform.detect() == Platform.LINUX);
filesystem = new ProjectFilesystem(tmp.getRoot());
}
@Test
public void testAppleBinaryBuildsBinaryWithLinkerMap() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "apple_binary_builds_something", tmp);
workspace.setUp();
BuildTarget target = BuildTargetFactory.newInstance("//Apps/TestApp:TestApp");
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
Path outputPath = workspace.getPath(BuildTargets.getGenPath(filesystem, target, "%s"));
assertThat(Files.exists(outputPath), is(true));
assertThat(Files.exists(Paths.get(outputPath.toString() + "-LinkMap.txt")), is(true));
assertThat(
workspace.runCommand("file", outputPath.toString()).getStdout().get(),
containsString("executable"));
}
@Test
public void testAppleBinaryBuildsBinaryWithoutLinkerMap() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "apple_binary_builds_something", tmp);
workspace.setUp();
BuildTarget target =
BuildTargetFactory.newInstance("//Apps/TestApp:TestApp")
.withFlavors(LinkerMapMode.NO_LINKER_MAP.getFlavor());
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
Path outputPath = workspace.getPath(BuildTargets.getGenPath(filesystem, target, "%s"));
assertThat(Files.exists(outputPath), is(true));
assertThat(Files.exists(Paths.get(outputPath.toString() + "-LinkMap.txt")), is(false));
assertThat(
workspace.runCommand("file", outputPath.toString()).getStdout().get(),
containsString("executable"));
}
@Test
public void testAppleBinaryUsesDefaultsFromConfig() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "apple_binary_with_config_default_platform", tmp);
workspace.setUp();
BuildTarget target =
BuildTargetFactory.newInstance("//Apps/TestApp:TestApp")
.withAppendedFlavors(InternalFlavor.of("iphoneos-arm64"));
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
Path outputPath = workspace.getPath(BuildTargets.getGenPath(filesystem, target, "%s"));
assertThat(Files.exists(outputPath), is(true));
assertThat(Files.exists(Paths.get(outputPath.toString() + "-LinkMap.txt")), is(true));
assertThat(
workspace.runCommand("file", outputPath.toString()).getStdout().get(),
containsString("executable"));
assertThat(
workspace.runCommand("otool", "-hv", outputPath.toString()).getStdout().get(),
containsString("ARM64"));
}
@Test
public void testAppleBinaryUsesDefaultsFromArgs() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "apple_binary_with_platform", tmp);
workspace.setUp();
BuildTarget target =
BuildTargetFactory.newInstance("//Apps/TestApp:TestApp")
.withAppendedFlavors(InternalFlavor.of("iphoneos-arm64"));
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
Path outputPath = workspace.getPath(BuildTargets.getGenPath(filesystem, target, "%s"));
assertThat(Files.exists(outputPath), is(true));
assertThat(Files.exists(Paths.get(outputPath.toString() + "-LinkMap.txt")), is(true));
assertThat(
workspace.runCommand("file", outputPath.toString()).getStdout().get(),
containsString("executable"));
assertThat(
workspace.runCommand("otool", "-hv", outputPath.toString()).getStdout().get(),
containsString("ARM64"));
}
@Test
public void testAppleBinaryUsesPlatformLinkerFlags() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "apple_binary_builds_something", tmp);
workspace.setUp();
BuildTarget target =
BuildTargetFactory.newInstance("//Apps/TestApp:TestAppWithNonstandardMain");
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
Path outputPath = workspace.getPath(BuildTargets.getGenPath(filesystem, target, "%s"));
assertThat(Files.exists(outputPath), is(true));
assertThat(
workspace.runCommand("file", outputPath.toString()).getStdout().get(),
containsString("executable"));
}
@Test
public void testAppleBinaryWithThinLTO() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "apple_binary_with_thinlto", tmp);
workspace.setUp();
BuildTarget target = BuildTargetFactory.newInstance("//Apps/TestApp:TestApp");
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
Path outputPath = workspace.getPath(BuildTargets.getGenPath(filesystem, target, "%s"));
assertThat(Files.exists(outputPath), is(true));
assertThat(Files.isDirectory(Paths.get(outputPath.toString() + "-lto")), is(true));
assertThat(
workspace.runCommand("file", outputPath.toString()).getStdout().get(),
containsString("executable"));
}
@Test
public void testAppleBinaryWithThinLTOWithLibraryDependency() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
assumeTrue(AppleNativeIntegrationTestUtils.isApplePlatformAvailable(ApplePlatform.MACOSX));
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "apple_binary_with_thinlto_library_dependency", tmp);
workspace.setUp();
BuildTarget target = BuildTargetFactory.newInstance("//Apps/TestApp:TestApp#app,macosx-x86_64");
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
Path outputPath =
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
BuildTargetFactory.newInstance("//Apps/TestApp:TestApp#macosx-x86_64")
.withAppendedFlavors(AppleDescriptions.INCLUDE_FRAMEWORKS_FLAVOR),
"%s"));
assertThat(Files.isDirectory(Paths.get(outputPath.toString() + "-lto")), is(true));
Path bundlePath =
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
target.withAppendedFlavors(
AppleDebugFormat.DWARF_AND_DSYM.getFlavor(),
AppleDescriptions.INCLUDE_FRAMEWORKS_FLAVOR),
"%s/TestApp.app"));
assertThat(Files.exists(bundlePath), is(true));
Path binaryPath = bundlePath.resolve("Contents/MacOS/TestApp");
assertThat(Files.exists(binaryPath), is(true));
assertThat(
workspace.runCommand("file", binaryPath.toString()).getStdout().get(),
containsString("executable"));
Path frameworkBundlePath = bundlePath.resolve("Contents/Frameworks/TestLibrary.framework");
assertThat(Files.exists(frameworkBundlePath), is(true));
Path frameworkBinaryPath = frameworkBundlePath.resolve("TestLibrary");
assertThat(Files.exists(frameworkBinaryPath), is(true));
assertThat(
workspace.runCommand("file", frameworkBinaryPath.toString()).getStdout().get(),
containsString("dynamically linked shared library"));
}
@Test
public void testAppleBinaryAppBuildsAppWithDsym() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "apple_binary_builds_something", tmp);
workspace.setUp();
BuildTarget target = BuildTargetFactory.newInstance("//Apps/TestApp:TestApp#app");
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
BuildTarget appTarget =
target.withFlavors(
AppleBinaryDescription.APP_FLAVOR,
AppleDescriptions.NO_INCLUDE_FRAMEWORKS_FLAVOR,
AppleDebugFormat.DWARF_AND_DSYM.getFlavor());
Path outputPath =
workspace.getPath(
BuildTargets.getGenPath(filesystem, appTarget, "%s")
.resolve(appTarget.getShortName() + ".app"));
assertThat(Files.exists(outputPath), is(true));
assertThat(Files.exists(outputPath.resolve("Info.plist")), is(true));
Path dsymPath =
workspace.getPath(
BuildTargets.getGenPath(filesystem, appTarget, "%s")
.resolve(appTarget.getShortName() + ".app.dSYM"));
assertThat(Files.exists(dsymPath), is(true));
assertThat(
workspace
.runCommand("file", outputPath.resolve(appTarget.getShortName()).toString())
.getStdout()
.get(),
containsString("executable"));
}
@Test
public void testAppleBinaryAppBuildsAppWithoutDsym() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "apple_binary_builds_something", tmp);
workspace.setUp();
BuildTarget target = BuildTargetFactory.newInstance("//Apps/TestApp:TestApp#app,no-debug");
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
BuildTarget appTarget =
target.withFlavors(
AppleBinaryDescription.APP_FLAVOR,
AppleDescriptions.NO_INCLUDE_FRAMEWORKS_FLAVOR,
AppleDebugFormat.NONE.getFlavor());
Path outputPath =
workspace.getPath(
BuildTargets.getGenPath(filesystem, appTarget, "%s")
.resolve(appTarget.getShortName() + ".app"));
assertThat(Files.exists(outputPath), is(true));
assertThat(Files.exists(outputPath.resolve("Info.plist")), is(true));
Path dsymPath =
workspace.getPath(
BuildTargets.getGenPath(filesystem, appTarget, "%s")
.resolve(appTarget.getShortName() + ".app.dSYM"));
assertThat(Files.exists(dsymPath), is(false));
}
@Test
public void testAppleBinaryWithSystemFrameworksBuildsSomething() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
assumeTrue(AppleNativeIntegrationTestUtils.isApplePlatformAvailable(ApplePlatform.MACOSX));
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "apple_binary_with_system_frameworks_builds_something", tmp);
workspace.setUp();
BuildTarget target = BuildTargetFactory.newInstance("//Apps/TestApp:TestApp#macosx-x86_64");
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
Path outputPath = workspace.getPath(BuildTargets.getGenPath(filesystem, target, "%s"));
assertThat(Files.exists(outputPath), is(true));
assertThat(
workspace.runCommand("file", outputPath.toString()).getStdout().get(),
containsString("executable"));
}
@Test
public void testAppleBinaryWithLibraryDependencyBuildsSomething() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
assumeTrue(AppleNativeIntegrationTestUtils.isApplePlatformAvailable(ApplePlatform.MACOSX));
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "apple_binary_with_library_dependency_builds_something", tmp);
workspace.setUp();
BuildTarget target =
BuildTargetFactory.newInstance("//Apps/TestApp:TestApp")
.withAppendedFlavors(InternalFlavor.of("macosx-x86_64"));
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
Path outputPath = workspace.getPath(BuildTargets.getGenPath(filesystem, target, "%s"));
assertThat(Files.exists(outputPath), is(true));
assertThat(
workspace.runCommand("file", outputPath.toString()).getStdout().get(),
containsString("executable"));
}
@Test
public void testAppleBinaryWithLibraryDependencyBuildsApp() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
assumeTrue(AppleNativeIntegrationTestUtils.isApplePlatformAvailable(ApplePlatform.MACOSX));
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "apple_binary_with_library_dependency_builds_something", tmp);
workspace.setUp();
BuildTarget target = BuildTargetFactory.newInstance("//Apps/TestApp:TestApp#app,macosx-x86_64");
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
Path bundlePath =
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
target.withAppendedFlavors(
AppleDebugFormat.DWARF_AND_DSYM.getFlavor(),
AppleDescriptions.INCLUDE_FRAMEWORKS_FLAVOR),
"%s/TestApp.app"));
assertThat(Files.exists(bundlePath), is(true));
Path binaryPath = bundlePath.resolve("Contents/MacOS/TestApp");
assertThat(Files.exists(binaryPath), is(true));
assertThat(
workspace.runCommand("file", binaryPath.toString()).getStdout().get(),
containsString("executable"));
Path frameworkBundlePath = bundlePath.resolve("Contents/Frameworks/TestLibrary.framework");
assertThat(Files.exists(frameworkBundlePath), is(true));
Path frameworkBinaryPath = frameworkBundlePath.resolve("TestLibrary");
assertThat(Files.exists(frameworkBinaryPath), is(true));
assertThat(
workspace.runCommand("file", frameworkBinaryPath.toString()).getStdout().get(),
containsString("dynamically linked shared library"));
}
@Test
public void testAppleBinaryWithLibraryDependencyWithSystemFrameworksBuildsSomething()
throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
assumeTrue(AppleNativeIntegrationTestUtils.isApplePlatformAvailable(ApplePlatform.MACOSX));
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this,
"apple_binary_with_library_dependency_with_system_frameworks_builds_something",
tmp);
workspace.setUp();
BuildTarget target =
BuildTargetFactory.newInstance("//Apps/TestApp:TestApp")
.withAppendedFlavors(InternalFlavor.of("macosx-x86_64"));
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
Path outputPath = workspace.getPath(BuildTargets.getGenPath(filesystem, target, "%s"));
assertThat(Files.exists(outputPath), is(true));
assertThat(
workspace.runCommand("file", outputPath.toString()).getStdout().get(),
containsString("executable"));
}
@Test
public void testAppleLibraryPropagatesExportedPlatformLinkerFlags()
throws IOException, InterruptedException {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "apple_binary_with_library_dependency_builds_something", tmp);
workspace.setUp();
ProjectWorkspace.ProcessResult buildResult =
workspace.runBuckCommand("build", "//Apps/TestApp:BadTestApp");
buildResult.assertFailure();
String stderr = buildResult.getStderr();
assertTrue(stderr.contains("bad-flag"));
}
@Test
public void testAppleBinaryHeaderSymlinkTree() throws IOException {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "apple_binary_header_symlink_tree", tmp);
workspace.setUp();
BuildTarget buildTarget =
BuildTargetFactory.newInstance(
"//Apps/TestApp:TestApp#default," + CxxDescriptionEnhancer.HEADER_SYMLINK_TREE_FLAVOR);
ProjectWorkspace.ProcessResult result =
workspace.runBuckCommand("build", buildTarget.getFullyQualifiedName());
result.assertSuccess();
Path projectRoot = tmp.getRoot().toRealPath();
Path inputPath = projectRoot.resolve(buildTarget.getBasePath());
Path outputPath = projectRoot.resolve(BuildTargets.getGenPath(filesystem, buildTarget, "%s"));
assertIsSymbolicLink(outputPath.resolve("Header.h"), inputPath.resolve("Header.h"));
assertIsSymbolicLink(outputPath.resolve("TestApp/Header.h"), inputPath.resolve("Header.h"));
}
@Test
public void testAppleBinaryWithHeaderMaps() throws Exception {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "apple_binary_with_header_maps", tmp);
workspace.setUp();
BuildTarget target = BuildTargetFactory.newInstance("//Apps/TestApp:TestApp");
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
Path outputPath = workspace.getPath(BuildTargets.getGenPath(filesystem, target, "%s"));
assertThat(Files.exists(outputPath), is(true));
assertThat(
workspace.runCommand("file", outputPath.toString()).getStdout().get(),
containsString("executable"));
}
@Test
public void testAppleXcodeError() throws IOException {
assumeTrue(Platform.detect() == Platform.MACOS);
String expectedError =
"Apps/TestApp/main.c:2:3: error: use of undeclared identifier 'SomeType'\n"
+ " SomeType a;\n"
+ " ^\n";
String expectedWarning =
"Apps/TestApp/main.c:3:10: warning: implicit conversion from 'double' to 'int' changes "
+ "value from 0.42 to 0 [-Wliteral-conversion]\n"
+ " return 0.42;\n"
+ " ~~~~~~ ^~~~\n";
String expectedSummary = "1 warning and 1 error generated.\n";
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "apple_xcode_error", tmp);
workspace.setUp();
ProjectWorkspace.ProcessResult buildResult =
workspace.runBuckCommand("build", "//Apps/TestApp:TestApp");
buildResult.assertFailure();
String stderr = buildResult.getStderr();
assertTrue(
stderr.contains(expectedError)
&& stderr.contains(expectedWarning)
&& stderr.contains(expectedSummary));
}
@Test
public void testAppleBinaryIsHermetic() throws IOException {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "apple_binary_is_hermetic", tmp);
workspace.setUp();
BuildTarget target =
BuildTargetFactory.newInstance("//Apps/TestApp:TestApp")
.withAppendedFlavors(InternalFlavor.of("iphonesimulator-x86_64"));
ProjectWorkspace.ProcessResult first =
workspace.runBuckCommand(
workspace.getPath("first"), "build", target.getFullyQualifiedName());
first.assertSuccess();
ProjectWorkspace.ProcessResult second =
workspace.runBuckCommand(
workspace.getPath("second"), "build", target.getFullyQualifiedName());
second.assertSuccess();
Path outputPath =
BuildTargets.getGenPath(
filesystem,
target.withFlavors(
InternalFlavor.of("iphonesimulator-x86_64"),
InternalFlavor.of("compile-" + sanitize("TestClass.m.o"))),
"%s/TestClass.m.o");
MoreAsserts.assertContentsEqual(
workspace.getPath(Paths.get("first").resolve(outputPath)),
workspace.getPath(Paths.get("second").resolve(outputPath)));
outputPath = BuildTargets.getGenPath(filesystem, target, "%s");
MoreAsserts.assertContentsEqual(
workspace.getPath(Paths.get("first").resolve(outputPath)),
workspace.getPath(Paths.get("second").resolve(outputPath)));
}
private void runTestAppleBinaryWithDebugFormatIsHermetic(AppleDebugFormat debugFormat)
throws IOException {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "apple_binary_is_hermetic", tmp);
workspace.setUp();
BuildTarget target =
BuildTargetFactory.newInstance(
"//Apps/TestApp:TestApp#iphonesimulator-x86_64," + debugFormat.getFlavor().getName());
ProjectWorkspace.ProcessResult first =
workspace.runBuckCommand(
workspace.getPath("first"), "build", target.getFullyQualifiedName());
first.assertSuccess();
ProjectWorkspace.ProcessResult second =
workspace.runBuckCommand(
workspace.getPath("second"), "build", target.getFullyQualifiedName());
second.assertSuccess();
Path outputPath =
BuildTargets.getGenPath(
filesystem,
target.withFlavors(
InternalFlavor.of("iphonesimulator-x86_64"),
InternalFlavor.of("compile-" + sanitize("TestClass.m.o"))),
"%s/TestClass.m.o");
MoreAsserts.assertContentsEqual(
workspace.getPath(Paths.get("first").resolve(outputPath)),
workspace.getPath(Paths.get("second").resolve(outputPath)));
outputPath =
BuildTargets.getGenPath(
filesystem, target.withoutFlavors(AppleDebugFormat.FLAVOR_DOMAIN.getFlavors()), "%s");
MoreAsserts.assertContentsEqual(
workspace.getPath(Paths.get("first").resolve(outputPath)),
workspace.getPath(Paths.get("second").resolve(outputPath)));
if (debugFormat != AppleDebugFormat.DWARF) {
Path strippedPath =
BuildTargets.getGenPath(
filesystem,
target
.withoutFlavors(AppleDebugFormat.FLAVOR_DOMAIN.getFlavors())
.withAppendedFlavors(
StripStyle.NON_GLOBAL_SYMBOLS.getFlavor(), CxxStrip.RULE_FLAVOR),
"%s");
MoreAsserts.assertContentsEqual(
workspace.getPath(Paths.get("first").resolve(strippedPath)),
workspace.getPath(Paths.get("second").resolve(strippedPath)));
}
}
@Test
public void testAppleBinaryWithDwarfDebugFormatIsHermetic() throws IOException {
runTestAppleBinaryWithDebugFormatIsHermetic(AppleDebugFormat.DWARF);
}
@Test
public void testAppleBinaryWithDwarfAndDsymDebugFormatIsHermetic() throws IOException {
runTestAppleBinaryWithDebugFormatIsHermetic(AppleDebugFormat.DWARF_AND_DSYM);
}
@Test
public void testAppleBinaryWithNoneDebugFormatIsHermetic() throws IOException {
runTestAppleBinaryWithDebugFormatIsHermetic(AppleDebugFormat.NONE);
}
@Test
public void testAppleBinaryBuildsFatBinaries() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "simple_application_bundle_dwarf_and_dsym", tmp);
workspace.setUp();
BuildTarget target =
BuildTargetFactory.newInstance(
"//:DemoAppBinary#iphonesimulator-i386,iphonesimulator-x86_64,no-linkermap");
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
Path output = workspace.getPath(BuildTargets.getGenPath(filesystem, target, "%s"));
assertThat(Files.exists(output), is(true));
assertThat(
workspace.runCommand("file", output.toString()).getStdout().get(),
containsString("executable"));
ProcessExecutor.Result lipoVerifyResult =
workspace.runCommand("lipo", output.toString(), "-verify_arch", "i386", "x86_64");
assertEquals(lipoVerifyResult.getStderr().orElse(""), 0, lipoVerifyResult.getExitCode());
}
@Test
public void testAppleBinaryBuildsFatBinariesWithDsym() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "simple_application_bundle_no_debug", tmp);
workspace.setUp();
BuildTarget target =
BuildTargetFactory.newInstance(
"//:DemoAppBinary#iphonesimulator-i386,iphonesimulator-x86_64,no-linkermap");
BuildTarget targetToBuild =
target.withAppendedFlavors(AppleDebugFormat.DWARF_AND_DSYM.getFlavor());
BuildTarget dsymTarget = target.withAppendedFlavors(AppleDsym.RULE_FLAVOR);
workspace.runBuckCommand("build", targetToBuild.getFullyQualifiedName()).assertSuccess();
Path output =
workspace.getPath(
AppleDsym.getDsymOutputPath(
dsymTarget.withoutFlavors(LinkerMapMode.NO_LINKER_MAP.getFlavor()), filesystem));
AppleDsymTestUtil.checkDsymFileHasDebugSymbolsForMainForConcreteArchitectures(
workspace, output, Optional.of(ImmutableList.of("i386", "x86_64")));
}
@Test
public void testFlavoredAppleBundleBuildsAndDsymFileCreatedAndBinaryIsStripped()
throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "simple_application_bundle_dwarf_and_dsym", tmp);
workspace.setUp();
BuildTarget target = BuildTargetFactory.newInstance("//:DemoApp#dwarf-and-dsym");
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
workspace
.runBuckCommand(
"build",
"--config",
"apple.default_debug_info_format_for_binaries=none",
target.getFullyQualifiedName())
.assertSuccess();
BuildTarget appTarget =
target.withFlavors(
AppleDebugFormat.DWARF_AND_DSYM.getFlavor(),
AppleDescriptions.NO_INCLUDE_FRAMEWORKS_FLAVOR);
Path output =
workspace.getPath(
BuildTargets.getGenPath(filesystem, appTarget, "%s")
.resolve(target.getShortName() + ".app.dSYM")
.resolve("Contents/Resources/DWARF")
.resolve(target.getShortName()));
assertThat(Files.exists(output), equalTo(true));
AppleDsymTestUtil.checkDsymFileHasDebugSymbolForMain(workspace, output);
Path binaryOutput =
workspace.getPath(
BuildTargets.getGenPath(filesystem, appTarget, "%s")
.resolve(target.getShortName() + ".app")
.resolve(target.getShortName()));
assertThat(Files.exists(binaryOutput), equalTo(true));
ProcessExecutor.Result hasSymbol = workspace.runCommand("nm", binaryOutput.toString());
String stdout = hasSymbol.getStdout().orElse("");
assertThat(stdout, Matchers.not(containsString("t -[AppDelegate window]")));
assertThat(stdout, containsString("U _UIApplicationMain"));
}
@Test
public void testFlavoredAppleBundleBuildsWithDwarfDebugFormatAndBinaryIsUnstripped()
throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "simple_application_bundle_dwarf_and_dsym", tmp);
workspace.setUp();
BuildTarget target = BuildTargetFactory.newInstance("//:DemoApp#dwarf");
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
BuildTarget appTarget =
target.withFlavors(
AppleDebugFormat.DWARF.getFlavor(), AppleDescriptions.NO_INCLUDE_FRAMEWORKS_FLAVOR);
Path output =
workspace.getPath(
BuildTargets.getGenPath(filesystem, appTarget, "%s")
.resolve(target.getShortName() + ".app")
.resolve(target.getShortName()));
assertThat(Files.exists(output), equalTo(true));
ProcessExecutor.Result hasSymbol = workspace.runCommand("nm", output.toString());
String stdout = hasSymbol.getStdout().orElse("");
assertThat(stdout, containsString("t -[AppDelegate window]"));
assertThat(stdout, containsString("U _UIApplicationMain"));
}
@Test
public void testBuildingWithDwarfProducesAllCompileRulesOnDisk() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "simple_application_bundle_dwarf_and_dsym", tmp);
workspace.setUp();
workspace.enableDirCache();
Flavor platformFlavor = InternalFlavor.of("iphonesimulator-x86_64");
BuildTarget target =
BuildTargetFactory.newInstance("//:DemoApp")
.withAppendedFlavors(AppleDebugFormat.DWARF.getFlavor());
BuildTarget binaryTarget =
BuildTargetFactory.newInstance("//:DemoAppBinary")
.withAppendedFlavors(platformFlavor, AppleDescriptions.NO_INCLUDE_FRAMEWORKS_FLAVOR);
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
workspace.runBuckCommand("clean").assertSuccess();
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
BuildTarget appTarget =
target.withFlavors(
AppleDebugFormat.DWARF.getFlavor(), AppleDescriptions.NO_INCLUDE_FRAMEWORKS_FLAVOR);
Path binaryOutput =
workspace.getPath(
BuildTargets.getGenPath(filesystem, appTarget, "%s")
.resolve(target.getShortName() + ".app")
.resolve(target.getShortName()));
Path delegateFileOutput =
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
binaryTarget.withFlavors(
platformFlavor,
InternalFlavor.of("compile-" + sanitize("AppDelegate.m.o"))),
"%s")
.resolve("AppDelegate.m.o"));
Path mainFileOutput =
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
binaryTarget.withFlavors(
platformFlavor, InternalFlavor.of("compile-" + sanitize("main.m.o"))),
"%s")
.resolve("main.m.o"));
assertThat(Files.exists(binaryOutput), equalTo(true));
assertThat(Files.exists(delegateFileOutput), equalTo(true));
assertThat(Files.exists(mainFileOutput), equalTo(true));
}
@Test
public void testBuildingWithNoDebugDoesNotProduceAllCompileRulesOnDisk() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "simple_application_bundle_dwarf_and_dsym", tmp);
workspace.setUp();
workspace.enableDirCache();
Flavor platformFlavor = InternalFlavor.of("iphonesimulator-x86_64");
BuildTarget target =
BuildTargetFactory.newInstance("//:DemoApp")
.withAppendedFlavors(AppleDebugFormat.NONE.getFlavor());
BuildTarget binaryTarget =
BuildTargetFactory.newInstance("//:DemoAppBinary")
.withAppendedFlavors(platformFlavor, AppleDescriptions.NO_INCLUDE_FRAMEWORKS_FLAVOR);
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
workspace.runBuckCommand("clean").assertSuccess();
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
BuildTarget appTarget =
target.withFlavors(
AppleDebugFormat.NONE.getFlavor(), AppleDescriptions.NO_INCLUDE_FRAMEWORKS_FLAVOR);
Path binaryOutput =
workspace.getPath(
BuildTargets.getGenPath(filesystem, appTarget, "%s")
.resolve(target.getShortName() + ".app")
.resolve(target.getShortName()));
Path delegateFileOutput =
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
binaryTarget.withFlavors(
platformFlavor,
InternalFlavor.of("compile-" + sanitize("AppDelegate.m.o")),
AppleDescriptions.NO_INCLUDE_FRAMEWORKS_FLAVOR),
"%s")
.resolve("AppDelegate.m.o"));
Path mainFileOutput =
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
binaryTarget.withFlavors(
platformFlavor,
InternalFlavor.of("compile-" + sanitize("main.m.o")),
AppleDescriptions.NO_INCLUDE_FRAMEWORKS_FLAVOR),
"%s")
.resolve("main.m.o"));
assertThat(Files.exists(binaryOutput), equalTo(true));
assertThat(Files.exists(delegateFileOutput), equalTo(false));
assertThat(Files.exists(mainFileOutput), equalTo(false));
}
@Test
public void testBuildingWithDwarfAndDsymDoesNotProduceAllCompileRulesOnDisk() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "simple_application_bundle_dwarf_and_dsym", tmp);
workspace.setUp();
workspace.enableDirCache();
Flavor platformFlavor = InternalFlavor.of("iphonesimulator-x86_64");
BuildTarget target =
BuildTargetFactory.newInstance("//:DemoApp")
.withAppendedFlavors(AppleDebugFormat.DWARF_AND_DSYM.getFlavor());
BuildTarget binaryTarget =
BuildTargetFactory.newInstance("//:DemoAppBinary")
.withAppendedFlavors(platformFlavor, AppleDescriptions.NO_INCLUDE_FRAMEWORKS_FLAVOR);
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
workspace.runBuckCommand("clean").assertSuccess();
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
BuildTarget appTarget =
target.withFlavors(
AppleDebugFormat.DWARF_AND_DSYM.getFlavor(),
AppleDescriptions.NO_INCLUDE_FRAMEWORKS_FLAVOR);
Path binaryOutput =
workspace.getPath(
BuildTargets.getGenPath(filesystem, appTarget, "%s")
.resolve(target.getShortName() + ".app")
.resolve(target.getShortName()));
Path delegateFileOutput =
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
binaryTarget.withFlavors(
platformFlavor,
InternalFlavor.of("compile-" + sanitize("AppDelegate.m.o")),
AppleDescriptions.NO_INCLUDE_FRAMEWORKS_FLAVOR),
"%s")
.resolve("AppDelegate.m.o"));
Path mainFileOutput =
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
binaryTarget.withFlavors(
platformFlavor,
InternalFlavor.of("compile-" + sanitize("main.m.o")),
AppleDescriptions.NO_INCLUDE_FRAMEWORKS_FLAVOR),
"%s")
.resolve("main.m.o"));
assertThat(Files.exists(binaryOutput), equalTo(true));
assertThat(Files.exists(delegateFileOutput), equalTo(false));
assertThat(Files.exists(mainFileOutput), equalTo(false));
}
@Test
public void testFlavoredAppleBundleBuildsAndDsymFileIsNotCreatedAndBinaryIsStripped()
throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "simple_application_bundle_no_debug", tmp);
workspace.setUp();
BuildTarget target = BuildTargetFactory.newInstance("//:DemoApp#no-debug");
workspace
.runBuckCommand(
"build",
"--config",
"apple.default_debug_info_format_for_binaries=dwarf_and_dsym",
target.getFullyQualifiedName())
.assertSuccess();
assertThat(
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(filesystem, target, "%s")
.resolve(target.getShortName() + ".app.dSYM")
.resolve("Contents/Resources/DWARF")
.resolve(target.getShortName()))),
equalTo(false));
assertThat(
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
target.withFlavors(AppleDebugFormat.DWARF_AND_DSYM.getFlavor()),
"%s")
.resolve(target.getShortName() + ".app.dSYM")
.resolve("Contents/Resources/DWARF")
.resolve(target.getShortName()))),
equalTo(false));
assertThat(
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(filesystem, target.withFlavors(), "%s")
.resolve(target.getShortName() + ".app.dSYM")
.resolve("Contents/Resources/DWARF")
.resolve(target.getShortName()))),
equalTo(false));
BuildTarget appTarget =
target.withFlavors(
AppleDebugFormat.NONE.getFlavor(), AppleDescriptions.NO_INCLUDE_FRAMEWORKS_FLAVOR);
Path binaryOutput =
workspace.getPath(
BuildTargets.getGenPath(filesystem, appTarget, "%s")
.resolve(target.getShortName() + ".app")
.resolve(target.getShortName()));
assertThat(Files.exists(binaryOutput), equalTo(true));
ProcessExecutor.Result hasSymbol = workspace.runCommand("nm", binaryOutput.toString());
String stdout = hasSymbol.getStdout().orElse("");
assertThat(stdout, Matchers.not(containsString("t -[AppDelegate window]")));
assertThat(stdout, containsString("U _UIApplicationMain"));
}
@Test
public void testAppleBundleDebugFormatRespectsDefaultConfigSettingDSYM() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "simple_application_bundle_no_debug", tmp);
workspace.setUp();
BuildTarget target = BuildTargetFactory.newInstance("//:DemoApp");
workspace
.runBuckCommand(
"build",
"--config",
"apple.default_debug_info_format_for_binaries=dwarf_and_dsym",
target.getFullyQualifiedName())
.assertSuccess();
BuildTarget appTarget =
target.withFlavors(
AppleDebugFormat.DWARF_AND_DSYM.getFlavor(),
AppleDescriptions.NO_INCLUDE_FRAMEWORKS_FLAVOR);
Path dwarfPath =
workspace.getPath(
BuildTargets.getGenPath(filesystem, appTarget, "%s")
.resolve(appTarget.getShortName() + ".app.dSYM")
.resolve("Contents/Resources/DWARF")
.resolve(appTarget.getShortName()));
assertThat(Files.exists(dwarfPath), equalTo(true));
AppleDsymTestUtil.checkDsymFileHasDebugSymbolForMain(workspace, dwarfPath);
}
@Test
public void testAppleBundleDebugFormatRespectsDefaultConfigSettingNoDebug() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "simple_application_bundle_no_debug", tmp);
workspace.setUp();
BuildTarget target = BuildTargetFactory.newInstance("//:DemoApp");
workspace
.runBuckCommand(
"build",
"--config",
"apple.default_debug_info_format_for_binaries=none",
target.getFullyQualifiedName())
.assertSuccess();
BuildTarget appTarget = target.withFlavors(AppleDebugFormat.NONE.getFlavor());
assertThat(
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(filesystem, appTarget, "%s")
.resolve(appTarget.getShortName() + ".app.dSYM")
.resolve("Contents/Resources/DWARF")
.resolve(appTarget.getShortName()))),
equalTo(false));
}
@Test
public void multiarchBinaryShouldCopyLinkMapOfComponents() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
BuildTarget singleArchI386Target =
BuildTargetFactory.newInstance("//:DemoApp#iphonesimulator-i386");
BuildTarget singleArchX8664Target =
BuildTargetFactory.newInstance("//:DemoApp#iphonesimulator-x86_64");
BuildTarget target =
BuildTargetFactory.newInstance("//:DemoApp#iphonesimulator-i386,iphonesimulator-x86_64");
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "multiarch_binary_linkmap", tmp);
workspace.setUp();
workspace.runBuckBuild(target.getFullyQualifiedName()).assertSuccess();
assertTrue(
"Has link map for i386 arch.",
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(filesystem, target, "%s-LinkMap")
.resolve(
singleArchI386Target.getShortNameAndFlavorPostfix() + "-LinkMap.txt"))));
assertTrue(
"Has link map for x86_64 arch.",
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(filesystem, target, "%s-LinkMap")
.resolve(
singleArchX8664Target.getShortNameAndFlavorPostfix() + "-LinkMap.txt"))));
}
@Test
public void testBuildEmptySourceAppleBinaryDependsOnNonEmptyAppleLibrary() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "empty_source_targets", tmp);
workspace.setUp();
BuildTarget target = workspace.newBuildTarget("//:real-none2#macosx-x86_64");
ProjectWorkspace.ProcessResult result =
workspace.runBuckCommand("run", target.getFullyQualifiedName());
result.assertSuccess();
Assert.assertThat(result.getStdout(), equalTo("Hello"));
}
@Test
public void testAppleBinaryBuildsFatBinariesWithSwift() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "mixed_swift_objc_application_bundle_dwarf_and_dsym", tmp);
workspace.setUp();
BuildTarget target =
BuildTargetFactory.newInstance(
"//:DemoAppBinary#iphonesimulator-i386,iphonesimulator-x86_64,no-linkermap");
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
Path output = workspace.getPath(BuildTargets.getGenPath(filesystem, target, "%s"));
assertThat(Files.exists(output), is(true));
assertThat(
workspace.runCommand("file", output.toString()).getStdout().get(),
containsString("executable"));
ProcessExecutor.Result lipoVerifyResult =
workspace.runCommand("lipo", output.toString(), "-verify_arch", "i386", "x86_64");
assertEquals(lipoVerifyResult.getStderr().orElse(""), 0, lipoVerifyResult.getExitCode());
}
private static void assertIsSymbolicLink(Path link, Path target) throws IOException {
assertTrue(Files.isSymbolicLink(link));
assertEquals(target, Files.readSymbolicLink(link));
}
}