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