Skip to content

Commit bc4a91c

Browse files
authored
Merge pull request #1395 from millenomi/pr/fhs-bundles-impl
2 parents 359c396 + f99509f commit bc4a91c

File tree

6 files changed

+396
-54
lines changed

6 files changed

+396
-54
lines changed

CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,8 @@ CF_EXPORT _Nullable CFErrorRef CFReadStreamCopyError(CFReadStreamRef stream);
327327

328328
CF_EXPORT _Nullable CFErrorRef CFWriteStreamCopyError(CFWriteStreamRef stream);
329329

330+
CF_SWIFT_EXPORT Boolean _CFBundleSupportsFHSBundles(void);
331+
330332
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
331333
// Version 0.8
332334

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
module CoreFoundation [extern_c] [system] {
22
umbrella header "CoreFoundation.h"
33
explicit module CFPlugInCOM { header "CFPlugInCOM.h" }
4-
54
}

CoreFoundation/PlugIn.subproj/CFBundle.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ CONST_STRING_DECL(_kCFBundleCFMLoadAsBundleKey, "CFBundleCFMLoadAsBundle")
118118
// Keys used by NSBundle for loaded Info plists.
119119
CONST_STRING_DECL(_kCFBundlePrincipalClassKey, "NSPrincipalClass")
120120

121+
121122
static CFTypeID __kCFBundleTypeID = _kCFRuntimeNotATypeID;
122123

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

137138
#pragma mark -
138139

140+
#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
141+
142+
// Functions and constants for FHS bundles:
143+
#define _CFBundleFHSDirectory_share CFSTR("share")
144+
145+
static Boolean _CFBundleURLIsForFHSInstalledBundle(CFURLRef bundleURL) {
146+
// Paths of this form are FHS installed bundles:
147+
// <anywhere>/share/<name>.resources
148+
149+
CFStringRef extension = CFURLCopyPathExtension(bundleURL);
150+
CFURLRef parentURL = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, bundleURL);
151+
CFStringRef containingDirectoryName = parentURL ? CFURLCopyLastPathComponent(parentURL) : NULL;
152+
153+
Boolean isFHSBundle =
154+
extension &&
155+
containingDirectoryName &&
156+
CFEqual(extension, _CFBundleSiblingResourceDirectoryExtension) &&
157+
CFEqual(containingDirectoryName, _CFBundleFHSDirectory_share);
158+
159+
if (extension) CFRelease(extension);
160+
if (parentURL) CFRelease(parentURL);
161+
if (containingDirectoryName) CFRelease(containingDirectoryName);
162+
163+
return isFHSBundle;
164+
}
165+
#endif // !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
166+
167+
Boolean _CFBundleSupportsFHSBundles() {
168+
#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
169+
return true;
170+
#else
171+
return false;
172+
#endif
173+
}
174+
139175
CF_PRIVATE os_log_t _CFBundleResourceLogger(void) {
140176
static os_log_t _log;
141177
static dispatch_once_t onceToken;
@@ -677,6 +713,10 @@ static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL,
677713
}
678714

679715
bundle->_url = newURL;
716+
717+
#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
718+
bundle->_isFHSInstalledBundle = _CFBundleURLIsForFHSInstalledBundle(newURL);
719+
#endif
680720

681721
bundle->_version = localVersion;
682722
bundle->_infoDict = NULL;

CoreFoundation/PlugIn.subproj/CFBundle_Executable.c

Lines changed: 99 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,41 @@
1515
#include <dlfcn.h>
1616
#endif
1717

18+
#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
19+
20+
#if DEPLOYMENT_TARGET_LINUX
21+
#if __LP64__
22+
#define _CFBundleFHSArchDirectorySuffix "64"
23+
#else // !__LP64__
24+
#define _CFBundleFHSArchDirectorySuffix "32"
25+
#endif // __LP64__
26+
#endif // DEPLOYMENT_TARGET_LINUX
27+
28+
CONST_STRING_DECL(_kCFBundleFHSDirectory_bin, "bin");
29+
CONST_STRING_DECL(_kCFBundleFHSDirectory_sbin, "sbin");
30+
CONST_STRING_DECL(_kCFBundleFHSDirectory_lib, "lib");
31+
#if DEPLOYMENT_TARGET_LINUX
32+
CONST_STRING_DECL(_kCFBundleFHSDirectory_libWithArchSuffix, "lib" _CFBundleFHSArchDirectorySuffix);
33+
#endif
34+
35+
#define _CFBundleFHSExecutablesDirectorySuffix CFSTR(".executables")
36+
#define _CFBundleFHSDirectoryCLiteral_libexec "libexec"
37+
38+
#if DEPLOYMENT_TARGET_LINUX
39+
#define _CFBundleFHSDirectoriesInExecutableSearchOrder \
40+
_kCFBundleFHSDirectory_bin, \
41+
_kCFBundleFHSDirectory_sbin, \
42+
_kCFBundleFHSDirectory_libWithArchSuffix, \
43+
_kCFBundleFHSDirectory_lib
44+
#else
45+
#define _CFBundleFHSDirectoriesInExecutableSearchOrder \
46+
_kCFBundleFHSDirectory_bin, \
47+
_kCFBundleFHSDirectory_sbin, \
48+
_kCFBundleFHSDirectory_lib
49+
#endif // DEPLOYMENT_TARGET_LINUX
50+
51+
#endif // !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
52+
1853
// This is here because on iPhoneOS with the dyld shared cache, we remove binaries from their
1954
// original locations on disk, so checking whether a binary's path exists is no longer sufficient.
2055
// For performance reasons, we only call dlopen_preflight() after we've verified that the binary
@@ -38,7 +73,22 @@ static CFURLRef _CFBundleCopyExecutableURLRaw(CFURLRef urlPath, CFStringRef exeN
3873
CFURLRef executableURL = NULL;
3974
if (!urlPath || !exeName) return NULL;
4075

41-
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
76+
#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
77+
if (!executableURL) {
78+
executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, exeName, kCFURLPOSIXPathStyle, false, urlPath);
79+
if (!_binaryLoadable(executableURL)) {
80+
CFRelease(executableURL);
81+
82+
CFStringRef sharedLibraryName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@%@"), _CFBundleFHSSharedLibraryFilenamePrefix, exeName, _CFBundleFHSSharedLibraryFilenameSuffix);
83+
84+
executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, sharedLibraryName, kCFURLPOSIXPathStyle, false, urlPath);
85+
if (!_binaryLoadable(executableURL)) {
86+
CFRelease(executableURL);
87+
executableURL = NULL;
88+
}
89+
}
90+
}
91+
#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
4292
const uint8_t *image_suffix = (uint8_t *)__CFgetenvIfNotRestricted("DYLD_IMAGE_SUFFIX");
4393

4494
if (image_suffix) {
@@ -152,19 +202,60 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL
152202
#else
153203
Boolean doExecSearch = true;
154204
#endif
205+
206+
#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
207+
if (lookupMainExe && bundle && bundle->_isFHSInstalledBundle) {
208+
// For a FHS installed bundle, the URL points to share/Bundle.resources, and the binary is in:
209+
210+
CFURLRef prefix = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, url);
211+
212+
CFStringRef directories[] = { _CFBundleFHSDirectoriesInExecutableSearchOrder };
213+
size_t directoriesCount = sizeof(directories) / sizeof(directories[0]);
214+
215+
for (size_t i = 0; i < directoriesCount; i++) {
216+
CFURLRef where = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, prefix, directories[i], true);
217+
executableURL = _CFBundleCopyExecutableURLRaw(where, executableName);
218+
CFRelease(where);
219+
220+
if (executableURL) {
221+
foundIt = true;
222+
break;
223+
}
224+
}
225+
226+
CFRelease(prefix);
227+
}
228+
#endif // !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
229+
155230
// Now, look for the executable inside the bundle.
156-
if (doExecSearch && 0 != version) {
231+
if (!foundIt && doExecSearch && 0 != version) {
157232
CFURLRef exeDirURL = NULL;
158233

234+
#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
235+
if (bundle && bundle->_isFHSInstalledBundle) {
236+
CFURLRef withoutExtension = CFURLCreateCopyDeletingPathExtension(kCFAllocatorSystemDefault, url);
237+
CFStringRef lastPathComponent = CFURLCopyLastPathComponent(withoutExtension);
238+
239+
CFURLRef libexec = CFURLCreateWithString(kCFAllocatorSystemDefault, CFSTR("../../" _CFBundleFHSDirectoryCLiteral_libexec), url);
240+
241+
CFStringRef exeDirName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@"), lastPathComponent, _CFBundleFHSExecutablesDirectorySuffix);
242+
exeDirURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, libexec, exeDirName, true);
243+
244+
CFRelease(withoutExtension);
245+
CFRelease(lastPathComponent);
246+
CFRelease(libexec);
247+
CFRelease(exeDirName);
248+
} else
249+
#endif // !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
159250
if (1 == version) {
160251
exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleExecutablesURLFromBase1, url);
161252
} else if (2 == version) {
162253
exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleExecutablesURLFromBase2, url);
163254
} else {
164-
#if DEPLOYMENT_TARGET_WINDOWS
165-
// On Windows, if the bundle URL is foo.resources, then the executable is at the same level as the .resources directory
255+
#if DEPLOYMENT_TARGET_WINDOWS || !DEPLOYMENT_RUNTIME_OBJC
256+
// 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
166257
CFStringRef extension = CFURLCopyPathExtension(url);
167-
if (extension && CFEqual(extension, _CFBundleWindowsResourceDirectoryExtension)) {
258+
if (extension && CFEqual(extension, _CFBundleSiblingResourceDirectoryExtension)) {
168259
exeDirURL = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, url);
169260
} else {
170261
exeDirURL = (CFURLRef)CFRetain(url);
@@ -201,7 +292,7 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL
201292
CFURLRef absURL = CFURLCopyAbsoluteURL(executableURL);
202293
#if DEPLOYMENT_TARGET_WINDOWS
203294
executablePath = CFURLCopyFileSystemPath(absURL, kCFURLWindowsPathStyle);
204-
#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
295+
#else
205296
executablePath = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle);
206297
#endif
207298
CFRelease(absURL);
@@ -232,15 +323,15 @@ static CFURLRef _CFBundleCopyBundleURLForExecutablePath(CFStringRef str) {
232323
#if DEPLOYMENT_TARGET_WINDOWS
233324
// Is this a .dll or .exe?
234325
if (buffLen >= 5 && (_wcsnicmp((wchar_t *)&(buff[buffLen-4]), L".dll", 4) == 0 || _wcsnicmp((wchar_t *)&(buff[buffLen-4]), L".exe", 4) == 0)) {
235-
CFIndex extensionLength = CFStringGetLength(_CFBundleWindowsResourceDirectoryExtension);
326+
CFIndex extensionLength = CFStringGetLength(_CFBundleSiblingResourceDirectoryExtension);
236327
buffLen -= 4;
237328
// If this is an _debug, we should strip that before looking for the bundle
238329
if (buffLen >= 7 && (_wcsnicmp((wchar_t *)&buff[buffLen-6], L"_debug", 6) == 0)) buffLen -= 6;
239330

240331
if (buffLen + 1 + extensionLength < CFMaxPathSize) {
241332
buff[buffLen] = '.';
242333
buffLen ++;
243-
CFStringGetCharacters(_CFBundleWindowsResourceDirectoryExtension, CFRangeMake(0, extensionLength), buff + buffLen);
334+
CFStringGetCharacters(_CFBundleSiblingResourceDirectoryExtension, CFRangeMake(0, extensionLength), buff + buffLen);
244335
buffLen += extensionLength;
245336
outstr = CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault, buff, buffLen, kCFAllocatorNull);
246337
url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, outstr, PLATFORM_PATH_STYLE, true);

CoreFoundation/PlugIn.subproj/CFBundle_Internal.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,21 @@ CF_EXTERN_C_BEGIN
3030
#define PLATFORM_PATH_STYLE kCFURLPOSIXPathStyle
3131
#endif
3232

33+
// FHS bundles are supported on the Swift and C runtimes, except on Windows.
34+
#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
35+
36+
#if DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
37+
#define _CFBundleFHSSharedLibraryFilenamePrefix CFSTR("lib")
38+
#define _CFBundleFHSSharedLibraryFilenameSuffix CFSTR(".so")
39+
#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
40+
#define _CFBundleFHSSharedLibraryFilenamePrefix CFSTR("lib")
41+
#define _CFBundleFHSSharedLibraryFilenameSuffix CFSTR(".dylib")
42+
#else // a non-covered DEPLOYMENT_TARGET…
43+
#error Disable FHS bundles or specify shared library prefixes and suffixes for this platform.
44+
#endif // DEPLOYMENT_TARGET_…
45+
46+
#endif // !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
47+
3348
#define CFBundleExecutableNotFoundError 4
3449
#define CFBundleExecutableNotLoadableError 3584
3550
#define CFBundleExecutableArchitectureMismatchError 3585
@@ -62,6 +77,10 @@ struct __CFBundle {
6277

6378
CFURLRef _url;
6479

80+
#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS
81+
Boolean _isFHSInstalledBundle;
82+
#endif
83+
6584
CFDictionaryRef _infoDict;
6685
CFDictionaryRef _localInfoDict;
6786
CFArrayRef _searchLanguages;
@@ -348,7 +367,7 @@ CF_PRIVATE CFStringRef _CFBundleGetPlatformNameSuffix(void);
348367

349368
#define _CFBundleLocalizedResourceForkFileName CFSTR("Localized")
350369

351-
#define _CFBundleWindowsResourceDirectoryExtension CFSTR("resources")
370+
#define _CFBundleSiblingResourceDirectoryExtension CFSTR("resources")
352371

353372
#define _CFBundleMacOSXInfoPlistPlatformName_OLD CFSTR("macos")
354373
#define _CFBundleWindowsInfoPlistPlatformName_OLD CFSTR("win32")

0 commit comments

Comments
 (0)