Merged revisions 1784-1824 via svnmerge from
[scons.git] / bootstrap.py
1 #
2 # __COPYRIGHT__
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #
23
24 import os
25 import os.path
26 import string
27 import sys
28
29 __doc__ = """bootstrap.py
30
31 This script supports "bootstrap" execution of the current SCons in
32 this local source tree by copying of all necessary Python scripts and
33 modules from underneath the src/ subdirectory into a subdirectory (named
34 "bootstrap/" by default), and then executing the copied SCons with the
35 supplied command-line arguments.
36
37 There are a handful of options that are specific to this bootstrap.py
38 script and which are *not* passed on to the underlying SCons script.
39 All of these begin with the string "bootstrap_":
40
41     --bootstrap_dir=DIR
42
43         Sets the name of the directory into which the SCons files will
44         be copied.  The default is "bootstrap" in the local subdirectory.
45
46     --bootstrap_force
47
48         Forces a copy of all necessary files.  By default, the
49         bootstrap.py script only updates the bootstrap copy if the
50         content of the source copy is different.
51
52     --bootstrap_src=DIR
53
54         Searches for the SCons files relative to the specified DIR,
55         then relative to the directory in which this bootstrap.py
56         script is found.
57
58     --bootstrap_update
59
60         Only updates the bootstrap subdirectory, and then exits.
61
62 In addition to the above options, the bootstrap.py script understands
63 the following SCons options:
64
65     -C, --directory
66
67         Changes to the specified directory before invoking SCons.
68         Because we change directory right away to the specified directory,
69         the SCons script itself doesn't need to, so this option gets
70         "eaten" by the bootstrap.py script.
71
72     -Y, --repository
73
74         These options are used under Aegis to specify a search path
75         for the source files that may not have been copied in to the
76         Aegis change.
77
78 This is essentially a minimal build of SCons to bootstrap ourselves into
79 executing it for the full build of all the packages, as specified in our
80 local SConstruct file.
81 """
82
83 bootstrap_dir = 'bootstrap'
84 pass_through_args = []
85 update_only = None
86
87 requires_an_argument = 'bootstrap.py:  %s requires an argument\n'
88
89 def must_copy(dst, src):
90     if not os.path.exists(dst):
91         return 1
92     return open(dst, 'rb').read() != open(src, 'rb').read()
93
94 search = [os.path.dirname(sys.argv[0])]
95 if search[0] == '': search[0] = '.'
96
97 # Note:  We don't use the getopt module to process the command-line
98 # arguments because we'd have to teach it about all of the SCons options.
99
100 command_line_args = sys.argv[1:]
101
102 while command_line_args:
103     arg = command_line_args.pop(0)
104
105     if arg == '--bootstrap_dir':
106         try:
107             bootstrap_dir = command_line_args.pop(0)
108         except IndexError:
109             sys.stderr.write(requires_an_argument % arg)
110             sys.exit(1)
111     elif arg[:16] == '--bootstrap_dir=':
112         bootstrap_dir = arg[16:]
113
114     elif arg == '--bootstrap_force':
115         def must_copy(dst, src):
116             return 1
117
118     elif arg == '--bootstrap_src':
119         try:
120             search.insert(0, command_line_args.pop(0))
121         except IndexError:
122             sys.stderr.write(requires_an_argument % arg)
123             sys.exit(1)
124     elif arg[:16] == '--bootstrap_src=':
125         search.insert(0, arg[16:])
126
127     elif arg == '--bootstrap_update':
128         update_only = 1
129
130     elif arg in ('-C', '--directory'):
131         try:
132             dir = command_line_args.pop(0)
133         except IndexError:
134             sys.stderr.write(requires_an_argument % arg)
135             sys.exit(1)
136         else:
137             os.chdir(dir)
138     elif arg[:2] == '-C':
139         os.chdir(arg[2:])
140     elif arg[:12] == '--directory=':
141         os.chdir(arg[12:])
142
143     elif arg in ('-Y', '--repository'):
144         try:
145             dir = command_line_args.pop(0)
146         except IndexError:
147             sys.stderr.write(requires_an_argument % arg)
148             sys.exit(1)
149         else:
150             search.append(dir)
151         pass_through_args.extend([arg, dir])
152     elif arg[:2] == '-Y':
153         search.append(arg[2:])
154         pass_through_args.append(arg)
155     elif arg[:13] == '--repository=':
156         search.append(arg[13:])
157         pass_through_args.append(arg)
158
159     else:
160         pass_through_args.append(arg)
161
162 def find(file, search=search):
163     for dir in search:
164         f = os.path.join(dir, file)
165         if os.path.exists(f):
166             return os.path.normpath(f)
167     sys.stderr.write("could not find `%s' in search path:\n" % file)
168     sys.stderr.write("\t" + string.join(search, "\n\t") + "\n")
169     sys.exit(2)
170
171 scons_py = os.path.join('src', 'script', 'scons.py')
172 src_engine = os.path.join('src', 'engine')
173 MANIFEST_in = find(os.path.join(src_engine, 'MANIFEST.in'))
174
175 files = [ scons_py ] + map(lambda x: os.path.join(src_engine, x[:-1]),
176                            open(MANIFEST_in).readlines())
177
178 for file in files:
179     src = find(file)
180     dst = os.path.join(bootstrap_dir, file)
181     if must_copy(dst, src):
182         dir = os.path.split(dst)[0]
183         if not os.path.isdir(dir):
184             os.makedirs(dir)
185         try: os.unlink(dst)
186         except: pass
187         open(dst, 'wb').write( open(src, 'rb').read() )
188
189 if update_only:
190     sys.exit(0)
191
192 args = [
193             os.path.split(sys.executable)[1],
194             os.path.join(bootstrap_dir, scons_py)
195        ] + pass_through_args
196
197 sys.stdout.write(string.join(args, " ") + '\n')
198 sys.stdout.flush()
199
200 os.environ['SCONS_LIB_DIR'] = os.path.join(bootstrap_dir, src_engine)
201
202 os.execve(sys.executable, args, os.environ)