001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: /*
020: * build notes
021: * -The reference CD to listen to while editing this file is
022: * nap:Cream+Live+2001+CD+2
023: */
024:
025: // place in the optional ant tasks package
026: // but in its own dotnet group
027: package org.apache.tools.ant.taskdefs.optional.dotnet;
028:
029: // imports
030:
031: import java.io.File;
032: import java.util.Vector;
033: import java.util.Enumeration;
034: import java.util.Hashtable;
035:
036: import org.apache.tools.ant.BuildException;
037: import org.apache.tools.ant.Project;
038: import org.apache.tools.ant.types.Commandline;
039: import org.apache.tools.ant.types.Path;
040: import org.apache.tools.ant.types.FileSet;
041: import org.apache.tools.ant.types.EnumeratedAttribute;
042:
043: /**
044: * Abstract superclass for dotnet compiler tasks.
045: *
046: * History
047: * <table>
048: * <tr>
049: * <td>
050: * 0.1
051: * </td>
052: * <td>
053: * First creation
054: * </td>
055: * <td>
056: * Most of the code here was copied verbatim from v0.3 of
057: * Steve Loughran's CSharp optional task. Abstracted functionality
058: * to allow subclassing of other dotnet compiler types.
059: * </td>
060: * </tr>
061: *
062: * </table>
063: *
064: *
065: * @version 0.1
066: */
067:
068: public abstract class DotnetCompile extends DotnetBaseMatchingTask {
069:
070: /**
071: * list of reference classes. (pretty much a classpath equivalent)
072: */
073: private String references;
074:
075: /**
076: * flag to enable automatic reference inclusion
077: */
078: private boolean includeDefaultReferences = true;
079:
080: /**
081: * icon for incorporation into apps
082: */
083: private File win32icon;
084:
085: /**
086: * icon for incorporation into apps
087: */
088: private File win32res;
089:
090: /**
091: * flag to control action on execution trouble
092: */
093: private boolean failOnError;
094:
095: /**
096: * using the path approach didn't work as it could not handle the implicit
097: * execution path. Perhaps that could be extracted from the runtime and
098: * then the path approach would be viable
099: */
100: private Path referenceFiles;
101:
102: /**
103: * optimise flag
104: */
105: private boolean optimize;
106:
107: // CheckStyle:VisibilityModifier OFF - bc
108: /**
109: * a list of definitions to support;
110: */
111: protected Vector definitionList = new Vector();
112:
113: /**
114: * our resources
115: */
116: protected Vector resources = new Vector();
117:
118: /**
119: * executable
120: */
121:
122: protected String executable;
123:
124: protected static final String REFERENCE_OPTION = "/reference:";
125:
126: /**
127: * debug flag. Controls generation of debug information.
128: */
129: protected boolean debug;
130:
131: /**
132: * warning level: 0-4, with 4 being most verbose
133: */
134: private int warnLevel;
135:
136: /**
137: * main class (or null for automatic choice)
138: */
139: protected String mainClass;
140:
141: /**
142: * any extra command options?
143: */
144: protected String extraOptions;
145:
146: /**
147: * type of target. Should be one of exe|library|module|winexe|(null)
148: * default is exe; the actual value (if not null) is fed to the command
149: * line. <br>
150: * See /target
151: */
152: protected String targetType;
153:
154: /**
155: * utf out flag
156: */
157:
158: protected boolean utf8output = false;
159:
160: /**
161: * list of extra modules to refer to
162: */
163: protected String additionalModules;
164: /**
165: * filesets of references
166: */
167: protected Vector referenceFilesets = new Vector();
168:
169: /**
170: * flag to set to to use @file based command cache
171: */
172: private boolean useResponseFile = false;
173: private static final int AUTOMATIC_RESPONSE_FILE_THRESHOLD = 64;
174:
175: // CheckStyle:VisibilityModifier ON
176:
177: /**
178: * constructor inits everything and set up the search pattern
179: */
180:
181: public DotnetCompile() {
182: clear();
183: setIncludes(getFilePattern());
184: }
185:
186: /**
187: * reset all contents.
188: */
189: public void clear() {
190: targetType = null;
191: win32icon = null;
192: srcDir = null;
193: mainClass = null;
194: warnLevel = 3;
195: optimize = false;
196: debug = true;
197: references = null;
198: failOnError = true;
199: additionalModules = null;
200: includeDefaultReferences = true;
201: extraOptions = null;
202: }
203:
204: /**
205: * Semicolon separated list of DLLs to refer to.
206: *
207: *@param s The new References value
208: */
209: public void setReferences(String s) {
210: references = s;
211: }
212:
213: /**
214: * get the reference string or null for no argument needed
215: *
216: *@return The References Parameter to CSC
217: */
218: protected String getReferencesParameter() {
219: //bail on no references
220: if (notEmpty(references)) {
221: if (isWindows) {
222: return '\"' + REFERENCE_OPTION + references + '\"';
223: } else {
224: return REFERENCE_OPTION + references;
225: }
226: } else {
227: return null;
228: }
229: }
230:
231: /**
232: * Path of references to include.
233: * Wildcards should work.
234: *
235: *@param path another path to append
236: */
237: public void setReferenceFiles(Path path) {
238: //demand create pathlist
239: if (referenceFiles == null) {
240: referenceFiles = new Path(this .getProject());
241: }
242: referenceFiles.append(path);
243: }
244:
245: /**
246: * add a new reference fileset to the compilation
247: * @param reference the files to use.
248: */
249: public void addReference(FileSet reference) {
250: referenceFilesets.add(reference);
251: }
252:
253: /**
254: * turn the path list into a list of files and a /references argument
255: *
256: *@return null or a string of references.
257: */
258: protected String getReferenceFilesParameter() {
259: //bail on no references
260: if (references == null) {
261: return null;
262: }
263: //iterate through the ref list & generate an entry for each
264: //or just rely on the fact that the toString operator does this, but
265: //noting that the separator is ';' on windows, ':' on unix
266:
267: //bail on no references listed
268: if (references.length() == 0) {
269: return null;
270: }
271:
272: StringBuffer s = new StringBuffer(REFERENCE_OPTION);
273: if (isWindows) {
274: s.append('\"');
275: }
276: s.append(references);
277: if (isWindows) {
278: s.append('\"');
279: }
280: return s.toString();
281: }
282:
283: /**
284: * If true, automatically includes the common assemblies
285: * in dotnet, and tells the compiler to link in mscore.dll.
286: *
287: * set the automatic reference inclusion flag on or off this flag controls
288: * the /nostdlib option in CSC
289: *
290: *@param f on/off flag
291: */
292: public void setIncludeDefaultReferences(boolean f) {
293: includeDefaultReferences = f;
294: }
295:
296: /**
297: * query automatic reference inclusion flag
298: *
299: *@return true if flag is turned on
300: */
301: public boolean getIncludeDefaultReferences() {
302: return includeDefaultReferences;
303: }
304:
305: /**
306: * get the include default references flag or null for no argument needed
307: *
308: *@return The Parameter to CSC
309: */
310: protected String getIncludeDefaultReferencesParameter() {
311: return "/nostdlib" + (includeDefaultReferences ? "-" : "+");
312: }
313:
314: /**
315: * If true, enables optimization flag.
316: *
317: *@param f on/off flag
318: */
319: public void setOptimize(boolean f) {
320: optimize = f;
321: }
322:
323: /**
324: * query the optimise flag
325: *
326: *@return true if optimise is turned on
327: */
328: public boolean getOptimize() {
329: return optimize;
330: }
331:
332: /**
333: * get the optimise flag or null for no argument needed
334: *
335: *@return The Optimize Parameter to CSC
336: */
337: protected String getOptimizeParameter() {
338: return "/optimize" + (optimize ? "+" : "-");
339: }
340:
341: /**
342: * set the debug flag on or off.
343: *
344: *@param f on/off flag
345: */
346: public void setDebug(boolean f) {
347: debug = f;
348: }
349:
350: /**
351: * query the debug flag
352: *
353: *@return true if debug is turned on
354: */
355: public boolean getDebug() {
356: return debug;
357: }
358:
359: /**
360: * get the debug switch argument
361: *
362: *@return The Debug Parameter to CSC
363: */
364: protected String getDebugParameter() {
365: return "/debug" + (debug ? "+" : "-");
366: }
367:
368: /**
369: * Level of warning currently between 1 and 4
370: * with 4 being the strictest.
371: *
372: *@param warnLevel warn level -see .net docs for valid range (probably
373: * 0-4)
374: */
375: public void setWarnLevel(int warnLevel) {
376: this .warnLevel = warnLevel;
377: }
378:
379: /**
380: * query warn level
381: *
382: *@return current value
383: */
384: public int getWarnLevel() {
385: return warnLevel;
386: }
387:
388: /**
389: * get the warn level switch
390: *
391: *@return The WarnLevel Parameter to CSC
392: */
393: protected String getWarnLevelParameter() {
394: return "/warn:" + warnLevel;
395: }
396:
397: /**
398: * Sets the name of main class for executables.
399: *
400: *@param mainClass The new MainClass value
401: */
402: public void setMainClass(String mainClass) {
403: this .mainClass = mainClass;
404: }
405:
406: /**
407: * Gets the MainClass attribute
408: *
409: *@return The MainClass value
410: */
411: public String getMainClass() {
412: return this .mainClass;
413: }
414:
415: /**
416: * get the /main argument or null for no argument needed
417: *
418: *@return The MainClass Parameter to CSC
419: */
420: protected String getMainClassParameter() {
421: if (mainClass != null && mainClass.length() != 0) {
422: return "/main:" + mainClass;
423: } else {
424: return null;
425: }
426: }
427:
428: /**
429: * Any extra options which are not explicitly supported
430: * by this task.
431: *
432: *@param extraOptions The new ExtraOptions value
433: */
434: public void setExtraOptions(String extraOptions) {
435: this .extraOptions = extraOptions;
436: }
437:
438: /**
439: * Gets the ExtraOptions attribute
440: *
441: *@return The ExtraOptions value
442: */
443: public String getExtraOptions() {
444: return this .extraOptions;
445: }
446:
447: /**
448: * get any extra options or null for no argument needed
449: *
450: *@return The ExtraOptions Parameter to CSC
451: */
452: protected String getExtraOptionsParameter() {
453: if (extraOptions != null && extraOptions.length() != 0) {
454: return extraOptions;
455: } else {
456: return null;
457: }
458: }
459:
460: /**
461: * get any extra options or null for no argument needed, split
462: * them if they represent multiple options.
463: *
464: * @return The ExtraOptions Parameter to CSC
465: */
466: protected String[] getExtraOptionsParameters() {
467: String extra = getExtraOptionsParameter();
468: return extra == null ? null : Commandline
469: .translateCommandline(extra);
470: }
471:
472: /**
473: * Set the destination directory of files to be compiled.
474: *
475: *@param dirName The new DestDir value
476: */
477: public void setDestDir(File dirName) {
478: log("DestDir currently unused", Project.MSG_WARN);
479: }
480:
481: /**
482: * set the target type to one of exe|library|module|winexe
483: * @param targetType the enumerated value.
484: */
485: public void setTargetType(TargetTypes targetType) {
486: this .targetType = targetType.getValue();
487: }
488:
489: /**
490: * Set the type of target.
491: *
492: *@param ttype The new TargetType value
493: *@exception BuildException if target is not one of
494: * exe|library|module|winexe
495: */
496: public void setTargetType(String ttype) throws BuildException {
497: ttype = ttype.toLowerCase();
498: if (ttype.equals("exe") || ttype.equals("library")
499: || ttype.equals("module") || ttype.equals("winexe")) {
500: targetType = ttype;
501: } else {
502: throw new BuildException(
503: "targetType "
504: + ttype
505: + " is not one of 'exe', 'module', 'winexe' or 'library'");
506: }
507: }
508:
509: /**
510: * Gets the TargetType attribute
511: *
512: *@return The TargetType value
513: */
514: public String getTargetType() {
515: return targetType;
516: }
517:
518: /**
519: * get the argument or null for no argument needed
520: *
521: *@return The TargetType Parameter to CSC
522: */
523: protected String getTargetTypeParameter() {
524: if (notEmpty(targetType)) {
525: return "/target:" + targetType;
526: } else {
527: return null;
528: }
529: }
530:
531: /**
532: * Set the filename of icon to include.
533: *
534: *@param fileName path to the file. Can be relative, absolute, whatever.
535: */
536: public void setWin32Icon(File fileName) {
537: win32icon = fileName;
538: }
539:
540: /**
541: * get the argument or null for no argument needed
542: *
543: *@return The Win32Icon Parameter to CSC
544: */
545: protected String getWin32IconParameter() {
546: if (win32icon != null) {
547: return "/win32icon:" + win32icon.toString();
548: } else {
549: return null;
550: }
551: }
552:
553: /**
554: * Sets the filename of a win32 resource (.RES) file to include.
555: * This is not a .NET resource, but what Windows is used to.
556: *
557: *@param fileName path to the file. Can be relative, absolute, whatever.
558: */
559: public void setWin32Res(File fileName) {
560: win32res = fileName;
561: }
562:
563: /**
564: * Gets the file of the win32 .res file to include.
565: * @return path to the file.
566: */
567: public File getWin32Res() {
568: return win32res;
569: }
570:
571: /**
572: * get the argument or null for no argument needed
573: *
574: *@return The Win32Res Parameter to CSC
575: */
576: protected String getWin32ResParameter() {
577: if (win32res != null) {
578: return "/win32res:" + win32res.toString();
579: } else {
580: return null;
581: }
582: }
583:
584: /**
585: * If true, require all compiler output to be in UTF8 format.
586: *
587: *@param enabled The new utf8Output value
588: */
589: public void setUtf8Output(boolean enabled) {
590: utf8output = enabled;
591: }
592:
593: /**
594: * Gets the utf8OutpuParameter attribute of the CSharp object
595: *
596: *@return The utf8OutpuParameter value
597: */
598: protected String getUtf8OutputParameter() {
599: return utf8output ? "/utf8output" : null;
600: }
601:
602: /**
603: * add a define to the list of definitions
604: * @param define the define value.
605: */
606: public void addDefine(DotnetDefine define) {
607: definitionList.addElement(define);
608: }
609:
610: /**
611: * get a list of definitions or null
612: * @return a string beginning /D: or null for no definitions
613: * @throws BuildException if there is an error.
614: */
615: protected String getDefinitionsParameter() throws BuildException {
616: StringBuffer defines = new StringBuffer();
617: Enumeration defEnum = definitionList.elements();
618: boolean firstDefinition = true;
619: while (defEnum.hasMoreElements()) {
620: //loop through all definitions
621: DotnetDefine define = (DotnetDefine) defEnum.nextElement();
622: if (define.isSet(this )) {
623: //add those that are set, and a delimiter
624: if (!firstDefinition) {
625: defines.append(getDefinitionsDelimiter());
626: }
627: defines.append(define.getValue(this ));
628: firstDefinition = false;
629: }
630: }
631: if (defines.length() == 0) {
632: return null;
633: } else {
634: return "/d:" + defines;
635: }
636: }
637:
638: /**
639: * Semicolon separated list of modules to refer to.
640: *
641: *@param params The new additionalModules value
642: */
643: public void setAdditionalModules(String params) {
644: additionalModules = params;
645: }
646:
647: /**
648: * get the argument or null for no argument needed
649: *
650: *@return The AdditionalModules Parameter to CSC
651: */
652: protected String getAdditionalModulesParameter() {
653: if (notEmpty(additionalModules)) {
654: return "/addmodule:" + additionalModules;
655: } else {
656: return null;
657: }
658: }
659:
660: /**
661: * get the argument or null for no argument needed
662: *
663: *@return The OutputFile Parameter to CSC
664: */
665: protected String getDestFileParameter() {
666: if (outputFile != null) {
667: return "/out:" + outputFile.toString();
668: } else {
669: return null;
670: }
671: }
672:
673: /**
674: * If true, fail on compilation errors.
675: *
676: *@param b The new FailOnError value
677: */
678: public void setFailOnError(boolean b) {
679: failOnError = b;
680: }
681:
682: /**
683: * query fail on error flag
684: *
685: *@return The FailFailOnError value
686: */
687: public boolean getFailOnError() {
688: return failOnError;
689: }
690:
691: /**
692: * link or embed a resource
693: * @param resource the resource to use.
694: */
695: public void addResource(DotnetResource resource) {
696: resources.add(resource);
697: }
698:
699: /**
700: * This method gets the name of the executable.
701: * @return the name of the executable
702: */
703: protected String getExecutable() {
704: return executable;
705: }
706:
707: /**
708: * set the name of the program, overriding the defaults.
709: * Can be used to set the full path to a program, or to switch
710: * to an alternate implementation of the command, such as the Mono or Rotor
711: * versions -provided they use the same command line arguments as the
712: * .NET framework edition
713: * @param executable the name of the program.
714: */
715: public void setExecutable(String executable) {
716: this .executable = executable;
717: }
718:
719: /**
720: * test for a string containing something useful
721: *
722: *@param s string in
723: *@return true if the argument is not null or empty
724: */
725: protected boolean notEmpty(String s) {
726: return s != null && s.length() != 0;
727: }
728:
729: /**
730: * validation code
731: * @throws BuildException if validation failed
732: */
733: protected void validate() throws BuildException {
734: if (outputFile != null && outputFile.isDirectory()) {
735: throw new BuildException("destFile cannot be a directory");
736: }
737: if (getExecutable() == null) {
738: throw new BuildException(
739: "There is no executable defined for this task");
740: }
741: }
742:
743: /**
744: * Get the pattern for files to compile.
745: * @return The compilation file pattern.
746: */
747: public String getFilePattern() {
748: return "**/*." + getFileExtension();
749: }
750:
751: /**
752: * getter for flag
753: * @return The flag indicating whether the compilation is using a response file.
754: */
755: public boolean isUseResponseFile() {
756: return useResponseFile;
757: }
758:
759: /**
760: * Flag to turn on response file use; default=false.
761: * When set the command params are saved to a file and
762: * this is passed in with @file. The task automatically switches
763: * to this mode with big commands; this option is here for
764: * testing and emergencies
765: * @param useResponseFile a <code>boolean</code> value.
766: */
767: public void setUseResponseFile(boolean useResponseFile) {
768: this .useResponseFile = useResponseFile;
769: }
770:
771: /**
772: * do the work by building the command line and then calling it
773: *
774: *@throws BuildException if validation or execution failed
775: */
776: public void execute() throws BuildException {
777: log(
778: "This task is deprecated and will be removed in a future version\n"
779: + "of Ant. It is now part of the .NET Antlib:\n"
780: + "http://ant.apache.org/antlibs/dotnet/index.html",
781: Project.MSG_WARN);
782:
783: validate();
784: NetCommand command = createNetCommand();
785: //set up response file options
786: command
787: .setAutomaticResponseFileThreshold(AUTOMATIC_RESPONSE_FILE_THRESHOLD);
788: command.setUseResponseFile(useResponseFile);
789: //fill in args
790: fillInSharedParameters(command);
791: addResources(command);
792: addCompilerSpecificOptions(command);
793: int referencesOutOfDate = addReferenceFilesets(command,
794: getOutputFileTimestamp());
795: //if the refs are out of date, force a build.
796: boolean forceBuild = referencesOutOfDate > 0;
797: addFilesAndExecute(command, forceBuild);
798:
799: }
800:
801: /**
802: * Get the delimiter that the compiler uses between references.
803: * For example, c# will return ";"; VB.NET will return ","
804: * @return The string delimiter for the reference string.
805: */
806: public abstract String getReferenceDelimiter();
807:
808: /**
809: * Get the extension of filenames to compile.
810: * @return The string extension of files to compile.
811: */
812: public abstract String getFileExtension();
813:
814: /**
815: * fill in the common information
816: * @param command the net command.
817: */
818: protected void fillInSharedParameters(NetCommand command) {
819: command.setFailOnError(getFailOnError());
820: //fill in args
821: command.addArgument("/nologo");
822: command.addArgument(getAdditionalModulesParameter());
823: command.addArgument(getDebugParameter());
824: command.addArgument(getDefinitionsParameter());
825: command.addArguments(getExtraOptionsParameters());
826: command.addArgument(getMainClassParameter());
827: command.addArgument(getOptimizeParameter());
828: command.addArgument(getDestFileParameter());
829: command.addArgument(getReferencesParameter());
830: command.addArgument(getTargetTypeParameter());
831: command.addArgument(getUtf8OutputParameter());
832: command.addArgument(getWin32IconParameter());
833: command.addArgument(getWin32ResParameter());
834: }
835:
836: /**
837: * for every resource declared, we get the (language specific)
838: * resource setting
839: * @param command the net command.
840: */
841: protected void addResources(NetCommand command) {
842: Enumeration e = resources.elements();
843: while (e.hasMoreElements()) {
844: DotnetResource resource = (DotnetResource) e.nextElement();
845: createResourceParameter(command, resource);
846: }
847: }
848:
849: /**
850: * Build a C# style parameter.
851: * @param command the command.
852: * @param resource the resource.
853: */
854: protected abstract void createResourceParameter(NetCommand command,
855: DotnetResource resource);
856:
857: /**
858: * run through the list of reference files and add them to the command
859: * @param command the command to use.
860: * @param outputTimestamp timestamp to compare against
861: * @return number of files out of date
862: */
863:
864: protected int addReferenceFilesets(NetCommand command,
865: long outputTimestamp) {
866: int filesOutOfDate = 0;
867: Hashtable filesToBuild = new Hashtable();
868: for (int i = 0; i < referenceFilesets.size(); i++) {
869: FileSet fs = (FileSet) referenceFilesets.elementAt(i);
870: filesOutOfDate += command.scanOneFileset(fs
871: .getDirectoryScanner(getProject()), filesToBuild,
872: outputTimestamp);
873: }
874: //bail out early if there were no files
875: if (filesToBuild.size() == 0) {
876: return 0;
877: }
878: //now scan the hashtable and add the files
879: Enumeration files = filesToBuild.elements();
880: while (files.hasMoreElements()) {
881: File file = (File) files.nextElement();
882: if (isFileManagedBinary(file)) {
883: if (isWindows) {
884: command.addArgument('"' + REFERENCE_OPTION
885: + file.toString() + '"');
886: } else {
887: command.addArgument(REFERENCE_OPTION
888: + file.toString());
889: }
890: } else {
891: log("ignoring " + file
892: + " as it is not a managed executable",
893: Project.MSG_VERBOSE);
894: }
895:
896: }
897:
898: return filesOutOfDate;
899: }
900:
901: /**
902: * create our helper command
903: * @return a command prefilled with the exe name and task name
904: */
905: protected NetCommand createNetCommand() {
906: NetCommand command = new NetCommand(this , getTaskName(),
907: getExecutable());
908: return command;
909: }
910:
911: /**
912: * add any compiler specifics
913: * @param command the command to use.
914: */
915: protected abstract void addCompilerSpecificOptions(
916: NetCommand command);
917:
918: /**
919: * override point for delimiting definitions.
920: * @return The definitions limiter, i.e., ";"
921: */
922: public String getDefinitionsDelimiter() {
923: return ";";
924: }
925:
926: /**
927: * test for a file being managed or not
928: * @param file the file to test.
929: * @return true if we think this is a managed executable, and thus OK
930: * for linking
931: * @todo look at the PE header of the exe and see if it is managed or not.
932: */
933: protected static boolean isFileManagedBinary(File file) {
934: String filename = file.toString().toLowerCase();
935: return filename.endsWith(".exe") || filename.endsWith(".dll")
936: || filename.endsWith(".netmodule");
937: }
938:
939: /**
940: * Target types to build.
941: * valid build types are exe|library|module|winexe
942: */
943: public static class TargetTypes extends EnumeratedAttribute {
944: /** {@inheritDoc}. */
945: public String[] getValues() {
946: return new String[] { "exe", "library", "module", "winexe" };
947: }
948: }
949:
950: }
|