In this conclusion to a four-part series on cross-platform software development, you'll learn how to handle patches and rejects. This article is excerpted from chapter three of the book Cross-Platform Development in C++, written by Syd Logan (Addison-Wesley; ISBN: 032124642X).
So much for identifying which ﬁles to patch. The second difﬁculty you may run into is rejects. If patch is unable to perform the patch operation, it will announce this fact, and do one of two things. Either it will generate a reject ﬁle, which is a ﬁlename in the same directory as the ﬁle being patched, but with a .rej sufﬁx (for example, bar.cpp.rej), or it will place text inside of the patched ﬁle to identify the lines that it was unable to resolve. (The -dry-run option can be used to preview the work performed by patch. As the name implies, it will cause patch to do a “dry run” of the patch operation, to let you know if it will succeed, without actually changing any of the target ﬁles.)
If either of these situations happens, there are a few ways to deal with it. The ﬁrst thing I would do is remove the original source ﬁle, re-pull it from the repository using cvs update, and try to reapply the patch, in case I was applying the patch to a ﬁle that was not up-to-date with the tip. If this didn’t work, I would contact the person who generated the patch and ask that person to verify that his or her source tree was up-to-date at the time the patch ﬁle was generated. If it was not, I would ask that person to run cvs update on the ﬁle or ﬁles and generate a new patch ﬁle.
If neither of these strategies works, what happens next depends on the type of output generated by patch. If patch created a .rej ﬁle, I would open it and the source ﬁle being patched in an editor, and manually copy and paste the contents of the .rej ﬁle into the source, consulting with the author of the patch ﬁle in case there are situations that are not clear. If, on the other hand, patch inlined the errors instead of generating a .rej ﬁle, open the source that was patched and search for lines containing <<<. These lines (and ones containing >>>) delimit the original and new source changes that were in conﬂict. By careful inspection of the patch output, and perhaps some consultation with the author of the patch, you should be able to identify which portions of the resulting output should stay, and which portions of the result need to go, and perform the appropriate editing in the ﬁle to come up with the desired ﬁnal result.
Thankfully, problems like this happen only rarely. The two most common causes of conﬂict occur when a developer is accepting a patch that affects code that he or she has also modiﬁed, or the patch ﬁles are created against a different baseline. There is little to do to avoid the ﬁrst case, other than better communication among developers to ensure that they are not modifying the same code at the same time. The second case is usually are avoided when developers are conscientious about ensuring that their source trees (and patches) are consistent with the contents of the CVS repository. When this is done, problems are relatively rare, and if they do occur, usually are slight and easy to deal with.
Patch and Cross-Platform Development
Now that you have an idea of why patch is so important to open source software, and how to use it, I need to describe how patch can make developing cross-platform software easier. At Netscape, each developer had a Mac, PC, and Linux system in his or her cubicle, or was at least encouraged to have one of each platform. (Not all did, in reality.) Each developer, like most of us, tended to specialize in one platform. (There were many Windows developers, a group of Mac developers, and a small handful of Linux developers.) As a result, it would only be natural that each developer did the majority of his or her work on the platform of his or her choice.
At Netscape, to overcome the tendency for the Windows developers to ignore the Linux and Macintosh platforms (I’m not picking on just Windows developers; Macintosh and Linux developers at Netscape were just as likely to avoid the other platforms, too), it was required that each developer ensure that all changes made to the repository correctly built and executed on each of the primary supported platforms, not just the developer’s primary platform. To do this, some developers installed Network File System (NFS) clients on their Macintosh and Windows machines, and then pulled sources from the repository only on the Linux machine, to which both Mac and Windows machines had mounts. Effectively, Linux was a ﬁle server for the source, and the other platforms simply built off that source remotely. (The build system for Netscape/Mozilla allowed for this by isolating the output of builds; see Item 12.) This allowed, for example, the Windows developers to do all of their work on Windows, walk over to the Mac and Linux boxes, and do the required builds on those platforms, using the same source tree.
But what if NFS (or, Samba these days) was not available? Or, more likely, developers did not have all three platforms to build on (or if they did, have the skills needed to make use of them)? In these cases, the patch program would come to the rescue. Developers could create a patch ﬁle, for example, on their Windows machine, and then either copy it to the other machines (where a clean source tree awaited for it to be applied to), or they could mail it to a “build buddy” who would build the source for those platforms that the developer was not equipped to build for. (Macintosh build buddies were highly sought after at Netscape because most developers at Netscape did not have the desire, or the necessary skills, to set up a Macintosh development system; it was much easier to ask one of the Macintosh helpers to be a build buddy.)
Netscape’s policy was a good one, and patch was an important part of its implementation. The policy was a good one because, by forcing all check-ins to build and run on all three platforms at the same time, it made sure that the feature set of each of the three platforms moved forward at the about the same pace (see Item 1). Mozilla, Netscape, and today, Firefox, pretty much work the same on Mac, Windows, and Linux, at the time of release. The combination of cvs diff, which accurately captured changes made to a local tree, and patch, which accurately merged these changes into a second copy of the tree, played a big role in enabling this sort of policy to be carried out, and allows projects such as Mozilla Firefox to continue to achieve cross-platform parity.