Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
android.py1170 linesDownload Raw Back to generator
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 
codekingpro/portable-devtools · Team Ai