diff --git a/BUILD b/BUILD
index 4502f06c..1d78d1d5 100644
--- a/BUILD
+++ b/BUILD
@@ -157,3 +157,7 @@ recipe_test(
recipe_test(
name = "variantOutput",
)
+
+recipe_test(
+ name = "registerPreBuild",
+)
diff --git a/recipes/registerPreBuild/README.md b/recipes/registerPreBuild/README.md
new file mode 100644
index 00000000..ebb119b4
--- /dev/null
+++ b/recipes/registerPreBuild/README.md
@@ -0,0 +1,32 @@
+# Register tasks to run before build using LifecycleTasks.registerPreBuild()
+
+This sample demonstrates how to use the [`LifecycleTasks.registerPreBuild()`](https://developer.android.com/reference/tools/gradle-api/current/com/android/build/api/variant/LifecycleTasks#registerPreBuild(kotlin.Array)) API
+to register tasks to run before a build begins.
+
+| Module | Content |
+|----------------------------|-------------------------------------------------------------|
+| [build-logic](build-logic) | Contains the Project plugin that is the core of the recipe. |
+| [app](app) | An Android application that has the plugin applied. |
+
+The [build-logic](build-logic) sub-project contains the [`CustomPlugin`](build-logic/plugins/src/main/kotlin/CustomPlugin.kt)
+and [`PreBuildValidationTask`](build-logic/plugins/src/main/kotlin/PreBuildValidationTask.kt) classes.
+
+[`CustomPlugin`](build-logic/plugins/src/main/kotlin/CustomPlugin.kt) contains the creation of the task provider for the [`PreBuildValidationTask`](build-logic/plugins/src/main/kotlin/PreBuildValidationTask.kt). The API call
+to register this task per-variant is below:
+
+```
+androidComponents.onVariants { variant ->
+ variant.lifecycleTasks.registerPreBuild(preBuildTaskProvider)
+}
+```
+
+[`CustomPlugin`](build-logic/plugins/src/main/kotlin/CustomPlugin.kt) also registers the `ValidateTask` which verifies that the pre-build task ran.
+
+## To Run
+To run the example, you can just do
+
+```
+./gradlew :app:build
+```
+
+and you should see the file created by the task in `app/build/preBuildOutput/gradle_version.txt`.
diff --git a/recipes/registerPreBuild/app/build.gradle.kts b/recipes/registerPreBuild/app/build.gradle.kts
new file mode 100644
index 00000000..b30b3444
--- /dev/null
+++ b/recipes/registerPreBuild/app/build.gradle.kts
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2024 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
+ *
+ * https://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.
+ */
+
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+ id("android.recipes.register_pre_build")
+}
+
+android {
+ namespace = "com.example.android.recipes.register_pre_build"
+ compileSdk = $COMPILE_SDK
+ defaultConfig {
+ minSdk = $MINIMUM_SDK
+ targetSdk = $COMPILE_SDK
+ }
+}
+
+java {
+ toolchain {
+ languageVersion.set(JavaLanguageVersion.of(17))
+ }
+}
diff --git a/recipes/registerPreBuild/app/src/main/AndroidManifest.xml b/recipes/registerPreBuild/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..8b7fed3d
--- /dev/null
+++ b/recipes/registerPreBuild/app/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/recipes/registerPreBuild/build-logic/gradle.properties b/recipes/registerPreBuild/build-logic/gradle.properties
new file mode 100644
index 00000000..3dcf88f0
--- /dev/null
+++ b/recipes/registerPreBuild/build-logic/gradle.properties
@@ -0,0 +1,2 @@
+# Gradle properties are not passed to included builds https://github.com/gradle/gradle/issues/2534
+org.gradle.parallel=true
diff --git a/recipes/registerPreBuild/build-logic/gradle/libs.versions.toml b/recipes/registerPreBuild/build-logic/gradle/libs.versions.toml
new file mode 100644
index 00000000..d362ae08
--- /dev/null
+++ b/recipes/registerPreBuild/build-logic/gradle/libs.versions.toml
@@ -0,0 +1,9 @@
+[versions]
+androidGradlePlugin = $AGP_VERSION
+kotlin = $KOTLIN_VERSION
+
+[libraries]
+android-gradlePlugin-api = { group = "com.android.tools.build", name = "gradle-api", version.ref = "androidGradlePlugin" }
+
+[plugins]
+kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
diff --git a/recipes/registerPreBuild/build-logic/plugins/build.gradle.kts b/recipes/registerPreBuild/build-logic/plugins/build.gradle.kts
new file mode 100644
index 00000000..6539205a
--- /dev/null
+++ b/recipes/registerPreBuild/build-logic/plugins/build.gradle.kts
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2024 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
+ *
+ * https://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.
+ */
+
+plugins {
+ `java-gradle-plugin`
+ alias(libs.plugins.kotlin.jvm)
+}
+
+java {
+ toolchain {
+ languageVersion.set(JavaLanguageVersion.of(17))
+ }
+}
+
+dependencies {
+ compileOnly(libs.android.gradlePlugin.api)
+ implementation(gradleKotlinDsl())
+}
+
+gradlePlugin {
+ plugins {
+ create("customPlugin") {
+ id = "android.recipes.register_pre_build"
+ implementationClass = "CustomPlugin"
+ }
+ }
+}
diff --git a/recipes/registerPreBuild/build-logic/plugins/src/main/kotlin/CustomPlugin.kt b/recipes/registerPreBuild/build-logic/plugins/src/main/kotlin/CustomPlugin.kt
new file mode 100644
index 00000000..8632b11c
--- /dev/null
+++ b/recipes/registerPreBuild/build-logic/plugins/src/main/kotlin/CustomPlugin.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2024 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
+ *
+ * https://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.
+ */
+
+import com.android.build.api.variant.AndroidComponentsExtension
+import com.android.build.gradle.AppPlugin
+import org.gradle.api.Plugin
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.Project
+import org.gradle.api.tasks.InputDirectory
+import org.gradle.api.tasks.TaskAction
+import org.gradle.configurationcache.extensions.capitalized
+import org.gradle.kotlin.dsl.register
+
+/**
+ * This custom plugin will register a callback that is applied to all variants.
+ */
+class CustomPlugin : Plugin {
+ override fun apply(project: Project) {
+ // Registers a callback on the application of the Android Application plugin.
+ // This allows the CustomPlugin to work whether it's applied before or after
+ // the Android Application plugin.
+ project.plugins.withType(AppPlugin::class.java) {
+
+ // Create the provider for the pre-build task
+ val preBuildTaskProvider = project.tasks.register("preBuildValidation") {
+ gradleVersion.set(project.gradle.gradleVersion)
+ output.set(project.layout.buildDirectory.dir("preBuildOutput"))
+ }
+
+ val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
+
+ // Registers a callback to be called, when a new variant is configured
+ androidComponents.onVariants { variant ->
+ // Register the pre-build task with the LifecycleTask API
+ variant.lifecycleTasks.registerPreBuild(preBuildTaskProvider)
+
+ // -- Verification --
+ // the following is just to validate the recipe and is not actually part of the recipe itself
+ project.tasks.register("validate${variant.name.capitalized()}") {
+ // The input of the validation task should be the output of the pre-build task.
+ // The normal way to do this would be:
+ // input.set(preBuildTaskProvider.flatMap { it.output }
+ // However, doing this will force running the pre-build task when we want it to run
+ // automatically when build is invoked.
+ // So we set the input manually, and the validation task will have to be called
+ // separately (in a separate Gradle execution or Gradle will detect the
+ // lack of dependency between the 2 tasks and complain).
+ input.set(project.layout.buildDirectory.dir("preBuildOutput"))
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Validation task to verify the behavior of the recipe
+ */
+abstract class ValidateTask : DefaultTask() {
+
+ @get:InputDirectory
+ abstract val input: DirectoryProperty
+
+ @TaskAction
+ fun taskAction() {
+ val gradleVersionFile = input.get().file("gradle_version.txt").asFile
+ if (!gradleVersionFile.exists()) {
+ throw RuntimeException("Expected file missing: $gradleVersionFile")
+ }
+ }
+}
diff --git a/recipes/registerPreBuild/build-logic/plugins/src/main/kotlin/PreBuildValidationTask.kt b/recipes/registerPreBuild/build-logic/plugins/src/main/kotlin/PreBuildValidationTask.kt
new file mode 100644
index 00000000..f080b8b3
--- /dev/null
+++ b/recipes/registerPreBuild/build-logic/plugins/src/main/kotlin/PreBuildValidationTask.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.TaskAction
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.provider.Property
+import java.io.File
+
+/**
+ * This is the pre-build task that runs before the build executes.
+ * The task does a trivial validation of the Gradle version, and produces an output for verification in another task.
+ */
+abstract class PreBuildValidationTask : DefaultTask() {
+
+ // In order for the task to be up-to-date when the inputs have not changed,
+ // the task must declare an output, even if it's not used. Tasks with no
+ // output are always run regardless of whether the inputs changed
+ @get:OutputDirectory
+ abstract val output: DirectoryProperty
+
+ @get:Input
+ abstract val gradleVersion: Property
+
+ @TaskAction
+ fun taskAction() {
+ // This is a trivial validation, but a more useful one could be used in this task
+ if (gradleVersion.get().isEmpty()) {
+ throw RuntimeException("The Gradle version must not be empty.")
+ }
+
+ // The next part of this task is done only for verification purposes
+ // delete the previous content. This task does not support incremental mode but could be modified to do so
+ val outputFile = output.get().asFile
+ outputFile.deleteRecursively()
+ outputFile.mkdirs()
+
+ val newFile = File(outputFile, "gradle_version.txt")
+ newFile.writeText(gradleVersion.get())
+ }
+}
diff --git a/recipes/registerPreBuild/build-logic/settings.gradle.kts b/recipes/registerPreBuild/build-logic/settings.gradle.kts
new file mode 100644
index 00000000..e2e5e9e5
--- /dev/null
+++ b/recipes/registerPreBuild/build-logic/settings.gradle.kts
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2024 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
+ *
+ * https://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.
+ */
+
+rootProject.name = "build-logic"
+
+pluginManagement {
+ repositories {
+ $AGP_REPOSITORY
+ $PLUGIN_REPOSITORIES
+ }
+}
+
+dependencyResolutionManagement {
+ repositories {
+ $AGP_REPOSITORY
+ $DEPENDENCY_REPOSITORIES
+ }
+}
+
+include(":plugins")
diff --git a/recipes/registerPreBuild/build.gradle.kts b/recipes/registerPreBuild/build.gradle.kts
new file mode 100644
index 00000000..68e631f2
--- /dev/null
+++ b/recipes/registerPreBuild/build.gradle.kts
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 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
+ *
+ * https://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.
+ */
+
+plugins {
+ alias(libs.plugins.android.application) apply false
+ alias(libs.plugins.kotlin.android) apply false
+}
diff --git a/recipes/registerPreBuild/gradle.properties b/recipes/registerPreBuild/gradle.properties
new file mode 100644
index 00000000..55cce922
--- /dev/null
+++ b/recipes/registerPreBuild/gradle.properties
@@ -0,0 +1,23 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
diff --git a/recipes/registerPreBuild/gradle/libs.versions.toml b/recipes/registerPreBuild/gradle/libs.versions.toml
new file mode 100644
index 00000000..8c672bba
--- /dev/null
+++ b/recipes/registerPreBuild/gradle/libs.versions.toml
@@ -0,0 +1,9 @@
+[versions]
+androidGradlePlugin = $AGP_VERSION
+kotlin = $KOTLIN_VERSION
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
+kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+
+
diff --git a/recipes/registerPreBuild/recipe_metadata.toml b/recipes/registerPreBuild/recipe_metadata.toml
new file mode 100644
index 00000000..e9e1e805
--- /dev/null
+++ b/recipes/registerPreBuild/recipe_metadata.toml
@@ -0,0 +1,29 @@
+# optional (if present and non-blank) name to use in the index
+indexName = ""
+# optional (if present and non-blank) folder name to use when converting recipe in RELEASE mode
+destinationFolder = ""
+
+description ="""
+ This sample shows how to register a task dependency on the PreBuild lifecycle task.
+ """
+
+[agpVersion]
+min = "8.4.0-alpha13"
+
+# Relevant Gradle tasks to run per recipe
+[gradleTasks]
+tasks = [
+ "assembleDebug"
+]
+validationTasks = [
+ "validateDebug"
+]
+
+# All the relevant metadata fields to create an index based on language/API/etc'
+[indexMetadata]
+index = [
+ "APIs/AndroidComponentsExtension.onVariants()",
+ "Call chains/androidComponents.onVariants {}",
+ "Call chains/variant.registerPreBuild()",
+ "APIs/LifeCycleTasks.registerPreBuild()"
+]
diff --git a/recipes/registerPreBuild/settings.gradle.kts b/recipes/registerPreBuild/settings.gradle.kts
new file mode 100644
index 00000000..71944cfa
--- /dev/null
+++ b/recipes/registerPreBuild/settings.gradle.kts
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 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
+ *
+ * https://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.
+ */
+
+rootProject.name = "registerPreBuild"
+
+pluginManagement {
+ includeBuild("build-logic")
+ repositories {
+ $AGP_REPOSITORY
+ $PLUGIN_REPOSITORIES
+ }
+}
+
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ $AGP_REPOSITORY
+ $DEPENDENCY_REPOSITORIES
+ }
+}
+
+include(":app")
diff --git a/recipes/transformManifest/build-logic/plugins/src/main/kotlin/CustomPlugin.kt b/recipes/transformManifest/build-logic/plugins/src/main/kotlin/CustomPlugin.kt
index 1de7d982..baa04622 100644
--- a/recipes/transformManifest/build-logic/plugins/src/main/kotlin/CustomPlugin.kt
+++ b/recipes/transformManifest/build-logic/plugins/src/main/kotlin/CustomPlugin.kt
@@ -26,7 +26,7 @@ import java.io.File
import com.android.build.api.variant.AndroidComponentsExtension
/**
- * This custom plugin will register tree tasks that will update and veridy Android manifest.
+ * This custom plugin will register tree tasks that will update and verify Android manifest.
*/
class CustomPlugin : Plugin {
override fun apply(project: Project) {