1#!/usr/bin/env python 2 3# Copyright (c) 2009 Google Inc. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7""" 8SCons generator. 9 10This contains class definitions and supporting functions for generating 11pieces of SCons files for the different types of GYP targets. 12""" 13 14import os 15 16 17def WriteList(fp, list, prefix='', 18 separator=',\n ', 19 preamble=None, 20 postamble=None): 21 fp.write(preamble or '') 22 fp.write((separator or ' ').join([prefix + l for l in list])) 23 fp.write(postamble or '') 24 25 26class TargetBase(object): 27 """ 28 Base class for a SCons representation of a GYP target. 29 """ 30 is_ignored = False 31 target_prefix = '' 32 target_suffix = '' 33 def __init__(self, spec): 34 self.spec = spec 35 def full_product_name(self): 36 """ 37 Returns the full name of the product being built: 38 39 * Uses 'product_name' if it's set, else prefix + 'target_name'. 40 * Prepends 'product_dir' if set. 41 * Appends SCons suffix variables for the target type (or 42 product_extension). 43 """ 44 suffix = self.target_suffix 45 product_extension = self.spec.get('product_extension') 46 if product_extension: 47 suffix = '.' + product_extension 48 prefix = self.spec.get('product_prefix', self.target_prefix) 49 name = self.spec['target_name'] 50 name = prefix + self.spec.get('product_name', name) + suffix 51 product_dir = self.spec.get('product_dir') 52 if product_dir: 53 name = os.path.join(product_dir, name) 54 else: 55 name = os.path.join(self.out_dir, name) 56 return name 57 58 def write_input_files(self, fp): 59 """ 60 Writes the definition of the input files (sources). 61 """ 62 sources = self.spec.get('sources') 63 if not sources: 64 fp.write('\ninput_files = []\n') 65 return 66 preamble = '\ninput_files = [\n ' 67 postamble = ',\n]\n' 68 WriteList(fp, map(repr, sources), preamble=preamble, postamble=postamble) 69 70 def builder_call(self): 71 """ 72 Returns the actual SCons builder call to build this target. 73 """ 74 name = self.full_product_name() 75 return 'env.%s(env.File(%r), input_files)' % (self.builder_name, name) 76 def write_target(self, fp, src_dir='', pre=''): 77 """ 78 Writes the lines necessary to build this target. 79 """ 80 fp.write('\n' + pre) 81 fp.write('_outputs = %s\n' % self.builder_call()) 82 fp.write('target_files.extend(_outputs)\n') 83 84 85class NoneTarget(TargetBase): 86 """ 87 A GYP target type of 'none', implicitly or explicitly. 88 """ 89 def write_target(self, fp, pre=''): 90 fp.write('\ntarget_files.extend(input_files)\n') 91 92 93class SettingsTarget(TargetBase): 94 """ 95 A GYP target type of 'settings'. 96 """ 97 is_ignored = True 98 99 100compilable_sources_template = """ 101_result = [] 102for infile in input_files: 103 if env.compilable(infile): 104 if (type(infile) == type('') 105 and (infile.startswith(%(src_dir)r) 106 or not os.path.isabs(env.subst(infile)))): 107 # Force files below the build directory by replacing all '..' 108 # elements in the path with '__': 109 base, ext = os.path.splitext(os.path.normpath(infile)) 110 base = [d == '..' and '__' or d for d in base.split('/')] 111 base = os.path.join(*base) 112 object = '${OBJ_DIR}/${COMPONENT_NAME}/${TARGET_NAME}/' + base 113 if not infile.startswith(%(src_dir)r): 114 infile = %(src_dir)r + infile 115 infile = env.%(name)s(object, infile)[0] 116 else: 117 infile = env.%(name)s(infile)[0] 118 _result.append(infile) 119input_files = _result 120""" 121 122class CompilableSourcesTargetBase(TargetBase): 123 """ 124 An abstract base class for targets that compile their source files. 125 126 We explicitly transform compilable files into object files, 127 even though SCons could infer that for us, because we want 128 to control where the object file ends up. (The implicit rules 129 in SCons always put the object file next to the source file.) 130 """ 131 intermediate_builder_name = None 132 def write_target(self, fp, src_dir='', pre=''): 133 if self.intermediate_builder_name is None: 134 raise NotImplementedError 135 if src_dir and not src_dir.endswith('/'): 136 src_dir += '/' 137 variables = { 138 'src_dir': src_dir, 139 'name': self.intermediate_builder_name, 140 } 141 fp.write(compilable_sources_template % variables) 142 super(CompilableSourcesTargetBase, self).write_target(fp) 143 144 145class ProgramTarget(CompilableSourcesTargetBase): 146 """ 147 A GYP target type of 'executable'. 148 """ 149 builder_name = 'GypProgram' 150 intermediate_builder_name = 'StaticObject' 151 target_prefix = '${PROGPREFIX}' 152 target_suffix = '${PROGSUFFIX}' 153 out_dir = '${TOP_BUILDDIR}' 154 155 156class StaticLibraryTarget(CompilableSourcesTargetBase): 157 """ 158 A GYP target type of 'static_library'. 159 """ 160 builder_name = 'GypStaticLibrary' 161 intermediate_builder_name = 'StaticObject' 162 target_prefix = '${LIBPREFIX}' 163 target_suffix = '${LIBSUFFIX}' 164 out_dir = '${LIB_DIR}' 165 166 167class SharedLibraryTarget(CompilableSourcesTargetBase): 168 """ 169 A GYP target type of 'shared_library'. 170 """ 171 builder_name = 'GypSharedLibrary' 172 intermediate_builder_name = 'SharedObject' 173 target_prefix = '${SHLIBPREFIX}' 174 target_suffix = '${SHLIBSUFFIX}' 175 out_dir = '${LIB_DIR}' 176 177 178class LoadableModuleTarget(CompilableSourcesTargetBase): 179 """ 180 A GYP target type of 'loadable_module'. 181 """ 182 builder_name = 'GypLoadableModule' 183 intermediate_builder_name = 'SharedObject' 184 target_prefix = '${SHLIBPREFIX}' 185 target_suffix = '${SHLIBSUFFIX}' 186 out_dir = '${TOP_BUILDDIR}' 187 188 189TargetMap = { 190 None : NoneTarget, 191 'none' : NoneTarget, 192 'settings' : SettingsTarget, 193 'executable' : ProgramTarget, 194 'static_library' : StaticLibraryTarget, 195 'shared_library' : SharedLibraryTarget, 196 'loadable_module' : LoadableModuleTarget, 197} 198 199def Target(spec): 200 return TargetMap[spec.get('type')](spec) 201