Add support for CFBundle targets on the Mac (#11295)

This commit enables building, for example, plugin bundles
to be loaded by web browsers.
This commit is contained in:
Richard Bateman 2010-10-06 20:43:04 -06:00 committed by David Cole
parent 6754b26bf0
commit 5457b8254c
19 changed files with 415 additions and 8 deletions

View File

@ -325,6 +325,12 @@ cmExportFileGenerator
os << "SET_PROPERTY(TARGET " << targetName
<< " PROPERTY MACOSX_BUNDLE 1)\n";
}
if (target->IsCFBundleOnApple())
{
os << "SET_PROPERTY(TARGET " << targetName
<< " PROPERTY BUNDLE 1)\n";
}
os << "\n";
}

View File

@ -263,6 +263,20 @@ cmExportInstallFileGenerator
value += ".framework/";
value += itgen->GetInstallFilename(target, config);
}
else if(target->IsCFBundleOnApple())
{
const char *ext = target->GetProperty("BUNDLE_EXTENSION");
if (!ext)
{
ext = "bundle";
}
value += itgen->GetInstallFilename(target, config);
value += ".";
value += ext;
value += "/";
value += itgen->GetInstallFilename(target, config);
}
else if(target->IsAppBundleOnApple())
{
value += itgen->GetInstallFilename(target, config);

View File

@ -570,6 +570,12 @@ cmGlobalXCodeGenerator::CreateXCodeSourceFile(cmLocalGenerator* lg,
}
}
if(cmtarget.IsCFBundleOnApple())
{
cmtarget.SetProperty("PREFIX", "");
cmtarget.SetProperty("SUFFIX", "");
}
// Add the fileRef to the top level Resources group/folder if it is not
// already there.
//
@ -804,6 +810,7 @@ cmGlobalXCodeGenerator::CreateXCodeTargets(cmLocalGenerator* gen,
// some build phases only apply to bundles and/or frameworks
bool isFrameworkTarget = cmtarget.IsFrameworkOnApple();
bool isBundleTarget = cmtarget.GetPropertyAsBool("MACOSX_BUNDLE");
bool isCFBundleTarget = cmtarget.IsCFBundleOnApple();
cmXCodeObject* buildFiles = 0;
@ -849,7 +856,8 @@ cmGlobalXCodeGenerator::CreateXCodeTargets(cmLocalGenerator* gen,
// create resource build phase - only for framework or bundle targets
cmXCodeObject* resourceBuildPhase = 0;
if (!resourceFiles.empty() && (isFrameworkTarget || isBundleTarget))
if (!resourceFiles.empty() &&
(isFrameworkTarget || isBundleTarget || isCFBundleTarget))
{
resourceBuildPhase =
this->CreateObject(cmXCodeObject::PBXResourcesBuildPhase);
@ -870,7 +878,7 @@ cmGlobalXCodeGenerator::CreateXCodeTargets(cmLocalGenerator* gen,
// create vector of "non-resource content file" build phases - only for
// framework or bundle targets
std::vector<cmXCodeObject*> contentBuildPhases;
if (isFrameworkTarget || isBundleTarget)
if (isFrameworkTarget || isBundleTarget || isCFBundleTarget)
{
typedef std::map<cmStdString, std::vector<cmSourceFile*> >
mapOfVectorOfSourceFiles;
@ -1594,7 +1602,33 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target,
{
buildSettings->AddAttribute("LIBRARY_STYLE",
this->CreateString("BUNDLE"));
if(this->XcodeVersion >= 22)
if (target.GetPropertyAsBool("BUNDLE"))
{
// It turns out that a BUNDLE is basically the same
// in many ways as an application bundle, as far as
// link flags go
std::string createFlags =
this->LookupFlags("CMAKE_SHARED_MODULE_CREATE_", lang, "_FLAGS",
"-bundle");
if(!createFlags.empty())
{
extraLinkOptions += " ";
extraLinkOptions += createFlags;
}
std::string plist = this->ComputeInfoPListLocation(target);
// Xcode will create the final version of Info.plist at build time,
// so let it replace the cfbundle name. This avoids creating
// a per-configuration Info.plist file. The cfbundle plist
// is very similar to the application bundle plist
this->CurrentLocalGenerator
->GenerateAppleInfoPList(&target, "$(EXECUTABLE_NAME)",
plist.c_str());
std::string path =
this->ConvertToRelativeForXCode(plist.c_str());
buildSettings->AddAttribute("INFOPLIST_FILE",
this->CreateString(path.c_str()));
}
else if(this->XcodeVersion >= 22)
{
buildSettings->AddAttribute("MACH_O_TYPE",
this->CreateString("mh_bundle"));
@ -1633,7 +1667,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target,
std::string plist = this->ComputeInfoPListLocation(target);
// Xcode will create the final version of Info.plist at build time,
// so let it replace the framework name. This avoids creating
// so let it replace the framework name. This avoids creating
// a per-configuration Info.plist file.
this->CurrentLocalGenerator
->GenerateFrameworkInfoPList(&target, "$(EXECUTABLE_NAME)",
@ -2032,7 +2066,10 @@ const char* cmGlobalXCodeGenerator::GetTargetFileType(cmTarget& cmtarget)
case cmTarget::STATIC_LIBRARY:
return "archive.ar";
case cmTarget::MODULE_LIBRARY:
return ((this->XcodeVersion >= 22)?
if (cmtarget.IsCFBundleOnApple())
return "wrapper.plug-in";
else
return ((this->XcodeVersion >= 22)?
"compiled.mach-o.executable" : "compiled.mach-o.dylib");
case cmTarget::SHARED_LIBRARY:
return (cmtarget.GetPropertyAsBool("FRAMEWORK")?
@ -2052,8 +2089,12 @@ const char* cmGlobalXCodeGenerator::GetTargetProductType(cmTarget& cmtarget)
case cmTarget::STATIC_LIBRARY:
return "com.apple.product-type.library.static";
case cmTarget::MODULE_LIBRARY:
return ((this->XcodeVersion >= 22)? "com.apple.product-type.tool" :
"com.apple.product-type.library.dynamic");
if (cmtarget.IsCFBundleOnApple())
return "com.apple.product-type.bundle";
else
return ((this->XcodeVersion >= 22)?
"com.apple.product-type.tool" :
"com.apple.product-type.library.dynamic");
case cmTarget::SHARED_LIBRARY:
return (cmtarget.GetPropertyAsBool("FRAMEWORK")?
"com.apple.product-type.framework" :

View File

@ -26,6 +26,12 @@ cmMakefileLibraryTargetGenerator
::cmMakefileLibraryTargetGenerator(cmTarget* target):
cmMakefileTargetGenerator(target)
{
if(this->Target->IsCFBundleOnApple())
{
target->SetProperty("PREFIX", "");
target->SetProperty("SUFFIX", "");
}
this->CustomCommandDriver = OnDepends;
this->Target->GetLibraryNames(
this->TargetNameOut, this->TargetNameSO, this->TargetNameReal,
@ -41,6 +47,20 @@ cmMakefileLibraryTargetGenerator
this->MacContentDirectory += this->FrameworkVersion;
this->MacContentDirectory += "/";
}
else if(this->Target->IsCFBundleOnApple())
{
this->MacContentDirectory = this->Target->GetDirectory(this->ConfigName);
this->MacContentDirectory += "/";
this->MacContentDirectory += this->TargetNameOut;
this->MacContentDirectory += ".";
const char *ext = this->Target->GetProperty("BUNDLE_EXTENSION");
if (!ext)
{
ext = "bundle";
}
this->MacContentDirectory += ext;
this->MacContentDirectory += "/Contents/";
}
}
//----------------------------------------------------------------------------
@ -300,6 +320,27 @@ cmMakefileLibraryTargetGenerator
}
}
//----------------------------------------------------------------------------
void
cmMakefileLibraryTargetGenerator::CreateCFBundle(std::string& targetName,
std::string& outpath)
{
// Compute bundle directory names.
outpath = this->MacContentDirectory;
outpath += "MacOS";
cmSystemTools::MakeDirectory(outpath.c_str());
this->Makefile->AddCMakeOutputFile(outpath.c_str());
outpath += "/";
// Configure the Info.plist file. Note that it needs the executable name
// to be set.
std::string plist = this->MacContentDirectory + "Info.plist";
this->LocalGenerator->GenerateAppleInfoPList(this->Target,
targetName.c_str(),
plist.c_str());
this->Makefile->AddCMakeOutputFile(plist.c_str());
}
//----------------------------------------------------------------------------
void cmMakefileLibraryTargetGenerator::WriteLibraryRules
(const char* linkRuleVar, const char* extraFlags, bool relink)
@ -354,6 +395,12 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules
outpath = this->MacContentDirectory;
this->CreateFramework(targetName);
}
else if(this->Target->IsCFBundleOnApple())
{
outpath = this->Target->GetDirectory(this->ConfigName);
outpath += "/";
this->CreateCFBundle(targetName, outpath);
}
else if(relink)
{
outpath = this->Makefile->GetStartOutputDirectory();
@ -417,6 +464,8 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules
buildEcho += " shared library ";
break;
case cmTarget::MODULE_LIBRARY:
if (this->Target->IsCFBundleOnApple())
buildEcho += " CFBundle";
buildEcho += " shared module ";
break;
default:

View File

@ -33,6 +33,7 @@ protected:
// MacOSX Framework support methods
void WriteFrameworkRules(bool relink);
void CreateFramework(std::string const& targetName);
void CreateCFBundle(std::string& targetName, std::string& outpath);
// Store the computd framework version for OS X Frameworks.
std::string FrameworkVersion;

View File

@ -484,17 +484,21 @@ void cmSourceFile::DefineProperties(cmake *cm)
cm->DefineProperty
("MACOSX_PACKAGE_LOCATION", cmProperty::SOURCE_FILE,
"Place a source file inside a Mac OS X bundle or framework.",
"Place a source file inside a Mac OS X bundle, CFBundle, or framework.",
"Executable targets with the MACOSX_BUNDLE property set are built "
"as Mac OS X application bundles on Apple platforms. "
"Shared library targets with the FRAMEWORK property set are built "
"as Mac OS X frameworks on Apple platforms. "
"Module library targets with the BUNDLE property set are built "
"as Mac OS X CFBundle bundles on Apple platforms. "
"Source files listed in the target with this property set will "
"be copied to a directory inside the bundle or framework content "
"folder specified by the property value. "
"For bundles the content folder is \"<name>.app/Contents\". "
"For frameworks the content folder is "
"\"<name>.framework/Versions/<version>\". "
"For cfbundles the content folder is "
"\"<name>.bundle/Contents\" (unless the extension is changed). "
"See the PUBLIC_HEADER, PRIVATE_HEADER, and RESOURCE target "
"properties for specifying files meant for Headers, PrivateHeaders, "
"or Resources directories.");

View File

@ -193,6 +193,22 @@ void cmTarget::DefineProperties(cmake *cm)
"A message to display on some generators (such as makefiles) when "
"the target is built.");
cm->DefineProperty
("BUNDLE", cmProperty::TARGET,
"This target is a CFBundle on the Mac.",
"If a module library target has this property set to true it will "
"be built as a CFBundle when built on the mac. It will have the "
"directory structure required for a CFBundle and will be suitable "
"to be used for creating Browser Plugins or other application "
"resources.");
cm->DefineProperty
("BUNDLE_EXTENSION", cmProperty::TARGET,
"The file extension used to name a BUNDLE target on the Mac.",
"The default value is \"bundle\" - you can also use \"plugin\" or "
"whatever file extension is required by the host app for your "
"bundle.");
cm->DefineProperty
("FRAMEWORK", cmProperty::TARGET,
"This target is a framework on the Mac.",
@ -1203,6 +1219,14 @@ bool cmTarget::IsAppBundleOnApple()
this->GetPropertyAsBool("MACOSX_BUNDLE"));
}
//----------------------------------------------------------------------------
bool cmTarget::IsCFBundleOnApple()
{
return (this->GetType() == cmTarget::MODULE_LIBRARY &&
this->Makefile->IsOn("APPLE") &&
this->GetPropertyAsBool("BUNDLE"));
}
//----------------------------------------------------------------------------
class cmTargetTraceDependencies
{

View File

@ -430,6 +430,9 @@ public:
Apple. */
bool IsFrameworkOnApple();
/** Return whether this target is a CFBundle (plugin) on Apple. */
bool IsCFBundleOnApple();
/** Return whether this target is an executable Bundle on Apple. */
bool IsAppBundleOnApple();

View File

@ -0,0 +1,53 @@
#this is adapted from FireBreath (http://www.firebreath.org)
cmake_minimum_required(VERSION 2.8)
project(CFBundleTest)
include(PluginConfig.cmake)
message ("Creating Mac Browser Plugin project ${PROJECT_NAME}")
set(SOURCES
np_macmain.cpp
Localized.r
${CMAKE_CURRENT_BINARY_DIR}/Info.plist
${CMAKE_CURRENT_BINARY_DIR}/InfoPlist.strings
${CMAKE_CURRENT_BINARY_DIR}/Localized.rsrc
)
add_library( ${PROJECT_NAME} MODULE
${SOURCES}
)
set (RCFILES ${CMAKE_CURRENT_SOURCE_DIR}/Localized.r)
configure_file(Info.plist.in ${CMAKE_CURRENT_BINARY_DIR}/Info.plist)
configure_file(InfoPlist.strings.in ${CMAKE_CURRENT_BINARY_DIR}/InfoPlist.strings)
# Compile the resource file
find_program(RC_COMPILER Rez NO_DEFAULT_PATHS)
execute_process(COMMAND
${RC_COMPILER} ${RCFILES} -useDF -o ${CMAKE_CURRENT_BINARY_DIR}/Localized.rsrc
)
set_source_files_properties(
${CMAKE_CURRENT_BINARY_DIR}/Localized.rsrc
PROPERTIES GENERATED 1
)
# note that for some reason, the makefile and xcode generators use a different
# property to indicate where the Info.plist file is :-/ For that reason, we
# specify it twice so it will work both places
set_target_properties(CFBundleTest PROPERTIES
BUNDLE 1
BUNDLE_EXTENSION plugin
XCODE_ATTRIBUTE_WRAPPER_EXTENSION plugin #sets the extension to .plugin
XCODE_ATTRIBUTE_MACH_O_TYPE mh_bundle
XCODE_ATTRIBUTE_INFOPLIST_FILE ${CMAKE_CURRENT_BINARY_DIR}/Info.plist
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_BINARY_DIR}/Info.plist
LINK_FLAGS "-Wl,-exported_symbols_list,\"${CMAKE_CURRENT_SOURCE_DIR}/ExportList_plugin.txt\"")
set_source_files_properties(
${CMAKE_CURRENT_BINARY_DIR}/InfoPlist.strings
${CMAKE_CURRENT_BINARY_DIR}/Localized.rsrc
PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/English.lproj")

View File

@ -0,0 +1,3 @@
_NP_GetEntryPoints
_NP_Initialize
_NP_Shutdown

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${PLUGIN_NAME}</string>
<key>CFBundleGetInfoString</key>
<string>${PLUGIN_NAME} ${FBSTRING_PLUGIN_VERSION}, ${FBSTRING_LegalCopyright}</string>
<key>CFBundleIdentifier</key>
<string>com.${ACTIVEX_PROGID}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BRPL</string>
<key>CFBundleShortVersionString</key>
<string>${PLUGIN_NAME} ${FBSTRING_PLUGIN_VERSION}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${FBSTRING_PLUGIN_VERSION}</string>
<key>CFPlugInDynamicRegisterFunction</key>
<string></string>
<key>CFPlugInDynamicRegistration</key>
<string>NO</string>
<key>CFPlugInFactories</key>
<dict>
<key>00000000-0000-0000-0000-000000000000</key>
<string>MyFactoryFunction</string>
</dict>
<key>CFPlugInTypes</key>
<dict>
<key>00000000-0000-0000-0000-000000000000</key>
<array>
<string>00000000-0000-0000-0000-000000000000</string>
</array>
</dict>
<key>CFPlugInUnloadFunction</key>
<string></string>
<key>WebPluginName</key>
<string>${FBSTRING_ProductName}</string>
<key>WebPluginDescription</key>
<string>${FBSTRING_FileDescription}</string>
<key>WebPluginMIMETypes</key>
<dict>
<key>${FBSTRING_MIMEType}</key>
<dict>
<key>WebPluginTypeDescription</key>
<string>${FBSTRING_FileDescription}</string>
</dict>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,4 @@
/* Localized versions of Info.plist keys */
CFBundleName = "${PLUGIN_NAME}.plugin";
NSHumanReadableCopyright = "${FBSTRING_LegalCopyright}";

View File

@ -0,0 +1,18 @@
#include <CoreServices/CoreServices.r>
resource 'STR#' (126)
{ {
"${FBSTRING_LegalCopyright}",
"${FBSTRING_ProductName}"
} };
resource 'STR#' (127)
{ {
"${FBSTRING_FileDescription}"
} };
resource 'STR#' (128)
{ {
"${FBSTRING_MIMEType}",
"${FBSTRING_FileExtents}"
} };

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 B

View File

@ -0,0 +1,21 @@
#/**********************************************************\
# Auto-Generated Plugin Configuration file
# for CFTestPlugin
#\**********************************************************/
set(PLUGIN_NAME "CFTestPlugin")
set(PLUGIN_PREFIX "CFTP")
set(COMPANY_NAME "FBDevTeam")
set(MOZILLA_PLUGINID "@firebreath.googlecode.com/CFTestPlugin")
# strings
set(FBSTRING_CompanyName "Firebreath Dev Team")
set(FBSTRING_FileDescription "CFBundle Test Plugin - Plugin for testing cmake patch to improve FireBreath project generation")
set(FBSTRING_PLUGIN_VERSION "1.0.0")
set(FBSTRING_LegalCopyright "Copyright 2010 Firebreath Dev Team")
set(FBSTRING_PluginFileName "np${PLUGIN_NAME}.dll")
set(FBSTRING_ProductName "CFTestPlugin")
set(FBSTRING_FileExtents "")
set(FBSTRING_PluginName "CFTestPlugin")
set(FBSTRING_MIMEType "application/x-fbtestplugin")

View File

@ -0,0 +1,16 @@
CFBundle test project. The generated .plugin/ bundle from either makefiles or XCode should look like this:
./Contents
./Contents/Info.plist
./Contents/MacOS
./Contents/MacOS/CFBundleTest
./Contents/Resources
./Contents/Resources/English.lproj
./Contents/Resources/English.lproj/InfoPlist.strings
./Contents/Resources/English.lproj/Localized.rsrc
file Contents/MacOS/CFBundleTest should return something like:
Contents/MacOS/CFBundleTest: Mach-O 64-bit bundle x86_64
It is okay if it is a 32 bit binary; if it is not Mach-O, or is spelled differently, it is not okay.

View File

@ -0,0 +1,32 @@
if(NOT DEFINED CTEST_CONFIGURATION_TYPE)
message(FATAL_ERROR "expected variable CTEST_CONFIGURATION_TYPE not defined")
endif()
if(NOT DEFINED dir)
message(FATAL_ERROR "expected variable dir not defined")
endif()
if(NOT DEFINED gen)
message(FATAL_ERROR "expected variable gen not defined")
endif()
message(STATUS "CTEST_CONFIGURATION_TYPE='${CTEST_CONFIGURATION_TYPE}'")
message(STATUS "dir='${dir}'")
message(STATUS "gen='${gen}'")
if(gen MATCHES "Make" OR
"${CTEST_CONFIGURATION_TYPE}" STREQUAL "" OR
"${CTEST_CONFIGURATION_TYPE}" STREQUAL "." OR
"${CTEST_CONFIGURATION_TYPE}" STREQUAL "NoConfig")
set(expected_filename "${dir}/CFBundleTest.plugin/Contents/MacOS/CFBundleTest")
else()
set(expected_filename "${dir}/${CTEST_CONFIGURATION_TYPE}/CFBundleTest.plugin/Contents/MacOS/CFBundleTest")
endif()
if(NOT EXISTS "${expected_filename}")
message(FATAL_ERROR "test fails: expected output file does not exist [${expected_filename}]")
endif()
file(COPY "${expected_filename}"
DESTINATION "${dir}/LatestBuildResult"
)

View File

@ -0,0 +1,49 @@
/***********************************************************\
Written by: Richard Bateman (taxilian)
Based on the default np_macmain.cpp from FireBreath
http://firebreath.googlecode.com
This file has been stripped to prevent it from accidently
doing anything useful.
\***********************************************************/
#include <stdio.h>
typedef void (*NPP_ShutdownProcPtr)(void);
typedef short NPError;
#pragma GCC visibility push(default)
struct NPNetscapeFuncs;
struct NPPluginFuncs;
extern "C" {
NPError NP_Initialize(NPNetscapeFuncs *browserFuncs);
NPError NP_GetEntryPoints(NPPluginFuncs *pluginFuncs);
NPError NP_Shutdown(void);
}
#pragma GCC visibility pop
void initPluginModule()
{
}
NPError NP_GetEntryPoints(NPPluginFuncs* pFuncs)
{
printf("NP_GetEntryPoints()\n");
return 0;
}
NPError NP_Initialize(NPNetscapeFuncs* pFuncs)
{
printf("NP_Initialize()\n");
return 0;
}
NPError NP_Shutdown()
{
return 0;
}

View File

@ -1110,6 +1110,21 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=master -P ${CMake_SOURCE_DIR}/Utilities/
${BundleTestInstallDir}/Applications/SecondBundleExe.app/Contents/MacOS/SecondBundleExe)
LIST(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/BundleTest")
ADD_TEST(CFBundleTest ${CMAKE_CTEST_COMMAND}
--build-and-test
"${CMake_SOURCE_DIR}/Tests/CFBundleTest"
"${CMake_BINARY_DIR}/Tests/CFBundleTest"
--build-two-config
--build-generator ${CMAKE_TEST_GENERATOR}
--build-makeprogram ${CMAKE_TEST_MAKEPROGRAM}
--build-project CFBundleTest
--test-command
${CMAKE_CMAKE_COMMAND} -DCTEST_CONFIGURATION_TYPE=\${CTEST_CONFIGURATION_TYPE}
-Ddir=${CMake_BINARY_DIR}/Tests/CFBundleTest
-Dgen=${CMAKE_TEST_GENERATOR}
-P ${CMake_SOURCE_DIR}/Tests/CFBundleTest/VerifyResult.cmake)
LIST(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/CFBundleTest")
ADD_TEST_MACRO(ObjC++ ObjC++)
ENDIF (APPLE AND CMAKE_COMPILER_IS_GNUCXX)