LeRoyce Pearson Blog

Compiling Rust Apps for Termux with nix-shell

I've been building a program to track how I spend my time, and a couple of the requirements are an interface on my phone, and sync between multiple devices. Since I haven't built an android app in forever, I decided to target termux, at least initially. This will allow me to use the app and test out the synchronization protocol without a ton of upfront effort into building a graphical user interface.

Cross Compiling for termux

The first thing we need to cross compile to termux is a cross compiler. rustc is a cross compiler by default, but with some important caveats: rustc needs a linker and a pre-made rust stdlib for the target platform.

I use the wonderful NixOS as my dev environment, so we will be using nix-shell to get the dependencies we need. In your project's directory, create an android.nix file and put the following code inside it.

# android.nix

let
  # Get the mozilla nixpkg overlay for a convient rust installation
  moz_overlay = import (
    builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz
  );
  
  native-pkgs = import <nixpkgs> { overlays = [ moz_overlay ]; };
  pkgs = import <nixpkgs> {
    crossSystem = native-pkgs.lib.systems.examples.armv7a-android-prebuilt;
  };
  
  # Get the latest stable compiler version and install the armv7 android target with it
  rust = (native-pkgs.rustChannels.stable.rust.override {
    targets = [
        "armv7-linux-androideabi"
    ];
  });
in
  # Note the call to `pkgs` here, instead of `native-pkgs`
  pkgs.callPackage (
    {mkShell, cargo}:
    
    # mkShell will drop us into a shell that has a $CC for the target platform
    mkShell {
      nativeBuildInputs = [ rust ];
    }
  ) {}

Running nix-shell android.nix will drop you into a shell with cargo and an armv7 c compiler. Before we can build the rust project, we have to inform cargo to use said compiler for linking armv7 executables. Open .cargo/config (in your project's directory) and put the following in:

[target.armv7-linux-androideabi]
linker = "armv7a-unknown-linux-androideabi-clang"

Now we can build our project termux! We simply start nix-shell and then tell cargo to compile the project:

$ nix-shell android.nix --run "cargo build --target=armv7-linux-androideabi"

Now just push the executable to your android device.

$ adb push target/armv7-linux-androideabi/debug/cli /storage/self/primary/our-cli-app

And then run the app from termux.

$ cp /storage/self/primary/our-cli-app ./
$ chmod ./our-cli-app
$ ./our-cli-app --help

Results

It works! :O

Picture of time tracker working in termux

Resources