Reapply "[clang][deps] Support inferred modules"

This reapplies commit 95033eb3 that reverted commit 1d9e8e13.

The tests were failing on Windows due to spaces and backslashes in paths not being handled carefully.
This commit is contained in:
Michael Spencer 2021-05-19 13:04:56 +02:00 committed by Jan Svoboda
parent f5b5426433
commit c98833cdaa
11 changed files with 200 additions and 5 deletions

View File

@ -33,7 +33,6 @@ makeInvocationForModuleBuildWithoutPaths(const ModuleDeps &Deps,
CI.getFrontendOpts().IsSystemModule = Deps.IsSystem;
CI.getLangOpts()->ImplicitModules = false;
CI.getHeaderSearchOpts().ImplicitModuleMaps = false;
return CI;
}
@ -179,13 +178,22 @@ ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
const FileEntry *ModuleMap = Instance.getPreprocessor()
.getHeaderSearchInfo()
.getModuleMap()
.getContainingModuleMapFile(M);
.getModuleMapFileForUniquing(M);
MD.ClangModuleMapFile = std::string(ModuleMap ? ModuleMap->getName() : "");
serialization::ModuleFile *MF =
MDC.Instance.getASTReader()->getModuleManager().lookup(M->getASTFile());
MDC.Instance.getASTReader()->visitInputFiles(
*MF, true, true, [&](const serialization::InputFile &IF, bool isSystem) {
// __inferred_module.map is the result of the way in which an implicit
// module build handles inferred modules. It adds an overlay VFS with
// this file in the proper directory and relies on the rest of Clang to
// handle it like normal. With explicitly built modules we don't need
// to play VFS tricks, so replace it with the correct module map.
if (IF.getFile()->getName().endswith("__inferred_module.map")) {
MD.FileDeps.insert(ModuleMap->getName());
return;
}
MD.FileDeps.insert(IF.getFile()->getName());
});

View File

@ -0,0 +1 @@
typedef int inferred;

View File

@ -0,0 +1 @@
enum { bigger_than_int = 0x80000000 };

View File

@ -0,0 +1,3 @@
framework module System [system] {
umbrella header "System.h"
}

View File

@ -0,0 +1 @@
framework module * {}

View File

@ -0,0 +1,7 @@
[
{
"directory": "DIR",
"command": "clang -E DIR/modules_cdb_input.cpp -FFRAMEWORKS -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-modules -fimplicit-module-maps -pedantic -Werror",
"file": "DIR/modules_cdb_input.cpp"
}
]

View File

@ -48,7 +48,6 @@
// CHECK: "-emit-module"
// CHECK-NO-ABS-NOT: "-fmodule-file={{.*}}"
// CHECK-ABS: "-fmodule-file=[[PREFIX]]/module-cache{{(_clangcl)?}}/[[CONTEXT_HASH_H1]]/header2-{{[A-Z0-9]+}}.pcm"
// CHECK-NOT: "-fimplicit-module-maps"
// CHECK: "-fmodule-name=header1"
// CHECK: "-fno-implicit-modules"
// CHECK: ],
@ -65,7 +64,6 @@
// CHECK-NEXT: "command-line": [
// CHECK-NEXT: "-cc1",
// CHECK: "-emit-module",
// CHECK-NOT: "-fimplicit-module-maps",
// CHECK: "-fmodule-name=header1",
// CHECK: "-fno-implicit-modules",
// CHECK: ],
@ -82,7 +80,6 @@
// CHECK-NEXT: "command-line": [
// CHECK-NEXT: "-cc1",
// CHECK: "-emit-module",
// CHECK-NOT: "-fimplicit-module-maps",
// CHECK: "-fmodule-name=header2",
// CHECK: "-fno-implicit-modules",
// CHECK: ],

View File

@ -0,0 +1,22 @@
// RUN: rm -rf %t.dir
// RUN: rm -rf %t.cdb
// RUN: mkdir -p %t.dir
// RUN: cp %s %t.dir/modules_cdb_input.cpp
// RUN: sed -e "s|DIR|%/t.dir|g" -e "s|FRAMEWORKS|%/S/Inputs/frameworks|g" -e "s|-E|-x objective-c -E|g" \
// RUN: %S/Inputs/modules_inferred_cdb.json > %t.cdb
//
// RUN: clang-scan-deps -compilation-database %t.cdb -j 1 -format experimental-full \
// RUN: -mode preprocess-minimized-sources > %t.db
// RUN: %python %S/../../utils/module-deps-to-rsp.py %t.db --module-name=Inferred > %t.inferred.cc1.rsp
// RUN: %python %S/../../utils/module-deps-to-rsp.py %t.db --module-name=System > %t.system.cc1.rsp
// RUN: %python %S/../../utils/module-deps-to-rsp.py %t.db --tu-index=0 > %t.tu.rsp
// RUN: %clang @%t.inferred.cc1.rsp -pedantic -Werror
// RUN: %clang @%t.system.cc1.rsp -pedantic -Werror
// RUN: %clang -x objective-c -fsyntax-only %t.dir/modules_cdb_input.cpp \
// RUN: -F%S/Inputs/frameworks -fmodules -fimplicit-module-maps \
// RUN: -pedantic -Werror @%t.tu.rsp
#include <Inferred/Inferred.h>
#include <System/System.h>
inferred a = bigger_than_int;

View File

@ -0,0 +1,61 @@
// RUN: rm -rf %t.dir
// RUN: rm -rf %t.cdb
// RUN: mkdir -p %t.dir
// RUN: cp %s %t.dir/modules_cdb_input.cpp
// RUN: sed -e "s|DIR|%/t.dir|g" -e "s|FRAMEWORKS|%/S/Inputs/frameworks|g" \
// RUN: %/S/Inputs/modules_inferred_cdb.json > %t.cdb
//
// RUN: echo -%t.dir > %t.result
// RUN: echo -%S >> %t.result
// RUN: clang-scan-deps -compilation-database %t.cdb -j 1 -format experimental-full \
// RUN: -generate-modules-path-args -mode preprocess-minimized-sources >> %t.result
// RUN: cat %t.result | sed -e 's/\\\\/\//g' -e 's/\\/\//g' | FileCheck --check-prefixes=CHECK %s
#include <Inferred/Inferred.h>
inferred a = 0;
// CHECK: -[[PREFIX:.*]]
// CHECK-NEXT: -[[SOURCEDIR:.*]]
// CHECK-NEXT: {
// CHECK-NEXT: "modules": [
// CHECK-NEXT: {
// CHECK-NEXT: "clang-module-deps": [],
// CHECK-NEXT: "clang-modulemap-file": "[[SOURCEDIR]]/Inputs/frameworks/module.modulemap",
// CHECK-NEXT: "command-line": [
// CHECK-NEXT: "-cc1",
// CHECK: "-emit-module",
// CHECK: "-fmodule-name=Inferred",
// CHECK: "-fno-implicit-modules",
// CHECK: ],
// CHECK-NEXT: "context-hash": "[[CONTEXT_HASH_H1:[A-Z0-9]+]]",
// CHECK-NEXT: "file-deps": [
// CHECK-NEXT: "[[SOURCEDIR]]/Inputs/frameworks/Inferred.framework/Frameworks/Sub.framework/Headers/Sub.h",
// CHECK-NEXT: "[[SOURCEDIR]]/Inputs/frameworks/Inferred.framework/Headers/Inferred.h",
// CHECK-NEXT: "[[SOURCEDIR]]/Inputs/frameworks/module.modulemap"
// CHECK-NEXT: ],
// CHECK-NEXT: "name": "Inferred"
// CHECK-NEXT: }
// CHECK-NEXT: ],
// CHECK-NEXT: "translation-units": [
// CHECK-NEXT: {
// CHECK-NEXT: "clang-context-hash": "[[CONTEXT_HASH_H1]]",
// CHECK-NEXT: "clang-module-deps": [
// CHECK-NEXT: {
// CHECK-NEXT: "context-hash": "[[CONTEXT_HASH_H1]]",
// CHECK-NEXT: "module-name": "Inferred"
// CHECK-NEXT: }
// CHECK-NEXT: ],
// CHECK-NEXT: "command-line": [
// CHECK-NEXT: "-fno-implicit-modules",
// CHECK-NEXT: "-fno-implicit-module-maps",
// CHECK-NEXT: "-fmodule-file=[[PREFIX]]/module-cache/[[CONTEXT_HASH_H1]]/Inferred-{{[A-Z0-9]+}}.pcm",
// CHECK-NEXT: "-fmodule-map-file=[[SOURCEDIR]]/Inputs/frameworks/module.modulemap"
// CHECK-NEXT: ],
// CHECK-NEXT: "file-deps": [
// CHECK-NEXT: "[[PREFIX]]/modules_cdb_input.cpp"
// CHECK-NEXT: ],
// CHECK-NEXT: "input-file": "[[PREFIX]]/modules_cdb_input.cpp"
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: }

View File

@ -0,0 +1,94 @@
#!/usr/bin/env python3
# Converts clang-scan-deps output into response files.
# * For modules, arguments in the resulting response file are enough to build a PCM.
# * For translation units, the response file needs to be added to the original Clang invocation from compilation
# database.
#
# Usage:
#
# clang-scan-deps -compilation-database compile_commands.json ... > deps.json
# module-deps-to-rsp.py deps.json --module-name=ModuleName > module_name.cc1.rsp
# module-deps-to-rsp.py deps.json --tu-index=0 > tu.rsp
# clang @module_name.cc1.rsp
# clang ... @tu.rsp
import argparse
import json
import sys
class ModuleNotFoundError(Exception):
def __init__(self, module_name):
self.module_name = module_name
class FullDeps:
def __init__(self):
self.modules = dict()
self.translation_units = str()
def getModulePathArgs(modules, full_deps):
cmd = []
for md in modules:
m = full_deps.modules[md['module-name'] + '-' + md['context-hash']]
cmd += [u'-fmodule-map-file=' + m['clang-modulemap-file']]
cmd += [u'-fmodule-file=' + md['module-name'] + '-' + md['context-hash'] + '.pcm']
return cmd
def getCommandLineForModule(module_name, full_deps):
for m in full_deps.modules.values():
if m['name'] == module_name:
module = m
break
else:
raise ModuleNotFoundError(module_name)
cmd = m['command-line']
cmd += getModulePathArgs(m['clang-module-deps'], full_deps)
cmd += [u'-o', m['name'] + '-' + m['context-hash'] + '.pcm']
cmd += [m['clang-modulemap-file']]
return cmd
def getCommandLineForTU(tu, full_deps):
cmd = tu['command-line']
cmd += getModulePathArgs(tu['clang-module-deps'], full_deps)
return cmd
def parseFullDeps(json):
ret = FullDeps()
for m in json['modules']:
ret.modules[m['name'] + '-' + m['context-hash']] = m
ret.translation_units = json['translation-units']
return ret
def quote(str):
return '"' + str.replace("\\", "\\\\") + '"'
def main():
parser = argparse.ArgumentParser()
parser.add_argument("full_deps_file", help="Path to the full dependencies json file",
type=str)
action = parser.add_mutually_exclusive_group(required=True)
action.add_argument("--module-name", help="The name of the module to get arguments for",
type=str)
action.add_argument("--tu-index", help="The index of the translation unit to get arguments for",
type=int)
args = parser.parse_args()
full_deps = parseFullDeps(json.load(open(args.full_deps_file, 'r')))
try:
cmd = []
if args.module_name:
cmd = getCommandLineForModule(args.module_name, full_deps)
elif args.tu_index != None:
cmd = getCommandLineForTU(full_deps.translation_units[args.tu_index], full_deps)
print(" ".join(map(quote, cmd)))
except:
print("Unexpected error:", sys.exc_info()[0])
raise
if __name__ == '__main__':
main()