blob: 7a4b58e7ab03cb4a844d5589fca45f0bebf2d5e0 [file] [log] [blame]
"""A utility to update the searchable options files."""
import argparse
import json
import glob
import os
import platform
import re
import shutil
import stat
import subprocess
import sys
import tempfile
import zipfile
def extract_file(zip_file, info, extract_dir):
""" Extracts a file preserving file attributes. """
out_path = zip_file.extract(info.filename, path=extract_dir)
attr = info.external_attr >> 16
if attr:
os.chmod(out_path, attr)
def generate_searchable_options(work_dir, out_dir, ide_path, plugins):
"""Generates the xmls in out_dir, using work_dir as a scratch pad."""
suffix = {
"Windows": "win",
"Linux": "linux",
"Darwin": "mac_arm" if platform.machine() == "arm64" else "mac" ,
}
zip_path = os.path.join("%s.%s.zip" % (ide_path, suffix[platform.system()]))
with zipfile.ZipFile(zip_path) as zip_file:
for info in zip_file.infolist():
extract_file(zip_file, info, work_dir)
vmoptions_file = "%s/studio.vmoptions" % work_dir
with open(vmoptions_file, "w") as props:
props.writelines([
"-Didea.config.path=%s/config\n" % work_dir,
"-Didea.system.path=%s/system\n" % work_dir,
"-Duser.home=%s/home\n" % work_dir])
env = {
"STUDIO_VM_OPTIONS": vmoptions_file,
"XDG_DATA_HOME": "%s/data" % work_dir,
"SHELL": os.getenv("SHELL", "")
}
options_dir = os.path.join(work_dir, "options")
studio_bin = {
"Windows": "/android-studio/bin/studio.cmd",
"Linux": "/android-studio/bin/studio",
"Darwin": "/Android Studio*.app/Contents/MacOS/studio",
}
[bin_path] = glob.glob(work_dir + studio_bin[platform.system()])
subprocess.call([bin_path, "traverseUI", options_dir, "true"], env=env)
with open("%s.plugin.lst" % ide_path, "r") as list_file:
plugin_list = {}
for line in list_file.read().splitlines():
parts = [p.strip() for p in line.split(":", 1)]
plugin_list[parts[0]] = parts[1] if len(parts) > 1 else None
with open(os.path.join(options_dir, "content.json"), "r") as content_file:
content = json.loads(content_file.read())
os.makedirs(out_dir, exist_ok=True)
bzl = {}
for plugin_dir, id in plugin_list.items():
if plugins and id not in plugins:
continue
if id and id in content:
for entry in content[id]:
name = entry["file"]
shutil.move(os.path.join(options_dir, name), out_dir)
bzl[name] = id
with open(os.path.join(out_dir, "content.bzl"), "w") as f:
f.write("# This file is generated automatically. Do not modify.\n")
f.write("SEARCHABLE_OPTIONS = {\n")
for k,v in bzl.items():
f.write(" \"%s\": \"%s\",\n" % (k,v))
f.write("}\n")
return plugin_list
def update_searchable_options(work_dir, workspace_dir, out, ide_path, plugins):
so_dir = os.path.join(workspace_dir, out)
files = glob.glob(os.path.join(so_dir, "*.json"))
for file in files:
os.remove(file)
content_file = os.path.join(so_dir, "content.bzl")
if os.path.exists(content_file):
os.remove(content_file)
generate_searchable_options(work_dir, so_dir, ide_path, plugins)
def find_workspace():
curr_dir = os.getcwd()
while os.path.dirname(curr_dir) != curr_dir:
path = os.path.join(curr_dir, "DO_NOT_BUILD_HERE")
if os.path.exists(path):
with open(path, "r") as file:
return file.read()
curr_dir = os.path.abspath(os.path.join(curr_dir, os.pardir))
sys.exit("Can't find the DO_NOT_BUILD_HERE any of the parent directories. Was binary invoked through 'bazel run'? if not use --workspace")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"--workspace",
default="",
dest="workspace",
help="The workspace directory, if empty the tool will look for it assuming 'bazel run'")
parser.add_argument(
"--out",
dest="out",
required=True,
help="The output directory")
parser.add_argument(
"--ide",
dest="ide",
required=True,
help="The path (prefix) to the ide artifacts")
parser.add_argument(
"--plugins",
dest="plugins",
nargs="*",
default=[],
help="The plugins to export, if none chosen all plugins are exported")
tmp_dir = tempfile.mkdtemp()
args = parser.parse_args()
workspace = args.workspace if args.workspace else find_workspace()
update_searchable_options(tmp_dir, workspace, args.out, args.ide, args.plugins)