Add the location of the .bb file to BBPATH in the parser's handle() function rather...
[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 try:
32     import cPickle as pickle
33 except ImportError:
34     import pickle
35     print "NOTE: Importing cPickle failed. Falling back to a very slow implementation."
36
37 pkgdata = {}
38 cfg = data.init()
39 cache = None
40 digits = "0123456789"
41 ascii_letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
42 mtime_cache = {}
43
44 def get_bbfiles( path = os.getcwd() ):
45     """Get list of default .bb files by reading out the current directory"""
46     contents = os.listdir(path)
47     bbfiles = []
48     for f in contents:
49         (root, ext) = os.path.splitext(f)
50         if ext == ".bb":
51             bbfiles.append(os.path.abspath(os.path.join(os.getcwd(),f)))
52     return bbfiles
53
54 def find_bbfiles( path ):
55     """Find all the .bb files in a directory (uses find)"""
56     findcmd = 'find ' + path + ' -name *.bb | grep -v SCCS/'
57     try:
58         finddata = os.popen(findcmd)
59     except OSError:
60         return []
61     return finddata.readlines()
62
63 def deps_clean(d):
64     depstr = data.getVar('__depends', d)
65     if depstr:
66         deps = depstr.split(" ")
67         for dep in deps:
68             (f,old_mtime_s) = dep.split("@")
69             old_mtime = int(old_mtime_s)
70             new_mtime = parse.cached_mtime(f)
71             if (new_mtime > old_mtime):
72                 return False
73     return True
74
75 def load_bbfile( bbfile ):
76     """Load and parse one .bb build file"""
77
78     if not cache in [None, '']:
79         cache_bbfile = bbfile.replace( '/', '_' )
80
81         try:
82             cache_mtime = os.stat( "%s/%s" % ( cache, cache_bbfile ) )[8]
83         except OSError:
84             cache_mtime = 0
85         file_mtime = parse.cached_mtime(bbfile)
86
87         if file_mtime > cache_mtime:
88             #print " : '%s' dirty. reparsing..." % bbfile
89             pass
90         else:
91             #print " : '%s' clean. loading from cache..." % bbfile
92             cache_data = unpickle_bb( cache_bbfile )
93             if deps_clean(cache_data):
94                 return cache_data, True
95
96     topdir = data.getVar('TOPDIR', cfg)
97     if not topdir:
98         topdir = os.path.abspath(os.getcwd())
99         # set topdir to here
100         data.setVar('TOPDIR', topdir, cfg)
101     bbfile = os.path.abspath(bbfile)
102     bbfile_loc = os.path.abspath(os.path.dirname(bbfile))
103     # expand tmpdir to include this topdir
104     data.setVar('TMPDIR', data.getVar('TMPDIR', cfg, 1) or "", cfg)
105     # set topdir to location of .bb file
106     topdir = bbfile_loc
107     #data.setVar('TOPDIR', topdir, cfg)
108     # go there
109     oldpath = os.path.abspath(os.getcwd())
110     os.chdir(topdir)
111     bb = copy.deepcopy(cfg)
112     try:
113         parse.handle(bbfile, bb) # read .bb data
114         if not cache in [None, '']: pickle_bb( cache_bbfile, bb) # write cache
115         os.chdir(oldpath)
116         return bb, False
117     finally:
118         os.chdir(oldpath)
119
120 def pickle_bb( bbfile, bb ):
121     p = pickle.Pickler( file( "%s/%s" % ( cache, bbfile ), "wb" ), -1 )
122     p.dump( bb )
123
124 def unpickle_bb( bbfile ):
125     p = pickle.Unpickler( file( "%s/%s" % ( cache, bbfile ), "rb" ) )
126     bb = p.load()
127     funcstr = data.getVar('__functions__', bb)
128     if funcstr:
129         comp = compile(funcstr, "<pickled>", "exec")
130         exec comp in __builtins__
131     return bb
132
133 def collect_bbfiles( progressCallback ):
134     """Collect all available .bb build files"""
135
136     parsed, cached, skipped, masked = 0, 0, 0, 0
137     global cache
138     cache = bb.data.getVar( "CACHE", cfg, 1 )
139     if not cache in [None, '']:
140         print "NOTE: Using cache in '%s'" % cache
141         try:
142             os.stat( cache )
143         except OSError:
144             bb.mkdirhier( cache )
145     else: print "NOTE: Not using a cache. Set CACHE = <directory> to enable."
146     files = (data.getVar( "BBFILES", cfg, 1 ) or "").split()
147     data.setVar("BBFILES", " ".join(files), cfg)
148
149     if not len(files):
150         files = get_bbfiles()
151
152     if not len(files):
153         bb.error("no files to build.")
154
155     newfiles = []
156     for f in files:
157         if os.path.isdir(f):
158             dirfiles = find_bbfiles(f)
159             if dirfiles:
160                 newfiles += dirfiles
161                 continue
162         newfiles += glob.glob(f) or [ f ]
163
164     bbmask = bb.data.getVar('BBMASK', cfg, 1) or ""
165     try:
166         bbmask_compiled = re.compile(bbmask)
167     except sre_constants.error:
168         bb.fatal("BBMASK is not a valid regular expression.")
169
170     for i in xrange( len( newfiles ) ):
171         f = newfiles[i]
172         if bbmask and bbmask_compiled.search(f):
173               bb.debug(1, "bbmake: skipping %s" % f)
174               masked += 1
175               continue
176         progressCallback( i + 1, len( newfiles ), f )
177         debug(1, "bbmake: parsing %s" % f)
178
179         # read a file's metadata
180         try:
181             pkgdata[f], fromCache = load_bbfile(f)
182             if fromCache: cached += 1
183             else: parsed += 1
184             deps = None
185             if pkgdata[f] is not None:
186                 # allow metadata files to add items to BBFILES
187                 #data.update_data(pkgdata[f])
188                 addbbfiles = data.getVar('BBFILES', pkgdata[f]) or None
189                 if addbbfiles:
190                     for aof in addbbfiles.split():
191                         if not files.count(aof):
192                             if not os.path.isabs(aof):
193                                 aof = os.path.join(os.path.dirname(f),aof)
194                             files.append(aof)
195                 for var in pkgdata[f].keys():
196                     if data.getVarFlag(var, "handler", pkgdata[f]) and data.getVar(var, pkgdata[f]):
197                         event.register(data.getVar(var, pkgdata[f]))
198         except IOError, e:
199             bb.error("opening %s: %s" % (f, e))
200             pass
201         except bb.parse.SkipPackage:
202             skipped += 1
203             pass
204         except KeyboardInterrupt:
205             raise
206         except Exception, e:
207             bb.error("%s while parsing %s" % (e, f))
208     print "\rNOTE: Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked ), 
209
210 def explode_version(s):
211     import string
212     r = []
213     alpha_regexp = re.compile('^([a-zA-Z]+)(.*)$')
214     numeric_regexp = re.compile('^(\d+)(.*)$')
215     while (s != ''):
216         if s[0] in digits:
217             m = numeric_regexp.match(s)
218             r.append(int(m.group(1)))
219             s = m.group(2)
220             continue
221         if s[0] in ascii_letters:
222             m = alpha_regexp.match(s)
223             r.append(m.group(1))
224             s = m.group(2)
225             continue
226         s = s[1:]
227     return r
228
229 def vercmp_part(a, b):
230     va = explode_version(a)
231     vb = explode_version(b)
232     while True:
233         if va == []:
234             ca = None
235         else:
236             ca = va.pop(0)
237         if vb == []:
238             cb = None
239         else:
240             cb = vb.pop(0)
241         if ca == None and cb == None:
242             return 0
243         if ca > cb:
244             return 1
245         if ca < cb:
246             return -1
247
248 def vercmp(ta, tb):
249     (va, ra) = ta
250     (vb, rb) = tb
251
252     r = vercmp_part(va, vb)
253     if (r == 0):
254         r = vercmp_part(ra, rb)
255     return r