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