Fix for #88 - written by proti:
[bitbake.git] / lib / bb / make.py
1 # ex:ts=4:sw=4:sts=4:et
2 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3 """
4 BitBake 'Make' implementations
5
6 Functions for reading BB files, building a dependency graph and
7 building a set of BB files while walking along the dependency graph.
8
9 Copyright (C) 2003, 2004  Mickey Lauer
10 Copyright (C) 2003, 2004  Phil Blundell
11 Copyright (C) 2003, 2004  Chris Larson
12
13 This program is free software; you can redistribute it and/or modify it under
14 the terms of the GNU General Public License as published by the Free Software
15 Foundation; either version 2 of the License, or (at your option) any later
16 version.
17
18 This program is distributed in the hope that it will be useful, but WITHOUT
19 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License along with
23 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
24 Place, Suite 330, Boston, MA 02111-1307 USA.
25
26 This file is part of the BitBake build tools.
27 """
28
29 from bb import debug, digraph, data, fetch, fatal, error, note, event, parse
30 import copy, bb, re, sys, os, glob, sre_constants
31
32 pkgdata = None
33 cfg = data.init()
34 cache = None
35 digits = "0123456789"
36 ascii_letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
37 mtime_cache = {}
38
39 def get_bbfiles( path = os.getcwd() ):
40     """Get list of default .bb files by reading out the current directory"""
41     contents = os.listdir(path)
42     bbfiles = []
43     for f in contents:
44         (root, ext) = os.path.splitext(f)
45         if ext == ".bb":
46             bbfiles.append(os.path.abspath(os.path.join(os.getcwd(),f)))
47     return bbfiles
48
49 def find_bbfiles( path ):
50     """Find all the .bb files in a directory (uses find)"""
51     findcmd = 'find ' + path + ' -name *.bb | grep -v SCCS/'
52     try:
53         finddata = os.popen(findcmd)
54     except OSError:
55         return []
56     return finddata.readlines()
57
58 def deps_clean(d):
59     depstr = data.getVar('__depends', d)
60     if depstr:
61         deps = depstr.split(" ")
62         for dep in deps:
63             (f,old_mtime_s) = dep.split("@")
64             old_mtime = int(old_mtime_s)
65             new_mtime = parse.cached_mtime(f)
66             if (new_mtime > old_mtime):
67                 return False
68     return True
69
70 def load_bbfile( bbfile ):
71     """Load and parse one .bb build file"""
72
73     if not cache in [None, '']:
74         # get the times
75         cache_mtime = data.init_db_mtime(cache, bbfile)
76         file_mtime = parse.cached_mtime(bbfile)
77
78         if file_mtime > cache_mtime:
79             #print " : '%s' dirty. reparsing..." % bbfile
80             pass
81         else:
82             #print " : '%s' clean. loading from cache..." % bbfile
83             cache_data = data.init_db( cache, bbfile, False )
84             if deps_clean(cache_data):
85                 return cache_data, True
86
87     topdir = data.getVar('TOPDIR', cfg)
88     if not topdir:
89         topdir = os.path.abspath(os.getcwd())
90         # set topdir to here
91         data.setVar('TOPDIR', topdir, cfg)
92     bbfile = os.path.abspath(bbfile)
93     bbfile_loc = os.path.abspath(os.path.dirname(bbfile))
94     # expand tmpdir to include this topdir
95     data.setVar('TMPDIR', data.getVar('TMPDIR', cfg, 1) or "", cfg)
96     # set topdir to location of .bb file
97     topdir = bbfile_loc
98     #data.setVar('TOPDIR', topdir, cfg)
99     # go there
100     oldpath = os.path.abspath(os.getcwd())
101     os.chdir(topdir)
102     bb = data.init_db(cache,bbfile, True, cfg)
103     try:
104         parse.handle(bbfile, bb) # read .bb data
105         if not cache in [None, '']:
106             bb.commit(parse.cached_mtime(bbfile)) # write cache
107         os.chdir(oldpath)
108         return bb, False
109     finally:
110         os.chdir(oldpath)
111
112 def collect_bbfiles( progressCallback ):
113     """Collect all available .bb build files"""
114     collect_bbfiles.cb = progressCallback
115     parsed, cached, skipped, masked = 0, 0, 0, 0
116     global cache, pkgdata
117     cache   = bb.data.getVar( "CACHE", cfg, 1 )
118     pkgdata = data.pkgdata( not cache in [None, ''], cache )
119
120     if not cache in [None, '']:
121         if collect_bbfiles.cb is not None:
122             print "NOTE: Using cache in '%s'" % cache
123         try:
124             os.stat( cache )
125         except OSError:
126             bb.mkdirhier( cache )
127     else:
128         if collect_bbfiles.cb is not None:
129             print "NOTE: Not using a cache. Set CACHE = <directory> to enable."
130     files = (data.getVar( "BBFILES", cfg, 1 ) or "").split()
131     data.setVar("BBFILES", " ".join(files), cfg)
132
133     if not len(files):
134         files = get_bbfiles()
135
136     if not len(files):
137         bb.error("no files to build.")
138
139     newfiles = []
140     for f in files:
141         if os.path.isdir(f):
142             dirfiles = find_bbfiles(f)
143             if dirfiles:
144                 newfiles += dirfiles
145                 continue
146         newfiles += glob.glob(f) or [ f ]
147
148     bbmask = bb.data.getVar('BBMASK', cfg, 1) or ""
149     try:
150         bbmask_compiled = re.compile(bbmask)
151     except sre_constants.error:
152         bb.fatal("BBMASK is not a valid regular expression.")
153
154     for i in xrange( len( newfiles ) ):
155         f = newfiles[i]
156         if bbmask and bbmask_compiled.search(f):
157               bb.debug(1, "bbmake: skipping %s" % f)
158               masked += 1
159               continue
160         debug(1, "bbmake: parsing %s" % f)
161
162         # read a file's metadata
163         try:
164             bb_data, fromCache = load_bbfile(f)
165             if fromCache: cached += 1
166             else: parsed += 1
167             deps = None
168             if bb_data is not None:
169                 # allow metadata files to add items to BBFILES
170                 #data.update_data(pkgdata[f])
171                 addbbfiles = data.getVar('BBFILES', bb_data) or None
172                 if addbbfiles:
173                     for aof in addbbfiles.split():
174                         if not files.count(aof):
175                             if not os.path.isabs(aof):
176                                 aof = os.path.join(os.path.dirname(f),aof)
177                             files.append(aof)
178                 for var in bb_data.keys():
179                     if data.getVarFlag(var, "handler", bb_data) and data.getVar(var, bb_data):
180                         event.register(data.getVar(var, bb_data))
181                 pkgdata[f] = bb_data
182
183             # now inform the caller
184             if collect_bbfiles.cb is not None:
185                 collect_bbfiles.cb( i + 1, len( newfiles ), f, bb_data, fromCache )
186
187         except IOError, e:
188             bb.error("opening %s: %s" % (f, e))
189             pass
190         except bb.parse.SkipPackage:
191             skipped += 1
192             pass
193         except KeyboardInterrupt:
194             raise
195         except Exception, e:
196             bb.error("%s while parsing %s" % (e, f))
197
198     if collect_bbfiles.cb is not None:
199         print "\rNOTE: Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked ),
200
201 def explode_version(s):
202     import string
203     r = []
204     alpha_regexp = re.compile('^([a-zA-Z]+)(.*)$')
205     numeric_regexp = re.compile('^(\d+)(.*)$')
206     while (s != ''):
207         if s[0] in digits:
208             m = numeric_regexp.match(s)
209             r.append(int(m.group(1)))
210             s = m.group(2)
211             continue
212         if s[0] in ascii_letters:
213             m = alpha_regexp.match(s)
214             r.append(m.group(1))
215             s = m.group(2)
216             continue
217         s = s[1:]
218     return r
219
220 def vercmp_part(a, b):
221     va = explode_version(a)
222     vb = explode_version(b)
223     while True:
224         if va == []:
225             ca = None
226         else:
227             ca = va.pop(0)
228         if vb == []:
229             cb = None
230         else:
231             cb = vb.pop(0)
232         if ca == None and cb == None:
233             return 0
234         if ca > cb:
235             return 1
236         if ca < cb:
237             return -1
238
239 def vercmp(ta, tb):
240     (va, ra) = ta
241     (vb, rb) = tb
242
243     r = vercmp_part(va, vb)
244     if (r == 0):
245         r = vercmp_part(ra, rb)
246     return r