Project Alice genera el código fuente de Java, lo almacena en sources.jar y luego lo carga en un repositorio de Maven. Project Bob extrae sources.jar y necesita usarlo al compilar. Bob no sabe que Alice existe, solo donde encontrar las sources.jar .
Versiones: JDK 11, Gradle 7.3.1, IntelliJ IDEA 2021.3.1
Hacer que Gradle (y la IDEA de IntelliJ) se construyan utilizando archivos fuente incrustados en un archivo JAR. Para ser claros, el contenido del archivo JAR se parece a:
$ jar -tvf sources.jar 0 Thu Feb 03 08:38:56 PST 2022 META-INF/ 52 Thu Feb 03 08:38:56 PST 2022 META-INF/MANIFEST.MF 0 Thu Feb 03 08:38:30 PST 2022 com/ 0 Thu Feb 03 08:38:32 PST 2022 com/domain/ 0 Thu Feb 03 08:38:30 PST 2022 com/domain/package/ 938 Thu Feb 03 08:38:32 PST 2022 com/domain/package/SourceCode.java Las soluciones que extraen los archivos .java del archivo .jar introducen efectos colaterales que nos gustaría evitar, entre ellos:
sources.jar recién generado, tendremos que eliminar el directorio de extracción para eliminar cualquier archivo .java obsoleto que se haya conservado. Si hubiera una manera de evitar la extracción, entonces el acto de extraer el nuevo archivo sources.jar garantizaría la corrección (sin complejidad adicional). Al desarchivar los archivos .java, es posible ingresar a un estado inconsistente: $ jar -tvf sources.jar | grep java$ | wc -l 61 $ find src/gen -name "*java" | wc -l 65 Si hubiera una forma de tratar las fuentes. sources.jar como un directorio de origen sin extraer los archivos, estos efectos colaterales desaparecerían.
Varios enfoques han fallado.
Cambiar sourceSets no funciona:
sourceSets.main.java.srcDirs += "jar:${projectDir}/sources.jar!/"el error es:
No se puede convertir la URL 'jar:/home/user/dev/project/sources.jar!/' a un archivo.
El uso de zipTree con sourceSets no funciona, aunque el mensaje de error dice:
sourceSets.main.java.srcDirs += zipTree(file: "${projectDir}/sources.jar")Error:
No se puede convertir la notación proporcionada en un archivo o URI.
Se admiten los siguientes tipos/formatos:
- Una instancia de URI o URL.
Esto era de esperar. Lo inesperado fue que las instancias de URL están permitidas, pero aparentemente no si están incrustadas en un archivo JAR.
Lo siguiente permite construir a Bob, pero el IDE no puede encontrar SourceCode.java :
sourceSets.main.java.srcDirs += zipTree("${projectDir}/sources.jar").matching { include "com" }La modificación de la tarea de compilación para extraer primero el código generado funciona parcialmente:
task codeGen { copy { from( zipTree( "${projectDir}/sources.jar" ) ) into( "${buildDir}/src/gen/java" ) } sourceSets.main.java.srcDirs += ["${buildDir}/src/gen/java"] } build { doFirst { codeGen } } El problema es que eliminar el directorio de build evita las compilaciones estáticas (porque IDEA no puede encontrar los archivos fuente generados). En cualquier caso, no queremos extraer los archivos de origen debido a todos los problemas colaterales.
El siguiente fragmento tampoco se compila:
tasks.withType(JavaCompile) { source = zipTree(file: "${projectDir}/sources.jar") } No actualizar sourceSets significa que el IDE no puede descubrir los archivos fuente.
En su lugar, podríamos extraer los archivos en el directorio de origen principal, como:
def syncTask = task sync(type: Sync) { from zipTree("${projectDir}/sources.jar") into "${projectDir}/src/gen/java" preserve { include 'com/**' exclude 'META-INF/**' } } sourceSets.main.java.srcDir(syncTask)Si bien eso soluciona el problema de la limpieza, nos quedan los problemas originales que nos gustaría evitar.
Establecer la raíz del contenido y marcar la carpeta de origen desde IntelliJ IDEA funciona. El IDE actualiza .idea/misc.xml para incluir:
<component name="FrameworkDetectionExcludesConfiguration"> <file type="web" url="file://$PROJECT_DIR$" /> <file type="web" url="jar://$PROJECT_DIR$/project/sources.jar!/" /> </component> En teoría, el complemento de idea puede tener la capacidad de establecer este valor.
¿Cómo le indicaría a Gradle que haga referencia y construya usando archivos de origen que se almacenan en un archivo de Java externo al compilar un proyecto ( sin extraer el archivo) de modo que IDEA también pueda resolver estáticamente los archivos de origen?
Decidimos que extraer el archivo jar sería el mejor enfoque después de todo:
apply plugin: 'idea' final GENERATED_JAR = "${projectDir}/sources.jar" final GENERATED_DIR = "${buildDir}/generated/sources" task extractSources(type: Sync) { from zipTree(GENERATED_JAR) into GENERATED_DIR } sourceSets.main.java.srcDir extractSources clean.finalizedBy extractSources idea.module.generatedSourceDirs += file(GENERATED_DIR)Esta:
clean .build/generated/sources.jar y los archivos .java sincronizadosEn efecto, los siguientes comportamientos funcionan como se esperaba:
./gradlew clean : deja intactos los archivos .java extraídos, efectivamente./gradlew build -- vuelve a sincronizar archivos .java con contenidos .jar