Support distributed cross-compilation on Windows#271
Conversation
43c36ea to
6a4e9a3
Compare
| } | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
For the purposes of distributed compilation it's not sufficient to just 'pass through' any -Xclang options as suggested in #262 - it's important to filter out the preprocessor-only args, which the dependency stuff falls under.
I'm not sure what the best solution long-term is - by the time you've created a clang (or rather, gcc) ParseArguments you've lost a lot of information (e.g. "there is another input file" turns into "here's an arg") which reduces our accuracy. I think you almost want an interim LosslessParsedArgs so you can inspect it e.g. within this function to see how it affects the msvc compilation.
For now it just grabs the pieces that can be easily partitioned into common or preprocessor args without any side effects and bails otherwise.
There was a problem hiding this comment.
This seems like a reasonable way to handle things for now. It might be worth refactoring things in the future so that we could use something less leaky like parse_arguments, but I won't lose any sleep over it. clang-cl is an odd duck, and dealing with -Xclang is even odder. I'd probably want to read the clang-cl source to figure things out before anyone spent any more time on this.
| depfile: depfile.map(|d| d.into()), | ||
| outputs: outputs, | ||
| preprocessor_args: vec!(), | ||
| preprocessor_args: preprocessor_args, |
There was a problem hiding this comment.
I was a little unclear why all of these used to go in common args - it's pessimistic so wouldn't be a problem, and I couldn't see anything in the git history indicating why it's like this.
There was a problem hiding this comment.
I couldn't figure this out either, TBH. This is definitely better, though.
| take_arg!("-Xclang", String, Separated, PassThrough), | ||
| // TODO: should be extracted and reprocessed, though bear in mind some | ||
| // flags are not valid under a -Xclang | ||
| take_arg!("-Xclang", String, Separated, TooHard), |
There was a problem hiding this comment.
It felt a bit dubious to me to just pass these through blindly, though there is one test below that depended on it working. This isn't necessary for this PR, but I stumbled across it and thought I might tweak it, particularly given the work that msvc.rs now does indicates there could be all kinds of things hidden in here.
There was a problem hiding this comment.
I didn't realize this was even here, honestly. I don't think I've ever seen this used outside of clang-cl.
e0fe8db to
ad4ea62
Compare
ad4ea62 to
2707dfd
Compare
| pub archive: PathBuf, | ||
| pub archive_compiler_executable: String, | ||
| } | ||
|
|
There was a problem hiding this comment.
This is the way you specify a cross compiler toolchain - the compiler executable you want to override, an archive and a path within the archive you use to override the path of the compiler executable.
There was a problem hiding this comment.
OK, we can see how people like this in practice. I don't suppose it's worse than the current way people create cross-toolchains for icecream.
|
Oh, I hadn't realised this needed a rebase - odd, I was already rebasing on top of the last PR. |
| enum Void {} | ||
|
|
||
| // Only supported on x86_64 Linux machines | ||
| #[cfg(all(target_os = "linux", target_arch = "x86_64"))] |
There was a problem hiding this comment.
Is there a way to control this at the cargo level so that we don't build it on other platforms?
| fn into_inputs_creator(self: Box<Self>) -> Result<Box<FnMut(&mut io::Write)>> { | ||
| fn into_dist_inputs_creator(self: Box<Self>, path_transformer: &mut dist::PathTransformer) -> Result<Box<FnMut(&mut io::Write)>> { | ||
| let input_path = self.cwd.join(&self.parsed_args.input); | ||
| let dist_input_path = path_transformer.to_dist(&input_path).unwrap(); |
There was a problem hiding this comment.
Any particular reason this has an unwrap?
| let input_path = self.cwd.join(&self.parsed_args.input); | ||
| let dist_input_path = path_transformer.to_dist(&input_path).unwrap(); | ||
| // tar-rs imposes that `set_path` takes a relative path | ||
| assert!(dist_input_path.starts_with("/")); |
There was a problem hiding this comment.
These shouldn't be actual assert!s, should they? Those will take down the whole server.
| info!("Packaging C compiler"); | ||
| // TODO: write our own, since this is GPL | ||
| let curdir = env::current_dir().unwrap(); | ||
| env::set_current_dir("/tmp").unwrap(); |
There was a problem hiding this comment.
Wouldn't it be cleaner to set the cwd of the Command instead?
| let (compile_cmd, _dist_compile_cmd, cacheable) = compilation.generate_compile_commands().unwrap(); | ||
|
|
||
| let mut path_transformer = dist::PathTransformer::new(); | ||
| let (compile_cmd, _dist_compile_cmd, cacheable) = compilation.generate_compile_commands(&mut path_transformer).unwrap(); |
There was a problem hiding this comment.
I missed this previously, but this should propagate errors instead of using unwrap.
| let compile_out_pretty = out_pretty.clone(); | ||
| let compile_out_pretty2 = out_pretty.clone(); | ||
| let (compile_cmd, dist_compile_cmd, cacheable) = compilation.generate_compile_commands().unwrap(); | ||
| let compile_out_pretty3 = out_pretty.clone(); |
There was a problem hiding this comment.
I guess it would be nice to figure out something smarter here, eh?
| let dist_output_paths = compilation.outputs() | ||
| .map(|(_key, path)| path_transformer.to_dist_abs(&cwd.join(path))) | ||
| .collect::<Option<_>>() | ||
| .unwrap(); |
| .and_then(move |(path_transformer, mut dist_compile_cmd, dist_inputs_creator, dist_output_paths)| { | ||
| debug!("[{}]: Identifying toolchain", compile_out_pretty2); | ||
| let toolchain_creator_cb = BoxFnOnce::from(move |f| toolchain_creator.write_pkg(f)); | ||
| // TODO: put on a thread |
There was a problem hiding this comment.
If you don't have a fix for this TODO in a later patch, can you file a follow-up issue for it so we don't forget?
|
|
||
| let test = b"#if defined(_MSC_VER) | ||
| let test = b"#if defined(_MSC_VER) && defined(__clang__) | ||
| msvc-clang |
There was a problem hiding this comment.
I would use clang-cl here since that's how it's generally referred to.
| flag!("-Zi", DebugInfo), | ||
| flag!("-c", DoCompilation), | ||
| take_arg!("-deps", Path, Concatenated, DepFile), | ||
| take_arg!("-o", Path, Separated, Output), // Deprecated but valid |
There was a problem hiding this comment.
Is this needed for something in particular?
| } | ||
| } | ||
|
|
||
| // TODO: doing this here reorders the arguments, hopefully that doesn't affect the meaning |
There was a problem hiding this comment.
We'd probably have to look at the clang-cl source to figure this out, but hopefully it's not an issue. Maybe we can just make a note of it in the docs somewhere?
|
|
||
| let dist_command = (|| { | ||
| // http://releases.llvm.org/6.0.0/tools/clang/docs/UsersManual.html#clang-cl | ||
| // TODO: Use /T... for language? |
There was a problem hiding this comment.
This would be a useful followup. It looks like you could just replace -c with -Tc or -Tp:
https://docs.microsoft.com/en-us/cpp/build/reference/tc-tp-tc-tp-specify-source-file-type
| use std::fs::File; | ||
|
|
||
| pub trait CompilerPackager { | ||
| fn write_pkg(self: Box<Self>, f: File) -> io::Result<()>; |
There was a problem hiding this comment.
Is there a particular reason this uses io::Result instead of our custom Error/Result?
| use errors::*; | ||
|
|
||
| #[derive(Clone, Debug)] | ||
| pub struct CustomToolchain { |
There was a problem hiding this comment.
It seems a little unfortunate to have two CustomToolchain structs that differ by only one member. I gather that since this one gets stored in a HashMap the other member is the key, but it's still not great.
| let mut custom_toolchain_paths = HashMap::new(); | ||
| for ct in config_custom_toolchains.into_iter() { | ||
| if custom_toolchain_paths.contains_key(&ct.compiler_executable) { | ||
| panic!("Multiple toolchains for {:?}", ct.compiler_executable) |
| Err(e) => panic!("{}", e), | ||
| // TODO: by this point the toolchain should be known to exist | ||
| pub fn get_toolchain(&self, tc: &Toolchain) -> Option<Vec<u8>> { | ||
| // TODO: be more relaxed about path casing and slashes on Windows |
There was a problem hiding this comment.
File a followup for this? Maybe just running the paths through fs::canonicalize would be good enough.
| match self.cache.lock().unwrap().get(tc) { | ||
| Ok(rdr) => rdr, | ||
| Err(LruError::FileNotInCache) => return None, | ||
| Err(e) => panic!("{}", e), |
| rdr.read_to_end(&mut ret).unwrap(); | ||
| Some(ret) | ||
| } | ||
| // TODO: It's more correct to have a FnBox or Box<FnOnce> here |
There was a problem hiding this comment.
Well, you can remove this TODO now!
| pub fn put_toolchain(&self, compiler_path: &Path, weak_key: &str, create: BoxFnOnce<(fs::File,), io::Result<()>>) -> Result<(Toolchain, Option<String>)> { | ||
| if let Some(tc_and_compiler_path) = self.get_custom_toolchain(compiler_path) { | ||
| debug!("Using custom toolchain for {:?}", compiler_path); | ||
| let (tc, compiler_path) = tc_and_compiler_path.unwrap(); |
| Err(e) => return Some(Err(e)), | ||
| }; | ||
| let tc = Toolchain { archive_id }; | ||
| *maybe_tc = Some(tc.clone()); |
There was a problem hiding this comment.
Would this be better written using the entry API?
| mod test; | ||
|
|
||
| // TODO: paths (particularly outputs, which are accessed by an unsandboxed program) | ||
| // should be some pre-sanitised AbsPath type |
There was a problem hiding this comment.
Maybe as a first pass you could just have a newtype around PathBuf that implements AsRef?
| impl PathTransformer { | ||
| pub fn new() -> Self { PathTransformer } | ||
| pub fn to_dist_abs(&mut self, p: &Path) -> Option<String> { | ||
| if !p.is_absolute() { panic!("non absolute path {:?}", p) } |
| } | ||
| } | ||
| pub fn to_dist_abs(&mut self, p: &Path) -> Option<String> { | ||
| if !p.is_absolute() { panic!("non absolute path {:?}", p) } |
| Component::Prefix(_) | | ||
| Component::RootDir => panic!("unexpected part in path {:?}", p), | ||
| Component::Normal(osstr) => osstr.to_str()?, | ||
| // TODO: should be forbidden |
There was a problem hiding this comment.
Did you run into cases where these were necessary in practice? If not, it seems like it'd be better to forbid them now before anyone relies on this working.
luser
left a comment
There was a problem hiding this comment.
Overall everything in here looks sensible. I'd like to see all of the panic / assert / unwrap usage outside of tests cleaned up, but that can be done after everything lands.
For review after #249