cleaned up the project-update script a bit

This commit is contained in:
Eric Froemling 2019-10-07 05:36:48 -07:00
parent 7d800fd5a6
commit b22c29bd43

View File

@ -31,22 +31,6 @@ CLRRED = '\033[91m' # Red.
CLREND = '\033[0m' # End. CLREND = '\033[0m' # End.
def _find_files(scan_dir: str) -> Tuple[List[str], List[str]]:
src_files = set()
header_files = set()
exts = ['.c', '.cc', '.cpp', '.cxx', '.m', '.mm']
header_exts = ['.h']
# Gather all sources and headers.
for root, _dirs, files in os.walk(scan_dir):
for ftst in files:
if any(ftst.endswith(ext) for ext in exts):
src_files.add(os.path.join(root, ftst)[len(scan_dir):])
if any(ftst.endswith(ext) for ext in header_exts):
header_files.add(os.path.join(root, ftst)[len(scan_dir):])
return sorted(src_files), sorted(header_files)
def _check_files(src_files: Sequence[str]) -> None: def _check_files(src_files: Sequence[str]) -> None:
# A bit of sanity/lint-testing while we're here. # A bit of sanity/lint-testing while we're here.
@ -109,28 +93,114 @@ def _check_headers(header_files: Sequence[str], fixable_header_errors: Dict,
sys.exit(255) sys.exit(255)
def _update_cmake_file(fname: str, src_files: List[str], class App:
header_files: List[str], """Context for an app run."""
files_to_write: Dict[str, str]) -> None:
with open(fname) as infile: def __init__(self) -> None:
self._check = ('--check' in sys.argv)
self._fix = ('--fix' in sys.argv)
self._checkarg = ' --check' if self._check else ''
self._files_to_write: Dict[str, str] = {}
self._src_files: List[str] = []
self._header_files: List[str] = []
self._fixable_header_errors: Dict[str, List[Tuple[int, str]]] = {}
def run(self) -> None:
"""Do the thing."""
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements
# Make sure we're operating from a project root.
if not os.path.isdir('config') or not os.path.isdir('tools'):
raise Exception('This must be run from a project root.')
# NOTE: Do py-enums before updating asset deps since it is an asset.
self._update_python_enums_module()
self._update_resources_makefile()
self._update_generated_code_makefile()
self._update_assets_makefile()
self._check_python_files()
self._check_sync_states()
# Now go through and update our various build files
# (MSVC, CMake, Android NDK, etc) as well as sanity-testing/fixing
# various other files such as headers while we're at it.
# Grab sources/headers.
self._find_sources_and_headers('src/ballistica')
# Run some checks on them.
_check_files(self._src_files)
_check_headers(self._header_files, self._fixable_header_errors,
self._fix)
self._update_cmake_files()
self._update_visual_studio_projects()
# If we're all good to here, do the actual writes.
# First, write any header fixes.
if self._fixable_header_errors:
for filename, fixes in self._fixable_header_errors.items():
with open(filename, 'r') as infile:
lines = infile.read().splitlines() lines = infile.read().splitlines()
auto_start = lines.index(' #AUTOGENERATED_BEGIN (this section' for fix_line, fix_str in fixes:
' is managed by the "update_project" tool)') lines[fix_line] = fix_str
auto_end = lines.index(' #AUTOGENERATED_END') with open(filename, 'w') as outfile:
our_lines = [ outfile.write('\n'.join(lines) + '\n')
' ${BA_SRC_ROOT}/ballistica' + f print(CLRBLU + 'Writing header: ' + filename + CLREND)
for f in sorted(src_files + header_files) else:
if not f.endswith('.mm') and not f.endswith('.m') print(f'No issues found in {len(self._header_files)} headers.')
]
filtered = lines[:auto_start + 1] + our_lines + lines[auto_end:]
files_to_write[fname] = '\n'.join(filtered) + '\n'
# Now write out any project files that have changed
# (or error if we're in check mode).
unchanged_project_count = 0
for fname, fcode in self._files_to_write.items():
f_orig: Optional[str]
if os.path.exists(fname):
with open(fname, 'r') as infile:
f_orig = infile.read()
else:
f_orig = None
if f_orig == fcode.replace('\r\n', '\n'):
unchanged_project_count += 1
else:
if self._check:
print(f'{CLRRED}ERROR: found out-of-date'
f' project file: {fname}{CLREND}')
sys.exit(255)
def _update_visual_studio_project(fname: str, src_root: str, print(f'{CLRBLU}Writing project file: {fname}{CLREND}')
src_files: List[str], with open(fname, 'w') as outfile:
header_files: List[str], outfile.write(fcode)
files_to_write: Dict[str, str]) -> None: if unchanged_project_count > 0:
print(f'All {unchanged_project_count} project files up to date.')
# Update our local compile-commands file based on any changes to
# our cmake stuff. Do this at end so cmake changes already happened.
if not self._check and os.path.exists('ballisticacore-cmake'):
if os.system('make .irony/compile_commands.json') != 0:
print(CLRRED + 'Error updating compile-commands.' + CLREND)
sys.exit(255)
# Lastly update our dummy _ba module.
# We need to do this very last because it may run the cmake build
# so its success may depend on the cmake build files having already
# been updated.
if os.path.exists('tools/gendummymodule.py'):
if os.system('tools/gendummymodule.py' + self._checkarg) != 0:
print(CLRRED + 'Error checking/updating dummy module' + CLREND)
sys.exit(255)
if self._check:
print('Check-Builds: Everything up to date.')
else:
print('Update-Builds: SUCCESS!')
def _update_visual_studio_project(self, fname: str, src_root: str) -> None:
# pylint: disable=too-many-locals # pylint: disable=too-many-locals
with open(fname) as infile: with open(fname) as infile:
lines = infile.read().splitlines() lines = infile.read().splitlines()
@ -140,8 +210,9 @@ def _update_visual_studio_project(fname: str, src_root: str,
# all_files = sorted(src_files + header_files) # all_files = sorted(src_files + header_files)
# del header_files # Unused. # del header_files # Unused.
all_files = sorted([ all_files = sorted([
f for f in (src_files + header_files) if not f.endswith('.m') f for f in (self._src_files + self._header_files)
and not f.endswith('.mm') and not f.endswith('.c') if not f.endswith('.m') and not f.endswith('.mm')
and not f.endswith('.c')
]) ])
# Find the ItemGroup containing stdafx.cpp. This is where we'll dump # Find the ItemGroup containing stdafx.cpp. This is where we'll dump
@ -164,12 +235,12 @@ def _update_visual_studio_project(fname: str, src_root: str,
# precompiled header stuff. (shouldn't be a problem though). # precompiled header stuff. (shouldn't be a problem though).
group_lines = [ group_lines = [
' <' + ' <' +
('ClInclude' if src.endswith('.h') else 'ClCompile') + ' Include="' + ('ClInclude' if src.endswith('.h') else 'ClCompile') + ' Include="'
src_root + '\\ballistica' + src.replace('/', '\\') + '" />' + src_root + '\\ballistica' + src.replace('/', '\\') + '" />'
for src in all_files for src in all_files
] + group_lines ] + group_lines
filtered = lines[:begin_index + 1] + group_lines + lines[end_index:] filtered = lines[:begin_index + 1] + group_lines + lines[end_index:]
files_to_write[fname] = '\r\n'.join(filtered) + '\r\n' self._files_to_write[fname] = '\r\n'.join(filtered) + '\r\n'
filterpaths: Set[str] = set() filterpaths: Set[str] = set()
filterlines: List[str] = [ filterlines: List[str] = [
@ -182,7 +253,8 @@ def _update_visual_studio_project(fname: str, src_root: str,
for line in sourcelines: for line in sourcelines:
entrytype = line.strip().split()[0][1:] entrytype = line.strip().split()[0][1:]
path = line.split('"')[1] path = line.split('"')[1]
filterlines.append(' <' + entrytype + ' Include="' + path + '">') filterlines.append(' <' + entrytype + ' Include="' + path +
'">')
# If we have a dir foo/bar/eep we need to create filters for # If we have a dir foo/bar/eep we need to create filters for
# each of foo, foo/bar, and foo/bar/eep # each of foo, foo/bar, and foo/bar/eep
@ -191,7 +263,8 @@ def _update_visual_studio_project(fname: str, src_root: str,
splits = splits[:-1] splits = splits[:-1]
for i in range(len(splits)): for i in range(len(splits)):
filterpaths.add('\\'.join(splits[:(i + 1)])) filterpaths.add('\\'.join(splits[:(i + 1)]))
filterlines.append(' <Filter>' + '\\'.join(splits) + '</Filter>') filterlines.append(' <Filter>' + '\\'.join(splits) +
'</Filter>')
filterlines.append(' </' + entrytype + '>') filterlines.append(' </' + entrytype + '>')
filterlines += [ filterlines += [
' </ItemGroup>', ' </ItemGroup>',
@ -204,52 +277,63 @@ def _update_visual_studio_project(fname: str, src_root: str,
'</Project>', '</Project>',
] ]
files_to_write[fname + '.filters'] = '\r\n'.join(filterlines) + '\r\n' self._files_to_write[fname +
'.filters'] = '\r\n'.join(filterlines) + '\r\n'
def _update_visual_studio_projects(self) -> None:
fname = 'ballisticacore-windows/BallisticaCore/BallisticaCore.vcxproj'
if os.path.exists(fname):
self._update_visual_studio_project(fname, '..\\..\\src')
fname = ('ballisticacore-windows/BallisticaCoreHeadless/'
'BallisticaCoreHeadless.vcxproj')
if os.path.exists(fname):
self._update_visual_studio_project(fname, '..\\..\\src')
fname = ('ballisticacore-windows/BallisticaCoreOculus'
'/BallisticaCoreOculus.vcxproj')
if os.path.exists(fname):
self._update_visual_studio_project(fname, '..\\..\\src')
def main() -> None: def _update_cmake_file(self, fname: str) -> None:
"""Main script entry point."""
# pylint: disable=too-many-locals with open(fname) as infile:
# pylint: disable=too-many-branches lines = infile.read().splitlines()
# pylint: disable=too-many-statements auto_start = lines.index(' #AUTOGENERATED_BEGIN (this section'
' is managed by the "update_project" tool)')
auto_end = lines.index(' #AUTOGENERATED_END')
our_lines = [
' ${BA_SRC_ROOT}/ballistica' + f
for f in sorted(self._src_files + self._header_files)
if not f.endswith('.mm') and not f.endswith('.m')
]
filtered = lines[:auto_start + 1] + our_lines + lines[auto_end:]
self._files_to_write[fname] = '\n'.join(filtered) + '\n'
# Make sure we're operating from dist root. def _update_cmake_files(self) -> None:
os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '..')) fname = 'ballisticacore-cmake/CMakeLists.txt'
if os.path.exists(fname):
self._update_cmake_file(fname)
fname = ('ballisticacore-android/BallisticaCore'
'/src/main/cpp/CMakeLists.txt')
if os.path.exists(fname):
self._update_cmake_file(fname)
check = ('--check' in sys.argv) def _find_sources_and_headers(self, scan_dir: str) -> None:
fix = ('--fix' in sys.argv) src_files = set()
header_files = set()
exts = ['.c', '.cc', '.cpp', '.cxx', '.m', '.mm']
header_exts = ['.h']
checkarg = ' --check' if check else '' # Gather all sources and headers.
for root, _dirs, files in os.walk(scan_dir):
# Update our python enums module. for ftst in files:
# Should do this before updating asset deps since this is an asset. if any(ftst.endswith(ext) for ext in exts):
if os.path.exists('tools/update_python_enums_module'): src_files.add(os.path.join(root, ftst)[len(scan_dir):])
if os.system('tools/update_python_enums_module' + checkarg) != 0: if any(ftst.endswith(ext) for ext in header_exts):
print(CLRRED + 'Error checking/updating python enums module' + header_files.add(os.path.join(root, ftst)[len(scan_dir):])
CLREND) self._src_files = sorted(src_files)
sys.exit(255) self._header_files = sorted(header_files)
# Update our resources Makefile.
if os.path.exists('tools/update_resources_makefile'):
if os.system('tools/update_resources_makefile' + checkarg) != 0:
print(CLRRED + 'Error checking/updating resources Makefile' +
CLREND)
sys.exit(255)
# Update our generated-code Makefile.
if os.path.exists('tools/update_generated_code_makefile'):
if os.system('tools/update_generated_code_makefile' + checkarg) != 0:
print(CLRRED + 'Error checking/updating generated-code Makefile' +
CLREND)
sys.exit(255)
# Update our assets Makefile.
if os.path.exists('tools/update_assets_makefile'):
if os.system('tools/update_assets_makefile' + checkarg) != 0:
print(CLRRED + 'Error checking/updating assets Makefile' + CLREND)
sys.exit(255)
def _check_python_files(self) -> None:
# Make sure all module dirs in python scripts contain an __init__.py # Make sure all module dirs in python scripts contain an __init__.py
scripts_dir = 'assets/src/data/scripts' scripts_dir = 'assets/src/data/scripts'
for root, _dirs, files in os.walk(scripts_dir): for root, _dirs, files in os.walk(scripts_dir):
@ -260,6 +344,7 @@ def main() -> None:
root + CLREND) root + CLREND)
sys.exit(255) sys.exit(255)
def _check_sync_states(self) -> None:
# Make sure none of our sync targets have been mucked with since # Make sure none of our sync targets have been mucked with since
# their last sync. # their last sync.
if os.system('tools/snippets sync check') != 0: if os.system('tools/snippets sync check') != 0:
@ -267,131 +352,38 @@ def main() -> None:
CLREND) CLREND)
sys.exit(255) sys.exit(255)
# Now go through and update our various build files def _update_assets_makefile(self) -> None:
# (MSVC, CMake, Android NDK, etc) as well as sanity-testing/fixing if os.path.exists('tools/update_assets_makefile'):
# various other files such as headers while we're at it. if os.system('tools/update_assets_makefile' + self._checkarg) != 0:
print(CLRRED + 'Error checking/updating assets Makefile' +
files_to_write: Dict[str, str] = {} CLREND)
# Grab sources/headers.
src_files, header_files = _find_files('src/ballistica')
# Run some checks on them.
_check_files(src_files)
fixable_header_errors: Dict[str, List[Tuple[int, str]]] = {}
_check_headers(header_files, fixable_header_errors, fix)
# Now update various builds.
# CMake
fname = 'ballisticacore-cmake/CMakeLists.txt'
if os.path.exists(fname):
_update_cmake_file(
fname,
src_files,
header_files,
files_to_write,
)
fname = 'ballisticacore-android/BallisticaCore/src/main/cpp/CMakeLists.txt'
if os.path.exists(fname):
_update_cmake_file(
fname,
src_files,
header_files,
files_to_write,
)
# Visual Studio
fname = 'ballisticacore-windows/BallisticaCore/BallisticaCore.vcxproj'
if os.path.exists(fname):
_update_visual_studio_project(
fname,
'..\\..\\src',
src_files,
header_files,
files_to_write,
)
fname = ('ballisticacore-windows/BallisticaCoreHeadless/'
'BallisticaCoreHeadless.vcxproj')
if os.path.exists(fname):
_update_visual_studio_project(
fname,
'..\\..\\src',
src_files,
header_files,
files_to_write,
)
fname = ('ballisticacore-windows/BallisticaCoreOculus'
'/BallisticaCoreOculus.vcxproj')
if os.path.exists(fname):
_update_visual_studio_project(
fname,
'..\\..\\src',
src_files,
header_files,
files_to_write,
)
# If we're all good to here, do the actual writes.
# First, write any header fixes.
if fixable_header_errors:
for filename, fixes in fixable_header_errors.items():
with open(filename, 'r') as infile:
lines = infile.read().splitlines()
for fix_line, fix_str in fixes:
lines[fix_line] = fix_str
with open(filename, 'w') as outfile:
outfile.write('\n'.join(lines) + '\n')
print(CLRBLU + 'Writing header: ' + filename + CLREND)
else:
print(f'No issues found in {len(header_files)} headers.')
# Now write out any project files that have changed
# (or error if we're in check mode).
unchanged_project_count = 0
for fname, fcode in files_to_write.items():
f_orig: Optional[str]
if os.path.exists(fname):
with open(fname, 'r') as infile:
f_orig = infile.read()
else:
f_orig = None
if f_orig == fcode.replace('\r\n', '\n'):
unchanged_project_count += 1
else:
if check:
print(f'{CLRRED}ERROR: found out-of-date'
f' project file: {fname}{CLREND}')
sys.exit(255) sys.exit(255)
print(f'{CLRBLU}Writing project file: {fname}{CLREND}') def _update_generated_code_makefile(self) -> None:
with open(fname, 'w') as outfile: if os.path.exists('tools/update_generated_code_makefile'):
outfile.write(fcode) if os.system('tools/update_generated_code_makefile' +
if unchanged_project_count > 0: self._checkarg) != 0:
print(f'All {unchanged_project_count} project files up to date.') print(CLRRED +
'Error checking/updating generated-code Makefile' +
# Update our local compile-commands file based on any changes to CLREND)
# our cmake stuff. Do this at end so cmake changes already happened.
if not check and os.path.exists('ballisticacore-cmake'):
if os.system('make .irony/compile_commands.json') != 0:
print(CLRRED + 'Error updating compile-commands.' + CLREND)
sys.exit(255) sys.exit(255)
# Lastly update our dummy _ba module. def _update_resources_makefile(self) -> None:
# We need to do this very last because it may run the cmake build if os.path.exists('tools/update_resources_makefile'):
# so its success may depend on the cmake build files having already if os.system('tools/update_resources_makefile' +
# been updated. self._checkarg) != 0:
if os.path.exists('tools/gendummymodule.py'): print(CLRRED + 'Error checking/updating resources Makefile' +
if os.system('tools/gendummymodule.py' + checkarg) != 0: CLREND)
print(CLRRED + 'Error checking/updating dummy module' + CLREND)
sys.exit(255) sys.exit(255)
if check: def _update_python_enums_module(self) -> None:
print('Check-Builds: Everything up to date.') if os.path.exists('tools/update_python_enums_module'):
else: if os.system('tools/update_python_enums_module' +
print('Update-Builds: SUCCESS!') self._checkarg) != 0:
print(CLRRED + 'Error checking/updating python enums module' +
CLREND)
sys.exit(255)
if __name__ == '__main__': if __name__ == '__main__':
main() App().run()