Apache Ant – быстрый старт

Apache Ant должен быть знаком каждому Java-программисту: это популярный инструмент сборки ПО (build tool), полностью написанный на Java. Ant использует сценарий, который является обычным XML-файлом. Несмотря на его Java-направленность, веб-разработчики тоже используют этот инструмент.

Мы пройдём пять простых шагов, чтобы начать использовать Ant:

  1. Скачаем, установим и проверим.
  2. Напишем простой HelloWorld сценарий.
  3. Разберёмся с принципами работы и XML форматом сценария сборки.
  4. Узнаем минимально необходимый список заданий.
  5. Напишем сценарий для полного цикла сборки и тестирования учебного проекта.

Нам потребуется Java SE Development Kit (JDK, скачиваем по адресу www.oracle.com/technetwork/java/javase/downloads), ваш любимый текстовый редактор (в Linux – vi/vim/gedit, для Windows рекомендую Notepad++) и базовые навыки работы в командной строке. Сценарии сборки и примеры на Java протестированы в Linux (Simply Linux 7.95.0, CentOS Linux 6.8) и в Windows (XP/7). Использование Ant одинаково и в Linux и в Windows.

1. Скачиваем, устанавливаем, проверяем


Linux: устанавливаем из репозитария командой вроде sudo apt-get install ant (замените apt-get на yum если необходимо). Важно: нам нужна версия не ниже 1.8.*. В репозитарии CentOS 6.8 версия 1.7.1, поэтому лучше использовать скрипт, описанный в предыдущей статье.

Windows: посещаем веб-сайт ant.apache.org, заходим в раздел Download/Binary Distributions и скачиваем архив apache-ant-1.10.1-bin.zip (возможно сейчас есть уже более свежая версия). Содержимое архива копируем в любой каталог, например в «C:\Program Files\Apache Ant». Затем добавляем путь к каталогу bin (C:\Program Files\Apache Ant\bin) в системную переменную Path.

Проверяем работоспособность, вызвав ant в командной строке:
$ ant -version
Apache Ant(TM) version 1.10.1 compiled on February 2 2017

Если аналогичное сообщение получено – всё в порядке.

2. Пишем HelloWorld сценарий


<?xml version="1.0"?>
<project name="HelloWorld" default="hello">
    <target name="hello">
        <echo>Hello, World!</echo>
    </target>
</project>

Создаём в домашнем каталоге подкаталог hello (в Linux это делает команда mkdir, в Windows – md) и сохраняем туда файл с именем build.xml, содержащий предложенный выше сценарий. Переходим в этот каталог и вызываем ant:
$ mkdir hello
$ cd hello
$ ant
Buildfile: /home/lamp/hello/build.xml

hello:
[echo] Hello, World!
BUILD SUCCESSFULL

Total time: 0 seconds

Что произошло? Ant нашел файл сценария с именем по умолчанию (build.xml) и выполнил target c именем hello, также указанный по умолчанию в теге project с помощью атрибута default (обратите внимание что в теге project мы ещё указали имя проекта, используя атрибут name). Мы получим такой же результат, если при вызове ant укажем в качестве параметра hello:
$ ant hello

3. Основные принципы работы


Сценарий сборки – обычный XML-файл. Текст открывается (и закрывается) тегом project, в котором можно указать имя проекта и цель по умолчанию. Далее он содержит определение целей (target), зависимостей (depends) и свойств (property). Простейший сценарий должен иметь хотя бы одну цель. В теге target мы описываем вызов одного или нескольких заданий (tasks). Для target можно задать имя с помощью атрибута name (name=«name_of_target»). Заданное имя становится командой для нашего сценария и вызвать соответствующий target можно так:
$ ant name_of_target

В target есть возможность указать зависимость с помощью атрибута depends. Зависимости связывают target'ы между собой. Например, есть target c именем “compile”, а есть – с именем “run”, зависимый от “compile”. И если мы захотим выполнить “run”, сначала выполнится “compile”.

4. Минимально необходимый список заданий (tasks)


Стандартная версия Ant содержит более 150 заданий (https://ant.apache.org/manual/tasklist.html). Нам пока потребуются только семь:

  • echo – вывод сообщений в консоль
  • mkdir – создание директорий
  • delete – удаление файлов и директорий
  • javac – компиляция Java–кода
  • java – запуск class и jar файлов
  • jar – создание jar файла
  • junit – запуск тестов

5. Сценарий для сборки и тестирования Java проекта


Ant предоставляет полную свободу в формировании структуры каталогов. Создаём в нашем каталоге hello подкаталог src для исходных текстов на Java:
$ mkdir src

И сохраняем туда файл HelloWorld.java следующего содержания:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

А затем немного усложняем текст нашего сценария (build.xml):

<?xml version="1.0"?>
<project name="HelloWorld" default="run">
    <target name="compile">
        <mkdir dir="build/classes"/>
        <javac destdir="build/classes" includeantruntime="false">
            <src path="src"/>
        </javac>
    </target>
    <target name="run" depends="compile">
        <java classname="HelloWorld" classpath="build/classes"/>
    </target>
    <target name="clean">
        <delete dir="build"/>
    </target>
</project>

Теперь сценарий содержит три target (команды): compile (компиляция файла(ов) .java), run (запуск файла .class), clean (удаление папок с результатами компиляции). При этом compile содержит два tasks – mkdir и javac. Обратите внимание на зависимость: target run предварительно вызовет compile. Кроме того run – это target по умолчанию для проекта.

Запускаем сценарий без параметров и видим результат работы Java программы: Hello, World!

Прямое указание имен каталогов не говорит о хорошем стиле. Особенно если имена в сценарии повторяются. Модифицируем build.xml, используя property (обратите внимание, как нам пригодилось имя проекта, заданное в project) и добавив пару комментариев:

 <?xml version="1.0"?>
 <project name="HelloWorld" default="run">
     <!-- define names of directories -->
     <property name="src" location="src"/>
     <property name="build" location="build"/>
     <property name="classes" location="${build}/classes"/>
     <!-- define all targets -->
     <target name="compile">
         <mkdir dir="${classes}"/>
         <javac srcdir="${src}" destdir="${classes}" includeAntRuntime="false"/>
     </target>
     <target name="run" depends="compile">
         <java classname="${ant.project.name}" classpath="${classes}"/>
     </target>
     <target name="clean">
         <delete dir="${build}"/>
     </target>
 </project>

Теперь добавим в сценарий target package для формирования jar файла:

<target name="package" depends="compile">
    <jar destfile="${build}/${ant.project.name}.jar" basedir="${classes}">
        <manifest>
            <attribute name="Main–Class" value="${ant.project.name}"/>
        </manifest>
    </jar>
</target>

и убедимся что всё работает:
$ ant package
$ java -jar build/HelloWorld.jar
Hello, World!

Перейдём к тестированию. Изменим код проекта (чтобы было что тестировать):

public class HelloWorld {
    public static void main(String[] args) {
        HelloWorld hello = new HelloWorld();
        System.out.println(hello.sayHello());
    }
    String sayHello() {
        return "Hello, World!";
    }
}

и добавим в каталог src файл/класс TestHello.java с простым тестом:

import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class TestHello {
    @Test
    public void testHello() {
        HelloWorld hello = new HelloWorld();
        assertEquals("Hello, World!", hello.sayHello());
    }
}

Используя информацию со страницы https://github.com/junit-team/junit4/wiki/getting-started скачиваем два файла, junit-4.12.jar и hamcrest-core-1.3.jar и копируем их в каталог нашего JDK/jre/lib/ext (такую команду копирования используем в CentOS 6.8):
$ sudo cp ~/Downloads/*.jar /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.121-0.b13.el6_8.i386/jre/lib/ext/

Теперь можно проверить как работает тест в командной строке:
$ java -cp build/classes org.junit.runner.JUnitCore TestHello
JUnit version 4.12
.
Time: 0,281
OK (1 test)

Добавляем в наш сценарий ещё один target – test:

<target name="test" depends="compile">
    <junit>
        <classpath>
            <pathelement location="${classes}"/>
        </classpath>
        <test name="TestHello"/>
    </junit>
</target>

и дополняем строку в target package (jar):

<jar destfile="${build}/${ant.project.name}.jar" basedir="${classes}" excludes="Test*.class">

Теперь к списку команд нашего сценария (compile, run, package, clean) добавилась test.
В заключение меняем код нашего проекта так, чтобы приветствие выводилось в отдельном графическом окне. Затем формируем jar файл и запускаем его двойным кликом мыши (у вас должно быть настроено выполнение jar файлов по двойному клику).

Третья версия Java кода:

import javax.swing.*;
import java.awt.*;
public class HelloWorld extends JFrame {
    public static void main(String[] args) {
        new HelloWorld();
    }
    HelloWorld() {
        setTitle(sayHello());
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setBounds(200, 200, 300, 200);
        JLabel label = new JLabel(sayHello(), SwingConstants.CENTER);
        label.setFont(new Font("", Font.BOLD, 24));
        add(label);
        setVisible(true);
    }
    String sayHello() {
        return "Hello, World!";
    }
}

Слегка дополняем сценарий (в target run), дописав fork=«true» (запуск выполнения класса в другой виртуальной машине). В противном случае run не сработает (проверено экспериментально):

<java classname="${ant.project.name}" classpath="${classes}" fork="true"/>

Выполняем команду формирования jar файла ($ ant package), открываем в проводнике файлов каталог ~/hello/build, находим там HelloWorld.jar, дважды кликаем по нему мышкой и получаем удовольствие от созерцания графического окошка с приветствием.
Метки:
Поделиться публикацией
Похожие публикации
Комментарии 31
  • +14
    Аж слеза прокатилась по щеке.
    • +4

      От воспоминаний о хороших нечитаемых build.xml на 5-10к строк?

  • 0
    Но статья будет не полной, если не упомянуть Apache Ivy
    http://ant.apache.org/ivy/
    • –4
      Спасибо, учту. Хотя в ближайший планах аналогичная статья по Maven.
      • +11

        Не надо, 105 статья про указание первой зависимости в maven нафиг никому не нужна.

  • +17
    Посмотрел в календарь. Вроде 2017 на дворе.
    • +5
      Откровенно говооря, тоже не понял, нафига сегодня об этом писать. Кому-то нужен учебный материал? Ну так извините, вот же оно: оно, нет? При этом еще в 2010 написано — и более подробно.
      • +2

        При этом еще в 2010 году в комментариях все обсуждают, что ant уже безнадежно неактуален и смысла о нем говорить нет ;)

        • 0
          Комментарии к той статье очень забавные.
        • +2
          Именно. Уже тогда это было ясно. А сегодня я бы лично за попытку применить ant для такого, как тут описано, уже бил бы по рукам.

          Потому что если у вас типовой проект — maven, если что-то нестандартное, и хочется скриптов — gradle/sbt/что угодно из той же оперы, но не ant. Все что надо про него знать — что оно еще существует, потому что в свое время много понаписали.
      • –3
        Именно так, учебный материал, статья писалась в рамках подготовки к вебинару для самых начинающих. Пример в той статье (я, кстати, предварительно с ней ознакомился и не только с ней) слишком сложен для новичков.
  • +3
    Помолодел прям :) А примеры для Win никого не удивили? Слеш вот этот в обратную сторону…
    • 0
      Думаю доработать, добавив варианты для Linux.
    • +1
      Примеры для win? Это вы про «скопируйте в Program Files»? А ниче что оно скорее всего не сработает — прав не хватит туда скопировать?
    • 0
      Переработал материал в сторону Linux.
  • +3

    Пожалуйста, не нужно продолжать использовать эту древнюю тулзовину. Есть гораздо более адекватные современности вещи; если хочется лёгкой императивности, то можно взят тот же Gradle.


    Брр, как вспоминаю рекурсивные антовые сборки из сотен файлов по несколько тысяч строк...

    • +1
      Спасибо за замечанию, обязательно учту на будущее.
    • –2

      Ant, как инструмент, может и древний, но ведь работает. Молоток тоже не вчера изобрели. А зная, что такая штука имеется, можно облегчить себе жизнь. Например в gradle: https://docs.gradle.org/current/userguide/ant.html

  • +2
    junit-4.12.jar и hamcrest-core-1.3.jar и скопируем их в каталог нашего \jre\lib\ext

    А потом в другом проекте, использующем другие версии артефактов, всё поломается.

    Не надо ничего складывать ext-каталог, если без этого можно обойтись. А обойтись можно в 145% процентах случаев.
    • 0
      Не надо ничего складывать ext-каталог, если без этого можно обойтись. А обойтись можно
      Поделитесь — как бы вы поступили в данном случае?
      • 0
        Предпочтительнее использовать ivy.

        Без ivy, сделать каталог в проекте, в который складывать необходимые зависимости. При компиляции засовывать этот каталог в classpath. Тэг в ant для этого есть.
        • 0
          использовать ivy

          Спасибо, попробую. Что порекомендуете прочесть для быстрого старта?
          • 0
            Официальную документацию, как и всегда Нет, лучше ничего про ivy не читать, почитай лучше про Gradle, можно даже на Хабре :)
            • 0
              ок, спасибо, так и сделаю. Как вообще думаешь, можно что-то простое сделать с Gradle, чтобы показывать начинающим?
              • 0
                Я обычно использую его примерно так, чтоб утрамбовать внутрь все внешние зависимости. Если зависимостей нет, как в твоем случае, то все еще проще. может быть достаточно чего-нибудь в таком духе.
  • 0

    Зашел, почитал, поностальгировал (эх бесшабашная молодость, джарки в либочке и или флешочкой обменивались или все в CVS/SVN зато можно было кофе пойти попить пока новый проект чекапился), закрыл, вернулся к домашнему уютному gradle(работа) и sbt(дом).

  • –1
    А вот если необходимо продемонстрировать Заказчику сборку проекта из исходников на компьютере без интернета.
    Куда тут без анта?
    • 0

      Какая разница, таскать кусок .m2/repository или lib, если у вас внешние зависимости есть? А если нет, то mvn clean install.

    • 0
      C gradle все равно будет проще:

      dependencies {
          compile fileTree(dir: 'lib', include: ['*.jar'])
      }
      

      Это если вас не заломает вытягивать и раскладывать зависимости руками, иначе частичное зеркало maven (например, средствами nginx proxy_pass и proxy_store) тоже вполне себе вариант.
      • 0

        В чём проблема дёрнуть mvn dependency:copy-dependencies для того же?

    • –1
      Вот только все решения требуют наличия
      1. всех зависимостей сложенных в кучу (каталог или репозиторий)
      2. предварительно написанного скрипта

      и в итоге получаем проблему попа и попадьи

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