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