@ -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)
i f e q ( $( HOST_OS ) , l i n u x )
STRIP_TOOL := $( NDK_ROOT) /toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-objcopy
e l s e i f e q ( $( HOST_OS ) , d a r w i n )
STRIP_TOOL := $( NDK_ROOT) /toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-objcopy
e n d i f
$(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.
i f e q ( $( shell uname ) , L i n u x )
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"
e l s e
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"
e n d i f
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.
i f e q ( $( 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
e n d i f
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.
i f e q ( $( JAVA_HOME ) , )
unexport JAVA_HOME
e l s e
@ -59,7 +77,7 @@ else
e n d i f
AVD_BASE_IMAGE := "system-images;android-33;google_apis;"
export HOST_ARCH =$( shell uname -m)
export HOST_ARCH : = $( shell uname -m)
i f e q ( $( HOST_ARCH ) , a a r c h 6 4 )
AVD_IMAGE := " $( AVD_BASE_IMAGE) arm64-v8a "
e l s e i f e q ( $( HOST_ARCH ) , a r m 6 4 )
@ -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 Build s:
#
# ------------------------------------------------------------------------------
# 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