cmLoadCommandCommand.cxx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmLoadCommandCommand.h"
  4. #include <csignal>
  5. #include <cstdio>
  6. #include <cstdlib>
  7. #include <cstring>
  8. #include <utility>
  9. #include "cm_memory.hxx"
  10. #include "cmCPluginAPI.cxx"
  11. #include "cmCPluginAPI.h"
  12. #include "cmCommand.h"
  13. #include "cmDynamicLoader.h"
  14. #include "cmExecutionStatus.h"
  15. #include "cmMakefile.h"
  16. #include "cmState.h"
  17. #include "cmStringAlgorithms.h"
  18. #include "cmSystemTools.h"
  19. #ifdef __QNX__
  20. # include <malloc.h> /* for malloc/free on QNX */
  21. #endif
  22. namespace {
  23. const char* LastName = nullptr;
  24. extern "C" void TrapsForSignals(int sig)
  25. {
  26. fprintf(stderr, "CMake loaded command %s crashed with signal: %d.\n",
  27. LastName, sig);
  28. }
  29. struct SignalHandlerGuard
  30. {
  31. explicit SignalHandlerGuard(const char* name)
  32. {
  33. LastName = name != nullptr ? name : "????";
  34. signal(SIGSEGV, TrapsForSignals);
  35. #ifdef SIGBUS
  36. signal(SIGBUS, TrapsForSignals);
  37. #endif
  38. signal(SIGILL, TrapsForSignals);
  39. }
  40. ~SignalHandlerGuard()
  41. {
  42. signal(SIGSEGV, nullptr);
  43. #ifdef SIGBUS
  44. signal(SIGBUS, nullptr);
  45. #endif
  46. signal(SIGILL, nullptr);
  47. }
  48. SignalHandlerGuard(SignalHandlerGuard const&) = delete;
  49. SignalHandlerGuard& operator=(SignalHandlerGuard const&) = delete;
  50. };
  51. struct LoadedCommandImpl : cmLoadedCommandInfo
  52. {
  53. explicit LoadedCommandImpl(CM_INIT_FUNCTION init)
  54. : cmLoadedCommandInfo{ 0, 0, &cmStaticCAPI, 0,
  55. nullptr, nullptr, nullptr, nullptr,
  56. nullptr, nullptr, nullptr, nullptr }
  57. {
  58. init(this);
  59. }
  60. ~LoadedCommandImpl()
  61. {
  62. if (this->Destructor) {
  63. SignalHandlerGuard guard(this->Name);
  64. this->Destructor(this);
  65. }
  66. if (this->Error != nullptr) {
  67. free(this->Error);
  68. }
  69. }
  70. LoadedCommandImpl(LoadedCommandImpl const&) = delete;
  71. LoadedCommandImpl& operator=(LoadedCommandImpl const&) = delete;
  72. int DoInitialPass(cmMakefile* mf, int argc, char* argv[])
  73. {
  74. SignalHandlerGuard guard(this->Name);
  75. return this->InitialPass(this, mf, argc, argv);
  76. }
  77. void DoFinalPass(cmMakefile* mf)
  78. {
  79. SignalHandlerGuard guard(this->Name);
  80. this->FinalPass(this, mf);
  81. }
  82. };
  83. // a class for loadabple commands
  84. class cmLoadedCommand : public cmCommand
  85. {
  86. public:
  87. cmLoadedCommand() = default;
  88. explicit cmLoadedCommand(CM_INIT_FUNCTION init)
  89. : Impl(std::make_shared<LoadedCommandImpl>(init))
  90. {
  91. }
  92. /**
  93. * This is a virtual constructor for the command.
  94. */
  95. std::unique_ptr<cmCommand> Clone() override
  96. {
  97. auto newC = cm::make_unique<cmLoadedCommand>();
  98. // we must copy when we clone
  99. newC->Impl = this->Impl;
  100. return std::unique_ptr<cmCommand>(std::move(newC));
  101. }
  102. /**
  103. * This is called when the command is first encountered in
  104. * the CMakeLists.txt file.
  105. */
  106. bool InitialPass(std::vector<std::string> const& args,
  107. cmExecutionStatus&) override;
  108. private:
  109. std::shared_ptr<LoadedCommandImpl> Impl;
  110. };
  111. bool cmLoadedCommand::InitialPass(std::vector<std::string> const& args,
  112. cmExecutionStatus&)
  113. {
  114. if (!this->Impl->InitialPass) {
  115. return true;
  116. }
  117. // clear the error string
  118. if (this->Impl->Error) {
  119. free(this->Impl->Error);
  120. }
  121. // create argc and argv and then invoke the command
  122. int argc = static_cast<int>(args.size());
  123. char** argv = nullptr;
  124. if (argc) {
  125. argv = static_cast<char**>(malloc(argc * sizeof(char*)));
  126. }
  127. int i;
  128. for (i = 0; i < argc; ++i) {
  129. argv[i] = strdup(args[i].c_str());
  130. }
  131. int result = this->Impl->DoInitialPass(this->Makefile, argc, argv);
  132. cmFreeArguments(argc, argv);
  133. if (result) {
  134. if (this->Impl->FinalPass) {
  135. auto impl = this->Impl;
  136. this->Makefile->AddFinalAction(
  137. [impl](cmMakefile& makefile) { impl->DoFinalPass(&makefile); });
  138. }
  139. return true;
  140. }
  141. /* Initial Pass must have failed so set the error string */
  142. if (this->Impl->Error) {
  143. this->SetError(this->Impl->Error);
  144. }
  145. return false;
  146. }
  147. } // namespace
  148. // cmLoadCommandCommand
  149. bool cmLoadCommandCommand(std::vector<std::string> const& args,
  150. cmExecutionStatus& status)
  151. {
  152. if (args.empty()) {
  153. return true;
  154. }
  155. // Construct a variable to report what file was loaded, if any.
  156. // Start by removing the definition in case of failure.
  157. std::string reportVar = cmStrCat("CMAKE_LOADED_COMMAND_", args[0]);
  158. status.GetMakefile().RemoveDefinition(reportVar);
  159. // the file must exist
  160. std::string moduleName = cmStrCat(
  161. status.GetMakefile().GetRequiredDefinition("CMAKE_SHARED_MODULE_PREFIX"),
  162. "cm", args[0],
  163. status.GetMakefile().GetRequiredDefinition("CMAKE_SHARED_MODULE_SUFFIX"));
  164. // search for the file
  165. std::vector<std::string> path;
  166. for (unsigned int j = 1; j < args.size(); j++) {
  167. // expand variables
  168. std::string exp = args[j];
  169. cmSystemTools::ExpandRegistryValues(exp);
  170. // Glob the entry in case of wildcards.
  171. cmSystemTools::GlobDirs(exp, path);
  172. }
  173. // Try to find the program.
  174. std::string fullPath = cmSystemTools::FindFile(moduleName, path);
  175. if (fullPath.empty()) {
  176. status.SetError(cmStrCat("Attempt to load command failed from file \"",
  177. moduleName, "\""));
  178. return false;
  179. }
  180. // try loading the shared library / dll
  181. cmsys::DynamicLoader::LibraryHandle lib =
  182. cmDynamicLoader::OpenLibrary(fullPath.c_str());
  183. if (!lib) {
  184. std::string err =
  185. cmStrCat("Attempt to load the library ", fullPath, " failed.");
  186. const char* error = cmsys::DynamicLoader::LastError();
  187. if (error) {
  188. err += " Additional error info is:\n";
  189. err += error;
  190. }
  191. status.SetError(err);
  192. return false;
  193. }
  194. // Report what file was loaded for this command.
  195. status.GetMakefile().AddDefinition(reportVar, fullPath);
  196. // find the init function
  197. std::string initFuncName = args[0] + "Init";
  198. CM_INIT_FUNCTION initFunction = reinterpret_cast<CM_INIT_FUNCTION>(
  199. cmsys::DynamicLoader::GetSymbolAddress(lib, initFuncName));
  200. if (!initFunction) {
  201. initFuncName = cmStrCat('_', args[0], "Init");
  202. initFunction = reinterpret_cast<CM_INIT_FUNCTION>(
  203. cmsys::DynamicLoader::GetSymbolAddress(lib, initFuncName));
  204. }
  205. // if the symbol is found call it to set the name on the
  206. // function blocker
  207. if (initFunction) {
  208. status.GetMakefile().GetState()->AddScriptedCommand(
  209. args[0],
  210. cmLegacyCommandWrapper(cm::make_unique<cmLoadedCommand>(initFunction)));
  211. return true;
  212. }
  213. status.SetError("Attempt to load command failed. "
  214. "No init function found.");
  215. return false;
  216. }