Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

undefined reference on custom components after updating esp-idf and tools (IDFGH-1472) #3741

Closed
avanbremen opened this issue Jul 5, 2019 · 15 comments

Comments

@avanbremen
Copy link

Environment

  • Development Kit: none
  • Module or chip used: ESP32-WROOM-32D
  • IDF version: v4.0-dev-1136-g28f1cdf5e
  • Build System: CMake
  • Compiler version: xtensa-esp32-elf-gcc (crosstool-NG esp32-2019r1) 8.2.0
  • Operating System: Windows
  • Power Supply: USB

Problem Description

After updating esp-idf to the latest version and ESP-IDF Tools from version 1.2 to version 2.0, I am no longer able to compile my project due to undefined reference to '<function name>' errors for custom components (my own code).

Expected Behavior

Project should build without errors as before.

Actual Behavior

Build fails due to undefined reference to '<function name>' errors.

Debug Info

Component Grouping

Note that individual components are grouped together based on functionality, like:

myProject
  \_ components
        \_ application
             \_ component1
             \_ component2
        \_ data
             \_ component3
        \_ drivers
             \_ component4
        \_ library
             \_ component5
             \_ component6
        \_ system
             \_ component7

The top-level CMakeLists.txt contains:

set(EXTRA_COMPONENT_DIRS "components/application"
                         "components/data"
                         "components/drivers"
                         "components/library"
                         "components/system")

I have been using the CMake-based ESP-IDF build system since the beginning and even though it has been challenging at times (keeping up with the changes), I am very happy with the transition.

Unfortunately I cannot build my project at the moment so I cannot continue with development. Perhaps something was changed in the way extra components are handled, or the order in which custom components are built?

Build Output

Note: Project, user and function names have been replaced with dummy names.

-- Configuring done
-- Generating done
-- Build files have been written to: C:/Users/myUser/esp/myProject/build
[1/7] Performing build step for 'bootloader'
ninja: no work to do.
[4/5] Linking CXX executable myProject.elf
FAILED: myProject.elf
cmd.exe /C "cd . && C:\Users\myUser\.espressif\tools\xtensa-esp32-elf\esp32-2019r1-8.2.0\xtensa-esp32-elf\bin\xtensa-esp32-elf-g++.exe  -mlongcalls -Wno-frame-address  -nostdlib @CMakeFiles\myProject.elf.rsp  -o myProject.elf  && cd ."
c:/users/myUser/.espressif/tools/xtensa-esp32-elf/esp32-2019r1-8.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld.exe: esp-idf/my_component_1/libmy_component_1.a(app_MyComponentName.c.obj):(.literal.app_myFunction1+0x1c): undefined reference to `lib_myFunction1'
c:/users/myUser/.espressif/tools/xtensa-esp32-elf/esp32-2019r1-8.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld.exe: esp-idf/my_component_1/libmy_component_1.a(app_MyComponentName.c.obj):(.literal.app_myFunction1+0x20): undefined reference to `lib_myFunction2'
c:/users/myUser/.espressif/tools/xtensa-esp32-elf/esp32-2019r1-8.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld.exe: esp-idf/my_component_1/libmy_component_1.a(app_MyComponentName.c.obj):(.literal.app_myFunction1+0x24): undefined reference to `lib_myFunction3'
c:/users/myUser/.espressif/tools/xtensa-esp32-elf/esp32-2019r1-8.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld.exe: esp-idf/my_component_1/libmy_component_1.a(app_MyComponentName.c.obj): in function `app_myFunction1':
c:\users\myUser\esp\myProject\build/../components/application/my_component_1/app_MyComponentName.c:196: undefined reference to `lib_myFunction1'
c:/users/myUser/.espressif/tools/xtensa-esp32-elf/esp32-2019r1-8.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld.exe: c:\users\myUser\esp\myProject\build/../components/application/my_component_1/app_MyComponentName.c:212: undefined reference to `lib_myFunction2'
c:/users/myUser/.espressif/tools/xtensa-esp32-elf/esp32-2019r1-8.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld.exe: c:\users\myUser\esp\myProject\build/../components/application/my_component_1/app_MyComponentName.c:251: undefined reference to `lib_myFunction3'
c:/users/myUser/.espressif/tools/xtensa-esp32-elf/esp32-2019r1-8.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld.exe: esp-idf/my_component_2/libmy_component_2.a(app_MyComponentName2.c.obj):(.literal.app_myFunction2+0x54): undefined reference to `app_myFunction3'
c:/users/myUser/.espressif/tools/xtensa-esp32-elf/esp32-2019r1-8.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld.exe: esp-idf/my_component_2/libmy_component_2.a(app_MyComponentName2.c.obj): in function `app_myFunction2':
c:\users\myUser\esp\myProject\build/../components/application/my_component_2/app_MyComponentName2.c:132: undefined reference to `app_myFunction3'
collect2.exe: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
ninja failed with exit code 1
@github-actions github-actions bot changed the title undefined reference on custom components after updating esp-idf and tools undefined reference on custom components after updating esp-idf and tools (IDFGH-1472) Jul 5, 2019
@PerMalmberg
Copy link
Contributor

I believe you're looking for idf_build_component(component_dir).

@renzbagaporo
Copy link
Contributor

Could also be that some REQUIRES or PRIV_REQUIRES are missing from those components.

@avanbremen
Copy link
Author

avanbremen commented Jul 5, 2019

@PerMalmberg Thank you very much for your reply. From the documentation sections Optional Project Variables and Renaming main component I believe using set(EXTRA_COMPONENT_DIRS "components/application") before calling the core CMake file (project.cmake) is the preferred way of doing this. Please correct me if I'm wrong, because I'm always looking to adopt the latest and greatest.

On a side note, adding the snippet below to the top-level CMakeLists.txt resulted in a CMake Error Unknown CMake command "idf_build_component". Do you know if this function is intended to replace set(EXTRA_COMPONENT_DIRS "")?

idf_build_component("components/application"
                    "components/data"
                    "components/drivers"
                    "components/library"
                    "components/system")

@renzbagaporo Thank you very much for your reply. I do not believe that is the case, because I was able to build the project before updating esp-idf and ESP-IDF Tools. My code did not change. So I know REQUIRES and PRIV_REQUIRES were set correctly before.

However I did notice that build.cmake no longer sets esp32 as COMPONENT_REQUIRES_COMMON, which was previously set by idf_functions.cmake. Instead, the new build.cmake now sets esp_rom esp_common xtensa which were not in the older idf_functions.cmake. Is component esp32 replaced by esp_rom and esp_common? I will see if this change might be causing the build issues.

Just to be sure, is it allowed to put the REQUIRES and PRIV_REQUIRES on a new line using the new idf_component_register function?

idf_component_register(SRCS "src1.c"
                            "src2.c"
                       INCLUDE_DIRS "include"
                       REQUIRES component1
                                component2
                       PRIV_REQUIRES component3
                                     component4
                                     component5)

Also, is there an easy way to keep track of deprecated CMake functions and variables, so I know what to update? Is there a changelog somewhere?

Thanks for all the hard work!

@PerMalmberg
Copy link
Contributor

@avanbremen I'm using idf_build_component, see here. It was introduced fairly recently, at least as part of the v4-branch. You need to include($ENV{IDF_PATH}/tools/cmake/idf.cmake).

@renzbagaporo
Copy link
Contributor

@PerMalmberg As a clarification, idf_build_component is recommended for people not using the standard project (which looks like @avanbremen is), and creating a custom project like yourself. Under the hood, EXTRA_COMPONENT_DIRS are implemented using idf_build_component (some oversimplification in this statement). Should clarify this in the docs.

However I did notice that build.cmake no longer sets esp32 as COMPONENT_REQUIRES_COMMON, which was previously set by idf_functions.cmake. Instead, the new build.cmake now sets esp_rom esp_common xtensa which were not in the older idf_functions.cmake. Is component esp32 replaced by esp_rom and esp_common? I will see if this change might be causing the build issues.

@avanbremen The target, in this case esp32 is still added in the common requirements, just later in the process by idf_build_process (https://github.com/espressif/esp-idf/blob/master/tools/cmake/build.cmake#L380)

You mentioning idf_functions.cmake makes me think you originally transitioned from ESP-IDF v3.3. This is important as one of the changes is that we no longer use link groups (a dangling --start-group to me more accurate) for linking, and your problem, undefined reference to xxxx is a linking error. Since before the link lines is like this:

--start-group libA libB libC.....

The order of the link libraries didn't matter since all symbols were eventually resolved.

Now though we rely on CMake's ordering and repetition of static libraries (https://cmake.org/cmake/help/v3.12/command/target_link_libraries.html#cyclic-dependencies-of-static-libraries) for this. And the ordering is based on what is in each components REQUIRES and PRIV_REQUIRES, i.e. the component should appear earlier in the link line than its dependencies.

Just to be sure, is it allowed to put the REQUIRES and PRIV_REQUIRES on a new line using the new idf_component_register function?

This should work.

Also, is there an easy way to keep track of deprecated CMake functions and variables, so I know what to update? Is there a changelog somewhere?

This is probably a good idea, will work on it.

@PerMalmberg
Copy link
Contributor

idf_build_component is recommended for people not using the standard project (which looks like @avanbremen is), and creating a custom project like yourself. Under the hood, EXTRA_COMPONENT_DIRS are implemented using idf_build_component (some oversimplification in this statement). Should clarify this in the docs.

May I suggest sticking with a single public-facing API for adding components? Comparing set() vs idf_build_component, the latter definitely feels more modern and in-line with where CMake generally is heading, i.e. using public facing functions instead of variables (compare to the new-ish target_* functions)

@avanbremen
Copy link
Author

@renzbagaporo Thanks for clarifying. I got rid of the initial undefined reference errors by moving certain common headers into their own component. It was a better design choice anyway. However one error remains and I have not yet been able to get rid of it.

It is a pretty straightforward dependency of application-level component1 relying on component2. component1 has component2 as REQUIRES in its CMakeLists.txt. There is no cyclic dependency. Can you please explain how I could use CMake add_library and target_link_libraries to resolve the linker error? I assume this works by altering the component1 CMakeLists.txt, correct?

I appreciate you looking into creating a CMake changelog, this will definitely help users that adopted the CMake-based build system early on.

@PerMalmberg From what I understand idf_build_component is meant to build a single component and set(EXTRA_COMPONENT_DIRS can be used to specify additional directories in which components will automatically be found, just like the default 'components' directory. When using idf_build_component I assume you would have to explicitly specify all those individual components. If this is the case, than it would make sense to keep both as they offer different functionality.

@renzbagaporo
Copy link
Contributor

renzbagaporo commented Jul 8, 2019

It is a pretty straightforward dependency of application-level component1 relying on component2. component1 has component2 as REQUIRES in its CMakeLists.txt. There is no cyclic dependency. Can you please explain how I could use CMake add_library and target_link_libraries to resolve the linker error? I assume this works by altering the component1 CMakeLists.txt, correct?

@avanbremen You mentioned that component1 has component2 in its REQUIRES, therefore, I think the dependency between them should already be established. Under the hood, REQUIRES and PRIV_REQUIRES are implemented using target_link_libraries anyway, so there must be something more at play here.

Are you able to give more information as to the relationship/structure between component1 and component2? Are you absolutely sure there is no circular dependency between them, perhaps a non-obvious one due to a larger loop? In that case, you may have to restructure the source code or use LINK_INTERFACE_MULTIPLICITY (https://cmake.org/cmake/help/v3.5/prop_tgt/LINK_INTERFACE_MULTIPLICITY.html).

May I suggest sticking with a single public-facing API for adding components? Comparing set() vs idf_build_component, the latter definitely feels more modern and in-line with where CMake generally is heading, i.e. using public facing functions instead of variables (compare to the new-ish target_* functions)

@PerMalmberg Making our build system in line with what CMake is doing is one of the goals of refactor. Though the reason for retaining this is that people are more familiar with adding components this way. There is no reason that idf_build_component can gain the ability of adding multiple components by way of an option argument to do so.

Like I've mentioned, the variables are for people who do not need to touch the underlying build system APIs, which is most likely the people just using the standard projects. For people with custom projects, using the build system API directly makes more sense.

We'll definitely take this comment under advisement, though.

@avanbremen
Copy link
Author

@renzbagaporo Because of the strong dependency of component1 on component2 I decided to group them together and redesign them as a new single component. This approach resulted in a better overall design and it solved the linker errors.

Thank you both for your constructive help. Appreciate it.

@renzbagaporo
Copy link
Contributor

Glad to hear that @avanbremen. Just FYI this design change also exposed some unsavory dependency graph with components delivered in ESP-IDF, which we've had to workaround for now in order for things to build. I think this definitely forces developers to think about their source code structure.

@su-Koch
Copy link

su-Koch commented Mar 31, 2020

got exactly the same issue: described it here: https://www.esp32.com/viewtopic.php?f=13&t=14952
maybe someone can help me

@renzbagaporo
Copy link
Contributor

@su-Koch Have replied on the thread you pointed out.

@su-Koch
Copy link

su-Koch commented Apr 3, 2020

Hei Renz, thank you but I think that didn't help. I posted another comment in the forum. Thanks

@ip-v1
Copy link

ip-v1 commented Aug 14, 2020

I just setup VScode with the ESP IDF plugin and setup things. I started with the Ethernet basic example(which worked) and added code for a web server to the main file and it worked as well. I tried to create a component out of the http_server example but i am getting an error at the linker stage.

> Executing task: cmake -G Ninja .. <

-- mconf-idf version mconf-v4.6.0.0-idf-20190628-win32
-- ccache will be used for faster recompilation
-- Project version: b079e27
-- Building ESP-IDF components for target esp32
-- Adding linker script C:/Users/isingh/Desktop/workspace/projects/inderpreet/esp32_ethernet/basic/basic/build/esp-idf/esp32/esp32_out.ld
-- Adding linker script C:/Users/isingh/esp-idf/components/esp32/ld/esp32.project.ld.in
-- Adding linker script C:/Users/isingh/esp-idf/components/esp32/ld/esp32.peripherals.ld
-- Adding linker script C:/Users/isingh/esp-idf/components/esp_rom/esp32/ld/esp32.rom.ld
-- Adding linker script C:/Users/isingh/esp-idf/components/esp_rom/esp32/ld/esp32.rom.libgcc.ld
-- Adding linker script C:/Users/isingh/esp-idf/components/esp_rom/esp32/ld/esp32.rom.syscalls.ld
-- Adding linker script C:/Users/isingh/esp-idf/components/esp_rom/esp32/ld/esp32.rom.newlib-data.ld
-- Adding linker script C:/Users/isingh/esp-idf/components/esp_rom/esp32/ld/esp32.rom.newlib-funcs.ld
-- Components: app_trace app_update asio bootloader bootloader_support bt coap console cxx driver efuse esp-tls esp32 esp_adc_cal esp_common esp_eth esp_event esp_gdbstub esp_http_client esp_http_server esp_https_ota esp_https_server esp_local_ctrl esp_ringbuf esp_rom esp_websocket_client esp_wifi espcoredump esptool_py expat fatfs freemodbus freertos heap idf_test ip_web_server jsmn json libsodium log lwip main mbedtls mdns mqtt newlib nghttp nvs_flash openssl partition_table protobuf-c protocomm pthread sdmmc soc spi_flash spiffs tcp_transport tcpip_adapter ulp unity vfs wear_levelling wifi_provisioning wpa_supplicant xtensa
-- Component paths: C:/Users/isingh/esp-idf/components/app_trace C:/Users/isingh/esp-idf/components/app_update C:/Users/isingh/esp-idf/components/asio C:/Users/isingh/esp-idf/components/bootloader C:/Users/isingh/esp-idf/components/bootloader_support C:/Users/isingh/esp-idf/components/bt C:/Users/isingh/esp-idf/components/coap C:/Users/isingh/esp-idf/components/console C:/Users/isingh/esp-idf/components/cxx C:/Users/isingh/esp-idf/components/driver C:/Users/isingh/esp-idf/components/efuse C:/Users/isingh/esp-idf/components/esp-tls C:/Users/isingh/esp-idf/components/esp32 C:/Users/isingh/esp-idf/components/esp_adc_cal C:/Users/isingh/esp-idf/components/esp_common C:/Users/isingh/esp-idf/components/esp_eth C:/Users/isingh/esp-idf/components/esp_event C:/Users/isingh/esp-idf/components/esp_gdbstub C:/Users/isingh/esp-idf/components/esp_http_client C:/Users/isingh/esp-idf/components/esp_http_server C:/Users/isingh/esp-idf/components/esp_https_ota C:/Users/isingh/esp-idf/components/esp_https_server C:/Users/isingh/esp-idf/components/esp_local_ctrl C:/Users/isingh/esp-idf/components/esp_ringbuf C:/Users/isingh/esp-idf/components/esp_rom C:/Users/isingh/esp-idf/components/esp_websocket_client C:/Users/isingh/esp-idf/components/esp_wifi C:/Users/isingh/esp-idf/components/espcoredump C:/Users/isingh/esp-idf/components/esptool_py C:/Users/isingh/esp-idf/components/expat C:/Users/isingh/esp-idf/components/fatfs C:/Users/isingh/esp-idf/components/freemodbus C:/Users/isingh/esp-idf/components/freertos C:/Users/isingh/esp-idf/components/heap C:/Users/isingh/esp-idf/components/idf_test C:/Users/isingh/Desktop/workspace/projects/inderpreet/esp32_ethernet/basic/basic/components/ip_web_server C:/Users/isingh/esp-idf/components/jsmn C:/Users/isingh/esp-idf/components/json 
C:/Users/isingh/esp-idf/components/libsodium C:/Users/isingh/esp-idf/components/log C:/Users/isingh/esp-idf/components/lwip C:/Users/isingh/Desktop/workspace/projects/inderpreet/esp32_ethernet/basic/basic/main C:/Users/isingh/esp-idf/components/mbedtls C:/Users/isingh/esp-idf/components/mdns C:/Users/isingh/esp-idf/components/mqtt C:/Users/isingh/esp-idf/components/newlib C:/Users/isingh/esp-idf/components/nghttp C:/Users/isingh/esp-idf/components/nvs_flash C:/Users/isingh/esp-idf/components/openssl C:/Users/isingh/esp-idf/components/partition_table C:/Users/isingh/esp-idf/components/protobuf-c C:/Users/isingh/esp-idf/components/protocomm C:/Users/isingh/esp-idf/components/pthread C:/Users/isingh/esp-idf/components/sdmmc C:/Users/isingh/esp-idf/components/soc C:/Users/isingh/esp-idf/components/spi_flash C:/Users/isingh/esp-idf/components/spiffs C:/Users/isingh/esp-idf/components/tcp_transport C:/Users/isingh/esp-idf/components/tcpip_adapter C:/Users/isingh/esp-idf/components/ulp C:/Users/isingh/esp-idf/components/unity C:/Users/isingh/esp-idf/components/vfs C:/Users/isingh/esp-idf/components/wear_levelling C:/Users/isingh/esp-idf/components/wifi_provisioning C:/Users/isingh/esp-idf/components/wpa_supplicant C:/Users/isingh/esp-idf/components/xtensa
-- Configuring done
-- Generating done
-- Build files have been written to: C:/Users/isingh/Desktop/workspace/projects/inderpreet/esp32_ethernet/basic/basic/build

Terminal will be reused by tasks, press any key to close it.

> Executing task: cmake --build . <

[1/6] cmd.exe /C "cd /D C:\Users\isingh\Desktop\workspace\projects\inderpreet\esp32_ethernet\basic\basic\build...\3.13.4\bin\cmake.exe -E echo *******************************************************************************"Partition table binary generated. Contents:
*******************************************************************************
# Espressif ESP32 Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs,data,nvs,0x9000,24K,
phy_init,data,phy,0xf000,4K,
factory,app,factory,0x10000,1M,
*******************************************************************************
[2/6] Performing build step for 'bootloader'
ninja: no work to do.
[3/4] Linking CXX executable ethernet_basic.elf
FAILED: ethernet_basic.elf
cmd.exe /C "cd . && C:\Users\isingh\.espressif\tools\xtensa-esp32-elf\esp-2019r2-8.2.0\xtensa-esp32-elf\bin\xtensa-esp32-elf-g++.exe  -mlongcalls -Wno-frame-address  -nostdlib @CMakeFiles\ethernet_basic.elf.rsp  -o ethernet_basic.elf  && cd ."
c:/users/isingh/.espressif/tools/xtensa-esp32-elf/esp-2019r2-8.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld.exe: esp-idf/main/libmain.a(ethernet_example_main.c.obj):(.literal.app_main+0x38): undefined reference to `start_webserver'
c:/users/isingh/.espressif/tools/xtensa-esp32-elf/esp-2019r2-8.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld.exe: esp-idf/main/libmain.a(ethernet_example_main.c.obj): in function `app_main':
C:\Users\isingh\Desktop\workspace\projects\inderpreet\esp32_ethernet\basic\basic\build/../main/ethernet_example_main.c:155: undefined reference to `start_webserver'
collect2.exe: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
The terminal process "C:\Windows\System32\cmd.exe /d /c cmake --build ." terminated with exit code: 1.

Development Kit: ESP32 Ethernet Kit 1.2
Module or chip used: ESP32-WROVER-E
IDF version: v4.0.1
Operating System: Windows
Power Supply: USB

Code at https://github.com/ip-v1/esp32_ethernet_http_component

Not sure what I am doing wrong. Can anyone help? Thanks

@francis2tm
Copy link

Hello, I have exactly this problem and there seems to be no solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants