# Copyright 2013 The Chromium Authors
# 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()