Migrate C++ Build Environment to x64

Project Description

We have a C++ legacy application, which is currently being built on 32-bit Windows with Visual Studio 2005. We need to migrate the build to an x64 system with a more recent version of Visual Studio.

Completion Notes

We are dealing with an old application, that may still have 32-bit parts or components in it. Although it builds 64-bit binaries, some parts are 32-bit, specifically the installer. Also, we expect some obstacles during migrations, so let's see if we can plan the process a bit first.

Preliminary Project Plan

Our plan can look like this:
  1. Install clean 64-bit OS with necessary updates and drivers. This gives us a clean system to start with.
  2. Install the latest version of Visual Studio on it.
  3. Install the necessary developer tools such as things to work with source control system, Notepad++, 7zip, etc.
  4. When we have all the tools ready, checkout the files and try to build the application. Some refactoring work may be needed to make it happen, and also to remove no longer needed 32-bit parts.
Note that we are aiming at setting up a BUILD system with only the pieces on it that are needed to do builds, and nothing more.

Installing OS

Install 64-bit Windows 7 OS from CD, drivers, and updates (excluding telemetry). As usual with Windows, it may take quite a while. After trying for a day, I got through to install updates before Service Pack 1, and the Service Pack 1 itself, at which point Windows Updates stopped working for me (would check for updates forever). Turned out that if you try long enough, you may get eventually through with a list of about 200 important and recommended updates.

To avoid telemetry do not install:


To avoid "Get Windows 10" adware, do not install:


Installing Visual Studio

Let's try to keep things simple and install Visual Studio Community 2015, with the only feature selected: Visual C++ under Programming Languages.

Select Visual C++ Feature
Select Visual C++ Feature

Now it is time for a "Hello World!" program, of course. Build it and see it working, so far everything i good.

Installing Tools

We'll also need a few additional tools:


Building the Application

I tried try opening an old solution in new Visual Studio to see if it can migrate things and build the application. It did not work well, as there were many errors.

Perhaps, a better way is to create a new solution in Visual Studio 2015 and migrate projects in there one by one. Before we do it, however, we need to get rid of something that Visual Studio creates for projects: large .sdf files. As explained here we can let it store in C:\Temp instead of project directories.

So, we go to Tools > Options > Text Editor > C/C++ > Advanced > Fallback Location

In there, we set "Always Use Fallback Location" and "Do Not Warn If Fallback Location Used" to True, "Fallback Location" to C:\Temp.

Now, if we try to do a build of even a small part of the app with Visual Studio 2015, we'll see various issues, mostly compiler warnings because pointer size is different from win32, and compiler is more picky. This introduces another step into the project, which is refactoring of the app in an effort to eliminate such compiler warnings.

Refactoring the Application

As the app is quite complicated and also requires a lot of changes for a smooth compilation on x64 with a Visual Studio that is 10 years newer, the following process may work.


Test Plan

Apparently, rudimentary testing is not enough, we need a more elaborate checklist that may look like below.

We have to obtain OS install media for testing first. Luckily, we still have access to a Windows 7 CD. As for new ones, Windows 8 Enterprise, Windows 8.1 Enterprise, and Windows 10 Enterprise can be downloaded as 90-day evaluation versions from here. Install them all in bare bones form without any updates. This will gets us an OS as how it comes from the shop. The point is that our app must work on such systems (without .net installed, etc.)

Testing Checklist

Trial Version

  1. Clean install with all defaults works.
  2. All files are in place, with correct version and certificates.
  3. The app starts, all major features work as expected.
  4. All commands in app menu function properly.
  5. App can be configured by user, confirm this is the case by trying a few different settings.
  6. Automatic start feature works OK after a reboot.
  7. Expiration mechanism is OK.
  8. Uninstall works.

Registered Version

  1. Clean install and all the steps above work.
  2. Install over running trial works good.
  3. Install over running trial into a different destination directory is OK.
  4. Manual update feature works as expected with or without available updates.
  5. Automatic update feature works as expected with or without available updates.
Although the above checklist is simple, it will help us catch most obvious bugs. We will definitely be able to see a missing DLL problem, among other potential issues.

Modified Project Plan

Now we can adjust our project plan. There many things that may break, so testing per checklist or a test plan is essential. The other problem is the volume of changes. Ideally, we do not want to deploy a new project in its entirety to users in a single step, but rather go in smaller increments. For example, if our app consists of 7 pieces, we may try to update one piece at a time and involve user testing and reporting early. So, our modified plan may be like this:

  1. Refactor one binary at a time. Do a pre-release testing on all supported operating systems using our checklist in an effort to detect most obvious bugs.
  2. If things look good after testing, deploy an update to a small number of users. We can do it by allowing automatic update for a limited time (for example: 1 hour), then rollback to the original build and deploy system.
  3. Monitor user feedback to see if they report anything that we missed in testing. It takes time to collect user feedback, we can use this time while refactoring other parts of the application.
  4. If problems are found - fix them, otherwise deploy the update again to a bigger portion of users and repeat the process.

Refactoring for x64

We can now move forward carefully, while working on 1 piece at a time and testing.

To complete the project in reasonable time, it now looks like we may need to limit the refactoring effort only to the migration problem at hand. Meaning that we probably should not allocate a lot of effort in attempting to get rid of all x64 compatibility warnings. We will have to do it as another project later. The scope of this project is, therefore, to make compiling, linking, building the installers, and deployment pieces working on x64 system, while at the same time keeping the source code for all pieces in such a shape that the build still works on x86 in case we have to roll back.

With a bit of minor fixing and testing, we can finally build all pieces together. To make installer builds possible, we have to reuse the NSIS Unicode parts from the original build system. Binary copy seems to work. Not an ideal solution probably, but suits our purpose for this project. And to make deployment possible, we also have to install putty tool, as our deployment script relies on it to copy files.

Summary

Although it may seem like an easy project, it is not. Migration is hard, you touch one thing and it brings with itself more things that need fixing, which in turn depend on other things.

What helps is limiting the scope and not trying to address everything at once.

Also, our original planning was too optimistic. Because of insufficient testing, we had to do a product rollback and adjust the plan.

In the end, the project completed successfully after approximately 1-man-week effort (full-time).

If you need something done, feel free to post a project here. You can also leave a comment on this project.