have bb.parse.handle() throw ParseError if the input file is not
[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         debug(1, "bbmake: parsing %s" % f)
158
159         # read a file's metadata
160         try:
161             bb_data, fromCache = load_bbfile(f)
162             if fromCache: cached += 1
163             else: parsed += 1
164             deps = None
165             if bb_data is not None:
166                 # allow metadata files to add items to BBFILES
167                 #data.update_data(pkgdata[f])
168                 addbbfiles = data.getVar('BBFILES', bb_data) or None
169                 if addbbfiles:
170                     for aof in addbbfiles.split():
171                         if not files.count(aof):
172                             if not os.path.isabs(aof):
173                                 aof = os.path.join(os.path.dirname(f),aof)
174                             files.append(aof)
175                 for var in bb_data.keys():
176                     if data.getVarFlag(var, "handler", bb_data) and data.getVar(var, bb_data):
177                         event.register(data.getVar(var, bb_data))
178                 pkgdata[f] = bb_data
179
180             # now inform the caller
181             progressCallback( i + 1, len( newfiles ), f, bb_data, fromCache )
182         except IOError, e:
183             bb.error("opening %s: %s" % (f, e))
184             pass
185         except bb.parse.SkipPackage:
186             skipped += 1
187             pass
188         except KeyboardInterrupt:
189             raise
190         except Exception, e:
191             bb.error("%s while parsing %s" % (e, f))
192     print "\rNOTE: Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked ), 
193
194 def explode_version(s):
195     import string
196     r = []
197     alpha_regexp = re.compile('^([a-zA-Z]+)(.*)$')
198     numeric_regexp = re.compile('^(\d+)(.*)$')
199     while (s != ''):
200         if s[0] in digits:
201             m = numeric_regexp.match(s)
202             r.append(int(m.group(1)))
203             s = m.group(2)
204             continue
205         if s[0] in ascii_letters:
206             m = alpha_regexp.match(s)
207             r.append(m.group(1))
208             s = m.group(2)
209             continue
210         s = s[1:]
211     return r
212
213 def vercmp_part(a, b):
214     va = explode_version(a)
215     vb = explode_version(b)
216     while True:
217         if va == []:
218             ca = None
219         else:
220             ca = va.pop(0)
221         if vb == []:
222             cb = None
223         else:
224             cb = vb.pop(0)
225         if ca == None and cb == None:
226             return 0
227         if ca > cb:
228             return 1
229         if ca < cb:
230             return -1
231
232 def vercmp(ta, tb):
233     (va, ra) = ta
234     (vb, rb) = tb
235
236     r = vercmp_part(va, vb)
237     if (r == 0):
238         r = vercmp_part(ra, rb)
239     return r