diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 5227dfa4e7c46..b3b32591277a5 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -782,6 +782,9 @@ ERROR(serialization_name_mismatch_repl,none, ERROR(serialization_target_incompatible,Fatal, "module %0 was created for incompatible target %1: %2", (Identifier, StringRef, StringRef)) +ERROR(serialization_sdk_mismatch,Fatal, + "cannot load module %0 built with SDK '%1' when using SDK '%2': %3", + (Identifier, StringRef, StringRef, StringRef)) ERROR(serialization_target_incompatible_repl,none, "module %0 was created for incompatible target %1: %2", (Identifier, StringRef, StringRef)) diff --git a/include/swift/AST/SearchPathOptions.h b/include/swift/AST/SearchPathOptions.h index f873e82ada368..02f61eadfe59a 100644 --- a/include/swift/AST/SearchPathOptions.h +++ b/include/swift/AST/SearchPathOptions.h @@ -82,6 +82,10 @@ class SearchPathOptions { /// would for a non-system header. bool DisableModulesValidateSystemDependencies = false; + /// Enforce loading only serialized modules built with the same SDK + /// as the context loading it. + bool EnableSameSDKCheck = true; + /// A set of compiled modules that may be ready to use. std::vector CandidateCompiledModules; diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index f8d161d346e51..6d14419be809c 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -127,6 +127,9 @@ namespace swift { /// The target variant SDK version, if known. Optional VariantSDKVersion; + /// The SDK canonical name, if known. + std::string SDKName; + /// The alternate name to use for the entry point instead of main. std::string entryPointFunctionName = "main"; diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 0097ea215f0ff..7ea2ec76be348 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -823,6 +823,9 @@ def target_sdk_version : Separate<["-"], "target-sdk-version">, def target_variant_sdk_version : Separate<["-"], "target-variant-sdk-version">, HelpText<"The version of target variant SDK used for compilation">; +def target_sdk_name : Separate<["-"], "target-sdk-name">, + HelpText<"Canonical name of the target SDK used for compilation">; + def extra_clang_options_only : Flag<["-"], "only-use-extra-clang-opts">, HelpText<"Options passed via -Xcc are sufficient for Clang configuration">; diff --git a/include/swift/Serialization/SerializationOptions.h b/include/swift/Serialization/SerializationOptions.h index 2b180591a19d7..8f19f7d9ed22d 100644 --- a/include/swift/Serialization/SerializationOptions.h +++ b/include/swift/Serialization/SerializationOptions.h @@ -35,6 +35,7 @@ namespace swift { bool SkipSymbolGraphInheritedDocs = true; bool IncludeSPISymbolsInSymbolGraph = false; llvm::VersionTuple UserModuleVersion; + std::string SDKName; StringRef GroupInfoPath; StringRef ImportedHeader; diff --git a/include/swift/Serialization/Validation.h b/include/swift/Serialization/Validation.h index e5564a139473d..a2ffe970ed987 100644 --- a/include/swift/Serialization/Validation.h +++ b/include/swift/Serialization/Validation.h @@ -66,7 +66,11 @@ enum class Status { TargetIncompatible, /// The module file was built for a target newer than the current target. - TargetTooNew + TargetTooNew, + + /// The module file was built with a different SDK than the one in use + /// to build the client. + SDKMismatch }; /// Returns true if the data looks like it contains a serialized AST. @@ -80,6 +84,7 @@ struct ValidationInfo { StringRef miscVersion = {}; version::Version compatibilityVersion = {}; llvm::VersionTuple userModuleVersion; + StringRef sdkName = {}; size_t bytes = 0; Status status = Status::Malformed; }; diff --git a/lib/APIDigester/ModuleAnalyzerNodes.cpp b/lib/APIDigester/ModuleAnalyzerNodes.cpp index c9b9fa9837f14..cd59b02dbffe0 100644 --- a/lib/APIDigester/ModuleAnalyzerNodes.cpp +++ b/lib/APIDigester/ModuleAnalyzerNodes.cpp @@ -2233,6 +2233,9 @@ swift::ide::api::getSDKNodeRoot(SDKContext &SDKCtx, auto &Ctx = CI.getASTContext(); + // Don't check if the stdlib was build with the same SDK as what is loaded + // here as some tests rely on using a different stdlib. + Ctx.SearchPathOpts.EnableSameSDKCheck = false; // Load standard library so that Clang importer can use it. auto *Stdlib = Ctx.getStdlibModule(/*loadIfAbsent=*/true); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index a854fc85e2dec..4c740f3cb4a00 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -774,6 +774,11 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, } } + // Get the SDK name. + if (Arg *A = Args.getLastArg(options::OPT_target_sdk_name)) { + Opts.SDKName = A->getValue(); + } + if (const Arg *A = Args.getLastArg(OPT_entry_point_function_name)) { Opts.entryPointFunctionName = A->getValue(); } diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index de292051246f9..685974fb55471 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -150,6 +150,7 @@ SerializationOptions CompilerInvocation::computeSerializationOptions( serializationOpts.ExtraClangOptions = getClangImporterOptions().ExtraArgs; serializationOpts.PublicDependentLibraries = getIRGenOptions().PublicLinkLibraries; + serializationOpts.SDKName = getLangOptions().SDKName; if (opts.EmitSymbolGraph) { if (!opts.SymbolGraphOutputDir.empty()) { diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index 4c8a3e18579da..19a3240826c5b 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -257,6 +257,8 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal( if (!getRelativeDepPath(InPath, SDKPath)) SerializationOpts.ModuleInterface = InPath; + SerializationOpts.SDKName = SubInstance.getASTContext().LangOpts.SDKName; + SmallVector Deps; bool serializeHashes = FEOpts.SerializeModuleInterfaceDependencyHashes; if (collectDepsForSerialization(SubInstance, Deps, serializeHashes)) { diff --git a/lib/Serialization/ModuleFile.cpp b/lib/Serialization/ModuleFile.cpp index 1d2aeeb027679..38990f4b4e4c5 100644 --- a/lib/Serialization/ModuleFile.cpp +++ b/lib/Serialization/ModuleFile.cpp @@ -149,6 +149,15 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc, return error(status); } + auto clientSDK = ctx.LangOpts.SDKName; + StringRef moduleSDK = Core->SDKName; + if (ctx.SearchPathOpts.EnableSameSDKCheck && + !moduleSDK.empty() && !clientSDK.empty() && + moduleSDK != clientSDK) { + status = Status::SDKMismatch; + return error(status); + } + for (const auto &searchPath : Core->SearchPaths) ctx.addSearchPath(searchPath.Path, searchPath.IsFramework, searchPath.IsSystem); diff --git a/lib/Serialization/ModuleFileSharedCore.cpp b/lib/Serialization/ModuleFileSharedCore.cpp index 8fba7e2ed85b1..eeec9a2fef78b 100644 --- a/lib/Serialization/ModuleFileSharedCore.cpp +++ b/lib/Serialization/ModuleFileSharedCore.cpp @@ -299,6 +299,10 @@ validateControlBlock(llvm::BitstreamCursor &cursor, case control_block::TARGET: result.targetTriple = blobData; break; + case control_block::SDK_NAME: { + result.sdkName = blobData; + break; + } default: // Unknown metadata record, possibly for use by a future version of the // module format. @@ -1210,6 +1214,7 @@ ModuleFileSharedCore::ModuleFileSharedCore( } Name = info.name; TargetTriple = info.targetTriple; + SDKName = info.sdkName; CompatibilityVersion = info.compatibilityVersion; UserModuleVersion = info.userModuleVersion; Bits.ArePrivateImportsEnabled = extInfo.arePrivateImportsEnabled(); diff --git a/lib/Serialization/ModuleFileSharedCore.h b/lib/Serialization/ModuleFileSharedCore.h index e192013c596fc..7f8fde8b28461 100644 --- a/lib/Serialization/ModuleFileSharedCore.h +++ b/lib/Serialization/ModuleFileSharedCore.h @@ -58,6 +58,9 @@ class ModuleFileSharedCore { /// The target the module was built for. StringRef TargetTriple; + /// The canonical name of the SDK the module was built with. + StringRef SDKName; + /// The name of the module interface this module was compiled from. /// /// Empty if this module didn't come from an interface file. diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 599ef3e5c0570..18e9530e8612e 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -56,7 +56,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 = 628; // unaligned pointer +const uint16_t SWIFTMODULE_VERSION_MINOR = 629; // BuilderSDK /// A standard hash seed used for all string hashes in a serialized module. /// @@ -754,7 +754,8 @@ namespace control_block { enum { METADATA = 1, MODULE_NAME, - TARGET + TARGET, + SDK_NAME }; using MetadataLayout = BCRecordLayout< @@ -779,6 +780,11 @@ namespace control_block { TARGET, BCBlob // LLVM triple >; + + using SDKNameLayout = BCRecordLayout< + SDK_NAME, + BCBlob + >; } /// The record types within the options block (a sub-block of the control diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 92ce769c8851b..0720a001b724f 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -802,6 +802,7 @@ void Serializer::writeBlockInfoBlock() { BLOCK_RECORD(control_block, METADATA); BLOCK_RECORD(control_block, MODULE_NAME); BLOCK_RECORD(control_block, TARGET); + BLOCK_RECORD(control_block, SDK_NAME); BLOCK(OPTIONS_BLOCK); BLOCK_RECORD(options_block, SDK_PATH); @@ -952,6 +953,7 @@ void Serializer::writeHeader(const SerializationOptions &options) { control_block::ModuleNameLayout ModuleName(Out); control_block::MetadataLayout Metadata(Out); control_block::TargetLayout Target(Out); + control_block::SDKNameLayout SDKName(Out); ModuleName.emit(ScratchRecord, M->getName().str()); @@ -985,6 +987,9 @@ void Serializer::writeHeader(const SerializationOptions &options) { userModuleSubminor, userModuleBuild, versionString.str()); + if (!options.SDKName.empty()) + SDKName.emit(ScratchRecord, options.SDKName); + Target.emit(ScratchRecord, M->getASTContext().LangOpts.Target.str()); { diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index 57c3443143717..5d5dac786cd21 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -976,6 +976,13 @@ void swift::serialization::diagnoseSerializedASTLoadFailure( moduleOSInfo.second, moduleBufferID); break; } + + case serialization::Status::SDKMismatch: + auto currentSDK = Ctx.LangOpts.SDKName; + auto moduleSDK = loadInfo.sdkName; + Ctx.Diags.diagnose(diagLoc, diag::serialization_sdk_mismatch, + ModuleName, moduleSDK, currentSDK, moduleBufferID); + break; } } diff --git a/test/DebugInfo/compileunit-sysroot-sdk.swift b/test/DebugInfo/compileunit-sysroot-sdk.swift index 0000a5f50e58d..283616f480b5b 100644 --- a/test/DebugInfo/compileunit-sysroot-sdk.swift +++ b/test/DebugInfo/compileunit-sysroot-sdk.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend %s -emit-ir -g -o - \ +// RUN: %target-swift-frontend %s -emit-ir -g -o - -parse-stdlib \ // RUN: -sdk /SWIFT_SYSROOT/MacOSX.sdk | %FileCheck %s // Test that sysroot and SDK are stored in the debug info. // CHECK: distinct !DICompileUnit({{.*}}sysroot: "/SWIFT_SYSROOT/MacOSX.sdk", diff --git a/test/Serialization/restrict-swiftmodule-to-sdk.swift b/test/Serialization/restrict-swiftmodule-to-sdk.swift new file mode 100644 index 0000000000000..5c678fe70a3a8 --- /dev/null +++ b/test/Serialization/restrict-swiftmodule-to-sdk.swift @@ -0,0 +1,20 @@ +// RUN: %empty-directory(%t/cache) +// RUN: %empty-directory(%t/build) +// RUN: %{python} %utils/split_file.py -o %t %s + +/// Build Lib against SDK A. +// RUN: %target-swift-frontend -emit-module %t/Lib.swift -swift-version 5 -target-sdk-name A -o %t/build -parse-stdlib -module-cache-path %t/cache + +/// Building Client against SDK A should work fine as expected. +// RUN: %target-swift-frontend -typecheck %t/Client.swift -swift-version 5 -target-sdk-name A -I %t/build -parse-stdlib -module-cache-path %t/cache + +/// Build Client against SDK B, this should fail at loading Lib against a different SDK than A. +// RUN: not %target-swift-frontend -typecheck %t/Client.swift -swift-version 5 -target-sdk-name B -I %t/build -parse-stdlib -module-cache-path %t/cache 2>&1 | %FileCheck %s +// CHECK: cannot load module 'Lib' built with SDK 'A' when using SDK 'B': {{.*}}Lib.swiftmodule + +// BEGIN Lib.swift +public func foo() {} + +// BEGIN Client.swift +import Lib +foo() diff --git a/test/api-digester/Inputs/mock-sdk-baseline.sdk/System/Library/Frameworks/SwiftFoo.framework/Modules/SwiftFoo.swiftmodule/arm64-apple-macos.swiftinterface b/test/api-digester/Inputs/mock-sdk-baseline.sdk/System/Library/Frameworks/SwiftFoo.framework/Modules/SwiftFoo.swiftmodule/arm64-apple-macos.swiftinterface index 04fba0eeed4da..851f24ce52b03 100644 --- a/test/api-digester/Inputs/mock-sdk-baseline.sdk/System/Library/Frameworks/SwiftFoo.framework/Modules/SwiftFoo.swiftmodule/arm64-apple-macos.swiftinterface +++ b/test/api-digester/Inputs/mock-sdk-baseline.sdk/System/Library/Frameworks/SwiftFoo.framework/Modules/SwiftFoo.swiftmodule/arm64-apple-macos.swiftinterface @@ -1,7 +1,6 @@ // swift-interface-format-version: 1.0 // swift-tools-version: Apple Swift version 5.1 (swiftlang-1100.0.38 clang-1100.0.20.14) -// swift-module-flags: -target arm64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name SwiftFoo -import Swift +// swift-module-flags: -target arm64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name SwiftFoo -parse-stdlib public class RemovedClass { @objc deinit } diff --git a/test/api-digester/Inputs/mock-sdk-baseline.sdk/System/Library/Frameworks/SwiftFoo.framework/Modules/SwiftFoo.swiftmodule/x86_64-apple-macos.swiftinterface b/test/api-digester/Inputs/mock-sdk-baseline.sdk/System/Library/Frameworks/SwiftFoo.framework/Modules/SwiftFoo.swiftmodule/x86_64-apple-macos.swiftinterface index 5dd2912092b75..616271689153f 100644 --- a/test/api-digester/Inputs/mock-sdk-baseline.sdk/System/Library/Frameworks/SwiftFoo.framework/Modules/SwiftFoo.swiftmodule/x86_64-apple-macos.swiftinterface +++ b/test/api-digester/Inputs/mock-sdk-baseline.sdk/System/Library/Frameworks/SwiftFoo.framework/Modules/SwiftFoo.swiftmodule/x86_64-apple-macos.swiftinterface @@ -1,7 +1,6 @@ // swift-interface-format-version: 1.0 // swift-tools-version: Apple Swift version 5.1 (swiftlang-1100.0.38 clang-1100.0.20.14) -// swift-module-flags: -target x86_64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name SwiftFoo -import Swift +// swift-module-flags: -target x86_64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name SwiftFoo -parse-stdlib public class RemovedClass { @objc deinit } diff --git a/test/api-digester/Inputs/mock-sdk.sdk/System/Library/Frameworks/SwiftFoo.framework/Modules/SwiftFoo.swiftmodule/arm64-apple-macos.swiftinterface b/test/api-digester/Inputs/mock-sdk.sdk/System/Library/Frameworks/SwiftFoo.framework/Modules/SwiftFoo.swiftmodule/arm64-apple-macos.swiftinterface index 606b8bd222422..f20e4f4217a6c 100644 --- a/test/api-digester/Inputs/mock-sdk.sdk/System/Library/Frameworks/SwiftFoo.framework/Modules/SwiftFoo.swiftmodule/arm64-apple-macos.swiftinterface +++ b/test/api-digester/Inputs/mock-sdk.sdk/System/Library/Frameworks/SwiftFoo.framework/Modules/SwiftFoo.swiftmodule/arm64-apple-macos.swiftinterface @@ -1,7 +1,6 @@ // swift-interface-format-version: 1.0 // swift-tools-version: Apple Swift version 5.1 (swiftlang-1100.0.38 clang-1100.0.20.14) -// swift-module-flags: -target arm64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name SwiftFoo -import Swift +// swift-module-flags: -target arm64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name SwiftFoo -parse-stdlib public class AddedClass { @objc deinit } diff --git a/test/api-digester/Inputs/mock-sdk.sdk/System/Library/Frameworks/SwiftFoo.framework/Modules/SwiftFoo.swiftmodule/x86_64-apple-macos.swiftinterface b/test/api-digester/Inputs/mock-sdk.sdk/System/Library/Frameworks/SwiftFoo.framework/Modules/SwiftFoo.swiftmodule/x86_64-apple-macos.swiftinterface index 3bc9676211a66..5315e1537ee54 100644 --- a/test/api-digester/Inputs/mock-sdk.sdk/System/Library/Frameworks/SwiftFoo.framework/Modules/SwiftFoo.swiftmodule/x86_64-apple-macos.swiftinterface +++ b/test/api-digester/Inputs/mock-sdk.sdk/System/Library/Frameworks/SwiftFoo.framework/Modules/SwiftFoo.swiftmodule/x86_64-apple-macos.swiftinterface @@ -1,7 +1,6 @@ // swift-interface-format-version: 1.0 // swift-tools-version: Apple Swift version 5.1 (swiftlang-1100.0.38 clang-1100.0.20.14) -// swift-module-flags: -target x86_64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name SwiftFoo -import Swift +// swift-module-flags: -target x86_64-apple-macos10.14 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -O -module-name SwiftFoo -parse-stdlib public class AddedClass { @objc deinit }