Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Test that clangd --check works at HEAD. #50901

Merged
merged 13 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ci/builders/linux_unopt.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@
"flutter/shell/testing/observatory/empty_main.dart"
]
},
{
"language": "dart",
"name": "test: clangd",
"script": "flutter/tools/clangd_check/bin/main.dart"
},
{
"language": "dart",
"name": "test: Lint android host",
Expand Down
5 changes: 5 additions & 0 deletions ci/builders/mac_unopt.json
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,11 @@
"ios_debug_unopt_sim_arm64_extension_safe"
],
"script": "flutter/testing/scenario_app/run_ios_tests.sh"
},
{
"language": "dart",
"name": "test: clangd",
"script": "flutter/tools/clangd_check/bin/main.dart"
}
]
}
Expand Down
27 changes: 27 additions & 0 deletions tools/clangd_check/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# `clangd_check`

`clangd_check` is a tool to run clangd on a codebase and check for diagnostics.

The practical use of this tool is intentionally limited; it's designed to
provide a quick way to verify that `clangd` is able to parse and analyze a
C++ codebase.

## Usage

```sh
dart ./tools/clangd_check/bin/main.dart
```

On success, and with no diagnostics, `clangd_check` will exit with status 0.

By default, `clangd_check` will try to infer the path of `clangd`, as well as
the path to `--compile-commands-dir` based on what artifacts are present in
`$ENGINE/src/out`.

You can also specify the path to `clangd` and `--compile-commands-dir` manually:

```sh
dart ./tools/clangd_check/bin/main.dart \
--clangd ../buildtools/mac-arm64/clang/bin/clangd \
--compile-commands-dir ../out/host_Debug_unopt_arm64
```
75 changes: 75 additions & 0 deletions tools/clangd_check/bin/main.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import 'dart:convert';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Needs the engine copyright header.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

import 'dart:io' as io;

import 'package:args/args.dart';
import 'package:engine_repo_tools/engine_repo_tools.dart';
import 'package:path/path.dart' as p;

void main(List<String> args) {
final Engine? engine = Engine.tryFindWithin();
final ArgParser parser = ArgParser()
..addOption(
'clangd',
help: 'Path to clangd. Defaults to deriving the path from compile_commands.json.',
)
..addOption(
'compile-commands-dir',
help: 'Path to a directory containing compile_commands.json.',
defaultsTo: engine?.latestOutput()?.compileCommandsJson.parent.path,
);
final ArgResults results = parser.parse(args);

final String? compileCommandsDir = results['compile-commands-dir'] as String?;
if (compileCommandsDir == null) {
io.stderr.writeln('Must provide a path to compile_commands.json');
io.exitCode = 1;
return;
}
final io.File compileCommandsFile = io.File(p.join(compileCommandsDir, 'compile_commands.json'));
if (!compileCommandsFile.existsSync()) {
io.stderr.writeln('No compile_commands.json found in $compileCommandsDir');
io.exitCode = 1;
return;
}

final List<Object?> compileCommands = json.decode(compileCommandsFile.readAsStringSync()) as List<Object?>;
if (compileCommands.isEmpty) {
io.stderr.writeln('Unexpected: compile_commands.json is empty');
io.exitCode = 1;
return;
}

String? clangd = results['clangd'] as String?;
final Map<String, Object?> entry = compileCommands.first! as Map<String, Object?>;
final String checkFile;
if (entry case {
'command': final String path,
'file': final String file,
}) {
checkFile = p.split(file).skip(3).join(p.separator);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments here about what's being skipped would be helpful.

clangd ??= p.join(p.dirname(p.dirname(path.split(' ').first)), 'clang', 'bin', 'clangd');
} else {
io.stderr.writeln('Unexpected: compile_commands.json has an unexpected format');
io.stderr.writeln('First entry: ${const JsonEncoder.withIndent(' ').convert(entry)}');
io.exitCode = 1;
return;
}

// Run clangd.
final io.ProcessResult result = io.Process.runSync(clangd, <String>[
'--compile-commands-dir',
compileCommandsDir,
'--check=$checkFile',
]);
io.stdout.write(result.stdout);
io.stderr.write(result.stderr);
if ((result.stderr as String).contains('Path specified by --compile-commands-dir does not exist')) {
io.stdout.writeln('clangd_check failed: --compile-commands-dir does not exist');
io.exitCode = 1;
} else if ((result.stderr as String).contains('Failed to resolve path')) {
io.stdout.writeln('clangd_check failed: --check file does not exist');
io.exitCode = 1;
} else {
io.exitCode = result.exitCode;
}
}
68 changes: 68 additions & 0 deletions tools/clangd_check/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Copyright 2013 The Flutter Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

name: clangd_check
publish_to: none

# Do not add any dependencies that require more than what is provided in
# //third_party/dart/pkg or //third_party/dart/third_party/pkg.
# In particular, package:test is not usable here.

# If you do add packages here, make sure you can run `pub get --offline`, and
# check the .packages and .package_config to make sure all the paths are
# relative to this directory into //third_party/dart

environment:
sdk: '>=3.2.0-0 <4.0.0'

dependencies:
args: any
engine_repo_tools: any
path: any
source_span: any

dev_dependencies:
async_helper: any
expect: any
litetest: any
process_fakes: any
smith: any

dependency_overrides:
args:
path: ../../../third_party/dart/third_party/pkg/args
async:
path: ../../../third_party/dart/third_party/pkg/async
async_helper:
path: ../../../third_party/dart/pkg/async_helper
collection:
path: ../../../third_party/dart/third_party/pkg/collection
engine_repo_tools:
path: ../pkg/engine_repo_tools
expect:
path: ../../../third_party/dart/pkg/expect
file:
path: ../../../third_party/dart/third_party/pkg/file/packages/file
git_repo_tools:
path: ../pkg/git_repo_tools
litetest:
path: ../../testing/litetest
meta:
path: ../../../third_party/dart/pkg/meta
path:
path: ../../../third_party/dart/third_party/pkg/path
platform:
path: ../../third_party/pkg/platform
process:
path: ../../third_party/pkg/process
process_fakes:
path: ../pkg/process_fakes
process_runner:
path: ../../third_party/pkg/process_runner
smith:
path: ../../../third_party/dart/pkg/smith
source_span:
path: ../../../third_party/dart/third_party/pkg/source_span
term_glyph:
path: ../../../third_party/dart/third_party/pkg/term_glyph
1 change: 1 addition & 0 deletions tools/pub_get_offline.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
os.path.join(ENGINE_DIR, 'tools', 'api_check'),
os.path.join(ENGINE_DIR, 'tools', 'build_bucket_golden_scraper'),
os.path.join(ENGINE_DIR, 'tools', 'clang_tidy'),
os.path.join(ENGINE_DIR, 'tools', 'clangd_check'),
os.path.join(ENGINE_DIR, 'tools', 'compare_goldens'),
os.path.join(ENGINE_DIR, 'tools', 'const_finder'),
os.path.join(ENGINE_DIR, 'tools', 'dir_contents_diff'),
Expand Down