net.sf.antcontrib.cpptasks.devstudio.DevStudioProjectWriter Maven / Gradle / Ivy
/*
*
* 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;
}
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy