From 7609ac77647b4818b85714ad59b60dd59780ba5a Mon Sep 17 00:00:00 2001 From: Steve Loughran Date: Sat, 6 Sep 2003 07:09:59 +0000 Subject: [PATCH] 1. new ildasm command to disassemble .il files. So that you can disassemble the output of tlbimport, patch it and recompile it. Yes, that is the official way to get C arrays marshalled into .net 2. patch to ilasm so that the default fileset is ignored when elements are added and srcDir attr is undefined 3. tests! ildasm has duff resource handling. It autogenerates the resources in the dir it runs, you cannot predict or control their names. So we CD to output dir, that is the dir of destFile unless an explicit res dir is provided. Ugly, ugly ugly. And to think that they claim .net is better on account of the ease of development. git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@275190 13f79535-47bb-0310-9956-ffa450edef68 --- .../testcases/taskdefs/optional/dotnet.xml | 41 +- .../tools/ant/taskdefs/defaults.properties | 1 + .../ant/taskdefs/optional/dotnet/Ilasm.java | 6 +- .../ant/taskdefs/optional/dotnet/Ildasm.java | 481 ++++++++++++++++++ .../taskdefs/optional/dotnet/NetCommand.java | 25 +- .../ant/taskdefs/optional/DotnetTest.java | 21 +- 6 files changed, 563 insertions(+), 12 deletions(-) create mode 100644 src/main/org/apache/tools/ant/taskdefs/optional/dotnet/Ildasm.java 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 @@ - + @@ -48,6 +48,15 @@ mono.ilasm.found=${mono.ilasm.found} + + + + + + + + ilasm.found=${ildasm.found} + @@ -228,17 +237,17 @@ - + - + - No app ${testCSC.exe} created + No app ${testILASM.exe} created @@ -258,5 +267,29 @@ ${ilasm.string} + + + + + + No file ${testILDASM.il} created + + + + + + + + + + diff --git a/src/main/org/apache/tools/ant/taskdefs/defaults.properties b/src/main/org/apache/tools/ant/taskdefs/defaults.properties index bcf74f6dd..e259c024c 100644 --- a/src/main/org/apache/tools/ant/taskdefs/defaults.properties +++ b/src/main/org/apache/tools/ant/taskdefs/defaults.properties @@ -196,6 +196,7 @@ sshexec=org.apache.tools.ant.taskdefs.optional.ssh.SSHExec jsharpc=org.apache.tools.ant.taskdefs.optional.dotnet.JSharp rexec=org.apache.tools.ant.taskdefs.optional.net.RExecTask scriptdef=org.apache.tools.ant.taskdefs.optional.script.ScriptDef +ildasm=org.apache.tools.ant.taskdefs.optional.dotnet.Ildasm # deprecated ant tasks (kept for back compatibility) starteam=org.apache.tools.ant.taskdefs.optional.scm.AntStarTeamCheckOut diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/Ilasm.java b/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/Ilasm.java index 42fadf15e..6cb32362a 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/Ilasm.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/Ilasm.java @@ -304,7 +304,7 @@ public class Ilasm /** * Set the output file; identical to setDestFile - * @see DotnetBaseMatchingTask.setDestFile + * @see DotnetBaseMatchingTask#setDestFile *@param params The new outputFile value */ public void setOutputFile(File params) { @@ -473,10 +473,6 @@ public class Ilasm */ public void execute() throws BuildException { - if (srcDir == null) { - srcDir = getProject().resolveFile("."); - } - NetCommand command = buildIlasmCommand(); addFilesAndExecute(command, false); diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/Ildasm.java b/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/Ildasm.java new file mode 100644 index 000000000..b5bb501dc --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/Ildasm.java @@ -0,0 +1,481 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Ant" and "Apache Software Foundation" + * must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package org.apache.tools.ant.taskdefs.optional.dotnet; + +import org.apache.tools.ant.types.EnumeratedAttribute; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.util.FileUtils; + +import java.io.File; + +/** + * Task to take a .NET or Mono -generated managed executable and turn it + * into ILASM assembly code. Useful when converting imported typelibs into + * assembler before patching and recompiling, as one has to do when doing + * advanced typelib work. + * + * As well as generating the named output file, the ildasm program + * will also generate resource files 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"); + } + }