Skip to content

Commit

Permalink
Merge pull request #579 from gradle/asodja/upgrade-profiler
Browse files Browse the repository at this point in the history
Make gradle-profiler work with Android Studio Koala and Ladybug
  • Loading branch information
asodja authored Nov 4, 2024
2 parents 7570436 + 109ecd4 commit 40500b6
Show file tree
Hide file tree
Showing 20 changed files with 252 additions and 180 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.idea/
.intellijPlatform/
*.iml
*.ipr
*.iws
Expand All @@ -15,6 +16,7 @@
**/.settings/

.gradle/
.kotlin/

.DS_Store

Expand Down
2 changes: 1 addition & 1 deletion .teamcity/settings/extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fun BuildType.agentRequirement(os: Os, arch: Arch = Arch.AMD64) {
fun toolchainConfiguration(os: Os) = listOf(
"-Porg.gradle.java.installations.auto-detect=false",
"-Porg.gradle.java.installations.auto-download=false",
""""-Porg.gradle.java.installations.paths=%${os.name}.java8.oracle.64bit%,%${os.name}.java11.openjdk.64bit%""""
""""-Porg.gradle.java.installations.paths=%${os.name}.java8.oracle.64bit%,%${os.name}.java11.openjdk.64bit%,%${os.name}.java17.openjdk.64bit%""""
).joinToString(" ")

fun ParametrizedWithType.javaHome(os: Os, javaVersion: JavaVersion) {
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,8 @@ Here is an example:
androidStudioSync {
title = "Android Studio Sync"
# Measure an Android studio sync
# Note: Android Studio Bumblebee (2021.1.1) or newer is required
# Note2: If you are testing with Android Studio Giraffe (2022.3) or later
# you need to have local.properties file in your project with sdk.dir set
# Note: Android Studio Hedgehog (2023.1.1) or newer is required
# Note2: you need to have local.properties file in your project with sdk.dir set
android-studio-sync {
# Override default Android Studio jvm args
# studio-jvm-args = ["-Xms256m", "-Xmx4096m"]
Expand Down
48 changes: 21 additions & 27 deletions buildSrc/src/main/kotlin/profiler.android-studio-setup.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@ import extensions.AndroidStudioTestExtension
import tasks.InstallAndroidSdkTask
import providers.AndroidStudioInstallation
import providers.AndroidStudioSystemProperties
import tasks.ExtractAndroidStudioTask

repositories {
ivy {
// Url of Android Studio archive
url = uri("https://redirector.gvt1.com/edgedl/android/studio/ide-zips")
patternLayout {
artifact("[revision]/[artifact]-[revision]-[ext]")
}
metadataSources { artifact() }
content {
includeGroup("android-studio")
listOf(
// Urls of Android Studio archive
"https://redirector.gvt1.com/edgedl/android/studio/ide-zips",
"https://redirector.gvt1.com/edgedl/android/studio/install"
).forEach {
ivy {
url = uri(it)
patternLayout {
artifact("[revision]/[artifact]-[revision]-[ext]")
}
metadataSources { artifact() }
content {
includeGroup("android-studio")
}
}
}
}
Expand All @@ -35,29 +41,17 @@ val androidStudioRuntime by configurations.creating
dependencies {
val fileExtension = when {
isWindows() -> "windows.zip"
isMacOS() && isIntel() -> "mac.zip"
isMacOS() && !isIntel() -> "mac_arm.zip"
isMacOS() && isIntel() -> "mac.dmg"
isMacOS() && !isIntel() -> "mac_arm.dmg"
isLinux() -> "linux.tar.gz"
else -> throw IllegalStateException("Unsupported OS: $os")
}
androidStudioRuntime(extension.testAndroidStudioVersion.map { version -> "android-studio:android-studio:$version@$fileExtension" })
}

val unpackAndroidStudio = tasks.register<Copy>("unpackAndroidStudio") {
from(Callable {
val singleFile = androidStudioRuntime.singleFile
when {
singleFile.name.endsWith(".tar.gz") -> tarTree(singleFile)
else -> zipTree(singleFile)
}
}) {
eachFile {
// Remove top folder when unzipping, that way we get rid of Android Studio.app folder that can cause issues on Mac
// where MacOS would kill the Android Studio process right after start, issue: https://github.com/gradle/gradle-profiler/issues/469
relativePath = RelativePath(true, *relativePath.segments.drop(1).toTypedArray())
}
}
into(layout.buildDirectory.dir("android-studio"))
val unpackAndroidStudio = tasks.register<ExtractAndroidStudioTask>("unpackAndroidStudio") {
androidStudioLocation = androidStudioRuntime
outputDir = layout.buildDirectory.dir("android-studio")
}

val installAndroidSdk = tasks.register<InstallAndroidSdkTask>("installAndroidSdk") {
Expand All @@ -68,7 +62,7 @@ val installAndroidSdk = tasks.register<InstallAndroidSdkTask>("installAndroidSdk
}

val androidStudioInstallation = objects.newInstance<AndroidStudioInstallation>().apply {
studioInstallLocation.fileProvider(unpackAndroidStudio.map { it.destinationDir })
studioInstallLocation = unpackAndroidStudio.flatMap { it.outputDir }
}

tasks.withType<Test>().configureEach {
Expand Down
85 changes: 85 additions & 0 deletions buildSrc/src/main/kotlin/tasks/ExtractAndroidStudioTask.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package tasks

import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileSystemOperations
import org.gradle.api.file.RelativePath
import org.gradle.api.internal.file.FileOperations
import org.gradle.api.tasks.*
import org.gradle.process.ExecOperations
import org.gradle.work.DisableCachingByDefault
import java.io.File
import javax.inject.Inject


@DisableCachingByDefault(because = "Not worth caching")
abstract class ExtractAndroidStudioTask @Inject constructor(
private val execOps: ExecOperations,
private val fsOps: FileSystemOperations,
private val fileOps: FileOperations
) : DefaultTask() {

companion object {
private const val VOLUME_NAME = "AndroidStudioForGradle"
}

@get:InputFiles
@get:PathSensitive(PathSensitivity.NONE)
abstract val androidStudioLocation: ConfigurableFileCollection

@get:OutputDirectory
abstract val outputDir: DirectoryProperty

@TaskAction
fun extract() {
val androidStudioDistribution = androidStudioLocation.singleFile
when {
androidStudioDistribution.name.endsWith(".dmg") -> extractDmg(androidStudioDistribution)
else -> extractZipOrTar(androidStudioDistribution)
}
}

fun extractZipOrTar(androidStudioDistribution: File) {
fsOps.copy {
val src = when {
androidStudioDistribution.name.endsWith(".tar.gz") -> fileOps.tarTree(androidStudioDistribution)
else -> fileOps.zipTree(androidStudioDistribution)
}

from(src) {
eachFile {
// Remove top folder when unzipping, that way we get rid of Android Studio.app folder that can cause issues on Mac
// where MacOS would kill the Android Studio process right after start, issue: https://github.com/gradle/gradle-profiler/issues/469
@Suppress("SpreadOperator")
relativePath = RelativePath(true, *relativePath.segments.drop(1).toTypedArray())
}
}

into(outputDir)
}
}

fun extractDmg(androidStudioDistribution: File) {
val volumeDir = "/Volumes/$VOLUME_NAME"
val srcDir = "/Volumes/$VOLUME_NAME/Android Studio.app"
require(!File(srcDir).exists()) {
"The directory $srcDir already exists. Please unmount it via `hdiutil detach $volumeDir`."
}

try {
execOps.exec {
commandLine("hdiutil", "attach", androidStudioDistribution.absolutePath, "-mountpoint", volumeDir)
}

outputDir.get().asFile.mkdirs()
execOps.exec {
commandLine("cp", "-r", "$volumeDir/Android Studio.app/Contents", outputDir.get().asFile.absolutePath)
}
} finally {
execOps.exec {
commandLine("hdiutil", "detach", volumeDir)
}
}
}
}
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
profiler.version=0.22.0-SNAPSHOT
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.caching=true
org.gradle.parallel=true
org.gradle.unsafe.configuration-cache=false
Expand Down
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[versions]
groovy = "3.0.15"
spock = "2.1-groovy-3.0"
# Iguana (2023.2.1.7) Canary 7
testAndroidStudioVersion = "2023.2.1.7"
# Ladybug Patch 1 (2024.2.1.10)
testAndroidStudioVersion = "2024.2.1.10"
testAndroidSdkVersion = "7.3.0"

[libraries]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
import org.gradle.profiler.InvocationSettings;
import org.gradle.profiler.client.protocol.ServerConnection;
import org.gradle.profiler.client.protocol.messages.*;
import org.gradle.profiler.gradle.DaemonControl;
import org.gradle.profiler.instrument.GradleInstrumentation;
import org.gradle.profiler.result.BuildActionResult;
import org.gradle.profiler.studio.invoker.StudioBuildActionResult;
import org.gradle.profiler.studio.invoker.StudioGradleScenarioDefinition;
import org.gradle.profiler.studio.invoker.StudioGradleScenarioDefinition.StudioGradleBuildConfiguration;
import org.gradle.profiler.studio.process.StudioProcess.StudioConnections;
import org.gradle.profiler.studio.process.StudioProcessController;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@ abstract class AbstractProfilerIntegrationTest extends AbstractIntegrationTest {

@Shared
List<String> supportedGradleVersions = gradleVersionsSupportedOnCurrentJvm([
"3.3", "3.5",
"4.0", "4.7",
"5.2.1", "5.6.3",
"6.0.1", "6.1", "6.6.1",
"7.1.1", "7.5"
"5.6.4",
"6.9.4",
"7.6.4",
"8.0.2", "8.10.2"
])

@Shared
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import spock.lang.Requires
import static org.gradle.profiler.studio.AndroidStudioTestSupport.setupLocalProperties

/**
* If running this tests with Android Studio Giraffe (2022.3) or later you need ANDROID_HOME or ANDROID_SDK_ROOT set or
* having Android sdk installed in <user.home>/Library/Android/sdk (e.g. on Mac /Users/<username>/Library/Android/sdk)
* You need ANDROID_HOME or ANDROID_SDK_ROOT set or
* Android sdk installed in <user.home>/Library/Android/sdk (e.g. on Mac /Users/<username>/Library/Android/sdk)
*/
@ShowAndroidStudioLogsOnFailure
@Requires({ StudioFinder.findStudioHome() })
Expand Down Expand Up @@ -46,7 +46,6 @@ class AndroidStudioIntegrationTest extends AbstractProfilerIntegrationTest {

then:
logFile.find("Gradle invocation 1 has completed in").size() == 5
logFile.find("Gradle invocation 2 has completed in").size() == 0
logFile.find("Full sync has completed in").size() == 5
logFile.find("and it SUCCEEDED").size() == 5
logFile.find("* Cleaning Android Studio cache, this will require a restart...").size() == 0
Expand All @@ -73,22 +72,17 @@ class AndroidStudioIntegrationTest extends AbstractProfilerIntegrationTest {

then:
logFile.find("Gradle invocation 1 has completed in").size() == 2
logFile.find("Gradle invocation 2 has completed in").size() == 2
def firstDurations = (logFile.text =~ /Gradle invocation 1 has completed in: (\d+)ms/)
def duration = (logFile.text =~ /Gradle invocation 1 has completed in: (\d+)ms/)
.findAll()
.collect { it[1] as Integer }
def secondDurations = (logFile.text =~ /Gradle invocation 2 has completed in: (\d+)ms/)
.findAll()
.collect { it[1] as Integer }
logFile.find("Full Gradle execution time: ${firstDurations[0] + secondDurations[0]}ms").size() == 1
logFile.find("Full Gradle execution time: ${firstDurations[1] + secondDurations[1]}ms").size() == 1
logFile.find("Full Gradle execution time: ${duration[0]}ms").size() == 1
logFile.find("Full sync has completed in").size() == 2
logFile.find("and it SUCCEEDED").size() == 2
logFile.find("* Cleaning Android Studio cache, this will require a restart...").size() == 0
logFile.find("* Starting Android Studio").size() == 1

and:
resultFile.lines[3] == "value,total execution time,Gradle execution time #1,Gradle execution time #2,Gradle total execution time,IDE execution time"
resultFile.lines[3] == "value,total execution time,Gradle total execution time,IDE execution time"
}

def "benchmarks Android Studio sync by cleaning ide cache before build"() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,13 +386,13 @@ class BuildOperationInstrumentationIntegrationTest extends AbstractProfilerInteg
output.contains("Scenario using Gradle ${gradleVersion}: Measuring build configuration is only supported for Gradle 6.1-milestone-3 and later")

where:
gradleVersion << gradleVersionsSupportedOnCurrentJvm([minimalSupportedGradleVersion, "4.0", "4.10", "6.0"])
gradleVersion << gradleVersionsSupportedOnCurrentJvm([minimalSupportedGradleVersion, "6.0"])
}

def "complains when attempting to benchmark configuration time for build using unsupported Gradle version from scenario file"() {
given:
instrumentedBuildScript()
def unsupportedGradleVersions = gradleVersionsSupportedOnCurrentJvm(["${minimalSupportedGradleVersion}", "4.0", "4.10", "6.0"])
def unsupportedGradleVersions = gradleVersionsSupportedOnCurrentJvm(["${minimalSupportedGradleVersion}", "6.0"])
def scenarioFile = file("performance.scenarios")
scenarioFile.text = """
assemble {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class ChromeTraceIntegrationTest extends AbstractProfilerIntegrationTest {
@ShowAndroidStudioLogsOnFailure
@Requires({ StudioFinder.findStudioHome() })
@Requires({ AndroidStudioTestSupport.findAndroidSdkPath() })
def "profiles build to produce chrome trace output for builds with multiple gradle invocations"() {
def "profiles Android Studio build to produce chrome trace output for builds"() {
given:
def studioHome = StudioFinder.findStudioHome()
setupLocalProperties(new File(projectDir, "local.properties"))
Expand All @@ -104,8 +104,6 @@ class ChromeTraceIntegrationTest extends AbstractProfilerIntegrationTest {

then:
new File(outputDir, "scenario-${latestSupportedGradleVersion}-trace/scenario-${latestSupportedGradleVersion}-warm-up-build-1-invocation-1-trace.json").isFile()
new File(outputDir, "scenario-${latestSupportedGradleVersion}-trace/scenario-${latestSupportedGradleVersion}-warm-up-build-1-invocation-2-trace.json").isFile()
new File(outputDir, "scenario-${latestSupportedGradleVersion}-trace/scenario-${latestSupportedGradleVersion}-measured-build-1-invocation-1-trace.json").isFile()
new File(outputDir, "scenario-${latestSupportedGradleVersion}-trace/scenario-${latestSupportedGradleVersion}-measured-build-1-invocation-2-trace.json").isFile()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -367,11 +367,12 @@ class ProfilerIntegrationTest extends AbstractProfilerIntegrationTest {
println "<tasks: " + gradle.startParameter.taskNames + ">"
println "<daemon: " + gradle.services.get(org.gradle.internal.environment.GradleBuildEnvironment).longLivingProcess + ">"
"""
def buildScanVersion = "2.2.1"

when:
new Main().
run("--project-dir", projectDir.absolutePath, "--output-dir", outputDir.absolutePath, "--gradle-version", minimalSupportedGradleVersion,
"--profile", "buildscan", "--buildscan-version", "1.2",
"--profile", "buildscan", "--buildscan-version", buildScanVersion,
"--profile", "jfr",
"assemble")

Expand All @@ -380,7 +381,7 @@ class ProfilerIntegrationTest extends AbstractProfilerIntegrationTest {
logFile.find("<gradle-version: $minimalSupportedGradleVersion>").size() == 4
logFile.find("<daemon: true").size() == 4
logFile.find("<tasks: [assemble]>").size() == 3
assertBuildScanPublished("1.2")
assertBuildScanPublished(buildScanVersion)

def profileFile = new File(outputDir, "${minimalSupportedGradleVersion}.jfr")
profileFile.isFile()
Expand Down
9 changes: 5 additions & 4 deletions subprojects/studio-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ plugins {
groovy
`java-test-fixtures`
id("profiler.allprojects")
id("org.jetbrains.intellij") version "1.17.2"
id("org.jetbrains.intellij") version "1.17.4"
id("org.jetbrains.kotlin.jvm") version "2.0.21"
}

description = "Contains logic for Android Studio plugin that communicates with profiler"
Expand Down Expand Up @@ -37,7 +38,7 @@ project.configurations

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(11))
languageVersion.set(JavaLanguageVersion.of(17))
}
}

Expand All @@ -50,10 +51,10 @@ tasks.test {

intellij {
pluginName.set("gradle-profiler-studio-plugin")
version.set("2021.1.1")
version.set("2023.1.1")
// Don't override "since-build" and "until-build" properties in plugin.xml,
// so we don't need to update plugin to use it also on future IntelliJ versions.
updateSinceUntilBuild.set(false)
// Any plugin here must be also added to resources/META-INF/plugin.xml
plugins.set(listOf("java", "gradle", "android"))
plugins.set(listOf("java", "gradle", "org.jetbrains.android"))
}
Loading

0 comments on commit 40500b6

Please sign in to comment.