| """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) |