]> sigrok.org Git - sigrok-util.git/blob - source/new-driver
new-driver: Whitespace fixes in the generated Makefile.am.
[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}=$HW_ENABLED_DEFAULT])
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
44 TMPL_HWMAKE_DRIVERLIB = """if HW_${upper}
45 libsigrok_la_SOURCES += \\
46         hardware/${short}/protocol.h \\
47         hardware/${short}/protocol.c \\
48         hardware/${short}/api.c
49 endif
50 """
51 TMPL_HWDRIVER_EXTERN = """\
52 #ifdef HAVE_HW_${upper}
53 extern SR_PRIV struct sr_dev_driver ${lib}_driver_info;
54 #endif
55 """
56 TMPL_HWDRIVER_DIPTR = """\
57 #ifdef HAVE_HW_${upper}
58         &${lib}_driver_info,
59 #endif
60 """
61 FILE_DRV_API = 'drv-api.c'
62 FILE_DRV_PROTOCOL = 'drv-protocol.c'
63 FILE_DRV_PROTOCOL_H = 'drv-protocol.h'
64
65 def tmpl(template):
66     out = re.sub(r'\${([^}]+)}', lambda x: str(names[x.group(1)]), template)
67
68     return out
69
70
71 def tmpl_file(filename):
72     template = open(TMPLDIR + '/' + filename).read()
73
74     return tmpl(template)
75
76
77 def new_driver():
78     tmp = tempfile.mkdtemp()
79     try:
80         os.chdir(tmp)
81         process = Popen("git clone " + LIBSR, shell=True, stderr=PIPE)
82         out, err = process.communicate()
83         if process.returncode:
84             raise Exception(err.decode())
85         gitdir = tmp + '/libsigrok/'
86         do_configure_ac(gitdir)
87         do_hwdriver(gitdir)
88         do_hwmake(gitdir)
89         do_driverskel(gitdir)
90         make_patch(gitdir)
91     except Exception as e:
92         print(e)
93     shutil.rmtree(tmp)
94
95
96 def do_configure_ac(gitdir):
97     cacpath = gitdir + 'configure.ac'
98     configure_ac = open(cacpath).read()
99
100     # add AC_ARG_ENABLE option
101     out = ''
102     state = 'copy'
103     for line in configure_ac.split('\n')[:-1]:
104         if state == 'copy':
105             if line == "# Hardware support '--enable' options.":
106                 state = 'acarg'
107         elif state == 'acarg':
108             m = re.match('AC_ARG_ENABLE\(([^,]+)', line)
109             if m:
110                 drv_short = m.group(1)
111                 if drv_short.lower() > names['short']:
112                     out += tmpl(TMPL_AUTOCONF_AC_ARG_ENABLE)
113                     state = 'done'
114             if line == '# Checks for libraries.':
115                 # new one at the end
116                 out += tmpl(TMPL_AUTOCONF_AC_ARG_ENABLE)
117                 state = 'done'
118         out += line + '\n'
119     if state != 'done':
120         raise Exception('AC_ARG_ENABLE markers not found in configure.ac')
121     configure_ac = out
122
123     # add summary line
124     out = ''
125     state = 'copy'
126     names['summary'] = "%s%s $HW_%s" % (names['short'],
127             '.' * (32 - len(names['name'])), names['upper'])
128     for line in configure_ac.split('\n')[:-1]:
129         if state == 'copy':
130             if line.find('Enabled hardware drivers') > -1:
131                 state = 'echo'
132         elif state == 'echo':
133             m = re.match('echo "  - ([^\.]+)', line)
134             if m:
135                 drv_short = m.group(1)
136                 if drv_short.lower() > names['name'].lower():
137                     out += tmpl(TMPL_AUTOCONF_SUMMARY)
138                     state = 'done'
139             else:
140                 # new one at the end
141                 out += tmpl(TMPL_AUTOCONF_SUMMARY)
142                 state = 'done'
143         out += line + '\n'
144     if state != 'done':
145         raise Exception('summary marker not found in configure.ac')
146     configure_ac = out
147
148     open(cacpath, 'w').write(configure_ac)
149
150
151 def do_hwdriver(gitdir):
152     path = gitdir + 'hwdriver.c'
153     hwdriver = open(path).read()
154     # add HAVE_HW_thing extern and pointers
155     out = ''
156     state = 'copy'
157     for line in hwdriver.split('\n')[:-1]:
158         if state == 'copy':
159             if line.find('/** @cond PRIVATE */') == 0:
160                 state = 'extern'
161             elif line.find('static struct sr_dev_driver *drivers_list') == 0:
162                 state = 'diptr'
163         elif state in ('extern', 'diptr'):
164             if state == 'extern':
165                 entry = tmpl(TMPL_HWDRIVER_EXTERN)
166                 next_state = 'copy'
167             else:
168                 entry = tmpl(TMPL_HWDRIVER_DIPTR)
169                 next_state = 'done'
170             m = re.match('#ifdef HAVE_.._(.*)', line)
171             if m:
172                 drv = m.group(1)
173                 if drv > names['upper']:
174                     out += entry
175                     state = next_state
176             elif not re.match('(extern|\t&|#endif)', line):
177                 # new one at the end
178                 out += entry
179                 state = next_state
180         out += line + '\n'
181     if state != 'done':
182         raise Exception('HAVE_* markers not found in hwdriver.c')
183     hwdriver = out
184
185     open(path, 'w').write(hwdriver)
186
187
188 def do_hwmake(gitdir):
189     path = gitdir + 'Makefile.am'
190     hwmake = open(path).read()
191
192     # add entry at correct place in Makefile
193     out = ''
194     state = 'copy'
195     for line in hwmake.split('\n')[:-1]:
196         if state == 'copy':
197             if line.find('# Hardware drivers') > -1:
198                 state = 'driverlib'
199         elif state == 'driverlib':
200             m = re.match('if [A-Z]{2}_(.*)$', line)
201             if m:
202                 drv_short = m.group(1)
203                 if drv_short > names['upper']:
204                     out += tmpl(TMPL_HWMAKE_DRIVERLIB)
205                     state = 'done'
206         out += line + '\n'
207     if state != 'done':
208         raise Exception('markers not found in Makefile.am')
209     hwmake = out
210
211     open(path, 'w').write(hwmake)
212
213
214 def do_driverskel(gitdir):
215     drvdir = gitdir + 'hardware/' + names['short']
216     os.mkdir(drvdir)
217     open(drvdir + '/api.c', 'w').write(tmpl_file(FILE_DRV_API))
218     open(drvdir + '/protocol.c', 'w').write(tmpl_file(FILE_DRV_PROTOCOL))
219     open(drvdir + '/protocol.h', 'w').write(tmpl_file(FILE_DRV_PROTOCOL_H))
220
221
222 def make_patch(gitdir):
223     os.chdir(gitdir)
224     command('git add hardware/' + names['short'])
225     cmd = 'git commit -m "%s: Initial driver skeleton." ' % names['short']
226     cmd += 'configure.ac Makefile.am hwdriver.c hardware/' + names['short']
227     command(cmd)
228     cmd = "git format-patch HEAD~1"
229     out, err = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).communicate()
230     if err:
231         raise Exception(err.decode())
232     patch = out.decode().strip()
233     shutil.move(gitdir + '/' + patch, scriptdir + '/' + patch)
234     print(patch)
235
236
237 def command(cmd):
238     out, err = Popen(cmd, shell=True, stderr=PIPE).communicate()
239     if err:
240         raise Exception(err.decode())
241
242
243 def parse_gitconfig():
244     try:
245         author = email = None
246         for line in open(os.environ['HOME'] + '/.gitconfig').readlines():
247             m = re.match('\s*(\w+)\s*=\s*(.*)\s*$', line)
248             if m:
249                 key, value = m.groups()
250                 if key == 'name':
251                     author = value
252                 elif key == 'email':
253                     email = value
254                 if author and email:
255                     break
256     except:
257         pass
258     if not author or not email:
259         print("Please put your name and email in ~/.gitconfig")
260         sys.exit()
261
262     return author, email
263
264 #
265 # main
266 #
267
268 scriptdir = os.getcwd()
269 if scriptdir.split('/')[-2:] != ['sigrok-util', 'source']:
270         print("Please call this script from the 'source' directory.")
271         sys.exit(1)
272
273 LIBSR = 'git://sigrok.org/libsigrok'
274 TMPLDIR = scriptdir
275
276 if len(sys.argv) < 2:
277     print("Usage: new-driver <name>")
278     sys.exit()
279
280 author, email = parse_gitconfig()
281 name = ' '.join(sys.argv[1:])
282 names = {
283     'name': name,
284     'short': re.sub('[^a-z0-9]', '-', name.lower()),
285     'lib': re.sub('[^a-z0-9]', '_', name.lower()),
286     'upper': re.sub('[^A-Z0-9]', '_', name.upper()),
287     'libupper': re.sub('[^A-Z0-9]', '', name.upper()),
288     'year': datetime.datetime.now().year,
289     'author': author,
290     'email': email,
291 }
292 new_driver()
293