mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Serialization: restrict swiftmodules to distribution channels
There are scenarios where different compilers are distributed with compatible serialization format versions and the same tag. Distinguish swiftmodules in such a case by assigning them to different distribution channels. A compiler expecting a specific channel will only read swiftmodules from the same channel. The channels should be defined by downstream code as it is by definition vendor specific. For development, a no-channel compiler loads or defining the env var SWIFT_IGNORE_SWIFTMODULE_REVISION skips this new check. rdar://123731777
This commit is contained in:
@@ -850,6 +850,11 @@ ERROR(serialization_module_incompatible_revision,Fatal,
|
||||
"compiled module was created by a different version of the compiler '%0'; "
|
||||
"rebuild %1 and try again: %2",
|
||||
(StringRef, Identifier, StringRef))
|
||||
ERROR(serialization_module_incompatible_channel,Fatal,
|
||||
"compiled module was created for a different distribution channel '%0' "
|
||||
"than the local compiler '%1', "
|
||||
"please ensure %2 is found from the expected path: %3",
|
||||
(StringRef, StringRef, Identifier, StringRef))
|
||||
ERROR(serialization_missing_single_dependency,Fatal,
|
||||
"missing required module '%0'", (StringRef))
|
||||
ERROR(serialization_missing_dependencies,Fatal,
|
||||
|
||||
@@ -177,6 +177,11 @@ StringRef getCurrentCompilerTag();
|
||||
/// depending on the vendor.
|
||||
StringRef getCurrentCompilerSerializationTag();
|
||||
|
||||
/// Distribution channel of the running compiler for distributed swiftmodules.
|
||||
/// Helps to distinguish swiftmodules between different compilers using the
|
||||
/// same serialization tag.
|
||||
StringRef getCurrentCompilerChannel();
|
||||
|
||||
/// Retrieves the value of the upcoming C++ interoperability compatibility
|
||||
/// version that's going to be presented as some new concrete version to the
|
||||
/// users.
|
||||
|
||||
@@ -48,6 +48,9 @@ enum class Status {
|
||||
/// The precise revision version doesn't match.
|
||||
RevisionIncompatible,
|
||||
|
||||
/// The distribution channel doesn't match.
|
||||
ChannelIncompatible,
|
||||
|
||||
/// The module is required to be in OSSA, but is not.
|
||||
NotInOSSA,
|
||||
|
||||
@@ -105,6 +108,7 @@ struct ValidationInfo {
|
||||
llvm::VersionTuple userModuleVersion;
|
||||
StringRef sdkName = {};
|
||||
StringRef problematicRevision = {};
|
||||
StringRef problematicChannel = {};
|
||||
size_t bytes = 0;
|
||||
Status status = Status::Malformed;
|
||||
std::vector<StringRef> allowableClients;
|
||||
|
||||
@@ -313,6 +313,16 @@ StringRef getCurrentCompilerSerializationTag() {
|
||||
#endif
|
||||
}
|
||||
|
||||
StringRef getCurrentCompilerChannel() {
|
||||
static const char* forceDebugChannel =
|
||||
::getenv("SWIFT_FORCE_SWIFTMODULE_CHANNEL");
|
||||
if (forceDebugChannel)
|
||||
return forceDebugChannel;
|
||||
|
||||
// Leave it to downstream compilers to define the different channels.
|
||||
return StringRef();
|
||||
}
|
||||
|
||||
unsigned getUpcomingCxxInteropCompatVersion() {
|
||||
return SWIFT_VERSION_MAJOR + 1;
|
||||
}
|
||||
|
||||
@@ -316,6 +316,8 @@ struct ModuleRebuildInfo {
|
||||
return "compiled with a newer version of the compiler";
|
||||
case Status::RevisionIncompatible:
|
||||
return "compiled with a different version of the compiler";
|
||||
case Status::ChannelIncompatible:
|
||||
return "compiled for a different distribution channel";
|
||||
case Status::NotInOSSA:
|
||||
return "module was not built with OSSA";
|
||||
case Status::NoncopyableGenericsMismatch:
|
||||
|
||||
@@ -418,6 +418,21 @@ static ValidationInfo validateControlBlock(
|
||||
}
|
||||
break;
|
||||
}
|
||||
case control_block::CHANNEL: {
|
||||
static const char* ignoreRevision =
|
||||
::getenv("SWIFT_IGNORE_SWIFTMODULE_REVISION");
|
||||
if (ignoreRevision)
|
||||
break;
|
||||
|
||||
StringRef moduleChannel = blobData,
|
||||
compilerChannel = version::getCurrentCompilerChannel();
|
||||
if (requiresRevisionMatch && !compilerChannel.empty() &&
|
||||
moduleChannel != compilerChannel) {
|
||||
result.problematicChannel = moduleChannel;
|
||||
result.status = Status::ChannelIncompatible;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case control_block::IS_OSSA: {
|
||||
auto isModuleInOSSA = scratch[0];
|
||||
if (requiresOSSAModules && !isModuleInOSSA)
|
||||
@@ -532,6 +547,7 @@ std::string serialization::StatusToString(Status S) {
|
||||
case Status::FormatTooOld: return "FormatTooOld";
|
||||
case Status::FormatTooNew: return "FormatTooNew";
|
||||
case Status::RevisionIncompatible: return "RevisionIncompatible";
|
||||
case Status::ChannelIncompatible: return "ChannelIncompatible";
|
||||
case Status::NotInOSSA: return "NotInOSSA";
|
||||
case Status::NoncopyableGenericsMismatch:
|
||||
return "NoncopyableGenericsMismatch";
|
||||
|
||||
@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
|
||||
/// describe what change you made. The content of this comment isn't important;
|
||||
/// it just ensures a conflict if two people change the module format.
|
||||
/// Don't worry about adhering to the 80-column limit for this line.
|
||||
const uint16_t SWIFTMODULE_VERSION_MINOR = 858; // Replace inherited types with protocols in protocol layout
|
||||
const uint16_t SWIFTMODULE_VERSION_MINOR = 859; // channel check
|
||||
|
||||
/// A standard hash seed used for all string hashes in a serialized module.
|
||||
///
|
||||
@@ -860,6 +860,7 @@ namespace control_block {
|
||||
TARGET,
|
||||
SDK_NAME,
|
||||
REVISION,
|
||||
CHANNEL,
|
||||
IS_OSSA,
|
||||
ALLOWABLE_CLIENT_NAME,
|
||||
HAS_NONCOPYABLE_GENERICS,
|
||||
@@ -898,6 +899,11 @@ namespace control_block {
|
||||
BCBlob
|
||||
>;
|
||||
|
||||
using ChannelLayout = BCRecordLayout<
|
||||
CHANNEL,
|
||||
BCBlob
|
||||
>;
|
||||
|
||||
using IsOSSALayout = BCRecordLayout<
|
||||
IS_OSSA,
|
||||
BCFixed<1>
|
||||
|
||||
@@ -834,6 +834,7 @@ void Serializer::writeBlockInfoBlock() {
|
||||
BLOCK_RECORD(control_block, TARGET);
|
||||
BLOCK_RECORD(control_block, SDK_NAME);
|
||||
BLOCK_RECORD(control_block, REVISION);
|
||||
BLOCK_RECORD(control_block, CHANNEL);
|
||||
BLOCK_RECORD(control_block, IS_OSSA);
|
||||
BLOCK_RECORD(control_block, ALLOWABLE_CLIENT_NAME);
|
||||
BLOCK_RECORD(control_block, HAS_NONCOPYABLE_GENERICS);
|
||||
@@ -982,6 +983,7 @@ void Serializer::writeHeader() {
|
||||
control_block::TargetLayout Target(Out);
|
||||
control_block::SDKNameLayout SDKName(Out);
|
||||
control_block::RevisionLayout Revision(Out);
|
||||
control_block::ChannelLayout Channel(Out);
|
||||
control_block::IsOSSALayout IsOSSA(Out);
|
||||
control_block::AllowableClientLayout Allowable(Out);
|
||||
control_block::HasNoncopyableGenerics HasNoncopyableGenerics(Out);
|
||||
@@ -1035,6 +1037,8 @@ void Serializer::writeHeader() {
|
||||
forcedDebugRevision : version::getCurrentCompilerSerializationTag();
|
||||
Revision.emit(ScratchRecord, revision);
|
||||
|
||||
Channel.emit(ScratchRecord, version::getCurrentCompilerChannel());
|
||||
|
||||
IsOSSA.emit(ScratchRecord, Options.IsOSSA);
|
||||
|
||||
HasNoncopyableGenerics.emit(ScratchRecord,
|
||||
|
||||
@@ -1081,6 +1081,12 @@ void swift::serialization::diagnoseSerializedASTLoadFailure(
|
||||
Ctx.Diags.diagnose(diagLoc, diag::serialization_module_incompatible_revision,
|
||||
loadInfo.problematicRevision, ModuleName, moduleBufferID);
|
||||
break;
|
||||
case serialization::Status::ChannelIncompatible:
|
||||
Ctx.Diags.diagnose(diagLoc, diag::serialization_module_incompatible_channel,
|
||||
loadInfo.problematicChannel,
|
||||
version::getCurrentCompilerChannel(),
|
||||
ModuleName, moduleBufferID);
|
||||
break;
|
||||
case serialization::Status::Malformed:
|
||||
Ctx.Diags.diagnose(diagLoc, diag::serialization_malformed_module,
|
||||
moduleBufferID);
|
||||
@@ -1168,6 +1174,7 @@ void swift::serialization::diagnoseSerializedASTLoadFailureTransitive(
|
||||
case serialization::Status::NotInOSSA:
|
||||
case serialization::Status::NoncopyableGenericsMismatch:
|
||||
case serialization::Status::RevisionIncompatible:
|
||||
case serialization::Status::ChannelIncompatible:
|
||||
case serialization::Status::Malformed:
|
||||
case serialization::Status::MalformedDocumentation:
|
||||
case serialization::Status::FailedToLoadBridgingHeader:
|
||||
|
||||
89
test/Serialization/restrict-swiftmodule-to-channel.swift
Normal file
89
test/Serialization/restrict-swiftmodule-to-channel.swift
Normal file
@@ -0,0 +1,89 @@
|
||||
// Binary swiftmodules are restricted to a channel when set in the compiler.
|
||||
|
||||
// RUN: %empty-directory(%t/cache)
|
||||
// RUN: %empty-directory(%t/build)
|
||||
// RUN: split-file %s %t --leading-lines
|
||||
|
||||
//--- Lib.swift
|
||||
public func foo() {}
|
||||
|
||||
/// Build Lib as a resilient and non-resilient swiftmodule
|
||||
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -swift-version 5 \
|
||||
// RUN: -o %t/build -parse-stdlib -module-cache-path %t/cache \
|
||||
// RUN: -module-name ResilientLib -enable-library-evolution \
|
||||
// RUN: -emit-module-interface-path %t/build/ResilientLib.swiftinterface
|
||||
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -swift-version 5 \
|
||||
// RUN: -o %t/build -parse-stdlib -module-cache-path %t/cache \
|
||||
// RUN: -module-name NonResilientLib
|
||||
|
||||
/// Build a channel restricted Lib.
|
||||
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel \
|
||||
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -swift-version 5 \
|
||||
// RUN: -o %t/build -parse-stdlib -module-cache-path %t/cache \
|
||||
// RUN: -module-name ChannelLib -enable-library-evolution
|
||||
|
||||
|
||||
/// 2. Test importing the non-resilient no-channel library from a channel compiler.
|
||||
//--- NonResilientClient.swift
|
||||
import NonResilientLib // expected-error {{compiled module was created for a different distribution channel '' than the local compiler 'restricted-channel', please ensure 'NonResilientLib' is found from the expected path:}}
|
||||
foo()
|
||||
|
||||
/// Building a NonResilientLib client should reject the import for a tagged compiler
|
||||
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel \
|
||||
// RUN: %target-swift-frontend -typecheck %t/NonResilientClient.swift \
|
||||
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache \
|
||||
// RUN: -verify -verify-ignore-unknown
|
||||
|
||||
|
||||
/// 3. Test importing the resilient no-channel library.
|
||||
//--- ResilientClient.swift
|
||||
import ResilientLib // expected-reject-error {{compiled module was created for a different distribution channel '' than the local compiler 'restricted-channel', please ensure 'ResilientLib' is found from the expected path:}}
|
||||
// expected-rebuild-remark @-1 {{rebuilding module 'ResilientLib' from interface}}
|
||||
// expected-rebuild-note @-2 {{compiled module is out of date}}
|
||||
// expected-rebuild-note @-3 {{compiled for a different distribution channel}}
|
||||
foo()
|
||||
|
||||
/// ResilientLib client should rebuild from swiftinterface when available.
|
||||
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel \
|
||||
// RUN: %target-swift-frontend -typecheck %t/ResilientClient.swift \
|
||||
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache \
|
||||
// RUN: -verify -verify-additional-prefix rebuild- -Rmodule-interface-rebuild
|
||||
|
||||
// RUN: rm %t/build/ResilientLib.swiftinterface
|
||||
// RUN: %empty-directory(%t/cache)
|
||||
|
||||
/// Building a ResilientLib client should succeed in no-channel / dev mode.
|
||||
// RUN: %target-swift-frontend -typecheck %t/ResilientClient.swift \
|
||||
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache
|
||||
|
||||
/// Building a ResilientLib client should reject the import for a channel compiler.
|
||||
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel \
|
||||
// RUN: %target-swift-frontend -typecheck %t/ResilientClient.swift \
|
||||
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache \
|
||||
// RUN: -verify -verify-ignore-unknown -verify-additional-prefix reject-
|
||||
|
||||
/// Building a ResilientLib client should succeed for a channel compiler with SWIFT_IGNORE_SWIFTMODULE_REVISION
|
||||
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel SWIFT_IGNORE_SWIFTMODULE_REVISION=true \
|
||||
// RUN: %target-swift-frontend -typecheck %t/ResilientClient.swift \
|
||||
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache
|
||||
|
||||
|
||||
/// 4. Test importing the channel restricted library
|
||||
//--- ChannelClient.swift
|
||||
import ChannelLib // expected-reject-error {{compiled module was created for a different distribution channel 'restricted-channel' than the local compiler 'other-channel', please ensure 'ChannelLib' is found from the expected path}}
|
||||
foo()
|
||||
|
||||
/// Importing ChannelLib should succeed with the same channel.
|
||||
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel \
|
||||
// RUN: %target-swift-frontend -typecheck %t/ChannelClient.swift \
|
||||
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache
|
||||
|
||||
/// Importing ChannelLib should succeed with a dev compiler.
|
||||
// RUN: %target-swift-frontend -typecheck %t/ChannelClient.swift \
|
||||
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache
|
||||
|
||||
/// Importing ChannelLib from a different channel should be rejected.
|
||||
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=other-channel \
|
||||
// RUN: %target-swift-frontend -typecheck %t/ChannelClient.swift \
|
||||
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache \
|
||||
// RUN: -verify -verify-ignore-unknown -verify-additional-prefix reject-
|
||||
Reference in New Issue
Block a user