[clang] edbb99a - Ensure -extract-api handles multiple headers correctly
Daniel Grumberg via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 21 14:04:59 PDT 2022
Author: Daniel Grumberg
Date: 2022-03-21T21:04:47Z
New Revision: edbb99a7edc6f2dca0ebb27d95c624aa6479eb21
URL: https://github.com/llvm/llvm-project/commit/edbb99a7edc6f2dca0ebb27d95c624aa6479eb21
DIFF: https://github.com/llvm/llvm-project/commit/edbb99a7edc6f2dca0ebb27d95c624aa6479eb21.diff
LOG: Ensure -extract-api handles multiple headers correctly
clang -extract-api should accept multiple headers and forward them to a
single CC1 instance. This change introduces a new ExtractAPIJobAction.
Currently API Extraction is done during the Precompile phase as this is
the current phase that matches the requirements the most. Adding a new
phase would need to change some logic in how phases are scheduled. If
the headers scheduled for API extraction are of different types the
driver emits a diagnostic.
Differential Revision: https://reviews.llvm.org/D121936
Added:
clang/test/Driver/extract-api-multiheader-kind-diag.h
clang/test/Driver/extract-api-multiheader.h
clang/test/Driver/extract-api.h
Modified:
clang/include/clang/Basic/DiagnosticDriverKinds.td
clang/include/clang/Driver/Action.h
clang/include/clang/Driver/Types.def
clang/lib/Driver/Action.cpp
clang/lib/Driver/Driver.cpp
clang/lib/Driver/ToolChain.cpp
clang/lib/Driver/ToolChains/Clang.cpp
clang/test/SymbolGraph/global_record.c
Removed:
clang/test/Driver/extract-api.c
################################################################################
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 0a62d4b85c9d1..59862555cc8f6 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -464,6 +464,10 @@ def err_test_module_file_extension_format : Error<
"-ftest-module-file-extension argument '%0' is not of the required form "
"'blockname:major:minor:hashed:user info'">;
+def err_drv_extract_api_wrong_kind : Error<
+ "header file '%0' input '%1' does not match the type of prior input "
+ "in api extraction; use '-x %2' to override">;
+
def warn_slash_u_filename : Warning<"'/U%0' treated as the '/U' option">,
InGroup<DiagGroup<"slash-u-filename">>;
def note_use_dashdash : Note<
diff --git a/clang/include/clang/Driver/Action.h b/clang/include/clang/Driver/Action.h
index 458a10ee11274..36410150c2797 100644
--- a/clang/include/clang/Driver/Action.h
+++ b/clang/include/clang/Driver/Action.h
@@ -59,6 +59,7 @@ class Action {
PreprocessJobClass,
PrecompileJobClass,
HeaderModulePrecompileJobClass,
+ ExtractAPIJobClass,
AnalyzeJobClass,
MigrateJobClass,
CompileJobClass,
@@ -443,6 +444,19 @@ class HeaderModulePrecompileJobAction : public PrecompileJobAction {
const char *getModuleName() const { return ModuleName; }
};
+class ExtractAPIJobAction : public JobAction {
+ void anchor() override;
+
+public:
+ ExtractAPIJobAction(Action *Input, types::ID OutputType);
+
+ static bool classof(const Action *A) {
+ return A->getKind() == ExtractAPIJobClass;
+ }
+
+ void addHeaderInput(Action *Input) { getInputs().push_back(Input); }
+};
+
class AnalyzeJobAction : public JobAction {
void anchor() override;
diff --git a/clang/include/clang/Driver/Types.def b/clang/include/clang/Driver/Types.def
index 7adf59ca5c992..7e38417a84a66 100644
--- a/clang/include/clang/Driver/Types.def
+++ b/clang/include/clang/Driver/Types.def
@@ -100,5 +100,5 @@ TYPE("dSYM", dSYM, INVALID, "dSYM", phases
TYPE("dependencies", Dependencies, INVALID, "d", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
TYPE("cuda-fatbin", CUDA_FATBIN, INVALID, "fatbin", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
TYPE("hip-fatbin", HIP_FATBIN, INVALID, "hipfb", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("api-information", API_INFO, INVALID, "json", phases::Compile)
+TYPE("api-information", API_INFO, INVALID, "json", phases::Precompile)
TYPE("none", Nothing, INVALID, nullptr, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
diff --git a/clang/lib/Driver/Action.cpp b/clang/lib/Driver/Action.cpp
index eb08bfe9cde56..21691c4ac4b98 100644
--- a/clang/lib/Driver/Action.cpp
+++ b/clang/lib/Driver/Action.cpp
@@ -26,6 +26,8 @@ const char *Action::getClassName(ActionClass AC) {
case PreprocessJobClass: return "preprocessor";
case PrecompileJobClass: return "precompiler";
case HeaderModulePrecompileJobClass: return "header-module-precompiler";
+ case ExtractAPIJobClass:
+ return "api-extractor";
case AnalyzeJobClass: return "analyzer";
case MigrateJobClass: return "migrator";
case CompileJobClass: return "compiler";
@@ -339,6 +341,11 @@ HeaderModulePrecompileJobAction::HeaderModulePrecompileJobAction(
: PrecompileJobAction(HeaderModulePrecompileJobClass, Input, OutputType),
ModuleName(ModuleName) {}
+void ExtractAPIJobAction::anchor() {}
+
+ExtractAPIJobAction::ExtractAPIJobAction(Action *Inputs, types::ID OutputType)
+ : JobAction(ExtractAPIJobClass, Inputs, OutputType) {}
+
void AnalyzeJobAction::anchor() {}
AnalyzeJobAction::AnalyzeJobAction(Action *Input, types::ID OutputType)
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 8099c1d71f0e7..44a89e9e26ee1 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -59,6 +59,7 @@
#include "clang/Driver/InputInfo.h"
#include "clang/Driver/Job.h"
#include "clang/Driver/Options.h"
+#include "clang/Driver/Phases.h"
#include "clang/Driver/SanitizerArgs.h"
#include "clang/Driver/Tool.h"
#include "clang/Driver/ToolChain.h"
@@ -334,10 +335,10 @@ phases::ID Driver::getFinalPhase(const DerivedArgList &DAL,
FinalPhase = phases::Preprocess;
// --precompile only runs up to precompilation.
- } else if ((PhaseArg = DAL.getLastArg(options::OPT__precompile))) {
+ } else if ((PhaseArg = DAL.getLastArg(options::OPT__precompile)) ||
+ (PhaseArg = DAL.getLastArg(options::OPT_extract_api))) {
FinalPhase = phases::Precompile;
-
- // -{fsyntax-only,-analyze,emit-ast} only run up to the compiler.
+ // -{fsyntax-only,-analyze,emit-ast} only run up to the compiler.
} else if ((PhaseArg = DAL.getLastArg(options::OPT_fsyntax_only)) ||
(PhaseArg = DAL.getLastArg(options::OPT_print_supported_cpus)) ||
(PhaseArg = DAL.getLastArg(options::OPT_module_file_info)) ||
@@ -346,8 +347,7 @@ phases::ID Driver::getFinalPhase(const DerivedArgList &DAL,
(PhaseArg = DAL.getLastArg(options::OPT_rewrite_legacy_objc)) ||
(PhaseArg = DAL.getLastArg(options::OPT__migrate)) ||
(PhaseArg = DAL.getLastArg(options::OPT__analyze)) ||
- (PhaseArg = DAL.getLastArg(options::OPT_emit_ast)) ||
- (PhaseArg = DAL.getLastArg(options::OPT_extract_api))) {
+ (PhaseArg = DAL.getLastArg(options::OPT_emit_ast))) {
FinalPhase = phases::Compile;
// -S only runs up to the backend.
@@ -3883,6 +3883,7 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
// Construct the actions to perform.
HeaderModulePrecompileJobAction *HeaderModuleAction = nullptr;
+ ExtractAPIJobAction *ExtractAPIAction = nullptr;
ActionList LinkerInputs;
ActionList MergerInputs;
@@ -3943,6 +3944,12 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
break;
}
+ if (Phase == phases::Precompile && ExtractAPIAction) {
+ ExtractAPIAction->addHeaderInput(Current);
+ Current = nullptr;
+ break;
+ }
+
// Try to build the offloading actions and add the result as a dependency
// to the host.
if (Args.hasArg(options::OPT_fopenmp_new_driver))
@@ -3960,6 +3967,8 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
if (auto *HMA = dyn_cast<HeaderModulePrecompileJobAction>(NewCurrent))
HeaderModuleAction = HMA;
+ else if (auto *EAA = dyn_cast<ExtractAPIJobAction>(NewCurrent))
+ ExtractAPIAction = EAA;
Current = NewCurrent;
@@ -4203,6 +4212,10 @@ Action *Driver::ConstructPhaseAction(
return C.MakeAction<PreprocessJobAction>(Input, OutputTy);
}
case phases::Precompile: {
+ // API extraction should not generate an actual precompilation action.
+ if (Args.hasArg(options::OPT_extract_api))
+ return C.MakeAction<ExtractAPIJobAction>(Input, types::TY_API_INFO);
+
types::ID OutputTy = getPrecompiledType(Input->getType());
assert(OutputTy != types::TY_INVALID &&
"Cannot precompile this input type!");
@@ -4217,8 +4230,7 @@ Action *Driver::ConstructPhaseAction(
OutputTy = types::TY_ModuleFile;
}
- if (Args.hasArg(options::OPT_fsyntax_only) ||
- Args.hasArg(options::OPT_extract_api)) {
+ if (Args.hasArg(options::OPT_fsyntax_only)) {
// Syntax checks should not emit a PCH file
OutputTy = types::TY_Nothing;
}
@@ -4247,7 +4259,7 @@ Action *Driver::ConstructPhaseAction(
if (Args.hasArg(options::OPT_verify_pch))
return C.MakeAction<VerifyPCHJobAction>(Input, types::TY_Nothing);
if (Args.hasArg(options::OPT_extract_api))
- return C.MakeAction<CompileJobAction>(Input, types::TY_API_INFO);
+ return C.MakeAction<ExtractAPIJobAction>(Input, types::TY_API_INFO);
return C.MakeAction<CompileJobAction>(Input, types::TY_LLVM_BC);
}
case phases::Backend: {
@@ -5778,7 +5790,8 @@ bool Driver::ShouldUseClangCompiler(const JobAction &JA) const {
// And say "no" if this is not a kind of action clang understands.
if (!isa<PreprocessJobAction>(JA) && !isa<PrecompileJobAction>(JA) &&
- !isa<CompileJobAction>(JA) && !isa<BackendJobAction>(JA))
+ !isa<CompileJobAction>(JA) && !isa<BackendJobAction>(JA) &&
+ !isa<ExtractAPIJobAction>(JA))
return false;
return true;
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 88e8ae76ccb20..f4415a30eb9d4 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -359,6 +359,7 @@ Tool *ToolChain::getTool(Action::ActionClass AC) const {
case Action::PrecompileJobClass:
case Action::HeaderModulePrecompileJobClass:
case Action::PreprocessJobClass:
+ case Action::ExtractAPIJobClass:
case Action::AnalyzeJobClass:
case Action::MigrateJobClass:
case Action::VerifyPCHJobClass:
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 5bf6b2471eaa5..2221ce5a07677 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -28,6 +28,7 @@
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/ObjCRuntime.h"
#include "clang/Basic/Version.h"
+#include "clang/Driver/Action.h"
#include "clang/Driver/Distro.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Driver/InputInfo.h"
@@ -4391,8 +4392,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
// CUDA/HIP compilation may have multiple inputs (source file + results of
// device-side compilations). OpenMP device jobs also take the host IR as a
// second input. Module precompilation accepts a list of header files to
- // include as part of the module. All other jobs are expected to have exactly
- // one input.
+ // include as part of the module. API extraction accepts a list of header
+ // files whose API information is emitted in the output. All other jobs are
+ // expected to have exactly one input.
bool IsCuda = JA.isOffloading(Action::OFK_Cuda);
bool IsCudaDevice = JA.isDeviceOffloading(Action::OFK_Cuda);
bool IsHIP = JA.isOffloading(Action::OFK_HIP);
@@ -4400,6 +4402,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
bool IsOpenMPDevice = JA.isDeviceOffloading(Action::OFK_OpenMP);
bool IsOpenMPHost = JA.isHostOffloading(Action::OFK_OpenMP);
bool IsHeaderModulePrecompile = isa<HeaderModulePrecompileJobAction>(JA);
+ bool IsExtractAPI = isa<ExtractAPIJobAction>(JA);
bool IsDeviceOffloadAction = !(JA.isDeviceOffloading(Action::OFK_None) ||
JA.isDeviceOffloading(Action::OFK_Host));
bool IsUsingLTO = D.isUsingLTO(IsDeviceOffloadAction);
@@ -4413,10 +4416,21 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
}();
InputInfo HeaderModuleInput(Inputs[0].getType(), ModuleName, ModuleName);
- const InputInfo &Input =
- IsHeaderModulePrecompile ? HeaderModuleInput : Inputs[0];
+ // Extract API doesn't have a main input file, so invent a fake one as a
+ // placeholder.
+ InputInfo ExtractAPIPlaceholderInput(Inputs[0].getType(), "extract-api",
+ "extract-api");
+
+ const InputInfo &Input = [&]() -> const InputInfo & {
+ if (IsHeaderModulePrecompile)
+ return HeaderModuleInput;
+ if (IsExtractAPI)
+ return ExtractAPIPlaceholderInput;
+ return Inputs[0];
+ }();
InputInfoList ModuleHeaderInputs;
+ InputInfoList ExtractAPIInputs;
InputInfoList OpenMPHostInputs;
const InputInfo *CudaDeviceInput = nullptr;
const InputInfo *OpenMPDeviceInput = nullptr;
@@ -4432,6 +4446,14 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
<< types::getTypeName(Expected);
}
ModuleHeaderInputs.push_back(I);
+ } else if (IsExtractAPI) {
+ auto ExpectedInputType = ExtractAPIPlaceholderInput.getType();
+ if (I.getType() != ExpectedInputType) {
+ D.Diag(diag::err_drv_extract_api_wrong_kind)
+ << I.getFilename() << types::getTypeName(I.getType())
+ << types::getTypeName(ExpectedInputType);
+ }
+ ExtractAPIInputs.push_back(I);
} else if ((IsCuda || IsHIP) && !CudaDeviceInput) {
CudaDeviceInput = &I;
} else if (IsOpenMPDevice && !OpenMPDeviceInput) {
@@ -4615,6 +4637,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-emit-pch");
} else if (isa<VerifyPCHJobAction>(JA)) {
CmdArgs.push_back("-verify-pch");
+ } else if (isa<ExtractAPIJobAction>(JA)) {
+ assert(JA.getType() == types::TY_API_INFO &&
+ "Extract API actions must generate a API information.");
+ CmdArgs.push_back("-extract-api");
} else {
assert((isa<CompileJobAction>(JA) || isa<BackendJobAction>(JA)) &&
"Invalid action for clang tool.");
@@ -4653,8 +4679,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
} else if (JA.getType() == types::TY_RewrittenLegacyObjC) {
CmdArgs.push_back("-rewrite-objc");
rewriteKind = RK_Fragile;
- } else if (JA.getType() == types::TY_API_INFO) {
- CmdArgs.push_back("-extract-api");
} else {
assert(JA.getType() == types::TY_PP_Asm && "Unexpected output type!");
}
@@ -7199,6 +7223,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
ArrayRef<InputInfo> FrontendInputs = Input;
if (IsHeaderModulePrecompile)
FrontendInputs = ModuleHeaderInputs;
+ else if (IsExtractAPI)
+ FrontendInputs = ExtractAPIInputs;
else if (Input.isNothing())
FrontendInputs = {};
diff --git a/clang/test/Driver/extract-api-multiheader-kind-diag.h b/clang/test/Driver/extract-api-multiheader-kind-diag.h
new file mode 100644
index 0000000000000..b07440b97c370
--- /dev/null
+++ b/clang/test/Driver/extract-api-multiheader-kind-diag.h
@@ -0,0 +1,14 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: not %clang -target x86_64-unknown-unknown -extract-api %t/first-header.h -x objective-c-header %t/second-header.h 2>&1 | FileCheck %s
+
+// CHECK: error: header file
+// CHECK-SAME: input 'objective-c-header' does not match the type of prior input in api extraction; use '-x c-header' to override
+
+//--- first-header.h
+
+void dummy_function(void);
+
+//--- second-header.h
+
+void other_dummy_function(void);
diff --git a/clang/test/Driver/extract-api-multiheader.h b/clang/test/Driver/extract-api-multiheader.h
new file mode 100644
index 0000000000000..1128fd5236967
--- /dev/null
+++ b/clang/test/Driver/extract-api-multiheader.h
@@ -0,0 +1,23 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: %clang -target x86_64-unknown-unknown -ccc-print-phases -extract-api %t/first-header.h %t/second-header.h 2> %t1
+// RUN: echo 'END' >> %t1
+// RUN: FileCheck -check-prefix EXTRACT-API-PHASES -input-file %t1 %s
+
+// EXTRACT-API-PHASES: 0: input
+// EXTRACT-API-PHASES-SAME: , c-header
+// EXTRACT-API-PHASES-NEXT: 1: preprocessor, {0}, c-header-cpp-output
+// EXTRACT-API-PHASES-NEXT: 2: input
+// EXTRACT-API-PHASES-SAME: , c-header
+// EXTRACT-API-PHASES-NEXT: 3: preprocessor, {2}, c-header-cpp-output
+// EXTRACT-API-PHASES-NEXT: 4: api-extractor, {1, 3}, api-information
+// EXTRACT-API-PHASES-NOT: 5:
+// EXTRACT-API-PHASES: END
+
+//--- first-header.h
+
+void dummy_function(void);
+
+//--- second-header.h
+
+void other_dummy_function(void);
diff --git a/clang/test/Driver/extract-api.c b/clang/test/Driver/extract-api.h
similarity index 60%
rename from clang/test/Driver/extract-api.c
rename to clang/test/Driver/extract-api.h
index 4a332817d8de5..5b0bbf766a705 100644
--- a/clang/test/Driver/extract-api.c
+++ b/clang/test/Driver/extract-api.h
@@ -3,8 +3,8 @@
// RUN: FileCheck -check-prefix EXTRACT-API-PHASES -input-file %t %s
// EXTRACT-API-PHASES: 0: input,
-// EXTRACT-API-PHASES: , c
-// EXTRACT-API-PHASES: 1: preprocessor, {0}, cpp-output
-// EXTRACT-API-PHASES: 2: compiler, {1}, api-information
+// EXTRACT-API-PHASES-SAME: , c-header
+// EXTRACT-API-PHASES-NEXT: 1: preprocessor, {0}, c-header-cpp-output
+// EXTRACT-API-PHASES-NEXT: 2: api-extractor, {1}, api-information
// EXTRACT-API-PHASES-NOT: 3:
// EXTRACT-API-PHASES: END
diff --git a/clang/test/SymbolGraph/global_record.c b/clang/test/SymbolGraph/global_record.c
index fa577ee29b17d..8c79fac1f025e 100644
--- a/clang/test/SymbolGraph/global_record.c
+++ b/clang/test/SymbolGraph/global_record.c
@@ -3,7 +3,7 @@
// RUN: sed -e "s at INPUT_DIR@%/t at g" %t/reference.output.json.in >> \
// RUN: %t/reference.output.json
// RUN: %clang -extract-api -target arm64-apple-macosx \
-// RUN: %t/input.c -o %t/output.json | FileCheck -allow-empty %s
+// RUN: %t/input.h -o %t/output.json | FileCheck -allow-empty %s
// Generator version is not consistent across test runs, normalize it.
// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
@@ -13,7 +13,7 @@
// CHECK-NOT: error:
// CHECK-NOT: warning:
-//--- input.c
+//--- input.h
int num;
/**
@@ -80,7 +80,7 @@ char unavailable __attribute__((unavailable));
"location": {
"character": 5,
"line": 1,
- "uri": "file://INPUT_DIR/input.c"
+ "uri": "file://INPUT_DIR/input.h"
},
"names": {
"subHeading": [
@@ -272,7 +272,7 @@ char unavailable __attribute__((unavailable));
"location": {
"character": 6,
"line": 9,
- "uri": "file://INPUT_DIR/input.c"
+ "uri": "file://INPUT_DIR/input.h"
},
"names": {
"subHeading": [
More information about the cfe-commits
mailing list