Andrew Eikum a
CodeWeavers employee and Wine developer is writing a series of post to introduce people to Wine usage and development.
About This Guide
This is a series of guides intended to introduce software developers
to the Wine ecosystem. It will cover what Wine is, how to use Wine, how
to debug Wine, how to fix Wine, and what to do with your fix once you've
made it.
The guide will be published throughout January.
- Part 1 describes what Wine is and provides a short description of various popular forks of Wine.
- Part 2 describes Wine's build process.
- Part 3 describes how to use Wine as a developer.
- Part 4 describes how to debug Wine in general.
- Part 5 describes Wine's source tree layout and how to edit the source.
- Part 6 describes how you can send your work upstream.
Once you've got some idea of what is causing the problem with your
application, it's time to go understand how those APIs are implemented
in Wine so you can fix it. You may also need to change Wine's code to
debug the application in the first place.
Search for existing bugs
Before digging into the source, it's always useful to first search for existing bugs. You can search Wine's Bugzilla
for your application, or for the symptoms you're seeing. As you begin
to debug your issue, you may find other search terms to try. If you find
a bug, you may find some useful analysis or work has already been done
for the issue.
You may also want to look through the Wine Staging patches,
especially once you have some idea of what the problem is. You may find
someone has already written a patch to fix this problem, but it isn't
ready to go upstream yet. You can pick up the torch and try to upstream
the work.
Wine source layout
Wine implements the Windows operating system, which is composed of
libraries, programs, and kernel functionality. You can find the code for
Wine's implementation of each of those components in
dlls
,
programs
, and
server
,
respectively. To give you some idea of how different components of Wine
work together, some important, core components are listed here.
dlls/ntdll
– This is where many of the core OS APIs
are implemented, like file handling, thread creation and
synchronization, timing, and much more. Applications typically use kernel32
instead of ntdll
directly.
dlls/kernel32
– This is the application-facing interface for the core ntdll
APIs mentioned above.
dlls/user32
– This is where much of the Windows GUI handling lives, like window creation, message handling, terminal services, and so on.
dlls/winex11.drv
and dlls/winemac.drv
– These libraries map the Windows GUI interfaces, and some other platform-specific functions, to the native platform. user32
calls into these platform drivers.
dlls/d3d*
and dlls/wined3d
– These libraries implement the Direct3D graphics API on top of the platform's OpenGL implementation.
programs/services
– This program manages background services, both those created by Wine and those provided by installed applications.
programs/wineboot
– This program kicks off initial prefix creation and other tasks when a prefix is booted.
programs/winecfg
– This is Wine's configuration program.
server
– The Wine server implements all
cross-process functionality, including message routing, multi-process
synchronization primitives, registry handling, and much more. The Wine
server is always running if an application is running.
While debug channels are often named after the component in which they are declared (e.g. the
dsound
channel is declared in
dlls/dsound
), this is not always the case. You can use
git grep
to find the relevant debug trace line if it isn't obvious.
Writing tests
In most cases, your work should be driven by tests. Study the
documentation for the relevant APIs and write tests first to confirm
that Windows's implementation behaves how you expect it to. Then, change
Wine's source to pass the tests that you wrote.
Wine has an extensive suite of unit tests. The unit tests comprise
well over one million lines of code as of this writing. Any patch you
submit
must pass all of these tests. Patches
should contain more tests proving that the new behavior is correct.
In general, you should focus your work on what a real application
actually does. That is, you don't need to implement a bunch of unrelated
functionality if the application doesn't actually need it. You should
write tests to show how an application is using an API, and how Wine
fails to meet its expectations. Then fix Wine to also pass those tests
without breaking any existing tests. Any change to Wine has the
potential to break other applications. Simply passing tests that you
wrote is insufficient reason to change Wine.
You can find the tests for a given component in the
tests
subdirectory. For example, the tests for
dsound
live in
dlls/dsound/tests
. You will find
.c
files in that directory. The tests start in the
START_TEST
function and exercise the component being tested. The
ok
function demonstrates correct behavior. Again, "correct" is defined to mean "like Windows." For example:
hr = IDirectSound_CreateSoundBuffer(ds, &bufdesc, &primary, NULL);
ok(hr == S_OK, "CreateSoundBuffer failed: %08x\n", hr);
Here you can see the return value from
IDirectSound::CreateSoundBuffer
is expected to be
S_OK
. If it is not
S_OK
, then the test will fail.
Your tests should demonstrate how your application behaves when
interacting with that API. Since you are fixing a bug in Wine's
implementation of the API, your tests should demonstrate that Wine will
fail without your fix. It isn't necessary, or even recommended, to fully
exercise all inputs to an API in the Wine tests. Instead, be thoughtful
about how your application might use the API given different inputs
from the user, and write tests that reflect those possibilities.
Your tests should be self-contained. They should initialize the
necessary functions, test the APIs, and then clean up before continuing.
If you need to create files on disk, create them in a temporary
location and/or clean them up afterwards. If your test may leave changes
to the system, expect to have to clean up after a previous run that
crashed, before you run your tests.
After you have written some tests, you need to verify that they pass when run on Windows. To do this locally, run
make crosstest
,
which will use your system's mingw-w64 compiler to build a Windows
executable. Run this executable on Windows in the command prompt to
verify Windows's behavior. If you do not have a Windows system or VM
available, you can upload your patch or test binary to the Wine Test Bot.
Once your tests pass on Windows, run them in Wine with
make test
.
If your tests meaningfully demonstrate a Wine bug, they should fail.
Now it is time to fix Wine to pass those tests, as well as all existing
tests.
Working with Wine's source
As you develop your fix, there are some rules you should know if you intend to send your work upstream.
For better or worse, Wine does not have a coding style standard and
likely never will. The rule is to try to match the surrounding code as
best as possible. Especially in older code, you will find a horrible
mish-mash of tabs and spaces and brace styles. Do your best to make the
code no worse than it was. However, extraneous changes are discouraged
as they make review difficult and looking up the source history in Git
more cumbersome. If you are changing a line, or even one line in a short
code block, it can be re-formatted to be less ugly than it was. But
don't reformat an entire function to fit some style, no matter how ugly
it is.
Wine only accepts code that adheres to the C89 standard, because some
compilers that Wine cares about don't support anything later. The three
most common snags here are that your code must declare its variables at
the top of the block, you cannot use
//
-style comments, and function declarations with no arguments must be declared with
void
arguments:
int some_function(void);
Resist the urge to gut an entire function or module and re-write it.
Small, discrete changes that can be easily understood are the right way
to fix Wine. Wine is more than two decades old. There is a lot of
hard-won knowledge in much of Wine's source, and throwing out all of
that history because it's easier than working in the existing code is
not likely to pass review.
Make your changes as obviously correct as possible. Unrelated changes
must be placed into their own commits. You should not introduce unused
code in an early patch, which begins to be used in a later patch.
Write your patches with the reviewer in mind. It may be unintuitive,
but understand that it is more important for your patch to be easy to
review than make Wine's code perfect. Reviewer time is one of Wine's
most valuable assets. Patches that are easier to review are more likely
to pass that review.
Be aware that some tests are "flaky." This is an unfortunate reality
of a system as complex as Wine. Ideally all tests would pass on all
Windows and Wine environments, but due to bugs, differing platform
behavior (especially window managers) and timing differences, they
don't. Most modules have well-written tests that should always pass.
Some, like the user32 messaging tests, and some audio and graphics
tests, fail with some frequency, even on Windows.
Full Article
Run Microsoft Windows Applications and Games on Mac, Linux or ChromeOS save up to 20% off CodeWeavers CrossOver+ today.