1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
4 #if !defined(_WIN32) && !defined(__sun) && !defined(__OpenBSD__)
5 // POSIX APIs are needed
6 // NOLINTNEXTLINE(bugprone-reserved-identifier)
7 # define _POSIX_C_SOURCE 200809L
9 #if defined(__FreeBSD__) || defined(__NetBSD__)
11 // NOLINTNEXTLINE(bugprone-reserved-identifier)
12 # define _XOPEN_SOURCE 700
15 #include "cmLoadCommandCommand.h"
25 #include "cmCPluginAPI.h"
26 #include "cmCommand.h"
27 #include "cmDynamicLoader.h"
28 #include "cmExecutionStatus.h"
29 #include "cmListFileCache.h"
30 #include "cmLocalGenerator.h"
31 #include "cmMakefile.h"
33 #include "cmStringAlgorithms.h"
34 #include "cmSystemTools.h"
36 // NOLINTNEXTLINE(bugprone-suspicious-include)
37 #include "cmCPluginAPI.cxx"
40 # include <malloc.h> /* for malloc/free on QNX */
45 const char* LastName = nullptr;
47 extern "C" void TrapsForSignals(int sig);
48 extern "C" void TrapsForSignals(int sig)
50 fprintf(stderr, "CMake loaded command %s crashed with signal: %d.\n",
54 struct SignalHandlerGuard
56 explicit SignalHandlerGuard(const char* name)
58 LastName = name != nullptr ? name : "????";
60 signal(SIGSEGV, TrapsForSignals);
62 signal(SIGBUS, TrapsForSignals);
64 signal(SIGILL, TrapsForSignals);
69 signal(SIGSEGV, nullptr);
71 signal(SIGBUS, nullptr);
73 signal(SIGILL, nullptr);
76 SignalHandlerGuard(SignalHandlerGuard const&) = delete;
77 SignalHandlerGuard& operator=(SignalHandlerGuard const&) = delete;
80 struct LoadedCommandImpl : cmLoadedCommandInfo
82 explicit LoadedCommandImpl(CM_INIT_FUNCTION init)
83 : cmLoadedCommandInfo{ 0, 0, &cmStaticCAPI, 0,
84 nullptr, nullptr, nullptr, nullptr,
85 nullptr, nullptr, nullptr, nullptr }
92 if (this->Destructor) {
93 SignalHandlerGuard guard(this->Name);
94 #if defined(__NVCOMPILER) || defined(__LCC__)
95 static_cast<void>(guard); // convince compiler var is used
97 this->Destructor(this);
99 if (this->Error != nullptr) {
104 LoadedCommandImpl(LoadedCommandImpl const&) = delete;
105 LoadedCommandImpl& operator=(LoadedCommandImpl const&) = delete;
107 int DoInitialPass(cmMakefile* mf, int argc, char* argv[])
109 SignalHandlerGuard guard(this->Name);
110 #if defined(__NVCOMPILER) || defined(__LCC__)
111 static_cast<void>(guard); // convince compiler var is used
113 return this->InitialPass(this, mf, argc, argv);
116 void DoFinalPass(cmMakefile* mf)
118 SignalHandlerGuard guard(this->Name);
119 #if defined(__NVCOMPILER) || defined(__LCC__)
120 static_cast<void>(guard); // convince compiler var is used
122 this->FinalPass(this, mf);
126 // a class for loadabple commands
127 class cmLoadedCommand : public cmCommand
130 cmLoadedCommand() = default;
131 explicit cmLoadedCommand(CM_INIT_FUNCTION init)
132 : Impl(std::make_shared<LoadedCommandImpl>(init))
137 * This is a virtual constructor for the command.
139 std::unique_ptr<cmCommand> Clone() override
141 auto newC = cm::make_unique<cmLoadedCommand>();
142 // we must copy when we clone
143 newC->Impl = this->Impl;
144 return std::unique_ptr<cmCommand>(std::move(newC));
148 * This is called when the command is first encountered in
149 * the CMakeLists.txt file.
151 bool InitialPass(std::vector<std::string> const& args,
152 cmExecutionStatus&) override;
155 std::shared_ptr<LoadedCommandImpl> Impl;
158 bool cmLoadedCommand::InitialPass(std::vector<std::string> const& args,
161 if (!this->Impl->InitialPass) {
165 // clear the error string
166 if (this->Impl->Error) {
167 free(this->Impl->Error);
170 // create argc and argv and then invoke the command
171 int argc = static_cast<int>(args.size());
172 char** argv = nullptr;
174 argv = static_cast<char**>(malloc(argc * sizeof(char*)));
177 for (i = 0; i < argc; ++i) {
178 argv[i] = strdup(args[i].c_str());
180 int result = this->Impl->DoInitialPass(this->Makefile, argc, argv);
181 cmFreeArguments(argc, argv);
184 if (this->Impl->FinalPass) {
185 auto impl = this->Impl;
186 this->Makefile->AddGeneratorAction(
187 [impl](cmLocalGenerator& lg, const cmListFileBacktrace&) {
188 impl->DoFinalPass(lg.GetMakefile());
194 /* Initial Pass must have failed so set the error string */
195 if (this->Impl->Error) {
196 this->SetError(this->Impl->Error);
203 // cmLoadCommandCommand
204 bool cmLoadCommandCommand(std::vector<std::string> const& args,
205 cmExecutionStatus& status)
211 // Construct a variable to report what file was loaded, if any.
212 // Start by removing the definition in case of failure.
213 std::string reportVar = cmStrCat("CMAKE_LOADED_COMMAND_", args[0]);
214 status.GetMakefile().RemoveDefinition(reportVar);
216 // the file must exist
217 std::string moduleName = cmStrCat(
218 status.GetMakefile().GetRequiredDefinition("CMAKE_SHARED_MODULE_PREFIX"),
220 status.GetMakefile().GetRequiredDefinition("CMAKE_SHARED_MODULE_SUFFIX"));
222 // search for the file
223 std::vector<std::string> path;
224 for (unsigned int j = 1; j < args.size(); j++) {
226 std::string exp = args[j];
227 cmSystemTools::ExpandRegistryValues(exp);
229 // Glob the entry in case of wildcards.
230 cmSystemTools::GlobDirs(exp, path);
233 // Try to find the program.
234 std::string fullPath = cmSystemTools::FindFile(moduleName, path);
235 if (fullPath.empty()) {
236 status.SetError(cmStrCat("Attempt to load command failed from file \"",
241 // try loading the shared library / dll
242 cmsys::DynamicLoader::LibraryHandle lib =
243 cmDynamicLoader::OpenLibrary(fullPath.c_str());
246 cmStrCat("Attempt to load the library ", fullPath, " failed.");
247 const char* error = cmsys::DynamicLoader::LastError();
249 err += " Additional error info is:\n";
252 status.SetError(err);
256 // Report what file was loaded for this command.
257 status.GetMakefile().AddDefinition(reportVar, fullPath);
259 // find the init function
260 std::string initFuncName = args[0] + "Init";
261 CM_INIT_FUNCTION initFunction = reinterpret_cast<CM_INIT_FUNCTION>(
262 cmsys::DynamicLoader::GetSymbolAddress(lib, initFuncName));
264 initFuncName = cmStrCat('_', args[0], "Init");
265 initFunction = reinterpret_cast<CM_INIT_FUNCTION>(
266 cmsys::DynamicLoader::GetSymbolAddress(lib, initFuncName));
268 // if the symbol is found call it to set the name on the
271 return status.GetMakefile().GetState()->AddScriptedCommand(
273 BT<cmState::Command>(
274 cmLegacyCommandWrapper(cm::make_unique<cmLoadedCommand>(initFunction)),
275 status.GetMakefile().GetBacktrace()),
276 status.GetMakefile());
278 status.SetError("Attempt to load command failed. "
279 "No init function found.");