main.dart 6.83 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:io';
6 7

import 'package:args/args.dart';
8
import 'package:path/path.dart' as path;
9 10 11 12

import 'configuration.dart';
import 'snippets.dart';

13
const String _kSerialOption = 'serial';
14
const String _kElementOption = 'element';
15
const String _kHelpOption = 'help';
16 17
const String _kInputOption = 'input';
const String _kLibraryOption = 'library';
18
const String _kOutputOption = 'output';
19 20 21
const String _kPackageOption = 'package';
const String _kTemplateOption = 'template';
const String _kTypeOption = 'type';
22
const String _kShowDartPad = 'dartpad';
23

24 25 26 27 28 29 30 31 32 33
String getChannelName() {
  final RegExp gitBranchRegexp = RegExp(r'^## (.*)');
  final ProcessResult gitResult = Process.runSync('git', <String>['status', '-b', '--porcelain']);
  if (gitResult.exitCode != 0)
    throw 'git status exit with non-zero exit code: ${gitResult.exitCode}';
  final Match gitBranchMatch = gitBranchRegexp.firstMatch(
      (gitResult.stdout as String).trim().split('\n').first);
  return gitBranchMatch == null ? '<unknown>' : gitBranchMatch.group(1).split('...').first;
}

34 35 36
/// Generates snippet dartdoc output for a given input, and creates any sample
/// applications needed by the snippet.
void main(List<String> argList) {
37
  final Map<String, String> environment = Platform.environment;
38 39 40 41 42
  final ArgParser parser = ArgParser();
  final List<String> snippetTypes =
      SnippetType.values.map<String>((SnippetType type) => getEnumName(type)).toList();
  parser.addOption(
    _kTypeOption,
43
    defaultsTo: getEnumName(SnippetType.sample),
44 45 46
    allowed: snippetTypes,
    allowedHelp: <String, String>{
      getEnumName(SnippetType.sample):
47 48 49
          'Produce a code sample application complete with embedding the sample in an '
          'application template.',
      getEnumName(SnippetType.snippet):
50
          'Produce a nicely formatted piece of sample code. Does not embed the '
51
          'sample into an application template.',
52 53 54 55 56 57 58 59
    },
    help: 'The type of snippet to produce.',
  );
  parser.addOption(
    _kTemplateOption,
    defaultsTo: null,
    help: 'The name of the template to inject the code into.',
  );
60 61 62
  parser.addOption(
    _kOutputOption,
    defaultsTo: null,
63
    help: 'The output path for the generated sample application. Overrides '
64
        'the naming generated by the --package/--library/--element arguments. '
65
        'Metadata will be written alongside in a .json file. '
66 67
        'The basename of this argument is used as the ID',
  );
68 69 70
  parser.addOption(
    _kInputOption,
    defaultsTo: environment['INPUT'],
71
    help: 'The input file containing the sample code to inject.',
72 73 74 75
  );
  parser.addOption(
    _kPackageOption,
    defaultsTo: environment['PACKAGE_NAME'],
76
    help: 'The name of the package that this sample belongs to.',
77 78 79 80
  );
  parser.addOption(
    _kLibraryOption,
    defaultsTo: environment['LIBRARY_NAME'],
81
    help: 'The name of the library that this sample belongs to.',
82 83 84 85
  );
  parser.addOption(
    _kElementOption,
    defaultsTo: environment['ELEMENT_NAME'],
86
    help: 'The name of the element that this sample belongs to.',
87
  );
88 89 90 91 92
  parser.addOption(
    _kSerialOption,
    defaultsTo: environment['INVOCATION_INDEX'],
    help: 'A unique serial number for this snippet tool invocation.',
  );
93 94 95 96 97 98
  parser.addFlag(
    _kHelpOption,
    defaultsTo: false,
    negatable: false,
    help: 'Prints help documentation for this command',
  );
99 100 101 102
  parser.addFlag(
    _kShowDartPad,
    defaultsTo: false,
    negatable: false,
103
    help: "Indicates whether DartPad should be included in the sample's "
104
        'final HTML output. This flag only applies when the type parameter is '
105
        '"sample".',
106
  );
107 108 109

  final ArgResults args = parser.parse(argList);

110
  if (args[_kHelpOption] as bool) {
111 112 113 114
    stderr.writeln(parser.usage);
    exit(0);
  }

115 116 117 118
  final SnippetType snippetType = SnippetType.values
      .firstWhere((SnippetType type) => getEnumName(type) == args[_kTypeOption], orElse: () => null);
  assert(snippetType != null, "Unable to find '${args[_kTypeOption]}' in SnippetType enum.");

119
  if (args[_kShowDartPad] == true && snippetType != SnippetType.sample) {
120
    errorExit('${args[_kTypeOption]} was selected, but the --dartpad flag is only valid '
121
      'for application sample code.');
122 123
  }

124 125 126 127 128 129
  if (args[_kInputOption] == null) {
    stderr.writeln(parser.usage);
    errorExit('The --$_kInputOption option must be specified, either on the command '
        'line, or in the INPUT environment variable.');
  }

130
  final File input = File(args['input'] as String);
131 132 133 134 135
  if (!input.existsSync()) {
    errorExit('The input file ${input.path} does not exist.');
  }

  String template;
136
  if (snippetType == SnippetType.sample) {
137 138
    final String templateArg = args[_kTemplateOption] as String;
    if (templateArg == null || templateArg.isEmpty) {
139 140
      stderr.writeln(parser.usage);
      errorExit('The --$_kTemplateOption option must be specified on the command '
141
          'line for application samples.');
142
    }
143
    template = templateArg.replaceAll(RegExp(r'.tmpl$'), '');
144 145
  }

146 147 148 149 150
  String emptyToNull(String value) => value?.isEmpty ?? true ? null : value;
  final String packageName = emptyToNull(args[_kPackageOption] as String);
  final String libraryName = emptyToNull(args[_kLibraryOption] as String);
  final String elementName = emptyToNull(args[_kElementOption] as String);
  final String serial = emptyToNull(args[_kSerialOption] as String);
151
  final List<String> id = <String>[];
152
  if (args[_kOutputOption] != null) {
153
    id.add(path.basename(path.basenameWithoutExtension(args[_kOutputOption] as String)));
154
  } else {
155 156
    if (packageName != null && packageName != 'flutter') {
      id.add(packageName);
157
    }
158 159
    if (libraryName != null) {
      id.add(libraryName);
160
    }
161 162
    if (elementName != null) {
      id.add(elementName);
163
    }
164 165 166
    if (serial != null) {
      id.add(serial);
    }
167 168
    if (id.isEmpty) {
      errorExit('Unable to determine ID. At least one of --$_kPackageOption, '
169 170
          '--$_kLibraryOption, --$_kElementOption, -$_kSerialOption, or the environment variables '
          'PACKAGE_NAME, LIBRARY_NAME, ELEMENT_NAME, or INVOCATION_INDEX must be non-empty.');
171
    }
172 173 174 175 176 177
  }

  final SnippetGenerator generator = SnippetGenerator();
  stdout.write(generator.generate(
    input,
    snippetType,
178
    showDartPad: args[_kShowDartPad] as bool,
179
    template: template,
180
    output: args[_kOutputOption] != null ? File(args[_kOutputOption] as String) : null,
181 182
    metadata: <String, Object>{
      'sourcePath': environment['SOURCE_PATH'],
183 184 185
      'sourceLine': environment['SOURCE_LINE'] != null
          ? int.tryParse(environment['SOURCE_LINE'])
          : null,
186
      'id': id.join('.'),
187
      'channel': getChannelName(),
188
      'serial': serial,
189 190 191 192
      'package': packageName,
      'library': libraryName,
      'element': elementName,
    },
193
  ));
194

195 196
  exit(0);
}