Index: llvm/trunk/utils/gn/build/sync_source_lists_from_cmake.py =================================================================== --- llvm/trunk/utils/gn/build/sync_source_lists_from_cmake.py +++ llvm/trunk/utils/gn/build/sync_source_lists_from_cmake.py @@ -8,6 +8,10 @@ Also checks that each CMakeLists.txt file below unittests/ folders that define binaries have corresponding BUILD.gn files. + +If --write is passed, tries to write modified .gn files and adds one git +commit for each cmake commit this merges. If an error is reported, the state +of HEAD is unspecified; run `git reset --hard origin/master` if this happens. """ from __future__ import print_function @@ -19,10 +23,38 @@ import sys -def sync_source_lists(): +def patch_gn_file(gn_file, add, remove): + with open(gn_file) as f: + gn_contents = f.read() + + srcs_tok = 'sources = [' + tokloc = gn_contents.find(srcs_tok) + + if tokloc == -1: raise ValueError(gn_file + ': Failed to find source list') + if gn_contents.find(srcs_tok, tokloc + 1) != -1: + raise ValueError(gn_file + ': Multiple source lists') + if gn_file.find('# NOSORT', 0, tokloc) != -1: + raise ValueError(gn_file + ': Found # NOSORT, needs manual merge') + + tokloc += len(srcs_tok) + for a in add: + gn_contents = (gn_contents[:tokloc] + ('"%s",' % a) + + gn_contents[tokloc:]) + for r in remove: + gn_contents = gn_contents.replace('"%s",' % r, '') + with open(gn_file, 'w') as f: + f.write(gn_contents) + + # Run `gn format`. + gn = os.path.join(os.path.dirname(__file__), '..', 'gn.py') + subprocess.check_call([sys.executable, gn, 'format', '-q', gn_file]) + + +def sync_source_lists(write): # Use shell=True on Windows in case git is a bat file. + git_want_shell = os.name == 'nt' gn_files = subprocess.check_output(['git', 'ls-files', '*BUILD.gn'], - shell=os.name == 'nt').splitlines() + shell=git_want_shell).splitlines() # Matches e.g. | "foo.cpp",|, captures |foo| in group 1. gn_cpp_re = re.compile(r'^\s*"([^"]+\.(?:cpp|c|h|S))",$', re.MULTILINE) @@ -35,13 +67,13 @@ def find_gitrev(touched_line, in_file): return subprocess.check_output( ['git', 'log', '--format=%h', '-1', '-S' + touched_line, in_file], - shell=os.name == 'nt').rstrip() + shell=git_want_shell).rstrip() def svnrev_from_gitrev(gitrev): git_llvm = os.path.join( os.path.dirname(__file__), '..', '..', 'git-svn', 'git-llvm') return int(subprocess.check_output( [sys.executable, git_llvm, 'svn-lookup', gitrev], - shell=os.name == 'nt').rstrip().lstrip('r')) + ).rstrip().lstrip('r')) # Collect changes to gn files, grouped by revision. for gn_file in gn_files: @@ -71,19 +103,29 @@ by_rev(sorted(gn_cpp - cmake_cpp), 'remove') # Output necessary changes grouped by revision. - for svnrev in sorted(changes_by_rev.keys()): + for svnrev in sorted(changes_by_rev): print('gn build: Merge r{0} -- https://reviews.llvm.org/rL{0}' .format(svnrev)) for gn_file, data in sorted(changes_by_rev[svnrev].items()): - print(' ' + gn_file) - add = data.get('add') - if add: - print(' add:\n' + '\n'.join(' "%s",' % a for a in add)) - remove = data.get('remove') - if remove: - print(' remove:\n ' + '\n '.join(remove)) + add = data.get('add', []) + remove = data.get('remove', []) + if write: + patch_gn_file(gn_file, add, remove) + subprocess.check_call(['git', 'add', gn_file], + shell=git_want_shell) + else: + print(' ' + gn_file) + if add: + print(' add:\n' + '\n'.join(' "%s",' % a for a in add)) + if remove: + print(' remove:\n ' + '\n '.join(remove)) + print() + if write: + subprocess.check_call( + ['git', 'commit', '-m', 'gn build: Merge r%d' % svnrev], + shell=git_want_shell) + else: print() - print() return bool(changes_by_rev) @@ -110,7 +152,7 @@ def main(): - src = sync_source_lists() + src = sync_source_lists(len(sys.argv) > 1 and sys.argv[1] == '--write') tests = sync_unittests() if src or tests: sys.exit(1)