Home | History | Annotate | Download | only in build_tools
      1 # Copyright (c) 2012 Google Inc. 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.
      6 It was originally designed for generating readable Makefiles for the
      7 the NaCL examples.
      8 """
      9 
     10 # pylint: disable=C0301
     11 
     12 import os
     13 import gyp.common
     14 from gyp.common import GetEnvironFallback
     15 from gyp.generator.make import QuoteIfNecessary
     16 
     17 generator_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 
     30 generator_additional_non_configuration_keys = [
     31     'make_valid_configurations',
     32 ]
     33 
     34 
     35 preamble = """\
     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
     43 BUILDTYPE = %(default_config)s
     44 
     45 # All possible build configurations
     46 BUILDTYPES = %(all_configs)s
     47 
     48 # Check for valid build configuration
     49 ifeq (,$(findstring $(BUILDTYPE),$(BUILDTYPES)))
     50 $(warning Possible build configurations are: $(BUILDTYPES))
     51 $(warning Cannot use BUILDTYPE=$(BUILDTYPE) with this Makefile.)
     52 all:
     53 else
     54 
     55 # Target toolchain
     56 CC.target ?= %(CC.target)s
     57 CFLAGS.target ?= $(CFLAGS)
     58 CXX.target ?= %(CXX.target)s
     59 CXXFLAGS.target ?= $(CXXFLAGS)
     60 LINK.target ?= %(LINK.target)s
     61 LDFLAGS.target ?= $(LDFLAGS)
     62 AR.target ?= %(AR.target)s
     63 ARFLAGS.target ?= %(ARFLAGS.target)s
     64 
     65 # Host toolchain
     66 CC.host ?= gcc
     67 CFLAGS.host ?=
     68 CXX.host ?= g++
     69 CXXFLAGS.host ?=
     70 LINK.host ?= g++
     71 LDFLAGS.host ?=
     72 AR.host ?= ar
     73 ARFLAGS.host := %(ARFLAGS.host)s
     74 
     75 BUILDDIR = build
     76 
     77 DEPFLAGS = -MMD
     78 
     79 DEPFILES :=
     80 
     81 .PHONY: all clean
     82 
     83 all:
     84 
     85 clean:
     86 \trm -rf $(BUILDDIR)
     87 """
     88 
     89 none_section = """
     90 TARGET = $(BUILDDIR)/$(BUILDTYPE)/%(target)s.stamp
     91 all: $(TARGET)
     92 
     93 INPUTS = %(inputs)s
     94 
     95 $(TARGET): $(INPUTS)
     96 """
     97 
     98 
     99 target_section = """
    100 TARGET = %(product)s
    101 all: $(TARGET)
    102 
    103 SOURCES = %(sources)s
    104 
    105 LIBS_%(target_name_var)s_$(BUILDTYPE) = %(libs)s
    106 
    107 OBJS = %(objs)s
    108 
    109 DEPFILES += $(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 
    122 lib_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 
    129 link_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 
    136 def 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 
    158 def 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 
    163 def 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 
    180 def 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 
    189 def 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 
    262 def 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 
    300 endif
    301 ''')
    302 
    303   makefile.close()
    304