.
1 """
2 Copyright (C) 2012, 2013, 2014 Eduardo Naufel Schettino and Johan Mattsson
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program 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
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 """
17
18 """bavala is a build-tool for Vala that uses doit (http://pydoit.org)"""
19
20 import glob
21 import os
22 import sys
23 from os.path import join
24 from doit.action import CmdAction
25
26 try:
27 import scripts.config as config
28 except ImportError:
29 import config
30
31 import fnmatch
32 import subprocess
33
34 def cmd(name, *args):
35 """create string for command line"""
36 parts = [name]
37
38 try:
39 base = basestring
40 except NameError:
41 base = str
42
43 for item in args:
44 if isinstance(item, base):
45 parts.append(item)
46 elif isinstance(item, dict):
47 for param, value in item.items():
48 if isinstance(value, base):
49 value = [value]
50 parts.extend('--{0} {1}'.format(param, v) for v in value)
51 else:
52 parts.extend(item)
53
54 cmd_parts = []
55 for p in parts:
56 if isinstance(p, str):
57 cmd_parts += [p]
58 else:
59 cmd_parts += [p.decode("utf-8")]
60
61 return ' '.join(cmd_parts)
62
63 def get_sources_path (folder, pattern):
64 """obtain the path to all source files that matches pattern"""
65 files = []
66 for root, dirnames, filenames in os.walk(folder):
67 for filename in fnmatch.filter(filenames, pattern):
68 files.append(os.path.join(root, filename))
69 return files
70
71
72
73 def get_sources_name (folder, pattern):
74 """obtain name of all source files that matches pattern"""
75 files = []
76 for root, dirnames, filenames in os.walk(folder):
77 for filename in fnmatch.filter(filenames, pattern):
78 files.append(filename)
79 return files
80
81
82
83 class Vala(object):
84 """helper to generate tasks to compile vala code"""
85
86 def __init__(self, src, build, pkg_libs, library=None, vala_deps=None, so_version=None):
87 self.src = src
88 self.build = build
89 self.pkg_libs = pkg_libs
90 self.vala_deps = vala_deps or []
91 self.library = library
92
93 self.vala = get_sources_path (src, '*.vala')
94 self.c = get_sources_path (src, '*.c') # copy regular c sources
95 self.c += get_sources_path (src, '*.h') # copy header files for the c sources
96 self.cc = [join(build + '/' + src, f) for f in get_sources_name (src, '*.c') ]
97 self.cc += [join(build + '/' + src, f.replace('.vala', '.c')) for f in get_sources_name (src, '*.vala')]
98 self.obj = [self.build + '/' + self.src + '/' + f.replace('.c', '.o') for f in get_sources_name (src, '*.c')]
99 self.obj += [self.build + '/' + self.src + '/' + f.replace('.vala', '.o') for f in get_sources_name (src, '*.vala')]
100
101 if library:
102 self.header = join(build, library) + '.h'
103 self.vapi = join(build, library) + '.vapi' # generated vapi file
104 self.other_vapi_files = get_sources_path (src, '*.vapi') # other vapi files
105 self.so = join(build, src) + '.so.' + so_version
106 self.so_link = join(build, src) + '.so'
107 self.so_link_name = src + '.so'
108 self.so_version = so_version
109 self.so_name = 'lib' + library + '.so.' + so_version
110 else:
111 self.other_vapi_files = []
112
113 def gen_c(self, opts):
114 """translate code from vala to C and create .vapi"""
115 options = ['--ccode']
116 options.extend(opts)
117 params = {
118 'basedir': join(self.build, self.src),
119 'vapidir': './',
120 'pkg': self.pkg_libs,
121 }
122 if self.library:
123 params['library'] = self.library
124 params['vapi'] = self.vapi
125 params['header'] = self.header
126
127 dep_vapi = [d.vapi for d in self.vala_deps]
128 action = cmd('valac', options, params, dep_vapi, self.vala)
129 targets = self.cc[:]
130
131 if self.library:
132 targets += [self.header, self.vapi]
133
134 for f in self.c:
135 yield {
136 'name': 'copy_c_' + f,
137 'actions': [
138 'mkdir -p '+ self.build + '/' + self.src + '/',
139 'cp ' + f + ' ' + self.build + '/' + self.src + '/'
140 ],
141 }
142
143 for f in self.other_vapi_files:
144 yield {
145 'name': 'vapi_files_' + f,
146 'actions': [
147 'mkdir -p '+ self.build + '/',
148 'cp ' + f + ' ' + self.build + '/'
149 ],
150 }
151
152 print (action)
153
154 if not self.vala == []:
155 yield {
156 'name': 'compile_c',
157 'actions': [ action ],
158 'file_dep': self.vala + dep_vapi,
159 'targets': targets,
160 }
161
162
163 def gen_o(self, opts):
164 """compile C files to obj `.o` """
165 def compile_cmd(conf, opts, libs, pos):
166 flags = []
167 for l in libs:
168 if not l == "posix" and not l == "posixtypes":
169 process = subprocess.Popen ('pkg-config --cflags ' + l, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
170 cflags = process.stdout.readline()
171 process.communicate()[0]
172 if not process.returncode == 0:
173 print ( "Library not found: " + l)
174 exit (1)
175 flags += [cflags.strip ()]
176
177 return cmd(config.CC, opts, flags, pos)
178
179 for cc, obj in zip(self.cc, self.obj):
180 pos = ["-c " + cc, "-o " + obj ]
181 cmd_args = {'libs':self.pkg_libs, 'opts':opts, 'pos':pos}
182 action = CmdAction((compile_cmd, [], cmd_args))
183
184 yield {
185 'name': obj.rsplit('/')[-1],
186 'file_dep': [ cc ],
187 'actions': [ action ],
188 'getargs': { 'conf': ('pkg_flags', 'out') },
189 'targets': [ obj ],
190 }
191
192
193 def gen_so(self, generated_libs = None):
194 """generate ".so" lib file"""
195 def compile_cmd(conf, libs):
196 obj_glob = join(self.build, self.src, '*.o')
197 opts = ['-shared '
198 + '-Wl,-soname,' + self.so_name
199 + ' ' + obj_glob
200 + ' -o ' + self.so ]
201
202 flags = []
203 for l in libs:
204 if not l == "posix" and not l == "posixtypes":
205 process = subprocess.Popen ('pkg-config --cflags ' + l, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
206 cflags = process.stdout.readline()
207 process.communicate()[0]
208 if not process.returncode == 0:
209 print ( "Library not found: " + l)
210 exit (1)
211 flags += [cflags.strip ()]
212
213 if generated_libs:
214 flags += [generated_libs]
215
216 return cmd(config.CC, opts, flags)
217
218 return {
219 'name': self.so.rsplit('/')[-1],
220 'actions': [ CmdAction((compile_cmd, [], {'libs':self.pkg_libs})) ],
221 'getargs': { 'conf': ('pkg_flags', 'out') },
222 'file_dep': self.obj,
223 'targets': [ self.so ],
224 }
225
226 def gen_ln(self):
227 """generate a symbolic link to the generated ".so" file"""
228 so_file = self.so.rsplit('/')[-1]
229 create_link = "ln -s -T " + so_file + " " + self.so_link_name + " "
230 create_link += "&& mv " + self.so_link_name + " " + self.build + "/"
231 return {
232 'name': self.so_link_name,
233 'actions': [ create_link],
234 'file_dep': [ self.so ],
235 'targets': [ self.so_link ],
236 }
237
238 def gen_bin(self, opts):
239 """generate binary"""
240 def compile_cmd(conf, opts, libs):
241 flags = [conf[l].strip() for l in libs]
242 return cmd(config.CC, opts, flags)
243
244 bin_path = join(self.build, 'bin')
245 target = join(bin_path, self.src)
246 opts = (self.cc + opts +
247 ['-o ' + target, '-I ' + self.build, '-L ' + self.build] +
248 ['-l ' + d.library for d in self.vala_deps])
249 action = CmdAction((compile_cmd, [], {'opts':opts, 'libs':self.pkg_libs}))
250 yield {
251 'name': "bin",
252 'actions': [ 'mkdir -p %s' % bin_path, action ],
253 'getargs': { 'conf': ('pkg_flags', 'out') },
254 'file_dep': self.cc + [ d.so_link for d in self.vala_deps ],
255 'targets': [ target ],
256 }
257
258
259
260
261