脚本

描述

Apache BSFJSR 223 支持的语言中执行脚本。

注意:此任务依赖于 Apache Ant 分发版中未包含的外部库。有关更多信息,请参阅 库依赖项

此任务可以使用 BSF 脚本管理器或 JDK 中包含的 JSR 223 管理器。这由 manager 属性控制。JSR 223 脚本管理器由 javax 表示。

运行项目的所有项目(任务、目标等)都可以从脚本中访问,使用它们的 nameid 属性(只要它们的名称被认为是有效的 Java 标识符)。这由任务的 setbeans 属性控制。名称 project 是对项目的预定义引用,可以用来代替项目名称。名称 self 是对实际 <script> 任务实例的预定义引用。
从这些对象中,您可以访问 Ant Java API,请参阅 JavaDoc(特别是 ProjectScript)以了解更多信息。

如果您在 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 起。要使用的脚本引擎管理器。这可以具有三个值之一:autobsfjavax
  • bsf 使用 BSF 脚本管理器运行语言。
  • javax 使用 javax.scripting 管理器运行语言。
  • auto 如果存在 BSF 引擎,则使用它,否则使用 javax.scripting 管理器。
否;默认值为 auto
src 脚本作为文件的位置,如果不在内联中
encoding 脚本作为文件的编码。自 Ant 1.10.2 起 否;默认为默认 JVM 字符编码
setbeans 此属性控制是否为运行脚本中的所有属性、引用和目标设置变量。如果此属性为 false,则仅设置 projectself 变量。如果此属性为 true,则设置所有变量。自 Ant 1.7 起 否;默认为 true
classpath 要传递到脚本的类路径。自 Ant 1.7 起
classpathref 要使用的类路径,作为对在其他地方定义的路径的 引用 给出。自 Ant 1.7 起

作为嵌套元素指定的参数

classpath

自 Ant 1.7 起

Scriptclasspath 属性是一个 路径状结构,也可以通过嵌套的 <classpath> 元素设置。

如果设置了类路径,它将用作当前线程上下文类加载器,以及传递给 BSF 管理器的类加载器。这意味着它可以用来指定包含 BSF 或 JSR 223 管理器语言实现的类路径。如果想要使 ${user.home}/.ant/lib 免于大量特定于脚本语言的 jar 文件,这将很有用。

注意:(自 Ant 1.7.1 起)此类路径可以用来指定 BSF jar 文件的位置和/或在 BSF jar 文件中具有引擎的语言。这包括 javascriptjythonnetrexxjacl 语言。

示例

以下代码段显示了五种不同语言的使用

    <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:jsorg.graalvm.js:js-engine,它们又需要 org.graalvm.regex:regexorg.graalvm.truffle:truffle-apiorg.graalvm.sdk:graal-sdkcom.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>