/* * * Copyright 2004-2005 The Ant-Contrib project * * 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 net.sf.antcontrib.cpptasks.apple; import java.io.File; import java.io.IOException; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import javax.xml.transform.TransformerConfigurationException; import net.sf.antcontrib.cpptasks.CCTask; import net.sf.antcontrib.cpptasks.CUtil; import net.sf.antcontrib.cpptasks.TargetInfo; import net.sf.antcontrib.cpptasks.compiler.CommandLineCompilerConfiguration; import net.sf.antcontrib.cpptasks.compiler.CommandLineLinkerConfiguration; import net.sf.antcontrib.cpptasks.compiler.ProcessorConfiguration; import net.sf.antcontrib.cpptasks.gcc.GccCCompiler; import net.sf.antcontrib.cpptasks.ide.DependencyDef; import net.sf.antcontrib.cpptasks.ide.ProjectDef; import net.sf.antcontrib.cpptasks.ide.ProjectWriter; import org.apache.tools.ant.BuildException; import org.xml.sax.SAXException; /** * Writes a Apple Xcode 2.1+ project directory. XCode stores project * configuration as a PropertyList. Though it will always write the project * as a Cocoa Old-Style ASCII property list, it will read projects * stored using Cocoa's XML Property List format. */ public final class XcodeProjectWriter implements ProjectWriter { /** * Constructor. */ public XcodeProjectWriter() { } /** * Writes a project definition file. * * @param fileName File name base, writer may append appropriate extension * @param task cc task for which to write project * @param projectDef project element * @param targets compilation targets * @param linkTarget link target * @throws IOException if error writing project file */ public void writeProject(final File fileName, final CCTask task, final ProjectDef projectDef, final List sources, final Map targets, final TargetInfo linkTarget) throws IOException { File xcodeDir = new File(fileName + ".xcodeproj"); if (!projectDef.getOverwrite() && xcodeDir.exists()) { throw new BuildException("Not allowed to overwrite project file " + xcodeDir.toString()); } CommandLineCompilerConfiguration compilerConfig = getBaseCompilerConfiguration(targets); if (compilerConfig == null) { throw new BuildException( "Unable to find compilation target using GNU C++ compiler"); } CommandLineLinkerConfiguration linkerConfig = null; if (linkTarget.getConfiguration() instanceof CommandLineLinkerConfiguration) { linkerConfig = (CommandLineLinkerConfiguration) linkTarget.getConfiguration(); } String projectName = projectDef.getName(); if (projectName == null) { projectName = fileName.getName(); } final String basePath = fileName.getAbsoluteFile().getParent(); xcodeDir.mkdir(); File xcodeProj = new File(xcodeDir, "project.pbxproj"); // // create property list // Map propertyList = new HashMap(); propertyList.put("archiveVersion", "1"); propertyList.put("classes", new HashMap()); propertyList.put("objectVersion", "42"); Map objects = new HashMap(); final String sourceTree = "<source>"; // // add source files and source group to property list // List sourceGroupChildren = addSources(objects, "SOURCE_ROOT", basePath, targets); PBXObjectRef sourceGroup = createPBXGroup("Source", sourceTree, sourceGroupChildren); objects.put(sourceGroup.getID(), sourceGroup.getProperties()); // // add product to property list // PBXObjectRef product = addProduct(objects, linkTarget); List productsList = new ArrayList(); productsList.add(product); PBXObjectRef productsGroup = createPBXGroup("Products", sourceTree, productsList); objects.put(productsGroup.getID(), productsGroup.getProperties()); // // add documentation group to property list // PBXObjectRef documentationGroup = addDocumentationGroup(objects, sourceTree); // // add main group containing source, products and documentation group // ArrayList groups = new ArrayList(3); groups.add(sourceGroup); groups.add(documentationGroup); groups.add(productsGroup); PBXObjectRef mainGroup = createPBXGroup(projectName, sourceTree, groups); StringBuffer comments = new StringBuffer(); for(Iterator iter = projectDef.getComments().iterator(); iter.hasNext();) { comments.append(iter.next()); } if (comments.length() > 0) { mainGroup.getProperties().put("comments", comments.toString()); } objects.put(mainGroup.getID(), mainGroup.getProperties()); // // add project configurations // PBXObjectRef compilerConfigurations = addProjectConfigurationList(objects, basePath, projectDef.getDependencies(), compilerConfig, linkerConfig); String projectDirPath = ""; List projectTargets = new ArrayList(); // // add project to property list // // // Calculate path (typically several ../..) of the root directory // (where build.xml lives) relative to the XCode project directory. // XCode 3.0 will now prompt user to supply the value if not specified. String projectRoot = CUtil.toUnixPath( CUtil.getRelativePath(basePath, projectDef.getProject().getBaseDir())); PBXObjectRef project = createPBXProject(compilerConfigurations, mainGroup, projectDirPath, projectRoot, projectTargets); objects.put(project.getID(), project.getProperties()); List frameworkBuildFiles = new ArrayList(); for (Iterator iter = projectDef.getDependencies().iterator(); iter.hasNext();) { DependencyDef dependency = (DependencyDef) iter.next(); PBXObjectRef buildFile = addDependency(objects, project, groups, basePath, dependency); if (buildFile != null) { frameworkBuildFiles.add(buildFile); } } // // add description of native target (that is the executable or // shared library) // PBXObjectRef nativeTarget = addNativeTarget(objects, linkTarget, product, projectName, sourceGroupChildren, frameworkBuildFiles); projectTargets.add(nativeTarget); // // finish up overall property list // propertyList.put("objects", objects); propertyList.put("rootObject", project.getID()); // // write property list out to XML file // try { PropertyListSerialization.serialize(propertyList, projectDef.getComments(), xcodeProj); } catch (TransformerConfigurationException ex) { throw new IOException(ex.toString()); } catch (SAXException ex) { if (ex.getException() instanceof IOException) { throw (IOException) ex.getException(); } throw new IOException(ex.toString()); } } /** * Adds a dependency to the object graph. * @param objects * @param project * @param mainGroupChildren * @param baseDir * @param dependency * @return PBXBuildFile to add to PBXFrameworksBuildPhase. */ private PBXObjectRef addDependency(final Map objects, final PBXObjectRef project, final List mainGroupChildren, final String baseDir, final DependencyDef dependency) { if (dependency.getFile() != null) { File xcodeDir = new File(dependency.getFile().getAbsolutePath() + ".xcodeproj"); if (xcodeDir.exists()) { PBXObjectRef xcodePrj = createPBXFileReference("SOURCE_ROOT", baseDir, xcodeDir); mainGroupChildren.add(xcodePrj); objects.put(xcodePrj.getID(), xcodePrj.getProperties()); int proxyType = 2; PBXObjectRef proxy = createPBXContainerItemProxy( xcodePrj, proxyType, dependency.getName()); objects.put(proxy.getID(), proxy.getProperties()); PBXObjectRef referenceProxy = createPBXReferenceProxy(proxy, dependency); objects.put(referenceProxy.getID(), referenceProxy.getProperties()); PBXObjectRef buildFile = createPBXBuildFile(referenceProxy, Collections.EMPTY_MAP); objects.put(buildFile.getID(), buildFile.getProperties()); List productsChildren = new ArrayList(); productsChildren.add(referenceProxy); PBXObjectRef products = createPBXGroup("Products", "<group>", productsChildren); objects.put(products.getID(), products.getProperties()); Map projectReference = new HashMap(); projectReference.put("ProductGroup", products); projectReference.put("ProjectRef", xcodePrj); List projectReferences = (List) project.getProperties().get("ProjectReferences"); if (projectReferences == null) { projectReferences = new ArrayList(); project.getProperties().put("ProjectReferences", projectReferences); } projectReferences.add(projectReference); return buildFile; } } return null; } /** * Add documentation group to map of objects. * @param objects object map. * @param sourceTree source tree description. * @return documentation group. */ private PBXObjectRef addDocumentationGroup(final Map objects, final String sourceTree) { List productsList = new ArrayList(); PBXObjectRef products = createPBXGroup("Documentation", sourceTree, productsList); objects.put(products.getID(), products.getProperties()); return products; } /** * Add file reference of product to map of objects. * @param objects object map. * @param linkTarget build description for executable or shared library. * @return file reference to generated executable or shared library. */ private PBXObjectRef addProduct(final Map objects, final TargetInfo linkTarget) { // // create file reference for executable file // forget Ant's location, just place in XCode's default location PBXObjectRef executable = createPBXFileReference("BUILD_PRODUCTS_DIR", linkTarget.getOutput().getParent(), linkTarget.getOutput()); Map executableProperties = executable.getProperties(); String fileType = getFileType(linkTarget); executableProperties.put("explicitFileType", fileType); executableProperties.put("includeInIndex", "0"); objects.put(executable.getID(), executableProperties); return executable; } /** * Add file references for all source files to map of objects. * @param objects map of objects. * @param sourceTree source tree. * @param basePath parent of XCode project dir * @param targets build targets. * @return list containing file references of source files. */ private List addSources(final Map objects, final String sourceTree, final String basePath, final Map targets) { List sourceGroupChildren = new ArrayList(); ArrayList sourceList = new ArrayList(targets.size()); Iterator targetIter = targets.values().iterator(); while (targetIter.hasNext()) { TargetInfo info = (TargetInfo) targetIter.next(); File[] targetsources = info.getSources(); for (int i = 0; i < targetsources.length; i++) { sourceList.add(targetsources[i]); } } Object[] sortedSources = sourceList.toArray(); Arrays.sort(sortedSources, new Comparator() { public int compare(final Object o1, final Object o2) { return (((File) o1).getName().compareTo( ((File) o2).getName())); } }); for (int i = 0; i < sortedSources.length; i++) { PBXObjectRef fileRef = createPBXFileReference(sourceTree, basePath, (File) sortedSources[i]); sourceGroupChildren.add(fileRef); objects.put(fileRef.getID(), fileRef.getProperties()); } return sourceGroupChildren; } /** * Add native target configuration list. * @param objects map of objects. * @param projectName project name. * @return build configurations for native target. */ private PBXObjectRef addNativeTargetConfigurationList(final Map objects, final String projectName) { // // Create a configuration list with // two stock configurations: Debug and Release // List configurations = new ArrayList(); Map debugSettings = new HashMap(); debugSettings.put("COPY_PHASE_STRIP", "NO"); debugSettings.put("GCC_DYNAMIC_NO_PIC", "NO"); debugSettings.put("GCC_ENABLE_FIX_AND_CONTINUE", "YES"); debugSettings.put("GCC_MODEL_TUNING", "G5"); debugSettings.put("GCC_OPTIMIZATION_LEVEL", "0"); debugSettings.put("INSTALL_PATH", "$(HOME)/bin"); debugSettings.put("PRODUCT_NAME", projectName); debugSettings.put("ZERO_LINK", "YES"); PBXObjectRef debugConfig = createXCBuildConfiguration("Debug", debugSettings); objects.put(debugConfig.getID(), debugConfig.getProperties()); configurations.add(debugConfig); Map releaseSettings = new HashMap(); List archs = new ArrayList(); archs.add("ppc"); archs.add("i386"); releaseSettings.put("ARCHS", archs); releaseSettings.put("GCC_GENERATE_DEBUGGING_SYMBOLS", "NO"); releaseSettings.put("GCC_MODEL_TUNING", "G5"); releaseSettings.put("INSTALL_PATH", "$(HOME)/bin"); releaseSettings.put("PRODUCT_NAME", projectName); PBXObjectRef releaseConfig = createXCBuildConfiguration("Release", releaseSettings); objects.put(releaseConfig.getID(), releaseConfig.getProperties()); configurations.add(releaseConfig); PBXObjectRef configurationList = createXCConfigurationList(configurations); objects.put(configurationList.getID(), configurationList.getProperties()); return configurationList; } /** * Add project configuration list. * @param objects map of objects. * @param baseDir base directory. * @param compilerConfig compiler configuration. * @return project configuration object. */ private PBXObjectRef addProjectConfigurationList(final Map objects, final String baseDir, final List dependencies, final CommandLineCompilerConfiguration compilerConfig, final CommandLineLinkerConfiguration linkerConfig) { // // Create a configuration list with // two stock configurations: Debug and Release // List configurations = new ArrayList(); Map debugSettings = new HashMap(); debugSettings.put("GCC_WARN_ABOUT_RETURN_TYPE", "YES"); debugSettings.put("GCC_WARN_UNUSED_VARIABLE", "YES"); debugSettings.put("PREBINDING", "NO"); debugSettings.put("SDKROOT", "/Developer/SDKs/MacOSX10.4u.sdk"); PBXObjectRef debugConfig = createXCBuildConfiguration("Debug", debugSettings); objects.put(debugConfig.getID(), debugConfig.getProperties()); configurations.add(debugConfig); Map releaseSettings = new HashMap(); releaseSettings.put("GCC_WARN_ABOUT_RETURN_TYPE", "YES"); releaseSettings.put("GCC_WARN_UNUSED_VARIABLE", "YES"); releaseSettings.put("PREBINDING", "NO"); releaseSettings.put("SDKROOT", "/Developer/SDKs/MacOSX10.4u.sdk"); PBXObjectRef releaseConfig = createXCBuildConfiguration("Release", releaseSettings); objects.put(releaseConfig.getID(), releaseConfig.getProperties()); configurations.add(releaseConfig); PBXObjectRef configurationList = createXCConfigurationList(configurations); Map projectConfigurationListProperties = configurationList.getProperties(); projectConfigurationListProperties.put("defaultConfigurationIsVisible", "0"); projectConfigurationListProperties.put("defaultConfigurationName", "Debug"); objects.put(configurationList.getID(), configurationList.getProperties()); // // add include paths to both configurations // File[] includeDirs = compilerConfig.getIncludePath(); if (includeDirs.length > 0) { ArrayList includePaths = new ArrayList(); Map includePathMap = new HashMap(); for (int i = 0; i < includeDirs.length; i++) { if(!CUtil.isSystemPath(includeDirs[i])) { String absPath = includeDirs[i].getAbsolutePath(); if (!includePathMap.containsKey(absPath)) { if(absPath.startsWith("/usr/")) { includePaths.add(CUtil.toUnixPath(absPath)); } else { String relPath = CUtil.toUnixPath( CUtil.getRelativePath(baseDir, includeDirs[i])); includePaths.add(relPath); } includePathMap.put(absPath, absPath); } } } includePaths.add("${inherited)"); debugSettings.put("HEADER_SEARCH_PATHS", includePaths); releaseSettings.put("HEADER_SEARCH_PATHS", includePaths); } // // add preprocessor definitions to both configurations // // String[] preArgs = compilerConfig.getPreArguments(); List defines = new ArrayList(); for (int i = 0; i < preArgs.length; i++) { if (preArgs[i].startsWith("-D")) { defines.add(preArgs[i].substring(2)); } } if (defines.size() > 0) { defines.add("$(inherited)"); debugSettings.put("GCC_PREPROCESSOR_DEFINITIONS", defines); releaseSettings.put("GCC_PREPROCESSOR_DEFINITIONS", defines); } if (linkerConfig != null) { Map librarySearchMap = new HashMap(); List librarySearchPaths = new ArrayList(); List otherLdFlags = new ArrayList(); String[] linkerArgs = linkerConfig.getEndArguments(); for (int i = 0; i < linkerArgs.length; i++) { if (linkerArgs[i].startsWith("-L")) { String libDir = linkerArgs[i].substring(2); if (!librarySearchMap.containsKey(libDir)) { if (!libDir.equals("/usr/lib")) { librarySearchPaths.add( CUtil.toUnixPath(CUtil.getRelativePath(baseDir, new File(libDir)))); } librarySearchMap.put(libDir, libDir); } } else if (linkerArgs[i].startsWith("-l")) { // // check if library is in dependencies list // String libName = linkerArgs[i].substring(2); boolean found = false; for(Iterator iter = dependencies.iterator();iter.hasNext();) { DependencyDef dependency = (DependencyDef) iter.next(); if (libName.startsWith(dependency.getName())) { File dependencyFile = dependency.getFile(); if (dependencyFile != null && new File(dependencyFile.getAbsolutePath() + ".xcodeproj").exists()) { found = true; break; } } } if (!found) { otherLdFlags.add(linkerArgs[i]); } } } debugSettings.put("LIBRARY_SEARCH_PATHS", librarySearchPaths); debugSettings.put("OTHER_LDFLAGS", otherLdFlags); releaseSettings.put("LIBRARY_SEARCH_PATHS", librarySearchPaths); releaseSettings.put("OTHER_LDFLAGS", otherLdFlags); } return configurationList; } /** * Add native target to map of objects. * @param objects map of objects. * @param linkTarget description of executable or shared library. * @param product product. * @param projectName project name. * @param sourceGroupChildren source files needed to build product. * @return native target. */ private PBXObjectRef addNativeTarget(final Map objects, final TargetInfo linkTarget, final PBXObjectRef product, final String projectName, final List sourceGroupChildren, final List frameworkBuildFiles) { PBXObjectRef buildConfigurations = addNativeTargetConfigurationList(objects, projectName); int buildActionMask = 2147483647; List buildPhases = new ArrayList(); Map settings = new HashMap(); settings.put("ATTRIBUTES", new ArrayList()); List buildFiles = new ArrayList(); for (Iterator iter = sourceGroupChildren.iterator(); iter.hasNext();) { PBXObjectRef sourceFile = (PBXObjectRef) iter.next(); PBXObjectRef buildFile = createPBXBuildFile(sourceFile, settings); buildFiles.add(buildFile); objects.put(buildFile.getID(), buildFile.getProperties()); } PBXObjectRef sourcesBuildPhase = createPBXSourcesBuildPhase(buildActionMask, buildFiles, false); objects.put(sourcesBuildPhase.getID(), sourcesBuildPhase.getProperties()); buildPhases.add(sourcesBuildPhase); buildActionMask = 8; PBXObjectRef frameworksBuildPhase = createPBXFrameworksBuildPhase(buildActionMask, frameworkBuildFiles, false); objects.put(frameworksBuildPhase.getID(), frameworksBuildPhase.getProperties()); buildPhases.add(frameworksBuildPhase); PBXObjectRef copyFilesBuildPhase = createPBXCopyFilesBuildPhase(8, "/usr/share/man/man1", "0", new ArrayList(), true); objects.put(copyFilesBuildPhase.getID(), copyFilesBuildPhase.getProperties()); buildPhases.add(copyFilesBuildPhase); List buildRules = new ArrayList(); List dependencies = new ArrayList(); String productInstallPath = "$(HOME)/bin"; String productType = getProductType(linkTarget); PBXObjectRef nativeTarget = createPBXNativeTarget(projectName, buildConfigurations, buildPhases, buildRules, dependencies, productInstallPath, projectName, product, productType); objects.put(nativeTarget.getID(), nativeTarget.getProperties()); return nativeTarget; } private int getProductTypeIndex(final TargetInfo linkTarget) { String outPath = linkTarget.getOutput().getPath(); String outExtension = null; int lastDot = outPath.lastIndexOf('.'); if (lastDot != -1) { outExtension = outPath.substring(lastDot); } if (".a".equalsIgnoreCase(outExtension) || ".lib".equalsIgnoreCase(outExtension)) { return 1; } else if (".dylib".equalsIgnoreCase(outExtension) || ".so".equalsIgnoreCase(outExtension) || ".dll".equalsIgnoreCase(outExtension)) { return 2; } return 0; } private String getProductType(final TargetInfo linkTarget) { switch(getProductTypeIndex(linkTarget)) { case 1: return "com.apple.product-type.library.static"; case 2: return "com.apple.product-type.library.dynamic"; default: return "com.apple.product-type.tool"; } } private String getFileType(final TargetInfo linkTarget) { switch(getProductTypeIndex(linkTarget)) { case 1: return "archive.ar"; case 2: return "compiled.mach-o.dylib"; default: return "compiled.mach-o.executable"; } } /** * Create PBXFileReference. * @param sourceTree source tree. * @param baseDir base directory. * @param file file. * @return PBXFileReference object. */ private static PBXObjectRef createPBXFileReference(final String sourceTree, final String baseDir, final File file) { Map map = new HashMap(); map.put("isa", "PBXFileReference"); String relPath = CUtil.toUnixPath(CUtil.getRelativePath(baseDir, file)); map.put("path", relPath); map.put("name", file.getName()); map.put("sourceTree", sourceTree); return new PBXObjectRef(map); } /** * Create PBXGroup. * @param name group name. * @param sourceTree source tree. * @param children list of PBXFileReferences. * @return group. */ private static PBXObjectRef createPBXGroup(final String name, final String sourceTree, final List children) { Map map = new HashMap(); map.put("isa", "PBXGroup"); map.put("name", name); map.put("sourceTree", sourceTree); map.put("children", children); return new PBXObjectRef(map); } /** * Create PBXProject. * @param buildConfigurationList build configuration list. * @param mainGroup main group. * @param projectDirPath project directory path. * @param targets targets. * @param projectRoot projectRoot directory relative to * @return project. */ private static PBXObjectRef createPBXProject(final PBXObjectRef buildConfigurationList, final PBXObjectRef mainGroup, final String projectDirPath, final String projectRoot, final List targets) { Map map = new HashMap(); map.put("isa", "PBXProject"); map.put("buildConfigurationList", buildConfigurationList.getID()); map.put("hasScannedForEncodings", "0"); map.put("mainGroup", mainGroup.getID()); map.put("projectDirPath", projectDirPath); map.put("targets", targets); map.put("projectRoot", projectRoot); return new PBXObjectRef(map); } /** * Create XCConfigurationList. * @param buildConfigurations build configurations. * @return configuration list. */ private static PBXObjectRef createXCConfigurationList(final List buildConfigurations) { Map map = new HashMap(); map.put("isa", "XCConfigurationList"); map.put("buildConfigurations", buildConfigurations); return new PBXObjectRef(map); } /** * Create XCBuildConfiguration. * @param name name. * @param buildSettings build settings. * @return build configuration. */ private static PBXObjectRef createXCBuildConfiguration(final String name, final Map buildSettings) { Map map = new HashMap(); map.put("isa", "XCBuildConfiguration"); map.put("buildSettings", buildSettings); map.put("name", name); return new PBXObjectRef(map); } /** * Create PBXNativeTarget. * @param name name. * @param buildConfigurationList build configuration list. * @param buildPhases build phases. * @param buildRules build rules. * @param dependencies dependencies. * @param productInstallPath product install path. * @param productName product name. * @param productReference file reference for product. * @param productType product type. * @return native target. */ private static PBXObjectRef createPBXNativeTarget(final String name, final PBXObjectRef buildConfigurationList, final List buildPhases, final List buildRules, final List dependencies, final String productInstallPath, final String productName, final PBXObjectRef productReference, final String productType) { Map map = new HashMap(); map.put("isa", "PBXNativeTarget"); map.put("buildConfigurationList", buildConfigurationList); map.put("buildPhases", buildPhases); map.put("buildRules", buildRules); map.put("dependencies", dependencies); map.put("name", name); map.put("productInstallPath", productInstallPath); map.put("productName", productName); map.put("productReference", productReference); map.put("productType", productType); return new PBXObjectRef(map); } /** * Create PBXSourcesBuildPhase. * @param buildActionMask build action mask. * @param files source files. * @param runOnly if true, phase should only be run on deployment. * @return PBXSourcesBuildPhase. */ private static PBXObjectRef createPBXSourcesBuildPhase(int buildActionMask, List files, boolean runOnly) { Map map = new HashMap(); map.put("buildActionMask", String.valueOf(buildActionMask)); map.put("files", files); map.put("isa", "PBXSourcesBuildPhase"); map.put("runOnlyForDeploymentPostprocessing", toString(runOnly)); return new PBXObjectRef(map); } /** * Create PBXBuildFile. * @param fileRef source file. * @param settings build settings. * @return PBXBuildFile. */ private static PBXObjectRef createPBXBuildFile(PBXObjectRef fileRef, Map settings) { Map map = new HashMap(); map.put("fileRef", fileRef); map.put("isa", "PBXBuildFile"); if (settings != null) { map.put("settings", settings); } return new PBXObjectRef(map); } /** * Create PBXFrameworksBuildPhase. * @param buildActionMask build action mask. * @param files files. * @param runOnly if true, phase should only be run on deployment. * @return PBXFrameworkBuildPhase. */ private static PBXObjectRef createPBXFrameworksBuildPhase( final int buildActionMask, final List files, final boolean runOnly) { Map map = new HashMap(); map.put("isa", "PBXFrameworksBuildPhase"); map.put("buildActionMask", NumberFormat.getIntegerInstance(Locale.US).format(buildActionMask)); map.put("files", files); map.put("runOnlyForDeploymentPostprocessing", toString(runOnly)); return new PBXObjectRef(map); } /** * Create a build phase that copies files to a destination. * @param buildActionMask build action mask. * @param dstPath destination path. * @param dstSubfolderSpec subfolder spec. * @param files files. * @param runOnly if true, phase should only be run on deployment. * @return PBXCopyFileBuildPhase. */ private static PBXObjectRef createPBXCopyFilesBuildPhase( final int buildActionMask, final String dstPath, final String dstSubfolderSpec, final List files, final boolean runOnly) { Map map = new HashMap(); map.put("isa", "PBXCopyFilesBuildPhase"); map.put("buildActionMask", NumberFormat.getIntegerInstance(Locale.US).format(buildActionMask)); map.put("dstPath", dstPath); map.put("dstSubfolderSpec", dstSubfolderSpec); map.put("files", files); map.put("runOnlyForDeploymentPostprocessing", toString(runOnly)); return new PBXObjectRef(map); } /** * Create a proxy for a file in a different project. * @param containerPortal XcodeProject containing file. * @param proxyType proxy type. * @return PBXContainerItemProxy. */ private static PBXObjectRef createPBXContainerItemProxy( final PBXObjectRef containerPortal, final int proxyType, final String remoteInfo) { Map map = new HashMap(); map.put("isa", "PBXContainerItemProxy"); map.put("containerPortal", containerPortal); map.put("proxyType", NumberFormat.getIntegerInstance(Locale.US).format(proxyType)); map.put("remoteInfo", remoteInfo); return new PBXObjectRef(map); } /** * Create a proxy for a file in a different project. * @param remoteRef PBXContainerItemProxy for reference. * @param dependency dependency. * @return PBXContainerItemProxy. */ private static PBXObjectRef createPBXReferenceProxy( final PBXObjectRef remoteRef, final DependencyDef dependency) { Map map = new HashMap(); map.put("isa", "PBXReferenceProxy"); String fileType = "compiled.mach-o.dylib"; map.put("fileType", fileType); map.put("remoteRef", remoteRef); map.put("path", dependency.getFile().getName() + ".dylib"); map.put("sourceTree", "BUILT_PRODUCTS_DIR"); return new PBXObjectRef(map); } /** * Method returns "1" for true, "0" for false. * @param b boolean value. * @return "1" for true, "0" for false. */ private static String toString(boolean b) { if (b) { return "1"; } else { return "0"; } } /** * Represents a property map with an 96 bit identity. * When placed in a property list, this object will * output the string representation of the identity * which XCode uses to find the corresponding property * bag in the "objects" property of the top-level property list. */ private static final class PBXObjectRef { /** * Identifier. */ private final String id; /** * Properties. */ private final Map properties; /** * Next available identifier. */ private static int nextID = 0; /** * Create reference. * @param props properties. */ public PBXObjectRef(final Map props) { if (props == null) { throw new NullPointerException("props"); } StringBuffer buf = new StringBuffer("000000000000000000000000"); String idStr = Integer.toHexString(nextID++); buf.replace(buf.length() - idStr.length(), buf.length(), idStr); id = buf.toString(); properties = props; } /** * Get object identifier. * @return identifier. */ public String toString() { return id; } /** * Get object identifier. * @return object identifier. */ public String getID() { return id; } /** * Get properties. * @return properties. */ public Map getProperties() { return properties; } } /** * Gets the first recognized compiler from the * compilation targets. * * @param targets compilation targets * @return representative (hopefully) compiler configuration */ private CommandLineCompilerConfiguration getBaseCompilerConfiguration(Map targets) { // // find first target with an GNU C++ compilation // CommandLineCompilerConfiguration compilerConfig; // // get the first target and assume that it is representative // Iterator targetIter = targets.values().iterator(); while (targetIter.hasNext()) { TargetInfo targetInfo = (TargetInfo) targetIter.next(); ProcessorConfiguration config = targetInfo.getConfiguration(); // // for the first cl compiler // if (config instanceof CommandLineCompilerConfiguration) { compilerConfig = (CommandLineCompilerConfiguration) config; if (compilerConfig.getCompiler() instanceof GccCCompiler) { return compilerConfig; } } } return null; } }