• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python3
2#
3# Copyright (C) 2022 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import argparse
18import contextlib
19import json
20import os
21
22
23def _parser():
24    """Return the argument parser"""
25    # Top-level parser
26    parser = argparse.ArgumentParser(prog=".inner_build")
27
28    # --out-dir is always provided.
29    parser.add_argument("--out_dir",
30                        action="store",
31                        required=True,
32                        help="output directory path")
33
34    sub = parser.add_subparsers(required=True,
35                                dest="command",
36                                help="subcommands")
37
38    # inner_build describe command
39    describe = sub.add_parser(
40        "describe",
41        help="describe the capabilities of this inner tree's build system")
42    describe.add_argument('--input_json',
43                          required=True,
44                          help="The json encoded request information.")
45    describe.add_argument('--output_json',
46                          required=True,
47                          help="The json encoded description.")
48
49    # create the parser for the "export_api_contributions" command.
50    export = sub.add_parser(
51        "export_api_contributions",
52        help="export the API contributions of this inner tree")
53    export.add_argument(
54        "--api_domain",
55        action="append",
56        required=True,
57        help="which API domains are to be built in this inner tree")
58    # This is not needed now that we have nsjail, since it launches inner_build
59    # with cwd at the top of the inner tree.
60    export.add_argument("--inner_tree",
61                        action="store",
62                        required=True,
63                        help="path to the inner tree")
64
65    # create the parser for the "analyze" command.
66    analyze = sub.add_parser("analyze",
67                             help="main build analysis for this inner tree")
68    # TODO: do we need this, or does it just need to be mapped in nsjail?
69    analyze.add_argument("--api_surfaces_dir",
70                         action="append",
71                         required=True,
72                         help="the api_surfaces directory path")
73    analyze.add_argument("--generate_ninja",
74                         action="store_true",
75                         help="generate ninja rules")
76    # This is not needed now that we have nsjail, since it launches inner_build
77    # with cwd at the top of the inner tree.
78    analyze.add_argument("--inner_tree",
79                         action="store",
80                         required=True,
81                         help="path to the inner tree")
82
83    return parser
84
85
86class Commands(object):
87    """Base class for inner_build commands."""
88
89    valid_commands = ("describe", "export_api_contributions", "analyze")
90
91    def Run(self, argv):
92        """Parse command arguments and call the named subcommand.
93
94        Throws AttributeError if the method for the command wasn't found.
95        """
96        args = _parser().parse_args(argv[1:])
97        if args.command not in self.valid_commands:
98            raise Exception(f"invalid command: {args.command}")
99        return getattr(self, args.command)(args)
100
101    def describe(self, args):
102        """Perform the default 'describe' processing."""
103
104        with open(args.input_json, encoding='iso-8859-1') as f:
105            query = json.load(f)
106
107        # This version of describe() simply replies with the build_domains
108        # requested.  If the inner tree can't build the requested build_domain,
109        # then the build will fail later.  If the inner tree has knowledge of
110        # what can be built, it should override this method with a method that
111        # returns the appropriate information.
112        # TODO: bazel-only builds will need to figure this out.
113        domain_data = [{"domains": [query.get("build_domains", [])]}]
114        reply = {"version": 0, "domain_data": domain_data}
115
116        filename = args.output_json or os.path.join(args.out_dir,
117                                                    "tree_info.json")
118        os.makedirs(os.path.dirname(filename), exist_ok=True)
119        with open(filename, "w", encoding='iso-8859-1') as f:
120            json.dump(reply, f, indent=4)
121
122    def export_api_contributions(self, args):
123        raise Exception(f"export_api_contributions({args}) not implemented")
124
125    def analyze(self, args):
126        raise Exception(f"analyze({args}) not implemented")
127
128
129@contextlib.contextmanager
130def setenv(**kwargs):
131    """Context to adjust environment."""
132    old_values = {}
133    delete_vars = set()
134    for k, v in kwargs.items():
135        # Save the prior state.
136        if k in os.environ:
137            old_values[k] = os.environ[k]
138        else:
139            delete_vars.add(k)
140
141        # Set the new value.
142        if v is None:
143            del os.environ[k]
144        else:
145            os.environ[k] = v
146    try:
147        yield
148    finally:
149        # Restore the old values.
150        for k, v in old_values.items():
151            os.environ[k] = v
152        for k in delete_vars:
153            del os.environ[k]
154