]> sigrok.org Git - sigrok-util.git/blob - source/new-driver
new-driver: Print driver "short" name in configure summary.
[sigrok-util.git] / source / new-driver
1 #!/usr/bin/python3
2 ##
3 ## This file is part of the sigrok-util project.
4 ##
5 ## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
6 ##
7 ## This program is free software: you can redistribute it and/or modify
8 ## it under the terms of the GNU General Public License as published by
9 ## the Free Software Foundation, either version 3 of the License, or
10 ## (at your option) any later version.
11 ##
12 ## This program is distributed in the hope that it will be useful,
13 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 ## GNU General Public License for more details.
16 ##
17 ## You should have received a copy of the GNU General Public License
18 ## along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 ##
20
21 import os
22 import sys
23 import tempfile
24 from subprocess import Popen, PIPE
25 import shutil
26 import re
27 import socket
28 import datetime
29
30 TMPL_AUTOCONF_AC_ARG_ENABLE = """\
31 AC_ARG_ENABLE(${short}, AC_HELP_STRING([--enable-${short}],
32         [enable ${name} support [default=yes]]),
33         [HW_${upper}="$enableval"],
34         [HW_${upper}=yes])
35 AM_CONDITIONAL(HW_${upper}, test x$HW_${upper} = xyes)
36 if test "x$HW_${upper}" = "xyes"; then
37         AC_DEFINE(HAVE_HW_${upper}, 1, [${name} support])
38 fi
39
40 """
41 TMPL_AUTOCONF_AC_CONFIG_FILES = "\t\t hardware/${short}/Makefile\n"
42 TMPL_AUTOCONF_SUMMARY = 'echo "  - ${summary}"\n'
43 TMPL_HWMAKE_SUBDIR = '\t${short}'
44 TMPL_HWMAKE_DRIVERLIB = """if HW_${upper}
45 libsigrokhardware_la_LIBADD += ${short}/libsigrok_hw_${lib}.la
46 endif
47
48 """
49 TMPL_HWDRIVER_EXTERN = """\
50 #ifdef HAVE_HW_${upper}
51 extern SR_PRIV struct sr_dev_driver ${lib}_driver_info;
52 #endif
53 """
54 TMPL_HWDRIVER_DIPTR = """\
55 #ifdef HAVE_HW_${upper}
56         &${lib}_driver_info,
57 #endif
58 """
59 FILE_DRV_MAKEFILE = 'drv-Makefile.am'
60 FILE_DRV_API = 'drv-api.c'
61 FILE_DRV_PROTOCOL = 'drv-protocol.c'
62 FILE_DRV_PROTOCOL_H = 'drv-protocol.h'
63
64 def tmpl(template):
65     out = re.sub(r'\${([^}]+)}', lambda x: str(names[x.group(1)]), template)
66
67     return out
68
69
70 def tmpl_file(filename):
71     template = open(TMPLDIR + '/' + filename).read()
72
73     return tmpl(template)
74
75
76 def new_driver():
77     tmp = tempfile.mkdtemp()
78     try:
79         os.chdir(tmp)
80         out, err = Popen("git clone " + LIBSR, shell=True, stderr=PIPE).communicate()
81         if err:
82             raise Exception(err.decode())
83         gitdir = tmp + '/libsigrok/'
84         do_configure_ac(gitdir)
85         do_hwdriver(gitdir)
86         do_hwmake(gitdir)
87         do_driverskel(gitdir)
88         make_patch(gitdir)
89     except Exception as e:
90         print(e)
91     shutil.rmtree(tmp)
92
93
94 def do_configure_ac(gitdir):
95     cacpath = gitdir + 'configure.ac'
96     configure_ac = open(cacpath).read()
97
98     # add AC_ARG_ENABLE option
99     out = ''
100     state = 'copy'
101     for line in configure_ac.split('\n')[:-1]:
102         if state == 'copy':
103             if line == "# Hardware support '--enable' options.":
104                 state = 'acarg'
105         elif state == 'acarg':
106             m = re.match('AC_ARG_ENABLE\(([^,]+)', line)
107             if m:
108                 drv_short = m.group(1)
109                 if drv_short.lower() > names['short']:
110                     out += tmpl(TMPL_AUTOCONF_AC_ARG_ENABLE)
111                     state = 'done'
112             if line == '# Checks for libraries.':
113                 # new one at the end
114                 out += tmpl(TMPL_AUTOCONF_AC_ARG_ENABLE)
115                 state = 'done'
116         out += line + '\n'
117     if state != 'done':
118         raise Exception('AC_ARG_ENABLE markers not found in configure.ac')
119     configure_ac = out
120
121     # add driver Makefile to AC_CONFIG_FILES
122     out = ''
123     state = 'copy'
124     for line in configure_ac.split('\n')[:-1]:
125         if state == 'copy':
126             if line.find("AC_CONFIG_FILES([Makefile") > -1:
127                 state = 'acconf'
128         elif state == 'acconf':
129             m = re.match('\t\t hardware/([^/]+)/Makefile', line)
130             if m:
131                 drv_short = m.group(1)
132                 if drv_short.lower() > names['short']:
133                     out += tmpl(TMPL_AUTOCONF_AC_CONFIG_FILES)
134                     state = 'done'
135             else:
136                 # new one at the end
137                 out += tmpl(TMPL_AUTOCONF_AC_CONFIG_FILES)
138                 state = 'done'
139         out += line + '\n'
140     if state != 'done':
141         raise Exception('AC_CONFIG_FILES marker not found in configure.ac')
142     configure_ac = out
143
144     # add summary line
145     out = ''
146     state = 'copy'
147     names['summary'] = "%s%s $HW_%s" % (names['short'],
148             '.' * (32 - len(names['name'])), names['upper'])
149     for line in configure_ac.split('\n')[:-1]:
150         if state == 'copy':
151             if line.find('Enabled hardware drivers') > -1:
152                 state = 'echo'
153         elif state == 'echo':
154             m = re.match('echo "  - ([^\.]+)', line)
155             if m:
156                 drv_short = m.group(1)
157                 if drv_short.lower() > names['name'].lower():
158                     out += tmpl(TMPL_AUTOCONF_SUMMARY)
159                     state = 'done'
160             else:
161                 # new one at the end
162                 out += tmpl(TMPL_AUTOCONF_SUMMARY)
163                 state = 'done'
164         out += line + '\n'
165     if state != 'done':
166         raise Exception('summary marker not found in configure.ac')
167     configure_ac = out
168
169     open(cacpath, 'w').write(configure_ac)
170
171
172 def do_hwdriver(gitdir):
173     path = gitdir + 'hwdriver.c'
174     hwdriver = open(path).read()
175     # add HAVE_HW_thing extern and pointers
176     out = ''
177     state = 'copy'
178     for line in hwdriver.split('\n')[:-1]:
179         if state == 'copy':
180             if line.find('/** @cond PRIVATE */') == 0:
181                 state = 'extern'
182             elif line.find('static struct sr_dev_driver *drivers_list') == 0:
183                 state = 'diptr'
184         elif state in ('extern', 'diptr'):
185             if state == 'extern':
186                 entry = tmpl(TMPL_HWDRIVER_EXTERN)
187                 next_state = 'copy'
188             else:
189                 entry = tmpl(TMPL_HWDRIVER_DIPTR)
190                 next_state = 'done'
191             m = re.match('#ifdef HAVE_.._(.*)', line)
192             if m:
193                 drv = m.group(1)
194                 if drv > names['upper']:
195                     out += entry
196                     state = next_state
197             elif not re.match('(extern|\t&|#endif)', line):
198                 # new one at the end
199                 out += entry
200                 state = next_state
201         out += line + '\n'
202     if state != 'done':
203         raise Exception('HAVE_* markers not found in hwdriver.c')
204     hwdriver = out
205
206     open(path, 'w').write(hwdriver)
207
208
209 def do_hwmake(gitdir):
210     path = gitdir + 'hardware/Makefile.am'
211     hwmake = open(path).read()
212
213     # add SUBDIRS entry
214     out = ''
215     state = 'copy'
216     for line in hwmake.split('\n')[:-1]:
217         if state == 'copy':
218             if line.find('SUBDIRS =') > -1:
219                 state = 'subdirs'
220         elif state == 'subdirs':
221             m = re.match('\t([^ \\\]+\s*\\\)', line)
222             if m:
223                 drv_short = m.group(1)
224                 if drv_short.lower() > names['short']:
225                     out += tmpl(TMPL_HWMAKE_SUBDIR) + ' \\\n'
226                     state = 'driverlib'
227             else:
228                 out += tmpl(TMPL_HWMAKE_SUBDIR) + ' \\\n'
229                 state = 'driverlib'
230         elif state == 'driverlib':
231             m = re.match('if [A-Z]{2}_(.*)$', line)
232             if m:
233                 drv_short = m.group(1)
234                 if drv_short > names['upper']:
235                     out += tmpl(TMPL_HWMAKE_DRIVERLIB)
236                     state = 'done'
237         out += line + '\n'
238     if state == 'driverlib':
239         # reached end of file, add it in here
240         out += tmpl(TMPL_HWMAKE_DRIVERLIB).strip()
241     elif state != 'done':
242         raise Exception('markers not found in hardware/Makefile.am')
243     hwmake = out
244
245     open(path, 'w').write(hwmake)
246
247
248 def do_driverskel(gitdir):
249     drvdir = gitdir + 'hardware/' + names['short']
250     os.mkdir(drvdir)
251     open(drvdir + '/Makefile.am', 'w').write(tmpl_file(FILE_DRV_MAKEFILE))
252     open(drvdir + '/api.c', 'w').write(tmpl_file(FILE_DRV_API))
253     open(drvdir + '/protocol.c', 'w').write(tmpl_file(FILE_DRV_PROTOCOL))
254     open(drvdir + '/protocol.h', 'w').write(tmpl_file(FILE_DRV_PROTOCOL_H))
255
256
257 def make_patch(gitdir):
258     os.chdir(gitdir)
259     command('git add hardware/' + names['short'])
260     cmd = 'git commit -m "%s: Initial driver skeleton." ' % names['short']
261     cmd += 'configure.ac hwdriver.c hardware/Makefile.am hardware/' + names['short']
262     command(cmd)
263     cmd = "git format-patch HEAD~1"
264     out, err = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).communicate()
265     if err:
266         raise Exception(err.decode())
267     patch = out.decode().strip()
268     shutil.move(gitdir + '/' + patch, scriptdir + '/' + patch)
269     print(patch)
270
271
272 def command(cmd):
273     out, err = Popen(cmd, shell=True, stderr=PIPE).communicate()
274     if err:
275         raise Exception(err.decode())
276
277
278 def parse_gitconfig():
279     try:
280         author = email = None
281         for line in open(os.environ['HOME'] + '/.gitconfig').readlines():
282             m = re.match('\s*(\w+)\s*=\s*(.*)\s*$', line)
283             if m:
284                 key, value = m.groups()
285                 if key == 'name':
286                     author = value
287                 elif key == 'email':
288                     email = value
289                 if author and email:
290                     break
291     except:
292         pass
293     if not author or not email:
294         print("Please put your name and email in ~/.gitconfig")
295         sys.exit()
296
297     return author, email
298
299 #
300 # main
301 #
302
303 scriptdir = os.getcwd()
304 if scriptdir.split('/')[-2:] != ['sigrok-util', 'source']:
305         print("Please call this script from the 'source' directory.")
306         sys.exit(1)
307
308 LIBSR = 'git://sigrok.org/libsigrok'
309 TMPLDIR = scriptdir
310
311 if len(sys.argv) < 2:
312     print("Usage: new-driver <name>")
313     sys.exit()
314
315 author, email = parse_gitconfig()
316 name = ' '.join(sys.argv[1:])
317 names = {
318     'name': name,
319     'short': re.sub('[^a-z0-9]', '-', name.lower()),
320     'lib': re.sub('[^a-z0-9]', '_', name.lower()),
321     'upper': re.sub('[^A-Z0-9]', '_', name.upper()),
322     'libupper': re.sub('[^A-Z0-9]', '', name.upper()),
323     'year': datetime.datetime.now().year,
324     'author': author,
325     'email': email,
326 }
327 new_driver()
328