Remove cmake 3.10+ warning for users of K_PLUGIN_FACTORY and K_PLUGIN_FACTORY_WITH_JSON
ClosedPublic

Authored by mlaurent on Dec 14 2017, 5:01 PM.

Details

Diff Detail

Repository
R244 KCoreAddons
Branch
remove_cmake_warning
Lint
No Linters Available
Unit
No Unit Test Coverage
mlaurent created this revision.Dec 14 2017, 5:01 PM
Restricted Application added a project: Frameworks. · View Herald TranscriptDec 14 2017, 5:01 PM
Restricted Application added a subscriber: Frameworks. · View Herald Transcript
mlaurent requested review of this revision.Dec 14 2017, 5:01 PM

Please expand the commit log. Like "Remove cmake 3.10+ warning for users of K_PLUGIN_FACTORY and K_PLUGIN_FACTORY_WITH_JSON"

KF5CoreAddonsConfig.cmake.in
10 ↗(On Diff #23923)

Remove this particular line of the comment, it makes no sense here.

mlaurent updated this revision to Diff 23928.Dec 14 2017, 5:23 PM
  • Remove line which made no sense in this module
mlaurent retitled this revision from Remove cmake warning to Remove cmake 3.10+ warning for users of K_PLUGIN_FACTORY and K_PLUGIN_FACTORY_WITH_JSON.Dec 14 2017, 5:24 PM
dfaure accepted this revision.Dec 14 2017, 9:23 PM
This revision is now accepted and ready to land.Dec 14 2017, 9:23 PM
This revision was automatically updated to reflect the committed changes.

We have a report in the wild that this broke compiling KWin with CMake 3.10.x for someone on Gentoo: https://bugs.kde.org/show_bug.cgi?id=387983

mpyne added a subscriber: mpyne.Dec 18 2017, 9:36 PM

In fact this appears to force files containing K_PLUGIN_FACTORY* into being evaluated by CMake's AUTOMOC (other warning fixes removed files from consideration by CMake AUTOMOC).

CMake itself appears to have had an interface change for AUTOMOC between 3.8 and 3.9+.

In particular, https://cmake.org/cmake/help/v3.8/manual/cmake-qt.7.html#automoc

If the macro is found in a C++ implementation file, the moc output will be put into a file named according to <basename>.moc, following the Qt conventions. The moc file may be included by the user in the C++ implementation file with a preprocessor #include. If it is not so included, it will be added to a separate file which is compiled into the target.

vs. https://cmake.org/cmake/help/v3.9/manual/cmake-qt.7.html#automoc

If the macro is found in a C++ implementation file, the moc output will be put into a file named according to <basename>.moc, following the Qt conventions. The <basename>.moc must be included by the user in the C++ implementation file with a preprocessor #include.

I'm not sure what the right answer is, or whether K_PLUGIN_FACTORY needs its output to be run through moc but if we do need moc to run then I'm not sure what's the best way to address the CMake behavior change for .cpp files.

kossebau added a subscriber: kossebau.EditedDec 18 2017, 11:49 PM

We have a report in the wild that this broke compiling KWin with CMake 3.10.x for someone on Gentoo: https://bugs.kde.org/show_bug.cgi?id=387983

Actually did it uncover some issue in kwinscripts code. Which had both a K_PLUGIN_FACTORY_DECLARATION(KcmKWinScriptsFactory) (in a file which also had a #include "module.moc") and a K_PLUGIN_FACTORY(KcmKWinScriptsFactory [...]) (in a file which had no such #include "main.moc"). Remember that K_PLUGIN_FACTORY covers both definition and declaration, so we had some duplicated declaration where only one is needed.
Milian fixed that today by dropping the duplicated K_PLUGIN_FACTORY_DECLARATION and adding a main.moc include (actually could also have removed the module.moc, no longer needed).

In D9334#180830, @mpyne wrote:

In fact this appears to force files containing K_PLUGIN_FACTORY* into being evaluated by CMake's AUTOMOC (other warning fixes removed files from consideration by CMake AUTOMOC).

CMake itself appears to have had an interface change for AUTOMOC between 3.8 and 3.9+.

In particular, https://cmake.org/cmake/help/v3.8/manual/cmake-qt.7.html#automoc

If the macro is found in a C++ implementation file, the moc output will be put into a file named according to <basename>.moc, following the Qt conventions. The moc file may be included by the user in the C++ implementation file with a preprocessor #include. If it is not so included, it will be added to a separate file which is compiled into the target.

vs. https://cmake.org/cmake/help/v3.9/manual/cmake-qt.7.html#automoc

If the macro is found in a C++ implementation file, the moc output will be put into a file named according to <basename>.moc, following the Qt conventions. The <basename>.moc must be included by the user in the C++ implementation file with a preprocessor #include.

Not sure if this is a behavioural change, or rather a fix in the docu: unless I missed something, the code in the moc file has to see (or rather the compiler compiling it) the declaration of the class with the Q_OBJECT macro, as the code consists of the definitions of those declarations done by the Q_OBJECT macro.
And if the macro is used for a declaration in a cpp file, the moc file cannot include the cpp file (otherwise the cpp definition content would be compiled 2x), so it has to be rather the cpp file which explicitely includes the moc file (after the code with the class with the Q_OBJECT declaration).

I'm not sure what the right answer is, or whether K_PLUGIN_FACTORY needs its output to be run through moc but if we do need moc to run then I'm not sure what's the best way to address the CMake behavior change for .cpp files.

Now, K_PLUGIN_FACTORY includes both the declaration (with the Q_OBJECT macro) and the definition. Which is also why it needs to be into a cpp file (unless the header is included only once). And moc needs to run the file which has K_PLUGIN_FACTORY, as it needs to create the implementation as declared from the contained Q_OBJECT macro. And put the generated code into a file. Which itself needs to be then pulled into the very cpp file with the K_PLUGIN_FACTORY,, due to the need to see the declaration, as said above.

So IMHO things are okayish as they are now and should not have really changed, besides just being less tolerant to doubled class declaration code as in kwinscripts. Which itself is questionable.

automoc in the end will stay a poor beast. It is run before the buildsystem has been created and dependencies are known, yet has to have an idea how to parse the code (despite include dirs yet unknown) to find out what classes to generate code for and how to add to the build (automoc.cpp files or the explicitly included files). The day will be great when moc no longer is needed :)

mpyne added a comment.Dec 19 2017, 2:59 AM
In D9334#180830, @mpyne wrote:

In fact this appears to force files containing K_PLUGIN_FACTORY* into being evaluated by CMake's AUTOMOC (other warning fixes removed files from consideration by CMake AUTOMOC).

CMake itself appears to have had an interface change for AUTOMOC between 3.8 and 3.9+.

In particular, https://cmake.org/cmake/help/v3.8/manual/cmake-qt.7.html#automoc

If the macro is found in a C++ implementation file, the moc output will be put into a file named according to <basename>.moc, following the Qt conventions. The moc file may be included by the user in the C++ implementation file with a preprocessor #include. If it is not so included, it will be added to a separate file which is compiled into the target.

vs. https://cmake.org/cmake/help/v3.9/manual/cmake-qt.7.html#automoc

If the macro is found in a C++ implementation file, the moc output will be put into a file named according to <basename>.moc, following the Qt conventions. The <basename>.moc must be included by the user in the C++ implementation file with a preprocessor #include.

Not sure if this is a behavioural change, or rather a fix in the docu.

It's definitely at least a behavior change. Before, CMake's AUTOMOC would generate a separate moc_foo.cpp and automatically add it to the project being built, unless the user manually added an #include "foo.moc" within the appropriate .cpp file. So the user could allow moc to generate a separate file and then have it compiled and linked separately, or include it directly in the .cpp file as used to be mandatory. From CMake 3.9 on, that is no longer true, and user code for moc files generated by AUTOMOC from classes found in a *.cpp* file now *must* be manually #include'd into that .cpp file.

There may be reasons for this, I'm not sure, but the separate-compilation model for moc has worked fine in CMake up to 3.9, even for classes being moc'd that were defined in a .cpp.

unless I missed something, the code in the moc file has to see (or rather the compiler compiling it) the declaration of the class with the Q_OBJECT macro, as the code consists of the definitions of those declarations done by the Q_OBJECT macro.

Yes, moc has to see the class that is using Q_OBJECT and, of course, generate the member function implementations for the methods added by the Q_OBJECT macro. So

And if the macro is used for a declaration in a cpp file, the moc file cannot include the cpp file (otherwise the cpp definition content would be compiled 2x), so it has to be rather the cpp file which explicitely includes the moc file (after the code with the class with the Q_OBJECT declaration).

Compiling the same file twice is certainly less efficient but it's not actually a C++ violation due to the one-definition rule (ODR). The linker gets to figure out what happens with duplicate symbols. Of course if the common subset of code in the foo.cpp compiles *differently* somehow when compiled as part of a moc_foo.cpp then that means we've introduced undefined behavior. So this is much more brittle method of use.

All the same, in code meeting our quality standards, separate moc compilation of classes defined in a .cpp *should* be nothing but a missed opportunity for optimization, not something that is impossible to make work.

Given that this did catch an error in kwinscripts, it sounds like it may be a worthy behavior change, but I still think it's a behavior change, even in code that was not technically broken.