cmFindProgramCommand.cxx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  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 "cmCacheManager.h"
  12. #include <stdlib.h>
  13. #if defined(__APPLE__)
  14. #include <CoreFoundation/CoreFoundation.h>
  15. #endif
  16. // cmFindProgramCommand
  17. bool cmFindProgramCommand
  18. ::InitialPass(std::vector<std::string> const& argsIn, cmExecutionStatus &)
  19. {
  20. this->VariableDocumentation = "Path to a program.";
  21. this->CMakePathName = "PROGRAM";
  22. // call cmFindBase::ParseArguments
  23. if(!this->ParseArguments(argsIn))
  24. {
  25. return false;
  26. }
  27. if(this->AlreadyInCache)
  28. {
  29. // If the user specifies the entry on the command line without a
  30. // type we should add the type and docstring but keep the original
  31. // value.
  32. if(this->AlreadyInCacheWithoutMetaInfo)
  33. {
  34. this->Makefile->AddCacheDefinition(this->VariableName, "",
  35. this->VariableDocumentation.c_str(),
  36. cmCacheManager::FILEPATH);
  37. }
  38. return true;
  39. }
  40. std::string result = FindProgram(this->Names);
  41. if(result != "")
  42. {
  43. // Save the value in the cache
  44. this->Makefile->AddCacheDefinition(this->VariableName,
  45. result.c_str(),
  46. this->VariableDocumentation.c_str(),
  47. cmCacheManager::FILEPATH);
  48. return true;
  49. }
  50. this->Makefile->AddCacheDefinition(this->VariableName,
  51. (this->VariableName + "-NOTFOUND").c_str(),
  52. this->VariableDocumentation.c_str(),
  53. cmCacheManager::FILEPATH);
  54. return true;
  55. }
  56. std::string cmFindProgramCommand::FindProgram(std::vector<std::string> names)
  57. {
  58. std::string program = "";
  59. if(this->SearchAppBundleFirst || this->SearchAppBundleOnly)
  60. {
  61. program = FindAppBundle(names);
  62. }
  63. if(program.empty() && !this->SearchAppBundleOnly)
  64. {
  65. program = cmSystemTools::FindProgram(names, this->SearchPaths, true);
  66. }
  67. if(program.empty() && this->SearchAppBundleLast)
  68. {
  69. program = this->FindAppBundle(names);
  70. }
  71. return program;
  72. }
  73. std::string cmFindProgramCommand
  74. ::FindAppBundle(std::vector<std::string> names)
  75. {
  76. for(std::vector<std::string>::const_iterator name = names.begin();
  77. name != names.end() ; ++name)
  78. {
  79. std::string appName = *name + std::string(".app");
  80. std::string appPath = cmSystemTools::FindDirectory(appName.c_str(),
  81. this->SearchPaths,
  82. true);
  83. if ( !appPath.empty() )
  84. {
  85. std::string executable = GetBundleExecutable(appPath);
  86. if (!executable.empty())
  87. {
  88. return cmSystemTools::CollapseFullPath(executable.c_str());
  89. }
  90. }
  91. }
  92. // Couldn't find app bundle
  93. return "";
  94. }
  95. std::string cmFindProgramCommand::GetBundleExecutable(std::string bundlePath)
  96. {
  97. std::string executable = "";
  98. (void)bundlePath;
  99. #if defined(__APPLE__)
  100. // Started with an example on developer.apple.com about finding bundles
  101. // and modified from that.
  102. // Get a CFString of the app bundle path
  103. // XXX - Is it safe to assume everything is in UTF8?
  104. CFStringRef bundlePathCFS =
  105. CFStringCreateWithCString(kCFAllocatorDefault ,
  106. bundlePath.c_str(), kCFStringEncodingUTF8 );
  107. // Make a CFURLRef from the CFString representation of the
  108. // bundle’s path.
  109. CFURLRef bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
  110. bundlePathCFS,
  111. kCFURLPOSIXPathStyle,
  112. true );
  113. // Make a bundle instance using the URLRef.
  114. CFBundleRef appBundle = CFBundleCreate( kCFAllocatorDefault, bundleURL );
  115. // returned executableURL is relative to <appbundle>/Contents/MacOS/
  116. CFURLRef executableURL = CFBundleCopyExecutableURL(appBundle);
  117. if (executableURL != NULL)
  118. {
  119. const int MAX_OSX_PATH_SIZE = 1024;
  120. char buffer[MAX_OSX_PATH_SIZE];
  121. // Convert the CFString to a C string
  122. CFStringGetCString( CFURLGetString(executableURL), buffer,
  123. MAX_OSX_PATH_SIZE, kCFStringEncodingUTF8 );
  124. // And finally to a c++ string
  125. executable = bundlePath + "/Contents/MacOS/" + std::string(buffer);
  126. // Only release CFURLRef if it's not null
  127. CFRelease( executableURL );
  128. }
  129. // Any CF objects returned from functions with "create" or
  130. // "copy" in their names must be released by us!
  131. CFRelease( bundlePathCFS );
  132. CFRelease( bundleURL );
  133. CFRelease( appBundle );
  134. #endif
  135. return executable;
  136. }