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,57 @@ 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+ // if the above failed, there could still be a KotlinCompile task that's not easily discoverable
581+ if (originalSourceDirsSize == sourceDirs .size ()) {
582+ final Path outputDir = getClassesOutputDir (task );
583+ if (outputDir != null && task .getInputs ().getHasInputs ()) {
584+ task .getInputs ().getSourceFiles ().getAsFileTree ().visit (visitor -> {
585+ if (visitor .getRelativePath ().getSegments ().length == 1 ) {
586+ sourceDirs .add (SourceDir .of (visitor .getFile ().getParentFile ().toPath (), outputDir ));
587+ }
588+ });
589+ }
590+ break ;
591+ }
592+ }
579593 }
580594 }
581595
582596 private static void doConfigureKotlinJvmCompile (Project project , FileCollection allClassesDirs ,
583597 List <SourceDir > sourceDirs , SourceSet sourceSet ) {
584598 // Use KotlinJvmCompile.class in a separate method to prevent that maybeConfigureKotlinJvmCompile() runs into
585599 // a ClassNotFoundException due to actually using KotlinJvmCompile.class.
586- project .getTasks ().withType (KotlinJvmCompile .class , t -> configureCompileTask (t .getSources ().getAsFileTree (),
600+ project .getTasks ().withType (KotlinCompileTool .class , t -> configureCompileTask (t .getSources ().getAsFileTree (),
587601 t .getDestinationDirectory (), allClassesDirs , sourceDirs , t , sourceSet ));
588602 }
589603
590604 private static void configureCompileTask (FileTree sources , DirectoryProperty destinationDirectory ,
591605 FileCollection allClassesDirs , List <SourceDir > sourceDirs , Task task , SourceSet sourceSet ) {
592- if (!task .getEnabled ()) {
593- return ;
594- }
595- if (sources .isEmpty ()) {
606+ if (!task .getEnabled () || sources .isEmpty ()) {
596607 return ;
597608 }
598-
599609 final File destDir = destinationDirectory .getAsFile ().get ();
600610 if (!allClassesDirs .contains (destDir )) {
601611 return ;
602612 }
603- sources .visit (a -> {
613+ sources .visit (visitor -> {
604614 // 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 ())));
615+ if (visitor .getRelativePath ().getSegments ().length == 1 ) {
616+ final File srcDir = visitor .getFile ().getParentFile ();
617+ sourceDirs .add (new DefaultSourceDir (srcDir .toPath (), destDir .toPath (),
618+ findGeneratedSourceDir (destDir , sourceSet ),
619+ Map .of ("compiler" , task .getName ())));
612620 }
613621 });
614622 }
@@ -620,9 +628,6 @@ private static Path findGeneratedSourceDir(File destDir, SourceSet sourceSet) {
620628 }
621629 String language = destDir .getParentFile ().getName (); // java
622630 String sourceSetName = destDir .getName (); // main
623- if (language == null ) {
624- return null ;
625- }
626631 // find the corresponding generated sources, same pattern, but under build/generated/sources/annotationProcessor/java/main
627632 for (File generatedDir : sourceSet .getOutput ().getGeneratedSourcesDirs ().getFiles ()) {
628633 if (generatedDir .getParentFile () == null ) {
@@ -636,6 +641,37 @@ private static Path findGeneratedSourceDir(File destDir, SourceSet sourceSet) {
636641 return null ;
637642 }
638643
644+ /**
645+ * This method is meant to figure out the output directory containing class files for a compile task
646+ * which is not available in the plugin classpath. An example would be KotlinCompile.
647+ *
648+ * @param compileTask a compile task
649+ */
650+ private static Path getClassesOutputDir (Task compileTask ) {
651+ if (compileTask .getOutputs ().getHasOutput ()) {
652+ final AtomicReference <Path > result = new AtomicReference <>();
653+ compileTask .getOutputs ().getFiles ().getAsFileTree ().visit (visitor -> {
654+ // We are looking for the first class file, since a compile task would typically
655+ // have a single output location for classes.
656+ // There in fact could be a few output locations, the rest though would typically be some internal caching bits
657+ if (visitor .getName ().endsWith (".class" )) {
658+ visitor .stopVisiting ();
659+ var file = visitor .getFile ();
660+ int relativeSegments = visitor .getRelativePath ().getSegments ().length ;
661+ while (file != null && relativeSegments > 0 ) {
662+ relativeSegments --;
663+ file = file .getParentFile ();
664+ }
665+ if (file != null ) {
666+ result .set (file .toPath ());
667+ }
668+ }
669+ });
670+ return result .get ();
671+ }
672+ return null ;
673+ }
674+
639675 private void addSubstitutedProject (PathList .Builder paths , File projectFile ) {
640676 File mainResourceDirectory = new File (projectFile , MAIN_RESOURCES_OUTPUT );
641677 if (mainResourceDirectory .exists ()) {
0 commit comments