diff --git a/packages/animations/example/.metadata b/packages/animations/example/.metadata index 706ff779c7b7..9fb00192b70a 100644 --- a/packages/animations/example/.metadata +++ b/packages/animations/example/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "603104015dd692ea3403755b55d07813d5cf8965" + revision: "582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536" channel: "stable" project_type: app @@ -13,11 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: 603104015dd692ea3403755b55d07813d5cf8965 - base_revision: 603104015dd692ea3403755b55d07813d5cf8965 + create_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536 + base_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536 - platform: android - create_revision: 603104015dd692ea3403755b55d07813d5cf8965 - base_revision: 603104015dd692ea3403755b55d07813d5cf8965 + create_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536 + base_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536 # User provided section diff --git a/packages/animations/example/android/.gitignore b/packages/animations/example/android/.gitignore index 55afd919c659..be3943c96d8e 100644 --- a/packages/animations/example/android/.gitignore +++ b/packages/animations/example/android/.gitignore @@ -5,6 +5,7 @@ gradle-wrapper.jar /gradlew.bat /local.properties GeneratedPluginRegistrant.java +.cxx/ # Remember to never publicly share your keystore. # See https://flutter.dev/to/reference-keystore diff --git a/packages/animations/example/android/app/build.gradle b/packages/animations/example/android/app/build.gradle.kts similarity index 66% rename from packages/animations/example/android/app/build.gradle rename to packages/animations/example/android/app/build.gradle.kts index e9083f04998b..ffa850c091c9 100644 --- a/packages/animations/example/android/app/build.gradle +++ b/packages/animations/example/android/app/build.gradle.kts @@ -1,12 +1,12 @@ plugins { - id "com.android.application" - id "kotlin-android" + id("com.android.application") + id("kotlin-android") // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. - id "dev.flutter.flutter-gradle-plugin" + id("dev.flutter.flutter-gradle-plugin") } android { - namespace = "dev.flutter.packages.animations.example" + namespace = "dev.flutter.packages.animations.animations_example" compileSdk = flutter.compileSdkVersion ndkVersion = flutter.ndkVersion @@ -19,12 +19,8 @@ android { jvmTarget = JavaVersion.VERSION_17.toString() } - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - defaultConfig { - applicationId = "dev.flutter.packages.animations.example" + applicationId = "dev.flutter.packages.animations.animations_example" minSdk = flutter.minSdkVersion targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode @@ -33,7 +29,7 @@ android { buildTypes { release { - signingConfig = signingConfigs.debug + signingConfig = signingConfigs.getByName("debug") } } } diff --git a/packages/animations/example/android/app/src/main/AndroidManifest.xml b/packages/animations/example/android/app/src/main/AndroidManifest.xml index 74a78b939e5e..c1af4eb74521 100644 --- a/packages/animations/example/android/app/src/main/AndroidManifest.xml +++ b/packages/animations/example/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ ("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/packages/animations/example/android/gradle.properties b/packages/animations/example/android/gradle.properties index 9b650e9517e0..fbee1d8cdafc 100644 --- a/packages/animations/example/android/gradle.properties +++ b/packages/animations/example/android/gradle.properties @@ -1,2 +1,2 @@ -org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true diff --git a/packages/animations/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/animations/example/android/gradle/wrapper/gradle-wrapper.properties index 6064aebd5424..e4ef43fb98df 100644 --- a/packages/animations/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/animations/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,5 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip - +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip diff --git a/packages/animations/example/android/settings.gradle b/packages/animations/example/android/settings.gradle deleted file mode 100644 index 852d4892b9b6..000000000000 --- a/packages/animations/example/android/settings.gradle +++ /dev/null @@ -1,28 +0,0 @@ -pluginManagement { - def flutterSdkPath = { - def properties = new Properties() - file("local.properties").withInputStream { properties.load(it) } - def flutterSdkPath = properties.getProperty("flutter.sdk") - assert flutterSdkPath != null, "flutter.sdk not set in local.properties" - return flutterSdkPath - }() - - includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") - - repositories { - google() - mavenCentral() - gradlePluginPortal() - } -} - - -// See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. -plugins { - id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "8.11.0" apply false - id "org.jetbrains.kotlin.android" version "2.2.0" apply false - id "com.google.cloud.artifactregistry.gradle-plugin" version "2.2.1" -} - -include ":app" diff --git a/packages/animations/example/android/settings.gradle.kts b/packages/animations/example/android/settings.gradle.kts new file mode 100644 index 000000000000..a0118892641d --- /dev/null +++ b/packages/animations/example/android/settings.gradle.kts @@ -0,0 +1,28 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +// See https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure for more info. +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.11.1" apply false + id("org.jetbrains.kotlin.android") version "2.2.20" apply false + id("com.google.cloud.artifactregistry.gradle-plugin") version "2.2.1" +} + +include(":app") diff --git a/script/tool/lib/src/gradle_check_command.dart b/script/tool/lib/src/gradle_check_command.dart index 9ad76a2bd8bc..499b332177cd 100644 --- a/script/tool/lib/src/gradle_check_command.dart +++ b/script/tool/lib/src/gradle_check_command.dart @@ -67,6 +67,7 @@ class GradleCheckCommand extends PackageLoopingCommand { FlutterPlatform.android, ); final File topLevelGradleFile = _getBuildGradleFile(androidDir); + final bool isKts = topLevelGradleFile.path.endsWith('.kts'); // This is tracked as a variable rather than a sequence of &&s so that all // failures are reported at once, not just the first one. @@ -81,6 +82,7 @@ class GradleCheckCommand extends PackageLoopingCommand { if (!_validateExampleTopLevelSettingsGradle( package, topLevelSettingsGradleFile, + isKts: isKts, )) { succeeded = false; } @@ -99,11 +101,24 @@ class GradleCheckCommand extends PackageLoopingCommand { } // Returns the gradle file in the given directory. - File _getBuildGradleFile(Directory dir) => dir.childFile('build.gradle'); + File _getBuildGradleFile(Directory dir) { + const buildGradleBaseName = 'build.gradle'; + const buildGradleKtsBaseName = '$buildGradleBaseName.kts'; + if (dir.childFile(buildGradleKtsBaseName).existsSync()) { + return dir.childFile(buildGradleKtsBaseName); + } + return dir.childFile(buildGradleBaseName); + } // Returns the settings gradle file in the given directory. - File _getSettingsGradleFile(Directory dir) => - dir.childFile('settings.gradle'); + File _getSettingsGradleFile(Directory dir) { + const settingsGradleBaseName = 'settings.gradle'; + const settingsGradleKtsBaseName = '$settingsGradleBaseName.kts'; + if (dir.childFile(settingsGradleKtsBaseName).existsSync()) { + return dir.childFile(settingsGradleKtsBaseName); + } + return dir.childFile(settingsGradleBaseName); + } // Returns the main/AndroidManifest.xml file for the given package. File _getMainAndroidManifest( @@ -131,25 +146,31 @@ class GradleCheckCommand extends PackageLoopingCommand { '${indentation}Validating ' '${getRelativePosixPath(gradleFile, from: package.directory)}.', ); + final bool isKts = gradleFile.path.endsWith('.kts'); final String contents = gradleFile.readAsStringSync(); final List lines = contents.split('\n'); // This is tracked as a variable rather than a sequence of &&s so that all // failures are reported at once, not just the first one. var succeeded = true; - if (!_validateNamespace(package, contents, isExample: false)) { + if (!_validateNamespace( + package, + contents, + isExample: false, + isKts: isKts, + )) { succeeded = false; } - if (!_validateCompatibilityVersions(lines)) { + if (!_validateCompatibilityVersions(lines, isKts: isKts)) { succeeded = false; } - if (!_validateKotlinJvmCompatibility(lines)) { + if (!_validateKotlinJvmCompatibility(lines, isKts: isKts)) { succeeded = false; } - if (!_validateJavaKotlinCompileOptionsAlignment(lines)) { + if (!_validateJavaKotlinCompileOptionsAlignment(lines, isKts: isKts)) { succeeded = false; } - if (!_validateGradleDrivenLintConfig(package, lines)) { + if (!_validateGradleDrivenLintConfig(lines, isKts: isKts)) { succeeded = false; } if (!_validateCompileSdkUsage(package, lines)) { @@ -166,7 +187,7 @@ class GradleCheckCommand extends PackageLoopingCommand { /// String printed as example of valid example root build.gradle repository /// configuration that enables artifact hub env variable. @visibleForTesting - static const String exampleRootGradleArtifactHubString = + static const String exampleGroovyRootGradleArtifactHubString = ''' // See $artifactHubDocumentationString for more info. def artifactRepoKey = 'ARTIFACT_HUB_REPOSITORY' @@ -176,31 +197,62 @@ class GradleCheckCommand extends PackageLoopingCommand { } '''; + /// String printed as example of valid example root build.gradle.kts repository + /// configuration that enables artifact hub env variable. + @visibleForTesting + static const String exampleKotlinRootGradleArtifactHubString = + ''' + // See $artifactHubDocumentationString for more info. + val artifactRepoKey = "ARTIFACT_HUB_REPOSITORY" + val artifactRepoUrl = System.getenv(artifactRepoKey) + if (artifactRepoUrl != null) { + println("Using artifact hub") + maven { + url = uri(artifactRepoUrl) + } + } +'''; + /// Validates that [gradleLines] reads and uses a artifiact hub repository /// when ARTIFACT_HUB_REPOSITORY is set. /// /// Required in root gradle file. bool _validateArtifactHubUsage( RepositoryPackage example, - List gradleLines, - ) { + List gradleLines, { + required bool isKts, + }) { // Gradle variable name used to hold environment variable string. const keyVariable = 'artifactRepoKey'; + const urlVariable = 'artifactRepoUrl'; final keyPresentRegex = RegExp( '$keyVariable' - r"\s+=\s+'ARTIFACT_HUB_REPOSITORY'", + r'''\s+=\s+["']ARTIFACT_HUB_REPOSITORY["']''', ); final documentationPresentRegex = RegExp( r'github\.com.*flutter.*blob.*Plugins-and-Packages-repository-structure.*gradle-structure', ); - final keyReadRegex = RegExp( - r'if.*System\.getenv.*\.containsKey.*' - '$keyVariable', - ); - final keyUsedRegex = RegExp( - r'maven.*url.*System\.getenv\(' - '$keyVariable', - ); + final keyReadRegex = isKts + ? RegExp( + '$urlVariable' + r'\s*=\s*System\.getenv\(' + '$keyVariable' + r'\)', + ) + : RegExp( + r'if.*System\.getenv.*\.containsKey.*' + '$keyVariable', + ); + final keyUsedRegex = isKts + ? RegExp( + r'url = uri\(' + '$urlVariable' + r'\)', + ) + : RegExp( + r'maven.*url.*System\.getenv\(' + '$keyVariable', + ); final bool keyPresent = gradleLines.any( (String line) => keyPresentRegex.hasMatch(line), @@ -216,9 +268,12 @@ class GradleCheckCommand extends PackageLoopingCommand { ); if (!(documentationPresent && keyPresent && keyRead && keyUsed)) { + final String exampleString = isKts + ? exampleKotlinRootGradleArtifactHubString + : exampleGroovyRootGradleArtifactHubString; printError( 'Failed Artifact Hub validation. Include the following in ' - 'example root build.gradle:\n$exampleRootGradleArtifactHubString', + 'example root build.gradle:\n$exampleString', ); } @@ -229,8 +284,9 @@ class GradleCheckCommand extends PackageLoopingCommand { /// some_package/example/android/settings.gradle). bool _validateExampleTopLevelSettingsGradle( RepositoryPackage package, - File gradleSettingsFile, - ) { + File gradleSettingsFile, { + required bool isKts, + }) { print( '${indentation}Validating ' '${getRelativePosixPath(gradleSettingsFile, from: package.directory)}.', @@ -240,7 +296,7 @@ class GradleCheckCommand extends PackageLoopingCommand { // This is tracked as a variable rather than a sequence of &&s so that all // failures are reported at once, not just the first one. var succeeded = true; - if (!_validateArtifactHubSettingsUsage(package, lines)) { + if (!_validateArtifactHubSettingsUsage(package, lines, isKts: isKts)) { succeeded = false; } return succeeded; @@ -249,7 +305,7 @@ class GradleCheckCommand extends PackageLoopingCommand { /// String printed as a valid example of settings.gradle repository /// configuration that enables artifact hub env variable. @visibleForTesting - static String exampleSettingsArtifactHubString = ''' + static const String exampleGroovySettingsArtifactHubString = ''' plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" // ...other plugins @@ -257,14 +313,26 @@ plugins { } '''; + /// String printed as a valid example of settings.gradle.kts repository + /// configuration that enables artifact hub env variable. + @visibleForTesting + static const String exampleKotlinSettingsArtifactHubString = ''' +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + // ...other plugins + id("com.google.cloud.artifactregistry.gradle-plugin") version "2.2.1" +} + '''; + /// Validates that [gradleLines] reads and uses a artifiact hub repository /// when ARTIFACT_HUB_REPOSITORY is set. /// /// Required in root gradle file. bool _validateArtifactHubSettingsUsage( RepositoryPackage example, - List gradleLines, - ) { + List gradleLines, { + required bool isKts, + }) { final documentationPresentRegex = RegExp( r'github\.com.*flutter.*blob.*Plugins-and-Packages-repository-structure.*gradle-structure', ); @@ -290,17 +358,20 @@ plugins { ); } if (!declarativeArtifactRegistryApplied) { + final String exampleString = isKts + ? exampleKotlinSettingsArtifactHubString + : exampleGroovySettingsArtifactHubString; printError( 'Include the following in ' - 'example root settings.gradle:\n$exampleSettingsArtifactHubString', + 'example root settings.gradle:\n$exampleString', ); } } return validArtifactConfiguration; } - /// Validates the top-level build.gradle for an example app (e.g., - /// some_package/example/android/build.gradle). + /// Validates the top-level build.gradle(.kts) for an example app (e.g., + /// some_package/example/android/build.gradle(.kts)). bool _validateExampleTopLevelBuildGradle( RepositoryPackage package, File gradleFile, @@ -309,6 +380,7 @@ plugins { '${indentation}Validating ' '${getRelativePosixPath(gradleFile, from: package.directory)}.', ); + final bool isKts = gradleFile.path.endsWith('.kts'); final String contents = gradleFile.readAsStringSync(); final List lines = contents.split('\n'); @@ -318,17 +390,17 @@ plugins { if (!_validateJavacLintConfig(package, lines)) { succeeded = false; } - if (!_validateKotlinVersion(package, lines)) { + if (!_validateKotlinVersion(package, lines, isKts: isKts)) { succeeded = false; } - if (!_validateArtifactHubUsage(package, lines)) { + if (!_validateArtifactHubUsage(package, lines, isKts: isKts)) { succeeded = false; } return succeeded; } - /// Validates the app-level build.gradle for an example app (e.g., - /// some_package/example/android/app/build.gradle). + /// Validates the app-level build.gradle(.kts) for an example app (e.g., + /// some_package/example/android/app/build.gradle(.kts)). bool _validateExampleAppBuildGradle( RepositoryPackage package, File gradleFile, @@ -337,12 +409,13 @@ plugins { '${indentation}Validating ' '${getRelativePosixPath(gradleFile, from: package.directory)}.', ); + final bool isKts = gradleFile.path.endsWith('.kts'); final String contents = gradleFile.readAsStringSync(); // This is tracked as a variable rather than a sequence of &&s so that all // failures are reported at once, not just the first one. var succeeded = true; - if (!_validateNamespace(package, contents, isExample: true)) { + if (!_validateNamespace(package, contents, isExample: true, isKts: isKts)) { succeeded = false; } return succeeded; @@ -354,6 +427,7 @@ plugins { RepositoryPackage package, String gradleContents, { required bool isExample, + required bool isKts, }) { // Regex to validate that the following namespace definition // is found (where the single quotes can be single or double): @@ -367,8 +441,9 @@ plugins { ); if (nameSpaceRegexMatch == null) { - const errorMessage = ''' -build.gradle must set a "namespace": + final errorMessage = + ''' +build.gradle${isKts ? '.kts' : ''} must set a "namespace": android { namespace = "dev.flutter.foo" @@ -388,6 +463,7 @@ https://developer.android.com/build/publish-library/prep-lib-release#choose-name package, isExample: isExample, namespace: nameSpaceRegexMatch.group(1)!, + isKts: isKts, ); } } @@ -401,6 +477,7 @@ https://developer.android.com/build/publish-library/prep-lib-release#choose-name RepositoryPackage package, { required bool isExample, required String namespace, + required bool isKts, }) { final manifestPackageRegex = RegExp(r'package\s*=\s*"(.*?)"'); final String manifestContents = _getMainAndroidManifest( @@ -411,10 +488,11 @@ https://developer.android.com/build/publish-library/prep-lib-release#choose-name manifestContents, ); if (packageMatch != null && namespace != packageMatch.group(1)) { + final buildGradleName = 'build.gradle${isKts ? '.kts' : ''}'; final errorMessage = ''' -build.gradle "namespace" must match the "package" attribute in AndroidManifest.xml, if one is present. - build.gradle namespace: "$namespace" +$buildGradleName "namespace" must match the "package" attribute in AndroidManifest.xml, if one is present. + $buildGradleName namespace: "$namespace" AndroidMastifest.xml package: "${packageMatch.group(1)}" '''; printError( @@ -428,7 +506,10 @@ build.gradle "namespace" must match the "package" attribute in AndroidManifest.x /// Checks for a source compatibiltiy version, so that it's explicit rather /// than using whatever the client's local toolchaing defaults to (which can /// lead to compile errors that show up for clients, but not in CI). - bool _validateCompatibilityVersions(List gradleLines) { + bool _validateCompatibilityVersions( + List gradleLines, { + required bool isKts, + }) { final bool hasLanguageVersion = gradleLines.any( (String line) => line.contains('languageVersion') && !_isCommented(line), ); @@ -449,9 +530,9 @@ build.gradle "namespace" must match the "package" attribute in AndroidManifest.x !_isCommented(line), ); if (!hasLanguageVersion && !hasCompabilityVersions) { - const javaErrorMessage = + final javaErrorMessage = ''' -build.gradle(.kts) must set an explicit Java compatibility version. +build.gradle${isKts ? '.kts' : ''} must set an explicit Java compatibility version. This can be done either via "sourceCompatibility"/"targetCompatibility": android { @@ -481,7 +562,10 @@ for more details.'''; return true; } - bool _validateKotlinJvmCompatibility(List gradleLines) { + bool _validateKotlinJvmCompatibility( + List gradleLines, { + required bool isKts, + }) { bool isKotlinOptions(String line) => line.contains('kotlinOptions') && !_isCommented(line); final bool hasKotlinOptions = gradleLines.any(isKotlinOptions); @@ -506,7 +590,7 @@ for more details.'''; } final kotlinErrorMessage = ''' -If build.gradle(.kts) sets jvmTarget then it must use JavaVersion syntax. +If build.gradle${isKts ? '.kts' : ''} sets jvmTarget then it must use JavaVersion syntax. Good: android { kotlinOptions { @@ -525,7 +609,11 @@ If build.gradle(.kts) sets jvmTarget then it must use JavaVersion syntax. return true; } - bool _validateJavaKotlinCompileOptionsAlignment(List gradleLines) { + bool _validateJavaKotlinCompileOptionsAlignment( + List gradleLines, { + required bool isKts, + }) { + final buildGradleName = 'build.gradle${isKts ? '.kts' : ''}'; final javaVersions = []; // Some java versions have the format VERSION_1_8 but we dont need to handle those // because they are below the minimum. @@ -544,8 +632,9 @@ If build.gradle(.kts) sets jvmTarget then it must use JavaVersion syntax. if (javaVersions.isNotEmpty) { final int version = int.parse(javaVersions.first); if (!javaVersions.every((String element) => element == '$version')) { - const javaVersionAlignmentError = ''' -If build.gradle(.kts) uses JavaVersion.* versions must be the same. + final javaVersionAlignmentError = + ''' +If $buildGradleName uses JavaVersion.* versions must be the same. '''; printError( '$indentation${javaVersionAlignmentError.split('\n').join('\n$indentation')}', @@ -556,7 +645,7 @@ If build.gradle(.kts) uses JavaVersion.* versions must be the same. if (version < _minimumJavaVersion) { final minimumJavaVersionError = ''' -build.gradle(.kts) uses "JavaVersion.VERSION_$version". +$buildGradleName uses "JavaVersion.VERSION_$version". Which is below the minimum required. Use at least "JavaVersion.VERSION_$_minimumJavaVersion". '''; printError( @@ -573,18 +662,14 @@ Which is below the minimum required. Use at least "JavaVersion.VERSION_$_minimum /// Gradle-driven lints (those checked by ./gradlew lint) and treat them as /// errors. bool _validateGradleDrivenLintConfig( - RepositoryPackage package, - List gradleLines, - ) { - final List gradleBuildContents = package - .platformDirectory(FlutterPlatform.android) - .childFile('build.gradle') - .readAsLinesSync(); - if (!gradleBuildContents.any( + List gradleLines, { + required bool isKts, + }) { + if (!gradleLines.any( (String line) => line.contains('checkAllWarnings = true') && !_isCommented(line), ) || - !gradleBuildContents.any( + !gradleLines.any( (String line) => line.contains('warningsAsErrors = true') && !_isCommented(line), )) { @@ -592,7 +677,7 @@ Which is below the minimum required. Use at least "JavaVersion.VERSION_$_minimum '${indentation}This package is not configured to enable all ' 'Gradle-driven lint warnings and treat them as errors. ' 'Please add the following to the lintOptions section of ' - 'android/build.gradle:', + 'android/build.gradle${isKts ? '.kts' : ''}:', ); print(''' checkAllWarnings = true @@ -748,8 +833,9 @@ gradle.projectsEvaluated { /// least a minimum value, if it is set at all. bool _validateKotlinVersion( RepositoryPackage example, - List gradleLines, - ) { + List gradleLines, { + required bool isKts, + }) { final kotlinVersionRegex = RegExp(r"ext\.kotlin_version\s*=\s*'([\d.]+)'"); RegExpMatch? match; if (gradleLines.any((String line) { @@ -759,7 +845,7 @@ gradle.projectsEvaluated { final version = Version.parse(match!.group(1)!); if (version < minKotlinVersion) { printError( - 'build.gradle sets "ext.kotlin_version" to "$version". The ' + 'build.gradle${isKts ? '.kts' : ''} sets "ext.kotlin_version" to "$version". The ' 'minimum Kotlin version that can be specified is ' '$minKotlinVersion, for compatibility with modern dependencies.', ); diff --git a/script/tool/test/gradle_check_command_test.dart b/script/tool/test/gradle_check_command_test.dart index dfb386fe6d45..7821995c0519 100644 --- a/script/tool/test/gradle_check_command_test.dart +++ b/script/tool/test/gradle_check_command_test.dart @@ -17,8 +17,8 @@ const String _defaultFakeNamespace = 'dev.flutter.foo'; void main() { late CommandRunner runner; late Directory packagesDir; - const javaIncompatabilityIndicator = - 'build.gradle(.kts) must set an explicit Java compatibility version.'; + const groovyJavaIncompatabilityIndicator = + 'build.gradle must set an explicit Java compatibility version.'; setUp(() { final GitDir gitDir; @@ -35,7 +35,7 @@ void main() { /// Writes a fake android/build.gradle file for plugin [package] with the /// given options. - void writeFakePluginBuildGradle( + void writeFakeGroovyPluginBuildGradle( RepositoryPackage package, { bool includeLanguageVersion = false, bool includeSourceCompat = false, @@ -130,7 +130,9 @@ dependencies { /// Writes a fake android/build.gradle file for an example [package] with the /// given options. - void writeFakeExampleTopLevelBuildGradle( + // TODO(stuartmorgan): Once all packages are migrated to Kotlin, remove all + // the Groovy variants, and switch all tests to the Kotlin variants. + void writeFakeGroovyExampleTopLevelBuildGradle( RepositoryPackage package, { required String pluginName, required bool warningsConfigured, @@ -156,7 +158,7 @@ gradle.projectsEvaluated { buildscript { ${kotlinVersion == null ? '' : "ext.kotlin_version = '$kotlinVersion'"} repositories { - ${includeArtifactHub ? GradleCheckCommand.exampleRootGradleArtifactHubString : ''} + ${includeArtifactHub ? GradleCheckCommand.exampleGroovyRootGradleArtifactHubString : ''} google() mavenCentral() } @@ -189,9 +191,60 @@ ${warningsConfigured ? warningConfig : ''} '''); } + /// Writes a fake android/build.gradle.kts file for an example [package] with + /// the given options. + void writeFakeKotlinExampleTopLevelBuildGradle( + RepositoryPackage package, { + required String pluginName, + bool warningsConfigured = true, + bool includeArtifactHub = true, + }) { + final File buildGradle = package + .platformDirectory(FlutterPlatform.android) + .childFile('build.gradle.kts'); + buildGradle.createSync(recursive: true); + + // TODO(stuartmorgan): Add support for warningsConfigured when we start + // migrating plugins to Kotlin Gradle. See Groovy variant above. For now, + // throw if any test requires warnings. + if (warningsConfigured) { + throw UnimplementedError( + 'warningsConfigured is not yet supported for Kotlin Gradle', + ); + } + + buildGradle.writeAsStringSync(''' +allprojects { + repositories { + ${includeArtifactHub ? GradleCheckCommand.exampleKotlinRootGradleArtifactHubString : ''} + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} +'''); + } + /// Writes a fake android/build.gradle file for an example [package] with the /// given options. - void writeFakeExampleSettingsGradle( + void writeFakeGroovyExampleSettingsGradle( RepositoryPackage package, { bool includeArtifactHub = true, bool includeArtifactDocumentation = true, @@ -230,14 +283,61 @@ pluginManagement { } ${includeArtifactDocumentation ? '// See ${GradleCheckCommand.artifactHubDocumentationString} for more info.' : ''} -${includeArtifactHub ? GradleCheckCommand.exampleSettingsArtifactHubString : exampleSettingsWithoutArtifactHubString} +${includeArtifactHub ? GradleCheckCommand.exampleGroovySettingsArtifactHubString : exampleSettingsWithoutArtifactHubString} +include ":app" +'''); + } + + /// Writes a fake android/build.gradle file for an example [package] with the + /// given options. + void writeFakeKotlinExampleSettingsGradle( + RepositoryPackage package, { + bool includeArtifactHub = true, + bool includeArtifactDocumentation = true, + }) { + final File settingsGradle = package + .platformDirectory(FlutterPlatform.android) + .childFile('settings.gradle.kts'); + settingsGradle.createSync(recursive: true); + + /// String printed as a valid example of settings.gradle repository + /// configuration without the artifact hub env variable. + const exampleSettingsWithoutArtifactHubString = ''' +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + // ...other plugins +} + '''; + + settingsGradle.writeAsStringSync(''' +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("\$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +${includeArtifactDocumentation ? '// See ${GradleCheckCommand.artifactHubDocumentationString} for more info.' : ''} +${includeArtifactHub ? GradleCheckCommand.exampleKotlinSettingsArtifactHubString : exampleSettingsWithoutArtifactHubString} include ":app" '''); } /// Writes a fake android/app/build.gradle file for an example [package] with /// the given options. - void writeFakeExampleAppBuildGradle( + void writeFakeGroovyExampleAppBuildGradle( RepositoryPackage package, { required bool includeNamespace, required bool commentNamespace, @@ -286,7 +386,65 @@ dependencies { '''); } - void writeFakeExampleBuildGradles( + /// Writes a fake android/app/build.gradle file for an example [package] with + /// the given options. + void writeFakeKotlinExampleAppBuildGradle( + RepositoryPackage package, { + required bool includeNamespace, + required bool commentNamespace, + }) { + final File buildGradle = package + .platformDirectory(FlutterPlatform.android) + .childDirectory('app') + .childFile('build.gradle.kts'); + buildGradle.createSync(recursive: true); + + final namespace = + '${commentNamespace ? '// ' : ''}namespace = "$_defaultFakeNamespace"'; + buildGradle.writeAsStringSync(''' +plugins { + id("com.android.application") + id("kotlin-android") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + ${includeNamespace ? namespace : ''} + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } + + defaultConfig { + applicationId = "$_defaultFakeNamespace.example" + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + signingConfig = signingConfigs.getByName("debug") + } + } +} + +flutter { + source = "../.." +} +'''); + } + + void writeFakeGroovyExampleBuildGradles( RepositoryPackage package, { required String pluginName, bool includeNamespace = true, @@ -299,21 +457,50 @@ dependencies { bool includeSettingsDocumentationArtifactHub = true, bool usePropertyAssignment = true, }) { - writeFakeExampleTopLevelBuildGradle( + writeFakeGroovyExampleTopLevelBuildGradle( package, pluginName: pluginName, warningsConfigured: warningsConfigured, kotlinVersion: kotlinVersion, includeArtifactHub: includeBuildArtifactHub, ); - writeFakeExampleAppBuildGradle( + writeFakeGroovyExampleAppBuildGradle( package, includeNamespace: includeNamespace, commentNamespace: commentNamespace, includeNameSpaceAsDeclaration: includeNameSpaceAsDeclaration, usePropertyAssignment: usePropertyAssignment, ); - writeFakeExampleSettingsGradle( + writeFakeGroovyExampleSettingsGradle( + package, + includeArtifactHub: includeSettingsArtifactHub, + includeArtifactDocumentation: includeSettingsDocumentationArtifactHub, + ); + } + + void writeFakeKotlinExampleBuildGradles( + RepositoryPackage package, { + required String pluginName, + bool includeNamespace = true, + bool commentNamespace = false, + // TODO(stuartmorgan): Default to true once the test helper supports it. + bool warningsConfigured = false, + bool includeBuildArtifactHub = true, + bool includeSettingsArtifactHub = true, + bool includeSettingsDocumentationArtifactHub = true, + }) { + writeFakeKotlinExampleTopLevelBuildGradle( + package, + pluginName: pluginName, + warningsConfigured: warningsConfigured, + includeArtifactHub: includeBuildArtifactHub, + ); + writeFakeKotlinExampleAppBuildGradle( + package, + includeNamespace: includeNamespace, + commentNamespace: commentNamespace, + ); + writeFakeKotlinExampleSettingsGradle( package, includeArtifactHub: includeSettingsArtifactHub, includeArtifactDocumentation: includeSettingsDocumentationArtifactHub, @@ -361,7 +548,7 @@ dependencies { packagesDir, examples: [], ); - writeFakePluginBuildGradle(package); + writeFakeGroovyPluginBuildGradle(package); writeFakeManifest(package); Error? commandError; @@ -376,7 +563,9 @@ dependencies { expect(commandError, isA()); expect( output, - containsAllInOrder([contains(javaIncompatabilityIndicator)]), + containsAllInOrder([ + contains(groovyJavaIncompatabilityIndicator), + ]), ); }); @@ -388,7 +577,7 @@ dependencies { packagesDir, examples: [], ); - writeFakePluginBuildGradle(package, includeSourceCompat: true); + writeFakeGroovyPluginBuildGradle(package, includeSourceCompat: true); writeFakeManifest(package); Error? commandError; @@ -403,7 +592,9 @@ dependencies { expect(commandError, isA()); expect( output, - containsAllInOrder([contains(javaIncompatabilityIndicator)]), + containsAllInOrder([ + contains(groovyJavaIncompatabilityIndicator), + ]), ); }, ); @@ -416,7 +607,7 @@ dependencies { packagesDir, examples: [], ); - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( package, includeSourceCompat: true, includeTargetCompat: true, @@ -452,7 +643,7 @@ dependencies { packagesDir, examples: [], ); - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( package, includeSourceCompat: true, includeTargetCompat: true, @@ -476,7 +667,7 @@ dependencies { output, containsAllInOrder([ contains( - 'If build.gradle(.kts) uses JavaVersion.* versions must be the same.', + 'If build.gradle uses JavaVersion.* versions must be the same.', ), ]), ); @@ -488,7 +679,7 @@ dependencies { packagesDir, examples: [], ); - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( package, includeSourceCompat: true, includeTargetCompat: true, @@ -517,7 +708,7 @@ dependencies { packagesDir, examples: [], ); - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( package, includeSourceCompat: true, includeTargetCompat: true, @@ -543,7 +734,7 @@ dependencies { packagesDir, examples: [], ); - writeFakePluginBuildGradle(package, includeLanguageVersion: true); + writeFakeGroovyPluginBuildGradle(package, includeLanguageVersion: true); writeFakeManifest(package); final List output = await runCapturingPrint(runner, [ @@ -558,13 +749,13 @@ dependencies { ); }); - test('does not require java version in examples', () async { + test('does not require java version in examples - groovy', () async { const pluginName = 'a_plugin'; final RepositoryPackage package = createFakePlugin(pluginName, packagesDir); - writeFakePluginBuildGradle(package, includeLanguageVersion: true); + writeFakeGroovyPluginBuildGradle(package, includeLanguageVersion: true); writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles(example, pluginName: pluginName); + writeFakeGroovyExampleBuildGradles(example, pluginName: pluginName); writeFakeManifest(example, isApp: true); final List output = await runCapturingPrint(runner, [ @@ -580,13 +771,35 @@ dependencies { ); }); + test('does not require java version in examples - kotlin', () async { + const pluginName = 'a_plugin'; + final RepositoryPackage package = createFakePlugin(pluginName, packagesDir); + writeFakeGroovyPluginBuildGradle(package, includeLanguageVersion: true); + writeFakeManifest(package); + final RepositoryPackage example = package.getExamples().first; + writeFakeKotlinExampleBuildGradles(example, pluginName: pluginName); + writeFakeManifest(example, isApp: true); + + final List output = await runCapturingPrint(runner, [ + 'gradle-check', + ]); + + expect( + output, + containsAllInOrder([ + contains('Validating android/build.gradle.kts'), + contains('Ran for 2 package(s)'), + ]), + ); + }); + test('fails when java compatibility version is commented out', () async { final RepositoryPackage package = createFakePlugin( 'a_plugin', packagesDir, examples: [], ); - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( package, includeSourceCompat: true, includeTargetCompat: true, @@ -606,7 +819,9 @@ dependencies { expect(commandError, isA()); expect( output, - containsAllInOrder([contains(javaIncompatabilityIndicator)]), + containsAllInOrder([ + contains(groovyJavaIncompatabilityIndicator), + ]), ); }); @@ -616,7 +831,7 @@ dependencies { packagesDir, examples: [], ); - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( package, includeLanguageVersion: true, commentSourceLanguage: true, @@ -635,7 +850,9 @@ dependencies { expect(commandError, isA()); expect( output, - containsAllInOrder([contains(javaIncompatabilityIndicator)]), + containsAllInOrder([ + contains(groovyJavaIncompatabilityIndicator), + ]), ); }); @@ -647,7 +864,7 @@ dependencies { packagesDir, examples: [], ); - writeFakePluginBuildGradle(package, includeLanguageVersion: true); + writeFakeGroovyPluginBuildGradle(package, includeLanguageVersion: true); writeFakeManifest(package, packageName: 'wrong.package.name'); Error? commandError; @@ -677,7 +894,7 @@ dependencies { packagesDir, examples: [], ); - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( package, includeLanguageVersion: true, includeNamespace: false, @@ -702,13 +919,13 @@ dependencies { ); }); - test('fails when namespace is missing from example', () async { + test('fails when namespace is missing from example - groovy', () async { const pluginName = 'a_plugin'; final RepositoryPackage package = createFakePlugin(pluginName, packagesDir); - writeFakePluginBuildGradle(package, includeLanguageVersion: true); + writeFakeGroovyPluginBuildGradle(package, includeLanguageVersion: true); writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles( + writeFakeGroovyExampleBuildGradles( example, pluginName: pluginName, includeNamespace: false, @@ -733,6 +950,37 @@ dependencies { ); }); + test('fails when namespace is missing from example - kotlin', () async { + const pluginName = 'a_plugin'; + final RepositoryPackage package = createFakePlugin(pluginName, packagesDir); + writeFakeGroovyPluginBuildGradle(package, includeLanguageVersion: true); + writeFakeManifest(package); + final RepositoryPackage example = package.getExamples().first; + writeFakeKotlinExampleBuildGradles( + example, + pluginName: pluginName, + includeNamespace: false, + ); + writeFakeManifest(example, isApp: true); + + Error? commandError; + final List output = await runCapturingPrint( + runner, + ['gradle-check'], + errorHandler: (Error e) { + commandError = e; + }, + ); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('build.gradle.kts must set a "namespace"'), + ]), + ); + }); + // TODO(stuartmorgan): Consider removing this in the future; we may at some // point decide that we have a use case of example apps having different // app IDs and namespaces. For now, it's enforced for consistency so they @@ -745,10 +993,10 @@ dependencies { pluginName, packagesDir, ); - writeFakePluginBuildGradle(package, includeLanguageVersion: true); + writeFakeGroovyPluginBuildGradle(package, includeLanguageVersion: true); writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles(example, pluginName: pluginName); + writeFakeGroovyExampleBuildGradles(example, pluginName: pluginName); writeFakeManifest( example, isApp: true, @@ -782,7 +1030,7 @@ dependencies { packagesDir, examples: [], ); - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( package, includeLanguageVersion: true, commentNamespace: true, @@ -810,10 +1058,10 @@ dependencies { test('fails when namespace is declared without "=" declaration', () async { const pluginName = 'a_plugin'; final RepositoryPackage package = createFakePlugin(pluginName, packagesDir); - writeFakePluginBuildGradle(package, includeLanguageVersion: true); + writeFakeGroovyPluginBuildGradle(package, includeLanguageVersion: true); writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles( + writeFakeGroovyExampleBuildGradles( example, pluginName: pluginName, includeNameSpaceAsDeclaration: false, @@ -846,7 +1094,7 @@ dependencies { packagesDir, examples: [], ); - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( plugin, includeLanguageVersion: true, warningsConfigured: false, @@ -886,10 +1134,10 @@ dependencies { platformAndroid: const PlatformDetails(PlatformSupport.inline), }, ); - writeFakePluginBuildGradle(plugin, includeLanguageVersion: true); + writeFakeGroovyPluginBuildGradle(plugin, includeLanguageVersion: true); writeFakeManifest(plugin); final RepositoryPackage example = plugin.getExamples().first; - writeFakeExampleBuildGradles( + writeFakeGroovyExampleBuildGradles( example, pluginName: pluginName, warningsConfigured: false, @@ -920,7 +1168,7 @@ dependencies { ); test( - 'passes if non-plugin package example javac lint-warnings-as-errors is missing', + 'passes if non-plugin package example javac lint-warnings-as-errors is missing - groovy', () async { const packageName = 'a_package'; final RepositoryPackage plugin = createFakePackage( @@ -928,7 +1176,7 @@ dependencies { packagesDir, ); final RepositoryPackage example = plugin.getExamples().first; - writeFakeExampleBuildGradles( + writeFakeGroovyExampleBuildGradles( example, pluginName: packageName, warningsConfigured: false, @@ -948,17 +1196,47 @@ dependencies { }, ); + test( + 'passes if non-plugin package example javac lint-warnings-as-errors is missing - kotlin', + () async { + const packageName = 'a_package'; + final RepositoryPackage plugin = createFakePackage( + packageName, + packagesDir, + ); + final RepositoryPackage example = plugin.getExamples().first; + writeFakeKotlinExampleBuildGradles( + example, + pluginName: packageName, + // TODO(stuartmorgan): Remove the ignore once the default flips. See + // the comment in writeFakeKotlinExampleBuildGradles. + // ignore: avoid_redundant_argument_values + warningsConfigured: false, + ); + writeFakeManifest(example, isApp: true); + + final List output = await runCapturingPrint(runner, [ + 'gradle-check', + ]); + + expect( + output, + containsAllInOrder([ + contains('Validating android/build.gradle.kts'), + ]), + ); + }, + ); + group('Artifact Hub check', () { - test('passes build.gradle artifact hub check when set', () async { + test('passes build.gradle artifact hub check when set - groovy', () async { const packageName = 'a_package'; final RepositoryPackage package = createFakePackage( 'a_package', packagesDir, ); - writeFakePluginBuildGradle(package, includeLanguageVersion: true); - writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles( + writeFakeGroovyExampleBuildGradles( example, pluginName: packageName, // ignore: avoid_redundant_argument_values @@ -982,18 +1260,49 @@ dependencies { ]), ); }); + + test('passes build.gradle artifact hub check when set - kotlin', () async { + const packageName = 'a_package'; + final RepositoryPackage package = createFakePackage( + 'a_package', + packagesDir, + ); + final RepositoryPackage example = package.getExamples().first; + writeFakeKotlinExampleBuildGradles( + example, + pluginName: packageName, + // ignore: avoid_redundant_argument_values + includeBuildArtifactHub: true, + // ignore: avoid_redundant_argument_values + includeSettingsArtifactHub: true, + // ignore: avoid_redundant_argument_values + includeSettingsDocumentationArtifactHub: true, + ); + writeFakeManifest(example, isApp: true); + + final List output = await runCapturingPrint(runner, [ + 'gradle-check', + ]); + + expect( + output, + containsAllInOrder([ + contains('Validating android/build.gradle.kts'), + contains('Validating android/settings.gradle.kts'), + ]), + ); + }); + test( - 'fails artifact hub check when build and settings sections missing', + 'fails artifact hub check when build and settings sections missing - groovy', () async { const packageName = 'a_package'; final RepositoryPackage package = createFakePackage( 'a_package', packagesDir, ); - writeFakePluginBuildGradle(package, includeLanguageVersion: true); - writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles( + writeFakeGroovyExampleBuildGradles( example, pluginName: packageName, includeBuildArtifactHub: false, @@ -1014,8 +1323,49 @@ dependencies { expect( output, containsAllInOrder([ - contains(GradleCheckCommand.exampleRootGradleArtifactHubString), - contains(GradleCheckCommand.exampleSettingsArtifactHubString), + contains( + GradleCheckCommand.exampleGroovyRootGradleArtifactHubString, + ), + contains(GradleCheckCommand.exampleGroovySettingsArtifactHubString), + ]), + ); + }, + ); + + test( + 'fails artifact hub check when build and settings sections missing - kotlin', + () async { + const packageName = 'a_package'; + final RepositoryPackage package = createFakePackage( + 'a_package', + packagesDir, + ); + final RepositoryPackage example = package.getExamples().first; + writeFakeKotlinExampleBuildGradles( + example, + pluginName: packageName, + includeBuildArtifactHub: false, + includeSettingsArtifactHub: false, + ); + writeFakeManifest(example, isApp: true); + + Error? commandError; + final List output = await runCapturingPrint( + runner, + ['gradle-check'], + errorHandler: (Error e) { + commandError = e; + }, + ); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains( + GradleCheckCommand.exampleKotlinRootGradleArtifactHubString, + ), + contains(GradleCheckCommand.exampleKotlinSettingsArtifactHubString), ]), ); }, @@ -1027,10 +1377,42 @@ dependencies { 'a_package', packagesDir, ); - writeFakePluginBuildGradle(package, includeLanguageVersion: true); - writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles( + writeFakeGroovyExampleBuildGradles( + example, + pluginName: packageName, + includeBuildArtifactHub: false, + // ignore: avoid_redundant_argument_values + includeSettingsArtifactHub: true, + ); + writeFakeManifest(example, isApp: true); + + Error? commandError; + final List output = await runCapturingPrint( + runner, + ['gradle-check'], + errorHandler: (Error e) { + commandError = e; + }, + ); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains(GradleCheckCommand.exampleGroovyRootGradleArtifactHubString), + ]), + ); + }); + + test('fails build.gradle.kts artifact hub check when missing', () async { + const packageName = 'a_package'; + final RepositoryPackage package = createFakePackage( + 'a_package', + packagesDir, + ); + final RepositoryPackage example = package.getExamples().first; + writeFakeKotlinExampleBuildGradles( example, pluginName: packageName, includeBuildArtifactHub: false, @@ -1052,7 +1434,7 @@ dependencies { expect( output, containsAllInOrder([ - contains(GradleCheckCommand.exampleRootGradleArtifactHubString), + contains(GradleCheckCommand.exampleKotlinRootGradleArtifactHubString), ]), ); }); @@ -1063,10 +1445,8 @@ dependencies { 'a_package', packagesDir, ); - writeFakePluginBuildGradle(package, includeLanguageVersion: true); - writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles( + writeFakeGroovyExampleBuildGradles( example, pluginName: packageName, // ignore: avoid_redundant_argument_values @@ -1088,12 +1468,54 @@ dependencies { expect( output, containsAllInOrder([ - contains(GradleCheckCommand.exampleSettingsArtifactHubString), + contains(GradleCheckCommand.exampleGroovySettingsArtifactHubString), ]), ); expect( output, - isNot(contains(GradleCheckCommand.exampleRootGradleArtifactHubString)), + isNot( + contains(GradleCheckCommand.exampleGroovyRootGradleArtifactHubString), + ), + ); + }); + + test('fails settings.gradle.kts artifact hub check when missing', () async { + const packageName = 'a_package'; + final RepositoryPackage package = createFakePackage( + 'a_package', + packagesDir, + ); + final RepositoryPackage example = package.getExamples().first; + writeFakeKotlinExampleBuildGradles( + example, + pluginName: packageName, + // ignore: avoid_redundant_argument_values + includeBuildArtifactHub: true, + includeSettingsArtifactHub: false, + ); + writeFakeManifest(example, isApp: true); + + Error? commandError; + final List output = await runCapturingPrint( + runner, + ['gradle-check'], + errorHandler: (Error e) { + commandError = e; + }, + ); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains(GradleCheckCommand.exampleKotlinSettingsArtifactHubString), + ]), + ); + expect( + output, + isNot( + contains(GradleCheckCommand.exampleKotlinRootGradleArtifactHubString), + ), ); }); @@ -1105,10 +1527,10 @@ dependencies { 'a_package', packagesDir, ); - writeFakePluginBuildGradle(package, includeLanguageVersion: true); + writeFakeGroovyPluginBuildGradle(package, includeLanguageVersion: true); writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles( + writeFakeGroovyExampleBuildGradles( example, pluginName: packageName, // ignore: avoid_redundant_argument_values @@ -1132,24 +1554,61 @@ dependencies { expect( output, containsAllInOrder([ - contains(GradleCheckCommand.exampleSettingsArtifactHubString), + contains(GradleCheckCommand.exampleGroovySettingsArtifactHubString), ]), ); }, ); test( - 'error message is printed when documentation link is missing', + 'error message is printed when documentation link is missing - groovy', + () async { + const packageName = 'a_package'; + final RepositoryPackage package = createFakePackage( + 'a_package', + packagesDir, + ); + final RepositoryPackage example = package.getExamples().first; + writeFakeGroovyExampleBuildGradles( + example, + pluginName: packageName, + // ignore: avoid_redundant_argument_values + includeBuildArtifactHub: true, + // ignore: avoid_redundant_argument_values + includeSettingsArtifactHub: true, + includeSettingsDocumentationArtifactHub: false, + ); + writeFakeManifest(example, isApp: true); + + Error? commandError; + final List output = await runCapturingPrint( + runner, + ['gradle-check'], + errorHandler: (Error e) { + commandError = e; + }, + ); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains(GradleCheckCommand.artifactHubDocumentationString), + ]), + ); + }, + ); + + test( + 'error message is printed when documentation link is missing - kotlin', () async { const packageName = 'a_package'; final RepositoryPackage package = createFakePackage( 'a_package', packagesDir, ); - writeFakePluginBuildGradle(package, includeLanguageVersion: true); - writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles( + writeFakeKotlinExampleBuildGradles( example, pluginName: packageName, // ignore: avoid_redundant_argument_values @@ -1187,10 +1646,10 @@ dependencies { 'a_package', packagesDir, ); - writeFakePluginBuildGradle(package, includeLanguageVersion: true); + writeFakeGroovyPluginBuildGradle(package, includeLanguageVersion: true); writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles(example, pluginName: packageName); + writeFakeGroovyExampleBuildGradles(example, pluginName: packageName); writeFakeManifest(example, isApp: true); final List output = await runCapturingPrint(runner, [ @@ -1211,10 +1670,10 @@ dependencies { 'a_package', packagesDir, ); - writeFakePluginBuildGradle(package, includeLanguageVersion: true); + writeFakeGroovyPluginBuildGradle(package, includeLanguageVersion: true); writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles( + writeFakeGroovyExampleBuildGradles( example, pluginName: packageName, kotlinVersion: minKotlinVersion.toString(), @@ -1239,10 +1698,10 @@ dependencies { 'a_package', packagesDir, ); - writeFakePluginBuildGradle(package, includeLanguageVersion: true); + writeFakeGroovyPluginBuildGradle(package, includeLanguageVersion: true); writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles( + writeFakeGroovyExampleBuildGradles( example, pluginName: packageName, kotlinVersion: '99.99.0', @@ -1267,10 +1726,10 @@ dependencies { 'a_package', packagesDir, ); - writeFakePluginBuildGradle(package, includeLanguageVersion: true); + writeFakeGroovyPluginBuildGradle(package, includeLanguageVersion: true); writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles( + writeFakeGroovyExampleBuildGradles( example, pluginName: packageName, kotlinVersion: '1.6.21', @@ -1311,14 +1770,14 @@ dependencies { isFlutter: true, ); // Current flutter.compileSdkVersion is 36. - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( package, includeLanguageVersion: true, compileSdk: '37', ); writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles(example, pluginName: packageName); + writeFakeGroovyExampleBuildGradles(example, pluginName: packageName); writeFakeManifest(example, isApp: true); final List output = await runCapturingPrint(runner, [ @@ -1344,14 +1803,14 @@ dependencies { isFlutter: true, flutterConstraint: '>=3.27.0', ); - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( package, includeLanguageVersion: true, compileSdk: 'flutter.compileSdkVersion', ); writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles(example, pluginName: packageName); + writeFakeGroovyExampleBuildGradles(example, pluginName: packageName); writeFakeManifest(example, isApp: true); final List output = await runCapturingPrint(runner, [ @@ -1379,14 +1838,14 @@ dependencies { // Current flutter.compileSdkVersion is 36. const minCompileSdkVersion = '36'; const testCompileSdkVersion = '35'; - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( package, includeLanguageVersion: true, compileSdk: testCompileSdkVersion, ); writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles(example, pluginName: packageName); + writeFakeGroovyExampleBuildGradles(example, pluginName: packageName); writeFakeManifest(example, isApp: true); Error? commandError; @@ -1421,14 +1880,14 @@ dependencies { isFlutter: true, flutterConstraint: '>=3.24.0', ); - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( package, includeLanguageVersion: true, compileSdk: 'flutter.compileSdkVersion', ); writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles(example, pluginName: packageName); + writeFakeGroovyExampleBuildGradles(example, pluginName: packageName); writeFakeManifest(example, isApp: true); Error? commandError; @@ -1461,14 +1920,14 @@ dependencies { packagesDir, isFlutter: true, ); - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( package, includeLanguageVersion: true, useDeprecatedCompileSdkVersion: true, ); writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles(example, pluginName: packageName); + writeFakeGroovyExampleBuildGradles(example, pluginName: packageName); writeFakeManifest(example, isApp: true); Error? commandError; @@ -1499,14 +1958,14 @@ dependencies { packagesDir, isFlutter: true, ); - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( package, includeLanguageVersion: true, usePropertyAssignment: false, ); writeFakeManifest(package); final RepositoryPackage example = package.getExamples().first; - writeFakeExampleBuildGradles( + writeFakeGroovyExampleBuildGradles( example, pluginName: packageName, usePropertyAssignment: false, @@ -1539,7 +1998,7 @@ dependencies { packagesDir, examples: [], ); - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( package, includeLanguageVersion: true, // ignore: avoid_redundant_argument_values ensure codepath is tested if defaults change. @@ -1565,7 +2024,7 @@ dependencies { packagesDir, examples: [], ); - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( package, includeLanguageVersion: true, includeKotlinOptions: false, @@ -1590,7 +2049,7 @@ dependencies { packagesDir, examples: [], ); - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( package, includeLanguageVersion: true, commentKotlinOptions: true, @@ -1615,7 +2074,7 @@ dependencies { packagesDir, examples: [], ); - writeFakePluginBuildGradle( + writeFakeGroovyPluginBuildGradle( package, includeLanguageVersion: true, useDeprecatedJvmTargetStyle: true, @@ -1636,7 +2095,7 @@ dependencies { output, containsAllInOrder([ contains( - 'build.gradle(.kts) sets jvmTarget then it must use JavaVersion syntax', + 'build.gradle sets jvmTarget then it must use JavaVersion syntax', ), ]), );