-
Notifications
You must be signed in to change notification settings - Fork 6
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
Conversation
79dd7e4
to
9fcfe54
Compare
Did you look into if using 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 |
I didn't look into it and have never used them yet, but I'm open to trying! I think
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 |
You usually want to use |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
049dd13
to
4dbf5aa
Compare
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 |
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 |
There was a problem hiding this 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?
Line 23 in e47fe3b
if cfg!(release) { |
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)
Lines 1 to 4 in e47fe3b
//! 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?
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.
Line 171 in e47fe3b
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.
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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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
Some("gccrs") => Wrapper::spawn(), | ||
Some("rustc") => Gccrs::handle_rust_args(&args), |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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)
488d50f
to
58bc1b7
Compare
892cefa
to
550023c
Compare
@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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM good work
There was a problem hiding this 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
Outdated
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), |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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)
"?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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 :)
ba209da
to
e630c9b
Compare
There was a problem hiding this 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!
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.