Chromium Code Reviews| Index: pkg/dart_messages/bin/publish.dart |
| diff --git a/pkg/dart_messages/bin/publish.dart b/pkg/dart_messages/bin/publish.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..67785b9b2dd64a0c8fb419859b37e08a4f1ddd45 |
| --- /dev/null |
| +++ b/pkg/dart_messages/bin/publish.dart |
| @@ -0,0 +1,220 @@ |
| +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| +// for details. 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 '../lib/shared_messages.dart'; |
| + |
| +const String jsonPath = '../lib/generated/shared_messages.json'; |
| +const String dart2jsPath = |
| + '../../compiler/lib/src/diagnostics/generated/shared_messages.dart'; |
| +const String analyzerPath = |
| + '../../analyzer/lib/src/generated/generated/shared_messages.dart'; |
| + |
| +final String dontEditWarning = """ |
| +/* |
| +DON'T EDIT. GENERATED. DON'T EDIT. |
| +This file has been generated by 'publish.dart' in the dart_messages package. |
|
Brian Wilkerson
2016/01/23 18:27:10
This comment should include instructions for how t
floitsch
2016/01/25 12:08:42
Done.
|
| +*/"""; |
| + |
| +const String copyrightHeader = ''' |
| +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file.'''; |
| + |
| +void markAsReadonly(String path) { |
| + // TODO(15078): mark as read-only. Currently not possible: |
| + // http://dartbug.com/15078. |
| +} |
| + |
| +void emitJson() { |
| + var input = MESSAGES; |
| + var outPath = Platform.script.resolve(jsonPath).toFilePath(); |
| + print("Emitting JSON:"); |
| + print(" Input: ${input.length} entries"); |
| + print(" Output: $outPath"); |
| + new File(outPath).writeAsStringSync(messagesAsJson); |
| + print("Emitting JSON done."); |
| +} |
| + |
| +String escapeString(String str) { |
| + return JSON.encode(str); |
| +} |
| + |
| +/// Emits the messages in dart2js format. |
| +/// |
| +/// The dart2js-format consists of two entities: |
| +/// 1. the `MessageKind` enum, and |
| +/// 2. the MessageKind-to-Template map `TEMPLATES`. |
| +/// |
| +/// The template is an instance of MessageTemplate: |
| +/// |
| +/// const MessageTemplate( |
| +/// this.kind, |
| +/// this.template, |
| +/// {this.howToFix, |
| +/// this.examples, |
| +/// this.options: const <String>[]}); |
| +/// |
| +/// A sample output thus looks as follows: |
| +/// |
| +/// enum MessageKind { |
| +/// EXAMPLE_MESSAGE, |
| +/// } |
| +/// |
| +/// const Map<MessageKind, MessageTemplate> { |
| +/// EXAMPLE_MESSAGE: const MessageTemplate( |
| +/// EXAMPLE_MESSAGE, |
| +/// "Don't use #foo with #bar", |
| +/// howToFix: "Just don't do it", |
| +/// options: const ['--some-flag']), |
| +/// examples: const [''' |
| +/// some example with bad code;'''], |
| +/// }; |
| +void emitDart2js() { |
| + var input = MESSAGES; |
| + var outPath = Platform.script.resolve(dart2jsPath).toFilePath(); |
| + print("Emitting dart2js:"); |
| + print(" Input: ${input.length} entries"); |
| + print(" Output: $outPath"); |
| + |
| + var enumIds = input.keys.toList(); |
| + |
| + StringBuffer out = new StringBuffer(); |
| + out.writeln(dontEditWarning); |
| + out.writeln(copyrightHeader); |
| + out.writeln("import '../messages.dart' show MessageTemplate;"); |
| + out.writeln(); |
| + out.write(("enum SharedMessageKind {\n ")); |
| + // We generate on one line on purpose, so that users are less likely to |
| + // modify the generated file. |
| + out.writeln(enumIds.join(",\n ")); |
| + out.writeln("}"); |
| + out.writeln(); |
| + out.writeln("const Map<SharedMessageKind, MessageTemplate> TEMPLATES = " |
| + "const <SharedMessageKind, MessageTemplate>{ "); |
| + input.forEach((name, message) { |
| + out.writeln(" SharedMessageKind.$name: const MessageTemplate("); |
| + // TODO(floitsch): include id. |
| + out.writeln(" SharedMessageKind.$name,"); |
| + out.write(" "); |
| + out.write(escapeString(message.template)); |
| + if (message.howToFix != null) { |
| + out.write(",\n howToFix: ${escapeString(message.howToFix)}"); |
| + } |
| + if (message.options != null) { |
| + out.write(",\n options: const ["); |
| + out.write(message.options.map(escapeString).join(",")); |
| + out.writeln("]"); |
| + } |
| + if (message.examples != null) { |
| + out.writeln(",\n examples: const ["); |
| + for (var example in message.examples) { |
| + if (example is String) { |
| + out.writeln(" r'''"); |
| + out.write(example); |
| + out.write("'''"); |
| + } else if (example is Map) { |
| + out.writeln(" const {"); |
| + example.forEach((String fileName, String content) { |
| + out.writeln(" '$fileName': r'''"); |
| + out.write(content); |
| + out.writeln("''',"); |
| + }); |
| + out.write(" }"); |
| + } |
| + out.writeln(","); |
| + } |
| + out.writeln(" ]"); |
| + } |
| + out.writeln(" ), // Generated. Don't edit."); |
| + }); |
| + out.writeln("};"); |
| + |
| + new File(outPath).writeAsStringSync(out.toString()); |
| + print("Emitting dart2js done."); |
| +} |
| + |
| +String convertToAnalyzerTemplate(String template, holeOrder) { |
| + var holeMap; |
| + if (holeOrder != null) { |
| + holeMap = {}; |
| + for (int i = 0; i < holeOrder.length; i++) { |
| + holeMap[holeOrder[i]] = i; |
| + } |
| + } |
| + int seenHoles = 0; |
| + return template.replaceAllMapped(new RegExp(r"#\w+"), (Match match) { |
| + if (holeMap != null) { |
| + String holeName = match[0].substring(1); |
| + int index = holeMap[holeName]; |
| + if (index == null) { |
| + throw "Couldn't find hole-position for $holeName $holeMap"; |
| + } |
| + return "{$index}"; |
| + } else { |
| + return "{${seenHoles++}}"; |
| + } |
| + }); |
| +} |
| + |
| +/// Emits the messages in analyzer format. |
| +/// |
| +/// Messages are encoded as instances of `ErrorCode` classes where the |
| +/// corresponding class is given by the `category` field of the Message. |
| +/// |
| +/// All instances are stored as top-level const variables. |
| +/// |
| +/// A sample output looks as follows: |
| +/// |
| +/// const EXAMPLE_MESSAGE = const FooCategoryErrorCode( |
|
Brian Wilkerson
2016/01/23 18:27:11
I think this is missing the class name:
const Foo
floitsch
2016/01/25 12:08:42
Done.
|
| +/// "EXAMPLE_MESSAGE", |
| +/// "Don't use #foo with #bar", |
|
Brian Wilkerson
2016/01/23 18:27:10
And I think messages get converted to
"Don't use
floitsch
2016/01/25 12:08:42
Done.
|
| +/// "Just don't do it"); // The how-to-fix. |
|
Brian Wilkerson
2016/01/23 18:27:10
"The how-to-fix." --> "Generated. Don't edit." (or
floitsch
2016/01/25 12:08:42
Done.
|
| +void emitAnalyzer() { |
| + var input = MESSAGES; |
| + var outPath = Platform.script.resolve(analyzerPath).toFilePath(); |
| + print("Emitting analyzer:"); |
| + print(" Input: ${input.length} entries"); |
| + print(" Output: $outPath"); |
| + |
| + StringBuffer out = new StringBuffer(); |
| + out.writeln(dontEditWarning); |
| + out.writeln(copyrightHeader); |
| + out.writeln("import '../error.dart';"); |
|
Brian Wilkerson
2016/01/23 18:27:10
We avoid relative paths where possible, so I would
floitsch
2016/01/25 12:08:42
I do the exact opposite: avoid non-relative paths
|
| + out.writeln(); |
| + input.forEach((name, message) { |
| + Category category = message.category; |
| + String className = category.name + "Code"; |
| + out.writeln("const $className $name = const $className("); |
| + out.writeln(" '$name',"); |
| + |
| + String template = message.template; |
| + List holeOrder = message.templateHoleOrder; |
| + String analyzerTemplate = convertToAnalyzerTemplate(template, holeOrder); |
| + out.write(" "); |
| + out.write(escapeString(analyzerTemplate)); |
| + |
| + if (message.howToFix) { |
|
Brian Wilkerson
2016/01/23 18:27:11
I think this wants to be "message.howToFix != null
floitsch
2016/01/25 12:08:42
Done.
|
| + out.write("\n, ${escapeString(message.howToFix)}"); |
| + } |
| + |
| + out.writeln("); // Generated. Don't edit."); |
|
Brian Wilkerson
2016/01/23 18:27:10
Having this on every constant seems like overkill.
floitsch
2016/01/25 12:08:42
Up to you, but since they don't hurt I think they
|
| + }); |
| + |
| + new File(outPath).writeAsStringSync(out.toString()); |
| + print("Emitting analyzer done."); |
| +} |
| + |
| +/// Translates the shared messages in `../lib/shared_messages.dart` to JSON, |
| +/// dart2js, and analyzer formats. |
| +/// |
| +/// Emits the json-output to [jsonPath], the dart2js-output to [dart2jsPath], |
| +/// and the analyzer-output to [analyzerPath]. |
| +void main() { |
| + emitJson(); |
| + emitDart2js(); |
| + emitAnalyzer(); |
| +} |