import a.fs import a.os import a.time import ..consts::_template_dir class SDK { r###" Logically wraps the Android SDK "### [android_sdk_path] static def __call() = { "Do some minimal validity check on the sdk path" __malloc(SDK, [_guess_sdk_root()]) } def _emulator(self) = self.android_sdk_path.join('emulator', 'emulator') def _adb(self) = self.android_sdk_path.join('platform-tools', 'adb') def materialize(self, outpath, project) { """ Materialize an android project to the given path """ fs::copy(_template_dir.join('base'), outpath) } def _run_args(self) = Table( encoding=:utf8, clear_envs=true, envs=[ :ANDROID_SDK_ROOT: self.android_sdk_path, :PATH: os::env('PATH'), ], ) def _run(self, *args, **kwargs) = { kwargs = (self._run_args() + kwargs) os::run( *args, **kwargs, ) } def _gradlew(self, project_path) = { if os::name == "windows" { """ process::Command can't really deal with UNC paths... So as a workaround, just strip the prefix for now """ path_str = str(project_path.canon()) path_str = path_str.lstrip(r'\\?\') path_str + r'\gradlew.bat' } else { project_path.canon().join('gradlew') } } def _run_gradlew(self, project_path, cmdargs, *args, **kwargs) = { if os::name == 'windows' { self._run( self._gradlew(project_path), cmdargs, dir=project_path, *args, **kwargs, ) } else { r###" We call sh instead of the script directly so that gradlew doesn't need executable permissions "### self._run( 'sh', [self._gradlew(project_path)] + cmdargs, dir=project_path, *args, **kwargs, ) } } def _start(self, *args, **kwargs) = { os::Process( *args, **(self._run_args() + kwargs), ) } def build(self, project_path) { r###" Builds the android app at the given path "### self._run_gradlew( project_path, ['build'], ) } def install(self, project_path) { r###" Installs the android app at the given path "### self._run_gradlew( project_path, ['installDebug'], ) } def _get_emulator_name(self) = { r###" Tries to get the name of some emulator available on the system "### self._run( self._emulator(), ['-list-avds'], stdout=:pipe, )[1].trim().lines().list()[-1] } def _start_emulator(self) = { emulator_name = self._get_emulator_name() self._start( self._emulator(), ['-avd', emulator_name, '-port', '5554'], ) self._wait_for_emulator_ready() } def _wait_for_emulator_ready(self) { self._run(self._adb(), ['wait-for-device']) } def _emulator_started(self) = { [_status, stdout, _stderr] = self._run( self._adb(), ['devices', '-l'], stdout=:pipe, encoding=:utf8, ) stdout.trim().lines().list().len() > 1 } def start_emulator(self) = { r###" Starts an emulator if none found, and waits for it to be ready "### if self._emulator_started() { self._wait_for_emulator_ready() nil } else { self._start_emulator() } } } def _guess_sdk_root() = { def _failure() { __raise( RuntimeError, 'Could not find android sdk root (maybe set ANDROID_SDK_ROOT?)', ) } path = os::env(:ANDROID_SDK_ROOT) if path { Path(path) } elif os::name == 'windows' { path = Path((os::home() or _failure()).join(r'AppData\Local\Android\Sdk')) if path.is_dir() { path } else { _failure() } } elif os::name == 'macos' { path = Path((os::home() or _failure()).join(r'Library/Android/sdk')) if path.is_dir() { path } else { _failure() } } else { _failure() } }