// Copyright 2014 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. // This script generates `android/build.gradle` for each directory specify in the stdin. // Then it generate the lockfiles for each Gradle project. // To regenerate these files, run `find . -type d -name 'android' | dart dev/tools/bin/generate_gradle_lockfiles.dart` import 'dart:io'; import 'package:file/file.dart'; import 'package:file/local.dart'; import 'package:path/path.dart' as path; void main(List<String> arguments) { print( "Usage: find . -type d -name 'android' | dart dev/tools/bin/generate_gradle_lockfiles.dart\n" 'If you would rather enter the files manually, just run `dart dev/tools/bin/generate_gradle_lockfiles.dart`,\n' "enter the absolute paths to the app's android directory, then press CTRL-D.\n" ); const FileSystem fileSystem = LocalFileSystem(); final List<String> androidDirectories = getFilesFromStdin(); for (final String androidDirectoryPath in androidDirectories) { final Directory androidDirectory = fileSystem.directory(path.normalize(androidDirectoryPath)); if (!androidDirectory.existsSync()) throw '$androidDirectory does not exist'; final File rootBuildGradle = androidDirectory.childFile('build.gradle'); if (!rootBuildGradle.existsSync()) { print('${rootBuildGradle.path} does not exist - skipping'); continue; } final File settingsGradle = androidDirectory.childFile('settings.gradle'); if (!settingsGradle.existsSync()) { print('${settingsGradle.path} does not exist - skipping'); continue; } if (settingsGradle.readAsStringSync().contains('include_flutter.groovy')) { print('${settingsGradle.path} add to app - skipping'); continue; } if (!androidDirectory.childDirectory('app').existsSync()) { print('${rootBuildGradle.path} is not an app - skipping'); continue; } if (!androidDirectory.parent.childFile('pubspec.yaml').existsSync()) { print('${rootBuildGradle.path} no pubspec.yaml in parent directory - skipping'); continue; } if (androidDirectory.parent.childFile('pubspec.yaml').readAsStringSync().contains('deferred-components')) { print('${rootBuildGradle.path} uses deferred components - skipping'); continue; } if (!androidDirectory.parent .childDirectory('lib') .childFile('main.dart') .existsSync()) { print('${rootBuildGradle.path} no main.dart under lib - skipping'); continue; } print('Processing ${androidDirectory.path}'); try { androidDirectory.childFile('buildscript-gradle.lockfile').deleteSync(); } on FileSystemException { // noop } rootBuildGradle.writeAsStringSync(rootGradleFileContent); settingsGradle.writeAsStringSync(settingGradleFile); final String appDirectory = androidDirectory.parent.absolute.path; // Fetch pub dependencies. exec('flutter', <String>['pub', 'get'], workingDirectory: appDirectory); // Verify that the Gradlew wrapper exists. final File gradleWrapper = androidDirectory.childFile('gradlew'); // Generate Gradle wrapper if it doesn't exists. // This logic is embedded within the Flutter tool. // To generate the wrapper, build a flavor that doesn't exist. if (!gradleWrapper.existsSync()) { Process.runSync( 'flutter', <String>['build', 'apk', '--debug', '--flavor=does-not-exist'], workingDirectory: appDirectory, ); } // Generate lock files. exec( gradleWrapper.absolute.path, <String>[':generateLockfiles'], workingDirectory: androidDirectory.absolute.path, ); print('Processed'); } } List<String> getFilesFromStdin() { final List<String> files = <String>[]; while (true) { final String? file = stdin.readLineSync(); if (file == null) { break; } files.add(file); } return files; } void exec( String cmd, List<String> args, { String? workingDirectory, }) { final ProcessResult result = Process.runSync(cmd, args, workingDirectory: workingDirectory); if (result.exitCode != 0) throw ProcessException( cmd, args, '${result.stdout}${result.stderr}', result.exitCode); } const String rootGradleFileContent = r''' // Copyright 2014 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. // This file is auto generated. // To update all the build.gradle files in the Flutter repo, // See dev/tools/bin/generate_gradle_lockfiles.dart. buildscript { ext.kotlin_version = '1.5.31' repositories { google() mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:4.1.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } configurations.classpath { resolutionStrategy.activateDependencyLocking() } } allprojects { repositories { google() mavenCentral() } } rootProject.buildDir = '../build' subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" } subprojects { project.evaluationDependsOn(':app') dependencyLocking { ignoredDependencies.add('io.flutter:*') lockFile = file("${rootProject.projectDir}/project-${project.name}.lockfile") if (!project.hasProperty('local-engine-repo')) { lockAllConfigurations() } } } task clean(type: Delete) { delete rootProject.buildDir } '''; const String settingGradleFile = r''' // Copyright 2014 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. // This file is auto generated. // To update all the settings.gradle files in the Flutter repo, // See dev/tools/bin/generate_gradle_lockfiles.dart. include ':app' enableFeaturePreview('ONE_LOCKFILE_PER_PROJECT') def localPropertiesFile = new File(rootProject.projectDir, "local.properties") def properties = new Properties() assert localPropertiesFile.exists() localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } def flutterSdkPath = properties.getProperty("flutter.sdk") assert flutterSdkPath != null, "flutter.sdk not set in local.properties" apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" ''';