From 8bdfb2bd57cc479b1a9ef14eb1004080c4f592c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Jose=CC=81=20Pereira=20Vieito?= Date: Thu, 31 May 2018 02:41:44 +0200 Subject: [PATCH 01/10] [WIP] Added support for Freestanding Bundles reverse lookup - This enables to find freestanding bundles directories. - Unifies Windows and other platforms approach. - Initial implementation (there are some leaks). --- .../PlugIn.subproj/CFBundle_Executable.c | 269 ++++++++++-------- .../PlugIn.subproj/CFBundle_Internal.h | 36 ++- 2 files changed, 180 insertions(+), 125 deletions(-) diff --git a/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c b/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c index dbb33d8eb3..2efdb4d530 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c +++ b/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c @@ -1,12 +1,12 @@ -/* CFBundle_Executable.c - Copyright (c) 1999-2017, Apple Inc. and the Swift project authors - - Portions Copyright (c) 2014-2017, Apple Inc. and the Swift project authors - Licensed under Apache License v2.0 with Runtime Library Exception - See http://swift.org/LICENSE.txt for license information - See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors - Responsibility: Tony Parker -*/ +/* CFBundle_Executable.c + Copyright (c) 1999-2018, Apple Inc. and the Swift project authors + + Portions Copyright (c) 2014-2017, Apple Inc. and the Swift project authors + Licensed under Apache License v2.0 with Runtime Library Exception + See http://swift.org/LICENSE.txt for license information + See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + Responsibility: Tony Parker + */ #include #include "CFBundle_Internal.h" @@ -15,7 +15,13 @@ #include #endif -#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS && !DEPLOYMENT_TARGET_ANDROID +#if FHS_BUNDLES + #define _CFBundleFHSExecutablesDirectorySuffix CFSTR(".executables") + #define _CFBundleFHSDirectoryCLiteral_libexec "libexec" + + CONST_STRING_DECL(_kCFBundleFHSDirectory_bin, "bin"); + CONST_STRING_DECL(_kCFBundleFHSDirectory_sbin, "sbin"); + CONST_STRING_DECL(_kCFBundleFHSDirectory_lib, "lib"); #if DEPLOYMENT_TARGET_LINUX #if __LP64__ @@ -23,32 +29,23 @@ #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 \ + #if DEPLOYMENT_TARGET_LINUX + #define _CFBundleFHSDirectoriesInExecutableSearchOrder \ _kCFBundleFHSDirectory_bin, \ _kCFBundleFHSDirectory_sbin, \ _kCFBundleFHSDirectory_libWithArchSuffix, \ _kCFBundleFHSDirectory_lib -#else - #define _CFBundleFHSDirectoriesInExecutableSearchOrder \ + #else + #define _CFBundleFHSDirectoriesInExecutableSearchOrder \ _kCFBundleFHSDirectory_bin, \ _kCFBundleFHSDirectory_sbin, \ _kCFBundleFHSDirectory_lib -#endif // DEPLOYMENT_TARGET_LINUX - -#endif // !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS && !DEPLOYMENT_TARGET_ANDROID + #endif +#endif // FHS_BUNDLES // 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. @@ -73,23 +70,8 @@ static CFURLRef _CFBundleCopyExecutableURLRaw(CFURLRef urlPath, CFStringRef exeN CFURLRef executableURL = NULL; if (!urlPath || !exeName) return NULL; -#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS && !DEPLOYMENT_TARGET_ANDROID - 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 DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI + const uint8_t *image_suffix = (uint8_t *)__CFgetenvIfNotRestricted("DYLD_IMAGE_SUFFIX"); if (image_suffix) { CFStringRef newExeName, imageSuffix; @@ -109,6 +91,7 @@ static CFURLRef _CFBundleCopyExecutableURLRaw(CFURLRef urlPath, CFStringRef exeN CFRelease(newExeName); CFRelease(imageSuffix); } + if (!executableURL) { executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, exeName, kCFURLPOSIXPathStyle, false, urlPath); if (executableURL && !_binaryLoadable(executableURL)) { @@ -116,47 +99,36 @@ static CFURLRef _CFBundleCopyExecutableURLRaw(CFURLRef urlPath, CFStringRef exeN executableURL = NULL; } } -#elif DEPLOYMENT_TARGET_WINDOWS - if (!executableURL) { - executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, exeName, kCFURLWindowsPathStyle, false, urlPath); - if (executableURL && !_urlExists(executableURL)) { - CFRelease(executableURL); - executableURL = NULL; - } - } - if (!executableURL) { - if (!CFStringFindWithOptions(exeName, CFSTR(".dll"), CFRangeMake(0, CFStringGetLength(exeName)), kCFCompareAnchored|kCFCompareBackwards|kCFCompareCaseInsensitive, NULL)) { -#if defined(DEBUG) - CFStringRef extension = CFSTR("_debug.dll"); -#else - CFStringRef extension = CFSTR(".dll"); #endif - CFStringRef newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@"), exeName, extension); - executableURL = CFURLCreateWithString(kCFAllocatorSystemDefault, newExeName, urlPath); - if (executableURL && !_binaryLoadable(executableURL)) { - CFRelease(executableURL); - executableURL = NULL; - } - CFRelease(newExeName); - } - } + +#if FHS_BUNDLES || FREESTANDING_BUNDLES if (!executableURL) { - if (!CFStringFindWithOptions(exeName, CFSTR(".exe"), CFRangeMake(0, CFStringGetLength(exeName)), kCFCompareAnchored|kCFCompareBackwards|kCFCompareCaseInsensitive, NULL)) { -#if defined(DEBUG) - CFStringRef extension = CFSTR("_debug.exe"); -#else - CFStringRef extension = CFSTR(".exe"); -#endif - CFStringRef newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@"), exeName, extension); - executableURL = CFURLCreateWithString(kCFAllocatorSystemDefault, newExeName, urlPath); - if (executableURL && !_binaryLoadable(executableURL)) { + executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, exeName, kCFURLPOSIXPathStyle, false, urlPath); + + // Try with the input name. + if (!_binaryLoadable(executableURL)) { + CFRelease(executableURL); + + CFStringRef sharedLibraryName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@%@"), _CFBundleSharedLibraryFilenamePrefix, exeName, _CFBundleSharedLibraryFilenameSuffix); + + executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, sharedLibraryName, kCFURLPOSIXPathStyle, false, urlPath); + + if (!_binaryLoadable(executableURL)) { CFRelease(executableURL); - executableURL = NULL; + + CFStringRef sharedLibraryName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@%@"), _CFBundleExecutableFilenamePrefix, exeName, _CFBundleExecutableFilenameSuffix); + + executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, sharedLibraryName, kCFURLPOSIXPathStyle, false, urlPath); + + if (!_binaryLoadable(executableURL)) { + CFRelease(executableURL); + executableURL = NULL; + } } - CFRelease(newExeName); } } #endif + return executableURL; } @@ -199,14 +171,14 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL Boolean doExecSearch = true; #endif -#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS && !DEPLOYMENT_TARGET_ANDROID +#if FHS_BUNDLES if (lookupMainExe && bundle && bundle->_isFHSInstalledBundle) { // For a FHS installed bundle, the URL points to share/Bundle.resources, and the binary is in: CFURLRef sharePath = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, url); CFURLRef prefixPath = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, sharePath); CFRelease(sharePath); - + CFStringRef directories[] = { _CFBundleFHSDirectoriesInExecutableSearchOrder }; size_t directoriesCount = sizeof(directories) / sizeof(directories[0]); @@ -223,13 +195,13 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL CFRelease(prefixPath); } -#endif // !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS && !DEPLOYMENT_TARGET_ANDROID +#endif // FHS_BUNDLES // Now, look for the executable inside the bundle. if (!foundIt && doExecSearch && 0 != version) { CFURLRef exeDirURL = NULL; -#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS && !DEPLOYMENT_TARGET_ANDROID +#if FHS_BUNDLES if (bundle && bundle->_isFHSInstalledBundle) { CFURLRef withoutExtension = CFURLCreateCopyDeletingPathExtension(kCFAllocatorSystemDefault, url); CFStringRef lastPathComponent = CFURLCopyLastPathComponent(withoutExtension); @@ -244,24 +216,24 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL CFRelease(libexec); CFRelease(exeDirName); } else -#endif // !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS && !DEPLOYMENT_TARGET_ANDROID - if (1 == version) { - exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleExecutablesURLFromBase1, url); - } else if (2 == version) { - exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleExecutablesURLFromBase2, url); - } else { -#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, _CFBundleSiblingResourceDirectoryExtension)) { - exeDirURL = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, url); +#endif // FHS_BUNDLES + if (1 == version) { + exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleExecutablesURLFromBase1, url); + } else if (2 == version) { + exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleExecutablesURLFromBase2, url); } else { - exeDirURL = (CFURLRef)CFRetain(url); - } +#if FREESTANDING_BUNDLES + // 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, _CFBundleSiblingResourceDirectoryExtension)) { + exeDirURL = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, url); + } else { + exeDirURL = (CFURLRef)CFRetain(url); + } #else - exeDirURL = (CFURLRef)CFRetain(url); + exeDirURL = (CFURLRef)CFRetain(url); #endif - } + } // Historical note: This used to search the directories "Mac OS X", "MacOSClassic", then "MacOS8". As of 10.13 we only look in "MacOS". CFURLRef exeSubdirURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, _CFBundleGetPlatformExecutablesSubdirectoryName(), kCFURLPOSIXPathStyle, true, exeDirURL); @@ -303,38 +275,96 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL return executableURL; } +static CFStringRef _CFStringRemovePrefixSuffix(CFStringRef str, CFStringRef prefix, CFStringRef suffix) { + CFMutableStringRef mutableString = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, 0, str); + + // Check if the executable has shared library prefix/suffix. + CFRange prefixRange = CFStringFind(mutableString, + _CFBundleSharedLibraryFilenamePrefix, + kCFCompareAnchored); + CFRange suffixRange = CFStringFind(mutableString, + _CFBundleSharedLibraryFilenameSuffix, + kCFCompareAnchored|kCFCompareBackwards); + + // Only return the stripped string if both the prefix and suffix are found. + if (prefixRange.location != kCFNotFound && + suffixRange.location != kCFNotFound && + prefixRange.location == 0 && + suffixRange.location + suffixRange.length == CFStringGetLength(mutableString)) { + CFStringReplace(mutableString, prefixRange, CFSTR("")); + + suffixRange = CFStringFind(mutableString, + _CFBundleSharedLibraryFilenameSuffix, + kCFCompareAnchored|kCFCompareBackwards); + + CFStringReplace(mutableString, suffixRange, CFSTR("")); + return mutableString; + } + else { + return NULL; + } +} + static CFURLRef _CFBundleCopyBundleURLForExecutablePath(CFStringRef str) { - //!!! need to handle frameworks, NT; need to integrate with NSBundle - drd - UniChar buff[CFMaxPathSize]; - CFIndex buffLen; - CFURLRef url = NULL; - CFStringRef outstr; + // Needs to handle: + // - Classic NEXTStep-style bundles (`.framework`, `.app`, `.appex`) + // - Freestanding bundles (`Bundle.resource` as sibling of the library/executable) + // - FHS bundles (`prefix/share/Bundle.resource`) + // + // Note: + // For freestanding and FHS bundles, this function has to support removing library/executable prefix/suffix + // on each platform. For example, it has to detect to the bundle name as `Bundle` for executables + // like `Bundle.exe` in Windows or `libBundle.so` on Linux. - buffLen = CFStringGetLength(str); - if (buffLen > CFMaxPathSize) buffLen = CFMaxPathSize; - CFStringGetCharacters(str, CFRangeMake(0, buffLen), buff); + CFURLRef url = NULL; -#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(_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 FREESTANDING_BUNDLES + if (!url) { + CFURLRef executableURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, PLATFORM_PATH_STYLE, false); + CFStringRef executableName = CFURLCopyLastPathComponent(executableURL); + CFStringRef bundleName = NULL; - if (buffLen + 1 + extensionLength < CFMaxPathSize) { - buff[buffLen] = '.'; - buffLen ++; - CFStringGetCharacters(_CFBundleSiblingResourceDirectoryExtension, CFRangeMake(0, extensionLength), buff + buffLen); - buffLen += extensionLength; - outstr = CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault, buff, buffLen, kCFAllocatorNull); - url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, outstr, PLATFORM_PATH_STYLE, true); - CFRelease(outstr); + // Check if the executable has shared library prefix/suffix (libCF.dylib, libCF.so, CF.dll). + if (!bundleName) { + bundleName = _CFStringRemovePrefixSuffix(executableName, + _CFBundleSharedLibraryFilenamePrefix, + _CFBundleSharedLibraryFilenameSuffix); + } + + // Check if the executable has executable-style prefix/suffix (CF, CF.exe). + if (!bundleName) { + bundleName = _CFStringRemovePrefixSuffix(executableName, + _CFBundleExecutableFilenamePrefix, + _CFBundleExecutableFilenameSuffix); + } + + // Otherwise, use the executable name as bundle name (CF). + if (!bundleName) { + bundleName = executableName; + } + + // Find the sibling `.resources` directory (CF.resources). + CFURLRef parentDirectory = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, executableURL); + CFStringRef siblingResourceDirectoryName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@.%@"), bundleName, _CFBundleSiblingResourceDirectoryExtension); + + CFURLRef siblingResourceDirectory = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, parentDirectory, siblingResourceDirectoryName, true); + + // Check that the bundle directory exists. + if (_CFURLExists(siblingResourceDirectory)) { + url = siblingResourceDirectory; } } #endif if (!url) { + UniChar buff[CFMaxPathSize]; + CFIndex buffLen; + CFStringRef outstr; + + buffLen = CFStringGetLength(str); + if (buffLen > CFMaxPathSize) buffLen = CFMaxPathSize; + CFStringGetCharacters(str, CFRangeMake(0, buffLen), buff); + buffLen = _CFLengthAfterDeletingLastPathComponent(buff, buffLen); // Remove exe name if (buffLen > 0) { @@ -373,6 +403,7 @@ static CFURLRef _CFBundleCopyBundleURLForExecutablePath(CFStringRef str) { CFRelease(outstr); } } + return url; } @@ -391,12 +422,14 @@ static CFURLRef _CFBundleCopyResolvedURLForExecutableURL(CFURLRef url) { if (len1 > 0 && len1 + 1 < buffLen) { str1 = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, buff, len1); CFIndex skipSlashCount = 1; + #if DEPLOYMENT_TARGET_WINDOWS // On Windows, _CFLengthAfterDeletingLastPathComponent will return a value of 3 if the path is at the root (e.g. C:\). This includes the \, which is not the case for URLs with subdirectories if (len1 == 3 && buff[1] == ':' && buff[2] == '\\') { skipSlashCount = 0; } #endif + str2 = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, buff + len1 + skipSlashCount, buffLen - len1 - skipSlashCount); if (str1 && str2) { url1 = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str1, PLATFORM_PATH_STYLE, true); diff --git a/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h b/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h index b94a9aeb18..2113f7d705 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h +++ b/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h @@ -30,20 +30,42 @@ CF_EXTERN_C_BEGIN #define PLATFORM_PATH_STYLE kCFURLPOSIXPathStyle #endif +// Freestanding bundles are supported on all platforms. +#if TRUE +#define FREESTANDING_BUNDLES 1 +#else +#define FREESTANDING_BUNDLES 0 +#endif + // FHS bundles are supported on the Swift and C runtimes, except on Windows. #if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS && !DEPLOYMENT_TARGET_ANDROID +#define FHS_BUNDLES 1 +#else +#define FHS_BUNDLES 0 +#endif + +#if FHS_BUNDLES || FREESTANDING_BUNDLES #if DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD - #define _CFBundleFHSSharedLibraryFilenamePrefix CFSTR("lib") - #define _CFBundleFHSSharedLibraryFilenameSuffix CFSTR(".so") + #define _CFBundleSharedLibraryFilenamePrefix CFSTR("lib") + #define _CFBundleSharedLibraryFilenameSuffix CFSTR(".so") + #define _CFBundleExecutableFilenamePrefix CFSTR("") + #define _CFBundleExecutableFilenameSuffix CFSTR("") #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI - #define _CFBundleFHSSharedLibraryFilenamePrefix CFSTR("lib") - #define _CFBundleFHSSharedLibraryFilenameSuffix CFSTR(".dylib") + #define _CFBundleSharedLibraryFilenamePrefix CFSTR("lib") + #define _CFBundleSharedLibraryFilenameSuffix CFSTR(".dylib") + #define _CFBundleExecutableFilenamePrefix CFSTR("") + #define _CFBundleExecutableFilenameSuffix CFSTR("") +#elif DEPLOYMENT_TARGET_WINDOWS + #define _CFBundleSharedLibraryFilenamePrefix CFSTR("") + #define _CFBundleSharedLibraryFilenameSuffix CFSTR(".dll") + #define _CFBundleExecutableFilenamePrefix CFSTR("") + #define _CFBundleExecutableFilenameSuffix CFSTR(".exe") #else // a non-covered DEPLOYMENT_TARGET… - #error Disable FHS bundles or specify shared library prefixes and suffixes for this platform. -#endif // DEPLOYMENT_TARGET_… + #error Disable Freestanding / FHS bundles or specify shared library prefixes and suffixes for this platform. +#endif -#endif // !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS && !DEPLOYMENT_TARGET_ANDROID +#endif #define CFBundleExecutableNotFoundError 4 #define CFBundleExecutableNotLoadableError 3584 From 92b163909e3768d3efa398d8c7c8e2b8aa90edac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Jose=CC=81=20Pereira=20Vieito?= Date: Fri, 1 Jun 2018 00:01:43 +0200 Subject: [PATCH 02/10] Disabled freestanding bundles on Darwin --- CoreFoundation/PlugIn.subproj/CFBundle_Internal.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h b/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h index 2113f7d705..fd8cd54722 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h +++ b/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h @@ -30,14 +30,14 @@ CF_EXTERN_C_BEGIN #define PLATFORM_PATH_STYLE kCFURLPOSIXPathStyle #endif -// Freestanding bundles are supported on all platforms. -#if TRUE +// Freestanding bundles are not supported on Darwin. +#if !DEPLOYMENT_RUNTIME_OBJC #define FREESTANDING_BUNDLES 1 #else #define FREESTANDING_BUNDLES 0 #endif -// FHS bundles are supported on the Swift and C runtimes, except on Windows. +// FHS bundles are not supported on Darwin, Windows and Android. #if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS && !DEPLOYMENT_TARGET_ANDROID #define FHS_BUNDLES 1 #else From 4706dd869cfa3bac1c0445415c03b5c2137c1978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Jose=CC=81=20Pereira=20Vieito?= Date: Fri, 1 Jun 2018 00:23:12 +0200 Subject: [PATCH 03/10] Fixed leaks and cleaned `_CFBundleCopyExecutableURLInDirectory2()` --- .../PlugIn.subproj/CFBundle_Executable.c | 281 ++++++++++-------- 1 file changed, 158 insertions(+), 123 deletions(-) diff --git a/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c b/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c index 2efdb4d530..323843e5ac 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c +++ b/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c @@ -66,24 +66,27 @@ static Boolean _binaryLoadable(CFURLRef url) { } static CFURLRef _CFBundleCopyExecutableURLRaw(CFURLRef urlPath, CFStringRef exeName) { - // Given an url to a folder and a name, this returns the url to the executable in that folder with that name, if it exists, and NULL otherwise. This function deals with appending the ".exe" or ".dll" on Windows. + // Given an url to a folder and a name, this returns the url to the executable in that folder + // with that name, if it exists, and NULL otherwise. + // This function deals with appending the library and executable prefix/suffixes of each platform. + CFURLRef executableURL = NULL; if (!urlPath || !exeName) return NULL; #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI - const uint8_t *image_suffix = (uint8_t *)__CFgetenvIfNotRestricted("DYLD_IMAGE_SUFFIX"); + const uint8_t *image_suffix = (uint8_t *)__CFgetenvIfNotRestricted("DYLD_IMAGE_SUFFIX"); if (image_suffix) { CFStringRef newExeName, imageSuffix; imageSuffix = CFStringCreateWithCString(kCFAllocatorSystemDefault, (char *)image_suffix, kCFStringEncodingUTF8); if (CFStringHasSuffix(exeName, CFSTR(".dylib"))) { CFStringRef bareExeName = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, exeName, CFRangeMake(0, CFStringGetLength(exeName)-6)); - newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@.dylib"), exeName, imageSuffix); + newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@.dylib"), bareExeName, imageSuffix); CFRelease(bareExeName); } else { newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@"), exeName, imageSuffix); } - executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, newExeName, kCFURLPOSIXPathStyle, false, urlPath); + executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, newExeName, PLATFORM_PATH_STYLE, false, urlPath); if (executableURL && !_binaryLoadable(executableURL)) { CFRelease(executableURL); executableURL = NULL; @@ -91,41 +94,45 @@ static CFURLRef _CFBundleCopyExecutableURLRaw(CFURLRef urlPath, CFStringRef exeN CFRelease(newExeName); CFRelease(imageSuffix); } +#endif if (!executableURL) { - executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, exeName, kCFURLPOSIXPathStyle, false, urlPath); + // Try finding the executable with input name. + executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, exeName, PLATFORM_PATH_STYLE, false, urlPath); + if (executableURL && !_binaryLoadable(executableURL)) { CFRelease(executableURL); executableURL = NULL; } } -#endif #if FHS_BUNDLES || FREESTANDING_BUNDLES if (!executableURL) { - executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, exeName, kCFURLPOSIXPathStyle, false, urlPath); + // Try finding the executable using library prefix/suffix. + CFStringRef sharedLibraryName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@%@"), _CFBundleSharedLibraryFilenamePrefix, exeName, _CFBundleSharedLibraryFilenameSuffix); + + executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, sharedLibraryName, PLATFORM_PATH_STYLE, false, urlPath); - // Try with the input name. if (!_binaryLoadable(executableURL)) { CFRelease(executableURL); - - CFStringRef sharedLibraryName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@%@"), _CFBundleSharedLibraryFilenamePrefix, exeName, _CFBundleSharedLibraryFilenameSuffix); - - executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, sharedLibraryName, kCFURLPOSIXPathStyle, false, urlPath); - - if (!_binaryLoadable(executableURL)) { - CFRelease(executableURL); - - CFStringRef sharedLibraryName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@%@"), _CFBundleExecutableFilenamePrefix, exeName, _CFBundleExecutableFilenameSuffix); - - executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, sharedLibraryName, kCFURLPOSIXPathStyle, false, urlPath); - - if (!_binaryLoadable(executableURL)) { - CFRelease(executableURL); - executableURL = NULL; - } - } + executableURL = NULL; + } + + CFRelease(sharedLibraryName); + } + + if (!executableURL) { + // Try finding the executable using executable prefix/suffix. + CFStringRef executableName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@%@"), _CFBundleExecutableFilenamePrefix, exeName, _CFBundleExecutableFilenameSuffix); + + executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, executableName, PLATFORM_PATH_STYLE, false, urlPath); + + if (!_binaryLoadable(executableURL)) { + CFRelease(executableURL); + executableURL = NULL; } + + CFRelease(executableName); } #endif @@ -137,7 +144,6 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL CFDictionaryRef infoDict = NULL; CFStringRef executablePath = NULL; CFURLRef executableURL = NULL; - Boolean foundIt = false; Boolean lookupMainExe = (executableName ? false : true); if (bundle) { @@ -155,14 +161,11 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL __CFUnlock(&bundle->_lock); if (executablePath) { executableURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, executablePath, PLATFORM_PATH_STYLE, false); - if (executableURL) { - foundIt = true; - } CFRelease(executablePath); } } - if (!foundIt) { + if (!executableURL) { if (lookupMainExe) executableName = _CFBundleCopyExecutableName(bundle, url, infoDict); if (executableName) { #if (DEPLOYMENT_TARGET_EMBEDDED && !TARGET_IPHONE_SIMULATOR) @@ -172,93 +175,109 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL #endif #if FHS_BUNDLES - if (lookupMainExe && bundle && bundle->_isFHSInstalledBundle) { - // For a FHS installed bundle, the URL points to share/Bundle.resources, and the binary is in: + if (bundle && bundle->_isFHSInstalledBundle) { - CFURLRef sharePath = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, url); - CFURLRef prefixPath = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, sharePath); - CFRelease(sharePath); - - CFStringRef directories[] = { _CFBundleFHSDirectoriesInExecutableSearchOrder }; - size_t directoriesCount = sizeof(directories) / sizeof(directories[0]); - - for (size_t i = 0; i < directoriesCount; i++) { - CFURLRef where = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, prefixPath, directories[i], true); - executableURL = _CFBundleCopyExecutableURLRaw(where, executableName); - CFRelease(where); + if (lookupMainExe) { + // For a FHS installed bundles we try to find the main executable in the different prefix locations: + CFURLRef sharePath = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, url); + CFURLRef prefixPath = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, sharePath); + CFRelease(sharePath); + + CFStringRef directories[] = { _CFBundleFHSDirectoriesInExecutableSearchOrder }; + size_t directoriesCount = sizeof(directories) / sizeof(directories[0]); - if (executableURL) { - foundIt = true; - break; + for (size_t i = 0; i < directoriesCount; i++) { + CFURLRef where = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, prefixPath, directories[i], true); + executableURL = _CFBundleCopyExecutableURLRaw(where, executableName); + CFRelease(where); } + + CFRelease(prefixPath); } - - CFRelease(prefixPath); - } -#endif // FHS_BUNDLES - - // Now, look for the executable inside the bundle. - if (!foundIt && doExecSearch && 0 != version) { - CFURLRef exeDirURL = NULL; - -#if FHS_BUNDLES - if (bundle && bundle->_isFHSInstalledBundle) { + else { + // For FHS bundles we try to find the auxiliary executables in `prefix/libexec` if. + 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); + CFURLRef exeDirURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, libexec, exeDirName, true); + executableURL = _CFBundleCopyExecutableURLRaw(exeDirURL, executableName); CFRelease(withoutExtension); CFRelease(lastPathComponent); CFRelease(libexec); CFRelease(exeDirName); - } else -#endif // FHS_BUNDLES + CFRelease(exeDirURL); + } + } + else { +#endif + + // Now, look for the executable inside the bundle. + if (!executableURL && doExecSearch && 0 != version) { + CFURLRef exeDirURL = NULL; + + // First, we find the executable directory: + if (1 == version) { exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleExecutablesURLFromBase1, url); - } else if (2 == version) { + } + else if (2 == version) { exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleExecutablesURLFromBase2, url); - } else { + } + else { + #if FREESTANDING_BUNDLES - // 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 + // For freestanding bundles if the directory has a `.resources` extension, the executable is at the same level. CFStringRef extension = CFURLCopyPathExtension(url); if (extension && CFEqual(extension, _CFBundleSiblingResourceDirectoryExtension)) { exeDirURL = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, url); - } else { - exeDirURL = (CFURLRef)CFRetain(url); } -#else - exeDirURL = (CFURLRef)CFRetain(url); #endif + + if (!exeDirURL) { + exeDirURL = (CFURLRef)CFRetain(url); + } } - - // Historical note: This used to search the directories "Mac OS X", "MacOSClassic", then "MacOS8". As of 10.13 we only look in "MacOS". - CFURLRef exeSubdirURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, _CFBundleGetPlatformExecutablesSubdirectoryName(), kCFURLPOSIXPathStyle, true, exeDirURL); - executableURL = _CFBundleCopyExecutableURLRaw(exeSubdirURL, executableName); - CFRelease(exeSubdirURL); - - if (!executableURL) executableURL = _CFBundleCopyExecutableURLRaw(exeDirURL, executableName); - CFRelease(exeDirURL); - } - - // If this was an old bundle, or we did not find the executable in the Executables subdirectory, look directly in the bundle wrapper. - if (!executableURL) executableURL = _CFBundleCopyExecutableURLRaw(url, executableName); - + + // After finding the executable directory, we search the executable: + // + // Historical note: This used to search the directories "Mac OS X", "MacOSClassic", then "MacOS8". + // As of 10.13 we only look in "MacOS". + CFURLRef exeSubdirURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, _CFBundleGetPlatformExecutablesSubdirectoryName(), PLATFORM_PATH_STYLE, true, exeDirURL); + executableURL = _CFBundleCopyExecutableURLRaw(exeSubdirURL, executableName); + CFRelease(exeSubdirURL); + + if (!executableURL) { + executableURL = _CFBundleCopyExecutableURLRaw(exeDirURL, executableName); + } + + CFRelease(exeDirURL); + + // If this was an old bundle, or we did not find the executable in the Executables subdirectory, look directly in the bundle wrapper. + if (!executableURL) { + executableURL = _CFBundleCopyExecutableURLRaw(url, executableName); + } + #if DEPLOYMENT_TARGET_WINDOWS - // Windows only: If we still haven't found the exe, look in the Executables folder. - // But only for the main bundle exe - if (lookupMainExe && !executableURL) { - CFURLRef exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, CFSTR("../../Executables"), url); - executableURL = _CFBundleCopyExecutableURLRaw(exeDirURL, executableName); - CFRelease(exeDirURL); + // Windows only: If we still haven't found the exe, look in the Executables folder. + // But only for the main bundle exe + if (lookupMainExe && !executableURL) { + CFURLRef exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, CFSTR("../../Executables"), url); + executableURL = _CFBundleCopyExecutableURLRaw(exeDirURL, executableName); + CFRelease(exeDirURL); + } +#endif + } +#if FHS_BUNDLES } #endif if (lookupMainExe && !ignoreCache && bundle && executableURL) { - // We found it. Cache the path. + // We found it. Cache the path. CFURLRef absURL = CFURLCopyAbsoluteURL(executableURL); executablePath = CFURLCopyFileSystemPath(absURL, PLATFORM_PATH_STYLE); CFRelease(absURL); @@ -267,42 +286,46 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL __CFUnlock(&bundle->_lock); CFRelease(executablePath); } + if (lookupMainExe && bundle && !executableURL) bundle->_binaryType = __CFBundleNoBinary; if (lookupMainExe) CFRelease(executableName); } } + if (!bundle && infoDict) CFRelease(infoDict); return executableURL; } -static CFStringRef _CFStringRemovePrefixSuffix(CFStringRef str, CFStringRef prefix, CFStringRef suffix) { - CFMutableStringRef mutableString = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, 0, str); - - // Check if the executable has shared library prefix/suffix. - CFRange prefixRange = CFStringFind(mutableString, - _CFBundleSharedLibraryFilenamePrefix, - kCFCompareAnchored); - CFRange suffixRange = CFStringFind(mutableString, - _CFBundleSharedLibraryFilenameSuffix, - kCFCompareAnchored|kCFCompareBackwards); +static CFStringRef _CFBundleCopyNameByRemovingExcutablePlatformPrefixSuffix(CFStringRef str, CFStringRef prefix, CFStringRef suffix) { + CFStringRef bundleName = NULL; - // Only return the stripped string if both the prefix and suffix are found. - if (prefixRange.location != kCFNotFound && - suffixRange.location != kCFNotFound && - prefixRange.location == 0 && - suffixRange.location + suffixRange.length == CFStringGetLength(mutableString)) { - CFStringReplace(mutableString, prefixRange, CFSTR("")); + // For plaforms without prefix/suffix we must return NULL so it is checked with other type of prefix/suffix. + if (!(CFEqual(prefix, CFSTR("")) && CFEqual(suffix, CFSTR("")))) { - suffixRange = CFStringFind(mutableString, - _CFBundleSharedLibraryFilenameSuffix, - kCFCompareAnchored|kCFCompareBackwards); + // Check if the executable has shared library prefix/suffix. + CFRange prefixRange = CFStringFind(str, + _CFBundleSharedLibraryFilenamePrefix, + kCFCompareAnchored); + CFRange suffixRange = CFStringFind(str, + _CFBundleSharedLibraryFilenameSuffix, + kCFCompareAnchored|kCFCompareBackwards); - CFStringReplace(mutableString, suffixRange, CFSTR("")); - return mutableString; - } - else { - return NULL; + // Only return the stripped string if both the prefix and suffix are found. + CFIndex inputStringLength = CFStringGetLength(str); + + if (prefixRange.location != kCFNotFound && + suffixRange.location != kCFNotFound && + prefixRange.location == 0 && + suffixRange.location + suffixRange.length == inputStringLength) { + + CFRange bundleNameRange = CFRangeMake(prefixRange.length, + inputStringLength - prefixRange.length - suffixRange.length); + + bundleName = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, str, bundleNameRange); + } } + + return bundleName; } static CFURLRef _CFBundleCopyBundleURLForExecutablePath(CFStringRef str) { @@ -320,38 +343,50 @@ static CFURLRef _CFBundleCopyBundleURLForExecutablePath(CFStringRef str) { #if FREESTANDING_BUNDLES if (!url) { + CFStringRef bundleName = NULL; + CFURLRef executableURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, PLATFORM_PATH_STYLE, false); CFStringRef executableName = CFURLCopyLastPathComponent(executableURL); - CFStringRef bundleName = NULL; // Check if the executable has shared library prefix/suffix (libCF.dylib, libCF.so, CF.dll). if (!bundleName) { - bundleName = _CFStringRemovePrefixSuffix(executableName, - _CFBundleSharedLibraryFilenamePrefix, - _CFBundleSharedLibraryFilenameSuffix); + bundleName = _CFBundleCopyNameByRemovingExcutablePlatformPrefixSuffix(executableName, + _CFBundleSharedLibraryFilenamePrefix, + _CFBundleSharedLibraryFilenameSuffix); } - // Check if the executable has executable-style prefix/suffix (CF, CF.exe). + // Check if the executable has executable-style prefix/suffix (CF.exe). if (!bundleName) { - bundleName = _CFStringRemovePrefixSuffix(executableName, - _CFBundleExecutableFilenamePrefix, - _CFBundleExecutableFilenameSuffix); + bundleName = _CFBundleCopyNameByRemovingExcutablePlatformPrefixSuffix(executableName, + _CFBundleExecutableFilenamePrefix, + _CFBundleExecutableFilenameSuffix); } // Otherwise, use the executable name as bundle name (CF). if (!bundleName) { - bundleName = executableName; + bundleName = CFStringCreateCopy(kCFAllocatorSystemDefault, executableName); } // Find the sibling `.resources` directory (CF.resources). CFURLRef parentDirectory = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, executableURL); + + CFRelease(executableName); + CFRelease(executableURL); + CFStringRef siblingResourceDirectoryName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@.%@"), bundleName, _CFBundleSiblingResourceDirectoryExtension); + CFRelease(bundleName); - CFURLRef siblingResourceDirectory = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, parentDirectory, siblingResourceDirectoryName, true); + CFURLRef siblingResourceDirectoryURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, parentDirectory, siblingResourceDirectoryName, true); + + CFRelease(parentDirectory); + CFRelease(siblingResourceDirectoryName); // Check that the bundle directory exists. - if (_CFURLExists(siblingResourceDirectory)) { - url = siblingResourceDirectory; + if (_CFURLExists(siblingResourceDirectoryURL)) { + url = siblingResourceDirectoryURL; + } + else { + CFRelease(siblingResourceDirectoryURL); } } #endif From 7f23670f30ba7271b08b05756fe389f7565c6f71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Jose=CC=81=20Pereira=20Vieito?= Date: Fri, 1 Jun 2018 01:17:07 +0200 Subject: [PATCH 04/10] Added test to check Bundle reverse lookup Tests ability to find bundle path from executable path --- Foundation/Bundle.swift | 14 ++++++++++++++ TestFoundation/TestBundle.swift | 33 +++++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/Foundation/Bundle.swift b/Foundation/Bundle.swift index 38b395e9cb..3df6ee24cf 100644 --- a/Foundation/Bundle.swift +++ b/Foundation/Bundle.swift @@ -67,6 +67,20 @@ open class Bundle: NSObject { _bundle = result } + internal convenience init?(executableURL: URL) { + guard let bundleURL = _CFBundleCopyBundleURLForExecutableURL(executableURL._cfObject)?.takeRetainedValue() else { + return nil + } + + self.init(url: bundleURL._swiftObject) + } + + internal convenience init?(executablePath: String) { + let executableURL = URL(fileURLWithPath: executablePath) + + self.init(executableURL: executableURL) + } + override open var description: String { return "\(String(describing: Bundle.self)) <\(bundleURL.path)> (\(isLoaded ? "loaded" : "not yet loaded"))" } diff --git a/TestFoundation/TestBundle.swift b/TestFoundation/TestBundle.swift index d5e8f5f6ea..17576b2d2e 100644 --- a/TestFoundation/TestBundle.swift +++ b/TestFoundation/TestBundle.swift @@ -9,6 +9,12 @@ import CoreFoundation +#if DEPLOYMENT_RUNTIME_OBJC || os(Linux) || os(Android) +@testable import Foundation +#else +@testable import SwiftFoundation +#endif + internal func testBundle() -> Bundle { #if DARWIN_COMPATIBILITY_TESTS for bundle in Bundle.allBundles { @@ -70,6 +76,7 @@ class BundlePlayground { let layout: Layout private(set) var bundlePath: String! + private(set) var executablePath: String! private var playgroundPath: String? init?(bundleName: String, @@ -108,7 +115,8 @@ class BundlePlayground { try FileManager.default.createDirectory(atPath: bundleURL.path, withIntermediateDirectories: false, attributes: nil) // Make a main and an auxiliary executable: - guard FileManager.default.createFile(atPath: bundleURL.appendingPathComponent(bundleName).path, contents: nil) else { + self.executablePath = bundleURL.appendingPathComponent(bundleName).path + guard FileManager.default.createFile(atPath: executablePath, contents: nil) else { return false } guard FileManager.default.createFile(atPath: bundleURL.appendingPathComponent(auxiliaryExecutableName).path, contents: nil) else { @@ -146,13 +154,14 @@ class BundlePlayground { try FileManager.default.createDirectory(atPath: temporaryDirectory.appendingPathComponent("lib").path, withIntermediateDirectories: false, attributes: nil) // Make a main and an auxiliary executable: - #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + #if canImport(Darwin) let pathExtension = "dylib" #else let pathExtension = "so" #endif - guard FileManager.default.createFile(atPath: temporaryDirectory.appendingPathComponent("lib").appendingPathComponent("lib\(bundleName).\(pathExtension)").path, contents: nil) else { return false } + self.executablePath = temporaryDirectory.appendingPathComponent("lib").appendingPathComponent("lib\(bundleName).\(pathExtension)").path + guard FileManager.default.createFile(atPath: executablePath, contents: nil) else { return false } let executablesDirectory = temporaryDirectory.appendingPathComponent("libexec").appendingPathComponent("\(bundleName).executables") try FileManager.default.createDirectory(atPath: executablesDirectory.path, withIntermediateDirectories: true, attributes: nil) @@ -187,7 +196,8 @@ class BundlePlayground { try FileManager.default.createDirectory(atPath: temporaryDirectory.path, withIntermediateDirectories: false, attributes: nil) // Make a main executable: - guard FileManager.default.createFile(atPath: temporaryDirectory.appendingPathComponent(bundleName).path, contents: nil) else { return false } + self.executablePath = temporaryDirectory.appendingPathComponent(bundleName).path + guard FileManager.default.createFile(atPath: executablePath, contents: nil) else { return false } // Make a .resources directory: let resourcesDirectory = temporaryDirectory.appendingPathComponent("\(bundleName).resources") @@ -248,6 +258,7 @@ class TestBundle : XCTestCase { ("test_bundleLoadWithError", test_bundleLoadWithError), ("test_bundleWithInvalidPath", test_bundleWithInvalidPath), ("test_bundlePreflight", test_bundlePreflight), + ("test_bundleReverseBundleLookup", test_bundleReverseBundleLookup), ("test_bundleFindExecutable", test_bundleFindExecutable), ("test_bundleFindAuxiliaryExecutables", test_bundleFindAuxiliaryExecutables), ("test_mainBundleExecutableURL", test_mainBundleExecutableURL), @@ -424,6 +435,20 @@ class TestBundle : XCTestCase { } } + func test_bundleReverseBundleLookup() { + _withEachPlaygroundLayout { (playground) in + if playground.layout == .fhsInstalled { + // Pending to be implemented. + return + } + + let bundle = Bundle(executablePath: playground.executablePath) + + XCTAssertNotNil(bundle) + XCTAssertEqual(bundle?.bundlePath, playground.bundlePath) + } + } + func test_bundleFindExecutable() { XCTAssertNotNil(testBundle().executableURL) From ad95f2bbf15d296d611fd381b8b94c39d2570b88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Jose=CC=81=20Pereira=20Vieito?= Date: Fri, 1 Jun 2018 01:17:47 +0200 Subject: [PATCH 05/10] Added note as FHS Bundle reverse lookup implementation is pending --- CoreFoundation/PlugIn.subproj/CFBundle_Executable.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c b/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c index 323843e5ac..dc38b9522a 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c +++ b/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c @@ -341,6 +341,10 @@ static CFURLRef _CFBundleCopyBundleURLForExecutablePath(CFStringRef str) { CFURLRef url = NULL; +#if FHS_BUNDLES + // Pending to be implemented. +#endif + #if FREESTANDING_BUNDLES if (!url) { CFStringRef bundleName = NULL; From 482ab99da63115dc451ddd63350f5fb27378c3e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Jose=CC=81=20Pereira=20Vieito?= Date: Sun, 3 Jun 2018 15:43:11 +0200 Subject: [PATCH 06/10] Implemented required changes --- CoreFoundation/PlugIn.subproj/CFBundle.c | 12 +- .../PlugIn.subproj/CFBundle_Executable.c | 154 ++++++++++-------- .../PlugIn.subproj/CFBundle_Internal.h | 30 ++-- TestFoundation/TestBundle.swift | 4 +- 4 files changed, 104 insertions(+), 96 deletions(-) diff --git a/CoreFoundation/PlugIn.subproj/CFBundle.c b/CoreFoundation/PlugIn.subproj/CFBundle.c index c6b9d16b1d..cfe33968df 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle.c +++ b/CoreFoundation/PlugIn.subproj/CFBundle.c @@ -137,7 +137,7 @@ static void _CFBundleEnsureBundlesExistForImagePaths(CFArrayRef imagePaths); #pragma mark - -#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS && !DEPLOYMENT_TARGET_ANDROID +#if !(DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_ANDROID || DEPLOYMENT_RUNTIME_OBJC) /* FHS_BUNDLES */ // Functions and constants for FHS bundles: #define _CFBundleFHSDirectory_share CFSTR("share") @@ -162,14 +162,14 @@ static Boolean _CFBundleURLIsForFHSInstalledBundle(CFURLRef bundleURL) { return isFHSBundle; } -#endif // !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS && !DEPLOYMENT_TARGET_ANDROID +#endif /* FHS_BUNDLES */ Boolean _CFBundleSupportsFHSBundles() { -#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS && !DEPLOYMENT_TARGET_ANDROID +#if !(DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_ANDROID || DEPLOYMENT_RUNTIME_OBJC) /* FHS_BUNDLES */ return true; #else return false; -#endif +#endif /* FHS_BUNDLES */ } CF_PRIVATE os_log_t _CFBundleResourceLogger(void) { @@ -714,9 +714,9 @@ static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL, bundle->_url = newURL; -#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS && !DEPLOYMENT_TARGET_ANDROID +#if !(DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_ANDROID || DEPLOYMENT_RUNTIME_OBJC) /* FHS_BUNDLES */ bundle->_isFHSInstalledBundle = _CFBundleURLIsForFHSInstalledBundle(newURL); -#endif +#endif /* FHS_BUNDLES */ bundle->_version = localVersion; bundle->_infoDict = NULL; diff --git a/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c b/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c index dc38b9522a..a5d18eb014 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c +++ b/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c @@ -1,7 +1,7 @@ /* CFBundle_Executable.c Copyright (c) 1999-2018, Apple Inc. and the Swift project authors - Portions Copyright (c) 2014-2017, Apple Inc. and the Swift project authors + Portions Copyright (c) 2014-2018, Apple Inc. and the Swift project authors Licensed under Apache License v2.0 with Runtime Library Exception See http://swift.org/LICENSE.txt for license information See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors @@ -15,7 +15,7 @@ #include #endif -#if FHS_BUNDLES +#if !(DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_ANDROID || DEPLOYMENT_RUNTIME_OBJC) /* FHS_BUNDLES */ #define _CFBundleFHSExecutablesDirectorySuffix CFSTR(".executables") #define _CFBundleFHSDirectoryCLiteral_libexec "libexec" @@ -45,7 +45,7 @@ _kCFBundleFHSDirectory_sbin, \ _kCFBundleFHSDirectory_lib #endif -#endif // FHS_BUNDLES +#endif /* FHS_BUNDLES */ // 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. @@ -81,12 +81,12 @@ static CFURLRef _CFBundleCopyExecutableURLRaw(CFURLRef urlPath, CFStringRef exeN imageSuffix = CFStringCreateWithCString(kCFAllocatorSystemDefault, (char *)image_suffix, kCFStringEncodingUTF8); if (CFStringHasSuffix(exeName, CFSTR(".dylib"))) { CFStringRef bareExeName = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, exeName, CFRangeMake(0, CFStringGetLength(exeName)-6)); - newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@.dylib"), bareExeName, imageSuffix); + newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@.dylib"), exeName, imageSuffix); CFRelease(bareExeName); } else { newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@"), exeName, imageSuffix); } - executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, newExeName, PLATFORM_PATH_STYLE, false, urlPath); + executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, newExeName, kCFURLPOSIXPathStyle, false, urlPath); if (executableURL && !_binaryLoadable(executableURL)) { CFRelease(executableURL); executableURL = NULL; @@ -100,16 +100,23 @@ static CFURLRef _CFBundleCopyExecutableURLRaw(CFURLRef urlPath, CFStringRef exeN // Try finding the executable with input name. executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, exeName, PLATFORM_PATH_STYLE, false, urlPath); - if (executableURL && !_binaryLoadable(executableURL)) { + if (!_binaryLoadable(executableURL)) { CFRelease(executableURL); executableURL = NULL; } } -#if FHS_BUNDLES || FREESTANDING_BUNDLES +#if !DEPLOYMENT_RUNTIME_OBJC /* FREESTANDING_BUNDLES || FHS_BUNDLES */ + +#if defined(DEBUG) && DEPLOYMENT_TARGET_WINDOWS + CFStringRef executableDebugSuffix = _CFBundleFilenameDebugSuffix; +#else + CFStringRef executableDebugSuffix = CFSTR(""); +#endif + if (!executableURL) { // Try finding the executable using library prefix/suffix. - CFStringRef sharedLibraryName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@%@"), _CFBundleSharedLibraryFilenamePrefix, exeName, _CFBundleSharedLibraryFilenameSuffix); + CFStringRef sharedLibraryName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@%@%@"), _CFBundleSharedLibraryFilenamePrefix, exeName, executableDebugSuffix, _CFBundleSharedLibraryFilenameSuffix); executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, sharedLibraryName, PLATFORM_PATH_STYLE, false, urlPath); @@ -120,11 +127,12 @@ static CFURLRef _CFBundleCopyExecutableURLRaw(CFURLRef urlPath, CFStringRef exeN CFRelease(sharedLibraryName); } - + +#if DEPLOYMENT_TARGET_WINDOWS if (!executableURL) { // Try finding the executable using executable prefix/suffix. - CFStringRef executableName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@%@"), _CFBundleExecutableFilenamePrefix, exeName, _CFBundleExecutableFilenameSuffix); - + CFStringRef sharedLibraryName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@%@%@"), _CFBundleExecutableFilenamePrefix, exeName, executableDebugSuffix, _CFBundleExecutableFilenameSuffix); + executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, executableName, PLATFORM_PATH_STYLE, false, urlPath); if (!_binaryLoadable(executableURL)) { @@ -134,7 +142,9 @@ static CFURLRef _CFBundleCopyExecutableURLRaw(CFURLRef urlPath, CFStringRef exeN CFRelease(executableName); } -#endif +#endif /* DEPLOYMENT_TARGET_WINDOWS */ + +#endif /* FREESTANDING_BUNDLES || FHS_BUNDLES */ return executableURL; } @@ -174,7 +184,7 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL Boolean doExecSearch = true; #endif -#if FHS_BUNDLES +#if !(DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_ANDROID || DEPLOYMENT_RUNTIME_OBJC) /* FHS_BUNDLES */ if (bundle && bundle->_isFHSInstalledBundle) { if (lookupMainExe) { @@ -193,9 +203,8 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL } CFRelease(prefixPath); - } - else { - // For FHS bundles we try to find the auxiliary executables in `prefix/libexec` if. + } else { + // For FHS bundles we try to find the auxiliary executables in `$(PREFIX)/libexec`. CFURLRef withoutExtension = CFURLCreateCopyDeletingPathExtension(kCFAllocatorSystemDefault, url); CFStringRef lastPathComponent = CFURLCopyLastPathComponent(withoutExtension); @@ -212,9 +221,8 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL CFRelease(exeDirName); CFRelease(exeDirURL); } - } - else { -#endif + } else { +#endif /* FHS_BUNDLES */ // Now, look for the executable inside the bundle. if (!executableURL && doExecSearch && 0 != version) { @@ -224,19 +232,17 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL if (1 == version) { exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleExecutablesURLFromBase1, url); - } - else if (2 == version) { + } else if (2 == version) { exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleExecutablesURLFromBase2, url); - } - else { + } else { -#if FREESTANDING_BUNDLES +#if !DEPLOYMENT_RUNTIME_OBJC /* FREESTANDING_BUNDLES */ // For freestanding bundles if the directory has a `.resources` extension, the executable is at the same level. CFStringRef extension = CFURLCopyPathExtension(url); if (extension && CFEqual(extension, _CFBundleSiblingResourceDirectoryExtension)) { exeDirURL = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, url); } -#endif +#endif /* FREESTANDING_BUNDLES */ if (!exeDirURL) { exeDirURL = (CFURLRef)CFRetain(url); @@ -256,25 +262,26 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL } CFRelease(exeDirURL); + } - // If this was an old bundle, or we did not find the executable in the Executables subdirectory, look directly in the bundle wrapper. - if (!executableURL) { - executableURL = _CFBundleCopyExecutableURLRaw(url, executableName); - } + // If this was an old bundle, or we did not find the executable in the Executables subdirectory, look directly in the bundle wrapper. + if (!executableURL) { + executableURL = _CFBundleCopyExecutableURLRaw(url, executableName); + } #if DEPLOYMENT_TARGET_WINDOWS - // Windows only: If we still haven't found the exe, look in the Executables folder. - // But only for the main bundle exe - if (lookupMainExe && !executableURL) { - CFURLRef exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, CFSTR("../../Executables"), url); - executableURL = _CFBundleCopyExecutableURLRaw(exeDirURL, executableName); - CFRelease(exeDirURL); - } -#endif + // Windows only: If we still haven't found the exe, look in the Executables folder. + // But only for the main bundle exe + if (lookupMainExe && !executableURL) { + CFURLRef exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, CFSTR("../../Executables"), url); + executableURL = _CFBundleCopyExecutableURLRaw(exeDirURL, executableName); + CFRelease(exeDirURL); } -#if FHS_BUNDLES +#endif /* DEPLOYMENT_TARGET_WINDOWS */ + +#if !(DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_ANDROID || DEPLOYMENT_RUNTIME_OBJC) /* FHS_BUNDLES */ } -#endif +#endif /* FHS_BUNDLES */ if (lookupMainExe && !ignoreCache && bundle && executableURL) { // We found it. Cache the path. @@ -299,30 +306,24 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL static CFStringRef _CFBundleCopyNameByRemovingExcutablePlatformPrefixSuffix(CFStringRef str, CFStringRef prefix, CFStringRef suffix) { CFStringRef bundleName = NULL; - // For plaforms without prefix/suffix we must return NULL so it is checked with other type of prefix/suffix. - if (!(CFEqual(prefix, CFSTR("")) && CFEqual(suffix, CFSTR("")))) { - - // Check if the executable has shared library prefix/suffix. - CFRange prefixRange = CFStringFind(str, - _CFBundleSharedLibraryFilenamePrefix, - kCFCompareAnchored); - CFRange suffixRange = CFStringFind(str, - _CFBundleSharedLibraryFilenameSuffix, - kCFCompareAnchored|kCFCompareBackwards); + // Check if the executable has shared library prefix/suffix. + CFRange prefixRange = CFStringFind(str, prefix, + kCFCompareAnchored); + CFRange suffixRange = CFStringFind(str, suffix, + kCFCompareAnchored|kCFCompareBackwards); + + // Only return the stripped string if both the prefix and suffix are found. + CFIndex inputStringLength = CFStringGetLength(str); + + if (prefixRange.location != kCFNotFound && + suffixRange.location != kCFNotFound && + prefixRange.location == 0 && + suffixRange.location + suffixRange.length == inputStringLength) { - // Only return the stripped string if both the prefix and suffix are found. - CFIndex inputStringLength = CFStringGetLength(str); + CFRange bundleNameRange = CFRangeMake(prefixRange.length, + inputStringLength - prefixRange.length - suffixRange.length); - if (prefixRange.location != kCFNotFound && - suffixRange.location != kCFNotFound && - prefixRange.location == 0 && - suffixRange.location + suffixRange.length == inputStringLength) { - - CFRange bundleNameRange = CFRangeMake(prefixRange.length, - inputStringLength - prefixRange.length - suffixRange.length); - - bundleName = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, str, bundleNameRange); - } + bundleName = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, str, bundleNameRange); } return bundleName; @@ -332,7 +333,7 @@ static CFURLRef _CFBundleCopyBundleURLForExecutablePath(CFStringRef str) { // Needs to handle: // - Classic NEXTStep-style bundles (`.framework`, `.app`, `.appex`) // - Freestanding bundles (`Bundle.resource` as sibling of the library/executable) - // - FHS bundles (`prefix/share/Bundle.resource`) + // - FHS bundles (`$(PREFIX)/share/Bundle.resource`) // // Note: // For freestanding and FHS bundles, this function has to support removing library/executable prefix/suffix @@ -341,11 +342,11 @@ static CFURLRef _CFBundleCopyBundleURLForExecutablePath(CFStringRef str) { CFURLRef url = NULL; -#if FHS_BUNDLES - // Pending to be implemented. -#endif +#if !(DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_ANDROID || DEPLOYMENT_RUNTIME_OBJC) /* FHS_BUNDLES */ + // TODO: FHS Bundles reverse lookup pending to be implemented. +#endif /* FHS_BUNDLES */ -#if FREESTANDING_BUNDLES +#if !DEPLOYMENT_RUNTIME_OBJC /* FREESTANDING_BUNDLES */ if (!url) { CFStringRef bundleName = NULL; @@ -359,18 +360,34 @@ static CFURLRef _CFBundleCopyBundleURLForExecutablePath(CFStringRef str) { _CFBundleSharedLibraryFilenameSuffix); } +#if DEPLOYMENT_TARGET_WINDOWS // Check if the executable has executable-style prefix/suffix (CF.exe). if (!bundleName) { bundleName = _CFBundleCopyNameByRemovingExcutablePlatformPrefixSuffix(executableName, _CFBundleExecutableFilenamePrefix, _CFBundleExecutableFilenameSuffix); } +#endif /* DEPLOYMENT_TARGET_WINDOWS */ // Otherwise, use the executable name as bundle name (CF). if (!bundleName) { bundleName = CFStringCreateCopy(kCFAllocatorSystemDefault, executableName); } +#if DEPLOYMENT_TARGET_WINDOWS + // Windows: If this is a debug executable, strip the _debug suffix to find the bundle name. + if (bundleName) { + CFStringRef strippedName = _CFBundleCopyNameByRemovingExcutablePlatformPrefixSuffix(bundleName, + _CFBundleFilenameDebugPrefix, + _CFBundleFilenameDebugSuffix); + + if (strippedName) { + bundleName = CFStringCreateCopy(kCFAllocatorSystemDefault, strippedName); + CFRelease(strippedName); + } + } +#endif /* DEPLOYMENT_TARGET_WINDOWS */ + // Find the sibling `.resources` directory (CF.resources). CFURLRef parentDirectory = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, executableURL); @@ -388,12 +405,11 @@ static CFURLRef _CFBundleCopyBundleURLForExecutablePath(CFStringRef str) { // Check that the bundle directory exists. if (_CFURLExists(siblingResourceDirectoryURL)) { url = siblingResourceDirectoryURL; - } - else { + } else { CFRelease(siblingResourceDirectoryURL); } } -#endif +#endif /* FREESTANDING_BUNDLES */ if (!url) { UniChar buff[CFMaxPathSize]; diff --git a/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h b/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h index fd8cd54722..4034fb058e 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h +++ b/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h @@ -30,42 +30,32 @@ CF_EXTERN_C_BEGIN #define PLATFORM_PATH_STYLE kCFURLPOSIXPathStyle #endif -// Freestanding bundles are not supported on Darwin. -#if !DEPLOYMENT_RUNTIME_OBJC -#define FREESTANDING_BUNDLES 1 -#else -#define FREESTANDING_BUNDLES 0 -#endif +// Freestanding bundles are not supported with the Objective-C Runtime. +// !DEPLOYMENT_RUNTIME_OBJC /* FREESTANDING_BUNDLES */ -// FHS bundles are not supported on Darwin, Windows and Android. -#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS && !DEPLOYMENT_TARGET_ANDROID -#define FHS_BUNDLES 1 -#else -#define FHS_BUNDLES 0 -#endif +// FHS bundles are not supported on Windows and Android or with the Objective-C Runtime. +// !(DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_ANDROID || DEPLOYMENT_RUNTIME_OBJC) /* FHS_BUNDLES */ -#if FHS_BUNDLES || FREESTANDING_BUNDLES +#if !DEPLOYMENT_RUNTIME_OBJC /* FREESTANDING_BUNDLES || FHS_BUNDLES */ #if DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD #define _CFBundleSharedLibraryFilenamePrefix CFSTR("lib") #define _CFBundleSharedLibraryFilenameSuffix CFSTR(".so") - #define _CFBundleExecutableFilenamePrefix CFSTR("") - #define _CFBundleExecutableFilenameSuffix CFSTR("") #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI #define _CFBundleSharedLibraryFilenamePrefix CFSTR("lib") #define _CFBundleSharedLibraryFilenameSuffix CFSTR(".dylib") - #define _CFBundleExecutableFilenamePrefix CFSTR("") - #define _CFBundleExecutableFilenameSuffix CFSTR("") #elif DEPLOYMENT_TARGET_WINDOWS #define _CFBundleSharedLibraryFilenamePrefix CFSTR("") #define _CFBundleSharedLibraryFilenameSuffix CFSTR(".dll") #define _CFBundleExecutableFilenamePrefix CFSTR("") #define _CFBundleExecutableFilenameSuffix CFSTR(".exe") + #define _CFBundleFilenameDebugPrefix CFSTR("") + #define _CFBundleFilenameDebugSuffix CFSTR("_debug") #else // a non-covered DEPLOYMENT_TARGET… - #error Disable Freestanding / FHS bundles or specify shared library prefixes and suffixes for this platform. + #error Disable Freestanding and FHS bundles or specify shared library / executable prefixes and suffixes for this platform. #endif -#endif +#endif /* FREESTANDING_BUNDLES || FHS_BUNDLES */ #define CFBundleExecutableNotFoundError 4 #define CFBundleExecutableNotLoadableError 3584 @@ -99,7 +89,7 @@ struct __CFBundle { CFURLRef _url; -#if !DEPLOYMENT_RUNTIME_OBJC && !DEPLOYMENT_TARGET_WINDOWS +#if !(DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_ANDROID || DEPLOYMENT_RUNTIME_OBJC) /* FHS_BUNDLES */ Boolean _isFHSInstalledBundle; #endif diff --git a/TestFoundation/TestBundle.swift b/TestFoundation/TestBundle.swift index 17576b2d2e..c42593a879 100644 --- a/TestFoundation/TestBundle.swift +++ b/TestFoundation/TestBundle.swift @@ -438,7 +438,9 @@ class TestBundle : XCTestCase { func test_bundleReverseBundleLookup() { _withEachPlaygroundLayout { (playground) in if playground.layout == .fhsInstalled { - // Pending to be implemented. + // TODO: FHS Bundles reverse lookup pending to be implemented. + // + // Implementation required at `_CFBundleCopyBundleURLForExecutablePath()` on Core Foundation. return } From 8327149a7d1263d63096e38ec48e15f6dc7ca3bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Jose=CC=81=20Pereira=20Vieito?= Date: Sun, 3 Jun 2018 15:47:35 +0200 Subject: [PATCH 07/10] Used guard instead of if --- TestFoundation/TestBundle.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TestFoundation/TestBundle.swift b/TestFoundation/TestBundle.swift index c42593a879..4481fa2417 100644 --- a/TestFoundation/TestBundle.swift +++ b/TestFoundation/TestBundle.swift @@ -437,7 +437,7 @@ class TestBundle : XCTestCase { func test_bundleReverseBundleLookup() { _withEachPlaygroundLayout { (playground) in - if playground.layout == .fhsInstalled { + guard playground.layout != .fhsInstalled else { // TODO: FHS Bundles reverse lookup pending to be implemented. // // Implementation required at `_CFBundleCopyBundleURLForExecutablePath()` on Core Foundation. From f88a812792167385aa9c485dc47b0c82e1229465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Jose=CC=81=20Pereira=20Vieito?= Date: Wed, 13 Jun 2018 23:05:08 +0200 Subject: [PATCH 08/10] Guarded test which require @testable import of Foundation with NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT --- TestFoundation/TestBundle.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TestFoundation/TestBundle.swift b/TestFoundation/TestBundle.swift index 4481fa2417..4810801cb9 100644 --- a/TestFoundation/TestBundle.swift +++ b/TestFoundation/TestBundle.swift @@ -9,11 +9,13 @@ import CoreFoundation +#if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT #if DEPLOYMENT_RUNTIME_OBJC || os(Linux) || os(Android) @testable import Foundation #else @testable import SwiftFoundation #endif +#endif internal func testBundle() -> Bundle { #if DARWIN_COMPATIBILITY_TESTS @@ -444,10 +446,12 @@ class TestBundle : XCTestCase { return } + #if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT let bundle = Bundle(executablePath: playground.executablePath) XCTAssertNotNil(bundle) XCTAssertEqual(bundle?.bundlePath, playground.bundlePath) + #endif } } From 4ca3eb0b115f33c8f4118bed1ac9be26d18e9173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Jose=CC=81=20Pereira=20Vieito?= Date: Sun, 14 Oct 2018 16:33:50 +0200 Subject: [PATCH 09/10] Updated conditional target naming --- .../PlugIn.subproj/CFBundle_Executable.c | 28 +++++++++---------- .../PlugIn.subproj/CFBundle_Internal.h | 8 +++--- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c b/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c index a5d18eb014..fe06797545 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c +++ b/CoreFoundation/PlugIn.subproj/CFBundle_Executable.c @@ -15,7 +15,7 @@ #include #endif -#if !(DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_ANDROID || DEPLOYMENT_RUNTIME_OBJC) /* FHS_BUNDLES */ +#if !(DEPLOYMENT_RUNTIME_OBJC || TARGET_OS_WINDOWS || TARGET_OS_ANDROID) /* FHS_BUNDLES */ #define _CFBundleFHSExecutablesDirectorySuffix CFSTR(".executables") #define _CFBundleFHSDirectoryCLiteral_libexec "libexec" @@ -108,7 +108,7 @@ static CFURLRef _CFBundleCopyExecutableURLRaw(CFURLRef urlPath, CFStringRef exeN #if !DEPLOYMENT_RUNTIME_OBJC /* FREESTANDING_BUNDLES || FHS_BUNDLES */ -#if defined(DEBUG) && DEPLOYMENT_TARGET_WINDOWS +#if defined(DEBUG) && TARGET_OS_WINDOWS CFStringRef executableDebugSuffix = _CFBundleFilenameDebugSuffix; #else CFStringRef executableDebugSuffix = CFSTR(""); @@ -128,7 +128,7 @@ static CFURLRef _CFBundleCopyExecutableURLRaw(CFURLRef urlPath, CFStringRef exeN CFRelease(sharedLibraryName); } -#if DEPLOYMENT_TARGET_WINDOWS +#if TARGET_OS_WINDOWS if (!executableURL) { // Try finding the executable using executable prefix/suffix. CFStringRef sharedLibraryName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@%@%@"), _CFBundleExecutableFilenamePrefix, exeName, executableDebugSuffix, _CFBundleExecutableFilenameSuffix); @@ -142,7 +142,7 @@ static CFURLRef _CFBundleCopyExecutableURLRaw(CFURLRef urlPath, CFStringRef exeN CFRelease(executableName); } -#endif /* DEPLOYMENT_TARGET_WINDOWS */ +#endif /* TARGET_OS_WINDOWS */ #endif /* FREESTANDING_BUNDLES || FHS_BUNDLES */ @@ -184,7 +184,7 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL Boolean doExecSearch = true; #endif -#if !(DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_ANDROID || DEPLOYMENT_RUNTIME_OBJC) /* FHS_BUNDLES */ +#if !(DEPLOYMENT_RUNTIME_OBJC || TARGET_OS_WINDOWS || TARGET_OS_ANDROID) /* FHS_BUNDLES */ if (bundle && bundle->_isFHSInstalledBundle) { if (lookupMainExe) { @@ -269,7 +269,7 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL executableURL = _CFBundleCopyExecutableURLRaw(url, executableName); } -#if DEPLOYMENT_TARGET_WINDOWS +#if TARGET_OS_WINDOWS // Windows only: If we still haven't found the exe, look in the Executables folder. // But only for the main bundle exe if (lookupMainExe && !executableURL) { @@ -277,9 +277,9 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURL executableURL = _CFBundleCopyExecutableURLRaw(exeDirURL, executableName); CFRelease(exeDirURL); } -#endif /* DEPLOYMENT_TARGET_WINDOWS */ +#endif /* TARGET_OS_WINDOWS */ -#if !(DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_ANDROID || DEPLOYMENT_RUNTIME_OBJC) /* FHS_BUNDLES */ +#if !(DEPLOYMENT_RUNTIME_OBJC || TARGET_OS_WINDOWS || TARGET_OS_ANDROID) /* FHS_BUNDLES */ } #endif /* FHS_BUNDLES */ @@ -342,7 +342,7 @@ static CFURLRef _CFBundleCopyBundleURLForExecutablePath(CFStringRef str) { CFURLRef url = NULL; -#if !(DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_ANDROID || DEPLOYMENT_RUNTIME_OBJC) /* FHS_BUNDLES */ +#if !(DEPLOYMENT_RUNTIME_OBJC || TARGET_OS_WINDOWS || TARGET_OS_ANDROID) /* FHS_BUNDLES */ // TODO: FHS Bundles reverse lookup pending to be implemented. #endif /* FHS_BUNDLES */ @@ -360,21 +360,21 @@ static CFURLRef _CFBundleCopyBundleURLForExecutablePath(CFStringRef str) { _CFBundleSharedLibraryFilenameSuffix); } -#if DEPLOYMENT_TARGET_WINDOWS +#if TARGET_OS_WINDOWS // Check if the executable has executable-style prefix/suffix (CF.exe). if (!bundleName) { bundleName = _CFBundleCopyNameByRemovingExcutablePlatformPrefixSuffix(executableName, _CFBundleExecutableFilenamePrefix, _CFBundleExecutableFilenameSuffix); } -#endif /* DEPLOYMENT_TARGET_WINDOWS */ +#endif /* TARGET_OS_WINDOWS */ // Otherwise, use the executable name as bundle name (CF). if (!bundleName) { bundleName = CFStringCreateCopy(kCFAllocatorSystemDefault, executableName); } -#if DEPLOYMENT_TARGET_WINDOWS +#if TARGET_OS_WINDOWS // Windows: If this is a debug executable, strip the _debug suffix to find the bundle name. if (bundleName) { CFStringRef strippedName = _CFBundleCopyNameByRemovingExcutablePlatformPrefixSuffix(bundleName, @@ -386,7 +386,7 @@ static CFURLRef _CFBundleCopyBundleURLForExecutablePath(CFStringRef str) { CFRelease(strippedName); } } -#endif /* DEPLOYMENT_TARGET_WINDOWS */ +#endif /* TARGET_OS_WINDOWS */ // Find the sibling `.resources` directory (CF.resources). CFURLRef parentDirectory = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, executableURL); @@ -478,7 +478,7 @@ static CFURLRef _CFBundleCopyResolvedURLForExecutableURL(CFURLRef url) { str1 = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, buff, len1); CFIndex skipSlashCount = 1; -#if DEPLOYMENT_TARGET_WINDOWS +#if TARGET_OS_WINDOWS // On Windows, _CFLengthAfterDeletingLastPathComponent will return a value of 3 if the path is at the root (e.g. C:\). This includes the \, which is not the case for URLs with subdirectories if (len1 == 3 && buff[1] == ':' && buff[2] == '\\') { skipSlashCount = 0; diff --git a/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h b/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h index d5360dec47..c4222de15f 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h +++ b/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h @@ -33,18 +33,18 @@ CF_EXTERN_C_BEGIN // Freestanding bundles are not supported with the Objective-C Runtime. // !DEPLOYMENT_RUNTIME_OBJC /* FREESTANDING_BUNDLES */ -// FHS bundles are not supported on Windows and Android or with the Objective-C Runtime. -// !(DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_ANDROID || DEPLOYMENT_RUNTIME_OBJC) /* FHS_BUNDLES */ +// FHS bundles are not supported with the Objective-C Runtime nor on Windows or Android. +// !(DEPLOYMENT_RUNTIME_OBJC || TARGET_OS_WINDOWS || TARGET_OS_ANDROID) /* FHS_BUNDLES */ #if !DEPLOYMENT_RUNTIME_OBJC /* FREESTANDING_BUNDLES || FHS_BUNDLES */ #if TARGET_OS_LINUX || TARGET_OS_BSD #define _CFBundleSharedLibraryFilenamePrefix CFSTR("lib") #define _CFBundleSharedLibraryFilenameSuffix CFSTR(".so") -#elif TARGET_OS_MAC +#elif TARGET_OS_DARWIN #define _CFBundleSharedLibraryFilenamePrefix CFSTR("lib") #define _CFBundleSharedLibraryFilenameSuffix CFSTR(".dylib") -#elif TARGET_OS_WIN32 +#elif TARGET_OS_WINDOWS #define _CFBundleSharedLibraryFilenamePrefix CFSTR("") #define _CFBundleSharedLibraryFilenameSuffix CFSTR(".dll") #define _CFBundleExecutableFilenamePrefix CFSTR("") From a8be484b74e85fda9bcd159d0e761f708152cc90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Jose=CC=81=20Pereira=20Vieito?= Date: Sun, 14 Oct 2018 16:37:42 +0200 Subject: [PATCH 10/10] Updated conditional target naming --- CoreFoundation/PlugIn.subproj/CFBundle.c | 6 +++--- CoreFoundation/PlugIn.subproj/CFBundle_Internal.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CoreFoundation/PlugIn.subproj/CFBundle.c b/CoreFoundation/PlugIn.subproj/CFBundle.c index fc74b3df19..5380e96655 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle.c +++ b/CoreFoundation/PlugIn.subproj/CFBundle.c @@ -135,7 +135,7 @@ static void _CFBundleEnsureBundlesExistForImagePaths(CFArrayRef imagePaths); #pragma mark - -#if !(TARGET_OS_WIN32 || TARGET_OS_ANDROID || DEPLOYMENT_RUNTIME_OBJC) /* FHS_BUNDLES */ +#if !(DEPLOYMENT_RUNTIME_OBJC || TARGET_OS_WINDOWS || TARGET_OS_ANDROID) /* FHS_BUNDLES */ // Functions and constants for FHS bundles: #define _CFBundleFHSDirectory_share CFSTR("share") @@ -163,7 +163,7 @@ static Boolean _CFBundleURLIsForFHSInstalledBundle(CFURLRef bundleURL) { #endif /* FHS_BUNDLES */ CF_CROSS_PLATFORM_EXPORT Boolean _CFBundleSupportsFHSBundles() { -#if !(TARGET_OS_WIN32 || TARGET_OS_ANDROID || DEPLOYMENT_RUNTIME_OBJC) /* FHS_BUNDLES */ +#if !(DEPLOYMENT_RUNTIME_OBJC || TARGET_OS_WINDOWS || TARGET_OS_ANDROID) /* FHS_BUNDLES */ return true; #else return false; @@ -726,7 +726,7 @@ static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL, bundle->_url = newURL; -#if !(TARGET_OS_WIN32 || TARGET_OS_ANDROID || DEPLOYMENT_RUNTIME_OBJC) /* FHS_BUNDLES */ +#if !(DEPLOYMENT_RUNTIME_OBJC || TARGET_OS_WINDOWS || TARGET_OS_ANDROID) /* FHS_BUNDLES */ bundle->_isFHSInstalledBundle = _CFBundleURLIsForFHSInstalledBundle(newURL); #endif /* FHS_BUNDLES */ diff --git a/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h b/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h index c4222de15f..437e82e318 100644 --- a/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h +++ b/CoreFoundation/PlugIn.subproj/CFBundle_Internal.h @@ -92,7 +92,7 @@ struct __CFBundle { CFURLRef _url; -#if !(TARGET_OS_WIN32 || TARGET_OS_ANDROID || DEPLOYMENT_RUNTIME_OBJC) /* FHS_BUNDLES */ +#if !(DEPLOYMENT_RUNTIME_OBJC || TARGET_OS_WINDOWS || TARGET_OS_ANDROID) /* FHS_BUNDLES */ Boolean _isFHSInstalledBundle; #endif