| /* 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 != nullptr ? 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 != nullptr) { | |
| 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; | |
| } |