From 97457e5bfdfa1fe779e976982c0386045a32ce93 Mon Sep 17 00:00:00 2001 From: Eric Froemling Date: Wed, 17 Jun 2020 17:31:41 -0700 Subject: [PATCH] More work towards Android 1.5 --- .efrocachemap | 24 +- .idea/dictionaries/ericf.xml | 1 + CHANGELOG.md | 6 + Makefile | 52 +- assets/src/server/ballisticacore_server.py | 8 +- .../server/launch_ballisticacore_server.bat | 5 +- tools/batools/snippets.py | 618 ++++++++++++++++++ tools/snippets | 562 +--------------- 8 files changed, 689 insertions(+), 587 deletions(-) create mode 100644 tools/batools/snippets.py diff --git a/.efrocachemap b/.efrocachemap index 571dca2e..e76d1774 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -4135,16 +4135,16 @@ "assets/build/windows/x64/vc_redist.x64.exe": "https://files.ballistica.net/cache/ba1/ea/19/8b8787d81abcdce158ba608cd24f", "assets/build/windows/x64/vcruntime140_1d.dll": "https://files.ballistica.net/cache/ba1/11/d8/ff6344b429b00c24d9a1930d4338", "assets/build/windows/x64/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/20/33/0825e11e6518f87ece3009309933", - "build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/29/9c/24a918c046b06f010d9948458a09", - "build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/2e/1c/2285cc92d83ce3a32b26c020cb71", - "build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/24/b6/9410d8cf8be5aaf2ffad07964950", - "build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/48/c4/2bdd6802858b92c348df702c9dcd", - "build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b4/ca/0a1426dabc53a9f90a6beb011423", - "build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f8/ef/4186b927c2675c3e4dfa19ba180d", - "build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/29/59/60175945534d87709dfc29d1b180", - "build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/5c/b8/16b868c1bbd8b1be2ebaf4da4d31", - "build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/30/6c/33b6b194062212beeae3043515a3", - "build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/4b/24/c54be077112185f6a3d2c67641ea", - "build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/ee/6e/a9d59e81b549b992673d20f84f11", - "build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/5b/73/97159eb1d7dfb4018c5a1241f164" + "build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/fc/3d/ab2ccd1f76fec48d1d1f8cc07aa4", + "build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e2/e1/672e2ccaceadf3b0867bf3b887a4", + "build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/5f/9d/90533da6e9d91dc6b932a2aa7f43", + "build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/30/ea/6af215aa0cab66e46c7041a7b091", + "build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/55/66/b8bc31f6818606e282eda887939f", + "build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/52/e7/2419b41d3524735dc709338c4290", + "build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/11/b5/5ee7f3ba18e2683d56b8a020b39f", + "build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/24/5f/6953fd0f2b47e943ea734bc14911", + "build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/1b/fb/739c5d5dc13218efdeaaa020c9f9", + "build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/01/15/75f7b94e3608435a4609a3992dbc", + "build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/9f/9d/cf77bfbd79aed7178d9674b3af14", + "build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/c9/b2/6863b197eac679f85a5414525aa9" } \ No newline at end of file diff --git a/.idea/dictionaries/ericf.xml b/.idea/dictionaries/ericf.xml index 74ab5b89..a631cdc7 100644 --- a/.idea/dictionaries/ericf.xml +++ b/.idea/dictionaries/ericf.xml @@ -1196,6 +1196,7 @@ modder modders modename + modestr modpack modtimes moduledir diff --git a/CHANGELOG.md b/CHANGELOG.md index 2696a3d2..20550909 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +### 1.5.6 (20072) +- Mouse wheel now works in manual camera mode on more platforms. +- Lots of internal event-handling cleanup/reorganization in preparation for Android 1.5 update. +- Server scripts now run in opt mode in release builds so they can use bundled .opt-1.pyc files. +- More misc bug fixes and tidying. + ### 1.5.5 (20069) - Cleaned up Windows version packaging. - More misc bug fixes. diff --git a/Makefile b/Makefile index 47257e12..79ede417 100644 --- a/Makefile +++ b/Makefile @@ -188,14 +188,15 @@ prefab-mac-server-debug-build: prereqs assets-cmake \ @${STAGE_ASSETS} -cmakeserver build/prefab/mac-server/debug/dist build/prefab/mac-server/debug/ballisticacore_server: \ - assets/src/server/ballisticacore_server.py - @cp $< $@ + assets/src/server/ballisticacore_server.py tools/batools/snippets.py + @tools/snippets stage_server_file debug $< $@ build/prefab/mac-server/debug/config_template.yaml: \ assets/src/server/config_template.yaml \ tools/batools/build.py \ + tools/batools/snippets.py \ tools/bacommon/servermanager.py - @tools/snippets filter_server_config $< $@ + @tools/snippets stage_server_file debug $< $@ build/prefab/mac-server/debug/README.txt: \ assets/src/server/README.txt @@ -219,14 +220,15 @@ prefab-mac-server-release-build: prereqs assets-cmake \ @${STAGE_ASSETS} -cmakeserver build/prefab/mac-server/release/dist build/prefab/mac-server/release/ballisticacore_server: \ - assets/src/server/ballisticacore_server.py - @cp $< $@ + assets/src/server/ballisticacore_server.py tools/batools/snippets.py + @tools/snippets stage_server_file release $< $@ build/prefab/mac-server/release/config_template.yaml: \ assets/src/server/config_template.yaml \ tools/batools/build.py \ + tools/batools/snippets.py \ tools/bacommon/servermanager.py - @tools/snippets filter_server_config $< $@ + @tools/snippets stage_server_file release $< $@ build/prefab/mac-server/release/README.txt: \ assets/src/server/README.txt @@ -276,14 +278,15 @@ prefab-linux-server-debug-build: prereqs assets-cmake \ @${STAGE_ASSETS} -cmakeserver build/prefab/linux-server/debug/dist build/prefab/linux-server/debug/ballisticacore_server: \ - assets/src/server/ballisticacore_server.py - @cp $< $@ + assets/src/server/ballisticacore_server.py tools/batools/snippets.py + @tools/snippets stage_server_file debug $< $@ build/prefab/linux-server/debug/config_template.yaml: \ assets/src/server/config_template.yaml \ tools/batools/build.py \ + tools/batools/snippets.py \ tools/bacommon/servermanager.py - @tools/snippets filter_server_config $< $@ + @tools/snippets stage_server_file debug $< $@ build/prefab/linux-server/debug/README.txt: \ assets/src/server/README.txt @@ -307,14 +310,15 @@ prefab-linux-server-release-build: prereqs assets-cmake \ @${STAGE_ASSETS} -cmakeserver build/prefab/linux-server/release/dist build/prefab/linux-server/release/ballisticacore_server: \ - assets/src/server/ballisticacore_server.py - @cp $< $@ + assets/src/server/ballisticacore_server.py tools/batools/snippets.py + @tools/snippets stage_server_file release $< $@ build/prefab/linux-server/release/config_template.yaml: \ assets/src/server/config_template.yaml \ tools/batools/build.py \ + tools/batools/snippets.py \ tools/bacommon/servermanager.py - @tools/snippets filter_server_config $< $@ + @tools/snippets stage_server_file release $< $@ build/prefab/linux-server/release/README.txt: \ assets/src/server/README.txt @@ -376,25 +380,26 @@ build/prefab/windows-server/debug/dist/ballisticacore_headless.exe: .efrocachema @tools/snippets efrocache_get $@ build/prefab/windows-server/debug/ballisticacore_server.py: \ - assets/src/server/ballisticacore_server.py - @cp $< $@ + assets/src/server/ballisticacore_server.py tools/batools/snippets.py + @tools/snippets stage_server_file debug $< $@ build/prefab/windows-server/debug/launch_ballisticacore_server.bat: \ - assets/src/server/launch_ballisticacore_server.bat - @cp $< $@ + assets/src/server/launch_ballisticacore_server.bat tools/batools/snippets.py + @tools/snippets stage_server_file debug $< $@ build/prefab/windows-server/debug/config_template.yaml: \ assets/src/server/config_template.yaml \ tools/batools/build.py \ + tools/batools/snippets.py \ tools/bacommon/servermanager.py - @tools/snippets filter_server_config $< $@ + @tools/snippets stage_server_file debug $< $@ build/prefab/windows-server/debug/README.txt: \ assets/src/server/README.txt @cp $< $@ RUN_PREFAB_WINDOWS_SERVER_RELEASE = cd build/prefab/windows-server/release \ - && dist/python.exe ballisticacore_server.py + && dist/python.exe -O ballisticacore_server.py prefab-windows-server-release: prefab-windows-server-release-build @tools/snippets ensure_prefab_platform windows @@ -414,18 +419,19 @@ build/prefab/windows-server/release/dist/ballisticacore_headless.exe: .efrocache @tools/snippets efrocache_get $@ build/prefab/windows-server/release/ballisticacore_server.py: \ - assets/src/server/ballisticacore_server.py - @cp $< $@ + assets/src/server/ballisticacore_server.py tools/batools/snippets.py + @tools/snippets stage_server_file release $< $@ build/prefab/windows-server/release/launch_ballisticacore_server.bat: \ - assets/src/server/launch_ballisticacore_server.bat - @cp $< $@ + assets/src/server/launch_ballisticacore_server.bat tools/batools/snippets.py + @tools/snippets stage_server_file release $< $@ build/prefab/windows-server/release/config_template.yaml: \ assets/src/server/config_template.yaml \ tools/batools/build.py \ + tools/batools/snippets.py \ tools/bacommon/servermanager.py - @tools/snippets filter_server_config $< $@ + @tools/snippets stage_server_file release $< $@ build/prefab/windows-server/release/README.txt: \ assets/src/server/README.txt diff --git a/assets/src/server/ballisticacore_server.py b/assets/src/server/ballisticacore_server.py index 969d5884..a0de2d87 100755 --- a/assets/src/server/ballisticacore_server.py +++ b/assets/src/server/ballisticacore_server.py @@ -51,7 +51,7 @@ if TYPE_CHECKING: # Not sure how much versioning we'll do with this, but this will get # printed at startup in case we need it. -VERSION_STR = '1.0.1' +VERSION_STR = '1.0.2' class ServerManagerApp: @@ -106,9 +106,13 @@ class ServerManagerApp: # Print basic usage info in interactive mode. if sys.stdin.isatty(): + if __debug__: + modestr = '(debug mode)' + else: + modestr = '(opt mode)' print(f'{Clr.CYN}{Clr.BLD}BallisticaCore server' f' manager {VERSION_STR}' - f' starting up...{Clr.RST}\n' + f' starting up {modestr}...{Clr.RST}\n' f'{Clr.CYN}Use the "mgr" object to make' f' live server adjustments.\n' f'Type "help(mgr)" for more information.{Clr.RST}') diff --git a/assets/src/server/launch_ballisticacore_server.bat b/assets/src/server/launch_ballisticacore_server.bat index a5315816..547324d6 100644 --- a/assets/src/server/launch_ballisticacore_server.bat +++ b/assets/src/server/launch_ballisticacore_server.bat @@ -1,4 +1,3 @@ :: Simply run the ballisticacore_server.py script with the bundled -:: python interpreter. Run in opt-mode so we pick up all the -:: bundled .opt-1.pyc files too. -dist\\python.exe -O ballisticacore_server.py +:: Python interpreter. +dist\\python.exe ballisticacore_server.py diff --git a/tools/batools/snippets.py b/tools/batools/snippets.py new file mode 100644 index 00000000..7324cc21 --- /dev/null +++ b/tools/batools/snippets.py @@ -0,0 +1,618 @@ +# Copyright (c) 2011-2020 Eric Froemling +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# ----------------------------------------------------------------------------- +"""Standard snippets that can be pulled into project snippets scripts. + +A snippet is a mini-program that directly takes input from stdin and does +some focused task. This module consists of ballistica-specific ones. +""" +from __future__ import annotations + +# Note: import as little as possible here at the module level to +# keep launch times fast for small snippets. +import sys +from typing import TYPE_CHECKING + +from efrotools.snippets import PROJROOT + +if TYPE_CHECKING: + from typing import Optional + + +def stage_server_file() -> None: + """Stage files for the server environment with some filtering.""" + import os + import subprocess + import batools.build + from efro.error import CleanError + from efrotools import replace_one + if len(sys.argv) != 5: + raise CleanError('Expected 3 args (mode, infile, outfile).') + mode, infilename, outfilename = sys.argv[2], sys.argv[3], sys.argv[4] + if mode not in ('debug', 'release'): + raise CleanError(f"Invalid mode '{mode}'; expected debug or release.") + + print(f'Building server file: {os.path.basename(outfilename)}') + + basename = os.path.basename(infilename) + if basename == 'config_template.yaml': + # Inject all available config values into the config file. + batools.build.filter_server_config(str(PROJROOT), infilename, + outfilename) + elif basename == 'ballisticacore_server.py': + # Run Python in opt mode for release builds. + with open(infilename) as infile: + lines = infile.read().splitlines() + if mode == 'release': + lines[0] = replace_one(lines[0], '#!/usr/bin/env python3.7', + '#!/usr/bin/env python3.7 -O') + with open(outfilename, 'w') as outfile: + outfile.write('\n'.join(lines) + '\n') + subprocess.run(['chmod', '+x', outfilename], check=True) + elif basename == 'launch_ballisticacore_server.bat': + # Run Python in opt mode for release builds. + with open(infilename) as infile: + lines = infile.read().splitlines() + if mode == 'release': + lines[1] = replace_one( + lines[1], ':: Python interpreter.', + ':: Python interpreter.' + ' (in opt mode so we use bundled .opt-1.pyc files)') + lines[2] = replace_one( + lines[2], 'dist\\\\python.exe ballisticacore_server.py', + 'dist\\\\python.exe -O ballisticacore_server.py') + with open(outfilename, 'w') as outfile: + outfile.write('\n'.join(lines) + '\n') + else: + raise CleanError(f"Unknown server file for staging: '{basename}'.") + + +def py_examine() -> None: + """Run a python examination at a given point in a given file.""" + import os + from pathlib import Path + import efrotools + if len(sys.argv) != 7: + print('ERROR: expected 7 args') + sys.exit(255) + filename = Path(sys.argv[2]) + line = int(sys.argv[3]) + column = int(sys.argv[4]) + selection: Optional[str] = (None if sys.argv[5] == '' else sys.argv[5]) + operation = sys.argv[6] + + # This stuff assumes it is being run from project root. + os.chdir(PROJROOT) + + # Set up pypaths so our main distro stuff works. + scriptsdir = os.path.abspath( + os.path.join(os.path.dirname(sys.argv[0]), + '../assets/src/ba_data/python')) + toolsdir = os.path.abspath( + os.path.join(os.path.dirname(sys.argv[0]), '../tools')) + if scriptsdir not in sys.path: + sys.path.append(scriptsdir) + if toolsdir not in sys.path: + sys.path.append(toolsdir) + efrotools.py_examine(PROJROOT, filename, line, column, selection, + operation) + + +def clean_orphaned_assets() -> None: + """Remove asset files that are no longer part of the build.""" + import os + import json + import efrotools + + # Operate from dist root.. + os.chdir(PROJROOT) + + # Our manifest is split into 2 files (public and private) + with open('assets/.asset_manifest_public.json') as infile: + manifest = set(json.loads(infile.read())) + with open('assets/.asset_manifest_private.json') as infile: + manifest.update(set(json.loads(infile.read()))) + for root, _dirs, fnames in os.walk('assets/build'): + for fname in fnames: + fpath = os.path.join(root, fname) + fpathrel = fpath[13:] # paths are relative to assets/build + if fpathrel not in manifest: + print(f'Removing orphaned asset file: {fpath}') + os.unlink(fpath) + + # Lastly, clear empty dirs. + efrotools.run('find assets/build -depth -empty -type d -delete') + + +def fix_mac_ssh() -> None: + """Turn off mac ssh password access. + + (This totally doesn't belong in this project btw..) + """ + configpath = '/etc/ssh/sshd_config' + with open(configpath) as infile: + lines = infile.readlines() + index = lines.index('#PasswordAuthentication yes\n') + lines[index] = 'PasswordAuthentication no\n' + index = lines.index('#ChallengeResponseAuthentication yes\n') + lines[index] = 'ChallengeResponseAuthentication no\n' + index = lines.index('UsePAM yes\n') + lines[index] = 'UsePAM no\n' + with open(configpath, 'w') as outfile: + outfile.write(''.join(lines)) + print('SSH config updated successfully!') + + +def check_mac_ssh() -> None: + """Make sure ssh password access is turned off. + + (This totally doesn't belong here, but I use it it to remind myself to + fix mac ssh after system updates which blow away ssh customizations). + """ + with open('/etc/ssh/sshd_config') as infile: + lines = infile.read().splitlines() + if ('UsePAM yes' in lines or '#PasswordAuthentication yes' in lines + or '#ChallengeResponseAuthentication yes' in lines): + print('ERROR: ssh config is allowing password access.\n' + 'To fix: sudo tools/snippets fix_mac_ssh') + sys.exit(255) + print('password ssh auth seems disabled; hooray!') + + +def resize_image() -> None: + """Resize an image and save it to a new location. + + args: xres, yres, src, dst + """ + import os + import efrotools + if len(sys.argv) != 6: + raise Exception('expected 5 args') + width = int(sys.argv[2]) + height = int(sys.argv[3]) + src = sys.argv[4] + dst = sys.argv[5] + if not dst.endswith('.png'): + raise RuntimeError(f'dst must be a png; got "{dst}"') + if not src.endswith('.png'): + raise RuntimeError(f'src must be a png; got "{src}"') + print('Creating: ' + os.path.basename(dst), file=sys.stderr) + efrotools.run(f'convert "{src}" -resize {width}x{height} "{dst}"') + + +def check_clean_safety() -> None: + """Ensure all files are are added to git or in gitignore. + + Use to avoid losing work if we accidentally do a clean without + adding something. + """ + import os + from efrotools.snippets import check_clean_safety as std_snippet + + # First do standard checks. + std_snippet() + + # Then also make sure there are no untracked changes to core files + # (since we may be blowing core away here). + spinoff_bin = os.path.join(str(PROJROOT), 'tools', 'spinoff') + if os.path.exists(spinoff_bin): + status = os.system(spinoff_bin + ' cleancheck') + if status != 0: + sys.exit(255) + + +def archive_old_builds() -> None: + """Stuff our old public builds into the 'old' dir. + + (called after we push newer ones) + """ + import batools.build + if len(sys.argv) < 3: + raise Exception('invalid arguments') + ssh_server = sys.argv[2] + builds_dir = sys.argv[3] + ssh_args = sys.argv[4:] + batools.build.archive_old_builds(ssh_server, builds_dir, ssh_args) + + +def lazy_increment_build() -> None: + """Increment build number only if C++ sources have changed. + + This is convenient to place in automatic commit/push scripts. + It could make sense to auto update build number when scripts/assets + change too, but a build number change requires rebuilding all binaries + so I'll leave that as an explicit choice to save work. + """ + import os + import subprocess + from efro.terminal import Clr + from efro.error import CleanError + from efrotools import get_files_hash + from efrotools.code import get_code_filenames + if sys.argv[2:] not in [[], ['--update-hash-only']]: + raise CleanError('Invalid arguments') + update_hash_only = '--update-hash-only' in sys.argv + codefiles = get_code_filenames(PROJROOT) + codehash = get_files_hash(codefiles) + hashfilename = '.cache/lazy_increment_build' + try: + with open(hashfilename) as infile: + lasthash = infile.read() + except FileNotFoundError: + lasthash = '' + if codehash != lasthash: + print(f'{Clr.SMAG}Source(s) changed; incrementing build...{Clr.RST}') + + if not update_hash_only: + # Just go ahead and bless; this will increment the build as needed. + # subprocess.run(['make', 'bless'], check=True) + subprocess.run(['tools/version_utils', 'incrementbuild'], + check=True) + + # We probably just changed code, so we need to re-calc the hash. + codehash = get_files_hash(codefiles) + os.makedirs(os.path.dirname(hashfilename), exist_ok=True) + with open(hashfilename, 'w') as outfile: + outfile.write(codehash) + + +def get_master_asset_src_dir() -> None: + """Print master-asset-source dir for this repo.""" + import subprocess + + # Ok, for now lets simply use our hard-coded master-src + # path if we're on master in and not otherwise. Should + # probably make this configurable. + output = subprocess.check_output( + ['git', 'status', '--branch', '--porcelain']).decode() + + # Also compare repo name to split version of itself to + # see if we're outside of core (filtering will cause mismatch if so). + if ('origin/master' in output.splitlines()[0] + and 'ballistica' + 'core' == 'ballisticacore'): + + # We seem to be in master in core repo; lets do it. + print('/Users/ericf/Dropbox/ballisticacore_master_assets') + else: + # Still need to supply dummy path for makefile if not.. + print('/__DUMMY_MASTER_SRC_DISABLED_PATH__') + + +def androidaddr() -> None: + """Return the source file location for an android program-counter. + + command line args: archive_dir architecture addr + """ + import batools.android + from efro.error import CleanError + if len(sys.argv) != 5: + raise CleanError(f'ERROR: expected 3 args; got {len(sys.argv) - 2}\n' + f'Usage: "tools/snippets android_addr' + f' "') + archive_dir = sys.argv[2] + arch = sys.argv[3] + addr = sys.argv[4] + batools.android.androidaddr(archive_dir=archive_dir, arch=arch, addr=addr) + + +def push_ipa() -> None: + """Construct and push ios IPA for testing.""" + from pathlib import Path + import efrotools.ios + root = Path(sys.argv[0], '../..').resolve() + if len(sys.argv) != 3: + raise Exception('expected 1 arg (debug or release)') + modename = sys.argv[2] + efrotools.ios.push_ipa(root, modename) + + +def printcolors() -> None: + """Print all colors available in efro.terminals.TerminalColor.""" + from efro.error import CleanError + from efro.terminal import TerminalColor, Clr + + if Clr.RED == '': + raise CleanError('Efro color terminal output is disabled.') + + clrnames = {getattr(Clr, s): s for s in dir(Clr) if s.isupper()} + + # Print everything in Clr (since that's what users should be using + # but do it in the order of TerminalColor (since Clr is just a class + # so is unordered) + for value in TerminalColor: + if value is TerminalColor.RESET: + continue + shortname = f'Clr.{clrnames[value.value]}' + longname = f'({value.name})' + print(f'{shortname:<12} {longname:<20} {value.value}' + f'The quick brown fox jumps over the lazy dog.' + f'{TerminalColor.RESET.value}') + + +def gen_fulltest_buildfile_android() -> None: + """Generate fulltest command list for jenkins. + + (so we see nice pretty split-up build trees) + """ + import batools.build + batools.build.gen_fulltest_buildfile_android() + + +def gen_fulltest_buildfile_windows() -> None: + """Generate fulltest command list for jenkins. + + (so we see nice pretty split-up build trees) + """ + import batools.build + batools.build.gen_fulltest_buildfile_windows() + + +def gen_fulltest_buildfile_apple() -> None: + """Generate fulltest command list for jenkins. + + (so we see nice pretty split-up build trees) + """ + import batools.build + batools.build.gen_fulltest_buildfile_apple() + + +def gen_fulltest_buildfile_linux() -> None: + """Generate fulltest command list for jenkins. + + (so we see nice pretty split-up build trees) + """ + import batools.build + batools.build.gen_fulltest_buildfile_linux() + + +def python_build_apple() -> None: + """Build an embeddable python for mac/ios/tvos.""" + _python_build_apple(debug=False) + + +def python_build_apple_debug() -> None: + """Build embeddable python for mac/ios/tvos (dbg ver).""" + _python_build_apple(debug=True) + + +def _python_build_apple(debug: bool) -> None: + """Build an embeddable python for macOS/iOS/tvOS.""" + import os + from efrotools import pybuild + os.chdir(PROJROOT) + archs = ('mac', 'ios', 'tvos') + if len(sys.argv) != 3: + print('ERROR: expected one arg: ' + ', '.join(archs)) + sys.exit(255) + arch = sys.argv[2] + if arch not in archs: + print('ERROR: invalid arch. valid values are: ' + ', '.join(archs)) + sys.exit(255) + pybuild.build_apple(arch, debug=debug) + + +def python_build_android() -> None: + """Build an embeddable Python lib for Android.""" + _python_build_android(debug=False) + + +def python_build_android_debug() -> None: + """Build embeddable Android Python lib (debug ver).""" + _python_build_android(debug=True) + + +def _python_build_android(debug: bool) -> None: + import os + from efrotools import pybuild + os.chdir(PROJROOT) + archs = ('arm', 'arm64', 'x86', 'x86_64') + if len(sys.argv) != 3: + print('ERROR: expected one arg: ' + ', '.join(archs)) + sys.exit(255) + arch = sys.argv[2] + if arch not in archs: + print('ERROR: invalid arch. valid values are: ' + ', '.join(archs)) + sys.exit(255) + pybuild.build_android(str(PROJROOT), arch, debug=debug) + + +def python_android_patch() -> None: + """Patches Python to prep for building for Android.""" + import os + from efrotools import pybuild + os.chdir(sys.argv[2]) + pybuild.android_patch() + + +def python_gather() -> None: + """Gather build python components into the project. + + This assumes all embeddable py builds have been run successfully. + """ + import os + from efrotools import pybuild + os.chdir(PROJROOT) + pybuild.gather() + + +def capitalize() -> None: + """Print args capitalized.""" + print(' '.join(w.capitalize() for w in sys.argv[2:])) + + +def efrocache_update() -> None: + """Build & push files to efrocache for public access.""" + from efrotools.efrocache import update_cache + makefile_dirs = ['', 'assets'] + update_cache(makefile_dirs) + + +def efrocache_get() -> None: + """Get a file from efrocache.""" + from efrotools.efrocache import get_target + if len(sys.argv) != 3: + raise RuntimeError('Expected exactly 1 arg') + get_target(sys.argv[2]) + + +def get_modern_make() -> None: + """Print name of a modern make command.""" + import platform + import subprocess + + # Mac gnu make is outdated (due to newer versions using GPL3 I believe). + # so let's return 'gmake' there which will point to homebrew make which + # should be up to date. + if platform.system() == 'Darwin': + if subprocess.run(['which', 'gmake'], check=False, + capture_output=True).returncode != 0: + print( + 'WARNING: this requires gmake (mac system make is too old).' + " Install it with 'brew install make'", + file=sys.stderr, + flush=True) + print('gmake') + else: + print('make') + + +def warm_start_asset_build() -> None: + """Prep asset builds to run faster.""" + import os + import subprocess + from pathlib import Path + from efrotools import getconfig + public: bool = getconfig(PROJROOT)['public'] + + if public: + from efrotools.efrocache import warm_start_cache + os.chdir(PROJROOT) + warm_start_cache() + else: + # For internal builds we don't use efrocache but we do use an + # internal build cache. Download an initial cache/etc. if need be. + subprocess.run( + [str(Path(PROJROOT, 'tools/convert_util')), '--init-asset-cache'], + check=True) + + +def update_docs_md() -> None: + """Updates docs markdown files if necessary.""" + import batools.build + batools.build.update_docs_md(check='--check' in sys.argv) + + +def list_pip_reqs() -> None: + """List Python Pip packages needed for this project.""" + from batools.build import get_pip_reqs + print(' '.join(get_pip_reqs())) + + +def install_pip_reqs() -> None: + """Install Python Pip packages needed for this project.""" + import subprocess + from efrotools import PYTHON_BIN + from efro.terminal import Clr + from batools.build import get_pip_reqs + subprocess.run([PYTHON_BIN, '-m', 'pip', 'install', '--upgrade'] + + get_pip_reqs(), + check=True) + print(f'{Clr.GRN}All pip requirements installed!{Clr.RST}') + + +def checkenv() -> None: + """Check for tools necessary to build and run the app.""" + import batools.build + from efro.error import CleanError + try: + batools.build.checkenv() + except RuntimeError as exc: + raise CleanError(exc) + + +def ensure_prefab_platform() -> None: + """Ensure we are running on a particular prefab platform.""" + import batools.build + from efro.error import CleanError + if len(sys.argv) != 3: + raise CleanError('Expected 1 platform name arg.') + needed = sys.argv[2] + current = batools.build.get_current_prefab_platform() + if current != needed: + raise CleanError( + f'Incorrect platform: we are {current}, this requires {needed}.') + + +def prefab_run_var() -> None: + """Print a var for running a prefab run for the current platform. + + We use this mechanism instead of just having a command recursively run + a make target so that ctrl-c can be handled cleanly and directly by the + command getting run instead of generating extra errors in the recursive + processes. + """ + import batools.build + if len(sys.argv) != 3: + raise RuntimeError('Expected 1 arg.') + base = sys.argv[2].replace('-', '_').upper() + platform = batools.build.get_current_prefab_platform().upper() + print(f'RUN_PREFAB_{platform}_{base}', end='') + + +def make_prefab() -> None: + """Run prefab builds for the current platform.""" + import subprocess + import batools.build + if len(sys.argv) != 3: + raise RuntimeError('Expected one argument') + target = batools.build.PrefabTarget(sys.argv[2]) + platform = batools.build.get_current_prefab_platform() + try: + subprocess.run(['make', f'prefab-{platform}-{target.value}-build'], + check=True) + except (Exception, KeyboardInterrupt) as exc: + if str(exc): + print(f'make_prefab failed with error: {exc}') + sys.exit(-1) + + +def update_makebob() -> None: + """Build fresh make_bob binaries for all relevant platforms.""" + import batools.build + batools.build.update_makebob() + + +def lazybuild() -> None: + """Run a build command only if an input has changed.""" + import subprocess + import batools.build + from efro.error import CleanError + if len(sys.argv) < 5: + raise CleanError('Expected at least 3 args') + try: + category = batools.build.SourceCategory(sys.argv[2]) + except ValueError as exc: + raise CleanError(exc) + target = sys.argv[3] + command = ' '.join(sys.argv[4:]) + try: + batools.build.lazybuild(target, category, command) + except subprocess.CalledProcessError as exc: + raise CleanError(exc) diff --git a/tools/snippets b/tools/snippets index 25bb7722..7a5d17eb 100755 --- a/tools/snippets +++ b/tools/snippets @@ -32,10 +32,10 @@ from __future__ import annotations # Note: import as little as possible here at the module level to # keep launch times fast for small snippets. -import sys from typing import TYPE_CHECKING -# Pull in some standard snippets we want to expose. +# Pull in the snippets we want to expose. Its more efficient to define them in +# modules rather than inline here because we'll be able to load them via pyc. # pylint: disable=unused-import from efrotools.snippets import ( PROJROOT, snippets_main, formatcode, formatscripts, formatmakefile, @@ -43,554 +43,22 @@ from efrotools.snippets import ( sync, sync_all, scriptfiles, pycharm, clioncode, androidstudiocode, makefile_target_list, spelling, spelling_all, pytest, echo, compile_python_files) +from batools.snippets import ( + stage_server_file, py_examine, fix_mac_ssh, check_mac_ssh, resize_image, + check_clean_safety, clean_orphaned_assets, archive_old_builds, + lazy_increment_build, get_master_asset_src_dir, androidaddr, push_ipa, + printcolors, gen_fulltest_buildfile_android, + gen_fulltest_buildfile_windows, gen_fulltest_buildfile_apple, + gen_fulltest_buildfile_linux, python_build_apple, python_build_apple_debug, + python_build_android, python_build_android_debug, python_android_patch, + python_gather, capitalize, efrocache_update, efrocache_get, + get_modern_make, warm_start_asset_build, update_docs_md, list_pip_reqs, + install_pip_reqs, checkenv, ensure_prefab_platform, prefab_run_var, + make_prefab, update_makebob, lazybuild) # pylint: enable=unused-import if TYPE_CHECKING: - from typing import Optional - - -def archive_old_builds() -> None: - """Stuff our old public builds into the 'old' dir. - - (called after we push newer ones) - """ - import batools.build - if len(sys.argv) < 3: - raise Exception('invalid arguments') - ssh_server = sys.argv[2] - builds_dir = sys.argv[3] - ssh_args = sys.argv[4:] - batools.build.archive_old_builds(ssh_server, builds_dir, ssh_args) - - -def gen_fulltest_buildfile_android() -> None: - """Generate fulltest command list for jenkins. - - (so we see nice pretty split-up build trees) - """ - import batools.build - batools.build.gen_fulltest_buildfile_android() - - -def gen_fulltest_buildfile_windows() -> None: - """Generate fulltest command list for jenkins. - - (so we see nice pretty split-up build trees) - """ - import batools.build - batools.build.gen_fulltest_buildfile_windows() - - -def gen_fulltest_buildfile_apple() -> None: - """Generate fulltest command list for jenkins. - - (so we see nice pretty split-up build trees) - """ - import batools.build - batools.build.gen_fulltest_buildfile_apple() - - -def gen_fulltest_buildfile_linux() -> None: - """Generate fulltest command list for jenkins. - - (so we see nice pretty split-up build trees) - """ - import batools.build - batools.build.gen_fulltest_buildfile_linux() - - -def resize_image() -> None: - """Resize an image and save it to a new location. - - args: xres, yres, src, dst - """ - import os - import efrotools - if len(sys.argv) != 6: - raise Exception('expected 5 args') - width = int(sys.argv[2]) - height = int(sys.argv[3]) - src = sys.argv[4] - dst = sys.argv[5] - if not dst.endswith('.png'): - raise RuntimeError(f'dst must be a png; got "{dst}"') - if not src.endswith('.png'): - raise RuntimeError(f'src must be a png; got "{src}"') - print('Creating: ' + os.path.basename(dst), file=sys.stderr) - efrotools.run(f'convert "{src}" -resize {width}x{height} "{dst}"') - - -def check_clean_safety() -> None: - """Ensure all files are are added to git or in gitignore. - - Use to avoid losing work if we accidentally do a clean without - adding something. - """ - import os - from efrotools.snippets import check_clean_safety as std_snippet - - # First do standard checks. - std_snippet() - - # Then also make sure there are no untracked changes to core files - # (since we may be blowing core away here). - spinoff_bin = os.path.join(str(PROJROOT), 'tools', 'spinoff') - if os.path.exists(spinoff_bin): - status = os.system(spinoff_bin + ' cleancheck') - if status != 0: - sys.exit(255) - - -def get_master_asset_src_dir() -> None: - """Print master-asset-source dir for this repo.""" - import subprocess - - # Ok, for now lets simply use our hard-coded master-src - # path if we're on master in and not otherwise. Should - # probably make this configurable. - output = subprocess.check_output( - ['git', 'status', '--branch', '--porcelain']).decode() - - # Also compare repo name to split version of itself to - # see if we're outside of core (filtering will cause mismatch if so). - if ('origin/master' in output.splitlines()[0] - and 'ballistica' + 'core' == 'ballisticacore'): - - # We seem to be in master in core repo; lets do it. - print('/Users/ericf/Dropbox/ballisticacore_master_assets') - else: - # Still need to supply dummy path for makefile if not.. - print('/__DUMMY_MASTER_SRC_DISABLED_PATH__') - - -def androidaddr() -> None: - """Return the source file location for an android program-counter. - - command line args: archive_dir architecture addr - """ - import batools.android - from efro.error import CleanError - if len(sys.argv) != 5: - raise CleanError(f'ERROR: expected 3 args; got {len(sys.argv) - 2}\n' - f'Usage: "tools/snippets android_addr' - f' "') - archive_dir = sys.argv[2] - arch = sys.argv[3] - addr = sys.argv[4] - batools.android.androidaddr(archive_dir=archive_dir, arch=arch, addr=addr) - - -def python_build_apple() -> None: - """Build an embeddable python for mac/ios/tvos.""" - _python_build_apple(debug=False) - - -def python_build_apple_debug() -> None: - """Build embeddable python for mac/ios/tvos (dbg ver).""" - _python_build_apple(debug=True) - - -def _python_build_apple(debug: bool) -> None: - """Build an embeddable python for macOS/iOS/tvOS.""" - import os - from efrotools import pybuild - os.chdir(PROJROOT) - archs = ('mac', 'ios', 'tvos') - if len(sys.argv) != 3: - print('ERROR: expected one arg: ' + ', '.join(archs)) - sys.exit(255) - arch = sys.argv[2] - if arch not in archs: - print('ERROR: invalid arch. valid values are: ' + ', '.join(archs)) - sys.exit(255) - pybuild.build_apple(arch, debug=debug) - - -def python_build_android() -> None: - """Build an embeddable Python lib for Android.""" - _python_build_android(debug=False) - - -def python_build_android_debug() -> None: - """Build embeddable Android Python lib (debug ver).""" - _python_build_android(debug=True) - - -def _python_build_android(debug: bool) -> None: - import os - from efrotools import pybuild - os.chdir(PROJROOT) - archs = ('arm', 'arm64', 'x86', 'x86_64') - if len(sys.argv) != 3: - print('ERROR: expected one arg: ' + ', '.join(archs)) - sys.exit(255) - arch = sys.argv[2] - if arch not in archs: - print('ERROR: invalid arch. valid values are: ' + ', '.join(archs)) - sys.exit(255) - pybuild.build_android(str(PROJROOT), arch, debug=debug) - - -def python_android_patch() -> None: - """Patches Python to prep for building for Android.""" - import os - from efrotools import pybuild - os.chdir(sys.argv[2]) - pybuild.android_patch() - - -def python_gather() -> None: - """Gather build python components into the project. - - This assumes all embeddable py builds have been run successfully. - """ - import os - from efrotools import pybuild - os.chdir(PROJROOT) - pybuild.gather() - - -def clean_orphaned_assets() -> None: - """Remove asset files that are no longer part of the build.""" - import os - import json - import efrotools - - # Operate from dist root.. - os.chdir(PROJROOT) - - # Our manifest is split into 2 files (public and private) - with open('assets/.asset_manifest_public.json') as infile: - manifest = set(json.loads(infile.read())) - with open('assets/.asset_manifest_private.json') as infile: - manifest.update(set(json.loads(infile.read()))) - for root, _dirs, fnames in os.walk('assets/build'): - for fname in fnames: - fpath = os.path.join(root, fname) - fpathrel = fpath[13:] # paths are relative to assets/build - if fpathrel not in manifest: - print(f'Removing orphaned asset file: {fpath}') - os.unlink(fpath) - - # Lastly, clear empty dirs. - efrotools.run('find assets/build -depth -empty -type d -delete') - - -def py_examine() -> None: - """Run a python examination at a given point in a given file.""" - import os - from pathlib import Path - import efrotools - if len(sys.argv) != 7: - print('ERROR: expected 7 args') - sys.exit(255) - filename = Path(sys.argv[2]) - line = int(sys.argv[3]) - column = int(sys.argv[4]) - selection: Optional[str] = (None if sys.argv[5] == '' else sys.argv[5]) - operation = sys.argv[6] - - # This stuff assumes it is being run from project root. - os.chdir(PROJROOT) - - # Set up pypaths so our main distro stuff works. - scriptsdir = os.path.abspath( - os.path.join(os.path.dirname(sys.argv[0]), - '../assets/src/ba_data/python')) - toolsdir = os.path.abspath( - os.path.join(os.path.dirname(sys.argv[0]), '../tools')) - if scriptsdir not in sys.path: - sys.path.append(scriptsdir) - if toolsdir not in sys.path: - sys.path.append(toolsdir) - efrotools.py_examine(PROJROOT, filename, line, column, selection, - operation) - - -def push_ipa() -> None: - """Construct and push ios IPA for testing.""" - from pathlib import Path - import efrotools.ios - root = Path(sys.argv[0], '../..').resolve() - if len(sys.argv) != 3: - raise Exception('expected 1 arg (debug or release)') - modename = sys.argv[2] - efrotools.ios.push_ipa(root, modename) - - -def fix_mac_ssh() -> None: - """Turn off mac ssh password access. - - (This totally doesn't belong in this project btw..) - """ - configpath = '/etc/ssh/sshd_config' - with open(configpath) as infile: - lines = infile.readlines() - index = lines.index('#PasswordAuthentication yes\n') - lines[index] = 'PasswordAuthentication no\n' - index = lines.index('#ChallengeResponseAuthentication yes\n') - lines[index] = 'ChallengeResponseAuthentication no\n' - index = lines.index('UsePAM yes\n') - lines[index] = 'UsePAM no\n' - with open(configpath, 'w') as outfile: - outfile.write(''.join(lines)) - print('SSH config updated successfully!') - - -def check_mac_ssh() -> None: - """Make sure ssh password access is turned off. - - (This totally doesn't belong here, but I use it it to remind myself to - fix mac ssh after system updates which blow away ssh customizations). - """ - with open('/etc/ssh/sshd_config') as infile: - lines = infile.read().splitlines() - if ('UsePAM yes' in lines or '#PasswordAuthentication yes' in lines - or '#ChallengeResponseAuthentication yes' in lines): - print('ERROR: ssh config is allowing password access.\n' - 'To fix: sudo tools/snippets fix_mac_ssh') - sys.exit(255) - print('password ssh auth seems disabled; hooray!') - - -def capitalize() -> None: - """Print args capitalized.""" - print(' '.join(w.capitalize() for w in sys.argv[2:])) - - -def efrocache_update() -> None: - """Build & push files to efrocache for public access.""" - from efrotools.efrocache import update_cache - makefile_dirs = ['', 'assets'] - update_cache(makefile_dirs) - - -def efrocache_get() -> None: - """Get a file from efrocache.""" - from efrotools.efrocache import get_target - if len(sys.argv) != 3: - raise RuntimeError('Expected exactly 1 arg') - get_target(sys.argv[2]) - - -def get_modern_make() -> None: - """Print name of a modern make command.""" - import platform - import subprocess - - # Mac gnu make is outdated (due to newer versions using GPL3 I believe). - # so let's return 'gmake' there which will point to homebrew make which - # should be up to date. - if platform.system() == 'Darwin': - if subprocess.run(['which', 'gmake'], check=False, - capture_output=True).returncode != 0: - print( - 'WARNING: this requires gmake (mac system make is too old).' - " Install it with 'brew install make'", - file=sys.stderr, - flush=True) - print('gmake') - else: - print('make') - - -def warm_start_asset_build() -> None: - """Prep asset builds to run faster.""" - import os - import subprocess - from pathlib import Path - from efrotools import getconfig - public: bool = getconfig(PROJROOT)['public'] - - if public: - from efrotools.efrocache import warm_start_cache - os.chdir(PROJROOT) - warm_start_cache() - else: - # For internal builds we don't use efrocache but we do use an - # internal build cache. Download an initial cache/etc. if need be. - subprocess.run( - [str(Path(PROJROOT, 'tools/convert_util')), '--init-asset-cache'], - check=True) - - -def update_docs_md() -> None: - """Updates docs markdown files if necessary.""" - import batools.build - batools.build.update_docs_md(check='--check' in sys.argv) - - -def list_pip_reqs() -> None: - """List Python Pip packages needed for this project.""" - from batools.build import get_pip_reqs - print(' '.join(get_pip_reqs())) - - -def install_pip_reqs() -> None: - """Install Python Pip packages needed for this project.""" - import subprocess - from efrotools import PYTHON_BIN - from efro.terminal import Clr - from batools.build import get_pip_reqs - subprocess.run([PYTHON_BIN, '-m', 'pip', 'install', '--upgrade'] + - get_pip_reqs(), - check=True) - print(f'{Clr.GRN}All pip requirements installed!{Clr.RST}') - - -def checkenv() -> None: - """Check for tools necessary to build and run the app.""" - import batools.build - from efro.error import CleanError - try: - batools.build.checkenv() - except RuntimeError as exc: - raise CleanError(exc) - - -def ensure_prefab_platform() -> None: - """Ensure we are running on a particular prefab platform.""" - import batools.build - from efro.error import CleanError - if len(sys.argv) != 3: - raise CleanError('Expected 1 platform name arg.') - needed = sys.argv[2] - current = batools.build.get_current_prefab_platform() - if current != needed: - raise CleanError( - f'Incorrect platform: we are {current}, this requires {needed}.') - - -def prefab_run_var() -> None: - """Print a var for running a prefab run for the current platform. - - We use this mechanism instead of just having a command recursively run - a make target so that ctrl-c can be handled cleanly and directly by the - command getting run instead of generating extra errors in the recursive - processes. - """ - import batools.build - if len(sys.argv) != 3: - raise RuntimeError('Expected 1 arg.') - base = sys.argv[2].replace('-', '_').upper() - platform = batools.build.get_current_prefab_platform().upper() - print(f'RUN_PREFAB_{platform}_{base}', end='') - - -def make_prefab() -> None: - """Run prefab builds for the current platform.""" - import subprocess - import batools.build - if len(sys.argv) != 3: - raise RuntimeError('Expected one argument') - target = batools.build.PrefabTarget(sys.argv[2]) - platform = batools.build.get_current_prefab_platform() - try: - subprocess.run(['make', f'prefab-{platform}-{target.value}-build'], - check=True) - except (Exception, KeyboardInterrupt) as exc: - if str(exc): - print(f'make_prefab failed with error: {exc}') - sys.exit(-1) - - -def update_makebob() -> None: - """Build fresh make_bob binaries for all relevant platforms.""" - import batools.build - batools.build.update_makebob() - - -def lazybuild() -> None: - """Run a build command only if an input has changed.""" - import subprocess - import batools.build - from efro.error import CleanError - if len(sys.argv) < 5: - raise CleanError('Expected at least 3 args') - try: - category = batools.build.SourceCategory(sys.argv[2]) - except ValueError as exc: - raise CleanError(exc) - target = sys.argv[3] - command = ' '.join(sys.argv[4:]) - try: - batools.build.lazybuild(target, category, command) - except subprocess.CalledProcessError as exc: - raise CleanError(exc) - - -def filter_server_config() -> None: - """Add commented-out config options to a server config.""" - import batools.build - from efro.error import CleanError - if len(sys.argv) != 4: - raise CleanError('Expected 2 args (infile and outfile).') - batools.build.filter_server_config(str(PROJROOT), sys.argv[2], sys.argv[3]) - - -def printcolors() -> None: - """Print all colors available in efro.terminals.TerminalColor.""" - from efro.error import CleanError - from efro.terminal import TerminalColor, Clr - - if Clr.RED == '': - raise CleanError('Efro color terminal output is disabled.') - - clrnames = {getattr(Clr, s): s for s in dir(Clr) if s.isupper()} - - # Print everything in Clr (since that's what users should be using - # but do it in the order of TerminalColor (since Clr is just a class - # so is unordered) - for value in TerminalColor: - if value is TerminalColor.RESET: - continue - shortname = f'Clr.{clrnames[value.value]}' - longname = f'({value.name})' - print(f'{shortname:<12} {longname:<20} {value.value}' - f'The quick brown fox jumps over the lazy dog.' - f'{TerminalColor.RESET.value}') - - -def lazy_increment_build() -> None: - """Increment build number only if C++ sources have changed. - - This is convenient to place in automatic commit/push scripts. - It could make sense to auto update build number when scripts/assets - change too, but a build number change requires rebuilding all binaries - so I'll leave that as an explicit choice to save work. - """ - import os - import subprocess - from efro.terminal import Clr - from efro.error import CleanError - from efrotools import get_files_hash - from efrotools.code import get_code_filenames - if sys.argv[2:] not in [[], ['--update-hash-only']]: - raise CleanError('Invalid arguments') - update_hash_only = '--update-hash-only' in sys.argv - codefiles = get_code_filenames(PROJROOT) - codehash = get_files_hash(codefiles) - hashfilename = '.cache/lazy_increment_build' - try: - with open(hashfilename) as infile: - lasthash = infile.read() - except FileNotFoundError: - lasthash = '' - if codehash != lasthash: - print(f'{Clr.SMAG}Source(s) changed; incrementing build...{Clr.RST}') - - if not update_hash_only: - # Just go ahead and bless; this will increment the build as needed. - # subprocess.run(['make', 'bless'], check=True) - subprocess.run(['tools/version_utils', 'incrementbuild'], - check=True) - - # We probably just changed code, so we need to re-calc the hash. - codehash = get_files_hash(codefiles) - os.makedirs(os.path.dirname(hashfilename), exist_ok=True) - with open(hashfilename, 'w') as outfile: - outfile.write(codehash) - + pass if __name__ == '__main__': snippets_main(globals())