summaryrefslogtreecommitdiff
path: root/src/main/java/net/sf/antcontrib/cpptasks/apple/XcodeProjectWriter.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/net/sf/antcontrib/cpptasks/apple/XcodeProjectWriter.java')
-rw-r--r--src/main/java/net/sf/antcontrib/cpptasks/apple/XcodeProjectWriter.java1016
1 files changed, 1016 insertions, 0 deletions
diff --git a/src/main/java/net/sf/antcontrib/cpptasks/apple/XcodeProjectWriter.java b/src/main/java/net/sf/antcontrib/cpptasks/apple/XcodeProjectWriter.java
new file mode 100644
index 0000000..f901b4d
--- /dev/null
+++ b/src/main/java/net/sf/antcontrib/cpptasks/apple/XcodeProjectWriter.java
@@ -0,0 +1,1016 @@
+/*
+ *
+ * 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 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;
+
+import javax.xml.transform.TransformerConfigurationException;
+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.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+
+/**
+ * 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 Hashtable 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 Hashtable 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(Hashtable 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;
+ }
+
+}