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

CMake Support #676

Closed
AndruePeters opened this issue Nov 6, 2020 · 61 comments · Fixed by #772
Closed

CMake Support #676

AndruePeters opened this issue Nov 6, 2020 · 61 comments · Fixed by #772

Comments

@AndruePeters
Copy link

AndruePeters commented Nov 6, 2020

Describe the problem
Configuring RF24 as part of another project is not 'simple'. My current method calls a script the configures and
installs RF24 on the host system. However, I'd like to be able to use this repo as I would other libraries such as:
with a package manager, or using git submodules. One of the present issues regards the build system which
requires running the configure script and then using Make.

Describe the solution you'd like
The solution would be to combine the configuration and build system and configuration. CMake provides an interface
to handle this. CMake has also transitioned to becoming industry standard for build systems. The interface for building using
CMake can be similar to the current interface for the configure script; e.g., cmake . -Bbuild -Ddriver=RPi

Using CMake provides several benefits. CMake will produce a compiler_commands.json, which many tools are supporting. This is mostly a benefit for developers with Intellisense and similar technologies. It should make cross-compilation simpler. Allows better configuration and modularization to warnings and errors while also keeping things simple. CMake itself is distributed as a binary package, so you don't need sudo privileges to use it or update it.

The benefits about simplicity and modularity only hold true if you use the target-based system like it's intended to. I can show examples as needed.

@2bndy5
Copy link
Member

2bndy5 commented Nov 7, 2020

I can show examples as needed.

Yes please. BTW, a PR is always welcome.

@AndruePeters
Copy link
Author

Just got back from an anniversary trip and catching up on work. I'll give an example within the next couple days.
I don't mind submitting a pull request, but I wanted to see if this idea would be encouraged first.

It will take some time, but shouldn't be too bad. In the meantime, look at https://github.com/lefticus/cpp_starter_project and how that CMakeLists.txt is setup. Pay attention to the use of project_options and project_warnings. These are "fake" libraries that are only used to attach compiler options and warnings to the rest of the project. It provides a modular method of doing so. This is the general style I'd use to setup the CMake stuff.

@2bndy5
Copy link
Member

2bndy5 commented Dec 10, 2020

I can think of a couple reasons to switch to CMake:

  1. doxygen doesn't parse the Makefile correctly since it doesn't have a file extension, and doxygen assumes it is a C source code file by default. I couldn't get doxygen to ignore parsing the Makefile without excluding it from the docs entirely. Thus, doxygen outputs a warning about reaching the EoF while inside nested comments; actually, (I think) this is because it sees /* as the beginning of a comment even though they're part of a directory/file path.
  2. building the python wrapper with boost.python is currently being done a via the Makefile.inc, but using pybind11 (a separate issue I'm looking into) allows using CMake directly from setup.py

Reason 1 is more important because we can have doxygen elevate warnings to errors (using Doxyfile's WARN_AS_ERROR tag) and immediately fail generating docs. Without failing fast on warnings, the doxygen build action I put together will always result in "passed" if no actual errors are encountered. Doxygen however also treats *.txt files as C source code, but we can explicitly force CMakeLists.txt to be parsed like a python file because the comment syntax is the same (actual CMakeLists.txt content will have no bearing on documentation if the comments are done in a way that doesn't trigger doxygen's parsing).

@AndruePeters
Copy link
Author

This is in the works. Going slower than I intended.
Are there any style guidelines to apply to this?

@2bndy5
Copy link
Member

2bndy5 commented Dec 17, 2020

This is in the works. Going slower than I intended.

No rush. I really appreciate you taking the initiative. Remember, good work is never finished...

Are there any style guidelines to apply to this?

We have contributing guidelines, but I'm not sure how much the NASA C-style guide or PEP8 would apply to CMake. I wouldn't worry about the Doxygen markup (essentially its Markdown syntax with some special inline cmds like @param); we can work that out in the PR.

@Avamander
Copy link
Member

CMakefiles have their own style usually. We can just agree on spaces, not tabs right now and leave the rest for later.

@2bndy5
Copy link
Member

2bndy5 commented Mar 22, 2021

I'm still exploring the CMake docs... The generation of the /utility/includes.h file from the /configure bash script might be rendered obsolete because I see we can use CMake to specify which extra support folder (from /utility directory) to include in the build process, but /RF24_config.h would need to compensate for this idea. I'm still brainstorming here; I have yet to try CMake out.

@AndruePeters
Copy link
Author

AndruePeters commented Mar 22, 2021 via email

@2bndy5
Copy link
Member

2bndy5 commented Mar 22, 2021

@AndruePeters if you want, I can create a branch called "use-cmake"

@AndruePeters
Copy link
Author

AndruePeters commented Mar 24, 2021 via email

@2bndy5
Copy link
Member

2bndy5 commented Mar 25, 2021

The "use-cmake' branch is created.

I just found out that CMake can take in a config.h.in, replace @embedded_variables@ with CMake's variables' values (including ones commented out), and output the result as config.h. This would actually be better than modifying the RF24_config.h manually (like I mentioned above).

Still learning here, but I'm determined enough to try this stuff out (on a separate/simple repo).

I'm still not that much help (obviously), but it doesn't sound like you need it.

@AndruePeters
Copy link
Author

AndruePeters commented Mar 25, 2021 via email

@2bndy5
Copy link
Member

2bndy5 commented Mar 25, 2021

You think it's okay if it doesn't support SSH builds at this point?

Yes, we can patch it up later. Personally, I think it's useful, but I don't like that it requires disabling security measures

@AndruePeters
Copy link
Author

Sweet. I'm on this now. Got it forked and working on it. Sorry about all the delays. Full time job + grad school + family = crazy busy.

@AndruePeters
Copy link
Author

Is there a reason there's currently no source directory? I'm mostly curious on the decisions for the project structure.

@2bndy5
Copy link
Member

2bndy5 commented Apr 7, 2021

Is there a reason there's currently no source directory?

Because the lib is so old... Using a src directory wasn't compatible with older versions of the Arduino IDE.

Sweet. I'm on this now.

That's so good to hear. I almost pinged you yesterday because I'm trying to incorporate examples for the Pico SDK (which uses CMake), and I have a vague idea of how to do it. Mostly concerned about building for Linux vs building for Pico SDK.

@AndruePeters
Copy link
Author

The approach I'm taking is fairly flexible. It's mostly as simple as defining a variable that checks for what version and then adds the proper files.

I know nothing about Pico SDK, but I think this approach should work. I'll try to have a basic example done by Friday.

@2bndy5
Copy link
Member

2bndy5 commented Apr 11, 2021

we just merged in a new feature to master (it shouldn't affect Linux builds - excluding work on the python wrapper).

@AndruePeters could you make a commit to your fork (I assume the use-cmake branch would be most applicable), so I can at least see your direction. Maybe I can mimic the same direction on the rp2xxx branch (for Pico SDK support). The work doesn't have to be complete; I guess I'm just not good with "teasers".

@AndruePeters
Copy link
Author

AndruePeters commented Apr 14, 2021

I just pushed to my user branch. CMake itself is configuring everything. I'm still testing the actual build process, though.

Currently to run:
cmake . -Bbuild -DRF24_DRIVER=wiringPi

By making it work this way, it allows for device autoconfiguration like the script itself does.

You can look in utility/CMakeLists.txt for how it's currently being handled.

@2bndy5
Copy link
Member

2bndy5 commented Apr 14, 2021

@kripton, please have a look at @AndruePeters fine work. This is what I was originally trying to do on the rp2xxx branch.

@AndruePeters I'm getting a build error at this line in RF24_config.h. I still propose the idea to modify this line so that CMake will automatically adjust this when copying the file into the binary source tree. Or maybe there's a better way (I haven't tested this idea yet).

CMakeLists.txt

add_subdirectory(utility)
configure_file(RF24_config.h RF24_config.h)

link to CMake docs about configure_file cmd

RF24_config.h

#include "utility/@RF24_DRIVER@/RF24_arch_config.h"

Its hard to decide what value the RF24_DRIVER var should default to; I'm leaning toward SPIDEV since its based on the Linux kernel, but the /configure script defaults to a value based on what processor is detected or if the WiringPi, MRAA, or LittleWire libs are found.

See #642 also as the absence of the arm-linux-gnueabihf compiler nullifies some of the current CCFLAGS being set in the /configure script (exemplified in this comment's posted output).

@AndruePeters
Copy link
Author

That's an easy fix. I'll get that up before midnight probably.
Issue was forgetting to add the directory to the include list.

@AndruePeters
Copy link
Author

I can make it default to SPIDEV. The current design allows a wide range of flexibility with how RF24_DRIVER gets set. I can implement the config script's behavior from within CMake and have it also default based upon the chip.

I can fix the build error just fine like I mentioned in the previous comment. However, I don't completely follow what you mean about modifying the line for copying into the binary source tree. Can you explain?

@2bndy5
Copy link
Member

2bndy5 commented Apr 15, 2021

Can you explain?

Its preformed by CMake's configure_file cmd. Basically, it takes the 1st arg as the input file from the src dir (root folder for RF24), and it replaces any syntax @VAR_NAME@ or {VAR_NAME} with the var value when it copies the input file to the build dir. The 2nd arg is the output file name used in the build dir. All of this is explained in the CMake docs, but I got the idea reading through the CMake basic tutorial. The only thing that might be a problem is the fact that @RF24_DRIVER@ would be enclosed in quotes, but since the CMake docs boast that the configure_file cmd also works for commented defines, I hope it would also work inside quotes.

I can implement the config script's behavior from within CMake and have it also default based upon the chip

I figured as much. I think we could also make use of CMake's find_library cmd to detect if WiringPi, MRAA, or LittleWire libs are installed.

@AndruePeters
Copy link
Author

Oh. Gotcha. I don't think it's necessary here if this is the only reason for wanting it. I think it will bring more confusion that anything. The build error was mostly an oversight from my part.

Speaking of the find_library, what should be done about WiringPi? I really miss that it will be gone, but I'm about to switch away from it in my own projects. What's the official status of support for here, and should I still add it?

@2bndy5
Copy link
Member

2bndy5 commented Apr 15, 2021

That's an easy fix. I'll get that up before midnight probably.
Issue was forgetting to add the directory to the include list.

@AndruePeters The problem is that /utility/includes.h doesn't exist (on github - see gitignore). If the user never runs the /configure script, then the includes.h file never gets copied from the appropriate driver dir in /utility. So, I'm not confident that simply including the /utility dir is the right/full fix. Did I understand that fix correctly?

What's the official status of support for [WiringPi] here, and should I still add it?

Yes, RF24 lib still supports it indiscriminately (as to which unofficial fork is installed - our workflow used CoRfr/wiringPi). I remember a while back that @TMRh20 mentioned one of the network libs (RF24Gateway) doesn't support WiringPi.

I really miss that it will be gone

Its not gone; its been re-incarnated by a multitude of offshoots. It seems Gordon's attempt to retire the lib was marginally ignored 😞 .

@kripton
Copy link

kripton commented Apr 18, 2021

Current "use-cmake"-branch on top of current "rp2xxx"-branch and modified to make the two approaches (building a shared library for Linux and including RF24 into a pico-sdk based project) work: #727 (comment). Sorry for cross-posting, just wanted to show that it's possible and as a response to #676 (comment) ;)
I've decided to branch to the pico-sdk-compatible CMakeLists.txt-file in utility/rp2/ pretty early since I assumed that most checks and fancy stuff should be rather done in the user's project than in the RF24-library when using RF24 as an INTERFACE-library as the pico-sdk recommends.

By the way: nice work @AndruePeters.

@2bndy5
Copy link
Member

2bndy5 commented Apr 18, 2021

Erroneous output posted by @kripton in #727 (comment):

RF24_config.h:59:14: fatal error: utility/includes.h: No such file or directory

@AndruePeters this is the error I was referring to in #676 (comment). Although, the line number is probably off a little because its using the rp2xxx branch's RF24_config.h.

@AndruePeters
Copy link
Author

Yeah. I'll take a look at these.

Please critique this idea. I don't think that the user should necessarily have to run a configure script, and I'm trying to think of a decent design that does not require the configure script. My view is that everything should be handled from within CMake, and that the user can define the proper CMake Variables (or default to auto config) within their own project.

So, if I was in a project for some Linux project using RF24, I'd probably expect to set (RF24_RPI_DRIVER ON), or even pass this as part of building the project. My view of the use of CMake is to due away with the configure script. Obviously, this makes the CMake slightly more complicated. However, this is still doable.

I implemented CMake at my job where we have a lot of custom code generation scripts that are ran. I think we should find a way to remove the utility file completely, or just see if you'd be okay with creating a script that only creates this file. That script can be called by CMake as part of the compilation or generation process.

For the complexity of my job, a combination of both was used. For this, the latter might be sufficient. I just like removing unnecessary moving parts as needed. Thoughts?

@2bndy5
Copy link
Member

2bndy5 commented May 10, 2021

I recently looked into having 2 differently named CMakeLists.txt files (for the unreleased RF24Log lib), and it turns out CMake doesn't allow you to pick a different name for the CMakeLists.txt files. The suggested alternatives (found when googling) were:

  • use an OPTION to customize the CMakeLists.txt behavior (this is most preferred)
  • use subdirectories that each have a CMakeLists.txt file for different desired behaviors (this may seem less transparent and would involve maintaining more than 1 CMakeLists.txt file)

@kripton I like the tactic you implemented in the top-level CMakeLists.txt file (involving if (PICO_SDK_PATH) ... return()). Is there a reason to change this approach?

@kripton
Copy link

kripton commented May 10, 2021

No specific reason to change the approach. I just thought that it might be cleaner since the two styles to build/use the library are totally independent. So of course we can also leave it with the if (PICO_SDK_PATH) ... solution in the CMakeLists.txt file.

About the names: It would not matter if the name of the cmake-file for pico-sdk based projects is not CMakeLists.txt. That only matters if you actually call cmake in/for the RF24 library. For pico-sdk projects, one needs to include() the file anyways, specifying the file name: https://github.com/kripton/rp2040-dongle/blob/kripton-rf24/src/CMakeLists.txt#L14
So the name wouldn't really matter, one just has to know it.

But as I said, the solution I proposed is still perfectly fine with me :)

@2bndy5
Copy link
Member

2bndy5 commented May 11, 2021

@AndruePeters I took another stab at building the lib on your fork's "use-cmake" branch, and I got it to work by changing a coupe things.

  1. destination of copied includes.h is now in ${CMAKE_SOURCE_DIR}/utility (not ${CMAKE_BINARY_DIR}/utility). This change also nullified the target_include_directories() line in copy_includes.cmake.
  2. all #include statements that point to files within the RF24 lib now use quotes instead of angle brackets. This change had to be applied to RF24_config.h (line 53 only), and all includes.h and RF24_arch_config.h files within the RPi, SPIDEV, MRAA, wiringPi, and LittleWire driver folders.
  3. I also changed the casing for the project name as that's what's used to create symlinks. For instance, RF24 lib currently installs librf24.so, but with a project title "RF24", the symlink name(s) would be libRF24.so. Additionally, I hard-coded the RF24 lib to be SHARED, this way CMake gives us the DLL files that we are currently installing with ./configure; make; sudo make install system.

I also tested the old ./configure; make commands still worked with these changes, and they do.


Our next (and hopefully final) challenge will be writing install rules and getting the examples_linux folder to build. Currently, the ./configure; make; sudo make install paradigm creates several interlinked librf24.so files:

  • librf24.so
  • librf24.so.1
  • librf24.so.1.4
  • librf24.so.1.4.0

I haven't been able to mimic this output using CMake. Although, I don't think versioned symlinks are a detrimental requirement, but I could be wrong because of users updating their RF24 lib's installation...

Reminder: We need to run /sbin/ldconfig after installing the RF24 lib to be able to link the examples to the RF24 lib's installation (not the lib's source).

@2bndy5
Copy link
Member

2bndy5 commented May 11, 2021

I just figured out that set_target_properties(rf24 PROPERTIES SOVERSION 1 VERSION 1.4.0) will produce 3 different symlinks:

  1. librf24.so
  2. librf24.so.1
  3. librf24.so.1.4.0

Still trying to find out how to create a major+minor versioned symlink (librf24.so.1.4) in addition to the symlinks listed above...

@2bndy5
Copy link
Member

2bndy5 commented May 11, 2021

I've been toiling away at getting the examples to compile with CMake using the installed symlink(s)... I forgot about all the special compiler flags that are implemented in the /configure script. These flags should be added conditionally based on

  1. the compiler used (Library doesn't compile for ubuntu-64bit on RPi3 & RPi4 #642), concerning the compiler's "bitness" and some compiler-accepted optimization flags
  2. the CPU detected (-march -mtune -mfpu -mfloat-abi). This collides with point 1
  3. the external libs linked (i.e. mraa and some others require -pthread)

Additionally, I couldn't get the rf24UtilityDriver to compile into librf24.so (instead it kept compiling separately after librf24.so was compiled), so I had to add the additional driver source files via a list var (dubbed rf24UtilityDriverSources). We can still use the proxy INTERFACE lib rf24UtilityDriver to link against mraa, wiringPi, or littlewire-spi libs (provided they are installed). I haven't commit any work upstream yet; I'm actually working in the rp2xxx branch which has the 1st of 2 commits from the use-cmake branch of AndruePeters' fork.

@2bndy5
Copy link
Member

2bndy5 commented May 12, 2021

Also forgot to mention in my last comment that the interrupt.c sources don't compile with a C compiler because it eventually #include <stdexcept> which is a C++ std lib. When explicitly setting the LANGUAGES property as only CXX (as AndruePeters implemented), the project skipped any *.c source files (even when they were explicitly named as part of the project's source files). After switching to the implicit default value of the LANGUAGES property (set to C CXX), I ran into this problem.

The compiler traced the includes (using SPIDEV as the utility driver but also applies to RPi utility driver):
interrupt.c ➡️ interrupt.h ➡️ RF24_arch_config.h ➡️ spi.h ➡️ stdexcept

Changing the suffix from interrupt.c to interrupt.cpp and setting the LANGUAGES property to C CXX fixed this problem.

@2bndy5
Copy link
Member

2bndy5 commented May 12, 2021

ok, I've got the RF24 lib building and installing almost exactly like the current build system does. The RF24_DRIVER var is automatically set depending on if MRAA, wiringPi, or LittleWire libs' installation are found. If the Broadcomm CPU (specifically excluding RPi3 & RPi4 like the configure script) is detected, then the RF24_DRIVER defaults to the RPi driver. Otherwise RF24_DRIVER will default to the SPIDEV driver when all else fails to be detected. Additionally, I've given the examples_linux folder a separate CMake project to build the examples from the installed shared RF24 lib symlink. So far, testing has been successful! Watch out for a new workflow that tests the CMake build system for Linux platforms 😉 .

@AndruePeters Could you look over the rp2xxx branch and tell me if I did anything unconventional or wrong. The biggest modifications are in the /utility/CMakeLists.txt and the top-level CMakeLists.txt. I'm thinking about making another .cmake module to compartmentalize the CPU detection and set compiler-friendly flags accordingly (see paragraph below).

direct solution to #642

The only thing left that I want to figure out before a PR is how to determine which GNU compiler CMake has selected. If its using the arm-gnu compiler, it still shows as GNU in the CMAKE_COMPILER_ID var. However, the current configure script injects optimization flags to the compiler (which at the time assumed using the arm-gnu compiler), but these optimization flags are not accepted by the x86/x64 GNU compiler (which is currently shipped with Ubuntu when running on a RPi).

@2bndy5
Copy link
Member

2bndy5 commented May 12, 2021

🤦🏼‍♂️

I forgot about the python wrapper's install method which relies on the configure script's generated Makefile.inc for compiler flags and lib version number. The docs say that the RF24 lib needs to be fully installed (symlink and all) before installing the python wrapper, but I have my doubts about that because the setup.py file invokes the compiler via python's std libs.

Hmm

We could just mandate that the lib be built traditionally (using configure script) for installing the python wrapper until I've had time to explore pybind11 more intensively (tagging nRF24/pyRF24#3).

@2bndy5
Copy link
Member

2bndy5 commented May 19, 2021

Here is a useful link to the CMake FAQ about selecting a compiler; this is a must-read because having CMakeLists.txt force using a specific compiler (via CMake's set() cmd) is frowned upon.

@AndruePeters
Copy link
Author

Hey,

I hope I've given enough for y'all to take and work on. I've realized I currently just don't have enough time to properly work and test these changes. Engineering job + family + grad school, etc. As long as this thread isn't closed, I can follow and comment on things as needed. But, I just don't have the time to do the work (despite being the one to open the issue).

This book is a great resource on using modern CMake.

The only design critique I have was regarding the location of the utility.h file. I can't seem to find the comment addressing that. I think the best fix it to keep it in CMAKE_BINARY_DIR as it keeps a clean build that way. The fix is just to add CMAKE_BINARY_DIR to the includes for that particular target.

@AndruePeters
Copy link
Author

ok, I've got the RF24 lib building and installing almost exactly like the current build system does. The RF24_DRIVER var is automatically set depending on if MRAA, wiringPi, or LittleWire libs' installation are found. If the Broadcomm CPU (specifically excluding RPi3 & RPi4 like the configure script) is detected, then the RF24_DRIVER defaults to the RPi driver. Otherwise RF24_DRIVER will default to the SPIDEV driver when all else fails to be detected. Additionally, I've given the examples_linux folder a separate CMake project to build the examples from the installed shared RF24 lib symlink. So far, testing has been successful! Watch out for a new workflow that tests the CMake build system for Linux platforms 😉 .

@AndruePeters Could you look over the rp2xxx branch and tell me if I did anything unconventional or wrong. The biggest modifications are in the /utility/CMakeLists.txt and the top-level CMakeLists.txt. I'm thinking about making another .cmake module to compartmentalize the CPU detection and set compiler-friendly flags accordingly (see paragraph below).

direct solution to #642

The only thing left that I want to figure out before a PR is how to determine which GNU compiler CMake has selected. If its using the arm-gnu compiler, it still shows as GNU in the CMAKE_COMPILER_ID var. However, the current configure script injects optimization flags to the compiler (which at the time assumed using the arm-gnu compiler), but these optimization flags are not accepted by the x86/x64 GNU compiler (which is currently shipped with Ubuntu when running on a RPi).

I didn't see anything with a cursory glance. Take a long at my previous post on here for a good book recommendation.

@2bndy5
Copy link
Member

2bndy5 commented May 30, 2021

About integrating remote installation of the RF24 lib (from host machine to target machine)

You think it's okay if it doesn't support SSH builds at this point?

Yes, we can patch it up later. Personally, I think it's useful, but I don't like that it requires disabling security measures

link to CMake docs about cross-compiling with Linux as the host and arm as the target

@AndruePeters I think if I figure out how to specify where the linker looks for cross-compiling symlinks (& installation headers like with /usr/local/include/RF24), we can at least have a set of files to copy over remotely with ssh (probably using a separate bash script with elevated sudo privileges).

On the other hand, if we host our own PPA repo that aids in distributing RF24 libs (via apt/yum or maybe just whatever OS-specific package manager), then remotely building/installing the RF24 libs wouldn't be necessary. But I have no idea how to set up a PPA repo or installable package (like librf24.deb). Link to a tutorial about creating a deb package.

@2bndy5
Copy link
Member

2bndy5 commented May 30, 2021

@AndruePeters Is the Doxygen.cmake module more useful to end-users? It certainly isn't useful for us because

  1. we already have a Doxyfile generated, and it is uniquely tweaked to
    • avoid duplicate docs for the multiple similar utility driver sources
    • force-output some docs that are specific to non-Linux platforms
    • parse the ino files as C++ examples
  2. the Doxyfile tags set in the Doxygen.cmake module aren't consistent with our Doxyfile's tags. Although this point can be rectified, I personally don't like having to read through CMake docs about the FindDoxygen module and then cross-reference that with the (more applicable/useful) Doxygen docs.
  3. we don't use graphs, so the dot lib isn't really required
  4. (considering a project may have RF24 as a submodule) end-users shouldn't need to build the RF24 docs as part of their project's docs. Unless, the project is intentionally pinned to a certain version of RF24, but there is such a big concern among nRF24 org owners for backward compatibility that this shouldn't be a problem.

I'm leaning toward removing this Doxygen.cmake module, but I'm trying to imagine a use case for end-users (in a similar convenience that the testing/fuzzing options provide) - maybe for outputting PDF, XML, or latex.

Alternatively, we could modify the Doxygen.cmake module to execute the doxygen cmd from a cmake -D ENABLE_DOXYGEN=ON cmd, but this seems (to me) like it would be more complicated than it needs to be. This idea is basically what the PicoSDK does for building docs from CMake


Concerning the tests folder

The current /tests folder is an unused relic from the ManiacBug dev era (prior to 2014), and it is only meant for the Arduino platform. Furthermore, the /tests folder may be broken since Arduino CLI doesn't use jam files and doesn't support the pde file format. I'm mentioning this because I noticed the cmd add_subdirectory(test) for which the "test" folder doesn't actually exist... yet; I have an idea to test a dummy object of the RF24 class but that's not a priority right now.

@TMRh20 I'm removing the /tests folder (on the rp2xxx branch) to avoid confusion with the CMake implementation's optional testing mechanisms.

@2bndy5
Copy link
Member

2bndy5 commented May 30, 2021

YEAH! I got the python wrapper to install from a CMake install of the RF24 lib 💯 . I also modified setup.py to check if the RF24 lib is installed and raise an exception if the /usr/local/lib/librf24.so file isn't found. The new exception prompt will tell users to install the RF24 lib first. This should avoid some confusion, but the python3 setup.py build step now requires sudo permission. If you look at the new CMake CI steps, I've devised a way to alias the symlink for boost.python that is agnostic to the compiler/OS. This also means that we can now properly test build/install the python wrapper in the linux_build workflow (had trouble doing this with the traditional build system). The Linux & python wrapper's install instructions in the docs are updated 😉 .

Reminder: the python wrapper cannot be cross-compiled when the RF24 lib is installed with CMake.

@2bndy5
Copy link
Member

2bndy5 commented May 31, 2021

ok, I've employed a toolchain file in CMake to cross-compile the lib with ease. Currently, only the arm platform is configured for cross-compiling, but more toolchain files can easily be added with some Copy-n-Paste skills. I've also created a new "dpkg-deb" workflow to package the lib for arm platform, but it currently only compiles the lib using SPIDEV & RPi drivers. My hope is that doing it this way will allow us to distribute Debian install-able packages (for SPIDEV & RPi drivers) with new releases (& nullify the current remote install method over ssh). Distributing deb packages with MRAA, wiringPi, and LittleWire drivers is going to be a bit trickier - I'd like to distribute the built drivers as part of librf24-MRAA_arm.deb, but that may not be the best practice (IDK).

I encourage anyone to download the saved artifacts from the workflow and try installing the deb file on a RPi (examples still need to be built though)... I'm going to sleep now 💤

@2bndy5 2bndy5 linked a pull request Jun 5, 2021 that will close this issue
@2bndy5
Copy link
Member

2bndy5 commented Jun 11, 2021

@Avamander I've been looking for a CMake style guide that we can link to and best suites contributions. I like this one. Although it doesn't really talk about syntax guidelines like indentation size & where the closing parenthesis should go for multi-line cmds (CMake docs use a mix of styles). There is also an official guideline in CMake docs, but it focuses more on writing a "Find-Module" script and variable naming. Maybe we can explicitly specify 4-space indentations and refer to both docs in our CONTRIBUTING.md

@2bndy5
Copy link
Member

2bndy5 commented Aug 13, 2021

@AndruePeters The RF24\cmake\CompilerWarnings.cmake doesn't seem to have the expected affect...

I started building the new python wrappers using CMake, and the compiler warnings prevents building RF24 as a linked lib. However, compiling RF24 as a standalone lib doesn't have any warnings raised as errors (no warnings at all in fact).

The sheer amount of warnings raised as errors feel like a rude awakening (there is a lot). As it stands, I don't think the WARINGS_AS_ERRORS option should default to TRUE.

The following is 1 warning that should be ignored for cross-platform reasons

/mnt/c/Users/%USERNAME%/Documents/GitHub/pyRF24/RF24/RF24.cpp: In member function ‘void RF24::csn(bool)’:
/mnt/c/Users/%USERNAME%/Documents/GitHub/pyRF24/RF24/RF24.cpp:16:21: warning: unused parameter ‘mode’ [-Wunused-parameter]
   16 | void RF24::csn(bool mode)
      |                ~~~~~^~~~

It would be too much work to workaround this warning (which is raised as an error by default).

@AndruePeters
Copy link
Author

AndruePeters commented Aug 13, 2021

I understand that. Jason Turner and some other top C++ people recommend to treat warnings as errors. It's always been something I enable for releases, but disable for normal development. I find it gets in the way a lot, but it does force developers to submit quality code. Figure out what warnings you think should stay enabled for this code-base, and then force very PR to compile with these flags. With that said, I tend to turn it off until I'm ready for a release.

So, to address the warning:

  1. Remove the -Wunused from CompilerWarnings.cmake.
    I tend to shy away from this approach because it's helped catch several bugs.

  2. (void)parameter
    Use this approach to suppress the warnings where you want.
    This approach might be somewhat cumbersome for the initial changes, but I believe it to be the best approach.

@2bndy5
Copy link
Member

2bndy5 commented Aug 13, 2021

I think the seconds approach actually raises a separate set of warnings (definitely will raise a -Wold-style-cast but I can get around that).

I've done the first approach already, but the warning still gets raised. I think -Wpedantic may be reasserting the Wunused-parameter

@AndruePeters
Copy link
Author

AndruePeters commented Aug 13, 2021 via email

@2bndy5
Copy link
Member

2bndy5 commented Aug 13, 2021

yep that did it!

All I have left to fix are all the -Wconversion warnings.

@2bndy5
Copy link
Member

2bndy5 commented Aug 13, 2021

Well, I fixed all the warnings... I still think its strange that the warnings don't show up when RF24 lib is built alone (they only showed up when I used RF24 lib as a dependency). Furthermore, I've been linking built/installed RF24 lib to RF24Network & RF24Mesh (all using CMake now), and the warnings never showed. I did copy (& include()'d) the CompilerWarnings.cmake module you submitted to the other RF24* libs' CMake-4-Linux branches.

@AndruePeters
Copy link
Author

Are you adding project_warnings to the target_link_libraries calls for those libraries?

I hate conversion warnings. They're so important, but also a pain with most interfaces.

@2bndy5
Copy link
Member

2bndy5 commented Aug 13, 2021

Yep. Actually I had to rename the project_options & project_warnings to ${LibTargetName}_project_* so they could be more specific to the project when building more than 1 lib at a time

@2bndy5
Copy link
Member

2bndy5 commented Aug 23, 2021

I have to use c-style casts for passing a pgm_read_ptr() types to printf() on certain platforms - Teensy platforms crash when using const_cast while all other platforms (including Linux) only like const_cast. I'm disabling the extra compiler warnings until teensyduino core updates their toolchain (which is rumored to happen soon).

@AndruePeters
Copy link
Author

AndruePeters commented Aug 23, 2021 via email

@2bndy5
Copy link
Member

2bndy5 commented Aug 23, 2021

ok here is the details for the problem I came across:
deprecated-avr-comp/avr/pgmspace.h (from ArduinoCore-API - literally all boards since 2016 use this)

// no printf_P defined
#define pgm_read_ptr(addr) (*(void *const *)(addr))

avr/pgmspace.h (from Teensyduino-core for teensy3 as that's what the errors site)

#define printf_P(...) printf(__VA_ARGS__)
// ....
#define pgm_read_ptr(addr) ({ \
	typeof(addr) _addr = (addr); \
	*(void * const *)(_addr); \
})

There is some confusion in the RF24 src

  • utilities/Teensy/RF24_arch_config.h
    #define printf Serial.printf
    While the Teensyduino-core's Print.h (parent header to any Serial objects)
     // format warnings are too pedantic - disable until newer toolchain offers better...
     // https://forum.pjrc.com/threads/62473?p=256873&viewfull=1#post256873
     int printf(const char *format, ...) /*__attribute__ ((format (printf, 2, 3)))*/;
     int printf(const __FlashStringHelper *format, ...);
    And in Print.cpp
    int Print::printf(const char *format, ...)
    {
        va_list ap;
        va_start(ap, format);
    #ifdef __STRICT_ANSI__
        return 0;  // TODO: make this work with -std=c++0x
    #else
        return vdprintf((int)this, format, ap);
    #endif
    }
    
    int Print::printf(const __FlashStringHelper *format, ...)
    {
        va_list ap;
        va_start(ap, format);
    #ifdef __STRICT_ANSI__
        return 0;
    #else
        return vdprintf((int)this, (const char *)format, ap);
    #endif
    }
  • all calls in printDetails() (this where the errors originate) use
    // global variables
    static const PROGMEM char rf24_model_e_str_0[] = "nRF24L01";
    static const PROGMEM char rf24_model_e_str_1[] = "nRF24L01+";
    static const PROGMEM char * const some_debug_strings[] = {
      rf24_model_e_str_0,
      rf24_model_e_str_1,
    };
    void printDetails() {
        printf_P((char*)pgm_read_ptr(some_debug_strings[index]), ...);
    }
    While RF24_config.h conditionally defines
    #else //Everything else
        #include <Arduino.h>
    // ...
        #if !defined pgm_read_ptr || defined ARDUINO_ARCH_MBED
            #define pgm_read_ptr(p) (*(p))
        #endif

I feel like this belongs in a separate issue, but posting it here (for now). I have disabled the -Wold-style-cast only (re-enable the rest by default).

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

Successfully merging a pull request may close this issue.

4 participants