/*============================================================================ CMake - Cross Platform Makefile Generator Copyright 2000-2009 Kitware, Inc., Insight Software Consortium Distributed under the OSI-approved BSD License (the "License"); see accompanying file Copyright.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more information. ============================================================================*/ #include "cmLoadCommandCommand.h" #include "cmCPluginAPI.h" #include "cmCPluginAPI.cxx" #include "cmDynamicLoader.h" #include <cmsys/DynamicLoader.hxx> #include <stdlib.h> #ifdef __QNX__ # include <malloc.h> /* for malloc/free on QNX */ #endif #include <signal.h> 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(); /** * This is a virtual constructor for the command. */ virtual cmCommand* Clone() { 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. */ virtual bool InitialPass(std::vector<std::string> const& args, cmExecutionStatus &); /** * 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. */ virtual void FinalPass(); virtual bool HasFinalPass() const { return this->info.FinalPass? true:false; } /** * The name of the command as specified in CMakeList.txt. */ virtual const char* GetName() const { return info.Name; } /** * Succinct documentation. */ virtual const char* GetTerseDocumentation() const { if (this->info.GetTerseDocumentation) { cmLoadedCommand::InstallSignalHandlers(info.Name); const char* ret = info.GetTerseDocumentation(); cmLoadedCommand::InstallSignalHandlers(info.Name, 1); return ret; } else { return "LoadedCommand without any additional documentation"; } } 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, 0); #ifdef SIGBUS signal(SIGBUS, 0); #endif signal(SIGILL, 0); } } /** * More documentation. */ virtual const char* GetFullDocumentation() const { if (this->info.GetFullDocumentation) { cmLoadedCommand::InstallSignalHandlers(info.Name); const char* ret = info.GetFullDocumentation(); cmLoadedCommand::InstallSignalHandlers(info.Name, 1); return ret; } else { return "LoadedCommand without any additional documentation"; } } cmTypeMacro(cmLoadedCommand, cmCommand); cmLoadedCommandInfo info; }; extern "C" void TrapsForSignalsCFunction(int sig) { cmLoadedCommand::TrapsForSignals(sig); } const char* cmLoadedCommand::LastName = 0; 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 = 0; if (argc) { argv = (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((void *)&info, (void *)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((void *)&this->info,(void *)this->Makefile); cmLoadedCommand::InstallSignalHandlers(info.Name, 1); } } cmLoadedCommand::~cmLoadedCommand() { if (this->info.Destructor) { cmLoadedCommand::InstallSignalHandlers(info.Name); this->info.Destructor((void *)&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.size() < 1 ) { 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.c_str()); // 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.c_str(), path); } // Try to find the program. std::string fullPath = cmSystemTools::FindFile(moduleName.c_str(), path); if (fullPath == "") { cmOStringStream e; e << "Attempt to load command failed from file \"" << moduleName << "\""; this->SetError(e.str().c_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.c_str()); return false; } // Report what file was loaded for this command. this->Makefile->AddDefinition(reportVar.c_str(), fullPath.c_str()); // find the init function std::string initFuncName = args[0] + "Init"; CM_INIT_FUNCTION initFunction = (CM_INIT_FUNCTION) cmsys::DynamicLoader::GetSymbolAddress(lib, initFuncName.c_str()); if ( !initFunction ) { initFuncName = "_"; initFuncName += args[0]; initFuncName += "Init"; initFunction = (CM_INIT_FUNCTION)( cmsys::DynamicLoader::GetSymbolAddress(lib, initFuncName.c_str())); } // 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->AddCommand(f); return true; } this->SetError("Attempt to load command failed. " "No init function found."); return false; }