/* * #%L * Native ARchive plugin for Maven * %% * Copyright (C) 2002 - 2014 NAR Maven Plugin developers. * %% * 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. * #L% */ package com.github.maven_nar.cpptasks.borland; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.List; import java.util.Locale; import java.util.Map; 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; import com.github.maven_nar.cpptasks.CCTask; import com.github.maven_nar.cpptasks.CUtil; import com.github.maven_nar.cpptasks.TargetInfo; import com.github.maven_nar.cpptasks.compiler.CommandLineCompilerConfiguration; import com.github.maven_nar.cpptasks.compiler.CommandLineLinkerConfiguration; import com.github.maven_nar.cpptasks.compiler.ProcessorConfiguration; import com.github.maven_nar.cpptasks.gcc.GccCCompiler; import com.github.maven_nar.cpptasks.ide.ProjectDef; import com.github.maven_nar.cpptasks.ide.ProjectWriter; /** * Writes a CBuilderX 1.0 project file. * * @author curta * */ public final class CBuilderXProjectWriter implements ProjectWriter { /** * Utility class to generate property elements. */ private static class PropertyWriter { /** * Content handler. */ private final ContentHandler content; /** * Attributes list. */ private final AttributesImpl propertyAttributes; /** * Constructor. * * @param contentHandler * ContentHandler content handler */ public PropertyWriter(final ContentHandler contentHandler) { this.content = contentHandler; this.propertyAttributes = new AttributesImpl(); this.propertyAttributes.addAttribute(null, "category", "category", "#PCDATA", ""); this.propertyAttributes.addAttribute(null, "name", "name", "#PCDATA", ""); this.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 { this.propertyAttributes.setValue(0, category); this.propertyAttributes.setValue(1, name); this.propertyAttributes.setValue(2, value); this.content.startElement(null, "property", "property", this.propertyAttributes); this.content.endElement(null, "property", "property"); } } /** * Constructor. */ public CBuilderXProjectWriter() { } /** * Gets active platform. * * @param task * CCTask cc task * @return String platform identifier */ private String getActivePlatform(final CCTask task) { final String osName = System.getProperty("os.name").toLowerCase(Locale.US); if (osName.contains("windows")) { return "win32"; } return "linux"; } /** * Gets the first recognized compiler from the * compilation targets. * * @param targets * compilation targets * @return representative (hopefully) compiler configuration */ private CommandLineCompilerConfiguration getBaseCompilerConfiguration(final Map<String, TargetInfo> targets) { // // find first target with an gcc or bcc compilation // CommandLineCompilerConfiguration compilerConfig = null; // // get the first target and assume that it is representative // for (final TargetInfo targetInfo : targets.values()) { final ProcessorConfiguration config = targetInfo.getConfiguration(); final 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; } /** * Gets build type from link target. * * @param task * CCTask current task * @return String build type */ private String getBuildType(final CCTask task) { final String outType = task.getOuttype(); if ("executable".equals(outType)) { return "exeproject"; } else if ("static".equals(outType)) { return "libraryproject"; } return "dllproject"; } private String getWin32Toolset(final CommandLineCompilerConfiguration compilerConfig) { if (compilerConfig != null && compilerConfig.getCompiler() instanceof BorlandCCompiler) { return "win32b"; } return "MinGW"; } /** * 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; } final 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 (final File element : includePath) { final String relPath = CUtil.getRelativePath(baseDir, element); 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"; } final String defineOption = defineBase + ".arg."; int defineIndex = 1; int undefineIndex = 1; final String[] preArgs = compilerConfig.getPreArguments(); for (final String preArg : preArgs) { if (preArg.startsWith("-D")) { writer.write(compileID, defineOption + defineIndex++, preArg.substring(2)); } else if (preArg.startsWith("-U")) { writer.write(compileID, "option.U.arg." + undefineIndex++, preArg.substring(2)); } else if (!(preArg.startsWith("-I") || preArg.startsWith("-o"))) { // // any others (-g, -fno-rtti, -w, -Wall, etc) // writer.write(compileID, "option." + preArg.substring(1) + ".enabled", "1"); } } if (defineIndex > 1) { writer.write(compileID, defineBase + ".enabled", "1"); } if (undefineIndex > 1) { writer.write(compileID, "option.U.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 (final String arg : args) { if (arg.charAt(0) == '/' || arg.charAt(0) == '-') { final int equalsPos = arg.indexOf('='); if (equalsPos > 0) { final String option = "option." + arg.substring(0, equalsPos - 1); writer.write(linkID, option + ".enabled", "1"); writer.write(linkID, option + ".value", arg.substring(equalsPos + 1)); } else { writer.write(linkID, "option." + arg.substring(1) + ".enabled", "1"); } } } } /** * 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 (final String preArg : preArgs) { if (preArg.startsWith("-o")) { writer.write(linkID, "option.o.arg." + objnameIndex++, preArg.substring(2)); } else if (preArg.startsWith("-l")) { writer.write(linkID, "option.l.arg." + libnameIndex++, preArg.substring(2)); } else if (preArg.startsWith("-L")) { writer.write(linkID, "option.L.arg." + libpathIndex++, preArg.substring(2)); } else { // // any others // writer.write(linkID, "option." + preArg.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 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) { final ProcessorConfiguration config = linkTarget.getConfiguration(); if (config instanceof CommandLineLinkerConfiguration) { final CommandLineLinkerConfiguration linkConfig = (CommandLineLinkerConfiguration) config; if (linkConfig.getLinker() instanceof BorlandLinker) { final 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; final String[] libNames = linkConfig.getLibraryNames(); for (final String libName : libNames) { writer.write(linkID, "param.libfiles." + libIndex++, libName); } final String startup = linkConfig.getStartupObject(); if (startup != null) { writer.write(linkID, "param.objfiles.1", startup); } } else { final String linkID = "linux.Debug_Build.gnuc++.g++link"; writeLdArgs(writer, linkID, linkConfig.getPreArguments()); writeLdArgs(writer, linkID, linkConfig.getEndArguments()); } } } } /** * 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 */ @Override public void writeProject(final File fileName, final CCTask task, final ProjectDef projectDef, final List<File> sources, final Map<String, TargetInfo> targets, final TargetInfo linkTarget) throws IOException, SAXException { String projectName = projectDef.getName(); if (projectName == null) { projectName = fileName.getName(); } final String basePath = fileName.getAbsoluteFile().getParent(); final File projectFile = new File(fileName + ".cbx"); if (!projectDef.getOverwrite() && projectFile.exists()) { throw new BuildException("Not allowed to overwrite project file " + projectFile.toString()); } final CommandLineCompilerConfiguration compilerConfig = getBaseCompilerConfiguration(targets); if (compilerConfig == null) { throw new BuildException("Unable to generate C++ BuilderX project when gcc or bcc is not used."); } final OutputStream outStream = new FileOutputStream(projectFile); final OutputFormat format = new OutputFormat("xml", "UTF-8", true); final Serializer serializer = new XMLSerializer(outStream, format); final ContentHandler content = serializer.asContentHandler(); content.startDocument(); final AttributesImpl emptyAttrs = new AttributesImpl(); content.startElement(null, "project", "project", emptyAttrs); final 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"); final 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"); } final 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()); } for (final TargetInfo info : targets.values()) { final File[] targetsources = info.getSources(); for (final File targetsource : targetsources) { final String relativePath = CUtil.getRelativePath(basePath, targetsource); 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(); } }