From cae4e6b37a6ff7caf0748f2702fb503a9c95dcc8 Mon Sep 17 00:00:00 2001 From: Bill Hoffman Date: Thu, 13 Apr 2006 11:00:52 -0400 Subject: ENH: add patch for finding applications on OSX --- Modules/Platform/Darwin.cmake | 8 +++ Source/CMakeLists.txt | 6 +++ Source/cmFindBase.cxx | 110 ++++++++++++++++++++++++++++++++++++++-- Source/cmFindBase.h | 5 ++ Source/cmFindProgramCommand.cxx | 96 ++++++++++++++++++++++++++++++++++- Source/cmFindProgramCommand.h | 8 +++ Source/kwsys/SystemTools.cxx | 55 +++++++++++++++++--- Source/kwsys/SystemTools.hxx.in | 23 ++++++++- bootstrap | 20 ++++++-- 9 files changed, 314 insertions(+), 17 deletions(-) diff --git a/Modules/Platform/Darwin.cmake b/Modules/Platform/Darwin.cmake index fb70f3b..884690a 100644 --- a/Modules/Platform/Darwin.cmake +++ b/Modules/Platform/Darwin.cmake @@ -73,6 +73,14 @@ SET(CMAKE_SYSTEM_FRAMEWORK_PATH /Network/Library/Frameworks /System/Library/Frameworks) +# default to searching for application bundles first +SET(CMAKE_FIND_APPBUNDLE FIRST) +# set up the default search directories for application bundles +SET(CMAKE_SYSTEM_APPBUNDLE_PATH + ~/Applications + /Applications + /Developer/Applications) + INCLUDE(Platform/UnixPaths) SET(CMAKE_SYSTEM_INCLUDE_PATH ${CMAKE_SYSTEM_INCLUDE_PATH} /sw/include) SET(CMAKE_SYSTEM_LIBRARY_PATH ${CMAKE_SYSTEM_LIBRARY_PATH} /sw/lib) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index e657f49..ee346a3 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -220,6 +220,12 @@ IF(UNIX) TARGET_LINK_LIBRARIES(CMakeLib ${CMAKE_DL_LIBS}) ENDIF(UNIX) +# On Apple we need Carbon +IF(APPLE) + FIND_LIBRARY(CARBON Carbon) + TARGET_LINK_LIBRARIES(CMakeLib ${CARBON}) +ENDIF(APPLE) + # On some platforms we need the rpcrt4 library for the VS 7 generators. IF(CMAKE_BUILD_ON_VISUAL_STUDIO OR MINGW) TARGET_LINK_LIBRARIES(CMakeLib rpcrt4) diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx index 758065e..8600fa8 100644 --- a/Source/cmFindBase.cxx +++ b/Source/cmFindBase.cxx @@ -27,11 +27,15 @@ cmFindBase::cmFindBase() // default is to search frameworks first on apple #if defined(__APPLE__) this->SearchFrameworkFirst = true; + this->SearchAppBundleFirst = true; #else this->SearchFrameworkFirst = false; + this->SearchAppBundleFirst = false; #endif this->SearchFrameworkOnly = false; this->SearchFrameworkLast = false; + this->SearchAppBundleOnly = false; + this->SearchAppBundleLast = false; this->GenericDocumentation = " FIND_XXX( name1 path1 path2 ...)\n" "This is the short-hand signature for the command that " @@ -71,6 +75,7 @@ cmFindBase::cmFindBase() "can be skipped if NO_CMAKE_ENVIRONMENT_PATH is passed.\n" "" " CMAKE_FRAMEWORK_PATH\n" + " CMAKE_APPBUNDLE_PATH\n" " CMAKE_XXX_PATH\n" "2. Search cmake variables with the same names as " "the cmake specific environment variables. These " @@ -79,6 +84,7 @@ cmFindBase::cmFindBase() "is passed.\n" "" " CMAKE_FRAMEWORK_PATH\n" + " CMAKE_APPBUNDLE_PATH\n" " CMAKE_XXX_PATH\n" "3. Search the standard system environment variables. " "This can be skipped if NO_SYSTEM_ENVIRONMENT_PATH is an argument.\n" @@ -88,6 +94,7 @@ cmFindBase::cmFindBase() "for the current system. This can be skipped if NO_CMAKE_SYSTEM_PATH " "is passed.\n" " CMAKE_SYSTEM_FRAMEWORK_PATH\n" + " CMAKE_SYSTEM_APPBUNDLE_PATH\n" " CMAKE_SYSTEM_XXX_PATH\n" "5. Search the paths specified after PATHS or in the short-hand version " "of the command.\n" @@ -99,6 +106,14 @@ cmFindBase::cmFindBase() " libraries or headers.\n" " \"ONLY\" - Only try to find frameworks.\n" " \"NEVER\". - Never try to find frameworks.\n" + "On Darwin or systems supporting OSX Application Bundles, the cmake variable" + " CMAKE_FIND_APPBUNDLE can be set to empty or one of the following:\n" + " \"FIRST\" - Try to find application bundles before standard\n" + " programs. This is the default on Darwin.\n" + " \"LAST\" - Try to find application bundles after standard\n" + " programs.\n" + " \"ONLY\" - Only try to find application bundles.\n" + " \"NEVER\". - Never try to find application bundles.\n" "The reason the paths listed in the call to the command are searched " "last is that most users of CMake would expect things to be found " "first in the locations specified by their environment. Projects may " @@ -142,6 +157,32 @@ bool cmFindBase::ParseArguments(std::vector const& argsIn) this->SearchFrameworkOnly = false; } + std::string fab = this->Makefile->GetSafeDefinition("CMAKE_FIND_APPBUNDLE"); + if(fab == "NEVER") + { + this->SearchAppBundleLast = false; + this->SearchAppBundleFirst = false; + this->SearchAppBundleOnly = false; + } + else if (fab == "ONLY") + { + this->SearchAppBundleLast = false; + this->SearchAppBundleFirst = false; + this->SearchAppBundleOnly = true; + } + else if (fab == "FIRST") + { + this->SearchAppBundleLast = false; + this->SearchAppBundleFirst = true; + this->SearchAppBundleOnly = false; + } + else if (fab == "LAST") + { + this->SearchAppBundleLast = true; + this->SearchAppBundleFirst = false; + this->SearchAppBundleOnly = false; + } + // CMake versions below 2.3 did not search all these extra // locations. Preserve compatibility unless a modern argument is // passed. @@ -355,28 +396,36 @@ void cmFindBase::ExpandPaths(std::vector userPaths) { this->AddFrameWorkPaths(); } - if(!this->NoCMakeEnvironmentPath && !this->SearchFrameworkOnly) + if(this->SearchAppBundleFirst) + { + this->AddAppBundlePaths(); + } + if(!this->NoCMakeEnvironmentPath && !(this->SearchFrameworkOnly || this->SearchAppBundleOnly)) { // Add CMAKE_*_PATH environment variables this->AddEnvironmentVairables(); } - if(!this->NoCMakePath && !this->SearchFrameworkOnly) + if(!this->NoCMakePath && !(this->SearchFrameworkOnly || this->SearchAppBundleOnly)) { // Add CMake varibles of the same name as the previous environment // varibles CMAKE_*_PATH to be used most of the time with -D // command line options this->AddCMakeVairables(); } - if(!this->NoSystemEnvironmentPath && !this->SearchFrameworkOnly) + if(!this->NoSystemEnvironmentPath && !(this->SearchFrameworkOnly || this->SearchAppBundleOnly)) { // add System environment PATH and (LIB or INCLUDE) this->AddSystemEnvironmentVairables(); } - if(!this->NoCMakeSystemPath && !this->SearchFrameworkOnly) + if(!this->NoCMakeSystemPath && !(this->SearchFrameworkOnly || this->SearchAppBundleOnly)) { // Add CMAKE_SYSTEM_*_PATH variables which are defined in platform files this->AddCMakeSystemVariables(); } + if(this->SearchAppBundleLast) + { + this->AddAppBundlePaths(); + } if(this->SearchFrameworkLast) { this->AddFrameWorkPaths(); @@ -398,6 +447,10 @@ void cmFindBase::AddEnvironmentVairables() var += this->CMakePathName; var += "_PATH"; cmSystemTools::GetPath(this->SearchPaths, var.c_str()); + if(this->SearchAppBundleLast) + { + cmSystemTools::GetPath(this->SearchPaths, "CMAKE_APPBUNDLE_PATH"); + } if(this->SearchFrameworkLast) { cmSystemTools::GetPath(this->SearchPaths, "CMAKE_FRAMEWORK_PATH"); @@ -436,6 +489,37 @@ void cmFindBase::AddFrameWorkPaths() } } +void cmFindBase::AddAppBundlePaths() +{ + if(this->NoDefaultPath) + { + return; + } + // first environment variables + if(!this->NoCMakeEnvironmentPath) + { + cmSystemTools::GetPath(this->SearchPaths, "CMAKE_APPBUNDLE_PATH"); + } + // add cmake variables + if(!this->NoCMakePath) + { + if(const char* path = + this->Makefile->GetDefinition("CMAKE_APPBUNDLE_PATH")) + { + cmSystemTools::ExpandListArgument(path, this->SearchPaths); + } + } + // AddCMakeSystemVariables + if(!this->NoCMakeSystemPath) + { + if(const char* path = + this->Makefile->GetDefinition("CMAKE_SYSTEM_APPBUNDLE_PATH")) + { + cmSystemTools::ExpandListArgument(path, this->SearchPaths); + } + } +} + void cmFindBase::AddCMakeVairables() { std::string var = "CMAKE_"; @@ -445,6 +529,14 @@ void cmFindBase::AddCMakeVairables() { cmSystemTools::ExpandListArgument(path, this->SearchPaths); } + if(this->SearchAppBundleLast) + { + if(const char* path = + this->Makefile->GetDefinition("CMAKE_APPBUNDLE_PATH")) + { + cmSystemTools::ExpandListArgument(path, this->SearchPaths); + } + } if(this->SearchFrameworkLast) { if(const char* path = @@ -475,6 +567,13 @@ void cmFindBase::AddCMakeSystemVariables() { cmSystemTools::ExpandListArgument(path, this->SearchPaths); } + if(this->SearchAppBundleLast) + { + if(const char* path = this->Makefile->GetDefinition("CMAKE_SYSTEM_APPBUNDLE_PATH")) + { + cmSystemTools::ExpandListArgument(path, this->SearchPaths); + } + } if(this->SearchFrameworkLast) { if(const char* path = this->Makefile->GetDefinition("CMAKE_SYSTEM_FRAMEWORK_PATH")) @@ -528,6 +627,9 @@ void cmFindBase::PrintFindStuff() std::cerr << "SearchFrameworkLast: " << this->SearchFrameworkLast << "\n"; std::cerr << "SearchFrameworkOnly: " << this->SearchFrameworkOnly << "\n"; std::cerr << "SearchFrameworkFirst: " << this->SearchFrameworkFirst << "\n"; + std::cerr << "SearchAppBundleLast: " << this->SearchAppBundleLast << "\n"; + std::cerr << "SearchAppBundleOnly: " << this->SearchAppBundleOnly << "\n"; + std::cerr << "SearchAppBundleFirst: " << this->SearchAppBundleFirst << "\n"; std::cerr << "VariableName " << this->VariableName << "\n"; std::cerr << "VariableDocumentation " << this->VariableDocumentation << "\n"; std::cerr << "NoDefaultPath " << this->NoDefaultPath << "\n"; diff --git a/Source/cmFindBase.h b/Source/cmFindBase.h index ddb4630..60c8853 100644 --- a/Source/cmFindBase.h +++ b/Source/cmFindBase.h @@ -43,6 +43,7 @@ protected: void PrintFindStuff(); void ExpandPaths(std::vector userPaths); void AddFrameWorkPaths(); + void AddAppBundlePaths(); void AddEnvironmentVairables(); void AddCMakeVairables(); void AddSystemEnvironmentVairables(); @@ -76,6 +77,10 @@ protected: bool SearchFrameworkOnly; bool SearchFrameworkLast; + bool SearchAppBundleFirst; + bool SearchAppBundleOnly; + bool SearchAppBundleLast; + }; diff --git a/Source/cmFindProgramCommand.cxx b/Source/cmFindProgramCommand.cxx index 5b08fc8..5e750bb 100644 --- a/Source/cmFindProgramCommand.cxx +++ b/Source/cmFindProgramCommand.cxx @@ -17,6 +17,10 @@ #include "cmFindProgramCommand.h" #include "cmCacheManager.h" #include + +#if defined(__APPLE__) +#include +#endif cmFindProgramCommand::cmFindProgramCommand() { @@ -48,8 +52,8 @@ bool cmFindProgramCommand::InitialPass(std::vector const& argsIn) { return true; } - std::string result = cmSystemTools::FindProgram(this->Names, - this->SearchPaths, true); + + std::string result = FindProgram(this->Names); if(result != "") { // Save the value in the cache @@ -67,3 +71,91 @@ bool cmFindProgramCommand::InitialPass(std::vector const& argsIn) return true; } +std::string cmFindProgramCommand::FindProgram(std::vector names) +{ + std::string program = ""; + + // First/last order taken care of in cmFindBase when the paths are setup. + if(this->SearchAppBundleFirst || this->SearchAppBundleLast) + { + program = FindAppBundle(names); + } + if(program.empty() && !this->SearchAppBundleOnly) + { + program = cmSystemTools::FindProgram(names, this->SearchPaths, true); + } + + return program; +} + +std::string cmFindProgramCommand::FindAppBundle(std::vector names) +{ + for(std::vector::const_iterator name = names.begin(); + name != names.end() ; ++name) + { + + std::string appName = *name + std::string(".app"); + std::string appPath = cmSystemTools::FindDirectory(appName.c_str(), this->SearchPaths, true); + + if ( !appPath.empty() ) + { + std::string executable = GetBundleExecutable(appPath); + if (!executable.empty()) + { + return cmSystemTools::CollapseFullPath(executable.c_str()); + } + } + } + + // Couldn't find app bundle + return ""; +} + +std::string cmFindProgramCommand::GetBundleExecutable(std::string bundlePath) +{ + std::string executable = ""; + +#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 /Contents/MacOS/ + CFURLRef executableURL = CFBundleCopyExecutableURL(appBundle); + + if (executableURL != NULL) + { + 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); + } + + // Any CF objects returned from functions with "create" or + // "copy" in their names must be released by us! + CFRelease( bundlePathCFS ); + CFRelease( bundleURL ); + CFRelease( appBundle ); + CFRelease( executableURL ); +#endif + + return executable; +} + diff --git a/Source/cmFindProgramCommand.h b/Source/cmFindProgramCommand.h index af5c6de..2113b02 100644 --- a/Source/cmFindProgramCommand.h +++ b/Source/cmFindProgramCommand.h @@ -64,6 +64,14 @@ public: } cmTypeMacro(cmFindProgramCommand, cmFindBase); + +protected: + std::string FindProgram(std::vector names); + +private: + std::string FindAppBundle(std::vector names); + std::string GetBundleExecutable(std::string bundlePath); + }; diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx index 0e77dbe..b404914 100644 --- a/Source/kwsys/SystemTools.cxx +++ b/Source/kwsys/SystemTools.cxx @@ -1877,13 +1877,17 @@ size_t SystemTools::GetMaximumFilePathLength() * found. Otherwise, the empty string is returned. */ kwsys_stl::string SystemTools -::FindFile(const char* name, - const kwsys_stl::vector& userPaths) +::FindName(const char* name, + const kwsys_stl::vector& userPaths, + bool no_system_path) { // Add the system search path to our path first kwsys_stl::vector path; - SystemTools::GetPath(path, "CMAKE_FILE_PATH"); - SystemTools::GetPath(path); + if (!no_system_path) + { + SystemTools::GetPath(path, "CMAKE_FILE_PATH"); + SystemTools::GetPath(path); + } // now add the additional paths for(kwsys_stl::vector::const_iterator i = userPaths.begin(); i != userPaths.end(); ++i) @@ -1898,10 +1902,9 @@ kwsys_stl::string SystemTools tryPath = *p; tryPath += "/"; tryPath += name; - if(SystemTools::FileExists(tryPath.c_str()) && - !SystemTools::FileIsDirectory(tryPath.c_str())) + if(SystemTools::FileExists(tryPath.c_str())) { - return SystemTools::CollapseFullPath(tryPath.c_str()); + return tryPath; } } // Couldn't find the file. @@ -1909,6 +1912,44 @@ kwsys_stl::string SystemTools } /** + * Find the file the given name. Searches the given path and then + * the system search path. Returns the full path to the file if it is + * found. Otherwise, the empty string is returned. + */ +kwsys_stl::string SystemTools +::FindFile(const char* name, + const kwsys_stl::vector& userPaths, + bool no_system_path) +{ + kwsys_stl::string tryPath = SystemTools::FindName(name, userPaths, no_system_path); + if(tryPath != "" && !SystemTools::FileIsDirectory(tryPath.c_str())) + { + return SystemTools::CollapseFullPath(tryPath.c_str()); + } + // Couldn't find the file. + return ""; +} + +/** + * Find the directory the given name. Searches the given path and then + * the system search path. Returns the full path to the directory if it is + * found. Otherwise, the empty string is returned. + */ +kwsys_stl::string SystemTools +::FindDirectory(const char* name, + const kwsys_stl::vector& userPaths, + bool no_system_path) +{ + kwsys_stl::string tryPath = SystemTools::FindName(name, userPaths, no_system_path); + if(tryPath != "" && SystemTools::FileIsDirectory(tryPath.c_str())) + { + return SystemTools::CollapseFullPath(tryPath.c_str()); + } + // Couldn't find the file. + return ""; +} + +/** * Find the executable with the given name. Searches the given path and then * the system search path. Returns the full path to the executable if it is * found. Otherwise, the empty string is returned. diff --git a/Source/kwsys/SystemTools.hxx.in b/Source/kwsys/SystemTools.hxx.in index f0afa36..af26444 100644 --- a/Source/kwsys/SystemTools.hxx.in +++ b/Source/kwsys/SystemTools.hxx.in @@ -495,7 +495,17 @@ public: static kwsys_stl::string FindFile( const char* name, const kwsys_stl::vector& path = - kwsys_stl::vector()); + kwsys_stl::vector(), + bool no_system_path = false); + + /** + * Find a directory in the system PATH, with optional extra paths + */ + static kwsys_stl::string FindDirectory( + const char* name, + const kwsys_stl::vector& path = + kwsys_stl::vector(), + bool no_system_path = false); /** * Find an executable in the system PATH, with optional extra paths @@ -763,6 +773,17 @@ private: } /** + * Find a filename (file or directory) in the system PATH, with + * optional extra paths. + */ + static kwsys_stl::string FindName( + const char* name, + const kwsys_stl::vector& path = + kwsys_stl::vector(), + bool no_system_path = false); + + + /** * Path translation table from dir to refdir * Each time 'dir' will be found it will be replace by 'refdir' */ diff --git a/bootstrap b/bootstrap index f00d92e..9acfbe4 100755 --- a/bootstrap +++ b/bootstrap @@ -35,6 +35,13 @@ else cmake_system_mingw=false fi +# Determine whether this is OS X +if echo "${cmake_system}" | grep Darwin >/dev/null 2>&1; then + cmake_system_darwin=true +else + cmake_system_darwin=false +fi + # Choose the generator to use for bootstrapping. if ${cmake_system_mingw}; then # Bootstrapping from an MSYS prompt. @@ -509,6 +516,13 @@ rm -f "${cmake_bootstrap_dir}/cmConfigure.h.tmp" cmake_c_flags=${CFLAGS} cmake_cxx_flags=${CXXFLAGS} +# Add Carbon framework on Darwin +if ${cmake_system_darwin}; then + cmake_ld_flags="${LDFLAGS} -framework Carbon" +else + cmake_ld_flags=${LDFLAGS} +fi + # Test C compiler cmake_c_compiler= @@ -1162,7 +1176,7 @@ cmake_c_flags="${cmake_c_flags}-I`cmake_escape \"${cmake_source_dir}/Source\"` \ cmake_cxx_flags="${cmake_cxx_flags}-I`cmake_escape \"${cmake_source_dir}/Source\"` \ -I`cmake_escape \"${cmake_bootstrap_dir}\"`" echo "cmake: ${objs}" > "${cmake_bootstrap_dir}/Makefile" -echo " ${cmake_cxx_compiler} ${LDFLAGS} ${cmake_cxx_flags} ${objs} -o cmake" >> "${cmake_bootstrap_dir}/Makefile" +echo " ${cmake_cxx_compiler} ${cmake_ld_flags} ${cmake_cxx_flags} ${objs} -o cmake" >> "${cmake_bootstrap_dir}/Makefile" for a in ${CMAKE_CXX_SOURCES}; do src=`cmake_escape "${cmake_source_dir}/Source/${a}.cxx"` echo "${a}.o : ${src} ${dep}" >> "${cmake_bootstrap_dir}/Makefile" @@ -1190,9 +1204,9 @@ if ${cmake_system_mingw}; then cmd=`cmake_escape "${cmake_bootstrap_dir}/cmsysEncodeExecutable.exe"` a="cmsysProcessFwd9xEnc" echo "${cmd} : EncodeExecutable.o" >> "${cmake_bootstrap_dir}/Makefile" - echo " ${cmake_c_compiler} ${LDFLAGS} ${cmake_c_flags} EncodeExecutable.o -o ${cmd}" >> "${cmake_bootstrap_dir}/Makefile" + echo " ${cmake_c_compiler} ${cmake_ld_flags} ${cmake_c_flags} EncodeExecutable.o -o ${cmd}" >> "${cmake_bootstrap_dir}/Makefile" echo "${in} : ProcessFwd9x.o" >> "${cmake_bootstrap_dir}/Makefile" - echo " ${cmake_c_compiler} ${LDFLAGS} ${cmake_c_flags} ProcessFwd9x.o -o ${in}" >> "${cmake_bootstrap_dir}/Makefile" + echo " ${cmake_c_compiler} ${cmake_ld_flags} ${cmake_c_flags} ProcessFwd9x.o -o ${in}" >> "${cmake_bootstrap_dir}/Makefile" echo "${src} : ${cmd} ${in}" >> "${cmake_bootstrap_dir}/Makefile" echo " ${cmd} ${in} ${src} cmsys ProcessFwd9x" >> "${cmake_bootstrap_dir}/Makefile" echo "${a}.o : ${src} ${dep}" >> "${cmake_bootstrap_dir}/Makefile" -- cgit v0.12