blob: 2dcac0112a53440358be9c27555250a8beb2db17 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'dart:convert';
import 'dart:io';
import 'package:dart_strict_deps_lib/file_processor.dart' as file_processor;
import 'package:meta/meta.dart';
import 'package:dart_strict_deps_lib/dependency_check.dart' as dependency_check;
import 'package:args/args.dart';
import 'package:collection/collection.dart';
import 'package:path/path.dart' as p;
import 'package:tools.dart-strict-deps.dart_strict_deps_proto/protos/models.pb.dart';
import 'package:package_config/package_config.dart';
p.Context context = p.Context(style: p.Style.posix);
/// Writes serialized TargetCheckResult to file
void writeOutfile(String file, TargetCheckResult checkResult) {
File(file).writeAsString(jsonEncode(checkResult.toProto3Json()));
}
// Returns string [s] with [indents] number of spaces in front.
String indentedString(String s, {@required int indents}) {
return s.padLeft(s.length + indents, ' ');
}
/// Represents a flattened out structure for the parsed data.
///
/// This is used for easier formatting of output.
class FlatResult {
// File path of the importing file
final String filePath;
// Import statement being represented
final ImportResult importResult;
// Whether the package of import has been imported or not
final bool knownPackage;
FlatResult(this.filePath, this.importResult, {this.knownPackage});
}
/// Prints file level warnings to stdout.
void printFileWarnings(String fileName, List<FlatResult> flatResults) {
final fileState = flatResults[0].importResult.state;
if (fileState == ImportResult_State.MISSING_FROM_PACKAGE_SOURCES) {
print(indentedString(
'File `$fileName` was not found in package sources list. Used here:',
indents: 2));
} else if (fileState == ImportResult_State.FILE_MISSING) {
print(indentedString(
'File `$fileName` does not exist on the filesystem. Used here:',
indents: 2));
} else {
print(indentedString('File `$fileName` could not be found. Used here:',
indents: 2));
}
for (var importer in flatResults) {
print(indentedString(
'Line ${importer.importResult.lineInfo}: ${importer.filePath}',
indents: 4));
}
}
/// Prints package level warnings to stdout.
void printPackageWarnings(MapEntry packageResult) {
Map<String, List<FlatResult>> byFile = groupBy(packageResult.value,
(packageResult) => packageResult.importResult.resolvedLocation);
if (packageResult.value[0].importResult.isDartPackageImported) {
print(
'Package ${packageResult.key} imported directly and errors found with files imported');
} else if (packageResult.value[0].knownPackage) {
print(
'Package ${packageResult.key} imported transitively and errors found with files imported from it:');
} else {
print(
'Package ${packageResult.key} was not a GN dep but files were imported from it:');
}
byFile.forEach(printFileWarnings);
}
/// Finds and prints warnings to stdout.
void printWarnings(
{TargetCheckResult targetCheckResult,
String outFile,
PackageConfig resolver}) {
final knownPackages =
resolver.packages.map((package) => package.name).toList();
final flatResults = targetCheckResult.files.expand((file) => file.imports.map(
(importResult) => FlatResult(file.filePath, importResult,
knownPackage: knownPackages.contains(importResult.dartPackage))));
final notFoundResults = flatResults
.where((result) => result.importResult.state != ImportResult_State.FOUND);
final notFoundByPackage =
groupBy(notFoundResults, (result) => result.importResult.dartPackage);
notFoundByPackage.entries.forEach(printPackageWarnings);
if (notFoundByPackage.entries.isNotEmpty) {
exitCode = -1;
}
}
void main(List<String> args) async {
var parser = ArgParser()
..addOption('metadata-file', help: 'collected metadata file from GN')
..addOption('output-file', help: 'output file to write result to')
..addOption('packages-file',
help: 'packages file to read dependency info from');
var parsedArgs = parser.parse(args);
String inputFile = parsedArgs['metadata-file'];
String outFile = parsedArgs['output-file'];
String packagesFile = parsedArgs['packages-file'];
PackageConfig resolver = await loadPackageConfig(File(packagesFile).absolute);
String inputFileContents = File(inputFile).readAsStringSync();
final targetCheckResult = dependency_check.checkBuildInfo(
buildInfo: file_processor.buildInfoFromJson(inputFileContents),
resolver: resolver);
printWarnings(
targetCheckResult: targetCheckResult,
outFile: outFile,
resolver: resolver);
writeOutfile(outFile, targetCheckResult);
}