mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
636 lines
16 KiB
C++
636 lines
16 KiB
C++
//===--- 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"
|
|
|
|
#ifdef __APPLE__
|
|
#include <TargetConditionals.h>
|
|
#endif
|
|
|
|
#if !defined(_WIN32) || defined(__CYGWIN__)
|
|
|
|
#if __has_include(<sys/stat.h>)
|
|
# include <sys/stat.h>
|
|
|
|
# ifdef S_IFMT
|
|
# define HAS_STAT 1
|
|
# else
|
|
# define HAS_STAT 0
|
|
# endif
|
|
#else
|
|
# define HAS_STAT 0
|
|
#endif
|
|
|
|
#if SWIFT_STDLIB_HAS_DLADDR
|
|
#include <dlfcn.h>
|
|
#endif
|
|
|
|
#if __has_include(<mach-o/dyld_priv.h>)
|
|
#include <mach-o/dyld_priv.h>
|
|
#define APPLE_OS_SYSTEM 1
|
|
#else
|
|
#define APPLE_OS_SYSTEM 0
|
|
#endif
|
|
|
|
#else // defined(_WIN32)
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#define NOMINMAX
|
|
#include <windows.h>
|
|
#include <psapi.h>
|
|
|
|
#endif // defined(_WIN32)
|
|
|
|
#ifdef __linux__
|
|
// Needed for 'readlink'.
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <cerrno>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
|
|
#if !SWIFT_STDLIB_HAS_FILESYSTEM
|
|
|
|
SWIFT_RUNTIME_EXPORT
|
|
const char *
|
|
swift_getRuntimeLibraryPath() {
|
|
return nullptr;
|
|
}
|
|
|
|
SWIFT_RUNTIME_EXPORT
|
|
const char *
|
|
swift_getRootPath() {
|
|
return nullptr;
|
|
}
|
|
|
|
SWIFT_RUNTIME_EXPORT
|
|
char *
|
|
swift_copyAuxiliaryExecutablePath(const char *name) {
|
|
return nullptr;
|
|
}
|
|
|
|
#else // SWIFT_STDLIB_HAS_FILESYSTEM
|
|
|
|
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();
|
|
char *_swift_getAuxExePathIn(const char *path, const char *name);
|
|
char *_swift_tryAuxExePath(const char *name, const char *path, ...);
|
|
|
|
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_getRuntimeLibraryPath()
|
|
{
|
|
swift::once(runtimePathToken, _swift_initRuntimePath, nullptr);
|
|
return runtimePath;
|
|
}
|
|
|
|
SWIFT_RUNTIME_EXPORT
|
|
const char *
|
|
swift_getRootPath()
|
|
{
|
|
swift::once(rootPathToken, _swift_initRootPath, nullptr);
|
|
return rootPath;
|
|
}
|
|
|
|
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()
|
|
{
|
|
const char *runtimePath = swift_getRuntimeLibraryPath();
|
|
|
|
if (!runtimePath)
|
|
return nullptr;
|
|
|
|
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));
|
|
|
|
if (_swift_lookingAtLibSwift(ptr, runtimePath)) {
|
|
// /some/path/to/some/thing/lib/swift/libswiftCore.dylib
|
|
// ^ ^
|
|
// | +---- ptr
|
|
// +-------------- ptr - 10
|
|
ptr -= 10;
|
|
} else {
|
|
// We *might* be in a <platform> or <platform>/<arch> directory, so scan
|
|
// backwards for that too
|
|
bool found = false;
|
|
const char *platform = ptr;
|
|
|
|
for (unsigned n = 0; n < 2; ++n) {
|
|
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;
|
|
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)) {
|
|
// C:\some\path\to\some\thing\bin\libswiftCore.dylib
|
|
// ^ ^
|
|
// | +---- ptr
|
|
// +-------- ptr - 4
|
|
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;
|
|
}
|
|
|
|
// Join paths together
|
|
char *
|
|
_swift_joinPathsV(const char *path, 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;
|
|
|
|
if (!baseLen)
|
|
totalLen = 1;
|
|
else
|
|
totalLen = baseLen;
|
|
|
|
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;
|
|
}
|
|
|
|
char *buffer = static_cast<char *>(std::malloc(totalLen + 1));
|
|
char *ptr = buffer;
|
|
|
|
if (!baseLen)
|
|
*ptr++ = PATHSEP_CHR;
|
|
else {
|
|
std::memcpy(ptr, path, baseLen);
|
|
ptr += baseLen;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 *)
|
|
{
|
|
// SWIFT_ROOT overrides the path returned by this function
|
|
const char *swiftRoot = swift::runtime::environment::SWIFT_ROOT();
|
|
|
|
if (!swiftRoot || !*swiftRoot) {
|
|
rootPath = _swift_getDefaultRootPath();
|
|
return;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
#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
|
|
|
|
} // namespace
|
|
|
|
SWIFT_RUNTIME_EXPORT
|
|
char *
|
|
swift_copyAuxiliaryExecutablePath(const char *name)
|
|
{
|
|
const char *rootPath = swift_getRootPath();
|
|
|
|
if (!rootPath)
|
|
return nullptr;
|
|
|
|
const char *platformName = SWIFT_LIB_SUBDIR;
|
|
const char *archName = SWIFT_ARCH;
|
|
|
|
// <rootPath>/libexec/swift/<platformName>
|
|
if (char *result = _swift_tryAuxExePath(name,
|
|
rootPath,
|
|
"libexec", "swift",
|
|
platformName, nullptr)) {
|
|
return result;
|
|
}
|
|
|
|
// <rootPath>/libexec/swift/<platformName>/<arch>
|
|
if (char *result = _swift_tryAuxExePath(name,
|
|
rootPath,
|
|
"libexec", "swift",
|
|
platformName,
|
|
archName, nullptr)) {
|
|
return result;
|
|
}
|
|
|
|
// <rootPath>/libexec/swift
|
|
if (char *result = _swift_tryAuxExePath(name,
|
|
rootPath,
|
|
"libexec", "swift",
|
|
nullptr)) {
|
|
return result;
|
|
}
|
|
|
|
// <rootPath>/libexec/swift/<arch>
|
|
if (char *result = _swift_tryAuxExePath(name,
|
|
rootPath,
|
|
"libexec", "swift",
|
|
archName, nullptr)) {
|
|
return result;
|
|
}
|
|
|
|
// <rootPath>/bin
|
|
if (char *result = _swift_tryAuxExePath(name,
|
|
rootPath,
|
|
"bin", nullptr)) {
|
|
return result;
|
|
}
|
|
|
|
// <rootPath>/bin/<arch>
|
|
if (char *result = _swift_tryAuxExePath(name,
|
|
rootPath,
|
|
"bin",
|
|
archName, nullptr)) {
|
|
return result;
|
|
}
|
|
|
|
// Otherwise, look in the root itself
|
|
return _swift_tryAuxExePath(name, rootPath, nullptr);
|
|
}
|
|
|
|
namespace {
|
|
|
|
char *
|
|
_swift_getAuxExePathIn(const char *path, const char *name)
|
|
{
|
|
#ifdef _WIN32
|
|
size_t nameLen = std::strlen(name);
|
|
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);
|
|
|
|
name = nameWithSuffix;
|
|
}
|
|
#endif
|
|
|
|
char *fullPath = _swift_joinPaths(path, name, nullptr);
|
|
|
|
#ifdef _WIN32
|
|
if (nameWithSuffix)
|
|
std::free(nameWithSuffix);
|
|
#endif
|
|
|
|
return fullPath;
|
|
}
|
|
|
|
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 *) {
|
|
#if APPLE_OS_SYSTEM
|
|
const char *path =
|
|
dyld_image_path_containing_address((const void *)_swift_initRuntimePath);
|
|
|
|
// No need to ::strdup() this, as the return value is guaranteed to remain
|
|
// valid as long as the library is loaded.
|
|
runtimePath = path;
|
|
#elif SWIFT_STDLIB_HAS_DLADDR
|
|
Dl_info dli;
|
|
int ret = ::dladdr((void *)_swift_initRuntimePath, &dli);
|
|
|
|
if (!ret) {
|
|
#ifdef __linux__
|
|
// If we don't find anything, try reading /proc/self/exe as a fallback;
|
|
// this is needed with Musl when statically linking because in that case
|
|
// dladdr() does nothing.
|
|
char pathBuf[4096];
|
|
ssize_t len = readlink("/proc/self/exe", pathBuf, sizeof(pathBuf)-1);
|
|
if (len > 0 ) {
|
|
pathBuf[len] = '\0';
|
|
runtimePath = ::strdup(pathBuf);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
swift::fatalError(/* flags = */ 0,
|
|
"Unable to obtain Swift runtime path\n");
|
|
}
|
|
|
|
runtimePath = ::strdup(dli.dli_fname);
|
|
#else
|
|
runtimePath = nullptr;
|
|
#endif
|
|
}
|
|
#else
|
|
|
|
void
|
|
_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,
|
|
dwBufSize);
|
|
if (!dwRet) {
|
|
swift::fatalError(/* flags = */ 0,
|
|
"Unable to obtain Swift runtime path\n");
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
std::free(lpWin32Filename);
|
|
}
|
|
#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)
|
|
# if !HAS_STAT
|
|
return false;
|
|
# else
|
|
struct stat st;
|
|
return stat(path, &st) == 0;
|
|
# endif
|
|
#else
|
|
wchar_t *wszPath = _swift_win32_copyWideFromUTF8(path);
|
|
bool result = GetFileAttributesW(wszPath) != INVALID_FILE_ATTRIBUTES;
|
|
free(wszPath);
|
|
return result;
|
|
#endif // defined(_WIN32)
|
|
}
|
|
|
|
}
|
|
|
|
#endif // SWIFT_STDLIB_HAS_FILESYSTEM
|