From 1ae25e469b50f5a5043ebdb4850aa23c69d981d0 Mon Sep 17 00:00:00 2001 From: Eric Froemling Date: Sat, 12 Jun 2021 17:15:47 -0500 Subject: [PATCH] consolidating xcode-build-path functionality --- tools/batools/pcommand.py | 15 +++++++ tools/batools/xcode.py | 91 +++++++++++++++++++++++++++++++++++++++ tools/efrotools/ios.py | 4 +- tools/pcommand | 3 +- 4 files changed, 110 insertions(+), 3 deletions(-) create mode 100755 tools/batools/xcode.py diff --git a/tools/batools/pcommand.py b/tools/batools/pcommand.py index b87aad70..682859c4 100644 --- a/tools/batools/pcommand.py +++ b/tools/batools/pcommand.py @@ -880,3 +880,18 @@ def update_meta_makefile() -> None: """Update the meta Makefile if needed.""" from batools.metamakefile import update update(projroot=str(PROJROOT), check='--check' in sys.argv) + + +def xcode_build_path() -> None: + """Get the build path for an xcode project.""" + import os + from batools.xcode import project_build_path + if len(sys.argv) != 4: + raise Exception( + 'Expected 2 args: ') + project_path = os.path.abspath(sys.argv[2]) + configuration = sys.argv[3] + path = project_build_path(projroot=str(PROJROOT), + project_path=project_path, + configuration=configuration) + print(path) diff --git a/tools/batools/xcode.py b/tools/batools/xcode.py new file mode 100755 index 00000000..4cc8a08c --- /dev/null +++ b/tools/batools/xcode.py @@ -0,0 +1,91 @@ +# Released under the MIT License. See LICENSE for details. +# +"""Fetch and cache xcode project build paths. + +This saves the few seconds it normally would take to fire up xcodebuild +and filter its output. +""" + +from __future__ import annotations + +import json +import os +import subprocess +import sys +import time +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Dict, Any, Optional + + +def project_build_path(projroot: str, project_path: str, + configuration: str) -> str: + """Main script entry point.""" + # pylint: disable=too-many-locals + + config_path = os.path.join(projroot, '.cache', 'xcode_build_path') + out_path = None + config: Dict[str, Dict[str, Any]] = {} + + build_dir: Optional[str] = None + + try: + if os.path.exists(config_path): + with open(config_path) as infile: + config = json.loads(infile.read()) + if (project_path in config + and configuration in config[project_path]): + + # Ok we've found a build-dir entry for this project; now if it + # exists on disk and all timestamps within it are decently + # close to the one we've got recorded, lets use it. + # (Anything using this script should also be building + # stuff there so mod times should be pretty recent; if not + # then its worth re-caching to be sure.) + build_dir = config[project_path][configuration]['build_dir'] + timestamp = config[project_path][configuration]['timestamp'] + assert build_dir is not None + if os.path.isdir(build_dir): + use_cached = True + + # if its been over a day since we cached this, renew it + now = time.time() + if abs(now - timestamp) > 60 * 60 * 24: + use_cached = False + + if use_cached: + out_path = build_dir + except Exception: + import traceback + print('EXCEPTION checking cached build path', file=sys.stderr) + traceback.print_exc() + out_path = None + + # If we don't have a path at this point we look it up and cache it. + if out_path is None: + print('Caching xcode build path...', file=sys.stderr) + output = subprocess.check_output([ + 'xcodebuild', '-project', project_path, '-showBuildSettings', + '-configuration', configuration + ]).decode('utf-8') + prefix = 'TARGET_BUILD_DIR = ' + lines = [ + l for l in output.splitlines() if l.strip().startswith(prefix) + ] + if len(lines) != 1: + raise Exception( + 'TARGET_BUILD_DIR not found in xcodebuild settings output') + build_dir = lines[0].replace(prefix, '').strip() + if project_path not in config: + config[project_path] = {} + config[project_path][configuration] = { + 'build_dir': build_dir, + 'timestamp': time.time() + } + os.makedirs(os.path.dirname(config_path), exist_ok=True) + with open(config_path, 'w') as outfile: + outfile.write(json.dumps(config)) + + assert build_dir is not None + return build_dir diff --git a/tools/efrotools/ios.py b/tools/efrotools/ios.py index 3c342d5d..b75448a7 100644 --- a/tools/efrotools/ios.py +++ b/tools/efrotools/ios.py @@ -67,10 +67,10 @@ def push_ipa(root: pathlib.Path, modename: str) -> None: raise Exception('invalid mode: "' + str(modename) + '"') mode = MODES[modename] - xc_build_path = pathlib.Path(root, 'tools/xc_build_path') + pcommand_path = pathlib.Path(root, 'tools/pcommand') xcprojpath = pathlib.Path(root, cfg.projectpath) app_dir = subprocess.run( - [xc_build_path, xcprojpath, mode['configuration']], + [pcommand_path, 'xcode_build_path', xcprojpath, mode['configuration']], check=True, capture_output=True).stdout.decode().strip() built_app_path = pathlib.Path(app_dir, cfg.app_bundle_name) diff --git a/tools/pcommand b/tools/pcommand index f4d8b35e..55b6e3f1 100755 --- a/tools/pcommand +++ b/tools/pcommand @@ -40,7 +40,8 @@ from batools.pcommand import ( update_assets_makefile, update_project, update_cmake_prefab_lib, cmake_prep_dir, gen_binding_code, gen_flat_data_code, wsl_path_to_win, wsl_build_check_win_drive, win_ci_binary_build, genchangelog, - android_sdk_utils, update_resources_makefile, update_meta_makefile) + android_sdk_utils, update_resources_makefile, update_meta_makefile, + xcode_build_path) # pylint: enable=unused-import if TYPE_CHECKING: