/* * * 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.borland; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; 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; import org.apache.tools.ant.BuildException; import org.apache.xml.serialize.OutputFormat; import org.apache.xml.serialize.Serializer; import org.apache.xml.serialize.XMLSerializer; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; /** * Writes a CBuilderX 1.0 project file. * * @author curta * */ public final class CBuilderXProjectWriter implements ProjectWriter { /** * Constructor. */ public CBuilderXProjectWriter() { } /** * Writes a project definition file. * * @param fileName * project name for file, should has .cbx extension * @param task * cc task for which to write project * @param projectDef * project element * @param sources source files * @param targets compilation targets * @param linkTarget link target * @throws IOException if I/O error * @throws SAXException if XML serialization error */ public void writeProject(final File fileName, final CCTask task, final ProjectDef projectDef, final List sources, final Map targets, final TargetInfo linkTarget) throws IOException, SAXException { String projectName = projectDef.getName(); if (projectName == null) { projectName = fileName.getName(); } final String basePath = fileName.getAbsoluteFile().getParent(); File projectFile = new File(fileName + ".cbx"); if (!projectDef.getOverwrite() && projectFile.exists()) { throw new BuildException("Not allowed to overwrite project file " + projectFile.toString()); } CommandLineCompilerConfiguration compilerConfig = getBaseCompilerConfiguration(targets); if (compilerConfig == null) { throw new BuildException( "Unable to generate C++ BuilderX project when gcc or bcc is not used."); } OutputStream outStream = new FileOutputStream(projectFile); OutputFormat format = new OutputFormat("xml", "UTF-8", true); Serializer serializer = new XMLSerializer(outStream, format); ContentHandler content = serializer.asContentHandler(); content.startDocument(); AttributesImpl emptyAttrs = new AttributesImpl(); content.startElement(null, "project", "project", emptyAttrs); PropertyWriter propertyWriter = new PropertyWriter(content); propertyWriter.write("build.config", "active", "0"); propertyWriter.write("build.config", "count", "0"); propertyWriter.write("build.config", "excludedefaultforzero", "0"); propertyWriter.write("build.config.0", "builddir", "Debug"); propertyWriter.write("build.config.0", "key", "Debug_Build"); propertyWriter.write("build.config.0", "linux.builddir", "linux/Debug_Build"); propertyWriter.write("build.config.0", "settings.MinGW", "default;debug"); propertyWriter.write("build.config.0", "settings.gnuc++", "default;debug"); propertyWriter.write("build.config.0", "settings.intellinia32", "default;debug"); propertyWriter.write("build.config.0", "settings.mswin32", "default;debug"); propertyWriter.write("build.config.0", "type", "Toolset"); propertyWriter.write("build.config.0", "win32.builddir", "windows/Debug_Build"); propertyWriter.write("build.node", "name", projectDef.getName()); final String buildType = getBuildType(task); propertyWriter.write("build.node", "type", buildType); propertyWriter.write("build.platform", "active", getActivePlatform(task)); propertyWriter.write("build.platform", "linux.Debug_Build.toolset", "gnuc++"); propertyWriter.write("build.platform", "linux.Release_Build.toolset", "gnuc++"); propertyWriter.write("build.platform", "linux.default", "gnuc++"); propertyWriter.write("build.platform", "linux.gnuc++.enabled", "1"); propertyWriter.write("build.platform", "linux.mswin32.enabled", "1"); propertyWriter.write("build.platform", "linux.win32b.enabled", "1"); propertyWriter.write("build.platform", "solaris.default", "gnuc++"); propertyWriter.write("build.platform", "solaris.enabled", "1"); String toolset = getWin32Toolset(compilerConfig); propertyWriter.write("build.platform", "win32.default", toolset); propertyWriter.write("build.platform", "win32." + toolset + ".enabled", "1"); propertyWriter.write("cbproject", "version", "X.1.0"); if ("dllproject".equals(buildType)) { propertyWriter.write("gnuc++.g++compile", "option.fpic_using_GOT.enabled", "1"); propertyWriter.write("gnuc++.g++link", "option.shared.enabled", "1"); propertyWriter.write("intellinia32.icc", "option.minus_Kpic.enabled", "1"); propertyWriter.write("intellinia32.icclink", "option.minus_shared.enabled", "1"); } // // assume the first target is representative of all compilation tasks // writeCompileOptions(basePath, propertyWriter, compilerConfig); writeLinkOptions(basePath, propertyWriter, linkTarget); propertyWriter.write("linux.gnuc++.Debug_Build", "saved", "1"); if ("dllproject".equals(buildType)) { propertyWriter.write("runtime", "ExcludeDefaultForZero", "1"); //propertyWriter.write("unique", "id", "852"); } else if ("exeproject".equals(buildType)) { propertyWriter.write("runtime.0", "BuildTargetOnRun", "com.borland.cbuilder.build." + "CBProjectBuilder$ProjectBuildAction;make"); propertyWriter.write("runtime.0", "ConfigurationName", projectDef.getName()); propertyWriter.write("runtime.0", "RunnableType", "com.borland.cbuilder.runtime.ExecutableRunner"); } AttributesImpl fileAttributes = new AttributesImpl(); fileAttributes.addAttribute(null, "path", "path", "#PCDATA", ""); AttributesImpl gccAttributes = null; if (!"g++".equals(compilerConfig.getCommand())) { gccAttributes = new AttributesImpl(); gccAttributes.addAttribute(null, "category", "category", "#PCDATA", "build.basecmd"); gccAttributes.addAttribute(null, "name", "name", "#PCDATA", "linux.gnuc++.Debug_Build.g++_key"); gccAttributes.addAttribute(null, "value", "value", "#PCDATA", compilerConfig.getCommand()); } 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++) { String relativePath = CUtil.getRelativePath(basePath, targetsources[i]); fileAttributes.setValue(0, relativePath); content.startElement(null, "file", "file", fileAttributes); // // if file ends with .c, use gcc instead of g++ // if (gccAttributes != null) { content.startElement(null, "property", "property", gccAttributes); content.endElement(null, "property", "property"); } content.endElement(null, "file", "file"); } } content.endElement(null, "project", "project"); content.endDocument(); } /** * Gets build type from link target. * @param task CCTask current task * @return String build type */ private String getBuildType(final CCTask task) { String outType = task.getOuttype(); if ("executable".equals(outType)) { return "exeproject"; } else if ("static".equals(outType)) { return "libraryproject"; } return "dllproject"; } /** * Gets active platform. * @param task CCTask cc task * @return String platform identifier */ private String getActivePlatform(final CCTask task) { String osName = System.getProperty("os.name").toLowerCase(Locale.US); if (osName.indexOf("windows") >= 0) { return "win32"; } return "linux"; } private String getWin32Toolset(final CommandLineCompilerConfiguration compilerConfig) { if (compilerConfig != null && compilerConfig.getCompiler() instanceof BorlandCCompiler) { return "win32b"; } return "MinGW"; } /** * Utility class to generate property elements. */ private static class PropertyWriter { /** * Content handler. */ private ContentHandler content; /** * Attributes list. */ private AttributesImpl propertyAttributes; /** * Constructor. * * @param contentHandler ContentHandler content handler */ public PropertyWriter(final ContentHandler contentHandler) { content = contentHandler; propertyAttributes = new AttributesImpl(); propertyAttributes.addAttribute(null, "category", "category", "#PCDATA", ""); propertyAttributes .addAttribute(null, "name", "name", "#PCDATA", ""); propertyAttributes.addAttribute(null, "value", "value", "#PCDATA", ""); } /** * Write property element. * * @param category String category * @param name String property name * @param value String property value * @throws SAXException if I/O error or illegal content */ public final void write(final String category, final String name, final String value) throws SAXException { propertyAttributes.setValue(0, category); propertyAttributes.setValue(1, name); propertyAttributes.setValue(2, value); content.startElement(null, "property", "property", propertyAttributes); content.endElement(null, "property", "property"); } } /** * Gets the first recognized compiler from the * compilation targets. * @param targets compilation targets * @return representative (hopefully) compiler configuration */ private CommandLineCompilerConfiguration getBaseCompilerConfiguration(final Map targets) { // // find first target with an gcc or bcc 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 gcc or bcc compiler // if (config instanceof CommandLineCompilerConfiguration) { compilerConfig = (CommandLineCompilerConfiguration) config; if (compilerConfig.getCompiler() instanceof GccCCompiler || compilerConfig.getCompiler() instanceof BorlandCCompiler) { return compilerConfig; } } } return null; } /** * Writes elements corresponding to compilation options. * * @param baseDir String base directory * @param writer PropertyWriter property writer * @param compilerConfig representative configuration * @throws SAXException if I/O error or illegal content */ private void writeCompileOptions(final String baseDir, final PropertyWriter writer, final CommandLineCompilerConfiguration compilerConfig) throws SAXException { boolean isBcc = false; boolean isUnix = true; String compileID = "linux.Debug_Build.gnuc++.g++compile"; if (compilerConfig.getCompiler() instanceof BorlandCCompiler) { compileID = "win32.Debug_Build.win32b.bcc32"; isUnix = false; isBcc = true; } File[] includePath = compilerConfig.getIncludePath(); int includeIndex = 1; if (isUnix) { writer.write(compileID, "option.I.arg." + (includeIndex++), "/usr/include"); writer.write(compileID, "option.I.arg." + (includeIndex++), "/usr/include/g++-3"); } for (int i = 0; i < includePath.length; i++) { String relPath = CUtil.getRelativePath(baseDir, includePath[i]); writer.write(compileID, "option.I.arg." + (includeIndex++), relPath); } if (includePath.length > 0) { writer.write(compileID, "option.I.enabled", "1"); } String defineBase = "option.D_MACRO_VALUE"; if (isBcc) { defineBase = "option.D"; } String defineOption = defineBase + ".arg."; int defineIndex = 1; int undefineIndex = 1; String[] preArgs = compilerConfig.getPreArguments(); for (int i = 0; i < preArgs.length; i++) { if (preArgs[i].startsWith("-D")) { writer.write(compileID, defineOption + (defineIndex++), preArgs[i].substring(2)); } else if (preArgs[i].startsWith("-U")) { writer.write(compileID, "option.U.arg." + (undefineIndex++), preArgs[i] .substring(2)); } else if (!(preArgs[i].startsWith("-I") || preArgs[i].startsWith("-o"))) { // // any others (-g, -fno-rtti, -w, -Wall, etc) // writer.write(compileID, "option." + preArgs[i].substring(1) + ".enabled", "1"); } } if (defineIndex > 1) { writer.write(compileID, defineBase + ".enabled", "1"); } if (undefineIndex > 1) { writer.write(compileID, "option.U.enabled", "1"); } } /** * Writes elements corresponding to link options. * * @param baseDir String base directory * @param writer PropertyWriter property writer * @param linkTarget TargetInfo link target * @throws SAXException if I/O error or illegal content */ private void writeLinkOptions(final String baseDir, final PropertyWriter writer, final TargetInfo linkTarget) throws SAXException { if (linkTarget != null) { ProcessorConfiguration config = linkTarget.getConfiguration(); if (config instanceof CommandLineLinkerConfiguration) { CommandLineLinkerConfiguration linkConfig = (CommandLineLinkerConfiguration) config; if (linkConfig.getLinker() instanceof BorlandLinker) { String linkID = "win32.Debug_Build.win32b.ilink32"; writeIlinkArgs(writer, linkID, linkConfig.getPreArguments()); writeIlinkArgs(writer, linkID, linkConfig.getEndArguments()); writer.write(linkID, "param.libfiles.1", "cw32mt.lib"); writer.write(linkID, "param.libfiles.2", "import32.lib"); int libIndex = 3; String[] libNames = linkConfig.getLibraryNames(); for(int i = 0; i < libNames.length; i++) { writer.write(linkID, "param.libfiles." + (libIndex++), libNames[i]); } String startup = linkConfig.getStartupObject(); if (startup != null) { writer.write(linkID, "param.objfiles.1", startup); } } else { String linkID = "linux.Debug_Build.gnuc++.g++link"; writeLdArgs(writer, linkID, linkConfig.getPreArguments()); writeLdArgs(writer, linkID, linkConfig.getEndArguments()); } } } } /** * Writes ld linker options to project file. * * @param writer PropertyWriter property writer * @param linkID String linker identifier * @param preArgs String[] linker arguments * @throws SAXException thrown if unable to write option */ private void writeLdArgs(final PropertyWriter writer, final String linkID, final String[] preArgs) throws SAXException { int objnameIndex = 1; int libnameIndex = 1; int libpathIndex = 1; for (int i = 0; i < preArgs.length; i++) { if (preArgs[i].startsWith("-o")) { writer.write(linkID, "option.o.arg." + (objnameIndex++), preArgs[i] .substring(2)); } else if (preArgs[i].startsWith("-l")) { writer.write(linkID, "option.l.arg." + (libnameIndex++), preArgs[i] .substring(2)); } else if (preArgs[i].startsWith("-L")) { writer.write(linkID, "option.L.arg." + (libpathIndex++), preArgs[i] .substring(2)); } else { // // any others // writer.write(linkID, "option." + preArgs[i].substring(1) + ".enabled", "1"); } } if (objnameIndex > 1) { writer.write(linkID, "option.o.enabled", "1"); } if (libnameIndex > 1) { writer.write(linkID, "option.l.enabled", "1"); } if (libpathIndex > 1) { writer.write(linkID, "option.L.enabled", "1"); } } /** * Writes ilink32 linker options to project file. * * @param writer PropertyWriter property writer * @param linkID String linker identifier * @param preArgs String[] linker arguments * @throws SAXException thrown if unable to write option */ private void writeIlinkArgs(final PropertyWriter writer, final String linkID, final String[] args) throws SAXException { for (int i = 0; i < args.length; i++) { if (args[i].charAt(0) == '/' || args[i].charAt(0) == '-') { int equalsPos = args[i].indexOf('='); if (equalsPos > 0) { String option = "option." + args[i].substring(0, equalsPos -1); writer.write(linkID, option + ".enabled", "1"); writer.write(linkID, option + ".value", args[i].substring(equalsPos + 1)); } else { writer.write(linkID, "option." + args[i].substring(1) + ".enabled", "1"); } } } } }