]>
git.ipfire.org Git - people/ms/ipfire-3.x.git/blob - naoki/backend.py
8 import urlgrabber
.progress
14 from exception
import *
15 from constants
import *
18 "package_names" : None,
22 # Python 2.4 does not have that email module, so
23 # we disable the mail function here.
25 import email
.mime
.multipart
26 import email
.mime
.text
38 def find_package_info(name
, toolchain
=False, **kwargs
):
39 for repo
in get_repositories(toolchain
):
40 if not os
.path
.exists(os
.path
.join(repo
.path
, name
, name
+ ".nm")):
43 return PackageInfo(name
, repo
=repo
, **kwargs
)
45 def find_package(name
, naoki
, toolchain
=False):
46 package
= find_package_info(name
, toolchain
)
48 return package
.getPackage(naoki
)
52 def parse_package_info(names
, toolchain
=False, **kwargs
):
55 package
= find_package_info(name
, toolchain
, **kwargs
)
57 packages
.append(package
)
61 def parse_package(names
, toolchain
=False, naoki
=None):
62 packages
= parse_package_info(names
, toolchain
)
64 return [Package(package
.name
, naoki
=naoki
, toolchain
=toolchain
) \
65 for package
in packages
]
67 def get_package_names(toolchain
=False):
68 if not __cache
["package_names"]:
70 for repo
in get_repositories(toolchain
):
71 names
.extend(repo
.package_names
)
73 __cache
["package_names"] = sorted(names
)
75 return __cache
["package_names"]
77 def get_group_names():
78 if not __cache
["group_names"]:
80 for package
in parse_package_info(get_package_names()):
81 if not package
.group
in groups
:
82 groups
.append(package
.group
)
84 __cache
["group_names"] = sorted(groups
)
86 return __cache
["group_names"]
88 def find_package_name(name
, toolchain
=False):
89 if name
in get_package_names(toolchain
):
92 for package
in get_package_names(toolchain
):
93 if os
.path
.basename(package
) == name
:
96 def depsolve(packages
, recursive
=False, build
=False, toolchain
=False):
98 for package
in packages
:
99 if not package
in deps
:
102 if not recursive
or not deps
:
108 deps
.extend(dep
.dependencies
)
109 if build
and not toolchain
:
110 deps
.extend(dep
.dependencies_build
)
114 if not dep
in new_deps
:
119 if length
== len(deps
):
125 def deptree(packages
, toolchain
=False):
131 for package
in stage
[:]:
132 for dep
in package
.dependencies_all
:
134 stage
.remove(package
)
147 def depsort(packages
, toolchain
=False):
149 for l1
in deptree(packages
, toolchain
=toolchain
):
155 obj
= hashlib
.sha1(data
)
159 return obj
.hexdigest()
161 def download(files
, logger
=None):
163 filepath
= os
.path
.join(TARBALLDIR
, file)
165 if not os
.path
.exists(TARBALLDIR
):
166 os
.makedirs(TARBALLDIR
)
168 if os
.path
.exists(filepath
):
171 url
= config
["sources_download_url"] + "/" + file
174 logger
.debug("Retrieving %s" % url
)
176 g
= urlgrabber
.grabber
.URLGrabber(
177 user_agent
= "%sSourceGrabber/%s" % (config
["distro_name"], config
["distro_version"],),
178 progress_obj
= urlgrabber
.progress
.TextMeter(),
183 gobj
= g
.urlopen(url
)
184 except urlgrabber
.grabber
.URLGrabError
, e
:
186 logger
.error("Could not retrieve %s - %s" % (url
, e
))
192 if gobj
.hdr
.has_key("X-Hash-Sha1"):
193 hash_server
= gobj
.hdr
["X-Hash-Sha1"]
194 msg
= "Comparing hashes - %s" % hash_server
196 hash_calculated
= calc_hash(data
)
197 if hash_calculated
== hash_server
:
199 logger
.debug(msg
+ " - OK")
202 logger
.error(msg
+ " - ERROR")
203 raise DownloadError
, "Hash sum of downloaded file does not match"
205 fobj
= open(filepath
, "w")
210 class PackageInfo(object):
213 def __init__(self
, name
, repo
=None, arch
=arches
.current
["name"]):
219 def __cmp__(self
, other
):
220 return cmp(self
.name
, other
.name
)
223 return "<PackageInfo %s>" % self
.name
226 if not self
.__data
.has_key(self
.name
):
227 self
.__data
[self
.name
] = self
.fetch()
229 return self
.__data
[self
.name
]
231 def set_data(self
, data
):
232 self
.__data
[self
.name
] = data
234 _data
= property(get_data
, set_data
)
237 env
= os
.environ
.copy()
238 env
.update(config
.environment
)
240 "PKG_ARCH" : self
.arch
,
243 output
= util
.do("make -f %s" % self
.filename
, shell
=True,
244 cwd
=os
.path
.join(PKGSDIR
, self
.repo
.name
, self
.name
), returnOutput
=1, env
=env
)
247 for line
in output
.splitlines():
248 a
= line
.split("=", 1)
249 if not len(a
) == 2: continue
251 ret
[key
] = val
.strip("\"")
253 ret
["FINGERPRINT"] = self
.fingerprint
260 def getPackage(self
, naoki
):
261 return Package(self
.name
, naoki
)
266 "build_deps" : [dep
.name
for dep
in self
.dependencies_build
],
267 "deps" : [dep
.name
for dep
in self
.dependencies
],
268 "description" : self
.description
,
269 "filename" : self
.filename
,
270 "fingerprint" : self
.fingerprint
,
271 "files" : self
.package_files
,
272 "group" : self
.group
,
274 "license" : self
.license
,
275 "maintainer" : self
.maintainer
,
277 "objects" : self
.objects
,
278 "patches" : self
.patches
,
279 "release" : self
.release
,
280 "summary" : self
.summary
,
282 "version" : self
.version
,
287 return self
.dependencies_unbuilt
== []
291 for file in self
.package_files
:
292 if not os
.path
.exists(os
.path
.join(PACKAGESDIR
, file)):
297 def _dependencies(self
, s
, recursive
=False, toolchain
=False):
299 if not self
._data
.has_key(c
):
300 deps
= parse_package_info(self
._data
.get(s
).split(" "), toolchain
=toolchain
)
301 self
._data
.update({c
: depsolve(deps
, recursive
)})
303 return self
._data
.get(c
)
306 def dependencies(self
):
308 return self
.dependencies_toolchain
310 return self
._dependencies
("PKG_DEPENDENCIES")
313 def dependencies_build(self
):
314 return self
._dependencies
("PKG_BUILD_DEPENDENCIES")
317 def dependencies_built(self
):
319 for dep
in self
.dependencies_all
:
326 def dependencies_unbuilt(self
):
328 for dep
in self
.dependencies_all
:
335 def dependencies_all(self
):
336 deps
= self
.dependencies
337 if not self
.__toolchain
:
338 deps
.extend(self
.dependencies_build
)
339 return depsolve(deps
, build
=True, recursive
=True, toolchain
=self
.__toolchain
)
342 def dependencies_toolchain(self
):
343 return self
._dependencies
("PKG_TOOLCHAIN_DEPENDENCIES", toolchain
=True)
346 def description(self
):
347 return self
._data
.get("PKG_DESCRIPTION")
351 return os
.path
.join(PKGSDIR
, self
.repo
.name
, self
.name
,
352 os
.path
.basename(self
.name
)) + ".nm"
355 def fingerprint(self
):
356 return "%d" % os
.stat(self
.filename
).st_mtime
360 return self
._data
.get("PKG_GROUP")
364 return "%s-%s-%s" % (self
.name
, self
.version
, self
.release
)
368 return self
._data
.get("PKG_LICENSE")
371 def maintainer(self
):
372 return self
._data
.get("PKG_MAINTAINER")
380 return self
._data
.get("PKG_OBJECTS").split(" ")
383 def package_files(self
):
384 return self
._data
.get("PKG_PACKAGES_FILES").split(" ")
388 return self
._data
.get("PKG_PATCHES").split(" ")
392 return self
._data
.get("PKG_REL")
396 return self
._data
.get("PKG_SUMMARY")
400 return self
._data
.get("PKG_URL")
404 return self
._data
.get("PKG_VER")
407 def __toolchain(self
):
408 return self
.repo
.name
== "toolchain"
411 class Package(object):
412 def __init__(self
, name
, naoki
, toolchain
=False):
413 self
.info
= find_package_info(name
, toolchain
)
418 #self.log.debug("Initialized package object %s" % name)
421 return "<Package %s>" % self
.info
.name
423 def __cmp__(self
, other
):
424 return cmp(self
.name
, other
.name
)
426 def __getattr__(self
, attr
):
427 return getattr(self
.info
, attr
)
431 return self
.info
.name
434 environment
= chroot
.PackageEnvironment(self
)
438 download(self
.info
.objects
, logger
=self
.log
)
440 def extract(self
, dest
):
441 files
= [os
.path
.join(PACKAGESDIR
, file) for file in self
.info
.package_files
]
445 self
.log
.debug("Extracting %s..." % files
)
446 util
.do("%s --root=%s %s" % (os
.path
.join(TOOLSDIR
, "decompressor"),
447 dest
, " ".join(files
)), shell
=True)
449 def getEnvironment(self
, *args
, **kwargs
):
450 return chroot
.PackageEnvironment(self
, *args
, **kwargs
)
454 return self
.naoki
.logging
.getBuildLogger(self
.info
.id)
457 def get_repositories(toolchain
=False):
459 return [Repository("toolchain")]
462 for repo
in os
.listdir(PKGSDIR
):
463 if os
.path
.isdir(os
.path
.join(PKGSDIR
, repo
)):
466 repos
.remove("toolchain")
468 return [Repository(repo
) for repo
in repos
]
470 class Repository(object):
471 def __init__(self
, name
):
475 return "<Repository %s>" % self
.name
480 for package
in os
.listdir(self
.path
):
481 package
= PackageInfo(package
, repo
=self
)
482 packages
.append(package
)
487 def package_names(self
):
488 return [package
.name
for package
in self
.packages
]
492 return os
.path
.join(PKGSDIR
, self
.name
)
495 class BinaryRepository(object):
496 DIRS
= ("db", "packages")
498 def __init__(self
, name
, naoki
=None, arch
=None):
500 self
.arch
= arch
or arches
.current
501 self
.repo
= Repository(self
.name
)
507 if not self
.buildable
:
508 raise Exception, "Cannot build repository"
510 # Create temporary directory layout
511 util
.rm(self
.repopath("tmp"))
512 for dir in self
.DIRS
:
513 util
.mkdir(self
.repopath("tmp", dir))
516 for package
in self
.packages
:
517 for file in package
.package_files
:
518 shutil
.copy(os
.path
.join(PACKAGESDIR
, file),
519 self
.repopath("tmp", "packages"))
521 # TODO check repository's sanity
522 # TODO create repoview
523 f
= open(self
.repopath("tmp", "db", "package-list.txt"), "w")
524 for package
in self
.packages
:
525 s
= "%-40s" % package
.fmtstr("%(name)s-%(version)s-%(release)s")
526 s
+= " | %s\n" % package
.summary
530 for dir in self
.DIRS
:
531 util
.rm(self
.repopath(dir))
532 shutil
.move(self
.repopath("tmp", dir), self
.repopath(dir))
533 util
.rm(self
.repopath("tmp"))
536 if os
.path
.exists(self
.path
):
537 self
.log
.debug("Cleaning up repository: %s" % self
.path
)
540 def repopath(self
, *args
):
541 return os
.path
.join(self
.path
, *args
)
545 for package
in self
.packages
:
554 return self
.naoki
.log
559 for package
in parse_package_info(get_package_names(), arch
=self
.arch
["name"]):
560 if not package
.repo
.name
== self
.name
:
562 packages
.append(package
)
567 return os
.path
.join(REPOSDIR
, self
.name
, self
.arch
["name"])
569 def report_error_by_mail(package
):
570 log
= package
.naoki
.log
572 # Do not send a report if no recipient is configured
573 if not config
["error_report_recipient"]:
577 log
.error("Can't send mail because this python version does not support this")
581 connection
= smtplib
.SMTP(config
["smtp_server"])
582 #connection.set_debuglevel(1)
584 if config
["smtp_user"] and config
["smtp_password"]:
585 connection
.login(config
["smtp_user"], config
["smtp_password"])
587 except SMTPConnectError
, e
:
588 log
.error("Could not establish a connection to the smtp server: %s" % e
)
590 except SMTPAuthenticationError
, e
:
591 log
.error("Could not successfully login to the smtp server: %s" % e
)
594 msg
= email
.mime
.multipart
.MIMEMultipart()
595 msg
["From"] = config
["error_report_sender"]
596 msg
["To"] = config
["error_report_recipient"]
597 msg
["Subject"] = config
["error_report_subject"] % package
.all
598 msg
.preamble
= 'You will not see this in a MIME-aware mail reader.\n'
601 The package %(name)s had a difficulty to build itself.
602 This email will give you a short report about the error.
605 Name : %(name)s - %(summary)s
606 Version : %(version)s
607 Release : %(release)s
609 This package in maintained by %(maintainer)s.
612 A detailed logfile is attached.
618 msg
.attach(email
.mime
.text
.MIMEText(body
))
620 # Read log and append it to mail
621 logfile
= os
.path
.join(LOGDIR
, package
.id + ".log")
622 if os
.path
.exists(logfile
):
627 line
= line
.rstrip("\n")
628 if line
.endswith(LOG_MARKER
):
637 log
= email
.mime
.text
.MIMEText("\n".join(log
), _subtype
="plain")
638 log
.add_header('Content-Disposition', 'attachment',
639 filename
="%s.log" % package
.id)
643 connection
.sendmail(config
["error_report_sender"],
644 config
["error_report_recipient"], msg
.as_string())
646 log
.error("Could not send error report: %s: %s" % (e
.__class
__.__name
__, e
))