modhelp.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. #!/usr/bin/env python
  2. # Module helper script.
  3. # Copyright 2015-2016 Free Software Foundation, Inc.
  4. # This file is part of GNU Emacs.
  5. # GNU Emacs 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 3 of the License, or
  8. # (at your option) any later version.
  9. # GNU Emacs is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. # You should have received a copy of the GNU General Public License
  14. # along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  15. import os
  16. import string
  17. import subprocess as sp
  18. import argparse
  19. import re
  20. EMACS = os.path.join('..', 'src', 'emacs')
  21. def find_modules():
  22. modpaths = []
  23. for (dirname, dirs, files) in os.walk('.'):
  24. if 'Makefile' in files:
  25. modpaths.append(dirname)
  26. return modpaths
  27. def cmd_test(args):
  28. mods = args.module
  29. if not mods:
  30. mods = find_modules()
  31. make_cmd = ['make']
  32. if args.force:
  33. make_cmd.append('-B')
  34. failed = []
  35. for m in mods:
  36. print '[*] %s: ------- start -------' % m
  37. print '[*] %s: running make' % m
  38. r = sp.call(make_cmd, cwd=m)
  39. if r != 0:
  40. print '[E] %s: make failed' % m
  41. failed += [m]
  42. continue
  43. print '[*] %s: running test' % m
  44. testpath = os.path.join(m, 'test.el')
  45. if os.path.isfile(testpath):
  46. emacs_cmd = [EMACS, '-batch', '-L', '.', '-l', 'ert',
  47. '-l', testpath, '-f', 'ert-run-tests-batch-and-exit']
  48. print ' '.join(emacs_cmd)
  49. r = sp.call(emacs_cmd)
  50. if r != 0:
  51. print '[E] %s: test failed' % m
  52. failed += [m]
  53. continue
  54. else:
  55. print '[W] %s: no test to run' % m
  56. print '\n[*] %d/%d MODULES OK' % (len(mods)-len(failed), len(mods))
  57. for m in failed:
  58. print '\tfailed: %s' % m
  59. def to_lisp_sym(sym):
  60. sym = re.sub('[_ ]', '-', sym)
  61. return sym
  62. def to_c_sym(sym):
  63. sym = re.sub('[- ]', '_', sym)
  64. return sym
  65. def cmd_init(args):
  66. if os.path.exists(args.module):
  67. print "%s: file/dir '%s' already exists" % (__file__, args.module)
  68. return
  69. os.mkdir(args.module)
  70. template_vars = {
  71. 'module': args.module,
  72. 'func': args.fun,
  73. 'c_file': '%s.c' % args.module,
  74. 'c_func': 'F%s_%s' % (to_c_sym(args.module), to_c_sym(args.fun)),
  75. 'lisp_func': '%s-%s' % (args.module, to_lisp_sym(args.fun)),
  76. }
  77. for path, t in TEMPLATES.items():
  78. if isinstance(path, string.Template):
  79. path = path.substitute(template_vars)
  80. path = os.path.join(args.module, path)
  81. print "writing %s..." % path
  82. with open(path, "w+") as f:
  83. f.write(t.substitute(template_vars))
  84. print "done! you can run %s test %s" % (__file__, args.module)
  85. def main():
  86. # path always written relative to this file
  87. os.chdir(os.path.dirname(os.path.realpath(__file__)))
  88. mainp = argparse.ArgumentParser()
  89. subp = mainp.add_subparsers()
  90. testp = subp.add_parser('test', help='run tests')
  91. testp.add_argument('-f', '--force', action='store_true',
  92. help='force regeneration (make -B)')
  93. testp.add_argument('module', nargs='*',
  94. help='path to module to test (default all)')
  95. testp.set_defaults(func=cmd_test)
  96. initp = subp.add_parser('init', help='create a test module from a template')
  97. initp.add_argument('module', help='name of the new module')
  98. initp.add_argument('-f', '--fun', default='fun',
  99. help='override name of the default function')
  100. initp.set_defaults(func=cmd_init)
  101. args = mainp.parse_args()
  102. args.func(args)
  103. # double the $ to escape python template syntax
  104. TEMPLATES = {
  105. 'Makefile': string.Template('''
  106. ROOT = ../..
  107. CC = gcc
  108. LD = gcc
  109. CFLAGS = -ggdb3 -Wall
  110. LDFLAGS =
  111. all: ${module}.so ${module}.doc
  112. %.so: %.o
  113. $$(LD) -shared $$(LDFLAGS) -o $$@ $$<
  114. %.o: %.c
  115. $$(CC) $$(CFLAGS) -I$$(ROOT)/src -fPIC -c $$<
  116. '''),
  117. string.Template('${c_file}'): string.Template('''
  118. #include <emacs-module.h>
  119. int plugin_is_GPL_compatible;
  120. static emacs_value
  121. ${c_func} (emacs_env *env, int nargs, emacs_value args[], void *data)
  122. {
  123. return env->intern (env, "t");
  124. }
  125. /* Bind NAME to FUN. */
  126. static void
  127. bind_function (emacs_env *env, const char *name, emacs_value Sfun)
  128. {
  129. emacs_value Qfset = env->intern (env, "fset");
  130. emacs_value Qsym = env->intern (env, name);
  131. emacs_value args[] = { Qsym, Sfun };
  132. env->funcall (env, Qfset, 2, args);
  133. }
  134. /* Provide FEATURE to Emacs. */
  135. static void
  136. provide (emacs_env *env, const char *feature)
  137. {
  138. emacs_value Qfeat = env->intern (env, feature);
  139. emacs_value Qprovide = env->intern (env, "provide");
  140. emacs_value args[] = { Qfeat };
  141. env->funcall (env, Qprovide, 1, args);
  142. }
  143. int
  144. emacs_module_init (struct emacs_runtime *ert)
  145. {
  146. emacs_env *env = ert->get_environment (ert);
  147. bind_function (env, "${lisp_func}",
  148. env->make_function (env, 1, 1, ${c_func}, "doc", NULL));
  149. provide (env, "${module}");
  150. return 0;
  151. }
  152. '''),
  153. 'test.el': string.Template('''
  154. (require 'ert)
  155. (require 'module-test-common)
  156. ;; #$$ works when loading, buffer-file-name when evaluating from emacs
  157. (module-load (module-path (or #$$ (expand-file-name (buffer-file-name)))))
  158. (ert-deftest ${lisp_func}-test ()
  159. (should (eq (${lisp_func} 42) t)))
  160. ''')
  161. }
  162. if __name__ == '__main__':
  163. main()