#!/usr/bin/python # This is 'comp', a tool to write HAL boilerplate # Copyright 2006 Jeff Epler # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import os, sys, tempfile, shutil, getopt, time BASE = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "..")) sys.path.insert(0, os.path.join(BASE, "lib", "python")) %% parser Hal: ignore: "//.*" ignore: "/[*](.|\n)*?[*]/" ignore: "[ \t\r\n]+" token END: ";;" token PARAMDIRECTION: "rw|r" token PINDIRECTION: "in|out|io" token TYPE: "float|bit|signed|unsigned|u32|s32" token NAME: "[a-zA-Z_][a-zA-Z0-9_]*" token FPNUMBER: "-?([0-9]*\.[0-9]+|[0-9]+\.?)([Ee][+-]?[0-9]+)?f?" token NUMBER: "-?[0-9]+|0x[0-9a-fA-F]+" token STRING: '"(\\.|[^\\"])*"' token TSTRING: '"""(\\.|\\\n|[^\\"]|"(?!"")|\n)*"""' rule File: Declaration* "$" {{ return True }} rule Declaration: "component" NAME OptString";" {{ comp(NAME, OptString); }} | "pin" PINDIRECTION TYPE NAME OptAssign OptString ";" {{ pin(NAME, TYPE, PINDIRECTION, OptString, OptAssign) }} | "param" PARAMDIRECTION TYPE NAME OptAssign OptString ";" {{ param(NAME, TYPE, PARAMDIRECTION, OptString, OptAssign) }} | "function" NAME OptFP OptString ";" {{ function(NAME, OptFP, OptString) }} | "option" NAME OptValue ";" {{ option(NAME, OptValue) }} | "see_also" String ";" {{ see_also(String) }} | "description" String ";" {{ description(String) }} rule String: TSTRING {{ return eval(TSTRING) }} | STRING {{ return eval(STRING) }} rule OptString: TSTRING {{ return eval(TSTRING) }} | STRING {{ return eval(STRING) }} | {{ return '' }} rule OptAssign: "=" Value {{ return Value; }} | {{ return None }} rule OptFP: "fp" {{ return 1 }} | "nofp" {{ return 0 }} | {{ return 1 }} rule Value: "yes" {{ return 1 }} | "no" {{ return 0 }} | "true" {{ return 1 }} | "false" {{ return 0 }} | "TRUE" {{ return 1 }} | "FALSE" {{ return 0 }} | NAME {{ return NAME }} | FPNUMBER {{ return float(FPNUMBER.rstrip("f")) }} | NUMBER {{ return int(NUMBER,0) }} rule OptValue: Value {{ return Value }} | {{ return 1 }} %% def parse(rule, text, filename=None): global P, S S = HalScanner(text, filename=filename) P = Hal(S) return runtime.wrap_error_reporter(P, rule) dirmap = {'r': 'HAL_RO', 'rw': 'HAL_RW', 'in': 'HAL_IN', 'out': 'HAL_OUT', 'io': 'HAL_IO' } typemap = {'signed': 's32', 'unsigned': 'u32'} deprmap = {'s32': 'signed', 'u32': 'unsigned'} deprecated = ['s32', 'u32'] def initialize(): global functions, params, pins, options, comp_name, names, docs functions = []; params = []; pins = []; options = {} docs = [] comp_name = None names = {} def Warn(msg, *args): if args: msg = msg % args print >>sys.stderr, "%s:%d: Warning: %s" % (S.filename, S.line, msg) def Error(msg, *args): if args: msg = msg % args raise runtime.SyntaxError(S.get_pos(), msg, None) def comp(name, doc): docs.append(('component', name, doc)) global comp_name if comp_name: Error("Duplicate specification of component name") comp_name = name; def description(doc): docs.append(('descr', doc)); def see_also(doc): docs.append(('see_also', doc)); def type2type(type): # When we start warning about s32/u32 this is where the warning goes return typemap.get(type, type) def pin(name, type, dir, doc, value): type = type2type(type) if name in names: Error("Duplicate item name %s" % name) docs.append(('pin', name, type, dir, doc, value)) names[name] = None pins.append((name, type, dir, value)) def param(name, type, dir, doc, value): type = type2type(type) if name in names: Error("Duplicate item name %s" % name) docs.append(('param', name, type, dir, doc, value)) names[name] = None params.append((name, type, dir, value)) def function(name, fp, doc): if name in names: raise runtime.SyntaxError, "Duplicate item name %s" % name docs.append(('funct', name, fp, doc)) names[name] = None functions.append((name, fp)) def option(name, value): if name in options: raise runtime.SyntaxError, "Duplicate option name %s" % name options[name] = value def removeprefix(s,p): if s.startswith(p): return s[len(p):] return s def to_hal(name): return name.replace("_", "-").rstrip("-").rstrip(".") def prologue(f): print >> f, "/* Autogenerated by %s on %s -- do not edit */" % ( sys.argv[0], time.asctime()) print >> f, """\ #include "rtapi.h" #include "rtapi_app.h" #include "hal.h" #include "rtapi_string.h" static int comp_id; """ names = {} def q(s): s = s.replace("\\", "\\\\") s = s.replace("\"", "\\\"") s = s.replace("\r", "\\r") s = s.replace("\n", "\\n") s = s.replace("\t", "\\t") s = s.replace("\v", "\\v") return '"%s"' % s print >>f, "#ifdef MODULE_INFO" for v in docs: if not v: continue v = ":".join(map(str, v)) print >>f, "MODULE_INFO(emc2, %s);" % q(v) print >>f, "#endif // __MODULE_INFO" print >>f has_data = options.get("data") print >>f print >>f, "struct state {" print >>f, " struct state *_next;" for name, type, dir, value in pins: if names.has_key(name): raise RuntimeError, "Duplicate item name: %s" % name print >>f, " hal_%s_t *%s;" % (type, name) names[name] = 1 for name, type, dir, value in params: if names.has_key(name): raise RuntimeError, "Duplicate item name: %s" % name print >>f, " hal_%s_t %s;" % (type, name) names[name] = 1 if has_data: print >>f, " void *_data;" print >>f, "};" if options.get("userspace"): print >>f, "#include " print >>f, "struct state *inst=0;" print >>f, "struct state *first_inst=0;" print >>f for name, fp in functions: if names.has_key(name): raise RuntimeError, "Duplicate item name: %s" % name print >>f, "static void %s(struct state *inst, long period);" % name names[name] = 1 print >>f, "static int get_data_size(void);" if options.get("extra_setup"): print >>f, "static int extra_setup(struct state *inst, long extra_arg);" if options.get("extra_cleanup"): print >>f, "static void extra_cleanup(void);" print >>f print >>f, "static int export(char *prefix, long extra_arg) {" print >>f, " char buf[HAL_NAME_LEN + 2];" print >>f, " int r = 0;" print >>f, " int sz = sizeof(struct state) + get_data_size();" print >>f, " struct state *inst = hal_malloc(sz);" print >>f, " memset(inst, 0, sz);" if options.get("extra_setup"): print >>f, " r = extra_setup(inst, extra_arg);" print >>f, " if(r != 0) return r;" for name, type, dir, value in pins: print >>f, " r = hal_pin_%s_newf(%s, &(inst->%s), comp_id," % ( type, dirmap[dir], name) print >>f, " \"%%s%s\", prefix);" % to_hal("." + name) print >>f, " if(r != 0) return r;" if value is not None: print >>f, " *(inst->%s) = %s;" % (name, value) for name, type, dir, value in params: print >>f, " r = hal_param_%s_newf(%s, &(inst->%s), comp_id," % ( type, dirmap[dir], name) print >>f, " \"%%s%s\", prefix);" % to_hal("." + name) if value is not None: print >>f, " inst->%s = %s;" % (name, value) print >>f, " if(r != 0) return r;" for name, fp in functions: print >>f, " rtapi_snprintf(buf, HAL_NAME_LEN, \"%%s%s\", prefix);"\ % to_hal("." + name) print >>f, " r = hal_export_funct(buf, (void(*)(void *inst, long))%s, inst, %s, 0, comp_id);" % ( name, int(fp)) print >>f, " if(r != 0) return r;" print >>f, " inst->_next = first_inst;" print >>f, " first_inst = inst;" print >>f, " return 0;" print >>f, "}" if options.get("count_function"): print >>f, "static int get_count(void);" if options.get("rtapi_app", 1): if options.get("constructable") and not options.get("singleton"): print >>f, "static int export_1(char *prefix, char *argstr) {" print >>f, " int arg = simple_strtol(argstr, NULL, 0);" print >>f, " return export(prefix, arg);" print >>f, "}" if not options.get("singleton") and not options.get("count_function") : print >>f, "static int count = %s;" \ % options.get("default_count", 1) if not options.get("userspace"): print >>f, "RTAPI_MP_INT(count, \"number of %s\");" % comp_name print >>f, "int rtapi_app_main(void) {" print >>f, " int r = 0;" if not options.get("singleton"): print >>f, " int i;" if options.get("count_function"): print >>f, " int count = get_count();" print >>f, " comp_id = hal_init(\"%s\");" % comp_name print >>f, " if(comp_id < 0) return comp_id;" if options.get("singleton"): print >>f, " r = export(\"%s\", 0);" % \ to_hal(removeprefix(comp_name, "hal_")) else: print >>f, " for(i=0; i>f, " char buf[HAL_NAME_LEN + 2];" print >>f, " rtapi_snprintf(buf, HAL_NAME_LEN, " \ "\"%s.%%d\", i);" % \ to_hal(removeprefix(comp_name, "hal_")) print >>f, " r = export(buf, i);" print >>f, " if(r != 0) break;" print >>f, " }" if options.get("constructable") and not options.get("singleton"): print >>f, " hal_set_constructor(comp_id, export_1);" print >>f, " if(r) {" if options.get("extra_cleanup"): print >>f, " extra_cleanup();" print >>f, " hal_exit(comp_id);" print >>f, " } else {" print >>f, " hal_ready(comp_id);" print >>f, " }" print >>f, " return r;"; print >>f, "}" print >>f print >>f, "void rtapi_app_exit(void) {" if options.get("extra_cleanup"): print >>f, " extra_cleanup();" print >>f, " hal_exit(comp_id);" print >>f, "}" if options.get("userspace"): print >>f, "static void user_mainloop(void);" if options.get("userinit"): print >>f, "static void user_init(int argc, char **argv);" print >>f, "int argc=0; char **argv=0;" print >>f, "int main(int argc_, char **argv_) {" print >>f, " argc = argc_; argv = argv;" print >>f if options.get("userinit", 0): print >>f, " userinit(argc, argv)"; print >>f print >>f, " if(rtapi_app_main() < 0) return 1;" print >>f, " user_mainloop();" print >>f, " rtapi_app_exit();" print >>f, " return 0;" print >>f, "}" print >>f if not options.get("no_convenience_defines"): print >>f, "#define FUNCTION(name) static void name(struct state *inst, long period)" print >>f, "#define EXTRA_SETUP() static int extra_setup(struct state *inst, long extra_arg)" print >>f, "#define EXTRA_CLEANUP() static void extra_cleanup(void)" print >>f, "#define fperiod (period * 1e-9)" for name, type, dir, value in pins: if dir == 'in': print >>f, "#define %s (0+*inst->%s)" % (name, name) else: print >>f, "#define %s (*inst->%s)" % (name, name) for name, type, dir, value in params: print >>f, "#define %s (inst->%s)" % (name, name) if has_data: print >>f, "#define data (*(%s*)&(inst->_data))" % options['data'] if options.get("userspace"): print >>f, "#define FOR_ALL_INSTS() for(inst = first_inst; inst; inst = inst->_next)" print >>f print >>f def epilogue(f): data = options.get('data') print >>f if data: print >>f, "static int get_data_size(void) { return sizeof(%s); }" % data else: print >>f, "static int get_data_size(void) { return 0; }" INSTALL, COMPILE, PREPROCESS, DOCUMENT = range(4) modinc = None def find_modinc(): global modinc if modinc: return modinc d = os.path.abspath(os.path.dirname(os.path.dirname(sys.argv[0]))) for e in ['src', 'etc/emc2', '/etc/emc2']: e = os.path.join(d, e, 'Makefile.modinc') if os.path.exists(e): modinc = e return e raise SystemExit, "Unable to locate Makefile.modinc" def build_usr(tempdir, filename, mode, origfilename): binname = os.path.basename(os.path.splitext(filename)[0]) makefile = os.path.join(tempdir, "Makefile") f = open(makefile, "w") print >>f, "%s: %s" % (binname, filename) print >>f, "\t$(CC) $(EXTRA_CFLAGS) -URTAPI -DULAPI -O2 %s -o $@ $< -Wl,-rpath,$(LIBDIR) -L$(LIBDIR) -lemchal %s" % ( options.get("extra_compile_args", ""), options.get("extra_link_args", "")) print >>f, "include %s" % find_modinc() f.close() result = os.system("cd %s; make -S %s" % (tempdir, binname)) if result != 0: raise SystemExit, result output = os.path.join(tempdir, binname) if mode == INSTALL: shutil.copy(output, os.path.join(BASE, "bin", binname)) elif mode == COMPILE: shutil.copy(output, os.path.join(os.path.dirname(origfilename),binname)) def build_rt(tempdir, filename, mode, origfilename): objname = os.path.basename(os.path.splitext(filename)[0] + ".o") makefile = os.path.join(tempdir, "Makefile") f = open(makefile, "w") print >>f, "obj-m += %s" % objname print >>f, "include %s" % find_modinc() print >>f, "EXTRA_CFLAGS += -I%s" % os.path.abspath(os.path.dirname(origfilename)) f.close() if mode == INSTALL: target = "modules install" else: target = "modules" result = os.system("cd %s; make -S %s" % (tempdir, target)) if result != 0: raise SystemExit, result if mode == COMPILE: for extension in ".ko", ".so", ".o": kobjname = os.path.splitext(filename)[0] + extension if os.path.exists(kobjname): shutil.copy(kobjname, os.path.basename(kobjname)) break else: raise SystemExit, "Unable to copy module from temporary directory" def finddoc(section=None, name=None): for item in docs: if ((section == None or section == item[0]) and (name == None or name == item[1])): return item return None def finddocs(section=None, name=None): for item in docs: if ((section == None or section == item[0]) and (name == None or name == item[1])): yield item def to_hal_man(s): if options.get("singleton"): s = "%s.%s" % (comp_name, s) else: s = "%s.\\fIN\\fB.%s" % (comp_name, s) s = s.replace("_", "-") s = s.rstrip("-") s = s.rstrip(".") # s = s.replace("-", "\\-") return s def document(filename, outfilename): if outfilename is None: outfilename = os.path.splitext(filename)[0] + ".9" initialize() f = open(filename).read() a, b = f.split("\n;;\n", 1) p = parse('File', a, filename) if not p: raise SystemExit, 1 f = open(outfilename, "w") print >>f, ".TH %s \"9\" \"%s\" \"EMC Documentation\" \"HAL Component\"" % ( comp_name.upper(), time.strftime("%F")) print >>f, ".de TQ\n.br\n.ns\n.TP \\\\$1\n..\n" print >>f, ".SH NAME\n" doc = finddoc('component') if doc and doc[2]: if '\n' in doc[2]: firstline, rest = doc[2].split('\n', 1) else: firstline = doc[2] rest = '' print >>f, "%s \\- %s" % (doc[1], firstline) else: rest = '' print >>f, "%s" % doc[1] print >>f, ".SH SYNOPSIS" if options.get("userspace"): print >>f, ".B %s" % comp_name else: if rest: print >>f, rest elif options.get("singleton") or options.get("count_function"): print >>f, ".B loadrt %s" % comp_name else: print >>f, ".B loadrt %s [count=\\fIN\\fB]" % comp_name if options.get("constructable") and not options.get("singleton"): print >>f, ".PP\n.B newinst %s \\fIname\\fB" % comp_name doc = finddoc('descr') if doc and doc[1]: print >>f, ".SH DESCRIPTION\n" print >>f, "%s" % doc[1] if not options.get("userspace"): print >>f, ".SH FUNCTIONS" for _, name, fp, doc in finddocs('funct'): print >>f, ".TP" print >>f, "\\fB%s\\fR" % to_hal_man(name), if fp: print >>f, "(uses floating-point)" else: print >>f print >>f, doc lead = ".TP" print >>f, ".SH PINS" for _, name, type, dir, doc, value in finddocs('pin'): print >>f, lead print >>f, ".B %s\\fR" % to_hal_man(name), print >>f, type, dir, if value: print >>f, "\\fR(default: \\fI%s\\fR)" % value else: print >>f, "\\fR" if doc: print >>f, doc lead = ".TP" else: lead = ".TQ" lead = ".TP" if params: print >>f, ".SH PARAMETERS" for _, name, type, dir, doc, value in finddocs('param'): print >>f, lead print >>f, ".B %s\\fR" % to_hal_man(name), print >>f, type, dir, if value: print >>f, "\\fR(default: \\fI%s\\fR)" % value else: print >>f, "\\fR" if doc: print >>f, doc lead = ".TP" else: lead = ".TQ" doc = finddoc('see_also') if doc and doc[1]: print >>f, ".SH SEE ALSO\n" print >>f, "%s" % doc[1] def process(filename, mode, outfilename): tempdir = tempfile.mkdtemp() try: if outfilename is None: if mode == PREPROCESS: outfilename = os.path.splitext(filename)[0] + ".c" else: outfilename = os.path.join(tempdir, os.path.splitext(os.path.basename(filename))[0] + ".c") initialize() f = open(filename).read() a, b = f.split("\n;;\n", 1) p = parse('File', a, filename) if not p: raise SystemExit, 1 f = open(outfilename, "w") if options.get("userspace"): if functions: raise SystemExit, "Userspace components may not have functions" else: if not functions: raise SystemExit, "Realtime component must have at least one function" if not pins: raise SystemExit, "Component must have at least one pin" prologue(f) lineno = a.count("\n") + 3 f.write("#line %d \"%s\"\n" % (lineno, filename)) f.write(b) epilogue(f) f.close() if mode != PREPROCESS: if options.get("userspace"): build_usr(tempdir, outfilename, mode, filename) else: build_rt(tempdir, outfilename, mode, filename) finally: shutil.rmtree(tempdir) def usage(exitval=0): print """%(name)s: Build, compile, and install EMC HAL components Usage: %(name)s [--install|--compile|--preprocess|--document] compfile... %(name)s [--install|--compile] cfile... """ % {'name': os.path.basename(sys.argv[0])} raise SystemExit, exitval def main(): mode = PREPROCESS outfile = None try: opts, args = getopt.getopt(sys.argv[1:], "icpdo:h?", ['install', 'compile', 'preprocess', 'outfile=', 'document', 'help']) except getopt.GetoptError: usage(1) for k, v in opts: if k in ("-i", "--install"): mode = INSTALL if k in ("-c", "--compile"): mode = COMPILE if k in ("-p", "--preprocess"): mode = PREPROCESS if k in ("-d", "--document"): mode = DOCUMENT if k in ("-o", "--outfile"): if len(args) != 1: raise SystemExit, "Cannot specify -o with multiple input files" outfile = v if k in ("-?", "-h", "--help"): usage(0) if outfile and mode != PREPROCESS and mode != DOCUMENT: raise SystemExit, "Can only specify -o when preprocessing or documenting" for f in args: if f.endswith(".comp") and mode == DOCUMENT: document(f, outfile) elif f.endswith(".comp"): process(f, mode, outfile) elif f.endswith(".c") and mode != PREPROCESS: tempdir = tempfile.mkdtemp() try: shutil.copy(f, tempdir) build(tempdir, os.path.join(tempdir, os.path.basename(f)), mode, f) finally: shutil.rmtree(tempdir) else: raise SystemExit, "Unrecognized file type: %s" % f if __name__ == '__main__': main() # vim:sw=4:sts=4:et