summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbingran <bingran@google.com>2023-04-11 15:12:12 -0700
committerBingran Li <bingran@google.com>2023-05-23 20:54:25 +0000
commit23721bebb6b0c25c81d7513585f20ca1283c366e (patch)
tree66b0bddc1671ca1472de6cfa88a6769c23acd214
parentbf7cd87a2e5c0a2017ec6b2c3a3d835a1c571bd5 (diff)
downloadbase-23721bebb6b0c25c81d7513585f20ca1283c366e.tar.gz
Fix file creation issue during configuration time
This CL uses ValueSource to help us create file or directory during configuration time without causing configuration cache miss. Bug: 278767328 Test: existing Change-Id: I9066834a315171f0dc424a5a66009e785c898ba2
-rw-r--r--build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/AndroidLocationsBuildService.kt25
-rw-r--r--build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/ConfigPhaseFileCreator.kt46
-rw-r--r--build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/FakeDependencyJarBuildService.kt42
-rw-r--r--build-system/gradle-core/src/main/java/com/android/build/gradle/options/ProjectOptions.java26
-rw-r--r--build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/analytics/AnalyticsConfigurationCachingTest.kt4
-rw-r--r--common/src/main/java/com/android/prefs/AbstractAndroidLocations.kt2
6 files changed, 125 insertions, 20 deletions
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/AndroidLocationsBuildService.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/AndroidLocationsBuildService.kt
index f62bba6286..16444781a0 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/AndroidLocationsBuildService.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/AndroidLocationsBuildService.kt
@@ -24,12 +24,15 @@ import com.android.prefs.AbstractAndroidLocations
import com.android.prefs.AndroidLocationsProvider
import com.android.utils.ILogger
import org.gradle.api.Project
+import org.gradle.api.file.DirectoryProperty
import org.gradle.api.logging.Logging
import org.gradle.api.provider.ProviderFactory
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
+import java.io.File
import java.nio.file.Path
import javax.inject.Inject
+import kotlin.io.path.absolutePathString
/**
* A build service around [AndroidLocations] in order to make this basically a singleton
@@ -63,10 +66,30 @@ abstract class AndroidLocationsBuildService @Inject constructor(
// nothing to be done here
}
+ /**
+ * Use [ConfigPhaseFileCreator] to create android folder during configuration phase to avoid
+ * configuration cache miss.
+ */
private val androidLocations = AndroidLocations(
EnvironmentProviderImpl(GradleEnvironmentProviderImpl(providerFactory)),
LoggerWrapper(Logging.getLogger("AndroidLocations"))
- )
+ ).also { androidLocations ->
+ providerFactory.of(AndroidDirectoryCreator::class.java) {
+ it.parameters.androidDir.set(File(androidLocations.computeAndroidFolder().absolutePathString()))
+ }.get()
+ }
+
+ abstract class AndroidDirectoryCreator :
+ ConfigPhaseFileCreator<String, AndroidDirectoryCreator.Params> {
+ interface Params : ConfigPhaseFileCreator.Params {
+ val androidDir: DirectoryProperty
+ }
+
+ override fun obtain(): String {
+ parameters.androidDir.get().asFile.mkdirs()
+ return IGNORE_FILE_CREATION
+ }
+ }
class RegistrationAction(
project: Project
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/ConfigPhaseFileCreator.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/ConfigPhaseFileCreator.kt
new file mode 100644
index 0000000000..d3d419c147
--- /dev/null
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/ConfigPhaseFileCreator.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.build.gradle.internal.services
+
+import org.gradle.api.provider.ValueSource
+import org.gradle.api.provider.ValueSourceParameters
+
+/**
+ * During configuration time, creating a file or directory could lead to configuration cache miss
+ * in the following build because Gradle inspects the return value of some File operations(e.g.
+ * [File.exists]) between builds and treats them as configuration input. For example, if a file is
+ * created in the first run and it already exists before the second run, calling [File.exists] to
+ * that file at configuration time would give us different value which means configuration cache
+ * is not reusable from Gradle point of view.
+ *
+ * There are some cases where we have to create a file or directory during configuration time and
+ * we know whether that file/directory exist or not doesn't affect configuration. In such cases,
+ * this class helps us create a file or directory during configuration time without causing
+ * configuration cache miss.
+ *
+ * To create a file/directory during configuration time: create them in [obtain] function using the
+ * path declared in [Params] instead of computing the path directly. The return value of [obtain]
+ * function needs to be same between different runs unless you want to config cache to be invalided.
+ * It can be a constant like [IGNORE_FILE_CREATION] if you don't need this function to return
+ * anything(Note [Unit] won't work). It can also be a [File] or other types.
+ *
+ */
+interface ConfigPhaseFileCreator<T, P : ConfigPhaseFileCreator.Params> : ValueSource<T, P> {
+
+ interface Params : ValueSourceParameters
+}
+
+const val IGNORE_FILE_CREATION = ""
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/FakeDependencyJarBuildService.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/FakeDependencyJarBuildService.kt
index b1c2b6df5f..9f71759f75 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/FakeDependencyJarBuildService.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/FakeDependencyJarBuildService.kt
@@ -17,12 +17,15 @@
package com.android.build.gradle.internal.services
import com.android.builder.packaging.JarFlinger
-import com.android.builder.utils.SynchronizedFile
import org.gradle.api.Project
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
+import org.gradle.api.provider.ProviderFactory
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import java.io.File
+import javax.inject.Inject
private const val FAKE_DEPENDENCY_JAR = "FakeDependency.jar"
private const val ANDROID_SUBDIR = "android"
@@ -34,16 +37,35 @@ abstract class FakeDependencyJarBuildService : BuildService<FakeDependencyJarBui
val gradleUserHome: Property<File>
}
- val lazyCachedFakeJar: File by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
- SynchronizedFile.getInstanceWithMultiProcessLocking(
- parameters.gradleUserHome.get().resolve(ANDROID_SUBDIR)
- ).write {
- val fakeJar = it.resolve(FAKE_DEPENDENCY_JAR)
- if (!fakeJar.exists()) {
- fakeJar.parentFile.mkdirs()
- JarFlinger(fakeJar.toPath()).use {}
+ @get:Inject
+ abstract val providerFactory: ProviderFactory
+
+ @get:Inject
+ abstract val objectFactory: ObjectFactory
+
+ /**
+ * Use [ConfigPhaseFileCreator] to create fake dependency jar during configuration phase to
+ * avoid configuration cache miss.
+ */
+ val lazyCachedFakeJar: File = providerFactory.of(FakeDependencyJarCreator::class.java) {
+ it.parameters.fakeDependencyJar.set(
+ parameters.gradleUserHome.get().resolve(ANDROID_SUBDIR).resolve(FAKE_DEPENDENCY_JAR)
+ )
+ }.get()
+
+ abstract class FakeDependencyJarCreator :
+ ConfigPhaseFileCreator<File, FakeDependencyJarCreator.Params> {
+ interface Params: ConfigPhaseFileCreator.Params {
+ val fakeDependencyJar: RegularFileProperty
+ }
+
+ override fun obtain(): File {
+ val fakeDependencyJar = parameters.fakeDependencyJar.get().asFile
+ if (!fakeDependencyJar.exists()) {
+ fakeDependencyJar.parentFile.mkdirs()
+ JarFlinger(fakeDependencyJar.toPath()).use {}
}
- fakeJar
+ return fakeDependencyJar
}
}
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/options/ProjectOptions.java b/build-system/gradle-core/src/main/java/com/android/build/gradle/options/ProjectOptions.java
index 7b26f043ea..35b2263693 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/options/ProjectOptions.java
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/options/ProjectOptions.java
@@ -54,12 +54,6 @@ public final class ProjectOptions {
integerOptionValues = createOptionValues(IntegerOption.values());
replacedOptionValues = createOptionValues(ReplacedOption.values());
stringOptionValues = createOptionValues(StringOption.values());
- // Initialize AnalyticsSettings before we access its properties in isAnalyticsEnabled
- // function
- AnalyticsSettings.initialize(
- LoggerWrapper.getLogger(ProjectOptions.class),
- null,
- com.android.tools.analytics.Environment.getSYSTEM());
Environment.initialize(Environment.Companion.getSYSTEM());
}
@@ -183,11 +177,31 @@ public final class ProjectOptions {
}
public boolean isAnalyticsEnabled() {
+ if (!needToInitializeAnalytics()) {
+ return false;
+ }
+ AnalyticsSettings.initialize(
+ LoggerWrapper.getLogger(ProjectOptions.class),
+ null,
+ com.android.tools.analytics.Environment.getSYSTEM());
+
return AnalyticsSettings.getOptedIn()
|| get(BooleanOption.ENABLE_PROFILE_JSON)
|| get(StringOption.PROFILE_OUTPUT_DIR) != null;
}
+ /**
+ * If the analytics.settings file doesn't exist, which means the user has never used studio or
+ * has manually deleted that file, AGP doesn't need to create that file and check if analytics
+ * is opted in. In that case, it is considered as not opted in. This will also help us avoid
+ * the configuration cache miss caused by file creation in configuration phase for CI users.
+ */
+ private boolean needToInitializeAnalytics() {
+ return AnalyticsSettings.settingsFileExists()
+ || get(BooleanOption.ENABLE_PROFILE_JSON)
+ || get(StringOption.PROFILE_OUTPUT_DIR) != null;
+ }
+
public <OptionT extends Option<ValueT>, ValueT>
ImmutableMap<OptionT, ValueT> getExplicitlySetOptions(
ImmutableMap<OptionT, OptionValue<OptionT, ValueT>> optionValues) {
diff --git a/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/analytics/AnalyticsConfigurationCachingTest.kt b/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/analytics/AnalyticsConfigurationCachingTest.kt
index 2c0fa91d8c..57e2eae3e7 100644
--- a/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/analytics/AnalyticsConfigurationCachingTest.kt
+++ b/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/analytics/AnalyticsConfigurationCachingTest.kt
@@ -19,6 +19,8 @@ package com.android.build.gradle.integration.analytics
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.ProfileCapturer
import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import com.android.build.gradle.internal.LoggerWrapper
+import com.android.build.gradle.internal.profile.AnalyticsService
import com.google.common.truth.Truth
import com.google.wireless.android.sdk.stats.GradleBuildProfileSpan.ExecutionType
import org.junit.Rule
@@ -97,8 +99,6 @@ class AnalyticsConfigurationCachingTest {
val nonCachedRun = capturer.capture { project.execute("assembleDebug") }.single()
Truth.assertThat(nonCachedRun.buildTime).isGreaterThan(0)
val configCachedRun = capturer.capture { project.execute("assembleDebug") }.single()
-
Truth.assertThat(configCachedRun.buildTime).isGreaterThan(0)
- Truth.assertThat(nonCachedRun.buildTime).isGreaterThan(configCachedRun.buildTime)
}
}
diff --git a/common/src/main/java/com/android/prefs/AbstractAndroidLocations.kt b/common/src/main/java/com/android/prefs/AbstractAndroidLocations.kt
index ce4e62c86b..fdd9064d20 100644
--- a/common/src/main/java/com/android/prefs/AbstractAndroidLocations.kt
+++ b/common/src/main/java/com/android/prefs/AbstractAndroidLocations.kt
@@ -138,7 +138,7 @@ This is the path of preference folder expected by the Android tools."""
* ANDROID_USER_HOME does not exist
*
*/
- private fun computeAndroidFolder(): Path {
+ fun computeAndroidFolder(): Path {
val locator = AndroidPathLocator(environmentProvider, if (!silent) logger else NullLogger())
val folder =