Вводная часть (со ссылками на все статьи)
Лет 10-15 назад, когда программы состояли из исходников и небольшого количества двоичных файлов с работой по сборке итоговых программ отлично справлялись всевозможные «?make». Однако сейчас современные программы и подходы к разработке сильно изменились – это:
множество различных файлов (не считаю исходников) – стили, шаблоны, ресурсы, конфигурации, скрипты, бинарные данные и.т.д;
Причём через этапы сборки итоговых или промежуточных артефактов с использованием перечисленных средств и методик среднестатистическому разработчику приходится проходить по несколько раз в день. Запуск командных файлов руками (с возможной проверкой результатов) в данном случае слишком обременительная процедура: необходим инструмент, отслеживающий изменения в ваших данных проекта и запускающий необходимые средства в зависимости от обнаруженных изменений.
Мой путь в использовании систем сборки был непонятное_количество_*make -> ant -> maven -> gradle (то обстоятельство, что android Studio под капотом использует gradle меня сильно порадовало).
Gradle меня привлёк:
Ознакомиться с возможностями Gradle можно на сайте разработчиков в разделе документации (можно узнать всё!). Для тех, кто хочет сравнить gradle и maven есть интересное видео от JUG.
В моём случае скрипты для сборки выглядят таким образом:
,
где:
В такой конфигурации все изменения скрипта сборки производятся в централизованных файлах (не даются на откуп каждому проекту) и могут/должны корректироваться централизованно лицом, ответственным за сборку проекта.
Из интересных вещей/советов, которыми я хотел бы поделиться, при настройке gradle в моём проекте есть следующие:
Запуск всего этого богатого функционала с консоли одной командой, при которой определяются низменные фрагменты проекта, обновляются необходимые зависимости, выполняются необходимые проверки и генерируется требуемые артефакты позволяют говорить о системах сборки как об инструментах, которые при правильной настройке могут радикально изменить скорость доставки вашего проекта до конченых потребителей.
Спасибо за внимание!
Лет 10-15 назад, когда программы состояли из исходников и небольшого количества двоичных файлов с работой по сборке итоговых программ отлично справлялись всевозможные «?make». Однако сейчас современные программы и подходы к разработке сильно изменились – это:
множество различных файлов (не считаю исходников) – стили, шаблоны, ресурсы, конфигурации, скрипты, бинарные данные и.т.д;
- предпроцессоры;
- системы проверки стиля исходников или всего проекта (lint, checkstyle и т.д.);
- методики разработки, основанные на тестах, с их запуском при сборке;
- различного типа стенды;
- системы развёртывания на базе облачных технологий и т.д. и.т.п.
Причём через этапы сборки итоговых или промежуточных артефактов с использованием перечисленных средств и методик среднестатистическому разработчику приходится проходить по несколько раз в день. Запуск командных файлов руками (с возможной проверкой результатов) в данном случае слишком обременительная процедура: необходим инструмент, отслеживающий изменения в ваших данных проекта и запускающий необходимые средства в зависимости от обнаруженных изменений.
Мой путь в использовании систем сборки был непонятное_количество_*make -> ant -> maven -> gradle (то обстоятельство, что android Studio под капотом использует gradle меня сильно порадовало).
Gradle меня привлёк:
- своей простой моделью (из которой правда можно вырастить монстра, соизмеримого с самим создаваемым продуктом);
- гибкостью (как в части настройки самих скриптов, так и организации их распределения в рамках больших проектов);
- постоянным развитием (как и со всеми остальными вещами в разработке — тут надо постоянно изучать что-то новое);
- лёгкостью адаптации (знание groovy и gradle DSL обязательно);
- наличием системы plugin’ов, разрабатываемых сообществом — это и разные предпроцессоры, генераторы кода, системы доставки и публикации и прочее, прочее (см. login.gradle.org)
Ознакомиться с возможностями Gradle можно на сайте разработчиков в разделе документации (можно узнать всё!). Для тех, кто хочет сравнить gradle и maven есть интересное видео от JUG.
В моём случае скрипты для сборки выглядят таким образом:
,
где:
- build_scripts/build-tasks.gradle — все задачи для сборки с указанием их зависимостей;
- build_scripts/dependencies.gradle — описания зависимостей и способов публикации;
- build.gradle — основной скрипт, определяющий зависимые модули, библиотеки и включающий другие скрипты сборки;
- settings.gradle — перечень зависимых модулей и настройки самого скрипта (можно переопределить ч/з аргументы запуска gradle).
В такой конфигурации все изменения скрипта сборки производятся в централизованных файлах (не даются на откуп каждому проекту) и могут/должны корректироваться централизованно лицом, ответственным за сборку проекта.
Tips
Из интересных вещей/советов, которыми я хотел бы поделиться, при настройке gradle в моём проекте есть следующие:
Вынос версий артефактов (шарится по блокам модулей было скучным занятием)
Объявляем блок с зависимостями:
// project dependencies ext { COMMONS_POOL_VER='2.4.2' DROPWIZARD_CORE_VER='1.1.0' DROPWIZARD_METRICS_VER='3.2.2' DROPWIZARD_METRICS_INFLUXDB_VER='0.9.3' JSOUP_VER='1.10.2' STORM_VER='1.0.3' ... GROOVY_VER='2.4.7' // test TEST_JUNIT_VER='4.12' TEST_MOCKITO_VER='2.7.9' TEST_ASSERTJ_VER='3.6.2' }
И используем его в проекте:
project(':crawler_scripts') { javaProject(it) javaLogLibrary(it) javaTestLibrary(it) dependencies { testCompile "org.codehaus.groovy:groovy:${GROOVY_VER}" testCompile "edu.uci.ics:crawler4j:${CRAWLER4J_VER}" testCompile "org.jsoup:jsoup:${JSOUP_VER}" testCompile "joda-time:joda-time:${JODATIME_VER}" testCompile "org.apache.commons:commons-lang3:${COMMONS_LANG_VER}" testCompile "commons-io:commons-io:${COMMONS_IO_VER}" } }
- Вынос настроек во внешний файл
Создаём или уже имеем файл с конфигурацией:
--- # presented - for test/development only - use artifact from ""/provision/artifacts" directory storyline_components: crawler_scripts: version: "0.5" crawler: version: "0.6" server_storm: version: "presented" server_web: version: "0.1"
И используем его в проекте:
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.dataformat.yaml.YAMLFactory buildscript { repositories { jcenter() } dependencies { // reading YAML classpath "com.fasterxml.jackson.core:jackson-databind:2.8.6" classpath "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.6" } } .... def loadArtifactVersions(type) { Map result = new HashMap() def name = "${projectDir}/deployment/${type}/hieradata/version.yaml" println "Reading artifact versions from ${name}" if (new File(name).exists()) { ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); result = mapper.readValue(new FileInputStream(name), HashMap.class); } return result['storyline_components']; }
- Использование шаблонов
Это моя любимая часть — позволяет сформировать необходимые конфигурационные файлы из шаблонов.
Формируем шаблоны:
version: '2' services: ... server_storm: domainname: story-line.ru hostname: server_storm build: ./server_storm depends_on: - zookeeper - elasticsearch - mongodb links: - zookeeper - elasticsearch - mongodb ports: - "${server_storm_ui_host_port}:8082" - "${server_storm_logviewer_host_port}:8083" - "${server_storm_nimbus_host_port}:6627" - "${server_storm_monit_host_port}:3000" - "${server_storm_drpc_host_port}:3772" volumes: - ${logs_dir}:/data/logs - ${data_dir}:/data/db ....
И используем его в проекте:
// выполнить копирование скриптов для подготовки сервера task copyTemplates (type: Copy, dependsOn: ['createStandDir']){ description "выполнить копирование шаблонов" from "${projectDir}/deployment/docker_templates" into project.ext.stand.deploy_dir expand(project.ext.stand) filteringCharset = 'UTF-8' }
В итоге получаете конфигурационный файл, данные которого заполнены значениями переменных, получившими значения в ходе работы скрипта.Однако необходимо отметить, что если вам потребуется более сложная логика с переменными (например скрытие при отсутствии значений) — вам потребуется другой движок шаблонов в системе сборке. Я эту ситуацию обошел используя формат YAML по ходу проекта.
- Использование списков и замыканий groovy для специфичной обработки конкретных модулей
Это фактически обычное использование groovy в скрипте сборки, но мне это помогло решить пару нетривиальных задач.
Объявляем или получаем переменные множественного значения:
ext { // образы, создаваемые docker'ом docker_machines = ['elasticsearch', 'zookeeper', 'mongodb', 'crawler', 'server_storm', 'server_web'] // образы, создаваемые docker'ом для которых необходимо копировать артефакты docker_machines_w_artifacts = ['crawler', 'server_storm', 'server_web'] }
И используем их в проекте:
// выполнить копирование шаблонов для docker с подстановкой значений docker_machines.each { machine -> task "copyProvisionScripts_${machine}" (type: Copy, dependsOn: ['createStandDir']){ ... } }
- Интеграция с репозитариями maven — больно громоздкое описание для включения в статью (и сама работа достаточно бесполезная с учётом наличия примеров в документации)
- Возможность задачи блоков зависимостей для модулей, копирование и вставка которых отсутствует для каждого модуля
Объявляем блоки:
def javaTestLibrary(project) { project.dependencies { testCompile "org.apache.commons:commons-lang3:${COMMONS_LANG_VER}" testCompile "commons-io:commons-io:${COMMONS_IO_VER}" testCompile "junit:junit:${TEST_JUNIT_VER}" testCompile "org.mockito:mockito-core:${TEST_MOCKITO_VER}" testCompile "org.assertj:assertj-core:${TEST_ASSERTJ_VER}" } }
И используем их в проекте:
project(':token') { javaProject(it) javaLogLibrary(it) javaTestLibrary(it) }
Запуск всего этого богатого функционала с консоли одной командой, при которой определяются низменные фрагменты проекта, обновляются необходимые зависимости, выполняются необходимые проверки и генерируется требуемые артефакты позволяют говорить о системах сборки как об инструментах, которые при правильной настройке могут радикально изменить скорость доставки вашего проекта до конченых потребителей.
Спасибо за внимание!
Поделиться с друзьями
apro
ИМХО конечно, но с точки зрения использования
make
ничего не поменялось.Все также нужно из множества файлов А получить множество файлов Б.
Будь это получение xml отчета lint из множества
.java
и.xml
. Или получение из множества файлов ресурсов с помощьюaapt
файла с.java
кодом для доступа видаR.id.X
,со всем этим
gnu make
отлично справиться.Другое дело, что язык для правил
make
слишком неудобный по сравнению например с groovy,да и встроенных правил (типа
%.o : %.c
) для сборки android проектов конечно нет.fedor_malyshkin
Есть истинна в этих словах. Но сборка продукта системой сборки (а так же его тестирование и доставка) не всегда предполагают работу с только файлами — это могут быть и сетевые сервисы (FTP, HTTP), и сервисы ОС (docker например), и взаимодействие с облачными сервисами, а это становится для
make
не простой задачей (но не невыполнимой).Полагаю свой скрипт сборки с сохранением функционала я мог бы реализовать на
ant
, но зная проgradle
— стоило бы?