| /* |
| * 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.Project; |
| 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. |
| * <p> |
| * 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>. |
| * |
| * <p> |
| * 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. |
| * |
| * <p> |
| * 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. |
| * @ant.task category="dotnet" |
| */ |
| 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 original 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"); |
| } |
| } |
| |
| /** |
| * Test for disassembly being needed; use existence and granularity |
| * correct date stamps |
| * @return true iff a rebuild is required. |
| */ |
| private boolean isDisassemblyNeeded() { |
| if (!destFile.exists()) { |
| log("Destination file does not exist: a build is required", |
| Project.MSG_VERBOSE); |
| return true; |
| } |
| long sourceTime = sourceFile.lastModified(); |
| long destTime = destFile.lastModified(); |
| if(sourceTime > (destTime + FileUtils.newFileUtils().getFileTimestampGranularity())) { |
| log("Source file is newer than the dest file: a rebuild is required", |
| Project.MSG_VERBOSE); |
| return true; |
| } else { |
| log("The .il file is up to date", Project.MSG_VERBOSE); |
| return false; |
| } |
| |
| } |
| /** |
| * do the work |
| * @throws BuildException |
| */ |
| public void execute() throws BuildException { |
| validate(); |
| if(!isDisassemblyNeeded()) { |
| return; |
| } |
| 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()) { |
| log("Deleting destination file as it may be corrupt"); |
| 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 option |
| * @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 |
| }; |
| } |
| |
| } |
| } |