Ant: Як виконати команду для кожного файлу в каталозі?


97

Я хочу виконати команду з файлу збірки Ant для кожного файлу в каталозі.
Я шукаю рішення, незалежне від платформи.

Як це зробити?

Звичайно, я міг би написати сценарій якоюсь мовою сценаріїв, але це додало б подальших залежностей до проекту.

Відповіді:


61

Коротка відповідь

Використовуйте <foreach>з вкладеними<FileSet>

Foreach вимагає мурашиного вкладу .

Оновлений приклад нещодавнього ant-contrib:

<target name="foo">
  <foreach target="bar" param="theFile">
    <fileset dir="${server.src}" casesensitive="yes">
      <include name="**/*.java"/>
      <exclude name="**/*Test*"/>
    </fileset>
  </foreach>
</target>

<target name="bar">
  <echo message="${theFile}"/>
</target>

Це призведе до виклику цільової "смуги" з результатом $ {theFile} у поточному файлі.


Отже, я повинен щось включити? Або мені потрібна якась зовнішня мурашина гілка? Я отримую "Проблема: не вдалося створити завдання або ввести foreach" . Якщо я правильно розумію, це означає, що foreach - це невідоме ключове слово.
ivan_ivanovich_ivanoff

4
Ох, кульга ... це завдання з мурашниками. Отже, вам потрібно щось встановити. Дивіться тут: ant-contrib.sourceforge.net
blak3r

3
Цей приклад є з передвісті до того, як він був включений до мурашника. Є хороший приклад на ant.1045680.n5.nabble.com/Using-foreach-td1354624.html Я збираюся оновити приклад для роботи.
Шон

2
Вкладений елемент набору файлів застарілий, використовуйте замість нього вкладений шлях
чувак

@dude Use <foreach> <path> <fileset> <include name = "*. jar" /> </fileset> </path> </foreach>
Шарат,

88

Використовуйте завдання <застосувати> .

Він виконує команду один раз для кожного файлу. Вкажіть файли за допомогою наборів файлів або будь-якого іншого ресурсу. <apply> вбудований; не потрібна додаткова залежність; не потрібне виконання спеціального завдання.

Також команду можна запустити лише один раз, додавши всі файли як аргументи за один раз. Використовуйте паралельний атрибут, щоб змінити поведінку.

Вибачте, що запізнився на рік.


4
Ну, я просто знайшов це корисним у 2011 році, тож дякую за це в будь-якому разі!
Michael Della Bitta

7
Проблема <apply> полягає в тому, що він виконує лише зовнішні системні команди. Це не буде робити будь-які анти-речі безпосередньо. Незрозуміло, саме про це йшлося у вихідному питанні чи ні. В основному вам потрібно використовувати <apply> або <foreach> для двох різних ситуацій ... що абсолютно німо.
Арчі

27

Тассіло Горн пропонує підхід без мурашника ( оригінальна мета тут )

По суті, оскільки розширення <java> (ще?) Не існує так само, як <apply> розширює <exec> , він пропонує використовувати <apply> (який, звичайно, також може запускати програму Java у командному рядку)

Ось кілька прикладів:

  <apply executable="java"> 
    <arg value="-cp"/> 
    <arg pathref="classpath"/> 
    <arg value="-f"/> 
    <srcfile/> 
    <arg line="-o ${output.dir}"/> 

    <fileset dir="${input.dir}" includes="*.txt"/> 
  </apply> 

2
Це не дуже незалежно від платформи, хоча це було чітко потрібно в оригінальному запитанні ...
Чакі,

18

Ось спосіб зробити це, використовуючи javascript і завдання ant scriptdef, вам не потрібно ant-contrib, щоб цей код працював, оскільки scriptdef є основним завданням ant.

<scriptdef name="bzip2-files" language="javascript">
<element name="fileset" type="fileset"/>
<![CDATA[
  importClass(java.io.File);
  filesets = elements.get("fileset");

  for (i = 0; i < filesets.size(); ++i) {
    fileset = filesets.get(i);
    scanner = fileset.getDirectoryScanner(project);
    scanner.scan();
    files = scanner.getIncludedFiles();
    for( j=0; j < files.length; j++) {

        var basedir  = fileset.getDir(project);
        var filename = files[j];
        var src = new File(basedir, filename);
        var dest= new File(basedir, filename + ".bz2");

        bzip2 = self.project.createTask("bzip2");        
        bzip2.setSrc( src);
        bzip2.setDestfile(dest ); 
        bzip2.execute();
    }
  }
]]>
</scriptdef>

<bzip2-files>
    <fileset id="test" dir="upstream/classpath/jars/development">
            <include name="**/*.jar" />
    </fileset>
</bzip2-files>

на змінну projectпосилається у наведеному вище прикладі, але без попереднього визначення. Було б добре, щоб це було представлено або роз’яснено. EDIT: n / m, виявлено, що projectце заздалегідь визначена змінна для доступу до поточного проекту. Я підозрював це, але не був впевнений.
Джон Л.

1
Для тих, хто пробує це в JDK8 або пізніших версіях, майте на увазі, що "importClass" працює, якщо ScriptEngine, завантажений JRE, є rhino (що було справедливо для більшості JDK 6 та 7), тоді як у Nashorn (з 8 і далі) ви можете використовувати зворотно сумісний "File = java.io.File" або новіший, але не сумісний із зворотним зв'язком Java.type. Здається, Мураха мовчки не вдається, коли виникають проблеми із запуском scriptdef, як я мав досвід сьогодні.
Маттео Стекколіні,

15

мурашиний внесок - це зло; написати власне мурашине завдання.

ant-contrib - це зло, оскільки він намагається перетворити мураха з декларативного стилю в імперативний. Але xml робить моту програмування безглуздо.

На відміну від цього, спеціальне мурашине завдання дозволяє писати справжньою мовою (Java) із справжньою IDE, де ви можете писати модульні тести, щоб переконатися, що маєте потрібну поведінку, а потім зробити чисту заяву у сценарії збірки про поведінку, яку ви хочете.

Ця зухвалість має значення лише в тому випадку, якщо ви переймаєтеся написанням скриптів, що підтримуються. Якщо ви не переймаєтесь ремонтом, будь-якими способами зробіть все, що спрацює. :)

Jtf


2
Ви маєте рацію, мені слід просто написати власне
мурашине

4
мурашник справді є злим. Зараз я переживаю великий проект побудови мурашок, який інтенсивно використовує if / then / else та antcalls, і це справді читає жахливо. Все це виглядає як перетворений пакетний / оболонковий сценарій, і все, що робить мураха, все повністю вимикається завдяки інтенсивному використанню ant-contrib. Якщо ви хочете підтримувати чистоту налаштувань, створіть своє власне завдання. : - /
крижатися

2
@cringe, я не згоден. Як і будь-що інше, ви повинні знати, коли в ваших інтересах використовувати ant-contrib. Тримайтеся подалі від таких речей, як if і var, і використовуйте ant-contrib, щоб уникнути необхідності винаходити колесо заново.
Ніл

1
І для початку це мала бути мова сценаріїв, настільки обов’язкова, а не декларативна. Тож це І хто злий ІМО
mvmn

Можливо, ant-contrib апріорі зло, але використання black3r видається розумним та мурашним, так би мовити.
ssimm

7

Я знаю, що ця публікація справді стара, але тепер, коли пройшло деякий час та версії мурашок, є спосіб зробити це з основними характеристиками мурашок, і я подумав, що я повинен поділитися нею.

Це робиться через рекурсивний макродеф, який викликає вкладені завдання (можуть бути викликані навіть інші макроси). Єдиною умовою є використання фіксованого імені змінної (тут елемент).

<project name="iteration-test" default="execute" xmlns="antlib:org.apache.tools.ant" xmlns:if="ant:if" xmlns:unless="ant:unless">

    <macrodef name="iterate">
        <attribute name="list" />
        <element name="call" implicit="yes" />
        <sequential>
            <local name="element" />
            <local name="tail" />
            <local name="hasMoreElements" />
            <!-- unless to not get a error on empty lists -->
            <loadresource property="element" unless:blank="@{list}" >
                <concat>@{list}</concat>
                <filterchain>
                    <replaceregex pattern="([^;]*).*" replace="\1" />
                </filterchain>
            </loadresource>
            <!-- call the tasks that handle the element -->
            <call />

            <!-- recursion -->
            <condition property="hasMoreElements">
                <contains string="@{list}" substring=";" />
            </condition>

            <loadresource property="tail" if:true="${hasMoreElements}">
                <concat>@{list}</concat>
                <filterchain>
                    <replaceregex pattern="[^;]*;(.*)" replace="\1" />
                </filterchain>
            </loadresource>

            <iterate list="${tail}" if:true="${hasMoreElements}">
                <call />
            </iterate>
        </sequential>
    </macrodef>

    <target name="execute">
        <fileset id="artifacts.fs" dir="build/lib">
            <include name="*.jar" />
            <include name="*.war" />
        </fileset>

        <pathconvert refid="artifacts.fs" property="artifacts.str" />

        <echo message="$${artifacts.str}: ${artifacts.str}" />
        <!-- unless is required for empty lists to not call the enclosed tasks -->
        <iterate list="${artifacts.str}" unless:blank="${artifacts.str}">
            <echo message="I see:" />
            <echo message="${element}" />
        </iterate>
        <!-- local variable is now empty -->
        <echo message="${element}" />
    </target>
</project>

Основні функції, необхідні там, де:

Мені не вдалося зробити змінний перехідник, але це може бути не головним недоліком.


На жаль, цього не вистачає пам'яті і вибухає досить швидко.
Девід Сен-Денис,

Так, це може підірвати ваш стек, залежно від кількості файлів, які ви обробляєте. Я зробив це досить успішно з приблизно 66 файлами (без збільшення можливостей пам'яті). Однак це може спричинити лише варіант, якщо у вас немає доступу до ant-contrib, який пропонує більше функціональних можливостей (наприклад, паралельний запуск).
dag

Це було дуже корисно.
DiamondDrake

0

Ви можете використовувати завдання ant-contrib "for" для ітерації списку файлів, відокремлених будь-яким деліметром, деліметр за замовчуванням - ",".

Далі наведено зразок файлу, який показує це:

<project name="modify-files" default="main" basedir=".">
    <taskdef resource="net/sf/antcontrib/antlib.xml"/>
    <target name="main">
        <for list="FileA,FileB,FileC,FileD,FileE" param="file">
          <sequential>
            <echo>Updating file: @{file}</echo>
            <!-- Do something with file here -->
          </sequential>
        </for>                         
    </target>
</project>

0

Виконайте те, що запропонував blak3r, і визначте цільовий шлях до класу так

<taskdef resource="net/sf/antcontrib/antlib.xml">
    <classpath>
        <fileset dir="lib">
          <include name="**/*.jar"/>
        </fileset>
    </classpath>        
</taskdef>

де lib - це місце, де ви зберігаєте свою банку

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.