diff --git a/cmake/modules/AddPureSwift.cmake b/cmake/modules/AddPureSwift.cmake index 47469796c86dd..1e6c769854c2b 100644 --- a/cmake/modules/AddPureSwift.cmake +++ b/cmake/modules/AddPureSwift.cmake @@ -4,13 +4,12 @@ include(macCatalystUtils) function(force_add_dependencies TARGET) foreach(DEPENDENCY ${ARGN}) string(REGEX REPLACE [<>:\"/\\|?*] _ sanitized ${DEPENDENCY}) - add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/forced-${sanitized}-dep.swift - COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/forced-${sanitized}-dep.swift + set(depfile "${CMAKE_CURRENT_BINARY_DIR}/forced-${sanitized}-dep.swift") + add_custom_command(OUTPUT ${depfile} + COMMAND ${CMAKE_COMMAND} -E touch ${depfile} DEPENDS ${DEPENDENCY} ) - target_sources(${TARGET} PRIVATE - ${CMAKE_CURRENT_BINARY_DIR}/forced-${sanitized}-dep.swift - ) + target_sources(${TARGET} PRIVATE ${depfile}) endforeach() endfunction() @@ -198,15 +197,6 @@ function(add_pure_swift_host_library name) # Depends on all '*.h' files in 'include/module.modulemap'. force_add_dependencies(${name} importedHeaderDependencies) - # Workaround to touch the library and its objects so that we don't - # continually rebuild (again, see corresponding change in swift-syntax). - add_custom_command( - TARGET ${name} - POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E touch_nocreate $ $ "${SWIFT_HOST_LIBRARIES_DEST_DIR}/${name}.swiftmodule" "${CMAKE_CURRENT_BINARY_DIR}/${name}.swiftmodule" - COMMAND_EXPAND_LISTS - COMMENT "Update mtime of library outputs workaround") - # Link against dependencies. target_link_libraries(${name} PUBLIC ${APSHL_DEPENDENCIES} @@ -216,15 +206,7 @@ function(add_pure_swift_host_library name) ${APSHL_SWIFT_DEPENDENCIES} ) - # Make sure we can use the host libraries. - target_include_directories(${name} PUBLIC - "${SWIFT_HOST_LIBRARIES_DEST_DIR}") - target_link_directories(${name} PUBLIC - "${SWIFT_HOST_LIBRARIES_DEST_DIR}") - if(APSHL_EMIT_MODULE) - # Determine where Swift modules will be built and installed. - set(module_triple "${SWIFT_HOST_MODULE_TRIPLE}") set(module_dir "${SWIFT_HOST_LIBRARIES_DEST_DIR}") set(module_base "${module_dir}/${name}.swiftmodule") @@ -233,14 +215,6 @@ function(add_pure_swift_host_library name) set(module_private_interface_file "${module_base}/${module_triple}.private.swiftinterface") set(module_sourceinfo_file "${module_base}/${module_triple}.swiftsourceinfo") - set_target_properties(${name} PROPERTIES - # Set the default module name to the target name. - Swift_MODULE_NAME ${name} - # Install the Swift module into the appropriate location. - Swift_MODULE_DIRECTORY ${module_dir} - # NOTE: workaround for CMake not setting up include flags. - INTERFACE_INCLUDE_DIRECTORIES ${module_dir}) - # Create the module directory. add_custom_command( TARGET ${name} @@ -260,12 +234,27 @@ function(add_pure_swift_host_library name) >) else() # Emit a swiftmodule in the current directory. - set_target_properties(${name} PROPERTIES - Swift_MODULE_NAME ${name} - Swift_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - set(module_file "${CMAKE_CURRENT_BINARY_DIR}/${name}.swiftmodule") + set(module_dir "${CMAKE_CURRENT_BINARY_DIR}/modules") + set(module_file "${module_dir}/${name}.swiftmodule") endif() + set_target_properties(${name} PROPERTIES + # Set the default module name to the target name. + Swift_MODULE_NAME ${name} + # Install the Swift module into the appropriate location. + Swift_MODULE_DIRECTORY ${module_dir} + # NOTE: workaround for CMake not setting up include flags. + INTERFACE_INCLUDE_DIRECTORIES ${module_dir}) + + # Workaround to touch the library and its objects so that we don't + # continually rebuild (again, see corresponding change in swift-syntax). + add_custom_command( + TARGET ${name} + POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E touch_nocreate $ $ "${module_file}" + COMMAND_EXPAND_LISTS + COMMENT "Update mtime of library outputs workaround") + # Downstream linking should include the swiftmodule in debug builds to allow lldb to # work correctly. Only do this on Darwin since neither gold (currently used by default # on Linux), nor the default Windows linker 'link' support '-add_ast_path'. diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index b7e56cdfaa4cd..9b2e7634ec9c0 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -1008,12 +1008,12 @@ function(add_swift_host_tool executable) set_property( TARGET ${executable} APPEND PROPERTY INSTALL_RPATH - "@executable_path/../${extra_relative_rpath}lib/swift/host") + "@executable_path/../${extra_relative_rpath}lib/swift/host/compiler") else() set_property( TARGET ${executable} APPEND PROPERTY INSTALL_RPATH - "$ORIGIN/../${extra_relative_rpath}lib/swift/host") + "$ORIGIN/../${extra_relative_rpath}lib/swift/host/compiler") endif() endif() diff --git a/cmake/modules/AddSwiftUnittests.cmake b/cmake/modules/AddSwiftUnittests.cmake index 6626fe8bcb68a..0979438fa032b 100644 --- a/cmake/modules/AddSwiftUnittests.cmake +++ b/cmake/modules/AddSwiftUnittests.cmake @@ -133,11 +133,11 @@ function(add_swift_unittest test_dirname) if(SWIFT_HOST_VARIANT_SDK IN_LIST SWIFT_DARWIN_PLATFORMS) set_property( TARGET ${test_dirname} - APPEND PROPERTY INSTALL_RPATH "@executable_path/${relative_lib_path}/swift/host") + APPEND PROPERTY INSTALL_RPATH "@executable_path/${relative_lib_path}/swift/host/compiler") elseif(SWIFT_HOST_VARIANT_SDK MATCHES "LINUX|ANDROID|OPENBSD|FREEBSD") set_property( TARGET ${test_dirname} - APPEND PROPERTY INSTALL_RPATH "$ORIGIN/${relative_lib_path}/swift/host") + APPEND PROPERTY INSTALL_RPATH "$ORIGIN/${relative_lib_path}/swift/host/compiler") endif() endif() endfunction() diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index 5a2783bb3e451..6f466cca6269d 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -1863,9 +1863,6 @@ void Plugin_setCapability(PluginHandle handle, /// Get a capability data set by \c Plugin_setCapability . PluginCapabilityPtr _Nullable Plugin_getCapability(PluginHandle handle); -/// Get the executable file path of the plugin. -const char *Plugin_getExecutableFilePath(PluginHandle handle); - /// Lock the plugin. Clients should lock it during sending and recving the /// response. void Plugin_lock(PluginHandle handle); diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index f0622adfcd0cf..09787818085a5 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -84,8 +84,6 @@ namespace swift { class DifferentiableAttr; class ExtensionDecl; struct ExternalSourceLocs; - class LoadedExecutablePlugin; - class LoadedLibraryPlugin; class ForeignRepresentationInfo; class FuncDecl; class GenericContext; diff --git a/include/swift/AST/MacroDefinition.h b/include/swift/AST/MacroDefinition.h index ac37b885eb36e..77ac17dc101d4 100644 --- a/include/swift/AST/MacroDefinition.h +++ b/include/swift/AST/MacroDefinition.h @@ -26,24 +26,38 @@ namespace swift { class ASTContext; /// A reference to an external macro definition that is understood by ASTGen. -struct ExternalMacroDefinition { - enum class PluginKind : int8_t { - InProcess = 0, - Executable = 1, - Error = -1, +class ExternalMacroDefinition { + enum class Status : int8_t { + Success = 0, + Error, }; - PluginKind kind; + Status status; /// ASTGen's notion of an macro definition, which is opaque to the C++ part /// of the compiler. If 'kind' is 'PluginKind::Error', this is a C-string to /// the error message - const void *opaqueHandle = nullptr; + const void *opaqueHandle; + + ExternalMacroDefinition(Status status, const void *opaqueHandle) + : status(status), opaqueHandle(opaqueHandle) {} + +public: + static ExternalMacroDefinition success(const void *opaqueHandle) { + return ExternalMacroDefinition{Status::Success, opaqueHandle}; + } static ExternalMacroDefinition error(NullTerminatedStringRef message) { - return ExternalMacroDefinition{PluginKind::Error, + return ExternalMacroDefinition{Status::Error, static_cast(message.data())}; } - bool isError() const { return kind == PluginKind::Error; } + + const void *get() { + if (status != Status::Success) + return nullptr; + return opaqueHandle; + } + bool isError() const { return status == Status::Error; } NullTerminatedStringRef getErrorMessage() const { + assert(isError()); return static_cast(opaqueHandle); } }; diff --git a/include/swift/AST/PluginLoader.h b/include/swift/AST/PluginLoader.h index f35d5adea7fb8..fdcd73f13ba97 100644 --- a/include/swift/AST/PluginLoader.h +++ b/include/swift/AST/PluginLoader.h @@ -78,15 +78,14 @@ class PluginLoader { /// returns a nullptr. /// NOTE: This method is idempotent. If the plugin is already loaded, the same /// instance is simply returned. - llvm::Expected loadLibraryPlugin(llvm::StringRef path); + llvm::Expected getInProcessPlugins(); /// Launch the specified executable plugin path resolving the path with the /// current VFS. If it fails to load the plugin, a diagnostic is emitted, and /// returns a nullptr. /// NOTE: This method is idempotent. If the plugin is already loaded, the same /// instance is simply returned. - llvm::Expected - loadExecutablePlugin(llvm::StringRef path); + llvm::Expected loadExecutablePlugin(llvm::StringRef path); /// Add the specified plugin associated with the module name to the dependency /// tracker if needed. diff --git a/include/swift/AST/PluginRegistry.h b/include/swift/AST/PluginRegistry.h index 74a323e520b80..3f417e1ad3d55 100644 --- a/include/swift/AST/PluginRegistry.h +++ b/include/swift/AST/PluginRegistry.h @@ -17,6 +17,7 @@ #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Chrono.h" +#include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/Error.h" #include "llvm/Support/Program.h" @@ -25,26 +26,114 @@ namespace swift { -/// Represent a 'dlopen'ed plugin library. -class LoadedLibraryPlugin { - // Opaque handle used to interface with OS-specific dynamic library. - void *handle; +/// Base class for compiler plugins, or plugin servers. +class CompilerPlugin { + const std::string path; - /// Cache of loaded symbols. - llvm::StringMap resolvedSymbols; + std::mutex mtx; + + /// Opaque value of the protocol capability of the plugin. This is a + /// value from ASTGen. + const void *capability = nullptr; + + /// Cleanup function to call ASTGen. + std::function cleanup; + + /// Callbacks to be called when the connection is restored. + llvm::SmallVector *, 0> onReconnect; + + /// Flag to enable dumping of plugin messages. + bool dumpMessaging = false; - /// Path to the plugin library. - const std::string LibraryPath; +protected: + CompilerPlugin(llvm::StringRef path) : path(path) {} + + bool shouldDumpMessaging() const { return dumpMessaging; } public: - LoadedLibraryPlugin(void *handle, StringRef path) - : handle(handle), LibraryPath(path) {} + NullTerminatedStringRef getPath() const { + return {path.c_str(), path.size()}; + } + + void lock() { mtx.lock(); } + void unlock() { mtx.unlock(); } + + bool isInitialized() const { return bool(cleanup); } + void setCleanup(std::function cleanup) { + this->cleanup = cleanup; + } + + const void *getCapability() { return capability; }; + void setCapability(const void *newValue) { capability = newValue; }; + + void setDumpMessaging(bool flag) { dumpMessaging = flag; } + + virtual ~CompilerPlugin(); + + /// Launch the plugin if it's not already running, or it's stale. Return an + /// error if it's fails to execute it. + virtual llvm::Error spawnIfNeeded() = 0; + + /// Send a message to the plugin. + virtual llvm::Error sendMessage(llvm::StringRef message) = 0; + + /// Wait for a message from plugin and returns it. + virtual llvm::Expected waitForNextMessage() = 0; + + /// Add "on reconnect" callback. + /// These callbacks are called when `spawnIfNeeded()` relaunched the plugin. + void addOnReconnect(std::function *fn) { + onReconnect.push_back(fn); + } - /// Finds the address of the given symbol within the library. - void *getAddressOfSymbol(const char *symbolName); + /// Remove "on reconnect" callback. + void removeOnReconnect(std::function *fn) { + llvm::erase_value(onReconnect, fn); + } - NullTerminatedStringRef getLibraryPath() { - return {LibraryPath.c_str(), LibraryPath.size()}; + ArrayRef *> getOnReconnectCallbacks() { + return onReconnect; + } +}; + +/// Represents a in-process plugin server. +class InProcessPlugins : public CompilerPlugin { + /// PluginServer + llvm::sys::DynamicLibrary server; + + /// Entry point in the in-process plugin server. It receives the request + /// string and populate the response string. The return value indicates there + /// was an error. If true the returned string contains the error message. + /// 'free'ing the populated `responseDataPtr` is caller's responsibility. + using HandleMessageFunction = bool (*)(const char *requestData, + size_t requestLength, + char **responseDataPtr, + size_t *responseDataLengthPtr); + HandleMessageFunction handleMessageFn; + + /// Temporary storage for the response data from 'handleMessageFn'. + std::string receivedResponse; + + InProcessPlugins(llvm::StringRef serverPath, llvm::sys::DynamicLibrary server, + HandleMessageFunction handleMessageFn) + : CompilerPlugin(serverPath), server(server), + handleMessageFn(handleMessageFn) {} + +public: + /// Create an instance by loading the in-process plugin server at 'serverPath' + /// and return it. + static llvm::Expected> + create(const char *serverPath); + + /// Send a message to the plugin. + llvm::Error sendMessage(llvm::StringRef message) override; + + /// Wait for a message from plugin and returns it. + llvm::Expected waitForNextMessage() override; + + llvm::Error spawnIfNeeded() override { + // NOOP. It's always loaded. + return llvm::Error::success(); } }; @@ -56,7 +145,7 @@ class LoadedLibraryPlugin { /// launch it and manages the process. When the plugin process crashes, this /// should automatically relaunch the process so the clients can keep using this /// object as the interface. -class LoadedExecutablePlugin { +class LoadedExecutablePlugin : public CompilerPlugin { /// Represents the current process of the executable plugin. struct PluginProcess { @@ -76,38 +165,23 @@ class LoadedExecutablePlugin { /// Launched current process. std::unique_ptr Process; - /// Path to the plugin executable. - const std::string ExecutablePath; - /// Last modification time of the `ExecutablePath` when this is initialized. const llvm::sys::TimePoint<> LastModificationTime; - /// Opaque value of the protocol capability of the pluugin. This is a - /// value from ASTGen. - const void *capability = nullptr; - - /// Callbacks to be called when the connection is restored. - llvm::SmallVector *, 0> onReconnect; - /// Disable sandbox. bool disableSandbox = false; - /// Flag to dump plugin messagings. - bool dumpMessaging = false; - - /// Cleanup function to call ASTGen. - std::function cleanup; - - std::mutex mtx; + /// Mark the current process "stale" (not usable anymore for some reason, + /// probably crashed). + void setStale() { Process->isStale = true; } public: LoadedExecutablePlugin(llvm::StringRef ExecutablePath, llvm::sys::TimePoint<> LastModificationTime, bool disableSandbox) - : ExecutablePath(ExecutablePath), + : CompilerPlugin(ExecutablePath), LastModificationTime(LastModificationTime), disableSandbox(disableSandbox){}; - ~LoadedExecutablePlugin(); /// The last modification time of 'ExecutablePath' when this object is /// created. @@ -118,54 +192,23 @@ class LoadedExecutablePlugin { /// Indicates that the current process is usable. bool isAlive() const { return Process != nullptr && !Process->isStale; } - /// Mark the current process "stale". - void setStale() const { Process->isStale = true; } - - void lock() { mtx.lock(); } - void unlock() { mtx.unlock(); } - // Launch the plugin if it's not already running, or it's stale. Return an // error if it's fails to execute it. - llvm::Error spawnIfNeeded(); + llvm::Error spawnIfNeeded() override; /// Send a message to the plugin. - llvm::Error sendMessage(llvm::StringRef message) const; + llvm::Error sendMessage(llvm::StringRef message) override; /// Wait for a message from plugin and returns it. - llvm::Expected waitForNextMessage() const; - - bool isInitialized() const { return bool(cleanup); } - void setCleanup(std::function cleanup) { - this->cleanup = cleanup; - } - - /// Add "on reconnect" callback. - /// These callbacks are called when `spawnIfNeeded()` relaunched the plugin. - void addOnReconnect(std::function *fn) { - onReconnect.push_back(fn); - } - - /// Remove "on reconnect" callback. - void removeOnReconnect(std::function *fn) { - llvm::erase_value(onReconnect, fn); - } + llvm::Expected waitForNextMessage() override; llvm::sys::procid_t getPid() { return Process->process.Pid; } llvm::sys::process_t getProcess() { return Process->process.Process; } - - NullTerminatedStringRef getExecutablePath() { - return {ExecutablePath.c_str(), ExecutablePath.size()}; - } - - const void *getCapability() { return capability; }; - void setCapability(const void *newValue) { capability = newValue; }; - - void setDumpMessaging(bool flag) { dumpMessaging = flag; } }; class PluginRegistry { - /// Record of loaded plugin library modules. - llvm::StringMap> LoadedPluginLibraries; + /// The in-process plugin server. + std::unique_ptr inProcessPlugins; /// Record of loaded plugin executables. llvm::StringMap> @@ -179,14 +222,17 @@ class PluginRegistry { public: PluginRegistry(); - /// Load a dynamic link library specified by \p path. - /// If \p path plugin is already loaded, this returns the cached object. - llvm::Expected loadLibraryPlugin(llvm::StringRef path); + /// Get the in-process plugin server. + /// If it's loaded, returned the cached object. If the loaded instance is + /// from a different 'serverPath', returns an error as we don't support + /// multiple in-process plugin server in a host process. + llvm::Expected + getInProcessPlugins(llvm::StringRef serverPath); /// Load an executable plugin specified by \p path . /// If \p path plugin is already loaded, this returns the cached object. - llvm::Expected - loadExecutablePlugin(llvm::StringRef path, bool disableSandbox); + llvm::Expected loadExecutablePlugin(llvm::StringRef path, + bool disableSandbox); }; } // namespace swift diff --git a/include/swift/AST/SearchPathOptions.h b/include/swift/AST/SearchPathOptions.h index f58abedcd2e4b..5d21ca6f29217 100644 --- a/include/swift/AST/SearchPathOptions.h +++ b/include/swift/AST/SearchPathOptions.h @@ -469,6 +469,9 @@ class SearchPathOptions { /// Plugin search path options. std::vector PluginSearchOpts; + /// Path to in-process plugin server shared library. + std::string InProcessPluginServerPath; + /// Don't look in for compiler-provided modules. bool SkipRuntimeLibraryImportPaths = false; diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 8451855a4e724..7973d19a1f539 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -50,7 +50,7 @@ class ContinueStmt; class DefaultArgumentExpr; class DefaultArgumentType; class DoCatchStmt; -struct ExternalMacroDefinition; +class ExternalMacroDefinition; class ClosureExpr; class GenericParamList; class InverseTypeRepr; @@ -4541,38 +4541,30 @@ class ExpandSynthesizedMemberMacroRequest /// Represent a loaded plugin either an in-process library or an executable. class CompilerPluginLoadResult { - enum class PluginKind : uint8_t { - Error = 0, - Library, - Executable, + enum class Status : uint8_t { + Success = 0, + Error, }; - PluginKind Kind; + Status status; void *opaqueHandle; - CompilerPluginLoadResult(PluginKind K, void *opaque) - : Kind(K), opaqueHandle(opaque) {} + CompilerPluginLoadResult(Status status, void *opaque) + : status(status), opaqueHandle(opaque) {} public: - CompilerPluginLoadResult(LoadedLibraryPlugin *ptr) - : CompilerPluginLoadResult(PluginKind::Library, ptr){}; - CompilerPluginLoadResult(LoadedExecutablePlugin *ptr) - : CompilerPluginLoadResult(PluginKind::Executable, ptr){}; + CompilerPluginLoadResult(CompilerPlugin *ptr) + : CompilerPluginLoadResult(Status::Success, ptr){}; static CompilerPluginLoadResult error(NullTerminatedStringRef message) { - return CompilerPluginLoadResult(PluginKind::Error, + return CompilerPluginLoadResult(Status::Error, const_cast(message.data())); } - LoadedLibraryPlugin *getAsLibraryPlugin() const { - if (Kind != PluginKind::Library) + CompilerPlugin *get() const { + if (status != Status::Success) return nullptr; - return static_cast(opaqueHandle); + return static_cast(opaqueHandle); } - LoadedExecutablePlugin *getAsExecutablePlugin() const { - if (Kind != PluginKind::Executable) - return nullptr; - return static_cast(opaqueHandle); - } - bool isError() const { return Kind == PluginKind::Error; } + bool isError() const { return status == Status::Error; } NullTerminatedStringRef getErrorMessage() const { assert(isError()); return static_cast(opaqueHandle); diff --git a/include/swift/Bridging/ASTGen.h b/include/swift/Bridging/ASTGen.h index be0ee055aea64..14eaae3cd3ccd 100644 --- a/include/swift/Bridging/ASTGen.h +++ b/include/swift/Bridging/ASTGen.h @@ -58,15 +58,12 @@ void swift_ASTGen_buildTopLevelASTNodes( BridgedLegacyParser legacyParser, void *_Nonnull outputContext, void (*_Nonnull)(void *_Nonnull, void *_Nonnull)); -void *_Nullable swift_ASTGen_resolveMacroType(const void *_Nonnull macroType); -void swift_ASTGen_destroyMacro(void *_Nonnull macro); - void swift_ASTGen_freeBridgedString(BridgedStringRef); -void *_Nonnull swift_ASTGen_resolveExecutableMacro( +void *_Nonnull swift_ASTGen_resolveExternalMacro( const char *_Nonnull moduleName, const char *_Nonnull typeName, void *_Nonnull opaquePluginHandle); -void swift_ASTGen_destroyExecutableMacro(void *_Nonnull macro); +void swift_ASTGen_destroyExternalMacro(void *_Nonnull macro); bool swift_ASTGen_checkDefaultArgumentMacroExpression( void *_Nonnull diagEngine, void *_Nonnull sourceFile, @@ -84,13 +81,13 @@ void swift_ASTGen_freeExpansionReplacements( ptrdiff_t *_Nullable replacementsPtr, ptrdiff_t numReplacements); ptrdiff_t swift_ASTGen_expandFreestandingMacro( - void *_Nonnull diagEngine, const void *_Nonnull macro, uint8_t externalKind, + void *_Nonnull diagEngine, const void *_Nonnull macro, const char *_Nonnull discriminator, uint8_t rawMacroRole, void *_Nonnull sourceFile, const void *_Nullable sourceLocation, BridgedStringRef *_Nonnull evaluatedSourceOut); ptrdiff_t swift_ASTGen_expandAttachedMacro( - void *_Nonnull diagEngine, const void *_Nonnull macro, uint8_t externalKind, + void *_Nonnull diagEngine, const void *_Nonnull macro, const char *_Nonnull discriminator, const char *_Nonnull qualifiedType, const char *_Nonnull conformances, uint8_t rawMacroRole, void *_Nonnull customAttrSourceFile, diff --git a/include/swift/Driver/ToolChain.h b/include/swift/Driver/ToolChain.h index 16631c703b833..d438980d9a18e 100644 --- a/include/swift/Driver/ToolChain.h +++ b/include/swift/Driver/ToolChain.h @@ -253,6 +253,9 @@ class ToolChain { const InvocationInfo &invocationInfo, const JobContext &context) const; + void addPluginArguments(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &Arguments) const; + public: virtual ~ToolChain() = default; @@ -341,9 +344,6 @@ class ToolChain { llvm::opt::ArgStringList &Arguments, StringRef LibName) const; - virtual void addPluginArguments(const llvm::opt::ArgList &Args, - llvm::opt::ArgStringList &Arguments) const {} - /// Validates arguments passed to the toolchain. /// /// An override point for platform-specific subclasses to customize the diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index e8e97b06cb1fd..e60e228fe3e28 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -250,6 +250,12 @@ class CompilerInvocation { /// If we haven't explicitly passed -blocklist-paths, set it to the default value. void setDefaultBlocklistsIfNecessary(); + /// If we haven't explicitly passed '-in-process-plugin-server-path', infer + /// it as a default value. + /// + /// FIXME: Remove this after all the clients start sending it. + void setDefaultInProcessPluginServerPathIfNecessary(); + /// Computes the runtime resource path relative to the given Swift /// executable. static void computeRuntimeResourcePathFromExecutablePath( diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 17716a6a2438a..534fa86ac915c 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -2002,6 +2002,10 @@ def load_plugin_executable: "of module names where the macro types are declared">, MetaVarName<"#">; +def in_process_plugin_server_path : Separate<["-"], "in-process-plugin-server-path">, + Flags<[FrontendOption, ArgumentIsPath]>, + HelpText<"Path to dynamic library plugin server">; + def disable_sandbox: Flag<["-"], "disable-sandbox">, Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>, diff --git a/lib/AST/ASTBridging.cpp b/lib/AST/ASTBridging.cpp index 8b71b6c81d744..eec9c58568f75 100644 --- a/lib/AST/ASTBridging.cpp +++ b/lib/AST/ASTBridging.cpp @@ -2585,32 +2585,27 @@ void BridgedTypeRepr_dump(void *type) { static_cast(type)->dump(); } //===----------------------------------------------------------------------===// PluginCapabilityPtr Plugin_getCapability(PluginHandle handle) { - auto *plugin = static_cast(handle); + auto *plugin = static_cast(handle); return plugin->getCapability(); } void Plugin_setCapability(PluginHandle handle, PluginCapabilityPtr data) { - auto *plugin = static_cast(handle); + auto *plugin = static_cast(handle); plugin->setCapability(data); } -const char *Plugin_getExecutableFilePath(PluginHandle handle) { - auto *plugin = static_cast(handle); - return plugin->getExecutablePath().data(); -} - void Plugin_lock(PluginHandle handle) { - auto *plugin = static_cast(handle); + auto *plugin = static_cast(handle); plugin->lock(); } void Plugin_unlock(PluginHandle handle) { - auto *plugin = static_cast(handle); + auto *plugin = static_cast(handle); plugin->unlock(); } bool Plugin_spawnIfNeeded(PluginHandle handle) { - auto *plugin = static_cast(handle); + auto *plugin = static_cast(handle); auto error = plugin->spawnIfNeeded(); bool hadError(error); llvm::consumeError(std::move(error)); @@ -2618,7 +2613,7 @@ bool Plugin_spawnIfNeeded(PluginHandle handle) { } bool Plugin_sendMessage(PluginHandle handle, const BridgedData data) { - auto *plugin = static_cast(handle); + auto *plugin = static_cast(handle); StringRef message(data.BaseAddress, data.Length); auto error = plugin->sendMessage(message); if (error) { @@ -2634,7 +2629,7 @@ bool Plugin_sendMessage(PluginHandle handle, const BridgedData data) { } bool Plugin_waitForNextMessage(PluginHandle handle, BridgedData *out) { - auto *plugin = static_cast(handle); + auto *plugin = static_cast(handle); auto result = plugin->waitForNextMessage(); if (!result) { // FIXME: Pass the error message back to the caller. diff --git a/lib/AST/PluginLoader.cpp b/lib/AST/PluginLoader.cpp index 95f1b01ed24a8..18303c73c2373 100644 --- a/lib/AST/PluginLoader.cpp +++ b/lib/AST/PluginLoader.cpp @@ -170,31 +170,24 @@ PluginLoader::lookupPluginByModuleName(Identifier moduleName) { return found->second; } -llvm::Expected -PluginLoader::loadLibraryPlugin(StringRef path) { +llvm::Expected PluginLoader::getInProcessPlugins() { + auto inProcPluginServerPath = Ctx.SearchPathOpts.InProcessPluginServerPath; + if (inProcPluginServerPath.empty()) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "library plugins require -in-process-plugin-server-path"); + } + auto fs = getPluginLoadingFS(Ctx); SmallString<128> resolvedPath; - if (auto err = fs->getRealPath(path, resolvedPath)) { + if (auto err = fs->getRealPath(inProcPluginServerPath, resolvedPath)) { return llvm::createStringError(err, err.message()); } - // Load the plugin. - auto plugin = getRegistry()->loadLibraryPlugin(resolvedPath); - if (!plugin) { - resolvedPath.push_back(0); - return llvm::handleErrors( - plugin.takeError(), [&](const llvm::ErrorInfoBase &err) { - return llvm::createStringError( - err.convertToErrorCode(), - "compiler plugin '%s' could not be loaded; %s", - resolvedPath.data(), err.message().data()); - }); - } - - return plugin; + return getRegistry()->getInProcessPlugins(resolvedPath); } -llvm::Expected +llvm::Expected PluginLoader::loadExecutablePlugin(StringRef path) { auto fs = getPluginLoadingFS(Ctx); SmallString<128> resolvedPath; diff --git a/lib/AST/PluginRegistry.cpp b/lib/AST/PluginRegistry.cpp index 12a82ff9f36d9..60ba319e2c493 100644 --- a/lib/AST/PluginRegistry.cpp +++ b/lib/AST/PluginRegistry.cpp @@ -44,46 +44,96 @@ PluginRegistry::PluginRegistry() { dumpMessaging = ::getenv("SWIFT_DUMP_PLUGIN_MESSAGING") != nullptr; } -llvm::Expected -PluginRegistry::loadLibraryPlugin(StringRef path) { - std::lock_guard lock(mtx); - auto &storage = LoadedPluginLibraries[path]; - if (storage) { - // Already loaded. - return storage.get(); +CompilerPlugin::~CompilerPlugin() { + // Let ASTGen do cleanup things. + if (this->cleanup) + this->cleanup(); +} + +llvm::Expected> +InProcessPlugins::create(const char *serverPath) { + std::string err; + auto server = llvm::sys::DynamicLibrary::getLibrary(serverPath, &err); + if (!server.isValid()) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), err); } - void *lib = nullptr; -#if defined(_WIN32) - lib = LoadLibraryA(path.str().c_str()); - if (!lib) { - std::error_code ec(GetLastError(), std::system_category()); - return llvm::errorCodeToError(ec); + auto funcPtr = + server.getAddressOfSymbol("swift_inproc_plugins_handle_message"); + if (!funcPtr) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "entry point not found in '%s'", serverPath); } -#else - lib = dlopen(path.str().c_str(), RTLD_LAZY | RTLD_LOCAL); - if (!lib) { - return llvm::createStringError(llvm::inconvertibleErrorCode(), dlerror()); + return std::unique_ptr(new InProcessPlugins( + serverPath, server, reinterpret_cast(funcPtr))); +} + +llvm::Error InProcessPlugins::sendMessage(llvm::StringRef message) { + assert(receivedResponse.empty() && + "sendMessage() called before consuming previous response?"); + + if (shouldDumpMessaging()) { + llvm::dbgs() << "->(plugin:0) " << message << '\n'; } -#endif - storage = std::make_unique(lib, path); - return storage.get(); + char *responseData = nullptr; + size_t responseLength = 0; + bool hadError = handleMessageFn(message.data(), message.size(), &responseData, + &responseLength); + + // 'responseData' now holds a response message or error message depending on + // 'hadError'. Either way, it's our responsibility to deallocate it. + SWIFT_DEFER { free(responseData); }; + if (hadError) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + StringRef(responseData, responseLength)); + } + + // Store the response and wait for 'waitForNextMessage()' call. + receivedResponse = std::string(responseData, responseLength); + + if (shouldDumpMessaging()) { + llvm::dbgs() << "<-(plugin:0) " << receivedResponse << "\n"; + } + + assert(!receivedResponse.empty() && "received empty response"); + return llvm::Error::success(); } -void *LoadedLibraryPlugin::getAddressOfSymbol(const char *symbolName) { - auto &cached = resolvedSymbols[symbolName]; - if (cached) - return cached; -#if defined(_WIN32) - cached = GetProcAddress(static_cast(handle), symbolName); -#else - cached = dlsym(handle, symbolName); -#endif - return cached; +llvm::Expected InProcessPlugins::waitForNextMessage() { + assert(!receivedResponse.empty() && + "waitForNextMessage() called without response data."); + SWIFT_DEFER { receivedResponse = ""; }; + return std::move(receivedResponse); } -llvm::Expected +llvm::Expected +PluginRegistry::getInProcessPlugins(llvm::StringRef serverPath) { + std::lock_guard lock(mtx); + if (!inProcessPlugins) { + auto server = InProcessPlugins::create(serverPath.str().c_str()); + if (!server) { + return llvm::handleErrors( + server.takeError(), [&](const llvm::ErrorInfoBase &err) { + return llvm::createStringError( + err.convertToErrorCode(), + "failed to load in-process plugin server: " + serverPath + + "; " + err.message()); + }); + } + inProcessPlugins = std::move(server.get()); + } else if (inProcessPlugins->getPath() != serverPath) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "loading multiple in-process servers are not supported: '%s' and '%s'", + inProcessPlugins->getPath().data(), serverPath.str().c_str()); + } + inProcessPlugins->setDumpMessaging(dumpMessaging); + + return inProcessPlugins.get(); +} + +llvm::Expected PluginRegistry::loadExecutablePlugin(StringRef path, bool disableSandbox) { llvm::sys::fs::file_status stat; if (auto err = llvm::sys::fs::status(path, stat)) { @@ -143,7 +193,7 @@ llvm::Error LoadedExecutablePlugin::spawnIfNeeded() { } // Create command line arguments. - SmallVector command{ExecutablePath}; + SmallVector command{getPath()}; // Apply sandboxing. llvm::BumpPtrAllocator Allocator; @@ -161,7 +211,7 @@ llvm::Error LoadedExecutablePlugin::spawnIfNeeded() { childInfo->Read, childInfo->Write); // Call "on reconnect" callbacks. - for (auto *callback : onReconnect) { + for (auto *callback : getOnReconnectCallbacks()) { (*callback)(); } @@ -179,12 +229,6 @@ LoadedExecutablePlugin::PluginProcess::~PluginProcess() { llvm::sys::Wait(process, /*SecondsToWait=*/0); } -LoadedExecutablePlugin::~LoadedExecutablePlugin() { - // Let ASTGen to cleanup things. - if (this->cleanup) - this->cleanup(); -} - ssize_t LoadedExecutablePlugin::PluginProcess::read(void *buf, size_t nbyte) const { #if defined(_WIN32) @@ -260,10 +304,10 @@ ssize_t LoadedExecutablePlugin::PluginProcess::write(const void *buf, #endif } -llvm::Error LoadedExecutablePlugin::sendMessage(llvm::StringRef message) const { +llvm::Error LoadedExecutablePlugin::sendMessage(llvm::StringRef message) { ssize_t writtenSize = 0; - if (dumpMessaging) { + if (shouldDumpMessaging()) { llvm::dbgs() << "->(plugin:" << Process->process.Pid << ") " << message << '\n'; } @@ -291,7 +335,7 @@ llvm::Error LoadedExecutablePlugin::sendMessage(llvm::StringRef message) const { return llvm::Error::success(); } -llvm::Expected LoadedExecutablePlugin::waitForNextMessage() const { +llvm::Expected LoadedExecutablePlugin::waitForNextMessage() { ssize_t readSize = 0; // Read header (message size). @@ -323,7 +367,7 @@ llvm::Expected LoadedExecutablePlugin::waitForNextMessage() const { message.append(buffer, readSize); } - if (dumpMessaging) { + if (shouldDumpMessaging()) { llvm::dbgs() << "<-(plugin:" << Process->process.Pid << ") " << message << "\n"; } diff --git a/lib/ASTGen/CMakeLists.txt b/lib/ASTGen/CMakeLists.txt index d5c227369439c..6897f14d59a2e 100644 --- a/lib/ASTGen/CMakeLists.txt +++ b/lib/ASTGen/CMakeLists.txt @@ -11,7 +11,7 @@ if(SWIFT_BUILD_REGEX_PARSER_IN_COMPILER) endforeach() message(STATUS "Using Experimental String Processing library for _CompilerRegexParser (${SWIFT_PATH_TO_STRING_PROCESSING_SOURCE}).") - add_pure_swift_host_library(_CompilerRegexParser STATIC EMIT_MODULE + add_pure_swift_host_library(_CompilerRegexParser STATIC "${COMPILER_REGEX_PARSER_SOURCES}" ) @@ -36,7 +36,6 @@ add_pure_swift_host_library(swiftASTGen STATIC Sources/ASTGen/Regex.swift Sources/ASTGen/SourceFile.swift Sources/ASTGen/SourceManager.swift - Sources/ASTGen/SourceManager+MacroExpansionContext.swift Sources/ASTGen/Stmts.swift Sources/ASTGen/TypeAttrs.swift Sources/ASTGen/Types.swift @@ -44,16 +43,15 @@ add_pure_swift_host_library(swiftASTGen STATIC DEPENDENCIES swiftAST SWIFT_DEPENDENCIES - SwiftBasicFormat - SwiftCompilerPluginMessageHandling - SwiftDiagnostics - SwiftOperators - SwiftParser - SwiftParserDiagnostics - SwiftSyntax - SwiftSyntaxBuilder - SwiftSyntaxMacros - SwiftSyntaxMacroExpansion + _CompilerSwiftSyntax + _CompilerSwiftOperators + _CompilerSwiftSyntaxBuilder + _CompilerSwiftParser + _CompilerSwiftParserDiagnostics + _CompilerSwiftCompilerPluginMessageHandling + _CompilerSwiftSyntaxMacroExpansion + _CompilerSwiftDiagnostics + _CompilerSwiftIDEUtils ${ASTGen_Swift_dependencies} ) @@ -62,14 +60,11 @@ add_pure_swift_host_library(swiftIDEUtilsBridging DEPENDENCIES swiftAST - swiftASTGen - SWIFT_DEPENDENCIES - SwiftIDEUtils - SwiftSyntax + _CompilerSwiftSyntax + swiftASTGen ) - set(c_include_paths # LLVM modules and headers. "${LLVM_MAIN_INCLUDE_DIR}" @@ -88,7 +83,6 @@ endforeach() set(compile_options ${c_include_paths_args} - "SHELL: -DRESILIENT_SWIFT_SYNTAX" "SHELL: -Xcc -std=c++17 -Xcc -DCOMPILED_WITH_SWIFT" # FIXME: Needed to work around an availability issue with CxxStdlib diff --git a/lib/ASTGen/Package.swift b/lib/ASTGen/Package.swift index 5c28d6dbb7c3a..ac5120f307ef7 100644 --- a/lib/ASTGen/Package.swift +++ b/lib/ASTGen/Package.swift @@ -52,15 +52,13 @@ let package = Package( .target( name: "swiftASTGen", dependencies: [ - .product(name: "SwiftBasicFormat", package: "swift-syntax"), - .product(name: "SwiftCompilerPluginMessageHandling", package: "swift-syntax"), + .product(name: "_SwiftCompilerPluginMessageHandling", package: "swift-syntax"), .product(name: "SwiftDiagnostics", package: "swift-syntax"), .product(name: "SwiftOperators", package: "swift-syntax"), .product(name: "SwiftParser", package: "swift-syntax"), .product(name: "SwiftParserDiagnostics", package: "swift-syntax"), .product(name: "SwiftSyntax", package: "swift-syntax"), .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"), - .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), .product(name: "SwiftSyntaxMacroExpansion", package: "swift-syntax"), "_CompilerRegexParser", ], diff --git a/lib/ASTGen/Sources/ASTGen/Macros.swift b/lib/ASTGen/Sources/ASTGen/Macros.swift index 87163884ea1b7..5831b23dd89ac 100644 --- a/lib/ASTGen/Sources/ASTGen/Macros.swift +++ b/lib/ASTGen/Sources/ASTGen/Macros.swift @@ -19,25 +19,13 @@ import SwiftParser import SwiftSyntax import SwiftSyntaxBuilder @_spi(ExperimentalLanguageFeature) @_spi(Compiler) import SwiftSyntaxMacroExpansion -@_spi(ExperimentalLanguageFeature) import SwiftSyntaxMacros -/// Describes a macro that has been "exported" to the C++ part of the -/// compiler, with enough information to interface with the C++ layer. -struct ExportedMacro { - var macro: Macro.Type -} - -struct ExportedExecutableMacro { +struct ExportedExternalMacro { var moduleName: String var typeName: String var plugin: CompilerPlugin } -enum MacroPluginKind: UInt8 { - case InProcess = 0 - case Executable = 1 -} - extension MacroRole { init(rawMacroRole: UInt8) { switch rawMacroRole { @@ -58,40 +46,8 @@ extension MacroRole { } } -/// Resolve a reference to type metadata into a macro, if posible. -/// -/// Returns an unmanaged pointer to an ExportedMacro instance that describes -/// the specified macro. If there is no macro with the given name, produces -/// nil. -@_cdecl("swift_ASTGen_resolveMacroType") -public func resolveMacroType( - macroTypePtr: UnsafePointer -) -> UnsafeRawPointer? { - let macroType = unsafeBitCast(macroTypePtr, to: Any.Type.self) - - guard let macro = macroType as? Macro.Type else { - return nil - } - - // Allocate and initialize the exported macro. - let exportedPtr = UnsafeMutablePointer.allocate(capacity: 1) - exportedPtr.initialize(to: .init(macro: macro)) - return UnsafeRawPointer(exportedPtr) -} - -/// Destroys the given macro. -@_cdecl("swift_ASTGen_destroyMacro") -public func destroyMacro( - macroPtr: UnsafeMutablePointer -) { - macroPtr.withMemoryRebound(to: ExportedMacro.self, capacity: 1) { macro in - macro.deinitialize(count: 1) - macro.deallocate() - } -} - -@_cdecl("swift_ASTGen_resolveExecutableMacro") -public func resolveExecutableMacro( +@_cdecl("swift_ASTGen_resolveExternalMacro") +public func resolveExternalMacro( moduleName: UnsafePointer, typeName: UnsafePointer, pluginOpaqueHandle: UnsafeMutableRawPointer @@ -99,7 +55,7 @@ public func resolveExecutableMacro( // NOTE: This doesn't actually resolve anything. // Executable plugins is "trusted" to have the macro implementation. If not, // the actual expansion fails. - let exportedPtr = UnsafeMutablePointer.allocate(capacity: 1) + let exportedPtr = UnsafeMutablePointer.allocate(capacity: 1) exportedPtr.initialize( to: .init( moduleName: String(cString: moduleName), @@ -110,11 +66,11 @@ public func resolveExecutableMacro( return UnsafeRawPointer(exportedPtr) } -@_cdecl("swift_ASTGen_destroyExecutableMacro") -public func destroyExecutableMacro( +@_cdecl("swift_ASTGen_destroyExternalMacro") +public func destroyExternalMacro( macroPtr: UnsafeMutableRawPointer ) { - let macroPtr = macroPtr.assumingMemoryBound(to: ExportedExecutableMacro.self) + let macroPtr = macroPtr.assumingMemoryBound(to: ExportedExternalMacro.self) macroPtr.deinitialize(count: 1) macroPtr.deallocate() } @@ -470,7 +426,6 @@ func makeExpansionOutputResult( func expandFreestandingMacro( diagEnginePtr: UnsafeMutableRawPointer, macroPtr: UnsafeRawPointer, - macroKind: UInt8, discriminatorText: UnsafePointer, rawMacroRole: UInt8, sourceFilePtr: UnsafeRawPointer, @@ -508,27 +463,14 @@ func expandFreestandingMacro( let discriminator = String(cString: discriminatorText) let macroRole = MacroRole(rawMacroRole: rawMacroRole) - let expandedSource: String? - switch MacroPluginKind(rawValue: macroKind)! { - case .InProcess: - expandedSource = expandFreestandingMacroInProcess( - macroPtr: macroPtr, - macroRole: macroRole, - diagEnginePtr: diagEnginePtr, - expansionSyntax: expansion, - sourceFilePtr: sourceFilePtr, - discriminator: discriminator - ) - case .Executable: - expandedSource = expandFreestandingMacroIPC( - macroPtr: macroPtr, - macroRole: macroRole, - diagEnginePtr: diagEnginePtr, - expansionSyntax: expansion, - sourceFilePtr: sourceFilePtr, - discriminator: discriminator - ) - } + let expandedSource: String? = expandFreestandingMacroImpl( + macroPtr: macroPtr, + macroRole: macroRole, + diagEnginePtr: diagEnginePtr, + expansionSyntax: expansion, + sourceFilePtr: sourceFilePtr, + discriminator: discriminator + ) return makeExpansionOutputResult( expandedSource: expandedSource, @@ -536,7 +478,7 @@ func expandFreestandingMacro( ) } -func expandFreestandingMacroIPC( +func expandFreestandingMacroImpl( macroPtr: UnsafeRawPointer, macroRole: MacroRole, diagEnginePtr: UnsafeMutableRawPointer, @@ -554,7 +496,7 @@ func expandFreestandingMacroIPC( fatalError("unknown syntax") } - let macro = macroPtr.assumingMemoryBound(to: ExportedExecutableMacro.self).pointee + let macro = macroPtr.assumingMemoryBound(to: ExportedExternalMacro.self).pointee // Map the macro role. let pluginMacroRole: PluginMessage.MacroRole @@ -615,55 +557,6 @@ func expandFreestandingMacroIPC( } } -func expandFreestandingMacroInProcess( - macroPtr: UnsafeRawPointer, - macroRole: MacroRole, - diagEnginePtr: UnsafeMutableRawPointer, - expansionSyntax: FreestandingMacroExpansionSyntax, - sourceFilePtr: UnsafePointer, - discriminator: String -) -> String? { - // Get the macro. - let macroPtr = macroPtr.bindMemory(to: ExportedMacro.self, capacity: 1) - let macro = macroPtr.pointee.macro - - // Create a source manager. This should probably persist and be given to us. - let sourceManager = SourceManager(cxxDiagnosticEngine: diagEnginePtr) - sourceManager.insert(sourceFilePtr) - - let context = sourceManager.createMacroExpansionContext( - lexicalContext: lexicalContext(of: expansionSyntax), - discriminator: discriminator - ) - - let macroName = expansionSyntax.macroName.text - - // Make sure we emit all of the diagnostics from the context. - defer { - // Emit diagnostics accumulated in the context. - for diag in context.diagnostics { - sourceManager.diagnose( - diagnostic: diag, - messageSuffix: " (from macro '\(macroName)')" - ) - } - - context.diagnostics = [] - } - - let node = sourceManager.detach( - expansionSyntax, - foldingWith: OperatorTable.standardOperators - ) - - return SwiftSyntaxMacroExpansion.expandFreestandingMacro( - definition: macro, - macroRole: macroRole, - node: node, - in: context - ) -} - /// Retrieve a syntax node in the given source file, with the given type. func findSyntaxNodeInSourceFile( sourceFilePtr: UnsafeRawPointer, @@ -730,7 +623,6 @@ func findSyntaxNodeInSourceFile( func expandAttachedMacro( diagEnginePtr: UnsafeMutableRawPointer, macroPtr: UnsafeRawPointer, - macroKind: UInt8, discriminatorText: UnsafePointer, qualifiedTypeText: UnsafePointer, conformanceListText: UnsafePointer, @@ -786,39 +678,20 @@ func expandAttachedMacro( let qualifiedType = String(cString: qualifiedTypeText) let conformanceList = String(cString: conformanceListText) - let expandedSource: String? - switch MacroPluginKind(rawValue: macroKind)! { - case .Executable: - expandedSource = expandAttachedMacroIPC( - diagEnginePtr: diagEnginePtr, - macroPtr: macroPtr, - rawMacroRole: rawMacroRole, - discriminator: discriminator, - qualifiedType: qualifiedType, - conformanceList: conformanceList, - customAttrSourceFilePtr: customAttrSourceFilePtr, - customAttrNode: customAttrNode, - declarationSourceFilePtr: declarationSourceFilePtr, - attachedTo: declarationNode, - parentDeclSourceFilePtr: parentDeclSourceFilePtr, - parentDeclNode: parentDeclNode - ) - case .InProcess: - expandedSource = expandAttachedMacroInProcess( - diagEnginePtr: diagEnginePtr, - macroPtr: macroPtr, - rawMacroRole: rawMacroRole, - discriminator: discriminator, - qualifiedType: qualifiedType, - conformanceList: conformanceList, - customAttrSourceFilePtr: customAttrSourceFilePtr, - customAttrNode: customAttrNode, - declarationSourceFilePtr: declarationSourceFilePtr, - attachedTo: declarationNode, - parentDeclSourceFilePtr: parentDeclSourceFilePtr, - parentDeclNode: parentDeclNode - ) - } + let expandedSource: String? = expandAttachedMacroImpl( + diagEnginePtr: diagEnginePtr, + macroPtr: macroPtr, + rawMacroRole: rawMacroRole, + discriminator: discriminator, + qualifiedType: qualifiedType, + conformanceList: conformanceList, + customAttrSourceFilePtr: customAttrSourceFilePtr, + customAttrNode: customAttrNode, + declarationSourceFilePtr: declarationSourceFilePtr, + attachedTo: declarationNode, + parentDeclSourceFilePtr: parentDeclSourceFilePtr, + parentDeclNode: parentDeclNode + ) return makeExpansionOutputResult( expandedSource: expandedSource, @@ -840,7 +713,7 @@ private func pluginLexicalContext(of node: some SyntaxProtocol) -> [PluginMessag lexicalContext(of: node).compactMap { .init(syntax: $0) } } -func expandAttachedMacroIPC( +func expandAttachedMacroImpl( diagEnginePtr: UnsafeMutableRawPointer, macroPtr: UnsafeRawPointer, rawMacroRole: UInt8, @@ -855,7 +728,7 @@ func expandAttachedMacroIPC( parentDeclNode: DeclSyntax? ) -> String? { let macroName: String = customAttrNode.attributeName.description - let macro = macroPtr.assumingMemoryBound(to: ExportedExecutableMacro.self).pointee + let macro = macroPtr.assumingMemoryBound(to: ExportedExternalMacro.self).pointee // Map the macro role. let macroRole: PluginMessage.MacroRole @@ -988,104 +861,3 @@ func expandAttachedMacroIPC( } } -func expandAttachedMacroInProcess( - diagEnginePtr: UnsafeMutableRawPointer, - macroPtr: UnsafeRawPointer, - rawMacroRole: UInt8, - discriminator: String, - qualifiedType: String, - conformanceList: String, - customAttrSourceFilePtr: UnsafePointer, - customAttrNode: AttributeSyntax, - declarationSourceFilePtr: UnsafePointer, - attachedTo declarationNode: DeclSyntax, - parentDeclSourceFilePtr: UnsafePointer?, - parentDeclNode: DeclSyntax? -) -> String? { - // Get the macro. - let macroPtr = macroPtr.bindMemory(to: ExportedMacro.self, capacity: 1) - let macro = macroPtr.pointee.macro - - // Create a source manager covering the files we know about. - let sourceManager = SourceManager(cxxDiagnosticEngine: diagEnginePtr) - sourceManager.insert(customAttrSourceFilePtr) - sourceManager.insert(declarationSourceFilePtr) - if let parentDeclSourceFilePtr = parentDeclSourceFilePtr { - sourceManager.insert(parentDeclSourceFilePtr) - } - - // Create an expansion context - let context = sourceManager.createMacroExpansionContext( - lexicalContext: lexicalContext(of: declarationNode), - discriminator: discriminator - ) - - let macroName = customAttrNode.attributeName.trimmedDescription - - // Emit all of the accumulated diagnostics before we exit. - defer { - // Emit diagnostics accumulated in the context. - for diag in context.diagnostics { - sourceManager.diagnose( - diagnostic: diag, - messageSuffix: " (from macro '\(macroName)')" - ) - } - - context.diagnostics = [] - } - - let attributeNode = sourceManager.detach( - customAttrNode, - foldingWith: OperatorTable.standardOperators - ) - let declarationNode = sourceManager.detach(declarationNode) - let parentDeclNode = parentDeclNode.map { sourceManager.detach($0) } - let extendedType: TypeSyntax = "\(raw: qualifiedType)" - - let conformanceListSyntax: InheritedTypeListSyntax? - if (conformanceList.isEmpty) { - conformanceListSyntax = nil - } else { - let placeholderDecl: DeclSyntax = - """ - struct Placeholder: \(raw: conformanceList) {} - """ - let placeholderStruct = placeholderDecl.cast(StructDeclSyntax.self) - if let inheritanceClause = placeholderStruct.inheritanceClause { - conformanceListSyntax = inheritanceClause.inheritedTypes - } else { - conformanceListSyntax = nil - } - } - - return SwiftSyntaxMacroExpansion.expandAttachedMacro( - definition: macro, - macroRole: MacroRole(rawMacroRole: rawMacroRole), - attributeNode: attributeNode, - declarationNode: declarationNode, - parentDeclNode: parentDeclNode, - extendedType: extendedType, - conformanceList: conformanceListSyntax, - in: context - ) -} - -fileprivate extension SyntaxProtocol { - /// Perform a format if required and then trim any leading/trailing - /// whitespace. - func formattedExpansion(_ mode: FormatMode) -> String { - let formatted: Syntax - switch mode { - case .auto: - formatted = self.formatted() - case .disabled: - formatted = Syntax(self) -#if RESILIENT_SWIFT_SYNTAX - @unknown default: - fatalError() -#endif - } - return formatted.trimmedDescription(matching: { $0.isWhitespace }) - } -} diff --git a/lib/ASTGen/Sources/ASTGen/PluginHost.swift b/lib/ASTGen/Sources/ASTGen/PluginHost.swift index 2319d5146a703..0e522b54d961d 100644 --- a/lib/ASTGen/Sources/ASTGen/PluginHost.swift +++ b/lib/ASTGen/Sources/ASTGen/PluginHost.swift @@ -194,10 +194,6 @@ struct CompilerPlugin { } return nil } - - var executableFilePath: String { - return String(cString: Plugin_getExecutableFilePath(opaqueHandle)) - } } class PluginDiagnosticsEngine { @@ -319,7 +315,7 @@ class PluginDiagnosticsEngine { messageSuffix: String? = nil ) { for diagnostic in diagnostics { - self.emit(diagnostic) + self.emit(diagnostic, messageSuffix: messageSuffix) } } @@ -378,6 +374,26 @@ class PluginDiagnosticsEngine { } } +extension String { + /// Retrieve the base name of a string that represents a path, removing the + /// directory. + var basename: String { + guard + let lastSlash = lastIndex(where: { + #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) || os(Android) || os(Linux) + ["/"].contains($0) + #else + ["/", "\\"].contains($0) + #endif + }) + else { + return self + } + + return String(self[index(after: lastSlash)...]) + } +} + extension PluginMessage.Syntax { init?(syntax: Syntax, in sourceFilePtr: UnsafePointer) { let kind: PluginMessage.Syntax.Kind diff --git a/lib/ASTGen/Sources/ASTGen/SourceManager+MacroExpansionContext.swift b/lib/ASTGen/Sources/ASTGen/SourceManager+MacroExpansionContext.swift deleted file mode 100644 index 04953e284805a..0000000000000 --- a/lib/ASTGen/Sources/ASTGen/SourceManager+MacroExpansionContext.swift +++ /dev/null @@ -1,192 +0,0 @@ -//===--- SourceManager+MacroExpansionContext.swift ------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2022-2023 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 -// -//===----------------------------------------------------------------------===// - -import SwiftDiagnostics -import SwiftSyntax -import SwiftSyntaxMacros - -extension SourceManager { - class MacroExpansionContext { - /// The source manager. - private let sourceManager: SourceManager - - /// The lexical context for this expansion. - let lexicalContext: [Syntax] - - /// The set of diagnostics that were emitted as part of expanding the - /// macro. - var diagnostics: [Diagnostic] = [] - - /// The macro expansion discriminator, which is used to form unique names - /// when requested. - /// - /// The expansion discriminator is combined with the `uniqueNames` counters - /// to produce unique names. - private var discriminator: String - - /// Counter for each of the uniqued names. - /// - /// Used in conjunction with `expansionDiscriminator`. - private var uniqueNames: [String: Int] = [:] - - init( - sourceManager: SourceManager, - lexicalContext: [Syntax], - discriminator: String - ) { - self.sourceManager = sourceManager - self.lexicalContext = lexicalContext - self.discriminator = discriminator - } - } - - /// Create a new macro expansion context - func createMacroExpansionContext( - lexicalContext: [Syntax], - discriminator: String = "" - ) -> MacroExpansionContext { - return MacroExpansionContext( - sourceManager: self, - lexicalContext: lexicalContext, - discriminator: discriminator - ) - } -} - -extension String { - /// Retrieve the base name of a string that represents a path, removing the - /// directory. - var basename: String { - guard - let lastSlash = lastIndex(where: { - #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(Android) || os(Linux) - ["/"].contains($0) - #else - ["/", "\\"].contains($0) - #endif - }) - else { - return self - } - - return String(self[index(after: lastSlash)...]) - } -} - -extension SourceManager.MacroExpansionContext: MacroExpansionContext { - /// Generate a unique name for use in the macro. - public func makeUniqueName(_ providedName: String) -> TokenSyntax { - // If provided with an empty name, substitute in something. - let name = providedName.isEmpty ? "__local" : providedName - - // Grab a unique index value for this name. - let uniqueIndex = uniqueNames[name, default: 0] - uniqueNames[name] = uniqueIndex + 1 - - // Start with the discriminator. - var resultString = discriminator - - // Mangle the name - resultString += "\(name.count)\(name)" - - // Mangle the operator for unique macro names. - resultString += "fMu" - - // Mangle the index. - if uniqueIndex > 0 { - resultString += "\(uniqueIndex - 1)" - } - resultString += "_" - - return TokenSyntax(.identifier(resultString), presence: .present) - } - - /// Produce a diagnostic while expanding the macro. - public func diagnose(_ diagnostic: Diagnostic) { - diagnostics.append(diagnostic) - } - - public func location( - of node: Node, - at position: PositionInSyntaxNode, - filePathMode: SourceLocationFilePathMode - ) -> AbstractSourceLocation? { - guard let (sourceFile, rootPosition) = sourceManager.rootSourceFile(of: node), - let exportedSourceFile = - sourceManager.exportedSourceFilesBySyntax[sourceFile]?.pointee - else { - return nil - } - - // Find the node's offset relative to its root. - let rawPosition: AbsolutePosition - switch position { - case .beforeLeadingTrivia: - rawPosition = node.position - - case .afterLeadingTrivia: - rawPosition = node.positionAfterSkippingLeadingTrivia - - case .beforeTrailingTrivia: - rawPosition = node.endPositionBeforeTrailingTrivia - - case .afterTrailingTrivia: - rawPosition = node.endPosition - -#if RESILIENT_SWIFT_SYNTAX - @unknown default: - fatalError() -#endif - } - - let offsetWithinSyntaxNode = - rawPosition.utf8Offset - node.position.utf8Offset - - var location = exportedSourceFile.sourceLocationConverter.location( - for: rootPosition.advanced(by: offsetWithinSyntaxNode) - ) - - switch filePathMode { - case .fileID: - // The `SourceLocationConverter` in `exportedSourceFile` uses `filePath` as the file mode. When the `fileID` mode - // is requested, we need to adjust the file and presumed file to the `fileID`. - let fileID = "\(exportedSourceFile.moduleName)/\(exportedSourceFile.fileName.basename)" - var adjustedFile = location.file - if adjustedFile == exportedSourceFile.fileName { - adjustedFile = fileID - } - var adjustedPresumedFile = location.presumedFile - if adjustedPresumedFile == exportedSourceFile.fileName { - adjustedPresumedFile = fileID - } - location = SourceLocation( - line: location.line, - column: location.column, - offset: location.offset, - file: adjustedFile, - presumedLine: location.presumedLine, - presumedFile: adjustedPresumedFile - ) - - case .filePath: - break - -#if RESILIENT_SWIFT_SYNTAX - @unknown default: - fatalError() -#endif - } - - // Do the location lookup. - return AbstractSourceLocation(location) - } -} diff --git a/lib/ASTGen/Sources/ASTGen/SourceManager.swift b/lib/ASTGen/Sources/ASTGen/SourceManager.swift index e1a13b7ddad07..2b8e2a1db62f5 100644 --- a/lib/ASTGen/Sources/ASTGen/SourceManager.swift +++ b/lib/ASTGen/Sources/ASTGen/SourceManager.swift @@ -14,7 +14,6 @@ import ASTBridging import BasicBridging import SwiftOperators import SwiftSyntax -import SwiftSyntaxMacros /// A source manager that keeps track of the source files in the program. class SourceManager { diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index dc942e5a54982..81d2b3fa03c04 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -40,6 +40,7 @@ add_subdirectory(AST) add_subdirectory(ASTGen) add_subdirectory(ASTSectionImporter) add_subdirectory(Basic) +add_subdirectory(CompilerSwiftSyntax) add_subdirectory(ConstExtract) add_subdirectory(ClangImporter) add_subdirectory(Demangling) diff --git a/lib/CompilerSwiftSyntax/CMakeLists.txt b/lib/CompilerSwiftSyntax/CMakeLists.txt new file mode 100644 index 0000000000000..79c1605d99324 --- /dev/null +++ b/lib/CompilerSwiftSyntax/CMakeLists.txt @@ -0,0 +1,52 @@ +if(NOT SWIFT_BUILD_SWIFT_SYNTAX) + return() +endif() +if(NOT EXISTS "${SWIFT_PATH_TO_SWIFT_SYNTAX_SOURCE}") + message(SEND_ERROR "swift-syntax is required to build the Swift compiler. Please run update-checkout or specify SWIFT_PATH_TO_SWIFT_SYNTAX_SOURCE") + return() +endif() + +# Build swift-syntax libraries with FetchContent. +function(includeSwiftSyntax) + set(CMAKE_Swift_COMPILER_TARGET ${SWIFT_HOST_TRIPLE}) + set(BUILD_SHARED_LIBS ON) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${SWIFT_HOST_LIBRARIES_DEST_DIR}/compiler") + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${SWIFT_HOST_LIBRARIES_DEST_DIR}/compiler") + if(SWIFT_HOST_VARIANT_SDK MATCHES "LINUX|ANDROID|OPENBSD|FREEBSD") + set(SWIFT_HOST_LIBRARIES_RPATH "$ORIGIN;$ORIGIN/../../${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_LIB_SUBDIR}") + endif() + + # Add unique ABI prefix to swift-syntax libraries so that compiler libraries (e.g. sourcekitdInProc) + # can be used from tools that has its own swift-syntax libraries as SwiftPM dependencies. + set(SWIFT_MODULE_ABI_NAME_PREFIX "_Compiler") + set(SWIFTSYNTAX_TARGET_NAMESPACE "_Compiler") + set(SWIFTSYNTAX_EMIT_MODULE OFF) + + file(TO_CMAKE_PATH "${SWIFT_PATH_TO_SWIFT_SYNTAX_SOURCE}" swift_syntax_path) + FetchContent_Declare(CompilerSwiftSyntax SOURCE_DIR "${swift_syntax_path}") + FetchContent_MakeAvailable(CompilerSwiftSyntax) +endfunction() +includeSwiftSyntax() + +set(compiler_swiftsyntax_libs + _CompilerSwiftSyntax + _CompilerSwiftOperators + _CompilerSwiftSyntaxBuilder + _CompilerSwiftParser + _CompilerSwiftParserDiagnostics + _CompilerSwiftCompilerPluginMessageHandling + _CompilerSwiftSyntaxMacroExpansion + _CompilerSwiftSyntaxMacros + _CompilerSwiftBasicFormat + _CompilerSwiftDiagnostics + _CompilerSwiftIDEUtils +) + +foreach(lib ${compiler_swiftsyntax_libs}) + target_compile_options(${lib} PRIVATE "SHELL:-module-link-name ${lib}") +endforeach() + +swift_install_in_component(TARGETS ${compiler_swiftsyntax_libs} + ARCHIVE DESTINATION "lib${LLVM_LIBDIR_SUFFIX}/swift/host/compiler" COMPONENT compiler + LIBRARY DESTINATION "lib${LLVM_LIBDIR_SUFFIX}/swift/host/compiler" COMPONENT compiler + RUNTIME DESTINATION "bin" COMPONENT compiler) diff --git a/lib/Driver/DarwinToolChains.cpp b/lib/Driver/DarwinToolChains.cpp index c7abfb36113b8..f7ee21dfe8f5f 100644 --- a/lib/Driver/DarwinToolChains.cpp +++ b/lib/Driver/DarwinToolChains.cpp @@ -128,31 +128,6 @@ std::string toolchains::Darwin::sanitizerRuntimeLibName(StringRef Sanitizer, .str(); } -void -toolchains::Darwin::addPluginArguments(const ArgList &Args, - ArgStringList &Arguments) const { - SmallString<64> pluginPath; - auto programPath = getDriver().getSwiftProgramPath(); - CompilerInvocation::computeRuntimeResourcePathFromExecutablePath( - programPath, /*shared=*/true, pluginPath); - - auto defaultPluginPath = pluginPath; - llvm::sys::path::append(defaultPluginPath, "host", "plugins"); - - // Default plugin path. - Arguments.push_back("-plugin-path"); - Arguments.push_back(Args.MakeArgString(defaultPluginPath)); - - // Local plugin path. - llvm::sys::path::remove_filename(pluginPath); // Remove "swift" - llvm::sys::path::remove_filename(pluginPath); // Remove "lib" - llvm::sys::path::append(pluginPath, "local", "lib"); - llvm::sys::path::append(pluginPath, "swift"); - llvm::sys::path::append(pluginPath, "host", "plugins"); - Arguments.push_back("-plugin-path"); - Arguments.push_back(Args.MakeArgString(pluginPath)); -} - static void addLinkRuntimeLibRPath(const ArgList &Args, ArgStringList &Arguments, StringRef DarwinLibName, diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index a945f376fa32d..cddc58b3056af 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -1466,6 +1466,68 @@ void ToolChain::addLinkRuntimeLib(const ArgList &Args, ArgStringList &Arguments, Arguments.push_back(Args.MakeArgString(P)); } +static void appendInProcPluginServerPath(StringRef PluginPathRoot, + llvm::SmallVectorImpl &InProcPluginServerPath) { + InProcPluginServerPath.append(PluginPathRoot.begin(), PluginPathRoot.end()); +#if defined(_WIN32) + llvm::sys::path::append(InProcPluginServerPath, "bin", "SwiftInProcPluginServer.dll"); +#elif defined(__APPLE__) + llvm::sys::path::append(InProcPluginServerPath, "lib", "swift", "host", "libSwiftInProcPluginServer.dylib"); +#elif defined(__unix__) + llvm::sys::path::append(InProcPluginServerPath, "lib", "swift", "host", "libSwiftInProcPluginServer.so"); +#else +#error Unknown compiler host +#endif +} + +static void appendPluginsPath(StringRef PluginPathRoot, + llvm::SmallVectorImpl &PluginsPath) { + PluginsPath.append(PluginPathRoot.begin(), PluginPathRoot.end()); +#if defined(_WIN32) + llvm::sys::path::append(PluginsPath, "bin"); +#elif defined(__APPLE__) || defined(__unix__) + llvm::sys::path::append(PluginsPath, "lib", "swift", "host", "plugins"); +#else +#error Unknown compiler host +#endif +} + +#if defined(__APPLE__) || defined(__unix__) +static void appendLocalPluginsPath(StringRef PluginPathRoot, + llvm::SmallVectorImpl &LocalPluginsPath) { + SmallString<261> localPluginPathRoot = PluginPathRoot; + llvm::sys::path::append(localPluginPathRoot, "local"); + appendPluginsPath(localPluginPathRoot, LocalPluginsPath); +} +#endif + +void ToolChain::addPluginArguments(const ArgList &Args, + ArgStringList &Arguments) const { + SmallString<261> pluginPathRoot = StringRef(getDriver().getSwiftProgramPath()); + llvm::sys::path::remove_filename(pluginPathRoot); // Remove `swift` + llvm::sys::path::remove_filename(pluginPathRoot); // Remove `bin` + + // In-process plugin server path. + SmallString<261> inProcPluginServerPath; + appendInProcPluginServerPath(pluginPathRoot, inProcPluginServerPath); + Arguments.push_back("-in-process-plugin-server-path"); + Arguments.push_back(Args.MakeArgString(inProcPluginServerPath)); + + // Default plugin path. + SmallString<261> defaultPluginPath; + appendPluginsPath(pluginPathRoot, defaultPluginPath); + Arguments.push_back("-plugin-path"); + Arguments.push_back(Args.MakeArgString(defaultPluginPath)); + + // Local plugin path. +#if defined(__APPLE__) || defined(__unix__) + SmallString<261> localPluginPath; + appendLocalPluginsPath(pluginPathRoot, localPluginPath); + Arguments.push_back("-plugin-path"); + Arguments.push_back(Args.MakeArgString(localPluginPath)); +#endif +} + void ToolChain::getClangLibraryPath(const ArgList &Args, SmallString<128> &LibPath) const { const llvm::Triple &T = getTriple(); diff --git a/lib/Driver/ToolChains.h b/lib/Driver/ToolChains.h index 59b755e9f0d34..52adec85b49de 100644 --- a/lib/Driver/ToolChains.h +++ b/lib/Driver/ToolChains.h @@ -66,9 +66,6 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public ToolChain { InvocationInfo constructInvocation(const StaticLinkJobAction &job, const JobContext &context) const override; - void addPluginArguments(const llvm::opt::ArgList &Args, - llvm::opt::ArgStringList &Arguments) const override; - void validateArguments(DiagnosticEngine &diags, const llvm::opt::ArgList &args, StringRef defaultTarget) const override; @@ -117,9 +114,6 @@ class LLVM_LIBRARY_VISIBILITY Windows : public ToolChain { std::string sanitizerRuntimeLibName(StringRef Sanitizer, bool shared = true) const override; - - void addPluginArguments(const llvm::opt::ArgList &Args, - llvm::opt::ArgStringList &Arguments) const override; }; class LLVM_LIBRARY_VISIBILITY WebAssembly : public ToolChain { @@ -173,9 +167,6 @@ class LLVM_LIBRARY_VISIBILITY GenericUnix : public ToolChain { ~GenericUnix() = default; std::string sanitizerRuntimeLibName(StringRef Sanitizer, bool shared = true) const override; - - void addPluginArguments(const llvm::opt::ArgList &Args, - llvm::opt::ArgStringList &Arguments) const override; }; class LLVM_LIBRARY_VISIBILITY Android : public GenericUnix { diff --git a/lib/Driver/UnixToolChains.cpp b/lib/Driver/UnixToolChains.cpp index 42f5e3e7ddfa8..e5ab5de94301b 100644 --- a/lib/Driver/UnixToolChains.cpp +++ b/lib/Driver/UnixToolChains.cpp @@ -47,31 +47,6 @@ toolchains::GenericUnix::sanitizerRuntimeLibName(StringRef Sanitizer, .str(); } -void -toolchains::GenericUnix::addPluginArguments(const ArgList &Args, - ArgStringList &Arguments) const { - SmallString<64> pluginPath; - auto programPath = getDriver().getSwiftProgramPath(); - CompilerInvocation::computeRuntimeResourcePathFromExecutablePath( - programPath, /*shared=*/true, pluginPath); - - auto defaultPluginPath = pluginPath; - llvm::sys::path::append(defaultPluginPath, "host", "plugins"); - - // Default plugin path. - Arguments.push_back("-plugin-path"); - Arguments.push_back(Args.MakeArgString(defaultPluginPath)); - - // Local plugin path. - llvm::sys::path::remove_filename(pluginPath); // Remove "swift" - llvm::sys::path::remove_filename(pluginPath); // Remove "lib" - llvm::sys::path::append(pluginPath, "local", "lib"); - llvm::sys::path::append(pluginPath, "swift"); - llvm::sys::path::append(pluginPath, "host", "plugins"); - Arguments.push_back("-plugin-path"); - Arguments.push_back(Args.MakeArgString(pluginPath)); -} - ToolChain::InvocationInfo toolchains::GenericUnix::constructInvocation(const InterpretJobAction &job, const JobContext &context) const { diff --git a/lib/Driver/WindowsToolChains.cpp b/lib/Driver/WindowsToolChains.cpp index d18408d660718..e51a7d139e9f3 100644 --- a/lib/Driver/WindowsToolChains.cpp +++ b/lib/Driver/WindowsToolChains.cpp @@ -43,17 +43,6 @@ std::string toolchains::Windows::sanitizerRuntimeLibName(StringRef Sanitizer, .str(); } -void -toolchains::Windows::addPluginArguments(const ArgList &Args, - ArgStringList &Arguments) const { - SmallString<261> LibraryPath = StringRef(getDriver().getSwiftProgramPath()); - llvm::sys::path::remove_filename(LibraryPath); // Remove `swift` - - // Default plugin path. - Arguments.push_back("-plugin-path"); - Arguments.push_back(Args.MakeArgString(LibraryPath)); -} - ToolChain::InvocationInfo toolchains::Windows::constructInvocation(const DynamicLinkJobAction &job, const JobContext &context) const { diff --git a/lib/DriverTool/CMakeLists.txt b/lib/DriverTool/CMakeLists.txt index c728f08399eeb..7e651d176b1a4 100644 --- a/lib/DriverTool/CMakeLists.txt +++ b/lib/DriverTool/CMakeLists.txt @@ -30,6 +30,17 @@ target_link_libraries(swiftDriverTool PUBLIC ${driver_common_libs}) +if (SWIFT_BUILD_SWIFT_SYNTAX) + target_link_libraries(swiftDriverTool + PRIVATE + swiftASTGen + ) + + add_dependencies(swiftDriverTool + swiftASTGen + ) +endif() + # If building as part of clang, make sure the headers are installed. if(NOT SWIFT_BUILT_STANDALONE) add_dependencies(swiftDriverTool clang-resource-headers) diff --git a/lib/Frontend/CMakeLists.txt b/lib/Frontend/CMakeLists.txt index 4c928fcce0cd6..db63361d816c4 100644 --- a/lib/Frontend/CMakeLists.txt +++ b/lib/Frontend/CMakeLists.txt @@ -37,4 +37,15 @@ target_link_libraries(swiftFrontend PRIVATE swiftSerialization swiftSymbolGraphGen) +if (SWIFT_BUILD_SWIFT_SYNTAX) + target_link_libraries(swiftFrontend + PRIVATE + swiftASTGen + ) + + add_dependencies(swiftFrontend + swiftASTGen + ) +endif() + set_swift_llvm_is_available(swiftFrontend) diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index d73cb0fae3aa3..40ef0041050d2 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -186,6 +186,37 @@ void CompilerInvocation::setDefaultBlocklistsIfNecessary() { } } +void CompilerInvocation::setDefaultInProcessPluginServerPathIfNecessary() { + if (!SearchPathOpts.InProcessPluginServerPath.empty()) + return; + if (FrontendOpts.MainExecutablePath.empty()) + return; + + // '/usr/bin/swift' + SmallString<64> serverLibPath{FrontendOpts.MainExecutablePath}; + llvm::sys::path::remove_filename(serverLibPath); // remove 'swift' + +#if defined(_WIN32) + // Windows: usr\bin\SwiftInProcPluginServer.dll + llvm::sys::path::append(serverLibPath, "SwiftInProcPluginServer.dll"); + +#elif defined(__APPLE__) + // Darwin: usr/lib/swift/host/libSwiftInProcPluginServer.dylib + llvm::sys::path::remove_filename(serverLibPath); // remove 'bin' + llvm::sys::path::append(serverLibPath, "lib", "swift", "host"); + llvm::sys::path::append(serverLibPath, "libSwiftInProcPluginServer.dylib"); + +#else + // Other: usr/lib/swift/host/libSwiftInProcPluginServer.so + llvm::sys::path::remove_filename(serverLibPath); // remove 'bin' + llvm::sys::path::append(serverLibPath, "lib", "swift", "host"); + llvm::sys::path::append(serverLibPath, "libSwiftInProcPluginServer.so"); + +#endif + + SearchPathOpts.InProcessPluginServerPath = serverLibPath.str(); +} + static void updateRuntimeLibraryPaths(SearchPathOptions &SearchPathOpts, const FrontendOptions &FrontendOpts, const LangOptions &LangOpts) { @@ -1971,6 +2002,9 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts, ArgList &Args, } Opts.setFrameworkSearchPaths(FrameworkSearchPaths); + if (const Arg *A = Args.getLastArg(OPT_in_process_plugin_server_path)) + Opts.InProcessPluginServerPath = A->getValue(); + // All plugin search options, i.e. '-load-plugin-library', // '-load-plugin-executable', '-plugin-path', and '-external-plugin-path' // are grouped, and plugins are searched by the order of these options. @@ -3501,6 +3535,7 @@ bool CompilerInvocation::parseArgs( updateRuntimeLibraryPaths(SearchPathOpts, FrontendOpts, LangOpts); setDefaultPrebuiltCacheIfNecessary(); setDefaultBlocklistsIfNecessary(); + setDefaultInProcessPluginServerPathIfNecessary(); // Now that we've parsed everything, setup some inter-option-dependent state. setIRGenOutputOptsFromFrontendOptions(IRGenOpts, FrontendOpts); diff --git a/lib/Parse/CMakeLists.txt b/lib/Parse/CMakeLists.txt index 4b51d767fe362..68bde86b613ef 100644 --- a/lib/Parse/CMakeLists.txt +++ b/lib/Parse/CMakeLists.txt @@ -29,26 +29,10 @@ target_link_libraries(swiftParse PRIVATE if (SWIFT_BUILD_SWIFT_SYNTAX) target_link_libraries(swiftParse PRIVATE - SwiftBasicFormat - SwiftParser - SwiftParserDiagnostics - SwiftDiagnostics - SwiftSyntax - SwiftOperators - SwiftSyntaxBuilder - SwiftSyntaxMacros swiftASTGen ) add_dependencies(swiftParse - SwiftBasicFormat - SwiftParser - SwiftParserDiagnostics - SwiftDiagnostics - SwiftSyntax - SwiftOperators - SwiftSyntaxBuilder - SwiftSyntaxMacros swiftASTGen ) endif() diff --git a/lib/Sema/TypeCheckMacros.cpp b/lib/Sema/TypeCheckMacros.cpp index f18f4ce2e608f..6a89256408183 100644 --- a/lib/Sema/TypeCheckMacros.cpp +++ b/lib/Sema/TypeCheckMacros.cpp @@ -48,38 +48,6 @@ using namespace swift; -#if SWIFT_BUILD_SWIFT_SYNTAX -/// Look for macro's type metadata given its external module and type name. -static void const * -lookupMacroTypeMetadataByExternalName(ASTContext &ctx, StringRef moduleName, - StringRef typeName, - LoadedLibraryPlugin *plugin) { - // Look up the type metadata accessor as a struct, enum, or class. - const Demangle::Node::Kind typeKinds[] = { - Demangle::Node::Kind::Structure, - Demangle::Node::Kind::Enum, - Demangle::Node::Kind::Class - }; - - void *accessorAddr = nullptr; - for (auto typeKind : typeKinds) { - auto symbolName = Demangle::mangledNameForTypeMetadataAccessor( - moduleName, typeName, typeKind); - accessorAddr = plugin->getAddressOfSymbol(symbolName.c_str()); - if (accessorAddr) - break; - } - - if (!accessorAddr) - return nullptr; - - // Call the accessor to form type metadata. - using MetadataAccessFunc = const void *(MetadataRequest); - auto accessor = reinterpret_cast(accessorAddr); - return accessor(MetadataRequest(MetadataState::Complete)); -} -#endif - /// Translate an argument provided as a string literal into an identifier, /// or return \c None and emit an error if it cannot be done. std::optional getIdentifierFromStringLiteralArgument( @@ -266,35 +234,33 @@ MacroDefinition MacroDefinitionRequest::evaluate( #endif } -static llvm::Expected -initializeExecutablePlugin(ASTContext &ctx, - LoadedExecutablePlugin *executablePlugin, - StringRef libraryPath, Identifier moduleName) { +static llvm::Expected +initializePlugin(ASTContext &ctx, CompilerPlugin *plugin, StringRef libraryPath, + Identifier moduleName) { // Lock the plugin while initializing. // Note that'executablePlugn' can be shared between multiple ASTContext. - executablePlugin->lock(); - SWIFT_DEFER { executablePlugin->unlock(); }; + plugin->lock(); + SWIFT_DEFER { plugin->unlock(); }; // FIXME: Ideally this should be done right after invoking the plugin. // But plugin loading is in libAST and it can't link ASTGen symbols. - if (!executablePlugin->isInitialized()) { + if (!plugin->isInitialized()) { #if SWIFT_BUILD_SWIFT_SYNTAX - if (!swift_ASTGen_initializePlugin(executablePlugin, &ctx.Diags)) { - return llvm::createStringError( - llvm::inconvertibleErrorCode(), "'%s' produced malformed response", - executablePlugin->getExecutablePath().data()); + if (!swift_ASTGen_initializePlugin(plugin, &ctx.Diags)) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "'%s' produced malformed response", + plugin->getPath().data()); } // Resend the compiler capability on reconnect. - auto *callback = new std::function( - [executablePlugin]() { - (void)swift_ASTGen_initializePlugin( - executablePlugin, /*diags=*/nullptr); - }); - executablePlugin->addOnReconnect(callback); + auto *callback = new std::function([plugin]() { + (void)swift_ASTGen_initializePlugin(plugin, /*diags=*/nullptr); + }); + plugin->addOnReconnect(callback); - executablePlugin->setCleanup([executablePlugin] { - swift_ASTGen_deinitializePlugin(executablePlugin); + plugin->setCleanup([plugin, callback] { + swift_ASTGen_deinitializePlugin(plugin); + delete callback; }); #endif } @@ -315,7 +281,7 @@ initializeExecutablePlugin(ASTContext &ctx, BridgedStringRef bridgedErrorOut{nullptr, 0}; bool loaded = swift_ASTGen_pluginServerLoadLibraryPlugin( - executablePlugin, resolvedLibraryPathStr.c_str(), moduleNameStr.c_str(), + plugin, resolvedLibraryPathStr.c_str(), moduleNameStr.c_str(), &bridgedErrorOut); auto errorOut = bridgedErrorOut.unbridged(); @@ -324,31 +290,30 @@ initializeExecutablePlugin(ASTContext &ctx, return llvm::createStringError( llvm::inconvertibleErrorCode(), "failed to load library plugin '%s' in plugin server '%s'; %s", - resolvedLibraryPathStr.c_str(), - executablePlugin->getExecutablePath().data(), errorOut.data()); + resolvedLibraryPathStr.c_str(), plugin->getPath().data(), + errorOut.data()); } assert(errorOut.data() == nullptr); // Set a callback to load the library again on reconnections. auto *callback = new std::function( - [executablePlugin, resolvedLibraryPathStr, moduleNameStr]() { + [plugin, resolvedLibraryPathStr, moduleNameStr]() { (void)swift_ASTGen_pluginServerLoadLibraryPlugin( - executablePlugin, resolvedLibraryPathStr.c_str(), - moduleNameStr.c_str(), + plugin, resolvedLibraryPathStr.c_str(), moduleNameStr.c_str(), /*errorOut=*/nullptr); }); - executablePlugin->addOnReconnect(callback); + plugin->addOnReconnect(callback); // Remove the callback and deallocate it when this ASTContext is destructed. - ctx.addCleanup([executablePlugin, callback]() { - executablePlugin->removeOnReconnect(callback); + ctx.addCleanup([plugin, callback]() { + plugin->removeOnReconnect(callback); delete callback; }); #endif } - return executablePlugin; + return plugin; } CompilerPluginLoadResult @@ -360,21 +325,21 @@ CompilerPluginLoadRequest::evaluate(Evaluator &evaluator, ASTContext *ctx, SmallString<0> errorMessage; if (!entry.executablePath.empty()) { - llvm::Expected executablePlugin = + llvm::Expected plugin = loader.loadExecutablePlugin(entry.executablePath); - if (executablePlugin) { + if (plugin) { if (ctx->LangOpts.EnableMacroLoadingRemarks) { unsigned tag = entry.libraryPath.empty() ? 1 : 2; ctx->Diags.diagnose(SourceLoc(), diag::macro_loaded, moduleName, tag, entry.executablePath, entry.libraryPath); } - executablePlugin = initializeExecutablePlugin( - *ctx, executablePlugin.get(), entry.libraryPath, moduleName); + plugin = + initializePlugin(*ctx, plugin.get(), entry.libraryPath, moduleName); } - if (executablePlugin) - return executablePlugin.get(); - llvm::handleAllErrors(executablePlugin.takeError(), + if (plugin) + return plugin.get(); + llvm::handleAllErrors(plugin.takeError(), [&](const llvm::ErrorInfoBase &err) { if (!errorMessage.empty()) errorMessage += ", "; @@ -382,17 +347,21 @@ CompilerPluginLoadRequest::evaluate(Evaluator &evaluator, ASTContext *ctx, }); } else if (!entry.libraryPath.empty()) { - llvm::Expected libraryPlugin = - loader.loadLibraryPlugin(entry.libraryPath); - if (libraryPlugin) { - if (ctx->LangOpts.EnableMacroLoadingRemarks) { - ctx->Diags.diagnose(SourceLoc(), diag::macro_loaded, moduleName, 0, - entry.libraryPath, StringRef()); + llvm::Expected plugins = loader.getInProcessPlugins(); + if (plugins) { + plugins = + initializePlugin(*ctx, plugins.get(), entry.libraryPath, moduleName); + if (plugins) { + if (ctx->LangOpts.EnableMacroLoadingRemarks) { + ctx->Diags.diagnose(SourceLoc(), diag::macro_loaded, moduleName, 0, + entry.libraryPath, StringRef()); + } + return plugins.get(); } + } - return libraryPlugin.get(); - } else { - llvm::handleAllErrors(libraryPlugin.takeError(), + if (!plugins) { + llvm::handleAllErrors(plugins.takeError(), [&](const llvm::ErrorInfoBase &err) { if (!errorMessage.empty()) errorMessage += ", "; @@ -410,63 +379,23 @@ CompilerPluginLoadRequest::evaluate(Evaluator &evaluator, ASTContext *ctx, } } -static ExternalMacroDefinition -resolveInProcessMacro(ASTContext &ctx, Identifier moduleName, - Identifier typeName, LoadedLibraryPlugin *plugin) { +static ExternalMacroDefinition resolveExternalMacro(ASTContext &ctx, + CompilerPlugin *plugin, + Identifier moduleName, + Identifier typeName) { #if SWIFT_BUILD_SWIFT_SYNTAX - /// Look for the type metadata given the external module and type names. - auto macroMetatype = lookupMacroTypeMetadataByExternalName( - ctx, moduleName.str(), typeName.str(), plugin); - if (macroMetatype) { - // Check whether the macro metatype is in-process. - if (auto inProcess = swift_ASTGen_resolveMacroType(macroMetatype)) { - // Make sure we clean up after the macro. - ctx.addCleanup([inProcess]() { - swift_ASTGen_destroyMacro(inProcess); - }); - - return ExternalMacroDefinition{ - ExternalMacroDefinition::PluginKind::InProcess, inProcess}; - } else { - NullTerminatedStringRef err( - "'" + moduleName.str() + "." + typeName.str() + - "' is not a valid macro implementation type in library plugin '" + - StringRef(plugin->getLibraryPath()) + "'", - ctx); - - return ExternalMacroDefinition::error(err); - } - } - NullTerminatedStringRef err("'" + moduleName.str() + "." + typeName.str() + - "' could not be found in library plugin '" + - StringRef(plugin->getLibraryPath()) + "'", - ctx); - return ExternalMacroDefinition::error(err); -#endif - return ExternalMacroDefinition::error( - "the current compiler was not built with macro support"); -} - -static ExternalMacroDefinition -resolveExecutableMacro(ASTContext &ctx, - LoadedExecutablePlugin *executablePlugin, - Identifier moduleName, Identifier typeName) { -#if SWIFT_BUILD_SWIFT_SYNTAX - if (auto *execMacro = swift_ASTGen_resolveExecutableMacro( - moduleName.get(), typeName.get(), executablePlugin)) { + if (auto *macro = swift_ASTGen_resolveExternalMacro(moduleName.get(), + typeName.get(), plugin)) { // Make sure we clean up after the macro. - ctx.addCleanup( - [execMacro]() { swift_ASTGen_destroyExecutableMacro(execMacro); }); - return ExternalMacroDefinition{ - ExternalMacroDefinition::PluginKind::Executable, execMacro}; + ctx.addCleanup([macro]() { swift_ASTGen_destroyExternalMacro(macro); }); + return ExternalMacroDefinition::success(macro); } // NOTE: this is not reachable because executable macro resolution always // succeeds. - NullTerminatedStringRef err( - "'" + moduleName.str() + "." + typeName.str() + - "' could not be found in executable plugin" + - StringRef(executablePlugin->getExecutablePath()), - ctx); + NullTerminatedStringRef err("'" + moduleName.str() + "." + typeName.str() + + "' could not be found in executable plugin" + + StringRef(plugin->getPath()), + ctx); return ExternalMacroDefinition::error(err); #endif return ExternalMacroDefinition::error( @@ -483,12 +412,8 @@ ExternalMacroDefinitionRequest::evaluate(Evaluator &evaluator, ASTContext *ctx, CompilerPluginLoadResult loaded = evaluateOrDefault( evaluator, loadRequest, CompilerPluginLoadResult::error("request error")); - if (auto loadedLibrary = loaded.getAsLibraryPlugin()) { - return resolveInProcessMacro(*ctx, moduleName, typeName, loadedLibrary); - } - - if (auto *executablePlugin = loaded.getAsExecutablePlugin()) { - return resolveExecutableMacro(*ctx, executablePlugin, moduleName, typeName); + if (auto plugin = loaded.get()) { + return resolveExternalMacro(*ctx, plugin, moduleName, typeName); } return ExternalMacroDefinition::error(loaded.getErrorMessage()); @@ -1222,8 +1147,7 @@ evaluateFreestandingMacro(FreestandingMacroExpansion *expansion, BridgedStringRef evaluatedSourceOut{nullptr, 0}; assert(!externalDef.isError()); swift_ASTGen_expandFreestandingMacro( - &ctx.Diags, externalDef.opaqueHandle, - static_cast(externalDef.kind), discriminator->c_str(), + &ctx.Diags, externalDef.get(), discriminator->c_str(), getRawMacroRole(macroRole), astGenSourceFile, expansion->getSourceRange().Start.getOpaquePointerValue(), &evaluatedSourceOut); @@ -1547,8 +1471,7 @@ static SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo, BridgedStringRef evaluatedSourceOut{nullptr, 0}; assert(!externalDef.isError()); swift_ASTGen_expandAttachedMacro( - &ctx.Diags, externalDef.opaqueHandle, - static_cast(externalDef.kind), discriminator->c_str(), + &ctx.Diags, externalDef.get(), discriminator->c_str(), extendedType.c_str(), conformanceList.c_str(), getRawMacroRole(role), astGenAttrSourceFile, attr->AtLoc.getOpaquePointerValue(), astGenDeclSourceFile, searchDecl->getStartLoc().getOpaquePointerValue(), diff --git a/test/Driver/compiler_plugin_path.swift b/test/Driver/compiler_plugin_path.swift index 535c52e1c8e45..3b539e92ff6f1 100644 --- a/test/Driver/compiler_plugin_path.swift +++ b/test/Driver/compiler_plugin_path.swift @@ -1,7 +1,6 @@ // RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 %s 2>^1 | %FileCheck %s -// CHECK: -plugin-path -// CHECK-SAME: {{(/|\\\\)}}lib{{(/|\\\\)}}swift{{(/|\\\\)}}host{{(/|\\\\)}}plugins +// REQUIRES: OS=macosx || OS=linux-gnu -// CHECK-SAME: -plugin-path -// CHECK-SAME: {{(/|\\\\)}}local{{(/|\\\\)}}lib{{(/|\\\\)}}swift{{(/|\\\\)}}host{{(/|\\\\)}}plugins +// CHECK: -plugin-path {{[^ ]+}}/lib/swift/host/plugins +// CHECK-SAME: -plugin-path {{[^ ]+}}/local/lib/swift/host/plugins diff --git a/test/Driver/compiler_plugin_path_windows.swift b/test/Driver/compiler_plugin_path_windows.swift new file mode 100644 index 0000000000000..ed62e588114cb --- /dev/null +++ b/test/Driver/compiler_plugin_path_windows.swift @@ -0,0 +1,5 @@ +// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 %s 2>^1 | %FileCheck %s + +// REQUIRES: OS=windows + +// CHECK: -plugin-path {{[^ ]+}}\bin diff --git a/test/Macros/macro_expand.swift b/test/Macros/macro_expand.swift index 840020e7b7f02..48dbfd9e72573 100644 --- a/test/Macros/macro_expand.swift +++ b/test/Macros/macro_expand.swift @@ -631,8 +631,8 @@ struct HasNestedType { #if TEST_DIAGNOSTICS @freestanding(expression) macro missingMacro() = #externalMacro(module: "MacroDefinition", type: "BluhBlah") -// expected-warning@-1 {{external macro implementation type 'MacroDefinition.BluhBlah' could not be found for macro 'missingMacro()'; 'MacroDefinition.BluhBlah' could not be found in library plugin '}} +// FIXME: xpected-warning@-1 {{external macro implementation type 'MacroDefinition.BluhBlah' could not be found for macro 'missingMacro()'; 'MacroDefinition.BluhBlah' could not be found in library plugin '}} @freestanding(expression) macro notMacro() = #externalMacro(module: "MacroDefinition", type: "NotMacroStruct") -// expected-warning@-1 {{macro implementation type 'MacroDefinition.NotMacroStruct' could not be found for macro 'notMacro()'; 'MacroDefinition.NotMacroStruct' is not a valid macro implementation type in library plugin '}} +// FIXME: xpected-warning@-1 {{macro implementation type 'MacroDefinition.NotMacroStruct' could not be found for macro 'notMacro()'; 'MacroDefinition.NotMacroStruct' is not a valid macro implementation type in library plugin '}} #endif diff --git a/test/Macros/macro_plugin_broken_shlib.swift b/test/Macros/macro_plugin_broken_shlib.swift index 9b11627774112..eb5de20a97a9d 100644 --- a/test/Macros/macro_plugin_broken_shlib.swift +++ b/test/Macros/macro_plugin_broken_shlib.swift @@ -34,8 +34,8 @@ // RUN: c-index-test -read-diagnostics %t/macro_expand_inproc.dia 2>&1 | %FileCheck -check-prefix INPROC %s // INPROC-NOT: {{error|warning}} -// INPROC: test.swift:1:33: warning: external macro implementation type 'TestPlugin.FooMacro' could not be found for macro 'fooMacro'; compiler plugin 'BUILD_DIR/{{.*}}/libTestPlugin.dylib' could not be loaded; dlopen(BUILD_DIR/{{.*}}/libTestPlugin.dylib, 0x0005): tried: 'BUILD_DIR/{{.*}}/libTestPlugin.dylib' -// INPROC: test.swift:4:7: error: external macro implementation type 'TestPlugin.FooMacro' could not be found for macro 'fooMacro'; compiler plugin 'BUILD_DIR/{{.*}}/libTestPlugin.dylib' could not be loaded; dlopen(BUILD_DIR/{{.*}}/libTestPlugin.dylib, 0x0005): tried: 'BUILD_DIR/{{.*}}/libTestPlugin.dylib' +// INPROC: test.swift:1:33: warning: external macro implementation type 'TestPlugin.FooMacro' could not be found for macro 'fooMacro'; failed to load library plugin 'BUILD_DIR/{{.*}}/libTestPlugin.dylib' in plugin server 'BUILD_DIR/{{.*}}/libSwiftInProcPluginServer.dylib'; loader error: dlopen(BUILD_DIR/{{.*}}/libTestPlugin.dylib, 0x0005): tried: 'BUILD_DIR/{{.*}}/libTestPlugin.dylib' +// INPROC: test.swift:4:7: error: external macro implementation type 'TestPlugin.FooMacro' could not be found for macro 'fooMacro'; failed to load library plugin 'BUILD_DIR/{{.*}}/libTestPlugin.dylib' in plugin server 'BUILD_DIR/{{.*}}/libSwiftInProcPluginServer.dylib'; loader error: dlopen(BUILD_DIR/{{.*}}/libTestPlugin.dylib, 0x0005): tried: 'BUILD_DIR/{{.*}}/libTestPlugin.dylib' // INPROC: test.swift:1:33: note: 'fooMacro' declared here // INPROC-NOT: {{error|warning}} diff --git a/test/Macros/macro_plugin_error.swift b/test/Macros/macro_plugin_error.swift index f57d8272ccd55..52746e7ec70cb 100644 --- a/test/Macros/macro_plugin_error.swift +++ b/test/Macros/macro_plugin_error.swift @@ -35,7 +35,7 @@ func test() { // FIXME: -module-abi-name ABI name is leaking. let _: String = #fooMacro(1) - // expected-error @-1 {{typeMismatch(CompilerSwiftCompilerPluginMessageHandling.PluginToHostMessage}} + // expected-error @-1 {{typeMismatch(_CompilerSwiftCompilerPluginMessageHandling.PluginToHostMessage}} let _: String = #fooMacro(2) // expected-error @-1 {{failed to receive result from plugin (from macro 'fooMacro')}} let _: String = #fooMacro(3) diff --git a/test/lit.cfg b/test/lit.cfg index c327efdb9f7bd..29df86abd4327 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -2708,14 +2708,14 @@ config.substitutions.append(('%target-sil-nm', config.target_sil_nm)) config.substitutions.append(('%batch-code-completion', '%empty-directory(%t/batch-code-completion) && %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t/batch-code-completion')) config.substitutions.append(('%target-swift-ide-test\(mock-sdk:([^)]+)\)', - SubstituteCaptures(r'%s \1 %s -swift-version %s' % ( + SubstituteCaptures(r'%s \1 %s %s' % ( escape_for_substitute_captures(subst_target_swift_ide_test_mock_sdk), escape_for_substitute_captures(subst_target_swift_ide_test_mock_sdk_after), - escape_for_substitute_captures(swift_version))))) + escape_for_substitute_captures(config.swift_test_options))))) config.substitutions.append(('%target-swift-ide-test', - "%s -swift-version %s %s" % (config.target_swift_ide_test, - swift_version, - config.clang_system_overlay_opt))) + "%s %s %s" % (config.target_swift_ide_test, + config.swift_test_options, + config.clang_system_overlay_opt))) config.substitutions.append(('%target-swift-symbolgraph-extract', config.target_swift_symbolgraph_extract)) config.substitutions.append(('%target-swift-api-extract', config.target_swift_api_extract)) diff --git a/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake b/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake index 00fc913fe8682..a37d3383f73e6 100644 --- a/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake +++ b/tools/SourceKit/cmake/modules/AddSwiftSourceKit.cmake @@ -150,11 +150,11 @@ function(add_sourcekit_swift_runtime_link_flags target path HAS_SWIFT_MODULES) if(SWIFT_BUILD_SWIFT_SYNTAX) if(SWIFT_HOST_VARIANT_SDK IN_LIST SWIFT_DARWIN_PLATFORMS) # Add rpath to the host Swift libraries. - file(RELATIVE_PATH relative_hostlib_path "${path}" "${SWIFTLIB_DIR}/host") + file(RELATIVE_PATH relative_hostlib_path "${path}" "${SWIFTLIB_DIR}/host/compiler") list(APPEND RPATH_LIST "@loader_path/${relative_hostlib_path}") elseif(SWIFT_HOST_VARIANT_SDK MATCHES "LINUX|ANDROID|OPENBSD") # Add rpath to the host Swift libraries. - file(RELATIVE_PATH relative_hostlib_path "${path}" "${SWIFTLIB_DIR}/host") + file(RELATIVE_PATH relative_hostlib_path "${path}" "${SWIFTLIB_DIR}/host/compiler") list(APPEND RPATH_LIST "$ORIGIN/${relative_hostlib_path}") else() target_link_directories(${target} PRIVATE diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 5f81c6cd17d70..a0d48d45450b6 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -12,6 +12,7 @@ function(add_swift_parser_link_libraries target) foreach(macrolib ${SWIFT_MACRO_PLUGINS}) add_dependencies(${target} ${macrolib}) endforeach() + add_dependencies(${target} SwiftInProcPluginServer) endif() endfunction() diff --git a/tools/libSwiftScan/CMakeLists.txt b/tools/libSwiftScan/CMakeLists.txt index b078db98f0dca..4ab7956061a3c 100644 --- a/tools/libSwiftScan/CMakeLists.txt +++ b/tools/libSwiftScan/CMakeLists.txt @@ -34,7 +34,7 @@ set_target_properties(libSwiftScan if(SWIFT_HOST_VARIANT_SDK MATCHES "LINUX|ANDROID|OPENBSD|FREEBSD" AND BOOTSTRAPPING_MODE STREQUAL "HOSTTOOLS") # Runtime INSTALL_RPATH are set by 'add_swift_host_library', but that expects # libSwiftScan be installed in 'lib'. But since it's actually installed in 'lib/swift/host', - # we need to have correct runtime path to 'lib/swift/{platform}'. + # we need to have correct swift runtime path to 'lib/swift/{platform}'. # FIXME: BUILD_RPATH and INSTALL_PATH should be different # FIXME: add_swift_host_library should accept 'DESTINATION' and handle installation # FIXME: Build this library into 'lib/swift/host/' instead of 'lib/' @@ -46,20 +46,20 @@ endif() if(SWIFT_BUILD_SWIFT_SYNTAX) if(SWIFT_HOST_VARIANT_SDK IN_LIST SWIFT_DARWIN_PLATFORMS) - # Ensure that we can find the host shared libraries. + # Ensure that we can find the shared swift-syntax libraries. set_property( TARGET libSwiftScan - APPEND PROPERTY INSTALL_RPATH "@loader_path/swift/host") + APPEND PROPERTY INSTALL_RPATH "@loader_path/swift/host/compiler") set_property( TARGET libSwiftScan - APPEND PROPERTY INSTALL_RPATH "@loader_path/../host") + APPEND PROPERTY INSTALL_RPATH "@loader_path/../host/compiler") elseif(SWIFT_HOST_VARIANT_SDK MATCHES "LINUX|ANDROID|OPENBSD|FREEBSD") set_property( TARGET libSwiftScan - APPEND PROPERTY INSTALL_RPATH "$ORIGIN/swift/host") + APPEND PROPERTY INSTALL_RPATH "$ORIGIN/swift/host/compiler") set_property( TARGET libSwiftScan - APPEND PROPERTY INSTALL_RPATH "$ORIGIN/../host") + APPEND PROPERTY INSTALL_RPATH "$ORIGIN/../host/compiler") endif() endif() diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 3abb0b4a2e4d9..e7a244240d92a 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -332,6 +332,11 @@ ImportObjCHeader("import-objc-header", llvm::cl::desc("header to implicitly import"), llvm::cl::cat(Category)); +static llvm::cl::opt +InProcessPluginServerPath("in-process-plugin-server-path", + llvm::cl::desc("in-process plugin server"), + llvm::cl::cat(Category)); + static llvm::cl::list PluginPath("plugin-path", llvm::cl::desc("plugin-path"), @@ -4541,6 +4546,10 @@ int main(int argc, char *argv[]) { } } + if (!options::InProcessPluginServerPath.empty()) { + InitInvok.getSearchPathOptions().InProcessPluginServerPath = + options::InProcessPluginServerPath; + } if (!options::LoadPluginLibrary.empty()) { std::vector paths; for (auto path: options::LoadPluginLibrary) { @@ -4567,6 +4576,7 @@ int main(int argc, char *argv[]) { InitInvok.getSearchPathOptions().PluginSearchOpts.emplace_back( PluginSearchOption::PluginPath{path}); } + InitInvok.setDefaultInProcessPluginServerPathIfNecessary(); // Process the clang arguments last and allow them to override previously // set options. diff --git a/tools/swift-plugin-server/CMakeLists.txt b/tools/swift-plugin-server/CMakeLists.txt index e1e7ffc6f93b5..1bedf2f0d9ee5 100644 --- a/tools/swift-plugin-server/CMakeLists.txt +++ b/tools/swift-plugin-server/CMakeLists.txt @@ -7,4 +7,32 @@ if (SWIFT_BUILD_SWIFT_SYNTAX) SwiftCompilerPluginMessageHandling SwiftLibraryPluginProvider ) + + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${SWIFT_HOST_LIBRARIES_DEST_DIR}") + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${SWIFT_HOST_LIBRARIES_DEST_DIR}") + add_pure_swift_host_library(SwiftInProcPluginServer SHARED + Sources/SwiftInProcPluginServer/InProcPluginServer.swift + SWIFT_DEPENDENCIES + SwiftCompilerPluginMessageHandling + SwiftLibraryPluginProvider + ) + + if(SWIFT_HOST_VARIANT_SDK IN_LIST SWIFT_DARWIN_PLATFORMS) + set_property(TARGET SwiftInProcPluginServer + APPEND PROPERTY INSTALL_RPATH + "@loader_path") + elseif(SWIFT_HOST_VARIANT_SDK MATCHES "LINUX|ANDROID|OPENBSD|FREEBSD") + set_property(TARGET SwiftInProcPluginServer + APPEND PROPERTY INSTALL_RPATH + "$ORIGIN") + endif() + + set_property(TARGET ${name} + PROPERTY BUILD_WITH_INSTALL_RPATH YES) + + add_dependencies(compiler SwiftInProcPluginServer) + swift_install_in_component(TARGETS SwiftInProcPluginServer + ARCHIVE DESTINATION "lib${LLVM_LIBDIR_SUFFIX}/swift/host" COMPONENT compiler + LIBRARY DESTINATION "lib${LLVM_LIBDIR_SUFFIX}/swift/host" COMPONENT compiler + RUNTIME DESTINATION "bin" COMPONENT compiler) endif() diff --git a/tools/swift-plugin-server/Package.swift b/tools/swift-plugin-server/Package.swift index a30a97a118bbf..5ad685b7b6ca1 100644 --- a/tools/swift-plugin-server/Package.swift +++ b/tools/swift-plugin-server/Package.swift @@ -7,6 +7,10 @@ let package = Package( platforms: [ .macOS(.v10_15) ], + products: [ + .executable(name: "swift-plugin-server", targets: ["swift-plugin-server"]), + .library(name: "SwiftInProcPluginServer", type: .dynamic, targets: ["SwiftInProcPluginServer"]), + ], dependencies: [ .package(path: "../../../swift-syntax"), ], @@ -14,7 +18,15 @@ let package = Package( .executableTarget( name: "swift-plugin-server", dependencies: [ - .product(name: "SwiftCompilerPluginMessageHandling", package: "swift-syntax"), + .product(name: "_SwiftCompilerPluginMessageHandling", package: "swift-syntax"), + .product(name: "_SwiftLibraryPluginProvider", package: "swift-syntax"), + ] + ), + .target( + name: "SwiftInProcPluginServer", + dependencies: [ + .product(name: "_SwiftCompilerPluginMessageHandling", package: "swift-syntax"), + .product(name: "_SwiftLibraryPluginProvider", package: "swift-syntax"), ] ), ], diff --git a/tools/swift-plugin-server/Sources/SwiftInProcPluginServer/InProcPluginServer.swift b/tools/swift-plugin-server/Sources/SwiftInProcPluginServer/InProcPluginServer.swift new file mode 100644 index 0000000000000..bf15260835e79 --- /dev/null +++ b/tools/swift-plugin-server/Sources/SwiftInProcPluginServer/InProcPluginServer.swift @@ -0,0 +1,88 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +@_spi(PluginMessage) import SwiftCompilerPluginMessageHandling +@_spi(PluginMessage) import SwiftLibraryPluginProvider + +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc +#elseif canImport(Musl) +import Musl +#elseif canImport(ucrt) +import ucrt +#else +#error("'malloc' not found") +#endif + +/// Entry point. +/// +/// Compiler 'dlopen' this 'SwiftInProcPluginServer' library, and 'dlsym' this +/// function. When the compiler wants to use dylib plugins, it calls this +/// function with the same message as `swift-plugin-server`. +/// +/// The caller must `free` the returned buffer +@_cdecl("swift_inproc_plugins_handle_message") +@MainActor +public func handleMessage( + _ inputData: UnsafePointer!, + _ inputLength: Int, + _ outputData: UnsafeMutablePointer?>!, + _ outputLength: UnsafeMutablePointer! +) -> Bool { + do { + let input = UnsafeBufferPointer(start: inputData, count: inputLength) + let output = try InProcPluginServer.shared.handleMessage(input) + output.withUnsafeBufferPointer(fillOutput(_:)) + return false // Success. + } catch { + var message = "Internal Error: \(error)" + message.withUTF8(fillOutput(_:)) + return true // Error. + } + + func fillOutput(_ responseData: UnsafeBufferPointer) { + // NOTE: Use 'malloc' instead of 'UnsafeMutablePointer.allocate()' so that + // C/C++ clients can deallocate it without using Swift. + let buffer = malloc(responseData.count)! + buffer.initializeMemory( + as: UInt8.self, + from: responseData.baseAddress!, + count: responseData.count + ) + outputData.pointee = buffer.assumingMemoryBound(to: UInt8.self) + outputLength.pointee = responseData.count + } +} + +/// Singleton "plugin server". +struct InProcPluginServer { + private let handler: CompilerPluginMessageHandler + + @MainActor + private init() { + self.handler = CompilerPluginMessageHandler( + provider: LibraryPluginProvider.shared + ) + } + + func handleMessage(_ input: UnsafeBufferPointer) throws -> [UInt8] { + let request = try JSON.decode(HostToPluginMessage.self, from: input) + let response = handler.handleMessage(request) + return try JSON.encode(response) + } + + @MainActor + static let shared = Self() +} +