From: Jason Ish Date: Wed, 26 Apr 2017 22:24:06 +0000 (-0600) Subject: rust: c header generator X-Git-Tag: suricata-4.0.0-beta1~33 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d0880d75ffff3152f3c60b2bab7ad89d525b5814;p=thirdparty%2Fsuricata.git rust: c header generator --- diff --git a/rust/gen-c-headers.py b/rust/gen-c-headers.py new file mode 100755 index 0000000000..391c51ccdd --- /dev/null +++ b/rust/gen-c-headers.py @@ -0,0 +1,194 @@ +#! /usr/bin/env python2 + +# This script will scan Rust source files looking for extern "C" +# functions and generate C header files from them with a filename +# based on the Rust filename. +# +# Usage: From the top suricata source directory: +# +# ./rust/gen-c-headers.py +# + +from __future__ import print_function + +import sys +import os +import re +from io import StringIO + +template = """/* Copyright (C) 2017 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * 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 + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/* + * DO NOT EDIT. This file is automatically generated. + */ + +#ifndef __%(name)s__ +#define __%(name)s__ + +%(prototypes)s +#endif /* ! __%(name)s__ */ +""" + +# Map of Rust types to C types. +type_map = { + "i8": "int8_t", + "i32" :"int32_t", + + "u8": "uint8_t", + + "libc::c_void": "void", + + "libc::c_int": "int", + "c_int": "int", + "libc::int8_t": "int8_t", + + "libc::uint8_t": "uint8_t", + "libc::uint16_t": "uint16_t", + "libc::uint32_t": "uint32_t", + "libc::uint64_t": "uint64_t", + + "SuricataContext": "SuricataContext", + "core::Flow": "Flow", + "DNSState": "RSDNSState", + "DNSTransaction": "RSDNSTransaction", + "JsonT": "json_t", + "DetectEngineState": "DetectEngineState", + "core::DetectEngineState": "DetectEngineState", + "core::AppLayerDecoderEvents": "AppLayerDecoderEvents", + "CLuaState": "lua_State", +} + +def convert_type(rs_type): + m = re.match("^[^\s]+$", rs_type) + if m: + if rs_type in type_map: + return type_map[rs_type] + + m = re.match("^(.*)(\s[^\s]+)$", rs_type) + if m: + mod = m.group(1).strip() + rtype = m.group(2).strip() + if rtype in type_map: + if mod in [ + "*mut", + "*const", + "&mut", + "&'static mut", + ]: + return "%s *" % (type_map[rtype]) + elif mod in [ + "*mut *const"]: + return "%s **" % (type_map[rtype]) + else: + raise Exception("Unknown modifier '%s' in '%s'." % ( + mod, rs_type)) + else: + raise Exception("Unknown type: %s" % (rtype)) + + raise Exception("Failed to parse Rust type: %s" % (rs_type)) + +def make_output_filename(filename): + parts = filename.split(os.path.sep)[2:] + last = os.path.splitext(parts.pop())[0] + outpath = "../src/rust-%s-%s.h" % ( + "-".join(parts), last) + return outpath.replace("--", "-") + +def write_header(fileobj, filename): + filename = os.path.basename(filename).replace( + "-", "_").replace(".", "_").upper() + fileobj.write(file_header % {"name": filename}) + +def should_regen(input_filename, output_filename): + """Check if a file should be regenerated. If the output doesn't exist, + or the input is newer than the output return True. Otherwise + return False. + + """ + if not os.path.exists(output_filename): + return True + if os.stat(input_filename).st_mtime > os.stat(output_filename).st_mtime: + return True + return False + +def gen_headers(filename): + + output_filename = make_output_filename(filename) + + if not should_regen(filename, output_filename): + return + + buf = open(filename).read() + writer = StringIO() + + for fn in re.findall( + r"^pub extern \"C\" fn ([A_Za-z0-9_]+)\(([^{]+)?\)" + r"(\s+-> ([^{]+))?", + buf, + re.M | re.DOTALL): + + args = [] + + fnName = fn[0] + + for arg in fn[1].split(","): + if not arg: + continue + arg_name, rs_type = arg.split(":", 1) + arg_name = arg_name.strip() + rs_type = rs_type.strip() + c_type = convert_type(rs_type) + + if arg_name != "_": + args.append("%s %s" % (c_type, arg_name)) + else: + args.append(c_type) + + if not args: + args.append("void") + + retType = fn[3].strip() + if retType == "": + returns = "void" + else: + returns = convert_type(retType) + + writer.write(u"%s %s(%s);\n" % (returns, fnName, ", ".join(args))) + + if writer.tell() > 0: + print("Writing %s" % (output_filename)) + with open(output_filename, "w") as output: + output.write(template % { + "prototypes": writer.getvalue(), + "name": os.path.basename(output_filename).replace( + "-", "_").replace(".", "_").upper() + }) + +def main(): + + rust_top = os.path.dirname(sys.argv[0]) + os.chdir(rust_top) + + for dirpath, dirnames, filenames in os.walk("./src"): + for filename in filenames: + if filename.endswith(".rs"): + path = os.path.join(dirpath, filename) + gen_headers(path) + +if __name__ == "__main__": + sys.exit(main())