diff --git a/Gemfile.lock b/Gemfile.lock index 84556ae..abb763b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -73,7 +73,8 @@ GEM rake (13.0.6) rchardet (1.8.0) regexp_parser (2.5.0) - rexml (3.2.5) + rexml (3.3.3) + strscan rouge (2.0.7) rubocop (1.31.2) json (~> 2.3) @@ -92,6 +93,7 @@ GEM sawyer (0.9.2) addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) + strscan (3.1.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) unicode-display_width (2.2.0) diff --git a/Package.swift b/Package.swift index d39e5b1..809e492 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.6 +// swift-tools-version: 6.0 import PackageDescription let package = Package( @@ -29,8 +29,9 @@ let package = Package( ), .binaryTarget( name: "swiftgen", - url: "https://github.com/SwiftGen/SwiftGen/releases/download/6.6.2/swiftgen-6.6.2.artifactbundle.zip", - checksum: "7586363e24edcf18c2da3ef90f379e9559c1453f48ef5e8fbc0b818fbbc3a045" + url: "https://github.com/SwiftGen/SwiftGen/releases/download/6.6.3/swiftgen-6.6.3.artifactbundle.zip", + checksum: "caf1feaf93dd32bc5037f0b6ded8d0f4fe28ab5d2f6e5c3edf2572006ba0b7eb" ) - ] + ], + swiftLanguageVersions: [.v5, .v6] ) diff --git a/Plugins/SwiftGen-Generate/Plugin.swift b/Plugins/SwiftGen-Generate/Plugin.swift index 14b89b1..ec477b4 100644 --- a/Plugins/SwiftGen-Generate/Plugin.swift +++ b/Plugins/SwiftGen-Generate/Plugin.swift @@ -12,24 +12,21 @@ struct SwiftGenPlugin: CommandPlugin { func performCommand(context: PluginContext, arguments: [String]) async throws { let swiftgen = try context.tool(named: "swiftgen") let fileManager = FileManager.default + + let configuration = context.package.directory.appending("swiftgen.yml") + if fileManager.fileExists(atPath: configuration.string) { + try swiftgen.run(configuration, environment: env(context: context)) + } - // if user provided arguments, use those - if !arguments.isEmpty { - try swiftgen.run(arguments: arguments, environment: env(context: context)) - } else { - // otherwise scan for configs - let configuration = context.package.directory.appending("swiftgen.yml") + // check each target + let targetsFromArgs = parseTargets(arguments) + let targets = context.package.targets + .compactMap { $0 as? SourceModuleTarget } + .filter { targetsFromArgs.isEmpty || targetsFromArgs.contains($0.name) } + for target in targets { + let configuration = target.directory.appending("swiftgen.yml") if fileManager.fileExists(atPath: configuration.string) { - try swiftgen.run(configuration, environment: env(context: context)) - } - - // check each target - let targets = context.package.targets.compactMap { $0 as? SourceModuleTarget } - for target in targets { - let configuration = target.directory.appending("swiftgen.yml") - if fileManager.fileExists(atPath: configuration.string) { - try swiftgen.run(configuration, environment: env(context: context, target: target)) - } + try swiftgen.run(configuration, environment: env(context: context, target: target)) } } } @@ -41,9 +38,21 @@ private extension SwiftGenPlugin { [ "PROJECT_DIR": context.package.directory.string, "TARGET_NAME": target?.name ?? "", - "PRODUCT_MODULE_NAME": target?.moduleName ?? "" + "PRODUCT_MODULE_NAME": target?.moduleName ?? "", + "DERIVED_SOURCES_DIR": context.pluginWorkDirectory.string ] } + + func parseTargets(_ arguments: [String]) -> [String] { + var result = [String]() + for (i, arg) in arguments.enumerated() where arg == "--target" { + let valueIdx = i + 1 + if valueIdx < arguments.count { + result.append(arguments[valueIdx]) + } + } + return result + } } private extension PluginContext.Tool { diff --git a/Plugins/SwiftGenPlugin/Plugin.swift b/Plugins/SwiftGenPlugin/Plugin.swift index 1aa9b45..119f619 100644 --- a/Plugins/SwiftGenPlugin/Plugin.swift +++ b/Plugins/SwiftGenPlugin/Plugin.swift @@ -31,6 +31,33 @@ struct SwiftGenPlugin: BuildToolPlugin { } } +#if canImport(XcodeProjectPlugin) +import XcodeProjectPlugin + +extension SwiftGenPlugin: XcodeBuildToolPlugin { + func createBuildCommands(context: XcodePluginContext, target: XcodeTarget) throws -> [Command] { + let fileManager = FileManager.default + + // Possible paths where there may be a config file (root of package, target dir.) + let configurations: [Path] = [context.xcodeProject.directory] + .map { $0.appending("swiftgen.yml") } + .filter { fileManager.fileExists(atPath: $0.string) } + + // Validate paths list + guard validate(configurations: configurations, target: target) else { + return [] + } + + // Clear the SwiftGen plugin's directory (in case of dangling files) + fileManager.forceClean(directory: context.pluginWorkDirectory) + + return try configurations.map { configuration in + try .swiftgen(using: configuration, context: context, target: target) + } + } +} +#endif + // MARK: - Helpers private extension SwiftGenPlugin { @@ -47,10 +74,41 @@ private extension SwiftGenPlugin { return true } + +#if canImport(XcodeProjectPlugin) + func validate(configurations: [Path], target: XcodeTarget) -> Bool { + guard !configurations.isEmpty else { + Diagnostics.error(""" + No SwiftGen configurations found for target \(target.displayName). If you would like to generate sources for this \ + target include a `swiftgen.yml` in the target's source directory, or include a shared `swiftgen.yml` at the \ + package's root. + """) + return false + } + + return true + } +#endif } private extension Command { static func swiftgen(using configuration: Path, context: PluginContext, target: Target) throws -> Command { +// .prebuildCommand( +// displayName: "SwiftGen BuildTool Plugin", +// executable: try context.tool(named: "swiftgen").url, +// arguments: [ +// "config", +// "run", +// "--verbose", +// "--config", "\(configuration)" +// ], +// environment: [ +// "PROJECT_DIR": context.package.directoryURL.path, +// "TARGET_NAME": target.name, +// "PRODUCT_MODULE_NAME": target.moduleName, +// "DERIVED_SOURCES_DIR": context.pluginWorkDirectoryURL.path +// ], +// outputFilesDirectory: context.pluginWorkDirectoryURL) .prebuildCommand( displayName: "SwiftGen BuildTool Plugin", executable: try context.tool(named: "swiftgen").path, @@ -69,6 +127,27 @@ private extension Command { outputFilesDirectory: context.pluginWorkDirectory ) } + +#if canImport(XcodeProjectPlugin) + static func swiftgen(using configuration: Path, context: XcodePluginContext, target: XcodeTarget) throws -> Command { + .prebuildCommand( + displayName: "SwiftGen BuildTool Plugin", + executable: try context.tool(named: "swiftgen").path, + arguments: [ + "config", + "run", + "--verbose", + "--config", "\(configuration)" + ], + environment: [ + "PROJECT_DIR": context.xcodeProject.directory, + "TARGET_NAME": target.displayName, + "DERIVED_SOURCES_DIR": context.pluginWorkDirectory + ], + outputFilesDirectory: context.pluginWorkDirectory + ) + } +#endif } private extension FileManager {