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