diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index db611d01d..38688119a 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -939,6 +939,8 @@ IF(BUILD_TESTING) ENDIF(${CMAKE_TEST_GENERATOR} MATCHES "Visual Studio") IF (APPLE AND CMAKE_COMPILER_IS_GNUCXX) + SET(BundleTestInstallDir + "${CMake_BINARY_DIR}/Tests/BundleTest/InstallDirectory") ADD_TEST(BundleTest ${CMAKE_CTEST_COMMAND} --build-and-test "${CMake_SOURCE_DIR}/Tests/BundleTest" @@ -947,7 +949,10 @@ IF(BUILD_TESTING) --build-generator ${CMAKE_TEST_GENERATOR} --build-makeprogram ${CMAKE_TEST_MAKEPROGRAM} --build-project BundleTest - --test-command BundleTest.app/Contents/MacOS/BundleTest) + --build-target install + --build-options "-DCMAKE_INSTALL_PREFIX:PATH=${BundleTestInstallDir}" + --test-command + ${BundleTestInstallDir}/Application/BundleTestExe.app/Contents/MacOS/BundleTestExe) ADD_TEST(objc++ ${CMAKE_CTEST_COMMAND} --build-and-test diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 49b0c5544..59bef9ead 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -1375,7 +1375,8 @@ cmTarget cmGlobalGenerator::CreateGlobalTarget( } //---------------------------------------------------------------------------- -void cmGlobalGenerator::AppendDirectoryForConfig(const char*, std::string&) +void cmGlobalGenerator::AppendDirectoryForConfig(const char*, const char*, + const char*, std::string&) { // Subclasses that support multiple configurations should implement // this method to append the subdirectory for the given build diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index acc50955a..6791d937b 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -155,8 +155,13 @@ public: ///! Find a local generator by its startdirectory cmLocalGenerator* FindLocalGenerator(const char* start_dir); - /** Append the subdirectory for the given configuration. */ - virtual void AppendDirectoryForConfig(const char* config, std::string& dir); + /** Append the subdirectory for the given configuration. If anything is + appended the given prefix and suffix will be appended around it, which + is useful for leading or trailing slashes. */ + virtual void AppendDirectoryForConfig(const char* prefix, + const char* config, + const char* suffix, + std::string& dir); protected: // Fill the m_ProjectMap, this must be called after m_LocalGenerators has been populated. diff --git a/Source/cmGlobalVisualStudio6Generator.cxx b/Source/cmGlobalVisualStudio6Generator.cxx index d24fd70a3..46c021559 100644 --- a/Source/cmGlobalVisualStudio6Generator.cxx +++ b/Source/cmGlobalVisualStudio6Generator.cxx @@ -510,11 +510,15 @@ void cmGlobalVisualStudio6Generator::GetDocumentation(cmDocumentationEntry& entr //---------------------------------------------------------------------------- void cmGlobalVisualStudio6Generator -::AppendDirectoryForConfig(const char* config, std::string& dir) +::AppendDirectoryForConfig(const char* prefix, + const char* config, + const char* suffix, + std::string& dir) { if(config) { + dir += prefix; dir += config; - dir += "/"; + dir += suffix; } } diff --git a/Source/cmGlobalVisualStudio6Generator.h b/Source/cmGlobalVisualStudio6Generator.h index ab27124a7..cbf6e3b72 100644 --- a/Source/cmGlobalVisualStudio6Generator.h +++ b/Source/cmGlobalVisualStudio6Generator.h @@ -76,7 +76,10 @@ public: std::vector& generators); /** Append the subdirectory for the given configuration. */ - virtual void AppendDirectoryForConfig(const char* config, std::string& dir); + virtual void AppendDirectoryForConfig(const char* prefix, + const char* config, + const char* suffix, + std::string& dir); ///! What is the configurations directory variable called? virtual const char* GetCMakeCFGInitDirectory() { return "$(IntDir)"; } diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index 72a654ae0..85ea04c92 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -736,11 +736,15 @@ void cmGlobalVisualStudio7Generator::Configure() //---------------------------------------------------------------------------- void cmGlobalVisualStudio7Generator -::AppendDirectoryForConfig(const char* config, std::string& dir) +::AppendDirectoryForConfig(const char* prefix, + const char* config, + const char* suffix, + std::string& dir) { if(config) { + dir += prefix; dir += config; - dir += "/"; + dir += suffix; } } diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h index cff22f403..946cfd860 100644 --- a/Source/cmGlobalVisualStudio7Generator.h +++ b/Source/cmGlobalVisualStudio7Generator.h @@ -82,7 +82,10 @@ public: virtual void Configure(); /** Append the subdirectory for the given configuration. */ - virtual void AppendDirectoryForConfig(const char* config, std::string& dir); + virtual void AppendDirectoryForConfig(const char* prefix, + const char* config, + const char* suffix, + std::string& dir); ///! What is the configurations directory variable called? virtual const char* GetCMakeCFGInitDirectory() { return "$(OutDir)"; } diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index cf9a6f284..b33b45978 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -2335,14 +2335,18 @@ std::string cmGlobalXCodeGenerator::XCodeEscapePath(const char* p) //---------------------------------------------------------------------------- void cmGlobalXCodeGenerator -::AppendDirectoryForConfig(const char* config, std::string& dir) +::AppendDirectoryForConfig(const char* prefix, + const char* config, + const char* suffix, + std::string& dir) { if(m_XcodeVersion > 20) { if(config) { + dir += prefix; dir += config; - dir += "/"; + dir += suffix; } } } diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index cc6a39a90..7bb7f8d69 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h @@ -70,7 +70,10 @@ public: virtual void Generate(); /** Append the subdirectory for the given configuration. */ - virtual void AppendDirectoryForConfig(const char* config, std::string& dir); + virtual void AppendDirectoryForConfig(const char* prefix, + const char* config, + const char* suffix, + std::string& dir); ///! What is the configurations directory variable called? virtual const char* GetCMakeCFGInitDirectory() { return "."; } diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx index 62f050472..6be87ec01 100644 --- a/Source/cmInstallTargetGenerator.cxx +++ b/Source/cmInstallTargetGenerator.cxx @@ -60,6 +60,10 @@ void cmInstallTargetGenerator::GenerateScript(std::ostream& os) std::string fromFile = fromDir; fromFile += fromName; + // Choose the final destination. This may be modified for certain + // target types. + std::string destination = this->Destination; + // Setup special properties for some target types. std::string props; const char* properties = 0; @@ -114,23 +118,27 @@ void cmInstallTargetGenerator::GenerateScript(std::ostream& os) { // Compute the source locations of the bundle executable and // Info.plist file. + this->PrepareScriptReference(os, this->Target, "INSTALL", + false, false); std::string plist = fromFile; plist += ".app/Contents/Info.plist"; fromFile += ".app/Contents/MacOS/"; - fromFile += fromName; + fromFile += this->GetScriptReference(this->Target, "INSTALL", + false); + + // Compute the destination locations of the bundle Info.plist file. + destination += "/"; + destination += this->GetScriptReference(this->Target, "INSTALL", + false); + destination += ".app/Contents"; - // Compute the destination locations of the bundle executable - // and Info.plist file. - std::string bdest = this->Destination; - bdest += "/"; - bdest += fromName; - std::string pdest = bdest; - pdest += ".app/Contents"; - bdest += ".app/Contents/MacOS"; // Install the Info.plist file. - this->AddInstallRule(os, pdest.c_str(), cmTarget::INSTALL_FILES, + this->AddInstallRule(os, destination.c_str(), cmTarget::INSTALL_FILES, plist.c_str()); + + // Compute the destination locations of the bundle executable file. + destination += "/MacOS"; } } break; @@ -149,7 +157,7 @@ void cmInstallTargetGenerator::GenerateScript(std::ostream& os) } // Write code to install the target file. - this->AddInstallRule(os, this->Destination.c_str(), type, fromFile.c_str(), + this->AddInstallRule(os, destination.c_str(), type, fromFile.c_str(), this->ImportLibrary, properties); // Fix the install_name settings in installed binaries. @@ -157,7 +165,7 @@ void cmInstallTargetGenerator::GenerateScript(std::ostream& os) type == cmTarget::MODULE_LIBRARY || type == cmTarget::EXECUTABLE) { - this->AddInstallNamePatchRule(os); + this->AddInstallNamePatchRule(os, destination.c_str()); } } @@ -182,7 +190,7 @@ cmInstallTargetGenerator { // Start with the configuration's subdirectory. target->GetMakefile()->GetLocalGenerator()->GetGlobalGenerator()-> - AppendDirectoryForConfig(i->c_str(), fname); + AppendDirectoryForConfig("", i->c_str(), "/", fname); } // Compute the name of the library. @@ -268,7 +276,8 @@ std::string cmInstallTargetGenerator::GetScriptReference(cmTarget* target, } //---------------------------------------------------------------------------- -void cmInstallTargetGenerator::AddInstallNamePatchRule(std::ostream& os) +void cmInstallTargetGenerator::AddInstallNamePatchRule(std::ostream& os, + const char* destination) { // Build a map of build-tree install_name to install-tree install_name for // shared libraries linked to this target. @@ -348,7 +357,7 @@ void cmInstallTargetGenerator::AddInstallNamePatchRule(std::ostream& os) { os << "\n -change \"" << i->first << "\" \"" << i->second << "\""; } - os << "\n \"" << this->Destination.c_str() << "/" + os << "\n \"" << destination << "/" << this->GetScriptReference(this->Target, "REMAPPED", true) << "\")\n"; } } diff --git a/Source/cmInstallTargetGenerator.h b/Source/cmInstallTargetGenerator.h index ac7058a2d..8ae834b39 100644 --- a/Source/cmInstallTargetGenerator.h +++ b/Source/cmInstallTargetGenerator.h @@ -38,7 +38,7 @@ protected: bool useSOName); std::string GetScriptReference(cmTarget* target, const char* place, bool useSOName); - void AddInstallNamePatchRule(std::ostream& os); + void AddInstallNamePatchRule(std::ostream& os, const char* destination); cmTarget* Target; std::string Destination; bool ImportLibrary; diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index b76334923..73b1e590c 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx @@ -109,7 +109,7 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) if(this->Target->GetPropertyAsBool("MACOSX_BUNDLE")) { // Make bundle directories - outpath += this->Target->GetName(); + outpath += targetName; outpath += ".app/Contents/MacOS/"; std::string f1 = this->Makefile->GetModulesFile("MacOSXBundleInfo.plist.in"); @@ -127,14 +127,17 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) { macdir += "/"; } - macdir += this->Target->GetName(); + macdir += targetName; macdir += ".app/Contents/"; + + // Configure the Info.plist file. Note that it needs the executable name + // to be set. std::string f2 = macdir + "Info.plist"; macdir += "MacOS"; cmSystemTools::MakeDirectory(macdir.c_str()); - this->Makefile->AddDefinition("MACOSX_BUNDLE_EXECUTABLE_NAME", this->Target->GetName()); - this->Makefile->ConfigureFile(f1.c_str(), f2.c_str(), - false, false, false); + this->Makefile->AddDefinition("MACOSX_BUNDLE_EXECUTABLE_NAME", + targetName.c_str()); + this->Makefile->ConfigureFile(f1.c_str(), f2.c_str(), false, false, false); } #endif if(relink) diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index a02e34ed1..3fd75a572 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -831,9 +831,8 @@ const char* cmTarget::GetDirectory(const char* config) if(config) { // Add the configuration's subdirectory. - m_Directory += "/"; m_Makefile->GetLocalGenerator()->GetGlobalGenerator()-> - AppendDirectoryForConfig(config, m_Directory); + AppendDirectoryForConfig("/", config, "", m_Directory); } return m_Directory.c_str(); } diff --git a/Tests/BundleTest/BundleLib.cxx b/Tests/BundleTest/BundleLib.cxx new file mode 100644 index 000000000..9fe07f82f --- /dev/null +++ b/Tests/BundleTest/BundleLib.cxx @@ -0,0 +1 @@ +int foo() { return 0; } diff --git a/Tests/BundleTest/BundleTest.cxx b/Tests/BundleTest/BundleTest.cxx index 59c0f1efc..c879be14d 100644 --- a/Tests/BundleTest/BundleTest.cxx +++ b/Tests/BundleTest/BundleTest.cxx @@ -1,4 +1,6 @@ +extern int foo(); + int main() { -return 0; + return foo(); } diff --git a/Tests/BundleTest/CMakeLists.txt b/Tests/BundleTest/CMakeLists.txt index dcc679854..4bab24ef1 100644 --- a/Tests/BundleTest/CMakeLists.txt +++ b/Tests/BundleTest/CMakeLists.txt @@ -1,4 +1,26 @@ PROJECT(BundleTest) SET(MACOSX_BUNDLE_INFO_STRING "bundle_info_string") -ADD_EXECUTABLE(BundleTest MACOSX_BUNDLE BundleTest.cxx) +# Test building a bundle linking to a shared library. +ADD_LIBRARY(BundleTestLib SHARED BundleLib.cxx) +ADD_EXECUTABLE(BundleTest MACOSX_BUNDLE BundleTest.cxx) +TARGET_LINK_LIBRARIES(BundleTest BundleTestLib) + +# Test bundle installation. +INSTALL(TARGETS BundleTestLib DESTINATION Application/BundleTestExe.app/Contents/Plugins) +INSTALL(TARGETS BundleTest DESTINATION Application) + +# Test whether bundles respect the output name. Since the library is +# installed into a location that uses this output name this will fail if the +# bundle does not respect the name. Also the executable will not be found by +# the test driver if this does not work. +SET_TARGET_PROPERTIES(BundleTest PROPERTIES OUTPUT_NAME BundleTestExe) + +# Test executable versioning if it is supported. +IF(NOT XCODE) + SET_TARGET_PROPERTIES(BundleTest PROPERTIES VERSION 1) +ENDIF(NOT XCODE) + +# Make sure the executable can find its installed library. +SET_TARGET_PROPERTIES(BundleTestLib PROPERTIES + INSTALL_NAME_DIR "@executable_path/../Plugins")