diff options
Diffstat (limited to 'Source/cmLoadCommandCommand.cxx')
-rw-r--r-- | Source/cmLoadCommandCommand.cxx | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/Source/cmLoadCommandCommand.cxx b/Source/cmLoadCommandCommand.cxx new file mode 100644 index 0000000..cd71518 --- /dev/null +++ b/Source/cmLoadCommandCommand.cxx @@ -0,0 +1,248 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmLoadCommandCommand.h" + +#include <signal.h> +#include <sstream> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "cmCPluginAPI.cxx" +#include "cmCPluginAPI.h" +#include "cmDynamicLoader.h" +#include "cmMakefile.h" +#include "cmState.h" +#include "cmSystemTools.h" + +class cmExecutionStatus; + +#ifdef __QNX__ +# include <malloc.h> /* for malloc/free on QNX */ +#endif + +extern "C" void TrapsForSignalsCFunction(int sig); + +// a class for loadabple commands +class cmLoadedCommand : public cmCommand +{ +public: + cmLoadedCommand() + { + memset(&this->info, 0, sizeof(this->info)); + this->info.CAPI = &cmStaticCAPI; + } + + ///! clean up any memory allocated by the plugin + ~cmLoadedCommand() override; + + /** + * This is a virtual constructor for the command. + */ + cmCommand* Clone() override + { + cmLoadedCommand* newC = new cmLoadedCommand; + // we must copy when we clone + memcpy(&newC->info, &this->info, sizeof(info)); + return 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; + + /** + * This is called at the end after all the information + * specified by the command is accumulated. Most commands do + * not implement this method. At this point, reading and + * writing to the cache can be done. + */ + void FinalPass() override; + bool HasFinalPass() const override + { + return this->info.FinalPass != nullptr; + } + + static const char* LastName; + static void TrapsForSignals(int sig) + { + fprintf(stderr, "CMake loaded command %s crashed with signal: %d.\n", + cmLoadedCommand::LastName, sig); + } + static void InstallSignalHandlers(const char* name, int remove = 0) + { + cmLoadedCommand::LastName = name; + if (!name) { + cmLoadedCommand::LastName = "????"; + } + + if (!remove) { + signal(SIGSEGV, TrapsForSignalsCFunction); +#ifdef SIGBUS + signal(SIGBUS, TrapsForSignalsCFunction); +#endif + signal(SIGILL, TrapsForSignalsCFunction); + } else { + signal(SIGSEGV, nullptr); +#ifdef SIGBUS + signal(SIGBUS, nullptr); +#endif + signal(SIGILL, nullptr); + } + } + + cmLoadedCommandInfo info; +}; + +extern "C" void TrapsForSignalsCFunction(int sig) +{ + cmLoadedCommand::TrapsForSignals(sig); +} + +const char* cmLoadedCommand::LastName = nullptr; + +bool cmLoadedCommand::InitialPass(std::vector<std::string> const& args, + cmExecutionStatus&) +{ + if (!info.InitialPass) { + return true; + } + + // clear the error string + if (this->info.Error) { + free(this->info.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()); + } + cmLoadedCommand::InstallSignalHandlers(info.Name); + int result = info.InitialPass(&info, this->Makefile, argc, argv); + cmLoadedCommand::InstallSignalHandlers(info.Name, 1); + cmFreeArguments(argc, argv); + + if (result) { + return true; + } + + /* Initial Pass must have failed so set the error string */ + if (this->info.Error) { + this->SetError(this->info.Error); + } + return false; +} + +void cmLoadedCommand::FinalPass() +{ + if (this->info.FinalPass) { + cmLoadedCommand::InstallSignalHandlers(info.Name); + this->info.FinalPass(&this->info, this->Makefile); + cmLoadedCommand::InstallSignalHandlers(info.Name, 1); + } +} + +cmLoadedCommand::~cmLoadedCommand() +{ + if (this->info.Destructor) { + cmLoadedCommand::InstallSignalHandlers(info.Name); + this->info.Destructor(&this->info); + cmLoadedCommand::InstallSignalHandlers(info.Name, 1); + } + if (this->info.Error) { + free(this->info.Error); + } +} + +// cmLoadCommandCommand +bool cmLoadCommandCommand::InitialPass(std::vector<std::string> const& args, + cmExecutionStatus&) +{ + 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 = "CMAKE_LOADED_COMMAND_"; + reportVar += args[0]; + this->Makefile->RemoveDefinition(reportVar); + + // the file must exist + std::string moduleName = + this->Makefile->GetRequiredDefinition("CMAKE_SHARED_MODULE_PREFIX"); + moduleName += "cm" + args[0]; + moduleName += + this->Makefile->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()) { + std::ostringstream e; + e << "Attempt to load command failed from file \"" << moduleName << "\""; + this->SetError(e.str()); + return false; + } + + // try loading the shared library / dll + cmsys::DynamicLoader::LibraryHandle lib = + cmDynamicLoader::OpenLibrary(fullPath.c_str()); + if (!lib) { + std::string err = "Attempt to load the library "; + err += fullPath + " failed."; + const char* error = cmsys::DynamicLoader::LastError(); + if (error) { + err += " Additional error info is:\n"; + err += error; + } + this->SetError(err); + return false; + } + + // Report what file was loaded for this command. + this->Makefile->AddDefinition(reportVar, fullPath.c_str()); + + // 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 = "_"; + initFuncName += args[0]; + initFuncName += "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) { + // create a function blocker and set it up + cmLoadedCommand* f = new cmLoadedCommand(); + (*initFunction)(&f->info); + this->Makefile->GetState()->AddScriptedCommand(args[0], f); + return true; + } + this->SetError("Attempt to load command failed. " + "No init function found."); + return false; +} |