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