Gradle и решение задач автоматизации

от автора

Всем привет, сегодня я хотел бы рассказать вам о моём опыте работы с Gradle, не просто переписать мануал (хотя он отлично написан), но рассказать с какими реальными проблемами я столкнулся и как побеждал их, а также показать какие возможности предоставляет нам Gradle. Тема очень обширная поэтому, к сожалению я не смогу рассмотреть многие аспекты подробно и последовательно, надеюсь что читатели уже немного знакомы с Gradle и смогут понять суть описываемых решений.

Лирика

Важное замечание по поводу 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 

Результат:

image

Настройка 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *