breathe icon indicating copy to clipboard operation
breathe copied to clipboard

Duplicate warning with breathe-apidoc

Open tdegeus opened this issue 2 years ago • 10 comments

I use breathe-apidoc as follows (I don't know if that is what was intended, I have not managed to find any documentation...):

python -m breathe.apidoc -p GooseFEM -o api _doxygen/xml

and that in my index.rst

.. toctree::
   :caption: API
   :maxdepth: 1
   :glob:

   api/*

Which works fine, accept that I'm getting a bunch of warnings such as

api/namespace/namespaceGooseFEM_1_1Tyings.rst:4: WARNING: Duplicate C++ declaration, also defined at api/class/classGooseFEM_1_1Tyings_1_1Control:4.

What can be done to prevent this?

tdegeus avatar Dec 01 '21 10:12 tdegeus

I am getting the same warnings when generating documents for a tool I built in Java. I am using Doxygen and Breathe to generate Sphinx documentation to go with documentation for a JPype wrapper in Python. I am getting these warnings when using abstract class methods and I am overriding the method in the subclass. Breathe is complaining about a duplicate declaration for each of these abstract methods since they are in the superclass and subclass both. This should not be a warning and Breathe should understand proper inheritance and virtual methods.

I also created another issue related to this one for another error message when parsing abstract class methods and can be found at #778

Caelin avatar Dec 21 '21 21:12 Caelin

I'm not sure if he issue is with what apidoc generates or with Breathe it self (or with Sphinx). If I remember correctly there are some duplicate-declaration problems when inheritance is involved. To be able to look more into this, it would be great if (both of) you can provide instructions for reproducing the problem, preferably in minimized example projects.

jakobandersen avatar Dec 23 '21 17:12 jakobandersen

I think this is related to the xml output from Doxygen. If you look carefully in the file you are going to see that Doxygen creates a <compound> for every file inside of the INPUT tag. If you are using C++ this means that you are creating a xml file for the header *.h and also for the source *.cpp, which eventually would lead to a Duplication warning from the breathe-apidoc.

There are some workaround to fix this warnings for C++ but I don't know how challenging would be to solve this problem from a Java source code.

@Caelin I am a little curious, Java has already a very robust automated documentation, why do you do not use javadoc?

Attach to this message I send an output of a index.xml Doxygen file with the problem that I am describing

<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<doxygenindex xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="index.xsd" version="1.8.17">
  <compound refid="classImage" kind="class"><name>Image</name>
    <member refid="classImage_1a5a9b26b4fb221248deb83abb666a1e21" kind="variable"><name>cols</name></member>
    <member refid="classImage_1ad0238c887b63a1d9dedab2edeca64707" kind="variable"><name>rows</name></member>
    <member refid="classImage_1aa06677ad88bf95e3ed87449786c681cb" kind="variable"><name>chan</name></member>
    <member refid="classImage_1a8c885ded00fd35077e4df3be9cc1df95" kind="variable"><name>size</name></member>
    <member refid="classImage_1ac707214a98f76a9bf7a268c10a4f16e1" kind="variable"><name>pixels</name></member>
    <member refid="classImage_1a2999c972d3a441a0af7dfd52441b983d" kind="variable"><name>ppm_type</name></member>
    <member refid="classImage_1a58edd1c45b4faeb5f789b0d036d02313" kind="function"><name>Image</name></member>
    <member refid="classImage_1adc5d9e643b449f21aa5b0d0d7dd4b80f" kind="function"><name>Image</name></member>
    <member refid="classImage_1ad480e22ff4c42487e72727de3417eb3d" kind="function"><name>Image</name></member>
    <member refid="classImage_1a3ba117b81aff6aac412ebfb9496452f2" kind="function"><name>operator[]</name></member>
    <member refid="classImage_1a0294f63700543e11c0f0da85601c7ae5" kind="function"><name>~Image</name></member>
  </compound>
  <compound refid="main_8cpp" kind="file"><name>main.cpp</name>
    <member refid="main_8cpp_1a45ba202b05caf39795aeca91b0ae547e" kind="define"><name>TIMEOUT</name></member>
    <member refid="main_8cpp_1a0ddf1224851353fc92bfbff6f499fa97" kind="function"><name>main</name></member>
  </compound>
  <compound refid="main__noHW_8cpp" kind="file"><name>main_noHW.cpp</name>
    <member refid="main__noHW_8cpp_1a45ba202b05caf39795aeca91b0ae547e" kind="define"><name>TIMEOUT</name></member>
    <member refid="main__noHW_8cpp_1a0ddf1224851353fc92bfbff6f499fa97" kind="function"><name>main</name></member>
  </compound>
  <compound refid="ppm_8cpp" kind="file"><name>ppm.cpp</name>
    <member refid="ppm_8cpp_1ab4fa15fe08bc20636c72949eeeca24ce" kind="function"><name>getHexString</name></member>
    <member refid="ppm_8cpp_1ab65a56f3b98ae30893a9e0c2dd42bf1d" kind="function"><name>readPPM</name></member>
    <member refid="ppm_8cpp_1acc6448566403e47ccb3f02a4a65a6d2b" kind="function"><name>savePPM</name></member>
    <member refid="ppm_8cpp_1acd6c96bf759e72c05352a64b1edc0e0f" kind="function"><name>savePixels</name></member>
  </compound>
  <compound refid="ppm_8h" kind="file"><name>ppm.h</name>
    <member refid="ppm_8h_1aae9749d96e15ccb4f482dd5f55d98f9b" kind="typedef"><name>BYTE</name></member>
    <member refid="ppm_8h_1ab65a56f3b98ae30893a9e0c2dd42bf1d" kind="function"><name>readPPM</name></member>
    <member refid="ppm_8h_1acc6448566403e47ccb3f02a4a65a6d2b" kind="function"><name>savePPM</name></member>
    <member refid="ppm_8h_1acd6c96bf759e72c05352a64b1edc0e0f" kind="function"><name>savePixels</name></member>
  </compound>
  <compound refid="serial_8cpp" kind="file"><name>serial.cpp</name>
    <member refid="serial_8cpp_1ab4b19dfdf208fee18951d91c59d5009c" kind="function"><name>rgbToGrayscale</name></member>
    <member refid="serial_8cpp_1ae55a803cd300fb3f3a7d5ff8be85f3c9" kind="function"><name>rgbToGrayscaleShift</name></member>
  </compound>
  <compound refid="serial_8h" kind="file"><name>serial.h</name>
    <member refid="serial_8h_1ab4b19dfdf208fee18951d91c59d5009c" kind="function"><name>rgbToGrayscale</name></member>
    <member refid="serial_8h_1ae55a803cd300fb3f3a7d5ff8be85f3c9" kind="function"><name>rgbToGrayscaleShift</name></member>
  </compound>
  <compound refid="dir_57e0317f685e46796bf0967b598c2df6" kind="dir"><name>/home/jairom/Documents/TUD_Projects/03_Semester/ESHO2/ESHO2/task_4/host/src/ppm</name>
  </compound>
  <compound refid="dir_ee75d340cb949c8d47b0901592a11d1b" kind="dir"><name>/home/jairom/Documents/TUD_Projects/03_Semester/ESHO2/ESHO2/task_4/host/src/serial</name>
  </compound>
  <compound refid="dir_68267d1309a1af8e8297ef4c3efbcdba" kind="dir"><name>/home/jairom/Documents/TUD_Projects/03_Semester/ESHO2/ESHO2/task_4/host/src</name>
  </compound>
</doxygenindex>

jrmejiaa avatar Jan 20 '22 22:01 jrmejiaa

Thanks. Just a comment the original question was about C++

tdegeus avatar Jan 21 '22 06:01 tdegeus

Hi @tdegeus yes, I know. I point out the problem for a C++ implementation with the breathe-apidoc. I tried to workaround the problem changing the Doxygen file, but most of the options does not apply to the output of the XML.

It is necessary to update the code to not use the information from the *.cpp files when there is already a .hpp file with those same functions. If you see the message that I put in the file serial.cpp has the same functions as serial.h. However, as the code of breathe-apidoc is right now, it cannot identify that those are the same implementation and shows the warning of Duplicate C++ declaration.

@jakobandersen are you a collaborator from the breathe-apidoc python file? I could help you but after some weeks because I have some deadlines to deal with in my work. If you are interested, please let me know.

jrmejiaa avatar Jan 21 '22 11:01 jrmejiaa

There are so many possible levels where the true problem originates (from Doxygen through Breathe to Sphinx it self), that it is difficult to pinpoint without getting concrete. So I think a great starting point would be if one of you can create a small example project is that reproduces the problem. I.e., something with the minimal amount of source code and rst code.

jakobandersen avatar Jan 22 '22 12:01 jakobandersen

Hi @jakobandersen

Attach to this message you will find a test_project that has the problem related to this issue. The only thing that you need to do, if you want to see the directly the warning output messages is use the cmake file that I use to build the documentation. Please be aware that the cmake from the C++ project is independent from the cmake in the folder docs.

# Go to the project
cd docs
mkdir build && cd build
cmake .. && make

The output of the HTML is going to be located in the folder html and the output from the breathe-apidoc command is located in the folder src inside of docs.

test_project.zip

jrmejiaa avatar Jan 24 '22 19:01 jrmejiaa

I'm not really familiar with this specific use-case, but based on a bit of playing around with the example, it looks like there are two problems:

  • Both .hpp and .cpp files are processed by Doxygen (and then breathe-apidoc). Changing the Doxyfile to remove *.cpp from the FILE_PATTERNS setting will make it skip those files, so only the public interface is processed.
  • The more difficult problem is that you are generating two versions of the documentation: a file-oriented view, and a class-oriented view. For example, this gives the following error:
    /test_project/docs/src/file/ppm_8hpp.rst:4: WARNING: Duplicate C++ declaration, also defined at src/class/classImage:4.
    Declaration is '.. cpp:class:: Image'
    
    This is where you hit one of the big issues, where https://github.com/michaeljones/breathe/issues/321 may be a good root for diving into all the variations of the problem (see the issues that link back to it as well). Essentially: Sphinx needs to have exactly one place for the documentation of an entity (class, function, ...). So it by definition can't be documented both in a file-oriented page and a class-oriented page. The good news is that there is the cpp:alias directive which can insert the signature of a entity somewhere, and link to it. The bad news is that we have not gotten to exploiting it in Breathe, yet.

jakobandersen avatar Feb 06 '22 11:02 jakobandersen

Yes, the file+class conflict seems to be a particularly bad one. Even using .. doxygenfile:: multiple times seems to want to repeat the anchor for a namespace. From https://raw.githubusercontent.com/celeritas-project/celeritas/master/doc/api/corecel.rst I get numerous warnings:

/Users/seth/.local/src/celeritas-temp/doc/api/corecel.rst:22: WARNING: Duplicate C++ declaration, also defined at api/corecel:20.
Declaration is '.. cpp:type:: celeritas'.
/Users/seth/.local/src/celeritas-temp/doc/api/corecel.rst:10: CRITICAL: Duplicate ID: "d2/dd1/namespaceceleritas".
/Users/seth/.local/src/celeritas-temp/doc/api/corecel.rst:10: WARNING: Duplicate explicit target name: "d2/dd1/namespaceceleritas".
/Users/seth/.local/src/celeritas-temp/doc/api/corecel.rst:29: WARNING: Duplicate C++ declaration, also defined at api/corecel:20.

Since all the functions/macros/variables/etc. that I want to document are inside an innernamespace I can't omit that section.

sethrj avatar Jan 12 '23 14:01 sethrj

I think the duplicate declaration warning for namespaces is inevitable because they're mapped to cpp:type: https://github.com/breathe-doc/breathe/blob/542ae9b9ed6e5bd5e92ba2360b094df8931ba756/breathe/renderer/sphinxrenderer.py#L251

It doesn't look like there is a Spinx entity for namespace, the cpp:namespace directive is actually for changing the current scope.

Maybe it would be better to instead just ensure the namespaces are merged into the entity names themselves?

ZedThree avatar Jan 23 '24 13:01 ZedThree