blob: 7e0642ed8cafb1f0b44e71d4c0aee6dbc1de861b [file] [log] [blame]
# Copyright 2024 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Package builder for the dev server."""
from __future__ import print_function
import logging
import os
import subprocess
import cherrypy # pylint: disable=import-error
# This must happen before any local modules get a chance to import
# anything from chromite.
import setup_chromite # pylint: disable=unused-import,import-error
from chromite.lib import gmerge_binhost
# Relative path to the wrapper directory inside the sysroot.
_SYSROOT_BUILD_BIN = "build/bin"
def _SysrootCmd(sysroot, cmd):
"""Path to the sysroot wrapper for |cmd|.
Args:
sysroot: Path to the sysroot.
cmd: Name of the command.
"""
return os.path.join(sysroot, _SYSROOT_BUILD_BIN, cmd)
# Module-local log function.
def _Log(message, *args):
return logging.info(message, *args)
def _OutputOf(command):
"""Runs command, a list of arguments beginning with an executable.
Args:
command: A list of arguments, beginning with the executable
Returns:
The output of the command
Raises:
subprocess.CalledProcessError if the command fails
"""
command_name = " ".join(command)
_Log("Executing: " + command_name)
with subprocess.Popen(command, stdout=subprocess.PIPE) as p:
output_blob = p.communicate()[0].decode("utf-8")
if p.returncode != 0:
raise subprocess.CalledProcessError(p.returncode, command_name)
return output_blob
class Builder:
"""Builds packages for the devserver."""
def _ShouldBeWorkedOn(self, board, pkg):
"""Is pkg a package that could be worked on, but is not?"""
if pkg in _OutputOf(["cros_workon", "--board=" + board, "list"]):
return False
# If it's in the list of possible workon targets, we should be working
# on it
return pkg in _OutputOf(
["cros_workon", "--board=" + board, "--all", "list"]
)
def SetError(self, text):
cherrypy.response.status = 500
_Log(text)
return text
def Build(self, board, pkg, additional_args):
"""Handles a build request from the cherrypy server."""
_Log("Additional build request arguments: " + str(additional_args))
def _AppendStrToEnvVar(env, var, additional_string):
env[var] = env.get(var, "") + " " + additional_string
_Log("%s flags modified to %s" % (var, env[var]))
env_copy = os.environ.copy()
if "use" in additional_args:
_AppendStrToEnvVar(env_copy, "USE", additional_args["use"])
if "features" in additional_args:
_AppendStrToEnvVar(
env_copy, "FEATURES", additional_args["features"]
)
try:
if not additional_args.get(
"accept_stable"
) and self._ShouldBeWorkedOn(board, pkg):
return self.SetError(
"Package is not cros_workon'd on the devserver machine.\n"
"Either start working on the package or pass "
"--accept_stable to gmerge"
)
sysroot = "/build/%s/" % board
# If user did not supply -n, we want to rebuild the package.
usepkg = additional_args.get("usepkg")
if not usepkg:
rc = subprocess.call(
[_SysrootCmd(sysroot, "emerge"), pkg], env=env_copy
)
if rc != 0:
return self.SetError("Could not emerge " + pkg)
# Sync gmerge binhost.
deep = additional_args.get("deep")
if not gmerge_binhost.update_gmerge_binhost(sysroot, [pkg], deep):
return self.SetError("Package %s is not installed" % pkg)
return "Success\n"
except OSError as e:
return self.SetError("Could not execute build command: " + str(e))