-
Notifications
You must be signed in to change notification settings - Fork 452
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
build: Inherit flags from rustc #1279
base: main
Are you sure you want to change the base?
Conversation
It's not fully ready yet, so far I'm looking for some feedback. |
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.
Thanks, I have some feedback with the code style/lint:
It's because in https://docs.rs/cc/latest/src/cc/lib.rs.html#660 Because the rust flag is turned on by default, it causes infinite looping. To fix this, you could use |
Ah interesting, I can't push to I'm calling |
Yes in that case you can call |
Hmm so I'm calling the new function like this:
and then inside the function even doing just this makes it infinite loop on creating the Tool for
Looking at the code I do not really understand why is_flag_supported_inner doesn't infinite loop on the first snippet already, can you explain what's different about these two calls? Sorry, not very familiar with the codebase.. |
Sorry, can you clarify on what function is |
Yep, I'm calling |
Sorri I made a mistake
So calling it there would trigger infinite loop. To fix that, you need to disable inherited flags detection in it: |
Ah indeed you're right, now it makes sense - many thanks! :) |
306b961
to
a9098fa
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.
Thanks for starting on this 🎉!
e76efbc
to
fc181eb
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.
Looks a lot better to me, thanks for doing the work! Only nits from my side now.
Where applicable, detect which RUSTFLAGS were set for rustc and convert them into their corresponding cc flags in order to ensure consistent codegen across Rust and non-Rust modules.
fc181eb
to
1a8e8de
Compare
Should be good now I think :) |
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.
Thanks!
I am happy about the direction and general code, just a few questions regarding panic, ergonomic and some simple optimization
match flag { | ||
// https://doc.rust-lang.org/rustc/codegen-options/index.html#code-model | ||
"-Ccode-model" => { | ||
self.code_model = Some(value.expect("-Ccode-model must have a value")); |
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.
Can we return an error instead of panicking?
IMO we can't fully rule out possibility of invalid input, and users could change env var before calling cc, so it's better to return an error, or ignore it and print out a warning.
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.
@madsmtm specifically asked me add this so maybe he should weigh in here? I think CARGO_ENCODED_RUSTFLAGS is only supposed to be set by cargo so this should be fine. If a user manually sets this env var to something that wouldn't be accepted through RUSTFLAGS then they're breaking the assumptions cargo makes anyway.
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.
Yeah, I requested it in #1279 (comment), copying my reasoning there:
This way, we know that a bare
-Ccode-model
wasn't specified (yes, this wouldn't be accepted by rustc, but we'd enter a wrong state if it was accepted in the future).
I'd be fine with a warning, ignoring the flag or returning an error, I just don't really want us to assume that it's something it's not.
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.
Returning an error could work, how about this?
fn flag_ok_or(flag: Option<String>, msg: &'static str) -> Result<String, Error> {
flag.ok_or(Error::new(ErrorKind::InvalidArgument, msg))
}
match flag {
// https://doc.rust-lang.org/rustc/codegen-options/index.html#code-model
"-Ccode-model" => {
self.code_model = Some(flag_ok_or(value, "-Ccode-model must have a value")?);
}
I can add a new ErrorKind too if we want to distinguish them
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.
Yeah I think a new ErrorKind
makes sense
Alternatively, if you decide to do logging instead, I would like the result of rustc flags to be discarded as it is ill-formed
fn arg_to_bool(arg: &str) -> Option<bool> { | ||
match arg { | ||
"y" | "yes" | "on" | "true" => Some(true), | ||
"n" | "no" | "off" | "false" => Some(false), | ||
_ => None, | ||
} | ||
} |
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.
fn arg_to_bool(arg: &str) -> Option<bool> { | |
match arg { | |
"y" | "yes" | "on" | "true" => Some(true), | |
"n" | "no" | "off" | "false" => Some(false), | |
_ => None, | |
} | |
} | |
fn arg_to_bool_impl(arg: &str) -> Option<bool> { | |
match arg { | |
"y" | "yes" | "on" | "true" => Some(true), | |
"n" | "no" | "off" | "false" => Some(false), | |
_ => None, | |
} | |
} | |
fn arg_to_bool(arg: impl AsRef<str>) -> Option<bool> { | |
arg_to_bool(arg.as_ref()) | |
} |
By accepting any impl AsRef<str>
, you can simplify the .map_or
to be:
.map_or(Some(...), arg_to_bool)
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.
How about just this then? Not much point making it into two functions since it's only used here to avoid code duplication
fn arg_to_bool(arg: impl AsRef<str>) -> Option<bool> {
match arg.as_ref() {
"y" | "yes" | "on" | "true" => Some(true),
"n" | "no" | "off" | "false" => Some(false),
_ => None,
}
}
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.
Yes that's fine to me, the reason I create two function is to avoid duplicate code monomorphisation, but it probably won't make much difference in real life so go for the simpler one please.
pub fn cc_flags( | ||
&self, | ||
build: &Build, | ||
path: &PathBuf, | ||
family: &ToolFamily, | ||
target: &Target, | ||
) -> Vec<OsString> { |
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.
pub fn cc_flags( | |
&self, | |
build: &Build, | |
path: &PathBuf, | |
family: &ToolFamily, | |
target: &Target, | |
) -> Vec<OsString> { | |
pub fn cc_flags( | |
&self, | |
build: &Build, | |
path: &PathBuf, | |
family: ToolFamily, | |
target: &Target, | |
args: &mut Vec<OsString>, | |
) { |
How about passing args directly so that we can push directly into it, and pass family as value since it's copyable and very small (much smaller than pointer)?
Alternatively, we can pass the tool: &mut Tool
here, whichever is more convenient.
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.
If we pass the vector as an argument, should I change the signature for push_if_supported or keep it as-is? Because I could make it into this
target: &Target,
flags: &mut Vec<OsString>,
) {
let mut push_if_supported = |flag: OsString| {
if build
.is_flag_supported_inner(&flag, path, target)
.unwrap_or(false)
{
flags.push(flag);
} else {
build.cargo_output.print_warning(&format!(
"Inherited flag {:?} is not supported by the currently used CC",
flag
));
}
};
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'm ok with both honestly, one potential problem with removing the param, is that you can't push to the args
directly and use the function at the same time.
If that doesn't happen, then yeah removing the param seems to be cleaner.
family: &ToolFamily, | ||
target: &Target, | ||
) -> Vec<OsString> { | ||
let push_if_supported = |flags: &mut Vec<OsString>, flag: OsString| { |
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.
let push_if_supported = |flags: &mut Vec<OsString>, flag: OsString| { | |
let push_if_supported = |flags: &mut Vec<OsString>, flag: String| { |
Given that all usages of this function creates String
, it would be more ergonomic to pass it to the function than doing additional .into()
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.
The into() will be done anyway because it needs to be an OsString for is_flag_supported
and to actually push it to the vector. Should I just move the into()s inside the helper function 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.
It'd have to be something along the lines of
let mut push_if_supported = |flag: String| {
let flag = OsString::from(flag);
if build
.is_flag_supported_inner(&flag, path, target)
.unwrap_or(false)
{
flags.push(flag);
} else {
build.cargo_output.print_warning(&format!(
"Inherited flag {:?} is not supported by the currently used CC",
flag
));
}
};
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.
Some of the flags are &str as well so if we start taking String it'll be &str -> String -> OsString for those instead of &str -> OsString directly as it is now. For the ones that use format!() and do create a String it'd just be moving the call converting them to OsString. I can do it if you'd like me to but I think this change adds some overhead for certain flags.
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.
Maybe we can use impl Into<OsString>
instead?
That'd work with &str
, String
or OsString
, and no extra allocation.
There's a merge conflict, I'd fine with either a merge or rebase, whatever is convenient to you. |
Where applicable, detect which RUSTFLAGS were set for rustc and convert them into their corresponding cc flags in order to ensure consistent codegen across Rust and non-Rust modules.
Resolves #1254