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

Child process and output management #392

Merged

Conversation

fitzgen
Copy link
Member

@fitzgen fitzgen commented Oct 4, 2018

Fix #287 but also all the instances of this that we might ever run into.

@fitzgen
Copy link
Member Author

fitzgen commented Oct 4, 2018

Just a note that this last commit started ballooning in size because it involved propagating failure::Error everywhere so I could use context methods :-/

@fitzgen fitzgen force-pushed the child-process-and-output-management branch from 8ffc5ad to 2fa961d Compare October 4, 2018 22:12
@fitzgen
Copy link
Member Author

fitzgen commented Oct 4, 2018

First build, that has to build the wasm-bindgen macro:

wasm-pack-build

Rebuilding again, which is super fast:

wasm-pack-build-again

@fitzgen fitzgen added this to the 0.6.0 milestone Oct 4, 2018
@ashleygwilliams
Copy link
Member

@fitzgen does this also cover #280 ? i'd really prefer if it didn't - but understand if that's tough. we had a collaborator ask to take #280 on in IRC today, so this is also why in the future- claiming issues and opening WIP prs asap is best.

Copy link
Contributor

@alexcrichton alexcrichton left a comment

Choose a reason for hiding this comment

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

I really like the idea of ensuring that all output is verbosely recorded in log files, that's something we should definitely pick up on in Cargo!

Also just to make sure, do you have an idea of how we'll fix the colors issue with this strategy? I'd want to make sure that we're forward compatible with adding colors.

src/child.rs Outdated
}
}

thread::yield_now();
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we'll definitely want a synchronization solution before this lands to ensure that we're not just eating a core spin-waiting while cargo tries to finish. We don't actually have to try_wait or wait on the process at all until stdout/stderr are closed, so we can wait until those are done to do that and otherwise just loop around receiving on the channel (which we can do with a timeout as well)

src/child.rs Outdated
// waiting on the child process.

let stdout_handle = thread::spawn(move || {
for line in stdout.lines() {
Copy link
Contributor

Choose a reason for hiding this comment

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

I've had unfortunately bad experiences in the past line-buffering stdout. An example of the bad behavior is that libtest in quiet mode used to simply print a .-per-test and then at the very end it'd print a newline. This logic means that no output would happen until the very very end where everything comes out all at once. We fixed this bug for Rust in the past, and I think a similar fix could be applied here where this just streams data as it comes, and the data processor specially handles newlines in the stream to append output.

src/child.rs Outdated
{
let message = match out {
Ok(Output::Stdout(line)) => format!("{} (stdout): {}", command_name, line.as_ref()),
Ok(Output::Stderr(line)) => format!("{} (stderr): {}", command_name, line.as_ref()),
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure we want to prefix things with (stderr) and/or (stdout) as it may quickly add noise to the output. Do you think it's worth differentiating? (people may be alarmed, for example, that Cargo always prints to stderr despite it typically not generating errors).

Additionally, the command_name may be best to print at the very beginning? Something like Running ... like in Cargo so it's there if necessary and otherwise not taking up too much space.

src/child.rs Outdated
let mut is_long_running = false;

loop {
if !is_long_running && taking_too_long(since) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure I'm 100% convinced this strategy of swallowing the first 200ms of output will work out in the long run. In the worst case a tool finishes very quickly but some crucial information it prints to stdout/stderr is lost. I don't think that wasm-pack invokes many tools like that today though? This is largely just in terms of future concerns.

In general I feel we should depend on tools responsibly not generating lots of output that's not necessary. Like adding a wasm target with rustup which is already there should produce zero output, but I'm not sure that we should gauge that based on the 200ms delay here. (similarly Cargo's "fresh" output of "you compiled this in 0.2s" is something we'd want to hide, but there isn't a great way to do that in Cargo right now unfortunately).

Copy link
Member Author

Choose a reason for hiding this comment

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

So you're saying you think we should always display the child's output unconditionally?

Copy link
Contributor

Choose a reason for hiding this comment

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

After talking over lunch I think the conclusion was that we should probably remove this yeah, and have the main thread block on dispatching output received from the channel

Copy link
Member Author

Choose a reason for hiding this comment

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

For context:

  • We really don't want to swallow warnings from rustc, even when cargo build exits super quickly.

  • That general scenario could play out with other CLI tools too

  • The noisiest parts of the wasm-pack build output are actually things like "checking if you have wasm32-u-u installed" when 99% of the time the user already does. For this particular case, it is wasm-pack itself printing this out, not a child process, so this module is unaffected.

Therefore, we should always print all output, regardless of time the child takes to run.

For that last bullet point, and any kind of "install X if it isn't already here", we only want to print in the case where X isn't already installed and we are going to commence with installing it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Er and to elaborate a bit! I think it's fine to leave this in if it's just hypothetical things I'm worried about and I can always file a follow-up issue, but one concrete use case we discovered which may turn out badly is a rustc invocation which prints a compiler warning (like a diagnostic) but finishes in under 200ms. I think the logic here would swallow up the warning and avoid it getting printed.

Copy link
Contributor

Choose a reason for hiding this comment

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

Er oops sorry, double post at the same time!

src/child.rs Outdated
return Ok(stdout);
} else {
let msg = format!("`{}` did not exit successfully", command_name);
return Err(Error::cli(&msg, stderr.into(), exit).into());
Copy link
Contributor

Choose a reason for hiding this comment

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

This'll want to ferry along stdout as well, want to be sure to not lose anything whenever we print failure messages!

I think it'd also be good to carry along the full command line here as opposed to just the name of the command to ensure that all the arguments are in the error message too

Copy link
Member

@ashleygwilliams ashleygwilliams left a comment

Choose a reason for hiding this comment

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

generally speaking, i agree with all of @alexcrichton 's points. do we know what happens in this if we don't have the magic 200ms check? i'd be curious how verbose it is. will give this a closer look once you've responded to alex's stuff.

@fitzgen
Copy link
Member Author

fitzgen commented Oct 5, 2018

Ok, new commits make it so that

  • we don't do the 200ms thing, and instead always send child output to the console,
  • we don't line buffer the child's stdout and stderr
  • we maintain the child's stdout in the Error::Cli variant

@fitzgen
Copy link
Member Author

fitzgen commented Oct 5, 2018

Note: we will want follow up PRs to allow making certain commands run in "quiet" mode where we don't dump their output to our console, but (a) I think having these switched defaults is better in the general case, and (b) we can do that as a follow up issue.

@fitzgen
Copy link
Member Author

fitzgen commented Oct 5, 2018

Notably, we still want "quiet" commands to have their output end up in the logs, just not the console.

Copy link
Member

@ashleygwilliams ashleygwilliams left a comment

Choose a reason for hiding this comment

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

so my only real feedback now is that we have a lot of mut when defining the commands- in some cases we conditionally change what command is run, but for many we don't. do they still need to be mut?

crate_path: &Path,
version: &str,
) -> Result<(), failure::Error> {
let mut cmd = Command::new("cargo");
Copy link
Member

Choose a reason for hiding this comment

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

is this mut here necessary?

.filter(|output| output.status.success())
.map(|output| {
String::from_utf8_lossy(&output.stdout)
let mut cmd = Command::new(bindgen_path);
Copy link
Member

Choose a reason for hiding this comment

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

is this mut here necessary?

} else {
Ok(())
}
let mut cmd = Command::new("rustup");
Copy link
Member

Choose a reason for hiding this comment

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

is this mut here necessary?

Ok(())
}
pub fn npm_pack(log: &Logger, path: &str) -> Result<(), failure::Error> {
let mut cmd = Command::new("npm");
Copy link
Member

Choose a reason for hiding this comment

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

is this mut necessary?

@fitzgen
Copy link
Member Author

fitzgen commented Oct 9, 2018

The muts are necessary because the methods for adding arguments etc to a command take &mut self.

@ashleygwilliams ashleygwilliams merged commit 2ca946e into rustwasm:master Oct 9, 2018
@ashleygwilliams ashleygwilliams modified the milestones: 0.6.0, 0.5.1 Oct 9, 2018
xmclark added a commit to xmclark/wasm-pack that referenced this pull request Oct 29, 2018
Change to INFO when alerting users about missing fields in Cargo.toml

Combine filed missing messages into one INFO line

Fix bad formating

Merge pull request rustwasm#394 from mstallmo/master

Change to INFO when alerting users about missing fields Cargo.toml
child: Always print everything to our output

Also don't buffer the child's stdout and stderr.

error: Add stdout to the `Error::Cli` variant

refactor: Return failure::Error instead of wasm_pack::error::Error

refactor: Import self and use full module path for failure

Use full module path for failure to be consistent since
it's used like that in other modules.

refactor: Return failure::Error instead of wasm_pack::error::Error

chore: Run rustfmt

chore: Run rustfmt

Merge pull request rustwasm#392 from fitzgen/child-process-and-output-management

Child process and output management
Merge pull request rustwasm#401 from drager/return-failure-error

Return `Result<T, failure::Error>` instead of `Result<T, wasm_pack::error::Error>`
v0.5.1

Merge pull request rustwasm#404 from rustwasm/0.5.1

v0.5.1
feat(website): bump vers

Merge pull request rustwasm#405 from rustwasm/website-update

feat(website): bump vers
test(command/build): add a test for build command

useing local wasm-bindgen

Fix typo in test function name for copying the README

bugfix(bindgen-target-dir): use PathBuf to join

the old code are hard coded path with "/", which may cause error
on windows, thus changing to use PathBuf.join.

fixing rustwasm#414

change for cargo_metadata

Merge branch 'master' into test-build-for-example
Merge pull request rustwasm#408 from huangjj27/test-build-for-example

test(command/build): add a test for build command
Merge pull request rustwasm#412 from mstallmo/typo-fix

Fix typo in test function name for copying the README
Merge branch 'master' into fix-canonical-paths-on-windows
this change is not related to this PR


use absolutize


remove unused use


cargo fmt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants