Rust: bootstrap.py --help starts with downloading

Created on 20 Oct 2016  路  39Comments  路  Source: rust-lang/rust

rustbuild really has a proper --help response, but the first time the user asks for it, it starts working / downloading right away. This is a bad first impression, and a stumbling block for getting to know the new build system.

A-rustbuild C-enhancement

Most helpful comment

You have a point. Perhaps instead of stopping dead, a bootstrap.py invocation that needs to download the nightlies could output something like this _before_ downloading?

NOTE: downloading bootstrap requirements before processing bootstrap ${given command line arguments}

All 39 comments

Kinda hard to do anything else, the help message comes from the src/bootstrap crate written in Rust. One could duplicate the usage text in the Python script, but besides being gross and prone to divergence from the ground truth, this increases our reliance on the Python wrapper which ideally should be as minimal as possible (perhaps even some day be replaced with a set of really stupid native platform-specific scripts).

It already parses command line flags before download -- it could catch --help and stop there.

It's not hard to catch --help (in fact it already treats that flag specially), but what should it do with that? Outputting the real help message requires knowledge that's not in bootstrap.py, only in bootstrap.rs (for lack of a better name). And simply saying something like

Can't show --help because [...]

doesn't seem like a big improvement to me. It gives you no useful information and the first impression is just as bad.

When I tried to get to know the new build system, my impression was that bootstrap had no --help, because it just started working. So I never found it until much later.

I know, not every misunderstanding is a bug, but it seems like this could be improved? The user just needs to know what they need to do to reach the real --help. Even better of course if that could just be shown, but we can't have it all.

You have a point. Perhaps instead of stopping dead, a bootstrap.py invocation that needs to download the nightlies could output something like this _before_ downloading?

NOTE: downloading bootstrap requirements before processing bootstrap ${given command line arguments}

OTOH, the script should probably create the build directory in the repo root. Currently it creates the build directory in the location where the script is invoked. For example, if one happens to invoke it from within the src directory: python bootstrap/bootstrap.py ... then build, build/cache, ... are created inside src.

Not sure if there is a reason for making the build directory relative to pwd. The following would seem more desirable:

diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index 2c2260a..056bd36 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -370,7 +370,7 @@ def main():
     rb.config_toml = ''
     rb.config_mk = ''
     rb.rust_root = os.path.abspath(os.path.join(__file__, '../../..'))
-    rb.build_dir = os.path.join(os.getcwd(), "build")
+    rb.build_dir = os.path.join(rb.rust_root, "build")
     rb.verbose = args.verbose
     rb.clean = args.clean

@0xmohit

I think the intent is to allow out-of-source builds (including multiple independent builds).

Aside: this seems completely unrelated to this issue? If you want to discuss this further please open a new issue.

FYI the relevant part is non-trivial and not easily refactored out. Maybe printing a note before proceeding to download is the only reasonable thing we can do without too much effort.

And even after the download, the --help output starts with some cruft (as it checks that the download/build is fine):

    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
unknown command: --help
Usage: x.py --help [options] [<args>...]

Options:
    -v, --verbose       use verbose output
...

Would it be an acceptable compromise to keep the output of ./build/bootstrap/debug/bootstrap --help in a text file that can be written back to the console when the ./x.py --help command is run?

That file would mostly be static and it would be easy to add a (tidy?) check that compares it to the output from ./build/bootstrap/debug/bootstrap --help to ensure consistency when the set of options and flags is changed.

@ranma42 I'm not quite sure but your suggestion may be best implemented with automation, i.e. a script that invokes bootstrap multiple times to obtain all the possible help messages. The script would be run by people developing rustbuild to sync their changes. However that's still quite a bit of complexity and tedious to do not to mention easy to forget, I'm not even sure we should go that far for those impatient people.

@alexcrichton What's your opinion on that, since you authored the whole system?

I've hacked together such a script:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import unicode_literals, absolute_import

import functools
import os
import subprocess
import sys


def extract_usage(bootstrap_path, args):
    argv = [bootstrap_path, ]
    argv.extend(args)
    proc = subprocess.Popen(argv, stdout=subprocess.PIPE)
    stdout, stderr = proc.communicate()
    return stdout


USAGES_TO_EXTRACT = {
        'cmds': [],
        'build': ['build', '-h'],
        'test': ['test', '-h'],
        'doc': ['doc', '-h'],
        'clean': ['clean', '-h'],
        'dist': ['dist', '-h'],
        }


def main():
    rust_root = os.path.abspath(os.path.join(__file__, '../../..'))
    bootstrap_path = os.path.join(rust_root, 'build/bootstrap/debug/bootstrap')

    usage = functools.partial(extract_usage, bootstrap_path)

    for category, args in USAGES_TO_EXTRACT.items():
        out_filename = 'usage-{}.txt'.format(category)
        out_path = os.path.join(rust_root, 'src/bootstrap', out_filename)
        with open(out_path, 'wb') as fp:
            fp.write(usage(args))


if __name__ == '__main__':
    main()

In case bootstrap is invoked without args or an unknown command, it outputs a line of error, which should be discarded but I didn't include it. Anyway it's still undecided whether rustbuild maintainers are expected to sync usage messages while working on it, so let's discuss.

@xen0n I had assumed that only the top-level --help option was to be provided without the download (i.e. the generic usage syntax, not the command-specific options). I was suggesting to add a check to the CI infrastructure to ensure the consistency specifically to prevent contributors from forgetting to update it when needed.

@ranma42 Oh that would be much easier to do! However AFAIK the CI infrastructure only deals with testing rustc itself right now? I don't know how much work it'll take extending rustbuild to self-test as well, as all heavy-lifting are done in Cargo.

I think some non-rustc checks are implemented by the tidy tool (found in src/tools/tidy) to enforce some conventions on syntax, licences, error declaration/messages, ...
We could add a check for the files that can be generated automatically but that we decide to ship with the repo for convenience (for example src/libcore/num/dec2flt/table.rs and src/librustc_unicode/tables.rs). This check could also apply to the new --help documentation.

(Side note: I thought the auto-generated files were already verified in some way, but I could not find any such check; would it make sense to implement this and open a PR for it?)

Unfortunately I don't really see a solution to this. This is to me is just a fact of how rustbuild works, we can't really work around it.

While committing help messages would be possible, I'd personally prefer to avoid putting any form of generated file in the repository as then we have to keep it up to date, add CI for it, ...

It doesn't need to be perfect, there's always solutions. Would it be ok to add a provisional message that explains that it must build itself before the real --help is displayed?

I'd personally consider that a non-starter because it'd be a pretty bad user experience of typing --help, then going back and typing --help-no-really-please to actually get the work done. Additionally, if we want to tell someone how to print the help message, we now can't say --help because it won't print it half the time.

@alexcrichton
If build system is uninitialized, then x.py --help and preferably x.py without arguments should give minimal help for the python script itself, reporting the build system status(not initialized) and telling how to initialize the build system if network is available (e.g. by simply starting the build) and if network is unavailable (specify paths to snapshots or something).

If build system is initialized then x.py --help can redirect to bootstrap.rs help.

Starting arbitrary long build process on x.py --help and not providing any help offline if just really really bad UI.
Personally, I'd just immediately abort the script trying to do this and start searching help in the source code.

I basically think that this is "bad UI" no matter what we do. Either:

  • You request --help and then have to pass --help-no-please-really
  • You request --help and it compiles a bunch of stuff you didn't expect

It seems like it's just a matter of weighing these and there's no clear winner

I don't understand why --help-no-please-really is necessary.

So let's say we make --help abort if the build system isn't built. How do you then build the build system and request --help?

@alexcrichton I believe what he means is just printing out why a build is started when being invoked uninitialized.

$ ./x.py
info: build system uninitialized, starting bootstrap
... download progresses and cargo output ...

To provide anything more than a simple hint needs extracting the generated output somehow, all the associated maintenance costs apply. I think we shouldn't go that far unless majority of people see it annoying. (Being more friendly to drive-by contributors by providing immediate help is another topic, let's not discuss that here.)

How do you then build the build system and request --help?

By following whatever recommendations uninitialized x.py --help gives me.

More concrete, uninitialized x.py --help should recommend something like x.py init && x.py --help for the "network is present" scenario.

@petrochenkov that's _exactly_ what I meant by x.py --help-no-please-really. Does that answer your question?

(Writing the edit as a new message in case you are using email)

More concrete, uninitialized x.py --help should recommend something like x.py init && x.py --help for the "network is present" scenario and x.py init --path-to-rustc=blah && x.py --help for offline scenario.

I mean, --help combines help for both x.py itself and bootstrap.rs if available. The first part of help is equally important and needs to always be available.

@alexcrichton I don't see why a provisional message means that --help-really is needed? Can we please focus on solutions.

Example future user experience:

$ ./x.py --help
NOTE: Building rustbuild before processing command line arguments
Download ... more output here
Running ... more output here
 ... lots more output here
 ... lots more output here
 ... lots more output here
 ... lots more output here

unknown command: --help
Usage: x.py --help [options] [<args>...]

Options:
    -v, --verbose       use verbose output
        --config FILE   TOML configuration file for build
        --build BUILD   build target of the stage0 compiler
        --host HOST     host targets to build
        --target TARGET target targets to build
        --stage N       stage to build
        --src DIR       path to the root of the rust checkout
    -j, --jobs JOBS     number of jobs to run in parallel
    -h, --help          print this help message

@petrochenkov

More concrete, uninitialized x.py --help should recommend something like x.py init && x.py --help

Sorry if I miscommunicated but this is what I meant by my first option above. A two-step print-the-help-message seems a bad UI just as much as download information to print the help is. It seems to me that newbies would far more often prefer to just get a help message rather than go through a multi-stage process just to get a help message.


@bluss

I don't see why a provisional message means that --help-really is needed? Can we please focus on solutions.

I'm sorry if I've miscommunicated as well, but this is precisely what I'm trying to do. @petrochenkov is proposing a two-stage help message where first you get a message saying you have to run something else. You then run that something else and then you get the help message. This is what I intended to communicate many comments back as what I believe is not better than the current situation.

I'm also confused by the code block you had as an example. It seems like that's exactly what happens today beyond an informational message that something is being compiled. Do you think that's sufficient to close this issue?

@alexcrichton It's not perfect, but even a simple thing like that is a noticeable improvement in user experience.

@rkruppe said:

doesn't seem like a big improvement to me. It gives you no useful information and the first impression is just as bad.

But I don't agree. If you just tell the user why something is happening and that it's intentional, that's much better.

FWIW I've come around and agree that simply saying that and why it's downloading and building stuff before printing the help message is an improvement.

@petrochenkov do you agree that an informational message on the first build would be sufficient?

@alexcrichton
Sufficient for what exactly? Fixing this issue? No. Improving the situation? Yes.

(Regarding https://github.com/rust-lang/rust/pull/37817, this issue is not an absolute blocker, it's just a problem magnified by rustbuild being a default and --help being the first thing people encounter after getting a copy of the repo.)

@petrochenkov sorry just trying to clarify. You commented on #37817 with

Fix #37305 before merging

which I interpreted as "#37305 must be fixed before merging this", as in an absolute blocker. I can certainly implement an information message, and we can leave this open if we feel it's still not fixed.

@alexcrichton
I tried the new "Submit feedback suggesting changes." GitHub option, it seems to make things appear... more imperative.

I've added a sample message to #37817

I've stumbled across this today, and I think this UX is really bad.

@alexcrichton

While committing help messages would be possible, I'd personally prefer to avoid putting any form of generated file in the repository as then we have to keep it up to date, add CI for it, ...

As there are already rustbuild related dependencies through cargo vendor (#37524), there isn't much more to mess up here by somehow autogenerating the help text and saving it in a form python can understand.

It can be generated every time after rustbuild is built, and CI can simply pass a flag to the python script to verify that the file didn't change, so that a PR always needs to check in the change of the file. Is that a good solution?

About the sample message: I think it can be improved if it includes a pointer to some document that doesn't require the internet, like src/bootstrap/README.md.

FWIW, I think that printing an informative message should suffice for rolling out the deprecation process. If you're going to use the build system, you're going to be bootstrapping anyway -- so the main issue is being informed about what's happening.

That said, I'd suggest specifically mentioning --help in the message, to make it more clear that the option is indeed recognized. I also agree with @est31 that a link to the README would be helpful as well.

[Hopefully, this issue also concerns x.py.]

With Rust 1.16.0 I get the message below.

[鈥
configure: mv -f config.tmp config.mk
configure:
configure: configured in release mode. for development consider --enable-debug
configure:
configure: NOTE you have now configured rust to use a rewritten build system 
configure:      called rustbuild, and as a result this may have bugs that
configure:      you did not see before. If you experience any issues you can 
configure:      go back to the old build system with --disable-rustbuild and 
configure:      please feel free to report any bugs!
configure:
configure: run `python /dev/shm/bee-pmenzel/rustc/rustc-1.16.0-0/source/x.py --help`
configure:

Running python /dev/shm/bee-pmenzel/rustc/rustc-1.16.0-0/source/x.py --help also starts downloads, which is unexpected (and unwanted as you can run that command from anywhere, and it creates build in the current directory).

Was this page helpful?
0 / 5 - 0 ratings