Sccache, Mozilla’s distributed compiler cache, now written in Rust
blog.mozilla.org
blog.mozilla.org
And minio [1] seems to be the easiest pseudo-s3 there is ($ ./minio server CacheDir/, done?), or are there better alternatives by now?
The S3 code went through a few revisions. I was originally using Rusoto (https://github.com/rusoto/rusoto), which is nice but just didn't quite meet my needs, so then I borrowed some code from the crates.io codebase and then rewrote most of it.
You can also run it using a local disk cache, similar to ccache, but it doesn't have any code to limit the size of the cache so it's not very good right now. (It's used in all the tests, though.) Fixing that specific issue is next on my plate.
format!("{}://{}", match ssl..., host)
otherwise run the existing codepath and return the amazonaws.com string.Mozilla sometimes gets flak for its experiments (or abandoning them), sometimes deservedly, but by doing so many of them and not being afraid to cancel them, they occasionally get big wins like Rust.
I won't repeat my entire comment from Reddit, but one other notable point is that tools like ccache/distcc don't generally support MSVC, and we build Firefox for Windows with MSVC, so that's pretty important to us. And frankly, our Windows builds are slow enough that we can use all the build time wins we can get.
It's been very frustrating on multiple open source projects I've worked on, people proposing to change entire build systems of projects just so that MSVC can be included as a build target. I hope the new Bash-on-Windows stuff will eventually make supporting MSVC a little simpler from various Unix-oriented build tools.
It's worth it though. There are lots of people on Windows! And thankfully, most of the other pieces (like the build system) in Rust are cross platform by default. :-)
That's true. ccache/distcc only supports GCC. Well, LLVM probably works as well since its flags are GCC compatible.
[1]: https://www.reddit.com/r/rust/comments/5e654a/sccache_mozill...
If there's a sccache maintainer reading this thread please consider explaining the advantages of using sccache over distcc in the README.
The distcc readme says nothing of the sort? It seems to specify that it shares header files and compiles cpp files (which are all presumably sent back to the source as object files, which links them together)
It seems to work with ccache, but if you're spinning up fresh instances all the time ccache will need to be synchronized somehow, and it doesn't seem to drive that (whereas sccache seems to do so)
At that point, the requester could cache that output, should they want to.
EDIT: my bad, they were inside src/
For instance, randomly looking at the first file in that repository, cache/cache.rs, you can see at the end a submodule tagged with #[cfg(test)] (meaning it will be compiled only when running the tests), and within it several functions tagged with #[test].
Also, the compiler will tell you if you have code that's not labelled with #[cfg(test)] but is designed to run only in tests.
The point is that some unit tests cannot be written in tests/, whereas all integration tests can be.
I've tried to write unit tests where they made sense, they're generally placed within the relevant source file in the tree, like the cache tests here: https://github.com/mozilla/sccache/blob/6eadccc9d6752747766d...
I also wrote some higher-level tests that test the server functionality: https://github.com/mozilla/sccache/blob/master/src/test/test...
I rolled my own set of traits and structs to mock process execution for those tests so they would run independent of the compilers installed on the system: https://github.com/mozilla/sccache/blob/master/src/mock_comm...
I actually like how that code turned out, I've thought about cleaning it up and publishing it as a standalone crate. Rust doesn't have a real test mocking solution yet, and even if it did this is a special case since the standard library's process execution types don't implement a trait that could be mocked.
As development went on I realized I didn't have automated tests that tested the whole program, especially running against real compilers (which is important given that the tool is a compiler wrapper), so I wrote some "system" tests that run the actual binary with an actual compiler and local disk cache and verify that it works as expected: https://github.com/mozilla/sccache/blob/master/src/test/syst...
enum Message {
Quit,
ChangeColor(i32, i32, i32),
Move { x: i32, y: i32 },
Write(String),
}
fn quit() { /* ... */ }
fn change_color(r: i32, g: i32, b: i32) { /* ... */ }
fn move_cursor(x: i32, y: i32) { /* ... */ }
fn process_message(msg: Message) {
match msg {
Message::Quit => quit(),
Message::ChangeColor(r, g, b) => change_color(r, g, b),
Message::Move { x: x, y: y } => move_cursor(x, y),
Message::Write(s) => println!("{}", s),
};
}It does not insist on a default clause it requires match completeness, which depending on the matched value may require a default clause (e.g. it does for integrals[0], it does not for enums as you can match each case individually)
> it's just like C's switch, right?
Only in its most simplistic form (though even then it does not ever fall through — whether by default or optionally — is type-safe and requires match-completeness), match performs refutable pattern matching on possibly complex values and allows additional per-match conditionals.
match foo {
// complex destructuring and multiple patterns for a case
Some((42, _)) | Some((55, _)) => { println!("1") }
// simple destructuring + conditional
Some(a) if a.0 % 5 == 0 => { println!("2") }
// matching + wildcard
Some(..) => { println!("3") }
// trivial matching
None => { println!("4") }
}
or match (i % 3, i % 5) {
(0, 0) => {}
(0, _) => {}
(_, 0) => {}
_ => {}
}
[0] because the completeness checking doesn't really handle non-enum values currentlyC/C++ switch makes no assertion about what is or isn't valid in its branches. That is, you might be switching on a tag field of a union or something like that, and then in one (or more) of the switch branches you may act on that information. But there is no compiler constraint on the correctness of that decision.
Pattern matching insists that the code inside a particular branch matches the type expectations asserted in its case clause. If you're in the branch for enum-type Blah, you can only act on Blah and not on Blorp. The compiler will force this on you.
To put this in practical terms, one area I have found this incredibly valuable (in Swift, but it applies here too) is in state machines. If you represent your state machine as an enum/union with fields for the information any particular state needs, every iteration through the machine you can be sure you are acting on the correct information. The compiler won't let you do otherwise.
1. No fallthrough-by-default.
2. Checked for exhaustiveness. This is different from "insisting on a default clause", because if you see a match with no default clause, then you know that the compiler is verifying that every possible case is being handled.
3. The cases of the match can be arbitrary patterns, not just integers. This allows you to perform very natural and powerful conditional control flow, especially when using tagged unions.
4. Can return a value.
Finally, remember the context of this post: Python doesn't have a C-style switch statement at all. :P
Edit: match doesn't insist on a default clause, it enforces exhaustiveness. Which can be achieved by having a default clause, but quite often you just have an explicit branch for every possible case.
not at all, absolutely not. consider:
switch (count % 8) {
case 0: do { *to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
} while (--n > 0);
}
https://en.wikipedia.org/wiki/Duff's_device1) Going from programming language X to Y 2) Rewriting the algorithm in language Y
I suspect (without any proof) that too much is attributed to 1) and not enough to 2).
Restart the process again after a few years of years, as is tradition.
I don't think anyone would argue that interpreters like CPython have longer startup times than compiled machine code.