From 9e755d68c339836f627c9bcfe11ca75e4ce71b53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C5=BEe=20Sodja?= Date: Fri, 22 Mar 2024 14:06:32 +0100 Subject: [PATCH] Add example implementation of ide-starter --- gradle/libs.versions.toml | 2 +- .../studio/AndroidStudioSyncAction.java | 7 -- .../profiler/studio/StudioGradleClient.java | 24 ++++++ .../ide-provisioning-api/build.gradle.kts | 1 - .../org/gradle/profiler/ide/CommandChain.java | 8 ++ .../java/org/gradle/profiler/ide/RunIde.java | 5 -- .../gradle/profiler/ide/RunIdeContext.java | 6 ++ .../gradle/profiler/ide/RunIdeStarter.java | 5 ++ subprojects/ide-provisioning/build.gradle.kts | 1 + .../org/gradle/profiler/ide/RunIdeImpl.java | 8 -- .../gradle/profiler/ide/CommandChainImpl.kt | 30 ++++++++ .../gradle/profiler/ide/RunIdeContextImpl.kt | 16 ++++ .../gradle/profiler/ide/RunIdeStarterImpl.kt | 77 +++++++++++++++++++ .../services/org.gradle.profiler.ide.RunIde | 1 - .../org.gradle.profiler.ide.RunIdeStarter | 1 + 15 files changed, 169 insertions(+), 23 deletions(-) create mode 100644 subprojects/ide-provisioning-api/src/main/java/org/gradle/profiler/ide/CommandChain.java delete mode 100644 subprojects/ide-provisioning-api/src/main/java/org/gradle/profiler/ide/RunIde.java create mode 100644 subprojects/ide-provisioning-api/src/main/java/org/gradle/profiler/ide/RunIdeContext.java create mode 100644 subprojects/ide-provisioning-api/src/main/java/org/gradle/profiler/ide/RunIdeStarter.java delete mode 100644 subprojects/ide-provisioning/src/main/java/org/gradle/profiler/ide/RunIdeImpl.java create mode 100644 subprojects/ide-provisioning/src/main/kotlin/org/gradle/profiler/ide/CommandChainImpl.kt create mode 100644 subprojects/ide-provisioning/src/main/kotlin/org/gradle/profiler/ide/RunIdeContextImpl.kt create mode 100644 subprojects/ide-provisioning/src/main/kotlin/org/gradle/profiler/ide/RunIdeStarterImpl.kt delete mode 100644 subprojects/ide-provisioning/src/main/resources/META-INF/services/org.gradle.profiler.ide.RunIde create mode 100644 subprojects/ide-provisioning/src/main/resources/META-INF/services/org.gradle.profiler.ide.RunIdeStarter diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 551371dc..f66fe389 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ testAndroidSdkVersion = "7.3.0" [libraries] commonIo = "commons-io:commons-io:2.11.0" -ideStarter = "com.jetbrains.intellij.tools:ide-starter-squashed:233.14475.28" +ideStarter = "com.jetbrains.intellij.tools:ide-starter-squashed:233.15026.9" toolingApi = "org.gradle:gradle-tooling-api:7.2" spock-core = { module = "org.spockframework:spock-core", version.ref = "spock" } diff --git a/src/main/java/org/gradle/profiler/studio/AndroidStudioSyncAction.java b/src/main/java/org/gradle/profiler/studio/AndroidStudioSyncAction.java index dec37501..545743d2 100644 --- a/src/main/java/org/gradle/profiler/studio/AndroidStudioSyncAction.java +++ b/src/main/java/org/gradle/profiler/studio/AndroidStudioSyncAction.java @@ -2,16 +2,9 @@ import org.gradle.profiler.BuildAction; import org.gradle.profiler.GradleClient; -import org.gradle.profiler.ide.RunIde; -import org.gradle.profiler.instrument.GradleInstrumentation; import org.gradle.profiler.result.BuildActionResult; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.Arrays; import java.util.List; -import java.util.ServiceLoader; /** * A mock-up of Android studio sync. diff --git a/src/main/java/org/gradle/profiler/studio/StudioGradleClient.java b/src/main/java/org/gradle/profiler/studio/StudioGradleClient.java index 74e79924..da521512 100644 --- a/src/main/java/org/gradle/profiler/studio/StudioGradleClient.java +++ b/src/main/java/org/gradle/profiler/studio/StudioGradleClient.java @@ -5,6 +5,8 @@ import org.gradle.profiler.InvocationSettings; import org.gradle.profiler.client.protocol.ServerConnection; import org.gradle.profiler.client.protocol.messages.*; +import org.gradle.profiler.ide.RunIdeContext; +import org.gradle.profiler.ide.RunIdeStarter; import org.gradle.profiler.instrument.GradleInstrumentation; import org.gradle.profiler.result.BuildActionResult; import org.gradle.profiler.studio.invoker.StudioBuildActionResult; @@ -16,12 +18,15 @@ import org.gradle.profiler.studio.tools.StudioSandboxCreator.StudioSandbox; import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; import java.nio.file.Path; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.ServiceLoader; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -32,6 +37,8 @@ public class StudioGradleClient implements GradleClient { + + public enum CleanCacheMode { BEFORE_SCENARIO, BEFORE_BUILD, @@ -48,11 +55,13 @@ public enum CleanCacheMode { private final CleanCacheMode cleanCacheMode; private final ExecutorService executor; private final StudioSandbox sandbox; + private final InvocationSettings invocationSettings; private boolean isFirstRun; public StudioGradleClient(StudioGradleBuildConfiguration buildConfiguration, InvocationSettings invocationSettings, CleanCacheMode cleanCacheMode) { this.isFirstRun = true; this.cleanCacheMode = cleanCacheMode; + this.invocationSettings = invocationSettings; Path studioInstallDir = invocationSettings.getStudioInstallDir().toPath(); Optional studioSandboxDir = invocationSettings.getStudioSandboxDir(); this.sandbox = StudioSandboxCreator.createSandbox(studioSandboxDir.map(File::toPath).orElse(null)); @@ -64,7 +73,22 @@ public StudioGradleClient(StudioGradleBuildConfiguration buildConfiguration, Inv this.executor = Executors.newSingleThreadExecutor(); } + private RunIdeContext newIdeContext() { + URL[] classpath = GradleInstrumentation.getClasspath("ide-provisioning"); + RunIdeStarter ideStarter = ServiceLoader.load(RunIdeStarter.class, new URLClassLoader(classpath, RunIdeStarter.class.getClassLoader())).iterator().next(); + return ideStarter.newContext( + invocationSettings.getProjectDir().getAbsolutePath(), + invocationSettings.getStudioInstallDir().getAbsolutePath() + ); + } + public BuildActionResult sync(List gradleArgs, List jvmArgs) { + newIdeContext() + .withCommands() + .waitForSmartMode() + .importGradleProject() + .exitApp(); + if (shouldCleanCache()) { processController.runAndWaitToStop((connections) -> { System.out.println("* Cleaning Android Studio cache, this will require a restart..."); diff --git a/subprojects/ide-provisioning-api/build.gradle.kts b/subprojects/ide-provisioning-api/build.gradle.kts index 1d0ec6a7..fe6b02a0 100644 --- a/subprojects/ide-provisioning-api/build.gradle.kts +++ b/subprojects/ide-provisioning-api/build.gradle.kts @@ -1,6 +1,5 @@ plugins { id("profiler.embedded-library") - id("profiler.publication") } description = "Api for IDE provisioning capabilities for Gradle profiler" diff --git a/subprojects/ide-provisioning-api/src/main/java/org/gradle/profiler/ide/CommandChain.java b/subprojects/ide-provisioning-api/src/main/java/org/gradle/profiler/ide/CommandChain.java new file mode 100644 index 00000000..e87b5ee1 --- /dev/null +++ b/subprojects/ide-provisioning-api/src/main/java/org/gradle/profiler/ide/CommandChain.java @@ -0,0 +1,8 @@ +package org.gradle.profiler.ide; + +public interface CommandChain { + CommandChain importGradleProject(); + CommandChain waitForSmartMode(); + CommandChain exitApp(); + void run(); +} diff --git a/subprojects/ide-provisioning-api/src/main/java/org/gradle/profiler/ide/RunIde.java b/subprojects/ide-provisioning-api/src/main/java/org/gradle/profiler/ide/RunIde.java deleted file mode 100644 index 99ca89da..00000000 --- a/subprojects/ide-provisioning-api/src/main/java/org/gradle/profiler/ide/RunIde.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.gradle.profiler.ide; - -public interface RunIde { - void run(); -} diff --git a/subprojects/ide-provisioning-api/src/main/java/org/gradle/profiler/ide/RunIdeContext.java b/subprojects/ide-provisioning-api/src/main/java/org/gradle/profiler/ide/RunIdeContext.java new file mode 100644 index 00000000..85ed28c4 --- /dev/null +++ b/subprojects/ide-provisioning-api/src/main/java/org/gradle/profiler/ide/RunIdeContext.java @@ -0,0 +1,6 @@ +package org.gradle.profiler.ide; + +public interface RunIdeContext { + RunIdeContext withSystemProperty(String key, String value); + CommandChain withCommands(); +} diff --git a/subprojects/ide-provisioning-api/src/main/java/org/gradle/profiler/ide/RunIdeStarter.java b/subprojects/ide-provisioning-api/src/main/java/org/gradle/profiler/ide/RunIdeStarter.java new file mode 100644 index 00000000..b834c626 --- /dev/null +++ b/subprojects/ide-provisioning-api/src/main/java/org/gradle/profiler/ide/RunIdeStarter.java @@ -0,0 +1,5 @@ +package org.gradle.profiler.ide; + +public interface RunIdeStarter { + RunIdeContext newContext(String projectLocation, String ideLocation); +} diff --git a/subprojects/ide-provisioning/build.gradle.kts b/subprojects/ide-provisioning/build.gradle.kts index 70d468d1..c2eee914 100644 --- a/subprojects/ide-provisioning/build.gradle.kts +++ b/subprojects/ide-provisioning/build.gradle.kts @@ -16,6 +16,7 @@ dependencies { implementation(libs.ideStarter) { exclude(group = "io.ktor") } + implementation("org.kodein.di:kodein-di-jvm:7.20.2") testImplementation(libs.bundles.testDependencies) testImplementation(libs.commonIo) } diff --git a/subprojects/ide-provisioning/src/main/java/org/gradle/profiler/ide/RunIdeImpl.java b/subprojects/ide-provisioning/src/main/java/org/gradle/profiler/ide/RunIdeImpl.java deleted file mode 100644 index b51585d0..00000000 --- a/subprojects/ide-provisioning/src/main/java/org/gradle/profiler/ide/RunIdeImpl.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.gradle.profiler.ide; - -public class RunIdeImpl implements RunIde { - @Override - public void run() { - System.out.println("Running IDE"); - } -} diff --git a/subprojects/ide-provisioning/src/main/kotlin/org/gradle/profiler/ide/CommandChainImpl.kt b/subprojects/ide-provisioning/src/main/kotlin/org/gradle/profiler/ide/CommandChainImpl.kt new file mode 100644 index 00000000..d972a86b --- /dev/null +++ b/subprojects/ide-provisioning/src/main/kotlin/org/gradle/profiler/ide/CommandChainImpl.kt @@ -0,0 +1,30 @@ +package org.gradle.profiler.ide + +import com.intellij.ide.starter.ide.IDETestContext +import com.intellij.tools.ide.performanceTesting.commands.exitApp +import com.intellij.tools.ide.performanceTesting.commands.importGradleProject +import com.intellij.tools.ide.performanceTesting.commands.waitForSmartMode + +class CommandChainImpl(private val context: IDETestContext) : CommandChain { + + private var commandChain = com.intellij.tools.ide.performanceTesting.commands.CommandChain() + + override fun importGradleProject(): CommandChain { + commandChain = commandChain.importGradleProject() + return this + } + + override fun waitForSmartMode(): CommandChain { + commandChain = commandChain.waitForSmartMode() + return this + } + + override fun exitApp(): CommandChain { + commandChain = commandChain.exitApp() + return this + } + + override fun run() { + context.runIDE(commands = commandChain) + } +} diff --git a/subprojects/ide-provisioning/src/main/kotlin/org/gradle/profiler/ide/RunIdeContextImpl.kt b/subprojects/ide-provisioning/src/main/kotlin/org/gradle/profiler/ide/RunIdeContextImpl.kt new file mode 100644 index 00000000..da20d980 --- /dev/null +++ b/subprojects/ide-provisioning/src/main/kotlin/org/gradle/profiler/ide/RunIdeContextImpl.kt @@ -0,0 +1,16 @@ +package org.gradle.profiler.ide + +import com.intellij.ide.starter.ide.IDETestContext + +class RunIdeContextImpl(private val context: IDETestContext) : RunIdeContext { + override fun withSystemProperty(key: String, value: String): RunIdeContext { + context.applyVMOptionsPatch { + addSystemProperty(key, value) + } + return this + } + + override fun withCommands(): CommandChain { + return CommandChainImpl(context) + } +} diff --git a/subprojects/ide-provisioning/src/main/kotlin/org/gradle/profiler/ide/RunIdeStarterImpl.kt b/subprojects/ide-provisioning/src/main/kotlin/org/gradle/profiler/ide/RunIdeStarterImpl.kt new file mode 100644 index 00000000..bb8fc1c9 --- /dev/null +++ b/subprojects/ide-provisioning/src/main/kotlin/org/gradle/profiler/ide/RunIdeStarterImpl.kt @@ -0,0 +1,77 @@ +package org.gradle.profiler.ide + +import com.intellij.ide.starter.ide.IDETestContext +import com.intellij.ide.starter.ide.IdeDistributionFactory +import com.intellij.ide.starter.ide.IdeInstaller +import com.intellij.ide.starter.ide.InstalledIde +import com.intellij.ide.starter.ide.installer.ExistingIdeInstaller +import com.intellij.ide.starter.ide.installer.IdeInstallerFile +import com.intellij.ide.starter.models.IdeInfo +import com.intellij.ide.starter.models.TestCase +import com.intellij.ide.starter.path.GlobalPaths +import com.intellij.ide.starter.process.exec.ProcessExecutor +import com.intellij.ide.starter.project.LocalProjectInfo +import com.intellij.ide.starter.runner.TestContainer +import com.intellij.ide.starter.runner.TestContainerImpl +import com.intellij.openapi.util.SystemInfo +import com.intellij.openapi.util.io.FileUtil +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.div +import kotlin.io.path.name +import kotlin.time.Duration.Companion.minutes + +class RunIdeStarterImpl : RunIdeStarter { + override fun newContext(projectLocation: String, ideLocation: String): RunIdeContext { + val testVersion = "2023.2.3" + val ideInfo = IdeInfo( + productCode = "IC", + version = "2023.2", +// buildNumber = testVersion, + executableFileName = "idea", + fullName = "IntelliJ IDEA Community", + platformPrefix = "idea", + // getInstaller = { _ -> ExistingIdeInstaller(Paths.get(ideLocation)) } + ) + val testCase = TestCase( + ideInfo, + LocalProjectInfo(Paths.get(projectLocation)), + ) + val context = Starter.newContext("test", testCase) + return RunIdeContextImpl(context) + } + + class ExistingIdeInstaller(private val installedIdePath: Path) : IdeInstaller { + override fun install(ideInfo: IdeInfo, includeRuntimeModuleRepository: Boolean): Pair { + val ideInstaller = IdeInstallerFile(installedIdePath, "locally-installed-ide") + val installDir = GlobalPaths.instance + .getCacheDirectoryFor("builds") / "${ideInfo.productCode}-${ideInstaller.buildNumber}" + installDir.toFile().deleteRecursively() + val installedIde = installedIdePath.toFile() + val destDir = installDir.resolve(installedIdePath.name).toFile() + if (SystemInfo.isMac) { + ProcessExecutor("copy app", null, 5.minutes, emptyMap(), listOf("ditto", installedIde.absolutePath, destDir.absolutePath)).start() + } + else { + FileUtil.copyDir(installedIde, destDir) + } + return Pair( + ideInstaller.buildNumber, + IdeDistributionFactory.installIDE(installDir.toFile(), ideInfo.executableFileName) + ) + } + } + + private object Starter { + private fun newTestContainer(): TestContainer<*> { + return TestContainerImpl() + } + + fun newContext(testName: String, testCase: TestCase<*>, preserveSystemDir: Boolean = false): IDETestContext = + newTestContainer().initializeTestContext( + testName = testName, + testCase = testCase, + preserveSystemDir = preserveSystemDir + ) + } +} diff --git a/subprojects/ide-provisioning/src/main/resources/META-INF/services/org.gradle.profiler.ide.RunIde b/subprojects/ide-provisioning/src/main/resources/META-INF/services/org.gradle.profiler.ide.RunIde deleted file mode 100644 index fa254b85..00000000 --- a/subprojects/ide-provisioning/src/main/resources/META-INF/services/org.gradle.profiler.ide.RunIde +++ /dev/null @@ -1 +0,0 @@ -org.gradle.profiler.ide.RunIdeImpl diff --git a/subprojects/ide-provisioning/src/main/resources/META-INF/services/org.gradle.profiler.ide.RunIdeStarter b/subprojects/ide-provisioning/src/main/resources/META-INF/services/org.gradle.profiler.ide.RunIdeStarter new file mode 100644 index 00000000..d3c57009 --- /dev/null +++ b/subprojects/ide-provisioning/src/main/resources/META-INF/services/org.gradle.profiler.ide.RunIdeStarter @@ -0,0 +1 @@ +org.gradle.profiler.ide.RunIdeStarterImpl