diff options
Diffstat (limited to 'src/main/java')
-rw-r--r-- | src/main/java/net/sf/antcontrib/cpptasks/apple/PropertyListSerialization.java | 221 | ||||
-rw-r--r-- | src/main/java/net/sf/antcontrib/cpptasks/apple/XcodeProjectWriter.java | 1163 |
2 files changed, 885 insertions, 499 deletions
diff --git a/src/main/java/net/sf/antcontrib/cpptasks/apple/PropertyListSerialization.java b/src/main/java/net/sf/antcontrib/cpptasks/apple/PropertyListSerialization.java new file mode 100644 index 0000000..20b4c1b --- /dev/null +++ b/src/main/java/net/sf/antcontrib/cpptasks/apple/PropertyListSerialization.java @@ -0,0 +1,221 @@ +/* + * Licensed to the Ant-Contrib Project under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The Ant-Contrib licenses this file to You 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 org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +import javax.xml.transform.Result; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.sax.TransformerHandler; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Static class that provides methods to serialize + * a Map to a Cocoa XML Property List. Does not currently support + * date or data elements. + */ +public final class PropertyListSerialization { + /** + * Private constructor. + */ + private PropertyListSerialization() { + + } + + /** + * Serializes a property list into a Cocoa XML Property List document. + * @param propertyList property list. + * @param result destination. + * @throws SAXException if exception during serialization. + * @throws TransformerConfigurationException if exception creating serializer. + */ + public static void serialize(final Map propertyList, + final Result result) + throws SAXException, TransformerConfigurationException { + SAXTransformerFactory sf = (SAXTransformerFactory) + SAXTransformerFactory.newInstance(); + TransformerHandler handler = sf.newTransformerHandler(); + handler.setResult(result); + + handler.startDocument(); + AttributesImpl attributes = new AttributesImpl(); + handler.startElement(null, "plist", "plist", attributes); + serializeMap(propertyList, handler); + handler.endElement(null, "plist", "plist"); + + handler.endDocument(); + } + + /** + * Serialize a map as a dict element. + * @param map map to serialize. + * @param handler destination of serialization events. + * @throws SAXException if exception during serialization. + */ + private static void serializeMap(final Map map, + final ContentHandler handler) + throws SAXException { + AttributesImpl attributes = new AttributesImpl(); + handler.startElement(null, "dict", "dict", attributes); + + if (map.size() > 0) { + // + // need to output with sorted keys to maintain + // reproducability + // + Object[] keys = map.keySet().toArray(); + Arrays.sort(keys); + for(int i = 0; i < keys.length; i++) { + String key = String.valueOf(keys[i]); + handler.startElement(null, "key", "key", attributes); + handler.characters(key.toCharArray(), 0, key.length()); + handler.endElement(null, "key", "key"); + serializeObject(map.get(keys[i]), handler); + } + } + handler.endElement(null, "dict", "dict"); + } + + /** + * Serialize a list as an array element. + * @param list list to serialize. + * @param handler destination of serialization events. + * @throws SAXException if exception during serialization. + */ + private static void serializeList(final List list, + final ContentHandler handler) + throws SAXException { + AttributesImpl attributes = new AttributesImpl(); + handler.startElement(null, "array", "array", attributes); + for(Iterator iter = list.iterator();iter.hasNext();) { + serializeObject(iter.next(), handler); + } + handler.endElement(null, "array", "array"); + } + + /** + * Creates an element with the specified tag name and character content. + * @param tag tag name. + * @param content character content. + * @param handler destination of serialization events. + * @throws SAXException if exception during serialization. + */ + private static void serializeElement(final String tag, + final String content, + final ContentHandler handler) + throws SAXException { + AttributesImpl attributes = new AttributesImpl(); + handler.startElement(null, tag, tag, attributes); + handler.characters(content.toCharArray(), 0, content.length()); + handler.endElement(null, tag, tag); + } + + + /** + * Serialize a Number as an integer element. + * @param integer number to serialize. + * @param handler destination of serialization events. + * @throws SAXException if exception during serialization. + */ + private static void serializeInteger(final Number integer, + final ContentHandler handler) + throws SAXException { + serializeElement("integer", String.valueOf(integer.longValue()), + handler); + } + + /** + * Serialize a Number as a real element. + * @param real number to serialize. + * @param handler destination of serialization events. + * @throws SAXException if exception during serialization. + */ + private static void serializeReal(final Number real, + final ContentHandler handler) + throws SAXException { + serializeElement("real", + String.valueOf(real.doubleValue()), + handler); + } + + + /** + * Serialize a Boolean as a true or false element. + * @param val boolean to serialize. + * @param handler destination of serialization events. + * @throws SAXException if exception during serialization. + */ + private static void serializeBoolean(final Boolean val, + final ContentHandler handler) + throws SAXException { + String tag = "false"; + if (val.booleanValue()) { + tag = "true"; + } + AttributesImpl attributes = new AttributesImpl(); + handler.startElement(null, tag, tag, attributes); + handler.endElement(null, tag, tag); + } + + /** + * Serialize a string as a string element. + * @param val string to serialize. + * @param handler destination of serialization events. + * @throws SAXException if exception during serialization. + */ + private static void serializeString(final String val, + final ContentHandler handler) + throws SAXException { + serializeElement("string", + val, + handler); + } + + + /** + * Serialize an object using the best available element. + * @param obj object to serialize. + * @param handler destination of serialization events. + * @throws SAXException if exception during serialization. + */ + private static void serializeObject(final Object obj, + final ContentHandler handler) + throws SAXException { + if (obj instanceof Map) { + serializeMap((Map) obj, handler); + } else if (obj instanceof List) { + serializeList((List) obj, handler); + } else if (obj instanceof Number) { + if(obj instanceof Double + ||obj instanceof Float) { + serializeReal((Number) obj, handler); + } else { + serializeInteger((Number) obj, handler); + } + } else if (obj instanceof Boolean) { + serializeBoolean((Boolean) obj, handler); + } else { + serializeString(String.valueOf(obj), handler); + } + } +} diff --git a/src/main/java/net/sf/antcontrib/cpptasks/apple/XcodeProjectWriter.java b/src/main/java/net/sf/antcontrib/cpptasks/apple/XcodeProjectWriter.java index 7f21bec..49c1b53 100644 --- a/src/main/java/net/sf/antcontrib/cpptasks/apple/XcodeProjectWriter.java +++ b/src/main/java/net/sf/antcontrib/cpptasks/apple/XcodeProjectWriter.java @@ -16,559 +16,724 @@ */ package net.sf.antcontrib.cpptasks.apple; -import java.io.BufferedWriter; +import net.sf.antcontrib.cpptasks.CCTask; +import net.sf.antcontrib.cpptasks.TargetInfo; +import net.sf.antcontrib.cpptasks.CUtil; +import net.sf.antcontrib.cpptasks.compiler.CommandLineCompilerConfiguration; +import net.sf.antcontrib.cpptasks.compiler.ProcessorConfiguration; +import net.sf.antcontrib.cpptasks.gcc.GccCCompiler; +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.Result; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.stream.StreamResult; import java.io.File; -import java.io.FileWriter; import java.io.IOException; -import java.io.Writer; +import java.text.NumberFormat; +import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; -import java.util.Vector; import java.util.List; +import java.util.Locale; +import java.util.Map; -import org.apache.tools.ant.BuildException; - -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.ProjectDef; -import net.sf.antcontrib.cpptasks.ide.ProjectWriter; /** - * Writes a Apple Xcode project directory. - * - * Status: Collects file list but does not pick up libraries and settings from - * project. - * - * @author curta + * 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 { - - /** - * Next object identifier for project file. - */ - private int nextID = 0x08FB7796; - - /** - * Constructor. - * - */ - public XcodeProjectWriter() { - } - - /** - * Get next unique ID. - * @return unique ID - */ - private String getUniqueID() { - return Integer.toString(nextID++, 16) + "FE84155DC02AAC07"; - } - - /** - * 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 + ".xcode"); - if (!projectDef.getOverwrite() && xcodeDir.exists()) { - throw new BuildException("Not allowed to overwrite project file " - + xcodeDir.toString()); - } + implements ProjectWriter { - CommandLineCompilerConfiguration compilerConfig = - getBaseCompilerConfiguration(targets); - if (compilerConfig == null) { - throw new BuildException( - "Unable to find compilation target using GNU C++ compiler"); + /** + * Constructor. + */ + public XcodeProjectWriter() { } - String projectName = projectDef.getName(); - if (projectName == null) { - projectName = fileName.getName(); + /** + * 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"); + } + + 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); + objects.put(mainGroup.getID(), mainGroup.getProperties()); + + // + // add project configurations + // + PBXObjectRef projectConfigurations = addProjectConfigurationList(objects); + + String projectDirPath = ""; + List projectTargets = new ArrayList(); + + // + // add description of native target (that is the executable or + // shared library) + // + PBXObjectRef nativeTarget = + addNativeTarget(objects, linkTarget, product, sourceGroupChildren); + projectTargets.add(nativeTarget); + + + // + // add project to property list + // + PBXObjectRef project = createPBXProject(projectConfigurations, mainGroup, + projectDirPath, projectTargets); + objects.put(project.getID(), project.getProperties()); + + + // + // finish up overall property list + // + propertyList.put("objects", objects); + propertyList.put("rootObject", project.getID()); + + + // + // write property list out to XML file + // + Result result = new StreamResult(xcodeProj); + + try { + PropertyListSerialization.serialize(propertyList, result); + } 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()); + } } - final String basePath = fileName.getAbsoluteFile().getParent(); - - xcodeDir.mkdir(); - - File xcodeProj = new File(xcodeDir, "project.pbxproj"); - - // - // assume that all C++ compiles can use the - // same settings - // - CommandLineCompilerConfiguration gccConfig = null; - - Vector sourceList = new Vector(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.addElement(new SourceEntry(targetsources[i], - getUniqueID(), getUniqueID())); - } - ProcessorConfiguration procConfig = info.getConfiguration(); - if (procConfig instanceof CommandLineCompilerConfiguration - && gccConfig == null) { - gccConfig = (CommandLineCompilerConfiguration) procConfig; - } + + + /** + * 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; } - SourceEntry[] sortedSources = new SourceEntry[sourceList.size()]; - sourceList.copyInto(sortedSources); - Arrays.sort(sortedSources, new Comparator() { - public int compare(final Object o1, final Object o2) { - return ( (SourceEntry) o1).getFile().getName().compareTo( ( ( - SourceEntry) o2).getFile().getName()); - } - }); - File outFile = task.getOutfile(); - - Writer writer = new BufferedWriter(new FileWriter(xcodeProj)); - - writer.write("// !$*UTF8*$!\n"); - writer.write(" {\n"); - writer.write(" archiveVersion = 1;\n"); - writer.write(" classes = {\n"); - writer.write(" };\n"); - writer.write(" objectVersion = 39;\n"); - writer.write(" objects = {\n"); - writer.write(" 014CEA520018CE5811CA2923 = {\n"); - writer.write(" buildRules = (\n"); - writer.write(" );\n"); - writer.write(" buildSettings = {\n"); - writer.write(" COPY_PHASE_STRIP = NO;\n"); - writer.write(" DEBUGGING_SYMBOLS = YES;\n"); - writer.write(" GCC_DYNAMIC_NO_PIC = NO;\n"); - writer.write(" GCC_ENABLE_FIX_AND_CONTINUE = YES;\n"); - writer.write(" GCC_GENERATE_DEBUGGING_SYMBOLS = YES;\n"); - writer.write(" GCC_OPTIMIZATION_LEVEL = 0;\n"); - writer.write(" OPTIMIZATION_CFLAGS = \"-O0\";\n"); - writer.write(" ZERO_LINK = YES;\n"); - writer.write(" };\n"); - writer.write(" isa = PBXBuildStyle;\n"); - writer.write(" name = Development;\n"); - writer.write(" };\n"); - writer.write(" 014CEA530018CE5811CA2923 = {\n"); - writer.write(" buildRules = (\n"); - writer.write(" );\n"); - writer.write(" buildSettings = {\n"); - writer.write(" COPY_PHASE_STRIP = YES;\n"); - writer.write(" GCC_ENABLE_FIX_AND_CONTINUE = NO;\n"); - writer.write(" ZERO_LINK = NO;\n"); - writer.write(" };\n"); - writer.write(" isa = PBXBuildStyle;\n"); - writer.write(" name = Deployment;\n"); - writer.write(" };\n"); - writer.write("// 010\n"); - writer.write("// 011\n"); - writer.write("// 012\n"); - writer.write("// 013\n"); - writer.write("// 014\n"); - writer.write("// 080\n"); - writer.write("// 081\n"); - writer.write("// 082\n"); - writer.write("// 083\n"); - writer.write("// 084\n"); - writer.write(" 08FB7793FE84155DC02AAC07 = {\n"); - writer.write(" buildSettings = {\n"); - writer.write(" };\n"); - writer.write(" buildStyles = (\n"); - writer.write(" 014CEA520018CE5811CA2923,\n"); - writer.write(" 014CEA530018CE5811CA2923,\n"); - writer.write(" );\n"); - writer.write(" hasScannedForEncodings = 1;\n"); - writer.write(" isa = PBXProject;\n"); - writer.write(" mainGroup = 08FB7794FE84155DC02AAC07;\n"); - writer.write(" projectDirPath = \"\";\n"); - writer.write(" targets = (\n"); - writer.write(" D2AAC0620554660B00DB518D,\n"); - writer.write(" );\n"); - writer.write(" };\n"); - writer.write(" 08FB7794FE84155DC02AAC07 = {\n"); - writer.write(" children = (\n"); - writer.write(" 08FB7795FE84155DC02AAC07,\n"); - writer.write(" 1AB674ADFE9D54B511CA2CBB,\n"); - writer.write(" );\n"); - writer.write(" isa = PBXGroup;\n"); - writer.write(" name = "); - writer.write(outFile.getName()); - writer.write(";\n"); - writer.write(" refType = 4;\n"); - writer.write(" sourceTree = \"<group>\";\n"); - writer.write(" };\n"); - writer.write(" 08FB7795FE84155DC02AAC07 = {\n"); - writer.write(" children = (\n"); - - // - // source ID's go here - // - for (int i = 0; i < sortedSources.length; i++) { - writer.write(" "); - writer.write(sortedSources[i].getSourceID()); - writer.write(",\n"); + + /** + * 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(); + executableProperties.put("explicitFileType", "compiled.mach-o.executable"); + executableProperties.put("includeInIndex", "0"); + objects.put(executable.getID(), executableProperties); + + return executable; } - writer.write(" );\n"); - writer.write(" isa = PBXGroup;\n"); - writer.write(" name = Source;\n"); - writer.write(" refType = 4;\n"); - writer.write(" sourceTree = \"<group>\";\n"); - writer.write(" };\n"); - - for (int i = 0; i < sortedSources.length; i++) { - // - // source definition - // - SourceEntry entry = sortedSources[i]; - writer.write(" "); - writer.write(entry.getSourceID()); - writer.write(" = {\n"); - writer.write(" fileEncoding = 4;\n"); - writer.write(" isa = PBXFileReference;\n"); - String sourceName = entry.getFile().getName(); - if (sourceName.endsWith(".c")) { - writer.write(" lastKnownFileType = sourcecode.c.c;\n"); - } else { - writer.write(" lastKnownFileType = sourcecode.cpp.cpp;\n"); - } - - String relativePath = CUtil.getRelativePath(basePath, entry.getFile()); - if (!relativePath.equals(sourceName)) { - writer.write(" name = "); - writer.write(sourceName); - writer.write(";\n"); - } - writer.write(" path = "); - writer.write(relativePath); - writer.write(";\n"); - writer.write(" refType = 4;\n"); - writer.write(" sourceTree = \"<group>\";\n"); - writer.write(" };\n"); - - // - // build definition - // - writer.write(" "); - writer.write(entry.getBuildID()); - writer.write(" = {\n"); - writer.write(" fileRef = "); - writer.write(entry.getSourceID()); - writer.write(";\n"); - writer.write(" isa = PBXBuildFile;\n"); - writer.write(" settings = {\n"); - writer.write(" };\n"); - writer.write(" };\n"); + /** + * 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; } - writer.write("// 080\n"); - writer.write("// 081\n"); - writer.write("// 082\n"); - writer.write("// 083\n"); - writer.write("// 084\n"); - writer.write("// 1A0\n"); - writer.write("// 1A1\n"); - writer.write("// 1A2\n"); - writer.write("// 1A3\n"); - writer.write("// 1A4\n"); - writer.write(" 1AB674ADFE9D54B511CA2CBB = {\n"); - writer.write(" children = (\n"); - writer.write(" D2AAC0630554660B00DB518D,\n"); - writer.write(" );\n"); - writer.write(" isa = PBXGroup;\n"); - writer.write(" name = Products;\n"); - writer.write(" refType = 4;\n"); - writer.write(" sourceTree = \"<group>\";\n"); - writer.write(" };\n"); - writer.write("// 1A0\n"); - writer.write("// 1A1\n"); - writer.write("// 1A2\n"); - writer.write("// 1A3\n"); - writer.write("// 1A4\n"); - writer.write("// D20\n"); - writer.write("// D21\n"); - writer.write("// D22\n"); - writer.write("// D23\n"); - writer.write("// D24\n"); - writer.write(" D2AAC0600554660B00DB518D = {\n"); - writer.write(" buildActionMask = 2147483647;\n"); - writer.write(" files = (\n"); - writer.write(" );\n"); - writer.write(" isa = PBXHeadersBuildPhase;\n"); - writer.write(" runOnlyForDeploymentPostprocessing = 0;\n"); - writer.write(" };\n"); - writer.write(" D2AAC0610554660B00DB518D = {\n"); - writer.write(" buildActionMask = 2147483647;\n"); - writer.write(" files = (\n"); - - // - // build ID's - // - for (int i = 0; i < sortedSources.length; i++) { - writer.write(" "); - writer.write(sortedSources[i].getBuildID()); - writer.write(",\n"); + + /** + * Add project configuration list. + * @param objects map of objects. + * @return project configuration object. + */ + private PBXObjectRef addProjectConfigurationList(final Map objects) { + // + // 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", "Release"); + objects.put(configurationList.getID(), configurationList.getProperties()); + return configurationList; } - writer.write(" );\n"); - writer.write(" isa = PBXSourcesBuildPhase;\n"); - writer.write(" runOnlyForDeploymentPostprocessing = 0;\n"); - writer.write(" };\n"); - writer.write(" D2AAC0620554660B00DB518D = {\n"); - writer.write(" buildPhases = (\n"); - writer.write(" D2AAC0600554660B00DB518D,\n"); - writer.write(" D2AAC0610554660B00DB518D,\n"); - writer.write(" );\n"); - writer.write(" buildRules = (\n"); - writer.write(" );\n"); - writer.write(" buildSettings = {\n"); - writer.write(" DYLIB_COMPATIBILITY_VERSION = 1;\n"); - writer.write(" DYLIB_CURRENT_VERSION = 1;\n"); - // - // write preprocessor macros - // - if (gccConfig != null) { - String[] options = gccConfig.getPreArguments(); - boolean hasD = false; - for (int i = 0; i < options.length; i++) { - if (options[i].startsWith("-D")) { - if (!hasD) { - writer.write(" GCC_PREPROCESSOR_DEFINITIONS = \""); - hasD = true; - } else { - writer.write(" "); - } - writer.write(options[i].substring(2)); + + /** + * Add native target to map of objects. + * @param objects map of objects. + * @param linkTarget description of executable or shared library. + * @param product product. + * @param sourceGroupChildren source files needed to build product. + * @return native target. + */ + private PBXObjectRef addNativeTarget(final Map objects, + final TargetInfo linkTarget, + final PBXObjectRef product, + final List sourceGroupChildren) { + + PBXObjectRef buildConfigurations = this.addNativeTargetConfigurationList(objects, + linkTarget.getOutput().getName()); + + 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()); } - } - if (hasD) { - writer.write("\";\n"); - } + + + PBXObjectRef sourcesBuildPhase = createPBXSourcesBuildPhase(buildActionMask, + buildFiles, false); + objects.put(sourcesBuildPhase.getID(), sourcesBuildPhase.getProperties()); + buildPhases.add(sourcesBuildPhase); + + + PBXObjectRef frameworksBuildPhase = + createPBXFrameworksBuildPhase(buildActionMask, + new ArrayList(), 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 = "com.apple.product-type.tool"; + + PBXObjectRef nativeTarget = createPBXNativeTarget("hello", + buildConfigurations, buildPhases, buildRules, dependencies, + productInstallPath, "hello", product, productType); + objects.put(nativeTarget.getID(), nativeTarget.getProperties()); + + return nativeTarget; } - writer.write(" GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO;\n"); - writer.write(" GCC_WARN_UNKNOWN_PRAGMAS = NO;\n"); - if (gccConfig != null) { - File[] includes = gccConfig.getIncludePath(); - if (includes.length > 0) { - writer.write(" HEADER_SEARCH_PATHS = \""); - for (int i = 0; i < includes.length; i++) { - if (i > 0) { - writer.write(" "); - } - writer.write(CUtil.getRelativePath(basePath, includes[i])); + + + /** + * 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.getRelativePath(baseDir, file); + String name = file.getName(); + + if (!name.equals(relPath)) { + map.put("name", name); } - writer.write("\";\n"); - } + map.put("path", relPath); + map.put("sourceTree", sourceTree); + return new PBXObjectRef(map); } - - - String[] linkerArgs = null; - ProcessorConfiguration linkConfig = linkTarget.getConfiguration(); - if (linkConfig instanceof CommandLineLinkerConfiguration) { - linkerArgs = ((CommandLineLinkerConfiguration) linkConfig).getPreArguments(); + + /** + * 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); } - - writer.write(" INSTALL_PATH = /usr/local/lib;\n"); - if (linkerArgs != null) { - boolean hasLibPath = false; - for (int i = 0; i < linkerArgs.length; i++) { - if (linkerArgs[i].startsWith("-L")) { - if (!hasLibPath) { - writer.write(" LIBRARY_SEARCH_PATHS = \""); - hasLibPath = true; - } else { - writer.write(" "); - } - writer.write(linkerArgs[i].substring(2)); - } - } - if (hasLibPath) { - writer.write("\";\n"); - } - } - writer.write(" LIBRARY_STYLE = DYNAMIC;\n"); - writer.write(" OTHER_CFLAGS = \"\";\n"); - writer.write(" OTHER_LDFLAGS = \""); - if (linkerArgs != null) { - String prepend = ""; - for (int i = 0; i < linkerArgs.length; i++) { - if (!linkerArgs[i].startsWith("-L")) { - writer.write(prepend); - writer.write(linkerArgs[i]); - prepend = " "; - } - } + /** + * Create PBXProject. + * @param buildConfigurationList build configuration list. + * @param mainGroup main group. + * @param projectDirPath project directory path. + * @param targets targets. + * @return project. + */ + private static PBXObjectRef createPBXProject(final PBXObjectRef buildConfigurationList, + final PBXObjectRef mainGroup, + final String projectDirPath, + 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); + 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); } - writer.write("\";\n"); - writer.write(" OTHER_REZFLAGS = \"\";\n"); - writer.write(" PRODUCT_NAME = testbsd;\n"); - writer.write(" SECTORDER_FLAGS = \"\";\n"); - writer.write(" WARNING_CFLAGS = \"-Wmost\";\n"); - writer.write(" };\n"); - writer.write(" dependencies = (\n"); - writer.write(" );\n"); - writer.write(" isa = PBXNativeTarget;\n"); - - writer.write(" name = "); - writer.write(outFile.getName()); - writer.write(";\n"); - writer.write(" productName = "); - writer.write(outFile.getName()); - writer.write(";\n"); - writer.write(" productReference = D2AAC0630554660B00DB518D;\n"); - - String productType = "com.apple.product-type.library.dynamic"; - String prefix = "lib"; - String suffix = ".dylib"; - String explicitFileType = "compiled.mach-o.dylib"; - String outType = task.getOuttype(); - if ("executable".equals(outType)) { - productType = "com.apple.product-type.tool"; - prefix = ""; - suffix = ""; - explicitFileType = "compiled.mach-o.executable"; - } else if ("static".equals(outType)) { - productType = "com.apple.product-type.library.static"; - suffix = ".a"; - explicitFileType = "archive.ar"; + + + /** + * 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); } - writer.write(" productType = \""); - writer.write(productType); - writer.write("\";\n"); - writer.write(" };\n"); - writer.write(" D2AAC0630554660B00DB518D = {\n"); - - writer.write(" explicitFileType = \""); - writer.write(explicitFileType); - writer.write("\";\n"); - writer.write(" includeInIndex = 0;\n"); - writer.write(" isa = PBXFileReference;\n"); - writer.write(" path = "); - writer.write(outFile.getName()); - writer.write(suffix); - writer.write(";\n"); - writer.write(" refType = 3;\n"); - writer.write(" sourceTree = BUILT_PRODUCTS_DIR;\n"); - writer.write(" };\n"); - writer.write(" };\n"); - writer.write(" rootObject = 08FB7793FE84155DC02AAC07;\n"); - writer.write(" }\n"); - - writer.close(); - } - - /** - * 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 = null; - // - // 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(); - String identifier = config.getIdentifier(); - // - // for the first cl compiler - // - if (config instanceof CommandLineCompilerConfiguration) { - compilerConfig = (CommandLineCompilerConfiguration) config; - if (compilerConfig.getCompiler() instanceof GccCCompiler) { - return compilerConfig; - } - } + + /** + * 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); } - return null; - } - /** - * Source file with 96-bit source and build ID's. - */ - private static final class SourceEntry { /** - * Source file. + * 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 final File file; + 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); + } /** - * Source ID. + * Create PBXBuildFile. + * @param fileRef source file. + * @param settings build settings. + * @return PBXBuildFile. */ - private final String sourceID; + 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); + } /** - * Build step ID. + * Create PBXFrameworksBuildPhase. + * @param buildActionMask build action mask. + * @param files files. + * @param runOnly if true, phase should only be run on deployment. + * @return PBXFrameworkBuildPhase. */ - private final String buildID; + 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); + } /** - * Constructor. - * @param fileArg source file - * @param sourceIDArg source ID - * @param buildIDArg build step ID + * 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. */ - public SourceEntry(final File fileArg, - final String sourceIDArg, - final String buildIDArg) { - file = fileArg; - sourceID = sourceIDArg; - buildID = buildIDArg; + 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); } /** - * Get source file. - * @return source file + * Method returns "1" for true, "0" for false. + * @param b boolean value. + * @return "1" for true, "0" for false. */ - public File getFile() { - return file; + private static String toString(boolean b) { + if (b) { + return "1"; + } else { + return "0"; + } } /** - * Get source ID. - * @return source ID + * 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. */ - public String getSourceID() { - return sourceID; + 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; + } } /** - * Get build step ID. - * @return build step ID + * Gets the first recognized compiler from the + * compilation targets. + * + * @param targets compilation targets + * @return representative (hopefully) compiler configuration */ - public String getBuildID() { - return buildID; + 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; } - } + } |