mirror of
https://github.com/flutter/packages.git
synced 2025-08-06 17:28:42 +08:00

Updated applying gradle plugins from usage of imperative apply to usage of declarative blocks {} apply. Intending on updating all android example apps under packages. Did one more as a proof of concept before doing more. More information on Flutter Gradle Plugin Apply [here](https://docs.flutter.dev/release/breaking-changes/flutter-gradle-plugin-apply) Partially addresses [#152656](https://github.com/flutter/flutter/issues/152656) ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] page, which explains my responsibilities. - [x] I read and followed the [relevant style guides] and ran the auto-formatter. (Unlike the flutter/flutter repo, the flutter/packages repo does use `dart format`.) - [x] I signed the [CLA]. - [x] The title of the PR starts with the name of the package surrounded by square brackets, e.g. `[shared_preferences]` - [x] I [linked to at least one issue that this PR fixes] in the description above. - [x] I updated `pubspec.yaml` with an appropriate new version according to the [pub versioning philosophy], or this PR is [exempt from version changes]. - [x] I updated `CHANGELOG.md` to add a description of the change, [following repository CHANGELOG style], or this PR is [exempt from CHANGELOG changes]. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/packages/blob/main/CONTRIBUTING.md [Tree Hygiene]: https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md [relevant style guides]: https://github.com/flutter/packages/blob/main/CONTRIBUTING.md#style [CLA]: https://cla.developers.google.com/ [Discord]: https://github.com/flutter/flutter/blob/master/docs/contributing/Chat.md [linked to at least one issue that this PR fixes]: https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#overview [pub versioning philosophy]: https://dart.dev/tools/pub/versioning [exempt from version changes]: https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#version [following repository CHANGELOG style]: https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changelog-style [exempt from CHANGELOG changes]: https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changelog [test-exempt]: https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#tests --------- Co-authored-by: Reid Baker <reidbaker@google.com>
521 lines
20 KiB
Dart
521 lines
20 KiB
Dart
// Copyright 2013 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'package:file/file.dart';
|
|
import 'package:meta/meta.dart';
|
|
import 'package:pub_semver/pub_semver.dart';
|
|
|
|
import 'common/core.dart';
|
|
import 'common/output_utils.dart';
|
|
import 'common/package_looping_command.dart';
|
|
import 'common/plugin_utils.dart';
|
|
import 'common/repository_package.dart';
|
|
|
|
/// The lowest `ext.kotlin_version` that example apps are allowed to use.
|
|
@visibleForTesting
|
|
final Version minKotlinVersion = Version(1, 7, 10);
|
|
|
|
/// A command to enforce gradle file conventions and best practices.
|
|
class GradleCheckCommand extends PackageLoopingCommand {
|
|
/// Creates an instance of the gradle check command.
|
|
GradleCheckCommand(super.packagesDir);
|
|
|
|
@override
|
|
final String name = 'gradle-check';
|
|
|
|
@override
|
|
List<String> get aliases => <String>['check-gradle'];
|
|
|
|
@override
|
|
final String description =
|
|
'Checks that gradle files follow repository conventions.';
|
|
|
|
@override
|
|
bool get hasLongOutput => false;
|
|
|
|
@override
|
|
PackageLoopingType get packageLoopingType =>
|
|
PackageLoopingType.includeAllSubpackages;
|
|
|
|
@override
|
|
Future<PackageResult> runForPackage(RepositoryPackage package) async {
|
|
if (!package.platformDirectory(FlutterPlatform.android).existsSync()) {
|
|
return PackageResult.skip('No android/ directory.');
|
|
}
|
|
|
|
const String exampleDirName = 'example';
|
|
final bool isExample = package.directory.basename == exampleDirName ||
|
|
package.directory.parent.basename == exampleDirName;
|
|
if (!_validateBuildGradles(package, isExample: isExample)) {
|
|
return PackageResult.fail();
|
|
}
|
|
return PackageResult.success();
|
|
}
|
|
|
|
bool _validateBuildGradles(RepositoryPackage package,
|
|
{required bool isExample}) {
|
|
final Directory androidDir =
|
|
package.platformDirectory(FlutterPlatform.android);
|
|
final File topLevelGradleFile = _getBuildGradleFile(androidDir);
|
|
|
|
// 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.
|
|
bool succeeded = true;
|
|
if (isExample) {
|
|
if (!_validateExampleTopLevelBuildGradle(package, topLevelGradleFile)) {
|
|
succeeded = false;
|
|
}
|
|
final File topLevelSettingsGradleFile =
|
|
_getSettingsGradleFile(androidDir);
|
|
if (!_validateExampleTopLevelSettingsGradle(
|
|
package, topLevelSettingsGradleFile)) {
|
|
succeeded = false;
|
|
}
|
|
|
|
final File appGradleFile =
|
|
_getBuildGradleFile(androidDir.childDirectory('app'));
|
|
if (!_validateExampleAppBuildGradle(package, appGradleFile)) {
|
|
succeeded = false;
|
|
}
|
|
} else {
|
|
succeeded = _validatePluginBuildGradle(package, topLevelGradleFile);
|
|
}
|
|
|
|
return succeeded;
|
|
}
|
|
|
|
// Returns the gradle file in the given directory.
|
|
File _getBuildGradleFile(Directory dir) => dir.childFile('build.gradle');
|
|
|
|
// Returns the settings gradle file in the given directory.
|
|
File _getSettingsGradleFile(Directory dir) =>
|
|
dir.childFile('settings.gradle');
|
|
|
|
// Returns the main/AndroidManifest.xml file for the given package.
|
|
File _getMainAndroidManifest(RepositoryPackage package,
|
|
{required bool isExample}) {
|
|
final Directory androidDir =
|
|
package.platformDirectory(FlutterPlatform.android);
|
|
final Directory baseDir =
|
|
isExample ? androidDir.childDirectory('app') : androidDir;
|
|
return baseDir
|
|
.childDirectory('src')
|
|
.childDirectory('main')
|
|
.childFile('AndroidManifest.xml');
|
|
}
|
|
|
|
bool _isCommented(String line) => line.trim().startsWith('//');
|
|
|
|
/// Validates the build.gradle file for a plugin
|
|
/// (some_plugin/android/build.gradle).
|
|
bool _validatePluginBuildGradle(RepositoryPackage package, File gradleFile) {
|
|
print('${indentation}Validating '
|
|
'${getRelativePosixPath(gradleFile, from: package.directory)}.');
|
|
final String contents = gradleFile.readAsStringSync();
|
|
final List<String> 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.
|
|
bool succeeded = true;
|
|
if (!_validateNamespace(package, contents, isExample: false)) {
|
|
succeeded = false;
|
|
}
|
|
if (!_validateCompatibilityVersions(lines)) {
|
|
succeeded = false;
|
|
}
|
|
if (!_validateGradleDrivenLintConfig(package, lines)) {
|
|
succeeded = false;
|
|
}
|
|
return succeeded;
|
|
}
|
|
|
|
/// Documentation url for Artifact hub implementation in flutter repo's.
|
|
@visibleForTesting
|
|
static const String artifactHubDocumentationString =
|
|
r'https://github.com/flutter/flutter/blob/master/docs/ecosystem/Plugins-and-Packages-repository-structure.md#gradle-structure';
|
|
|
|
/// String printed as example of valid example root build.gradle repository
|
|
/// configuration that enables artifact hub env variable.
|
|
@visibleForTesting
|
|
static const String exampleRootGradleArtifactHubString = '''
|
|
// See $artifactHubDocumentationString for more info.
|
|
def artifactRepoKey = 'ARTIFACT_HUB_REPOSITORY'
|
|
if (System.getenv().containsKey(artifactRepoKey)) {
|
|
println "Using artifact hub"
|
|
maven { url System.getenv(artifactRepoKey) }
|
|
}
|
|
''';
|
|
|
|
/// 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<String> gradleLines) {
|
|
// Gradle variable name used to hold environment variable string.
|
|
const String keyVariable = 'artifactRepoKey';
|
|
final RegExp keyPresentRegex =
|
|
RegExp('$keyVariable' r"\s+=\s+'ARTIFACT_HUB_REPOSITORY'");
|
|
final RegExp documentationPresentRegex = RegExp(
|
|
r'github\.com.*flutter.*blob.*Plugins-and-Packages-repository-structure.*gradle-structure');
|
|
final RegExp keyReadRegex =
|
|
RegExp(r'if.*System\.getenv.*\.containsKey.*' '$keyVariable');
|
|
final RegExp keyUsedRegex =
|
|
RegExp(r'maven.*url.*System\.getenv\(' '$keyVariable');
|
|
|
|
final bool keyPresent =
|
|
gradleLines.any((String line) => keyPresentRegex.hasMatch(line));
|
|
final bool documentationPresent = gradleLines
|
|
.any((String line) => documentationPresentRegex.hasMatch(line));
|
|
final bool keyRead =
|
|
gradleLines.any((String line) => keyReadRegex.hasMatch(line));
|
|
final bool keyUsed =
|
|
gradleLines.any((String line) => keyUsedRegex.hasMatch(line));
|
|
|
|
if (!(documentationPresent && keyPresent && keyRead && keyUsed)) {
|
|
printError('Failed Artifact Hub validation. Include the following in '
|
|
'example root build.gradle:\n$exampleRootGradleArtifactHubString');
|
|
}
|
|
|
|
return keyPresent && documentationPresent && keyRead && keyUsed;
|
|
}
|
|
|
|
/// Validates the top-level settings.gradle for an example app (e.g.,
|
|
/// some_package/example/android/settings.gradle).
|
|
bool _validateExampleTopLevelSettingsGradle(
|
|
RepositoryPackage package, File gradleSettingsFile) {
|
|
print('${indentation}Validating '
|
|
'${getRelativePosixPath(gradleSettingsFile, from: package.directory)}.');
|
|
final String contents = gradleSettingsFile.readAsStringSync();
|
|
final List<String> 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.
|
|
bool succeeded = true;
|
|
if (!_validateArtifactHubSettingsUsage(package, lines)) {
|
|
succeeded = false;
|
|
}
|
|
return succeeded;
|
|
}
|
|
|
|
/// String printed as example of valid example root settings.gradle repository
|
|
/// configuration that enables artifact hub env variable.
|
|
@visibleForTesting
|
|
static String exampleRootSettingsArtifactHubString = '''
|
|
buildscript {
|
|
repositories {
|
|
maven {
|
|
url "https://plugins.gradle.org/m2/"
|
|
}
|
|
}
|
|
dependencies {
|
|
classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.1"
|
|
}
|
|
}
|
|
apply plugin: "com.google.cloud.artifactregistry.gradle-plugin"
|
|
''';
|
|
|
|
/// String printed as a valid example of settings.gradle repository
|
|
/// configuration that enables artifact hub env variable.
|
|
/// GP stands for the gradle plugin method of flutter tooling inclusion.
|
|
@visibleForTesting
|
|
static String exampleSettingsArtifactHubStringGP = '''
|
|
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<String> gradleLines) {
|
|
final RegExp documentationPresentRegex = RegExp(
|
|
r'github\.com.*flutter.*blob.*Plugins-and-Packages-repository-structure.*gradle-structure');
|
|
final RegExp artifactRegistryDefinitionRegex = RegExp(
|
|
r'classpath.*gradle\.plugin\.com\.google\.cloud\.artifactregistry:artifactregistry-gradle-plugin');
|
|
final RegExp artifactRegistryPluginApplyRegex = RegExp(
|
|
r'apply.*plugin.*com\.google\.cloud\.artifactregistry\.gradle-plugin');
|
|
final RegExp artifactRegistryPluginApplyRegexGP = RegExp(
|
|
r'id.*com\.google\.cloud\.artifactregistry\.gradle-plugin.*version.*\b\d+\.\d+\.\d+\b');
|
|
final RegExp artifactRegistryPluginApplyDeclarativeRegex =
|
|
RegExp(r'\bpluginManagement\b');
|
|
|
|
final bool documentationPresent = gradleLines
|
|
.any((String line) => documentationPresentRegex.hasMatch(line));
|
|
final bool artifactRegistryDefined = gradleLines
|
|
.any((String line) => artifactRegistryDefinitionRegex.hasMatch(line));
|
|
final bool artifactRegistryPluginApplied = gradleLines
|
|
.any((String line) => artifactRegistryPluginApplyRegex.hasMatch(line));
|
|
final bool declarativeArtifactRegistryApplied = gradleLines.any(
|
|
(String line) => artifactRegistryPluginApplyRegexGP.hasMatch(line));
|
|
final bool declarativePluginBlockApplied = gradleLines.any((String line) =>
|
|
artifactRegistryPluginApplyDeclarativeRegex.hasMatch(line));
|
|
|
|
final bool imperativeArtifactRegistryApplied =
|
|
artifactRegistryDefined && artifactRegistryPluginApplied;
|
|
|
|
final bool validArtifactConfiguration = documentationPresent &&
|
|
(imperativeArtifactRegistryApplied ||
|
|
declarativeArtifactRegistryApplied);
|
|
|
|
if (!validArtifactConfiguration) {
|
|
printError('Failed Artifact Hub validation.');
|
|
if (!documentationPresent) {
|
|
printError(
|
|
'The link to the Artifact Hub documentation is missing. Include the following in '
|
|
'example root settings.gradle:\n// See $artifactHubDocumentationString for more info.');
|
|
}
|
|
if (artifactRegistryDefined ||
|
|
artifactRegistryPluginApplied ||
|
|
!declarativePluginBlockApplied) {
|
|
printError('Include the following in '
|
|
'example root settings.gradle:\n$exampleRootSettingsArtifactHubString');
|
|
} else if (!declarativeArtifactRegistryApplied) {
|
|
printError('Include the following in '
|
|
'example root settings.gradle:\n$exampleSettingsArtifactHubStringGP');
|
|
}
|
|
}
|
|
return validArtifactConfiguration;
|
|
}
|
|
|
|
/// Validates the top-level build.gradle for an example app (e.g.,
|
|
/// some_package/example/android/build.gradle).
|
|
bool _validateExampleTopLevelBuildGradle(
|
|
RepositoryPackage package, File gradleFile) {
|
|
print('${indentation}Validating '
|
|
'${getRelativePosixPath(gradleFile, from: package.directory)}.');
|
|
final String contents = gradleFile.readAsStringSync();
|
|
final List<String> 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.
|
|
bool succeeded = true;
|
|
if (!_validateJavacLintConfig(package, lines)) {
|
|
succeeded = false;
|
|
}
|
|
if (!_validateKotlinVersion(package, lines)) {
|
|
succeeded = false;
|
|
}
|
|
if (!_validateArtifactHubUsage(package, lines)) {
|
|
succeeded = false;
|
|
}
|
|
return succeeded;
|
|
}
|
|
|
|
/// Validates the app-level build.gradle for an example app (e.g.,
|
|
/// some_package/example/android/app/build.gradle).
|
|
bool _validateExampleAppBuildGradle(
|
|
RepositoryPackage package, File gradleFile) {
|
|
print('${indentation}Validating '
|
|
'${getRelativePosixPath(gradleFile, from: package.directory)}.');
|
|
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.
|
|
bool succeeded = true;
|
|
if (!_validateNamespace(package, contents, isExample: true)) {
|
|
succeeded = false;
|
|
}
|
|
return succeeded;
|
|
}
|
|
|
|
/// Validates that [gradleContents] sets a namespace, which is required for
|
|
/// compatibility with apps that use AGP 8+.
|
|
bool _validateNamespace(RepositoryPackage package, String gradleContents,
|
|
{required bool isExample}) {
|
|
final RegExp namespaceRegex =
|
|
RegExp('^\\s*namespace\\s+[\'"](.*?)[\'"]', multiLine: true);
|
|
final RegExpMatch? namespaceMatch =
|
|
namespaceRegex.firstMatch(gradleContents);
|
|
|
|
if (namespaceMatch == null) {
|
|
const String errorMessage = '''
|
|
build.gradle must set a "namespace":
|
|
|
|
android {
|
|
namespace 'dev.flutter.foo'
|
|
}
|
|
|
|
The value must match the "package" attribute in AndroidManifest.xml, if one is
|
|
present. For more information, see:
|
|
https://developer.android.com/build/publish-library/prep-lib-release#choose-namespace
|
|
''';
|
|
|
|
printError(
|
|
'$indentation${errorMessage.split('\n').join('\n$indentation')}');
|
|
return false;
|
|
} else {
|
|
return _validateNamespaceMatchesManifest(package,
|
|
isExample: isExample, namespace: namespaceMatch.group(1)!);
|
|
}
|
|
}
|
|
|
|
/// Validates that the given namespace matches the manifest package of
|
|
/// [package] (if any; a package does not need to be in the manifest in cases
|
|
/// where compatibility with AGP <7 is no longer required).
|
|
///
|
|
/// Prints an error and returns false if validation fails.
|
|
bool _validateNamespaceMatchesManifest(RepositoryPackage package,
|
|
{required bool isExample, required String namespace}) {
|
|
final RegExp manifestPackageRegex = RegExp(r'package\s*=\s*"(.*?)"');
|
|
final String manifestContents =
|
|
_getMainAndroidManifest(package, isExample: isExample)
|
|
.readAsStringSync();
|
|
final RegExpMatch? packageMatch =
|
|
manifestPackageRegex.firstMatch(manifestContents);
|
|
if (packageMatch != null && namespace != packageMatch.group(1)) {
|
|
final String errorMessage = '''
|
|
build.gradle "namespace" must match the "package" attribute in AndroidManifest.xml, if one is present.
|
|
build.gradle namespace: "$namespace"
|
|
AndroidMastifest.xml package: "${packageMatch.group(1)}"
|
|
''';
|
|
printError(
|
|
'$indentation${errorMessage.split('\n').join('\n$indentation')}');
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// 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<String> gradleLines) {
|
|
final bool hasLanguageVersion = gradleLines.any((String line) =>
|
|
line.contains('languageVersion') && !_isCommented(line));
|
|
final bool hasCompabilityVersions = gradleLines.any((String line) =>
|
|
line.contains('sourceCompatibility') && !_isCommented(line)) &&
|
|
// Newer toolchains default targetCompatibility to the same value as
|
|
// sourceCompatibility, but older toolchains require it to be set
|
|
// explicitly. The exact version cutoff (and of which piece of the
|
|
// toolchain; likely AGP) is unknown; for context see
|
|
// https://github.com/flutter/flutter/issues/125482
|
|
gradleLines.any((String line) =>
|
|
line.contains('targetCompatibility') && !_isCommented(line));
|
|
if (!hasLanguageVersion && !hasCompabilityVersions) {
|
|
const String errorMessage = '''
|
|
build.gradle must set an explicit Java compatibility version.
|
|
|
|
This can be done either via "sourceCompatibility"/"targetCompatibility":
|
|
android {
|
|
compileOptions {
|
|
sourceCompatibility JavaVersion.VERSION_11
|
|
targetCompatibility JavaVersion.VERSION_11
|
|
}
|
|
}
|
|
|
|
or "toolchain":
|
|
java {
|
|
toolchain {
|
|
languageVersion = JavaLanguageVersion.of(11)
|
|
}
|
|
}
|
|
|
|
See:
|
|
https://docs.gradle.org/current/userguide/java_plugin.html#toolchain_and_compatibility
|
|
for more details.''';
|
|
|
|
printError(
|
|
'$indentation${errorMessage.split('\n').join('\n$indentation')}');
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Returns whether the given gradle content is configured to enable all
|
|
/// Gradle-driven lints (those checked by ./gradlew lint) and treat them as
|
|
/// errors.
|
|
bool _validateGradleDrivenLintConfig(
|
|
RepositoryPackage package, List<String> gradleLines) {
|
|
final List<String> gradleBuildContents = package
|
|
.platformDirectory(FlutterPlatform.android)
|
|
.childFile('build.gradle')
|
|
.readAsLinesSync();
|
|
if (!gradleBuildContents.any((String line) =>
|
|
line.contains('checkAllWarnings true') && !_isCommented(line)) ||
|
|
!gradleBuildContents.any((String line) =>
|
|
line.contains('warningsAsErrors true') && !_isCommented(line))) {
|
|
printError('${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:');
|
|
print('''
|
|
checkAllWarnings true
|
|
warningsAsErrors true
|
|
''');
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Validates whether the given [example]'s gradle content is configured to
|
|
/// build its plugin target with javac lints enabled and treated as errors,
|
|
/// if the enclosing package is a plugin.
|
|
///
|
|
/// This can only be called on example packages. (Plugin packages should not
|
|
/// be configured this way, since it would affect clients.)
|
|
///
|
|
/// If [example]'s enclosing package is not a plugin package, this just
|
|
/// returns true.
|
|
bool _validateJavacLintConfig(
|
|
RepositoryPackage example, List<String> gradleLines) {
|
|
final RepositoryPackage enclosingPackage = example.getEnclosingPackage()!;
|
|
if (!pluginSupportsPlatform(platformAndroid, enclosingPackage,
|
|
requiredMode: PlatformSupport.inline)) {
|
|
return true;
|
|
}
|
|
final String enclosingPackageName = enclosingPackage.directory.basename;
|
|
|
|
// The check here is intentionally somewhat loose, to allow for the
|
|
// possibility of variations (e.g., not using Xlint:all in some cases, or
|
|
// passing other arguments).
|
|
if (!(gradleLines.any((String line) =>
|
|
line.contains('project(":$enclosingPackageName")')) &&
|
|
gradleLines.any((String line) =>
|
|
line.contains('options.compilerArgs') &&
|
|
line.contains('-Xlint') &&
|
|
line.contains('-Werror')))) {
|
|
printError('The example '
|
|
'"${getRelativePosixPath(example.directory, from: enclosingPackage.directory)}" '
|
|
'is not configured to treat javac lints and warnings as errors. '
|
|
'Please add the following to its build.gradle:');
|
|
print('''
|
|
gradle.projectsEvaluated {
|
|
project(":$enclosingPackageName") {
|
|
tasks.withType(JavaCompile) {
|
|
options.compilerArgs << "-Xlint:all" << "-Werror"
|
|
}
|
|
}
|
|
}
|
|
''');
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Validates whether the given [example] has its Kotlin version set to at
|
|
/// least a minimum value, if it is set at all.
|
|
bool _validateKotlinVersion(
|
|
RepositoryPackage example, List<String> gradleLines) {
|
|
final RegExp kotlinVersionRegex =
|
|
RegExp(r"ext\.kotlin_version\s*=\s*'([\d.]+)'");
|
|
RegExpMatch? match;
|
|
if (gradleLines.any((String line) {
|
|
match = kotlinVersionRegex.firstMatch(line);
|
|
return match != null;
|
|
})) {
|
|
final Version version = Version.parse(match!.group(1)!);
|
|
if (version < minKotlinVersion) {
|
|
printError('build.gradle sets "ext.kotlin_version" to "$version". The '
|
|
'minimum Kotlin version that can be specified is '
|
|
'$minKotlinVersion, for compatibility with modern dependencies.');
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|