Лирика
Важное замечание по поводу maven — многие задачи которые я делал с помощью Gradle я никогда не пытался сделать раньше maven-ом, поэтому сравнить многие моменты, что лучше, а что хуже я к сожалению не смогу.
Первое, что в нём понравилось, это наконец-то «прощай xml», о да, для меня это действительно как заново родиться, как же было невероятно приятно писать сборку на настоящем языке программирования, какая же это свобода и вдохновение.
В статье будут использоваться некоторые понятия из OSGI и eclipse rcp, я не буду заострять на них внимание, чтобы не отвлекаться от главной темы.
В приложении есть простенький проект с примерами скриптов и некоторыми исходниками используемыми в статье: тынц
Масштаб проблемы
Итак, что у нас было вначале: у нас имеется большая (по моим меркам) система запускаемая на OSGI платформе, проекты в системе это eclipse plug-in проекты, никакой интеграции с системой сборки нет, в качестве системы сборки используется ant, системы контроля зависимостей не было. Была задача сделать всё круто.
Итак я предложил Gradle мне дали добро, и началось.
Описание структуры проектов
Суть проблемы в том, что имеется директория с некой структурой в которой хранятся проекты.
Первое что нужно было сделать, это описать в скриптах сборки структуру директории и все имеющиеся проекты. Я это смог сделать с легкостью. В Gradle для этого используется понятие multi project. В корневой директории создаем файл settings.gradle и записываем пути к каждой папке где лежат проекты разделитель пути «:». Пример settings.gradle:
include "subproject-first" include "subproject-first:child-first" include "subproject-first:child-first:another-child:last-child" include "subproject-first:child-first:another-child:another-last-child" include "subproject-second"
Теперь мы можем посмотреть структуру нашей системы, выполнив команду:
gradle projects -q
Результат:
Настройка IDE
Следующим шагом была возможность импорта проектов в IDE. В Gradle есть плагины для idea и eclipse:
тынц
барабынц
У нас проекты очень жестко завязаны на eclipse из-за osgi, и куска eclipse-rcp который не позволял просто так взять target platform (это такая страшная штука из мира osgi, по сути это osgi framework и папка с кучей jar-ников) и использовать её в idea, но в общем это лирическое отступление.
Взяв eclipse плагин я смог настроить проекты именно так как мне надо было, а надо было мне сделать проекты как eclipse plug-in, прописать в настройках plugin dependencies, указать версию используемого компилятора. Пример настройки:
project.apply plugin: 'java' project.apply plugin: 'eclipse' project.apply plugin: 'idea' project.eclipse { jdt { sourceCompatibility = '1.6' } project { //применяем натуру и проект становится eclipse plug-in natures 'org.eclipse.pde.PluginNature' } classpath { downloadSources = true downloadJavadoc = true //используем java se 1.6 jre containers.clear() containers.add("org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6") file { //ручная правка .classpath добавление plugin-dependencies зависимостей (то есть зависимости от целевой платформы — есть такое понятие в osgi и эклипсе) withXml { Node node = it.asNode() node.appendNode('classpathentry', [kind: 'con', path: 'org.eclipse.pde.core.requiredPlugins']) } } } } }
То есть, как видите настроек для eclipse много, а то что нельзя сделать через api eclipse плагина, можно сделать вручную исправив xml файл настроек.
Зависимости
Следующий шаг, dependency management: документация.
Я раньше думал, что нет ничего лучше dependency managment в maven, сейчас я думаю, что я ошибался. Gradle может использовать зависимости из maven, ivy, и простые папки и файлы, да и вообще при желании можно сделать, что-то очень нестандартное, например использовать ftp сервер в качестве repository. Для примера, как прописываются зависимости:
project(":myproject") { dependencies { //зависимость от другого проекта compile project(":other-my-project") //берем зависимости из локальной папки compile fileTree(dir: "libs", include: "*.jar") //берем зависимость из maven репозитория compile 'org.hibernate:hibernate-core:3.6.3.Final' } }
Но это только начало. В Gradle есть понятие configurations — это что-то похожее на группы в которые добавляются зависимости. То есть в нашем примере compile это и есть configuration, которую создает java plugin.
Эта возможность помогла мне красиво решить одну проблему с зависимостями, суть этой проблемы вот в чем, у нас ядро разрабатывают за границей, и результат к нам приходит в виде zip архива, внутри которого имеется множество различных файлов, в том числе необходимый набор библиотек. Этот архив заливается в artifactory внешними силами, а я в свою очередь получаю его из artifactory, сохраняю его в temp директорию, распаковываю, беру из распакованной папки все необходимые мне библиотеки.
Скачивание и распаковка архива:
//получаем путь к temp папке куда будем распаковывать архив File sdkDir = getSdkDir(sdkVersion) //создаем необходимые нам конфигурации project.configurations { sdk libsFromSdk } //прописываем artifactory репозиторий откуда будем брать наш архив project.repositories { maven { url 'http://localrepo:8081/artifactory/repo' } mavenCentral() mavenLocal() } //прописываем зависимость project.dependencies { sdk group: 'com.kontora', name: 'sdk', version: sdkVersion, ext: 'zip' } Configuration cfg = project.configurations.sdk //берем zip файл из зависимости cfg.files.each { File zipArchive -> //извлекаем архив в папку project.ant.unzip src: zipArchive, dest: sdkDir.absolutePath }
Обратите внимание, что весь набор действий запрограммирован с помощью dsl языка Gradle в терминах сборки. Нет необходимости писать свой низкоуровневый код на языке программирования.
Подключение зависимостей в проект из распакованного архива:
project.dependencies { libsFromSdk project.fileTree(dir: "$pathToSdk/libs", include: "*.jar") … //копируем все зависимости из libsFromSdk в compile конфигурацию project.compile libsFromSdk }
И итоге мы получим все наши зависимости для компиляции.
Я люблю тебя код, что само по себе и не ново
Наши скрипты тем временем растут и увеличиваются, пора бы и подумать над вопросом копипасты дублирования кода. Предположим у нас есть программная система и мы все понимаем, что дублирование кода в системе это плохо, но почему-то дублирование строк текста в настройках системы сборки не считается чем-то неправильным. Я думаю причина этому, описание сборки в xml, ведь xml — это по сути конфигурационные файлы и применять правила программирования для файлов конфигурации не совсем корректно. Но вместе с тем писать в сотне файлов одинаковый текст конфигурации — это не совсем красиво, сложно для ориентации программиста и может стать причиной неявных и сложно обнаруживаемых ошибок в сборке. В Gradle eсли мы для сборки пишем код, то значит мы сможем избавиться от дублирования кода конфигурации в системе сборки, стандартным способом, как мы это делаем в обычном коде. И тут Gradle опять показал свою мощь и красоту.
Трям.
После некоторого этапа исследований я написал свой первый плагин. Оказывается можно создать папку buildSrc в корневой папке системы, которая будет java, groovy, scala etc проектом по вашему выбору, и при старте системы сборки, в первую очередь будет скомпилирован проект buildSrc и классами из этого проекта можно будет пользоваться в скриптах сборки. По мне так это гениально.
В итоге, например сейчас у меня имена и пути к проектам прописаны только в одном месте в классе в buildSrc, а все остальные скрипты пользуются этими переменными в которых хранятся имена проектов (конечно разговор о преимуществах и недостатках этого подхода это отдельный разговор), при этом у меня есть авто дополнение кода, я просто пишу в любом месте сборки примерно так: MyProjects.subproject.child.absolutePath. Помимо этого большая часть логики сборки лежит в плагинах там же в buildSrc.
Ниже приведен пример плагина и его вызова в скрипте сборки:
Пример плагина:
package org.gradle.example import org.gradle.api.Project import org.gradle.api.Plugin class MyPlugin implements Plugin<Project>{ private static final String PLUGIN_EXTENSION = 'my' Project project; @Override public void apply(Project project) { MyPlugin plugin = project.extensions.create(PLUGIN_EXTENSION, MyPlugin.class) plugin.project = project; } public void applyCommonSettings() { project.apply plugin: 'java' project.apply plugin: 'eclipse' project.apply plugin: 'idea' } }
Пример вызова плагина:
project.apply plugin: MyPlugin my{ applyCommonSettings() }
А у вас есть точно такое же только с перламутровыми пуговицами?
И переходя к плагинам Gradle: у нас в системе есть разные проекты с разными структурами и настройками, Это java, flex, android проекты. И всё это хозяйство нужно настраивать по разному для экспорта в IDE. Например у нас бОльшая часть проектов — это Osgi проекты, другая часть — это простые проекты, у некоторых проектов имеется legacy структура (то есть только папка src) и т.д. И тут Gradle позволил легко справиться с этой задачей. Я написал несколько классов в которых указал какие у нас могут быть настройки, написал класс фабрики в котором описал все настройки каждого проекта, и когда происходит импорт проекта в ide, мы получаем эти настройки и применяем их. Примера кода я тут приводить не буду, потому что это просто groovy код с обычной логикой.
Также я смог написать свои собственные валидаторы проверяющие правильность структуры проектов.
Также планируется много всяких прикольных штук. К сожалению нет времени на то, чтобы рассказать про написание unit тестов для gradle плагинов, возможности логгирования и многое другое
Заключение
Я очень доволен гредлом, он оправдал себя на 101% желаю и вам познать радость работы с этой системой.
ссылка на оригинал статьи http://habrahabr.ru/post/192268/
Добавить комментарий