BUG: Fixed installation of MacOSX Bundle executables and the corresponding install_name remapping support. Extended the BundleTest test to check that this all works. Part of these fixes required changing the signature of AppendDirectoryForConfig in all generators. It now accepts prefix and suffix strings to deal with whether leading or trailing slashes should be included with the configuration subdirectory.
This commit is contained in:
parent
ea8c278cd6
commit
93c95f1cc5
|
@ -939,6 +939,8 @@ IF(BUILD_TESTING)
|
||||||
ENDIF(${CMAKE_TEST_GENERATOR} MATCHES "Visual Studio")
|
ENDIF(${CMAKE_TEST_GENERATOR} MATCHES "Visual Studio")
|
||||||
|
|
||||||
IF (APPLE AND CMAKE_COMPILER_IS_GNUCXX)
|
IF (APPLE AND CMAKE_COMPILER_IS_GNUCXX)
|
||||||
|
SET(BundleTestInstallDir
|
||||||
|
"${CMake_BINARY_DIR}/Tests/BundleTest/InstallDirectory")
|
||||||
ADD_TEST(BundleTest ${CMAKE_CTEST_COMMAND}
|
ADD_TEST(BundleTest ${CMAKE_CTEST_COMMAND}
|
||||||
--build-and-test
|
--build-and-test
|
||||||
"${CMake_SOURCE_DIR}/Tests/BundleTest"
|
"${CMake_SOURCE_DIR}/Tests/BundleTest"
|
||||||
|
@ -947,7 +949,10 @@ IF(BUILD_TESTING)
|
||||||
--build-generator ${CMAKE_TEST_GENERATOR}
|
--build-generator ${CMAKE_TEST_GENERATOR}
|
||||||
--build-makeprogram ${CMAKE_TEST_MAKEPROGRAM}
|
--build-makeprogram ${CMAKE_TEST_MAKEPROGRAM}
|
||||||
--build-project BundleTest
|
--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}
|
ADD_TEST(objc++ ${CMAKE_CTEST_COMMAND}
|
||||||
--build-and-test
|
--build-and-test
|
||||||
|
|
|
@ -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
|
// Subclasses that support multiple configurations should implement
|
||||||
// this method to append the subdirectory for the given build
|
// this method to append the subdirectory for the given build
|
||||||
|
|
|
@ -155,8 +155,13 @@ public:
|
||||||
///! Find a local generator by its startdirectory
|
///! Find a local generator by its startdirectory
|
||||||
cmLocalGenerator* FindLocalGenerator(const char* start_dir);
|
cmLocalGenerator* FindLocalGenerator(const char* start_dir);
|
||||||
|
|
||||||
/** Append the subdirectory for the given configuration. */
|
/** Append the subdirectory for the given configuration. If anything is
|
||||||
virtual void AppendDirectoryForConfig(const char* config, std::string& dir);
|
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:
|
protected:
|
||||||
// Fill the m_ProjectMap, this must be called after m_LocalGenerators has been populated.
|
// Fill the m_ProjectMap, this must be called after m_LocalGenerators has been populated.
|
||||||
|
|
|
@ -510,11 +510,15 @@ void cmGlobalVisualStudio6Generator::GetDocumentation(cmDocumentationEntry& entr
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void
|
void
|
||||||
cmGlobalVisualStudio6Generator
|
cmGlobalVisualStudio6Generator
|
||||||
::AppendDirectoryForConfig(const char* config, std::string& dir)
|
::AppendDirectoryForConfig(const char* prefix,
|
||||||
|
const char* config,
|
||||||
|
const char* suffix,
|
||||||
|
std::string& dir)
|
||||||
{
|
{
|
||||||
if(config)
|
if(config)
|
||||||
{
|
{
|
||||||
|
dir += prefix;
|
||||||
dir += config;
|
dir += config;
|
||||||
dir += "/";
|
dir += suffix;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,10 @@ public:
|
||||||
std::vector<cmLocalGenerator*>& generators);
|
std::vector<cmLocalGenerator*>& generators);
|
||||||
|
|
||||||
/** Append the subdirectory for the given configuration. */
|
/** 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?
|
///! What is the configurations directory variable called?
|
||||||
virtual const char* GetCMakeCFGInitDirectory() { return "$(IntDir)"; }
|
virtual const char* GetCMakeCFGInitDirectory() { return "$(IntDir)"; }
|
||||||
|
|
|
@ -736,11 +736,15 @@ void cmGlobalVisualStudio7Generator::Configure()
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void
|
void
|
||||||
cmGlobalVisualStudio7Generator
|
cmGlobalVisualStudio7Generator
|
||||||
::AppendDirectoryForConfig(const char* config, std::string& dir)
|
::AppendDirectoryForConfig(const char* prefix,
|
||||||
|
const char* config,
|
||||||
|
const char* suffix,
|
||||||
|
std::string& dir)
|
||||||
{
|
{
|
||||||
if(config)
|
if(config)
|
||||||
{
|
{
|
||||||
|
dir += prefix;
|
||||||
dir += config;
|
dir += config;
|
||||||
dir += "/";
|
dir += suffix;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,10 @@ public:
|
||||||
virtual void Configure();
|
virtual void Configure();
|
||||||
|
|
||||||
/** Append the subdirectory for the given configuration. */
|
/** 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?
|
///! What is the configurations directory variable called?
|
||||||
virtual const char* GetCMakeCFGInitDirectory() { return "$(OutDir)"; }
|
virtual const char* GetCMakeCFGInitDirectory() { return "$(OutDir)"; }
|
||||||
|
|
|
@ -2335,14 +2335,18 @@ std::string cmGlobalXCodeGenerator::XCodeEscapePath(const char* p)
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void
|
void
|
||||||
cmGlobalXCodeGenerator
|
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(m_XcodeVersion > 20)
|
||||||
{
|
{
|
||||||
if(config)
|
if(config)
|
||||||
{
|
{
|
||||||
|
dir += prefix;
|
||||||
dir += config;
|
dir += config;
|
||||||
dir += "/";
|
dir += suffix;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,10 @@ public:
|
||||||
virtual void Generate();
|
virtual void Generate();
|
||||||
|
|
||||||
/** Append the subdirectory for the given configuration. */
|
/** 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?
|
///! What is the configurations directory variable called?
|
||||||
virtual const char* GetCMakeCFGInitDirectory() { return "."; }
|
virtual const char* GetCMakeCFGInitDirectory() { return "."; }
|
||||||
|
|
|
@ -60,6 +60,10 @@ void cmInstallTargetGenerator::GenerateScript(std::ostream& os)
|
||||||
std::string fromFile = fromDir;
|
std::string fromFile = fromDir;
|
||||||
fromFile += fromName;
|
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.
|
// Setup special properties for some target types.
|
||||||
std::string props;
|
std::string props;
|
||||||
const char* properties = 0;
|
const char* properties = 0;
|
||||||
|
@ -114,23 +118,27 @@ void cmInstallTargetGenerator::GenerateScript(std::ostream& os)
|
||||||
{
|
{
|
||||||
// Compute the source locations of the bundle executable and
|
// Compute the source locations of the bundle executable and
|
||||||
// Info.plist file.
|
// Info.plist file.
|
||||||
|
this->PrepareScriptReference(os, this->Target, "INSTALL",
|
||||||
|
false, false);
|
||||||
std::string plist = fromFile;
|
std::string plist = fromFile;
|
||||||
plist += ".app/Contents/Info.plist";
|
plist += ".app/Contents/Info.plist";
|
||||||
fromFile += ".app/Contents/MacOS/";
|
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.
|
// 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());
|
plist.c_str());
|
||||||
|
|
||||||
|
// Compute the destination locations of the bundle executable file.
|
||||||
|
destination += "/MacOS";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -149,7 +157,7 @@ void cmInstallTargetGenerator::GenerateScript(std::ostream& os)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write code to install the target file.
|
// 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);
|
this->ImportLibrary, properties);
|
||||||
|
|
||||||
// Fix the install_name settings in installed binaries.
|
// Fix the install_name settings in installed binaries.
|
||||||
|
@ -157,7 +165,7 @@ void cmInstallTargetGenerator::GenerateScript(std::ostream& os)
|
||||||
type == cmTarget::MODULE_LIBRARY ||
|
type == cmTarget::MODULE_LIBRARY ||
|
||||||
type == cmTarget::EXECUTABLE)
|
type == cmTarget::EXECUTABLE)
|
||||||
{
|
{
|
||||||
this->AddInstallNamePatchRule(os);
|
this->AddInstallNamePatchRule(os, destination.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +190,7 @@ cmInstallTargetGenerator
|
||||||
{
|
{
|
||||||
// Start with the configuration's subdirectory.
|
// Start with the configuration's subdirectory.
|
||||||
target->GetMakefile()->GetLocalGenerator()->GetGlobalGenerator()->
|
target->GetMakefile()->GetLocalGenerator()->GetGlobalGenerator()->
|
||||||
AppendDirectoryForConfig(i->c_str(), fname);
|
AppendDirectoryForConfig("", i->c_str(), "/", fname);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the name of the library.
|
// 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
|
// Build a map of build-tree install_name to install-tree install_name for
|
||||||
// shared libraries linked to this target.
|
// 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 -change \"" << i->first << "\" \"" << i->second << "\"";
|
||||||
}
|
}
|
||||||
os << "\n \"" << this->Destination.c_str() << "/"
|
os << "\n \"" << destination << "/"
|
||||||
<< this->GetScriptReference(this->Target, "REMAPPED", true) << "\")\n";
|
<< this->GetScriptReference(this->Target, "REMAPPED", true) << "\")\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ protected:
|
||||||
bool useSOName);
|
bool useSOName);
|
||||||
std::string GetScriptReference(cmTarget* target, const char* place,
|
std::string GetScriptReference(cmTarget* target, const char* place,
|
||||||
bool useSOName);
|
bool useSOName);
|
||||||
void AddInstallNamePatchRule(std::ostream& os);
|
void AddInstallNamePatchRule(std::ostream& os, const char* destination);
|
||||||
cmTarget* Target;
|
cmTarget* Target;
|
||||||
std::string Destination;
|
std::string Destination;
|
||||||
bool ImportLibrary;
|
bool ImportLibrary;
|
||||||
|
|
|
@ -109,7 +109,7 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
|
||||||
if(this->Target->GetPropertyAsBool("MACOSX_BUNDLE"))
|
if(this->Target->GetPropertyAsBool("MACOSX_BUNDLE"))
|
||||||
{
|
{
|
||||||
// Make bundle directories
|
// Make bundle directories
|
||||||
outpath += this->Target->GetName();
|
outpath += targetName;
|
||||||
outpath += ".app/Contents/MacOS/";
|
outpath += ".app/Contents/MacOS/";
|
||||||
std::string f1 =
|
std::string f1 =
|
||||||
this->Makefile->GetModulesFile("MacOSXBundleInfo.plist.in");
|
this->Makefile->GetModulesFile("MacOSXBundleInfo.plist.in");
|
||||||
|
@ -127,14 +127,17 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
|
||||||
{
|
{
|
||||||
macdir += "/";
|
macdir += "/";
|
||||||
}
|
}
|
||||||
macdir += this->Target->GetName();
|
macdir += targetName;
|
||||||
macdir += ".app/Contents/";
|
macdir += ".app/Contents/";
|
||||||
|
|
||||||
|
// Configure the Info.plist file. Note that it needs the executable name
|
||||||
|
// to be set.
|
||||||
std::string f2 = macdir + "Info.plist";
|
std::string f2 = macdir + "Info.plist";
|
||||||
macdir += "MacOS";
|
macdir += "MacOS";
|
||||||
cmSystemTools::MakeDirectory(macdir.c_str());
|
cmSystemTools::MakeDirectory(macdir.c_str());
|
||||||
this->Makefile->AddDefinition("MACOSX_BUNDLE_EXECUTABLE_NAME", this->Target->GetName());
|
this->Makefile->AddDefinition("MACOSX_BUNDLE_EXECUTABLE_NAME",
|
||||||
this->Makefile->ConfigureFile(f1.c_str(), f2.c_str(),
|
targetName.c_str());
|
||||||
false, false, false);
|
this->Makefile->ConfigureFile(f1.c_str(), f2.c_str(), false, false, false);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if(relink)
|
if(relink)
|
||||||
|
|
|
@ -831,9 +831,8 @@ const char* cmTarget::GetDirectory(const char* config)
|
||||||
if(config)
|
if(config)
|
||||||
{
|
{
|
||||||
// Add the configuration's subdirectory.
|
// Add the configuration's subdirectory.
|
||||||
m_Directory += "/";
|
|
||||||
m_Makefile->GetLocalGenerator()->GetGlobalGenerator()->
|
m_Makefile->GetLocalGenerator()->GetGlobalGenerator()->
|
||||||
AppendDirectoryForConfig(config, m_Directory);
|
AppendDirectoryForConfig("/", config, "", m_Directory);
|
||||||
}
|
}
|
||||||
return m_Directory.c_str();
|
return m_Directory.c_str();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
int foo() { return 0; }
|
|
@ -1,4 +1,6 @@
|
||||||
|
extern int foo();
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
return 0;
|
return foo();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,26 @@
|
||||||
PROJECT(BundleTest)
|
PROJECT(BundleTest)
|
||||||
SET(MACOSX_BUNDLE_INFO_STRING "bundle_info_string")
|
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")
|
||||||
|
|
Loading…
Reference in New Issue