use of com.facebook.buck.apple.xcode.xcodeproj.PBXShellScriptBuildPhase in project buck by facebook.
the class NewNativeTargetProjectMutatorTest method testScriptBuildPhase.
@Test
public void testScriptBuildPhase() throws NoSuchBuildTargetException {
NewNativeTargetProjectMutator mutator = mutatorWithCommonDefaults();
TargetNode<?, ?> prebuildNode = XcodePrebuildScriptBuilder.createBuilder(BuildTargetFactory.newInstance("//foo:script")).setSrcs(ImmutableSortedSet.of(new FakeSourcePath("script/input.png"))).setOutputs(ImmutableSortedSet.of("helloworld.txt")).setCmd("echo \"hello world!\"").build();
mutator.setPostBuildRunScriptPhasesFromTargetNodes(ImmutableList.of(prebuildNode));
NewNativeTargetProjectMutator.Result result = mutator.buildTargetAndAddToProject(generatedProject, true);
PBXShellScriptBuildPhase phase = getSingletonPhaseByType(result.target, PBXShellScriptBuildPhase.class);
assertThat("Should set input paths correctly", "../script/input.png", is(equalTo(Iterables.getOnlyElement(phase.getInputPaths()))));
assertThat("Should set output paths correctly", "helloworld.txt", is(equalTo(Iterables.getOnlyElement(phase.getOutputPaths()))));
assertEquals("should set script correctly", "echo \"hello world!\"", phase.getShellScript());
}
use of com.facebook.buck.apple.xcode.xcodeproj.PBXShellScriptBuildPhase in project buck by facebook.
the class ProjectGeneratorTest method testAppleBundleRuleWithPostBuildScriptDependency.
@Test
public void testAppleBundleRuleWithPostBuildScriptDependency() throws IOException {
BuildTarget scriptTarget = BuildTarget.builder(rootPath, "//foo", "post_build_script").addFlavors(DEFAULT_FLAVOR).build();
TargetNode<?, ?> scriptNode = XcodePostbuildScriptBuilder.createBuilder(scriptTarget).setCmd("script.sh").build();
BuildTarget resourceTarget = BuildTarget.builder(rootPath, "//foo", "resource").build();
TargetNode<?, ?> resourceNode = AppleResourceBuilder.createBuilder(resourceTarget).setFiles(ImmutableSet.of(new FakeSourcePath("bar.png"))).setDirs(ImmutableSet.of()).build();
BuildTarget sharedLibraryTarget = BuildTarget.builder(rootPath, "//dep", "shared").addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR).build();
TargetNode<?, ?> sharedLibraryNode = AppleLibraryBuilder.createBuilder(sharedLibraryTarget).setDeps(ImmutableSortedSet.of(resourceTarget)).build();
BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "bundle").build();
TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget).setExtension(Either.ofLeft(AppleBundleExtension.BUNDLE)).setInfoPlist(new FakeSourcePath("Info.plist")).setBinary(sharedLibraryTarget).setDeps(ImmutableSortedSet.of(scriptTarget)).build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(scriptNode, resourceNode, sharedLibraryNode, bundleNode));
projectGenerator.createXcodeProjects();
PBXProject project = projectGenerator.getGeneratedProject();
PBXTarget target = assertTargetExistsAndReturnTarget(project, "//foo:bundle");
assertThat(target.getName(), equalTo("//foo:bundle"));
assertThat(target.isa(), equalTo("PBXNativeTarget"));
PBXShellScriptBuildPhase shellScriptBuildPhase = ProjectGeneratorTestUtils.getSingletonPhaseByType(target, PBXShellScriptBuildPhase.class);
assertThat(shellScriptBuildPhase.getShellScript(), equalTo("script.sh"));
// Assert that the post-build script phase comes after resources are copied.
assertThat(target.getBuildPhases().get(0), instanceOf(PBXResourcesBuildPhase.class));
assertThat(target.getBuildPhases().get(1), instanceOf(PBXShellScriptBuildPhase.class));
}
use of com.facebook.buck.apple.xcode.xcodeproj.PBXShellScriptBuildPhase in project bazel by bazelbuild.
the class XcodeprojGeneration method xcodeproj.
/** Generates a project file. */
public static PBXProject xcodeproj(Path workspaceRoot, Control control, Iterable<PbxReferencesProcessor> postProcessors) {
checkArgument(control.hasPbxproj(), "Must set pbxproj field on control proto.");
FileSystem fileSystem = workspaceRoot.getFileSystem();
XcodeprojPath<Path> outputPath = XcodeprojPath.converter().fromPath(RelativePaths.fromString(fileSystem, control.getPbxproj()));
NSDictionary projBuildConfigMap = new NSDictionary();
projBuildConfigMap.put("ARCHS", cpuArchitectures(control.getCpuArchitectureList()));
projBuildConfigMap.put("VALID_ARCHS", new NSArray(new NSString("armv7"), new NSString("armv7s"), new NSString("arm64"), new NSString("i386"), new NSString("x86_64")));
projBuildConfigMap.put("CLANG_ENABLE_OBJC_ARC", "YES");
projBuildConfigMap.put("SDKROOT", "iphoneos");
projBuildConfigMap.put("IPHONEOS_DEPLOYMENT_TARGET", "7.0");
projBuildConfigMap.put("GCC_VERSION", "com.apple.compilers.llvm.clang.1_0");
projBuildConfigMap.put("CODE_SIGN_IDENTITY[sdk=iphoneos*]", "iPhone Developer");
// Disable bitcode for now.
// TODO(bazel-team): Need to re-enable once we have real Xcode 7 support.
projBuildConfigMap.put("ENABLE_BITCODE", "NO");
for (XcodeprojBuildSetting projectSetting : control.getBuildSettingList()) {
projBuildConfigMap.put(projectSetting.getName(), projectSetting.getValue());
}
PBXProject project = new PBXProject(outputPath.getProjectName());
project.getMainGroup().setPath(workspaceRoot.toString());
if (workspaceRoot.isAbsolute()) {
project.getMainGroup().setSourceTree(SourceTree.ABSOLUTE);
}
try {
project.getBuildConfigurationList().getBuildConfigurationsByName().get(DEFAULT_OPTIONS_NAME).setBuildSettings(projBuildConfigMap);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
Map<String, TargetInfo> targetInfoByLabel = new HashMap<>();
List<String> usedTargetNames = new ArrayList<>();
PBXFileReferences fileReferences = new PBXFileReferences();
LibraryObjects libraryObjects = new LibraryObjects(fileReferences);
PBXBuildFiles pbxBuildFiles = new PBXBuildFiles(fileReferences);
Resources resources = Resources.fromTargetControls(fileSystem, pbxBuildFiles, control.getTargetList());
Xcdatamodels xcdatamodels = Xcdatamodels.fromTargetControls(fileSystem, pbxBuildFiles, control.getTargetList());
// We use a hash set for the Project Navigator files so that the same PBXFileReference does not
// get added twice. Because PBXFileReference uses equality-by-identity semantics, this requires
// the PBXFileReferences cache to properly return the same reference for functionally-equivalent
// files.
Set<PBXReference> projectNavigatorFiles = new LinkedHashSet<>();
for (TargetControl targetControl : control.getTargetList()) {
checkArgument(targetControl.hasName(), "TargetControl requires a name: %s", targetControl);
checkArgument(targetControl.hasLabel(), "TargetControl requires a label: %s", targetControl);
ProductType productType = productType(targetControl);
Preconditions.checkArgument((productType != ProductType.APPLICATION) || hasAtLeastOneCompilableSource(targetControl), APP_NEEDS_SOURCE_ERROR);
PBXSourcesBuildPhase sourcesBuildPhase = new PBXSourcesBuildPhase();
for (SourceFile source : SourceFile.allSourceFiles(fileSystem, targetControl)) {
PBXFileReference fileRef = fileReferences.get(FileReference.of(source.path().toString(), SourceTree.GROUP));
projectNavigatorFiles.add(fileRef);
if (Equaling.of(source.buildType(), BuildType.NO_BUILD)) {
continue;
}
PBXBuildFile buildFile = new PBXBuildFile(fileRef);
if (Equaling.of(source.buildType(), BuildType.NON_ARC_BUILD)) {
buildFile.setSettings(Optional.of(nonArcCompileSettings()));
}
sourcesBuildPhase.getFiles().add(buildFile);
}
sourcesBuildPhase.getFiles().addAll(xcdatamodels.buildFiles().get(targetControl));
PBXFileReference productReference = fileReferences.get(productReference(targetControl));
projectNavigatorFiles.add(productReference);
NSDictionary targetBuildConfigMap = new NSDictionary();
// TODO(bazel-team): Stop adding the workspace root automatically once the
// released version of Bazel starts passing it.
targetBuildConfigMap.put("USER_HEADER_SEARCH_PATHS", headerSearchPaths(plus(targetControl.getUserHeaderSearchPathList(), "$(WORKSPACE_ROOT)")));
targetBuildConfigMap.put("HEADER_SEARCH_PATHS", headerSearchPaths(plus(targetControl.getHeaderSearchPathList(), "$(inherited)")));
targetBuildConfigMap.put("FRAMEWORK_SEARCH_PATHS", frameworkSearchPaths(Iterables.concat(targetControl.getFrameworkList(), targetControl.getFrameworkSearchPathOnlyList())));
targetBuildConfigMap.put("WORKSPACE_ROOT", workspaceRoot.toString());
if (targetControl.hasPchPath()) {
targetBuildConfigMap.put("GCC_PREFIX_HEADER", "$(WORKSPACE_ROOT)/" + targetControl.getPchPath());
}
targetBuildConfigMap.put("PRODUCT_NAME", productName(targetControl));
if (targetControl.hasInfoplist()) {
targetBuildConfigMap.put("INFOPLIST_FILE", "$(WORKSPACE_ROOT)/" + targetControl.getInfoplist());
}
// Double-quotes in copt strings need to be escaped for XCode.
if (targetControl.getCoptCount() > 0) {
List<String> escapedCopts = Lists.transform(targetControl.getCoptList(), QUOTE_ESCAPER.asFunction());
targetBuildConfigMap.put("OTHER_CFLAGS", NSObject.wrap(escapedCopts));
}
targetBuildConfigMap.put("OTHER_LDFLAGS", NSObject.wrap(otherLdflags(targetControl)));
for (XcodeprojBuildSetting setting : targetControl.getBuildSettingList()) {
String name = setting.getName();
String value = setting.getValue();
// TODO(bazel-team): Remove this hack after next Bazel release.
if (name.equals("CODE_SIGN_ENTITLEMENTS") && !value.startsWith("$")) {
value = "$(WORKSPACE_ROOT)/" + value;
}
targetBuildConfigMap.put(name, value);
}
// Note that HFS+ (the Mac filesystem) is usually case insensitive, so we cast all target
// names to lower case before checking for duplication because otherwise users may end up
// having duplicated intermediate build directories that can interfere with the build.
String targetName = targetControl.getName();
String targetNameInLowerCase = targetName.toLowerCase();
if (usedTargetNames.contains(targetNameInLowerCase)) {
// Use the label in the odd case where we have two targets with the same name.
targetName = targetControl.getLabel();
targetNameInLowerCase = targetName.toLowerCase();
}
checkState(!usedTargetNames.contains(targetNameInLowerCase), "Name (case-insensitive) already exists for target with label/name %s/%s in list: %s", targetControl.getLabel(), targetControl.getName(), usedTargetNames);
usedTargetNames.add(targetNameInLowerCase);
PBXNativeTarget target = new PBXNativeTarget(targetName, productType);
try {
target.getBuildConfigurationList().getBuildConfigurationsByName().get(DEFAULT_OPTIONS_NAME).setBuildSettings(targetBuildConfigMap);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
target.setProductReference(productReference);
// We only add frameworks here and not dylibs because of differences in how
// Xcode 6 and Xcode 7 specify dylibs in the project organizer.
// (Xcode 6 -> *.dylib, Xcode 7 -> *.tbd)
PBXFrameworksBuildPhase frameworksPhase = buildFrameworksInfo(libraryObjects, targetControl);
PBXResourcesBuildPhase resourcesPhase = resources.resourcesBuildPhase(targetControl);
for (String importedArchive : targetControl.getImportedLibraryList()) {
PBXFileReference fileReference = fileReferences.get(FileReference.of(importedArchive, SourceTree.GROUP).withExplicitFileType(FILE_TYPE_ARCHIVE_LIBRARY));
projectNavigatorFiles.add(fileReference);
}
project.getTargets().add(target);
target.getBuildPhases().add(frameworksPhase);
target.getBuildPhases().add(sourcesBuildPhase);
target.getBuildPhases().add(resourcesPhase);
checkState(!Mapping.of(targetInfoByLabel, targetControl.getLabel()).isPresent(), "Mapping already exists for target with label %s in map: %s", targetControl.getLabel(), targetInfoByLabel);
targetInfoByLabel.put(targetControl.getLabel(), new TargetInfo(targetControl, target, frameworksPhase, resourcesPhase, new PBXBuildFile(productReference), new LocalPBXTargetDependency(new LocalPBXContainerItemProxy(project, target, ProxyType.TARGET_REFERENCE)), targetBuildConfigMap));
}
for (HasProjectNavigatorFiles references : ImmutableList.of(pbxBuildFiles, libraryObjects)) {
Iterables.addAll(projectNavigatorFiles, references.mainGroupReferences());
}
Iterable<PBXReference> processedProjectFiles = projectNavigatorFiles;
for (PbxReferencesProcessor postProcessor : postProcessors) {
processedProjectFiles = postProcessor.process(processedProjectFiles);
}
Iterables.addAll(project.getMainGroup().getChildren(), processedProjectFiles);
for (TargetInfo targetInfo : targetInfoByLabel.values()) {
TargetControl targetControl = targetInfo.control;
for (DependencyControl dependency : targetControl.getDependencyList()) {
targetInfo.addDependencyInfo(dependency, targetInfoByLabel);
}
if (!Equaling.of(ProductType.STATIC_LIBRARY, productType(targetControl)) && !targetControl.getImportedLibraryList().isEmpty()) {
// We add a script build phase to copy the imported libraries to BUILT_PRODUCT_DIR with
// unique names before linking them to work around an Xcode issue where imported libraries
// with duplicated names lead to link errors.
//
// Internally Xcode uses linker flag -l{LIBRARY_NAME} to link a particular library and
// delegates to the linker to locate the actual library using library search paths. So given
// two imported libraries with the same name: a/b/libfoo.a, c/d/libfoo.a, Xcode uses
// duplicate linker flag -lfoo to link both of the libraries. Depending on the order of
// the library search paths, the linker will only be able to locate and link one of the
// libraries.
//
// With this workaround using a script build phase, all imported libraries to link have
// unique names. For the previous example with a/b/libfoo.a and c/d/libfoo.a, the script
// build phase will copy them to BUILT_PRODUCTS_DIR with unique names libfoo_b_a.a and
// libfoo_d_c.a, respectively. The linker flags Xcode uses to link them will be
// -lfoo_d_c and -lfoo_b_a, with no duplication.
PBXShellScriptBuildPhase scriptBuildPhase = new PBXShellScriptBuildPhase();
scriptBuildPhase.setShellScript("for ((i=0; i < ${SCRIPT_INPUT_FILE_COUNT}; i++)) do\n" + " INPUT_FILE=\"SCRIPT_INPUT_FILE_${i}\"\n" + " OUTPUT_FILE=\"SCRIPT_OUTPUT_FILE_${i}\"\n" + " cp -v -f \"${!INPUT_FILE}\" \"${!OUTPUT_FILE}\"\n" + "done");
for (String importedLibrary : targetControl.getImportedLibraryList()) {
String uniqueImportedLibrary = uniqueImportedLibraryName(importedLibrary);
scriptBuildPhase.getInputPaths().add("$(WORKSPACE_ROOT)/" + importedLibrary);
scriptBuildPhase.getOutputPaths().add("$(BUILT_PRODUCTS_DIR)/" + uniqueImportedLibrary);
FileReference fileReference = FileReference.of(uniqueImportedLibrary, SourceTree.BUILT_PRODUCTS_DIR).withExplicitFileType(FILE_TYPE_ARCHIVE_LIBRARY);
targetInfo.frameworksPhase.getFiles().add(pbxBuildFiles.getStandalone(fileReference));
}
targetInfo.nativeTarget.getBuildPhases().add(scriptBuildPhase);
}
}
return project;
}
use of com.facebook.buck.apple.xcode.xcodeproj.PBXShellScriptBuildPhase in project buck by facebook.
the class ProjectGeneratorTest method testHalideLibraryRule.
@Test
public void testHalideLibraryRule() throws IOException {
BuildTarget compilerTarget = BuildTarget.builder(rootPath, "//foo", "lib").addFlavors(HalideLibraryDescription.HALIDE_COMPILER_FLAVOR).build();
TargetNode<?, ?> compiler = new HalideLibraryBuilder(compilerTarget).setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("main.cpp")), SourceWithFlags.of(new FakeSourcePath("filter.cpp")))).build();
BuildTarget libTarget = BuildTarget.builder(rootPath, "//foo", "lib").build();
TargetNode<?, ?> lib = new HalideLibraryBuilder(libTarget).build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(compiler, lib));
projectGenerator.createXcodeProjects();
PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib");
assertThat(target.isa(), equalTo("PBXNativeTarget"));
assertHasConfigurations(target, "Debug", "Release", "Profile");
assertEquals(1, target.getBuildPhases().size());
PBXShellScriptBuildPhase scriptPhase = ProjectGeneratorTestUtils.getSingletonPhaseByType(target, PBXShellScriptBuildPhase.class);
assertEquals(0, scriptPhase.getInputPaths().size());
assertEquals(0, scriptPhase.getOutputPaths().size());
// Note that we require that both the Halide "compiler" and the unflavored
// library target are present in the requiredBuildTargets, so that both the
// compiler and the generated header for the pipeline will be available for
// use by the Xcode compilation step.
ImmutableSet<BuildTarget> requiredBuildTargets = projectGenerator.getRequiredBuildTargets();
assertTrue(requiredBuildTargets.contains(compilerTarget));
assertThat(requiredBuildTargets, hasItem(libTarget.withFlavors(HalideLibraryDescription.HALIDE_COMPILE_FLAVOR, DEFAULT_PLATFORM.getFlavor())));
}
use of com.facebook.buck.apple.xcode.xcodeproj.PBXShellScriptBuildPhase in project buck by facebook.
the class ProjectGeneratorTest method testAppleBundleRuleWithPreBuildScriptDependency.
@Test
public void testAppleBundleRuleWithPreBuildScriptDependency() throws IOException {
BuildTarget scriptTarget = BuildTarget.builder(rootPath, "//foo", "pre_build_script").addFlavors(DEFAULT_FLAVOR).build();
TargetNode<?, ?> scriptNode = XcodePrebuildScriptBuilder.createBuilder(scriptTarget).setCmd("script.sh").build();
BuildTarget resourceTarget = BuildTarget.builder(rootPath, "//foo", "resource").build();
TargetNode<?, ?> resourceNode = AppleResourceBuilder.createBuilder(resourceTarget).setFiles(ImmutableSet.of(new FakeSourcePath("bar.png"))).setDirs(ImmutableSet.of()).build();
BuildTarget sharedLibraryTarget = BuildTarget.builder(rootPath, "//dep", "shared").addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR).build();
TargetNode<?, ?> sharedLibraryNode = AppleLibraryBuilder.createBuilder(sharedLibraryTarget).setDeps(ImmutableSortedSet.of(resourceTarget)).build();
BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "bundle").build();
TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget).setExtension(Either.ofLeft(AppleBundleExtension.BUNDLE)).setInfoPlist(new FakeSourcePath("Info.plist")).setBinary(sharedLibraryTarget).setDeps(ImmutableSortedSet.of(scriptTarget)).build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(scriptNode, resourceNode, sharedLibraryNode, bundleNode));
projectGenerator.createXcodeProjects();
PBXProject project = projectGenerator.getGeneratedProject();
PBXTarget target = assertTargetExistsAndReturnTarget(project, "//foo:bundle");
assertThat(target.getName(), equalTo("//foo:bundle"));
assertThat(target.isa(), equalTo("PBXNativeTarget"));
PBXShellScriptBuildPhase shellScriptBuildPhase = ProjectGeneratorTestUtils.getSingletonPhaseByType(target, PBXShellScriptBuildPhase.class);
assertThat(shellScriptBuildPhase.getShellScript(), equalTo("script.sh"));
// Assert that the pre-build script phase comes before resources are copied.
assertThat(target.getBuildPhases().get(0), instanceOf(PBXShellScriptBuildPhase.class));
assertThat(target.getBuildPhases().get(1), instanceOf(PBXResourcesBuildPhase.class));
}
Aggregations