The dev-install tool is used to install developer tools on a release image. This is most used by external CrOS users who want to take their existing CrOS system, put it into dev mode, and then quickly install a bunch of useful tools.
It‘s generally intended to create the equivalent of a dev image that one would build using ./build_image ... dev
. We aren’t able to fully recreate that environment currently, but we get close.
It is not meant to create a fully standalone environment where one can build new packages from source and have them installed. For that, people should look into Crouton or Crostini instead.
Note: When we refer to the “stateful” partition, we usually refer to all of the related content that lives in it. The /var
and /usr/local
paths are actually bind mounted from directories that live in the stateful partition. It's used for other things as well, but dev-install only cares about those two particular paths.
In order to understand why we configure certain paths, we need to understand what environments & constraints we operate under.
There are basically four possible environments we need to consider:
/usr/local
is never mounted.crossystem "cros_debug?1"
)./usr/local
is mounted with exec permissions./var/db/pkg
(that tracks installed packages) is not available../build_image dev
).virtual/target-os-dev
) are installed into /usr/local
for developers to help with ad-hoc testing & verification./usr/local
to work correctly./var/db/pkg
(that tracks installed packages) is available../build_image test
).virtual/target-os-test
) are installed into /usr/local
in order to run automated testing (e.g. autotest & tast).It's important to emphasize that dev-install is only for helping developers get easy access to various developer tools when their device is in dev mode. We must not sacrifice security or stability of the system otherwise. That is why we strive to have tools be disabled/ineffective normally.
On a non-dev mode system, the /usr/local
path is never mounted. This allows us to create dangling symlinks in the rootfs that point to paths under /usr/local
without introducing security problems. Even then, we try to keep this to a minimum.
If we refer back to the Environments section, dev-install is designed to:
In order to achieve these goals, dev-install ends up configuring the base image in a way that any changes needed in the rootfs are always available there, and not just when creating a dev image.
When a developer runs ./build_image ... dev
, the build system will first create a “base” image which is equivalent to a release image. The GPT layout is initialized as are the various partitions (kernel, rootfs, EFI, stateful, etc...). The important part here is that the rootfs is not modified at all from a normal release setup, and that the stateful partition is empty. Most notably, /var
and /usr/local
will be empty. Everything in the depgraph of virtual/target-os
is installed into the rootfs.
When the dev image is created, a copy of the base image is created, and then a lot of extra packages are installed automatically into the stateful partition. This means /usr/local
will be initialized with a lot of packages -- everything in the depgraph of virtual/target-os-dev
(except what's already installed into the rootfs). We also modify the rootfs a bit to add symlinks to paths under /usr/local
.
We'll assume that test images are basically the same as dev images, except they have even more things installed, and some modifications to the rootfs are made. For example, a well known password & key are installed for automatic ssh access.
During build_image
, we generate the set of installable packages. See create_dev_install_lists for exact implementation details. Roughly, it builds a list of all the packages by taking the depgraphs of virtual/target-os-dev & virtual/target-os-test and subtracting the depgraph of virtual/target-os (since those packages are installed in the rootfs). That list is saved at /build/dev-install/package.installable
.
Release builders run the DevInstallerPrebuilts stage. This reads the set of packages (created above) and uploads their binpkgs to the gs://chromeos-dev-installer
bucket.
That bucket uses the layout gs://chromeos-dev-installer/board/${BOARD}/${OS_VERSION}/
. For example, gs://chromeos-dev-installer/board/eve/12763.0.0/
.
For local developer builds, the full URI is written to CHROMEOS_DEVSERVER
in the lsb-release file and used by the dev-install
command at runtime. The URI will point to the local developer's system, not the release bucket.
For release builds, the value is computed on the fly using lsb-release.
This is a normal Portage binpkg host repository.
Depending on whether the rootfs is a base or dev image, the set of available paths will be different. This tries to document the current state of the world, not to say that the status-quo is perfect (we know it's not) or otherwise immutable.
All symlinks on the system at this point are from the dev-install package. Once that‘s installed, we also generate package lists and a tarball of the existing /uar/local
state (otherwise we’d discard it).
Paths installed by the dev-install
package:
/etc/bash/bashrc.d/dev-install.sh
: Helper script for shells in dev mode to pass along the environment when using plain sudo
. Loaded by every interactive bash shell. A bit of a hack.
/etc/env.d/99devinstall
: Adds the /usr/local/lib*
paths to the LD_LIBRARY_PATH
env var so that extra programs and their libraries that are installed under /usr/local
can find them automatically. Normally only libraries listed in /etc/ld.so.cache
are automatically loaded, and that cache only includes files that are in the rootfs. This file is added to /etc/profile.env
which in is loaded by /etc/profile
which is only loaded by interactive shells. Since non-dev mode devices should never have any interactive shells run on them, this is OK.
/usr/bin/dev_install
: The main program to download & install packages on the fly into /usr/local
. This is run manually by end users when they put their device into dev mode.
/usr/share/dev-install/portage/
: Various files used to provide a simple stub Portage profile. This allows people to run emerge
to install the various binary packages we make available.
make.profile/
: A minimalistic Portage profile needed to install the binpkgs. Dynamically loads settings from /usr/local/
as needed.make.defaults
: The main profile file./etc/portage/
: This is symlinked to /usr/share/dev-install/portage/
on the device. We skip this in the build sysroot as that will be the normal “full” profile used to compile everything in the SDK.
/usr/local/etc/portage/make.profile/parent
: A stub profile that inherits from the rootfs profile. This allows users to install extra settings after the system has been set up. Only created during build_image
time.
Paths created on the fly by build_image
:
/build/dev-install/package.installable
: Not actually installed on the device, just created on the fly for use by the build system to determine which binpkgs to upload to the server.dev-only-extras.tar.xz
: Everything in /usr/local
(except for /var
)./usr/share/dev-install/
: The list of packages used by the device are computed at this point as the image has been finalized.bootstrap.packages
: The set of packages to manually install in order to bootstrap Portage. Once these are installed, emerge
is used to install all the rest of the binpkgs.rootfs.provided/
: For packages that are baked into the rootfs, we save different lists in this directory. This is used by dev_install
at runtime to mark them all as provided. We have to do this since the /var/db/pkg
metadata is not available.chromeos-base.packages
: The virtual/target-os
packages.Various python & portage symlinks are created in the rootfs to point to paths under /usr/local
. This is important for scripts that hardcode #!/usr/bin/python
in their script shebangs, and for packages that hardcode the path at compile time. We install these into the base image so they work with dev_install
too.
/etc/env.d/python
: Python config./usr/bin/python*
: Python programs./usr/lib/python-exec
: Gentoo Python wrapper./usr/lib/portage
: Portage runtime files./usr/share/portage/
: Portage settings.We install a set of additional symlinks and tweak files for dev images.
/
: A few additional packages from chromeos-base/chromeos-dev-root are installed to the rootfs.
usr/lib/debug
: A symlink to /usr/local/usr/lib/debug
so debug files can be found if available./usr/local
: All the virtual/target-os-dev packages are installed here.
etc/passwd
: Symlink to /etc/passwd
so dev-only packages which add accounts can update the rootfs settings.etc/group
: Same as above.etc/pam.d/
: Same as above.There are a few other files that get adapted, but they don‘t have bearing on the dev-install process, so we’ll ignore them.
Here we outline the expected workflows for developers and what is supported. Other flows might work, but only the ones detailed here need to be verified.
We expect people to use dev-install
to install the set of binary packages and initialize the /usr/local
tree. Once it's finished running, emerge
will be available to install more. It needs to be able to bootstrap from an empty /usr/local
tree.
The /var/db/pkg
will not be available, so packages installed in the rootfs won't be possible to upgrade.
Developers may use cros deploy
to install additional packages, or upgrade any existing ones as long as they were installed to /usr/local
.
Developers may also use other cros
helpers as makes sense e.g. cros flash
.
A normal dev image will already have the stateful partition set up.
It will have the full /var/db/pkg
tree available that reflects the state of the rootfs.
Developers may use cros deploy
to upgrade packages in the rootfs as well as /usr/local
, and may install additional packages to either path.
Running dev-install
is not required. If the stateful is wiped (for any reason), then dev-install
may be used to reinstall the developer packages. However, the /var/db/pkg
path is not restored, so this state will be more like a base image rather than a dev image.
Developers may also use other cros
helpers as makes sense e.g. cros flash
.