@@ -21,6 +21,7 @@ import 'dart:io'
2121 stdout;
2222
2323import 'package:args/args.dart' ;
24+ import 'package:path/path.dart' as path;
2425
2526Platform defaultPlatform = Platform ();
2627
@@ -78,7 +79,7 @@ class ProcessRunner {
7879 Future <ProcessRunnerResult > runProcess (
7980 List <String > commandLine, {
8081 Directory workingDirectory,
81- bool printOutput = true ,
82+ bool printOutput = false ,
8283 bool failOk = false ,
8384 Stream <List <int >> stdin,
8485 }) async {
@@ -209,8 +210,7 @@ class ProcessPool {
209210 final String total = totalJobs.toString ().padRight (3 );
210211 final String inProgress = inProgressJobs.length.toString ().padLeft (2 );
211212 final String pending = pendingJobs.length.toString ().padLeft (3 );
212- stdout.write (
213- 'Jobs: $percent % done, $completed /$total completed, $inProgress in '
213+ stdout.write ('Jobs: $percent % done, $completed /$total completed, $inProgress in '
214214 'progress, $pending pending. \r ' );
215215 }
216216
@@ -279,16 +279,17 @@ https://github.com/flutter/flutter/wiki/Engine-Clang-Tidy-Linter
279279''' ;
280280
281281class Command {
282- String directory = '' ;
282+ Directory directory = Directory ( '' ) ;
283283 String command = '' ;
284- String file = '' ;
284+ File file = File ( '' ) ;
285285}
286286
287287Command parseCommand (Map <String , dynamic > map) {
288+ final Directory dir = Directory (map['directory' ] as String ).absolute;
288289 return Command ()
289- ..directory = map[ 'directory' ] as String
290+ ..directory = dir
290291 ..command = map['command' ] as String
291- ..file = map['file' ] as String ;
292+ ..file = File (path. normalize (path. join (dir.path, map['file' ] as String ))) ;
292293}
293294
294295String calcTidyArgs (Command command) {
@@ -308,61 +309,52 @@ String calcTidyPath(Command command) {
308309
309310bool isNonEmptyString (String str) => str.isNotEmpty;
310311
311- bool containsAny (String str, List <String > queries) {
312- for (String query in queries) {
313- if (str.contains (query)) {
314- return true ;
315- }
316- }
317- return false ;
312+ bool containsAny (File file, Iterable <File > queries) {
313+ return queries.where ((File query) => path.equals (query.path, file.path)).isNotEmpty;
318314}
319315
320316/// Returns a list of all files with current changes or differ from `master` .
321- List <String > getListOfChangedFiles (String repoPath) {
322- final Set <String > result = < String > {};
323- final ProcessResult diffResult =
324- Process .runSync ('git' , < String > ['diff' , '--name-only' ], workingDirectory: repoPath);
325- final ProcessResult diffCachedResult = Process .runSync (
326- 'git' , < String > ['diff' , '--cached' , '--name-only' ],
327- workingDirectory: repoPath);
328-
317+ Future <List <File >> getListOfChangedFiles (Directory repoPath) async {
318+ final ProcessRunner processRunner = ProcessRunner (defaultWorkingDirectory: repoPath);
319+ String branch = 'upstream/master' ;
329320 final ProcessResult fetchResult = Process .runSync ('git' , < String > ['fetch' , 'upstream' , 'master' ]);
330321 if (fetchResult.exitCode != 0 ) {
322+ branch = 'origin/master' ;
331323 Process .runSync ('git' , < String > ['fetch' , 'origin' , 'master' ]);
332324 }
333- final ProcessResult mergeBaseResult = Process .runSync (
334- 'git' , < String > ['merge-base' , '--fork-point' , 'FETCH_HEAD' , 'HEAD' ],
335- workingDirectory: repoPath);
336- final String mergeBase = mergeBaseResult.stdout.trim () as String ;
337- final ProcessResult masterResult = Process .runSync (
338- 'git' , < String > ['diff' , '--name-only' , mergeBase],
339- workingDirectory: repoPath);
340- result.addAll (diffResult.stdout.split ('\n ' ).where (isNonEmptyString) as Iterable <String >);
341- result.addAll (diffCachedResult.stdout.split ('\n ' ).where (isNonEmptyString) as Iterable <String >);
342- result.addAll (masterResult.stdout.split ('\n ' ).where (isNonEmptyString) as Iterable <String >);
343- return result.toList ();
325+ final Set <String > result = < String > {};
326+ final ProcessRunnerResult diffResult = await processRunner.runProcess (
327+ < String > ['git' , 'diff' , '--name-only' , '--diff-filter=ACMRT' , if (branch.isNotEmpty) branch]);
328+
329+ result.addAll (utf8.decode (diffResult.stdout).split ('\n ' ).where (isNonEmptyString));
330+ return result.map <File >((String filePath) => File (path.join (repoPath.path, filePath))).toList ();
344331}
345332
346- Future <List <String >> dirContents (String repoPath) {
347- final Directory dir = Directory (repoPath);
348- final List <String > files = < String > [];
349- final Completer <List <String >> completer = Completer <List <String >>();
333+ Future <List <File >> dirContents (Directory dir) {
334+ final List <File > files = < File > [];
335+ final Completer <List <File >> completer = Completer <List <File >>();
350336 final Stream <FileSystemEntity > lister = dir.list (recursive: true );
351- lister.listen ((FileSystemEntity file) => files.add (file.path),
352- // should also register onError
353- onDone: () => completer.complete (files));
337+ lister.listen ((FileSystemEntity file) => file is File ? files.add (file) : null ,
338+ onError: (Object e) => completer.completeError (e), onDone: () => completer.complete (files));
354339 return completer.future;
355340}
356341
357- Future <bool > shouldIgnoreFile (String path) async {
358- if (path.contains ('/third_party/' )) {
342+ File buildFileAsRepoFile (String buildFile, Directory repoPath) {
343+ // Removes the "../../flutter" from the build files to make it relative to the flutter
344+ // dir.
345+ final String relativeBuildFile = path.joinAll (path.split (buildFile).sublist (3 ));
346+ final File result = File (path.join (repoPath.absolute.path, relativeBuildFile));
347+ print ('Build file: $buildFile => ${result .path }' );
348+ return result;
349+ }
350+
351+ Future <bool > shouldIgnoreFile (File file) async {
352+ if (path.split (file.path).contains ('third_party' )) {
359353 return true ;
360354 } else {
361355 final RegExp exp = RegExp (r'//.*FLUTTER_NOLINT' );
362- await for (String line in File (path.substring (6 ))
363- .openRead ()
364- .transform (utf8.decoder)
365- .transform (const LineSplitter ())) {
356+ await for (String line
357+ in file.openRead ().transform (utf8.decoder).transform (const LineSplitter ())) {
366358 if (exp.hasMatch (line)) {
367359 return true ;
368360 } else if (line.isNotEmpty && line[0 ] != '\n ' && line[0 ] != '/' ) {
@@ -376,8 +368,8 @@ Future<bool> shouldIgnoreFile(String path) async {
376368}
377369
378370void _usage (ArgParser parser) {
379- print ('lint.dart [--help] [--lint-all] [--verbose] [--diff-branch]' );
380- print (parser.usage);
371+ stderr. writeln ('lint.dart [--help] [--lint-all] [--verbose] [--diff-branch]' );
372+ stderr. writeln (parser.usage);
381373 exit (0 );
382374}
383375
@@ -405,14 +397,24 @@ void main(List<String> arguments) async {
405397 _usage (parser);
406398 }
407399
408- final String buildCommandsPath = options['compile-commands' ] as String ;
409- final String repoPath = options['repo' ] as String ;
400+ final File buildCommandsPath = File (options['compile-commands' ] as String );
401+ if (! buildCommandsPath.existsSync ()) {
402+ stderr.writeln ("Build commands path ${buildCommandsPath .absolute .path } doesn't exist." );
403+ _usage (parser);
404+ }
405+
406+ final Directory repoPath = Directory (options['repo' ] as String );
407+ if (! repoPath.existsSync ()) {
408+ stderr.writeln ("Repo path ${repoPath .absolute .path } doesn't exist." );
409+ _usage (parser);
410+ }
411+
410412 final String checksArg = options.wasParsed ('checks' ) ? options['checks' ] as String : '' ;
411413 final String checks = checksArg.isNotEmpty ? '--checks=$checksArg ' : '--config=' ;
412414 final bool lintAll =
413415 Platform .environment['FLUTTER_LINT_ALL' ] != null || options['lint-all' ] as bool ;
414- final List <String > changedFiles =
415- lintAll ? await dirContents (repoPath) : getListOfChangedFiles (repoPath);
416+ final List <File > changedFiles =
417+ lintAll ? await dirContents (repoPath) : await getListOfChangedFiles (repoPath);
416418
417419 if (verbose) {
418420 print ('Checking lint in repo at $repoPath .' );
@@ -427,7 +429,7 @@ void main(List<String> arguments) async {
427429 }
428430
429431 final List <dynamic > buildCommandMaps =
430- jsonDecode (await File ( buildCommandsPath) .readAsString ()) as List <dynamic >;
432+ jsonDecode (await buildCommandsPath.readAsString ()) as List <dynamic >;
431433 final List <Command > buildCommands = buildCommandMaps
432434 .map <Command >((dynamic x) => parseCommand (x as Map <String , dynamic >))
433435 .toList ();
@@ -448,11 +450,11 @@ void main(List<String> arguments) async {
448450 for (Command command in changedFileBuildCommands) {
449451 if (! (await shouldIgnoreFile (command.file))) {
450452 final String tidyArgs = calcTidyArgs (command);
451- final List <String > args = < String > [command.file, checks, '--' ];
453+ final List <String > args = < String > [command.file.path , checks, '--' ];
452454 args.addAll (tidyArgs? .split (' ' ) ?? < String > []);
453455 print ('🔶 linting ${command .file }' );
454- jobs.add (WorkerJob (command.file, < String > [tidyPath, ...args],
455- workingDirectory: Directory ( command.directory) ));
456+ jobs.add (WorkerJob (command.file.path , < String > [tidyPath, ...args],
457+ workingDirectory: command.directory));
456458 } else {
457459 print ('🔷 ignoring ${command .file }' );
458460 }
@@ -468,5 +470,8 @@ void main(List<String> arguments) async {
468470 print (utf8.decode (results[job].stdout));
469471 exitCode = 1 ;
470472 }
473+ if (exitCode == 0 ) {
474+ print ('No lint problems found.' );
475+ }
471476 exit (exitCode);
472477}
0 commit comments