All Downloads are FREE. Search and download functionalities are using the official Maven repository.

foundry.gradle.AndroidSourcesConfigurer.kt Maven / Gradle / Ivy

/*
 * Copyright (C) 2022 Slack Technologies, LLC
 *
 * 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.
 */
package foundry.gradle

import com.android.build.gradle.internal.SdkLocationSourceSet
import com.android.build.gradle.internal.SdkLocator
import foundry.gradle.agp.internal.NoOpIssueReporter
import java.io.File
import kotlin.system.measureTimeMillis
import org.gradle.api.Project
import org.gradle.api.logging.Logger

/**
 * For Android SDK 30, the real sources are not published until its release. This is annoying when
 * we want to build against them though (new APIs, etc). To cover for this, we copy over the
 * existing android-29 sources into the android-30 directory as temporary cover with a
 * `slack_patched_marker` file.
 *
 * Once real sources are published, this will just delete the copied jar and let AGP download it
 * automatically.
 */
internal object AndroidSourcesConfigurer {

  internal const val MARKER_FILE_NAME = "foundry_patched_marker"

  fun patchSdkSources(requestedSdkVersion: Int, rootProject: Project, latest: Int) {
    val sdkDir =
      SdkLocator.getSdkDirectory(
        rootProject.projectDir,
        NoOpIssueReporter,
        SdkLocationSourceSet(rootProject.projectDir, rootProject.providers),
      )
    patchSdkSources(requestedSdkVersion, sdkDir, rootProject.logger, latest)
  }

  @Suppress("LongMethod")
  fun patchSdkSources(requestedSdkVersion: Int, sdkDir: File, logger: Logger, latest: Int) {
    if (requestedSdkVersion == latest) {
      // Check for our patched marker. If it exists, delete the directory and let AGP download it
      // again.
      val marker = File(sdkDir, "sources/android-$latest/$MARKER_FILE_NAME")
      if (marker.exists()) {
        val sourcesDir = marker.parentFile
        logger.lifecycle(
          "Clearing patched SDK $latest sources dir at $sourcesDir so AGP will " +
            "download the final, real sources. Restart Android Studio + re-sync once after this!"
        )
        sourcesDir.deleteRecursively()
      } else {
        // Nothing to do!
        logger.debug("Skipping Android sources patching")
        return
      }
    } else {
      check((requestedSdkVersion - latest) == 1) {
        "Expected a maximum compile SDK delta of just 1. Cannot patch sources between " +
          "$requestedSdkVersion and latest $latest"
      }
      val requestedSources = File(sdkDir, "sources/android-$requestedSdkVersion")
      if (requestedSources.exists()) {
        // Nothing to do!
        logger.debug("Skipping Android sources patching")
        return
      }
      val latestSources = File(sdkDir, "sources/android-$latest")
      if (!latestSources.exists()) {
        // TODO we could try to download the sdk 29 sources via sdkmanager for them?
        logger.error(
          "Cannot patch android sources jar for requested SDK version " +
            "$requestedSdkVersion. This could be because your SDK has not downloaded it yet." +
            " Android SDK sources will not work in this project until you do download them though." +
            " You can do so manually via the SDK manager in Android studio."
        )
      } else {
        logger.lifecycle(
          "Patching Android sources from $latest as requested SDK version $requestedSdkVersion"
        )
        measureTimeMillis {
            requestedSources.mkdirs()
            latestSources.copyRecursively(requestedSources, overwrite = true)

            // Now create our marker
            File(requestedSources, MARKER_FILE_NAME).apply {
              createNewFile()
              // Write some explanatory text. This isn't used anywhere, just here for curious
              // readers.
              writeText(
                """
            Hello curious reader! This file exists solely as a marker for the
            AndroidSourcesConfigurer util to know if these are patched sdk sources. This sources
            directory is a copy of API $latest sources.

            Did you know if you drive from Houston to LA, El Paso is halfway?
              """
                  .trimIndent()
              )
            }
          }
          .also {
            logger.lifecycle(
              "Successfully patched Android sources for SDK $requestedSdkVersion " +
                "in ${it}ms. Restart Android Studio + re-sync once after this!"
            )
          }
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy