Skip to content

[5.1] [ModuleInterface] Look for a prebuilt module by full target name #24412

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 71 additions & 2 deletions lib/Frontend/ParseableInterfaceModuleLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "swift/AST/Module.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/Basic/Lazy.h"
#include "swift/Basic/Platform.h"
#include "swift/Basic/STLExtras.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/ParseableInterfaceSupport.h"
Expand Down Expand Up @@ -826,7 +827,7 @@ class ParseableInterfaceModuleLoaderImpl {
// Assemble the expected path: $PREBUILT_CACHE/Foo.swiftmodule or
// $PREBUILT_CACHE/Foo.swiftmodule/arch.swiftmodule. Note that there's no
// cache key here.
scratch.append(prebuiltCacheDir);
scratch = prebuiltCacheDir;

// FIXME: Would it be possible to only have architecture-specific names
// here? Then we could skip this check.
Expand All @@ -845,6 +846,54 @@ class ParseableInterfaceModuleLoaderImpl {
return scratch.str();
}

/// Hack to deal with build systems (including the Swift standard library, at
/// the time of this comment) that aren't yet using target-specific names for
/// multi-target swiftmodules, in case the prebuilt cache is.
Optional<StringRef>
computeFallbackPrebuiltModulePath(llvm::SmallString<256> &scratch) {
namespace path = llvm::sys::path;
StringRef sdkPath = ctx.SearchPathOpts.SDKPath;

// Check if the interface file comes from the SDK
if (sdkPath.empty() || !hasPrefix(path::begin(interfacePath),
path::end(interfacePath),
path::begin(sdkPath),
path::end(sdkPath)))
return None;

// If the module isn't target-specific, there's no fallback path.
StringRef inParentDirName =
path::filename(path::parent_path(interfacePath));
if (path::extension(inParentDirName) != ".swiftmodule")
return None;

// If the interface is already using the target-specific name, there's
// nothing else to try.
auto normalizedTarget = getTargetSpecificModuleTriple(ctx.LangOpts.Target);
if (path::stem(modulePath) == normalizedTarget.str())
return None;

// Assemble the expected path:
// $PREBUILT_CACHE/Foo.swiftmodule/target.swiftmodule. Note that there's no
// cache key here.
scratch = prebuiltCacheDir;
path::append(scratch, inParentDirName);
path::append(scratch, normalizedTarget.str());
scratch += ".swiftmodule";

// If there isn't a file at this location, skip returning a path.
if (!fs.exists(scratch))
return None;

return scratch.str();
}

bool isInResourceDir(StringRef path) {
StringRef resourceDir = ctx.SearchPathOpts.RuntimeLibraryPath;
if (resourceDir.empty()) return false;
return path.startswith(resourceDir);
}

/// Finds the most appropriate .swiftmodule, whose dependencies are up to
/// date, that we can load for the provided .swiftinterface file.
llvm::ErrorOr<DiscoveredModule> discoverUpToDateModuleForInterface(
Expand Down Expand Up @@ -920,7 +969,12 @@ class ParseableInterfaceModuleLoaderImpl {
if (!prebuiltCacheDir.empty()) {
llvm::SmallString<256> scratch;
std::unique_ptr<llvm::MemoryBuffer> moduleBuffer;
auto path = computePrebuiltModulePath(scratch);
Optional<StringRef> path = computePrebuiltModulePath(scratch);
if (!path) {
// Hack: deal with prebuilds of modules that still use the target-based
// names.
path = computeFallbackPrebuiltModulePath(scratch);
}
if (path) {
if (swiftModuleIsUpToDate(*path, deps, moduleBuffer)) {
LLVM_DEBUG(llvm::dbgs() << "Found up-to-date prebuilt module at "
Expand Down Expand Up @@ -950,6 +1004,21 @@ class ParseableInterfaceModuleLoaderImpl {
<< modulePath
<< "; deferring to serialized module loader\n");
return std::make_error_code(std::errc::not_supported);
} else if (isInResourceDir(modulePath) &&
loadMode == ModuleLoadingMode::PreferSerialized) {
// Special-case here: If we're loading a .swiftmodule from the resource
// dir adjacent to the compiler, defer to the serialized loader instead
// of falling back. This is mainly to support development of Swift,
// where one might change the module format version but forget to
// recompile the standard library. If that happens, don't fall back
// and silently recompile the standard library -- instead, error like
// we used to.
LLVM_DEBUG(llvm::dbgs() << "Found out-of-date module in the "
"resource-dir at "
<< modulePath
<< "; deferring to serialized module loader "
"to diagnose\n");
return std::make_error_code(std::errc::not_supported);
} else {
LLVM_DEBUG(llvm::dbgs() << "Found out-of-date module at "
<< modulePath << "\n");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Like prebuilt-module-cache-archs.swift, but testing the fallback behavior.
// This means we have to know the expected names in advance, so this test only
// runs on macOS.

// REQUIRES: OS=macosx
// REQUIRES: CPU=x86_64

// Use the short name "x86_64.swiftmodule".
// RUN: %empty-directory(%t)
// RUN: %empty-directory(%t/include/Lib.swiftmodule)
// RUN: cp %S/Inputs/prebuilt-module-cache/Lib.swiftinterface %t/include/Lib.swiftmodule/x86_64.swiftinterface

// Do a manual prebuild with the long name "x86_64-apple-macos.swiftmodule",
// and see if it gets picked up.
// RUN: %empty-directory(%t/MCP)
// RUN: %empty-directory(%t/prebuilt-cache/Lib.swiftmodule)
// RUN: sed -e 's/FromInterface/FromPrebuiltLong/g' %S/Inputs/prebuilt-module-cache/Lib.swiftinterface | %target-swift-frontend -parse-stdlib -module-cache-path %t/MCP -emit-module-path %t/prebuilt-cache/Lib.swiftmodule/x86_64-apple-macos.swiftmodule - -module-name Lib
// RUN: not %target-swift-frontend -typecheck -parse-stdlib -module-cache-path %t/MCP -sdk %t/include -I %t/include/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-PREBUILT-LONG %s
// RUN: %{python} %S/Inputs/check-is-forwarding-module.py %t/MCP/Lib-*.swiftmodule

// Prefer a matching name.
// RUN: %empty-directory(%t/MCP)
// RUN: sed -e 's/FromInterface/FromPrebuiltShort/g' %S/Inputs/prebuilt-module-cache/Lib.swiftinterface | %target-swift-frontend -parse-stdlib -module-cache-path %t/MCP -emit-module-path %t/prebuilt-cache/Lib.swiftmodule/x86_64.swiftmodule - -module-name Lib
// RUN: not %target-swift-frontend -typecheck -parse-stdlib -module-cache-path %t/MCP -sdk %t/include -I %t/include/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-PREBUILT-SHORT %s
// RUN: %{python} %S/Inputs/check-is-forwarding-module.py %t/MCP/Lib-*.swiftmodule

// Prefer a matching name in the other direction too.
// RUN: %empty-directory(%t/MCP)
// RUN: mv %t/include/Lib.swiftmodule/x86_64.swiftinterface %t/include/Lib.swiftmodule/x86_64-apple-macos.swiftinterface
// RUN: not %target-swift-frontend -typecheck -parse-stdlib -module-cache-path %t/MCP -sdk %t/include -I %t/include/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-PREBUILT-LONG %s
// RUN: %{python} %S/Inputs/check-is-forwarding-module.py %t/MCP/Lib-*.swiftmodule

// Don't do the fallback thing for long names to short names.
// RUN: %empty-directory(%t/MCP)
// RUN: rm %t/prebuilt-cache/Lib.swiftmodule/x86_64-apple-macos.swiftmodule
// RUN: not %target-swift-frontend -typecheck -parse-stdlib -module-cache-path %t/MCP -sdk %t/include -I %t/include/ -prebuilt-module-cache-path %t/prebuilt-cache %s 2>&1 | %FileCheck -check-prefix=FROM-INTERFACE %s
// RUN: not %{python} %S/Inputs/check-is-forwarding-module.py %t/MCP/Lib-*.swiftmodule

import Lib

struct X {}
let _: X = Lib.testValue
// FROM-INTERFACE: [[@LINE-1]]:16: error: cannot convert value of type 'FromInterface' to specified type 'X'
// FROM-PREBUILT-LONG: [[@LINE-2]]:16: error: cannot convert value of type 'FromPrebuiltLong' to specified type 'X'
// FROM-PREBUILT-SHORT: [[@LINE-3]]:16: error: cannot convert value of type 'FromPrebuiltShort' to specified type 'X'