Skip to content

Commit 3e60b06

Browse files
committed
[clang][clang-scan-deps] Add an experimental C API.
This adds an experimental C API for clang-scan-deps. It provides both the full module dependency graph along with a flattened list of dependencies. See clang/include/clang-c/Dependencies.h for the API and documentation. You can test it out using: c-index-test core --scan-deps <working-directory> -- clang --cc1 <args> This will output a list of modules and then the direct dependencies of the main translation unit.
1 parent b78622e commit 3e60b06

File tree

12 files changed

+655
-1
lines changed

12 files changed

+655
-1
lines changed

clang/include/clang-c/Dependencies.h

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
/*==-- clang-c/Dependencies.h - Dependency Discovery C Interface --*- C -*-===*\
2+
|* *|
3+
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *|
4+
|* Exceptions. *|
5+
|* See https://llvm.org/LICENSE.txt for license information. *|
6+
|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *|
7+
|* *|
8+
|*===----------------------------------------------------------------------===*|
9+
|* *|
10+
|* This header provides a dependency discovery interface similar to *|
11+
|* clang-scan-deps. *|
12+
|* *|
13+
|* An example of its usage is available in c-index-test/core_main.cpp. *|
14+
|* *|
15+
|* EXPERIMENTAL: These interfaces are experimental and will change. If you *|
16+
|* use these be prepared for them to change without notice on any commit. *|
17+
|* *|
18+
\*===----------------------------------------------------------------------===*/
19+
20+
#ifndef LLVM_CLANG_C_DEPENDENCIES_H
21+
#define LLVM_CLANG_C_DEPENDENCIES_H
22+
23+
#include "clang-c/BuildSystem.h"
24+
#include "clang-c/CXErrorCode.h"
25+
#include "clang-c/CXString.h"
26+
#include "clang-c/Platform.h"
27+
28+
#ifdef __cplusplus
29+
extern "C" {
30+
#endif
31+
32+
/**
33+
* \defgroup SCAN_DEPS Dependency scanning service.
34+
* @{
35+
*/
36+
37+
typedef struct {
38+
CXString Name;
39+
/**
40+
* The context hash of a module represents the set of compiler options that
41+
* may make one version of a module incompatible from another. This includes
42+
* things like language mode, predefined macros, header search paths, etc...
43+
*
44+
* Modules with the same name but a different \c ContextHash should be treated
45+
* as separate modules for the purpose of a build.
46+
*/
47+
CXString ContextHash;
48+
49+
/**
50+
* The path to the modulemap file which defines this module.
51+
*
52+
* This can be used to explicitly build this module. This file will
53+
* additionally appear in \c FileDeps as a dependency.
54+
*/
55+
CXString ModuleMapPath;
56+
57+
/**
58+
* The list of files which this module directly depends on.
59+
*
60+
* If any of these change then the module needs to be rebuilt.
61+
*/
62+
CXStringSet *FileDeps;
63+
64+
/**
65+
* The list of modules which this module direct depends on.
66+
*
67+
* This does include the context hash. The format is
68+
* `<module-name>:<context-hash>`
69+
*/
70+
CXStringSet *ModuleDeps;
71+
72+
/**
73+
* The full command line needed to build this module.
74+
*
75+
* Not including `-fmodule-file=` or `-o`.
76+
*/
77+
CXStringSet *BuildArguments;
78+
} CXModuleDependency;
79+
80+
typedef struct {
81+
int Count;
82+
CXModuleDependency *Modules;
83+
} CXModuleDependencySet;
84+
85+
/**
86+
* See \c CXModuleDependency for the meaning of these fields, with the addition
87+
* that they represent only the direct dependencies for \c CXDependencyMode_Full
88+
* mode.
89+
*/
90+
typedef struct {
91+
CXString ContextHash;
92+
CXStringSet *FileDeps;
93+
CXStringSet *ModuleDeps;
94+
95+
/**
96+
* Additional arguments to append to the build of this file.
97+
*
98+
* This contains things like disabling implicit modules. This does not include
99+
* the `-fmodule-file=` arguments that are needed.
100+
*/
101+
CXStringSet *AdditionalArguments;
102+
} CXFileDependencies;
103+
104+
CINDEX_LINKAGE void
105+
clang_experimental_ModuleDependencySet_dispose(CXModuleDependencySet *MD);
106+
107+
CINDEX_LINKAGE void
108+
clang_experimental_FileDependencies_dispose(CXFileDependencies *ID);
109+
110+
/**
111+
* Object encapsulating instance of a dependency scanner service.
112+
*
113+
* The dependency scanner service is a global instance that owns the
114+
* global cache and other global state that's shared between the dependency
115+
* scanner workers. The service APIs are thread safe.
116+
*/
117+
typedef struct CXOpaqueDependencyScannerService *CXDependencyScannerService;
118+
119+
/**
120+
* The mode to report module dependencies in.
121+
*/
122+
typedef enum {
123+
/**
124+
* Flatten all module dependencies. This reports the full transitive set of
125+
* header and module map dependencies needed to do an implicit module build.
126+
*/
127+
CXDependencyMode_Flat,
128+
129+
/**
130+
* Report the full module graph. This reports only the direct dependencies of
131+
* each file, and calls a callback for each module that is discovered.
132+
*/
133+
CXDependencyMode_Full,
134+
} CXDependencyMode;
135+
136+
/**
137+
* Create a \c CXDependencyScannerService object.
138+
* Must be disposed with \c clang_DependencyScannerService_dispose().
139+
*/
140+
CINDEX_LINKAGE CXDependencyScannerService
141+
clang_experimental_DependencyScannerService_create_v0(CXDependencyMode Format);
142+
143+
/**
144+
* Dispose of a \c CXDependencyScannerService object.
145+
*
146+
* The service object must be disposed of after the workers are disposed of.
147+
*/
148+
CINDEX_LINKAGE void clang_experimental_DependencyScannerService_dispose_v0(
149+
CXDependencyScannerService);
150+
151+
/**
152+
* Object encapsulating instance of a dependency scanner worker.
153+
*
154+
* The dependency scanner workers are expected to be used in separate worker
155+
* threads. An individual worker is not thread safe.
156+
*
157+
* Operations on a worker are not thread-safe and should only be used from a
158+
* single thread at a time. They are intended to be used by a single dedicated
159+
* thread in a thread pool, but they are not inherently pinned to a thread.
160+
*/
161+
typedef struct CXOpaqueDependencyScannerWorker *CXDependencyScannerWorker;
162+
163+
/**
164+
* Create a \c CXDependencyScannerWorker object.
165+
* Must be disposed with
166+
* \c clang_experimental_DependencyScannerWorker_dispose_v0().
167+
*/
168+
CINDEX_LINKAGE CXDependencyScannerWorker
169+
clang_experimental_DependencyScannerWorker_create_v0(
170+
CXDependencyScannerService);
171+
172+
CINDEX_LINKAGE void clang_experimental_DependencyScannerWorker_dispose_v0(
173+
CXDependencyScannerWorker);
174+
175+
/**
176+
* A callback that is called whenever a module is discovered when in
177+
* \c CXDependencyMode_Full mode.
178+
*
179+
* \param Context the context that was passed to
180+
* \c clang_experimental_DependencyScannerWorker_getFileDependencies_v0.
181+
* \param MDS the list of discovered modules. Must be freed by calling
182+
* \c clang_experimental_ModuleDependencySet_dispose.
183+
*/
184+
typedef void CXModuleDiscoveredCallback(void *Context,
185+
CXModuleDependencySet *MDS);
186+
187+
/**
188+
* Returns the list of file dependencies for a particular compiler invocation.
189+
*
190+
* \param argc the number of compiler invocation arguments (including argv[0]).
191+
* \param argv the compiler invocation arguments (including argv[0]).
192+
* the invocation may be a -cc1 clang invocation or a driver
193+
* invocation.
194+
* \param WorkingDirectory the directory in which the invocation runs.
195+
* \param MDC a callback that is called whenever a new module is discovered.
196+
* This may receive the same module on different workers. This should
197+
* be NULL if
198+
* \c clang_experimental_DependencyScannerService_create_v0 was
199+
* called with \c CXDependencyMode_Flat. This callback will be called
200+
* on the same thread that called this function.
201+
* \param Context the context that will be passed to \c MDC each time it is
202+
* called.
203+
* \param [out] error the error string to pass back to client (if any).
204+
*
205+
* \returns A pointer to a CXFileDependencies on success, NULL otherwise. The
206+
* CXFileDependencies must be freed by calling
207+
* \c clang_experimental_FileDependencies_dispose.
208+
*/
209+
CINDEX_LINKAGE CXFileDependencies *
210+
clang_experimental_DependencyScannerWorker_getFileDependencies_v0(
211+
CXDependencyScannerWorker Worker, int argc, const char *const *argv,
212+
const char *WorkingDirectory, CXModuleDiscoveredCallback *MDC,
213+
void *Context, CXString *error);
214+
215+
/**
216+
* @}
217+
*/
218+
219+
#ifdef __cplusplus
220+
}
221+
#endif
222+
223+
#endif // LLVM_CLANG_C_DEPENDENCIES_H

clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,15 @@ class DependencyScanningWorker {
6363
const CompilationDatabase &CDB,
6464
DependencyConsumer &Consumer);
6565

66+
llvm::Error
67+
computeDependenciesForClangInvocation(StringRef WorkingDirectory,
68+
ArrayRef<std::string> Arguments,
69+
DependencyConsumer &Consumer);
70+
71+
ScanningOutputFormat getFormat() const { return Format; }
72+
73+
llvm::StringSet<> AlreadySeen;
74+
6675
private:
6776
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
6877
std::shared_ptr<PCHContainerOperations> PCHContainerOps;

clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,29 @@ llvm::Error DependencyScanningWorker::computeDependencies(
220220
return !Tool.run(&Action);
221221
});
222222
}
223+
224+
llvm::Error DependencyScanningWorker::computeDependenciesForClangInvocation(
225+
StringRef WorkingDirectory, ArrayRef<std::string> Arguments,
226+
DependencyConsumer &Consumer) {
227+
RealFS->setCurrentWorkingDirectory(WorkingDirectory);
228+
return runWithDiags(DiagOpts.get(), [&](DiagnosticConsumer &DC) {
229+
IntrusiveRefCntPtr<DiagnosticIDs> DiagID = new DiagnosticIDs();
230+
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
231+
DiagnosticsEngine Diags(DiagID, &*DiagOpts, &DC, /*ShouldOwnClient=*/false);
232+
233+
llvm::opt::ArgStringList CC1Args;
234+
for (const auto &Arg : Arguments)
235+
CC1Args.push_back(Arg.c_str());
236+
std::unique_ptr<CompilerInvocation> Invocation(
237+
newInvocation(&Diags, CC1Args));
238+
239+
DependencyScanningAction Action(WorkingDirectory, Consumer, DepFS,
240+
PPSkipMappings.get(), Format);
241+
242+
llvm::IntrusiveRefCntPtr<FileManager> FM = Files;
243+
if (!FM)
244+
FM = new FileManager(FileSystemOptions(), RealFS);
245+
return Action.runInvocation(std::move(Invocation), FM.get(),
246+
PCHContainerOps, &DC);
247+
});
248+
}

clang/test/Index/Core/scan-deps.m

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: rm -rf %t.mcp
2+
// RUN: echo %S > %t.result
3+
// RUN: c-index-test core --scan-deps %S -- %clang -cc1 -I %S/Inputs/module \
4+
// RUN: -fmodules -fmodules-cache-path=%t.mcp -fimplicit-module-maps \
5+
// RUN: -o FoE.o -x objective-c %s >> %t.result
6+
// RUN: cat %t.result | sed 's/\\/\//g' | FileCheck %s
7+
8+
@import ModA;
9+
10+
// CHECK: [[PREFIX:.*]]
11+
// CHECK-NEXT: modules:
12+
// CHECK-NEXT: module:
13+
// CHECK-NEXT: name: ModA
14+
// CHECK-NEXT: context-hash: [[CONTEXT_HASH:[A-Z0-9]+]]
15+
// CHECK-NEXT: module-map-path: [[PREFIX]]/Inputs/module/module.modulemap
16+
// CHECK-NEXT: module-deps:
17+
// CHECK-NEXT: file-deps:
18+
// CHECK-NEXT: [[PREFIX]]/Inputs/module/ModA.h
19+
// CHECK-NEXT: [[PREFIX]]/Inputs/module/SubModA.h
20+
// CHECK-NEXT: [[PREFIX]]/Inputs/module/SubSubModA.h
21+
// CHECK-NEXT: [[PREFIX]]/Inputs/module/module.modulemap
22+
// CHECK-NEXT: build-args:
23+
// CHECK-NEXT: dependencies:
24+
// CHECK-NEXT: context-hash: [[CONTEXT_HASH]]
25+
// CHECK-NEXT: module-deps:
26+
// CHECK-NEXT: ModA:[[CONTEXT_HASH]]
27+
// CHECK-NEXT: file-deps:
28+
// CHECK-NEXT: [[PREFIX]]/scan-deps.m
29+
// CHECK-NEXT: additional-build-args:

clang/tools/c-index-test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ else()
3737
clangAST
3838
clangBasic
3939
clangCodeGen
40+
clangDependencyScanning
4041
clangDirectoryWatcher
4142
clangFrontend
4243
clangIndex

0 commit comments

Comments
 (0)