вторник, 14 декабря 2021 г.

Что такое сборщик продукта

Когда вы открываете любой сайт — например, google или facebook, вы видите конечный продукт. Но чтобы этот продукт увидеть, и пощупать, нужно:

  1. Написать код приложения

  2. Собрать проект

  3. Поднять его на сервере приложения

Сегодня я расскажу про второй этап. Сборку приложения можно проводить вручную, но есть также специальные инструменты для этого, которые называются «сборщик продукта». О них мы и поговорим.


Ссылка на Хабр (кликабельное содержание там есть)


Что это такое и зачем он нужен

Вася решил стать разработчиком, еще будучи студентом. Он выбрал язык программирования Java и начал его изучать. Тренировался на простых вещах:

— Hello Word

— Калькулятор

Сначала весь код его мини-программ хранился в одном файле — Main.java. Чтобы запустить приложение, достаточно было дважды кликнуть этот файл. И всё!

Потом Вася освоил Page Object Pattern и стал разносить логику по разным классам. Но всё равно это были простые программы, и для запуска оставался главный класс.

А потом Вася... Устроился на работу. Да-да, зеленым новичком, и такое бывает! Старший разработчик Николай на собеседовании разглядел в нем потенциал. Так Вася стал джуниор-разработчиком в компании ООО «Котики».

Проект в компании большой, с семилетней историй. Он состоит из 6000 классов исходного кода, над которыми трудятся 5 разработчиков.

В первый рабочий день Николай подошел к Васе и стал рассказывать:

— Наш код хранится в git. Знаешь, что это такое?

— Ага, система контроля версий!

— Да. Вот тебе ссылочка, скачивай.

Вася скачал. Только вот... Что делать дальше? Какой из 6000 классов запустить, чтобы пощупать приложение?

Николай только ухмыльнулся:

— Нет, так оно только для мелких проектов работает. А тебе нужно:

  • скомпилировать проект — из исходных текстов получить файлы классов с байт-кодом (которые потом будет исполнять JVM);

  • объединить вот эти классы в библиотеку Search.jar;

  • объединить вот эти классы в библиотеку Clean.jar;

  • объединить вот эти классы в библиотеку Update.jar;

  • объединить вот эти классы в библиотеку Report.jar;

  • объединить вот эти классы в библиотеку Link.jar;

  • а вот эти классы...

Вася в шоке выкатил глаза и промямлил:

— Ээээ, подожди... Мне это выучить надо?

Николай рассмеялся:

— В учебных проектах тебе не надо заморачиваться. Запустил конкретный класс, и всё работает. Ну, может, еще одну-две библиотечки собрал, но это тоже несложно.

Но когда приложение растет, растет и список действий. Вручную его выполнять смысла нет — скука, да и только. К тому же человек легко может ошибиться, а в итоге получим неработающее приложение.

Поэтому эту работу автоматизируют. Можно написать скрипт сборки на коленке, но зачем, если уже есть стандартные сборщики? Скажем, для java это ant, maven, gradle... У нас используется maven, почитай пока о нем.

Сборщик уже настроен, так что тебе достаточно зайти через командную строку в директорию проекта и написать команду:

mvn clean install

А дальше он сам всё сделает. На выходе получим cats.war. Это и есть наше приложение, которое мы потом подложим на сервер wildfly, чтобы его запустить.

— Ага, хорошо, спасибо!

Николай ушел, а Вася завороженно смотрел в экран, где в командной строке работал maven. Быстро-быстро бежали буквы по экрану. И вот, наконец, сборщик остановился. Он написал «BUILD SUCCESS». Значит, всё прошло хорошо. И в нужной директории Вася нашел архив «cats.war».

Пока Николая не было, Вася успел нагуглить это новое слово: «maven». И даже смог найти в проекте скрипт сборки, и начал разбираться, что конкретно там происходит. Всё-таки не зря его взяли на работу! Это отличное качество разработчика (да и не только его) — гуглить и копать самому, а не сидеть сложа ручки в ожидании ментора.

Когда Николай вернулся, он подытожил то, что Вася узнал:

— Когда разработчик пишет код — он просто создает набор файликов с текстом. Чтобы эти файлики превратились в работающее приложение, код нужно скомпилировать и запустить.

Что нужно сделать с кодом-источником:

  • скомпилировать проект;

  • объединить классы в файл JAR или другую библиотеку;

  • установить набор зависимостей;

  • ...

  • запустить конкретный класс или несколько классов.

И только после всех этих манипуляций у нас появляется ПО. Набор манипуляций зависит от конкретного проекта, но чем сложнее проект, тем больше действий надо сделать.

А если задача повторяется снова и снова, она становится первым кандидатом на автоматизацию. Ведь нам нужно что? За один шаг получить работающий проект! Этим и занимаются системы сборки!

— О, стой! Так в IDEA (среда для написания кода) же кнопка «Build» есть. Она ведь тоже билдит, то есть собирает код! Да? Это аналог maven-а?

— Не совсем. Это кнопка:

  • компилирует код — проверяет его на наличие простых синтаксических ошибок вида «забыл поставить точку с запятой в конце команды»

  • собирает нужные ресурсы (файлы конфигов и подобные) в одну папочку, из которой уже можно запускать программу

— Так если она все собирает и можно программу запускать, зачем maven нужен?)

— Она не собирает само приложение, так как не знает всех зависимостей. Поэтому нужна отдельная программа-сборщик кода.

— А в чем тогда смысл собирать ресурсы?

— Ну, например, для запуска автотестов. Когда мы их запускаем, там не используется полностью варник (приложение cats.war). Там вызываются конкретные функции + используются конкретные ресурсы типа справочника телефонных номеров.

Разработчик исправил код и нажимает «Build». Это обновляет ресурсы, которые используются в тестах. А если тебе нужен варник приложения, то собираешь maven-ом (ну или аналогом).

Комментарий коллеги-разработчика:

— Диалог правильный, но это подход 10-летней давности. Сейчас всё делегируют maven/gradle. В IDE даже галочка есть отдельная для этого. А уже в maven/gradle делаются гранулярные цели для каждой задачи.

— А вообще главное отличие сборщика от IDE — сборщик можно запустить из командной строки, на любом окружении, независимо от среды разработки + в CI/CD системе (TeamCity, Jenkins и тп). Это воспроизводимый билд.

То есть при желании IDEA можно настроить, чтобы она все это делала. Прописать все зависимости и другое, но это будет нерасширяемо.

 

Как работает сборщик

 

Сборщики работают примерно одинаково. Базовый функционал:

  • компилировать код (проверять на простейшие синтаксические ошибки)

  • создавать и удалять директории

  • группировать файлы

  • ...

Есть и более продвинутые программы, которые также позволяют автоматически извлекать зависимости в библиотеке или автоматизировать тестирование.

Как сборщик поймет, что именно от него нужно? Благодаря скрипту компоновки. В нем разработчик описывает, что конкректно сборщик должен делать.

Давайте посмотрим, как такой скрипт выглядит, на примере Ant. Ant — инструмент компоновки для Java-проектов.

Файл компоновки Ant — это XML-документ. Он разбит на 4 основные части:

  1. Проект

  2. Свойства

  3. Цели

  4. Задачи

В каждом файле компоновки есть проект, и хотя бы одна (умолчательная) цель. Внутри цели находятся задачи — что именно системе надо сделать, если вызвана данная цель.

 

Проект

 

Всё в файле является частью проекта. Поэтому тег <project> является корневым в скрипте:

<project name="folks" default="dist">

где:

  • name — имя проекта

  • default — умолчательная (дефолтная) цель, срабатывающая при запуске скрипта.

Внутри проекта уже находится все остальное — цели, которых мы хотим достичь, и конкретные действия для их достижения.

По-хорошему, у проекта должно быть имя и умолчательная цель. Хотя согласно официальной документации, эти параметры не являются обязательными.

Дефолтная цель сработает по умолчанию, если при запуске ant не указана другая. Поэтому в ней должно быть все нужное для работы проекта.

 

Свойства

 

Свойства Ant похожи на константы. Один раз указали значение, и используем в скрипте хоть в 20 местах. А если значение изменилось, исправить надо будет одно место, а не двадцать. Сюда очень хорошо выносить версии продукта и зависимых библиотек, а также пути к директориям.

Записываются свойства в теге <property>:

<property name="version" value="2.0"/>

<property name="src" location="src"/>

<property name="bin" location="bin"/>

У каждого свойства есть:

  1. имя — name

  2. значение — value, или место — location (если указываем путь к директории)

Свойства можно переиспользовать, записав как ${имя свойства}

<property name="lib-src" location="${src}/lib"/>

 

Цели

 

Цель — это то, что мы хотим получить от сборщика. Краткое название для «я хочу скомпилировать проект» или «я хочу создать все нужные папочки».

Цели записываются в тегах <target>:

<target name=”init”>

<target name=”init” description=”Create the needed directories”>

<target name=”build” depends=”compile”>

Основные атрибуты цели:

  • name — имя цели, которое мы будет указывать в командной строке, поэтому лучше делать его коротким. Обязательный параметр.

  • description — описание цели. Выводится при запросе информации по проекту.

  • depends — от кого эта цель зависит. То есть какую цель надо запустить перед выполнением текущей. Необязательный параметр

В примере цель ”build” зависит от цели ”compile”. Мы не можем начать сборку билда, пока не скомпилировали его. Поэтому, если мы вызываем ant с целью сборки билда, то он:

  1. Выполнит цель ”compile”

  2. Выполнит цель ”build

То есть за один присест можно выполнить сразу несколько целей, если прописать их в блоке depends. В данном случае мы вызвали одну цель, а выполнили две. Можно ли выполнить больше? Можно ли указать сразу несколько зависимостей? Да! Для этого надо записать имена целей через запятую в блоке depends:

<target name="D" depends="C,B,A"/>

Call-Graph:  A → B → C → D

Обратите внимание на граф вызова целей. Исходно мы хотим выполнить цель D. Читая атрибут depends, можно подумать, что первой будет вызвана цель C, потом B, и потом A. Это не так! Читать блок depends надо так:

  • C зависит от B

  • B зависит от A

Таким образом, сначала мы вызываем цель A, потом B, и только потом C. То есть фактически читаем справа налево этот атрибут.

У цели могут быть и другие атрибуты. Например, условие «if». Но подробнее о них читайте в официальной документации Ant — статья «Targets».

 

Цель по умолчанию

Какой должна быть цель по умолчанию? Должна ли проводиться компиляция, группировка, генерирование документации или все вместе?

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

Но для выполнения всех операций надо использовать ключи шифрования, установщики типа InstallShield и т.д. Стоит ли париться? Многие ставят целью по умолчанию вывод справки по проекту, чтобы новые люди сообразили, что им нужно делать.

  

Задачи

 

Задача — это конкретное действие, которое нужно выполнить, чтобы достичь поставленной цели. Например, создать директорию, скопировать файлы, скомпилировать Java-код...

В Ant задача обычно отражает определенную команду: «javac», «mkdir» или даже «javadoc». Именно эта команда и будет названием тега:

<mkdir dir=”bin”/> --- создать директорию «bin»

Эти теги всегда будут внутри тегов <target>. Задача всегда относится к какой-то цели. А вот цель может не иметь задач. Правда, тогда она будет бесполезная =)

Внутри цели может быть одна задача. Например, для компиляции проекта нам нужно скомпилировать java-код в конкретной директории:

<target name=”compile” depends=”init” description=”compile project”>

    <javac srcdir=”src” destrdir=”bin”/>

<target>

При вызове цели compile будет вызвана задача javac. Она компилирует код java в srcdir и складывает классы в destrdir.

Внутри цели может быть несколько задач, как одинаковых, так и разных. Например, мы хотим создать все нужные проекту директории, а их несколько. Вызываем несколько раз задачу mkdir. Она создает директорию, определяемую атрибутом dir:

<target name=”init” description=”Create the needed directories”>

   <mkdir dir=”bin”/>

   <mkdir dir=”lib”/>

<target>

При вызове цели init у нас будут созданы 2 директории — ”bin” и ”lib”.

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

<target name=”init” description=”Create the needed directories”>

   <mkdir dir=”bin”/>

   <mkdir dir=”lib”/>

   <copy file=”test.jar” todir=”lib”/>

<target>


Стандартная версия Ant содержит более 150 заданий. Вот некоторые из них:

  • echo – вывод сообщений в консоль

  • mkdir – создание директорий

  • copy — копирование файлов

  • delete – удаление файлов и директорий

  • move – перемещение файлов и директорий

  • replace — замещение фрагментов текста в файлах

  • javac – компиляция Java–кода

  • java – запуск class и jar файлов

  • jar – создание jar файла

  • junit – запуск тестов

  • exec — выполнение внешней команды

  • zip — создание архива в формате Zip

 

Доп ссылки

Writing a Simple Buildfile — официальная дока на английском

Apache Ant – быстрый старт — статья на Хабре

Apache Ant — википедия

Сборка Java приложения, ant

 

Как запустить сборку

 

Все зависит от сборщика. Но обычно это название сборщика + название цели, которую мы запускаем. Для Ant это выглядит так:

ant name_of_target

Целей может быть несколько:

ant name_of_target_1 name_of_target_2 name_of_target_3

Фишка ant в том, что вы можете назвать цель как угодно:

ant pupsik

ant mario

ant iron_man

…

С одной стороны, это звучит даже забавно:

— Запускайте Марио!

И все вокруг знают, что это означает пересобрать проект... Но такие хиханьки-хаханьки хороши лишь в умеренных дозах. Подумайте сами — если каждую цель назвать как-то «невпопад», то придется просто заучивать эти названия. Что неудобно.

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

Поэтому обычно используют стандартные названия целей

  • clean — чистка проекта: удаление файлов заготовок, которые остаются после компиляции. Приводит проект к виду, в котором он хранится в репозитории

  • compile — компиляция исходного кода

  • test — запуск тестов

  • install — установка пакетов в локальный репозиторий, чтобы использовать зависимости от других проектов локально

  • ...

Посмотрим на примере, который можно взять и пощупать — в проекте folks используется сборщик maven. А в статье «Как собрать проект и запустить тесты» (в старой версии, там возникли проблемки со сборщиком после обновления и команды заменили) мы видим такие команды:

mvn clean install -Dmaven.test.skip=true -U

mvn clean test

Эти команды — типовые для maven-а. Давайте разберемся, что они означают.

 

mvn

Это сокращение от maven. Название сборщика, к которому мы обращаемся.

Первое слово, которое вы пишете в командной строке — это вызов команды. Это может быть путь к самой программе вида:

D:\allpairs\pict\pict.exe

А может быть просто название команды:

pict

mvn

В этом случае система (я говорю про винду) полезет в PATH и проверит: а есть ли там такое? Если есть, то запустит программу и по короткой команде. Подробнее про PATH можно почитать здесь.

Когда вы устанавливаете maven (ant или любой другой сборщик), вы прописываете путь к нему в переменную PATH. По крайней мере, в инструкции по установке есть такой пункт. Именно благодаря этому пункту команда «mvn» в командной строке начинает работать.

По сути, тут все то же самое, что и в ant — мы пишем название сборщика (mvn) и те цели, которые мы хотим запустить. Точнее, фазы. В maven это называется phases, и набор фаз вполне определенный. В этом отличие сборщиков, ant более гибкий и цели мы можем настраивать и называть так, как захотим. В maven не можем.

 

clean

В обеих командах сначала вызывается фаза clean. Она нужна для того, чтобы очистить созданные другими сборками артефакты. Так как я тестировщик, то объясню с точки зрения тестирования.

Допустим, мы запускаем автотесты. Их можно запускать пачкой (вообще все или из конкретной папки), а можно запускать один конкретный. Во время прогона система создает артефакты — это может быть состояние базы данных или поискового индекса, временные файлики, или что-то еще.

Теперь, когда мы просто запускаем тест, без clean, система переиспользует созданные артефакты. И это хорошо! Потому что так тесты гоняются намного быстрее. Система уже не тратит время на повторное создание артефактов.

Это как если у вас перед готовкой пустой холодильник или полный. Если пустой — надо идти в магазин. Если вы уже вчера туда сходили и продукты все есть, можно сразу приступать к готовке!

Да, переиспользование артефактов — это хорошо. Но иногда их надо почистить. Вот, скажем, если у вас яйца протухли, то и омлет станет отравой, и шарлотку есть будет нельзя. Любое блюдо будет испорчено из-за одного плохого ингредиента. Баг в исходнике «сломал» готовый продукт!

Что мы делаем в таком случае? Испорченные яйца выкидываем, идем в магазин за новыми. Примерно так и работает clean — чистит наш холодильник. Единственная проблема — она чистит всё. Удалять так удалять!

Фаза удаляет все артефакты, созданные предыдущими сборками. То есть система возвращается в состояние на момент забора данных из репозитория. Плюс твои локальные изменения кода (именно исходного кода, а не сгенеренных по нему артефактов).

Фазу можно и не вызывать, но это надо делать с полным осознанием своих действий. Чтобы потом не тупить над тем, почему это вдруг разработчик исправил баг и у него все хорошо, а у вас до сих пор плохо.

Понимаете, часто повторяющиеся действия мы делаем на автомате, не задумываясь о них. Допустим, что мы привыкли утром очищать свой репозиторий и прогонять

mvn clean install

А потом, в процессе дня, мы фазу clean уже не вызываем:

mvn install

mvn test

...

Так вот, в коде обнаружился баг, который из кода «переехал» в артефакты. Разработчик баг исправил, даже проверил у себя локально — работает! Отдает вам, вы пересобираете проект — не работает! Как так?? Вы же пересобрали! Ведь вызвали же фазу «install».

При этом иногда над простыми ошибками тупят дольше всего. Разработчик снова ковыряется в коде, тратит время — должно же работать. Вы снова собираете — нет, не работает! И только когда разработчик приходит посмотреть, что именно вы делаете, он обращает внимание на команду:

— Стой! Ты же clean не делаешь!

— Ну да, я его только по утрам вызываю...

Это называется «делать на автомате», не задумываясь о своих действиях. Если вы не хотите думать над командой по сборке, то всегда вызывайте clean, не прогадаете. Да, сборка чуть дольше будет идти, но вы точно не огребете такую проблему.

Аналогичная ситуация может возникнуть при прогоне автотестов. Прогнали один тест — все хорошо, он работает. Прогоняете другой — он падает, причем очень странно. Тест пишет, что ожидает «Иванова», которого в вашем тесте нет вообще! Откуда же он взялся??

Да просто первый тест не почистил базу за собой! И Иванов остался в артефактах. А вы не вызвали фазу clean для экономии времени. Отсюда и баг. Поэтому при подозрительных падениях тестов из серии «откуда он взял эти данные» стоит перепрогнать тест с фазой clean.

 

install

Это сборка продукта. Folks нельзя развернуть и потыкать, но если бы можно было, то это выглядело бы именно так:

  1. Скачали исходный код продукта

  2. Выполнили команду «mvn clean install»

  3. Забрали готовый билд в определенной папочке!

Хотя по сути своей фаза install ничего не собирает. Она публикует собранный артефакт в локальном репозитории. Смотрите — в maven есть свой жизненный цикл (подробнее см в статье «Maven in 5 Minutes», раздел «Maven Phases»):

  • validate

  • compile

  • test

  • package

  • integration-test

  • verify

  • install

  • deploy

И когда вы вызываете какую-то команду, maven идет по своему жизненному циклу и выполняет все, что идет перед вызванной фазой:

  • Вызвали compile — система выполнит validate, а потом compile.

  • Вызвали test — система выполнит validatecompile, а потом уже test.

Таким образом, когда мы вызываем install, система на самом деле выполняет кучу других фаз. В том числе и package, который собственно собирает артефакт (jar-файл, war-файл, или что-то еще).

 

-Dmaven.test.skip=true

Это свойство, которое помогает пропустить тесты во время выполнения вызванной фазы. В нашем случае вызывается:

mvn clean install -Dmaven.test.skip=true

Это означает, что мы хотим собрать проект, не прогоняя при этом тесты. В большом проекте, где много тестов, именно так и делают. Потому что есть разница:

  1. Собрать проект — 2-5 мин

  2. Прогнать все тесты — 30+ мин

Если разработчик исправил баг в коде и вам просто нужна свежая сборка, вы собираете ее с флагом -Dmaven.test.skip=true. А система CI при этом прогоняет все автотесты.

У меня на одном из проектов в системе CI задачи были разделены — одна собирает только билд, с флагом -Dmaven.test.skip=true, а вторая честно прогоняет все тесты. И если в коде есть баг, из-за которого даже билд не собирается, мы узнаем об этом через 2 минуты, а не через 32. Удобно =)

 

-U

Это флаг означает принудительное обновление зависимостей.

Чем больше проект, тем больше там зависимостей. Это могут быть зависимости от каких-то стандартных библиотек, или от других ваших проектов. Вот, скажем, у меня на одной работе было несколько проектов. Чтобы не дублировать код, разработчики создали проект Utils, куда вынесли общие классы, которые никак не привязаны к бизнес логике проекта:

  • скопировать директорию;

  • распаковать архив;

  • и т.д

Проект использует код из Utils. И если разработчик вносил правки в Utils, то наш проект надо собирать обязательно с флагом -U. Иначе изменения не подтянутся и система продолжит работать по старому.

Разработчики используют флаг сознательно. Они ведь меняют код и понимают, нужно обновлять зависимости или нет. А тестировщики в проект Utils обычно не лезут, и за изменениями в нем не следят. Поэтому нас учили так — всегда используй флаг -U, не прогадаешь. И на первом этапе знакомства со сборщиком этого вполне достаточно =)

 

test

Фаза test запускает автотесты. Причем если запускать без параметров:

mvn clean test

То система прогонит ВСЕ тесты. Разумеется, это не всегда то, что нам надо. Вот, например, у нас в компании 4000 модульных автотестов. Прогоняются они за полчаса. Мне выдали новый функционал на тестирование, я пишу автотесты. Как прогнать только свеженаписанный тест? Чтобы узнать ответ через полминуты, а не полчаса?

Для этого есть параметры:

  • -Dtest — запустить Java-класс с тестом. А Java-класс обычно описывает логику автотестов, например — «Внутри этой директории тесты на поиск. Возьми данные из файла с таким названием, положи в базу. Потом возьми вот эту строку и по ней ищи».

  • -Dtest.case — запустить конкретный тест-кейс. Это уже точечная проверка: «В базе есть Маша и Ваня, поиск по «Ваня» Ваню вернет, а Машу нет» (это особенность фреймворка folks, а не работа из коробки maven). 

Итого

Для запуска сборки вам надо ввести в командную строку название сборщика + название цели, которую мы хотим выполнить. Иногда цели могут называться как ваша левая пятка того пожелает (ant), а иногда это набор вполне конкретных значений (maven).

 

Что сборщик умеет делать

  

1. Компилировать проект

Это самое важное в сборщике. В большинстве случаев нам нужно, чтобы при помощи одной команды делалось всё — от установки до упаковки проекта в пакеты.

2. Чистить все ненужное

Для этого обычно создают цель clean. Она нужна для наведения порядка после компоновки проекта — удаляет директории, созданные по ходу процесса компоновки, или артефакты тестирования.

3. Выводить информацию о проекте

Гуглить — это, конечно, хорошо. Но не всегда помогает. Для maven вы можете нагуглить команду сборки, а как быть с Ant? Что, если коллеги назвали цель сборки «SuperMario»? В сборщике должен быть:

  • Мануал — информация о том, как его использовать.

  • Информация о проекте — список доступных целей, описание каждой и цель, установленная по умолчанию.

В Ant для вывода информации о проекте достаточно напечатать команду:

ant -projecthelp

4. Обращаться к библиотекам, используемым в проекте

Это только на очень небольшом самописном проекте можно обойтись без сторонних библиотек. Да и то придется городить костыли и придумывать велосипеды. А зачем, когда есть готовые библиотеки? Сборщик должен уметь с ними общаться.

В Ant можно добавить библиотеки в путь компоновки, используя элемент «classpath» в задаче «javac»:

<javac srcdir=”src” destdir=”bin”>

  <classpath>

     <pathelement location=”libs/junit.jar”>

     <pathelement location=”libs/log4j.jar”>

  </classpath>

</javac>

В данном примере мы подключаем к проекту 2 библиотечки:

  • junit — для автотестов;

  • log4j — для логирования;

Каждый элемент пути (pathelement) указывает на добавление отдельного jar-файла. Но можно указать на директорию со внешними библиотеками.

Также сборщики умеют загружать библиотеки при помощи FTP, HTTP, SCP и т.д.

 

5. Запускать приложение

Запуск приложения может оказаться нетривиальной задачей. Например, для этого нужно прописать ряд переменных в разных файлах, или много разных опций в командной строке. Человек ошибается, робот — нет. Отдайте ему эту работу.

 

6. Запускать тесты

В идеале у каждого проекта должны быть автотесты, гоняемые при любом изменении кода. Увы, пока это остается лишь мечтами — нет бюджета, времени, да и просто не нужно для временного проекта.

Но если автотесты есть, сборщик должен уметь их запускать. Именно это мы и делаем в folks — пишем команду:

mvn clean test

И уже сборщик (maven) запускает прогон автотестов.

 

7. Другие задачи

Еще несколько примеров того, что может делать сборщик:

  • Брать код для внесения исправлений

  • Копировать компоновку в архивные директории

  • Шифровать файлы

  • Высылать сообщения на эл почту, когда закончена компоновка

  • Запускать SQL

Автоматизация позволяет сфокусироваться на коде, а не на повторяющихся задачах. С хорошим скриптом компоновки мы можем автоматизировать этот крайне сложный процесс.

Иногда для одного проекта есть несколько файлов компоновки, по одному для каждой библиотеки или компонента. И тогда нужен основной файл компоновки, который это все объединит.

 

Где хранить скрипт компоновки

 

Скрипт компоновки — это тоже код. Так что положите его в систему контроля версий.

Теперь, если на проект придет новый разработчик, у него не будет проблем! Выкачал из системы контроля версий исходный код и скрипт компоновки, прочитал скрипт и запустил нужную опцию!

 

Программы сборки

Для java — Ant, Maven, Gradle

.Net — NAnt

Ruby — rake

В Microsoft Visual Studio – MSBuild

При использовании Perl или PHP скрипты компоновки не так полезны. Но можно использовать инструмент для компоновки, даже если нужны не все его функции.

 

Как выбрать программу для сборки

 

На что ориентироваться при выборе сборщика? На то, что он умеет и насколько геморройно будет это настроить! А главное, что из этих функций нужно именно вам.

Иногда можно вообще обойтись с помощью командного файла. Ну а что, прописать всё те же команды через bash — создай файл, удали директорию... Но в системах компоновки есть много специальных задач, ориентированных на разработку. И в командном файле их придется писать самому. А зачем, когда есть готовое решение?

В статье я приводила примеры по двум инструментам для Java-проектов — Ant и Maven. Как выбрать из них?

Ant — очень простой инструмент компоновки. Может компилировать код, создавать и удалять директории, группировать файлы. Ant ничего не знает о жизненном цикле продукта. Он просто выполняет те задачи, которые вы пропишете в скрипте компоновки.

Однако Ant сейчас лучше не использовать. Он совсем устаревший, да и в нем нет из коробки поддержки зависимостей. Его используют на проектах, где «так повелось», где он был исходно. Ну или фриланс-разработчики, которые делают совсем небольшой проект.

У нас на  одном из проектов был Ant. В целом, свои функции он выполнял. Все скрипты уже давно написаны, так что жили по принципу «Работает? Не трогай».

С другой стороны, делать что-то новое намного сложнее, чем на более гибких инструментах. В итоге переехали на gradle. Да, переезд занимает время, но оно окупается, как и любой рефакторинг.

Maven — более многофункциональный инструмент. Он знает про жизненный цикл продукта. Знает, что и в каком порядке нужно делать. И умеет кучу всего, что Ant не делает:

  • автоматическое извлечение зависимостей в библиотеке;

  • автоматизация тестирования;

  • и т.д.

Это замечательный инструмент, но он предоставляет слишком много возможностей. А это не всегда хорошо ))

К тому же для любого действия maven «выкачивает полинтернета», как называют это мои коллеги. Ну да, он умеет автоматически извлекать зависимости. И он будет это делать, даже если вам надо прогнать один небольшой автотестик!

Чтобы выжать их Maven максимум, проект нужно структурировать определенным образом. Пример можно посмотреть все в том же folks. Эта структура:

  • main

  • test

То есть когда у тебя есть «главная» папка проекта или какого-то модуля, а в ней отдельно код, отдельно тесты — это как раз структура, которую требует maven. И намного проще писать проект под maven c нуля, чем переписывать уже существующий. Миграция — это жизньболь.

Gradle — более гибкий, позволяющий легко на языке  программирования (groovy/kotlin) дописать сложную логику сборки

При работе с небольшими проектами Ant обеспечит весь необходимый функционал. И проще выбрать его. Если нужно больше возможностей, осознанно выбираем Maven. Если нужно больше возможностей, но что-то погибче, тогда смотрим на Gradle.

 

Итого по выбору

Как выбрать — вопрос неоднозначный. В принципе, если команда уже освоила какой-то сборщик и он всех устраивает, то его можно и использовать.

  • maven более декларативный и простой (требует только понимания xml)

  • gradle более гибкий, позволяющий легко на языке  программирования (groovy/kotlin) дописать сложную логику сборки, но скорее всего среди энтерпрайза встречается реже

  • ant лучше не юзать уже, он устаревший, да и в нем нет из коробки поддежки зависимостей

 

Итого

 

Когда разработчик пишет код — он просто создает набор файликов с текстом. Чтобы эти файлики превратились в работающее приложение, нам нужно выполнить ряд действий:

  • скомпилировать проект;

  • объединить классы в файл JAR или другую библиотеку;

  • установить набор зависимостей;

  • запустить конкретный класс или несколько классов.

И только после всех этих манипуляций у нас появляется ПО. Набор манипуляций зависит от конкретного проекта.

Можно выполнять манипуляции вручную, но проще использовать готовое решение. Программу для сборки продукта. Тогда это все будет работать при выполнении одной команды, например:

ant install

Сборщик можно запустить из командной строки, на любом окружении, независимо от среды разработки + в CI/CD системе (TeamCity, Jenkins и тп).

См также:

Управление разработкой ПО. Дэн Пилон, Расс Майлз — отличная книга о процессе разработки, в том числе и о сборщиках там есть

 

См также другие статьи из цикла «Что такое...»:

Что такое VCS (система контроля версий)


Что такое API

Что такое XML

Что такое JSON


Что такое клиент-серверная архитектура

Что такое CI (Continuous Integration)

Что такое транзакция


Что такое регулярные выражения (regexp)

Что такое Docker

PS — это выдержка из моей книги терминов, написана в помощь студентам моих курсов

Комментариев нет:

Отправить комментарий