Revision de32b389

View differences:

configure
29 29
    dest="without_npm",
30 30
    help="Don\'t install the bundled npm package manager")
31 31

  
32
parser.add_option("--without-waf",
33
    action="store_true",
34
    dest="without_waf",
35
    help="Don\'t install node-waf")
36

  
37 32
parser.add_option("--without-ssl",
38 33
    action="store_true",
39 34
    dest="without_ssl",
......
321 316
  o['variables']['v8_no_strict_aliasing'] = 1 # work around compiler bugs
322 317
  o['variables']['node_prefix'] = os.path.expanduser(options.prefix or '')
323 318
  o['variables']['node_install_npm'] = b(not options.without_npm)
324
  o['variables']['node_install_waf'] = b(not options.without_waf)
325 319
  o['default_configuration'] = 'Debug' if options.debug else 'Release'
326 320

  
327 321
  host_arch = host_arch_win() if os.name == 'nt' else host_arch_cc()
doc/api/process.markdown
298 298
      variables:
299 299
       { host_arch: 'x64',
300 300
         node_install_npm: 'true',
301
         node_install_waf: 'true',
302 301
         node_prefix: '',
303 302
         node_shared_v8: 'false',
304 303
         node_shared_zlib: 'false',
tools/install.py
73 73
def install(paths, dst): map(lambda path: try_copy(path, dst), paths)
74 74
def uninstall(paths, dst): map(lambda path: try_remove(path, dst), paths)
75 75

  
76
def waf_files(action):
77
  action(['tools/node-waf'], 'bin/node-waf')
78
  action(['tools/wafadmin/ansiterm.py',
79
          'tools/wafadmin/Build.py',
80
          'tools/wafadmin/Configure.py',
81
          'tools/wafadmin/Constants.py',
82
          'tools/wafadmin/Environment.py',
83
          'tools/wafadmin/__init__.py',
84
          'tools/wafadmin/Logs.py',
85
          'tools/wafadmin/Node.py',
86
          'tools/wafadmin/Options.py',
87
          'tools/wafadmin/pproc.py',
88
          'tools/wafadmin/py3kfixes.py',
89
          'tools/wafadmin/Runner.py',
90
          'tools/wafadmin/Scripting.py',
91
          'tools/wafadmin/TaskGen.py',
92
          'tools/wafadmin/Task.py',
93
          'tools/wafadmin/Tools/ar.py',
94
          'tools/wafadmin/Tools/cc.py',
95
          'tools/wafadmin/Tools/ccroot.py',
96
          'tools/wafadmin/Tools/compiler_cc.py',
97
          'tools/wafadmin/Tools/compiler_cxx.py',
98
          'tools/wafadmin/Tools/compiler_d.py',
99
          'tools/wafadmin/Tools/config_c.py',
100
          'tools/wafadmin/Tools/cxx.py',
101
          'tools/wafadmin/Tools/dmd.py',
102
          'tools/wafadmin/Tools/d.py',
103
          'tools/wafadmin/Tools/gas.py',
104
          'tools/wafadmin/Tools/gcc.py',
105
          'tools/wafadmin/Tools/gdc.py',
106
          'tools/wafadmin/Tools/gnu_dirs.py',
107
          'tools/wafadmin/Tools/gob2.py',
108
          'tools/wafadmin/Tools/gxx.py',
109
          'tools/wafadmin/Tools/icc.py',
110
          'tools/wafadmin/Tools/icpc.py',
111
          'tools/wafadmin/Tools/__init__.py',
112
          'tools/wafadmin/Tools/intltool.py',
113
          'tools/wafadmin/Tools/libtool.py',
114
          'tools/wafadmin/Tools/misc.py',
115
          'tools/wafadmin/Tools/nasm.py',
116
          'tools/wafadmin/Tools/node_addon.py',
117
          'tools/wafadmin/Tools/osx.py',
118
          'tools/wafadmin/Tools/preproc.py',
119
          'tools/wafadmin/Tools/python.py',
120
          'tools/wafadmin/Tools/suncc.py',
121
          'tools/wafadmin/Tools/suncxx.py',
122
          'tools/wafadmin/Tools/unittestw.py',
123
          'tools/wafadmin/Tools/winres.py',
124
          'tools/wafadmin/Tools/xlc.py',
125
          'tools/wafadmin/Tools/xlcxx.py',
126
          'tools/wafadmin/Utils.py'],
127
          'lib/node/')
128

  
129 76
def update_shebang(path, shebang):
130 77
  print 'updating shebang of %s' % path
131 78
  s = open(path, 'r').read()
......
186 133
  # with dtrace support now (oracle's "unbreakable" linux)
187 134
  action(['src/node.d'], 'lib/dtrace/')
188 135

  
189
  if variables.get('node_install_waf'): waf_files(action)
190 136
  if variables.get('node_install_npm'): npm_files(action)
191 137

  
192 138
def run(args):
tools/node-waf
1
#!/usr/bin/env python
2
import os, sys
3

  
4

  
5
join = os.path.join
6
bindir = os.path.dirname(os.path.realpath(__file__))
7
prefix = join(bindir, "..")
8
wafdir = join(prefix, "lib", "node")
9

  
10
w = join(wafdir, 'wafadmin')
11
t = join(w, 'Tools')
12
sys.path = [w, t] + sys.path
13

  
14
import Scripting
15
VERSION="1.5.16"
16
Scripting.prepare(t, os.getcwd(), VERSION, wafdir)
17
sys.exit(0)
tools/waf-light
1
#!/usr/bin/env python
2
# encoding: ISO8859-1
3
# Thomas Nagy, 2005-2010
4

  
5
"""
6
Redistribution and use in source and binary forms, with or without
7
modification, are permitted provided that the following conditions
8
are met:
9

  
10
1. Redistributions of source code must retain the above copyright
11
   notice, this list of conditions and the following disclaimer.
12

  
13
2. Redistributions in binary form must reproduce the above copyright
14
   notice, this list of conditions and the following disclaimer in the
15
   documentation and/or other materials provided with the distribution.
16

  
17
3. The name of the author may not be used to endorse or promote products
18
   derived from this software without specific prior written permission.
19

  
20
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
21
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
24
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
29
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
POSSIBILITY OF SUCH DAMAGE.
31
"""
32

  
33
import os, sys
34
if sys.hexversion<0x203000f: raise ImportError("Waf requires Python >= 2.3")
35

  
36
if 'PSYCOWAF' in os.environ:
37
	try:import psyco;psyco.full()
38
	except:pass
39

  
40
VERSION="1.5.16"
41
REVISION="x"
42
INSTALL="x"
43
C1='x'
44
C2='x'
45
cwd = os.getcwd()
46
join = os.path.join
47

  
48
WAF='waf'
49
def b(x):
50
	return x
51

  
52
if sys.hexversion>0x300000f:
53
	WAF='waf3'
54
	def b(x):
55
		return x.encode()
56

  
57
def err(m):
58
	print(('\033[91mError: %s\033[0m' % m))
59
	sys.exit(1)
60

  
61
def unpack_wafdir(dir):
62
	f = open(sys.argv[0],'rb')
63
	c = "corrupted waf (%d)"
64
	while 1:
65
		line = f.readline()
66
		if not line: err("run waf-light from a folder containing wafadmin")
67
		if line == b('#==>\n'):
68
			txt = f.readline()
69
			if not txt: err(c % 1)
70
			if f.readline()!=b('#<==\n'): err(c % 2)
71
			break
72
	if not txt: err(c % 3)
73
	txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r'))
74

  
75
	import shutil, tarfile
76
	try: shutil.rmtree(dir)
77
	except OSError: pass
78
	try:
79
		for x in ['Tools', '3rdparty']:
80
			os.makedirs(join(dir, 'wafadmin', x))
81
	except OSError:
82
		err("Cannot unpack waf lib into %s\nMove waf into a writeable directory" % dir)
83

  
84
	os.chdir(dir)
85
	tmp = 't.bz2'
86
	t = open(tmp,'wb')
87
	t.write(txt)
88
	t.close()
89

  
90
	t = None
91
	try:
92
		t = tarfile.open(tmp)
93
	except:
94
		try:
95
			os.system('bunzip2 t.bz2')
96
			t = tarfile.open('t')
97
		except:
98
			os.chdir(cwd)
99
			try: shutil.rmtree(dir)
100
			except OSError: pass
101
			err("Waf cannot be unpacked, check that bzip2 support is present")
102

  
103
	for x in t: t.extract(x)
104
	t.close()
105

  
106
	for x in ['Tools', '3rdparty']:
107
		os.chmod(join('wafadmin',x), 493)
108

  
109
	if sys.hexversion>0x300000f:
110
		sys.path = [join(dir, 'wafadmin')] + sys.path
111
		import py3kfixes
112
		py3kfixes.fixdir(dir)
113

  
114
	os.chdir(cwd)
115

  
116
def test(dir):
117
	try: os.stat(join(dir, 'wafadmin')); return os.path.abspath(dir)
118
	except OSError: pass
119

  
120
def find_lib():
121
	name = sys.argv[0]
122
	base = os.path.dirname(os.path.abspath(name))
123

  
124
	#devs use $WAFDIR
125
	w=test(os.environ.get('WAFDIR', ''))
126
	if w: return w
127

  
128
	#waf-light
129
	if name.endswith('waf-light'):
130
		w = test(base)
131
		if w: return w
132
		err("waf-light requires wafadmin -> export WAFDIR=/folder")
133

  
134
	dir = "/lib/%s-%s-%s/" % (WAF, VERSION, REVISION)
135
	for i in [INSTALL,'/usr','/usr/local','/opt']:
136
		w = test(i+dir)
137
		if w: return w
138

  
139
	#waf-local
140
	s = '.%s-%s-%s'
141
	if sys.platform == 'win32': s = s[1:]
142
	dir = join(base, s % (WAF, VERSION, REVISION))
143
	w = test(dir)
144
	if w: return w
145

  
146
	#unpack
147
	unpack_wafdir(dir)
148
	return dir
149

  
150
wafdir = find_lib()
151
w = join(wafdir, 'wafadmin')
152
t = join(w, 'Tools')
153
f = join(w, '3rdparty')
154
sys.path = [w, t, f] + sys.path
155

  
156
if __name__ == '__main__':
157
	import Scripting
158
	Scripting.prepare(t, cwd, VERSION, wafdir)
159

  
tools/wafadmin/Build.py
1
#!/usr/bin/env python
2
# encoding: utf-8
3
# Thomas Nagy, 2005 (ita)
4

  
5
"""
6
Dependency tree holder
7

  
8
The class Build holds all the info related to a build:
9
* file system representation (tree of Node instances)
10
* various cached objects (task signatures, file scan results, ..)
11

  
12
There is only one Build object at a time (bld singleton)
13
"""
14

  
15
import os, sys, errno, re, glob, gc, datetime, shutil
16
try: import cPickle
17
except: import pickle as cPickle
18
import Runner, TaskGen, Node, Scripting, Utils, Environment, Task, Logs, Options
19
from Logs import debug, error, info
20
from Constants import *
21

  
22
SAVED_ATTRS = 'root srcnode bldnode node_sigs node_deps raw_deps task_sigs id_nodes'.split()
23
"Build class members to save"
24

  
25
bld = None
26
"singleton - safe to use when Waf is not used as a library"
27

  
28
class BuildError(Utils.WafError):
29
	def __init__(self, b=None, t=[]):
30
		self.bld = b
31
		self.tasks = t
32
		self.ret = 1
33
		Utils.WafError.__init__(self, self.format_error())
34

  
35
	def format_error(self):
36
		lst = ['Build failed:']
37
		for tsk in self.tasks:
38
			txt = tsk.format_error()
39
			if txt: lst.append(txt)
40
		sep = ' '
41
		if len(lst) > 2:
42
			sep = '\n'
43
		return sep.join(lst)
44

  
45
def group_method(fun):
46
	"""
47
	sets a build context method to execute after the current group has finished executing
48
	this is useful for installing build files:
49
	* calling install_files/install_as will fail if called too early
50
	* people do not want to define install method in their task classes
51

  
52
	TODO: try it
53
	"""
54
	def f(*k, **kw):
55
		if not k[0].is_install:
56
			return False
57

  
58
		postpone = True
59
		if 'postpone' in kw:
60
			postpone = kw['postpone']
61
			del kw['postpone']
62

  
63
		# TODO waf 1.6 in theory there should be no reference to the TaskManager internals here
64
		if postpone:
65
			m = k[0].task_manager
66
			if not m.groups: m.add_group()
67
			m.groups[m.current_group].post_funs.append((fun, k, kw))
68
			if not 'cwd' in kw:
69
				kw['cwd'] = k[0].path
70
		else:
71
			fun(*k, **kw)
72
	return f
73

  
74
class BuildContext(Utils.Context):
75
	"holds the dependency tree"
76
	def __init__(self):
77

  
78
		# not a singleton, but provided for compatibility
79
		global bld
80
		bld = self
81

  
82
		self.task_manager = Task.TaskManager()
83

  
84
		# instead of hashing the nodes, we assign them a unique id when they are created
85
		self.id_nodes = 0
86
		self.idx = {}
87

  
88
		# map names to environments, the 'Release' must be defined
89
		self.all_envs = {}
90

  
91
		# ======================================= #
92
		# code for reading the scripts
93

  
94
		# project build directory - do not reset() from load_dirs()
95
		self.bdir = ''
96

  
97
		# the current directory from which the code is run
98
		# the folder changes everytime a wscript is read
99
		self.path = None
100

  
101
		# Manual dependencies.
102
		self.deps_man = Utils.DefaultDict(list)
103

  
104
		# ======================================= #
105
		# cache variables
106

  
107
		# local cache for absolute paths - cache_node_abspath[variant][node]
108
		self.cache_node_abspath = {}
109

  
110
		# list of folders that are already scanned
111
		# so that we do not need to stat them one more time
112
		self.cache_scanned_folders = {}
113

  
114
		# list of targets to uninstall for removing the empty folders after uninstalling
115
		self.uninstall = []
116

  
117
		# ======================================= #
118
		# tasks and objects
119

  
120
		# build dir variants (release, debug, ..)
121
		for v in 'cache_node_abspath task_sigs node_deps raw_deps node_sigs'.split():
122
			var = {}
123
			setattr(self, v, var)
124

  
125
		self.cache_dir_contents = {}
126

  
127
		self.all_task_gen = []
128
		self.task_gen_cache_names = {}
129
		self.cache_sig_vars = {}
130
		self.log = None
131

  
132
		self.root = None
133
		self.srcnode = None
134
		self.bldnode = None
135

  
136
		# bind the build context to the nodes in use
137
		# this means better encapsulation and no build context singleton
138
		class node_class(Node.Node):
139
			pass
140
		self.node_class = node_class
141
		self.node_class.__module__ = "Node"
142
		self.node_class.__name__ = "Nodu"
143
		self.node_class.bld = self
144

  
145
		self.is_install = None
146

  
147
	def __copy__(self):
148
		"nodes are not supposed to be copied"
149
		raise Utils.WafError('build contexts are not supposed to be cloned')
150

  
151
	def load(self):
152
		"load the cache from the disk"
153
		try:
154
			env = Environment.Environment(os.path.join(self.cachedir, 'build.config.py'))
155
		except (IOError, OSError):
156
			pass
157
		else:
158
			if env['version'] < HEXVERSION:
159
				raise Utils.WafError('Version mismatch! reconfigure the project')
160
			for t in env['tools']:
161
				self.setup(**t)
162

  
163
		try:
164
			gc.disable()
165
			f = data = None
166

  
167
			Node.Nodu = self.node_class
168

  
169
			try:
170
				f = open(os.path.join(self.bdir, DBFILE), 'rb')
171
			except (IOError, EOFError):
172
				# handle missing file/empty file
173
				pass
174

  
175
			try:
176
				if f: data = cPickle.load(f)
177
			except AttributeError:
178
				# handle file of an old Waf version
179
				# that has an attribute which no longer exist
180
				# (e.g. AttributeError: 'module' object has no attribute 'BuildDTO')
181
				if Logs.verbose > 1: raise
182

  
183
			if data:
184
				for x in SAVED_ATTRS: setattr(self, x, data[x])
185
			else:
186
				debug('build: Build cache loading failed')
187

  
188
		finally:
189
			if f: f.close()
190
			gc.enable()
191

  
192
	def save(self):
193
		"store the cache on disk, see self.load"
194
		gc.disable()
195
		self.root.__class__.bld = None
196

  
197
		# some people are very nervous with ctrl+c so we have to make a temporary file
198
		Node.Nodu = self.node_class
199
		db = os.path.join(self.bdir, DBFILE)
200
		file = open(db + '.tmp', 'wb')
201
		data = {}
202
		for x in SAVED_ATTRS: data[x] = getattr(self, x)
203
		cPickle.dump(data, file, -1)
204
		file.close()
205

  
206
		# do not use shutil.move
207
		try: os.unlink(db)
208
		except OSError: pass
209
		os.rename(db + '.tmp', db)
210
		self.root.__class__.bld = self
211
		gc.enable()
212

  
213
	# ======================================= #
214

  
215
	def clean(self):
216
		debug('build: clean called')
217

  
218
		# does not clean files created during the configuration
219
		precious = set([])
220
		for env in self.all_envs.values():
221
			for x in env[CFG_FILES]:
222
				node = self.srcnode.find_resource(x)
223
				if node:
224
					precious.add(node.id)
225

  
226
		def clean_rec(node):
227
			for x in list(node.childs.keys()):
228
				nd = node.childs[x]
229

  
230
				tp = nd.id & 3
231
				if tp == Node.DIR:
232
					clean_rec(nd)
233
				elif tp == Node.BUILD:
234
					if nd.id in precious: continue
235
					for env in self.all_envs.values():
236
						try: os.remove(nd.abspath(env))
237
						except OSError: pass
238
					node.childs.__delitem__(x)
239

  
240
		clean_rec(self.srcnode)
241

  
242
		for v in 'node_sigs node_deps task_sigs raw_deps cache_node_abspath'.split():
243
			setattr(self, v, {})
244

  
245
	def compile(self):
246
		"""The cache file is not written if nothing was build at all (build is up to date)"""
247
		debug('build: compile called')
248

  
249
		"""
250
		import cProfile, pstats
251
		cProfile.run("import Build\nBuild.bld.flush()", 'profi.txt')
252
		p = pstats.Stats('profi.txt')
253
		p.sort_stats('cumulative').print_stats(80)
254
		"""
255
		self.flush()
256
		#"""
257

  
258
		self.generator = Runner.Parallel(self, Options.options.jobs)
259

  
260
		def dw(on=True):
261
			if Options.options.progress_bar:
262
				if on: sys.stderr.write(Logs.colors.cursor_on)
263
				else: sys.stderr.write(Logs.colors.cursor_off)
264

  
265
		debug('build: executor starting')
266

  
267
		back = os.getcwd()
268
		os.chdir(self.bldnode.abspath())
269

  
270
		try:
271
			try:
272
				dw(on=False)
273
				self.generator.start()
274
			except KeyboardInterrupt:
275
				dw()
276
				if Runner.TaskConsumer.consumers:
277
					self.save()
278
				raise
279
			except Exception:
280
				dw()
281
				# do not store anything, for something bad happened
282
				raise
283
			else:
284
				dw()
285
				if Runner.TaskConsumer.consumers:
286
					self.save()
287

  
288
			if self.generator.error:
289
				raise BuildError(self, self.task_manager.tasks_done)
290

  
291
		finally:
292
			os.chdir(back)
293

  
294
	def install(self):
295
		"this function is called for both install and uninstall"
296
		debug('build: install called')
297

  
298
		self.flush()
299

  
300
		# remove empty folders after uninstalling
301
		if self.is_install < 0:
302
			lst = []
303
			for x in self.uninstall:
304
				dir = os.path.dirname(x)
305
				if not dir in lst: lst.append(dir)
306
			lst.sort()
307
			lst.reverse()
308

  
309
			nlst = []
310
			for y in lst:
311
				x = y
312
				while len(x) > 4:
313
					if not x in nlst: nlst.append(x)
314
					x = os.path.dirname(x)
315

  
316
			nlst.sort()
317
			nlst.reverse()
318
			for x in nlst:
319
				try: os.rmdir(x)
320
				except OSError: pass
321

  
322
	def new_task_gen(self, *k, **kw):
323
		if self.task_gen_cache_names:
324
			self.task_gen_cache_names = {}
325

  
326
		kw['bld'] = self
327
		if len(k) == 0:
328
			ret = TaskGen.task_gen(*k, **kw)
329
		else:
330
			cls_name = k[0]
331

  
332
			try: cls = TaskGen.task_gen.classes[cls_name]
333
			except KeyError: raise Utils.WscriptError('%s is not a valid task generator -> %s' %
334
				(cls_name, [x for x in TaskGen.task_gen.classes]))
335
			ret = cls(*k, **kw)
336
		return ret
337

  
338
	def __call__(self, *k, **kw):
339
		if self.task_gen_cache_names:
340
			self.task_gen_cache_names = {}
341

  
342
		kw['bld'] = self
343
		return TaskGen.task_gen(*k, **kw)
344

  
345
	def load_envs(self):
346
		try:
347
			lst = Utils.listdir(self.cachedir)
348
		except OSError, e:
349
			if e.errno == errno.ENOENT:
350
				raise Utils.WafError('The project was not configured: run "waf configure" first!')
351
			else:
352
				raise
353

  
354
		if not lst:
355
			raise Utils.WafError('The cache directory is empty: reconfigure the project')
356

  
357
		for file in lst:
358
			if file.endswith(CACHE_SUFFIX):
359
				env = Environment.Environment(os.path.join(self.cachedir, file))
360
				name = file[:-len(CACHE_SUFFIX)]
361

  
362
				self.all_envs[name] = env
363

  
364
		self.init_variants()
365

  
366
		for env in self.all_envs.values():
367
			for f in env[CFG_FILES]:
368
				newnode = self.path.find_or_declare(f)
369
				try:
370
					hash = Utils.h_file(newnode.abspath(env))
371
				except (IOError, AttributeError):
372
					error("cannot find "+f)
373
					hash = SIG_NIL
374
				self.node_sigs[env.variant()][newnode.id] = hash
375

  
376
		# TODO: hmmm, these nodes are removed from the tree when calling rescan()
377
		self.bldnode = self.root.find_dir(self.bldnode.abspath())
378
		self.path = self.srcnode = self.root.find_dir(self.srcnode.abspath())
379
		self.cwd = self.bldnode.abspath()
380

  
381
	def setup(self, tool, tooldir=None, funs=None):
382
		"setup tools for build process"
383
		if isinstance(tool, list):
384
			for i in tool: self.setup(i, tooldir)
385
			return
386

  
387
		if not tooldir: tooldir = Options.tooldir
388

  
389
		module = Utils.load_tool(tool, tooldir)
390
		if hasattr(module, "setup"): module.setup(self)
391

  
392
	def init_variants(self):
393
		debug('build: init variants')
394

  
395
		lstvariants = []
396
		for env in self.all_envs.values():
397
			if not env.variant() in lstvariants:
398
				lstvariants.append(env.variant())
399
		self.lst_variants = lstvariants
400

  
401
		debug('build: list of variants is %r', lstvariants)
402

  
403
		for name in lstvariants+[0]:
404
			for v in 'node_sigs cache_node_abspath'.split():
405
				var = getattr(self, v)
406
				if not name in var:
407
					var[name] = {}
408

  
409
	# ======================================= #
410
	# node and folder handling
411

  
412
	# this should be the main entry point
413
	def load_dirs(self, srcdir, blddir, load_cache=1):
414
		"this functions should be the start of everything"
415

  
416
		assert(os.path.isabs(srcdir))
417
		assert(os.path.isabs(blddir))
418

  
419
		self.cachedir = os.path.join(blddir, CACHE_DIR)
420

  
421
		if srcdir == blddir:
422
			raise Utils.WafError("build dir must be different from srcdir: %s <-> %s " % (srcdir, blddir))
423

  
424
		self.bdir = blddir
425

  
426
		# try to load the cache file, if it does not exist, nothing happens
427
		self.load()
428

  
429
		if not self.root:
430
			Node.Nodu = self.node_class
431
			self.root = Node.Nodu('', None, Node.DIR)
432

  
433
		if not self.srcnode:
434
			self.srcnode = self.root.ensure_dir_node_from_path(srcdir)
435
		debug('build: srcnode is %s and srcdir %s', self.srcnode.name, srcdir)
436

  
437
		self.path = self.srcnode
438

  
439
		# create this build dir if necessary
440
		try: os.makedirs(blddir)
441
		except OSError: pass
442

  
443
		if not self.bldnode:
444
			self.bldnode = self.root.ensure_dir_node_from_path(blddir)
445

  
446
		self.init_variants()
447

  
448
	def rescan(self, src_dir_node):
449
		"""
450
		look the contents of a (folder)node and update its list of childs
451

  
452
		The intent is to perform the following steps
453
		* remove the nodes for the files that have disappeared
454
		* remove the signatures for the build files that have disappeared
455
		* cache the results of os.listdir
456
		* create the build folder equivalent (mkdir) for each variant
457
		src/bar -> build/Release/src/bar, build/Debug/src/bar
458

  
459
		when a folder in the source directory is removed, we do not check recursively
460
		to remove the unused nodes. To do that, call 'waf clean' and build again.
461
		"""
462

  
463
		# do not rescan over and over again
464
		# TODO use a single variable in waf 1.6
465
		if self.cache_scanned_folders.get(src_dir_node.id, None): return
466
		self.cache_scanned_folders[src_dir_node.id] = True
467

  
468
		# TODO remove in waf 1.6
469
		if hasattr(self, 'repository'): self.repository(src_dir_node)
470

  
471
		if not src_dir_node.name and sys.platform == 'win32':
472
			# the root has no name, contains drive letters, and cannot be listed
473
			return
474

  
475

  
476
		# first, take the case of the source directory
477
		parent_path = src_dir_node.abspath()
478
		try:
479
			lst = set(Utils.listdir(parent_path))
480
		except OSError:
481
			lst = set([])
482

  
483
		# TODO move this at the bottom
484
		self.cache_dir_contents[src_dir_node.id] = lst
485

  
486
		# hash the existing source files, remove the others
487
		cache = self.node_sigs[0]
488
		for x in src_dir_node.childs.values():
489
			if x.id & 3 != Node.FILE: continue
490
			if x.name in lst:
491
				try:
492
					cache[x.id] = Utils.h_file(x.abspath())
493
				except IOError:
494
					raise Utils.WafError('The file %s is not readable or has become a dir' % x.abspath())
495
			else:
496
				try: del cache[x.id]
497
				except KeyError: pass
498

  
499
				del src_dir_node.childs[x.name]
500

  
501

  
502
		# first obtain the differences between srcnode and src_dir_node
503
		h1 = self.srcnode.height()
504
		h2 = src_dir_node.height()
505

  
506
		lst = []
507
		child = src_dir_node
508
		while h2 > h1:
509
			lst.append(child.name)
510
			child = child.parent
511
			h2 -= 1
512
		lst.reverse()
513

  
514
		# list the files in the build dirs
515
		try:
516
			for variant in self.lst_variants:
517
				sub_path = os.path.join(self.bldnode.abspath(), variant , *lst)
518
				self.listdir_bld(src_dir_node, sub_path, variant)
519
		except OSError:
520

  
521
			# listdir failed, remove the build node signatures for all variants
522
			for node in src_dir_node.childs.values():
523
				if node.id & 3 != Node.BUILD:
524
					continue
525

  
526
				for dct in self.node_sigs.values():
527
					if node.id in dct:
528
						dct.__delitem__(node.id)
529

  
530
				# the policy is to avoid removing nodes representing directories
531
				src_dir_node.childs.__delitem__(node.name)
532

  
533
			for variant in self.lst_variants:
534
				sub_path = os.path.join(self.bldnode.abspath(), variant , *lst)
535
				try:
536
					os.makedirs(sub_path)
537
				except OSError:
538
					pass
539

  
540
	# ======================================= #
541
	def listdir_src(self, parent_node):
542
		"""do not use, kept for compatibility"""
543
		pass
544

  
545
	def remove_node(self, node):
546
		"""do not use, kept for compatibility"""
547
		pass
548

  
549
	def listdir_bld(self, parent_node, path, variant):
550
		"""in this method we do not add timestamps but we remove them
551
		when the files no longer exist (file removed in the build dir)"""
552

  
553
		i_existing_nodes = [x for x in parent_node.childs.values() if x.id & 3 == Node.BUILD]
554

  
555
		lst = set(Utils.listdir(path))
556
		node_names = set([x.name for x in i_existing_nodes])
557
		remove_names = node_names - lst
558

  
559
		# remove the stamps of the build nodes that no longer exist on the filesystem
560
		ids_to_remove = [x.id for x in i_existing_nodes if x.name in remove_names]
561
		cache = self.node_sigs[variant]
562
		for nid in ids_to_remove:
563
			if nid in cache:
564
				cache.__delitem__(nid)
565

  
566
	def get_env(self):
567
		return self.env_of_name('Release')
568
	def set_env(self, name, val):
569
		self.all_envs[name] = val
570

  
571
	env = property(get_env, set_env)
572

  
573
	def add_manual_dependency(self, path, value):
574
		if isinstance(path, Node.Node):
575
			node = path
576
		elif os.path.isabs(path):
577
			node = self.root.find_resource(path)
578
		else:
579
			node = self.path.find_resource(path)
580
		self.deps_man[node.id].append(value)
581

  
582
	def launch_node(self):
583
		"""return the launch directory as a node"""
584
		# p_ln is kind of private, but public in case if
585
		try:
586
			return self.p_ln
587
		except AttributeError:
588
			self.p_ln = self.root.find_dir(Options.launch_dir)
589
			return self.p_ln
590

  
591
	def glob(self, pattern, relative=True):
592
		"files matching the pattern, seen from the current folder"
593
		path = self.path.abspath()
594
		files = [self.root.find_resource(x) for x in glob.glob(path+os.sep+pattern)]
595
		if relative:
596
			files = [x.path_to_parent(self.path) for x in files if x]
597
		else:
598
			files = [x.abspath() for x in files if x]
599
		return files
600

  
601
	## the following methods are candidates for the stable apis ##
602

  
603
	def add_group(self, *k):
604
		self.task_manager.add_group(*k)
605

  
606
	def set_group(self, *k, **kw):
607
		self.task_manager.set_group(*k, **kw)
608

  
609
	def hash_env_vars(self, env, vars_lst):
610
		"""hash environment variables
611
		['CXX', ..] -> [env['CXX'], ..] -> md5()"""
612

  
613
		# ccroot objects use the same environment for building the .o at once
614
		# the same environment and the same variables are used
615

  
616
		idx = str(id(env)) + str(vars_lst)
617
		try: return self.cache_sig_vars[idx]
618
		except KeyError: pass
619

  
620
		lst = [str(env[a]) for a in vars_lst]
621
		ret = Utils.h_list(lst)
622
		debug('envhash: %r %r', ret, lst)
623

  
624
		# next time
625
		self.cache_sig_vars[idx] = ret
626
		return ret
627

  
628
	def name_to_obj(self, name, env):
629
		"""retrieve a task generator from its name or its target name
630
		remember that names must be unique"""
631
		cache = self.task_gen_cache_names
632
		if not cache:
633
			# create the index lazily
634
			for x in self.all_task_gen:
635
				vt = x.env.variant() + '_'
636
				if x.name:
637
					cache[vt + x.name] = x
638
				else:
639
					if isinstance(x.target, str):
640
						target = x.target
641
					else:
642
						target = ' '.join(x.target)
643
					v = vt + target
644
					if not cache.get(v, None):
645
						cache[v] = x
646
		return cache.get(env.variant() + '_' + name, None)
647

  
648
	def flush(self, all=1):
649
		"""tell the task generators to create the tasks"""
650

  
651
		self.ini = datetime.datetime.now()
652
		# force the initialization of the mapping name->object in flush
653
		# name_to_obj can be used in userland scripts, in that case beware of incomplete mapping
654
		self.task_gen_cache_names = {}
655
		self.name_to_obj('', self.env)
656

  
657
		debug('build: delayed operation TaskGen.flush() called')
658

  
659
		if Options.options.compile_targets:
660
			debug('task_gen: posting objects listed in compile_targets')
661

  
662
			# ensure the target names exist, fail before any post()
663
			target_objects = Utils.DefaultDict(list)
664
			for target_name in Options.options.compile_targets.split(','):
665
				# trim target_name (handle cases when the user added spaces to targets)
666
				target_name = target_name.strip()
667
				for env in self.all_envs.values():
668
					obj = self.name_to_obj(target_name, env)
669
					if obj:
670
						target_objects[target_name].append(obj)
671
				if not target_name in target_objects and all:
672
					raise Utils.WafError("target '%s' does not exist" % target_name)
673

  
674
			to_compile = []
675
			for x in target_objects.values():
676
				for y in x:
677
					to_compile.append(id(y))
678

  
679
			# tasks must be posted in order of declaration
680
			# we merely apply a filter to discard the ones we are not interested in
681
			for i in xrange(len(self.task_manager.groups)):
682
				g = self.task_manager.groups[i]
683
				self.task_manager.current_group = i
684
				if Logs.verbose:
685
					Logs.debug('group: group %s' % ([x for x in self.task_manager.groups_names if id(self.task_manager.groups_names[x]) == id(g)][0]))
686

  
687
				for tg in g.tasks_gen:
688
					if id(tg) in to_compile:
689
						if Logs.verbose:
690
							Logs.debug('group: %s' % tg)
691
						tg.post()
692

  
693
		else:
694
			debug('task_gen: posting objects (normal)')
695
			ln = self.launch_node()
696
			# if the build is started from the build directory, do as if it was started from the top-level
697
			# for the pretty-printing (Node.py), the two lines below cannot be moved to Build::launch_node
698
			if ln.is_child_of(self.bldnode) or not ln.is_child_of(self.srcnode):
699
				ln = self.srcnode
700

  
701
			# if the project file is located under the source directory, build all targets by default
702
			# else 'waf configure build' does nothing
703
			proj_node = self.root.find_dir(os.path.split(Utils.g_module.root_path)[0])
704
			if proj_node.id != self.srcnode.id:
705
				ln = self.srcnode
706

  
707
			for i in xrange(len(self.task_manager.groups)):
708
				g = self.task_manager.groups[i]
709
				self.task_manager.current_group = i
710
				if Logs.verbose:
711
					Logs.debug('group: group %s' % ([x for x in self.task_manager.groups_names if id(self.task_manager.groups_names[x]) == id(g)][0]))
712
				for tg in g.tasks_gen:
713
					if not tg.path.is_child_of(ln):
714
						continue
715
					if Logs.verbose:
716
						Logs.debug('group: %s' % tg)
717
					tg.post()
718

  
719
	def env_of_name(self, name):
720
		try:
721
			return self.all_envs[name]
722
		except KeyError:
723
			error('no such environment: '+name)
724
			return None
725

  
726
	def progress_line(self, state, total, col1, col2):
727
		n = len(str(total))
728

  
729
		Utils.rot_idx += 1
730
		ind = Utils.rot_chr[Utils.rot_idx % 4]
731

  
732
		ini = self.ini
733

  
734
		pc = (100.*state)/total
735
		eta = Utils.get_elapsed_time(ini)
736
		fs = "[%%%dd/%%%dd][%%s%%2d%%%%%%s][%s][" % (n, n, ind)
737
		left = fs % (state, total, col1, pc, col2)
738
		right = '][%s%s%s]' % (col1, eta, col2)
739

  
740
		cols = Utils.get_term_cols() - len(left) - len(right) + 2*len(col1) + 2*len(col2)
741
		if cols < 7: cols = 7
742

  
743
		ratio = int((cols*state)/total) - 1
744

  
745
		bar = ('='*ratio+'>').ljust(cols)
746
		msg = Utils.indicator % (left, bar, right)
747

  
748
		return msg
749

  
750

  
751
	# do_install is not used anywhere
752
	def do_install(self, src, tgt, chmod=O644):
753
		"""returns true if the file was effectively installed or uninstalled, false otherwise"""
754
		if self.is_install > 0:
755
			if not Options.options.force:
756
				# check if the file is already there to avoid a copy
757
				try:
758
					st1 = os.stat(tgt)
759
					st2 = os.stat(src)
760
				except OSError:
761
					pass
762
				else:
763
					# same size and identical timestamps -> make no copy
764
					if st1.st_mtime >= st2.st_mtime and st1.st_size == st2.st_size:
765
						return False
766

  
767
			srclbl = src.replace(self.srcnode.abspath(None)+os.sep, '')
768
			info("* installing %s as %s" % (srclbl, tgt))
769

  
770
			# following is for shared libs and stale inodes (-_-)
771
			try: os.remove(tgt)
772
			except OSError: pass
773

  
774
			try:
775
				shutil.copy2(src, tgt)
776
				if chmod >= 0: os.chmod(tgt, chmod)
777
			except IOError:
778
				try:
779
					os.stat(src)
780
				except (OSError, IOError):
781
					error('File %r does not exist' % src)
782
				raise Utils.WafError('Could not install the file %r' % tgt)
783
			return True
784

  
785
		elif self.is_install < 0:
786
			info("* uninstalling %s" % tgt)
787

  
788
			self.uninstall.append(tgt)
789

  
790
			try:
791
				os.remove(tgt)
792
			except OSError, e:
793
				if e.errno != errno.ENOENT:
794
					if not getattr(self, 'uninstall_error', None):
795
						self.uninstall_error = True
796
						Logs.warn('build: some files could not be uninstalled (retry with -vv to list them)')
797
					if Logs.verbose > 1:
798
						Logs.warn('could not remove %s (error code %r)' % (e.filename, e.errno))
799
			return True
800

  
801
	red = re.compile(r"^([A-Za-z]:)?[/\\\\]*")
802
	def get_install_path(self, path, env=None):
803
		"installation path prefixed by the destdir, the variables like in '${PREFIX}/bin' are substituted"
804
		if not env: env = self.env
805
		destdir = env.get_destdir()
806
		path = path.replace('/', os.sep)
807
		destpath = Utils.subst_vars(path, env)
808
		if destdir:
809
			destpath = os.path.join(destdir, self.red.sub('', destpath))
810
		return destpath
811

  
812
	def install_dir(self, path, env=None):
813
		"""
814
		create empty folders for the installation (very rarely used)
815
		"""
816
		if env:
817
			assert isinstance(env, Environment.Environment), "invalid parameter"
818
		else:
819
			env = self.env
820

  
821
		if not path:
822
			return []
823

  
824
		destpath = self.get_install_path(path, env)
825

  
826
		if self.is_install > 0:
827
			info('* creating %s' % destpath)
828
			Utils.check_dir(destpath)
829
		elif self.is_install < 0:
830
			info('* removing %s' % destpath)
831
			self.uninstall.append(destpath + '/xxx') # yes, ugly
832

  
833
	def install_files(self, path, files, env=None, chmod=O644, relative_trick=False, cwd=None):
834
		"""To install files only after they have been built, put the calls in a method named
835
		post_build on the top-level wscript
836

  
837
		The files must be a list and contain paths as strings or as Nodes
838

  
839
		The relative_trick flag can be set to install folders, use bld.path.ant_glob() with it
840
		"""
841
		if env:
842
			assert isinstance(env, Environment.Environment), "invalid parameter"
843
		else:
844
			env = self.env
845

  
846
		if not path: return []
847

  
848
		if not cwd:
849
			cwd = self.path
850

  
851
		if isinstance(files, str) and '*' in files:
852
			gl = cwd.abspath() + os.sep + files
853
			lst = glob.glob(gl)
854
		else:
855
			lst = Utils.to_list(files)
856

  
857
		if not getattr(lst, '__iter__', False):
858
			lst = [lst]
859

  
860
		destpath = self.get_install_path(path, env)
861

  
862
		Utils.check_dir(destpath)
863

  
864
		installed_files = []
865
		for filename in lst:
866
			if isinstance(filename, str) and os.path.isabs(filename):
867
				alst = Utils.split_path(filename)
868
				destfile = os.path.join(destpath, alst[-1])
869
			else:
870
				if isinstance(filename, Node.Node):
871
					nd = filename
872
				else:
873
					nd = cwd.find_resource(filename)
874
				if not nd:
875
					raise Utils.WafError("Unable to install the file %r (not found in %s)" % (filename, cwd))
876

  
877
				if relative_trick:
878
					destfile = os.path.join(destpath, filename)
879
					Utils.check_dir(os.path.dirname(destfile))
880
				else:
881
					destfile = os.path.join(destpath, nd.name)
882

  
883
				filename = nd.abspath(env)
884

  
885
			if self.do_install(filename, destfile, chmod):
886
				installed_files.append(destfile)
887
		return installed_files
888

  
889
	def install_as(self, path, srcfile, env=None, chmod=O644, cwd=None):
890
		"""
891
		srcfile may be a string or a Node representing the file to install
892

  
893
		returns True if the file was effectively installed, False otherwise
894
		"""
895
		if env:
896
			assert isinstance(env, Environment.Environment), "invalid parameter"
897
		else:
898
			env = self.env
899

  
900
		if not path:
901
			raise Utils.WafError("where do you want to install %r? (%r?)" % (srcfile, path))
902

  
903
		if not cwd:
904
			cwd = self.path
905

  
906
		destpath = self.get_install_path(path, env)
907

  
908
		dir, name = os.path.split(destpath)
909
		Utils.check_dir(dir)
910

  
911
		# the source path
912
		if isinstance(srcfile, Node.Node):
913
			src = srcfile.abspath(env)
914
		else:
915
			src = srcfile
916
			if not os.path.isabs(srcfile):
917
				node = cwd.find_resource(srcfile)
918
				if not node:
919
					raise Utils.WafError("Unable to install the file %r (not found in %s)" % (srcfile, cwd))
920
				src = node.abspath(env)
921

  
922
		return self.do_install(src, destpath, chmod)
923

  
924
	def symlink_as(self, path, src, env=None, cwd=None):
925
		"""example:  bld.symlink_as('${PREFIX}/lib/libfoo.so', 'libfoo.so.1.2.3') """
926

  
927
		if sys.platform == 'win32':
928
			# well, this *cannot* work
929
			return
930

  
931
		if not path:
932
			raise Utils.WafError("where do you want to install %r? (%r?)" % (src, path))
933

  
934
		tgt = self.get_install_path(path, env)
935

  
936
		dir, name = os.path.split(tgt)
937
		Utils.check_dir(dir)
938

  
939
		if self.is_install > 0:
940
			link = False
941
			if not os.path.islink(tgt):
942
				link = True
943
			elif os.readlink(tgt) != src:
944
				link = True
945

  
946
			if link:
947
				try: os.remove(tgt)
948
				except OSError: pass
949

  
950
				info('* symlink %s (-> %s)' % (tgt, src))
951
				os.symlink(src, tgt)
952
			return 0
953

  
954
		else: # UNINSTALL
955
			try:
956
				info('* removing %s' % (tgt))
957
				os.remove(tgt)
958
				return 0
959
			except OSError:
960
				return 1
961

  
962
	def exec_command(self, cmd, **kw):
963
		# 'runner' zone is printed out for waf -v, see wafadmin/Options.py
964
		debug('runner: system command -> %s', cmd)
965
		if self.log:
966
			self.log.write('%s\n' % cmd)
967
			kw['log'] = self.log
968
		try:
969
			if not kw.get('cwd', None):
970
				kw['cwd'] = self.cwd
971
		except AttributeError:
972
			self.cwd = kw['cwd'] = self.bldnode.abspath()
973
		return Utils.exec_command(cmd, **kw)
974

  
975
	def printout(self, s):
976
		f = self.log or sys.stderr
977
		f.write(s)
978
		f.flush()
979

  
980
	def add_subdirs(self, dirs):
981
		self.recurse(dirs, 'build')
982

  
983
	def pre_recurse(self, name_or_mod, path, nexdir):
984
		if not hasattr(self, 'oldpath'):
985
			self.oldpath = []
986
		self.oldpath.append(self.path)
987
		self.path = self.root.find_dir(nexdir)
988
		return {'bld': self, 'ctx': self}
989

  
990
	def post_recurse(self, name_or_mod, path, nexdir):
991
		self.path = self.oldpath.pop()
992

  
993
	###### user-defined behaviour
994

  
995
	def pre_build(self):
996
		if hasattr(self, 'pre_funs'):
997
			for m in self.pre_funs:
998
				m(self)
999

  
1000
	def post_build(self):
1001
		if hasattr(self, 'post_funs'):
1002
			for m in self.post_funs:
1003
				m(self)
1004

  
1005
	def add_pre_fun(self, meth):
1006
		try: self.pre_funs.append(meth)
1007
		except AttributeError: self.pre_funs = [meth]
1008

  
1009
	def add_post_fun(self, meth):
1010
		try: self.post_funs.append(meth)
1011
		except AttributeError: self.post_funs = [meth]
1012

  
1013
	def use_the_magic(self):
1014
		Task.algotype = Task.MAXPARALLEL
1015
		Task.file_deps = Task.extract_deps
1016
		self.magic = True
1017

  
1018
	install_as = group_method(install_as)
1019
	install_files = group_method(install_files)
1020
	symlink_as = group_method(symlink_as)
1021

  
tools/wafadmin/Configure.py
1
#!/usr/bin/env python
2
# encoding: utf-8
3
# Thomas Nagy, 2005-2008 (ita)
4

  
5
"""
6
Configuration system
7

  
8
A configuration instance is created when "waf configure" is called, it is used to:
9
* create data dictionaries (Environment instances)
10
* store the list of modules to import
11

  
12
The old model (copied from Scons) was to store logic (mapping file extensions to functions)
13
along with the data. In Waf a way was found to separate that logic by adding an indirection
14
layer (storing the names in the Environment instances)
15

  
16
In the new model, the logic is more object-oriented, and the user scripts provide the
17
logic. The data files (Environments) must contain configuration data only (flags, ..).
18

  
19
Note: the c/c++ related code is in the module config_c
20
"""
21

  
22
import os, shlex, sys, time
23
try: import cPickle
24
except ImportError: import pickle as cPickle
25
import Environment, Utils, Options, Logs
26
from Logs import warn
27
from Constants import *
28

  
29
try:
30
	from urllib import request
31
except:
32
	from urllib import urlopen
33
else:
34
	urlopen = request.urlopen
35

  
36
conf_template = '''# project %(app)s configured on %(now)s by
37
# waf %(wafver)s (abi %(abi)s, python %(pyver)x on %(systype)s)
38
# using %(args)s
39
#
40
'''
41

  
42
class ConfigurationError(Utils.WscriptError):
43
	pass
44

  
45
autoconfig = False
46
"reconfigure the project automatically"
47

  
48
def find_file(filename, path_list):
49
	"""find a file in a list of paths
50
	@param filename: name of the file to search for
51
	@param path_list: list of directories to search
52
	@return: the first occurrence filename or '' if filename could not be found
53
"""
54
	for directory in Utils.to_list(path_list):
55
		if os.path.exists(os.path.join(directory, filename)):
56
			return directory
57
	return ''
58

  
59
def find_program_impl(env, filename, path_list=[], var=None, environ=None):
60
	"""find a program in folders path_lst, and sets env[var]
61
	@param env: environment
62
	@param filename: name of the program to search for
63
	@param path_list: list of directories to search for filename
64
	@param var: environment value to be checked for in env or os.environ
65
	@return: either the value that is referenced with [var] in env or os.environ
66
         or the first occurrence filename or '' if filename could not be found
67
"""
68

  
69
	if not environ:
70
		environ = os.environ
71

  
72
	try: path_list = path_list.split()
73
	except AttributeError: pass
74

  
75
	if var:
76
		if env[var]: return env[var]
77
		if var in environ: env[var] = environ[var]
78

  
79
	if not path_list: path_list = environ.get('PATH', '').split(os.pathsep)
80

  
81
	ext = (Options.platform == 'win32') and '.exe,.com,.bat,.cmd' or ''
82
	for y in [filename+x for x in ext.split(',')]:
83
		for directory in path_list:
84
			x = os.path.join(directory, y)
85
			if os.path.isfile(x):
86
				if var: env[var] = x
87
				return x
88
	return ''
89

  
90
class ConfigurationContext(Utils.Context):
91
	tests = {}
92
	error_handlers = []
93
	def __init__(self, env=None, blddir='', srcdir=''):
94
		self.env = None
95
		self.envname = ''
96

  
97
		self.environ = dict(os.environ)
98

  
99
		self.line_just = 40
100

  
101
		self.blddir = blddir
102
		self.srcdir = srcdir
103
		self.all_envs = {}
104

  
105
		# curdir: necessary for recursion
106
		self.cwd = self.curdir = os.getcwd()
107

  
108
		self.tools = [] # tools loaded in the configuration, and that will be loaded when building
109

  
110
		self.setenv(DEFAULT)
111

  
112
		self.lastprog = ''
113

  
114
		self.hash = 0
115
		self.files = []
116

  
117
		self.tool_cache = []
118

  
119
		if self.blddir:
120
			self.post_init()
121

  
122
	def post_init(self):
123

  
124
		self.cachedir = os.path.join(self.blddir, CACHE_DIR)
125

  
126
		path = os.path.join(self.blddir, WAF_CONFIG_LOG)
127
		try: os.unlink(path)
128
		except (OSError, IOError): pass
129

  
130
		try:
131
			self.log = open(path, 'w')
132
		except (OSError, IOError):
133
			self.fatal('could not open %r for writing' % path)
134

  
135
		app = Utils.g_module.APPNAME
136
		if app:
137
			ver = getattr(Utils.g_module, 'VERSION', '')
138
			if ver:
139
				app = "%s (%s)" % (app, ver)
140

  
141
		now = time.ctime()
142
		pyver = sys.hexversion
143
		systype = sys.platform
144
		args = " ".join(sys.argv)
145
		wafver = WAFVERSION
146
		abi = ABI
147
		self.log.write(conf_template % vars())
148

  
149
	def __del__(self):
150
		"""cleanup function: close config.log"""
151

  
152
		# may be ran by the gc, not always after initialization
153
		if hasattr(self, 'log') and self.log:
154
			self.log.close()
155

  
156
	def fatal(self, msg):
157
		raise ConfigurationError(msg)
158

  
159
	def check_tool(self, input, tooldir=None, funs=None):
160
		"load a waf tool"
161

  
162
		tools = Utils.to_list(input)
163
		if tooldir: tooldir = Utils.to_list(tooldir)
164
		for tool in tools:
165
			tool = tool.replace('++', 'xx')
166
			if tool == 'java': tool = 'javaw'
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff