1010# with this name, it will be used.
1111#
1212# The convention here is tailscale-android-build-amd64-<date>
13- DOCKER_IMAGE = tailscale-android-build-amd64-191124
13+ DOCKER_IMAGE := tailscale-android-build-amd64-191124
1414export TS_USE_TOOLCHAIN =1
1515
16- DEBUG_APK =tailscale-debug.apk
17- RELEASE_AAB =tailscale-release.aab
18- RELEASE_TV_AAB =tailscale-tv-release.aab
19- LIBTAILSCALE =android/libs/libtailscale.aar
20- # Extract the version code from build.gradle.
16+ # Auto-select an NDK from ANDROID_HOME (choose highest version available)
17+ NDK_ROOT ?= $(shell ls -1d $(ANDROID_HOME ) /ndk/* 2>/dev/null | sort -V | tail -n 1)
18+
19+ HOST_OS := $(shell uname | tr A-Z a-z)
20+ ifeq ($(HOST_OS ) ,linux)
21+ STRIP_TOOL := $(NDK_ROOT ) /toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-objcopy
22+ else ifeq ($(HOST_OS),darwin)
23+ STRIP_TOOL := $(NDK_ROOT ) /toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-objcopy
24+ endif
25+
26+ $(info Using NDK_ROOT : $(NDK_ROOT ) )
27+ $(info Using STRIP_TOOL : $(STRIP_TOOL ) )
28+
29+ DEBUG_APK := tailscale-debug.apk
30+ RELEASE_AAB := tailscale-release.aab
31+ RELEASE_TV_AAB := tailscale-tv-release.aab
32+
33+ # Define output filenames.
34+ LIBTAILSCALE_AAR := android/libs/libtailscale.aar
35+ UNSTRIPPED_AAR := android/libs/libtailscale_unstripped.aar
36+ ARM64_SO_PATH := jni/arm64-v8a/libgojni.so
37+
38+ # Compute an absolute path for the unstripped AAR.
39+ ABS_UNSTRIPPED_AAR := $(shell pwd) /$(UNSTRIPPED_AAR )
40+
41+ # Android SDK & Tools settings.
2142ifeq ($(shell uname) ,Linux)
22- ANDROID_TOOLS_URL= "https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip"
23- ANDROID_TOOLS_SUM= "bd1aa17c7ef10066949c88dc6c9c8d536be27f992a1f3b5a584f9bd2ba5646a0 commandlinetools-linux-9477386_latest.zip"
43+ ANDROID_TOOLS_URL := "https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip"
44+ ANDROID_TOOLS_SUM := "bd1aa17c7ef10066949c88dc6c9c8d536be27f992a1f3b5a584f9bd2ba5646a0 commandlinetools-linux-9477386_latest.zip"
2445else
25- ANDROID_TOOLS_URL= "https://dl.google.com/android/repository/commandlinetools-mac-9477386_latest.zip"
26- ANDROID_TOOLS_SUM= "2072ffce4f54cdc0e6d2074d2f381e7e579b7d63e915c220b96a7db95b2900ee commandlinetools-mac-9477386_latest.zip"
46+ ANDROID_TOOLS_URL := "https://dl.google.com/android/repository/commandlinetools-mac-9477386_latest.zip"
47+ ANDROID_TOOLS_SUM := "2072ffce4f54cdc0e6d2074d2f381e7e579b7d63e915c220b96a7db95b2900ee commandlinetools-mac-9477386_latest.zip"
2748endif
28- ANDROID_SDK_PACKAGES = 'platforms;android-31' 'extras;android;m2repository' 'ndk;23.1.7779620' 'platform-tools' 'build-tools;33.0.2'
49+ ANDROID_SDK_PACKAGES := 'platforms;android-31' 'extras;android;m2repository' 'ndk;23.1.7779620' 'platform-tools' 'build-tools;33.0.2'
2950
3051# Attempt to find an ANDROID_SDK_ROOT / ANDROID_HOME based either from
3152# preexisting environment or common locations.
3253export 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)
33-
34- # If ANDROID_SDK_ROOT is still unset, set it to a default location by platform.
3554ifeq ($(ANDROID_SDK_ROOT ) ,)
36- ifeq ($(shell uname),Linux)
37- export ANDROID_SDK_ROOT= $(HOME)/Android/Sdk
38- else ifeq ($(shell uname),Darwin)
39- export ANDROID_SDK_ROOT= $(HOME)/Library/Android/sdk
40- else ifneq ($(WINDIR),) )
41- export ANDROID_SDK_ROOT= $(HOME)/AppData/Local/Android/sdk
42- else
43- export ANDROID_SDK_ROOT= $(PWD)/android-sdk
44- endif
55+ ifeq ($(shell uname),Linux)
56+ export ANDROID_SDK_ROOT := $(HOME ) /Android/Sdk
57+ else ifeq ($(shell uname),Darwin)
58+ export ANDROID_SDK_ROOT := $(HOME ) /Library/Android/sdk
59+ else ifneq ($(WINDIR),)
60+ export ANDROID_SDK_ROOT := $(HOME ) /AppData/Local/Android/sdk
61+ else
62+ export ANDROID_SDK_ROOT := $(PWD ) /android-sdk
63+ endif
4564endif
4665export ANDROID_HOME ?= $(ANDROID_SDK_ROOT )
4766
@@ -51,67 +70,71 @@ ANDROID_STUDIO_ROOT ?= $(shell find ~/android-studio /usr/local/android-studio /
5170
5271# Set JAVA_HOME to the Android Studio bundled JDK.
5372export 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)
54- # If JAVA_HOME is still unset, remove it, because SDK tools go into a CPU spin if it is set and empty.
5573ifeq ($(JAVA_HOME ) ,)
56- unexport JAVA_HOME
74+ unexport JAVA_HOME
5775else
58- export PATH := $(JAVA_HOME)/bin:$(PATH)
76+ export PATH := $(JAVA_HOME ) /bin:$(PATH )
5977endif
6078
6179AVD_BASE_IMAGE := "system-images;android-33;google_apis;"
62- export HOST_ARCH = $(shell uname -m)
80+ export HOST_ARCH := $(shell uname -m)
6381ifeq ($(HOST_ARCH ) ,aarch64)
64- AVD_IMAGE := "$(AVD_BASE_IMAGE)arm64-v8a"
82+ AVD_IMAGE := "$(AVD_BASE_IMAGE ) arm64-v8a"
6583else ifeq ($(HOST_ARCH),arm64)
66- AVD_IMAGE := "$(AVD_BASE_IMAGE)arm64-v8a"
84+ AVD_IMAGE := "$(AVD_BASE_IMAGE ) arm64-v8a"
6785else
68- AVD_IMAGE := "$(AVD_BASE_IMAGE)x86_64"
86+ AVD_IMAGE := "$(AVD_BASE_IMAGE ) x86_64"
6987endif
7088AVD ?= tailscale-$(HOST_ARCH )
7189export AVD_IMAGE
7290export AVD
7391
7492# Use our toolchain or the one that is specified, do not perform dynamic toolchain switching.
75- GOTOOLCHAIN = local
93+ GOTOOLCHAIN := local
7694export GOTOOLCHAIN
7795
78- # TOOLCHAINDIR is set by fdoid CI and used by tool/* scripts.
7996TOOLCHAINDIR ?=
8097export TOOLCHAINDIR
8198
82- GOBIN ? = $(PWD ) /android/build/go/bin
99+ GOBIN : = $(PWD ) /android/build/go/bin
83100export GOBIN
84101
85102export PATH := $(PWD ) /tool:$(GOBIN ) :$(ANDROID_HOME ) /cmdline-tools/latest/bin:$(ANDROID_HOME ) /platform-tools:$(PATH )
86103export GOROOT := # Unset
87104
88- #
89- # Android Builds:
90- #
105+ # ------------------------------------------------------------------------------
106+ # Android Build Targets
107+ # ------------------------------------------------------------------------------
108+
109+
110+ .PHONY : debug-unstripped
111+ debug-unstripped : build-unstripped-aar
112+ @echo " Listing contents of $( ABS_UNSTRIPPED_AAR) :"
113+ unzip -l $(ABS_UNSTRIPPED_AAR )
91114
92115.PHONY : apk
93- apk : $(DEBUG_APK ) # # Build the debug APK
116+ apk : $(DEBUG_APK )
94117
95118.PHONY : tailscale-debug
96- tailscale-debug : $(DEBUG_APK ) # # Build the debug APK
119+ tailscale-debug : $(DEBUG_APK )
120+
121+ $(DEBUG_APK ) : libtailscale debug-symbols version gradle-dependencies build-unstripped-aar
122+ (cd android && ./gradlew test assembleDebug)
123+ install -C android/build/outputs/apk/debug/android-debug.apk $@
97124
98125# Builds the release AAB and signs it (phone/tablet/chromeOS variant)
99126.PHONY : release
100- release : jarsign-env $(RELEASE_AAB ) # # Build the release AAB
127+ release : jarsign-env $(RELEASE_AAB )
101128 @jarsigner -sigalg SHA256withRSA -digestalg SHA-256 -keystore $(JKS_PATH ) -storepass $(JKS_PASSWORD ) $(RELEASE_AAB ) tailscale
102129
103130# Builds the release AAB and signs it (androidTV variant)
104131.PHONY : release-tv
105- release-tv : jarsign-env $(RELEASE_TV_AAB ) # # Build the release AAB
132+ release-tv : jarsign-env $(RELEASE_TV_AAB )
106133 @jarsigner -sigalg SHA256withRSA -digestalg SHA-256 -keystore $(JKS_PATH ) -storepass $(JKS_PASSWORD ) $(RELEASE_TV_AAB ) tailscale
107134
108135# gradle-dependencies groups together the android sources and libtailscale needed to assemble tests/debug/release builds.
109136.PHONY : gradle-dependencies
110- gradle-dependencies : $(shell find android -type f -not -path "android/build/* " -not -path '* /.* ') $(LIBTAILSCALE ) tailscale.version
111-
112- $(DEBUG_APK ) : version gradle-dependencies
113- (cd android && ./gradlew test assembleDebug)
114- install -C android/build/outputs/apk/debug/android-debug.apk $@
137+ gradle-dependencies : $(shell find android -type f -not -path "android/build/* " -not -path '* /.* ') $(LIBTAILSCALE_AAR ) tailscale.version
115138
116139$(RELEASE_AAB ) : version gradle-dependencies
117140 @echo " Building release AAB"
@@ -128,50 +151,93 @@ tailscale-test.apk: version gradle-dependencies
128151 install -C ./android/build/outputs/apk/androidTest/applicationTest/android-applicationTest-androidTest.apk $@
129152
130153tailscale.version : go.mod go.sum $(wildcard .git/HEAD)
131- $(shell ./tool/go run tailscale.com/cmd/mkversion > tailscale.version)
154+ @bash -c " ./tool/go run tailscale.com/cmd/mkversion > tailscale.version"
132155
133156.PHONY : version
134- version : tailscale.version # # print the current version information
135- cat tailscale.version
157+ version : tailscale.version
158+ @ cat tailscale.version
136159
137- #
138- # Go Builds:
139- #
160+ # ------------------------------------------------------------------------------
161+ # Go Build Targets (Unstripped AAR, Debug Symbols, Stripped SO, Packaging)
162+ # ------------------------------------------------------------------------------
140163
141164android/libs :
142165 mkdir -p android/libs
143166
144- $(GOBIN ) /gomobile : $(GOBIN ) /gobind go.mod go.sum
167+ $(GOBIN ) :
168+ mkdir -p $(GOBIN )
169+
170+ $(GOBIN ) /gomobile : $(GOBIN ) /gobind go.mod go.sum | $(GOBIN )
145171 ./tool/go install golang.org/x/mobile/cmd/gomobile
146172
147173$(GOBIN ) /gobind : go.mod go.sum
148174 ./tool/go install golang.org/x/mobile/cmd/gobind
149175
150- $(LIBTAILSCALE ) : Makefile android/libs $(shell find libtailscale -name * .go) go.mod go.sum $(GOBIN ) /gomobile tailscale.version
176+ .PHONY : build-unstripped-aar
177+ build-unstripped-aar : tailscale.version $(GOBIN ) /gomobile
178+ @echo " Running gomobile bind to generate unstripped AAR..."
179+ @echo " Output file: $( ABS_UNSTRIPPED_AAR) "
180+ mkdir -p $(dir $(ABS_UNSTRIPPED_AAR ) )
181+ rm -f $(ABS_UNSTRIPPED_AAR )
151182 $(GOBIN ) /gomobile bind -target android -androidapi 26 \
152183 -tags " $$ (./build-tags.sh)" \
153- -ldflags " -w $$ (./version-ldflags.sh)" \
154- -o $@ ./libtailscale
184+ -ldflags " $$ (./version-ldflags.sh)" \
185+ -o $(ABS_UNSTRIPPED_AAR ) ./libtailscale || { echo " gomobile bind failed" ; exit 1; }
186+ @if [ ! -f $( ABS_UNSTRIPPED_AAR) ]; then \
187+ echo " Error: $( ABS_UNSTRIPPED_AAR) was not created" ; exit 1; \
188+ fi
189+ @echo " Generated unstripped AAR: $( ABS_UNSTRIPPED_AAR) "
190+
191+ $(UNSTRIPPED_AAR ) : build-unstripped-aar
192+
193+ libgojni.so.unstripped : $(UNSTRIPPED_AAR )
194+ @echo " Extracting libgojni.so from unstripped AAR..."
195+ @if unzip -p $(ABS_UNSTRIPPED_AAR ) jni/arm64-v8a/libgojni.so > libgojni.so.unstripped; then \
196+ echo " Found arm64-v8a libgojni.so" ; \
197+ elif unzip -p $(ABS_UNSTRIPPED_AAR ) jni/armeabi-v7a/libgojni.so > libgojni.so.unstripped; then \
198+ echo " Found armeabi-v7a libgojni.so" ; \
199+ else \
200+ echo " Neither jni/arm64-v8a/libgojni.so nor jni/armeabi-v7a/libgojni.so was found." ; \
201+ echo " Listing contents of $( ABS_UNSTRIPPED_AAR) :" ; \
202+ unzip -l $(ABS_UNSTRIPPED_AAR ) ; exit 1; \
203+ fi
204+
205+ libgojni.so.debug : libgojni.so.unstripped
206+ @echo " Extracting debug symbols from libgojni.so..."
207+ $(STRIP_TOOL ) --only-keep-debug libgojni.so.unstripped libgojni.so.debug
208+
209+ libgojni.so.stripped : libgojni.so.unstripped
210+ @echo " Stripping debug symbols from libgojni.so..."
211+ $(STRIP_TOOL ) --strip-debug libgojni.so.unstripped libgojni.so.stripped
212+
213+ $(LIBTAILSCALE_AAR ) : libgojni.so.stripped $(UNSTRIPPED_AAR )
214+ @echo " Repackaging AAR with stripped libgojni.so..."
215+ rm -rf temp_aar
216+ mkdir temp_aar
217+ unzip $(ABS_UNSTRIPPED_AAR ) -d temp_aar
218+ cp libgojni.so.stripped temp_aar/$(ARM64_SO_PATH )
219+ (cd temp_aar && zip -r ../$( LIBTAILSCALE_AAR) .)
220+ rm -rf temp_aar
155221
156222.PHONY : libtailscale
157- libtailscale : $(LIBTAILSCALE ) # # Build the libtailscale AAR
223+ libtailscale : $(LIBTAILSCALE_AAR ) # # Build the stripped libtailscale AAR
158224
159- #
160- # Utility tasks:
161- #
225+ .PHONY : debug-symbols
226+ debug-symbols : libgojni.so.debug
162227
163- .PHONY : all
164- all : test $(DEBUG_APK ) # # Build and test everything
228+ # ------------------------------------------------------------------------------
229+ # Utility Targets
230+ # ------------------------------------------------------------------------------
165231
166232.PHONY : env
167233env :
168- @echo PATH=$(PATH )
169- @echo ANDROID_SDK_ROOT=$(ANDROID_SDK_ROOT )
170- @echo ANDROID_HOME=$(ANDROID_HOME )
171- @echo ANDROID_STUDIO_ROOT=$(ANDROID_STUDIO_ROOT )
172- @echo JAVA_HOME=$(JAVA_HOME )
173- @echo TOOLCHAINDIR=$(TOOLCHAINDIR )
174- @echo AVD_IMAGE=" $( AVD_IMAGE) "
234+ @echo " PATH=$( PATH) "
235+ @echo " ANDROID_SDK_ROOT=$( ANDROID_SDK_ROOT) "
236+ @echo " ANDROID_HOME=$( ANDROID_HOME) "
237+ @echo " ANDROID_STUDIO_ROOT=$( ANDROID_STUDIO_ROOT) "
238+ @echo " JAVA_HOME=$( JAVA_HOME) "
239+ @echo " TOOLCHAINDIR=$( TOOLCHAINDIR) "
240+ @echo " AVD_IMAGE=$( AVD_IMAGE) "
175241
176242# Ensure that JKS_PATH and JKS_PASSWORD are set before we attempt a build
177243# that requires signing.
@@ -195,21 +261,20 @@ androidpath:
195261 @echo ' export PATH=$(ANDROID_HOME)/cmdline-tools/latest/bin:$(ANDROID_HOME)/platform-tools:$$PATH'
196262
197263.PHONY : tag_release
198- tag_release : tailscale.version # # Tag the current commit with the current version
264+ tag_release : debug-symbols tailscale.version # # Tag the current commit with the current version
199265 source tailscale.version && git tag -a " $$ {VERSION_LONG}" -m " OSS and Version updated to $$ {VERSION_LONG}"
200266
201-
202267.PHONY : bumposs # # Bump to the latest oss and update the versions.
203268bumposs : update-oss tailscale.version
204269 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
205270 source tailscale.version && git tag -a " $$ {VERSION_LONG}" -m " OSS and Version updated to $$ {VERSION_LONG}"
206271
207- .PHONY : bump_version_code
208- bump_version_code : # # Bump the version code in build.gradle
272+ .PHONY : bump_version_code # # Bump the version code in build.gradle
273+ bump_version_code :
209274 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
210275
211- .PHONY : update-oss
212- update-oss : # # Update the tailscale.com go module
276+ .PHONY : update-oss # # Update the tailscale.com go module
277+ update-oss :
213278 GOPROXY=direct ./tool/go get tailscale.com@main
214279 ./tool/go mod tidy -compat=1.24
215280 ./tool/go run tailscale.com/cmd/printdep --go > go.toolchain.rev.new
@@ -247,7 +312,7 @@ checkandroidsdk: ## Check that Android SDK is installed
247312test : gradle-dependencies # # Run the Android tests
248313 (cd android && ./gradlew test)
249314
250- .PHONY : emulator
315+ .PHONY : emulator
251316emulator : # # Start an android emulator instance
252317 @echo " Checking installed SDK packages..."
253318 @if ! $(ANDROID_HOME ) /cmdline-tools/latest/bin/sdkmanager --list_installed | grep -q " $( AVD_IMAGE) " ; then \
@@ -262,15 +327,15 @@ emulator: ## Start an android emulator instance
262327 @echo " Starting emulator..."
263328 @$(ANDROID_HOME ) /emulator/emulator -avd " $( AVD) " -logcat-output /dev/stdout -netdelay none -netspeed full
264329
265- .PHONY : install
330+ .PHONY : install
266331install : $(DEBUG_APK ) # # Install the debug APK on a connected device
267332 adb install -r $<
268333
269334.PHONY : run
270335run : install # # Run the debug APK on a connected device
271336 adb shell am start -n com.tailscale.ipn/com.tailscale.ipn.MainActivity
272337
273- .PHONY : docker-build-image
338+ .PHONY : docker-build-image
274339docker-build-image : # # Builds the docker image for the android build environment if it does not exist
275340 @echo " Checking if docker image $( DOCKER_IMAGE) already exists..."
276341 @if ! docker images $(DOCKER_IMAGE ) -q | grep -q . ; then \
@@ -279,29 +344,29 @@ docker-build-image: ## Builds the docker image for the android build environment
279344 fi
280345
281346.PHONY : docker-run-build
282- docker-run-build : clean jarsign-env docker-build-image # # Runs the docker image for the android build environment and builds release
347+ docker-run-build : clean jarsign-env docker-build-image # # Runs the docker image for the android build environment and builds release
283348 @docker run --rm -v $(CURDIR ) :/build/tailscale-android --env JKS_PASSWORD=$(JKS_PASSWORD ) --env JKS_PATH=$(JKS_PATH ) $(DOCKER_IMAGE )
284349
285350.PHONY : docker-remove-build-image
286351docker-remove-build-image : # # Removes the current docker build image
287352 docker rmi --force $(DOCKER_IMAGE )
288353
289- .PHONY : docker-all # # Makes a fresh docker environment, builds docker and cleans up. For CI.
354+ .PHONY : docker-all # # Makes a fresh docker environment, builds docker and cleans up. For CI.
290355docker-all : docker-build-image docker-run-build $(DOCKER_IMAGE )
291356
292357.PHONY : docker-shell
293358docker-shell : # # Builds a docker image with the android build env and opens a shell
294- docker build -f docker/DockerFile.amd64-shell -t tailscale-android-shell-amd64 .
359+ docker build -f docker/DockerFile.amd64-shell -t tailscale-android-shell-amd64 .
295360 docker run --rm -v $(CURDIR ) :/build/tailscale-android -it tailscale-android-shell-amd64
296361
297362.PHONY : docker-remove-shell-image
298363docker-remove-shell-image : # # Removes all docker shell image
299364 docker rmi --force tailscale-android-shell-amd64
300365
301366.PHONY : clean
302- clean : # # Remove build artifacts. Does not purge docker build envs. Use dockerRemoveEnv for that.
367+ clean : # # Remove build artifacts. Does not purge docker build envs. Use dockerRemoveEnv for that.
303368 @echo " Cleaning up old build artifacts"
304- -rm -rf android/build $(DEBUG_APK ) $(RELEASE_AAB ) $(RELEASE_TV_AAB ) $(LIBTAILSCALE ) android/libs * .apk * .aab
369+ -rm -rf android/build $(DEBUG_APK ) $(RELEASE_AAB ) $(RELEASE_TV_AAB ) $(LIBTAILSCALE_AAR ) android/libs * .apk * .aab
305370 @echo " Cleaning cached toolchain"
306371 -rm -rf $(HOME ) /.cache/tailscale-go{,.extracted}
307372 -pkill -f gradle
0 commit comments