change the name of the cache folder
[build-farm.git] / buildfarm / __init__.py
1 #!/usr/bin/python
2 # Simple database query script for the buildfarm
3 #
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org>         2010
5 #
6 #   This program is free software; you can redistribute it and/or modify
7 #   it under the terms of the GNU General Public License as published by
8 #   the Free Software Foundation; either version 2 of the License, or
9 #   (at your option) any later version.
10 #
11 #   This program is distributed in the hope that it will be useful,
12 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 #   GNU General Public License for more details.
15 #
16 #   You should have received a copy of the GNU General Public License
17 #   along with this program; if not, write to the Free Software
18 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 import ConfigParser
21 import os
22 import re
23
24
25 class Tree(object):
26     """A tree to build."""
27
28     def __init__(self, name, scm, repo, branch, subdir="", srcdir=""):
29         self.name = name
30         self.repo = repo
31         self.scm = scm
32         self.branch = branch
33         self.subdir = subdir
34         self.srcdir = srcdir
35         self.scm = scm
36
37     def __repr__(self):
38         return "<%s %r>" % (self.__class__.__name__, self.name)
39
40
41 def read_trees_from_conf(path):
42     """Read trees from a configuration file."""
43     ret = {}
44     cfp = ConfigParser.ConfigParser()
45     cfp.readfp(open(path))
46     for s in cfp.sections():
47         ret[s] = Tree(name=s, **dict(cfp.items(s)))
48     return ret
49
50
51 def lcov_extract_percentage(text):
52     m = re.search('\<td class="headerItem".*?\>Code\&nbsp\;covered\:\<\/td\>.*?\n.*?\<td class="headerValue".*?\>([0-9.]+) \%', text)
53     if m:
54         return m.group(1)
55     else:
56         return None
57
58
59 class BuildFarm(object):
60
61     LCOVHOST = "magni"
62     OLDAGE = 60*60*4,
63     DEADAGE = 60*60*24*4
64
65     def __init__(self, path=None):
66         if path is None:
67             path = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
68         self.path = path
69         self.cachedir = os.path.join(self.path, "cache")
70         self.webdir = os.path.join(self.path, "web")
71         if not os.path.isdir(path):
72             raise Exception("web directory %s does not exist" % self.webdir)
73         self.trees = read_trees_from_conf(os.path.join(self.webdir, "trees.conf"))
74         self.builds = self._open_build_results()
75         self.upload_builds = self._open_upload_build_results()
76         self.hostdb = self._open_hostdb()
77         self.compilers = self._load_compilers()
78         self.lcovdir = os.path.join(self.path, "lcov/data")
79
80     def __repr__(self):
81         return "%s(%r)" % (self.__class__.__name__, self.path)
82
83     def _open_build_results(self):
84         from buildfarm import data
85         return data.BuildResultStore(os.path.join(self.path, "data", "oldrevs"))
86
87     def _open_upload_build_results(self):
88         from buildfarm import data
89         return data.UploadBuildResultStore(os.path.join(self.path, "data", "upload"))
90
91     def _open_hostdb(self):
92         from buildfarm import hostdb
93         return hostdb.HostDatabase(
94             os.path.join(self.path, "hostdb.sqlite"))
95
96     def _load_compilers(self):
97         from buildfarm import util
98         return util.load_list(os.path.join(self.webdir, "compilers.list"))
99
100     def lcov_status(self, tree):
101         """get status of build"""
102         from buildfarm import data, util
103         file = os.path.join(self.lcovdir, self.LCOVHOST, tree, "index.html")
104         try:
105             lcov_html = util.FileLoad(file)
106         except OSError:
107             # File does not exist
108             raise data.NoSuchBuildError(tree, self.LCOVHOST, "lcov")
109
110         perc = lcov_extract_percentage(lcov_html)
111         if perc is None:
112             ret = ""
113         else:
114             ret = perc
115         return perc
116
117     def get_build(self, tree, host, compiler, rev=None):
118         if rev:
119             return self.builds.get_build(tree, host, compiler, rev)
120         else:
121             return self.upload_builds.get_build(tree, host, compiler)
122
123     def get_new_builds(self):
124         from buildfarm import data
125         for host in self.hostdb.hosts():
126             for tree in self.trees:
127                 for compiler in self.compilers:
128                     # By building the log file name this way, using only the list of
129                     # hosts, trees and compilers as input, we ensure we
130                     # control the inputs
131                     try:
132                         yield self.upload_builds.get_build(host, tree, compiler)
133                     except data.NoSuchBuildError:
134                         continue
135
136
137 class CachingBuildFarm(BuildFarm):
138
139     def __init__(self, path=None, cachedirname=None):
140         self.cachedir = None
141         super(CachingBuildFarm, self).__init__(path)
142
143         if cachedirname:
144             self.cachedir = os.path.join(self.path, cachedirname)
145         else:
146             self.cachedir = os.path.join(self.path, "cache2")
147         self.builds = self._open_build_results()
148         self.upload_builds = self._open_upload_build_results()
149
150     def _open_build_results(self):
151         from buildfarm import data
152         if not self.cachedir:
153             return
154         return data.CachingBuildResultStore(os.path.join(self.path, "data", "oldrevs"),
155                 self.cachedir)
156
157     def _open_upload_build_results(self):
158         from buildfarm import data
159         if not self.cachedir:
160             return
161         return data.CachingUploadBuildResultStore(os.path.join(self.path, "data", "upload"),
162                 self.cachedir)
163
164     def lcov_status(self, tree):
165         """get status of build"""
166         from buildfarm import data, util
167         cachefile = self.builds.get_lcov_cached_status(self.LCOVHOST, tree)
168         file = os.path.join(self.lcovdir, self.LCOVHOST, tree, "index.html")
169         try:
170             st1 = os.stat(file)
171         except OSError:
172             # File does not exist
173             raise data.NoSuchBuildError(tree, self.LCOVHOST, "lcov")
174         try:
175             st2 = os.stat(cachefile)
176         except OSError:
177             # file does not exist
178             st2 = None
179
180         if st2 and st1.st_ctime <= st2.st_mtime:
181             ret = util.FileLoad(cachefile)
182             if ret == "":
183                 return None
184             return ret
185
186         lcov_html = util.FileLoad(file)
187         perc = lcov_extract_percentage(lcov_html)
188         if perc is None:
189             ret = ""
190         else:
191             ret = perc
192         if not self.readonly:
193             util.FileSave(cachefile, ret)
194         return perc