Skip to content

Commit 03bbe7a

Browse files
authored
fix: fixed Path deprecation warnings in package plugin
1 parent cc5416f commit 03bbe7a

10 files changed

Lines changed: 307 additions & 60 deletions

File tree

Plugins/MetaProtocolCodable/Config.swift

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -71,24 +71,6 @@ extension Config: Codable {
7171
ScanMode.self, forCaseInsensitiveKey: .scan
7272
) ?? .target
7373
}
74-
75-
/// Returns file path as URL converting provided string.
76-
///
77-
/// Uses platform and version specific API to create URL file path.
78-
///
79-
/// - Parameter filePath: The path to file as string.
80-
/// - Returns: The file path URL.
81-
static func url(forFilePath filePath: String) -> URL {
82-
#if canImport(Darwin)
83-
if #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) {
84-
return URL(filePath: filePath)
85-
} else {
86-
return URL(fileURLWithPath: filePath)
87-
}
88-
#else
89-
return URL(fileURLWithPath: filePath)
90-
#endif
91-
}
9274
}
9375

9476
extension KeyedDecodingContainerProtocol {
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import Foundation
2+
import PackagePlugin
3+
4+
#if swift(<6)
5+
extension PluginContext.Tool {
6+
/// Full path of the built or provided tool in the file system.
7+
var url: URL {
8+
return url(forFilePath: path.string)
9+
}
10+
}
11+
12+
extension PackagePlugin.File {
13+
/// The path of the file.
14+
var url: URL {
15+
return url(forFilePath: path.string)
16+
}
17+
}
18+
19+
extension PluginContext {
20+
/// The path of a writable directory into which the plugin or the build
21+
/// commands it constructs can write anything it wants. This could include
22+
/// any generated source files that should be processed further, and it
23+
/// could include any caches used by the build tool or the plugin itself.
24+
/// The plugin is in complete control of what is written under this di-
25+
/// rectory, and the contents are preserved between builds.
26+
///
27+
/// A plugin would usually create a separate subdirectory of this directory
28+
/// for each command it creates, and the command would be configured to
29+
/// write its outputs to that directory. The plugin may also create other
30+
/// directories for cache files and other file system content that either
31+
/// it or the command will need.
32+
var pluginWorkDirectoryURL: URL {
33+
return url(forFilePath: pluginWorkDirectory.string)
34+
}
35+
}
36+
37+
extension Command {
38+
/// Returns a command that runs when any of its output files are needed by
39+
/// the build, but out-of-date.
40+
///
41+
/// An output file is out-of-date if it doesn't exist, or if any input files
42+
/// have changed since the command was last run.
43+
///
44+
/// - Note: the paths in the list of output files may depend on the list of
45+
/// input file paths, but **must not** depend on reading the contents of
46+
/// any input files. Such cases must be handled using a `prebuildCommand`.
47+
///
48+
/// - parameters:
49+
/// - displayName: An optional string to show in build logs and other
50+
/// status areas.
51+
/// - executable: The absolute path to the executable to be invoked.
52+
/// - arguments: Command-line arguments to be passed to the executable.
53+
/// - environment: Environment variable assignments visible to the
54+
/// executable.
55+
/// - inputFiles: Files on which the contents of output files may depend.
56+
/// Any paths passed as `arguments` should typically be passed here as
57+
/// well.
58+
/// - outputFiles: Files to be generated or updated by the executable.
59+
/// Any files recognizable by their extension as source files
60+
/// (e.g. `.swift`) are compiled into the target for which this command
61+
/// was generated as if in its source directory; other files are treated
62+
/// as resources as if explicitly listed in `Package.swift` using
63+
/// `.process(...)`.
64+
func buildCommand(
65+
displayName: String?, executable: URL, arguments: [String],
66+
environment: [String : String] = [:], inputFiles: [URL] = [],
67+
outputFiles: [URL] = []
68+
) -> Self {
69+
return .buildCommand(
70+
displayName: displayName,
71+
executable: .init(path: filePath(forURL: executable)),
72+
arguments: arguments,
73+
environment: environment,
74+
inputFiles: inputFiles.map { .init(path: filePath(forURL: $0)) },
75+
outputFiles: outputFiles.map { .init(path: filePath(forURL: $0)) }
76+
)
77+
}
78+
}
79+
80+
#if canImport(XcodeProjectPlugin)
81+
extension XcodePluginContext {
82+
/// The path of a writable directory into which the plugin or the build
83+
/// commands it constructs can write anything it wants. This could include
84+
/// any generated source files that should be processed further, and it
85+
/// could include any caches used by the build tool or the plugin itself.
86+
/// The plugin is in complete control of what is written under this di-
87+
/// rectory, and the contents are preserved between builds.
88+
///
89+
/// A plugin would usually create a separate subdirectory of this directory
90+
/// for each command it creates, and the command would be configured to
91+
/// write its outputs to that directory. The plugin may also create other
92+
/// directories for cache files and other file system content that either
93+
/// it or the command will need.
94+
var pluginWorkDirectoryURL: URL {
95+
return url(forFilePath: pluginWorkDirectory.string)
96+
}
97+
}
98+
#endif
99+
#endif
100+
101+
extension SourceModuleTarget {
102+
/// The absolute path of the target directory in the local file system.
103+
var directoryURL: URL {
104+
#if swift(<6)
105+
return url(forFilePath: directory.string)
106+
#else
107+
switch self {
108+
case let target as ClangSourceModuleTarget:
109+
return target.directoryURL
110+
case let target as SwiftSourceModuleTarget:
111+
return target.directoryURL
112+
default:
113+
fatalError("Unsupported target type")
114+
}
115+
#endif
116+
}
117+
}
118+
119+
/// Returns file path as URL converting provided string.
120+
///
121+
/// Uses platform and version specific API to create URL file path.
122+
///
123+
/// - Parameter filePath: The path to file as string.
124+
/// - Returns: The file path URL.
125+
private func url(forFilePath filePath: String) -> URL {
126+
#if canImport(Darwin)
127+
if #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) {
128+
return URL(filePath: filePath)
129+
} else {
130+
return URL(fileURLWithPath: filePath)
131+
}
132+
#else
133+
return URL(fileURLWithPath: filePath)
134+
#endif
135+
}
136+
137+
/// Returns file path as string converting provided URL.
138+
///
139+
/// Uses platform and version specific API to create string file path.
140+
///
141+
/// - Parameter url: The path to file as URL.
142+
/// - Returns: The file path string.
143+
func filePath(forURL url: URL) -> String {
144+
#if canImport(Darwin)
145+
if #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) {
146+
return url.path(percentEncoded: false)
147+
} else {
148+
return url.path
149+
}
150+
#else
151+
return url.path
152+
#endif
153+
}
154+
155+
/// Returns a URL by appending the specified path to the URL.
156+
///
157+
/// This method doesn’t percent-encode any path separators.
158+
///
159+
/// - Parameters:
160+
/// - path: The path component to append to the URL.
161+
/// - url: The base URL to which the path will be appended.
162+
/// - Returns: A new URL with the path component appended.
163+
func appending(path: String, to url: URL) -> URL {
164+
#if canImport(Darwin)
165+
if #available(macOS 13, iOS 16, macCatalyst 16, tvOS 16, watchOS 9, *) {
166+
return url.appending(path: path)
167+
} else {
168+
return url.appendingPathComponent(path)
169+
}
170+
#else
171+
return url.appendingPathComponent(path)
172+
#endif
173+
}

Plugins/MetaProtocolCodable/Plugin.swift

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,8 @@ struct MetaProtocolCodable: BuildToolPlugin {
2020
func fetchConfig<Target: MetaProtocolCodableSourceTarget>(
2121
for target: Target
2222
) throws -> Config {
23-
let pathStr = try target.configPath(named: "metacodableconfig")
24-
guard let pathStr else { return .init(scan: .target) }
25-
let path = Config.url(forFilePath: pathStr)
23+
let path = try target.configPath(named: "metacodableconfig")
24+
guard let path = path else { return .init(scan: .target) }
2625
let conf = try Data(contentsOf: path)
2726
let pConf = try? PropertyListDecoder().decode(Config.self, from: conf)
2827
let config = try pConf ?? JSONDecoder().decode(Config.self, from: conf)
@@ -50,32 +49,32 @@ struct MetaProtocolCodable: BuildToolPlugin {
5049
let (allTargets, imports) = config.scanInput(for: target, in: context)
5150

5251
// Setup folder
53-
let genFolder = context.pluginWorkDirectory.appending(["ProtocolGen"])
52+
let genFolder = appending(path: "ProtocolGen", to: context.pluginWorkDirectoryURL)
5453
try FileManager.default.createDirectory(
55-
atPath: genFolder.string, withIntermediateDirectories: true
54+
at: genFolder, withIntermediateDirectories: true
5655
)
5756

5857
// Create source scan commands
59-
var intermFiles: [Path] = []
58+
var intermFiles: [URL] = []
6059
var buildCommands = allTargets.flatMap { target in
6160
return target.sourceFiles(withSuffix: "swift").map { file in
6261
let moduleName = target.moduleName
63-
let fileName = file.path.stem
62+
let fileName = file.url.deletingPathExtension().lastPathComponent
6463
let genFileName = "\(moduleName)-\(fileName)-gen.json"
65-
let genFile = genFolder.appending([genFileName])
64+
let genFile = appending(path: genFileName, to: genFolder)
6665
intermFiles.append(genFile)
6766
return Command.buildCommand(
6867
displayName: """
6968
Parse source file "\(fileName)" in module "\(moduleName)"
7069
""",
71-
executable: tool.path,
70+
executable: tool.url,
7271
arguments: [
7372
"parse",
74-
file.path.string,
73+
filePath(forURL: file.url),
7574
"--output",
76-
genFile.string,
75+
filePath(forURL: genFile),
7776
],
78-
inputFiles: [file.path],
77+
inputFiles: [file.url],
7978
outputFiles: [genFile]
8079
)
8180
}
@@ -84,20 +83,20 @@ struct MetaProtocolCodable: BuildToolPlugin {
8483
// Create syntax generation command
8584
let moduleName = target.moduleName
8685
let genFileName = "\(moduleName)+ProtocolHelperCoders.swift"
87-
let genPath = genFolder.appending(genFileName)
88-
var genArgs = ["generate", "--output", genPath.string]
86+
let genPath = appending(path: genFileName, to: genFolder)
87+
var genArgs = ["generate", "--output", filePath(forURL: genPath)]
8988
for `import` in imports {
9089
genArgs.append(contentsOf: ["--module", `import`])
9190
}
9291
for file in intermFiles {
93-
genArgs.append(file.string)
92+
genArgs.append(filePath(forURL: file))
9493
}
9594
buildCommands.append(
9695
.buildCommand(
9796
displayName: """
9897
Generate protocol decoding/encoding syntax for "\(moduleName)"
9998
""",
100-
executable: tool.path,
99+
executable: tool.url,
101100
arguments: genArgs,
102101
inputFiles: intermFiles,
103102
outputFiles: [genPath]

Plugins/MetaProtocolCodable/PluginContext.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Foundation
12
import PackagePlugin
23

34
/// Provides information about the package for which the plugin is invoked,
@@ -23,7 +24,7 @@ protocol MetaProtocolCodablePluginContext {
2324
/// write its outputs to that directory. The plugin may also create other
2425
/// directories for cache files and other file system content that either
2526
/// it or the command will need.
26-
var pluginWorkDirectory: Path { get }
27+
var pluginWorkDirectoryURL: URL { get }
2728
/// The targets which are local to current context.
2829
///
2930
/// These targets are included in the same package/project as this context.

Plugins/MetaProtocolCodable/SourceTarget/MetaProtocolCodableSourceTarget.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Foundation
12
import PackagePlugin
23

34
/// Represents a target consisting of a source code module,
@@ -42,7 +43,7 @@ protocol MetaProtocolCodableSourceTarget {
4243
///
4344
/// - Parameter name: The config file name.
4445
/// - Returns: The config file path.
45-
func configPath(named name: String) throws -> String?
46+
func configPath(named name: String) throws -> URL?
4647
}
4748

4849
extension Config {

Plugins/MetaProtocolCodable/SourceTarget/SwiftPackageTarget.swift

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,18 @@ extension SwiftPackageTarget: MetaProtocolCodableSourceTarget {
6262
///
6363
/// - Parameter name: The config file name.
6464
/// - Returns: The config file path.
65-
func configPath(named name: String) throws -> String? {
65+
func configPath(named name: String) throws -> URL? {
6666
let fileManager = FileManager.default
67-
let directory = module.directory.string
68-
let contents = try fileManager.contentsOfDirectory(atPath: directory)
69-
let file = contents.first { file in
70-
let path = Path(file)
67+
let directory = module.directoryURL
68+
let contents = try fileManager.contentsOfDirectory(
69+
at: directory, includingPropertiesForKeys: nil
70+
)
71+
return contents.first { file in
7172
return name.lowercased()
72-
== path.stem
73+
== file.deletingPathExtension().lastPathComponent
7374
.components(separatedBy: .alphanumerics.inverted)
7475
.joined(separator: "")
7576
.lowercased()
7677
}
77-
guard let file else { return nil }
78-
return module.directory.appending([file]).string
7978
}
8079
}

Plugins/MetaProtocolCodable/SourceTarget/XcodeTarget.swift

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#if canImport(XcodeProjectPlugin)
2+
import Foundation
23
import PackagePlugin
34
import XcodeProjectPlugin
45

@@ -66,23 +67,14 @@ extension XcodeTarget: MetaProtocolCodableSourceTarget {
6667
///
6768
/// - Parameter name: The config file name.
6869
/// - Returns: The config file path.
69-
func configPath(named name: String) -> String? {
70-
let file = inputFiles.first { file in
71-
#if swift(>=6)
72-
let path = file.url.lastPathComponent
73-
#else
74-
let path = file.path.stem
75-
#endif
76-
let fileName = path.components(separatedBy: .alphanumerics.inverted)
70+
func configPath(named name: String) -> URL? {
71+
return inputFiles.first { file in
72+
return name.lowercased()
73+
== file.url.deletingPathExtension().lastPathComponent
74+
.components(separatedBy: .alphanumerics.inverted)
7775
.joined(separator: "")
7876
.lowercased()
79-
return name.lowercased() == fileName
80-
}
81-
#if swift(>=6)
82-
return file?.url.lastPathComponent
83-
#else
84-
return file?.path.stem
85-
#endif
77+
}?.url
8678
}
8779
}
8880
#endif

0 commit comments

Comments
 (0)