.
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 import config
26 import fnmatch
27 import subprocess
28
29 def cmd(name, *args):
30 """create string for command line"""
31 parts = [name]
32 for item in args:
33 if isinstance(item, basestring):
34 parts.append(item)
35 elif isinstance(item, dict):
36 for param, value in item.iteritems():
37 if isinstance(value, basestring):
38 value = [value]
39 parts.extend('--{0} {1}'.format(param, v) for v in value)
40 else:
41 parts.extend(item)
42 return ' '.join(parts)
43
44
45 def get_sources_path (folder, pattern):
46 """obtain the path to all source files that matches pattern"""
47 files = []
48 for root, dirnames, filenames in os.walk(folder):
49 for filename in fnmatch.filter(filenames, pattern):
50 files.append(os.path.join(root, filename))
51 return files
52
53
54
55 def get_sources_name (folder, pattern):
56 """obtain name of all source files that matches pattern"""
57 files = []
58 for root, dirnames, filenames in os.walk(folder):
59 for filename in fnmatch.filter(filenames, pattern):
60 files.append(filename)
61 return files
62
63
64
65 class Vala(object):
66 """helper to generate tasks to compile vala code"""
67
68 def __init__(self, src, build, pkg_libs, library=None, vala_deps=None, so_version=None):
69 self.src = src
70 self.build = build
71 self.pkg_libs = pkg_libs
72 self.vala_deps = vala_deps or []
73 self.library = library
74
75 self.vala = get_sources_path (src, '*.vala')
76 self.c = get_sources_path (src, '*.c') # copy regular c sources
77 self.c += get_sources_path (src, '*.h') # copy header files for the c sources
78 self.cc = [join(build + '/' + src, f) for f in get_sources_name (src, '*.c') ]
79 self.cc += [join(build + '/' + src, f.replace('.vala', '.c')) for f in get_sources_name (src, '*.vala')]
80 self.obj = [self.build + '/' + self.src + '/' + f.replace('.c', '.o') for f in get_sources_name (src, '*.c')]
81 self.obj += [self.build + '/' + self.src + '/' + f.replace('.vala', '.o') for f in get_sources_name (src, '*.vala')]
82
83 if library:
84 self.header = join(build, library) + '.h'
85 self.vapi = join(build, library) + '.vapi' # generated vapi file
86 self.other_vapi_files = get_sources_path (src, '*.vapi') # other vapi files
87 self.so = join(build, src) + '.so.' + so_version
88 self.so_link = join(build, src) + '.so'
89 self.so_link_name = src + '.so'
90 self.so_version = so_version
91 self.so_name = 'lib' + library + '.so.' + so_version
92 else:
93 self.other_vapi_files = []
94
95 def gen_c(self, opts):
96 """translate code from vala to C and create .vapi"""
97 options = ['--ccode']
98 options.extend(opts)
99 params = {
100 'basedir': join(self.build, self.src),
101 'vapidir': './',
102 'pkg': self.pkg_libs,
103 }
104 if self.library:
105 params['library'] = self.library
106 params['vapi'] = self.vapi
107 params['header'] = self.header
108
109 dep_vapi = [d.vapi for d in self.vala_deps]
110 action = cmd('valac', options, params, dep_vapi, self.vala)
111 targets = self.cc[:]
112
113 if self.library:
114 targets += [self.header, self.vapi]
115
116 for f in self.c:
117 yield {
118 'name': 'copy_c_' + f,
119 'actions': [
120 'mkdir -p '+ self.build + '/' + self.src + '/',
121 'cp ' + f + ' ' + self.build + '/' + self.src + '/'
122 ],
123 }
124
125 for f in self.other_vapi_files:
126 yield {
127 'name': 'vapi_files_' + f,
128 'actions': [
129 'mkdir -p '+ self.build + '/',
130 'cp ' + f + ' ' + self.build + '/'
131 ],
132 }
133
134 print (action)
135
136 if not self.vala == []:
137 yield {
138 'name': 'compile_c',
139 'actions': [ action ],
140 'file_dep': self.vala + dep_vapi,
141 'targets': targets,
142 }
143
144
145 def gen_o(self, opts):
146 """compile C files to obj `.o` """
147 def compile_cmd(conf, opts, libs, pos):
148 flags = []
149 for l in libs:
150 if not l == "posix" and not l == "posixtypes":
151 process = subprocess.Popen ('pkg-config --cflags ' + l, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
152 cflags = process.stdout.readline()
153 process.communicate()[0]
154 if not process.returncode == 0:
155 print ( "Library not found: " + l)
156 exit (1)
157 flags += [cflags.strip ()]
158
159 return cmd(config.CC, opts, flags, pos)
160
161 for cc, obj in zip(self.cc, self.obj):
162 pos = ["-c " + cc, "-o " + obj ]
163 cmd_args = {'libs':self.pkg_libs, 'opts':opts, 'pos':pos}
164 action = CmdAction((compile_cmd, [], cmd_args))
165
166 yield {
167 'name': obj.rsplit('/')[-1],
168 'file_dep': [ cc ],
169 'actions': [ action ],
170 'getargs': { 'conf': ('pkg_flags', 'out') },
171 'targets': [ obj ],
172 }
173
174
175 def gen_so(self, generated_libs = None):
176 """generate ".so" lib file"""
177 def compile_cmd(conf, libs):
178 obj_glob = join(self.build, self.src, '*.o')
179 opts = ['-shared '
180 + '-Wl,-soname,' + self.so_name
181 + ' ' + obj_glob
182 + ' -o ' + self.so ]
183
184 flags = []
185 for l in libs:
186 if not l == "posix" and not l == "posixtypes":
187 process = subprocess.Popen ('pkg-config --cflags ' + l, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
188 cflags = process.stdout.readline()
189 process.communicate()[0]
190 if not process.returncode == 0:
191 print ( "Library not found: " + l)
192 exit (1)
193 flags += [cflags.strip ()]
194
195 if generated_libs:
196 flags += [generated_libs]
197
198 return cmd(config.CC, opts, flags)
199
200 return {
201 'name': self.so.rsplit('/')[-1],
202 'actions': [ CmdAction((compile_cmd, [], {'libs':self.pkg_libs})) ],
203 'getargs': { 'conf': ('pkg_flags', 'out') },
204 'file_dep': self.obj,
205 'targets': [ self.so ],
206 }
207
208 def gen_ln(self):
209 """generate a symbolic link to the generated ".so" file"""
210 so_file = self.so.rsplit('/')[-1]
211 create_link = "ln -s -T " + so_file + " " + self.so_link_name + " "
212 create_link += "&& mv " + self.so_link_name + " " + self.build + "/"
213 return {
214 'name': self.so_link_name,
215 'actions': [ create_link],
216 'file_dep': [ self.so ],
217 'targets': [ self.so_link ],
218 }
219
220 def gen_bin(self, opts):
221 """generate binary"""
222 def compile_cmd(conf, opts, libs):
223 flags = [conf[l].strip() for l in libs]
224 return cmd(config.CC, opts, flags)
225
226 bin_path = join(self.build, 'bin')
227 target = join(bin_path, self.src)
228 opts = (self.cc + opts +
229 ['-o ' + target, '-I ' + self.build, '-L ' + self.build] +
230 ['-l ' + d.library for d in self.vala_deps])
231 action = CmdAction((compile_cmd, [], {'opts':opts, 'libs':self.pkg_libs}))
232 yield {
233 'name': "bin",
234 'actions': [ 'mkdir -p %s' % bin_path, action ],
235 'getargs': { 'conf': ('pkg_flags', 'out') },
236 'file_dep': self.cc + [ d.so_link for d in self.vala_deps ],
237 'targets': [ target ],
238 }
239
240
241
242
243