mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Merge pull request #63248 from xymus/swift-export-as
[ModuleInterface] Intro the flag `-export-as` for Swift modules
This commit is contained in:
@@ -180,6 +180,11 @@ ERROR(error_stdlib_module_name,none,
|
||||
"module name \"%0\" is reserved for the standard library"
|
||||
"%select{|; use -module-name flag to specify an alternate name}1",
|
||||
(StringRef, bool))
|
||||
|
||||
ERROR(error_bad_export_as_name,none,
|
||||
"export-as name \"%0\" is not a valid identifier",
|
||||
(StringRef))
|
||||
|
||||
ERROR(error_bad_package_name,none,
|
||||
"package name \"%0\" is not a valid identifier",
|
||||
(StringRef))
|
||||
|
||||
@@ -322,8 +322,11 @@ public:
|
||||
/// The 'real name' is the actual binary name of the module, which can be different from the 'name'
|
||||
/// if module aliasing was used (via -module-alias flag).
|
||||
///
|
||||
/// Usually this is the module real name itself, but certain Clang features allow
|
||||
/// substituting another name instead.
|
||||
/// This is usually the module real name which can be overriden by an
|
||||
/// `export_as` definition of a clang module, or `-export-as` flag on an
|
||||
/// imported Swift module. Swift modules built from source do not apply
|
||||
/// their own `-export-as` flag, this way the swiftinterface can be
|
||||
/// verified.
|
||||
virtual StringRef getExportedModuleName() const {
|
||||
return getParentModule()->getRealName().str();
|
||||
}
|
||||
|
||||
@@ -175,6 +175,10 @@ class ModuleDecl
|
||||
|
||||
/// The name of the package this module belongs to
|
||||
mutable Identifier PackageName;
|
||||
|
||||
/// Module name to use when referenced in clients module interfaces.
|
||||
mutable Identifier ExportAsName;
|
||||
|
||||
public:
|
||||
/// Produces the components of a given module's full name in reverse order.
|
||||
///
|
||||
@@ -410,6 +414,12 @@ public:
|
||||
PackageName = name;
|
||||
}
|
||||
|
||||
Identifier getExportAsName() const { return ExportAsName; }
|
||||
|
||||
void setExportAsName(Identifier name) {
|
||||
ExportAsName = name;
|
||||
}
|
||||
|
||||
/// Retrieve the actual module name of an alias used for this module (if any).
|
||||
///
|
||||
/// For example, if '-module-alias Foo=Bar' is passed in when building the main module,
|
||||
|
||||
@@ -67,6 +67,9 @@ public:
|
||||
/// The name of the package this module belongs to.
|
||||
std::string PackageName;
|
||||
|
||||
/// Module name to use when referenced in clients module interfaces.
|
||||
std::string ExportAsName;
|
||||
|
||||
/// Arguments which should be passed in immediate mode.
|
||||
std::vector<std::string> ImmediateArgv;
|
||||
|
||||
|
||||
@@ -484,6 +484,9 @@ def module_abi_name : Separate<["-"], "module-abi-name">,
|
||||
def package_name : Separate<["-"], "package-name">,
|
||||
Flags<[FrontendOption, ModuleInterfaceOptionIgnorable]>,
|
||||
HelpText<"Name of the package the module belongs to">;
|
||||
def export_as : Separate<["-"], "export-as">,
|
||||
Flags<[FrontendOption, ModuleInterfaceOptionIgnorable]>,
|
||||
HelpText<"Module name to use when referenced in clients module interfaces">;
|
||||
|
||||
def emit_module : Flag<["-"], "emit-module">,
|
||||
Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput]>,
|
||||
|
||||
@@ -455,6 +455,8 @@ public:
|
||||
|
||||
virtual StringRef getModuleDefiningPath() const override;
|
||||
|
||||
virtual StringRef getExportedModuleName() const override;
|
||||
|
||||
ValueDecl *getMainDecl() const override;
|
||||
|
||||
bool hasEntryPoint() const override;
|
||||
|
||||
@@ -108,6 +108,7 @@ class ExtendedValidationInfo {
|
||||
std::string SDKPath;
|
||||
StringRef ModuleABIName;
|
||||
StringRef ModulePackageName;
|
||||
StringRef ExportAsName;
|
||||
struct {
|
||||
unsigned ArePrivateImportsEnabled : 1;
|
||||
unsigned IsSIB : 1;
|
||||
@@ -183,6 +184,9 @@ public:
|
||||
StringRef getModulePackageName() const { return ModulePackageName; }
|
||||
void setModulePackageName(StringRef name) { ModulePackageName = name; }
|
||||
|
||||
StringRef getExportAsName() const { return ExportAsName; }
|
||||
void setExportAsName(StringRef name) { ExportAsName = name; }
|
||||
|
||||
bool isConcurrencyChecked() const {
|
||||
return Bits.IsConcurrencyChecked;
|
||||
}
|
||||
|
||||
@@ -253,6 +253,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
|
||||
inputArgs.AddLastArg(arguments, options::OPT_module_link_name);
|
||||
inputArgs.AddLastArg(arguments, options::OPT_module_abi_name);
|
||||
inputArgs.AddLastArg(arguments, options::OPT_package_name);
|
||||
inputArgs.AddLastArg(arguments, options::OPT_export_as);
|
||||
inputArgs.AddLastArg(arguments, options::OPT_nostdimport);
|
||||
inputArgs.AddLastArg(arguments, options::OPT_parse_stdlib);
|
||||
inputArgs.AddLastArg(arguments, options::OPT_resource_dir);
|
||||
|
||||
@@ -279,6 +279,14 @@ bool ArgsToFrontendOptionsConverter::convert(
|
||||
Opts.PackageName = pkgName;
|
||||
}
|
||||
|
||||
if (const Arg *A = Args.getLastArg(OPT_export_as)) {
|
||||
auto exportAs = A->getValue();
|
||||
if (!Lexer::isIdentifier(exportAs))
|
||||
Diags.diagnose(SourceLoc(), diag::error_bad_export_as_name, exportAs);
|
||||
else
|
||||
Opts.ExportAsName = exportAs;
|
||||
}
|
||||
|
||||
// This must be called after computing module name, module abi name,
|
||||
// and module link name. If computing module aliases is unsuccessful,
|
||||
// return early.
|
||||
|
||||
@@ -1085,6 +1085,10 @@ ModuleDecl *CompilerInstance::getMainModule() const {
|
||||
MainModule->setPackageName(getASTContext().getIdentifier(
|
||||
Invocation.getFrontendOptions().PackageName));
|
||||
}
|
||||
if (!Invocation.getFrontendOptions().ExportAsName.empty()) {
|
||||
MainModule->setExportAsName(getASTContext().getIdentifier(
|
||||
Invocation.getFrontendOptions().ExportAsName));
|
||||
}
|
||||
if (Invocation.getFrontendOptions().EnableLibraryEvolution)
|
||||
MainModule->setResilienceStrategy(ResilienceStrategy::Resilient);
|
||||
if (Invocation.getLangOptions().isSwiftVersionAtLeast(6))
|
||||
|
||||
@@ -1324,3 +1324,10 @@ StringRef SerializedASTFile::getModuleDefiningPath() const {
|
||||
|
||||
return moduleFilename;
|
||||
}
|
||||
|
||||
StringRef SerializedASTFile::getExportedModuleName() const {
|
||||
auto name = File.getModuleExportAsName();
|
||||
if (!name.empty())
|
||||
return name;
|
||||
return FileUnit::getExportedModuleName();
|
||||
}
|
||||
|
||||
@@ -535,6 +535,11 @@ public:
|
||||
StringRef getModulePackageName() const {
|
||||
return Core->ModulePackageName;
|
||||
}
|
||||
|
||||
StringRef getModuleExportAsName() const {
|
||||
return Core->ModuleExportAsName;
|
||||
}
|
||||
|
||||
/// The ABI name of the module.
|
||||
StringRef getModuleABIName() const {
|
||||
return Core->ModuleABIName;
|
||||
|
||||
@@ -166,6 +166,9 @@ static bool readOptionsBlock(llvm::BitstreamCursor &cursor,
|
||||
case options_block::MODULE_PACKAGE_NAME:
|
||||
extendedInfo.setModulePackageName(blobData);
|
||||
break;
|
||||
case options_block::MODULE_EXPORT_AS_NAME:
|
||||
extendedInfo.setExportAsName(blobData);
|
||||
break;
|
||||
default:
|
||||
// Unknown options record, possibly for use by a future version of the
|
||||
// module format.
|
||||
@@ -1350,6 +1353,7 @@ ModuleFileSharedCore::ModuleFileSharedCore(
|
||||
MiscVersion = info.miscVersion;
|
||||
ModuleABIName = extInfo.getModuleABIName();
|
||||
ModulePackageName = extInfo.getModulePackageName();
|
||||
ModuleExportAsName = extInfo.getExportAsName();
|
||||
|
||||
hasValidControlBlock = true;
|
||||
break;
|
||||
|
||||
@@ -85,6 +85,9 @@ class ModuleFileSharedCore {
|
||||
/// The name of the package this module belongs to.
|
||||
StringRef ModulePackageName;
|
||||
|
||||
/// Module name to use when referenced in clients module interfaces.
|
||||
StringRef ModuleExportAsName;
|
||||
|
||||
/// \c true if this module has incremental dependency information.
|
||||
bool HasIncrementalInfo = false;
|
||||
|
||||
|
||||
@@ -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 = 739; // CxxStdlib
|
||||
const uint16_t SWIFTMODULE_VERSION_MINOR = 740; // export-as
|
||||
|
||||
/// A standard hash seed used for all string hashes in a serialized module.
|
||||
///
|
||||
@@ -861,6 +861,7 @@ namespace options_block {
|
||||
MODULE_ABI_NAME,
|
||||
IS_CONCURRENCY_CHECKED,
|
||||
MODULE_PACKAGE_NAME,
|
||||
MODULE_EXPORT_AS_NAME,
|
||||
};
|
||||
|
||||
using SDKPathLayout = BCRecordLayout<
|
||||
@@ -924,6 +925,11 @@ namespace options_block {
|
||||
MODULE_PACKAGE_NAME,
|
||||
BCBlob
|
||||
>;
|
||||
|
||||
using ModuleExportAsNameLayout = BCRecordLayout<
|
||||
MODULE_EXPORT_AS_NAME,
|
||||
BCBlob
|
||||
>;
|
||||
}
|
||||
|
||||
/// The record types within the input block.
|
||||
|
||||
@@ -1073,6 +1073,11 @@ void Serializer::writeHeader(const SerializationOptions &options) {
|
||||
PackageName.emit(ScratchRecord, M->getPackageName().str());
|
||||
}
|
||||
|
||||
if (!M->getExportAsName().empty()) {
|
||||
options_block::ModuleExportAsNameLayout ExportAs(Out);
|
||||
ExportAs.emit(ScratchRecord, M->getExportAsName().str());
|
||||
}
|
||||
|
||||
if (M->isConcurrencyChecked()) {
|
||||
options_block::IsConcurrencyCheckedLayout IsConcurrencyChecked(Out);
|
||||
IsConcurrencyChecked.emit(ScratchRecord);
|
||||
|
||||
89
test/ModuleInterface/swift-export-as.swift
Normal file
89
test/ModuleInterface/swift-export-as.swift
Normal file
@@ -0,0 +1,89 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// RUN: split-file %s %t
|
||||
|
||||
// RUN: %target-swift-frontend -emit-module %t/PrivateLib.swift \
|
||||
// RUN: -swift-version 5 -enable-library-evolution \
|
||||
// RUN: -export-as PublicLib \
|
||||
// RUN: -o %t/PrivateLib.swiftmodule \
|
||||
// RUN: -emit-module-interface-path %t/PrivateLib.swiftinterface \
|
||||
// RUN: -emit-private-module-interface-path %t/PrivateLib.private.swiftinterface
|
||||
// RUN: %target-swift-typecheck-module-from-interface(%t/PrivateLib.swiftinterface)
|
||||
// RUN: %target-swift-typecheck-module-from-interface(%t/PrivateLib.private.swiftinterface) \
|
||||
// RUN: -module-name PrivateLib
|
||||
// RUN: cat %t/PrivateLib.swiftinterface | %FileCheck --check-prefixes=PRIVATELIB-PUBLIC %s
|
||||
// RUN: cat %t/PrivateLib.private.swiftinterface | %FileCheck --check-prefixes=PRIVATELIB-PUBLIC %s
|
||||
|
||||
// RUN: %target-swift-frontend -emit-module %t/PublicLib.swift -I %t \
|
||||
// RUN: -swift-version 5 -enable-library-evolution \
|
||||
// RUN: -o %t/PublicLib.swiftmodule \
|
||||
// RUN: -emit-module-interface-path %t/PublicLib.swiftinterface \
|
||||
// RUN: -emit-private-module-interface-path %t/PublicLib.private.swiftinterface
|
||||
// RUN: %target-swift-typecheck-module-from-interface(%t/PublicLib.swiftinterface) -I %t
|
||||
// RUN: %target-swift-typecheck-module-from-interface(%t/PublicLib.private.swiftinterface) -I %t \
|
||||
// RUN: -module-name PublicLib
|
||||
// RUN: cat %t/PublicLib.swiftinterface | %FileCheck --check-prefixes=PUBLICLIB-PUBLIC %s
|
||||
// RUN: cat %t/PublicLib.private.swiftinterface | %FileCheck --check-prefixes=PUBLICLIB-PUBLIC %s
|
||||
|
||||
/// Default logic applying export-as in both swiftinterface.
|
||||
// RUN: %target-swift-frontend -emit-module %t/ClientLib.swift -I %t \
|
||||
// RUN: -swift-version 5 -enable-library-evolution \
|
||||
// RUN: -o %t/ClientLib.swiftmodule \
|
||||
// RUN: -emit-module-interface-path %t/ClientLib.swiftinterface \
|
||||
// RUN: -emit-private-module-interface-path %t/ClientLib.private.swiftinterface
|
||||
// RUN: %target-swift-typecheck-module-from-interface(%t/ClientLib.swiftinterface) -I %t
|
||||
// RUN: %target-swift-typecheck-module-from-interface(%t/ClientLib.private.swiftinterface) -I %t
|
||||
// RUN: cat %t/ClientLib.swiftinterface | %FileCheck --check-prefixes=CLIENT-PUBLIC %s
|
||||
// RUN: cat %t/ClientLib.private.swiftinterface | %FileCheck --check-prefixes=CLIENT-PUBLIC %s
|
||||
|
||||
/// New logic applying export-as only in the public swiftinterface with
|
||||
/// `-enable-experimental-feature ModuleInterfaceExportAs`.
|
||||
// RUN: %target-swift-frontend -emit-module %t/ClientLib.swift -I %t \
|
||||
// RUN: -swift-version 5 -enable-library-evolution \
|
||||
// RUN: -o %t/ClientLib.swiftmodule \
|
||||
// RUN: -emit-module-interface-path %t/ClientLib.swiftinterface \
|
||||
// RUN: -emit-private-module-interface-path %t/ClientLib.private.swiftinterface \
|
||||
// RUN: -enable-experimental-feature ModuleInterfaceExportAs
|
||||
// RUN: %target-swift-typecheck-module-from-interface(%t/ClientLib.swiftinterface) -I %t
|
||||
// RUN: %target-swift-typecheck-module-from-interface(%t/ClientLib.private.swiftinterface) -I %t
|
||||
// RUN: cat %t/ClientLib.swiftinterface | %FileCheck --check-prefixes=CLIENT-PUBLIC %s
|
||||
// RUN: cat %t/ClientLib.private.swiftinterface | %FileCheck --check-prefixes=CLIENT-PRIVATE %s
|
||||
|
||||
/// Check that we get the same behavior using swiftinterfaces only.
|
||||
// RUN: rm -f %t/PrivateLib.swiftmodule %t/PublicLib.swiftmodule
|
||||
// RUN: %target-swift-frontend -emit-module %t/ClientLib.swift -I %t \
|
||||
// RUN: -swift-version 5 -enable-library-evolution \
|
||||
// RUN: -o %t/ClientLib.swiftmodule \
|
||||
// RUN: -emit-module-interface-path %t/ClientLib.swiftinterface \
|
||||
// RUN: -emit-private-module-interface-path %t/ClientLib.private.swiftinterface \
|
||||
// RUN: -enable-experimental-feature ModuleInterfaceExportAs
|
||||
// RUN: %target-swift-typecheck-module-from-interface(%t/ClientLib.swiftinterface) -I %t
|
||||
// RUN: %target-swift-typecheck-module-from-interface(%t/ClientLib.private.swiftinterface) -I %t
|
||||
// RUN: cat %t/ClientLib.swiftinterface | %FileCheck --check-prefixes=CLIENT-PUBLIC %s
|
||||
// RUN: cat %t/ClientLib.private.swiftinterface | %FileCheck --check-prefixes=CLIENT-PRIVATE %s
|
||||
|
||||
//--- PrivateLib.swift
|
||||
|
||||
public struct PrivateNameStruct {}
|
||||
|
||||
public func privateLibUser(_ arg: PrivateNameStruct) {}
|
||||
// PRIVATELIB-PUBLIC: arg: PrivateLib.PrivateNameStruct
|
||||
|
||||
//--- PublicLib.swift
|
||||
|
||||
@_exported import PrivateLib
|
||||
|
||||
public struct PublicNameStruct {}
|
||||
|
||||
public func publicLibUser(_ arg: PrivateNameStruct) {}
|
||||
// PUBLICLIB-PUBLIC: arg: PublicLib.PrivateNameStruct
|
||||
|
||||
//--- ClientLib.swift
|
||||
|
||||
import PublicLib
|
||||
|
||||
public func userOfPrivate(_ argUserOfPrivate: PrivateNameStruct) {}
|
||||
// CLIENT-PUBLIC: argUserOfPrivate: PublicLib.PrivateNameStruct
|
||||
// CLIENT-PRIVATE: argUserOfPrivate: PrivateLib.PrivateNameStruct
|
||||
public func userOfPublic(_ argUserOfPublic: PublicNameStruct) {}
|
||||
// CLIENT-PUBLIC: argUserOfPublic: PublicLib.PublicNameStruct
|
||||
// CLIENT-PRIVATE: argUserOfPublic: PublicLib.PublicNameStruct
|
||||
Reference in New Issue
Block a user