| 
									
										
										
										
											2011-04-07 07:53:28 +00:00
										 |  |  | # ##### BEGIN GPL LICENSE BLOCK ##### | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #  This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  | #  modify it under the terms of the GNU General Public License | 
					
						
							|  |  |  | #  as published by the Free Software Foundation; either version 2 | 
					
						
							|  |  |  | #  of the License, or (at your option) any later version. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #  This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | #  GNU General Public License for more details. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #  You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  | #  along with this program; if not, write to the Free Software Foundation, | 
					
						
							|  |  |  | #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # ##### END GPL LICENSE BLOCK ##### | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # <pep8 compliant> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | Dump the python API into a text file so we can generate changelogs. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | output from this tool should be added into "doc/python_api/rst/change_log.rst" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # dump api blender_version.py in CWD | 
					
						
							| 
									
										
										
										
											2011-06-22 12:05:24 +00:00
										 |  |  | blender --background --python doc/python_api/sphinx_changelog_gen.py -- --dump | 
					
						
							| 
									
										
										
										
											2011-04-07 07:53:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # create changelog | 
					
						
							| 
									
										
										
										
											2011-06-22 12:05:24 +00:00
										 |  |  | blender --background --python doc/python_api/sphinx_changelog_gen.py -- \ | 
					
						
							| 
									
										
										
										
											2011-04-07 07:53:28 +00:00
										 |  |  |         --api_from blender_2_56_1.py \ | 
					
						
							|  |  |  |         --api_to blender_2_57_0.py \ | 
					
						
							|  |  |  |         --api_out changes.rst | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Api comparison can also run without blender | 
					
						
							| 
									
										
										
										
											2011-06-22 12:05:24 +00:00
										 |  |  | python doc/python_api/sphinx_changelog_gen.py \ | 
					
						
							| 
									
										
										
										
											2011-04-07 07:53:28 +00:00
										 |  |  |         --api_from blender_api_2_56_6.py \ | 
					
						
							|  |  |  |         --api_to blender_api_2_57.py \ | 
					
						
							|  |  |  |         --api_out changes.rst | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # format | 
					
						
							|  |  |  | '''
 | 
					
						
							|  |  |  | {"module.name": | 
					
						
							|  |  |  |     {"parent.class": | 
					
						
							|  |  |  |         {"basic_type", "member_name": ("Name", type, range, length, default, descr, f_args, f_arg_types, f_ret_types)}, ... | 
					
						
							|  |  |  |     }, ... | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | '''
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | api_names = "basic_type" "name", "type", "range", "length", "default", "descr", "f_args", "f_arg_types", "f_ret_types" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | API_BASIC_TYPE = 0 | 
					
						
							|  |  |  | API_F_ARGS = 7 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def api_dunp_fname(): | 
					
						
							|  |  |  |     import bpy | 
					
						
							|  |  |  |     return "blender_api_%s.py" % "_".join([str(i) for i in bpy.app.version]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def api_dump(): | 
					
						
							|  |  |  |     dump = {} | 
					
						
							|  |  |  |     dump_module = dump["bpy.types"] = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     import rna_info | 
					
						
							|  |  |  |     import inspect | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     struct = rna_info.BuildRNAInfo()[0] | 
					
						
							|  |  |  |     for struct_id, strict_info in sorted(struct.items()): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         struct_id_str = strict_info.identifier | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if rna_info.rna_id_ignore(struct_id_str): | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for base in strict_info.get_bases(): | 
					
						
							|  |  |  |             struct_id_str = base.identifier + "." + struct_id_str | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         dump_class = dump_module[struct_id_str] = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         props = [(prop.identifier, prop) for prop in strict_info.properties] | 
					
						
							|  |  |  |         for prop_id, prop in sorted(props): | 
					
						
							|  |  |  |             # if prop.type == 'boolean': | 
					
						
							|  |  |  |             #     continue | 
					
						
							|  |  |  |             prop_type = prop.type | 
					
						
							|  |  |  |             prop_length = prop.array_length | 
					
						
							|  |  |  |             prop_range = round(prop.min, 4), round(prop.max, 4) | 
					
						
							|  |  |  |             prop_default = prop.default | 
					
						
							|  |  |  |             if type(prop_default) is float: | 
					
						
							|  |  |  |                 prop_default = round(prop_default, 4) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if prop_range[0] == -1 and prop_range[1] == -1: | 
					
						
							|  |  |  |                 prop_range = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             dump_class[prop_id] = ( | 
					
						
							|  |  |  |                     "prop_rna",                 # basic_type | 
					
						
							|  |  |  |                     prop.name,                  # name | 
					
						
							|  |  |  |                     prop_type,                  # type | 
					
						
							|  |  |  |                     prop_range,                 # range | 
					
						
							|  |  |  |                     prop_length,                # length | 
					
						
							|  |  |  |                     prop.default,               # default | 
					
						
							|  |  |  |                     prop.description,           # descr | 
					
						
							|  |  |  |                     Ellipsis,                   # f_args | 
					
						
							|  |  |  |                     Ellipsis,                   # f_arg_types | 
					
						
							|  |  |  |                     Ellipsis,                   # f_ret_types | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |         del props | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # python props, tricky since we dont know much about them. | 
					
						
							|  |  |  |         for prop_id, attr in strict_info.get_py_properties(): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             dump_class[prop_id] = ( | 
					
						
							|  |  |  |                     "prop_py",                  # basic_type | 
					
						
							|  |  |  |                     Ellipsis,                   # name | 
					
						
							|  |  |  |                     Ellipsis,                   # type | 
					
						
							|  |  |  |                     Ellipsis,                   # range | 
					
						
							|  |  |  |                     Ellipsis,                   # length | 
					
						
							|  |  |  |                     Ellipsis,                   # default | 
					
						
							|  |  |  |                     attr.__doc__,               # descr | 
					
						
							|  |  |  |                     Ellipsis,                   # f_args | 
					
						
							|  |  |  |                     Ellipsis,                   # f_arg_types | 
					
						
							|  |  |  |                     Ellipsis,                   # f_ret_types | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # kludge func -> props | 
					
						
							|  |  |  |         funcs = [(func.identifier, func) for func in strict_info.functions] | 
					
						
							|  |  |  |         for func_id, func in funcs: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             func_ret_types = tuple([prop.type for prop in func.return_values]) | 
					
						
							|  |  |  |             func_args_ids = tuple([prop.identifier for prop in func.args]) | 
					
						
							|  |  |  |             func_args_type = tuple([prop.type for prop in func.args]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             dump_class[func_id] = ( | 
					
						
							|  |  |  |                     "func_rna",                 # basic_type | 
					
						
							|  |  |  |                     Ellipsis,                   # name | 
					
						
							|  |  |  |                     Ellipsis,                   # type | 
					
						
							|  |  |  |                     Ellipsis,                   # range | 
					
						
							|  |  |  |                     Ellipsis,                   # length | 
					
						
							|  |  |  |                     Ellipsis,                   # default | 
					
						
							|  |  |  |                     func.description,           # descr | 
					
						
							|  |  |  |                     func_args_ids,              # f_args | 
					
						
							|  |  |  |                     func_args_type,             # f_arg_types | 
					
						
							|  |  |  |                     func_ret_types,             # f_ret_types | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |         del funcs | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # kludge func -> props | 
					
						
							|  |  |  |         funcs = strict_info.get_py_functions() | 
					
						
							|  |  |  |         for func_id, attr in funcs: | 
					
						
							|  |  |  |             # arg_str = inspect.formatargspec(*inspect.getargspec(py_func)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             func_args_ids = tuple(inspect.getargspec(attr).args) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             dump_class[func_id] = ( | 
					
						
							|  |  |  |                     "func_py",                  # basic_type | 
					
						
							|  |  |  |                     Ellipsis,                   # name | 
					
						
							|  |  |  |                     Ellipsis,                   # type | 
					
						
							|  |  |  |                     Ellipsis,                   # range | 
					
						
							|  |  |  |                     Ellipsis,                   # length | 
					
						
							|  |  |  |                     Ellipsis,                   # default | 
					
						
							|  |  |  |                     attr.__doc__,               # descr | 
					
						
							|  |  |  |                     func_args_ids,              # f_args | 
					
						
							|  |  |  |                     Ellipsis,                   # f_arg_types | 
					
						
							|  |  |  |                     Ellipsis,                   # f_ret_types | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |         del funcs | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     import pprint | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     filename = api_dunp_fname() | 
					
						
							|  |  |  |     filehandle = open(filename, 'w') | 
					
						
							|  |  |  |     tot = filehandle.write(pprint.pformat(dump, width=1)) | 
					
						
							|  |  |  |     filehandle.close() | 
					
						
							|  |  |  |     print("%s, %d bytes written" % (filename, tot)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def compare_props(a, b, fuzz=0.75): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # must be same basic_type, function != property | 
					
						
							|  |  |  |     if a[0] != b[0]: | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tot = 0 | 
					
						
							|  |  |  |     totlen = 0 | 
					
						
							|  |  |  |     for i in range(1, len(a)): | 
					
						
							|  |  |  |         if not (Ellipsis is a[i] is b[i]): | 
					
						
							|  |  |  |             tot += (a[i] == b[i]) | 
					
						
							|  |  |  |             totlen += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ((tot / totlen) >= fuzz) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def api_changelog(api_from, api_to, api_out): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     file_handle = open(api_from, 'r') | 
					
						
							|  |  |  |     dict_from = eval(file_handle.read()) | 
					
						
							|  |  |  |     file_handle.close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     file_handle = open(api_to, 'r') | 
					
						
							|  |  |  |     dict_to = eval(file_handle.read()) | 
					
						
							|  |  |  |     file_handle.close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     api_changes = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # first work out what moved | 
					
						
							|  |  |  |     for mod_id, mod_data in dict_to.items(): | 
					
						
							|  |  |  |         mod_data_other = dict_from[mod_id] | 
					
						
							|  |  |  |         for class_id, class_data in mod_data.items(): | 
					
						
							|  |  |  |             class_data_other = mod_data_other.get(class_id) | 
					
						
							|  |  |  |             if class_data_other is None: | 
					
						
							|  |  |  |                 # TODO, document new structs | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # find the props which are not in either | 
					
						
							|  |  |  |             set_props_new = set(class_data.keys()) | 
					
						
							|  |  |  |             set_props_other = set(class_data_other.keys()) | 
					
						
							|  |  |  |             set_props_shared = set_props_new & set_props_other | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             props_moved = [] | 
					
						
							|  |  |  |             props_new = [] | 
					
						
							|  |  |  |             props_old = [] | 
					
						
							|  |  |  |             func_args = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             set_props_old = set_props_other - set_props_shared | 
					
						
							|  |  |  |             set_props_new = set_props_new - set_props_shared | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # first find settings which have been moved old -> new | 
					
						
							|  |  |  |             for prop_id_old in set_props_old.copy(): | 
					
						
							|  |  |  |                 prop_data_other = class_data_other[prop_id_old] | 
					
						
							|  |  |  |                 for prop_id_new in set_props_new.copy(): | 
					
						
							|  |  |  |                     prop_data = class_data[prop_id_new] | 
					
						
							|  |  |  |                     if compare_props(prop_data_other, prop_data): | 
					
						
							|  |  |  |                         props_moved.append((prop_id_old, prop_id_new)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         # remove | 
					
						
							|  |  |  |                         if prop_id_old in set_props_old: | 
					
						
							|  |  |  |                             set_props_old.remove(prop_id_old) | 
					
						
							|  |  |  |                         set_props_new.remove(prop_id_new) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # func args | 
					
						
							|  |  |  |             for prop_id in set_props_shared: | 
					
						
							|  |  |  |                 prop_data = class_data[prop_id] | 
					
						
							|  |  |  |                 prop_data_other = class_data_other[prop_id] | 
					
						
							|  |  |  |                 if prop_data[API_BASIC_TYPE] == prop_data_other[API_BASIC_TYPE]: | 
					
						
							|  |  |  |                     if prop_data[API_BASIC_TYPE].startswith("func"): | 
					
						
							|  |  |  |                         args_new = prop_data[API_F_ARGS] | 
					
						
							|  |  |  |                         args_old = prop_data_other[API_F_ARGS] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         if args_new != args_old: | 
					
						
							|  |  |  |                             func_args.append((prop_id, args_old, args_new)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if props_moved or set_props_new or set_props_old or func_args: | 
					
						
							|  |  |  |                 props_moved.sort() | 
					
						
							|  |  |  |                 props_new[:] = sorted(set_props_new) | 
					
						
							|  |  |  |                 props_old[:] = sorted(set_props_old) | 
					
						
							|  |  |  |                 func_args.sort() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 api_changes.append((mod_id, class_id, props_moved, props_new, props_old, func_args)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # also document function argument changes | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fout = open(api_out, 'w') | 
					
						
							|  |  |  |     fw = fout.write | 
					
						
							|  |  |  |     # print(api_changes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # :class:`bpy_struct.id_data` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def write_title(title, title_char): | 
					
						
							|  |  |  |         fw("%s\n%s\n\n" % (title, title_char * len(title))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for mod_id, class_id, props_moved, props_new, props_old, func_args in api_changes: | 
					
						
							|  |  |  |         class_name = class_id.split(".")[-1] | 
					
						
							|  |  |  |         title = mod_id + "." + class_name | 
					
						
							|  |  |  |         write_title(title, "-") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if props_new: | 
					
						
							|  |  |  |             write_title("Added", "^") | 
					
						
							|  |  |  |             for prop_id in props_new: | 
					
						
							|  |  |  |                 fw("* :class:`%s.%s.%s`\n" % (mod_id, class_name, prop_id)) | 
					
						
							|  |  |  |             fw("\n") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if props_old: | 
					
						
							|  |  |  |             write_title("Removed", "^") | 
					
						
							|  |  |  |             for prop_id in props_old: | 
					
						
							|  |  |  |                 fw("* **%s**\n" % prop_id)  # cant link to remvoed docs | 
					
						
							|  |  |  |             fw("\n") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if props_moved: | 
					
						
							|  |  |  |             write_title("Renamed", "^") | 
					
						
							|  |  |  |             for prop_id_old, prop_id in props_moved: | 
					
						
							|  |  |  |                 fw("* **%s** -> :class:`%s.%s.%s`\n" % (prop_id_old, mod_id, class_name, prop_id)) | 
					
						
							|  |  |  |             fw("\n") | 
					
						
							| 
									
										
										
										
											2011-04-10 10:45:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-04-07 07:53:28 +00:00
										 |  |  |         if func_args: | 
					
						
							|  |  |  |             write_title("Function Arguments", "^") | 
					
						
							|  |  |  |             for func_id, args_old, args_new in func_args: | 
					
						
							|  |  |  |                 args_new = ", ".join(args_new) | 
					
						
							|  |  |  |                 args_old = ", ".join(args_old) | 
					
						
							|  |  |  |                 fw("* :class:`%s.%s.%s` (%s), *was (%s)*\n" % (mod_id, class_name, prop_id, args_new, args_old)) | 
					
						
							|  |  |  |             fw("\n") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fout.close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def main(): | 
					
						
							|  |  |  |     import sys | 
					
						
							|  |  |  |     import os | 
					
						
							| 
									
										
										
										
											2011-04-10 10:45:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-04-07 07:53:28 +00:00
										 |  |  |     try: | 
					
						
							|  |  |  |         import argparse | 
					
						
							|  |  |  |     except: | 
					
						
							|  |  |  |         print("Old Blender, just dumping") | 
					
						
							|  |  |  |         api_dump() | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     argv = sys.argv | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if "--" not in argv: | 
					
						
							|  |  |  |         argv = []  # as if no args are passed | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         argv = argv[argv.index("--") + 1:]  # get all args after "--" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # When --help or no args are given, print this help | 
					
						
							|  |  |  |     usage_text = "Run blender in background mode with this script: " | 
					
						
							|  |  |  |     "blender --background --python %s -- [options]" % os.path.basename(__file__) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     epilog = "Run this before releases" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     parser = argparse.ArgumentParser(description=usage_text, epilog=epilog) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     parser.add_argument("--dump", dest="dump", action='store_true', | 
					
						
							|  |  |  |             help="When set the api will be dumped into blender_version.py") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     parser.add_argument("--api_from", dest="api_from", metavar='FILE', | 
					
						
							|  |  |  |             help="File to compare from (previous version)") | 
					
						
							|  |  |  |     parser.add_argument("--api_to", dest="api_to", metavar='FILE', | 
					
						
							|  |  |  |             help="File to compare from (current)") | 
					
						
							|  |  |  |     parser.add_argument("--api_out", dest="api_out", metavar='FILE', | 
					
						
							|  |  |  |             help="Output sphinx changelog") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     args = parser.parse_args(argv)  # In this example we wont use the args | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if not argv: | 
					
						
							|  |  |  |         parser.print_help() | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if args.dump: | 
					
						
							|  |  |  |         api_dump() | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         if args.api_from and args.api_to and args.api_out: | 
					
						
							|  |  |  |             api_changelog(args.api_from, args.api_to, args.api_out) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             print("Error: --api_from/api_to/api_out args needed") | 
					
						
							|  |  |  |             parser.print_help() | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     print("batch job finished, exiting") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     main() |