Introduce dartanalyze into our build.
This creates a separate toplevel build group ("dartcheck"), and when invoked,
it will run dartanalyze on each source file in a dart_packaged_application
template in the gn files. Currently, we disable hints, warnings, and a
few specific hard errors so we can make progress here.
Long term I'd like to have this as part of the normal build, but currently
it takes roughly three and a half seconds to check each dart file for
errors.
BUG=459376
R=zra@google.com
Review URL: https://codereview.chromium.org/953953003
diff --git a/BUILD.gn b/BUILD.gn
index d23d33e..945e123 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -24,6 +24,24 @@
}
}
+# A group which invokes the dart checker on each dart package. This can take
+# multiple seconds per target, so we separate it out from the main building.
+# Eventually, we'd like this to be part of the default dart building, but for
+# now, to participate in checking, please add "{package_name}_analyze" to the
+# list of dependencies below.
+group("dartcheck") {
+ testonly = true
+ deps = [
+ "//examples/dart/console_example:console_example_analyze",
+ "//examples/dart/hello_world/hello:hello_analyze",
+ "//examples/dart/hello_world/world:world_analyze",
+ "//examples/dart/wget:wget_analyze",
+ "//services/dart/test/echo:echo_analyze",
+ "//services/dart/test/pingpong:pingpong_analyze",
+ "//services/dart/test/pingpong_target:pingpong_target_analyze",
+ ]
+}
+
# Deprecated name for the default build target.
group("root") {
testonly = true
diff --git a/README.md b/README.md
index 744c165..7fcaffb 100644
--- a/README.md
+++ b/README.md
@@ -150,6 +150,14 @@
If you see javac compile errors, make sure you have an up-to-date JDK:
https://code.google.com/p/chromium/wiki/AndroidBuildInstructions#Install_Java_JDK
+## Dart Code
+
+Because the dart analyzer is a bit slow, we don't run it unless the user specifically asks for it. To run the dart analyzer against the list of dart targets in the toplevel BUILD.gn file, run:
+
+```
+$ mojo/tools/mojob.py dartcheck
+```
+
## Googlers
If you're a Googler, you can use Goma, a distributed compiler service for open-source projects such as Chrome and Android. The instructions below assume that Goma is installed in the default location (~/goma).
diff --git a/mojo/public/dart/rules.gni b/mojo/public/dart/rules.gni
index 5845f2e..31cfd27 100644
--- a/mojo/public/dart/rules.gni
+++ b/mojo/public/dart/rules.gni
@@ -54,6 +54,7 @@
# dart_package targets.
template("dart_packaged_application") {
package_name = "${target_name}_package"
+ package_analyze_sources = "${target_name}_analyze"
package_output = "$target_out_dir/$package_name.dartzip"
if (defined(invoker.output_name)) {
@@ -62,6 +63,39 @@
mojo_output = "$root_out_dir/" + target_name + ".mojo"
}
+ action_foreach(package_analyze_sources) {
+ sources = invoker.sources
+
+ script = rebase_path("mojo/public/tools/dart_analyze.py", ".", mojo_root)
+
+ args = [
+ rebase_path(root_gen_dir),
+ rebase_path("$target_gen_dir/{{source_name_part}}.stamp"),
+ "{{source}}",
+ "--no-hints",
+ "--url-mapping=mojo:application,/" +
+ rebase_path("mojo/public/dart/application.dart", "/", mojo_root),
+ "--url-mapping=mojo:bindings,/" +
+ rebase_path("mojo/public/dart/bindings.dart", "/", mojo_root),
+ "--url-mapping=mojo:builtin,/" +
+ rebase_path("mojo/dart/embedder/builtin.dart", "/", mojo_root),
+ "--url-mapping=mojo:core,/" +
+ rebase_path("mojo/public/dart/core.dart", "/", mojo_root),
+ ]
+
+ if (defined(invoker.deps)) {
+ deps = invoker.deps
+ }
+
+ if (defined(invoker.datadeps)) {
+ datadeps = invoker.datadeps
+ }
+
+ outputs = [
+ "$target_gen_dir/{{source_name_part}}.stamp",
+ ]
+ }
+
dart_package(package_name) {
sources = invoker.sources
if (defined(invoker.deps)) {
@@ -89,6 +123,9 @@
]
deps = [
+ # TODO(erg): When dartanalyze runs at an acceptable speed, add
+ # ":$package_analyze_sources" as a dependency here and remove the
+ # manual group("check") in the toplevel build file.
":$package_name",
]
if (defined(invoker.deps)) {
diff --git a/mojo/public/dart/src/codec.dart b/mojo/public/dart/src/codec.dart
index 677dc10..b32cc81 100644
--- a/mojo/public/dart/src/codec.dart
+++ b/mojo/public/dart/src/codec.dart
@@ -132,7 +132,7 @@
void encodeUint8(int value, int offset) {
if (value < 0) {
- throw new MojoCodecError('$kErrorUnsigned: $val');
+ throw new MojoCodecError('$kErrorUnsigned: $value');
}
_buffer.buffer.setUint8(_base + offset, value);
}
@@ -142,7 +142,7 @@
void encodeUint16(int value, int offset) {
if (value < 0) {
- throw new MojoCodecError('$kErrorUnsigned: $val');
+ throw new MojoCodecError('$kErrorUnsigned: $value');
}
_buffer.buffer.setUint16(_base + offset, value, Endianness.LITTLE_ENDIAN);
}
@@ -152,7 +152,7 @@
void encodeUint32(int value, int offset) {
if (value < 0) {
- throw new MojoCodecError('$kErrorUnsigned: $val');
+ throw new MojoCodecError('$kErrorUnsigned: $value');
}
_buffer.buffer.setUint32(_base + offset, value, Endianness.LITTLE_ENDIAN);
}
@@ -162,7 +162,7 @@
void encodeUint64(int value, int offset) {
if (value < 0) {
- throw new MojoCodecError('$kErrorUnsigned: $val');
+ throw new MojoCodecError('$kErrorUnsigned: $value');
}
_buffer.buffer.setUint64(_base + offset, value, Endianness.LITTLE_ENDIAN);
}
@@ -483,10 +483,10 @@
void appendUint64Array(List<int> value) =>
appendBytes(new Uint8List.view(new Uint64List.fromList(value).buffer));
- void appendFloatArray(List<int> value) =>
+ void appendFloatArray(List<double> value) =>
appendBytes(new Uint8List.view(new Float32List.fromList(value).buffer));
- void appendDoubleArray(List<int> value) =>
+ void appendDoubleArray(List<double> value) =>
appendBytes(new Uint8List.view(new Float64List.fromList(value).buffer));
Encoder encoderForMap(int offset) {
diff --git a/mojo/public/dart/src/data_pipe.dart b/mojo/public/dart/src/data_pipe.dart
index 2af863f..9739be5 100644
--- a/mojo/public/dart/src/data_pipe.dart
+++ b/mojo/public/dart/src/data_pipe.dart
@@ -107,7 +107,7 @@
int read(ByteData data, [int numBytes = -1, int flags = 0]) {
if (handle == null) {
status = MojoResult.INVALID_ARGUMENT;
- return status;
+ return 0;
}
int data_numBytes = (numBytes == -1) ? data.lengthInBytes : numBytes;
@@ -115,7 +115,7 @@
handle.h, data, data_numBytes, flags);
if (result == null) {
status = MojoResult.INVALID_ARGUMENT;
- return status;
+ return 0;
}
assert((result is List) && (result.length == 2));
status = new MojoResult(result[0]);
diff --git a/mojo/public/tools/dart_analyze.py b/mojo/public/tools/dart_analyze.py
new file mode 100755
index 0000000..8b8117e
--- /dev/null
+++ b/mojo/public/tools/dart_analyze.py
@@ -0,0 +1,67 @@
+#!/usr/bin/python
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# To integrate dartanalyze with out build system, we take an input file, run
+# the analyzer on it, and write a stamp file if it passed.
+#
+# The first argument to this script is a reference to this build's gen
+# directory, which we treat as the package root. The second is the stamp file
+# to touch if we succeed. The rest are passed to the analyzer verbatim.
+
+import os
+import subprocess
+import sys
+import re
+
+_ANALYZING_PATTERN = re.compile(r'^Analyzing \[')
+_FINAL_REPORT_PATTERN = re.compile(r'^[0-9]+ errors and [0-9]+ warnings found.')
+
+_NATIVE_ERROR_PATTERN = re.compile(
+ r'^\[error\] Native functions can only be declared in the SDK and code that '
+ r'is loaded through native extensions')
+_WARNING_PATTERN = re.compile(r'^\[warning\]')
+_THAT_ONE_BROKEN_CLOSE_IN_WEB_SOCKETS_PATTERN = re.compile(
+ r'^\[error\] The name \'close\' is already defined')
+
+def main(args):
+ cmd = [
+ "../../third_party/dart-sdk/dart-sdk/bin/dartanalyzer",
+ ]
+
+ gen_dir = args.pop(0)
+ stamp_file = args.pop(0)
+ cmd.extend(args)
+ cmd.append("--package-root=%s" % gen_dir)
+
+ passed = True
+ try:
+ subprocess.check_output(cmd, shell=False, stderr=subprocess.STDOUT)
+ except subprocess.CalledProcessError as e:
+ # Perform post processing on the output. Filter out non-error messages and
+ # known problem patterns that we're working on.
+ raw_lines = e.output.split('\n')
+ # Remove the last empty line
+ raw_lines.pop()
+ filtered_lines = [i for i in raw_lines if (
+ not re.match(_ANALYZING_PATTERN, i) and
+ not re.match(_FINAL_REPORT_PATTERN, i) and
+ # TODO(erg): Remove the rest of these as fixes land:
+ not re.match(_WARNING_PATTERN, i) and
+ not re.match(_NATIVE_ERROR_PATTERN, i) and
+ not re.match(_THAT_ONE_BROKEN_CLOSE_IN_WEB_SOCKETS_PATTERN, i))]
+ for line in filtered_lines:
+ passed = False
+ print >> sys.stderr, line
+
+ if passed:
+ # We passed cleanly, so touch the stamp file so that we don't run again.
+ with open(stamp_file, 'a'):
+ os.utime(stamp_file, None)
+ return 0
+ else:
+ return -2
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/mojo/tools/mojob.py b/mojo/tools/mojob.py
index fb9e144..7f60900 100755
--- a/mojo/tools/mojob.py
+++ b/mojo/tools/mojob.py
@@ -112,6 +112,14 @@
return subprocess.call(['ninja', '-C', out_dir])
+def dartcheck(config):
+ """Runs the dart analyzer on code for the given config."""
+
+ out_dir = _get_out_dir(config)
+ print 'Checking dart code in %s ...' % out_dir
+ return subprocess.call(['ninja', '-C', out_dir, 'dartcheck'])
+
+
def _run_tests(config, test_types):
"""Runs the tests of the given type(s) for the given config."""
@@ -244,6 +252,10 @@
help='Run NaCl unit tests (does not build).')
nacltest_parser.set_defaults(func=nacltest)
+ dartcheck_parser = subparsers.add_parser('dartcheck', parents=[parent_parser],
+ help='Run the dart source code analyzer to check for warnings.')
+ dartcheck_parser.set_defaults(func=dartcheck)
+
args = parser.parse_args()
config = _args_to_config(args)
return args.func(config)