Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tracked command abstraction to bootstrap #126564

Closed
wants to merge 3 commits into from

Conversation

Kobzol
Copy link
Contributor

@Kobzol Kobzol commented Jun 16, 2024

Boostrap currently accesses its external environment (the filesystem, process spawning etc.) in completely arbitrary ways, where essentially any function in bootstrap can just invoke a command. While this makes programming easier, it also makes bootstrap quite challenging to test, debug and profile.

As one possible improvement, it would be great if we could route all command invocations through a single location. This would enable us to log all command invocations, measure how long do they take, and in the future even potentially mock them somehow, to simplify testing.

For that, we need to have some custom representation of commands, using std::process::Command diretly won't do. There is already BootstrapCommand, but that is mostly just a very light refactoring of code that was spawning commands through Build(er), with various idiosyncracies (like the output and fail modes). In this PR, I added a new TrackedCommand struct, with which I'd ultimately like to replace all current Command invocations in bootstrap (step 1), and then also the current commands that are already routed through Build(er) (step 2, after which BootstrapCommand can be removed).

The first commits adds the command struct (with a few basic APIs), while the second commit demonstrates its usage in the channel.rs file.

Once everything is routed through TrackedCommand, we can use clippy to disable creating new commands outside of TrackedCommand itself, which should ensure that really everything will be routed through this struct.

Apart from executing commands using this API, we should also ideally thread all command invocations through a single location (context). This would be useful for profiling (measuring how long each command execution took, and storing it into bootstrap build metrics), caching (hashing idempotent commands and storing their cached output, to avoid rerunning e.g. 20 identical git commands in one bootstrap invocation) and logging (this one could be done without a shared context, but it would again be essentially abusing global variables). While logging and profiling could be done e.g. just by printing to a file or to stderr (although I find that hacky and not good enough), caching could not be achieved in this way. So I would really prefer to use the shared context instead, even though it will require more changes to boostrap.

This can be done in one of (probably) two ways:

  1. Create a shared context variable that will hold caches, metrics etc., and require it to be present when invoking commands. There is already a similar thing in bootstrap - Build(er). However, one issue with that is bootstrap code actually invokes commands even before Build is created, when Config is being parsed. Therefore, we would either need to refactor this code to avoid that (which would be probably quite difficult), or move the shared state to a different struct that is created sooner and it is passed to Config parsing (this is what the third commit does).
  2. Do not pass a context everywhere explicitly, but use a global variable/thread local that will hold the currently active context. Then when a tracked command will be executed, it will access this context and do whatever it needs (profiling, caching, logging, etc.). This would not require as many changes in bootstrap, but it's also quite magical. I think that it shouldn't be such a problem to thread the context everywhere, most of the code can access Build(er) already, and at least being explicit lets us know that the code is accessing an external environment.

Let me know what do you think.

r? @onur-ozkan

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) labels Jun 16, 2024
@rust-log-analyzer
Copy link
Collaborator

The job mingw-check-tidy failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)
Getting action download info
Download action repository 'msys2/setup-msys2@v2.22.0' (SHA:cc11e9188b693c2b100158c3322424c4cc1dadea)
Download action repository 'actions/checkout@v4' (SHA:692973e3d937129bcbf40652eb9f2f61becf3332)
Download action repository 'actions/upload-artifact@v4' (SHA:65462800fd760344b1a7b4382951275a0abb4808)
Complete job name: PR - mingw-check-tidy
git config --global core.autocrlf false
shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
---
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh

COPY host-x86_64/mingw-check/reuse-requirements.txt /tmp/
RUN pip3 install --no-deps --no-cache-dir --require-hashes -r /tmp/reuse-requirements.txt \
    && pip3 install virtualenv
COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/

# NOTE: intentionally uses python2 for x.py so we can test it still works.
# NOTE: intentionally uses python2 for x.py so we can test it still works.
# validate-toolstate only runs in our CI, so it's ok for it to only support python3.
ENV SCRIPT TIDY_PRINT_DIFF=1 python2.7 ../x.py test \
           --stage 0 src/tools/tidy tidyselftest --extra-checks=py:lint
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
#    pip-compile --allow-unsafe --generate-hashes reuse-requirements.in
---

#12 [5/8] COPY host-x86_64/mingw-check/reuse-requirements.txt /tmp/
#12 DONE 0.0s

#13 [6/8] RUN pip3 install --no-deps --no-cache-dir --require-hashes -r /tmp/reuse-requirements.txt     && pip3 install virtualenv
#13 0.430   Downloading binaryornot-0.4.4-py2.py3-none-any.whl (9.0 kB)
#13 0.446 Collecting boolean-py==4.0
#13 0.454   Downloading boolean.py-4.0-py3-none-any.whl (25 kB)
#13 0.470 Collecting chardet==5.1.0
---
#13 3.547 Building wheels for collected packages: reuse
#13 3.548   Building wheel for reuse (pyproject.toml): started
#13 3.872   Building wheel for reuse (pyproject.toml): finished with status 'done'
#13 3.873   Created wheel for reuse: filename=reuse-1.1.0-cp310-cp310-manylinux_2_35_x86_64.whl size=181117 sha256=f5f58750481f69515c2c0d1d503daf565e2565c370d07fc6aeb95fe3498b4269
#13 3.874   Stored in directory: /tmp/pip-ephem-wheel-cache-95jv75qs/wheels/c2/3c/b9/1120c2ab4bd82694f7e6f0537dc5b9a085c13e2c69a8d0c76d
#13 3.876 Installing collected packages: boolean-py, binaryornot, setuptools, reuse, python-debian, markupsafe, license-expression, jinja2, chardet
#13 3.898   Attempting uninstall: setuptools
#13 3.899     Found existing installation: setuptools 59.6.0
#13 3.900     Not uninstalling setuptools at /usr/lib/python3/dist-packages, outside environment /usr
#13 3.900     Not uninstalling setuptools at /usr/lib/python3/dist-packages, outside environment /usr
#13 3.900     Can't uninstall 'setuptools'. No files were found to uninstall.
#13 4.564 Successfully installed binaryornot-0.4.4 boolean-py-4.0 chardet-5.1.0 jinja2-3.1.2 license-expression-30.0.0 markupsafe-2.1.1 python-debian-0.1.49 reuse-1.1.0 setuptools-66.0.0
#13 4.564 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
#13 5.071 Collecting virtualenv
#13 5.126   Downloading virtualenv-20.26.2-py3-none-any.whl (3.9 MB)
#13 5.255      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.9/3.9 MB 31.0 MB/s eta 0:00:00
#13 5.293 Collecting distlib<1,>=0.3.7
#13 5.300   Downloading distlib-0.3.8-py2.py3-none-any.whl (468 kB)
#13 5.344 Collecting filelock<4,>=3.12.2
#13 5.350   Downloading filelock-3.15.1-py3-none-any.whl (15 kB)
#13 5.350   Downloading filelock-3.15.1-py3-none-any.whl (15 kB)
#13 5.381 Collecting platformdirs<5,>=3.9.1
#13 5.388   Downloading platformdirs-4.2.2-py3-none-any.whl (18 kB)
#13 5.475 Installing collected packages: distlib, platformdirs, filelock, virtualenv
#13 5.635 Successfully installed distlib-0.3.8 filelock-3.15.1 platformdirs-4.2.2 virtualenv-20.26.2
#13 DONE 5.7s

#14 [7/8] COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
#14 DONE 0.0s
---
DirectMap4k:      188352 kB
DirectMap2M:     8200192 kB
DirectMap1G:    10485760 kB
##[endgroup]
Executing TIDY_PRINT_DIFF=1 python2.7 ../x.py test            --stage 0 src/tools/tidy tidyselftest --extra-checks=py:lint
+ TIDY_PRINT_DIFF=1 python2.7 ../x.py test --stage 0 src/tools/tidy tidyselftest --extra-checks=py:lint
    Finished `dev` profile [unoptimized] target(s) in 0.03s
##[endgroup]
downloading https://ci-artifacts.rust-lang.org/rustc-builds-alt/55cac26a9ef17da1c9c77c0816e88e178b7cc5dd/rust-dev-nightly-x86_64-unknown-linux-gnu.tar.xz
extracting /checkout/obj/build/cache/llvm-55cac26a9ef17da1c9c77c0816e88e178b7cc5dd-true/rust-dev-nightly-x86_64-unknown-linux-gnu.tar.xz to /checkout/obj/build/x86_64-unknown-linux-gnu/ci-llvm
---
   Compiling tidy v0.1.0 (/checkout/src/tools/tidy)
    Finished `release` profile [optimized] target(s) in 26.01s
##[endgroup]
fmt check
##[error]Diff in /checkout/src/bootstrap/src/utils/mod.rs at line 9:
 pub(crate) mod cmd;
 pub(crate) mod dylib;
 pub(crate) mod exec;
+mod git;
 pub(crate) mod helpers;
 pub(crate) mod job;
 #[cfg(feature = "build-metrics")]
##[error]Diff in /checkout/src/bootstrap/src/utils/mod.rs at line 15:
 pub(crate) mod metrics;
 pub(crate) mod render_tests;
 pub(crate) mod tarball;
-mod git;
 
fmt error: Running `"/checkout/obj/build/x86_64-unknown-linux-gnu/rustfmt/bin/rustfmt" "--config-path" "/checkout" "--edition" "2021" "--unstable-features" "--skip-children" "--check" "/checkout/src/tools/build_helper/src/ci.rs" "/checkout/src/tools/build_helper/src/lib.rs" "/checkout/src/tools/replace-version-placeholder/src/main.rs" "/checkout/src/tools/run-make-support/src/diff/tests.rs" "/checkout/src/tools/run-make-support/src/diff/mod.rs" "/checkout/src/tools/run-make-support/src/llvm.rs" "/checkout/src/tools/run-make-support/src/rustc.rs" "/checkout/src/tools/run-make-support/src/rustdoc.rs" "/checkout/src/tools/run-make-support/src/drop_bomb/tests.rs" "/checkout/src/tools/run-make-support/src/drop_bomb/mod.rs" "/checkout/src/tools/run-make-support/src/command.rs" "/checkout/src/tools/run-make-support/src/clang.rs" "/checkout/src/tools/run-make-support/src/run.rs" "/checkout/src/tools/run-make-support/src/llvm_readobj.rs" "/checkout/src/tools/run-make-support/src/cc.rs" "/checkout/src/tools/run-make-support/src/fs_wrapper.rs" "/checkout/src/tools/run-make-support/src/lib.rs" "/checkout/src/tools/compiletest/src/header/test-auxillary/known_legacy_directive.rs" "/checkout/src/tools/compiletest/src/header/test-auxillary/error_annotation.rs" "/checkout/src/tools/compiletest/src/header/test-auxillary/known_directive.rs" "/checkout/src/tools/compiletest/src/header/test-auxillary/unknown_directive.rs" "/checkout/src/tools/compiletest/src/header/tests.rs" "/checkout/src/tools/compiletest/src/header/needs.rs" "/checkout/src/tools/compiletest/src/header/cfg.rs" "/checkout/src/tools/compiletest/src/tests.rs" "/checkout/src/tools/compiletest/src/compute_diff.rs" "/checkout/src/tools/compiletest/src/errors/tests.rs" "/checkout/src/tools/compiletest/src/header.rs" "/checkout/src/tools/compiletest/src/read2.rs" "/checkout/src/tools/compiletest/src/runtest.rs" "/checkout/src/tools/compiletest/src/runtest/coverage.rs" "/checkout/src/tools/compiletest/src/runtest/tests.rs" "/checkout/src/tools/compiletest/src/runtest/debugger.rs" "/checkout/src/tools/compiletest/src/util.rs" "/checkout/src/tools/compiletest/src/json.rs" "/checkout/src/tools/compiletest/src/common.rs" "/checkout/src/tools/compiletest/src/util/tests.rs" "/checkout/src/tools/compiletest/src/read2/tests.rs" "/checkout/src/tools/compiletest/src/lib.rs" "/checkout/src/tools/compiletest/src/main.rs" "/checkout/src/tools/compiletest/src/errors.rs" "/checkout/src/tools/compiletest/src/raise_fd_limit.rs" "/checkout/src/tools/rustdoc-gui-test/src/config.rs" "/checkout/src/tools/rustdoc-gui-test/src/main.rs" "/checkout/src/tools/html-checker/main.rs" "/checkout/src/tools/jsondocck/src/error.rs" "/checkout/src/tools/jsondocck/src/config.rs" "/checkout/src/tools/jsondocck/src/cache.rs" "/checkout/src/tools/jsondocck/src/main.rs" "/checkout/src/tools/remote-test-client/src/main.rs" "/checkout/src/rustdoc-json-types/tests.rs" "/checkout/src/rustdoc-json-types/lib.rs" "/checkout/src/bootstrap/build.rs" "/checkout/src/bootstrap/src/utils/git.rs" "/checkout/src/bootstrap/src/utils/exec.rs" "/checkout/src/bootstrap/src/utils/metrics.rs" "/checkout/src/bootstrap/src/utils/change_tracker/tests.rs" "/checkout/src/bootstrap/src/utils/change_tracker.rs" "/checkout/src/bootstrap/src/utils/mod.rs" "/checkout/src/bootstrap/src/utils/render_tests.rs" "/checkout/src/bootstrap/src/utils/tarball.rs" "/checkout/src/bootstrap/src/utils/job.rs" "/checkout/src/bootstrap/src/utils/cc_detect.rs" "/checkout/src/tools/build_helper/src/util.rs"` failed.
If you're running `tidy`, try again with `--bless`. Or, if you just want to format code, run `./x.py fmt` instead.
  local time: Sun Jun 16 22:19:51 UTC 2024
  network time: Sun, 16 Jun 2024 22:19:51 GMT
##[error]Process completed with exit code 1.
Post job cleanup.

src/bootstrap/src/utils/cmd.rs Outdated Show resolved Hide resolved
/// Runs the command.
#[track_caller]
pub fn run_maybe(&mut self) -> CommandOutput {
let output = self.cmd.output().expect("Cannot execute process");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: it might be nice to report the complete command invocation that failed

src/bootstrap/src/utils/channel.rs Outdated Show resolved Hide resolved
use std::path::Path;

/// Command that checks whether git works at the given `directory`.
pub fn cmd_works(directory: &Path) -> TrackedCommand {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_in_repo maybe instead?

@onur-ozkan
Copy link
Member

Are we still working on this? #126731 makes this PR redundant if I am not mistaken.

@Kobzol
Copy link
Contributor Author

Kobzol commented Jun 22, 2024

Yeah, we can close in favour of #126731.

@Kobzol Kobzol closed this Jun 22, 2024
@Kobzol Kobzol deleted the bootstrap-tracked-cmd branch June 22, 2024 07:14
@onur-ozkan onur-ozkan removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Oct 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants