Browse Source

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 <src> 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
master
Steve Loughran 22 years ago
parent
commit
7609ac7764
6 changed files with 563 additions and 12 deletions
  1. +37
    -4
      src/etc/testcases/taskdefs/optional/dotnet.xml
  2. +1
    -0
      src/main/org/apache/tools/ant/taskdefs/defaults.properties
  3. +1
    -5
      src/main/org/apache/tools/ant/taskdefs/optional/dotnet/Ilasm.java
  4. +481
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/dotnet/Ildasm.java
  5. +24
    -1
      src/main/org/apache/tools/ant/taskdefs/optional/dotnet/NetCommand.java
  6. +19
    -2
      src/testcases/org/apache/tools/ant/taskdefs/optional/DotnetTest.java

+ 37
- 4
src/etc/testcases/taskdefs/optional/dotnet.xml View File

@@ -6,7 +6,7 @@
<property name="src.dir" location="dotnet"/>
<property name="out.csc" location="${src.dir}/out.cs"/>
<property name="out.app" location="${classes.dir}/out.exe"/>
<property name="out.app" location="${build.dir}/out.exe"/>
<property name="out.type" value="exe"/>
<target name="probe_for_apps" >
@@ -48,6 +48,15 @@
</condition>
<echo> mono.ilasm.found=${mono.ilasm.found}</echo>

<condition property="ildasm.found">
<or>
<available file="ildasm" filepath="${env.PATH}" />
<available file="ildasm.exe" filepath="${env.PATH}" />
<available file="ildasm.exe" filepath="${env.Path}" />
</or>
</condition>
<echo> ilasm.found=${ildasm.found}</echo>
<condition property="dotnetapps.found">
<or>
<and>
@@ -228,17 +237,17 @@
</exec>
</target>

<target name="testILASM" depends="init">
<target name="testILASM" depends="init" if="ilasm.found">
<property name="testILASM.exe"
location="${build.dir}/ExampleIlasm.exe" />
<ilasm
destFile="${testILASM.exe}"
targetType="exe"
>
<src dir="${src.dir}" includes="**/*.il"/>
<src dir="${src.dir}" includes="*.il"/>
</ilasm>
<available property="ilasm.created" file="${testILASM.exe}"/>
<fail unless="ilasm.created">No app ${testCSC.exe} created</fail>
<fail unless="ilasm.created">No app ${testILASM.exe} created</fail>
<exec executable="${testILASM.exe}" failonerror="true" />
</target>

@@ -258,5 +267,29 @@
<property name="ilasm.string" refid="ilasm"/>
<echo>${ilasm.string}</echo>
</target>
<target name="testILDASM" depends="testILASM" if="ildasm.found">
<property name="testILDASM.il"
location="${build.dir}/ExampleIldasm.il" />
<ildasm
srcFile="${testILASM.exe}"
destFile="${testILDASM.il}"
metadata="true"
header="true"
linenumbers="true"
encoding="ascii"
/>
<available property="ildasm.created" file="${testILDASM.il}"/>
<fail unless="ildasm.created">No file ${testILDASM.il} created</fail>
</target>

<!-- this is an error -->
<target name="testILDASM_empty" depends="init" >
<ildasm/>
</target>
<target name="testILDASM_empty" depends="init" >
<ildasm/>
</target>
</project>


+ 1
- 0
src/main/org/apache/tools/ant/taskdefs/defaults.properties View File

@@ -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


+ 1
- 5
src/main/org/apache/tools/ant/taskdefs/optional/dotnet/Ilasm.java View File

@@ -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);


+ 481
- 0
src/main/org/apache/tools/ant/taskdefs/optional/dotnet/Ildasm.java View File

@@ -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
* <http://www.apache.org/>.
*/
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 <code>Icons.resources</code>
* <code>Message.resources</code> 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 <code>resourceDir</code> if
* set, else in the same directory as the <code>destFile</code>.
*
*
* 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 <code>dest</code>
* attribute is <i>always</i> 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 &lt;property location&gt 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:
* <pre>
* pub : Public
* pri : Private
* fam : Family
* asm : Assembly
* faa : Family and Assembly
* foa : Family or Assembly
* psc : Private Scope
*</pre>
* e.g. visibility="pub+pri".
* Family means <code>protected</code> 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
};
}

}
}

+ 24
- 1
src/main/org/apache/tools/ant/taskdefs/optional/dotnet/NetCommand.java View File

@@ -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);


+ 19
- 2
src/testcases/org/apache/tools/ant/taskdefs/optional/DotnetTest.java View File

@@ -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");
}


}


Loading…
Cancel
Save