Skip to content
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

Consider distributing static binaries #65

Closed
osa1 opened this issue Dec 23, 2017 · 13 comments
Closed

Consider distributing static binaries #65

osa1 opened this issue Dec 23, 2017 · 13 comments

Comments

@osa1
Copy link
Owner

osa1 commented Dec 23, 2017

Per @reading123's request in #61,

I think producing static binaries is a good idea. tiny is light on dependencies (serde being the largest one) and the only culprit for Linux seems to be OpenSSL (or LibreSSL) which is required by native_tls. I actually managed to compile tiny using x86_64-unknown-linux-musl target, but (1) the binary didn't work (some kind of loader problem) (2) it still had libssl.so.1.1 and libcrypto.so.1.1 as dynamic dependencies.

Need to investigate further. It might help to find another Rust project that (1) generates static binaries (2) uses native_tls crate.

@BrianOn99
Copy link
Contributor

If it is found very difficult to overcome these problems, we may also consider appimage or flatpak. From my experience both works decently on Arch and ubuntu.

@saethlin
Copy link

saethlin commented Dec 23, 2017

To get openssl to link statically, just don't let the build script see the dynamic libraries. I downloaded openssl-1.1.0g from here, and built them myself. Then I made a directory inside the source one called lib and moved the static libraries into that. Then you can build tiny with OPENSSL_DIR=/home/username/openssl-1.1.0g/ cargo build and you'll have no openssl dependency at link time.

The much harder problem I've run into is getting the allocator to link properly. I get a huge pile of undefined symbols, all ending in _chk. As far as I can tell from reading this and related issues the issue is that these __memcpy_chk are source fortification that I think is inserted by gcc to protect against mistakes with the more dangerous bits of the C API. It looks like ring decided to discard the fortification for musl builds. Maybe we should do the same? Maybe with a fork of openssl-sys?

(just remembered that openssl-sys doesn't actually build openssl. I think that's silly, but it looks like ring does?)

@saethlin
Copy link

saethlin commented Dec 23, 2017

I have a solution.

Download the openssl-1.1.0g sources. Configure with CC=clang ./config no-async, then make. Move the static lib files to a lib directory inside the source directory. Then build tiny with OPENSSL_DIR=/openssl/source/path cargo build --target=x86_64-unknown-linux-musl.

ldd tells me that the output binary is not a dynamic executable, and it's 7.1 MB but compresses down to 2.6 MB with debug symbols.

Can anyone confirm that this works on other machines and that the binary is actually portable?

@osa1
Copy link
Owner Author

osa1 commented Dec 24, 2017

It worked! Thanks @saethlin .

We should probably write a script that does all this (fetch openssl, unpack, compile with clang and no-async, move .a files to a dir, install toolchain, compile tiny with correct parameters). I may be busy for a few days so if anyone would want to go for it it'd be appreciated.

@ghost ghost mentioned this issue Jan 5, 2018
@ghost
Copy link

ghost commented Jan 5, 2018

@saethlin I got this working too in my Arch VM, but my binary is 16.3 MB (with --release). How do you compress it?

@ghost
Copy link

ghost commented Jan 5, 2018

Might need some work.

@saethlin
Copy link

saethlin commented Jan 5, 2018

@wrycode That's strange, my release binary with LTO is 7.2 MB. You can strip the binary to cut down the size a lot, usually by 50% but you'll never get a helpful backtrace out of that binary because you've removed all the debug symbols. After all that you can still compress it. I just used gzip tiny --keep, you can also throw --best at it but the compression ratio doesn't improve much. You'll need to unzip the file as part of the your distribution. If you don't want to do that there are executable compressors you can throw at it. I've never used one but I hear they're quite good, and the file you get out seems like a normal executable it just starts up a bit slower.

@osa1
Copy link
Owner Author

osa1 commented Jan 6, 2018

I got this working too in my Arch VM, but my binary is 16.3 MB (with --release)

This doesn't seem right, on my system (Xubuntu 16.04) this method generates 4,7M binary.

@ghost
Copy link

ghost commented Jan 8, 2018

I was looking at the wrong file output, apologies. Mine was 4.9M

@saethlin
Copy link

FYI, if you want to do this you'll run into the situation described here:
cross-rs/cross#119

I'm applying the workaround by inserting this at the top of main:

if let Err(std::env::VarError::NotPresent) = std::env::var("SSL_CERT_DIR") {
    std::env::set_var("SSL_CERT_DIR", "/etc/ssl/certs");
}

osa1 added a commit that referenced this issue Jan 29, 2020
With this I can make a statically linked executable with

    $ cargo build --target=x86_64-unknown-linux-musl \
        --no-default-features --features tls-rustls

The generated executable does not support generating desktop
notifications and uses rustls instead of libssl.

(#65)
@osa1
Copy link
Owner Author

osa1 commented Jan 29, 2020

With #172 we can now build tiny with rustls, so no need to statically link OpenSSL or LibreSSL.

I just submitted another PR for making dbus dependency optional: #178 hides it behind a feature flag (enabled by default). Using it I can generate a statically linked executable simply by running this in tiny/ directory:

cargo build --target=x86_64-unknown-linux-musl --no-default-features --features tls-rustls

The generated executable uses rustls for TLS and does not support generating desktop notifications.

osa1 added a commit that referenced this issue Jan 31, 2020
With this I can make a statically linked executable with

    $ cargo build --target=x86_64-unknown-linux-musl \
        --no-default-features --features tls-rustls

The generated executable does not support generating desktop
notifications and uses rustls instead of libssl.

(#65)
osa1 added a commit that referenced this issue Feb 14, 2020
This makes dbus dependency opt-in by making desktop notifications an
optional feature.

We also make native-tls optional and rustls the default. The idea is
that `cargo build` will be easier to build because it doesn't need any
system dependencies, and features that need system dependencies will be
hidden behind feature flags.

After this patch: (all commands in 'tiny' directory)

- dbus + rustls:
  cargo build --features="desktop-notifications"
- dbus + native-tls
  cargo build --features="desktop-notifications tls-native" \
              --no-default-features

Removing "desktop-notifications" will make a build without dbus
dependency.

It's now possible to make a statically-linked executable with

    $ cargo build --target=x86_64-unknown-linux-musl

(#65)
osa1 added a commit that referenced this issue Feb 14, 2020
This makes dbus dependency opt-in by making desktop notifications an
optional feature.

We also make native-tls optional and rustls the default. The idea is
that `cargo build` will be easier to build because it doesn't need any
system dependencies, and features that need system dependencies will be
hidden behind feature flags.

After this patch: (all commands in 'tiny' directory)

- dbus + rustls:
  cargo build --features="desktop-notifications"
- dbus + native-tls
  cargo build --features="desktop-notifications tls-native" \
              --no-default-features

Removing "desktop-notifications" will make a build without dbus
dependency.

It's now possible to make a statically-linked executable with

    $ cargo build --target=x86_64-unknown-linux-musl

(#65)
osa1 added a commit that referenced this issue Feb 14, 2020
This makes dbus dependency opt-in by making desktop notifications an
optional feature.

We also make native-tls optional and rustls the default. The idea is
that `cargo build` will be easier to build because it doesn't need any
system dependencies, and features that need system dependencies will be
hidden behind feature flags.

After this patch: (all commands in 'tiny' directory)

- dbus + rustls:
  cargo build --features="desktop-notifications"
- dbus + native-tls
  cargo build --features="desktop-notifications tls-native" \
              --no-default-features

Removing "desktop-notifications" will make a build without dbus
dependency.

It's now possible to make a statically-linked executable with

    $ cargo build --target=x86_64-unknown-linux-musl

(#65)
@osa1
Copy link
Owner Author

osa1 commented Feb 14, 2020

Almost done.. only problem is ring-0.16 doesn't build on Github actions' Ubuntu 18.04 container for some reason. The error message can be seen here. I also reported this on ring's issue tracker.

@osa1
Copy link
Owner Author

osa1 commented Feb 15, 2020

This is now implemented. When I push a new version tag the Github workflow makes tarballs for all combination of features and uploads them. See v0.5.2-test as an example.

@osa1 osa1 closed this as completed Feb 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants