Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[jni] Migrates java source build of jni from using maven to gradle #2003

Merged
merged 4 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 5 additions & 0 deletions pkgs/jni/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.14.1-wip

- Updated `bin/setup.dart` to use Gradle instead of Maven for building Java sources. Added gradle executables
and bootstrap jars [#2003](https://github.com/dart-lang/native/issues/2003)

## 0.14.0

- Added `DynamicLibraryLoadError` which is thrown when the dynamic library fails
Expand Down
84 changes: 59 additions & 25 deletions pkgs/jni/bin/setup.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ const _verbose = 'verbose';
const _cmakeArgs = 'cmake-args';

Future<void> runCommand(
String exec, List<String> args, String workingDir) async {
String exec,
List<String> args,
String workingDir,
) async {
// For printing relative path always.
var current = Directory.current.path;
if (!current.endsWith(Platform.pathSeparator)) {
Expand All @@ -37,7 +40,8 @@ Future<void> runCommand(
int status;
if (options.verbose) {
final process = await Process.start(
exec, args,
exec,
args,
workingDirectory: workingDir,
mode: ProcessStartMode.inheritStdio,
// without `runInShell`, sometimes cmake doesn't run on windows.
Expand All @@ -49,8 +53,12 @@ Future<void> runCommand(
}
} else {
// ProcessStartMode.normal sometimes hangs on windows. No idea why.
final process = await Process.run(exec, args,
runInShell: true, workingDirectory: workingDir);
final process = await Process.run(
exec,
args,
runInShell: true,
workingDirectory: workingDir,
);
status = process.exitCode;
if (status != 0) {
exitCode = status;
Expand Down Expand Up @@ -144,17 +152,28 @@ String getTargetName(Directory cDir) {

void main(List<String> arguments) async {
final parser = ArgParser()
..addOption(_buildPath,
abbr: 'b', help: 'Directory to place built artifacts')
..addMultiOption(_srcPath,
abbr: 's', help: 'alternative path to package:jni sources')
..addMultiOption(_packageName,
abbr: 'p',
help: 'package for which native'
'library should be built')
..addOption(
_buildPath,
abbr: 'b',
help: 'Directory to place built artifacts',
)
..addMultiOption(
_srcPath,
abbr: 's',
help: 'alternative path to package:jni sources',
)
..addMultiOption(
_packageName,
abbr: 'p',
help: 'package for which native'
'library should be built',
)
..addFlag(_verbose, abbr: 'v', help: 'Enable verbose output')
..addMultiOption(_cmakeArgs,
abbr: 'm', help: 'Pass additional argument to CMake');
..addMultiOption(
_cmakeArgs,
abbr: 'm',
help: 'Pass additional argument to CMake',
);
final argResults = parser.parse(arguments);
options = Options(argResults);
final rest = argResults.rest;
Expand All @@ -174,8 +193,10 @@ void main(List<String> arguments) async {
}
if (sources.isEmpty) {
final dependencySources = await findDependencySources();
stderr.writeln('selecting source directories for dependencies: '
'${dependencySources.keys}');
stderr.writeln(
'selecting source directories for dependencies: '
'${dependencySources.keys}',
);
sources.addAll(dependencySources.values);
} else {
stderr.writeln('selecting source directories: $sources');
Expand All @@ -194,17 +215,28 @@ void main(List<String> arguments) async {

final javaSrc = await findSources('jni', 'java');
final targetJar = File.fromUri(buildDir.uri.resolve('jni.jar'));
final isWin = Platform.isWindows;
verboseLog('File exists:${targetJar.existsSync()}');

final gradleWExecutable = (Platform.isLinux || Platform.isMacOS)
? Uri.directory(javaSrc).resolve('gradlew')
: Uri.directory(javaSrc).resolve('gradlew.bat');

if (!needsBuild(targetJar, Directory.fromUri(Uri.directory(javaSrc)))) {
verboseLog('Last modified of ${targetJar.path}: '
'${targetJar.lastModifiedSync()}.');
verboseLog(
'Last modified of ${targetJar.path}: '
'${targetJar.lastModifiedSync()}.',
);
stderr.writeln('Target newer than source, skipping build.');
} else {
verboseLog('Running mvn package for jni java sources to $buildPath.');
verboseLog('Running gradle task for building jni sources to $buildPath.');
await runCommand(
'mvn',
['package', '-Dtarget=${buildDir.absolute.path}'],
await findSources('jni', 'java'),
);
gradleWExecutable.toFilePath(windows: isWin),
[
'jar',
'-Pjni.targetDir=${buildDir.absolute.uri.toFilePath(windows: isWin)}',
],
await findSources('jni', 'java'));
}

for (var srcPath in sources) {
Expand All @@ -221,8 +253,10 @@ void main(List<String> arguments) async {
final targetFileUri = buildDir.uri.resolve(getTargetName(srcDir));
final targetFile = File.fromUri(targetFileUri);
if (!needsBuild(targetFile, srcDir)) {
verboseLog('Last modified of ${targetFile.path}: '
'${targetFile.lastModifiedSync()}.');
verboseLog(
'Last modified of ${targetFile.path}: '
'${targetFile.lastModifiedSync()}.',
);
stderr.writeln('Target newer than source, skipping build.');
continue;
}
Expand Down
39 changes: 39 additions & 0 deletions pkgs/jni/java/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
plugins {
java
application
}

repositories {
mavenCentral()
google()
}

dependencies {
implementation(libs.org.jetbrains.kotlin.kotlin.stdlib)
implementation(libs.org.jetbrains.kotlinx.kotlinx.coroutines.core)
}

group = "com.github.dart_lang"
description = "jni"

java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

tasks.withType<Jar> {
val targetDir: String? = project.findProperty("jni.targetDir") as String?
if (targetDir != null) {
destinationDirectory = file(targetDir)
} else {
destinationDirectory = file("build/jni_libs")
}
}

tasks.withType<JavaCompile>() {
options.encoding = "UTF-8"
}

tasks.withType<Javadoc>() {
options.encoding = "UTF-8"
}
10 changes: 10 additions & 0 deletions pkgs/jni/java/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# This file was generated by the Gradle 'init' task.
# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format

[versions]
org-jetbrains-kotlin-kotlin-stdlib = "1.6.20"
org-jetbrains-kotlinx-kotlinx-coroutines-core = "1.6.4"

[libraries]
org-jetbrains-kotlin-kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "org-jetbrains-kotlin-kotlin-stdlib" }
org-jetbrains-kotlinx-kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "org-jetbrains-kotlinx-kotlinx-coroutines-core" }
Binary file added pkgs/jni/java/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
7 changes: 7 additions & 0 deletions pkgs/jni/java/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading