Formatting: Format all python code with black.
This CL is probably not what you're looking for, it's only
automated formatting. Ignore it with
`git blame --ignore-rev <revision>` for this commit.
BUG=b:233893248
TEST=CQ
Change-Id: I66591d7a738d241aed3290138c0f68065ab10a6d
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/3879174
Reviewed-by: Mike Frysinger <vapier@chromium.org>
Tested-by: Alex Klein <saklein@chromium.org>
diff --git a/scripts/deploy_chrome.py b/scripts/deploy_chrome.py
index 55d6530..3a2ce51 100644
--- a/scripts/deploy_chrome.py
+++ b/scripts/deploy_chrome.py
@@ -50,34 +50,36 @@
KILL_PROC_MAX_WAIT = 10
POST_KILL_WAIT = 2
-MOUNT_RW_COMMAND = 'mount -o remount,rw /'
-LSOF_COMMAND_CHROME = 'lsof %s/chrome'
-LSOF_COMMAND = 'lsof %s'
-DBUS_RELOAD_COMMAND = 'killall -HUP dbus-daemon'
+MOUNT_RW_COMMAND = "mount -o remount,rw /"
+LSOF_COMMAND_CHROME = "lsof %s/chrome"
+LSOF_COMMAND = "lsof %s"
+DBUS_RELOAD_COMMAND = "killall -HUP dbus-daemon"
-_ANDROID_DIR = '/system/chrome'
-_ANDROID_DIR_EXTRACT_PATH = 'system/chrome/*'
+_ANDROID_DIR = "/system/chrome"
+_ANDROID_DIR_EXTRACT_PATH = "system/chrome/*"
-_CHROME_DIR = '/opt/google/chrome'
-_CHROME_DIR_MOUNT = '/mnt/stateful_partition/deploy_rootfs/opt/google/chrome'
-_CHROME_DIR_STAGING_TARBALL_ZSTD = 'chrome.tar.zst'
-_CHROME_TEST_BIN_DIR = '/usr/local/libexec/chrome-binary-tests'
+_CHROME_DIR = "/opt/google/chrome"
+_CHROME_DIR_MOUNT = "/mnt/stateful_partition/deploy_rootfs/opt/google/chrome"
+_CHROME_DIR_STAGING_TARBALL_ZSTD = "chrome.tar.zst"
+_CHROME_TEST_BIN_DIR = "/usr/local/libexec/chrome-binary-tests"
_UMOUNT_DIR_IF_MOUNTPOINT_CMD = (
- 'if mountpoint -q %(dir)s; then umount %(dir)s; fi')
-_BIND_TO_FINAL_DIR_CMD = 'mount --rbind %s %s'
-_SET_MOUNT_FLAGS_CMD = 'mount -o remount,exec,suid %s'
-_MKDIR_P_CMD = 'mkdir -p --mode 0775 %s'
-_FIND_TEST_BIN_CMD = 'find %s -maxdepth 1 -executable -type f' % (
- _CHROME_TEST_BIN_DIR)
+ "if mountpoint -q %(dir)s; then umount %(dir)s; fi"
+)
+_BIND_TO_FINAL_DIR_CMD = "mount --rbind %s %s"
+_SET_MOUNT_FLAGS_CMD = "mount -o remount,exec,suid %s"
+_MKDIR_P_CMD = "mkdir -p --mode 0775 %s"
+_FIND_TEST_BIN_CMD = "find %s -maxdepth 1 -executable -type f" % (
+ _CHROME_TEST_BIN_DIR
+)
-DF_COMMAND = 'df -k %s'
+DF_COMMAND = "df -k %s"
-LACROS_DIR = '/usr/local/lacros-chrome'
-_CONF_FILE = '/etc/chrome_dev.conf'
-_KILL_LACROS_CHROME_CMD = 'pkill -f %(lacros_dir)s/chrome'
-_RESET_LACROS_CHROME_CMD = 'rm -rf /home/chronos/user/lacros'
-MODIFIED_CONF_FILE = f'modified {_CONF_FILE}'
+LACROS_DIR = "/usr/local/lacros-chrome"
+_CONF_FILE = "/etc/chrome_dev.conf"
+_KILL_LACROS_CHROME_CMD = "pkill -f %(lacros_dir)s/chrome"
+_RESET_LACROS_CHROME_CMD = "rm -rf /home/chronos/user/lacros"
+MODIFIED_CONF_FILE = f"modified {_CONF_FILE}"
# This command checks if "--enable-features=LacrosSupport" is present in
# /etc/chrome_dev.conf. If it is not, then it is added.
@@ -105,818 +107,1096 @@
fi
"""
+
def _UrlBaseName(url):
- """Return the last component of the URL."""
- return url.rstrip('/').rpartition('/')[-1]
+ """Return the last component of the URL."""
+ return url.rstrip("/").rpartition("/")[-1]
class DeployFailure(failures_lib.StepFailure):
- """Raised whenever the deploy fails."""
+ """Raised whenever the deploy fails."""
DeviceInfo = collections.namedtuple(
- 'DeviceInfo', ['target_dir_size', 'target_fs_free'])
+ "DeviceInfo", ["target_dir_size", "target_fs_free"]
+)
class DeployChrome(object):
- """Wraps the core deployment functionality."""
+ """Wraps the core deployment functionality."""
- def __init__(self, options, tempdir, staging_dir):
- """Initialize the class.
+ def __init__(self, options, tempdir, staging_dir):
+ """Initialize the class.
- Args:
- options: options object.
- tempdir: Scratch space for the class. Caller has responsibility to clean
- it up.
- staging_dir: Directory to stage the files to.
- """
- self.tempdir = tempdir
- self.options = options
- self.staging_dir = staging_dir
- if not self.options.staging_only:
- hostname = options.device.hostname
- port = options.device.port
- self.device = remote.ChromiumOSDevice(hostname, port=port,
- ping=options.ping,
- private_key=options.private_key,
- include_dev_paths=False)
- self._root_dir_is_still_readonly = multiprocessing.Event()
+ Args:
+ options: options object.
+ tempdir: Scratch space for the class. Caller has responsibility to clean
+ it up.
+ staging_dir: Directory to stage the files to.
+ """
+ self.tempdir = tempdir
+ self.options = options
+ self.staging_dir = staging_dir
+ if not self.options.staging_only:
+ hostname = options.device.hostname
+ port = options.device.port
+ self.device = remote.ChromiumOSDevice(
+ hostname,
+ port=port,
+ ping=options.ping,
+ private_key=options.private_key,
+ include_dev_paths=False,
+ )
+ self._root_dir_is_still_readonly = multiprocessing.Event()
- self._deployment_name = 'lacros' if options.lacros else 'chrome'
- self.copy_paths = chrome_util.GetCopyPaths(self._deployment_name)
+ self._deployment_name = "lacros" if options.lacros else "chrome"
+ self.copy_paths = chrome_util.GetCopyPaths(self._deployment_name)
- self.chrome_dir = LACROS_DIR if self.options.lacros else _CHROME_DIR
+ self.chrome_dir = LACROS_DIR if self.options.lacros else _CHROME_DIR
- # Whether UI was stopped during setup.
- self._stopped_ui = False
+ # Whether UI was stopped during setup.
+ self._stopped_ui = False
- def _GetRemoteMountFree(self, remote_dir):
- result = self.device.run(DF_COMMAND % remote_dir)
- line = result.stdout.splitlines()[1]
- value = line.split()[3]
- multipliers = {
- 'G': 1024 * 1024 * 1024,
- 'M': 1024 * 1024,
- 'K': 1024,
- }
- return int(value.rstrip('GMK')) * multipliers.get(value[-1], 1)
+ def _GetRemoteMountFree(self, remote_dir):
+ result = self.device.run(DF_COMMAND % remote_dir)
+ line = result.stdout.splitlines()[1]
+ value = line.split()[3]
+ multipliers = {
+ "G": 1024 * 1024 * 1024,
+ "M": 1024 * 1024,
+ "K": 1024,
+ }
+ return int(value.rstrip("GMK")) * multipliers.get(value[-1], 1)
- def _GetRemoteDirSize(self, remote_dir):
- result = self.device.run('du -ks %s' % remote_dir,
- capture_output=True, encoding='utf-8')
- return int(result.stdout.split()[0])
+ def _GetRemoteDirSize(self, remote_dir):
+ result = self.device.run(
+ "du -ks %s" % remote_dir, capture_output=True, encoding="utf-8"
+ )
+ return int(result.stdout.split()[0])
- def _GetStagingDirSize(self):
- result = cros_build_lib.dbg_run(['du', '-ks', self.staging_dir],
- capture_output=True, encoding='utf-8')
- return int(result.stdout.split()[0])
+ def _GetStagingDirSize(self):
+ result = cros_build_lib.dbg_run(
+ ["du", "-ks", self.staging_dir],
+ capture_output=True,
+ encoding="utf-8",
+ )
+ return int(result.stdout.split()[0])
- def _ChromeFileInUse(self):
- result = self.device.run(LSOF_COMMAND_CHROME % (self.options.target_dir,),
- check=False, capture_output=True)
- return result.returncode == 0
+ def _ChromeFileInUse(self):
+ result = self.device.run(
+ LSOF_COMMAND_CHROME % (self.options.target_dir,),
+ check=False,
+ capture_output=True,
+ )
+ return result.returncode == 0
- def _Reboot(self):
- # A reboot in developer mode takes a while (and has delays), so the user
- # will have time to read and act on the USB boot instructions below.
- logging.info('Please remember to press Ctrl-U if you are booting from USB.')
- self.device.Reboot()
+ def _Reboot(self):
+ # A reboot in developer mode takes a while (and has delays), so the user
+ # will have time to read and act on the USB boot instructions below.
+ logging.info(
+ "Please remember to press Ctrl-U if you are booting from USB."
+ )
+ self.device.Reboot()
- def _DisableRootfsVerification(self):
- if not self.options.force:
- logging.error('Detected that the device has rootfs verification enabled.')
- logging.info('This script can automatically remove the rootfs '
- 'verification, which requires it to reboot the device.')
- logging.info('Make sure the device is in developer mode!')
- logging.info('Skip this prompt by specifying --force.')
- if not cros_build_lib.BooleanPrompt('Remove rootfs verification?', False):
- return False
+ def _DisableRootfsVerification(self):
+ if not self.options.force:
+ logging.error(
+ "Detected that the device has rootfs verification enabled."
+ )
+ logging.info(
+ "This script can automatically remove the rootfs "
+ "verification, which requires it to reboot the device."
+ )
+ logging.info("Make sure the device is in developer mode!")
+ logging.info("Skip this prompt by specifying --force.")
+ if not cros_build_lib.BooleanPrompt(
+ "Remove rootfs verification?", False
+ ):
+ return False
- logging.info('Removing rootfs verification from %s', self.options.device)
- # Running in VMs cause make_dev_ssd's firmware confidence checks to fail.
- # Use --force to bypass the checks.
- cmd = ('/usr/share/vboot/bin/make_dev_ssd.sh --partitions %d '
- '--remove_rootfs_verification --force')
- for partition in (KERNEL_A_PARTITION, KERNEL_B_PARTITION):
- self.device.run(cmd % partition, check=False)
+ logging.info(
+ "Removing rootfs verification from %s", self.options.device
+ )
+ # Running in VMs cause make_dev_ssd's firmware confidence checks to fail.
+ # Use --force to bypass the checks.
+ cmd = (
+ "/usr/share/vboot/bin/make_dev_ssd.sh --partitions %d "
+ "--remove_rootfs_verification --force"
+ )
+ for partition in (KERNEL_A_PARTITION, KERNEL_B_PARTITION):
+ self.device.run(cmd % partition, check=False)
- self._Reboot()
+ self._Reboot()
- # Now that the machine has been rebooted, we need to kill Chrome again.
- self._KillAshChromeIfNeeded()
+ # Now that the machine has been rebooted, we need to kill Chrome again.
+ self._KillAshChromeIfNeeded()
- # Make sure the rootfs is writable now.
- self._MountRootfsAsWritable(check=True)
+ # Make sure the rootfs is writable now.
+ self._MountRootfsAsWritable(check=True)
- return True
+ return True
- def _CheckUiJobStarted(self):
- # status output is in the format:
- # <job_name> <status> ['process' <pid>].
- # <status> is in the format <goal>/<state>.
- try:
- result = self.device.run('status ui', capture_output=True,
- encoding='utf-8')
- except cros_build_lib.RunCommandError as e:
- if 'Unknown job' in e.stderr:
- return False
- else:
- raise e
+ def _CheckUiJobStarted(self):
+ # status output is in the format:
+ # <job_name> <status> ['process' <pid>].
+ # <status> is in the format <goal>/<state>.
+ try:
+ result = self.device.run(
+ "status ui", capture_output=True, encoding="utf-8"
+ )
+ except cros_build_lib.RunCommandError as e:
+ if "Unknown job" in e.stderr:
+ return False
+ else:
+ raise e
- return result.stdout.split()[1].split('/')[0] == 'start'
+ return result.stdout.split()[1].split("/")[0] == "start"
- def _KillLacrosChrome(self):
- """This method kills lacros-chrome on the device, if it's running."""
- self.device.run(_KILL_LACROS_CHROME_CMD %
- {'lacros_dir': self.options.target_dir}, check=False)
+ def _KillLacrosChrome(self):
+ """This method kills lacros-chrome on the device, if it's running."""
+ self.device.run(
+ _KILL_LACROS_CHROME_CMD % {"lacros_dir": self.options.target_dir},
+ check=False,
+ )
- def _ResetLacrosChrome(self):
- """Reset Lacros to fresh state by deleting user data dir."""
- self.device.run(_RESET_LACROS_CHROME_CMD, check=False)
+ def _ResetLacrosChrome(self):
+ """Reset Lacros to fresh state by deleting user data dir."""
+ self.device.run(_RESET_LACROS_CHROME_CMD, check=False)
- def _KillAshChromeIfNeeded(self):
- """This method kills ash-chrome on the device, if it's running.
+ def _KillAshChromeIfNeeded(self):
+ """This method kills ash-chrome on the device, if it's running.
- This method calls 'stop ui', and then also manually pkills both ash-chrome
- and the session manager.
- """
- if self._CheckUiJobStarted():
- logging.info('Shutting down Chrome...')
- self.device.run('stop ui')
+ This method calls 'stop ui', and then also manually pkills both ash-chrome
+ and the session manager.
+ """
+ if self._CheckUiJobStarted():
+ logging.info("Shutting down Chrome...")
+ self.device.run("stop ui")
- # Developers sometimes run session_manager manually, in which case we'll
- # need to help shut the chrome processes down.
- try:
- with timeout_util.Timeout(self.options.process_timeout):
- while self._ChromeFileInUse():
- logging.warning('The chrome binary on the device is in use.')
- logging.warning('Killing chrome and session_manager processes...\n')
+ # Developers sometimes run session_manager manually, in which case we'll
+ # need to help shut the chrome processes down.
+ try:
+ with timeout_util.Timeout(self.options.process_timeout):
+ while self._ChromeFileInUse():
+ logging.warning(
+ "The chrome binary on the device is in use."
+ )
+ logging.warning(
+ "Killing chrome and session_manager processes...\n"
+ )
- self.device.run("pkill 'chrome|session_manager'", check=False)
- # Wait for processes to actually terminate
- time.sleep(POST_KILL_WAIT)
- logging.info('Rechecking the chrome binary...')
- except timeout_util.TimeoutError:
- msg = ('Could not kill processes after %s seconds. Please exit any '
- 'running chrome processes and try again.'
- % self.options.process_timeout)
- raise DeployFailure(msg)
+ self.device.run(
+ "pkill 'chrome|session_manager'", check=False
+ )
+ # Wait for processes to actually terminate
+ time.sleep(POST_KILL_WAIT)
+ logging.info("Rechecking the chrome binary...")
+ except timeout_util.TimeoutError:
+ msg = (
+ "Could not kill processes after %s seconds. Please exit any "
+ "running chrome processes and try again."
+ % self.options.process_timeout
+ )
+ raise DeployFailure(msg)
- def _MountRootfsAsWritable(self, check=False):
- """Mounts the rootfs as writable.
+ def _MountRootfsAsWritable(self, check=False):
+ """Mounts the rootfs as writable.
- If the command fails and the root dir is not writable then this function
- sets self._root_dir_is_still_readonly.
+ If the command fails and the root dir is not writable then this function
+ sets self._root_dir_is_still_readonly.
- Args:
- check: See remote.RemoteAccess.RemoteSh for details.
- """
- # TODO: Should migrate to use the remount functions in remote_access.
- result = self.device.run(MOUNT_RW_COMMAND, check=check,
- capture_output=True, encoding='utf-8')
- if result.returncode and not self.device.IsDirWritable('/'):
- self._root_dir_is_still_readonly.set()
+ Args:
+ check: See remote.RemoteAccess.RemoteSh for details.
+ """
+ # TODO: Should migrate to use the remount functions in remote_access.
+ result = self.device.run(
+ MOUNT_RW_COMMAND, check=check, capture_output=True, encoding="utf-8"
+ )
+ if result.returncode and not self.device.IsDirWritable("/"):
+ self._root_dir_is_still_readonly.set()
- def _EnsureTargetDir(self):
- """Ensures that the target directory exists on the remote device."""
- target_dir = self.options.target_dir
- # Any valid /opt directory should already exist so avoid the remote call.
- if os.path.commonprefix([target_dir, '/opt']) == '/opt':
- return
- self.device.run(['mkdir', '-p', '--mode', '0775', target_dir])
+ def _EnsureTargetDir(self):
+ """Ensures that the target directory exists on the remote device."""
+ target_dir = self.options.target_dir
+ # Any valid /opt directory should already exist so avoid the remote call.
+ if os.path.commonprefix([target_dir, "/opt"]) == "/opt":
+ return
+ self.device.run(["mkdir", "-p", "--mode", "0775", target_dir])
- def _GetDeviceInfo(self):
- """Returns the disk space used and available for the target diectory."""
- steps = [
- functools.partial(self._GetRemoteDirSize, self.options.target_dir),
- functools.partial(self._GetRemoteMountFree, self.options.target_dir)
- ]
- return_values = parallel.RunParallelSteps(steps, return_values=True)
- return DeviceInfo(*return_values)
+ def _GetDeviceInfo(self):
+ """Returns the disk space used and available for the target diectory."""
+ steps = [
+ functools.partial(self._GetRemoteDirSize, self.options.target_dir),
+ functools.partial(
+ self._GetRemoteMountFree, self.options.target_dir
+ ),
+ ]
+ return_values = parallel.RunParallelSteps(steps, return_values=True)
+ return DeviceInfo(*return_values)
- def _CheckDeviceFreeSpace(self, device_info):
- """See if target device has enough space for Chrome.
+ def _CheckDeviceFreeSpace(self, device_info):
+ """See if target device has enough space for Chrome.
- Args:
- device_info: A DeviceInfo named tuple.
- """
- effective_free = device_info.target_dir_size + device_info.target_fs_free
- staging_size = self._GetStagingDirSize()
- if effective_free < staging_size:
- raise DeployFailure(
- 'Not enough free space on the device. Required: %s MiB, '
- 'actual: %s MiB.' % (staging_size // 1024, effective_free // 1024))
- if device_info.target_fs_free < (100 * 1024):
- logging.warning('The device has less than 100MB free. deploy_chrome may '
- 'hang during the transfer.')
+ Args:
+ device_info: A DeviceInfo named tuple.
+ """
+ effective_free = (
+ device_info.target_dir_size + device_info.target_fs_free
+ )
+ staging_size = self._GetStagingDirSize()
+ if effective_free < staging_size:
+ raise DeployFailure(
+ "Not enough free space on the device. Required: %s MiB, "
+ "actual: %s MiB."
+ % (staging_size // 1024, effective_free // 1024)
+ )
+ if device_info.target_fs_free < (100 * 1024):
+ logging.warning(
+ "The device has less than 100MB free. deploy_chrome may "
+ "hang during the transfer."
+ )
- def _ShouldUseCompression(self):
- """Checks if compression should be used for rsync."""
- if self.options.compress == 'always':
- return True
- elif self.options.compress == 'never':
- return False
- elif self.options.compress == 'auto':
- return not self.device.HasGigabitEthernet()
+ def _ShouldUseCompression(self):
+ """Checks if compression should be used for rsync."""
+ if self.options.compress == "always":
+ return True
+ elif self.options.compress == "never":
+ return False
+ elif self.options.compress == "auto":
+ return not self.device.HasGigabitEthernet()
- def _Deploy(self):
- logging.info('Copying %s to %s on device...', self._deployment_name,
- self.options.target_dir)
- # CopyToDevice will fall back to scp if rsync is corrupted on stateful.
- # This does not work for deploy.
- if not self.device.HasRsync():
- raise DeployFailure(
- 'rsync is not found on the device.\n'
- 'Run dev_install on the device to get rsync installed.')
- self.device.CopyToDevice('%s/' % os.path.abspath(self.staging_dir),
- self.options.target_dir,
- mode='rsync', inplace=True,
- compress=self._ShouldUseCompression(),
- debug_level=logging.INFO,
- verbose=self.options.verbose)
+ def _Deploy(self):
+ logging.info(
+ "Copying %s to %s on device...",
+ self._deployment_name,
+ self.options.target_dir,
+ )
+ # CopyToDevice will fall back to scp if rsync is corrupted on stateful.
+ # This does not work for deploy.
+ if not self.device.HasRsync():
+ raise DeployFailure(
+ "rsync is not found on the device.\n"
+ "Run dev_install on the device to get rsync installed."
+ )
+ self.device.CopyToDevice(
+ "%s/" % os.path.abspath(self.staging_dir),
+ self.options.target_dir,
+ mode="rsync",
+ inplace=True,
+ compress=self._ShouldUseCompression(),
+ debug_level=logging.INFO,
+ verbose=self.options.verbose,
+ )
- # Set the security context on the default Chrome dir if that's where it's
- # getting deployed, and only on SELinux supported devices.
- if not self.options.lacros and self.device.IsSELinuxAvailable() and (
- _CHROME_DIR in (self.options.target_dir, self.options.mount_dir)):
- self.device.run(['restorecon', '-R', _CHROME_DIR])
+ # Set the security context on the default Chrome dir if that's where it's
+ # getting deployed, and only on SELinux supported devices.
+ if (
+ not self.options.lacros
+ and self.device.IsSELinuxAvailable()
+ and (
+ _CHROME_DIR in (self.options.target_dir, self.options.mount_dir)
+ )
+ ):
+ self.device.run(["restorecon", "-R", _CHROME_DIR])
- for p in self.copy_paths:
- if p.mode:
- # Set mode if necessary.
- self.device.run('chmod %o %s/%s' % (
- p.mode, self.options.target_dir, p.src if not p.dest else p.dest))
+ for p in self.copy_paths:
+ if p.mode:
+ # Set mode if necessary.
+ self.device.run(
+ "chmod %o %s/%s"
+ % (
+ p.mode,
+ self.options.target_dir,
+ p.src if not p.dest else p.dest,
+ )
+ )
- if self.options.lacros:
- self.device.run(['chown', '-R', 'chronos:chronos',
- self.options.target_dir])
+ if self.options.lacros:
+ self.device.run(
+ ["chown", "-R", "chronos:chronos", self.options.target_dir]
+ )
- # Send SIGHUP to dbus-daemon to tell it to reload its configs. This won't
- # pick up major changes (bus type, logging, etc.), but all we care about is
- # getting the latest policy from /opt/google/chrome/dbus so that Chrome will
- # be authorized to take ownership of its service names.
- self.device.run(DBUS_RELOAD_COMMAND, check=False)
+ # Send SIGHUP to dbus-daemon to tell it to reload its configs. This won't
+ # pick up major changes (bus type, logging, etc.), but all we care about is
+ # getting the latest policy from /opt/google/chrome/dbus so that Chrome will
+ # be authorized to take ownership of its service names.
+ self.device.run(DBUS_RELOAD_COMMAND, check=False)
- if self.options.startui and self._stopped_ui:
- logging.info('Starting UI...')
- self.device.run('start ui')
-
- def _DeployTestBinaries(self):
- """Deploys any local test binary to _CHROME_TEST_BIN_DIR on the device.
-
- There could be several binaries located in the local build dir, so compare
- what's already present on the device in _CHROME_TEST_BIN_DIR , and copy
- over any that we also built ourselves.
- """
- r = self.device.run(_FIND_TEST_BIN_CMD, check=False)
- if r.returncode != 0:
- raise DeployFailure('Unable to ls contents of %s' % _CHROME_TEST_BIN_DIR)
- binaries_to_copy = []
- for f in r.stdout.splitlines():
- binaries_to_copy.append(
- chrome_util.Path(os.path.basename(f), exe=True, optional=True))
-
- staging_dir = os.path.join(
- self.tempdir, os.path.basename(_CHROME_TEST_BIN_DIR))
- _PrepareStagingDir(self.options, self.tempdir, staging_dir,
- copy_paths=binaries_to_copy)
- # Deploying can occasionally run into issues with rsync getting a broken
- # pipe, so retry several times. See crbug.com/1141618 for more
- # information.
- retry_util.RetryException(
- None, 3, self.device.CopyToDevice, staging_dir,
- os.path.dirname(_CHROME_TEST_BIN_DIR), mode='rsync')
-
- def _CheckConnection(self):
- try:
- logging.info('Testing connection to the device...')
- self.device.run('true')
- except cros_build_lib.RunCommandError as ex:
- logging.error('Error connecting to the test device.')
- raise DeployFailure(ex)
-
- def _CheckBoard(self):
- """Check that the Chrome build is targeted for the device board."""
- if self.options.board == self.device.board:
- return
- logging.warning('Device board is %s whereas target board is %s.',
- self.device.board, self.options.board)
- if self.options.force:
- return
- if not cros_build_lib.BooleanPrompt('Continue despite board mismatch?',
- False):
- raise DeployFailure('Aborted.')
-
- def _CheckDeployType(self):
- if self.options.build_dir:
- def BinaryExists(filename):
- """Checks if the passed-in file is present in the build directory."""
- return os.path.exists(os.path.join(self.options.build_dir, filename))
-
- # In the future, lacros-chrome and ash-chrome will likely be named
- # something other than 'chrome' to avoid confusion.
- # Handle non-Chrome deployments.
- if not BinaryExists('chrome'):
- if BinaryExists('app_shell'):
- self.copy_paths = chrome_util.GetCopyPaths('app_shell')
-
- def _PrepareStagingDir(self):
- _PrepareStagingDir(self.options, self.tempdir, self.staging_dir,
- self.copy_paths, self.chrome_dir)
-
- def _MountTarget(self):
- logging.info('Mounting Chrome...')
-
- # Create directory if does not exist.
- self.device.run(_MKDIR_P_CMD % self.options.mount_dir)
- try:
- # Umount the existing mount on mount_dir if present first.
- self.device.run(_UMOUNT_DIR_IF_MOUNTPOINT_CMD %
- {'dir': self.options.mount_dir})
- except cros_build_lib.RunCommandError as e:
- logging.error('Failed to umount %s', self.options.mount_dir)
- # If there is a failure, check if some processs is using the mount_dir.
- result = self.device.run(LSOF_COMMAND % (self.options.mount_dir,),
- check=False, capture_output=True,
- encoding='utf-8')
- logging.error('lsof %s -->', self.options.mount_dir)
- logging.error(result.stdout)
- raise e
-
- self.device.run(_BIND_TO_FINAL_DIR_CMD % (self.options.target_dir,
- self.options.mount_dir))
-
- # Chrome needs partition to have exec and suid flags set
- self.device.run(_SET_MOUNT_FLAGS_CMD % (self.options.mount_dir,))
-
- def Cleanup(self):
- """Clean up RemoteDevice."""
- if not self.options.staging_only:
- self.device.Cleanup()
-
- def Perform(self):
- self._CheckDeployType()
-
- # If requested, just do the staging step.
- if self.options.staging_only:
- self._PrepareStagingDir()
- return 0
-
- # Check that the build matches the device. Lacros-chrome skips this check as
- # it's currently board independent. This means that it's possible to deploy
- # a build of lacros-chrome with a mismatched architecture. We don't try to
- # prevent this developer foot-gun.
- if not self.options.lacros:
- self._CheckBoard()
-
- # Ensure that the target directory exists before running parallel steps.
- self._EnsureTargetDir()
-
- logging.info('Preparing device')
- steps = [self._GetDeviceInfo, self._CheckConnection,
- self._MountRootfsAsWritable,
- self._PrepareStagingDir]
-
- restart_ui = True
- if self.options.lacros:
- # If this is a lacros build, we only want to restart ash-chrome if needed.
- restart_ui = False
- steps.append(self._KillLacrosChrome)
- if self.options.reset_lacros:
- steps.append(self._ResetLacrosChrome)
- if self.options.modify_config_file:
- restart_ui = self._ModifyConfigFileIfNeededForLacros()
-
- if restart_ui:
- steps.append(self._KillAshChromeIfNeeded)
- self._stopped_ui = True
-
- ret = parallel.RunParallelSteps(steps, halt_on_error=True,
- return_values=True)
- self._CheckDeviceFreeSpace(ret[0])
-
- # If the root dir is not writable, try disabling rootfs verification.
- # (We always do this by default so that developers can write to
- # /etc/chriome_dev.conf and other directories in the rootfs).
- if self._root_dir_is_still_readonly.is_set():
- if self.options.noremove_rootfs_verification:
- logging.warning('Skipping disable rootfs verification.')
- elif not self._DisableRootfsVerification():
- logging.warning('Failed to disable rootfs verification.')
-
- # If the target dir is still not writable (i.e. the user opted out or the
- # command failed), abort.
- if not self.device.IsDirWritable(self.options.target_dir):
if self.options.startui and self._stopped_ui:
- logging.info('Restarting Chrome...')
- self.device.run('start ui')
- raise DeployFailure('Target location is not writable. Aborting.')
+ logging.info("Starting UI...")
+ self.device.run("start ui")
- if self.options.mount_dir is not None:
- self._MountTarget()
+ def _DeployTestBinaries(self):
+ """Deploys any local test binary to _CHROME_TEST_BIN_DIR on the device.
- # Actually deploy Chrome to the device.
- self._Deploy()
- if self.options.deploy_test_binaries:
- self._DeployTestBinaries()
+ There could be several binaries located in the local build dir, so compare
+ what's already present on the device in _CHROME_TEST_BIN_DIR , and copy
+ over any that we also built ourselves.
+ """
+ r = self.device.run(_FIND_TEST_BIN_CMD, check=False)
+ if r.returncode != 0:
+ raise DeployFailure(
+ "Unable to ls contents of %s" % _CHROME_TEST_BIN_DIR
+ )
+ binaries_to_copy = []
+ for f in r.stdout.splitlines():
+ binaries_to_copy.append(
+ chrome_util.Path(os.path.basename(f), exe=True, optional=True)
+ )
+ staging_dir = os.path.join(
+ self.tempdir, os.path.basename(_CHROME_TEST_BIN_DIR)
+ )
+ _PrepareStagingDir(
+ self.options, self.tempdir, staging_dir, copy_paths=binaries_to_copy
+ )
+ # Deploying can occasionally run into issues with rsync getting a broken
+ # pipe, so retry several times. See crbug.com/1141618 for more
+ # information.
+ retry_util.RetryException(
+ None,
+ 3,
+ self.device.CopyToDevice,
+ staging_dir,
+ os.path.dirname(_CHROME_TEST_BIN_DIR),
+ mode="rsync",
+ )
- def _ModifyConfigFileIfNeededForLacros(self):
- """Modifies the /etc/chrome_dev.conf file for lacros-chrome.
+ def _CheckConnection(self):
+ try:
+ logging.info("Testing connection to the device...")
+ self.device.run("true")
+ except cros_build_lib.RunCommandError as ex:
+ logging.error("Error connecting to the test device.")
+ raise DeployFailure(ex)
- Returns:
- True if the file is modified, and the return value is usually used to
- determine whether restarting ash-chrome is needed.
- """
- assert self.options.lacros, (
- 'Only deploying lacros-chrome needs to modify the config file.')
- # Update /etc/chrome_dev.conf to include appropriate flags.
- modified = False
- result = self.device.run(ENABLE_LACROS_VIA_CONF_COMMAND, shell=True)
- if result.stdout.strip() == MODIFIED_CONF_FILE:
- modified = True
- result = self.device.run(
- _SET_LACROS_PATH_VIA_CONF_COMMAND % {
- 'conf_file': _CONF_FILE,
- 'lacros_path': self.options.target_dir,
- 'modified_conf_file': MODIFIED_CONF_FILE
- },
- shell=True)
- if result.stdout.strip() == MODIFIED_CONF_FILE:
- modified = True
+ def _CheckBoard(self):
+ """Check that the Chrome build is targeted for the device board."""
+ if self.options.board == self.device.board:
+ return
+ logging.warning(
+ "Device board is %s whereas target board is %s.",
+ self.device.board,
+ self.options.board,
+ )
+ if self.options.force:
+ return
+ if not cros_build_lib.BooleanPrompt(
+ "Continue despite board mismatch?", False
+ ):
+ raise DeployFailure("Aborted.")
- return modified
+ def _CheckDeployType(self):
+ if self.options.build_dir:
+
+ def BinaryExists(filename):
+ """Checks if the passed-in file is present in the build directory."""
+ return os.path.exists(
+ os.path.join(self.options.build_dir, filename)
+ )
+
+ # In the future, lacros-chrome and ash-chrome will likely be named
+ # something other than 'chrome' to avoid confusion.
+ # Handle non-Chrome deployments.
+ if not BinaryExists("chrome"):
+ if BinaryExists("app_shell"):
+ self.copy_paths = chrome_util.GetCopyPaths("app_shell")
+
+ def _PrepareStagingDir(self):
+ _PrepareStagingDir(
+ self.options,
+ self.tempdir,
+ self.staging_dir,
+ self.copy_paths,
+ self.chrome_dir,
+ )
+
+ def _MountTarget(self):
+ logging.info("Mounting Chrome...")
+
+ # Create directory if does not exist.
+ self.device.run(_MKDIR_P_CMD % self.options.mount_dir)
+ try:
+ # Umount the existing mount on mount_dir if present first.
+ self.device.run(
+ _UMOUNT_DIR_IF_MOUNTPOINT_CMD % {"dir": self.options.mount_dir}
+ )
+ except cros_build_lib.RunCommandError as e:
+ logging.error("Failed to umount %s", self.options.mount_dir)
+ # If there is a failure, check if some processs is using the mount_dir.
+ result = self.device.run(
+ LSOF_COMMAND % (self.options.mount_dir,),
+ check=False,
+ capture_output=True,
+ encoding="utf-8",
+ )
+ logging.error("lsof %s -->", self.options.mount_dir)
+ logging.error(result.stdout)
+ raise e
+
+ self.device.run(
+ _BIND_TO_FINAL_DIR_CMD
+ % (self.options.target_dir, self.options.mount_dir)
+ )
+
+ # Chrome needs partition to have exec and suid flags set
+ self.device.run(_SET_MOUNT_FLAGS_CMD % (self.options.mount_dir,))
+
+ def Cleanup(self):
+ """Clean up RemoteDevice."""
+ if not self.options.staging_only:
+ self.device.Cleanup()
+
+ def Perform(self):
+ self._CheckDeployType()
+
+ # If requested, just do the staging step.
+ if self.options.staging_only:
+ self._PrepareStagingDir()
+ return 0
+
+ # Check that the build matches the device. Lacros-chrome skips this check as
+ # it's currently board independent. This means that it's possible to deploy
+ # a build of lacros-chrome with a mismatched architecture. We don't try to
+ # prevent this developer foot-gun.
+ if not self.options.lacros:
+ self._CheckBoard()
+
+ # Ensure that the target directory exists before running parallel steps.
+ self._EnsureTargetDir()
+
+ logging.info("Preparing device")
+ steps = [
+ self._GetDeviceInfo,
+ self._CheckConnection,
+ self._MountRootfsAsWritable,
+ self._PrepareStagingDir,
+ ]
+
+ restart_ui = True
+ if self.options.lacros:
+ # If this is a lacros build, we only want to restart ash-chrome if needed.
+ restart_ui = False
+ steps.append(self._KillLacrosChrome)
+ if self.options.reset_lacros:
+ steps.append(self._ResetLacrosChrome)
+ if self.options.modify_config_file:
+ restart_ui = self._ModifyConfigFileIfNeededForLacros()
+
+ if restart_ui:
+ steps.append(self._KillAshChromeIfNeeded)
+ self._stopped_ui = True
+
+ ret = parallel.RunParallelSteps(
+ steps, halt_on_error=True, return_values=True
+ )
+ self._CheckDeviceFreeSpace(ret[0])
+
+ # If the root dir is not writable, try disabling rootfs verification.
+ # (We always do this by default so that developers can write to
+ # /etc/chriome_dev.conf and other directories in the rootfs).
+ if self._root_dir_is_still_readonly.is_set():
+ if self.options.noremove_rootfs_verification:
+ logging.warning("Skipping disable rootfs verification.")
+ elif not self._DisableRootfsVerification():
+ logging.warning("Failed to disable rootfs verification.")
+
+ # If the target dir is still not writable (i.e. the user opted out or the
+ # command failed), abort.
+ if not self.device.IsDirWritable(self.options.target_dir):
+ if self.options.startui and self._stopped_ui:
+ logging.info("Restarting Chrome...")
+ self.device.run("start ui")
+ raise DeployFailure(
+ "Target location is not writable. Aborting."
+ )
+
+ if self.options.mount_dir is not None:
+ self._MountTarget()
+
+ # Actually deploy Chrome to the device.
+ self._Deploy()
+ if self.options.deploy_test_binaries:
+ self._DeployTestBinaries()
+
+ def _ModifyConfigFileIfNeededForLacros(self):
+ """Modifies the /etc/chrome_dev.conf file for lacros-chrome.
+
+ Returns:
+ True if the file is modified, and the return value is usually used to
+ determine whether restarting ash-chrome is needed.
+ """
+ assert (
+ self.options.lacros
+ ), "Only deploying lacros-chrome needs to modify the config file."
+ # Update /etc/chrome_dev.conf to include appropriate flags.
+ modified = False
+ result = self.device.run(ENABLE_LACROS_VIA_CONF_COMMAND, shell=True)
+ if result.stdout.strip() == MODIFIED_CONF_FILE:
+ modified = True
+ result = self.device.run(
+ _SET_LACROS_PATH_VIA_CONF_COMMAND
+ % {
+ "conf_file": _CONF_FILE,
+ "lacros_path": self.options.target_dir,
+ "modified_conf_file": MODIFIED_CONF_FILE,
+ },
+ shell=True,
+ )
+ if result.stdout.strip() == MODIFIED_CONF_FILE:
+ modified = True
+
+ return modified
def ValidateStagingFlags(value):
- """Convert formatted string to dictionary."""
- return chrome_util.ProcessShellFlags(value)
+ """Convert formatted string to dictionary."""
+ return chrome_util.ProcessShellFlags(value)
def ValidateGnArgs(value):
- """Convert GN_ARGS-formatted string to dictionary."""
- return gn_helpers.FromGNArgs(value)
+ """Convert GN_ARGS-formatted string to dictionary."""
+ return gn_helpers.FromGNArgs(value)
def _CreateParser():
- """Create our custom parser."""
- parser = commandline.ArgumentParser(description=__doc__, caching=True)
+ """Create our custom parser."""
+ parser = commandline.ArgumentParser(description=__doc__, caching=True)
- # TODO(rcui): Have this use the UI-V2 format of having source and target
- # device be specified as positional arguments.
- parser.add_argument('--force', action='store_true', default=False,
- help='Skip all prompts (such as the prompt for disabling '
- 'of rootfs verification). This may result in the '
- 'target machine being rebooted.')
- sdk_board_env = os.environ.get(cros_chrome_sdk.SDKFetcher.SDK_BOARD_ENV)
- parser.add_argument('--board', default=sdk_board_env,
- help='The board the Chrome build is targeted for. When '
- "in a 'cros chrome-sdk' shell, defaults to the SDK "
- 'board.')
- parser.add_argument('--build-dir', type='path',
- help='The directory with Chrome build artifacts to '
- 'deploy from. Typically of format '
- '<chrome_root>/out/Debug. When this option is used, '
- 'the GN_ARGS environment variable must be set.')
- parser.add_argument('--target-dir', type='path',
- default=None,
- help='Target directory on device to deploy Chrome into.')
- parser.add_argument('-g', '--gs-path', type='gs_path',
- help='GS path that contains the chrome to deploy.')
- parser.add_argument('--private-key', type='path', default=None,
- help='An ssh private key to use when deploying to '
- 'a CrOS device.')
- parser.add_argument('--nostartui', action='store_false', dest='startui',
- default=True,
- help="Don't restart the ui daemon after deployment.")
- parser.add_argument('--nostrip', action='store_false', dest='dostrip',
- default=True,
- help="Don't strip binaries during deployment. Warning: "
- 'the resulting binaries will be very large!')
- parser.add_argument(
- '-d', '--device',
- type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
- help='Device hostname or IP in the format hostname[:port].')
- parser.add_argument('--mount-dir', type='path', default=None,
- help='Deploy Chrome in target directory and bind it '
- 'to the directory specified by this flag.'
- 'Any existing mount on this directory will be '
- 'umounted first.')
- parser.add_argument('--mount', action='store_true', default=False,
- help='Deploy Chrome to default target directory and bind '
- 'it to the default mount directory.'
- 'Any existing mount on this directory will be '
- 'umounted first.')
- parser.add_argument('--noremove-rootfs-verification', action='store_true',
- default=False, help='Never remove rootfs verification.')
- parser.add_argument('--deploy-test-binaries', action='store_true',
- default=False,
- help='Also deploy any test binaries to %s. Useful for '
- 'running any Tast tests that execute these '
- 'binaries.' % _CHROME_TEST_BIN_DIR)
- parser.add_argument('--lacros', action='store_true', default=False,
- help='Deploys lacros-chrome rather than ash-chrome.')
- parser.add_argument('--reset-lacros', action='store_true', default=False,
- help='Reset Lacros by deleting Lacros user data dir if '
- 'exists.')
- parser.add_argument('--skip-modifying-config-file', action='store_false',
- dest='modify_config_file',
- help='By default, deploying lacros-chrome modifies the '
- '/etc/chrome_dev.conf file, which interferes with '
- 'automated testing, and this argument disables it.')
- parser.add_argument('--use-external-config', action='store_true',
- help='When identifying the configuration for a board, '
- 'force usage of the external configuration if both '
- 'internal and external are available. This only '
- 'has an effect when stripping Chrome, i.e. when '
- '--nostrip is not passed in.')
+ # TODO(rcui): Have this use the UI-V2 format of having source and target
+ # device be specified as positional arguments.
+ parser.add_argument(
+ "--force",
+ action="store_true",
+ default=False,
+ help="Skip all prompts (such as the prompt for disabling "
+ "of rootfs verification). This may result in the "
+ "target machine being rebooted.",
+ )
+ sdk_board_env = os.environ.get(cros_chrome_sdk.SDKFetcher.SDK_BOARD_ENV)
+ parser.add_argument(
+ "--board",
+ default=sdk_board_env,
+ help="The board the Chrome build is targeted for. When "
+ "in a 'cros chrome-sdk' shell, defaults to the SDK "
+ "board.",
+ )
+ parser.add_argument(
+ "--build-dir",
+ type="path",
+ help="The directory with Chrome build artifacts to "
+ "deploy from. Typically of format "
+ "<chrome_root>/out/Debug. When this option is used, "
+ "the GN_ARGS environment variable must be set.",
+ )
+ parser.add_argument(
+ "--target-dir",
+ type="path",
+ default=None,
+ help="Target directory on device to deploy Chrome into.",
+ )
+ parser.add_argument(
+ "-g",
+ "--gs-path",
+ type="gs_path",
+ help="GS path that contains the chrome to deploy.",
+ )
+ parser.add_argument(
+ "--private-key",
+ type="path",
+ default=None,
+ help="An ssh private key to use when deploying to " "a CrOS device.",
+ )
+ parser.add_argument(
+ "--nostartui",
+ action="store_false",
+ dest="startui",
+ default=True,
+ help="Don't restart the ui daemon after deployment.",
+ )
+ parser.add_argument(
+ "--nostrip",
+ action="store_false",
+ dest="dostrip",
+ default=True,
+ help="Don't strip binaries during deployment. Warning: "
+ "the resulting binaries will be very large!",
+ )
+ parser.add_argument(
+ "-d",
+ "--device",
+ type=commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH),
+ help="Device hostname or IP in the format hostname[:port].",
+ )
+ parser.add_argument(
+ "--mount-dir",
+ type="path",
+ default=None,
+ help="Deploy Chrome in target directory and bind it "
+ "to the directory specified by this flag."
+ "Any existing mount on this directory will be "
+ "umounted first.",
+ )
+ parser.add_argument(
+ "--mount",
+ action="store_true",
+ default=False,
+ help="Deploy Chrome to default target directory and bind "
+ "it to the default mount directory."
+ "Any existing mount on this directory will be "
+ "umounted first.",
+ )
+ parser.add_argument(
+ "--noremove-rootfs-verification",
+ action="store_true",
+ default=False,
+ help="Never remove rootfs verification.",
+ )
+ parser.add_argument(
+ "--deploy-test-binaries",
+ action="store_true",
+ default=False,
+ help="Also deploy any test binaries to %s. Useful for "
+ "running any Tast tests that execute these "
+ "binaries." % _CHROME_TEST_BIN_DIR,
+ )
+ parser.add_argument(
+ "--lacros",
+ action="store_true",
+ default=False,
+ help="Deploys lacros-chrome rather than ash-chrome.",
+ )
+ parser.add_argument(
+ "--reset-lacros",
+ action="store_true",
+ default=False,
+ help="Reset Lacros by deleting Lacros user data dir if " "exists.",
+ )
+ parser.add_argument(
+ "--skip-modifying-config-file",
+ action="store_false",
+ dest="modify_config_file",
+ help="By default, deploying lacros-chrome modifies the "
+ "/etc/chrome_dev.conf file, which interferes with "
+ "automated testing, and this argument disables it.",
+ )
+ parser.add_argument(
+ "--use-external-config",
+ action="store_true",
+ help="When identifying the configuration for a board, "
+ "force usage of the external configuration if both "
+ "internal and external are available. This only "
+ "has an effect when stripping Chrome, i.e. when "
+ "--nostrip is not passed in.",
+ )
- group = parser.add_argument_group('Advanced Options')
- group.add_argument('-l', '--local-pkg-path', type='path',
- help='Path to local chrome prebuilt package to deploy.')
- group.add_argument('--sloppy', action='store_true', default=False,
- help='Ignore when mandatory artifacts are missing.')
- group.add_argument('--staging-flags', default=None, type=ValidateStagingFlags,
- help=('Extra flags to control staging. Valid flags are - '
- '%s' % ', '.join(chrome_util.STAGING_FLAGS)))
- # TODO(stevenjb): Remove --strict entirely once removed from the ebuild.
- group.add_argument('--strict', action='store_true', default=False,
- help='Deprecated. Default behavior is "strict". Use '
- '--sloppy to omit warnings for missing optional '
- 'files.')
- group.add_argument('--strip-flags', default=None,
- help="Flags to call the 'strip' binutil tool with. "
- 'Overrides the default arguments.')
- group.add_argument('--ping', action='store_true', default=False,
- help='Ping the device before connection attempt.')
- group.add_argument('--process-timeout', type=int,
- default=KILL_PROC_MAX_WAIT,
- help='Timeout for process shutdown.')
+ group = parser.add_argument_group("Advanced Options")
+ group.add_argument(
+ "-l",
+ "--local-pkg-path",
+ type="path",
+ help="Path to local chrome prebuilt package to deploy.",
+ )
+ group.add_argument(
+ "--sloppy",
+ action="store_true",
+ default=False,
+ help="Ignore when mandatory artifacts are missing.",
+ )
+ group.add_argument(
+ "--staging-flags",
+ default=None,
+ type=ValidateStagingFlags,
+ help=(
+ "Extra flags to control staging. Valid flags are - "
+ "%s" % ", ".join(chrome_util.STAGING_FLAGS)
+ ),
+ )
+ # TODO(stevenjb): Remove --strict entirely once removed from the ebuild.
+ group.add_argument(
+ "--strict",
+ action="store_true",
+ default=False,
+ help='Deprecated. Default behavior is "strict". Use '
+ "--sloppy to omit warnings for missing optional "
+ "files.",
+ )
+ group.add_argument(
+ "--strip-flags",
+ default=None,
+ help="Flags to call the 'strip' binutil tool with. "
+ "Overrides the default arguments.",
+ )
+ group.add_argument(
+ "--ping",
+ action="store_true",
+ default=False,
+ help="Ping the device before connection attempt.",
+ )
+ group.add_argument(
+ "--process-timeout",
+ type=int,
+ default=KILL_PROC_MAX_WAIT,
+ help="Timeout for process shutdown.",
+ )
- group = parser.add_argument_group(
- 'Metadata Overrides (Advanced)',
- description='Provide all of these overrides in order to remove '
- 'dependencies on metadata.json existence.')
- group.add_argument('--target-tc', action='store', default=None,
- help='Override target toolchain name, e.g. '
- 'x86_64-cros-linux-gnu')
- group.add_argument('--toolchain-url', action='store', default=None,
- help='Override toolchain url format pattern, e.g. '
- '2014/04/%%(target)s-2014.04.23.220740.tar.xz')
+ group = parser.add_argument_group(
+ "Metadata Overrides (Advanced)",
+ description="Provide all of these overrides in order to remove "
+ "dependencies on metadata.json existence.",
+ )
+ group.add_argument(
+ "--target-tc",
+ action="store",
+ default=None,
+ help="Override target toolchain name, e.g. " "x86_64-cros-linux-gnu",
+ )
+ group.add_argument(
+ "--toolchain-url",
+ action="store",
+ default=None,
+ help="Override toolchain url format pattern, e.g. "
+ "2014/04/%%(target)s-2014.04.23.220740.tar.xz",
+ )
- # DEPRECATED: --gyp-defines is ignored, but retained for backwards
- # compatibility. TODO(stevenjb): Remove once eliminated from the ebuild.
- parser.add_argument('--gyp-defines', default=None, type=ValidateStagingFlags,
- help=argparse.SUPPRESS)
+ # DEPRECATED: --gyp-defines is ignored, but retained for backwards
+ # compatibility. TODO(stevenjb): Remove once eliminated from the ebuild.
+ parser.add_argument(
+ "--gyp-defines",
+ default=None,
+ type=ValidateStagingFlags,
+ help=argparse.SUPPRESS,
+ )
- # GN_ARGS (args.gn) used to build Chrome. Influences which files are staged
- # when --build-dir is set. Defaults to reading from the GN_ARGS env variable.
- # CURRENLY IGNORED, ADDED FOR FORWARD COMPATABILITY.
- parser.add_argument('--gn-args', default=None, type=ValidateGnArgs,
- help=argparse.SUPPRESS)
+ # GN_ARGS (args.gn) used to build Chrome. Influences which files are staged
+ # when --build-dir is set. Defaults to reading from the GN_ARGS env variable.
+ # CURRENLY IGNORED, ADDED FOR FORWARD COMPATABILITY.
+ parser.add_argument(
+ "--gn-args", default=None, type=ValidateGnArgs, help=argparse.SUPPRESS
+ )
- # Path of an empty directory to stage chrome artifacts to. Defaults to a
- # temporary directory that is removed when the script finishes. If the path
- # is specified, then it will not be removed.
- parser.add_argument('--staging-dir', type='path', default=None,
- help=argparse.SUPPRESS)
- # Only prepare the staging directory, and skip deploying to the device.
- parser.add_argument('--staging-only', action='store_true', default=False,
- help=argparse.SUPPRESS)
- # Uploads the compressed staging directory to the given gs:// path URI.
- parser.add_argument('--staging-upload', type='gs_path',
- help='GS path to upload the compressed staging files to.')
- # Used alongside --staging-upload to upload with public-read ACL.
- parser.add_argument('--public-read', action='store_true', default=False,
- help='GS path to upload the compressed staging files to.')
- # Path to a binutil 'strip' tool to strip binaries with. The passed-in path
- # is used as-is, and not normalized. Used by the Chrome ebuild to skip
- # fetching the SDK toolchain.
- parser.add_argument('--strip-bin', default=None, help=argparse.SUPPRESS)
- parser.add_argument('--compress', action='store', default='auto',
- choices=('always', 'never', 'auto'),
- help='Choose the data compression behavior. Default '
- 'is set to "auto", that disables compression if '
- 'the target device has a gigabit ethernet port.')
- return parser
+ # Path of an empty directory to stage chrome artifacts to. Defaults to a
+ # temporary directory that is removed when the script finishes. If the path
+ # is specified, then it will not be removed.
+ parser.add_argument(
+ "--staging-dir", type="path", default=None, help=argparse.SUPPRESS
+ )
+ # Only prepare the staging directory, and skip deploying to the device.
+ parser.add_argument(
+ "--staging-only",
+ action="store_true",
+ default=False,
+ help=argparse.SUPPRESS,
+ )
+ # Uploads the compressed staging directory to the given gs:// path URI.
+ parser.add_argument(
+ "--staging-upload",
+ type="gs_path",
+ help="GS path to upload the compressed staging files to.",
+ )
+ # Used alongside --staging-upload to upload with public-read ACL.
+ parser.add_argument(
+ "--public-read",
+ action="store_true",
+ default=False,
+ help="GS path to upload the compressed staging files to.",
+ )
+ # Path to a binutil 'strip' tool to strip binaries with. The passed-in path
+ # is used as-is, and not normalized. Used by the Chrome ebuild to skip
+ # fetching the SDK toolchain.
+ parser.add_argument("--strip-bin", default=None, help=argparse.SUPPRESS)
+ parser.add_argument(
+ "--compress",
+ action="store",
+ default="auto",
+ choices=("always", "never", "auto"),
+ help="Choose the data compression behavior. Default "
+ 'is set to "auto", that disables compression if '
+ "the target device has a gigabit ethernet port.",
+ )
+ return parser
def _ParseCommandLine(argv):
- """Parse args, and run environment-independent checks."""
- parser = _CreateParser()
- options = parser.parse_args(argv)
+ """Parse args, and run environment-independent checks."""
+ parser = _CreateParser()
+ options = parser.parse_args(argv)
- if not any([options.gs_path, options.local_pkg_path, options.build_dir]):
- parser.error('Need to specify either --gs-path, --local-pkg-path, or '
- '--build-dir')
- if options.build_dir and any([options.gs_path, options.local_pkg_path]):
- parser.error('Cannot specify both --build_dir and '
- '--gs-path/--local-pkg-patch')
- if options.lacros:
- if options.dostrip and not options.board:
- parser.error('Please specify --board.')
- if options.mount_dir or options.mount:
- parser.error('--lacros does not support --mount or --mount-dir')
- if options.deploy_test_binaries:
- parser.error('--lacros does not support --deploy-test-binaries')
- if options.local_pkg_path:
- parser.error('--lacros does not support --local-pkg-path')
- else:
- if not options.board and options.build_dir:
- match = re.search(r'out_([^/]+)/Release$', options.build_dir)
- if match:
- options.board = match.group(1)
- logging.info('--board is set to %s', options.board)
- if not options.board:
- parser.error('--board is required')
- if options.gs_path and options.local_pkg_path:
- parser.error('Cannot specify both --gs-path and --local-pkg-path')
- if not (options.staging_only or options.device):
- parser.error('Need to specify --device')
- if options.staging_flags and not options.build_dir:
- parser.error('--staging-flags require --build-dir to be set.')
+ if not any([options.gs_path, options.local_pkg_path, options.build_dir]):
+ parser.error(
+ "Need to specify either --gs-path, --local-pkg-path, or "
+ "--build-dir"
+ )
+ if options.build_dir and any([options.gs_path, options.local_pkg_path]):
+ parser.error(
+ "Cannot specify both --build_dir and " "--gs-path/--local-pkg-patch"
+ )
+ if options.lacros:
+ if options.dostrip and not options.board:
+ parser.error("Please specify --board.")
+ if options.mount_dir or options.mount:
+ parser.error("--lacros does not support --mount or --mount-dir")
+ if options.deploy_test_binaries:
+ parser.error("--lacros does not support --deploy-test-binaries")
+ if options.local_pkg_path:
+ parser.error("--lacros does not support --local-pkg-path")
+ else:
+ if not options.board and options.build_dir:
+ match = re.search(r"out_([^/]+)/Release$", options.build_dir)
+ if match:
+ options.board = match.group(1)
+ logging.info("--board is set to %s", options.board)
+ if not options.board:
+ parser.error("--board is required")
+ if options.gs_path and options.local_pkg_path:
+ parser.error("Cannot specify both --gs-path and --local-pkg-path")
+ if not (options.staging_only or options.device):
+ parser.error("Need to specify --device")
+ if options.staging_flags and not options.build_dir:
+ parser.error("--staging-flags require --build-dir to be set.")
- if options.strict:
- logging.warning('--strict is deprecated.')
- if options.gyp_defines:
- logging.warning('--gyp-defines is deprecated.')
+ if options.strict:
+ logging.warning("--strict is deprecated.")
+ if options.gyp_defines:
+ logging.warning("--gyp-defines is deprecated.")
- if options.mount or options.mount_dir:
- if not options.target_dir:
- options.target_dir = _CHROME_DIR_MOUNT
- else:
- if not options.target_dir:
- options.target_dir = LACROS_DIR if options.lacros else _CHROME_DIR
+ if options.mount or options.mount_dir:
+ if not options.target_dir:
+ options.target_dir = _CHROME_DIR_MOUNT
+ else:
+ if not options.target_dir:
+ options.target_dir = LACROS_DIR if options.lacros else _CHROME_DIR
- if options.mount and not options.mount_dir:
- options.mount_dir = _CHROME_DIR
+ if options.mount and not options.mount_dir:
+ options.mount_dir = _CHROME_DIR
- return options
+ return options
def _PostParseCheck(options):
- """Perform some usage validation (after we've parsed the arguments).
+ """Perform some usage validation (after we've parsed the arguments).
- Args:
- options: The options object returned by the cli parser.
- """
- if options.local_pkg_path and not os.path.isfile(options.local_pkg_path):
- cros_build_lib.Die('%s is not a file.', options.local_pkg_path)
+ Args:
+ options: The options object returned by the cli parser.
+ """
+ if options.local_pkg_path and not os.path.isfile(options.local_pkg_path):
+ cros_build_lib.Die("%s is not a file.", options.local_pkg_path)
- if not options.gn_args:
- gn_env = os.getenv('GN_ARGS')
- if gn_env is not None:
- options.gn_args = gn_helpers.FromGNArgs(gn_env)
- logging.debug('GN_ARGS taken from environment: %s', options.gn_args)
+ if not options.gn_args:
+ gn_env = os.getenv("GN_ARGS")
+ if gn_env is not None:
+ options.gn_args = gn_helpers.FromGNArgs(gn_env)
+ logging.debug("GN_ARGS taken from environment: %s", options.gn_args)
- if not options.staging_flags:
- use_env = os.getenv('USE')
- if use_env is not None:
- options.staging_flags = ' '.join(set(use_env.split()).intersection(
- chrome_util.STAGING_FLAGS))
- logging.info('Staging flags taken from USE in environment: %s',
- options.staging_flags)
+ if not options.staging_flags:
+ use_env = os.getenv("USE")
+ if use_env is not None:
+ options.staging_flags = " ".join(
+ set(use_env.split()).intersection(chrome_util.STAGING_FLAGS)
+ )
+ logging.info(
+ "Staging flags taken from USE in environment: %s",
+ options.staging_flags,
+ )
def _FetchChromePackage(cache_dir, tempdir, gs_path):
- """Get the chrome prebuilt tarball from GS.
+ """Get the chrome prebuilt tarball from GS.
- Returns:
- Path to the fetched chrome tarball.
- """
- gs_ctx = gs.GSContext(cache_dir=cache_dir, init_boto=True)
- files = gs_ctx.LS(gs_path)
- files = [found for found in files if
- _UrlBaseName(found).startswith('%s-' % constants.CHROME_PN)]
- if not files:
- raise Exception('No chrome package found at %s' % gs_path)
- elif len(files) > 1:
- # - Users should provide us with a direct link to either a stripped or
- # unstripped chrome package.
- # - In the case of being provided with an archive directory, where both
- # stripped and unstripped chrome available, use the stripped chrome
- # package.
- # - Stripped chrome pkg is chromeos-chrome-<version>.tar.gz
- # - Unstripped chrome pkg is chromeos-chrome-<version>-unstripped.tar.gz.
- files = [f for f in files if not 'unstripped' in f]
- assert len(files) == 1
- logging.warning('Multiple chrome packages found. Using %s', files[0])
+ Returns:
+ Path to the fetched chrome tarball.
+ """
+ gs_ctx = gs.GSContext(cache_dir=cache_dir, init_boto=True)
+ files = gs_ctx.LS(gs_path)
+ files = [
+ found
+ for found in files
+ if _UrlBaseName(found).startswith("%s-" % constants.CHROME_PN)
+ ]
+ if not files:
+ raise Exception("No chrome package found at %s" % gs_path)
+ elif len(files) > 1:
+ # - Users should provide us with a direct link to either a stripped or
+ # unstripped chrome package.
+ # - In the case of being provided with an archive directory, where both
+ # stripped and unstripped chrome available, use the stripped chrome
+ # package.
+ # - Stripped chrome pkg is chromeos-chrome-<version>.tar.gz
+ # - Unstripped chrome pkg is chromeos-chrome-<version>-unstripped.tar.gz.
+ files = [f for f in files if not "unstripped" in f]
+ assert len(files) == 1
+ logging.warning("Multiple chrome packages found. Using %s", files[0])
- filename = _UrlBaseName(files[0])
- logging.info('Fetching %s...', filename)
- gs_ctx.Copy(files[0], tempdir, print_cmd=False)
- chrome_path = os.path.join(tempdir, filename)
- assert os.path.exists(chrome_path)
- return chrome_path
+ filename = _UrlBaseName(files[0])
+ logging.info("Fetching %s...", filename)
+ gs_ctx.Copy(files[0], tempdir, print_cmd=False)
+ chrome_path = os.path.join(tempdir, filename)
+ assert os.path.exists(chrome_path)
+ return chrome_path
@contextlib.contextmanager
def _StripBinContext(options):
- if not options.dostrip:
- yield None
- elif options.strip_bin:
- yield options.strip_bin
- else:
- sdk = cros_chrome_sdk.SDKFetcher(
- options.cache_dir, options.board,
- use_external_config=options.use_external_config)
- components = (sdk.TARGET_TOOLCHAIN_KEY, constants.CHROME_ENV_TAR)
- with sdk.Prepare(components=components, target_tc=options.target_tc,
- toolchain_url=options.toolchain_url) as ctx:
- env_path = os.path.join(ctx.key_map[constants.CHROME_ENV_TAR].path,
- constants.CHROME_ENV_FILE)
- strip_bin = osutils.SourceEnvironment(env_path, ['STRIP'])['STRIP']
- strip_bin = os.path.join(ctx.key_map[sdk.TARGET_TOOLCHAIN_KEY].path,
- 'bin', os.path.basename(strip_bin))
- yield strip_bin
-
-
-def _UploadStagingDir(options: commandline.ArgumentNamespace, tempdir: str,
- staging_dir: str) -> None:
- """Uploads the compressed staging directory.
-
- Args:
- options: options object.
- tempdir: Scratch space.
- staging_dir: Directory staging chrome files.
- """
- staging_tarball_path = os.path.join(tempdir, _CHROME_DIR_STAGING_TARBALL_ZSTD)
- logging.info('Compressing staging dir (%s) to (%s)',
- staging_dir, staging_tarball_path)
- cros_build_lib.CreateTarball(
- staging_tarball_path,
- staging_dir,
- compression=cros_build_lib.COMP_ZSTD,
- extra_env={'ZSTD_CLEVEL': '9'})
- logging.info('Uploading staging tarball (%s) into %s',
- staging_tarball_path, options.staging_upload)
- ctx = gs.GSContext()
- ctx.Copy(staging_tarball_path, options.staging_upload,
- acl='public-read' if options.public_read else '')
-
-
-def _PrepareStagingDir(options, tempdir, staging_dir, copy_paths=None,
- chrome_dir=None):
- """Place the necessary files in the staging directory.
-
- The staging directory is the directory used to rsync the build artifacts over
- to the device. Only the necessary Chrome build artifacts are put into the
- staging directory.
- """
- if chrome_dir is None:
- chrome_dir = LACROS_DIR if options.lacros else _CHROME_DIR
- osutils.SafeMakedirs(staging_dir)
- os.chmod(staging_dir, 0o755)
- if options.build_dir:
- with _StripBinContext(options) as strip_bin:
- strip_flags = (None if options.strip_flags is None else
- shlex.split(options.strip_flags))
- chrome_util.StageChromeFromBuildDir(
- staging_dir, options.build_dir, strip_bin,
- sloppy=options.sloppy, gn_args=options.gn_args,
- staging_flags=options.staging_flags,
- strip_flags=strip_flags, copy_paths=copy_paths)
- else:
- pkg_path = options.local_pkg_path
- if options.gs_path:
- pkg_path = _FetchChromePackage(options.cache_dir, tempdir,
- options.gs_path)
-
- assert pkg_path
- logging.info('Extracting %s...', pkg_path)
- # Extract only the ./opt/google/chrome contents, directly into the staging
- # dir, collapsing the directory hierarchy.
- if pkg_path[-4:] == '.zip':
- cros_build_lib.dbg_run(
- ['unzip', '-X', pkg_path, _ANDROID_DIR_EXTRACT_PATH, '-d',
- staging_dir])
- for filename in glob.glob(os.path.join(staging_dir, 'system/chrome/*')):
- shutil.move(filename, staging_dir)
- osutils.RmDir(os.path.join(staging_dir, 'system'), ignore_missing=True)
+ if not options.dostrip:
+ yield None
+ elif options.strip_bin:
+ yield options.strip_bin
else:
- compression = cros_build_lib.CompressionDetectType(pkg_path)
- compressor = cros_build_lib.FindCompressor(compression)
- cros_build_lib.dbg_run(
- ['tar', '--strip-components', '4', '--extract', '-I', compressor,
- '--preserve-permissions', '--file', pkg_path, '.%s' % chrome_dir],
- cwd=staging_dir)
+ sdk = cros_chrome_sdk.SDKFetcher(
+ options.cache_dir,
+ options.board,
+ use_external_config=options.use_external_config,
+ )
+ components = (sdk.TARGET_TOOLCHAIN_KEY, constants.CHROME_ENV_TAR)
+ with sdk.Prepare(
+ components=components,
+ target_tc=options.target_tc,
+ toolchain_url=options.toolchain_url,
+ ) as ctx:
+ env_path = os.path.join(
+ ctx.key_map[constants.CHROME_ENV_TAR].path,
+ constants.CHROME_ENV_FILE,
+ )
+ strip_bin = osutils.SourceEnvironment(env_path, ["STRIP"])["STRIP"]
+ strip_bin = os.path.join(
+ ctx.key_map[sdk.TARGET_TOOLCHAIN_KEY].path,
+ "bin",
+ os.path.basename(strip_bin),
+ )
+ yield strip_bin
- if options.staging_upload:
- _UploadStagingDir(options, tempdir, staging_dir)
+
+def _UploadStagingDir(
+ options: commandline.ArgumentNamespace, tempdir: str, staging_dir: str
+) -> None:
+ """Uploads the compressed staging directory.
+
+ Args:
+ options: options object.
+ tempdir: Scratch space.
+ staging_dir: Directory staging chrome files.
+ """
+ staging_tarball_path = os.path.join(
+ tempdir, _CHROME_DIR_STAGING_TARBALL_ZSTD
+ )
+ logging.info(
+ "Compressing staging dir (%s) to (%s)",
+ staging_dir,
+ staging_tarball_path,
+ )
+ cros_build_lib.CreateTarball(
+ staging_tarball_path,
+ staging_dir,
+ compression=cros_build_lib.COMP_ZSTD,
+ extra_env={"ZSTD_CLEVEL": "9"},
+ )
+ logging.info(
+ "Uploading staging tarball (%s) into %s",
+ staging_tarball_path,
+ options.staging_upload,
+ )
+ ctx = gs.GSContext()
+ ctx.Copy(
+ staging_tarball_path,
+ options.staging_upload,
+ acl="public-read" if options.public_read else "",
+ )
+
+
+def _PrepareStagingDir(
+ options, tempdir, staging_dir, copy_paths=None, chrome_dir=None
+):
+ """Place the necessary files in the staging directory.
+
+ The staging directory is the directory used to rsync the build artifacts over
+ to the device. Only the necessary Chrome build artifacts are put into the
+ staging directory.
+ """
+ if chrome_dir is None:
+ chrome_dir = LACROS_DIR if options.lacros else _CHROME_DIR
+ osutils.SafeMakedirs(staging_dir)
+ os.chmod(staging_dir, 0o755)
+ if options.build_dir:
+ with _StripBinContext(options) as strip_bin:
+ strip_flags = (
+ None
+ if options.strip_flags is None
+ else shlex.split(options.strip_flags)
+ )
+ chrome_util.StageChromeFromBuildDir(
+ staging_dir,
+ options.build_dir,
+ strip_bin,
+ sloppy=options.sloppy,
+ gn_args=options.gn_args,
+ staging_flags=options.staging_flags,
+ strip_flags=strip_flags,
+ copy_paths=copy_paths,
+ )
+ else:
+ pkg_path = options.local_pkg_path
+ if options.gs_path:
+ pkg_path = _FetchChromePackage(
+ options.cache_dir, tempdir, options.gs_path
+ )
+
+ assert pkg_path
+ logging.info("Extracting %s...", pkg_path)
+ # Extract only the ./opt/google/chrome contents, directly into the staging
+ # dir, collapsing the directory hierarchy.
+ if pkg_path[-4:] == ".zip":
+ cros_build_lib.dbg_run(
+ [
+ "unzip",
+ "-X",
+ pkg_path,
+ _ANDROID_DIR_EXTRACT_PATH,
+ "-d",
+ staging_dir,
+ ]
+ )
+ for filename in glob.glob(
+ os.path.join(staging_dir, "system/chrome/*")
+ ):
+ shutil.move(filename, staging_dir)
+ osutils.RmDir(
+ os.path.join(staging_dir, "system"), ignore_missing=True
+ )
+ else:
+ compression = cros_build_lib.CompressionDetectType(pkg_path)
+ compressor = cros_build_lib.FindCompressor(compression)
+ cros_build_lib.dbg_run(
+ [
+ "tar",
+ "--strip-components",
+ "4",
+ "--extract",
+ "-I",
+ compressor,
+ "--preserve-permissions",
+ "--file",
+ pkg_path,
+ ".%s" % chrome_dir,
+ ],
+ cwd=staging_dir,
+ )
+
+ if options.staging_upload:
+ _UploadStagingDir(options, tempdir, staging_dir)
def main(argv):
- options = _ParseCommandLine(argv)
- _PostParseCheck(options)
+ options = _ParseCommandLine(argv)
+ _PostParseCheck(options)
- with osutils.TempDir(set_global=True) as tempdir:
- staging_dir = options.staging_dir
- if not staging_dir:
- staging_dir = os.path.join(tempdir, 'chrome')
+ with osutils.TempDir(set_global=True) as tempdir:
+ staging_dir = options.staging_dir
+ if not staging_dir:
+ staging_dir = os.path.join(tempdir, "chrome")
- deploy = DeployChrome(options, tempdir, staging_dir)
- try:
- deploy.Perform()
- except failures_lib.StepFailure as ex:
- raise SystemExit(str(ex).strip())
- deploy.Cleanup()
+ deploy = DeployChrome(options, tempdir, staging_dir)
+ try:
+ deploy.Perform()
+ except failures_lib.StepFailure as ex:
+ raise SystemExit(str(ex).strip())
+ deploy.Cleanup()