diff --git a/CMakeLists.txt b/CMakeLists.txt index 97fb50ccdee..d53732ee4ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,34 +27,14 @@ include(ExternalProject) string(TOLOWER ${CMAKE_SYSTEM_NAME} swift_os) get_swift_host_arch(swift_arch) -ExternalProject_Add(CoreFoundation - SOURCE_DIR - ${CMAKE_CURRENT_SOURCE_DIR}/CoreFoundation - CMAKE_COMMAND - ${CMAKE_COMMAND} - CMAKE_ARGS - -DBUILD_SHARED_LIBS=NO - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} - -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} - -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} - -DCMAKE_INSTALL_PREFIX= - -DCMAKE_INSTALL_LIBDIR=usr/lib - -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} - -DCMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR} - -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} - -DCF_DEPLOYMENT_SWIFT=YES - -DCF_ENABLE_LIBDISPATCH=${FOUNDATION_ENABLE_LIBDISPATCH} - -DCF_PATH_TO_LIBDISPATCH_SOURCE=${FOUNDATION_PATH_TO_LIBDISPATCH_SOURCE} - -DCF_PATH_TO_LIBDISPATCH_BUILD=${FOUNDATION_PATH_TO_LIBDISPATCH_BUILD} - -DICU_LIBRARY=${ICU_LIBRARY} - -DICU_INCLUDE_DIR=${ICU_INCLUDE_DIR} - -DCURL_LIBRARY=${CURL_LIBRARY} - -DCURL_INCLUDE_DIR=${CURL_INCLUDE_DIR} - -DLIBXML2_LIBRARY=${LIBXML2_LIBRARY} - -DLIBXML2_INCLUDE_DIR=${LIBXML2_INCLUDE_DIR} - INSTALL_COMMAND - ${CMAKE_COMMAND} -E env --unset=DESTDIR ${CMAKE_COMMAND} --build . --target install) -ExternalProject_Get_Property(CoreFoundation install_dir) +set(CF_PATH_TO_LIBDISPATCH_SOURCE ${FOUNDATION_PATH_TO_LIBDISPATCH_SOURCE} CACHE PATH "Path to libdispatch source" FORCE) +set(CF_PATH_TO_LIBDISPATCH_BUILD ${FOUNDATION_PATH_TO_LIBDISPATCH_BUILD} CACHE PATH "Path to libdispatch build" FORCE) +set(CF_DEPLOYMENT_SWIFT YES CACHE BOOL "Build for Swift" FORCE) + +set(SAVED_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) +set(BUILD_SHARED_LIBS NO) +add_subdirectory(CoreFoundation) +set(BUILD_SHARED_LIBS ${SAVED_BUILD_SHARED_LIBS}) add_library(uuid STATIC @@ -67,13 +47,14 @@ set_target_properties(uuid # the dependency on TargetConditionals.h target_compile_options(uuid PUBLIC - -I${install_dir}/System/Library/Frameworks/CoreFoundation.framework/Headers) + -I${CMAKE_CURRENT_BINARY_DIR}/CoreFoundation.framework/Headers) if(CMAKE_SYSTEM_NAME STREQUAL Windows) target_compile_definitions(uuid PRIVATE _CRT_NONSTDC_NO_WARNINGS _CRT_SECURE_NO_DEPRECATE _CRT_SECURE_NO_WARNINGS) + target_link_libraries(uuid PRIVATE Bcrypt) endif() add_dependencies(uuid CoreFoundation) @@ -107,32 +88,24 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL FreeBSD) set(Foundation_RPATH -Xlinker;-rpath;-Xlinker;"\\\$\$ORIGIN") elseif(CMAKE_SYSTEM_NAME STREQUAL Windows) set(deployment_target -DDEPLOYMENT_TARGET_WINDOWS) - # FIXME(compnerd) these are not all CoreFoundation dependencies, some of them - # are Foundation's and others are libcurl's. We should split them up - # accordingly. - set(CoreFoundation_INTERFACE_LIBRARIES - -lAdvAPI32 - -lCrypt32 - -lDbgHelp - -lShell32 - -lOle32 - -lRpcRT4 - -lSecur32 - -lShLwApi - -lUser32 - -lWldap32 - -lWS2_32 - -liphlpapi - -lmincore - -lnormaliz - -lpathcch - -lucrt - -lshell32) # FIXME(SR9138) Silence "locally defined symbol '…' imported in function '…' set(WORKAROUND_SR9138 -Xlinker;-ignore:4217) set(WORKAROUND_SR9995 -Xlinker;-nodefaultlib:libcmt) endif() +# NOTE(compnerd) this is a horrible hack to work around the fact that we do not +# have a proper library target for Foundation which can link against the +# CoreFoundation target. When we gain proper CMake support for Swift, we should +# be able to remove this and just use +# `target_link_libraries(Foundation PRIVATE CoreFoundation)`. +set(CoreFoundation_LIBRARIES $) +get_target_property(CoreFoundation_LINK_LIBRARIES CoreFoundation LINK_LIBRARIES) +foreach(library ${CoreFoundation_LINK_LIBRARIES}) + if(NOT library STREQUAL Threads::Threads) + list(APPEND CoreFoundation_LIBRARIES -l${library}) + endif() +endforeach() + add_swift_library(Foundation MODULE_NAME Foundation @@ -308,21 +281,25 @@ add_swift_library(Foundation CFLAGS ${deployment_target} ${deployment_enable_libdispatch} - -F${install_dir}/System/Library/Frameworks + -F${CMAKE_CURRENT_BINARY_DIR} -D_DLL LINK_FLAGS - -L${install_dir}/usr/lib - -lCoreFoundation + ${CoreFoundation_LIBRARIES} ${CURL_LIBRARIES} ${ICU_UC_LIBRARY} ${ICU_I18N_LIBRARY} ${LIBXML2_LIBRARIES} ${libdispatch_ldflags} - -L${CMAKE_CURRENT_BINARY_DIR} - -luuid + $ ${Foundation_RPATH} - ${CoreFoundation_INTERFACE_LIBRARIES} ${WORKAROUND_SR9138} ${WORKAROUND_SR9995} + $<$:-lDbgHelp> + $<$:-lOle32> + $<$:-lShLwApi> + $<$:-lShell32> + $<$:-lWS2_32> + $<$:-liphlpapi> + $<$:-lpathcch> SWIFT_FLAGS -DDEPLOYMENT_RUNTIME_SWIFT ${deployment_enable_libdispatch} @@ -352,7 +329,7 @@ add_swift_executable(plutil CFLAGS ${deployment_target} ${deployment_enable_libdispatch} - -F${install_dir}/System/Library/Frameworks + -F${CMAKE_CURRENT_BINARY_DIR} LINK_FLAGS ${libdispatch_ldflags} -L${CMAKE_CURRENT_BINARY_DIR} @@ -378,7 +355,7 @@ if(ENABLE_TESTING) CFLAGS ${deployment_target} ${deployment_enable_libdispatch} - -F${install_dir}/System/Library/Frameworks + -F${CMAKE_CURRENT_BINARY_DIR} LINK_FLAGS ${libdispatch_ldflags} -L${CMAKE_CURRENT_BINARY_DIR} @@ -424,7 +401,6 @@ if(ENABLE_TESTING) TestFoundation/TestIndexSet.swift TestFoundation/TestISO8601DateFormatter.swift TestFoundation/TestJSONEncoder.swift - TestFoundation/TestPropertyListEncoder.swift TestFoundation/TestJSONSerialization.swift TestFoundation/TestLengthFormatter.swift TestFoundation/TestMassFormatter.swift @@ -449,7 +425,6 @@ if(ENABLE_TESTING) TestFoundation/TestNSNumber.swift TestFoundation/TestNSOrderedSet.swift TestFoundation/TestNSPredicate.swift - TestFoundation/TestNSProgressFraction.swift TestFoundation/TestNSRange.swift TestFoundation/TestNSRegularExpression.swift TestFoundation/TestNSSet.swift @@ -466,6 +441,8 @@ if(ENABLE_TESTING) TestFoundation/TestProcessInfo.swift TestFoundation/TestProcess.swift TestFoundation/TestProgress.swift + TestFoundation/TestProgressFraction.swift + TestFoundation/TestPropertyListEncoder.swift TestFoundation/TestPropertyListSerialization.swift TestFoundation/TestRunLoop.swift TestFoundation/TestScanner.swift @@ -490,7 +467,7 @@ if(ENABLE_TESTING) CFLAGS ${deployment_target} ${deployment_enable_libdispatch} - -F${install_dir}/System/Library/Frameworks + -F${CMAKE_CURRENT_BINARY_DIR} LINK_FLAGS ${libdispatch_ldflags} -L${CMAKE_CURRENT_BINARY_DIR} @@ -576,7 +553,7 @@ else() endif() # TODO(compnerd) install as a Framework as that is how swift actually is built install(DIRECTORY - ${install_dir}/System/Library/Frameworks/CoreFoundation.framework/Headers/ + ${CMAKE_CURRENT_BINARY_DIR}/CoreFoundation.framework/Headers/ DESTINATION lib/swift/CoreFoundation FILES_MATCHING PATTERN "*.h") @@ -585,6 +562,6 @@ install(FILES DESTINATION lib/swift/CoreFoundation) install(PROGRAMS - ${CMAKE_CURRENT_BINARY_DIR}/plutil + ${CMAKE_CURRENT_BINARY_DIR}/plutil${CMAKE_EXECUTABLE_SUFFIX} DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) diff --git a/CoreFoundation/Base.subproj/CFFileUtilities.c b/CoreFoundation/Base.subproj/CFFileUtilities.c index dba76490d12..5db9dd33979 100644 --- a/CoreFoundation/Base.subproj/CFFileUtilities.c +++ b/CoreFoundation/Base.subproj/CFFileUtilities.c @@ -1024,66 +1024,48 @@ CF_PRIVATE void _CFIterateDirectory(CFStringRef directoryPath, Boolean appendSla if (!CFStringGetFileSystemRepresentation(directoryPath, directoryPathBuf, CFMaxPathSize)) return; #if DEPLOYMENT_TARGET_WINDOWS - CFIndex cpathLen = strlen(directoryPathBuf); // Make sure there is room for the additional space we need in the win32 api - if (cpathLen + 2 < CFMaxPathSize) { - WIN32_FIND_DATAW file; - HANDLE handle; - - directoryPathBuf[cpathLen++] = '\\'; - directoryPathBuf[cpathLen++] = '*'; - directoryPathBuf[cpathLen] = '\0'; - - // Convert UTF8 buffer to windows appropriate UTF-16LE - // Get the real length of the string in UTF16 characters - CFStringRef cfStr = CFStringCreateWithCString(kCFAllocatorSystemDefault, directoryPathBuf, kCFStringEncodingUTF8); - cpathLen = CFStringGetLength(cfStr); - // Allocate a wide buffer to hold the converted string, including space for a NULL terminator - wchar_t *wideBuf = (wchar_t *)malloc((cpathLen + 1) * sizeof(wchar_t)); - // Copy the string into the buffer and terminate - CFStringGetCharacters(cfStr, CFRangeMake(0, cpathLen), (UniChar *)wideBuf); - wideBuf[cpathLen] = 0; - CFRelease(cfStr); - - WCHAR directory[MAX_PATH + 1]; - if (!CFStringGetCString(directoryPath, directory, MAX_PATH, kCFStringEncodingUTF16)) - return; - - handle = FindFirstFileW(wideBuf, (LPWIN32_FIND_DATAW)&file); - if (handle != INVALID_HANDLE_VALUE) { - WCHAR path[MAX_PATH + 1]; - - do { - CFIndex nameLen = wcslen(file.cFileName); - if (file.cFileName[0] == '.' && (nameLen == 1 || (nameLen == 2 && file.cFileName[1] == '.'))) { - continue; - } + if (strlen(directoryPathBuf) > CFMaxPathSize - 2) return; - if (!PathCombineW(path, directory, file.cFileName)) { - continue; - } + strlcat(directoryPathBuf, "\\*", CFMaxPathSize); - CFStringRef filePath = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const uint8_t *)path, wcslen(path) * sizeof(WCHAR), kCFStringEncodingUTF16, NO); - if (!filePath) { - continue; - } + UniChar wideBuf[CFMaxPathSize]; - CFStringRef fileName = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const uint8_t *)file.cFileName, nameLen * sizeof(wchar_t), kCFStringEncodingUTF16, NO); - if (!fileName) { - CFRelease(filePath); - continue; - } + // Convert UTF8 buffer to windows appropriate UTF-16LE + // Get the real length of the string in UTF16 characters + CFStringRef cfStr = CFStringCreateWithCString(kCFAllocatorSystemDefault, directoryPathBuf, kCFStringEncodingUTF8); - Boolean isDirectory = file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; - Boolean result = fileHandler(fileName, filePath, isDirectory ? DT_DIR : DT_REG); - CFRelease(fileName); - CFRelease(filePath); - if (!result) break; - } while (FindNextFileW(handle, &file)); + // Copy the string into the buffer and terminate + CFStringGetCharacters(cfStr, CFRangeMake(0, CFStringGetLength(cfStr)), wideBuf); + wideBuf[CFStringGetLength(cfStr)] = L'\0'; - FindClose(handle); - } - free(wideBuf); + CFRelease(cfStr); + + WIN32_FIND_DATAW file; + HANDLE handle = FindFirstFileW(wideBuf, &file); + if (handle != INVALID_HANDLE_VALUE) { + do { + CFIndex nameLen = wcslen(file.cFileName); + if (file.cFileName[0] == '.' && (nameLen == 1 || (nameLen == 2 && file.cFileName[1] == '.'))) { + continue; + } + + CFStringRef fileName = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, file.cFileName, nameLen); + if (!fileName) { + continue; + } + + assert(!stuffToPrefix && "prefix not yet implemented"); + CFStringRef filePath = CFRetain(fileName); + + Boolean isDirectory = file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + Boolean result = fileHandler(fileName, filePath, isDirectory ? DT_DIR : DT_REG); + CFRelease(fileName); + CFRelease(filePath); + if (!result) break; + } while (FindNextFileW(handle, &file)); + + FindClose(handle); } #else DIR *dirp; diff --git a/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h b/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h index 88ebf4cd96d..7960d691b92 100644 --- a/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h +++ b/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h @@ -566,6 +566,10 @@ _stat_with_btime(const char *filename, struct stat *buffer, struct timespec *bti #warning "Enabling statx" #endif +#if DEPLOYMENT_TARGET_WINDOWS +CF_EXPORT void __CFSocketInitializeWinSock(void); +#endif + _CF_EXPORT_SCOPE_END #endif /* __COREFOUNDATION_FORSWIFTFOUNDATIONONLY__ */ diff --git a/CoreFoundation/CMakeLists.txt b/CoreFoundation/CMakeLists.txt index 01735dd8e97..a8880a145ff 100644 --- a/CoreFoundation/CMakeLists.txt +++ b/CoreFoundation/CMakeLists.txt @@ -205,6 +205,7 @@ add_framework(CoreFoundation URL.subproj/CFURLAccess.h URL.subproj/CFURLComponents.h SOURCES + $<$:CoreFoundation.rc> # Base Base.subproj/CFBase.c Base.subproj/CFFileUtilities.c @@ -315,6 +316,15 @@ add_framework(CoreFoundation URL.subproj/CFURLComponents.c URL.subproj/CFURLComponents_URIParser.c URL.subproj/CFURLSessionInterface.c) +if(CMAKE_SYSTEM_NAME STREQUAL Windows) + # NOTE(compnerd) the WindowsOlsonMapping.plist and OlsonWindowsMapping.plist + # are embedded Windows resources that we use to map the Windows timezone to + # the Olson name. Ensure that we rebuild on regeneration of the files. + add_custom_target(CoreFoundationWindowsTimeZonesPLists DEPENDS + WindowsOlsonMapping.plist + OlsonWindowsMapping.plist) + add_dependencies(CoreFoundation CoreFoundationWindowsTimeZonesPLists) +endif() if(CMAKE_SYSTEM_NAME STREQUAL Linux OR CMAKE_SYSTEM_NAME STREQUAL Android) target_compile_definitions(CoreFoundation PRIVATE @@ -373,7 +383,7 @@ target_compile_definitions(CoreFoundation target_include_directories(CoreFoundation PRIVATE - ${CMAKE_SOURCE_DIR}) + ${PROJECT_SOURCE_DIR}) if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) find_package(LibXml2 REQUIRED) target_include_directories(CoreFoundation @@ -403,11 +413,11 @@ endif() if("${CMAKE_C_SIMULATE_ID}" STREQUAL "MSVC") target_compile_options(CoreFoundation PRIVATE - $<$:/FI${CMAKE_SOURCE_DIR}/Base.subproj/CoreFoundation_Prefix.h>) + $<$:/FI${PROJECT_SOURCE_DIR}/Base.subproj/CoreFoundation_Prefix.h>) else() target_compile_options(CoreFoundation PRIVATE - $<$:-include;${CMAKE_SOURCE_DIR}/Base.subproj/CoreFoundation_Prefix.h>) + $<$:-include;${PROJECT_SOURCE_DIR}/Base.subproj/CoreFoundation_Prefix.h>) endif() if("${CMAKE_C_SIMULATE_ID}" STREQUAL "MSVC") @@ -462,6 +472,14 @@ target_link_libraries(CoreFoundation PRIVATE Threads::Threads ${CMAKE_DL_LIBS}) +if(CMAKE_SYSTEM_NAME STREQUAL Windows) + target_link_libraries(CoreFoundation + PRIVATE + AdvAPI32 + Secur32 + User32 + mincore) +endif() if(NOT CMAKE_SYSTEM_NAME STREQUAL Windows AND NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) target_link_libraries(CoreFoundation PRIVATE diff --git a/CoreFoundation/CoreFoundation.rc b/CoreFoundation/CoreFoundation.rc new file mode 100644 index 00000000000..5419bacca5d --- /dev/null +++ b/CoreFoundation/CoreFoundation.rc @@ -0,0 +1,6 @@ + +#include "WindowsResources.h" + +IDR_WINDOWS_OLSON_MAPPING PLIST "WindowsOlsonMapping.plist" +IDR_OLSON_WINDOWS_MAPPING PLIST "OlsonWindowsMapping.plist" + diff --git a/CoreFoundation/Locale.subproj/CFDateIntervalFormatter.c b/CoreFoundation/Locale.subproj/CFDateIntervalFormatter.c index 3a7760bb7d7..5f099b2624f 100644 --- a/CoreFoundation/Locale.subproj/CFDateIntervalFormatter.c +++ b/CoreFoundation/Locale.subproj/CFDateIntervalFormatter.c @@ -408,7 +408,7 @@ void CFDateIntervalFormatterSetTimeStyle(CFDateIntervalFormatterRef formatter, C _CFDateIntervalFormatterBoundaryStyle _CFDateIntervalFormatterGetBoundaryStyle(CFDateIntervalFormatterRef formatter) { LOCK(); - CFDateIntervalFormatterStyle result = formatter->_boundaryStyle; + _CFDateIntervalFormatterBoundaryStyle result = formatter->_boundaryStyle; UNLOCK(); return result; } diff --git a/CoreFoundation/NumberDate.subproj/CFTimeZone.c b/CoreFoundation/NumberDate.subproj/CFTimeZone.c index 080b7bec213..5a9973ef485 100644 --- a/CoreFoundation/NumberDate.subproj/CFTimeZone.c +++ b/CoreFoundation/NumberDate.subproj/CFTimeZone.c @@ -34,12 +34,17 @@ #endif #if TARGET_OS_WIN32 #include + +#include "WindowsResources.h" +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include "Windows.h" #endif + #if TARGET_OS_MAC #include #define MACOS_TZDIR1 "/usr/share/zoneinfo/" // 10.12 and earlier #define MACOS_TZDIR2 "/var/db/timezone/zoneinfo/" // 10.13 onwards - #elif TARGET_OS_LINUX || TARGET_OS_BSD #ifndef TZDIR #define TZDIR "/usr/share/zoneinfo/" /* Time zone object file directory */ @@ -65,9 +70,11 @@ struct tzhead { #include +#if !TARGET_OS_WIN32 static CFStringRef __tzZoneInfo = NULL; static char *__tzDir = NULL; static void __InitTZStrings(void); +#endif CONST_STRING_DECL(kCFTimeZoneSystemTimeZoneDidChangeNotification, "kCFTimeZoneSystemTimeZoneDidChangeNotification") @@ -111,6 +118,9 @@ CF_INLINE void __CFTimeZoneUnlockCompatibilityMapping(void) { } #if TARGET_OS_WIN32 +#define CF_TIME_ZONES_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones" +#define COUNT_OF(array) (sizeof((array)) / sizeof((array)[0])) + /* This function should be used for WIN32 instead of * __CFCopyRecursiveDirectoryList function. * It takes TimeZone names from the registry @@ -119,33 +129,60 @@ CF_INLINE void __CFTimeZoneUnlockCompatibilityMapping(void) { static CFMutableArrayRef __CFCopyWindowsTimeZoneList() { CFMutableArrayRef result = NULL; HKEY hkResult; - TCHAR lpName[MAX_PATH+1]; + WCHAR szName[MAX_PATH + 1]; DWORD dwIndex, retCode; - if (RegOpenKey(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones"), &hkResult) != - ERROR_SUCCESS ) + if (RegOpenKeyW(HKEY_LOCAL_MACHINE, CF_TIME_ZONES_KEY, &hkResult) != ERROR_SUCCESS) return NULL; result = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); - for (dwIndex=0; (retCode = RegEnumKey(hkResult,dwIndex,lpName,MAX_PATH)) != ERROR_NO_MORE_ITEMS ; dwIndex++) { + for (dwIndex = 0; (retCode = RegEnumKeyW(hkResult, dwIndex, szName, COUNT_OF(szName) - 1)) != ERROR_NO_MORE_ITEMS; dwIndex++) { if (retCode != ERROR_SUCCESS) { RegCloseKey(hkResult); CFRelease(result); return NULL; } else { -#if defined(UNICODE) - CFStringRef string = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const UInt8 *)lpName, (_tcslen(lpName) * sizeof(UniChar)), kCFStringEncodingUnicode, false); -#else - CFStringRef string = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const unsigned char *)lpName, _tcslen(lpName), CFStringGetSystemEncoding(), false); -#endif - CFArrayAppendValue(result, string); - CFRelease(string); + CFStringRef string = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const UInt8 *)szName, (wcslen(szName) * sizeof(WCHAR)), kCFStringEncodingUTF16, false); + CFArrayAppendValue(result, string); + CFRelease(string); } } RegCloseKey(hkResult); return result; } + +static void __CFTimeZoneGetOffset(CFStringRef timezone, int32_t *offset) { + typedef struct _REG_TZI_FORMAT { + LONG Bias; + LONG StandardBias; + LONG DaylightBias; + SYSTEMTIME StandardDate; + SYSTEMTIME DaylightDate; + } REG_TZI_FORMAT; + + WCHAR szRegKey[COUNT_OF(CF_TIME_ZONES_KEY) + 1 + COUNT_OF(((TIME_ZONE_INFORMATION *)0)->StandardName) + 1]; + REG_TZI_FORMAT tziInfo; + Boolean bResult; + DWORD cbData; + HKEY hKey; + + *offset = 0; + + memset(szRegKey, 0, sizeof(szRegKey)); + wcscpy(szRegKey, CF_TIME_ZONES_KEY); + szRegKey[COUNT_OF(CF_TIME_ZONES_KEY) - 1] = L'\\'; + CFStringGetBytes(timezone, CFRangeMake(0, CFStringGetLength(timezone)), kCFStringEncodingUnicode, FALSE, FALSE, (uint8_t *)&szRegKey[wcslen(CF_TIME_ZONES_KEY) + 1], sizeof(((TIME_ZONE_INFORMATION *)0)->StandardName) + sizeof(WCHAR), NULL); + + if (RegOpenKeyW(HKEY_LOCAL_MACHINE, szRegKey, &hKey) != ERROR_SUCCESS) + return; + + cbData = sizeof(tziInfo); + if (RegQueryValueExW(hKey, L"TZI", NULL, NULL, (LPBYTE)&tziInfo, &cbData) == ERROR_SUCCESS) + *offset = tziInfo.Bias; + + RegCloseKey(hKey); +} #elif TARGET_OS_MAC || TARGET_OS_LINUX || TARGET_OS_BSD static CFMutableArrayRef __CFCopyRecursiveDirectoryList() { CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); @@ -463,172 +500,6 @@ CFTypeID CFTimeZoneGetTypeID(void) { } #if TARGET_OS_WIN32 -static const char *__CFTimeZoneWinToOlsonDefaults = -/* Mappings to time zones in Windows Registry are best-guess */ -"" -" " -" " -" " -" Afghanistan Asia/Kabul" -" Afghanistan Standard Time Asia/Kabul" -" Alaskan America/Anchorage" -" Alaskan Standard Time America/Anchorage" -" Arab Asia/Riyadh" -" Arab Standard Time Asia/Riyadh" -" Arabian Asia/Muscat" -" Arabian Standard Time Asia/Muscat" -" Arabic Standard Time Asia/Baghdad" -" Atlantic America/Halifax" -" Atlantic Standard Time America/Halifax" -" AUS Central Australia/Darwin" -" AUS Central Standard Time Australia/Darwin" -" AUS Eastern Australia/Sydney" -" AUS Eastern Standard Time Australia/Sydney" -" Azerbaijan Standard Time Asia/Baku" -" Azores Atlantic/Azores" -" Azores Standard Time Atlantic/Azores" -" Bangkok Asia/Bangkok" -" Bangkok Standard Time Asia/Bangkok" -" Beijing Asia/Shanghai" -" Canada Central America/Regina" -" Canada Central Standard Time America/Regina" -" Cape Verde Standard Time Atlantic/Cape_Verde" -" Caucasus Asia/Yerevan" -" Caucasus Standard Time Asia/Yerevan" -" Cen. Australia Australia/Adelaide" -" Cen. Australia Standard Time Australia/Adelaide" -" Central America/Chicago" -" Central America Standard Time America/Regina" -" Central Asia Asia/Dhaka" -" Central Asia Standard Time Asia/Dhaka" -" Central Brazilian Standard Time America/Manaus" -" Central Europe Europe/Prague" -" Central Europe Standard Time Europe/Prague" -" Central European Europe/Belgrade" -" Central European Standard Time Europe/Belgrade" -" Central Pacific Pacific/Guadalcanal" -" Central Pacific Standard Time Pacific/Guadalcanal" -" Central Standard Time America/Chicago" -" Central Standard Time (Mexico) America/Mexico_City" -" China Asia/Shanghai" -" China Standard Time Asia/Shanghai" -" Dateline GMT-1200" -" Dateline Standard Time GMT-1200" -" E. Africa Africa/Nairobi" -" E. Africa Standard Time Africa/Nairobi" -" E. Australia Australia/Brisbane" -" E. Australia Standard Time Australia/Brisbane" -" E. Europe Europe/Minsk" -" E. Europe Standard Time Europe/Minsk" -" E. South America America/Sao_Paulo" -" E. South America Standard Time America/Sao_Paulo" -" Eastern America/New_York" -" Eastern Standard Time America/New_York" -" Egypt Africa/Cairo" -" Egypt Standard Time Africa/Cairo" -" Ekaterinburg Asia/Yekaterinburg" -" Ekaterinburg Standard Time Asia/Yekaterinburg" -" Fiji Pacific/Fiji" -" Fiji Standard Time Pacific/Fiji" -" FLE Europe/Helsinki" -" FLE Standard Time Europe/Helsinki" -" Georgian Standard Time Asia/Tbilisi" -" GFT Europe/Athens" -" GFT Standard Time Europe/Athens" -" GMT Europe/London" -" GMT Standard Time Europe/London" -" Greenland Standard Time America/Godthab" -" Greenwich GMT" -" Greenwich Standard Time GMT" -" GTB Europe/Athens" -" GTB Standard Time Europe/Athens" -" Hawaiian Pacific/Honolulu" -" Hawaiian Standard Time Pacific/Honolulu" -" India Asia/Calcutta" -" India Standard Time Asia/Calcutta" -" Iran Asia/Tehran" -" Iran Standard Time Asia/Tehran" -" Israel Asia/Jerusalem" -" Israel Standard Time Asia/Jerusalem" -" Jordan Standard Time Asia/Amman" -" Korea Asia/Seoul" -" Korea Standard Time Asia/Seoul" -" Mexico America/Mexico_City" -" Mexico Standard Time America/Mexico_City" -" Mexico Standard Time 2 America/Chihuahua" -" Mid-Atlantic Atlantic/South_Georgia" -" Mid-Atlantic Standard Time Atlantic/South_Georgia" -" Middle East Standard Time Asia/Beirut" -" Mountain America/Denver" -" Mountain Standard Time America/Denver" -" Mountain Standard Time (Mexico) America/Chihuahua" -" Myanmar Standard Time Asia/Rangoon" -" N. Central Asia Standard Time Asia/Novosibirsk" -" Namibia Standard Time Africa/Windhoek" -" Nepal Standard Time Asia/Katmandu" -" New Zealand Pacific/Auckland" -" New Zealand Standard Time Pacific/Auckland" -" Newfoundland America/St_Johns" -" Newfoundland Standard Time America/St_Johns" -" North Asia East Standard Time Asia/Ulaanbaatar" -" North Asia Standard Time Asia/Krasnoyarsk" -" Pacific America/Los_Angeles" -" Pacific SA America/Santiago" -" Pacific SA Standard Time America/Santiago" -" Pacific Standard Time America/Los_Angeles" -" Pacific Standard Time (Mexico) America/Tijuana" -" Prague Bratislava Europe/Prague" -" Romance Europe/Paris" -" Romance Standard Time Europe/Paris" -" Russian Europe/Moscow" -" Russian Standard Time Europe/Moscow" -" SA Eastern America/Buenos_Aires" -" SA Eastern Standard Time America/Buenos_Aires" -" SA Pacific America/Bogota" -" SA Pacific Standard Time America/Bogota" -" SA Western America/Caracas" -" SA Western Standard Time America/Caracas" -" Samoa Pacific/Apia" -" Samoa Standard Time Pacific/Apia" -" Saudi Arabia Asia/Riyadh" -" Saudi Arabia Standard Time Asia/Riyadh" -" SE Asia Standard Time Asia/Bangkok" -" Singapore Asia/Singapore" -" Singapore Standard Time Asia/Singapore" -" South Africa Africa/Harare" -" South Africa Standard Time Africa/Harare" -" Sri Lanka Asia/Colombo" -" Sri Lanka Standard Time Asia/Colombo" -" Sydney Standard Time Australia/Sydney" -" Taipei Asia/Taipei" -" Taipei Standard Time Asia/Taipei" -" Tasmania Australia/Hobart" -" Tasmania Standard Time Australia/Hobart" -" Tasmania Standard Time Australia/Hobart" -" Tokyo Asia/Tokyo" -" Tokyo Standard Time Asia/Tokyo" -" Tonga Standard Time Pacific/Tongatapu" -" US Eastern America/Indianapolis" -" US Eastern Standard Time America/Indianapolis" -" US Mountain America/Phoenix" -" US Mountain Standard Time America/Phoenix" -" Vladivostok Asia/Vladivostok" -" Vladivostok Standard Time Asia/Vladivostok" -" W. Australia Australia/Perth" -" W. Australia Standard Time Australia/Perth" -" W. Central Africa Standard Time Africa/Luanda" -" W. Europe Europe/Berlin" -" W. Europe Standard Time Europe/Berlin" -" Warsaw Europe/Warsaw" -" West Asia Asia/Karachi" -" West Asia Standard Time Asia/Karachi" -" West Pacific Pacific/Guam" -" West Pacific Standard Time Pacific/Guam" -" Western Brazilian Standard Time America/Rio_Branco" -" Yakutsk Asia/Yakutsk" -" " -" "; - CF_INLINE void __CFTimeZoneLockWinToOlson(void) { __CFLock(&__CFTimeZoneWinToOlsonLock); } @@ -637,22 +508,75 @@ CF_INLINE void __CFTimeZoneUnlockWinToOlson(void) { __CFUnlock(&__CFTimeZoneWinToOlsonLock); } +static Boolean CFTimeZoneLoadPlistResource(LPCSTR lpName, LPVOID *ppResource, LPDWORD pdwSize) { + HRSRC hResource; + HGLOBAL hMemory; + HMODULE hModule; + + if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCWSTR)&CFTimeZoneLoadPlistResource, &hModule)) { + return FALSE; + } + + hResource = FindResourceA(hModule, lpName, "PLIST"); + if (hResource == NULL) { + return FALSE; + } + + hMemory = LoadResource(hModule, hResource); + if (hMemory == NULL) { + return FALSE; + } + + *pdwSize = SizeofResource(hModule, hResource); + *ppResource = LockResource(hMemory); + + return *pdwSize && *ppResource; +} + CFDictionaryRef CFTimeZoneCopyWinToOlsonDictionary(void) { CFDictionaryRef dict; + __CFTimeZoneLockWinToOlson(); if (NULL == __CFTimeZoneWinToOlsonDict) { - CFDataRef data = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)__CFTimeZoneWinToOlsonDefaults, strlen(__CFTimeZoneWinToOlsonDefaults)); - __CFTimeZoneWinToOlsonDict = (CFDictionaryRef)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListImmutable, NULL); - CFRelease(data); + const uint8_t *plist; + DWORD dwSize; + + if (CFTimeZoneLoadPlistResource(MAKEINTRESOURCEA(IDR_WINDOWS_OLSON_MAPPING), (LPVOID *)&plist, &dwSize)) { + CFDataRef data = CFDataCreate(kCFAllocatorSystemDefault, plist, dwSize); + __CFTimeZoneWinToOlsonDict = (CFDictionaryRef)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListImmutable, NULL); + CFRelease(data); + } } if (NULL == __CFTimeZoneWinToOlsonDict) { __CFTimeZoneWinToOlsonDict = CFDictionaryCreate(kCFAllocatorSystemDefault, NULL, NULL, 0, NULL, NULL); } - dict = __CFTimeZoneWinToOlsonDict ? (CFDictionaryRef)CFRetain(__CFTimeZoneWinToOlsonDict) : NULL; __CFTimeZoneUnlockWinToOlson(); + + dict = __CFTimeZoneWinToOlsonDict ? (CFDictionaryRef)CFRetain(__CFTimeZoneWinToOlsonDict) : NULL; return dict; } +static CFDictionaryRef CFTimeZoneCopyOlsonToWindowsDictionary(void) { + static CFDictionaryRef dict; + static CFLock_t lock; + + __CFLock(&lock); + if (dict == NULL) { + const uint8_t *plist; + DWORD dwSize; + + if (CFTimeZoneLoadPlistResource(MAKEINTRESOURCEA(IDR_OLSON_WINDOWS_MAPPING), (LPVOID *)&plist, &dwSize)) { + CFDataRef data = CFDataCreate(kCFAllocatorSystemDefault, plist, dwSize); + dict = CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListImmutable, NULL); + CFRelease(data); + } + } + __CFUnlock(&lock); + + return dict ? CFRetain(dict) : NULL; +} + void CFTimeZoneSetWinToOlsonDictionary(CFDictionaryRef dict) { __CFGenericValidateType(dict, CFDictionaryGetTypeID()); __CFTimeZoneLockWinToOlson(); @@ -678,23 +602,6 @@ CFTimeZoneRef CFTimeZoneCreateWithWindowsName(CFAllocatorRef allocator, CFString CFRelease(winToOlson); return retval; } - -static void __InitTZStrings(void) { - static CFLock_t __CFTZDirLock = CFLockInit; - __CFLock(&__CFTZDirLock); - // TODO(compnerd) figure out how to initialize __tzZoneInfo - if (!__tzDir && __tzZoneInfo) { - int length = CFStringGetLength(__tzZoneInfo) + sizeof("\\zone.tab") + 1; - __tzDir = malloc(length); // If we don't use ascii, we'll need to malloc more space - if (!__tzDir || !CFStringGetCString(__tzZoneInfo, __tzDir, length, kCFStringEncodingASCII)) { - free(__tzDir); - } else { - strcat(__tzDir, "\\zone.tab"); - } - } - __CFUnlock(&__CFTZDirLock); -} - #elif TARGET_OS_MAC static void __InitTZStrings(void) { static dispatch_once_t initOnce = 0; @@ -1132,117 +1039,141 @@ Boolean _CFTimeZoneInitWithTimeIntervalFromGMT(CFTimeZoneRef result, CFTimeInter return true; } +Boolean _CFTimeZoneInitInternal(CFTimeZoneRef timezone, CFStringRef name, CFDataRef data) { + CFTZPeriod *tzp = NULL; + CFIndex cnt = 0; + Boolean success = false; + + __CFTimeZoneLockGlobal(); + success = __CFParseTimeZoneData(kCFAllocatorSystemDefault, data, &tzp, &cnt); + __CFTimeZoneUnlockGlobal(); + + if (success) { + ((struct __CFTimeZone *)timezone)->_name = (CFStringRef)CFStringCreateCopy(kCFAllocatorSystemDefault, name); + ((struct __CFTimeZone *)timezone)->_data = CFDataCreateCopy(kCFAllocatorSystemDefault, data); + ((struct __CFTimeZone *)timezone)->_periods = tzp; + ((struct __CFTimeZone *)timezone)->_periodCnt = cnt; + } + + return success; +} + Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data) { - if (name && __nameStringOK(name)) { - if (data) { - CFTZPeriod *tzp = NULL; - CFIndex cnt = 0; - __CFTimeZoneLockGlobal(); - if (__CFParseTimeZoneData(kCFAllocatorSystemDefault, data, &tzp, &cnt)) { - __CFTimeZoneUnlockGlobal(); - - } else { - __CFTimeZoneUnlockGlobal(); - return false; - } - ((struct __CFTimeZone *)timeZone)->_name = (CFStringRef)CFStringCreateCopy(kCFAllocatorSystemDefault, name); - ((struct __CFTimeZone *)timeZone)->_data = CFDataCreateCopy(kCFAllocatorSystemDefault, data); - ((struct __CFTimeZone *)timeZone)->_periods = tzp; - ((struct __CFTimeZone *)timeZone)->_periodCnt = cnt; - return true; - } else { - CFStringRef tzName = NULL; - CFDataRef data = NULL; - - CFIndex len = CFStringGetLength(name); - if (6 == len || 8 == len) { - UniChar buffer[8]; - CFStringGetCharacters(name, CFRangeMake(0, len), buffer); - if ('G' == buffer[0] && 'M' == buffer[1] && 'T' == buffer[2] && ('+' == buffer[3] || '-' == buffer[3])) { - if (('0' <= buffer[4] && buffer[4] <= '9') && ('0' <= buffer[5] && buffer[5] <= '9')) { - int32_t hours = (buffer[4] - '0') * 10 + (buffer[5] - '0'); - if (-14 <= hours && hours <= 14) { - CFTimeInterval ti = hours * 3600.0; - if (6 == len) { + if (!name || !__nameStringOK(name)) { + return false; + } + + if (data) { + return _CFTimeZoneInitInternal(timeZone, name, data); + } + + CFIndex len = CFStringGetLength(name); + if (6 == len || 8 == len) { + UniChar buffer[8]; + CFStringGetCharacters(name, CFRangeMake(0, len), buffer); + if ('G' == buffer[0] && 'M' == buffer[1] && 'T' == buffer[2] && ('+' == buffer[3] || '-' == buffer[3])) { + if (('0' <= buffer[4] && buffer[4] <= '9') && ('0' <= buffer[5] && buffer[5] <= '9')) { + int32_t hours = (buffer[4] - '0') * 10 + (buffer[5] - '0'); + if (-14 <= hours && hours <= 14) { + CFTimeInterval ti = hours * 3600.0; + if (6 == len) { + return _CFTimeZoneInitWithTimeIntervalFromGMT(timeZone, ('-' == buffer[3] ? -1.0 : 1.0) * ti); + } else { + if (('0' <= buffer[6] && buffer[6] <= '9') && ('0' <= buffer[7] && buffer[7] <= '9')) { + int32_t minutes = (buffer[6] - '0') * 10 + (buffer[7] - '0'); + if ((-14 == hours && 0 == minutes) || (14 == hours && 0 == minutes) || (0 <= minutes && minutes <= 59)) { + ti = ti + minutes * 60.0; return _CFTimeZoneInitWithTimeIntervalFromGMT(timeZone, ('-' == buffer[3] ? -1.0 : 1.0) * ti); - } else { - if (('0' <= buffer[6] && buffer[6] <= '9') && ('0' <= buffer[7] && buffer[7] <= '9')) { - int32_t minutes = (buffer[6] - '0') * 10 + (buffer[7] - '0'); - if ((-14 == hours && 0 == minutes) || (14 == hours && 0 == minutes) || (0 <= minutes && minutes <= 59)) { - ti = ti + minutes * 60.0; - return _CFTimeZoneInitWithTimeIntervalFromGMT(timeZone, ('-' == buffer[3] ? -1.0 : 1.0) * ti); - } - } } } } } } - Boolean tryAbbrev = true; - CFURLRef baseURL, tempURL; - void *bytes; - CFIndex length; - Boolean result = false; - - if (!__tzZoneInfo) __InitTZStrings(); - if (!__tzZoneInfo) return NULL; + } + } + + CFStringRef tzName = NULL; + CFURLRef baseURL, tempURL; + void *bytes; + CFIndex length; + Boolean result = false; + #if TARGET_OS_WIN32 - baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLWindowsPathStyle, true); + CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); + + tzName = CFDictionaryGetValue(abbrevs, name); + if (tzName == NULL) { + CFDictionaryRef olson = CFTimeZoneCopyOlsonToWindowsDictionary(); + tzName = CFDictionaryGetValue(olson, name); + CFRelease(olson); + } + + CFRelease(abbrevs); + + if (tzName) { + int32_t offset; + __CFTimeZoneGetOffset(tzName, &offset); + CFRelease(tzName); + // TODO(compnerd) handle DST + __CFTimeZoneInitFixed(timeZone, offset, name, 0); + return TRUE; + } + + return FALSE; #else - baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLPOSIXPathStyle, true); -#endif - if (tryAbbrev) { - CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); - tzName = CFDictionaryGetValue(abbrevs, name); - if (NULL != tzName) { - tempURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, tzName, false); - if (NULL != tempURL) { - if (_CFReadBytesFromFile(kCFAllocatorSystemDefault, tempURL, &bytes, &length, 0, 0)) { - data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, bytes, length, kCFAllocatorSystemDefault); - } - CFRelease(tempURL); - } - } - CFRelease(abbrevs); - } - if (NULL == data) { - CFDictionaryRef dict = __CFTimeZoneCopyCompatibilityDictionary(); - CFStringRef mapping = CFDictionaryGetValue(dict, name); - if (mapping) { - name = mapping; - } else if (CFStringHasPrefix(name, __tzZoneInfo)) { - CFMutableStringRef unprefixed = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, CFStringGetLength(name), name); - CFStringDelete(unprefixed, CFRangeMake(0, CFStringGetLength(__tzZoneInfo))); - mapping = CFDictionaryGetValue(dict, unprefixed); - if (mapping) { - name = mapping; - } - CFRelease(unprefixed); - } - CFRelease(dict); - if (CFEqual(CFSTR(""), name)) { - return false; - } + if (!__tzZoneInfo) __InitTZStrings(); + if (!__tzZoneInfo) return NULL; + baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLPOSIXPathStyle, true); + + CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); + tzName = CFDictionaryGetValue(abbrevs, name); + if (NULL != tzName) { + tempURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, tzName, false); + if (NULL != tempURL) { + if (_CFReadBytesFromFile(kCFAllocatorSystemDefault, tempURL, &bytes, &length, 0, 0)) { + data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, bytes, length, kCFAllocatorSystemDefault); } - if (NULL == data) { - tzName = name; - tempURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, tzName, false); - if (NULL != tempURL) { - if (_CFReadBytesFromFile(kCFAllocatorSystemDefault, tempURL, &bytes, &length, 0, 0)) { - data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, bytes, length, kCFAllocatorSystemDefault); - } - CFRelease(tempURL); - } + CFRelease(tempURL); + } + } + CFRelease(abbrevs); + + if (NULL == data) { + CFDictionaryRef dict = __CFTimeZoneCopyCompatibilityDictionary(); + CFStringRef mapping = CFDictionaryGetValue(dict, name); + if (mapping) { + name = mapping; + } else if (CFStringHasPrefix(name, __tzZoneInfo)) { + CFMutableStringRef unprefixed = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, CFStringGetLength(name), name); + CFStringDelete(unprefixed, CFRangeMake(0, CFStringGetLength(__tzZoneInfo))); + mapping = CFDictionaryGetValue(dict, unprefixed); + if (mapping) { + name = mapping; } - CFRelease(baseURL); - if (NULL != data) { - result = _CFTimeZoneInit(timeZone, tzName, data); - CFRelease(data); + CFRelease(unprefixed); + } + CFRelease(dict); + if (CFEqual(CFSTR(""), name)) { + return false; + } + } + if (NULL == data) { + tzName = name; + tempURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, tzName, false); + if (NULL != tempURL) { + if (_CFReadBytesFromFile(kCFAllocatorSystemDefault, tempURL, &bytes, &length, 0, 0)) { + data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, bytes, length, kCFAllocatorSystemDefault); } - return result; + CFRelease(tempURL); } } - return false; + CFRelease(baseURL); + if (NULL != data) { + result = _CFTimeZoneInitInternal(timeZone, tzName, data); + CFRelease(data); + } + return result; +#endif } CFTimeZoneRef CFTimeZoneCreate(CFAllocatorRef allocator, CFStringRef name, CFDataRef data) { @@ -1384,13 +1315,31 @@ CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef nam void *bytes; CFIndex length; - if (!__tzZoneInfo) __InitTZStrings(); - if (!__tzZoneInfo) return NULL; #if TARGET_OS_WIN32 - baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLWindowsPathStyle, true); + CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); + + tzName = CFDictionaryGetValue(abbrevs, name); + if (tzName == NULL) { + CFDictionaryRef olson = CFTimeZoneCopyOlsonToWindowsDictionary(); + tzName = CFDictionaryGetValue(olson, name); + CFRelease(olson); + } + + CFRelease(abbrevs); + + if (tzName) { + int32_t offset; + __CFTimeZoneGetOffset(tzName, &offset); + CFRelease(tzName); + // TODO(compnerd) handle DST + result = __CFTimeZoneCreateFixed(allocator, offset, name, 0); + } + + return result; #else + if (!__tzZoneInfo) __InitTZStrings(); + if (!__tzZoneInfo) return NULL; baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLPOSIXPathStyle, true); -#endif if (tryAbbrev) { CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); tzName = CFDictionaryGetValue(abbrevs, name); @@ -1447,6 +1396,7 @@ CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef nam CFRelease(data); } return result; +#endif } CFStringRef CFTimeZoneGetName(CFTimeZoneRef tz) { diff --git a/CoreFoundation/OlsonWindowsMapping.plist b/CoreFoundation/OlsonWindowsMapping.plist new file mode 100644 index 00000000000..871ee7abb9b --- /dev/null +++ b/CoreFoundation/OlsonWindowsMapping.plist @@ -0,0 +1,738 @@ + + + + + Etc/GMT+12 + Dateline Standard Time + Pacific/Pago_Pago + UTC-11 + Pacific/Niue + UTC-11 + Pacific/Midway + UTC-11 + Etc/GMT+11 + UTC-11 + America/Adak + Aleutian Standard Time + Pacific/Rarotonga + Hawaiian Standard Time + Pacific/Tahiti + Hawaiian Standard Time + Pacific/Johnston + Hawaiian Standard Time + Pacific/Honolulu + Hawaiian Standard Time + Etc/GMT+10 + Hawaiian Standard Time + Pacific/Marquesas + Marquesas Standard Time + America/Anchorage + Alaskan Standard Time + Pacific/Gambier + UTC-09 + Etc/GMT+9 + UTC-09 + America/Tijuana + Pacific Standard Time (Mexico) + Pacific/Pitcairn + UTC-08 + Etc/GMT+8 + UTC-08 + America/Vancouver + Pacific Standard Time + America/Los_Angeles + Pacific Standard Time + PST8PDT + Pacific Standard Time + America/Dawson_Creek + US Mountain Standard Time + America/Hermosillo + US Mountain Standard Time + America/Phoenix + US Mountain Standard Time + Etc/GMT+7 + US Mountain Standard Time + America/Chihuahua + Mountain Standard Time (Mexico) + America/Edmonton + Mountain Standard Time + America/Ojinaga + Mountain Standard Time + America/Denver + Mountain Standard Time + MST7MDT + Mountain Standard Time + America/Belize + Central America Standard Time + America/Costa_Rica + Central America Standard Time + Pacific/Galapagos + Central America Standard Time + America/Guatemala + Central America Standard Time + America/Tegucigalpa + Central America Standard Time + America/Managua + Central America Standard Time + America/El_Salvador + Central America Standard Time + Etc/GMT+6 + Central America Standard Time + America/Winnipeg + Central Standard Time + America/Matamoros + Central Standard Time + America/Chicago + Central Standard Time + CST6CDT + Central Standard Time + Pacific/Easter + Easter Island Standard Time + America/Mexico_City + Central Standard Time (Mexico) + America/Regina + Canada Central Standard Time + America/Rio_Branco + SA Pacific Standard Time + America/Coral_Harbour + SA Pacific Standard Time + America/Bogota + SA Pacific Standard Time + America/Guayaquil + SA Pacific Standard Time + America/Jamaica + SA Pacific Standard Time + America/Cayman + SA Pacific Standard Time + America/Panama + SA Pacific Standard Time + America/Lima + SA Pacific Standard Time + Etc/GMT+5 + SA Pacific Standard Time + America/Cancun + Eastern Standard Time (Mexico) + America/Nassau + Eastern Standard Time + America/Toronto + Eastern Standard Time + America/New_York + Eastern Standard Time + EST5EDT + Eastern Standard Time + America/Port-au-Prince + Haiti Standard Time + America/Havana + Cuba Standard Time + America/Indianapolis + US Eastern Standard Time + America/Asuncion + Paraguay Standard Time + Atlantic/Bermuda + Atlantic Standard Time + America/Halifax + Atlantic Standard Time + America/Thule + Atlantic Standard Time + America/Caracas + Venezuela Standard Time + America/Cuiaba + Central Brazilian Standard Time + America/Antigua + SA Western Standard Time + America/Anguilla + SA Western Standard Time + America/Aruba + SA Western Standard Time + America/Barbados + SA Western Standard Time + America/St_Barthelemy + SA Western Standard Time + America/La_Paz + SA Western Standard Time + America/Kralendijk + SA Western Standard Time + America/Manaus + SA Western Standard Time + America/Blanc-Sablon + SA Western Standard Time + America/Curacao + SA Western Standard Time + America/Dominica + SA Western Standard Time + America/Santo_Domingo + SA Western Standard Time + America/Grenada + SA Western Standard Time + America/Guadeloupe + SA Western Standard Time + America/Guyana + SA Western Standard Time + America/St_Kitts + SA Western Standard Time + America/St_Lucia + SA Western Standard Time + America/Marigot + SA Western Standard Time + America/Martinique + SA Western Standard Time + America/Montserrat + SA Western Standard Time + America/Puerto_Rico + SA Western Standard Time + America/Lower_Princes + SA Western Standard Time + America/Port_of_Spain + SA Western Standard Time + America/St_Vincent + SA Western Standard Time + America/Tortola + SA Western Standard Time + America/St_Thomas + SA Western Standard Time + Etc/GMT+4 + SA Western Standard Time + America/Santiago + Pacific SA Standard Time + America/Grand_Turk + Turks And Caicos Standard Time + America/St_Johns + Newfoundland Standard Time + America/Araguaina + Tocantins Standard Time + America/Sao_Paulo + E. South America Standard Time + Antarctica/Rothera + SA Eastern Standard Time + America/Fortaleza + SA Eastern Standard Time + Atlantic/Stanley + SA Eastern Standard Time + America/Cayenne + SA Eastern Standard Time + America/Paramaribo + SA Eastern Standard Time + Etc/GMT+3 + SA Eastern Standard Time + America/Buenos_Aires + Argentina Standard Time + America/Godthab + Greenland Standard Time + America/Montevideo + Montevideo Standard Time + Antarctica/Palmer + Magallanes Standard Time + America/Punta_Arenas + Magallanes Standard Time + America/Miquelon + Saint Pierre Standard Time + America/Bahia + Bahia Standard Time + America/Noronha + UTC-02 + Atlantic/South_Georgia + UTC-02 + Etc/GMT+2 + UTC-02 + America/Scoresbysund + Azores Standard Time + Atlantic/Azores + Azores Standard Time + Atlantic/Cape_Verde + Cape Verde Standard Time + Etc/GMT+1 + Cape Verde Standard Time + America/Danmarkshavn + UTC + Etc/GMT + UTC + Atlantic/Canary + GMT Standard Time + Atlantic/Faeroe + GMT Standard Time + Europe/London + GMT Standard Time + Europe/Guernsey + GMT Standard Time + Europe/Dublin + GMT Standard Time + Europe/Isle_of_Man + GMT Standard Time + Europe/Jersey + GMT Standard Time + Europe/Lisbon + GMT Standard Time + Africa/Ouagadougou + Greenwich Standard Time + Africa/Abidjan + Greenwich Standard Time + Africa/Accra + Greenwich Standard Time + Africa/Banjul + Greenwich Standard Time + Africa/Conakry + Greenwich Standard Time + Africa/Bissau + Greenwich Standard Time + Atlantic/Reykjavik + Greenwich Standard Time + Africa/Monrovia + Greenwich Standard Time + Africa/Bamako + Greenwich Standard Time + Africa/Nouakchott + Greenwich Standard Time + Atlantic/St_Helena + Greenwich Standard Time + Africa/Freetown + Greenwich Standard Time + Africa/Dakar + Greenwich Standard Time + Africa/Lome + Greenwich Standard Time + Europe/Andorra + W. Europe Standard Time + Europe/Vienna + W. Europe Standard Time + Europe/Zurich + W. Europe Standard Time + Europe/Berlin + W. Europe Standard Time + Europe/Gibraltar + W. Europe Standard Time + Europe/Rome + W. Europe Standard Time + Europe/Vaduz + W. Europe Standard Time + Europe/Luxembourg + W. Europe Standard Time + Europe/Monaco + W. Europe Standard Time + Europe/Malta + W. Europe Standard Time + Europe/Amsterdam + W. Europe Standard Time + Europe/Oslo + W. Europe Standard Time + Europe/Stockholm + W. Europe Standard Time + Arctic/Longyearbyen + W. Europe Standard Time + Europe/San_Marino + W. Europe Standard Time + Europe/Vatican + W. Europe Standard Time + Europe/Tirane + Central Europe Standard Time + Europe/Prague + Central Europe Standard Time + Europe/Budapest + Central Europe Standard Time + Europe/Podgorica + Central Europe Standard Time + Europe/Belgrade + Central Europe Standard Time + Europe/Ljubljana + Central Europe Standard Time + Europe/Bratislava + Central Europe Standard Time + Europe/Brussels + Romance Standard Time + Europe/Copenhagen + Romance Standard Time + Europe/Madrid + Romance Standard Time + Europe/Paris + Romance Standard Time + Africa/El_Aaiun + Morocco Standard Time + Africa/Casablanca + Morocco Standard Time + Africa/Sao_Tome + Sao Tome Standard Time + Europe/Sarajevo + Central European Standard Time + Europe/Zagreb + Central European Standard Time + Europe/Skopje + Central European Standard Time + Europe/Warsaw + Central European Standard Time + Africa/Luanda + W. Central Africa Standard Time + Africa/Porto-Novo + W. Central Africa Standard Time + Africa/Kinshasa + W. Central Africa Standard Time + Africa/Bangui + W. Central Africa Standard Time + Africa/Brazzaville + W. Central Africa Standard Time + Africa/Douala + W. Central Africa Standard Time + Africa/Algiers + W. Central Africa Standard Time + Africa/Libreville + W. Central Africa Standard Time + Africa/Malabo + W. Central Africa Standard Time + Africa/Niamey + W. Central Africa Standard Time + Africa/Lagos + W. Central Africa Standard Time + Africa/Ndjamena + W. Central Africa Standard Time + Africa/Tunis + W. Central Africa Standard Time + Etc/GMT-1 + W. Central Africa Standard Time + Asia/Amman + Jordan Standard Time + Asia/Famagusta + GTB Standard Time + Europe/Athens + GTB Standard Time + Europe/Bucharest + GTB Standard Time + Asia/Beirut + Middle East Standard Time + Africa/Cairo + Egypt Standard Time + Europe/Chisinau + E. Europe Standard Time + Asia/Damascus + Syria Standard Time + Asia/Hebron + West Bank Standard Time + Africa/Bujumbura + South Africa Standard Time + Africa/Gaborone + South Africa Standard Time + Africa/Lubumbashi + South Africa Standard Time + Africa/Maseru + South Africa Standard Time + Africa/Blantyre + South Africa Standard Time + Africa/Maputo + South Africa Standard Time + Africa/Kigali + South Africa Standard Time + Africa/Mbabane + South Africa Standard Time + Africa/Johannesburg + South Africa Standard Time + Africa/Lusaka + South Africa Standard Time + Africa/Harare + South Africa Standard Time + Etc/GMT-2 + South Africa Standard Time + Europe/Mariehamn + FLE Standard Time + Europe/Sofia + FLE Standard Time + Europe/Tallinn + FLE Standard Time + Europe/Helsinki + FLE Standard Time + Europe/Vilnius + FLE Standard Time + Europe/Riga + FLE Standard Time + Europe/Kiev + FLE Standard Time + Asia/Jerusalem + Israel Standard Time + Europe/Kaliningrad + Kaliningrad Standard Time + Africa/Khartoum + Sudan Standard Time + Africa/Tripoli + Libya Standard Time + Africa/Windhoek + Namibia Standard Time + Asia/Baghdad + Arabic Standard Time + Europe/Istanbul + Turkey Standard Time + Asia/Bahrain + Arab Standard Time + Asia/Kuwait + Arab Standard Time + Asia/Qatar + Arab Standard Time + Asia/Riyadh + Arab Standard Time + Asia/Aden + Arab Standard Time + Europe/Minsk + Belarus Standard Time + Europe/Moscow + Russian Standard Time + Europe/Simferopol + Russian Standard Time + Antarctica/Syowa + E. Africa Standard Time + Africa/Djibouti + E. Africa Standard Time + Africa/Asmera + E. Africa Standard Time + Africa/Addis_Ababa + E. Africa Standard Time + Africa/Nairobi + E. Africa Standard Time + Indian/Comoro + E. Africa Standard Time + Indian/Antananarivo + E. Africa Standard Time + Africa/Mogadishu + E. Africa Standard Time + Africa/Juba + E. Africa Standard Time + Africa/Dar_es_Salaam + E. Africa Standard Time + Africa/Kampala + E. Africa Standard Time + Indian/Mayotte + E. Africa Standard Time + Etc/GMT-3 + E. Africa Standard Time + Asia/Tehran + Iran Standard Time + Asia/Dubai + Arabian Standard Time + Asia/Muscat + Arabian Standard Time + Etc/GMT-4 + Arabian Standard Time + Europe/Astrakhan + Astrakhan Standard Time + Asia/Baku + Azerbaijan Standard Time + Europe/Samara + Russia Time Zone 3 + Indian/Mauritius + Mauritius Standard Time + Indian/Reunion + Mauritius Standard Time + Indian/Mahe + Mauritius Standard Time + Europe/Saratov + Saratov Standard Time + Asia/Tbilisi + Georgian Standard Time + Asia/Yerevan + Caucasus Standard Time + Asia/Kabul + Afghanistan Standard Time + Antarctica/Mawson + West Asia Standard Time + Asia/Oral + West Asia Standard Time + Indian/Maldives + West Asia Standard Time + Indian/Kerguelen + West Asia Standard Time + Asia/Dushanbe + West Asia Standard Time + Asia/Ashgabat + West Asia Standard Time + Asia/Tashkent + West Asia Standard Time + Etc/GMT-5 + West Asia Standard Time + Asia/Yekaterinburg + Ekaterinburg Standard Time + Asia/Karachi + Pakistan Standard Time + Asia/Calcutta + India Standard Time + Asia/Colombo + Sri Lanka Standard Time + Asia/Katmandu + Nepal Standard Time + Antarctica/Vostok + Central Asia Standard Time + Asia/Urumqi + Central Asia Standard Time + Indian/Chagos + Central Asia Standard Time + Asia/Bishkek + Central Asia Standard Time + Asia/Almaty + Central Asia Standard Time + Etc/GMT-6 + Central Asia Standard Time + Asia/Dhaka + Bangladesh Standard Time + Asia/Thimphu + Bangladesh Standard Time + Asia/Omsk + Omsk Standard Time + Indian/Cocos + Myanmar Standard Time + Asia/Rangoon + Myanmar Standard Time + Antarctica/Davis + SE Asia Standard Time + Indian/Christmas + SE Asia Standard Time + Asia/Jakarta + SE Asia Standard Time + Asia/Phnom_Penh + SE Asia Standard Time + Asia/Vientiane + SE Asia Standard Time + Asia/Bangkok + SE Asia Standard Time + Asia/Saigon + SE Asia Standard Time + Etc/GMT-7 + SE Asia Standard Time + Asia/Barnaul + Altai Standard Time + Asia/Hovd + W. Mongolia Standard Time + Asia/Krasnoyarsk + North Asia Standard Time + Asia/Novosibirsk + N. Central Asia Standard Time + Asia/Tomsk + Tomsk Standard Time + Asia/Shanghai + China Standard Time + Asia/Hong_Kong + China Standard Time + Asia/Macau + China Standard Time + Asia/Irkutsk + North Asia East Standard Time + Asia/Brunei + Singapore Standard Time + Asia/Makassar + Singapore Standard Time + Asia/Kuala_Lumpur + Singapore Standard Time + Asia/Manila + Singapore Standard Time + Asia/Singapore + Singapore Standard Time + Etc/GMT-8 + Singapore Standard Time + Antarctica/Casey + W. Australia Standard Time + Australia/Perth + W. Australia Standard Time + Asia/Taipei + Taipei Standard Time + Asia/Ulaanbaatar + Ulaanbaatar Standard Time + Australia/Eucla + Aus Central W. Standard Time + Asia/Chita + Transbaikal Standard Time + Asia/Jayapura + Tokyo Standard Time + Asia/Tokyo + Tokyo Standard Time + Pacific/Palau + Tokyo Standard Time + Asia/Dili + Tokyo Standard Time + Etc/GMT-9 + Tokyo Standard Time + Asia/Pyongyang + North Korea Standard Time + Asia/Seoul + Korea Standard Time + Asia/Yakutsk + Yakutsk Standard Time + Australia/Adelaide + Cen. Australia Standard Time + Australia/Darwin + AUS Central Standard Time + Australia/Brisbane + E. Australia Standard Time + Australia/Sydney + AUS Eastern Standard Time + Antarctica/DumontDUrville + West Pacific Standard Time + Pacific/Truk + West Pacific Standard Time + Pacific/Guam + West Pacific Standard Time + Pacific/Saipan + West Pacific Standard Time + Pacific/Port_Moresby + West Pacific Standard Time + Etc/GMT-10 + West Pacific Standard Time + Australia/Hobart + Tasmania Standard Time + Asia/Vladivostok + Vladivostok Standard Time + Australia/Lord_Howe + Lord Howe Standard Time + Pacific/Bougainville + Bougainville Standard Time + Asia/Srednekolymsk + Russia Time Zone 10 + Asia/Magadan + Magadan Standard Time + Pacific/Norfolk + Norfolk Standard Time + Asia/Sakhalin + Sakhalin Standard Time + Antarctica/Macquarie + Central Pacific Standard Time + Pacific/Ponape + Central Pacific Standard Time + Pacific/Noumea + Central Pacific Standard Time + Pacific/Guadalcanal + Central Pacific Standard Time + Pacific/Efate + Central Pacific Standard Time + Etc/GMT-11 + Central Pacific Standard Time + Asia/Kamchatka + Russia Time Zone 11 + Antarctica/McMurdo + New Zealand Standard Time + Pacific/Auckland + New Zealand Standard Time + Pacific/Tarawa + UTC+12 + Pacific/Majuro + UTC+12 + Pacific/Nauru + UTC+12 + Pacific/Funafuti + UTC+12 + Pacific/Wake + UTC+12 + Pacific/Wallis + UTC+12 + Etc/GMT-12 + UTC+12 + Pacific/Fiji + Fiji Standard Time + Pacific/Chatham + Chatham Islands Standard Time + Pacific/Enderbury + UTC+13 + Pacific/Fakaofo + UTC+13 + Etc/GMT-13 + UTC+13 + Pacific/Tongatapu + Tonga Standard Time + Pacific/Apia + Samoa Standard Time + Pacific/Kiritimati + Line Islands Standard Time + Etc/GMT-14 + Line Islands Standard Time + + diff --git a/CoreFoundation/String.subproj/CFBurstTrie.h b/CoreFoundation/String.subproj/CFBurstTrie.h index ec1a28dacb3..fd86e0cc906 100644 --- a/CoreFoundation/String.subproj/CFBurstTrie.h +++ b/CoreFoundation/String.subproj/CFBurstTrie.h @@ -60,7 +60,7 @@ typedef CF_OPTIONS(CFOptionFlags, CFBurstTrieOpts) { typedef void (*CFBurstTrieTraversalCallback)(void* context, const UInt8* key, uint32_t keyLength, uint32_t payload, Boolean *stop); CF_EXPORT -CFBurstTrieRef CFBurstTrieCreate() API_AVAILABLE(macos(10.7), ios(4.2), watchos(2.0), tvos(9.0)); +CFBurstTrieRef CFBurstTrieCreate(void) API_AVAILABLE(macos(10.7), ios(4.2), watchos(2.0), tvos(9.0)); CF_EXPORT CFBurstTrieRef CFBurstTrieCreateWithOptions(CFDictionaryRef options) API_AVAILABLE(macos(10.8), ios(6.0), watchos(2.0), tvos(9.0)); diff --git a/CoreFoundation/Tools/OlsonWindowsDatabase.xsl b/CoreFoundation/Tools/OlsonWindowsDatabase.xsl new file mode 100644 index 00000000000..86ab9b87614 --- /dev/null +++ b/CoreFoundation/Tools/OlsonWindowsDatabase.xsl @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + <!DOCTYPE plist SYSTEM "file:///localhost/System/Library/DTDs/PropertyList.dtd"> + + + + + + + + + + + + + + + + + diff --git a/CoreFoundation/Tools/WindowsOlsonDatabase.xsl b/CoreFoundation/Tools/WindowsOlsonDatabase.xsl new file mode 100644 index 00000000000..3966d46cf75 --- /dev/null +++ b/CoreFoundation/Tools/WindowsOlsonDatabase.xsl @@ -0,0 +1,27 @@ + + + + + + + <!DOCTYPE plist SYSTEM "file:///localhost/System/Library/DTDs/PropertyList.dtd"> + + + + + + + + + + + diff --git a/CoreFoundation/URL.subproj/CFURLComponents.h b/CoreFoundation/URL.subproj/CFURLComponents.h index 081ea63d2a9..51e05529a8c 100644 --- a/CoreFoundation/URL.subproj/CFURLComponents.h +++ b/CoreFoundation/URL.subproj/CFURLComponents.h @@ -87,12 +87,12 @@ CF_EXPORT CFStringRef _CFStringCreateByAddingPercentEncodingWithAllowedCharacter CF_EXPORT CFStringRef _Nullable _CFStringCreateByRemovingPercentEncoding(CFAllocatorRef alloc, CFStringRef string); // These return singletons -CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLUserAllowedCharacterSet(); -CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLPasswordAllowedCharacterSet(); -CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLHostAllowedCharacterSet(); -CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLPathAllowedCharacterSet(); -CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLQueryAllowedCharacterSet(); -CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLFragmentAllowedCharacterSet(); +CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLUserAllowedCharacterSet(void); +CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLPasswordAllowedCharacterSet(void); +CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLHostAllowedCharacterSet(void); +CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLPathAllowedCharacterSet(void); +CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLQueryAllowedCharacterSet(void); +CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLFragmentAllowedCharacterSet(void); // keys for dictionaries returned by _CFURLComponentsCopyQueryItems CF_EXPORT const CFStringRef _kCFURLComponentsNameKey; diff --git a/CoreFoundation/WindowsOlsonMapping.plist b/CoreFoundation/WindowsOlsonMapping.plist new file mode 100644 index 00000000000..7af48262deb --- /dev/null +++ b/CoreFoundation/WindowsOlsonMapping.plist @@ -0,0 +1,276 @@ + + + + + Dateline Standard Time + Etc/GMT+12 + UTC-11 + Etc/GMT+11 + Aleutian Standard Time + America/Adak + Hawaiian Standard Time + Pacific/Honolulu + Marquesas Standard Time + Pacific/Marquesas + Alaskan Standard Time + America/Anchorage + UTC-09 + Etc/GMT+9 + Pacific Standard Time (Mexico) + America/Tijuana + UTC-08 + Etc/GMT+8 + Pacific Standard Time + America/Los_Angeles + US Mountain Standard Time + America/Phoenix + Mountain Standard Time (Mexico) + America/Chihuahua + Mountain Standard Time + America/Denver + Central America Standard Time + America/Guatemala + Central Standard Time + America/Chicago + Easter Island Standard Time + Pacific/Easter + Central Standard Time (Mexico) + America/Mexico_City + Canada Central Standard Time + America/Regina + SA Pacific Standard Time + America/Bogota + Eastern Standard Time (Mexico) + America/Cancun + Eastern Standard Time + America/New_York + Haiti Standard Time + America/Port-au-Prince + Cuba Standard Time + America/Havana + US Eastern Standard Time + America/Indianapolis + Paraguay Standard Time + America/Asuncion + Atlantic Standard Time + America/Halifax + Venezuela Standard Time + America/Caracas + Central Brazilian Standard Time + America/Cuiaba + SA Western Standard Time + America/La_Paz + Pacific SA Standard Time + America/Santiago + Turks And Caicos Standard Time + America/Grand_Turk + Newfoundland Standard Time + America/St_Johns + Tocantins Standard Time + America/Araguaina + E. South America Standard Time + America/Sao_Paulo + SA Eastern Standard Time + America/Cayenne + Argentina Standard Time + America/Buenos_Aires + Greenland Standard Time + America/Godthab + Montevideo Standard Time + America/Montevideo + Magallanes Standard Time + America/Punta_Arenas + Saint Pierre Standard Time + America/Miquelon + Bahia Standard Time + America/Bahia + UTC-02 + Etc/GMT+2 + Azores Standard Time + Atlantic/Azores + Cape Verde Standard Time + Atlantic/Cape_Verde + UTC + Etc/GMT + GMT Standard Time + Europe/London + Greenwich Standard Time + Atlantic/Reykjavik + W. Europe Standard Time + Europe/Berlin + Central Europe Standard Time + Europe/Budapest + Romance Standard Time + Europe/Paris + Morocco Standard Time + Africa/Casablanca + Sao Tome Standard Time + Africa/Sao_Tome + Central European Standard Time + Europe/Warsaw + W. Central Africa Standard Time + Africa/Lagos + Jordan Standard Time + Asia/Amman + GTB Standard Time + Europe/Bucharest + Middle East Standard Time + Asia/Beirut + Egypt Standard Time + Africa/Cairo + E. Europe Standard Time + Europe/Chisinau + Syria Standard Time + Asia/Damascus + West Bank Standard Time + Asia/Hebron + South Africa Standard Time + Africa/Johannesburg + FLE Standard Time + Europe/Kiev + Israel Standard Time + Asia/Jerusalem + Kaliningrad Standard Time + Europe/Kaliningrad + Sudan Standard Time + Africa/Khartoum + Libya Standard Time + Africa/Tripoli + Namibia Standard Time + Africa/Windhoek + Arabic Standard Time + Asia/Baghdad + Turkey Standard Time + Europe/Istanbul + Arab Standard Time + Asia/Riyadh + Belarus Standard Time + Europe/Minsk + Russian Standard Time + Europe/Moscow + E. Africa Standard Time + Africa/Nairobi + Iran Standard Time + Asia/Tehran + Arabian Standard Time + Asia/Dubai + Astrakhan Standard Time + Europe/Astrakhan + Azerbaijan Standard Time + Asia/Baku + Russia Time Zone 3 + Europe/Samara + Mauritius Standard Time + Indian/Mauritius + Saratov Standard Time + Europe/Saratov + Georgian Standard Time + Asia/Tbilisi + Caucasus Standard Time + Asia/Yerevan + Afghanistan Standard Time + Asia/Kabul + West Asia Standard Time + Asia/Tashkent + Ekaterinburg Standard Time + Asia/Yekaterinburg + Pakistan Standard Time + Asia/Karachi + India Standard Time + Asia/Calcutta + Sri Lanka Standard Time + Asia/Colombo + Nepal Standard Time + Asia/Katmandu + Central Asia Standard Time + Asia/Almaty + Bangladesh Standard Time + Asia/Dhaka + Omsk Standard Time + Asia/Omsk + Myanmar Standard Time + Asia/Rangoon + SE Asia Standard Time + Asia/Bangkok + Altai Standard Time + Asia/Barnaul + W. Mongolia Standard Time + Asia/Hovd + North Asia Standard Time + Asia/Krasnoyarsk + N. Central Asia Standard Time + Asia/Novosibirsk + Tomsk Standard Time + Asia/Tomsk + China Standard Time + Asia/Shanghai + North Asia East Standard Time + Asia/Irkutsk + Singapore Standard Time + Asia/Singapore + W. Australia Standard Time + Australia/Perth + Taipei Standard Time + Asia/Taipei + Ulaanbaatar Standard Time + Asia/Ulaanbaatar + Aus Central W. Standard Time + Australia/Eucla + Transbaikal Standard Time + Asia/Chita + Tokyo Standard Time + Asia/Tokyo + North Korea Standard Time + Asia/Pyongyang + Korea Standard Time + Asia/Seoul + Yakutsk Standard Time + Asia/Yakutsk + Cen. Australia Standard Time + Australia/Adelaide + AUS Central Standard Time + Australia/Darwin + E. Australia Standard Time + Australia/Brisbane + AUS Eastern Standard Time + Australia/Sydney + West Pacific Standard Time + Pacific/Port_Moresby + Tasmania Standard Time + Australia/Hobart + Vladivostok Standard Time + Asia/Vladivostok + Lord Howe Standard Time + Australia/Lord_Howe + Bougainville Standard Time + Pacific/Bougainville + Russia Time Zone 10 + Asia/Srednekolymsk + Magadan Standard Time + Asia/Magadan + Norfolk Standard Time + Pacific/Norfolk + Sakhalin Standard Time + Asia/Sakhalin + Central Pacific Standard Time + Pacific/Guadalcanal + Russia Time Zone 11 + Asia/Kamchatka + New Zealand Standard Time + Pacific/Auckland + UTC+12 + Etc/GMT-12 + Fiji Standard Time + Pacific/Fiji + Chatham Islands Standard Time + Pacific/Chatham + UTC+13 + Etc/GMT-13 + Tonga Standard Time + Pacific/Tongatapu + Samoa Standard Time + Pacific/Apia + Line Islands Standard Time + Pacific/Kiritimati + + diff --git a/CoreFoundation/WindowsResources.h b/CoreFoundation/WindowsResources.h new file mode 100644 index 00000000000..f1bd285a978 --- /dev/null +++ b/CoreFoundation/WindowsResources.h @@ -0,0 +1,4 @@ + +#define IDR_WINDOWS_OLSON_MAPPING 101 +#define IDR_OLSON_WINDOWS_MAPPING 102 + diff --git a/CoreFoundation/cmake/modules/CoreFoundationAddFramework.cmake b/CoreFoundation/cmake/modules/CoreFoundationAddFramework.cmake index 58d8c41291a..53f6a1ee7e3 100644 --- a/CoreFoundation/cmake/modules/CoreFoundationAddFramework.cmake +++ b/CoreFoundation/cmake/modules/CoreFoundationAddFramework.cmake @@ -63,7 +63,7 @@ function(add_framework NAME) endif() target_compile_options(${NAME} PRIVATE - -I;${CMAKE_BINARY_DIR}/${NAME}.framework/PrivateHeaders) + $<$,$>:-I;${CMAKE_BINARY_DIR}/${NAME}.framework/PrivateHeaders>) add_dependencies(${NAME} ${NAME}_POPULATE_HEADERS) if(AF_FRAMEWORK_DIRECTORY) diff --git a/DarwinCompatibilityTests.xcodeproj/project.pbxproj b/DarwinCompatibilityTests.xcodeproj/project.pbxproj index fd26c80e57f..8adac6c162b 100644 --- a/DarwinCompatibilityTests.xcodeproj/project.pbxproj +++ b/DarwinCompatibilityTests.xcodeproj/project.pbxproj @@ -11,8 +11,11 @@ B907F36F20BB188800013CBE /* NSString-ISO-8859-1-data.txt in Resources */ = {isa = PBXBuildFile; fileRef = B907F36E20BB188800013CBE /* NSString-ISO-8859-1-data.txt */; }; B917D32420B0DB9700728EE0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B917D32320B0DB9700728EE0 /* Foundation.framework */; }; B917D32620B0DE2000728EE0 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = B917D32520B0DE2000728EE0 /* main.swift */; }; + B940492B223B13D000FB4384 /* TestProgressFraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B940492A223B13D000FB4384 /* TestProgressFraction.swift */; }; B94897772135E7AD00FB930E /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94897762135E7AC00FB930E /* Utilities.swift */; }; B95788861F6FB9470003EB01 /* TestNSNumberBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = B95788851F6FB9470003EB01 /* TestNSNumberBridging.swift */; }; + B97E7854222AF973007596B0 /* TestDateIntervalFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B97E7853222AF973007596B0 /* TestDateIntervalFormatter.swift */; }; + B97E7856222AF995007596B0 /* TestPropertyListEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B97E7855222AF995007596B0 /* TestPropertyListEncoder.swift */; }; B987C65E2093C8AF0026B50D /* TestImports.swift in Sources */ = {isa = PBXBuildFile; fileRef = B987C65D2093C8AF0026B50D /* TestImports.swift */; }; B98E33E02136AC120044EBE9 /* TestFileWithZeros.txt in Resources */ = {isa = PBXBuildFile; fileRef = B98E33DF2136AC120044EBE9 /* TestFileWithZeros.txt */; }; B9C89F361F6BF89C00087AF4 /* TestScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89EE61F6BF88F00087AF4 /* TestScanner.swift */; }; @@ -80,7 +83,6 @@ B9C89F781F6BF89C00087AF4 /* TestNSData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89F291F6BF89900087AF4 /* TestNSData.swift */; }; B9C89F791F6BF89C00087AF4 /* TestNSRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89F2A1F6BF89900087AF4 /* TestNSRange.swift */; }; B9C89F7A1F6BF89C00087AF4 /* TestNSGeometry.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89F2B1F6BF89900087AF4 /* TestNSGeometry.swift */; }; - B9C89F7B1F6BF89C00087AF4 /* TestNSProgressFraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89F2C1F6BF89900087AF4 /* TestNSProgressFraction.swift */; }; B9C89F7C1F6BF89C00087AF4 /* TestURLCredential.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89F2D1F6BF89A00087AF4 /* TestURLCredential.swift */; }; B9C89F7D1F6BF89C00087AF4 /* TestRunLoop.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89F2E1F6BF89A00087AF4 /* TestRunLoop.swift */; }; B9C89F7E1F6BF89C00087AF4 /* TestThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89F2F1F6BF89B00087AF4 /* TestThread.swift */; }; @@ -155,8 +157,11 @@ B917D31C20B0DB8B00728EE0 /* xdgTestHelper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = xdgTestHelper; sourceTree = BUILT_PRODUCTS_DIR; }; B917D32320B0DB9700728EE0 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; B917D32520B0DE2000728EE0 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = main.swift; path = TestFoundation/xdgTestHelper/main.swift; sourceTree = ""; }; + B940492A223B13D000FB4384 /* TestProgressFraction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TestProgressFraction.swift; path = TestFoundation/TestProgressFraction.swift; sourceTree = ""; }; B94897762135E7AC00FB930E /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Utilities.swift; path = TestFoundation/Utilities.swift; sourceTree = ""; }; B95788851F6FB9470003EB01 /* TestNSNumberBridging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TestNSNumberBridging.swift; path = TestFoundation/TestNSNumberBridging.swift; sourceTree = ""; }; + B97E7853222AF973007596B0 /* TestDateIntervalFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TestDateIntervalFormatter.swift; path = TestFoundation/TestDateIntervalFormatter.swift; sourceTree = ""; }; + B97E7855222AF995007596B0 /* TestPropertyListEncoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TestPropertyListEncoder.swift; path = TestFoundation/TestPropertyListEncoder.swift; sourceTree = ""; }; B987C65D2093C8AF0026B50D /* TestImports.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TestImports.swift; path = TestFoundation/TestImports.swift; sourceTree = ""; }; B98E33DF2136AC120044EBE9 /* TestFileWithZeros.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = TestFileWithZeros.txt; path = TestFoundation/Resources/TestFileWithZeros.txt; sourceTree = ""; }; B9C89ED11F6BF67C00087AF4 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/MacOSX.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; @@ -294,6 +299,9 @@ B9C89EB81F6BF47D00087AF4 = { isa = PBXGroup; children = ( + B940492A223B13D000FB4384 /* TestProgressFraction.swift */, + B97E7855222AF995007596B0 /* TestPropertyListEncoder.swift */, + B97E7853222AF973007596B0 /* TestDateIntervalFormatter.swift */, B9C89FAC1F6DCAE700087AF4 /* Info.plist */, B9C89FAD1F6DCAE800087AF4 /* NSKeyedUnarchiver-ArrayTest.plist */, B9C89FB31F6DCAE900087AF4 /* NSKeyedUnarchiver-ComplexTest.plist */, @@ -558,6 +566,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B940492B223B13D000FB4384 /* TestProgressFraction.swift in Sources */, + B97E7856222AF995007596B0 /* TestPropertyListEncoder.swift in Sources */, + B97E7854222AF973007596B0 /* TestDateIntervalFormatter.swift in Sources */, B94897772135E7AD00FB930E /* Utilities.swift in Sources */, B987C65E2093C8AF0026B50D /* TestImports.swift in Sources */, B95788861F6FB9470003EB01 /* TestNSNumberBridging.swift in Sources */, @@ -632,7 +643,6 @@ B9C89F781F6BF89C00087AF4 /* TestNSData.swift in Sources */, B9C89F791F6BF89C00087AF4 /* TestNSRange.swift in Sources */, B9C89F7A1F6BF89C00087AF4 /* TestNSGeometry.swift in Sources */, - B9C89F7B1F6BF89C00087AF4 /* TestNSProgressFraction.swift in Sources */, B9C89F7C1F6BF89C00087AF4 /* TestURLCredential.swift in Sources */, B9C89F7D1F6BF89C00087AF4 /* TestRunLoop.swift in Sources */, B9C89F7E1F6BF89C00087AF4 /* TestThread.swift in Sources */, @@ -661,10 +671,10 @@ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CODE_SIGN_STYLE = Automatic; - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = 10.14; OTHER_SWIFT_FLAGS = "-DDEPLOYMENT_RUNTIME_OBJC -DDARWIN_COMPATIBILITY_TESTS"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -675,10 +685,10 @@ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CODE_SIGN_STYLE = Automatic; - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = 10.14; OTHER_SWIFT_FLAGS = "-DDEPLOYMENT_RUNTIME_OBJC -DDARWIN_COMPATIBILITY_TESTS"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -730,7 +740,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -781,7 +791,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -795,10 +805,11 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = DarwinCompatibilityTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.14; "OTHER_SWIFT_FLAGS[arch=*]" = "-DDEPLOYMENT_RUNTIME_OBJC -DDARWIN_COMPATIBILITY_TESTS"; PRODUCT_BUNDLE_IDENTIFIER = org.swift.DarwinCompatibilityTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -809,10 +820,11 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = DarwinCompatibilityTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.14; "OTHER_SWIFT_FLAGS[arch=*]" = "-DDEPLOYMENT_RUNTIME_OBJC -DDARWIN_COMPATIBILITY_TESTS"; PRODUCT_BUNDLE_IDENTIFIER = org.swift.DarwinCompatibilityTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/DarwinCompatibilityTests.xcodeproj/xcshareddata/xcschemes/xdgTestHelper.xcscheme b/DarwinCompatibilityTests.xcodeproj/xcshareddata/xcschemes/xdgTestHelper.xcscheme new file mode 100644 index 00000000000..192a059c872 --- /dev/null +++ b/DarwinCompatibilityTests.xcodeproj/xcshareddata/xcschemes/xdgTestHelper.xcscheme @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Docs/Status.md b/Docs/Status.md index 5a18734cff3..d0f2e31d62c 100644 --- a/Docs/Status.md +++ b/Docs/Status.md @@ -56,7 +56,7 @@ There is no _Complete_ status for test coverage because there are always additio | `URLProtocolClient` | Unimplemented | None | | | `NSURLRequest` | Complete | Incomplete | | | `NSMutableURLRequest` | Complete | Incomplete | | - | `URLResponse` | Complete | Incomplete | | + | `URLResponse` | Complete | Substantial | | | `NSHTTPURLResponse` | Complete | Substantial | | | `NSURL` | Mostly Complete | Substantial | Resource values remain unimplemented | | `NSURLQueryItem` | Complete | N/A | | @@ -76,13 +76,13 @@ There is no _Complete_ status for test coverage because there are always additio | `HTTPMessage` | N/A | N/A | For internal use only | | `libcurlHelpers` | N/A | N/A | For internal use only | | `MultiHandle` | N/A | N/A | For internal use only | - | `URLSession` | Mostly Complete | Incomplete | `shared`, invalidation, resetting, flushing, getting tasks, and others remain unimplemented | + | `URLSession` | Mostly Complete | Incomplete | invalidation, resetting, flushing, getting tasks, and others remain unimplemented | | `URLSessionConfiguration` | Mostly Complete | Incomplete | `ephemeral` and `background(withIdentifier:)` remain unimplemented | | `URLSessionDelegate` | Complete | N/A | | | `URLSessionTask` | Mostly Complete | Incomplete | `cancel()`, `createTransferState(url:)` with streams, and others remain unimplemented | | `URLSessionDataTask` | Complete | Incomplete | | | `URLSessionUploadTask` | Complete | None | | - | `URLSessionDownloadTask` | Incomplete | Incomplete | | + | `URLSessionDownloadTask` | Incomplete | Incomplete | | | `URLSessionStreamTask` | Unimplemented | None | | | `TaskRegistry` | N/A | N/A | For internal use only | | `TransferState` | N/A | N/A | For internal use only | @@ -94,10 +94,10 @@ There is no _Complete_ status for test coverage because there are always additio | Entity Name | Status | Test Coverage | Notes | |---------------------------------|-----------------|---------------|-------------------------------------------------------------------------------------------| - | `DateComponentFormatter` | Unimplemented | None | | - | `DateIntervalFormatter` | Unimplemented | None | | - | `EnergyFormatter` | Unimplemented | None | | - | `ISO8601DateFormatter` | Unimplemented | None | | + | `DateComponentsFormatter` | Unimplemented | None | | + | `DateIntervalFormatter` | Mostly Complete | Incomplete | `NSCoding` remains to be implemented | + | `EnergyFormatter` | Complete | Substantial | | + | `ISO8601DateFormatter` | Mostly Complete | Substantial | `NSCoding` remains to be implemented | | `LengthFormatter` | Complete | Substantial | | | `MassFormatter` | Complete | Substantial | Needs localization | | `NumberFormatter` | Mostly Complete | Substantial | `objectValue(_:range:)` remains unimplemented | @@ -159,18 +159,18 @@ There is no _Complete_ status for test coverage because there are always additio | `NSIndexSet` | Mostly Complete | Incomplete | `NSCoding` remains to be implemented | | `NSMutableIndexSet` | Mostly Complete | Incomplete | `NSCoding` remains to be implemented | | `IndexSet` | Complete | Incomplete | | - | `NSIndexPath` | Mostly Complete | None | `NSCoding`, `NSCopying`, `getIndexes(_:)` remain unimplemented | + | `NSIndexPath` | Mostly Complete | None | `NSCoding` remains to be implemented | | `IndexPath` | Complete | Incomplete | | - | `NSArray` | Mostly Complete | Substantial | Reading/writing to files/URLs, concurrent `enumerateObjects(at:options:using:)`, and `sortedArray(from:options:usingComparator:)` with options remain unimplemented | + | `NSArray` | Mostly Complete | Substantial | concurrent `enumerateObjects(at:options:using:)`, and `sortedArray(from:options:usingComparator:)` with options remain unimplemented | | `NSMutableArray` | Mostly Complete | Substantial | `exchangeObject(at:withObjectAt:)` and `replaceObjects(in:withObjectsFromArray:)` remain unimplemented for types other than `NSMutableArray` | - | `NSDictionary` | Mostly Complete | Incomplete | `NSCoding` with non-keyed-coding archivers, `descriptionInStringsFileFormat`, `sharedKeySet(forKeys:)`, and reading/writing to files/URLs remain unimplemented | - | `NSMutableDictionary` | Mostly Complete | Incomplete | `descriptionInStringsFileFormat`, `sharedKeySet(forKeys:)`, and reading/writing to files/URLs remain unimplemented | + | `NSDictionary` | Mostly Complete | Incomplete | `NSCoding` with non-keyed-coding archivers and `sharedKeySet(forKeys:)` | + | `NSMutableDictionary` | Mostly Complete | Incomplete | `NSCoding` with non-keyed-coding archivers and`sharedKeySet(forKeys:)` | | `NSCFDictionary` | N/A | N/A | For internal use only | - | `NSSet` | Mostly Complete | Incomplete | `description(withLocale:)` and `customMirror` remain unimplemented | + | `NSSet` | Mostly Complete | Incomplete | `NSCoding` and `customMirror` remain unimplemented | | `NSMutableSet` | Mostly Complete | Incomplete | `init?(coder:)` remains unimplemented | | `NSCountedSet` | Mostly Complete | Incomplete | `init?(coder:)` remains unimplemented | | `NSCFSet` | N/A | N/A | For internal use only | - | `NSCache` | Complete | Incomplete | | + | `NSCache` | Complete | Incomplete | `NSCoding` and `customMirror` remain unimplemented | | `NSSortDescriptor` | Unimplemented | None | | * **RunLoop**: Timers, streams and run loops. @@ -200,7 +200,7 @@ There is no _Complete_ status for test coverage because there are always additio |-----------------------------|-----------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `NSRegularExpression` | Complete | Substantial | | | `Scanner` | Mostly Complete | Incomplete | `localizedScannerWithString(_:)` remains unimplemented | - | `NSTextCheckingResult` | Mostly Complete | Incomplete | `NSCoding`, `NSCopying`, `resultType`, and `range(at:)` remain unimplemented | + | `NSTextCheckingResult` | Mostly Complete | Incomplete | `NSCoding`, `resultType`, and `range(at:)` remain unimplemented | | `NSAttributedString` | Mostly Complete | Incomplete | `NSCoding` remains unimplemented | | `NSMutableAttributedString` | Mostly Complete | Incomplete | `NSCoding` remains unimplemented | | `NSCharacterSet` | Mostly Complete | Incomplete | `NSCoding` remains unimplemented | @@ -219,7 +219,7 @@ There is no _Complete_ status for test coverage because there are always additio |-----------------------------------|-----------------|---------------|-------------------------------------------------------------------------------| | `NSRange` | Complete | Incomplete | | | `Decimal` | Complete | Substantial | | - | `NSDecimalNumber` | Mostly Complete | Substantial | | + | `NSDecimalNumber` | Complete | Substantial | | | `NSDecimalNumberHandler` | Complete | None | | | `CGPoint` | Complete | Substantial | | | `CGSize` | Complete | Substantial | | @@ -273,10 +273,10 @@ There is no _Complete_ status for test coverage because there are always additio | Entity Name | Status | Test Coverage | Notes | |------------------|-----------------|---------------|---------------------------------------------------------------------------------------------------------------------------| - | `FileHandle` | Mostly Complete | Incomplete | `NSCoding`, and background operations remain unimplemented | + | `FileHandle` | Mostly Complete | Incomplete | `NSCoding` remain unimplemented | | `Pipe` | Complete | Incomplete | | - | `FileManager` | Incomplete | Incomplete | URL searches, relationship lookups, item copying, cross-device moving, recursive linking, and others remain unimplemented | - | `Process` | Mostly Complete | Substantial | `interrupt()`, `terminate()`, `suspend()`, and `resume()` remain unimplemented | + | `FileManager` | Incomplete | Incomplete | relationship lookups, `displayName(atPath:)`, `componentsToDisplay(forPath:)`, and replacing items remain unimplemented | + | `Process` | Complete | Substantial | | | `Bundle` | Mostly Complete | Incomplete | `allBundles`, `init(for:)`, `unload()`, `classNamed()`, and `principalClass` remain unimplemented | | `ProcessInfo` | Complete | Substantial | | | `Thread` | Complete | Incomplete | | @@ -292,7 +292,7 @@ There is no _Complete_ status for test coverage because there are always additio | Entity Name | Status | Test Coverage | Notes | |--------------------|-----------------|---------------|---------------------------------------------------------------------------------------------------------------------------------| - | `NSCalendar` | Complete | None | `autoupdatingCurrent`, and `enumerateDates` remain unimplemented | + | `NSCalendar` | Complete | None | `autoupdatingCurrent` remains unimplemented | | `NSDateComponents` | Complete | None | | | `Calendar` | Complete | Incomplete | | | `DateComponents` | Complete | Incomplete | | diff --git a/Foundation.xcodeproj/project.pbxproj b/Foundation.xcodeproj/project.pbxproj index f64b9194ff0..63764470a58 100644 --- a/Foundation.xcodeproj/project.pbxproj +++ b/Foundation.xcodeproj/project.pbxproj @@ -34,6 +34,7 @@ 159884921DCC877700E3314C /* TestHTTPCookieStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 159884911DCC877700E3314C /* TestHTTPCookieStorage.swift */; }; 15F10CDC218909BF00D88114 /* TestNSCalendar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F10CDB218909BF00D88114 /* TestNSCalendar.swift */; }; 231503DB1D8AEE5D0061694D /* TestDecimal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231503DA1D8AEE5D0061694D /* TestDecimal.swift */; }; + 25EB1806223334D30053EE59 /* TestURLCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25EB1805223334D30053EE59 /* TestURLCache.swift */; }; 294E3C1D1CC5E19300E4F44C /* TestNSAttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294E3C1C1CC5E19300E4F44C /* TestNSAttributedString.swift */; }; 2EBE67A51C77BF0E006583D5 /* TestDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBE67A31C77BF05006583D5 /* TestDateFormatter.swift */; }; 3E55A2331F52463B00082000 /* TestUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E55A2321F52463B00082000 /* TestUnit.swift */; }; @@ -366,6 +367,7 @@ B910957B1EEF237800A71930 /* NSString-UTF16-BE-data.txt in Resources */ = {isa = PBXBuildFile; fileRef = B91095791EEF237800A71930 /* NSString-UTF16-BE-data.txt */; }; B933A79E1F3055F700FE6846 /* NSString-UTF32-BE-data.txt in Resources */ = {isa = PBXBuildFile; fileRef = B933A79C1F3055F600FE6846 /* NSString-UTF32-BE-data.txt */; }; B933A79F1F3055F700FE6846 /* NSString-UTF32-LE-data.txt in Resources */ = {isa = PBXBuildFile; fileRef = B933A79D1F3055F600FE6846 /* NSString-UTF32-LE-data.txt */; }; + B940492D223B146800FB4384 /* TestProgressFraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B940492C223B146800FB4384 /* TestProgressFraction.swift */; }; B951B5EC1F4E2A2000D8B332 /* TestNSLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B951B5EB1F4E2A2000D8B332 /* TestNSLock.swift */; }; B98E33DD2136AA740044EBE9 /* TestFileWithZeros.txt in Resources */ = {isa = PBXBuildFile; fileRef = B98E33DC2136AA740044EBE9 /* TestFileWithZeros.txt */; }; B9974B961EDF4A22007F15B8 /* TransferState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9974B8F1EDF4A22007F15B8 /* TransferState.swift */; }; @@ -412,7 +414,6 @@ E1A3726F1C31EBFB0023AF4D /* NSXMLDocumentTestData.xml in Resources */ = {isa = PBXBuildFile; fileRef = E1A3726E1C31EBFB0023AF4D /* NSXMLDocumentTestData.xml */; }; EA01AAEC1DA839C4008F4E07 /* TestProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA01AAEB1DA839C4008F4E07 /* TestProgress.swift */; }; EA0812691DA71C8A00651B70 /* ProgressFraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0812681DA71C8A00651B70 /* ProgressFraction.swift */; }; - EA08126B1DA80C3600651B70 /* TestNSProgressFraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA08126A1DA80C3600651B70 /* TestNSProgressFraction.swift */; }; EA08126C1DA810BE00651B70 /* ProgressFraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0812681DA71C8A00651B70 /* ProgressFraction.swift */; }; EA418C261D57257D005EAD0D /* NSKeyedArchiverHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA418C251D57257D005EAD0D /* NSKeyedArchiverHelpers.swift */; }; EA54A6FB1DB16D53009E0809 /* TestObjCRuntime.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA54A6FA1DB16D53009E0809 /* TestObjCRuntime.swift */; }; @@ -574,6 +575,7 @@ 15F10CDB218909BF00D88114 /* TestNSCalendar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestNSCalendar.swift; sourceTree = ""; }; 22B9C1E01C165D7A00DECFF9 /* TestDate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestDate.swift; sourceTree = ""; }; 231503DA1D8AEE5D0061694D /* TestDecimal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestDecimal.swift; sourceTree = ""; }; + 25EB1805223334D30053EE59 /* TestURLCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestURLCache.swift; sourceTree = ""; }; 294E3C1C1CC5E19300E4F44C /* TestNSAttributedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSAttributedString.swift; sourceTree = ""; }; 2EBE67A31C77BF05006583D5 /* TestDateFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestDateFormatter.swift; sourceTree = ""; }; 3E55A2321F52463B00082000 /* TestUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestUnit.swift; sourceTree = ""; }; @@ -896,6 +898,7 @@ B91095791EEF237800A71930 /* NSString-UTF16-BE-data.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "NSString-UTF16-BE-data.txt"; sourceTree = ""; }; B933A79C1F3055F600FE6846 /* NSString-UTF32-BE-data.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "NSString-UTF32-BE-data.txt"; sourceTree = ""; }; B933A79D1F3055F600FE6846 /* NSString-UTF32-LE-data.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "NSString-UTF32-LE-data.txt"; sourceTree = ""; }; + B940492C223B146800FB4384 /* TestProgressFraction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TestProgressFraction.swift; path = TestFoundation/TestProgressFraction.swift; sourceTree = ""; }; B951B5EB1F4E2A2000D8B332 /* TestNSLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestNSLock.swift; sourceTree = ""; }; B98E33DC2136AA740044EBE9 /* TestFileWithZeros.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = TestFileWithZeros.txt; sourceTree = ""; }; B9974B8F1EDF4A22007F15B8 /* TransferState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransferState.swift; sourceTree = ""; }; @@ -1119,6 +1122,7 @@ 5B5D88531BBC938800234F36 = { isa = PBXGroup; children = ( + B940492C223B146800FB4384 /* TestProgressFraction.swift */, B167A6641ED7303F0040B09A /* README.md */, 5BDC3F2C1BCC5DB500ED97BB /* Foundation */, EAB57B681BD1A255004AC5C5 /* CoreFoundation */, @@ -1656,6 +1660,7 @@ 5B6F17961C48631C00935030 /* TestUtils.swift */, 03B6F5831F15F339004F25AF /* TestURLProtocol.swift */, 3E55A2321F52463B00082000 /* TestUnit.swift */, + 25EB1805223334D30053EE59 /* TestURLCache.swift */, ); name = Tests; sourceTree = ""; @@ -2240,6 +2245,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -2592,6 +2598,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B940492D223B146800FB4384 /* TestProgressFraction.swift in Sources */, 159884921DCC877700E3314C /* TestHTTPCookieStorage.swift in Sources */, 5FE52C951D147D1C00F7D270 /* TestNSTextCheckingResult.swift in Sources */, 5B13B3451C582D4C00651CE2 /* TestNSString.swift in Sources */, @@ -2618,10 +2625,10 @@ 5B13B3411C582D4C00651CE2 /* TestNSRegularExpression.swift in Sources */, 5B13B3491C582D4C00651CE2 /* TestTimeZone.swift in Sources */, 5B13B34B1C582D4C00651CE2 /* TestNSURLRequest.swift in Sources */, - EA08126B1DA80C3600651B70 /* TestNSProgressFraction.swift in Sources */, 5B13B33E1C582D4C00651CE2 /* TestProcessInfo.swift in Sources */, 5B13B33F1C582D4C00651CE2 /* TestPropertyListSerialization.swift in Sources */, 5B13B32C1C582D4C00651CE2 /* TestDate.swift in Sources */, + 25EB1806223334D30053EE59 /* TestURLCache.swift in Sources */, C7DE1FCC21EEE67200174F35 /* TestUUID.swift in Sources */, 231503DB1D8AEE5D0061694D /* TestDecimal.swift in Sources */, 7900433C1CACD33E00ECCBF1 /* TestNSPredicate.swift in Sources */, diff --git a/Foundation/Bundle.swift b/Foundation/Bundle.swift index 0a2601edded..95c1cb9fdc8 100644 --- a/Foundation/Bundle.swift +++ b/Foundation/Bundle.swift @@ -292,8 +292,18 @@ open class Bundle: NSObject { } } - open func classNamed(_ className: String) -> AnyClass? { NSUnimplemented() } - open var principalClass: AnyClass? { NSUnimplemented() } + open func classNamed(_ className: String) -> AnyClass? { + // FIXME: This will return a class that may not be associated with the receiver. https://bugs.swift.org/browse/SR-10347. + guard isLoaded || load() else { return nil } + return NSClassFromString(className) + } + + open var principalClass: AnyClass? { + // NB: Cross-platform Swift doesn't have a notion of 'the first class in the ObjC segment' that ObjC platforms have. For swift-corelibs-foundation, if a bundle doesn't have a principal class named, the principal class is nil. + guard let name = infoDictionary?["NSPrincipalClass"] as? String else { return nil } + return classNamed(name) + } + open var preferredLocalizations: [String] { return Bundle.preferredLocalizations(from: localizations) } diff --git a/Foundation/FileHandle.swift b/Foundation/FileHandle.swift index 304fd72ab80..e9df8ece03e 100644 --- a/Foundation/FileHandle.swift +++ b/Foundation/FileHandle.swift @@ -342,6 +342,23 @@ open class FileHandle : NSObject, NSSecureCoding { } #endif } + + internal func _readBytes(into buffer: UnsafeMutablePointer, length: Int) throws -> Int { +#if os(Windows) + var BytesRead: DWORD = 0 + let BytesToRead: DWORD = DWORD(length) + if ReadFile(_handle, buffer, BytesToRead, &BytesRead, nil) == FALSE { + throw _NSErrorWithWindowsError(GetLastError(), reading: true) + } + return Int(BytesRead) +#else + let amtRead = _read(_fd, buffer, length) + if amtRead < 0 { + throw _NSErrorWithErrno(errno, reading: true) + } + return amtRead +#endif + } internal func _writeBytes(buf: UnsafeRawPointer, length: Int) throws { #if os(Windows) @@ -426,6 +443,19 @@ open class FileHandle : NSObject, NSSecureCoding { } #endif + internal convenience init?(fileSystemRepresentation: UnsafePointer, flags: Int32, createMode: Int) { +#if os(Windows) + var fd: Int32 = -1 + if let path = String(cString: fileSystemRepresentation).cString(using: .utf16) { + fd = _CFOpenFileWithMode(path, flags, mode_t(createMode)) + } +#else + let fd = _CFOpenFileWithMode(fileSystemRepresentation, flags, mode_t(createMode)) +#endif + guard fd > 0 else { return nil } + self.init(fileDescriptor: fd, closeOnDealloc: true) + } + deinit { // .close() tries to wait after operations in flight on the handle queue, if one exists, and then close. It does so by sending .sync { … } work to it. // if we try to do that here, we may end up in a situation where: @@ -477,7 +507,9 @@ open class FileHandle : NSObject, NSSecureCoding { for region in data.regions { try region.withUnsafeBytes { (bytes) in - try _writeBytes(buf: UnsafeRawPointer(bytes.baseAddress!), length: bytes.count) + if let baseAddress = bytes.baseAddress, bytes.count > 0 { + try _writeBytes(buf: UnsafeRawPointer(baseAddress), length: bytes.count) + } } } } @@ -752,8 +784,10 @@ extension FileHandle { } internal static func _openFileDescriptorForURL(_ url : URL, flags: Int32, reading: Bool) throws -> Int32 { - let path = url.path - let fd = _CFOpenFile(path, flags) + let fd = url.withUnsafeFileSystemRepresentation( { (fsRep) -> Int32 in + guard let fsRep = fsRep else { return -1 } + return _CFOpenFile(fsRep, flags) + }) if fd < 0 { throw _NSErrorWithErrno(errno, reading: reading, url: url) } @@ -882,7 +916,7 @@ extension FileHandle { @available(Windows, unavailable, message: "A SOCKET cannot be treated as a fd") open func acceptConnectionInBackgroundAndNotify(forModes modes: [RunLoop.Mode]?) { #if os(Windows) - NSUnavailable() + NSUnsupported() #else let owner = monitor(forReading: true, resumed: false) { (handle, source) in var notification = Notification(name: .NSFileHandleConnectionAccepted, object: handle, userInfo: [:]) diff --git a/Foundation/FileManager.swift b/Foundation/FileManager.swift index 2e886f29399..70f03e19207 100644 --- a/Foundation/FileManager.swift +++ b/Foundation/FileManager.swift @@ -15,6 +15,21 @@ internal func &(left: UInt32, right: mode_t) -> mode_t { import CoreFoundation +#if os(Windows) +internal func joinPath(prefix: String, suffix: String) -> String { + var pszPath: PWSTR? + _ = prefix.withCString(encodedAs: UTF16.self) { prefix in + _ = suffix.withCString(encodedAs: UTF16.self) { suffix in + PathAllocCombine(prefix, suffix, ULONG(PATHCCH_ALLOW_LONG_PATHS.rawValue), &pszPath) + } + } + + let path: String = String(decodingCString: pszPath!, as: UTF16.self) + LocalFree(pszPath) + return path +} +#endif + open class FileManager : NSObject { /* Returns the default singleton instance. @@ -690,7 +705,7 @@ open class FileManager : NSObject { try path.withCString(encodedAs: UTF16.self) { - if CreateDirectoryW($0, psaAttributes) != FALSE { + if CreateDirectoryW($0, psaAttributes) == FALSE { // FIXME(compnerd) pass along path throw _NSErrorWithWindowsError(GetLastError(), reading: false) } @@ -801,21 +816,6 @@ open class FileManager : NSObject { return contents } -#if os(Windows) - private func joinPath(prefix: String, suffix: String) -> String { - var pszPath: PWSTR? - _ = prefix.withCString(encodedAs: UTF16.self) { prefix in - _ = suffix.withCString(encodedAs: UTF16.self) { suffix in - PathAllocCombine(prefix, suffix, ULONG(PATHCCH_ALLOW_LONG_PATHS.rawValue), &pszPath) - } - } - - let path: String = String(decodingCString: pszPath!, as: UTF16.self) - LocalFree(pszPath) - return path - } -#endif - /** Performs a deep enumeration of the specified directory and returns the paths of all of the contained subdirectories. @@ -871,14 +871,6 @@ open class FileManager : NSObject { open func attributesOfItem(atPath path: String) throws -> [FileAttributeKey : Any] { var result: [FileAttributeKey:Any] = [:] -#if os(Windows) - let faAttributes: WIN32_FILE_ATTRIBUTE_DATA = try windowsFileAttributes(atPath: path) - - result[.size] = NSNumber(value: (faAttributes.nFileSizeHigh << 32) | faAttributes.nFileSizeLow) - result[.modificationDate] = Date(timeIntervalSinceReferenceDate: TimeInterval(faAttributes.ftLastWriteTime)) - // FIXME(compnerd) what about .posixPermissions, .referenceCount, .systemNumber, .systemFileNumber, .ownerAccountName, .groupOwnerAccountName, .type, .immuatable, .appendOnly, .ownerAccountID, .groupOwnerAccountID -#else - #if os(Linux) let (s, creationDate) = try _statxFile(atPath: path) result[.creationDate] = creationDate @@ -892,29 +884,36 @@ open class FileManager : NSObject { let ti = (TimeInterval(s.st_mtimespec.tv_sec) - kCFAbsoluteTimeIntervalSince1970) + (1.0e-9 * TimeInterval(s.st_mtimespec.tv_nsec)) #elseif os(Android) let ti = (TimeInterval(s.st_mtime) - kCFAbsoluteTimeIntervalSince1970) + (1.0e-9 * TimeInterval(s.st_mtime_nsec)) +#elseif os(Windows) + let ti = (TimeInterval(s.st_mtime) - kCFAbsoluteTimeIntervalSince1970) #else let ti = (TimeInterval(s.st_mtim.tv_sec) - kCFAbsoluteTimeIntervalSince1970) + (1.0e-9 * TimeInterval(s.st_mtim.tv_nsec)) #endif result[.modificationDate] = Date(timeIntervalSinceReferenceDate: ti) - - result[.posixPermissions] = NSNumber(value: UInt64(s.st_mode & ~S_IFMT)) + + result[.posixPermissions] = NSNumber(value: _filePermissionsMask(mode: UInt32(s.st_mode))) result[.referenceCount] = NSNumber(value: UInt64(s.st_nlink)) result[.systemNumber] = NSNumber(value: UInt64(s.st_dev)) result[.systemFileNumber] = NSNumber(value: UInt64(s.st_ino)) - + +#if os(Windows) + result[.deviceIdentifier] = NSNumber(value: UInt64(s.st_rdev)) + let type = FileAttributeType(attributes: try windowsFileAttributes(atPath: path)) +#else if let pwd = getpwuid(s.st_uid), pwd.pointee.pw_name != nil { let name = String(cString: pwd.pointee.pw_name) result[.ownerAccountName] = name } - + if let grd = getgrgid(s.st_gid), grd.pointee.gr_name != nil { let name = String(cString: grd.pointee.gr_name) result[.groupOwnerAccountName] = name } let type = FileAttributeType(statMode: s.st_mode) +#endif result[.type] = type - + if type == .typeBlockSpecial || type == .typeCharacterSpecial { result[.deviceIdentifier] = NSNumber(value: UInt64(s.st_rdev)) } @@ -929,11 +928,10 @@ open class FileManager : NSObject { #endif result[.ownerAccountID] = NSNumber(value: UInt64(s.st_uid)) result[.groupOwnerAccountID] = NSNumber(value: UInt64(s.st_gid)) -#endif return result } - + /* attributesOfFileSystemForPath:error: returns an NSDictionary of key/value pairs containing the attributes of the filesystem containing the provided path. If this method returns 'nil', an NSError will be returned by reference in the 'error' parameter. This method does not traverse a terminal symlink. This method replaces fileSystemAttributesAtPath:. @@ -1450,61 +1448,58 @@ open class FileManager : NSObject { #if os(Windows) NSUnimplemented() #else - if rmdir(path) == 0 { - return - } else if errno == ENOTEMPTY { - - let stream = URL(fileURLWithPath: path).withUnsafeFileSystemRepresentation { (fsRep) -> UnsafeMutablePointer? in + try _fileSystemRepresentation(withPath: path, { fsRep in + if rmdir(fsRep) == 0 { + return + } else if errno == ENOTEMPTY { let ps = UnsafeMutablePointer?>.allocate(capacity: 2) ps.initialize(to: UnsafeMutablePointer(mutating: fsRep)) ps.advanced(by: 1).initialize(to: nil) - defer { - ps.deinitialize(count: 2) - ps.deallocate() - } - return fts_open(ps, FTS_PHYSICAL | FTS_XDEV | FTS_NOCHDIR, nil) - } - - if stream != nil { - defer { - fts_close(stream) - } + let stream = fts_open(ps, FTS_PHYSICAL | FTS_XDEV | FTS_NOCHDIR, nil) + ps.deinitialize(count: 2) + ps.deallocate() - while let current = fts_read(stream)?.pointee { - let itemPath = string(withFileSystemRepresentation: current.fts_path, length: Int(current.fts_pathlen)) - guard alreadyConfirmed || shouldRemoveItemAtPath(itemPath, isURL: isURL) else { - continue + if stream != nil { + defer { + fts_close(stream) } - - do { - switch Int32(current.fts_info) { - case FTS_DEFAULT, FTS_F, FTS_NSOK, FTS_SL, FTS_SLNONE: - if unlink(current.fts_path) == -1 { - throw _NSErrorWithErrno(errno, reading: false, path: itemPath) + + while let current = fts_read(stream)?.pointee { + let itemPath = string(withFileSystemRepresentation: current.fts_path, length: Int(current.fts_pathlen)) + guard alreadyConfirmed || shouldRemoveItemAtPath(itemPath, isURL: isURL) else { + continue + } + + do { + switch Int32(current.fts_info) { + case FTS_DEFAULT, FTS_F, FTS_NSOK, FTS_SL, FTS_SLNONE: + if unlink(current.fts_path) == -1 { + throw _NSErrorWithErrno(errno, reading: false, path: itemPath) + } + case FTS_DP: + if rmdir(current.fts_path) == -1 { + throw _NSErrorWithErrno(errno, reading: false, path: itemPath) + } + case FTS_DNR, FTS_ERR, FTS_NS: + throw _NSErrorWithErrno(current.fts_errno, reading: false, path: itemPath) + default: + break } - case FTS_DP: - if rmdir(current.fts_path) == -1 { - throw _NSErrorWithErrno(errno, reading: false, path: itemPath) + } catch { + if !shouldProceedAfterError(error, removingItemAtPath: itemPath, isURL: isURL) { + throw error } - case FTS_DNR, FTS_ERR, FTS_NS: - throw _NSErrorWithErrno(current.fts_errno, reading: false, path: itemPath) - default: - break - } - } catch { - if !shouldProceedAfterError(error, removingItemAtPath: itemPath, isURL: isURL) { - throw error } } + } else { + let _ = _NSErrorWithErrno(ENOTEMPTY, reading: false, path: path) } - } else { - let _ = _NSErrorWithErrno(ENOTEMPTY, reading: false, path: path) + } else if errno != ENOTDIR { + throw _NSErrorWithErrno(errno, reading: false, path: path) + } else if unlink(fsRep) != 0 { + throw _NSErrorWithErrno(errno, reading: false, path: path) } - } else if errno != ENOTDIR { - throw _NSErrorWithErrno(errno, reading: false, path: path) - } else if _fileSystemRepresentation(withPath: path, { unlink($0) != 0 }) { - throw _NSErrorWithErrno(errno, reading: false, path: path) - } + }) #endif } @@ -1720,32 +1715,23 @@ open class FileManager : NSObject { #if os(Windows) NSUnimplemented() #else - let fd1 = open(file1Rep, O_RDONLY) - guard fd1 >= 0 else { - return false - } - defer { close(fd1) } - - let fd2 = open(file2Rep, O_RDONLY) - guard fd2 >= 0 else { - return false - } - defer { close(fd2) } - - let buffer1 = UnsafeMutablePointer.allocate(capacity: bufSize) - let buffer2 = UnsafeMutablePointer.allocate(capacity: bufSize) + guard let file1 = FileHandle(fileSystemRepresentation: file1Rep, flags: O_RDONLY, createMode: 0) else { return false } + guard let file2 = FileHandle(fileSystemRepresentation: file2Rep, flags: O_RDONLY, createMode: 0) else { return false } + + var buffer1 = UnsafeMutablePointer.allocate(capacity: bufSize) + var buffer2 = UnsafeMutablePointer.allocate(capacity: bufSize) defer { buffer1.deallocate() buffer2.deallocate() } - var bytesLeft = size while bytesLeft > 0 { let bytesToRead = Int(min(Int64(bufSize), bytesLeft)) - guard read(fd1, buffer1, bytesToRead) == bytesToRead else { + + guard let file1BytesRead = try? file1._readBytes(into: buffer1, length: bytesToRead), file1BytesRead == bytesToRead else { return false } - guard read(fd2, buffer2, bytesToRead) == bytesToRead else { + guard let file2BytesRead = try? file2._readBytes(into: buffer2, length: bytesToRead), file2BytesRead == bytesToRead else { return false } guard memcmp(buffer1, buffer2, bytesToRead) == 0 else { @@ -1804,7 +1790,6 @@ open class FileManager : NSObject { return path1entries.isEmpty } -#if !os(Windows) private func _lstatFile(atPath path: String, withFileSystemRepresentation fsRep: UnsafePointer? = nil) throws -> stat { let _fsRep: UnsafePointer if fsRep == nil { @@ -1818,23 +1803,74 @@ open class FileManager : NSObject { } var statInfo = stat() +#if os(Windows) + let h = path.withCString(encodedAs: UTF16.self) { + CreateFileW(/*lpFileName=*/$0, + /*dwDesiredAccess=*/DWORD(0), + /*dwShareMode=*/DWORD(FILE_SHARE_READ), + /*lpSecurityAttributes=*/nil, + /*dwCreationDisposition=*/DWORD(OPEN_EXISTING), + /*dwFlagsAndAttributes=*/DWORD(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS), + /*hTemplateFile=*/nil) + } + if h == INVALID_HANDLE_VALUE { + throw _NSErrorWithWindowsError(GetLastError(), reading: false) + } + var info: BY_HANDLE_FILE_INFORMATION = BY_HANDLE_FILE_INFORMATION() + GetFileInformationByHandle(h, &info) + // Group id is always 0 on Windows + statInfo.st_gid = 0 + statInfo.st_atime = info.ftLastAccessTime.time_t + statInfo.st_ctime = info.ftCreationTime.time_t + statInfo.st_dev = info.dwVolumeSerialNumber + // inodes have meaning on FAT/HPFS/NTFS + statInfo.st_ino = 0 + statInfo.st_rdev = info.dwVolumeSerialNumber + + let isReparsePoint = info.dwFileAttributes & DWORD(FILE_ATTRIBUTE_REPARSE_POINT) != 0 + let isDir = info.dwFileAttributes & DWORD(FILE_ATTRIBUTE_DIRECTORY) != 0 + let fileMode = isDir ? _S_IFDIR : _S_IFREG + // On a symlink to a directory, Windows sets both the REPARSE_POINT and + // DIRECTORY attributes. Since Windows doesn't provide S_IFLNK and we + // want unix style "symlinks to directories are not directories + // themselves, we say symlinks are regular files + statInfo.st_mode = UInt16(isReparsePoint ? _S_IFREG : fileMode) + let isReadOnly = info.dwFileAttributes & DWORD(FILE_ATTRIBUTE_READONLY) != 0 + statInfo.st_mode |= UInt16(isReadOnly ? _S_IREAD : (_S_IREAD | _S_IWRITE)) + statInfo.st_mode |= UInt16(_S_IEXEC) + + statInfo.st_mtime = info.ftLastWriteTime.time_t + statInfo.st_nlink = Int16(info.nNumberOfLinks) + if info.nFileSizeHigh != 0 { + throw _NSErrorWithErrno(EOVERFLOW, reading: true, path: path) + } + statInfo.st_size = Int32(info.nFileSizeLow) + // Uid is always 0 on Windows systems + statInfo.st_uid = 0 + CloseHandle(h) +#else guard lstat(_fsRep, &statInfo) == 0 else { throw _NSErrorWithErrno(errno, reading: true, path: path) } +#endif return statInfo } -#endif - @available(Windows, deprecated, message: "Not Yet Implemented") - internal func _permissionsOfItem(atPath path: String) throws -> Int { + internal func _filePermissionsMask(mode : UInt32) -> Int { #if os(Windows) - NSUnimplemented() + return Int(mode & ~UInt32(ucrt.S_IFMT)) +#elseif canImport(Darwin) + return Int(mode & ~UInt32(S_IFMT)) #else - let fileInfo = try _lstatFile(atPath: path) - return Int(fileInfo.st_mode & ~S_IFMT) + return Int(mode & ~S_IFMT) #endif } + internal func _permissionsOfItem(atPath path: String) throws -> Int { + let fileInfo = try _lstatFile(atPath: path) + return _filePermissionsMask(mode: UInt32(fileInfo.st_mode)) + } + #if os(Linux) // statx() is only supported by Linux kernels >= 4.11.0 @@ -2377,7 +2413,6 @@ extension FileManager { self.innerEnumerator = ie } - @available(Windows, deprecated, message: "Not Yet Implemented") override func nextObject() -> Any? { let o = innerEnumerator.nextObject() guard let url = o as? URL else { @@ -2385,24 +2420,104 @@ extension FileManager { } #if os(Windows) - NSUnimplemented() + var relativePath = UnsafeMutableBufferPointer.allocate(capacity: Int(MAX_PATH)) + defer { relativePath.deallocate() } + func withURLCString(url: URL, _ f: (UnsafePointer) -> Result?) -> Result? { + return url.withUnsafeFileSystemRepresentation { fsr in + (fsr.flatMap { String(utf8String: $0) })?.withCString(encodedAs: UTF16.self) { f($0) } + } + } + let result = withURLCString(url: baseURL) { pszFrom -> BOOL? in + withURLCString(url: url) { pszTo in + let fromAttrs = GetFileAttributesW(pszFrom) + let toAttrs = GetFileAttributesW(pszTo) + guard fromAttrs != INVALID_FILE_ATTRIBUTES, toAttrs != INVALID_FILE_ATTRIBUTES else { + return FALSE + } + return PathRelativePathToW(relativePath.baseAddress, pszFrom, fromAttrs, pszTo, toAttrs) + } + } + + guard result == TRUE, let (path, _) = String.decodeCString(relativePath.baseAddress, as: UTF16.self) else { + return nil + } #else let path = url.path.replacingOccurrences(of: baseURL.path+"/", with: "") - _currentItemPath = path - return path #endif + _currentItemPath = path + return _currentItemPath } } -#if os(Windows) internal class NSURLDirectoryEnumerator : DirectoryEnumerator { - internal typealias ErrorHandler = /* @escaping */ (URL, Error) -> Bool +#if os(Windows) + var _options : FileManager.DirectoryEnumerationOptions + var _errorHandler : ((URL, Error) -> Bool)? + var _stack: [URL] + var _current: URL? + var _rootDepth : Int - init(url: URL, options: FileManager.DirectoryEnumerationOptions, errorHandler: ErrorHandler?) { + init(url: URL, options: FileManager.DirectoryEnumerationOptions, errorHandler: (/* @escaping */ (URL, Error) -> Bool)?) { + _options = options + _errorHandler = errorHandler + _stack = [url] + _rootDepth = url.pathComponents.count + } + + override func nextObject() -> Any? { + func contentsOfDir(directory: URL) -> [URL]? { + var ffd: WIN32_FIND_DATAW = WIN32_FIND_DATAW() + guard let dirFSR = directory.withUnsafeFileSystemRepresentation({ $0.flatMap { fsr in String(utf8String: fsr) } }) + else { return nil } + let dirPath = joinPath(prefix: dirFSR, suffix: "*") + let h: HANDLE = dirPath.withCString(encodedAs: UTF16.self) { + FindFirstFileW($0, &ffd) + } + guard h != INVALID_HANDLE_VALUE else { return nil } + defer { FindClose(h) } + + var files: [URL] = [] + repeat { + let fileArr = Array( + UnsafeBufferPointer(start: &ffd.cFileName.0, + count: MemoryLayout.size(ofValue: ffd.cFileName))) + let file = String(decodingCString: fileArr, as: UTF16.self) + if file != "." + && file != ".." + && (!_options.contains(.skipsHiddenFiles) + || (ffd.dwFileAttributes & DWORD(FILE_ATTRIBUTE_HIDDEN) == 0)) { + files.append(URL(fileURLWithPath: joinPath(prefix: dirFSR, suffix: file))) + } + } while(FindNextFileW(h, &ffd) != 0) + return files + } + while let url = _stack.popLast() { + if url.hasDirectoryPath && !_options.contains(.skipsSubdirectoryDescendants) { + guard let dirContents = contentsOfDir(directory: url)?.reversed() else { + if let handler = _errorHandler { + let dirFSR = url.withUnsafeFileSystemRepresentation { $0.flatMap { fsr in String(utf8String: fsr) } } + let keepGoing = handler(URL(fileURLWithPath: dirFSR ?? ""), + _NSErrorWithWindowsError(GetLastError(), reading: true)) + if !keepGoing { return nil } + } + continue + } + _stack.append(contentsOf: dirContents) + } + _current = url + return url + } + return nil + } + + override var level: Int { + return _rootDepth - (_current?.pathComponents.count ?? _rootDepth) + } + + override func skipDescendants() { + _options.insert(.skipsSubdirectoryDescendants) } - } #else - internal class NSURLDirectoryEnumerator : DirectoryEnumerator { var _url : URL var _options : FileManager.DirectoryEnumerationOptions var _errorHandler : ((URL, Error) -> Bool)? @@ -2410,13 +2525,14 @@ extension FileManager { var _current : UnsafeMutablePointer? = nil var _rootError : Error? = nil var _gotRoot : Bool = false - + + // See @escaping comments above. init(url: URL, options: FileManager.DirectoryEnumerationOptions, errorHandler: (/* @escaping */ (URL, Error) -> Bool)?) { _url = url _options = options _errorHandler = errorHandler - + if FileManager.default.fileExists(atPath: _url.path) { let fsRep = FileManager.default.fileSystemRepresentation(withPath: _url.path) let ps = UnsafeMutablePointer?>.allocate(capacity: 2) @@ -2430,15 +2546,15 @@ extension FileManager { _rootError = _NSErrorWithErrno(ENOENT, reading: true, url: url) } } - + deinit { if let stream = _stream { fts_close(stream) } } - - override func nextObject() -> Any? { + + override func nextObject() -> Any? { func match(filename: String, to options: DirectoryEnumerationOptions, isDir: Bool) -> (Bool, Bool) { var showFile = true var skipDescendants = false @@ -2459,10 +2575,10 @@ extension FileManager { if let stream = _stream { - + if !_gotRoot { _gotRoot = true - + // Skip the root. _current = fts_read(stream) } @@ -2504,7 +2620,7 @@ extension FileManager { _current = fts_read(stream) } // TODO: Error handling if fts_read fails. - + } else if let error = _rootError { // Was there an error opening the stream? if let handler = _errorHandler { @@ -2513,24 +2629,21 @@ extension FileManager { } return nil } - - override var directoryAttributes : [FileAttributeKey : Any]? { - return nil - } - - override var fileAttributes: [FileAttributeKey : Any]? { - return nil - } - override var level: Int { return Int(_current?.pointee.fts_level ?? 0) } - + override func skipDescendants() { if let stream = _stream, let current = _current { fts_set(stream, current, FTS_SKIP) } } - } #endif + override var directoryAttributes : [FileAttributeKey : Any]? { + return nil + } + override var fileAttributes: [FileAttributeKey : Any]? { + return nil + } + } } diff --git a/Foundation/HTTPCookie.swift b/Foundation/HTTPCookie.swift index 0e9beff8c1e..4f7547e0d89 100644 --- a/Foundation/HTTPCookie.swift +++ b/Foundation/HTTPCookie.swift @@ -255,10 +255,16 @@ open class HTTPCookie : NSObject { /// - Experiment: This is a draft API currently under consideration for official import into Foundation as a suitable alternative /// - Note: Since this API is under consideration it may be either removed or revised in the near future public init?(properties: [HTTPCookiePropertyKey : Any]) { + func stringValue(_ strVal: Any?) -> String? { + if let subStr = strVal as? Substring { + return String(subStr) + } + return strVal as? String + } guard - let path = properties[.path] as? String, - let name = properties[.name] as? String, - let value = properties[.value] as? String + let path = stringValue(properties[.path]), + let name = stringValue(properties[.name]), + let value = stringValue(properties[.value]) else { return nil } @@ -659,4 +665,3 @@ fileprivate extension String { return self.replacingOccurrences(of: "&comma", with: ",") } } - diff --git a/Foundation/Host.swift b/Foundation/Host.swift index f7e68cbdc31..a39cbbb77be 100644 --- a/Foundation/Host.swift +++ b/Foundation/Host.swift @@ -75,7 +75,8 @@ open class Host: NSObject { } open func isEqual(to aHost: Host) -> Bool { - return false + if self === aHost { return true } + return addresses.firstIndex { aHost.addresses.contains($0) } != nil } internal func _resolveCurrent() { diff --git a/Foundation/NSArray.swift b/Foundation/NSArray.swift index 56eefb83942..e2d4f4d3bc8 100644 --- a/Foundation/NSArray.swift +++ b/Foundation/NSArray.swift @@ -952,7 +952,9 @@ extension NSArray : ExpressibleByArrayLiteral { } extension NSArray : CustomReflectable { - public var customMirror: Mirror { NSUnimplemented() } + public var customMirror: Mirror { + return Mirror(reflecting: _storage) + } } extension NSArray : _StructTypeBridgeable { diff --git a/Foundation/NSCalendar.swift b/Foundation/NSCalendar.swift index 47bb95ffafc..5d7ba0cab0b 100644 --- a/Foundation/NSCalendar.swift +++ b/Foundation/NSCalendar.swift @@ -643,9 +643,9 @@ open class NSCalendar : NSObject, NSCopying, NSSecureCoding { open func components(_ unitFlags: Unit, from startingDate: Date, to resultDate: Date, options opts: Options = []) -> DateComponents { let validUnitFlags: NSCalendar.Unit = [ - .era, .year, .month, .day, .hour, .minute, .second, .weekOfYear, .weekOfMonth, .yearForWeekOfYear, .weekday, .weekdayOrdinal ] + .era, .year, .month, .day, .hour, .minute, .second, .nanosecond, .weekOfYear, .weekOfMonth, .yearForWeekOfYear, .weekday, .weekdayOrdinal ] - let invalidUnitFlags: NSCalendar.Unit = [ .quarter, .nanosecond, .timeZone, .calendar] + let invalidUnitFlags: NSCalendar.Unit = [ .quarter, .timeZone, .calendar] // Mask off the unsupported fields let newUnitFlags = Unit(rawValue: unitFlags.rawValue & validUnitFlags.rawValue) @@ -665,10 +665,7 @@ open class NSCalendar : NSObject, NSCopying, NSSecureCoding { let emptyUnitFlags = Unit(rawValue: unitFlags.rawValue & invalidUnitFlags.rawValue) var components = _components(newUnitFlags, vector: ints, addIsLeapMonth: false) - // nanosecond and quarter always get set to zero if requested in the output - if emptyUnitFlags.contains(.nanosecond) { - components.nanosecond = 0 - } + // quarter always gets set to zero if requested in the output if emptyUnitFlags.contains(.quarter) { components.quarter = 0 } diff --git a/Foundation/NSNumber.swift b/Foundation/NSNumber.swift index 48b8dccbb0a..f6b5bd15d99 100644 --- a/Foundation/NSNumber.swift +++ b/Foundation/NSNumber.swift @@ -945,8 +945,8 @@ open class NSNumber : NSValue { // Apply special handling for NaN as <, >, == always return false // when comparing with NaN if lhs.isNaN && rhs.isNaN { return .orderedSame } - if lhs.isNaN { return .orderedAscending } - if rhs.isNaN { return .orderedDescending } + if lhs.isNaN { return rhs < 0 ? .orderedDescending : .orderedAscending } + if rhs.isNaN { return lhs < 0 ? .orderedAscending : .orderedDescending } if lhs < rhs { return .orderedAscending } if lhs > rhs { return .orderedDescending } diff --git a/Foundation/NSPathUtilities.swift b/Foundation/NSPathUtilities.swift index 067b0f8177d..6f7a513507e 100755 --- a/Foundation/NSPathUtilities.swift +++ b/Foundation/NSPathUtilities.swift @@ -14,7 +14,7 @@ public func NSTemporaryDirectory() -> String { let cchLength: DWORD = GetTempPathW(0, nil) var wszPath: [WCHAR] = Array(repeating: 0, count: Int(cchLength + 1)) guard GetTempPathW(DWORD(wszPath.count), &wszPath) <= cchLength else { - precondition(false, "GetTempPathW mutation race") + preconditionFailure("GetTempPathW mutation race") } return String(decodingCString: wszPath, as: UTF16.self) #else diff --git a/Foundation/NSSet.swift b/Foundation/NSSet.swift index a64f62d7038..86368c2a634 100644 --- a/Foundation/NSSet.swift +++ b/Foundation/NSSet.swift @@ -107,9 +107,45 @@ open class NSSet : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCodi return true } - open func description(withLocale locale: Locale?) -> String { - // NSUnimplemented() - return description + override open var description: String { + return description(withLocale: nil) + } + + open func description(withLocale locale: Locale?) -> String { + return description(withLocale: locale, indent: 0) + } + + private func description(withLocale locale: Locale?, indent level: Int) -> String { + var descriptions = [String]() + + for obj in self._storage { + if let string = obj as? String { + descriptions.append(string) + } else if let array = obj as? [Any] { + descriptions.append(NSArray(array: array).description(withLocale: locale, indent: level + 1)) + } else if let dict = obj as? [AnyHashable : Any] { + descriptions.append(dict._bridgeToObjectiveC().description(withLocale: locale, indent: level + 1)) + } else if let set = obj as? Set { + descriptions.append(set._bridgeToObjectiveC().description(withLocale: locale, indent: level + 1)) + } else { + descriptions.append("\(obj)") + } + } + var indent = "" + for _ in 0.. UnsafeRawPointer { @usableFromInline @_cdecl("__CFInitializeSwift") internal func __CFInitializeSwift() { - +#if os(Windows) + __CFSocketInitializeWinSock() +#endif + _CFRuntimeBridgeTypeToClass(CFStringGetTypeID(), unsafeBitCast(_NSCFString.self, to: UnsafeRawPointer.self)) _CFRuntimeBridgeTypeToClass(CFArrayGetTypeID(), unsafeBitCast(_NSCFArray.self, to: UnsafeRawPointer.self)) _CFRuntimeBridgeTypeToClass(CFDictionaryGetTypeID(), unsafeBitCast(_NSCFDictionary.self, to: UnsafeRawPointer.self)) diff --git a/Foundation/NSURL.swift b/Foundation/NSURL.swift index a1b8d933b23..d158d9500b7 100644 --- a/Foundation/NSURL.swift +++ b/Foundation/NSURL.swift @@ -15,6 +15,13 @@ internal let kCFURLPOSIXPathStyle = CFURLPathStyle.cfurlposixPathStyle internal let kCFURLWindowsPathStyle = CFURLPathStyle.cfurlWindowsPathStyle #endif +// NOTE: this represents PLATFORM_PATH_STYLE +#if os(Windows) +internal let kCFURLPlatformPathStyle = kCFURLWindowsPathStyle +#else +internal let kCFURLPlatformPathStyle = kCFURLPOSIXPathStyle +#endif + private func _standardizedPath(_ path: String) -> String { if !path.absolutePath { return path._nsObject.standardizingPath @@ -298,9 +305,9 @@ open class NSURL : NSObject, NSSecureCoding, NSCopying { let thePath = _standardizedPath(path) if thePath.length > 0 { - _CFURLInitWithFileSystemPathRelativeToBase(_cfObject, thePath._cfObject, kCFURLPOSIXPathStyle, isDir, baseURL?._cfObject) + _CFURLInitWithFileSystemPathRelativeToBase(_cfObject, thePath._cfObject, kCFURLPlatformPathStyle, isDir, baseURL?._cfObject) } else if let baseURL = baseURL { - _CFURLInitWithFileSystemPathRelativeToBase(_cfObject, baseURL.path._cfObject, kCFURLPOSIXPathStyle, baseURL.hasDirectoryPath, nil) + _CFURLInitWithFileSystemPathRelativeToBase(_cfObject, baseURL.path._cfObject, kCFURLPlatformPathStyle, baseURL.hasDirectoryPath, nil) } } @@ -347,7 +354,7 @@ open class NSURL : NSObject, NSSecureCoding, NSCopying { } } super.init() - _CFURLInitWithFileSystemPathRelativeToBase(_cfObject, thePath._cfObject, kCFURLPOSIXPathStyle, isDir.boolValue, nil) + _CFURLInitWithFileSystemPathRelativeToBase(_cfObject, thePath._cfObject, kCFURLPlatformPathStyle, isDir.boolValue, nil) } public convenience init(fileURLWithFileSystemRepresentation path: UnsafePointer, isDirectory isDir: Bool, relativeTo baseURL: URL?) { @@ -517,7 +524,7 @@ open class NSURL : NSObject, NSSecureCoding, NSCopying { open var path: String? { let absURL = CFURLCopyAbsoluteURL(_cfObject) - return CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle)?._swiftObject + return CFURLCopyFileSystemPath(absURL, kCFURLPlatformPathStyle)?._swiftObject } open var fragment: String? { @@ -534,7 +541,7 @@ open class NSURL : NSObject, NSSecureCoding, NSCopying { // The same as path if baseURL is nil open var relativePath: String? { - return CFURLCopyFileSystemPath(_cfObject, kCFURLPOSIXPathStyle)?._swiftObject + return CFURLCopyFileSystemPath(_cfObject, kCFURLPlatformPathStyle)?._swiftObject } /* Determines if a given URL string's path represents a directory (i.e. the path component in the URL string ends with a '/' character). This does not check the resource the URL refers to. diff --git a/Foundation/NumberFormatter.swift b/Foundation/NumberFormatter.swift index c3c67901597..08fe3bc2ac4 100644 --- a/Foundation/NumberFormatter.swift +++ b/Foundation/NumberFormatter.swift @@ -1,26 +1,14 @@ // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2016, 2019 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 +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // import CoreFoundation -#if os(macOS) || os(iOS) -internal let kCFNumberFormatterNoStyle = CFNumberFormatterStyle.noStyle -internal let kCFNumberFormatterDecimalStyle = CFNumberFormatterStyle.decimalStyle -internal let kCFNumberFormatterCurrencyStyle = CFNumberFormatterStyle.currencyStyle -internal let kCFNumberFormatterPercentStyle = CFNumberFormatterStyle.percentStyle -internal let kCFNumberFormatterScientificStyle = CFNumberFormatterStyle.scientificStyle -internal let kCFNumberFormatterSpellOutStyle = CFNumberFormatterStyle.spellOutStyle -internal let kCFNumberFormatterOrdinalStyle = CFNumberFormatterStyle.ordinalStyle -internal let kCFNumberFormatterCurrencyISOCodeStyle = CFNumberFormatterStyle.currencyISOCodeStyle -internal let kCFNumberFormatterCurrencyPluralStyle = CFNumberFormatterStyle.currencyPluralStyle -internal let kCFNumberFormatterCurrencyAccountingStyle = CFNumberFormatterStyle.currencyAccountingStyle -#endif extension NumberFormatter { public enum Style : UInt { @@ -55,7 +43,7 @@ extension NumberFormatter { } open class NumberFormatter : Formatter { - + typealias CFType = CFNumberFormatter private var _currentCfFormatter: CFType? private var _cfFormatter: CFType { @@ -67,38 +55,43 @@ open class NumberFormatter : Formatter { #else let numberStyle = CFNumberFormatterStyle(self.numberStyle.rawValue) #endif - + let obj = CFNumberFormatterCreate(kCFAllocatorSystemDefault, locale._cfObject, numberStyle)! _setFormatterAttributes(obj) - if let format = _format { + if _positiveFormat != nil || _negativeFormat != nil { + var format = _positiveFormat ?? "#" + if let negative = _negativeFormat { + format.append(";") + format.append(negative) + } CFNumberFormatterSetFormat(obj, format._cfObject) } _currentCfFormatter = obj return obj } } - + // this is for NSUnitFormatter - + open var formattingContext: Context = .unknown // default is NSFormattingContextUnknown - + // Report the used range of the string and an NSError, in addition to the usual stuff from Formatter /// - Experiment: This is a draft API currently under consideration for official import into Foundation as a suitable alternative /// - Note: Since this API is under consideration it may be either removed or revised in the near future open func objectValue(_ string: String, range: inout NSRange) throws -> Any? { NSUnimplemented() } - + open override func string(for obj: Any) -> String? { //we need to allow Swift's numeric types here - Int, Double et al. guard let number = __SwiftValue.store(obj) as? NSNumber else { return nil } return string(from: number) } - + // Even though NumberFormatter responds to the usual Formatter methods, // here are some convenience methods which are a little more obvious. open func string(from number: NSNumber) -> String? { return CFNumberFormatterCreateStringWithNumber(kCFAllocatorSystemDefault, _cfFormatter, number._cfObject)._swiftObject } - + open func number(from string: String) -> NSNumber? { var range = CFRange(location: 0, length: string.length) let number = withUnsafeMutablePointer(to: &range) { (rangePointer: UnsafeMutablePointer) -> NSNumber? in @@ -114,18 +107,18 @@ open class NumberFormatter : Formatter { } return number } - + open class func localizedString(from num: NSNumber, number nstyle: Style) -> String { let numberFormatter = NumberFormatter() numberFormatter.numberStyle = nstyle return numberFormatter.string(for: num)! } - - internal func _reset() { + + private func _reset() { _currentCfFormatter = nil } - - internal func _setFormatterAttributes(_ formatter: CFNumberFormatter) { + + private func _setFormatterAttributes(_ formatter: CFNumberFormatter) { _setFormatterAttribute(formatter, attributeName: kCFNumberFormatterCurrencyCode, value: _currencyCode?._cfObject) _setFormatterAttribute(formatter, attributeName: kCFNumberFormatterDecimalSeparator, value: _decimalSeparator?._cfObject) _setFormatterAttribute(formatter, attributeName: kCFNumberFormatterCurrencyDecimalSeparator, value: _currencyDecimalSeparator?._cfObject) @@ -168,20 +161,25 @@ open class NumberFormatter : Formatter { _setFormatterAttribute(formatter, attributeName: kCFNumberFormatterMaxSignificantDigits, value: _maximumSignificantDigits._bridgeToObjectiveC()._cfObject) } } - - internal func _setFormatterAttribute(_ formatter: CFNumberFormatter, attributeName: CFString, value: AnyObject?) { + + private func _setFormatterAttribute(_ formatter: CFNumberFormatter, attributeName: CFString, value: AnyObject?) { if let value = value { CFNumberFormatterSetProperty(formatter, attributeName, value) } } - + + private func _getFormatterAttribute(_ formatter: CFNumberFormatter, attributeName: CFString) -> String? { + return CFNumberFormatterCopyProperty(formatter, attributeName) as? String + } + + // Attributes of a NumberFormatter - internal var _numberStyle: Style = .none + private var _numberStyle: Style = .none open var numberStyle: Style { get { return _numberStyle } - + set { switch newValue { case .none, .ordinal, .spellOut: @@ -215,7 +213,7 @@ open class NumberFormatter : Formatter { if _groupingSize == 0 { _groupingSize = 3 } - + case .percent: _usesSignificantDigits = false _usesGroupingSeparator = true @@ -239,8 +237,8 @@ open class NumberFormatter : Formatter { _numberStyle = newValue } } - - internal var _locale: Locale = Locale.current + + private var _locale: Locale = Locale.current /*@NSCopying*/ open var locale: Locale! { get { return _locale @@ -250,8 +248,8 @@ open class NumberFormatter : Formatter { _locale = newValue } } - - internal var _generatesDecimalNumbers: Bool = false + + private var _generatesDecimalNumbers: Bool = false open var generatesDecimalNumbers: Bool { get { return _generatesDecimalNumbers @@ -261,19 +259,8 @@ open class NumberFormatter : Formatter { _generatesDecimalNumbers = newValue } } - - internal var _negativeFormat: String! - open var negativeFormat: String! { - get { - return _negativeFormat - } - set { - _reset() - _negativeFormat = newValue - } - } - - internal var _textAttributesForNegativeValues: [String : Any]? + + private var _textAttributesForNegativeValues: [String : Any]? open var textAttributesForNegativeValues: [String : Any]? { get { return _textAttributesForNegativeValues @@ -283,19 +270,8 @@ open class NumberFormatter : Formatter { _textAttributesForNegativeValues = newValue } } - - internal var _positiveFormat: String! - open var positiveFormat: String! { - get { - return _positiveFormat - } - set { - _reset() - _positiveFormat = newValue - } - } - - internal var _textAttributesForPositiveValues: [String : Any]? + + private var _textAttributesForPositiveValues: [String : Any]? open var textAttributesForPositiveValues: [String : Any]? { get { return _textAttributesForPositiveValues @@ -305,8 +281,8 @@ open class NumberFormatter : Formatter { _textAttributesForPositiveValues = newValue } } - - internal var _allowsFloats: Bool = true + + private var _allowsFloats: Bool = true open var allowsFloats: Bool { get { return _allowsFloats @@ -317,18 +293,18 @@ open class NumberFormatter : Formatter { } } - internal var _decimalSeparator: String! + private var _decimalSeparator: String! open var decimalSeparator: String! { get { - return _decimalSeparator + return _decimalSeparator ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterDecimalSeparator) } set { _reset() _decimalSeparator = newValue } } - - internal var _alwaysShowsDecimalSeparator: Bool = false + + private var _alwaysShowsDecimalSeparator: Bool = false open var alwaysShowsDecimalSeparator: Bool { get { return _alwaysShowsDecimalSeparator @@ -338,19 +314,19 @@ open class NumberFormatter : Formatter { _alwaysShowsDecimalSeparator = newValue } } - - internal var _currencyDecimalSeparator: String! + + private var _currencyDecimalSeparator: String! open var currencyDecimalSeparator: String! { get { - return _currencyDecimalSeparator + return _currencyDecimalSeparator ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterCurrencyDecimalSeparator) } set { _reset() _currencyDecimalSeparator = newValue } } - - internal var _usesGroupingSeparator: Bool = false + + private var _usesGroupingSeparator: Bool = false open var usesGroupingSeparator: Bool { get { return _usesGroupingSeparator @@ -360,21 +336,19 @@ open class NumberFormatter : Formatter { _usesGroupingSeparator = newValue } } - - internal var _groupingSeparator: String! + + private var _groupingSeparator: String! open var groupingSeparator: String! { get { - return _groupingSeparator + return _groupingSeparator ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterGroupingSeparator) } set { _reset() _groupingSeparator = newValue } } - - // - - internal var _zeroSymbol: String? + + private var _zeroSymbol: String? open var zeroSymbol: String? { get { return _zeroSymbol @@ -384,8 +358,8 @@ open class NumberFormatter : Formatter { _zeroSymbol = newValue } } - - internal var _textAttributesForZero: [String : Any]? + + private var _textAttributesForZero: [String : Any]? open var textAttributesForZero: [String : Any]? { get { return _textAttributesForZero @@ -395,8 +369,8 @@ open class NumberFormatter : Formatter { _textAttributesForZero = newValue } } - - internal var _nilSymbol: String = "" + + private var _nilSymbol: String = "" open var nilSymbol: String { get { return _nilSymbol @@ -406,8 +380,8 @@ open class NumberFormatter : Formatter { _nilSymbol = newValue } } - - internal var _textAttributesForNil: [String : Any]? + + private var _textAttributesForNil: [String : Any]? open var textAttributesForNil: [String : Any]? { get { return _textAttributesForNil @@ -417,19 +391,19 @@ open class NumberFormatter : Formatter { _textAttributesForNil = newValue } } - - internal var _notANumberSymbol: String! + + private var _notANumberSymbol: String! open var notANumberSymbol: String! { get { - return _notANumberSymbol + return _notANumberSymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterNaNSymbol) } set { _reset() _notANumberSymbol = newValue } } - - internal var _textAttributesForNotANumber: [String : Any]? + + private var _textAttributesForNotANumber: [String : Any]? open var textAttributesForNotANumber: [String : Any]? { get { return _textAttributesForNotANumber @@ -439,8 +413,8 @@ open class NumberFormatter : Formatter { _textAttributesForNotANumber = newValue } } - - internal var _positiveInfinitySymbol: String = "+∞" + + private var _positiveInfinitySymbol: String = "+∞" open var positiveInfinitySymbol: String { get { return _positiveInfinitySymbol @@ -450,8 +424,8 @@ open class NumberFormatter : Formatter { _positiveInfinitySymbol = newValue } } - - internal var _textAttributesForPositiveInfinity: [String : Any]? + + private var _textAttributesForPositiveInfinity: [String : Any]? open var textAttributesForPositiveInfinity: [String : Any]? { get { return _textAttributesForPositiveInfinity @@ -461,8 +435,8 @@ open class NumberFormatter : Formatter { _textAttributesForPositiveInfinity = newValue } } - - internal var _negativeInfinitySymbol: String = "-∞" + + private var _negativeInfinitySymbol: String = "-∞" open var negativeInfinitySymbol: String { get { return _negativeInfinitySymbol @@ -472,8 +446,8 @@ open class NumberFormatter : Formatter { _negativeInfinitySymbol = newValue } } - - internal var _textAttributesForNegativeInfinity: [String : Any]? + + private var _textAttributesForNegativeInfinity: [String : Any]? open var textAttributesForNegativeInfinity: [String : Any]? { get { return _textAttributesForNegativeInfinity @@ -483,144 +457,140 @@ open class NumberFormatter : Formatter { _textAttributesForNegativeInfinity = newValue } } - - // - - internal var _positivePrefix: String! + + private var _positivePrefix: String! open var positivePrefix: String! { get { - return _positivePrefix + return _positivePrefix ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterPositivePrefix) } set { _reset() _positivePrefix = newValue } } - - internal var _positiveSuffix: String! + + private var _positiveSuffix: String! open var positiveSuffix: String! { get { - return _positiveSuffix + return _positiveSuffix ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterPositiveSuffix) } set { _reset() _positiveSuffix = newValue } } - - internal var _negativePrefix: String! + + private var _negativePrefix: String! open var negativePrefix: String! { get { - return _negativePrefix + return _negativePrefix ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterNegativePrefix) } set { _reset() _negativePrefix = newValue } } - - internal var _negativeSuffix: String! + + private var _negativeSuffix: String! open var negativeSuffix: String! { get { - return _negativeSuffix + return _negativeSuffix ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterNegativeSuffix) } set { _reset() _negativeSuffix = newValue } } - - internal var _currencyCode: String! + + private var _currencyCode: String! open var currencyCode: String! { get { - return _currencyCode + return _currencyCode ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterCurrencyCode) } set { _reset() _currencyCode = newValue } } - - internal var _currencySymbol: String! + + private var _currencySymbol: String! open var currencySymbol: String! { get { - return _currencySymbol + return _currencySymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterCurrencySymbol) } set { _reset() _currencySymbol = newValue } } - - internal var _internationalCurrencySymbol: String! + + private var _internationalCurrencySymbol: String! open var internationalCurrencySymbol: String! { get { - return _internationalCurrencySymbol + return _internationalCurrencySymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterInternationalCurrencySymbol) } set { _reset() _internationalCurrencySymbol = newValue } } - - internal var _percentSymbol: String! + + private var _percentSymbol: String! open var percentSymbol: String! { get { - return _percentSymbol + return _percentSymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterPercentSymbol) ?? "%" } set { _reset() _percentSymbol = newValue } } - - internal var _perMillSymbol: String! + + private var _perMillSymbol: String! open var perMillSymbol: String! { get { - return _perMillSymbol + return _perMillSymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterPerMillSymbol) } set { _reset() _perMillSymbol = newValue } } - - internal var _minusSign: String! + + private var _minusSign: String! open var minusSign: String! { get { - return _minusSign + return _minusSign ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterMinusSign) } set { _reset() _minusSign = newValue } } - - internal var _plusSign: String! + + private var _plusSign: String! open var plusSign: String! { get { - return _plusSign + return _plusSign ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterPlusSign) } set { _reset() _plusSign = newValue } } - - internal var _exponentSymbol: String! + + private var _exponentSymbol: String! open var exponentSymbol: String! { get { - return _exponentSymbol + return _exponentSymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterExponentSymbol) } set { _reset() _exponentSymbol = newValue } } - - // - - internal var _groupingSize: Int = 0 + + private var _groupingSize: Int = 0 open var groupingSize: Int { get { return _groupingSize @@ -630,8 +600,8 @@ open class NumberFormatter : Formatter { _groupingSize = newValue } } - - internal var _secondaryGroupingSize: Int = 0 + + private var _secondaryGroupingSize: Int = 0 open var secondaryGroupingSize: Int { get { return _secondaryGroupingSize @@ -641,8 +611,8 @@ open class NumberFormatter : Formatter { _secondaryGroupingSize = newValue } } - - internal var _multiplier: NSNumber? + + private var _multiplier: NSNumber? /*@NSCopying*/ open var multiplier: NSNumber? { get { return _multiplier @@ -652,8 +622,8 @@ open class NumberFormatter : Formatter { _multiplier = newValue } } - - internal var _formatWidth: Int = 0 + + private var _formatWidth: Int = 0 open var formatWidth: Int { get { return _formatWidth @@ -663,21 +633,19 @@ open class NumberFormatter : Formatter { _formatWidth = newValue } } - - internal var _paddingCharacter: String! + + private var _paddingCharacter: String! open var paddingCharacter: String! { get { - return _paddingCharacter + return _paddingCharacter ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterPaddingCharacter) } set { _reset() _paddingCharacter = newValue } } - - // - - internal var _paddingPosition: PadPosition = .beforePrefix + + private var _paddingPosition: PadPosition = .beforePrefix open var paddingPosition: PadPosition { get { return _paddingPosition @@ -687,8 +655,8 @@ open class NumberFormatter : Formatter { _paddingPosition = newValue } } - - internal var _roundingMode: RoundingMode = .halfEven + + private var _roundingMode: RoundingMode = .halfEven open var roundingMode: RoundingMode { get { return _roundingMode @@ -698,8 +666,8 @@ open class NumberFormatter : Formatter { _roundingMode = newValue } } - - internal var _roundingIncrement: NSNumber! = 0 + + private var _roundingIncrement: NSNumber! = 0 /*@NSCopying*/ open var roundingIncrement: NSNumber! { get { return _roundingIncrement @@ -713,7 +681,7 @@ open class NumberFormatter : Formatter { // Use an optional for _minimumIntegerDigits to track if the value is // set BEFORE the .numberStyle is changed. This allows preserving a setting // of 0. - internal var _minimumIntegerDigits: Int? + private var _minimumIntegerDigits: Int? open var minimumIntegerDigits: Int { get { return _minimumIntegerDigits ?? 0 @@ -723,8 +691,8 @@ open class NumberFormatter : Formatter { _minimumIntegerDigits = newValue } } - - internal var _maximumIntegerDigits: Int = 42 + + private var _maximumIntegerDigits: Int = 42 open var maximumIntegerDigits: Int { get { return _maximumIntegerDigits @@ -734,8 +702,8 @@ open class NumberFormatter : Formatter { _maximumIntegerDigits = newValue } } - - internal var _minimumFractionDigits: Int = 0 + + private var _minimumFractionDigits: Int = 0 open var minimumFractionDigits: Int { get { return _minimumFractionDigits @@ -745,8 +713,8 @@ open class NumberFormatter : Formatter { _minimumFractionDigits = newValue } } - - internal var _maximumFractionDigits: Int = 0 + + private var _maximumFractionDigits: Int = 0 open var maximumFractionDigits: Int { get { return _maximumFractionDigits @@ -756,8 +724,8 @@ open class NumberFormatter : Formatter { _maximumFractionDigits = newValue } } - - internal var _minimum: NSNumber? + + private var _minimum: NSNumber? /*@NSCopying*/ open var minimum: NSNumber? { get { return _minimum @@ -767,8 +735,8 @@ open class NumberFormatter : Formatter { _minimum = newValue } } - - internal var _maximum: NSNumber? + + private var _maximum: NSNumber? /*@NSCopying*/ open var maximum: NSNumber? { get { return _maximum @@ -778,19 +746,19 @@ open class NumberFormatter : Formatter { _maximum = newValue } } - - internal var _currencyGroupingSeparator: String! + + private var _currencyGroupingSeparator: String! open var currencyGroupingSeparator: String! { get { - return _currencyGroupingSeparator + return _currencyGroupingSeparator ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterCurrencyGroupingSeparator) } set { _reset() _currencyGroupingSeparator = newValue } } - - internal var _lenient: Bool = false + + private var _lenient: Bool = false open var isLenient: Bool { get { return _lenient @@ -800,8 +768,8 @@ open class NumberFormatter : Formatter { _lenient = newValue } } - - internal var _usesSignificantDigits: Bool = false + + private var _usesSignificantDigits: Bool = false open var usesSignificantDigits: Bool { get { return _usesSignificantDigits @@ -811,8 +779,8 @@ open class NumberFormatter : Formatter { _usesSignificantDigits = newValue } } - - internal var _minimumSignificantDigits: Int = 1 + + private var _minimumSignificantDigits: Int = 1 open var minimumSignificantDigits: Int { get { return _minimumSignificantDigits @@ -823,8 +791,8 @@ open class NumberFormatter : Formatter { _minimumSignificantDigits = newValue } } - - internal var _maximumSignificantDigits: Int = 6 + + private var _maximumSignificantDigits: Int = 6 open var maximumSignificantDigits: Int { get { return _maximumSignificantDigits @@ -835,8 +803,8 @@ open class NumberFormatter : Formatter { _maximumSignificantDigits = newValue } } - - internal var _partialStringValidationEnabled: Bool = false + + private var _partialStringValidationEnabled: Bool = false open var isPartialStringValidationEnabled: Bool { get { return _partialStringValidationEnabled @@ -846,10 +814,8 @@ open class NumberFormatter : Formatter { _partialStringValidationEnabled = newValue } } - - // - - internal var _hasThousandSeparators: Bool = false + + private var _hasThousandSeparators: Bool = false open var hasThousandSeparators: Bool { get { return _hasThousandSeparators @@ -859,8 +825,8 @@ open class NumberFormatter : Formatter { _hasThousandSeparators = newValue } } - - internal var _thousandSeparator: String! + + private var _thousandSeparator: String! open var thousandSeparator: String! { get { return _thousandSeparator @@ -870,10 +836,8 @@ open class NumberFormatter : Formatter { _thousandSeparator = newValue } } - - // - - internal var _localizesFormat: Bool = true + + private var _localizesFormat: Bool = true open var localizesFormat: Bool { get { return _localizesFormat @@ -883,23 +847,85 @@ open class NumberFormatter : Formatter { _localizesFormat = newValue } } - - // - - internal var _format: String? + + private func getFormatterComponents() -> (String, String) { + let format = CFNumberFormatterGetFormat(_cfFormatter)._swiftObject + let components = format.components(separatedBy: ";") + let positive = _positiveFormat ?? components.first ?? "#" + let negative = _negativeFormat ?? components.last ?? "#" + return (positive, negative) + } + + private func getZeroFormat() -> String { + return string(from: 0) ?? "0" + } + open var format: String { get { - return _format ?? "#" + let (p, n) = getFormatterComponents() + let z = _zeroSymbol ?? getZeroFormat() + return "\(p);\(z);\(n)" + } + set { + // Special case empty string + if newValue == "" { + _positiveFormat = "" + _negativeFormat = "-" + _zeroSymbol = "0" + _reset() + } else { + let components = newValue.components(separatedBy: ";") + let count = components.count + guard count <= 3 else { return } + _reset() + + _positiveFormat = components.first ?? "" + if count == 1 { + _negativeFormat = "-\(_positiveFormat ?? "")" + } + else if count == 2 { + _negativeFormat = components[1] + _zeroSymbol = getZeroFormat() + } + else if count == 3 { + _zeroSymbol = components[1] + _negativeFormat = components[2] + } + + if _negativeFormat == nil { + _negativeFormat = getFormatterComponents().1 + } + + if _zeroSymbol == nil { + _zeroSymbol = getZeroFormat() + } + } + } + } + + private var _positiveFormat: String! + open var positiveFormat: String! { + get { + return getFormatterComponents().0 } set { _reset() - _format = newValue + _positiveFormat = newValue } } - - // - - internal var _attributedStringForZero: NSAttributedString = NSAttributedString(string: "0") + + private var _negativeFormat: String! + open var negativeFormat: String! { + get { + return getFormatterComponents().1 + } + set { + _reset() + _negativeFormat = newValue + } + } + + private var _attributedStringForZero: NSAttributedString = NSAttributedString(string: "0") /*@NSCopying*/ open var attributedStringForZero: NSAttributedString { get { return _attributedStringForZero @@ -909,8 +935,8 @@ open class NumberFormatter : Formatter { _attributedStringForZero = newValue } } - - internal var _attributedStringForNil: NSAttributedString = NSAttributedString(string: "") + + private var _attributedStringForNil: NSAttributedString = NSAttributedString(string: "") /*@NSCopying*/ open var attributedStringForNil: NSAttributedString { get { return _attributedStringForNil @@ -920,8 +946,8 @@ open class NumberFormatter : Formatter { _attributedStringForNil = newValue } } - - internal var _attributedStringForNotANumber: NSAttributedString = NSAttributedString(string: "NaN") + + private var _attributedStringForNotANumber: NSAttributedString = NSAttributedString(string: "NaN") /*@NSCopying*/ open var attributedStringForNotANumber: NSAttributedString { get { return _attributedStringForNotANumber @@ -931,8 +957,8 @@ open class NumberFormatter : Formatter { _attributedStringForNotANumber = newValue } } - - internal var _roundingBehavior: NSDecimalNumberHandler = NSDecimalNumberHandler.default + + private var _roundingBehavior: NSDecimalNumberHandler = NSDecimalNumberHandler.default /*@NSCopying*/ open var roundingBehavior: NSDecimalNumberHandler { get { return _roundingBehavior diff --git a/Foundation/Process.swift b/Foundation/Process.swift index 8b8aa868b4d..da8906ef514 100644 --- a/Foundation/Process.swift +++ b/Foundation/Process.swift @@ -102,6 +102,67 @@ private func processIsEqual(_ a : UnsafeRawPointer?, _ b : UnsafeRawPointer?) -> return true } +#if os(Windows) + +private func quoteWindowsCommandLine(_ commandLine: [String]) -> String { + func quoteWindowsCommandArg(arg: String) -> String { + // Windows escaping, adapted from Daniel Colascione's "Everyone quotes + // command line arguments the wrong way" - Microsoft Developer Blog + if !arg.contains(where: {" \t\n\"".contains($0)}) { + return arg + } + + // To escape the command line, we surround the argument with quotes. However + // the complication comes due to how the Windows command line parser treats + // backslashes (\) and quotes (") + // + // - \ is normally treated as a literal backslash + // - e.g. foo\bar\baz => foo\bar\baz + // - However, the sequence \" is treated as a literal " + // - e.g. foo\"bar => foo"bar + // + // But then what if we are given a path that ends with a \? Surrounding + // foo\bar\ with " would be "foo\bar\" which would be an unterminated string + + // since it ends on a literal quote. To allow this case the parser treats: + // + // - \\" as \ followed by the " metachar + // - \\\" as \ followed by a literal " + // - In general: + // - 2n \ followed by " => n \ followed by the " metachar + // - 2n+1 \ followed by " => n \ followed by a literal " + var quoted = "\"" + var unquoted = arg.unicodeScalars + + while !unquoted.isEmpty { + guard let firstNonBackslash = unquoted.firstIndex(where: { $0 != "\\" }) else { + // String ends with a backslash e.g. foo\bar\, escape all the backslashes + // then add the metachar " below + let backslashCount = unquoted.count + quoted.append(String(repeating: "\\", count: backslashCount * 2)) + break + } + let backslashCount = unquoted.distance(from: unquoted.startIndex, to: firstNonBackslash) + if (unquoted[firstNonBackslash] == "\"") { + // This is a string of \ followed by a " e.g. foo\"bar. Escape the + // backslashes and the quote + quoted.append(String(repeating: "\\", count: backslashCount * 2 + 1)) + quoted.append(String(unquoted[firstNonBackslash])) + } else { + // These are just literal backslashes + quoted.append(String(repeating: "\\", count: backslashCount)) + quoted.append(String(unquoted[firstNonBackslash])) + } + // Drop the backslashes and the following character + unquoted.removeFirst(backslashCount + 1) + } + quoted.append("\"") + return quoted + } + return commandLine.map(quoteWindowsCommandArg).joined(separator: " ") +} +#endif + open class Process: NSObject { private static func setup() { struct Once { @@ -318,8 +379,6 @@ open class Process: NSObject { } #if os(Windows) - // TODO(compnerd) quote the commandline correctly - // https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ var command: [String] = [launchPath] if let arguments = self.arguments { command.append(contentsOf: arguments) @@ -412,7 +471,7 @@ open class Process: NSObject { CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0) CFRunLoopAddSource(managerThreadRunLoop?._cfRunLoop, source, kCFRunLoopDefaultMode) - try command.joined(separator: " ").withCString(encodedAs: UTF16.self) { wszCommandLine in + try quoteWindowsCommandLine(command).withCString(encodedAs: UTF16.self) { wszCommandLine in try currentDirectoryURL.path.withCString(encodedAs: UTF16.self) { wszCurrentDirectory in try szEnvironment.withCString(encodedAs: UTF16.self) { wszEnvironment in if CreateProcessW(nil, UnsafeMutablePointer(mutating: wszCommandLine), diff --git a/Foundation/ProcessInfo.swift b/Foundation/ProcessInfo.swift index a379df9d77f..71e9eaf2a8d 100644 --- a/Foundation/ProcessInfo.swift +++ b/Foundation/ProcessInfo.swift @@ -104,12 +104,26 @@ open class ProcessInfo: NSObject { } versionString = productVersion._swiftObject #elseif os(Windows) - var siVersionInfo: OSVERSIONINFOW = OSVERSIONINFOW() - siVersionInfo.dwOSVersionInfoSize = DWORD(MemoryLayout.size) - if GetVersionExW(&siVersionInfo) == FALSE { - return OperatingSystemVersion(majorVersion: fallbackMajor, minorVersion: fallbackMinor, patchVersion: fallbackPatch) + guard let ntdll = ("ntdll.dll".withCString(encodedAs: UTF16.self) { + LoadLibraryExW($0, nil, DWORD(LOAD_LIBRARY_SEARCH_SYSTEM32)) + }) else { + return OperatingSystemVersion(majorVersion: fallbackMajor, minorVersion: fallbackMinor, patchVersion: fallbackPatch) + } + defer { FreeLibrary(ntdll) } + typealias RTLGetVersionTy = @convention(c) (UnsafeMutablePointer) -> NTSTATUS + guard let pfnRTLGetVersion = unsafeBitCast(GetProcAddress(ntdll, "RtlGetVersion"), to: Optional.self) else { + return OperatingSystemVersion(majorVersion: fallbackMajor, minorVersion: fallbackMinor, patchVersion: fallbackPatch) + } + var osVersionInfo = RTL_OSVERSIONINFOW() + osVersionInfo.dwOSVersionInfoSize = DWORD(MemoryLayout.size) + guard pfnRTLGetVersion(&osVersionInfo) == 0 else { + return OperatingSystemVersion(majorVersion: fallbackMajor, minorVersion: fallbackMinor, patchVersion: fallbackPatch) } - return OperatingSystemVersion(majorVersion: Int(siVersionInfo.dwMajorVersion), minorVersion: Int(siVersionInfo.dwMinorVersion), patchVersion: Int(siVersionInfo.dwBuildNumber)) + return OperatingSystemVersion( + majorVersion: Int(osVersionInfo.dwMajorVersion), + minorVersion: Int(osVersionInfo.dwMinorVersion), + patchVersion: Int(osVersionInfo.dwBuildNumber) + ) #else var utsNameBuffer = utsname() guard uname(&utsNameBuffer) == 0 else { diff --git a/Foundation/URL.swift b/Foundation/URL.swift index 1ee3396e60e..94e377fb346 100644 --- a/Foundation/URL.swift +++ b/Foundation/URL.swift @@ -433,26 +433,16 @@ public struct URL : ReferenceConvertible, Equatable { /// /// Returns `nil` if a `URL` cannot be formed with the string (for example, if the string contains characters that are illegal in a URL, or is an empty string). public init?(string: String) { - guard !string.isEmpty else { return nil } - - if let inner = NSURL(string: string) { - _url = URL._converted(from: inner) - } else { - return nil - } + guard !string.isEmpty, let inner = NSURL(string: string) else { return nil } + _url = URL._converted(from: inner) } /// Initialize with string, relative to another URL. /// /// Returns `nil` if a `URL` cannot be formed with the string (for example, if the string contains characters that are illegal in a URL, or is an empty string). public init?(string: String, relativeTo url: URL?) { - guard !string.isEmpty else { return nil } - - if let inner = NSURL(string: string, relativeTo: url) { - _url = URL._converted(from: inner) - } else { - return nil - } + guard !string.isEmpty, let inner = NSURL(string: string, relativeTo: url) else { return nil } + _url = URL._converted(from: inner) } /// Initializes a newly created file URL referencing the local file or directory at path, relative to a base URL. @@ -750,15 +740,8 @@ public struct URL : ReferenceConvertible, Equatable { /// If the URL has an empty path (e.g., `http://www.example.com`), then this function will return the URL unchanged. public func deletingLastPathComponent() -> URL { // This is a slight behavior change from NSURL, but better than returning "http://www.example.com../". - if path.isEmpty { - return self - } - - if let result = _url.deletingLastPathComponent { - return result - } else { - return self - } + guard !path.isEmpty, let result = _url.deletingLastPathComponent else { return self } + return result } /// Returns a URL constructed by appending the given path extension to self. @@ -768,30 +751,16 @@ public struct URL : ReferenceConvertible, Equatable { /// Certain special characters (for example, Unicode Right-To-Left marks) cannot be used as path extensions. If any of those are contained in `pathExtension`, the function will return the URL unchanged. /// - parameter pathExtension: The extension to append. public func appendingPathExtension(_ pathExtension: String) -> URL { - if path.isEmpty { - return self - } - - if let result = _url.appendingPathExtension(pathExtension) { - return result - } else { - return self - } + guard !path.isEmpty, let result = _url.appendingPathExtension(pathExtension) else { return self } + return result } /// Returns a URL constructed by removing any path extension. /// /// If the URL has an empty path (e.g., `http://www.example.com`), then this function will return the URL unchanged. public func deletingPathExtension() -> URL { - if path.isEmpty { - return self - } - - if let result = _url.deletingPathExtension { - return result - } else { - return self - } + guard !path.isEmpty, let result = _url.deletingPathExtension else { return self } + return result } /// Appends a path component to the URL. @@ -839,11 +808,8 @@ public struct URL : ReferenceConvertible, Equatable { /// Returns a `URL` with any instances of ".." or "." removed from its path. public var standardized : URL { // The NSURL API can only return nil in case of file reference URL, which we should not be - if let result = _url.standardized { - return result - } else { - return self - } + guard let result = _url.standardized else { return self } + return result } /// Standardizes the path of a file URL. @@ -858,11 +824,8 @@ public struct URL : ReferenceConvertible, Equatable { /// If the `isFileURL` is false, this method returns `self`. public var standardizedFileURL : URL { // NSURL should not return nil here unless this is a file reference URL, which should be impossible - if let result = _url.standardizingPath { - return result - } else { - return self - } + guard let result = _url.standardizingPath else { return self } + return result } /// Resolves any symlinks in the path of a file URL. @@ -870,11 +833,8 @@ public struct URL : ReferenceConvertible, Equatable { /// If the `isFileURL` is false, this method returns `self`. public func resolvingSymlinksInPath() -> URL { // NSURL should not return nil here unless this is a file reference URL, which should be impossible - if let result = _url.resolvingSymlinksInPath { - return result - } else { - return self - } + guard let result = _url.resolvingSymlinksInPath else { return self } + return result } /// Resolves any symlinks in the path of a file URL. diff --git a/Foundation/URLCache.swift b/Foundation/URLCache.swift index 561f31cd31c..16e67a40e89 100644 --- a/Foundation/URLCache.swift +++ b/Foundation/URLCache.swift @@ -7,6 +7,7 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // +import SQLite3 /*! @enum URLCache.StoragePolicy @@ -123,6 +124,26 @@ open class CachedURLResponse : NSObject, NSSecureCoding, NSCopying { open class URLCache : NSObject { + private static let sharedSyncQ = DispatchQueue(label: "org.swift.URLCache.sharedSyncQ") + + private static var sharedCache: URLCache? { + willSet { + URLCache.sharedCache?.syncQ.sync { + URLCache.sharedCache?._databaseClient?.close() + URLCache.sharedCache?.flushDatabase() + } + } + didSet { + URLCache.sharedCache?.syncQ.sync { + URLCache.sharedCache?.setupCacheDatabaseIfNotExist() + } + } + } + + private let syncQ = DispatchQueue(label: "org.swift.URLCache.syncQ") + private let _baseDiskPath: String? + private var _databaseClient: _CacheSQLiteClient? + /*! @method sharedURLCache @abstract Returns the shared URLCache instance. @@ -142,10 +163,22 @@ open class URLCache : NSObject { */ open class var shared: URLCache { get { - NSUnimplemented() + return sharedSyncQ.sync { + if let cache = sharedCache { + return cache + } else { + let fourMegaByte = 4 * 1024 * 1024 + let twentyMegaByte = 20 * 1024 * 1024 + let cacheDirectoryPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first?.path ?? "\(NSHomeDirectory())/Library/Caches/" + let path = "\(cacheDirectoryPath)\(Bundle.main.bundleIdentifier ?? UUID().uuidString)" + let cache = URLCache(memoryCapacity: fourMegaByte, diskCapacity: twentyMegaByte, diskPath: path) + sharedCache = cache + return cache + } + } } set { - NSUnimplemented() + sharedSyncQ.sync { sharedCache = newValue } } } @@ -162,7 +195,13 @@ open class URLCache : NSObject { @result an initialized URLCache, with the given capacity, backed by disk. */ - public init(memoryCapacity: Int, diskCapacity: Int, diskPath path: String?) { NSUnimplemented() } + public init(memoryCapacity: Int, diskCapacity: Int, diskPath path: String?) { + self.memoryCapacity = memoryCapacity + self.diskCapacity = diskCapacity + self._baseDiskPath = path + + super.init() + } /*! @method cachedResponseForRequest: @@ -244,6 +283,18 @@ open class URLCache : NSObject { @result the current usage of the on-disk cache of the receiver. */ open var currentDiskUsage: Int { NSUnimplemented() } + + private func flushDatabase() { + guard let path = _baseDiskPath else { return } + + do { + let dbPath = path.appending("/Cache.db") + try FileManager.default.removeItem(atPath: dbPath) + } catch { + fatalError("Unable to flush database for URLCache: \(error.localizedDescription)") + } + } + } extension URLCache { @@ -251,3 +302,113 @@ extension URLCache { public func getCachedResponse(for dataTask: URLSessionDataTask, completionHandler: (CachedURLResponse?) -> Void) { NSUnimplemented() } public func removeCachedResponse(for dataTask: URLSessionDataTask) { NSUnimplemented() } } + +extension URLCache { + + private func setupCacheDatabaseIfNotExist() { + guard let path = _baseDiskPath else { return } + + if !FileManager.default.fileExists(atPath: path) { + do { + try FileManager.default.createDirectory(atPath: path, withIntermediateDirectories: true) + } catch { + fatalError("Unable to create directories for URLCache: \(error.localizedDescription)") + } + } + + // Close the currently opened database connection(if any), before creating/replacing the db file + _databaseClient?.close() + + let dbPath = path.appending("/Cache.db") + if !FileManager.default.createFile(atPath: dbPath, contents: nil, attributes: nil) { + fatalError("Unable to setup database for URLCache") + } + + _databaseClient = _CacheSQLiteClient(databasePath: dbPath) + if _databaseClient == nil { + _databaseClient?.close() + flushDatabase() + fatalError("Unable to setup database for URLCache") + } + + if !createTables() { + _databaseClient?.close() + flushDatabase() + fatalError("Unable to setup database for URLCache: Tables not created") + } + + if !createIndicesForTables() { + _databaseClient?.close() + flushDatabase() + fatalError("Unable to setup database for URLCache: Indices not created for tables") + } + } + + private func createTables() -> Bool { + guard _databaseClient != nil else { + fatalError("Cannot create table before database setup") + } + + let tableSQLs = [ + "CREATE TABLE cfurl_cache_response(entry_ID INTEGER PRIMARY KEY, version INTEGER, hash_value VARCHAR, storage_policy INTEGER, request_key VARCHAR, time_stamp DATETIME, partition VARCHAR)", + "CREATE TABLE cfurl_cache_receiver_data(entry_ID INTEGER PRIMARY KEY, isDataOnFS INTEGER, receiver_data BLOB)", + "CREATE TABLE cfurl_cache_blob_data(entry_ID INTEGER PRIMARY KEY, response_object BLOB, request_object BLOB, proto_props BLOB, user_info BLOB)", + "CREATE TABLE cfurl_cache_schema_version(schema_version INTEGER)" + ] + + for sql in tableSQLs { + if let isSuccess = _databaseClient?.execute(sql: sql), !isSuccess { + return false + } + } + + return true + } + + private func createIndicesForTables() -> Bool { + guard _databaseClient != nil else { + fatalError("Cannot create table before database setup") + } + + let indicesSQLs = [ + "CREATE INDEX proto_props_index ON cfurl_cache_blob_data(entry_ID)", + "CREATE INDEX receiver_data_index ON cfurl_cache_receiver_data(entry_ID)", + "CREATE INDEX request_key_index ON cfurl_cache_response(request_key)", + "CREATE INDEX time_stamp_index ON cfurl_cache_response(time_stamp)" + ] + + for sql in indicesSQLs { + if let isSuccess = _databaseClient?.execute(sql: sql), !isSuccess { + return false + } + } + + return true + } + +} + +fileprivate struct _CacheSQLiteClient { + + private var database: OpaquePointer? + + init?(databasePath: String) { + if sqlite3_open_v2(databasePath, &database, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nil) != SQLITE_OK { + return nil + } + } + + func execute(sql: String) -> Bool { + guard let db = database else { return false } + + return sqlite3_exec(db, sql, nil, nil, nil) == SQLITE_OK + } + + mutating func close() { + guard let db = database else { return } + + sqlite3_close_v2(db) + database = nil + } + +} diff --git a/Foundation/URLSession/BodySource.swift b/Foundation/URLSession/BodySource.swift index f5d3a0a7cd4..577db95223e 100644 --- a/Foundation/URLSession/BodySource.swift +++ b/Foundation/URLSession/BodySource.swift @@ -135,11 +135,12 @@ extension _BodyDataSource : _BodySource { /// have to be thread safe. internal final class _BodyFileSource { fileprivate let fileURL: URL - fileprivate let channel: DispatchIO - fileprivate let workQueue: DispatchQueue + fileprivate let channel: DispatchIO + fileprivate let workQueue: DispatchQueue fileprivate let dataAvailableHandler: () -> Void fileprivate var hasActiveReadHandler = false fileprivate var availableChunk: _Chunk = .empty + /// Create a new data source backed by a file. /// /// - Parameter fileURL: the file to read from @@ -156,13 +157,13 @@ internal final class _BodyFileSource { self.fileURL = fileURL self.workQueue = workQueue self.dataAvailableHandler = dataAvailableHandler - var fileSystemRepresentation: UnsafePointer! = nil - fileURL.withUnsafeFileSystemRepresentation { - fileSystemRepresentation = $0 - } - guard let channel = DispatchIO(type: .stream, path: fileSystemRepresentation, - oflag: O_RDONLY, mode: 0, queue: workQueue, - cleanupHandler: {_ in }) else { + + guard let channel = fileURL.withUnsafeFileSystemRepresentation({ + // DisptachIO (dispatch_io_create_with_path) makes a copy of the path + DispatchIO(type: .stream, path: $0!, + oflag: O_RDONLY, mode: 0, queue: workQueue, + cleanupHandler: {_ in }) + }) else { fatalError("Cant create DispatchIO channel") } self.channel = channel diff --git a/Foundation/URLSession/URLSessionTask.swift b/Foundation/URLSession/URLSessionTask.swift index 1c1c68ac107..f6f5fc128b8 100644 --- a/Foundation/URLSession/URLSessionTask.swift +++ b/Foundation/URLSession/URLSessionTask.swift @@ -574,11 +574,11 @@ extension _ProtocolClient : URLProtocolClient { sender: `protocol` as! _HTTPURLProtocol) task.previousFailureCount += 1 urlProtocol(`protocol`, didReceive: authenticationChallenge) - return } else { let urlError = URLError(_nsError: NSError(domain: NSURLErrorDomain, code: NSURLErrorUserAuthenticationRequired, userInfo: nil)) urlProtocol(`protocol`, didFailWithError: urlError) } + return } switch session.behaviour(for: task) { case .taskDelegate(let delegate): diff --git a/Foundation/XMLParser.swift b/Foundation/XMLParser.swift index c2a17d4ebca..e17b3a083bc 100644 --- a/Foundation/XMLParser.swift +++ b/Foundation/XMLParser.swift @@ -296,13 +296,14 @@ internal func _NSXMLParserStartElementNs(_ ctx: _CFXMLInterface, localname: Unsa var qualifiedName: String? = nil if parser.shouldProcessNamespaces { namespaceURI = UTF8STRING(URI) ?? "" - qualifiedName = elementName if let prefix = UTF8STRING(prefix) { - qualifiedName = elementName + ":" + prefix + qualifiedName = prefix + ":" + elementName + } else { + qualifiedName = elementName } } else if let prefix = UTF8STRING(prefix) { - elementName = elementName + ":" + prefix + elementName = prefix + ":" + elementName } parser.delegate?.parser(parser, didStartElement: elementName, namespaceURI: namespaceURI, qualifiedName: qualifiedName, attributes: attrDict) @@ -316,13 +317,14 @@ internal func _NSXMLParserEndElementNs(_ ctx: _CFXMLInterface , localname: Unsaf var qualifiedName: String? = nil if parser.shouldProcessNamespaces { namespaceURI = UTF8STRING(URI) ?? "" - qualifiedName = elementName if let prefix = UTF8STRING(prefix) { - qualifiedName = elementName + ":" + prefix + qualifiedName = prefix + ":" + elementName + } else { + qualifiedName = elementName } } else if let prefix = UTF8STRING(prefix) { - elementName = elementName + ":" + prefix + elementName = prefix + ":" + elementName } parser.delegate?.parser(parser, didEndElement: elementName, namespaceURI: namespaceURI, qualifiedName: qualifiedName) diff --git a/TestFoundation/HTTPServer.swift b/TestFoundation/HTTPServer.swift index 310d084cc70..d1b77e2a150 100644 --- a/TestFoundation/HTTPServer.swift +++ b/TestFoundation/HTTPServer.swift @@ -156,7 +156,7 @@ class _TCPSocket { let texts = split(body, count) for item in texts { - sleep(UInt32(sendDelay)) + Thread.sleep(forTimeInterval: sendDelay) var bytes = Array(item.utf8) _ = try attempt("send", valid: isNotNegative, CInt(send(connectionSocket, &bytes, bytes.count, sendFlags))) } @@ -335,7 +335,13 @@ class _HTTPServer { try self.socket.writeRawData(responseData) } - + func respondWithUnauthorizedHeader() throws{ + let responseData = ("HTTP/1.1 401 UNAUTHORIZED \r\n" + + "Content-Length: 0\r\n" + + "Connection: keep-Alive\r\n" + + "\r\n").data(using: .utf8)! + try self.socket.writeRawData(responseData) + } } struct _HTTPRequest { @@ -453,6 +459,8 @@ public class TestURLSessionServer { } else if req.uri.hasPrefix("/auth") { httpServer.willReadAgain = true try httpServer.respondWithAuthResponse(uri: req.uri, firstRead: true) + } else if req.uri.hasPrefix("/unauthorized") { + try httpServer.respondWithUnauthorizedHeader() } else { try httpServer.respond(with: process(request: req), startDelay: self.startDelay, sendDelay: self.sendDelay, bodyChunks: self.bodyChunks) } diff --git a/TestFoundation/TestCalendar.swift b/TestFoundation/TestCalendar.swift index 1a848cfa8d3..ca2d0d47eed 100644 --- a/TestFoundation/TestCalendar.swift +++ b/TestFoundation/TestCalendar.swift @@ -215,6 +215,7 @@ class TestNSDateComponents: XCTestCase { ("test_hash", test_hash), ("test_copyNSDateComponents", test_copyNSDateComponents), ("test_dateDifferenceComponents", test_dateDifferenceComponents), + ("test_nanoseconds", test_nanoseconds), ] } @@ -429,4 +430,36 @@ class TestNSDateComponents: XCTestCase { XCTAssertNil(diff9.calendar) XCTAssertNil(diff9.timeZone) } + + func test_nanoseconds() throws { + // 1971-06-21 00:00:00 + let date1 = Date(timeIntervalSince1970: 46310400) + + // 1971-06-21 00:00:00.00123 + let date2 = Date(timeIntervalSince1970: 46310400.00123) + + // 1971-06-24 00:16:40:00123 + let date3 = Date(timeIntervalSince1970: 46570600.45678) + + var calendar = Calendar.current + calendar.timeZone = try TimeZone(abbreviation: "UTC").unwrapped() + + let diff1 = calendar.dateComponents([.nanosecond], from: date1, to: date2) + XCTAssertEqual(diff1.nanosecond, 1230003) + + let diff2 = calendar.dateComponents([.nanosecond], from: date1, to: date2) + XCTAssertEqual(diff2.nanosecond, 1230003) + + let diff3 = calendar.dateComponents([.day, .minute, .second, .nanosecond], from: date2, to: date3) + XCTAssertEqual(diff3.day, 3) + XCTAssertEqual(diff3.minute, 16) + XCTAssertEqual(diff3.second, 40) + XCTAssertEqual(diff3.nanosecond, 455549949) + + let diff4 = calendar.dateComponents([.day, .minute, .second, .nanosecond], from: date3, to: date2) + XCTAssertEqual(diff4.day, -3) + XCTAssertEqual(diff4.minute, -16) + XCTAssertEqual(diff4.second, -40) + XCTAssertEqual(diff4.nanosecond, -455549950) + } } diff --git a/TestFoundation/TestDateIntervalFormatter.swift b/TestFoundation/TestDateIntervalFormatter.swift index 9f1e0e3d980..571b6d8a8e8 100644 --- a/TestFoundation/TestDateIntervalFormatter.swift +++ b/TestFoundation/TestDateIntervalFormatter.swift @@ -8,10 +8,10 @@ // #if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT - #if (os(Linux) || os(Android)) - @testable import Foundation - #else + #if canImport(SwiftFoundation) && !DEPLOYMENT_RUNTIME_OBJC @testable import SwiftFoundation + #else + @testable import Foundation #endif #endif diff --git a/TestFoundation/TestFileHandle.swift b/TestFoundation/TestFileHandle.swift index f7d0db3e597..4b73139b8bd 100644 --- a/TestFoundation/TestFileHandle.swift +++ b/TestFoundation/TestFileHandle.swift @@ -7,6 +7,8 @@ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // +#if !DARWIN_COMPATIBILITY_TESTS // Disable until Foundation has the new FileHandle API + import Dispatch class TestFileHandle : XCTestCase { @@ -318,7 +320,7 @@ class TestFileHandle : XCTestCase { let handle = createFileHandle() handle.readabilityHandler = { _ = $0.offsetInFile } handle.closeFile() - usleep(1000) + Thread.sleep(forTimeInterval: 0.001) } } @@ -327,7 +329,7 @@ class TestFileHandle : XCTestCase { let handle = createFileHandle() handle.readabilityHandler = { _ = try? $0.offset() } try handle.close() - usleep(1000) + Thread.sleep(forTimeInterval: 0.001) } } @@ -484,3 +486,5 @@ class TestFileHandle : XCTestCase { ] } } + +#endif diff --git a/TestFoundation/TestFileManager.swift b/TestFoundation/TestFileManager.swift index 2c24c63b6ba..433000a682b 100644 --- a/TestFoundation/TestFileManager.swift +++ b/TestFoundation/TestFileManager.swift @@ -8,10 +8,10 @@ // #if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT - #if (os(Linux) || os(Android)) - @testable import Foundation - #else + #if canImport(SwiftFoundation) && !DEPLOYMENT_RUNTIME_OBJC @testable import SwiftFoundation + #else + @testable import Foundation #endif #endif diff --git a/TestFoundation/TestHTTPCookie.swift b/TestFoundation/TestHTTPCookie.swift index aee767f6fdf..035e8fe679d 100644 --- a/TestFoundation/TestHTTPCookie.swift +++ b/TestFoundation/TestHTTPCookie.swift @@ -19,6 +19,7 @@ class TestHTTPCookie: XCTestCase { ("test_cookiesWithResponseHeaderNoDomain", test_cookiesWithResponseHeaderNoDomain), ("test_cookiesWithResponseHeaderNoPathNoDomain", test_cookiesWithResponseHeaderNoPathNoDomain), ("test_cookieExpiresDateFormats", test_cookieExpiresDateFormats), + ("test_httpCookieWithSubstring", test_httpCookieWithSubstring), ] } @@ -191,4 +192,13 @@ class TestHTTPCookie: XCTestCase { XCTAssertEqual(cookie.path, "/") } } + + func test_httpCookieWithSubstring() { + let cookie = HTTPCookie(properties: [.domain: ".", .path: "/", .name: "davesy".dropLast(), .value: "Jonesy".dropLast()]) + if let cookie = cookie { + XCTAssertEqual(cookie.name, "daves") + } else { + XCTFail("Unable to create cookie with substring") + } + } } diff --git a/TestFoundation/TestHost.swift b/TestFoundation/TestHost.swift index 6cdb0903635..0fdc79a6e36 100644 --- a/TestFoundation/TestHost.swift +++ b/TestFoundation/TestHost.swift @@ -12,6 +12,7 @@ class TestHost: XCTestCase { static var allTests: [(String, (TestHost) -> () throws -> Void)] { return [ ("test_addressesDoNotGrow", test_addressesDoNotGrow), + ("test_isEqual", test_isEqual) ] } @@ -32,5 +33,21 @@ class TestHost: XCTestCase { let swiftAddressesSecond = swift.addresses XCTAssertEqual(swiftAddressesSecond.count, swiftAddressesFirst.count) } + + func test_isEqual() { + let host0 = Host(address: "8.8.8.8") + let host1 = Host(address: "8.8.8.8") + XCTAssertTrue(host0.isEqual(to: host1)) + + let host2 = Host(address: "8.8.8.9") + XCTAssertFalse(host0.isEqual(to: host2)) + + let swift0 = Host(name: "swift.org") + let swift1 = Host(name: "swift.org") + XCTAssertTrue(swift0.isEqual(to: swift1)) + + let google = Host(name: "google.com") + XCTAssertFalse(swift0.isEqual(to: google)) + } } diff --git a/TestFoundation/TestImports.swift b/TestFoundation/TestImports.swift index 60b48b6cf3e..7df40f90de4 100644 --- a/TestFoundation/TestImports.swift +++ b/TestFoundation/TestImports.swift @@ -9,10 +9,10 @@ // Centralized conditional imports for all test sources -#if DEPLOYMENT_RUNTIME_OBJC || os(Linux) || os(Android) -@_exported import Foundation -@_exported import XCTest -#else +#if !DEPLOYMENT_RUNTIME_OBJC && canImport(SwiftFoundation) && canImport(SwiftXCTest) @_exported import SwiftFoundation @_exported import SwiftXCTest +#else +@_exported import Foundation +@_exported import XCTest #endif diff --git a/TestFoundation/TestNSArray.swift b/TestFoundation/TestNSArray.swift index a02975eb743..738df182643 100644 --- a/TestFoundation/TestNSArray.swift +++ b/TestFoundation/TestNSArray.swift @@ -45,6 +45,7 @@ class TestNSArray : XCTestCase { ("test_replaceObjectsAtIndexesWithObjects", test_replaceObjectsAtIndexesWithObjects), ("test_pathsMatchingExtensions", test_pathsMatchingExtensions), ("test_arrayUsedAsCFArrayInvokesArrayMethods", test_arrayUsedAsCFArrayInvokesArrayMethods), + ("test_customMirror", test_customMirror), ] } @@ -796,6 +797,20 @@ class TestNSArray : XCTestCase { let match5 = paths.pathsMatchingExtensions(["..txt"]) XCTAssertEqual(match5, []) } + + func test_customMirror() { + let inputArray = ["this", "is", "a", "test", "of", "custom", "mirror"] + let array = NSArray(array: inputArray) + let arrayMirror = array.customMirror + + XCTAssertEqual(array[0] as! String, arrayMirror.descendant(0) as! String) + XCTAssertEqual(array[1] as! String, arrayMirror.descendant(1) as! String) + XCTAssertEqual(array[2] as! String, arrayMirror.descendant(2) as! String) + XCTAssertEqual(array[3] as! String, arrayMirror.descendant(3) as! String) + XCTAssertEqual(array[4] as! String, arrayMirror.descendant(4) as! String) + XCTAssertEqual(array[5] as! String, arrayMirror.descendant(5) as! String) + XCTAssertEqual(array[6] as! String, arrayMirror.descendant(6) as! String) + } func test_arrayUsedAsCFArrayInvokesArrayMethods() { let number = 789 as NSNumber diff --git a/TestFoundation/TestNSCalendar.swift b/TestFoundation/TestNSCalendar.swift index 9c6a9cea622..69c1eb39327 100644 --- a/TestFoundation/TestNSCalendar.swift +++ b/TestFoundation/TestNSCalendar.swift @@ -751,6 +751,7 @@ class TestNSCalendar: XCTestCase { } func test_rangeOfWeekendStartDate_interval_containingDate() throws { +#if !DARWIN_COMPATIBILITY_TESTS // NSCalender range(ofWeekendContaining:) is experimental try enumerateTestWeekends() { (calendar, interval) in let startDateResult = calendar.range(ofWeekendContaining: interval.start) XCTAssertEqual(startDateResult, interval) @@ -764,6 +765,7 @@ class TestNSCalendar: XCTestCase { let oneSecondBeforeEndResult = calendar.range(ofWeekendContaining: interval.end - 1) XCTAssertEqual(oneSecondBeforeEndResult, interval) } +#endif } func test_enumerateDatesStartingAfterDate_chineseEra_matchYearOne() throws { diff --git a/TestFoundation/TestNSNumber.swift b/TestFoundation/TestNSNumber.swift index cc1de16c0e0..9fa321f3b04 100644 --- a/TestFoundation/TestNSNumber.swift +++ b/TestFoundation/TestNSNumber.swift @@ -853,6 +853,19 @@ class TestNSNumber : XCTestCase { XCTAssertEqual(NSNumber(value: Float(1)).doubleValue, Double(1)) XCTAssertEqual(NSNumber(value: Float(-37.5)).doubleValue, Double(-37.5)) XCTAssertEqual(NSNumber(value: Float(42.5)).doubleValue, Double(42.5)) + + let nanFloat = NSNumber(value: Float.nan) + XCTAssertTrue(nanFloat.doubleValue.isNaN) + XCTAssertEqual(nanFloat.intValue, Int(bitPattern: 1 << 63)) + XCTAssertEqual(nanFloat.uintValue, UInt(bitPattern: 1 << 63)) + XCTAssertEqual(nanFloat.int8Value, 0) + XCTAssertEqual(nanFloat.uint8Value, 0) + XCTAssertEqual(nanFloat.int16Value, 0) + XCTAssertEqual(nanFloat.uint16Value, 0) + XCTAssertEqual(nanFloat.int32Value, 0) + XCTAssertEqual(nanFloat.uint32Value, 0) + XCTAssertEqual(nanFloat.int64Value, Int64(bitPattern: 1 << 63)) + XCTAssertEqual(nanFloat.uint64Value, UInt64(bitPattern: 1 << 63)) } func test_numberWithDouble() { @@ -885,6 +898,20 @@ class TestNSNumber : XCTestCase { XCTAssertEqual(NSNumber(value: Double(0)).doubleValue, Double(0)) XCTAssertEqual(NSNumber(value: Double(-37.5)).doubleValue, Double(-37.5)) XCTAssertEqual(NSNumber(value: Double(42.1)).doubleValue, Double(42.1)) + + let nanDouble = NSNumber(value: Double.nan) + XCTAssertTrue(nanDouble.floatValue.isNaN) + XCTAssertEqual(nanDouble.intValue, Int(bitPattern: 1 << 63)) + XCTAssertEqual(nanDouble.uintValue, UInt(bitPattern: 1 << 63)) + XCTAssertEqual(nanDouble.int8Value, 0) + XCTAssertEqual(nanDouble.uint8Value, 0) + XCTAssertEqual(nanDouble.int16Value, 0) + XCTAssertEqual(nanDouble.uint16Value, 0) + XCTAssertEqual(nanDouble.int32Value, 0) + XCTAssertEqual(nanDouble.uint32Value, 0) + XCTAssertEqual(nanDouble.int64Value, Int64(bitPattern: 1 << 63)) + XCTAssertEqual(nanDouble.uint64Value, UInt64(bitPattern: 1 << 63)) + } func test_compareNumberWithBool() { @@ -1351,23 +1378,55 @@ class TestNSNumber : XCTestCase { let num = NSNumber(value: Int8.min) XCTAssertFalse(num == NSNumber(value: num.uint64Value)) - let num1 = NSNumber(value: Float.nan) - XCTAssertEqual(num1.compare(num1), .orderedSame) + let zero = NSNumber(value: 0) + let one = NSNumber(value: 1) + let minusOne = NSNumber(value: -1) + let intMin = NSNumber(value: Int.min) + let intMax = NSNumber(value: Int.max) + + let nanFloat = NSNumber(value: Float.nan) + XCTAssertEqual(nanFloat.compare(nanFloat), .orderedSame) + + XCTAssertFalse(nanFloat == zero) + XCTAssertFalse(zero == nanFloat) + XCTAssertEqual(nanFloat.compare(zero), .orderedAscending) + XCTAssertEqual(zero.compare(nanFloat), .orderedDescending) + + XCTAssertEqual(nanFloat.compare(one), .orderedAscending) + XCTAssertEqual(one.compare(nanFloat), .orderedDescending) + + XCTAssertEqual(nanFloat.compare(intMax), .orderedAscending) + XCTAssertEqual(intMax.compare(nanFloat), .orderedDescending) + + XCTAssertEqual(nanFloat.compare(minusOne), .orderedDescending) + XCTAssertEqual(minusOne.compare(nanFloat), .orderedAscending) + + XCTAssertEqual(nanFloat.compare(intMin), .orderedDescending) + XCTAssertEqual(intMin.compare(nanFloat), .orderedAscending) + + + let nanDouble = NSNumber(value: Double.nan) + XCTAssertEqual(nanDouble.compare(nanDouble), .orderedSame) + + XCTAssertFalse(nanDouble == zero) + XCTAssertFalse(zero == nanDouble) + XCTAssertEqual(nanDouble.compare(zero), .orderedAscending) + XCTAssertEqual(zero.compare(nanDouble), .orderedDescending) + + XCTAssertEqual(nanDouble.compare(one), .orderedAscending) + XCTAssertEqual(one.compare(nanDouble), .orderedDescending) + + XCTAssertEqual(nanDouble.compare(intMax), .orderedAscending) + XCTAssertEqual(intMax.compare(nanDouble), .orderedDescending) - let num2 = NSNumber(value: num1.uint8Value) // 0 - XCTAssertFalse(num1 == num2) - XCTAssertFalse(num2 == num1) - XCTAssertEqual(num1.compare(num2), .orderedAscending) - XCTAssertEqual(num2.compare(num1), .orderedDescending) + XCTAssertEqual(nanDouble.compare(minusOne), .orderedDescending) + XCTAssertEqual(minusOne.compare(nanDouble), .orderedAscending) - let num3 = NSNumber(value: Double.nan) - XCTAssertEqual(num3.compare(num3), .orderedSame) + XCTAssertEqual(nanDouble.compare(intMin), .orderedDescending) + XCTAssertEqual(intMin.compare(nanDouble), .orderedAscending) - let num4 = NSNumber(value: num3.intValue) // 0 - XCTAssertFalse(num3 == num2) - XCTAssertFalse(num4 == num3) - XCTAssertEqual(num3.compare(num4), .orderedAscending) - XCTAssertEqual(num4.compare(num3), .orderedDescending) + XCTAssertEqual(nanDouble, nanFloat) + XCTAssertEqual(nanFloat, nanDouble) XCTAssertEqual(NSNumber(value: Double.leastNonzeroMagnitude).compare(NSNumber(value: 0)), .orderedDescending) XCTAssertEqual(NSNumber(value: Double.greatestFiniteMagnitude).compare(NSNumber(value: 0)), .orderedDescending) diff --git a/TestFoundation/TestNSNumberBridging.swift b/TestFoundation/TestNSNumberBridging.swift index ade7bca089a..aace790cfc6 100644 --- a/TestFoundation/TestNSNumberBridging.swift +++ b/TestFoundation/TestNSNumberBridging.swift @@ -626,6 +626,11 @@ class TestNSNumberBridging : XCTestCase { } func testNSNumberToBool() { + // FIXME: When running swift-corelibs-foundation on Darwin, these tests all give the warning: + // 'Conditional cast from 'NSNumber' to 'Bool' always succeeds' and never return nil so these tests fail. + // These tests work on Darwin using normal Foundation (when testing with DarwinCompatibilityTests) + // and also work on Linux, so just disable them on Darwin/swift-corelibs-foundation. +#if !canImport(SwiftFoundation) let b0 = NSNumber(value: 0) as? Bool XCTAssertNotNil(b0) XCTAssertEqual(b0, false) @@ -664,6 +669,7 @@ class TestNSNumberBridging : XCTestCase { XCTAssertNil(NSNumber(value: Int64.max) as? Bool) XCTAssertNil(NSNumber(value: Int.min) as? Bool) XCTAssertNil(NSNumber(value: Int.max) as? Bool) +#endif } } diff --git a/TestFoundation/TestNSSet.swift b/TestFoundation/TestNSSet.swift index d9f99d941a4..47e64d3980e 100644 --- a/TestFoundation/TestNSSet.swift +++ b/TestFoundation/TestNSSet.swift @@ -25,7 +25,8 @@ class TestNSSet : XCTestCase { ("test_CountedSetRemoveObject", test_CountedSetRemoveObject), ("test_CountedSetCopying", test_CountedSetCopying), ("test_mutablesetWithDictionary", test_mutablesetWithDictionary), - ("test_Subsets", test_Subsets) + ("test_Subsets", test_Subsets), + ("test_description", test_description) ] } @@ -237,4 +238,24 @@ class TestNSSet : XCTestCase { XCTAssert(otherSet.isSubset(of: otherOtherSet)) XCTAssertFalse(newSet.isSubset(of: otherSet as! Set)) } + + func test_description() { + let array = NSArray(array: ["array_element1", "array_element2"]) + let dictionary = NSDictionary(dictionary: ["key1": "value1", "key2": "value2"]) + let innerSet = NSSet(array: [4444, 5555]) + let set: NSSet = NSSet(array: [array, dictionary, innerSet, 1111, 2222, 3333]) + + let description = NSString(string: set.description) + + XCTAssertTrue(description.substring(to: 2).isEqual(to: "{(")) + XCTAssertTrue(description.substring(from: description.length - 2).isEqual(to: ")}")) + XCTAssertTrue(description.contains(" (\n array_element1,\n array_element2\n )")) + XCTAssertTrue(description.contains(" key1 = value1")) + XCTAssertTrue(description.contains(" key2 = value2")) + XCTAssertTrue(description.contains(" 4444")) + XCTAssertTrue(description.contains(" 5555")) + XCTAssertTrue(description.contains(" 1111")) + XCTAssertTrue(description.contains(" 2222")) + XCTAssertTrue(description.contains(" 3333")) + } } diff --git a/TestFoundation/TestNumberFormatter.swift b/TestFoundation/TestNumberFormatter.swift index e8435195600..0fa48c52ec9 100644 --- a/TestFoundation/TestNumberFormatter.swift +++ b/TestFoundation/TestNumberFormatter.swift @@ -54,15 +54,28 @@ class TestNumberFormatter: XCTestCase { ("test_maximumSignificantDigits", test_maximumSignificantDigits), ("test_stringFor", test_stringFor), ("test_numberFrom", test_numberFrom), - //("test_en_US_initialValues", test_en_US_initialValues) + ("test_en_US_initialValues", test_en_US_initialValues), + ("test_pt_BR_initialValues", test_pt_BR_initialValues), + ("test_changingLocale", test_changingLocale), + ("test_settingFormat", test_settingFormat), + ("test_usingFormat", test_usingFormat), ] } func test_currencyCode() { let numberFormatter = NumberFormatter() + numberFormatter.locale = Locale(identifier: "en_GB") numberFormatter.numberStyle = .currency + XCTAssertEqual(numberFormatter.format, "¤#,##0.00;£0.00;¤#,##0.00") + XCTAssertEqual(numberFormatter.string(from: 1.1), "£1.10") + XCTAssertEqual(numberFormatter.string(from: 0), "£0.00") + XCTAssertEqual(numberFormatter.string(from: -1.1), "-£1.10") + numberFormatter.currencyCode = "T" + XCTAssertEqual(numberFormatter.format, "¤#,##0.00;T0.00;¤#,##0.00") numberFormatter.currencyDecimalSeparator = "_" + XCTAssertEqual(numberFormatter.format, "¤#,##0.00;T0_00;¤#,##0.00") + let formattedString = numberFormatter.string(from: 42) XCTAssertEqual(formattedString, "T42_00") } @@ -70,9 +83,11 @@ class TestNumberFormatter: XCTestCase { func test_decimalSeparator() { let numberFormatter = NumberFormatter() numberFormatter.numberStyle = .decimal + XCTAssertEqual(numberFormatter.format, "#,##0.###;0;#,##0.###") let separator = "-" numberFormatter.decimalSeparator = separator + XCTAssertEqual(numberFormatter.format, "#,##0.###;0;#,##0.###") XCTAssertEqual(numberFormatter.decimalSeparator, separator) let formattedString = numberFormatter.string(from: 42.42) @@ -84,6 +99,7 @@ class TestNumberFormatter: XCTestCase { numberFormatter.numberStyle = .currency numberFormatter.currencyDecimalSeparator = "-" numberFormatter.currencyCode = "T" + XCTAssertEqual(numberFormatter.format, "¤#,##0.00;T0-00;¤#,##0.00") let formattedString = numberFormatter.string(from: 42.42) XCTAssertEqual(formattedString, "T42-42") } @@ -92,6 +108,7 @@ class TestNumberFormatter: XCTestCase { let numberFormatter = NumberFormatter() numberFormatter.decimalSeparator = "-" numberFormatter.alwaysShowsDecimalSeparator = true + XCTAssertEqual(numberFormatter.format, "#.;0-;#.") let formattedString = numberFormatter.string(from: 42) XCTAssertEqual(formattedString, "42-") } @@ -101,18 +118,23 @@ class TestNumberFormatter: XCTestCase { XCTAssertEqual(decFormatter1.groupingSize, 0) decFormatter1.numberStyle = .decimal XCTAssertEqual(decFormatter1.groupingSize, 3) + XCTAssertEqual(decFormatter1.format, "#,##0.###;0;#,##0.###") let decFormatter2 = NumberFormatter() XCTAssertEqual(decFormatter2.groupingSize, 0) decFormatter2.groupingSize = 1 decFormatter2.numberStyle = .decimal XCTAssertEqual(decFormatter2.groupingSize, 1) + XCTAssertEqual(decFormatter2.format, "#,0.###;0;#,0.###") let numberFormatter = NumberFormatter() numberFormatter.usesGroupingSeparator = true numberFormatter.groupingSeparator = "_" XCTAssertEqual(numberFormatter.groupingSize, 0) + XCTAssertEqual(numberFormatter.format, "#;0;#") numberFormatter.groupingSize = 3 + XCTAssertEqual(numberFormatter.format, "#,###;0;#,###") + let formattedString = numberFormatter.string(from: 42_000) XCTAssertEqual(formattedString, "42_000") } @@ -121,6 +143,8 @@ class TestNumberFormatter: XCTestCase { let numberFormatter = NumberFormatter() numberFormatter.numberStyle = .percent numberFormatter.percentSymbol = "💯" + XCTAssertEqual(numberFormatter.format, "#,##0%;0💯;#,##0%") + let formattedString = numberFormatter.string(from: 0.42) XCTAssertEqual(formattedString, "42💯") } @@ -128,6 +152,8 @@ class TestNumberFormatter: XCTestCase { func test_zeroSymbol() { let numberFormatter = NumberFormatter() numberFormatter.zeroSymbol = "⚽️" + XCTAssertEqual(numberFormatter.format, "#;⚽️;#") + let formattedString = numberFormatter.string(from: 0) XCTAssertEqual(formattedString, "⚽️") } @@ -136,6 +162,7 @@ class TestNumberFormatter: XCTestCase { func test_notANumberSymbol() { let numberFormatter = NumberFormatter() numberFormatter.notANumberSymbol = "👽" + XCTAssertEqual(numberFormatter.format, "#;0;#") let number: Double = -42 let numberObject = NSNumber(value: sqrt(number)) let formattedString = numberFormatter.string(from: numberObject) @@ -145,6 +172,7 @@ class TestNumberFormatter: XCTestCase { func test_positiveInfinitySymbol() { let numberFormatter = NumberFormatter() numberFormatter.positiveInfinitySymbol = "🚀" + XCTAssertEqual(numberFormatter.format, "#;0;#") let numberObject = NSNumber(value: Double(42.0) / Double(0)) let formattedString = numberFormatter.string(from: numberObject) @@ -154,6 +182,7 @@ class TestNumberFormatter: XCTestCase { func test_minusSignSymbol() { let numberFormatter = NumberFormatter() numberFormatter.minusSign = "👎" + XCTAssertEqual(numberFormatter.format, "#;0;#") let formattedString = numberFormatter.string(from: -42) XCTAssertEqual(formattedString, "👎42") } @@ -163,8 +192,12 @@ class TestNumberFormatter: XCTestCase { let numberFormatter = NumberFormatter() let format = "#E+0" numberFormatter.format = format - XCTAssertEqual(numberFormatter.format, format) - + XCTAssertEqual(numberFormatter.positiveFormat, "#E+0") + XCTAssertEqual(numberFormatter.zeroSymbol, "0E+0") + XCTAssertEqual(numberFormatter.negativeFormat, "-#E+0") + XCTAssertEqual(numberFormatter.format, "#E+0;0E+0;-#E+0") + XCTAssertEqual(numberFormatter.string(from: 0), "0E+0") + XCTAssertEqual(numberFormatter.plusSign, "+") let sign = "👍" numberFormatter.plusSign = sign XCTAssertEqual(numberFormatter.plusSign, sign) @@ -189,6 +222,7 @@ class TestNumberFormatter: XCTestCase { numberFormatter.numberStyle = .currency numberFormatter.currencySymbol = "🍯" numberFormatter.currencyDecimalSeparator = "_" + XCTAssertEqual(numberFormatter.format, "¤#,##0.00;🍯0_00;¤#,##0.00") let formattedString = numberFormatter.string(from: 42) XCTAssertEqual(formattedString, "🍯42_00") } @@ -197,6 +231,7 @@ class TestNumberFormatter: XCTestCase { let numberFormatter = NumberFormatter() numberFormatter.numberStyle = .scientific numberFormatter.exponentSymbol = "⬆️" + XCTAssertEqual(numberFormatter.format, "#E0;0⬆️0;#E0") let formattedString = numberFormatter.string(from: 42) XCTAssertEqual(formattedString, "4.2⬆️1") } @@ -207,17 +242,20 @@ class TestNumberFormatter: XCTestCase { numberFormatter1.minimumIntegerDigits = 3 numberFormatter1.numberStyle = .decimal XCTAssertEqual(numberFormatter1.minimumIntegerDigits, 3) + XCTAssertEqual(numberFormatter1.format, "#,000.###;000;#,000.###") let numberFormatter = NumberFormatter() XCTAssertEqual(numberFormatter.minimumIntegerDigits, 0) numberFormatter.numberStyle = .decimal XCTAssertEqual(numberFormatter.minimumIntegerDigits, 1) numberFormatter.minimumIntegerDigits = 3 + XCTAssertEqual(numberFormatter.format, "#,000.###;000;#,000.###") var formattedString = numberFormatter.string(from: 0) XCTAssertEqual(formattedString, "000") numberFormatter.numberStyle = .decimal XCTAssertEqual(numberFormatter.minimumIntegerDigits, 3) + XCTAssertEqual(numberFormatter.format, "#,000.###;000;#,000.###") formattedString = numberFormatter.string(from: 0.1) XCTAssertEqual(formattedString, "000.1") } @@ -225,19 +263,24 @@ class TestNumberFormatter: XCTestCase { func test_currencyMinimumIntegerDigits() { // If .minimumIntegerDigits is set to 0 before .numberStyle change, preserve the value let formatter = NumberFormatter() + formatter.locale = Locale(identifier: "en_GB") XCTAssertEqual(formatter.minimumIntegerDigits, 0) formatter.minimumIntegerDigits = 0 formatter.numberStyle = .currency + XCTAssertEqual(formatter.format, "¤#,###.00;£.00;¤#,###.00") XCTAssertEqual(formatter.minimumIntegerDigits, 0) formatter.locale = Locale(identifier: "en_US") + XCTAssertEqual(formatter.format, "¤#,###.00;$.00;¤#,###.00") XCTAssertEqual(formatter.string(from: 0), "$.00") XCTAssertEqual(formatter.string(from: 1.23), "$1.23") XCTAssertEqual(formatter.string(from: 123.4), "$123.40") // If .minimumIntegerDigits is not set before .numberStyle change, update the value let formatter2 = NumberFormatter() + formatter2.locale = Locale(identifier: "en_GB") XCTAssertEqual(formatter2.minimumIntegerDigits, 0) formatter2.numberStyle = .currency + XCTAssertEqual(formatter2.format, "¤#,##0.00;£0.00;¤#,##0.00") XCTAssertEqual(formatter2.minimumIntegerDigits, 1) formatter2.locale = Locale(identifier: "en_US") XCTAssertEqual(formatter2.string(from: 0.001), "$0.00") @@ -671,5 +714,201 @@ class TestNumberFormatter: XCTestCase { XCTAssertEqual(numberFormatter.exponentSymbol, "E") XCTAssertEqual(numberFormatter.groupingSeparator, ",") XCTAssertEqual(numberFormatter.paddingCharacter, " ") + XCTAssertEqual(numberFormatter.currencyCode, "USD") + XCTAssertEqual(numberFormatter.currencySymbol, "$") + XCTAssertEqual(numberFormatter.currencyDecimalSeparator, ".") + XCTAssertEqual(numberFormatter.currencyGroupingSeparator, ",") + XCTAssertEqual(numberFormatter.internationalCurrencySymbol, "USD") + XCTAssertNil(numberFormatter.zeroSymbol) + } + + func test_pt_BR_initialValues() { + let numberFormatter = NumberFormatter(); + numberFormatter.locale = Locale(identifier: "pt_BR") + + XCTAssertEqual(numberFormatter.format, "#;0;#") + XCTAssertEqual(numberFormatter.plusSign, "+") + XCTAssertEqual(numberFormatter.minusSign, "-") + XCTAssertEqual(numberFormatter.decimalSeparator, ",") + XCTAssertEqual(numberFormatter.groupingSeparator, ".") + XCTAssertEqual(numberFormatter.nilSymbol, "") + XCTAssertEqual(numberFormatter.notANumberSymbol, "NaN") + XCTAssertEqual(numberFormatter.positiveInfinitySymbol, "+∞") + XCTAssertEqual(numberFormatter.negativeInfinitySymbol, "-∞") + XCTAssertEqual(numberFormatter.positivePrefix, "") + XCTAssertEqual(numberFormatter.negativePrefix, "-") + XCTAssertEqual(numberFormatter.positiveSuffix, "") + XCTAssertEqual(numberFormatter.negativeSuffix, "") + XCTAssertEqual(numberFormatter.percentSymbol, "%") + XCTAssertEqual(numberFormatter.perMillSymbol, "‰") + XCTAssertEqual(numberFormatter.exponentSymbol, "E") + XCTAssertEqual(numberFormatter.groupingSeparator, ".") + XCTAssertEqual(numberFormatter.paddingCharacter, " ") + XCTAssertEqual(numberFormatter.currencyCode, "BRL") + XCTAssertEqual(numberFormatter.currencySymbol, "R$") + XCTAssertEqual(numberFormatter.currencyDecimalSeparator, ",") + XCTAssertEqual(numberFormatter.currencyGroupingSeparator, ".") + XCTAssertEqual(numberFormatter.internationalCurrencySymbol, "BRL") + XCTAssertNil(numberFormatter.zeroSymbol) + } + + func test_changingLocale() { + let numberFormatter = NumberFormatter() + numberFormatter.locale = Locale(identifier: "fr_FR") + + XCTAssertEqual(numberFormatter.currencyCode, "EUR") + XCTAssertEqual(numberFormatter.currencySymbol, "€") + numberFormatter.currencySymbol = "E" + XCTAssertEqual(numberFormatter.currencySymbol, "E") + + numberFormatter.locale = Locale(identifier: "fr_FR") + XCTAssertEqual(numberFormatter.currencySymbol, "E") + + numberFormatter.locale = Locale(identifier: "en_GB") + + XCTAssertEqual(numberFormatter.currencyCode, "GBP") + XCTAssertEqual(numberFormatter.currencySymbol, "E") + numberFormatter.currencySymbol = nil + XCTAssertEqual(numberFormatter.currencySymbol, "£") + } + + func test_settingFormat() { + let formatter = NumberFormatter() + + XCTAssertEqual(formatter.format, "#;0;#") + XCTAssertEqual(formatter.positiveFormat, "#") + XCTAssertEqual(formatter.zeroSymbol, nil) + XCTAssertEqual(formatter.negativeFormat, "#") + + formatter.positiveFormat = "#" + XCTAssertEqual(formatter.format, "#;0;#") + XCTAssertEqual(formatter.positiveFormat, "#") + XCTAssertEqual(formatter.zeroSymbol, nil) + XCTAssertEqual(formatter.negativeFormat, "#") + + formatter.positiveFormat = "##.##" + XCTAssertEqual(formatter.format, "##.##;0;#0.##") + XCTAssertEqual(formatter.positiveFormat, "##.##") + XCTAssertEqual(formatter.zeroSymbol, nil) + XCTAssertEqual(formatter.negativeFormat, "#0.##") + + formatter.positiveFormat = "##;##" + XCTAssertEqual(formatter.format, "##;##;0;#") + XCTAssertEqual(formatter.positiveFormat, "##;##") + XCTAssertEqual(formatter.zeroSymbol, nil) + XCTAssertEqual(formatter.negativeFormat, "#") + + formatter.positiveFormat = "+#.#########" + XCTAssertEqual(formatter.format, "+#.#########;+0;+#0.#########") + XCTAssertEqual(formatter.positiveFormat, "+#.#########") + XCTAssertEqual(formatter.zeroSymbol, nil) + XCTAssertEqual(formatter.negativeFormat, "+#0.#########") + + formatter.negativeFormat = "-#.#########" + XCTAssertEqual(formatter.format, "+#.#########;+0;-#.#########") + XCTAssertEqual(formatter.positiveFormat, "+#.#########") + XCTAssertEqual(formatter.zeroSymbol, nil) + XCTAssertEqual(formatter.negativeFormat, "-#.#########") + + formatter.format = "+++#;000;---#.##" + XCTAssertEqual(formatter.format, "+++#;000;---#.##") + XCTAssertEqual(formatter.positiveFormat, "+++#") + XCTAssertEqual(formatter.zeroSymbol, "000") + XCTAssertEqual(formatter.negativeFormat, "---#.##") + + formatter.positiveFormat = nil + XCTAssertEqual(formatter.positiveFormat, "#") + XCTAssertEqual(formatter.format, "#;000;---#.##") + + formatter.zeroSymbol = "00" + formatter.positiveFormat = "+++#.#" + XCTAssertEqual(formatter.format, "+++#.#;00;---#.##") + XCTAssertEqual(formatter.positiveFormat, "+++#.#") + XCTAssertEqual(formatter.zeroSymbol, "00") + XCTAssertEqual(formatter.negativeFormat, "---#.##") + + formatter.negativeFormat = "---#.#" + XCTAssertEqual(formatter.format, "+++#.#;00;---#.#") + XCTAssertEqual(formatter.positiveFormat, "+++#.#") + XCTAssertEqual(formatter.zeroSymbol, "00") + XCTAssertEqual(formatter.negativeFormat, "---#.#") + + // Test setting only first 2 parts + formatter.format = "+##.##;0.00" + XCTAssertEqual(formatter.format, "+##.##;00;0.00") + XCTAssertEqual(formatter.positiveFormat, "+##.##") + XCTAssertEqual(formatter.zeroSymbol, "00") + XCTAssertEqual(formatter.negativeFormat, "0.00") + + formatter.format = "+##.##;+0;0.00" + XCTAssertEqual(formatter.format, "+##.##;+0;0.00") + XCTAssertEqual(formatter.positiveFormat, "+##.##") + XCTAssertEqual(formatter.zeroSymbol, "+0") + XCTAssertEqual(formatter.negativeFormat, "0.00") + + formatter.format = "#;0;#" + formatter.positiveFormat = "1" + XCTAssertEqual(formatter.format, "1;0;#") + XCTAssertEqual(formatter.positiveFormat, "1") + XCTAssertEqual(formatter.zeroSymbol, "0") + XCTAssertEqual(formatter.negativeFormat, "#") + + formatter.format = "1" + XCTAssertEqual(formatter.format, "1;0;-1") + XCTAssertEqual(formatter.positiveFormat, "1") + XCTAssertEqual(formatter.zeroSymbol, "0") + XCTAssertEqual(formatter.negativeFormat, "-1") + + formatter.format = "1;2;3" + XCTAssertEqual(formatter.format, "1;2;3") + XCTAssertEqual(formatter.positiveFormat, "1") + XCTAssertEqual(formatter.zeroSymbol, "2") + XCTAssertEqual(formatter.negativeFormat, "3") + + formatter.format = "" + XCTAssertEqual(formatter.format, ";0;-") + XCTAssertEqual(formatter.positiveFormat, "") + XCTAssertEqual(formatter.zeroSymbol, "0") + XCTAssertEqual(formatter.negativeFormat, "-") + } + + func test_usingFormat() { + var formatter = NumberFormatter() + + formatter.format = "+++#.#;00;-+-#.#" + XCTAssertEqual(formatter.string(from: 1), "+++1") + XCTAssertEqual(formatter.string(from: Int.max as NSNumber), "+++9223372036854775807") + XCTAssertEqual(formatter.string(from: 0), "00") + XCTAssertEqual(formatter.string(from: -1), "-+-1") + XCTAssertEqual(formatter.string(from: Int.min as NSNumber), "-+-9223372036854775808") + + + formatter.format = "+#.##;0.00;-#.##" + XCTAssertEqual(formatter.string(from: 0.5), "+0.5") + XCTAssertEqual(formatter.string(from: 0), "0.00") + XCTAssertEqual(formatter.string(from: -0.2), "-0.2") + + formatter.positiveFormat = "#.##" + formatter.negativeFormat = "-#.##" + XCTAssertEqual(formatter.string(from: NSNumber(value: Double.pi)), "3.14") + XCTAssertEqual(formatter.string(from: NSNumber(value: -Double.pi)), "-3.14") + + formatter = NumberFormatter() + formatter.negativeFormat = "--#.##" + XCTAssertEqual(formatter.string(from: NSNumber(value: -Double.pi)), "--3") + formatter.positiveFormat = "#.###" + XCTAssertEqual(formatter.string(from: NSNumber(value: Double.pi)), "3.142") + + formatter.positiveFormat = "#.####" + XCTAssertEqual(formatter.string(from: NSNumber(value: Double.pi)), "3.1416") + + formatter.positiveFormat = "#.#####" + XCTAssertEqual(formatter.string(from: NSNumber(value: Double.pi)), "3.14159") + + formatter = NumberFormatter() + formatter.negativeFormat = "#.#########" + formatter.positiveFormat = "#.#########" + XCTAssertEqual(formatter.string(from: NSNumber(value: 0.5)), "0.5") + XCTAssertEqual(formatter.string(from: NSNumber(value: -0.5)), "-0.5") } } diff --git a/TestFoundation/TestOperationQueue.swift b/TestFoundation/TestOperationQueue.swift index cb3454cd350..7f67f7276b9 100644 --- a/TestFoundation/TestOperationQueue.swift +++ b/TestFoundation/TestOperationQueue.swift @@ -28,7 +28,7 @@ class TestOperationQueue : XCTestCase { func test_OperationCount() { let queue = OperationQueue() - let op1 = BlockOperation(block: { sleep(2) }) + let op1 = BlockOperation(block: { Thread.sleep(forTimeInterval: 2) }) queue.addOperation(op1) XCTAssertTrue(queue.operationCount == 1) queue.waitUntilAllOperationsAreFinished() @@ -252,7 +252,7 @@ class AsyncOperation: Operation { isExecuting = true queue.async { - sleep(1) + Thread.sleep(forTimeInterval: 1) self.isExecuting = false self.isFinished = true } diff --git a/TestFoundation/TestPipe.swift b/TestFoundation/TestPipe.swift index 7eaff9528af..0c285b581dd 100644 --- a/TestFoundation/TestPipe.swift +++ b/TestFoundation/TestPipe.swift @@ -7,6 +7,7 @@ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // +#if !DARWIN_COMPATIBILITY_TESTS // Disable until Foundation has the new FileHandle API class TestPipe: XCTestCase { static var allTests: [(String, (TestPipe) -> () throws -> Void)] { @@ -42,7 +43,10 @@ class TestPipe: XCTestCase { // First write some data into the pipe let stringAsData = try text.data(using: .utf8).unwrapped() try aPipe.fileHandleForWriting.write(contentsOf: stringAsData) - + + // SR-10240 - Check empty Data() can be written without crashing + aPipe.fileHandleForWriting.write(Data()) + // Then read it out again let data = try aPipe.fileHandleForReading.read(upToCount: stringAsData.count).unwrapped() @@ -57,3 +61,4 @@ class TestPipe: XCTestCase { XCTAssertEqual(text, convertedData) } } +#endif diff --git a/TestFoundation/TestProcess.swift b/TestFoundation/TestProcess.swift index b72c6424986..0850ffafc4b 100644 --- a/TestFoundation/TestProcess.swift +++ b/TestFoundation/TestProcess.swift @@ -281,27 +281,8 @@ class TestProcess : XCTestCase { } } - private func realpathOf(path: String) -> String? { - let fm = FileManager.default - let length = Int(PATH_MAX) + 1 - var buf = [Int8](repeating: 0, count: length) - let fsRep = fm.fileSystemRepresentation(withPath: path) -#if !DARWIN_COMPATIBILITY_TESTS - defer { fsRep.deallocate() } -#endif - guard let result = realpath(fsRep, &buf) else { - return nil - } - return fm.string(withFileSystemRepresentation: result, length: strlen(result)) - } - func test_current_working_directory() { - let tmpDir = "/tmp" - - guard let resolvedTmpDir = realpathOf(path: tmpDir) else { - XCTFail("Cant find realpath of /tmp") - return - } + let tmpDir = "/tmp".standardizingPath let fm = FileManager.default let previousWorkingDirectory = fm.currentDirectoryPath @@ -310,7 +291,7 @@ class TestProcess : XCTestCase { do { let (pwd, _) = try runTask([xdgTestHelperURL().path, "--getcwd"], currentDirectoryPath: tmpDir) // Check the sub-process used the correct directory - XCTAssertEqual(pwd.trimmingCharacters(in: .newlines), resolvedTmpDir) + XCTAssertEqual(pwd.trimmingCharacters(in: .newlines), tmpDir) } catch { XCTFail("Test failed: \(error)") } @@ -536,8 +517,7 @@ class _SignalHelperRunner { let semaphore = DispatchSemaphore(value: 0) private let outputPipe = Pipe() - private let sQueue = DispatchQueue(label: "io queue") - private let source: DispatchSourceRead + private let sQueue = DispatchQueue(label: "signal queue") private var gotReady = false private var bytesIn = Data() @@ -553,17 +533,16 @@ class _SignalHelperRunner { process.arguments = ["--signal-test"] process.standardOutput = outputPipe.fileHandleForWriting - source = DispatchSource.makeReadSource(fileDescriptor: outputPipe.fileHandleForReading.fileDescriptor, queue: sQueue) - let workItem = DispatchWorkItem(block: { [weak self] in + outputPipe.fileHandleForReading.readabilityHandler = { [weak self] fh in if let strongSelf = self { let newLine = UInt8(ascii: "\n") - strongSelf.bytesIn.append(strongSelf.outputPipe.fileHandleForReading.availableData) + strongSelf.bytesIn.append(fh.availableData) if strongSelf.bytesIn.isEmpty { return } // Split the incoming data into lines. - while let index = strongSelf.bytesIn.index(of: newLine) { + while let index = strongSelf.bytesIn.firstIndex(of: newLine) { if index >= strongSelf.bytesIn.startIndex { // dont include the newline when converting to string let line = String(data: strongSelf.bytesIn[strongSelf.bytesIn.startIndex.. Int32 { + assert(overwrite == 1) + return putenv("\(key)=\(value)") + } +#endif + let preset = ProcessInfo.processInfo.environment["test"] setenv("test", "worked", 1) let postset = ProcessInfo.processInfo.environment["test"] diff --git a/TestFoundation/TestNSProgressFraction.swift b/TestFoundation/TestProgressFraction.swift similarity index 91% rename from TestFoundation/TestNSProgressFraction.swift rename to TestFoundation/TestProgressFraction.swift index 05a6e3817aa..1b371b570d7 100644 --- a/TestFoundation/TestNSProgressFraction.swift +++ b/TestFoundation/TestProgressFraction.swift @@ -1,16 +1,19 @@ // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2016, 2019 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 +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // -#if !DARWIN_COMPATIBILITY_TESTS + class TestProgressFraction : XCTestCase { - static var allTests: [(String, (TestProgressFraction) -> () throws -> Void)] { - return [ + +#if !NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT // _ProgressFraction is an internal type + static let allTests: [(String, (TestProgressFraction) -> () throws -> Void)] = [] +#else + static let allTests: [(String, (TestProgressFraction) -> () throws -> Void)] = [ ("test_equal", test_equal ), ("test_subtract", test_subtract), ("test_multiply", test_multiply), @@ -21,7 +24,6 @@ class TestProgressFraction : XCTestCase { ("test_fractionFromDouble", test_fractionFromDouble), ("test_unnecessaryOverflow", test_unnecessaryOverflow), ] - } func test_equal() { let f1 = _ProgressFraction(completed: 5, total: 10) @@ -151,6 +153,7 @@ class TestProgressFraction : XCTestCase { let r = f1 + f2 XCTAssertFalse(r.overflowed) } -} #endif +} + diff --git a/TestFoundation/TestScanner.swift b/TestFoundation/TestScanner.swift index d418b8164f3..4678569b3f7 100644 --- a/TestFoundation/TestScanner.swift +++ b/TestFoundation/TestScanner.swift @@ -7,6 +7,8 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // +#if !DARWIN_COMPATIBILITY_TESTS // Disable until Foundation has the new Scanner API + fileprivate func withScanner(for string: String, invoking block: ((Scanner) throws -> Void)? = nil) rethrows { let scanner = Scanner(string: string) scanner.locale = Locale(identifier: "en_US_POSIX") @@ -481,3 +483,5 @@ class TestScanner : XCTestCase { ] } } + +#endif diff --git a/TestFoundation/TestStream.swift b/TestFoundation/TestStream.swift index c9763e6d366..6f8216a9a6f 100644 --- a/TestFoundation/TestStream.swift +++ b/TestFoundation/TestStream.swift @@ -7,10 +7,10 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // -#if (os(Linux) || os(Android)) - @testable import Foundation -#else +#if canImport(SwiftFoundation) && !DEPLOYMENT_RUNTIME_OBJC @testable import SwiftFoundation +#else + @testable import Foundation #endif @@ -33,13 +33,12 @@ private extension Data { class TestStream : XCTestCase { static var allTests: [(String, (TestStream) -> () throws -> Void)] { - return [ + var tests: [(String, (TestStream) -> () throws -> Void)] = [ ("test_InputStreamWithData", test_InputStreamWithData), ("test_InputStreamWithUrl", test_InputStreamWithUrl), ("test_InputStreamWithFile", test_InputStreamWithFile), ("test_InputStreamHasBytesAvailable", test_InputStreamHasBytesAvailable), ("test_InputStreamInvalidPath", test_InputStreamInvalidPath), - ("test_InputStreamSeekToPosition", test_InputStreamSeekToPosition), ("test_outputStreamCreationToFile", test_outputStreamCreationToFile), ("test_outputStreamCreationToBuffer", test_outputStreamCreationToBuffer), ("test_outputStreamCreationWithUrl", test_outputStreamCreationWithUrl), @@ -47,6 +46,11 @@ class TestStream : XCTestCase { ("test_outputStreamHasSpaceAvailable", test_outputStreamHasSpaceAvailable), ("test_ouputStreamWithInvalidPath", test_ouputStreamWithInvalidPath), ] + +#if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT + tests.append(("test_InputStreamSeekToPosition", test_InputStreamSeekToPosition)) +#endif + return tests } func test_InputStreamWithData(){ @@ -140,7 +144,8 @@ class TestStream : XCTestCase { fileStream.open() XCTAssertEqual(.error, fileStream.streamStatus) } - + +#if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT // Stream.seek(to:) is an internal API method func test_InputStreamSeekToPosition() { let str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras congue laoreet facilisis. Sed porta tristique orci. Fusce ut nisl dignissim, tempor tortor id, molestie neque. Nam non tincidunt mi. Integer ac diam quis leo aliquam congue et non magna. In porta mauris suscipit erat pulvinar, sed fringilla quam ornare. Nulla vulputate et ligula vitae sollicitudin. Nulla vel vehicula risus. Quisque eu urna ullamcorper, tincidunt ante vitae, aliquet sem. Suspendisse nec turpis placerat, porttitor ex vel, tristique orci. Maecenas pretium, augue non elementum imperdiet, diam ex vestibulum tortor, non ultrices ante enim iaculis ex. Fusce ut nisl dignissim, tempor tortor id, molestie neque. Nam non tincidunt mi. Integer ac diam quis leo aliquam congue et non magna. In porta mauris suscipit erat pulvinar, sed fringilla quam ornare. Nulla vulputate et ligula vitae sollicitudin. Nulla vel vehicula risus. Quisque eu urna ullamcorper, tincidunt ante vitae, aliquet sem. Suspendisse nec turpis placerat, porttitor ex vel, tristique orci. Maecenas pretium, augue non elementum imperdiet, diam ex vestibulum tortor, non ultrices ante enim iaculis ex.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras congue laoreet facilisis. Sed porta tristique orci. Fusce ut nisl dignissim, tempor tortor id, molestie neque. Nam non tincidunt mi. Integer ac diam quis leo aliquam congue et non magna. In porta mauris suscipit erat pulvinar, sed fringilla quam ornare. Nulla vulputate et ligula vitae sollicitudin. Nulla vel vehicula risus. Quisque eu urna ullamcorper, tincidunt ante vitae, aliquet sem. Suspendisse nec turpis placerat, porttitor ex vel." XCTAssert(str.count > 1024) // str.count must be bigger than buffersize inside InputStream.seek func. @@ -184,6 +189,7 @@ class TestStream : XCTestCase { XCTFail() } } +#endif func test_outputStreamCreationToFile() { guard let filePath = createTestFile("TestFileOut.txt", _contents: Data(capacity: 256)) else { diff --git a/TestFoundation/TestURLCache.swift b/TestFoundation/TestURLCache.swift new file mode 100644 index 00000000000..dcfe10fb245 --- /dev/null +++ b/TestFoundation/TestURLCache.swift @@ -0,0 +1,92 @@ +// +// TestURLCache.swift +// TestFoundation +// +// Created by Karthikkeyan Bala Sundaram on 3/8/19. +// Copyright © 2019 Apple. All rights reserved. +// + +import SQLite3 + +class TestURLCache: XCTestCase { + + static var allTests: [(String, (TestURLCache) -> () throws -> Void)] { + return [ + ("test_cacheFileAndDirectorySetup", test_cacheFileAndDirectorySetup), + ("test_cacheDatabaseTables", test_cacheDatabaseTables), + ("test_cacheDatabaseIndices", test_cacheDatabaseIndices), + ] + } + + private var cacheDirectoryPath: String { + if let path = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first?.path { + return "\(path)/org.swift.TestFoundation" + } else { + return "\(NSHomeDirectory())/Library/Caches/org.swift.TestFoundation" + } + } + + private var cacheDatabasePath: String { + return "\(cacheDirectoryPath)/Cache.db" + } + + func test_cacheFileAndDirectorySetup() { + let _ = URLCache.shared + + XCTAssertTrue(FileManager.default.fileExists(atPath: cacheDirectoryPath)) + XCTAssertTrue(FileManager.default.fileExists(atPath: cacheDatabasePath)) + } + + func test_cacheDatabaseTables() { + let _ = URLCache.shared + + var db: OpaquePointer? = nil + let openDBResult = sqlite3_open_v2(cacheDatabasePath, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nil) + XCTAssertTrue(openDBResult == SQLITE_OK, "Unable to open database") + + var statement: OpaquePointer? = nil + let prepareResult = sqlite3_prepare_v2(db!, "select tbl_name from sqlite_master where type='table'", -1, &statement, nil) + XCTAssertTrue(prepareResult == SQLITE_OK, "Unable to prepare list tables statement") + + var tables = ["cfurl_cache_response": false, "cfurl_cache_receiver_data": false, "cfurl_cache_blob_data": false, "cfurl_cache_schema_version": false] + while sqlite3_step(statement!) == SQLITE_ROW { + let tableName = String(cString: sqlite3_column_text(statement!, 0)) + tables[tableName] = true + } + + let tablesNotExist = tables.filter({ !$0.value }) + if tablesNotExist.count == tables.count { + XCTFail("No tables created for URLCache") + } + + XCTAssertTrue(tablesNotExist.count == 0, "Table(s) not created: \(tablesNotExist.map({ $0.key }).joined(separator: ", "))") + sqlite3_close_v2(db!) + } + + func test_cacheDatabaseIndices() { + let _ = URLCache.shared + + var db: OpaquePointer? = nil + let openDBResult = sqlite3_open_v2(cacheDatabasePath, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nil) + XCTAssertTrue(openDBResult == SQLITE_OK, "Unable to open database") + + var statement: OpaquePointer? = nil + let prepareResult = sqlite3_prepare_v2(db!, "select name from sqlite_master where type='index'", -1, &statement, nil) + XCTAssertTrue(prepareResult == SQLITE_OK, "Unable to prepare list tables statement") + + var indices = ["proto_props_index": false, "receiver_data_index": false, "request_key_index": false, "time_stamp_index": false] + while sqlite3_step(statement!) == SQLITE_ROW { + let name = String(cString: sqlite3_column_text(statement!, 0)) + indices[name] = true + } + + let indicesNotExist = indices.filter({ !$0.value }) + if indicesNotExist.count == indices.count { + XCTFail("No index created for URLCache") + } + + XCTAssertTrue(indicesNotExist.count == 0, "Indices not created: \(indicesNotExist.map({ $0.key }).joined(separator: ", "))") + sqlite3_close_v2(db!) + } + +} diff --git a/TestFoundation/TestURLResponse.swift b/TestFoundation/TestURLResponse.swift index f2797ba4577..84a81f5c0cd 100644 --- a/TestFoundation/TestURLResponse.swift +++ b/TestFoundation/TestURLResponse.swift @@ -11,11 +11,10 @@ class TestURLResponse : XCTestCase { static var allTests: [(String, (TestURLResponse) -> () throws -> Void)] { return [ ("test_URL", test_URL), - ("test_MIMEType_1", test_MIMEType_1), - ("test_MIMEType_2", test_MIMEType_2), + ("test_MIMEType", test_MIMEType), ("test_ExpectedContentLength", test_ExpectedContentLength), ("test_TextEncodingName", test_TextEncodingName), - ("test_suggestedFilename", test_suggestedFilename), + ("test_suggestedFilename_1", test_suggestedFilename_1), ("test_suggestedFilename_2", test_suggestedFilename_2), ("test_suggestedFilename_3", test_suggestedFilename_3), ("test_copywithzone", test_copyWithZone), @@ -23,44 +22,52 @@ class TestURLResponse : XCTestCase { ] } + let testURL = URL(string: "test")! + func test_URL() { let url = URL(string: "a/test/path")! let res = URLResponse(url: url, mimeType: "txt", expectedContentLength: 0, textEncodingName: nil) XCTAssertEqual(res.url, url, "should be the expected url") } - func test_MIMEType_1() { - let mimetype = "text/plain" - let res = URLResponse(url: URL(string: "test")!, mimeType: mimetype, expectedContentLength: 0, textEncodingName: nil) + func test_MIMEType() { + var mimetype: String? = "text/plain" + var res = URLResponse(url: testURL, mimeType: mimetype, expectedContentLength: 0, textEncodingName: nil) XCTAssertEqual(res.mimeType, mimetype, "should be the passed in mimetype") - } - - func test_MIMEType_2() { - let mimetype = "APPlication/wordperFECT" - let res = URLResponse(url: URL(string: "test")!, mimeType: mimetype, expectedContentLength: 0, textEncodingName: nil) + + mimetype = "APPlication/wordperFECT" + res = URLResponse(url: testURL, mimeType: mimetype, expectedContentLength: 0, textEncodingName: nil) + XCTAssertEqual(res.mimeType, mimetype, "should be the other mimetype") + + mimetype = nil + res = URLResponse(url: testURL, mimeType: mimetype, expectedContentLength: 0, textEncodingName: nil) XCTAssertEqual(res.mimeType, mimetype, "should be the other mimetype") } - + func test_ExpectedContentLength() { - let zeroContentLength = 0 - let positiveContentLength = 100 - let url = URL(string: "test")! - let res1 = URLResponse(url: url, mimeType: "text/plain", expectedContentLength: zeroContentLength, textEncodingName: nil) - XCTAssertEqual(res1.expectedContentLength, Int64(zeroContentLength), "should be Int65 of the zero length") - let res2 = URLResponse(url: url, mimeType: "text/plain", expectedContentLength: positiveContentLength, textEncodingName: nil) - XCTAssertEqual(res2.expectedContentLength, Int64(positiveContentLength), "should be Int64 of the positive content length") + var contentLength = 100 + var res = URLResponse(url: testURL, mimeType: "text/plain", expectedContentLength: contentLength, textEncodingName: nil) + XCTAssertEqual(res.expectedContentLength, Int64(contentLength), "should be positive Int64 content length") + + contentLength = 0 + res = URLResponse(url: testURL, mimeType: nil, expectedContentLength: contentLength, textEncodingName: nil) + XCTAssertEqual(res.expectedContentLength, Int64(contentLength), "should be zero Int64 content length") + + contentLength = -1 + res = URLResponse(url: testURL, mimeType: nil, expectedContentLength: contentLength, textEncodingName: nil) + XCTAssertEqual(res.expectedContentLength, Int64(contentLength), "should be invalid (-1) Int64 content length") } func test_TextEncodingName() { let encoding = "utf8" - let url = URL(string: "test")! - let res1 = URLResponse(url: url, mimeType: nil, expectedContentLength: 0, textEncodingName: encoding) - XCTAssertEqual(res1.textEncodingName, encoding, "should be the utf8 encoding") - let res2 = URLResponse(url: url, mimeType: nil, expectedContentLength: 0, textEncodingName: nil) - XCTAssertNil(res2.textEncodingName) + var res = URLResponse(url: testURL, mimeType: nil, expectedContentLength: 0, textEncodingName: encoding) + XCTAssertEqual(res.textEncodingName, encoding, "should be the utf8 encoding") + + res = URLResponse(url: testURL, mimeType: nil, expectedContentLength: 0, textEncodingName: nil) + XCTAssertNil(res.textEncodingName) } - func test_suggestedFilename() { + func test_suggestedFilename_1() { let url = URL(string: "a/test/name.extension")! let res = URLResponse(url: url, mimeType: "txt", expectedContentLength: 0, textEncodingName: nil) XCTAssertEqual(res.suggestedFilename, "name.extension") @@ -77,6 +84,7 @@ class TestURLResponse : XCTestCase { let res = URLResponse(url: url, mimeType: "txt", expectedContentLength: 0, textEncodingName: nil) XCTAssertEqual(res.suggestedFilename, "Unknown") } + func test_copyWithZone() { let url = URL(string: "a/test/path")! let res = URLResponse(url: url, mimeType: "txt", expectedContentLength: 0, textEncodingName: nil) diff --git a/TestFoundation/TestURLSession.swift b/TestFoundation/TestURLSession.swift index eb4a3ad4717..c31a907f746 100644 --- a/TestFoundation/TestURLSession.swift +++ b/TestFoundation/TestURLSession.swift @@ -47,6 +47,7 @@ class TestURLSession : LoopbackServerTest { ("test_basicAuthRequest", test_basicAuthRequest), ("test_redirectionWithSetCookies", test_redirectionWithSetCookies), ("test_postWithEmptyBody", test_postWithEmptyBody), + ("test_basicAuthWithUnauthorizedHeader", test_basicAuthWithUnauthorizedHeader), ] } @@ -523,7 +524,7 @@ class TestURLSession : LoopbackServerTest { XCTAssertNil(response) XCTAssertNotNil(error) - defer { expect.fulfill() } + expect.fulfill() } task.resume() waitForExpectations(timeout: 12) @@ -758,6 +759,20 @@ class TestURLSession : LoopbackServerTest { task.resume() waitForExpectations(timeout: 30) } + + func test_basicAuthWithUnauthorizedHeader() { + let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/unauthorized" + let url = URL(string: urlString)! + let expect = expectation(description: "GET \(urlString): with a completion handler") + var expectedResult = "unknown" + let session = URLSession(configuration: URLSessionConfiguration.default) + let task = session.dataTask(with: url) { _, _, error in + defer { expect.fulfill() } + XCTAssertNotNil(error) + } + task.resume() + waitForExpectations(timeout: 12, handler: nil) + } } class SharedDelegate: NSObject { diff --git a/TestFoundation/TestXMLDocument.swift b/TestFoundation/TestXMLDocument.swift index 53ed6f7fbad..d7437ed4f6e 100644 --- a/TestFoundation/TestXMLDocument.swift +++ b/TestFoundation/TestXMLDocument.swift @@ -13,7 +13,8 @@ class TestXMLDocument : LoopbackServerTest { return [ ("test_basicCreation", test_basicCreation), ("test_nextPreviousNode", test_nextPreviousNode), - ("test_xpath", test_xpath), + // Disabled because of https://bugs.swift.org/browse/SR-10098 + // ("test_xpath", test_xpath), ("test_elementCreation", test_elementCreation), ("test_elementChildren", test_elementChildren), ("test_stringValue", test_stringValue), diff --git a/TestFoundation/TestXMLParser.swift b/TestFoundation/TestXMLParser.swift index 9c0516d8e05..d7214f9f340 100644 --- a/TestFoundation/TestXMLParser.swift +++ b/TestFoundation/TestXMLParser.swift @@ -66,6 +66,7 @@ class TestXMLParser : XCTestCase { ("test_withDataEncodings", test_withDataEncodings), ("test_withDataOptions", test_withDataOptions), ("test_sr9758_abortParsing", test_sr9758_abortParsing), + ("test_sr10157_swappedElementNames", test_sr10157_swappedElementNames), ] } @@ -154,4 +155,49 @@ class TestXMLParser : XCTestCase { XCTAssertNotNil(parser.parserError) } + func test_sr10157_swappedElementNames() { + class ElementNameChecker: NSObject, XMLParserDelegate { + let name: String + init(_ name: String) { self.name = name } + func parser(_ parser: XMLParser, + didStartElement elementName: String, + namespaceURI: String?, + qualifiedName qName: String?, + attributes attributeDict: [String: String] = [:]) + { + if parser.shouldProcessNamespaces { + XCTAssertEqual(self.name, qName) + } else { + XCTAssertEqual(self.name, elementName) + } + } + func parser(_ parser: XMLParser, + didEndElement elementName: String, + namespaceURI: String?, + qualifiedName qName: String?) + { + if parser.shouldProcessNamespaces { + XCTAssertEqual(self.name, qName) + } else { + XCTAssertEqual(self.name, elementName) + } + } + func check() { + let elementString = "<\(self.name) />" + var parser = XMLParser(data: elementString.data(using: .utf8)!) + parser.delegate = self + XCTAssertTrue(parser.parse()) + + // Confirm that the parts of QName is also not swapped. + parser = XMLParser(data: elementString.data(using: .utf8)!) + parser.delegate = self + parser.shouldProcessNamespaces = true + XCTAssertTrue(parser.parse()) + } + } + + ElementNameChecker("noPrefix").check() + ElementNameChecker("myPrefix:myLocalName").check() + } + } diff --git a/TestFoundation/main.swift b/TestFoundation/main.swift index e7494dae92b..74c6c553ae2 100644 --- a/TestFoundation/main.swift +++ b/TestFoundation/main.swift @@ -15,8 +15,10 @@ import Glibc #endif +#if !os(Windows) // ignore SIGPIPE which is sent when writing to closed file descriptors. _ = signal(SIGPIPE, SIG_IGN) +#endif // For the Swift version of the Foundation tests, we must manually list all test cases here. XCTMain([ @@ -78,6 +80,7 @@ XCTMain([ testCase(TestTimer.allTests), testCase(TestTimeZone.allTests), testCase(TestURL.allTests), + testCase(TestURLCache.allTests), testCase(TestURLComponents.allTests), testCase(TestURLCredential.allTests), testCase(TestURLProtectionSpace.allTests), diff --git a/TestFoundation/xdgTestHelper/main.swift b/TestFoundation/xdgTestHelper/main.swift index e9607183c8f..dd8dbe98ece 100644 --- a/TestFoundation/xdgTestHelper/main.swift +++ b/TestFoundation/xdgTestHelper/main.swift @@ -7,10 +7,10 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // -#if DEPLOYMENT_RUNTIME_OBJC || os(Linux) || os(Android) -import Foundation -#else +#if canImport(SwiftFoundation) && !DEPLOYMENT_RUNTIME_OBJC import SwiftFoundation +#else +import Foundation #endif enum HelperCheckStatus : Int32 { @@ -55,6 +55,7 @@ class XDGCheck { } } +#if !os(Windows) // ----- // Used by TestProcess: test_interrupt(), test_suspend_resume() @@ -103,6 +104,7 @@ func signalTest() { } } } +#endif // ----- @@ -190,8 +192,10 @@ case "--nspathfor": test.run() #endif +#if !os(Windows) case "--signal-test": signalTest() +#endif default: fatalError("These arguments are not recognized. Only run this from a unit test.") diff --git a/cmake/modules/SwiftSupport.cmake b/cmake/modules/SwiftSupport.cmake index 41495785569..9db8590544d 100644 --- a/cmake/modules/SwiftSupport.cmake +++ b/cmake/modules/SwiftSupport.cmake @@ -1,6 +1,37 @@ include(CMakeParseArguments) +# Creates an output file map give a target and its list of sources at +# output_path +# +# Usage: +# create_output_file_map(target sources output_path) +function(create_output_file_map target sources output_path) + set(output_list) + set(output_file_map "{\n") + foreach(source ${sources}) + get_filename_component(name ${source} NAME) + + set(obj ${CMAKE_CURRENT_BINARY_DIR}/${target}.dir/${name}${CMAKE_C_OUTPUT_EXTENSION}) + set(deps ${CMAKE_CURRENT_BINARY_DIR}/${target}.dir/${name}.d) + set(swiftdeps ${CMAKE_CURRENT_BINARY_DIR}/${target}.dir/${name}.swiftdeps) + set(dia ${CMAKE_CURRENT_BINARY_DIR}/${target}.dir/${name}.dia) + set(output_entry "\"${source}\": {\n\ + \"object\": \"${obj}\",\n\ + \"dependencies\": \"${deps}\",\n\ + \"swift-dependencies\": \"${swiftdeps}\",\n\ + \"diagnostics\": \"${dia}\"\n\ +},\n") + string(APPEND output_file_map ${output_entry}) + endforeach() + set(master_deps ${CMAKE_CURRENT_BINARY_DIR}/${target}.swiftdeps) + string(APPEND output_file_map "\"\": {\n\ + \"swift-dependencies\": \"${master_deps}\"\n\ + }\n") + string(APPEND output_file_map "}\n") + file(WRITE ${output_path} ${output_file_map}) +endfunction() + function(add_swift_target target) set(options LIBRARY;SHARED;STATIC) set(single_value_options MODULE_NAME;MODULE_LINK_NAME;MODULE_PATH;MODULE_CACHE_PATH;OUTPUT;TARGET) @@ -11,6 +42,7 @@ function(add_swift_target target) set(compile_flags ${CMAKE_SWIFT_FLAGS}) set(link_flags) + list(APPEND compile_flags -incremental) if(AST_TARGET) list(APPEND compile_flags -target;${AST_TARGET}) list(APPEND link_flags -target;${AST_TARGET}) @@ -26,6 +58,11 @@ function(add_swift_target target) if(AST_MODULE_CACHE_PATH) list(APPEND compile_flags -module-cache-path;${AST_MODULE_CACHE_PATH}) endif() + if(AST_MODULE_PATH) + get_filename_component(module_location ${AST_MODULE_PATH} PATH) + file(MAKE_DIRECTORY "${module_location}") + list(APPEND compile_flags "-emit-module-path;${AST_MODULE_PATH}") + endif() if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES RelWithDebInfo) list(APPEND compile_flags -g) endif() @@ -75,74 +112,37 @@ function(add_swift_target target) endif() set(sources) + set(rsp_text) foreach(source ${AST_SOURCES}) get_filename_component(location ${source} PATH) if(IS_ABSOLUTE ${location}) list(APPEND sources ${source}) + string(APPEND rsp_text "${source} ") else() list(APPEND sources ${CMAKE_CURRENT_SOURCE_DIR}/${source}) + string(APPEND rsp_text "${CMAKE_CURRENT_SOURCE_DIR}/${source} ") endif() endforeach() - set(objs) - set(mods) - set(docs) - set(i 0) - foreach(source ${sources}) - get_filename_component(name ${source} NAME) - - set(obj ${CMAKE_CURRENT_BINARY_DIR}/${target}.dir/${name}${CMAKE_C_OUTPUT_EXTENSION}) - set(mod ${CMAKE_CURRENT_BINARY_DIR}/${target}.dir/${name}.swiftmodule) - set(doc ${CMAKE_CURRENT_BINARY_DIR}/${target}.dir/${name}.swiftdoc) - - set(all_sources ${sources}) - list(INSERT all_sources ${i} -primary-file) - - add_custom_command(OUTPUT - ${obj} - ${mod} - ${doc} - DEPENDS - ${source} - ${AST_DEPENDS} - COMMAND - ${CMAKE_SWIFT_COMPILER} -frontend ${compile_flags} -emit-module-path ${mod} -emit-module-doc-path ${doc} -o ${obj} -c ${all_sources}) - - list(APPEND objs ${obj}) - list(APPEND mods ${mod}) - list(APPEND docs ${doc}) - - math(EXPR i "${i}+1") - endforeach() - - if(AST_LIBRARY) - get_filename_component(module_directory ${AST_MODULE_PATH} DIRECTORY) - - set(module ${AST_MODULE_PATH}) - set(documentation ${module_directory}/${AST_MODULE_NAME}.swiftdoc) + set(output_map_path ${CMAKE_CURRENT_BINARY_DIR}/${target}.dir/output-file-map.json) + create_output_file_map(${target} "${sources}" ${output_map_path}) + list(APPEND compile_flags -output-file-map;${output_map_path}) - add_custom_command(OUTPUT - ${module} - ${documentation} - DEPENDS - ${mods} - ${docs} - ${AST_DEPENDS} - COMMAND - ${CMAKE_SWIFT_COMPILER} -frontend ${compile_flags} -sil-merge-partial-modules -emit-module ${mods} -o ${module} -emit-module-doc-path ${documentation}) - endif() + string(LENGTH sources source_length) + set(rsp_file ${CMAKE_CURRENT_BINARY_DIR}/${target}.dir/${target}.rsp) + file(WRITE ${rsp_file} ${rsp_text}) if(AST_LIBRARY) - set(emit_library -emit-library) + set(emit_library -emit-library) endif() if(NOT AST_LIBRARY OR library_kind STREQUAL SHARED) add_custom_command(OUTPUT ${AST_OUTPUT} DEPENDS - ${objs} + ${sources} ${AST_DEPENDS} COMMAND - ${CMAKE_SWIFT_COMPILER} ${emit_library} ${link_flags} -o ${AST_OUTPUT} ${objs}) + ${CMAKE_SWIFT_COMPILER} ${emit_library} ${compile_flags} ${link_flags} -o ${AST_OUTPUT} @${rsp_file}) add_custom_target(${target} ALL DEPENDS @@ -150,7 +150,7 @@ function(add_swift_target target) ${module} ${documentation}) else() - add_library(${target}-static STATIC ${objs}) + add_library(${target}-static STATIC ${sources}) if(AST_DEPENDS) add_dependencies(${target}-static ${AST_DEPENDS}) endif()