mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

This reverts commit 765191e7b4
.
because the updated packages cause a gradle build problem.
270 lines
9.3 KiB
Dart
270 lines
9.3 KiB
Dart
// Copyright 2017 The Chromium 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 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:args/args.dart';
|
|
import 'package:path/path.dart' as path;
|
|
|
|
const String CHROMIUM_REPO =
|
|
'https://chromium.googlesource.com/external/github.com/flutter/flutter';
|
|
const String GITHUB_REPO = 'https://github.com/flutter/flutter.git';
|
|
|
|
/// The type of the process runner function. This allows us to
|
|
/// inject a fake process runner into the ArchiveCreator for tests.
|
|
typedef ProcessResult ProcessRunner(
|
|
String executable,
|
|
List<String> arguments, {
|
|
String workingDirectory,
|
|
Map<String, String> environment,
|
|
bool includeParentEnvironment,
|
|
bool runInShell,
|
|
Encoding stdoutEncoding,
|
|
Encoding stderrEncoding,
|
|
});
|
|
|
|
/// Error class for when a process fails to run, so we can catch
|
|
/// it and provide something more readable than a stack trace.
|
|
class ProcessFailedException extends Error {
|
|
ProcessFailedException([this.message, this.exitCode]);
|
|
|
|
String message = '';
|
|
int exitCode = 0;
|
|
|
|
@override
|
|
String toString() => message;
|
|
}
|
|
|
|
/// Creates a pre-populated Flutter archive from a git repo.
|
|
class ArchiveCreator {
|
|
/// [tempDir] is the directory to use for creating the archive. Will place
|
|
/// several GiB of data there, so it should have available space.
|
|
/// [outputFile] is the name of the output archive. It should end in either
|
|
/// ".tar.xz" or ".zip".
|
|
/// The runner argument is used to inject a mock of [Process.runSync] for
|
|
/// testing purposes.
|
|
ArchiveCreator(this.tempDir, this.outputFile, {ProcessRunner runner})
|
|
: assert(outputFile.path.toLowerCase().endsWith('.zip') ||
|
|
outputFile.path.toLowerCase().endsWith('.tar.xz')),
|
|
flutterRoot = new Directory(path.join(tempDir.path, 'flutter')),
|
|
_runner = runner ?? Process.runSync {
|
|
flutter = path.join(
|
|
flutterRoot.absolute.path,
|
|
'bin',
|
|
Platform.isWindows ? 'flutter.bat' : 'flutter',
|
|
);
|
|
environment = new Map<String, String>.from(Platform.environment);
|
|
environment['PUB_CACHE'] = path.join(flutterRoot.absolute.path, '.pub-cache');
|
|
}
|
|
|
|
final Directory flutterRoot;
|
|
final Directory tempDir;
|
|
final File outputFile;
|
|
final ProcessRunner _runner;
|
|
String flutter;
|
|
final String git = Platform.isWindows ? 'git.bat' : 'git';
|
|
final String zip = Platform.isWindows ? '7za.exe' : 'zip';
|
|
final String tar = Platform.isWindows ? 'tar.exe' : 'tar';
|
|
Map<String, String> environment;
|
|
|
|
/// Clone the Flutter repo and make sure that the git environment is sane
|
|
/// for when the user will unpack it.
|
|
void checkoutFlutter(String revision) {
|
|
// We want the user to start out the in the 'master' branch instead of a
|
|
// detached head. To do that, we need to make sure master points at the
|
|
// desired revision.
|
|
runGit(<String>['clone', '-b', 'master', CHROMIUM_REPO], workingDirectory: tempDir);
|
|
runGit(<String>['reset', '--hard', revision]);
|
|
|
|
// Make the origin point to github instead of the chromium mirror.
|
|
runGit(<String>['remote', 'remove', 'origin']);
|
|
runGit(<String>['remote', 'add', 'origin', GITHUB_REPO]);
|
|
}
|
|
|
|
/// Prepare the archive repo so that it has all of the caches warmed up and
|
|
/// is configured for the user to being working.
|
|
void prepareArchive() {
|
|
runFlutter(<String>['doctor']);
|
|
runFlutter(<String>['update-packages']);
|
|
runFlutter(<String>['precache']);
|
|
runFlutter(<String>['ide-config']);
|
|
|
|
// Create each of the templates, since they will call pub get on
|
|
// themselves when created, and this will warm the cache with their
|
|
// dependencies too.
|
|
for (String template in <String>['app', 'package', 'plugin']) {
|
|
final String createName = path.join(tempDir.path, 'create_$template');
|
|
runFlutter(
|
|
<String>['create', '--template=$template', createName],
|
|
);
|
|
}
|
|
|
|
// Yes, we could just skip all .packages files when constructing
|
|
// the archive, but some are checked in, and we don't want to skip
|
|
// those.
|
|
runGit(<String>['clean', '-f', '-X', '**/.packages']);
|
|
}
|
|
|
|
/// Create the archive into the given output file.
|
|
void createArchive() {
|
|
if (outputFile.path.toLowerCase().endsWith('.zip')) {
|
|
createZipArchive(outputFile, flutterRoot);
|
|
} else if (outputFile.path.toLowerCase().endsWith('.tar.xz')) {
|
|
createTarArchive(outputFile, flutterRoot);
|
|
}
|
|
}
|
|
|
|
String _runProcess(String executable, List<String> args, {Directory workingDirectory}) {
|
|
workingDirectory ??= flutterRoot;
|
|
stderr.write('Running "$executable ${args.join(' ')}" in ${workingDirectory.path}.\n');
|
|
ProcessResult result;
|
|
try {
|
|
result = _runner(
|
|
executable,
|
|
args,
|
|
workingDirectory: workingDirectory.absolute.path,
|
|
environment: environment,
|
|
includeParentEnvironment: false,
|
|
);
|
|
} on ProcessException catch (e) {
|
|
final String message = 'Running "$executable ${args.join(' ')}" in ${workingDirectory.path} '
|
|
'failed with:\n${e.toString()}\n PATH: ${environment['PATH']}';
|
|
throw new ProcessFailedException(message, -1);
|
|
} catch (e) {
|
|
rethrow;
|
|
}
|
|
stdout.write(result.stdout);
|
|
stderr.write(result.stderr);
|
|
if (result.exitCode != 0) {
|
|
final String message = 'Running "$executable ${args.join(' ')}" in ${workingDirectory.path} '
|
|
'failed with ${result.exitCode}.';
|
|
throw new ProcessFailedException(message, result.exitCode);
|
|
}
|
|
return result.stdout.trim();
|
|
}
|
|
|
|
String runFlutter(List<String> args) {
|
|
return _runProcess(flutter, args);
|
|
}
|
|
|
|
String runGit(List<String> args, {Directory workingDirectory}) {
|
|
return _runProcess(git, args, workingDirectory: workingDirectory);
|
|
}
|
|
|
|
void createZipArchive(File output, Directory source) {
|
|
final List<String> args = <String>[];
|
|
if (Platform.isWindows) {
|
|
// We use 7-Zip on Windows, which has different args.
|
|
args.addAll(<String>['a', '-tzip', '-mx=9']);
|
|
} else {
|
|
args.addAll(<String>['-r', '-9', '-q']);
|
|
}
|
|
args.addAll(<String>[
|
|
output.absolute.path,
|
|
path.basename(source.absolute.path),
|
|
]);
|
|
|
|
_runProcess(zip, args,
|
|
workingDirectory: new Directory(path.dirname(source.absolute.path)));
|
|
}
|
|
|
|
void createTarArchive(File output, Directory source) {
|
|
final List<String> args = <String>[
|
|
'cJf',
|
|
output.absolute.path,
|
|
path.basename(source.absolute.path),
|
|
];
|
|
_runProcess(tar, args,
|
|
workingDirectory: new Directory(path.dirname(source.absolute.path)));
|
|
}
|
|
}
|
|
|
|
/// Prepares a flutter git repo to be packaged up for distribution.
|
|
/// It mainly serves to populate the .pub-cache with any appropriate Dart
|
|
/// packages, and the flutter cache in bin/cache with the appropriate
|
|
/// dependencies and snapshots.
|
|
void main(List<String> argList) {
|
|
final ArgParser argParser = new ArgParser();
|
|
argParser.addOption(
|
|
'temp_dir',
|
|
defaultsTo: null,
|
|
help: 'A location where temporary files may be written. Defaults to a '
|
|
'directory in the system temp folder. Will write a few GiB of data, '
|
|
'so it should have sufficient free space.',
|
|
);
|
|
argParser.addOption(
|
|
'revision',
|
|
defaultsTo: 'master',
|
|
help: 'The Flutter revision to build the archive with. Defaults to the '
|
|
"master branch's HEAD revision.",
|
|
);
|
|
argParser.addOption(
|
|
'output',
|
|
defaultsTo: null,
|
|
help: 'The path where the output archive should be written. '
|
|
'The suffix determines the output format: .tar.xz or .zip are the '
|
|
'only formats supported.',
|
|
);
|
|
final ArgResults args = argParser.parse(argList);
|
|
|
|
void errorExit(String message, {int exitCode = -1}) {
|
|
stderr.write('Error: $message\n\n');
|
|
stderr.write('${argParser.usage}\n');
|
|
exit(exitCode);
|
|
}
|
|
|
|
if (args['revision'].isEmpty) {
|
|
errorExit('Invalid argument: --revision must be specified.');
|
|
}
|
|
|
|
Directory tmpDir;
|
|
bool removeTempDir = false;
|
|
if (args['temp_dir'] == null || args['temp_dir'].isEmpty) {
|
|
tmpDir = Directory.systemTemp.createTempSync('flutter_');
|
|
removeTempDir = true;
|
|
} else {
|
|
tmpDir = new Directory(args['temp_dir']);
|
|
if (!tmpDir.existsSync()) {
|
|
errorExit("Temporary directory ${args['temp_dir']} doesn't exist.");
|
|
}
|
|
}
|
|
|
|
String outputFileString = args['output'];
|
|
if (outputFileString == null || outputFileString.isEmpty) {
|
|
final String suffix = Platform.isWindows ? '.zip' : '.tar.xz';
|
|
outputFileString = path.join(tmpDir.path, 'flutter_${args['revision']}$suffix');
|
|
} else if (!outputFileString.toLowerCase().endsWith('.zip') &&
|
|
!outputFileString.toLowerCase().endsWith('.tar.xz')) {
|
|
errorExit('Output file has unsupported suffix. It should be either ".zip" or ".tar.xz".');
|
|
}
|
|
|
|
final File outputFile = new File(outputFileString);
|
|
if (outputFile.existsSync()) {
|
|
errorExit('Output file ${outputFile.absolute.path} already exists.');
|
|
}
|
|
|
|
final ArchiveCreator preparer = new ArchiveCreator(tmpDir, outputFile);
|
|
int exitCode = 0;
|
|
String message;
|
|
try {
|
|
preparer.checkoutFlutter(args['revision']);
|
|
preparer.prepareArchive();
|
|
preparer.createArchive();
|
|
} on ProcessFailedException catch (e) {
|
|
exitCode = e.exitCode;
|
|
message = e.message;
|
|
} catch (e) {
|
|
rethrow;
|
|
} finally {
|
|
if (removeTempDir) {
|
|
tmpDir.deleteSync(recursive: true);
|
|
}
|
|
if (exitCode != 0) {
|
|
errorExit(message, exitCode: exitCode);
|
|
}
|
|
exit(0);
|
|
}
|
|
}
|