Makefile: make unstripped libtailscale and debug symbols (#619)

- Added Makefile targets to build an unstripped version of the libtailscale AAR.
- Extracted the native .so (libgojni.so) from the unstripped AAR.
- Generated a debug symbols file (libgojni.so.debug) using llvm-objcopy with --only-keep-debug.
- Stripped the native library (libgojni.so.stripped) with --strip-debug and repackaged the final AAR.

This allows the build chain to produce a stripped AAR for the app and a separate debug symbols file that can be uploaded to Google Play for crash deobfuscation.

Signed-off-by: kari-ts <kari@tailscale.com>
Updates tailscale/tailscale#15210
pull/623/head
kari-ts 9 months ago committed by GitHub
parent 189de89466
commit 141b1cff45
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

7
.gitignore vendored

@ -35,6 +35,13 @@ tailscale.jks
.vscode
.idea
# Native libraries
*.stripped
*.unstripped
# Debug symbols
*.debug
libtailscale.aar
libtailscale-sources.jar
.DS_Store

@ -10,37 +10,56 @@
# with this name, it will be used.
#
# The convention here is tailscale-android-build-amd64-<date>
DOCKER_IMAGE=tailscale-android-build-amd64-191124
DOCKER_IMAGE := tailscale-android-build-amd64-191124
export TS_USE_TOOLCHAIN=1
DEBUG_APK=tailscale-debug.apk
RELEASE_AAB=tailscale-release.aab
RELEASE_TV_AAB=tailscale-tv-release.aab
LIBTAILSCALE=android/libs/libtailscale.aar
# Extract the version code from build.gradle.
# Auto-select an NDK from ANDROID_HOME (choose highest version available)
NDK_ROOT ?= $(shell ls -1d $(ANDROID_HOME)/ndk/* 2>/dev/null | sort -V | tail -n 1)
HOST_OS := $(shell uname | tr A-Z a-z)
ifeq ($(HOST_OS),linux)
STRIP_TOOL := $(NDK_ROOT)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-objcopy
else ifeq ($(HOST_OS),darwin)
STRIP_TOOL := $(NDK_ROOT)/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-objcopy
endif
$(info Using NDK_ROOT: $(NDK_ROOT))
$(info Using STRIP_TOOL: $(STRIP_TOOL))
DEBUG_APK := tailscale-debug.apk
RELEASE_AAB := tailscale-release.aab
RELEASE_TV_AAB := tailscale-tv-release.aab
# Define output filenames.
LIBTAILSCALE_AAR := android/libs/libtailscale.aar
UNSTRIPPED_AAR := android/libs/libtailscale_unstripped.aar
ARM64_SO_PATH := jni/arm64-v8a/libgojni.so
# Compute an absolute path for the unstripped AAR.
ABS_UNSTRIPPED_AAR := $(shell pwd)/$(UNSTRIPPED_AAR)
# Android SDK & Tools settings.
ifeq ($(shell uname),Linux)
ANDROID_TOOLS_URL="https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip"
ANDROID_TOOLS_SUM="bd1aa17c7ef10066949c88dc6c9c8d536be27f992a1f3b5a584f9bd2ba5646a0 commandlinetools-linux-9477386_latest.zip"
ANDROID_TOOLS_URL := "https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip"
ANDROID_TOOLS_SUM := "bd1aa17c7ef10066949c88dc6c9c8d536be27f992a1f3b5a584f9bd2ba5646a0 commandlinetools-linux-9477386_latest.zip"
else
ANDROID_TOOLS_URL="https://dl.google.com/android/repository/commandlinetools-mac-9477386_latest.zip"
ANDROID_TOOLS_SUM="2072ffce4f54cdc0e6d2074d2f381e7e579b7d63e915c220b96a7db95b2900ee commandlinetools-mac-9477386_latest.zip"
ANDROID_TOOLS_URL := "https://dl.google.com/android/repository/commandlinetools-mac-9477386_latest.zip"
ANDROID_TOOLS_SUM := "2072ffce4f54cdc0e6d2074d2f381e7e579b7d63e915c220b96a7db95b2900ee commandlinetools-mac-9477386_latest.zip"
endif
ANDROID_SDK_PACKAGES='platforms;android-31' 'extras;android;m2repository' 'ndk;23.1.7779620' 'platform-tools' 'build-tools;33.0.2'
ANDROID_SDK_PACKAGES := 'platforms;android-31' 'extras;android;m2repository' 'ndk;23.1.7779620' 'platform-tools' 'build-tools;33.0.2'
# Attempt to find an ANDROID_SDK_ROOT / ANDROID_HOME based either from
# preexisting environment or common locations.
export ANDROID_SDK_ROOT ?= $(shell find $$ANDROID_SDK_ROOT $$ANDROID_HOME $$HOME/Library/Android/sdk $$HOME/Android/Sdk $$HOME/AppData/Local/Android/Sdk /usr/lib/android-sdk -maxdepth 1 -type d 2>/dev/null | head -n 1)
# If ANDROID_SDK_ROOT is still unset, set it to a default location by platform.
ifeq ($(ANDROID_SDK_ROOT),)
ifeq ($(shell uname),Linux)
export ANDROID_SDK_ROOT=$(HOME)/Android/Sdk
export ANDROID_SDK_ROOT := $(HOME)/Android/Sdk
else ifeq ($(shell uname),Darwin)
export ANDROID_SDK_ROOT=$(HOME)/Library/Android/sdk
else ifneq ($(WINDIR),))
export ANDROID_SDK_ROOT=$(HOME)/AppData/Local/Android/sdk
export ANDROID_SDK_ROOT := $(HOME)/Library/Android/sdk
else ifneq ($(WINDIR),)
export ANDROID_SDK_ROOT := $(HOME)/AppData/Local/Android/sdk
else
export ANDROID_SDK_ROOT=$(PWD)/android-sdk
export ANDROID_SDK_ROOT := $(PWD)/android-sdk
endif
endif
export ANDROID_HOME ?= $(ANDROID_SDK_ROOT)
@ -51,7 +70,6 @@ ANDROID_STUDIO_ROOT ?= $(shell find ~/android-studio /usr/local/android-studio /
# Set JAVA_HOME to the Android Studio bundled JDK.
export JAVA_HOME ?= $(shell find "$(ANDROID_STUDIO_ROOT)/jbr" "$(ANDROID_STUDIO_ROOT)/jre" "$(ANDROID_STUDIO_ROOT)/Contents/jbr/Contents/Home" "$(ANDROID_STUDIO_ROOT)/Contents/jre/Contents/Home" -maxdepth 1 -type d 2>/dev/null | head -n 1)
# If JAVA_HOME is still unset, remove it, because SDK tools go into a CPU spin if it is set and empty.
ifeq ($(JAVA_HOME),)
unexport JAVA_HOME
else
@ -59,7 +77,7 @@ else
endif
AVD_BASE_IMAGE := "system-images;android-33;google_apis;"
export HOST_ARCH=$(shell uname -m)
export HOST_ARCH := $(shell uname -m)
ifeq ($(HOST_ARCH),aarch64)
AVD_IMAGE := "$(AVD_BASE_IMAGE)arm64-v8a"
else ifeq ($(HOST_ARCH),arm64)
@ -72,46 +90,51 @@ export AVD_IMAGE
export AVD
# Use our toolchain or the one that is specified, do not perform dynamic toolchain switching.
GOTOOLCHAIN=local
GOTOOLCHAIN := local
export GOTOOLCHAIN
# TOOLCHAINDIR is set by fdoid CI and used by tool/* scripts.
TOOLCHAINDIR ?=
export TOOLCHAINDIR
GOBIN ?= $(PWD)/android/build/go/bin
GOBIN := $(PWD)/android/build/go/bin
export GOBIN
export PATH := $(PWD)/tool:$(GOBIN):$(ANDROID_HOME)/cmdline-tools/latest/bin:$(ANDROID_HOME)/platform-tools:$(PATH)
export GOROOT := # Unset
#
# Android Builds:
#
# ------------------------------------------------------------------------------
# Android Build Targets
# ------------------------------------------------------------------------------
.PHONY: debug-unstripped
debug-unstripped: build-unstripped-aar
@echo "Listing contents of $(ABS_UNSTRIPPED_AAR):"
unzip -l $(ABS_UNSTRIPPED_AAR)
.PHONY: apk
apk: $(DEBUG_APK) ## Build the debug APK
apk: $(DEBUG_APK)
.PHONY: tailscale-debug
tailscale-debug: $(DEBUG_APK) ## Build the debug APK
tailscale-debug: $(DEBUG_APK)
$(DEBUG_APK): libtailscale debug-symbols version gradle-dependencies build-unstripped-aar
(cd android && ./gradlew test assembleDebug)
install -C android/build/outputs/apk/debug/android-debug.apk $@
# Builds the release AAB and signs it (phone/tablet/chromeOS variant)
.PHONY: release
release: jarsign-env $(RELEASE_AAB) ## Build the release AAB
release: jarsign-env $(RELEASE_AAB)
@jarsigner -sigalg SHA256withRSA -digestalg SHA-256 -keystore $(JKS_PATH) -storepass $(JKS_PASSWORD) $(RELEASE_AAB) tailscale
# Builds the release AAB and signs it (androidTV variant)
.PHONY: release-tv
release-tv: jarsign-env $(RELEASE_TV_AAB) ## Build the release AAB
release-tv: jarsign-env $(RELEASE_TV_AAB)
@jarsigner -sigalg SHA256withRSA -digestalg SHA-256 -keystore $(JKS_PATH) -storepass $(JKS_PASSWORD) $(RELEASE_TV_AAB) tailscale
# gradle-dependencies groups together the android sources and libtailscale needed to assemble tests/debug/release builds.
.PHONY: gradle-dependencies
gradle-dependencies: $(shell find android -type f -not -path "android/build/*" -not -path '*/.*') $(LIBTAILSCALE) tailscale.version
$(DEBUG_APK): version gradle-dependencies
(cd android && ./gradlew test assembleDebug)
install -C android/build/outputs/apk/debug/android-debug.apk $@
gradle-dependencies: $(shell find android -type f -not -path "android/build/*" -not -path '*/.*') $(LIBTAILSCALE_AAR) tailscale.version
$(RELEASE_AAB): version gradle-dependencies
@echo "Building release AAB"
@ -128,50 +151,93 @@ tailscale-test.apk: version gradle-dependencies
install -C ./android/build/outputs/apk/androidTest/applicationTest/android-applicationTest-androidTest.apk $@
tailscale.version: go.mod go.sum $(wildcard .git/HEAD)
$(shell ./tool/go run tailscale.com/cmd/mkversion > tailscale.version)
@bash -c "./tool/go run tailscale.com/cmd/mkversion > tailscale.version"
.PHONY: version
version: tailscale.version ## print the current version information
cat tailscale.version
version: tailscale.version
@cat tailscale.version
#
# Go Builds:
#
# ------------------------------------------------------------------------------
# Go Build Targets (Unstripped AAR, Debug Symbols, Stripped SO, Packaging)
# ------------------------------------------------------------------------------
android/libs:
mkdir -p android/libs
$(GOBIN)/gomobile: $(GOBIN)/gobind go.mod go.sum
$(GOBIN):
mkdir -p $(GOBIN)
$(GOBIN)/gomobile: $(GOBIN)/gobind go.mod go.sum | $(GOBIN)
./tool/go install golang.org/x/mobile/cmd/gomobile
$(GOBIN)/gobind: go.mod go.sum
./tool/go install golang.org/x/mobile/cmd/gobind
$(LIBTAILSCALE): Makefile android/libs $(shell find libtailscale -name *.go) go.mod go.sum $(GOBIN)/gomobile tailscale.version
.PHONY: build-unstripped-aar
build-unstripped-aar: tailscale.version $(GOBIN)/gomobile
@echo "Running gomobile bind to generate unstripped AAR..."
@echo "Output file: $(ABS_UNSTRIPPED_AAR)"
mkdir -p $(dir $(ABS_UNSTRIPPED_AAR))
rm -f $(ABS_UNSTRIPPED_AAR)
$(GOBIN)/gomobile bind -target android -androidapi 26 \
-tags "$$(./build-tags.sh)" \
-ldflags "-w $$(./version-ldflags.sh)" \
-o $@ ./libtailscale
-ldflags "$$(./version-ldflags.sh)" \
-o $(ABS_UNSTRIPPED_AAR) ./libtailscale || { echo "gomobile bind failed"; exit 1; }
@if [ ! -f $(ABS_UNSTRIPPED_AAR) ]; then \
echo "Error: $(ABS_UNSTRIPPED_AAR) was not created"; exit 1; \
fi
@echo "Generated unstripped AAR: $(ABS_UNSTRIPPED_AAR)"
$(UNSTRIPPED_AAR): build-unstripped-aar
libgojni.so.unstripped: $(UNSTRIPPED_AAR)
@echo "Extracting libgojni.so from unstripped AAR..."
@if unzip -p $(ABS_UNSTRIPPED_AAR) jni/arm64-v8a/libgojni.so > libgojni.so.unstripped; then \
echo "Found arm64-v8a libgojni.so"; \
elif unzip -p $(ABS_UNSTRIPPED_AAR) jni/armeabi-v7a/libgojni.so > libgojni.so.unstripped; then \
echo "Found armeabi-v7a libgojni.so"; \
else \
echo "Neither jni/arm64-v8a/libgojni.so nor jni/armeabi-v7a/libgojni.so was found."; \
echo "Listing contents of $(ABS_UNSTRIPPED_AAR):"; \
unzip -l $(ABS_UNSTRIPPED_AAR); exit 1; \
fi
libgojni.so.debug: libgojni.so.unstripped
@echo "Extracting debug symbols from libgojni.so..."
$(STRIP_TOOL) --only-keep-debug libgojni.so.unstripped libgojni.so.debug
libgojni.so.stripped: libgojni.so.unstripped
@echo "Stripping debug symbols from libgojni.so..."
$(STRIP_TOOL) --strip-debug libgojni.so.unstripped libgojni.so.stripped
$(LIBTAILSCALE_AAR): libgojni.so.stripped $(UNSTRIPPED_AAR)
@echo "Repackaging AAR with stripped libgojni.so..."
rm -rf temp_aar
mkdir temp_aar
unzip $(ABS_UNSTRIPPED_AAR) -d temp_aar
cp libgojni.so.stripped temp_aar/$(ARM64_SO_PATH)
(cd temp_aar && zip -r ../$(LIBTAILSCALE_AAR) .)
rm -rf temp_aar
.PHONY: libtailscale
libtailscale: $(LIBTAILSCALE) ## Build the libtailscale AAR
libtailscale: $(LIBTAILSCALE_AAR) ## Build the stripped libtailscale AAR
#
# Utility tasks:
#
.PHONY: debug-symbols
debug-symbols: libgojni.so.debug
.PHONY: all
all: test $(DEBUG_APK) ## Build and test everything
# ------------------------------------------------------------------------------
# Utility Targets
# ------------------------------------------------------------------------------
.PHONY: env
env:
@echo PATH=$(PATH)
@echo ANDROID_SDK_ROOT=$(ANDROID_SDK_ROOT)
@echo ANDROID_HOME=$(ANDROID_HOME)
@echo ANDROID_STUDIO_ROOT=$(ANDROID_STUDIO_ROOT)
@echo JAVA_HOME=$(JAVA_HOME)
@echo TOOLCHAINDIR=$(TOOLCHAINDIR)
@echo AVD_IMAGE="$(AVD_IMAGE)"
@echo "PATH=$(PATH)"
@echo "ANDROID_SDK_ROOT=$(ANDROID_SDK_ROOT)"
@echo "ANDROID_HOME=$(ANDROID_HOME)"
@echo "ANDROID_STUDIO_ROOT=$(ANDROID_STUDIO_ROOT)"
@echo "JAVA_HOME=$(JAVA_HOME)"
@echo "TOOLCHAINDIR=$(TOOLCHAINDIR)"
@echo "AVD_IMAGE=$(AVD_IMAGE)"
# Ensure that JKS_PATH and JKS_PASSWORD are set before we attempt a build
# that requires signing.
@ -195,21 +261,20 @@ androidpath:
@echo 'export PATH=$(ANDROID_HOME)/cmdline-tools/latest/bin:$(ANDROID_HOME)/platform-tools:$$PATH'
.PHONY: tag_release
tag_release: tailscale.version ## Tag the current commit with the current version
tag_release: debug-symbols tailscale.version ## Tag the current commit with the current version
source tailscale.version && git tag -a "$${VERSION_LONG}" -m "OSS and Version updated to $${VERSION_LONG}"
.PHONY: bumposs ## Bump to the latest oss and update the versions.
bumposs: update-oss tailscale.version
source tailscale.version && git commit -sm "android: bump OSS" -m "OSS and Version updated to $${VERSION_LONG}" go.toolchain.rev android/build.gradle go.mod go.sum
source tailscale.version && git tag -a "$${VERSION_LONG}" -m "OSS and Version updated to $${VERSION_LONG}"
.PHONY: bump_version_code
bump_version_code: ## Bump the version code in build.gradle
.PHONY: bump_version_code ## Bump the version code in build.gradle
bump_version_code:
sed -i'.bak' "s/versionCode .*/versionCode $$(expr $$(awk '/versionCode ([0-9]+)/{print $$2}' android/build.gradle) + 1)/" android/build.gradle && rm android/build.gradle.bak
.PHONY: update-oss
update-oss: ## Update the tailscale.com go module
.PHONY: update-oss ## Update the tailscale.com go module
update-oss:
GOPROXY=direct ./tool/go get tailscale.com@main
./tool/go mod tidy -compat=1.24
./tool/go run tailscale.com/cmd/printdep --go > go.toolchain.rev.new
@ -301,7 +366,7 @@ docker-remove-shell-image: ## Removes all docker shell image
.PHONY: clean
clean: ## Remove build artifacts. Does not purge docker build envs. Use dockerRemoveEnv for that.
@echo "Cleaning up old build artifacts"
-rm -rf android/build $(DEBUG_APK) $(RELEASE_AAB) $(RELEASE_TV_AAB) $(LIBTAILSCALE) android/libs *.apk *.aab
-rm -rf android/build $(DEBUG_APK) $(RELEASE_AAB) $(RELEASE_TV_AAB) $(LIBTAILSCALE_AAR) android/libs *.apk *.aab
@echo "Cleaning cached toolchain"
-rm -rf $(HOME)/.cache/tailscale-go{,.extracted}
-pkill -f gradle

Loading…
Cancel
Save