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

Neon Rust bindings support #193

Closed
stoically opened this issue May 12, 2019 · 17 comments
Closed

Neon Rust bindings support #193

stoically opened this issue May 12, 2019 · 17 comments

Comments

@stoically
Copy link

neon compiles Rust code to native nodejs modules and provides bindings to work with those native modules. Would be awesome if nodejs-mobile could support neon compiled native modules.

Notes

"Related" issues over at neon:

Also, maybe it's already possible by manually building #173 the neon module and providing it to nodejs-mobile? I've just naively tried to copy over my neon-build module into the nodejs-mobile node_modules folder, but that makes react-native run-android fail with ':app:packageDebug'. > org.gradle.tooling.BuildException (no error message).

@stoically
Copy link
Author

stoically commented May 13, 2019

Did some digging, tl;dr is it builds but crashes without error when trying to run.

Prerequisites

  • nodejs-mobile-react-native with Android Studio Emulator
  • Globally install neon-cli through npm i -g neon-cli
  • Create neon test project: neon new neon-test

Findings

neon projects don't have a bindings.gyp, so it's needed to put a BUILD_NATIVE_MODULES.txt with content 1 into the nodejs-assets directory. That will trigger the neon build if the neon project has additionally an "install": "neon build" scripts entry in the package.json.

react-native run-android will successfully build, put the compiled neon native module into build/nodejs-native-assets/nodejs-native-assets-{abi}/node_modules/neon-test/native/index.node and then packages the native/index.node into the apk and deploys it to the emulator.

  • Details
    • neon build calls rustc build internally in the neon-test crate located at neon-test/native
    • The neon-test/native crate depends on the neon crate which in turn has the neon-runtime crate as build-dependency
    • rustc build triggers the build for the crate neon-runtime as defined in its build.rs
    • neon-runtimes build runs npm run build-{debug|release}, which is a call to node-gyp build as defined in its package.json
    • Checked the env::vars in the build.rs and the npm_configs_, like e.g. npm_config_node_gyp, from nodejs-mobile are set correctly

Errors

Requiring the neon-test project inside the nodejs-assets/nodejs-project/main.js and nodejs.starting from react-native will crash the app without an error message. Not sure how to debug further. Any pointers would be appreciated.

Trying to build the apk with ./gradlew assembleRelease will fail if there's already a native/index.node and native/target in the neon-test node_modules directory, which is the case if npm install is executed in the nodejs-assets/nodejs-project with the "install": "neon build" script in the package.json. It's needed to remove native/index.node and native/target first to have a successful build.

@stoically
Copy link
Author

stoically commented May 15, 2019

So I realized that I can check the Android Studio Logcat and there's actually an error:

11909-11956/com.neontest E/NODEJS-MOBILE: internal/modules/cjs/loader.js:717
      return process.dlopen(module, path.toNamespacedPath(filename));
                     ^

  Error: dlopen failed: cannot locate symbol "node_module_register" referenced by "/data/data/com.neontest/files/nodejs-project/node_modules/neon-test/native/index.node"...
stacktrace at Object.Module._extensions..node (internal/modules/cjs/loader.js:717:18) at Module.load (internal/modules/cjs/loader.js:598:32) at tryModuleLoad (internal/modules/cjs/loader.js:537:12) at Function.Module._load (internal/modules/cjs/loader.js:529:3) at Module.require (internal/modules/cjs/loader.js:636:17) at require (internal/modules/cjs/helpers.js:20:18) at Object. (/data/data/com.neontest/files/nodejs-project/node_modules/neon-test/lib/index.js:1:75) at Module._compile (internal/modules/cjs/loader.js:688:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10) at Module.load (internal/modules/cjs/loader.js:598:32) 11909-11914/com.neontest A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x5d8 in tid 11914 (Jit thread pool), pid 11909 (com.neontest)

To actually get neon to build successfully, it's needed to set some rust cargo build environment variables and have the neon-cli respect the CARGO_BUILD_TARGET. I've opened PRs regarding that here:

(I've pushed the neon change to a fork, so it's possible to add npx neon-cli@stoically/neon#neon-cli-dist build as install script)

Also, rust needs the targets installed, on linux these are:

rustup target add i686-linux-android
rustup target add arm-linux-androideabi
rustup target add armv7-linux-androideabi
rustup target add aarch64-linux-android
rustup target add x86_64-linux-android

@jaimecbernardo
Copy link
Member

There's also the matter of having to link the final binary with nodejs-mobile for it to load successfully at runtime (this might be why the dlopen failed error appeared) : JaneaSystems/nodejs-mobile-gyp#4

It's working now, then?

@stoically
Copy link
Author

stoically commented May 15, 2019

Thanks for the hint. I've tried to let neon use the nodejs-mobile fork of node-gyp by putting /path/to/nodejs-mobile-gyp/bin/nody-gyp.js build --nodedir=/path/to/nodejs-mobile-react-native/android/libnode into the neon-runtime package.json where currently node-gyp is called. It again builds successfully, but unfortunately still seeing the dlopen failed error. Maybe you got an idea on how to debug this further?

@stoically
Copy link
Author

Here's the output from nodejs-mobile-gyp configure and build (run by neon-runtime):

gyp configure
Output { status: ExitStatus(ExitStatus(0)), stdout: "
> @ configure-debug /path/to/.cargo/registry/src/github.com-1ecc6299db9ec823/neon-runtime-0.2.0
> /path/to/nodejs-mobile-react-native/android/../../nodejs-mobile-gyp/bin/node-gyp.js configure --verbose --debug

", stderr: "npm info it worked if it ends with ok
npm verb cli [ \'/path/to/.nvm/versions/node/v10.15.3/bin/node\',
npm verb cli   \'/path/to/.nvm/versions/node/v10.15.3/bin/npm\',
npm verb cli   \'run\',
npm verb cli   \'configure-debug\' ]
npm info using [email protected]
npm info using [email protected]
npm verb run-script [ \'preconfigure-debug\',
npm verb run-script   \'configure-debug\',
npm verb run-script   \'postconfigure-debug\' ]
npm info lifecycle @~preconfigure-debug: @
npm info lifecycle @~configure-debug: @
gyp info it worked if it ends with ok
gyp verb cli [ \'/path/to/.nvm/versions/node/v10.15.3/bin/node\',
gyp verb cli   \'/path/to/nodejs-mobile-gyp/bin/node-gyp.js\',
gyp verb cli   \'configure\',
gyp verb cli   \'--verbose\',
gyp verb cli   \'--debug\' ]
gyp info using [email protected]
gyp info using [email protected] | linux | x64
gyp verb command configure []
gyp verb check python checking for Python executable \"python2\" in the PATH
gyp verb `which` succeeded python2 /usr/bin/python2
gyp verb check python version `/usr/bin/python2 -c \"import sys; print \"2.7.15
gyp verb check python version .%s.%s\" % sys.version_info[:3];\"` returned: %j
gyp verb get node dir compiling against specified --nodedir dev files: /path/to/nodejs-mobile-react-native/android/libnode/
gyp verb build dir attempting to create \"build\" dir: /path/to/.cargo/registry/src/github.com-1ecc6299db9ec823/neon-runtime-0.2.0/build
gyp verb build dir \"build\" dir needed to be created? null
gyp verb build/config.gypi creating config file
gyp verb build/config.gypi writing out config file: /path/to/.cargo/registry/src/github.com-1ecc6299db9ec823/neon-runtime-0.2.0/build/config.gypi
gyp verb config.gypi checking for gypi file: /path/to/.cargo/registry/src/github.com-1ecc6299db9ec823/neon-runtime-0.2.0/config.gypi
gyp verb common.gypi checking for gypi file: /path/to/.cargo/registry/src/github.com-1ecc6299db9ec823/neon-runtime-0.2.0/common.gypi
gyp verb gyp gyp format was not specified; forcing \"make\"
gyp info spawn /usr/bin/python2
gyp info spawn args [ \'/path/to/nodejs-mobile-gyp/gyp/gyp_main.py\',
gyp info spawn args   \'binding.gyp\',
gyp info spawn args   \'-f\',
gyp info spawn args   \'make-android\',
gyp info spawn args   \'-I\',
gyp info spawn args   \'/path/to/.cargo/registry/src/github.com-1ecc6299db9ec823/neon-runtime-0.2.0/build/config.gypi\',
gyp info spawn args   \'-I\',
gyp info spawn args   \'/path/to/nodejs-mobile-gyp/addon.gypi\',
gyp info spawn args   \'-I\',
gyp info spawn args   \'/path/to/nodejs-mobile-react-native/android/libnode/include/node/common.gypi\',
gyp info spawn args   \'-Dlibrary=shared_library\',
gyp info spawn args   \'-Dvisibility=default\',
gyp info spawn args   \'-Dnode_root_dir=/path/to/nodejs-mobile-react-native/android/libnode/\',
gyp info spawn args   \'-Dnode_gyp_dir=/path/to/nodejs-mobile-gyp\',
gyp info spawn args   \'-Dnode_lib_file=/path/to/nodejs-mobile-react-native/android/libnode/$(Configuration)/node.lib\',
gyp info spawn args   \'-Dmodule_root_dir=/path/to/.cargo/registry/src/github.com-1ecc6299db9ec823/neon-runtime-0.2.0\',
gyp info spawn args   \'-Dnode_engine=v8\',
gyp info spawn args   \'--depth=.\',
gyp info spawn args   \'--no-parallel\',
gyp info spawn args   \'--generator-output\',
gyp info spawn args   \'build\',
gyp info spawn args   \'-Goutput_dir=.\' ]
gyp info ok 
npm verb lifecycle @~configure-debug: unsafe-perm in lifecycle true
npm verb lifecycle @~configure-debug: CWD: /path/to/.cargo/registry/src/github.com-1ecc6299db9ec823/neon-runtime-0.2.0
npm info lifecycle @~postconfigure-debug: @
npm verb exit [ 0, true ]
npm timing npm Completed in 510ms
npm info ok 
" }
gyp build
gyp build output Output { status: ExitStatus(ExitStatus(0)), stdout: "
> @ build-debug /path/to/.cargo/registry/src/github.com-1ecc6299db9ec823/neon-runtime-0.2.0
> /path/to/nodejs-mobile-react-native/android/../../nodejs-mobile-gyp/bin/node-gyp.js build --debug

make: Entering directory 
'/path/to/.cargo/registry/src/github.com-1ecc6299db9ec823/neon-runtime-0.2.0/build
'
make: Nothing to be done for 
'all
'.
make: Leaving directory 
'/path/to/.cargo/registry/src/github.com-1ecc6299db9ec823/neon-runtime-0.2.0/build
'
", stderr: "npm info it worked if it ends with ok
npm verb cli [ 
'/path/to/.nvm/versions/node/v10.15.3/bin/node
',
npm verb cli   
'/path/to/.nvm/versions/node/v10.15.3/bin/npm
',
npm verb cli   
'run
',
npm verb cli   
'build-debug
' ]
npm info using [email protected]
npm info using [email protected]
npm verb run-script [ 
'prebuild-debug
', 
'build-debug
', 
'postbuild-debug
' ]
npm info lifecycle @~prebuild-debug: @
npm info lifecycle @~build-debug: @
gyp info it worked if it ends with ok
gyp verb cli [ 
'/path/to/.nvm/versions/node/v10.15.3/bin/node
',
gyp verb cli   
'/path/to/nodejs-mobile-gyp/bin/node-gyp.js
',
gyp verb cli   
'build
',
gyp verb cli   
'--debug
' ]
gyp info using [email protected]
gyp info using [email protected] | linux | x64
gyp verb command build []
gyp verb build type Debug
gyp verb architecture x86_64
gyp verb node dev dir /path/to/nodejs-mobile-react-native/android/libnode/
gyp verb `which` succeeded for `make` /usr/bin/make
gyp info spawn make
gyp info spawn args [ 
'V=1
', 
'BUILDTYPE=Debug
', 
'-C
', 
'build
' ]
gyp info ok 
npm verb lifecycle @~build-debug: unsafe-perm in lifecycle true
npm verb lifecycle @~build-debug: CWD: /path/to/.cargo/registry/src/github.com-1ecc6299db9ec823/neon-runtime-0.2.0
npm info lifecycle @~postbuild-debug: @
npm verb exit [ 0, true ]
npm timing npm Completed in 254ms
npm info ok 
" }

It says gyp verb get node dir compiling against specified --nodedir dev files: /path/to/nodejs-mobile-react-native/android/libnode/, so it seems to use the correct npm_config_ env from the gradle task. (I've removed the command-line --nodedir= mentioned in the last comment)

@jaimecbernardo
Copy link
Member

Hi @stoically ,

The resulting binary files need to be linked to the nodejs-mobile shared library : https://github.com/janeasystems/nodejs-mobile/blob/2101f2096d59d2a081f64da2f10fd7385222ac8a/common.gypi#L517-L538

One way of debugging this would be to verify if the resulting ELF binary (the .node file) has a reference to the library. Here's an example, where I use the NDK's readelf tool for arm64 binaries to check it in a macOS machine:

$ANDROID_NDK_HOME/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-readelf -d android/build/nodejs-native-assets/nodejs-native-assets-arm64-v8a/node_modules/sha3/build/Release/sha3.node | grep "NEEDED"

This gives the following output:

 0x0000000000000001 (NEEDED)             Shared library: [liblog.so]
 0x0000000000000001 (NEEDED)             Shared library: [libnode.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc++_shared.so]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so]
 0x0000000000000001 (NEEDED)             Shared library: [libdl.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so]

The line for [libnode.so] is needed in order for the runtime dynamic loader in Android to find the symbols at runtime.

@stoically
Copy link
Author

stoically commented May 16, 2019

The neon.node binary which is generated by the neon-runtime from the node-gyp call internally indeed links to the libnode.so

$ANDROID_NDK_HOME/toolchains/x86_64-4.9/prebuilt/linux-x86_64/bin/x86_64-linux-android-readelf -d /home/user/.cargo/registry/src/github.com-1ecc6299db9ec823/neon-runtime-0.2.0/build/Debug/obj.target/neon.node | grep "NEEDED"
 0x0000000000000001 (NEEDED)             Shared library: [liblog.so]
 0x0000000000000001 (NEEDED)             Shared library: [libnode.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc++_shared.so]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so]
 0x0000000000000001 (NEEDED)             Shared library: [libdl.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so]

But that binary is not used. The neon-runtime proceeds to compile a libneon.a based on the neon.o (build from node-gyp). That libneon.a is linked into the neon-runtime crate. Finally the resulting dylib (lib<neon-project-name>.so) is copied to native/index.node.

And the resulting index.node binary, obviously, ends up not being linked against libnode.so.

$ANDROID_NDK_HOME/toolchains/x86_64-4.9/prebuilt/linux-x86_64/bin/x86_64-linux-android-readelf -d android/build/nodejs-native-assets-temp-build/nodejs-native-assets-x86_64/nodejs-project/node_modules/neon-test/native/index.node | grep "NEEDED"
 0x0000000000000001 (NEEDED)             Shared library: [libdl.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so]

So my best guess at the moment is that it's needed to let the neon-runtime Cargo.toml point to the nodejs-mobile lib files in its link section.

@stoically
Copy link
Author

stoically commented May 16, 2019

Linking the libnode.so worked by putting links = "node" in the neon project native/Cargo.toml and

[target.x86_64-linux-android.node]
rustc-link-search = ["/path/to/nodejs-mobile-react-native/android/libnode/bin/x86_64"]
rustc-link-lib = ["node"]

in the native/.cargo/config

Now adb logcat tells me the following when starting the app:

05-16 20:06:45.983 16152 16200 E NODEJS-MOBILE: internal/modules/cjs/loader.js:717
05-16 20:06:45.983 16152 16200 E NODEJS-MOBILE:   return process.dlopen(module, path.toNamespacedPath(filename));
05-16 20:06:45.983 16152 16200 E NODEJS-MOBILE:                  ^
05-16 20:06:45.983 16152 16200 E NODEJS-MOBILE: 
05-16 20:06:45.983 16152 16200 E NODEJS-MOBILE: Error: Module did not self-register.

So my next best guess was that libc++_shared.so and liblog.so are needed too. I've tried linking the /path/to/android/build/standalone-toolchains/x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so, but that results in

error: linking with `/path/to/android/build/standalone-toolchains/x86_64/bin/x86_64-linux-android-clang++` failed: exit code: 1
  = note: /path/to/android/build/standalone-toolchains/x86_64/bin/../lib/gcc/x86_64-linux-android/4.9.x/../../../../x86_64-linux-android/bin/ld: error: /path/to/android/build/standalone-toolchains/x86_64/sysroot/usr/lib/x86_64-linux-android/libc.a(sse2-memset-slm.o):
requires dynamic R_X86_64_PC32 reloc against '__memset_chk_fail' which may overflow at runtime;
recompile with -fPIC

I'm not sure what exactly needs to compiled with -fPIC and if using the libc++_shared.so from the toolchain is even the right approach. Maybe you got another helpful hint?

@jaimecbernardo
Copy link
Member

Hi @stoically ,

The error here is Error: Module did not self-register.

Node Modules register themselves by declaring themselves through NODE_MODULE, which is a macro that defines a function that's supposed to run when you dlopen the module. The function is marked as a __attribute__((constructor)) for this behavior, so that the function is registered in the .ctor section of the ELF binary.

Here's an example of such a function being declared:
https://stackoverflow.com/a/41283828/4594761

Some build systems might not include these symbols when building something from a static library, since these symbols were not referenced anywhere in the code that's using the static library. You mentioned the module is first built as a static library and then into a shared library, so it's possible there's some issues there.

There's also some changes to nodejs-mobile headers that might be related to this, in order to properly build the nodejs-mobile library for iOS (builds into a static library that is used by a Framework project. For Rust, the function might need to be static, so this change might need to be reverted in the node headers:
7ee73e9
The node.h file is inside the --nodedir.

There might be some way to use the NDK's readelf (or similar) tool to see if the registered initialization function for the module you are building is properly exported in the final shared library, in order to debug this.

I hope this is helpful :)

@stoically
Copy link
Author

Hey @jaimecbernardo - thanks again for another, indeed helpful, hint!

Checked the registered constructors in the final shared library with objdump -Dr -j .init_array. The index.node compiled from neon itself contains

00000000024d8468 <_ZN4node18__LOAD_NEON_MODULE17h3645e986fe67a8c5E>:

which is the responsible code for calling node_register_module.

The index.node compiled from nodejs-mobile doesn't contain an .init_array section as you suspected. I've tried changing the node.h as you suggested and did a full rebuild, but that unfortunately didn't help either. So I guess I have to figure out why the ctor is not included in the final library. I have no experience regarding compilation of static/shared libraries, so yet another suggestion on where I could start looking would be much appreciated!

@jaimecbernardo
Copy link
Member

Hi @stoically ,

I'm not familiar enough with Rust's toolset to understand what could be going on, but my first step would be comparing the commands being run when compiling to run in the Desktop, if the symbols are being correctly exported to the final .so there. Whatever flag is being passed to include those symbols might not be supported in the NDK toolchain, perhaps?

@cgdusek
Copy link

cgdusek commented Sep 25, 2019

image

Added the missing npm_config environment variables to build-settings in neon/src/build-settings.ts and neon/lib/build-settings.js and now the module registers

image

image
image

@cgdusek
Copy link

cgdusek commented Oct 6, 2019

Forgot to add that you need to also add in the android/iOS target here like so:

#[cfg_attr(target_os = "android", link_section = ".ctors")]

https://github.com/neon-bindings/neon/blob/37191e316e12bdbd8f84d3cd42739263d3312455/src/lib.rs#L87

@stoically
Copy link
Author

@cgdusek Cool stuff, thanks! I guess we can close this issue then. Would you be interested in opening an PR against Neon with those changes?

@cgdusek
Copy link

cgdusek commented Oct 30, 2019

Yeah, I will do it when I get a little bit of time. The neon runtime also needs to have the nodejs-mobile-nodegyp dependency like this. The Neon team is currently working on a different methodology for the Node bindings so I have been hesitant to PR waiting for the outcome of that work.

Screenshot_20191030_095746

@staltz
Copy link

staltz commented Oct 14, 2020

I've been following this thread closely, and the tips you've put @stoically @cgdusek @jaimecbernardo have been very helpful.

I managed to get my Neon-built npm package working with nodejs-mobile, and for reference I'll list here the right configurations and changes that made it possible:

These changes to the neon repo are necessary (I will submit a PR):

──────────────────────────────────────────────────────────────────────────────────────────────────────────
modified: cli/src/build-settings.ts
──────────────────────────────────────────────────────────────────────────────────────────────────────────
@ cli/src/build-settings.ts:60 @ export default class BuildSettings {
      npm_config_disturl:           process.env.npm_config_disturl || null,
      npm_config_runtime:           process.env.npm_config_runtime || null,
      npm_config_build_from_source: process.env.npm_config_build_from_source || null,
-     npm_config_devdir:            process.env.npm_config_devdir || null
+     npm_config_devdir:            process.env.npm_config_devdir || null,
+     npm_config_node_engine:       process.env.npm_config_node_engine || null,
+     npm_config_nodedir:           process.env.npm_config_nodedir || null,
+     npm_config_node_gyp:          process.env.npm_config_node_gyp || null,
+     npm_config_platform:          process.env.npm_config_platform || null
    });
  }

──────────────────────────────────────────────────────────────────────────────────────────────────────────
modified: crates/neon-sys/build.rs
──────────────────────────────────────────────────────────────────────────────────────────────────────────
@ crates/neon-sys/build.rs:97 @ mod build {
    //
    //     gyp verb architecture ia32
    fn parse_node_arch(node_gyp_output: &str) -> String {
-       let version_regex = Regex::new(r"gyp verb architecture (?P<arch>ia32|x64)").unwrap();
+       let version_regex = Regex::new(r"gyp verb architecture (?P<arch>ia32|x64|arm|arm64)").unwrap();
        let captures = version_regex.captures(&node_gyp_output).unwrap();
        String::from(&captures["arch"])
    }
──────────────────────────────────────────────────────────────────────────────────────────────────────────
modified: src/lib.rs
──────────────────────────────────────────────────────────────────────────────────────────────────────────
@ src/lib.rs:108 @ macro_rules! register_module {
        // Mark this function as a global constructor (like C++).
        #[allow(improper_ctypes)]
        #[cfg_attr(target_os = "linux", link_section = ".ctors")]
+       #[cfg_attr(target_os = "android", link_section = ".ctors")]
        #[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
        #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
        #[used]

These rustup targets need to be installed:

rustup target add aarch64-linux-android
rustup target add armv7-linux-androideabi
rustup target add arm-linux-androideabi

The Cargo.toml of the Neon-built npm package needs this:

diff --git a/node_modules/ssb-neon-keys/native/Cargo.toml b/node_modules/ssb-neon-keys/native/Cargo.toml
index 4e72059..6fc3dbb 100644
--- a/node_modules/ssb-neon-keys/native/Cargo.toml
+++ b/node_modules/ssb-neon-keys/native/Cargo.toml
@@ -4,6 +4,7 @@ version = "8.0.0"
 authors = ["Andre Staltz <[email protected]>"]
 license = "AGPL-3.0"
 build = "build.rs"
+links = "node"
 exclude = ["artifacts.json", "index.node"]
 edition = "2018"

The Neon-built npm package needs this additional config file (it uses absolute paths, so this file should be created locally via a script):

node_modules/ssb-neon-keys/native/.cargo/config.toml

[target.aarch64-linux-android.node]
rustc-link-search = ["/home/me/absolute/path/to/project/node_modules/nodejs-mobile-react-native/android/libnode/bin/arm64-v8a"]
rustc-link-lib = ["node"]

[target.armv7-linux-androideabi.node]
rustc-link-search = ["/home/me/absolute/path/to/project/node_modules/nodejs-mobile-react-native/android/libnode/bin/armeabi-v7a"]
rustc-link-lib = ["node"]

[target.arm-linux-androideabi.node]
rustc-link-search = ["/home/me/absolute/path/to/project/node_modules/nodejs-mobile-react-native/android/libnode/bin/armeabi-v7a"]
rustc-link-lib = ["node"]

The project compiled correctly and ran successfully in runtime. Only on Android. I'm still trying to figure out iOS and would I appreciate a lot if anyone has made progress on iOS.

@staltz
Copy link

staltz commented Oct 16, 2020

I just figured out iOS support! Compiled and tested on an iPad. You should assume that the following changes build on top of my previous comment, I can't guarantee that this will work if you don't apply the changes from the previous comment.

These changes to the neon repo are necessary (I will submit a PR):

──────────────────────────────────────────────────────────────────────────────────────────────────────────
modified: src/lib.rs
──────────────────────────────────────────────────────────────────────────────────────────────────────────
@ src/lib.rs:108 @ macro_rules! register_module {
        // Mark this function as a global constructor (like C++).
        #[allow(improper_ctypes)]
        #[cfg_attr(target_os = "linux", link_section = ".ctors")]
        #[cfg_attr(target_os = "android", link_section = ".ctors")]
        #[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
+       #[cfg_attr(target_os = "ios", link_section = "__DATA,__mod_init_func")]
        #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
        #[used]

These rustup targets need to be installed:

rustup target add aarch64-apple-ios
rustup target add x86_64-apple-ios

nodejs-mobile-react-native/scripts/ios-build-native-modules.sh needs these changes to add CARGO_BUILD_TARGET (I will submit a PR):

 pushd $CODESIGNING_FOLDER_PATH/nodejs-project/
 if [ "$PLATFORM_NAME" == "iphoneos" ]
 then
-  GYP_DEFINES="OS=ios" npm_config_nodedir="$NODEJS_HEADERS_DIR" npm_config_node_gyp="$NODEJS_MOBILE_GYP_BIN_FILE" npm_config_platform="ios" npm_config_format="make-ios" npm_config_node_engine="chakracore" npm_config_arch="arm64" npm --verbose rebuild --build-from-source
+  GYP_DEFINES="OS=ios" CARGO_BUILD_TARGET="aarch64-apple-ios" npm_config_nodedir="$NODEJS_HEADERS_DIR" npm_config_node_gyp="$NODEJS_MOBILE_GYP_BIN_FILE" npm_config_platform="ios" npm_config_format="make-ios" npm_config_node_engine="chakracore" npm_config_arch="arm64" npm --verbose rebuild --build-from-source
else
-  GYP_DEFINES="OS=ios" npm_config_nodedir="$NODEJS_HEADERS_DIR" npm_config_node_gyp="$NODEJS_MOBILE_GYP_BIN_FILE" npm_config_platform="ios" npm_config_format="make-ios" npm_config_node_engine="chakracore" npm_config_arch="x64" npm --verbose rebuild --build-from-source
+  GYP_DEFINES="OS=ios" CARGO_BUILD_TARGET="x86_64-apple-ios" npm_config_nodedir="$NODEJS_HEADERS_DIR" npm_config_node_gyp="$NODEJS_MOBILE_GYP_BIN_FILE" npm_config_platform="ios" npm_config_format="make-ios" npm_config_node_engine="chakracore" npm_config_arch="x64" npm --verbose rebuild --build-from-source
 fi
 popd

Finally, the index.node files need to be converted to folders, this was important so that nodejs-mobile scripts create .framework files for these Cargo-built (not gyp-built) .node files. I did this by patching the package.json of the my neon npm packages, to add a postinstall script which runs after Cargo has built the index.node. I wrote a script in my project to automatically apply this patch after npm install. Maybe this should be part of Neon? I'm not sure. Anyway, here it is, a simple hack:

 {
   "name": "my-neon-package",
   ...
   "scripts": {
+    "postinstall": "mv native/index.node native/index && mkdir native/index.node && mv native/index native/index.node/index"
   }
 }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants