arduino-cmake / Arduino-CMake-NG

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

Generate function prototypes when converting sketches #32

Open MrPointer opened 5 years ago

MrPointer commented 5 years ago

The Arduino Build-Process Specification specifies:

Prototypes are generated for all function definitions in .ino files that don't already have prototypes. In some rare cases prototype generation may fail for some functions. To work around this, you can provide your own prototypes for these functions.

In their terminology, prototypes are simply function declarations, or signatures. For example, given the following sketch file:

void setup()
{
    foo(5, 1);
}

void loop() {}

void foo(int pin, int iterations) 
{
    // Do something with arguments
}

The following prototype/declaration will be generated above the setup function in the converted source:

void foo(int pin, int iterations);

This is necessary for a source file to be valid and compile as required - Otherwise the compiler will complain it can't find declarations for the function symbol (foo in our example).

MrPointer commented 5 years ago

Optional Solution

Since a sketch may include many headers, it's not possible to simply generate prototypes for every function defined in the sketch/source file as some of them might already be declared in one of those headers. To cope with that, all headers need to be parsed, extracting all function declarations inside them. Then, each function definition in the sketch file can be compared against the list of all function declarations.

Function Signature Comparison

Function declarations don't have to name their arguments, thus a function foo declared like this:

void foo (int bar1, short bar2);

is the same as this:

void foo(int, short);

That complicates things as comparing string isn't enough to determine whether a defined function is already declared, and a smarter approach is required. Searching merely for a function name also doesn't suffice as functions might be overloaded (Arduino is C++ after all)

Functions Map-Lookup

To deal with the problems described above, a map-like data structure can be used: The key will be a function's name, and the value a list of matching overloads. The overloads themselves might be in the form of a list of argument types, as it's enough to differentiate one function declaration from another.

Then, each function defined in the sketch file will be converted to a similar form, and its name will be searched in the map as a key - If it exists, the argument list will be compared. Based on that, a decision on whether to generate a prototype/signature/declaration will be taken.

Update

CMake doesn't quite allow constructing any data structs pragmatically, mainly because of its variable scope limitations (It's extremely difficult to propagate a variable declared inside a function 2 or more levels up the call stack). Because of that, pre-mapping all functions declarations is not a feasible solution. Custom data structs could be constructed in CMake only by using properties (namely global ones), as it's the only way to handle variable scoping in pragmatically. However, for now - The lookup process should be performed for each function definition met inside a sketch file, since it wouldn't actually require to build a map struct. Later on it could be optimized to use a map to pre-map the functions.

MrPointer commented 5 years ago

Conclusion

After a long period developing this feature, It seems that CMake just isn't the right tool for this job, and there are tools better suited for this, like python. While the general algorithm isn't hard to grasp, it's painfully difficult to implement in pure CMake. Besides, the task of sketch-to-source conversion happens only once - The 1st time a sketch is added to a project. What it means is that it doesn't really matter if the conversion would be done using the framework itself, or an external tool better suited for this. After all, the framework only supports standard C/C++ source files, so the process of converting a sketch to a source is merely a utility.

Next steps

Developing an external tool, desirably in Python (As it would be cross-platform without the burden of compiling it for each architecture), should be the ultimate way to implement this feature. It would allow all kinds of optimizations, as well as perfect function argument matching, which has been the main concern in the pure CMake implementation.