I'm trying to create a Windows 32-bit exe. For this, I'm trying out a "Hello World" Message box from this gist. It seems that even with -C lto the file size is ~750K. After compression with upx, it's about 360K.
Are there any ways to reduce the file further?
This is still using llvm, right? Is there a way to use the Visual Studio infrastructure? I'm currenltly on 1.0 stable, but I'm ok with trying out the nightlies.
No, you can't really use MSVC yet. I forget the reason why LTO doesn't yet strip a Rust executable down as much as it should.
But honestly, why does it matter? It's not like this is indicative of some multiplicative factor on binary size; it's the overhead of the standard library. If you compile with prefer-dynamic (forget where exactly to set this), the executable shrinks down dramatically, but then you have to distribute the stdlib DLLs which are probably even bigger.
If you genuinely need to make small executables on the order of 10s of kilobytes, well, you're probably in the realm of #[no_std], in which case conventional advice no longer applies and you just throw out everything and write only what you actually need.
Thanks a lot for your insightful answer and links to the reddit threads!
My use case is: I'm experimenting with replacing a C++ application that is downloaded a lot and thus needs to be rather small in size. The application I'm trying to replace is around 400K including texts in multiple languages, icons etc. I have a harder time justify replacing it with rust if the binary ends up with double the size.
From the two threads you linked, it seems that going with #[no_std] can yield really tiny tiny binaries. Is there a way to selectively link the parts I actually need? I would have expected LTO to do that, but I guess it doesn't work on the stdlib (yet).
If I want to go down the hard road and go with no_std and use libcore instead, it seems I don't even have dynamic allocations nor strings. Is there a way to import these "selectively"?
I know I have a kinda special use case there and the rust team probably has more important things to work on at the moment. So in the worst case, I need to find another pet project to really try our rust
libstd, aside from defining a lot of things, also acts as a facade over other, small libraries. For example, libcore, liballoc, libcollections. You can try just linking the sub-std libraries you actually want.
That said, doing so is not explicitly supported, and isn't possible at all in stable Rust; you'll need to use a nightly compiler.
Being able to import the different libraries selectively is indeed very cool!.
I would like to try that but it seems I need to use the nightlies for that (stable and beta refuse these "unstable" features). However, the nightly can't find "ar":
E:\msgbox>cargo clean && cargo build --verbose --release
Compiling msgbox v0.1.0 (file:///E:/msgbox)
Running `rustc src\main.rs --crate-name msgbox --crate-type bin -C opt-leve
l=3 -C lto --out-dir E:\msgbox\target\release --emit=dep-info,link -L dependency
=E:\msgbox\target\release -L dependency=E:\msgbox\target\release\deps`
error: could not exec `ar`: The system cannot find the file specified.
(os error 2)
error: aborting due to previous error
Could not compile `msgbox`.
Is there a way to run a more err... "stable" version of the nightlies? Preferably one that is identical to 1.0 or maybe 1.1 beta, just with the unstable/experimental stuff enabled. (I tried searching for something like that but failed)
Well, you could run the nightly from the same night that stable was built, but it's the same thing, just without the feature gate stuff. So this AR issue will still happen, I'd guess...unless it was a recently introduced bug.
@steveklabnik Well the same code compiles on stable and beta, so I'd guess it's a recent bug. Actually, to me it looks like a "file not found" of a crucial tool during linking (or just before or after that). So it must be a rather recent issue...
However, I'm not sure I'm using #[no_std] right, as the file size stays the same. On the other hand, my test code uses two strings, so maybe that's why
I'll look into this more tomorrow. Thanks for you help everyone!
It might be that it's #![no_std] (not #[no_std]) and it has to be at the top of the crate root file (so either src/lib.rs or src/main.rs if you're building a library or binary with cargo). You may also need to remove a feature gate for it, but the compiler should tell you about that one.
However, once I include the "collections" crate, the file size exploded to 660 KB (303 after upx).
Do you think it's worthwhile copy&pasting the Vec and String parts of the collections crate? I'm not sure if these two are actually the ones blowing up the file size...
Now I tried to run this exe on another computer. I errors out saying it cannot find some libgcc dll. This happens also for the non-stripped, non-upx-compressed file.
Any ideas? I would like to create a redistributable file that can run on any Windows computer.
(Edit: Sorry that my google-fu is so poor with rust. But I couldn't find anything related to this issue)
Oh yeah... Rust needs that for unwinding support on Windows. There's no way around it aside from moving to MSVC (which you can't, just yet).
And don't forget, if you distribute that file, you have to also distribute the license and make the source code available to anyone who might request it. I forget which license (it's one of the *GPLs, probably GPL3 with runtime exception).
Additionally, I believe that, due to the way it works, you can't statically link it, either. Or that might have just been legally impossible... it's been a while since I tried to work this stuff out and got multiple, contradictory answers on the subject.Edit: see comment below.
There's no way around it aside from moving to MSVC (which you can't, just yet).
Well, you can link libgcc statically as well: `rustc -Clink-args=-static-libgcc. This breaks cross-module stack unwinding, but in this case you probably don't care.
However, this will also add ~1MB of libgcc code into your executable, because on Windows ld does not support dead code removal (actually, I'm not sure about the ratio of live/dead code we link from libgcc).
There's been a bunch of discussion about adding compilation mode that turns panics into aborts (thus removing the need for stack unwinding), but nothing like that has been implemented yet.
And don't forget, if you distribute that file, you have to also distribute the license and make the source code available to anyone who might request it. I forget which license (it's one of the *GPLs, probably).
Not quite... libgcc is one of the libraries covered by the linking exception clause, so you are exempted from such requirements for that one.
I went back and re-read the license, but again, I don't see anything that implies that libgcc is exempt from the distribution conditions. The output of the compiler is, but libgcc isn't "Target Code", though any parts of libgcc that end up inside your executable are.
Unless I see something from an actual FSF lawyer explicitly saying otherwise, I will continue to operate under the assumption that distributing libgcc without making the source available is illegal.
Also there's an old thread on the GCC mailing list...
The rationale for creating the exception was "to allow developers to use GCC's libraries to compile any program, regardless of its license", so I'll take that for an answer. Also, FAQ entry #6 answers the question about static vs dynamic linking quite clearly (that is, there is no difference).
Ok, I tried building with -Clink-args=-static-libgcc.
This increases the file size to:
Cargo build: 875 KB
stripped: 414 KB
upx compressed: 148 KB
This is quite acceptable. (Not sure about the GPL implications though, as I'm paid to write close source software - but let's keep this out of the thread)
The file seems to run on Windows Vista and Windows 8.1.