Skip to content

Commit

Permalink
Merge branch 'main' into arch-checks
Browse files Browse the repository at this point in the history
  • Loading branch information
rmartin16 committed Aug 8, 2023
2 parents e147b56 + 650ad94 commit a081900
Show file tree
Hide file tree
Showing 17 changed files with 417 additions and 185 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ repos:
- id: isort
additional_dependencies: [toml]
- repo: https://github.com/asottile/pyupgrade
rev: v3.9.0
rev: v3.10.1
hooks:
- id: pyupgrade
args: [--py38-plus]
Expand Down
1 change: 1 addition & 0 deletions changes/1397.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The Android SDK's Command-Line Tools is now version 9.0. If an externally-managed Android SDK is being used, it must provide this version of Command-Line Tools. Use the SDK Manager in Android Studio to ensure it is installed.
1 change: 1 addition & 0 deletions changes/1398.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The Java JDK version was upgraded from 17.0.7+7 to 17.0.8+7.
1 change: 1 addition & 0 deletions changes/1400.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The ``pre-commit`` hook for ``pyupgrade`` was updated to its latest version.
1 change: 1 addition & 0 deletions changes/1401.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Updated sphinx from 7.1.1 to 7.1.2.
4 changes: 3 additions & 1 deletion docs/reference/platforms/android.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ Gradle requires an install of the Android SDK and a Java 17 JDK.
If you have an existing install of the Android SDK, it will be used by Briefcase
if the ``ANDROID_HOME`` environment variable is set. If ``ANDROID_HOME`` is not
present in the environment, Briefcase will honor the deprecated
``ANDROID_SDK_ROOT`` environment variable.
``ANDROID_SDK_ROOT`` environment variable. Additionally, an existing SDK install
must have version 9.0 of Command-line Tools installed; this version can be
installed in the SDK Manager in Android Studio.

If you have an existing install of a Java 17 JDK, it will be used by Briefcase
if the ``JAVA_HOME`` environment variable is set. On macOS, if ``JAVA_HOME`` is
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ dev =
docs =
furo == 2023.7.26
pyenchant == 3.2.2
sphinx == 7.1.1
sphinx == 7.1.2
sphinx_tabs == 3.4.1
sphinx-autobuild == 2021.3.14
sphinx-copybutton == 0.5.2
Expand Down
233 changes: 157 additions & 76 deletions src/briefcase/integrations/android_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ class AndroidSDK(ManagedTool):
name = "android_sdk"
full_name = "Android SDK"

# Latest version for Command-Line Tools download as of August 2023
# **Be sure the android.rst docs stay in sync with version updates here**
SDK_MANAGER_DOWNLOAD_VER = "9477386"
SDK_MANAGER_VER = "9.0"

def __init__(self, tools: ToolCache, root_path: Path):
super().__init__(tools=tools)
self.dot_android_path = self.tools.home_path / ".android"
Expand Down Expand Up @@ -84,33 +89,22 @@ def cmdline_tools_url(self) -> str:
) from e

return (
f"https://dl.google.com/android/repository/commandlinetools-"
f"{platform_name}-{self.cmdline_tools_version}_latest.zip"
f"https://dl.google.com/android/repository/"
f"commandlinetools-{platform_name}-{self.SDK_MANAGER_DOWNLOAD_VER}_latest.zip"
)

@property
def cmdline_tools_path(self) -> Path:
return self.root_path / "cmdline-tools" / "latest"

@property
def cmdline_tools_version(self) -> str:
# This is the version of the Android SDK Command-line tools that
# are current as of May 2022. These tools can generally self-update,
# so using a fixed download URL isn't a problem.
# However, if/when this version number is changed, ensure that the
# checks done during verification include any required upgrade steps.
return "8092744"
"""Version-specific Command-line tools install root directory."""
return self.root_path / "cmdline-tools" / self.SDK_MANAGER_VER

@property
def cmdline_tools_version_path(self) -> Path:
return self.root_path / "cmdline-tools" / self.cmdline_tools_version
def sdkmanager_filename(self) -> str:
return "sdkmanager.bat" if self.tools.host_os == "Windows" else "sdkmanager"

@property
def sdkmanager_path(self) -> Path:
sdkmanager = (
"sdkmanager.bat" if self.tools.host_os == "Windows" else "sdkmanager"
)
return self.cmdline_tools_path / "bin" / sdkmanager
return self.cmdline_tools_path / "bin" / self.sdkmanager_filename

@property
def adb_path(self) -> Path:
Expand Down Expand Up @@ -247,15 +241,16 @@ def verify_install(
JDK.verify(tools=tools, install=install)

sdk = None
sdk_root, sdk_env_source = cls.sdk_path_from_env(tools=tools)

if sdk_root:
# Verify externally-managed Android SDK
sdk_root_env, sdk_source_env = cls.sdk_path_from_env(tools=tools)
if sdk_root_env:
tools.logger.debug("Evaluating ANDROID_HOME...", prefix=cls.full_name)
tools.logger.debug(f"{sdk_env_source}={sdk_root}")
sdk = AndroidSDK(tools=tools, root_path=Path(sdk_root))
tools.logger.debug(f"{sdk_source_env}={sdk_root_env}")
sdk = AndroidSDK(tools=tools, root_path=Path(sdk_root_env))

if sdk.exists():
if sdk_env_source == "ANDROID_SDK_ROOT":
if sdk_source_env == "ANDROID_SDK_ROOT":
tools.logger.warning(
"""
*************************************************************************
Expand All @@ -274,26 +269,49 @@ def verify_install(
*************************************************************************
"""
)
sdk.verify_license()
elif sdk.cmdline_tools_path.parent.exists():
# a cmdline-tools directory exists but the required version isn't installed.
# try to install the required version using the 'latest' version.
if not sdk.install_cmdline_tools():
sdk = None
tools.logger.warning(
f"""
*************************************************************************
** WARNING: Incompatible Command-Line Tools Version **
*************************************************************************
The Android SDK specified by {sdk_source_env} at:
{sdk_root_env}
does not contain Command-Line Tools version {cls.SDK_MANAGER_VER}. Briefcase requires
this version to be installed to use an external Android SDK.
Use Android Studio's SDK Manager to install Command-Line Tools {cls.SDK_MANAGER_VER}.
Briefcase will proceed using its own SDK instance.
*************************************************************************
"""
)
else:
sdk = None
tools.logger.warning(
f"""
*************************************************************************
** {f"WARNING: {sdk_env_source} does not point to an Android SDK":67} **
** {f"WARNING: {sdk_source_env} does not point to an Android SDK":67} **
*************************************************************************
The location pointed to by the {sdk_env_source} environment
The location pointed to by the {sdk_source_env} environment
variable:
{sdk_root}
{sdk_root_env}
doesn't appear to contain an Android SDK.
If {sdk_env_source} is an Android SDK, ensure it is the root directory
If {sdk_source_env} is an Android SDK, ensure it is the root directory
of the Android SDK instance such that
${sdk_env_source}/cmdline-tools/latest/bin/sdkmanager
${sdk_source_env}{os.sep}{sdk.sdkmanager_path.relative_to(sdk.root_path)}
is a valid filepath.
Expand All @@ -302,53 +320,35 @@ def verify_install(
*************************************************************************
"""
)
sdk = None

# Verify Briefcase-managed Android SDK
if sdk is None:
# Build an SDK wrapper for the Briefcase SDK instance.
sdk_root_path = tools.base_path / "android_sdk"
sdk = AndroidSDK(tools=tools, root_path=sdk_root_path)

if sdk.exists():
# NOTE: For now, all known versions of the cmdline-tools are compatible.
# If/when that ever changes, do a verification check here.
sdk.verify_license()
else:
# The legacy SDK Tools exist. Delete them.
if (sdk_root_path / "tools").exists():
tools.logger.warning(
f"""
*************************************************************************
** WARNING: Upgrading Android SDK tools **
*************************************************************************
Briefcase needs to replace the older Android SDK Tools with the
newer Android SDK Command-Line Tools. This will involve some large
downloads, as well as re-accepting the licenses for the Android
SDKs.
Any emulators created with the older Android SDK Tools will not be
compatible with the new tools. You will need to create new
emulators. Old emulators can be removed by deleting the files
in {sdk.avd_path} matching the emulator name.
if not sdk.exists():
if not install:
raise MissingToolError("Android SDK")

*************************************************************************
"""
)
tools.shutil.rmtree(sdk_root_path)
sdk.delete_legacy_sdk_tools()

if install:
if sdk.cmdline_tools_path.parent.exists():
tools.logger.info("Upgrading Android SDK...", prefix=cls.name)
else:
tools.logger.info(
"The Android SDK was not found; downloading and installing...",
prefix=cls.name,
)
tools.logger.info(
"To use an existing Android SDK instance, "
"specify its root directory path in the ANDROID_HOME environment variable."
"To use an existing Android SDK instance, specify its root "
"directory path in the ANDROID_HOME environment variable."
)
tools.logger.info()
sdk.install()
else:
raise MissingToolError("Android SDK")
sdk.install()

# Licences must be accepted to use the SDK
sdk.verify_license()

tools.logger.debug(f"Using Android SDK at {sdk.root_path}")
tools.android_sdk = sdk
Expand All @@ -374,7 +374,6 @@ def managed_install(self) -> bool:

def uninstall(self):
"""The Android SDK is upgraded in-place instead of being reinstalled."""
pass

def install(self):
"""Download and install the Android SDK."""
Expand All @@ -385,18 +384,18 @@ def install(self):
)

# The cmdline-tools package *must* be installed as:
# <sdk_path>/cmdline-tools/latest
# <sdk_path>/cmdline-tools/<cmdline-tools version>
#
# However, the zip file unpacks a top-level folder named `cmdline-tools`.
# So, the unpacking process is:
#
# 1. Make a <sdk_path>/cmdline-tools folder
# 2. Unpack the zip file into that folder, creating <sdk_path>/cmdline-tools/cmdline-tools
# 3. Move <sdk_path>/cmdline-tools/cmdline-tools to <sdk_path>/cmdline-tools/latest
# 4. Drop a marker file named <sdk_path>/cmdline-tools/<version> so we can track
# the version that was installed.
# 3. Move <sdk_path>/cmdline-tools/cmdline-tools to <sdk_path>/cmdline-tools/<cmdline-tools version>

with self.tools.input.wait_bar("Installing Android SDK Command-Line Tools..."):
with self.tools.input.wait_bar(
f"Installing Android SDK Command-Line Tools {self.SDK_MANAGER_VER}..."
):
self.cmdline_tools_path.parent.mkdir(parents=True, exist_ok=True)
try:
self.tools.shutil.unpack_archive(
Expand All @@ -412,18 +411,14 @@ def install(self):
"""
) from e

# If there's an existing version of the cmdline tools (or the version marker), delete them.
# If there's an existing version of the cmdline tools, delete them.
if self.cmdline_tools_path.exists():
self.tools.shutil.rmtree(self.cmdline_tools_path)
if self.cmdline_tools_version_path.exists():
self.tools.os.unlink(self.cmdline_tools_version_path)

# Rename the top level zip content to the final name
(self.cmdline_tools_path.parent / "cmdline-tools").rename(
self.cmdline_tools_path
)
# Touch a file with the version that was installed.
self.cmdline_tools_version_path.touch()

# Zip file no longer needed once unpacked.
cmdline_tools_zip_path.unlink()
Expand All @@ -437,8 +432,8 @@ def install(self):
if not self.tools.os.access(binpath, self.tools.os.X_OK):
binpath.chmod(0o755)

# Licences must be accepted.
self.verify_license()
with self.tools.input.wait_bar("Removing older Android SDK packages..."):
self.cleanup_old_installs()

def upgrade(self):
"""Upgrade the Android SDK."""
Expand All @@ -461,6 +456,92 @@ def upgrade(self):
"""
) from e

def install_cmdline_tools(self) -> bool:
"""Attempt to use 'latest' cmdline-tools to install the currently required
version of the Command-Line Tools.
The Briefcase-managed SDK should always have the required version of cmdline-
tools installed; however, user-provided SDKs may not have it.
:returns: True if successfully installed; False otherwise
"""
self.tools.logger.info(
f"Installing Android Command-Line Tools {self.SDK_MANAGER_VER}...",
prefix=self.full_name,
)
self.tools.logger.info(f"Using Android SDK at {self.root_path}")
latest_sdkmanager_path = (
self.root_path
/ "cmdline-tools"
/ "latest"
/ "bin"
/ self.sdkmanager_filename
)
try:
self.tools.subprocess.run(
[
latest_sdkmanager_path,
f"cmdline-tools;{self.SDK_MANAGER_VER}",
],
check=True,
stream_output=False,
)
except (OSError, subprocess.CalledProcessError) as e:
self.tools.logger.debug(str(e))
self.tools.logger.warning(
f"Failed to install cmdline-tools;{self.SDK_MANAGER_VER}"
)
return False
return True

def delete_legacy_sdk_tools(self):
"""Delete any legacy Android SDK tools that are installed.
If no versions of the Command-Line Tools are installed but the 'tools' directory
exists, the legacy SDK Tools are probably installed. Since they have been
deprecated by more recent releases of SDK Manager, delete them and perform a
fresh install.
The Android SDK Tools were deprecated in Sept 2017.
"""
if (
not self.cmdline_tools_path.parent.exists()
and (self.root_path / "tools").exists()
):
self.tools.logger.warning(
f"""
*************************************************************************
** WARNING: Upgrading Android SDK tools **
*************************************************************************
Briefcase needs to replace the older Android SDK Tools with the
newer Android SDK Command-Line Tools. This will involve some large
downloads, as well as re-accepting the licenses for the Android
SDKs.
Any emulators created with the older Android SDK Tools will not be
compatible with the new tools. You will need to create new
emulators. Old emulators can be removed by deleting the files
in {self.avd_path} matching the emulator name.
*************************************************************************
"""
)
self.tools.shutil.rmtree(self.root_path)

def cleanup_old_installs(self):
"""Remove old versions of Android SDK packages and version markers.
When the Android SDK is upgraded, old versions of packages should be removed to
keep the SDK tidy. This is namely the Command-line Tools that are used to manage
the SDK and AVDs. Additionally, previous version of Briefcase created a version
marker file that needs to be deleted.
"""
if (ver_file := self.cmdline_tools_path.parent / "8092744").is_file():
self.tools.os.unlink(ver_file)
if (latest := self.cmdline_tools_path.parent / "latest").is_dir():
self.tools.shutil.rmtree(latest)

def list_packages(self):
"""In debug output, list the packages currently managed by the SDK."""
try:
Expand Down
Loading

0 comments on commit a081900

Please sign in to comment.