cmFindProgramCommand.cxx 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
  4. Distributed under the OSI-approved BSD License (the "License");
  5. see accompanying file Copyright.txt for details.
  6. This software is distributed WITHOUT ANY WARRANTY; without even the
  7. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8. See the License for more information.
  9. ============================================================================*/
  10. #include "cmFindProgramCommand.h"
  11. #include <stdlib.h>
  12. #if defined(__APPLE__)
  13. #include <CoreFoundation/CoreFoundation.h>
  14. #endif
  15. //----------------------------------------------------------------------------
  16. struct cmFindProgramHelper
  17. {
  18. cmFindProgramHelper()
  19. {
  20. #if defined (_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
  21. // Consider platform-specific extensions.
  22. this->Extensions.push_back(".com");
  23. this->Extensions.push_back(".exe");
  24. #endif
  25. // Consider original name with no extensions.
  26. this->Extensions.push_back("");
  27. }
  28. // List of valid extensions.
  29. std::vector<std::string> Extensions;
  30. // Keep track of the best program file found so far.
  31. std::string BestPath;
  32. // Current names under consideration.
  33. std::vector<std::string> Names;
  34. // Current full path under consideration.
  35. std::string TestPath;
  36. void AddName(std::string const& name)
  37. {
  38. this->Names.push_back(name);
  39. }
  40. void SetName(std::string const& name)
  41. {
  42. this->Names.clear();
  43. this->AddName(name);
  44. }
  45. bool CheckDirectory(std::string const& path)
  46. {
  47. for (std::vector<std::string>::iterator i = this->Names.begin();
  48. i != this->Names.end(); ++i)
  49. {
  50. if (this->CheckDirectoryForName(path, *i))
  51. {
  52. return true;
  53. }
  54. }
  55. return false;
  56. }
  57. bool CheckDirectoryForName(std::string const& path, std::string const& name)
  58. {
  59. for (std::vector<std::string>::iterator ext = this->Extensions.begin();
  60. ext != this->Extensions.end(); ++ext)
  61. {
  62. this->TestPath = path;
  63. this->TestPath += name;
  64. if (!ext->empty() && cmSystemTools::StringEndsWith(name, ext->c_str()))
  65. {
  66. continue;
  67. }
  68. this->TestPath += *ext;
  69. if (cmSystemTools::FileExists(this->TestPath, true))
  70. {
  71. this->BestPath = cmSystemTools::CollapseFullPath(this->TestPath);
  72. return true;
  73. }
  74. }
  75. return false;
  76. }
  77. };
  78. cmFindProgramCommand::cmFindProgramCommand()
  79. {
  80. this->NamesPerDirAllowed = true;
  81. }
  82. // cmFindProgramCommand
  83. bool cmFindProgramCommand
  84. ::InitialPass(std::vector<std::string> const& argsIn, cmExecutionStatus &)
  85. {
  86. this->VariableDocumentation = "Path to a program.";
  87. this->CMakePathName = "PROGRAM";
  88. // call cmFindBase::ParseArguments
  89. if(!this->ParseArguments(argsIn))
  90. {
  91. return false;
  92. }
  93. if(this->AlreadyInCache)
  94. {
  95. // If the user specifies the entry on the command line without a
  96. // type we should add the type and docstring but keep the original
  97. // value.
  98. if(this->AlreadyInCacheWithoutMetaInfo)
  99. {
  100. this->Makefile->AddCacheDefinition(this->VariableName, "",
  101. this->VariableDocumentation.c_str(),
  102. cmState::FILEPATH);
  103. }
  104. return true;
  105. }
  106. std::string result = FindProgram();
  107. if(result != "")
  108. {
  109. // Save the value in the cache
  110. this->Makefile->AddCacheDefinition(this->VariableName,
  111. result.c_str(),
  112. this->VariableDocumentation.c_str(),
  113. cmState::FILEPATH);
  114. return true;
  115. }
  116. this->Makefile->AddCacheDefinition(this->VariableName,
  117. (this->VariableName + "-NOTFOUND").c_str(),
  118. this->VariableDocumentation.c_str(),
  119. cmState::FILEPATH);
  120. return true;
  121. }
  122. std::string cmFindProgramCommand::FindProgram()
  123. {
  124. std::string program = "";
  125. if(this->SearchAppBundleFirst || this->SearchAppBundleOnly)
  126. {
  127. program = FindAppBundle();
  128. }
  129. if(program.empty() && !this->SearchAppBundleOnly)
  130. {
  131. program = this->FindNormalProgram();
  132. }
  133. if(program.empty() && this->SearchAppBundleLast)
  134. {
  135. program = this->FindAppBundle();
  136. }
  137. return program;
  138. }
  139. //----------------------------------------------------------------------------
  140. std::string cmFindProgramCommand::FindNormalProgram()
  141. {
  142. if(this->NamesPerDir)
  143. {
  144. return this->FindNormalProgramNamesPerDir();
  145. }
  146. else
  147. {
  148. return this->FindNormalProgramDirsPerName();
  149. }
  150. }
  151. //----------------------------------------------------------------------------
  152. std::string cmFindProgramCommand::FindNormalProgramNamesPerDir()
  153. {
  154. // Search for all names in each directory.
  155. cmFindProgramHelper helper;
  156. for (std::vector<std::string>::const_iterator ni = this->Names.begin();
  157. ni != this->Names.end() ; ++ni)
  158. {
  159. helper.AddName(*ni);
  160. }
  161. // Check for the names themselves (e.g. absolute paths).
  162. if (helper.CheckDirectory(std::string()))
  163. {
  164. return helper.BestPath;
  165. }
  166. // Search every directory.
  167. for (std::vector<std::string>::const_iterator
  168. p = this->SearchPaths.begin(); p != this->SearchPaths.end(); ++p)
  169. {
  170. if(helper.CheckDirectory(*p))
  171. {
  172. return helper.BestPath;
  173. }
  174. }
  175. // Couldn't find the program.
  176. return "";
  177. }
  178. //----------------------------------------------------------------------------
  179. std::string cmFindProgramCommand::FindNormalProgramDirsPerName()
  180. {
  181. // Search the entire path for each name.
  182. cmFindProgramHelper helper;
  183. for (std::vector<std::string>::const_iterator ni = this->Names.begin();
  184. ni != this->Names.end() ; ++ni)
  185. {
  186. // Switch to searching for this name.
  187. helper.SetName(*ni);
  188. // Check for the name by itself (e.g. an absolute path).
  189. if (helper.CheckDirectory(std::string()))
  190. {
  191. return helper.BestPath;
  192. }
  193. // Search every directory.
  194. for (std::vector<std::string>::const_iterator
  195. p = this->SearchPaths.begin();
  196. p != this->SearchPaths.end(); ++p)
  197. {
  198. if (helper.CheckDirectory(*p))
  199. {
  200. return helper.BestPath;
  201. }
  202. }
  203. }
  204. // Couldn't find the program.
  205. return "";
  206. }
  207. std::string cmFindProgramCommand::FindAppBundle()
  208. {
  209. for(std::vector<std::string>::const_iterator name = this->Names.begin();
  210. name != this->Names.end() ; ++name)
  211. {
  212. std::string appName = *name + std::string(".app");
  213. std::string appPath = cmSystemTools::FindDirectory(appName,
  214. this->SearchPaths,
  215. true);
  216. if ( !appPath.empty() )
  217. {
  218. std::string executable = GetBundleExecutable(appPath);
  219. if (!executable.empty())
  220. {
  221. return cmSystemTools::CollapseFullPath(executable);
  222. }
  223. }
  224. }
  225. // Couldn't find app bundle
  226. return "";
  227. }
  228. std::string cmFindProgramCommand::GetBundleExecutable(std::string bundlePath)
  229. {
  230. std::string executable = "";
  231. (void)bundlePath;
  232. #if defined(__APPLE__)
  233. // Started with an example on developer.apple.com about finding bundles
  234. // and modified from that.
  235. // Get a CFString of the app bundle path
  236. // XXX - Is it safe to assume everything is in UTF8?
  237. CFStringRef bundlePathCFS =
  238. CFStringCreateWithCString(kCFAllocatorDefault ,
  239. bundlePath.c_str(), kCFStringEncodingUTF8 );
  240. // Make a CFURLRef from the CFString representation of the
  241. // bundle’s path.
  242. CFURLRef bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
  243. bundlePathCFS,
  244. kCFURLPOSIXPathStyle,
  245. true );
  246. // Make a bundle instance using the URLRef.
  247. CFBundleRef appBundle = CFBundleCreate( kCFAllocatorDefault, bundleURL );
  248. // returned executableURL is relative to <appbundle>/Contents/MacOS/
  249. CFURLRef executableURL = CFBundleCopyExecutableURL(appBundle);
  250. if (executableURL != NULL)
  251. {
  252. const int MAX_OSX_PATH_SIZE = 1024;
  253. char buffer[MAX_OSX_PATH_SIZE];
  254. // Convert the CFString to a C string
  255. CFStringGetCString( CFURLGetString(executableURL), buffer,
  256. MAX_OSX_PATH_SIZE, kCFStringEncodingUTF8 );
  257. // And finally to a c++ string
  258. executable = bundlePath + "/Contents/MacOS/" + std::string(buffer);
  259. // Only release CFURLRef if it's not null
  260. CFRelease( executableURL );
  261. }
  262. // Any CF objects returned from functions with "create" or
  263. // "copy" in their names must be released by us!
  264. CFRelease( bundlePathCFS );
  265. CFRelease( bundleURL );
  266. CFRelease( appBundle );
  267. #endif
  268. return executable;
  269. }