cmLoadCommandCommand.cxx 7.0 KB

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