Vishal Bhoj | 82c8071 | 2015-12-15 21:13:33 +0530 | [diff] [blame^] | 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 | |