diff options
Diffstat (limited to 'Source/cmFindProgramCommand.cxx')
-rw-r--r-- | Source/cmFindProgramCommand.cxx | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/Source/cmFindProgramCommand.cxx b/Source/cmFindProgramCommand.cxx new file mode 100644 index 0000000..2059b3d --- /dev/null +++ b/Source/cmFindProgramCommand.cxx @@ -0,0 +1,254 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmFindProgramCommand.h" + +#include "cmMakefile.h" +#include "cmStateTypes.h" +#include "cmSystemTools.h" + +class cmExecutionStatus; + +#if defined(__APPLE__) +#include <CoreFoundation/CoreFoundation.h> +#endif + +struct cmFindProgramHelper +{ + cmFindProgramHelper() + { +#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) + // Consider platform-specific extensions. + this->Extensions.push_back(".com"); + this->Extensions.push_back(".exe"); +#endif + // Consider original name with no extensions. + this->Extensions.push_back(""); + } + + // List of valid extensions. + std::vector<std::string> Extensions; + + // Keep track of the best program file found so far. + std::string BestPath; + + // Current names under consideration. + std::vector<std::string> Names; + + // Current full path under consideration. + std::string TestPath; + + void AddName(std::string const& name) { this->Names.push_back(name); } + void SetName(std::string const& name) + { + this->Names.clear(); + this->AddName(name); + } + bool CheckDirectory(std::string const& path) + { + for (std::string const& n : this->Names) { + if (this->CheckDirectoryForName(path, n)) { + return true; + } + } + return false; + } + bool CheckDirectoryForName(std::string const& path, std::string const& name) + { + for (std::string const& ext : this->Extensions) { + this->TestPath = path; + this->TestPath += name; + if (!ext.empty() && cmSystemTools::StringEndsWith(name, ext.c_str())) { + continue; + } + this->TestPath += ext; + if (cmSystemTools::FileExists(this->TestPath, true)) { + this->BestPath = cmSystemTools::CollapseFullPath(this->TestPath); + return true; + } + } + return false; + } +}; + +cmFindProgramCommand::cmFindProgramCommand() +{ + this->NamesPerDirAllowed = true; +} + +// cmFindProgramCommand +bool cmFindProgramCommand::InitialPass(std::vector<std::string> const& argsIn, + cmExecutionStatus&) +{ + this->VariableDocumentation = "Path to a program."; + this->CMakePathName = "PROGRAM"; + // call cmFindBase::ParseArguments + if (!this->ParseArguments(argsIn)) { + return false; + } + if (this->AlreadyInCache) { + // If the user specifies the entry on the command line without a + // type we should add the type and docstring but keep the original + // value. + if (this->AlreadyInCacheWithoutMetaInfo) { + this->Makefile->AddCacheDefinition(this->VariableName, "", + this->VariableDocumentation.c_str(), + cmStateEnums::FILEPATH); + } + return true; + } + + std::string const result = FindProgram(); + if (!result.empty()) { + // Save the value in the cache + this->Makefile->AddCacheDefinition(this->VariableName, result.c_str(), + this->VariableDocumentation.c_str(), + cmStateEnums::FILEPATH); + + return true; + } + this->Makefile->AddCacheDefinition( + this->VariableName, (this->VariableName + "-NOTFOUND").c_str(), + this->VariableDocumentation.c_str(), cmStateEnums::FILEPATH); + return true; +} + +std::string cmFindProgramCommand::FindProgram() +{ + std::string program; + + if (this->SearchAppBundleFirst || this->SearchAppBundleOnly) { + program = FindAppBundle(); + } + if (program.empty() && !this->SearchAppBundleOnly) { + program = this->FindNormalProgram(); + } + + if (program.empty() && this->SearchAppBundleLast) { + program = this->FindAppBundle(); + } + return program; +} + +std::string cmFindProgramCommand::FindNormalProgram() +{ + if (this->NamesPerDir) { + return this->FindNormalProgramNamesPerDir(); + } + return this->FindNormalProgramDirsPerName(); +} + +std::string cmFindProgramCommand::FindNormalProgramNamesPerDir() +{ + // Search for all names in each directory. + cmFindProgramHelper helper; + for (std::string const& n : this->Names) { + helper.AddName(n); + } + + // Check for the names themselves (e.g. absolute paths). + if (helper.CheckDirectory(std::string())) { + return helper.BestPath; + } + + // Search every directory. + for (std::string const& sp : this->SearchPaths) { + if (helper.CheckDirectory(sp)) { + return helper.BestPath; + } + } + // Couldn't find the program. + return ""; +} + +std::string cmFindProgramCommand::FindNormalProgramDirsPerName() +{ + // Search the entire path for each name. + cmFindProgramHelper helper; + for (std::string const& n : this->Names) { + // Switch to searching for this name. + helper.SetName(n); + + // Check for the name by itself (e.g. an absolute path). + if (helper.CheckDirectory(std::string())) { + return helper.BestPath; + } + + // Search every directory. + for (std::string const& sp : this->SearchPaths) { + if (helper.CheckDirectory(sp)) { + return helper.BestPath; + } + } + } + // Couldn't find the program. + return ""; +} + +std::string cmFindProgramCommand::FindAppBundle() +{ + for (std::string const& name : this->Names) { + + std::string appName = name + std::string(".app"); + std::string appPath = + cmSystemTools::FindDirectory(appName, this->SearchPaths, true); + + if (!appPath.empty()) { + std::string executable = GetBundleExecutable(appPath); + if (!executable.empty()) { + return cmSystemTools::CollapseFullPath(executable); + } + } + } + + // Couldn't find app bundle + return ""; +} + +std::string cmFindProgramCommand::GetBundleExecutable( + std::string const& bundlePath) +{ + std::string executable; + (void)bundlePath; +#if defined(__APPLE__) + // Started with an example on developer.apple.com about finding bundles + // and modified from that. + + // Get a CFString of the app bundle path + // XXX - Is it safe to assume everything is in UTF8? + CFStringRef bundlePathCFS = CFStringCreateWithCString( + kCFAllocatorDefault, bundlePath.c_str(), kCFStringEncodingUTF8); + + // Make a CFURLRef from the CFString representation of the + // bundle’s path. + CFURLRef bundleURL = CFURLCreateWithFileSystemPath( + kCFAllocatorDefault, bundlePathCFS, kCFURLPOSIXPathStyle, true); + + // Make a bundle instance using the URLRef. + CFBundleRef appBundle = CFBundleCreate(kCFAllocatorDefault, bundleURL); + + // returned executableURL is relative to <appbundle>/Contents/MacOS/ + CFURLRef executableURL = CFBundleCopyExecutableURL(appBundle); + + if (executableURL != nullptr) { + const int MAX_OSX_PATH_SIZE = 1024; + char buffer[MAX_OSX_PATH_SIZE]; + + // Convert the CFString to a C string + CFStringGetCString(CFURLGetString(executableURL), buffer, + MAX_OSX_PATH_SIZE, kCFStringEncodingUTF8); + + // And finally to a c++ string + executable = bundlePath + "/Contents/MacOS/" + std::string(buffer); + // Only release CFURLRef if it's not null + CFRelease(executableURL); + } + + // Any CF objects returned from functions with "create" or + // "copy" in their names must be released by us! + CFRelease(bundlePathCFS); + CFRelease(bundleURL); + CFRelease(appBundle); +#endif + + return executable; +} |