Eliminate / replace remaining cPickle references in test scripts.
[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 sys
27
28 __doc__ = """bootstrap.py
29
30 This script supports "bootstrap" execution of the current SCons in
31 this local source tree by copying of all necessary Python scripts and
32 modules from underneath the src/ subdirectory into a subdirectory (named
33 "bootstrap/" by default), and then executing the copied SCons with the
34 supplied command-line arguments.
35
36 There are a handful of options that are specific to this bootstrap.py
37 script and which are *not* passed on to the underlying SCons script.
38 All of these begin with the string "bootstrap_":
39
40     --bootstrap_dir=DIR
41
42         Sets the name of the directory into which the SCons files will
43         be copied.  The default is "bootstrap" in the local subdirectory.
44
45     --bootstrap_force
46
47         Forces a copy of all necessary files.  By default, the
48         bootstrap.py script only updates the bootstrap copy if the
49         content of the source copy is different.
50
51     --bootstrap_src=DIR
52
53         Searches for the SCons files relative to the specified DIR,
54         then relative to the directory in which this bootstrap.py
55         script is found.
56
57     --bootstrap_update
58
59         Only updates the bootstrap subdirectory, and then exits.
60
61 In addition to the above options, the bootstrap.py script understands
62 the following SCons options:
63
64     -C, --directory
65
66         Changes to the specified directory before invoking SCons.
67         Because we change directory right away to the specified directory,
68         the SCons script itself doesn't need to, so this option gets
69         "eaten" by the bootstrap.py script.
70
71     -Y, --repository
72
73         These options are used under Aegis to specify a search path
74         for the source files that may not have been copied in to the
75         Aegis change.
76
77 This is essentially a minimal build of SCons to bootstrap ourselves into
78 executing it for the full build of all the packages, as specified in our
79 local SConstruct file.
80 """
81
82 try:
83     script_dir = os.path.abspath(os.path.dirname(__file__))
84 except NameError:
85     # Pre-2.3 versions of Python don't have __file__.
86     script_dir = os.path.abspath(os.path.dirname(sys.argv[0]))
87
88 bootstrap_dir = os.path.join(script_dir, 'bootstrap')
89
90 pass_through_args = []
91 update_only = None
92
93 requires_an_argument = 'bootstrap.py:  %s requires an argument\n'
94
95 def must_copy(dst, src):
96     if not os.path.exists(dst):
97         return 1
98     return open(dst, 'rb').read() != open(src, 'rb').read()
99
100 search = [script_dir]
101
102 # Note:  We don't use the getopt module to process the command-line
103 # arguments because we'd have to teach it about all of the SCons options.
104
105 command_line_args = sys.argv[1:]
106
107 while command_line_args:
108     arg = command_line_args.pop(0)
109
110     if arg == '--bootstrap_dir':
111         try:
112             bootstrap_dir = command_line_args.pop(0)
113         except IndexError:
114             sys.stderr.write(requires_an_argument % arg)
115             sys.exit(1)
116     elif arg[:16] == '--bootstrap_dir=':
117         bootstrap_dir = arg[16:]
118
119     elif arg == '--bootstrap_force':
120         def must_copy(dst, src):
121             return 1
122
123     elif arg == '--bootstrap_src':
124         try:
125             search.insert(0, command_line_args.pop(0))
126         except IndexError:
127             sys.stderr.write(requires_an_argument % arg)
128             sys.exit(1)
129     elif arg[:16] == '--bootstrap_src=':
130         search.insert(0, arg[16:])
131
132     elif arg == '--bootstrap_update':
133         update_only = 1
134
135     elif arg in ('-C', '--directory'):
136         try:
137             dir = command_line_args.pop(0)
138         except IndexError:
139             sys.stderr.write(requires_an_argument % arg)
140             sys.exit(1)
141         else:
142             os.chdir(dir)
143     elif arg[:2] == '-C':
144         os.chdir(arg[2:])
145     elif arg[:12] == '--directory=':
146         os.chdir(arg[12:])
147
148     elif arg in ('-Y', '--repository'):
149         try:
150             dir = command_line_args.pop(0)
151         except IndexError:
152             sys.stderr.write(requires_an_argument % arg)
153             sys.exit(1)
154         else:
155             search.append(dir)
156         pass_through_args.extend([arg, dir])
157     elif arg[:2] == '-Y':
158         search.append(arg[2:])
159         pass_through_args.append(arg)
160     elif arg[:13] == '--repository=':
161         search.append(arg[13:])
162         pass_through_args.append(arg)
163
164     else:
165         pass_through_args.append(arg)
166
167 def find(file, search=search):
168     for dir in search:
169         f = os.path.join(dir, file)
170         if os.path.exists(f):
171             return os.path.normpath(f)
172     sys.stderr.write("could not find `%s' in search path:\n" % file)
173     sys.stderr.write("\t" + "\n\t".join(search) + "\n")
174     sys.exit(2)
175
176 scons_py = os.path.join('src', 'script', 'scons.py')
177 src_engine = os.path.join('src', 'engine')
178 MANIFEST_in = find(os.path.join(src_engine, 'MANIFEST.in'))
179
180 files = [ scons_py ] + [os.path.join(src_engine, x[:-1])
181                         for x in open(MANIFEST_in).readlines()]
182
183 for file in files:
184     src = find(file)
185     dst = os.path.join(bootstrap_dir, file)
186     if must_copy(dst, src):
187         dir = os.path.split(dst)[0]
188         if not os.path.isdir(dir):
189             os.makedirs(dir)
190         try: os.unlink(dst)
191         except: pass
192         open(dst, 'wb').write( open(src, 'rb').read() )
193
194 if update_only:
195     sys.exit(0)
196
197 args = [
198             sys.executable,
199             os.path.join(bootstrap_dir, scons_py)
200        ] + pass_through_args
201
202 sys.stdout.write(" ".join(args) + '\n')
203 sys.stdout.flush()
204
205 os.environ['SCONS_LIB_DIR'] = os.path.join(bootstrap_dir, src_engine)
206
207 os.execve(sys.executable, args, os.environ)
208
209 # Local Variables:
210 # tab-width:4
211 # indent-tabs-mode:nil
212 # End:
213 # vim: set expandtab tabstop=4 shiftwidth=4: