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

Commit b917b24

Browse files
authored
Test that clangd --check works at HEAD. (#50901)
Closes flutter/flutter#141641. Basically, this should verify "it's possible to use VSCode+LSC, at least in theory". I'm open to writing a test, but given it _is_ a test I'm less sure it's valuable. Feel free to push back.
1 parent 1c08774 commit b917b24

File tree

9 files changed

+320
-0
lines changed

9 files changed

+320
-0
lines changed

.ci.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,12 @@ targets:
277277
- ci/**
278278
- flutter_frontend_server/**
279279

280+
- name: Linux clangd
281+
recipe: engine_v2/engine_v2
282+
bringup: true
283+
properties:
284+
config_name: linux_unopt_debug_no_rbe
285+
280286
- name: Linux linux_unopt
281287
recipe: engine_v2/engine_v2
282288
timeout: 120
@@ -362,6 +368,12 @@ targets:
362368
drone_dimensions:
363369
- os=Mac-13
364370

371+
- name: Mac clangd
372+
recipe: engine_v2/engine_v2
373+
bringup: true
374+
properties:
375+
config_name: mac_unopt_debug_no_rbe
376+
365377
- name: Mac mac_unopt
366378
recipe: engine_v2/engine_v2
367379
properties:

.clangd

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Used to allow clangd to run on CI platforms as part of //tools/clangd_check.
2+
#
3+
# Intended to have no effect elsewhere.
4+
#
5+
# See also:
6+
# - https://clangd.llvm.org/config#compileflags
7+
# - https://github.com/clangd/clangd/issues/662
8+
CompileFlags:
9+
Add: -Wno-unknown-warning-option
10+
Remove: [-m*, -f*]
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"_comment": [
3+
"This build is only used to generate compile_commands.json for clangd ",
4+
"and should not be used for any other purpose."
5+
],
6+
"builds": [
7+
{
8+
"drone_dimensions": ["device_type=none", "os=Linux", "cores=32"],
9+
"gn": [
10+
"--runtime-mode",
11+
"debug",
12+
"--unoptimized",
13+
"--prebuilt-dart-sdk",
14+
"--no-rbe",
15+
"--no-goma"
16+
],
17+
"name": "linux_unopt_debug_no_rbe",
18+
"ninja": {
19+
"config": "linux_unopt_debug_no_rbe",
20+
"targets": ["flutter/build/dart:copy_dart_sdk"]
21+
},
22+
"tests": [
23+
{
24+
"language": "dart",
25+
"name": "clangd",
26+
"script": "flutter/tools/clangd_check/bin/main.dart",
27+
"parameters": ["--clangd=buildtools/linux-x64/clang/bin/clangd"]
28+
}
29+
]
30+
}
31+
]
32+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"_comment": [
3+
"This build is only used to generate compile_commands.json for clangd ",
4+
"and should not be used for any other purpose."
5+
],
6+
"builds": [
7+
{
8+
"drone_dimensions": [
9+
"device_type=none",
10+
"os=Mac-13",
11+
"cpu=x86",
12+
"mac_model=Macmini8,1"
13+
],
14+
"gn": [
15+
"--runtime-mode",
16+
"debug",
17+
"--unoptimized",
18+
"--prebuilt-dart-sdk",
19+
"--no-rbe",
20+
"--no-goma",
21+
"--xcode-symlinks"
22+
],
23+
"name": "mac_unopt_debug_no_rbe",
24+
"ninja": {
25+
"config": "mac_unopt_debug_no_rbe",
26+
"targets": ["flutter/build/dart:copy_dart_sdk"]
27+
},
28+
"tests": [
29+
{
30+
"language": "dart",
31+
"name": "clangd",
32+
"script": "flutter/tools/clangd_check/bin/main.dart",
33+
"parameters": ["--clangd=buildtools/mac-x64/clang/bin/clangd"]
34+
}
35+
]
36+
}
37+
]
38+
}

ci/licenses_golden/licenses_flutter

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41993,6 +41993,7 @@ ORIGIN: ../../../flutter/vulkan/vulkan_utilities.h + ../../../flutter/LICENSE
4199341993
ORIGIN: ../../../flutter/vulkan/vulkan_window.cc + ../../../flutter/LICENSE
4199441994
ORIGIN: ../../../flutter/vulkan/vulkan_window.h + ../../../flutter/LICENSE
4199541995
TYPE: LicenseType.bsd
41996+
FILE: ../../../flutter/.clangd
4199641997
FILE: ../../../flutter/.pylintrc
4199741998
FILE: ../../../flutter/assets/asset_manager.cc
4199841999
FILE: ../../../flutter/assets/asset_manager.h

tools/clangd_check/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# `clangd_check`
2+
3+
`clangd_check` is a tool to run clangd on a codebase and check for diagnostics.
4+
5+
The practical use of this tool is intentionally limited; it's designed to
6+
provide a quick way to verify that `clangd` is able to parse and analyze a
7+
C++ codebase.
8+
9+
## Usage
10+
11+
```sh
12+
dart ./tools/clangd_check/bin/main.dart
13+
```
14+
15+
On success, and with no diagnostics, `clangd_check` will exit with status 0.
16+
17+
By default, `clangd_check` will try to infer the path of `clangd`, as well as
18+
the path to `--compile-commands-dir` based on what artifacts are present in
19+
`$ENGINE/src/out`.
20+
21+
You can also specify the path to `clangd` and `--compile-commands-dir` manually:
22+
23+
```sh
24+
dart ./tools/clangd_check/bin/main.dart \
25+
--clangd ../buildtools/mac-arm64/clang/bin/clangd \
26+
--compile-commands-dir ../out/host_Debug_unopt_arm64
27+
```

tools/clangd_check/bin/main.dart

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:convert';
6+
import 'dart:io' as io;
7+
8+
import 'package:args/args.dart';
9+
import 'package:engine_repo_tools/engine_repo_tools.dart';
10+
import 'package:path/path.dart' as p;
11+
12+
void main(List<String> args) {
13+
final Engine? engine = Engine.tryFindWithin();
14+
final ArgParser parser = ArgParser()
15+
..addFlag(
16+
'help',
17+
abbr: 'h',
18+
help: 'Print this usage information.',
19+
negatable: false,
20+
)
21+
..addOption(
22+
'clangd',
23+
help: 'Path to clangd. Defaults to deriving the path from compile_commands.json.',
24+
)
25+
..addOption(
26+
'compile-commands-dir',
27+
help: 'Path to a directory containing compile_commands.json.',
28+
defaultsTo: engine?.latestOutput()?.compileCommandsJson.parent.path,
29+
);
30+
final ArgResults results = parser.parse(args);
31+
if (results['help'] as bool) {
32+
io.stdout.writeln(parser.usage);
33+
return;
34+
}
35+
36+
final String? compileCommandsDir = results['compile-commands-dir'] as String?;
37+
if (compileCommandsDir == null) {
38+
io.stderr.writeln('Must provide a path to compile_commands.json');
39+
io.exitCode = 1;
40+
return;
41+
}
42+
final io.File compileCommandsFile = io.File(p.join(compileCommandsDir, 'compile_commands.json'));
43+
if (!compileCommandsFile.existsSync()) {
44+
io.stderr.writeln('No compile_commands.json found in $compileCommandsDir');
45+
io.exitCode = 1;
46+
return;
47+
}
48+
49+
final List<Object?> compileCommands = json.decode(compileCommandsFile.readAsStringSync()) as List<Object?>;
50+
if (compileCommands.isEmpty) {
51+
io.stderr.writeln('Unexpected: compile_commands.json is empty');
52+
io.exitCode = 1;
53+
return;
54+
}
55+
56+
String? clangd = results['clangd'] as String?;
57+
final Map<String, Object?> entry = compileCommands.first! as Map<String, Object?>;
58+
final String checkFile;
59+
if (entry case {
60+
'command': final String command,
61+
'file': final String file,
62+
}) {
63+
// Given a path like ../../flutter/foo.cc, we want to check foo.cc.
64+
checkFile = p.split(file).skip(3).join(p.separator);
65+
// On CI, the command path is different from the local path.
66+
// Find the engine root and derive the clangd path from there.
67+
if (clangd == null) {
68+
// Strip the command to find the path to the engine root.
69+
// i.e. "command": "/path/to/engine/src/... arg1 arg2 ..."
70+
//
71+
// This now looks like "../../flutter/buildtools/{platform}/{...}"
72+
final String commandPath = p.dirname(command.split(' ').first);
73+
74+
// Find the canonical path to the command (i.e. resolve "../" and ".")
75+
//
76+
// This now looks like "/path/to/engine/src/flutter/buildtools/{platform}/{...}"
77+
final String path = p.canonicalize(
78+
p.join(compileCommandsDir, commandPath),
79+
);
80+
81+
// Extract which platform we're building for (e.g. linux-x64, mac-arm64, mac-x64).
82+
final String platform = RegExp(
83+
r'buildtools/([^/]+)/',
84+
).firstMatch(path)!.group(1)!;
85+
86+
// Find the engine root and derive the clangd path from there.
87+
final Engine compileCommandsEngineRoot = Engine.findWithin(path);
88+
clangd = p.join(
89+
// engine/src/flutter
90+
compileCommandsEngineRoot.flutterDir.path,
91+
// buildtools
92+
'buildtools',
93+
// {platform}
94+
platform,
95+
// clangd
96+
'clang',
97+
'bin',
98+
'clangd',
99+
);
100+
}
101+
} else {
102+
io.stderr.writeln('Unexpected: compile_commands.json has an unexpected format');
103+
io.stderr.writeln('First entry: ${const JsonEncoder.withIndent(' ').convert(entry)}');
104+
io.exitCode = 1;
105+
return;
106+
}
107+
108+
// Run clangd.
109+
try {
110+
final io.ProcessResult result = io.Process.runSync(clangd, <String>[
111+
'--compile-commands-dir',
112+
compileCommandsDir,
113+
'--check=$checkFile',
114+
]);
115+
io.stdout.write(result.stdout);
116+
io.stderr.write(result.stderr);
117+
if ((result.stderr as String).contains('Path specified by --compile-commands-dir does not exist')) {
118+
io.stdout.writeln('clangd_check failed: --compile-commands-dir does not exist');
119+
io.exitCode = 1;
120+
} else if ((result.stderr as String).contains('Failed to resolve path')) {
121+
io.stdout.writeln('clangd_check failed: --check file does not exist');
122+
io.exitCode = 1;
123+
} else {
124+
io.exitCode = result.exitCode;
125+
}
126+
} on io.ProcessException catch (e) {
127+
io.stderr.writeln('Failed to run clangd: $e');
128+
io.stderr.writeln(const JsonEncoder.withIndent(' ').convert(entry));
129+
io.exitCode = 1;
130+
}
131+
}

tools/clangd_check/pubspec.yaml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Copyright 2013 The Flutter Authors. All rights reserved.
2+
# Use of this source code is governed by a BSD-style license that can be
3+
# found in the LICENSE file.
4+
5+
name: clangd_check
6+
publish_to: none
7+
8+
# Do not add any dependencies that require more than what is provided in
9+
# //third_party/dart/pkg or //third_party/dart/third_party/pkg.
10+
# In particular, package:test is not usable here.
11+
12+
# If you do add packages here, make sure you can run `pub get --offline`, and
13+
# check the .packages and .package_config to make sure all the paths are
14+
# relative to this directory into //third_party/dart
15+
16+
environment:
17+
sdk: '>=3.2.0-0 <4.0.0'
18+
19+
dependencies:
20+
args: any
21+
engine_repo_tools: any
22+
path: any
23+
source_span: any
24+
25+
dev_dependencies:
26+
async_helper: any
27+
expect: any
28+
litetest: any
29+
process_fakes: any
30+
smith: any
31+
32+
dependency_overrides:
33+
args:
34+
path: ../../../third_party/dart/third_party/pkg/args
35+
async:
36+
path: ../../../third_party/dart/third_party/pkg/async
37+
async_helper:
38+
path: ../../../third_party/dart/pkg/async_helper
39+
collection:
40+
path: ../../../third_party/dart/third_party/pkg/collection
41+
engine_repo_tools:
42+
path: ../pkg/engine_repo_tools
43+
expect:
44+
path: ../../../third_party/dart/pkg/expect
45+
file:
46+
path: ../../../third_party/dart/third_party/pkg/file/packages/file
47+
git_repo_tools:
48+
path: ../pkg/git_repo_tools
49+
litetest:
50+
path: ../../testing/litetest
51+
meta:
52+
path: ../../../third_party/dart/pkg/meta
53+
path:
54+
path: ../../../third_party/dart/third_party/pkg/path
55+
platform:
56+
path: ../../third_party/pkg/platform
57+
process:
58+
path: ../../third_party/pkg/process
59+
process_fakes:
60+
path: ../pkg/process_fakes
61+
process_runner:
62+
path: ../../third_party/pkg/process_runner
63+
smith:
64+
path: ../../../third_party/dart/pkg/smith
65+
source_span:
66+
path: ../../../third_party/dart/third_party/pkg/source_span
67+
term_glyph:
68+
path: ../../../third_party/dart/third_party/pkg/term_glyph

tools/pub_get_offline.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
os.path.join(ENGINE_DIR, 'tools', 'api_check'),
3535
os.path.join(ENGINE_DIR, 'tools', 'build_bucket_golden_scraper'),
3636
os.path.join(ENGINE_DIR, 'tools', 'clang_tidy'),
37+
os.path.join(ENGINE_DIR, 'tools', 'clangd_check'),
3738
os.path.join(ENGINE_DIR, 'tools', 'compare_goldens'),
3839
os.path.join(ENGINE_DIR, 'tools', 'const_finder'),
3940
os.path.join(ENGINE_DIR, 'tools', 'dir_contents_diff'),

0 commit comments

Comments
 (0)