565 lines
19 KiB
Python
Executable File
565 lines
19 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
## @file
|
|
#
|
|
# Automation of instructions from:
|
|
# http://mingw-w64.svn.sourceforge.net/viewvc/mingw-w64/trunk/mingw-w64-doc/
|
|
# howto-build/mingw-w64-howto-build.txt?revision=216&view=markup
|
|
#
|
|
# Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved.<BR>
|
|
# This program and the accompanying materials
|
|
# are licensed and made available under the terms and conditions of the BSD License
|
|
# which accompanies this distribution. The full text of the license may be found at
|
|
# http://opensource.org/licenses/bsd-license.php
|
|
#
|
|
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
#
|
|
|
|
|
|
from optparse import OptionParser
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tarfile
|
|
import urllib
|
|
import urlparse
|
|
try:
|
|
from hashlib import md5
|
|
except Exception:
|
|
from md5 import md5
|
|
|
|
if sys.version_info < (2, 5):
|
|
#
|
|
# This script (and edk2 BaseTools) require Python 2.5 or newer
|
|
#
|
|
print 'Python version 2.5 or later is required.'
|
|
sys.exit(-1)
|
|
|
|
#
|
|
# Version and Copyright
|
|
#
|
|
VersionNumber = "0.01"
|
|
__version__ = "%prog Version " + VersionNumber
|
|
__copyright__ = "Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved."
|
|
|
|
class Config:
|
|
"""class Config
|
|
|
|
Stores the configuration options for the rest of the script.
|
|
|
|
Handles the command line options, and allows the code within
|
|
the script to easily interact with the 'config' requested by
|
|
the user.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.base_dir = os.getcwd()
|
|
(self.options, self.args) = self.CheckOptions()
|
|
self.__init_dirs__()
|
|
|
|
def CheckOptions(self):
|
|
Parser = \
|
|
OptionParser(
|
|
description=__copyright__,
|
|
version=__version__,
|
|
prog="mingw-gcc-build",
|
|
usage="%prog [options] [target]"
|
|
)
|
|
Parser.add_option(
|
|
"--arch",
|
|
action = "store", type = "string",
|
|
default = '',
|
|
dest = "arch",
|
|
help = "Processor architecture to build gcc for."
|
|
)
|
|
Parser.add_option(
|
|
"--src-dir",
|
|
action = "store", type = "string", dest = "src_dir",
|
|
default = os.path.join(self.base_dir, 'src'),
|
|
help = "Directory to download/extract binutils/gcc sources"
|
|
)
|
|
Parser.add_option(
|
|
"--build-dir",
|
|
action = "store", type = "string", dest = "build_dir",
|
|
default = os.path.join(self.base_dir, 'build'),
|
|
help = "Directory to download/extract binutils/gcc sources"
|
|
)
|
|
Parser.add_option(
|
|
"--prefix",
|
|
action = "store", type = "string", dest = "prefix",
|
|
default = os.path.join(self.base_dir, 'install'),
|
|
help = "Prefix to install binutils/gcc into"
|
|
)
|
|
Parser.add_option(
|
|
"--skip-binutils",
|
|
action = "store_true", dest = "skip_binutils",
|
|
default = False,
|
|
help = "Will skip building binutils"
|
|
)
|
|
Parser.add_option(
|
|
"--skip-gcc",
|
|
action = "store_true", dest = "skip_gcc",
|
|
default = False,
|
|
help = "Will skip building GCC"
|
|
)
|
|
Parser.add_option(
|
|
"--symlinks",
|
|
action = "store", type = "string", dest = "symlinks",
|
|
default = os.path.join(self.base_dir, 'symlinks'),
|
|
help = "Directory to create binutils/gcc symbolic links into."
|
|
)
|
|
Parser.add_option(
|
|
"-v", "--verbose",
|
|
action="store_true",
|
|
type=None, help="Print verbose messages"
|
|
)
|
|
|
|
(Opt, Args) = Parser.parse_args()
|
|
|
|
self.arch = Opt.arch.lower()
|
|
allowedArchs = ('ia32', 'x64', 'ipf')
|
|
if self.arch not in allowedArchs:
|
|
Parser.error(
|
|
'Please use --arch to specify one of: %s' %
|
|
', '.join(allowedArchs)
|
|
)
|
|
self.target_arch = {'ia32': 'i686', 'x64': 'x86_64', 'ipf': 'ia64'}[self.arch]
|
|
self.target_sys = {'ia32': 'pc', 'x64': 'pc', 'ipf': 'pc'}[self.arch]
|
|
self.target_bin = {'ia32': 'mingw32', 'x64': 'mingw32', 'ipf': 'elf'}[self.arch]
|
|
self.target_combo = '-'.join((self.target_arch, self.target_sys, self.target_bin))
|
|
|
|
return (Opt, Args)
|
|
|
|
def __init_dirs__(self):
|
|
self.src_dir = os.path.realpath(os.path.expanduser(self.options.src_dir))
|
|
self.build_dir = os.path.realpath(os.path.expanduser(self.options.build_dir))
|
|
self.prefix = os.path.realpath(os.path.expanduser(self.options.prefix))
|
|
self.symlinks = os.path.realpath(os.path.expanduser(self.options.symlinks))
|
|
|
|
def IsConfigOk(self):
|
|
|
|
building = []
|
|
if not self.options.skip_binutils:
|
|
building.append('binutils')
|
|
if not self.options.skip_gcc:
|
|
building.append('gcc')
|
|
if len(building) == 0:
|
|
print "Nothing will be built!"
|
|
print
|
|
print "Please try using --help and then change the configuration."
|
|
return False
|
|
|
|
print "Current directory:"
|
|
print " ", self.base_dir
|
|
print "Sources download/extraction:", self.Relative(self.src_dir)
|
|
print "Build directory :", self.Relative(self.build_dir)
|
|
print "Prefix (install) directory :", self.Relative(self.prefix)
|
|
print "Create symlinks directory :", self.Relative(self.symlinks)
|
|
print "Building :", ', '.join(building)
|
|
print
|
|
answer = raw_input("Is this configuration ok? (default = no): ")
|
|
if (answer.lower() not in ('y', 'yes')):
|
|
print
|
|
print "Please try using --help and then change the configuration."
|
|
return False
|
|
|
|
if self.arch.lower() == 'ipf':
|
|
print
|
|
print 'Please note that the IPF compiler built by this script has'
|
|
print 'not yet been validated!'
|
|
print
|
|
answer = raw_input("Are you sure you want to build it? (default = no): ")
|
|
if (answer.lower() not in ('y', 'yes')):
|
|
print
|
|
print "Please try using --help and then change the configuration."
|
|
return False
|
|
|
|
print
|
|
return True
|
|
|
|
def Relative(self, path):
|
|
if path.startswith(self.base_dir):
|
|
return '.' + path[len(self.base_dir):]
|
|
return path
|
|
|
|
def MakeDirs(self):
|
|
for path in (self.src_dir, self.build_dir,self.prefix, self.symlinks):
|
|
if not os.path.exists(path):
|
|
os.makedirs(path)
|
|
|
|
class SourceFiles:
|
|
"""class SourceFiles
|
|
|
|
Handles the downloading of source files used by the script.
|
|
"""
|
|
|
|
def __init__(self, config):
|
|
self.config = config
|
|
self.source_files = self.source_files[config.arch]
|
|
|
|
if config.options.skip_binutils:
|
|
del self.source_files['binutils']
|
|
|
|
if config.options.skip_gcc:
|
|
del self.source_files['gcc']
|
|
del self.source_files['mingw_hdr']
|
|
|
|
source_files_common = {
|
|
'binutils': {
|
|
'url': 'http://www.kernel.org/pub/linux/devel/binutils/' + \
|
|
'binutils-$version.tar.bz2',
|
|
'version': '2.20.51.0.5',
|
|
'md5': '6d2de7cdf7a8389e70b124e3d73b4d37',
|
|
},
|
|
}
|
|
|
|
source_files_x64 = {
|
|
'gcc': {
|
|
'url': 'http://ftpmirror.gnu.org/gcc/' + \
|
|
'gcc-$version/gcc-$version.tar.bz2',
|
|
'version': '4.3.0',
|
|
'md5': '197ed8468b38db1d3481c3111691d85b',
|
|
},
|
|
}
|
|
|
|
source_files_ia32 = {
|
|
'gcc': source_files_x64['gcc'],
|
|
}
|
|
|
|
source_files_ipf = source_files_x64.copy()
|
|
source_files_ipf['gcc']['configure-params'] = (
|
|
'--with-gnu-as', '--with-gnu-ld', '--with-newlib',
|
|
'--verbose', '--disable-libssp', '--disable-nls',
|
|
'--enable-languages=c,c++'
|
|
)
|
|
|
|
source_files = {
|
|
'ia32': [source_files_common, source_files_ia32],
|
|
'x64': [source_files_common, source_files_x64],
|
|
'ipf': [source_files_common, source_files_ipf],
|
|
}
|
|
|
|
for arch in source_files:
|
|
mergedSourceFiles = {}
|
|
for source_files_dict in source_files[arch]:
|
|
mergedSourceFiles.update(source_files_dict)
|
|
for downloadItem in mergedSourceFiles:
|
|
fdata = mergedSourceFiles[downloadItem]
|
|
fdata['filename'] = fdata['url'].split('/')[-1]
|
|
if 'extract-dir' not in fdata:
|
|
for ext in ('.tar.gz', '.tar.bz2', '.zip'):
|
|
if fdata['filename'].endswith(ext):
|
|
fdata['extract-dir'] = fdata['filename'][:-len(ext)]
|
|
break
|
|
replaceables = ('extract-dir', 'filename', 'url')
|
|
for replaceItem in fdata:
|
|
if replaceItem in replaceables: continue
|
|
if type(fdata[replaceItem]) != str: continue
|
|
for replaceable in replaceables:
|
|
if type(fdata[replaceable]) != str: continue
|
|
if replaceable in fdata:
|
|
fdata[replaceable] = \
|
|
fdata[replaceable].replace(
|
|
'$' + replaceItem,
|
|
fdata[replaceItem]
|
|
)
|
|
source_files[arch] = mergedSourceFiles
|
|
#print 'source_files:', source_files
|
|
|
|
def GetAll(self):
|
|
|
|
def progress(received, blockSize, fileSize):
|
|
if fileSize < 0: return
|
|
wDots = (100 * received * blockSize) / fileSize / 10
|
|
if wDots > self.dots:
|
|
for i in range(wDots - self.dots):
|
|
print '.',
|
|
sys.stdout.flush()
|
|
self.dots += 1
|
|
|
|
maxRetries = 1
|
|
for (fname, fdata) in self.source_files.items():
|
|
for retries in range(maxRetries):
|
|
try:
|
|
self.dots = 0
|
|
local_file = os.path.join(self.config.src_dir, fdata['filename'])
|
|
url = fdata['url']
|
|
print 'Downloading %s:' % fname, url
|
|
if retries > 0:
|
|
print '(retry)',
|
|
sys.stdout.flush()
|
|
|
|
completed = False
|
|
if os.path.exists(local_file):
|
|
md5_pass = self.checkHash(fdata)
|
|
if md5_pass:
|
|
print '[md5 match]',
|
|
else:
|
|
print '[md5 mismatch]',
|
|
sys.stdout.flush()
|
|
completed = md5_pass
|
|
|
|
if not completed:
|
|
urllib.urlretrieve(url, local_file, progress)
|
|
|
|
#
|
|
# BUGBUG: Suggest proxy to user if download fails.
|
|
#
|
|
# export http_proxy=http://proxyservername.mycompany.com:911
|
|
# export ftp_proxy=http://proxyservername.mycompany.com:911
|
|
|
|
if not completed and os.path.exists(local_file):
|
|
md5_pass = self.checkHash(fdata)
|
|
if md5_pass:
|
|
print '[md5 match]',
|
|
else:
|
|
print '[md5 mismatch]',
|
|
sys.stdout.flush()
|
|
completed = md5_pass
|
|
|
|
if completed:
|
|
print '[done]'
|
|
break
|
|
else:
|
|
print '[failed]'
|
|
print ' Tried to retrieve', url
|
|
print ' to', local_file
|
|
print 'Possible fixes:'
|
|
print '* If you are behind a web-proxy, try setting the',
|
|
print 'http_proxy environment variable'
|
|
print '* You can try to download this file separately',
|
|
print 'and rerun this script'
|
|
raise Exception()
|
|
|
|
except KeyboardInterrupt:
|
|
print '[KeyboardInterrupt]'
|
|
return False
|
|
|
|
except Exception, e:
|
|
print e
|
|
|
|
if not completed: return False
|
|
|
|
return True
|
|
|
|
def checkHash(self, fdata):
|
|
local_file = os.path.join(self.config.src_dir, fdata['filename'])
|
|
expect_md5 = fdata['md5']
|
|
data = open(local_file).read()
|
|
md5sum = md5()
|
|
md5sum.update(data)
|
|
return md5sum.hexdigest().lower() == expect_md5.lower()
|
|
|
|
def GetModules(self):
|
|
return self.source_files.keys()
|
|
|
|
def GetFilenameOf(self, module):
|
|
return self.source_files[module]['filename']
|
|
|
|
def GetMd5Of(self, module):
|
|
return self.source_files[module]['md5']
|
|
|
|
def GetExtractDirOf(self, module):
|
|
return self.source_files[module]['extract-dir']
|
|
|
|
def GetAdditionalParameters(self, module, step):
|
|
key = step + '-params'
|
|
if key in self.source_files[module]:
|
|
return self.source_files[module][key]
|
|
else:
|
|
return tuple()
|
|
|
|
class Extracter:
|
|
"""class Extracter
|
|
|
|
Handles the extraction of the source files from their downloaded
|
|
archive files.
|
|
"""
|
|
|
|
def __init__(self, source_files, config):
|
|
self.source_files = source_files
|
|
self.config = config
|
|
|
|
def Extract(self, module):
|
|
src = self.config.src_dir
|
|
extractDst = os.path.join(src, self.config.arch)
|
|
local_file = os.path.join(src, self.source_files.GetFilenameOf(module))
|
|
moduleMd5 = self.source_files.GetMd5Of(module)
|
|
extracted = os.path.join(extractDst, os.path.split(local_file)[1] + '.extracted')
|
|
if not os.path.exists(extractDst):
|
|
os.mkdir(extractDst)
|
|
|
|
extractedMd5 = None
|
|
if os.path.exists(extracted):
|
|
extractedMd5 = open(extracted).read()
|
|
|
|
if extractedMd5 != moduleMd5:
|
|
print 'Extracting %s:' % self.config.Relative(local_file)
|
|
tar = tarfile.open(local_file)
|
|
tar.extractall(extractDst)
|
|
open(extracted, 'w').write(moduleMd5)
|
|
else:
|
|
pass
|
|
#print 'Previously extracted', self.config.Relative(local_file)
|
|
|
|
def ExtractAll(self):
|
|
for module in self.source_files.GetModules():
|
|
self.Extract(module)
|
|
|
|
class Builder:
|
|
"""class Builder
|
|
|
|
Builds and installs the GCC tool suite.
|
|
"""
|
|
|
|
def __init__(self, source_files, config):
|
|
self.source_files = source_files
|
|
self.config = config
|
|
|
|
def Build(self):
|
|
if not self.config.options.skip_binutils:
|
|
self.BuildModule('binutils')
|
|
if not self.config.options.skip_gcc:
|
|
self.BuildModule('gcc')
|
|
self.MakeSymLinks()
|
|
|
|
def IsBuildStepComplete(self, step):
|
|
return \
|
|
os.path.exists(
|
|
os.path.join(
|
|
self.config.build_dir, self.config.arch, step + '.completed'
|
|
)
|
|
)
|
|
|
|
def MarkBuildStepComplete(self, step):
|
|
open(
|
|
os.path.join(
|
|
self.config.build_dir, self.config.arch, step + '.completed'
|
|
),
|
|
"w"
|
|
).close()
|
|
|
|
|
|
def BuildModule(self, module):
|
|
base_dir = os.getcwd()
|
|
build_dir = os.path.join(self.config.build_dir, self.config.arch, module)
|
|
module_dir = self.source_files.GetExtractDirOf(module)
|
|
module_dir = os.path.realpath(os.path.join('src', self.config.arch, module_dir))
|
|
configure = os.path.join(module_dir, 'configure')
|
|
prefix = self.config.prefix
|
|
if not os.path.exists(build_dir):
|
|
os.makedirs(build_dir)
|
|
os.chdir(build_dir)
|
|
|
|
cmd = (
|
|
configure,
|
|
'--target=%s' % self.config.target_combo,
|
|
'--prefix=' + prefix,
|
|
'--with-sysroot=' + prefix,
|
|
'--disable-werror',
|
|
)
|
|
if os.path.exists('/opt/local/include/gmp.h'):
|
|
cmd += ('--with-gmp=/opt/local',)
|
|
if module == 'gcc': cmd += ('--oldincludedir=/opt/local/include',)
|
|
cmd += self.source_files.GetAdditionalParameters(module, 'configure')
|
|
self.RunCommand(cmd, module, 'config', skipable=True)
|
|
|
|
cmd = ('make',)
|
|
if module == 'gcc':
|
|
cmd += ('all-gcc',)
|
|
self.RunCommand(cmd, module, 'build')
|
|
|
|
cmd = ('make',)
|
|
if module == 'gcc':
|
|
cmd += ('install-gcc',)
|
|
else:
|
|
cmd += ('install',)
|
|
self.RunCommand(cmd, module, 'install')
|
|
|
|
os.chdir(base_dir)
|
|
|
|
print '%s module is now built and installed' % module
|
|
|
|
def RunCommand(self, cmd, module, stage, skipable=False):
|
|
if skipable:
|
|
if self.IsBuildStepComplete('%s.%s' % (module, stage)):
|
|
return
|
|
|
|
popen = lambda cmd: \
|
|
subprocess.Popen(
|
|
cmd,
|
|
stdin=subprocess.PIPE,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT
|
|
)
|
|
|
|
print '%s [%s] ...' % (module, stage),
|
|
sys.stdout.flush()
|
|
p = popen(cmd)
|
|
output = p.stdout.read()
|
|
p.wait()
|
|
if p.returncode != 0:
|
|
print '[failed!]'
|
|
logFile = os.path.join(self.config.build_dir, 'log.txt')
|
|
f = open(logFile, "w")
|
|
f.write(output)
|
|
f.close()
|
|
raise Exception, 'Failed to %s %s\n' % (stage, module) + \
|
|
'See output log at %s' % self.config.Relative(logFile)
|
|
else:
|
|
print '[done]'
|
|
|
|
if skipable:
|
|
self.MarkBuildStepComplete('%s.%s' % (module, stage))
|
|
|
|
def MakeSymLinks(self):
|
|
links_dir = os.path.join(self.config.symlinks, self.config.arch)
|
|
if not os.path.exists(links_dir):
|
|
os.makedirs(links_dir)
|
|
startPrinted = False
|
|
for link in ('ar', 'ld', 'gcc'):
|
|
src = os.path.join(
|
|
self.config.prefix, 'bin', self.config.target_combo + '-' + link
|
|
)
|
|
linkdst = os.path.join(links_dir, link)
|
|
if not os.path.lexists(linkdst):
|
|
if not startPrinted:
|
|
print 'Making symlinks in %s:' % self.config.Relative(links_dir),
|
|
startPrinted = True
|
|
print link,
|
|
os.symlink(src, linkdst)
|
|
|
|
if startPrinted:
|
|
print '[done]'
|
|
|
|
class App:
|
|
"""class App
|
|
|
|
The main body of the application.
|
|
"""
|
|
|
|
def __init__(self):
|
|
config = Config()
|
|
|
|
if not config.IsConfigOk():
|
|
return
|
|
|
|
config.MakeDirs()
|
|
|
|
sources = SourceFiles(config)
|
|
result = sources.GetAll()
|
|
if result:
|
|
print 'All files have been downloaded & verified'
|
|
else:
|
|
print 'An error occured while downloading a file'
|
|
return
|
|
|
|
Extracter(sources, config).ExtractAll()
|
|
|
|
Builder(sources, config).Build()
|
|
|
|
App()
|
|
|