forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrewrite_modern_objc.py
executable file
·157 lines (135 loc) · 6.12 KB
/
rewrite_modern_objc.py
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()