arduino-cmake / Arduino-CMake-NG

CMake-Based framework for Arduino platforms
MIT License
138 stars 39 forks source link

An unfortunate series of roadblocks in a multiproject configuration #55

Closed Nihlus closed 5 years ago

Nihlus commented 5 years ago

I have some issues using a multi-project structure for my project. In general, I have a root CMakeLists.txt with common configuration, from which I add subdirectories where my subprojects live. These subprojects are one executable (the main program) and a custom arduino library. The library is not packaged, and is developed as a part of the project.

I've hit a number of issues, worked around a couple, and finally hit a roadblock. These are the problems I hit:

  1. get_board_id fails if called before a project() call, preventing it from being used in the root file without creating a fake project with an `Unknown CMake command" fault.
/home/jarl/.local/share/JetBrains/Toolbox/apps/CLion/ch-0/183.4139.25/bin/cmake/linux/bin/cmake -DCMAKE_BUILD_TYPE=Debug -G "CodeBlocks - Unix Makefiles" /home/jarl/Programming/Plantbuddy
CMake Error at CMakeLists.txt:11 (get_board_id):
  Unknown CMake command "get_board_id".

-- Configuring incomplete, errors occurred!

[Finished]
  1. find_arduino_library fails if called before an add_arduino_executable or add_arduino_library, preventing it from being used in the root file. Instead, it must be used in a subdirectory, making it unclear which libraries are available to which targets, and in what order to load them.
/home/jarl/.local/share/JetBrains/Toolbox/apps/CLion/ch-0/183.4139.25/bin/cmake/linux/bin/cmake -DCMAKE_BUILD_TYPE=Debug -G "CodeBlocks - Unix Makefiles" /home/jarl/Programming/Plantbuddy
-- The C compiler identification is GNU 4.9.2
-- The CXX compiler identification is GNU 4.9.2
-- Arduino SDK version 1.8.1: /opt/arduino-1.8.1
-- Determined Platform Header: /opt/arduino-1.8.1/hardware/arduino/avr/cores/arduino/Arduino.h
-- Arduino SDK version 1.8.1: /opt/arduino-1.8.1
-- Determined Platform Header: /opt/arduino-1.8.1/hardware/arduino/avr/cores/arduino/Arduino.h
-- Check for working C compiler: /opt/arduino-1.8.1/hardware/tools/avr/bin/avr-gcc
-- Check for working C compiler: /opt/arduino-1.8.1/hardware/tools/avr/bin/avr-gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Arduino SDK version 1.8.1: /opt/arduino-1.8.1
-- Determined Platform Header: /opt/arduino-1.8.1/hardware/arduino/avr/cores/arduino/Arduino.h
-- Check for working CXX compiler: /opt/arduino-1.8.1/hardware/tools/avr/bin/avr-g++
-- Check for working CXX compiler: /opt/arduino-1.8.1/hardware/tools/avr/bin/avr-g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
CMake Error at cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake:60 (get_target_property):
  get_target_property() called with non-existent target "uno_core_lib".
Call Stack (most recent call first):
  cmake/Platform/Targets/PlatformLibraryTarget.cmake:63 (_link_arduino_cmake_library)
  cmake/Platform/Targets/ArduinoLibraryTarget.cmake:29 (link_platform_library)
  cmake/Platform/Libraries/LibrariesFinder.cmake:77 (add_arduino_library)
  CMakeLists.txt:17 (find_arduino_library)

CMake Error at cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake:60 (get_target_property):
  get_target_property() called with non-existent target "uno_core_lib".
Call Stack (most recent call first):
  cmake/Platform/Targets/PlatformLibraryTarget.cmake:63 (_link_arduino_cmake_library)
  cmake/Platform/Targets/ArduinoLibraryTarget.cmake:29 (link_platform_library)
  cmake/Platform/Libraries/LibrariesFinder.cmake:77 (add_arduino_library)
  CMakeLists.txt:17 (find_arduino_library)

CMake Error at cmake/Platform/Targets/ArduinoLibraryTarget.cmake:66 (message):
  Core Library target doesn't exist.  This is bad and should be reported
Call Stack (most recent call first):
  CMakeLists.txt:19 (link_arduino_library)

-- Configuring incomplete, errors occurred!
See also "/home/jarl/Programming/Plantbuddy/cmake-build-debug/CMakeFiles/CMakeOutput.log".

[Finished]
  1. add_arduino_library fails completely if _sources is omitted, preventing target_sources from being used.
/home/jarl/.local/share/JetBrains/Toolbox/apps/CLion/ch-0/183.4139.25/bin/cmake/linux/bin/cmake -DCMAKE_BUILD_TYPE=Debug -G "CodeBlocks - Unix Makefiles" /home/jarl/Programming/Plantbuddy
-- Arduino SDK version 1.8.1: /opt/arduino-1.8.1
-- Determined Platform Header: /opt/arduino-1.8.1/hardware/arduino/avr/cores/arduino/Arduino.h
-- Arduino SDK version 1.8.1: /opt/arduino-1.8.1
-- Determined Platform Header: /opt/arduino-1.8.1/hardware/arduino/avr/cores/arduino/Arduino.h
-- Arduino SDK version 1.8.1: /opt/arduino-1.8.1
-- Determined Platform Header: /opt/arduino-1.8.1/hardware/arduino/avr/cores/arduino/Arduino.h
CMake Error at cmake/Platform/Utilities/CMakeArgumentsUtils.cmake:21 (list):
  list GET given empty list
Call Stack (most recent call first):
  cmake/Platform/Utilities/CMakeArgumentsUtils.cmake:60 (_consume_reserved_arguments)
  cmake/Platform/Targets/ArduinoLibraryTarget.cmake:11 (parse_sources_arguments)
  microui/CMakeLists.txt:3 (add_arduino_library)

CMake Error at cmake/Platform/Utilities/CMakeArgumentsUtils.cmake:23 (if):
  if given arguments:

    "IN_LIST" "_reserved_options"

  Unknown arguments specified
Call Stack (most recent call first):
  cmake/Platform/Utilities/CMakeArgumentsUtils.cmake:60 (_consume_reserved_arguments)
  cmake/Platform/Targets/ArduinoLibraryTarget.cmake:11 (parse_sources_arguments)
  microui/CMakeLists.txt:3 (add_arduino_library)

-- Configuring incomplete, errors occurred!
See also "/home/jarl/Programming/Plantbuddy/cmake-build-debug/CMakeFiles/CMakeOutput.log".

[Finished]
  1. find_arduino_library fails once more if called after add_arduino_library with explicit source specification, with the same error as 2.

  2. Removing the libraries altogether to continue results in find_arduino_library failing when the executable attempts to link to it, due to it not being able to find the target.

The full source for the project scaffolding is available here: https://github.com/Nihlus/plantbuddy Each problem stage has its own branch, so they can be individually inspected. In each branch succeeding roadblock-1, the workaround has been applied. roadblock-1 can be considered the ideal setup.

Any help with this would be much appreciated. I love being able to use CMake & CLion for my arduino projects, and in single-project mode, I haven't had any issues at all.

MrPointer commented 5 years ago

@Nihlus Sorry for the long delay, I'm extremely busy these weeks... As to your problems, I'll relate to each at the order they appeared:

  1. That's just how CMake works. You shouldn't do anything before the project() call, since this is what initializes CMake itself behind the scenes, kind of. In our case, you're trying to call a function from a framework that is actually part of a toolchain. All toolchains are read after the call to project(), or more accurately - As part of it. So if you'd think about it for a moment you'll realize you're trying to call an undefined function, which of course fails the execution of the script.
  2. This is a somewhat expected behavior. The find_arduino_library function attempts to link the board's CoreLib (Core Library) target to the found library target, i.e. to itself. However, the CoreLib target is only created inside the add_arduino_executable or add_arduino_library functions, since these are the ones that create the "top-level" target one would be working with. With that being said, I agree that it's unclear why this assumption has been made, or worse - How should the Find API should be used. It's definitely a note to the project's developers to fix in future versions.
  3. Well, this is interesting since it requires some extra knowledge about how libraries work in general C/C++ programming, not just in Arduino. Whenever there's a library without sources, i.e. without .c or .cpp files, it's called a header-only library. Due to a user request, support for these types of libraries in the Arduino context also has been added to the framework, but currently it requires calling a different function. You can read more about it here: Header-Only Libraries
  4. It maybe an indirect continuation of 3 if the libraries you're attempting to find are also header-only. Everything you need to know on the subject is embedded in the link above.
  5. Well, this is obvious, that's not how CMake should be used of course and it's considered a "user error", which probably is derived from all the errors mentioned above. There's nothing to add here.

Let me know if anything has helped you, good luck 😃

Nihlus commented 5 years ago

@MrPointer Thanks for getting back to me!

  1. I was not aware of that - typically, in non-toolchain projects, I never run into this issue.
  2. Not having this possible makes it very clunky to use, and seems very strange to me - requiring a library to link against the main executable instead of the other way around seems very backwards, and is far from expected behaviour. Not only that, but that it fails in such an explosive way.
  3. In this case, the library is not a header-only library. It simply has no sources associated with the target at target creation time, and only get sources after a call to target_sources. This is a fundamental function of modern CMake, and not being able to use it makes using the library rather clunky. It works fine on add_arduino_executable.

At the very least, a more descriptive error message ought to be emitted.

  1. See above, although I can try with a source file as well. It might help.

  2. I think you've misunderstood me - if you have a look at the CMakeLists.txt file in the microui library at this point (which is header-only, due to me just using a dummy file. I'll try with sources, too), I was referring to it not linking to any other libraries, but the main executable in turn not being able to link to it. I was not referring to linking to a nonexistent library.

MrPointer commented 5 years ago

@Nihlus Well, as strange as it may seem - I haven't elaborated enough on each item, so please let me.

  1. It's considered best practice to write CMake code of any form after the project() call, even on non-toolchain-based projects. However, it's completely acceptable to have a project structure like yours, where the top-level CMakeLists.txt sets some global definitions which are later used by sub-projects - It simply requires the top-level CMake file to define a project, named as you'd like (usually NONE).
  2. It happens to be my mistake - Too much time away from the code... One can definitely find a library before adding a custom/user library/executable target! With that in mind, what currently can't be done is linking a found library to another if a custom/user target hasn't been already created. Yes, I completely agree that this is still unexpected behavior from a user's POV, and this will be fixed in the upcoming versions.
  3. If so, this is indeed a bug which should also be fixed.
  4. Same as above
  5. If I understood you better this time, it seems you're trying to link the microui library target to the executable target, and it fails, right? If so, I think it's a CMake limitation. Looking at your repo at this point, I notice that you're dividing the library target and the executable into 2 separate sub-directories. If I'm not mistaken, it means that they won't "see" each other's internals, meaning that target and variables in general won't be shared between them. Nevertheless, I still need to verify this behavior and hopefully suggest a solution for you if indeed this is true.

Edit: Issues number 3 and 4 are solved by #60, Issue number 2 can be discussed #56.

MrPointer commented 5 years ago

@Nihlus Please update if there's anything else to this issue before I close it.