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

Кстати, если вы пишете на Java и по какой-то причине всё ещё не используете Lombok в своём проекте, то я рекомендую познакомиться со статьями на Хабре (раз и два). Уверен, вам понравится!
Проект, над которым мы работаем, состоит из нескольких модулей. Часть из них (назовём их условно backend) при выпуске релиза пакуется в архив (поставку), загружается в репозиторий и впоследствии деплоится на серверы приложений. Другая часть — т.н. клиентский модуль — публикуется в репозиторий в виде набора артефактов, содержащего в т.ч. sources.jar и javadoc.jar. Lombok мы используем во всех частях, а собирается всё это Maven'ом.
Некоторое время назад один из потребителей нашего сервиса обратился с проблемой — он пытался дебажить наш модуль, но не мог этого сделать, т.к. в sources.jar отсутствовали методы (и даже классы), в которых он хотел бы поставить breakpoint. Мы в нашей команде считаем, что попытка само��тоятельно выявить и решить проблему, вместо того, чтобы бездумно заводить дефект — поступок достойного мужа, который нужно поощрять! :-) Поэтому было принято решение привести sources.jar в соответствие с байткодом.
Давайте представим себе, что у нас есть простое приложение, состоящее из двух классов:
И собирается наше приложение с помощью Maven'а:
Если скомпилировать этот проект (
Довольно сильно отличается от того, что попадёт в наш sources.jar, не правда ли? ;) Как видите, если бы вы подключили исходники для дебага SomePojo и захотели поставить breakpoint, например, в конструкторе, то вы бы столкнулись с проблемой — breakpoint ставить некуда, а класса SomePojoBuilder там вообще нет.
Как это часто бывает — у этой проблемы есть несколько способов решения. Давайте рассмотрим каждый из них.
Когда мы столкнулись с этой проблемой впервые — речь шла о модуле, в котором была всего пара классов, использующих Lombok. Отказываться от него, конечно, не хотелось, поэтому я сразу подумал о том, чтоб делать delombok. Поисследовав этот вопрос, я нашёл несколько странных решений с использованием Lombok-плагина для Maven'а — lombok-maven-plugin. В одном из них предлагали, например, держать исходники, в которых используется Lombok, в отдельной директории, для которой будет выполняться delombok, и сгенерированные исходники будут попадать в generated-sources, откуда уже будет компилироваться и попадать в sources.jar. Вариант это, наверное, рабочий но в этом случае в IDE не будет работать подсветка синтаксиса в оригинальных исходниках, т.к. каталог с ними не будет считаться директорией с исходниками. Такой вариант меня не устраивал, и, поскольку цена отказа от Lombok'а в этом модуле была невелика, было принято решение не тратить на это время, отключить Lombok и просто сгенерировать нужные методы через IDE.
В целом, мне кажется, что такой вариант имеет право на жизнь, но только если классов, использующих Lombok действительно мало и они редко меняются.

Спустя некоторое время пришлось вернуться к этой проблеме снова, когда речь шла уже о модуле, в котором Lombok использовался гораздо более интенсивно. Вернувшись снова к изучению этой проблемы, я наткнулся на вопрос на stackoverflow, где предлагалось выполнять для исходников delombok, а затем с помощью задачи в Ant генерировать sources.jar.
Тут нужно сделать отступление о том, почему генерировать sources.jar нужно именно с помощью Ant'а, а не с помощью Source-плагина (maven-source-plugin). Дело в том, что для этого плагина нельзя сконфигурировать директорию с исходниками. Он всегда будет использовать содержимое свойства
Итак, в случае с нашим примером, pom.xml станет выглядеть так:
Как видите, конфигурация очень сильно разрослась, и в ней есть далеко не только
Кроме того, я обнаружил, что при выполнении delombok'а по умолчанию Lombok добавляет в шапку сгенерированных файлов комментарий. При этом формат генерирумеых файлов управляется не с помощью опций в файлеслишком ленивый для этого ж программист, поэтому я нашёл их в исходниках на гитхабе.
Но ни объём конфигурации, ни её особенности не идут ни в какое сравнение с главным недостатком этого способа. Он не решает проблему. Байткод компилируется из одних исходников, а в sources.jar попадают другие. И несмотря на то, что delombok выполняется тем же самым Lombok'ом, между байткодом и сгенерированными исходниками всё равно будут отличия, т.е. для дебага они по прежнему непригодны. Мягко говоря, я расстроился, когда понял это.

Так что же делать? У меня был sources.jar с «правильными» исходниками, но они всё равно отличались от байткода. В принципе проблему могла бы решить компиляция из исходников, сгенерированных в результате delombok'а. Но проблема в том, что maven-compiler-plugin'у нельзя указать путь до исходников. Он всегда использует исходники, указанные в
Можно использовать профили! Создать профиль, который бы использовался только при сборке проекта и в котором бы подменялось значение
К счастью, для этой проблемы есть обходной путь. Можно объявить свойство, которое будет подставляться в тег
В этом случае конфигурация проекта будет выглядеть так:
Работает это следующим образом. В свойство
В результате мы получим байткод, скомпилированный из тех же исходников, которые попадут в sources.jar, т.е. дебаг наконец-то будет работать. При этом нам не нужно конфигурировать установку и деплой артефакта с исходниками, поскольку мы используем для генерации артефакта плагин
А ещё можно в

Думаю, если вы уже знакомы с Gradle, то не стоит объяснять все его преимущества перед Maven. Если же вы ещё не знакомы с ним, то на хабре для этого есть целый Хаб. Отличный повод заглянуть в него! :)
Вообще, когда я подумал об использовании Gradle, то я ожидал, что сделать в нём то, что мне нужно, будет гораздо проще, поскольку я знал, что уж в нём-то я без проблем смогу указать из чего собирать sources.jar и что компилировать в байткод. Проблема поджидала меня там, где я ожидал меньше всего — для Gradle нет работающего delombok плагина.
Есть этот плагин, но похоже, что в нём нельзя указать опции для форматирования delomboked-исходников, что мне не подходило.
Есть ещё один плагин, он генерирует текстовый файл из переданных ему опций, а потом передаёт его в качестве аргумента lombok.jar. Мне не удалось заставить его складывать сгенерированные исходники в нужный каталог, похоже это связано с тем, что путь в текстовом файле с аргументами не берётся в кавычки и не экранируется должным образом. Возможно позже я сделаю пулл реквест автору плагина с предложением исправления.
В итоге я решил пойти другим путём и просто описал задачу с вызовом Ant для выполнения delombok'а, в Lombok'е как раз есть Ant task для этого, и выглядит это вполне неплохо:
По результату этот вариант эквивалентен предыдущему.

Довольно тривиальная, по сути, задача в итоге оказалась чередой попыток найти обходные пути вокруг странных решений авторов Maven. Как по мне — скрипт Gradle, на фоне получившихся конфигов Maven'а, выглядит гораздо более очевидно и логично. Впрочем, может быть мне просто не удалось найти решение лучше? Расскажите в комментариях, решали ли вы похожую задачу, и если решали, то каким образом.
Спасибо за чтение!
Исходники

Кстати, если вы пишете на Java и по какой-то причине всё ещё не используете Lombok в своём проекте, то я рекомендую познакомиться со статьями на Хабре (раз и два). Уверен, вам понравится!
Проблема
Проект, над которым мы работаем, состоит из нескольких модулей. Часть из них (назовём их условно backend) при выпуске релиза пакуется в архив (поставку), загружается в репозиторий и впоследствии деплоится на серверы приложений. Другая часть — т.н. клиентский модуль — публикуется в репозиторий в виде набора артефактов, содержащего в т.ч. sources.jar и javadoc.jar. Lombok мы используем во всех частях, а собирается всё это Maven'ом.
Некоторое время назад один из потребителей нашего сервиса обратился с проблемой — он пытался дебажить наш модуль, но не мог этого сделать, т.к. в sources.jar отсутствовали методы (и даже классы), в которых он хотел бы поставить breakpoint. Мы в нашей команде считаем, что попытка само��тоятельно выявить и решить проблему, вместо того, чтобы бездумно заводить дефект — поступок достойного мужа, который нужно поощрять! :-) Поэтому было принято решение привести sources.jar в соответствие с байткодом.
Пример
Давайте представим себе, что у нас есть простое приложение, состоящее из двух классов:
SomePojo.java
package com.github.monosoul.lombok.sourcesjar; import lombok.Builder; import lombok.Value; @Value @Builder(toBuilder = true) class SomePojo { /** * Some string field */ String someStringField; /** * Another string field */ String anotherStringField; }
Main.java
package com.github.monosoul.lombok.sourcesjar; import lombok.val; public final class Main { public static void main(String[] args) { if (args.length != 2) { throw new IllegalArgumentException("Wrong arguments!"); } val pojo = SomePojo.builder() .someStringField(args[0]) .anotherStringField(args[1]) .build(); System.out.println(pojo); } }
И собирается наше приложение с помощью Maven'а:
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <artifactId>lombok-sourcesjar</artifactId> <groupId>com.github.monosoul</groupId> <version>1.0.0</version> <packaging>jar</packaging> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.2</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.1.1</version> <configuration> <archive> <manifest> <mainClass>com.github.monosoul.lombok.sourcesjar.Main</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <version>3.0.1</version> <executions> <execution> <id>attach-sources</id> <goals> <goal>jar</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
Если скомпилировать этот проект (
mvn compile), а затем сдекомпилировать получившийся байткод, то класс SomePojo будет выглядеть так:SomePojo.class
package com.github.monosoul.lombok.sourcesjar; final class SomePojo { private final String someStringField; private final String anotherStringField; SomePojo(String someStringField, String anotherStringField) { this.someStringField = someStringField; this.anotherStringField = anotherStringField; } public static SomePojo.SomePojoBuilder builder() { return new SomePojo.SomePojoBuilder(); } public SomePojo.SomePojoBuilder toBuilder() { return (new SomePojo.SomePojoBuilder()).someStringField(this.someStringField).anotherStringField(this.anotherStringField); } public String getSomeStringField() { return this.someStringField; } public String getAnotherStringField() { return this.anotherStringField; } public boolean equals(Object o) { if (o == this) { return true; } else if (!(o instanceof SomePojo)) { return false; } else { SomePojo other = (SomePojo)o; Object this$someStringField = this.getSomeStringField(); Object other$someStringField = other.getSomeStringField(); if (this$someStringField == null) { if (other$someStringField != null) { return false; } } else if (!this$someStringField.equals(other$someStringField)) { return false; } Object this$anotherStringField = this.getAnotherStringField(); Object other$anotherStringField = other.getAnotherStringField(); if (this$anotherStringField == null) { if (other$anotherStringField != null) { return false; } } else if (!this$anotherStringField.equals(other$anotherStringField)) { return false; } return true; } } public int hashCode() { int PRIME = true; int result = 1; Object $someStringField = this.getSomeStringField(); int result = result * 59 + ($someStringField == null ? 43 : $someStringField.hashCode()); Object $anotherStringField = this.getAnotherStringField(); result = result * 59 + ($anotherStringField == null ? 43 : $anotherStringField.hashCode()); return result; } public String toString() { return "SomePojo(someStringField=" + this.getSomeStringField() + ", anotherStringField=" + this.getAnotherStringField() + ")"; } public static class SomePojoBuilder { private String someStringField; private String anotherStringField; SomePojoBuilder() { } public SomePojo.SomePojoBuilder someStringField(String someStringField) { this.someStringField = someStringField; return this; } public SomePojo.SomePojoBuilder anotherStringField(String anotherStringField) { this.anotherStringField = anotherStringField; return this; } public SomePojo build() { return new SomePojo(this.someStringField, this.anotherStringField); } public String toString() { return "SomePojo.SomePojoBuilder(someStringField=" + this.someStringField + ", anotherStringField=" + this.anotherStringField + ")"; } } }
Довольно сильно отличается от того, что попадёт в наш sources.jar, не правда ли? ;) Как видите, если бы вы подключили исходники для дебага SomePojo и захотели поставить breakpoint, например, в конструкторе, то вы бы столкнулись с проблемой — breakpoint ставить некуда, а класса SomePojoBuilder там вообще нет.
Что с этим делать?
Как это часто бывает — у этой проблемы есть несколько способов решения. Давайте рассмотрим каждый из них.
Не использовать Lombok
Когда мы столкнулись с этой проблемой впервые — речь шла о модуле, в котором была всего пара классов, использующих Lombok. Отказываться от него, конечно, не хотелось, поэтому я сразу подумал о том, чтоб делать delombok. Поисследовав этот вопрос, я нашёл несколько странных решений с использованием Lombok-плагина для Maven'а — lombok-maven-plugin. В одном из них предлагали, например, держать исходники, в которых используется Lombok, в отдельной директории, для которой будет выполняться delombok, и сгенерированные исходники будут попадать в generated-sources, откуда уже будет компилироваться и попадать в sources.jar. Вариант это, наверное, рабочий но в этом случае в IDE не будет работать подсветка синтаксиса в оригинальных исходниках, т.к. каталог с ними не будет считаться директорией с исходниками. Такой вариант меня не устраивал, и, поскольку цена отказа от Lombok'а в этом модуле была невелика, было принято решение не тратить на это время, отключить Lombok и просто сгенерировать нужные методы через IDE.
В целом, мне кажется, что такой вариант имеет право на жизнь, но только если классов, использующих Lombok действительно мало и они редко меняются.

Delombok плагин + сборка sources.jar с помощью Ant
Спустя некоторое время пришлось вернуться к этой проблеме снова, когда речь шла уже о модуле, в котором Lombok использовался гораздо более интенсивно. Вернувшись снова к изучению этой проблемы, я наткнулся на вопрос на stackoverflow, где предлагалось выполнять для исходников delombok, а затем с помощью задачи в Ant генерировать sources.jar.
Тут нужно сделать отступление о том, почему генерировать sources.jar нужно именно с помощью Ant'а, а не с помощью Source-плагина (maven-source-plugin). Дело в том, что для этого плагина нельзя сконфигурировать директорию с исходниками. Он всегда будет использовать содержимое свойства
sourceDirectory проекта.Итак, в случае с нашим примером, pom.xml станет выглядеть так:
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <artifactId>lombok-sourcesjar</artifactId> <groupId>com.github.monosoul</groupId> <version>1.0.0</version> <packaging>jar</packaging> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <lombok.version>1.18.2</lombok.version> </properties> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.1.1</version> <configuration> <archive> <manifest> <mainClass>com.github.monosoul.lombok.sourcesjar.Main</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <groupId>org.projectlombok</groupId> <artifactId>lombok-maven-plugin</artifactId> <version>${lombok.version}.0</version> <executions> <execution> <phase>generate-sources</phase> <goals> <goal>delombok</goal> </goals> </execution> </executions> <configuration> <sourceDirectory>src/main/java</sourceDirectory> <outputDirectory>${project.build.directory}/delombok</outputDirectory> <addOutputDirectory>false</addOutputDirectory> <encoding>UTF-8</encoding> <formatPreferences> <generateDelombokComment>skip</generateDelombokComment> </formatPreferences> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.1.0</version> <executions> <execution> <id>copy-to-lombok-build</id> <phase>process-resources</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <resources> <resource> <directory>${project.basedir}/src/main/resources</directory> </resource> </resources> <outputDirectory>${project.build.directory}/delombok</outputDirectory> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <version>1.8</version> <executions> <execution> <id>generate-delomboked-sources-jar</id> <phase>package</phase> <goals> <goal>run</goal> </goals> <configuration> <target> <jar destfile="${project.build.directory}/${project.build.finalName}-sources.jar" basedir="${project.build.directory}/delombok"/> </target> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> <executions> <execution> <id>install-source-jar</id> <goals> <goal>install-file</goal> </goals> <phase>install</phase> <configuration> <file>${project.build.directory}/${project.build.finalName}-sources.jar</file> <classifier>sources</classifier> <generatePom>true</generatePom> <pomFile>${project.basedir}/pom.xml</pomFile> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> <version>3.0.0-M1</version> <executions> <execution> <id>deploy-source-jar</id> <goals> <goal>deploy-file</goal> </goals> <phase>deploy</phase> <configuration> <file>${project.build.directory}/${project.build.finalName}-sources.jar</file> <classifier>sources</classifier> <generatePom>true</generatePom> <pomFile>${project.basedir}/pom.xml</pomFile> <repositoryId>someRepoId</repositoryId> <url>some://repo.url</url> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
Как видите, конфигурация очень сильно разрослась, и в ней есть далеко не только
lombok-maven-plugin и maven-antrun-plugin. Почему так произошло? Дело в том, что поскольку sources.jar мы теперь собираем Ant'ом, то Maven ничего об этом артефакте не знает. И нам нужно явно указать ему, как этот артефакт устанавливать, как его деплоить и как паковать в него ресурсы.Кроме того, я обнаружил, что при выполнении delombok'а по умолчанию Lombok добавляет в шапку сгенерированных файлов комментарий. При этом формат генерирумеых файлов управляется не с помощью опций в файле
lombok.config, а с помощью опций плагина. Список этих опций оказалось непросто найти. Можно было, конечно, вызвать jar-ник Lombok'а с ключами delombok и --help, но я Но ни объём конфигурации, ни её особенности не идут ни в какое сравнение с главным недостатком этого способа. Он не решает проблему. Байткод компилируется из одних исходников, а в sources.jar попадают другие. И несмотря на то, что delombok выполняется тем же самым Lombok'ом, между байткодом и сгенерированными исходниками всё равно будут отличия, т.е. для дебага они по прежнему непригодны. Мягко говоря, я расстроился, когда понял это.

Delombok плагин + профиль в maven
Так что же делать? У меня был sources.jar с «правильными» исходниками, но они всё равно отличались от байткода. В принципе проблему могла бы решить компиляция из исходников, сгенерированных в результате delombok'а. Но проблема в том, что maven-compiler-plugin'у нельзя указать путь до исходников. Он всегда использует исходники, указанные в
sourceDirectory проекта, как и maven-source-plugin. Можно было бы указать там каталог, в который генерируются delomboked исходники, но в этом случае при импорте проекта в IDE, каталог с реальными исходниками не будет считаться таковым и для файлов в нём не будет работать подсветка синтаксиса и другие фичи. Такой вариант меня тоже не устраивал.Можно использовать профили! Создать профиль, который бы использовался только при сборке проекта и в котором бы подменялось значение
sourceDirectory! Но есть нюанс. Тег sourceDirectory можно объявить только внутри тега build в корне проекта.К счастью, для этой проблемы есть обходной путь. Можно объявить свойство, которое будет подставляться в тег
sourceDirectory, а в профиле менять значение этого свойства!В этом случае конфигурация проекта будет выглядеть так:
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <artifactId>lombok-sourcesjar</artifactId> <groupId>com.github.monosoul</groupId> <version>1.0.0</version> <packaging>jar</packaging> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <lombok.version>1.18.2</lombok.version> <origSourceDir>${project.basedir}/src/main/java</origSourceDir> <sourceDir>${origSourceDir}</sourceDir> <delombokedSourceDir>${project.build.directory}/delombok</delombokedSourceDir> </properties> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <scope>provided</scope> </dependency> </dependencies> <profiles> <profile> <id>build</id> <properties> <sourceDir>${delombokedSourceDir}</sourceDir> </properties> </profile> </profiles> <build> <sourceDirectory>${sourceDir}</sourceDirectory> <plugins> <plugin> <groupId>org.projectlombok</groupId> <artifactId>lombok-maven-plugin</artifactId> <version>${lombok.version}.0</version> <executions> <execution> <phase>generate-sources</phase> <goals> <goal>delombok</goal> </goals> </execution> </executions> <configuration> <sourceDirectory>${origSourceDir}</sourceDirectory> <outputDirectory>${delombokedSourceDir}</outputDirectory> <addOutputDirectory>false</addOutputDirectory> <encoding>UTF-8</encoding> <formatPreferences> <generateDelombokComment>skip</generateDelombokComment> <javaLangAsFQN>skip</javaLangAsFQN> </formatPreferences> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.1.1</version> <configuration> <archive> <manifest> <mainClass>com.github.monosoul.lombok.sourcesjar.Main</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <version>3.0.1</version> <executions> <execution> <id>attach-sources</id> <goals> <goal>jar</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
Работает это следующим образом. В свойство
origSourceDir мы подставляем путь до каталога с оригинальными исходниками, а в свойство sourceDir по умолчанию подставляем значение из origSourceDir. В свойстве delombokedSourceDir мы указываем путь до исходников, сгенерированных delombok'ом. Таким образом, при импорте проекта в IDE используется каталог из origSourceDir, а при сборке проекта, если указать профиль build, будет использован каталог delombokedSourceDir.В результате мы получим байткод, скомпилированный из тех же исходников, которые попадут в sources.jar, т.е. дебаг наконец-то будет работать. При этом нам не нужно конфигурировать установку и деплой артефакта с исходниками, поскольку мы используем для генерации артефакта плагин
maven-source-plugin. Правда, магия с переменными может запутать незнакомого с нюансами Maven'а человека.А ещё можно в
lombok.config добавить опцию lombok.addJavaxGeneratedAnnotation = true, тогда в сгенерированных исходниках над сгенерированным кодом будет стоять аннотация @javax.annotation.Generated("lombok"), что поможет избежать вопросов типа «Почему ваш код выглядит так странно?!». :)
Использовать Gradle
Думаю, если вы уже знакомы с Gradle, то не стоит объяснять все его преимущества перед Maven. Если же вы ещё не знакомы с ним, то на хабре для этого есть целый Хаб. Отличный повод заглянуть в него! :)
Вообще, когда я подумал об использовании Gradle, то я ожидал, что сделать в нём то, что мне нужно, будет гораздо проще, поскольку я знал, что уж в нём-то я без проблем смогу указать из чего собирать sources.jar и что компилировать в байткод. Проблема поджидала меня там, где я ожидал меньше всего — для Gradle нет работающего delombok плагина.
Есть этот плагин, но похоже, что в нём нельзя указать опции для форматирования delomboked-исходников, что мне не подходило.
Есть ещё один плагин, он генерирует текстовый файл из переданных ему опций, а потом передаёт его в качестве аргумента lombok.jar. Мне не удалось заставить его складывать сгенерированные исходники в нужный каталог, похоже это связано с тем, что путь в текстовом файле с аргументами не берётся в кавычки и не экранируется должным образом. Возможно позже я сделаю пулл реквест автору плагина с предложением исправления.
В итоге я решил пойти другим путём и просто описал задачу с вызовом Ant для выполнения delombok'а, в Lombok'е как раз есть Ant task для этого, и выглядит это вполне неплохо:
build.gradle.kts
group = "com.github.monosoul" version = "1.0.0" plugins { java } java { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } dependencies { val lombokDependency = "org.projectlombok:lombok:1.18.2" annotationProcessor(lombokDependency) compileOnly(lombokDependency) } repositories { mavenCentral() } tasks { "jar"(Jar::class) { manifest { attributes( Pair("Main-Class", "com.github.monosoul.lombok.sourcesjar.Main") ) } } val delombok by creating { group = "lombok" val delombokTarget by extra { File(buildDir, "delomboked") } doLast({ ant.withGroovyBuilder { "taskdef"( "name" to "delombok", "classname" to "lombok.delombok.ant.Tasks\$Delombok", "classpath" to sourceSets.main.get().compileClasspath.asPath) "mkdir"("dir" to delombokTarget) "delombok"( "verbose" to false, "encoding" to "UTF-8", "to" to delombokTarget, "from" to sourceSets.main.get().java.srcDirs.first().absolutePath ) { "format"("value" to "generateDelombokComment:skip") "format"("value" to "generated:generate") "format"("value" to "javaLangAsFQN:skip") } } }) } register<Jar>("sourcesJar") { dependsOn(delombok) val delombokTarget: File by delombok.extra from(delombokTarget) archiveClassifier.set("sources") } withType(JavaCompile::class) { dependsOn(delombok) val delombokTarget: File by delombok.extra source = fileTree(delombokTarget) } }
По результату этот вариант эквивалентен предыдущему.

Выводы
Довольно тривиальная, по сути, задача в итоге оказалась чередой попыток найти обходные пути вокруг странных решений авторов Maven. Как по мне — скрипт Gradle, на фоне получившихся конфигов Maven'а, выглядит гораздо более очевидно и логично. Впрочем, может быть мне просто не удалось найти решение лучше? Расскажите в комментариях, решали ли вы похожую задачу, и если решали, то каким образом.
Спасибо за чтение!
Исходники
