BaseTools/gcc/x86_64-mingw-gcc-build.py:
[people/mcb30/basetools.git] / gcc / x86_64-mingw-gcc-build.py
1 #!/usr/bin/env python
2
3 #
4 # Automation of instructions from:
5 #   http://mingw-w64.svn.sourceforge.net/viewvc/mingw-w64/trunk/mingw-w64-doc/
6 #     howto-build/mingw-w64-howto-build.txt?revision=216&view=markup
7 #
8
9 from optparse import OptionParser
10 import os
11 import shutil
12 import subprocess
13 import sys
14 import tarfile
15 import urllib
16 import urlparse
17 try:
18     from hashlib import md5
19 except Exception:
20     from md5 import md5
21
22 #
23 # Version and Copyright
24 #
25 VersionNumber = "0.01"
26 __version__ = "%prog Version " + VersionNumber
27 __copyright__ = "Copyright (c) 2008, Intel Corporation.  All rights reserved."
28
29 class Config:
30     """class Config
31
32     Stores the configuration options for the rest of the script.
33
34     Handles the command line options, and allows the code within
35     the script to easily interact with the 'config' requested by
36     the user.
37     """
38
39     def __init__(self):
40         self.base_dir = os.getcwd()
41         (self.options, self.args) = self.CheckOptions()
42         self.__init_dirs__()
43
44     def CheckOptions(self):
45         Parser = \
46             OptionParser(
47                 description=__copyright__,
48                 version=__version__,
49                 prog="x86_64-mingw-gcc-build",
50                 usage="%prog [options] [target]"
51                 )
52         Parser.add_option(
53             "--arch",
54             action = "append", type = "choice", choices = ['X64'],
55             default = ['X64'],
56             dest = "arch",
57             help = "Processor architecture to build gcc for."
58             )
59         Parser.add_option(
60             "--src-dir",
61             action = "store", type = "string", dest = "src_dir",
62             default = os.path.join(self.base_dir, 'src'),
63             help = "Directory to download/extract binutils/gcc sources"
64             )
65         Parser.add_option(
66             "--build-dir",
67             action = "store", type = "string", dest = "build_dir",
68             default = os.path.join(self.base_dir, 'build'),
69             help = "Directory to download/extract binutils/gcc sources"
70             )
71         Parser.add_option(
72             "--prefix",
73             action = "store", type = "string", dest = "prefix",
74             default = os.path.join(self.base_dir, 'install'),
75             help = "Prefix to install binutils/gcc into"
76             )
77         Parser.add_option(
78             "--symlinks",
79             action = "store", type = "string", dest = "symlinks",
80             default = os.path.join(self.base_dir, 'symlinks'),
81             help = "Directory to create binutils/gcc symbolic links into."
82             )
83         Parser.add_option(
84             "-v", "--verbose",
85             action="store_true",
86             type=None, help="Print verbose messages"
87             )
88     
89         (Opt, Args) = Parser.parse_args()
90         return (Opt, Args)
91
92     def __init_dirs__(self):
93         self.src_dir = os.path.realpath(self.options.src_dir)
94         self.build_dir = os.path.realpath(self.options.build_dir)
95         self.prefix = os.path.realpath(self.options.prefix)
96         self.symlinks = os.path.realpath(self.options.symlinks)
97
98     def IsConfigOk(self):
99                 
100         print "Current directory:"
101         print "   ", self.base_dir
102         print "Sources download/extraction:", self.Relative(self.src_dir)
103         print "Build directory            :", self.Relative(self.build_dir)
104         print "Prefix (install) directory :", self.Relative(self.prefix)
105         print "Create symlinks directory  :", self.Relative(self.symlinks)
106         print
107         answer = raw_input("Is this configuration ok? (default = no): ")
108         if (answer.lower() not in ('y', 'yes')):
109             print
110             print "Please try using --help and then change the configuration."
111             return False
112         
113         print
114         return True
115
116     def Relative(self, path):
117         if path.startswith(self.base_dir):
118             return '.' + path[len(self.base_dir):]
119         return path
120
121     def MakeDirs(self):
122         for path in (self.src_dir, self.build_dir,self.prefix, self.symlinks):
123             if not os.path.exists(path):
124                 os.makedirs(path)
125
126 class SourceFiles:
127     """class SourceFiles
128
129     Handles the downloading of source files used by the script.
130     """
131
132     def __init__(self, config):
133         self.config = config
134
135     source_files = {
136         'gcc': {
137             'url': 'ftp://gd.tuwien.ac.at/gnu/gcc/snapshots/' + \
138                    '$version/gcc-$version.tar.bz2',
139             'version': '4.3-20071207',
140             'md5': 'b340fa520908cd195f3501e216d0036f'
141             },
142         'binutils': {
143             'url': 'http://www.kernel.org/pub/linux/devel/binutils/' + \
144                    'binutils-$version.tar.bz2',
145             'version': '2.18.50.0.3',
146             'md5': '4e0692b6ff63d51b7ae3529fabe290ce'
147             },
148         'mingw_hdr': {
149             'url': 'http://superb-west.dl.sourceforge.net/sourceforge/' + \
150                    'mingw/mingw-w64-headers-$version.tar.bz2',
151             'version': '20070802',
152             'md5': 'b79e28f7685682cf9c9bf360c352ae7c'
153             },
154         }
155     for (fname, fdata) in source_files.items():
156         fdata['url'] = fdata['url'].replace('$version', fdata['version'])
157         fdata['filename'] = fdata['url'].split('/')[-1]
158
159     def GetAll(self):
160
161         def progress(received, blockSize, fileSize):
162             if fileSize < 0: return
163             wDots = (100 * received * blockSize) / fileSize / 10
164             if wDots > self.dots:
165                 for i in range(wDots - self.dots):
166                     print '.',
167                     sys.stdout.flush()
168                     self.dots += 1
169
170         for (fname, fdata) in self.source_files.items():
171             self.dots = 0
172             local_file = os.path.join(self.config.src_dir, fdata['filename'])
173             url = fdata['url']
174             print 'Downloading %s:' % fname,
175             sys.stdout.flush()
176
177             completed = False
178             if os.path.exists(local_file):
179                 md5_pass = self.checkHash(fdata)
180                 if md5_pass:
181                     print '[md5 match]',
182                 else:
183                     print '[md5 mismatch]',
184                 sys.stdout.flush()
185                 completed = md5_pass
186
187             if not completed:
188                 urllib.urlretrieve(url, local_file, progress)
189
190             #
191             # BUGBUG: Suggest proxy to user if download fails.
192             #
193             # export http_proxy=http://proxyservername.mycompany.com:911
194             # export ftp_proxy=http://proxyservername.mycompany.com:911
195
196             if not completed and os.path.exists(local_file):
197                 md5_pass = self.checkHash(fdata)
198                 if md5_pass:
199                     print '[md5 match]',
200                 else:
201                     print '[md5 mismatch]',
202                 sys.stdout.flush()
203                 completed = md5_pass
204
205             if completed:
206                 print '[done]'
207             else:
208                 print '[failed]'
209                 return False
210
211         return True
212
213     def checkHash(self, fdata):
214         local_file = os.path.join(self.config.src_dir, fdata['filename'])
215         expect_md5 = fdata['md5']
216         data = open(local_file).read()
217         md5sum = md5()
218         md5sum.update(data)
219         return md5sum.hexdigest().lower() == expect_md5.lower()
220
221     def GetModules(self):
222         return self.source_files.keys()
223
224     def GetFilenameOf(self, module):
225         return self.source_files[module]['filename']
226
227     def GetMd5Of(self, module):
228         return self.source_files[module]['md5']
229
230 class Extracter:
231     """class Extracter
232
233     Handles the extraction of the source files from their downloaded
234     archive files.
235     """
236
237     def __init__(self, source_files, config):
238         self.source_files = source_files
239         self.config = config
240
241     def Extract(self, module):
242         src = self.config.src_dir
243         local_file = os.path.join(src, self.source_files.GetFilenameOf(module))
244         moduleMd5 = self.source_files.GetMd5Of(module)
245         extracted = os.path.join(src, local_file + '.extracted')
246         if not os.path.exists(src):
247             os.mkdir(src)
248
249         extractedMd5 = None
250         if os.path.exists(extracted):
251             extractedMd5 = open(extracted).read()
252
253         if extractedMd5 != moduleMd5:
254             print 'Extracting %s:' % self.config.Relative(local_file)
255             tar = tarfile.open(local_file)
256             tar.extractall(src)
257             open(extracted, 'w').write(moduleMd5)
258         else:
259             pass
260             #print 'Previously extracted', self.config.Relative(local_file)
261
262     def ExtractAll(self):
263         for module in self.source_files.GetModules():
264             self.Extract(module)
265
266 class Builder:
267     """class Builder
268
269     Builds and installs the GCC tool suite.
270     """
271
272     def __init__(self, source_files, config):
273         self.source_files = source_files
274         self.config = config
275
276     def Build(self):
277         self.BuildModule('binutils')
278         self.CopyIncludeDirectory()
279         self.BuildModule('gcc')
280         self.MakeSymLinks()
281
282     def IsBuildStepComplete(self, step):
283         return \
284             os.path.exists(
285                 os.path.join(
286                     self.config.build_dir, step + '.completed'
287                     )
288                 )
289
290     def MarkBuildStepComplete(self, step):
291         open(
292             os.path.join(
293                 self.config.build_dir, step + '.completed'
294                 ),
295             "w"
296             ).close()
297
298     def CopyIncludeDirectory(self):
299         src = os.path.join(self.config.src_dir, 'include')
300         dst_parent = os.path.join(self.config.prefix, 'x86_64-pc-mingw32')
301         dst = os.path.join(dst_parent, 'include')
302         linkdst = os.path.join(self.config.prefix, 'mingw')
303         if not os.path.exists(dst):
304             if not os.path.exists(dst_parent):
305                 os.makedirs(dst_parent)
306             print 'Copying headers to', self.config.Relative(dst)
307             shutil.copytree(src, dst, True)
308         if not os.path.lexists(linkdst):
309             print 'Making symlink at', self.config.Relative(linkdst)
310             os.symlink('x86_64-pc-mingw32', linkdst)
311
312     def BuildModule(self, module):
313         base_dir = os.getcwd()
314         build_dir = os.path.join(self.config.build_dir, 'x64', module)
315         module_dir = self.source_files.GetFilenameOf(module)
316         module_dir = module_dir[:-len('.tar.bz2')]
317         module_dir = os.path.realpath(os.path.join('src', module_dir))
318         configure = os.path.join(module_dir, 'configure')
319         prefix = self.config.prefix
320         if not os.path.exists(build_dir):
321             os.makedirs(build_dir)
322         os.chdir(build_dir)
323
324         cmd = (
325             configure,
326             '--target=x86_64-pc-mingw32',
327             '--prefix=' + prefix,
328             '--with-sysroot=' + prefix,
329             )
330         if os.path.exists('/opt/local/include/gmp.h'):
331             cmd += ('--with-gmp=/opt/local',)
332         self.RunCommand(cmd, module, 'config', skipable=True)
333
334         cmd = ('make',)
335         if module == 'gcc':
336             cmd += ('all-gcc',)
337         self.RunCommand(cmd, module, 'build')
338
339         cmd = ('make',)
340         if module == 'gcc':
341             cmd += ('install-gcc',)
342         else:
343             cmd += ('install',)
344         self.RunCommand(cmd, module, 'install')
345
346         os.chdir(base_dir)
347
348         print '%s module is now built and installed' % module
349
350     def RunCommand(self, cmd, module, stage, skipable=False):
351         if skipable:
352             if self.IsBuildStepComplete('%s.%s' % (module, stage)):
353                 return
354
355         popen = lambda cmd: \
356             subprocess.Popen(
357                 cmd,
358                 stdin=subprocess.PIPE,
359                 stdout=subprocess.PIPE,
360                 stderr=subprocess.STDOUT
361                 )
362
363         print '%s [%s] ...' % (module, stage),
364         sys.stdout.flush()
365         p = popen(cmd)
366         output = p.stdout.read()
367         p.wait()
368         if p.returncode != 0:
369             print '[failed!]'
370             logFile = os.path.join(self.config.build_dir, 'log.txt')
371             f = open(logFile, "w")
372             f.write(output)
373             f.close()
374             raise Exception, 'Failed to %s %s\n' % (stage, module) + \
375                 'See output log at %s' % self.config.Relative(logFile)
376         else:
377             print '[done]'
378
379         if skipable:
380             self.MarkBuildStepComplete('%s.%s' % (module, stage))
381
382     def MakeSymLinks(self):
383         links_dir = os.path.join(self.config.symlinks, 'x64')
384         if not os.path.exists(links_dir):
385             os.makedirs(links_dir)
386         startPrinted = False
387         for link in ('ar', 'ld', 'gcc'):
388             src = os.path.join(
389                 self.config.prefix, 'bin', 'x86_64-pc-mingw32-' + link
390                 )
391             linkdst = os.path.join(links_dir, link)
392             if not os.path.lexists(linkdst):
393                 if not startPrinted:
394                     print 'Making symlinks in %s:' % self.config.Relative(links_dir),
395                     startPrinted = True
396                 print link,
397                 os.symlink(src, linkdst)
398
399         if startPrinted:
400             print '[done]'
401
402 class App:
403     """class App
404
405     The main body of the application.
406     """
407
408     def __init__(self):
409         config = Config()
410
411         if not config.IsConfigOk():
412             return
413
414         config.MakeDirs()
415
416         sources = SourceFiles(config)
417         result = sources.GetAll()
418         if result:
419             print 'All files have been downloaded & verified'
420         else:
421             print 'An error occured while downloading a file'
422             return
423
424         Extracter(sources, config).ExtractAll()
425
426         Builder(sources, config).Build()
427
428 App()
429