Project Directory Organization
A recent question on Reddit’s C Programming sub asked what sort of directory structure people use for their projects. Perhaps not unsurprisingly this didn’t elicit a flood of answers - maybe there are no organizational schemes that people are happy with or perhaps few people consider it a glamorous topic (not that the C Programming subreddit is filled with glamorous people -no offense I love you all). Personally I find it to be a very interesting topic. Organization and process are two of my favorite things - a love that, while genuine, I realize makes me a bit of a nerd. Nerdy as it may be, I think that an organized project is easier and more fulfilling to work on and to this end I’ve developed an organization scheme that suffices for small projects.
No more dilly-dallying: here’s a tree diagram of my prefered directory structure (with some files for examples):
│ └── unitTest.exe
│ ├── fut.gcda
│ ├── fut.gcno
│ ├── fut.o
│ ├── unitTest.gcda
│ ├── unitTest.gcno
│ └── unitTest.o
│ ├── cov
│ │ └── fut.c.gcov
│ ├── prof
│ ├── static
│ └── test
│ └── fut.c
(Many thanks to the developers of the tree command-line utility for this lovely and easy diagram!)
The project that I’m showing you here is the project that contains some of the example unit tests from my previous article on unit tests. There’s definitely not a whole lot here, but it shows you the basics of a directory structure and has some real-world files lying around in it.
The goal behind this organizational scheme is to provide a logical, orderly structure that will make sense to a wide variety of people. Piquing the interest of developers may be easy, but drawing in and retaining developers to join in your project can depend on how easy you make it for them to contribute. Good organization goes a long way towards this. Too many open source projects (and closed-source projects alike) have poor organization and documentation. Poor documentation means that users won’t be able to use your software; poor project organization means you won’t have any developers joining you. Maintaining a logical organizational structure can lower the barriers to participation in developing your project.
Let’s take a look at each directory in the above tree individually.
The root directory of your project is a lot like the main page of a website or the first page of a book. If someone clones your repo the first thing they’re going to do is enter the directory and type ‘dir’ or ‘ls’. Do you want them to see this or this?
The first example is OpenSSL, the second, musl - I’m unfamiliar with it but it was held up as a well-organized project on the Reddit thread and I must agree.. Personally, I just about have a panic attack when I look at OpenSSL’s root directory. Yes, there are things I recognize in there (FAQ, README, etc.) but there’s some downright scary things I’m not sure I want any part of:
- install.com - Seriously? There’s a random .com file just hanging around in the root directory named install.com - begging to be double-clicked? The last time I saw a legitimate .com file, people were still discussing whether the Y2K bug would really be a problem. This feels like a virus that one of the developers got and accidentally committed to the source tree.
- e_os2.h -Okay, there’s a random header file in the root directory. For… OS/2 Warp? (for you youngins - it is was a consumer operating system back in the day) Or maybe it’s the second e_os header file they made?
- openssl.doxy - I know that .doxy is a Doxygen markup file, but there’s a whole other folder of Docs there - what’s this doing in the root?
- openssl.spec - Oh, is this the spec for OpenSSL? And here I thought they were winging it! Regardless, this is the perfect place for it (sarcasm).
Meanwhile, for everything that scares me about the OpenSSL root directory, there’s something familiar and welcoming in the musl directory:
- COPYRIGHT - This file could not be any more obvious or necessary - it describes the copyright of the code
- README - Begs to be read
- WHATSNEW - Not much with me, but if I want to know about you I’m sure I can read this file and find out
- INSTALL - I bet this contains installation instructions
- src folder - Gee, I wonder what could be in here?
It’s not necessary to beat this into the ground. OpenSSL is the designated rented mule for abuse in the open source community these days - but they actually deserve it. It’s no wonder that they couldn’t attract people to do write code, do reviews or any other useful task - the first thing they show to developers is a big ‘Abandon all hope ye who enter’ sign. musl on the other hand presents me exactly the information that I need to start figuring out what’s going on in this project. Guess who I’m going to develop for?
That’s the good, the bad, and the ugly - what should you do? You should follow my lead and have at least the following files in your root directory:
- README.md - What is your project? Why do I care? Where can I use it? How can I use it? You can’t just puke a project tree on to someone’s hard drive and tell them to go to town - everything needs a description, documentation, explanation! This is the first file that will be opened up after someone checks your your repo - tell them what to do! And also, explain your directory structure and build process. What files can they find where? Where will object files be generated? Where are unit test results stored? This file should welcome people to your project. The .md means it’s a Markdown compatible file - it can still easily be read as a text file in any editor though.
- LICENSE - If you release your code to the public, license it and put the license in this file so people know what they’re dealing with. If nothing else, use the Beerware license. If you don’t do this you will regret it. If you want people to take your code and freely use it, they’ll be too afraid to touch your code because there’s no license. If you don’t want people to use it, they’ll use it and sue you when you try to enforce your rights. You can’t win - there’s no benefit to not licensing your code, so do it and put the license in this file.
- makefile - This is by far the most important file. The project makefile is the thing that binds the whole project together and automates it. There’s way too much about makefiles to get into in in this article though (sorry to disappoint).
Any other files that are in the root directory are really up to you. The musl project had a few obvious ones that are potentially useful for a larger project:
- WHATSNEW - If you have major releases with major differences between them then it might be worthwhile to explain them in a file such as this.
- INSTALL - Yeah, if your project can be installed then installation instructions would be helpful. This doesn’t apply to every project though.
- VERSION - Looks like a hash - if you need to be able to completely and uniquely identify releases, then it makes sense have this file.
I have no qualms with musl’s directory structure but I don’t think it will apply to every project. A lot of this is not entirely necessary for smaller, personal projects. Apart from the file I mentioned above I can’t give you hard and fast rules - I can only implore you to listen to your gut and do what seems appropriate.
When it comes to subdirectories I can provide some good direction.
This is the first directory you should create. Your code shouldn’t be hanging around in the root directory - it needs its own place. Some people will say ‘Why put the code in a subdirectory? It’s the most important part of the project - put it up front and center!’ While that seems like logic it misses the fact that the root directory is like the lobby of an apartment building - yes, everyone lives in the building, but no one lives in the lobby. You’ll need space not only for code but for documentation, final outputs, test results, etc. Putting everything in the root directory is the prime organizational anti-pattern. Break the pattern by creating a ./src directory and put all of your source files in it. Don’t worry - you can make subdirectories under ./src to keep all of your source organized.
This is the second directory you need. The idea behind this folder is to store the object files that are a result of the build process. Most of the time. the object files are a mere interstitial step between source and final product. They don’t contain any real information or significance - they just are. The obj directory provides a convenient place to store files which are produced by the build process but are not the final result. In the output of tree above you’ll see not only .o files but also .gcno and .gcda files. These are files that are associated with generating coverage reports but are not the actual coverage reports themselves. That’s a common refrain for all the files that I store in the ./obj directory. I suggest you let it fill a similar role. On a side note, if you use version control you should ignore all of the files in this subdirectory - they’re just wasted space and extra confusion. They don’t contain anything that can’t be regenerated from the source.
This is one of the directories that people will be concerned with the most. The idea is that it contains your final product - generally this is an executable file or binary file of some sort. That is, of course, the reason the directory is named ‘bin’. Still, not every project is going to produce an executable file as its output - some of them might produce a compiled library instead. You could call this directory ‘./bin’ if you wanted to put executable files in there or you could just as easily call it ‘./lib’ or ‘./target’ or ‘./output’ instead and not really lose any understanding. The thing to be clear on is that this directory always contains the final product of the whole build process no matter what it is.
This is one of my favorite directories. I’m a big proponent of writing tests for your code. In my opinion, untested code is guilty until proven innocent. This directory contains all of the test cases and test procedures for your code. This could really be anything from unit tests to text-based manual test procedures. Anything goes, but if it verifies the correct operation of your code then it goes in here. In my directory structure I have it broken down into two further subdirectories: case and proc. This is a holdover from my day job in software testing: test cases are human-readable documents that show each test’s inputs and outputs where test procedures are the actual executable tests. The reasoning behind this in my line of work is largely driven by compliance issues so this approach may be overkill for a small project. You could have any sub-directory structure you wanted - maybe a ‘./test/unit’ directory for unit tests, or ‘./test/system’ for system-level tests. However your project shakes out, you can tailor the structure under ‘./test’ to meet your needs.
This is also a fun directory! (Can you tell I love this yet?) I like to do lots of analysis on my code and executables: static analysis (Splint, for example), coverage (GCov), profiling (GProf) and unit tests (Check usually). You need a place to store all of the reports of these analysis tools. To that end, I created this directory with four separate subdirectories: static, cov, prof and test to hold reports of the various types. Again, you can modify this for your usage - maybe you’d like a ‘dynamic’ subdirectory if you perform any dynamic analysis (such as with Valgrind) or you could separate the ‘test’ subdirectory into ‘unit-test’ and ‘system’ for different types of test results, or make those subdirectories under ‘test’.
I’m also a big fan of documentation, so this directory contains all of the documentation for all of the software you write. What’s the organization of your source code? How do you build your final target? How do you install your target? What is your target? How do you use it? You could answer these questions with a user’s manual or design document or Software Environment Configuration Index. You could use a wiki, a Word document, or (my favorite) a PDF generated from LaTeX and Encapsulated Postscript files derived from Graphviz files and source code littered with comments containing Doxygen markup (it’s the easiest complicated way to write design documentation ever!). Whatever you choose to write down about your project, it should go in this subdirectory.
I use a lot of miscellaneous utilities - indent, Splint, Doxygen, etc. All of them have configuration files and I hate for them to be sitting around in the root directory. Typically, I also don’t want to store them in a global location because each project will typically have its own unique settings or style. ‘conf’ provides a nice place to put all of the miscellaneous files.
I’m no fan of IDEs these days because they all seem determined to produce as many large and unnecessary files as possible: projects, workspaces, solutions, environments, etc. If possible, I like to store all of that nonsense here. I say ‘if possible’ because (much to my chagrin) it seems that most of today’s IDEs will also try to enforce their own directory structure on you. So even if you put all of the project files in the ‘proj’ directory some ‘helpful’ IDE is likely to correct you and move all of your source files, overwrite your custom makefile with a generic one or just dump a bunch of random object files wherever it pleases. If you use an IDE then try to manage clutter by putting all of the project files, workspaces or whatever here.
This project directory structure that I’ve described is not the best, not the simplest and certainly not the right answer to the question “How should I organize my project?” There is no one right answer for that question - only a lot of guidance. As with most things in life finding your right answer is a very personal journey. I can only hope to point you in the right direction and wish you good luck and a fair journey.Or, if you’re less romantic, I hope I saved you five minutes deciding what subdirectories you should create in your project folder. Enjoy!
They did standardize on a set of xml files where you give them the location of all the interesting files so that your tools and find them for you.
If you design for human consumption then a human being has to unpack your design in order to use it. If you design for machine consumption it makes things a lot easier.
To post reply to a comment, click on the 'reply' button attached to each comment. To post a new comment (not a reply to a comment) check out the 'Write a Comment' tab at the top of the comments.
Please login (on the right) if you already have an account on this platform.
Otherwise, please use this form to register (free) an join one of the largest online community for Electrical/Embedded/DSP/FPGA/ML engineers: