]> git.ipfire.org Git - pakfire.git/blob - python/pakfire/packages/make.py
logging: Make own pakfire logger.
[pakfire.git] / python / pakfire / packages / make.py
1 #!/usr/bin/python
2 ###############################################################################
3 # #
4 # Pakfire - The IPFire package management system #
5 # Copyright (C) 2011 Pakfire development team #
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
22 import os
23 import re
24 import shutil
25 import socket
26 import tarfile
27
28 from urlgrabber.grabber import URLGrabber, URLGrabError
29 from urlgrabber.progress import TextMeter
30
31 import lexer
32 import packager
33
34 import logging
35 log = logging.getLogger("pakfire")
36
37 import pakfire.chroot as chroot
38 import pakfire.downloader as downloader
39 import pakfire.util as util
40
41 from base import Package
42 from file import SourcePackage
43
44 from pakfire.constants import *
45 from pakfire.i18n import _
46
47 class MakefileBase(Package):
48 def __init__(self, pakfire, filename):
49 Package.__init__(self, pakfire)
50
51 # Save the filename of the makefile.
52 self.filename = os.path.abspath(filename)
53
54 # Update environment.
55 environ = self.pakfire.environ
56 environ.update({
57 "PARALLELISMFLAGS" : "-j%s" % util.calc_parallelism(),
58 })
59
60 # Open and parse the makefile.
61 self.lexer = lexer.RootLexer.open(self.filename, environ=environ)
62
63 @property
64 def package_filename(self):
65 return PACKAGE_FILENAME_FMT % {
66 "arch" : self.arch,
67 "ext" : PACKAGE_EXTENSION,
68 "name" : self.name,
69 "release" : self.release,
70 "version" : self.version,
71 }
72
73 def lint(self):
74 errors = []
75
76 if not self.name:
77 errors.append(_("Package name is undefined."))
78
79 if not self.version:
80 errors.append(_("Package version is undefined."))
81
82 # XXX to do...
83
84 return errors
85
86 @property
87 def name(self):
88 return self.lexer.get_var("name")
89
90 @property
91 def epoch(self):
92 epoch = self.lexer.get_var("epoch")
93 if not epoch:
94 return 0
95
96 return int(epoch)
97
98 @property
99 def version(self):
100 return self.lexer.get_var("version")
101
102 @property
103 def release(self):
104 release = self.lexer.get_var("release")
105 assert release
106
107 tag = self.lexer.get_var("DISTRO_DISTTAG")
108 assert tag
109
110 return ".".join((release, tag))
111
112 @property
113 def summary(self):
114 return self.lexer.get_var("summary")
115
116 @property
117 def description(self):
118 description = self.lexer.get_var("description")
119
120 # Replace all backslashes at the end of a line.
121 return description.replace("\\\n", "\n")
122
123 @property
124 def groups(self):
125 groups = self.lexer.get_var("groups").split()
126
127 return sorted(groups)
128
129 @property
130 def url(self):
131 return self.lexer.get_var("url")
132
133 @property
134 def license(self):
135 return self.lexer.get_var("license")
136
137 @property
138 def maintainer(self):
139 maintainer = self.lexer.get_var("maintainer")
140
141 if not maintainer:
142 maintainer = self.lexer.get_var("DISTRO_MAINTAINER")
143
144 return maintainer
145
146 @property
147 def vendor(self):
148 return self.lexer.get_var("DISTRO_VENDOR")
149
150 @property
151 def buildroot(self):
152 return self.lexer.get_var("BUILDROOT")
153
154 @property
155 def build_host(self):
156 return socket.gethostname()
157
158 # XXX build_id and build_time are used to create a source package
159
160 @property
161 def build_id(self):
162 # XXX todo
163 # Not existant for Makefiles
164 return None
165
166 @property
167 def build_time(self):
168 # XXX todo
169 # Not existant for Makefiles
170 return None
171
172
173 class Makefile(MakefileBase):
174 @property
175 def uuid(self):
176 hash1 = util.calc_hash1(self.filename)
177
178 # Return UUID version 5 (SHA1 hash)
179 return "%8s-%4s-5%3s-%4s-%11s" % \
180 (hash1[0:8], hash1[9:13], hash1[14:17], hash1[18:22], hash1[23:34])
181
182 @property
183 def path(self):
184 return os.path.dirname(self.filename)
185
186 @property
187 def arch(self):
188 return "src"
189
190 @property
191 def supported_arches(self):
192 """
193 These are the supported arches. Which means, packages of these
194 architectures can be built out of this source package.
195 """
196 return self.lexer.get_var("sup_arches", "all")
197
198 @property
199 def packages(self):
200 pkgs = []
201
202 for lexer in self.lexer.packages:
203 name = lexer.get_var("_name")
204
205 pkg = MakefilePackage(self.pakfire, name, lexer)
206 pkgs.append(pkg)
207
208 return pkgs
209
210 @property
211 def source_dl(self):
212 dls = []
213
214 if self.pakfire.distro.source_dl:
215 dls.append(self.pakfire.distro.source_dl)
216
217 dl = self.lexer.get_var("source_dl")
218 if dl:
219 dls.append(dl)
220
221 return dls
222
223 def download(self):
224 """
225 Download all external sources and return a list with the local
226 copies.
227 """
228 # Download source files.
229 grabber = downloader.SourceDownloader(self.pakfire,
230 mirrors=self.source_dl)
231
232 return grabber.download(self.sources)
233
234 def dist(self, resultdirs):
235 """
236 Create a source package.
237 """
238 # Download all files we need for this package.
239 self.download()
240
241 p = packager.SourcePackager(self.pakfire, self)
242 p.run(resultdirs)
243
244 def dump(self, *args, **kwargs):
245 dump = MakefileBase.dump(self, *args, **kwargs)
246 dump = dump.splitlines()
247
248 #dump += ["", _("Containing the following binary packages:"),]
249 #
250 #for pkg in self.packages:
251 # _dump = pkg.dump(*args, **kwargs)
252 #
253 # for line in _dump.splitlines():
254 # dump.append(" %s" % line)
255 # dump.append("")
256
257 return "\n".join(dump)
258
259 def get_buildscript(self, stage):
260 return self.lexer.build.get_var("_%s" % stage)
261
262 @property
263 def prerequires(self):
264 return []
265
266 @property
267 def requires(self):
268 return self.lexer.build.get_var("requires", "").split()
269
270 @property
271 def provides(self):
272 return []
273
274 @property
275 def obsoletes(self):
276 return []
277
278 @property
279 def conflicts(self):
280 return []
281
282 @property
283 def files(self):
284 files = []
285 basedir = os.path.dirname(self.filename)
286
287 for dirs, subdirs, _files in os.walk(basedir):
288 for f in _files:
289 files.append(os.path.join(dirs, f))
290
291 return files
292
293 @property
294 def sources(self):
295 return self.lexer.get_var("sources").split()
296
297 @property
298 def exports(self):
299 exports = {}
300
301 # Include quality agent exports.
302 exports.update(self.lexer.quality_agent.exports)
303
304 for export in self.lexer.build.exports:
305 exports[export] = self.lexer.build.get_var(export)
306
307 return exports
308
309 def extract(self, message=None, prefix=None):
310 # XXX neeed to make this waaaaaaaaaay better.
311
312 files = self.files
313
314 # Load progressbar.
315 pb = None
316 if message:
317 message = "%-10s : %s" % (message, self.friendly_name)
318 pb = util.make_progress(message, len(files), eta=False)
319
320 dir_len = len(os.path.dirname(self.filename))
321
322 # Copy all files that belong to the package
323 i = 0
324 for f in files:
325 if pb:
326 i += 1
327 pb.update(i)
328
329 _f = f[dir_len:]
330 log.debug("%s/%s" % (prefix, _f))
331
332 path = "%s/%s" % (prefix, _f)
333
334 path_dir = os.path.dirname(path)
335 if not os.path.exists(path_dir):
336 os.makedirs(path_dir)
337
338 shutil.copy2(f, path)
339
340 if pb:
341 pb.finish()
342
343 # Download source files.
344 for _filename in self.download():
345 filename = "%s/files/%s" % (prefix, os.path.basename(_filename))
346 dirname = os.path.dirname(filename)
347
348 if not os.path.exists(dirname):
349 os.makedirs(dirname)
350
351 shutil.copy2(_filename, filename)
352
353
354 class MakefilePackage(MakefileBase):
355 def __init__(self, pakfire, name, lexer):
356 Package.__init__(self, pakfire)
357
358 self._name = name
359 self.lexer = lexer
360
361 # Store additional dependencies in here.
362 self._dependencies = {}
363
364 @property
365 def name(self):
366 return self._name
367
368 @property
369 def arch(self):
370 return self.lexer.get_var("arch", "%{DISTRO_ARCH}")
371
372 @property
373 def configfiles(self):
374 return self.lexer.get_var("configfiles").split()
375
376 @property
377 def files(self):
378 return self.lexer.get_var("files").split()
379
380 @property
381 def uuid(self):
382 return None
383
384 def track_dependencies(self, builder, path):
385 # Dependency types.
386 dep_types = ("prerequires", "requires", "provides", "conflicts", "obsoletes",)
387
388 result = builder.do("/usr/lib/pakfire/dependency-tracker %s" \
389 % path, returnOutput=True)
390
391 for line in result.splitlines():
392 m = re.match(r"^(\w+)=(.*)$", line)
393 if m is None:
394 continue
395
396 key, val = m.groups()
397
398 if not key in dep_types:
399 continue
400
401 val = val.strip("\"")
402 val = val.split()
403
404 self._dependencies[key] = sorted(val)
405
406 # Filter dependencies.
407 for key in dep_types:
408 self._dependencies[key] = self.filter_deps(
409 self._dependencies.get(key, []),
410 self.lexer.get_var("filter_%s" % key)
411 )
412
413 @staticmethod
414 def filter_deps(deps, filters):
415 if not filters:
416 return deps
417
418 filters = filters.splitlines()
419 filtered_deps = []
420
421 for dep in deps:
422 filtered = False
423 for filter in filters:
424 m = re.search(filter, dep)
425 if not m:
426 continue
427
428 # Yes, we found a match.
429 filtered = True
430
431 if not filtered:
432 filtered_deps.append(dep)
433
434 return filtered_deps
435
436 def get_deps(self, key):
437 # Collect all dependencies that were set in the makefile by the user.
438 deps = self.lexer.get_var(key).split()
439
440 # Collect all dependencies that were discovered by the tracker.
441 deps += self._dependencies.get(key, [])
442
443 # Remove duplicates.
444 deps = set(deps)
445 deps = list(deps)
446
447 return sorted(deps)
448
449 @property
450 def prerequires(self):
451 return self.get_deps("prerequires")
452
453 @property
454 def requires(self):
455 return self.get_deps("requires")
456
457 @property
458 def provides(self):
459 return self.get_deps("provides")
460
461 @property
462 def obsoletes(self):
463 return self.get_deps("obsoletes")
464
465 @property
466 def conflicts(self):
467 return self.get_deps("conflicts")
468
469 def get_scriptlet(self, type):
470 return self.lexer.get_scriptlet(type)
471
472 @property
473 def inst_size(self):
474 # The size of this is unknown.
475 return 0