Installing Rust + MSVC + native libs
Running rust programs is easy enough. Compiling them is also generally a breeze so long as you're playing inside cargo's walled garden. Where you might come unstuck though is with compiling native code and with native library dependencies.
I've put together this as a guide on how to get some common things working on windows with the 64bit MSVC flavour of the rust toolchain as the primary toolchain, although in a couple of places we need to mix and match and use the gnu toolchain as well.
Credit where it's due, the openssl section exists thanks to the instructions here: https://facility9.com/2016/03/installing-rust-on-windows/
For installing packages on windows I normally look first at http://chocolatey.org but in most of these cases the package we need either isn't there or is only there in a 32bit flavour. I use powershell but for the most part you can use cmd.exe instead if you prefer.
Please read the instructions carefully even if you're going to skip a step or adapt the instructions to suit; we'll need everything we install early on to make the later things work.
We're going to install the following programs:
rustup |
rust |
vs2015 community |
sqlite3 |
postgresql |
openssl |
diesel_cli |
Rustup
If you aren't already using a rust version manager now is a good time to install one. Unix OSs have had multirust for a while. There's also a rust reimplementation called multirust-rs which works on windows. Recently multirust-rs has morphed into a new and improved version called rustup.
You can download and install it from http://rustup.rs.
Now use rustup to install the two 64bit toolchains that we'll need by running these commands:
rustup update stable-x86_64-gnu
rustup update stable-x86_64-msvc
rustup will put the installed toolchains in %HOME%/.multirust/toolchains
. (or maybe by the time you read this the directory will have been renamed to match rustup's name). This is different to multirust-rs which used %LOCALAPPDATA%/.multirust/toolchains
.
To make the msvc toolchain the default, run:
rustup default stable-x86_64-msvc
If you need to override it for a single project switch to the project directory and create an override like so:
rustup override add stable-x86_64-gnu
Hopefull that won't be necessary too often.
~/.cargo/bin
Cargo is now an essential program for building and managing dependencies in rust projects and comes as part of your rust installation. rustup will already have installed cargo for you alongside rustc. Cargo is also capable of installing built rust programs to a central location. We'll use this feature at the end to install the diesel_cli. You can also use this mechanism to get rust extensions like clippy and rustfmt.
When using cargo installed by rustup, anything installed by cargo will go in $HOME/.cargo/bin
(multirust-rs used %LOCALAPPDATA%/.multirust/cargo/bin
). Rustup also standardises on using this directory as a place to install shims for rustc, rustdoc etc. Those are proxies for the actual implementations in their respective toolchain directories. To make this all work, rustup should have add this directory to your %PATH%
environment variable during setup. You shouldn't need to change anything here, but you can verify everything is in its place if you like.
Visual Studio C++ tools
The msvc rust toolchain requires that you have the microsoft c++ tools installed. There's a standalone installation called Visual C++ Build Tools which was recently released. Alternatively you can install the required tools with Visual Studio.
Chocolatey does have packages for msvc but to get the required tools requires creating and editing an unattended installation prompt file and it's a bit fiddly.
To install visual studio, either download and install VS2015 Community Edition or use Microsoft's Web Platform Installer if you have it (requires elevated permissions):
C:\Program Files\Microsoft\Web Platform Installer\webpicmd.exe /Install /Products:VS2015_CE_Only
The msvc installer leaves quite a lot of options unchecked. Please make sure you select the c++ tools. The whole visual studio installation is currently quite a big download and a slow process but future versions will be much leaner.
sqlite3
SQLite itself is one C source file and one header file. You could quite happily include the source in your program and use the gcc crate to compile it directly if you wanted.
The sqlite3-sys crate doesn't bundle it though -- it expects to find it already installed. Helpfully it does detect the install location using an environment variable we can set.
First download the zipped 64bit windows binaries directly from sqlite.org.
I'm going to unzip the files to C:\tools\SQLite64
. You can adapt these instructions as needed if you installed the files elsewhere.
Wherever you put them, you should have a .dll
and a .def
file there and nothing else. This is all you need for mingw but it isn't quite enough for msvc's linker. We'll want to generate a .lib
file from the .def
file.
If you don't already have one handy, you can use this command line in a shortcut to run a cmd terminal with the 64bit msvc toolchain in the path:
%comspec% /K "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
Then run that shortcut and use the following commands to generate the lib file:
cd C:\tools\SQLite64
lib /def:sqlite3.def /out:sqlite3.lib
Next for the sqlite3-sys crate we'll need to set an environment variable which it uses to tell cargo which directory to add to the link path. You can either do it the old-fashioned click-clicky way using the System Properties gui, or use powershell. This sets the environment variable for just your user:
set-itemproperty hkcu:environment -name SQLITE3_LIB_DIR -value "C:\tools\SQLite64"
You can alternatively set it systemwide from a powershell prompt with elevated permissions:
set-itemproperty "hklm:system/currentcontrolset/control/session manager/environment" -name SQLITE3_LIB_DIR -value "C:\tools\SQLite64"
There's one final critical step, and it's a real pain. DLLs need to be on the library search path at runtime: https://msdn.microsoft.com/en-us/library/7d83bc18.aspx. The only dial we have to turn here is the contents of the PATH environment variable. This means you either need to setup your path every time you need it perhaps using batch scripts, or set it system wide. Welcome to dll hell. For now, add the sqlite directory to your path. In production I recommend keeping a tight control over your dependencies and bundling any dlls you need in the same folder as your exe.
Don't forget to close and re-open your command prompt in order to pick up the changed environment variable value.
OpenSSL
The chocolatey package is only the light version and doesn't include the development files we need to compile programs against it so we'll have to get it from elsewhere. The best compiled package I've found is available from http://slproweb.com/products/Win32OpenSSL.html. You'll want the latest full-fat 64bit version (not the "Light" version). I used 1.0.2g.
Once you've got it you need to create a few environment variables to keep the openssl crate happy:
DEP_OPENSSL_INCLUDE | C:\OpenSSL-Win64\include |
OPENSSL_INCLUDE_DIR | C:\OpenSSL-Win64\include |
OPENSSL_LIB_DIR | C:\OpenSSL-Win64\lib\VC |
OPENSSL_LIBS | ssleay32MT:libeay32MT |
Don't worry about the "32" in the library names.
The MT variants are the static libraries so you shouldn't need to put them on your path at runtime.
postgresql
Postgres will be useful when it comes to developing more interesting applications. It already has good support in rust and works well with the diesel ORM.
You can technically run postgres inside a VM or docker container if you prefer, but the diesel cli below depends on the client library libpq.dll
from the postgres install. Since I haven't found a client-only package for windows this still seems like the best way to get it.
The most up-to-date builds seem to be available from http://www.enterprisedb.com/products-services-training/pgdownload#windows. You'll need the 64bit version. Go ahead and install it now. You'll be prompted for a password for the database's postgres user; keep a note of that somewhere safe.
If you don't want to run postgres all the time you can stop it using either the services snap-in (run comexp.msc
from the command line) or do it with some powershell:
$pg_svc = (get-service | where-object {$_.name -like "postgres*"}).name
stop-service -name $pg_svc
set-service -name $pg_svc -startuptype manual
When you're done, check that the path to the postgres bin folder was added to your path. Don't forget to close and re-open any command prompts. The pq-sys crate uses pg_config.exe
to lookup the linker path for libpq.dll
.
You'll need to perform the same trick again with the dll library search by by adding postgres's lib folder to your PATH environment variable. On my system it's in C:\Program Files\PostgreSQL\9.5\lib
. If you're unlucky by this point your PATH environment variable will have grown so big it can even break things: http://superuser.com/questions/635082/too-many-folders-in-the-path-variable.
diesel_cli
This is the first rust program we'll try and install. It's a good validation of what's gone before since it depends on sqlite and postgres. That also means it's the part where this can blow up horribly if you've mixed up your 32bits with your 64bits.
rustup run stable-x86_64-gnu C:\Windows\System32\cmd.exe
cargo install diesel_cli
If you were unable to install either postgres or sqlite earlier you can turn them off with the features flag and have another go, or try and diagnose and fix the problem using some of the tips below.
Common Problems
The most likely problem if anything goes wrong is a linker error right at the end of compiling your rust program a little bit like in this stackoverflow question. The msvc linker will also sometimes complain it can't find a dll such as in the case of libpq.dll
. It expects it to be called pq.dll
i.e. without the "lib" prefix. That's usually a clue anyway that the library was compiled with mingw and therefore won't be compatible with the msvc toolchain. Libraries written in C like sqlite should work with both compilers, but C++ libraries definitely won't.
The other common error is a mismatch between your 64bit program and a 32bit library. It could also be the other way around. In order to fix mismatched libraries you'll need to double check that you're using the 64bit rust toolchain with the right corresponding 64bit native libraries. Hopefully though if you followed this guide then it'll work first time. As a last resort you can try compiling the native library from source but there you're really at the mercy of the library authors.
If you're a bit stuck you can try running dumpbin from the visual studio command prompt:
dumpbin /headers sqlite3.dll | more
Here's the output for the 32bit dll:
Microsoft (R) COFF/PE Dumper Version 14.00.23506.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file sqlite3.dll
PE signature found
File Type: DLL
FILE HEADER VALUES
14C machine (x86)
12 number of sections
5707D320 time date stamp Fri Apr 8 16:49:52 2016
AD000 file pointer to symbol table
F1C number of symbols
E0 size of optional header
2106 characteristics
Executable
Line numbers stripped
32 bit word machine
DLL
For comparison, here's the 64bit version:
Microsoft (R) COFF/PE Dumper Version 14.00.23506.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file sqlite3.dll
PE signature found
File Type: DLL
FILE HEADER VALUES
8664 machine (x64)
9 number of sections
5707E693 time date stamp Fri Apr 8 18:12:51 2016
0 file pointer to symbol table
0 number of symbols
F0 size of optional header
2022 characteristics
Executable
Application can handle large (>2GB) addresses
DLL
Once that's fixed you can move on to try running your program.
There are still some things that can fail at run time. I found the first few times I tried that the diesel cli would exit with no output at all. gdb showed it was crashing before main even started. The exit code was the key: 0xc000007b is the code for an invalid exe or dll. In my case I had a 32bit sqlite.dll earlier in my path than the 64bit version. The other error code I saw was 0xc0000135 which means a dll was not found. That was when I forgot to put the postgres lib dir in the path. Both problems can be spotted with gdb on programs like diesel_cli which have been built with mingw. Alternatively you can try http://dependencywalker.com/ which will show you which dlls are missing or have the wrong types. Running dumpbin can also show you which libraries are linked:
dumpbin /dependents %USERPROFILE%\.cargo\bin\diesel.exe
This should print a list like this:
File Type: EXECUTABLE IMAGE
Image has the following dependencies:
libpq.dll
sqlite3.dll
ADVAPI32.dll
KERNEL32.dll
msvcrt.dll
SHELL32.dll
USERENV.dll
WS2_32.dll
Only the first two are specific to our program. The rest are provided by windows. You'll have to check by hand that the our database libraries are on the path at runtime.
Once that's all done and sorted then you can get on with choosing whether to use vim, emacs, sublime, atom, vscode or some other editor.
Good luck.