Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Porting C++ Code from NT to UNIX


Apr99: Programmer's Toolchest

George is a member of the consulting staff for Cadence Design Systems. He can be contacted at [email protected].


When Microsoft released Windows 3.1, UNIX developers started getting demands for their software on this new platform. Since the advent of Windows NT 4.0 and the explosion of TCP/IP and the Internet onto the non-UNIX desktop, thousands of programs have been ported to Windows. Early ports were often only a glorified recompile decorated with an after-the-fact GUI that replaced the command-line interface.

In contrast, today's major cross-platform tools are just as likely to have started out on Windows as on UNIX. Software developers are finding that customers in the midst of migrating from Solaris or HP to NT still want UNIX clients (if not full-blown applications) that run on their old boxes. Thus, more and more programmers are doing something that once felt inherently backward (if not evil) -- porting from Windows to UNIX.

For large C++ programs, the most realistic solution for porting from Windows to UNIX is to use a tool that implements the Windows API natively on the target system. One such product is MainWin XDE (eXtended Development Environment) 3.1 from MainSoft (http://www .mainsoft.com/). The company I work for recently finished porting a major application from Windows NT to Solaris, using MainWin with satisfactory results. MainWin provides MFC Libraries, common controls, a resource compiler, help engine, automatic makefile generator, specialized source files to include with application programs, and a whole set of tools that implement the cross-development environment on target systems. The advantages provided by using MainWin include a common code base, a relatively easy porting step with regards to compilation and linking, and an application that executes native code on the target platform. You can also offer either a Windows or "motif" look to your programs. The principal disadvantage of MainWin is a sometimes sluggish (but certainly not intractable) run-time performance with regard to graphics.

In this article, I'll discuss the specifics of using MainWin to port an executable application that compiles and runs in Windows to UNIX. I'll discuss preparing the Windows code and planning the port to UNIX, preparing the UNIX environment, dealing with third-party libraries, generating MainWin makefiles, building the project, and several run-time hurdles that you might have to clear. Along the way, I'll point out stumbling blocks that repeatedly caused us headaches and some interesting technical hurdles that MainSoft had to handle to provide its relatively seamless integration.

The MainWin Environment

Currently, MainWin is supported for SPARC Solaris 2.5.1, HP-UX 10.20, and SGI Irix 6.2 through Irix 6.5. You'll also need Native X11 Release R5 or R6 on all supported platforms. MainWin runs on top of X Windows interacting with both the the X Server and the window manager. Many window managers are supported, including twm, olwm, mwm, vuewm, 4Dwm, and olvwm. For most uses of MainWin a few environment variables need to be set. MWHOME refers to the root directory of your MainWin installation. MWREGISTRY stores the location of your registry file. Depending on your OS, the run-time libraries, MFC libraries, setup scripts, and other features are stored in logically organized directories under the installation root. One of the first things you'll notice using the tool is that it's much easier to find things with this hierarchical organization than in Windows, where most applications, DLLs, and help files are dumped in two main and several other lesser installation directories.

An important issue in planning any cross-platform development effort is the basic problem of how to access source code on multiple platforms. In my case I used ClearCase, Rational's multiplatform code-management system (http:// www.rational.com/). You could also use a shared file server. Clearly, the headaches caused by copying code back and forth between systems either via FTP or floppy disks far outweigh any time that you might spend in setting up a proper code-sharing schema.

Planning the Port to UNIX

My first worries when faced with porting a large Windows program to UNIX centered around the ignoble task of placing #ifndef WIN32 directives around the Windows API calls and replacing them with UNIX and X commands. But since MainWin implements the Windows API, none of this is necessary. In fact, having worked on nontrivial ports before, I wasn't ready for the relative luxury of porting using MainWin. One of the paradoxical tricks about this port was that the traditionally platform-safe practice of protecting OS-specific calls with precompiler directives actually backfired with MainWin because it defines WIN32 and _WIN32!

Although you don't need to isolate the Win32-specific code in your project, it is important to verify that it compiles and links properly on Windows. It was our experience that taking the time to debug and test the code on Windows first saved considerable time on the UNIX side (here there were invariably several compile and run-time issues to deal with no matter how meticulous I tried to be in anticipating the UNIX port). You will also need to obtain thoroughly tested versions of any third-party libraries that are required by your Windows project. This step alone can cost weeks of schedule time if the libraries are unavailable or worse yet, need to be ported or coded from scratch.

A MainWin project is a collection of MainWin modules. MainWin modules produce either an executable, DLL, or static library. If your Windows project is sufficiently complex, or if you are unfamiliar with the layout, you might want to create a link dependency table to suggest the build order in the MainWin makefile. Figure 1 shows the directory structure of the simple Windows project TestBench (available electronically; see "Resource Center," page 5), an executable that links against a DLL (DLL1), two libraries (LIB1 and LIB2), and an OLE control (ActiveX1). In addition, DLL1 needs a third-party library (LIB3). The module types can often help you determine dependencies because applications often depend on static and shared libraries, and other libraries and executables usually include controls and libraries. Table 1 is a link-dependency table for TestBench derived from the directory structure of Figure 1. Remember that run-time dependencies such as dynamic linking and automation can be ignored when determining build order because they don't affect the compile and link process.

Generating a MainWin Makefile

The only significant new code that you'll have to write in porting a Windows project to MainWin involves custom makefiles for each Windows project. Whereas Microsoft insulates you from the details of the actual makefile syntax, MainWin requires that you delve into the nitty gritty of makefiles to support your applications on UNIX. The MainWin utility mwgenmake partially automates the process of generating makefiles, but the bulk of the work consists of customizing the scripts via a suite of MainWin defined variables in Figure 2. A MainWin makefile is still fairly simple compared to a standard UNIX makefile because each of your makefiles will in turn include one of the generic mainwin makefiles that do most of the work for you.

The project TestBench consists of: TestBench.cpp, main.cpp, TestBench.rc, TestBench.def, and TestBench.idl. The first step in producing a MainWin makefile is to run mwgenmake in the home directory of TestBench (where all of the source files reside). Mwgenmake will put all C and C++ files in this directory into the makefile it generates. In addition, it will look for a module definition file (.def) for the module name type (either .exe or .dll), and whether the module is an MFC ActiveX control (.ocx). You can run mwgenmake from the command line or use the wizard version that is invoked with no parameters. The basic information contained in the makefile is a list of the source files for our application TestBench, its type (.exe), its name (TestBench), names of the Windows and UNIX resource files, compile flags (that enable you to specify the include file directory, a path to the .h files, and whether to build optimized or debug) and link flags (to specify a list of libraries to link against), and the dependencies of the project.

Listing One is the actual makefile for our project. Of course you'll need to generate a separate makefile for every MainWin module that is part of your project. Also, I edited the makefile to include link dependencies and include paths.

Building the MainWin Project

By the time you get around to compiling your newly created MainWin project, the brunt of the work shifts from fitting your code into the MainWin schema to the inevitable fighting with compiler differences. A full list of the differences between the Microsoft Visual C++ compiler and the various UNIX compilers is beyond the scope and purpose of this article. Suffice it to say that the supported UNIX compilers deal with templates differently, have varying degrees of ANSI compliance, and will likely generate unexpected warnings, if not errors, at first. Be particularly stealthy in your search for problems that arise from differences in naming conventions between Windows and UNIX. Hard-coded path strings will generally not be valid on a UNIX system and will cause problems at run time. MainWin provides the preprocessing tool mwprepro that converts both the names of files found in #include directives in the source code and the names of files found in the current directory to lowercase. This was too violent a treatment for my taste, so I searched for these problems by hand (and not without cost!). Other possible prebuild steps might include rewriting assembly-language routines and removing all instances of language features that are only supported in Visual C++, such as full method names within class definitions, extraneous semicolons, remarks after #endif, empty arrays within structures, nameless structures or unions, and the use of large integers.

You'll be ready to make an initial stab at building at this point. To execute MainWin's make utility on your project, run mwmake in the directory of each of your MainWin projects in the predetermined build order. The MainWin build system, through mwmake, will automatically invoke the MainWin DIP utility mwdip at link time to build a file that includes all necessary DLL initialization according to the order found in the link line of your makefile. This is important to note if you are using a utility other than mwmake to build (such as make or clearmake). In this case you'll need to invoke mwdip explicitly.

Again, there are a host of issues that will likely cause your initial links to fail. You may often run into duplicate symbols, multiple included files, unresolved symbols, and so on. If you are working with a non-MFC project and the linker produces an error about the symbol WinMain() being multiple defined, you must make sure that the makefile TYPE was not incorrectly defined as MFC, causing the linker to add standard MFC WinMain() entry points to your code. A similar problem is a multiply-defined main() symbol, defined in the file MainWin.c, a file that is compiled into all MainWin projects. If you have main() defined in your code, you will need to add Example 1(a) to your makefile. Then, the file that contains your definition of main() includes Example 1(b). This is only necessary for C++ source files.

Run-Time Issues

Run-time problems (aside from your own bugs) that you may encounter with a newly created MainWin port include: Windows NT language support, memory APIs, global handles, byte ordering, DDR functions, DCOM, asynchronous communication, and various ActiveX API functions. Particularly common run-time glitches pop up when dealing with the registry, programs with multiple threads, and customizing the look of your application.

All MAINWIN registry changes made at run time affect only the memory copy of the registry file. You can use the API functions RegCreate.../RegSet... to make changes to the registry at run time. This precaution prevents ambiguities due to multiple write operations by different MainWin processes. To save registry updates between sessions set MWAUTO_REG_SAVE to true.

MainWin assumes that you are working in a threadsafe environment. You may encounter problems if you run on a nonthreadsafe platform. These problems are likely to pop up in relation to the display (display lockups are common). But because developers sometimes have to use nonthreadsafe libraries like Xlib Release 5 on Irix, and because many programs have only one thread, MainWin does not require a threadsafe X library. So an application that is not thread dependent and runs on nonthreadsafe software should work if ported with MainWin.

Finally, it is possible that you won't be satisfied with the default Motif colors or the default shadow thicknesses/margin widths used by MainWin in the Motif look. You can modify the motif look of your application by adding the following X Motif resources in Table 2. The values are sample values suggested by MainSoft.

Conclusion

Reading this might cause you to wonder whether it would be better to just rewrite your program in Java. I'll admit that fighting with compiler issues, makefiles, and a host of other unforeseen road blocks is not my favorite software development activity. In many situations Java is a better solution. But in our case, we had a significant investment in our C++ code base. We weren't willing to give up the many person-years we had invested in programming and careful optimization that would be lost in a Java rewrite.

In short, we found MainWin to be an adequate solution to the vexing problem of porting a large nontrivial NT software product to UNIX.

DDJ

Listing One

ifeq ($(COMPILE_OPTION), debug)
CFGSUFFIX=d
else
CFGSUFFIX=
COMPILE_OPTION=optimized
endif

RUN.dir=/TestBench/bin
PROG      = TestBench${CFGSUFFIX}
TYPELIB_FILES = ${PROG}.tlb
MIDL_TLB=${PROG}.tlb
WRESOURCE = ${PROG}.rc   # Windows resource
TRESOURCE = ${PROG}.rxt   # program
MIDL_IDL=${PROG}.odl
APP_CCPPFLAGS=-I/testbench/include -I${MWHOME}/../mfc400/include -DRW_NO_STL 
-D_REENTRANT

SOURCES =  \
    TestBench.cpp \
    main.cpp \
    
mwupdatedependencies=depend-sunos5

CPP_OBJS  = ${SOURCES:%.C=%.o}
CPP_OBJS := ${CPP_OBJS:%.cpp=%.o}
CPP_OBJS := ${CPP_OBJS:%.cxx=%.o}
OBJS      = ${CPP_OBJS:%.c=%.o}
SRCS      = ${SOURCES}

MAKE_VERBOSE=true
MWUSE_OLE_LIBS = true
LDEXTERNAL= \
        -lSimDef${CFGSUFFIX} \
        -lcddbutil${CFGSUFFIX} \
        -ltypes${CFGSUFFIX} \
        -lAltaString${CFGSUFFIX}
APP_LDFLAGS =  \
        -lActiveX1${CFGSUFFIX} \
        -lDLL1${CFGSUFFIX} 

__cplusplus = true

## MFC library related definitions.
## Removing these definitions disables linking with MFC.
## MFC_MODULE_TYPE can be one of the following: DLL, EXTENSION_DLL, EXE
MFC_MODULE_TYPE=EXE
MFC_LIB_ROOT=mfc400

include $(MWHOME)/make.rules.simple



Back to Article


Copyright © 1999, Dr. Dobb's Journal

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.