| # Copyright (c) 2012 Google Inc. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """ |
| SCons generator. |
| |
| This contains class definitions and supporting functions for generating |
| pieces of SCons files for the different types of GYP targets. |
| """ |
| |
| import os |
| |
| |
| def WriteList(fp, list, prefix='', |
| separator=',\n ', |
| preamble=None, |
| postamble=None): |
| fp.write(preamble or '') |
| fp.write((separator or ' ').join([prefix + l for l in list])) |
| fp.write(postamble or '') |
| |
| |
| class TargetBase(object): |
| """ |
| Base class for a SCons representation of a GYP target. |
| """ |
| is_ignored = False |
| target_prefix = '' |
| target_suffix = '' |
| def __init__(self, spec): |
| self.spec = spec |
| def full_product_name(self): |
| """ |
| Returns the full name of the product being built: |
| |
| * Uses 'product_name' if it's set, else prefix + 'target_name'. |
| * Prepends 'product_dir' if set. |
| * Appends SCons suffix variables for the target type (or |
| product_extension). |
| """ |
| suffix = self.target_suffix |
| product_extension = self.spec.get('product_extension') |
| if product_extension: |
| suffix = '.' + product_extension |
| prefix = self.spec.get('product_prefix', self.target_prefix) |
| name = self.spec['target_name'] |
| name = prefix + self.spec.get('product_name', name) + suffix |
| product_dir = self.spec.get('product_dir') |
| if product_dir: |
| name = os.path.join(product_dir, name) |
| else: |
| name = os.path.join(self.out_dir, name) |
| return name |
| |
| def write_input_files(self, fp): |
| """ |
| Writes the definition of the input files (sources). |
| """ |
| sources = self.spec.get('sources') |
| if not sources: |
| fp.write('\ninput_files = []\n') |
| return |
| preamble = '\ninput_files = [\n ' |
| postamble = ',\n]\n' |
| WriteList(fp, map(repr, sources), preamble=preamble, postamble=postamble) |
| |
| def builder_call(self): |
| """ |
| Returns the actual SCons builder call to build this target. |
| """ |
| name = self.full_product_name() |
| return 'env.%s(env.File(%r), input_files)' % (self.builder_name, name) |
| def write_target(self, fp, src_dir='', pre=''): |
| """ |
| Writes the lines necessary to build this target. |
| """ |
| fp.write('\n' + pre) |
| fp.write('_outputs = %s\n' % self.builder_call()) |
| fp.write('target_files.extend(_outputs)\n') |
| |
| |
| class NoneTarget(TargetBase): |
| """ |
| A GYP target type of 'none', implicitly or explicitly. |
| """ |
| def write_target(self, fp, src_dir='', pre=''): |
| fp.write('\ntarget_files.extend(input_files)\n') |
| |
| |
| class SettingsTarget(TargetBase): |
| """ |
| A GYP target type of 'settings'. |
| """ |
| is_ignored = True |
| |
| |
| compilable_sources_template = """ |
| _result = [] |
| for infile in input_files: |
| if env.compilable(infile): |
| if (type(infile) == type('') |
| and (infile.startswith(%(src_dir)r) |
| or not os.path.isabs(env.subst(infile)))): |
| # Force files below the build directory by replacing all '..' |
| # elements in the path with '__': |
| base, ext = os.path.splitext(os.path.normpath(infile)) |
| base = [d == '..' and '__' or d for d in base.split('/')] |
| base = os.path.join(*base) |
| object = '${OBJ_DIR}/${COMPONENT_NAME}/${TARGET_NAME}/' + base |
| if not infile.startswith(%(src_dir)r): |
| infile = %(src_dir)r + infile |
| infile = env.%(name)s(object, infile)[0] |
| else: |
| infile = env.%(name)s(infile)[0] |
| _result.append(infile) |
| input_files = _result |
| """ |
| |
| class CompilableSourcesTargetBase(TargetBase): |
| """ |
| An abstract base class for targets that compile their source files. |
| |
| We explicitly transform compilable files into object files, |
| even though SCons could infer that for us, because we want |
| to control where the object file ends up. (The implicit rules |
| in SCons always put the object file next to the source file.) |
| """ |
| intermediate_builder_name = None |
| def write_target(self, fp, src_dir='', pre=''): |
| if self.intermediate_builder_name is None: |
| raise NotImplementedError |
| if src_dir and not src_dir.endswith('/'): |
| src_dir += '/' |
| variables = { |
| 'src_dir': src_dir, |
| 'name': self.intermediate_builder_name, |
| } |
| fp.write(compilable_sources_template % variables) |
| super(CompilableSourcesTargetBase, self).write_target(fp) |
| |
| |
| class ProgramTarget(CompilableSourcesTargetBase): |
| """ |
| A GYP target type of 'executable'. |
| """ |
| builder_name = 'GypProgram' |
| intermediate_builder_name = 'StaticObject' |
| target_prefix = '${PROGPREFIX}' |
| target_suffix = '${PROGSUFFIX}' |
| out_dir = '${TOP_BUILDDIR}' |
| |
| |
| class StaticLibraryTarget(CompilableSourcesTargetBase): |
| """ |
| A GYP target type of 'static_library'. |
| """ |
| builder_name = 'GypStaticLibrary' |
| intermediate_builder_name = 'StaticObject' |
| target_prefix = '${LIBPREFIX}' |
| target_suffix = '${LIBSUFFIX}' |
| out_dir = '${LIB_DIR}' |
| |
| |
| class SharedLibraryTarget(CompilableSourcesTargetBase): |
| """ |
| A GYP target type of 'shared_library'. |
| """ |
| builder_name = 'GypSharedLibrary' |
| intermediate_builder_name = 'SharedObject' |
| target_prefix = '${SHLIBPREFIX}' |
| target_suffix = '${SHLIBSUFFIX}' |
| out_dir = '${LIB_DIR}' |
| |
| |
| class LoadableModuleTarget(CompilableSourcesTargetBase): |
| """ |
| A GYP target type of 'loadable_module'. |
| """ |
| builder_name = 'GypLoadableModule' |
| intermediate_builder_name = 'SharedObject' |
| target_prefix = '${SHLIBPREFIX}' |
| target_suffix = '${SHLIBSUFFIX}' |
| out_dir = '${TOP_BUILDDIR}' |
| |
| |
| TargetMap = { |
| None : NoneTarget, |
| 'none' : NoneTarget, |
| 'settings' : SettingsTarget, |
| 'executable' : ProgramTarget, |
| 'static_library' : StaticLibraryTarget, |
| 'shared_library' : SharedLibraryTarget, |
| 'loadable_module' : LoadableModuleTarget, |
| } |
| |
| |
| def Target(spec): |
| return TargetMap[spec.get('type')](spec) |