11b1da577f
For posix paths, replacing the file path during normalization and then swapping it back later works for both paths and URIs, because a file URI just contains the file path verbatim:
file:///foo/bar/baz
However that's not the case for Windows:
C:\foo\bar\baz
file:///c:/foo/bar/baz
So when normalizing, we need to know if we normalized a URI or a file path, so that we can reverse it later.
With this change, we'll use `{{workspaceFolder-0}}` for the URI, and `{{workspaceFolder-0:filePath}}` for the file path. Then when reversing, we can easily put the correct one back.
This also updates the log replace/scenarios to use the LogNormalizer to perform the denormalization so they don't have to have duplicated logic about what to restore.
I've also updated the existing committed scenarios (EDIT: moved this to a separate CL because Gerrit is falling over) - although even with those changes, they all fail for different reasons (invalid git hashes, mismatches in expected vs actual requests) so I think there is still more work to do here.
Fixes https://github.com/dart-lang/sdk/issues/63330
Change-Id: Ib4c4aabe2c7c0d089bd620bdf00de37acde25f52
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/501600
Reviewed-by: Keerti Parthasarathy <keertip@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
150 lines
4.7 KiB
Dart
150 lines
4.7 KiB
Dart
// Copyright (c) 2025, 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.
|
|
|
|
import 'dart:io' show Directory, exit;
|
|
|
|
import 'package:analysis_server/src/session_logger/log_normalizer.dart';
|
|
import 'package:analyzer/file_system/file_system.dart';
|
|
import 'package:analyzer/file_system/physical_file_system.dart';
|
|
import 'package:args/args.dart';
|
|
import 'package:cli_util/cli_util.dart';
|
|
import 'package:package_config/package_config.dart';
|
|
|
|
import '../performance/project_generator/project_generator.dart'
|
|
show ContextRoot, getContextRoots;
|
|
import 'log.dart';
|
|
|
|
Future<void> main(List<String> args) async {
|
|
var parsed = argParser.parse(args);
|
|
if (parsed.flag('help')) {
|
|
print(argParser.usage);
|
|
return;
|
|
}
|
|
|
|
var resourceProvider = PhysicalResourceProvider.INSTANCE;
|
|
var inputFile = resourceProvider.getFile(
|
|
Uri.base.resolve(parsed.option('input')!).toFilePath(),
|
|
);
|
|
if (!inputFile.exists) {
|
|
print('Input file ${inputFile.path} does not exist');
|
|
exit(1);
|
|
}
|
|
var outputFile = resourceProvider.getFile(
|
|
Uri.base.resolve(parsed.option('output')!).toFilePath(),
|
|
);
|
|
var rootDirPath = parsed.option('root-dir');
|
|
if (rootDirPath == null) {
|
|
print('Root directory not specified');
|
|
exit(1);
|
|
}
|
|
var rootDir = resourceProvider.getFolder(rootDirPath);
|
|
if (!rootDir.exists) {
|
|
print('Root directory $rootDirPath does not exist');
|
|
exit(1);
|
|
}
|
|
|
|
List<ContextRoot> contextRoots;
|
|
|
|
var packageConfigPath = parsed.option('package-config');
|
|
if (packageConfigPath != null) {
|
|
var packageConfigFile = resourceProvider.getFile(packageConfigPath);
|
|
if (!packageConfigFile.exists) {
|
|
print('Package config file $packageConfigPath does not exist');
|
|
exit(1);
|
|
}
|
|
var packageConfig = PackageConfig.parseBytes(
|
|
packageConfigFile.readAsBytesSync(),
|
|
packageConfigFile.toUri(),
|
|
);
|
|
|
|
contextRoots = [ContextRoot(Directory(rootDirPath), packageConfig)];
|
|
} else {
|
|
contextRoots = await getContextRoots(rootDirPath);
|
|
}
|
|
|
|
print('normalizing log at ${inputFile.path}');
|
|
var normalized = normalizeLog(inputFile, contextRoots);
|
|
outputFile.writeAsStringSync(normalized);
|
|
print('wrote normalized log to ${outputFile.path}');
|
|
|
|
var absFileMatches = normalized.allMatches('"file:///');
|
|
if (absFileMatches.isNotEmpty) {
|
|
print('found ${absFileMatches.length} absolute file paths remaining:');
|
|
}
|
|
for (var match in absFileMatches.take(5)) {
|
|
print('- ${match[0]}');
|
|
}
|
|
}
|
|
|
|
final argParser = ArgParser()
|
|
..addOption(
|
|
'input',
|
|
abbr: 'i',
|
|
help: 'The path to the input log to be normalized',
|
|
mandatory: true,
|
|
)
|
|
..addOption(
|
|
'output',
|
|
abbr: 'o',
|
|
help: 'The path output the normalized log to',
|
|
mandatory: true,
|
|
)
|
|
..addOption(
|
|
'root-dir',
|
|
abbr: 'r',
|
|
help: 'The path to the root directory for normalizing package paths',
|
|
mandatory: true,
|
|
)
|
|
..addOption(
|
|
'package-config',
|
|
abbr: 'p',
|
|
help:
|
|
'The path to the package config file, if specified, will be used '
|
|
'instead of inferring it from the workspace directories.',
|
|
)
|
|
..addFlag('help', abbr: 'h', help: 'Prints the usage text');
|
|
|
|
/// Reads an [input] log file, and attempts to normalize it so that it can work
|
|
/// across multiple environments.
|
|
///
|
|
/// Specifically, this:
|
|
/// - Replaces all workspace folder paths with `{{workspaceFolder-[i]}}`
|
|
/// placeholders.
|
|
/// - Replaces the Dart SDK root with `{{dartSdkRoot}}`.
|
|
/// - Replaces all package roots with `{{package-root:[package-name]}}`.
|
|
///
|
|
/// Returns the new file contents after normalization.
|
|
//
|
|
// TODO(somebody): Support legacy protocol.
|
|
//
|
|
// TODO(somebody): Don't take a package config, instead infer them from the
|
|
// workspace directories.
|
|
String normalizeLog(File input, List<ContextRoot> contextRoots) {
|
|
var normalizer = LogNormalizer();
|
|
|
|
var content = input.readAsStringSync();
|
|
var original = Log.fromString(content);
|
|
var initializeMessage = original.entries.firstWhere(
|
|
(log) => log.isMessage && log.message.isInitializeRequest,
|
|
);
|
|
|
|
// Add all the workspace + root paths from the initialize message.
|
|
normalizer.addLspWorkspaceReplacements(initializeMessage.message);
|
|
|
|
// Additionally add SDKs and packages.
|
|
normalizer.addReplacementsForPath(sdkPath, 'dartSdkRoot');
|
|
// TODO(somebody): replace {{flutterSdkRoot}} with the flutter SDK path
|
|
for (var i = 0; i < contextRoots.length; i++) {
|
|
for (var package in contextRoots[i].packageConfig.packages) {
|
|
normalizer.addReplacementsForUri(
|
|
package.root,
|
|
'context-$i:package-root:${package.name}',
|
|
);
|
|
}
|
|
}
|
|
|
|
// Now normalize the full content.
|
|
return normalizer.normalize(content);
|
|
}
|