001: /*
002: * The Apache Software License, Version 1.1
003: *
004: * Copyright (c) 2002 The Apache Software Foundation. All rights
005: * reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The end-user documentation included with the redistribution, if
020: * any, must include the following acknowlegement:
021: * "This product includes software developed by the
022: * Apache Software Foundation (http://www.apache.org/)."
023: * Alternately, this acknowlegement may appear in the software itself,
024: * if and wherever such third-party acknowlegements normally appear.
025: *
026: * 4. The names "The Jakarta Project", "Ant", and "Apache Software
027: * Foundation" must not be used to endorse or promote products derived
028: * from this software without prior written permission. For written
029: * permission, please contact apache@apache.org.
030: *
031: * 5. Products derived from this software may not be called "Apache"
032: * nor may "Apache" appear in their names without prior written
033: * permission of the Apache Group.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: *
049: * This software consists of voluntary contributions made by many
050: * individuals on behalf of the Apache Software Foundation. For more
051: * information on the Apache Software Foundation, please see
052: * <http://www.apache.org/>.
053: */
054: //package org.apache.tools.ant.taskdefs.optional.python;
055: package org.python.util;
056:
057: import org.apache.tools.ant.taskdefs.MatchingTask;
058: import org.apache.tools.ant.BuildException;
059: import org.apache.tools.ant.taskdefs.Java;
060: import org.apache.tools.ant.types.Path;
061: import org.apache.tools.ant.DirectoryScanner;
062: import org.python.core.PySystemState;
063:
064: import java.io.File;
065:
066: /**
067: * Jythonc is a Python compiler into Java Bytecode. So you can
068: * call your python code from Java, and call you java code from
069: * Python, create bean, servlet...
070: *
071: * <p>
072: *
073: * The task is a directory based task, so attributes like <b>includes="*.py"</b> and
074: * <b>excludes="broken.py"</b> can be used to control the files pulled in. By default,
075: * all *.py files from the project folder down are included in the command.
076: *
077: * @author Cyrille Morvan - cmorvan@ingenosya.com - Ingenosya France
078: * @version 1.0
079: */
080: public class JythoncAntTask extends MatchingTask {
081:
082: protected static final String JYTHONC_PY = "Tools/jythonc/jythonc.py";
083: protected static final String JYTHON_CLASS = "org.python.util.jython";
084:
085: /**
086: * The classpath for the virtual machine.
087: */
088: protected Path classpath;
089:
090: /**
091: * Put all compiled code into the named Java package.
092: */
093: protected String packageName;
094:
095: /**
096: * Specifies a .jar file to create and put the results of
097: * the freeze into. Set the deep option to true.
098: */
099: protected File jarFile;
100:
101: /**
102: * the compiler.
103: */
104: protected File jythoncpy;
105:
106: /**
107: * Compile all Python dependencies of the module. This is
108: * used for creating applets.
109: */
110: protected boolean deep;
111:
112: /**
113: * Include the core Jython libraries (about 130K).
114: * Needed for applets since Netscape doesn't yet support
115: * multiple archives.
116: */
117: protected boolean core;
118:
119: /**
120: * Include all of the Jython libraries (everything in core + compiler and
121: * parser).
122: */
123: protected boolean all;
124:
125: /**
126: * Include Java dependencies from this list of packages. Default is
127: * org.python.modules and org.apache.oro.text.regex.
128: */
129: protected String addpackages;
130:
131: /**
132: * Compile into jarfile, including the correct manifest for the bean.
133: */
134: protected File jarFileBean;
135:
136: /**
137: * Don't include any of these modules in compilation. This is a
138: * comma-separated list of modules.
139: */
140: protected String skipModule;
141:
142: /**
143: * Compiler name.
144: */
145: protected String compiler;
146:
147: /**
148: * Use a different compiler than `standard' javac. If this is set to "NONE"
149: * then compile ends with the generation of the Java source file.
150: * Alternatively, you can set the property python.jythonc.compiler in the
151: * registry.
152: */
153: protected String compileropts;
154:
155: /**
156: * Options passed directly to the Java compiler. Alternatively, you can set
157: * the property python.jythonc.compileropts in the registry.
158: */
159: protected String falsenames;
160:
161: /**
162: * Path to jython directory.
163: */
164: protected File jythonHome;
165:
166: /**
167: * Destination, build directory.
168: */
169: protected File destDir;
170:
171: /**
172: * Source directory.
173: */
174: protected File srcDir;
175:
176: /**
177: * Specify the working directory where the generated Java source code is
178: * placed. Default is "./jpywork"
179: */
180: protected File workdir;
181:
182: /**
183: * aditionnals args.
184: */
185: protected String extraArgs;
186:
187: /**
188: * constructor set up the search pattern
189: */
190: public JythoncAntTask() {
191: setIncludes("**/*.py");
192: }
193:
194: /**
195: * Add a classpath. Used to handle the nested classpath
196: * element.
197: * @return A Path object representing the classpath to be used.
198: */
199: public Path createClasspath() {
200: if (classpath == null) {
201: classpath = new Path(this .getProject());
202: }
203: return classpath.createPath();
204: }
205:
206: /**
207: * Sets the classpath field.
208: * @param aClasspath A Path object representing the "classpath" attribute.
209: */
210: public void setClasspath(Path aClasspath) {
211: classpath = aClasspath;
212: }
213:
214: /**
215: * Put all compiled code into the named Java package.
216: * @param aString the packake name.
217: */
218: public void setPackage(String aString) {
219: packageName = aString;
220: }
221:
222: /**
223: * Specifies a .jar file to create and put the results of
224: * the freeze into. Set the deep option to true.
225: */
226: public void setJar(File aJarFile) {
227: jarFile = aJarFile;
228: deep = true;
229: }
230:
231: /**
232: * Include the core Jython libraries (about 130K).
233: * Needed for applets since Netscape doesn't yet support
234: * multiple archives. Set the deep option to true.
235: */
236: public void setCore(boolean aValue) {
237: core = aValue;
238: deep = true;
239: }
240:
241: /**
242: * Include all of the Jython libraries (everything in core + compiler and
243: * parser). Set the deep option to true.
244: */
245: public void setAll(boolean aValue) {
246: all = aValue;
247: deep = true;
248: }
249:
250: /**
251: * Compile into jarfile, including the correct manifest for the bean.
252: */
253: public void setBean(File aJarFileBean) {
254: jarFileBean = aJarFileBean;
255: }
256:
257: /**
258: * Don't include any of these modules in compilation. This is a
259: * comma-separated list of modules.
260: */
261: public void setSkip(String aValue) {
262: skipModule = aValue;
263: }
264:
265: /**
266: * Compile all Python dependencies of the module. This is
267: * used for creating applets.
268: */
269: public void setDeep(boolean aValue) {
270: deep = aValue;
271: }
272:
273: /**
274: * Include Java dependencies from this list of packages. Default is
275: * org.python.modules and org.apache.oro.text.regex.
276: */
277: public void setAddpackages(String aValue) {
278: addpackages = aValue;
279: }
280:
281: /**
282: * Specify the working directory where the generated Java source code is
283: * placed. Default is "./jpywork"
284: */
285: public void setWorkdir(File aValue) {
286: if (aValue.exists()) {
287: if (!aValue.isDirectory()) {
288: throw new BuildException("Workdir (" + aValue
289: + ") is not a directory");
290: }
291: } else {
292: aValue.mkdirs();
293: }
294: workdir = aValue;
295: }
296:
297: /**
298: * Set the compiler.
299: */
300: public void setCompiler(String aCompiler) {
301: compiler = aCompiler;
302: }
303:
304: /**
305: * Options passed directly to the Java compiler. Alternatively, you can set
306: * the property python.jythonc.compileropts in the registry.
307: */
308: public void setCompileropts(String aValue) {
309: compileropts = aValue;
310: }
311:
312: /**
313: * A comma-separated list of names that are always false. Can be used to
314: * short-circuit if clauses.
315: */
316: public void setFalsenames(String aValue) {
317: falsenames = aValue;
318: }
319:
320: /**
321: * Jython home directory.
322: */
323: public void setHome(File aFile) {
324: jythonHome = aFile;
325: }
326:
327: /**
328: * Home for the source.
329: */
330: public void setSrcdir(File aFile) {
331: srcDir = aFile;
332: }
333:
334: /**
335: * Home for the destination (build).
336: */
337: public void setDestdir(File aFile) {
338: destDir = aFile;
339: }
340:
341: /**
342: * Change the default Python compiler.
343: */
344: public void setJythoncpy(File aValue) {
345: jythoncpy = aValue;
346: }
347:
348: /**
349: * sets some additional args to send to jythonc.
350: */
351: public void setArgs(String aValue) {
352: extraArgs = aValue;
353: }
354:
355: /**
356: * get the compiler option, null if none.
357: */
358: public String getCompilerOptions() {
359: StringBuffer aStringBuffer = new StringBuffer();
360: if (destDir != null) {
361: aStringBuffer.append("-d \"");
362: aStringBuffer.append(destDir);
363: aStringBuffer.append("\"");
364:
365: createClasspath().setLocation(destDir);
366: destDir.mkdirs();
367: }
368: if (compileropts != null) {
369: aStringBuffer.append(compileropts);
370: }
371: if (aStringBuffer.length() == 0) {
372: return null;
373: } else {
374: return aStringBuffer.toString();
375: }
376: }
377:
378: /**
379: * Get the path to the jython home (or python home)
380: */
381: public File getPythonHome() {
382: if (jythonHome == null) {
383: String aPythonHome = getProject()
384: .getProperty("python.home");
385: if (aPythonHome == null) {
386: throw new BuildException(
387: "No python.home or home specified");
388: }
389: jythonHome = new File(aPythonHome);
390: }
391: return jythonHome;
392: }
393:
394: /**
395: * Get the path to the jython compiler file (in python).
396: */
397: public File getJythoncPY() {
398: if (jythoncpy == null) {
399: return new File(getPythonHome(), JYTHONC_PY);
400: }
401: return jythoncpy;
402: }
403:
404: /**
405: * Exectute the compiler.
406: */
407: public void execute() {
408: try {
409: Java javaTask = null;
410:
411: javaTask = (Java) getProject().createTask("java");
412: javaTask.setTaskName("jythonc");
413:
414: javaTask.setClassname(JYTHON_CLASS);
415:
416: javaTask.createJvmarg().setValue(
417: "-Dpython.home=" + getPythonHome());
418:
419: // classpath
420: File aJythonJarFile = new File(getPythonHome(),
421: PySystemState.JYTHON_JAR);
422: createClasspath().setLocation(aJythonJarFile);
423:
424: javaTask.setClasspath(classpath);
425:
426: // jythonc file
427: javaTask.createArg().setFile(getJythoncPY());
428:
429: if (packageName != null) {
430: javaTask.createArg().setValue("--package");
431: javaTask.createArg().setValue(packageName);
432: }
433:
434: if (jarFile != null) {
435: javaTask.createArg().setValue("--jar");
436: javaTask.createArg().setFile(jarFile);
437: }
438:
439: if (deep) {
440: javaTask.createArg().setValue("--deep");
441: }
442:
443: if (core) {
444: javaTask.createArg().setValue("--core");
445: }
446:
447: if (all) {
448: javaTask.createArg().setValue("--all");
449: }
450:
451: if (jarFileBean != null) {
452: javaTask.createArg().setValue("--bean");
453: javaTask.createArg().setFile(jarFileBean);
454: }
455:
456: if (addpackages != null) {
457: javaTask.createArg().setValue("--addpackages ");
458: javaTask.createArg().setValue(addpackages);
459: }
460:
461: if (workdir != null) {
462: javaTask.createArg().setValue("--workdir ");
463: javaTask.createArg().setFile(workdir);
464: }
465:
466: if (skipModule != null) {
467: javaTask.createArg().setValue("--skip");
468: javaTask.createArg().setValue(skipModule);
469: }
470:
471: // --compiler
472: if (compiler == null) {
473: // try to use the compiler specified by build.compiler. Right now we are
474: // just going to allow Jikes
475: String buildCompiler = getProject().getProperty(
476: "build.compiler");
477: if (buildCompiler != null
478: && buildCompiler.equals("jikes")) {
479: javaTask.createArg().setValue("--compiler");
480: javaTask.createArg().setValue("jikes");
481: }
482: } else {
483: javaTask.createArg().setValue("--compiler");
484: javaTask.createArg().setValue(compiler);
485: }
486:
487: String aCompilerOpts = getCompilerOptions();
488: if (aCompilerOpts != null) {
489: javaTask.createArg().setValue("--compileropts");
490: javaTask.createArg().setValue(aCompilerOpts);
491: }
492:
493: if (falsenames != null) {
494: javaTask.createArg().setValue("--falsenames");
495: javaTask.createArg().setValue(falsenames);
496: }
497:
498: if (extraArgs != null) {
499: javaTask.createArg().setLine(extraArgs);
500: }
501:
502: //get dependencies list.
503: if (srcDir == null) {
504: srcDir = project.resolveFile(".");
505: }
506: DirectoryScanner scanner = super
507: .getDirectoryScanner(srcDir);
508: String[] dependencies = scanner.getIncludedFiles();
509: log("compiling " + dependencies.length + " file"
510: + ((dependencies.length == 1) ? "" : "s"));
511: String baseDir = scanner.getBasedir().toString()
512: + File.separator;
513: //add to the command
514: for (int i = 0; i < dependencies.length; i++) {
515: String targetFile = dependencies[i];
516: javaTask.createArg().setValue(baseDir + targetFile);
517: }
518:
519: // change the location directory
520: javaTask.setDir(srcDir);
521: javaTask.setFork(true);
522: if (javaTask.executeJava() != 0) {
523: throw new BuildException("jythonc reported an error");
524: }
525: } catch (Exception e) {
526: // Have to catch this because of the semantics of calling main()
527: String msg = "Exception while calling " + JYTHON_CLASS
528: + ". Details: " + e.toString();
529: throw new BuildException(msg, e);
530: }
531: }
532: }
|