1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
#!/usr/bin/env python
# Copyright 2018 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Runs clang's "modern objective-c" rewriter on chrome code.
Does the same as Xcode's Edit->Convert->To Modern Objective-C Syntax.
Note that this just runs compile commands and doesn't look at build
dependencies, i.e. it doesn't make sure generated headers exist. It also
requires reclient to be disabled. Suggested workflow: Build the target you want
to convert locally with reclient to create generated headers, then disable
reclient, re-run gn, and then run this script.
Since Chrome's clang disables the rewriter, to run this you will need to
build ToT clang with `-DCLANG_ENABLE_ARCMT` and (temporarily) add the following
to your Chromium build args:
clang_base_path = /path/to/clang
clang_use_chrome_plugins = false
"""
from __future__ import print_function
import argparse
import glob
import json
import math
import os
import shlex
import subprocess
import sys
def main():
# As far as I can tell, clang's ObjC rewriter can't do in-place rewriting
# (the ARC rewriter can). libclang exposes functions for parsing the remap
# file, but doing that manually in python seems a lot easier.
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('builddir', help='build directory, e.g. out/gn')
parser.add_argument('substr', default='', nargs='?',
help='source dir part, eg chrome/browser/ui/cocoa')
args = parser.parse_args()
rewrite_dir = os.path.abspath(
os.path.join(args.builddir, 'rewrite_modern_objc'))
try:
os.mkdir(rewrite_dir)
except OSError:
pass
remap_file = os.path.join(rewrite_dir, 'remap')
try:
# Remove remap files from prior runs.
os.remove(remap_file)
except OSError:
pass
# The basic idea is to call clang's objcmt rewriter for each source file.
# The rewriter writes a "remap" file containing N times 3 lines:
# Name of an original source file, the original file's timestamp
# at rewriting time, and the name of a temp file containing the rewritten
# contents.
# The rewriter gets confused if several instances run in parallel. We could
# be fancy and have num_cpus rewrite dirs and combine their contents in the
# end, but for now just run the rewrites serially.
# First, ask ninja for the compile commands of all .m and .mm files.
compdb = subprocess.check_output(
['ninja', '-C', args.builddir, '-t', 'compdb', 'objc', 'objcxx'])
for cmd in json.loads(compdb):
objc_file = cmd['file']
if args.substr not in objc_file:
continue
clang_cmd = cmd['command']
had_error = False
if 'rewrapper' in clang_cmd:
print('need builddir with use_remoteexec not set', file=sys.stderr)
had_error = True
if 'jumbo' in clang_cmd:
print('need builddir with use_jumbo_build not set', file=sys.stderr)
had_error = True
if 'precompile.h-m' in clang_cmd:
print(
'need builddir with enable_precompiled_headers=false',
file=sys.stderr)
had_error = True
if had_error:
sys.exit(1)
# Ninja creates the directory containing the build output, but we
# don't run ninja, so we need to do that ourselves.
split_cmd = shlex.split(clang_cmd)
o_index = split_cmd.index('-o')
assert o_index != -1
try:
os.makedirs(os.path.dirname(split_cmd[o_index + 1]))
except OSError:
pass
# Add flags to tell clang to do the rewriting.
# Passing "-ccc-objcmt-migrate dir" doesn't give us control over each
# individual setting, so use the Xclang flags. The individual flags are at
# http://llvm-cs.pcc.me.uk/tools/clang/include/clang/Driver/Options.td#291
# Note that -objcmt-migrate-all maps to ObjCMT_MigrateDecls in
# http://llvm-cs.pcc.me.uk/tools/clang/lib/Frontend/CompilerInvocation.cpp#1479
# which is not quite all the options:
# http://llvm-cs.pcc.me.uk/tools/clang/include/clang/Frontend/FrontendOptions.h#248
flags = ['-Xclang', '-mt-migrate-directory', '-Xclang', rewrite_dir]
flags += ['-Xclang', '-objcmt-migrate-subscripting' ]
flags += ['-Xclang', '-objcmt-migrate-literals' ]
#flags += ['-Xclang', '-objcmt-returns-innerpointer-property'] # buggy
#flags += ['-Xclang', '-objcmt-migrate-property-dot-syntax'] # do not want
# objcmt-migrate-all is the same as the flags following it here (it does
# not include the flags listed above it).
# Probably don't want ns-nonatomic-iosonly (or atomic-property), so we
# can't use migrate-alll which includes that, and have to manually set the
# bits of migrate-all we do want.
#flags += ['-Xclang', '-objcmt-migrate-all']
#flags += ['-Xclang', '-objcmt-migrate-property'] # not sure if want
flags += ['-Xclang', '-objcmt-migrate-annotation']
flags += ['-Xclang', '-objcmt-migrate-instancetype']
flags += ['-Xclang', '-objcmt-migrate-ns-macros']
#flags += ['-Xclang', '-objcmt-migrate-protocol-conformance'] # buggy
#flags += ['-Xclang', '-objcmt-atomic-property'] # not sure if want
#flags += ['-Xclang', '-objcmt-ns-nonatomic-iosonly'] # not sure if want
# Want, but needs careful manual review, and doesn't find everything:
#flags += ['-Xclang', '-objcmt-migrate-designated-init']
clang_cmd += ' ' + ' '.join(flags)
print(objc_file)
subprocess.check_call(clang_cmd, shell=True, cwd=cmd['directory'])
if not os.path.exists(remap_file):
print('no changes')
return
# Done with rewriting. Now the read the above-described 'remap' file and
# copy modified files over the originals.
remap = open(remap_file).readlines()
for i in range(0, len(remap), 3):
infile, mtime, outfile = map(str.strip, remap[i:i+3])
if args.substr not in infile:
# Ignore rewritten header files not containing args.substr too.
continue
if math.trunc(os.path.getmtime(infile)) != int(mtime):
print('%s was modified since rewriting; exiting' % infile)
sys.exit(1)
os.rename(outfile, infile) # Copy rewritten file over.
print('all done. commit, run `git cl format`, commit again, and upload!')
if __name__ == '__main__':
main()
|