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