CMAKE include PATHS causing trouble for linux cross compiling for arm64 (Raspberry pi)

Hi
I am creating programs in C++ for my Raspberry Pi Pico W on my dev machine, which is a Raspberry Pi 400 running Linux. The problem, I am going to describe is exactly the same whether I am working on a Raspberry PI OS 64 Bit Desktop or Ubuntu 22.04.3.

I am using CMAKE because I am using the Pico SDK, which is using CMAKE. I have made some of the pico-examples and a number of simple test programs. They all build without any issues.

Let me show you the CMakeLists.txt used in my test program (same as suggested by the raspberry pi organisation):

CMakeLists.txt

cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(itest_project C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
add_executable(itest
itest.cpp
)
pico_enable_stdio_usb(itest 1)
pico_enable_stdio_uart(itest 1)
pico_add_extra_outputs(itest)
target_include_directories(itest PRIVATE ${CMAKE_CURRENT_LIST_DIR} )
target_link_libraries(itest pico_cyw43_arch_lwip_threadsafe_background pico_stdlib)

So far so good. Problem is I am creating a program, where in an easy way needs to read from a file on the internet. For this purpose I need to install external libraries, such as CURL or httplib. I have tried both, none of them are working. Problem is that when I in my program writes #include <curl/curl.h>, the linker says it cannot find the file (similar for httplib). This is happeing, when I am using above CMakeList.txt.

I think I have realized though - not that it was mentioned by the Raspberry PI organisation - that when I do that, it seems to be needed to define the PATHS in CMakeLists.txt, which I the did as follows:

CMakelists.txt

cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(itest_project C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

set(INCLUDE_PATH "/usr/include")
include_directories(${INCLUDE_PATH})

set(INCLUDE_PATH1 "/usr/include/aarch64-linux-gnu")
include_directories(${INCLUDE_PATH1})

set(INCLUDE_PATH2 "/usr/include/aarch64-linux-gnu/bits/types")
include_directories(${INCLUDE_PATH2})

pico_sdk_init()
add_executable(itest
itest.cpp
)
pico_enable_stdio_usb(itest 1)
pico_enable_stdio_uart(itest 1)
pico_add_extra_outputs(itest)
target_include_directories(itest PRIVATE ${CMAKE_CURRENT_LIST_DIR})
#target_link_libraries(itest pico_cyw43_arch_lwip_threadsafe_background pico_stdlib libssl libcrypto)
target_link_libraries(itest pico_cyw43_arch_lwip_threadsafe_background pico_stdlib)

The includes have some effects and the linker does no longer complains about missing files, but instead it complains about really weird things:

…

Consolidate compiler generated dependencies of target itest
[ 11%] Building CXX object CMakeFiles/itest.dir/itest.cpp.obj
In file included from /usr/include/newlib/c++/10.3.1/bits/locale_facets.h:41,
from /usr/include/newlib/c++/10.3.1/bits/basic_ios.h:37,
from /usr/include/newlib/c++/10.3.1/ios:44,
from /usr/include/newlib/c++/10.3.1/ostream:38,
from /usr/include/newlib/c++/10.3.1/iostream:39,
from /home/oti/pico/itest/itest.cpp:1:
/usr/include/newlib/c++/10.3.1/arm-none-eabi/thumb/v6-m/nofp/bits/ctype_base.h:44:35: error: '_U' was not declared in this scope; did you mean '_u'?
44 | static const mask upper = _U;
| ^~
| _u
/usr/include/newlib/c++/10.3.1/arm-none-eabi/thumb/v6-m/nofp/bits/ctype_base.h:45:32: error: '_L' was not declared in this scope; did you mean '_u'?
45 | static const mask lower = _L;

| ^~
…
It goes without saying that I did not touch any of these files - and again If I remove the CMAKE paths I do not see the problem, but then the compiler cannot find curl/curl.h !!

What is going on and how do I fix it?

Your help is highly appreciated - I have spent many hours trying to get this to work…

Best regards, Ole

Are you sure you want to include host paths for cross compiling? Should these instead be under some “sysroot” path? Are you using a toolchain file for cross compilation?

Hi Ben

Thanks a lot for your prompt response.

As for your suggestions, my answers are unfortunately that I do not know. It is very likely that include statements are wrong, since I am not seeing the problem, if they are not there. But I am a newbie and I am using a standard set-up all configured by a shell script provided by the raspberry pi organisation. It would be really great if you could me a few more specific hints as how to get answers to your questions/suggestions.

Thank you for you patience.

Br, Ole

I don’t do embedded, but I suspect that toolchain files for this are around somewhere. I’d try to find one and use it.

1 Like

You need to include the CURL library in your project properly. There are three options:

  1. find_package(CURL REQUIRED) (easy, but fails if CURL isn’t found)
  2. Use the FetchContent module (will download & build CURL if needed; see Using (a.k.a., Linking) Third Party Libraries Like RayLib in Your Project with CMake » Kea Sigma Delta)
  3. Put the CURL library’s source code in a subdirectory, and use add_subdirectory()

All of the cases above will set the CURL_INCLUDE_DIRS and CURL_LIBRARIES variables (amongst others), which you can then use with your project. For example:
target_include_directories(itest PRIVATE ${CURL_INCLUDE_DIRS})
target_link_libraries(itest ${CURL_LIBRARIES})

I’m not familiar with httplib, but including it should be similar.

Hi Hans

Thanks a lot for your concrete suggestions. I agree that Curl must be included properly, which is not the case as I am doing it. I realize that the fundamental problem is that I am mixing up the linux tool chain with the cross-compiler tool chain and that will not work. Whether the CURl library will actually compile and link properly for my target µC, which is an ARM Cortex-M0+ is maybe doubtful, I am told, but I will give it a try - otherwise I have seen they have a Tiny version, which might work :slight_smile:

Best wishes,
Ole

If httplib is simpler, then perhaps try that one first.

You can increase the chance that CURL builds for your target uC by disabling all the unneeded modules. In your case, you only need HTTP(S), and so can use the HTTP_ONLY option. That’ll disable all the modules you don’t need. Use the following code before the lines to import CURL:
set(HTTP_ONLY OFF CACHE INTERNAL “”)

You probably also want to build the static library, instead of shared, so set BUILD_SHARED_LIBS to OFF, and BUILD_STATIC_LIBS to ON.