The Why
I'm building a photo review app for Android, and I want to parse the full EXIF metadata in Canon RAW CR3 files.
I found no better tool than ExifTool by Phil Harvey. It works amazingly for this format and more!
Good news: It's platform-independent!
Not so good news: It needs Perl 😰
The How
After some digging, I found this article — Embedding Perl on Android — which covers compiling and using Perl on Android, and funny enough it also mentions using this for ExifTool 😆
I reviewed the prescribed steps, but found it to be using a now obsolete method for compilation.
🏁 Let's begin doing this the 2023 way!
Sorry Windows devs, the following assumes a UNIX-like setup, and I only tested this using macOS. Maybe WSL will work?
Here's the summary of my derived approach:
- Download and install the Android NDK
- Download the Perl source code
- Set up an ARM based emulator
- Prepare the environment for cross-compilation
- Compile Perl with
Configure
andmake
- Extract the
perl
binary andlib/
outputs - Try things out using ADB
1. Download and install the Android NDK
The easiest way I found to do this is to download the NDK (Side by side)
option under the SDK Tools
section of the Android SDK Manager (which comes with Android Studio).
It will be installed into your Android SDK Location under an ndk/<version>
sub directory.
2. Download the Perl source code
I downloaded the latest source from the same location as the article:
Comprehensive Perl Archive Network - Perl Source
curl -O https://www.cpan.org/src/5.0/perl-5.36.1.tar.gz
tar -xzf perl-5.36.1.tar.gz
cd perl-5.36.1
Notes
The website's page mentions:
Read both INSTALL and README.android [sic] in the perl-5.36.1 directory for more detailed information.
Doing so I found that there is actually some useful information about cross-compilation in general, and for Android specifically.
It mentions that there's two methods for cross-compilation, one that requires access to a target system, and a second which doesn't. Since it doesn't explain the second method, we'll setup a target system in the next step.
3. Set up an ARM based emulator
Create a device in the Virtual Device Manager
tool included in the Android SDK (or Android Studio).
You can use the recommended image, which will most likely be an arm64
image, if not select an ARM Image
manually instead.
After the device is created, ▶️ start the emulator.
This will be used as the target system with the target architecture for cross compilation.
After you finish this whole process, you may want to do it again and compile for different architectures. At this point you'll be creating other devices with the
armeabi
(32 bit arm) image, or thex86
andx86_64
images.
4. Prepare the environment for cross-compilation
This took some trial and error, so to spare you the pain here's a script that sets up the environment for you:
### Make sure to check/modify this path to match your NDK location
export NDK=$HOME/Library/Android/sdk/ndk/25.2.9519653
### Confirm this by running `adb devices`
export DEVICE=emulator-5554
### A temporary path on the device that will be used during cross-compilation
export TARGETDIR=/data/local/tmp
export TARGET_ARCH=aarch64-linux-android
### OR use one of these other architectures instead:
# export TARGET_ARCH=armv7a-linux-androideabi
# export TARGET_ARCH=x86_64-linux-android
# export TARGET_ARCH=i686-linux-android
export TARGET_LIB_ARCH=$TARGET_ARCH
### OR if using armv7a-linux-androideabi as TARGET_ARCH, use this instead
# export TARGET_LIB_ARCH=arm-linux-androideabi
export HOST_TAG=darwin-x86_64
### OR if using linux, use this instead:
# export HOST_TAG=linux-x86_64
## The API level of the Android version you are targeting
export BUILD_VERSION=23
## (Could also maybe use 21, but may run into compilation issues)
export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/$HOST_TAG
export SYSROOT=$TOOLCHAIN/sysroot
export LIB_ARCH=$TOOLCHAIN/sysroot/usr/lib/$TARGET_LIB_ARCH/$BUILD_VERSION
export AR=$TOOLCHAIN/bin/llvm-ar
export AS=$TOOLCHAIN/bin/llvm-a
export CC=$TOOLCHAIN/bin/${TARGET_ARCH}${BUILD_VERSION}-clang
export CXX=$TOOLCHAIN/bin/${TARGET_ARCH}${BUILD_VERSION}-clang++
export LD=$TOOLCHAIN/bin/ld
export RANLIB=$TOOLCHAIN/bin/llvm-ranlib
export STRIP=$TOOLCHAIN/bin/llvm-strip
export PATH=$PATH:$TOOLCHAIN/bin
5. Compile Perl with Configure
and make
After executing the environment set-up script, execute the configure script:
./Configure -des -Dusecrosscompile -Dtargetarch=$TARGET_ARCH -Dtargetrun=adb -Dcc=$CC -Dcxx=$CXX -Dranlib=$RANLIB -Dsysroot=$SYSROOT -Dlibpth=$LIB_ARCH -Dtargetdir=$TARGETDIR -Dtargethost=$DEVICE
If you get this error: ./Configure: line 4700: ./try: cannot execute binary file
Try editing the ./Configure
file and modifying the source code to comment out lines slightly before 4700 # Really old versions of gcc
for about 53 lines until $rm -f try try.*
(Click here to see the exact lines I commented out for reference.)
# $cat >try.c <<'EOCP'
# int main(int argc, char **argv) {
# argc = argc + 1;
# /* This is deliberately a declaration after a statement. */
# unsigned long long count = 0;
# for (char **p = argv; *p; ++p) {
# ++count;
# }
# return count == 1 ? 0 : argc;
# }
# EOCP
# c99_for=no
# for flag in '' '-std=gnu99' '-std=c99'; do
# if $cc -o try $flag $ccflags $ldflags try.c 2>/dev/null && ./try; then
# c99_for="$flag"
# break;
# fi
# done
# case "$c99_for" in
# '') echo "Your C compiler doesn't need any special flags to compile C99 code"
# ;;
# no) echo >&4 "Your C compiler doesn't seem to be able to compile C99 code"
# rp='Do you really want to continue?'
# dflt='n'
# . ./myread
# case "$ans" in
# [yY]) echo >&4 "Okay, continuing." ;;
# *) exit 1 ;;
# esac
# ;;
# *) echo "Your C compiler needs $c99_for to compile C99 code"
# ccflags="$c99_for $ccflags"
# ;;
# esac
# $rm -f try try.*
And compile it all by executing:
make
6. Extract the perl
binary and lib/
outputs
If everything ran without error, you'll find that a perl
binary file is now in your working directory (the source file directory we started in)
Congratulations, we have compiled Perl for Android 🎉
Now let's copy this perl
binary file and the lib/
directory and run to test these outputs in the target system.
7. Try things out using ADB
Push the output files into the emulator
adb push perl /data/local/tmp
adb push lib/ /data/local/tmp
Start an ADB shell
adb shell
In the shell try out your Perl distribution 🤩
cd /data/local/tmp
export PERL5LIB=/data/local/tmp/lib
./perl -e "print \"Hello from Android!\";"
It should print something out, if not try to make the file executable with
chmod +x ./perl
That's it! Hopefully this all worked for you.
If you want more ideas on how to use your Perl in an Android app the article I found has some ideas.
Let me know if you run into issues in the comments and I will try to work it out with you 🙌
Top comments (0)