Uncrustify

About this project
Fork of Uncrustify for use on Project Mu and Edk2 based projects. This Fork will only contain minor changes and the goal is to get all changes upstreamed into the original project.
Languages
Wiki / Project Mu (EDK II) Fork Readme

Fork Build Status

Ubuntu GCC Debug Ubuntu GCC Release Windows VS Debug Windows VS Release
Build Status Build Status Build Status Build Status
Debug NuGet Package Release NuGet Package Debug NuGet Package Release NuGet Package

Uncrustify POC for EDK II Source Code

Uncrustify is a source code beautifier that works against C/C++ and other languages.

Skip to Using EDK II Fork Steps 🏃

If you are just interested in the steps necessary to run the EDK II fork of Uncrustify against your code, skip ahead
to the Usage Steps section.

EDK II POC Details

Current Uncrustify fork formatting results on edk2 codebase

This proof-of-concept explores the use of Uncrustify for edk2 source code. The goal being to establish a configuration
for the tool that closely adheres to the
EDK II C Coding Standards Specification . By
doing so, the tool can be used by developers to help reduce some of the tedious work involved in understanding and
applying the rules in the specification to their code. For general background on rationale behind the current set
of style guidelines and arguments for uniformity of style across the codebase, refer to the coding standards
specification.

The results from this tool should be considered a "best effort". Part of the initial POC work involves identifying
shortcomings and areas of ambiguity and deciding upon the best course of action. For example, Uncrustify is highly
customizable (as of version 0.72.0, 742 configuration options exist), eventually a scenario will have an explicit rule
defined in the Uncrustify configuration that was not explicitly defined in the coding standards specification. A
consensus must be reached about how to treat that particular scenario. In turn, the specification might be updated to
reflect this change. In other cases, a rule in the specification might not be enforceable in Uncrustify as-is. Several
options exist:

  1. Ignore the rule in Uncrustify
  2. Update the EDK II C Coding Standards Specification
  3. Submit an upstream change to Uncrustify
  4. Maintain an edk2 fork of Uncrustify with the change

Some essential changes required in the Uncrustify tool have already led to a fork of Uncrustify for edk2. That
fork is being maintained in this repository.

Ideally, after further review and testing, the changes could be upstreamed and the fork eliminated. The goal of
this fork is to provide a dedicated area for adapting Uncrustify to best support edk2 source code not to maintain
a long term deviation from the mainline.

Background

This POC began by researching open source beautifiers capable of checking and formatting C/C++ source code
based on a set of rules in a configuration file. Three final options were considered:

  1. clang-format -
    Not enough customization . The tool has active
    development but the configuration file options available at the time were severely lacking what is needed
    to come close to the EDK II C Coding Standards Specification.
  2. Vera++ -
    Not enough customization . Development of the tool was not
    very active and the limited set of customization options made this a non-starter.
  3. Uncrustify - The tool has active development and detailed customization
    options.

Repository Branching Strategy

This repository maintains the following branches:

  1. main - This is equivalent to uncrustify/master
  2. edk2/main - Contains edk2-specific changes on top of the main branch. This should be considered the "edk2" fork main.

The main branch is updated when a bug fix or feature needs to be pulled into the fork (there is not a set frequency).

To reduce maintenance overhead, these are currently the only two branches. If a strong need arises to base the edk2 changes
on Uncrustify releases, release branches will be made with the format edk2/{uncrustify_release_tag}. For example, the
Uncrustify 0.73.0 release would be maintained in the branch edk2/0_73_0.

Current State

A configuration file is present in the edk2 branch(es) of Uncrustify (etc/edk2.cfg)
that has been tweaked to get close to edk2 style. We request you return any changes that improve the file to better adhere
to the EDK II C Coding Standard so we can keep it up-to-date as the primary reference configuration file for the project.

The Uncrustify fork build can be run against Project Mu (and edk2) source code using this configuration file to produce
the expected results. Due to a new configuration file option introduced indent_func_call_edk2_style, this branch
will not work as-is with the mainline version of Uncrustify.

While there's a few minor issues, some noteworthy deficiencies are below.

Missing: Naming Convention Check

See 3.1 Naming .

Many of the naming conventions can be likely be checked with a different tool. The most common violations include:

  • Unclear variable names
  • Not defining non-standard abbreviations/acronyms in the header file
  • Not using Pascal case
  • Capitalizing acronyms (e.g. MyPCIAddress)
  • Not separating distinct words with an underscore in macro names (e.g. EACH_WORD_ISNOT_SEPARATE)
  • Usage of Hungarian notation
  • Global variables not prefixed with (g) and module variables not prefixed with (m)

The following issues in the main version of Uncrustify are what led to the fork. They are resolved in the fork but
information concerning the issues are kept here for reference.

Issues Addressed in the Uncrustify Fork

Function Call Format

See 5.2.2.4 .

Uncrustify feature request:
Feature Request: Multi-line function call argument indentation from function name · Issue #3077

  • The problem is that Uncrustify cannot indent function call arguments relative to the start of a function name.
  • Uncrustify can indent function call arguments relative to the block indentation level or the open parenthesis level
    and/or align multi-line arguments to the first argument.
  • Uncrustify can keep the first argument on the same line as the opening parenthesis or move it to the next line for
    a multi-line argument list.

Note: If the fork implementation in this repository is found to not introduce regressions, a cleaned up version
can be used to satisfy the above feature request (Issue #3077).

Special Handling for DEBUG ()

Due to the way arguments to the DEBUG () macro are substituted internally, the actual argument list is surrounded by
a pair of two opening and closing parenthesis.

This is uniquely different than the convention for other function / function macros which would normally put the
parenthesis on the next line to form a new indentation level.

  • Uncrustify cannot make an exception for the DEBUG () macro as-is.

How to Run Uncrustify

Option 1: Run in a VS Code Plugin

This option is recommended for most scenarios as it can seamlessly let Uncrustify become part of your day-to-day
development flow.

The first five steps are needed for first time setup. Once set up, running Uncrustify is very easy.

  1. Get the latest EDK II Uncrustify executable:

Download Official Fork Release

  • Uncrustify executables are built from this fork source code and published as Nuget packages to a Nuget feed
    in this Azure Devops project.

    ℹ️ Note: This Nuget feed can be used in CI or other automated environments to always pull the latest release
    from this fork.

  • To download directly, choose the build type you would like and then click the "Download" button.

    ℹ️ Note: You can click "Versions" to see all of the previously published versions.

  • Once the Uncrustify executable is downloaded to your local system you will have a .nupkg file. Several tools
    can open the file to get the executable including 7-zip. Note the file path.

Download Latest Fork Build

The instructions above get you the latest release. If you'd like the cutting edge build (which may or may not be
the same content as the latest release), you can get it directly from the build pipelines following the below
instructions:

ℹ️ Note: Go to the most recent build and click the link that says "x Published". That will lead you to the
page with the executable to download.

  • Once the Uncrustify executable is downloaded to your local system, note the file path.
  1. Download the EDK II Uncrustify configuration file from this repository.

    1. Go to etc/edk2.cfg
    2. In the upper-right corner, client the three ellipsis and select "Download" from the menu
    3. Once the configuration file is downloaded, note the file path.
  2. Install the Uncrustify VS Code extension:

    Name: Uncrustify
    Id: zachflower.uncrustify
    Description: Code format using uncrustify
    Publisher: Zachary Flower
    VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=zachflower.uncrustify

  3. Configure the Uncrustify plugin for your local setup by adding the following to your VS Code settings.json file:
    (Windows example)

    "uncrustify.configPath.windows": "path_to_your_config_file",
    "uncrustify.executablePath.windows": "path_to_your_uncrustify_executable
    
  4. Open a C source code file, Ctrl+Shift+P -> Format Document With... -> Configure Default Formatter -> Uncrustify

  5. Then, Ctrl+Shift+P -> Format Document any time you would like to format your source code file with Uncrustify

Option 2: Run in a terminal

These instructions are written for Windows 10. These activities could be further automated into a high-level script
but that has not been done yet.

  1. Generate a list of all .c and .h files recursively

    • It is recommended to run this in cleanly cloned edk2 repo without submodules to prevent submodule files
      (such as Brotli files in MdeModulePkg) from getting included in the file list. This will significantly increase
      the amount of time Uncrustify takes to run.

    • Sample Powershell command to recursively write all .c and .h files in a given package to a text file:

      Get-ChildItem -Path .\MdePkg\* -Include *.c, *.h -Recurse -Force | %{$_.fullname} | Out-File
      -FilePath .\MdePkgFiles.txt -Encoding utf8
      

    ⚠️Powershell will put the UTF-8 BOM at the beginning of the output file. Uncrustify does not recognize the BOM
    and it should be removed before passing the file as input to Uncrustify. If it is not removed, Uncrustify will
    not read the first file path in the text file properly which will cause the file to not be formatted.

  2. Run Uncrustify using the generated text file as input

    The following assume you move the EDK II Uncrustify configuration file to the directory .uncrustify in your edk2
    workspace.

    uncrustify.exe -c .\.uncrustify\edk2.cfg -F MdePkgFiles.txt --replace --no-backup --if-changed
    

    ℹ️ Note: When testing a configuration change, it is sometimes useful to run Uncrustify against a particular file
    and check the debug output to understand what rule was applied and why it was applied. The command shows an
    example of how to run the configuration file edk2.cfg against the source file VariableSmm.c where the file
    is forced to be treated as C, the debug output is written to uncrustify_debug.txt and the log severity level
    is set to "all".

    uncrustify.exe -c .\cfg\edk2.cfg -f .\MdeModulePkg\Universal\Variable\RuntimeDxe\VariableSmm.c -o output.c -l C -p uncrustify_debug.txt -L A 2>verbose_debug.txt
    

Uncrustify will update the source files in-place (with the commands given). This allows you to diff the results with
git. From here, you can iteratively tweak the configuration file and check the results until your satisfied with the
outcome.

Next Steps

We see enough potential that we would like to share the current state with others in the hope they
will be willing to further advance the work and share any improvements so we can seriously consider using Uncrustify
in the edk2 development process.

The tool itself could be used in several ways to aid developers. For example, Uncrustify could be run as a PR hook to
post an "Uncrustified" branch of the PR submitted so the author can quickly accept changes into their PR (assuming they
did not first run the tool offline). Having such an easy mechanism to format large amounts of code could more strictly
enforce consistency in edk2 while providing downstream consumers a low overhead path to style alignment with edk2.

How to integrate the tool into the edk2 development flow is something we will need to address if the edk2 project considers
adoption.

Project stats
Pipelines
0%
Builds succeeded