@@ -0,0 +1,6 @@ | |||||
inc_path := $(shell pwd)/metadef/inc/external/ | |||||
out_path := $(shell pwd)/out/graph/lib64/stub/ | |||||
stub_path := $(shell pwd)/metadef/graph/stub/ | |||||
mkdir_stub := $(shell mkdir -p $(out_path)) | |||||
graph_local_stub := $(shell $(HI_PYTHON) $(stub_path)/gen_stubapi.py $(inc_path) $(out_path)) |
@@ -0,0 +1,578 @@ | |||||
import os | |||||
import re | |||||
import sys | |||||
import logging | |||||
logging.basicConfig(stream=sys.stdout, format='[%(asctime)s] [%(lineno)s] %(levelname)s: %(message)s', | |||||
level=logging.INFO) | |||||
""" | |||||
this attr is used for symbol table visible | |||||
""" | |||||
GE_ATTR = 'GE_FUNC_DEV_VISIBILITY GE_FUNC_HOST_VISIBILITY' | |||||
""" | |||||
generate stub func body by return type | |||||
""" | |||||
RETURN_STATEMENTS = { | |||||
'graphStatus': ' std::cout << "[ERROR]: stub library libgraph or libge_compiler cannot be used for execution, please check your "\n ' | |||||
' << "environment variables and compilation options to make sure you use the correct library."\n' | |||||
' << std::endl;\n' | |||||
' return ACL_ERROR_COMPILING_STUB_MODE;', | |||||
'Status': ' return SUCCESS;', | |||||
'Graph': ' return Graph();', | |||||
'Graph&': ' return *this;', | |||||
'Format': ' return Format();', | |||||
'Format&': ' return *this;', | |||||
'Shape': ' return Shape();', | |||||
'Shape&': ' return *this;', | |||||
'TensorDesc': ' return TensorDesc();', | |||||
'TensorDesc&': ' return *this;', | |||||
'Tensor': ' return Tensor();', | |||||
'Tensor&': ' return *this;', | |||||
'Operator': ' return Operator();', | |||||
'Operator&': ' return *this;', | |||||
'Ptr': ' return nullptr;', | |||||
'std::string': ' return "";', | |||||
'std::string&': ' return "";', | |||||
'string': ' return "";', | |||||
'int': ' return 0;', | |||||
'DataType': ' return DT_FLOAT;', | |||||
'InferenceContextPtr': ' return nullptr;', | |||||
'SubgraphBuilder': ' return nullptr;', | |||||
'OperatorImplPtr': ' return nullptr;', | |||||
'OutHandler': ' return nullptr;', | |||||
'std::vector<std::string>': ' return {};', | |||||
'std::vector<int64_t>': ' return {};', | |||||
'std::map': ' return {};', | |||||
'uint32_t': ' return 0;', | |||||
'int64_t': ' return 0;', | |||||
'uint64_t': ' return 0;', | |||||
'size_t': ' return 0;', | |||||
'float': ' return 0.0f;', | |||||
'bool': ' return false;', | |||||
} | |||||
""" | |||||
max code len per line in hua_wei software programming specifications | |||||
""" | |||||
max_code_len_per_line = 100 | |||||
""" | |||||
white_list_for_debug, include_dir_key_words is to | |||||
determines which header files to generate cc files from | |||||
when DEBUG on | |||||
""" | |||||
white_list_for_debug = ["attr_value.h", "operator.h", "tensor.h", "graph.h", "operator_factory.h", "inference_context.h", | |||||
"ge_ir_build.h", "ge_api.h", "ascend_string.h", "gnode.h"] | |||||
include_dir_key_words = ["ge", "graph"] | |||||
DEBUG = True | |||||
def need_generate_func(func_line): | |||||
""" | |||||
:param func_line: | |||||
:return: | |||||
""" | |||||
if func_line.strip().endswith("default") or func_line.strip().endswith("delete") \ | |||||
or func_line.strip().startswith("typedef") or func_line.strip().startswith("using"): | |||||
return False | |||||
return True | |||||
def file_endswith_white_list_suffix(file): | |||||
""" | |||||
:param file: | |||||
:return: | |||||
""" | |||||
if DEBUG: | |||||
for suffix in white_list_for_debug: | |||||
if file.endswith(suffix): | |||||
return True | |||||
return False | |||||
else: | |||||
return True | |||||
""" | |||||
belows are patterns used for analyse .h file | |||||
""" | |||||
# pattern function | |||||
pattern_func = re.compile(r"""(^[\s]*) #leading with space,we will find and delete after | |||||
([a-zA-Z~_] # void int likely | |||||
.* | |||||
[)] #we find ) | |||||
(?!.*{) # we do not want the case int abc() const | |||||
.*) | |||||
(;.*) #we want to find ; and after for we will replace these later | |||||
\n$ | |||||
""", re.VERBOSE | re.MULTILINE | re.DOTALL) | |||||
# pattern comment | |||||
pattern_comment = re.compile(r'^\s*//') | |||||
pattern_comment_2_start = re.compile(r'^\s*/[*]') | |||||
pattern_comment_2_end = re.compile(r'[*]/\s*$') | |||||
# pattern define | |||||
pattern_define = re.compile(r'^\s*#define') | |||||
pattern_define_return = re.compile(r'\\\s*$') | |||||
# blank line | |||||
pattern_blank_line = re.compile(r'^\s*$') | |||||
# virtual,explicit,friend,static | |||||
pattern_keyword = re.compile(r'(virtual\s+|explicit\s+|friend\s+|static\s+)') | |||||
# lead space | |||||
pattern_leading_space = re.compile(r'(^[\s]*)[a-zA-Z~_]') | |||||
# functions will have patterns such as func ( or func( | |||||
# but operator is an exception; the class name is preceded by an operator, and the above mode does not exist | |||||
# format like :"operator = ()" | |||||
pattern_func_name = re.compile(r'([a-zA-Z0-9~_\-]+\s*|operator?.*)[(]') | |||||
# template | |||||
pattern_template = re.compile(r'^\s*template') | |||||
pattern_template_end = re.compile(r'>\s*$') | |||||
# namespace | |||||
pattern_namespace = re.compile(r'namespace.*{') | |||||
# class : which can handle classA a and {not on the same line, but if found ';' after class,then don't deal with | |||||
pattern_class = re.compile(r'^[\s]*(class|struct)\s+(%s\s+)?([a-zA-Z0-9_\-]+<?)(?!.*;)' % GE_ATTR) | |||||
# {} | |||||
pattern_start = re.compile('{') | |||||
pattern_end = re.compile('}') | |||||
line_index = 0 | |||||
class H2CC(object): | |||||
def __init__(self, input_file, output_file, shared_includes_content): | |||||
""" | |||||
:param input_file: | |||||
:param output_file: | |||||
:param shared_includes_content: | |||||
""" | |||||
self.input_file = input_file | |||||
self.output_file = output_file | |||||
self.shared_includes_content = shared_includes_content | |||||
self.line_index = 0 | |||||
self.input_fd = open(self.input_file, 'r') | |||||
self.input_content = self.input_fd.readlines() | |||||
self.output_fd = open(self.output_file, 'w') | |||||
# The state may be normal_now(in the middle of {}),class_now,namespace_now | |||||
self.stack = [] | |||||
self.stack_class = [] | |||||
self.stack_template = [] | |||||
# record funcs generated by h2cc func | |||||
self.func_list_exist = [] | |||||
def __del__(self): | |||||
self.input_fd.close() | |||||
self.output_fd.close() | |||||
del self.stack | |||||
del self.stack_class | |||||
del self.stack_template | |||||
del self.func_list_exist | |||||
def just_skip(self): | |||||
# skip blank line or comment | |||||
if pattern_blank_line.search(self.input_content[self.line_index]) or pattern_comment.search( | |||||
self.input_content[self.line_index]): # /n or comment using // | |||||
self.line_index += 1 | |||||
if pattern_comment_2_start.search(self.input_content[self.line_index]): # comment using /* | |||||
while not pattern_comment_2_end.search(self.input_content[self.line_index]): # */ | |||||
self.line_index += 1 | |||||
self.line_index += 1 | |||||
# skip define | |||||
if pattern_define.search(self.input_content[self.line_index]): | |||||
while pattern_blank_line.search(self.input_content[self.line_index]) or pattern_define_return.search( | |||||
self.input_content[self.line_index]): | |||||
self.line_index += 1 | |||||
self.line_index += 1 | |||||
def write_inc_content(self): | |||||
for shared_include_content in self.shared_includes_content: | |||||
self.output_fd.write(shared_include_content) | |||||
def h2cc(self): | |||||
""" | |||||
:return: | |||||
""" | |||||
logging.info("start generate cc_file[%s] from h_file[%s]", self.output_file, self.input_file) | |||||
global pattern_comment | |||||
global pattern_comment_2_start | |||||
global pattern_comment_2_end | |||||
global pattern_blank_line | |||||
global pattern_func | |||||
global pattern_keyword | |||||
global pattern_leading_space | |||||
global pattern_func_name | |||||
global pattern_template | |||||
global pattern_template_end | |||||
global pattern_namespace | |||||
global pattern_class | |||||
global pattern_start | |||||
global pattern_end | |||||
global line_index | |||||
# write inc content | |||||
self.write_inc_content() | |||||
# core processing cycle, process the input .h file by line | |||||
while self.line_index < len(self.input_content): | |||||
# handle comment and blank line | |||||
self.just_skip() | |||||
# match namespace | |||||
self.handle_namespace() | |||||
# match template | |||||
template_string = self.handle_template() | |||||
# match class | |||||
line = self.input_content[self.line_index] | |||||
match_class = pattern_class.search(line) | |||||
match_start = pattern_start.search(line) | |||||
handle_class_result = self.handle_class(template_string, line, match_start, match_class) | |||||
if handle_class_result == "continue": | |||||
continue | |||||
# match "}" | |||||
handle_stack_result = self.handle_stack(match_start) | |||||
if handle_stack_result == "continue": | |||||
continue | |||||
# handle func | |||||
handle_func1_result, line, start_i = self.handle_func1(line) | |||||
if handle_func1_result == "continue": | |||||
continue | |||||
# here means func is found | |||||
# delete key word | |||||
line = pattern_keyword.sub('', line) | |||||
logging.info("line[%s]", line) | |||||
# Class member function | |||||
# if friend we will not add class name | |||||
friend_match = re.search('friend ', line) | |||||
if len(self.stack_class) > 0 and not friend_match: | |||||
line, func_name = self.handle_class_member_func(line, template_string) | |||||
# Normal functions | |||||
else: | |||||
line, func_name = self.handle_normal_func(line, template_string) | |||||
need_generate = need_generate_func(line) | |||||
# func body | |||||
line += self.implement_function(line) | |||||
# comment | |||||
line = self.gen_comment(start_i) + line | |||||
# write to out file | |||||
self.write_func_content(line, func_name, need_generate) | |||||
# next loop | |||||
self.line_index += 1 | |||||
logging.info('Added %s functions', len(self.func_list_exist)) | |||||
logging.info('Successfully converted,please see ' + self.output_file) | |||||
def handle_func1(self, line): | |||||
""" | |||||
:param line: | |||||
:return: | |||||
""" | |||||
find1 = re.search('[(]', line) | |||||
if not find1: | |||||
self.line_index += 1 | |||||
return "continue", line, None | |||||
find2 = re.search('[)]', line) | |||||
start_i = self.line_index | |||||
space_match = pattern_leading_space.search(line) | |||||
# deal with | |||||
# int abc(int a, | |||||
# int b) | |||||
if find1 and (not find2): | |||||
self.line_index += 1 | |||||
line2 = self.input_content[self.line_index] | |||||
if space_match: | |||||
line2 = re.sub('^' + space_match.group(1), '', line2) | |||||
line += line2 | |||||
while self.line_index < len(self.input_content) and (not re.search('[)]', line2)): | |||||
self.line_index += 1 | |||||
line2 = self.input_content[self.line_index] | |||||
line2 = re.sub('^' + space_match.group(1), '', line2) | |||||
line += line2 | |||||
match_start = pattern_start.search(self.input_content[self.line_index]) | |||||
match_end = pattern_end.search(self.input_content[self.line_index]) | |||||
if match_start: # like ) { or ) {} int the last line | |||||
if not match_end: | |||||
self.stack.append('normal_now') | |||||
ii = start_i | |||||
while ii <= self.line_index: | |||||
ii += 1 | |||||
self.line_index += 1 | |||||
return "continue", line, start_i | |||||
logging.info("line[%s]", line) | |||||
# ' int abc();'->'int abc()' | |||||
(line, match) = pattern_func.subn(r'\2\n', line) | |||||
logging.info("line[%s]", line) | |||||
# deal with case: | |||||
# 'int \n abc(int a, int b)' | |||||
if re.search(r'^\s*(inline)?\s*[a-zA-Z0-9_]+\s*$', self.input_content[start_i - 1]): | |||||
line = self.input_content[start_i - 1] + line | |||||
line = line.lstrip() | |||||
if not match: | |||||
self.line_index += 1 | |||||
return "continue", line, start_i | |||||
return "pass", line, start_i | |||||
def handle_stack(self, match_start): | |||||
""" | |||||
:param match_start: | |||||
:return: | |||||
""" | |||||
line = self.input_content[self.line_index] | |||||
match_end = pattern_end.search(line) | |||||
if match_start: | |||||
self.stack.append('normal_now') | |||||
if match_end: | |||||
top_status = self.stack.pop() | |||||
if top_status == 'namespace_now': | |||||
self.output_fd.write(line + '\n') | |||||
elif top_status == 'class_now': | |||||
self.stack_class.pop() | |||||
self.stack_template.pop() | |||||
if match_start or match_end: | |||||
self.line_index += 1 | |||||
return "continue" | |||||
if len(self.stack) > 0 and self.stack[-1] == 'normal_now': | |||||
self.line_index += 1 | |||||
return "continue" | |||||
return "pass" | |||||
def handle_class(self, template_string, line, match_start, match_class): | |||||
""" | |||||
:param template_string: | |||||
:param line: | |||||
:param match_start: | |||||
:param match_class: | |||||
:return: | |||||
""" | |||||
if match_class: # we face a class | |||||
self.stack_template.append(template_string) | |||||
self.stack.append('class_now') | |||||
class_name = match_class.group(3) | |||||
# class template specializations: class A<u,Node<u> > | |||||
if '<' in class_name: | |||||
k = line.index('<') | |||||
fit = 1 | |||||
for ii in range(k + 1, len(line)): | |||||
if line[ii] == '<': | |||||
fit += 1 | |||||
if line[ii] == '>': | |||||
fit -= 1 | |||||
if fit == 0: | |||||
break | |||||
class_name += line[k + 1:ii + 1] | |||||
logging.info('class_name[%s]', class_name) | |||||
self.stack_class.append(class_name) | |||||
while not match_start: | |||||
self.line_index += 1 | |||||
line = self.input_content[self.line_index] | |||||
match_start = pattern_start.search(line) | |||||
self.line_index += 1 | |||||
return "continue" | |||||
return "pass" | |||||
def handle_template(self): | |||||
line = self.input_content[self.line_index] | |||||
match_template = pattern_template.search(line) | |||||
template_string = '' | |||||
if match_template: | |||||
match_template_end = pattern_template_end.search(line) | |||||
template_string = line | |||||
while not match_template_end: | |||||
self.line_index += 1 | |||||
line = self.input_content[self.line_index] | |||||
template_string += line | |||||
match_template_end = pattern_template_end.search(line) | |||||
self.line_index += 1 | |||||
return template_string | |||||
def handle_namespace(self): | |||||
line = self.input_content[self.line_index] | |||||
match_namespace = pattern_namespace.search(line) | |||||
if match_namespace: # we face namespace | |||||
self.output_fd.write(line + '\n') | |||||
self.stack.append('namespace_now') | |||||
self.line_index += 1 | |||||
def handle_normal_func(self, line, template_string): | |||||
template_line = '' | |||||
self.stack_template.append(template_string) | |||||
if self.stack_template[-1] != '': | |||||
template_line = re.sub(r'\s*template', 'template', self.stack_template[-1]) | |||||
# change '< class T = a, class U = A(3)>' to '<class T, class U>' | |||||
template_line = re.sub(r'\s*=.*>(\s*)$', r'>\1', template_line) | |||||
template_line = re.sub(r'\s*=.*,', ',', template_line) | |||||
template_line = re.sub(r'\s*=.*', '', template_line) | |||||
line = re.sub(r'\s*=.*,', ',', line) | |||||
line = re.sub(r'\s*=.*\)', ')', line) | |||||
line = template_line + line | |||||
self.stack_template.pop() | |||||
func_name = re.search(r'^.*\)', line, re.MULTILINE | re.DOTALL).group() | |||||
logging.info("line[%s]", line) | |||||
logging.info("func_name[%s]", func_name) | |||||
return line, func_name | |||||
def handle_class_member_func(self, line, template_string): | |||||
template_line = '' | |||||
x = '' | |||||
if template_string != '': | |||||
template_string = re.sub(r'\s*template', 'template', template_string) | |||||
template_string = re.sub(r'\s*=.*>(\s*)$', r'>\1', template_string) | |||||
template_string = re.sub(r'\s*=.*,', ',', template_string) | |||||
template_string = re.sub(r'\s*=.*', '', template_string) | |||||
if self.stack_template[-1] != '': | |||||
if not (re.search(r'<\s*>', stack_template[-1])): | |||||
template_line = re.sub(r'^\s*template', 'template', stack_template[-1]) | |||||
if not (re.search(r'<.*>', self.stack_class[-1])): | |||||
# for x we get like template<class T, typename U> -> <T,U> | |||||
x = re.sub(r'template\s*<', '<', template_line) # remove template -> <class T, typename U> | |||||
x = re.sub(r'\n', '', x) | |||||
x = re.sub(r'\s*=.*,', ',', x) | |||||
x = re.sub(r'\s*=.*\>', '>', x) | |||||
x = x.rstrip() # remove \n | |||||
x = re.sub(r'(class|typename)\s+|(<class>|<typename>\s*class)', '', | |||||
x) # remove class,typename -> <T, U> | |||||
x = re.sub(r'<\s+', '<', x) | |||||
x = re.sub(r'\s+>', '>', x) | |||||
x = re.sub(r'\s+,', ',', x) | |||||
x = re.sub(r',\s+', ', ', x) | |||||
line = re.sub(r'\s*=\s+0', '', line) | |||||
line = re.sub(r'\s*=\s+.*,', ',', line) | |||||
line = re.sub(r'\s*=\s+.*\)', ')', line) | |||||
logging.info("x[%s]\nline[%s]", x, line) | |||||
# if the function is long, void ABC::foo() | |||||
# breaks into two lines void ABC::\n foo() | |||||
temp_line = pattern_func_name.sub(self.stack_class[-1] + x + '::' + r'\1(', line, count=1) | |||||
if len(temp_line) > max_code_len_per_line: | |||||
line = pattern_func_name.sub(self.stack_class[-1] + x + '::\n' + r'\1(', line, count=1) | |||||
else: | |||||
line = temp_line | |||||
logging.info("line[%s]", line) | |||||
# add template as the above if there is one | |||||
template_line = re.sub(r'\s*=.*>(\s*)$', r'>\1', template_line) | |||||
template_line = re.sub(r'\s*=.*,', ',', template_line) | |||||
template_line = re.sub(r'\s*=.*', '', template_line) | |||||
line = template_line + template_string + line | |||||
func_name = re.search(r'^.*\)', line, re.MULTILINE | re.DOTALL).group() | |||||
logging.info("line[%s]", line) | |||||
logging.info("func_name[%s]", func_name) | |||||
return line, func_name | |||||
def write_func_content(self, content, func_name, need_generate): | |||||
if not (func_name in self.func_list_exist) and need_generate: | |||||
self.output_fd.write(content) | |||||
self.func_list_exist.append(func_name) | |||||
logging.info('add func:[%s]', func_name) | |||||
def gen_comment(self, start_i): | |||||
comment_line = '' | |||||
# Function comments are on top of function declarations, copy them over | |||||
k = start_i - 1 # one line before this func start | |||||
if pattern_template.search(self.input_content[k]): | |||||
k -= 1 | |||||
if pattern_comment_2_end.search(self.input_content[k]): | |||||
comment_line = self.input_content[k].lstrip() | |||||
while not pattern_comment_2_start.search(self.input_content[k]): | |||||
k -= 1 | |||||
comment_line = self.input_content[k].lstrip() + comment_line | |||||
else: | |||||
for j in range(k, 0, -1): | |||||
c_line = self.input_content[j] | |||||
if pattern_comment.search(c_line): | |||||
c_line = re.sub(r'\s*//', '//', c_line) | |||||
comment_line = c_line + comment_line | |||||
else: | |||||
break | |||||
return comment_line | |||||
@staticmethod | |||||
def implement_function(func): | |||||
function_def = '' | |||||
function_def += '{\n' | |||||
all_items = func.split() | |||||
start = 0 | |||||
return_type = all_items[start] | |||||
if return_type == "const": | |||||
start += 1 | |||||
return_type = all_items[start] | |||||
if return_type.startswith(('std::map', 'std::set', 'std::vector')): | |||||
return_type = "std::map" | |||||
if return_type.endswith('*') or (len(all_items) > start + 1 and all_items[start + 1].startswith('*')): | |||||
return_type = "Ptr" | |||||
if len(all_items) > start + 1 and all_items[start + 1].startswith('&'): | |||||
return_type += "&" | |||||
if RETURN_STATEMENTS.__contains__(return_type): | |||||
function_def += RETURN_STATEMENTS[return_type] | |||||
else: | |||||
logging.warning("Unhandled return type[%s]", return_type) | |||||
function_def += '\n' | |||||
function_def += '}\n' | |||||
function_def += '\n' | |||||
return function_def | |||||
def collect_header_files(path): | |||||
""" | |||||
:param path: | |||||
:return: | |||||
""" | |||||
header_files = [] | |||||
shared_includes_content = [] | |||||
for root, dirs, files in os.walk(path): | |||||
files.sort() | |||||
for file in files: | |||||
if file.find("git") >= 0: | |||||
continue | |||||
if not file.endswith('.h'): | |||||
continue | |||||
file_path = os.path.join(root, file) | |||||
file_path = file_path.replace('\\', '/') | |||||
header_files.append(file_path) | |||||
include_str = '#include "{}"\n'.format(file_path[path.rindex('/') + 1:]) | |||||
shared_includes_content.append(include_str) | |||||
# for acl error code | |||||
shared_includes_content.append('#include <iostream>\n') | |||||
shared_includes_content.append('const int ACL_ERROR_COMPILING_STUB_MODE = 100039;\n') | |||||
return header_files, shared_includes_content | |||||
def generate_stub_file(inc_dir, out_cc_dir): | |||||
""" | |||||
:param inc_dir: | |||||
:param out_cc_dir: | |||||
:return: | |||||
""" | |||||
target_header_files, shared_includes_content = collect_header_files(inc_dir) | |||||
for header_file in target_header_files: | |||||
if not file_endswith_white_list_suffix(header_file): | |||||
continue | |||||
cc_file = re.sub('.h*$', '.cc', header_file) | |||||
h_2_cc = H2CC(header_file, out_cc_dir + cc_file[cc_file.rindex('/') + 1:], shared_includes_content) | |||||
h_2_cc.h2cc() | |||||
def gen_code(inc_dir, out_cc_dir): | |||||
""" | |||||
:param inc_dir: | |||||
:param out_cc_dir: | |||||
:return: | |||||
""" | |||||
if not inc_dir.endswith('/'): | |||||
inc_dir += '/' | |||||
if not out_cc_dir.endswith('/'): | |||||
out_cc_dir += '/' | |||||
for include_dir_key_word in include_dir_key_words: | |||||
generate_stub_file(inc_dir + include_dir_key_word, out_cc_dir) | |||||
if __name__ == '__main__': | |||||
inc_dir = sys.argv[1] | |||||
out_cc_dir = sys.argv[2] | |||||
gen_code(inc_dir, out_cc_dir) |
@@ -0,0 +1,6 @@ | |||||
inc_path := $(shell pwd)/inc/external/ | |||||
out_path := $(shell pwd)/out/ge/lib64/stub/ | |||||
stub_path := $(shell pwd)/framework/domi/stub/ | |||||
mkdir_stub := $(shell mkdir -p $(out_path)) | |||||
local_stub := $(shell $(HI_PYTHON) $(stub_path)/gen_stubapi.py $(inc_path) $(out_path)) |
@@ -0,0 +1,4 @@ | |||||
################################################################################### | |||||
the directory (stub) saves the stub file | |||||
gen_stubapi.py is using for retrieving API and generating stub functions | |||||
################################################################################### |
@@ -0,0 +1,44 @@ | |||||
# "stub" usage: | |||||
## Description | |||||
- File libge_compiler.so ,libgraph.so are used in IR build application interface. | |||||
# Attention | |||||
- Don't link other library except libge_compiler.so ,libgraph.so, as they may be changed in the future. | |||||
# Usage | |||||
## Compile: compile the application invoking the IR build API. | |||||
Makefile: | |||||
''' | |||||
ATC_INCLUDE_DIR := $(ASCEND_PATH)/atc/include | |||||
OPP_INCLUDE_DIR := $(ASCEND_PATH)/opp/op_proto/built-in/inc | |||||
LOCAL_MODULE_NAME := ir_build | |||||
CC := g++ | |||||
CFLAGS := -std=c++11 -g -Wall | |||||
SRCS := $(wildcard $(LOCAL_DIR)/main.cpp) | |||||
INCLUDES := -I $(ASCEND_OPP_PATH)/op_proto/built-in/inc \ | |||||
-I $(ATC_INCLUDE_DIR)/graph \ | |||||
-I $(ATC_INCLUDE_DIR)/ge \ | |||||
LIBS := -L ${ASCEND_PATH}/atc/lib64/stub \ | |||||
-lgraph \ | |||||
-lge_compiler | |||||
ir_build: | |||||
mkdir -p out | |||||
$(CC) $(SRCS) $(INCLUDES) $(LIBS) $(CFLAGS) -o ./out/$(LOCAL_MODULE_NAME) | |||||
clean: | |||||
rm -rf out | |||||
''' | |||||
make | |||||
## Run the application after set the LD_LIBRARY_PATH to include the real path of the library which locates in the directory of atc/lib64 | |||||
export LD_LIBRARY_PATH= $(ASCEND_PATH)/atc/lib64 | |||||
- ./ ir_build |
@@ -0,0 +1,578 @@ | |||||
import os | |||||
import re | |||||
import sys | |||||
import logging | |||||
logging.basicConfig(stream=sys.stdout, format='[%(asctime)s] [%(lineno)s] %(levelname)s: %(message)s', | |||||
level=logging.INFO) | |||||
""" | |||||
this attr is used for symbol table visible | |||||
""" | |||||
GE_ATTR = 'GE_FUNC_DEV_VISIBILITY GE_FUNC_HOST_VISIBILITY' | |||||
""" | |||||
generate stub func body by return type | |||||
""" | |||||
RETURN_STATEMENTS = { | |||||
'graphStatus': ' std::cout << "[ERROR]: stub library libgraph or libge_compiler cannot be used for execution, please check your "\n ' | |||||
' << "environment variables and compilation options to make sure you use the correct library."\n' | |||||
' << std::endl;\n' | |||||
' return ACL_ERROR_COMPILING_STUB_MODE;', | |||||
'Status': ' return SUCCESS;', | |||||
'Graph': ' return Graph();', | |||||
'Graph&': ' return *this;', | |||||
'Format': ' return Format();', | |||||
'Format&': ' return *this;', | |||||
'Shape': ' return Shape();', | |||||
'Shape&': ' return *this;', | |||||
'TensorDesc': ' return TensorDesc();', | |||||
'TensorDesc&': ' return *this;', | |||||
'Tensor': ' return Tensor();', | |||||
'Tensor&': ' return *this;', | |||||
'Operator': ' return Operator();', | |||||
'Operator&': ' return *this;', | |||||
'Ptr': ' return nullptr;', | |||||
'std::string': ' return "";', | |||||
'std::string&': ' return "";', | |||||
'string': ' return "";', | |||||
'int': ' return 0;', | |||||
'DataType': ' return DT_FLOAT;', | |||||
'InferenceContextPtr': ' return nullptr;', | |||||
'SubgraphBuilder': ' return nullptr;', | |||||
'OperatorImplPtr': ' return nullptr;', | |||||
'OutHandler': ' return nullptr;', | |||||
'std::vector<std::string>': ' return {};', | |||||
'std::vector<int64_t>': ' return {};', | |||||
'std::map': ' return {};', | |||||
'uint32_t': ' return 0;', | |||||
'int64_t': ' return 0;', | |||||
'uint64_t': ' return 0;', | |||||
'size_t': ' return 0;', | |||||
'float': ' return 0.0f;', | |||||
'bool': ' return false;', | |||||
} | |||||
""" | |||||
max code len per line in hua_wei software programming specifications | |||||
""" | |||||
max_code_len_per_line = 100 | |||||
""" | |||||
white_list_for_debug, include_dir_key_words is to | |||||
determines which header files to generate cc files from | |||||
when DEBUG on | |||||
""" | |||||
white_list_for_debug = ["attr_value.h", "operator.h", "tensor.h", "graph.h", "operator_factory.h", | |||||
"ge_ir_build.h", "ge_api.h", "tensorflow_parser.h", "caffe_parser.h"] | |||||
include_dir_key_words = ["ge", "graph", "parser"] | |||||
DEBUG = True | |||||
def need_generate_func(func_line): | |||||
""" | |||||
:param func_line: | |||||
:return: | |||||
""" | |||||
if func_line.strip().endswith("default") or func_line.strip().endswith("delete") \ | |||||
or func_line.strip().startswith("typedef") or func_line.strip().startswith("using"): | |||||
return False | |||||
return True | |||||
def file_endswith_white_list_suffix(file): | |||||
""" | |||||
:param file: | |||||
:return: | |||||
""" | |||||
if DEBUG: | |||||
for suffix in white_list_for_debug: | |||||
if file.endswith(suffix): | |||||
return True | |||||
return False | |||||
else: | |||||
return True | |||||
""" | |||||
belows are patterns used for analyse .h file | |||||
""" | |||||
# pattern function | |||||
pattern_func = re.compile(r"""(^[\s]*) #leading with space,we will find and delete after | |||||
([a-zA-Z~_] # void int likely | |||||
.* | |||||
[)] #we find ) | |||||
(?!.*{) # we do not want the case int abc() const { return 1;} | |||||
.*) | |||||
(;.*) #we want to find ; and after for we will replace these later | |||||
\n$ | |||||
""", re.VERBOSE | re.MULTILINE | re.DOTALL) | |||||
# pattern comment | |||||
pattern_comment = re.compile(r'^\s*//') | |||||
pattern_comment_2_start = re.compile(r'^\s*/[*]') | |||||
pattern_comment_2_end = re.compile(r'[*]/\s*$') | |||||
# pattern define | |||||
pattern_define = re.compile(r'^\s*#define') | |||||
pattern_define_return = re.compile(r'\\\s*$') | |||||
# blank line | |||||
pattern_blank_line = re.compile(r'^\s*$') | |||||
# virtual,explicit,friend,static | |||||
pattern_keyword = re.compile(r'(virtual\s+|explicit\s+|friend\s+|static\s+)') | |||||
# lead space | |||||
pattern_leading_space = re.compile(r'(^[\s]*)[a-zA-Z~_]') | |||||
# functions will have patterns such as func ( or func( | |||||
# but operator is an exception; the class name is preceded by an operator, and the above mode does not exist | |||||
# format like :"operator = ()" | |||||
pattern_func_name = re.compile(r'([a-zA-Z0-9~_\-]+\s*|operator?.*)[(]') | |||||
# template | |||||
pattern_template = re.compile(r'^\s*template') | |||||
pattern_template_end = re.compile(r'>\s*$') | |||||
# namespace | |||||
pattern_namespace = re.compile(r'namespace.*{') | |||||
# class : which can handle classA a and {not on the same line, but if found ';' after class,then don't deal with | |||||
pattern_class = re.compile(r'^[\s]*(class|struct)\s+(%s\s+)?([a-zA-Z0-9_\-]+<?)(?!.*;)' % GE_ATTR) | |||||
# {} | |||||
pattern_start = re.compile('{') | |||||
pattern_end = re.compile('}') | |||||
line_index = 0 | |||||
class H2CC(object): | |||||
def __init__(self, input_file, output_file, shared_includes_content): | |||||
""" | |||||
:param input_file: | |||||
:param output_file: | |||||
:param shared_includes_content: | |||||
""" | |||||
self.input_file = input_file | |||||
self.output_file = output_file | |||||
self.shared_includes_content = shared_includes_content | |||||
self.line_index = 0 | |||||
self.input_fd = open(self.input_file, 'r') | |||||
self.input_content = self.input_fd.readlines() | |||||
self.output_fd = open(self.output_file, 'w') | |||||
# The state may be normal_now(in the middle of {}),class_now,namespace_now | |||||
self.stack = [] | |||||
self.stack_class = [] | |||||
self.stack_template = [] | |||||
# record funcs generated by h2cc func | |||||
self.func_list_exist = [] | |||||
def __del__(self): | |||||
self.input_fd.close() | |||||
self.output_fd.close() | |||||
del self.stack | |||||
del self.stack_class | |||||
del self.stack_template | |||||
del self.func_list_exist | |||||
def just_skip(self): | |||||
# skip blank line or comment | |||||
if pattern_blank_line.search(self.input_content[self.line_index]) or pattern_comment.search( | |||||
self.input_content[self.line_index]): # /n or comment using // | |||||
self.line_index += 1 | |||||
if pattern_comment_2_start.search(self.input_content[self.line_index]): # comment using /* | |||||
while not pattern_comment_2_end.search(self.input_content[self.line_index]): # */ | |||||
self.line_index += 1 | |||||
self.line_index += 1 | |||||
# skip define | |||||
if pattern_define.search(self.input_content[self.line_index]): | |||||
while pattern_blank_line.search(self.input_content[self.line_index]) or pattern_define_return.search( | |||||
self.input_content[self.line_index]): | |||||
self.line_index += 1 | |||||
self.line_index += 1 | |||||
def write_inc_content(self): | |||||
for shared_include_content in self.shared_includes_content: | |||||
self.output_fd.write(shared_include_content) | |||||
def h2cc(self): | |||||
""" | |||||
:return: | |||||
""" | |||||
logging.info("start generate cc_file[%s] from h_file[%s]", self.output_file, self.input_file) | |||||
global pattern_comment | |||||
global pattern_comment_2_start | |||||
global pattern_comment_2_end | |||||
global pattern_blank_line | |||||
global pattern_func | |||||
global pattern_keyword | |||||
global pattern_leading_space | |||||
global pattern_func_name | |||||
global pattern_template | |||||
global pattern_template_end | |||||
global pattern_namespace | |||||
global pattern_class | |||||
global pattern_start | |||||
global pattern_end | |||||
global line_index | |||||
# write inc content | |||||
self.write_inc_content() | |||||
# core processing cycle, process the input .h file by line | |||||
while self.line_index < len(self.input_content): | |||||
# handle comment and blank line | |||||
self.just_skip() | |||||
# match namespace | |||||
self.handle_namespace() | |||||
# match template | |||||
template_string = self.handle_template() | |||||
# match class | |||||
line = self.input_content[self.line_index] | |||||
match_class = pattern_class.search(line) | |||||
match_start = pattern_start.search(line) | |||||
handle_class_result = self.handle_class(template_string, line, match_start, match_class) | |||||
if handle_class_result == "continue": | |||||
continue | |||||
# match "}" | |||||
handle_stack_result = self.handle_stack(match_start) | |||||
if handle_stack_result == "continue": | |||||
continue | |||||
# handle func | |||||
handle_func1_result, line, start_i = self.handle_func1(line) | |||||
if handle_func1_result == "continue": | |||||
continue | |||||
# here means func is found | |||||
# delete key word | |||||
line = pattern_keyword.sub('', line) | |||||
logging.info("line[%s]", line) | |||||
# Class member function | |||||
# if friend we will not add class name | |||||
friend_match = re.search('friend ', line) | |||||
if len(self.stack_class) > 0 and not friend_match: | |||||
line, func_name = self.handle_class_member_func(line, template_string) | |||||
# Normal functions | |||||
else: | |||||
line, func_name = self.handle_normal_func(line, template_string) | |||||
need_generate = need_generate_func(line) | |||||
# func body | |||||
line += self.implement_function(line) | |||||
# comment | |||||
line = self.gen_comment(start_i) + line | |||||
# write to out file | |||||
self.write_func_content(line, func_name, need_generate) | |||||
# next loop | |||||
self.line_index += 1 | |||||
logging.info('Added %s functions', len(self.func_list_exist)) | |||||
logging.info('Successfully converted,please see ' + self.output_file) | |||||
def handle_func1(self, line): | |||||
""" | |||||
:param line: | |||||
:return: | |||||
""" | |||||
find1 = re.search('[(]', line) | |||||
if not find1: | |||||
self.line_index += 1 | |||||
return "continue", line, None | |||||
find2 = re.search('[)]', line) | |||||
start_i = self.line_index | |||||
space_match = pattern_leading_space.search(line) | |||||
# deal with | |||||
# int abc(int a, | |||||
# int b) | |||||
if find1 and (not find2): | |||||
self.line_index += 1 | |||||
line2 = self.input_content[self.line_index] | |||||
if space_match: | |||||
line2 = re.sub('^' + space_match.group(1), '', line2) | |||||
line += line2 | |||||
while self.line_index < len(self.input_content) and (not re.search('[)]', line2)): | |||||
self.line_index += 1 | |||||
line2 = self.input_content[self.line_index] | |||||
line2 = re.sub('^' + space_match.group(1), '', line2) | |||||
line += line2 | |||||
match_start = pattern_start.search(self.input_content[self.line_index]) | |||||
match_end = pattern_end.search(self.input_content[self.line_index]) | |||||
if match_start: # like ) { or ) {} int the last line | |||||
if not match_end: | |||||
self.stack.append('normal_now') | |||||
ii = start_i | |||||
while ii <= self.line_index: | |||||
ii += 1 | |||||
self.line_index += 1 | |||||
return "continue", line, start_i | |||||
logging.info("line[%s]", line) | |||||
# ' int abc();'->'int abc()' | |||||
(line, match) = pattern_func.subn(r'\2\n', line) | |||||
logging.info("line[%s]", line) | |||||
# deal with case: | |||||
# 'int \n abc(int a, int b)' | |||||
if re.search(r'^\s*(inline)?\s*[a-zA-Z0-9_]+\s*$', self.input_content[start_i - 1]): | |||||
line = self.input_content[start_i - 1] + line | |||||
line = line.lstrip() | |||||
if not match: | |||||
self.line_index += 1 | |||||
return "continue", line, start_i | |||||
return "pass", line, start_i | |||||
def handle_stack(self, match_start): | |||||
""" | |||||
:param match_start: | |||||
:return: | |||||
""" | |||||
line = self.input_content[self.line_index] | |||||
match_end = pattern_end.search(line) | |||||
if match_start: | |||||
self.stack.append('normal_now') | |||||
if match_end: | |||||
top_status = self.stack.pop() | |||||
if top_status == 'namespace_now': | |||||
self.output_fd.write(line + '\n') | |||||
elif top_status == 'class_now': | |||||
self.stack_class.pop() | |||||
self.stack_template.pop() | |||||
if match_start or match_end: | |||||
self.line_index += 1 | |||||
return "continue" | |||||
if len(self.stack) > 0 and self.stack[-1] == 'normal_now': | |||||
self.line_index += 1 | |||||
return "continue" | |||||
return "pass" | |||||
def handle_class(self, template_string, line, match_start, match_class): | |||||
""" | |||||
:param template_string: | |||||
:param line: | |||||
:param match_start: | |||||
:param match_class: | |||||
:return: | |||||
""" | |||||
if match_class: # we face a class | |||||
self.stack_template.append(template_string) | |||||
self.stack.append('class_now') | |||||
class_name = match_class.group(3) | |||||
# class template specializations: class A<u,Node<u> > | |||||
if '<' in class_name: | |||||
k = line.index('<') | |||||
fit = 1 | |||||
for ii in range(k + 1, len(line)): | |||||
if line[ii] == '<': | |||||
fit += 1 | |||||
if line[ii] == '>': | |||||
fit -= 1 | |||||
if fit == 0: | |||||
break | |||||
class_name += line[k + 1:ii + 1] | |||||
logging.info('class_name[%s]', class_name) | |||||
self.stack_class.append(class_name) | |||||
while not match_start: | |||||
self.line_index += 1 | |||||
line = self.input_content[self.line_index] | |||||
match_start = pattern_start.search(line) | |||||
self.line_index += 1 | |||||
return "continue" | |||||
return "pass" | |||||
def handle_template(self): | |||||
line = self.input_content[self.line_index] | |||||
match_template = pattern_template.search(line) | |||||
template_string = '' | |||||
if match_template: | |||||
match_template_end = pattern_template_end.search(line) | |||||
template_string = line | |||||
while not match_template_end: | |||||
self.line_index += 1 | |||||
line = self.input_content[self.line_index] | |||||
template_string += line | |||||
match_template_end = pattern_template_end.search(line) | |||||
self.line_index += 1 | |||||
return template_string | |||||
def handle_namespace(self): | |||||
line = self.input_content[self.line_index] | |||||
match_namespace = pattern_namespace.search(line) | |||||
if match_namespace: # we face namespace | |||||
self.output_fd.write(line + '\n') | |||||
self.stack.append('namespace_now') | |||||
self.line_index += 1 | |||||
def handle_normal_func(self, line, template_string): | |||||
template_line = '' | |||||
self.stack_template.append(template_string) | |||||
if self.stack_template[-1] != '': | |||||
template_line = re.sub(r'\s*template', 'template', self.stack_template[-1]) | |||||
# change '< class T = a, class U = A(3)>' to '<class T, class U>' | |||||
template_line = re.sub(r'\s*=.*>(\s*)$', r'>\1', template_line) | |||||
template_line = re.sub(r'\s*=.*,', ',', template_line) | |||||
template_line = re.sub(r'\s*=.*', '', template_line) | |||||
line = re.sub(r'\s*=.*,', ',', line) | |||||
line = re.sub(r'\s*=.*\)', ')', line) | |||||
line = template_line + line | |||||
self.stack_template.pop() | |||||
func_name = re.search(r'^.*\)', line, re.MULTILINE | re.DOTALL).group() | |||||
logging.info("line[%s]", line) | |||||
logging.info("func_name[%s]", func_name) | |||||
return line, func_name | |||||
def handle_class_member_func(self, line, template_string): | |||||
template_line = '' | |||||
x = '' | |||||
if template_string != '': | |||||
template_string = re.sub(r'\s*template', 'template', template_string) | |||||
template_string = re.sub(r'\s*=.*>(\s*)$', r'>\1', template_string) | |||||
template_string = re.sub(r'\s*=.*,', ',', template_string) | |||||
template_string = re.sub(r'\s*=.*', '', template_string) | |||||
if self.stack_template[-1] != '': | |||||
if not (re.search(r'<\s*>', stack_template[-1])): | |||||
template_line = re.sub(r'^\s*template', 'template', stack_template[-1]) | |||||
if not (re.search(r'<.*>', self.stack_class[-1])): | |||||
# for x we get like template<class T, typename U> -> <T,U> | |||||
x = re.sub(r'template\s*<', '<', template_line) # remove template -> <class T, typename U> | |||||
x = re.sub(r'\n', '', x) | |||||
x = re.sub(r'\s*=.*,', ',', x) | |||||
x = re.sub(r'\s*=.*\>', '>', x) | |||||
x = x.rstrip() # remove \n | |||||
x = re.sub(r'(class|typename)\s+|(<class>|<typename>\s*class)', '', | |||||
x) # remove class,typename -> <T, U> | |||||
x = re.sub(r'<\s+', '<', x) | |||||
x = re.sub(r'\s+>', '>', x) | |||||
x = re.sub(r'\s+,', ',', x) | |||||
x = re.sub(r',\s+', ', ', x) | |||||
line = re.sub(r'\s*=\s+0', '', line) | |||||
line = re.sub(r'\s*=\s+.*,', ',', line) | |||||
line = re.sub(r'\s*=\s+.*\)', ')', line) | |||||
logging.info("x[%s]\nline[%s]", x, line) | |||||
# if the function is long, void ABC::foo() | |||||
# breaks into two lines void ABC::\n foo() | |||||
temp_line = pattern_func_name.sub(self.stack_class[-1] + x + '::' + r'\1(', line, count=1) | |||||
if len(temp_line) > max_code_len_per_line: | |||||
line = pattern_func_name.sub(self.stack_class[-1] + x + '::\n' + r'\1(', line, count=1) | |||||
else: | |||||
line = temp_line | |||||
logging.info("line[%s]", line) | |||||
# add template as the above if there is one | |||||
template_line = re.sub(r'\s*=.*>(\s*)$', r'>\1', template_line) | |||||
template_line = re.sub(r'\s*=.*,', ',', template_line) | |||||
template_line = re.sub(r'\s*=.*', '', template_line) | |||||
line = template_line + template_string + line | |||||
func_name = re.search(r'^.*\)', line, re.MULTILINE | re.DOTALL).group() | |||||
logging.info("line[%s]", line) | |||||
logging.info("func_name[%s]", func_name) | |||||
return line, func_name | |||||
def write_func_content(self, content, func_name, need_generate): | |||||
if not (func_name in self.func_list_exist) and need_generate: | |||||
self.output_fd.write(content) | |||||
self.func_list_exist.append(func_name) | |||||
logging.info('add func:[%s]', func_name) | |||||
def gen_comment(self, start_i): | |||||
comment_line = '' | |||||
# Function comments are on top of function declarations, copy them over | |||||
k = start_i - 1 # one line before this func start | |||||
if pattern_template.search(self.input_content[k]): | |||||
k -= 1 | |||||
if pattern_comment_2_end.search(self.input_content[k]): | |||||
comment_line = self.input_content[k].lstrip() | |||||
while not pattern_comment_2_start.search(self.input_content[k]): | |||||
k -= 1 | |||||
comment_line = self.input_content[k].lstrip() + comment_line | |||||
else: | |||||
for j in range(k, 0, -1): | |||||
c_line = self.input_content[j] | |||||
if pattern_comment.search(c_line): | |||||
c_line = re.sub(r'\s*//', '//', c_line) | |||||
comment_line = c_line + comment_line | |||||
else: | |||||
break | |||||
return comment_line | |||||
@staticmethod | |||||
def implement_function(func): | |||||
function_def = '' | |||||
function_def += '{\n' | |||||
all_items = func.split() | |||||
start = 0 | |||||
return_type = all_items[start] | |||||
if return_type == "const": | |||||
start += 1 | |||||
return_type = all_items[start] | |||||
if return_type.startswith(('std::map', 'std::set', 'std::vector')): | |||||
return_type = "std::map" | |||||
if return_type.endswith('*') or (len(all_items) > start + 1 and all_items[start + 1].startswith('*')): | |||||
return_type = "Ptr" | |||||
if len(all_items) > start + 1 and all_items[start + 1].startswith('&'): | |||||
return_type += "&" | |||||
if RETURN_STATEMENTS.__contains__(return_type): | |||||
function_def += RETURN_STATEMENTS[return_type] | |||||
else: | |||||
logging.warning("Unhandled return type[%s]", return_type) | |||||
function_def += '\n' | |||||
function_def += '}\n' | |||||
function_def += '\n' | |||||
return function_def | |||||
def collect_header_files(path): | |||||
""" | |||||
:param path: | |||||
:return: | |||||
""" | |||||
header_files = [] | |||||
shared_includes_content = [] | |||||
for root, dirs, files in os.walk(path): | |||||
files.sort() | |||||
for file in files: | |||||
if file.find("git") >= 0: | |||||
continue | |||||
if not file.endswith('.h'): | |||||
continue | |||||
file_path = os.path.join(root, file) | |||||
file_path = file_path.replace('\\', '/') | |||||
header_files.append(file_path) | |||||
include_str = '#include "{}"\n'.format(file_path[path.rindex('/') + 1:]) | |||||
shared_includes_content.append(include_str) | |||||
# for acl error code | |||||
shared_includes_content.append('#include <iostream>\n') | |||||
shared_includes_content.append('const int ACL_ERROR_COMPILING_STUB_MODE = 100039;\n') | |||||
return header_files, shared_includes_content | |||||
def generate_stub_file(inc_dir, out_cc_dir): | |||||
""" | |||||
:param inc_dir: | |||||
:param out_cc_dir: | |||||
:return: | |||||
""" | |||||
target_header_files, shared_includes_content = collect_header_files(inc_dir) | |||||
for header_file in target_header_files: | |||||
if not file_endswith_white_list_suffix(header_file): | |||||
continue | |||||
cc_file = re.sub('.h*$', '.cc', header_file) | |||||
h_2_cc = H2CC(header_file, out_cc_dir + cc_file[cc_file.rindex('/') + 1:], shared_includes_content) | |||||
h_2_cc.h2cc() | |||||
def gen_code(inc_dir, out_cc_dir): | |||||
""" | |||||
:param inc_dir: | |||||
:param out_cc_dir: | |||||
:return: | |||||
""" | |||||
if not inc_dir.endswith('/'): | |||||
inc_dir += '/' | |||||
if not out_cc_dir.endswith('/'): | |||||
out_cc_dir += '/' | |||||
for include_dir_key_word in include_dir_key_words: | |||||
generate_stub_file(inc_dir + include_dir_key_word, out_cc_dir) | |||||
if __name__ == '__main__': | |||||
inc_dir = sys.argv[1] | |||||
out_cc_dir = sys.argv[2] | |||||
gen_code(inc_dir, out_cc_dir) |