-
Notifications
You must be signed in to change notification settings - Fork 85
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
RFC: proposed rustfmt settings for the project #477
Conversation
If we harmonize on `rustfmt` for the repo, all exclusions would be removed.
One small thing I found out while reading the If this PR is accepted, we should also add to our code generators ( |
LFTM, less than that and it becomes an hamburger made of few chars.
My dayjob involves writing and reviewing lots of Go code, in which this is routine: in my opinion it makes navigating a big code file quite simpler, and I'm all for it. It also looks neat! Having project-wide, universally accepted (not necessarily loved) code formatting settings is the way to go for a huge codebase like Xous. I find code that looks uniform less hard to navigate and contribute to - this is probably due to some hints of ADHD on my side, but hey this is the world we live in :^) - so I strongly believe we should move forward with this proposal. Although, I'm not sure we should allow for any exceptions, like the 110 chars one: a project's code should all look the same if there are well-known settings. But! I'm fine even if we keep it, I just love having everything On Matrix I alluded to some strong opinions of mine, so here we go: no PR should go through if it isn't formatted correctly. One can achieve this with a combo made of The development workflow looks like this:
In case 3.b, developer is alerted through a git error message, and they just need to commit again for it to go through: |
I am relatively new to rust, and the transition into this language has been more challenging than any since I learned to code back in the dark ages. We all develop a person style over time, and mine was untenable in the context of Rust Ownership and verbose match blocks. So I have been focused on "how things are done around here" (in both Rust and xous). My Rust style is most certainly still under active development as I slowly move beyond simply surviving Rust, and start stretching my legs a little. I do have a clear preference for a consistent style within a project. Policy backed by automated formatting are the only way to go IMO. But the question is: which policy? Short answer: I think 110 columns is a sensible width for today. Musing follows .... Transparency and practical audits is a (the) central tenant of Precursor/xous. It is very important to be able to comprehend what is going on in the code, and to be able to easily review the places where unwelcome code might be hiding, and unexpected code-paths that might be taken. I find rusts match blocks both a blessing and a curse in this regard. Mixing up "normal" code with "error-handling" tends tends to result in a vertical explosion containing a minority of "normal code". On the other hand, the match blocks make it easy to appreciate all possible code-paths. All of this musing to agree that vertical is something of a problem/frustration. The diffs either side of "flag-day" do not concern me so much, because I am late to the party. So the sooner we have the "flag-day" the better, so more code is post "flag-day" Personally, I would split imports into std/core/crate sets AND force alphabetical ordering. But this is not a biggie IMO. The vertical rustfmt example (above) of the call to There are other examples in the xous code-base where the match blocks are perhaps just a bit too deep, and cause a vertical explosion with 80 col rustfmt. One answer is to add more columns; another is to break out the deeper blocks into fn calls. I have been experimenting with a "parallel" match pattern that may be another approach to the "vertical" problem (or maybe not).
Here is an extreme case - which would traditionally result in a match blocks 20 deep! |
I agree with pretty much everything above, including a "flag-day" sooner rather than later. I don't much care what the preferred style is as long as it is there and completely automated. I use "format on save" in Emacs so I can just focus on typing as fast as possible and disregard the code layout. When I finish something logical, I immediately save and let it format. I do like a longer line width than the default, and think 110 is fine to avoid the Stack of Blankness. I really just want code density. A line too long that I can't read it is annoying, but if there are only a few such lines in a file, I can focus on the important bits better. A line with almost no characters on it is worse because it waste screen real estate. I will note that sometimes breaking the rules makes sense, but there is One style that I would like for us to adopt, though I'm not sure if this can be put in rustfmt is to disallow One other things which is not specifically rustfmt, but is style related, is that I think that we should disallow rust code generation during build. Macros and whatnot are completely fine as is generating other file formats, but a build.rs which calls a command line protobuf compiler disgusts me. If you have to use such a tool, I think the appropriate thing is to commit the generated code directly, and mention how it was made. There are two reasons for this, one is auditability, the other is comprehensibility. I think currently I am the only one dealing with this, but trying to dig through other people's rust that uses such things wasted a fair bit of my time, so I'd like to not perpetuate that. |
Having a tool-supported standard code formatting policy sounds like a great idea. I don't have any strong opinions about what the details of that policy should be. My main feedback is, for whatever policy might get decided on, it would be good to consider and document how developers can apply that policy using their preferred operating system and code editor. For example, if you want to have a client-side pre-commit githook to run For documenting how to invoke rustfmt, I'm guessing that once you commit a policy to |
absorb feedback on alphabetical ordering
Thank you everyone for the productive input! Let me try to aggregate everything I've seen so far, and if I've missed a key point, please let me know.
On that last point, I will need guidance & help on tooling. This is one of my weak points. ToolingTL;DR Would it be possible to just have github actions run rustfmt and automatically reject a PR if it hasn't been formatted (and can github actions do things like strip trailing whitespaces)? And then back that up with a list of resources for making properly formatted code, starting with the simplest command-line invocation of formatting with nightly rust, and escalating into fancy automated tools and scripts you can add to your environment or editor? If the answer to the first question is yes, then could someone either help to write such a github action (could even be a PR to this PR!) or point me to an example of how it is done? Thoughts on automated tooling I am trying to wrap my head around I don't like the idea of mandating developers to run any third party program on their machines in order to contribute to the repo -- everyone has their reasons to trust or not trust a tool. If For context, I'm already a little leery of the fact that the existing So my preference is that if users wrote proper code manually, as tedious as that might be, a PR could be accepted (no need to even run fwiw, my personal flow is a vscode setting to run rustfmt on save, with a plugin to strip trailing whitespaces, and a local llama instance. So I'm closer to the latter than the former developer. Protecting MainA final side-point is that @xobs has recommended we protect the Thanks again to everyone who took the time to add their thoughts to this thread. I appreciate your feedback and I am happy to see that we seem to be converging toward consensus. |
Yes! If we decide to go with
I've been using it at various workplaces, although I understand the threat model of Xous is quite different (but maybe not that far off?). Consider also that developer can choose not to set up
Oh, imma need details on this 👀
100% agree, protecting |
I strongly agree that whatever automation should be simple enough that users on different setups don't have trouble using it, and not prescribe any particular setup, and I'm happy @bunnie feels the same. Ideally, we'd be able to explain what the tools did in such a way that you could accomplish the same things manually. As a principle example, personally hate System packages are up to a user, and we only rely on reasonable things, which is good. I love that almost everything just needs the rust toolchain and plain python, but I think our use of python could use some attention, especially if we're about to open the At a minimum, I'd like for us to at least pin the deps in a requirements.txt (or pyproject.toml) with hashes, and we should be wary of introducing while supply chain security holes so that we can get nice formatting or what not. Since pip can execute arbitrary code during install, the more random packages you include, the worse. (So, can npm (and pre-commit loves npm junk) and the js developers have a habit of writing tiny packages that do next to nothing except operating as the perfect vector for randomware after they get abandoned. Google it and find numerous examples of such shenanigans.) Python being python tends to end up with a lot of transitive deps, and using plain pip doesn't mean you get pinned version, but often deps like "whatever > 1". This isn't that big of a deal for the code running on the github CI runners since it shouldn't have any actual secrets, but I like to be careful when installing python packages on my local system. If we wanted to get a little more modern with our python tooling, I highly recommend poetry. It's as close to Cargo as you can get in python, utilizing lock files with hashes, venv management, and just generally pretty good tooling, while also not inventing it's own package format (looking at you conda). If we used poetry to manage anything that we might want for something like As someone who is far closer to the developer using "ed on a VT100 terminal over acoustic modem", (which sadly only means I'm using Emacs on a Mac and often in remote areas where I get abhorrently slow internet) I really appreciate not being forced to either install whatever operating system is currently in vogue just to contribute or spend an unreasonable amount of time trying to get things installed to match the system the developers use.
|
Assuming things may go in the direction of a server-side status check that needs to pass before new PRs can be merged, here are some background links on a possible way to go about setting up such a thing. I haven't tried this, but, from my reading, it looks like git pre-commit stuff would not be required. I think this can all be done with GitHub stuff? Stack Overflow Q&A:
GitHub Documentation:
|
I think we are converging on server-side checks to enforce rules on formatting, with it up to the user to decide how to get past the checks. And ideally a well-curated and not overwhelming list of options for newcomers to navigate the formatting hurdle. One other thing I'm going to put out there -- in terms of a "Flag Day" where we make patches/cherry picks/etc more difficult to revert due to formatting changes, my thought is to do it crate-by-crate, probably over a short period of time, rather than the whole repo all at once in a single mega-commit after running format on everything. The idea being that if we did encounter a crate that is broken by formatting changes, we can undo the formatting patch for that one crate easily. And yah, I know of at least one crate that will be broken by alphabetical ordering of modules -- the graphics server's font list depends on the order of modules to lay them out in memory correctly (the order in the file directly corresponds to link order). They are currently ordered biggest font - to - smallest font order, in part to improve L2 cache re-use by clustering all the small fonts together, and in part to make it less painful to make fixes to small font sets that are frequently used. @xobs stepped on this grenade already and we have a work-around for it that is compatible with rustfmt (using @generated, putting a space in between each module line), but, it is one solid example where rustfmt broke something we didn't expect it to. I'd love to hear any maintainer's wisdom about best practices conducting a flag day change like this. |
I made a demo repo with a .yml file that implements a nightly rustfmt check with
Misc Notes
|
Here is a shell command that could be used with
The algorithm is:
Since the perl script prints the filenames to the console, those names should end up in the status check log for the GitHub Action. So, theoretically, people could check that log to figure out which files had extra whitespace. It would work, but it wouldn't be super easy to use. Some other tool might be nice to have. |
I made another GitHub Action status check file that uses
Feel free to copy the yaml stuff from my demo repo. That's what I wrote it for. |
I think we don't need to run the full rust toolchain script, just `rustup update`? Also update actions version for checkout and comments.
this is necessary so that patches can't sneak through by modifying rustfmt.toml to bypass a CI checke.
looks like the regex is perl syntax
sorry this is all pushing to GH directly, the only way i know how to test this is to push to the branch :P so y'all get to see me flail.
maybe i'm reading the gh automation error message wrong?
OK, sorry for all the commits. The only way I know to test a github action is to...push it. This amends the PR to absorb the trailing whitespace and format checks (thanks @samblenny). As we can see, the status of this PR is a failure, because the automation is in the PR and we haven't cleaned up the repo to meet the standard yet. I'll probably try to merge this and move the repo over to meeting its own code criteria with a series of commits over the weekend...during the process we'll have a lot of automation failures, but I presume that's OK and I can still merge even if the automation fails. |
this hopefully helps new users get started with formatting. didn't want to edit the CONTRIBUTING.md file because it already has a lot of other stuff in there not specific to formatting.
also format the file, because, hey guess what...format on save is on. :P
I think that does it? |
Just to be fully transparent - I plan to merge the PR this weekend and format Xous to meet the checks within it. It will take some time for the full set of changes to roll out, a lot of iterations through CI to make sure nothing breaks. I'll try to remember to drop a note in chat once that's done. Thanks to all for your help! |
@bunnie Somehow I missed the entire discussion but I'm happy to see this. We're using a rustfmt config in our fork that excludes kernel and loader files to avoid unrelated diffs due to formatting but with formatting finally introduced over the whole codebase this problem will go away. I don't see any downsides tbh. |
Proposing a "flag day" where
rustfmt
gets run on the entire repo. It will make diffs against stuff older than the "flag day" harder, but will hopefully make patches easier in the future as we start to take on more contributors & PRs.Some background -- both @xobs and I have Opinions about formatting and they are different. For me at least, for code that I maintain, I'm particular about
rustfmt
's tendency to squish things into a small number of columns and consume lots of vertical space, which directly impacts my ability to read the code (see example below).The proposed middle-ground solution is to adopt
+nightly
rustfmt, which finally has native support invscode
. I took the template proposed in its comment thread and modified it.The primary modification is expanding the column width to 110 characters. This allows users to continue to contribute code with tall/narrow orientation if that is their preference, but doesn't eliminate wider code styles that allow better use of vertical real estate.
The rationale is that in the 90's I had SVGA computers with 132 column displays and 1/6th the screen area, so 110 characters seems like splitting the difference between 80 cols and 132 cols, allowing me to take better use of large, modern 4k displays, but potentially putting users coding primarily on mobile or small laptop screens at a disadvantage.
The other possibly controversial decision is the flag to automatically split imports into std/core/crate sets (but not forcing alphabetical ordering), under the reasoning that I tend to format my imports in logical groups and in order of attention significance, but I'm open to hearing arguments against that rationale.
Anyways, I ran these settings on the most "challenging" cases from my perspective and can live with the results.
Would love to hear opinions from all contributors (looking through commit logs -- @gsora @nworbnhoj @kotval @tmarble @rowr111 @samblenny @eupn @jeandudey, @Hoverbear maybe others I'm missing...) since the idea is we would make a
rustfmt
pass mandatory going forward, to make less formatting differences on patchsets going forward as we grow our base of contributors.I'll give a week or so for this to proposal to gain consensus, and if there aren't any major objections will move ahead; if we can't achieve consensus, then I would abandon this PR and keep with the status quo.
A direct example of the narrow-formatting problem is something like this. I would prefer code that read like this:
with default settings, rustfmt would produce this:
The original line is 91 columns wide, so
rustfmt
takes every arg including the0
's and sticks them on their own line. Unfortunately a series of0
arguments is a common idiom in Xous, and in some libraries likellio
it causes the vertical space to explode and bury more significant lines of code.Note that the
rustfmt
setting would allow users to still write code that puts0
s on lines by themselves if that is their preference; it simply doesn't force it until the line width exceeds 110 characters.I will note that the default width of this text box I'm using to enter this github comment is 135 columns.