Skip to content

Commit c05442a

Browse files
committed
Expand the "link to system libraries" section
1 parent 85a96e4 commit c05442a

File tree

2 files changed

+86
-2
lines changed

2 files changed

+86
-2
lines changed

src/cargo/ops/cargo_rustc/links.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use std::collections::HashMap;
33
use core::PackageSet;
44
use util::{CargoResult, human};
55

6-
// Returns a mapping of the root package plus its immediate dependencies to
7-
// where the compiled libraries are all located.
6+
// Validate that there are no duplicated native libraries among packages and
7+
// that all packages with `links` also have a build script.
88
pub fn validate(deps: &PackageSet) -> CargoResult<()> {
99
let mut map = HashMap::new();
1010

src/doc/build-script.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,50 @@ performing this in a platform-agnostic fashion, and the purpose of a build
407407
script is again to farm out as much of this as possible to make this as easy as
408408
possible for consumers.
409409

410+
As an example to follow, let's take a look at one of [Cargo's own
411+
dependencies][git2-rs], [libgit2][libgit2]. This library has a number of
412+
constraints:
413+
414+
[git2-rs]: https://github.com/alexcrichton/git2-rs/tree/master/libgit2-sys
415+
[libgit2]: https://github.com/libgit2/libgit2
416+
417+
* It has an optional dependency on OpenSSL on Unix to implement the https
418+
transport.
419+
* It has an optional dependency on libssh2 on all platforms to implement the ssh
420+
transport.
421+
* It is often not installed on all systems by default.
422+
* It can be built from source using `cmake`.
423+
424+
To visualize what's going on here, let's take a look at the manifest for the
425+
relevant Cargo package.
426+
427+
```toml
428+
[package]
429+
name = "libgit2-sys"
430+
version = "0.0.1"
431+
authors = ["..."]
432+
links = "git2"
433+
build = "build.rs"
434+
435+
[dependencies.libssh2-sys]
436+
git = "https://github.com/alexcrichton/ssh2-rs"
437+
438+
[target.x86_64-unknown-linux-gnu.dependencies.openssl-sys]
439+
git = "https://github.com/alexcrichton/openssl-sys"
440+
441+
# ...
442+
```
443+
444+
As the above manifests show, we've got a `build` script specified, but it's
445+
worth noting that this example has a `links` entry which indicates that the
446+
crate (`libgit2-sys`) links to the `git2` native library.
447+
448+
Here we also see the unconditional dependency on `libssh2` via the
449+
`libssh2-sys` crate, as well as a platform-specific dependency on `openssl-sys`
450+
for unix (other variants elided for now). It may seem a little counterintuitive
451+
to express *C dependencies* in the *Cargo manifest*, but this is actually using
452+
one of Cargo's conventions in this space.
453+
410454
## `*-sys` Packages
411455

412456
To alleviate linking to system libraries, Cargo has a *convention* of package
@@ -428,3 +472,43 @@ convention of native-library-related packages:
428472
* A common dependency allows centralizing logic on discovering `libfoo` itself
429473
(or building it from source).
430474
* These dependencies are easily overridable.
475+
476+
## Building libgit2
477+
478+
Now that we've got libgit2's dependencies sorted out, we need to actually write
479+
the build script. We're not going to look at specific snippets of code here and
480+
instead only take a look at the high-level details of the build script of
481+
`libgit2-sys`. This is not recommending all packages follow this strategy, but
482+
rather just outlining one specific strategy.
483+
484+
The first step of the build script should do is to query whether libgit2 is
485+
already installed on the host system. To do this we'll leverage the preexisting
486+
tool `pkg-config` (when its available). We'll also use a `build-dependencies`
487+
section to refactor out all the `pkg-config` related code (or someone's already
488+
done that!).
489+
490+
If `pkg-config` failed to find libgit2, or if `pkg-config` just wasn't
491+
installed, the next step is to build libgit2 from bundled source code
492+
(distributed as part of `libgit2-sys` itself). There are a few nuances when
493+
doing so that we need to take into account, however:
494+
495+
* The build system of libgit2, `cmake`, needs to be able to find libgit2's
496+
optional dependency of libssh2. We're sure we've already built it (it's a
497+
Cargo dependency), we just need to communicate this information. To do this
498+
we leverage the metadata format to communicate information between build
499+
scripts. In this example the libssh2 package printed out `cargo:root=...` to
500+
tell us where libssh2 is installed at, and we can then pass this along to
501+
cmake with the `CMAKE_PREFIX_PATH` environment variable.
502+
503+
* We'll need to handle some `CFLAGS` values when compiling C code (and tell
504+
`cmake` about this). Some flags we may want to pass are `-m64` for 64-bit
505+
code, `-m32` for 32-bit code, or `-fPIC` for 64-bit code as well.
506+
507+
* Finally, we'll invoke `cmake` to place all output into the `OUT_DIR`
508+
environment variable, and then we'll print the necessary metadata to instruct
509+
rustc how to link to libgit2.
510+
511+
Most of the functionality of this build script is easily refactorable into
512+
common dependencies, so our build script isn't quite as intimidating as this
513+
descriptions! In reality it's expected that build scripts are quite succinct by
514+
farming logic such as above to build dependencies.

0 commit comments

Comments
 (0)