You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
282 lines
7.5 KiB
282 lines
7.5 KiB
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
|
|
#if !defined(_WIN32) && !defined(__sun) && !defined(__OpenBSD__)
|
|
// POSIX APIs are needed
|
|
// NOLINTNEXTLINE(bugprone-reserved-identifier)
|
|
# define _POSIX_C_SOURCE 200809L
|
|
#endif
|
|
#if defined(__FreeBSD__) || defined(__NetBSD__)
|
|
// For isascii
|
|
// NOLINTNEXTLINE(bugprone-reserved-identifier)
|
|
# define _XOPEN_SOURCE 700
|
|
#endif
|
|
|
|
#include "cmLoadCommandCommand.h"
|
|
|
|
#include <csignal>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <utility>
|
|
|
|
#include <cm/memory>
|
|
|
|
#include "cmCPluginAPI.h"
|
|
#include "cmCommand.h"
|
|
#include "cmDynamicLoader.h"
|
|
#include "cmExecutionStatus.h"
|
|
#include "cmListFileCache.h"
|
|
#include "cmLocalGenerator.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmState.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
|
|
// NOLINTNEXTLINE(bugprone-suspicious-include)
|
|
#include "cmCPluginAPI.cxx"
|
|
|
|
#ifdef __QNX__
|
|
# include <malloc.h> /* for malloc/free on QNX */
|
|
#endif
|
|
|
|
namespace {
|
|
|
|
const char* LastName = nullptr;
|
|
|
|
extern "C" void TrapsForSignals(int sig);
|
|
extern "C" void TrapsForSignals(int sig)
|
|
{
|
|
fprintf(stderr, "CMake loaded command %s crashed with signal: %d.\n",
|
|
LastName, sig);
|
|
}
|
|
|
|
struct SignalHandlerGuard
|
|
{
|
|
explicit SignalHandlerGuard(const char* name)
|
|
{
|
|
LastName = name ? name : "????";
|
|
|
|
signal(SIGSEGV, TrapsForSignals);
|
|
#ifdef SIGBUS
|
|
signal(SIGBUS, TrapsForSignals);
|
|
#endif
|
|
signal(SIGILL, TrapsForSignals);
|
|
}
|
|
|
|
~SignalHandlerGuard()
|
|
{
|
|
signal(SIGSEGV, nullptr);
|
|
#ifdef SIGBUS
|
|
signal(SIGBUS, nullptr);
|
|
#endif
|
|
signal(SIGILL, nullptr);
|
|
}
|
|
|
|
SignalHandlerGuard(SignalHandlerGuard const&) = delete;
|
|
SignalHandlerGuard& operator=(SignalHandlerGuard const&) = delete;
|
|
};
|
|
|
|
struct LoadedCommandImpl : cmLoadedCommandInfo
|
|
{
|
|
explicit LoadedCommandImpl(CM_INIT_FUNCTION init)
|
|
: cmLoadedCommandInfo{ 0, 0, &cmStaticCAPI, 0,
|
|
nullptr, nullptr, nullptr, nullptr,
|
|
nullptr, nullptr, nullptr, nullptr }
|
|
{
|
|
init(this);
|
|
}
|
|
|
|
~LoadedCommandImpl()
|
|
{
|
|
if (this->Destructor) {
|
|
SignalHandlerGuard guard(this->Name);
|
|
#if defined(__NVCOMPILER) || defined(__LCC__)
|
|
static_cast<void>(guard); // convince compiler var is used
|
|
#endif
|
|
this->Destructor(this);
|
|
}
|
|
if (this->Error) {
|
|
free(this->Error);
|
|
}
|
|
}
|
|
|
|
LoadedCommandImpl(LoadedCommandImpl const&) = delete;
|
|
LoadedCommandImpl& operator=(LoadedCommandImpl const&) = delete;
|
|
|
|
int DoInitialPass(cmMakefile* mf, int argc, char* argv[])
|
|
{
|
|
SignalHandlerGuard guard(this->Name);
|
|
#if defined(__NVCOMPILER) || defined(__LCC__)
|
|
static_cast<void>(guard); // convince compiler var is used
|
|
#endif
|
|
return this->InitialPass(this, mf, argc, argv);
|
|
}
|
|
|
|
void DoFinalPass(cmMakefile* mf)
|
|
{
|
|
SignalHandlerGuard guard(this->Name);
|
|
#if defined(__NVCOMPILER) || defined(__LCC__)
|
|
static_cast<void>(guard); // convince compiler var is used
|
|
#endif
|
|
this->FinalPass(this, mf);
|
|
}
|
|
};
|
|
|
|
// a class for loadabple commands
|
|
class cmLoadedCommand : public cmCommand
|
|
{
|
|
public:
|
|
cmLoadedCommand() = default;
|
|
explicit cmLoadedCommand(CM_INIT_FUNCTION init)
|
|
: Impl(std::make_shared<LoadedCommandImpl>(init))
|
|
{
|
|
}
|
|
|
|
/**
|
|
* This is a virtual constructor for the command.
|
|
*/
|
|
std::unique_ptr<cmCommand> Clone() override
|
|
{
|
|
auto newC = cm::make_unique<cmLoadedCommand>();
|
|
// we must copy when we clone
|
|
newC->Impl = this->Impl;
|
|
return std::unique_ptr<cmCommand>(std::move(newC));
|
|
}
|
|
|
|
/**
|
|
* This is called when the command is first encountered in
|
|
* the CMakeLists.txt file.
|
|
*/
|
|
bool InitialPass(std::vector<std::string> const& args,
|
|
cmExecutionStatus&) override;
|
|
|
|
private:
|
|
std::shared_ptr<LoadedCommandImpl> Impl;
|
|
};
|
|
|
|
bool cmLoadedCommand::InitialPass(std::vector<std::string> const& args,
|
|
cmExecutionStatus&)
|
|
{
|
|
if (!this->Impl->InitialPass) {
|
|
return true;
|
|
}
|
|
|
|
// clear the error string
|
|
if (this->Impl->Error) {
|
|
free(this->Impl->Error);
|
|
}
|
|
|
|
// create argc and argv and then invoke the command
|
|
int argc = static_cast<int>(args.size());
|
|
char** argv = nullptr;
|
|
if (argc) {
|
|
argv = static_cast<char**>(malloc(argc * sizeof(char*)));
|
|
}
|
|
int i;
|
|
for (i = 0; i < argc; ++i) {
|
|
argv[i] = strdup(args[i].c_str());
|
|
}
|
|
int result = this->Impl->DoInitialPass(this->Makefile, argc, argv);
|
|
cmFreeArguments(argc, argv);
|
|
|
|
if (result) {
|
|
if (this->Impl->FinalPass) {
|
|
auto impl = this->Impl;
|
|
this->Makefile->AddGeneratorAction(
|
|
[impl](cmLocalGenerator& lg, const cmListFileBacktrace&) {
|
|
impl->DoFinalPass(lg.GetMakefile());
|
|
});
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* Initial Pass must have failed so set the error string */
|
|
if (this->Impl->Error) {
|
|
this->SetError(this->Impl->Error);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// cmLoadCommandCommand
|
|
bool cmLoadCommandCommand(std::vector<std::string> const& args,
|
|
cmExecutionStatus& status)
|
|
{
|
|
if (args.empty()) {
|
|
return true;
|
|
}
|
|
|
|
// Construct a variable to report what file was loaded, if any.
|
|
// Start by removing the definition in case of failure.
|
|
std::string reportVar = cmStrCat("CMAKE_LOADED_COMMAND_", args[0]);
|
|
status.GetMakefile().RemoveDefinition(reportVar);
|
|
|
|
// the file must exist
|
|
std::string moduleName = cmStrCat(
|
|
status.GetMakefile().GetRequiredDefinition("CMAKE_SHARED_MODULE_PREFIX"),
|
|
"cm", args[0],
|
|
status.GetMakefile().GetRequiredDefinition("CMAKE_SHARED_MODULE_SUFFIX"));
|
|
|
|
// search for the file
|
|
std::vector<std::string> path;
|
|
for (unsigned int j = 1; j < args.size(); j++) {
|
|
// expand variables
|
|
std::string exp = args[j];
|
|
cmSystemTools::ExpandRegistryValues(exp);
|
|
|
|
// Glob the entry in case of wildcards.
|
|
cmSystemTools::GlobDirs(exp, path);
|
|
}
|
|
|
|
// Try to find the program.
|
|
std::string fullPath = cmSystemTools::FindFile(moduleName, path);
|
|
if (fullPath.empty()) {
|
|
status.SetError(cmStrCat("Attempt to load command failed from file \"",
|
|
moduleName, "\""));
|
|
return false;
|
|
}
|
|
|
|
// try loading the shared library / dll
|
|
cmsys::DynamicLoader::LibraryHandle lib =
|
|
cmDynamicLoader::OpenLibrary(fullPath.c_str());
|
|
if (!lib) {
|
|
std::string err =
|
|
cmStrCat("Attempt to load the library ", fullPath, " failed.");
|
|
const char* error = cmsys::DynamicLoader::LastError();
|
|
if (error) {
|
|
err += " Additional error info is:\n";
|
|
err += error;
|
|
}
|
|
status.SetError(err);
|
|
return false;
|
|
}
|
|
|
|
// Report what file was loaded for this command.
|
|
status.GetMakefile().AddDefinition(reportVar, fullPath);
|
|
|
|
// find the init function
|
|
std::string initFuncName = args[0] + "Init";
|
|
CM_INIT_FUNCTION initFunction = reinterpret_cast<CM_INIT_FUNCTION>(
|
|
cmsys::DynamicLoader::GetSymbolAddress(lib, initFuncName));
|
|
if (!initFunction) {
|
|
initFuncName = cmStrCat('_', args[0], "Init");
|
|
initFunction = reinterpret_cast<CM_INIT_FUNCTION>(
|
|
cmsys::DynamicLoader::GetSymbolAddress(lib, initFuncName));
|
|
}
|
|
// if the symbol is found call it to set the name on the
|
|
// function blocker
|
|
if (initFunction) {
|
|
return status.GetMakefile().GetState()->AddScriptedCommand(
|
|
args[0],
|
|
BT<cmState::Command>(
|
|
cmLegacyCommandWrapper(cm::make_unique<cmLoadedCommand>(initFunction)),
|
|
status.GetMakefile().GetBacktrace()),
|
|
status.GetMakefile());
|
|
}
|
|
status.SetError("Attempt to load command failed. "
|
|
"No init function found.");
|
|
return false;
|
|
}
|