在 Apache BSF 或 JSR 223 支持的语言中执行脚本。
注意:此任务依赖于 Apache Ant 分发版中未包含的外部库。有关更多信息,请参阅 库依赖项。
此任务可以使用 BSF 脚本管理器或 JDK 中包含的 JSR 223 管理器。这由 manager 属性控制。JSR 223 脚本管理器由 javax
表示。
运行项目的所有项目(任务、目标等)都可以从脚本中访问,使用它们的 name 或 id 属性(只要它们的名称被认为是有效的 Java 标识符)。这由任务的 setbeans 属性控制。名称 project
是对项目的预定义引用,可以用来代替项目名称。名称 self
是对实际 <script>
任务实例的预定义引用。
从这些对象中,您可以访问 Ant Java API,请参阅 JavaDoc(特别是 Project 和 Script)以了解更多信息。
如果您在 BSF 下使用 JavaScript,一个很好的资源是 https://www.mozilla.org/rhino/doc.html,因为我们使用的是他们的 JavaScript 解释器。
脚本几乎可以完成用 Java 编写的任务所能完成的任何事情。
Rhino 提供了一个特殊的构造——JavaAdapter
。有了它,您可以创建一个实现多个接口、扩展类并可以覆盖方法的对象。由于这是一个未记录的功能(目前),这里有一个解释链接:Google Groups:"Rhino, enum.js, JavaAdapter?" 由 Norris Boyd 在新闻组 netscape.public.mozilla.jseng 中发布。
如果您正在以编程方式创建目标,请确保将位置设置为有用的值。特别是所有目标都应该具有不同的位置值。
属性 | 描述 | 必需 |
---|---|---|
language | 脚本编写的编程语言。必须是受支持的 Apache BSF 或 JSR 223 语言 | 是 |
manager |
自 Ant 1.7 起。要使用的脚本引擎管理器。这可以具有三个值之一:auto、 bsf或 javax。
|
否;默认值为 auto |
src | 脚本作为文件的位置,如果不在内联中 | 否 |
encoding | 脚本作为文件的编码。自 Ant 1.10.2 起。 | 否;默认为默认 JVM 字符编码 |
setbeans | 此属性控制是否为运行脚本中的所有属性、引用和目标设置变量。如果此属性为 false,则仅设置 project 和 self 变量。如果此属性为 true,则设置所有变量。自 Ant 1.7 起 |
否;默认为 true |
classpath | 要传递到脚本的类路径。自 Ant 1.7 起 | 否 |
classpathref | 要使用的类路径,作为对在其他地方定义的路径的 引用 给出。自 Ant 1.7 起 | 否 |
自 Ant 1.7 起
Script
的 classpath 属性是一个 路径状结构,也可以通过嵌套的 <classpath>
元素设置。
如果设置了类路径,它将用作当前线程上下文类加载器,以及传递给 BSF 管理器的类加载器。这意味着它可以用来指定包含 BSF 或 JSR 223 管理器语言实现的类路径。如果想要使 ${user.home}/.ant/lib 免于大量特定于脚本语言的 jar 文件,这将很有用。
注意:(自 Ant 1.7.1 起)此类路径可以用来指定 BSF jar 文件的位置和/或在 BSF jar 文件中具有引擎的语言。这包括 javascript
、jython
、netrexx
和 jacl
语言。
以下代码段显示了五种不同语言的使用
<property name="message" value="Hello world"/> <script language="groovy"> println("message is " + message) </script> <script language="beanshell"> System.out.println("message is " + message); </script> <script language="judoscript"> println 'message is ', message </script> <script language="ruby"> print 'message is ', $message, "\n" </script> <script language="jython"> print "message is %s" % message </script>
请注意,对于 jython
示例,脚本内容必须从第一列开始。
另请注意,对于 ruby
示例,设置变量的名称以 $
为前缀。
以下脚本显示了一个更复杂的 JRuby 示例
<script language="ruby"> xmlfiles = Dir.new(".").entries.delete_if { |i| ! (i =~ /\.xml$/) } xmlfiles.sort.each { |i| $self.log(i) } </script>
Groovy 中的相同示例是
<script language="groovy"> xmlfiles = new java.io.File(".").listFiles().findAll{ it =~ "\.xml$"} xmlfiles.sort().each { self.log(it.toString()) } </script>
以下示例显示了使用 classpath 指定 beanshell jar 文件位置。
<script language="beanshell" setbeans="true"> <classpath> <fileset dir="${user.home}/lang/beanshell" includes="*.jar"/> </classpath> System.out.println("Hello world"); </script>
以下脚本使用 JavaScript 创建多个 echo
任务并执行它们。
<project name="squares" default="main" basedir="."> <target name="main"> <script language="javascript"> <![CDATA[ for (i = 1; i <= 10; i++) { echo = squares.createTask("echo"); echo.setMessage(i*i); echo.perform(); } ]]> </script> </target> </project>
生成
main: 1 4 9 16 25 36 49 64 81 100 BUILD SUCCESSFUL
现在是一个更复杂的示例,使用 Java API 和 Ant API。目标是列出 <fileset/>
捕获的所有文件的尺寸。
<?xml version="1.0" encoding="UTF-8"?> <project name="MyProject" basedir="." default="main"> <property name="fs.dir" value="src"/> <property name="fs.includes" value="**/*.txt"/> <property name="fs.excludes" value="**/*.tmp"/> <target name="main"> <script language="javascript"> <![CDATA[ // import statements // importPackage(java.io); importClass(java.io.File); // Nashorn syntax // load("nashorn:mozilla_compat.js"); // or // var File = Java.type('java.io.File'); // Access to Ant-Properties by their names dir = project.getProperty("fs.dir"); includes = MyProject.getProperty("fs.includes"); excludes = self.getProject().getProperty("fs.excludes"); // Create a <fileset dir="" includes=""/> fs = project.createDataType("fileset"); fs.setDir(new File(dir)); fs.setIncludes(includes); fs.setExcludes(excludes); // Get the files (array) of that fileset ds = fs.getDirectoryScanner(project); srcFiles = ds.getIncludedFiles(); // iterate over that array for (i = 0; i < srcFiles.length; i++) { // get the values via Java API var basedir = fs.getDir(project); var filename = srcFiles[i]; var file = new File(basedir, filename); var size = file.length(); // create and use a Task via Ant API echo = MyProject.createTask("echo"); echo.setMessage(filename + ": " + size + " byte"); echo.perform(); } ]]></script> </target> </project>
我们想要使用 Java API。因为我们不想总是键入包签名,所以我们进行导入。Rhino 知道两种不同的导入语句:一种用于包,另一种用于单个类。默认情况下,只有 java
包可用,因此 java.lang.System
可以直接使用 importClass
/importPackage
导入。对于其他包,您必须使用 Packages 为完整的分类名称添加前缀。例如,Ant 的 FileUtils
类可以使用 importClass(Packages.org.apache.tools.ant.util.FileUtils)
导入
在 Java 8 到 Java 14 中,您可以使用内置的 Nashorn JavaScript 引擎,而不是 Rhino(它在 Java 7 运行时可用)。然后,使用 Java.type
作为任何 Java 类或 兼容性脚本 的导入语句:load("nashorn:mozilla_compat.js");
。
从 Java 15 开始,Nashorn 再次被移除,您需要提供一个外部 JavaScript 引擎。您最好的选择可能是 GraalVM JavaScript,它需要您添加许多额外的 jar 文件。对于 GraalVM JavaScript 20.1,您需要 org.graalvm.js:js
、org.graalvm.js:js-engine
,它们又需要 org.graalvm.regex:regex
、org.graalvm.truffle:truffle-api
、org.graalvm.sdk:graal-sdk
和 com.ibm.icu:icu4j
。GraalVM JavaScript 不是 Nashorn 的直接替代品,请参阅 Graal 的 Nashorn 迁移指南 以了解更多详细信息。
当使用 GraalVM JavaScript 时,Ant 将启用功能 polyglot.js.allowAllAccess
以允许脚本使用 Ant 对象。默认情况下,它还将启用 Nashorn 兼容模式,但您可以通过将神奇的 Ant 属性 ant.disable.graal.nashorn.compat
设置为 true
来禁用它。
<script>
任务在名称为 project
的情况下填充 Project 实例,因此我们可以使用该引用。另一种方法是使用其给定的名称或从任务本身获取其引用。Project 提供了用于访问和设置属性、创建数据类型和任务以及更多功能的方法。
在创建 FileSet 对象后,我们通过调用其 set 方法来初始化它。然后,我们可以像使用普通 Ant 任务一样使用该对象(例如 <copy>
)。
为了获取文件的尺寸,我们实例化一个 java.io.File
。因此,我们在这里使用的是普通的 Java API。
最后,我们使用 <echo>
任务来生成输出。该任务不会通过其 execute()
方法执行,因为 perform()
方法(在 Task 本身中实现)会在调用 execute()
之前和之后执行适当的日志记录。
以下是如何使用 beanshell 创建 Ant 任务的示例。此任务将文件集和路径添加到引用的路径中。如果路径不存在,它将被创建。
<!-- Define addtopath task --> <script language="beanshell"> import org.apache.tools.ant.Task; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.FileSet; public class AddToPath extends Task { private Path path; public void setRefId(String id) { path = getProject().getReference(id); if (path == null) { path = new Path(getProject()); getProject().addReference(id, path); } } public void add(Path c) { path.add(c); } public void add(FileSet c) { path.add(c); } public void execute() { // Do nothing } } project.addTaskDefinition("addtopath", AddToPath.class); </script>
以下是如何使用此任务从目录列表创建路径(使用 Ant-Contrib 的 <for> 任务)的示例
<path id="main.path"> <fileset dir="build/classes"/> </path> <ac:for param="ref" list="commons,fw,lps" xmlns:ac="antlib:net.sf.antcontrib"> <sequential> <addtopath refid="main.path"> <fileset dir="${dist.dir}/@{ref}/main" includes="**/*.jar"/> </addtopath> </sequential> </ac:for>