Home | History | Annotate | Download | only in gcc
      1 #!/usr/bin/env python
      2 
      3 ## @file
      4 #
      5 # Automation of instructions from:
      6 #   http://mingw-w64.svn.sourceforge.net/viewvc/mingw-w64/trunk/mingw-w64-doc/
      7 #     howto-build/mingw-w64-howto-build.txt?revision=216&view=markup
      8 #
      9 # Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved.<BR>
     10 # This program and the accompanying materials
     11 # are licensed and made available under the terms and conditions of the BSD License
     12 # which accompanies this distribution.    The full text of the license may be found at
     13 # http://opensource.org/licenses/bsd-license.php
     14 #
     15 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     16 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     17 #
     18 
     19 
     20 from optparse import OptionParser
     21 import os
     22 import shutil
     23 import subprocess
     24 import sys
     25 import tarfile
     26 import urllib
     27 import urlparse
     28 try:
     29     from hashlib import md5
     30 except Exception:
     31     from md5 import md5
     32 
     33 if sys.version_info < (2, 5):
     34     #
     35     # This script (and edk2 BaseTools) require Python 2.5 or newer
     36     #
     37     print 'Python version 2.5 or later is required.'
     38     sys.exit(-1)
     39 
     40 #
     41 # Version and Copyright
     42 #
     43 VersionNumber = "0.01"
     44 __version__ = "%prog Version " + VersionNumber
     45 __copyright__ = "Copyright (c) 2008 - 2010, Intel Corporation.  All rights reserved."
     46 
     47 class Config:
     48     """class Config
     49 
     50     Stores the configuration options for the rest of the script.
     51 
     52     Handles the command line options, and allows the code within
     53     the script to easily interact with the 'config' requested by
     54     the user.
     55     """
     56 
     57     def __init__(self):
     58         self.base_dir = os.getcwd()
     59         (self.options, self.args) = self.CheckOptions()
     60         self.__init_dirs__()
     61 
     62     def CheckOptions(self):
     63         Parser = \
     64             OptionParser(
     65                 description=__copyright__,
     66                 version=__version__,
     67                 prog="mingw-gcc-build",
     68                 usage="%prog [options] [target]"
     69                 )
     70         Parser.add_option(
     71             "--arch",
     72             action = "store", type = "string",
     73             default = '',
     74             dest = "arch",
     75             help = "Processor architecture to build gcc for."
     76             )
     77         Parser.add_option(
     78             "--src-dir",
     79             action = "store", type = "string", dest = "src_dir",
     80             default = os.path.join(self.base_dir, 'src'),
     81             help = "Directory to download/extract binutils/gcc sources"
     82             )
     83         Parser.add_option(
     84             "--build-dir",
     85             action = "store", type = "string", dest = "build_dir",
     86             default = os.path.join(self.base_dir, 'build'),
     87             help = "Directory to download/extract binutils/gcc sources"
     88             )
     89         Parser.add_option(
     90             "--prefix",
     91             action = "store", type = "string", dest = "prefix",
     92             default = os.path.join(self.base_dir, 'install'),
     93             help = "Prefix to install binutils/gcc into"
     94             )
     95         Parser.add_option(
     96             "--skip-binutils",
     97             action = "store_true", dest = "skip_binutils",
     98             default = False,
     99             help = "Will skip building binutils"
    100             )
    101         Parser.add_option(
    102             "--skip-gcc",
    103             action = "store_true", dest = "skip_gcc",
    104             default = False,
    105             help = "Will skip building GCC"
    106             )
    107         Parser.add_option(
    108             "--symlinks",
    109             action = "store", type = "string", dest = "symlinks",
    110             default = os.path.join(self.base_dir, 'symlinks'),
    111             help = "Directory to create binutils/gcc symbolic links into."
    112             )
    113         Parser.add_option(
    114             "-v", "--verbose",
    115             action="store_true",
    116             type=None, help="Print verbose messages"
    117             )
    118 
    119         (Opt, Args) = Parser.parse_args()
    120 
    121         self.arch = Opt.arch.lower()
    122         allowedArchs = ('ia32', 'x64', 'ipf')
    123         if self.arch not in allowedArchs:
    124             Parser.error(
    125                 'Please use --arch to specify one of: %s' %
    126                     ', '.join(allowedArchs)
    127                 )
    128         self.target_arch = {'ia32': 'i686', 'x64': 'x86_64', 'ipf': 'ia64'}[self.arch]
    129         self.target_sys = {'ia32': 'pc', 'x64': 'pc', 'ipf': 'pc'}[self.arch]
    130         self.target_bin = {'ia32': 'mingw32', 'x64': 'mingw32', 'ipf': 'elf'}[self.arch]
    131         self.target_combo = '-'.join((self.target_arch, self.target_sys, self.target_bin))
    132 
    133         return (Opt, Args)
    134 
    135     def __init_dirs__(self):
    136         self.src_dir = os.path.realpath(os.path.expanduser(self.options.src_dir))
    137         self.build_dir = os.path.realpath(os.path.expanduser(self.options.build_dir))
    138         self.prefix = os.path.realpath(os.path.expanduser(self.options.prefix))
    139         self.symlinks = os.path.realpath(os.path.expanduser(self.options.symlinks))
    140 
    141     def IsConfigOk(self):
    142 
    143         building = []
    144         if not self.options.skip_binutils:
    145             building.append('binutils')
    146         if not self.options.skip_gcc:
    147             building.append('gcc')
    148         if len(building) == 0:
    149             print "Nothing will be built!"
    150             print
    151             print "Please try using --help and then change the configuration."
    152             return False
    153 
    154         print "Current directory:"
    155         print "   ", self.base_dir
    156         print "Sources download/extraction:", self.Relative(self.src_dir)
    157         print "Build directory            :", self.Relative(self.build_dir)
    158         print "Prefix (install) directory :", self.Relative(self.prefix)
    159         print "Create symlinks directory  :", self.Relative(self.symlinks)
    160         print "Building                   :", ', '.join(building)
    161         print
    162         answer = raw_input("Is this configuration ok? (default = no): ")
    163         if (answer.lower() not in ('y', 'yes')):
    164             print
    165             print "Please try using --help and then change the configuration."
    166             return False
    167 
    168         if self.arch.lower() == 'ipf':
    169             print
    170             print 'Please note that the IPF compiler built by this script has'
    171             print 'not yet been validated!'
    172             print
    173             answer = raw_input("Are you sure you want to build it? (default = no): ")
    174             if (answer.lower() not in ('y', 'yes')):
    175                 print
    176                 print "Please try using --help and then change the configuration."
    177                 return False
    178 
    179         print
    180         return True
    181 
    182     def Relative(self, path):
    183         if path.startswith(self.base_dir):
    184             return '.' + path[len(self.base_dir):]
    185         return path
    186 
    187     def MakeDirs(self):
    188         for path in (self.src_dir, self.build_dir,self.prefix, self.symlinks):
    189             if not os.path.exists(path):
    190                 os.makedirs(path)
    191 
    192 class SourceFiles:
    193     """class SourceFiles
    194 
    195     Handles the downloading of source files used by the script.
    196     """
    197 
    198     def __init__(self, config):
    199         self.config = config
    200         self.source_files = self.source_files[config.arch]
    201 
    202         if config.options.skip_binutils:
    203             del self.source_files['binutils']
    204 
    205         if config.options.skip_gcc:
    206             del self.source_files['gcc']
    207             del self.source_files['mingw_hdr']
    208 
    209     source_files_common = {
    210         'binutils': {
    211             'url': 'http://www.kernel.org/pub/linux/devel/binutils/' + \
    212                    'binutils-$version.tar.bz2',
    213             'version': '2.20.51.0.5',
    214             'md5': '6d2de7cdf7a8389e70b124e3d73b4d37',
    215             },
    216         }
    217 
    218     source_files_x64 = {
    219         'gcc': {
    220             'url': 'http://ftpmirror.gnu.org/gcc/' + \
    221                    'gcc-$version/gcc-$version.tar.bz2',
    222             'version': '4.3.0',
    223             'md5': '197ed8468b38db1d3481c3111691d85b',
    224             },
    225         }
    226 
    227     source_files_ia32 = {
    228         'gcc': source_files_x64['gcc'],
    229         }
    230 
    231     source_files_ipf = source_files_x64.copy()
    232     source_files_ipf['gcc']['configure-params'] = (
    233         '--with-gnu-as', '--with-gnu-ld', '--with-newlib',
    234         '--verbose', '--disable-libssp', '--disable-nls',
    235         '--enable-languages=c,c++'
    236         )
    237 
    238     source_files = {
    239         'ia32': [source_files_common, source_files_ia32],
    240         'x64': [source_files_common, source_files_x64],
    241         'ipf': [source_files_common, source_files_ipf],
    242         }
    243 
    244     for arch in source_files:
    245         mergedSourceFiles = {}
    246         for source_files_dict in source_files[arch]:
    247             mergedSourceFiles.update(source_files_dict)
    248         for downloadItem in mergedSourceFiles:
    249             fdata = mergedSourceFiles[downloadItem]
    250             fdata['filename'] = fdata['url'].split('/')[-1]
    251             if 'extract-dir' not in fdata:
    252                 for ext in ('.tar.gz', '.tar.bz2', '.zip'):
    253                     if fdata['filename'].endswith(ext):
    254                         fdata['extract-dir'] = fdata['filename'][:-len(ext)]
    255                         break
    256             replaceables = ('extract-dir', 'filename', 'url')
    257             for replaceItem in fdata:
    258                 if replaceItem in replaceables: continue
    259                 if type(fdata[replaceItem]) != str: continue
    260                 for replaceable in replaceables:
    261                     if type(fdata[replaceable]) != str: continue
    262                     if replaceable in fdata:
    263                         fdata[replaceable] = \
    264                             fdata[replaceable].replace(
    265                                 '$' + replaceItem,
    266                                 fdata[replaceItem]
    267                                 )
    268         source_files[arch] = mergedSourceFiles
    269     #print 'source_files:', source_files
    270 
    271     def GetAll(self):
    272 
    273         def progress(received, blockSize, fileSize):
    274             if fileSize < 0: return
    275             wDots = (100 * received * blockSize) / fileSize / 10
    276             if wDots > self.dots:
    277                 for i in range(wDots - self.dots):
    278                     print '.',
    279                     sys.stdout.flush()
    280                     self.dots += 1
    281 
    282         maxRetries = 1
    283         for (fname, fdata) in self.source_files.items():
    284             for retries in range(maxRetries):
    285                 try:
    286                     self.dots = 0
    287                     local_file = os.path.join(self.config.src_dir, fdata['filename'])
    288                     url = fdata['url']
    289                     print 'Downloading %s:' % fname, url
    290                     if retries > 0:
    291                         print '(retry)',
    292                     sys.stdout.flush()
    293 
    294                     completed = False
    295                     if os.path.exists(local_file):
    296                         md5_pass = self.checkHash(fdata)
    297                         if md5_pass:
    298                             print '[md5 match]',
    299                         else:
    300                             print '[md5 mismatch]',
    301                         sys.stdout.flush()
    302                         completed = md5_pass
    303 
    304                     if not completed:
    305                         urllib.urlretrieve(url, local_file, progress)
    306 
    307                     #
    308                     # BUGBUG: Suggest proxy to user if download fails.
    309                     #
    310                     # export http_proxy=http://proxyservername.mycompany.com:911
    311                     # export ftp_proxy=http://proxyservername.mycompany.com:911
    312 
    313                     if not completed and os.path.exists(local_file):
    314                         md5_pass = self.checkHash(fdata)
    315                         if md5_pass:
    316                             print '[md5 match]',
    317                         else:
    318                             print '[md5 mismatch]',
    319                         sys.stdout.flush()
    320                         completed = md5_pass
    321 
    322                     if completed:
    323                         print '[done]'
    324                         break
    325                     else:
    326                         print '[failed]'
    327                         print '  Tried to retrieve', url
    328                         print '  to', local_file
    329                         print 'Possible fixes:'
    330                         print '* If you are behind a web-proxy, try setting the',
    331                         print 'http_proxy environment variable'
    332                         print '* You can try to download this file separately',
    333                         print 'and rerun this script'
    334                         raise Exception()
    335                 
    336                 except KeyboardInterrupt:
    337                     print '[KeyboardInterrupt]'
    338                     return False
    339 
    340                 except Exception, e:
    341                     print e
    342 
    343             if not completed: return False
    344 
    345         return True
    346 
    347     def checkHash(self, fdata):
    348         local_file = os.path.join(self.config.src_dir, fdata['filename'])
    349         expect_md5 = fdata['md5']
    350         data = open(local_file).read()
    351         md5sum = md5()
    352         md5sum.update(data)
    353         return md5sum.hexdigest().lower() == expect_md5.lower()
    354 
    355     def GetModules(self):
    356         return self.source_files.keys()
    357 
    358     def GetFilenameOf(self, module):
    359         return self.source_files[module]['filename']
    360 
    361     def GetMd5Of(self, module):
    362         return self.source_files[module]['md5']
    363 
    364     def GetExtractDirOf(self, module):
    365         return self.source_files[module]['extract-dir']
    366 
    367     def GetAdditionalParameters(self, module, step):
    368         key = step + '-params'
    369         if key in self.source_files[module]:
    370             return self.source_files[module][key]
    371         else:
    372             return tuple()
    373 
    374 class Extracter:
    375     """class Extracter
    376 
    377     Handles the extraction of the source files from their downloaded
    378     archive files.
    379     """
    380 
    381     def __init__(self, source_files, config):
    382         self.source_files = source_files
    383         self.config = config
    384 
    385     def Extract(self, module):
    386         src = self.config.src_dir
    387         extractDst = os.path.join(src, self.config.arch)
    388         local_file = os.path.join(src, self.source_files.GetFilenameOf(module))
    389         moduleMd5 = self.source_files.GetMd5Of(module)
    390         extracted = os.path.join(extractDst, os.path.split(local_file)[1] + '.extracted')
    391         if not os.path.exists(extractDst):
    392             os.mkdir(extractDst)
    393 
    394         extractedMd5 = None
    395         if os.path.exists(extracted):
    396             extractedMd5 = open(extracted).read()
    397 
    398         if extractedMd5 != moduleMd5:
    399             print 'Extracting %s:' % self.config.Relative(local_file)
    400             tar = tarfile.open(local_file)
    401             tar.extractall(extractDst)
    402             open(extracted, 'w').write(moduleMd5)
    403         else:
    404             pass
    405             #print 'Previously extracted', self.config.Relative(local_file)
    406 
    407     def ExtractAll(self):
    408         for module in self.source_files.GetModules():
    409             self.Extract(module)
    410 
    411 class Builder:
    412     """class Builder
    413 
    414     Builds and installs the GCC tool suite.
    415     """
    416 
    417     def __init__(self, source_files, config):
    418         self.source_files = source_files
    419         self.config = config
    420 
    421     def Build(self):
    422         if not self.config.options.skip_binutils:
    423             self.BuildModule('binutils')
    424         if not self.config.options.skip_gcc:
    425             self.BuildModule('gcc')
    426             self.MakeSymLinks()
    427 
    428     def IsBuildStepComplete(self, step):
    429         return \
    430             os.path.exists(
    431                 os.path.join(
    432                     self.config.build_dir, self.config.arch, step + '.completed'
    433                     )
    434                 )
    435 
    436     def MarkBuildStepComplete(self, step):
    437         open(
    438             os.path.join(
    439                 self.config.build_dir, self.config.arch, step + '.completed'
    440                 ),
    441             "w"
    442             ).close()
    443 
    444 
    445     def BuildModule(self, module):
    446         base_dir = os.getcwd()
    447         build_dir = os.path.join(self.config.build_dir, self.config.arch, module)
    448         module_dir = self.source_files.GetExtractDirOf(module)
    449         module_dir = os.path.realpath(os.path.join('src', self.config.arch, module_dir))
    450         configure = os.path.join(module_dir, 'configure')
    451         prefix = self.config.prefix
    452         if not os.path.exists(build_dir):
    453             os.makedirs(build_dir)
    454         os.chdir(build_dir)
    455 
    456         cmd = (
    457             configure,
    458             '--target=%s' % self.config.target_combo,
    459             '--prefix=' + prefix,
    460             '--with-sysroot=' + prefix,
    461             '--disable-werror',
    462             )
    463         if os.path.exists('/opt/local/include/gmp.h'):
    464             cmd += ('--with-gmp=/opt/local',)
    465         if module == 'gcc': cmd += ('--oldincludedir=/opt/local/include',)
    466         cmd += self.source_files.GetAdditionalParameters(module, 'configure')
    467         self.RunCommand(cmd, module, 'config', skipable=True)
    468 
    469         cmd = ('make',)
    470         if module == 'gcc':
    471             cmd += ('all-gcc',)
    472         self.RunCommand(cmd, module, 'build')
    473 
    474         cmd = ('make',)
    475         if module == 'gcc':
    476             cmd += ('install-gcc',)
    477         else:
    478             cmd += ('install',)
    479         self.RunCommand(cmd, module, 'install')
    480 
    481         os.chdir(base_dir)
    482 
    483         print '%s module is now built and installed' % module
    484 
    485     def RunCommand(self, cmd, module, stage, skipable=False):
    486         if skipable:
    487             if self.IsBuildStepComplete('%s.%s' % (module, stage)):
    488                 return
    489 
    490         popen = lambda cmd: \
    491             subprocess.Popen(
    492                 cmd,
    493                 stdin=subprocess.PIPE,
    494                 stdout=subprocess.PIPE,
    495                 stderr=subprocess.STDOUT
    496                 )
    497 
    498         print '%s [%s] ...' % (module, stage),
    499         sys.stdout.flush()
    500         p = popen(cmd)
    501         output = p.stdout.read()
    502         p.wait()
    503         if p.returncode != 0:
    504             print '[failed!]'
    505             logFile = os.path.join(self.config.build_dir, 'log.txt')
    506             f = open(logFile, "w")
    507             f.write(output)
    508             f.close()
    509             raise Exception, 'Failed to %s %s\n' % (stage, module) + \
    510                 'See output log at %s' % self.config.Relative(logFile)
    511         else:
    512             print '[done]'
    513 
    514         if skipable:
    515             self.MarkBuildStepComplete('%s.%s' % (module, stage))
    516 
    517     def MakeSymLinks(self):
    518         links_dir = os.path.join(self.config.symlinks, self.config.arch)
    519         if not os.path.exists(links_dir):
    520             os.makedirs(links_dir)
    521         startPrinted = False
    522         for link in ('ar', 'ld', 'gcc'):
    523             src = os.path.join(
    524                 self.config.prefix, 'bin', self.config.target_combo + '-' + link
    525                 )
    526             linkdst = os.path.join(links_dir, link)
    527             if not os.path.lexists(linkdst):
    528                 if not startPrinted:
    529                     print 'Making symlinks in %s:' % self.config.Relative(links_dir),
    530                     startPrinted = True
    531                 print link,
    532                 os.symlink(src, linkdst)
    533 
    534         if startPrinted:
    535             print '[done]'
    536 
    537 class App:
    538     """class App
    539 
    540     The main body of the application.
    541     """
    542 
    543     def __init__(self):
    544         config = Config()
    545 
    546         if not config.IsConfigOk():
    547             return
    548 
    549         config.MakeDirs()
    550 
    551         sources = SourceFiles(config)
    552         result = sources.GetAll()
    553         if result:
    554             print 'All files have been downloaded & verified'
    555         else:
    556             print 'An error occured while downloading a file'
    557             return
    558 
    559         Extracter(sources, config).ExtractAll()
    560 
    561         Builder(sources, config).Build()
    562 
    563 App()
    564 
    565