From a29e5e399041544e7bf51cd9fe7fb9ea873ddd02 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Wed, 7 Dec 2022 14:58:41 +0000 Subject: [PATCH 1/8] Add some utility functions for obtaining runtime-relative paths. Adds the ability to locate files relative to the path of the Swift runtime library, libswiftCore. rdar://103071801 --- include/swift/Runtime/EnvironmentVariables.h | 4 + include/swift/Runtime/Paths.h | 66 ++++ .../swift/Threading/Impl/Win32/Win32Defs.h | 3 +- stdlib/public/runtime/CMakeLists.txt | 1 + .../public/runtime/EnvironmentVariables.cpp | 15 + .../public/runtime/EnvironmentVariables.def | 5 + stdlib/public/runtime/Paths.cpp | 282 ++++++++++++++++++ test/Runtime/Paths.cpp | 77 +++++ test/Runtime/environment_variables.swift | 1 + 9 files changed, 452 insertions(+), 2 deletions(-) create mode 100644 include/swift/Runtime/Paths.h create mode 100644 stdlib/public/runtime/Paths.cpp create mode 100644 test/Runtime/Paths.cpp diff --git a/include/swift/Runtime/EnvironmentVariables.h b/include/swift/Runtime/EnvironmentVariables.h index 51e58f63285e4..ae2c8b72a719e 100644 --- a/include/swift/Runtime/EnvironmentVariables.h +++ b/include/swift/Runtime/EnvironmentVariables.h @@ -24,6 +24,10 @@ void initialize(void *); extern swift::once_t initializeToken; +// Define a typedef "string" in swift::runtime::environment to make string +// environment variables work +using string = const char *; + // Declare backing variables. #define VARIABLE(name, type, defaultValue, help) extern type name ## _variable; #include "../../../stdlib/public/runtime/EnvironmentVariables.def" diff --git a/include/swift/Runtime/Paths.h b/include/swift/Runtime/Paths.h new file mode 100644 index 0000000000000..f8218e849e281 --- /dev/null +++ b/include/swift/Runtime/Paths.h @@ -0,0 +1,66 @@ +//===--- Paths.h - Swift Runtime path utility functions ---------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Functions that obtain paths that might be useful within the runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RUNTIME_UTILS_H +#define SWIFT_RUNTIME_UTILS_H + +#include "swift/Runtime/Config.h" + +/// Return the path of the libswiftCore library. +/// +/// This can be used to locate files that are installed alongside the +/// Swift runtime library. +/// +/// \return A string containing the full path to libswiftCore. The string +/// is owned by the runtime and should not be freed. +SWIFT_RUNTIME_EXPORT +const char * +swift_getRuntimePath(); + +/// Return the path of the Swift root. +/// +/// If the path to libswiftCore is `/usr/local/swift/lib/libswiftCore.dylib`, +/// this function would return `/usr/local/swift`. +/// +/// The path returned here can be overridden by setting the environment +/// variable SWIFT_ROOT. +/// +/// \return A string containing the full path to the Swift root directory, +/// based either on the location of the Swift runtime, or on the +/// `SWIFT_ROOT` environment variable if set. +SWIFT_RUNTIME_EXPORT +const char * +swift_getRootPath(); + +/// Return the path of the specified auxiliary executable. +/// +/// This function will return `/path/to/swift/root/libexec/`, and on +/// Windows, it will automatically add `.exe` to the end. (This means that +/// you don't need to special case the name for Windows.) +/// +/// It does not test that the executable exists or that it is indeed +/// executable by the current user. If you are using this function to locate +/// a utility program for use by the runtime, you should provide a way to +/// override its location using an environment variable. +/// +/// \param name The name of the executable to locate. +/// +/// \return A string containing the full path to the executable. +SWIFT_RUNTIME_EXPORT +const char * +swift_getAuxiliaryExecutablePath(const char *name); + +#endif // SWIFT_RUNTIME_PATHS_H diff --git a/include/swift/Threading/Impl/Win32/Win32Defs.h b/include/swift/Threading/Impl/Win32/Win32Defs.h index f2a00de6e0560..b7c97b2c21d63 100644 --- a/include/swift/Threading/Impl/Win32/Win32Defs.h +++ b/include/swift/Threading/Impl/Win32/Win32Defs.h @@ -47,8 +47,7 @@ typedef struct _RTL_CONDITION_VARIABLE *PRTL_CONDITION_VARIABLE; typedef PRTL_CONDITION_VARIABLE PCONDITION_VARIABLE; // These have to be #defines, to avoid problems with -#define RTL_SRWLOCK_INIT \ - { 0 } +#define RTL_SRWLOCK_INIT {0} #define SRWLOCK_INIT RTL_SRWLOCK_INIT #define FLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF) diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index c41c9912e608b..b33ae006a6fb1 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -61,6 +61,7 @@ set(swift_runtime_sources MetadataLookup.cpp Numeric.cpp Once.cpp + Paths.cpp Portability.cpp ProtocolConformance.cpp RefCount.cpp diff --git a/stdlib/public/runtime/EnvironmentVariables.cpp b/stdlib/public/runtime/EnvironmentVariables.cpp index 5aa91f23e497b..0519f41a7f6bd 100644 --- a/stdlib/public/runtime/EnvironmentVariables.cpp +++ b/stdlib/public/runtime/EnvironmentVariables.cpp @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #include "swift/Runtime/Debug.h" +#include "swift/Runtime/Paths.h" #include "swift/Runtime/EnvironmentVariables.h" #include @@ -23,6 +24,12 @@ using namespace swift; namespace { +// This is required to make the macro machinery work correctly; we can't +// declare a VARIABLE(..., const char *, ...) because then the token-pasted +// names won't work properly. It *does* mean that if you want to use std::string +// somewhere in this file, you'll have to fully qualify the name. +typedef const char *string; + // Require all environment variable names to start with SWIFT_ static constexpr bool hasSwiftPrefix(const char *str) { const char prefix[] = "SWIFT_"; @@ -123,6 +130,14 @@ static uint32_t parse_uint32_t(const char *name, return n; } +static string parse_string(const char *name, + const char *value, + string defaultValue) { + if (!value || value[0] == 0) + return strdup(defaultValue); + return strdup(value); +} + // Print a list of all the environment variables. Lazy initialization makes // this a bit odd, but the use of these variables in the metadata system means // it's almost certain to run early. diff --git a/stdlib/public/runtime/EnvironmentVariables.def b/stdlib/public/runtime/EnvironmentVariables.def index a9cbb45045dbf..48b85a762f48d 100644 --- a/stdlib/public/runtime/EnvironmentVariables.def +++ b/stdlib/public/runtime/EnvironmentVariables.def @@ -81,4 +81,9 @@ VARIABLE(SWIFT_BINARY_COMPATIBILITY_VERSION, uint32_t, 0, VARIABLE(SWIFT_DEBUG_FAILED_TYPE_LOOKUP, bool, false, "Enable warnings when we fail to look up a type by name.") +VARIABLE(SWIFT_ROOT, string, "", + "Overrides the root directory of the Swift installation. " + "This is used to locate auxiliary files relative to the runtime " + "itself.") + #undef VARIABLE diff --git a/stdlib/public/runtime/Paths.cpp b/stdlib/public/runtime/Paths.cpp new file mode 100644 index 0000000000000..a937d1b959084 --- /dev/null +++ b/stdlib/public/runtime/Paths.cpp @@ -0,0 +1,282 @@ +//===--- Paths.cpp - Swift Runtime path utility functions -------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Functions that obtain paths that might be useful within the runtime. +// +//===----------------------------------------------------------------------===// + +#include "swift/Runtime/Config.h" +#include "swift/Runtime/EnvironmentVariables.h" +#include "swift/Runtime/Debug.h" +#include "swift/Runtime/Paths.h" +#include "swift/Runtime/Win32.h" +#include "swift/Threading/Once.h" + +#include + +#if !defined(_WIN32) || defined(__CYGWIN__) +#include + +#include +#else +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#include +#endif + +#include +#include +#include + +namespace { + +swift::once_t runtimePathToken; +const char *runtimePath; + +swift::once_t rootPathToken; +const char *rootPath; + +void _swift_initRuntimePath(void *); +void _swift_initRootPath(void *); +const char *_swift_getDefaultRootPath(); +const char *_swift_getAuxExePathIn(const char *path, const char *name); + +bool _swift_isPathSep(char ch) { +#ifdef _WIN32 + return ch == '/' || ch == '\\'; +#else + return ch == '/'; +#endif +} + +bool _swift_exists(const char *path); + +#if !defined(_WIN32) || defined(__CYGWIN__) +#define PATHSEP_STR "/" +#define PATHSEP_CHR '/' +#else +#define PATHSEP_STR "\\" +#define PATHSEP_CHR '\\' +#endif + +} + +SWIFT_RUNTIME_EXPORT +const char * +swift_getRuntimePath() +{ + swift::once(runtimePathToken, _swift_initRuntimePath, nullptr); + return runtimePath; +} + +SWIFT_RUNTIME_EXPORT +const char * +swift_getRootPath() +{ + swift::once(rootPathToken, _swift_initRootPath, nullptr); + return rootPath; +} + +namespace { + +const char * +_swift_getDefaultRootPath() +{ + const char *runtimePath = swift_getRuntimePath(); + size_t runtimePathLen = std::strlen(runtimePath); + + // Scan backwards until we find a path separator + const char *ptr = runtimePath + runtimePathLen; + while (ptr > runtimePath && !_swift_isPathSep(*--ptr)); + + // Remove the "lib" directory if it's present + if (ptr - runtimePath >= 4 + && _swift_isPathSep(ptr[-4]) + && std::strncmp(ptr - 3, "lib", 3) == 0) { + ptr -= 4; + } + + // If the result is empty, return "./" or ".\\" + if (ptr == runtimePath) { + return "." PATHSEP_STR; + } + + // Duplicate the string up to and including ptr + size_t len = ptr - runtimePath + 1; + char *thePath = (char *)malloc(len + 1); + std::memcpy(thePath, runtimePath, len); + thePath[len] = 0; + + return thePath; +} + +void +_swift_initRootPath(void *) +{ + // SWIFT_ROOT overrides the path returned by this function + const char *swiftRoot = swift::runtime::environment::SWIFT_ROOT(); + if (swiftRoot && *swiftRoot) { + size_t len = std::strlen(swiftRoot); + + // Ensure that there's a trailing slash + if (_swift_isPathSep(swiftRoot[len - 1])) { + rootPath = swiftRoot; + } else { + char *thePath = (char *)malloc(len + 2); + std::memcpy(thePath, swiftRoot, len); + thePath[len] = PATHSEP_CHR; + thePath[len + 1] = 0; + + rootPath = thePath; + } + } else { + rootPath = _swift_getDefaultRootPath(); + } +} + +} + +SWIFT_RUNTIME_EXPORT +const char * +swift_getAuxiliaryExecutablePath(const char *name) +{ + const char *rootPath = swift_getRootPath(); + + // Form /libexec/ + size_t rootPathLen = std::strlen(rootPath); + const char *libexecStr = PATHSEP_STR "libexec" PATHSEP_STR; + size_t libexecLen = std::strlen(libexecStr); + char *libexecPath = (char *)malloc(rootPathLen + libexecLen + 1); + std::memcpy(libexecPath, rootPath, rootPathLen); + std::memcpy(libexecPath + rootPathLen, libexecStr, libexecLen + 1); + + // If libexec exists, look there + if (_swift_exists(libexecPath)) { + const char *result = _swift_getAuxExePathIn(libexecPath, name); + + free(libexecPath); + + return result; + } + + free(libexecPath); + + // Otherwise, look in the root itself + return _swift_getAuxExePathIn(rootPath, name); +} + +namespace { + +const char * +_swift_getAuxExePathIn(const char *path, const char *name) +{ + size_t pathLen = std::strlen(path); + size_t nameLen = std::strlen(name); + size_t extLen = 0; + +#ifdef _WIN32 + if (nameLen > 4 && strcmp(name + nameLen - 4, ".exe") != 0) + extLen = 4; +#endif + + // /[.exe] + size_t totalLen = pathLen + nameLen + extLen + 1; + char *fullPath = (char *)malloc(totalLen); + char *ptr = fullPath; + std::memcpy(ptr, path, pathLen); + ptr += pathLen; + std::memcpy(ptr, name, nameLen); + ptr += nameLen; + +#ifdef _WIN32 + if (extLen) { + std::memcpy(ptr, ".exe", 4); + ptr += 4; + } +#endif + + *ptr = 0; + + return fullPath; +} + +#if !defined(_WIN32) || defined(__CYGWIN__) +void +_swift_initRuntimePath(void *) { + const char *path; + +#if APPLE_OS_SYSTEM + path = dyld_image_path_containing_address(_swift_initRuntimePath); +#else + Dl_info dli; + int ret = ::dladdr((void *)_swift_initRuntimePath, &dli); + + if (!ret) { + swift::fatalError(/* flags = */ 0, + "Unable to obtain Swift runtime path\n"); + } + + path = dli.dli_fname; +#endif + + runtimePath = ::strdup(path); +} +#else + +void +_swift_initRuntimePath(void *) { + const DWORD dwBufSize = 4096; + LPWSTR lpFilename = (LPWSTR)std::malloc(dwBufSize * sizeof(WCHAR)); + + DWORD dwRet = GetMappedFileNameW(GetCurrentProcess(), + _swift_initRuntimePath, + lpFilename, + dwBufSize); + if (!dwRet) { + swift::fatalError(/* flags = */ 0, + "Unable to obtain Swift runtime path\n"); + } + + runtimePath = swift::win32::copyUTF8FromWide(lpFilename); + if (!runtimePath) { + swift::fatalError(/* flags = */ 0, + "Unable to convert Swift runtime path to UTF-8: %lx, %d\n", + ::GetLastError(), errno); + } + + free(lpFilename); +} +#endif + +/// Return true if a file exists at path. +/// +/// On Windows, path will be in UTF-8 so can't be passed to _stat() or any +/// of the ANSI functions. +/// +/// @param path The path to check +/// +/// @result true iff there is a file at @a path +bool _swift_exists(const char *path) +{ +#if !defined(_WIN32) + struct stat st; + return stat(path, &st) == 0; +#else + wchar_t *wszPath = swift::win32::copyWideFromUTF8(path); + bool result = GetFileAttributesW(wszPath) != INVALID_FILE_ATTRIBUTES; + free(wszPath); + return result; +#endif // defined(_WIN32) +} + +} diff --git a/test/Runtime/Paths.cpp b/test/Runtime/Paths.cpp new file mode 100644 index 0000000000000..0ce12d4c8516d --- /dev/null +++ b/test/Runtime/Paths.cpp @@ -0,0 +1,77 @@ +// RUN: %empty-directory(%t) +// RUN: %target-clang %s -std=c++11 -I %swift_src_root/include -I %swift_src_root/stdlib/public/SwiftShims -I %clang-include-dir -isysroot %sdk %platform-dylib-dir/%target-library-name(swiftCore) -o %t/paths-test +// RUN: %target-codesign %t/paths-test +// RUN: %target-run %t/paths-test | %FileCheck %s + +// REQUIRES: executable_test +// UNSUPPORTED: remote_run + +// This can't be done in unittests, because that statically links the runtime +// so we get the wrong paths. We explicitly want to test that we get the +// path we expect (that is, the path to the runtime, and paths relative to +// that). + +#include "swift/Runtime/Paths.h" + +#include +#include + +#include +#include + +#if defined(_WIN32) && !defined(__CYGWIN) +#define stat _stat +#define S_IFDIR _S_IFDIR +#endif + +static bool +exists(const char *path) { + struct stat st; + + return stat(path, &st) == 0; +} + +static bool +isdir(const char *path) { + struct stat st; + + return stat(path, &st) == 0 && (st.st_mode & S_IFDIR); +} + +static bool +isfile(const char *path) { + struct stat st; + + return stat(path, &st) == 0 && !(st.st_mode & S_IFDIR); +} + +int main(void) { + const char *runtimePath = swift_getRuntimePath(); + + // Runtime path must point to libswiftCore and must be a file. + + // CHECK: runtime path: {{.*[\\/]}}libswiftCore.{{so|dylib|dll}} + // CHECK-NEXT: runtime is a file: yes + printf("runtime path: %s\n", runtimePath ? runtimePath: ""); + printf("runtime is a file: %s\n", isfile(runtimePath) ? "yes" : "no"); + + const char *rootPath = swift_getRootPath(); + + // Root path must end in a separator and must be a directory + + // CHECK: root path: {{.*[\\/]$}} + // CHECK-NEXT: root is a directory: yes + printf("root path: %s\n", rootPath ? rootPath : ""); + printf("root is a directory: %s\n", isdir(rootPath) ? "yes" : "no"); + + // Auxiliary executable path must be in swift-root/libexec. +#define UNLIKELY_NAME "anUnlikelyExecutableName" + + const size_t unlikelyLen = strlen(UNLIKELY_NAME); + const char *auxPath = swift_getAuxiliaryExecutablePath(UNLIKELY_NAME); + + // CHECK: aux path: {{.*[\\/](libexec[\\/])?}}anUnlikelyExecutableName{{(\.exe)?}} + printf("aux path: %s\n", auxPath ? auxPath : ""); + + return 0; +} diff --git a/test/Runtime/environment_variables.swift b/test/Runtime/environment_variables.swift index 43321ab420087..db0609ec06703 100644 --- a/test/Runtime/environment_variables.swift +++ b/test/Runtime/environment_variables.swift @@ -18,6 +18,7 @@ // CHECK-DAG: bool SWIFT_DETERMINISTIC_HASHING [default: false] - Disable randomized hash seeding. // CHECK-DAG: bool SWIFT_ENABLE_MANGLED_NAME_VERIFICATION [default: false] - Enable verification that metadata can roundtrip through a mangled name each time metadata is instantiated. // CHECK-DAG: bool SWIFT_DEBUG_ENABLE_MALLOC_SCRIBBLE [default: false] - Scribble on runtime allocations such as metadata allocations. +// CHECK-DAG: string SWIFT_ROOT [default: "{{[^"]*}}"] - Overrides the root directory of the Swift installation. This is used to locate auxiliary files relative to the runtime itself. // We need to do this because the DAG checks require a non-DAG check as an // anchor: From d289b34d2fcce82c0c3ccbe62e2101765697a64d Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 16 Dec 2022 11:43:11 +0000 Subject: [PATCH 2/8] Fix the paths up a little, and address a linking issue on Windows. The path to the runtime should be something like /usr/lib/swift/libswiftCore.dylib So we want to strip lib/swift/ off to get the Swift root. We also want auxiliary executables in /usr/libexec/swift rather than just /usr/libexec (While still supporting flat layout for Windows.) rdar://103071801 --- stdlib/public/runtime/Paths.cpp | 16 +++++++++------- test/Runtime/Paths.cpp | 21 ++++++++++++++++++++- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/stdlib/public/runtime/Paths.cpp b/stdlib/public/runtime/Paths.cpp index a937d1b959084..d4f6e014d0f59 100644 --- a/stdlib/public/runtime/Paths.cpp +++ b/stdlib/public/runtime/Paths.cpp @@ -99,11 +99,13 @@ _swift_getDefaultRootPath() const char *ptr = runtimePath + runtimePathLen; while (ptr > runtimePath && !_swift_isPathSep(*--ptr)); - // Remove the "lib" directory if it's present - if (ptr - runtimePath >= 4 - && _swift_isPathSep(ptr[-4]) - && std::strncmp(ptr - 3, "lib", 3) == 0) { - ptr -= 4; + // Remove lib/swift/ if present + if (ptr - runtimePath >= 10 + && _swift_isPathSep(ptr[-10]) + && std::strncmp(ptr - 9, "lib", 3) == 0 + && _swift_isPathSep(ptr[-6]) + && std::strncmp(ptr - 5, "swift", 5) == 0) { + ptr -= 10; } // If the result is empty, return "./" or ".\\" @@ -152,9 +154,9 @@ swift_getAuxiliaryExecutablePath(const char *name) { const char *rootPath = swift_getRootPath(); - // Form /libexec/ + // Form /libexec/swift/ size_t rootPathLen = std::strlen(rootPath); - const char *libexecStr = PATHSEP_STR "libexec" PATHSEP_STR; + const char *libexecStr = PATHSEP_STR "libexec" PATHSEP_STR "swift" PATHSEP_STR; size_t libexecLen = std::strlen(libexecStr); char *libexecPath = (char *)malloc(rootPathLen + libexecLen + 1); std::memcpy(libexecPath, rootPath, rootPathLen); diff --git a/test/Runtime/Paths.cpp b/test/Runtime/Paths.cpp index 0ce12d4c8516d..009b04c8e9cb7 100644 --- a/test/Runtime/Paths.cpp +++ b/test/Runtime/Paths.cpp @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-clang %s -std=c++11 -I %swift_src_root/include -I %swift_src_root/stdlib/public/SwiftShims -I %clang-include-dir -isysroot %sdk %platform-dylib-dir/%target-library-name(swiftCore) -o %t/paths-test +// RUN: %target-clang %s -std=c++11 -I %swift_src_root/include -I %swift_src_root/stdlib/public/SwiftShims -I %clang-include-dir -isysroot %sdk -L%swift_obj_root/lib/swift/%target-sdk-name -lswiftCore -o %t/paths-test // RUN: %target-codesign %t/paths-test // RUN: %target-run %t/paths-test | %FileCheck %s @@ -45,6 +45,19 @@ isfile(const char *path) { return stat(path, &st) == 0 && !(st.st_mode & S_IFDIR); } +static bool +endsWithLibSwift(const char *path) { + const char *posixSuffix = "/lib/swift/"; + const char *windowsSuffix = "\\lib\\swift\\"; + const size_t suffixLen = strlen(posixSuffix); + size_t len = strlen(path); + if (len < suffixLen) + return false; + const char *maybeSuffix = path + len - suffixLen; + return strcmp(maybeSuffix, posixSuffix) == 0 + || strcmp(maybeSuffix, windowsSuffix) == 0; +} + int main(void) { const char *runtimePath = swift_getRuntimePath(); @@ -64,6 +77,12 @@ int main(void) { printf("root path: %s\n", rootPath ? rootPath : ""); printf("root is a directory: %s\n", isdir(rootPath) ? "yes" : "no"); + // Root path should not end with /lib/swift/ + + // CHECK: root path ends with /lib/swift/: no + printf("root path ends with /lib/swift/: %s\n", + endsWithLibSwift(rootPath) ? "yes" : "no"); + // Auxiliary executable path must be in swift-root/libexec. #define UNLIKELY_NAME "anUnlikelyExecutableName" From 07579fe019aca6c3645bfa976a17c715282c8084 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 3 Jan 2023 12:44:01 +0000 Subject: [PATCH 3/8] Tweak the linker path for Windows slightly. Apparently the `swiftCore.lib` file is in `%target-sdk-name/%target-arch` within the build directory. rdar://103071801 --- test/Runtime/Paths.cpp | 2 +- test/lit.cfg | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/test/Runtime/Paths.cpp b/test/Runtime/Paths.cpp index 009b04c8e9cb7..3dd1bbd227e43 100644 --- a/test/Runtime/Paths.cpp +++ b/test/Runtime/Paths.cpp @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-clang %s -std=c++11 -I %swift_src_root/include -I %swift_src_root/stdlib/public/SwiftShims -I %clang-include-dir -isysroot %sdk -L%swift_obj_root/lib/swift/%target-sdk-name -lswiftCore -o %t/paths-test +// RUN: %target-clang %s -std=c++11 -I %swift_src_root/include -I %swift_src_root/stdlib/public/SwiftShims -I %clang-include-dir -isysroot %sdk -L%swift_obj_root/lib/swift/%target-sdk-name/%target-arch -lswiftCore -o %t/paths-test // RUN: %target-codesign %t/paths-test // RUN: %target-run %t/paths-test | %FileCheck %s diff --git a/test/lit.cfg b/test/lit.cfg index 259c7772be1f1..8f579e924d219 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -1756,6 +1756,9 @@ config.substitutions.append(('%module-target-future', target_future)) # Add 'target-sdk-name' as the name for platform-specific directories config.substitutions.append(('%target-sdk-name', config.target_sdk_name)) +# Also add 'target-arch' so we can locate some things inside the build tree +config.substitutions.append(('%target-arch', target_arch)) + # Add 'stdlib_dir' as the path to the stdlib resource directory stdlib_dir = os.path.join(config.swift_lib_dir, "swift") if run_os == 'maccatalyst': From 668a63f1a3214cba7706f9dbc005986afba6837b Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 3 Jan 2023 13:41:07 +0000 Subject: [PATCH 4/8] Fix the paths test on Windows. Windows doesn't have a "lib" prefix on the Swift DLLs. rdar://103071801 --- test/Runtime/Paths.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Runtime/Paths.cpp b/test/Runtime/Paths.cpp index 3dd1bbd227e43..69e57cdcd9684 100644 --- a/test/Runtime/Paths.cpp +++ b/test/Runtime/Paths.cpp @@ -63,7 +63,7 @@ int main(void) { // Runtime path must point to libswiftCore and must be a file. - // CHECK: runtime path: {{.*[\\/]}}libswiftCore.{{so|dylib|dll}} + // CHECK: runtime path: {{.*[\\/](lib)?}}swiftCore.{{so|dylib|dll}} // CHECK-NEXT: runtime is a file: yes printf("runtime path: %s\n", runtimePath ? runtimePath: ""); printf("runtime is a file: %s\n", isfile(runtimePath) ? "yes" : "no"); From 0b2ebb5dec88c78a9bc90355c007eee81b1894b2 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 3 Jan 2023 17:16:33 +0000 Subject: [PATCH 5/8] Add code to map NT-style paths to Win32 paths. GetMappedFilenameW() returns an NT-style path, not a Win32 path, but we need a Win32 path. rdar://103071801 --- stdlib/public/runtime/Paths.cpp | 91 +++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 3 deletions(-) diff --git a/stdlib/public/runtime/Paths.cpp b/stdlib/public/runtime/Paths.cpp index d4f6e014d0f59..11f66a9c8dfb2 100644 --- a/stdlib/public/runtime/Paths.cpp +++ b/stdlib/public/runtime/Paths.cpp @@ -146,6 +146,81 @@ _swift_initRootPath(void *) } } +#if _WIN32 +/// Map an NT-style filename to a Win32 filename. +/// +/// We can't use GetFinalPathNameByHandle() because there's no way to obtain +/// a handle (at least, not without using the internal NtCreateFile() API, which +/// we aren't supposed to be using). Additionally, that function would resolve +/// symlinks, which we don't want to do here. +/// +/// As a result, we use the approach demonstrated here: +/// +/// https://learn.microsoft.com/en-us/windows/win32/memory/obtaining-a-file-name-from-a-file-handle +/// +/// @param pszFilename The NT-style filename to convert. +/// +/// @result A string, allocated using std::malloc(), containing the Win32-style +/// filename. +LPWSTR +_swift_win32NameFromNTName(LPWSTR pszFilename) { + DWORD dwLen = GetLogicalDriveStringsW(0, NULL); + if (!dwLen) + return NULL; + + LPWSTR lpDriveStrings = (LPWSTR)std::malloc(dwLen * sizeof(WCHAR)); + if (!lpDriveStrings) + return NULL; + + DWORD dwRet = GetLogicalDriveStringsW(dwLen, lpDriveStrings); + if (!dwRet) + return NULL; + + LPWSTR pszDrive = lpDriveStrings; + while (*pszDrive) { + size_t len = wcslen(pszDrive); + if (len && pszDrive[len - 1] == '\\') + pszDrive[len - 1] = 0; + + WCHAR ntPath[4096]; + dwRet = QueryDosDeviceW(pszDrive, ntPath, 4096); + if (dwRet) { + size_t ntLen = wcslen(ntPath); + + if (_wcsnicmp(pszFilename, ntPath, ntLen) == 0 + && pszFilename[ntLen] == '\\') { + size_t fnLen = wcslen(pszFilename); + size_t driveLen = wcslen(pszDrive); + size_t pathLen = fnLen - ntLen; + size_t newLen = driveLen + pathLen + 1; + LPWSTR pszWin32Name = (LPWSTR)std::malloc(newLen * sizeof(WCHAR)); + if (!pszWin32Name) { + std::free(lpDriveStrings); + return NULL; + } + + LPWSTR ptr = pszWin32Name; + memcpy(ptr, pszDrive, driveLen * sizeof(WCHAR)); + ptr += driveLen; + memcpy(ptr, pszFilename + ntLen, pathLen * sizeof(WCHAR)); + ptr += pathLen; + *ptr = 0; + + std::free(lpDriveStrings); + + return pszWin32Name; + } + } + + pszDrive += len + 1; + } + + std::free(lpDriveStrings); + + return _wcsdup(pszFilename); +} +#endif + } SWIFT_RUNTIME_EXPORT @@ -241,7 +316,7 @@ _swift_initRuntimePath(void *) { LPWSTR lpFilename = (LPWSTR)std::malloc(dwBufSize * sizeof(WCHAR)); DWORD dwRet = GetMappedFileNameW(GetCurrentProcess(), - _swift_initRuntimePath, + (void *)_swift_initRuntimePath, lpFilename, dwBufSize); if (!dwRet) { @@ -249,14 +324,24 @@ _swift_initRuntimePath(void *) { "Unable to obtain Swift runtime path\n"); } - runtimePath = swift::win32::copyUTF8FromWide(lpFilename); + // GetMappedFileNameW() returns an NT-style path, not a Win32 path; that is, + // it starts with \Device\DeviceName rather than a drive letter. + LPWSTR lpWin32Filename = _swift_win32NameFromNTName(lpFilename); + if (!lpWin32Filename) { + swift::fatalError(/* flags = */ 0, + "Unable to obtain Win32 path for Swift runtime\n"); + } + + std::free(lpFilename); + + runtimePath = swift::win32::copyUTF8FromWide(lpWin32Filename); if (!runtimePath) { swift::fatalError(/* flags = */ 0, "Unable to convert Swift runtime path to UTF-8: %lx, %d\n", ::GetLastError(), errno); } - free(lpFilename); + std::free(lpWin32Filename); } #endif From 082d6f218bd1ed5c08dfb50070a1c201d0883627 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 12 Jan 2023 16:11:55 +0000 Subject: [PATCH 6/8] Improve path lookup. On Windows, swift_getRootPath() should return the root path (i.e. outside the "bin" directory). On Linux, the path for the runtime looks like /lib/swift/linux/libswiftCore.so rather than /lib/swift/libswiftCore.so as you might expect. On macOS, the path for the runtime *can* have "macosx" in it if we're using the library from the build path. That's not how it gets installed when it's part of the operating system, but it's how we build it. Also make swift_getAuxiliaryExecutablePath() actually check that the executable exists. This does mean it can return nullptr now, but it also means that it can search through the various locations it supports to try to find the file you're after, which is more robust. rdar://103071801 --- include/swift/Runtime/Paths.h | 45 +++-- stdlib/cmake/modules/AddSwiftStdlib.cmake | 5 +- stdlib/public/runtime/Paths.cpp | 236 ++++++++++++++++++---- test/Runtime/Paths.cpp | 54 ++--- 4 files changed, 261 insertions(+), 79 deletions(-) diff --git a/include/swift/Runtime/Paths.h b/include/swift/Runtime/Paths.h index f8218e849e281..21964e4d69adb 100644 --- a/include/swift/Runtime/Paths.h +++ b/include/swift/Runtime/Paths.h @@ -21,11 +21,11 @@ /// Return the path of the libswiftCore library. /// -/// This can be used to locate files that are installed alongside the -/// Swift runtime library. +/// This can be used to locate files that are installed alongside the Swift +/// runtime library. /// -/// \return A string containing the full path to libswiftCore. The string -/// is owned by the runtime and should not be freed. +/// \return A string containing the full path to libswiftCore. The string is +/// owned by the runtime and should not be freed. SWIFT_RUNTIME_EXPORT const char * swift_getRuntimePath(); @@ -35,28 +35,39 @@ swift_getRuntimePath(); /// If the path to libswiftCore is `/usr/local/swift/lib/libswiftCore.dylib`, /// this function would return `/usr/local/swift`. /// -/// The path returned here can be overridden by setting the environment -/// variable SWIFT_ROOT. +/// The path returned here can be overridden by setting the environment variable +/// SWIFT_ROOT. /// -/// \return A string containing the full path to the Swift root directory, -/// based either on the location of the Swift runtime, or on the -/// `SWIFT_ROOT` environment variable if set. +/// \return A string containing the full path to the Swift root directory, based +/// either on the location of the Swift runtime, or on the `SWIFT_ROOT` +/// environment variable if set. SWIFT_RUNTIME_EXPORT const char * swift_getRootPath(); /// Return the path of the specified auxiliary executable. /// -/// This function will return `/path/to/swift/root/libexec/`, and on -/// Windows, it will automatically add `.exe` to the end. (This means that -/// you don't need to special case the name for Windows.) +/// This function will search for the auxiliary executable in the following +/// paths: /// -/// It does not test that the executable exists or that it is indeed -/// executable by the current user. If you are using this function to locate -/// a utility program for use by the runtime, you should provide a way to -/// override its location using an environment variable. +/// /libexec/swift// +/// /libexec/swift/ +/// /bin/ +/// / /// -/// \param name The name of the executable to locate. +/// It will return the first of those that exists, but it does not test that +/// the file is indeed executable. +/// +/// On Windows, it will automatically add `.exe` to the name, which means you +/// do not need to special case the name for Windows. +/// +/// If you are using this function to locate a utility program for use by the +/// runtime, you should provide a way to override its location using an +/// environment variable. +/// +/// If the executable cannot be found, it will return nullptr. +/// +/// \param name The name of the executable to locate. /// /// \return A string containing the full path to the executable. SWIFT_RUNTIME_EXPORT diff --git a/stdlib/cmake/modules/AddSwiftStdlib.cmake b/stdlib/cmake/modules/AddSwiftStdlib.cmake index bf2a8dbf6fc30..13a93ca214963 100644 --- a/stdlib/cmake/modules/AddSwiftStdlib.cmake +++ b/stdlib/cmake/modules/AddSwiftStdlib.cmake @@ -156,7 +156,10 @@ function(_add_target_variant_c_compile_flags) set(result ${${CFLAGS_RESULT_VAR_NAME}}) - list(APPEND result "-DSWIFT_RUNTIME") + list(APPEND result + "-DSWIFT_RUNTIME" + "-DSWIFT_LIB_SUBDIR=\"${SWIFT_SDK_${CFLAGS_SDK}_LIB_SUBDIR}\"" + ) if ("${CFLAGS_ARCH}" STREQUAL "arm64" OR "${CFLAGS_ARCH}" STREQUAL "arm64_32") diff --git a/stdlib/public/runtime/Paths.cpp b/stdlib/public/runtime/Paths.cpp index 11f66a9c8dfb2..17a977a4a70b2 100644 --- a/stdlib/public/runtime/Paths.cpp +++ b/stdlib/public/runtime/Paths.cpp @@ -49,7 +49,7 @@ const char *rootPath; void _swift_initRuntimePath(void *); void _swift_initRootPath(void *); const char *_swift_getDefaultRootPath(); -const char *_swift_getAuxExePathIn(const char *path, const char *name); +char *_swift_getAuxExePathIn(const char *path, const char *name); bool _swift_isPathSep(char ch) { #ifdef _WIN32 @@ -89,6 +89,34 @@ swift_getRootPath() namespace { +bool +_swift_lookingAtLibSwift(const char *ptr, const char *base) +{ + // /some/path/to/some/thing/lib/swift/libswiftCore.dylib + // ^ ^ + // | +---- ptr + // +-------------- ptr - 10 + + return (ptr - base >= 10 + && _swift_isPathSep(ptr[-10]) + && std::strncmp(ptr - 9, "lib", 3) == 0 + && _swift_isPathSep(ptr[-6]) + && std::strncmp(ptr - 5, "swift", 5) == 0); +} + +bool +_swift_lookingAtBin(const char *ptr, const char *base) +{ + // C:\some\path\to\some\thing\bin\libswiftCore.dylib + // ^ ^ + // | +---- ptr + // +-------- ptr - 4 + + return (ptr - base > 4 + && _swift_isPathSep(ptr[-4]) + && std::strncmp(ptr - 3, "bin", 3) == 0); +} + const char * _swift_getDefaultRootPath() { @@ -99,13 +127,48 @@ _swift_getDefaultRootPath() const char *ptr = runtimePath + runtimePathLen; while (ptr > runtimePath && !_swift_isPathSep(*--ptr)); +<<<<<<< Updated upstream // Remove lib/swift/ if present if (ptr - runtimePath >= 10 && _swift_isPathSep(ptr[-10]) && std::strncmp(ptr - 9, "lib", 3) == 0 && _swift_isPathSep(ptr[-6]) && std::strncmp(ptr - 5, "swift", 5) == 0) { +======= + if (_swift_lookingAtLibSwift(ptr, runtimePath)) { + // /some/path/to/some/thing/lib/swift/libswiftCore.dylib + // ^ ^ + // | +---- ptr + // +-------------- ptr - 10 +>>>>>>> Stashed changes ptr -= 10; + } else { + // We *might* be in a directory, so scan backwards for that too + const char *platform = ptr; + while (platform > runtimePath && !_swift_isPathSep(*--platform)); + + if (_swift_lookingAtLibSwift(platform, runtimePath)) { + + // When we get here, we have: + // + // /some/path/to/some/thing/lib/swift/macosx/libswiftCore.dylib + // ^ ^ ^ + // | | +---- ptr + // | +----------- platform + // +--------------------- platform - 10 + + ptr = platform - 10; + } else { + // We *might* also be in a bin directory, for instance on Windows, so + // check if we should remove that also. + if (_swift_lookingAtBin(ptr, runtimePath)) { + // C:\some\path\to\some\thing\bin\libswiftCore.dylib + // ^ ^ + // | +---- ptr + // +-------- ptr - 4 + ptr -= 4; + } + } } // If the result is empty, return "./" or ".\\" @@ -122,6 +185,60 @@ _swift_getDefaultRootPath() return thePath; } +// Join paths together +char * +_swift_joinPaths(const char *path, ...) +{ + va_list val; + size_t baseLen = 0; + size_t totalLen = 0; + const char *pathSeg; + + baseLen = std::strlen(path); + while (baseLen && _swift_isPathSep(path[baseLen - 1])) + --baseLen; + + if (!baseLen) + totalLen = 1; + else + totalLen = baseLen; + + va_start(val, path); + while ((pathSeg = va_arg(val, const char *))) { + size_t len = std::strlen(pathSeg); + while (len && _swift_isPathSep(pathSeg[len - 1])) + --len; + if (len) + totalLen += 1 + len; + } + va_end(val); + + char *buffer = static_cast(std::malloc(totalLen + 1)); + char *ptr = buffer; + + if (!baseLen) + *ptr++ = PATHSEP_CHR; + else { + std::memcpy(ptr, path, baseLen); + ptr += baseLen; + } + + va_start(val, path); + while ((pathSeg = va_arg(val, const char *))) { + size_t len = std::strlen(pathSeg); + while (len && _swift_isPathSep(pathSeg[len - 1])) + --len; + if (len) { + *ptr++ = PATHSEP_CHR; + std::memcpy(ptr, pathSeg, len); + ptr += len; + } + } + buffer[totalLen] = 0; + + return buffer; +} + void _swift_initRootPath(void *) { @@ -221,7 +338,7 @@ _swift_win32NameFromNTName(LPWSTR pszFilename) { } #endif -} +} // namespace SWIFT_RUNTIME_EXPORT const char * @@ -229,61 +346,108 @@ swift_getAuxiliaryExecutablePath(const char *name) { const char *rootPath = swift_getRootPath(); - // Form /libexec/swift/ - size_t rootPathLen = std::strlen(rootPath); - const char *libexecStr = PATHSEP_STR "libexec" PATHSEP_STR "swift" PATHSEP_STR; - size_t libexecLen = std::strlen(libexecStr); - char *libexecPath = (char *)malloc(rootPathLen + libexecLen + 1); - std::memcpy(libexecPath, rootPath, rootPathLen); - std::memcpy(libexecPath + rootPathLen, libexecStr, libexecLen + 1); + const char *platformName = SWIFT_LIB_SUBDIR; - // If libexec exists, look there - if (_swift_exists(libexecPath)) { - const char *result = _swift_getAuxExePathIn(libexecPath, name); + // /libexec/swift/ + { + char *libexecPlatPath = _swift_joinPaths(rootPath, + "libexec" PATHSEP_STR "swift", + platformName, nullptr); - free(libexecPath); + // If it exists, look there + if (_swift_exists(libexecPlatPath)) { + char *result = _swift_getAuxExePathIn(libexecPlatPath, name); - return result; + if (_swift_exists(result)) { + std::free(libexecPlatPath); + + return result; + } + + std::free(result); + } + + + std::free(libexecPlatPath); } - free(libexecPath); + // /libexec/swift + { + char *libexecPath = _swift_joinPaths(rootPath, + "libexec" PATHSEP_STR "swift", + nullptr); + + // If it exists, look there + if (_swift_exists(libexecPath)) { + char *result = _swift_getAuxExePathIn(libexecPath, name); + + if (_swift_exists(result)) { + std::free(libexecPath); + + return result; + } + + std::free(result); + } + + std::free(libexecPath); + } + + // /bin + { + char *binPath = _swift_joinPaths(rootPath, "bin", nullptr); + + // If bin exists, look there + if (_swift_exists(binPath)) { + char *result = _swift_getAuxExePathIn(binPath, name); + + if (_swift_exists(result)) { + std::free(binPath); + + return result; + } + + std::free(result); + } + + std::free(binPath); + } // Otherwise, look in the root itself - return _swift_getAuxExePathIn(rootPath, name); + char *result = _swift_getAuxExePathIn(rootPath, name); + + if (_swift_exists(result)) + return result; + + std::free(result); + + return nullptr; } namespace { -const char * +char * _swift_getAuxExePathIn(const char *path, const char *name) { - size_t pathLen = std::strlen(path); +#ifdef _WIN32 size_t nameLen = std::strlen(name); - size_t extLen = 0; + char *nameWithSuffix = nullptr; + if (nameLen > 4 && strcmp(name + nameLen - 4, ".exe") != 0) { + nameWithSuffix = (char *)std::malloc(nameLen + 4 + 1); + std::memcpy(nameWithSuffix, name, nameLen); + std::memcpy(nameWithSuffix + nameLen, ".exe", 4 + 1); -#ifdef _WIN32 - if (nameLen > 4 && strcmp(name + nameLen - 4, ".exe") != 0) - extLen = 4; + name = nameWithSuffix; + } #endif - // /[.exe] - size_t totalLen = pathLen + nameLen + extLen + 1; - char *fullPath = (char *)malloc(totalLen); - char *ptr = fullPath; - std::memcpy(ptr, path, pathLen); - ptr += pathLen; - std::memcpy(ptr, name, nameLen); - ptr += nameLen; + char *fullPath = _swift_joinPaths(path, name, nullptr); #ifdef _WIN32 - if (extLen) { - std::memcpy(ptr, ".exe", 4); - ptr += 4; - } + if (nameWithSuffix) + std::free(nameWithSuffix); #endif - *ptr = 0; - return fullPath; } diff --git a/test/Runtime/Paths.cpp b/test/Runtime/Paths.cpp index 69e57cdcd9684..9623182980c2a 100644 --- a/test/Runtime/Paths.cpp +++ b/test/Runtime/Paths.cpp @@ -1,8 +1,14 @@ // RUN: %empty-directory(%t) + +// RUN: mkdir -p %t/swift-root/libexec/swift %t/swift-root/bin +// RUN: touch %t/swift-root/libexec/swift/Foo +// RUN: touch %t/swift-root/libexec/swift/Foo.exe +// RUN: touch %t/swift-root/bin/Foo.exe + // RUN: %target-clang %s -std=c++11 -I %swift_src_root/include -I %swift_src_root/stdlib/public/SwiftShims -I %clang-include-dir -isysroot %sdk -L%swift_obj_root/lib/swift/%target-sdk-name/%target-arch -lswiftCore -o %t/paths-test // RUN: %target-codesign %t/paths-test // RUN: %target-run %t/paths-test | %FileCheck %s - +// RUN: env %env-SWIFT_ROOT=%t/swift-root %target-run %t/paths-test | %FileCheck %s --check-prefix CHECK-FR // REQUIRES: executable_test // UNSUPPORTED: remote_run @@ -32,30 +38,25 @@ exists(const char *path) { } static bool -isdir(const char *path) { +isfile(const char *path) { struct stat st; - return stat(path, &st) == 0 && (st.st_mode & S_IFDIR); + return stat(path, &st) == 0 && !(st.st_mode & S_IFDIR); } static bool -isfile(const char *path) { +isdir(const char *path) { struct stat st; - return stat(path, &st) == 0 && !(st.st_mode & S_IFDIR); + return stat(path, &st) == 0 && (st.st_mode & S_IFDIR); } static bool -endsWithLibSwift(const char *path) { - const char *posixSuffix = "/lib/swift/"; - const char *windowsSuffix = "\\lib\\swift\\"; - const size_t suffixLen = strlen(posixSuffix); - size_t len = strlen(path); - if (len < suffixLen) - return false; - const char *maybeSuffix = path + len - suffixLen; - return strcmp(maybeSuffix, posixSuffix) == 0 - || strcmp(maybeSuffix, windowsSuffix) == 0; +containsLibSwift(const char *path) { + const char *posix = "/lib/swift/"; + const char *windows = "\\lib\\swift\\"; + + return strstr(path, posix) || strstr(path, windows); } int main(void) { @@ -65,6 +66,9 @@ int main(void) { // CHECK: runtime path: {{.*[\\/](lib)?}}swiftCore.{{so|dylib|dll}} // CHECK-NEXT: runtime is a file: yes + + // CHECK-FR: runtime path: {{.*[\\/](lib)?}}swiftCore.{{so|dylib|dll}} + // CHECK-FR-NEXT: runtime is a file: yes printf("runtime path: %s\n", runtimePath ? runtimePath: ""); printf("runtime is a file: %s\n", isfile(runtimePath) ? "yes" : "no"); @@ -74,22 +78,22 @@ int main(void) { // CHECK: root path: {{.*[\\/]$}} // CHECK-NEXT: root is a directory: yes + + // CHECK-FR: root path: {{.*[\\/]$}} + // CHECK-FR-NEXT: root is a directory: yes printf("root path: %s\n", rootPath ? rootPath : ""); printf("root is a directory: %s\n", isdir(rootPath) ? "yes" : "no"); - // Root path should not end with /lib/swift/ - - // CHECK: root path ends with /lib/swift/: no - printf("root path ends with /lib/swift/: %s\n", - endsWithLibSwift(rootPath) ? "yes" : "no"); + // CHECK: root path contains /lib/swift/: no + // CHECK-FR: root path contains /lib/swift/: no + printf("root path contains /lib/swift/: %s\n", + containsLibSwift(rootPath) ? "yes" : "no"); - // Auxiliary executable path must be in swift-root/libexec. -#define UNLIKELY_NAME "anUnlikelyExecutableName" + const char *auxPath = swift_getAuxiliaryExecutablePath("Foo"); - const size_t unlikelyLen = strlen(UNLIKELY_NAME); - const char *auxPath = swift_getAuxiliaryExecutablePath(UNLIKELY_NAME); + // CHECK: aux path: + // CHECK-FR: aux path: {{.*[\\/]libexec[\\/]swift[\\/]Foo(\.exe)?}} - // CHECK: aux path: {{.*[\\/](libexec[\\/])?}}anUnlikelyExecutableName{{(\.exe)?}} printf("aux path: %s\n", auxPath ? auxPath : ""); return 0; From b7589458420759675aeb853f6dd304a7c3626cbf Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 12 Jan 2023 16:21:09 +0000 Subject: [PATCH 7/8] Style tweak and an extra comment. Change _swift_initRootPath() to do an early return, which reduces indentation. Add a comment to reinforce that we can't use GetFinalPathNameByHandle() in _swift_initRuntimePath(). rdar://103071801 --- stdlib/public/runtime/Paths.cpp | 40 +++++++++++++++------------------ 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/stdlib/public/runtime/Paths.cpp b/stdlib/public/runtime/Paths.cpp index 17a977a4a70b2..bbf8a56a57f4f 100644 --- a/stdlib/public/runtime/Paths.cpp +++ b/stdlib/public/runtime/Paths.cpp @@ -127,20 +127,11 @@ _swift_getDefaultRootPath() const char *ptr = runtimePath + runtimePathLen; while (ptr > runtimePath && !_swift_isPathSep(*--ptr)); -<<<<<<< Updated upstream - // Remove lib/swift/ if present - if (ptr - runtimePath >= 10 - && _swift_isPathSep(ptr[-10]) - && std::strncmp(ptr - 9, "lib", 3) == 0 - && _swift_isPathSep(ptr[-6]) - && std::strncmp(ptr - 5, "swift", 5) == 0) { -======= if (_swift_lookingAtLibSwift(ptr, runtimePath)) { // /some/path/to/some/thing/lib/swift/libswiftCore.dylib // ^ ^ // | +---- ptr // +-------------- ptr - 10 ->>>>>>> Stashed changes ptr -= 10; } else { // We *might* be in a directory, so scan backwards for that too @@ -244,22 +235,24 @@ _swift_initRootPath(void *) { // SWIFT_ROOT overrides the path returned by this function const char *swiftRoot = swift::runtime::environment::SWIFT_ROOT(); - if (swiftRoot && *swiftRoot) { - size_t len = std::strlen(swiftRoot); - // Ensure that there's a trailing slash - if (_swift_isPathSep(swiftRoot[len - 1])) { - rootPath = swiftRoot; - } else { - char *thePath = (char *)malloc(len + 2); - std::memcpy(thePath, swiftRoot, len); - thePath[len] = PATHSEP_CHR; - thePath[len + 1] = 0; + if (!swiftRoot || !*swiftRoot) { + rootPath = _swift_getDefaultRootPath(); + return; + } - rootPath = thePath; - } + size_t len = std::strlen(swiftRoot); + + // Ensure that there's a trailing slash + if (_swift_isPathSep(swiftRoot[len - 1])) { + rootPath = swiftRoot; } else { - rootPath = _swift_getDefaultRootPath(); + char *thePath = (char *)malloc(len + 2); + std::memcpy(thePath, swiftRoot, len); + thePath[len] = PATHSEP_CHR; + thePath[len + 1] = 0; + + rootPath = thePath; } } @@ -479,6 +472,9 @@ _swift_initRuntimePath(void *) { const DWORD dwBufSize = 4096; LPWSTR lpFilename = (LPWSTR)std::malloc(dwBufSize * sizeof(WCHAR)); + // Again, we can't use GetFinalPathNameByHandle for the reasons given + // above. + DWORD dwRet = GetMappedFileNameW(GetCurrentProcess(), (void *)_swift_initRuntimePath, lpFilename, From 87932c631dae383774c5888a21e27b7f2eac1e8d Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 13 Jan 2023 11:36:28 +0000 Subject: [PATCH 8/8] Support even more paths. It turns out that the path to the runtime might also include the architecture as well as the platform. So we need to search /lib/swift/ /lib/swift// /lib/swift /lib/swift/ /bin/ /bin/ in that order. Hopefully this now covers all the possibilities. rdar://103071801 --- stdlib/cmake/modules/AddSwiftStdlib.cmake | 1 + stdlib/public/runtime/Paths.cpp | 187 ++++++++++++---------- 2 files changed, 107 insertions(+), 81 deletions(-) diff --git a/stdlib/cmake/modules/AddSwiftStdlib.cmake b/stdlib/cmake/modules/AddSwiftStdlib.cmake index 13a93ca214963..10db08f7dbf1d 100644 --- a/stdlib/cmake/modules/AddSwiftStdlib.cmake +++ b/stdlib/cmake/modules/AddSwiftStdlib.cmake @@ -159,6 +159,7 @@ function(_add_target_variant_c_compile_flags) list(APPEND result "-DSWIFT_RUNTIME" "-DSWIFT_LIB_SUBDIR=\"${SWIFT_SDK_${CFLAGS_SDK}_LIB_SUBDIR}\"" + "-DSWIFT_ARCH=\"${CFLAGS_ARCH}\"" ) if ("${CFLAGS_ARCH}" STREQUAL "arm64" OR diff --git a/stdlib/public/runtime/Paths.cpp b/stdlib/public/runtime/Paths.cpp index bbf8a56a57f4f..3baf1a6288f86 100644 --- a/stdlib/public/runtime/Paths.cpp +++ b/stdlib/public/runtime/Paths.cpp @@ -50,6 +50,7 @@ void _swift_initRuntimePath(void *); void _swift_initRootPath(void *); const char *_swift_getDefaultRootPath(); char *_swift_getAuxExePathIn(const char *path, const char *name); +const char *_swift_tryAuxExePath(const char *name, const char *path, ...); bool _swift_isPathSep(char ch) { #ifdef _WIN32 @@ -134,22 +135,31 @@ _swift_getDefaultRootPath() // +-------------- ptr - 10 ptr -= 10; } else { - // We *might* be in a directory, so scan backwards for that too + // We *might* be in a or / directory, so scan + // backwards for that too + bool found = false; const char *platform = ptr; - while (platform > runtimePath && !_swift_isPathSep(*--platform)); - if (_swift_lookingAtLibSwift(platform, runtimePath)) { + for (unsigned n = 0; n < 2; ++n) { + while (platform > runtimePath && !_swift_isPathSep(*--platform)); - // When we get here, we have: - // - // /some/path/to/some/thing/lib/swift/macosx/libswiftCore.dylib - // ^ ^ ^ - // | | +---- ptr - // | +----------- platform - // +--------------------- platform - 10 + if (_swift_lookingAtLibSwift(platform, runtimePath)) { - ptr = platform - 10; - } else { + // When we get here, we have: + // + // /some/path/to/some/thing/lib/swift/macosx/libswiftCore.dylib + // ^ ^ ^ + // | | +---- ptr + // | +----------- platform + // +--------------------- platform - 10 + + ptr = platform - 10; + found = true; + break; + } + } + + if (!found) { // We *might* also be in a bin directory, for instance on Windows, so // check if we should remove that also. if (_swift_lookingAtBin(ptr, runtimePath)) { @@ -178,13 +188,15 @@ _swift_getDefaultRootPath() // Join paths together char * -_swift_joinPaths(const char *path, ...) +_swift_joinPathsV(const char *path, va_list val) { - va_list val; + va_list val2; size_t baseLen = 0; size_t totalLen = 0; const char *pathSeg; + va_copy(val2, val); + baseLen = std::strlen(path); while (baseLen && _swift_isPathSep(path[baseLen - 1])) --baseLen; @@ -194,15 +206,13 @@ _swift_joinPaths(const char *path, ...) else totalLen = baseLen; - va_start(val, path); - while ((pathSeg = va_arg(val, const char *))) { + while ((pathSeg = va_arg(val2, const char *))) { size_t len = std::strlen(pathSeg); while (len && _swift_isPathSep(pathSeg[len - 1])) --len; if (len) totalLen += 1 + len; } - va_end(val); char *buffer = static_cast(std::malloc(totalLen + 1)); char *ptr = buffer; @@ -214,7 +224,6 @@ _swift_joinPaths(const char *path, ...) ptr += baseLen; } - va_start(val, path); while ((pathSeg = va_arg(val, const char *))) { size_t len = std::strlen(pathSeg); while (len && _swift_isPathSep(pathSeg[len - 1])) @@ -230,6 +239,19 @@ _swift_joinPaths(const char *path, ...) return buffer; } +char * +_swift_joinPaths(const char *path, ...) +{ + va_list val; + char *result; + + va_start(val, path); + result = _swift_joinPathsV(path, val); + va_end(val); + + return result; +} + void _swift_initRootPath(void *) { @@ -340,81 +362,58 @@ swift_getAuxiliaryExecutablePath(const char *name) const char *rootPath = swift_getRootPath(); const char *platformName = SWIFT_LIB_SUBDIR; + const char *archName = SWIFT_ARCH; // /libexec/swift/ - { - char *libexecPlatPath = _swift_joinPaths(rootPath, - "libexec" PATHSEP_STR "swift", - platformName, nullptr); - - // If it exists, look there - if (_swift_exists(libexecPlatPath)) { - char *result = _swift_getAuxExePathIn(libexecPlatPath, name); - - if (_swift_exists(result)) { - std::free(libexecPlatPath); - - return result; - } - - std::free(result); - } - + if (const char *result = _swift_tryAuxExePath(name, + rootPath, + "libexec", "swift", + platformName, nullptr)) { + return result; + } - std::free(libexecPlatPath); + // /libexec/swift// + if (const char *result = _swift_tryAuxExePath(name, + rootPath, + "libexec", "swift", + platformName, + archName, nullptr)) { + return result; } // /libexec/swift - { - char *libexecPath = _swift_joinPaths(rootPath, - "libexec" PATHSEP_STR "swift", - nullptr); - - // If it exists, look there - if (_swift_exists(libexecPath)) { - char *result = _swift_getAuxExePathIn(libexecPath, name); - - if (_swift_exists(result)) { - std::free(libexecPath); - - return result; - } - - std::free(result); - } + if (const char *result = _swift_tryAuxExePath(name, + rootPath, + "libexec", "swift", + nullptr)) { + return result; + } - std::free(libexecPath); + // /libexec/swift/ + if (const char *result = _swift_tryAuxExePath(name, + rootPath, + "libexec", "swift", + archName, nullptr)) { + return result; } // /bin - { - char *binPath = _swift_joinPaths(rootPath, "bin", nullptr); - - // If bin exists, look there - if (_swift_exists(binPath)) { - char *result = _swift_getAuxExePathIn(binPath, name); - - if (_swift_exists(result)) { - std::free(binPath); - - return result; - } - - std::free(result); - } - - std::free(binPath); + if (const char *result = _swift_tryAuxExePath(name, + rootPath, + "bin", nullptr)) { + return result; } - // Otherwise, look in the root itself - char *result = _swift_getAuxExePathIn(rootPath, name); - - if (_swift_exists(result)) + // /bin/ + if (const char *result = _swift_tryAuxExePath(name, + rootPath, + "bin", + archName, nullptr)) { return result; + } - std::free(result); - - return nullptr; + // Otherwise, look in the root itself + return _swift_tryAuxExePath(name, rootPath, nullptr); } namespace { @@ -444,6 +443,32 @@ _swift_getAuxExePathIn(const char *path, const char *name) return fullPath; } +const char * +_swift_tryAuxExePath(const char *name, const char *path, ...) +{ + va_list val; + char *fullPath; + va_start(val, path); + fullPath = _swift_joinPathsV(path, val); + va_end(val); + + if (_swift_exists(fullPath)) { + char *result = _swift_getAuxExePathIn(fullPath, name); + + if (_swift_exists(result)) { + std::free(fullPath); + + return result; + } + + std::free(result); + } + + std::free(fullPath); + + return nullptr; +} + #if !defined(_WIN32) || defined(__CYGWIN__) void _swift_initRuntimePath(void *) { @@ -494,7 +519,7 @@ _swift_initRuntimePath(void *) { std::free(lpFilename); - runtimePath = swift::win32::copyUTF8FromWide(lpWin32Filename); + runtimePath = _swift_win32_copyUTF8FromWide(lpWin32Filename); if (!runtimePath) { swift::fatalError(/* flags = */ 0, "Unable to convert Swift runtime path to UTF-8: %lx, %d\n", @@ -519,7 +544,7 @@ bool _swift_exists(const char *path) struct stat st; return stat(path, &st) == 0; #else - wchar_t *wszPath = swift::win32::copyWideFromUTF8(path); + wchar_t *wszPath = _swift_win32_copyWideFromUTF8(path); bool result = GetFileAttributesW(wszPath) != INVALID_FILE_ATTRIBUTES; free(wszPath); return result;