1717import java .util .Objects ;
1818import java .util .Properties ;
1919import java .util .Set ;
20+ import java .util .concurrent .atomic .AtomicReference ;
2021
2122import org .gradle .api .Project ;
2223import org .gradle .api .Task ;
3637import org .gradle .internal .composite .IncludedBuildInternal ;
3738import org .gradle .language .jvm .tasks .ProcessResources ;
3839import org .gradle .tooling .provider .model .ParameterizedToolingModelBuilder ;
39- import org .jetbrains .kotlin .gradle .tasks .KotlinJvmCompile ;
40+ import org .jetbrains .kotlin .gradle .tasks .KotlinCompileTool ;
4041
4142import io .quarkus .bootstrap .BootstrapConstants ;
4243import io .quarkus .bootstrap .model .ApplicationModel ;
6061import io .quarkus .maven .dependency .GACT ;
6162import io .quarkus .maven .dependency .GACTV ;
6263import io .quarkus .maven .dependency .GAV ;
63- import io .quarkus .maven .dependency .ResolvedDependency ;
6464import io .quarkus .maven .dependency .ResolvedDependencyBuilder ;
6565import io .quarkus .paths .PathCollection ;
6666import io .quarkus .paths .PathList ;
@@ -352,23 +352,20 @@ private void collectDependencies(org.gradle.api.artifacts.ResolvedDependency res
352352 }
353353
354354 PathCollection paths = null ;
355- if (workspaceDiscovery && a .getId ().getComponentIdentifier () instanceof ProjectComponentIdentifier ) {
356-
357- Project projectDep = project .getRootProject ().findProject (
358- ((ProjectComponentIdentifier ) a .getId ().getComponentIdentifier ()).getProjectPath ());
355+ if (workspaceDiscovery && a .getId ().getComponentIdentifier () instanceof ProjectComponentIdentifier compId ) {
356+ Project projectDep = project .getRootProject ().findProject (compId .getProjectPath ());
359357 SourceSetContainer sourceSets = projectDep == null ? null
360358 : projectDep .getExtensions ().findByType (SourceSetContainer .class );
361359
362360 final String classifier = a .getClassifier ();
363361 if (classifier == null || classifier .isEmpty ()) {
364362 final IncludedBuild includedBuild = ToolingUtils .includedBuild (project .getRootProject (),
365- (( ProjectComponentIdentifier ) a . getId (). getComponentIdentifier ()) .getBuild ().getName ());
363+ compId .getBuild ().getName ());
366364 if (includedBuild != null ) {
367365 final PathList .Builder pathBuilder = PathList .builder ();
368366
369- if (includedBuild instanceof IncludedBuildInternal ) {
370- projectDep = ToolingUtils .includedBuildProject ((IncludedBuildInternal ) includedBuild ,
371- ((ProjectComponentIdentifier ) a .getId ().getComponentIdentifier ()).getProjectPath ());
367+ if (includedBuild instanceof IncludedBuildInternal ib ) {
368+ projectDep = ToolingUtils .includedBuildProject (ib , compId .getProjectPath ());
372369 }
373370 if (projectDep != null ) {
374371 projectModule = initProjectModuleAndBuildPaths (projectDep , a , modelBuilder , depBuilder ,
@@ -517,7 +514,6 @@ private static Properties readDescriptor(final Path path) {
517514
518515 private static void initProjectModule (Project project , WorkspaceModule .Mutable module , SourceSet sourceSet ,
519516 String classifier ) {
520-
521517 if (sourceSet == null ) {
522518 return ;
523519 }
@@ -570,45 +566,58 @@ private static void initProjectModule(Project project, WorkspaceModule.Mutable m
570566
571567 private static void maybeConfigureKotlinJvmCompile (Project project , FileCollection allClassesDirs ,
572568 List <SourceDir > sourceDirs , SourceSet sourceSet ) {
573- // This "try/catch" is needed because of the way the "quarkus-cli" Gradle tests work. Without it, the tests fail.
574- try {
575- Class .forName ("org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile" );
576- doConfigureKotlinJvmCompile (project , allClassesDirs , sourceDirs , sourceSet );
577- } catch (ClassNotFoundException e ) {
578- // ignore
569+ for (var task : project .getTasks ()) {
570+ if (task .getName ().contains ("compileKotlin" ) && task .getEnabled ()) {
571+ int originalSourceDirsSize = sourceDirs .size ();
572+
573+ // This "try/catch" is needed because of the way the "quarkus-cli" Gradle tests work. Without it, the tests fail.
574+ try {
575+ Class .forName ("org.jetbrains.kotlin.gradle.tasks.KotlinCompileTool" );
576+ doConfigureKotlinJvmCompile (project , allClassesDirs , sourceDirs , sourceSet );
577+ } catch (ClassNotFoundException e ) {
578+ // ignore
579+ }
580+
581+ // if the above failed, there could still be a KotlinCompile task that's not easily discoverable
582+ if (originalSourceDirsSize == sourceDirs .size ()) {
583+ final Path outputDir = getClassesOutputDir (task );
584+ if (outputDir != null && task .getInputs ().getHasInputs ()) {
585+ task .getInputs ().getSourceFiles ().getAsFileTree ().visit (visitor -> {
586+ if (visitor .getRelativePath ().getSegments ().length == 1 ) {
587+ sourceDirs .add (SourceDir .of (visitor .getFile ().getParentFile ().toPath (), outputDir ));
588+ }
589+ });
590+ }
591+ break ;
592+ }
593+ }
579594 }
580595 }
581596
582597 private static void doConfigureKotlinJvmCompile (Project project , FileCollection allClassesDirs ,
583598 List <SourceDir > sourceDirs , SourceSet sourceSet ) {
584599 // Use KotlinJvmCompile.class in a separate method to prevent that maybeConfigureKotlinJvmCompile() runs into
585600 // a ClassNotFoundException due to actually using KotlinJvmCompile.class.
586- project .getTasks ().withType (KotlinJvmCompile .class , t -> configureCompileTask (t .getSources ().getAsFileTree (),
601+ project .getTasks ().withType (KotlinCompileTool .class , t -> configureCompileTask (t .getSources ().getAsFileTree (),
587602 t .getDestinationDirectory (), allClassesDirs , sourceDirs , t , sourceSet ));
588603 }
589604
590605 private static void configureCompileTask (FileTree sources , DirectoryProperty destinationDirectory ,
591606 FileCollection allClassesDirs , List <SourceDir > sourceDirs , Task task , SourceSet sourceSet ) {
592- if (!task .getEnabled ()) {
593- return ;
594- }
595- if (sources .isEmpty ()) {
607+ if (!task .getEnabled () || sources .isEmpty ()) {
596608 return ;
597609 }
598-
599610 final File destDir = destinationDirectory .getAsFile ().get ();
600611 if (!allClassesDirs .contains (destDir )) {
601612 return ;
602613 }
603- sources .visit (a -> {
614+ sources .visit (visitor -> {
604615 // we are looking for the root dirs containing sources
605- if (a .getRelativePath ().getSegments ().length == 1 ) {
606- final File srcDir = a .getFile ().getParentFile ();
607-
608- sourceDirs
609- .add (new DefaultSourceDir (srcDir .toPath (), destDir .toPath (),
610- findGeneratedSourceDir (destDir , sourceSet ),
611- Map .of ("compiler" , task .getName ())));
616+ if (visitor .getRelativePath ().getSegments ().length == 1 ) {
617+ final File srcDir = visitor .getFile ().getParentFile ();
618+ sourceDirs .add (new DefaultSourceDir (srcDir .toPath (), destDir .toPath (),
619+ findGeneratedSourceDir (destDir , sourceSet ),
620+ Map .of ("compiler" , task .getName ())));
612621 }
613622 });
614623 }
@@ -620,9 +629,6 @@ private static Path findGeneratedSourceDir(File destDir, SourceSet sourceSet) {
620629 }
621630 String language = destDir .getParentFile ().getName (); // java
622631 String sourceSetName = destDir .getName (); // main
623- if (language == null ) {
624- return null ;
625- }
626632 // find the corresponding generated sources, same pattern, but under build/generated/sources/annotationProcessor/java/main
627633 for (File generatedDir : sourceSet .getOutput ().getGeneratedSourcesDirs ().getFiles ()) {
628634 if (generatedDir .getParentFile () == null ) {
@@ -636,6 +642,37 @@ private static Path findGeneratedSourceDir(File destDir, SourceSet sourceSet) {
636642 return null ;
637643 }
638644
645+ /**
646+ * This method is meant to figure out the output directory containing class files for a compile task
647+ * which is not available in the plugin classpath. An example would be KotlinCompile.
648+ *
649+ * @param compileTask a compile task
650+ */
651+ private static Path getClassesOutputDir (Task compileTask ) {
652+ if (compileTask .getOutputs ().getHasOutput ()) {
653+ final AtomicReference <Path > result = new AtomicReference <>();
654+ compileTask .getOutputs ().getFiles ().getAsFileTree ().visit (visitor -> {
655+ // We are looking for the first class file, since a compile task would typically
656+ // have a single output location for classes.
657+ // There in fact could be a few output locations, the rest though would typically be some internal caching bits
658+ if (visitor .getName ().endsWith (".class" )) {
659+ visitor .stopVisiting ();
660+ var file = visitor .getFile ();
661+ int relativeSegments = visitor .getRelativePath ().getSegments ().length ;
662+ while (file != null && relativeSegments > 0 ) {
663+ relativeSegments --;
664+ file = file .getParentFile ();
665+ }
666+ if (file != null ) {
667+ result .set (file .toPath ());
668+ }
669+ }
670+ });
671+ return result .get ();
672+ }
673+ return null ;
674+ }
675+
639676 private void addSubstitutedProject (PathList .Builder paths , File projectFile ) {
640677 File mainResourceDirectory = new File (projectFile , MAIN_RESOURCES_OUTPUT );
641678 if (mainResourceDirectory .exists ()) {
0 commit comments