17
17
import java .util .Objects ;
18
18
import java .util .Properties ;
19
19
import java .util .Set ;
20
+ import java .util .concurrent .atomic .AtomicReference ;
20
21
21
22
import org .gradle .api .Project ;
22
23
import org .gradle .api .Task ;
36
37
import org .gradle .internal .composite .IncludedBuildInternal ;
37
38
import org .gradle .language .jvm .tasks .ProcessResources ;
38
39
import org .gradle .tooling .provider .model .ParameterizedToolingModelBuilder ;
39
- import org .jetbrains .kotlin .gradle .tasks .KotlinJvmCompile ;
40
+ import org .jetbrains .kotlin .gradle .tasks .KotlinCompileTool ;
40
41
41
42
import io .quarkus .bootstrap .BootstrapConstants ;
42
43
import io .quarkus .bootstrap .model .ApplicationModel ;
60
61
import io .quarkus .maven .dependency .GACT ;
61
62
import io .quarkus .maven .dependency .GACTV ;
62
63
import io .quarkus .maven .dependency .GAV ;
63
- import io .quarkus .maven .dependency .ResolvedDependency ;
64
64
import io .quarkus .maven .dependency .ResolvedDependencyBuilder ;
65
65
import io .quarkus .paths .PathCollection ;
66
66
import io .quarkus .paths .PathList ;
@@ -352,23 +352,20 @@ private void collectDependencies(org.gradle.api.artifacts.ResolvedDependency res
352
352
}
353
353
354
354
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 ());
359
357
SourceSetContainer sourceSets = projectDep == null ? null
360
358
: projectDep .getExtensions ().findByType (SourceSetContainer .class );
361
359
362
360
final String classifier = a .getClassifier ();
363
361
if (classifier == null || classifier .isEmpty ()) {
364
362
final IncludedBuild includedBuild = ToolingUtils .includedBuild (project .getRootProject (),
365
- (( ProjectComponentIdentifier ) a . getId (). getComponentIdentifier ()) .getBuild ().getName ());
363
+ compId .getBuild ().getName ());
366
364
if (includedBuild != null ) {
367
365
final PathList .Builder pathBuilder = PathList .builder ();
368
366
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 ());
372
369
}
373
370
if (projectDep != null ) {
374
371
projectModule = initProjectModuleAndBuildPaths (projectDep , a , modelBuilder , depBuilder ,
@@ -517,7 +514,6 @@ private static Properties readDescriptor(final Path path) {
517
514
518
515
private static void initProjectModule (Project project , WorkspaceModule .Mutable module , SourceSet sourceSet ,
519
516
String classifier ) {
520
-
521
517
if (sourceSet == null ) {
522
518
return ;
523
519
}
@@ -570,45 +566,57 @@ private static void initProjectModule(Project project, WorkspaceModule.Mutable m
570
566
571
567
private static void maybeConfigureKotlinJvmCompile (Project project , FileCollection allClassesDirs ,
572
568
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
+ }
579
593
}
580
594
}
581
595
582
596
private static void doConfigureKotlinJvmCompile (Project project , FileCollection allClassesDirs ,
583
597
List <SourceDir > sourceDirs , SourceSet sourceSet ) {
584
598
// Use KotlinJvmCompile.class in a separate method to prevent that maybeConfigureKotlinJvmCompile() runs into
585
599
// 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 (),
587
601
t .getDestinationDirectory (), allClassesDirs , sourceDirs , t , sourceSet ));
588
602
}
589
603
590
604
private static void configureCompileTask (FileTree sources , DirectoryProperty destinationDirectory ,
591
605
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 ()) {
596
607
return ;
597
608
}
598
-
599
609
final File destDir = destinationDirectory .getAsFile ().get ();
600
610
if (!allClassesDirs .contains (destDir )) {
601
611
return ;
602
612
}
603
- sources .visit (a -> {
613
+ sources .visit (visitor -> {
604
614
// 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 ())));
612
620
}
613
621
});
614
622
}
@@ -620,9 +628,6 @@ private static Path findGeneratedSourceDir(File destDir, SourceSet sourceSet) {
620
628
}
621
629
String language = destDir .getParentFile ().getName (); // java
622
630
String sourceSetName = destDir .getName (); // main
623
- if (language == null ) {
624
- return null ;
625
- }
626
631
// find the corresponding generated sources, same pattern, but under build/generated/sources/annotationProcessor/java/main
627
632
for (File generatedDir : sourceSet .getOutput ().getGeneratedSourcesDirs ().getFiles ()) {
628
633
if (generatedDir .getParentFile () == null ) {
@@ -636,6 +641,37 @@ private static Path findGeneratedSourceDir(File destDir, SourceSet sourceSet) {
636
641
return null ;
637
642
}
638
643
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
+
639
675
private void addSubstitutedProject (PathList .Builder paths , File projectFile ) {
640
676
File mainResourceDirectory = new File (projectFile , MAIN_RESOURCES_OUTPUT );
641
677
if (mainResourceDirectory .exists ()) {
0 commit comments