Blog

Requirements Traceability with Doxygen

Published:

Sometimes it would be nice to look at our code and know exactly which requirement(s) it is intended to satisfy. We can of course add comments refering to requirements, and if we put those in Doxygen comments, they'll show in our Doxygen. But what if we want to link those directly in to our requirements management system? Doxygen offers no built-in way to link to requirements. It does, however, provide a means of creating custom Doxygen commands using ALIASES and \xrefitem. Let's try it out. In our Doxyfile we add:

ALIASES += "requirement=\xrefitem requirements \"Requirement\" \"Requirements\""

With this, Doxygen will create a new "Related Page" titled "Requirements", and list all the requirements we reference in the code. Now in our code we can do something like:

/**
 * ANSI escape codes namespace.
 */
namespace ansi {

/**
 * Reset any styles applied
 * @requirement ANSI-002 reset styles
 * @param os Ostream to reset styles on.
 * @return Reference to os.
 */
template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& reset( std::basic_ostream<CharT, Traits>& os ) {
	os << "\033[0m";
	return os;
}

/**
 * Set foreground color to red.
 * @requirement ANSI-003 red text
 * @param os Ostream to set text color on.
 * @return Reference to os.
 */
template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& fgred( std::basic_ostream<CharT, Traits>& os ) {
	os << "\033[31m";
	return os;
}

} // namespace ansi

When we run Doxygen now, we see a new "Requirements" page:

And on the function descriptions:

That's a good start, but we can take it a step further and link those to our requirements management tool. Let's say we're using Doorstop, we can change the Doxygen alias a little:

ALIASES += "requirement{1}=\xrefitem requirements \"Requirement\" \"Requirements\" <a href=\"http://127.0.0.1:7867/documents/ANSI#\1\">\1</a>""

This unfortunately requires a slightly different syntax in the Doxygen comments:

/**
 * ANSI escape codes namespace.
 */
namespace ansi {

/**
 * Reset any styles applied
 * @requirement{ANSI-002} reset styles
 * @param os Ostream to reset styles on.
 * @return Reference to os.
 */
template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& reset( std::basic_ostream<CharT, Traits>& os ) {
	os << "\033[0m";
	return os;
}

/**
 * Set foreground color to red.
 * @requirement{ANSI-003} red text
 * @param os Ostream to set text color on.
 * @return Reference to os.
 */
template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& fgred( std::basic_ostream<CharT, Traits>& os ) {
	os << "\033[31m";
	return os;
}

} // namespace ansi

Now when we run Doxygen, we get links directly to doorstop:

Linking our requirements management tool back to Doxygen is more difficult. First, it will have to be tool-specific. Second, Doxygen doesn't use our requirement IDs as its anchors. We can probably write a script to parse the Doxygen output and create the appropriate links in Doorstop, but that is a problem for another day.