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.
13 import buildbot_common
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
22 sys.path.append(os.path.join(SDK_SRC_DIR, 'tools'))
26 MAKE = 'nacl_sdk/make_3.99.90-26-gf80222c/make.exe'
42 # Global verbosity setting.
43 # If set to try (normally via a command line arg) then build_projects will
44 # add V=1 to all calls to 'make'
48 def CopyFilesFromTo(filelist, srcdir, dstdir):
49 for filename in filelist:
50 srcpath = os.path.join(srcdir, filename)
51 dstpath = os.path.join(dstdir, filename)
52 buildbot_common.CopyFile(srcpath, dstpath)
55 def UpdateHelpers(pepperdir, clobber=False):
56 tools_dir = os.path.join(pepperdir, 'tools')
57 if not os.path.exists(tools_dir):
58 buildbot_common.ErrorExit('SDK tools dir is missing: %s' % tools_dir)
60 exampledir = os.path.join(pepperdir, 'examples')
62 buildbot_common.RemoveDir(exampledir)
63 buildbot_common.MakeDir(exampledir)
65 # Copy files for individual build and landing page
66 files = ['favicon.ico', 'httpd.cmd', 'index.css', 'index.js',
67 'button_close.png', 'button_close_hover.png']
68 CopyFilesFromTo(files, SDK_RESOURCE_DIR, exampledir)
70 # Copy tools scripts and make includes
71 buildbot_common.CopyDir(os.path.join(SDK_SRC_DIR, 'tools', '*.py'),
73 buildbot_common.CopyDir(os.path.join(SDK_SRC_DIR, 'tools', '*.mk'),
76 # Copy tools/lib scripts
77 tools_lib_dir = os.path.join(pepperdir, 'tools', 'lib')
78 buildbot_common.MakeDir(tools_lib_dir)
79 buildbot_common.CopyDir(os.path.join(SDK_SRC_DIR, 'tools', 'lib', '*.py'),
82 # On Windows add a prebuilt make
83 if getos.GetPlatform() == 'win':
84 buildbot_common.BuildStep('Add MAKE')
85 make_url = posixpath.join(GSTORE, MAKE)
86 make_exe = os.path.join(tools_dir, 'make.exe')
87 with open(make_exe, 'wb') as f:
88 f.write(urllib2.urlopen(make_url).read())
91 def ValidateToolchains(toolchains):
92 invalid_toolchains = set(toolchains) - set(VALID_TOOLCHAINS)
93 if invalid_toolchains:
94 buildbot_common.ErrorExit('Invalid toolchain(s): %s' % (
95 ', '.join(invalid_toolchains)))
97 def GetDeps(projects):
100 # Build list of all project names
101 localtargets = [proj['NAME'] for proj in projects]
104 for proj in projects:
106 # generate a list of dependencies
107 for targ in proj.get('TARGETS', []):
108 deplist.extend(targ.get('DEPS', []) + targ.get('LIBS', []))
110 # and add dependencies to targets built in this subtree
111 localdeps = [dep for dep in deplist if dep in localtargets]
113 out[proj['NAME']] = localdeps
118 def UpdateProjects(pepperdir, project_tree, toolchains,
119 clobber=False, configs=None, first_toolchain=False):
121 configs = ['Debug', 'Release']
122 if not os.path.exists(os.path.join(pepperdir, 'tools')):
123 buildbot_common.ErrorExit('Examples depend on missing tools.')
124 if not os.path.exists(os.path.join(pepperdir, 'toolchain')):
125 buildbot_common.ErrorExit('Examples depend on missing toolchains.')
127 ValidateToolchains(toolchains)
129 # Create the library output directories
130 libdir = os.path.join(pepperdir, 'lib')
131 platform = getos.GetPlatform()
132 for config in configs:
133 for arch in LIB_DICT[platform]:
134 dirpath = os.path.join(libdir, '%s_%s_host' % (platform, arch), config)
136 buildbot_common.RemoveDir(dirpath)
137 buildbot_common.MakeDir(dirpath)
140 for branch, projects in project_tree.iteritems():
141 dirpath = os.path.join(pepperdir, branch)
143 buildbot_common.RemoveDir(dirpath)
144 buildbot_common.MakeDir(dirpath)
145 targets = [desc['NAME'] for desc in projects]
146 deps = GetDeps(projects)
148 # Generate master make for this branch of projects
149 generate_make.GenerateMasterMakefile(pepperdir,
150 os.path.join(pepperdir, branch),
153 if branch.startswith('examples') and not landing_page:
154 landing_page = LandingPage()
156 # Generate individual projects
157 for desc in projects:
158 srcroot = os.path.dirname(desc['FILEPATH'])
159 generate_make.ProcessProject(pepperdir, srcroot, pepperdir, desc,
160 toolchains, configs=configs,
161 first_toolchain=first_toolchain)
163 if branch.startswith('examples'):
164 landing_page.AddDesc(desc)
167 # Generate the landing page text file.
168 index_html = os.path.join(pepperdir, 'examples', 'index.html')
169 index_template = os.path.join(SDK_RESOURCE_DIR, 'index.html.template')
170 with open(index_html, 'w') as fh:
171 out = landing_page.GeneratePage(index_template)
174 # Generate top Make for examples
175 targets = ['api', 'demo', 'getting_started', 'tutorial']
176 targets = [x for x in targets if 'examples/'+x in project_tree]
177 branch_name = 'examples'
178 generate_make.GenerateMasterMakefile(pepperdir,
179 os.path.join(pepperdir, branch_name),
183 def BuildProjectsBranch(pepperdir, branch, deps, clean, config, args=None):
184 make_dir = os.path.join(pepperdir, branch)
185 print "\nMake: " + make_dir
187 if getos.GetPlatform() == 'win':
188 # We need to modify the environment to build host on Windows.
189 make = os.path.join(make_dir, 'make.bat')
194 if os.environ.get('USE_GOMA') == '1':
195 env = dict(os.environ)
196 env['NACL_COMPILER_PREFIX'] = 'gomacc'
197 # Add -m32 to the CFLAGS when building using i686-nacl-gcc
198 # otherwise goma won't recognise it as different to the x86_64
200 env['X86_32_CFLAGS'] = '-m32'
201 env['X86_32_CXXFLAGS'] = '-m32'
204 jobs = str(multiprocessing.cpu_count())
206 make_cmd = [make, '-j', jobs]
208 make_cmd.append('CONFIG='+config)
209 # We always ENABLE_BIONIC in case we need it. If neither --bionic nor
210 # -t bionic have been provided on the command line, then VALID_TOOLCHAINS
211 # will not contain a bionic target.
212 make_cmd.append('ENABLE_BIONIC=1')
214 make_cmd.append('IGNORE_DEPS=1')
217 make_cmd.append('V=1')
222 make_cmd.append('TOOLCHAIN=all')
224 buildbot_common.Run(make_cmd, cwd=make_dir, env=env)
226 # Clean to remove temporary files but keep the built
227 buildbot_common.Run(make_cmd + ['clean'], cwd=make_dir, env=env)
230 def BuildProjects(pepperdir, project_tree, deps=True,
231 clean=False, config='Debug'):
233 # Make sure we build libraries (which live in 'src') before
234 # any of the examples.
235 build_first = [p for p in project_tree if p != 'src']
236 build_second = [p for p in project_tree if p == 'src']
238 for branch in build_first + build_second:
239 BuildProjectsBranch(pepperdir, branch, deps, clean, config)
243 parser = optparse.OptionParser()
244 parser.add_option('-c', '--clobber',
245 help='Clobber project directories before copying new files',
246 action='store_true', default=False)
247 parser.add_option('-b', '--build',
248 help='Build the projects.', action='store_true')
249 parser.add_option('--config',
250 help='Choose configuration to build (Debug or Release). Builds both '
252 parser.add_option('--bionic',
253 help='Enable bionic projects', action='store_true')
254 parser.add_option('-x', '--experimental',
255 help='Build experimental projects', action='store_true')
256 parser.add_option('-t', '--toolchain',
257 help='Build using toolchain. Can be passed more than once.',
258 action='append', default=[])
259 parser.add_option('-d', '--dest',
260 help='Select which build destinations (project types) are valid.',
262 parser.add_option('-v', '--verbose', action='store_true')
264 # To setup bash completion for this command first install optcomplete
265 # and then add this line to your .bashrc:
266 # complete -F _optcomplete build_projects.py
269 optcomplete.autocomplete(parser)
273 options, args = parser.parse_args(argv[1:])
275 if 'NACL_SDK_ROOT' in os.environ:
276 # We don't want the currently configured NACL_SDK_ROOT to have any effect
278 del os.environ['NACL_SDK_ROOT']
280 pepper_ver = str(int(build_version.ChromeMajorVersion()))
281 pepperdir = os.path.join(OUT_DIR, 'pepper_' + pepper_ver)
283 if not options.toolchain:
284 # Order matters here: the default toolchain for an example's Makefile will
285 # be the first toolchain in this list that is available in the example.
286 # e.g. If an example supports newlib and glibc, then the default will be
288 options.toolchain = ['pnacl', 'newlib', 'glibc', 'host']
289 if options.experimental or options.bionic:
290 options.toolchain.append('bionic')
292 if 'host' in options.toolchain:
293 options.toolchain.remove('host')
294 options.toolchain.append(getos.GetPlatform())
295 print 'Adding platform: ' + getos.GetPlatform()
297 ValidateToolchains(options.toolchain)
300 if options.toolchain:
301 filters['TOOLS'] = options.toolchain
302 print 'Filter by toolchain: ' + str(options.toolchain)
303 if not options.experimental:
304 filters['EXPERIMENTAL'] = False
306 filters['DEST'] = options.dest
307 print 'Filter by type: ' + str(options.dest)
309 filters['NAME'] = args
310 print 'Filter by name: ' + str(args)
313 project_tree = parse_dsc.LoadProjectTree(SDK_SRC_DIR, include=filters)
314 except parse_dsc.ValidationError as e:
315 buildbot_common.ErrorExit(str(e))
316 parse_dsc.PrintProjectTree(project_tree)
318 UpdateHelpers(pepperdir, clobber=options.clobber)
319 UpdateProjects(pepperdir, project_tree, options.toolchain,
320 clobber=options.clobber)
328 configs = [options.config]
330 configs = ['Debug', 'Release']
331 for config in configs:
332 BuildProjects(pepperdir, project_tree, config=config)
337 if __name__ == '__main__':
338 script_name = os.path.basename(sys.argv[0])
340 sys.exit(main(sys.argv))
341 except parse_dsc.ValidationError as e:
342 buildbot_common.ErrorExit('%s: %s' % (script_name, e))
343 except KeyboardInterrupt:
344 buildbot_common.ErrorExit('%s: interrupted' % script_name)