Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9b624db
Add repositories configuration option in swift-java.config
bo2themax Aug 26, 2025
7bf0de7
Add a repositories configuration example and fix artifactUrls output
bo2themax Aug 26, 2025
6a126ec
Update naming convention
bo2themax Aug 26, 2025
ef69b18
Rename descriptionGradleStyle
bo2themax Aug 28, 2025
5a6616b
Add JavaJson Example
bo2themax Aug 28, 2025
9416c56
Change JavaRepositoryDescriptor to enum
bo2themax Aug 28, 2025
149358a
Add JavaRepositoryTests to test dependencies resolving with custom re…
bo2themax Aug 28, 2025
52243ad
Add documentation for swift-java resolve
bo2themax Aug 28, 2025
764d586
Add another non-resolvable config to verify artifactUrls
bo2themax Aug 28, 2025
7f6cd04
Move JavaRepositoryTests to SwiftJavaToolTests
bo2themax Aug 29, 2025
8bb6cfa
Rename JavaJson to OrgAndrejsJson
bo2themax Aug 29, 2025
c57c017
Add referenced issue in the document
bo2themax Aug 29, 2025
ab8f3ab
[Test] Change minified json to pretty printed
bo2themax Aug 29, 2025
f6f9800
Remove System dependency from OrgAndrejsJsonTests
bo2themax Aug 29, 2025
de3dab3
Add more referenced documents for JavaRepositoryDescriptor
bo2themax Aug 29, 2025
f6ad15f
[Test] Add a SimpleJavaProject to JavaRepositoryTests
bo2themax Aug 29, 2025
8e0687b
Merge branch 'swiftlang:main' into main
bo2themax Aug 29, 2025
a61c518
[Test] Update error messages in JavaRepositoryTests.swift
bo2themax Aug 29, 2025
967ccaf
[Test] Add missing license headers
bo2themax Aug 31, 2025
8fe1360
Add JavaResolver in SwiftJavaToolLib to resolve for ResolveCommand
bo2themax Sep 1, 2025
be87290
[Test] Move SwiftJavaToolTests/JavaRepositoryTests
bo2themax Sep 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ let package = Package(
.testTarget(
name: "SwiftJavaTests",
dependencies: [
"SwiftJava",
"SwiftJava",
"JavaNet"
],
swiftSettings: [
Expand Down Expand Up @@ -488,6 +488,9 @@ let package = Package(
dependencies: [
"SwiftJavaToolLib"
],
exclude: [
"SimpleJavaProject",
],
swiftSettings: [
.swiftLanguageMode(.v5),
.unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"])
Expand Down
22 changes: 21 additions & 1 deletion Samples/JavaDependencySampleApp/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ let package = Package(
.product(name: "SwiftJava", package: "swift-java"),
.product(name: "CSwiftJavaJNI", package: "swift-java"),
.product(name: "JavaUtilFunction", package: "swift-java"),
"JavaCommonsCSV"
"JavaCommonsCSV",
"OrgAndrejsJson",
],
exclude: ["swift-java.config"],
swiftSettings: [
Expand Down Expand Up @@ -99,6 +100,25 @@ let package = Package(
]
),

.target(
name: "OrgAndrejsJson",
dependencies: [
.product(name: "SwiftJava", package: "swift-java"),
.product(name: "JavaUtilFunction", package: "swift-java"),
.product(name: "JavaUtil", package: "swift-java"),
.product(name: "JavaIO", package: "swift-java"),
.product(name: "JavaNet", package: "swift-java"),
],
exclude: ["swift-java.config"],
swiftSettings: [
.unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"]),
.swiftLanguageMode(.v5),
],
plugins: [
.plugin(name: "SwiftJavaPlugin", package: "swift-java"),
]
),

.target(name: "JavaExample"),

]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import Foundation
import SwiftJava

// Import the json library wrapper:
import OrgAndrejsJson

enum OrgAndrejsJsonTests {
static func run() async throws {
print("Now testing Json library...")

let json = Json(#"{"host": "localhost", "port": 80}"#)

precondition(json.hasOwnProperty("port"))

print(json.get("port").toString())
precondition(json.get("port").as(JavaInteger.self)!.intValue() == 80)

print("Reading swift-java.config inside OrgAndrejsJson folder...")

let configPath = String.currentWorkingDirectory.appending("/Sources/OrgAndrejsJson/swift-java.config")

let config = try JavaClass<Json>().of.url("file://" + configPath)!

precondition(config.hasOwnProperty("repositories"))

print(config.toString())
}
}

private extension String {
static var currentWorkingDirectory: Self {
let path = getcwd(nil, 0)!
defer { free(path) }
return String(cString: path)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,6 @@ for record in try CSVFormatClass.RFC4180.parse(reader)!.getRecords()! {
}
}

try await OrgAndrejsJsonTests.run()

print("Done.")
13 changes: 13 additions & 0 deletions Samples/JavaDependencySampleApp/Sources/OrgAndrejsJson/dummy.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"classes": {
"org.andrejs.json.Json": "Json",
"org.andrejs.json.JsonFactory": "JsonFactory"
},
"dependencies": [
"org.andrejs:json:1.2"
],
"repositories": [
{
"type": "maven",
"url": "https://jitpack.io"
}
]
}
82 changes: 82 additions & 0 deletions Sources/SwiftJavaConfigurationShared/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ public struct Configuration: Codable {

// Java dependencies we need to fetch for this target.
public var dependencies: [JavaDependencyDescriptor]?
/// Maven repositories for this target when fetching dependencies.
///
/// `mavenCentral()` will always be used.
///
/// Reference: [Repository Types](https://docs.gradle.org/current/userguide/supported_repository_types.html)
public var repositories: [JavaRepositoryDescriptor]?

public init() {
}
Expand Down Expand Up @@ -133,6 +139,82 @@ public struct JavaDependencyDescriptor: Hashable, Codable {
}
}

/// Descriptor for [repositories](https://docs.gradle.org/current/userguide/supported_repository_types.html#sec:maven-repo)
public enum JavaRepositoryDescriptor: Hashable, Codable, Equatable {

/// Haven't found a proper way to test credentials, packages that need to download from private repo can be downloaded by maven and then use local repo instead
///
/// References:
/// - [Maven repositories](https://docs.gradle.org/current/userguide/supported_repository_types.html#sec:maven-repo)
/// - [Artifacts](https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.repositories.MavenArtifactRepository.html#:~:text=urls)-,Adds%20some%20additional%20URLs%20to%20use%20to%20find%20artifact%20files.%20Note%20that%20these%20URLs%20are%20not%20used%20to%20find%20POM%20files.,-The)
case maven(url: String, artifactUrls: [String]? = nil)
case mavenLocal(includeGroups: [String]? = nil)
case other(_ type: String)

enum CodingKeys: String, CodingKey { case type, url, artifactUrls, credentials, includeGroups }

public init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
let type = try c.decode(String.self, forKey: .type)
switch type {
case "maven":
self = try .maven(
url: c.decode(String.self, forKey: .url),
artifactUrls: try? c.decode([String].self, forKey: .artifactUrls),
)
case "mavenLocal":
self = .mavenLocal(includeGroups: try? c.decode([String].self, forKey: .includeGroups))
default:
self = .other(type)
}
}

public func encode(to encoder: Encoder) throws {
var c = encoder.container(keyedBy: CodingKeys.self)
switch self {
case let .maven(url, artifactUrls/*, creds*/):
try c.encode("maven", forKey: .type)
try c.encode(url, forKey: .url)
if let artifactUrls = artifactUrls {
try c.encode(artifactUrls, forKey: .artifactUrls)
}
case let .mavenLocal(includeGroups):
try c.encode("mavenLocal", forKey: .type)
if let gs = includeGroups {
try c.encode(gs, forKey: .includeGroups)
}
case let .other(type):
try c.encode("\(type)", forKey: .type)
}
}

public func renderGradleRepository() -> String? {
switch self {
case let .maven(url, artifactUrls):
return """
maven {
url = uri("\(url)")
\((artifactUrls ?? []).map({ "artifactUrls(\"\($0)\")" }).joined(separator: "\n"))
}
"""
case let .mavenLocal(groups):
if let gs = groups {
return """
mavenLocal {
content {
\(gs.map({ "includeGroup(\"\($0)\")" }).joined(separator: "\n"))
}
}
"""
} else {
return "mavenLocal()"
}
case let .other(type):
return "\(type)()"
}
}
}

public func readConfiguration(sourceDir: String, file: String = #fileID, line: UInt = #line) throws -> Configuration? {
// Workaround since filePath is macOS 13
let sourcePath =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,89 @@ struct HelloSwiftMain: ParsableCommand {

### Download Java dependencies in Swift builds: swift-java resolve

> TIP: See the `Samples/DependencySampleApp` for a fully functional showcase of this mode.
> TIP: See the `Samples/JavaDependencySampleApp` for a fully functional showcase of this mode.

TODO: documentation on this feature

The `swift-java resolve` command automates the process of downloading and resolving Java dependencies for your Swift project. This is configured through your `swift-java.config` file, where you can declare both the dependencies you need and the repositories from which to fetch them.

To get started, add a `dependencies` array to your configuration file, listing the Maven coordinates for each required library (e.g., `group:artifact:version`). You may also include a `repositories` array to specify custom Maven repositories. For example:

```json
{
"classes": {
"org.apache.commons.io.FilenameUtils": "FilenameUtils",
"org.apache.commons.io.IOCase": "IOCase",
"org.apache.commons.csv.CSVFormat": "CSVFormat",
"org.apache.commons.csv.CSVParser": "CSVParser",
"org.apache.commons.csv.CSVRecord": "CSVRecord"
},
"dependencies": [
"org.apache.commons:commons-csv:1.12.0"
]
}
```

To resolve and download these dependencies, run:

```bash
# See Samples/JavaDependencySampleApp/ci-validate.sh for a complete example
swift-java resolve \
swift-java.config \
--swift-module JavaCommonsCSV \
--output-directory .build/plugins/JavaCommonsCSV/destination/SwiftJavaPlugin/
```

The tool will fetch all specified dependencies from the repositories listed in your config (or Maven Central by default), and generate a `swift-java.classpath` file. This file is then used for building and running your Swift-Java interop code.

If you do not specify any `repositories`, dependencies are resolved from Maven Central. To use a custom or private repository, add it to the `repositories` array, for example:

```json
{
"repositories": [
{ "type": "maven", "url": "https://repo.mycompany.com/maven2" },
{
"type": "maven",
"url": "https://repo2.mycompany.com/maven2",
"artifactUrls": [
"https://repo.mycompany.com/jars",
"https://repo.mycompany.com/jars2"
]
},
{ "type": "maven", "url": "https://secure.repo.com/maven2" },
{ "type": "mavenLocal", "includeGroups": ["com.example.myproject"] },
{ "type": "maven", "url": "build/repo" }, // Relative to build folder of the temporary project, better to use absolute path here, no need to add `file:` prefix
{ "type": "mavenCentral" },
{ "type": "mavenLocal" },
{ "type": "google" }
]
}
```

> Note: [Authentication for private repositories is not currently handled directly by `swift-java`](https://github.com/swiftlang/swift-java/issues/382). If you need to access packages from a private repository that requires credentials, you can use Maven to download the required artifacts and then reference them via your local Maven repository in your configuration.

For practical usage, refer to `Samples/JavaDependencySampleApp` and the tests in `Tests/SwiftJavaTests/JavaRepositoryTests.swift`.

This workflow streamlines Java dependency management for Swift projects, letting you use Java libraries without manually downloading JAR files.

#### About the `classes` section

The `classes` section in your `swift-java.config` file specifies which Java classes should be made available in Swift, and what their corresponding Swift type names should be. Each entry maps a fully-qualified Java class name to a Swift type name. For example:

```json
{
"classes": {
"org.apache.commons.io.FilenameUtils" : "FilenameUtils",
"org.apache.commons.io.IOCase" : "IOCase",
"org.apache.commons.csv.CSVFormat" : "CSVFormat",
"org.apache.commons.csv.CSVParser" : "CSVParser",
"org.apache.commons.csv.CSVRecord" : "CSVRecord"
}
}
```

When you run `swift-java wrap-java` (or build your project with the plugin), Swift source files are generated for each mapped class. For instance, the above config will result in `CSVFormat.swift`, `CSVParser.swift`, `CSVRecord.swift`, `FilenameUtils.swift` and `IOCase.swift` files, each containing a Swift class that wraps the corresponding Java class and exposes its constructors, methods, and fields for use in Swift.

This mapping allows you to use Java APIs directly from Swift, with type-safe wrappers and automatic bridging of method calls and data types.

### Expose Swift code to Java: swift-java jextract

Expand Down
Loading
Loading