1515import ArgumentParser
1616import Foundation
1717import SwiftJavaToolLib
18- import SwiftJava
19- import Foundation
20- import JavaUtilJar
21- import SwiftJavaToolLib
2218import SwiftJavaConfigurationShared
23- import SwiftJavaShared
24- import _Subprocess
25- #if canImport(System)
26- import System
27- #else
28- @preconcurrency import SystemPackage
29- #endif
3019
3120typealias Configuration = SwiftJavaConfigurationShared . Configuration
3221
@@ -58,205 +47,13 @@ extension SwiftJava {
5847}
5948
6049extension SwiftJava . ResolveCommand {
61- var SwiftJavaClasspathPrefix : String { " SWIFT_JAVA_CLASSPATH: " }
62- var printRuntimeClasspathTaskName : String { " printRuntimeClasspath " }
6350
6451 mutating func runSwiftJavaCommand( config: inout Configuration ) async throws {
65- var dependenciesToResolve : [ JavaDependencyDescriptor ] = [ ]
66- if let input, let inputDependencies = parseDependencyDescriptor ( input) {
67- dependenciesToResolve. append ( inputDependencies)
68- }
69- if let dependencies = config. dependencies {
70- dependenciesToResolve += dependencies
71- }
72-
73- if dependenciesToResolve. isEmpty {
74- print ( " [warn][swift-java] Attempted to 'resolve' dependencies but no dependencies specified in swift-java.config or command input! " )
75- return
76- }
77-
78- var configuredRepositories : [ JavaRepositoryDescriptor ] = [ ]
79-
80- if let repositories = config. repositories {
81- configuredRepositories += repositories
82- }
83-
84- if !configuredRepositories. contains ( where: { $0 == . other( " mavenCentral " ) } ) {
85- // swift-java dependencies are originally located in mavenCentral
86- configuredRepositories. append ( . other( " mavenCentral " ) )
87- }
88-
89- let dependenciesClasspath =
90- try await resolveDependencies ( swiftModule: swiftModule, dependencies: dependenciesToResolve, repositories: configuredRepositories)
91-
92- // FIXME: disentangle the output directory from SwiftJava and then make it a required option in this Command
93- guard let outputDirectory = self . commonOptions. outputDirectory else {
94- fatalError ( " error: Must specify --output-directory in 'resolve' mode! This option will become explicitly required " )
95- }
96-
97- try writeSwiftJavaClasspathFile (
52+ try await JavaResolver . runResolveCommand (
53+ config: & config,
54+ input: input,
9855 swiftModule: swiftModule,
99- outputDirectory: outputDirectory,
100- resolvedClasspath: dependenciesClasspath)
101- }
102-
103-
104- /// Resolves Java dependencies from swift-java.config and returns classpath information.
105- ///
106- /// - Parameters:
107- /// - swiftModule: module name from --swift-module. e.g.: --swift-module MySwiftModule
108- /// - dependencies: parsed maven-style dependency descriptors (groupId:artifactId:version)
109- /// from Sources/MySwiftModule/swift-java.config "dependencies" array.
110- /// - repositories: repositories used to resolve dependencies
111- ///
112- /// - Throws:
113- func resolveDependencies(
114- swiftModule: String , dependencies: [ JavaDependencyDescriptor ] ,
115- repositories: [ JavaRepositoryDescriptor ]
116- ) async throws -> ResolvedDependencyClasspath {
117- let deps = dependencies. map { $0. descriptionGradleStyle }
118- print ( " [debug][swift-java] Resolve and fetch dependencies for: \( deps) " )
119-
120- let workDir = URL ( fileURLWithPath: FileManager . default. currentDirectoryPath)
121- . appendingPathComponent ( " .build " )
122-
123- let dependenciesClasspath = await resolveDependencies ( dependencies: dependencies, repositories: repositories)
124- let classpathEntries = dependenciesClasspath. split ( separator: " : " )
125-
126- print ( " [info][swift-java] Resolved classpath for \( deps. count) dependencies of ' \( swiftModule) ', classpath entries: \( classpathEntries. count) , " , terminator: " " )
127- print ( " done. " . green)
128-
129- for entry in classpathEntries {
130- print ( " [info][swift-java] Classpath entry: \( entry) " )
131- }
132-
133- return ResolvedDependencyClasspath ( for: dependencies, classpath: dependenciesClasspath)
134- }
135-
136-
137- /// Resolves maven-style dependencies from swift-java.config under temporary project directory.
138- ///
139- /// - Parameter dependencies: maven-style dependencies to resolve
140- /// - Parameter repositories: repositories used to resolve dependencies
141- /// - Returns: Colon-separated classpath
142- func resolveDependencies( workDir: URL , dependencies: [ JavaDependencyDescriptor ] ) async -> String {
143- print ( " Create directory: \( workDir. absoluteString) " )
144-
145- let resolverDir : URL
146- do {
147- resolverDir = try createTemporaryDirectory ( in: workDir)
148- } catch {
149- fatalError ( " Unable to create temp directory at: \( workDir. absoluteString) ! \( error) " )
150- }
151- defer {
152- try ? FileManager . default. removeItem ( at: resolverDir)
153- }
154-
155- // We try! because it's easier to track down errors like this than when we bubble up the errors,
156- // and don't get great diagnostics or backtraces due to how swiftpm plugin tools are executed.
157-
158- try ! copyGradlew ( to: resolverDir)
159-
160- try ! printGradleProject ( directory: resolverDir, dependencies: dependencies, repositories: repositories)
161-
162- if #available( macOS 15 , * ) {
163- let process = try ! await _Subprocess. run (
164- . path( FilePath ( resolverDir. appendingPathComponent ( " gradlew " ) . path) ) ,
165- arguments: [
166- " --no-daemon " ,
167- " --rerun-tasks " ,
168- " \( printRuntimeClasspathTaskName) " ,
169- ] ,
170- workingDirectory: Optional ( FilePath ( resolverDir. path) ) ,
171- // TODO: we could move to stream processing the outputs
172- output: . string( limit: Int . max, encoding: UTF8 . self) , // Don't limit output, we know it will be reasonable size
173- error: . string( limit: Int . max, encoding: UTF8 . self) // Don't limit output, we know it will be reasonable size
174- )
175-
176- let outString = process. standardOutput ?? " "
177- let errString = process. standardError ?? " "
178-
179- let classpathOutput : String
180- if let found = outString. split ( separator: " \n " ) . first ( where: { $0. hasPrefix ( self . SwiftJavaClasspathPrefix) } ) {
181- classpathOutput = String ( found)
182- } else if let found = errString. split ( separator: " \n " ) . first ( where: { $0. hasPrefix ( self . SwiftJavaClasspathPrefix) } ) {
183- classpathOutput = String ( found)
184- } else {
185- let suggestDisablingSandbox = " It may be that the Sandbox has prevented dependency fetching, please re-run with '--disable-sandbox'. "
186- fatalError ( " Gradle output had no SWIFT_JAVA_CLASSPATH! \( suggestDisablingSandbox) . \n " +
187- " Command was: \( CommandLine . arguments. joined ( separator: " " ) . bold) \n " +
188- " Output was: <<< \( outString) >>>; \n " +
189- " Err was: <<< \( errString) >>> " )
190- }
191-
192- return String ( classpathOutput. dropFirst ( SwiftJavaClasspathPrefix . count) )
193- } else {
194- // Subprocess is unavailable
195- fatalError ( " Subprocess is unavailable yet required to execute `gradlew` subprocess. Please update to macOS 15+ " )
196- }
197- }
198-
199- /// Creates Gradle project files (build.gradle, settings.gradle.kts) in temporary directory.
200- func printGradleProject( directory: URL , dependencies: [ JavaDependencyDescriptor ] , repositories: [ JavaRepositoryDescriptor ] ) throws {
201- let buildGradle = directory
202- . appendingPathComponent ( " build.gradle " , isDirectory: false )
203-
204- let buildGradleText =
205- """
206- plugins { id 'java-library' }
207- repositories {
208- \( repositories. compactMap ( { $0. renderGradleRepository ( ) } ) . joined ( separator: " \n " ) )
209- }
210-
211- dependencies {
212- \( dependencies. map ( { dep in " implementation( \" \( dep. descriptionGradleStyle) \" ) " } ) . joined ( separator: " , \n " ) )
213- }
214-
215- tasks.register( " printRuntimeClasspath " ) {
216- def runtimeClasspath = sourceSets.main.runtimeClasspath
217- inputs.files(runtimeClasspath)
218- doLast {
219- println( " \( SwiftJavaClasspathPrefix) ${runtimeClasspath.asPath} " )
220- }
221- }
222- """
223- try buildGradleText. write ( to: buildGradle, atomically: true , encoding: . utf8)
224-
225- let settingsGradle = directory
226- . appendingPathComponent ( " settings.gradle.kts " , isDirectory: false )
227- let settingsGradleText =
228- """
229- rootProject.name = " swift-java-resolve-temp-project "
230- """
231- try settingsGradleText. write ( to: settingsGradle, atomically: true , encoding: . utf8)
232- }
233-
234- /// Creates {MySwiftModule}.swift.classpath in the --output-directory.
235- ///
236- /// - Parameters:
237- /// - swiftModule: Swift module name for classpath filename (--swift-module value)
238- /// - outputDirectory: Directory path for classpath file (--output-directory value)
239- /// - resolvedClasspath: Complete dependency classpath information
240- ///
241- mutating func writeSwiftJavaClasspathFile(
242- swiftModule: String ,
243- outputDirectory: String ,
244- resolvedClasspath: ResolvedDependencyClasspath ) throws {
245- // Convert the artifact name to a module name
246- // e.g. reactive-streams -> ReactiveStreams
247-
248- // The file contents are just plain
249- let contents = resolvedClasspath. classpath
250-
251- let filename = " \( swiftModule) .swift-java.classpath "
252- print ( " [debug][swift-java] Write resolved dependencies to: \( outputDirectory) / \( filename) " )
253-
254- // Write the file
255- try writeContents (
256- contents,
257- outputDirectory: URL ( fileURLWithPath: outputDirectory) ,
258- to: filename,
259- description: " swift-java.classpath file for module \( swiftModule) "
56+ outputDirectory: commonOptions. outputDirectory
26057 )
26158 }
26259
@@ -265,74 +62,5 @@ extension SwiftJava.ResolveCommand {
26562 let camelCased = components. map { $0. capitalized } . joined ( )
26663 return camelCased
26764 }
268-
269- // copy gradlew & gradle.bat from root, throws error if there is no gradle setup.
270- func copyGradlew( to resolverWorkDirectory: URL ) throws {
271- var searchDir = URL ( fileURLWithPath: FileManager . default. currentDirectoryPath)
272-
273- while searchDir. pathComponents. count > 1 {
274- let gradlewFile = searchDir. appendingPathComponent ( " gradlew " )
275- let gradlewExists = FileManager . default. fileExists ( atPath: gradlewFile. path)
276- guard gradlewExists else {
277- searchDir = searchDir. deletingLastPathComponent ( )
278- continue
279- }
280-
281- let gradlewBatFile = searchDir. appendingPathComponent ( " gradlew.bat " )
282- let gradlewBatExists = FileManager . default. fileExists ( atPath: gradlewFile. path)
283-
284- let gradleDir = searchDir. appendingPathComponent ( " gradle " )
285- let gradleDirExists = FileManager . default. fileExists ( atPath: gradleDir. path)
286- guard gradleDirExists else {
287- searchDir = searchDir. deletingLastPathComponent ( )
288- continue
289- }
290-
291- // TODO: gradle.bat as well
292- try ? FileManager . default. copyItem (
293- at: gradlewFile,
294- to: resolverWorkDirectory. appendingPathComponent ( " gradlew " ) )
295- if gradlewBatExists {
296- try ? FileManager . default. copyItem (
297- at: gradlewBatFile,
298- to: resolverWorkDirectory. appendingPathComponent ( " gradlew.bat " ) )
299- }
300- try ? FileManager . default. copyItem (
301- at: gradleDir,
302- to: resolverWorkDirectory. appendingPathComponent ( " gradle " ) )
303- return
304- }
305- }
306-
307- func createTemporaryDirectory( in directory: URL ) throws -> URL {
308- let uuid = UUID ( ) . uuidString
309- let resolverDirectoryURL = directory. appendingPathComponent ( " swift-java-dependencies- \( uuid) " )
310-
311- try FileManager . default. createDirectory ( at: resolverDirectoryURL, withIntermediateDirectories: true , attributes: nil )
312-
313- return resolverDirectoryURL
314- }
315-
316- }
317-
318- struct ResolvedDependencyClasspath : CustomStringConvertible {
319- /// The dependency identifiers this is the classpath for.
320- let rootDependencies : [ JavaDependencyDescriptor ]
321-
322- /// Plain string representation of a Java classpath
323- let classpath : String
324-
325- var classpathEntries : [ String ] {
326- classpath. split ( separator: " : " ) . map ( String . init)
327- }
328-
329- init ( for rootDependencies: [ JavaDependencyDescriptor ] , classpath: String ) {
330- self . rootDependencies = rootDependencies
331- self . classpath = classpath
332- }
333-
334- var description : String {
335- " JavaClasspath(for: \( rootDependencies) , classpath: \( classpath) ) "
336- }
33765}
33866
0 commit comments