diff --git a/se-commons-gradle/build.gradle b/se-commons-gradle/build.gradle index 6f36923..542bf72 100644 --- a/se-commons-gradle/build.gradle +++ b/se-commons-gradle/build.gradle @@ -16,13 +16,6 @@ dependencies { implementation "com.google.guava:guava:33.1.0-jre" } -publishing { - publications { - maven(MavenPublication) { - from(components.java) - } - } -} // Test our plugin infrastructure with a test-plugin sourceSets { diff --git a/se-commons-gradle/src/main/java/de/monticore/gradle/internal/ProgressLoggerService.java b/se-commons-gradle/src/main/java/de/monticore/gradle/internal/ProgressLoggerService.java index 6c2f05f..c75dcc2 100644 --- a/se-commons-gradle/src/main/java/de/monticore/gradle/internal/ProgressLoggerService.java +++ b/se-commons-gradle/src/main/java/de/monticore/gradle/internal/ProgressLoggerService.java @@ -10,6 +10,7 @@ /** * We use a build service to pass a ProgressLogger instance to the workers * See https://github.com/gradle/gradle/issues/2678 + * Note: Build services cannot be serialized when isolation a workqueue (when isolating the queue) */ public abstract class ProgressLoggerService implements BuildService { diff --git a/se-commons-gradle/src/main/java/de/monticore/gradle/internal/isolation/CachedIsolation.java b/se-commons-gradle/src/main/java/de/monticore/gradle/internal/isolation/CachedIsolation.java index 167a690..35e35d0 100644 --- a/se-commons-gradle/src/main/java/de/monticore/gradle/internal/isolation/CachedIsolation.java +++ b/se-commons-gradle/src/main/java/de/monticore/gradle/internal/isolation/CachedIsolation.java @@ -352,7 +352,10 @@ protected synchronized void cleanupOld(long pCloseThreshold) { // Close closeable classloaders try { ((Closeable) data.getClassLoader()).close(); - } catch (IOException ignored) { } + } catch (Throwable ignored) { + // In case java is eagerly unloading the classloader already, + // used classes from libraries might result in NoClassDefErrors + } } data.cleanUp(); isolated.remove(); diff --git a/se-commons-gradle/src/main/java/de/monticore/gradle/internal/isolation/IsolatedURLClassLoader.java b/se-commons-gradle/src/main/java/de/monticore/gradle/internal/isolation/IsolatedURLClassLoader.java index 496e265..0b05f26 100644 --- a/se-commons-gradle/src/main/java/de/monticore/gradle/internal/isolation/IsolatedURLClassLoader.java +++ b/se-commons-gradle/src/main/java/de/monticore/gradle/internal/isolation/IsolatedURLClassLoader.java @@ -75,7 +75,14 @@ public void close() throws IOException { } } // And finally, make *really* sure we unset their context classloader - for (Thread thread : Iterables.concat(Thread.getAllStackTraces().keySet(), shutdownHooks)) { + for (Thread thread : Thread.getAllStackTraces().keySet()) { + if (thread.getContextClassLoader() != IsolatedURLClassLoader.this) + continue; // but only threads within this context + thread.setContextClassLoader(null); + } + // duplicate code to avoid library usage here during cleanup + // (in case Iterables were to be used the first time) + for (Thread thread : shutdownHooks) { if (thread.getContextClassLoader() != IsolatedURLClassLoader.this) continue; // but only threads within this context thread.setContextClassLoader(null); diff --git a/se-commons-gradle/src/main/java/de/monticore/gradle/queue/CachedIsolationStats.java b/se-commons-gradle/src/main/java/de/monticore/gradle/queue/CachedIsolationStats.java index 626ddbf..d033cf6 100644 --- a/se-commons-gradle/src/main/java/de/monticore/gradle/queue/CachedIsolationStats.java +++ b/se-commons-gradle/src/main/java/de/monticore/gradle/queue/CachedIsolationStats.java @@ -5,16 +5,26 @@ import com.google.gson.Gson; import javax.annotation.Nullable; +import java.io.File; +import java.nio.file.Files; import java.util.*; import java.util.stream.Collectors; /** * Statistics collector for the cached isolated worker. - * + * Can be exported via the reportCachedQueueService gradle task + * or -Dde.se_rwth.workqueue.report.continuous=true */ public class CachedIsolationStats { - + // Unique ID for tracing reasons + final UUID instanceUUID = UUID.randomUUID(); + protected List events = Collections.synchronizedList(new ArrayList<>()); + protected boolean continuousTrack = shouldContinuousTrack(); + + protected boolean shouldContinuousTrack() { + return "true".equals(System.getProperty("de.se_rwth.workqueue.report.continuous", "false")); + } void track(EventKind kind, @Nullable UUID uuid, int semaphoreMax, List runners) { track(kind, uuid, semaphoreMax, runners, null); @@ -29,6 +39,22 @@ void track(EventKind kind, @Nullable UUID uuid, int semaphoreMax, List, AutoCloseable, BuildOperationListener { + implements ICachedQueueService, BuildService, AutoCloseable, BuildOperationListener { static CachedQueueService INSTANCE; @@ -95,7 +95,7 @@ protected void init(ServiceRegistry serviceRegistry) { * Time (in ms) after the last use of an isolated classloader before its * allocated resources are freed */ - protected long closeThreshold = 6 * 1000; // 6 seconds + protected long closeThreshold = 20 * 1000; // 20 seconds /** * We periodically clean up the open classloaders @@ -267,6 +267,7 @@ protected Set getPassThroughPackages() { } public void doExecuteWorkAction(UUID actionUUID) { + long timeWaited = System.currentTimeMillis(); try { // In case we run into the limit of maximum concurrent MontiCore Generation actions, // we wait until another generation has concluded @@ -275,14 +276,15 @@ public void doExecuteWorkAction(UUID actionUUID) { // Unable to acquire slot to run -> abort throw new RuntimeException(e); } + timeWaited = System.currentTimeMillis() - timeWaited; try { - doExecuteWorkAction(taskInfoMap.get(actionUUID)); + doExecuteWorkAction(taskInfoMap.get(actionUUID), timeWaited); } finally { semaphore.release(); } } - void doExecuteWorkAction(ActualTaskInfo info) { + void doExecuteWorkAction(ActualTaskInfo info, long timeWaitedForSemaphore) { Class parameterTypeNotIsolated = isolationScheme.parameterTypeFor(info.workActionClass); @@ -299,21 +301,23 @@ void doExecuteWorkAction(ActualTaskInfo info) { Isolatable paramIsol = serviceRegistry.get(IsolatableFactory.class).isolate(parametersUnsafe); - IsolatableSerializerRegistry isolatableSerializerRegistry = this.serviceRegistry.get(IsolatableSerializerRegistry.class); + IsolatableSerializerRegistryWrapper isolatableSerializerRegistry = getIsolatableSerializerRegistryWrapper(); ByteArrayOutputStream bos = new ByteArrayOutputStream(); Serializer serializer = isolatableSerializerRegistry.build(paramIsol.getClass()); try { serializer.write(new OutputStreamBackedEncoder(bos), paramIsol); } catch (Exception e) { - passThrowableAlong(e); + throw new RuntimeException("Failed to serialize " + info.workActionClass.getName() + " " + parameterTypeNotIsolated.getName() + " with " + parametersUnsafe.getClass(), e); } String prefix = parametersUnsafe instanceof CachedIsolatedWorkQueue.WorkQueueParameters ? ((CachedIsolatedWorkQueue.WorkQueueParameters) parametersUnsafe) .getPrefix().getOrElse("[WA]") : "[WA]"; String uniqueId = parametersUnsafe instanceof CachedIsolatedWorkQueue.WorkQueueParameters ? ((CachedIsolatedWorkQueue.WorkQueueParameters) parametersUnsafe).getStatsUniqueName().getOrElse("?") : "?"; - + + uniqueId += "," + timeWaitedForSemaphore + "ms"; + executeInClassloader(() -> { try { @@ -635,17 +639,71 @@ public void markForErasure() { /** - * Construc + * Construct a new WorkQueue * * @param workerExecutor the worker executor to use * @param extraClasspathElement the classpath elements to use * @return a new {@link WorkQueue} */ public WorkQueue newWorkQueue(WorkerExecutor workerExecutor, FileCollection extraClasspathElement) { + Objects.requireNonNull(workerExecutor, "worker executor must not be null"); + Objects.requireNonNull(serviceRegistry, "serviceRegistry must not be null"); return new CachedIsolatedWorkQueue(workerExecutor.noIsolation(), serviceRegistry.get(InstantiatorFactory.class), Objects.requireNonNull(serviceRegistry, "serviceRegistry"), this.providerSelf, extraClasspathElement); } + + // Gradle Version compat + protected IsolatableSerializerRegistryWrapper getIsolatableSerializerRegistryWrapper() { + return new IsolatableSerializerRegistryWrapper(this.serviceRegistry.get(getIsolatableSerializerRegistryClass())); + } + + protected Class getIsolatableSerializerRegistryClass() { + try { + return Class.forName("org.gradle.workers.internal.IsolatableSerializerRegistry"); + } + catch (ClassNotFoundException e) { + try { + return Class.forName("org.gradle.internal.snapshot.impl.IsolatableSerializerRegistry"); + } + catch (ClassNotFoundException ex) { + throw new IllegalStateException(ex); + } + } + } + + /** + * Wrapper around gradle internals. + * Must not refer to gradle internal classes to avoid errors during plugin-ASM-phase + */ + protected static class IsolatableSerializerRegistryWrapper { + + final Object instance; + + IsolatableSerializerRegistryWrapper(Object instance) { + this.instance = instance; + } + + Serializer build(Class baseType) { + try { + return (Serializer) instance.getClass().getMethod("build", Class.class) + .invoke(instance, baseType); + } + catch (ReflectiveOperationException e) { + throw new IllegalStateException("Failed to wrap IsolatableSerializerRegistry", e); + } + } + + Isolatable readIsolatable(Decoder decoder) { + try { + return (Isolatable) instance.getClass().getMethod("readIsolatable", Decoder.class) + .invoke(instance, decoder); + } + catch (ReflectiveOperationException e) { + throw new IllegalStateException("Failed to wrap IsolatableSerializerRegistry", e); + } + } + } } diff --git a/se-commons-gradle/src/main/java/de/monticore/gradle/queue/CachedQueueServicePlugin.java b/se-commons-gradle/src/main/java/de/monticore/gradle/queue/CachedQueueServicePlugin.java index 37b9371..9196d94 100644 --- a/se-commons-gradle/src/main/java/de/monticore/gradle/queue/CachedQueueServicePlugin.java +++ b/se-commons-gradle/src/main/java/de/monticore/gradle/queue/CachedQueueServicePlugin.java @@ -4,7 +4,6 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.gradle.api.Plugin; import org.gradle.api.Project; -import org.gradle.api.provider.Provider; import org.gradle.api.tasks.TaskProvider; /** @@ -13,29 +12,17 @@ public class CachedQueueServicePlugin implements Plugin { @Override public void apply(@NonNull Project project) { - // Register the service, if needed - Provider sp = project.getGradle().getSharedServices().registerIfAbsent(CachedQueueService.NAME, CachedQueueService.class, spec -> { - }); + // Register the service in the settings plugin + project.getGradle().getPluginManager().apply(InternalCachedQueueSetupSettingsPlugin.class); if (project == project.getRootProject()) { - // initialize the service but only if it is the root project - sp.get().init(project.getGradle()); - // Register an optional reporting task TaskProvider reportTask = project.getTasks().register("reportCachedQueueService", ReportCachedQueueServiceTask.class, spec -> { spec.mustRunAfter(project.getTasks().withType(ICachedQueueTask.class)); - spec.getSharedQueueService().set(sp); }); project.getTasks().withType(ICachedQueueTask.class).configureEach(task -> { task.finalizedBy(reportTask); }); } - - - // And configure all tasks with the ICachedQueueTask interface to access this service - - project.getTasks().withType(ICachedQueueTask.class).configureEach(task -> { - task.getSharedQueueService().set(sp); - }); } } diff --git a/se-commons-gradle/src/main/java/de/monticore/gradle/queue/ICachedQueueService.java b/se-commons-gradle/src/main/java/de/monticore/gradle/queue/ICachedQueueService.java new file mode 100644 index 0000000..5799a45 --- /dev/null +++ b/se-commons-gradle/src/main/java/de/monticore/gradle/queue/ICachedQueueService.java @@ -0,0 +1,49 @@ +package de.monticore.gradle.queue; + +import org.gradle.api.file.FileCollection; +import org.gradle.workers.WorkQueue; +import org.gradle.workers.WorkerExecutor; + +import java.lang.reflect.Proxy; + +/** + * @since 7.9.0 + */ +public interface ICachedQueueService { + + /** + * Construct a new WorkQueue + * + * @param workerExecutor the worker executor to use + * @param extraClasspathElement the classpath elements to use + * @return a new {@link WorkQueue} + */ + WorkQueue newWorkQueue(WorkerExecutor workerExecutor, FileCollection extraClasspathElement); + + /** + * Returns the tracked stats as a serialized JSON string. + * + * @return a serialized JSON string of the stats + * @see CachedIsolationStats + */ + String getStats(); + + void setMaxConcurrentMC(int maxParallelMC); + + void setCloseThreshold(long closeThreshold); + + static ICachedQueueService asICachedQueueService(Object sharedQueueService) { + if (sharedQueueService instanceof ICachedQueueService) { + return (ICachedQueueService) sharedQueueService; + } + // Gradle might load the plugin into multiple classloaders (once per subproject where it is applied) + // If that is the case, ICachedQueueService (from CL 1) is not castable to ICachedQueueService (from CL 2) + // As we only expose the ICachedQueueService, we can create a proxy between both classloaders + return (ICachedQueueService) Proxy.newProxyInstance(CachedQueueService.class.getClassLoader(), + new Class[] { ICachedQueueService.class }, + (proxy, method, args) -> sharedQueueService.getClass() + .getMethod(method.getName(), method.getParameterTypes()) + .invoke(sharedQueueService, args)); + } + +} diff --git a/se-commons-gradle/src/main/java/de/monticore/gradle/queue/ICachedQueueTask.java b/se-commons-gradle/src/main/java/de/monticore/gradle/queue/ICachedQueueTask.java index 1f26654..ba86076 100644 --- a/se-commons-gradle/src/main/java/de/monticore/gradle/queue/ICachedQueueTask.java +++ b/se-commons-gradle/src/main/java/de/monticore/gradle/queue/ICachedQueueTask.java @@ -5,11 +5,25 @@ import org.gradle.api.provider.Property; import org.gradle.api.tasks.Internal; +import java.lang.reflect.Proxy; + /** * A task with access to a cached queue service. * Automatically managed via the {@link CachedQueueServicePlugin} */ public interface ICachedQueueTask extends Task { + + // Type must be object due to https://github.com/gradle/gradle/issues/17559 + @Internal + Property getSharedQueueServiceProperty(); + @Internal - Property getSharedQueueService(); + @Deprecated(forRemoval = true) + default Property getSharedQueueService() { + return (Property) ((Property) getSharedQueueServiceProperty()); + } + + default ICachedQueueService doGetSharedQueueService() { + return ICachedQueueService.asICachedQueueService(getSharedQueueServiceProperty().get()); + } } diff --git a/se-commons-gradle/src/main/java/de/monticore/gradle/queue/InternalCachedQueueSetupSettingsPlugin.java b/se-commons-gradle/src/main/java/de/monticore/gradle/queue/InternalCachedQueueSetupSettingsPlugin.java new file mode 100644 index 0000000..b62f02f --- /dev/null +++ b/se-commons-gradle/src/main/java/de/monticore/gradle/queue/InternalCachedQueueSetupSettingsPlugin.java @@ -0,0 +1,43 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.gradle.queue; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.invocation.Gradle; +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; +import org.gradle.api.provider.Provider; + +/** + * Base plugin managing the cached isolation service + */ +public class InternalCachedQueueSetupSettingsPlugin implements Plugin { + protected Logger logger = Logging.getLogger(CachedQueueService.class); + @Override + public void apply(@NonNull Gradle gradle) { + // Register the service, if needed + Provider sp = gradle.getSharedServices().registerIfAbsent(CachedQueueService.NAME, CachedQueueService.class, spec -> { + }); + try { + sp.get().init(gradle); + }catch (ClassCastException ignored) { + logger.warn("WARNING: The se-commons cached work queue is setup multiple times.\n" + + "Reporting, log prefixes, and performance might be incorrect. \n" + + "Please add the (possibly MontiCore Generator) plugin to your root plugin (possible with apply false). \n" + + "More details: https://monticore.github.io/monticore/docs/Gradle/#root-warning"); + } + gradle.allprojects(p->withType(p, sp)); + } + + protected void withType(Project p, Provider sp) { + p.getTasks().withType(ICachedQueueTask.class).configureEach(task -> { + task.getSharedQueueServiceProperty().set(sp); + }); + // The report task has to avoid circular dependencies + p.getTasks().withType(ReportCachedQueueServiceTask.class).configureEach(task -> { + task.getSharedQueueService().set(sp); + }); + } + +} diff --git a/se-commons-gradle/src/test/java/IsolatedWorkerQueueTest.java b/se-commons-gradle/src/test/java/IsolatedWorkerQueueTest.java index 0f846b0..18fa508 100644 --- a/se-commons-gradle/src/test/java/IsolatedWorkerQueueTest.java +++ b/se-commons-gradle/src/test/java/IsolatedWorkerQueueTest.java @@ -3,6 +3,7 @@ import org.gradle.testkit.runner.BuildResult; import org.gradle.testkit.runner.GradleRunner; import org.gradle.testkit.runner.TaskOutcome; +import org.gradle.testkit.runner.UnexpectedBuildFailure; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -83,7 +84,7 @@ public void testCLIsolation() throws Exception { @ParameterizedTest - @ValueSource(strings = {"7.4.2", "7.6.4", "8.0.1", "8.7"}) + @ValueSource(strings = {"7.4.2", "7.6.4", "7.6.6", "8.0.1", "8.7", "8.14"}) // gradle 9 requires class file version 61 public void testSharedIsolation(String version) throws Exception { File projectDir = new File("build/functionalTest/shared/" + version); projectDir.mkdirs(); @@ -119,8 +120,34 @@ public void testSharedIsolation(String version) throws Exception { Assertions.assertEquals(4, StringUtils.countMatches(json, "\"START\"")); Assertions.assertEquals(4, StringUtils.countMatches(json, "\"DONE\"")); } - - + + @Test + + public void testSharedBuildService( ) throws Exception { + File projectDir = new File("build/functionalTest/shared_bs/"); + projectDir.mkdirs(); + new File(projectDir, "settings.gradle").createNewFile(); + write(new File(projectDir, "build.gradle"), + " plugins {\n" + " id 'se.rwth.example' \n" + " } \n" + + " import se.rwth.example.ExampleTask \n" + + " import se.rwth.example.ExampleTask.WorkerKind \n" + + " tasks.register('A', ExampleTask.class, t -> {t.taskNames.add('A1'); t.taskNames.add('A2'); t.workerKind = WorkerKind.SHARED; t.withTestService = true;} ) \n" + + " tasks.register('B', ExampleTask.class, t -> {t.taskNames.add('B1'); t.taskNames.add('B2'); t.waitSeconds = 1; t.workerKind = WorkerKind.SHARED; t.withTestService = true;} ) \n" + + " B.dependsOn(A)\n" + " \n"); + + Assertions.assertThrows(UnexpectedBuildFailure.class, () -> { + BuildResult result = GradleRunner.create() + .withGradleVersion("7.6.4") + .withProjectDir(projectDir).withPluginClasspath() + .withArguments("A", "B", "--stacktrace").build(); + }); + // Unfortunately, isolation and shared build services is not supported by gradle: + // Caused by: java.lang.UnsupportedOperationException: Build services cannot be serialized + // https://github.com/gradle/gradle/issues/28061#issuecomment-1945685806 + + } + + protected void write(File file, String content) throws IOException { Files.write(file.toPath(), content.getBytes()); } diff --git a/se-commons-gradle/src/testPlugin/java/se/rwth/example/ExampleTask.java b/se-commons-gradle/src/testPlugin/java/se/rwth/example/ExampleTask.java index e97e195..74fda27 100644 --- a/se-commons-gradle/src/testPlugin/java/se/rwth/example/ExampleTask.java +++ b/se-commons-gradle/src/testPlugin/java/se/rwth/example/ExampleTask.java @@ -1,11 +1,13 @@ /* (c) https://github.com/MontiCore/monticore */ package se.rwth.example; +import de.monticore.gradle.internal.ProgressLoggerService; import de.monticore.gradle.queue.ICachedQueueTask; import org.gradle.api.DefaultTask; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Internal; import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.TaskAction; import org.gradle.workers.WorkQueue; @@ -31,7 +33,11 @@ public abstract class ExampleTask extends DefaultTask implements ICachedQueueTas @Input @Optional abstract public Property getWorkerKind(); - + + @Input + @Optional + abstract public Property getWithTestService(); + @TaskAction public void execute() { WorkQueue queue; @@ -43,7 +49,7 @@ public void execute() { queue = getWorkerExecutor().classLoaderIsolation(); break; case SHARED: - queue = getSharedQueueService().get().newWorkQueue(getWorkerExecutor(), getProject().getObjects().fileCollection()); + queue = doGetSharedQueueService().newWorkQueue(getWorkerExecutor(), getProject().getObjects().fileCollection()); break; default: throw new IllegalStateException("Unknown worker kind: " + getWorkerKind().get()); @@ -52,11 +58,15 @@ public void execute() { int waitSeconds = getWaitSeconds().getOrElse(1); for (final String name : getTaskNames().get()) { final int secondForThisRun = waitSeconds += 2; - queue.submit(TestAction.class, task -> { - task.getWaitSeconds().set(secondForThisRun); - task.getName().set(name); - task.getPrefix().set("[" + name + "]"); // and set the prefix - task.getStatsUniqueName().set(name); + queue.submit(TestAction.class, params -> { + params.getWaitSeconds().set(secondForThisRun); + params.getName().set(name); + params.getPrefix().set("[" + name + "]"); // and set the prefix + params.getStatsUniqueName().set(name); + params.getWithTestService().set(this.getWithTestService().orElse(false)); + if (this.getWithTestService().getOrElse(false)) { + params.getProgressLogger().set(getProgressLoggerService()); + } }); } @@ -67,4 +77,8 @@ public enum WorkerKind { CL, // classloader isolation SHARED // shared isolation } + + + @Internal + public abstract Property getProgressLoggerService(); } diff --git a/se-commons-gradle/src/testPlugin/java/se/rwth/example/ReportStatsTask.java b/se-commons-gradle/src/testPlugin/java/se/rwth/example/ReportStatsTask.java index b248d7a..9f4428f 100644 --- a/se-commons-gradle/src/testPlugin/java/se/rwth/example/ReportStatsTask.java +++ b/se-commons-gradle/src/testPlugin/java/se/rwth/example/ReportStatsTask.java @@ -12,7 +12,7 @@ public abstract class ReportStatsTask extends DefaultTask implements ICachedQueu @TaskAction public void execute() { - System.err.println("Stats[[" + getSharedQueueService().get().getStats() + "]]Stats"); + System.err.println("Stats[[" + doGetSharedQueueService().getStats() + "]]Stats"); } } diff --git a/se-commons-gradle/src/testPlugin/java/se/rwth/example/TestAction.java b/se-commons-gradle/src/testPlugin/java/se/rwth/example/TestAction.java index 6a94c84..8011167 100644 --- a/se-commons-gradle/src/testPlugin/java/se/rwth/example/TestAction.java +++ b/se-commons-gradle/src/testPlugin/java/se/rwth/example/TestAction.java @@ -19,6 +19,9 @@ public abstract class TestAction implements WorkAction { @Override public void execute() { + if (getParameters().getWithTestService().get()) { + getParameters().getProgressLogger().get(); + } try { System.out.println("before wait " + getParameters().getName().get()); Thread.sleep(1000L * getParameters().getWaitSeconds().get()); diff --git a/se-commons-gradle/src/testPlugin/java/se/rwth/example/TestParams.java b/se-commons-gradle/src/testPlugin/java/se/rwth/example/TestParams.java index 13a2ad0..6d49ecb 100644 --- a/se-commons-gradle/src/testPlugin/java/se/rwth/example/TestParams.java +++ b/se-commons-gradle/src/testPlugin/java/se/rwth/example/TestParams.java @@ -1,10 +1,15 @@ /* (c) https://github.com/MontiCore/monticore */ package se.rwth.example; +import de.monticore.gradle.internal.ProgressLoggerService; import de.monticore.gradle.queue.CachedIsolatedWorkQueue; import org.gradle.api.provider.Property; public interface TestParams extends CachedIsolatedWorkQueue.WorkQueueParameters { Property getName(); Property getWaitSeconds(); + + // NOTE: Sharing BuildServices does not work with isolation!!! + Property getProgressLogger(); + Property getWithTestService(); } diff --git a/se-commons-gradle/src/testPlugin/java/se/rwth/example/TestPlugin.java b/se-commons-gradle/src/testPlugin/java/se/rwth/example/TestPlugin.java index 1688990..25fdd09 100644 --- a/se-commons-gradle/src/testPlugin/java/se/rwth/example/TestPlugin.java +++ b/se-commons-gradle/src/testPlugin/java/se/rwth/example/TestPlugin.java @@ -2,16 +2,24 @@ package se.rwth.example; +import de.monticore.gradle.internal.ProgressLoggerService; import de.monticore.gradle.queue.CachedQueueServicePlugin; import org.checkerframework.checker.nullness.qual.NonNull; import org.gradle.api.Plugin; import org.gradle.api.Project; +import org.gradle.api.provider.Provider; import org.gradle.api.tasks.TaskProvider; public class TestPlugin implements Plugin { @Override public void apply(@NonNull Project project) { + + // ServiceProvider to pass a ProgressLogger instance to the workers + Provider serviceProvider = project.getGradle().getSharedServices() + .registerIfAbsent(ProgressLoggerService.class.getSimpleName(), + ProgressLoggerService.class, spec -> {}); + // Register the service project.getPluginManager().apply(CachedQueueServicePlugin.class); @@ -20,6 +28,7 @@ public void apply(@NonNull Project project) { project.getTasks().withType(ExampleTask.class).configureEach(task -> { task.finalizedBy(reportStatsTask); + task.getProgressLoggerService().set(serviceProvider); }); } }