Skip to content

Commit 9a65bbe

Browse files
Bigcheesearichardson
authored andcommitted
[clang][deps] Support inferred modules
This patch adds support for inferred modules to the dependency scanner. Effectively a cherry-pick of swiftlang/llvm-project#699 authored by @Bigcheese with libclang and other changes omitted. Contains following changes: 1. [Clang][ScanDeps] Ignore __inferred_module.map dependency. * This shows up with inferred modules, but it doesn't exist on disk, so don't report it as a dependency. 2. [Clang][ScanDeps] Use the module map a module was inferred from for inferred modules. Also includes a smoke test that uses clang-scan-deps output to perform an explicit build. There's no intention to duplicate whatever `test/Modules` contains, just to verify the produced command-line does "work" (with very loose definition of work). Split from D100934. Reviewed By: dexonsmith Differential Revision: https://reviews.llvm.org/D102495
2 parents 855b119 + 1d9e8e1 commit 9a65bbe

File tree

11 files changed

+194
-5
lines changed

11 files changed

+194
-5
lines changed

clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ makeInvocationForModuleBuildWithoutPaths(const ModuleDeps &Deps,
3333
CI.getFrontendOpts().IsSystemModule = Deps.IsSystem;
3434

3535
CI.getLangOpts()->ImplicitModules = false;
36-
CI.getHeaderSearchOpts().ImplicitModuleMaps = false;
3736

3837
return CI;
3938
}
@@ -179,13 +178,22 @@ ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
179178
const FileEntry *ModuleMap = Instance.getPreprocessor()
180179
.getHeaderSearchInfo()
181180
.getModuleMap()
182-
.getContainingModuleMapFile(M);
181+
.getModuleMapFileForUniquing(M);
183182
MD.ClangModuleMapFile = std::string(ModuleMap ? ModuleMap->getName() : "");
184183

185184
serialization::ModuleFile *MF =
186185
MDC.Instance.getASTReader()->getModuleManager().lookup(M->getASTFile());
187186
MDC.Instance.getASTReader()->visitInputFiles(
188187
*MF, true, true, [&](const serialization::InputFile &IF, bool isSystem) {
188+
// __inferred_module.map is the result of the way in which an implicit
189+
// module build handles inferred modules. It adds an overlay VFS with
190+
// this file in the proper directory and relies on the rest of Clang to
191+
// handle it like normal. With explicitly built modules we don't need
192+
// to play VFS tricks, so replace it with the correct module map.
193+
if (IF.getFile()->getName().endswith("__inferred_module.map")) {
194+
MD.FileDeps.insert(ModuleMap->getName());
195+
return;
196+
}
189197
MD.FileDeps.insert(IF.getFile()->getName());
190198
});
191199

clang/test/ClangScanDeps/Inputs/frameworks/Inferred.framework/Frameworks/Sub.framework/Headers/Sub.h

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
typedef int inferred;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
enum { bigger_than_int = 0x80000000 };
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
framework module System [system] {
2+
umbrella header "System.h"
3+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
framework module * {}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[
2+
{
3+
"directory": "DIR",
4+
"command": "clang -E DIR/modules_cdb_input.cpp -FFRAMEWORKS -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-modules -fimplicit-module-maps -pedantic -Werror",
5+
"file": "DIR/modules_cdb_input.cpp"
6+
}
7+
]

clang/test/ClangScanDeps/modules-full.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@
4848
// CHECK: "-emit-module"
4949
// CHECK-NO-ABS-NOT: "-fmodule-file={{.*}}"
5050
// CHECK-ABS: "-fmodule-file=[[PREFIX]]/module-cache{{(_clangcl)?}}/[[CONTEXT_HASH_H1]]/header2-{{[A-Z0-9]+}}.pcm"
51-
// CHECK-NOT: "-fimplicit-module-maps"
5251
// CHECK: "-fmodule-name=header1"
5352
// CHECK: "-fno-implicit-modules"
5453
// CHECK: ],
@@ -65,7 +64,6 @@
6564
// CHECK-NEXT: "command-line": [
6665
// CHECK-NEXT: "-cc1",
6766
// CHECK: "-emit-module",
68-
// CHECK-NOT: "-fimplicit-module-maps",
6967
// CHECK: "-fmodule-name=header1",
7068
// CHECK: "-fno-implicit-modules",
7169
// CHECK: ],
@@ -82,7 +80,6 @@
8280
// CHECK-NEXT: "command-line": [
8381
// CHECK-NEXT: "-cc1",
8482
// CHECK: "-emit-module",
85-
// CHECK-NOT: "-fimplicit-module-maps",
8683
// CHECK: "-fmodule-name=header2",
8784
// CHECK: "-fno-implicit-modules",
8885
// CHECK: ],
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: rm -rf %t.dir
2+
// RUN: rm -rf %t.cdb
3+
// RUN: mkdir -p %t.dir
4+
// RUN: cp %s %t.dir/modules_cdb_input.cpp
5+
// RUN: sed -e "s|DIR|%/t.dir|g" -e "s|FRAMEWORKS|%/S/Inputs/frameworks|g" -e "s|-E|-x objective-c -E|g" \
6+
// RUN: %S/Inputs/modules_inferred_cdb.json > %t.cdb
7+
//
8+
// RUN: clang-scan-deps -compilation-database %t.cdb -j 1 -format experimental-full \
9+
// RUN: -mode preprocess-minimized-sources > %t.db
10+
// RUN: %S/../../utils/module-deps-to-rsp.py %t.db --module-name=Inferred > %t.inferred.cc1.rsp
11+
// RUN: %S/../../utils/module-deps-to-rsp.py %t.db --module-name=System > %t.system.cc1.rsp
12+
// RUN: %S/../../utils/module-deps-to-rsp.py %t.db --tu-index=0 > %t.tu.rsp
13+
// RUN: %clang @%t.inferred.cc1.rsp -pedantic -Werror
14+
// RUN: %clang @%t.system.cc1.rsp -pedantic -Werror
15+
// RUN: %clang -x objective-c -fsyntax-only %t.dir/modules_cdb_input.cpp \
16+
// RUN: -F%S/Inputs/frameworks -fmodules -fimplicit-module-maps \
17+
// RUN: -pedantic -Werror @%t.tu.rsp
18+
19+
#include <Inferred/Inferred.h>
20+
#include <System/System.h>
21+
22+
inferred a = bigger_than_int;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// RUN: rm -rf %t.dir
2+
// RUN: rm -rf %t.cdb
3+
// RUN: mkdir -p %t.dir
4+
// RUN: cp %s %t.dir/modules_cdb_input.cpp
5+
// RUN: sed -e "s|DIR|%/t.dir|g" -e "s|FRAMEWORKS|%S/Inputs/frameworks|g" \
6+
// RUN: %S/Inputs/modules_inferred_cdb.json > %t.cdb
7+
//
8+
// RUN: echo -%t.dir > %t.result
9+
// RUN: echo -%S >> %t.result
10+
// RUN: clang-scan-deps -compilation-database %t.cdb -j 1 -format experimental-full \
11+
// RUN: -generate-modules-path-args -mode preprocess-minimized-sources >> %t.result
12+
// RUN: cat %t.result | sed 's/\\/\//g' | FileCheck --check-prefixes=CHECK %s
13+
14+
#include <Inferred/Inferred.h>
15+
16+
inferred a = 0;
17+
18+
// CHECK: -[[PREFIX:.*]]
19+
// CHECK-NEXT: -[[SOURCEDIR:.*]]
20+
// CHECK-NEXT: {
21+
// CHECK-NEXT: "modules": [
22+
// CHECK-NEXT: {
23+
// CHECK-NEXT: "clang-module-deps": [],
24+
// CHECK-NEXT: "clang-modulemap-file": "[[SOURCEDIR]]/Inputs/frameworks/module.modulemap",
25+
// CHECK-NEXT: "command-line": [
26+
// CHECK-NEXT: "-cc1",
27+
// CHECK: "-emit-module",
28+
// CHECK: "-fmodule-name=Inferred",
29+
// CHECK: "-fno-implicit-modules",
30+
// CHECK: ],
31+
// CHECK-NEXT: "context-hash": "[[CONTEXT_HASH_H1:[A-Z0-9]+]]",
32+
// CHECK-NEXT: "file-deps": [
33+
// CHECK-NEXT: "[[SOURCEDIR]]/Inputs/frameworks/Inferred.framework/Frameworks/Sub.framework/Headers/Sub.h",
34+
// CHECK-NEXT: "[[SOURCEDIR]]/Inputs/frameworks/Inferred.framework/Headers/Inferred.h",
35+
// CHECK-NEXT: "[[SOURCEDIR]]/Inputs/frameworks/module.modulemap"
36+
// CHECK-NEXT: ],
37+
// CHECK-NEXT: "name": "Inferred"
38+
// CHECK-NEXT: }
39+
// CHECK-NEXT: ],
40+
// CHECK-NEXT: "translation-units": [
41+
// CHECK-NEXT: {
42+
// CHECK-NEXT: "clang-context-hash": "[[CONTEXT_HASH_H1]]",
43+
// CHECK-NEXT: "clang-module-deps": [
44+
// CHECK-NEXT: {
45+
// CHECK-NEXT: "context-hash": "[[CONTEXT_HASH_H1]]",
46+
// CHECK-NEXT: "module-name": "Inferred"
47+
// CHECK-NEXT: }
48+
// CHECK-NEXT: ],
49+
// CHECK-NEXT: "command-line": [
50+
// CHECK-NEXT: "-fno-implicit-modules",
51+
// CHECK-NEXT: "-fno-implicit-module-maps",
52+
// CHECK-NEXT: "-fmodule-file=[[PREFIX]]/module-cache/[[CONTEXT_HASH_H1]]/Inferred-{{[A-Z0-9]+}}.pcm",
53+
// CHECK-NEXT: "-fmodule-map-file=[[SOURCEDIR]]/Inputs/frameworks/module.modulemap"
54+
// CHECK-NEXT: ],
55+
// CHECK-NEXT: "file-deps": [
56+
// CHECK-NEXT: "[[PREFIX]]/modules_cdb_input.cpp"
57+
// CHECK-NEXT: ],
58+
// CHECK-NEXT: "input-file": "[[PREFIX]]/modules_cdb_input.cpp"
59+
// CHECK-NEXT: }
60+
// CHECK-NEXT: ]
61+
// CHECK-NEXT: }

clang/utils/module-deps-to-rsp.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#!/usr/bin/env python
2+
3+
# Converts clang-scan-deps output into response files.
4+
# * For modules, arguments in the resulting response file are enough to build a PCM.
5+
# * For translation units, the response file needs to be added to the original Clang invocation from compilation
6+
# database.
7+
#
8+
# Usage:
9+
#
10+
# clang-scan-deps -compilation-database compile_commands.json ... > deps.json
11+
# module-deps-to-rsp.py deps.json --module-name=ModuleName > module_name.cc1.rsp
12+
# module-deps-to-rsp.py deps.json --tu-index=0 > tu.rsp
13+
# clang @module_name.cc1.rsp
14+
# clang ... @tu.rsp
15+
16+
import argparse
17+
import json
18+
import sys
19+
20+
class ModuleNotFoundError(Exception):
21+
def __init__(self, module_name):
22+
self.module_name = module_name
23+
24+
class FullDeps:
25+
def __init__(self):
26+
self.modules = dict()
27+
self.translation_units = str()
28+
29+
def getModulePathArgs(modules, full_deps):
30+
cmd = []
31+
for md in modules:
32+
m = full_deps.modules[md['module-name'] + '-' + md['context-hash']]
33+
cmd += [u'-fmodule-map-file=' + m['clang-modulemap-file']]
34+
cmd += [u'-fmodule-file=' + md['module-name'] + '-' + md['context-hash'] + '.pcm']
35+
return cmd
36+
37+
def getCommandLineForModule(module_name, full_deps):
38+
for m in full_deps.modules.values():
39+
if m['name'] == module_name:
40+
module = m
41+
break
42+
else:
43+
raise ModuleNotFoundError(module_name)
44+
45+
cmd = m['command-line']
46+
cmd += getModulePathArgs(m['clang-module-deps'], full_deps)
47+
cmd += [u'-o', m['name'] + '-' + m['context-hash'] + '.pcm']
48+
cmd += [m['clang-modulemap-file']]
49+
50+
return cmd
51+
52+
def getCommandLineForTU(tu, full_deps):
53+
cmd = tu['command-line']
54+
cmd += getModulePathArgs(tu['clang-module-deps'], full_deps)
55+
return cmd
56+
57+
def parseFullDeps(json):
58+
ret = FullDeps()
59+
for m in json['modules']:
60+
ret.modules[m['name'] + '-' + m['context-hash']] = m
61+
ret.translation_units = json['translation-units']
62+
return ret
63+
64+
def main():
65+
parser = argparse.ArgumentParser()
66+
parser.add_argument("full_deps_file", help="Path to the full dependencies json file",
67+
type=str)
68+
action = parser.add_mutually_exclusive_group(required=True)
69+
action.add_argument("--module-name", help="The name of the module to get arguments for",
70+
type=str)
71+
action.add_argument("--tu-index", help="The index of the translation unit to get arguments for",
72+
type=int)
73+
args = parser.parse_args()
74+
75+
full_deps = parseFullDeps(json.load(open(args.full_deps_file, 'r')))
76+
77+
try:
78+
if args.module_name:
79+
print(" ".join(getCommandLineForModule(args.module_name, full_deps)))
80+
81+
elif args.tu_index != None:
82+
print(" ".join(getCommandLineForTU(full_deps.translation_units[args.tu_index], full_deps)))
83+
except:
84+
print("Unexpected error:", sys.exc_info()[0])
85+
raise
86+
87+
if __name__ == '__main__':
88+
main()

0 commit comments

Comments
 (0)