feat(tooling-api): fetch project sync issues from the Android Gradle Plugin

This commit is contained in:
Akash Yadav
2023-10-05 19:50:44 +05:30
parent 69e5671fb9
commit 5562dc2069
33 changed files with 2188 additions and 58 deletions
@@ -61,6 +61,8 @@ import com.itsaky.androidide.utils.resolveAttr
import com.itsaky.androidide.utils.showOnUiThread
import com.itsaky.androidide.utils.withIcon
import com.itsaky.androidide.viewmodel.BuildVariantsViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File
import java.util.concurrent.CompletableFuture
import java.util.regex.Pattern
@@ -457,11 +459,13 @@ abstract class ProjectHandlerActivity : BaseEditorActivity() {
}
manager.cachedInitResult = result
manager.setupProject()
manager.notifyProjectUpdate()
updateBuildVariants(manager.androidBuildVariants)
activityBackroundScope.launch(Dispatchers.IO) {
manager.setupProject()
manager.notifyProjectUpdate()
updateBuildVariants(manager.androidBuildVariants)
ThreadUtils.runOnUiThread { postProjectInit(true) }
com.itsaky.androidide.tasks.runOnUiThread { postProjectInit(true) }
}
}
protected open fun preProjectInit() {
@@ -43,6 +43,7 @@ import com.itsaky.androidide.utils.ILogger
import io.github.rosemoe.sora.text.Content
import io.mockk.every
import io.mockk.mockkStatic
import kotlinx.coroutines.runBlocking
import org.greenrobot.eventbus.EventBus
import org.junit.Before
import org.junit.runner.RunWith
@@ -102,7 +103,7 @@ abstract class LSPTest {
val projectManager = ProjectManagerImpl.getInstance()
projectManager.register()
projectManager.setupProject(project)
runBlocking { projectManager.setupProject(project) }
// We need to manually setup the language server with the project here
// ProjectManager.notifyProjectUpdate()
@@ -0,0 +1,792 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
/*
* Copyright (C) 2017 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.options
import com.android.build.gradle.options.DeprecationTarget.BUILD_CONFIG_GLOBAL_PROPERTY
import com.android.build.gradle.options.DeprecationTarget.VERSION_9_0
import com.android.build.gradle.options.Version.VERSION_3_5
import com.android.build.gradle.options.Version.VERSION_3_6
import com.android.build.gradle.options.Version.VERSION_4_0
import com.android.build.gradle.options.Version.VERSION_4_1
import com.android.build.gradle.options.Version.VERSION_4_2
import com.android.build.gradle.options.Version.VERSION_7_0
import com.android.build.gradle.options.Version.VERSION_7_2
import com.android.build.gradle.options.Version.VERSION_7_3
import com.android.build.gradle.options.Version.VERSION_BEFORE_4_0
import com.android.builder.model.AndroidProject
import com.android.builder.model.AndroidProject.PROPERTY_BUILD_MODEL_ONLY
import com.android.builder.model.PROPERTY_BUILD_MODEL_V2_ONLY
import com.android.builder.model.PROPERTY_BUILD_WITH_STABLE_IDS
import com.android.builder.model.PROPERTY_DEPLOY_AS_INSTANT_APP
import com.android.builder.model.PROPERTY_EXTRACT_INSTANT_APK
import com.android.builder.model.PROPERTY_INVOKED_FROM_IDE
import com.android.builder.model.PROPERTY_REFRESH_EXTERNAL_NATIVE_MODEL
enum class BooleanOption(
override val propertyName: String,
override val defaultValue: Boolean,
val stage: Stage
) : Option<Boolean> {
/* ----------
* STABLE API
*/
// IDE properties
IDE_INVOKED_FROM_IDE(PROPERTY_INVOKED_FROM_IDE, false, ApiStage.Stable),
IDE_BUILD_MODEL_ONLY_V2(PROPERTY_BUILD_MODEL_V2_ONLY, false, ApiStage.Stable),
@Deprecated("This is for model v1 only. Please also use IDE_BUILD_MODEL_ONLY_V2")
IDE_BUILD_MODEL_ONLY(PROPERTY_BUILD_MODEL_ONLY, false, ApiStage.Stable),
@Deprecated("Use IDE_BUILD_MODEL_ONLY_V2")
IDE_BUILD_MODEL_ONLY_ADVANCED(AndroidProject.PROPERTY_BUILD_MODEL_ONLY_ADVANCED, false, ApiStage.Stable),
@Deprecated("Use IDE_BUILD_MODEL_ONLY_V2")
IDE_BUILD_MODEL_FEATURE_FULL_DEPENDENCIES(AndroidProject.PROPERTY_BUILD_MODEL_FEATURE_FULL_DEPENDENCIES, false, ApiStage.Stable),
IDE_REFRESH_EXTERNAL_NATIVE_MODEL(PROPERTY_REFRESH_EXTERNAL_NATIVE_MODEL, false, ApiStage.Stable),
//IDE_GENERATE_SOURCES_ONLY(AndroidProject.PROPERTY_GENERATE_SOURCES_ONLY, false, ApiStage.Stable),
// tell bundletool to only extract instant APKs.
IDE_EXTRACT_INSTANT(PROPERTY_EXTRACT_INSTANT_APK, false, ApiStage.Stable),
// Flag used to indicate a "deploy as instant" run configuration.
IDE_DEPLOY_AS_INSTANT_APP(PROPERTY_DEPLOY_AS_INSTANT_APP, false, ApiStage.Stable),
ENABLE_STUDIO_VERSION_CHECK("android.injected.studio.version.check", true, ApiStage.Stable),
ENABLE_STABLE_IDS(PROPERTY_BUILD_WITH_STABLE_IDS, false, ApiStage.Stable),
// Features' default values
BUILD_FEATURE_DATABINDING("android.defaults.buildfeatures.databinding", false, ApiStage.Stable),
BUILD_FEATURE_RESVALUES("android.defaults.buildfeatures.resvalues", true, ApiStage.Stable),
BUILD_FEATURE_SHADERS("android.defaults.buildfeatures.shaders", true, ApiStage.Stable),
BUILD_FEATURE_VIEWBINDING("android.defaults.buildfeatures.viewbinding", false, ApiStage.Stable),
BUILD_FEATURE_ANDROID_RESOURCES("android.library.defaults.buildfeatures.androidresources", true, ApiStage.Stable),
// DSLs default values
ENABLE_DATABINDING_KTX("android.defaults.databinding.addKtx", true, ApiStage.Stable),
// AndroidX & Jetifier
USE_ANDROID_X("android.useAndroidX", false, ApiStage.Stable),
ENABLE_JETIFIER("android.enableJetifier", false, ApiStage.Stable),
DEBUG_OBSOLETE_API("android.debug.obsoleteApi", false, ApiStage.Stable),
// Disabled by default due to low usage.
GENERATE_MANIFEST_CLASS("android.generateManifestClass", false, ApiStage.Stable),
USE_NON_FINAL_RES_IDS("android.nonFinalResIds", true, ApiStage.Stable),
NON_TRANSITIVE_R_CLASS("android.nonTransitiveRClass", true, ApiStage.Stable),
/**
* Setting this field to false indicates that in the current
* project, all the APKs installed during test will be uninstalled
* after test finishes. Setting it to true means that the AGP
* will leave the test APKs untouched after test.
*
* Default is false
*/
ANDROID_TEST_LEAVE_APKS_INSTALLED_AFTER_RUN("android.injected.androidTest.leaveApksInstalledAfterRun", false, ApiStage.Stable),
/**
* When this option is enabled, dexing transforms will use the full classpath (if desugaring
* requires a classpath). This classpath consists of all external artifacts
* ([com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactScope.EXTERNAL])
* in addition to the input artifact's dependencies provided by Gradle through
* [org.gradle.api.artifacts.transform.InputArtifactDependencies].
*
* This option is useful when some dependencies are missing from the input artifact's
* dependencies (see bug 230454566), so the full classpath is needed.
*
* If dexing transforms do not require a classpath, this option is not used.
*/
USE_FULL_CLASSPATH_FOR_DEXING_TRANSFORM(
"android.useFullClasspathForDexingTransform",
false,
ApiStage.Stable
),
/* ------------------
* SUPPORTED FEATURES
*/
// Used by Studio as workaround for b/71054106, b/75955471
ENABLE_SDK_DOWNLOAD("android.builder.sdkDownload", true, FeatureStage.Supported),
ENFORCE_UNIQUE_PACKAGE_NAMES("android.uniquePackageNames", false, FeatureStage.Supported),
// Flag added to work around b/130596259.
FORCE_JACOCO_OUT_OF_PROCESS("android.forceJacocoOutOfProcess", false, FeatureStage.Supported),
PRECOMPILE_DEPENDENCIES_RESOURCES("android.precompileDependenciesResources", true, FeatureStage.Supported),
INCLUDE_DEPENDENCY_INFO_IN_APKS("android.includeDependencyInfoInApks", true, FeatureStage.Supported),
// FIXME switch to false once we know we don't use these getters internally.
ENABLE_LEGACY_API("android.compatibility.enableLegacyApi", true, FeatureStage.Supported),
FULL_R8("android.enableR8.fullMode", true, FeatureStage.Supported),
/* ---------------------
* EXPERIMENTAL FEATURES
*/
BUILD_FEATURE_MLMODELBINDING("android.defaults.buildfeatures.mlmodelbinding", false, ApiStage.Experimental),
ENABLE_DEFAULT_DEBUG_SIGNING_CONFIG("android.experimental.useDefaultDebugSigningConfigForProfileableBuildtypes", false, ApiStage.Experimental),
ENABLE_PROFILE_JSON("android.enableProfileJson", false, FeatureStage.Experimental),
DISALLOW_DEPENDENCY_RESOLUTION_AT_CONFIGURATION("android.dependencyResolutionAtConfigurationTime.disallow", false, FeatureStage.Experimental),
VERSION_CHECK_OVERRIDE_PROPERTY("android.overrideVersionCheck", false, FeatureStage.Experimental),
OVERRIDE_PATH_CHECK_PROPERTY("android.overridePathCheck", false, FeatureStage.Experimental),
DISABLE_RESOURCE_VALIDATION("android.disableResourceValidation", false, FeatureStage.Experimental),
CONSUME_DEPENDENCIES_AS_SHARED_LIBRARIES("android.consumeDependenciesAsSharedLibraries", false, FeatureStage.Experimental),
DISABLE_EARLY_MANIFEST_PARSING("android.disableEarlyManifestParsing", false, FeatureStage.Experimental),
ENABLE_RESOURCE_NAMESPACING_DEFAULT("android.enableResourceNamespacingDefault", false, FeatureStage.Experimental),
CONDITIONAL_KEEP_RULES("android.useConditionalKeepRules", false, FeatureStage.Experimental),
KEEP_SERVICES_BETWEEN_BUILDS("android.keepWorkerActionServicesBetweenBuilds", false, FeatureStage.Experimental),
ENABLE_PARTIAL_R_INCREMENTAL_BUILDS("android.enablePartialRIncrementalBuilds", false, FeatureStage.Experimental),
ENABLE_NEW_RESOURCE_SHRINKER_PRECISE("android.experimental.enableNewResourceShrinker.preciseShrinking", false, FeatureStage.Experimental),
ENABLE_LOCAL_TESTING("android.bundletool.enableLocalTesting", false, FeatureStage.Experimental),
DISABLE_MINSDKLIBRARY_CHECK("android.unsafe.disable.minSdkLibraryCheck", false, FeatureStage.Experimental),
ENABLE_INSTRUMENTATION_TEST_DESUGARING("android.experimental.library.desugarAndroidTest", false, FeatureStage.Experimental),
ENABLE_EMULATOR_CONTROL("android.experimental.androidTest.enableEmulatorControl", false, FeatureStage.Experimental),
ENABLE_SCREENSHOT_TEST("android.experimental.enableScreenshotTest", false, FeatureStage.Experimental),
/**
* When enabled, incompatible APKs installed on a testing device will be uninstalled automatically
* during an instrumentation test run (e.g. When INSTALL_FAILED_UPDATE_INCOMPATIBLE error happens
* after attempting to install APKs for testing).
*/
UNINSTALL_INCOMPATIBLE_APKS("android.experimental.testOptions.uninstallIncompatibleApks", false, FeatureStage.Experimental),
/**
* When enabled, "-show-kernel" and "-verbose" flags are used when running an Android emulator
* for Gradle Managed devices.
*/
GRADLE_MANAGED_DEVICE_EMULATOR_SHOW_KERNEL_LOGGING("android.experimental.testOptions.managedDevices.emulator.showKernelLogging", false, FeatureStage.Experimental),
/**
* Gradle Managed devices officially supports Android devices on API level 27 and higher because
* using the API level 26 and lower devices increase instability. When a user tries to use those
* old API devices, GMD task throws an exception and task fails by default.
*
* When this flag is enabled, it allows a user to use any old API level devices regardless of
* its instability.
*/
GRADLE_MANAGED_DEVICE_ALLOW_OLD_API_LEVEL_DEVICES("android.experimental.testOptions.managedDevices.allowOldApiLevelDevices", false, FeatureStage.Experimental),
/**
* When enabled, Gradle Managed Device allows a custom managed device type that can be provided
* by a plugin by implementing ManagedDeviceTestRunner APIs.
*/
GRADLE_MANAGED_DEVICE_CUSTOM_DEVICE("android.experimental.testOptions.managedDevices.customDevice", false, FeatureStage.Experimental),
/** When set R classes are treated as compilation classpath in libraries, rather than runtime classpath, with values set to 0. */
ENABLE_ADDITIONAL_ANDROID_TEST_OUTPUT("android.enableAdditionalTestOutput", true, FeatureStage.Experimental),
ENABLE_APP_COMPILE_TIME_R_CLASS("android.enableAppCompileTimeRClass", false, FeatureStage.Experimental),
ENABLE_EXTRACT_ANNOTATIONS("android.enableExtractAnnotations", true, FeatureStage.Experimental),
// Marked as stable to avoid reporting deprecation twice.
CONVERT_NON_NAMESPACED_DEPENDENCIES("android.convertNonNamespacedDependencies", true, FeatureStage.Experimental),
/** Set to true to build native .so libraries only for the device it will be run on. */
BUILD_ONLY_TARGET_ABI("android.buildOnlyTargetAbi", true, FeatureStage.Experimental),
ENABLE_PARALLEL_NATIVE_JSON_GEN("android.enableParallelJsonGen", false, FeatureStage.Experimental),
ENABLE_SIDE_BY_SIDE_CMAKE("android.enableSideBySideCmake", true, FeatureStage.Experimental),
ENABLE_NATIVE_COMPILER_SETTINGS_CACHE("android.enableNativeCompilerSettingsCache", false, FeatureStage.Experimental),
ENABLE_CMAKE_BUILD_COHABITATION("android.enableCmakeBuildCohabitation", false, FeatureStage.Experimental),
ENABLE_PROGUARD_RULES_EXTRACTION("android.proguard.enableRulesExtraction", true, FeatureStage.Experimental),
USE_DEPENDENCY_CONSTRAINTS("android.dependency.useConstraints", true, FeatureStage.Experimental),
EXCLUDE_LIBRARY_COMPONENTS_FROM_CONSTRAINTS("android.experimental.dependency.excludeLibraryComponentsFromConstraints", false, FeatureStage.Experimental),
ENABLE_DUPLICATE_CLASSES_CHECK("android.enableDuplicateClassesCheck", true, FeatureStage.Experimental),
MINIMAL_KEEP_RULES("android.useMinimalKeepRules", true, FeatureStage.Experimental),
EXCLUDE_RES_SOURCES_FOR_RELEASE_BUNDLES("android.bundle.excludeResSourcesForRelease", true, FeatureStage.Experimental),
ENABLE_BUILD_CONFIG_AS_BYTECODE("android.enableBuildConfigAsBytecode", false, FeatureStage.Experimental),
/** Whether lint should be run in process; the default is true. */
RUN_LINT_IN_PROCESS("android.experimental.runLintInProcess", true, FeatureStage.Experimental),
ENABLE_TEST_FIXTURES("android.experimental.enableTestFixtures", false, FeatureStage.Experimental),
USE_NEW_DSL_INTERFACES("android.experimental.newDslInterfaces", false, FeatureStage.Experimental),
/** Whether to force the APK to be deterministic. */
FORCE_DETERMINISTIC_APK("android.experimental.forceDeterministicApk", false, FeatureStage.Experimental),
MISSING_LINT_BASELINE_IS_EMPTY_BASELINE(
"android.experimental.lint.missingBaselineIsEmptyBaseline",
false,
FeatureStage.Experimental,
),
LEGACY_TRANSFORM_TASK_FORCE_NON_INCREMENTAL(
"android.experimental.legacyTransform.forceNonIncremental",
false,
FeatureStage.Experimental
),
PRIVACY_SANDBOX_SDK_SUPPORT("android.experimental.privacysandboxsdk.enable", false, FeatureStage.Experimental),
PRIVACY_SANDBOX_SDK_REQUIRE_SERVICES(
"android.experimental.privacysandboxsdk.requireServices", true, FeatureStage.Experimental),
PRINT_LINT_STACK_TRACE("android.lint.printStackTrace", false, FeatureStage.Experimental),
VERIFY_AAR_CLASSES("android.experimental.verifyLibraryClasses", false, FeatureStage.Experimental),
DISABLE_COMPILE_SDK_CHECKS("android.experimental.disableCompileSdkChecks", false, FeatureStage.Experimental),
ADDITIONAL_ARTIFACTS_IN_MODEL("android.experimental.additionalArtifactsInModel", false, FeatureStage.Experimental),
// Whether to suppress warnings about android:extractNativeLibs set to true in dependencies
SUPPRESS_EXTRACT_NATIVE_LIBS_WARNINGS(
"android.experimental.suppressExtractNativeLibsWarnings",
false,
FeatureStage.Experimental
),
FUSED_LIBRARY_SUPPORT("android.experimental.fusedLibrarySupport", false, FeatureStage.Experimental),
SUPPORT_PAST_STUDIO_VERSIONS("android.experimental.support.past.studio.versions", false, FeatureStage.Experimental),
/**
* Enables task to add version control info to APKs/Bundle
*/
ENABLE_VCS_INFO("android.enableVcsInfo", false, FeatureStage.Experimental),
/**
* Whether to omit line numbers when writing lint baselines
*/
LINT_BASELINE_OMIT_LINE_NUMBERS(
"android.lint.baselineOmitLineNumbers",
false,
FeatureStage.Experimental
),
/**
* Whether to use K2 UAST when running lint
*/
LINT_USE_K2_UAST(
"android.lint.useK2Uast",
false,
FeatureStage.Experimental
),
/* ------------------------
* SOFTLY-ENFORCED FEATURES
*/
ENABLE_RESOURCE_OPTIMIZATIONS(
"android.enableResourceOptimizations",
true,
FeatureStage.SoftlyEnforced(VERSION_9_0)
),
ANDROID_TEST_USES_UNIFIED_TEST_PLATFORM(
"android.experimental.androidTest.useUnifiedTestPlatform",
true,
FeatureStage.SoftlyEnforced(VERSION_9_0)
),
/**
* Whether to do lint analysis per component (instead of analysing the main variant and the test
* components in the same lint invocation).
*/
LINT_ANALYSIS_PER_COMPONENT(
"android.experimental.lint.analysisPerComponent",
true,
FeatureStage.SoftlyEnforced(VERSION_9_0)
),
/* -------------------
* DEPRECATED FEATURES
*/
BUILD_FEATURE_AIDL(
"android.defaults.buildfeatures.aidl",
false,
ApiStage.Deprecated(VERSION_9_0)
),
BUILD_FEATURE_RENDERSCRIPT(
"android.defaults.buildfeatures.renderscript",
false,
ApiStage.Deprecated(VERSION_9_0)
),
BUILD_FEATURE_BUILDCONFIG(
"android.defaults.buildfeatures.buildconfig",
false,
ApiStage.Deprecated(BUILD_CONFIG_GLOBAL_PROPERTY))
,
/* -----------------
* ENFORCED FEATURES
*/
@Suppress("unused")
PREFER_CMAKE_FILE_API(
"android.preferCmakeFileApi",
true, FeatureStage.Enforced(VERSION_7_0)),
@Suppress("unused")
ENABLE_NATIVE_CONFIGURATION_FOLDING(
"android.enableNativeConfigurationFolding",
true,
FeatureStage.Enforced(VERSION_7_0)),
@Suppress("unused")
ENABLE_SIDE_BY_SIDE_NDK(
"android.enableSideBySideNdk",
true,
FeatureStage.Enforced(
VERSION_4_1,
"The android.enableSideBySideNdk property does not have any effect. " +
"Side-by-side NDK is always enabled."
)
),
@Suppress("unused")
ENABLE_IMPROVED_DEPENDENCY_RESOLUTION(
"android.enableImprovedDependenciesResolution",
true,
FeatureStage.Enforced(
VERSION_BEFORE_4_0,
"The android.enableImprovedDependenciesResolution property does not have any effect. "
+ "Dependency resolution is only performed during task execution phase."
)
),
@Suppress("unused")
ENABLE_NEW_RESOURCE_PROCESSING(
"android.enableNewResourceProcessing",
true,
FeatureStage.Enforced(
VERSION_BEFORE_4_0,
"New resource processing is now always enabled."
)
),
@Suppress("unused")
DISABLE_RES_MERGE_IN_LIBRARY(
"android.disable.res.merge",
true,
FeatureStage.Enforced(
VERSION_BEFORE_4_0,
"Resources from dependencies are never merged in libraries."
)
),
@Suppress("unused")
ENABLE_DAEMON_MODE_AAPT2(
"android.enableAapt2DaemonMode",
true,
FeatureStage.Enforced(VERSION_BEFORE_4_0, "AAPT2 daemon mode is now always enabled.")
),
@Suppress("unused")
ENABLE_INCREMENTAL_DESUGARING(
"android.enableIncrementalDesugaring",
true,
FeatureStage.Enforced(
VERSION_BEFORE_4_0,
"This property has no effect, incremental desugaring is always enabled."
)
),
@Suppress("unused")
ENABLE_CORE_LAMBDA_STUBS(
"android.enableCoreLambdaStubs",
true,
FeatureStage.Enforced(
VERSION_BEFORE_4_0,
"This property has no effect, core-lambda-stubs.jar is always in the bootclasspath."
)
),
@Suppress("unused")
ENABLE_DEX_ARCHIVE(
"android.useDexArchive",
true,
FeatureStage.Enforced(
VERSION_BEFORE_4_0,
"This property has no effect, incremental dexing is always used."
)
),
@Suppress("unused")
ENABLE_AAPT2(
"android.enableAapt2",
true,
FeatureStage.Enforced(
VERSION_BEFORE_4_0,
"This property has no effect, AAPT2 is now always used."
)
),
@Suppress("unused")
USE_AAPT2_FROM_MAVEN(
"android.useAapt2FromMaven",
true,
FeatureStage.Enforced(
VERSION_BEFORE_4_0,
"This property has no effect and AAPT2 from maven.google.com is now always used. "
+ "If you wish to use a local executable of AAPT2 please use the "
+ "'android.aapt2FromMavenOverride' option."
)
),
@Suppress("unused")
ENABLE_D8_MAIN_DEX_LIST(
"android.enableD8MainDexList",
true,
FeatureStage.Enforced(
VERSION_BEFORE_4_0,
"This property has no effect, D8 is always used to compute the main dex list."
)
),
@Suppress("unused")
ENABLE_DATA_BINDING_V2(
"android.databinding.enableV2",
true,
FeatureStage.Enforced(VERSION_BEFORE_4_0, "Databinding v1 is removed.")
),
ENABLE_SEPARATE_R_CLASS_COMPILATION(
"android.enableSeparateRClassCompilation",
true,
FeatureStage.Enforced(
VERSION_BEFORE_4_0,
"Separate R class compilation has been enabled and can no longer be disabled."
)
),
@Suppress("unused")
ENABLE_GRADLE_WORKERS(
"android.enableGradleWorkers",
true,
FeatureStage.Enforced(
VERSION_4_2,
"Gradle workers are always used."
)
),
@Suppress("unused")
ENABLE_D8(
"android.enableD8",
true,
FeatureStage.Enforced(
VERSION_7_0,
"For more details, see https://d.android.com/r/studio-ui/d8-overview.html."
)
),
@Suppress("unused")
ENABLE_D8_DESUGARING(
"android.enableD8.desugaring",
true,
FeatureStage.Enforced(
VERSION_7_0,
"D8 desugaring is used by default, when applicable."
)
),
@Suppress("unused")
ENABLE_R8_DESUGARING(
"android.enableR8.desugaring",
true,
FeatureStage.Enforced(
VERSION_7_0,
"R8 desugaring is used by default, when applicable."
)
),
USE_NEW_LINT_MODEL("android.experimental.useNewLintModel", true, FeatureStage.Enforced(VERSION_7_0)),
/** Whether Jetifier will skip libraries that already support AndroidX. */
JETIFIER_SKIP_IF_POSSIBLE("android.jetifier.skipIfPossible", true, FeatureStage.Enforced(VERSION_7_0)),
@Suppress("unused")
NON_TRANSITIVE_APP_R_CLASS(
"android.experimental.nonTransitiveAppRClass",
true,
FeatureStage.Enforced(
VERSION_7_0,
"Non-transitive R classes are now enabled in app modules with the " +
"${NON_TRANSITIVE_R_CLASS.propertyName} option.")),
/** Incremental dexing task using D8's new API for desugaring graph computation. */
ENABLE_INCREMENTAL_DEXING_TASK_V2("android.enableIncrementalDexingTaskV2", true, FeatureStage.Enforced(VERSION_7_0)),
/** Incremental dexing transform. */
ENABLE_INCREMENTAL_DEXING_TRANSFORM("android.enableIncrementalDexingTransform", true, FeatureStage.Enforced(VERSION_7_0)),
ENABLE_R8_LIBRARIES("android.enableR8.libraries", true, FeatureStage.Enforced(VERSION_7_0)),
ENABLE_SYMBOL_TABLE_CACHING("android.enableSymbolTableCaching", true, FeatureStage.Enforced(VERSION_7_0)),
ENABLE_JVM_RESOURCE_COMPILER("android.enableJvmResourceCompiler", true, FeatureStage.Enforced(VERSION_7_0)),
/** Whether to use lint's partial analysis functionality. */
USE_LINT_PARTIAL_ANALYSIS("android.enableParallelLint", true, FeatureStage.Enforced(VERSION_7_2)),
ENABLE_AAPT2_WORKER_ACTIONS(
"android.enableAapt2WorkerActions",
true,
FeatureStage.Enforced(
VERSION_7_3,
"AAPT2 worker actions have been used unconditionally since Android Gradle Plugin 3.3"
)
),
ENABLE_JACOCO_TRANSFORM_INSTRUMENTATION(
"android.enableJacocoTransformInstrumentation",
true,
FeatureStage.Enforced(VERSION_7_3)
),
@Suppress("unused")
ENABLE_SOURCE_SET_PATHS_MAP(
"android.enableSourceSetPathsMap",
true,
FeatureStage.Enforced(Version.VERSION_8_0)
),
@Suppress("unused")
RELATIVE_COMPILE_LIB_RESOURCES(
"android.cacheCompileLibResources",
true,
FeatureStage.Enforced(Version.VERSION_8_0)
),
@Suppress("unused")
R8_FAIL_ON_MISSING_CLASSES(
"android.r8.failOnMissingClasses",
true,
FeatureStage.Enforced(Version.VERSION_8_0)
),
USE_NEW_JAR_CREATOR(
"android.useNewJarCreator",
true,
FeatureStage.Enforced(Version.VERSION_8_0)
),
USE_NEW_APK_CREATOR(
"android.useNewApkCreator",
true,
FeatureStage.Enforced(Version.VERSION_8_0)
),
WARN_ABOUT_DEPENDENCY_RESOLUTION_AT_CONFIGURATION(
"android.dependencyResolutionAtConfigurationTime.warn",
true,
FeatureStage.Enforced(Version.VERSION_8_0)
),
ENABLE_ART_PROFILES(
"android.enableArtProfiles",
true,
FeatureStage.Enforced(Version.VERSION_8_0)
),
USE_RELATIVE_PATH_IN_TEST_CONFIG(
"android.testConfig.useRelativePath",
true,
FeatureStage.Enforced(Version.VERSION_8_0)
),
INCLUDE_REPOSITORIES_IN_DEPENDENCY_REPORT(
"android.bundletool.includeRepositoriesInDependencyReport",
true,
FeatureStage.Enforced(Version.VERSION_8_0)
),
ENABLE_INCREMENTAL_DATA_BINDING(
"android.databinding.incremental",
true,
FeatureStage.Enforced(Version.VERSION_8_1)
),
ENABLE_NEW_RESOURCE_SHRINKER("android.enableNewResourceShrinker",
true,
FeatureStage.Enforced(Version.VERSION_8_0)),
@Suppress("unused")
ENABLE_R_TXT_RESOURCE_SHRINKING(
"android.enableRTxtResourceShrinking",
true,
FeatureStage.Enforced(Version.VERSION_8_1)
),
@Suppress("unused")
COMPILE_CLASSPATH_LIBRARY_R_CLASSES(
"android.useCompileClasspathLibraryRClasses",
true,
FeatureStage.Enforced(Version.VERSION_8_1)
),
ENABLE_UNCOMPRESSED_NATIVE_LIBS_IN_BUNDLE(
"android.bundle.enableUncompressedNativeLibs",
true,
FeatureStage.Enforced(Version.VERSION_8_1)
),
ENABLE_GLOBAL_SYNTHETICS(
"android.enableGlobalSyntheticsGeneration",
true,
FeatureStage.Enforced(Version.VERSION_8_1)
),
ENABLE_DEXING_DESUGARING_ARTIFACT_TRANSFORM(
"android.enableDexingArtifactTransform.desugaring",
true,
FeatureStage.Enforced(
Version.VERSION_8_2,
"If you run into issues with dexing transforms, try setting `${USE_FULL_CLASSPATH_FOR_DEXING_TRANSFORM.propertyName} = true` instead."
)
),
ENABLE_DEXING_ARTIFACT_TRANSFORM_FOR_EXTERNAL_LIBS(
"android.enableDexingArtifactTransformForExternalLibs",
true,
FeatureStage.Enforced(Version.VERSION_8_2)
),
ENABLE_DEXING_ARTIFACT_TRANSFORM(
"android.enableDexingArtifactTransform",
true,
FeatureStage.Enforced(
Version.VERSION_8_3,
"If you run into issues with dexing transforms, try setting `${USE_FULL_CLASSPATH_FOR_DEXING_TRANSFORM.propertyName} = true` instead."
)
),
/* ----------------
* REMOVED FEATURES
*/
@Suppress("unused")
ENABLE_IN_PROCESS_AAPT2(
"android.enableAapt2jni",
false,
FeatureStage.Removed(VERSION_BEFORE_4_0, "AAPT2 JNI has been removed.")
),
@Suppress("unused")
ENABLE_DEPRECATED_NDK(
"android.useDeprecatedNdk",
false,
FeatureStage.Removed(VERSION_BEFORE_4_0, "NdkCompile is no longer supported")
),
@Suppress("unused")
INJECT_SDK_MAVEN_REPOS(
"android.injectSdkMavenRepos",
false,
FeatureStage.Removed(
VERSION_3_5,
"The ability to inject the Android SDK maven repos is removed in AGP 3.5"
)
),
@Suppress("unused")
ENABLE_UNIT_TEST_BINARY_RESOURCES(
"android.enableUnitTestBinaryResources",
false,
FeatureStage.Removed(
VERSION_BEFORE_4_0,
"The raw resource for unit test functionality is removed."
)
),
@Suppress("unused")
ENABLE_EXPERIMENTAL_FEATURE_DATABINDING(
"android.enableExperimentalFeatureDatabinding",
false,
FeatureStage.Removed(
VERSION_4_0,
"This property has no effect. The features plugin was removed in AGP 4.0.")
),
@Suppress("unused")
ENABLE_SEPARATE_APK_RESOURCES(
"android.enableSeparateApkRes",
false,
FeatureStage.Removed(VERSION_BEFORE_4_0, "Instant run is replaced by apply changes.")
),
@Suppress("unused")
KEEP_TIMESTAMPS_IN_APK(
"android.keepTimestampsInApk",
false,
FeatureStage.Removed(
VERSION_3_6,
"The ability to keep timestamps in the APK is removed in AGP 3.6"
)
),
@Suppress("unused")
ENABLE_SEPARATE_ANNOTATION_PROCESSING(
"android.enableSeparateAnnotationProcessing",
false,
FeatureStage.Removed(VERSION_4_0, "This feature was removed in AGP 4.0")
),
@Suppress("unused")
GENERATE_R_JAVA(
"android.generateRJava",
false,
FeatureStage.Removed(VERSION_4_1, "This feature was removed in AGP 4.1")),
@Suppress("unused")
ENABLE_BUILD_CACHE(
"android.enableBuildCache",
false,
FeatureStage.Removed(VERSION_7_0, "The Android-specific build caches were superseded by the Gradle build cache (https://docs.gradle.org/current/userguide/build_cache.html).")
),
@Suppress("unused")
ENABLE_INTERMEDIATE_ARTIFACTS_CACHE(
"android.enableIntermediateArtifactsCache",
false,
FeatureStage.Removed(VERSION_7_0, "The Android-specific build caches were superseded by the Gradle build cache (https://docs.gradle.org/current/userguide/build_cache.html).")
),
@Suppress("unused")
ENABLE_DESUGAR(
"android.enableDesugar",
false,
FeatureStage.Removed(VERSION_7_0, "Desugar tool has been removed from AGP.")
),
@Suppress("unused")
ENABLE_TEST_SHARDING("android.androidTest.shardBetweenDevices", false, FeatureStage.Removed(Version.VERSION_8_2, "Cross device sharding is no longer supported.")),
; // end of enums
override val status = stage.status
override fun parse(value: Any): Boolean {
return parseBoolean(propertyName, value)
}
}
@@ -0,0 +1,100 @@
/*
* Copyright (C) 2017 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.options
/** Enum for deprecated element removal target. */
enum class DeprecationTarget constructor(
/**
* The target version when a deprecated element will be removed.
*
* Usage note: Do not use this field to construct a deprecation message, use
* getDeprecationTargetMessage() instead to ensure consistent message format.
*/
val removalTarget: Version,
/**
* Additional message to be shown below the pre-formatted error/warning message.
*
* Note that this additional message should be constructed such that it fits well in the
* overall message:
*
* "This feature will be removed in version X.Y of the Android Gradle plugin.\n
* $additionalMessage"
*
* For example, avoid writing additional messages that say "This feature is planned for
* removal", as it will be duplicated.
*/
private val additionalMessage: String? = null
) {
VERSION_9_0(Version.VERSION_9_0),
VERSION_8_3(Version.VERSION_8_3),
VERSION_8_0(Version.VERSION_8_0),
// Obsolete dx Dex Options
DEX_OPTIONS(Version.VERSION_8_0, "Using it has no effect, and the Android" +
"Gradle plugin optimizes dexing automatically."),
// Deprecation of Task Access in the variant API
TASK_ACCESS_VIA_VARIANT(Version.VERSION_9_0),
USE_PROPERTIES(
Version.VERSION_8_0,
"Gradle Properties must be used to change Variant information."
),
LINT_CHECK_ONLY(
Version.VERSION_8_0,
"`check` has been renamed to `checkOnly` to make it clear that it " +
"will turn off all other checks than those explicitly listed. If that is " +
"what you actually intended, use `checkOnly`; otherwise switch to `enable`."
),
DEFAULT_PUBLISH_CONFIG(
Version.VERSION_8_0,
"The support for publishing artifacts with Maven Plugin is removed, " +
"please migrate Maven Publish Plugin. See " +
"https://developer.android.com/studio/build/maven-publish-plugin for more information."
),
TRANSFORM_API(
Version.VERSION_8_0,
"""
The Transform API support has been removed in AGP 8.0.
""".trimIndent()
),
DENSITY_SPLIT_API(
Version.VERSION_9_0,
"Density-based apk split feature is deprecated and will be removed in AGP 9.0. " +
"Use Android App Bundle (https://developer.android.com/guide/app-bundle) " +
"to generate optimized APKs."
),
BUILD_CONFIG_GLOBAL_PROPERTY(
Version.VERSION_9_0,
"You can resolve this warning in Android Studio via `Refactor` > `Migrate " +
"BuildConfig to Gradle Build Files`"
)
;
fun getDeprecationTargetMessage(): String {
return removalTarget.getDeprecationTargetMessage() +
(additionalMessage?.let { "\n$it" } ?: "")
}
}
@@ -0,0 +1,147 @@
/*
* Copyright (C) 2017 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.options
import com.android.build.gradle.options.Version.VERSION_8_2
import com.android.builder.model.AndroidProject
import com.android.build.gradle.options.Version.VERSION_BEFORE_4_0
import com.android.builder.model.PROPERTY_BUILD_API
enum class IntegerOption(
override val propertyName: String,
stage: ApiStage
) : Option<Int> {
ANDROID_TEST_SHARD_COUNT("android.androidTest.numShards", ApiStage.Removed(VERSION_8_2, "Cross device sharding is no longer supported.")),
ANDROID_SDK_CHANNEL("android.sdk.channel", ApiStage.Experimental),
/**
* Returns the level of model-only mode.
*
* The model-only mode is triggered when the IDE does a sync, and therefore we do things a
* bit differently (don't throw exceptions for instance). Things evolved a bit over versions and
* the behavior changes. This reflects the mode to use.
*
* @see AndroidProject#MODEL_LEVEL_0_ORIGINAL
* @see AndroidProject#MODEL_LEVEL_1_SYNC_ISSUE
* @see AndroidProject#MODEL_LEVEL_3_VARIANT_OUTPUT_POST_BUILD
* @see AndroidProject#MODEL_LEVEL_4_NEW_DEP_MODEL
*/
IDE_BUILD_MODEL_ONLY_VERSION(AndroidProject.PROPERTY_BUILD_MODEL_ONLY_VERSIONED, ApiStage.Stable),
/**
* The api level for the target device.
*
* <p>For preview versions that is the last stable version, and the {@link
* StringOption#IDE_TARGET_DEVICE_CODENAME} will also be set.
*/
IDE_TARGET_DEVICE_API(PROPERTY_BUILD_API, ApiStage.Stable),
/**
* Size of the buffers in kilobytes used to read .class files and storage for writing .dex files
* translations into.
*/
DEXING_READ_BUFFER_SIZE("android.dexingReadBuffer.size", ApiStage.Experimental),
DEXING_WRITE_BUFFER_SIZE("android.dexingWriteBuffer.size", ApiStage.Experimental),
/** Number of buckets used by `DexArchiveBuilderTask` and `DexMergingTask`. */
DEXING_NUMBER_OF_BUCKETS("android.dexingNumberOfBuckets", ApiStage.Experimental),
/**
* Maximum number of dynamic features that can be allocated before Oreo platforms.
*/
PRE_O_MAX_NUMBER_OF_FEATURES("android.maxNumberOfFeaturesBeforeOreo", ApiStage.Experimental),
/**
* Override the thread pool size dedicated to AAPT2 work units when not running with WorkerAPI.
*/
AAPT2_THREAD_POOL_SIZE("android.aapt2ThreadPoolSize", ApiStage.Experimental),
/**
* Max number of R8 workers to run at once
*/
R8_MAX_WORKERS("android.r8.maxWorkers", ApiStage.Experimental),
/**
* Flags for Android Test Retention
* >=1: enable Android Test Retention and set it as maximum number of snapshots.
* 0: disable Android Test Retention.
* <0: enable Android Test Retention with unlimited number of snapshots.
*/
TEST_FAILURE_RETENTION("android.experimental.testOptions.emulatorSnapshots.maxSnapshotsForTestFailures", ApiStage.Experimental),
/**
* The number of shards ran on Managed Devices during testing.
*/
MANAGED_DEVICE_SHARD_POOL_SIZE("android.experimental.androidTest.numManagedDeviceShards", ApiStage.Experimental),
/**
* The timeout duration in minute for Gradle Managed Device setup steps (booting AVD and creating snapshot image)
* If the value is 0 or negative value, it will never time out.
*/
GRADLE_MANAGED_DEVICE_SETUP_TIMEOUT_MINUTES("android.experimental.testOptions.managedDevices.setupTimeoutMinutes", ApiStage.Experimental),
/**
* Maximum number of concurrent Gradle Managed Devices (AVDs) to be active at any one point in time.
*
* If the value is 0 or negative value, there is no maximum devices.
*/
GRADLE_MANAGED_DEVICE_MAX_CONCURRENT_DEVICES("android.experimental.testOptions.managedDevices.maxConcurrentDevices", ApiStage.Experimental),
/**
* The timeout duration in seconds for install apk
* If the value is 0 or negative value, it will be set to a default value by UTP.
*/
INSTALL_APK_TIMEOUT("android.experimental.testOptions.installApkTimeout", ApiStage.Experimental),
/* ------------
* REMOVED APIS
*/
THREAD_POOL_SIZE(
"android.threadPoolSize",
ApiStage.Removed(
VERSION_BEFORE_4_0,
"The android.threadPoolSize property has no effect"
)
),
;
override val status = stage.status
override fun parse(value: Any): Int {
if (value is CharSequence) {
try {
return Integer.parseInt(value.toString())
} catch (ignored: NumberFormatException) {
// Throws below.
}
}
if (value is Number) {
return value.toInt()
}
throw IllegalArgumentException(
"Cannot parse project property "
+ this.propertyName
+ "='"
+ value
+ "' of type '"
+ value.javaClass
+ "' as integer."
)
}
}
@@ -0,0 +1,93 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
/*
* Copyright (C) 2017 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.options
interface Option<out T> {
sealed class Status {
data object EXPERIMENTAL : Status()
data object STABLE : Status()
class Deprecated(val deprecationTarget: DeprecationTarget) :
Status() {
fun getDeprecationTargetMessage(): String {
return deprecationTarget.getDeprecationTargetMessage()
}
}
class Removed(
/**
* The version when an element was removed.
*
* Usage note: Do not use this field to construct a removal message, use
* getRemovedVersionMessage() instead to ensure consistent message format.
*/
val removedVersion: Version,
/**
* Additional message to be shown below the pre-formatted error/warning message.
*
* Note that this additional message should be constructed such that it fits well in the
* overall message:
*
* "This feature was removed in version X.Y of the Android Gradle plugin.\n
* $additionalMessage"
*
* For example, avoid writing additional messages that say "This feature has been
* removed", as it will be duplicated.
*/
private val additionalMessage: String? = null
) : Status() {
fun getRemovedVersionMessage(): String {
return removedVersion.getRemovedVersionMessage() +
(additionalMessage?.let { "\n$it" } ?: "")
}
}
}
val propertyName: String
val defaultValue: T?
get() = null
val status: Status
fun parse(value: Any): T
}
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2018 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.
*/
@file:JvmName("OptionParsers")
package com.android.build.gradle.options
import java.util.Locale
fun parseBoolean(propertyName: String, value: Any, propertyKind: String = "project"): Boolean {
return when (value) {
is Boolean -> value
is CharSequence ->
when (value.toString().lowercase(Locale.US)) {
"true" -> true
"false" -> false
else -> parseBooleanFailure(propertyName, value, propertyKind)
}
is Number ->
when (value.toInt()) {
0 -> false
1 -> true
else -> parseBooleanFailure(propertyName, value, propertyKind)
}
else -> parseBooleanFailure(propertyName, value, propertyKind)
}
}
private fun parseBooleanFailure(propertyName: String, value: Any, propertyKind: String): Nothing {
throw IllegalArgumentException(
"Cannot parse "
+ propertyKind
+ " property "
+ propertyName
+ "='"
+ value
+ "' of type '"
+ value.javaClass
+ "' as boolean. Expected 'true' or 'false'."
)
}
@@ -0,0 +1,86 @@
/*
* Copyright (C) 2017 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.options
import com.android.build.gradle.options.Version.VERSION_7_0
import com.android.build.gradle.options.Version.VERSION_8_0
import com.android.build.gradle.options.Version.VERSION_BEFORE_4_0
import com.android.builder.model.PROPERTY_SIGNING_V1_ENABLED
import com.android.builder.model.PROPERTY_SIGNING_V2_ENABLED
import com.android.builder.model.PROPERTY_TEST_ONLY
enum class OptionalBooleanOption(
override val propertyName: String,
val stage: Stage,
val recommendedValue: Boolean? = null
) : Option<Boolean> {
SIGNING_V1_ENABLED(PROPERTY_SIGNING_V1_ENABLED, ApiStage.Stable),
SIGNING_V2_ENABLED(PROPERTY_SIGNING_V2_ENABLED, ApiStage.Stable),
IDE_TEST_ONLY(PROPERTY_TEST_ONLY, ApiStage.Stable),
/**
* This project property is read by the firebase plugin, and has no direct impact on AGP behavior.
*
* It is included as an OptionalBooleanOption in order that its value, if set, is recorded in the AGP analytics.
*/
FIREBASE_PERF_PLUGIN_ENABLE_FLAG("firebasePerformanceInstrumentationEnabled", ApiStage.Stable),
// Flags for Android Test Retention
ENABLE_TEST_FAILURE_RETENTION_COMPRESS_SNAPSHOT("android.experimental.testOptions.emulatorSnapshots.compressSnapshots", ApiStage.Experimental),
ENABLE_API_MODELING_AND_GLOBAL_SYNTHETICS("android.enableApiModelingAndGlobalSynthetics", ApiStage.Experimental),
/* ----------------
* SOFTLY ENFORCED FEATURES
*/
/* ----------------
* ENFORCED FEATURES
*/
@Suppress("unused")
ENABLE_R8(
"android.enableR8",
FeatureStage.Enforced(VERSION_7_0, "Please remove it from `gradle.properties`.")
),
@Suppress("unused")
DISABLE_AUTOMATIC_COMPONENT_CREATION(
"android.disableAutomaticComponentCreation",
FeatureStage.Enforced(VERSION_8_0, "Please remove it from `gradle.properties`.")
),
/* ----------------
* REMOVED FEATURES
*/
@Suppress("unused")
SERIAL_AAPT2(
"android.injected.aapt2.serial",
FeatureStage.Removed(
VERSION_BEFORE_4_0,
"Invoking AAPT2 serially is no longer supported."
)
),
;
override val status = stage.status
override fun parse(value: Any): Boolean {
return parseBoolean(propertyName, value)
}
}
@@ -0,0 +1,171 @@
/*
* Copyright (C) 2017 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.options
import com.android.build.gradle.options.Version.VERSION_4_0
import com.android.build.gradle.options.Version.VERSION_4_1
import com.android.build.gradle.options.Version.VERSION_7_0
import com.android.build.gradle.options.Version.VERSION_BEFORE_4_0
/**
* [Option] that has been replaced by something else (e.g., another option or the DSL).
*
* Note that replaced options are different from removed options. Removed options belong to the
* [Option] subclass where they were first introduced, and should not be put in this class.
*/
enum class ReplacedOption(
override val propertyName: String,
private val replacedVersion: Version,
private val additionalMessage: String
) : Option<String> {
@Suppress("unused")
INCREMENTAL_JAVA_COMPILE(
"android.incrementalJavaCompile",
VERSION_BEFORE_4_0,
"The android.incrementalJavaCompile property has been replaced by a DSL property. "
+ "Please add the following to your build.gradle instead:\n"
+ "android {\n"
+ " compileOptions.incremental = false\n"
+ "}"
),
@Suppress("unused")
THREAD_POOL_SIZE_OLD(
"com.android.build.threadPoolSize",
VERSION_BEFORE_4_0,
"This property has been replaced by ${IntegerOption.THREAD_POOL_SIZE.propertyName}"
),
@Suppress("unused")
VERSION_CHECK_OVERRIDE_PROPERTY_OLD(
"com.android.build.gradle.overrideVersionCheck",
VERSION_BEFORE_4_0,
"This property has been replaced by ${BooleanOption.VERSION_CHECK_OVERRIDE_PROPERTY.propertyName}"
),
@Suppress("unused")
OVERRIDE_PATH_CHECK_PROPERTY_OLD(
"com.android.build.gradle.overridePathCheck",
VERSION_BEFORE_4_0,
"This property has been replaced by ${BooleanOption.OVERRIDE_PATH_CHECK_PROPERTY.propertyName}"
),
@Suppress("unused")
AAPT_NAMESPACING(
"android.aaptNamespacing",
VERSION_BEFORE_4_0,
"This property has been replaced by android.aaptOptions.namespaced (DSL)"
),
@Suppress("unused")
EXCLUDE_R_AND_MANIFEST_DOT_JAVA_FROM_GENERATED_SOURCES(
"android.excludeRAndManifestDotJavaFromGeneratedSources",
VERSION_BEFORE_4_0,
"Subsumed by ${BooleanOption.ENABLE_SEPARATE_R_CLASS_COMPILATION.propertyName}"
),
// This option should belong to the LongOption class, but that class has been removed so we put
// it here.
@Suppress("unused")
DEPRECATED_NDK_COMPILE_LEASE(
"android.deprecatedNdkCompileLease",
VERSION_BEFORE_4_0,
"NdkCompile is no longer supported"
),
@Suppress("unused")
USE_NON_FINAL_RES_IDS_IN_TESTS(
"android.androidTest.nonFinalResIds",
VERSION_BEFORE_4_0,
"This property has been replaced by ${BooleanOption.USE_NON_FINAL_RES_IDS.propertyName}"
),
@Suppress("unused")
ENABLE_INCREMENTAL_DESUGARING_V2(
"android.enableIncrementalDesugaringV2",
VERSION_4_0,
"This property has been replaced by ${BooleanOption.ENABLE_INCREMENTAL_DEXING_TASK_V2.propertyName}"
),
@Suppress("unused")
ENABLE_INCREMENTAL_DEXING_V2(
"android.enableIncrementalDexingV2",
VERSION_4_1,
"This property has been replaced by ${BooleanOption.ENABLE_INCREMENTAL_DEXING_TASK_V2.propertyName}"
),
@Suppress("unused")
NAMESPACED_R_CLASS(
"android.namespacedRClass",
VERSION_4_1,
"This property has been replaced by ${BooleanOption.NON_TRANSITIVE_R_CLASS.propertyName}"
),
@Suppress("unused")
ENABLE_PREFAB(
"android.enablePrefab",
VERSION_4_1,
"This property has been replaced by android.buildFeatures.prefab (DSL)"
),
// The flag was renamed.
@Suppress("unused")
ENABLE_STABLE_IDS(
"android.experimental.enableStableIds",
VERSION_4_1,
"This property has been replaced by ${BooleanOption.ENABLE_STABLE_IDS.propertyName}"
),
@Suppress("unused")
TEST_FAILURE_RETENTION_0(
"android.experimental.testOptions.emulatorSnapshots",
VERSION_7_0,
"This property has been replaced by ${IntegerOption.TEST_FAILURE_RETENTION.propertyName}"
),
@Suppress("unused")
TEST_FAILURE_RETENTION_1(
"android.testOptions.failureRetention",
VERSION_7_0,
"This property has been replaced by ${IntegerOption.TEST_FAILURE_RETENTION.propertyName}"
),
@Suppress("unused")
ENABLE_TEST_FAILURE_RETENTION_COMPRESS_SNAPSHOT(
"android.testOptions.failureRetention.compressSnapshots",
VERSION_7_0,
"This property has been replaced by ${OptionalBooleanOption.ENABLE_TEST_FAILURE_RETENTION_COMPRESS_SNAPSHOT.propertyName}"
),
// Jetifier: List of regular expressions for libraries that should not be jetified
@Suppress("unused", "WrongTerminology")
JETIFIER_BLACKLIST(
"android.jetifier.blacklist",
VERSION_7_0,
"This property has been replaced by ${StringOption.JETIFIER_IGNORE_LIST.propertyName}"
)
;
override val status: Option.Status
get() = Option.Status.Removed(replacedVersion, additionalMessage)
override fun parse(value: Any): String {
return value.toString()
}
}
@@ -0,0 +1,167 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
/*
* Copyright (C) 2019 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.options
/**
* The stage of an API or feature in its life cycle.
*
* An API or feature has an associated [Option] to allow the users to change the behavior of the
* Android Gradle plugin. The difference between them is that:
* - The [Option] associated with an API is intended to be used in the long term. For example, if
* it is a [BooleanOption] (or [OptionalBooleanOption]), one value of the option may be
* experimental at first, but eventually both values of the option will be supported.
* - The [Option] associated with a feature is intended to be used only in the short term. It can
* only be a [BooleanOption] (or [OptionalBooleanOption]). One value of the [BooleanOption] may
* be experimental at first to enable the new behavior, but eventually the option will be
* removed, either because the feature is fully supported and now enforced, or because the
* feature was not / is no longer useful and now removed.
*/
open class Stage(
/**
* Status of the [Option] which represents an API or feature. It is related but not the same as
* the [Stage] of the API of feature.
*/
val status: Option.Status
)
/**
* The stage of an API in its life cycle.
*
* See [Stage] for the difference between an API and a feature.
*/
sealed class ApiStage(status: Option.Status) : Stage(status) {
/**
* Indicates that the API is experimental.
*
* It may become stable or may be removed in a future release (see stage [Stable] and
* [Removed]).
*/
object Experimental : ApiStage(Option.Status.EXPERIMENTAL)
/**
* Indicates that the API is stable.
*/
object Stable : ApiStage(Option.Status.STABLE)
/**
* Indicates that the API will be removed soon because it was not / is no longer useful (see
* stage [Removed]).
*
* @param removalTarget a target when the API and the corresponding [Option] will be removed
*/
class Deprecated(removalTarget: DeprecationTarget) :
ApiStage(Option.Status.Deprecated(removalTarget))
/**
* Indicates that the API and the corresponding [Option] have been removed.
*
* @param removedVersion the version when the API and the corresponding [Option] were removed
* @param additionalMessage the additional message to be shown if the [Option] is used
*/
class Removed(removedVersion: Version, additionalMessage: String? = null) :
ApiStage(Option.Status.Removed(removedVersion, additionalMessage))
}
/**
* The stage of a feature in its life cycle.
*
* See [Stage] for the difference between an API and a feature.
*/
sealed class FeatureStage(status: Option.Status) : Stage(status) {
/**
* Indicates that the feature is experimental.
*
* It may be enforced or removed in a future release (see stage [Enforced] and [Removed]).
*/
object Experimental : FeatureStage(Option.Status.EXPERIMENTAL)
/**
* Indicates that the feature is fully supported.
*
* It may or may not be enabled by default. If it is not yet enabled by default, it will likely
* be enabled by default in a future release.
*
* Eventually, the feature will likely be enforced (see stage [SoftlyEnforced] and [Enforced]).
* In some cases, it may be removed (see stage [Deprecated] and [Removed]).
*
* DISCOURAGED USAGE: The use of this stage is actually discouraged as it doesn't specify a
* clear timeline and features may stay in this stage for too long, thus increasing maintenance
* cost to AGP and users. Consider using [SoftlyEnforced] or [Deprecated] instead.
*/
object Supported : FeatureStage(Option.Status.STABLE)
/**
* Indicates that the feature will be enforced soon (see stage [Enforced]).
*
* @param enforcementTarget a target when the feature will be enforced, at which point the
* corresponding [Option] will be removed (hence this parameter has type
* `DeprecationTarget`)
*/
class SoftlyEnforced(enforcementTarget: DeprecationTarget) :
FeatureStage(Option.Status.Deprecated(enforcementTarget))
/**
* Indicates that the feature is enforced (always enabled), and the corresponding [Option] has
* been removed.
*
* @param enforcedVersion the version when the feature is enforced and the corresponding
* [Option] was removed
* @param additionalMessage the additional message to be shown if the [Option] is used
*/
class Enforced(enforcedVersion: Version, additionalMessage: String? = null) :
FeatureStage(Option.Status.Removed(enforcedVersion, additionalMessage))
/**
* Indicates that the feature will be removed soon because it was not / is no longer useful (see
* stage [Removed]).
*
* @param removalTarget a target when the feature and the corresponding [Option] will be removed
*/
class Deprecated(removalTarget: DeprecationTarget) :
FeatureStage(Option.Status.Deprecated(removalTarget))
/**
* Indicates that the feature has been removed (always disabled), and the corresponding [Option]
* has been removed.
*
* @param removedVersion the version when the feature and the corresponding [Option] were
* removed
* @param additionalMessage the additional message to be shown if the [Option] is used
*/
class Removed(removedVersion: Version, additionalMessage: String? = null) :
FeatureStage(Option.Status.Removed(removedVersion, additionalMessage))
}
@@ -0,0 +1,181 @@
/*
* Copyright (C) 2017 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.options
import com.android.builder.model.AndroidProject
import com.android.builder.model.PROPERTY_APK_LOCATION
import com.android.builder.model.PROPERTY_APK_SELECT_CONFIG
import com.android.builder.model.PROPERTY_ATTRIBUTION_FILE_LOCATION
import com.android.builder.model.PROPERTY_BUILD_ABI
import com.android.builder.model.PROPERTY_BUILD_API_CODENAME
import com.android.builder.model.PROPERTY_BUILD_DENSITY
import com.android.builder.model.PROPERTY_CHECK_JETIFIER_RESULT_FILE
import com.android.builder.model.PROPERTY_INJECTED_DYNAMIC_MODULES_LIST
import com.android.builder.model.PROPERTY_NATIVE_BUILD_OUTPUT_LEVEL
import com.android.builder.model.PROPERTY_SIGNING_KEY_ALIAS
import com.android.builder.model.PROPERTY_SIGNING_KEY_PASSWORD
import com.android.builder.model.PROPERTY_SIGNING_STORE_FILE
import com.android.builder.model.PROPERTY_SIGNING_STORE_PASSWORD
import com.android.builder.model.PROPERTY_SIGNING_STORE_TYPE
enum class StringOption(
override val propertyName: String,
stage: ApiStage
) : Option<String> {
/* -----------
* STABLE APIs
*/
IDE_BUILD_TARGET_ABI(PROPERTY_BUILD_ABI, ApiStage.Stable),
IDE_ATTRIBUTION_FILE_LOCATION(PROPERTY_ATTRIBUTION_FILE_LOCATION, ApiStage.Stable),
/** Absolute path to a file containing the result of the `CheckJetifier` task. */
IDE_CHECK_JETIFIER_RESULT_FILE(PROPERTY_CHECK_JETIFIER_RESULT_FILE, ApiStage.Stable),
// Signing options
IDE_SIGNING_STORE_TYPE(PROPERTY_SIGNING_STORE_TYPE, ApiStage.Stable),
IDE_SIGNING_STORE_FILE(PROPERTY_SIGNING_STORE_FILE, ApiStage.Stable),
IDE_SIGNING_STORE_PASSWORD(PROPERTY_SIGNING_STORE_PASSWORD, ApiStage.Stable),
IDE_SIGNING_KEY_ALIAS(PROPERTY_SIGNING_KEY_ALIAS, ApiStage.Stable),
IDE_SIGNING_KEY_PASSWORD(PROPERTY_SIGNING_KEY_PASSWORD, ApiStage.Stable),
// device config for ApkSelect
IDE_APK_SELECT_CONFIG(PROPERTY_APK_SELECT_CONFIG, ApiStage.Stable),
// location where to write the APK/BUNDLE
IDE_APK_LOCATION(PROPERTY_APK_LOCATION, ApiStage.Stable),
IDE_TARGET_DEVICE_CODENAME(PROPERTY_BUILD_API_CODENAME, ApiStage.Stable),
// Profiler plugin
IDE_ANDROID_CUSTOM_CLASS_TRANSFORMS("android.advanced.profiling.transforms", ApiStage.Stable),
// The exact version of Android Support plugin used, e.g. 2.4.0.6
IDE_ANDROID_STUDIO_VERSION(AndroidProject.PROPERTY_ANDROID_SUPPORT_VERSION, ApiStage.Stable),
// The version of Android Game Development Extension used to orchestrate the build
IDE_AGDE_VERSION("agde.version", ApiStage.Stable),
// Native
NATIVE_BUILD_OUTPUT_LEVEL(PROPERTY_NATIVE_BUILD_OUTPUT_LEVEL, ApiStage.Stable),
// AGP suggests it should be upgraded if the compile sdk is newer than the version it was tested
// with. This option allows developers to suppress that warning.
// e.g. android.suppressUnsupportedCompileSdk=S,31,32
SUPPRESS_UNSUPPORTED_COMPILE_SDK("android.suppressUnsupportedCompileSdk", ApiStage.Stable),
// User-specified flag for using a profileable or debuggable build,
// if the flag is not set, the debuggable value will fallback to using the DSL 'debuggable'.
PROFILING_MODE("android.profilingMode", ApiStage.Stable),
// Override for the default execution profile in the settings plugin.
EXECUTION_PROFILE_SELECTION("android.settings.executionProfile", ApiStage.Stable),
/* -----------------
* EXPERIMENTAL APIs
*/
// Installation related options
IDE_INSTALL_DYNAMIC_MODULES_LIST(PROPERTY_INJECTED_DYNAMIC_MODULES_LIST, ApiStage.Experimental),
// Testing
DEVICE_POOL_SERIAL("com.android.test.devicepool.serial", ApiStage.Experimental),
PROFILE_OUTPUT_DIR("android.advanced.profileOutputDir", ApiStage.Experimental),
BUILD_ARTIFACT_REPORT_FILE("android.buildartifact.reportfile", ApiStage.Experimental),
AAPT2_FROM_MAVEN_OVERRIDE("android.aapt2FromMavenOverride", ApiStage.Experimental),
AAPT2_FROM_MAVEN_VERSION_OVERRIDE("android.aapt2Version", ApiStage.Experimental),
AAPT2_FROM_MAVEN_PLATFORM_OVERRIDE("android.aapt2Platform", ApiStage.Experimental),
SUPPRESS_UNSUPPORTED_OPTION_WARNINGS("android.suppressUnsupportedOptionWarnings", ApiStage.Experimental),
// User-specified path to Prefab jar to return from getPrefabFromMaven.
PREFAB_CLASSPATH("android.prefabClassPath", ApiStage.Experimental),
// User-specified Prefab version to pull from Maven in getPrefabFromMaven.
PREFAB_VERSION("android.prefabVersion", ApiStage.Experimental),
// Jetifier: List of regular expressions for libraries that should not be jetified
JETIFIER_IGNORE_LIST("android.jetifier.ignorelist", ApiStage.Experimental),
// Lint: Allow customization of the heap size of the process started to run lint
LINT_HEAP_SIZE("android.experimental.lint.heapSize", ApiStage.Experimental),
// Lint: Allow customization of the amount of memory earmarked for each lint task (when lint
// runs in process)
LINT_RESERVED_MEMORY_PER_TASK("android.experimental.lint.reservedMemoryPerTask", ApiStage.Experimental),
// Lint: Allow override of the version. Note that lint versions are generally 23 higher than
// the version of Android Gradle Plugin. So AGP 7.0.0-beta02 defaults to using lint
// 30.0.0-beta02
LINT_VERSION_OVERRIDE("android.experimental.lint.version", ApiStage.Experimental),
// User-specified flag to override the emulator gpu mode for Gradle Managed Devices,
// If the flag is not set, the emulator gpu mode will default to auto-no-window.
// Supported values are "auto", "auto-no-window", "host", "swiftshader_indirect",
// "angle_indirect"
GRADLE_MANAGED_DEVICE_EMULATOR_GPU_MODE("android.testoptions.manageddevices.emulator.gpu", ApiStage.Experimental),
ANDROID_PRIVACY_SANDBOX_SDK_API_GENERATOR("android.privacySandboxSdk.apiGenerator", ApiStage.Experimental),
ANDROID_PRIVACY_SANDBOX_SDK_API_GENERATOR_GENERATED_RUNTIME_DEPENDENCIES("android.privacySandboxSdk.apiGenerator.generatedRuntimeDependencies", ApiStage.Experimental),
ANDROID_PRIVACY_SANDBOX_SDK_API_PACKAGER("android.privacySandboxSdk.apiPackager", ApiStage.Experimental),
/* ---------------
* DEPRECATED APIs
*/
/* ------------
* REMOVED APIs
*/
@Suppress("unused")
BUILD_CACHE_DIR(
"android.buildCacheDir",
ApiStage.Removed(Version.VERSION_7_0, "The Android-specific build caches were superseded by the Gradle build cache (https://docs.gradle.org/current/userguide/build_cache.html).")
),
@Suppress("unused")
IDE_BUILD_TARGET_DENSITY(
PROPERTY_BUILD_DENSITY,
ApiStage.Removed(Version.VERSION_8_0, "Density property injection from Android Studio has been removed.")
),
;
override val status = stage.status
override fun parse(value: Any): String {
if (value is CharSequence || value is Number) {
return value.toString()
}
throw IllegalArgumentException(
"Cannot parse project property "
+ this.propertyName
+ "='"
+ value
+ "' of type '"
+ value.javaClass
+ "' as string."
)
}
}
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2020 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.options
/**
* A list of standard instrumentation test runner keys.
*
* Details: https://developer.android.com/studio/test/command-line#AMOptionsSyntax
*/
enum class TestRunnerArguments(
private val key: String
) {
@Suppress("unused")
PACKAGE("package"),
@Suppress("unused")
CLASS("class"),
@Suppress("unused")
FUNC("func"),
@Suppress("unused")
SIZE("size"),
@Suppress("unused")
PERF("perf"),
@Suppress("unused")
DEBUG("debug"),
@Suppress("unused")
LOG("log"),
@Suppress("unused")
EMMA("emma"),
@Suppress("unused")
COVERAGE_FILE("coverageFile"),
;
fun getFullKey() : String = "$TEST_RUNNER_ARGS_PREFIX$key"
fun getShortKey() : String = key
companion object {
const val TEST_RUNNER_ARGS_PREFIX = "android.testInstrumentationRunnerArguments."
}
}
@@ -0,0 +1,66 @@
/*
* Copyright (C) 2020 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.options
/** An Android Gradle plugin version. */
enum class Version(
/**
* String value of the version, or `null` if it is not known (e.g., [VERSION_BEFORE_4_0]).
*
* Usage note: Do not use this field to construct a deprecation/removal message, use
* getDeprecationTargetMessage()/getRemovedVersionMessage() instead to ensure consistent message
* format.
*/
val versionString: String?
) {
/**
* A version before version 4.0, used when the exact version is not known, except that it's
* guaranteed to be before 4.0.
*/
VERSION_BEFORE_4_0(null),
VERSION_3_5("3.5"),
VERSION_3_6("3.6"),
VERSION_4_0("4.0"),
VERSION_4_1("4.1"),
VERSION_4_2("4.2"),
VERSION_7_0("7.0"),
VERSION_7_2("7.2"),
VERSION_7_3("7.3"),
VERSION_8_0("8.0"),
VERSION_8_1("8.1"),
VERSION_8_2("8.2"),
VERSION_8_3("8.3"),
VERSION_9_0("9.0"),
; // end of enums
fun getDeprecationTargetMessage(): String {
check(this != VERSION_BEFORE_4_0)
return "It will be removed in version $versionString of the Android Gradle plugin."
}
fun getRemovedVersionMessage(): String {
return if (this == VERSION_BEFORE_4_0) {
"It has been removed from the current version of the Android Gradle plugin."
} else {
"It was removed in version $versionString of the Android Gradle plugin."
}
}
}
@@ -20,7 +20,7 @@ import com.android.builder.model.v2.models.ProjectSyncIssues
import java.io.Serializable
/** @author Akash Yadav */
class DefaultProjectSyncIssues(override val syncIssues: Collection<DefaultSyncIssue>) :
data class DefaultProjectSyncIssues(override val syncIssues: Collection<DefaultSyncIssue>) :
ProjectSyncIssues, Serializable {
private val serialVersionUID = 1L
}
@@ -17,16 +17,16 @@
package com.itsaky.androidide.builder.model
import com.android.builder.model.v2.ide.SyncIssue
import java.io.Serializable
/** @author Akash Yadav */
class DefaultSyncIssue(
data class DefaultSyncIssue(
override val data: String?,
override val message: String,
override val multiLineMessage: List<String?>?,
override val severity: Int,
override val type: Int
) : SyncIssue, Serializable {
) : IDESyncIssue, Serializable {
private val serialVersionUID = 1L
}
@@ -0,0 +1,57 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.builder.model
import com.android.build.gradle.options.StringOption
import com.android.build.gradle.options.StringOption.AAPT2_FROM_MAVEN_OVERRIDE
import com.android.builder.model.v2.ide.SyncIssue
/**
* Sync issue model for AndroidIDE.
*
* @author Akash Yadav
*/
interface IDESyncIssue : SyncIssue {
companion object {
/**
* Indicates that the Android Gradle Plugin that is being used by the project
* is too new for AndroidIDE. Data is `projectAgpVersion:maxAgpVersion`.
*/
const val TYPE_AGP_VERSION_TOO_NEW = -1
// Note: When adding new types, decrement the version by 1
// The types that are defined in SyncIssue class have their values starting at 0 and incremented
// by 1 when new types are added. So, we could never know what is the latest type's value
}
}
/**
* Checks whether this [SyncIssue] should be ignored and not reported to the user.
*
* @return Whether the issue can be ignored.
*/
fun SyncIssue.shouldBeIgnored() : Boolean {
if (this.type != SyncIssue.TYPE_UNSUPPORTED_PROJECT_OPTION_USE) {
return false
}
// AndroidIDE sets android.aapt2FromMavenOverride in order to use a custom AAPT2 that is
// compatible with Android
return AAPT2_FROM_MAVEN_OVERRIDE.propertyName == this.data
}
@@ -42,7 +42,7 @@ open class CachingProject(val project: IProject) : IProject {
private val log = ILogger.newInstance(javaClass.simpleName)
private val projects = mutableListOf<BasicProjectMetadata>()
private val syncIssues = mutableMapOf<String, DefaultProjectSyncIssues>()
private var syncIssues: DefaultProjectSyncIssues? = null
override fun getProjects(): CompletableFuture<List<BasicProjectMetadata>> {
return if (this.projects.isNotEmpty()) {
@@ -64,23 +64,23 @@ open class CachingProject(val project: IProject) : IProject {
}
}
override fun getProjectSyncIssues(): CompletableFuture<Map<String, DefaultProjectSyncIssues>> {
return if (this.syncIssues.isNotEmpty()) {
log.info("Using cached sync issues...")
CompletableFuture.completedFuture(this.syncIssues)
} else this.project.getProjectSyncIssues().whenComplete { syncIssues, err ->
if (err != null || syncIssues == null) {
override fun getProjectSyncIssues(): CompletableFuture<DefaultProjectSyncIssues> {
this.syncIssues?.also {
return CompletableFuture.completedFuture(it)
}
return this.project.getProjectSyncIssues().whenComplete { projectSyncIssues, err ->
if (err != null || projectSyncIssues == null) {
log.debug("Unable to fetch project sync issues from tooling server", err)
return@whenComplete
}
if (syncIssues.isEmpty()) {
if (projectSyncIssues.syncIssues.isEmpty()) {
log.debug("No sync issues.")
return@whenComplete
}
this.syncIssues.clear()
this.syncIssues.putAll(syncIssues)
this.syncIssues = projectSyncIssues
}
}
@@ -18,6 +18,7 @@
package com.itsaky.androidide.projects
import androidx.annotation.RestrictTo
import com.android.builder.model.v2.models.ProjectSyncIssues
import com.itsaky.androidide.lookup.Lookup
import com.itsaky.androidide.projects.api.AndroidModule
import com.itsaky.androidide.projects.api.ModuleProject
@@ -68,6 +69,11 @@ interface IProjectManager {
val projectDir: File
get() = File(projectDirPath)
/**
* Issues that were encountered during project synchronization.
*/
val projectSyncIssues: ProjectSyncIssues?
/**
* Build variant information about Android modules in the project.
*
@@ -81,7 +87,7 @@ interface IProjectManager {
* @param project The project proxy.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
fun setupProject(
suspend fun setupProject(
project: IProject = Lookup.getDefault().lookup(BuildService.KEY_PROJECT_PROXY)!!
)
@@ -18,6 +18,7 @@
package com.itsaky.androidide.projects
import androidx.annotation.RestrictTo
import com.android.builder.model.v2.models.ProjectSyncIssues
import com.google.auto.service.AutoService
import com.google.common.collect.ImmutableList
import com.itsaky.androidide.eventbus.events.EventReceiver
@@ -51,6 +52,7 @@ import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
@@ -84,6 +86,9 @@ class ProjectManagerImpl : IProjectManager, EventReceiver {
override val projectDirPath: String
get() = projectPath
override val projectSyncIssues: ProjectSyncIssues?
get() = rootProject?.projectSyncIssues
companion object {
private val log = ILogger.newInstance("ProjectManagerImpl")
@@ -95,8 +100,13 @@ class ProjectManagerImpl : IProjectManager, EventReceiver {
}
}
override fun setupProject(project: IProject) {
this.rootProject = ProjectTransformer().transform(CachingProject(project))
override suspend fun setupProject(project: IProject) {
this.rootProject = withStopWatch("Transform project proxy") {
withContext(Dispatchers.IO) {
ProjectTransformer().transform(CachingProject(project))
}
}
val rootProject = this.rootProject ?: return
// build variants must be updated before the sources and classpaths are indexed
@@ -104,6 +114,8 @@ class ProjectManagerImpl : IProjectManager, EventReceiver {
androidBuildVariants = buildVariants
}
log.info("Found ${rootProject.projectSyncIssues.syncIssues.size} project sync issues: ${rootProject.projectSyncIssues.syncIssues}")
withStopWatch("Setup project") {
val indexerScope = CoroutineScope(Dispatchers.Default)
val modulesFlow = flow {
@@ -18,7 +18,7 @@
package com.itsaky.androidide.projects.api
import com.android.builder.model.v2.ide.ProjectType
import com.itsaky.androidide.builder.model.DefaultProjectSyncIssues
import com.android.builder.model.v2.models.ProjectSyncIssues
import java.io.File
import java.nio.file.Path
@@ -27,11 +27,14 @@ import java.nio.file.Path
*
* @property rootProject The root Gradle project.
* @property subProjects List of all project that are included the project.
* @property syncIssues The issues that occurred while syncing the project.
* @property projectSyncIssues The issues that occurred while syncing the project.
* @author Akash Yadav
*/
data class Project(val rootProject: GradleProject, val subProjects: List<GradleProject>,
val syncIssues: Map<String, DefaultProjectSyncIssues>) {
data class Project(
val rootProject: GradleProject,
val subProjects: List<GradleProject>,
val projectSyncIssues: ProjectSyncIssues
) {
/**
* Finds the project by the given path.
@@ -57,8 +57,11 @@ class ProjectTransformer {
"Root project must be either an Android project or a Gradle project")
}
return Project(rootProject, CopyOnWriteArrayList(transform(allProjects, project)),
project.getProjectSyncIssues().get())
return Project(
rootProject,
CopyOnWriteArrayList(transform(allProjects, project)),
project.getProjectSyncIssues().get()
)
} catch (error: Throwable) {
log.error("Unable to transform project", error)
return null
@@ -30,6 +30,7 @@ import com.itsaky.androidide.tooling.api.messages.InitializeProjectParams
import com.itsaky.androidide.tooling.testing.ToolingApiTestLauncher
import com.itsaky.androidide.utils.FileProvider
import com.itsaky.androidide.utils.SourceClassTrie.SourceNode
import kotlinx.coroutines.runBlocking
import java.io.File
import java.nio.file.Files
import kotlin.io.path.pathString
@@ -205,7 +206,7 @@ class ModuleProjectTest {
}
val projectManager = ProjectManagerImpl.getInstance()
projectManager.setupProject()
runBlocking { projectManager.setupProject() }
val rootProject = IProjectManager.getInstance().rootProject
assertThat(rootProject).isNotNull()
@@ -159,8 +159,7 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) :
stopWatch.lapFromLast("Project read successful")
stopWatch.log()
this.project.rootProject = project.rootProject
this.project.projects = project.projects
this.project.setFrom(project)
this.isInitialized = true
notifyBuildSuccess(emptyList())
@@ -33,11 +33,11 @@ import java.util.concurrent.CompletableFuture
/**
* @author Akash Yadav
*/
@Suppress("JoinDeclarationAndAssignment")
internal class ProjectImpl(
var rootProject: IGradleProject? = null,
var rootProjectPath: String? = null,
var projects: List<IGradleProject> = emptyList()
var projects: List<IGradleProject> = emptyList(),
var projectSyncIssues: DefaultProjectSyncIssues = DefaultProjectSyncIssues(emptyList())
) : IProject, Serializable {
private val serialVersionUID = 1L
@@ -57,6 +57,13 @@ internal class ProjectImpl(
this.selectedProject = ForwardingProject()
}
fun setFrom(other: ProjectImpl) {
this.rootProject = other.rootProject
this.rootProjectPath = other.rootProjectPath
this.projects = other.projects
this.projectSyncIssues = other.projectSyncIssues
}
private fun getProject(path: String): IGradleProject? {
return if (path.isBlank()) rootProject else projects.find {
it.getMetadata().get().projectPath == path
@@ -69,8 +76,10 @@ internal class ProjectImpl(
}
}
override fun getProjectSyncIssues(): CompletableFuture<Map<String, DefaultProjectSyncIssues>> {
return CompletableFuture.completedFuture(emptyMap())
override fun getProjectSyncIssues(): CompletableFuture<DefaultProjectSyncIssues> {
return CompletableFuture.completedFuture(
this.projectSyncIssues ?: DefaultProjectSyncIssues(emptyList())
)
}
override fun selectProject(param: StringParameter): CompletableFuture<SelectProjectResult> {
@@ -16,7 +16,10 @@
*/
package com.itsaky.androidide.tooling.impl.sync
import com.android.builder.model.v2.ide.SyncIssue
import com.android.builder.model.v2.models.Versions
import com.itsaky.androidide.builder.model.DefaultSyncIssue
import com.itsaky.androidide.builder.model.IDESyncIssue
import com.itsaky.androidide.tooling.api.messages.InitializeProjectParams
import com.itsaky.androidide.tooling.impl.util.StopWatch
import com.itsaky.androidide.utils.AndroidPluginVersion
@@ -54,9 +57,13 @@ abstract class AbstractModelBuilder<P, R>(
* [AndroidPluginVersion.LATEST_TESTED], warns the user.
*
* @param versions The [Versions] model.
* @param syncIssueReporter [ISyncIssueReporter] for reporting issues with the Android Gradle Plugin version.
*/
@JvmStatic
protected fun checkAgpVersion(versions: Versions) {
protected fun checkAgpVersion(
versions: Versions,
syncIssueReporter: ISyncIssueReporter
) {
val agpVersion = AndroidPluginVersion.parse(versions.agp)
@@ -72,14 +79,14 @@ abstract class AbstractModelBuilder<P, R>(
// Warn the user if the project is using a newer AGP version
if (!newerAgpWarned.get() && agpVersion > LATEST_TESTED) {
log()
log("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
log("You are using Android Gradle Plugin version that has not been tested with AndroidIDE.")
log("Newer versions may not work properly!")
log("Current version : ${agpVersion.toStringSimple()}")
log("Latest tested : ${LATEST_TESTED.toStringSimple()}")
log("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
log()
val syncIssue = DefaultSyncIssue(
data = "${agpVersion.toStringSimple()}:${LATEST_TESTED.toStringSimple()}",
message = "You are using Android Gradle Plugin version that has not been tested with AndroidIDE.",
multiLineMessage = null,
severity = SyncIssue.SEVERITY_WARNING,
type = IDESyncIssue.TYPE_AGP_VERSION_TOO_NEW
)
syncIssueReporter.report(syncIssue)
newerAgpWarned.set(true)
}
}
@@ -19,6 +19,7 @@ package com.itsaky.androidide.tooling.impl.sync
import com.android.builder.model.v2.models.AndroidProject
import com.android.builder.model.v2.models.BasicAndroidProject
import com.android.builder.model.v2.models.ModelBuilderParameter
import com.android.builder.model.v2.models.ProjectSyncIssues
import com.android.builder.model.v2.models.VariantDependencies
import com.itsaky.androidide.tooling.api.IAndroidProject
import com.itsaky.androidide.tooling.api.messages.InitializeProjectParams
@@ -33,7 +34,7 @@ class AndroidProjectModelBuilder(initializationParams: InitializeProjectParams)
AbstractModelBuilder<AndroidProjectModelBuilderParams, IAndroidProject>(initializationParams) {
override fun build(param: AndroidProjectModelBuilderParams): IAndroidProject {
val (controller, module, versions) = param
val (controller, module, versions, syncIssueReporter) = param
val androidParams = initializationParams.androidParams
val projectPath = module.gradleProject.path
@@ -47,7 +48,8 @@ class AndroidProjectModelBuilder(initializationParams: InitializeProjectParams)
var androidVariant = androidParams.variantSelections[projectPath]
if (androidVariant != null && !variantNames.contains(androidVariant)) {
log("Configured variant '$androidVariant' not found for project '$projectPath'. Falling back to default variant.")
log(
"Configured variant '$androidVariant' not found for project '$projectPath'. Falling back to default variant.")
androidVariant = null
}
@@ -65,6 +67,10 @@ class AndroidProjectModelBuilder(initializationParams: InitializeProjectParams)
it.dontBuildRuntimeClasspath = false
}
controller.findModel(module, ProjectSyncIssues::class.java)?.also { syncIssues ->
syncIssueReporter.reportAll(syncIssues)
}
return AndroidProjectImpl(
module.gradleProject,
configurationVariant,
@@ -0,0 +1,48 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.tooling.impl.sync
import com.android.builder.model.v2.ide.SyncIssue
import com.android.builder.model.v2.models.ProjectSyncIssues
/**
* A sync issue reporter reports project synchronization issues.
*
* @author Akash Yadav
*/
fun interface ISyncIssueReporter {
/**
* Report all sync issues from the given [ProjectSyncIssues].
*/
fun reportAll(issues: ProjectSyncIssues) {
reportAll(issues.syncIssues)
}
/**
* Report all the given [sync issues][issues].
*/
fun reportAll(issues: Collection<SyncIssue>) {
issues.forEach(this::report)
}
/**
* Report the give [sync issue][issue].
*/
fun report(issue: SyncIssue)
}
@@ -31,9 +31,14 @@ class ModuleProjectModelBuilder(initializationParams: InitializeProjectParams) :
override fun build(param: ModuleProjectModelBuilderParams): IModuleProject {
val versions = getAndroidVersions(param.module, param.controller)
return if (versions != null) {
checkAgpVersion(versions)
checkAgpVersion(versions, param.syncIssueReporter)
AndroidProjectModelBuilder(initializationParams)
.build(Triple(param.controller, param.module, versions))
.build(AndroidProjectModelBuilderParams(
param.controller,
param.module,
versions,
param.syncIssueReporter
))
} else {
JavaProjectModelBuilder(initializationParams).build(
JavaProjectModelBuilderParams(param))
@@ -17,9 +17,13 @@
package com.itsaky.androidide.tooling.impl.sync
import com.itsaky.androidide.builder.model.DefaultProjectSyncIssues
import com.itsaky.androidide.builder.model.DefaultSyncIssue
import com.itsaky.androidide.builder.model.shouldBeIgnored
import com.itsaky.androidide.tooling.api.IAndroidProject
import com.itsaky.androidide.tooling.api.IProject
import com.itsaky.androidide.tooling.api.messages.InitializeProjectParams
import com.itsaky.androidide.tooling.api.util.AndroidModulePropertyCopier
import com.itsaky.androidide.tooling.impl.Main
import com.itsaky.androidide.tooling.impl.Main.finalizeLauncher
import com.itsaky.androidide.tooling.impl.internal.ProjectImpl
@@ -55,20 +59,48 @@ class RootModelBuilder(initializationParams: InitializeProjectParams) :
val rootProjectVersions = getAndroidVersions(rootModule, controller)
val syncIssues = hashSetOf<DefaultSyncIssue>()
val syncIssueReporter = ISyncIssueReporter {
if (it.shouldBeIgnored()) {
// this SyncIssue should not be shown to the user
return@ISyncIssueReporter
}
val issue = it as? DefaultSyncIssue ?: AndroidModulePropertyCopier.copy(it)
syncIssues.add(issue)
}
val rootProject = if (rootProjectVersions != null) {
// Root project is an Android project
checkAgpVersion(rootProjectVersions)
AndroidProjectModelBuilder(initializationParams).build(Triple(controller, rootModule, rootProjectVersions))
checkAgpVersion(rootProjectVersions, syncIssueReporter)
AndroidProjectModelBuilder(initializationParams)
.build(AndroidProjectModelBuilderParams(
controller,
rootModule,
rootProjectVersions,
syncIssueReporter
))
} else {
GradleProjectModelBuilder(initializationParams).build(rootModule.gradleProject)
}
val projects = ideaModules.map { ideaModule ->
ModuleProjectModelBuilder(initializationParams).build(
ModuleProjectModelBuilderParams(controller, ideaProject, ideaModule, modulePaths))
ModuleProjectModelBuilderParams(
controller,
ideaProject,
ideaModule,
modulePaths,
syncIssueReporter
))
}
return@action ProjectImpl(rootProject, rootModule.gradleProject.path, projects)
return@action ProjectImpl(
rootProject,
rootModule.gradleProject.path,
projects,
DefaultProjectSyncIssues(syncIssues)
)
}
finalizeLauncher(executor)
@@ -22,10 +22,28 @@ import org.gradle.tooling.BuildController
import org.gradle.tooling.model.idea.IdeaModule
import org.gradle.tooling.model.idea.IdeaProject
typealias AndroidProjectModelBuilderParams = Triple<BuildController, IdeaModule, Versions>
/**
* Parameters for building model for an Android project.
*
* @property controller The build controller that will be used to fetch project models.
* @property module The [IdeaModule] to fetch the models from.
* @property versions The Android Gradle Plugin version information.
* @property syncIssueReporter [ISyncIssueReporter] to report project synchronization issues.
*/
data class AndroidProjectModelBuilderParams(
val controller: BuildController,
val module: IdeaModule,
val versions: Versions,
val syncIssueReporter: ISyncIssueReporter
)
class ModuleProjectModelBuilderParams(val controller: BuildController, project: IdeaProject,
module: IdeaModule, modulePaths: Map<String, String>) : JavaProjectModelBuilderParams(
class ModuleProjectModelBuilderParams(
val controller: BuildController,
project: IdeaProject,
module: IdeaModule,
modulePaths: Map<String, String>,
val syncIssueReporter: ISyncIssueReporter
) : JavaProjectModelBuilderParams(
project, module, modulePaths)
open class JavaProjectModelBuilderParams(val project: IdeaProject, val module: IdeaModule,
@@ -43,7 +43,7 @@ interface IProject : IProjectQueries {
* Get the project sync issues.
*/
@JsonRequest
fun getProjectSyncIssues(): CompletableFuture<Map<String, DefaultProjectSyncIssues>>
fun getProjectSyncIssues(): CompletableFuture<DefaultProjectSyncIssues>
companion object {
@@ -306,5 +306,5 @@ object AndroidModulePropertyCopier {
}
fun copy(issue: SyncIssue) =
DefaultSyncIssue(issue.data, issue.message, issue.multiLineMessage, issue.severity, issue.type)
DefaultSyncIssue(issue.data, issue.message, issue.multiLineMessage?.toTypedArray()?.let { listOf(*it) }, issue.severity, issue.type)
}
@@ -27,6 +27,7 @@ import com.itsaky.androidide.projects.api.AndroidModule
import com.itsaky.androidide.projects.builder.BuildService
import com.itsaky.androidide.projects.util.findAppModule
import com.itsaky.androidide.tooling.testing.ToolingApiTestLauncher
import kotlinx.coroutines.runBlocking
import org.junit.Ignore
import org.robolectric.Robolectric
import java.util.concurrent.atomic.AtomicBoolean
@@ -47,7 +48,7 @@ object XmlInflaterTest {
assertThat(result?.isSuccessful).isTrue()
Lookup.getDefault().register(BuildService.KEY_PROJECT_PROXY, project)
IProjectManager.getInstance().setupProject(project)
runBlocking { IProjectManager.getInstance().setupProject(project) }
init.set(true)
}
}