prepare_package.dart 5.12 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'dart:async';
6
import 'dart:io' show exit, stderr;
7 8

import 'package:args/args.dart';
9 10
import 'package:file/file.dart';
import 'package:file/local.dart';
11

12 13 14
import 'prepare_package/archive_creator.dart';
import 'prepare_package/archive_publisher.dart';
import 'prepare_package/common.dart';
15

16
const FileSystem fs = LocalFileSystem();
17

18 19
/// Prepares a flutter git repo to be packaged up for distribution. It mainly
/// serves to populate the .pub-preload-cache with any appropriate Dart
20 21
/// packages, and the flutter cache in bin/cache with the appropriate
/// dependencies and snapshots.
22
///
Ian Hickson's avatar
Ian Hickson committed
23 24
/// Archives contain the executables and customizations for the platform that
/// they are created on.
Ian Hickson's avatar
Ian Hickson committed
25
Future<void> main(List<String> rawArguments) async {
26
  final ArgParser argParser = ArgParser();
27 28 29 30
  argParser.addOption(
    'temp_dir',
    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, '
31 32 33
        'so it should have sufficient free space. If a temp_dir is not '
        'specified, then the default temp_dir will be created, used, and '
        'removed automatically.',
34
  );
35 36 37
  argParser.addOption('revision',
      help: 'The Flutter git repo revision to build the '
          'archive with. Must be the full 40-character hash. Required.');
38
  argParser.addOption(
39
    'branch',
40
    allowed: Branch.values.map<String>((Branch branch) => branch.name),
41
    help: 'The Flutter branch to build the archive with. Required.',
42 43 44
  );
  argParser.addOption(
    'output',
45 46 47 48 49 50 51
    help: 'The path to the directory where the output archive should be '
        'written. If --output is not specified, the archive will be written to '
        "the current directory. If the output directory doesn't exist, it, and "
        'the path to it, will be created.',
  );
  argParser.addFlag(
    'publish',
52 53
    help: 'If set, will publish the archive to Google Cloud Storage upon '
        'successful creation of the archive. Will publish under this '
54
        'directory: $baseUrl$releaseFolder',
55
  );
56 57 58 59 60
  argParser.addFlag(
    'force',
    abbr: 'f',
    help: 'Overwrite a previously uploaded package.',
  );
61 62 63 64 65
  argParser.addFlag(
    'dry_run',
    negatable: false,
    help: 'Prints gsutil commands instead of executing them.',
  );
66 67 68 69
  argParser.addFlag(
    'help',
    negatable: false,
    help: 'Print help for this command.',
70
  );
71

Ian Hickson's avatar
Ian Hickson committed
72
  final ArgResults parsedArguments = argParser.parse(rawArguments);
73

74
  if (parsedArguments['help'] as bool) {
75 76 77 78
    print(argParser.usage);
    exit(0);
  }

79 80 81 82 83 84
  void errorExit(String message, {int exitCode = -1}) {
    stderr.write('Error: $message\n\n');
    stderr.write('${argParser.usage}\n');
    exit(exitCode);
  }

85
  if (!parsedArguments.wasParsed('revision')) {
86 87
    errorExit('Invalid argument: --revision must be specified.');
  }
88
  final String revision = parsedArguments['revision'] as String;
89 90 91 92
  if (revision.length != 40) {
    errorExit('Invalid argument: --revision must be the entire hash, not just a prefix.');
  }

93
  if (!parsedArguments.wasParsed('branch')) {
94 95
    errorExit('Invalid argument: --branch must be specified.');
  }
96

97
  final String? tempDirArg = parsedArguments['temp_dir'] as String?;
98
  final Directory tempDir;
99
  bool removeTempDir = false;
100
  if (tempDirArg == null || tempDirArg.isEmpty) {
101
    tempDir = fs.systemTempDirectory.createTempSync('flutter_package.');
102 103
    removeTempDir = true;
  } else {
104
    tempDir = fs.directory(tempDirArg);
105
    if (!tempDir.existsSync()) {
106
      errorExit("Temporary directory $tempDirArg doesn't exist.");
107 108 109
    }
  }

110
  final Directory outputDir;
Ian Hickson's avatar
Ian Hickson committed
111
  if (parsedArguments['output'] == null) {
112
    outputDir = tempDir;
113
  } else {
114
    outputDir = fs.directory(parsedArguments['output'] as String);
115 116
    if (!outputDir.existsSync()) {
      outputDir.createSync(recursive: true);
117
    }
118 119
  }

120 121
  final bool publish = parsedArguments['publish'] as bool;
  final bool dryRun = parsedArguments['dry_run'] as bool;
122
  final Branch branch = Branch.values.byName(parsedArguments['branch'] as String);
123 124 125 126 127
  final ArchiveCreator creator = ArchiveCreator(
    tempDir,
    outputDir,
    revision,
    branch,
128
    fs: fs,
129 130
    strict: publish && !dryRun,
  );
131
  int exitCode = 0;
132
  late String message;
133
  try {
134
    final Map<String, String> version = await creator.initializeRepo();
135
    final File outputFile = await creator.createArchive();
136 137 138 139 140 141 142
    final ArchivePublisher publisher = ArchivePublisher(
      tempDir,
      revision,
      branch,
      version,
      outputFile,
      dryRun,
143
      fs: fs,
144 145
    );
    await publisher.generateLocalMetadata();
146
    if (parsedArguments['publish'] as bool) {
147
      await publisher.publishArchive(parsedArguments['force'] as bool);
148
    }
149
  } on PreparePackageException catch (e) {
150
    exitCode = e.exitCode;
151 152
    message = e.message;
  } catch (e) {
153
    exitCode = -1;
154
    message = e.toString();
155 156
  } finally {
    if (removeTempDir) {
157
      tempDir.deleteSync(recursive: true);
158 159 160 161 162 163 164
    }
    if (exitCode != 0) {
      errorExit(message, exitCode: exitCode);
    }
    exit(0);
  }
}