I am in the process of creating a macro to generate random sample data for unit testing and SwiftUI previews. To produce this data, I am utilizing a library named Fakery (https://github.com/vadymmarkov/Fakery). Everything seemed to be compiling and functioning smoothly until I initiated a call to an object from Fakery. At this juncture, Xcode presented me with an error stating, "Failed to receive result from plugin", indicating a problem with code generation.
In my efforts to pinpoint the issue, I created a fresh project to modify the #stringify
macro so it would produce a String
utilizing Fakery. The identical issue reemerged. Out of curiosity, I introduced alternative dependencies with the capability of generating Strings
for testing. To my astonishment, these other libraries did not manifest the same problem that Fakery was producing.
The puzzling aspect is that the tests run and pass flawlessly, and everything compiles perfectly unless the client invokes these particular instances. This leads me to wonder: Are there specific requirements for dependencies in macros that ensure proper code generation and correct compilation?
Below is the package configuration, with the submodule utilizing Fakery being dubbed "Demo":
import PackageDescription
import CompilerPluginSupport
let package = Package(
name: "MyMacroDemoFakery",
platforms: [.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6), .macCatalyst(.v13)],
products: [
// Products define the executables and libraries a package produces, making them visible to other packages.
.library(
name: "MyMacroDemoFakery",
targets: ["MyMacroDemoFakery"]
),
.executable(
name: "MyMacroDemoFakeryClient",
targets: ["MyMacroDemoFakeryClient"]
),
],
dependencies: [
// Depend on the latest Swift 5.9 prerelease of SwiftSyntax
.package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.0-swift-5.9-DEVELOPMENT-SNAPSHOT-2023-04-25-b"),
.package(url: "https://github.com/vadymmarkov/Fakery.git", from: "5.1.0"),
.package(url: "https://github.com/Rightpoint/BonMot.git", from: "6.1.3")
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
// Macro implementation that performs the source transformation of a macro.
.macro(
name: "MyMacroDemoFakeryMacros",
dependencies: [
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
"Demo",
]
),
// Library that exposes a macro as part of its API, which is used in client programs.
.target(name: "MyMacroDemoFakery", dependencies: ["MyMacroDemoFakeryMacros"]),
.target(name: "Demo", dependencies: [
.product(name: "Fakery", package: "Fakery"), // Fakery is failing 🔥
.product(name: "BonMot", package: "BonMot") // BonMot is working as expected âś…
]),
// A client of the library, which is able to use the macro in its own code.
.executableTarget(name: "MyMacroDemoFakeryClient", dependencies: [
"MyMacroDemoFakery",
]),
// A test target used to develop the macro implementation.
.testTarget(
name: "MyMacroDemoFakeryTests",
dependencies: [
"MyMacroDemoFakeryMacros",
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
"Demo"
]
),
]
)
This is the code incorporating Fakery:
import Foundation
import Fakery
import BonMot
public struct Demo {
public static func getName() -> String {
Faker().name.name()
//"\"\(StringStyle().attributes.description)\""//#""Hello""#
}
}
The Macro implementation:
public struct StringifyMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
guard let argument = node.argumentList.first?.expression else {
fatalError("compiler bug: the macro does not have any arguments")
}
return ExprSyntax(stringLiteral: Demo.getName())
}
}
And here is the client:
import MyMacroDemoFakery
let result = #stringify(1)
print("The value \(result)")
Thanks,
Pitt