1 | /*
|
---|
2 | * Copyright 2003-2004 The Apache Software Foundation
|
---|
3 | *
|
---|
4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
---|
5 | * you may not use this file except in compliance with the License.
|
---|
6 | * You may obtain a copy of the License at
|
---|
7 | *
|
---|
8 | * http://www.apache.org/licenses/LICENSE-2.0
|
---|
9 | *
|
---|
10 | * Unless required by applicable law or agreed to in writing, software
|
---|
11 | * distributed under the License is distributed on an "AS IS" BASIS,
|
---|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
13 | * See the License for the specific language governing permissions and
|
---|
14 | * limitations under the License.
|
---|
15 | *
|
---|
16 | */
|
---|
17 | package org.apache.tools.ant.taskdefs.optional.dotnet;
|
---|
18 |
|
---|
19 | import org.apache.tools.ant.types.EnumeratedAttribute;
|
---|
20 | import org.apache.tools.ant.BuildException;
|
---|
21 | import org.apache.tools.ant.Task;
|
---|
22 | import org.apache.tools.ant.Project;
|
---|
23 | import org.apache.tools.ant.util.FileUtils;
|
---|
24 |
|
---|
25 | import java.io.File;
|
---|
26 |
|
---|
27 | /**
|
---|
28 | * Task to take a .NET or Mono -generated managed executable and turn it
|
---|
29 | * into ILASM assembly code. Useful when converting imported typelibs into
|
---|
30 | * assembler before patching and recompiling, as one has to do when doing
|
---|
31 | * advanced typelib work.
|
---|
32 | * <p>
|
---|
33 | * As well as generating the named output file, the ildasm program
|
---|
34 | * will also generate resource files <code>Icons.resources</code>
|
---|
35 | * <code>Message.resources</code> and a .res file whose filename stub is derived
|
---|
36 | * from the source in ways to obscure to determine.
|
---|
37 | * There is no way to control whether or not these files are created, or where they are created
|
---|
38 | * (they are created in the current directory; their names come from inside the
|
---|
39 | * executable and may be those used by the original developer). This task
|
---|
40 | * creates the resources in the directory specified by <code>resourceDir</code> if
|
---|
41 | * set, else in the same directory as the <code>destFile</code>.
|
---|
42 | *
|
---|
43 | * <p>
|
---|
44 | * This task requires the .NET SDK installed and ildasm on the path.
|
---|
45 | * To disassemble using alternate CLR systems, set the executable attribute
|
---|
46 | * to the name/path of the alternate implementation -one that must
|
---|
47 | * support all the classic ildasm commands.
|
---|
48 | *
|
---|
49 | * <p>
|
---|
50 | * Dependency logic: the task executes the command if the output file is missing
|
---|
51 | * or older than the source file. It does not take into account changes
|
---|
52 | * in the options of the task, or timestamp differences in resource files.
|
---|
53 | * When the underlying ildasm executable fails for some reason, it leaves the
|
---|
54 | * .il file in place with some error message. To prevent this from confusing
|
---|
55 | * the dependency logic, the file specified by the <code>dest</code>
|
---|
56 | * attribute is <i>always</i> deleted after an unsuccessful build.
|
---|
57 | * @ant.task category="dotnet"
|
---|
58 | */
|
---|
59 | public class Ildasm extends Task {
|
---|
60 |
|
---|
61 | /**
|
---|
62 | * source file (mandatory)
|
---|
63 | */
|
---|
64 | private File sourceFile;
|
---|
65 |
|
---|
66 | /**
|
---|
67 | * dest file (mandatory)
|
---|
68 | */
|
---|
69 | private File destFile;
|
---|
70 | /**
|
---|
71 | * progress bar switch
|
---|
72 | */
|
---|
73 | private boolean progressBar = false;
|
---|
74 |
|
---|
75 | /**
|
---|
76 | * what is our encoding
|
---|
77 | */
|
---|
78 | private String encoding;
|
---|
79 |
|
---|
80 | /**
|
---|
81 | * /bytes flag for byte markup
|
---|
82 | */
|
---|
83 |
|
---|
84 | private boolean bytes = false;
|
---|
85 |
|
---|
86 | /**
|
---|
87 | * line numbers? /linenum
|
---|
88 | */
|
---|
89 | private boolean linenumbers = false;
|
---|
90 |
|
---|
91 | /**
|
---|
92 | * /raweh flag for raw exception handling
|
---|
93 | */
|
---|
94 | private boolean rawExceptionHandling = false;
|
---|
95 |
|
---|
96 | /**
|
---|
97 | * show the source; /source
|
---|
98 | */
|
---|
99 | private boolean showSource = false;
|
---|
100 |
|
---|
101 | /**
|
---|
102 | * /quoteallnames to quote all names
|
---|
103 | */
|
---|
104 | private boolean quoteallnames = false;
|
---|
105 |
|
---|
106 | /**
|
---|
107 | * /header for header information
|
---|
108 | */
|
---|
109 | private boolean header = false;
|
---|
110 |
|
---|
111 | /**
|
---|
112 | * when false, sets the /noil attribute
|
---|
113 | * to suppress assembly info
|
---|
114 | */
|
---|
115 | private boolean assembler = true;
|
---|
116 |
|
---|
117 | /**
|
---|
118 | * include metadata
|
---|
119 | * /tokens
|
---|
120 | */
|
---|
121 |
|
---|
122 | private boolean metadata = false;
|
---|
123 |
|
---|
124 | /**
|
---|
125 | * what visibility do we want.
|
---|
126 | *
|
---|
127 | */
|
---|
128 | private String visibility;
|
---|
129 |
|
---|
130 | /**
|
---|
131 | * specific item to disassemble
|
---|
132 | */
|
---|
133 |
|
---|
134 | private String item;
|
---|
135 |
|
---|
136 | /**
|
---|
137 | * override for the executable
|
---|
138 | */
|
---|
139 | private String executable = "ildasm";
|
---|
140 |
|
---|
141 | /**
|
---|
142 | * name of the directory for resources to be created. We cannot control
|
---|
143 | * their names, but we can say where they get created. If not set, the
|
---|
144 | * directory of the dest file is used
|
---|
145 | */
|
---|
146 | private File resourceDir;
|
---|
147 |
|
---|
148 |
|
---|
149 | /**
|
---|
150 | * Set the name of the directory for resources to be created. We cannot control
|
---|
151 | * their names, but we can say where they get created. If not set, the
|
---|
152 | * directory of the dest file is used
|
---|
153 | */
|
---|
154 | public void setResourceDir(File resourceDir) {
|
---|
155 | this.resourceDir = resourceDir;
|
---|
156 | }
|
---|
157 |
|
---|
158 | /**
|
---|
159 | * override the name of the executable (normally ildasm) or set
|
---|
160 | * its full path. Do not set a relative path, as the ugly hacks
|
---|
161 | * needed to create resource files in the dest directory
|
---|
162 | * force us to change to this directory before running the application.
|
---|
163 | * i.e use <property location> to create an absolute path from a
|
---|
164 | * relative one before setting this value.
|
---|
165 | * @param executable
|
---|
166 | */
|
---|
167 | public void setExecutable(String executable) {
|
---|
168 | this.executable = executable;
|
---|
169 | }
|
---|
170 |
|
---|
171 | /**
|
---|
172 | * Select the output encoding: ascii, utf8 or unicode
|
---|
173 | * @param encoding
|
---|
174 | */
|
---|
175 | public void setEncoding(EncodingTypes encoding) {
|
---|
176 | this.encoding = encoding.getValue();
|
---|
177 | }
|
---|
178 |
|
---|
179 | /**
|
---|
180 | * enable (default) or disable assembly language in the output
|
---|
181 | * @param assembler
|
---|
182 | */
|
---|
183 | public void setAssembler(boolean assembler) {
|
---|
184 | this.assembler = assembler;
|
---|
185 | }
|
---|
186 |
|
---|
187 | /**
|
---|
188 | * enable or disable (default) the original bytes as comments
|
---|
189 | * @param bytes
|
---|
190 | */
|
---|
191 | public void setBytes(boolean bytes) {
|
---|
192 | this.bytes = bytes;
|
---|
193 | }
|
---|
194 |
|
---|
195 | /**
|
---|
196 | * the output file (required)
|
---|
197 | * @param destFile
|
---|
198 | */
|
---|
199 | public void setDestFile(File destFile) {
|
---|
200 | this.destFile = destFile;
|
---|
201 | }
|
---|
202 |
|
---|
203 | /**
|
---|
204 | * include header information; default false.
|
---|
205 | * @param header
|
---|
206 | */
|
---|
207 | public void setHeader(boolean header) {
|
---|
208 | this.header = header;
|
---|
209 | }
|
---|
210 |
|
---|
211 | /**
|
---|
212 | * name a single item to decode; a class or a method
|
---|
213 | * e.g item="Myclass::method" or item="namespace1::namespace2::Myclass:method(void(int32))
|
---|
214 | * @param item
|
---|
215 | */
|
---|
216 | public void setItem(String item) {
|
---|
217 | this.item = item;
|
---|
218 | }
|
---|
219 |
|
---|
220 | /**
|
---|
221 | * include line number information; default=false
|
---|
222 | * @param linenumbers
|
---|
223 | */
|
---|
224 | public void setLinenumbers(boolean linenumbers) {
|
---|
225 | this.linenumbers = linenumbers;
|
---|
226 | }
|
---|
227 |
|
---|
228 | /**
|
---|
229 | * include metadata information
|
---|
230 | * @param metadata
|
---|
231 | */
|
---|
232 | public void setMetadata(boolean metadata) {
|
---|
233 | this.metadata = metadata;
|
---|
234 | }
|
---|
235 |
|
---|
236 | /**
|
---|
237 | * show a graphical progress bar in a window during the process; off by default
|
---|
238 | * @param progressBar
|
---|
239 | */
|
---|
240 | public void setProgressBar(boolean progressBar) {
|
---|
241 | this.progressBar = progressBar;
|
---|
242 | }
|
---|
243 |
|
---|
244 | /**
|
---|
245 | * quote all names.
|
---|
246 | * @param quoteallnames
|
---|
247 | */
|
---|
248 | public void setQuoteallnames(boolean quoteallnames) {
|
---|
249 | this.quoteallnames = quoteallnames;
|
---|
250 | }
|
---|
251 |
|
---|
252 | /**
|
---|
253 | * enable raw exception handling (default = false)
|
---|
254 | * @param rawExceptionHandling
|
---|
255 | */
|
---|
256 | public void setRawExceptionHandling(boolean rawExceptionHandling) {
|
---|
257 | this.rawExceptionHandling = rawExceptionHandling;
|
---|
258 | }
|
---|
259 |
|
---|
260 | /**
|
---|
261 | * include the source as comments (default=false)
|
---|
262 | */
|
---|
263 | public void setShowSource(boolean showSource) {
|
---|
264 | this.showSource = showSource;
|
---|
265 | }
|
---|
266 |
|
---|
267 | /**
|
---|
268 | * the file to disassemble -required
|
---|
269 | * @param sourceFile
|
---|
270 | */
|
---|
271 | public void setSourceFile(File sourceFile) {
|
---|
272 | this.sourceFile = sourceFile;
|
---|
273 | }
|
---|
274 |
|
---|
275 | /**
|
---|
276 | * alternate name for sourceFile
|
---|
277 | * @param sourceFile
|
---|
278 | */
|
---|
279 | public void setSrcFile(File sourceFile) {
|
---|
280 | setSourceFile(sourceFile);
|
---|
281 | }
|
---|
282 | /**
|
---|
283 | * This method sets the visibility options. It chooses one or more of the following, with + signs to
|
---|
284 | * concatenate them:
|
---|
285 | * <pre>
|
---|
286 | * pub : Public
|
---|
287 | * pri : Private
|
---|
288 | * fam : Family
|
---|
289 | * asm : Assembly
|
---|
290 | * faa : Family and Assembly
|
---|
291 | * foa : Family or Assembly
|
---|
292 | * psc : Private Scope
|
---|
293 | *</pre>
|
---|
294 | * e.g. visibility="pub+pri".
|
---|
295 | * Family means <code>protected</code> in C#;
|
---|
296 | * @param visibility
|
---|
297 | */
|
---|
298 | public void setVisibility(String visibility) {
|
---|
299 | this.visibility = visibility;
|
---|
300 | }
|
---|
301 |
|
---|
302 | /**
|
---|
303 | * verify that source and dest are ok
|
---|
304 | */
|
---|
305 | private void validate() {
|
---|
306 | if (sourceFile == null || !sourceFile.exists() || !sourceFile.isFile()) {
|
---|
307 | throw new BuildException("invalid source");
|
---|
308 | }
|
---|
309 | if (destFile == null || destFile.isDirectory()) {
|
---|
310 | throw new BuildException("invalid dest");
|
---|
311 | }
|
---|
312 | if (resourceDir != null
|
---|
313 | && (!resourceDir.exists() || !resourceDir.isDirectory())) {
|
---|
314 | throw new BuildException("invalid resource directory");
|
---|
315 | }
|
---|
316 | }
|
---|
317 |
|
---|
318 | /**
|
---|
319 | * Test for disassembly being needed; use existence and granularity
|
---|
320 | * correct date stamps
|
---|
321 | * @return true iff a rebuild is required.
|
---|
322 | */
|
---|
323 | private boolean isDisassemblyNeeded() {
|
---|
324 | if (!destFile.exists()) {
|
---|
325 | log("Destination file does not exist: a build is required",
|
---|
326 | Project.MSG_VERBOSE);
|
---|
327 | return true;
|
---|
328 | }
|
---|
329 | long sourceTime = sourceFile.lastModified();
|
---|
330 | long destTime = destFile.lastModified();
|
---|
331 | if(sourceTime > (destTime + FileUtils.newFileUtils().getFileTimestampGranularity())) {
|
---|
332 | log("Source file is newer than the dest file: a rebuild is required",
|
---|
333 | Project.MSG_VERBOSE);
|
---|
334 | return true;
|
---|
335 | } else {
|
---|
336 | log("The .il file is up to date", Project.MSG_VERBOSE);
|
---|
337 | return false;
|
---|
338 | }
|
---|
339 |
|
---|
340 | }
|
---|
341 | /**
|
---|
342 | * do the work
|
---|
343 | * @throws BuildException
|
---|
344 | */
|
---|
345 | public void execute() throws BuildException {
|
---|
346 | validate();
|
---|
347 | if(!isDisassemblyNeeded()) {
|
---|
348 | return;
|
---|
349 | }
|
---|
350 | NetCommand command = new NetCommand(this, "ildasm", executable);
|
---|
351 | command.setFailOnError(true);
|
---|
352 | //fill in args
|
---|
353 | command.addArgument("/text");
|
---|
354 | command.addArgument("/out=" + destFile.toString());
|
---|
355 | if (!progressBar) {
|
---|
356 | command.addArgument("/nobar");
|
---|
357 | }
|
---|
358 | if (linenumbers) {
|
---|
359 | command.addArgument("/linenum");
|
---|
360 | }
|
---|
361 | if (showSource) {
|
---|
362 | command.addArgument("/source");
|
---|
363 | }
|
---|
364 | if (quoteallnames) {
|
---|
365 | command.addArgument("/quoteallnames");
|
---|
366 | }
|
---|
367 | if (header) {
|
---|
368 | command.addArgument("/header");
|
---|
369 | }
|
---|
370 | if (!assembler) {
|
---|
371 | command.addArgument("/noil");
|
---|
372 | }
|
---|
373 | if (metadata) {
|
---|
374 | command.addArgument("/tokens");
|
---|
375 | }
|
---|
376 | command.addArgument("/item:", item);
|
---|
377 | if (rawExceptionHandling) {
|
---|
378 | command.addArgument("/raweh");
|
---|
379 | }
|
---|
380 | command.addArgument(EncodingTypes.getEncodingOption(encoding));
|
---|
381 | if (bytes) {
|
---|
382 | command.addArgument("/bytes");
|
---|
383 | }
|
---|
384 | command.addArgument("/vis:", visibility);
|
---|
385 |
|
---|
386 | //add the source file
|
---|
387 | command.addArgument(sourceFile.getAbsolutePath());
|
---|
388 |
|
---|
389 | //determine directory: resourceDir if set,
|
---|
390 | //the dir of the destFile if not
|
---|
391 | File execDir = resourceDir;
|
---|
392 | if (execDir == null) {
|
---|
393 | execDir = destFile.getParentFile();
|
---|
394 | }
|
---|
395 | command.setDirectory(execDir);
|
---|
396 |
|
---|
397 | //now run
|
---|
398 | try {
|
---|
399 | command.runCommand();
|
---|
400 | } catch (BuildException e) {
|
---|
401 | //forcibly delete the output file in case of trouble
|
---|
402 | if (destFile.exists()) {
|
---|
403 | log("Deleting destination file as it may be corrupt");
|
---|
404 | destFile.delete();
|
---|
405 | }
|
---|
406 | //then rethrow the exception
|
---|
407 | throw e;
|
---|
408 | }
|
---|
409 |
|
---|
410 | }
|
---|
411 |
|
---|
412 | /**
|
---|
413 | * encoding options; the default is ascii
|
---|
414 | */
|
---|
415 | public static class EncodingTypes extends EnumeratedAttribute {
|
---|
416 | public final static String UNICODE = "unicode";
|
---|
417 | public final static String UTF8 = "utf8";
|
---|
418 | public final static String ASCII = "ascii";
|
---|
419 | public String[] getValues() {
|
---|
420 | return new String[]{
|
---|
421 | ASCII,
|
---|
422 | UTF8,
|
---|
423 | UNICODE,
|
---|
424 | };
|
---|
425 | }
|
---|
426 |
|
---|
427 | /**
|
---|
428 | * This method maps from an encoding enum to an encoding option.
|
---|
429 | * @param enumValue
|
---|
430 | * @return The encoding option indicated by the enum value.
|
---|
431 | */
|
---|
432 | public static String getEncodingOption(String enumValue) {
|
---|
433 | if (UNICODE.equals(enumValue)) {
|
---|
434 | return "/unicode";
|
---|
435 | }
|
---|
436 | if (UTF8.equals(enumValue)) {
|
---|
437 | return "/utf8";
|
---|
438 | }
|
---|
439 | return null;
|
---|
440 | }
|
---|
441 | }
|
---|
442 |
|
---|
443 | /**
|
---|
444 | * visibility options for decoding
|
---|
445 | */
|
---|
446 | public static class VisibilityOptions extends EnumeratedAttribute {
|
---|
447 | public String[] getValues() {
|
---|
448 | return new String[]{
|
---|
449 | "pub", //Public
|
---|
450 | "pri", //Private
|
---|
451 | "fam", //Family
|
---|
452 | "asm", //Assembly
|
---|
453 | "faa", //Family and Assembly
|
---|
454 | "foa", //Family or Assembly
|
---|
455 | "psc", //Private Scope
|
---|
456 | };
|
---|
457 | }
|
---|
458 |
|
---|
459 | }
|
---|
460 | }
|
---|