Integrating uncrustify into Visual Studio

I like clean code and I cannot lie. Visual Studio’s native code formatter is quite basic — it indents code, but doesn’t clean up spacing etc. While the Resharper extension does a great job for C#, there isn’t a good integrated tool for VC++.

Uncrustify is a command line tool that does a remarkable job on C / C++ and other languages. Running it from a command prompt (or even as an external tool) can get tedious rather quickly. At RemoteReality, we use a VS macro to launder code through uncrustify with a single keyboard shortcut.

    Sub Uncrustify()
        Dim proc As New System.Diagnostics.Process()

        If (DTE.ActiveDocument IsNot Nothing) Then
            Dim doc As Document = DTE.ActiveDocument.Object("Document")
            Dim procStart As New System.Diagnostics.ProcessStartInfo()
            Dim outputFile As String = doc.FullName + ".uncrustify"

            procStart.Arguments = "-c \rrdeV\Vperi\repos\unCrustify\rr.cfg -o " + outputFile + " -f " + doc.FullName
            procStart.FileName = "C:\Program Files\uncrustify\uncrustify.exe"
            procStart.CreateNoWindow = False
            Diagnostics.Process.Start(procStart).WaitForExit()

            Dim txt As TextDocument = DTE.ActiveDocument.Object("TextDocument")
            Dim ep As EditPoint = txt.StartPoint.CreateEditPoint

            ep.EndOfDocument()
            Dim endPoint As TextPoint = ep

            Dim sp As EditPoint = txt.StartPoint.CreateEditPoint
            sp.Delete(endPoint)
            sp.InsertFromFile(outputFile)
            System.IO.File.Delete(outputFile)

        End If
    End Sub

The macro runs uncrustify on the document currently active in the editor and sends the formatted output to a temporary file. It deletes the text in the editor and replaces it text from the generated file.

The macro and our favorite configuration files can be found here.

Automatic moc’ing in Visual Studio

A key step in Qt’s build pipeline is running the toolkit’s Meta Object Compiler to enable C++ extensions.  moc.exe runs on Qt  derived headers which generates an additional .cpp file that enable Qt extensions such as reflection, signals, etc.

For Qt projects on Windows, I prefer Visual Studio’s IDE and msbuild over Qt’s qmake. Although Qt provides a VS addin to create and manage Qt projects from within VS,  the default workflow can be rather cumbersome and I find myself editing .vcxproj files for each new header.

To automate the process, I use a VS properties (.props) file to batch moc commands. It includes a target that runs before the ClCompile target.

  <Target Inputs="@(QtIncludes)" 
          Name="Moc" 
          BeforeTargets="ClCompile" 
          Outputs="@(QtIncludes->'$(GenDir)/moc_%(Filename).cpp')">
    <Exec Command = "$(Moc) %(QtIncludes.identity) -nw -o $(GenDir)/moc_%(Filename).cpp $(MocFlags)"/>
  </Target>

For my projects, QtIncludes is any header with the .hpp extension, which I use only for Qt specific headers

  <ItemGroup>
    <QtIncludes Include="$(CppDir)/**/*.$(MocExt)"/>
  </ItemGroup>

My project sources are located in src/main/cpp. Moc outputs are located in src/gen/cpp.

  <PropertyGroup>
    <SourceDir>src/</SourceDir>
    <CppDir>$(SourceDir)main</CppDir>
    <GenDir>$(SourceDir)gen/cpp</GenDir>
  </PropertyGroup>

  <PropertyGroup>
    <QtDir>c:\qt\4.8.0\</QtDir>
    <MocExt>hpp</MocExt>
    <Moc>$(QtDir)/bin/moc.exe</Moc>
  </PropertyGroup>

The props file contains  additional housekeeping targets that create the generated files directory and copy moc outputs to src/gen/cpp

<Target Name="CreateDirectories" BeforeTargets="Moc">
    <MakeDir
        Directories="$(GenDir)"/>
  </Target>

  <Target Inputs="@(CopyToOutput)"
          Name="CopytoOut"
          Outputs="@(CopyToOutput->'%(DestinationFolder)/%(RecursiveDir)%(Filename)%(Extension)')"
          AfterTargets="Link">
  </Target>

The full props file an be found here. Include the contents in Microsoft.Cpp.Win32.user.props for global use. For a single project, add a reference in the project’s .vcxproj

<ImportGroup Condition="'$(Configuration)|$(Platform)'=='xxx|yyy'" Label="PropertySheets">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
    <Import Project="Moc.props" />
</ImportGroup>