- /*
- * Copyright 2003-2004 The Apache Software Foundation
- *
- * 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 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);
- }
- /**
- * This method sets the visibility options. It chooses 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,
- };
- }
-
- /**
- * This method maps from an encoding enum to an encoding option.
- * @param enumValue
- * @return The encoding option indicated by the enum value.
- */
- 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
- };
- }
-
- }
- }