Skip to content

Commit 3e7ae28

Browse files
authored
Merge pull request #198 from ktoso/wip-attempt-build-plugin-bootstrap
2 parents 207d071 + f156619 commit 3e7ae28

26 files changed

+6072
-23
lines changed

.github/workflows/pull_request.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ jobs:
6060
- name: Prepare CI Environment
6161
uses: ./.github/actions/prepare_env
6262
- name: Swift Build
63-
run: "swift build --build-tests"
63+
run: "swift build --build-tests --disable-sandbox"
6464
- name: Swift Test
6565
run: "swift test"
6666

.licenseignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Package.resolved
1414
README.md
1515
SECURITY.md
1616
scripts/unacceptable-language.txt
17+
.unacceptablelanguageignore
1718
docker/*
1819
**/*.docc/*
1920
**/.gitignore
@@ -43,4 +44,5 @@ gradlew.bat
4344
**/DO_NOT_EDIT.txt
4445
Plugins/**/_PluginsShared
4546
Plugins/**/0_PLEASE_SYMLINK*
46-
Plugins/PluginsShared/JavaKitConfigurationShared
47+
Plugins/PluginsShared/JavaKitConfigurationShared
48+
Sources/_Subprocess/_nio_locks.swift

.unacceptablelanguageignore

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Sources/SwiftJavaBootstrapJavaTool/SwiftJavaBootstrapJavaTool.swift
2+
Sources/_Subprocess/Platforms/Subprocess+Darwin.swift
3+
Sources/_Subprocess/Platforms/Subprocess+Linux.swift
4+
Sources/_Subprocess/Platforms/Subprocess+Unix.swift
5+
Sources/_Subprocess/Platforms/Subprocess+Unix.swift
6+
Sources/_Subprocess/Platforms/Subprocess+Unix.swift
7+
Sources/_Subprocess/Platforms/Subprocess+Unix.swift
8+
Sources/_Subprocess/Platforms/Subprocess+Unix.swift
9+
Sources/_Subprocess/Platforms/Subprocess+Unix.swift
10+
Sources/_Subprocess/Subprocess+Teardown.swift
11+
Sources/_Subprocess/Subprocess+Teardown.swift
12+
Sources/_Subprocess/Subprocess+Teardown.swift
13+
Sources/_Subprocess/Subprocess+Teardown.swift
14+
Sources/_Subprocess/Subprocess+Teardown.swift
15+
Sources/_Subprocess/Subprocess+Teardown.swift
16+
Sources/_Subprocess/Subprocess+Teardown.swift
17+
Sources/_Subprocess/Subprocess.swift

JavaKit/build.gradle

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,13 @@ tasks.processResources {
6060
}
6161
}
6262

63-
//task fatJar(type: Jar) {
64-
// archiveBaseName = 'java-kit-fat-jar'
65-
// duplicatesStrategy = DuplicatesStrategy.EXCLUDE
66-
// from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
67-
// with jar
68-
//}
63+
// Task necessary to bootstrap and EXIT, this is to prevent hangs when used via SwiftPM plugin.
64+
tasks.register('printRuntimeClasspath') {
65+
dependsOn 'jar'
6966

70-
// Task necessary to bootstrap
71-
task printRuntimeClasspath {
72-
def runtimeClasspath = sourceSets.main.runtimeClasspath
73-
inputs.files(runtimeClasspath)
74-
doLast {
75-
println("CLASSPATH:${runtimeClasspath.asPath}")
76-
}
67+
def runtimeClasspath = sourceSets.main.runtimeClasspath
68+
inputs.files(runtimeClasspath)
69+
doLast {
70+
println("SWIFT_JAVA_CLASSPATH:${runtimeClasspath.asPath}")
71+
}
7772
}

JavaKit/src/main/java/org/swift/javakit/dependencies/DependencyResolver.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
@SuppressWarnings("unused")
3535
public class DependencyResolver {
3636

37-
private static final String COMMAND_OUTPUT_LINE_PREFIX_CLASSPATH = "CLASSPATH:";
37+
private static final String COMMAND_OUTPUT_LINE_PREFIX_CLASSPATH = "SWIFT_JAVA_CLASSPATH:";
3838
private static final String CLASSPATH_CACHE_FILENAME = "JavaKitDependencyResolver.swift-java.classpath";
3939

4040
public static String GRADLE_API_DEPENDENCY = "dev.gradleplugins:gradle-api:8.10.1";
@@ -236,11 +236,11 @@ private static void printBuildFiles(File projectDir, String[] dependencies) thro
236236
writer.println("}");
237237

238238
writer.println("""
239-
task printRuntimeClasspath {
239+
tasks.register("printRuntimeClasspath") {
240240
def runtimeClasspath = sourceSets.main.runtimeClasspath
241241
inputs.files(runtimeClasspath)
242242
doLast {
243-
println("CLASSPATH:${runtimeClasspath.asPath}")
243+
println("SWIFT_JAVA_CLASSPATH:${runtimeClasspath.asPath}")
244244
}
245245
}
246246
""");

Package.swift

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ let package = Package(
139139
"JExtractSwiftCommandPlugin"
140140
]
141141
),
142-
142+
143143
// ==== Examples
144144

145145
.library(
@@ -152,6 +152,9 @@ let package = Package(
152152
dependencies: [
153153
.package(url: "https://github.com/swiftlang/swift-syntax", from: "600.0.1"),
154154
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.5.0"),
155+
.package(url: "https://github.com/apple/swift-system", from: "1.4.0"),
156+
157+
// Benchmarking
155158
.package(url: "https://github.com/ordo-one/package-benchmark", .upToNextMajor(from: "1.4.0")),
156159
],
157160
targets: [
@@ -184,6 +187,11 @@ let package = Package(
184187
.swiftLanguageMode(.v5),
185188
.unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"]),
186189
]
190+
// // FIXME: when the tool is run from plugin it hangs even if sandbox is disabled
191+
// ,
192+
// plugins: [
193+
// "SwiftJavaBootstrapJavaPlugin",
194+
// ]
187195
),
188196

189197
.target(
@@ -211,7 +219,7 @@ let package = Package(
211219
.unsafeFlags(
212220
[
213221
"-L\(javaHome)/lib"
214-
],
222+
],
215223
.when(platforms: [.windows])),
216224
.linkedLibrary("jvm"),
217225
]
@@ -378,6 +386,25 @@ let package = Package(
378386
]
379387
),
380388

389+
.executableTarget(
390+
name: "SwiftJavaBootstrapJavaTool",
391+
dependencies: [
392+
"JavaKitConfigurationShared", // for Configuration reading at runtime
393+
"_Subprocess",
394+
],
395+
swiftSettings: [
396+
.swiftLanguageMode(.v5)
397+
]
398+
),
399+
400+
.plugin(
401+
name: "SwiftJavaBootstrapJavaPlugin",
402+
capability: .buildTool(),
403+
dependencies: [
404+
"SwiftJavaBootstrapJavaTool"
405+
]
406+
),
407+
381408
.plugin(
382409
name: "SwiftJavaPlugin",
383410
capability: .buildTool(),
@@ -442,6 +469,24 @@ let package = Package(
442469
.swiftLanguageMode(.v5),
443470
.unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"])
444471
]
472+
),
473+
474+
// Experimental Foundation Subprocess Copy
475+
.target(
476+
name: "_CShims",
477+
swiftSettings: [
478+
.swiftLanguageMode(.v5)
479+
]
480+
),
481+
.target(
482+
name: "_Subprocess",
483+
dependencies: [
484+
"_CShims",
485+
.product(name: "SystemPackage", package: "swift-system"),
486+
],
487+
swiftSettings: [
488+
.swiftLanguageMode(.v5)
489+
]
445490
)
446491
]
447492
)
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import Foundation
16+
import PackagePlugin
17+
18+
fileprivate let SwiftJavaConfigFileName = "swift-java.config"
19+
20+
@main
21+
struct Java2SwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin {
22+
23+
var pluginName: String = "swift-java-bootstrap"
24+
var verbose: Bool = getEnvironmentBool("SWIFT_JAVA_VERBOSE")
25+
26+
func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] {
27+
log("Create build commands for target '\(target.name)'")
28+
guard let sourceModule = target.sourceModule else { return [] }
29+
30+
let executable = try context.tool(named: "SwiftJavaBootstrapJavaTool").url
31+
var commands: [Command] = []
32+
33+
// Note: Target doesn't have a directoryURL counterpart to directory,
34+
// so we cannot eliminate this deprecation warning.
35+
let sourceDir = target.directory.string
36+
37+
// The name of the configuration file JavaKit.config from the target for
38+
// which we are generating Swift wrappers for Java classes.
39+
let configFile = URL(filePath: sourceDir)
40+
.appending(path: SwiftJavaConfigFileName)
41+
let config = try readConfiguration(sourceDir: sourceDir)
42+
43+
log("Config on path: \(configFile.path(percentEncoded: false))")
44+
log("Config was: \(config)")
45+
var javaDependencies = config.dependencies ?? []
46+
47+
/// Find the manifest files from other Java2Swift executions in any targets
48+
/// this target depends on.
49+
var dependentConfigFiles: [(String, URL)] = []
50+
func searchForConfigFiles(in target: any Target) {
51+
// log("Search for config files in target: \(target.name)")
52+
let dependencyURL = URL(filePath: target.directory.string)
53+
54+
// Look for a config file within this target.
55+
let dependencyConfigURL = dependencyURL
56+
.appending(path: SwiftJavaConfigFileName)
57+
let dependencyConfigString = dependencyConfigURL
58+
.path(percentEncoded: false)
59+
60+
if FileManager.default.fileExists(atPath: dependencyConfigString) {
61+
dependentConfigFiles.append((target.name, dependencyConfigURL))
62+
}
63+
}
64+
65+
// Process direct dependencies of this target.
66+
for dependency in target.dependencies {
67+
switch dependency {
68+
case .target(let target):
69+
searchForConfigFiles(in: target)
70+
71+
case .product(let product):
72+
for target in product.targets {
73+
searchForConfigFiles(in: target)
74+
}
75+
76+
@unknown default:
77+
break
78+
}
79+
}
80+
81+
// Process indirect target dependencies.
82+
for dependency in target.recursiveTargetDependencies {
83+
searchForConfigFiles(in: dependency)
84+
}
85+
86+
var arguments: [String] = []
87+
arguments += argumentsModuleName(sourceModule: sourceModule)
88+
arguments += argumentsOutputDirectory(context: context)
89+
90+
arguments += dependentConfigFiles.flatMap { moduleAndConfigFile in
91+
let (moduleName, configFile) = moduleAndConfigFile
92+
return [
93+
"--depends-on",
94+
"\(moduleName)=\(configFile.path(percentEncoded: false))"
95+
]
96+
}
97+
arguments.append(configFile.path(percentEncoded: false))
98+
99+
let classes = config.classes ?? [:]
100+
print("Classes to wrap: \(classes.map(\.key))")
101+
102+
/// Determine the set of Swift files that will be emitted by the Java2Swift tool.
103+
// TODO: this is not precise and won't work with more advanced Java files, e.g. lambdas etc.
104+
let outputDirectoryGenerated = self.outputDirectory(context: context, generated: true)
105+
let outputSwiftFiles = classes.map { (javaClassName, swiftName) in
106+
let swiftNestedName = swiftName.replacingOccurrences(of: ".", with: "+")
107+
return outputDirectoryGenerated.appending(path: "\(swiftNestedName).swift")
108+
}
109+
110+
arguments += [
111+
"--cache-directory",
112+
context.pluginWorkDirectoryURL.path(percentEncoded: false)
113+
]
114+
115+
// Find the Java .class files generated from prior plugins.
116+
let compiledClassFiles = sourceModule.pluginGeneratedResources.filter { url in
117+
url.pathExtension == "class"
118+
}
119+
120+
if let firstClassFile = compiledClassFiles.first {
121+
// Keep stripping off parts of the path until we hit the "Java" part.
122+
// That's where the class path starts.
123+
var classpath = firstClassFile
124+
while classpath.lastPathComponent != "Java" {
125+
classpath.deleteLastPathComponent()
126+
}
127+
arguments += ["--classpath", classpath.path()]
128+
}
129+
130+
var fetchDependenciesOutputFiles: [URL] = []
131+
if let dependencies = config.dependencies, !dependencies.isEmpty {
132+
let displayName = "Fetch (Java) dependencies for Swift target \(sourceModule.name)"
133+
log("Prepared: \(displayName)")
134+
135+
let arguments = [
136+
"--fetch", configFile.path(percentEncoded: false),
137+
"--module-name", sourceModule.name,
138+
"--output-directory", outputDirectory(context: context, generated: false).path(percentEncoded: false)
139+
]
140+
141+
log("Command: \(executable) \(arguments.joined(separator: " "))")
142+
143+
fetchDependenciesOutputFiles += [
144+
outputFilePath(context: context, generated: false, filename: "\(sourceModule.name).swift-java.classpath")
145+
]
146+
147+
commands += [
148+
.buildCommand(
149+
displayName: displayName,
150+
executable: executable,
151+
arguments: arguments,
152+
inputFiles: [configFile],
153+
outputFiles: fetchDependenciesOutputFiles
154+
)
155+
]
156+
} else {
157+
log("No dependencies to fetch for target \(sourceModule.name)")
158+
}
159+
160+
return commands
161+
}
162+
}
163+
164+
extension Java2SwiftBuildToolPlugin {
165+
func argumentsModuleName(sourceModule: Target) -> [String] {
166+
return [
167+
"--module-name", sourceModule.name
168+
]
169+
}
170+
171+
func argumentsOutputDirectory(context: PluginContext, generated: Bool = true) -> [String] {
172+
return [
173+
"--output-directory",
174+
outputDirectory(context: context, generated: generated).path(percentEncoded: false)
175+
]
176+
}
177+
178+
func outputDirectory(context: PluginContext, generated: Bool = true) -> URL {
179+
let dir = context.pluginWorkDirectoryURL
180+
if generated {
181+
return dir.appending(path: "generated")
182+
} else {
183+
return dir
184+
}
185+
}
186+
187+
func outputFilePath(context: PluginContext, generated: Bool, filename: String) -> URL {
188+
outputDirectory(context: context, generated: generated).appending(path: filename)
189+
}
190+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../PluginsShared

Samples/JavaDependencySampleApp/ci-validate.sh

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
set -e
44
set -x
55

6-
cd ../../JavaKit
7-
./gradlew build
6+
# TODO: this is a workaround for build plugins getting stuck running the bootstrap plugin
7+
cd ../../
8+
swift build --product SwiftJavaBootstrapJavaTool
9+
.build/debug/SwiftJavaBootstrapJavaTool --fetch Sources/JavaKitDependencyResolver/swift-java.config --module-name JavaKitDependencyResolver --output-directory .build/plugins/outputs/swift-java/JavaKitDependencyResolver/destination/SwiftJavaBootstrapJavaPlugin
810

911
cd -
1012
swift run --disable-sandbox

0 commit comments

Comments
 (0)