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@INPUT_DIR@%/t@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": [ _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits