Cactus
Cactus

Reputation: 27636

Link to external object using the right (target-specific) compiler

I have a Rust crate that is targeting AVR. If I do a cargo build, it fails at linking, which is expected because I will need to add an extra object (compiled from C) to it. However, the error message shows that Cargo has correctly picked up from the target definition that it should use avr-gcc for linking:

"avr-gcc" "-mmcu=atmega328p" ....
  = note: chirp8-avr/target/avr-unknown-gnu-atmega328/release/deps/chirp8_avr-e38dd93db57fe6d7.chirp8_avr.dlqaoark-cgu.0.rcgu.o: In function `main':
          chirp8_avr.dlqaoark-cgu.0:(.text.main+0x18a): undefined reference to `font_rom_size'
          chirp8_avr.dlqaoark-cgu.0:(.text.main+0x19e): undefined reference to `read_font_rom'
          chirp8_avr.dlqaoark-cgu.0:(.text.main+0x1b4): undefined reference to `prog_rom_size'
          chirp8_avr.dlqaoark-cgu.0:(.text.main+0x1c6): undefined reference to `read_prog_rom'
          collect2: error: ld returned 1 exit status

So the next logical step is to add my external C source to the crate. From what I understand, cc is exactly the crate for that. So let's try that: I add cc to my [build-dependencies], and write the following build.rs:

fn main() {
    cc::Build::new()
        // .flag("-mmcu=atmega328p").pic(false)
        .file("src/rom.c")
        .compile("rom");
}

If I now try building again, this fails because Cargo suddenly seems to have forgotten to use avr-gcc, and is instead trying to use the system's default C compiler:

  running: "cc" "-O3" "-ffunction-sections" "-fdata-sections" "-fPIC" "-Wall" "-Wextra" "-o" "chirp8-avr/target/avr-unknown-gnu-atmega328/release/build/chirp8-avr-fd0f12cc4390c66b/out/src/rom.o" "-c" "src/rom.c"
  cargo:warning=src/rom.c:1:10: fatal error: avr/pgmspace.h: No such file or directory
  cargo:warning=    1 | #include <avr/pgmspace.h>
  cargo:warning=      |          ^~~~~~~~~~~~~~~~
  cargo:warning=compilation terminated.
  exit code: 1

What am I doing wrong? Why is the target platform not picked up correctly by Cargo?

Upvotes: 0

Views: 261

Answers (1)

eggyal
eggyal

Reputation: 126025

As documented in the cc crate's Readme:

This crate calls out to the most relevant compiler for a platform, for example using cl on MSVC.

Inspecting the source reveals that the cc crate does this with some hard-coded logic for certain statically known host/target combinations. Unfortunately, avr is not a target for which it knows to use avr-gcc.

The Readme also states:

External configuration via environment variables

To control the programs and flags used for building, the builder can set a number of different environment variables.

[ deletia ]

  • CC - the actual C compiler used. Note that this is used as an exact executable name, so (for example) no extra flags can be passed inside this variable, and the builder must ensure that there aren't any trailing spaces. This compiler must understand the -c flag. For certain TARGETs, it also is assumed to know about other flags (most common is -fPIC).

[ deletia ]

Each of these variables can also be supplied with certain prefixes and suffixes, in the following prioritized order:

  1. <var>_<target> - for example, CC_x86_64-unknown-linux-gnu
  2. <var>_<target_with_underscores> - for example, CC_x86_64_unknown_linux_gnu
  3. <build-kind>_<var> - for example, HOST_CC or TARGET_CFLAGS
  4. <var> - a plain CC, AR as above.

If none of these variables exist, cc-rs uses built-in defaults

Therefore, I suggest adding to your build.rs:

std::env::set_var("CC_avr-unknown-gnu-atmega328", "avr-gcc");

Upvotes: 1

Related Questions