| Index: pkg/front_end/lib/src/base/libraries_specification.dart
|
| diff --git a/pkg/front_end/lib/src/base/libraries_specification.dart b/pkg/front_end/lib/src/base/libraries_specification.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f21c8a2579a0ea836570acc288a2df0f310dc0b5
|
| --- /dev/null
|
| +++ b/pkg/front_end/lib/src/base/libraries_specification.dart
|
| @@ -0,0 +1,221 @@
|
| +// Copyright (c) 2017, 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.
|
| +
|
| +/// Library specification in-memory representation.
|
| +///
|
| +/// Many dart tools are configurable to support different target platforms. For
|
| +/// a given target, they need to know what libraries are available and where are
|
| +/// the sources and target-specific patches.
|
| +///
|
| +/// Here we define APIs to represent this specification and implement
|
| +/// serialization to (and deserialization from) a JSON file.
|
| +///
|
| +/// Here is an example specification JSON file:
|
| +///
|
| +/// {
|
| +/// "vm": {
|
| +/// "libraries": {
|
| +/// "core": {
|
| +/// "uri": "async/core.dart",
|
| +/// "patches": [
|
| +/// "path/to/core_patch.dart",
|
| +/// "path/to/list_patch.dart"
|
| +/// ]
|
| +/// }
|
| +/// "async": {
|
| +/// "uri": "async/async.dart",
|
| +/// "patches": "path/to/async_patch.dart"
|
| +/// }
|
| +/// "convert": {
|
| +/// "uri": "convert/convert.dart",
|
| +/// }
|
| +/// }
|
| +/// }
|
| +/// }
|
| +///
|
| +/// The format contains:
|
| +/// - a top level entry for each target. Keys are target names (e.g. "vm"
|
| +/// above), and values contain the entire specification of a target.
|
| +///
|
| +/// - each target specification is a map. Today only one key ("libraries") is
|
| +/// supported, but this may be extended in the future to add more
|
| +/// information on each target.
|
| +///
|
| +/// - The "libraries" entry contains details for how each platform library is
|
| +/// implemented. The entry is a map, where keys are the name of the platform
|
| +/// library and values contain details for where to find the implementation
|
| +/// fo that library.
|
| +///
|
| +/// - The name of the library is a single token (e.g. "core") that matches the
|
| +/// Uri path used after `dart:` (e.g. "dart:core").
|
| +///
|
| +/// - The "uri" entry on the library information is mandatory. The value is a
|
| +/// string URI reference. The "patches" entry is optional and may have as a
|
| +/// value a string URI reference or a list of URI references.
|
| +///
|
| +/// All URI references can either be a file URI or a relative URI path,
|
| +/// which will be resolved relative to the location of the library
|
| +/// specification file.
|
| +///
|
| +///
|
| +/// Note: we currently have several different files that need to be updated
|
| +/// when changing libraries, sources, and patch files:
|
| +/// * .platform files (for dart2js)
|
| +/// * .gypi files (for vm)
|
| +/// * sdk_library_metadata/lib/libraries.dart (for analyzer, ddc)
|
| +///
|
| +/// we are in the process of unifying them all under this format (see
|
| +/// https://github.com/dart-lang/sdk/issues/28836), but for now we need to pay
|
| +/// close attention to change them consistently.
|
| +
|
| +// TODO(sigmund): move this file to a shared package.
|
| +import 'dart:convert' show JSON;
|
| +
|
| +import '../fasta/util/relativize.dart';
|
| +
|
| +/// Contents from a single library specification file.
|
| +///
|
| +/// Contains information about all libraries on all target platforms defined in
|
| +/// that file.
|
| +class LibrariesSpecification {
|
| + final Map<String, TargetLibrariesSpecification> _targets;
|
| +
|
| + const LibrariesSpecification(
|
| + [this._targets = const <String, TargetLibrariesSpecification>{}]);
|
| +
|
| + /// The library specification for a given [target], or null if none is
|
| + /// available.
|
| + TargetLibrariesSpecification specificationFor(String target) =>
|
| + _targets[target];
|
| +
|
| + /// Parse the given [json] as a library specification, resolving any relative
|
| + /// paths from [baseUri].
|
| + ///
|
| + /// May throw an exception if [json] is not properly formatted or contains
|
| + /// invalid values.
|
| + static LibrariesSpecification parse(Uri baseUri, String json) {
|
| + if (json == null) return const LibrariesSpecification();
|
| + var jsonData;
|
| + try {
|
| + var data = JSON.decode(json);
|
| + if (data is! Map) {
|
| + return _reportError('top-level specification is not a map');
|
| + }
|
| + jsonData = data as Map;
|
| + } on FormatException catch (e) {
|
| + throw new LibrariesSpecificationException(e);
|
| + }
|
| + var targets = <String, TargetLibrariesSpecification>{};
|
| + jsonData.forEach((String targetName, targetData) {
|
| + Map<String, LibraryInfo> libraries = <String, LibraryInfo>{};
|
| + if (targetData is! Map) {
|
| + return _reportError(
|
| + "target specification for '$targetName' is not a map");
|
| + }
|
| + if (!targetData.containsKey("libraries")) {
|
| + return _reportError("target specification "
|
| + "for '$targetName' doesn't have a libraries entry");
|
| + }
|
| + var librariesData = targetData["libraries"];
|
| + if (librariesData is! Map) {
|
| + return _reportError("libraries entry for '$targetName' is not a map");
|
| + }
|
| + librariesData.forEach((String name, data) {
|
| + if (data is! Map) {
|
| + return _reportError(
|
| + "library data for '$name' in target '$targetName' is not a map");
|
| + }
|
| + Uri checkAndResolve(uriString) {
|
| + if (uriString is! String) {
|
| + return _reportError("uri value '$uriString' is not a string"
|
| + "(from library '$name' in target '$targetName')");
|
| + }
|
| + var uri = Uri.parse(uriString);
|
| + if (uri.scheme != '' && uri.scheme != 'file') {
|
| + return _reportError("uri scheme in '$uriString' is not supported.");
|
| + }
|
| + return baseUri.resolveUri(uri);
|
| + }
|
| +
|
| + var uri = checkAndResolve(data['uri']);
|
| + var patches;
|
| + if (data['patches'] is List) {
|
| + patches = data['patches'].map(baseUri.resolve).toList();
|
| + } else if (data['patches'] is String) {
|
| + patches = [checkAndResolve(data['patches'])];
|
| + } else if (data['patches'] == null) {
|
| + patches = const [];
|
| + } else {
|
| + return _reportError(
|
| + "patches entry for '$name' is not a list or a string");
|
| + }
|
| + libraries[name] = new LibraryInfo(name, uri, patches);
|
| + });
|
| + targets[targetName] =
|
| + new TargetLibrariesSpecification(targetName, libraries);
|
| + });
|
| + return new LibrariesSpecification(targets);
|
| + }
|
| +
|
| + static _reportError(String error) =>
|
| + throw new LibrariesSpecificationException(error);
|
| +
|
| + /// Serialize this specification to json.
|
| + ///
|
| + /// If possible serializes paths relative to [outputUri].
|
| + String toJsonString(Uri outputUri) => JSON.encode(toJsonMap(outputUri));
|
| +
|
| + Map toJsonMap(Uri outputUri) {
|
| + var result = {};
|
| + var dir = outputUri.resolve('.');
|
| + String pathFor(Uri uri) => relativizeUri(uri, base: dir);
|
| + _targets.forEach((targetName, target) {
|
| + var libraries = {};
|
| + target._libraries.forEach((name, lib) {
|
| + libraries[name] = {
|
| + 'uri': pathFor(lib.uri),
|
| + 'patches': lib.patches.map(pathFor).toList(),
|
| + };
|
| + });
|
| + result[targetName] = {'libraries': libraries};
|
| + });
|
| + return result;
|
| + }
|
| +}
|
| +
|
| +/// Specifies information about all libraries supported by a given target.
|
| +class TargetLibrariesSpecification {
|
| + /// Name of the target platform.
|
| + final String targetName;
|
| +
|
| + final Map<String, LibraryInfo> _libraries;
|
| +
|
| + const TargetLibrariesSpecification(this.targetName,
|
| + [this._libraries = const <String, LibraryInfo>{}]);
|
| +
|
| + /// Details about a library whose import is `dart:$name`.
|
| + LibraryInfo libraryInfoFor(String name) => _libraries[name];
|
| +}
|
| +
|
| +/// Information about a `dart:` library in a specific target platform.
|
| +class LibraryInfo {
|
| + /// The name of the library, which is the path developers use to import this
|
| + /// library (as `dart:$name`).
|
| + final String name;
|
| +
|
| + /// The file defining the main implementation of the library.
|
| + final Uri uri;
|
| +
|
| + /// Patch files used for this library in the target platform, if any.
|
| + final List<Uri> patches;
|
| +
|
| + const LibraryInfo(this.name, this.uri, this.patches);
|
| +}
|
| +
|
| +class LibrariesSpecificationException {
|
| + Object error;
|
| + LibrariesSpecificationException(this.error);
|
| +
|
| + String toString() => '$error';
|
| +}
|
|
|