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

Split up project in library and binary #21

Merged
merged 11 commits into from
Aug 25, 2021
Merged

Conversation

CohenArthur
Copy link
Member

@CohenArthur CohenArthur commented Jun 22, 2021

Closes #19
Closes #22
In reference to this comment

@flip1995 I am unsure about the implementation as I usually just panic when handling errors in the main haha. I believe this to be cleaner, as it displays useful information and exits using std::process::exit() rather than panicking.

@flip1995
Copy link
Member

Did you look into if using anyhow and/or thiserror would make sense for this?

Using those crates would save you from writing boilerplate code and keeping all that boilerplate code up to date when adding new error sources.


Also a more general note: I saw that you have a main.rs and then a gccrs module. Usually you just make a lib and add one small main.rs to keep this split. You could do this, by moving everything from src/gccrs/* to src/ and rename mod.rs to lib.rs

@CohenArthur
Copy link
Member Author

Did you look into if using anyhow and/or thiserror would make sense for this?

Using those crates would save you from writing boilerplate code and keeping all that boilerplate code up to date when adding new error sources.

I didn't look into it and have never used them yet, but I'm open to trying! I think thiserror would be simpler to work with and contribute to.

Also a more general note: I saw that you have a main.rs and then a gccrs module. Usually you just make a lib and add one small main.rs to keep this split. You could do this, by moving everything from src/gccrs/* to src/ and rename mod.rs to lib.rs

Fair point. I didn't know at the time if we were gonna need multiple modules or not. I think it's established now that it won't be necessary, so I'll do this. Maybe in a future PR (I'll open an issue) as to not clutter this one

@flip1995
Copy link
Member

You usually want to use thiserror for the library part and then anyhow in the binary part. I think this is a good idea, since you'll get good looking error messages for free.

Copy link
Member

@philberty philberty left a comment

Choose a reason for hiding this comment

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

LGTM

@CohenArthur
Copy link
Member Author

I cleaned up the error handling but this is a temporary and ugly solution. This way, as @flip1995 said, we get good looking error messages and handling for free. Once the project is split into binary and library (#22), this will get refactored. There will be a single error type (not a WrapperError and a gccrs::Error one), and the binary will not need to map errors etc as it does now.

@flip1995
Copy link
Member

flip1995 commented Jul 5, 2021

How much work do you think it would be to do the split between lib and binary before merging this PR? AFAICT this shouldn't be that hard to do. I'd rather do that first and then do this PR right once, instead of merging this PR -> do the split -> merging a PR refactoring this PR.

@CohenArthur
Copy link
Member Author

How much work do you think it would be to do the split between lib and binary before merging this PR? AFAICT this shouldn't be that hard to do. I'd rather do that first and then do this PR right once, instead of merging this PR -> do the split -> merging a PR refactoring this PR.

You make a good point haha. I'll do the split here and rename the PR then

@CohenArthur CohenArthur changed the title Add better terminal error handling Split up project in library and binary Jul 6, 2021
Copy link
Member

@flip1995 flip1995 left a comment

Choose a reason for hiding this comment

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

I figured that this is now the right time to do a whole project review, and not only a review of this PR. My suggestions don't have to be implemented all in one/this PR, but rather get implemented step by step. I tried to sort them from easy to hard to do.


What is this line supposed to do?

if cfg!(release) {


cargo-gccrs/src/gccrs.rs

Lines 43 to 44 in e47fe3b

// On UNIX, we can check using the `command` built-in command, but it's not cross-platform.
// The slow but sure way to do this is to just try and spawn a `gccrs` process.

You can use the which crate for cross-platform support for this


I think you want to have 2 binaries here. One for the driver, that does all the argument conversion and other hard work, and one that just invokes the driver from the cargo command. But I may be misunderstanding something, see my comment #21 (comment)


//! The Config module aims to parse gccrs target options and translate them to an output
//! similar to what rustc does. This module parses a file previously created by the
//! [`Gccrs::dump_config()`] function. This corresponds to invoking gccrs with the
//! `-frust-dump-target_options` argument.

What format is the config dump in and can you use serde for this?


cargo-gccrs/src/config.rs

Lines 101 to 102 in e47fe3b

/// Display the gccrs target options on stdout, in a format that cargo understands
pub fn display() -> Result {

A fallible display impl seems weird to me. You should rather implement a fallible constructor and then the Display trait. That way you can use the format machinery and get functions like to_string for free.

I'm also not 100% sure what the option dumping is for, so please tell me if there is a reason why it has to be done that way.


pub fn from_rustc_args(rustc_args: &[String]) -> Result<Vec<GccrsArgs>> {

I still don't quite get why it is necessary to return a Vec<GccrsArgs>. Just from the struct name and the documentation, I would assume that when I have a GccrsArgs instance, that I have all the arguments that I need. This is also the main thing that prevents the GccrsArgs from implementing things like the TryFrom trait and so on.


cargo-gccrs/src/args.rs

Lines 164 to 168 in e47fe3b

// Execute an argument set's callback if present. Returns Ok if no callback was
// present
pub fn callback(&self) -> Option<&'static dyn Fn(&GccrsArgs) -> Result> {
self.callback
}

I still think that with this, GccrsArgs is doing too much. One good principle of OOP is that one class should only do one thing. With that principle, GccrsArgs should only handle arguments, which includes parsing, converting, and then holding them. Now it handles arguments and does random other things like creating archives and in the future possibly even more.

This is the area of the current implementation that really needs a refactor IMHO. Apply the KISS principle as often as you can. I have trouble following what is done where, so the current impl isn't really simple to follow.

src/lib.rs Outdated

/// Abstraction around spawning a new `cargo` process with `cargo-gccrs` set as a wrapper
/// to `rustc`
pub struct Wrapper;
Copy link
Member

Choose a reason for hiding this comment

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

Why do you need a wrapper for this? Do you plan to do more things with this wrapper in the future? If not, I'd just implement the spawn() function here directly.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's what I had done originally but thought it'd be cleaner from the binary side of things. I'm fine with reverting to a simple function named spawn_as_wrapper() as it was originally

src/lib.rs Outdated
// Skip `cargo` and `gccrs` in the invocation. Since we spawn a new cargo command,
// `cargo gccrs arg0 arg1` will become `cargo run arg0 arg1`
let mut cargo_gccrs = std::process::Command::new("cargo")
.env("RUSTC_WRAPPER", "cargo-gccrs")
Copy link
Member

Choose a reason for hiding this comment

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

I'd rather name this gccrs-driver to stick to the rustc naming.

src/main.rs Outdated
Comment on lines 12 to 13
Some("gccrs") => Wrapper::spawn(),
Some("rustc") => Gccrs::handle_rust_args(&args),
Copy link
Member

Choose a reason for hiding this comment

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

Wait, does this handle both the driver and the cargo part? So if you call cargo gccrs build, this binary will execute itself with the same arguments?

When is this called with gccrs vs rustc?

Copy link
Contributor

Choose a reason for hiding this comment

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

When you do cargo gccrs, cargo will run cargo-gccrs gccrs. When you do RUSTC_WRAPPER=cargo-gccrs, the spawned rustc processes will be cargo-gccrs rustc. (RUSTC instead of RUSTC_WRAPPER would omit the rustc part)

@flip1995 flip1995 mentioned this pull request Jul 7, 2021
7 tasks
@flip1995
Copy link
Member

flip1995 commented Jul 7, 2021

Let's track and further talk about this in #36 and revisit this PR once the other points are addressed. Fixing everything in #36 should bring this to a point, where this split can just be done by moving files around.

@CohenArthur CohenArthur force-pushed the cleanup-error-handling branch 2 times, most recently from 488d50f to 58bc1b7 Compare August 16, 2021 17:42
@CohenArthur
Copy link
Member Author

@flip1995 I believe that all items have been resolved. I also took the liberty of splitting the wrapper in two binaries as mentioned in your big review comment, since it ties in nicely with the PR.

Copy link
Member

@philberty philberty left a comment

Choose a reason for hiding this comment

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

LGTM good work

Copy link
Member

@flip1995 flip1995 left a comment

Choose a reason for hiding this comment

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

LGTM overall. My 2 comments shouldn't block this PR from going through.

src/bin-driver.rs Show resolved Hide resolved
let args: Vec<String> = std::env::args().collect();

let res = match args.get(1).map(String::as_str) {
Some("rustc") => Gccrs::handle_rust_args(&args),
Copy link
Member

Choose a reason for hiding this comment

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

A function call named handle_rust_args that also compiles things is a bit confusing IMO.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's fair. Do you have any idea as to what to call the function? Since it doesn't only compile, and also has the task of translating the Rust arguments

Copy link
Member

Choose a reason for hiding this comment

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

I would either split it up and first handle the args and then compile or just call it compile if you want to keep only one public function to interface with the lib part.

src/gccrs.rs Outdated
@@ -136,7 +136,7 @@ impl Gccrs {
}

/// Convert arguments given to `rustc` into valid arguments for `gccrs`
pub fn handle_rust_args(args: &[String]) -> Result {
pub fn compile_rust_args(args: &[String]) -> Result {
Copy link
Member

Choose a reason for hiding this comment

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

Now the method name sounds like it compiles the arguments. Why not just "compile(args)"?

Copy link
Member Author

Choose a reason for hiding this comment

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

I have a bit of an issue with compile because it sounds like we're simply using some sort of gccrs wrapper, which would logically take gccrs arguments only. I would be okay with compile since the gccrs module will not become its own crate or anything, but I feel weird looking at Gccrs::compile(args) when the args in question are clearly rustc args and not gcc args.

I don't know, I thought this was a good compromise but I'm okay with renaming it as just compile if you'd like

Copy link
Member

Choose a reason for hiding this comment

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

That's a good point. What about the name compile_with_rust_args? So adding a "with". (This is really NIT picking, so if you don't want to change it, but just merge it, I'd also be ok with that)

Copy link
Member

Choose a reason for hiding this comment

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

That's a good point. What about the name compile_with_rust_args? So adding a "with". (This is really NIT picking, so if you don't want to change it, but just merge it, I'd also be ok with that)

Copy link
Member Author

Choose a reason for hiding this comment

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

Perfect! This is what I was thinking too this morning :)

Copy link
Member

@flip1995 flip1995 left a comment

Choose a reason for hiding this comment

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

Great, let's get this merged then!

@CohenArthur CohenArthur merged commit 87a9173 into main Aug 25, 2021
@CohenArthur CohenArthur deleted the cleanup-error-handling branch August 25, 2021 08:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Split up sources and create library Cleanup error handling
4 participants