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