Chapter 3. The Winelib development toolkit

3.1. Winemaker

3.1.1. Support for Visual C++ projects

Winemaker supports the Visual C++ project files! Supported filetypes are .dsp, .dsw, .vcproj and .sln. It detects the defines to be used, any custom include path, the list of libraries to link with, and exactly which source files to use to build a specific target.

It is recommended to use Winemaker with a project file if available. If you have to choose between a .dsp file and a .vcproj, then you should use the .dsp, because it offers more information.

The usage is very easy, just replace the path to the source folder with the path to the project file.

$ winemaker --lower-uppercase myproject.vcproj
$ make

3.1.2. Winemaker source analysis

Winemaker can do its work even without a Windows makefile or a Visual Studio project to start from (it would not know what to do with a windows makefile anyway). This involves doing many educated guesses which may be wrong. But by and large it works. The purpose of this section is to describe in more details how winemaker proceeds so that you can better understand why it gets things wrong and how to fix it/avoid it.

At the core winemaker does a recursive traversal of your source tree looking for targets (things to build) and source files. Let's start with the targets.

First are executables and DLLs. Each time it finds one of these in a directory, winemaker puts it in the list of things to build and will later generate a Makefile in this directory. Note that Winemaker also knows about the commonly used Release and Debug directories, so it will attribute the executables and libraries found in these to their parent directory. When it finds an executable or a DLL winemaker is happy because these give it more information than the other cases described below.

If it does not find any executable or DLL winemaker will look for files with a .mak extension. If they are not disguised Visual C++ projects (and currently even if they are), winemaker will assume that a target by that name should be built in this directory. But it will not know whether this target is an executable or a library. So it will assume it is of the default type, i.e. a graphical application, which you can override by using the --cuiexe and --dll options.

Finally winemaker will check to see if there is a file called makefile. If there is, then it will assume that there is exactly one target to build for this directory. But it will not know the name or type of this target. For the type it will do as in the above case. And for the name it will use the directory's name. Actually, if the directory starts with src, winemaker will try to make use of the name of the parent directory instead.

Once the target list for a directory has been established, it will check whether it contains a mix of executables and libraries. If it is so, then it will make it so that each executable is linked with all the libraries of that directory.

If the previous two steps don't produce the expected results (or you think they will not) then you should put winemaker in interactive mode (see The interactive mode). This will allow you to specify the target list (and more) for each directory.

In each directory winemaker also looks for source files: C, C++ or resource files. If it also found targets to build in this directory it will then try to assign each source file to one of these targets based on their names. Source files that do not seem to match any specific target are put in a global list for this directory, see the EXTRA_xxx variables in the Makefile, and linked with each of the targets. The assumption here is that these source files contain common code which is shared by all the targets. If no targets were found in the directory where these files are located, then they are assigned to the parent's directory. So if a target is found in the parent directory it will also 'inherit' the source files found in its subdirectories.

Finally winemaker also looks for more exotic files like .h headers, .inl files containing inline functions and a few others. These are not put in the regular source file lists since they are not compiled directly. But it will still remember them so that they are processed when the time comes to fix the source files.

Fixing the source files is done as soon as the recursive directory traversal is completed. The two main tasks in this step are fixing the CRLF issues and verifying the case of the include statements.

winemaker makes a backup of each source file (in such a way that symbolic links are preserved), then reads it fixing the CRLF issues and the other issues as it goes. Once it has finished working on a file it checks whether it has done any non CRLF-related modification and deletes the backup file if it did not (or if you used --nobackup).

Checking the case of the include statements (of any form, including files referenced by resource files), is done in the context of that source file's project. This way winemaker can use the proper include path when looking for the file that is included. If winemaker fails to find a file in any of the directories of the include path, it will rename it to lowercase on the basis that it is most likely a system header and that all system headers names are lowercase (this can be overridden by using --nolower-include).

Finally winemaker generates the Makefile files. From the above description you can guess at the items that it may get wrong in this phase: macro definitions, include path, DLL path, DLLs to import, library path, libraries to link with. You can deal with these issues by using winemaker -D, -P, -i, -I, -L and -l options if they are homogeneous enough between all your targets. Otherwise you may want to use winemaker interactive mode so that you can specify different settings for each project / target.

For instance, one of the problems you are likely to encounter is that of the STRICT macro. Some programs will not compile if STRICT is not turned on, and others will not compile if it is. Fortunately all the files in a given source tree use the same setting so that all you have to do is add -DSTRICT on winemaker command line or in the Makefile file(s).

Finally the most likely reasons for missing or duplicate symbols are:

  • The target is not importing the right set of DLLs, or is not being linked with the right set of libraries. You can avoid this by using winemaker -P, -i, -L and -l options or adding these DLLs and libraries to the Makefile.

  • Maybe you have multiple targets in a single directory and winemaker guessed wrong when trying to match the source files with the targets. The only way to fix this kind of problem is to edit the Makefile file manually.

  • Winemaker assumes you have organized your source files hierarchically. If a target uses source files that are in a sibling directory, e.g. if you link with ../hello/world.o then you will get missing symbols. Again the only solution is to manually edit the Makefile file.

3.1.3. The interactive mode

The interactive mode will ask you to confirm or change options while winemaker analyzes your code. This might be useful when compiling MFC or in case you need different settings for each project / target. You should consider adding more options to the following example:

$ winemaker --interactive .
$ make

3.1.4. The Makefile files

The Makefile is your makefile. So this is the file to modify if you want to customize things. Here's a detailed description of its content:

SRCDIR                = .
SUBDIRS               =
DLLS                  =
LIBS                  =
EXES                  = hello.exe

This is where the targets for this directory are listed. The names are pretty self-explanatory. SUBDIRS is usually only present in the top-level makefile. For libraries and executables, specify the full name, including the .dll, .a or .exe extension. Note that these names must be in all lowercase.

### Common settings

DEFINES               = -DSTRICT
INCLUDE_PATH          =
DLL_PATH              =
DLL_IMPORTS           =
LIBRARY_PATH          =
LIBRARIES             =

This section contains the global compilation settings: they apply to all the targets in this makefile. The LIBRARIES variable allows you to specify additional Unix libraries to link with. Note that you would normally not specify Winelib libraries there. To link with a Winelib library, one uses DLL_IMPORTS. The exception is for C++ libraries where you currently don't have a choice but to link with them in the Unix sense. One library you are likely to find here is mfc.

The other variable names should be self-explanatory. There are also CEXTRA, CXXEXTRA and RCEXTRA which allow you to specify additional flags for, respectively, the C compiler, the C++ compiler and the resource compiler. Finally note that all these variables contain the option's name.

Then there is one section per target, each describing the various components that target is made of.

### hello.exe sources and settings

hello_exe_C_SRCS          = hello.c
hello_exe_CXX_SRCS        =
hello_exe_RC_SRCS         =

The above variables list the sources that are used to generate the target. Each section will start with a comment indicating the name of the target. Then come a series of variables prefixed with the name of that target. Note that the name of the prefix may be slightly different from that of the target because of restrictions on the variable names.

hello_exe_DLL_PATH        =
hello_exe_DLLS            =
hello_exe_LIBRARY_PATH    =
hello_exe_LIBRARIES       =

The above variables specify how to link the target. Note that they add to the global settings we saw at the beginning of this file.

The DLLS field is where you would enumerate the list of DLLs that executable imports. It should contain the full DLL name, but not the -l option.

hello_exe_OBJS        = $(hello_exe_C_SRCS:.c=.o) \
                        $(hello_exe_CXX_SRCS:.cpp=.o) \

The above just builds a list of all the object files that correspond to this target. This list is later used for the link command.

### Global source lists

C_SRCS                = $(hello_exe_C_SRCS)
CXX_SRCS              = $(hello_exe_CXX_SRCS)
RC_SRCS               = $(hello_exe_RC_SRCS)

This section builds "summary" lists of source files.

### Generic targets

all: $(SUBDIRS) $( $(LIBS) $(EXES)

The above first defines the default target for this makefile. Here it consists in trying to build all the targets.

### Target specific build rules

Then come additional directives to link the executables and libraries. These are pretty much standard and you should not need to modify them.