Search code examples
qtmsbuildmoc

How to make Qt automatically find files to be MOC'd


I am modifying some existing projects that uses QT (version 5.10). I am updating them to use better, more concise msbuild syntax (Target Visual Studio 2015) on windows.

One project has about 170 header files, of which about 135 header files need to be run through MOC.exe. Therefore I wrote a custom target to send 135 files to moc.exe. But the msbuild syntax to tell Moc which files to process is quite long. i.e.

        <QtMocs Include="A.h;
                    B.h;
                    C.h;
                    D.h;
                    etc...

I tried sending ALL of the header files through to moc.exe. But if a header file doesn't have Q_OBJECT, then moc.exe emits a warning about not needing to moc the header file. And to add insult to injury it still emits a cpp file, even though nothing needed to be moc'd.

So I'd like to write a nice short (one line?) concise way to tell QT to moc only the headers that are needed.

Is this possible?


Solution

  • So after two days with no response, I decided to write my own solution, which works really well. I wrote a custom task for MSBuild. It takes an array of ProjectItem's that is supposed to point to all the header files in your project. Then for each file in the array, it opens the files, searches for Q_OBJECT and if found saves off the Item into an output array. That output array is then queried later on and sent to moc.exe.

        <!-- Task to automatically discover header files that need to be run through QT's MOC.exe compiler.
         It does this by examing each file and checking if 'Q_OBJECT' is in the file. -->
    <UsingTask TaskName="FindFilesForQtMoc" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
        <ParameterGroup>
            <Files    ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
            <MocFiles ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" />
        </ParameterGroup>
        <Task>
            <Using Namespace="System" />
            <Using Namespace="System.IO" />
            <Using Namespace="System.Diagnostics" />
            <Code Type="Fragment" Language="cs">
            <![CDATA[
            var result = new List<Microsoft.Build.Framework.ITaskItem>();
            foreach(var item in Files)
            {
                String filePath = item.GetMetadata("FullPath");
                var content = File.ReadAllText(filePath);
                if (content.Contains("Q_OBJECT"))
                {
                    result.Add(item);
                }
            }
            MocFiles = result.ToArray();
            ]]>
            </Code>
        </Task>
    </UsingTask>
    

    I call the task like this:

    <FindFilesForQtMoc Files="@(ClInclude)" >
            <Output ItemName="FileForMoc" TaskParameter="MocFiles" />
    </FindFilesForQtMoc>
    <Message Text="Moc: %(FileForMoc.Identity)" />
    

    Therefore I only have to declare all my header files in my .vcxproj like this:

    <ClInclude Include="*.h" />
    

    Which is way better than explicitly declaring each and every file that needs moc.exe.