From 926613ddae05307b3e15ceee3ea70ae122d1d274 Mon Sep 17 00:00:00 2001 From: James Tucker Date: Mon, 31 Jul 2023 22:20:22 -0700 Subject: [PATCH] Makefile,Dockerfile,README.md: improve build dependency setup and documentation (#114) - Teach Makefile to install an Android SDK and the components we need - Make Dockerfile delegate to Makefile for Android SDK setup - Detect Android Studio and other known SDK paths, and use them if found. - Update documentation to more consistently point to Android Studio & make. - Add a task that checks for the SDK components and produces a useful error. - Build an APK by default. - Allow TOOLCHAINDIR to be passed in, and strip the first go/ component so that it is the path a user would expect. Updates #cleanup --- .gitignore | 4 ++ Dockerfile | 23 ++++----- Makefile | 133 +++++++++++++++++++++++++++++++++++++++++------------ README.md | 73 ++++++++++++++++++----------- 4 files changed, 161 insertions(+), 72 deletions(-) diff --git a/.gitignore b/.gitignore index db2815d..a7086bf 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,10 @@ android/libs # Output files from the Makefile: tailscale-debug.apk tailscale-release.aab +tailscale-fdroid.apk # Signing key tailscale.jks + +# android sdk dir +./android-sdk \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 87eaaaf..102dcd2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,26 +12,14 @@ RUN apt-get -y --no-install-recommends install ca-certificates libc6-dev git RUN apt-get -y install make -RUN mkdir -p BUILD +RUN mkdir -p build ENV HOME /build -# Get android sdk, ndk, and rest of the stuff needed to build the android app. -WORKDIR $HOME +# Make android sdk location, the later make step will populate it. RUN mkdir android-sdk ENV ANDROID_HOME $HOME/android-sdk -WORKDIR $ANDROID_HOME -RUN curl -O https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip -RUN echo 'bd1aa17c7ef10066949c88dc6c9c8d536be27f992a1f3b5a584f9bd2ba5646a0 commandlinetools-linux-9477386_latest.zip' | sha256sum -c -RUN mkdir cmdline-tools && unzip -d cmdline-tools/latest commandlinetools-linux-9477386_latest.zip && mv cmdline-tools/latest/cmdline-tools/* cmdline-tools/latest/ -RUN echo y | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --update -RUN echo y | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager 'platforms;android-31' -RUN echo y | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager 'extras;android;m2repository' -RUN echo y | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager 'ndk;23.1.7779620' -RUN echo y | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager 'platform-tools' -RUN echo y | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager 'build-tools;33.0.2' - +ENV ANDROID_SDK_ROOT $ANDROID_HOME ENV PATH $PATH:$HOME/bin:$ANDROID_HOME/platform-tools -ENV ANDROID_SDK_ROOT /build/android-sdk # We need some version of Go new enough to support the "embed" package # to run "go run tailscale.com/cmd/printdep" to figure out which Tailscale Go @@ -43,6 +31,11 @@ RUN mkdir -p $HOME/tailscale-android RUN git config --global --add safe.directory $HOME/tailscale-android WORKDIR $HOME/tailscale-android +# Preload Android SDK +COPY Makefile Makefile +# Get android sdk, ndk, and rest of the stuff needed to build the android app. +RUN make androidsdk + # Preload Gradle COPY android/gradlew android/gradlew COPY android/gradle android/gradle diff --git a/Makefile b/Makefile index 27e8ffa..0155317 100644 --- a/Makefile +++ b/Makefile @@ -21,15 +21,55 @@ TAILSCALE_COMMIT=$(shell echo $(TAILSCALE_VERSION) | cut -d - -f 2 | cut -d t -f # Extract the version code from build.gradle. VERSIONCODE=$(lastword $(shell grep versionCode android/build.gradle)) VERSIONCODE_PLUSONE=$(shell expr $(VERSIONCODE) + 1) +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" +else + 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' + +# 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 + 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 + else + export ANDROID_SDK_ROOT=$(PWD)/android-sdk + endif +endif +export ANDROID_HOME ?= $(ANDROID_SDK_ROOT) + +# Attempt to find Android Studio for Linux configuration, which does not have a +# predetermined location. +ANDROID_STUDIO_ROOT ?= $(shell find ~/android-studio /usr/local/android-studio /opt/android-studio /Applications/Android\ Studio.app $(PROGRAMFILES)/Android/Android\ Studio -type d -maxdepth 1 2>/dev/null | head -n 1) + +# Set JAVA_HOME to the Android Studio bundled JDK. +export JAVA_HOME ?= $(shell find $(ANDROID_STUDIO_ROOT)/jre $(ANDROID_STUDIO_ROOT)/jbr $(ANDROID_STUDIO_ROOT)/Contents/jre/Contents/Home -maxdepth 1 -type d 2>/dev/null | head -n 1) -TOOLCHAINREV=$(shell go run tailscale.com/cmd/printdep --go) -TOOLCHAINDIR=${HOME}/.cache/tailscale-android-go-$(TOOLCHAINREV) -TOOLCHAINSUM=$(shell $(TOOLCHAINDIR)/go/bin/go version >/dev/null && echo "okay" || echo "bad") -TOOLCHAINWANT=okay -export PATH := $(TOOLCHAINDIR)/go/bin:$(PATH) +# Go toolchain path, by default pulled from Tailscale prebuilts pinned to the +# version in tailscale.com/cmd/printdep. +TOOLCHAINDIR ?= ${HOME}/.cache/tailscale-android-go-$(shell go run tailscale.com/cmd/printdep --go) + +export PATH := $(TOOLCHAINDIR)/bin:$(JAVA_HOME)/bin:$(ANDROID_HOME)/cmdline-tools/latest/bin:$(ANDROID_HOME)/platform-tools:$(PATH) export GOROOT := # Unset -all: $(APK) +all: $(DEBUG_APK) tailscale-fdroid.apk + +env: + @echo PATH=$(PATH) + @echo ANDROID_SDK_ROOT=$(ANDROID_SDK_ROOT) + @echo ANDROID_HOME=$(ANDROID_HOME) + @echo JAVA_HOME=$(JAVA_HOME) + @echo TOOLCHAINDIR=$(TOOLCHAINDIR) tag_release: sed -i'.bak' 's/versionCode $(VERSIONCODE)/versionCode $(VERSIONCODE_PLUSONE)/' android/build.gradle && rm android/build.gradle.bak @@ -41,41 +81,75 @@ bumposs: toolchain GOPROXY=direct go get tailscale.com@main go mod tidy -compat=1.20 -toolchain: -ifneq ($(TOOLCHAINWANT),$(TOOLCHAINSUM)) - @echo want: $(TOOLCHAINWANT) - @echo got: $(TOOLCHAINSUM) +$(TOOLCHAINDIR)/bin/go: + @if ! echo $(TOOLCHAINDIR) | grep -q 'tailscale-android-go'; then \ + echo "ERROR: TOOLCHAINDIR=$(TOOLCHAINDIR) is missing bin/go and does not appear to be a tailscale managed path"; \ + exit 1; \ + fi rm -rf ${HOME}/.cache/tailscale-android-go-* mkdir -p $(TOOLCHAINDIR) - curl --silent -L $(shell go run tailscale.com/cmd/printdep --go-url) | tar -C $(TOOLCHAINDIR) -zx -endif - -$(DEBUG_APK): toolchain + curl --silent -L $(shell go run tailscale.com/cmd/printdep --go-url) | tar --strip-components=1 -C $(TOOLCHAINDIR) -zx + +# Get the commandline tools package, this provides (among other things) the sdkmanager binary. +$(ANDROID_HOME)/cmdline-tools/latest/bin/sdkmanager: + mkdir -p $(ANDROID_HOME)/tmp + mkdir -p $(ANDROID_HOME)/cmdline-tools + (cd $(ANDROID_HOME)/tmp && \ + curl --silent -O -L $(ANDROID_TOOLS_URL) && \ + echo $(ANDROID_TOOLS_SUM) | sha256sum -c && \ + unzip $(shell basename $(ANDROID_TOOLS_URL))) + mv $(ANDROID_HOME)/tmp/cmdline-tools $(ANDROID_HOME)/cmdline-tools/latest + rm -rf $(ANDROID_HOME)/tmp + +# Install the set of Android SDK packages we need. +androidsdk: $(ANDROID_HOME)/cmdline-tools/latest/bin/sdkmanager + yes | $(ANDROID_HOME)/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null + $(ANDROID_HOME)/cmdline-tools/latest/bin/sdkmanager --update + $(ANDROID_HOME)/cmdline-tools/latest/bin/sdkmanager $(ANDROID_SDK_PACKAGES) + +# Normally in make you would simply take a dependency on the task that provides +# the binaries, however users may have a decision to make as to whether they +# want to install an SDK or use the one from an Android Studio installation. +checkandroidsdk: + @$(ANDROID_HOME)/cmdline-tools/latest/bin/sdkmanager --list_installed | grep -q 'ndk' || (\ + echo -e "\n\tERROR: Android SDK not installed.\n\ + \tANDROID_HOME=$(ANDROID_HOME)\n\ + \tANDROID_SDK_ROOT=$(ANDROID_SDK_ROOT)\n\n\ + See README.md for instructions on how to install the prerequisites.\n"; exit 1) + +androidpath: + @echo "export ANDROID_HOME=$(ANDROID_HOME)" + @echo "export ANDROID_SDK_ROOT=$(ANDROID_SDK_ROOT)" + @echo 'export PATH=$(ANDROID_HOME)/cmdline-tools/latest/bin:$(ANDROID_HOME)/platform-tools:$$PATH' + +toolchain: $(TOOLCHAINDIR)/bin/go + +android/libs: mkdir -p android/libs - go run gioui.org/cmd/gogio -buildmode archive -target android -appid $(APPID) -tags novulkan,tailscale_go -o $(AAR) github.com/tailscale/tailscale-android/cmd/tailscale + +$(AAR): toolchain checkandroidsdk android/libs + go run gioui.org/cmd/gogio \ + -ldflags "-X tailscale.com/version.longStamp=$(VERSIONNAME) -X tailscale.com/version.shortStamp=$(VERSIONNAME_SHORT) -X tailscale.com/version.gitCommitStamp=$(TAILSCALE_COMMIT) -X tailscale.com/version.extraGitCommitStamp=$(OUR_VERSION)" \ + -buildmode archive -target android -appid $(APPID) -tags novulkan,tailscale_go -o $@ github.com/tailscale/tailscale-android/cmd/tailscale + +# tailscale-debug.apk builds a debuggable APK with the Google Play SDK. +$(DEBUG_APK): $(AAR) (cd android && ./gradlew test assemblePlayDebug) mv android/build/outputs/apk/play/debug/android-play-debug.apk $@ -rundebug: $(DEBUG_APK) - adb install -r $(DEBUG_APK) +apk: $(DEBUG_APK) + +run: install adb shell am start -n com.tailscale.ipn/com.tailscale.ipn.IPNActivity # tailscale-fdroid.apk builds a non-Google Play SDK, without the Google bits. # This is effectively what the F-Droid build definition produces. # This is useful for testing on e.g. Amazon Fire Stick devices. -tailscale-fdroid.apk: toolchain - mkdir -p android/libs - go run gioui.org/cmd/gogio -buildmode archive -target android -appid $(APPID) -tags novulkan,tailscale_go -o $(AAR) github.com/tailscale/tailscale-android/cmd/tailscale +tailscale-fdroid.apk: $(AAR) (cd android && ./gradlew test assembleFdroidDebug) mv android/build/outputs/apk/fdroid/debug/android-fdroid-debug.apk $@ -# This target is also used by the F-Droid builder. -release_aar: toolchain -release_aar: - mkdir -p android/libs - go run gioui.org/cmd/gogio -ldflags "-X tailscale.com/version.longStamp=$(VERSIONNAME) -X tailscale.com/version.shortStamp=$(VERSIONNAME_SHORT) -X tailscale.com/version.gitCommitStamp=$(TAILSCALE_COMMIT) -X tailscale.com/version.extraGitCommitStamp=$(OUR_VERSION)" -buildmode archive -target android -appid $(APPID) -tags novulkan,tailscale_go -o $(AAR) github.com/tailscale/tailscale-android/cmd/tailscale - -$(RELEASE_AAB): release_aar +$(RELEASE_AAB): $(AAR) (cd android && ./gradlew test bundlePlayRelease) mv ./android/build/outputs/bundle/playRelease/android-play-release.aab $@ @@ -90,6 +164,7 @@ dockershell: docker run -v $(CURDIR):/build/tailscale-android -it --rm tailscale-android clean: - rm -rf android/build $(RELEASE_AAB) $(DEBUG_APK) $(AAR) + -rm -rf android/build $(DEBUG_APK) $(RELEASE_AAB) $(AAR) tailscale-fdroid.apk + -pkill -f gradle -.PHONY: all clean install $(DEBUG_APK) $(RELEASE_AAB) release_aar release bump_version dockershell +.PHONY: all clean install android/lib $(DEBUG_APK) $(RELEASE_AAB) $(AAR) release bump_version dockershell diff --git a/README.md b/README.md index 85b9013..67135ad 100644 --- a/README.md +++ b/README.md @@ -17,33 +17,63 @@ This repository contains the open source Tailscale Android client. alt="Get it on Google Play" height="80">](https://play.google.com/store/apps/details?id=com.tailscale.ipn) -## Building -[Go](https://golang.org), the [Android -SDK](https://developer.android.com/studio/releases/platform-tools), -the [Android NDK](https://developer.android.com/ndk) are required. +## Preparing a build environment + +There are several options for setting up a build environment. The Android Studio +path is the most useful path for longer term development. + +In all cases you will need: + +- Go runtime +- Android SDK +- Android SDK components (`make androidsdk` will install them) + +### Android Studio + +1. Install a Go runtime (https://go.dev/dl/). +2. Install Android Studio (https://developer.android.com/studio). +3. Start Android Studio, from the Welcome screen select "More Actions" and "SDK Manager". +4. In the SDK manager, select the "SDK Tools" tab and install the "Android SDK Command-line Tools (latest)". +3. Run `make androidsdk` to install the necessary SDK components. + +If you would prefer to avoid Android Studio, you can also install an Android +SDK. The makefile detects common paths, so `sudo apt install android-sdk` is +sufficient on Debian / Ubuntu systems. To use an Android SDK installed in a +non-standard location, set the `ANDROID_SDK_ROOT` environment variable to the +path to the SDK. + +If you installed Android Studio the tools may not be in your path. To get the +correct tool path, run `make androidpath` and export the provided path in your +shell. + +### Docker + +If you wish to avoid installing software on your host system, a Docker based development strategy is available, you can build and start a shell with: ```sh -$ make tailscale-debug.apk -$ adb install -r tailscale-debug.apk +make dockershell ``` -The `dockershell` target builds a container with the necessary -dependencies and runs a shell inside it. +### Nix + +If you have Nix 2.4 or later installed, a Nix development environment can +be set up with: ```sh -$ make dockershell -# make tailscale-debug.apk +alias nix='nix --extra-experimental-features "nix-command flakes"' +nix develop ``` -If you have Nix 2.4 or later installed, a Nix development environment can -be set up with +## Building ```sh -$ alias nix='nix --extra-experimental-features "nix-command flakes"' -$ nix develop +make apk +make install ``` +## Building a release + Use `make tag_release` to bump the Android version code, update the version name, and tag the current commit. @@ -52,6 +82,7 @@ release candidate builds (currently Go 1.14) in module mode. It might work in earlier Go versions or in GOPATH mode, but we're making no effort to keep those working. + ## Google Sign-In Google Sign-In support relies on configuring a [Google API Console @@ -97,20 +128,6 @@ adb shell am start -n com.tailscale.ipn/com.tailscale.ipn.IPNActivity adb shell pm uninstall com.tailscale.ipn ``` -## Building on macOS - -To build from the CLI on macOS: - -1. Install Android Studio (when asked which SDKs to install, choose 31 or the current value of targetSdkVersion in `build.gradle`) -2. In Android Studio's home screen: "More Actions" > "SDK Manager", install NDK. -3. You can now close Android Studio, unless you want it to create virtual devices - ("More Actions" > "Virtual Device Manager"). -4. Then, from CLI: -5. `export JAVA_HOME='/Applications/Android Studio.app/Contents/jbr/Contents/Home'` -6. `export ANDROID_SDK_ROOT=$HOME/Library/Android/sdk` -7. `export PATH=$ANDROID_SDK_ROOT/platform-tools:$PATH` (to allow `adb` and the like to work) -8. `make tailscale-fdroid.apk`, etc - ## Bugs Please file any issues about this code or the hosted service on