1. /*
  2. * Copyright 2001-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. /*
  18. * build notes
  19. * -The reference CD to listen to while editing this file is
  20. * nap:Cream+Live+2001+CD+2
  21. */
  22. // place in the optional ant tasks package
  23. // but in its own dotnet group
  24. package org.apache.tools.ant.taskdefs.optional.dotnet;
  25. // imports
  26. import java.io.File;
  27. import java.util.Vector;
  28. import java.util.Enumeration;
  29. import java.util.Hashtable;
  30. import org.apache.tools.ant.BuildException;
  31. import org.apache.tools.ant.Project;
  32. import org.apache.tools.ant.types.Path;
  33. import org.apache.tools.ant.types.FileSet;
  34. import org.apache.tools.ant.types.EnumeratedAttribute;
  35. /**
  36. * Abstract superclass for dotnet compiler tasks.
  37. *
  38. * History
  39. * <table>
  40. * <tr>
  41. * <td>
  42. * 0.1
  43. * </td>
  44. * <td>
  45. * First creation
  46. * </td>
  47. * <td>
  48. * Most of the code here was copied verbatim from v0.3 of
  49. * Steve Loughran's CSharp optional task. Abstracted functionality
  50. * to allow subclassing of other dotnet compiler types.
  51. * </td>
  52. * </tr>
  53. *
  54. * </table>
  55. *
  56. *
  57. * @version 0.1
  58. */
  59. public abstract class DotnetCompile
  60. extends DotnetBaseMatchingTask {
  61. /**
  62. * list of reference classes. (pretty much a classpath equivalent)
  63. */
  64. private String references;
  65. /**
  66. * flag to enable automatic reference inclusion
  67. */
  68. private boolean includeDefaultReferences = true;
  69. /**
  70. * icon for incorporation into apps
  71. */
  72. private File win32icon;
  73. /**
  74. * icon for incorporation into apps
  75. */
  76. private File win32res;
  77. /**
  78. * flag to control action on execution trouble
  79. */
  80. private boolean failOnError;
  81. /**
  82. * using the path approach didn't work as it could not handle the implicit
  83. * execution path. Perhaps that could be extracted from the runtime and
  84. * then the path approach would be viable
  85. */
  86. private Path referenceFiles;
  87. /**
  88. * optimise flag
  89. */
  90. private boolean optimize;
  91. /**
  92. * a list of definitions to support;
  93. */
  94. protected Vector definitionList = new Vector();
  95. /**
  96. * our resources
  97. */
  98. protected Vector resources = new Vector();
  99. /**
  100. * executable
  101. */
  102. protected String executable;
  103. protected static final String REFERENCE_OPTION = "/reference:";
  104. /**
  105. * debug flag. Controls generation of debug information.
  106. */
  107. protected boolean debug;
  108. /**
  109. * warning level: 0-4, with 4 being most verbose
  110. */
  111. private int warnLevel;
  112. /**
  113. * main class (or null for automatic choice)
  114. */
  115. protected String mainClass;
  116. /**
  117. * any extra command options?
  118. */
  119. protected String extraOptions;
  120. /**
  121. * type of target. Should be one of exe|library|module|winexe|(null)
  122. * default is exe; the actual value (if not null) is fed to the command
  123. * line. <br>
  124. * See /target
  125. */
  126. protected String targetType;
  127. /**
  128. * utf out flag
  129. */
  130. protected boolean utf8output = false;
  131. /**
  132. * list of extra modules to refer to
  133. */
  134. protected String additionalModules;
  135. /**
  136. * filesets of references
  137. */
  138. protected Vector referenceFilesets = new Vector();
  139. /**
  140. * flag to set to to use @file based command cache
  141. */
  142. private boolean useResponseFile = false;
  143. private static final int AUTOMATIC_RESPONSE_FILE_THRESHOLD = 64;
  144. /**
  145. * constructor inits everything and set up the search pattern
  146. */
  147. public DotnetCompile() {
  148. clear();
  149. setIncludes(getFilePattern());
  150. }
  151. /**
  152. * reset all contents.
  153. */
  154. public void clear() {
  155. targetType = null;
  156. win32icon = null;
  157. srcDir = null;
  158. mainClass = null;
  159. warnLevel = 3;
  160. optimize = false;
  161. debug = true;
  162. references = null;
  163. failOnError = true;
  164. additionalModules = null;
  165. includeDefaultReferences = true;
  166. extraOptions = null;
  167. }
  168. /**
  169. * Semicolon separated list of DLLs to refer to.
  170. *
  171. *@param s The new References value
  172. */
  173. public void setReferences(String s) {
  174. references = s;
  175. }
  176. /**
  177. * get the reference string or null for no argument needed
  178. *
  179. *@return The References Parameter to CSC
  180. */
  181. protected String getReferencesParameter() {
  182. //bail on no references
  183. if (notEmpty(references)) {
  184. return REFERENCE_OPTION + references;
  185. } else {
  186. return null;
  187. }
  188. }
  189. /**
  190. * Path of references to include.
  191. * Wildcards should work.
  192. *
  193. *@param path another path to append
  194. */
  195. public void setReferenceFiles(Path path) {
  196. //demand create pathlist
  197. if (referenceFiles == null) {
  198. referenceFiles = new Path(this.getProject());
  199. }
  200. referenceFiles.append(path);
  201. }
  202. /**
  203. * add a new reference fileset to the compilation
  204. * @param reference
  205. */
  206. public void addReference(FileSet reference) {
  207. referenceFilesets.add(reference);
  208. }
  209. /**
  210. * turn the path list into a list of files and a /references argument
  211. *
  212. *@return null or a string of references.
  213. */
  214. protected String getReferenceFilesParameter() {
  215. //bail on no references
  216. if (references == null) {
  217. return null;
  218. }
  219. //iterate through the ref list & generate an entry for each
  220. //or just rely on the fact that the toString operator does this, but
  221. //noting that the separator is ';' on windows, ':' on unix
  222. String refpath = references.toString();
  223. //bail on no references listed
  224. if (refpath.length() == 0) {
  225. return null;
  226. }
  227. StringBuffer s = new StringBuffer(REFERENCE_OPTION);
  228. s.append(refpath);
  229. return new String(s);
  230. }
  231. /**
  232. * If true, automatically includes the common assemblies
  233. * in dotnet, and tells the compiler to link in mscore.dll.
  234. *
  235. * set the automatic reference inclusion flag on or off this flag controls
  236. * the /nostdlib option in CSC
  237. *
  238. *@param f on/off flag
  239. */
  240. public void setIncludeDefaultReferences(boolean f) {
  241. includeDefaultReferences = f;
  242. }
  243. /**
  244. * query automatic reference inclusion flag
  245. *
  246. *@return true if flag is turned on
  247. */
  248. public boolean getIncludeDefaultReferences() {
  249. return includeDefaultReferences;
  250. }
  251. /**
  252. * get the include default references flag or null for no argument needed
  253. *
  254. *@return The Parameter to CSC
  255. */
  256. protected String getIncludeDefaultReferencesParameter() {
  257. return "/nostdlib" + (includeDefaultReferences ? "-" : "+");
  258. }
  259. /**
  260. * If true, enables optimization flag.
  261. *
  262. *@param f on/off flag
  263. */
  264. public void setOptimize(boolean f) {
  265. optimize = f;
  266. }
  267. /**
  268. * query the optimise flag
  269. *
  270. *@return true if optimise is turned on
  271. */
  272. public boolean getOptimize() {
  273. return optimize;
  274. }
  275. /**
  276. * get the optimise flag or null for no argument needed
  277. *
  278. *@return The Optimize Parameter to CSC
  279. */
  280. protected String getOptimizeParameter() {
  281. return "/optimize" + (optimize ? "+" : "-");
  282. }
  283. /**
  284. * set the debug flag on or off.
  285. *
  286. *@param f on/off flag
  287. */
  288. public void setDebug(boolean f) {
  289. debug = f;
  290. }
  291. /**
  292. * query the debug flag
  293. *
  294. *@return true if debug is turned on
  295. */
  296. public boolean getDebug() {
  297. return debug;
  298. }
  299. /**
  300. * get the debug switch argument
  301. *
  302. *@return The Debug Parameter to CSC
  303. */
  304. protected String getDebugParameter() {
  305. return "/debug" + (debug ? "+" : "-");
  306. }
  307. /**
  308. * Level of warning currently between 1 and 4
  309. * with 4 being the strictest.
  310. *
  311. *@param warnLevel warn level -see .net docs for valid range (probably
  312. * 0-4)
  313. */
  314. public void setWarnLevel(int warnLevel) {
  315. this.warnLevel = warnLevel;
  316. }
  317. /**
  318. * query warn level
  319. *
  320. *@return current value
  321. */
  322. public int getWarnLevel() {
  323. return warnLevel;
  324. }
  325. /**
  326. * get the warn level switch
  327. *
  328. *@return The WarnLevel Parameter to CSC
  329. */
  330. protected String getWarnLevelParameter() {
  331. return "/warn:" + warnLevel;
  332. }
  333. /**
  334. * Sets the name of main class for executables.
  335. *
  336. *@param mainClass The new MainClass value
  337. */
  338. public void setMainClass(String mainClass) {
  339. this.mainClass = mainClass;
  340. }
  341. /**
  342. * Gets the MainClass attribute
  343. *
  344. *@return The MainClass value
  345. */
  346. public String getMainClass() {
  347. return this.mainClass;
  348. }
  349. /**
  350. * get the /main argument or null for no argument needed
  351. *
  352. *@return The MainClass Parameter to CSC
  353. */
  354. protected String getMainClassParameter() {
  355. if (mainClass != null && mainClass.length() != 0) {
  356. return "/main:" + mainClass;
  357. } else {
  358. return null;
  359. }
  360. }
  361. /**
  362. * Any extra options which are not explicitly supported
  363. * by this task.
  364. *
  365. *@param extraOptions The new ExtraOptions value
  366. */
  367. public void setExtraOptions(String extraOptions) {
  368. this.extraOptions = extraOptions;
  369. }
  370. /**
  371. * Gets the ExtraOptions attribute
  372. *
  373. *@return The ExtraOptions value
  374. */
  375. public String getExtraOptions() {
  376. return this.extraOptions;
  377. }
  378. /**
  379. * get any extra options or null for no argument needed
  380. *
  381. *@return The ExtraOptions Parameter to CSC
  382. */
  383. protected String getExtraOptionsParameter() {
  384. if (extraOptions != null && extraOptions.length() != 0) {
  385. return extraOptions;
  386. } else {
  387. return null;
  388. }
  389. }
  390. /**
  391. * Set the destination directory of files to be compiled.
  392. *
  393. *@param dirName The new DestDir value
  394. */
  395. public void setDestDir(File dirName) {
  396. log("DestDir currently unused", Project.MSG_WARN);
  397. }
  398. /**
  399. * set the target type to one of exe|library|module|winexe
  400. * @param targetType
  401. */
  402. public void setTargetType(TargetTypes targetType) {
  403. this.targetType = targetType.getValue();
  404. }
  405. /**
  406. * Set the type of target.
  407. *
  408. *@param ttype The new TargetType value
  409. *@exception BuildException if target is not one of
  410. * exe|library|module|winexe
  411. */
  412. public void setTargetType(String ttype)
  413. throws BuildException {
  414. ttype = ttype.toLowerCase();
  415. if (ttype.equals("exe") || ttype.equals("library")
  416. || ttype.equals("module") || ttype.equals("winexe")) {
  417. targetType = ttype;
  418. } else {
  419. throw new BuildException("targetType " + ttype
  420. + " is not one of 'exe', 'module', 'winexe' or 'library'");
  421. }
  422. }
  423. /**
  424. * Gets the TargetType attribute
  425. *
  426. *@return The TargetType value
  427. */
  428. public String getTargetType() {
  429. return targetType;
  430. }
  431. /**
  432. * get the argument or null for no argument needed
  433. *
  434. *@return The TargetType Parameter to CSC
  435. */
  436. protected String getTargetTypeParameter() {
  437. if (notEmpty(targetType)) {
  438. return "/target:" + targetType;
  439. } else {
  440. return null;
  441. }
  442. }
  443. /**
  444. * Set the filename of icon to include.
  445. *
  446. *@param fileName path to the file. Can be relative, absolute, whatever.
  447. */
  448. public void setWin32Icon(File fileName) {
  449. win32icon = fileName;
  450. }
  451. /**
  452. * get the argument or null for no argument needed
  453. *
  454. *@return The Win32Icon Parameter to CSC
  455. */
  456. protected String getWin32IconParameter() {
  457. if (win32icon != null) {
  458. return "/win32icon:" + win32icon.toString();
  459. } else {
  460. return null;
  461. }
  462. }
  463. /**
  464. * Sets the filename of a win32 resource (.RES) file to include.
  465. * This is not a .NET resource, but what Windows is used to.
  466. *
  467. *@param fileName path to the file. Can be relative, absolute, whatever.
  468. */
  469. public void setWin32Res(File fileName) {
  470. win32res = fileName;
  471. }
  472. /**
  473. * Gets the file of the win32 .res file to include.
  474. * @return path to the file.
  475. */
  476. public File getWin32Res() {
  477. return win32res;
  478. }
  479. /**
  480. * get the argument or null for no argument needed
  481. *
  482. *@return The Win32Res Parameter to CSC
  483. */
  484. protected String getWin32ResParameter() {
  485. if (win32res != null) {
  486. return "/win32res:" + win32res.toString();
  487. } else {
  488. return null;
  489. }
  490. }
  491. /**
  492. * If true, require all compiler output to be in UTF8 format.
  493. *
  494. *@param enabled The new utf8Output value
  495. */
  496. public void setUtf8Output(boolean enabled) {
  497. utf8output = enabled;
  498. }
  499. /**
  500. * Gets the utf8OutpuParameter attribute of the CSharp object
  501. *
  502. *@return The utf8OutpuParameter value
  503. */
  504. protected String getUtf8OutputParameter() {
  505. return utf8output ? "/utf8output" : null;
  506. }
  507. /**
  508. * add a define to the list of definitions
  509. * @param define
  510. */
  511. public void addDefine(DotnetDefine define) {
  512. definitionList.addElement(define);
  513. }
  514. /**
  515. * get a list of definitions or null
  516. * @return a string beginning /D: or null for no definitions
  517. */
  518. protected String getDefinitionsParameter() throws BuildException {
  519. StringBuffer defines = new StringBuffer();
  520. Enumeration defEnum = definitionList.elements();
  521. boolean firstDefinition = true;
  522. while (defEnum.hasMoreElements()) {
  523. //loop through all definitions
  524. DotnetDefine define = (DotnetDefine) defEnum.nextElement();
  525. if (define.isSet(this)) {
  526. //add those that are set, and a delimiter
  527. if (!firstDefinition) {
  528. defines.append(getDefinitionsDelimiter());
  529. }
  530. defines.append(define.getValue(this));
  531. firstDefinition = false;
  532. }
  533. }
  534. if (defines.length() == 0) {
  535. return null;
  536. } else {
  537. return "/d:" + defines;
  538. }
  539. }
  540. /**
  541. * Semicolon separated list of modules to refer to.
  542. *
  543. *@param params The new additionalModules value
  544. */
  545. public void setAdditionalModules(String params) {
  546. additionalModules = params;
  547. }
  548. /**
  549. * get the argument or null for no argument needed
  550. *
  551. *@return The AdditionalModules Parameter to CSC
  552. */
  553. protected String getAdditionalModulesParameter() {
  554. if (notEmpty(additionalModules)) {
  555. return "/addmodule:" + additionalModules;
  556. } else {
  557. return null;
  558. }
  559. }
  560. /**
  561. * get the argument or null for no argument needed
  562. *
  563. *@return The OutputFile Parameter to CSC
  564. */
  565. protected String getDestFileParameter() {
  566. if (outputFile != null) {
  567. return "/out:" + outputFile.toString();
  568. } else {
  569. return null;
  570. }
  571. }
  572. /**
  573. * If true, fail on compilation errors.
  574. *
  575. *@param b The new FailOnError value
  576. */
  577. public void setFailOnError(boolean b) {
  578. failOnError = b;
  579. }
  580. /**
  581. * query fail on error flag
  582. *
  583. *@return The FailFailOnError value
  584. */
  585. public boolean getFailOnError() {
  586. return failOnError;
  587. }
  588. /**
  589. * link or embed a resource
  590. * @param resource
  591. */
  592. public void addResource(DotnetResource resource) {
  593. resources.add(resource);
  594. }
  595. /**
  596. * This method gets the name of the executable.
  597. * @return the name of the executable
  598. */
  599. protected String getExecutable() {
  600. return executable;
  601. }
  602. /**
  603. * set the name of the program, overriding the defaults.
  604. * Can be used to set the full path to a program, or to switch
  605. * to an alternate implementation of the command, such as the Mono or Rotor
  606. * versions -provided they use the same command line arguments as the
  607. * .NET framework edition
  608. * @param executable
  609. */
  610. public void setExecutable(String executable) {
  611. this.executable = executable;
  612. }
  613. /**
  614. * test for a string containing something useful
  615. *
  616. *@param s string in
  617. *@return true if the argument is not null or empty
  618. */
  619. protected boolean notEmpty(String s) {
  620. return s != null && s.length() != 0;
  621. }
  622. /**
  623. * validation code
  624. * @throws BuildException if validation failed
  625. */
  626. protected void validate()
  627. throws BuildException {
  628. if (outputFile != null && outputFile.isDirectory()) {
  629. throw new BuildException("destFile cannot be a directory");
  630. }
  631. if (getExecutable() == null) {
  632. throw new BuildException("There is no executable defined for this task");
  633. }
  634. }
  635. /**
  636. * Get the pattern for files to compile.
  637. * @return The compilation file pattern.
  638. */
  639. public String getFilePattern() {
  640. return "**/*." + getFileExtension();
  641. }
  642. /**
  643. * getter for flag
  644. * @return The flag indicating whether the compilation is using a response file.
  645. */
  646. public boolean isUseResponseFile() {
  647. return useResponseFile;
  648. }
  649. /**
  650. * Flag to turn on response file use; default=false.
  651. * When set the command params are saved to a file and
  652. * this is passed in with @file. The task automatically switches
  653. * to this mode with big commands; this option is here for
  654. * testing and emergencies
  655. * @param useResponseFile
  656. */
  657. public void setUseResponseFile(boolean useResponseFile) {
  658. this.useResponseFile = useResponseFile;
  659. }
  660. /**
  661. * do the work by building the command line and then calling it
  662. *
  663. *@throws BuildException if validation or execution failed
  664. */
  665. public void execute()
  666. throws BuildException {
  667. validate();
  668. NetCommand command = createNetCommand();
  669. //set up response file options
  670. command.setAutomaticResponseFileThreshold(AUTOMATIC_RESPONSE_FILE_THRESHOLD);
  671. command.setUseResponseFile(useResponseFile);
  672. //fill in args
  673. fillInSharedParameters(command);
  674. addResources(command);
  675. addCompilerSpecificOptions(command);
  676. int referencesOutOfDate
  677. = addReferenceFilesets(command, getOutputFileTimestamp());
  678. //if the refs are out of date, force a build.
  679. boolean forceBuild = referencesOutOfDate > 0;
  680. addFilesAndExecute(command, forceBuild);
  681. }
  682. /**
  683. * Get the delimiter that the compiler uses between references.
  684. * For example, c# will return ";"; VB.NET will return ","
  685. * @return The string delimiter for the reference string.
  686. */
  687. public abstract String getReferenceDelimiter();
  688. /**
  689. * Get the extension of filenames to compile.
  690. * @return The string extension of files to compile.
  691. */
  692. public abstract String getFileExtension();
  693. /**
  694. * fill in the common information
  695. * @param command
  696. */
  697. protected void fillInSharedParameters(NetCommand command) {
  698. command.setFailOnError(getFailOnError());
  699. //fill in args
  700. command.addArgument("/nologo");
  701. command.addArgument(getAdditionalModulesParameter());
  702. command.addArgument(getDebugParameter());
  703. command.addArgument(getDefinitionsParameter());
  704. command.addArgument(getExtraOptionsParameter());
  705. command.addArgument(getMainClassParameter());
  706. command.addArgument(getOptimizeParameter());
  707. command.addArgument(getDestFileParameter());
  708. command.addArgument(getReferencesParameter());
  709. command.addArgument(getTargetTypeParameter());
  710. command.addArgument(getUtf8OutputParameter());
  711. command.addArgument(getWin32IconParameter());
  712. command.addArgument(getWin32ResParameter());
  713. }
  714. /**
  715. * for every resource declared, we get the (language specific)
  716. * resource setting
  717. */
  718. protected void addResources(NetCommand command) {
  719. Enumeration e = resources.elements();
  720. while (e.hasMoreElements()) {
  721. DotnetResource resource = (DotnetResource) e.nextElement();
  722. command.addArgument(createResourceParameter(resource));
  723. }
  724. }
  725. /**
  726. * from a resource, get the
  727. * @param resource
  728. * @return a string containing the resource param, or a null string
  729. * to conditionally exclude a resource.
  730. */
  731. protected abstract String createResourceParameter(DotnetResource resource);
  732. /**
  733. * run through the list of reference files and add them to the command
  734. * @param outputTimestamp timestamp to compare against
  735. * @return number of files out of date
  736. */
  737. protected int addReferenceFilesets(NetCommand command, long outputTimestamp) {
  738. int filesOutOfDate = 0;
  739. Hashtable filesToBuild = new Hashtable();
  740. for (int i = 0; i < referenceFilesets.size(); i++) {
  741. FileSet fs = (FileSet) referenceFilesets.elementAt(i);
  742. filesOutOfDate += command.scanOneFileset(
  743. fs.getDirectoryScanner(getProject()),
  744. filesToBuild,
  745. outputTimestamp);
  746. }
  747. //bail out early if there were no files
  748. if (filesToBuild.size() == 0) {
  749. return 0;
  750. }
  751. StringBuffer referenceList = new StringBuffer(REFERENCE_OPTION);
  752. //now scan the hashtable and add the files
  753. Enumeration files = filesToBuild.elements();
  754. boolean firstEntry = true;
  755. while (files.hasMoreElements()) {
  756. File file = (File) files.nextElement();
  757. if (isFileManagedBinary(file)) {
  758. if (!firstEntry) {
  759. referenceList.append(getReferenceDelimiter());
  760. }
  761. referenceList.append(file.toString());
  762. firstEntry = false;
  763. } else {
  764. log("ignoring " + file + " as it is not a managed executable",
  765. Project.MSG_VERBOSE);
  766. }
  767. }
  768. //add it all to an argument
  769. command.addArgument(referenceList.toString());
  770. return filesOutOfDate;
  771. }
  772. /**
  773. * create our helper command
  774. * @return a command prefilled with the exe name and task name
  775. */
  776. protected NetCommand createNetCommand() {
  777. NetCommand command = new NetCommand(this, getTaskName(), getExecutable());
  778. return command;
  779. }
  780. /**
  781. * add any compiler specifics
  782. * @param command
  783. */
  784. protected abstract void addCompilerSpecificOptions(NetCommand command);
  785. /**
  786. * override point for delimiting definitions.
  787. * @return The definitions limiter, i.e., ";"
  788. */
  789. public String getDefinitionsDelimiter() {
  790. return ";";
  791. }
  792. /**
  793. * test for a file being managed or not
  794. * @return true if we think this is a managed executable, and thus OK
  795. * for linking
  796. * @todo look at the PE header of the exe and see if it is managed or not.
  797. */
  798. protected static boolean isFileManagedBinary(File file) {
  799. String filename = file.toString().toLowerCase();
  800. return filename.endsWith(".exe") || filename.endsWith(".dll")
  801. || filename.endsWith(".netmodule");
  802. }
  803. /**
  804. * Target types to build.
  805. * valid build types are exe|library|module|winexe
  806. */
  807. public static class TargetTypes extends EnumeratedAttribute {
  808. public String[] getValues() {
  809. return new String[] {
  810. "exe",
  811. "library",
  812. "module",
  813. "winexe"
  814. };
  815. }
  816. }
  817. }