-
Notifications
You must be signed in to change notification settings - Fork 594
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
Fix up enum generation #544
base: master
Are you sure you want to change the base?
Conversation
@hamishwillee you may want to look at fastmavlink how i solved it there, i think conceptually more correctly and more systematically, should not be so difficult to translate to pymavlink |
@olliw42 I basically just hooked into your expand_includes() then call merge on every include. At the end clean up all the enums to add the end value. I'm fine with the algorithm - maybe should move the location of the merge code to the enum itself as that makes more sense. I'll have a look at the code thanks. In all likelihood though it is likely to take me more time to work out what you're doing and pulling it into mavgen than I have free for this. Happy to take a PR from you though. Mostly I just want @peterbarker to look at the PR and confirm that the end result is correct. |
I'm a bit confused here.
We seem to have the same definitions twice? |
You'll have the definitions in every file "above" where the MAV_CMD was defined in the dialect chain. But they should be guarded so that they are imported only once, in the top level file. Is that not what you're seeing? I.e. is that grep of one file, or showing the definitions across multiple files? |
On Thu, 17 Jun 2021, Hamish Willee wrote:
I.e. is that grep of one file, or showing the definitions across multiple files?
One file
|
This is Python right? I ask because I tested on C. Of course Python has to work too and I will look at this. |
@peterbarker Fixed for python. Identical-ish output now before and after: py_compared.zip In summary, the original parser was flawed because it removed any duplicated enums from the nested dialect objects. The merged enums were aggregated in the object for the top level dialect file, which means that including the top level C dialect file works (i.e. ardupilotmega/mavlink.h) but not the nested ones (i.e. common/mavlink.h) and makes build order of libraries relevant. Further, it meant that cases like the original python library could just include the enums from all objects - because the duplicates were already stripped from the dialects and it would just get the single copy from the top level dialect file object. My fix for C added the appropriate merged enums for each level - which means that you get the correct set for whatever dialect you include. But this means that the python generator was now appending the duplicated enums in each dialect (no longer stripped) What this does now is re-run merging the enums in the Python parser. All the duplicates get chucked out. This fix will need to be applied to anything else that relies on the defective stripping of nested object merged enums. |
So quick scan indicates that the same problem probably affects JavaScript at least. I wonder if we might be best off doing a "total" enums calculation in the top level and passing it to all the generators so they don't have to duplicate this code. Thoughts? |
@peterbarker Discussed this in the dev call with @auturgy. He raised the point, (which we are already aware of) that the fix to make C work cascades to all generators. I made the point that I did this PR to verify the defective behaviour and test our appetite for "doing the right thing". I'm not the greatest coder and I would much prefer that someone else (you ideally) took this over. If not, then I am happy to progress this but would prefer to do so with your review of the architecture or some roll out plan. We could for example create a pre-merged enum in the top level of mavgen and pass that to the generators like pymavlink to use instead of merging the individual files themselves. We'd have to update them all, but the code would be simpler. Or we could make the changes I did for C-only. The other libraries would remain the same and also be strictly compatible "at the top level". The costs would be that a) anything constructed using a nested structure like C would still be wrong for the inner nesting levels, b) inconsistent implementation and a little confusing for anyone as to how they implement a new parser. Anyway, have a chat to James and let me know how you want to progress this. |
As the author of large parts of JavaScript_NextGen, i'm here and paying attention, if i'm needed. |
That's great @davidbuzz . I think we could modify this slightly to only impact C and Python now, allowing other runtimes to modify themselves at will. But this is up to @auturgy - I can't invest more time without direction. |
So it does look like this solution will cause issues in the javascript bindings and in the c-sharp bindings. Is this just a C/C++-11 generation issue? I'm wondering if we should actually stop generating any of this as multiple include files - so if you generate from ardupilotmega.xml you only get an includefile for People probably won't bite on that one.... While I do see the problem here, and do agree that it needs fixing, we can't break random language bindings that people are happily using (or we get bug reports that have to be chased in short order)). I'll keep looking at this. |
I have reviewed the output from this pr with respect to how it affects the current JavaScript bindings, and it looks good to me. ... by example... it leaves the output derived from common.xml as-is, but it increases the size of the output of 'ardupilotmega.xml and/or 'all.xml' by the appropriate number of formerly missing enums. happy for it to be merged if others are. |
This PR should work properly (now) for anything that builds per-dialect files, and break anything that generates a single flat library (other than Python which I specifically fixed). This will show up in the flat libraries as duplicate entries. I think you're right that it is very risky to accept this, without knowing which libraries are affected. We could however make this change restricted to C and Python and then build it out to other things following more precise testing.
IMO both the parser and the generators are much more complex than they need to be because the assumption is that you build all the dialects. I am not sure whether there are any particular benefits to resulting code either. Upshot you're probably right, but it would not be me biting. I am awestruck by the cleverness of the code which I don't understand the justification for. |
Pretty please review @peterbarker |
@hamishwillee we just ran into the effects of not having this. @peterbarker can we revive this and move it forward? |
@tstastny FMI "where"/"how" did you run into this? Provided you build the top level dialect and use the top level dialect there are no issues. The only problem is when you build a dialect but then try to access the commands through a lower level generated library will you run into issues. Right now https://github.com/mavlink/c_library_v2/ should be ordered such that if you take that library the you can use any level. I have been nagging about this for years. @peterbarker What's the current story? |
@hamishwillee the issue comes when a dialect in the chain sets an enum. that means that only in the dialect header the enums are defined, so if one of the other generated headers (e.g. development) is included in some library (e.g. as in PX4 SITL..), but mavlink is built with a different dialect on the top level project (PX4), then it wont find the enums. So in that case the only solution is to set the dialect as the include path for also the potential submodule lib (is the case in PX4 SITL). But that means a bunch of forks. What would be better is if you can include any of the generated headers and still have access to the enums. Which is what I understood this PR to do.. but I could be misunderstanding. I'm probably generally very poorly explaining this. @bkueng could you help add context? |
@tstastny So what is supposed to happen is that if you build a top level dialect - say ardupilotmega.xml, you can include ardupilotmega/mavlink.h and get the enums and entities for common.xml, standard.xml, minimal.xml. If you wanted to, from that same build, you should also be able to include common/mavlink.h and get everything that was defined in standard.xml, minimal.xml (but not ardupilotmega.xml) and so on. However what happens is that if you build ardupilotmega.xml the merged enums, which include MAV_CMDs only appear in ardupilotmega/mavlink.h. If you import common/mavlink.h and ardupilotmega.xml has any MAV_CMDs then those are gone - they appear in the higher mavlink. You still get any enums that aren't merged. If you want to use the libraries now, you should just take the top level one that has all the definitions you want and include the mavlink.h for that library. If you want to use a lower level library, rebuild for that xml and use that version. The prebuilt library https://github.com/mavlink/c_library_v2/ SHOULD work correctly because it builds the high level libraries first - so they get all the enums - and then builds the lower level libraries (overlaying) so that each level has the correct enums, and so on. Testing indicates this works, but it may be that my testing wasn't good enough. Integrating this bug fix would mean that it would all work, but as I say, good to understand why it is needed (I don't from your explanation). |
@hamishwillee correct on all that you say. so the "why" in this case is particularly:
My contention is that it should not be necessary to go now down into that submodule and change to the high level dialect to make everything play nice together. But that as you say - the low-up-to-included header merged enums should be defined in all headers, such that if e.g. SITL submodule includes development/mavlink.h.. it still gets all enums development and down, despite the fact that PX4 itself was built with a higher level dialect/mavlink.h which may or may not add additional enums. I believe this is also what you are saying.. but correct me if I'm wrong |
If it includes
You're saying that you build MAVLink once for PX4 using some dialect. If that happens to be development.xml then everything works. However if you build it with all.xml then PX4_SITL will miss the headers. Right? Shouldn't PX4 SITL use the version of MAVlink you're building with? I.e. I would expect it to not be hard coded.
Correct. Though to be very precise, it will be missing any enums merged with the higher layer, such as MAV_CMDs. It will get to keep non-merged enums.
I agree. Now we have to convince @peterbarker. This fixes pymavlink and c/c++ libraries but the other libraries might still not work. I think it is worth making them break so that they get fixed, or we might make the changes conditional on particular language. Or we might throw this all away, build the library into a single monolith, and stop pretending that the libraries can be used as we expect. |
ping @peterbarker |
This attempts to fix #538.
The old code worked through each included file creating an "enum map". After parsing a file only the new enums (with respect to the map) were assigned back to the file. What this meant is that duplicated enums get stripped out of any included files. So for example if you build ardupilotmega.xml the generated common.xml does not get the MAV_CMDs - they are stripped out.
This is contrary to the intended design - you should be able to build ardupilot.xml and use the library for common.xml. It also means that the order you build files in matters.
This version does a predictable merge:
This removes some code that would re number auto-generated enum values (only) if there were clashes. Instead if there are clashes this results in duplicates being discovered. This is better because it is predictable - if autogenerated values can change then the order at which things are included might change - your values for a sub library might differ from a higher library.
Testing
Here is a comparison.zip of the libraries built with the current mavlink/mavlink generator and with the new version. Comparison shows:
@peterbarker @julianoes Can you review. I suspect the code could be better architected. Happy for you to do that, or advise on how. Note, that OllieW's fastmavlink does this similarly, but IMO more cleanly. I'm not a professional programmer though.
@auturgy FYI.
Fixes #538.