From 683d490074d93baa8a47db7ec43b7bd31145a12a Mon Sep 17 00:00:00 2001 From: Mark Donszelmann Date: Thu, 5 Nov 2009 23:00:15 +0100 Subject: Tar file expanded and committed --- .../cpptasks/devstudio/DevStudioProjectWriter.java | 710 +++++++++++++++++++++ 1 file changed, 710 insertions(+) create mode 100644 src/main/java/net/sf/antcontrib/cpptasks/devstudio/DevStudioProjectWriter.java (limited to 'src/main/java/net/sf/antcontrib/cpptasks/devstudio/DevStudioProjectWriter.java') diff --git a/src/main/java/net/sf/antcontrib/cpptasks/devstudio/DevStudioProjectWriter.java b/src/main/java/net/sf/antcontrib/cpptasks/devstudio/DevStudioProjectWriter.java new file mode 100644 index 0000000..51ab1dd --- /dev/null +++ b/src/main/java/net/sf/antcontrib/cpptasks/devstudio/DevStudioProjectWriter.java @@ -0,0 +1,710 @@ +/* + * + * Copyright 2004-2008 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.devstudio; + +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.ide.DependencyDef; +import net.sf.antcontrib.cpptasks.ide.ProjectDef; +import net.sf.antcontrib.cpptasks.ide.ProjectWriter; +import net.sf.antcontrib.cpptasks.ide.CommentDef; +import org.apache.tools.ant.BuildException; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.*; +import java.text.MessageFormat; + +/** + * Writes a Microsoft Visual Studio 97 or Visual Studio 6 project file. + * + * Status: Collects file list but does not pick + * up libraries and settings from project. + * + * @author curta + */ +public final class DevStudioProjectWriter + implements ProjectWriter { + /** + * Visual Studio version. + */ + private String version; + + /** + * Constructor. + * @param versionArg String Visual Studio version. + */ + public DevStudioProjectWriter(final String versionArg) { + this.version = versionArg; + } + + private static String toProjectName(final String name) { + // + // some characters are apparently not allowed in VS project names + // but have not been able to find them documented + // limiting characters to alphas, numerics and hyphens + StringBuffer projectNameBuf = new StringBuffer(name); + for (int i = 0; i < projectNameBuf.length(); i++) { + final char ch = projectNameBuf.charAt(i); + if (!((ch >= 'a' && ch <= 'z') + || (ch >= 'A' && ch <= 'Z') + || (ch >= '0' && ch <= '9'))) { + projectNameBuf.setCharAt(i, '_'); + } + } + return projectNameBuf.toString(); + + } + + /** + * 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 files source files + * @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 files, + final Hashtable targets, + final TargetInfo linkTarget) throws IOException { + + // + // some characters are apparently not allowed in VS project names + // but have not been able to find them documented + // limiting characters to alphas, numerics and hyphens + String projectName = projectDef.getName(); + if (projectName != null) { + projectName = toProjectName(projectName); + } else { + projectName = toProjectName(fileName.getName()); + } + + final String basePath = fileName.getAbsoluteFile().getParent(); + + File dspFile = new File(fileName + ".dsp"); + if (!projectDef.getOverwrite() && dspFile.exists()) { + throw new BuildException("Not allowed to overwrite project file " + + dspFile.toString()); + } + File dswFile = new File(fileName + ".dsw"); + if (!projectDef.getOverwrite() && dswFile.exists()) { + throw new BuildException("Not allowed to overwrite project file " + + dswFile.toString()); + } + + CommandLineCompilerConfiguration compilerConfig = + getBaseCompilerConfiguration(targets); + if (compilerConfig == null) { + throw new BuildException( + "Unable to generate Visual Studio project " + + "when Microsoft C++ is not used."); + } + + Writer writer = new BufferedWriter(new FileWriter(dspFile)); + writer.write("# Microsoft Developer Studio Project File - Name=\""); + writer.write(projectName); + writer.write("\" - Package Owner=<4>\r\n"); + writer.write( + "# Microsoft Developer Studio Generated Build File, Format Version "); + writer.write(this.version); + writer.write("\r\n"); + writer.write("# ** DO NOT EDIT **\r\n\r\n"); + + writeComments(writer, projectDef.getComments()); + + String outputType = task.getOuttype(); + String subsystem = task.getSubsystem(); + String targtype = "Win32 (x86) Dynamic-Link Library"; + String targid = "0x0102"; + if ("executable".equals(outputType)) { + if ("console".equals(subsystem)) { + targtype = "Win32 (x86) Console Application"; + targid = "0x0103"; + } else { + targtype = "Win32 (x86) Application"; + targid = "0x0101"; + } + } else if ("static".equals(outputType)) { + targtype = "Win32 (x86) Static Library"; + targid = "0x0104"; + } + writer.write("# TARGTYPE \""); + writer.write(targtype); + writer.write("\" "); + writer.write(targid); + writer.write("\r\n\r\nCFG="); + + writer.write(projectName + " - Win32 Debug"); + writer.write("\r\n"); + + writeMessage(writer, projectName, targtype); + + writer.write("# Begin Project\r\n"); + if (version.equals("6.00")) { + writer.write("# PROP AllowPerConfigDependencies 0\r\n"); + } + writer.write("# PROP Scc_ProjName \"\"\r\n"); + writer.write("# PROP Scc_LocalPath \"\"\r\n"); + writer.write("CPP=cl.exe\r\n"); + writer.write("MTL=midl.exe\r\n"); + writer.write("RSC=rc.exe\r\n"); + + writer.write("\r\n!IF \"$(CFG)\" == \"" + projectName + " - Win32 Release\"\r\n"); + + writeConfig(writer, false, projectDef.getDependencies(), basePath, compilerConfig, linkTarget, targets); + + writer.write("\r\n!ELSEIF \"$(CFG)\" == \"" + projectName + " - Win32 Debug\"\r\n"); + + writeConfig(writer, true, projectDef.getDependencies(), basePath, compilerConfig, linkTarget, targets); + + writer.write("\r\n!ENDIF\r\n"); + + writer.write("# Begin Target\r\n\r\n"); + writer.write("# Name \"" + projectName + " - Win32 Release\"\r\n"); + writer.write("# Name \"" + projectName + " - Win32 Debug\"\r\n"); + + + + File[] sortedSources = getSources(files); + + if (version.equals("6.00")) { + final String sourceFilter = "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"; + final String headerFilter = "h;hpp;hxx;hm;inl"; + final String resourceFilter = + "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"; + + writer.write("# Begin Group \"Source Files\"\r\n\r\n"); + writer.write("# PROP Default_Filter \"" + sourceFilter + "\"\r\n"); + + for (int i = 0; i < sortedSources.length; i++) { + if (!isGroupMember(headerFilter, sortedSources[i]) + && !isGroupMember(resourceFilter, sortedSources[i])) { + writeSource(writer, basePath, sortedSources[i]); + } + } + writer.write("# End Group\r\n"); + + writer.write("# Begin Group \"Header Files\"\r\n\r\n"); + writer.write("# PROP Default_Filter \"" + headerFilter + "\"\r\n"); + + for (int i = 0; i < sortedSources.length; i++) { + if (isGroupMember(headerFilter, sortedSources[i])) { + writeSource(writer, basePath, sortedSources[i]); + } + } + writer.write("# End Group\r\n"); + + writer.write("# Begin Group \"Resource Files\"\r\n\r\n"); + writer.write("# PROP Default_Filter \"" + resourceFilter + "\"\r\n"); + + for (int i = 0; i < sortedSources.length; i++) { + if (isGroupMember(resourceFilter, sortedSources[i])) { + writeSource(writer, basePath, sortedSources[i]); + } + } + writer.write("# End Group\r\n"); + + } else { + for (int i = 0; i < sortedSources.length; i++) { + writeSource(writer, basePath, sortedSources[i]); + } + } + + writer.write("# End Target\r\n"); + writer.write("# End Project\r\n"); + writer.close(); + + // + // write workspace file + // + writer = new BufferedWriter(new FileWriter(dswFile)); + writeWorkspace(writer, projectDef, projectName, dspFile); + writer.close(); + + } + + private void writeConfig(final Writer writer, + boolean isDebug, + final List dependencies, + final String basePath, + CommandLineCompilerConfiguration compilerConfig, + TargetInfo linkTarget, + Hashtable targets) throws IOException { + writer.write("# PROP BASE Use_MFC 0\r\n"); + + String configType = "Release"; + String configInt = "0"; + String configMacro = "NDEBUG"; + if (isDebug) { + configType = "Debug"; + configInt = "1"; + configMacro = "_DEBUG"; + } + + writer.write("# PROP BASE Use_Debug_Libraries "); + writer.write(configInt); + writer.write("\r\n# PROP BASE Output_Dir \""); + writer.write(configType); + writer.write("\"\r\n"); + writer.write("# PROP BASE Intermediate_Dir \""); + writer.write(configType); + writer.write("\"\r\n"); + writer.write("# PROP BASE Target_Dir \"\"\r\n"); + writer.write("# PROP Use_MFC 0\r\n"); + writer.write("# PROP Use_Debug_Libraries "); + writer.write(configInt); + writer.write("\r\n# PROP Output_Dir \""); + writer.write(configType); + writer.write("\"\r\n"); + writer.write("# PROP Intermediate_Dir \""); + writer.write(configType); + writer.write("\"\r\n"); + writer.write("# PROP Target_Dir \"\"\r\n"); + writeCompileOptions(writer, isDebug, basePath, compilerConfig); + writer.write( + "# ADD BASE MTL /nologo /D \"" + configMacro + "\" /mktyplib203 /o NUL /win32\r\n"); + writer.write( + "# ADD MTL /nologo /D \"" + configMacro + "\" /mktyplib203 /o NUL /win32\r\n"); + writer.write("# ADD BASE RSC /l 0x409 /d \"" + configMacro + "\"\r\n"); + writer.write("# ADD RSC /l 0x409 /d \"" + configMacro + "\"\r\n"); + writer.write("BSC32=bscmake.exe\r\n"); + writer.write("# ADD BASE BSC32 /nologo\r\n"); + writer.write("# ADD BSC32 /nologo\r\n"); + writer.write("LINK32=link.exe\r\n"); + writeLinkOptions(writer, isDebug, dependencies, basePath, linkTarget, targets); + } + private static void writeWorkspaceProject(final Writer writer, + final String projectName, + final String projectFile, + final List dependsOn) throws IOException { + writer.write("############################################"); + writer.write("###################################\r\n\r\n"); + String file = projectFile; + if(!file.startsWith(".") && !file.startsWith("\\") && !file.startsWith("/")) { + file = ".\\" + file; + } + writer.write("Project: \"" + projectName + "\"=\"" + + file + + "\" - Package Owner=<4>\r\n\r\n"); + + writer.write("Package=<5>\r\n{{{\r\n}}}\r\n\r\n"); + writer.write("Package=<4>\r\n{{{\r\n"); + if (dependsOn != null) { + for(Iterator iter = dependsOn.iterator(); iter.hasNext();) { + writer.write(" Begin Project Dependency\r\n"); + writer.write(" Project_Dep_Name " + toProjectName(String.valueOf(iter.next())) + "\r\n"); + writer.write(" End Project Dependency\r\n"); + } + } + writer.write("}}}\r\n\r\n"); + + } + + private void writeWorkspace(final Writer writer, + final ProjectDef project, + final String projectName, + final File dspFile) throws IOException { + + writer.write("Microsoft Developer Studio Workspace File, Format Version "); + writer.write(version); + writer.write("\r\n"); + writer.write("# WARNING: DO NOT EDIT OR DELETE"); + writer.write(" THIS WORKSPACE FILE!\r\n\r\n"); + + writeComments(writer, project.getComments()); + + + List dependencies = project.getDependencies(); + List projectDeps = new ArrayList(); + String basePath = dspFile.getParent(); + for(Iterator iter = dependencies.iterator(); iter.hasNext();) { + DependencyDef dep = (DependencyDef) iter.next(); + if (dep.getFile() != null) { + String projName = toProjectName(dep.getName()); + projectDeps.add(projName); + String depProject = CUtil.toWindowsPath( + CUtil.getRelativePath(basePath, + new File(dep.getFile() + ".dsp"))); + writeWorkspaceProject(writer, projName, depProject, dep.getDependsList()); + } + } + + writeWorkspaceProject(writer, projectName, dspFile.getName(), projectDeps); + + writer.write("############################################"); + writer.write("###################################\r\n\r\n"); + + + writer.write("Global:\r\n\r\nPackage=<5>\r\n{{{\r\n}}}"); + writer.write("\r\n\r\nPackage=<3>\r\n{{{\r\n}}}\r\n\r\n"); + + writer.write("########################################"); + writer.write("#######################################\r\n\r\n"); + + } + + /** + * Returns true if the file has an extension that appears in the group filter. + * @param filter String group filter + * @param candidate File file + * @return boolean true if member of group + */ + private boolean isGroupMember(final String filter, final File candidate) { + String fileName = candidate.getName(); + int lastDot = fileName.lastIndexOf('.'); + if (lastDot >= 0 && lastDot < fileName.length() - 1) { + String extension = + ";" + fileName.substring(lastDot + 1).toLowerCase() + ";"; + String semiFilter = ";" + filter + ";"; + return semiFilter.indexOf(extension) >= 0; + } + return false; + } + + /** + * Writes the entry for one source file in the project. + * @param writer Writer writer + * @param basePath String base path for project + * @param groupMember File project source file + * @throws IOException if error writing project file + */ + private void writeSource(final Writer writer, + final String basePath, + final File groupMember) + throws IOException { + writer.write("# Begin Source File\r\n\r\nSOURCE="); + String relativePath = CUtil.getRelativePath(basePath, + groupMember); + // + // if relative path is just a name (hello.c) then + // make it .\hello.c + if (!relativePath.startsWith(".") + && relativePath.indexOf(":") < 0 + && !relativePath.startsWith("\\")) { + relativePath = ".\\" + relativePath; + } + writer.write(CUtil.toWindowsPath(relativePath)); + writer.write("\r\n# End Source File\r\n"); + } + + /** + * Get alphabetized array of source files. + * @param sourceList list of source files + * @return File[] source files + */ + private File[] getSources(final List sourceList) { + File[] sortedSources = new File[sourceList.size()]; + sourceList.toArray(sortedSources); + Arrays.sort(sortedSources, new Comparator() { + public int compare(final Object o1, final Object o2) { + return ((File) o1).getName().compareTo(((File) o2).getName()); + } + }); + return sortedSources; + } + + /** + * Writes "This is not a makefile" warning. + * @param writer Writer writer + * @param projectName String project name + * @param targtype String target type + * @throws IOException if error writing project + */ + + private void writeMessage(final Writer writer, + final String projectName, + final String targtype) throws IOException { + writer.write( + "!MESSAGE This is not a valid makefile. "); + writer.write("To build this project using NMAKE,\r\n"); + writer.write("!MESSAGE use the Export Makefile command and run\r\n"); + writer.write("!MESSAGE \r\n"); + writer.write("!MESSAGE NMAKE /f \""); + writer.write(projectName); + writer.write(".mak\".\r\n"); + writer.write("!MESSAGE \r\n"); + writer.write( + "!MESSAGE You can specify a configuration when running NMAKE\r\n"); + writer.write( + "!MESSAGE by defining the macro CFG on the command line. "); + writer.write("For example:\r\n"); + writer.write("!MESSAGE \r\n"); + writer.write("!MESSAGE NMAKE /f \""); + writer.write(projectName); + writer.write(".mak\" CFG=\""); + writer.write(projectName); + writer.write(" - Win32 Debug\"\r\n"); + writer.write("!MESSAGE \r\n"); + writer.write("!MESSAGE Possible choices for configuration are:\r\n"); + writer.write("!MESSAGE \r\n"); + String pattern = "!MESSAGE \"{0} - Win32 {1}\" (based on \"{2}\")\r\n"; + writer.write(MessageFormat.format(pattern, new Object[] { projectName, "Release", targtype })); + writer.write(MessageFormat.format(pattern, new Object[] { projectName, "Debug", targtype })); + writer.write("!MESSAGE \r\n"); + writer.write("\r\n"); + + } + + /** + * Gets the first recognized compiler from the + * compilation targets. + * @param targets compilation targets + * @return representative (hopefully) compiler configuration + */ + private CommandLineCompilerConfiguration + getBaseCompilerConfiguration(final Hashtable targets) { + // + // find first target with an DevStudio 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 DevStudioCCompiler) { + return compilerConfig; + } + } + } + return null; + } + + /** + * Writes compiler options. + * @param writer Writer writer + * @param isDebug true if debug. + * @param baseDir String base directory + * @param compilerConfig compiler configuration + * @throws IOException if error on writing project + */ + private void writeCompileOptions(final Writer writer, + final boolean isDebug, + final String baseDir, + final CommandLineCompilerConfiguration + compilerConfig) throws IOException { + StringBuffer baseOptions = new StringBuffer(50); + baseOptions.append("# ADD BASE CPP"); + StringBuffer options = new StringBuffer(50); + options.append("# ADD CPP"); + File[] includePath = compilerConfig.getIncludePath(); + for (int i = 0; i < includePath.length; i++) { + options.append(" /I \""); + String relPath = CUtil.getRelativePath(baseDir, includePath[i]); + options.append(CUtil.toWindowsPath(relPath)); + options.append('"'); + } + Hashtable optionMap = new Hashtable(); + + if (isDebug) { + // + // release options that should be mapped to debug counterparts + // + optionMap.put("/MT", "/MTd"); + optionMap.put("/ML", "/MLd"); + optionMap.put("/MD", "/MDd"); + optionMap.put("/O2", "/Od"); + optionMap.put("/O3", "/Od"); + } else { + // + // debug options that should be mapped to release counterparts + // + optionMap.put("/MTD", "/MT"); + optionMap.put("/MLD", "/ML"); + optionMap.put("/MDD", "/MD"); + optionMap.put("/GM", ""); + optionMap.put("/ZI", ""); + optionMap.put("/OD", "/O2"); + optionMap.put("/GZ", ""); + } + + + + String[] preArgs = compilerConfig.getPreArguments(); + for (int i = 0; i < preArgs.length; i++) { + if (preArgs[i].startsWith("/D")) { + options.append(" /D "); + baseOptions.append(" /D "); + String body = preArgs[i].substring(2); + if (preArgs[i].indexOf('=') >= 0) { + options.append(body); + baseOptions.append(body); + } else { + StringBuffer buf = new StringBuffer("\""); + if ("NDEBUG".equals(body) || "_DEBUG".equals(body)) { + if (isDebug) { + buf.append("_DEBUG"); + } else { + buf.append("NDEBUG"); + } + } else { + buf.append(body); + } + buf.append("\""); + options.append(buf); + baseOptions.append(buf); + } + } else if (!preArgs[i].startsWith("/I")) { + String option = preArgs[i]; + String key = option.toUpperCase(Locale.US); + if (optionMap.containsKey(key)) { + option = optionMap.get(key).toString(); + } + options.append(" "); + options.append(option); + baseOptions.append(" "); + baseOptions.append(option); + } + } + baseOptions.append("\r\n"); + options.append("\r\n"); + writer.write(baseOptions.toString()); + writer.write(options.toString()); + + } + + + + + + /** + * Writes link options. + * @param writer Writer writer + * @param basePath String base path + * @param dependencies project dependencies, used to suppress explicit linking. + * @param linkTarget TargetInfo link target + * @param targets Hashtable all targets + * @throws IOException if unable to write to project file + */ + private void writeLinkOptions(final Writer writer, + final boolean isDebug, + final List dependencies, + final String basePath, + final TargetInfo linkTarget, + final Hashtable targets) throws IOException { + + StringBuffer baseOptions = new StringBuffer(100); + StringBuffer options = new StringBuffer(100); + baseOptions.append("# ADD BASE LINK32"); + options.append("# ADD LINK32"); + + ProcessorConfiguration config = linkTarget.getConfiguration(); + if (config instanceof CommandLineLinkerConfiguration) { + CommandLineLinkerConfiguration linkConfig = + (CommandLineLinkerConfiguration) config; + + File[] linkSources = linkTarget.getAllSources(); + for (int i = 0; i < linkSources.length; i++) { + // + // if file was not compiled or otherwise generated + // + if (targets.get(linkSources[i].getName()) == null) { + // + // if source appears to be a system library or object file + // just output the name of the file (advapi.lib for example) + // otherwise construct a relative path. + // + String relPath = linkSources[i].getName(); + // + // check if file comes from a project dependency + // if it does it should not be explicitly linked + boolean fromDependency = false; + if (relPath.indexOf(".") > 0) { + String baseName = relPath.substring(0, relPath.indexOf(".")); + for(Iterator iter = dependencies.iterator(); iter.hasNext(); ) { + DependencyDef depend = (DependencyDef) iter.next(); + if (baseName.compareToIgnoreCase(depend.getName()) == 0) { + fromDependency = true; + } + } + } + if (!fromDependency) { + if (!CUtil.isSystemPath(linkSources[i])) { + relPath = CUtil.getRelativePath(basePath, linkSources[i]); + } + // + // if path has an embedded space then + // must quote + if (relPath.indexOf(' ') > 0) { + options.append(" \""); + options.append(CUtil.toWindowsPath(relPath)); + options.append("\""); + } else { + options.append(' '); + options.append(CUtil.toWindowsPath(relPath)); + } + } + } + } + String[] preArgs = linkConfig.getPreArguments(); + for (int i = 0; i < preArgs.length; i++) { + if (isDebug || !preArgs[i].equals("/DEBUG")) { + options.append(' '); + options.append(preArgs[i]); + baseOptions.append(' '); + baseOptions.append(preArgs[i]); + } + } + String[] endArgs = linkConfig.getEndArguments(); + for (int i = 0; i < endArgs.length; i++) { + options.append(' '); + options.append(endArgs[i]); + baseOptions.append(' '); + baseOptions.append(endArgs[i]); + } + } + baseOptions.append("\r\n"); + options.append("\r\n"); + writer.write(baseOptions.toString()); + writer.write(options.toString()); + } + + private static void writeComments(final Writer writer, + final List comments) throws IOException { + for(Iterator iter = comments.iterator();iter.hasNext();) { + String comment = ((CommentDef) iter.next()).getText(); + if (comment != null) { + int start = 0; + for(int end = comment.indexOf('\n'); + end != -1; + end = comment.indexOf('\n', start)) { + writer.write("#" + comment.substring(start, end) + "\r\n"); + start = end + 1; + } + } + } + } +} -- cgit v1.2.3