axmol/tools/localvartoauto/LocalVarToAuto.py

275 lines
7.3 KiB
Python
Executable File

#!/usr/bin/python
# LocalVarToAuto.py
# Change the class name in local variable assignment to auto
# Copyright (c) 2013 cocos2d-x.org
# Author: YuBo
# This script is used for change the class name in local variable assignment to auto.
# Right now there is some poblems you might want notice:
# To start we will assume we has a class A.
#
# 1. If you have classes like this:
# class B : public A{};
# class C : public A{};
# And you write code like this:
# A* b = a_bclass_ptr;
# C* c = new C();
# ...
# b = c;
# We will change it to:
# auto b = a_bclass_ptr;
# auto c = new C();
# It will cause a compile error because you assign pointer with type C to different pointer
# with type B. You must edit it manually.
#
# 2. If your function has default params with class A, we will change it to auto you might
# unexpected. For example:
# void f(A* a = new A()){} or void f(A a = A()){}
#
# 3. If you have define macro like this:
# #define ANOTHER_A_NAME A
# and use it like this:
# ANOTHER_A_NAME* a = new A();
# We will not convert it to auto for you.
import os.path
import re
import types
import fileinput
import cStringIO
# The cocos root entry. We will aspect it as parent dictionary, all other file or dictionary
# relative path are base on this.
COCOS_ROOT = "../../"
# The class declaration dictionaries, we will search the class declaration with .h files in it.
H_DIR = ("cocos2dx", "CocosDenshion", "extensions")
# The src files you want to edit, we will search the .cpp and .mm files in it.
CXX_DIR = ("Samples",)
# The dictionaries and files with class declaration you don't want to search, you can exclude some third party
# dictionaries in here
EXCLUDE = ("cocos2dx/platform/third_party/",)
# The extra dictionaries and files with class declaration. You can add some extra .h files in it
INCLUDE = ()
# The macroes use with declare class, like "class CC_DLL A{}"
MACROES_WITH_CLASS = ("CC_DLL",)
# The strings represent the null pointer, because you set a point to null, we will not change that
# variable to auto.
NULL_PTR = ("0", "NULL", "nullptr")
# normalize the path
COCOS_ROOT = os.path.abspath(COCOS_ROOT)
def check_file_match_rep(repString, filePath):
'''Check the file with filepath is match the repstring or not.
Return True if match, return False if not.
NOTE: it will check the EXCLUDE files and directories, if the file is in the EXCLUDE directories
or is a EXCLUDE file, it will return False.'''
#normalize the path
realFilePath = os.path.abspath(filePath)
if not os.path.isfile(realFilePath):
return False
rep = re.compile(repString)
curDir, fileName = os.path.split(realFilePath)
# check dir is exclude or not
for dir in EXCLUDE:
dir = os.path.abspath(os.path.join(COCOS_ROOT, dir))
if os.path.isdir(dir) and os.path.isdir(curDir[:len(dir)]):
if os.path.samefile(dir, curDir[:len(dir)]):
return False
if rep.match(fileName):
# check file is exclude or not
for file in EXCLUDE:
if os.path.isfile(os.path.join(COCOS_ROOT, file)):
if os.path.samefile(realFilePath, os.path.join(COCOS_ROOT, file)):
return False
return True
return False
def walk_collect_h_files(lst, dirname, names):
"collect *.h files and insert into lst"
for name in names:
if check_file_match_rep(".*\.h$", os.path.join(dirname, name)):
if type(lst) is types.ListType:
lst += [os.path.relpath(os.path.abspath(os.path.join(dirname, name)), COCOS_ROOT)]
def walk_collect_cxx_files(lst, dirname, names):
"collect *.cpp and *.mm files and insert into lst"
for name in names:
if check_file_match_rep(".*\.(?:cpp)|(?:mm)$", os.path.join(dirname, name)):
if type(lst) is types.ListType:
lst += [os.path.relpath(os.path.abspath(os.path.join(dirname, name)), COCOS_ROOT)]
def collect_class_name(filename, st):
"collect all class name appear in the file"
#generate the rep
if not hasattr(collect_class_name, "rep"):
repString = cStringIO.StringIO()
repString.write("(?:\s+|^)class\s+")
for word in MACROES_WITH_CLASS:
repString.write(word + "\s+")
repString.write("(?P<cls_name>\w+)")
collect_class_name.rep = re.compile(repString.getvalue())
repString.close()
f = open(os.path.join(COCOS_ROOT, filename))
try:
for line in f:
res = collect_class_name.rep.match(line)
if res:
if type(st) == type(set()):
st.add(res.group("cls_name"))
finally:
f.close()
def change_local_classvarname_to_auto(filename, rep, change):
"change all local class variable name to auto"
f = open(filename)
content = None
changed = False
# read the file, change it, and save it to content
try:
content = cStringIO.StringIO()
changed = False
for line in f:
i = 0
#start to replace
while True:
result = rep.match(line, i)
# founded
if result:
changed = True
#find the matched string where to start
startIndex = line.index(result.group(0))
#replace the change part
line = line.replace(result.group(change), "auto ", startIndex)
i += 1
else:
break
#write the result to content
content.write(line)
finally:
f.close()
if changed:
f = open(filename, "w")
f.write(content.getvalue())
f.close()
content.close()
def main():
print ".......VARIABLES......."
print "COCOS_ROOT:",
print COCOS_ROOT
print "H_DIR:",
print H_DIR
print "CXX_DIR:",
print CXX_DIR
print "EXCLUDE:",
print EXCLUDE
print "INCLUDE:",
print INCLUDE
print ".......VARIABLES END......"
# save the .h file for search
hfiles = []
# save the .cpp and .mm file for search
cxxfiles = []
print "search .h files..."
for dir in H_DIR:
os.path.walk(os.path.join(COCOS_ROOT, dir), walk_collect_h_files, hfiles)
for dir in INCLUDE:
if os.path.isdir(os.path.join(COCOS_ROOT, dir)):
os.path.walk(os.path.join(COCOS_ROOT, dir), walk_collect_h_files, hfiles)
else:
hfiles += [dir]
print "search end"
print "search .cxx files..."
for dir in CXX_DIR:
os.path.walk(os.path.join(COCOS_ROOT, dir), walk_collect_cxx_files, cxxfiles)
print "search end"
print "search class declarations"
# the class set, ignore the namespace
classes = set()
for file in hfiles:
collect_class_name(file, classes)
print "search end"
# generate example rep:
# (\W+|^)(?P<keyWord>\S*(?:Class1|Class2|class3)\s*(?:\*+|\s+)\s*)\w+\s*=(?!\s*(?:0|NULL|nullptr)\s*\W)
# examples:
# Class1* c = new Class1() ; //match
# Class1* c; //not match
# Class1* c = nullptr; //not match
# Class1* c = nullptrabc; //match
# Class1 c; //not match
# Class1 c = Class1(); //match
def gen_rep(keyWord):
s = cStringIO.StringIO()
s.write("(?:\W+|^)(?P<")
s.write(keyWord)
s.write(">\S*(?:")
# add classes want to match
first = True
for cls in classes:
if first:
first = False
else:
s.write("|")
s.write(cls)
s.write(")\s*(?:\*+|\s+)\s*)\w+\s*=(?!\s*(?:")
# let nullptr assignment not to convert
first = True
for nullptr in NULL_PTR:
if first:
first = False
else:
s.write("|")
s.write(nullptr)
s.write(")\s*\W)")
result = s.getvalue()
s.close()
print "generated regular expression is:"
print result
return re.compile(result)
repWord = "change"
rep = gen_rep(repWord)
print "scan and edit the .cxx files..."
# scan the cxx files
for file in cxxfiles:
change_local_classvarname_to_auto(os.path.join(COCOS_ROOT, file), rep, repWord)
print "success!"
if __name__ == "__main__":
main()