| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| |
| #if !defined(_WIN32) && !defined(__sun) && !defined(__OpenBSD__) |
| // POSIX APIs are needed |
| // NOLINTNEXTLINE(bugprone-reserved-identifier) |
| # define _POSIX_C_SOURCE 200809L |
| #endif |
| #if defined(__FreeBSD__) || defined(__NetBSD__) |
| // For isascii |
| // NOLINTNEXTLINE(bugprone-reserved-identifier) |
| # define _XOPEN_SOURCE 700 |
| #endif |
| |
| #include "cmLoadCommandCommand.h" |
| |
| #include <csignal> |
| #include <cstdio> |
| #include <cstdlib> |
| #include <cstring> |
| #include <utility> |
| |
| #include <cm/memory> |
| |
| #include "cmCPluginAPI.h" |
| #include "cmCommand.h" |
| #include "cmDynamicLoader.h" |
| #include "cmExecutionStatus.h" |
| #include "cmListFileCache.h" |
| #include "cmLocalGenerator.h" |
| #include "cmMakefile.h" |
| #include "cmState.h" |
| #include "cmStringAlgorithms.h" |
| #include "cmSystemTools.h" |
| |
| // NOLINTNEXTLINE(bugprone-suspicious-include) |
| #include "cmCPluginAPI.cxx" |
| |
| #ifdef __QNX__ |
| # include <malloc.h> /* for malloc/free on QNX */ |
| #endif |
| |
| namespace { |
| |
| const char* LastName = nullptr; |
| |
| extern "C" void TrapsForSignals(int sig); |
| extern "C" void TrapsForSignals(int sig) |
| { |
| fprintf(stderr, "CMake loaded command %s crashed with signal: %d.\n", |
| LastName, sig); |
| } |
| |
| struct SignalHandlerGuard |
| { |
| explicit SignalHandlerGuard(const char* name) |
| { |
| LastName = name ? name : "????"; |
| |
| signal(SIGSEGV, TrapsForSignals); |
| #ifdef SIGBUS |
| signal(SIGBUS, TrapsForSignals); |
| #endif |
| signal(SIGILL, TrapsForSignals); |
| } |
| |
| ~SignalHandlerGuard() |
| { |
| signal(SIGSEGV, nullptr); |
| #ifdef SIGBUS |
| signal(SIGBUS, nullptr); |
| #endif |
| signal(SIGILL, nullptr); |
| } |
| |
| SignalHandlerGuard(SignalHandlerGuard const&) = delete; |
| SignalHandlerGuard& operator=(SignalHandlerGuard const&) = delete; |
| }; |
| |
| struct LoadedCommandImpl : cmLoadedCommandInfo |
| { |
| explicit LoadedCommandImpl(CM_INIT_FUNCTION init) |
| : cmLoadedCommandInfo{ 0, 0, &cmStaticCAPI, 0, |
| nullptr, nullptr, nullptr, nullptr, |
| nullptr, nullptr, nullptr, nullptr } |
| { |
| init(this); |
| } |
| |
| ~LoadedCommandImpl() |
| { |
| if (this->Destructor) { |
| SignalHandlerGuard guard(this->Name); |
| #if defined(__NVCOMPILER) || defined(__LCC__) |
| static_cast<void>(guard); // convince compiler var is used |
| #endif |
| this->Destructor(this); |
| } |
| if (this->Error) { |
| free(this->Error); |
| } |
| } |
| |
| LoadedCommandImpl(LoadedCommandImpl const&) = delete; |
| LoadedCommandImpl& operator=(LoadedCommandImpl const&) = delete; |
| |
| int DoInitialPass(cmMakefile* mf, int argc, char* argv[]) |
| { |
| SignalHandlerGuard guard(this->Name); |
| #if defined(__NVCOMPILER) || defined(__LCC__) |
| static_cast<void>(guard); // convince compiler var is used |
| #endif |
| return this->InitialPass(this, mf, argc, argv); |
| } |
| |
| void DoFinalPass(cmMakefile* mf) |
| { |
| SignalHandlerGuard guard(this->Name); |
| #if defined(__NVCOMPILER) || defined(__LCC__) |
| static_cast<void>(guard); // convince compiler var is used |
| #endif |
| this->FinalPass(this, mf); |
| } |
| }; |
| |
| // a class for loadabple commands |
| class cmLoadedCommand : public cmCommand |
| { |
| public: |
| cmLoadedCommand() = default; |
| explicit cmLoadedCommand(CM_INIT_FUNCTION init) |
| : Impl(std::make_shared<LoadedCommandImpl>(init)) |
| { |
| } |
| |
| /** |
| * This is a virtual constructor for the command. |
| */ |
| std::unique_ptr<cmCommand> Clone() override |
| { |
| auto newC = cm::make_unique<cmLoadedCommand>(); |
| // we must copy when we clone |
| newC->Impl = this->Impl; |
| return std::unique_ptr<cmCommand>(std::move(newC)); |
| } |
| |
| /** |
| * This is called when the command is first encountered in |
| * the CMakeLists.txt file. |
| */ |
| bool InitialPass(std::vector<std::string> const& args, |
| cmExecutionStatus&) override; |
| |
| private: |
| std::shared_ptr<LoadedCommandImpl> Impl; |
| }; |
| |
| bool cmLoadedCommand::InitialPass(std::vector<std::string> const& args, |
| cmExecutionStatus&) |
| { |
| if (!this->Impl->InitialPass) { |
| return true; |
| } |
| |
| // clear the error string |
| if (this->Impl->Error) { |
| free(this->Impl->Error); |
| } |
| |
| // create argc and argv and then invoke the command |
| int argc = static_cast<int>(args.size()); |
| char** argv = nullptr; |
| if (argc) { |
| argv = static_cast<char**>(malloc(argc * sizeof(char*))); |
| } |
| int i; |
| for (i = 0; i < argc; ++i) { |
| argv[i] = strdup(args[i].c_str()); |
| } |
| int result = this->Impl->DoInitialPass(this->Makefile, argc, argv); |
| cmFreeArguments(argc, argv); |
| |
| if (result) { |
| if (this->Impl->FinalPass) { |
| auto impl = this->Impl; |
| this->Makefile->AddGeneratorAction( |
| [impl](cmLocalGenerator& lg, const cmListFileBacktrace&) { |
| impl->DoFinalPass(lg.GetMakefile()); |
| }); |
| } |
| return true; |
| } |
| |
| /* Initial Pass must have failed so set the error string */ |
| if (this->Impl->Error) { |
| this->SetError(this->Impl->Error); |
| } |
| return false; |
| } |
| |
| } // namespace |
| |
| // cmLoadCommandCommand |
| bool cmLoadCommandCommand(std::vector<std::string> const& args, |
| cmExecutionStatus& status) |
| { |
| if (args.empty()) { |
| return true; |
| } |
| |
| // Construct a variable to report what file was loaded, if any. |
| // Start by removing the definition in case of failure. |
| std::string reportVar = cmStrCat("CMAKE_LOADED_COMMAND_", args[0]); |
| status.GetMakefile().RemoveDefinition(reportVar); |
| |
| // the file must exist |
| std::string moduleName = cmStrCat( |
| status.GetMakefile().GetRequiredDefinition("CMAKE_SHARED_MODULE_PREFIX"), |
| "cm", args[0], |
| status.GetMakefile().GetRequiredDefinition("CMAKE_SHARED_MODULE_SUFFIX")); |
| |
| // search for the file |
| std::vector<std::string> path; |
| for (unsigned int j = 1; j < args.size(); j++) { |
| // expand variables |
| std::string exp = args[j]; |
| cmSystemTools::ExpandRegistryValues(exp); |
| |
| // Glob the entry in case of wildcards. |
| cmSystemTools::GlobDirs(exp, path); |
| } |
| |
| // Try to find the program. |
| std::string fullPath = cmSystemTools::FindFile(moduleName, path); |
| if (fullPath.empty()) { |
| status.SetError(cmStrCat("Attempt to load command failed from file \"", |
| moduleName, "\"")); |
| return false; |
| } |
| |
| // try loading the shared library / dll |
| cmsys::DynamicLoader::LibraryHandle lib = |
| cmDynamicLoader::OpenLibrary(fullPath.c_str()); |
| if (!lib) { |
| std::string err = |
| cmStrCat("Attempt to load the library ", fullPath, " failed."); |
| const char* error = cmsys::DynamicLoader::LastError(); |
| if (error) { |
| err += " Additional error info is:\n"; |
| err += error; |
| } |
| status.SetError(err); |
| return false; |
| } |
| |
| // Report what file was loaded for this command. |
| status.GetMakefile().AddDefinition(reportVar, fullPath); |
| |
| // find the init function |
| std::string initFuncName = args[0] + "Init"; |
| CM_INIT_FUNCTION initFunction = reinterpret_cast<CM_INIT_FUNCTION>( |
| cmsys::DynamicLoader::GetSymbolAddress(lib, initFuncName)); |
| if (!initFunction) { |
| initFuncName = cmStrCat('_', args[0], "Init"); |
| initFunction = reinterpret_cast<CM_INIT_FUNCTION>( |
| cmsys::DynamicLoader::GetSymbolAddress(lib, initFuncName)); |
| } |
| // if the symbol is found call it to set the name on the |
| // function blocker |
| if (initFunction) { |
| return status.GetMakefile().GetState()->AddScriptedCommand( |
| args[0], |
| BT<cmState::Command>( |
| cmLegacyCommandWrapper(cm::make_unique<cmLoadedCommand>(initFunction)), |
| status.GetMakefile().GetBacktrace()), |
| status.GetMakefile()); |
| } |
| status.SetError("Attempt to load command failed. " |
| "No init function found."); |
| return false; |
| } |