Development resources and tips
Never worked on an open source software project before? Read Havoc Pennington's article Working on Free Software to find out what it's like.
Making changes or suggestions about the user interface? Before you do, read Havoc's article Free software and good user interfaces.
- SourceForge Project
- Browse the source code online
- Anonymous CVS access
- List of contributors - find out whom to ask about different areas of the code
- Translator's guide
If you cannot or do not want to compile TortoiseCVS yourself, you might want to try out one of the test releases. These are built by the TortoiseCVS developers fairly often from the code currently in CVS. Only one test release is kept at any time; you can find it at this site.
TortoiseCVS is an interesting program. It is the most Windows of things, an Explorer shell extension implemented as several OLE classes. It is also the most Open Source of things, with a wxWidgets interface, GPL license and because it is a CVS tool!
Because of this, it might take a small amount of effort to get it compiling and to debug it. Please persevere as it's fun and rewarding! If you get stuck then ask for help on the TortoiseCVS mailing list. Here are some pointers.
Structure: TortoiseCVS consists of two programs. TortoiseShell.dll is the Explorer shell extension. TortoiseAct.exe actually does the work - it gets launched by the shell extension for each command that you activate. You might like to look at a dependency graph of the source code.
Compiler: TortoiseCVS can be compiled with Visual C++ 8.0/9.0 or MinGW. The developers currently use Visual C++ 8.0 (also known as Visual Studio .NET 2005). Gabriel Genellina has contributed these instructions for compiling using the Express Edition of Visual C++ 9.0 (also known as Visual Studio 2008).
We use CMake (currently 2.6.2) to generate solutions. This means that you need to install CMake from here; then go to the build folder and run the runcmake script for the compiler you are using. You only need to do this once.
Patches to improve building with different compilers are welcomed. If you do get something to work better, please submit a patch to the SourceForge patch tracker.
wxWidgets: The GUI library wxWidgets is used when compiling TortoiseAct. This is a cross-platform library, but in TortoiseCVS it isn't used for that reason. It is used because it is much easier to code in than raw Win32 or MFC calls. TortoiseAct links statically to wxWidgets.
You may wish to compile the wxWidgets source code if you want to debug inside wxWidgets, but this is easy to do. When you compile wxWidgets with Visual C++, make sure you set these values (in lib/vc_lib/msw*/wx/setup.h):
wxUSE_DEBUG_NEW_ALWAYS to 0
wxUSE_MEMORY_TRACING to 0
wxUSE_GLOBAL_MEMORY_OPERATORS to 0
wxUSE_STL to 1
Otherwise wxWidgets is incompatible with templates, and adds memory debugging features that Visual C++ has already anyway. To set the values, edit the configuration file as described in the wxWidgets installation instructions.
TortoiseCVS currently uses version 2.8.9 of wxWidgets.
Flex/Bison: There is some parsing code in the cvstree folder which uses the tools Flex and Bison. You will have to download these two packages from GnuWin32 and install them in a directory named GnuWin32 located in the root directory of the drive where the TortoiseCVS source code is located (i.e. normally C:\GnuWin32).
TortoiseAct Parameters: TortoiseAct.exe takes some parameters. You can use these when you run it in the debugger. The first is the verb to execute, e.g. CVSCheckOut, CVSDiff. Following this, specify -l and the filenames to perform the action on. Alternatively, you can use -f and then specify a file. The file contains a list of the files to perform the action on, one on each line. You can also pass a window handle with -h. This is used for refreshing the Explorer view after changes, and for the properties dialog updating. Example:
CVSCheckOut -l c:\mydirectory\myfile1.cpp c:\mydirectory\myfile2.cpp.
The build script: You can build TortoiseAct.exe and TortoiseShell.dll directly using the CMake-generated solution file, but before you can build the RunTimeInstaller project, you need to run the autobuild script as described below.
Running DLL: It's not easy running a shell extension. Where possible, do
all your debugging in TortoiseAct.exe. When you change TortoiseShell.dll, you'll want
to link it directly to the place where TortoiseCVS
is installed. Unfortunately, if Explorer is already loaded you won't be
able to overwrite the DLL file. There are several ways round this.
- Read Microsoft's article Debugging with the Shell. It explains the DesktopProcess and AlwaysUnloadDll registry settings. Note that AlwaysUnloadDll is just a key, it doesn't have a value, it just needs to exist. Under NT 4.0 the DesktopProcess key broke Explorer on my machine, so double clicking on anything would curiously launch the Visual Studio Dependency Walker.
- Use Ctrl+Alt+Del or Task Manager to kill and restart Explorer. Then overwrite the DLL file and restart Explorer again. This only works on some systems - the ones where Explorer only loads TortoiseShell.dll when you first open an Explorer window, not when the Start bar appears.
- Change your shell. Edit system.ini and replace "shell=Explorer.exe" with "shell=winfile.exe" or "shell=progman.exe". When you restart, you will get part of the old Windows 3.1 shell. From here, you can launch Explorer from the File|Run menu. You can easily close it like any other application, and then change the DLL.
- Windows NT/2000: It's possible to kill Explorer from the Task Manager and it won't respawn itself. Do this, and then compile the DLL in Visual Studio. You can restart Explorer from the debugger (see below) or with the File|Run menu on Task Manager. Shift+Ctrl+Escape is a useful shortcut to open the Task Manager.
- You can use Process Explorer under NT/2000 to find out which process has the DLL still open. Often it has been loaded by the File|Open dialog of an application.
- Another trick I've had to use on Windows 2000. TortoiseShell.dll was mysteriously being held open by something; I suspect the dll loader's caching mechanism. You can solve this by renaming the file. Open up a Cygwin or DOS window, kill Explorer and then do something like mv TortoiseShell.dll not.dll. Like other proper file systems, NTFS lets you do that even if the file is in use. Then make a new TortoiseShell.dll file, and restart Explorer. This technique is very fiddly, you often have to have several differently named not*.dll files, and you should delete them whenever you can. Sometimes you get Explorer instances running the wrong version of the DLL, which can usually be solved by killing and restarting them. Persevere though, after a bit it becomes second nature. Update: Stephane Lajoie has provided this batch file to do this. It uses taskkill.exe, which is part of Microsoft's NT Support Tools. You could also use pskill.exe from the superb PsTools suite.
Shortcut key tip: In Windows NT/2000/XP you can press Shift+Ctrl+Escape to bring up Task Manager directly. You can then get quite quick at killing Explorer entirely with the keyboard.
Debugging DLL: To debug the DLL you need to use one of the methods described in running above that kills Explorer completely. When Explorer isn't running, launch it directly from Visual Studio like this: Under Project|Settings|Debug choose explorer.exe as your Executable for debug session. Then start a debug session and Explorer will launch, loading your DLL into the debugger. You can then set breakpoints and debug as normal.
Another trick that you might find useful in Visual C++ is to add an ASSERT(false) near where you want to start your debugging. When the assert dialog pops up, press Retry then Cancel and the debugger will attach. If that doesn't work, try explicitly attaching the debugger to the process before pressing Retry.
Localisation: TortoiseCVS uses GNU gettext markers to indicate text which needs translating. Whenever you add a string constant to the code, please mark it in one of the following ways:
- If it always requires translation, wrap it in the _ macro. For example, _("This is some funky text")
- If it is text that CVS has output and you are parsing, wrap it in the CVSPARSE macro, for example CVSPARSE("No CVSROOT specified!"). The language to which this text is translated depends on the version of cvs.exe in use, and moreover the text must be translated identically to in cvs.exe.
- Otherwise the string is in computerese, leave it unmarked.
If you want to build the installer, you need Cygwin installed in addition to an appropriate compiler. Then go to the top folder and run build/autobuild -h. This will show you the possible parameters you can use to customize the build process.
Note that you need to mount the TortoiseCVS folder in text mode.
The installer supports custom builds, which is a way of inserting your own modifications into the build process. To make a custom build, you must create a file named buildinfo.txt in the folder build/custom. You may also put a custom icon set in build/custom/icons. To insert additional registry entries, put them in build/custom/registry.txt (in Inno Setup format).
The following preprocessor defines are used for conditional compilation (currently these can only be set by modifying the project files):
When this is defined, various features that are not found in the TortoiseCVS builds distributed from
You are of course welcome to enable these features in your own build, but do not expect support from the TortoiseCVS team for them.
The exact features are:
- Checking out a module read-only without setting 'watch on'
Things to Do
There are quite a few things to do. Search for TODO in the source code to find some glaring holes. Or read the Bugs & Feature trackers on the SourceForge project site.
We welcome any additions or bug fixes that you make! Please remember though that TortoiseCVS is meant to have a simple interface. Options should be as unobtrusive and internally clever as possible.
Style issues: The code in TortoiseCVS stems from a diverse set of contributors. Therefore it may differ in style. These are the conventions that I (Torsten) try to enforce:
- Indentation is four spaces (no tab characters used).
- There is a space after keywords like if and the following parentheses, but there are no spaces neither before nor after the parentheses in a function call.
- Hungarian notation is not used.
- Ordinary member variables are prefixed with 'my', static member variables are prefixed with 'our'.
- Use PascalCase for classes and functions, use camelCase for variables.
- The * in a pointer type is part of the type, i.e. int* p and not int *p. The same applies for references.
- Variables are declared and initialized at their first point of use.
- Use plain 0 instead of NULL.