xref: /aosp_15_r20/external/bazelbuild-rules_python/tools/precompiler/precompiler.py (revision 60517a1edbc8ecf509223e9af94a7adec7d736b8)
1*60517a1eSAndroid Build Coastguard Worker# Copyright 2024 The Bazel Authors. All rights reserved.
2*60517a1eSAndroid Build Coastguard Worker#
3*60517a1eSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*60517a1eSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*60517a1eSAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*60517a1eSAndroid Build Coastguard Worker#
7*60517a1eSAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
8*60517a1eSAndroid Build Coastguard Worker#
9*60517a1eSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*60517a1eSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*60517a1eSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*60517a1eSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*60517a1eSAndroid Build Coastguard Worker# limitations under the License.
14*60517a1eSAndroid Build Coastguard Worker
15*60517a1eSAndroid Build Coastguard Worker"""A simple precompiler to generate deterministic pyc files for Bazel."""
16*60517a1eSAndroid Build Coastguard Worker
17*60517a1eSAndroid Build Coastguard Worker# NOTE: Imports specific to the persistent worker should only be imported
18*60517a1eSAndroid Build Coastguard Worker# when a persistent worker is used. Avoiding the unnecessary imports
19*60517a1eSAndroid Build Coastguard Worker# saves significant startup time for non-worker invocations.
20*60517a1eSAndroid Build Coastguard Workerimport argparse
21*60517a1eSAndroid Build Coastguard Workerimport py_compile
22*60517a1eSAndroid Build Coastguard Workerimport sys
23*60517a1eSAndroid Build Coastguard Worker
24*60517a1eSAndroid Build Coastguard Worker
25*60517a1eSAndroid Build Coastguard Workerdef _create_parser() -> "argparse.Namespace":
26*60517a1eSAndroid Build Coastguard Worker    parser = argparse.ArgumentParser(fromfile_prefix_chars="@")
27*60517a1eSAndroid Build Coastguard Worker    parser.add_argument("--invalidation_mode", default="CHECKED_HASH")
28*60517a1eSAndroid Build Coastguard Worker    parser.add_argument("--optimize", type=int, default=-1)
29*60517a1eSAndroid Build Coastguard Worker    parser.add_argument("--python_version")
30*60517a1eSAndroid Build Coastguard Worker
31*60517a1eSAndroid Build Coastguard Worker    parser.add_argument("--src", action="append", dest="srcs")
32*60517a1eSAndroid Build Coastguard Worker    parser.add_argument("--src_name", action="append", dest="src_names")
33*60517a1eSAndroid Build Coastguard Worker    parser.add_argument("--pyc", action="append", dest="pycs")
34*60517a1eSAndroid Build Coastguard Worker
35*60517a1eSAndroid Build Coastguard Worker    parser.add_argument("--persistent_worker", action="store_true")
36*60517a1eSAndroid Build Coastguard Worker    parser.add_argument("--log_level", default="ERROR")
37*60517a1eSAndroid Build Coastguard Worker    parser.add_argument("--worker_impl", default="async")
38*60517a1eSAndroid Build Coastguard Worker    return parser
39*60517a1eSAndroid Build Coastguard Worker
40*60517a1eSAndroid Build Coastguard Worker
41*60517a1eSAndroid Build Coastguard Workerdef _compile(options: "argparse.Namespace") -> None:
42*60517a1eSAndroid Build Coastguard Worker    try:
43*60517a1eSAndroid Build Coastguard Worker        invalidation_mode = py_compile.PycInvalidationMode[
44*60517a1eSAndroid Build Coastguard Worker            options.invalidation_mode.upper()
45*60517a1eSAndroid Build Coastguard Worker        ]
46*60517a1eSAndroid Build Coastguard Worker    except KeyError as e:
47*60517a1eSAndroid Build Coastguard Worker        raise ValueError(
48*60517a1eSAndroid Build Coastguard Worker            f"Unknown PycInvalidationMode: {options.invalidation_mode}"
49*60517a1eSAndroid Build Coastguard Worker        ) from e
50*60517a1eSAndroid Build Coastguard Worker
51*60517a1eSAndroid Build Coastguard Worker    if not (len(options.srcs) == len(options.src_names) == len(options.pycs)):
52*60517a1eSAndroid Build Coastguard Worker        raise AssertionError(
53*60517a1eSAndroid Build Coastguard Worker            "Mismatched number of --src, --src_name, and/or --pyc args"
54*60517a1eSAndroid Build Coastguard Worker        )
55*60517a1eSAndroid Build Coastguard Worker
56*60517a1eSAndroid Build Coastguard Worker    for src, src_name, pyc in zip(options.srcs, options.src_names, options.pycs):
57*60517a1eSAndroid Build Coastguard Worker        py_compile.compile(
58*60517a1eSAndroid Build Coastguard Worker            src,
59*60517a1eSAndroid Build Coastguard Worker            pyc,
60*60517a1eSAndroid Build Coastguard Worker            doraise=True,
61*60517a1eSAndroid Build Coastguard Worker            dfile=src_name,
62*60517a1eSAndroid Build Coastguard Worker            optimize=options.optimize,
63*60517a1eSAndroid Build Coastguard Worker            invalidation_mode=invalidation_mode,
64*60517a1eSAndroid Build Coastguard Worker        )
65*60517a1eSAndroid Build Coastguard Worker    return 0
66*60517a1eSAndroid Build Coastguard Worker
67*60517a1eSAndroid Build Coastguard Worker
68*60517a1eSAndroid Build Coastguard Worker# A stub type alias for readability.
69*60517a1eSAndroid Build Coastguard Worker# See the Bazel WorkRequest object definition:
70*60517a1eSAndroid Build Coastguard Worker# https://github.com/bazelbuild/bazel/blob/master/src/main/protobuf/worker_protocol.proto
71*60517a1eSAndroid Build Coastguard WorkerJsonWorkerRequest = object
72*60517a1eSAndroid Build Coastguard Worker
73*60517a1eSAndroid Build Coastguard Worker# A stub type alias for readability.
74*60517a1eSAndroid Build Coastguard Worker# See the Bazel WorkResponse object definition:
75*60517a1eSAndroid Build Coastguard Worker# https://github.com/bazelbuild/bazel/blob/master/src/main/protobuf/worker_protocol.proto
76*60517a1eSAndroid Build Coastguard WorkerJsonWorkerResponse = object
77*60517a1eSAndroid Build Coastguard Worker
78*60517a1eSAndroid Build Coastguard Worker
79*60517a1eSAndroid Build Coastguard Workerclass _SerialPersistentWorker:
80*60517a1eSAndroid Build Coastguard Worker    """Simple, synchronous, serial persistent worker."""
81*60517a1eSAndroid Build Coastguard Worker
82*60517a1eSAndroid Build Coastguard Worker    def __init__(self, instream: "typing.TextIO", outstream: "typing.TextIO"):
83*60517a1eSAndroid Build Coastguard Worker        self._instream = instream
84*60517a1eSAndroid Build Coastguard Worker        self._outstream = outstream
85*60517a1eSAndroid Build Coastguard Worker        self._parser = _create_parser()
86*60517a1eSAndroid Build Coastguard Worker
87*60517a1eSAndroid Build Coastguard Worker    def run(self) -> None:
88*60517a1eSAndroid Build Coastguard Worker        try:
89*60517a1eSAndroid Build Coastguard Worker            while True:
90*60517a1eSAndroid Build Coastguard Worker                request = None
91*60517a1eSAndroid Build Coastguard Worker                try:
92*60517a1eSAndroid Build Coastguard Worker                    request = self._get_next_request()
93*60517a1eSAndroid Build Coastguard Worker                    if request is None:
94*60517a1eSAndroid Build Coastguard Worker                        _logger.info("Empty request: exiting")
95*60517a1eSAndroid Build Coastguard Worker                        break
96*60517a1eSAndroid Build Coastguard Worker                    response = self._process_request(request)
97*60517a1eSAndroid Build Coastguard Worker                    if response:  # May be none for cancel request
98*60517a1eSAndroid Build Coastguard Worker                        self._send_response(response)
99*60517a1eSAndroid Build Coastguard Worker                except Exception:
100*60517a1eSAndroid Build Coastguard Worker                    _logger.exception("Unhandled error: request=%s", request)
101*60517a1eSAndroid Build Coastguard Worker                    output = (
102*60517a1eSAndroid Build Coastguard Worker                        f"Unhandled error:\nRequest: {request}\n"
103*60517a1eSAndroid Build Coastguard Worker                        + traceback.format_exc()
104*60517a1eSAndroid Build Coastguard Worker                    )
105*60517a1eSAndroid Build Coastguard Worker                    request_id = 0 if not request else request.get("requestId", 0)
106*60517a1eSAndroid Build Coastguard Worker                    self._send_response(
107*60517a1eSAndroid Build Coastguard Worker                        {
108*60517a1eSAndroid Build Coastguard Worker                            "exitCode": 3,
109*60517a1eSAndroid Build Coastguard Worker                            "output": output,
110*60517a1eSAndroid Build Coastguard Worker                            "requestId": request_id,
111*60517a1eSAndroid Build Coastguard Worker                        }
112*60517a1eSAndroid Build Coastguard Worker                    )
113*60517a1eSAndroid Build Coastguard Worker        finally:
114*60517a1eSAndroid Build Coastguard Worker            _logger.info("Worker shutting down")
115*60517a1eSAndroid Build Coastguard Worker
116*60517a1eSAndroid Build Coastguard Worker    def _get_next_request(self) -> "object | None":
117*60517a1eSAndroid Build Coastguard Worker        line = self._instream.readline()
118*60517a1eSAndroid Build Coastguard Worker        if not line:
119*60517a1eSAndroid Build Coastguard Worker            return None
120*60517a1eSAndroid Build Coastguard Worker        return json.loads(line)
121*60517a1eSAndroid Build Coastguard Worker
122*60517a1eSAndroid Build Coastguard Worker    def _process_request(self, request: "JsonWorkRequest") -> "JsonWorkResponse | None":
123*60517a1eSAndroid Build Coastguard Worker        if request.get("cancel"):
124*60517a1eSAndroid Build Coastguard Worker            return None
125*60517a1eSAndroid Build Coastguard Worker        options = self._options_from_request(request)
126*60517a1eSAndroid Build Coastguard Worker        _compile(options)
127*60517a1eSAndroid Build Coastguard Worker        response = {
128*60517a1eSAndroid Build Coastguard Worker            "requestId": request.get("requestId", 0),
129*60517a1eSAndroid Build Coastguard Worker            "exitCode": 0,
130*60517a1eSAndroid Build Coastguard Worker        }
131*60517a1eSAndroid Build Coastguard Worker        return response
132*60517a1eSAndroid Build Coastguard Worker
133*60517a1eSAndroid Build Coastguard Worker    def _options_from_request(
134*60517a1eSAndroid Build Coastguard Worker        self, request: "JsonWorkResponse"
135*60517a1eSAndroid Build Coastguard Worker    ) -> "argparse.Namespace":
136*60517a1eSAndroid Build Coastguard Worker        options = self._parser.parse_args(request["arguments"])
137*60517a1eSAndroid Build Coastguard Worker        if request.get("sandboxDir"):
138*60517a1eSAndroid Build Coastguard Worker            prefix = request["sandboxDir"]
139*60517a1eSAndroid Build Coastguard Worker            options.srcs = [os.path.join(prefix, v) for v in options.srcs]
140*60517a1eSAndroid Build Coastguard Worker            options.pycs = [os.path.join(prefix, v) for v in options.pycs]
141*60517a1eSAndroid Build Coastguard Worker        return options
142*60517a1eSAndroid Build Coastguard Worker
143*60517a1eSAndroid Build Coastguard Worker    def _send_response(self, response: "JsonWorkResponse") -> None:
144*60517a1eSAndroid Build Coastguard Worker        self._outstream.write(json.dumps(response) + "\n")
145*60517a1eSAndroid Build Coastguard Worker        self._outstream.flush()
146*60517a1eSAndroid Build Coastguard Worker
147*60517a1eSAndroid Build Coastguard Worker
148*60517a1eSAndroid Build Coastguard Workerclass _AsyncPersistentWorker:
149*60517a1eSAndroid Build Coastguard Worker    """Asynchronous, concurrent, persistent worker."""
150*60517a1eSAndroid Build Coastguard Worker
151*60517a1eSAndroid Build Coastguard Worker    def __init__(self, reader: "typing.TextIO", writer: "typing.TextIO"):
152*60517a1eSAndroid Build Coastguard Worker        self._reader = reader
153*60517a1eSAndroid Build Coastguard Worker        self._writer = writer
154*60517a1eSAndroid Build Coastguard Worker        self._parser = _create_parser()
155*60517a1eSAndroid Build Coastguard Worker        self._request_id_to_task = {}
156*60517a1eSAndroid Build Coastguard Worker        self._task_to_request_id = {}
157*60517a1eSAndroid Build Coastguard Worker
158*60517a1eSAndroid Build Coastguard Worker    @classmethod
159*60517a1eSAndroid Build Coastguard Worker    async def main(cls, instream: "typing.TextIO", outstream: "typing.TextIO") -> None:
160*60517a1eSAndroid Build Coastguard Worker        reader, writer = await cls._connect_streams(instream, outstream)
161*60517a1eSAndroid Build Coastguard Worker        await cls(reader, writer).run()
162*60517a1eSAndroid Build Coastguard Worker
163*60517a1eSAndroid Build Coastguard Worker    @classmethod
164*60517a1eSAndroid Build Coastguard Worker    async def _connect_streams(
165*60517a1eSAndroid Build Coastguard Worker        cls, instream: "typing.TextIO", outstream: "typing.TextIO"
166*60517a1eSAndroid Build Coastguard Worker    ) -> "tuple[asyncio.StreamReader, asyncio.StreamWriter]":
167*60517a1eSAndroid Build Coastguard Worker        loop = asyncio.get_event_loop()
168*60517a1eSAndroid Build Coastguard Worker        reader = asyncio.StreamReader()
169*60517a1eSAndroid Build Coastguard Worker        protocol = asyncio.StreamReaderProtocol(reader)
170*60517a1eSAndroid Build Coastguard Worker        await loop.connect_read_pipe(lambda: protocol, instream)
171*60517a1eSAndroid Build Coastguard Worker
172*60517a1eSAndroid Build Coastguard Worker        w_transport, w_protocol = await loop.connect_write_pipe(
173*60517a1eSAndroid Build Coastguard Worker            asyncio.streams.FlowControlMixin, outstream
174*60517a1eSAndroid Build Coastguard Worker        )
175*60517a1eSAndroid Build Coastguard Worker        writer = asyncio.StreamWriter(w_transport, w_protocol, reader, loop)
176*60517a1eSAndroid Build Coastguard Worker        return reader, writer
177*60517a1eSAndroid Build Coastguard Worker
178*60517a1eSAndroid Build Coastguard Worker    async def run(self) -> None:
179*60517a1eSAndroid Build Coastguard Worker        while True:
180*60517a1eSAndroid Build Coastguard Worker            _logger.info("pending requests: %s", len(self._request_id_to_task))
181*60517a1eSAndroid Build Coastguard Worker            request = await self._get_next_request()
182*60517a1eSAndroid Build Coastguard Worker            request_id = request.get("requestId", 0)
183*60517a1eSAndroid Build Coastguard Worker            task = asyncio.create_task(
184*60517a1eSAndroid Build Coastguard Worker                self._process_request(request), name=f"request_{request_id}"
185*60517a1eSAndroid Build Coastguard Worker            )
186*60517a1eSAndroid Build Coastguard Worker            self._request_id_to_task[request_id] = task
187*60517a1eSAndroid Build Coastguard Worker            self._task_to_request_id[task] = request_id
188*60517a1eSAndroid Build Coastguard Worker            task.add_done_callback(self._handle_task_done)
189*60517a1eSAndroid Build Coastguard Worker
190*60517a1eSAndroid Build Coastguard Worker    async def _get_next_request(self) -> "JsonWorkRequest":
191*60517a1eSAndroid Build Coastguard Worker        _logger.debug("awaiting line")
192*60517a1eSAndroid Build Coastguard Worker        line = await self._reader.readline()
193*60517a1eSAndroid Build Coastguard Worker        _logger.debug("recv line: %s", line)
194*60517a1eSAndroid Build Coastguard Worker        return json.loads(line)
195*60517a1eSAndroid Build Coastguard Worker
196*60517a1eSAndroid Build Coastguard Worker    def _handle_task_done(self, task: "asyncio.Task") -> None:
197*60517a1eSAndroid Build Coastguard Worker        request_id = self._task_to_request_id[task]
198*60517a1eSAndroid Build Coastguard Worker        _logger.info("task done: %s %s", request_id, task)
199*60517a1eSAndroid Build Coastguard Worker        del self._task_to_request_id[task]
200*60517a1eSAndroid Build Coastguard Worker        del self._request_id_to_task[request_id]
201*60517a1eSAndroid Build Coastguard Worker
202*60517a1eSAndroid Build Coastguard Worker    async def _process_request(self, request: "JsonWorkRequest") -> None:
203*60517a1eSAndroid Build Coastguard Worker        _logger.info("request %s: start: %s", request.get("requestId"), request)
204*60517a1eSAndroid Build Coastguard Worker        try:
205*60517a1eSAndroid Build Coastguard Worker            if request.get("cancel", False):
206*60517a1eSAndroid Build Coastguard Worker                await self._process_cancel_request(request)
207*60517a1eSAndroid Build Coastguard Worker            else:
208*60517a1eSAndroid Build Coastguard Worker                await self._process_compile_request(request)
209*60517a1eSAndroid Build Coastguard Worker        except asyncio.CancelledError:
210*60517a1eSAndroid Build Coastguard Worker            _logger.info(
211*60517a1eSAndroid Build Coastguard Worker                "request %s: cancel received, stopping processing",
212*60517a1eSAndroid Build Coastguard Worker                request.get("requestId"),
213*60517a1eSAndroid Build Coastguard Worker            )
214*60517a1eSAndroid Build Coastguard Worker            # We don't send a response because we assume the request that
215*60517a1eSAndroid Build Coastguard Worker            # triggered cancelling sent the response
216*60517a1eSAndroid Build Coastguard Worker            raise
217*60517a1eSAndroid Build Coastguard Worker        except:
218*60517a1eSAndroid Build Coastguard Worker            _logger.exception("Unhandled error: request=%s", request)
219*60517a1eSAndroid Build Coastguard Worker            self._send_response(
220*60517a1eSAndroid Build Coastguard Worker                {
221*60517a1eSAndroid Build Coastguard Worker                    "exitCode": 3,
222*60517a1eSAndroid Build Coastguard Worker                    "output": f"Unhandled error:\nRequest: {request}\n"
223*60517a1eSAndroid Build Coastguard Worker                    + traceback.format_exc(),
224*60517a1eSAndroid Build Coastguard Worker                    "requestId": 0 if not request else request.get("requestId", 0),
225*60517a1eSAndroid Build Coastguard Worker                }
226*60517a1eSAndroid Build Coastguard Worker            )
227*60517a1eSAndroid Build Coastguard Worker
228*60517a1eSAndroid Build Coastguard Worker    async def _process_cancel_request(self, request: "JsonWorkRequest") -> None:
229*60517a1eSAndroid Build Coastguard Worker        request_id = request.get("requestId", 0)
230*60517a1eSAndroid Build Coastguard Worker        task = self._request_id_to_task.get(request_id)
231*60517a1eSAndroid Build Coastguard Worker        if not task:
232*60517a1eSAndroid Build Coastguard Worker            # It must be already completed, so ignore the request, per spec
233*60517a1eSAndroid Build Coastguard Worker            return
234*60517a1eSAndroid Build Coastguard Worker
235*60517a1eSAndroid Build Coastguard Worker        task.cancel()
236*60517a1eSAndroid Build Coastguard Worker        self._send_response({"requestId": request_id, "wasCancelled": True})
237*60517a1eSAndroid Build Coastguard Worker
238*60517a1eSAndroid Build Coastguard Worker    async def _process_compile_request(self, request: "JsonWorkRequest") -> None:
239*60517a1eSAndroid Build Coastguard Worker        options = self._options_from_request(request)
240*60517a1eSAndroid Build Coastguard Worker        # _compile performs a varity of blocking IO calls, so run it separately
241*60517a1eSAndroid Build Coastguard Worker        await asyncio.to_thread(_compile, options)
242*60517a1eSAndroid Build Coastguard Worker        self._send_response(
243*60517a1eSAndroid Build Coastguard Worker            {
244*60517a1eSAndroid Build Coastguard Worker                "requestId": request.get("requestId", 0),
245*60517a1eSAndroid Build Coastguard Worker                "exitCode": 0,
246*60517a1eSAndroid Build Coastguard Worker            }
247*60517a1eSAndroid Build Coastguard Worker        )
248*60517a1eSAndroid Build Coastguard Worker
249*60517a1eSAndroid Build Coastguard Worker    def _options_from_request(self, request: "JsonWorkRequest") -> "argparse.Namespace":
250*60517a1eSAndroid Build Coastguard Worker        options = self._parser.parse_args(request["arguments"])
251*60517a1eSAndroid Build Coastguard Worker        if request.get("sandboxDir"):
252*60517a1eSAndroid Build Coastguard Worker            prefix = request["sandboxDir"]
253*60517a1eSAndroid Build Coastguard Worker            options.srcs = [os.path.join(prefix, v) for v in options.srcs]
254*60517a1eSAndroid Build Coastguard Worker            options.pycs = [os.path.join(prefix, v) for v in options.pycs]
255*60517a1eSAndroid Build Coastguard Worker        return options
256*60517a1eSAndroid Build Coastguard Worker
257*60517a1eSAndroid Build Coastguard Worker    def _send_response(self, response: "JsonWorkResponse") -> None:
258*60517a1eSAndroid Build Coastguard Worker        _logger.info("request %s: respond: %s", response.get("requestId"), response)
259*60517a1eSAndroid Build Coastguard Worker        self._writer.write(json.dumps(response).encode("utf8") + b"\n")
260*60517a1eSAndroid Build Coastguard Worker
261*60517a1eSAndroid Build Coastguard Worker
262*60517a1eSAndroid Build Coastguard Workerdef main(args: "list[str]") -> int:
263*60517a1eSAndroid Build Coastguard Worker    options = _create_parser().parse_args(args)
264*60517a1eSAndroid Build Coastguard Worker
265*60517a1eSAndroid Build Coastguard Worker    # Persistent workers are started with the `--persistent_worker` flag.
266*60517a1eSAndroid Build Coastguard Worker    # See the following docs for details on persistent workers:
267*60517a1eSAndroid Build Coastguard Worker    # https://bazel.build/remote/persistent
268*60517a1eSAndroid Build Coastguard Worker    # https://bazel.build/remote/multiplex
269*60517a1eSAndroid Build Coastguard Worker    # https://bazel.build/remote/creating
270*60517a1eSAndroid Build Coastguard Worker    if options.persistent_worker:
271*60517a1eSAndroid Build Coastguard Worker        global asyncio, itertools, json, logging, os, traceback, _logger
272*60517a1eSAndroid Build Coastguard Worker        import asyncio
273*60517a1eSAndroid Build Coastguard Worker        import itertools
274*60517a1eSAndroid Build Coastguard Worker        import json
275*60517a1eSAndroid Build Coastguard Worker        import logging
276*60517a1eSAndroid Build Coastguard Worker        import os.path
277*60517a1eSAndroid Build Coastguard Worker        import traceback
278*60517a1eSAndroid Build Coastguard Worker
279*60517a1eSAndroid Build Coastguard Worker        _logger = logging.getLogger("precompiler")
280*60517a1eSAndroid Build Coastguard Worker        # Only configure logging for workers. This prevents non-worker
281*60517a1eSAndroid Build Coastguard Worker        # invocations from spamming stderr with logging info
282*60517a1eSAndroid Build Coastguard Worker        logging.basicConfig(level=getattr(logging, options.log_level))
283*60517a1eSAndroid Build Coastguard Worker        _logger.info("persistent worker: impl=%s", options.worker_impl)
284*60517a1eSAndroid Build Coastguard Worker        if options.worker_impl == "serial":
285*60517a1eSAndroid Build Coastguard Worker            _SerialPersistentWorker(sys.stdin, sys.stdout).run()
286*60517a1eSAndroid Build Coastguard Worker        elif options.worker_impl == "async":
287*60517a1eSAndroid Build Coastguard Worker            asyncio.run(_AsyncPersistentWorker.main(sys.stdin, sys.stdout))
288*60517a1eSAndroid Build Coastguard Worker        else:
289*60517a1eSAndroid Build Coastguard Worker            raise ValueError(f"Unknown worker impl: {options.worker_impl}")
290*60517a1eSAndroid Build Coastguard Worker    else:
291*60517a1eSAndroid Build Coastguard Worker        _compile(options)
292*60517a1eSAndroid Build Coastguard Worker    return 0
293*60517a1eSAndroid Build Coastguard Worker
294*60517a1eSAndroid Build Coastguard Worker
295*60517a1eSAndroid Build Coastguard Workerif __name__ == "__main__":
296*60517a1eSAndroid Build Coastguard Worker    sys.exit(main(sys.argv[1:]))
297