Bring the linkage documentation up-to-date

This includes documentation for all the previous changes done to linking
in #10582. Additionally, this brings the list of feature-gates up-to-date with
the currently recognized list of features.
This commit is contained in:
Alex Crichton 2013-11-30 13:26:46 -08:00
parent f9d6fd20a5
commit d4c40b519b
2 changed files with 223 additions and 19 deletions

View File

@ -2070,6 +2070,38 @@ The currently implemented features of the compiler are:
closure as `once` is unlikely to be supported going forward. So
they are hidden behind this feature until they are to be removed.
* `managed_boxes` - Usage of `@` pointers is gated due to many
planned changes to this feature. In the past, this has meant
"a GC pointer", but the current implementation uses
reference counting and will likely change drastically over
time. Additionally, the `@` syntax will no longer be used to
create GC boxes.
* `asm` - The `asm!` macro provides a means for inline assembly. This is often
useful, but the exact syntax for this feature along with its semantics
are likely to change, so this macro usage must be opted into.
* `non_ascii_idents` - The compiler supports the use of non-ascii identifiers,
but the implementation is a little rough around the
edges, so this can be seen as an experimental feature for
now until the specification of identifiers is fully
fleshed out.
* `thread_local` - The usage of the `#[thread_local]` attribute is experimental
and should be seen as unstable. This attribute is used to
declare a `static` as being unique per-thread leveraging
LLVM's implementation which works in concert with the kernel
loader and dynamic linker. This is not necessarily available
on all platforms, and usage of it is discouraged (rust
focuses more on task-local data instead of thread-local
data).
* `link_args` - This attribute is used to specify custom flags to the linker,
but usage is strongly discouraged. The compiler's usage of the
system linker is not guaranteed to continue in the future, and
if the system linker is not used then specifying custom flags
doesn't have much meaning.
If a feature is promoted to a language feature, then all existing programs will
start to receive compilation warnings about #[feature] directives which enabled
the new feature (because the directive is no longer necessary). However, if
@ -3611,6 +3643,111 @@ queues, as well as code to copy values between queues and their recipients and
to serialize values for transmission over operating-system inter-process
communication facilities.
### Linkage
The Rust compiler supports various methods to link crates together both
statically and dynamically. This section will explore the various methods to
link Rust crates together, and more information about native libraries can be
found in the [ffi tutorial][ffi].
In one session of compilation, the compiler can generate multiple artifacts
through the usage of command line flags and the `crate_type` attribute.
* `--bin`, `#[crate_type = "bin"]` - A runnable executable will be produced.
This requires that there is a `main` function in the crate which will be run
when the program begins executing. This will link in all Rust and native
dependencies, producing a distributable binary.
* `--lib`, `#[crate_type = "lib"]` - A Rust library will be produced. This is
an ambiguous concept as to what exactly is produced because a library can
manifest itself in several forms. The purpose of this generic `lib` option is
to generate the "compiler recommended" style of library. The output library
will always be usable by rustc, but the actual type of library may change
from time-to-time. The remaining output types are all different flavors of
libraries, and the `lib` type can be seen as an alias for one of them (but
the actual one is compiler-defined).
* `--dylib`, `#[crate_type = "dylib"]` - A dynamic Rust library will be
produced. This is different from the `lib` output type in that this forces
dynamic library generation. The resulting dynamic library can be used as a
dependency for other libraries and/or executables. This output type will
create `*.so` files on linux, `*.dylib` files on osx, and `*.dll` files on
windows.
* `--staticlib`, `#[crate_type = "staticlib"]` - A static system library will
be produced. This is different from other library outputs in that the Rust
compiler will never attempt to link to `staticlib` outputs. The purpose of
this output type is to create a static library containing all of the local
crate's code along with all upstream dependencies. The static library is
actually a `*.a` archive on linux and osx and a `*.lib` file on windows. This
format is recommended for use in situtations such as linking Rust code into an
existing non-Rust application because it will not have dynamic dependencies on
other Rust code.
* `--rlib`, `#[crate_type = "rlib"]` - A "Rust library" file will be produced.
This is used as an intermediate artifact and can be thought of as a "static
Rust library". These `rlib` files, unlike `staticlib` files, are interpreted
by the Rust compiler in future linkage. This essentially means that `rustc`
will look for metadata in `rlib` files like it looks for metadata in dynamic
libraries. This form of output is used to produce statically linked
executables as well as `staticlib` outputs.
Note that these outputs are stackable in the sense that if multiple are
specified, then the compiler will produce each form of output at once without
having to recompile.
With all these different kinds of outputs, if crate A depends on crate B, then
the compiler could find B in various different forms throughout the system. The
only forms looked for by the compiler, however, are the `rlib` format and the
dynamic library format. With these two options for a dependent library, the
compiler must at some point make a choice between these two formats. With this
in mind, the compiler follows these rules when determining what format of
dependencies will be used:
1. If a dynamic library is being produced, then it is required for all upstream
Rust dependencies to also be dynamic. This is a limitation of the current
implementation of the linkage model. The reason behind this limitation is to
prevent multiple copies of the same upstream library from showing up, and in
the future it is planned to support a mixture of dynamic and static linking.
When producing a dynamic library, the compiler will generate an error if an
upstream dependency could not be found, and also if an upstream dependency
could only be found in an `rlib` format. Remember that `staticlib` formats
are always ignored by `rustc` for crate-linking purposes.
2. If a static library is being produced, all upstream dependecies are
required to be available in `rlib` formats. This requirement stems from the
same reasons that a dynamic library must have all dynamic dependencies.
Note that it is impossible to link in native dynamic dependencies to a static
library, and in this case warnings will be printed about all unlinked native
dynamic dependencies.
3. If an `rlib` file is being produced, then there are no restrictions on what
format the upstream dependencies are available in. It is simply required that
all upstream dependencies be available for reading metadata from.
The reason for this is that `rlib` files do not contain any of their upstream
dependencies. It wouldn't be very efficient for all `rlib` files to contain a
copy of `libstd.rlib`!
4. If an executable is being produced, then things get a little interesting. As
with the above limitations in dynamic and static libraries, it is required
for all upstream dependencies to be in the same format. The next question is
whether to prefer a dynamic or a static format. The compiler currently favors
static linking over dynamic linking, but this can be inverted with the `-Z
prefer-dynamic` flag to the compiler.
What this means is that first the compiler will attempt to find all upstream
dependencies as `rlib` files, and if successful, it will create a statically
linked executable. If an upstream dependency is missing as an `rlib` file,
then the compiler will force all dependencies to be dynamic and will generate
errors if dynamic versions could not be found.
In general, `--bin` or `--lib` should be sufficient for all compilation needs,
and the other options are just available if more fine-grained control is desired
over the output format of a Rust crate.
### Logging system
The runtime contains a system for directing [logging
@ -3762,3 +3899,5 @@ Additional specific influences can be seen from the following languages:
* The typeclass system of Haskell.
* The lexical identifier rule of Python.
* The block syntax of Ruby.
[ffi]: tutorial-ffi.html

View File

@ -8,13 +8,13 @@ foreign code. Rust is currently unable to call directly into a C++ library, but
snappy includes a C interface (documented in
[`snappy-c.h`](https://code.google.com/p/snappy/source/browse/trunk/snappy-c.h)).
The following is a minimal example of calling a foreign function which will compile if snappy is
installed:
The following is a minimal example of calling a foreign function which will
compile if snappy is installed:
~~~~ {.xfail-test}
use std::libc::size_t;
#[link_args = "-lsnappy"]
#[link(name = "snappy")]
extern {
fn snappy_max_compressed_length(source_length: size_t) -> size_t;
}
@ -25,26 +25,28 @@ fn main() {
}
~~~~
The `extern` block is a list of function signatures in a foreign library, in this case with the
platform's C ABI. The `#[link_args]` attribute is used to instruct the linker to link against the
snappy library so the symbols are resolved.
The `extern` block is a list of function signatures in a foreign library, in
this case with the platform's C ABI. The `#[link(...)]` attribute is used to
instruct the linker to link against the snappy library so the symbols are
resolved.
Foreign functions are assumed to be unsafe so calls to them need to be wrapped with `unsafe {}` as a
promise to the compiler that everything contained within truly is safe. C libraries often expose
interfaces that aren't thread-safe, and almost any function that takes a pointer argument isn't
valid for all possible inputs since the pointer could be dangling, and raw pointers fall outside of
Foreign functions are assumed to be unsafe so calls to them need to be wrapped
with `unsafe {}` as a promise to the compiler that everything contained within
truly is safe. C libraries often expose interfaces that aren't thread-safe, and
almost any function that takes a pointer argument isn't valid for all possible
inputs since the pointer could be dangling, and raw pointers fall outside of
Rust's safe memory model.
When declaring the argument types to a foreign function, the Rust compiler will not check if the
declaration is correct, so specifying it correctly is part of keeping the binding correct at
runtime.
When declaring the argument types to a foreign function, the Rust compiler can
not check if the declaration is correct, so specifying it correctly is part of
keeping the binding correct at runtime.
The `extern` block can be extended to cover the entire snappy API:
~~~~ {.xfail-test}
use std::libc::{c_int, size_t};
#[link_args = "-lsnappy"]
#[link(name = "snappy")]
extern {
fn snappy_compress(input: *u8,
input_length: size_t,
@ -232,9 +234,72 @@ fn main() {
# Linking
In addition to the `#[link_args]` attribute for explicitly passing arguments to the linker, an
`extern mod` block will pass `-lmodname` to the linker by default unless it has a `#[nolink]`
attribute applied.
The `link` attribute on `extern` blocks provides the basic building block for
instructing rustc how it will link to native libraries. There are two accepted
forms of the link attribute today:
* `#[link(name = "foo")]`
* `#[link(name = "foo", kind = "bar")]`
In both of these cases, `foo` is the name of the native library that we're
linking to, and in the second case `bar` is the type of native library that the
compiler is linking to. There are currently three known types of native
libraries:
* Dynamic - `#[link(name = "readline")]
* Static - `#[link(name = "my_build_dependency", kind = "static")]
* Frameworks - `#[link(name = "CoreFoundation", kind = "framework")]
Note that frameworks are only available on OSX targets.
The different `kind` values are meant to differentiate how the native library
participates in linkage. From a linkage perspective, the rust compiler creates
two flavors of artifacts: partial (rlib/staticlib) and final (dylib/binary).
Native dynamic libraries and frameworks are propagated to the final artifact
boundary, while static libraries are not propagated at all.
A few examples of how this model can be used are:
* A native build dependency. Sometimes some C/C++ glue is needed when writing
some rust code, but distribution of the C/C++ code in a library format is just
a burden. In this case, the code will be archived into `libfoo.a` and then the
rust crate would declare a dependency via `#[link(name = "foo", kind =
"static")]`.
Regardless of the flavor of output for the crate, the native static library
will be included in the output, meaning that distribution of the native static
library is not necessary.
* A normal dynamic dependency. Common system libraries (like `readline`) are
available on a large number of systems, and often a static copy of these
libraries cannot be found. When this dependency is included in a rust crate,
partial targets (like rlibs) will not link to the library, but when the rlib
is included in a final target (like a binary), the native library will be
linked in.
On OSX, frameworks behave with the same semantics as a dynamic library.
## The `link_args` attribute
There is one other way to tell rustc how to customize linking, and that is via
the `link_args` attribute. This attribute is applied to `extern` blocks and
specifies raw flags which need to get passed to the linker when producing an
artifact. An example usage would be:
~~~ {.xfail-test}
#[link_args = "-foo -bar -baz"]
extern {}
~~~
Note that this feature is currently hidden behind the `feature(link_args)` gate
because this is not a sanctioned way of performing linking. Right now rustc
shells out to the system linker, so it makes sense to provide extra command line
arguments, but this will not always be the case. In the future rustc may use
LLVM directly to link native libraries in which case `link_args` will have no
meaning.
It is highly recommended to *not* use this attribute, and rather use the more
formal `#[link(...)]` attribute on `extern` blocks instead.
# Unsafe blocks
@ -260,7 +325,7 @@ blocks with the `static` keyword:
~~~{.xfail-test}
use std::libc;
#[link_args = "-lreadline"]
#[link(name = "readline")]
extern {
static rl_readline_version: libc::c_int;
}
@ -279,7 +344,7 @@ them.
use std::libc;
use std::ptr;
#[link_args = "-lreadline"]
#[link(name = "readline")]
extern {
static mut rl_prompt: *libc::c_char;
}