Category Archives: Gradle

Gradle и maven enforcer

Written by elwood

Те, кто плотно пользуются мавном, наверняка знают и активно используют плагин maven enforcer. Если включить проверку DependencyConvergence, то плагин будет проверять проект на отсутствие конфликтов между версиями используемых артефактов:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-enforcer-plugin</artifactId>
    <version>1.0.1</version>
    <executions>
        <execution>
            <id>enforce</id>
            <configuration>
                <rules>
                    <DependencyConvergence />
                </rules>
            </configuration>
            <goals>
                <goal>enforce</goal>
            </goals>
        </execution>
    </executions>
</plugin>

У него есть ещё проверки, но самая важная именно эта. Например, в проекте используется log4j версии 1.2, а какая-то библиотека в одной из транзитивных зависимостей ссылается на log4j 1.1, то enforcer ругнётся во время сборки. После этого программист увидит проблему и пойдёт чинить. Как правило, для решения траблы достаточно замаскировать часть транзитивных зависимостей с помощью exclude. Если же этого не сделать, а отключить enforcer, то в билд попадут обе зависимости: и log4j:1.2, и log4j:1.1, что не очень хорошо, так как то, какая версия будет реально использована, зависит лишь от порядка его нахождения в classpath.

В gradle же по умолчанию работает следующая схема. Если есть конфликт, то будет использована крайняя версия артефакта, и дублей в результирующем билде не будет. И — черт возьми — в 99% случаев это именно то, что надо ! Но душа взывает к возможности полного контроля за ситуацией. Хочется, чтобы при появлении конфликтующей зависимости программист сразу об этом узнал и на всякий случай проверил ещё раз руками. Если у вас такая же параноя — вы можете установить другой режим разрешения конфликтов (благо грейдл это позволяет):

configurations.all {
   resolutionStrategy {
      // Fail eagerly on version conflict (includes transitive dependencies)
      // e.g. multiple different versions of the same dependency (group and name are equal)
      failOnVersionConflict()
   }
}

Всё, теперь сборка будет фейлиться при появлении конфликтов. Разрешать эти конфликты вам придётся самостоятельно, опять же с помощью exclude’ов, либо пользуясь force-установкой версий артефактов (см. документацию).

Единственный минус этой схемы по сравнению с maven enforcer в том, что enforcer вываливает все конфликты сразу, а gradle будет фейлиться на каждом конфликте по одному 🙂 В общем, занятие чистить руками конфликты это минимум на полчаса ковыряний с gradle :dependencies.

А вообще, режим градла по умолчанию очень разумный, в принципе, можно ничего и не трогать, всё будет работать (почти всегда).

Как опубликовать maven-артефакт в jCenter

Written by elwood

Недавно я случайным образом написал плагин для MyBatis. Написал тесты, readme, все дела. И даже прикрутил бейдж от travis-ci. Но самое главное ещё только предстояло сделать. А именно – запилить этот мейвеновский пакет в какой-нибудь публичный репозиторий. Я никогда ещё этого не делал, и сначала было пошёл смотреть, как опубликовать свой артефакт на Maven Central. Нашёл статью “Публикация артефакта в Maven Central через Sonatype OSS Repository Hosting Service”, полистал, и призадумался. Что-то всё как-то больно сложно. И вспомнил, что gretty, один из моих любимых плагинов для gradle, хостит свои релизы на jcenter. Решил попробовать – и вуаля ! Всё получилось. Единственный затык был связан с тем, что я заливал файлы в неправильный путь. И ещё пришлось догадаться сделать pom-файл отдельно, а не надеяться на парсер джарника. Чтобы вы не повторяли моих ошибок и туплений, напишу краткий гайд о том, как опубликовать простой обычный артефакт в jcenter.

  1. Сходите на https://bintray.com и зарегистрируйтесь
  2. В интерфейсе кабинета создайте свою организацию, внутри неё – maven репозиторий (с произвольным названием), далее внутри репозитория создайте пакет, а внутри пакета – версию. Версия должна быть release, то есть не содержать суффикса “SNAPSHOT”
  3. Подготовьте 3 файла:
    • project-1.0.jar – этот файл обычно делается командой mvn clean package
    • project-1.0-sources.jar – здесь можно попробовать команду mvn source:jar
    • project-1.0.pom – а этот файл делается простым переименованием вашего pom.xml
  4. В контексте версии в веб-интерфейсе перейтиде к странице загрузки файлов. Выберите сразу 3 файла и перетащите их в броузер. Вы должны будете увидеть что-то типа этого:

    bintray-uploading

    Жмите Accept, и пути будут выставлены корректно, согласно координатам, указанным в вашем pom-файле.

  5. Теперь bintray предложит опубликовать загруженные артефакты. Не отказывайтесь.
  6. Далее возвращаемся к версии, наводим мышку на Maven Central, и выбираем опцию Synchronize with jCenter. Если всё сделано правильно, то система примет этот запрос, и теперь остается некоторое время подождать, пока сотрудник jCenter рассмотрит его. Вам придет письмо на email и уведомление по внутренней почте.
  7. Артефакт успешно опубликован в jcenter и доступен для загрузки ! Теперь его можно просто брать и использовать, в мейвене :
    <repositories>
        <repository>
            <id>jcenter</id>
            <url>http://jcenter.bintray.com</url>
        </repository>
    </repositories>
     
    <dependencies>
        <dependency>
            <groupId>org.mybatis.scripting</groupId>
            <artifactId>mybatis-freemarker</artifactId>
            <version>1.1</version>
        </dependency>
    </dependencies>

    или в Gradle:

    repositories {
        jcenter()
    }
     
    dependencies {
        compile("org.mybatis.scripting:mybatis-freemarker:1.1")
    }

Далее в планах все-таки попробовать запилить свой пакет в Maven Central, по результатам постараюсь отписаться тоже.

И снова gradle: делаем профили

Written by elwood

gradle_logo

Научился сегодня по-простому делать профили а-ля мавно. Рецепт таков:

1) Создаём файл defaults.gradle. В нём будут лежать значения свойств по умолчанию, например:

allprojects {
    ext {
        // Solr connection
        host = 'localhost'
        port = '8983'
        username = ''
        password = ''
    }
}
 
project(':crawler') {
    // SQL Server connection
    project.ext["jdbc.url"]='jdbc:jtds:sqlserver://...'
    project.ext["jdbc.username"] = 'user'
    project.ext["jdbc.password"] = 'passasdf'
    project.ext["jdbc.driverClassName"]='net.sourceforge.jtds.jdbcx.JtdsDataSource'
}

2) Для каждого профиля создаём файл с именем profile-${profile}.gradle, например, файл profile-stage.gradle:

allprojects {
    ext {
        host = '234.23.42.3'
        port = '6001'
        username = ''
        password = ''
    }
}

3) В основной скрипт сборки build.gradle в начало добавляем следующий код:

apply from: "defaults.gradle"
if (hasProperty('profile')) {
    apply from: "profile-${profile}.gradle"
}

Таким образом, defaults.gradle будет применяться всегда, а скрипт, специфичный для конкретного профиля – только если указывается свойство profile.

4) Запустить сборку с указанием профиля:

gradlew -Pprofile=stage :crawler:build

Если вы только что собрали что-то с одним профилем, а потом решили пересобрать для другого, убедитесь, что gradle прознал о том, что что-то надо пересобрать. Например, если вы используете Ant Filtering, то градло не поймёт, что ресурсы надо пересобрать – нужно явно выполнить clean.