1# Copyright (c) 2012 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import json 6import os 7import sys 8 9import buildbot_common 10import build_version 11import getos 12from buildbot_common import ErrorExit 13from easy_template import RunTemplateFileIfChanged 14from build_paths import SDK_RESOURCE_DIR 15 16def Trace(msg): 17 if Trace.verbose: 18 sys.stderr.write(str(msg) + '\n') 19Trace.verbose = False 20 21 22def IsExample(desc): 23 dest = desc['DEST'] 24 return dest.startswith(('examples', 'tests', 'getting_started')) 25 26 27def GenerateSourceCopyList(desc): 28 sources = [] 29 # Some examples use their own Makefile/sources/etc. 30 if 'TARGETS' not in desc: 31 # Only copy the DATA files. 32 return desc.get('DATA', []) 33 34 # Add sources for each target 35 for target in desc['TARGETS']: 36 sources.extend(target['SOURCES']) 37 38 # And HTML and data files 39 sources.extend(desc.get('DATA', [])) 40 41 if IsExample(desc): 42 sources.append('common.js') 43 if not desc.get('NO_PACKAGE_FILES'): 44 sources.extend(['icon128.png', 'background.js']) 45 46 return sources 47 48 49def GetSourcesDict(sources): 50 source_map = {} 51 for key in ['.c', '.cc']: 52 source_list = [fname for fname in sources if fname.endswith(key)] 53 if source_list: 54 source_map[key] = source_list 55 else: 56 source_map[key] = [] 57 return source_map 58 59 60def GetProjectObjects(source_dict): 61 object_list = [] 62 for key in ['.c', '.cc']: 63 for src in source_dict[key]: 64 object_list.append(os.path.splitext(src)[0]) 65 return object_list 66 67 68def GetPlatforms(plat_list, plat_filter, first_toolchain): 69 platforms = [] 70 for plat in plat_list: 71 if plat in plat_filter: 72 platforms.append(plat) 73 74 if first_toolchain: 75 return [platforms[0]] 76 return platforms 77 78 79def ErrorMsgFunc(text): 80 sys.stderr.write(text + '\n') 81 82 83def AddMakeBat(pepperdir, makepath): 84 """Create a simple batch file to execute Make. 85 86 Creates a simple batch file named make.bat for the Windows platform at the 87 given path, pointing to the Make executable in the SDK.""" 88 89 makepath = os.path.abspath(makepath) 90 if not makepath.startswith(pepperdir): 91 ErrorExit('Make.bat not relative to Pepper directory: ' + makepath) 92 93 makeexe = os.path.abspath(os.path.join(pepperdir, 'tools')) 94 relpath = os.path.relpath(makeexe, makepath) 95 96 fp = open(os.path.join(makepath, 'make.bat'), 'wb') 97 outpath = os.path.join(relpath, 'make.exe') 98 99 # Since make.bat is only used by Windows, for Windows path style 100 outpath = outpath.replace(os.path.sep, '\\') 101 fp.write('@%s %%*\n' % outpath) 102 fp.close() 103 104 105def FindFile(name, srcroot, srcdirs): 106 checks = [] 107 for srcdir in srcdirs: 108 srcfile = os.path.join(srcroot, srcdir, name) 109 srcfile = os.path.abspath(srcfile) 110 if os.path.exists(srcfile): 111 return srcfile 112 else: 113 checks.append(srcfile) 114 115 ErrorMsgFunc('%s not found in:\n\t%s' % (name, '\n\t'.join(checks))) 116 return None 117 118 119def IsNexe(desc): 120 for target in desc['TARGETS']: 121 if target['TYPE'] == 'main': 122 return True 123 return False 124 125 126def ProcessHTML(srcroot, dstroot, desc, toolchains, configs, first_toolchain): 127 name = desc['NAME'] 128 nmf = desc['TARGETS'][0]['NAME'] 129 outdir = os.path.join(dstroot, desc['DEST'], name) 130 srcpath = os.path.join(srcroot, 'index.html') 131 dstpath = os.path.join(outdir, 'index.html') 132 133 tools = GetPlatforms(toolchains, desc['TOOLS'], first_toolchain) 134 135 path = "{tc}/{config}" 136 replace = { 137 'title': desc['TITLE'], 138 'attrs': 139 'data-name="%s" data-tools="%s" data-configs="%s" data-path="%s"' % ( 140 nmf, ' '.join(tools), ' '.join(configs), path), 141 } 142 RunTemplateFileIfChanged(srcpath, dstpath, replace) 143 144 145def GenerateManifest(srcroot, dstroot, desc): 146 outdir = os.path.join(dstroot, desc['DEST'], desc['NAME']) 147 srcpath = os.path.join(SDK_RESOURCE_DIR, 'manifest.json.template') 148 dstpath = os.path.join(outdir, 'manifest.json') 149 permissions = desc.get('PERMISSIONS', []) 150 socket_permissions = desc.get('SOCKET_PERMISSIONS', []) 151 combined_permissions = list(permissions) 152 if socket_permissions: 153 combined_permissions.append({'socket': socket_permissions}) 154 pretty_permissions = json.dumps(combined_permissions, 155 sort_keys=True, indent=4) 156 replace = { 157 'name': desc['TITLE'], 158 'description': '%s Example' % desc['TITLE'], 159 'key': True, 160 'channel': None, 161 'permissions': pretty_permissions, 162 'multi_platform': desc.get('MULTI_PLATFORM', False), 163 'version': build_version.ChromeVersionNoTrunk(), 164 'min_chrome_version': desc.get('MIN_CHROME_VERSION') 165 } 166 RunTemplateFileIfChanged(srcpath, dstpath, replace) 167 168 169def FindAndCopyFiles(src_files, root, search_dirs, dst_dir): 170 buildbot_common.MakeDir(dst_dir) 171 for src_name in src_files: 172 src_file = FindFile(src_name, root, search_dirs) 173 if not src_file: 174 ErrorExit('Failed to find: ' + src_name) 175 dst_file = os.path.join(dst_dir, src_name) 176 if os.path.exists(dst_file): 177 if os.stat(src_file).st_mtime <= os.stat(dst_file).st_mtime: 178 Trace('Skipping "%s", destination "%s" is newer.' % ( 179 src_file, dst_file)) 180 continue 181 dst_path = os.path.dirname(dst_file) 182 if not os.path.exists(dst_path): 183 buildbot_common.MakeDir(dst_path) 184 buildbot_common.CopyFile(src_file, dst_file) 185 186 187def ModifyDescInPlace(desc): 188 """Perform post-load processing on .dsc file data. 189 190 Currently this consists of: 191 - Add -Wall to CXXFLAGS 192 """ 193 194 for target in desc['TARGETS']: 195 target.setdefault('CXXFLAGS', []) 196 target['CXXFLAGS'].insert(0, '-Wall') 197 198 199def ProcessProject(pepperdir, srcroot, dstroot, desc, toolchains, configs=None, 200 first_toolchain=False): 201 if not configs: 202 configs = ['Debug', 'Release'] 203 204 name = desc['NAME'] 205 out_dir = os.path.join(dstroot, desc['DEST'], name) 206 buildbot_common.MakeDir(out_dir) 207 srcdirs = desc.get('SEARCH', ['.', SDK_RESOURCE_DIR]) 208 209 # Copy sources to example directory 210 sources = GenerateSourceCopyList(desc) 211 FindAndCopyFiles(sources, srcroot, srcdirs, out_dir) 212 213 # Copy public headers to the include directory. 214 for headers_set in desc.get('HEADERS', []): 215 headers = headers_set['FILES'] 216 header_out_dir = os.path.join(dstroot, headers_set['DEST']) 217 FindAndCopyFiles(headers, srcroot, srcdirs, header_out_dir) 218 219 make_path = os.path.join(out_dir, 'Makefile') 220 221 outdir = os.path.dirname(os.path.abspath(make_path)) 222 if getos.GetPlatform() == 'win': 223 AddMakeBat(pepperdir, outdir) 224 225 # If this project has no TARGETS, then we don't need to generate anything. 226 if 'TARGETS' not in desc: 227 return (name, desc['DEST']) 228 229 if IsNexe(desc): 230 template = os.path.join(SDK_RESOURCE_DIR, 'Makefile.example.template') 231 else: 232 template = os.path.join(SDK_RESOURCE_DIR, 'Makefile.library.template') 233 234 # Ensure the order of |tools| is the same as toolchains; that way if 235 # first_toolchain is set, it will choose based on the order of |toolchains|. 236 tools = [tool for tool in toolchains if tool in desc['TOOLS']] 237 if first_toolchain: 238 tools = [tools[0]] 239 240 ModifyDescInPlace(desc) 241 242 template_dict = { 243 'desc': desc, 244 'rel_sdk': '/'.join(['..'] * (len(desc['DEST'].split('/')) + 1)), 245 'pre': desc.get('PRE', ''), 246 'post': desc.get('POST', ''), 247 'tools': tools, 248 'sel_ldr': desc.get('SEL_LDR'), 249 'targets': desc['TARGETS'], 250 'multi_platform': desc.get('MULTI_PLATFORM', False), 251 } 252 RunTemplateFileIfChanged(template, make_path, template_dict) 253 254 if IsExample(desc): 255 ProcessHTML(srcroot, dstroot, desc, toolchains, configs, 256 first_toolchain) 257 if not desc.get('NO_PACKAGE_FILES'): 258 GenerateManifest(srcroot, dstroot, desc) 259 260 return (name, desc['DEST']) 261 262 263def GenerateMasterMakefile(pepperdir, out_path, targets, deps): 264 """Generate a Master Makefile that builds all examples. 265 266 Args: 267 pepperdir: NACL_SDK_ROOT 268 out_path: Root for output such that out_path+NAME = full path 269 targets: List of targets names 270 """ 271 in_path = os.path.join(SDK_RESOURCE_DIR, 'Makefile.index.template') 272 out_path = os.path.join(out_path, 'Makefile') 273 rel_path = os.path.relpath(pepperdir, os.path.dirname(out_path)) 274 template_dict = { 275 'projects': targets, 276 'deps' : deps, 277 'rel_sdk' : rel_path, 278 } 279 RunTemplateFileIfChanged(in_path, out_path, template_dict) 280 outdir = os.path.dirname(os.path.abspath(out_path)) 281 if getos.GetPlatform() == 'win': 282 AddMakeBat(pepperdir, outdir) 283