resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmLoadCommandCommand.cxx
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3
4 #if !defined(_WIN32) && !defined(__sun) && !defined(__OpenBSD__)
5 // POSIX APIs are needed
6 // NOLINTNEXTLINE(bugprone-reserved-identifier)
7 #  define _POSIX_C_SOURCE 200809L
8 #endif
9 #if defined(__FreeBSD__) || defined(__NetBSD__)
10 // For isascii
11 // NOLINTNEXTLINE(bugprone-reserved-identifier)
12 #  define _XOPEN_SOURCE 700
13 #endif
14
15 #include "cmLoadCommandCommand.h"
16
17 #include <csignal>
18 #include <cstdio>
19 #include <cstdlib>
20 #include <cstring>
21 #include <utility>
22
23 #include <cm/memory>
24
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"
32 #include "cmState.h"
33 #include "cmStringAlgorithms.h"
34 #include "cmSystemTools.h"
35
36 // NOLINTNEXTLINE(bugprone-suspicious-include)
37 #include "cmCPluginAPI.cxx"
38
39 #ifdef __QNX__
40 #  include <malloc.h> /* for malloc/free on QNX */
41 #endif
42
43 namespace {
44
45 const char* LastName = nullptr;
46
47 extern "C" void TrapsForSignals(int sig);
48 extern "C" void TrapsForSignals(int sig)
49 {
50   fprintf(stderr, "CMake loaded command %s crashed with signal: %d.\n",
51           LastName, sig);
52 }
53
54 struct SignalHandlerGuard
55 {
56   explicit SignalHandlerGuard(const char* name)
57   {
58     LastName = name != nullptr ? name : "????";
59
60     signal(SIGSEGV, TrapsForSignals);
61 #ifdef SIGBUS
62     signal(SIGBUS, TrapsForSignals);
63 #endif
64     signal(SIGILL, TrapsForSignals);
65   }
66
67   ~SignalHandlerGuard()
68   {
69     signal(SIGSEGV, nullptr);
70 #ifdef SIGBUS
71     signal(SIGBUS, nullptr);
72 #endif
73     signal(SIGILL, nullptr);
74   }
75
76   SignalHandlerGuard(SignalHandlerGuard const&) = delete;
77   SignalHandlerGuard& operator=(SignalHandlerGuard const&) = delete;
78 };
79
80 struct LoadedCommandImpl : cmLoadedCommandInfo
81 {
82   explicit LoadedCommandImpl(CM_INIT_FUNCTION init)
83     : cmLoadedCommandInfo{ 0,       0,       &cmStaticCAPI, 0,
84                            nullptr, nullptr, nullptr,       nullptr,
85                            nullptr, nullptr, nullptr,       nullptr }
86   {
87     init(this);
88   }
89
90   ~LoadedCommandImpl()
91   {
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
96 #endif
97       this->Destructor(this);
98     }
99     if (this->Error != nullptr) {
100       free(this->Error);
101     }
102   }
103
104   LoadedCommandImpl(LoadedCommandImpl const&) = delete;
105   LoadedCommandImpl& operator=(LoadedCommandImpl const&) = delete;
106
107   int DoInitialPass(cmMakefile* mf, int argc, char* argv[])
108   {
109     SignalHandlerGuard guard(this->Name);
110 #if defined(__NVCOMPILER) || defined(__LCC__)
111     static_cast<void>(guard); // convince compiler var is used
112 #endif
113     return this->InitialPass(this, mf, argc, argv);
114   }
115
116   void DoFinalPass(cmMakefile* mf)
117   {
118     SignalHandlerGuard guard(this->Name);
119 #if defined(__NVCOMPILER) || defined(__LCC__)
120     static_cast<void>(guard); // convince compiler var is used
121 #endif
122     this->FinalPass(this, mf);
123   }
124 };
125
126 // a class for loadabple commands
127 class cmLoadedCommand : public cmCommand
128 {
129 public:
130   cmLoadedCommand() = default;
131   explicit cmLoadedCommand(CM_INIT_FUNCTION init)
132     : Impl(std::make_shared<LoadedCommandImpl>(init))
133   {
134   }
135
136   /**
137    * This is a virtual constructor for the command.
138    */
139   std::unique_ptr<cmCommand> Clone() override
140   {
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));
145   }
146
147   /**
148    * This is called when the command is first encountered in
149    * the CMakeLists.txt file.
150    */
151   bool InitialPass(std::vector<std::string> const& args,
152                    cmExecutionStatus&) override;
153
154 private:
155   std::shared_ptr<LoadedCommandImpl> Impl;
156 };
157
158 bool cmLoadedCommand::InitialPass(std::vector<std::string> const& args,
159                                   cmExecutionStatus&)
160 {
161   if (!this->Impl->InitialPass) {
162     return true;
163   }
164
165   // clear the error string
166   if (this->Impl->Error) {
167     free(this->Impl->Error);
168   }
169
170   // create argc and argv and then invoke the command
171   int argc = static_cast<int>(args.size());
172   char** argv = nullptr;
173   if (argc) {
174     argv = static_cast<char**>(malloc(argc * sizeof(char*)));
175   }
176   int i;
177   for (i = 0; i < argc; ++i) {
178     argv[i] = strdup(args[i].c_str());
179   }
180   int result = this->Impl->DoInitialPass(this->Makefile, argc, argv);
181   cmFreeArguments(argc, argv);
182
183   if (result) {
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());
189         });
190     }
191     return true;
192   }
193
194   /* Initial Pass must have failed so set the error string */
195   if (this->Impl->Error) {
196     this->SetError(this->Impl->Error);
197   }
198   return false;
199 }
200
201 } // namespace
202
203 // cmLoadCommandCommand
204 bool cmLoadCommandCommand(std::vector<std::string> const& args,
205                           cmExecutionStatus& status)
206 {
207   if (args.empty()) {
208     return true;
209   }
210
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);
215
216   // the file must exist
217   std::string moduleName = cmStrCat(
218     status.GetMakefile().GetRequiredDefinition("CMAKE_SHARED_MODULE_PREFIX"),
219     "cm", args[0],
220     status.GetMakefile().GetRequiredDefinition("CMAKE_SHARED_MODULE_SUFFIX"));
221
222   // search for the file
223   std::vector<std::string> path;
224   for (unsigned int j = 1; j < args.size(); j++) {
225     // expand variables
226     std::string exp = args[j];
227     cmSystemTools::ExpandRegistryValues(exp);
228
229     // Glob the entry in case of wildcards.
230     cmSystemTools::GlobDirs(exp, path);
231   }
232
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 \"",
237                              moduleName, "\""));
238     return false;
239   }
240
241   // try loading the shared library / dll
242   cmsys::DynamicLoader::LibraryHandle lib =
243     cmDynamicLoader::OpenLibrary(fullPath.c_str());
244   if (!lib) {
245     std::string err =
246       cmStrCat("Attempt to load the library ", fullPath, " failed.");
247     const char* error = cmsys::DynamicLoader::LastError();
248     if (error) {
249       err += " Additional error info is:\n";
250       err += error;
251     }
252     status.SetError(err);
253     return false;
254   }
255
256   // Report what file was loaded for this command.
257   status.GetMakefile().AddDefinition(reportVar, fullPath);
258
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));
263   if (!initFunction) {
264     initFuncName = cmStrCat('_', args[0], "Init");
265     initFunction = reinterpret_cast<CM_INIT_FUNCTION>(
266       cmsys::DynamicLoader::GetSymbolAddress(lib, initFuncName));
267   }
268   // if the symbol is found call it to set the name on the
269   // function blocker
270   if (initFunction) {
271     return status.GetMakefile().GetState()->AddScriptedCommand(
272       args[0],
273       BT<cmState::Command>(
274         cmLegacyCommandWrapper(cm::make_unique<cmLoadedCommand>(initFunction)),
275         status.GetMakefile().GetBacktrace()),
276       status.GetMakefile());
277   }
278   status.SetError("Attempt to load command failed. "
279                   "No init function found.");
280   return false;
281 }