scripts/compile_thrift.py (151 lines of code) (raw):
#!/usr/bin/env python2
# The MIT License (MIT)
#
# Copyright (c) 2015 Microsoft Corporation
#
# -=- Robust Distributed System Nucleus (rDSN) -=-
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import os
import sys
import platform
import re
'''
the default thrift generator
'''
thrift_description = [
{
"name": "dsn.layer2",
"path": "idl",
"include_fix": {
"_types.h": {
"add": ["\"runtime/rpc/serialization.h\""],
"remove": ["dsn_types.h"]
},
"_types.cpp": {
"add": ["\"common/serialization_helper/dsn.layer2_types.h\""],
"remove": ["dsn.layer2_types.h"]
}
},
"file_move": {
"_types.h": "src/common/serialization_helper",
"_types.cpp": "src/runtime"
}
},
]
class CompileError(Exception):
""" Raised when dealing with thrift idl have errors"""
def __init__(self, msg):
self.msg = msg
def __str__(self):
return self.msg
def fix_include_file(filename, fix_commands):
tmp_result = filename + ".swapfile"
from_fd, to_fd = open(filename, "r"), open(tmp_result, "w")
add_ok = not "add" in fix_commands
for line in from_fd:
include_statement = False
if len(line.strip()) > 0:
stripped_line = line.strip()
if stripped_line[0] == "#" and "include" in stripped_line:
include_statement = True
if include_statement == True and add_ok == False:
add_includes = "\n".join(["#include %s" % (s)
for s in fix_commands["add"]])
to_fd.write(add_includes + "\n")
add_ok = True
if include_statement == True and ("remove" in fix_commands):
if len(list(filter(lambda x: x in line, fix_commands["remove"]))) == 0:
to_fd.write(line)
else:
to_fd.write(line)
from_fd.close()
to_fd.close()
os.remove(filename)
os.rename(tmp_result, filename)
def fix_include(thrift_name, include_fix_dict):
# current dir is thrift file dir
os.chdir("output")
for pair in include_fix_dict.items():
filename = thrift_name + pair[0]
fix_include_file(filename, pair[1])
os.chdir("..")
def compile_thrift_file(thrift_info):
thrift_name = thrift_info["name"]
print("\n>>> compiling thrift file %s.thrift ..." % (thrift_name))
if "path" not in thrift_info:
raise CompileError("can't find thrift file")
# ensure <name>.thrift exists
os.chdir(root_dir + "/" + thrift_info["path"])
if os.path.isfile(thrift_name+".thrift") == False:
raise CompileError("can't find thrift file")
# create tmp directory: <thrift_info["path"]>/output
os.system("rm -rf output")
os.system("mkdir output")
print("mkdir {}/output".format(os.getcwd()))
# generate files
cmd = "{} -gen cpp:moveable_types -out output {}.thrift".format(
thrift_exe, thrift_name)
os.system(cmd)
print(cmd)
# TODO(wutao1): code format files
# os.system("clang-format-3.9 -i output/*")
if "include_fix" in thrift_info:
fix_include(thrift_name, thrift_info["include_fix"])
if "hook" in thrift_info:
os.chdir("output")
for hook_func, args in thrift_info["hook"]:
hook_func(args)
os.chdir("..")
if "file_move" in thrift_info:
for pair in thrift_info["file_move"].items():
dest_path = root_dir + "/" + pair[1]
for postfix in pair[0].split():
src_path = "output/%s%s" % (thrift_name, postfix)
cmd = "mv %s %s" % (src_path, dest_path)
os.system(cmd)
print(cmd)
os.system("rm -rf output")
print("rm -rf {}/output".format(os.getcwd()))
os.chdir(root_dir)
# special hooks for thrift, all these are executed in the output dir
def constructor_hook(args):
generated_fname = args[0]
class_name = args[1]
add_code = args[2]
target_fname = generated_fname + ".swapfile"
src_fd, dst_fd = open(generated_fname, "r"), open(target_fname, "w")
in_class = 0
for line in src_fd:
if in_class == 1:
if "public:" in line:
line = line + add_code + "\n"
elif "bool operator <" in line:
line = ""
# this may not be right
elif line.startswith("};"):
in_class = 2
elif in_class == 0 and line.startswith("class " + class_name + " {"):
in_class = 1
dst_fd.write(line)
src_fd.close()
dst_fd.close()
os.remove(generated_fname)
os.rename(target_fname, generated_fname)
def replace_hook(args):
generated_fname = args[0]
replace_map = args[1]
target_fname = generated_fname + ".swapfile"
src_fd, dst_fd = open(generated_fname, "r"), open(target_fname, "w")
for line in src_fd:
for key, value in replace_map.items():
line = re.sub(key, value, line)
dst_fd.write(line)
src_fd.close()
dst_fd.close()
os.remove(generated_fname)
os.rename(target_fname, generated_fname)
def add_hook(name, path, func, args):
for i in thrift_description:
if name == i["name"] and path == i["path"]:
if "hook" not in i:
i["hook"] = [(func, args)]
else:
i["hook"].append((func, args))
if __name__ == "__main__":
root_dir = os.getcwd()
thrift_exe = root_dir + "/thirdparty/output/bin/thrift"
print("thrift_exe = " + thrift_exe)
print("root_dir = " + root_dir)
if not os.path.isfile(thrift_exe):
print("Error: can't find compiler %s\nPlease build thrift in thirdparty/" % thrift_exe)
sys.exit()
ctor_kv_pair = " kv_pair(const std::string& _key, const std::string& _val): key(_key), value(_val) {\n }"
ctor_configuration_proposal_action = " configuration_proposal_action(::dsn::rpc_address t, ::dsn::rpc_address n, config_type::type tp): target(t), node(n), type(tp) {}"
add_hook("dsn.layer2", "idl", replace_hook, ["dsn.layer2_types.h", {
r"dsn\.layer2_TYPES_H": 'dsn_layer2_TYPES_H'}])
for i in thrift_description:
compile_thrift_file(i)