/native_client_sdk/src/build_tools/build_projects.py

https://gitlab.com/jonnialva90/iridium-browser · Python · 357 lines · 250 code · 68 blank · 39 comment · 67 complexity · 1ee63c029a35f702e242fcc7d0a3a503 MD5 · raw file

  1. #!/usr/bin/env python
  2. # Copyright (c) 2013 The Chromium Authors. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. import argparse
  6. import multiprocessing
  7. import os
  8. import posixpath
  9. import sys
  10. import urllib2
  11. import buildbot_common
  12. import build_version
  13. import generate_make
  14. import parse_dsc
  15. from build_paths import SDK_SRC_DIR, OUT_DIR, SDK_RESOURCE_DIR
  16. from build_paths import GSTORE
  17. from generate_index import LandingPage
  18. sys.path.append(os.path.join(SDK_SRC_DIR, 'tools'))
  19. import getos
  20. MAKE = 'nacl_sdk/make_3.99.90-26-gf80222c/make.exe'
  21. LIB_DICT = {
  22. 'linux': [],
  23. 'mac': [],
  24. 'win': ['x86_32']
  25. }
  26. VALID_TOOLCHAINS = [
  27. 'bionic',
  28. 'newlib',
  29. 'clang-newlib',
  30. 'glibc',
  31. 'pnacl',
  32. 'win',
  33. 'linux',
  34. 'mac',
  35. ]
  36. # Global verbosity setting.
  37. # If set to True (normally via a command line arg) then build_projects will
  38. # add V=1 to all calls to 'make'
  39. verbose = False
  40. def Trace(msg):
  41. if verbose:
  42. sys.stderr.write(str(msg) + '\n')
  43. def CopyFilesFromTo(filelist, srcdir, dstdir):
  44. for filename in filelist:
  45. srcpath = os.path.join(srcdir, filename)
  46. dstpath = os.path.join(dstdir, filename)
  47. buildbot_common.CopyFile(srcpath, dstpath)
  48. def UpdateHelpers(pepperdir, clobber=False):
  49. tools_dir = os.path.join(pepperdir, 'tools')
  50. if not os.path.exists(tools_dir):
  51. buildbot_common.ErrorExit('SDK tools dir is missing: %s' % tools_dir)
  52. exampledir = os.path.join(pepperdir, 'examples')
  53. if clobber:
  54. buildbot_common.RemoveDir(exampledir)
  55. buildbot_common.MakeDir(exampledir)
  56. # Copy files for individual build and landing page
  57. files = ['favicon.ico', 'httpd.cmd', 'index.css', 'index.js',
  58. 'button_close.png', 'button_close_hover.png']
  59. CopyFilesFromTo(files, SDK_RESOURCE_DIR, exampledir)
  60. # Copy tools scripts and make includes
  61. buildbot_common.CopyDir(os.path.join(SDK_SRC_DIR, 'tools', '*.py'),
  62. tools_dir)
  63. buildbot_common.CopyDir(os.path.join(SDK_SRC_DIR, 'tools', '*.mk'),
  64. tools_dir)
  65. # Copy tools/lib scripts
  66. tools_lib_dir = os.path.join(pepperdir, 'tools', 'lib')
  67. buildbot_common.MakeDir(tools_lib_dir)
  68. buildbot_common.CopyDir(os.path.join(SDK_SRC_DIR, 'tools', 'lib', '*.py'),
  69. tools_lib_dir)
  70. # On Windows add a prebuilt make
  71. if getos.GetPlatform() == 'win':
  72. buildbot_common.BuildStep('Add MAKE')
  73. make_url = posixpath.join(GSTORE, MAKE)
  74. make_exe = os.path.join(tools_dir, 'make.exe')
  75. with open(make_exe, 'wb') as f:
  76. f.write(urllib2.urlopen(make_url).read())
  77. def ValidateToolchains(toolchains):
  78. invalid_toolchains = set(toolchains) - set(VALID_TOOLCHAINS)
  79. if invalid_toolchains:
  80. buildbot_common.ErrorExit('Invalid toolchain(s): %s' % (
  81. ', '.join(invalid_toolchains)))
  82. def GetDeps(projects):
  83. out = {}
  84. # Build list of all project names
  85. localtargets = [proj['NAME'] for proj in projects]
  86. # For each project
  87. for proj in projects:
  88. deplist = []
  89. # generate a list of dependencies
  90. for targ in proj.get('TARGETS', []):
  91. deplist.extend(targ.get('DEPS', []) + targ.get('LIBS', []))
  92. # and add dependencies to targets built in this subtree
  93. localdeps = [dep for dep in deplist if dep in localtargets]
  94. if localdeps:
  95. out[proj['NAME']] = localdeps
  96. return out
  97. def UpdateProjects(pepperdir, project_tree, toolchains,
  98. clobber=False, configs=None, first_toolchain=False):
  99. if configs is None:
  100. configs = ['Debug', 'Release']
  101. if not os.path.exists(os.path.join(pepperdir, 'tools')):
  102. buildbot_common.ErrorExit('Examples depend on missing tools.')
  103. if not os.path.exists(os.path.join(pepperdir, 'toolchain')):
  104. buildbot_common.ErrorExit('Examples depend on missing toolchains.')
  105. ValidateToolchains(toolchains)
  106. # Create the library output directories
  107. libdir = os.path.join(pepperdir, 'lib')
  108. platform = getos.GetPlatform()
  109. for config in configs:
  110. for arch in LIB_DICT[platform]:
  111. dirpath = os.path.join(libdir, '%s_%s_host' % (platform, arch), config)
  112. if clobber:
  113. buildbot_common.RemoveDir(dirpath)
  114. buildbot_common.MakeDir(dirpath)
  115. landing_page = None
  116. for branch, projects in project_tree.iteritems():
  117. dirpath = os.path.join(pepperdir, branch)
  118. if clobber:
  119. buildbot_common.RemoveDir(dirpath)
  120. buildbot_common.MakeDir(dirpath)
  121. targets = [desc['NAME'] for desc in projects]
  122. deps = GetDeps(projects)
  123. # Generate master make for this branch of projects
  124. generate_make.GenerateMasterMakefile(pepperdir,
  125. os.path.join(pepperdir, branch),
  126. targets, deps)
  127. if branch.startswith('examples') and not landing_page:
  128. landing_page = LandingPage()
  129. # Generate individual projects
  130. for desc in projects:
  131. srcroot = os.path.dirname(desc['FILEPATH'])
  132. generate_make.ProcessProject(pepperdir, srcroot, pepperdir, desc,
  133. toolchains, configs=configs,
  134. first_toolchain=first_toolchain)
  135. if branch.startswith('examples'):
  136. landing_page.AddDesc(desc)
  137. if landing_page:
  138. # Generate the landing page text file.
  139. index_html = os.path.join(pepperdir, 'examples', 'index.html')
  140. index_template = os.path.join(SDK_RESOURCE_DIR, 'index.html.template')
  141. with open(index_html, 'w') as fh:
  142. out = landing_page.GeneratePage(index_template)
  143. fh.write(out)
  144. # Generate top Make for examples
  145. targets = ['api', 'demo', 'getting_started', 'tutorial']
  146. targets = [x for x in targets if 'examples/'+x in project_tree]
  147. branch_name = 'examples'
  148. generate_make.GenerateMasterMakefile(pepperdir,
  149. os.path.join(pepperdir, branch_name),
  150. targets, {})
  151. def BuildProjectsBranch(pepperdir, branch, deps, clean, config, args=None):
  152. make_dir = os.path.join(pepperdir, branch)
  153. print "\nMake: " + make_dir
  154. if getos.GetPlatform() == 'win':
  155. # We need to modify the environment to build host on Windows.
  156. make = os.path.join(make_dir, 'make.bat')
  157. else:
  158. make = 'make'
  159. env = None
  160. if os.environ.get('USE_GOMA') == '1':
  161. env = dict(os.environ)
  162. env['NACL_COMPILER_PREFIX'] = 'gomacc'
  163. # Add -m32 to the CFLAGS when building using i686-nacl-gcc
  164. # otherwise goma won't recognise it as different to the x86_64
  165. # build.
  166. env['X86_32_CFLAGS'] = '-m32'
  167. env['X86_32_CXXFLAGS'] = '-m32'
  168. jobs = '50'
  169. else:
  170. jobs = str(multiprocessing.cpu_count())
  171. make_cmd = [make, '-j', jobs]
  172. make_cmd.append('CONFIG='+config)
  173. # We always ENABLE_BIONIC in case we need it. If neither --bionic nor
  174. # -t bionic have been provided on the command line, then VALID_TOOLCHAINS
  175. # will not contain a bionic target.
  176. make_cmd.append('ENABLE_BIONIC=1')
  177. if not deps:
  178. make_cmd.append('IGNORE_DEPS=1')
  179. if verbose:
  180. make_cmd.append('V=1')
  181. if args:
  182. make_cmd += args
  183. else:
  184. make_cmd.append('TOOLCHAIN=all')
  185. buildbot_common.Run(make_cmd, cwd=make_dir, env=env)
  186. if clean:
  187. # Clean to remove temporary files but keep the built
  188. buildbot_common.Run(make_cmd + ['clean'], cwd=make_dir, env=env)
  189. def BuildProjects(pepperdir, project_tree, deps=True,
  190. clean=False, config='Debug'):
  191. # Make sure we build libraries (which live in 'src') before
  192. # any of the examples.
  193. build_first = [p for p in project_tree if p != 'src']
  194. build_second = [p for p in project_tree if p == 'src']
  195. for branch in build_first + build_second:
  196. BuildProjectsBranch(pepperdir, branch, deps, clean, config)
  197. def main(args):
  198. parser = argparse.ArgumentParser(description=__doc__)
  199. parser.add_argument('-c', '--clobber',
  200. help='Clobber project directories before copying new files',
  201. action='store_true', default=False)
  202. parser.add_argument('-b', '--build',
  203. help='Build the projects. Otherwise the projects are only copied.',
  204. action='store_true')
  205. parser.add_argument('--config',
  206. help='Choose configuration to build (Debug or Release). Builds both '
  207. 'by default')
  208. parser.add_argument('--bionic',
  209. help='Enable bionic projects', action='store_true')
  210. parser.add_argument('-x', '--experimental',
  211. help='Build experimental projects', action='store_true')
  212. parser.add_argument('-t', '--toolchain',
  213. help='Build using toolchain. Can be passed more than once.',
  214. action='append', default=[])
  215. parser.add_argument('-d', '--dest',
  216. help='Select which build destinations (project types) are valid.',
  217. action='append')
  218. parser.add_argument('projects', nargs='*',
  219. help='Select which projects to build.')
  220. parser.add_argument('-v', '--verbose', action='store_true')
  221. # To setup bash completion for this command first install optcomplete
  222. # and then add this line to your .bashrc:
  223. # complete -F _optcomplete build_projects.py
  224. try:
  225. import optcomplete
  226. optcomplete.autocomplete(parser)
  227. except ImportError:
  228. pass
  229. options = parser.parse_args(args)
  230. global verbose
  231. if options.verbose:
  232. verbose = True
  233. buildbot_common.verbose = verbose
  234. if 'NACL_SDK_ROOT' in os.environ:
  235. # We don't want the currently configured NACL_SDK_ROOT to have any effect
  236. # on the build.
  237. del os.environ['NACL_SDK_ROOT']
  238. pepper_ver = str(int(build_version.ChromeMajorVersion()))
  239. pepperdir = os.path.join(OUT_DIR, 'pepper_' + pepper_ver)
  240. if not options.toolchain:
  241. # Order matters here: the default toolchain for an example's Makefile will
  242. # be the first toolchain in this list that is available in the example.
  243. # e.g. If an example supports newlib and glibc, then the default will be
  244. # newlib.
  245. options.toolchain = ['pnacl', 'newlib', 'glibc', 'host', 'clang-newlib']
  246. if options.experimental or options.bionic:
  247. options.toolchain.append('bionic')
  248. if 'host' in options.toolchain:
  249. options.toolchain.remove('host')
  250. options.toolchain.append(getos.GetPlatform())
  251. Trace('Adding platform: ' + getos.GetPlatform())
  252. ValidateToolchains(options.toolchain)
  253. filters = {}
  254. if options.toolchain:
  255. filters['TOOLS'] = options.toolchain
  256. Trace('Filter by toolchain: ' + str(options.toolchain))
  257. if not options.experimental:
  258. filters['EXPERIMENTAL'] = False
  259. if options.dest:
  260. filters['DEST'] = options.dest
  261. Trace('Filter by type: ' + str(options.dest))
  262. if options.projects:
  263. filters['NAME'] = options.projects
  264. Trace('Filter by name: ' + str(options.projects))
  265. try:
  266. project_tree = parse_dsc.LoadProjectTree(SDK_SRC_DIR, include=filters)
  267. except parse_dsc.ValidationError as e:
  268. buildbot_common.ErrorExit(str(e))
  269. if verbose:
  270. parse_dsc.PrintProjectTree(project_tree)
  271. UpdateHelpers(pepperdir, clobber=options.clobber)
  272. UpdateProjects(pepperdir, project_tree, options.toolchain,
  273. clobber=options.clobber)
  274. if options.build:
  275. if options.config:
  276. configs = [options.config]
  277. else:
  278. configs = ['Debug', 'Release']
  279. for config in configs:
  280. BuildProjects(pepperdir, project_tree, config=config, deps=False)
  281. return 0
  282. if __name__ == '__main__':
  283. script_name = os.path.basename(sys.argv[0])
  284. try:
  285. sys.exit(main(sys.argv[1:]))
  286. except parse_dsc.ValidationError as e:
  287. buildbot_common.ErrorExit('%s: %s' % (script_name, e))
  288. except KeyboardInterrupt:
  289. buildbot_common.ErrorExit('%s: interrupted' % script_name)