1# Copyright (c) 2013 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
5"""This is a simplified Makefile generator for single-target gyp files.
6It was originally designed for generating readable Makefiles for the
7the NaCl examples.
8"""
9
10# pylint: disable=C0301
11
12import os
13import gyp.common
14from gyp.common import GetEnvironFallback
15from gyp.generator.make import QuoteIfNecessary
16
17generator_default_variables = {
18  'EXECUTABLE_PREFIX': '',
19  'EXECUTABLE_SUFFIX': '',
20  'STATIC_LIB_PREFIX': 'lib',
21  'SHARED_LIB_PREFIX': 'lib',
22  'STATIC_LIB_SUFFIX': '.a',
23  'INTERMEDIATE_DIR': '$(BUILDDIR)/$(BUILDTYPE)/obj',
24  'SHARED_INTERMEDIATE_DIR': '$(obj)/gen',
25  'PRODUCT_DIR': '$(BUILDDIR)/$(BUILDTYPE)',
26  'CONFIGURATION_NAME': '$(BUILDTYPE)',
27}
28
29
30generator_additional_non_configuration_keys = [
31    'make_valid_configurations',
32]
33
34
35preamble = """\
36#
37# GNU Make based build file.  For details on GNU Make see:
38#   http://www.gnu.org/software/make/manual/make.html
39#
40# This file was generated by gyp (http://code.google.com/p/gyp/)
41
42# Default build configuration
43BUILDTYPE = %(default_config)s
44
45# All possible build configurations
46BUILDTYPES = %(all_configs)s
47
48# Check for valid build configuration
49ifeq (,$(findstring $(BUILDTYPE),$(BUILDTYPES)))
50$(warning Possible build configurations are: $(BUILDTYPES))
51$(warning Cannot use BUILDTYPE=$(BUILDTYPE) with this Makefile.)
52all:
53else
54
55# Target toolchain
56CC.target ?= %(CC.target)s
57CFLAGS.target ?= $(CFLAGS)
58CXX.target ?= %(CXX.target)s
59CXXFLAGS.target ?= $(CXXFLAGS)
60LINK.target ?= %(LINK.target)s
61LDFLAGS.target ?= $(LDFLAGS)
62AR.target ?= %(AR.target)s
63ARFLAGS.target ?= %(ARFLAGS.target)s
64
65# Host toolchain
66CC.host ?= gcc
67CFLAGS.host ?=
68CXX.host ?= g++
69CXXFLAGS.host ?=
70LINK.host ?= g++
71LDFLAGS.host ?=
72AR.host ?= ar
73ARFLAGS.host := %(ARFLAGS.host)s
74
75BUILDDIR = build
76
77DEPFLAGS = -MMD
78
79DEPFILES :=
80
81.PHONY: all clean
82
83all:
84
85clean:
86\trm -rf $(BUILDDIR)
87"""
88
89none_section = """
90TARGET = $(BUILDDIR)/$(BUILDTYPE)/%(target)s.stamp
91all: $(TARGET)
92
93INPUTS = %(inputs)s
94
95$(TARGET): $(INPUTS)
96"""
97
98
99target_section = """
100TARGET = %(product)s
101all: $(TARGET)
102
103SOURCES = %(sources)s
104
105LIBS_%(target_name_var)s_$(BUILDTYPE) = %(libs)s
106
107OBJS = %(objs)s
108
109DEPFILES += $(OBJS:%%.o=%%.d)
110
111# Suffix rules, putting all outputs into build folder.
112$(BUILDDIR)/$(BUILDTYPE)/obj_%(target)s/%%.o: %%.c
113\t@mkdir -p $(dir $@)
114\t$(CC.%(toolset)s) $(CFLAGS_%(target_name_var)s_$(BUILDTYPE)) -c -o $@ $<
115
116$(BUILDDIR)/$(BUILDTYPE)/obj_%(target)s/%%.o: %%.cc
117\t@mkdir -p $(dir $@)
118\t$(CXX.%(toolset)s) $(CXXFLAGS_%(target_name_var)s_$(BUILDTYPE)) -c -o $@ $<
119"""
120
121
122lib_section = """
123$(TARGET): $(OBJS)
124\t@mkdir -p $(dir $@)
125\t$(AR.%(toolset)s) $(ARFLAGS.%(toolset)s) $(ARFLAGS_%(target_name_var)s_$(BUILDTYPE)) $@ $^
126"""
127
128
129link_section = """
130$(TARGET): $(OBJS)
131\t@mkdir -p $(dir $@)
132\t$(LINK.%(toolset)s) $(LDFLAGS_%(target_name_var)s_$(BUILDTYPE)) $(LDFLAGS.%(toolset)s) -o $@ -Wl,--start-group $^ $(LIBS_%(target_name_var)s_$(BUILDTYPE)) -Wl,--end-group
133"""
134
135
136def MakeList(value_list, prefix='', quoter=QuoteIfNecessary, initial_indent=0):
137  """Construct from a list of values a string that can be assigned to a make
138  variable.  This uses line continuations and limits line length to 80 chars.
139  """
140  if not value_list:
141    return ''
142
143  value_list = [quoter(prefix + l) for l in value_list]
144  lines = []
145  line = ' ' * initial_indent
146  for value in value_list:
147    if len(line) + len(value) >= 79:
148      lines.append(line)
149      line = ''
150    elif line:
151      line += ' '
152    line += value
153  lines.append(line)
154  rtn = ' \\\n\t'.join(lines)
155  return rtn.lstrip()
156
157
158def WriteList(makefile, value_list, variable, prefix='', quoter=QuoteIfNecessary):
159  values = MakeList(value_list, prefix, quoter, initial_indent=len(variable)+4)
160  makefile.write("\n%s := %s\n" % (variable, values))
161
162
163def WriteConfig(makefile, name, config, target_type):
164  WriteList(makefile, config.get('defines', []), 'DEFS_%s' % name, '-D')
165  WriteList(makefile, config.get('cflags', []), 'CPPFLAGS_%s' % name)
166  WriteList(makefile, config.get('arflags', []), 'ARFLAGS_%s' % name)
167  ldflags = config.get('ldflags', [])
168  if target_type == 'shared_library':
169    ldflags.insert(0, '-shared')
170  WriteList(makefile, ldflags, 'LDFLAGS_%s' % name)
171
172  include_dirs = config.get('include_dirs', [])
173  include_dirs = ["-I%s" % i for i in include_dirs]
174  common_flags = ['$(CPPFLAGS_%s)' % name, '$(DEFS_%s)' % name, '$(DEPFLAGS)']
175  common_flags += include_dirs
176  WriteList(makefile, common_flags + config.get('cflags_c', []), 'CFLAGS_%s' % name)
177  WriteList(makefile, common_flags + config.get('cflags_cc', []), 'CXXFLAGS_%s' % name)
178
179
180def WriteActions(makefile, actions, target_type):
181  for action in actions:
182    cmd = gyp.common.EncodePOSIXShellList(action['action'])
183    makefile.write("\t%s\n" % cmd)
184  if target_type == 'none':
185    makefile.write("\ttouch $@\n")
186  makefile.write("\n")
187
188
189def WriteTarget(makefile, target_info):
190  valid_conf = ' '.join(target_info.get('make_valid_configurations', []))
191  if valid_conf:
192    makefile.write("\nifneq (,$(findstring $(BUILDTYPE),%s))\n" % valid_conf)
193
194  makefile.write('''
195##
196# Settings for the '%(target_name)s'
197##
198''' % target_info)
199
200  sources = target_info.get('sources', [])
201  exts = ['.cc', '.c', '.cxx', '.cpp']
202  sources = [s for s in sources if os.path.splitext(s)[1] in exts]
203  objects = [os.path.splitext(src)[0] for src in sources]
204  objects = [obj + '.o' for obj in objects]
205
206  target_name_var = target_info['target_name']
207  target_name_var = target_name_var.replace('.', '_')
208
209  for name, config in target_info['configurations'].items():
210    name = target_name_var + '_' + name
211    WriteConfig(makefile, name, config, target_info['type'])
212
213  actions = target_info.get('actions', [])
214
215  params = {
216    'target': target_info['target_name'],
217    'product': target_info['target_name'],
218    'target_name_var': target_name_var,
219  }
220
221  if 'product_name' in target_info:
222    params['product'] = target_info['product_name']
223
224  if target_info['type'] == 'static_library':
225    prefix = 'lib'
226  elif target_info['type'] == 'shared_library':
227    prefix = 'lib'
228  else:
229    prefix = ''
230
231  if prefix and not params['product'].startswith(prefix):
232    params['product'] = prefix + params['product']
233
234  dirname = target_info.get('product_dir', '$(BUILDDIR)/$(BUILDTYPE)')
235  params['product'] = os.path.join(dirname, params['product'])
236
237  if target_info['type'] == 'none':
238    params.update({
239      'inputs': MakeList(actions[0]['inputs'])
240    })
241    makefile.write(none_section % params)
242  else:
243    builddir = '$(BUILDDIR)/$(BUILDTYPE)/obj_%s' % target_info['target_name']
244    params.update({
245      'sources': MakeList(sources),
246      'libs': MakeList(target_info['libraries']),
247      'objs': MakeList(["%s/%s" % (builddir, obj) for obj in objects]),
248      'toolset': target_info['toolset']
249    })
250
251    makefile.write(target_section % params)
252    if target_info['type'] == 'static_library':
253      makefile.write(lib_section % params)
254    else:
255      makefile.write(link_section % params)
256
257  WriteActions(makefile, actions, target_info['type'])
258  if valid_conf:
259    makefile.write('endif\n')
260
261
262def GenerateOutput(target_list, target_dicts, data, params):
263  """Main entry point for this generator.
264
265  gyp will call this function.
266  """
267  options = params['options']
268  makefilename = os.path.join(options.toplevel_dir, 'Makefile')
269  makefile = open(makefilename, 'w')
270
271  build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
272  make_global_settings = data[build_file].get('make_global_settings', [])
273  settings_map = dict((key, value) for key, value in make_global_settings)
274
275  target_info = target_dicts[target_list[0]]
276
277  params = {
278    'CC.target': GetEnvironFallback(['CC_target'], '$(CC)'),
279    'AR.target': GetEnvironFallback(['AR_target'], '$(AR)'),
280    'ARFLAGS.target': GetEnvironFallback(['ARFLAGS_target'], 'crs'),
281    'CXX.target': GetEnvironFallback(['CXX_target'], '$(CXX)'),
282    'LINK.target': GetEnvironFallback(['LINK_target'], '$(LINK)') ,
283
284    'ARFLAGS.host': GetEnvironFallback(['ARFLAGS_host'], 'crs'),
285
286    'default_config': target_info['default_configuration'],
287    'all_configs': ' '.join(target_info['configurations'].keys()),
288  }
289
290  params.update(settings_map)
291  makefile.write(preamble % params)
292
293  for target_info in target_dicts.values():
294    WriteTarget(makefile, target_info)
295
296  makefile.write('''
297# include (if they exists) the .d dependency files that the compiler generates
298-include $(DEPFILES)
299
300endif
301''')
302
303  makefile.close()
304