diff --git a/Modules/Platform/Darwin.cmake b/Modules/Platform/Darwin.cmake index 7d18906c5..4ca7d4961 100644 --- a/Modules/Platform/Darwin.cmake +++ b/Modules/Platform/Darwin.cmake @@ -47,6 +47,8 @@ IF(NOT XCODE) SET(CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG "-install_name") ENDIF(NOT XCODE) +SET(CMAKE_MacOSX_Content_COMPILE_OBJECT "\"${CMAKE_COMMAND}\" -E copy_if_different ") + SET(CMAKE_C_CREATE_SHARED_LIBRARY_FORBIDDEN_FLAGS -w) SET(CMAKE_CXX_CREATE_SHARED_LIBRARY_FORBIDDEN_FLAGS -w) SET(CMAKE_C_CREATE_SHARED_LIBRARY diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index b590ed88d..224034530 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -1542,15 +1542,54 @@ cmLocalUnixMakefileGenerator3 { objectName = objectName.substr(0, dot_pos); } - objectName += - this->GlobalGenerator->GetLanguageOutputExtensionFromExtension( - source.GetSourceExtension().c_str()); + if ( source.GetPropertyAsBool("KEEP_EXTENSION") ) + { + if ( !source.GetSourceExtension().empty() ) + { + objectName += "." + source.GetSourceExtension(); + } + } + else + { + objectName += + this->GlobalGenerator->GetLanguageOutputExtensionFromExtension( + source.GetSourceExtension().c_str()); + } // Convert to a safe name. objectName = this->CreateSafeUniqueObjectFileName(objectName.c_str()); // Prepend the target directory. - std::string obj = this->GetTargetDirectory(target); + std::string obj; + const char* fileTargetDirectory = source.GetProperty("MACOSX_PACKAGE_LOCATION"); + if ( fileTargetDirectory ) + { + std::string targetName; + std::string targetNameReal; + target.GetExecutableNames(targetName, targetNameReal, + this->ConfigurationName.c_str()); + if ( target.GetPropertyAsBool("MACOSX_BUNDLE") ) + { + // Construct the full path version of the names. + obj = this->ExecutableOutputPath; + if(obj.empty()) + { + obj = this->Makefile->GetStartOutputDirectory(); + obj += "/"; + } + obj += targetName + ".app/Contents/"; + obj += fileTargetDirectory; + } + else + { + // Framework not handled yet + abort(); + } + } + else + { + obj = this->GetTargetDirectory(target); + } obj += "/"; obj += objectName; if(nameWithoutTargetDir) @@ -1763,6 +1802,11 @@ const char* cmLocalUnixMakefileGenerator3 ::GetSourceFileLanguage(const cmSourceFile& source) { + const char* lang = source.GetProperty("LANGUAGE"); + if ( lang ) + { + return lang; + } // Identify the language of the source file. return (this->GlobalGenerator ->GetLanguageFromExtension(source.GetSourceExtension().c_str())); diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index afdca4d89..6922cf0df 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx @@ -28,7 +28,7 @@ void cmMakefileExecutableTargetGenerator::WriteRuleFiles() { // create the build.make file and directory, put in the common blocks this->CreateRuleFile(); - + // Add in any rules for custom commands this->WriteCustomCommandsForTarget(); @@ -82,8 +82,8 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) // Add a dependency on the rule file itself. this->LocalGenerator->AppendRuleDepend(depends, this->BuildFileNameFull.c_str()); - - for(std::vector::const_iterator obj = + + for(std::vector::const_iterator obj = this->ExternalObjects.begin(); obj != this->ExternalObjects.end(); ++obj) { @@ -111,13 +111,13 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) // Make bundle directories outpath += targetName; outpath += ".app/Contents/MacOS/"; - std::string f1 = + std::string f1 = this->Makefile->GetModulesFile("MacOSXBundleInfo.plist.in"); if ( f1.size() == 0 ) { cmSystemTools::Error("could not find Mac OSX bundle template file."); } - std::string macdir = + std::string macdir = this->Makefile->GetSafeDefinition("EXECUTABLE_OUTPUT_PATH"); if ( macdir.size() == 0 ) { @@ -130,6 +130,25 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) macdir += targetName; macdir += ".app/Contents/"; + std::vector::iterator sourceIt; + for ( sourceIt = this->Target->GetSourceFiles().begin(); + sourceIt != this->Target->GetSourceFiles().end(); + ++ sourceIt ) + { + const char* subDir = (*sourceIt)->GetProperty("MACOSX_PACKAGE_LOCATION"); + if ( subDir ) + { + std::string newDir = macdir; + newDir += subDir; + if ( !cmSystemTools::MakeDirectory(newDir.c_str()) ) + { + cmSystemTools::Error("Cannot create a subdirectory for \"", + newDir.c_str(), "\"."); + return; + } + } + } + // Configure the Info.plist file. Note that it needs the executable name // to be set. std::string f2 = macdir + "Info.plist"; @@ -232,7 +251,7 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) cmLocalGenerator::START_OUTPUT, cmLocalGenerator::UNCHANGED)); } - } + } // Add a command to remove any existing files for this executable. std::vector commands1; @@ -240,7 +259,7 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) *this->Target, "target"); this->LocalGenerator->CreateCDCommand(commands1, this->Makefile->GetStartOutputDirectory(), - this->Makefile->GetHomeOutputDirectory()); + this->Makefile->GetHomeOutputDirectory()); commands.insert(commands.end(), commands1.begin(), commands1.end()); commands1.clear(); @@ -257,7 +276,7 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) std::string linkRuleVar = "CMAKE_"; linkRuleVar += linkLanguage; linkRuleVar += "_LINK_EXECUTABLE"; - std::string linkRule = + std::string linkRule = this->Makefile->GetRequiredDefinition(linkRuleVar.c_str()); cmSystemTools::ExpandListArgument(linkRule, commands1); this->LocalGenerator->CreateCDCommand @@ -317,9 +336,9 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) } // Write the build rule. - this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, + this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0, - targetFullPathReal.c_str(), + targetFullPathReal.c_str(), depends, commands, false); // The symlink name for the target should depend on the real target @@ -331,7 +350,7 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) commands.clear(); depends.push_back(targetFullPathReal.c_str()); this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0, - targetFullPath.c_str(), + targetFullPath.c_str(), depends, commands, false); } @@ -341,11 +360,11 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) dir += this->LocalGenerator->GetTargetDirectory(*this->Target); std::string buildTargetRuleName = dir; buildTargetRuleName += relink?"/preinstall":"/build"; - buildTargetRuleName = + buildTargetRuleName = this->Convert(buildTargetRuleName.c_str(), cmLocalGenerator::HOME_OUTPUT, cmLocalGenerator::MAKEFILE); - this->LocalGenerator->WriteConvenienceRule(*this->BuildFileStream, + this->LocalGenerator->WriteConvenienceRule(*this->BuildFileStream, targetFullPath.c_str(), buildTargetRuleName.c_str()); diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index d0b5de357..2f5e12033 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -237,8 +237,6 @@ void cmMakefileTargetGenerator::WriteCommonCodeRules() } } - - //---------------------------------------------------------------------------- void cmMakefileTargetGenerator::WriteObjectRuleFiles(cmSourceFile& source) { @@ -282,6 +280,10 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(cmSourceFile& source) (this->LocalGenerator->ConvertToFullPath(dir).c_str()); // Save this in the target's list of object files. + if ( source.GetPropertyAsBool("EXTRA_CONTENT") ) + { + this->ExtraContent.insert(obj); + } this->Objects.push_back(obj); std::string relativeObj = this->LocalGenerator->GetHomeRelativeOutputPath(); relativeObj += obj; @@ -587,6 +589,7 @@ void cmMakefileTargetGenerator } } +//---------------------------------------------------------------------------- void cmMakefileTargetGenerator::WriteCustomCommands() { // add custom commands to the clean rules? @@ -667,6 +670,10 @@ cmMakefileTargetGenerator for(std::vector::const_iterator i = this->Objects.begin(); i != this->Objects.end(); ++i) { + if ( this->ExtraContent.find(i->c_str()) != this->ExtraContent.end() ) + { + continue; + } *this->BuildFileStream << " " << lineContinue << "\n"; if(objName) { diff --git a/Source/cmMakefileTargetGenerator.h b/Source/cmMakefileTargetGenerator.h index 85f0fb2ec..c2422d75a 100644 --- a/Source/cmMakefileTargetGenerator.h +++ b/Source/cmMakefileTargetGenerator.h @@ -34,7 +34,7 @@ class cmSourceFile; * \brief Support Routines for writing makefiles * */ -class cmMakefileTargetGenerator +class cmMakefileTargetGenerator { public: // constructor to set the ivars @@ -49,12 +49,12 @@ public: /* the main entry point for this class. Writes the Makefiles associated with this target */ virtual void WriteRuleFiles() = 0; - + protected: // create the file and directory etc void CreateRuleFile(); - + // outputs the rules for any custom commands used by this target void WriteCustomCommandsForTarget(); @@ -75,38 +75,38 @@ protected: // write the build rule for an object void WriteObjectBuildFile(std::string &obj, - const char *lang, + const char *lang, cmSourceFile& source, std::vector& depends); - + // write the depend.make file for an object void WriteObjectDependRules(cmSourceFile& source, std::vector& depends); - + // this is responsible for writing all of the rules for all this // directories custom commands (but not utility targets) void WriteCustomCommands(); void GenerateCustomRuleFile(const cmCustomCommand& cc); - + // write out the variable that lists the objects for this target void WriteObjectsVariable(std::string& variableName, std::string& variableNameExternal); - + // Return the a string with -F flags on apple std::string GetFrameworkFlags(); - + // append intertarget dependencies void AppendTargetDepends(std::vector& depends); virtual void CloseFileStreams(); - void RemoveForbiddenFlags(const char* flagVar, const char* linkLang, + void RemoveForbiddenFlags(const char* flagVar, const char* linkLang, std::string& linkFlags); cmStdString TargetName; cmTarget *Target; cmLocalUnixMakefileGenerator3 *LocalGenerator; cmGlobalGenerator *GlobalGenerator; cmMakefile *Makefile; - + // the full path to the build file std::string BuildFileName; std::string BuildFileNameFull; @@ -132,19 +132,20 @@ protected: // objects used by this target std::vector Objects; std::vector ExternalObjects; + std::set ExtraContent; // Set of object file names that will be built in this directory. std::set ObjectFiles; //================================================================== - // Convenience routines that do nothing more than forward to + // Convenience routines that do nothing more than forward to // implementaitons - std::string Convert(const char* source, - cmLocalGenerator::RelativeRoot relative, - cmLocalGenerator::OutputFormat output = + std::string Convert(const char* source, + cmLocalGenerator::RelativeRoot relative, + cmLocalGenerator::OutputFormat output = cmLocalGenerator::UNCHANGED, - bool optional = false) + bool optional = false) { return this->LocalGenerator->Convert(source, relative, output, optional); } diff --git a/Source/cmSetSourceFilesPropertiesCommand.cxx b/Source/cmSetSourceFilesPropertiesCommand.cxx index 377eb73aa..8966c6b30 100644 --- a/Source/cmSetSourceFilesPropertiesCommand.cxx +++ b/Source/cmSetSourceFilesPropertiesCommand.cxx @@ -9,8 +9,8 @@ Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. - This software is distributed WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ @@ -63,7 +63,8 @@ bool cmSetSourceFilesPropertiesCommand::InitialPass( ++j; if(j == args.end()) { - this->SetError("called with incorrect number of arguments COMPILE_FLAGS with no flags"); + this->SetError("called with incorrect number of arguments " + "COMPILE_FLAGS with no flags"); return false; } propertyPairs.push_back(*j); @@ -85,6 +86,7 @@ bool cmSetSourceFilesPropertiesCommand::InitialPass( { // now loop through the rest of the arguments, new style ++j; + bool dontPush = false; while (j != args.end()) { propertyPairs.push_back(*j); @@ -96,6 +98,25 @@ bool cmSetSourceFilesPropertiesCommand::InitialPass( generated = true; } } + else if(*j == "MACOSX_PACKAGE_LOCATION") + { + doingFiles = false; + ++j; + if(j == args.end()) + { + this->SetError("called with incorrect number of arguments " + "MACOSX_PACKAGE_LOCATION with no flags"); + return false; + } + propertyPairs.push_back(*j); + propertyPairs.push_back("EXTRA_CONTENT"); + propertyPairs.push_back("1"); + propertyPairs.push_back("KEEP_EXTENSION"); + propertyPairs.push_back("1"); + propertyPairs.push_back("LANGUAGE"); + propertyPairs.push_back("MacOSX_Content"); + dontPush = true; + } else { ++j; @@ -105,8 +126,12 @@ bool cmSetSourceFilesPropertiesCommand::InitialPass( this->SetError("called with incorrect number of arguments."); return false; } - propertyPairs.push_back(*j); + if ( !dontPush ) + { + propertyPairs.push_back(*j); + } ++j; + dontPush = false; } // break out of the loop because j is already == end break; @@ -117,16 +142,17 @@ bool cmSetSourceFilesPropertiesCommand::InitialPass( } else { - this->SetError("called with illegal arguments, maybe missing a PROPERTIES specifier?"); + this->SetError("called with illegal arguments, maybe missing a " + "PROPERTIES specifier?"); return false; } } - + // now loop over all the files int i; unsigned int k; for(i = 0; i < numFiles; ++i) - { + { // get the source file cmSourceFile* sf = this->Makefile->GetOrCreateSource(args[i].c_str(), generated); diff --git a/Tests/BundleTest/CMakeLists.txt b/Tests/BundleTest/CMakeLists.txt index 9e8cb2045..1e905664b 100644 --- a/Tests/BundleTest/CMakeLists.txt +++ b/Tests/BundleTest/CMakeLists.txt @@ -1,9 +1,36 @@ PROJECT(BundleTest) SET(MACOSX_BUNDLE_INFO_STRING "bundle_info_string") +SET(CMAKE_MacOSX_Content_COMPILE_OBJECT "\"${CMAKE_COMMAND}\" -E copy_if_different ") + +ADD_CUSTOM_COMMAND( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/randomResourceFile.plist" + COMMAND /bin/cp + ARGS "${CMAKE_CURRENT_SOURCE_DIR}/randomResourceFile.plist.in" + "${CMAKE_CURRENT_BINARY_DIR}/randomResourceFile.plist") + +SET_SOURCE_FILES_PROPERTIES( + "${CMAKE_CURRENT_BINARY_DIR}/randomResourceFile.plist" + PROPERTIES + MACOSX_PACKAGE_LOCATION Resources + ) + +SET_SOURCE_FILES_PROPERTIES( + SomeRandomFile.txt + PROPERTIES + MACOSX_PACKAGE_LOCATION MacOS + ) + +SET(EXECUTABLE_OUTPUT_PATH "${CMAKE_CURRENT_BINARY_DIR}/foobar") # Test building a bundle linking to a shared library. ADD_LIBRARY(BundleTestLib SHARED BundleLib.cxx) -ADD_EXECUTABLE(BundleTest MACOSX_BUNDLE BundleTest.cxx) +ADD_EXECUTABLE(BundleTest + MACOSX_BUNDLE + BundleTest.cxx + SomeRandomFile.txt + "${CMAKE_CURRENT_BINARY_DIR}/randomResourceFile.plist" + ) + TARGET_LINK_LIBRARIES(BundleTest BundleTestLib) # Test bundle installation. diff --git a/Tests/BundleTest/SomeRandomFile.txt b/Tests/BundleTest/SomeRandomFile.txt new file mode 100644 index 000000000..e69de29bb diff --git a/Tests/BundleTest/randomResourceFile.plist.in b/Tests/BundleTest/randomResourceFile.plist.in new file mode 100644 index 000000000..cfe3222b6 --- /dev/null +++ b/Tests/BundleTest/randomResourceFile.plist.in @@ -0,0 +1,9 @@ + + + + + Package + CMake + + +