diff --git a/src/etc/testcases/taskdefs/optional/dotnet.xml b/src/etc/testcases/taskdefs/optional/dotnet.xml
index eaa4e1c9a..7c4cfab69 100644
--- a/src/etc/testcases/taskdefs/optional/dotnet.xml
+++ b/src/etc/testcases/taskdefs/optional/dotnet.xml
@@ -6,7 +6,7 @@
Icons.resources
+ * Message.resources
and a .res file whose filename stub is derived
+ * from the source in ways to obscure to determine.
+ * There is no way to control whether or not these files are created, or where they are created
+ * (they are created in the current directory; their names come from inside the
+ * executable and may be those used by the original developer). This task
+ * creates the resources in the directory specified by resourceDir
if
+ * set, else in the same directory as the destFile
.
+ *
+ *
+ * This task requires the .NET SDK installed and ildasm on the path.
+ * To disassemble using alternate CLR systems, set the executable attribute
+ * to the name/path of the alternate implementation -one that must
+ * support all the classic ildasm commands.
+ *
+ *
+ * Dependency logic: the task executes the command if the output file is missing
+ * or older than the source file. It does not take into account changes
+ * in the options of the task, or timestamp differences in resource files.
+ * When the underlying ildasm executable fails for some reason, it leaves the
+ * .il file in place with some error message. To prevent this from confusing
+ * the dependency logic, the file specified by the dest
+ * attribute is always deleted after an unsuccessful build.
+ */
+public class Ildasm extends Task {
+
+ /**
+ * source file (mandatory)
+ */
+ private File sourceFile;
+
+ /**
+ * dest file (mandatory)
+ */
+ private File destFile;
+ /**
+ * progress bar switch
+ */
+ private boolean progressBar=false;
+
+ /**
+ * what is our encoding
+ */
+ private String encoding;
+
+ /**
+ * /bytes flag for byte markup
+ */
+
+ private boolean bytes=false;
+
+ /**
+ * line numbers? /linenum
+ */
+ private boolean linenumbers=false;
+
+ /**
+ * /raweh flag for raw exception handling
+ */
+ private boolean rawExceptionHandling=false;
+
+ /**
+ * show the source; /source
+ */
+ private boolean showSource=false;
+
+ /**
+ * /quoteallnames to quote all names
+ */
+ private boolean quoteallnames=false;
+
+ /**
+ * /header for header information
+ */
+ private boolean header=false;
+
+ /**
+ * when false, sets the /noil attribute
+ * to suppress assembly info
+ */
+ private boolean assembler=true;
+
+ /**
+ * include metadata
+ * /tokens
+ */
+
+ private boolean metadata=false;
+
+ /**
+ * what visibility do we want.
+ *
+ */
+ private String visibility;
+
+ /**
+ * specific item to disassemble
+ */
+
+ private String item;
+
+ /**
+ * override for the executable
+ */
+ private String executable="ildasm";
+
+ /**
+ * name of the directory for resources to be created. We cannot control
+ * their names, but we can say where they get created. If not set, the
+ * directory of the dest file is used
+ */
+ private File resourceDir;
+
+
+ /**
+ * Set the name of the directory for resources to be created. We cannot control
+ * their names, but we can say where they get created. If not set, the
+ * directory of the dest file is used
+ */
+ public void setResourceDir(File resourceDir) {
+ this.resourceDir = resourceDir;
+ }
+
+ /**
+ * override the name of the executable (normally ildasm) or set
+ * its full path. Do not set a relative path, as the ugly hacks
+ * needed to create resource files in the dest directory
+ * force us to change to this directory before running the application.
+ * i.e use <property location> to create an absolute path from a
+ * relative one before setting this value.
+ * @param executable
+ */
+ public void setExecutable(String executable) {
+ this.executable = executable;
+ }
+
+ /**
+ * Select the output encoding: ascii, utf8 or unicode
+ * @param encoding
+ */
+ public void setEncoding(EncodingTypes encoding) {
+ this.encoding = encoding.getValue();
+ }
+
+ /**
+ * enable (default) or disable assembly language in the output
+ * @param assembler
+ */
+ public void setAssembler(boolean assembler) {
+ this.assembler = assembler;
+ }
+
+ /**
+ * enable or disable (default) the orginal bytes as comments
+ * @param bytes
+ */
+ public void setBytes(boolean bytes) {
+ this.bytes = bytes;
+ }
+
+ /**
+ * the output file (required)
+ * @param destFile
+ */
+ public void setDestFile(File destFile) {
+ this.destFile = destFile;
+ }
+
+ /**
+ * include header information; default false.
+ * @param header
+ */
+ public void setHeader(boolean header) {
+ this.header = header;
+ }
+
+ /**
+ * name a single item to decode; a class or a method
+ * e.g item="Myclass::method" or item="namespace1::namespace2::Myclass:method(void(int32))
+ * @param item
+ */
+ public void setItem(String item) {
+ this.item = item;
+ }
+
+ /**
+ * include line number information; default=false
+ * @param linenumbers
+ */
+ public void setLinenumbers(boolean linenumbers) {
+ this.linenumbers = linenumbers;
+ }
+
+ /**
+ * include metadata information
+ * @param metadata
+ */
+ public void setMetadata(boolean metadata) {
+ this.metadata = metadata;
+ }
+
+ /**
+ * show a graphical progress bar in a window during the process; off by default
+ * @param progressBar
+ */
+ public void setProgressBar(boolean progressBar) {
+ this.progressBar = progressBar;
+ }
+
+ /**
+ * quote all names.
+ * @param quoteallnames
+ */
+ public void setQuoteallnames(boolean quoteallnames) {
+ this.quoteallnames = quoteallnames;
+ }
+
+ /**
+ * enable raw exception handling (default = false)
+ * @param rawExceptionHandling
+ */
+ public void setRawExceptionHandling(boolean rawExceptionHandling) {
+ this.rawExceptionHandling = rawExceptionHandling;
+ }
+
+ /**
+ * include the source as comments (default=false)
+ */
+ public void setShowSource(boolean showSource) {
+ this.showSource = showSource;
+ }
+
+ /**
+ * the file to disassemble -required
+ * @param sourceFile
+ */
+ public void setSourceFile(File sourceFile) {
+ this.sourceFile = sourceFile;
+ }
+
+ /**
+ * alternate name for sourceFile
+ * @param sourceFile
+ */
+ public void setSrcFile(File sourceFile) {
+ setSourceFile(sourceFile);
+ }
+ /**
+ * visibility options: one or more of the following, with + signs to
+ * concatenate them:
+ *
+ * pub : Public + * pri : Private + * fam : Family + * asm : Assembly + * faa : Family and Assembly + * foa : Family or Assembly + * psc : Private Scope + *+ * e.g. visibility="pub+pri". + * Family means
protected
in C#;
+ * @param visibility
+ */
+ public void setVisibility(String visibility) {
+ this.visibility = visibility;
+ }
+
+ /**
+ * verify that source and dest are ok
+ */
+ private void validate() {
+ if(sourceFile==null || !sourceFile.exists() || !sourceFile.isFile()) {
+ throw new BuildException("invalid source");
+ }
+ if(destFile==null || destFile.isDirectory()) {
+ throw new BuildException("invalid dest");
+ }
+ if(resourceDir!=null
+ && (!resourceDir.exists() || !resourceDir.isDirectory())) {
+ throw new BuildException("invalid resource directory");
+ }
+ }
+
+ /**
+ *
+ * @return
+ */
+ private boolean isDisassemblyNeeded() {
+ if(!destFile.exists()) {
+ return true;
+ }
+ long sourceTime=sourceFile.lastModified();
+ long destTime=destFile.lastModified();
+ return sourceTime>(destTime+ FileUtils.newFileUtils().getFileTimestampGranularity());
+
+ }
+ /**
+ * do the work
+ * @throws BuildException
+ */
+ public void execute() throws BuildException {
+ validate();
+ NetCommand command = new NetCommand(this, "ildasm", executable);
+ command.setFailOnError(true);
+ //fill in args
+ command.addArgument("/text");
+ command.addArgument("/out="+destFile.toString());
+ if(!progressBar) {
+ command.addArgument("/nobar");
+ }
+ if(linenumbers) {
+ command.addArgument("/linenum");
+ }
+ if (showSource) {
+ command.addArgument("/source");
+ }
+ if (quoteallnames) {
+ command.addArgument("/quoteallnames");
+ }
+ if (header) {
+ command.addArgument("/header");
+ }
+ if (!assembler) {
+ command.addArgument("/noil");
+ }
+ if (metadata) {
+ command.addArgument("/tokens");
+ }
+ command.addArgument("/item:",item);
+ if (rawExceptionHandling) {
+ command.addArgument("/raweh");
+ }
+ command.addArgument(EncodingTypes.getEncodingOption(encoding));
+ if (bytes) {
+ command.addArgument("/bytes");
+ }
+ command.addArgument("/vis:",visibility);
+
+ //add the source file
+ command.addArgument(sourceFile.getAbsolutePath());
+
+ //determine directory: resourceDir if set,
+ //the dir of the destFile if not
+ File execDir=resourceDir;
+ if(execDir==null) {
+ execDir=destFile.getParentFile();
+ }
+ command.setDirectory(execDir);
+
+ //now run
+ try {
+ command.runCommand();
+ } catch (BuildException e) {
+ //forcibly delete the output file in case of trouble
+ if(destFile.exists()) {
+ destFile.delete();
+ }
+ //then rethrow the exception
+ throw e;
+ }
+
+ }
+
+ /**
+ * encoding options; the default is ascii
+ */
+ public static class EncodingTypes extends EnumeratedAttribute {
+ public final static String UNICODE= "unicode";
+ public final static String UTF8 = "utf8";
+ public final static String ASCII = "ascii";
+ public String[] getValues() {
+ return new String[]{
+ ASCII,
+ UTF8,
+ UNICODE,
+ };
+ }
+
+ /**
+ * map from an encoding enum to an encoding opion
+ * @param enumValue
+ * @return
+ */
+ public static String getEncodingOption(String enumValue) {
+ if(UNICODE.equals(enumValue)) {
+ return "/unicode";
+ }
+ if (UTF8.equals(enumValue)) {
+ return "/utf8";
+ }
+ return null;
+ }
+ }
+
+ /**
+ * visibility options for decoding
+ */
+ public static class VisibilityOptions extends EnumeratedAttribute {
+ public String[] getValues() {
+ return new String[]{
+ "pub", //Public
+ "pri", //Private
+ "fam", //Family
+ "asm", //Assembly
+ "faa", //Family and Assembly
+ "foa", //Family or Assembly
+ "psc", //Private Scope
+ };
+ }
+
+ }
+}
diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/NetCommand.java b/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/NetCommand.java
index e0ceeb695..da6868d8f 100644
--- a/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/NetCommand.java
+++ b/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/NetCommand.java
@@ -124,6 +124,11 @@ public class NetCommand {
*/
protected boolean failOnError;
+ /**
+ * the directory to execute the command in. When null, the current
+ * directory is used.
+ */
+ private File directory;
/**
* constructor
@@ -174,6 +179,14 @@ public class NetCommand {
}
+ /**
+ * set the directory to run from, if the default is inadequate
+ * @param directory
+ */
+ public void setDirectory(File directory) {
+ this.directory = directory;
+ }
+
/**
* verbose text log
*
@@ -206,7 +219,13 @@ public class NetCommand {
}
}
- public void addArgument(String argument1, String argument2) {
+ /**
+ * concatenate two strings together and add them as a single argument,
+ * but only if argument2 is non-null and non-zero length
+ *
+ *@param argument1 The first argument
+ *@param argument2 The second argument
+ */ public void addArgument(String argument1, String argument2) {
if (argument2 != null && argument2.length() != 0) {
commandLine.createArgument().setValue(argument1 + argument2);
}
@@ -224,6 +243,10 @@ public class NetCommand {
throw new RuntimeException("Owner has no project");
}
File dir = owner.getProject().getBaseDir();
+ if (directory != null) {
+ dir=directory;
+ }
+
ExecuteStreamHandler handler = new LogStreamHandler(owner,
Project.MSG_INFO, Project.MSG_WARN);
executable = new Execute(handler, null);
diff --git a/src/testcases/org/apache/tools/ant/taskdefs/optional/DotnetTest.java b/src/testcases/org/apache/tools/ant/taskdefs/optional/DotnetTest.java
index c48d6160b..9ddf7bcdd 100644
--- a/src/testcases/org/apache/tools/ant/taskdefs/optional/DotnetTest.java
+++ b/src/testcases/org/apache/tools/ant/taskdefs/optional/DotnetTest.java
@@ -129,11 +129,28 @@ public class DotnetTest extends BuildFileTest {
}
/**
- * A unit test for JUnit
+ * test we can assemble
*/
public void testILASM() throws Exception {
executeTarget("testILASM");
- }
+ }
+
+ /**
+ * test we can disassemble
+ */
+ public void testILDASM() throws Exception {
+ executeTarget("testILDASM");
+ }
+
+ /**
+ * test we can disassemble
+ */
+ public void testILDASM_empty() throws Exception {
+ expectBuildExceptionContaining("testILDASM_empty",
+ "parameter validation",
+ "invalid");
+ }
+
}