• 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 enum
18import os
19import platform
20
21_API_SURFACES = "api_surfaces"
22_INTERMEDIATES = "intermediates"
23
24# Phony target to assemble al the api files in out/api_surfaces
25ASSEMBLE_PHONY_TARGET = "multitree-sdk"
26
27class Context(object):
28    """Mockable container for global state."""
29
30    def __init__(self, out_root, errors):
31        self.out = OutDir(out_root)
32        self.errors = errors
33        self.tools = HostTools()
34
35
36class ContextTest(Context):
37    """Context for testing.
38
39    The real Context is manually constructed in orchestrator.py.
40    """
41
42    def __init__(self, test_work_dir: str, test_name: str):
43        """Initialize the object.
44
45        Args:
46          test_work_dir: The working directory to use for the test: typically
47                         created with tempfile.mkdtemp().
48          test_name: A name unique to the test: typically self.id() in the test.
49        """
50        super().__init__(os.path.join(test_work_dir, test_name), Errors(None))
51
52
53@enum.unique
54class OutDirBase(enum.Enum):
55    """The basepath to use for output paths.
56
57    ORIGIN: Path is relative to ${OUT_DIR}. Use this when the path will be
58            consumed while not nsjailed. (default)
59    OUTER:  Path is relative to the outer tree root.  Use this when the path
60            will be consumed while nsjailed in the outer tree.
61    """
62    DEFAULT = 0
63    ORIGIN = 1
64    OUTER = 2
65
66
67class OutDir(object):
68    """Encapsulates the logic about the out directory at the outer-tree level.
69    See also inner_tree.OutDirLayout for inner tree out dir contents."""
70
71    # For ease of use.
72    Base = OutDirBase
73
74    def __init__(self, out_origin, out_path="out"):
75        """Initialize with the root of the OUT_DIR for the inner tree.
76
77        Args:
78          out_origin: The OUT_DIR path to use.  Usually "out".
79          out_path: Where the outer tree out_dir will be mapped, relative to the
80                    outer tree root. Usually "out".
81        """
82        self._base = {}
83        self._base[self.Base.ORIGIN] = out_origin
84        self._base[self.Base.OUTER] = out_path
85        self._base[self.Base.DEFAULT] = self._base[self.Base.ORIGIN]
86
87    def _generate_path(self,
88                       *args,
89                       base: OutDirBase = OutDirBase.DEFAULT,
90                       abspath=False):
91        """Return the path to the file.
92
93        Args:
94          args: Path elements to use.
95          base: Which base path to use.
96          abspath: Whether to return the absolute path.
97        """
98        ret = os.path.join(self._base[base], *args)
99        if abspath:
100            ret = os.path.abspath(ret)
101        return ret
102
103    def root(self, **kwargs):
104        """The provided out_dir, mapped into "out/" for ninja."""
105        return self._generate_path(**kwargs)
106
107    def inner_tree_dir(self, tree_root, product, **kwargs):
108        """True root directory for inner tree inside the out dir."""
109        product = product or "unbundled"
110        out_root = f'{tree_root}_{product}'
111        return self._generate_path("trees", out_root, **kwargs)
112
113    def api_ninja_file(self, **kwargs):
114        """The ninja file that assembles API surfaces."""
115        return self._generate_path("api_surfaces.ninja", **kwargs)
116
117    def api_library_dir(self, surface, version, library, **kwargs):
118        """Directory for all the contents of a library inside an API surface.
119
120        This includes the build files.  Any intermediates should go in
121        api_library_work_dir.
122        """
123        return self._generate_path(_API_SURFACES, surface, str(version),
124                                   library, **kwargs)
125
126    def api_library_work_dir(self, surface, version, library, **kwargs):
127        """Intermediate scratch directory for library inside an API surface."""
128        return self._generate_path(self.api_surfaces_work_dir(),
129                                   surface,
130                                   str(version),
131                                   library,
132                                   **kwargs)
133
134    def api_surfaces_dir(self, **kwargs):
135        """The published api_surfaces directory."""
136        return self._generate_path(_API_SURFACES, **kwargs)
137
138    def api_surfaces_work_dir(self, *args):
139        """Intermediates / scratch directory for API surface assembly.
140        Useful for creating artifacts that are expected to be shared between multiple API libraries"""
141        return self._generate_path(_INTERMEDIATES, _API_SURFACES, *args)
142
143    def outer_ninja_file(self, **kwargs):
144        return self._generate_path("multitree.ninja", **kwargs)
145
146    def module_share_dir(self, module_type, module_name, **kwargs):
147        return self._generate_path("shared", module_type, module_name,
148                                   **kwargs)
149
150    def staging_dir(self, **kwargs):
151        return self._generate_path("staging", **kwargs)
152
153    def dist_dir(self, **kwargs):
154        """The DIST_DIR provided or out/dist"""
155        # TODO: Look at DIST_DIR
156        return self._generate_path("dist", **kwargs)
157
158    def nsjail_config_file(self, **kwargs):
159        """The nsjail config file used for the ninja run."""
160        return self._generate_path("nsjail.cfg", **kwargs)
161
162
163class Errors(object):
164    """Class for reporting and tracking errors."""
165
166    def __init__(self, stream):
167        """Initialize Error reporter with a file-like object."""
168        self._stream = stream
169        self._all = []
170
171    def error(self, message, file=None, line=None, col=None):
172        """Record the error message."""
173        s = ""
174        if file:
175            s += str(file)
176            s += ":"
177        if line:
178            s += str(line)
179            s += ":"
180        if col:
181            s += str(col)
182            s += ":"
183        if s:
184            s += " "
185        s += str(message)
186        if s[-1] != "\n":
187            s += "\n"
188        self._all.append(s)
189        if self._stream:
190            self._stream.write(s)
191
192    def had_error(self):
193        """Return if there were any errors reported."""
194        return len(self._all)
195
196    def get_errors(self):
197        """Get all errors that were reported."""
198        return self._all
199
200# This clang_version was picked from Soong (build/soong/cc/config/global.go).
201# However, C stubs are ABI-stable and should not be affected by divergence in toolchain
202# versions of orchestrator and inner_build.
203CLANG_VERSION = "clang-r475365b"
204
205class HostTools(object):
206    def __init__(self):
207        if platform.system() == "Linux":
208            self._arch = "linux-x86"
209        else:
210            raise Exception(
211                f"Orchestrator: unknown system {platform.system()}")
212
213        # Some of these are called a lot, so pre-compute the strings to save
214        # memory.
215        self._prebuilts = os.path.join("orchestrator", "prebuilts",
216                                       "build-tools", self._arch, "bin")
217        self._acp = os.path.join(self._prebuilts, "acp")
218        self._ninja = os.path.join(self._prebuilts, "ninja")
219        self._nsjail = os.path.join(self._prebuilts, "nsjail")
220        clang_root = os.path.join(
221            "orchestrator",
222            "prebuilts",
223            "clang",
224            "host",
225            self._arch,
226            CLANG_VERSION,
227            "bin",
228        )
229        self._clang = os.path.join(clang_root, "clang")
230        self._clang_cxx = os.path.join(clang_root, "clang++")
231
232
233    # TODO: @property
234    def acp(self):
235        return self._acp
236
237    # TODO: @property
238    def ninja(self):
239        return self._ninja
240
241    def clang(self):
242        return self._clang
243
244    def clang_cxx(self):
245        return self._clang_cxx
246
247    @property
248    def nsjail(self):
249        return self._nsjail
250
251
252def choose_out_dir():
253    """Get the root of the out dir, either $OUT_DIR or a default."""
254    return os.environ.get("OUT_DIR") or "out"
255