embedded software boot camp

What’s in your main() header?

Saturday, February 2nd, 2013 by Nigel Jones

One of the consequences of being in the consulting business is that I get to look at a lot of code written by other people. Usually it is necessary for me to get up to speed on the code as quickly as possible, and so to this end, one of the first things I do is look for main.c, or if it doesn’t exist the file that contains main(). Here’s what I usually find:

 Possibly a one line description.
 Legal notice. Sometimes many lines long.

That’s it. Now maybe it’s just me, but I find this a bit inadequate. Before I describe what I put in the header for main.c, I should first note my motivation. Anyone that has written code for many years realizes that the whole point of writing code is to allow it to be maintained. The person maintaining the code may be a future version of yourself, but often is some poor sod who gets thrown a bunch of code and told to get on with it. As a result, it is imperative that this future maintainer be told as much as possible about what it is they are maintaining. Now I realize that a lot of what I describe below could be described elsewhere. However, it’s my experience that Word documents and other non source code related documents tend to get lost over time (or perhaps more accurately not packaged with the source code when it is given to someone else), and so by putting this information in main.c, you pretty much guarantee that the maintainer will receive the information. With this as a background, here’s what I think should be in the header for main.c.

A product description

I typically write somewhere between 10 and 100 lines of text describing the product, what it does, how it does it, what makes it unique and the things about it that make it difficult. Note I’m not describing the code. I always find this a challenge because it forces me to really get to the core of the product. I can sometimes take many hours on this stage, as I try to refine and precis my description to include as much useful information as possible. Who has this sort of time you ask? Well if you think about it, if you can’t write a concise, yet detailed description of the product, surely you aren’t ready to start writing code? Thus if you go through this exercise and find yourself stymied, then you simply shouldn’t be sitting in front of a text editor.

Text Editor settings

Talking of text editors, the next thing you should have is an entry that describes how you have your text editor configured. I’m not interested in getting into a discussion about what your text editor settings should be – I’d just like to know what they are so that I can configure my text editor to match. This is a critical step as there is no bigger time waster than trying to understand code that looks like a disaster because you used tabs with an indentation of 2 and my editor is using an indentation of 4.

Development Environment

The text editor is of course part of the larger development environment. While it’s obvious to you what build environment you are using, it isn’t to someone else. Thus if you are using an IDE from Keil then say so. Conversely if you are from the IDE’s are evil camp and instead rely upon makefiles, well make that clear as well. Note the presence of a makefile in the source code directory does not IMHO constitute adequate documentation that this is how you intend the code to be built.

Compiler make and version

Almost every project I look at fails to make it clear what compiler make and version the code was written for. This always blows me away, because I’ve never yet seen an embedded system that doesn’t rely on compiler specific facets for it to successfully compile. Thus you should spell out exactly what compiler you used – even if you don’t think it really matters much.


If you are using libraries, particularly ones from a third party, then you should really be spelling this out and of course specifying what version of the library you used. If there are special licensing restrictions on the use of the library, then this isn’t a bad place to mention it either.

Other tools

If you use other tools, particularly code generation tools, then it would be really nice to let the reader know that your code relies upon tool X, version Y. If you are using make rather than an IDE, it would also be nice to let us all know what version of make you used.

CPU configuration

Many CPUs are configurable via fuse bits of some type (PICs and AVRs are prime examples). These configuration bits usually have a dramatic impact on how the CPU behaves, and so it is critical that you document what fuse bit settings you are assuming. It’s possible to waste many hours debugging a system that in fact has no code problems per se, but rather is simply misconfigured at the fuse bit level.

How to build

Finally, it would be really nice if you told everyone how to actually make the executable. I’m constantly amazed at the number of projects I see where either the method of building is unclear, or worse, the ‘obvious method’ (e.g. typing make) results in a build failure because prior to e.g. invoking make, it is necessary to run some batch file etc.

While I think there’s a lot more project specific information that can go in the header, I think the above is a pretty decent start. I’d be interested in hearing about other information that you put in your main.c header.



18 Responses to “What’s in your main() header?”

  1. Gauthier says:

    How about instructions to check out the latest version of the software (including the location of the main repository)? If there is anything fancy with special branches, rules for creating version numbers, I guess that would also fit here.

  2. From my experience
    – reference the archive of installation files/cd/patches/libraries on the network
    So you can rebuild the exact setup on another machine after theft/flood/breakdown.
    Always install from there and add notes on installation keys/library
    integration/settings etc. Consider archiving virtual machines
    – description
    What it tries to do, how it does it and the main problems
    what is handled by interrupts, the background process and the connection between them
    – Always configure the fuses in the source files
    Can be several pages these days, so another file is reasonable
    – Separate the #define constants/options that are likely to change
    don’t need to search for simple widening higher and lower limits, changing timeouts etc
    – one batch file to do the whole build that also saves error/size info
    So build is easy to repeat and always saves away versions,errors and size maps
    Calling the compiler twice to achieve this is fast enough and has been done!
    Evident at a later date the compiler version, size RAM/ROM used, tolerated issues/warnings!
    – version notes
    what was wrong last time, what was changed and you hope to achieve
    – zip up the whole directory so you get a snapshot in time (assuming small to modest project)
    So the directory on the new/rebuilt machine is just the same as the moment the version was made.
    Just so you don’t loose those gem files ie datasheet,table of codes,list of tests,
    trial notes… that version control systems seem to default as not required

  3. Steven Bass says:

    My response comes from a services company perspective. My previous job was at a services company developing software for medical devices. Clients might come back 5-10 years later for a change. Resurrecting a Windows 95 PC with obsolete tools was occasionally necessary. You can’t depend on using the same VCS, or being able to load the tools from the original floppy disks (assuming they could be found). Restoring a development environment and verifying we could recreate the original build could be a challenge.

    We bought a couple of identical cheap PCs, and selected one as the official build machine for a project (and used it for nothing else). At the end of the project, we pulled the hard drive, put in a clean one and used the system for the next project. (We also archived the disk image since a drive might fail after sitting on a shelf for 10 years.) So if a project came back, we knew we had compatible hardware and an environment guaranteed to be right, simply by reinstalling the saved hard drive.

    We also took all the notes from the project wiki (how to install tools, run a build, install the boot loader, debugging tricks, etc.) and appended them to the design spec at the end of the project.

    This approach works best for products where after completion no one expects to revise the software unless issues arise. Hard drives are cheap and this is low cost insurance if a change is ever needed. While we were a services company, the same issues could arise for a product company with legacy products to support.

    • Nigel Jones says:

      Well done Steven. I can’t say I am as methodical as your previous employer. However I fully understand the problem. I once had someone call me about work I had done over ten years earlier (in fact it may have been closer to 15 years). Being able to help someone in those circumstances does wonders for fostering good relations to say the least.

    • Ashleigh says:

      These days, use a big server and a VM for all that. It can then live forever.

      • Stephen Robertson says:

        We have started doing the same thing with VMs however rather than server hosted VMs we are using VMs that can be copied to a developers machine and be used with programmers/debuggers etc. One area that is not as tidy as I would like is the control of these VM images. Currently we have them stored on a backed up network share but I would much prefer some sort of source/revision control system but I have not identified a suitable system.

        Any suggestions?

        • Ashleigh says:

          I used to check VM’s into IBM/Rational ClearCase.

          I was called bat-shit crazy for doing this, but breaking the VM disk into 2GB chunks and checking it in DID take a long time.

          After that the magic of ClearCase dynamic views meant it did not need to be checked out (a long and slow copy process), you just used it – and then checked in the changes (or abandoned them) as needed.

          It might have been crazy but it worked.

          Otherwise, a NAS or similar with directories and careful access control to prevent accidentally clobbering the VM disk files would work.

  4. Lundin says:

    Personally, I tend to keep documentation in deparate documents, always placed together with the source code. In fact, I try to keep all project-related files in the same folder structure. In developer utopia, if you have one project with the internal name “Project X”, you have a project folder called “Project_X”, which consists of all documents and specs related to the project, you find the source code, where the IDE project name is “Project_X”, and you find the PCB files and BOM, also named “Project X”. I have been part of far too many projects where the project name was X, the source code project Y and the PCB was called Z, though they all referred to the same thing.

    I don’t usually have any main.h, but a similar file. My file equivalent to “main.h” contains three things:

    – a list of all basic variable types to be used in the project. Typically a pre-processor #ifdef __STDC_VERSION__ and so on checking if the compiler is C99. If it is C99, then great, just #include and . If not, a long list of typedefs follow, corresponding to those C99-specific types. uint8_t and so on.

    – a universal compiler switch similar to #define DEBUG, which I then use everywhere in the code when I want to do some “quick & dirty” debug snippet, disable the watchdog or similar. This compiler switch ensures that no such possibly dangerous code make it to the release build. Optimally a text search should be done on all source file and purge the debug switch before release. In mission-critical software, it is often a rule that no code that shouldn’t be executed is allowed to exist in the final build.

    – some sort of statement and/or variable containing the program version. And preferably also a textual list of changes to the program as whole. What changed at version 2.0.1? This should be mentioned in the source code itself. External version handling software is good, it may tell you -what- a certain change consisted of, but it won’t necessarily tell you -why-.

    As for the text editor settings, they should be made evident by the coding style standard you follow. There should be a coding style rule stating indention of either 2 or 4. There should be a rule stating, “If you indent with tab then make sure the IDE is inserting spaces”. Anyone who inserts real tab tokens in their source code should be beaten with sticks, or worse, they should be forced to read their own source code in another editor.

  5. I agree that all this information should be documented and easily found, but I do not believe that this documentation should be embedded in the source code, for the most part. After all, the building arrangements (for example) could change substantially during a project, or for a new project, without any alteration to the code at all, or with modifications completely unrelated to the building arrangements.

    A few simple links in the source code to the relevant documentation would be better, in my view, assuming the latter to be organised so that the acutal links don’t change too often.

  6. Ashleigh says:

    Heh – what you describe is what has been required for military s/w design for > 20 years – its called a Software Maintenance and Support document (SMSD), which tells you in a few paras what the s/w is, and then describes the building environment, toolset, external devices, prerequisites, and so on. Basically any and everything needed to modify and rebuild the program.

    The version information is (was) to be separately described in a Software Version Description (SVD).

    I’ve written many of those over the years. Generally a pretty thankless task.

    Putting that information in the main, or in a separate README that gets checked into version control and travels with the source code is a good move.

    • 'Tiger' Joe Sallmen says:

      I agree with the README File to capture what the author would like any future person on a project to see.

      I’m not a big fan of putting headers in the source code files that go well beyond what the particular module is supposed to do. The comments then become too cumbersome to maintain when things change.

      A README File, having links to relevant documentation that someone else pointed out, that is revision controlled, to me seems to be the way to go.

  7. David says:

    I’m all in favor of more information rather than less. I suppose that there is such a thing as too much information, but in a long career doing embedded software consulting, I never ran into that.

    I’m also in favor of putting a lot of the information suggested here into separate documents rather than in the main.c header. There’s lots of useful things that should go into function headers — meaning of parameters, meaning of return values, function side effects, possible errors or exceptions, etc. — but general information such as much of what’s described above should be on its own.

    The principal reason I believe this is that much of this information is better understood written in English. Word processors make it easy to and encourage us to write coherent documentation, complete with paragraphs, bullet points, numbered lists, headings, cross-references, tables of contents, and all the rest. Programming editors make it easy to and encourage us to write short grunts in a ‘c’-like language that doesn’t really quite qualify as prose. Further, sitting down in front of a word processor puts one in the mindset of ‘I’m taking the necessary time now to write down the information the next person is going to need’ rather than the mindset of ‘Let me dash off something quick and dirty and get back to the important work of coding.’

  8. Ralf Holly says:

    Even though I believe that the mentioned information should be close to the code, it wouldn’t put it in the code itself. Such “orthogonal” topics should live in documents of their own. Linux developers have made it a habit of putting such information in top-level files like README and INSTALL. A project probably also needs many other documents (like coding standards and architectural overview documents) so it is a very good idea to create a doc/ folder next to the src/ folder where all the project documents can go. And instead of just documenting the dependencies on particular versions of libraries and compilers/tools I would write a ./configure script that also checks that these requirements are actually met. I’m always amazed at how little embedded developers know about the time-proven best practices of the Linux/open-source community.

    • Ashleigh says:

      Sad to say but I’m horrified at the appalling lack of comments in the Linux source code.

      I don’t really hold Linux up as a shining example of embedded design, there are too many poor practices for my liking – usually starting with very poor documentation, especially down in the guts of things. There’s also a prevailing developer attitude that documentation is a waste of time as a competent person should be able to read the code.

      Things like automake and configure, etc, are very nice. For some things, some of the time. When using the likes of MPLAB, or IAR C, or other real down-n-dirty embedded compilers such tools are just not suitable or are excessively complex to set up (if you can bend them to your will at all).

      Typically it takes 30 mins to 2 hours used to write a document, update header, or write a README file that lists the prerequisite tools, their versions, where to get them, what other source dependancies are, etc.

      SOMETIMES, a very good makefile is sufficient to cover all of this. Or a makefile with some comments that describes the bits not covered anywhere else.

      Really, there’s no excuses.

      • GroovyD says:

        +1 – Linux code is a mess and most of the people who work on it and many open source projects are awful at making it easier for anyone else.

      • oliver says:

        Not sure if I entirly agree with you. The Linux source code does have the Documentation directory which does cover some ground. About the deep guts deep down, newer code is commented, internal kernel api calls have several books written on the subject. Sometimes even the git commit messages help a ton. Could it have been done better? Absolutly, there’s absolutly more room for comment improvment, but when is there not?

        If a patch has good comment, nobody is going to ‘ban’ it from the kernel. Are developers expected to try to understand the code, probably a little too much, but even starting devs get helped on the various mailing lists 🙂

        What I have generally missed however in all of these series of blog posts, what I think is one of Open Source and mostly bigger projects like Linux, is code review.

        So i’ll hijack this comment to share my 0.02 $ on that, since it is somewhat related still 🙂

        Nigel, you often speak of the code you wrote and the comments you add for either your future self or someone else. However, you are mostly relying on yourself to review your own code? While I see from your fine posts you are quite meticious in what you do, one easily overlooks ones mistakes. Code review by someone else however, is something I think is quite importan and will always catch things. And this is where I think a big project like Linux really shines. Every patch gets reviewed, often by more then one person. Is the system perfect? Nothing ever is. I’m sure patches slip through, but I feel that it is very rare.

        Anyway, this post is too long as it is 😉 And argueing wether Linux code is crap or a mess is fruitless anyway, but show me something of identical size and scale and convince me that is not a mess; then we’ll talk 🙂

  9. […] can leave a response, or trackback from your own […]

Leave a Reply