Skip to content

[FHS bundles] Initial implementation. #1395

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,8 @@ CF_EXPORT _Nullable CFErrorRef CFReadStreamCopyError(CFReadStreamRef stream);

CF_EXPORT _Nullable CFErrorRef CFWriteStreamCopyError(CFWriteStreamRef stream);

CF_SWIFT_EXPORT Boolean _CFBundleSupportsFHSBundles(void);

// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
// Version 0.8

Expand Down
1 change: 0 additions & 1 deletion CoreFoundation/Base.subproj/module.modulemap
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
module CoreFoundation [extern_c] [system] {
umbrella header "CoreFoundation.h"
explicit module CFPlugInCOM { header "CFPlugInCOM.h" }

}
40 changes: 40 additions & 0 deletions CoreFoundation/PlugIn.subproj/CFBundle.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ CONST_STRING_DECL(_kCFBundleCFMLoadAsBundleKey, "CFBundleCFMLoadAsBundle")
// Keys used by NSBundle for loaded Info plists.
CONST_STRING_DECL(_kCFBundlePrincipalClassKey, "NSPrincipalClass")


static CFTypeID __kCFBundleTypeID = _kCFRuntimeNotATypeID;

static pthread_mutex_t CFBundleGlobalDataLock = PTHREAD_MUTEX_INITIALIZER;
Expand All @@ -136,6 +137,41 @@ static void _CFBundleEnsureBundlesExistForImagePaths(CFArrayRef imagePaths);

#pragma mark -

#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS

// Functions and constants for FHS bundles:
#define _CFBundleFHSDirectory_share CFSTR("share")

static Boolean _CFBundleURLIsForFHSInstalledBundle(CFURLRef bundleURL) {
// Paths of this form are FHS installed bundles:
// <anywhere>/share/<name>.resources

CFStringRef extension = CFURLCopyPathExtension(bundleURL);
CFURLRef parentURL = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, bundleURL);
CFStringRef containingDirectoryName = parentURL ? CFURLCopyLastPathComponent(parentURL) : NULL;

Boolean isFHSBundle =
extension &&
containingDirectoryName &&
CFEqual(extension, _CFBundleSiblingResourceDirectoryExtension) &&
CFEqual(containingDirectoryName, _CFBundleFHSDirectory_share);

if (extension) CFRelease(extension);
if (parentURL) CFRelease(parentURL);
if (containingDirectoryName) CFRelease(containingDirectoryName);

return isFHSBundle;
}
#endif // !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS

Boolean _CFBundleSupportsFHSBundles() {
#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
return true;
#else
return false;
#endif
}

CF_PRIVATE os_log_t _CFBundleResourceLogger(void) {
static os_log_t _log;
static dispatch_once_t onceToken;
Expand Down Expand Up @@ -677,6 +713,10 @@ static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL,
}

bundle->_url = newURL;

#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
bundle->_isFHSInstalledBundle = _CFBundleURLIsForFHSInstalledBundle(newURL);
#endif

bundle->_version = localVersion;
bundle->_infoDict = NULL;
Expand Down
107 changes: 99 additions & 8 deletions CoreFoundation/PlugIn.subproj/CFBundle_Executable.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,41 @@
#include <dlfcn.h>
#endif

#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS

#if DEPLOYMENT_TARGET_LINUX
#if __LP64__
#define _CFBundleFHSArchDirectorySuffix "64"
#else // !__LP64__
#define _CFBundleFHSArchDirectorySuffix "32"
#endif // __LP64__
#endif // DEPLOYMENT_TARGET_LINUX

CONST_STRING_DECL(_kCFBundleFHSDirectory_bin, "bin");
CONST_STRING_DECL(_kCFBundleFHSDirectory_sbin, "sbin");
CONST_STRING_DECL(_kCFBundleFHSDirectory_lib, "lib");
#if DEPLOYMENT_TARGET_LINUX
CONST_STRING_DECL(_kCFBundleFHSDirectory_libWithArchSuffix, "lib" _CFBundleFHSArchDirectorySuffix);
#endif

#define _CFBundleFHSExecutablesDirectorySuffix CFSTR(".executables")
#define _CFBundleFHSDirectoryCLiteral_libexec "libexec"

#if DEPLOYMENT_TARGET_LINUX
#define _CFBundleFHSDirectoriesInExecutableSearchOrder \
_kCFBundleFHSDirectory_bin, \
_kCFBundleFHSDirectory_sbin, \
_kCFBundleFHSDirectory_libWithArchSuffix, \
_kCFBundleFHSDirectory_lib
#else
#define _CFBundleFHSDirectoriesInExecutableSearchOrder \
_kCFBundleFHSDirectory_bin, \
_kCFBundleFHSDirectory_sbin, \
_kCFBundleFHSDirectory_lib
#endif // DEPLOYMENT_TARGET_LINUX

#endif // !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS

// This is here because on iPhoneOS with the dyld shared cache, we remove binaries from their
// original locations on disk, so checking whether a binary's path exists is no longer sufficient.
// For performance reasons, we only call dlopen_preflight() after we've verified that the binary
Expand All @@ -38,7 +73,22 @@ static CFURLRef _CFBundleCopyExecutableURLRaw(CFURLRef urlPath, CFStringRef exeN
CFURLRef executableURL = NULL;
if (!urlPath || !exeName) return NULL;

#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
if (!executableURL) {
executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, exeName, kCFURLPOSIXPathStyle, false, urlPath);
if (!_binaryLoadable(executableURL)) {
CFRelease(executableURL);

CFStringRef sharedLibraryName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@%@"), _CFBundleFHSSharedLibraryFilenamePrefix, exeName, _CFBundleFHSSharedLibraryFilenameSuffix);

executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, sharedLibraryName, kCFURLPOSIXPathStyle, false, urlPath);
if (!_binaryLoadable(executableURL)) {
CFRelease(executableURL);
executableURL = NULL;
}
}
}
#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
const uint8_t *image_suffix = (uint8_t *)__CFgetenvIfNotRestricted("DYLD_IMAGE_SUFFIX");

if (image_suffix) {
Expand Down Expand Up @@ -152,19 +202,60 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL
#else
Boolean doExecSearch = true;
#endif

#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
if (lookupMainExe && bundle && bundle->_isFHSInstalledBundle) {
// For a FHS installed bundle, the URL points to share/Bundle.resources, and the binary is in:

CFURLRef prefix = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, url);

CFStringRef directories[] = { _CFBundleFHSDirectoriesInExecutableSearchOrder };
size_t directoriesCount = sizeof(directories) / sizeof(directories[0]);

for (size_t i = 0; i < directoriesCount; i++) {
CFURLRef where = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, prefix, directories[i], true);
executableURL = _CFBundleCopyExecutableURLRaw(where, executableName);
CFRelease(where);

if (executableURL) {
foundIt = true;
break;
}
}

CFRelease(prefix);
}
#endif // !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS

// Now, look for the executable inside the bundle.
if (doExecSearch && 0 != version) {
if (!foundIt && doExecSearch && 0 != version) {
CFURLRef exeDirURL = NULL;

#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
if (bundle && bundle->_isFHSInstalledBundle) {
CFURLRef withoutExtension = CFURLCreateCopyDeletingPathExtension(kCFAllocatorSystemDefault, url);
CFStringRef lastPathComponent = CFURLCopyLastPathComponent(withoutExtension);

CFURLRef libexec = CFURLCreateWithString(kCFAllocatorSystemDefault, CFSTR("../../" _CFBundleFHSDirectoryCLiteral_libexec), url);

CFStringRef exeDirName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@"), lastPathComponent, _CFBundleFHSExecutablesDirectorySuffix);
exeDirURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, libexec, exeDirName, true);

CFRelease(withoutExtension);
CFRelease(lastPathComponent);
CFRelease(libexec);
CFRelease(exeDirName);
} else
#endif // !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
if (1 == version) {
exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleExecutablesURLFromBase1, url);
} else if (2 == version) {
exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleExecutablesURLFromBase2, url);
} else {
#if DEPLOYMENT_TARGET_WINDOWS
// On Windows, if the bundle URL is foo.resources, then the executable is at the same level as the .resources directory
#if DEPLOYMENT_TARGET_WINDOWS || !DEPLOYMENT_RUNTIME_OBJC
// On Windows and on targets that support FHS bundles, if the bundle URL is foo.resources, then the executable is at the same level as the .resources directory
CFStringRef extension = CFURLCopyPathExtension(url);
if (extension && CFEqual(extension, _CFBundleWindowsResourceDirectoryExtension)) {
if (extension && CFEqual(extension, _CFBundleSiblingResourceDirectoryExtension)) {
exeDirURL = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, url);
} else {
exeDirURL = (CFURLRef)CFRetain(url);
Expand Down Expand Up @@ -201,7 +292,7 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL
CFURLRef absURL = CFURLCopyAbsoluteURL(executableURL);
#if DEPLOYMENT_TARGET_WINDOWS
executablePath = CFURLCopyFileSystemPath(absURL, kCFURLWindowsPathStyle);
#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#else
executablePath = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle);
#endif
CFRelease(absURL);
Expand Down Expand Up @@ -232,15 +323,15 @@ static CFURLRef _CFBundleCopyBundleURLForExecutablePath(CFStringRef str) {
#if DEPLOYMENT_TARGET_WINDOWS
// Is this a .dll or .exe?
if (buffLen >= 5 && (_wcsnicmp((wchar_t *)&(buff[buffLen-4]), L".dll", 4) == 0 || _wcsnicmp((wchar_t *)&(buff[buffLen-4]), L".exe", 4) == 0)) {
CFIndex extensionLength = CFStringGetLength(_CFBundleWindowsResourceDirectoryExtension);
CFIndex extensionLength = CFStringGetLength(_CFBundleSiblingResourceDirectoryExtension);
buffLen -= 4;
// If this is an _debug, we should strip that before looking for the bundle
if (buffLen >= 7 && (_wcsnicmp((wchar_t *)&buff[buffLen-6], L"_debug", 6) == 0)) buffLen -= 6;

if (buffLen + 1 + extensionLength < CFMaxPathSize) {
buff[buffLen] = '.';
buffLen ++;
CFStringGetCharacters(_CFBundleWindowsResourceDirectoryExtension, CFRangeMake(0, extensionLength), buff + buffLen);
CFStringGetCharacters(_CFBundleSiblingResourceDirectoryExtension, CFRangeMake(0, extensionLength), buff + buffLen);
buffLen += extensionLength;
outstr = CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault, buff, buffLen, kCFAllocatorNull);
url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, outstr, PLATFORM_PATH_STYLE, true);
Expand Down
21 changes: 20 additions & 1 deletion CoreFoundation/PlugIn.subproj/CFBundle_Internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ CF_EXTERN_C_BEGIN
#define PLATFORM_PATH_STYLE kCFURLPOSIXPathStyle
#endif

// FHS bundles are supported on the Swift and C runtimes, except on Windows.
#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS

#if DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
#define _CFBundleFHSSharedLibraryFilenamePrefix CFSTR("lib")
#define _CFBundleFHSSharedLibraryFilenameSuffix CFSTR(".so")
#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#define _CFBundleFHSSharedLibraryFilenamePrefix CFSTR("lib")
#define _CFBundleFHSSharedLibraryFilenameSuffix CFSTR(".dylib")
#else // a non-covered DEPLOYMENT_TARGET…
#error Disable FHS bundles or specify shared library prefixes and suffixes for this platform.
#endif // DEPLOYMENT_TARGET_…

#endif // !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS

#define CFBundleExecutableNotFoundError 4
#define CFBundleExecutableNotLoadableError 3584
#define CFBundleExecutableArchitectureMismatchError 3585
Expand Down Expand Up @@ -62,6 +77,10 @@ struct __CFBundle {

CFURLRef _url;

#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
Boolean _isFHSInstalledBundle;
#endif

CFDictionaryRef _infoDict;
CFDictionaryRef _localInfoDict;
CFArrayRef _searchLanguages;
Expand Down Expand Up @@ -348,7 +367,7 @@ CF_PRIVATE CFStringRef _CFBundleGetPlatformNameSuffix(void);

#define _CFBundleLocalizedResourceForkFileName CFSTR("Localized")

#define _CFBundleWindowsResourceDirectoryExtension CFSTR("resources")
#define _CFBundleSiblingResourceDirectoryExtension CFSTR("resources")

#define _CFBundleMacOSXInfoPlistPlatformName_OLD CFSTR("macos")
#define _CFBundleWindowsInfoPlistPlatformName_OLD CFSTR("win32")
Expand Down
Loading