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_EXAMPLE_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 bild and landing page 58 files = ['favicon.ico', 'httpd.cmd'] 59 CopyFilesFromTo(files, SDK_EXAMPLE_DIR, exampledir) 60 61 resourcesdir = os.path.join(SDK_EXAMPLE_DIR, 'resources') 62 files = ['index.css', 'index.js', 'button_close.png', 63 'button_close_hover.png'] 64 CopyFilesFromTo(files, resourcesdir, exampledir) 65 66 # Copy tools scripts and make includes 67 buildbot_common.CopyDir(os.path.join(SDK_SRC_DIR, 'tools', '*.py'), 68 tools_dir) 69 buildbot_common.CopyDir(os.path.join(SDK_SRC_DIR, 'tools', '*.mk'), 70 tools_dir) 71 72 # On Windows add a prebuilt make 73 if getos.GetPlatform() == 'win': 74 buildbot_common.BuildStep('Add MAKE') 75 http_download.HttpDownload(GSTORE + MAKE, 76 os.path.join(tools_dir, 'make.exe')) 77 78 79 def ValidateToolchains(toolchains): 80 invalid_toolchains = set(toolchains) - set(VALID_TOOLCHAINS) 81 if invalid_toolchains: 82 buildbot_common.ErrorExit('Invalid toolchain(s): %s' % ( 83 ', '.join(invalid_toolchains))) 84 85 86 def UpdateProjects(pepperdir, project_tree, toolchains, 87 clobber=False, configs=None, first_toolchain=False): 88 if configs is None: 89 configs = ['Debug', 'Release'] 90 if not os.path.exists(os.path.join(pepperdir, 'tools')): 91 buildbot_common.ErrorExit('Examples depend on missing tools.') 92 if not os.path.exists(os.path.join(pepperdir, 'toolchain')): 93 buildbot_common.ErrorExit('Examples depend on missing toolchains.') 94 95 ValidateToolchains(toolchains) 96 97 # Create the library output directories 98 libdir = os.path.join(pepperdir, 'lib') 99 platform = getos.GetPlatform() 100 for config in configs: 101 for arch in LIB_DICT[platform]: 102 dirpath = os.path.join(libdir, '%s_%s_host' % (platform, arch), config) 103 if clobber: 104 buildbot_common.RemoveDir(dirpath) 105 buildbot_common.MakeDir(dirpath) 106 107 landing_page = None 108 for branch, projects in project_tree.iteritems(): 109 dirpath = os.path.join(pepperdir, branch) 110 if clobber: 111 buildbot_common.RemoveDir(dirpath) 112 buildbot_common.MakeDir(dirpath) 113 targets = [desc['NAME'] for desc in projects] 114 115 # Generate master make for this branch of projects 116 generate_make.GenerateMasterMakefile(pepperdir, 117 os.path.join(pepperdir, branch), 118 targets) 119 120 if branch.startswith('examples') and not landing_page: 121 landing_page = LandingPage() 122 123 # Generate individual projects 124 for desc in projects: 125 srcroot = os.path.dirname(desc['FILEPATH']) 126 generate_make.ProcessProject(pepperdir, srcroot, pepperdir, desc, 127 toolchains, configs=configs, 128 first_toolchain=first_toolchain) 129 130 if branch.startswith('examples'): 131 landing_page.AddDesc(desc) 132 133 if landing_page: 134 # Generate the landing page text file. 135 index_html = os.path.join(pepperdir, 'examples', 'index.html') 136 example_resources_dir = os.path.join(SDK_EXAMPLE_DIR, 'resources') 137 index_template = os.path.join(example_resources_dir, 138 'index.html.template') 139 with open(index_html, 'w') as fh: 140 out = landing_page.GeneratePage(index_template) 141 fh.write(out) 142 143 # Generate top Make for examples 144 targets = ['api', 'demo', 'getting_started', 'tutorial'] 145 targets = [x for x in targets if 'examples/'+x in project_tree] 146 branch_name = 'examples' 147 generate_make.GenerateMasterMakefile(pepperdir, 148 os.path.join(pepperdir, branch_name), 149 targets) 150 151 152 def BuildProjectsBranch(pepperdir, branch, deps, clean, config, args=None): 153 make_dir = os.path.join(pepperdir, branch) 154 print "\nMake: " + make_dir 155 156 if getos.GetPlatform() == 'win': 157 # We need to modify the environment to build host on Windows. 158 make = os.path.join(make_dir, 'make.bat') 159 else: 160 make = 'make' 161 162 env = None 163 if os.environ.get('USE_GOMA') == '1': 164 env = dict(os.environ) 165 env['NACL_COMPILER_PREFIX'] = 'gomacc' 166 # Add -m32 to the CFLAGS when building using i686-nacl-gcc 167 # otherwise goma won't recognise it as different to the x86_64 168 # build. 169 env['X86_32_CFLAGS'] = '-m32' 170 env['X86_32_CXXFLAGS'] = '-m32' 171 jobs = '50' 172 else: 173 jobs = str(multiprocessing.cpu_count()) 174 175 make_cmd = [make, '-j', jobs] 176 177 make_cmd.append('CONFIG='+config) 178 if not deps: 179 make_cmd.append('IGNORE_DEPS=1') 180 181 if verbose: 182 make_cmd.append('V=1') 183 184 if args: 185 make_cmd += args 186 else: 187 make_cmd.append('TOOLCHAIN=all') 188 189 buildbot_common.Run(make_cmd, cwd=make_dir, env=env) 190 if clean: 191 # Clean to remove temporary files but keep the built 192 buildbot_common.Run(make_cmd + ['clean'], cwd=make_dir, env=env) 193 194 195 def BuildProjects(pepperdir, project_tree, deps=True, 196 clean=False, config='Debug'): 197 198 # Make sure we build libraries (which live in 'src') before 199 # any of the examples. 200 build_first = [p for p in project_tree if p != 'src'] 201 build_second = [p for p in project_tree if p == 'src'] 202 203 for branch in build_first + build_second: 204 BuildProjectsBranch(pepperdir, branch, deps, clean, config) 205 206 207 def main(args): 208 parser = optparse.OptionParser() 209 parser.add_option('-c', '--clobber', 210 help='Clobber project directories before copying new files', 211 action='store_true', default=False) 212 parser.add_option('-b', '--build', 213 help='Build the projects.', action='store_true') 214 parser.add_option('--config', 215 help='Choose configuration to build (Debug or Release). Builds both ' 216 'by default') 217 parser.add_option('-x', '--experimental', 218 help='Build experimental projects', action='store_true') 219 parser.add_option('-t', '--toolchain', 220 help='Build using toolchain. Can be passed more than once.', 221 action='append', default=[]) 222 parser.add_option('-d', '--dest', 223 help='Select which build destinations (project types) are valid.', 224 action='append') 225 parser.add_option('-p', '--project', 226 help='Select which projects are valid.', 227 action='append') 228 parser.add_option('-v', '--verbose', action='store_true') 229 230 options, args = parser.parse_args(args[1:]) 231 if args: 232 parser.error('Not expecting any arguments.') 233 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 239 pepper_ver = str(int(build_version.ChromeMajorVersion())) 240 pepperdir = os.path.join(OUT_DIR, 'pepper_' + pepper_ver) 241 242 if not options.toolchain: 243 options.toolchain = ['newlib', 'glibc', 'pnacl', 'host'] 244 245 if 'host' in options.toolchain: 246 options.toolchain.remove('host') 247 options.toolchain.append(getos.GetPlatform()) 248 print 'Adding platform: ' + getos.GetPlatform() 249 250 ValidateToolchains(options.toolchain) 251 252 filters = {} 253 if options.toolchain: 254 filters['TOOLS'] = options.toolchain 255 print 'Filter by toolchain: ' + str(options.toolchain) 256 if not options.experimental: 257 filters['EXPERIMENTAL'] = False 258 if options.dest: 259 filters['DEST'] = options.dest 260 print 'Filter by type: ' + str(options.dest) 261 if options.project: 262 filters['NAME'] = options.project 263 print 'Filter by name: ' + str(options.project) 264 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 parse_dsc.PrintProjectTree(project_tree) 270 271 UpdateHelpers(pepperdir, clobber=options.clobber) 272 UpdateProjects(pepperdir, project_tree, options.toolchain, 273 clobber=options.clobber) 274 275 if options.verbose: 276 global verbose 277 verbose = True 278 279 if options.build: 280 if options.config: 281 configs = [options.config] 282 else: 283 configs = ['Debug', 'Release'] 284 for config in configs: 285 BuildProjects(pepperdir, project_tree, config=config) 286 287 return 0 288 289 290 if __name__ == '__main__': 291 script_name = os.path.basename(sys.argv[0]) 292 try: 293 sys.exit(main(sys.argv)) 294 except parse_dsc.ValidationError as e: 295 buildbot_common.ErrorExit('%s: %s' % (script_name, e)) 296 except KeyboardInterrupt: 297 buildbot_common.ErrorExit('%s: interrupted' % script_name) 298