lib/bb/data.py:
[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
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         print "NOTE: Using cache in '%s'" % cache
122         try:
123             os.stat( cache )
124         except OSError:
125             bb.mkdirhier( cache )
126     else: print "NOTE: Not using a cache. Set CACHE = <directory> to enable."
127     files = (data.getVar( "BBFILES", cfg, 1 ) or "").split()
128     data.setVar("BBFILES", " ".join(files), cfg)
129
130     if not len(files):
131         files = get_bbfiles()
132
133     if not len(files):
134         bb.error("no files to build.")
135
136     newfiles = []
137     for f in files:
138         if os.path.isdir(f):
139             dirfiles = find_bbfiles(f)
140             if dirfiles:
141                 newfiles += dirfiles
142                 continue
143         newfiles += glob.glob(f) or [ f ]
144
145     bbmask = bb.data.getVar('BBMASK', cfg, 1) or ""
146     try:
147         bbmask_compiled = re.compile(bbmask)
148     except sre_constants.error:
149         bb.fatal("BBMASK is not a valid regular expression.")
150
151     for i in xrange( len( newfiles ) ):
152         f = newfiles[i]
153         if bbmask and bbmask_compiled.search(f):
154               bb.debug(1, "bbmake: skipping %s" % f)
155               masked += 1
156               continue
157         progressCallback( i + 1, len( newfiles ), f )
158         debug(1, "bbmake: parsing %s" % f)
159
160         # read a file's metadata
161         try:
162             bb_data, fromCache = load_bbfile(f)
163             if fromCache: cached += 1
164             else: parsed += 1
165             deps = None
166             if bb_data is not None:
167                 # allow metadata files to add items to BBFILES
168                 #data.update_data(pkgdata[f])
169                 addbbfiles = data.getVar('BBFILES', bb_data) or None
170                 if addbbfiles:
171                     for aof in addbbfiles.split():
172                         if not files.count(aof):
173                             if not os.path.isabs(aof):
174                                 aof = os.path.join(os.path.dirname(f),aof)
175                             files.append(aof)
176                 for var in bb_data.keys():
177                     if data.getVarFlag(var, "handler", bb_data) and data.getVar(var, bb_data):
178                         event.register(data.getVar(var, bb_data))
179                 pkgdata[f] = bb_data
180         except IOError, e:
181             bb.error("opening %s: %s" % (f, e))
182             pass
183         except bb.parse.SkipPackage:
184             skipped += 1
185             pass
186         except KeyboardInterrupt:
187             raise
188         except Exception, e:
189             bb.error("%s while parsing %s" % (e, f))
190     print "\rNOTE: Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked ), 
191
192 def explode_version(s):
193     import string
194     r = []
195     alpha_regexp = re.compile('^([a-zA-Z]+)(.*)$')
196     numeric_regexp = re.compile('^(\d+)(.*)$')
197     while (s != ''):
198         if s[0] in digits:
199             m = numeric_regexp.match(s)
200             r.append(int(m.group(1)))
201             s = m.group(2)
202             continue
203         if s[0] in ascii_letters:
204             m = alpha_regexp.match(s)
205             r.append(m.group(1))
206             s = m.group(2)
207             continue
208         s = s[1:]
209     return r
210
211 def vercmp_part(a, b):
212     va = explode_version(a)
213     vb = explode_version(b)
214     while True:
215         if va == []:
216             ca = None
217         else:
218             ca = va.pop(0)
219         if vb == []:
220             cb = None
221         else:
222             cb = vb.pop(0)
223         if ca == None and cb == None:
224             return 0
225         if ca > cb:
226             return 1
227         if ca < cb:
228             return -1
229
230 def vercmp(ta, tb):
231     (va, ra) = ta
232     (vb, rb) = tb
233
234     r = vercmp_part(va, vb)
235     if (r == 0):
236         r = vercmp_part(ra, rb)
237     return r