目标 *.elf 的 AtmelStudio 配方失败

AtmelStudio recipe for target *.elf failed

我已将代码分成文件(*.c 和 *.h)并包含它们。我有 guard headers 并且所有分离的文件都被报告为正在构建:

Finished building: .././test.c

但是我的项目构建导致了这个错误:

recipe for target 'Midi.elf' failed

,因为

undefined reference to 'some_test()'

(所有函数调用)定义在这些单独的文件中。 而且我不知道如何调试它。我插入了编译器警告,可以证明要处理的函数定义和声明。也许 avr-g++.exe 和 avr-gcc.exe 构建输出无法链接?

Sketch.cpp:

#include "test.h"
void setup() {
   some_test();
}

void loop() {
}

test.h:

#ifndef TEST_H
#define TEST_H

void some_test(void);
#warning "processing test.h"

#endif //TEST_H

test.c:

#include "test.h"

void some_test(void){
    #warning "processing test.c"
}

输出(项目相关部分):

...
Done building project "ArduinoCore.cppproj".

Build succeeded.
------ Build started: Project: Midi, Configuration: Debug AVR ------
Build started.
Project "Midi.cppproj" (default targets):
Target "PreBuildEvent" skipped, due to false condition; ('$(PreBuildEvent)'!='') was evaluated as (''!='').
Target "CoreBuild" in file "C:\Program Files\Atmel\Studio.0\Vs\Compiler.targets" from project "D:\Mirror\Anwendungen\AVRStudio\Arduino\MIDIout\MIDIout\MIDIout\Midi\Midi.cppproj" (target "Build" depends on it):
    Task "RunCompilerTask"
        Shell Utils Path C:\Program Files\Atmel\Studio.0\shellUtils
        C:\Program Files\Atmel\Studio.0\shellUtils\make.exe all --jobs 4 --output-sync 
        Building file: .././Sketch.cpp
        Invoking: AVR8/GNU C Compiler : 4.9.2
        "C:\Program Files\Atmel\Studio.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-g++.exe" -funsigned-char -funsigned-bitfields -DDEBUG -DF_CPU=16000000L -DARDUINO=108013 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR -DUSB_VID=0x2341 -DUSB_PID=0x0010 -DUSB_MANUFACTURER="\"Arduino LLC\""  -I"C:\Program Files\Atmel\Studio.0\Packs\atmel\ATmega_DFP.1.130\include" -I"..\..\ArduinoCore\include\core" -I"..\..\ArduinoCore\include\variants\mega"  -Os -fno-threadsafe-statics -ffunction-sections -fdata-sections -fpack-struct -fshort-enums -mrelax -g2 -Wall -w -mmcu=atmega2560 -B "C:\Program Files\Atmel\Studio.0\Packs\atmel\ATmega_DFP.1.130\gcc\dev\atmega2560" -c -std=gnu++11 -MD -MP -MF "Sketch.d" -MT"Sketch.d" -MT"Sketch.o"   -o "Sketch.o" ".././Sketch.cpp" 
        Finished building: .././Sketch.cpp
        Building file: .././test.c
        Invoking: AVR8/GNU C Compiler : 4.9.2
        "C:\Program Files\Atmel\Studio.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-gcc.exe"  -x c -funsigned-char -funsigned-bitfields -DDEBUG -DF_CPU=16000000L -DARDUINO=108013 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR -DUSB_VID=0x2341 -DUSB_PID=0x0010 -DUSB_MANUFACTURER="\"Arduino LLC\""  -I"C:\Program Files\Atmel\Studio.0\Packs\atmel\ATmega_DFP.1.130\include" -I"..\..\ArduinoCore\include\core" -I"..\..\ArduinoCore\include\variants\mega"  -Os -fno-threadsafe-statics -ffunction-sections -fdata-sections -fpack-struct -fshort-enums -mrelax -g2 -Wall -mmcu=atmega2560 -B "C:\Program Files\Atmel\Studio.0\Packs\atmel\ATmega_DFP.1.130\gcc\dev\atmega2560" -c -std=gnu99 -std=gnu11 -MD -MP -MF "test.d" -MT"test.d" -MT"test.o"   -o "test.o" ".././test.c" 
        Finished building: .././test.c
cc1.exe(0,0): warning: command line option '-fno-threadsafe-statics' is valid for C++/ObjC++ but not for C
        In file included from .././test.c:1:0:
D:\Mirror\Anwendungen\AVRStudio\Arduino\MIDIout\MIDIout\MIDIout\Midi\test.h(5,2): warning: #warning "processing test.h" [-Wcpp]
         #warning "processing test.h"
          ^
        .././test.c: In function 'some_test':
D:\Mirror\Anwendungen\AVRStudio\Arduino\MIDIout\MIDIout\MIDIout\Midi\test.c(4,3): warning: #warning "processing test.c" [-Wcpp]
          #warning "processing test.c"
           ^
        Building target: Midi.elf
        Invoking: AVR8/GNU Linker : 4.9.2
        "C:\Program Files\Atmel\Studio.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-g++.exe" -o Midi.elf  Sketch.o test.o   -Wl,-Map="Midi.map" -Wl,--start-group -Wl,-lm -Wl,-lArduinoCore  -Wl,--end-group -Wl,-L"D:\Mirror\Anwendungen\AVRStudio\Arduino\MIDIout\MIDIout\MIDIout\ArduinoCore\Debug"  -Wl,--gc-sections -mrelax -mmcu=atmega2560 -B "C:\Program Files\Atmel\Studio.0\Packs\atmel\ATmega_DFP.1.130\gcc\dev\atmega2560" -Os  
        Sketch.o: In function `setup':
D:\Mirror\Anwendungen\AVRStudio\Arduino\MIDIout\MIDIout\MIDIout\Midi\Sketch.cpp(20,1): error: undefined reference to `some_test()'
collect2.exe(0,0): error: ld returned 1 exit status
        make: *** [Midi.elf] Error 1
D:\Mirror\Anwendungen\AVRStudio\Arduino\MIDIout\MIDIout\MIDIout\Midi\Debug\Makefile(123,1): error: recipe for target 'Midi.elf' failed
        The command exited with code 2.
    Done executing task "RunCompilerTask" -- FAILED.
Done building target "CoreBuild" in project "Midi.cppproj" -- FAILED.
Done building project "Midi.cppproj" -- FAILED.

Build FAILED.
========== Build: 1 succeeded or up-to-date, 1 failed, 0 skipped ==========

Sketch.cpp 编译为 C++,包括 test.h。为了支持函数重载、class 成员资格等,C++ 使用 name mangling 在符号名称中编码这些 C++ 特性。因此,Sketch.cpp 中 some_test 的符号名称与 test.c 中的符号名称不同,后者被编译为 C 且未应用名称混淆。

解决方案是在 header 是 C++ 编译时通过指定符号具有 C 链接来防止此符号的名称混淆:

#ifndef TEST_H
#define TEST_H

#if defined __cplusplus
extern "C"
{
    #warning Using C linkage in C++ for test.h
#endif

void some_test(void);
#warning "processing test.h"

#if defined __cplusplus
}
#endif


#endif //TEST_H

extern "C" 声明中的大括号({...})表示所有包含在其中的符号声明都具有 C 链接。对于 C/C++ 互操作性,所有 C 代码 header 都应包含此包装器。

当然,另一种解决方案是将 test.c 编译为 C++(最简单的方法是将其重命名为 text.cpp)。请注意,Arduino Sketch 代码和库 C++ 代码。通常最好始终坚持 C++ 编译,以允许访问 Arduino 库,该库将具有 C++ 链接,并且需要大量包装代码才能从 C 访问。

毫无帮助的是,构建日志明显地使用 C++ 编译时显示“Invoking: AVR8/GNU C Compiler”。我认为这只是 Atmel Studio 的事情,不是由编译器本身生成的。