| # Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """This is a simplified Makefile generator for single-target gyp files. |
| It was originally designed for generating readable Makefiles for the |
| the NaCl examples. |
| """ |
| |
| # pylint: disable=C0301 |
| |
| import os |
| import gyp.common |
| from gyp.common import GetEnvironFallback |
| from gyp.generator.make import QuoteIfNecessary |
| |
| generator_default_variables = { |
| 'EXECUTABLE_PREFIX': '', |
| 'EXECUTABLE_SUFFIX': '', |
| 'STATIC_LIB_PREFIX': 'lib', |
| 'SHARED_LIB_PREFIX': 'lib', |
| 'STATIC_LIB_SUFFIX': '.a', |
| 'INTERMEDIATE_DIR': '$(BUILDDIR)/$(BUILDTYPE)/obj', |
| 'SHARED_INTERMEDIATE_DIR': '$(obj)/gen', |
| 'PRODUCT_DIR': '$(BUILDDIR)/$(BUILDTYPE)', |
| 'CONFIGURATION_NAME': '$(BUILDTYPE)', |
| } |
| |
| |
| generator_additional_non_configuration_keys = [ |
| 'make_valid_configurations', |
| ] |
| |
| |
| preamble = """\ |
| # |
| # GNU Make based build file. For details on GNU Make see: |
| # http://www.gnu.org/software/make/manual/make.html |
| # |
| # This file was generated by gyp (http://code.google.com/p/gyp/) |
| |
| # Default build configuration |
| BUILDTYPE = %(default_config)s |
| |
| # All possible build configurations |
| BUILDTYPES = %(all_configs)s |
| |
| # Check for valid build configuration |
| ifeq (,$(findstring $(BUILDTYPE),$(BUILDTYPES))) |
| $(warning Possible build configurations are: $(BUILDTYPES)) |
| $(warning Cannot use BUILDTYPE=$(BUILDTYPE) with this Makefile.) |
| all: |
| else |
| |
| # Target toolchain |
| CC.target ?= %(CC.target)s |
| CFLAGS.target ?= $(CFLAGS) |
| CXX.target ?= %(CXX.target)s |
| CXXFLAGS.target ?= $(CXXFLAGS) |
| LINK.target ?= %(LINK.target)s |
| LDFLAGS.target ?= $(LDFLAGS) |
| AR.target ?= %(AR.target)s |
| ARFLAGS.target ?= %(ARFLAGS.target)s |
| |
| # Host toolchain |
| CC.host ?= gcc |
| CFLAGS.host ?= |
| CXX.host ?= g++ |
| CXXFLAGS.host ?= |
| LINK.host ?= g++ |
| LDFLAGS.host ?= |
| AR.host ?= ar |
| ARFLAGS.host := %(ARFLAGS.host)s |
| |
| BUILDDIR = build |
| |
| DEPFLAGS = -MMD |
| |
| DEPFILES := |
| |
| .PHONY: all clean |
| |
| all: |
| |
| clean: |
| \trm -rf $(BUILDDIR) |
| """ |
| |
| none_section = """ |
| TARGET = $(BUILDDIR)/$(BUILDTYPE)/%(target)s.stamp |
| all: $(TARGET) |
| |
| INPUTS = %(inputs)s |
| |
| $(TARGET): $(INPUTS) |
| """ |
| |
| |
| target_section = """ |
| TARGET = %(product)s |
| all: $(TARGET) |
| |
| SOURCES = %(sources)s |
| |
| LIBS_%(target_name_var)s_$(BUILDTYPE) = %(libs)s |
| |
| OBJS = %(objs)s |
| |
| DEPFILES += $(OBJS:%%.o=%%.d) |
| |
| # Suffix rules, putting all outputs into build folder. |
| $(BUILDDIR)/$(BUILDTYPE)/obj_%(target)s/%%.o: %%.c |
| \t@mkdir -p $(dir $@) |
| \t$(CC.%(toolset)s) $(CFLAGS_%(target_name_var)s_$(BUILDTYPE)) -c -o $@ $< |
| |
| $(BUILDDIR)/$(BUILDTYPE)/obj_%(target)s/%%.o: %%.cc |
| \t@mkdir -p $(dir $@) |
| \t$(CXX.%(toolset)s) $(CXXFLAGS_%(target_name_var)s_$(BUILDTYPE)) -c -o $@ $< |
| """ |
| |
| |
| lib_section = """ |
| $(TARGET): $(OBJS) |
| \t@mkdir -p $(dir $@) |
| \t$(AR.%(toolset)s) $(ARFLAGS.%(toolset)s) $(ARFLAGS_%(target_name_var)s_$(BUILDTYPE)) $@ $^ |
| """ |
| |
| |
| link_section = """ |
| $(TARGET): $(OBJS) |
| \t@mkdir -p $(dir $@) |
| \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 |
| """ |
| |
| |
| def MakeList(value_list, prefix='', quoter=QuoteIfNecessary, initial_indent=0): |
| """Construct from a list of values a string that can be assigned to a make |
| variable. This uses line continuations and limits line length to 80 chars. |
| """ |
| if not value_list: |
| return '' |
| |
| value_list = [quoter(prefix + l) for l in value_list] |
| lines = [] |
| line = ' ' * initial_indent |
| for value in value_list: |
| if len(line) + len(value) >= 79: |
| lines.append(line) |
| line = '' |
| elif line: |
| line += ' ' |
| line += value |
| lines.append(line) |
| rtn = ' \\\n\t'.join(lines) |
| return rtn.lstrip() |
| |
| |
| def WriteList(makefile, value_list, variable, prefix='', quoter=QuoteIfNecessary): |
| values = MakeList(value_list, prefix, quoter, initial_indent=len(variable)+4) |
| makefile.write("\n%s := %s\n" % (variable, values)) |
| |
| |
| def WriteConfig(makefile, name, config, target_type): |
| WriteList(makefile, config.get('defines', []), 'DEFS_%s' % name, '-D') |
| WriteList(makefile, config.get('cflags', []), 'CPPFLAGS_%s' % name) |
| WriteList(makefile, config.get('arflags', []), 'ARFLAGS_%s' % name) |
| ldflags = config.get('ldflags', []) |
| if target_type == 'shared_library': |
| ldflags.insert(0, '-shared') |
| WriteList(makefile, ldflags, 'LDFLAGS_%s' % name) |
| |
| include_dirs = config.get('include_dirs', []) |
| include_dirs = ["-I%s" % i for i in include_dirs] |
| common_flags = ['$(CPPFLAGS_%s)' % name, '$(DEFS_%s)' % name, '$(DEPFLAGS)'] |
| common_flags += include_dirs |
| WriteList(makefile, common_flags + config.get('cflags_c', []), 'CFLAGS_%s' % name) |
| WriteList(makefile, common_flags + config.get('cflags_cc', []), 'CXXFLAGS_%s' % name) |
| |
| |
| def WriteActions(makefile, actions, target_type): |
| for action in actions: |
| cmd = gyp.common.EncodePOSIXShellList(action['action']) |
| makefile.write("\t%s\n" % cmd) |
| if target_type == 'none': |
| makefile.write("\ttouch $@\n") |
| makefile.write("\n") |
| |
| |
| def WriteTarget(makefile, target_info): |
| valid_conf = ' '.join(target_info.get('make_valid_configurations', [])) |
| if valid_conf: |
| makefile.write("\nifneq (,$(findstring $(BUILDTYPE),%s))\n" % valid_conf) |
| |
| makefile.write(''' |
| ## |
| # Settings for the '%(target_name)s' |
| ## |
| ''' % target_info) |
| |
| sources = target_info.get('sources', []) |
| exts = ['.cc', '.c', '.cxx', '.cpp'] |
| sources = [s for s in sources if os.path.splitext(s)[1] in exts] |
| objects = [os.path.splitext(src)[0] for src in sources] |
| objects = [obj + '.o' for obj in objects] |
| |
| target_name_var = target_info['target_name'] |
| target_name_var = target_name_var.replace('.', '_') |
| |
| for name, config in target_info['configurations'].items(): |
| name = target_name_var + '_' + name |
| WriteConfig(makefile, name, config, target_info['type']) |
| |
| actions = target_info.get('actions', []) |
| |
| params = { |
| 'target': target_info['target_name'], |
| 'product': target_info['target_name'], |
| 'target_name_var': target_name_var, |
| } |
| |
| if 'product_name' in target_info: |
| params['product'] = target_info['product_name'] |
| |
| if target_info['type'] == 'static_library': |
| prefix = 'lib' |
| elif target_info['type'] == 'shared_library': |
| prefix = 'lib' |
| else: |
| prefix = '' |
| |
| if prefix and not params['product'].startswith(prefix): |
| params['product'] = prefix + params['product'] |
| |
| dirname = target_info.get('product_dir', '$(BUILDDIR)/$(BUILDTYPE)') |
| params['product'] = os.path.join(dirname, params['product']) |
| |
| if target_info['type'] == 'none': |
| params.update({ |
| 'inputs': MakeList(actions[0]['inputs']) |
| }) |
| makefile.write(none_section % params) |
| else: |
| builddir = '$(BUILDDIR)/$(BUILDTYPE)/obj_%s' % target_info['target_name'] |
| params.update({ |
| 'sources': MakeList(sources), |
| 'libs': MakeList(target_info['libraries']), |
| 'objs': MakeList(["%s/%s" % (builddir, obj) for obj in objects]), |
| 'toolset': target_info['toolset'] |
| }) |
| |
| makefile.write(target_section % params) |
| if target_info['type'] == 'static_library': |
| makefile.write(lib_section % params) |
| else: |
| makefile.write(link_section % params) |
| |
| WriteActions(makefile, actions, target_info['type']) |
| if valid_conf: |
| makefile.write('endif\n') |
| |
| |
| def GenerateOutput(target_list, target_dicts, data, params): |
| """Main entry point for this generator. |
| |
| gyp will call this function. |
| """ |
| options = params['options'] |
| makefilename = os.path.join(options.toplevel_dir, 'Makefile') |
| makefile = open(makefilename, 'w') |
| |
| build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0]) |
| make_global_settings = data[build_file].get('make_global_settings', []) |
| settings_map = dict((key, value) for key, value in make_global_settings) |
| |
| target_info = target_dicts[target_list[0]] |
| |
| params = { |
| 'CC.target': GetEnvironFallback(['CC_target'], '$(CC)'), |
| 'AR.target': GetEnvironFallback(['AR_target'], '$(AR)'), |
| 'ARFLAGS.target': GetEnvironFallback(['ARFLAGS_target'], 'crs'), |
| 'CXX.target': GetEnvironFallback(['CXX_target'], '$(CXX)'), |
| 'LINK.target': GetEnvironFallback(['LINK_target'], '$(LINK)') , |
| |
| 'ARFLAGS.host': GetEnvironFallback(['ARFLAGS_host'], 'crs'), |
| |
| 'default_config': target_info['default_configuration'], |
| 'all_configs': ' '.join(target_info['configurations'].keys()), |
| } |
| |
| params.update(settings_map) |
| makefile.write(preamble % params) |
| |
| for target_info in target_dicts.values(): |
| WriteTarget(makefile, target_info) |
| |
| makefile.write(''' |
| # include (if they exists) the .d dependency files that the compiler generates |
| -include $(DEPFILES) |
| |
| endif |
| ''') |
| |
| makefile.close() |