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)
5 // POSIX APIs are needed
6 // NOLINTNEXTLINE(bugprone-reserved-identifier)
7 # define _POSIX_C_SOURCE 200809L
9 #if defined(__OpenBSD__) || 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)
49 fprintf(stderr, "CMake loaded command %s crashed with signal: %d.\n",
53 struct SignalHandlerGuard
55 explicit SignalHandlerGuard(const char* name)
57 LastName = name != nullptr ? name : "????";
59 signal(SIGSEGV, TrapsForSignals);
61 signal(SIGBUS, TrapsForSignals);
63 signal(SIGILL, TrapsForSignals);
68 signal(SIGSEGV, nullptr);
70 signal(SIGBUS, nullptr);
72 signal(SIGILL, nullptr);
75 SignalHandlerGuard(SignalHandlerGuard const&) = delete;
76 SignalHandlerGuard& operator=(SignalHandlerGuard const&) = delete;
79 struct LoadedCommandImpl : cmLoadedCommandInfo
81 explicit LoadedCommandImpl(CM_INIT_FUNCTION init)
82 : cmLoadedCommandInfo{ 0, 0, &cmStaticCAPI, 0,
83 nullptr, nullptr, nullptr, nullptr,
84 nullptr, nullptr, nullptr, nullptr }
91 if (this->Destructor) {
92 SignalHandlerGuard guard(this->Name);
93 this->Destructor(this);
95 if (this->Error != nullptr) {
100 LoadedCommandImpl(LoadedCommandImpl const&) = delete;
101 LoadedCommandImpl& operator=(LoadedCommandImpl const&) = delete;
103 int DoInitialPass(cmMakefile* mf, int argc, char* argv[])
105 SignalHandlerGuard guard(this->Name);
106 return this->InitialPass(this, mf, argc, argv);
109 void DoFinalPass(cmMakefile* mf)
111 SignalHandlerGuard guard(this->Name);
112 this->FinalPass(this, mf);
116 // a class for loadabple commands
117 class cmLoadedCommand : public cmCommand
120 cmLoadedCommand() = default;
121 explicit cmLoadedCommand(CM_INIT_FUNCTION init)
122 : Impl(std::make_shared<LoadedCommandImpl>(init))
127 * This is a virtual constructor for the command.
129 std::unique_ptr<cmCommand> Clone() override
131 auto newC = cm::make_unique<cmLoadedCommand>();
132 // we must copy when we clone
133 newC->Impl = this->Impl;
134 return std::unique_ptr<cmCommand>(std::move(newC));
138 * This is called when the command is first encountered in
139 * the CMakeLists.txt file.
141 bool InitialPass(std::vector<std::string> const& args,
142 cmExecutionStatus&) override;
145 std::shared_ptr<LoadedCommandImpl> Impl;
148 bool cmLoadedCommand::InitialPass(std::vector<std::string> const& args,
151 if (!this->Impl->InitialPass) {
155 // clear the error string
156 if (this->Impl->Error) {
157 free(this->Impl->Error);
160 // create argc and argv and then invoke the command
161 int argc = static_cast<int>(args.size());
162 char** argv = nullptr;
164 argv = static_cast<char**>(malloc(argc * sizeof(char*)));
167 for (i = 0; i < argc; ++i) {
168 argv[i] = strdup(args[i].c_str());
170 int result = this->Impl->DoInitialPass(this->Makefile, argc, argv);
171 cmFreeArguments(argc, argv);
174 if (this->Impl->FinalPass) {
175 auto impl = this->Impl;
176 this->Makefile->AddGeneratorAction(
177 [impl](cmLocalGenerator& lg, const cmListFileBacktrace&) {
178 impl->DoFinalPass(lg.GetMakefile());
184 /* Initial Pass must have failed so set the error string */
185 if (this->Impl->Error) {
186 this->SetError(this->Impl->Error);
193 // cmLoadCommandCommand
194 bool cmLoadCommandCommand(std::vector<std::string> const& args,
195 cmExecutionStatus& status)
201 // Construct a variable to report what file was loaded, if any.
202 // Start by removing the definition in case of failure.
203 std::string reportVar = cmStrCat("CMAKE_LOADED_COMMAND_", args[0]);
204 status.GetMakefile().RemoveDefinition(reportVar);
206 // the file must exist
207 std::string moduleName = cmStrCat(
208 status.GetMakefile().GetRequiredDefinition("CMAKE_SHARED_MODULE_PREFIX"),
210 status.GetMakefile().GetRequiredDefinition("CMAKE_SHARED_MODULE_SUFFIX"));
212 // search for the file
213 std::vector<std::string> path;
214 for (unsigned int j = 1; j < args.size(); j++) {
216 std::string exp = args[j];
217 cmSystemTools::ExpandRegistryValues(exp);
219 // Glob the entry in case of wildcards.
220 cmSystemTools::GlobDirs(exp, path);
223 // Try to find the program.
224 std::string fullPath = cmSystemTools::FindFile(moduleName, path);
225 if (fullPath.empty()) {
226 status.SetError(cmStrCat("Attempt to load command failed from file \"",
231 // try loading the shared library / dll
232 cmsys::DynamicLoader::LibraryHandle lib =
233 cmDynamicLoader::OpenLibrary(fullPath.c_str());
236 cmStrCat("Attempt to load the library ", fullPath, " failed.");
237 const char* error = cmsys::DynamicLoader::LastError();
239 err += " Additional error info is:\n";
242 status.SetError(err);
246 // Report what file was loaded for this command.
247 status.GetMakefile().AddDefinition(reportVar, fullPath);
249 // find the init function
250 std::string initFuncName = args[0] + "Init";
251 CM_INIT_FUNCTION initFunction = reinterpret_cast<CM_INIT_FUNCTION>(
252 cmsys::DynamicLoader::GetSymbolAddress(lib, initFuncName));
254 initFuncName = cmStrCat('_', args[0], "Init");
255 initFunction = reinterpret_cast<CM_INIT_FUNCTION>(
256 cmsys::DynamicLoader::GetSymbolAddress(lib, initFuncName));
258 // if the symbol is found call it to set the name on the
261 return status.GetMakefile().GetState()->AddScriptedCommand(
263 BT<cmState::Command>(
264 cmLegacyCommandWrapper(cm::make_unique<cmLoadedCommand>(initFunction)),
265 status.GetMakefile().GetBacktrace()),
266 status.GetMakefile());
268 status.SetError("Attempt to load command failed. "
269 "No init function found.");