cmLoadCommandCommand.cxx 7.4 KB

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