Skip to content

Commit d6970de

Browse files
authored
Merge pull request #61 from stepfunc/feature/java-aarch64
Add AArch64 support for Java and .NET
2 parents 4f30d40 + 8da9085 commit d6970de

File tree

11 files changed

+169
-66
lines changed

11 files changed

+169
-66
lines changed

.github/workflows/ci.yml

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
run: cargo-license --avoid-dev-deps --avoid-build-deps -j > ../dependencies.json
4040
- name: Generate dependencies.txt
4141
working-directory: complicense
42-
run: cargo run -- --import ../oo-bindgen/dependencies.json --config ../oo-bindgen/deps-config.json --token ${{ github.token }} > ../oo-bindgen/dependencies.txt
42+
run: cargo run -- --import ../oo-bindgen/dependencies.json --config ../oo-bindgen/deps-config.json --token ${{ github.token }} > ../oo-bindgen/dependencies.txt
4343
- name: Upload dependencies info
4444
uses: actions/upload-artifact@v2
4545
with:
@@ -192,6 +192,65 @@ jobs:
192192
with:
193193
name: ffi-modules
194194
path: tests/bindings/java/foo/src/main/resources
195+
# Build bindings on Linux AArch64
196+
bindings-aarch64:
197+
needs: lock
198+
runs-on: ubuntu-latest
199+
steps:
200+
- name: Checkout
201+
uses: actions/checkout@v2
202+
- name: Install Rust
203+
uses: actions-rs/toolchain@v1
204+
with:
205+
profile: minimal
206+
toolchain: stable
207+
target: aarch64-unknown-linux-gnu
208+
override: true
209+
- name: Caching
210+
uses: Swatinem/rust-cache@v1
211+
- name: Download Cargo.lock
212+
uses: actions/download-artifact@v2
213+
with:
214+
name: dependencies
215+
- name: Build
216+
uses: actions-rs/cargo@v1
217+
with:
218+
use-cross: true
219+
command: build
220+
args: --release --target aarch64-unknown-linux-gnu
221+
- name: C bindings
222+
uses: actions-rs/cargo@v1
223+
with:
224+
use-cross: true
225+
command: run
226+
args: --release --target aarch64-unknown-linux-gnu --bin foo-bindings -- --c --no-tests
227+
- name: .NET bindings
228+
uses: actions-rs/cargo@v1
229+
with:
230+
use-cross: true
231+
command: run
232+
args: --release --target aarch64-unknown-linux-gnu --bin foo-bindings -- --dotnet --no-tests
233+
- name: Java bindings
234+
uses: actions-rs/cargo@v1
235+
with:
236+
use-cross: true
237+
command: run
238+
args: --release --target aarch64-unknown-linux-gnu --bin foo-bindings -- --java --no-tests
239+
- name: Upload compiled FFI modules
240+
uses: actions/upload-artifact@v2
241+
with:
242+
name: ffi-modules
243+
path: tests/bindings/c/generated/aarch64-unknown-linux-gnu/lib
244+
- name: Upload C bindings
245+
uses: actions/upload-artifact@v2
246+
with:
247+
name: c-bindings
248+
path: tests/bindings/c/generated
249+
- name: Upload compiled Java bindings
250+
uses: actions/upload-artifact@v2
251+
with:
252+
name: ffi-modules
253+
path: tests/bindings/java/foo/src/main/resources
195254
# Cross-compilation for ARM devices and produce C bindings
196255
cross:
197256
needs: lock
@@ -202,7 +261,6 @@ jobs:
202261
- arm-unknown-linux-gnueabi # ARMv6 Linux (kernel 3.2, glibc 2.17)
203262
- arm-unknown-linux-gnueabihf # ARMv6 Linux, hardfloat (kernel 3.2, glibc 2.17)
204263
- armv7-unknown-linux-gnueabihf # ARMv7 Linux, hardfloat (kernel 3.2, glibc 2.17)
205-
- aarch64-unknown-linux-gnu # ARM64 Linux (kernel 4.2, glibc 2.17+)
206264
runs-on: ubuntu-latest
207265
steps:
208266
- name: Checkout

ci-script/src/lib.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,11 @@ fn run_builder<'a, B: BindingBuilder<'a>>(
147147

148148
builder.generate(package);
149149

150-
if !package && run_tests {
151-
builder.build();
152-
builder.test();
150+
if !package {
151+
if run_tests {
152+
builder.build();
153+
builder.test();
154+
}
153155
} else {
154156
builder.package();
155157
}
@@ -203,7 +205,7 @@ impl<'a> CBindingBuilder<'a> {
203205
platform_location: self.platforms.iter().next().unwrap(),
204206
};
205207

206-
c_oo_bindgen::generate_doxygen(&self.settings.library, &config)
208+
c_oo_bindgen::generate_doxygen(self.settings.library, &config)
207209
.expect("failed to package C lib");
208210
}
209211

@@ -254,7 +256,7 @@ impl<'a> BindingBuilder<'a> for CBindingBuilder<'a> {
254256
platform_location: platform.clone(),
255257
};
256258

257-
c_oo_bindgen::generate_c_package(&self.settings.library, &config)
259+
c_oo_bindgen::generate_c_package(self.settings.library, &config)
258260
.expect("failed to package C lib");
259261
}
260262
}
@@ -493,12 +495,21 @@ impl<'a> BindingBuilder<'a> for JavaBindingBuilder<'a> {
493495
// You: *still puzzled about the situation, but accepting this hack*
494496
let target_dir = pathdiff::diff_paths("./target", self.rust_build_dir()).unwrap();
495497
let mut cmd = Command::new("cargo");
498+
499+
let current_platform = Platform::from_target_triple(env!("TARGET_TRIPLE")).unwrap();
500+
496501
cmd.current_dir(self.rust_build_dir()).args(&[
497502
"build",
498503
"--target-dir",
499504
&target_dir.to_string_lossy(),
500505
]);
501506

507+
if current_platform == Platform::LinuxArm8Gnu {
508+
// This is a hack in order to have the CI properly work
509+
// It is due to how `cross` works.
510+
cmd.args(&["--target", env!("TARGET_TRIPLE")]);
511+
}
512+
502513
if env!("PROFILE") == "release" {
503514
cmd.arg("--release");
504515
}

generators/dotnet-oo-bindgen/src/class.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ fn generate_constructor(
163163
f.write(")")?;
164164

165165
blocked(f, |f| {
166-
call_native_function(f, &constructor, "this.self = ", None, true)
166+
call_native_function(f, constructor, "this.self = ", None, true)
167167
})
168168
}
169169

generators/dotnet-oo-bindgen/src/interface.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,7 @@ pub(crate) fn generate(
8181

8282
// Write the Action<>/Func<> based implementation if it's a functional interface
8383
if interface.is_functional() {
84-
generate_functional_callback(
85-
f,
86-
&interface,
87-
interface.callbacks().next().unwrap(),
88-
lib,
89-
)?;
84+
generate_functional_callback(f, interface, interface.callbacks().next().unwrap(), lib)?;
9085
f.newline()?;
9186
}
9287

generators/dotnet-oo-bindgen/src/lib.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,11 @@ mod wrappers;
6969

7070
pub const NATIVE_FUNCTIONS_CLASSNAME: &str = "NativeFunctions";
7171

72-
const SUPPORTED_PLATFORMS: &[Platform] = &[Platform::WinX64Msvc, Platform::LinuxX64Gnu];
72+
const SUPPORTED_PLATFORMS: &[Platform] = &[
73+
Platform::WinX64Msvc,
74+
Platform::LinuxX64Gnu,
75+
Platform::LinuxArm8Gnu,
76+
];
7377

7478
pub struct DotnetBindgenConfig {
7579
pub output_dir: PathBuf,
@@ -450,6 +454,7 @@ fn dotnet_platform_string(platform: Platform) -> &'static str {
450454
match platform {
451455
Platform::WinX64Msvc => "win-x64",
452456
Platform::LinuxX64Gnu => "linux-x64",
457+
Platform::LinuxArm8Gnu => "linux-arm64",
453458
_ => panic!("Unsupported platform"),
454459
}
455460
}

generators/java-oo-bindgen/src/java/class.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ fn generate_constructor(
136136
f.write(")")?;
137137

138138
blocked(f, |f| {
139-
call_native_function(f, &constructor, &format!("{} object = ", classname), false)?;
139+
call_native_function(f, constructor, &format!("{} object = ", classname), false)?;
140140
f.writeln("this.self = object.self;")?;
141141
f.writeln("object.disposed.set(true);")
142142
})

generators/java-oo-bindgen/src/java/mod.rs

Lines changed: 74 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use self::conversion::*;
22
use self::formatting::*;
33
use crate::JavaBindgenConfig;
4+
use heck::ShoutySnakeCase;
45
use heck::{CamelCase, KebabCase};
56
use oo_bindgen::formatting::*;
67
use oo_bindgen::native_function::*;
@@ -20,7 +21,11 @@ mod structure;
2021

2122
const NATIVE_FUNCTIONS_CLASSNAME: &str = "NativeFunctions";
2223

23-
const SUPPORTED_PLATFORMS: &[Platform] = &[Platform::WinX64Msvc, Platform::LinuxX64Gnu];
24+
const SUPPORTED_PLATFORMS: &[Platform] = &[
25+
Platform::WinX64Msvc,
26+
Platform::LinuxX64Gnu,
27+
Platform::LinuxArm8Gnu,
28+
];
2429

2530
pub fn generate_java_bindings(lib: &Library, config: &JavaBindgenConfig) -> FormattingResult<()> {
2631
fs::create_dir_all(&config.java_output_dir)?;
@@ -46,7 +51,7 @@ pub fn generate_java_bindings(lib: &Library, config: &JavaBindgenConfig) -> Form
4651
let mut target_file = target_dir.clone();
4752
target_file.push(platform.bin_filename(&ffi_name));
4853

49-
fs::copy(source_file, target_file)?;
54+
fs::copy(&source_file, &target_file)?;
5055
}
5156

5257
// Copy the extra files
@@ -170,11 +175,6 @@ fn generate_pom(lib: &Library, config: &JavaBindgenConfig) -> FormattingResult<(
170175
f.writeln(" <artifactId>joou-java-6</artifactId>")?;
171176
f.writeln(" <version>0.9.4</version>")?;
172177
f.writeln(" </dependency>")?;
173-
f.writeln(" <dependency>")?;
174-
f.writeln(" <groupId>org.apache.commons</groupId>")?;
175-
f.writeln(" <artifactId>commons-lang3</artifactId>")?;
176-
f.writeln(" <version>3.11</version>")?;
177-
f.writeln(" </dependency>")?;
178178
f.writeln("</dependencies>")?;
179179

180180
f.newline()?;
@@ -226,35 +226,62 @@ fn generate_native_func_class(lib: &Library, config: &JavaBindgenConfig) -> Form
226226
blocked(f, |f| {
227227
f.writeln("try")?;
228228
blocked(f, |f| {
229-
let libname = format!("{}_java", config.ffi_name);
230-
for platform in config.platforms.iter() {
231-
match platform.platform {
232-
Platform::WinX64Msvc => {
233-
f.writeln("if(org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS && org.apache.commons.lang3.ArchUtils.getProcessor().is64Bit())")?;
234-
blocked(f, |f| {
235-
f.writeln(&format!(
236-
"loadLibrary(\"{}\", \"{}\", \"dll\");",
237-
platform.platform.as_string(),
238-
libname
239-
))
240-
})?;
241-
}
242-
Platform::LinuxX64Gnu => {
243-
f.writeln("if(org.apache.commons.lang3.SystemUtils.IS_OS_LINUX && org.apache.commons.lang3.ArchUtils.getProcessor().is64Bit())")?;
244-
blocked(f, |f| {
245-
f.writeln(&format!(
246-
"loadLibrary(\"{}\", \"lib{}\", \"so\");",
247-
platform.platform.as_string(),
248-
libname
249-
))
250-
})?;
229+
let env_variable_name =
230+
format!("{}_NATIVE_LIB_LOCATION", lib.name.to_shouty_snake_case());
231+
f.writeln(&format!(
232+
"String nativeLibLocation = System.getenv(\"{}\");",
233+
env_variable_name
234+
))?;
235+
f.writeln("if(nativeLibLocation != null)")?;
236+
blocked(f, |f| f.writeln("System.load(nativeLibLocation);"))?;
237+
238+
f.writeln("else")?;
239+
blocked(f, |f| {
240+
f.writeln("boolean loaded = false;")?;
241+
let libname = format!("{}_java", config.ffi_name);
242+
for platform in config.platforms.iter() {
243+
match platform.platform {
244+
Platform::WinX64Msvc => {
245+
f.writeln("if(!loaded)")?;
246+
blocked(f, |f| {
247+
f.writeln(&format!(
248+
"loaded = loadLibrary(\"{}\", \"{}\", \"dll\");",
249+
platform.platform.as_string(),
250+
libname
251+
))
252+
})?;
253+
}
254+
Platform::LinuxX64Gnu => {
255+
f.writeln("if(!loaded)")?;
256+
blocked(f, |f| {
257+
f.writeln(&format!(
258+
"loaded = loadLibrary(\"{}\", \"lib{}\", \"so\");",
259+
platform.platform.as_string(),
260+
libname
261+
))
262+
})?;
263+
}
264+
Platform::LinuxArm8Gnu => {
265+
f.writeln("if(!loaded)")?;
266+
blocked(f, |f| {
267+
f.writeln(&format!(
268+
"loaded = loadLibrary(\"{}\", \"lib{}\", \"so\");",
269+
platform.platform.as_string(),
270+
libname
271+
))
272+
})?;
273+
}
274+
_ => (), // Other platforms are not supported
251275
}
252-
_ => (), // Other platforms are not supported
253276
}
254-
}
255-
Ok(())
277+
278+
f.writeln("if(!loaded)")?;
279+
blocked(f, |f| {
280+
f.writeln("throw new Exception(\"Unable to load any of the included native library\");")
281+
})
282+
})
256283
})?;
257-
f.writeln("catch(java.io.IOException e)")?;
284+
f.writeln("catch(Exception e)")?;
258285
blocked(f, |f| {
259286
f.writeln("System.err.println(\"Native code library failed to load: \" + e);")?;
260287
f.writeln("System.exit(1);")
@@ -264,13 +291,20 @@ fn generate_native_func_class(lib: &Library, config: &JavaBindgenConfig) -> Form
264291
f.newline()?;
265292

266293
// Load library helper function
267-
f.writeln("private static void loadLibrary(String directory, String name, String extension) throws java.io.IOException {")?;
268-
f.writeln(" java.io.InputStream stream = NativeFunctions.class.getResourceAsStream(\"/\" + directory + \"/\" + name + \".\" + extension);")?;
269-
f.writeln(" java.nio.file.Path tempFilePath = java.nio.file.Files.createTempFile(name, \".\" + extension);")?;
270-
f.writeln(" tempFilePath.toFile().deleteOnExit();")?;
271-
f.writeln(" java.nio.file.Files.copy(stream, tempFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);")?;
272-
f.writeln(" System.load(tempFilePath.toString());")?;
273-
f.writeln("}")?;
294+
f.writeln("private static boolean loadLibrary(String directory, String name, String extension) throws Exception")?;
295+
blocked(f, |f| {
296+
f.writeln("try")?;
297+
blocked(f, |f| {
298+
f.writeln("java.io.InputStream stream = NativeFunctions.class.getResourceAsStream(\"/\" + directory + \"/\" + name + \".\" + extension);")?;
299+
f.writeln("java.nio.file.Path tempFilePath = java.nio.file.Files.createTempFile(name, \".\" + extension);")?;
300+
f.writeln("tempFilePath.toFile().deleteOnExit();")?;
301+
f.writeln("java.nio.file.Files.copy(stream, tempFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);")?;
302+
f.writeln("System.load(tempFilePath.toString());")?;
303+
f.writeln("return true;")
304+
})?;
305+
f.writeln("catch(Exception e)")?;
306+
blocked(f, |f| f.writeln("return false;"))
307+
})?;
274308

275309
f.newline()?;
276310

generators/java-oo-bindgen/src/rust/interface.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,10 +222,10 @@ fn write_interface_init<'a>(
222222
"({}){}",
223223
callback
224224
.params()
225-
.map(|param| { param.param_type.as_jni_sig(&lib_path) })
225+
.map(|param| { param.param_type.as_jni_sig(lib_path) })
226226
.collect::<Vec<_>>()
227227
.join(""),
228-
callback.return_type.as_jni_sig(&lib_path)
228+
callback.return_type.as_jni_sig(lib_path)
229229
);
230230
f.writeln(&format!("let cb_{method_snake} = env.get_method_id(class, \"{method_mixed}\", \"{method_sig}\").map(|mid| mid.into_inner().into()).expect(\"Unable to find method {method_mixed}\");", method_snake=callback.name.to_snake_case(), method_mixed=callback.name.to_mixed_case(), method_sig=method_sig))?;
231231
}
@@ -289,7 +289,7 @@ fn call_java_callback<'a>(
289289
"_env.call_method_unchecked(_obj.as_obj(), _cache.interfaces.{}.cb_{}, jni::signature::JavaType::from_str(\"{}\").unwrap(), &[{}]).unwrap();",
290290
interface_name,
291291
callback_name,
292-
return_type.as_jni_sig(&lib_path),
292+
return_type.as_jni_sig(lib_path),
293293
params.iter().map(|param| format!("{}.into()", param.name.to_snake_case())).collect::<Vec<_>>().join(", ")
294294
))?;
295295

0 commit comments

Comments
 (0)