new-driver: Add missing \n that causes build errors.
[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, check_output
25 import shutil
26 import re
27 import socket
28 import datetime
29
30 TMPL_AUTOCONF_DRIVER = "SR_DRIVER([{name}], [{short}])\n"
31
32 TMPL_FILES = ('api.c', 'protocol.c', 'protocol.h')
33
34 TMPL_HWMAKE_DRIVERLIB = """if HW_{upper}
35 libsigrok_la_SOURCES +="""
36 for tmpl_file in TMPL_FILES:
37     TMPL_HWMAKE_DRIVERLIB += " \\\nsrc/hardware/{short}/" + tmpl_file
38 TMPL_HWMAKE_DRIVERLIB += "\nendif\n"
39
40
41 def tmpl(template, names):
42     return template.format(**names)
43
44
45 def tmpl_file(filename, tmpldir, names):
46     with open(os.path.join(tmpldir, filename)) as template:
47         return tmpl(template.read(), names)
48
49
50 def new_driver(srcurl, tmpldir, names):
51     tmp = tempfile.mkdtemp()
52     try:
53         process = Popen(['git', 'clone', '--depth=1', srcurl, tmp],
54                         stdout=PIPE, stderr=PIPE)
55         out, err = process.communicate()
56         if process.returncode:
57             raise Exception(err.decode())
58         gitdir = tmp
59         do_autoconf(gitdir, names)
60         do_automake(gitdir, names)
61         do_driverskel(gitdir, tmpldir, names)
62         make_patch(gitdir, names)
63     except Exception as e:
64         raise
65         print(e)
66     finally:
67         shutil.rmtree(tmp)
68
69
70 # add DRIVER entry to configure.ac
71 def do_autoconf(gitdir, names):
72     cacpath = os.path.join(gitdir, 'configure.ac')
73     configure_ac = open(cacpath).read()
74
75     out = ''
76     state = 'driver'
77     active = False
78     for line in configure_ac.split('\n')[:-1]:
79         if state == 'driver':
80             m = re.match(r'SR_DRIVER\(\[([^\]]+)', line)
81             if m:
82                 active = True
83             if active:
84                 if (m and m.group(1).upper() > names['name'].upper()) or m is None:
85                     out += tmpl(TMPL_AUTOCONF_DRIVER, names)
86                     state = 'done'
87                     active = False
88         out += line + '\n'
89     if state != 'done':
90         raise Exception('No SR_DRIVER entries found in configure.ac')
91     open(cacpath, 'w').write(out)
92
93
94 # add HW_ entry to Makefile.am
95 def do_automake(gitdir, names):
96     path = os.path.join(gitdir, 'Makefile.am')
97     hwmake = open(path).read()
98
99     out = ''
100     state = 'copy'
101     for line in hwmake.split('\n')[:-1]:
102         if state == 'copy' and re.match(r'if\s+HW_\w+$', line):
103             state = 'drivers'
104         if state == 'drivers':
105             m = re.match(r'if\s+HW_(\w+)$', line)
106             if m:
107                 drv_short = m.group(1)
108                 if drv_short > names['upper']:
109                     out += tmpl(TMPL_HWMAKE_DRIVERLIB, names)
110                     state = 'done'
111             elif not re.match(r'\s*libsigrok_la_SOURCES\b|\s*src/hardware/|endif\b', line):
112                 print("[%s]" % line.strip())
113                 # we passed the last entry
114                 out += tmpl(TMPL_HWMAKE_DRIVERLIB, names)
115                 state = 'done'
116         out += line + '\n'
117     if state != 'done':
118         raise Exception('No "if HW_" markers found in Makefile.am')
119     open(path, 'w').write(out)
120
121
122 def do_driverskel(gitdir, tmpldir, names):
123     drvdir = os.path.join(gitdir, 'src', 'hardware', names['short'])
124     os.mkdir(drvdir)
125     for fname in TMPL_FILES:
126         with open(os.path.join(drvdir, fname), 'w') as outf:
127             outf.write(tmpl_file('drv-{0}'.format(fname), tmpldir, names))
128
129
130 def make_patch(gitdir, names):
131     cwd = os.getcwd()
132     try:
133         os.chdir(gitdir)
134         command(['git', 'add', os.path.join('src', 'hardware', names['short'])])
135         cmd = ['git', 'commit',
136                '-m', '%s: Initial driver skeleton.' % names['short'],
137                'configure.ac', 'Makefile.am',
138                os.path.join('src', 'hardware', names['short'])]
139         command(cmd)
140         cmd = ['git', 'format-patch', 'HEAD~1']
141         out, err = Popen(cmd, stdout=PIPE, stderr=PIPE).communicate()
142         if err:
143             raise Exception(err.decode())
144         patch = out.decode().strip()
145         shutil.move(os.path.join(gitdir, patch), cwd)
146         print("Patch {0} has been generated in {1}".format(patch, cwd))
147     finally:
148         os.chdir(cwd)
149
150
151 def command(cmd):
152     out, err = Popen(cmd, stderr=PIPE).communicate()
153     if err:
154         raise Exception(err.decode())
155
156
157 def parse_gitconfig():
158     try:
159         author = check_output(["git", "config", "user.name"]).decode().strip();
160     except:
161         author = None
162     try:
163         email = check_output(["git", "config", "user.email"]).decode().strip();
164     except:
165         email = None
166     return author, email
167
168
169 #
170 # main
171 #
172
173 if __name__ == '__main__':
174     from argparse import ArgumentParser
175
176     defaulturl = 'git://sigrok.org/libsigrok'
177     defaultdir = os.path.abspath(os.path.dirname(__file__))
178     author, email = parse_gitconfig()
179
180     parser = ArgumentParser(description='Bootstrap a new sigrok hardware driver')
181     parser.add_argument('name', help='new driver name')
182     parser.add_argument('--giturl', default=defaulturl,
183                         help='URL of the libsigrok git repository '
184                         '(defaults to {0})'.format(defaulturl))
185     parser.add_argument('--tmpl-dir', default=defaultdir,
186                         help='Directory in which the templates are stored '
187                         '(defaults to {0})'.format(defaultdir))
188     parser.add_argument('--author', default=author, required=not author,
189                         help='User name to write the Copyright lines')
190     parser.add_argument('--email', default=email, required=not email,
191                         help='Email address to write the Copyright lines')
192     opts = parser.parse_args()
193
194     if not opts.author or not opts.email:
195         parser.error('Please provide your username and email address, '
196                      'or set your git configuration up.') 
197     name = opts.name
198     names = {
199         'name': name,
200         'short': re.sub('[^a-z0-9]', '-', name.lower()),
201         'lib': re.sub('[^a-z0-9]', '_', name.lower()),
202         'upper': re.sub('[^A-Z0-9]', '_', name.upper()),
203         'year': datetime.datetime.now().year,
204         'author': opts.author,
205         'email': opts.email,
206     }
207     new_driver(opts.giturl, opts.tmpl_dir, names)
208