| OLD | NEW |
| (Empty) |
| 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 # Notes: | |
| 6 # | |
| 7 # This generates makefiles suitable for inclusion into the Android build system | |
| 8 # via an Android.mk file. It is based on make.py, the standard makefile | |
| 9 # generator. | |
| 10 # | |
| 11 # The code below generates a separate .mk file for each target, but | |
| 12 # all are sourced by the top-level GypAndroid.mk. This means that all | |
| 13 # variables in .mk-files clobber one another, and furthermore that any | |
| 14 # variables set potentially clash with other Android build system variables. | |
| 15 # Try to avoid setting global variables where possible. | |
| 16 | |
| 17 import gyp | |
| 18 import gyp.common | |
| 19 import gyp.generator.make as make # Reuse global functions from make backend. | |
| 20 import os | |
| 21 import re | |
| 22 import subprocess | |
| 23 | |
| 24 generator_default_variables = { | |
| 25 'OS': 'android', | |
| 26 'EXECUTABLE_PREFIX': '', | |
| 27 'EXECUTABLE_SUFFIX': '', | |
| 28 'STATIC_LIB_PREFIX': 'lib', | |
| 29 'SHARED_LIB_PREFIX': 'lib', | |
| 30 'STATIC_LIB_SUFFIX': '.a', | |
| 31 'SHARED_LIB_SUFFIX': '.so', | |
| 32 'INTERMEDIATE_DIR': '$(gyp_intermediate_dir)', | |
| 33 'SHARED_INTERMEDIATE_DIR': '$(gyp_shared_intermediate_dir)', | |
| 34 'PRODUCT_DIR': '$(gyp_shared_intermediate_dir)', | |
| 35 'SHARED_LIB_DIR': '$(builddir)/lib.$(TOOLSET)', | |
| 36 'LIB_DIR': '$(obj).$(TOOLSET)', | |
| 37 'RULE_INPUT_ROOT': '%(INPUT_ROOT)s', # This gets expanded by Python. | |
| 38 'RULE_INPUT_DIRNAME': '%(INPUT_DIRNAME)s', # This gets expanded by Python. | |
| 39 'RULE_INPUT_PATH': '$(RULE_SOURCES)', | |
| 40 'RULE_INPUT_EXT': '$(suffix $<)', | |
| 41 'RULE_INPUT_NAME': '$(notdir $<)', | |
| 42 'CONFIGURATION_NAME': '$(GYP_CONFIGURATION)', | |
| 43 } | |
| 44 | |
| 45 # Make supports multiple toolsets | |
| 46 generator_supports_multiple_toolsets = True | |
| 47 | |
| 48 | |
| 49 # Generator-specific gyp specs. | |
| 50 generator_additional_non_configuration_keys = [ | |
| 51 # Boolean to declare that this target does not want its name mangled. | |
| 52 'android_unmangled_name', | |
| 53 # Map of android build system variables to set. | |
| 54 'aosp_build_settings', | |
| 55 ] | |
| 56 generator_additional_path_sections = [] | |
| 57 generator_extra_sources_for_rules = [] | |
| 58 | |
| 59 | |
| 60 ALL_MODULES_FOOTER = """\ | |
| 61 # "gyp_all_modules" is a concatenation of the "gyp_all_modules" targets from | |
| 62 # all the included sub-makefiles. This is just here to clarify. | |
| 63 gyp_all_modules: | |
| 64 """ | |
| 65 | |
| 66 header = """\ | |
| 67 # This file is generated by gyp; do not edit. | |
| 68 | |
| 69 """ | |
| 70 | |
| 71 # Map gyp target types to Android module classes. | |
| 72 MODULE_CLASSES = { | |
| 73 'static_library': 'STATIC_LIBRARIES', | |
| 74 'shared_library': 'SHARED_LIBRARIES', | |
| 75 'executable': 'EXECUTABLES', | |
| 76 } | |
| 77 | |
| 78 | |
| 79 def IsCPPExtension(ext): | |
| 80 return make.COMPILABLE_EXTENSIONS.get(ext) == 'cxx' | |
| 81 | |
| 82 | |
| 83 def Sourceify(path): | |
| 84 """Convert a path to its source directory form. The Android backend does not | |
| 85 support options.generator_output, so this function is a noop.""" | |
| 86 return path | |
| 87 | |
| 88 | |
| 89 # Map from qualified target to path to output. | |
| 90 # For Android, the target of these maps is a tuple ('static', 'modulename'), | |
| 91 # ('dynamic', 'modulename'), or ('path', 'some/path') instead of a string, | |
| 92 # since we link by module. | |
| 93 target_outputs = {} | |
| 94 # Map from qualified target to any linkable output. A subset | |
| 95 # of target_outputs. E.g. when mybinary depends on liba, we want to | |
| 96 # include liba in the linker line; when otherbinary depends on | |
| 97 # mybinary, we just want to build mybinary first. | |
| 98 target_link_deps = {} | |
| 99 | |
| 100 | |
| 101 class AndroidMkWriter(object): | |
| 102 """AndroidMkWriter packages up the writing of one target-specific Android.mk. | |
| 103 | |
| 104 Its only real entry point is Write(), and is mostly used for namespacing. | |
| 105 """ | |
| 106 | |
| 107 def __init__(self, android_top_dir): | |
| 108 self.android_top_dir = android_top_dir | |
| 109 | |
| 110 def Write(self, qualified_target, relative_target, base_path, output_filename, | |
| 111 spec, configs, part_of_all, write_alias_target, sdk_version): | |
| 112 """The main entry point: writes a .mk file for a single target. | |
| 113 | |
| 114 Arguments: | |
| 115 qualified_target: target we're generating | |
| 116 relative_target: qualified target name relative to the root | |
| 117 base_path: path relative to source root we're building in, used to resolve | |
| 118 target-relative paths | |
| 119 output_filename: output .mk file name to write | |
| 120 spec, configs: gyp info | |
| 121 part_of_all: flag indicating this target is part of 'all' | |
| 122 write_alias_target: flag indicating whether to create short aliases for | |
| 123 this target | |
| 124 sdk_version: what to emit for LOCAL_SDK_VERSION in output | |
| 125 """ | |
| 126 gyp.common.EnsureDirExists(output_filename) | |
| 127 | |
| 128 self.fp = open(output_filename, 'w') | |
| 129 | |
| 130 self.fp.write(header) | |
| 131 | |
| 132 self.qualified_target = qualified_target | |
| 133 self.relative_target = relative_target | |
| 134 self.path = base_path | |
| 135 self.target = spec['target_name'] | |
| 136 self.type = spec['type'] | |
| 137 self.toolset = spec['toolset'] | |
| 138 | |
| 139 deps, link_deps = self.ComputeDeps(spec) | |
| 140 | |
| 141 # Some of the generation below can add extra output, sources, or | |
| 142 # link dependencies. All of the out params of the functions that | |
| 143 # follow use names like extra_foo. | |
| 144 extra_outputs = [] | |
| 145 extra_sources = [] | |
| 146 | |
| 147 self.android_class = MODULE_CLASSES.get(self.type, 'GYP') | |
| 148 self.android_module = self.ComputeAndroidModule(spec) | |
| 149 (self.android_stem, self.android_suffix) = self.ComputeOutputParts(spec) | |
| 150 self.output = self.output_binary = self.ComputeOutput(spec) | |
| 151 | |
| 152 # Standard header. | |
| 153 self.WriteLn('include $(CLEAR_VARS)\n') | |
| 154 | |
| 155 # Module class and name. | |
| 156 self.WriteLn('LOCAL_MODULE_CLASS := ' + self.android_class) | |
| 157 self.WriteLn('LOCAL_MODULE := ' + self.android_module) | |
| 158 # Only emit LOCAL_MODULE_STEM if it's different to LOCAL_MODULE. | |
| 159 # The library module classes fail if the stem is set. ComputeOutputParts | |
| 160 # makes sure that stem == modulename in these cases. | |
| 161 if self.android_stem != self.android_module: | |
| 162 self.WriteLn('LOCAL_MODULE_STEM := ' + self.android_stem) | |
| 163 self.WriteLn('LOCAL_MODULE_SUFFIX := ' + self.android_suffix) | |
| 164 if self.toolset == 'host': | |
| 165 self.WriteLn('LOCAL_IS_HOST_MODULE := true') | |
| 166 self.WriteLn('LOCAL_MULTILIB := $(GYP_HOST_MULTILIB)') | |
| 167 else: | |
| 168 self.WriteLn('LOCAL_MODULE_TARGET_ARCH := ' | |
| 169 '$(TARGET_$(GYP_VAR_PREFIX)ARCH)') | |
| 170 self.WriteLn('LOCAL_SDK_VERSION := %s' % sdk_version) | |
| 171 | |
| 172 # Grab output directories; needed for Actions and Rules. | |
| 173 if self.toolset == 'host': | |
| 174 self.WriteLn('gyp_intermediate_dir := ' | |
| 175 '$(call local-intermediates-dir,,$(GYP_HOST_VAR_PREFIX))') | |
| 176 else: | |
| 177 self.WriteLn('gyp_intermediate_dir := ' | |
| 178 '$(call local-intermediates-dir,,$(GYP_VAR_PREFIX))') | |
| 179 self.WriteLn('gyp_shared_intermediate_dir := ' | |
| 180 '$(call intermediates-dir-for,GYP,shared,,,$(GYP_VAR_PREFIX))') | |
| 181 self.WriteLn() | |
| 182 | |
| 183 # List files this target depends on so that actions/rules/copies/sources | |
| 184 # can depend on the list. | |
| 185 # TODO: doesn't pull in things through transitive link deps; needed? | |
| 186 target_dependencies = [x[1] for x in deps if x[0] == 'path'] | |
| 187 self.WriteLn('# Make sure our deps are built first.') | |
| 188 self.WriteList(target_dependencies, 'GYP_TARGET_DEPENDENCIES', | |
| 189 local_pathify=True) | |
| 190 | |
| 191 # Actions must come first, since they can generate more OBJs for use below. | |
| 192 if 'actions' in spec: | |
| 193 self.WriteActions(spec['actions'], extra_sources, extra_outputs) | |
| 194 | |
| 195 # Rules must be early like actions. | |
| 196 if 'rules' in spec: | |
| 197 self.WriteRules(spec['rules'], extra_sources, extra_outputs) | |
| 198 | |
| 199 if 'copies' in spec: | |
| 200 self.WriteCopies(spec['copies'], extra_outputs) | |
| 201 | |
| 202 # GYP generated outputs. | |
| 203 self.WriteList(extra_outputs, 'GYP_GENERATED_OUTPUTS', local_pathify=True) | |
| 204 | |
| 205 # Set LOCAL_ADDITIONAL_DEPENDENCIES so that Android's build rules depend | |
| 206 # on both our dependency targets and our generated files. | |
| 207 self.WriteLn('# Make sure our deps and generated files are built first.') | |
| 208 self.WriteLn('LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) ' | |
| 209 '$(GYP_GENERATED_OUTPUTS)') | |
| 210 self.WriteLn() | |
| 211 | |
| 212 # Sources. | |
| 213 if spec.get('sources', []) or extra_sources: | |
| 214 self.WriteSources(spec, configs, extra_sources) | |
| 215 | |
| 216 self.WriteTarget(spec, configs, deps, link_deps, part_of_all, | |
| 217 write_alias_target) | |
| 218 | |
| 219 # Update global list of target outputs, used in dependency tracking. | |
| 220 target_outputs[qualified_target] = ('path', self.output_binary) | |
| 221 | |
| 222 # Update global list of link dependencies. | |
| 223 if self.type == 'static_library': | |
| 224 target_link_deps[qualified_target] = ('static', self.android_module) | |
| 225 elif self.type == 'shared_library': | |
| 226 target_link_deps[qualified_target] = ('shared', self.android_module) | |
| 227 | |
| 228 self.fp.close() | |
| 229 return self.android_module | |
| 230 | |
| 231 | |
| 232 def WriteActions(self, actions, extra_sources, extra_outputs): | |
| 233 """Write Makefile code for any 'actions' from the gyp input. | |
| 234 | |
| 235 extra_sources: a list that will be filled in with newly generated source | |
| 236 files, if any | |
| 237 extra_outputs: a list that will be filled in with any outputs of these | |
| 238 actions (used to make other pieces dependent on these | |
| 239 actions) | |
| 240 """ | |
| 241 for action in actions: | |
| 242 name = make.StringToMakefileVariable('%s_%s' % (self.relative_target, | |
| 243 action['action_name'])) | |
| 244 self.WriteLn('### Rules for action "%s":' % action['action_name']) | |
| 245 inputs = action['inputs'] | |
| 246 outputs = action['outputs'] | |
| 247 | |
| 248 # Build up a list of outputs. | |
| 249 # Collect the output dirs we'll need. | |
| 250 dirs = set() | |
| 251 for out in outputs: | |
| 252 if not out.startswith('$'): | |
| 253 print ('WARNING: Action for target "%s" writes output to local path ' | |
| 254 '"%s".' % (self.target, out)) | |
| 255 dir = os.path.split(out)[0] | |
| 256 if dir: | |
| 257 dirs.add(dir) | |
| 258 if int(action.get('process_outputs_as_sources', False)): | |
| 259 extra_sources += outputs | |
| 260 | |
| 261 # Prepare the actual command. | |
| 262 command = gyp.common.EncodePOSIXShellList(action['action']) | |
| 263 if 'message' in action: | |
| 264 quiet_cmd = 'Gyp action: %s ($@)' % action['message'] | |
| 265 else: | |
| 266 quiet_cmd = 'Gyp action: %s ($@)' % name | |
| 267 if len(dirs) > 0: | |
| 268 command = 'mkdir -p %s' % ' '.join(dirs) + '; ' + command | |
| 269 | |
| 270 cd_action = 'cd $(gyp_local_path)/%s; ' % self.path | |
| 271 command = cd_action + command | |
| 272 | |
| 273 # The makefile rules are all relative to the top dir, but the gyp actions | |
| 274 # are defined relative to their containing dir. This replaces the gyp_* | |
| 275 # variables for the action rule with an absolute version so that the | |
| 276 # output goes in the right place. | |
| 277 # Only write the gyp_* rules for the "primary" output (:1); | |
| 278 # it's superfluous for the "extra outputs", and this avoids accidentally | |
| 279 # writing duplicate dummy rules for those outputs. | |
| 280 main_output = make.QuoteSpaces(self.LocalPathify(outputs[0])) | |
| 281 self.WriteLn('%s: gyp_local_path := $(LOCAL_PATH)' % main_output) | |
| 282 self.WriteLn('%s: gyp_var_prefix := $(GYP_VAR_PREFIX)' % main_output) | |
| 283 self.WriteLn('%s: gyp_intermediate_dir := ' | |
| 284 '$(abspath $(gyp_intermediate_dir))' % main_output) | |
| 285 self.WriteLn('%s: gyp_shared_intermediate_dir := ' | |
| 286 '$(abspath $(gyp_shared_intermediate_dir))' % main_output) | |
| 287 | |
| 288 # Android's envsetup.sh adds a number of directories to the path including | |
| 289 # the built host binary directory. This causes actions/rules invoked by | |
| 290 # gyp to sometimes use these instead of system versions, e.g. bison. | |
| 291 # The built host binaries may not be suitable, and can cause errors. | |
| 292 # So, we remove them from the PATH using the ANDROID_BUILD_PATHS variable | |
| 293 # set by envsetup. | |
| 294 self.WriteLn('%s: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))' | |
| 295 % main_output) | |
| 296 | |
| 297 # Don't allow spaces in input/output filenames, but make an exception for | |
| 298 # filenames which start with '$(' since it's okay for there to be spaces | |
| 299 # inside of make function/macro invocations. | |
| 300 for input in inputs: | |
| 301 if not input.startswith('$(') and ' ' in input: | |
| 302 raise gyp.common.GypError( | |
| 303 'Action input filename "%s" in target %s contains a space' % | |
| 304 (input, self.target)) | |
| 305 for output in outputs: | |
| 306 if not output.startswith('$(') and ' ' in output: | |
| 307 raise gyp.common.GypError( | |
| 308 'Action output filename "%s" in target %s contains a space' % | |
| 309 (output, self.target)) | |
| 310 | |
| 311 self.WriteLn('%s: %s $(GYP_TARGET_DEPENDENCIES)' % | |
| 312 (main_output, ' '.join(map(self.LocalPathify, inputs)))) | |
| 313 self.WriteLn('\t@echo "%s"' % quiet_cmd) | |
| 314 self.WriteLn('\t$(hide)%s\n' % command) | |
| 315 for output in outputs[1:]: | |
| 316 # Make each output depend on the main output, with an empty command | |
| 317 # to force make to notice that the mtime has changed. | |
| 318 self.WriteLn('%s: %s ;' % (self.LocalPathify(output), main_output)) | |
| 319 | |
| 320 extra_outputs += outputs | |
| 321 self.WriteLn() | |
| 322 | |
| 323 self.WriteLn() | |
| 324 | |
| 325 | |
| 326 def WriteRules(self, rules, extra_sources, extra_outputs): | |
| 327 """Write Makefile code for any 'rules' from the gyp input. | |
| 328 | |
| 329 extra_sources: a list that will be filled in with newly generated source | |
| 330 files, if any | |
| 331 extra_outputs: a list that will be filled in with any outputs of these | |
| 332 rules (used to make other pieces dependent on these rules) | |
| 333 """ | |
| 334 if len(rules) == 0: | |
| 335 return | |
| 336 | |
| 337 for rule in rules: | |
| 338 if len(rule.get('rule_sources', [])) == 0: | |
| 339 continue | |
| 340 name = make.StringToMakefileVariable('%s_%s' % (self.relative_target, | |
| 341 rule['rule_name'])) | |
| 342 self.WriteLn('\n### Generated for rule "%s":' % name) | |
| 343 self.WriteLn('# "%s":' % rule) | |
| 344 | |
| 345 inputs = rule.get('inputs') | |
| 346 for rule_source in rule.get('rule_sources', []): | |
| 347 (rule_source_dirname, rule_source_basename) = os.path.split(rule_source) | |
| 348 (rule_source_root, rule_source_ext) = \ | |
| 349 os.path.splitext(rule_source_basename) | |
| 350 | |
| 351 outputs = [self.ExpandInputRoot(out, rule_source_root, | |
| 352 rule_source_dirname) | |
| 353 for out in rule['outputs']] | |
| 354 | |
| 355 dirs = set() | |
| 356 for out in outputs: | |
| 357 if not out.startswith('$'): | |
| 358 print ('WARNING: Rule for target %s writes output to local path %s' | |
| 359 % (self.target, out)) | |
| 360 dir = os.path.dirname(out) | |
| 361 if dir: | |
| 362 dirs.add(dir) | |
| 363 extra_outputs += outputs | |
| 364 if int(rule.get('process_outputs_as_sources', False)): | |
| 365 extra_sources.extend(outputs) | |
| 366 | |
| 367 components = [] | |
| 368 for component in rule['action']: | |
| 369 component = self.ExpandInputRoot(component, rule_source_root, | |
| 370 rule_source_dirname) | |
| 371 if '$(RULE_SOURCES)' in component: | |
| 372 component = component.replace('$(RULE_SOURCES)', | |
| 373 rule_source) | |
| 374 components.append(component) | |
| 375 | |
| 376 command = gyp.common.EncodePOSIXShellList(components) | |
| 377 cd_action = 'cd $(gyp_local_path)/%s; ' % self.path | |
| 378 command = cd_action + command | |
| 379 if dirs: | |
| 380 command = 'mkdir -p %s' % ' '.join(dirs) + '; ' + command | |
| 381 | |
| 382 # We set up a rule to build the first output, and then set up | |
| 383 # a rule for each additional output to depend on the first. | |
| 384 outputs = map(self.LocalPathify, outputs) | |
| 385 main_output = outputs[0] | |
| 386 self.WriteLn('%s: gyp_local_path := $(LOCAL_PATH)' % main_output) | |
| 387 self.WriteLn('%s: gyp_var_prefix := $(GYP_VAR_PREFIX)' % main_output) | |
| 388 self.WriteLn('%s: gyp_intermediate_dir := ' | |
| 389 '$(abspath $(gyp_intermediate_dir))' % main_output) | |
| 390 self.WriteLn('%s: gyp_shared_intermediate_dir := ' | |
| 391 '$(abspath $(gyp_shared_intermediate_dir))' % main_output) | |
| 392 | |
| 393 # See explanation in WriteActions. | |
| 394 self.WriteLn('%s: export PATH := ' | |
| 395 '$(subst $(ANDROID_BUILD_PATHS),,$(PATH))' % main_output) | |
| 396 | |
| 397 main_output_deps = self.LocalPathify(rule_source) | |
| 398 if inputs: | |
| 399 main_output_deps += ' ' | |
| 400 main_output_deps += ' '.join([self.LocalPathify(f) for f in inputs]) | |
| 401 | |
| 402 self.WriteLn('%s: %s $(GYP_TARGET_DEPENDENCIES)' % | |
| 403 (main_output, main_output_deps)) | |
| 404 self.WriteLn('\t%s\n' % command) | |
| 405 for output in outputs[1:]: | |
| 406 # Make each output depend on the main output, with an empty command | |
| 407 # to force make to notice that the mtime has changed. | |
| 408 self.WriteLn('%s: %s ;' % (output, main_output)) | |
| 409 self.WriteLn() | |
| 410 | |
| 411 self.WriteLn() | |
| 412 | |
| 413 | |
| 414 def WriteCopies(self, copies, extra_outputs): | |
| 415 """Write Makefile code for any 'copies' from the gyp input. | |
| 416 | |
| 417 extra_outputs: a list that will be filled in with any outputs of this action | |
| 418 (used to make other pieces dependent on this action) | |
| 419 """ | |
| 420 self.WriteLn('### Generated for copy rule.') | |
| 421 | |
| 422 variable = make.StringToMakefileVariable(self.relative_target + '_copies') | |
| 423 outputs = [] | |
| 424 for copy in copies: | |
| 425 for path in copy['files']: | |
| 426 # The Android build system does not allow generation of files into the | |
| 427 # source tree. The destination should start with a variable, which will | |
| 428 # typically be $(gyp_intermediate_dir) or | |
| 429 # $(gyp_shared_intermediate_dir). Note that we can't use an assertion | |
| 430 # because some of the gyp tests depend on this. | |
| 431 if not copy['destination'].startswith('$'): | |
| 432 print ('WARNING: Copy rule for target %s writes output to ' | |
| 433 'local path %s' % (self.target, copy['destination'])) | |
| 434 | |
| 435 # LocalPathify() calls normpath, stripping trailing slashes. | |
| 436 path = Sourceify(self.LocalPathify(path)) | |
| 437 filename = os.path.split(path)[1] | |
| 438 output = Sourceify(self.LocalPathify(os.path.join(copy['destination'], | |
| 439 filename))) | |
| 440 | |
| 441 self.WriteLn('%s: %s $(GYP_TARGET_DEPENDENCIES) | $(ACP)' % | |
| 442 (output, path)) | |
| 443 self.WriteLn('\t@echo Copying: $@') | |
| 444 self.WriteLn('\t$(hide) mkdir -p $(dir $@)') | |
| 445 self.WriteLn('\t$(hide) $(ACP) -rpf $< $@') | |
| 446 self.WriteLn() | |
| 447 outputs.append(output) | |
| 448 self.WriteLn('%s = %s' % (variable, | |
| 449 ' '.join(map(make.QuoteSpaces, outputs)))) | |
| 450 extra_outputs.append('$(%s)' % variable) | |
| 451 self.WriteLn() | |
| 452 | |
| 453 | |
| 454 def WriteSourceFlags(self, spec, configs): | |
| 455 """Write out the flags and include paths used to compile source files for | |
| 456 the current target. | |
| 457 | |
| 458 Args: | |
| 459 spec, configs: input from gyp. | |
| 460 """ | |
| 461 for configname, config in sorted(configs.iteritems()): | |
| 462 extracted_includes = [] | |
| 463 | |
| 464 self.WriteLn('\n# Flags passed to both C and C++ files.') | |
| 465 cflags, includes_from_cflags = self.ExtractIncludesFromCFlags( | |
| 466 config.get('cflags', []) + config.get('cflags_c', [])) | |
| 467 extracted_includes.extend(includes_from_cflags) | |
| 468 self.WriteList(cflags, 'MY_CFLAGS_%s' % configname) | |
| 469 | |
| 470 self.WriteList(config.get('defines'), 'MY_DEFS_%s' % configname, | |
| 471 prefix='-D', quoter=make.EscapeCppDefine) | |
| 472 | |
| 473 self.WriteLn('\n# Include paths placed before CFLAGS/CPPFLAGS') | |
| 474 includes = list(config.get('include_dirs', [])) | |
| 475 includes.extend(extracted_includes) | |
| 476 includes = map(Sourceify, map(self.LocalPathify, includes)) | |
| 477 includes = self.NormalizeIncludePaths(includes) | |
| 478 self.WriteList(includes, 'LOCAL_C_INCLUDES_%s' % configname) | |
| 479 | |
| 480 self.WriteLn('\n# Flags passed to only C++ (and not C) files.') | |
| 481 self.WriteList(config.get('cflags_cc'), 'LOCAL_CPPFLAGS_%s' % configname) | |
| 482 | |
| 483 self.WriteLn('\nLOCAL_CFLAGS := $(MY_CFLAGS_$(GYP_CONFIGURATION)) ' | |
| 484 '$(MY_DEFS_$(GYP_CONFIGURATION))') | |
| 485 # Undefine ANDROID for host modules | |
| 486 # TODO: the source code should not use macro ANDROID to tell if it's host | |
| 487 # or target module. | |
| 488 if self.toolset == 'host': | |
| 489 self.WriteLn('# Undefine ANDROID for host modules') | |
| 490 self.WriteLn('LOCAL_CFLAGS += -UANDROID') | |
| 491 self.WriteLn('LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) ' | |
| 492 '$(LOCAL_C_INCLUDES_$(GYP_CONFIGURATION))') | |
| 493 self.WriteLn('LOCAL_CPPFLAGS := $(LOCAL_CPPFLAGS_$(GYP_CONFIGURATION))') | |
| 494 # Android uses separate flags for assembly file invocations, but gyp expects | |
| 495 # the same CFLAGS to be applied: | |
| 496 self.WriteLn('LOCAL_ASFLAGS := $(LOCAL_CFLAGS)') | |
| 497 | |
| 498 | |
| 499 def WriteSources(self, spec, configs, extra_sources): | |
| 500 """Write Makefile code for any 'sources' from the gyp input. | |
| 501 These are source files necessary to build the current target. | |
| 502 We need to handle shared_intermediate directory source files as | |
| 503 a special case by copying them to the intermediate directory and | |
| 504 treating them as a genereated sources. Otherwise the Android build | |
| 505 rules won't pick them up. | |
| 506 | |
| 507 Args: | |
| 508 spec, configs: input from gyp. | |
| 509 extra_sources: Sources generated from Actions or Rules. | |
| 510 """ | |
| 511 sources = filter(make.Compilable, spec.get('sources', [])) | |
| 512 generated_not_sources = [x for x in extra_sources if not make.Compilable(x)] | |
| 513 extra_sources = filter(make.Compilable, extra_sources) | |
| 514 | |
| 515 # Determine and output the C++ extension used by these sources. | |
| 516 # We simply find the first C++ file and use that extension. | |
| 517 all_sources = sources + extra_sources | |
| 518 local_cpp_extension = '.cpp' | |
| 519 for source in all_sources: | |
| 520 (root, ext) = os.path.splitext(source) | |
| 521 if IsCPPExtension(ext): | |
| 522 local_cpp_extension = ext | |
| 523 break | |
| 524 if local_cpp_extension != '.cpp': | |
| 525 self.WriteLn('LOCAL_CPP_EXTENSION := %s' % local_cpp_extension) | |
| 526 | |
| 527 # We need to move any non-generated sources that are coming from the | |
| 528 # shared intermediate directory out of LOCAL_SRC_FILES and put them | |
| 529 # into LOCAL_GENERATED_SOURCES. We also need to move over any C++ files | |
| 530 # that don't match our local_cpp_extension, since Android will only | |
| 531 # generate Makefile rules for a single LOCAL_CPP_EXTENSION. | |
| 532 local_files = [] | |
| 533 for source in sources: | |
| 534 (root, ext) = os.path.splitext(source) | |
| 535 if '$(gyp_shared_intermediate_dir)' in source: | |
| 536 extra_sources.append(source) | |
| 537 elif '$(gyp_intermediate_dir)' in source: | |
| 538 extra_sources.append(source) | |
| 539 elif IsCPPExtension(ext) and ext != local_cpp_extension: | |
| 540 extra_sources.append(source) | |
| 541 else: | |
| 542 local_files.append(os.path.normpath(os.path.join(self.path, source))) | |
| 543 | |
| 544 # For any generated source, if it is coming from the shared intermediate | |
| 545 # directory then we add a Make rule to copy them to the local intermediate | |
| 546 # directory first. This is because the Android LOCAL_GENERATED_SOURCES | |
| 547 # must be in the local module intermediate directory for the compile rules | |
| 548 # to work properly. If the file has the wrong C++ extension, then we add | |
| 549 # a rule to copy that to intermediates and use the new version. | |
| 550 final_generated_sources = [] | |
| 551 # If a source file gets copied, we still need to add the orginal source | |
| 552 # directory as header search path, for GCC searches headers in the | |
| 553 # directory that contains the source file by default. | |
| 554 origin_src_dirs = [] | |
| 555 for source in extra_sources: | |
| 556 local_file = source | |
| 557 if not '$(gyp_intermediate_dir)/' in local_file: | |
| 558 basename = os.path.basename(local_file) | |
| 559 local_file = '$(gyp_intermediate_dir)/' + basename | |
| 560 (root, ext) = os.path.splitext(local_file) | |
| 561 if IsCPPExtension(ext) and ext != local_cpp_extension: | |
| 562 local_file = root + local_cpp_extension | |
| 563 if local_file != source: | |
| 564 self.WriteLn('%s: %s' % (local_file, self.LocalPathify(source))) | |
| 565 self.WriteLn('\tmkdir -p $(@D); cp $< $@') | |
| 566 origin_src_dirs.append(os.path.dirname(source)) | |
| 567 final_generated_sources.append(local_file) | |
| 568 | |
| 569 # We add back in all of the non-compilable stuff to make sure that the | |
| 570 # make rules have dependencies on them. | |
| 571 final_generated_sources.extend(generated_not_sources) | |
| 572 self.WriteList(final_generated_sources, 'LOCAL_GENERATED_SOURCES') | |
| 573 | |
| 574 origin_src_dirs = gyp.common.uniquer(origin_src_dirs) | |
| 575 origin_src_dirs = map(Sourceify, map(self.LocalPathify, origin_src_dirs)) | |
| 576 self.WriteList(origin_src_dirs, 'GYP_COPIED_SOURCE_ORIGIN_DIRS') | |
| 577 | |
| 578 self.WriteList(local_files, 'LOCAL_SRC_FILES') | |
| 579 | |
| 580 # Write out the flags used to compile the source; this must be done last | |
| 581 # so that GYP_COPIED_SOURCE_ORIGIN_DIRS can be used as an include path. | |
| 582 self.WriteSourceFlags(spec, configs) | |
| 583 | |
| 584 | |
| 585 def ComputeAndroidModule(self, spec): | |
| 586 """Return the Android module name used for a gyp spec. | |
| 587 | |
| 588 We use the complete qualified target name to avoid collisions between | |
| 589 duplicate targets in different directories. We also add a suffix to | |
| 590 distinguish gyp-generated module names. | |
| 591 """ | |
| 592 | |
| 593 if int(spec.get('android_unmangled_name', 0)): | |
| 594 assert self.type != 'shared_library' or self.target.startswith('lib') | |
| 595 return self.target | |
| 596 | |
| 597 if self.type == 'shared_library': | |
| 598 # For reasons of convention, the Android build system requires that all | |
| 599 # shared library modules are named 'libfoo' when generating -l flags. | |
| 600 prefix = 'lib_' | |
| 601 else: | |
| 602 prefix = '' | |
| 603 | |
| 604 if spec['toolset'] == 'host': | |
| 605 suffix = '_$(TARGET_$(GYP_VAR_PREFIX)ARCH)_host_gyp' | |
| 606 else: | |
| 607 suffix = '_gyp' | |
| 608 | |
| 609 if self.path: | |
| 610 middle = make.StringToMakefileVariable('%s_%s' % (self.path, self.target)) | |
| 611 else: | |
| 612 middle = make.StringToMakefileVariable(self.target) | |
| 613 | |
| 614 return ''.join([prefix, middle, suffix]) | |
| 615 | |
| 616 | |
| 617 def ComputeOutputParts(self, spec): | |
| 618 """Return the 'output basename' of a gyp spec, split into filename + ext. | |
| 619 | |
| 620 Android libraries must be named the same thing as their module name, | |
| 621 otherwise the linker can't find them, so product_name and so on must be | |
| 622 ignored if we are building a library, and the "lib" prepending is | |
| 623 not done for Android. | |
| 624 """ | |
| 625 assert self.type != 'loadable_module' # TODO: not supported? | |
| 626 | |
| 627 target = spec['target_name'] | |
| 628 target_prefix = '' | |
| 629 target_ext = '' | |
| 630 if self.type == 'static_library': | |
| 631 target = self.ComputeAndroidModule(spec) | |
| 632 target_ext = '.a' | |
| 633 elif self.type == 'shared_library': | |
| 634 target = self.ComputeAndroidModule(spec) | |
| 635 target_ext = '.so' | |
| 636 elif self.type == 'none': | |
| 637 target_ext = '.stamp' | |
| 638 elif self.type != 'executable': | |
| 639 print ("ERROR: What output file should be generated?", | |
| 640 "type", self.type, "target", target) | |
| 641 | |
| 642 if self.type != 'static_library' and self.type != 'shared_library': | |
| 643 target_prefix = spec.get('product_prefix', target_prefix) | |
| 644 target = spec.get('product_name', target) | |
| 645 product_ext = spec.get('product_extension') | |
| 646 if product_ext: | |
| 647 target_ext = '.' + product_ext | |
| 648 | |
| 649 target_stem = target_prefix + target | |
| 650 return (target_stem, target_ext) | |
| 651 | |
| 652 | |
| 653 def ComputeOutputBasename(self, spec): | |
| 654 """Return the 'output basename' of a gyp spec. | |
| 655 | |
| 656 E.g., the loadable module 'foobar' in directory 'baz' will produce | |
| 657 'libfoobar.so' | |
| 658 """ | |
| 659 return ''.join(self.ComputeOutputParts(spec)) | |
| 660 | |
| 661 | |
| 662 def ComputeOutput(self, spec): | |
| 663 """Return the 'output' (full output path) of a gyp spec. | |
| 664 | |
| 665 E.g., the loadable module 'foobar' in directory 'baz' will produce | |
| 666 '$(obj)/baz/libfoobar.so' | |
| 667 """ | |
| 668 if self.type == 'executable': | |
| 669 # We install host executables into shared_intermediate_dir so they can be | |
| 670 # run by gyp rules that refer to PRODUCT_DIR. | |
| 671 path = '$(gyp_shared_intermediate_dir)' | |
| 672 elif self.type == 'shared_library': | |
| 673 if self.toolset == 'host': | |
| 674 path = '$($(GYP_HOST_VAR_PREFIX)HOST_OUT_INTERMEDIATE_LIBRARIES)' | |
| 675 else: | |
| 676 path = '$($(GYP_VAR_PREFIX)TARGET_OUT_INTERMEDIATE_LIBRARIES)' | |
| 677 else: | |
| 678 # Other targets just get built into their intermediate dir. | |
| 679 if self.toolset == 'host': | |
| 680 path = ('$(call intermediates-dir-for,%s,%s,true,,' | |
| 681 '$(GYP_HOST_VAR_PREFIX))' % (self.android_class, | |
| 682 self.android_module)) | |
| 683 else: | |
| 684 path = ('$(call intermediates-dir-for,%s,%s,,,$(GYP_VAR_PREFIX))' | |
| 685 % (self.android_class, self.android_module)) | |
| 686 | |
| 687 assert spec.get('product_dir') is None # TODO: not supported? | |
| 688 return os.path.join(path, self.ComputeOutputBasename(spec)) | |
| 689 | |
| 690 def NormalizeIncludePaths(self, include_paths): | |
| 691 """ Normalize include_paths. | |
| 692 Convert absolute paths to relative to the Android top directory. | |
| 693 | |
| 694 Args: | |
| 695 include_paths: A list of unprocessed include paths. | |
| 696 Returns: | |
| 697 A list of normalized include paths. | |
| 698 """ | |
| 699 normalized = [] | |
| 700 for path in include_paths: | |
| 701 if path[0] == '/': | |
| 702 path = gyp.common.RelativePath(path, self.android_top_dir) | |
| 703 normalized.append(path) | |
| 704 return normalized | |
| 705 | |
| 706 def ExtractIncludesFromCFlags(self, cflags): | |
| 707 """Extract includes "-I..." out from cflags | |
| 708 | |
| 709 Args: | |
| 710 cflags: A list of compiler flags, which may be mixed with "-I.." | |
| 711 Returns: | |
| 712 A tuple of lists: (clean_clfags, include_paths). "-I.." is trimmed. | |
| 713 """ | |
| 714 clean_cflags = [] | |
| 715 include_paths = [] | |
| 716 for flag in cflags: | |
| 717 if flag.startswith('-I'): | |
| 718 include_paths.append(flag[2:]) | |
| 719 else: | |
| 720 clean_cflags.append(flag) | |
| 721 | |
| 722 return (clean_cflags, include_paths) | |
| 723 | |
| 724 def FilterLibraries(self, libraries): | |
| 725 """Filter the 'libraries' key to separate things that shouldn't be ldflags. | |
| 726 | |
| 727 Library entries that look like filenames should be converted to android | |
| 728 module names instead of being passed to the linker as flags. | |
| 729 | |
| 730 Args: | |
| 731 libraries: the value of spec.get('libraries') | |
| 732 Returns: | |
| 733 A tuple (static_lib_modules, dynamic_lib_modules, ldflags) | |
| 734 """ | |
| 735 static_lib_modules = [] | |
| 736 dynamic_lib_modules = [] | |
| 737 ldflags = [] | |
| 738 for libs in libraries: | |
| 739 # Libs can have multiple words. | |
| 740 for lib in libs.split(): | |
| 741 # Filter the system libraries, which are added by default by the Android | |
| 742 # build system. | |
| 743 if (lib == '-lc' or lib == '-lstdc++' or lib == '-lm' or | |
| 744 lib.endswith('libgcc.a')): | |
| 745 continue | |
| 746 match = re.search(r'([^/]+)\.a$', lib) | |
| 747 if match: | |
| 748 static_lib_modules.append(match.group(1)) | |
| 749 continue | |
| 750 match = re.search(r'([^/]+)\.so$', lib) | |
| 751 if match: | |
| 752 dynamic_lib_modules.append(match.group(1)) | |
| 753 continue | |
| 754 if lib.startswith('-l'): | |
| 755 ldflags.append(lib) | |
| 756 return (static_lib_modules, dynamic_lib_modules, ldflags) | |
| 757 | |
| 758 | |
| 759 def ComputeDeps(self, spec): | |
| 760 """Compute the dependencies of a gyp spec. | |
| 761 | |
| 762 Returns a tuple (deps, link_deps), where each is a list of | |
| 763 filenames that will need to be put in front of make for either | |
| 764 building (deps) or linking (link_deps). | |
| 765 """ | |
| 766 deps = [] | |
| 767 link_deps = [] | |
| 768 if 'dependencies' in spec: | |
| 769 deps.extend([target_outputs[dep] for dep in spec['dependencies'] | |
| 770 if target_outputs[dep]]) | |
| 771 for dep in spec['dependencies']: | |
| 772 if dep in target_link_deps: | |
| 773 link_deps.append(target_link_deps[dep]) | |
| 774 deps.extend(link_deps) | |
| 775 return (gyp.common.uniquer(deps), gyp.common.uniquer(link_deps)) | |
| 776 | |
| 777 | |
| 778 def WriteTargetFlags(self, spec, configs, link_deps): | |
| 779 """Write Makefile code to specify the link flags and library dependencies. | |
| 780 | |
| 781 spec, configs: input from gyp. | |
| 782 link_deps: link dependency list; see ComputeDeps() | |
| 783 """ | |
| 784 # Libraries (i.e. -lfoo) | |
| 785 # These must be included even for static libraries as some of them provide | |
| 786 # implicit include paths through the build system. | |
| 787 libraries = gyp.common.uniquer(spec.get('libraries', [])) | |
| 788 static_libs, dynamic_libs, ldflags_libs = self.FilterLibraries(libraries) | |
| 789 | |
| 790 if self.type != 'static_library': | |
| 791 for configname, config in sorted(configs.iteritems()): | |
| 792 ldflags = list(config.get('ldflags', [])) | |
| 793 self.WriteLn('') | |
| 794 self.WriteList(ldflags, 'LOCAL_LDFLAGS_%s' % configname) | |
| 795 self.WriteList(ldflags_libs, 'LOCAL_GYP_LIBS') | |
| 796 self.WriteLn('LOCAL_LDFLAGS := $(LOCAL_LDFLAGS_$(GYP_CONFIGURATION)) ' | |
| 797 '$(LOCAL_GYP_LIBS)') | |
| 798 | |
| 799 # Link dependencies (i.e. other gyp targets this target depends on) | |
| 800 # These need not be included for static libraries as within the gyp build | |
| 801 # we do not use the implicit include path mechanism. | |
| 802 if self.type != 'static_library': | |
| 803 static_link_deps = [x[1] for x in link_deps if x[0] == 'static'] | |
| 804 shared_link_deps = [x[1] for x in link_deps if x[0] == 'shared'] | |
| 805 else: | |
| 806 static_link_deps = [] | |
| 807 shared_link_deps = [] | |
| 808 | |
| 809 # Only write the lists if they are non-empty. | |
| 810 if static_libs or static_link_deps: | |
| 811 self.WriteLn('') | |
| 812 self.WriteList(static_libs + static_link_deps, | |
| 813 'LOCAL_STATIC_LIBRARIES') | |
| 814 self.WriteLn('# Enable grouping to fix circular references') | |
| 815 self.WriteLn('LOCAL_GROUP_STATIC_LIBRARIES := true') | |
| 816 if dynamic_libs or shared_link_deps: | |
| 817 self.WriteLn('') | |
| 818 self.WriteList(dynamic_libs + shared_link_deps, | |
| 819 'LOCAL_SHARED_LIBRARIES') | |
| 820 | |
| 821 | |
| 822 def WriteTarget(self, spec, configs, deps, link_deps, part_of_all, | |
| 823 write_alias_target): | |
| 824 """Write Makefile code to produce the final target of the gyp spec. | |
| 825 | |
| 826 spec, configs: input from gyp. | |
| 827 deps, link_deps: dependency lists; see ComputeDeps() | |
| 828 part_of_all: flag indicating this target is part of 'all' | |
| 829 write_alias_target: flag indicating whether to create short aliases for this | |
| 830 target | |
| 831 """ | |
| 832 self.WriteLn('### Rules for final target.') | |
| 833 | |
| 834 if self.type != 'none': | |
| 835 self.WriteTargetFlags(spec, configs, link_deps) | |
| 836 | |
| 837 settings = spec.get('aosp_build_settings', {}) | |
| 838 if settings: | |
| 839 self.WriteLn('### Set directly by aosp_build_settings.') | |
| 840 for k, v in settings.iteritems(): | |
| 841 if isinstance(v, list): | |
| 842 self.WriteList(v, k) | |
| 843 else: | |
| 844 self.WriteLn('%s := %s' % (k, make.QuoteIfNecessary(v))) | |
| 845 self.WriteLn('') | |
| 846 | |
| 847 # Add to the set of targets which represent the gyp 'all' target. We use the | |
| 848 # name 'gyp_all_modules' as the Android build system doesn't allow the use | |
| 849 # of the Make target 'all' and because 'all_modules' is the equivalent of | |
| 850 # the Make target 'all' on Android. | |
| 851 if part_of_all and write_alias_target: | |
| 852 self.WriteLn('# Add target alias to "gyp_all_modules" target.') | |
| 853 self.WriteLn('.PHONY: gyp_all_modules') | |
| 854 self.WriteLn('gyp_all_modules: %s' % self.android_module) | |
| 855 self.WriteLn('') | |
| 856 | |
| 857 # Add an alias from the gyp target name to the Android module name. This | |
| 858 # simplifies manual builds of the target, and is required by the test | |
| 859 # framework. | |
| 860 if self.target != self.android_module and write_alias_target: | |
| 861 self.WriteLn('# Alias gyp target name.') | |
| 862 self.WriteLn('.PHONY: %s' % self.target) | |
| 863 self.WriteLn('%s: %s' % (self.target, self.android_module)) | |
| 864 self.WriteLn('') | |
| 865 | |
| 866 # Add the command to trigger build of the target type depending | |
| 867 # on the toolset. Ex: BUILD_STATIC_LIBRARY vs. BUILD_HOST_STATIC_LIBRARY | |
| 868 # NOTE: This has to come last! | |
| 869 modifier = '' | |
| 870 if self.toolset == 'host': | |
| 871 modifier = 'HOST_' | |
| 872 if self.type == 'static_library': | |
| 873 self.WriteLn('include $(BUILD_%sSTATIC_LIBRARY)' % modifier) | |
| 874 elif self.type == 'shared_library': | |
| 875 self.WriteLn('LOCAL_PRELINK_MODULE := false') | |
| 876 self.WriteLn('include $(BUILD_%sSHARED_LIBRARY)' % modifier) | |
| 877 elif self.type == 'executable': | |
| 878 # Executables are for build and test purposes only, so they're installed | |
| 879 # to a directory that doesn't get included in the system image. | |
| 880 self.WriteLn('LOCAL_MODULE_PATH := $(gyp_shared_intermediate_dir)') | |
| 881 self.WriteLn('include $(BUILD_%sEXECUTABLE)' % modifier) | |
| 882 else: | |
| 883 self.WriteLn('LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp') | |
| 884 self.WriteLn('LOCAL_UNINSTALLABLE_MODULE := true') | |
| 885 if self.toolset == 'target': | |
| 886 self.WriteLn('LOCAL_2ND_ARCH_VAR_PREFIX := $(GYP_VAR_PREFIX)') | |
| 887 else: | |
| 888 self.WriteLn('LOCAL_2ND_ARCH_VAR_PREFIX := $(GYP_HOST_VAR_PREFIX)') | |
| 889 self.WriteLn() | |
| 890 self.WriteLn('include $(BUILD_SYSTEM)/base_rules.mk') | |
| 891 self.WriteLn() | |
| 892 self.WriteLn('$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)') | |
| 893 self.WriteLn('\t$(hide) echo "Gyp timestamp: $@"') | |
| 894 self.WriteLn('\t$(hide) mkdir -p $(dir $@)') | |
| 895 self.WriteLn('\t$(hide) touch $@') | |
| 896 self.WriteLn() | |
| 897 self.WriteLn('LOCAL_2ND_ARCH_VAR_PREFIX :=') | |
| 898 | |
| 899 | |
| 900 def WriteList(self, value_list, variable=None, prefix='', | |
| 901 quoter=make.QuoteIfNecessary, local_pathify=False): | |
| 902 """Write a variable definition that is a list of values. | |
| 903 | |
| 904 E.g. WriteList(['a','b'], 'foo', prefix='blah') writes out | |
| 905 foo = blaha blahb | |
| 906 but in a pretty-printed style. | |
| 907 """ | |
| 908 values = '' | |
| 909 if value_list: | |
| 910 value_list = [quoter(prefix + l) for l in value_list] | |
| 911 if local_pathify: | |
| 912 value_list = [self.LocalPathify(l) for l in value_list] | |
| 913 values = ' \\\n\t' + ' \\\n\t'.join(value_list) | |
| 914 self.fp.write('%s :=%s\n\n' % (variable, values)) | |
| 915 | |
| 916 | |
| 917 def WriteLn(self, text=''): | |
| 918 self.fp.write(text + '\n') | |
| 919 | |
| 920 | |
| 921 def LocalPathify(self, path): | |
| 922 """Convert a subdirectory-relative path into a normalized path which starts | |
| 923 with the make variable $(LOCAL_PATH) (i.e. the top of the project tree). | |
| 924 Absolute paths, or paths that contain variables, are just normalized.""" | |
| 925 if '$(' in path or os.path.isabs(path): | |
| 926 # path is not a file in the project tree in this case, but calling | |
| 927 # normpath is still important for trimming trailing slashes. | |
| 928 return os.path.normpath(path) | |
| 929 local_path = os.path.join('$(LOCAL_PATH)', self.path, path) | |
| 930 local_path = os.path.normpath(local_path) | |
| 931 # Check that normalizing the path didn't ../ itself out of $(LOCAL_PATH) | |
| 932 # - i.e. that the resulting path is still inside the project tree. The | |
| 933 # path may legitimately have ended up containing just $(LOCAL_PATH), though, | |
| 934 # so we don't look for a slash. | |
| 935 assert local_path.startswith('$(LOCAL_PATH)'), ( | |
| 936 'Path %s attempts to escape from gyp path %s !)' % (path, self.path)) | |
| 937 return local_path | |
| 938 | |
| 939 | |
| 940 def ExpandInputRoot(self, template, expansion, dirname): | |
| 941 if '%(INPUT_ROOT)s' not in template and '%(INPUT_DIRNAME)s' not in template: | |
| 942 return template | |
| 943 path = template % { | |
| 944 'INPUT_ROOT': expansion, | |
| 945 'INPUT_DIRNAME': dirname, | |
| 946 } | |
| 947 return os.path.normpath(path) | |
| 948 | |
| 949 | |
| 950 def PerformBuild(data, configurations, params): | |
| 951 # The android backend only supports the default configuration. | |
| 952 options = params['options'] | |
| 953 makefile = os.path.abspath(os.path.join(options.toplevel_dir, | |
| 954 'GypAndroid.mk')) | |
| 955 env = dict(os.environ) | |
| 956 env['ONE_SHOT_MAKEFILE'] = makefile | |
| 957 arguments = ['make', '-C', os.environ['ANDROID_BUILD_TOP'], 'gyp_all_modules'] | |
| 958 print 'Building: %s' % arguments | |
| 959 subprocess.check_call(arguments, env=env) | |
| 960 | |
| 961 | |
| 962 def GenerateOutput(target_list, target_dicts, data, params): | |
| 963 options = params['options'] | |
| 964 generator_flags = params.get('generator_flags', {}) | |
| 965 builddir_name = generator_flags.get('output_dir', 'out') | |
| 966 limit_to_target_all = generator_flags.get('limit_to_target_all', False) | |
| 967 write_alias_targets = generator_flags.get('write_alias_targets', True) | |
| 968 sdk_version = generator_flags.get('aosp_sdk_version', 19) | |
| 969 android_top_dir = os.environ.get('ANDROID_BUILD_TOP') | |
| 970 assert android_top_dir, '$ANDROID_BUILD_TOP not set; you need to run lunch.' | |
| 971 | |
| 972 def CalculateMakefilePath(build_file, base_name): | |
| 973 """Determine where to write a Makefile for a given gyp file.""" | |
| 974 # Paths in gyp files are relative to the .gyp file, but we want | |
| 975 # paths relative to the source root for the master makefile. Grab | |
| 976 # the path of the .gyp file as the base to relativize against. | |
| 977 # E.g. "foo/bar" when we're constructing targets for "foo/bar/baz.gyp". | |
| 978 base_path = gyp.common.RelativePath(os.path.dirname(build_file), | |
| 979 options.depth) | |
| 980 # We write the file in the base_path directory. | |
| 981 output_file = os.path.join(options.depth, base_path, base_name) | |
| 982 assert not options.generator_output, ( | |
| 983 'The Android backend does not support options.generator_output.') | |
| 984 base_path = gyp.common.RelativePath(os.path.dirname(build_file), | |
| 985 options.toplevel_dir) | |
| 986 return base_path, output_file | |
| 987 | |
| 988 # TODO: search for the first non-'Default' target. This can go | |
| 989 # away when we add verification that all targets have the | |
| 990 # necessary configurations. | |
| 991 default_configuration = None | |
| 992 toolsets = set([target_dicts[target]['toolset'] for target in target_list]) | |
| 993 for target in target_list: | |
| 994 spec = target_dicts[target] | |
| 995 if spec['default_configuration'] != 'Default': | |
| 996 default_configuration = spec['default_configuration'] | |
| 997 break | |
| 998 if not default_configuration: | |
| 999 default_configuration = 'Default' | |
| 1000 | |
| 1001 srcdir = '.' | |
| 1002 makefile_name = 'GypAndroid' + options.suffix + '.mk' | |
| 1003 makefile_path = os.path.join(options.toplevel_dir, makefile_name) | |
| 1004 assert not options.generator_output, ( | |
| 1005 'The Android backend does not support options.generator_output.') | |
| 1006 gyp.common.EnsureDirExists(makefile_path) | |
| 1007 root_makefile = open(makefile_path, 'w') | |
| 1008 | |
| 1009 root_makefile.write(header) | |
| 1010 | |
| 1011 # We set LOCAL_PATH just once, here, to the top of the project tree. This | |
| 1012 # allows all the other paths we use to be relative to the Android.mk file, | |
| 1013 # as the Android build system expects. | |
| 1014 root_makefile.write('\nLOCAL_PATH := $(call my-dir)\n') | |
| 1015 | |
| 1016 # Find the list of targets that derive from the gyp file(s) being built. | |
| 1017 needed_targets = set() | |
| 1018 for build_file in params['build_files']: | |
| 1019 for target in gyp.common.AllTargets(target_list, target_dicts, build_file): | |
| 1020 needed_targets.add(target) | |
| 1021 | |
| 1022 build_files = set() | |
| 1023 include_list = set() | |
| 1024 android_modules = {} | |
| 1025 for qualified_target in target_list: | |
| 1026 build_file, target, toolset = gyp.common.ParseQualifiedTarget( | |
| 1027 qualified_target) | |
| 1028 relative_build_file = gyp.common.RelativePath(build_file, | |
| 1029 options.toplevel_dir) | |
| 1030 build_files.add(relative_build_file) | |
| 1031 included_files = data[build_file]['included_files'] | |
| 1032 for included_file in included_files: | |
| 1033 # The included_files entries are relative to the dir of the build file | |
| 1034 # that included them, so we have to undo that and then make them relative | |
| 1035 # to the root dir. | |
| 1036 relative_include_file = gyp.common.RelativePath( | |
| 1037 gyp.common.UnrelativePath(included_file, build_file), | |
| 1038 options.toplevel_dir) | |
| 1039 abs_include_file = os.path.abspath(relative_include_file) | |
| 1040 # If the include file is from the ~/.gyp dir, we should use absolute path | |
| 1041 # so that relocating the src dir doesn't break the path. | |
| 1042 if (params['home_dot_gyp'] and | |
| 1043 abs_include_file.startswith(params['home_dot_gyp'])): | |
| 1044 build_files.add(abs_include_file) | |
| 1045 else: | |
| 1046 build_files.add(relative_include_file) | |
| 1047 | |
| 1048 base_path, output_file = CalculateMakefilePath(build_file, | |
| 1049 target + '.' + toolset + options.suffix + '.mk') | |
| 1050 | |
| 1051 spec = target_dicts[qualified_target] | |
| 1052 configs = spec['configurations'] | |
| 1053 | |
| 1054 part_of_all = qualified_target in needed_targets | |
| 1055 if limit_to_target_all and not part_of_all: | |
| 1056 continue | |
| 1057 | |
| 1058 relative_target = gyp.common.QualifiedTarget(relative_build_file, target, | |
| 1059 toolset) | |
| 1060 writer = AndroidMkWriter(android_top_dir) | |
| 1061 android_module = writer.Write(qualified_target, relative_target, base_path, | |
| 1062 output_file, spec, configs, | |
| 1063 part_of_all=part_of_all, | |
| 1064 write_alias_target=write_alias_targets, | |
| 1065 sdk_version=sdk_version) | |
| 1066 if android_module in android_modules: | |
| 1067 print ('ERROR: Android module names must be unique. The following ' | |
| 1068 'targets both generate Android module name %s.\n %s\n %s' % | |
| 1069 (android_module, android_modules[android_module], | |
| 1070 qualified_target)) | |
| 1071 return | |
| 1072 android_modules[android_module] = qualified_target | |
| 1073 | |
| 1074 # Our root_makefile lives at the source root. Compute the relative path | |
| 1075 # from there to the output_file for including. | |
| 1076 mkfile_rel_path = gyp.common.RelativePath(output_file, | |
| 1077 os.path.dirname(makefile_path)) | |
| 1078 include_list.add(mkfile_rel_path) | |
| 1079 | |
| 1080 root_makefile.write('GYP_CONFIGURATION ?= %s\n' % default_configuration) | |
| 1081 root_makefile.write('GYP_VAR_PREFIX ?=\n') | |
| 1082 root_makefile.write('GYP_HOST_VAR_PREFIX ?=\n') | |
| 1083 root_makefile.write('GYP_HOST_MULTILIB ?=\n') | |
| 1084 | |
| 1085 # Write out the sorted list of includes. | |
| 1086 root_makefile.write('\n') | |
| 1087 for include_file in sorted(include_list): | |
| 1088 root_makefile.write('include $(LOCAL_PATH)/' + include_file + '\n') | |
| 1089 root_makefile.write('\n') | |
| 1090 | |
| 1091 if write_alias_targets: | |
| 1092 root_makefile.write(ALL_MODULES_FOOTER) | |
| 1093 | |
| 1094 root_makefile.close() | |
| OLD | NEW |