指令碼

說明

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

如果您以編程方式建立目標,請務必將位置設定為有用的值。特別是,所有目標都應該有不同的位置值。

參數

屬性 說明 必要
語言 指令碼所寫的程式語言。必須是 Apache BSF 或 JSR 223 支援的語言
管理員 自 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>

下列範例顯示使用類別路徑指定 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>