aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/develop.py
diff options
context:
space:
mode:
authorJacek Antonelli2008-09-06 18:24:57 -0500
committerJacek Antonelli2008-09-06 18:25:07 -0500
commit798d367d54a6c6379ad355bd8345fa40e31e7fe9 (patch)
tree1921f1708cd0240648c97bc02df2c2ab5f2fc41e /linden/indra/develop.py
parentSecond Life viewer sources 1.20.15 (diff)
downloadmeta-impy-798d367d54a6c6379ad355bd8345fa40e31e7fe9.zip
meta-impy-798d367d54a6c6379ad355bd8345fa40e31e7fe9.tar.gz
meta-impy-798d367d54a6c6379ad355bd8345fa40e31e7fe9.tar.bz2
meta-impy-798d367d54a6c6379ad355bd8345fa40e31e7fe9.tar.xz
Second Life viewer sources 1.21.0-RC
Diffstat (limited to 'linden/indra/develop.py')
-rwxr-xr-xlinden/indra/develop.py676
1 files changed, 676 insertions, 0 deletions
diff --git a/linden/indra/develop.py b/linden/indra/develop.py
new file mode 100755
index 0000000..8edfccc
--- /dev/null
+++ b/linden/indra/develop.py
@@ -0,0 +1,676 @@
1#!/usr/bin/env python
2#
3# @file develop.py
4# @authors Bryan O'Sullivan, Mark Palange, Aaron Brashears
5# @brief Fire and forget script to appropriately configure cmake for SL.
6#
7# $LicenseInfo:firstyear=2007&license=viewergpl$
8#
9# Copyright (c) 2007-2008, Linden Research, Inc.
10#
11# Second Life Viewer Source Code
12# The source code in this file ("Source Code") is provided by Linden Lab
13# to you under the terms of the GNU General Public License, version 2.0
14# ("GPL"), unless you have obtained a separate licensing agreement
15# ("Other License"), formally executed by you and Linden Lab. Terms of
16# the GPL can be found in doc/GPL-license.txt in this distribution, or
17# online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
18#
19# There are special exceptions to the terms and conditions of the GPL as
20# it is applied to this Source Code. View the full text of the exception
21# in the file doc/FLOSS-exception.txt in this software distribution, or
22# online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
23#
24# By copying, modifying or distributing this software, you acknowledge
25# that you have read and understood your obligations described above,
26# and agree to abide by those obligations.
27#
28# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
29# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
30# COMPLETENESS OR PERFORMANCE.
31# $/LicenseInfo$
32
33
34import errno
35import getopt
36import os
37import random
38import re
39import shutil
40import socket
41import sys
42import commands
43
44class CommandError(Exception):
45 pass
46
47
48def mkdir(path):
49 try:
50 os.mkdir(path)
51 return path
52 except OSError, err:
53 if err.errno != errno.EEXIST or not os.path.isdir(path):
54 raise
55
56def quote(opts):
57 return '"' + '" "'.join([ opt.replace('"', '') for opt in opts ]) + '"'
58
59class PlatformSetup(object):
60 generator = None
61 build_types = {}
62 for t in ('Debug', 'Release', 'RelWithDebInfo'):
63 build_types[t.lower()] = t
64
65 build_type = build_types['relwithdebinfo']
66 standalone = 'FALSE'
67 unattended = 'FALSE'
68 distcc = True
69 cmake_opts = []
70
71 def __init__(self):
72 self.script_dir = os.path.realpath(
73 os.path.dirname(__import__(__name__).__file__))
74
75 def os(self):
76 '''Return the name of the OS.'''
77
78 raise NotImplemented('os')
79
80 def arch(self):
81 '''Return the CPU architecture.'''
82
83 return None
84
85 def platform(self):
86 '''Return a stringified two-tuple of the OS name and CPU
87 architecture.'''
88
89 ret = self.os()
90 if self.arch():
91 ret += '-' + self.arch()
92 return ret
93
94 def build_dirs(self):
95 '''Return the top-level directories in which builds occur.
96
97 This can return more than one directory, e.g. if doing a
98 32-bit viewer and server build on Linux.'''
99
100 return ['build-' + self.platform()]
101
102 def cmake_commandline(self, src_dir, build_dir, opts, simple):
103 '''Return the command line to run cmake with.'''
104
105 args = dict(
106 dir=src_dir,
107 generator=self.generator,
108 opts=quote(opts),
109 standalone=self.standalone,
110 unattended=self.unattended,
111 type=self.build_type.upper(),
112 )
113 #if simple:
114 # return 'cmake %(opts)s %(dir)r' % args
115 return ('cmake -DCMAKE_BUILD_TYPE:STRING=%(type)s '
116 '-DSTANDALONE:BOOL=%(standalone)s '
117 '-DUNATTENDED:BOOL=%(unattended)s '
118 '-G %(generator)r %(opts)s %(dir)r' % args)
119
120 def run(self, command, name=None):
121 '''Run a program. If the program fails, raise an exception.'''
122 ret = os.system(command)
123 if ret:
124 if name is None:
125 name = command.split(None, 1)[0]
126 if os.WIFEXITED(ret):
127 event = 'exited'
128 status = 'status %d' % os.WEXITSTATUS(ret)
129 elif os.WIFSIGNALED(ret):
130 event = 'was killed'
131 status = 'signal %d' % os.WTERMSIG(ret)
132 else:
133 event = 'died unexpectedly (!?)'
134 status = '16-bit status %d' % ret
135 raise CommandError('the command %r %s with %s' %
136 (name, event, status))
137
138 def run_cmake(self, args=[]):
139 '''Run cmake.'''
140
141 # do a sanity check to make sure we have a generator
142 if not hasattr(self, 'generator'):
143 raise "No generator available for '%s'" % (self.__name__,)
144 cwd = os.getcwd()
145 created = []
146 try:
147 for d in self.build_dirs():
148 simple = True
149 if mkdir(d):
150 created.append(d)
151 simple = False
152 try:
153 os.chdir(d)
154 cmd = self.cmake_commandline(cwd, d, args, simple)
155 print 'Running %r in %r' % (cmd, d)
156 self.run(cmd, 'cmake')
157 finally:
158 os.chdir(cwd)
159 except:
160 # If we created a directory in which to run cmake and
161 # something went wrong, the directory probably just
162 # contains garbage, so delete it.
163 os.chdir(cwd)
164 for d in created:
165 print 'Cleaning %r' % d
166 shutil.rmtree(d)
167 raise
168
169 def parse_build_opts(self, arguments):
170 opts, targets = getopt.getopt(arguments, 'o:', ['option='])
171 build_opts = []
172 for o, a in opts:
173 if o in ('-o', '--option'):
174 build_opts.append(a)
175 return build_opts, targets
176
177 def run_build(self, opts, targets):
178 '''Build the default targets for this platform.'''
179
180 raise NotImplemented('run_build')
181
182 def cleanup(self):
183 '''Delete all build directories.'''
184
185 cleaned = 0
186 for d in self.build_dirs():
187 if os.path.isdir(d):
188 print 'Cleaning %r' % d
189 shutil.rmtree(d)
190 cleaned += 1
191 if not cleaned:
192 print 'Nothing to clean up!'
193
194 def is_internal_tree(self):
195 '''Indicate whether we are building in an internal source tree.'''
196
197 return os.path.isdir(os.path.join(self.script_dir, 'newsim'))
198
199
200class UnixSetup(PlatformSetup):
201 '''Generic Unixy build instructions.'''
202
203 def __init__(self):
204 super(UnixSetup, self).__init__()
205 self.generator = 'Unix Makefiles'
206
207 def os(self):
208 return 'unix'
209
210 def arch(self):
211 cpu = os.uname()[-1]
212 if cpu.endswith('386'):
213 cpu = 'i386'
214 elif cpu.endswith('86'):
215 cpu = 'i686'
216 elif cpu in ('athlon',):
217 cpu = 'i686'
218 elif cpu == 'Power Macintosh':
219 cpu = 'ppc'
220 return cpu
221
222
223class LinuxSetup(UnixSetup):
224 def __init__(self):
225 super(LinuxSetup, self).__init__()
226
227 def os(self):
228 return 'linux'
229
230 def build_dirs(self):
231 # Only build the server code if (a) we have it and (b) we're
232 # on 32-bit x86.
233 if self.arch() == 'i686' and self.is_internal_tree():
234 return ['viewer-' + self.platform(), 'server-' + self.platform()]
235 else:
236 return ['viewer-' + self.platform()]
237
238 def find_in_path(self, name, defval=None, basename=False):
239 for p in os.getenv('PATH', '/usr/bin').split(':'):
240 path = os.path.join(p, name)
241 if os.access(path, os.X_OK):
242 return [basename and os.path.basename(path) or path]
243 if defval:
244 return [defval]
245 return []
246
247 def cmake_commandline(self, src_dir, build_dir, opts, simple):
248 args = dict(
249 dir=src_dir,
250 generator=self.generator,
251 opts=quote(opts),
252 standalone=self.standalone,
253 unattended=self.unattended,
254 type=self.build_type.upper()
255 )
256 if not self.is_internal_tree():
257 args.update({'cxx':'g++', 'server':'FALSE', 'viewer':'TRUE'})
258 else:
259 if self.distcc:
260 distcc = self.find_in_path('distcc')
261 baseonly = True
262 else:
263 distcc = []
264 baseonly = False
265 if 'server' in build_dir:
266 gcc33 = distcc + self.find_in_path('g++-3.3', 'g++', baseonly)
267 args.update({'cxx':' '.join(gcc33), 'server':'TRUE',
268 'viewer':'FALSE'})
269 else:
270 gcc41 = distcc + self.find_in_path('g++-4.1', 'g++', baseonly)
271 args.update({'cxx': ' '.join(gcc41), 'server':'FALSE',
272 'viewer':'TRUE'})
273 #if simple:
274 # return (('cmake %(opts)s '
275 # '-DSERVER:BOOL=%(server)s '
276 # '-DVIEWER:BOOL=%(viewer)s '
277 # '%(dir)r') % args)
278 cmd = (('cmake -DCMAKE_BUILD_TYPE:STRING=%(type)s '
279 '-G %(generator)r -DSERVER:BOOL=%(server)s '
280 '-DVIEWER:BOOL=%(viewer)s -DSTANDALONE:BOOL=%(standalone)s '
281 '-DUNATTENDED:BOOL=%(unattended)s '
282 '%(opts)s %(dir)r')
283 % args)
284 if 'CXX' not in os.environ:
285 args.update({'cmd':cmd})
286 cmd = ('CXX=%(cxx)r %(cmd)s' % args)
287 return cmd
288
289 def run_build(self, opts, targets):
290 job_count = None
291
292 for i in range(len(opts)):
293 if opts[i].startswith('-j'):
294 try:
295 job_count = int(opts[i][2:])
296 except ValueError:
297 try:
298 job_count = int(opts[i+1])
299 except ValueError:
300 job_count = True
301
302 def get_cpu_count():
303 count = 0
304 for line in open('/proc/cpuinfo'):
305 if re.match(r'processor\s*:', line):
306 count += 1
307 return count
308
309 def localhost():
310 count = get_cpu_count()
311 return 'localhost/' + str(count), count
312
313 def get_distcc_hosts():
314 try:
315 hosts = []
316 name = os.getenv('DISTCC_DIR', '/etc/distcc') + '/hosts'
317 for l in open(name):
318 l = l[l.find('#')+1:].strip()
319 if l: hosts.append(l)
320 return hosts
321 except IOError:
322 return (os.getenv('DISTCC_HOSTS', '').split() or
323 [localhost()[0]])
324
325 def count_distcc_hosts():
326 cpus = 0
327 hosts = 0
328 for host in get_distcc_hosts():
329 m = re.match(r'.*/(\d+)', host)
330 hosts += 1
331 cpus += m and int(m.group(1)) or 1
332 return hosts, cpus
333
334 def mk_distcc_hosts():
335 '''Generate a list of LL-internal machines to build on.'''
336 loc_entry, cpus = localhost()
337 hosts = [loc_entry]
338 dead = []
339 stations = [s for s in xrange(36) if s not in dead]
340 random.shuffle(stations)
341 hosts += ['station%d.lindenlab.com/2,lzo' % s for s in stations]
342 cpus += 2 * len(stations)
343 return ' '.join(hosts), cpus
344
345 if job_count is None:
346 hosts, job_count = count_distcc_hosts()
347 if hosts == 1 and socket.gethostname().startswith('station'):
348 hosts, job_count = mk_distcc_hosts()
349 os.putenv('DISTCC_HOSTS', hosts)
350 opts.extend(['-j', str(job_count)])
351
352 if targets:
353 targets = ' '.join(targets)
354 else:
355 targets = 'all'
356
357 for d in self.build_dirs():
358 cmd = 'make -C %r %s %s' % (d, ' '.join(opts), targets)
359 print 'Running %r' % cmd
360 self.run(cmd)
361
362
363class DarwinSetup(UnixSetup):
364 def __init__(self):
365 super(DarwinSetup, self).__init__()
366 self.generator = 'Xcode'
367
368 def os(self):
369 return 'darwin'
370
371 def arch(self):
372 if self.unattended == 'TRUE':
373 return 'universal'
374 else:
375 return UnixSetup.arch(self)
376
377 def cmake_commandline(self, src_dir, build_dir, opts, simple):
378 args = dict(
379 dir=src_dir,
380 generator=self.generator,
381 opts=quote(opts),
382 standalone=self.standalone,
383 unattended=self.unattended,
384 universal='',
385 type=self.build_type.upper()
386 )
387 if self.unattended == 'TRUE':
388 args['universal'] = '-DCMAKE_OSX_ARCHITECTURES:STRING=\'i386;ppc\''
389 #if simple:
390 # return 'cmake %(opts)s %(dir)r' % args
391 return ('cmake -G %(generator)r '
392 '-DCMAKE_BUILD_TYPE:STRING=%(type)s '
393 '-DSTANDALONE:BOOL=%(standalone)s '
394 '-DUNATTENDED:BOOL=%(unattended)s '
395 '%(universal)s '
396 '%(opts)s %(dir)r' % args)
397
398 def run_build(self, opts, targets):
399 cwd = os.getcwd()
400 if targets:
401 targets = ' '.join(['-target ' + repr(t) for t in targets])
402 else:
403 targets = ''
404 cmd = ('xcodebuild -parallelizeTargets '
405 '-configuration %s %s %s' %
406 (self.build_type, ' '.join(opts), targets))
407 for d in self.build_dirs():
408 try:
409 os.chdir(d)
410 print 'Running %r in %r' % (cmd, d)
411 self.run(cmd)
412 finally:
413 os.chdir(cwd)
414
415
416class WindowsSetup(PlatformSetup):
417 gens = {
418 'vc71' : {
419 'gen' : r'Visual Studio 7 .NET 2003',
420 'ver' : r'7.1'
421 },
422 'vc80' : {
423 'gen' : r'Visual Studio 8 2005',
424 'ver' : r'8.0'
425 },
426 'vc90' : {
427 'gen' : r'Visual Studio 9 2008',
428 'ver' : r'9.0'
429 }
430 }
431 gens['vs2003'] = gens['vc71']
432 gens['vs2005'] = gens['vc80']
433 gens['vs2008'] = gens['vc90']
434
435 def __init__(self):
436 super(WindowsSetup, self).__init__()
437 self._generator = None
438 self.incredibuild = False
439
440 def _get_generator(self):
441 if self._generator is None:
442 for version in 'vc71 vc80 vc90'.split():
443 if self.find_visual_studio(version):
444 self._generator = version
445 print 'Building with ', self.gens[version]['gen']
446 break
447 else:
448 print >> sys.stderr, 'Cannot find a Visual Studio installation!'
449 eys.exit(1)
450 return self._generator
451
452 def _set_generator(self, gen):
453 self._generator = gen
454
455 generator = property(_get_generator, _set_generator)
456
457 def os(self):
458 return 'win32'
459
460 def build_dirs(self):
461 return ['build-' + self.generator]
462
463 def cmake_commandline(self, src_dir, build_dir, opts, simple):
464 args = dict(
465 dir=src_dir,
466 generator=self.gens[self.generator.lower()]['gen'],
467 opts=quote(opts),
468 standalone=self.standalone,
469 unattended=self.unattended,
470 )
471 #if simple:
472 # return 'cmake %(opts)s "%(dir)s"' % args
473 return ('cmake -G "%(generator)s" '
474 '-DSTANDALONE:BOOL=%(standalone)s '
475 '-DUNATTENDED:BOOL=%(unattended)s '
476 '%(opts)s "%(dir)s"' % args)
477
478 def find_visual_studio(self, gen=None):
479 if gen is None:
480 gen = self._generator
481 gen = gen.lower()
482 try:
483 import _winreg
484 key_str = (r'SOFTWARE\Microsoft\VisualStudio\%s\Setup\VS' %
485 self.gens[gen]['ver'])
486 value_str = (r'EnvironmentDirectory')
487 print ('Reading VS environment from HKEY_LOCAL_MACHINE\%s\%s' %
488 (key_str, value_str))
489 print key_str
490
491 reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE)
492 key = _winreg.OpenKey(reg, key_str)
493 value = _winreg.QueryValueEx(key, value_str)[0]
494 print 'Found: %s' % value
495 return value
496 except WindowsError, err:
497 print >> sys.stderr, "Didn't find ", self.gens[gen]['gen']
498 return ''
499
500 def get_build_cmd(self):
501 if self.incredibuild:
502 config = self.build_type
503 if self.gens[self.generator]['ver'] in [ r'8.0', r'9.0' ]:
504 config = '\"%s|Win32\"' % config
505
506 return "buildconsole Secondlife.sln /build %s" % config
507
508 # devenv.com is CLI friendly, devenv.exe... not so much.
509 return ('"%sdevenv.com" Secondlife.sln /build %s' %
510 (self.find_visual_studio(), self.build_type))
511
512 # this override of run exists because the PlatformSetup version
513 # uses Unix/Mac only calls. Freakin' os module!
514 def run(self, command, name=None):
515 '''Run a program. If the program fails, raise an exception.'''
516 ret = os.system(command)
517 if ret:
518 if name is None:
519 name = command.split(None, 1)[0]
520 raise CommandError('the command %r exited with %s' %
521 (name, ret))
522
523 def run_cmake(self, args=[]):
524 '''Override to add the vstool.exe call after running cmake.'''
525 PlatformSetup.run_cmake(self, args)
526 if self.unattended == 'FALSE':
527 for build_dir in self.build_dirs():
528 vstool_cmd = os.path.join('tools','vstool','VSTool.exe') \
529 + ' --solution ' \
530 + os.path.join(build_dir,'SecondLife.sln') \
531 + ' --config RelWithDebInfo' \
532 + ' --startup secondlife-bin'
533 print 'Running %r in %r' % (vstool_cmd, os.getcwd())
534 self.run(vstool_cmd)
535
536 def run_build(self, opts, targets):
537 cwd = os.getcwd()
538 build_cmd = self.get_build_cmd()
539
540 for d in self.build_dirs():
541 try:
542 os.chdir(d)
543 if targets:
544 for t in targets:
545 cmd = '%s /project %s %s' % (build_cmd, t, ' '.join(opts))
546 print 'Running %r in %r' % (cmd, d)
547 self.run(cmd)
548 else:
549 cmd = '%s %s' % (build_cmd, ' '.join(opts))
550 print 'Running %r in %r' % (cmd, d)
551 self.run(cmd)
552 finally:
553 os.chdir(cwd)
554
555class CygwinSetup(WindowsSetup):
556 def __init__(self):
557 super(CygwinSetup, self).__init__()
558 self.generator = 'vc71'
559
560 def cmake_commandline(self, src_dir, build_dir, opts, simple):
561 dos_dir = commands.getoutput("cygpath -w %s" % src_dir)
562 args = dict(
563 dir=dos_dir,
564 generator=self.gens[self.generator.lower()]['gen'],
565 opts=quote(opts),
566 standalone=self.standalone,
567 unattended=self.unattended,
568 )
569 #if simple:
570 # return 'cmake %(opts)s "%(dir)s"' % args
571 return ('cmake -G "%(generator)s" '
572 '-DUNATTENDED:BOOl=%(unattended)s '
573 '-DSTANDALONE:BOOL=%(standalone)s '
574 '%(opts)s "%(dir)s"' % args)
575
576setup_platform = {
577 'darwin': DarwinSetup,
578 'linux2': LinuxSetup,
579 'win32' : WindowsSetup,
580 'cygwin' : CygwinSetup
581 }
582
583
584usage_msg = '''
585Usage: develop.py [options] command [command-options]
586
587Options:
588 -h | --help print this help message
589 --standalone build standalone, without Linden prebuild libraries
590 --unattended build unattended, do not invoke any tools requiring
591 a human response
592 -t | --type=NAME build type ("Debug", "Release", or "RelWithDebInfo")
593 -N | --no-distcc disable use of distcc
594 -G | --generator=NAME generator name
595 Windows: VC71 or VS2003 (default), VC80 (VS2005) or VC90 (VS2008)
596 Mac OS X: Xcode (default), Unix Makefiles
597 Linux: Unix Makefiles (default), KDevelop3
598Commands:
599 build configure and build default target
600 clean delete all build directories (does not affect sources)
601 configure configure project by running cmake
602
603If you do not specify a command, the default is "configure".
604'''
605
606def main(arguments):
607 setup = setup_platform[sys.platform]()
608 try:
609 opts, args = getopt.getopt(
610 arguments,
611 '?hNt:G:',
612 ['help', 'standalone', 'no-distcc', 'unattended', 'type=', 'incredibuild', 'generator='])
613 except getopt.GetoptError, err:
614 print >> sys.stderr, 'Error:', err
615 sys.exit(1)
616
617 for o, a in opts:
618 if o in ('-?', '-h', '--help'):
619 print usage_msg.strip()
620 sys.exit(0)
621 elif o in ('--standalone',):
622 setup.standalone = 'TRUE'
623 elif o in ('--unattended',):
624 setup.unattended = 'TRUE'
625 elif o in ('-t', '--type'):
626 try:
627 setup.build_type = setup.build_types[a.lower()]
628 except KeyError:
629 print >> sys.stderr, 'Error: unknown build type', repr(a)
630 print >> sys.stderr, 'Supported build types:'
631 types = setup.build_types.values()
632 types.sort()
633 for t in types:
634 print ' ', t
635 sys.exit(1)
636 elif o in ('-G', '--generator'):
637 setup.generator = a
638 elif o in ('-N', '--no-distcc'):
639 setup.distcc = False
640 elif o in ('--incredibuild'):
641 setup.incredibuild = True
642 else:
643 print >> sys.stderr, 'INTERNAL ERROR: unhandled option', repr(o)
644 sys.exit(1)
645 if not args:
646 setup.run_cmake()
647 return
648 try:
649 cmd = args.pop(0)
650 if cmd in ('cmake', 'configure'):
651 setup.run_cmake(args)
652 elif cmd == 'build':
653 for d in setup.build_dirs():
654 if not os.path.exists(d):
655 raise CommandError('run "develop.py cmake" first')
656 setup.run_cmake()
657 opts, targets = setup.parse_build_opts(args)
658 setup.run_build(opts, targets)
659 elif cmd == 'clean':
660 if args:
661 raise CommandError('clean takes no arguments')
662 setup.cleanup()
663 else:
664 print >> sys.stderr, 'Error: unknown subcommand', repr(cmd)
665 print >> sys.stderr, "(run 'develop.py --help' for help)"
666 sys.exit(1)
667 except CommandError, err:
668 print >> sys.stderr, 'Error:', err
669 sys.exit(1)
670 except getopt.GetoptError, err:
671 print >> sys.stderr, 'Error with %r subcommand: %s' % (cmd, err)
672 sys.exit(1)
673
674
675if __name__ == '__main__':
676 main(sys.argv[1:])