Kawac.java 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122
  1. // An Ant Task to invoke the kawa compiler.
  2. // Jamison Hope
  3. package gnu.kawa.ant;
  4. import java.io.File;
  5. import java.io.IOException;
  6. import java.util.Vector;
  7. import org.apache.tools.ant.BuildException;
  8. import org.apache.tools.ant.DirectoryScanner;
  9. import org.apache.tools.ant.MagicNames;
  10. import org.apache.tools.ant.Project;
  11. import org.apache.tools.ant.taskdefs.Execute;
  12. import org.apache.tools.ant.taskdefs.LogStreamHandler;
  13. import org.apache.tools.ant.taskdefs.MatchingTask;
  14. import org.apache.tools.ant.taskdefs.condition.Os;
  15. import org.apache.tools.ant.types.Commandline;
  16. import org.apache.tools.ant.types.DataType;
  17. import org.apache.tools.ant.types.FileList;
  18. import org.apache.tools.ant.types.FileSet;
  19. import org.apache.tools.ant.types.Path;
  20. import org.apache.tools.ant.types.PatternSet;
  21. import org.apache.tools.ant.types.Reference;
  22. import org.apache.tools.ant.types.selectors.AndSelector;
  23. import org.apache.tools.ant.types.selectors.ContainsRegexpSelector;
  24. import org.apache.tools.ant.types.selectors.ContainsSelector;
  25. import org.apache.tools.ant.types.selectors.DateSelector;
  26. import org.apache.tools.ant.types.selectors.DependSelector;
  27. import org.apache.tools.ant.types.selectors.DepthSelector;
  28. import org.apache.tools.ant.types.selectors.ExtendSelector;
  29. import org.apache.tools.ant.types.selectors.FilenameSelector;
  30. import org.apache.tools.ant.types.selectors.FileSelector;
  31. import org.apache.tools.ant.types.selectors.MajoritySelector;
  32. import org.apache.tools.ant.types.selectors.NoneSelector;
  33. import org.apache.tools.ant.types.selectors.NotSelector;
  34. import org.apache.tools.ant.types.selectors.OrSelector;
  35. import org.apache.tools.ant.types.selectors.PresentSelector;
  36. import org.apache.tools.ant.types.selectors.SelectSelector;
  37. import org.apache.tools.ant.types.selectors.SizeSelector;
  38. import org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector;
  39. import org.apache.tools.ant.util.ChainedMapper;
  40. import org.apache.tools.ant.util.CompositeMapper;
  41. import org.apache.tools.ant.util.FileNameMapper;
  42. import org.apache.tools.ant.util.GlobPatternMapper;
  43. import org.apache.tools.ant.util.JavaEnvUtils;
  44. import org.apache.tools.ant.util.SourceFileScanner;
  45. import org.apache.tools.ant.util.StringUtils;
  46. /**
  47. * Compiles Kawa source files. This task can take the following
  48. * arguments:
  49. * <ul>
  50. * <li>srcdir
  51. * <li>destdir
  52. * <li>classpath
  53. * <li>target
  54. * <li>failonerror
  55. * <li>prefix
  56. * <li>main
  57. * <li>fulltailcalls
  58. * <li>modulestatic
  59. * <li>warnundefinedvariable
  60. * <li>language
  61. * </ul>
  62. * If <b>srcdir</b> is set, then that directory is searched for source
  63. * files to compile (subject to any nested
  64. * includes/excludes). Otherwise, files included in nested filesets
  65. * and filelists will be compiled and written into <b>destdir</b>.
  66. * <p>
  67. * When this task executes, it will compile any listed source file
  68. * which is younger than its corresponding class file.
  69. * <p>
  70. * Superficially based upon the standard Ant Javac Task, but with
  71. * FileSet and FileList support as well.
  72. *
  73. * @author Jamison Hope
  74. */
  75. public class Kawac extends MatchingTask {
  76. private static final String FAIL_MSG
  77. = "Compile failed; see the compiler error output for details.";
  78. // generic compiler stuff
  79. private Path src; // location of source files
  80. private File destDir; // destination for class files
  81. private Path compileClasspath; // classpath
  82. private boolean listFiles = false; // list the files being compiled
  83. private boolean failOnError = true; // does what it says
  84. private String target; // target VM ("1.5", "1.6", etc)
  85. private boolean includeDestClasses = true; // include destDir in classpath
  86. // properties for success or failure
  87. private String updatedProperty; // property to be set on successful compile
  88. private String errorProperty; // property to be set on error
  89. // properties corresponding to various kawa.repl flags
  90. private String prefix; // Kawa -P argument
  91. private boolean main = false; // Kawa --main argument
  92. private boolean applet = false; // Kawa --applet argument
  93. private boolean servlet = false; // Kawa --servlet argument
  94. private boolean fullTailCalls = false; // Kawa --full-tailcalls argument
  95. private String moduleStatic; // Kawa --module-static, --module-static-run
  96. private boolean warnUndefinedVariable = false; // Kawa --warn-undefined-variable
  97. private boolean warnAsError = false; // Kawa --warn-as-error
  98. private String language; // Kawa language (--clisp, --scheme, etc)
  99. private File[] compileList = new File[0]; // the Files to compile
  100. private boolean taskSuccess = true; // will be set false on error
  101. private boolean usedMatchingTask = false; // a la org.apache.tools.ant.taskdefs.Delete
  102. private Vector<DataType> filesets = new Vector<DataType>();
  103. private Vector<Commandline.Argument> otherArgs =
  104. new Vector<Commandline.Argument>();
  105. /**
  106. * Kawac task for compilation of Kawa files.
  107. */
  108. public Kawac() {
  109. }
  110. /**
  111. * Set the source directories to find the source Kawa files.
  112. * @param srcDir the source directories as a path
  113. */
  114. public void setSrcdir(Path srcDir) {
  115. usedMatchingTask = true;
  116. if (src == null)
  117. src = srcDir;
  118. else
  119. src.append(srcDir);
  120. }
  121. /**
  122. * Gets the source dirs to find the source Kawa files.
  123. * @return the source directories as a path
  124. */
  125. public Path getSrcdir() {
  126. return src;
  127. }
  128. /**
  129. * Set the destination directory into which the Kawa source files
  130. * should be compiled.
  131. * @param destDir the destination directory
  132. */
  133. public void setDestdir(File destDir) {
  134. this.destDir = destDir;
  135. }
  136. /**
  137. * Gets the destination directory into which the Kawa source files
  138. * should be compiled.
  139. * @return the destination directory
  140. */
  141. public File getDestdir() {
  142. return destDir;
  143. }
  144. /**
  145. * Set the classpath to be used for this compilation.
  146. * @param classpath an Ant Path object containing the compilation
  147. * classpath.
  148. */
  149. public void setClasspath(Path classpath) {
  150. if (compileClasspath == null)
  151. compileClasspath = classpath;
  152. else
  153. compileClasspath.append(classpath);
  154. }
  155. /**
  156. * Gets the classpath to be used for this compilation.
  157. * @return the class path
  158. */
  159. public Path getClasspath() {
  160. return compileClasspath;
  161. }
  162. /**
  163. * Adds a path to the classpath.
  164. * @return a class path to be configured
  165. */
  166. public Path createClasspath() {
  167. if (compileClasspath == null)
  168. compileClasspath = new Path(getProject());
  169. return compileClasspath.createPath();
  170. }
  171. /**
  172. * Adds a reference to a classpath defined elsewhere.
  173. * @param r a reference to a classpath
  174. */
  175. public void setClasspathRef(Reference r) {
  176. createClasspath().setRefid(r);
  177. }
  178. /**
  179. * If true, list the source files being handed off to the compiler.
  180. * @param list if true list the source files
  181. */
  182. public void setListfiles(boolean list) {
  183. listFiles = list;
  184. }
  185. /**
  186. * Get the listfiles flag.
  187. * @return the listfiles flag
  188. */
  189. public boolean getListfiles() {
  190. return listFiles;
  191. }
  192. /**
  193. * Indicates whether the build will continue even if there are
  194. * compilation errors; defaults to true.
  195. * @param fail if true halt the build on failure
  196. */
  197. public void setFailonerror(boolean fail) {
  198. failOnError = fail;
  199. }
  200. /**
  201. * Gets the failonerror flag.
  202. * @return the failonerror flag
  203. */
  204. public boolean getFailonerror() {
  205. return failOnError;
  206. }
  207. /**
  208. * Sets the target VM that the classes will be compiled for. Valid
  209. * values are "7", "6", "1.6", "5", "1.5", "1.4", "1.3", "1.2", and
  210. * "1.1".
  211. * @param target the target VM
  212. */
  213. public void setTarget(String target) {
  214. this.target = target;
  215. }
  216. /**
  217. * Gets the target VM that the classes will be compiled for.
  218. * @return the target VM
  219. */
  220. public String getTarget() {
  221. return target != null
  222. ? target : getProject().getProperty(MagicNames.BUILD_JAVAC_TARGET);
  223. }
  224. /**
  225. * This property controls whether to include the destination classes
  226. * directory in the classpath given to the compiler. The default
  227. * value is true.
  228. * @param includeDestClasses the value to use
  229. */
  230. public void setIncludeDestClasses(boolean includeDestClasses) {
  231. this.includeDestClasses = includeDestClasses;
  232. }
  233. /**
  234. * Get the value of the includeDestClasses property.
  235. * @return the value
  236. */
  237. public boolean isIncludeDestClasses() {
  238. return includeDestClasses;
  239. }
  240. /**
  241. * The property to set on compilation success. This property will
  242. * not be set if the compilation fails, or if there are no files to
  243. * compile.
  244. * @param updatedProperty the property name to use
  245. */
  246. public void setUpdatedProperty(String updatedProperty) {
  247. this.updatedProperty = updatedProperty;
  248. }
  249. /**
  250. * The property to set on compilation failure. This property will be
  251. * set if the compilation fails.
  252. * @param errorProperty the property name to use
  253. */
  254. public void setErrorProperty(String errorProperty) {
  255. this.errorProperty = errorProperty;
  256. }
  257. /**
  258. * Sets the prefix.
  259. * @param prefix the prefix to use
  260. */
  261. public void setPrefix(String prefix) {
  262. this.prefix = prefix;
  263. }
  264. /**
  265. * Gets the prefix
  266. * @return the prefix
  267. */
  268. public String getPrefix() {
  269. return prefix;
  270. }
  271. /**
  272. * If true, then Kawa will create a static main method.
  273. * @param main true to pass --main to Kawa
  274. */
  275. public void setMain(boolean main) {
  276. this.main = main;
  277. }
  278. /**
  279. * Gets the value of the "main" property.
  280. * @return the value
  281. */
  282. public boolean getMain() {
  283. return main;
  284. }
  285. /**
  286. * If true, then Kawa will generate an applet.
  287. * @param applet true to pass --applet to Kawa
  288. */
  289. public void setApplet(boolean applet) {
  290. this.applet = applet;
  291. }
  292. /**
  293. * Gets the value of the "applet" property.
  294. * @return the value
  295. */
  296. public boolean getApplet() {
  297. return applet;
  298. }
  299. /**
  300. * If true, then Kawa will generate a servlet.
  301. * @param servlet true to pass --applet to Kawa
  302. */
  303. public void setServlet(boolean servlet) {
  304. this.servlet = servlet;
  305. }
  306. /**
  307. * Gets the value of the "servlet" property.
  308. * @return the value
  309. */
  310. public boolean getServlet() {
  311. return servlet;
  312. }
  313. /**
  314. * If true, Kawa will use full tailcalls.
  315. * @param tailcalls true to use full-tailcalls
  316. */
  317. public void setFulltailcalls(boolean tailcalls) {
  318. fullTailCalls = tailcalls;
  319. }
  320. /**
  321. * Gets the value of the fulltailcalls property.
  322. * @return the value
  323. */
  324. public boolean getFulltailcalls() {
  325. return fullTailCalls;
  326. }
  327. /**
  328. * Passes the --module-static or --module-static-run flag.
  329. * @param moduleStatic the flag
  330. */
  331. public void setModulestatic(String moduleStatic) {
  332. this.moduleStatic = moduleStatic;
  333. }
  334. /**
  335. * Gets the value of the modulestatic property.
  336. * @return the value
  337. */
  338. public String getModulestatic() {
  339. return moduleStatic;
  340. }
  341. /**
  342. * Passes the --warn-undefined-variable flag if true.
  343. * @param undefined true for --warn-undefined-variable
  344. */
  345. public void setWarnundefinedvariable(boolean undefined) {
  346. warnUndefinedVariable = undefined;
  347. }
  348. /**
  349. * Gets the value of the warnundefinedvariable property.
  350. * @return the value
  351. */
  352. public boolean getWarnundefinedvariable() {
  353. return warnUndefinedVariable;
  354. }
  355. /**
  356. * Passes the --warn-as-error flag if true.
  357. * @param warnaserror true for --warn-as-error
  358. */
  359. public void setWarnaserror(boolean warnaserror) {
  360. warnAsError = warnaserror;
  361. }
  362. /**
  363. * Gets the value of the warnaserror property.
  364. * @return the value
  365. */
  366. public boolean getWarnaserror() {
  367. return warnAsError;
  368. }
  369. /**
  370. * Sets the language to use: "scheme", "commonlisp", or "elisp".
  371. * @param lang the language to use
  372. */
  373. public void setLanguage(String lang) {
  374. language = lang;
  375. }
  376. /**
  377. * Gets the language.
  378. * @return the value
  379. */
  380. public String getLanguage() {
  381. return language;
  382. }
  383. /**
  384. * Adds a set of files to be compiled.
  385. * @param set the set of files to be compiled
  386. */
  387. public void addFileset(FileSet set) {
  388. filesets.addElement(set);
  389. }
  390. /**
  391. * Adds a list of files to be compiled.
  392. * @param list the list of files to be compiled
  393. */
  394. public void addFilelist(FileList list) {
  395. filesets.addElement(list);
  396. }
  397. /**
  398. * Adds a nested {@code <arg>}.
  399. * @param arg the argument to add to the kawa command line
  400. */
  401. public void addArg(Commandline.Argument arg) {
  402. otherArgs.add(arg);
  403. }
  404. //////////////////////////////////
  405. ///// MatchingTask overrides /////
  406. //////////////////////////////////
  407. @Override public PatternSet.NameEntry createInclude() {
  408. usedMatchingTask = true;
  409. return super.createInclude();
  410. }
  411. @Override public PatternSet.NameEntry createIncludesFile() {
  412. usedMatchingTask = true;
  413. return super.createIncludesFile();
  414. }
  415. @Override public PatternSet.NameEntry createExclude() {
  416. usedMatchingTask = true;
  417. return super.createExclude();
  418. }
  419. @Override public PatternSet.NameEntry createExcludesFile() {
  420. usedMatchingTask = true;
  421. return super.createExcludesFile();
  422. }
  423. @Override public PatternSet createPatternSet() {
  424. usedMatchingTask = true;
  425. return super.createPatternSet();
  426. }
  427. @Override public void setIncludes(String includes) {
  428. usedMatchingTask = true;
  429. super.setIncludes(includes);
  430. }
  431. @Override public void setExcludes(String excludes) {
  432. usedMatchingTask = true;
  433. super.setExcludes(excludes);
  434. }
  435. @Override public void setDefaultexcludes(boolean useDefaultExcludes) {
  436. usedMatchingTask = true;
  437. super.setDefaultexcludes(useDefaultExcludes);
  438. }
  439. @Override public void setIncludesfile(File includesfile) {
  440. usedMatchingTask = true;
  441. super.setIncludesfile(includesfile);
  442. }
  443. @Override public void setExcludesfile(File excludesfile) {
  444. usedMatchingTask = true;
  445. super.setExcludesfile(excludesfile);
  446. }
  447. @Override public void setCaseSensitive(boolean isCaseSensitive) {
  448. usedMatchingTask = true;
  449. super.setCaseSensitive(isCaseSensitive);
  450. }
  451. @Override public void setFollowSymlinks(boolean followSymlinks) {
  452. usedMatchingTask = true;
  453. super.setFollowSymlinks(followSymlinks);
  454. }
  455. @Override public void addSelector(SelectSelector selector) {
  456. usedMatchingTask = true;
  457. super.addSelector(selector);
  458. }
  459. @Override public void addAnd(AndSelector selector) {
  460. usedMatchingTask = true;
  461. super.addAnd(selector);
  462. }
  463. @Override public void addOr(OrSelector selector) {
  464. usedMatchingTask = true;
  465. super.addOr(selector);
  466. }
  467. @Override public void addNot(NotSelector selector) {
  468. usedMatchingTask = true;
  469. super.addNot(selector);
  470. }
  471. @Override public void addNone(NoneSelector selector) {
  472. usedMatchingTask = true;
  473. super.addNone(selector);
  474. }
  475. @Override public void addMajority(MajoritySelector selector) {
  476. usedMatchingTask = true;
  477. super.addMajority(selector);
  478. }
  479. @Override public void addDate(DateSelector selector) {
  480. usedMatchingTask = true;
  481. super.addDate(selector);
  482. }
  483. @Override public void addSize(SizeSelector selector) {
  484. usedMatchingTask = true;
  485. super.addSize(selector);
  486. }
  487. @Override public void addFilename(FilenameSelector selector) {
  488. usedMatchingTask = true;
  489. super.addFilename(selector);
  490. }
  491. @Override public void addCustom(ExtendSelector selector) {
  492. usedMatchingTask = true;
  493. super.addCustom(selector);
  494. }
  495. @Override public void addContains(ContainsSelector selector) {
  496. usedMatchingTask = true;
  497. super.addContains(selector);
  498. }
  499. @Override public void addPresent(PresentSelector selector) {
  500. usedMatchingTask = true;
  501. super.addPresent(selector);
  502. }
  503. @Override public void addDepth(DepthSelector selector) {
  504. usedMatchingTask = true;
  505. super.addDepth(selector);
  506. }
  507. @Override public void addDepend(DependSelector selector) {
  508. usedMatchingTask = true;
  509. super.addDepend(selector);
  510. }
  511. @Override public void addContainsRegexp(ContainsRegexpSelector selector) {
  512. usedMatchingTask = true;
  513. super.addContainsRegexp(selector);
  514. }
  515. @Override public void addModified(ModifiedSelector selector) {
  516. usedMatchingTask = true;
  517. super.addModified(selector);
  518. }
  519. @Override public void add(FileSelector selector) {
  520. usedMatchingTask = true;
  521. super.add(selector);
  522. }
  523. /**
  524. * Get the result of the kawac task (success or failure).
  525. * @return true if compilation succeeded, or was not necessary,
  526. * false if the compilation failed.
  527. */
  528. public boolean getTaskSuccess() {
  529. return taskSuccess;
  530. }
  531. /**
  532. * Executes the task.
  533. * Throws BuildException if an error occurs
  534. */
  535. public void execute() throws BuildException {
  536. checkParameters();
  537. resetFileLists();
  538. // scan source directories and dest directory to build up compile
  539. // lists
  540. FileNameMapper mapper = getMapper();
  541. if (usedMatchingTask) { // scan srcdir
  542. String[] list = src.list();
  543. for (int i = 0; i < list.length; i++) {
  544. File srcDir = getProject().resolveFile(list[i]);
  545. if (!srcDir.exists()) {
  546. throw new BuildException("srcdir \"" + srcDir.getPath() +
  547. "\" does not exist!", getLocation());
  548. }
  549. DirectoryScanner ds = getDirectoryScanner(srcDir);
  550. String[] files = ds.getIncludedFiles();
  551. scanDir(srcDir, destDir != null ? destDir : srcDir, files,
  552. mapper);
  553. }
  554. }
  555. // now scan each fileset and filelist
  556. for (DataType dt : filesets) {
  557. if (dt instanceof FileSet) {
  558. FileSet set = (FileSet) dt;
  559. DirectoryScanner ds = set.getDirectoryScanner(getProject());
  560. String[] files = ds.getIncludedFiles();
  561. scanDir(set.getDir(), destDir, files, mapper);
  562. } else if (dt instanceof FileList) {
  563. FileList list = (FileList) dt;
  564. scanDir(list.getDir(getProject()), destDir,
  565. list.getFiles(getProject()), mapper);
  566. }
  567. }
  568. compile();
  569. if (updatedProperty != null
  570. && taskSuccess
  571. && compileList.length != 0) {
  572. getProject().setNewProperty(updatedProperty, "true");
  573. }
  574. }
  575. /**
  576. * Clear the list of files to be compiled.
  577. */
  578. private void resetFileLists() {
  579. compileList = new File[0];
  580. }
  581. /**
  582. * Scans the directory looking for source files to be compiled.
  583. * The results are returned in the class variable compileList. Uses
  584. * the provided mapper to compare source and class file names.
  585. * @param srcDir the source directory
  586. * @param destDir the destination directory
  587. * @param files an array of filenames
  588. * @param mapper a FileNameMapper
  589. */
  590. private void scanDir(File srcDir, File destDir, String[] files,
  591. FileNameMapper mapper) {
  592. if ((prefix != null) &&
  593. (prefix.length() > 0)) {
  594. String prefixPath = prefix.replace('.', File.separatorChar);
  595. String prefixPathSlash = prefixPath;
  596. if (prefixPath.endsWith(File.separator)) {
  597. prefixPath = prefixPath.substring(0, prefixPath.length()-1);
  598. } else {
  599. prefixPathSlash += File.separator;
  600. }
  601. String srcStr = srcDir.getPath();
  602. if (srcStr.endsWith(prefixPath) ||
  603. srcStr.endsWith(prefixPathSlash)) {
  604. // the listed file names do not include the prefix
  605. String destStr = destDir.getPath();
  606. if (!(destStr.endsWith(prefixPath) ||
  607. destStr.endsWith(prefixPathSlash))) {
  608. // The destination path does not include the prefix, so the
  609. // glob will look in the wrong place if we use the original
  610. // destDir.
  611. destDir = new File(destDir, prefixPath);
  612. }
  613. }
  614. }
  615. SourceFileScanner sfs = new SourceFileScanner(this);
  616. File[] newFiles = sfs.restrictAsFiles(files, srcDir, destDir, mapper);
  617. if (newFiles.length > 0) {
  618. File[] newCompileList
  619. = new File[compileList.length + newFiles.length];
  620. System.arraycopy(compileList, 0, newCompileList, 0,
  621. compileList.length);
  622. System.arraycopy(newFiles, 0, newCompileList,
  623. compileList.length, newFiles.length);
  624. compileList = newCompileList;
  625. }
  626. }
  627. /**
  628. * Returns a file name mapper which maps source files to class
  629. * files. This needs to stay in sync with Languages and file name
  630. * extensions supported by Kawa.
  631. */
  632. private FileNameMapper getMapper() {
  633. if (language == null) {
  634. // unspecified, use all
  635. CompositeMapper mapper = new CompositeMapper();
  636. mapper.add(getSchemeMapper());
  637. mapper.add(getKrlMapper());
  638. mapper.add(getBrlMapper());
  639. mapper.add(getEmacsLispMapper());
  640. mapper.add(getXQueryMapper());
  641. mapper.add(getQ2Mapper());
  642. mapper.add(getXsltMapper());
  643. mapper.add(getCommonLispMapper());
  644. return mapper;
  645. } else if (languageMatches("scheme", ".scm", ".sc")) { // Scheme
  646. return getSchemeMapper();
  647. } else if (languageMatches("krl", ".krl")) {
  648. return getKrlMapper();
  649. } else if (languageMatches("brl", ".brl")) {
  650. return getBrlMapper();
  651. } else if (languageMatches("emacs", "elisp", "emacs-lisp", ".el")) {
  652. return getEmacsLispMapper();
  653. } else if (languageMatches("xquery", ".xquery", ".xq", ".xql")) {
  654. return getXQueryMapper();
  655. } else if (languageMatches("q2", ".q2")) {
  656. return getQ2Mapper();
  657. } else if (languageMatches("xslt", "xsl", ".xsl")) {
  658. return getXsltMapper();
  659. } else if (languageMatches("commonlisp", "common-lisp", "clisp",
  660. "lisp", ".lisp", ".lsp", ".cl")) {
  661. return getCommonLispMapper();
  662. } else {
  663. return null;
  664. }
  665. }
  666. private static class MangleFileNameMapper implements FileNameMapper {
  667. // A simplified version of Compilation.mangleSymbolic(). We're
  668. // dealing with paths and file extensions, so do not mangle
  669. // anything before the last '/', and do not mangle '.'.
  670. private String mangleNameIfNeeded(String name) {
  671. if (name == null) return name;
  672. StringBuilder sbuf = null;
  673. int dirend = name.lastIndexOf('/');
  674. String dir = null;
  675. String fname = name;
  676. if (dirend != -1) {
  677. dir = name.substring(0, dirend+1);
  678. fname = name.substring(dirend+1);
  679. }
  680. int len = fname.length();
  681. int dangerous = 0;
  682. for (int i = 0; i < len; i++) {
  683. char ch = fname.charAt(i);
  684. char ch2 = 0;
  685. switch (ch) {
  686. case ';': ch2 = '?'; break;
  687. case '$': ch2 = '%'; break;
  688. case '[': ch2 = '{'; break;
  689. case ']': ch2 = '}'; break;
  690. case ':': ch2 = '!'; break;
  691. case '\\': ch2 = '-'; break;
  692. }
  693. if (ch2 != 0 && ch != '\\')
  694. dangerous++;
  695. if (sbuf != null) {
  696. if (ch2 == 0)
  697. sbuf.append(ch);
  698. else
  699. sbuf.append('\\').append(ch2);
  700. } else if (ch2 != 0) {
  701. sbuf = new StringBuilder();
  702. if (dir != null)
  703. sbuf.append(dir);
  704. if (i != 0)
  705. sbuf.append("\\=");
  706. sbuf.append(fname, 0, i);
  707. sbuf.append('\\').append(ch2);
  708. }
  709. }
  710. return sbuf == null || dangerous == 0 ? name : sbuf.toString();
  711. }
  712. public String[] mapFileName(String sourceFileName) {
  713. String mangled = mangleNameIfNeeded(sourceFileName);
  714. if (mangled == null) return null;
  715. return new String[] { mangled };
  716. }
  717. public void setFrom(String from) {}
  718. public void setTo(String to) {}
  719. public static MangleFileNameMapper INSTANCE = new MangleFileNameMapper();
  720. }
  721. /**
  722. * Compares the language property to each of the given strings, and
  723. * returns true if there is a match.
  724. */
  725. private boolean languageMatches(String... possibilities) {
  726. for (String s : possibilities)
  727. if (s.equalsIgnoreCase(language))
  728. return true;
  729. return false;
  730. }
  731. ///////////////////////////
  732. ///// FileNameMappers /////
  733. ///////////////////////////
  734. private FileNameMapper getSchemeMapper() {
  735. return getMapper(".scm", ".sc");
  736. }
  737. private FileNameMapper getKrlMapper() {
  738. return getMapper(".krl");
  739. }
  740. private FileNameMapper getBrlMapper() {
  741. return getMapper(".brl");
  742. }
  743. private FileNameMapper getEmacsLispMapper() {
  744. return getMapper(".el");
  745. }
  746. private FileNameMapper getXQueryMapper() {
  747. return getMapper(".xquery", ".xq", ".xql");
  748. }
  749. private FileNameMapper getQ2Mapper() {
  750. return getMapper(".q2");
  751. }
  752. private FileNameMapper getXsltMapper() {
  753. return getMapper(".xsl");
  754. }
  755. private FileNameMapper getCommonLispMapper() {
  756. return getMapper(".lisp", ".lsp", ".cl");
  757. }
  758. /**
  759. * Constructs a glob pattern mapper which matches file names ending
  760. * with {@code ext} to ones ending with ".class".
  761. * @param ext a file name extension, including the period
  762. * (e.g. ".scm").
  763. */
  764. private FileNameMapper getMapper(String ext) {
  765. GlobPatternMapper m = new GlobPatternMapper();
  766. m.setFrom("*" + ext);
  767. m.setTo("*.class");
  768. ChainedMapper c = new ChainedMapper();
  769. c.add(m);
  770. c.add(MangleFileNameMapper.INSTANCE);
  771. return c;
  772. }
  773. /**
  774. * Constructs a composite mapper which consists of glob pattern
  775. * mappers for each of the given extensions.
  776. */
  777. private FileNameMapper getMapper(String... extensions) {
  778. CompositeMapper m = new CompositeMapper();
  779. for (String ext : extensions)
  780. m.add(getMapper(ext));
  781. return m;
  782. }
  783. /**
  784. * Gets the list of files to be compiled.
  785. * @return the list of files as an array
  786. */
  787. public File[] getFileList() {
  788. return compileList;
  789. }
  790. /**
  791. * Check that all required attributes have been set and nothing
  792. * silly has been entered.
  793. * @exception BuildException if an error occurs
  794. */
  795. private void checkParameters() throws BuildException {
  796. if (usedMatchingTask && src == null) {
  797. throw new BuildException("srcdir attribute must be set!",
  798. getLocation());
  799. }
  800. if (usedMatchingTask && src.size() == 0) {
  801. throw new BuildException("srcdir attribute must be set!",
  802. getLocation());
  803. }
  804. if (destDir != null && !destDir.isDirectory()) {
  805. throw new BuildException("destination directory \"" + destDir +
  806. "\" does not exist or is not a directory",
  807. getLocation());
  808. }
  809. if (filesets.size() > 0 && destDir == null) {
  810. throw new BuildException("destination directory must be set when"
  811. + " compiling filesets!", getLocation());
  812. }
  813. if (!usedMatchingTask && filesets.size() == 0) {
  814. throw new BuildException("either srcdir or a nested fileset/list"
  815. + " must be specified", getLocation());
  816. }
  817. }
  818. /**
  819. * Perform the compilation.
  820. */
  821. private void compile() {
  822. if (compileList.length > 0) {
  823. log("Compiling " + compileList.length + " source file"
  824. + (compileList.length == 1 ? "" : "s")
  825. + (destDir != null ? " to " + destDir : ""));
  826. if (listFiles) {
  827. for (int i = 0; i < compileList.length; i++) {
  828. String filename = compileList[i].getAbsolutePath();
  829. log(filename);
  830. }
  831. }
  832. // Set up the command line.
  833. Commandline cmd = new Commandline();
  834. setupKawaCommandline(cmd);
  835. int firstFileName = cmd.size();
  836. logAndAddFilesToCompile(cmd);
  837. // Do the compilation
  838. int rc = executeExternalCompile(cmd.getCommandline(),
  839. firstFileName, true);
  840. if (rc != 0) { // Failure
  841. taskSuccess = false;
  842. if (errorProperty != null) {
  843. getProject().setNewProperty(errorProperty, "true");
  844. }
  845. if (failOnError) {
  846. throw new BuildException(FAIL_MSG, getLocation());
  847. } else {
  848. log(FAIL_MSG, Project.MSG_ERR);
  849. }
  850. }
  851. }
  852. }
  853. /**
  854. * Get the Java executable which will invoke kawa.repl.
  855. */
  856. private String getSystemJava() {
  857. return JavaEnvUtils.getJreExecutable("java");
  858. }
  859. /**
  860. * Build the command line, translating the parameters into flags for
  861. * java and kawa.repl.
  862. * @param cmd the Commandline to be executed
  863. */
  864. private void setupKawaCommandline(Commandline cmd) {
  865. cmd.setExecutable(getSystemJava());
  866. Path classpath = getCompileClasspath();
  867. cmd.createArgument().setValue("-classpath");
  868. cmd.createArgument().setPath(classpath);
  869. cmd.createArgument().setValue("kawa.repl");
  870. if (target != null) {
  871. cmd.createArgument().setValue("--target");
  872. cmd.createArgument().setValue(target);
  873. }
  874. if (destDir != null) {
  875. cmd.createArgument().setValue("-d");
  876. cmd.createArgument().setFile(destDir);
  877. }
  878. if (prefix != null) {
  879. cmd.createArgument().setValue("-P");
  880. cmd.createArgument().setValue(prefix);
  881. }
  882. if (language != null) {
  883. cmd.createArgument().setValue("--"+language);
  884. }
  885. if (main) {
  886. cmd.createArgument().setValue("--main");
  887. }
  888. if (applet) {
  889. cmd.createArgument().setValue("--applet");
  890. }
  891. if (servlet) {
  892. cmd.createArgument().setValue("--servlet");
  893. }
  894. if (fullTailCalls) {
  895. cmd.createArgument().setValue("--full-tailcalls");
  896. // } else {
  897. // cmd.createArgument().setValue("--no-full-tailcalls");
  898. }
  899. if (moduleStatic != null) {
  900. if ("yes".equals(moduleStatic) || "on".equals(moduleStatic) ||
  901. "true".equals(moduleStatic))
  902. cmd.createArgument().setValue("--module-static");
  903. else if ("run".equals(moduleStatic))
  904. cmd.createArgument().setValue("--module-static-run");
  905. else if("no".equals(moduleStatic) || "off".equals(moduleStatic)
  906. || "false".equals(moduleStatic))
  907. cmd.createArgument().setValue("--no-module-static");
  908. }
  909. if (warnUndefinedVariable) {
  910. cmd.createArgument().setValue("--warn-undefined-variable");
  911. }
  912. if (warnAsError) {
  913. cmd.createArgument().setValue("--warn-as-error");
  914. }
  915. // Add nested command line args.
  916. for (Commandline.Argument arg : otherArgs) {
  917. for (String p : arg.getParts()) {
  918. cmd.createArgument().setValue(p); // should probably do some validation?
  919. }
  920. }
  921. cmd.createArgument().setValue("-C");
  922. }
  923. /**
  924. * Add the file names to the Commandline. Log them in verbose mode.
  925. * @param cmd the Commandline to be executed
  926. */
  927. private void logAndAddFilesToCompile(Commandline cmd) {
  928. log("Compilation " + cmd.describeArguments(),
  929. Project.MSG_VERBOSE);
  930. StringBuffer niceSourceList = new StringBuffer("File");
  931. if (compileList.length != 1) {
  932. niceSourceList.append("s");
  933. }
  934. niceSourceList.append(" to be compiled:");
  935. niceSourceList.append(StringUtils.LINE_SEP);
  936. for (int i = 0; i < compileList.length; i++) {
  937. String arg = compileList[i].getAbsolutePath();
  938. cmd.createArgument().setValue(arg);
  939. niceSourceList.append(" ");
  940. niceSourceList.append(arg);
  941. niceSourceList.append(StringUtils.LINE_SEP);
  942. }
  943. log(niceSourceList.toString(), Project.MSG_VERBOSE);
  944. }
  945. /**
  946. * Get the classpath to use, which includes a specified CP along
  947. * with the destination directory if appropriate.
  948. * @return the classpath as a Path
  949. */
  950. private Path getCompileClasspath() {
  951. Path classpath = new Path(getProject());
  952. // add dest dir to classpath so that previously compiled and
  953. // untouched classes are on classpath
  954. if (destDir != null && isIncludeDestClasses()) {
  955. classpath.setLocation(destDir);
  956. }
  957. Path cp = compileClasspath;
  958. if (cp == null) {
  959. cp = new Path(getProject());
  960. }
  961. classpath.addExisting(cp);
  962. return classpath;
  963. }
  964. /**
  965. * Invoke kawa.repl to really do the compilation.
  966. */
  967. private int executeExternalCompile(String[] args, int firstFileName,
  968. boolean quoteFiles) {
  969. try {
  970. Execute exe = new Execute(new LogStreamHandler(this,
  971. Project.MSG_INFO,
  972. Project.MSG_WARN));
  973. if (Os.isFamily("openvms")) {
  974. // Use the VM launcher instead of shell launcher on VMS for
  975. // java
  976. exe.setVMLauncher(true);
  977. }
  978. exe.setAntRun(getProject());
  979. exe.setWorkingDirectory(getProject().getBaseDir());
  980. exe.setCommandline(args);
  981. exe.execute();
  982. return exe.getExitValue();
  983. } catch (IOException e) {
  984. throw new BuildException("Error running Kawa compiler", e, getLocation());
  985. }
  986. }
  987. }