How to load Rust compiler plugins without modifying the source code? - rust

Rust provides various ways to write plugins. To extend checks on Rust code, it allows developer to write Lint Plugins. A typical way to use the plugin is to add a line to the source code indicating the use of this plugin:
#![plugin(myplugin)]
You also need to edit the Cargo.toml file to include your plugin project in the dependencies section:
myplugin = {path = "/path/to/myproject"}
However, if you want to analyze big projects, these modifications seem to be troubling, I wonder if cargo build or rustc provides any way to load my plugins without modifying the source code.

rustc has a command-line parameter for loading additional plugins: -Z extra-plugins=<plugins>. However, this option also requires that the path to the compiled plugin library be passed to the compiler. This is done automatically if the plugin library is declared as a dependency in Cargo.toml. If it's not in Cargo.toml, then you can compile it independently and reference it manually with --extern my_plugin=/path/to/plugin.rlib, in addition to the -Z extra-plugins=<plugins> option.
There's another option. Clippy, a large collection of general lints for Rust, provides a program that can be invoked as cargo clippy. That program basically acts as a fake rustc, implementing a compiler frontend (using internal crates used by rustc) that loads Clippy directly into the compiler's plugin registry (for the main project only, not for the project's dependencies). You can see the code on GitHub (licensed under MPLv2). The advantage of this approach is that you don't have to give a path to the plugin, because the plugin is built in the frontend. This makes it very convenient to use for the plugin's users. The disadvantage is that such a program relies on unstable compiler internals. This means that your program can stop compiling at any time due to a breaking change in rustc's unstable API.

Related

What is current documentation tool for Rust? [duplicate]

How can I make cargo to save-analysis? I know that I can do this with rustc by calling
rustc -Zsave-snalysis <files...>
But, I couldn't figure out for cargo. And also I like to know how I can read them back to rls_analysis data structures.
I tried cargo rustc -Zsave-analysis, but it doesn't seem to work.
I also tried export RUSTC_SAVE_ANALYSIS=api, no work too.
What I want to do is getting fully qualified path (e.g. ::foo1::foo2::Foo3) to the types notated in source code. If there's other solution, please let me know that too.
Just do this before calling cargo build.
export RUSTFLAGS="-Z save-analysis"
Update
Saved analysis data won't be loaded with default configuration of AnalysisHost. It's because CargoAnalysisLoader tries to load data from non-default location.
To workaround, just move save-analysis directory to proper location.
target/debug/deps/save-analysis
target/rls/debug/deps/save-analysis
Replace debug to release according to your build mode.

How to skip a library crate's examples depending on the target platform?

I have a Rust library crate which I only use on Linux, but I don't know of any reason it shouldn't work on Windows; however one of the examples is Unix-specific, so it the crate fails the build on AppVeyor.
The reason is that there's a Unix-only dependency (Termion) used by one of the examples, so when I used an AppVeyor template, it fails to build that dev-dependency.
So I made the dependency conditional:
[target.'cfg(unix)'.dev-dependencies]
termion = "1.0"
So far so good, but of course now that example fails on extern crate termion.
What I need is to just not build that example on non-Unix-like targets. I was hoping something like:
[[target.'cfg(unix)'.example]]
name = "foo"
would work, but I get:
warning: unused manifest key: target.cfg(unix).example
error: no example target named `foo`
Another promising way forward was Cargo's required-features, but as of writing it's apparently not in stable Cargo, which means that doesn't help me checking that it works on stable Rust on Windows.
The last option I can think of is to #[cfg(unix)] away most of the example's source, turning it into a stub on Windows, but I'd really like a cleaner way of doing it.
Is there some existing way of skipping an example on some unsupported targets that works with current stable Rust/Cargo?

Setting the include path with bindgen

I'm writing a Rust interface to a small C library, which has headers spread in a few locations. It's not a system library, and is normally used by some executables in the same package; I'm currently including it as a git submodule in my Cargo project.
Building the library seems to be pretty easy; I've opted to use the gcc crate from build.rs:
gcc::Config::new()
.file("external/foo/dir1/file1.c")
.file("external/foo/dir2/file2.c")
.include("external/foo/dir1/")
.include("external/foo/dir2/")
.include("external/foo/config_a/")
.compile("libfoo.a");
Now I was hoping to use the bindgen crate to generate the FFI interface without too much fuss, but it doesn't seem to have a way of setting include paths.
I can create a wrapper.h as suggested by this blog and include several headers, but if dir1/dir1.h includes conf.h directly, which works when building due to .include("external/foo/config_a/") it can't be found.
I can't find anything in bindgen's API to help here (essentially I want to pass the equivalent of gcc/clang's -I option). Am I missing anything?
The best option I can think of so far is to copy the various headers from the library source into some temporary directory in build.rs and run bindgen on that, but that seems somewhat messy if there's a nicer way.
With the API you can use Builder::clang_arg with arbitrary arguments:
let b = bindgen::builder().header("foo.h").clang_arg("-I/path");
From the command line you can do the same by appending arguments after --, like:
bindgen foo.h -- -I/path

How do I properly link to a Rust dynamic library from C?

I have a Cargo project named foo that produces a libfoo-<random-hash>.dylib. How do I link against it? clang only finds it if I rename it to libfoo.dylib but libfoo-<random-hash>.dylib is needed for running the program. My workaround is to manually copy and rename the lib but this can’t be the proper procedure.
My procedure to compile and run the C program is:
$ cargo build
$ cp target/debug/libfoo-*.dylib target/debug/libfoo.dylib
$ clang -lfoo -L target/debug -o bar bar.c
$ LD_LIBRARY_PATH=target/debug ./bar
The hash serves a purpose: It gives a link error in case of a version mismatch (especially important with cross-crate inlining). It also allows one binaries to link to two versions of a crate (e.g., because your application uses two libraries that internally use different versions). But in the C world, nobody cares about that. If you don't care either, or care more about making it easy to link to your library, just rename the shared object to get rid of the hash.
Note that the hash is added by Cargo. If you build by directly invoking rustc, no hash is added — but nobody wants that. AFAIK Cargo doesn't offer an option to omit the hash, but you might want to try asking the developers or filing an issue.
Alternatively, you could extract the full library name (including hash) from the file name, and instruct clang to link to that. Then you get the above advantages, but it makes the build process of everyone who wants to link to your library more complicated.
Since Rust 1.11, Cargo supports the
cdylib
artifact type for this exact purpose:
[lib]
name = "foo"
crate-type = ["rlib", "cdylib"]
This definition yields a ./target/{release,debug}/libfoo.so with
all the Rust dependencies statically linked in so it can be linked into C
projects right away.

Building Boost on Linux - library names

I am trying to build an application which depends on Boost. So I downloaded Boost 1_41_0 to my Linux box and followed the instructions found on the Boost site for Unix variants,
http://www.boost.org/doc/libs/1_41_0/more/getting_started/unix-variants.html.
They basically suggest that I run ./bjam install, which I did. The build completed successfully. However, the library names don't seem to match the Boost naming convention described both in the documentation above, and what is specified in the makefile of the application I am trying to build.
I noticed that there are a bunch of options that I can specify to bjam and I tried to play with those, but no matter what happens I can't seem to get it quite right. My understanding is that the libraries should go into the $BOOST_ROOT/lib directory. This is where the libraries show up, but named:
libboost_thread.a
libboost_thread.so
libboost_thread.so.1.41.0
I'd expect them to be named libboost_thread-gcc41-mt-d-1_41_0 or something similar.
I did try ./bjam --build-type=complete --layout=tagged and I see:
libboost_thread.a
libboost_thread-mt.a
libboost_thread-mt-d.a
libboost_thread-mt-d.so
libboost_thread-mt-d.so.1.41.0
libboost_thread-mt-s.a
libboost_thread-mt-sd.a
libboost_thread-mt.so
libboost_thread-mt.so.1.41.0
libboost_thread.so
libboost_thread.so.1.41.0
So, I am not sure if I should just make stage my -L directory? Is there any documentation which describe this in more detail?
The names was changed in 1.40.0 - see in release notes:
Build System
The default naming of libraries in
Unix-like environment now matches
system conventions, and does not
include various decorations.
They probably forgot to update this part in the build documentation.
There are two variables here. First is "install" vs. "stage" (default). "install" copies both libraries and headers into a directory -- /usr/local by default, and you can then remove source tree. "stage" puts libraries to "stage/lib", and you should add "-L /stage/lib -I " flags.
Second is --layout=versioned and --layout=system. It seems like you have discovered what they do already, and indeed, system is default since 1.40. The getting started guide fails to mention this, and I've added an action item to update it. Ideally, you should talk to the authors of the application to use the system naming of boost libraries. If that's not possible, then building with --layout=versioned is the only option.
From the Boost documentation at http://www.boost.org/doc/libs/1_35_0/more/getting_started/windows.html#library-naming, the convention is:
-mt Threading tag: indicates that the library was built with multithreading support enabled. Libraries built without multithreading support can be identified by the absence of -mt.
-d ABI tag: encodes details that affect the library's interoperability with other compiled code. For each such feature, a single letter is added to the tag:
Key Use this library when:
s linking statically to the C++ standard library and compiler runtime support libraries.
g using debug versions of the standard and runtime support libraries.
y using a special debug build of Python.
d building a debug version of your code.
p using the STLPort standard library rather than the default one supplied with your compiler.
n using STLPort's deprecated “native iostreams” feature.
For example, if you build a debug version of your code for use with debug versions of the static runtime library and the STLPort standard library in “native iostreams” mode, the tag would be: -sgdpn. If none of the above apply, the ABI tag is ommitted.

Resources