/ src / hal / utils / halcompile.g
halcompile.g
   1  #!/usr/bin/env python
   2  #    This is 'halcompile', a tool to write HAL boilerplate
   3  #    Copyright 2006 Jeff Epler <jepler@unpythonic.net>
   4  #
   5  #    This program is free software; you can redistribute it and/or modify
   6  #    it under the terms of the GNU General Public License as published by
   7  #    the Free Software Foundation; either version 2 of the License, or
   8  #    (at your option) any later version.
   9  #
  10  #    This program is distributed in the hope that it will be useful,
  11  #    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  #    GNU General Public License for more details.
  14  #
  15  #    You should have received a copy of the GNU General Public License
  16  #    along with this program; if not, write to the Free Software
  17  #    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18  from __future__ import print_function
  19  
  20  %%
  21  parser Hal:
  22      ignore: "//.*"
  23      ignore: "/[*](.|\n)*?[*]/"
  24      ignore: "[ \t\r\n]+"
  25  
  26      token END: ";;"
  27      token PARAMDIRECTION: "rw|r"
  28      token PINDIRECTION: "in|out|io"
  29      token TYPE: "float|bit|signed|unsigned|u32|s32"
  30      token NAME: "[a-zA-Z_][a-zA-Z0-9_]*"
  31      token STARREDNAME: "[*]*[a-zA-Z_][a-zA-Z0-9_]*"
  32      token HALNAME: "[#a-zA-Z_][-#a-zA-Z0-9_.]*"
  33      token FPNUMBER: "-?([0-9]*\.[0-9]+|[0-9]+\.?)([Ee][+-]?[0-9]+)?f?"
  34      token NUMBER: "0x[0-9a-fA-F]+|[+-]?[0-9]+"
  35      token STRING: "\"(\\.|[^\\\"])*\""
  36      token HEADER: "<.*?>"
  37      token POP: "[-()+*/]|&&|\\|\\||personality|==|&|!=|<<|<|<=|>>|>|>="
  38      token TSTRING: "r?\"\"\"(\\.|\\\n|[^\\\"]|\"(?!\"\")|\n)*\"\"\""
  39  
  40      rule File: ComponentDeclaration Declaration* "$" {{ return True }}
  41      rule ComponentDeclaration:
  42          "component" NAME OptString";" {{ comp(NAME, OptString); }}
  43      rule Declaration:
  44          "pin" PINDIRECTION TYPE HALNAME OptArray OptSAssign OptPersonality OptString ";"  {{ pin(HALNAME, TYPE, OptArray, PINDIRECTION, OptString, OptSAssign, OptPersonality) }}
  45        | "param" PARAMDIRECTION TYPE HALNAME OptArray OptSAssign OptPersonality OptString ";" {{ param(HALNAME, TYPE, OptArray, PARAMDIRECTION, OptString, OptSAssign, OptPersonality) }}
  46        | "function" NAME OptFP OptString ";"       {{ function(NAME, OptFP, OptString) }}
  47        | "variable" NAME STARREDNAME OptSimpleArray OptAssign ";" {{ variable(NAME, STARREDNAME, OptSimpleArray, OptAssign) }}
  48        | "option" NAME OptValue ";"   {{ option(NAME, OptValue) }}
  49        | "see_also" String ";"   {{ see_also(String) }}
  50        | "notes" String ";"   {{ notes(String) }}
  51        | "description" String ";"   {{ description(String) }}
  52        | "license" String ";"   {{ license(String) }}
  53        | "author" String ";"   {{ author(String) }}
  54        | "include" Header ";"   {{ include(Header) }}
  55        | "modparam" NAME {{ NAME1=NAME; }} NAME OptSAssign OptString ";" {{ modparam(NAME1, NAME, OptSAssign, OptString) }}
  56  
  57      rule Header: STRING {{ return STRING }} | HEADER {{ return HEADER }}
  58  
  59      rule String: TSTRING {{ return eval(TSTRING) }} 
  60              | STRING {{ return eval(STRING) }}
  61   
  62      rule OptPersonality: "if" Personality {{ return Personality }}
  63              | {{ return None }}
  64      rule Personality: {{ pp = [] }} (PersonalityPart {{ pp.append(PersonalityPart) }} )* {{ return " ".join(pp) }}
  65      rule PersonalityPart: NUMBER {{ return NUMBER }}
  66              | POP {{ return POP }}
  67      rule OptSimpleArray: "\[" NUMBER "\]" {{ return int(NUMBER) }}
  68              | {{ return 0 }}
  69      rule OptArray: "\[" NUMBER OptArrayPersonality "\]" {{ return OptArrayPersonality and (int(NUMBER), OptArrayPersonality) or int(NUMBER) }}
  70              | {{ return 0 }}
  71      rule OptArrayPersonality: ":" Personality {{ return Personality }}
  72              | {{ return None }} 
  73      rule OptString: TSTRING {{ return eval(TSTRING) }} 
  74              | STRING {{ return eval(STRING) }}
  75              | {{ return '' }}
  76      rule OptAssign: "=" Value {{ return Value; }}
  77                  | {{ return None }}
  78      rule OptSAssign: "=" SValue {{ return SValue; }}
  79                  | {{ return None }}
  80      rule OptFP: "fp" {{ return 1 }} | "nofp" {{ return 0 }} | {{ return 1 }}
  81      rule Value: "yes" {{ return 1 }} | "no" {{ return 0 }}  
  82                  | "true" {{ return 1 }} | "false" {{ return 0 }}  
  83                  | "TRUE" {{ return 1 }} | "FALSE" {{ return 0 }}  
  84                  | NAME {{ return NAME }}
  85                  | FPNUMBER {{ return float(FPNUMBER.rstrip("f")) }}
  86                  | NUMBER {{ return int(NUMBER,0) }}
  87      rule SValue: "yes" {{ return "yes" }} | "no" {{ return "no" }}  
  88                  | "true" {{ return "true" }} | "false" {{ return "false" }}  
  89                  | "TRUE" {{ return "TRUE" }} | "FALSE" {{ return "FALSE" }}  
  90                  | NAME {{ return NAME }}
  91                  | FPNUMBER {{ return FPNUMBER }}
  92                  | NUMBER {{ return NUMBER }}
  93      rule OptValue: Value {{ return Value }}
  94                  | TSTRING {{ return eval(TSTRING) }}
  95                  | STRING {{ return eval(STRING) }}
  96                  | {{ return 1 }}
  97      rule OptSValue: SValue {{ return SValue }}
  98                  | {{ return 1 }}
  99  %%
 100  
 101  import os, sys, tempfile, shutil, getopt, time
 102  BASE = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), ".."))
 103  sys.path.insert(0, os.path.join(BASE, "lib", "python"))
 104  
 105  MAX_USERSPACE_NAMES = 16 # for userspace (loadusr) components
 106  # NOTE: names are assigned dynamically for realtime components (loadrt)
 107  
 108  # Components that use 'personality' features statically allocate
 109  # memory based on MAX_PERSONALITIES (RTAPI_MP_ARRAY_INT)
 110  # The number can be set with the cmdline option -P|--personalities
 111  # Smaller values may be useful since the index of the personality
 112  # exported is computed modulo MAX_PERSONALITIES
 113  MAX_PERSONALITIES = 64
 114  
 115  mp_decl_map = {'int': 'RTAPI_MP_INT', 'dummy': None}
 116  
 117  # These are symbols that comp puts in the global namespace of the C file it
 118  # creates.  The user is thus not allowed to add any symbols with these
 119  # names.  That includes not only global variables and functions, but also
 120  # HAL pins & parameters, because comp adds #defines with the names of HAL
 121  # pins & params.
 122  reserved_names = [ 'comp_id', 'fperiod', 'rtapi_app_main', 'rtapi_app_exit', 'extra_setup', 'extra_cleanup' ]
 123  
 124  def _parse(rule, text, filename=None):
 125      global P, S
 126      S = HalScanner(text, filename=filename)
 127      P = Hal(S)
 128      return runtime.wrap_error_reporter(P, rule)
 129  
 130  def parse(filename):
 131      initialize()
 132      f = open(filename).read()
 133      if '\r' in f:
 134          if require_unix_line_endings:
 135              raise SystemExit("%s:0: Error: File contains DOS-style or Mac-style line endings." % filename)
 136          else:
 137              print("%s:0: Warning: File contains DOS-style or Mac-style line endings." % filename, file=sys.stderr)
 138          f = open(filename, "rU").read()
 139      a, b = f.split("\n;;\n", 1)
 140      p = _parse('File', a + "\n\n", filename)
 141      if not p: raise SystemExit(1)
 142      if require_license:
 143          if not finddoc('license'):
 144              raise SystemExit("%s:0: License not specified" % filename)
 145      return a, b
 146  
 147  dirmap = {'r': 'HAL_RO', 'rw': 'HAL_RW', 'in': 'HAL_IN', 'out': 'HAL_OUT', 'io': 'HAL_IO' }
 148  typemap = {'signed': 's32', 'unsigned': 'u32'}
 149  deprmap = {'s32': 'signed', 'u32': 'unsigned'}
 150  deprecated = ['s32', 'u32']
 151  
 152  def initialize():
 153      global functions, params, pins, options, comp_name, names, docs, variables
 154      global modparams, includes
 155  
 156      functions = []; params = []; pins = []; options = {}; variables = []
 157      modparams = []; docs = []; includes = [];
 158      comp_name = None
 159  
 160      names = {}
 161  
 162  def Warn(msg, *args):
 163      if args:
 164          msg = msg % args
 165      print("%s:%d: Warning: %s" % (S.filename, S.line, msg), file=sys.stderr)
 166  
 167  def Error(msg, *args):
 168      if args:
 169          msg = msg % args
 170      raise runtime.SyntaxError(S.get_pos(), msg, None)
 171  
 172  def comp(name, doc):
 173      docs.append(('component', name, doc))
 174      global comp_name
 175      if comp_name:
 176          Error("Duplicate specification of component name")
 177      comp_name = name;
 178  
 179  def description(doc):
 180      docs.append(('descr', doc));
 181  
 182  def license(doc):
 183      docs.append(('license', doc));
 184  
 185  def author(doc):
 186      docs.append(('author', doc));
 187  
 188  def see_also(doc):
 189      docs.append(('see_also', doc));
 190  
 191  def notes(doc):
 192      docs.append(('notes', doc));
 193  
 194  def type2type(type):
 195      # When we start warning about s32/u32 this is where the warning goes
 196      return typemap.get(type, type)
 197      
 198  def checkarray(name, array):
 199      hashes = len(re.findall("#+", name))
 200      if array:
 201          if hashes == 0: Error("Array name contains no #: %r" % name)
 202          if hashes > 1: Error("Array name contains more than one block of #: %r" % name)
 203      else:
 204          if hashes > 0: Error("Non-array name contains #: %r" % name)
 205  
 206  def check_name_ok(name):
 207      if name in reserved_names:
 208          Error("Variable name %s is reserved" % name)
 209      if name in names:
 210          Error("Duplicate item name %s" % name)
 211  
 212  def pin(name, type, array, dir, doc, value, personality):
 213      checkarray(name, array)
 214      type = type2type(type)
 215      check_name_ok(name)
 216      docs.append(('pin', name, type, array, dir, doc, value, personality))
 217      names[name] = None
 218      pins.append((name, type, array, dir, value, personality))
 219  
 220  def param(name, type, array, dir, doc, value, personality):
 221      checkarray(name, array)
 222      type = type2type(type)
 223      check_name_ok(name)
 224      docs.append(('param', name, type, array, dir, doc, value, personality))
 225      names[name] = None
 226      params.append((name, type, array, dir, value, personality))
 227  
 228  def function(name, fp, doc):
 229      check_name_ok(name)
 230      docs.append(('funct', name, fp, doc))
 231      names[name] = None
 232      functions.append((name, fp))
 233  
 234  def option(name, value):
 235      if name in options:
 236          Error("Duplicate option name %s" % name)
 237      options[name] = value
 238  
 239  def variable(type, name, array, default):
 240      check_name_ok(name)
 241      names[name] = None
 242      variables.append((type, name, array, default))
 243  
 244  def modparam(type, name, default, doc):
 245      check_name_ok(name)
 246      names[name] = None
 247      modparams.append((type, name, default, doc))
 248  
 249  def include(value):
 250      includes.append((value))
 251  
 252  def removeprefix(s,p):
 253      if s.startswith(p): return s[len(p):]
 254      return s
 255  
 256  def to_hal(name):
 257      name = re.sub("#+", lambda m: "%%0%dd" % len(m.group(0)), name)
 258      return name.replace("_", "-").rstrip("-").rstrip(".")
 259  def to_c(name):
 260      name = re.sub("[-._]*#+", "", name)
 261      name = name.replace("#", "").replace(".", "_").replace("-", "_")
 262      return re.sub("_+", "_", name)
 263  
 264  def prologue(f):
 265      print("/* Autogenerated by %s on %s -- do not edit */" % (
 266          sys.argv[0], time.asctime()), file=f)
 267      print("""\
 268  #include "rtapi.h"
 269  #ifdef RTAPI
 270  #include "rtapi_app.h"
 271  #endif
 272  #include "rtapi_string.h"
 273  #include "rtapi_errno.h"
 274  #include "hal.h"
 275  #include "rtapi_math64.h"
 276  
 277  static int comp_id;
 278  """, file=f)
 279      for name in includes:
 280          print("#include %s" % name, file=f)
 281  
 282      names = {}
 283  
 284      def q(s):
 285          s = s.replace("\\", "\\\\")
 286          s = s.replace("\"", "\\\"")
 287          s = s.replace("\r", "\\r")
 288          s = s.replace("\n", "\\n")
 289          s = s.replace("\t", "\\t")
 290          s = s.replace("\v", "\\v")
 291          return '"%s"' % s
 292  
 293      print("#ifdef MODULE_INFO", file=f)
 294      for v in docs:
 295          if not v: continue
 296          v = ":".join(map(str, v))
 297          print("MODULE_INFO(linuxcnc, %s);" % q(v), file=f)
 298          license = finddoc('license')
 299      if license and license[1]:
 300          print("MODULE_LICENSE(\"%s\");" % license[1].split("\n")[0], file=f)
 301      print("#endif // MODULE_INFO", file=f)
 302      print("", file=f)
 303  
 304  
 305      has_data = options.get("data")
 306  
 307      has_array = False
 308      has_personality = False
 309      for name, type, array, dir, value, personality in pins:
 310          if array: has_array = True
 311          if isinstance(array, tuple): has_personality = True
 312          if personality: has_personality = True
 313      for name, type, array, dir, value, personality in params:
 314          if array: has_array = True
 315          if isinstance(array, tuple): has_personality = True
 316          if personality: has_personality = True
 317      for type, name, default, doc in modparams:
 318          decl = mp_decl_map[type]
 319          if decl:
 320              print("%s %s" % (type, name), end=' ', file=f)
 321              if default: print("= %s;" % default, file=f)
 322              else: print(";", file=f)
 323              print("%s(%s, %s);" % (decl, name, q(doc)), file=f)
 324              
 325      print("", file=f)
 326      print("struct __comp_state {", file=f)
 327      print("    struct __comp_state *_next;", file=f)
 328      if has_personality:
 329          print("    int _personality;", file=f)
 330  
 331      for name, type, array, dir, value, personality in pins:
 332          if array:
 333              if isinstance(array, tuple): array = array[0]
 334              print("    hal_%s_t *%s[%s];" % (type, to_c(name), array), file=f)
 335          else:
 336              print("    hal_%s_t *%s;" % (type, to_c(name)), file=f)
 337          names[name] = 1
 338  
 339      for name, type, array, dir, value, personality in params:
 340          if array:
 341              if isinstance(array, tuple): array = array[0]
 342              print("    hal_%s_t %s[%s];" % (type, to_c(name), array), file=f)
 343          else:
 344              print("    hal_%s_t %s;" % (type, to_c(name)), file=f)
 345          names[name] = 1
 346  
 347      for type, name, array, value in variables:
 348          if array:
 349              print("    %s %s[%d];\n" % (type, name, array), file=f)
 350          else:
 351              print("    %s %s;\n" % (type, name), file=f)
 352      if has_data:
 353          print("    void *_data;", file=f)
 354  
 355      print("};", file=f)
 356  
 357      if options.get("userspace"):
 358          print("#include <stdlib.h>", file=f)
 359  
 360      print("struct __comp_state *__comp_first_inst=0, *__comp_last_inst=0;", file=f)
 361      
 362      print("", file=f)
 363      for name, fp in functions:
 364          if name in names:
 365              Error("Duplicate item name: %s" % name)
 366          print("static void %s(struct __comp_state *__comp_inst, long period);" % to_c(name), file=f)
 367          names[name] = 1
 368  
 369      print("static int __comp_get_data_size(void);", file=f)
 370      if options.get("extra_setup"):
 371          print("static int extra_setup(struct __comp_state *__comp_inst, char *prefix, long extra_arg);", file=f)
 372      if options.get("extra_cleanup"):
 373          print("static void extra_cleanup(void);", file=f)
 374  
 375      if not options.get("no_convenience_defines"):
 376          print("#undef TRUE", file=f)
 377          print("#define TRUE (1)", file=f)
 378          print("#undef FALSE", file=f)
 379          print("#define FALSE (0)", file=f)
 380          print("#undef true", file=f)
 381          print("#define true (1)", file=f)
 382          print("#undef false", file=f)
 383          print("#define false (0)", file=f)
 384  
 385      print("", file=f)
 386      if has_personality:
 387          print("static int export(char *prefix, long extra_arg, long personality) {", file=f)
 388      else:
 389          print("static int export(char *prefix, long extra_arg) {", file=f)
 390      if len(functions) > 0:
 391          print("    char buf[HAL_NAME_LEN + 1];", file=f)
 392      print("    int r = 0;", file=f)
 393      if has_array:
 394          print("    int j = 0;", file=f)
 395      print("    int sz = sizeof(struct __comp_state) + __comp_get_data_size();", file=f)
 396      print("    struct __comp_state *inst = hal_malloc(sz);", file=f)
 397      print("    memset(inst, 0, sz);", file=f)
 398      if has_data:
 399          print("    inst->_data = (char*)inst + sizeof(struct __comp_state);", file=f)
 400      if has_personality:
 401          print("    inst->_personality = personality;", file=f)
 402      if options.get("extra_setup"):
 403          print("    r = extra_setup(inst, prefix, extra_arg);", file=f)
 404          print("    if(r != 0) return r;", file=f)
 405          # the extra_setup() function may have changed the personality
 406          if has_personality:
 407              print("    personality = inst->_personality;", file=f)
 408      for name, type, array, dir, value, personality in pins:
 409          if personality:
 410              print("if(%s) {" % personality, file=f)
 411          if array:
 412              if isinstance(array, tuple):
 413                  lim, cnt = array
 414                  print("    if((%s) > (%s)) {" % (cnt, lim), file=f)
 415                  print('        rtapi_print_msg(RTAPI_MSG_ERR,' \
 416                                  '"Pin %s: Requested size %%d exceeds max size %%d\\n",'
 417                                  '(int)%s, (int)%s);' % (name, cnt, lim), file=f)
 418                  print("        return -ENOSPC;", file=f)
 419                  print("    }", file=f)
 420              else: cnt = array
 421              print("    for(j=0; j < (%s); j++) {" % cnt, file=f)
 422              print("        r = hal_pin_%s_newf(%s, &(inst->%s[j]), comp_id," % (
 423                  type, dirmap[dir], to_c(name)), file=f)
 424              print("            \"%%s%s\", prefix, j);" % to_hal("." + name), file=f)
 425              print("        if(r != 0) return r;", file=f)
 426              if value is not None:
 427                  print("    *(inst->%s[j]) = %s;" % (to_c(name), value), file=f)
 428              print("    }", file=f)
 429          else:
 430              print("    r = hal_pin_%s_newf(%s, &(inst->%s), comp_id," % (
 431                  type, dirmap[dir], to_c(name)), file=f)
 432              print("        \"%%s%s\", prefix);" % to_hal("." + name), file=f)
 433              print("    if(r != 0) return r;", file=f)
 434              if value is not None:
 435                  print("    *(inst->%s) = %s;" % (to_c(name), value), file=f)
 436          if personality:
 437              print("}", file=f)
 438  
 439      for name, type, array, dir, value, personality in params:
 440          if personality:
 441              print("if(%s) {" % personality, file=f)
 442          if array:
 443              if isinstance(array, tuple):
 444                  lim, cnt = array
 445                  print("    if((%s) > (%s)) {" % (cnt, lim), file=f)
 446                  print('        rtapi_print_msg(RTAPI_MSG_ERR,' \
 447                                  '"Parameter %s: Requested size %%d exceeds max size %%d\\n",'
 448                                  '(int)%s, (int)%s);' % (name, cnt, lim), file=f)
 449                  print("        return -ENOSPC;", file=f)
 450                  print("    }", file=f)
 451              else: cnt = array
 452              print("    for(j=0; j < (%s); j++) {" % cnt, file=f)
 453              print("        r = hal_param_%s_newf(%s, &(inst->%s[j]), comp_id," % (
 454                  type, dirmap[dir], to_c(name)), file=f)
 455              print("            \"%%s%s\", prefix, j);" % to_hal("." + name), file=f)
 456              print("        if(r != 0) return r;", file=f)
 457              if value is not None:
 458                  print("    inst->%s[j] = %s;" % (to_c(name), value), file=f)
 459              print("    }", file=f)
 460          else:
 461              print("    r = hal_param_%s_newf(%s, &(inst->%s), comp_id," % (
 462                  type, dirmap[dir], to_c(name)), file=f)
 463              print("        \"%%s%s\", prefix);" % to_hal("." + name), file=f)
 464              if value is not None:
 465                  print("    inst->%s = %s;" % (to_c(name), value), file=f)
 466              print("    if(r != 0) return r;", file=f)
 467          if personality:
 468              print("}", file=f)
 469  
 470      for type, name, array, value in variables:
 471          if value is None: continue
 472          if array:
 473              print("    for(j=0; j < %s; j++) {" % array, file=f)
 474              print("        inst->%s[j] = %s;" % (name, value), file=f)
 475              print("    }", file=f)
 476          else:
 477              print("    inst->%s = %s;" % (name, value), file=f)
 478  
 479      for name, fp in functions:
 480          print("    rtapi_snprintf(buf, sizeof(buf), \"%%s%s\", prefix);"\
 481              % to_hal("." + name), file=f)
 482          print("    r = hal_export_funct(buf, (void(*)(void *inst, long))%s, inst, %s, 0, comp_id);" % (
 483              to_c(name), int(fp)), file=f)
 484          print("    if(r != 0) return r;", file=f)
 485      print("    if(__comp_last_inst) __comp_last_inst->_next = inst;", file=f)
 486      print("    __comp_last_inst = inst;", file=f)
 487      print("    if(!__comp_first_inst) __comp_first_inst = inst;", file=f)
 488      print("    return 0;", file=f)
 489      print("}", file=f)
 490  
 491      if options.get("count_function"):
 492          print("static int get_count(void);", file=f)
 493  
 494      if options.get("rtapi_app", 1):
 495          if options.get("constructable") and not options.get("singleton"):
 496              print("static int export_1(char *prefix, char *argstr) {", file=f)
 497              print("    int arg = simple_strtol(argstr, NULL, 0);", file=f)
 498              print("    return export(prefix, arg);", file=f)
 499              print("}"   , file=f)
 500          if not options.get("singleton") and not options.get("count_function") :
 501              print("static int default_count=%s, count=0;" \
 502                  % options.get("default_count", 1), file=f)
 503              if options.get("userspace"):
 504                  print("char *names[%d] = {0,};"%(MAX_USERSPACE_NAMES), file=f)
 505              else:
 506                  print("RTAPI_MP_INT(count, \"number of %s\");" % comp_name, file=f)
 507                  print("char *names = \"\"; // comma separated names", file=f)
 508                  print("RTAPI_MP_STRING(names, \"names of %s\");" % comp_name, file=f)
 509          if has_personality:
 510              init1 = str(int(options.get('default_personality', 0)))
 511              init = ",".join([init1] * MAX_PERSONALITIES)
 512              print("static int personality[%d] = {%s};" %(MAX_PERSONALITIES,init), file=f)
 513              print("RTAPI_MP_ARRAY_INT(personality, %d, \"personality of each %s\");" %(MAX_PERSONALITIES,comp_name), file=f)
 514  
 515              # Return personality value.
 516              # If requested index excedes MAX_PERSONALITIES, use modulo indexing and give message
 517              print("""
 518              static int p_value(char* cname, char *name, int idx) {
 519                  int ans = personality[idx%%%d];
 520                  if (idx >= %d) {
 521              """%(MAX_PERSONALITIES,MAX_PERSONALITIES), file=f)
 522              print("""
 523                      if (name==NULL) {
 524                          rtapi_print_msg(RTAPI_MSG_ERR,"%s: instance %d assigned personality=%d(=%#0x)\\n",
 525                                          cname, idx, ans, ans);
 526                      } else {
 527                          rtapi_print_msg(RTAPI_MSG_ERR,"%s: name %s assigned personality=%d(=%#0x)\\n",
 528                                          cname, name, ans, ans);
 529                      }
 530                  }
 531                  return ans;
 532              }
 533              """, file=f)
 534  
 535          print("int rtapi_app_main(void) {", file=f)
 536          print("    int r = 0;", file=f)
 537          if not options.get("singleton"):
 538              print("    int i;", file=f)
 539          if options.get("count_function"):
 540              print("    int count = get_count();", file=f)
 541  
 542          print("    comp_id = hal_init(\"%s\");" % comp_name, file=f)
 543          print("    if(comp_id < 0) return comp_id;", file=f)
 544  
 545          if options.get("singleton"):
 546              if has_personality:
 547                  print("    r = export(\"%s\", 0, personality[0]);" % \
 548                          to_hal(removeprefix(comp_name, "hal_")), file=f)
 549              else:
 550                  print("    r = export(\"%s\", 0);" % \
 551                          to_hal(removeprefix(comp_name, "hal_")), file=f)
 552          elif options.get("count_function"):
 553              print("    for(i=0; i<count; i++) {", file=f)
 554              print("        char buf[HAL_NAME_LEN + 1];", file=f)
 555              print("        rtapi_snprintf(buf, sizeof(buf), " \
 556                                          "\"%s.%%d\", i);" % \
 557                      to_hal(removeprefix(comp_name, "hal_")), file=f)
 558              if has_personality:
 559                  print("        r = export(buf, i, p_value(\"%s\", buf, i) );"%comp_name, file=f)
 560              else:
 561                  print("        r = export(buf, i);", file=f)
 562              print("    }", file=f)
 563          else:
 564              print("    if(count && names[0]) {", file=f)
 565              print("        rtapi_print_msg(RTAPI_MSG_ERR," \
 566                              "\"count= and names= are mutually exclusive\\n\");", file=f)
 567              print("        return -EINVAL;", file=f)
 568              print("    }", file=f)
 569              print("    if(!count && !names[0]) count = default_count;", file=f)
 570              print("    if(count) {", file=f)
 571              print("        for(i=0; i<count; i++) {", file=f)
 572              print("            char buf[HAL_NAME_LEN + 1];", file=f)
 573              print("            rtapi_snprintf(buf, sizeof(buf), " \
 574                                          "\"%s.%%d\", i);" % \
 575                      to_hal(removeprefix(comp_name, "hal_")), file=f)
 576              if has_personality:
 577                  print("            r = export(buf, i, p_value(\"%s\", buf, i) );"%comp_name, file=f)
 578              else:
 579                  print("            r = export(buf, i);", file=f)
 580              print("            if(r != 0) break;", file=f)
 581              print("       }", file=f)
 582              print("    } else {", file=f)
 583              if options.get("userspace"):
 584                  print("        int max_names = sizeof(names)/sizeof(names[0]);", file=f)
 585                  print("        for(i=0; (i < max_names) && names[i]; i++) {", file=f)
 586                  print("            if (strlen(names[i]) < 1) {", file=f)
 587                  print("                rtapi_print_msg(RTAPI_MSG_ERR, \"names[%d] is invalid (empty string)\\n\", i);", file=f)
 588                  print("                r = -EINVAL;", file=f)
 589                  print("                break;", file=f)
 590                  print("            }", file=f)
 591                  if has_personality:
 592                      print("            r = export(names[i], i, p_value(\"%s\", names[i], i) );"%comp_name, file=f)
 593                  else:
 594                      print("            r = export(names[i], i);", file=f)
 595                  print("            if(r != 0) break;", file=f)
 596                  print("       }", file=f)
 597                  print("    }", file=f)
 598              else:
 599                  print("        int j,idx;", file=f)
 600                  print("        char *ptr;", file=f)
 601                  print("        char buf[HAL_NAME_LEN+1];", file=f)
 602                  print("        ptr = names;", file=f)
 603                  print("        idx = 0;", file=f)
 604                  print("        for (i=0,j=0; i <= strlen(names); i++) {", file=f)
 605                  print("            buf[j] = *(ptr+i);", file=f)
 606                  print("            if ( (*(ptr+i) == ',') || (*(ptr+i) == 0) ) {", file=f)
 607                  print("                buf[j] = 0;", file=f)
 608                  if has_personality:
 609                      print("                r = export(buf, idx, p_value(\"%s\", buf, idx) );"%comp_name, file=f)
 610                  else:
 611                      print("                r = export(buf, idx);", file=f)
 612                  print("                if (*(ptr+i+1) == 0) {break;}", file=f)
 613                  print("                idx++;", file=f)
 614                  print("                if(r != 0) {break;}", file=f)
 615                  print("                j=0;", file=f)
 616                  print("            } else {", file=f)
 617                  print("                j++;", file=f)
 618                  print("            }", file=f)
 619                  print("        }", file=f)
 620                  print("    }", file=f)
 621  
 622          if options.get("constructable") and not options.get("singleton"):
 623              print("    hal_set_constructor(comp_id, export_1);", file=f)
 624          print("    if(r) {", file=f)
 625          if options.get("extra_cleanup"):
 626              print("    extra_cleanup();", file=f)
 627          print("        hal_exit(comp_id);", file=f)
 628          print("    } else {", file=f)
 629          print("        hal_ready(comp_id);", file=f)
 630          print("    }", file=f)
 631          print("    return r;", file=f)
 632          print("}", file=f)
 633  
 634          print("", file=f)
 635          print("void rtapi_app_exit(void) {", file=f)
 636          if options.get("extra_cleanup"):
 637              print("    extra_cleanup();", file=f)
 638          print("    hal_exit(comp_id);", file=f)
 639          print("}", file=f)
 640  
 641      if options.get("userspace"):
 642          print("static void user_mainloop(void);", file=f)
 643          if options.get("userinit"):
 644              print("static void userinit(int argc, char **argv);", file=f)
 645  
 646          if not options.get("singleton"):
 647              print("""
 648  int __comp_parse_count(int *argc, char **argv) {
 649      int i;
 650      for (i = 0; i < *argc; i ++) {
 651          if (strncmp(argv[i], "count=", 6) == 0) {
 652              errno = 0;
 653              count = strtoul(&argv[i][6], NULL, 0);
 654              for (; i+1 < *argc; i ++) {
 655                  argv[i] = argv[i+1];
 656              }
 657              argv[i] = NULL;
 658              (*argc)--;
 659              if (errno == 0) {
 660                  return 1;
 661              }
 662          }
 663      }
 664      return 0;
 665  }
 666  """, file=f)
 667              print("""
 668  int __comp_parse_names(int *argc, char **argv) {
 669      int i;
 670      for (i = 0; i < *argc; i ++) {
 671          if (strncmp(argv[i], "names=", 6) == 0) {
 672              char *p = &argv[i][6];
 673              int j;
 674              for (; i+1 < *argc; i ++) {
 675                  argv[i] = argv[i+1];
 676              }
 677              argv[i] = NULL;
 678              (*argc)--;
 679              for (j = 0; j < %d; j ++) {
 680                  names[j] = strtok(p, ",");
 681                  p = NULL;
 682                  if (names[j] == NULL) {
 683                      return 1;
 684                  }
 685              }
 686              return 1;
 687          }
 688      }
 689      return 0;
 690  }
 691  """%MAX_USERSPACE_NAMES, file=f)
 692          print("int argc=0; char **argv=0;", file=f)
 693          print("int main(int argc_, char **argv_) {"    , file=f)
 694          print("    argc = argc_; argv = argv_;", file=f)
 695          if not options.get("singleton"):
 696              print("    int found_count, found_names;", file=f)
 697              print("    found_count = __comp_parse_count(&argc, argv);", file=f)
 698              print("    found_names = __comp_parse_names(&argc, argv);", file=f)
 699              print("    if (found_count && found_names) {", file=f)
 700              print("        rtapi_print_msg(RTAPI_MSG_ERR, \"count= and names= are mutually exclusive\\n\");", file=f)
 701              print("        return 1;", file=f)
 702              print("    }", file=f)
 703          if options.get("userinit", 0):
 704              print("    userinit(argc, argv);", file=f)
 705          print("", file=f)
 706          print("    if(rtapi_app_main() < 0) return 1;", file=f)
 707          print("    user_mainloop();", file=f)
 708          print("    rtapi_app_exit();", file=f)
 709          print("    return 0;", file=f)
 710          print("}", file=f)
 711  
 712      print("", file=f)
 713      if not options.get("no_convenience_defines"):
 714          print("#undef FUNCTION", file=f)
 715          print("#define FUNCTION(name) static void name(struct __comp_state *__comp_inst, long period)", file=f)
 716          print("#undef EXTRA_SETUP", file=f)
 717          print("#define EXTRA_SETUP() static int extra_setup(struct __comp_state *__comp_inst, char *prefix, long extra_arg)", file=f)
 718          print("#undef EXTRA_CLEANUP", file=f)
 719          print("#define EXTRA_CLEANUP() static void extra_cleanup(void)", file=f)
 720          print("#undef fperiod", file=f)
 721          print("#define fperiod (period * 1e-9)", file=f)
 722          for name, type, array, dir, value, personality in pins:
 723              print("#undef %s" % to_c(name), file=f)
 724              if array:
 725                  if dir == 'in':
 726                      print("#define %s(i) (0+*(__comp_inst->%s[i]))" % (to_c(name), to_c(name)), file=f)
 727                  else:
 728                      print("#define %s(i) (*(__comp_inst->%s[i]))" % (to_c(name), to_c(name)), file=f)
 729              else:
 730                  if dir == 'in':
 731                      print("#define %s (0+*__comp_inst->%s)" % (to_c(name), to_c(name)), file=f)
 732                  else:
 733                      print("#define %s (*__comp_inst->%s)" % (to_c(name), to_c(name)), file=f)
 734          for name, type, array, dir, value, personality in params:
 735              print("#undef %s" % to_c(name), file=f)
 736              if array:
 737                  print("#define %s(i) (__comp_inst->%s[i])" % (to_c(name), to_c(name)), file=f)
 738              else:
 739                  print("#define %s (__comp_inst->%s)" % (to_c(name), to_c(name)), file=f)
 740  
 741          for type, name, array, value in variables:
 742              name = name.replace("*", "")
 743              print("#undef %s" % name, file=f)
 744              print("#define %s (__comp_inst->%s)" % (name, name), file=f)
 745  
 746          if has_data:
 747              print("#undef data", file=f)
 748              print("#define data (*(%s*)(__comp_inst->_data))" % options['data'], file=f)
 749          if has_personality:
 750              print("#undef personality", file=f)
 751              print("#define personality (__comp_inst->_personality)", file=f)
 752  
 753          if options.get("userspace"):
 754              print("#undef FOR_ALL_INSTS", file=f)
 755              if options.get("singleton"):
 756                  print("#define __comp_inst __comp_first_inst", file=f)
 757                  print("#define FOR_ALL_INSTS()", file=f)
 758              else:
 759                  print("#define FOR_ALL_INSTS() struct __comp_state *__comp_inst; for(__comp_inst = __comp_first_inst; __comp_inst; __comp_inst = __comp_inst->_next)", file=f)
 760      print("", file=f)
 761      print("", file=f)
 762  
 763  def epilogue(f):
 764      data = options.get('data')
 765      print("", file=f)
 766      if data:
 767          print("static int __comp_get_data_size(void) { return sizeof(%s); }" % data, file=f)
 768      else:
 769          print("static int __comp_get_data_size(void) { return 0; }", file=f)
 770  
 771  INSTALL, COMPILE, PREPROCESS, DOCUMENT, INSTALLDOC, VIEWDOC, MODINC = range(7)
 772  modename = ("install", "compile", "preprocess", "document", "installdoc", "viewdoc", "print-modinc")
 773  
 774  modinc = None
 775  def find_modinc():
 776      global modinc
 777      if modinc: return modinc
 778      d = os.path.abspath(os.path.dirname(os.path.dirname(sys.argv[0])))
 779      for e in ['src', 'etc/linuxcnc', '/etc/linuxcnc', 'share/linuxcnc']:
 780          e = os.path.join(d, e, 'Makefile.modinc')
 781          if os.path.exists(e):
 782              modinc = e
 783              return e
 784      raise SystemExit("Unable to locate Makefile.modinc")
 785  
 786  def build_usr(tempdir, filename, mode, origfilename):
 787      binname = os.path.basename(os.path.splitext(filename)[0])
 788  
 789      makefile = os.path.join(tempdir, "Makefile")
 790      f = open(makefile, "w")
 791      print("%s: %s" % (binname, filename), file=f)
 792      print("\t$(CC) $(EXTRA_CFLAGS) -URTAPI -U__MODULE__ -DULAPI -Os %s -o $@ $< -Wl,-rpath,$(LIBDIR) -L$(LIBDIR) -llinuxcnchal %s" % (
 793          options.get("extra_compile_args", ""),
 794          options.get("extra_link_args", "")), file=f)
 795      print("include %s" % find_modinc(), file=f)
 796      f.close()
 797      result = os.system("cd %s && make -S %s" % (tempdir, binname))
 798      if result != 0:
 799          raise SystemExit(os.WEXITSTATUS(result) or 1)
 800      output = os.path.join(tempdir, binname)
 801      if mode == INSTALL:
 802          shutil.copy(output, os.path.join(BASE, "bin", binname))
 803      elif mode == COMPILE:
 804          shutil.copy(output, os.path.join(os.path.dirname(origfilename),binname))
 805  
 806  def build_rt(tempdir, filename, mode, origfilename):
 807      objname = os.path.basename(os.path.splitext(filename)[0] + ".o")
 808      makefile = os.path.join(tempdir, "Makefile")
 809      f = open(makefile, "w")
 810      print("obj-m += %s" % objname, file=f)
 811      print("include %s" % find_modinc(), file=f)
 812      print("EXTRA_CFLAGS += -I%s" % os.path.abspath(os.path.dirname(origfilename)), file=f)
 813      print("EXTRA_CFLAGS += -I%s" % os.path.abspath('.'), file=f)
 814      f.close()
 815      if mode == INSTALL:
 816          target = "modules install"
 817      else:
 818          target = "modules"
 819      result = os.system("cd %s && make -S %s" % (tempdir, target))
 820      if result != 0:
 821          raise SystemExit(os.WEXITSTATUS(result) or 1)
 822      if mode == COMPILE:
 823          for extension in ".ko", ".so", ".o":
 824              kobjname = os.path.splitext(filename)[0] + extension
 825              if os.path.exists(kobjname):
 826                  shutil.copy(kobjname, os.path.basename(kobjname))
 827                  break
 828          else:
 829              raise SystemExit("Unable to copy module from temporary directory")
 830  
 831  def finddoc(section=None, name=None):
 832      for item in docs:
 833          if ((section == None or section == item[0]) and
 834                  (name == None or name == item[1])): return item
 835      return None
 836  
 837  def finddocs(section=None, name=None):
 838      for item in docs:
 839          if ((section == None or section == item[0]) and
 840                  (name == None or name == item[1])):
 841                      yield item
 842  
 843  def to_hal_man_unnumbered(s):
 844      s = "%s.%s" % (comp_name, s)
 845      s = s.replace("_", "-")
 846      s = s.rstrip("-")
 847      s = s.rstrip(".")
 848      s = re.sub("#+", lambda m: "\\fI" + "M" * len(m.group(0)) + "\\fB", s)
 849      # s = s.replace("-", "\\-")
 850      return s
 851  
 852  
 853  def to_hal_man(s):
 854      if options.get("singleton"):
 855          s = "%s.%s" % (comp_name, s)
 856      else:
 857          s = "%s.\\fIN\\fB.%s" % (comp_name, s)
 858      s = s.replace("_", "-")
 859      s = s.rstrip("-")
 860      s = s.rstrip(".")
 861      s = re.sub("#+", lambda m: "\\fI" + "M" * len(m.group(0)) + "\\fB", s)
 862      # s = s.replace("-", "\\-")
 863      return s
 864  
 865  def document(filename, outfilename):
 866      if outfilename is None:
 867          outfilename = os.path.splitext(filename)[0] + ".9"
 868  
 869      a, b = parse(filename)
 870      f = open(outfilename, "w")
 871  
 872      has_personality = False
 873      for name, type, array, dir, value, personality in pins:
 874          if personality: has_personality = True
 875          if isinstance(array, tuple): has_personality = True
 876      for name, type, array, dir, value, personality in params:
 877          if personality: has_personality = True
 878          if isinstance(array, tuple): has_personality = True
 879  
 880      print(".TH %s \"9\" \"%s\" \"LinuxCNC Documentation\" \"HAL Component\"" % (
 881          comp_name.upper(), time.strftime("%F")), file=f)
 882      print(".de TQ\n.br\n.ns\n.TP \\\\$1\n..\n", file=f)
 883  
 884      print(".SH NAME\n", file=f)
 885      doc = finddoc('component')    
 886      if doc and doc[2]:
 887          if '\n' in doc[2]:
 888              firstline, rest = doc[2].split('\n', 1)
 889          else:
 890              firstline = doc[2]
 891              rest = ''
 892          print("%s \\- %s" % (doc[1], firstline), file=f)
 893      else:
 894          rest = ''
 895          print("%s" % doc[1], file=f)
 896  
 897  
 898      print(".SH SYNOPSIS", file=f)
 899      if options.get("userspace"):
 900          print(".B %s" % comp_name, file=f)
 901      else:
 902          if rest:
 903              print(rest, file=f)
 904          else:
 905              print(".HP", file=f)
 906              if options.get("singleton") or options.get("count_function"):
 907                  if has_personality:
 908                      print(".B loadrt %s personality=\\fIP\\fB" % comp_name, end='', file=f)
 909                  else:
 910                      print(".B loadrt %s" % comp_name, end='', file=f)
 911              else:
 912                  if has_personality:
 913                      print(".B loadrt %s [count=\\fIN\\fB|names=\\fIname1\\fB[,\\fIname2...\\fB]] [personality=\\fIP,P,...\\fB]" % comp_name, end='', file=f)
 914                  else:
 915                      print(".B loadrt %s [count=\\fIN\\fB|names=\\fIname1\\fB[,\\fIname2...\\fB]]" % comp_name, end='', file=f)
 916              for type, name, default, doc in modparams:
 917                  print(" [%s=\\fIN\\fB]" % name, end='', file=f)
 918              print("", file=f)
 919  
 920              hasparamdoc = False
 921              for type, name, default, doc in modparams:
 922                  if doc: hasparamdoc = True
 923  
 924              if hasparamdoc:
 925                  print(".RS 4", file=f)
 926                  for type, name, default, doc in modparams:
 927                      print(".TP", file=f)
 928                      print("\\fB%s\\fR" % name, end='', file=f)
 929                      if default:
 930                          print(" [default: %s]" % default, file=f)
 931                      else:
 932                          print("", file=f)
 933                      print(doc, file=f)
 934                  print(".RE", file=f)
 935  
 936          if options.get("constructable") and not options.get("singleton"):
 937              print(".PP\n.B newinst %s \\fIname\\fB" % comp_name, file=f)
 938  
 939      doc = finddoc('descr')    
 940      if doc and doc[1]:
 941          print(".SH DESCRIPTION\n", file=f)
 942          print("%s" % doc[1], file=f)
 943  
 944      if functions:
 945          print(".SH FUNCTIONS", file=f)
 946          for _, name, fp, doc in finddocs('funct'):
 947              print(".TP", file=f)
 948              print("\\fB%s\\fR" % to_hal_man(name), end='', file=f)
 949              if fp:
 950                  print(" (requires a floating-point thread)", file=f)
 951              else:
 952                  print("", file=f)
 953              print(doc, file=f)
 954  
 955      lead = ".TP"
 956      print(".SH PINS", file=f)
 957      for _, name, type, array, dir, doc, value, personality in finddocs('pin'):
 958          print(lead, file=f)
 959          print(".B %s\\fR" % to_hal_man(name), end=' ', file=f)
 960          print(type, dir, end=' ', file=f)
 961          if array:
 962              sz = name.count("#")
 963              if isinstance(array, tuple):
 964                  print(" (%s=%0*d..%s)" % ("M" * sz, sz, 0, array[1]), end=' ', file=f)
 965              else:
 966                  print(" (%s=%0*d..%0*d)" % ("M" * sz, sz, 0, sz, array-1), end=' ', file=f)
 967          if personality:
 968              print(" [if %s]" % personality, end=' ', file=f)
 969          if value:
 970              print("\\fR(default: \\fI%s\\fR)" % value, file=f)
 971          else:
 972              print("\\fR", file=f)
 973          if doc:
 974              print(doc, file=f)
 975              lead = ".TP"
 976          else:
 977              lead = ".TQ"
 978  
 979      lead = ".TP"
 980      if params:
 981          print(".SH PARAMETERS", file=f)
 982          for _, name, type, array, dir, doc, value, personality in finddocs('param'):
 983              print(lead, file=f)
 984              print(".B %s\\fR" % to_hal_man(name), end=' ', file=f)
 985              print(type, dir, end=' ', file=f)
 986              if array:
 987                  sz = name.count("#")
 988                  if isinstance(array, tuple):
 989                      print(" (%s=%0*d..%s)" % ("M" * sz, sz, 0, array[1]), end=' ', file=f)
 990                  else:
 991                      print(" (%s=%0*d..%0*d)" % ("M" * sz, sz, 0, sz, array-1), end=' ', file=f)
 992              if personality:
 993                  print(" [if %s]" % personality, end=' ', file=f)
 994              if value:
 995                  print("\\fR(default: \\fI%s\\fR)" % value, file=f)
 996              else:
 997                  print("\\fR", file=f)
 998              if doc:
 999                  print(doc, file=f)
1000                  lead = ".TP"
1001              else:
1002                  lead = ".TQ"
1003  
1004      doc = finddoc('see_also')    
1005      if doc and doc[1]:
1006          print(".SH SEE ALSO\n", file=f)
1007          print("%s" % doc[1], file=f)
1008  
1009      doc = finddoc('notes')    
1010      if doc and doc[1]:
1011          print(".SH NOTES\n", file=f)
1012          print("%s" % doc[1], file=f)
1013  
1014      doc = finddoc('author')    
1015      if doc and doc[1]:
1016          print(".SH AUTHOR\n", file=f)
1017          print("%s" % doc[1], file=f)
1018  
1019      doc = finddoc('license')    
1020      if doc and doc[1]:
1021          print(".SH LICENSE\n", file=f)
1022          print("%s" % doc[1], file=f)
1023  
1024  def process(filename, mode, outfilename):
1025      tempdir = tempfile.mkdtemp()
1026      try:
1027          if outfilename is None:
1028              if mode == PREPROCESS:
1029                  outfilename = os.path.splitext(filename)[0] + ".c"
1030              else:
1031                  outfilename = os.path.join(tempdir,
1032                      os.path.splitext(os.path.basename(filename))[0] + ".c")
1033  
1034          a, b = parse(filename)
1035          base_name = os.path.splitext(os.path.basename(outfilename))[0]
1036          if comp_name != base_name:
1037              raise SystemExit("Component name (%s) does not match filename (%s)" % (comp_name, base_name))
1038  
1039          f = open(outfilename, "w")
1040  
1041          if options.get("userinit") and not options.get("userspace"):
1042              print("Warning: comp '%s' sets 'userinit' without 'userspace', ignoring" % filename, file=sys.stderr)
1043  
1044          if options.get("userspace"):
1045              if functions:
1046                  raise SystemExit("Userspace components may not have functions")
1047          if not pins:
1048              raise SystemExit("Component must have at least one pin")
1049          prologue(f)
1050          lineno = a.count("\n") + 3
1051  
1052          if options.get("userspace"):
1053              if functions:
1054                  raise SystemExit("May not specify functions with a userspace component.")
1055              f.write("#line %d \"%s\"\n" % (lineno, filename))
1056              f.write(b)
1057          else:
1058              if not functions or "FUNCTION" in b:
1059                  f.write("#line %d \"%s\"\n" % (lineno, filename))
1060                  f.write(b)
1061              elif len(functions) == 1:
1062                  f.write("FUNCTION(%s) {\n" % functions[0][0])
1063                  f.write("#line %d \"%s\"\n" % (lineno, filename))
1064                  f.write(b)
1065                  f.write("}\n")
1066              else:
1067                  raise SystemExit("Must use FUNCTION() when more than one function is defined")
1068          epilogue(f)
1069          f.close()
1070  
1071          if mode != PREPROCESS:
1072              if options.get("userspace"):
1073                  build_usr(tempdir, outfilename, mode, filename)
1074              else:
1075                  build_rt(tempdir, outfilename, mode, filename)
1076  
1077      finally:
1078          shutil.rmtree(tempdir) 
1079  
1080  def usage(exitval=0):
1081      print("""%(name)s: Build, compile, and install LinuxCNC HAL components
1082  
1083  Usage:
1084             %(name)s [--compile|--preprocess|--document|--view-doc] compfile...
1085      [sudo] %(name)s [--install|--install-doc] compfile...
1086             %(name)s --compile --userspace cfile...
1087      [sudo] %(name)s --install --userspace cfile...
1088      [sudo] %(name)s --install --userspace pyfile...
1089             %(name)s --print-modinc
1090  
1091  Option to set maximum 'personalities' items:
1092      --personalities=integer_value   (default is %(dflt)d)
1093  """ % {'name': os.path.basename(sys.argv[0]),'dflt':MAX_PERSONALITIES})
1094      raise SystemExit(exitval)
1095  
1096  def main():
1097      global require_license
1098      global MAX_USERSPACE_NAMES
1099      global MAX_PERSONALITIES
1100      require_license = True
1101      global require_unix_line_endings
1102      require_unix_line_endings = False
1103      mode = PREPROCESS
1104      outfile = None
1105      userspace = False
1106      try:
1107          opts, args = getopt.getopt(sys.argv[1:], "Uluijcpdo:h?P:",
1108                             ['unix', 'install', 'compile', 'preprocess', 'outfile=',
1109                              'document', 'help', 'userspace', 'install-doc',
1110                              'view-doc', 'require-license', 'print-modinc',
1111                              'personalities='])
1112      except getopt.GetoptError:
1113          usage(1)
1114  
1115      for k, v in opts:
1116          if k in ("-U", "--unix"):
1117              require_unix_line_endings = True
1118          if k in ("-u", "--userspace"):
1119              userspace = True
1120          if k in ("-i", "--install"):
1121              mode = INSTALL
1122          if k in ("-c", "--compile"):
1123              mode = COMPILE
1124          if k in ("-p", "--preprocess"):
1125              mode = PREPROCESS
1126          if k in ("-d", "--document"):
1127              mode = DOCUMENT
1128          if k in ("-j", "--install-doc"):
1129              mode = INSTALLDOC
1130          if k in ("-j", "--view-doc"):
1131              mode = VIEWDOC
1132          if k in ("--print-modinc",):
1133              mode = MODINC
1134          if k in ("-l", "--require-license"):
1135              require_license = True
1136          if k in ("-o", "--outfile"):
1137              if len(args) != 1:
1138                  raise SystemExit("Cannot specify -o with multiple input files")
1139              outfile = v 
1140          if k in ("-P", "--personalities"):
1141              try:
1142                  MAX_PERSONALITIES = int(v)
1143                  print("MAX_PERSONALITIES=%d"%(MAX_PERSONALITIES))
1144              except Exception as detail:
1145                  raise SystemExit("Bad value for -P (--personalities)=",v,"\n",detail)
1146          if k in ("-?", "-h", "--help"):
1147              usage(0)
1148  
1149      if outfile and mode != PREPROCESS and mode != DOCUMENT:
1150          raise SystemExit("Can only specify -o when preprocessing or documenting")
1151  
1152      if mode == MODINC:
1153          if args:
1154              raise SystemExit(
1155                  "Can not specify input files when using --print-modinc")
1156          print(find_modinc())
1157          return 0
1158  
1159      for f in args:
1160          try:
1161              basename = os.path.basename(os.path.splitext(f)[0])
1162              if f.endswith(".comp") and mode == DOCUMENT:
1163                  document(f, outfile)            
1164              elif f.endswith(".comp") and mode == VIEWDOC:
1165                  tempdir = tempfile.mkdtemp()
1166                  try:
1167                      outfile = os.path.join(tempdir, basename + ".9")
1168                      document(f, outfile)
1169                      os.spawnvp(os.P_WAIT, "man", ["man", outfile])
1170                  finally:
1171                      shutil.rmtree(tempdir)
1172              elif f.endswith(".comp") and mode == INSTALLDOC:
1173                  manpath = os.path.join(BASE, "share/man/man9")
1174                  if not os.path.isdir(manpath):
1175                      manpath = os.path.join(BASE, "docs/man/man9")
1176                  outfile = os.path.join(manpath, basename + ".9")
1177                  print("INSTALLDOC", outfile)
1178                  document(f, outfile)            
1179              elif f.endswith(".comp"):
1180                  process(f, mode, outfile)
1181              elif f.endswith(".py") and mode == INSTALL:
1182                  lines = open(f).readlines()
1183                  if lines[0].startswith("#!"): del lines[0]
1184                  lines[0] = "#!%s\n" % sys.executable
1185                  outfile = os.path.join(BASE, "bin", basename)
1186                  try: os.unlink(outfile)
1187                  except os.error: pass
1188                  open(outfile, "w").writelines(lines)
1189                  os.chmod(outfile, 0o555)
1190              elif f.endswith(".c") and mode != PREPROCESS:
1191                  initialize()
1192                  tempdir = tempfile.mkdtemp()
1193                  try:
1194                      shutil.copy(f, tempdir)
1195                      if userspace:
1196                          build_usr(tempdir, os.path.join(tempdir, os.path.basename(f)), mode, f)
1197                      else:
1198                          build_rt(tempdir, os.path.join(tempdir, os.path.basename(f)), mode, f)
1199                  finally:
1200                      shutil.rmtree(tempdir) 
1201              else:
1202                  raise SystemExit("Unrecognized file type for mode %s: %r" % (modename[mode], f))
1203          except Exception as e:
1204              try:
1205                  if outfile is not None: os.unlink(outfile)
1206              except: # os.error:
1207                  pass
1208              raise
1209  if __name__ == '__main__':
1210      main()
1211  
1212  # vim:sw=4:sts=4:et