chore(dart/transform): Integrate protoc into gulp build

This change detects if the user has `protoc` available and, if so, uses
it to generate `.pb.dart` files. If not, pre-built files are used
instead.
This commit is contained in:
Tim Blasi 2015-09-09 09:31:33 -07:00
parent 5298eb0709
commit cb4ff7491a
16 changed files with 5366 additions and 3 deletions

View File

@ -21,6 +21,7 @@ var licenseWrap = require('./tools/build/licensewrap');
var watch = require('./tools/build/watch');
var pubget = require('./tools/build/pubget');
var proto = require('./tools/build/proto');
var linknodemodules = require('./tools/build/linknodemodules');
var pubbuild = require('./tools/build/pubbuild');
var dartanalyzer = require('./tools/build/dartanalyzer');
@ -830,7 +831,7 @@ gulp.task('build/pure-packages.dart', function() {
var transformStream = gulp
.src([
'modules_dart/transform/**/*',
'!modules_dart/transform/pubspec.yaml'
'!modules_dart/transform/**/*.proto'
])
.pipe(gulp.dest(path.join(CONFIG.dest.dart, 'angular2')));
@ -882,6 +883,7 @@ gulp.task('build/pure-packages.dart', function() {
// Builds all Dart packages, but does not compile them
gulp.task('build/packages.dart', function(done) {
runSequence(
'lint_protos.dart',
'build/tree.dart',
'build/pure-packages.dart',
// Run after 'build/tree.dart' because broccoli clears the dist/dart folder
@ -1206,9 +1208,21 @@ gulp.task('clean', ['build/clean.tools', 'build/clean.js', 'build/clean.dart', '
gulp.task('build', ['build.js', 'build.dart']);
// ------------
// transform codegen
gulp.task('lint_protos.dart', function(done) {
return proto.lint({
dir: 'modules_dart/transform/lib/src/transform/common/model/'
}, done);
});
gulp.task('gen_protos.dart', function(done) {
return proto.generate({
dir: 'modules_dart/transform/lib/src/transform/common/model/',
plugin: 'tools/build/protoc-gen-dart'
}, done);
});
// change detection codegen
gulp.task('build.change_detect.dart', function(done) {
return runSequence('build/packages.dart', '!build/pubget.angular2.dart',
'!build/change_detect.dart', done);

View File

@ -18,6 +18,7 @@ dependencies:
intl: '^0.12.4'
logging: '>=0.9.0 <0.12.0'
observe: '^0.13.1'
protobuf: '^0.4.2'
quiver: '^0.21.4'
source_span: '^1.0.0'
stack_trace: '^1.1.1'

View File

@ -0,0 +1,128 @@
///
// Generated code. Do not modify.
///
library angular2.src.transform.common.model.proto_annotation_model;
import 'package:protobuf/protobuf.dart';
class NamedParameter extends GeneratedMessage {
static final BuilderInfo _i = new BuilderInfo('NamedParameter')
..a(1, 'name', PbFieldType.QS)
..a(2, 'value', PbFieldType.QS)
;
NamedParameter() : super();
NamedParameter.fromBuffer(List<int> i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r);
NamedParameter.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromJson(i, r);
NamedParameter clone() => new NamedParameter()..mergeFromMessage(this);
BuilderInfo get info_ => _i;
static NamedParameter create() => new NamedParameter();
static PbList<NamedParameter> createRepeated() => new PbList<NamedParameter>();
static NamedParameter getDefault() {
if (_defaultInstance == null) _defaultInstance = new _ReadonlyNamedParameter();
return _defaultInstance;
}
static NamedParameter _defaultInstance;
static void $checkItem(NamedParameter v) {
if (v is !NamedParameter) checkItemFailed(v, 'NamedParameter');
}
String get name => getField(1);
void set name(String v) { setField(1, v); }
bool hasName() => hasField(1);
void clearName() => clearField(1);
String get value => getField(2);
void set value(String v) { setField(2, v); }
bool hasValue() => hasField(2);
void clearValue() => clearField(2);
}
class _ReadonlyNamedParameter extends NamedParameter with ReadonlyMessageMixin {}
class AnnotationModel extends GeneratedMessage {
static final BuilderInfo _i = new BuilderInfo('AnnotationModel')
..a(1, 'name', PbFieldType.QS)
..p(2, 'parameters', PbFieldType.PS)
..pp(3, 'namedParameters', PbFieldType.PM, NamedParameter.$checkItem, NamedParameter.create)
..a(4, 'isView', PbFieldType.OB)
..a(5, 'isDirective', PbFieldType.OB)
..a(6, 'isComponent', PbFieldType.OB)
..a(7, 'isInjectable', PbFieldType.OB)
;
AnnotationModel() : super();
AnnotationModel.fromBuffer(List<int> i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r);
AnnotationModel.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromJson(i, r);
AnnotationModel clone() => new AnnotationModel()..mergeFromMessage(this);
BuilderInfo get info_ => _i;
static AnnotationModel create() => new AnnotationModel();
static PbList<AnnotationModel> createRepeated() => new PbList<AnnotationModel>();
static AnnotationModel getDefault() {
if (_defaultInstance == null) _defaultInstance = new _ReadonlyAnnotationModel();
return _defaultInstance;
}
static AnnotationModel _defaultInstance;
static void $checkItem(AnnotationModel v) {
if (v is !AnnotationModel) checkItemFailed(v, 'AnnotationModel');
}
String get name => getField(1);
void set name(String v) { setField(1, v); }
bool hasName() => hasField(1);
void clearName() => clearField(1);
List<String> get parameters => getField(2);
List<NamedParameter> get namedParameters => getField(3);
bool get isView => getField(4);
void set isView(bool v) { setField(4, v); }
bool hasIsView() => hasField(4);
void clearIsView() => clearField(4);
bool get isDirective => getField(5);
void set isDirective(bool v) { setField(5, v); }
bool hasIsDirective() => hasField(5);
void clearIsDirective() => clearField(5);
bool get isComponent => getField(6);
void set isComponent(bool v) { setField(6, v); }
bool hasIsComponent() => hasField(6);
void clearIsComponent() => clearField(6);
bool get isInjectable => getField(7);
void set isInjectable(bool v) { setField(7, v); }
bool hasIsInjectable() => hasField(7);
void clearIsInjectable() => clearField(7);
}
class _ReadonlyAnnotationModel extends AnnotationModel with ReadonlyMessageMixin {}
const NamedParameter$json = const {
'1': 'NamedParameter',
'2': const [
const {'1': 'name', '3': 1, '4': 2, '5': 9},
const {'1': 'value', '3': 2, '4': 2, '5': 9},
],
};
const AnnotationModel$json = const {
'1': 'AnnotationModel',
'2': const [
const {'1': 'name', '3': 1, '4': 2, '5': 9},
const {'1': 'parameters', '3': 2, '4': 3, '5': 9},
const {'1': 'named_parameters', '3': 3, '4': 3, '5': 11, '6': '.angular2.src.transform.common.model.proto.NamedParameter'},
const {'1': 'is_view', '3': 4, '4': 1, '5': 8},
const {'1': 'is_directive', '3': 5, '4': 1, '5': 8},
const {'1': 'is_component', '3': 6, '4': 1, '5': 8},
const {'1': 'is_injectable', '3': 7, '4': 1, '5': 8},
],
};
/**
* Generated with:
* annotation_model.proto (5ca037df4c4d3e5d2771a177c9f5ea9dc8ae5f91)
* libprotoc 2.5.0
* dart-protoc-plugin (cc35f743de982a4916588b9c505dd21c7fe87d17)
*/

View File

@ -0,0 +1,35 @@
syntax = "proto2";
package angular2.src.transform.common.model.proto;
message NamedParameter {
required string name = 1;
required string value = 2;
}
message AnnotationModel {
// The constructor that creates the annotation, or the name of the field that
// defines the annotation.
required string name = 1;
// The positional parameters provided to the annotation.
repeated string parameters = 2;
// The named parameters provided to the annotation.
repeated NamedParameter named_parameters = 3;
// Whether this is a `View` annotation.
optional bool is_view = 4;
// Whether this is a `Directive` annotation. This takes inheritance into
// account, that is, this should be true if `is_component` is true.
optional bool is_directive = 5;
// Whether htis is a `Component` annotation.
optional bool is_component = 6;
// Whether this is an `Injectable` annotation. This takes inheritance into
// account, that is, this should be true if `is_directive` and/or
// `is_component` is true.
optional bool is_injectable = 7;
}

View File

@ -0,0 +1,115 @@
///
// Generated code. Do not modify.
///
library angular2.src.transform.common.model.proto_import_export_model;
import 'package:protobuf/protobuf.dart';
class ImportModel extends GeneratedMessage {
static final BuilderInfo _i = new BuilderInfo('ImportModel')
..a(1, 'uri', PbFieldType.QS)
..p(2, 'showCombinators', PbFieldType.PS)
..p(3, 'hideCombinators', PbFieldType.PS)
..a(4, 'prefix', PbFieldType.OS)
..a(5, 'isDeferred', PbFieldType.OB)
;
ImportModel() : super();
ImportModel.fromBuffer(List<int> i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r);
ImportModel.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromJson(i, r);
ImportModel clone() => new ImportModel()..mergeFromMessage(this);
BuilderInfo get info_ => _i;
static ImportModel create() => new ImportModel();
static PbList<ImportModel> createRepeated() => new PbList<ImportModel>();
static ImportModel getDefault() {
if (_defaultInstance == null) _defaultInstance = new _ReadonlyImportModel();
return _defaultInstance;
}
static ImportModel _defaultInstance;
static void $checkItem(ImportModel v) {
if (v is !ImportModel) checkItemFailed(v, 'ImportModel');
}
String get uri => getField(1);
void set uri(String v) { setField(1, v); }
bool hasUri() => hasField(1);
void clearUri() => clearField(1);
List<String> get showCombinators => getField(2);
List<String> get hideCombinators => getField(3);
String get prefix => getField(4);
void set prefix(String v) { setField(4, v); }
bool hasPrefix() => hasField(4);
void clearPrefix() => clearField(4);
bool get isDeferred => getField(5);
void set isDeferred(bool v) { setField(5, v); }
bool hasIsDeferred() => hasField(5);
void clearIsDeferred() => clearField(5);
}
class _ReadonlyImportModel extends ImportModel with ReadonlyMessageMixin {}
class ExportModel extends GeneratedMessage {
static final BuilderInfo _i = new BuilderInfo('ExportModel')
..a(1, 'uri', PbFieldType.QS)
..p(2, 'showCombinators', PbFieldType.PS)
..p(3, 'hideCombinators', PbFieldType.PS)
;
ExportModel() : super();
ExportModel.fromBuffer(List<int> i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r);
ExportModel.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromJson(i, r);
ExportModel clone() => new ExportModel()..mergeFromMessage(this);
BuilderInfo get info_ => _i;
static ExportModel create() => new ExportModel();
static PbList<ExportModel> createRepeated() => new PbList<ExportModel>();
static ExportModel getDefault() {
if (_defaultInstance == null) _defaultInstance = new _ReadonlyExportModel();
return _defaultInstance;
}
static ExportModel _defaultInstance;
static void $checkItem(ExportModel v) {
if (v is !ExportModel) checkItemFailed(v, 'ExportModel');
}
String get uri => getField(1);
void set uri(String v) { setField(1, v); }
bool hasUri() => hasField(1);
void clearUri() => clearField(1);
List<String> get showCombinators => getField(2);
List<String> get hideCombinators => getField(3);
}
class _ReadonlyExportModel extends ExportModel with ReadonlyMessageMixin {}
const ImportModel$json = const {
'1': 'ImportModel',
'2': const [
const {'1': 'uri', '3': 1, '4': 2, '5': 9},
const {'1': 'show_combinators', '3': 2, '4': 3, '5': 9},
const {'1': 'hide_combinators', '3': 3, '4': 3, '5': 9},
const {'1': 'prefix', '3': 4, '4': 1, '5': 9},
const {'1': 'is_deferred', '3': 5, '4': 1, '5': 8},
],
};
const ExportModel$json = const {
'1': 'ExportModel',
'2': const [
const {'1': 'uri', '3': 1, '4': 2, '5': 9},
const {'1': 'show_combinators', '3': 2, '4': 3, '5': 9},
const {'1': 'hide_combinators', '3': 3, '4': 3, '5': 9},
],
};
/**
* Generated with:
* import_export_model.proto (36a3a72d0884b84b451b7188ffa1fc93b44e7b62)
* libprotoc 2.5.0
* dart-protoc-plugin (cc35f743de982a4916588b9c505dd21c7fe87d17)
*/

View File

@ -0,0 +1,28 @@
syntax = "proto2";
package angular2.src.transform.common.model.proto;
// Note that the fields that are common between `ImportModel` and `ExportModel`
// are stored at the same indexes, which allows them to be semi-wire-compatible
// with one another. This will hopefully not be necessary to exploit, but on the
// chance that it is it's easier to define this now.
message ImportModel {
required string uri = 1;
repeated string show_combinators = 2;
repeated string hide_combinators = 3;
optional string prefix = 4;
optional bool is_deferred = 5;
}
// See message above about wire-compatiblity with `ImportModel`.
message ExportModel {
required string uri = 1;
repeated string show_combinators = 2;
repeated string hide_combinators = 3;
}

View File

@ -0,0 +1,67 @@
///
// Generated code. Do not modify.
///
library angular2.src.transform.common.model.proto_ng_deps_model;
import 'package:protobuf/protobuf.dart';
import 'import_export_model.pb.dart';
import 'reflection_info_model.pb.dart';
class NgDepsModel extends GeneratedMessage {
static final BuilderInfo _i = new BuilderInfo('NgDepsModel')
..a(1, 'libraryUri', PbFieldType.OS)
..p(2, 'partUris', PbFieldType.PS)
..pp(3, 'imports', PbFieldType.PM, ImportModel.$checkItem, ImportModel.create)
..pp(4, 'exports', PbFieldType.PM, ExportModel.$checkItem, ExportModel.create)
..pp(5, 'reflectables', PbFieldType.PM, ReflectionInfoModel.$checkItem, ReflectionInfoModel.create)
;
NgDepsModel() : super();
NgDepsModel.fromBuffer(List<int> i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r);
NgDepsModel.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromJson(i, r);
NgDepsModel clone() => new NgDepsModel()..mergeFromMessage(this);
BuilderInfo get info_ => _i;
static NgDepsModel create() => new NgDepsModel();
static PbList<NgDepsModel> createRepeated() => new PbList<NgDepsModel>();
static NgDepsModel getDefault() {
if (_defaultInstance == null) _defaultInstance = new _ReadonlyNgDepsModel();
return _defaultInstance;
}
static NgDepsModel _defaultInstance;
static void $checkItem(NgDepsModel v) {
if (v is !NgDepsModel) checkItemFailed(v, 'NgDepsModel');
}
String get libraryUri => getField(1);
void set libraryUri(String v) { setField(1, v); }
bool hasLibraryUri() => hasField(1);
void clearLibraryUri() => clearField(1);
List<String> get partUris => getField(2);
List<ImportModel> get imports => getField(3);
List<ExportModel> get exports => getField(4);
List<ReflectionInfoModel> get reflectables => getField(5);
}
class _ReadonlyNgDepsModel extends NgDepsModel with ReadonlyMessageMixin {}
const NgDepsModel$json = const {
'1': 'NgDepsModel',
'2': const [
const {'1': 'library_uri', '3': 1, '4': 1, '5': 9},
const {'1': 'part_uris', '3': 2, '4': 3, '5': 9},
const {'1': 'imports', '3': 3, '4': 3, '5': 11, '6': '.angular2.src.transform.common.model.proto.ImportModel'},
const {'1': 'exports', '3': 4, '4': 3, '5': 11, '6': '.angular2.src.transform.common.model.proto.ExportModel'},
const {'1': 'reflectables', '3': 5, '4': 3, '5': 11, '6': '.angular2.src.transform.common.model.proto.ReflectionInfoModel'},
],
};
/**
* Generated with:
* ng_deps_model.proto (c84f449fc55ef46e38a652f65449c6645b9fe6cb)
* libprotoc 2.5.0
* dart-protoc-plugin (cc35f743de982a4916588b9c505dd21c7fe87d17)
*/

View File

@ -0,0 +1,18 @@
syntax = "proto2";
import "import_export_model.proto";
import "reflection_info_model.proto";
package angular2.src.transform.common.model.proto;
message NgDepsModel {
optional string library_uri = 1;
repeated string part_uris = 2;
repeated ImportModel imports = 3;
repeated ExportModel exports = 4;
repeated ReflectionInfoModel reflectables = 5;
}

View File

@ -0,0 +1,68 @@
///
// Generated code. Do not modify.
///
library angular2.src.transform.common.model.proto_parameter_model;
import 'package:protobuf/protobuf.dart';
class ParameterModel extends GeneratedMessage {
static final BuilderInfo _i = new BuilderInfo('ParameterModel')
..a(1, 'typeName', PbFieldType.OS)
..a(2, 'typeArgs', PbFieldType.OS)
..p(3, 'metadata', PbFieldType.PS)
..a(4, 'paramName', PbFieldType.OS)
..hasRequiredFields = false
;
ParameterModel() : super();
ParameterModel.fromBuffer(List<int> i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r);
ParameterModel.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromJson(i, r);
ParameterModel clone() => new ParameterModel()..mergeFromMessage(this);
BuilderInfo get info_ => _i;
static ParameterModel create() => new ParameterModel();
static PbList<ParameterModel> createRepeated() => new PbList<ParameterModel>();
static ParameterModel getDefault() {
if (_defaultInstance == null) _defaultInstance = new _ReadonlyParameterModel();
return _defaultInstance;
}
static ParameterModel _defaultInstance;
static void $checkItem(ParameterModel v) {
if (v is !ParameterModel) checkItemFailed(v, 'ParameterModel');
}
String get typeName => getField(1);
void set typeName(String v) { setField(1, v); }
bool hasTypeName() => hasField(1);
void clearTypeName() => clearField(1);
String get typeArgs => getField(2);
void set typeArgs(String v) { setField(2, v); }
bool hasTypeArgs() => hasField(2);
void clearTypeArgs() => clearField(2);
List<String> get metadata => getField(3);
String get paramName => getField(4);
void set paramName(String v) { setField(4, v); }
bool hasParamName() => hasField(4);
void clearParamName() => clearField(4);
}
class _ReadonlyParameterModel extends ParameterModel with ReadonlyMessageMixin {}
const ParameterModel$json = const {
'1': 'ParameterModel',
'2': const [
const {'1': 'type_name', '3': 1, '4': 1, '5': 9},
const {'1': 'type_args', '3': 2, '4': 1, '5': 9},
const {'1': 'metadata', '3': 3, '4': 3, '5': 9},
const {'1': 'param_name', '3': 4, '4': 1, '5': 9},
],
};
/**
* Generated with:
* parameter_model.proto (2a97dcb9a65b199f50fba67120a85590bceb083a)
* libprotoc 2.5.0
* dart-protoc-plugin (cc35f743de982a4916588b9c505dd21c7fe87d17)
*/

View File

@ -0,0 +1,13 @@
syntax = "proto2";
package angular2.src.transform.common.model.proto;
message ParameterModel {
optional string type_name = 1;
optional string type_args = 2;
repeated string metadata = 3;
optional string param_name = 4;
}

View File

@ -0,0 +1,77 @@
///
// Generated code. Do not modify.
///
library angular2.src.transform.common.model.proto_reflection_info_model;
import 'package:protobuf/protobuf.dart';
import 'annotation_model.pb.dart';
import 'parameter_model.pb.dart';
class ReflectionInfoModel extends GeneratedMessage {
static final BuilderInfo _i = new BuilderInfo('ReflectionInfoModel')
..a(1, 'name', PbFieldType.QS)
..a(2, 'ctorName', PbFieldType.OS)
..a(3, 'isFunction', PbFieldType.OB)
..pp(4, 'annotations', PbFieldType.PM, AnnotationModel.$checkItem, AnnotationModel.create)
..pp(5, 'parameters', PbFieldType.PM, ParameterModel.$checkItem, ParameterModel.create)
..p(6, 'interfaces', PbFieldType.PS)
;
ReflectionInfoModel() : super();
ReflectionInfoModel.fromBuffer(List<int> i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r);
ReflectionInfoModel.fromJson(String i, [ExtensionRegistry r = ExtensionRegistry.EMPTY]) : super.fromJson(i, r);
ReflectionInfoModel clone() => new ReflectionInfoModel()..mergeFromMessage(this);
BuilderInfo get info_ => _i;
static ReflectionInfoModel create() => new ReflectionInfoModel();
static PbList<ReflectionInfoModel> createRepeated() => new PbList<ReflectionInfoModel>();
static ReflectionInfoModel getDefault() {
if (_defaultInstance == null) _defaultInstance = new _ReadonlyReflectionInfoModel();
return _defaultInstance;
}
static ReflectionInfoModel _defaultInstance;
static void $checkItem(ReflectionInfoModel v) {
if (v is !ReflectionInfoModel) checkItemFailed(v, 'ReflectionInfoModel');
}
String get name => getField(1);
void set name(String v) { setField(1, v); }
bool hasName() => hasField(1);
void clearName() => clearField(1);
String get ctorName => getField(2);
void set ctorName(String v) { setField(2, v); }
bool hasCtorName() => hasField(2);
void clearCtorName() => clearField(2);
bool get isFunction => getField(3);
void set isFunction(bool v) { setField(3, v); }
bool hasIsFunction() => hasField(3);
void clearIsFunction() => clearField(3);
List<AnnotationModel> get annotations => getField(4);
List<ParameterModel> get parameters => getField(5);
List<String> get interfaces => getField(6);
}
class _ReadonlyReflectionInfoModel extends ReflectionInfoModel with ReadonlyMessageMixin {}
const ReflectionInfoModel$json = const {
'1': 'ReflectionInfoModel',
'2': const [
const {'1': 'name', '3': 1, '4': 2, '5': 9},
const {'1': 'ctor_name', '3': 2, '4': 1, '5': 9},
const {'1': 'is_function', '3': 3, '4': 1, '5': 8},
const {'1': 'annotations', '3': 4, '4': 3, '5': 11, '6': '.angular2.src.transform.common.model.proto.AnnotationModel'},
const {'1': 'parameters', '3': 5, '4': 3, '5': 11, '6': '.angular2.src.transform.common.model.proto.ParameterModel'},
const {'1': 'interfaces', '3': 6, '4': 3, '5': 9},
],
};
/**
* Generated with:
* reflection_info_model.proto (fd01d8a29e6bccccc343ef975829fd7cb6a63312)
* libprotoc 2.5.0
* dart-protoc-plugin (cc35f743de982a4916588b9c505dd21c7fe87d17)
*/

View File

@ -0,0 +1,24 @@
syntax = "proto2";
import "annotation_model.proto";
import "parameter_model.proto";
package angular2.src.transform.common.model.proto;
message ReflectionInfoModel {
// The (potentially prefixed) name of this Injectable.
// This can be a `Type` or a function name.
required string name = 1;
// The name of the ctor used to create this Injectable. In most cases, this
// will be null and we will use the default constructor.
optional string ctor_name = 2;
optional bool is_function = 3;
repeated AnnotationModel annotations = 4;
repeated ParameterModel parameters = 5;
repeated string interfaces = 6;
}

View File

@ -82,6 +82,7 @@
"gulp-typescript": "^2.6.0",
"gulp-uglify": "^1.2.0",
"gulp-webserver": "^0.8.7",
"hash-files": "^1.0.0",
"html2jade": "^0.8.3",
"indent-string": "^1.2.1",
"jasmine": "2.3.1",

123
tools/build/proto.js Normal file
View File

@ -0,0 +1,123 @@
var fs = require('fs');
var glob = require('glob');
var hashFiles = require('hash-files');
var path = require('path');
var protocDetect = require('./protoc').detect;
var spawn = require('child_process').spawn;
// Hashs all .proto files at `config.dir`, calling `computedCallback` on each
// when done with the original file name and its hash.
// When all files have been hashed, calls `completeCallback` with no parameters.
function _hashProtosTask(config, computedCallback, completeCallback) {
var files = glob.sync(path.join(config.dir, '*.proto'));
var toUpdate = {};
var checkComplete = function() {
for (var key in toUpdate) {
if (toUpdate.hasOwnProperty(key) && toUpdate[key]) {
return false;
}
}
return true;
};
files.forEach(function(file) { toUpdate[file] = true; });
files.forEach(
function(file) {
hashFiles({
algorithm: config.algorithm || 'sha1',
files: [file]
}, function(error, hash) {
computedCallback(file, hash);
toUpdate[file] = false;
if (checkComplete()) {
completeCallback();
}
});
});
}
function _toPbDartExtension(path) {
return path.replace(/\.proto$/, '.pb.dart');
}
module.exports = {
// Generates `.pb.dart` files from all `.proto` files located at `config.dir`.
// This task requires the Dart protoc plugin, which is expected to reside at
// the path specified in `config.plugin`.
generate: function(config, done) {
// Note that while the Dart protoc plugin requires the Dart sdk, this task will be skipped if the
// Dart sdk is not available.
var protoc = protocDetect();
if (!protoc) {
done(new Error('Could not detect protoc - failed to rebuild Dart proto code.'));
return;
}
var protoPaths = glob.sync(path.join(config.dir, '*.proto'));
var spawnArgs = [
'--plugin', config.plugin,
'--proto_path', config.dir,
'--dart_out', config.dir,
].concat(protoPaths);
var proc = spawn(protoc.bin, spawnArgs, {
cwd: '.',
stdio: ['ignore', 2, 'inherit']
});
var failed = false;
var failWithError = function(msg) {
if (failed) return;
failed = true;
done(new Error('Failed while generating transformer boilerplate. Check for output above.\n' +
'Message: ' + msg + '\n' +
'Please run manually: ' + [protoc.bin].concat(spawnArgs).join(' ')));
};
proc.on('error', function(err) { failWithError(String(err)); });
proc.on('exit', function(code, signal) {
if (!code) {
var protocHash = hashFiles.sync({
algorithm: config.algorithm || 'sha1',
files: [config.plugin]
});
var computedCallback = function(fileName, hash) {
var pbDartPath = _toPbDartExtension(fileName);
var toAppend = '/**\n' +
' * Generated with:\n' +
' * ' + path.basename(fileName) + ' (' + hash + ')\n' +
' * ' + protoc.version + '\n' +
' * dart-protoc-plugin (' + protocHash + ')\n' +
' */\n';
fs.appendFileSync(pbDartPath, toAppend);
};
return _hashProtosTask(config, computedCallback, function() { done(); });
} else {
failWithError('Exit code was ' + code);
}
});
},
// Checks that the `.pb.dart` files located at `config.dir` are in sync with
// the `proto` files at the same directory.
// It does this by computing the hash of the `.proto` files and looking for
// that string in the contents of the associated `.pb.dart` file. If one or
// more file(s) do not have that hash present, this task will fail with a
// descriptive error message.
lint: function(config, done) {
var missing = [];
var computedCallback = function(filePath, hash) {
var pbDartPath = _toPbDartExtension(filePath);
if (String(fs.readFileSync(pbDartPath)).indexOf(hash) < 0) {
missing.push(' ' + hash + ' not found in ' + pbDartPath + '\n');
}
};
var completeCallback = function() {
if (missing.length == 0) {
done();
} else {
done(new Error(
'Generated Dart protobuf files are out of date. Please run `gulp gen_protos.dart`.\n' +
missing.join(''))
);
}
};
return _hashProtosTask(config, computedCallback, completeCallback);
}
};

4633
tools/build/protoc-gen-dart Executable file

File diff suppressed because it is too large Load Diff

18
tools/build/protoc.js Normal file
View File

@ -0,0 +1,18 @@
var which = require('which');
var spawnSync = require('child_process').spawnSync;
module.exports.detect = function() {
var PROTOC = false;
try {
var bin = 'protoc';
which.sync(bin);
var version = spawnSync(bin, ['--version']).stdout.toString().replace(/\n/g, '');
PROTOC = {
bin: bin,
version: version
};
} catch (e) {
// Ignore, just return `false` instead of an object.
}
return PROTOC;
};