Run `rustc` to check a program without generating any files - rust

As cargo check shows, it's often useful to check if your program is well-formed without actually generating code (an often pretty time-consuming task). I want to check a single (library) Rust file with rustc directly (I cannot use Cargo!).
cargo check apparently works by calling this:
rustc --emit=metadata -Z no-codegen
This only emits metadata, a .rmeta file. Cargo actually needs that to check crates dependent on the checked crate. In my case I really don't need the metadata file.
I tried the following:
rustc --crate-type=lib --emit=
rustc --crate-type=lib --emit=nothing
But both didn't work. I use --crate-type=lib because my file doesn't have a main function. I need a platform-independent solution (I don't just want to use it on my machine, but use it in a public script).
How do I make rustc not write a single file?

You can just skip the --emit flag.
The final command would then be: rustc -Z no-codegen rust.rs

To quote my own GitHub comment about this very question, there are a few options for stable Rust:
rustc --emit=mir -o /dev/null seems to work in 1.18 and newer, writing nothing. (--emit=mir is the only helpful --emit option—the others try to create silly files like /dev/null.foo0.rcgu.o, except --emit=dep-info, which does no checking.)
rustc -C extra-filename=-tmp -C linker=true (i.e. use /bin/true as a “linker”) seems to work in all versions, writing some intermediate files but cleaning them up.
rustc --out-dir=<new empty temporary directory> is less clever and therefore perhaps less likely to break?
Note that linker errors, if any, will not be found by the first two options (nor by the nightly-only -Zno-codegen option).

Related

rust libraries with cargo (rlib)

I am trying to create a library in rust to be used with rust executables. In C you can just create your .a or .so (or .lib or .dll on windows) and use tools like CMake to link everything, however rust does not seem to have this kind of infrastructure?
It is possible to make an executable with cargo (cargo new ) and create a library by adding the --lib flag (cargo new --lib), but then how would you use the resulting .rlib file (from the library cargo project)? I managed to link the .rlib file as follows:
rustc main.rs --extern foo=libfoo.rlib
and that works beautifully, though, I am not interested in writing a thousand rustc commands to build the final executable (which depends on the .rlib) if there is cargo that can do that for you. I tried working with a build script (which works perfectly for any C library, static or dynamic), but if I try it with the .rlib file, cargo says that it cannot find "foo" (-lfoo), the build script:
fn main() {
println!("cargo:rustc-link-search=.");
println!("cargo:rustc-link-lib=foo");
}
I tried replacing the path (search) to different directories (whilst also moving the .rlib file to the correct directory), also tried different combinations of libfoo, libfoo.rlib, ... (note that for the C libaries, foo is sufficient).
So my question really is: How can you create a rust library for private use, and how do you use it with a rust executable in a proper way, avoiding manual rustc commands? Are there tools that do this? Am I missing something in the build script? Perhaps there exists something like CMake for rust?
I suppose it is possible to just create a C interface over the rust code and compile another C project as that does work with cargo.
I do NOT want to publish the code to crates.io as I want this library strictly for private use.
Cargo does not support using pre-compiled .rlibs. Cargo is designed to compile programs fully from source (not counting native libraries).
How can you create a rust library for private use … I do NOT want to publish the code to crates.io as I want this library strictly for private use.
To use a private library, you write a dependency using a path or git dependency (or use a private package registry, but that's more work to set up).
[dependencies]
my-lib-a = { path = "../my-lib-a/" }
my-lib-b = { git = "https://my-git-host.example/my-lib-b", branch = "stable" }
Your private library is now compiled exactly like a “public” one.

Rust: build for atmega16

I would like to write a simple program and compile it for atmega16. How do I add atmega16 as a compilation target? There are little to no docs about this...
Thanks
The only built-in AVR target is avr-unknown-gnu-atmega328. If you are using a different AVR microcontroller, you need a custom target.
The AVR-Rust Guidebook describes how to add custom targets for different AVR microcontrollers. The process boils down to:
Export the JSON specification for the built-in atmega328 target, to use as a template
rustc --print target-spec-json -Z unstable-options --target avr-unknown-gnu-atmega328 > avr-unknown-gnu-atmega16.json
Edit this JSON file. I found that to get it to compile for atmega16, I had to change the following:
"cpu": "atmega16",
"is-builtin": false,
"-mmcu=atmega16"
Note:
The CPU is specified as "atmega16" not "atmega16p" as you had attempted to use
I don't have any atmega16 MCU to hand so I have not been able to test the resulting binary. You may need to tweak other elements of the file.
You will also need to install avr-gcc, as Rust uses it to link the resulting binary. You can get that here.
You should then be able to build your project like this:
cargo build -Z build-std=core --target ./avr-unknown-gnu-atmega16.json --release
Note that --target is specified as the path to the JSON file. If in the same directory, prefix with ./.

How do I remove the dependency on libunwind when cross-compiling Rust programs with the panic_abort option?

I'm specifying the -Cpanic=abort and -Zbuild-std=panic_abort when compiling. Why does the linker still say it needs libunwind to compile a program?
I'm experimenting with various ways to cross-compile Rust programs as small as possible (using the min-sized-rust repo as a reference). Right now I'm trying to compile the powerpc64-unknown-linux-musl target and I'm stuck on trying to remove a dependency on libunwind.
Here's my setup:
# 1. Install the Rust std source code
rustup component add rust-src --toolchain nightly
# 2. Setup a simple rust repo
cargo init testing
cd testing
# 3. Download a musl toolchain
wget https://musl.cc/powerpc64-linux-musl-cross.tgz
tar xzf powerpc64-linux-musl-cross.tgz
# 4. Try to compile the project (options on the command line instead of in files for
# maximum obviousness).
# RUSTFLAGS:
# -Cpanic=abort - abort immediately on panic
# -Clink-self-contained=no - don't use rustc's builtin libraries and objects (this
# is needed because powerpc64-unknown-linux-musl is a tier 3 target)
# -Clink-arg=--sysroot and -Clink-arg=/path/to/sysroot - pass the option to the linker
# to specify the sysroot of cross-compilation toolchain
# Cargo options:
# --config target.<triple>.linker - specify the linker to use
# -Zbuild-std=std,panic_abort - build the standard library from source. Specify
# panic_abort to make the abort on panic work
RUSTFLAGS="-Cpanic=abort -Clink-self-contained=no -Clink-arg=--sysroot -Clink-arg=powerpc64-linux-musl-cross/powerpc64-linux-musl/" \
cargo +nightly build \
--config "target.powerpc64-unknown-linux-musl.linker=\"powerpc64-linux-musl-cross/bin/powerpc64-linux-musl-gcc\"" \
--target powerpc64-unknown-linux-musl -Zbuild-std=panic_abort,std --release
This fails with the following error:
error: linking with `/home/user/Projects/testing/powerpc64-linux-musl-cross/bin/powerpc64-linux-musl-gcc` failed: exit status: 1
<output snipped>
= note: /home/user/Projects/testing/powerpc64-linux-musl-cross/bin/../lib/gcc/powerpc64-linux-musl/11.2.1/../../../../powerpc64-linux-musl/bin/ld: cannot find -lunwind
From min-size-rust repository:
"Even if panic = "abort" is specified in Cargo.toml, rustc will still include panic strings and formatting code in final binary by default. An unstable panic_immediate_abort feature has been merged into the nightly rustc compiler to address this.
To use this, repeat the instructions above to use build-std, but also pass the following -Z build-std-features=panic_immediate_abort option."
Still, you will get "cannot find -lunwind", because the linker still uses libunwind, even though it's truly unneeded,why! I do not know, maybe it's a bug.(Maybe someone with fair knowledge about linkers can easily solve that.I tried a naive solution which is "cargo .... --verbose", copy , remove "libunwind" then relinking which failed)
I verified that is indeed the missing piece by build from source(--target=x86_64-unknown-linux-musl) AND using an old simple trick which is "touch libunwind.a" in the "self-contained" directory inside a target lib folder.(because the linker would still use it even though it's now truly unneeded, then I gave him a dummy libunwind.a)
In your case, I really tried to build it to your target until I got a headache, but couldn't and stopped, but here is possible solutions:
Giving that you're using "-Z build-std-features=panic_immediate_abort"
-If you can custom the linking process, then solve it (until what seems to be a bug is solved)
-Create a dummy(empty) libunwind.a where it should be in your toolchain

How to make `include!` work with macros defined in external crates?

When using serde in stable as recommended by the respective blog post, one will have to use the built-in include! macro to pull in a file generated by serde-codegen.
The file linked here shows this in a more complex example which can use rustc nightly as well as rustc stable.
However, as the docs of include! suggest, it does not behave hygienically.
What this means was unclear to me until I ran into the issue that macros defined in an external crate, yup-hyper-mock, were not defined at include-time. Some tests showed that even something like extern crate foo-bar-snoo-snoo; will not trigger an error at include-time, showing that it was not yet evaluated at all.
The problem arises from include! trying to expand macros, and failing if these are coming from external crates which haven't been evaluated yet.
An attempt of mine to define empty macros with the correct signature cause the include! macro to work, but the compile would fail later as include! would actually expand the macro right away, which was empty at the time of the include.
Is there a way to make include! work with macros defined in external crates? Alternatively, can you imagine a workaround to make my particular case work?
Personally I think include! should not expand macros at all, but leave that to the next compile step which should bring in external crates - maybe we are looking at a bug in rustc, but I am not sure about that.
How to reproduce
Please note that both stable and nightly compilers show the same issues.
git clone --branch syntex https://github.com/Byron/yup-oauth2
cd yup-oauth2
git reset --hard f59d97d
# build and test fail because 'include!' runs before crates are pulled
# in, but still expands macros
cargo build
cargo test
Meta
stable
➜ yup-oauth2 git:(syntex) rustc --version --verbose
rustc 1.0.0 (a59de37e9 2015-05-13) (built 2015-05-14)
binary: rustc
commit-hash: a59de37e99060162a2674e3ff45409ac73595c0e
commit-date: 2015-05-13
build-date: 2015-05-14
host: x86_64-apple-darwin
release: 1.0.0
nightly
➜ yup-oauth2 git:(syntex) rustc --version --verbose
rustc 1.2.0-nightly (2228ce10c 2015-06-09)
binary: rustc
commit-hash: 2228ce10c6d83c17b6346396aa7c7ef9082f1c04
commit-date: 2015-06-09
host: x86_64-apple-darwin
release: 1.2.0-nightly

Should I use an environment to invoke SConscript()?

What difference in effect is there, if any, between these?
SConscript('subdir/SConscript')
env.SConscript('subdir/SConscript')
I initially wondered if this affected the called SConscript's DefaultEnvironment, so I tried this experiment:
SConstruct:
env = Environment(CC='my_cc')
SConscript('SConscript', exports={'program':'p1.c'})
env.SConscript('SConscript', exports={'program':'p2.c'})
SConscript:
Import('program')
Program(program)
Result:
$ scons -n -Q
gcc -o p1.o -c p1.c
gcc -o p1 p1.o
gcc -o p2.o -c p2.c
gcc -o p2 p2.o
But, as you can see, the construction environment used in SConscript seems unaffected.
If you simply write:
Program('main','main.cpp')
SCons will use what is called the "DefaultEnvironment" internally. It was added for people, like starting developers in Bioinformatics, when they don't need to work with special environment settings...or don't have several different environments in their builds.
It makes typing a little less verbose, but you're stuck with one Environment. However, one can always freely mix the DefaultEnvironment with self-defined ones...it's just a matter of taste.
But what's important to note is, that the DefaultEnvironment and additional Environments are not automatically related or connected, regarding their settings.
Finally, the DefaultEnvironment can also be accessed directly by using:
import SCons.DefaultEnvironment
SCons.DefaultEnvironment(tools=[])
env = Environment(...)
which here has the effect of stopping SCons from searching for tools/apps in the system twice...once for the DefaultEnv, and once for your special environment.
Internally, a command like "Program()" is simply a wrapper for "SCons.DefaultEnvironment.Program()"...that's all there is to it.
Based on all of the above, the SConscript() method is available in any Environment...and it should always be safe to include other build spec files with a simple
SConscript('...')
, which is also the method I prefer personally.

Resources