/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ /* this file contains the implementation of the C API to CMake. Generally these routines just manipulate arguments and then call the associated methods on the CMake classes. */ #include "cmCPluginAPI.h" #include #include "cmExecutionStatus.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmSourceFile.h" #include "cmState.h" #include "cmVersion.h" #ifdef __QNX__ # include /* for malloc/free on QNX */ #endif extern "C" { static void CCONV* cmGetClientData(void* info) { return ((cmLoadedCommandInfo*)info)->ClientData; } static void CCONV cmSetClientData(void* info, void* cd) { ((cmLoadedCommandInfo*)info)->ClientData = cd; } static void CCONV cmSetError(void* info, const char* err) { if (((cmLoadedCommandInfo*)info)->Error) { free(((cmLoadedCommandInfo*)info)->Error); } ((cmLoadedCommandInfo*)info)->Error = strdup(err); } static unsigned int CCONV cmGetCacheMajorVersion(void* arg) { cmMakefile* mf = static_cast(arg); cmState* state = mf->GetState(); return state->GetCacheMajorVersion(); } static unsigned int CCONV cmGetCacheMinorVersion(void* arg) { cmMakefile* mf = static_cast(arg); cmState* state = mf->GetState(); return state->GetCacheMinorVersion(); } static unsigned int CCONV cmGetMajorVersion(void*) { return cmVersion::GetMajorVersion(); } static unsigned int CCONV cmGetMinorVersion(void*) { return cmVersion::GetMinorVersion(); } static void CCONV cmAddDefinition(void* arg, const char* name, const char* value) { if (value) { cmMakefile* mf = static_cast(arg); mf->AddDefinition(name, value); } } /* Add a definition to this makefile and the global cmake cache. */ static void CCONV cmAddCacheDefinition(void* arg, const char* name, const char* value, const char* doc, int type) { cmMakefile* mf = static_cast(arg); switch (type) { case CM_CACHE_BOOL: mf->AddCacheDefinition(name, value, doc, cmStateEnums::BOOL); break; case CM_CACHE_PATH: mf->AddCacheDefinition(name, value, doc, cmStateEnums::PATH); break; case CM_CACHE_FILEPATH: mf->AddCacheDefinition(name, value, doc, cmStateEnums::FILEPATH); break; case CM_CACHE_STRING: mf->AddCacheDefinition(name, value, doc, cmStateEnums::STRING); break; case CM_CACHE_INTERNAL: mf->AddCacheDefinition(name, value, doc, cmStateEnums::INTERNAL); break; case CM_CACHE_STATIC: mf->AddCacheDefinition(name, value, doc, cmStateEnums::STATIC); break; } } static const char* CCONV cmGetProjectName(void* arg) { cmMakefile* mf = static_cast(arg); static std::string name; name = mf->GetStateSnapshot().GetProjectName(); return name.c_str(); } static const char* CCONV cmGetHomeDirectory(void* arg) { cmMakefile* mf = static_cast(arg); return mf->GetHomeDirectory().c_str(); } static const char* CCONV cmGetHomeOutputDirectory(void* arg) { cmMakefile* mf = static_cast(arg); return mf->GetHomeOutputDirectory().c_str(); } static const char* CCONV cmGetStartDirectory(void* arg) { cmMakefile* mf = static_cast(arg); return mf->GetCurrentSourceDirectory().c_str(); } static const char* CCONV cmGetStartOutputDirectory(void* arg) { cmMakefile* mf = static_cast(arg); return mf->GetCurrentBinaryDirectory().c_str(); } static const char* CCONV cmGetCurrentDirectory(void* arg) { cmMakefile* mf = static_cast(arg); return mf->GetCurrentSourceDirectory().c_str(); } static const char* CCONV cmGetCurrentOutputDirectory(void* arg) { cmMakefile* mf = static_cast(arg); return mf->GetCurrentBinaryDirectory().c_str(); } static const char* CCONV cmGetDefinition(void* arg, const char* def) { cmMakefile* mf = static_cast(arg); return mf->GetDefinition(def).GetCStr(); } static int CCONV cmIsOn(void* arg, const char* name) { cmMakefile* mf = static_cast(arg); return static_cast(mf->IsOn(name)); } /** Check if a command exists. */ static int CCONV cmCommandExists(void* arg, const char* name) { cmMakefile* mf = static_cast(arg); return static_cast(mf->GetState()->GetCommand(name) ? 1 : 0); } static void CCONV cmAddDefineFlag(void* arg, const char* definition) { cmMakefile* mf = static_cast(arg); mf->AddDefineFlag(definition); } static void CCONV cmAddLinkDirectoryForTarget(void* arg, const char* tgt, const char* d) { cmMakefile* mf = static_cast(arg); cmTarget* t = mf->FindLocalNonAliasTarget(tgt); if (!t) { cmSystemTools::Error( "Attempt to add link directories to non-existent target: " + std::string(tgt) + " for directory " + std::string(d)); return; } t->InsertLinkDirectory(BT(d, mf->GetBacktrace())); } static void CCONV cmAddExecutable(void* arg, const char* exename, int numSrcs, const char** srcs, int win32) { cmMakefile* mf = static_cast(arg); std::vector srcs2; int i; for (i = 0; i < numSrcs; ++i) { srcs2.emplace_back(srcs[i]); } cmTarget* tg = mf->AddExecutable(exename, srcs2); if (win32) { tg->SetProperty("WIN32_EXECUTABLE", "ON"); } } static void CCONV cmAddUtilityCommand(void* arg, const char* utilityName, const char* command, const char* arguments, int all, int numDepends, const char** depends, int, const char**) { // Get the makefile instance. Perform an extra variable expansion // now because the API caller expects it. cmMakefile* mf = static_cast(arg); // Construct the command line for the command. cmCustomCommandLine commandLine; std::string expand = command; commandLine.push_back(mf->ExpandVariablesInString(expand)); if (arguments && arguments[0]) { // TODO: Parse arguments! expand = arguments; commandLine.push_back(mf->ExpandVariablesInString(expand)); } cmCustomCommandLines commandLines; commandLines.push_back(commandLine); // Accumulate the list of dependencies. std::vector depends2; for (int i = 0; i < numDepends; ++i) { expand = depends[i]; depends2.push_back(mf->ExpandVariablesInString(expand)); } // Pass the call to the makefile instance. auto cc = cm::make_unique(); cc->SetDepends(depends2); cc->SetCommandLines(commandLines); mf->AddUtilityCommand(utilityName, !all, std::move(cc)); } static void CCONV cmAddCustomCommand(void* arg, const char* source, const char* command, int numArgs, const char** args, int numDepends, const char** depends, int numOutputs, const char** outputs, const char* target) { // Get the makefile instance. Perform an extra variable expansion // now because the API caller expects it. cmMakefile* mf = static_cast(arg); // Construct the command line for the command. cmCustomCommandLine commandLine; std::string expand = command; commandLine.push_back(mf->ExpandVariablesInString(expand)); for (int i = 0; i < numArgs; ++i) { expand = args[i]; commandLine.push_back(mf->ExpandVariablesInString(expand)); } cmCustomCommandLines commandLines; commandLines.push_back(commandLine); // Accumulate the list of dependencies. std::vector depends2; for (int i = 0; i < numDepends; ++i) { expand = depends[i]; depends2.push_back(mf->ExpandVariablesInString(expand)); } // Accumulate the list of outputs. std::vector outputs2; for (int i = 0; i < numOutputs; ++i) { expand = outputs[i]; outputs2.push_back(mf->ExpandVariablesInString(expand)); } // Pass the call to the makefile instance. const char* no_comment = nullptr; mf->AddCustomCommandOldStyle(target, outputs2, depends2, source, commandLines, no_comment); } static void CCONV cmAddCustomCommandToOutput(void* arg, const char* output, const char* command, int numArgs, const char** args, const char* main_dependency, int numDepends, const char** depends) { // Get the makefile instance. Perform an extra variable expansion // now because the API caller expects it. cmMakefile* mf = static_cast(arg); // Construct the command line for the command. cmCustomCommandLine commandLine; std::string expand = command; commandLine.push_back(mf->ExpandVariablesInString(expand)); for (int i = 0; i < numArgs; ++i) { expand = args[i]; commandLine.push_back(mf->ExpandVariablesInString(expand)); } cmCustomCommandLines commandLines; commandLines.push_back(commandLine); // Accumulate the list of dependencies. std::vector depends2; for (int i = 0; i < numDepends; ++i) { expand = depends[i]; depends2.push_back(mf->ExpandVariablesInString(expand)); } // Pass the call to the makefile instance. auto cc = cm::make_unique(); cc->SetOutputs(output); cc->SetMainDependency(main_dependency); cc->SetDepends(depends2); cc->SetCommandLines(commandLines); mf->AddCustomCommandToOutput(std::move(cc)); } static void CCONV cmAddCustomCommandToTarget(void* arg, const char* target, const char* command, int numArgs, const char** args, int commandType) { // Get the makefile instance. cmMakefile* mf = static_cast(arg); // Construct the command line for the command. Perform an extra // variable expansion now because the API caller expects it. cmCustomCommandLine commandLine; std::string expand = command; commandLine.push_back(mf->ExpandVariablesInString(expand)); for (int i = 0; i < numArgs; ++i) { expand = args[i]; commandLine.push_back(mf->ExpandVariablesInString(expand)); } cmCustomCommandLines commandLines; commandLines.push_back(commandLine); // Select the command type. cmCustomCommandType cctype = cmCustomCommandType::POST_BUILD; switch (commandType) { case CM_PRE_BUILD: cctype = cmCustomCommandType::PRE_BUILD; break; case CM_PRE_LINK: cctype = cmCustomCommandType::PRE_LINK; break; case CM_POST_BUILD: cctype = cmCustomCommandType::POST_BUILD; break; } // Pass the call to the makefile instance. auto cc = cm::make_unique(); cc->SetCommandLines(commandLines); mf->AddCustomCommandToTarget(target, cctype, std::move(cc)); } static void addLinkLibrary(cmMakefile* mf, std::string const& target, std::string const& lib, cmTargetLinkLibraryType llt) { cmTarget* t = mf->FindLocalNonAliasTarget(target); if (!t) { std::ostringstream e; e << "Attempt to add link library \"" << lib << "\" to target \"" << target << "\" which is not built in this directory."; mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); return; } cmTarget* tgt = mf->GetGlobalGenerator()->FindTarget(lib); if (tgt && (tgt->GetType() != cmStateEnums::STATIC_LIBRARY) && (tgt->GetType() != cmStateEnums::SHARED_LIBRARY) && (tgt->GetType() != cmStateEnums::INTERFACE_LIBRARY) && !tgt->IsExecutableWithExports()) { std::ostringstream e; e << "Target \"" << lib << "\" of type " << cmState::GetTargetTypeName(tgt->GetType()) << " may not be linked into another target. " << "One may link only to STATIC or SHARED libraries, or " << "to executables with the ENABLE_EXPORTS property set."; mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); } t->AddLinkLibrary(*mf, lib, llt); } static void CCONV cmAddLinkLibraryForTarget(void* arg, const char* tgt, const char* value, int libtype) { cmMakefile* mf = static_cast(arg); switch (libtype) { case CM_LIBRARY_GENERAL: addLinkLibrary(mf, tgt, value, GENERAL_LibraryType); break; case CM_LIBRARY_DEBUG: addLinkLibrary(mf, tgt, value, DEBUG_LibraryType); break; case CM_LIBRARY_OPTIMIZED: addLinkLibrary(mf, tgt, value, OPTIMIZED_LibraryType); break; } } static void CCONV cmAddLibrary(void* arg, const char* libname, int shared, int numSrcs, const char** srcs) { cmMakefile* mf = static_cast(arg); std::vector srcs2; int i; for (i = 0; i < numSrcs; ++i) { srcs2.emplace_back(srcs[i]); } mf->AddLibrary( libname, (shared ? cmStateEnums::SHARED_LIBRARY : cmStateEnums::STATIC_LIBRARY), srcs2); } static char CCONV* cmExpandVariablesInString(void* arg, const char* source, int escapeQuotes, int atOnly) { cmMakefile* mf = static_cast(arg); std::string barf = source; std::string const& result = mf->ExpandVariablesInString(barf, escapeQuotes != 0, atOnly != 0); return strdup(result.c_str()); } static int CCONV cmExecuteCommand(void* arg, const char* name, int numArgs, const char** args) { cmMakefile* mf = static_cast(arg); std::vector lffArgs; lffArgs.reserve(numArgs); for (int i = 0; i < numArgs; ++i) { // Assume all arguments are quoted. lffArgs.emplace_back(args[i], cmListFileArgument::Quoted, 0); } cmListFileFunction lff{ name, 0, 0, std::move(lffArgs) }; cmExecutionStatus status(*mf); return mf->ExecuteCommand(lff, status); } static void CCONV cmExpandSourceListArguments(void* arg, int numArgs, const char** args, int* resArgc, char*** resArgv, unsigned int startArgumentIndex) { (void)arg; (void)startArgumentIndex; std::vector result; int i; for (i = 0; i < numArgs; ++i) { result.emplace_back(args[i]); } int resargc = static_cast(result.size()); char** resargv = nullptr; if (resargc) { resargv = (char**)malloc(resargc * sizeof(char*)); } for (i = 0; i < resargc; ++i) { resargv[i] = strdup(result[i].c_str()); } *resArgc = resargc; *resArgv = resargv; } static void CCONV cmFreeArguments(int argc, char** argv) { int i; for (i = 0; i < argc; ++i) { free(argv[i]); } free(argv); } static int CCONV cmGetTotalArgumentSize(int argc, char** argv) { int i; int result = 0; for (i = 0; i < argc; ++i) { if (argv[i]) { result = result + static_cast(strlen(argv[i])); } } return result; } // Source file proxy object to support the old cmSourceFile/cmMakefile // API for source files. struct cmCPluginAPISourceFile { cmSourceFile* RealSourceFile = nullptr; std::string SourceName; std::string SourceExtension; std::string FullPath; std::vector Depends; cmPropertyMap Properties; }; // Keep a map from real cmSourceFile instances stored in a makefile to // the CPluginAPI proxy source file. using cmCPluginAPISourceFileMap = std::map>; static cmCPluginAPISourceFileMap cmCPluginAPISourceFiles; static void* CCONV cmCreateSourceFile() { return new cmCPluginAPISourceFile; } static void* CCONV cmCreateNewSourceFile(void*) { return new cmCPluginAPISourceFile; } static void CCONV cmDestroySourceFile(void* arg) { cmCPluginAPISourceFile* sf = static_cast(arg); // Only delete if it was created by cmCreateSourceFile or // cmCreateNewSourceFile and is therefore not in the map. if (!sf->RealSourceFile) { delete sf; } } static void CCONV* cmGetSource(void* arg, const char* name) { cmMakefile* mf = static_cast(arg); if (cmSourceFile* rsf = mf->GetSource(name)) { // Lookup the proxy source file object for this source. auto i = cmCPluginAPISourceFiles.find(rsf); if (i == cmCPluginAPISourceFiles.end()) { // Create a proxy source file object for this source. auto sf = cm::make_unique(); sf->RealSourceFile = rsf; sf->FullPath = rsf->ResolveFullPath(); sf->SourceName = cmSystemTools::GetFilenameWithoutLastExtension(sf->FullPath); sf->SourceExtension = cmSystemTools::GetFilenameLastExtension(sf->FullPath); // Store the proxy in the map so it can be re-used and deleted later. i = cmCPluginAPISourceFiles.emplace(rsf, std::move(sf)).first; } return i->second.get(); } return nullptr; } static void* CCONV cmAddSource(void* arg, void* arg2) { cmMakefile* mf = static_cast(arg); cmCPluginAPISourceFile* osf = static_cast(arg2); if (osf->FullPath.empty()) { return nullptr; } // Create the real cmSourceFile instance and copy over saved information. cmSourceFile* rsf = mf->GetOrCreateSource(osf->FullPath); rsf->SetProperties(osf->Properties); // In case the properties contain the GENERATED property, // mark the real cmSourceFile as generated. if (rsf->GetIsGenerated()) { rsf->MarkAsGenerated(); } for (std::string const& d : osf->Depends) { rsf->AddDepend(d); } // Create the proxy for the real source file. auto sf = cm::make_unique(); sf->RealSourceFile = rsf; sf->FullPath = osf->FullPath; sf->SourceName = osf->SourceName; sf->SourceExtension = osf->SourceExtension; // Store the proxy in the map so it can be re-used and deleted later. auto* value = sf.get(); cmCPluginAPISourceFiles[rsf] = std::move(sf); return value; } static const char* CCONV cmSourceFileGetSourceName(void* arg) { cmCPluginAPISourceFile* sf = static_cast(arg); return sf->SourceName.c_str(); } static const char* CCONV cmSourceFileGetFullPath(void* arg) { cmCPluginAPISourceFile* sf = static_cast(arg); return sf->FullPath.c_str(); } static const char* CCONV cmSourceFileGetProperty(void* arg, const char* prop) { cmCPluginAPISourceFile* sf = static_cast(arg); if (cmSourceFile* rsf = sf->RealSourceFile) { return rsf->GetProperty(prop).GetCStr(); } if (!strcmp(prop, "LOCATION")) { return sf->FullPath.c_str(); } return sf->Properties.GetPropertyValue(prop).GetCStr(); } static int CCONV cmSourceFileGetPropertyAsBool(void* arg, const char* prop) { cmCPluginAPISourceFile* sf = static_cast(arg); if (cmSourceFile* rsf = sf->RealSourceFile) { return rsf->GetPropertyAsBool(prop) ? 1 : 0; } return cmIsOn(cmSourceFileGetProperty(arg, prop)) ? 1 : 0; } static void CCONV cmSourceFileSetProperty(void* arg, const char* prop, const char* value) { cmCPluginAPISourceFile* sf = static_cast(arg); if (cmSourceFile* rsf = sf->RealSourceFile) { rsf->SetProperty(prop, value); } else if (prop) { if (!value) { value = "NOTFOUND"; } sf->Properties.SetProperty(prop, value); } } static void CCONV cmSourceFileAddDepend(void* arg, const char* depend) { cmCPluginAPISourceFile* sf = static_cast(arg); if (cmSourceFile* rsf = sf->RealSourceFile) { rsf->AddDepend(depend); } else { sf->Depends.emplace_back(depend); } } static void CCONV cmSourceFileSetName(void* arg, const char* name, const char* dir, int numSourceExtensions, const char** sourceExtensions, int numHeaderExtensions, const char** headerExtensions) { cmCPluginAPISourceFile* sf = static_cast(arg); if (sf->RealSourceFile) { // SetName is allowed only on temporary source files created by // the command for building and passing to AddSource. return; } std::vector sourceExts; std::vector headerExts; int i; for (i = 0; i < numSourceExtensions; ++i) { sourceExts.emplace_back(sourceExtensions[i]); } for (i = 0; i < numHeaderExtensions; ++i) { headerExts.emplace_back(headerExtensions[i]); } // Save the original name given. sf->SourceName = name; // Convert the name to a full path in case the given name is a // relative path. std::string pathname = cmSystemTools::CollapseFullPath(name, dir); // First try and see whether the listed file can be found // as is without extensions added on. std::string hname = pathname; if (cmSystemTools::FileExists(hname)) { sf->SourceName = cmSystemTools::GetFilenamePath(name); if (!sf->SourceName.empty()) { sf->SourceName += "/"; } sf->SourceName += cmSystemTools::GetFilenameWithoutLastExtension(name); std::string::size_type pos = hname.rfind('.'); if (pos != std::string::npos) { sf->SourceExtension = hname.substr(pos + 1, hname.size() - pos); if (cmSystemTools::FileIsFullPath(name)) { std::string::size_type pos2 = hname.rfind('/'); if (pos2 != std::string::npos) { sf->SourceName = hname.substr(pos2 + 1, pos - pos2 - 1); } } } sf->FullPath = hname; return; } // Next, try the various source extensions for (std::string const& ext : sourceExts) { hname = cmStrCat(pathname, '.', ext); if (cmSystemTools::FileExists(hname)) { sf->SourceExtension = ext; sf->FullPath = hname; return; } } // Finally, try the various header extensions for (std::string const& ext : headerExts) { hname = cmStrCat(pathname, '.', ext); if (cmSystemTools::FileExists(hname)) { sf->SourceExtension = ext; sf->FullPath = hname; return; } } std::ostringstream e; e << "Cannot find source file \"" << pathname << "\""; e << "\n\nTried extensions"; for (std::string const& ext : sourceExts) { e << " ." << ext; } for (std::string const& ext : headerExts) { e << " ." << ext; } cmSystemTools::Error(e.str()); } static void CCONV cmSourceFileSetName2(void* arg, const char* name, const char* dir, const char* ext, int headerFileOnly) { cmCPluginAPISourceFile* sf = static_cast(arg); if (sf->RealSourceFile) { // SetName is allowed only on temporary source files created by // the command for building and passing to AddSource. return; } // Implement the old SetName method code here. if (headerFileOnly) { sf->Properties.SetProperty("HEADER_FILE_ONLY", "1"); } sf->SourceName = name; std::string fname = sf->SourceName; if (cmNonempty(ext)) { fname += "."; fname += ext; } sf->FullPath = cmSystemTools::CollapseFullPath(fname, dir); cmSystemTools::ConvertToUnixSlashes(sf->FullPath); sf->SourceExtension = ext; } static char* CCONV cmGetFilenameWithoutExtension(const char* name) { std::string sres = cmSystemTools::GetFilenameWithoutExtension(name); return strdup(sres.c_str()); } static char* CCONV cmGetFilenamePath(const char* name) { std::string sres = cmSystemTools::GetFilenamePath(name); return strdup(sres.c_str()); } static char* CCONV cmCapitalized(const char* name) { std::string sres = cmSystemTools::Capitalized(name); return strdup(sres.c_str()); } static void CCONV cmCopyFileIfDifferent(const char* name1, const char* name2) { cmSystemTools::CopyFileIfDifferent(name1, name2); } static void CCONV cmRemoveFile(const char* name) { cmSystemTools::RemoveFile(name); } static void CCONV cmDisplayStatus(void* arg, const char* message) { cmMakefile* mf = static_cast(arg); mf->DisplayStatus(message, -1); } static void CCONV cmFree(void* data) { free(data); } static void CCONV DefineSourceFileProperty(void* arg, const char* name, const char* briefDocs, const char* longDocs, int chained) { cmMakefile* mf = static_cast(arg); mf->GetState()->DefineProperty(name, cmProperty::SOURCE_FILE, briefDocs ? briefDocs : "", longDocs ? longDocs : "", chained != 0); } } // close the extern "C" scope static cmCAPI cmStaticCAPI = { cmGetClientData, cmGetTotalArgumentSize, cmFreeArguments, cmSetClientData, cmSetError, cmAddCacheDefinition, cmAddCustomCommand, cmAddDefineFlag, cmAddDefinition, cmAddExecutable, cmAddLibrary, cmAddLinkDirectoryForTarget, cmAddLinkLibraryForTarget, cmAddUtilityCommand, cmCommandExists, cmExecuteCommand, cmExpandSourceListArguments, cmExpandVariablesInString, cmGetCacheMajorVersion, cmGetCacheMinorVersion, cmGetCurrentDirectory, cmGetCurrentOutputDirectory, cmGetDefinition, cmGetHomeDirectory, cmGetHomeOutputDirectory, cmGetMajorVersion, cmGetMinorVersion, cmGetProjectName, cmGetStartDirectory, cmGetStartOutputDirectory, cmIsOn, cmAddSource, cmCreateSourceFile, cmDestroySourceFile, cmGetSource, cmSourceFileAddDepend, cmSourceFileGetProperty, cmSourceFileGetPropertyAsBool, cmSourceFileGetSourceName, cmSourceFileGetFullPath, cmSourceFileSetName, cmSourceFileSetName2, cmSourceFileSetProperty, cmCapitalized, cmCopyFileIfDifferent, cmGetFilenameWithoutExtension, cmGetFilenamePath, cmRemoveFile, cmFree, cmAddCustomCommandToOutput, cmAddCustomCommandToTarget, cmDisplayStatus, cmCreateNewSourceFile, DefineSourceFileProperty, };