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,195 +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 dependenciesClasspath = await resolveDependencies ( dependencies: dependencies, repositories: repositories)
121- let classpathEntries = dependenciesClasspath. split ( separator: " : " )
122-
123- print ( " [info][swift-java] Resolved classpath for \( deps. count) dependencies of ' \( swiftModule) ', classpath entries: \( classpathEntries. count) , " , terminator: " " )
124- print ( " done. " . green)
125-
126- for entry in classpathEntries {
127- print ( " [info][swift-java] Classpath entry: \( entry) " )
128- }
129-
130- return ResolvedDependencyClasspath ( for: dependencies, classpath: dependenciesClasspath)
131- }
132-
133-
134- /// Resolves maven-style dependencies from swift-java.config under temporary project directory.
135- ///
136- /// - Parameter dependencies: maven-style dependencies to resolve
137- /// - Parameter repositories: repositories used to resolve dependencies
138- /// - Returns: Colon-separated classpath
139- func resolveDependencies( dependencies: [ JavaDependencyDescriptor ] , repositories: [ JavaRepositoryDescriptor ] ) async -> String {
140- let workDir = URL ( fileURLWithPath: FileManager . default. currentDirectoryPath)
141- . appendingPathComponent ( " .build " )
142- let resolverDir = try ! createTemporaryDirectory ( in: workDir)
143- defer {
144- try ? FileManager . default. removeItem ( at: resolverDir)
145- }
146-
147- // We try! because it's easier to track down errors like this than when we bubble up the errors,
148- // and don't get great diagnostics or backtraces due to how swiftpm plugin tools are executed.
149-
150- try ! copyGradlew ( to: resolverDir)
151-
152- try ! printGradleProject ( directory: resolverDir, dependencies: dependencies, repositories: repositories)
153-
154- if #available( macOS 15 , * ) {
155- let process = try ! await _Subprocess. run (
156- . path( FilePath ( resolverDir. appendingPathComponent ( " gradlew " ) . path) ) ,
157- arguments: [
158- " --no-daemon " ,
159- " --rerun-tasks " ,
160- " \( printRuntimeClasspathTaskName) " ,
161- ] ,
162- workingDirectory: Optional ( FilePath ( resolverDir. path) ) ,
163- // TODO: we could move to stream processing the outputs
164- output: . string( limit: Int . max, encoding: UTF8 . self) , // Don't limit output, we know it will be reasonable size
165- error: . string( limit: Int . max, encoding: UTF8 . self) // Don't limit output, we know it will be reasonable size
166- )
167-
168- let outString = process. standardOutput ?? " "
169- let errString = process. standardError ?? " "
170-
171- let classpathOutput : String
172- if let found = outString. split ( separator: " \n " ) . first ( where: { $0. hasPrefix ( self . SwiftJavaClasspathPrefix) } ) {
173- classpathOutput = String ( found)
174- } else if let found = errString. split ( separator: " \n " ) . first ( where: { $0. hasPrefix ( self . SwiftJavaClasspathPrefix) } ) {
175- classpathOutput = String ( found)
176- } else {
177- let suggestDisablingSandbox = " It may be that the Sandbox has prevented dependency fetching, please re-run with '--disable-sandbox'. "
178- fatalError ( " Gradle output had no SWIFT_JAVA_CLASSPATH! \( suggestDisablingSandbox) . \n " +
179- " Output was:<<< \( outString) >>>; Err was:<<< \( errString ?? " <empty> " ) >>> " )
180- }
181-
182- return String ( classpathOutput. dropFirst ( SwiftJavaClasspathPrefix . count) )
183- } else {
184- // Subprocess is unavailable
185- fatalError ( " Subprocess is unavailable yet required to execute `gradlew` subprocess. Please update to macOS 15+ " )
186- }
187- }
188-
189- /// Creates Gradle project files (build.gradle, settings.gradle.kts) in temporary directory.
190- func printGradleProject( directory: URL , dependencies: [ JavaDependencyDescriptor ] , repositories: [ JavaRepositoryDescriptor ] ) throws {
191- let buildGradle = directory
192- . appendingPathComponent ( " build.gradle " , isDirectory: false )
193-
194- let buildGradleText =
195- """
196- plugins { id 'java-library' }
197- repositories {
198- \( repositories. compactMap ( { $0. renderGradleRepository ( ) } ) . joined ( separator: " \n " ) )
199- }
200-
201- dependencies {
202- \( dependencies. map ( { dep in " implementation( \" \( dep. descriptionGradleStyle) \" ) " } ) . joined ( separator: " , \n " ) )
203- }
204-
205- tasks.register( " printRuntimeClasspath " ) {
206- def runtimeClasspath = sourceSets.main.runtimeClasspath
207- inputs.files(runtimeClasspath)
208- doLast {
209- println( " \( SwiftJavaClasspathPrefix) ${runtimeClasspath.asPath} " )
210- }
211- }
212- """
213- try buildGradleText. write ( to: buildGradle, atomically: true , encoding: . utf8)
214-
215- let settingsGradle = directory
216- . appendingPathComponent ( " settings.gradle.kts " , isDirectory: false )
217- let settingsGradleText =
218- """
219- rootProject.name = " swift-java-resolve-temp-project "
220- """
221- try settingsGradleText. write ( to: settingsGradle, atomically: true , encoding: . utf8)
222- }
223-
224- /// Creates {MySwiftModule}.swift.classpath in the --output-directory.
225- ///
226- /// - Parameters:
227- /// - swiftModule: Swift module name for classpath filename (--swift-module value)
228- /// - outputDirectory: Directory path for classpath file (--output-directory value)
229- /// - resolvedClasspath: Complete dependency classpath information
230- ///
231- mutating func writeSwiftJavaClasspathFile(
232- swiftModule: String ,
233- outputDirectory: String ,
234- resolvedClasspath: ResolvedDependencyClasspath ) throws {
235- // Convert the artifact name to a module name
236- // e.g. reactive-streams -> ReactiveStreams
237-
238- // The file contents are just plain
239- let contents = resolvedClasspath. classpath
240-
241- let filename = " \( swiftModule) .swift-java.classpath "
242- print ( " [debug][swift-java] Write resolved dependencies to: \( outputDirectory) / \( filename) " )
243-
244- // Write the file
245- try writeContents (
246- contents,
247- outputDirectory: URL ( fileURLWithPath: outputDirectory) ,
248- to: filename,
249- description: " swift-java.classpath file for module \( swiftModule) "
56+ outputDirectory: commonOptions. outputDirectory
25057 )
25158 }
25259
@@ -255,74 +62,5 @@ extension SwiftJava.ResolveCommand {
25562 let camelCased = components. map { $0. capitalized } . joined ( )
25663 return camelCased
25764 }
258-
259- // copy gradlew & gradle.bat from root, throws error if there is no gradle setup.
260- func copyGradlew( to resolverWorkDirectory: URL ) throws {
261- var searchDir = URL ( fileURLWithPath: FileManager . default. currentDirectoryPath)
262-
263- while searchDir. pathComponents. count > 1 {
264- let gradlewFile = searchDir. appendingPathComponent ( " gradlew " )
265- let gradlewExists = FileManager . default. fileExists ( atPath: gradlewFile. path)
266- guard gradlewExists else {
267- searchDir = searchDir. deletingLastPathComponent ( )
268- continue
269- }
270-
271- let gradlewBatFile = searchDir. appendingPathComponent ( " gradlew.bat " )
272- let gradlewBatExists = FileManager . default. fileExists ( atPath: gradlewFile. path)
273-
274- let gradleDir = searchDir. appendingPathComponent ( " gradle " )
275- let gradleDirExists = FileManager . default. fileExists ( atPath: gradleDir. path)
276- guard gradleDirExists else {
277- searchDir = searchDir. deletingLastPathComponent ( )
278- continue
279- }
280-
281- // TODO: gradle.bat as well
282- try ? FileManager . default. copyItem (
283- at: gradlewFile,
284- to: resolverWorkDirectory. appendingPathComponent ( " gradlew " ) )
285- if gradlewBatExists {
286- try ? FileManager . default. copyItem (
287- at: gradlewBatFile,
288- to: resolverWorkDirectory. appendingPathComponent ( " gradlew.bat " ) )
289- }
290- try ? FileManager . default. copyItem (
291- at: gradleDir,
292- to: resolverWorkDirectory. appendingPathComponent ( " gradle " ) )
293- return
294- }
295- }
296-
297- func createTemporaryDirectory( in directory: URL ) throws -> URL {
298- let uuid = UUID ( ) . uuidString
299- let resolverDirectoryURL = directory. appendingPathComponent ( " swift-java-dependencies- \( uuid) " )
300-
301- try FileManager . default. createDirectory ( at: resolverDirectoryURL, withIntermediateDirectories: true , attributes: nil )
302-
303- return resolverDirectoryURL
304- }
305-
306- }
307-
308- struct ResolvedDependencyClasspath : CustomStringConvertible {
309- /// The dependency identifiers this is the classpath for.
310- let rootDependencies : [ JavaDependencyDescriptor ]
311-
312- /// Plain string representation of a Java classpath
313- let classpath : String
314-
315- var classpathEntries : [ String ] {
316- classpath. split ( separator: " : " ) . map ( String . init)
317- }
318-
319- init ( for rootDependencies: [ JavaDependencyDescriptor ] , classpath: String ) {
320- self . rootDependencies = rootDependencies
321- self . classpath = classpath
322- }
323-
324- var description : String {
325- " JavaClasspath(for: \( rootDependencies) , classpath: \( classpath) ) "
326- }
32765}
32866
0 commit comments