Last active
November 26, 2020 06:43
-
-
Save katyo/1c2a39c490e360049245868f1aa76515 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
import os | |
import re | |
from optparse import OptionParser | |
import ConfigParser | |
try: | |
import pygccxml | |
except ImportError: | |
print "You must instal pygccxml in-order to run this script." | |
sys.exit(1) | |
from pygccxml.declarations.cpptypes import * | |
from pygccxml.declarations.type_traits import * | |
from pygccxml.declarations.type_traits_classes import * | |
from pygccxml.declarations.calldef import * | |
from pygccxml.declarations.calldef_types import * | |
from pygccxml.declarations.free_calldef import * | |
from pygccxml.declarations.calldef_members import * | |
from pygccxml.declarations.class_declaration import ACCESS_TYPES, class_declaration_t | |
from pygccxml.declarations.typedef import typedef_t | |
class UnsupportedError(NotImplementedError): | |
"""Exception for unsupported features.""" | |
pass | |
DEBUG = False | |
ARRAY_SIZE_VAR_NAME = 'arr_size' | |
RET_VAL_ON_EXCEPTION = 'NULL' | |
GENERATED_FILE_SUFFIX = "_C_Wrapper" | |
THIS_VAR_NAME = 'class_this' | |
RET_VAL_CLASS_NAME = 'ptr_ret_val_class' | |
WAS_EXCEPTION_ARG_NAME = 'ptr_was_exception' | |
C_BOOL_TYPE_NAME = 'BOOL_C' | |
C_TRUE_VAL = 'TRUE_C' | |
C_FALSE_VAL = 'FALSE_C' | |
# TODO: check whether those vars are in [arg_info.arg_name for arg_info in func_info.func_args_info_list], and if so - add a suffix. | |
PYGCCXML_DTOR_TOKEN = re.compile(r"._\d+") | |
PYGCCXML_FUNC_DECL_ARGS = re.compile(r".*\((.*?)\)") # =The last ()'s contents. | |
OPERATOR_MAP = { | |
'+': "plus", | |
'-': "minus", | |
'*': "multiply", | |
'/': "divison", | |
'%': "mod", | |
'^': "bitwise_xor", | |
'&': "bitwise_and", | |
'|': "bitwise_or", | |
'~': "bitwise_not", | |
'!': "not", | |
'=': "assign", | |
'<': "smaller", | |
'>': "bigger", | |
'+=': "plus_assign", | |
'-=': "minus_assign", | |
'*=': "multiply_assign", | |
'/=': "division_assign", | |
'%=': "mod_assign", | |
'^=': "bitwise_xor_assign", | |
'&=': "bitwise_and_assign", | |
'|=': "bitwise_or_assign", | |
'<<': "shift_left", | |
'>>': "shift_right", | |
'<<=': "shift_left_assign", | |
'>>=': "shift_right_assign", | |
'==': "equal", | |
'!=': "not_assign", | |
'<=': "smaller_or_equal", | |
'>=': "bigger_or_equal", | |
'&&': "and", | |
'||': "or", | |
'++': "plus_plus", | |
'--': "minus_minus", | |
',': "comma", | |
'->*': "pointer_redirect", | |
'->': "redirect", | |
'()': "function_call", | |
'[]': "subscript", | |
'new': "new", | |
'new []': "new_array", | |
'delete': "delete", | |
'delete []': "delete_array" | |
} # TODO: Check the function_call op. | |
OPERATORS_LIST_BY_LENGTH = sorted(OPERATOR_MAP.keys(), key=len, reverse=True) # From the longest to the shortest, to check '+=' before '+' (or '='). | |
# Proxy funcs to shield from possible pygccxml implementation changes: | |
def is_declarated_type(arg): | |
"""Returns whether the argument is a declaration type.""" | |
return isinstance(arg, declarated_t) | |
def is_ellipsis(arg): | |
"""Returns whether the argument is ellipsis (...).""" | |
return isinstance(arg, ellipsis_t) | |
def is_qualifier(arg): | |
"""Returns whether the argument is a qualifier.""" | |
return isinstance(arg, type_qualifiers_t) | |
def is_unknown_type(arg): | |
"""Returns whether the argument is an unknown type.""" | |
return isinstance(arg, unknown_t) | |
def is_member_function_type(arg): | |
"""Returns whether the argument is a member function type.""" | |
return isinstance(arg, member_function_type_t) | |
def is_typedef(arg): | |
"""Returns whether the argument is a typedef.""" | |
return isinstance(arg, typedef_t) | |
def get_public_default_ctor(cls): | |
"""Returns the class public default ctor, or None if one doesn't exist.""" | |
return has_trivial_constructor(cls) | |
def has_public_dtor(cls): | |
"""Returns whether the class has a public destructor.""" | |
return has_public_destructor(cls) | |
def is_abstract_class_declaration(arg): | |
"""Returns whether arg is a class declaration that is not a typedef - for example: typedef abstract_class_decl typedef_decl""" | |
#return is_class_declaration(decl) and not is_typedef(decl) | |
return isinstance(arg, class_declaration_t) | |
def get_full_name(decl, sub_seq=None): | |
"""Generates a C++ token's name, including namespaces.""" | |
#if is_abstract_class_declaration(decl): | |
# ns_name = get_full_name(decl.parent) | |
# if not ns_name.endswith("::"): # True to every namespace except for the global namespace. | |
# ns_name += "::" | |
# return ns_name + decl.declaration.name | |
full_name = pygccxml.declarations.full_name(decl) | |
if sub_seq is not None: | |
for old_token, new_token in sub_seq: | |
full_name = full_name.replace(old_token, new_token) | |
return full_name | |
def get_func_decl_str(func): | |
"""Return a func declaration string.""" | |
args_str = PYGCCXML_FUNC_DECL_ARGS.match(func.create_decl_string()).group(1).strip() | |
return "%s(%s)" % (get_full_name(func), args_str) | |
def python_to_camel_case(decl_str): | |
"""Python to (Upper-)Camel Case naming convention, e.g.: "func_name" -> "FuncName" """ | |
words = decl_str.split("_") | |
return "".join([word.capitalize() for word in words]) | |
def strip_global_ns(decl_str): | |
"""Strip the global namespace ot a C++ declaration string.""" | |
return decl_str.lstrip(':') | |
def get_c_name(decl, is_full_name=True, c_sub_seq=None): | |
"""Generates a C token from a C++ token.""" | |
if is_full_name: | |
cpp_name = get_full_name(decl) | |
else: | |
cpp_name = decl.name | |
c_name = strip_global_ns(cpp_name).replace('::', '_').replace('~', "delete_").replace('>', '_').replace('<', '_').replace(' ', '').replace(',', '_').replace('*', "_ptr_").replace('&', "_ref_") | |
# The first substitution is for namespaces. The second is for dtors. All others are for templates. | |
if c_sub_seq is not None: | |
for old_token, new_token in c_sub_seq: | |
if old_token: # FIXME: | |
c_name = c_name.replace(old_token, new_token) | |
return c_name | |
def get_class_ptr_name(class_c_name): | |
"""Generates a class pointer token from its C class token.""" | |
return "PTR_%s" % (class_c_name,) | |
def get_error_arg_str(is_c99): | |
"""Returns the type-name string of the error arg.""" | |
if is_c99: | |
c_bool_type_str = "bool" | |
else: | |
c_bool_type_str = C_BOOL_TYPE_NAME | |
return "%s *%s" % (c_bool_type_str, WAS_EXCEPTION_ARG_NAME) | |
def get_enum_c_name(enum): | |
"""Generates a C enum token from a C++ one.""" | |
enum_name = get_c_name(enum) | |
if enum_name == enum.name: # To prevent enum redifinition in the global scope. unlike typedef redifinition - it causes a compilation error. | |
enum_name += "_C" # TODO: Can (still) cause redifinition. | |
return enum_name | |
def get_header_guard_name(generated_base_file_path): | |
"""Returns the header guard. for a specific header file name.""" | |
generated_header_file_name = os.path.basename(generated_base_file_path+".h") | |
return generated_header_file_name.upper().replace('.', '_') | |
def is_public_concrete_func(member_func): | |
"""Reurns whether a member function\operator is public and concrete.""" | |
return ((member_func.access_type == ACCESS_TYPES.PUBLIC) and (member_func.virtuality != VIRTUALITY_TYPES.PURE_VIRTUAL)) | |
def get_class_by_decl(global_ns, class_decl, is_safe=True): | |
"""Returns the class for a class declaration.""" | |
try: | |
return global_ns.class_(get_full_name(class_decl)) | |
except pygccxml.declarations.runtime_errors.multiple_declarations_found_t: | |
#print(global_ns, get_full_name(class_decl)) | |
#pass | |
return global_ns.classes(get_full_name(class_decl))[1] | |
except pygccxml.declarations.runtime_errors.declaration_not_found_t: | |
if is_safe: | |
raise UnsupportedError("No concrete class for class declaration %s. Possible reason: template instatiation is missing." % (str(class_decl),)) | |
else: | |
raise | |
def exception_handling_str(generate_exception_handling_code, output_str, generate_error_arg, is_c99, is_void_ret_type, ret_type_c_str): | |
"""Generates exception handling wrapper around an implementation output string, with or without an error argument.""" | |
if not generate_exception_handling_code: | |
return output_str | |
if is_void_ret_type: | |
on_exception_return_str = "" | |
else: | |
on_exception_return_str = "\treturn (%s) %s;\n" % (ret_type_c_str, RET_VAL_ON_EXCEPTION) | |
if is_c99: | |
c_false_str = "false" | |
c_true_str = "true" | |
else: | |
c_false_str = C_FALSE_VAL | |
c_true_str = C_TRUE_VAL | |
error_arg_no_exception_str = "" | |
error_arg_was_exception_str = "" | |
if generate_error_arg: | |
error_arg_no_exception_str = "if((void *)%s != NULL) (*%s) = %s;\n" % (WAS_EXCEPTION_ARG_NAME, WAS_EXCEPTION_ARG_NAME, c_false_str) | |
error_arg_was_exception_str = "if((void *)%s != NULL) (*%s) = %s;\n" % (WAS_EXCEPTION_ARG_NAME, WAS_EXCEPTION_ARG_NAME, c_true_str) | |
return """ try { | |
%s%s | |
} | |
catch(...) { | |
%s\t%s | |
}""" % (error_arg_no_exception_str, output_str, error_arg_was_exception_str, on_exception_return_str) | |
class MemoryFile(object): | |
"""A class to implement a file on the memory.""" | |
MARK_TOKEN = "@@@" | |
def __init__(self, path): | |
self.path = path | |
self.lines = [self.MARK_TOKEN] | |
def close(self): | |
"""Closes the memory file.""" | |
self.lines.remove(self.MARK_TOKEN) | |
out_file = open(self.path, 'w') | |
out_file.write("\n".join(self.lines)) | |
out_file.close() | |
def write(self, str): | |
"""Writes a string to the memory file.""" | |
self.lines.append(str) | |
def write_at_mark(self, str): | |
"""Writes a string at the mark position of the memory file.""" | |
self.lines.insert(self.lines.index(self.MARK_TOKEN), str) | |
def set_mark(self): | |
"""Set the mark posiion in the memory file.""" | |
self.lines.remove(self.MARK_TOKEN) | |
self.lines.append(self.MARK_TOKEN) | |
class BasicOutputClass(object): | |
"""A class to handle the files output.""" | |
def __init__(self, header_file_path, generate_dl): | |
self.header_file_path = header_file_path | |
header_file_name = os.path.basename(header_file_path) | |
generated_base_file_name = header_file_name[:header_file_name.rindex(".h")]+GENERATED_FILE_SUFFIX | |
self.generated_base_file_path = os.path.join(os.path.dirname(header_file_path), generated_base_file_name) | |
self.generate_dl = generate_dl | |
self.cpp_file = MemoryFile(self.generated_base_file_path+".cpp") | |
self.h_file = MemoryFile(self.generated_base_file_path+".h") | |
if generate_dl: | |
self.def_file = MemoryFile(self.generated_base_file_path+".def") | |
def close(self): | |
"""Forces the output file creation.""" | |
self.cpp_file.close() | |
self.h_file.close() | |
if self.generate_dl: | |
self.def_file.close() | |
def write_to_cpp_file(self, output_str): | |
"""Write a string in the cpp file.""" | |
self.cpp_file.write(output_str+'\n') | |
if DEBUG: | |
print "CPP: %s" % (output_str,) | |
def write_to_h_file(self, output_str): | |
"""Write a string in the header file.""" | |
self.h_file.write(output_str+'\n') | |
if DEBUG: | |
print "H: %s" % (output_str,) | |
def write_at_h_file_mark(self, output_str): | |
"""Write a string in the header file's mark.""" | |
self.h_file.write_at_mark(output_str+'\n') | |
if DEBUG: | |
print "H (FRONT): %s" % (output_str,) | |
def write_to_def_file(self, output_str): | |
"""Write a string in the def file, if this option is enabled.""" | |
if self.generate_dl: | |
self.def_file.write(output_str+'\n') | |
if DEBUG: | |
print "DEF: %s" % (output_str,) | |
def mark_h_file_front(self): | |
"""Mark the start of the header file, where the typedefs would be written.""" | |
self.h_file.set_mark() | |
class CodeOutputClass(BasicOutputClass): | |
"""A class to handle the code output.""" | |
def __init__(self, header_file_path, generate_exception_handling_code, generate_dl, is_compact_string, generate_operators, is_assume_copy, is_assume_assign, generate_error_arg, is_verbose, is_camel_case): | |
super(CodeOutputClass, self).__init__(header_file_path, generate_dl) | |
self.generate_exception_handling_code = generate_exception_handling_code | |
self.is_compact_string = is_compact_string | |
self.generate_operators = generate_operators | |
self.is_assume_copy = is_assume_copy | |
self.is_assume_assign = is_assume_assign | |
self.generate_error_arg = generate_error_arg | |
self.is_verbose = is_verbose | |
self.is_camel_case = is_camel_case | |
def __del__(self): | |
super(CodeOutputClass, self).__del__() | |
def output_class_typedef(self, cls, global_context, alternate_class_c_ptr_name=None): | |
"""Outputs a C class representation.""" | |
class_c_name, class_c_ptr_name = global_context.add_class(cls, alternate_class_c_ptr_name) | |
verbose_str = "" | |
if self.is_verbose: | |
verbose_str = "\t/* A C wrapper for class %s */" % (get_full_name(cls),) | |
self.write_at_h_file_mark("typedef struct _%s *%s;%s" % (class_c_name, class_c_ptr_name, verbose_str)) | |
def output_func(self, func, global_context, is_array_version=False, generate_min_args_ver_only=False): | |
"""Outputs a function C wrapper.""" | |
func_info = FuncInfo(func, global_context, self.generate_error_arg, generate_min_args_ver_only) | |
c_func_name = func_info.c_func_name | |
if (func_info.func_type == func_info.DTOR): | |
# To fix the pygccxml dtor name of ~._<num> for declarated classes. | |
c_func_name = PYGCCXML_DTOR_TOKEN.sub(get_c_name(func.parent, False), c_func_name) | |
for used_def_vals_num in range(func_info.optional_args_num+1): # Variations to handle default values. | |
func_args_decl_list = [arg_c_str for arg_c_str in func_info.gen_func_args_c_strs()] | |
func_args = func_args_decl_list[:len(func_args_decl_list)-used_def_vals_num] | |
if is_array_version and (func_info.func_type == func_info.CTOR): | |
func_args.append("size_t %s" % (ARRAY_SIZE_VAR_NAME,)) # Needed for new[]. | |
func_args_str = ", ".join(func_args) | |
func_args_impl_list = [] | |
for arg_info in func_info.func_args_info_list[:len(func_info.func_args_info_list)-used_def_vals_num]: | |
redirection_str = "" | |
if arg_info.is_redirected: | |
redirection_str = "*" | |
cast_str = "" | |
if arg_info.cast_str: | |
cast_str = "(%s)" % (arg_info.cast_str,) | |
arg_name = arg_info.arg_name | |
func_args_impl_list.append((redirection_str, cast_str, arg_name)) | |
func_args_impl_str = ", ".join(["%s%s%s" % (redirection_str, cast_str, arg_name) for redirection_str, cast_str, arg_name in func_args_impl_list]) | |
if is_array_version: | |
c_func_name = "%s_array" % (c_func_name,) | |
unique_c_func_name = global_context.generate_unique_token(c_func_name) | |
if self.is_camel_case: | |
unique_c_func_name = python_to_camel_case(unique_c_func_name) | |
self.write_to_def_file('\t'+unique_c_func_name) | |
func_prototype = "%s %s(%s)" % (func_info.ret_type_info.arg_type_c_str, unique_c_func_name , func_args_str) | |
verbose_str = "" | |
if self.is_verbose: | |
verbose_str = "\t/* A C wrapper for func %s */" % (get_func_decl_str(func),) | |
self.write_to_h_file("%s;%s" % (func_prototype, verbose_str)) | |
self.write_to_cpp_file("%s {" % (func_prototype,)) | |
ret_val_c_str = "%s%s(%s)" % (func_info.class_redirection, func_info.full_name, func_args_impl_str) | |
output_str = "\t\t" | |
if (func_info.func_type == func_info.CTOR): | |
if func_info.is_default_ctor(): | |
ctor_args_str = "" | |
else: | |
ctor_args_str = "(%s)" % (func_args_impl_str,) | |
array_str = "" | |
if is_array_version: | |
array_str = "[%s]" % (ARRAY_SIZE_VAR_NAME,) | |
nothrow_str = "" | |
if not self.generate_exception_handling_code: | |
nothrow_str = "(std::nothrow) " | |
ret_val_c_str = "new %s%s%s%s" % (nothrow_str, func_info.ret_type_info.class_name, ctor_args_str, array_str) | |
elif (func_info.func_type == func_info.DTOR): | |
array_str = "" | |
if is_array_version: | |
array_str = "[]" | |
cast_str = "(%s*)" % (global_context.get_full_name(func.parent),) | |
ret_val_c_str = "delete %s(%s%s)" % (array_str, cast_str, THIS_VAR_NAME) | |
else: | |
if (func_info.ret_type_info.is_class) and (func_info.ret_type_info.is_redirected):# and not(func_info.ret_type_info.is_ptr): | |
# Special case: a class should be returned by value. We'll try to return a pointer to a new instance, if possible. | |
nothrow_str = "" | |
if not self.generate_exception_handling_code: | |
nothrow_str = "(std::nothrow) " | |
if func_info.ret_type_info.can_create_with_copy_constructor or ((func_info.ret_type_info.can_create_with_copy_constructor is None) and self.is_assume_copy): | |
ret_val_c_str = "new %s%s(%s)" % (nothrow_str, func_info.ret_type_info.class_name, ret_val_c_str) | |
elif func_info.ret_type_info.can_create_with_default_constructor or ((func_info.ret_type_info.can_create_with_default_constructor is None) and self.is_assume_assign): | |
output_str += "%s *%s = new %s%s;\n" % (func_info.ret_type_info.class_name, RET_VAL_CLASS_NAME, nothrow_str, func_info.ret_type_info.class_name) | |
nothrow_null_check_str = "" | |
if not self.generate_exception_handling_code: | |
nothrow_null_check_str = "if((void*)%s != NULL) " % (RET_VAL_CLASS_NAME,) | |
output_str += "%s*%s = %s;\n" % (nothrow_null_check_str, RET_VAL_CLASS_NAME, ret_val_c_str) | |
ret_val_c_str = RET_VAL_CLASS_NAME | |
else: | |
raise UnsupportedError("Cannot handle a class return value without a public copy ctor or public default ctor an assignment operator in %s" % (func_info.full_name, )) | |
if func_info.ret_type_info.is_void(): | |
return_str = "" | |
else: | |
return_str = "return " | |
cast_str = "" | |
if func_info.ret_type_info.cast_str: | |
cast_str = "(%s)" % (func_info.ret_type_info.arg_type_c_str,) | |
ref_str = "" | |
if (func_info.ret_type_info.is_ref and not(func_info.ret_type_info.is_class)): | |
ref_str = "&" | |
output_str += "%s%s%s%s;" % (return_str, cast_str, ref_str, ret_val_c_str) | |
output_str = exception_handling_str(self.generate_exception_handling_code, output_str, self.generate_error_arg, global_context.is_c99, func_info.ret_type_info.is_void(), func_info.ret_type_info.arg_type_c_str) | |
self.write_to_cpp_file("%s" % (output_str,)) | |
self.write_to_cpp_file( "}") | |
def output_std_string(self, string_typedef, global_context): | |
"""Outputs the std::(w)string C wrapper.""" | |
is_wstring, string_class, string_c_ptr_name = global_context.add_std_string(string_typedef) | |
if self.is_compact_string: # Otherwise, it would be outputed like other classes, recursively, as needed. | |
self.output_class_typedef(string_class, global_context, string_c_ptr_name) | |
public_default_ctor = get_public_default_ctor(string_class) | |
self.output_func(public_default_ctor, global_context) | |
self.output_func(public_default_ctor, global_context, True) | |
if is_wstring: | |
char_str = "wchar_t" | |
else: | |
char_str = "char" | |
char_ptr_str = "const %s *" % (char_str,) | |
for ctor in string_class.constructors(): | |
if (len(ctor.required_args) == 1) and (char_ptr_str == ctor.required_args[0].decl_type.build_decl_string()): | |
self.output_func(ctor, global_context, False, True) # Outputs the std::(w)string(char*) ctor. We don't generate the allocator<char_str> optional arg. | |
break | |
string_dtor = has_destructor(string_class) | |
self.output_func(string_dtor, global_context) | |
self.output_func(string_dtor, global_context, True) | |
c_str_func = string_class.member_function("c_str") | |
self.output_func(c_str_func, global_context) | |
def output_enum(self, enum, global_context): | |
"""Outputs an enum C version.""" | |
enum_name = get_enum_c_name(enum) | |
verbose_str = "" | |
if self.is_verbose: | |
verbose_str = "\t/* A C wrapper for enum %s */" % (get_full_name(enum),) | |
self.write_at_h_file_mark("enum %s {" % (enum_name,)) | |
self.write_at_h_file_mark("%s" % (",\n".join(["\t%s=%s" % (global_context.generate_unique_token(key, True), val) for key, val in enum.values]),)) # Needed to handle 2 enums with same keys in different namespaces. | |
self.write_at_h_file_mark("};%s" % (verbose_str,)) | |
global_context.add_enum(enum) | |
def output_typedef(self, typedef, global_context): | |
"""Outputs a typedef C version.""" | |
typedef_info=ArgInfo(typedef.decl_type, global_context, get_c_name(typedef)) | |
if typedef_info.is_c_decl: # There is no support for typedefs that aren't C declerations - they'll be stripped to their raw C-type. | |
verbose_str = "" | |
if self.is_verbose: | |
verbose_str = "\t/* A C wrapper for typedef %s */" % (get_full_name(typedef),) | |
self.write_at_h_file_mark("typedef %s;%s" % (typedef_info.get_type_name_str(), verbose_str)) | |
global_context.add_typedef(typedef) | |
# TODO: What about a typedef of an enum? | |
def output_class_code(self, cls, global_context): | |
"""Output cls class code.""" | |
for ctor in cls.constructors(allow_empty=True): | |
if (ctor.access_type == ACCESS_TYPES.PUBLIC): | |
self.output_func(ctor, global_context) | |
if has_public_dtor(cls): | |
dtor = has_destructor(cls) | |
self.output_func(dtor, global_context) | |
public_default_ctor = get_public_default_ctor(cls) | |
if public_default_ctor is not None: | |
self.output_func(public_default_ctor, global_context, True) | |
self.output_func(dtor, global_context, True) | |
for member_func in cls.member_functions(allow_empty=True): | |
if is_public_concrete_func(member_func): | |
self.output_func(member_func, global_context) | |
if self.generate_operators: | |
for member_op in cls.member_operators(allow_empty=True): | |
if is_public_concrete_func(member_op): | |
self.output_func(member_op, global_context) | |
def output_prefix_code(self, is_c99): | |
"""Output the prefix code (includes, base typedefs, etc.).""" | |
header_file_name = os.path.basename(self.header_file_path) | |
generated_header_file_name = os.path.basename(self.generated_base_file_path+".h") | |
generated_base_file_name = os.path.basename(self.generated_base_file_path) | |
self.write_to_cpp_file("""#include "%s" """ % (header_file_name,)) | |
self.write_to_cpp_file("""#include "%s" """ % (generated_header_file_name,)) | |
self.write_to_def_file("""LIBRARY "%s" | |
EXPORTS""" % (generated_base_file_name,)) | |
header_guard = get_header_guard_name(self.generated_base_file_path) | |
self.write_to_h_file("#ifndef %s" % (header_guard,)) | |
self.write_to_h_file("#define %s" % (header_guard,)) | |
self.write_to_h_file("""#ifdef __cplusplus | |
extern "C" { | |
#endif""") | |
if not is_c99: | |
self.write_to_h_file("#define %s 0" % (C_FALSE_VAL,)) | |
self.write_to_h_file("#define %s 1" % (C_TRUE_VAL,)) | |
self.write_to_h_file("typedef unsigned char %s;" % (C_BOOL_TYPE_NAME,)) | |
self.mark_h_file_front() | |
if self.generate_dl: | |
self.write_to_cpp_file("""#ifdef WIN32 | |
#include <Windows.h> | |
extern "C" BOOL WINAPI DllMain( | |
HINSTANCE hinstDLL, // handle to DLL module | |
DWORD fdwReason, // reason for calling function | |
LPVOID lpReserved ) // reserved | |
{ | |
// Perform actions based on the reason for calling. | |
switch( fdwReason ) | |
{ | |
case DLL_PROCESS_ATTACH: | |
// Initialize once for each new process. | |
// Return FALSE to fail DLL load. | |
break; | |
case DLL_THREAD_ATTACH: | |
// Do thread-specific initialization. | |
break; | |
case DLL_THREAD_DETACH: | |
// Do thread-specific cleanup. | |
break; | |
case DLL_PROCESS_DETACH: | |
// Perform any necessary cleanup. | |
break; | |
} | |
return TRUE; // Successful DLL_PROCESS_ATTACH. | |
} | |
#endif // WIN32""") | |
def output_suffix_code(self): | |
"""Output the suffix code (header protectors, etc.).""" | |
self.write_to_h_file("""#ifdef __cplusplus | |
} | |
#endif /* __cplusplus */""") | |
generated_header_file_name = os.path.basename(self.generated_base_file_path+".h") | |
self.write_to_h_file("#endif /* %s */" % (get_header_guard_name(self.generated_base_file_path),)) | |
class CodeContext(object): | |
"""The code context (classes, typdefs, etc.) of the parsed header files.""" | |
CLASS = 0 | |
TYPEDEF = 1 | |
ENUM = 2 | |
def __init__(self, header_file_path, gccxml_file_path, compiler_type, is_c99): | |
generator_path, generator_name = pygccxml.utils.find_xml_generator() | |
#config = pygccxml.parser.config_t(gccxml_path=gccxml_file_path, compiler=compiler_type) | |
config = pygccxml.parser.xml_generator_configuration_t( | |
gccxml_path=gccxml_file_path, compiler=compiler_type, | |
xml_generator=generator_name, xml_generator_path=generator_path, | |
include_paths=["/usr/include", "/usr/include/opencascade"]) | |
#config = pygccxml.parser.xml_generator_configuration_t( | |
# xml_generator_path=gccxml_file_path, | |
# xml_generator=compiler_type or "gccxml") | |
decls = pygccxml.parser.parse([header_file_path], config) | |
self.global_ns = pygccxml.declarations.get_global_namespace(decls) | |
self.is_c99 = is_c99 | |
self.class_c_ptrs_map = {} | |
self.token_freqs = {} | |
self.typedefs_map = {} | |
self.std_wstring_c_name = None | |
self.std_string_c_name = None | |
self.recursive_classes = [] | |
self.recursive_typedefs = [] | |
self.recursive_enums = [] | |
self.enums_map = {} | |
def __getattr__(self, attr_name): | |
""" CodeContext becomes a proxy to the global namespace attributes.""" | |
return getattr(self.global_ns, attr_name) | |
def generate_unique_token(self, c_func_name, always_add_suffix=False): | |
"""Generates a unique C token.""" | |
# TODO: Use the args types instead, perhaps. | |
if c_func_name in self.token_freqs: | |
self.token_freqs[c_func_name] += 1 | |
return "%s%d" % (c_func_name, self.token_freqs[c_func_name]) | |
else: | |
self.token_freqs[c_func_name] = 1 | |
if always_add_suffix: | |
return "%s%d" % (c_func_name, self.token_freqs[c_func_name]) | |
else: | |
return c_func_name | |
def add_class(self, cls, alternate_class_c_ptr_name=None): | |
"""Add cls to the code context.""" | |
class_c_name = self.get_c_name(cls) | |
if alternate_class_c_ptr_name is None: | |
class_c_ptr_name = get_class_ptr_name(class_c_name) | |
else: | |
class_c_ptr_name = alternate_class_c_ptr_name | |
self.class_c_ptrs_map[self.get_full_name(cls)] = class_c_ptr_name | |
return (class_c_name, class_c_ptr_name) | |
def add_typedef(self, typedef): | |
"""Add typedef to the code context.""" | |
self.typedefs_map[get_full_name(typedef)] = get_c_name(typedef) | |
def add_enum(self, enum): | |
"""Add an enum to the code context.""" | |
self.enums_map[get_full_name(enum)] = get_enum_c_name(enum) | |
def get_class_data(self, cls): | |
"""Get the class data for cls from the code context.""" | |
class_name = self.get_full_name(cls) | |
if class_name not in self.class_c_ptrs_map: | |
self.add_class(cls) | |
if is_abstract_class_declaration(cls): | |
cls = get_class_by_decl(self.global_ns, cls) | |
if cls is not None: | |
self.recursive_classes.append(cls) | |
return (class_name, self.class_c_ptrs_map[class_name]) | |
def get_typedef_data(self, typedef): | |
"""Get the data for typedef from the code context.""" | |
typedef_name = get_full_name(typedef) | |
if typedef_name not in self.typedefs_map: | |
self.add_typedef(typedef) | |
self.recursive_typedefs.append(typedef) | |
return (typedef_name, self.typedefs_map[typedef_name]) | |
def get_enum_data(self, enum): | |
"""Get the data for enum from the code context.""" | |
enum_name = get_full_name(enum) | |
if enum_name not in self.enums_map: | |
self.add_enum(enum) | |
self.recursive_enums.append(enum) | |
return (enum_name, self.enums_map[enum_name]) | |
def add_std_string(self, string_typedef): | |
"""Add std::(w)string to the code context.""" | |
string_class = get_class_by_decl(self.global_ns, string_typedef.decl_type.declaration, False) | |
string_name = string_typedef.name | |
is_wstring = ("wstring" in string_name) | |
if is_wstring: | |
self.wstring_ctor_name = get_public_default_ctor(string_class).name | |
self.std_wstring_c_name = get_c_name(string_class) | |
self.std_wstring_typedef_c_name = get_c_name(string_typedef) | |
self.wstring_name = string_name | |
self.wstring_full_name = get_full_name(string_class) | |
self.wstring_full_typedef_name = get_full_name(string_typedef) | |
string_c_ptr_name=get_class_ptr_name(self.std_wstring_typedef_c_name) # We replace the ptr of the class with that of the typedef - more readable. | |
else: | |
self.string_ctor_name = get_public_default_ctor(string_class).name | |
self.std_string_c_name = get_c_name(string_class) | |
self.std_string_typedef_c_name = get_c_name(string_typedef) | |
self.string_name = string_name | |
self.string_full_name = get_full_name(string_class) | |
self.string_full_typedef_name = get_full_name(string_typedef) | |
string_c_ptr_name=get_class_ptr_name(self.std_string_typedef_c_name) # We replace the ptr of the class with that of the typedef - more readable. | |
self.add_typedef(string_typedef) | |
return (is_wstring, string_class, string_c_ptr_name) | |
def gen_recursive_elems(self): | |
"""Generates the elements gathered recursively that needs to be outputed.""" | |
while self.recursive_classes or self.recursive_typedefs or self.recursive_enums: | |
# Not a for loop - since classes may get into self.recursive_classes during this loop (e.g.: std::string::allocator inside std::string methods). The same is true for typedefs. | |
if self.recursive_enums: | |
yield (self.ENUM, self.recursive_enums.pop(0)) | |
elif self.recursive_typedefs: | |
yield (self.TYPEDEF, self.recursive_typedefs.pop(0)) | |
elif self.recursive_classes: | |
yield (self.CLASS, self.recursive_classes.pop(0)) | |
def gen_sub_seq(self): | |
"""Generates a substitution sequence for C++ tokens.""" | |
if self.std_wstring_c_name is not None: | |
yield (self.wstring_full_name, self.wstring_full_typedef_name) # Make the std::wstring declerations more readable. | |
if self.std_string_c_name is not None: | |
yield (self.string_full_name, self.string_full_typedef_name) # Make the std::wstring declerations more readable. | |
def gen_c_sub_seq(self): | |
"""Generates a substitution sequence for C tokens.""" | |
if self.std_wstring_c_name is not None: | |
yield (self.std_wstring_c_name, self.std_wstring_typedef_c_name) # Make the std::wstring declerations more readable. | |
yield (self.wstring_ctor_name, self.wstring_name) # Replace basic_string() with wstring(). | |
if self.std_string_c_name is not None: | |
yield (self.std_string_c_name, self.std_string_typedef_c_name) # Make the std::string declerations more readable. | |
yield (self.string_ctor_name, self.string_name) # Replace basic_string() with string(). | |
def get_full_name(self, decl): | |
""" Returns decl full name in the current code context.""" | |
return get_full_name(decl, self.gen_sub_seq()) | |
def get_c_name(self, decl): | |
""" Returns decl C-name in the current code context.""" | |
return get_c_name(decl, True, self.gen_c_sub_seq()) | |
class ArgInfo(object): | |
"""A class to parse a func argument.""" | |
def __init__(self, arg_type, global_context, arg_name=""): | |
self.class_name = None | |
self.is_const = False | |
self.is_ref = False | |
self.is_class = False | |
self.arg_type_c_str = None | |
self.is_ptr = False | |
self.is_enum = False | |
self.is_typedef = False | |
self.arg_type = arg_type | |
self.arg_name = arg_name | |
self.cast_str = "" | |
self.is_func_ptr = False | |
ptrs_list=[] | |
if "std::_Aux_cont" in str(arg_type): | |
# Handle special case for MSVSC 2008, for example, when trying to instantiate vector<int>. | |
# TODO: Handle the special case in a generic way. | |
raise UnsupportedError("No support for std::_Aux_cont in: %s" % (str(arg_type),)) | |
curr_arg = arg_type | |
while not(is_fundamental(curr_arg)) or is_typedef(curr_arg) or is_declarated_type(curr_arg) or is_enum(curr_arg): | |
if is_abstract_class_declaration(curr_arg): | |
self.is_class = True | |
cls = get_class_by_decl(global_context.global_ns, curr_arg) | |
break | |
elif is_typedef(curr_arg): | |
self.is_typedef = True | |
typedef_name, typedef_c_name = global_context.get_typedef_data(curr_arg) | |
curr_arg = curr_arg.decl_type # Don't use remove_alias() since this deletes ALL the typedefs - and we don't want that. | |
#TODO: Is this good to classes, either? | |
elif is_declarated_type(curr_arg): | |
#curr_arg_decl_str = curr_arg.build_decl_string() | |
#if pygccxml.declarations.templates.is_instantiation(curr_arg_decl_str) and not(is_std_string(curr_arg) or is_std_wstring(curr_arg)): | |
# raise UnsupportedError("Currently templates are not supported.") | |
# TODO: Check STL contains, for example vector. | |
#self.is_typedef, typedef_name, typedef_c_name = global_context.get_possible_typedef_data(curr_arg) | |
curr_arg = curr_arg.declaration # And not remove_declarated(curr_arg) - since this would break a typedef completely, etc. | |
elif is_const(curr_arg): | |
self.is_const = True | |
curr_arg = remove_const(curr_arg) | |
elif is_ellipsis(curr_arg): | |
raise UnsupportedError("Ellipsis arg types are not handeled.") | |
elif is_qualifier(curr_arg): | |
curr_arg = base_type(curr_arg) # TODO: Is this correct? | |
elif is_unknown_type(curr_arg): | |
raise UnsupportedError("Unknown arg type: %s" % (str(curr_arg),)) | |
elif is_volatile(curr_arg): | |
curr_arg = remove_volatile(curr_arg) # Not being dealt. | |
elif is_calldef_pointer(curr_arg): | |
curr_arg = curr_arg.base # remove_pointer() doesn't work here. | |
if is_member_function_type(curr_arg): | |
raise UnsupportedError("Member function pointers are not supported: %s" % (str(arg_type),)) | |
self.is_func_ptr = True | |
self.func_ptr_info = FuncPtrInfo(curr_arg, arg_name, global_context) | |
break | |
elif is_pointer(curr_arg) or is_array(curr_arg): | |
ptrs_list.append(self.is_const) | |
self.is_const = False | |
curr_arg = remove_pointer(curr_arg) | |
elif is_reference(curr_arg): | |
self.is_ref = True # Only the 1st ref is handeled | |
curr_arg = remove_reference(curr_arg) | |
elif is_enum(curr_arg): | |
self.is_enum = True | |
break | |
elif is_class(curr_arg): | |
self.is_class = True | |
cls = curr_arg | |
break | |
self.is_c_bool = is_bool(curr_arg) and not global_context.is_c99 # This is redundent if the compiler is C99 compatible. | |
self.is_c_decl = not(self.is_class or self.is_ref or self.is_c_bool) #= A simple C decleration. | |
self.is_redirected = (self.is_class or self.is_ref) | |
if self.is_c_decl: | |
self.arg_type_c_str = strip_global_ns(arg_type.build_decl_string()) | |
if self.is_typedef: | |
stripped_typedef_name = strip_global_ns(typedef_name) | |
if typedef_c_name != stripped_typedef_name: # There is a namespace | |
self.cast_str = self.arg_type_c_str | |
self.arg_type_c_str = self.arg_type_c_str.replace(typedef_name, typedef_c_name) | |
# The following line is needed to handle the case where arg_type.build_decl_string() is a typedef -> we've stripped the global namespace, and the type won't be replaced with the last line. | |
self.arg_type_c_str = self.arg_type_c_str.replace(stripped_typedef_name, typedef_c_name) | |
elif self.is_enum: | |
self.cast_str = self.arg_type_c_str | |
enum_name, enum_c_name = global_context.get_enum_data(curr_arg) | |
if "enum " not in enum_c_name: # C++ style's enum | |
enum_c_name = "enum %s" % (enum_c_name,) | |
self.arg_type_c_str = self.arg_type_c_str.replace(strip_global_ns(get_full_name(curr_arg)), enum_c_name) | |
else: | |
if self.is_class: | |
if cls is not None: | |
self.can_create_with_copy_constructor = has_copy_constructor(cls) | |
self.can_create_with_default_constructor = (has_public_assign(cls) and has_trivial_constructor(cls)) | |
self.class_name, base_arg_type_c_str = global_context.get_class_data(cls) | |
else: # For class declaration with no concrete classes. | |
self.can_create_with_copy_constructor = None | |
self.can_create_with_default_constructor = None | |
self.class_name, base_arg_type_c_str = global_context.get_class_data(curr_arg) | |
if len(ptrs_list) > 0: | |
if ptrs_list.pop(): # The class ptr covers 1 redirection level... | |
base_arg_type_c_str = "const " + base_arg_type_c_str #...but we shouldn't forget the const correctness for the ptr we've just removed. | |
self.is_redirected = False | |
else: | |
base_arg_type_c_str = strip_global_ns(curr_arg.build_decl_string()) | |
if self.is_ref: # The (is_class and is_ref) case is dealt in the is_class case. | |
ptrs_list.append(True) # We add a const indirection. Notice that self.is_const is about the contents - not about the ptr. | |
curr_ptrs_list = [] | |
for is_ptr_const in reversed(ptrs_list): # reversed - since ptrs are read from right-to-left. | |
if is_ptr_const: | |
curr_ptrs_list.append('* const') | |
else: | |
curr_ptrs_list.append('*') | |
ptrs_str = ''.join(curr_ptrs_list) | |
const_content_str = "" | |
if self.is_const: | |
const_content_str = "const " | |
self.arg_type_c_str = "%s%s%s" % (const_content_str, base_arg_type_c_str, ptrs_str) | |
if self.is_class: | |
self.cast_str = self.arg_type_c_str.replace(global_context.get_class_data(curr_arg)[1], self.class_name+'*') | |
if self.is_c_bool: | |
self.cast_str = self.arg_type_c_str | |
self.arg_type_c_str = self.arg_type_c_str.replace("bool", C_BOOL_TYPE_NAME) | |
if self.is_func_ptr and not(self.is_typedef): # TODO: Is this position covers all bases? | |
self.arg_type_c_str = self.func_ptr_info.type_str | |
if len(ptrs_list) > 0: | |
self.is_ptr = True | |
def is_void(self): | |
"""Returns whether the argument is void.""" | |
return is_void(self.arg_type) | |
def get_type_name_str(self): | |
"""Returns the type and name argument string.""" | |
if self.is_func_ptr and not(self.is_typedef): # TODO: What about if the typedef is stripped down? | |
return self.arg_type_c_str.replace("(*)", "(*%s)" % (self.arg_name,)) | |
return "%s %s" % (self.arg_type_c_str, self.arg_name) | |
class FuncPtrInfo: | |
"""A class to parse a function pointer.""" | |
def __init__(self, func_type, name, global_context): | |
self.func_args_info_list = [] | |
self.func_type = func_type | |
self.name = name | |
for arg in func_type.arguments_types: | |
self.func_args_info_list.append(ArgInfo(arg, global_context)) | |
self.ret_type_info = ArgInfo(func_type.return_type, global_context) | |
func_args_decl_list = [arg_info.arg_type_c_str for arg_info in self.func_args_info_list] | |
func_args_str = ", ".join(func_args_decl_list) | |
self.type_str = "%s (*%s)(%s)" % (self.ret_type_info.arg_type_c_str, name, func_args_str) | |
class FuncInfo: | |
"""A class to parse a function or a class method.""" | |
FREE_FUNC = 0 | |
FREE_OP = 1 | |
MEMBER_FUNC = 2 | |
MEMBER_OP = 3 | |
CTOR = 4 | |
DTOR = 5 | |
FUNC_TYPE_MAP = {free_function_t: FREE_FUNC, | |
free_operator_t: FREE_OP, | |
member_function_t: MEMBER_FUNC, | |
member_operator_t: MEMBER_OP, | |
constructor_t: CTOR, | |
destructor_t: DTOR} | |
def __init__(self, func, global_context, generate_error_arg, generate_min_args_ver_only=False): | |
if func.has_ellipsis: | |
raise UnsupportedError("Ellipsis arg types are not handeled.") | |
self.func_args_info_list = [] | |
self.func = func | |
self.full_name = global_context.get_full_name(func) | |
self.c_func_name = global_context.get_c_name(func) | |
self.func_type = self.FUNC_TYPE_MAP[type(func)] | |
if (self.func_type == self.MEMBER_OP) or (self.func_type == self.FREE_OP): | |
if not("operator_" in self.c_func_name): | |
# Fix missing space in the func name ("operator+" -> "operator_+"). | |
self.c_func_name = self.c_func_name.replace("operator", "operator_") | |
for op_name in OPERATORS_LIST_BY_LENGTH: | |
self.c_func_name = self.c_func_name.replace(op_name, OPERATOR_MAP[op_name]) | |
self.generate_error_arg = generate_error_arg | |
self.is_c99 = global_context.is_c99 | |
if generate_min_args_ver_only: | |
self.optional_args_num = 0 | |
else: | |
self.optional_args_num = len(func.optional_args) # There are always len(func.optional_args) optional arguments, but we don't want to use them when we generate_min_args_ver_only. | |
if generate_min_args_ver_only: | |
arguments = func.required_args | |
else: | |
arguments = func.arguments | |
for arg in arguments: | |
self.func_args_info_list.append(ArgInfo(arg.decl_type, global_context, arg.name)) | |
self.class_redirection = "" | |
if (self.func_type == self.MEMBER_FUNC) or (self.func_type == self.DTOR) or (self.func_type == self.MEMBER_OP): | |
self.class_name, class_c_ptr = global_context.get_class_data(func.parent) | |
self.class_arg_c_str = "%s %s" % (class_c_ptr, THIS_VAR_NAME) | |
if (self.func_type == self.MEMBER_FUNC) or (self.func_type == self.MEMBER_OP): | |
self.is_static = func.has_static | |
if self.is_static: | |
self.c_func_name += "_static" | |
self.class_redirection = "" | |
const_class_redirection_str = "" | |
if func.has_const: | |
self.class_arg_c_str = "const " + self.class_arg_c_str | |
self.c_func_name += "_const" | |
const_class_redirection_str = "const " | |
if not self.is_static: | |
self.class_redirection = "((%s%s*) %s)->" % (const_class_redirection_str, self.class_name, THIS_VAR_NAME,) | |
if self.func_type == self.CTOR: | |
self.ret_type_info = ArgInfo(func.parent, global_context) | |
# TODO: The ctor returns a ptr to the class. Here we put the class_t and not pointer_t(class_t) - since it messes-up the ArgInfo's ctor. | |
# The reason it works is that handling class and class-ptr is the same - but depending on it is very bad. | |
elif (self.func_type == self.DTOR): | |
self.ret_type_info = ArgInfo(void_t(), global_context) | |
else: | |
self.ret_type_info = ArgInfo(func.return_type, global_context) | |
def gen_func_args_c_strs(self): | |
"""A generator to the func args types and names.""" | |
if self.generate_error_arg: | |
yield get_error_arg_str(self.is_c99) | |
if (self.func_type == self.DTOR) or (self.func_type == self.MEMBER_OP) or ((self.func_type == self.MEMBER_FUNC) and not self.is_static): | |
yield self.class_arg_c_str | |
for arg_info in self.func_args_info_list: | |
yield arg_info.get_type_name_str() | |
def is_default_ctor(self): | |
"""Returns whether the func is a default ctor.""" | |
if self.func_type == self.CTOR: | |
return is_trivial_constructor(self.func) | |
return False | |
def unsupported_wrapper(func, ignore_unsupported_features=True): | |
"""A decorator to ignore unsupported features exception, if enabled.""" | |
def decorated(*args, **kws): | |
try: | |
func(*args, **kws) | |
except UnsupportedError, ex: | |
if ignore_unsupported_features: | |
print ex.args[0] | |
else: | |
raise | |
return decorated | |
def generate_c_wrapper(header_file_path, generate_dl=True, generate_exception_handling_code=True, is_compact_string=True, is_assume_copy=False, is_assume_assign=False, generate_error_arg=True, is_verbose=False, generate_operators=True, ignore_unsupported_features=True, is_c99=False, is_camel_case=False, gccxml_file_path = '', compiler_type = None): | |
"""Outputs a C wrapper for header_file_path.""" | |
if generate_error_arg and not(generate_exception_handling_code): | |
print "Ignoring error argument generation option, since the exception handling generation option is disabled" | |
generate_error_arg = False | |
header_file_path=os.path.abspath(header_file_path) | |
output_class = CodeOutputClass(header_file_path, generate_exception_handling_code, generate_dl, is_compact_string, generate_operators, is_assume_copy, is_assume_assign, generate_error_arg, is_verbose, is_camel_case) | |
global_context = CodeContext(header_file_path, gccxml_file_path, compiler_type, is_c99) | |
output_class.output_prefix_code(global_context.is_c99) | |
output_class.output_typedef=unsupported_wrapper(output_class.output_typedef, ignore_unsupported_features) | |
output_class.output_func=unsupported_wrapper(output_class.output_func, ignore_unsupported_features) | |
try: | |
string_typedef = global_context.typedef(name="::std::string") | |
output_class.output_std_string(string_typedef, global_context) | |
string_typedef = global_context.typedef(name="::std::wstring") | |
output_class.output_std_string(string_typedef, global_context) | |
except pygccxml.declarations.runtime_errors.declaration_not_found_t: | |
pass | |
for cls in global_context.classes(header_file=header_file_path, allow_empty=True): | |
output_class.output_class_typedef(cls, global_context) | |
for typedef in global_context.typedefs(header_file=header_file_path, allow_empty=True): | |
output_class.output_typedef(typedef, global_context) | |
for enum in global_context.enumerations(header_file=header_file_path, allow_empty=True): | |
output_class.output_enum(enum, global_context) | |
for cls in global_context.classes(header_file=header_file_path, allow_empty=True): | |
output_class.output_class_code(cls, global_context) | |
for free_func in global_context.free_functions(header_file=header_file_path, allow_empty=True): | |
output_class.output_func(free_func, global_context) | |
if output_class.generate_operators: | |
for free_op in global_context.free_operators(header_file=header_file_path, allow_empty=True): | |
output_class.output_func(free_op, global_context) | |
for elem_type, elem in global_context.gen_recursive_elems(): | |
if elem_type == global_context.CLASS: | |
output_class.output_class_typedef(elem, global_context) | |
output_class.output_class_code(elem, global_context) | |
elif elem_type == global_context.TYPEDEF: | |
output_class.output_typedef(elem, global_context) | |
elif elem_type == global_context.ENUM: | |
output_class.output_enum(elem, global_context) | |
output_class.output_suffix_code() | |
output_class.close() # Forces the output file creation. | |
def safe_config_get_wrapper(config_get_func): | |
"""A decorator to mask ConfigParser methods from option not existing exception, returning a default value instead.""" | |
def decorated(section_name, key_name, def_val=None): | |
try: | |
return config_get_func(section_name, key_name) | |
except ConfigParser.NoOptionError: | |
return def_val | |
return decorated | |
def get_option(options, option, def_val): | |
"""Returns a default value (other than None) when an optparse var option does not exist.""" | |
ret_val = getattr(options, option) | |
if ret_val is None: | |
return def_val | |
return ret_val | |
def parse_option(config_get_func, section_name, option_name, options, def_val): | |
"""Parses one program option. The args parsing order is: | |
1. Command line args. | |
2. Config file options. | |
3. Default values.""" | |
option = def_val | |
if config_get_func is not None: | |
option = config_get_func(section_name, option_name, def_val) | |
return get_option(options, option_name, option) | |
if __name__ == '__main__': | |
cmd_line_parser = OptionParser("usage: %prog [options] <header_file_path>") | |
cmd_line_parser.add_option("-g", "--gccxml", dest='gccxml_file_path', help="The gccxml file path") | |
cmd_line_parser.add_option("-c", "--config", dest='config_file_path', help="The config file path") | |
cmd_line_parser.add_option("-t", "--compiler", dest='compiler_type', help="The compiler type") | |
cmd_line_parser.add_option("-i", "--ignore", action='store_false', dest='ignore_unsupported_features', help="Raise exception if an unsupported feature (like templates) is encountered.") | |
cmd_line_parser.add_option("-d", "--dl", action='store_false', dest='generate_dl', help="Don't generate a def file (and a DllMain() function under Windows).") | |
cmd_line_parser.add_option("-e", "--error", action='store_false', dest='generate_error_arg', help="Don't add error output args.") | |
cmd_line_parser.add_option("-n", "--nothrow", action='store_false', dest='generate_exception_handling_code', help="Don't generate exception handling code.") | |
cmd_line_parser.add_option("-v", "--verbose", action='store_false', dest='is_verbose', help="Don'e generate verbose output.") | |
cmd_line_parser.add_option("-9", "--c99", action='store_true', dest='is_c99', help="Compiler with C99 support.") | |
cmd_line_parser.add_option("-o", "--operator", action='store_false', dest='generate_operators', help="Don't generate operators.") | |
cmd_line_parser.add_option("-s", "--string", action='store_false', dest='is_compact_string', help="Output std::string in a compact format.") | |
cmd_line_parser.add_option("--camel", action='store_true', dest='is_camel_case', help="The functions would be outputed in (Upper) Camel Case conventions, not Python conventions (e.g.: FuncName and not func_name).") | |
cmd_line_parser.add_option("--copy", action='store_true', dest='is_assume_copy', help="Assume public copy constructor for class declarations with no concrete classes.") | |
cmd_line_parser.add_option("--assign", action='store_true', dest='is_assume_assign', help="Assume public default constructor and assignment operator for class delarations with no concrete classes.") | |
(options, args) = cmd_line_parser.parse_args() | |
if len(args) != 1: | |
cmd_line_parser.error("<header_file_path> is required") | |
safe_config_get = None | |
safe_config_get_bool = None | |
if options.config_file_path is not None: | |
config = ConfigParser.RawConfigParser() | |
config.read(options.config_file_path) | |
safe_config_get = safe_config_get_wrapper(config.get) | |
safe_config_get_bool = safe_config_get_wrapper(config.getboolean) | |
generate_dl = parse_option(safe_config_get_bool, 'Cpp2C Config', 'generate_dl', options, True) | |
generate_error_arg = parse_option(safe_config_get_bool, 'Cpp2C Config', 'generate_error_arg', options, True) | |
ignore_unsupported_features = parse_option(safe_config_get_bool, 'Cpp2C Config', 'ignore_unsupported_features', options, True) | |
generate_exception_handling_code = parse_option(safe_config_get_bool, 'Cpp2C Config', 'generate_exception_handling_code', options, True) | |
gccxml_file_path = parse_option(safe_config_get, 'GccXml Config', 'gccxml_file_path', options, "") | |
compiler_type = parse_option(safe_config_get, 'GccXml Config', 'compiler_type', options, None) | |
is_verbose = parse_option(safe_config_get_bool, 'Cpp2C Config', 'is_verbose', options, True) | |
is_c99 = parse_option(safe_config_get_bool, 'Cpp2C Config', 'is_c99', options, False) | |
generate_operators = parse_option(safe_config_get_bool, 'Cpp2C Config', 'generate_operators', options, True) | |
is_compact_string = parse_option(safe_config_get_bool, 'Cpp2C Config', 'is_compact_string', options, True) | |
is_camel_case = parse_option(safe_config_get_bool, 'Cpp2C Config', 'is_camel_case', options, False) | |
is_assume_copy = parse_option(safe_config_get_bool, 'Cpp2C Config', 'is_assume_copy', options, False) | |
is_assume_assign = parse_option(safe_config_get_bool, 'Cpp2C Config', 'is_assume_assign', options, False) | |
generate_c_wrapper(args[0], generate_dl, generate_exception_handling_code, is_compact_string, is_assume_copy, is_assume_assign, generate_error_arg, is_verbose, generate_operators, ignore_unsupported_features, is_c99, is_camel_case, gccxml_file_path, compiler_type) | |
print "Done." |
This is very old tool which is incomplete yet.
And I'm not an original author.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
from where i can get the header file?? please help its urgent...