generate_make.py revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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      'version': build_version.ChromeVersionNoTrunk()
163  }
164  RunTemplateFileIfChanged(srcpath, dstpath, replace)
165
166
167def FindAndCopyFiles(src_files, root, search_dirs, dst_dir):
168  buildbot_common.MakeDir(dst_dir)
169  for src_name in src_files:
170    src_file = FindFile(src_name, root, search_dirs)
171    if not src_file:
172      ErrorExit('Failed to find: ' + src_name)
173    dst_file = os.path.join(dst_dir, src_name)
174    if os.path.exists(dst_file):
175      if os.stat(src_file).st_mtime <= os.stat(dst_file).st_mtime:
176        Trace('Skipping "%s", destination "%s" is newer.' % (
177            src_file, dst_file))
178        continue
179    dst_path = os.path.dirname(dst_file)
180    if not os.path.exists(dst_path):
181      buildbot_common.MakeDir(dst_path)
182    buildbot_common.CopyFile(src_file, dst_file)
183
184
185def ProcessProject(pepperdir, srcroot, dstroot, desc, toolchains, configs=None,
186                   first_toolchain=False):
187  if not configs:
188    configs = ['Debug', 'Release']
189
190  name = desc['NAME']
191  out_dir = os.path.join(dstroot, desc['DEST'], name)
192  buildbot_common.MakeDir(out_dir)
193  srcdirs = desc.get('SEARCH', ['.', SDK_RESOURCE_DIR])
194
195  # Copy sources to example directory
196  sources = GenerateSourceCopyList(desc)
197  FindAndCopyFiles(sources, srcroot, srcdirs, out_dir)
198
199  # Copy public headers to the include directory.
200  for headers_set in desc.get('HEADERS', []):
201    headers = headers_set['FILES']
202    header_out_dir = os.path.join(dstroot, headers_set['DEST'])
203    FindAndCopyFiles(headers, srcroot, srcdirs, header_out_dir)
204
205  make_path = os.path.join(out_dir, 'Makefile')
206
207  outdir = os.path.dirname(os.path.abspath(make_path))
208  if getos.GetPlatform() == 'win':
209    AddMakeBat(pepperdir, outdir)
210
211  # If this project has no TARGETS, then we don't need to generate anything.
212  if 'TARGETS' not in desc:
213    return (name, desc['DEST'])
214
215  if IsNexe(desc):
216    template = os.path.join(SDK_RESOURCE_DIR, 'Makefile.example.template')
217  else:
218    template = os.path.join(SDK_RESOURCE_DIR, 'Makefile.library.template')
219
220  # Ensure the order of |tools| is the same as toolchains; that way if
221  # first_toolchain is set, it will choose based on the order of |toolchains|.
222  tools = [tool for tool in toolchains if tool in desc['TOOLS']]
223  if first_toolchain:
224    tools = [tools[0]]
225  for target in desc['TARGETS']:
226    target.setdefault('CXXFLAGS', [])
227    target['CXXFLAGS'].insert(0, '-Wall')
228
229  template_dict = {
230    'desc': desc,
231    'rel_sdk': '/'.join(['..'] * (len(desc['DEST'].split('/')) + 1)),
232    'pre': desc.get('PRE', ''),
233    'post': desc.get('POST', ''),
234    'tools': tools,
235    'targets': desc['TARGETS'],
236  }
237  RunTemplateFileIfChanged(template, make_path, template_dict)
238
239  if IsExample(desc):
240    ProcessHTML(srcroot, dstroot, desc, toolchains, configs,
241                first_toolchain)
242    if not desc.get('NO_PACKAGE_FILES'):
243      GenerateManifest(srcroot, dstroot, desc)
244
245  return (name, desc['DEST'])
246
247
248def GenerateMasterMakefile(pepperdir, out_path, targets):
249  """Generate a Master Makefile that builds all examples.
250
251  Args:
252    pepperdir: NACL_SDK_ROOT
253    out_path: Root for output such that out_path+NAME = full path
254    targets: List of targets names
255  """
256  in_path = os.path.join(SDK_RESOURCE_DIR, 'Makefile.index.template')
257  out_path = os.path.join(out_path, 'Makefile')
258  rel_path = os.path.relpath(pepperdir, os.path.dirname(out_path))
259  template_dict = {
260    'projects': targets,
261    'rel_sdk' : rel_path,
262  }
263  RunTemplateFileIfChanged(in_path, out_path, template_dict)
264  outdir = os.path.dirname(os.path.abspath(out_path))
265  if getos.GetPlatform() == 'win':
266    AddMakeBat(pepperdir, outdir)
267