Cross-platform C SDK logo

Cross-platform C SDK

Portability

❮ Back
Next ❯
This page has been automatically translated using the Google Translate API services. We are working on improving texts. Thank you for your understanding and patience.

It's hard to write software that runs correctly and efficiently. So once a program works in one environment, you don't want to repeat much of the effort if you move it to a different compiler or processor or operating system. Ideally, it should need no changes whatsoever. Kernighan & Pike - The Practice of Programming.


In Quick start and New project we used Visual Studio 2019 to generate the NAppGUI solution. We understand by portability the ability to compile and debug our programs on platforms other than those that were written, without touching a single line of code. We understand by platform the combination of a compiler and a CPU architecture. For example, v142_x64 refers to Visual Studio 2019 and Intel 64bits. In (Figure 1) we see the different steps in the code migration process.

Three Windows, macOS and Linux computers networked to the same source code repository.
Figure 1: Stages in the code portability between platforms.
  • Working copy: A copy of the project source code must exist on each machine. Normally this will be done through a version control system such as Subversion or Git, although it could also be updated from an external memory.
  • CMake: CMake will create a build project (or solution) from the source code using the scripts in /prj directory of the SDK distribution. This will be done fully automatically.
  • Build: Using Visual Studio, Xcode or GCC the solution will be compiled and the libraries and executables included in it will be generated.
  • Run/Debug: Binaries can already be executed and debugged on the target platform.

1. Platforms

In (Table 1) you have the complete list of supported platforms. In the folder /lib for each project NAppGUI binaries must be present, depending on the platforms on which it should compile. CMake will recognize the contents of this folder and configure the solution in each case.

Table 1: Platforms supported by NAppGUI.
Platform Compiler CPU Minimum S.O.
v142_x64 VS2019 Intel x64 Vista
v142_x86 VS2019 Intel x86 Vista
v141_x64 VS2017 Intel x64 Vista
v141_x86 VS2017 Intel x86 Vista
v141_xp_x64 VS2017 Intel x64 XP
v141_xp_x86 VS2017 Intel x86 XP
v140_x64 VS2015 Intel x64 Vista
v140_x86 VS2015 Intel x86 Vista
v140_xp_x64 VS2015 Intel x64 XP
v140_xp_x86 VS2015 Intel x86 XP
v120_x64 VS2013 Intel x64 Vista
v120_x86 VS2013 Intel x86 Vista
v120_xp_x64 VS2013 Intel x64 XP
v120_xp_x86 VS2013 Intel x86 XP
v110_x64 VS2012 Intel x64 Vista
v110_x86 VS2012 Intel x86 Vista
v110_xp_x64 VS2012 Intel x64 XP
v110_xp_x86 VS2012 Intel x86 XP
v100_x64 VS2010 Intel x64 XP
v100_x86 VS2010 Intel x86 XP
v90_x64 VS2008 Intel x64 XP
v90_x86 VS2008 Intel x86 XP
v80_x64 VS2005 Intel x64 XP
v80_x86 VS2005 Intel x86 XP
sdk10_15_x64 Xcode 11.3 Intel x64 Catalina
sdk10_14_x64 Xcode 10.2.1 Intel x64 Mojave
sdk10_13_x64 Xcode 9.4.1 Intel x64 High Sierra
sdk10_12_x64 Xcode 8.3.3 Intel x64 Sierra
sdk10_11_x64 Xcode 7.3.1 Intel x64 El Capitan
sdk10_10_x64 Xcode 6.4 Intel x64 Yosemite
sdk10_9_x64 Xcode 6.2 Intel x64 Mavericks
sdk10_8_x64 Xcode 5.1.1 Intel x64 Mountain Lion
sdk10_7_x64 Xcode 4.6.3 Intel x64 Lion
sdk10_6_x64 Xcode 3.2.6 Intel x64 Snow Leopard
sdk10_6_x86 Xcode 3.2.6 Intel x86 Snow Leopard
gcc9_gtk3_x64 GCC 9.3/GTK 3.24 Intel x64 Ubuntu 20.04 LTS
gcc7_gtk3_x64 GCC 7.3.0 Intel x64 Ubuntu 18.04 LTS
gcc5_gtk3_x64 GCC 5.3.1 Intel x64 Ubuntu 16.04 LTS
gcc5_gtk3_x86 GCC 5.3.1 Intel x86 Ubuntu 16.04 LTS
gcc4_gtk3_x64 GCC 4.8.2 Intel x64 Ubuntu 14.04 LTS
gcc4_gtk3_x86 GCC 4.8.2 Intel x86 Ubuntu 14.04 LTS
gcc6_gtk3_arm GCC 6.3.0 ARM Raspbian 9.1 Strech

2. Windows Portability

We can use any version of Visual Studio from 2005 to compile under Windows. As we saw on Quick start the first thing we have to do is launch CMake over the source code:

  • Where is the source code: C:\nappgui\src.
  • Where to build the binaries: C:\nappgui_build.
  • Press [Configure] and [Generate].

The first time CMake will ask what version of Visual Studio we want to use (Figure 2) and that, obviously, we must have installed in the machine in question. The architecture selected by default can be changed from the same dialog (Figure 3).

Drop list that shows the different versions of Visual Studio with which we can compile.
Figure 2: Selecting the Visual Studio version from the CMake wizard.
CMake Optional platform for generator.
Figure 3: Selection of the architecture in the generator panel.

Once we press [Open Project] , we will have the project ready to compile. From Visual Studio Build->Build Solution and debug Debug->Start Debugging (Figure 4).

Visual Studio 2010 debugging a C application.
Figure 4: Debugging the Die application in Visual Studio 2010.
To change the Visual Studio version we have to clear the CMake cache File->Delete Cache or change the destination directory: Where to build the binaries. It is possible to have different solutions from the same code in several directories, each with its own version of Visual Studio.

Starting with Visual Studio 2010, a dissociation occurs between the editor and the compiler. The term Plaform Toolset identifies the compiler, that can be used with more modern IDEs. If we do not indicate anything, CMake will use the toolset included by default in each version of VS, but it can be changed from Optional toolset to use. For example, we can combine Visual Studio 15 2017 with the VS2013 toolset v120_xp (Figure 5).

Optional toolset to use in CMake.
Figure 5: Using the toolset v120_xp with the editor of VS2017.

2.1. Visual C++ Redistributable

By default, Visual Studio dynamically links the functions of the C library, which causes the .exe may not work on machines that do not have the VC++ DLLs (Figure 6). This forces applications to include a copy of MSVCRT.dll, VCRUNTIME.dll, ... or install the famous Visual C ++ Redistributable packages to ensure the application can run smoothly.

Error message caused by the lack of VC ++ libraries.
Figure 6: Error due to lack of VC++ DLLs.

NAppGUI uses a small set of the C library, and directly accesses the Windows API whenever possible (more details in Standard C library). For this reason, all applications created with NAppGUI perform a static link (option /MT) of the stdlib functions that need, avoiding dependencies at the expense of slightly increasing (a few Kb) the size of the final executable. This ensures that applications will run smoothly on all Windows machines without the need for additional DLLs and without having to install the VC++ Redistributable.

NAppGUI applications do not require the Visual C++ Redistributable. Neither do they use the MFC "Microsoft Foundation Classes" nor the .NET platform.

2.2. WindowsXP support

From VS2012, the Platform Toolset generates executables that are not compatible with WindowsXP. If we want our applications to run on this system, we must select the alternative toolset finished in _xp: V141_xp, V140_xp, V120_xp, V110_xp. Or, v100, v90 or v80, that do support XP (Figure 7).

Support for WindowsXP has been permanently removed in Visual Studio 2019. Platform Toolset v142_xp does not exist.
Using Visual Studio 2005 in WindowsXP by debugging an application written in C.
Figure 7: Debugging the Die application in WindowsXP with VS2005 (toolset v80).
NAppGUI does not work on Windows prior to XP or with Visual Studio prior to 2005.

2.3. SSE support

With the Pentium III, Intel incorporated an additional set of instructions for floating-point operations called SSE Streaming SIMD Extensions. This allows optimizing mathematical calculations at the cost of losing compatibility, since applications that use SSE will not work on Pentium II or earlier models. In NAppGUI the toolset have been reserved v80_x86 and v90_x86 of Visual Studio to create applications compatible with older processors (Table 2). From v100_x86, SSE2 will be used in all the toolset.

Table 2: SSE support
Toolset SSE Minimum CPU
v80_x86 x87 (no SSE) Pentium II/AMD K6
v90_x86 SSE Pentium III/AMD Duron
v100_x86 SSE2 Pentium IV/AMD Sempron
v110_x86 SSE2 Pentium IV/AMD Sempron
... SSE2 ...
SSE support is only disabled in 32-bit (x86) architectures. All 64-bit (x64) CPUs incorporate SSE2.

3. macOS portability

Compiling on Apple iMac, macBook and macMini follows the same philosophy as in Windows, we will only need CMake and Xcode. As we have already done in Windows, we open CMake and assign the directories:

  • Where is the source code: /Users/fran/nappgui/src (or your WORKING_DIR/nappgui/src).
  • Where to build the binaries: /Users/fran/nappgui_build. Directory where the generated code will go.

Press [Configure]. The first time CMake will ask us to select the compiler. Unlike Visual Studio, it only offers one option for Xcode (Figure 8). We accept, press [Generate] and then [Open Project].

Selecting Xcode from the CMake wizard.
Figure 8: Selection of the Xcode generator in CMake.

When opening the Xcode solution (NAppGUI.xcodeproj), we see the different projects that compose it, including Die and Dice. We select Die in the upper left drop-down and then press Play or Product->Run (Figure 9). This will compile the program and launch it in debug mode, where we can establish breakpoints to inspect the stack and the value of the variables.

Debugging the Die application from Xcode 10.2.1.
Figure 9: Debugging the Die application in Xcode.

3.1. Base SDK and Deployment Target

Each year, Apple releases a new version of macOS, which comes with a new SDK and the Xcode update that includes the SDK.

Base SDK is the version included in each new major version of Xcode, which matches the latest version of the macOS system on the market.

Apple has a much more restrictive policy than Microsoft regarding the applications compatibility with earlier versions of the operating system. By default, a program compiled with SDK 10.14 (macOS Mojave) will not work in the immediately previous macOS High Sierra (Figure 10).

Error running an application with deployment target 10.14 on macOS High Sierra.
Figure 10: Die with Base SDK 10.14 will not work in High Sierra.

To avoid this problem, and for applications to work in older macOS, the Deployment Target parameter exists. When using it, a macro will be activated thatavoid the new features of the Base SDK. This will allow the program to run in old versions at the expense, of course, of not having access to the latest iMac features. You can select the Deployment Target through CMake options list, after pressing [Configure] (Figure 11).

Selecting the Xcode Deployment Target from CMake.
Figure 11: Selection of the Deployment Target of our solution.
Xcode 8 considers Deployment Target to be less than 10.9 obsolete (Figure 12). Use Xcode 7 if you want support for Mac OSX 10.8 Mountain Lion and earlier.
Warning osx 10.9 deprecated.
Figure 12: Deployment Target 10.9 obsolete from Xcode 8.

3.2. xcode-select

We have already seen that CMake only offers an option for Xcode, although it is possible to have several versions on the same machine, each within its own Xcode.app bundle. There will always be a default Xcode in the system (the most recent) but it can be changed using the xcode-select utility:

Listing 1: Check the current version of Xcode.
1
2
xcode-select -p
/Applications/Xcode.app/Contents/Developer
Listing 2: Change of the active version of Xcode.
1
sudo xcode-select -s /Applications/Xcode8.app/Contents/Developer
Listing 3: Set the default version of Xcode.
1
sudo xcode-select -r
You must press CMake [Configure] and [Generate] every time you use xcode-select so that your project updates the compiler change.

3.3. macOS 32bits

Since the macOS High Sierra version, Apple has declared obsolete the architecture of 32 bits, issuing warnings to the users in the case of detecting executables i386 (Figure 13). From Xcode 10, you can not compile in this architecture (Figure 14).

Warning of macOS when trying to run 32bit applications.
Figure 13: MacOS warnings in 32bit applications.
Error message in Xcode 10 when compiled in 32bits.
Figure 14: Error in Xcode 10 when trying to compile in 32bits.
Support for 32-bit applications has definitely removed in macOS Catalina, which only allows 64-bit applications to run.

This makes some sense since all models of Intel iMac incorporate 64-bit processors, with the exception of a few 2006 models in white polycarbonate that mounted the 32-bit Intel Core Duo (Figure 15). These iMac admitted at most the Mac OSX 10.6 Snow Leopard, being fundamental requirement from 10.7 Lion, the have a 64-bit CPU. To compile without problems in 32bits you have to use, at most, Xcode 6 (Figure 16).

iMac, macBook and macMini with Intel 32bit processors.
Figure 15: Unique Apple models with Intel 32bit processor.
Debugging the Die application from Xcode in Snow Leopard.
Figure 16: 32 bits builds Xcode 3.2.6 (Snow Leopard).

4. Linux portability

In the Linux world, the gcc compiler and the make tool are used to generate the binaries, but there is no "official" development environment. To carry out an elementary configuration of our team in Ubuntu, type the following commands:

1
2
3
4
5
6
7
8
sudo apt-get install gcc
sudo apt-get install g++
sudo apt-get install make
sudo apt-get install svn
sudo apt-get install cmake cmake-qt-gui
sudo apt-get install libgtk-3-dev
sudo apt-get install libglu1-mesa-dev freeglut3-dev mesa-common-dev
gsettings set org.gtk.Settings.Debug enable-inspector-keybinding true

The work methodology does not change at all. We open CMake, select the directories, press [Configure] and then [Generate] (Figure 17).

Configure the CMake directories on Linux.
Figure 17: Configuring CMake on Linux.

In Linux we must use the CMake Unix Makefiles generator, which will create a series of Makefile scripts in /home/fran/nappgui_build (or the binary directory that we have selected). These Makefile contain the necessary commands to compile and link all the projects of the solution. We open a terminal:

Listing 5: Generate the solution through make.
1
2
3
4
5
6
7
8
cd /home/fran/nappgui_build
make
[ 15%] Building C object games/die/CMakeFiles/Die.dir/dgui.c.o
[ 30%] Building C object games/die/CMakeFiles/Die.dir/die.c.o
[ 45%] Building C object games/die/CMakeFiles/Die.dir/draw/ddraw.c.o
[ 60%] Building C object games/die/CMakeFiles/Die.dir/resgen/all.c.o
[ 75%] Linking CXX executable Debug/Die
[ 95%] Built target Die

Once the compilation is finished, we can launch the executables directly from the terminal:

Listing 6: Launch the Die application.
1
./die/Debug/Die

And if we are real hackers, we will be able to debug the programs directly from gdb (Figure 18).

Listing 7: Debugging Die with gdb (Figure 18).
1
2
3
gdb ./die/Debug/Die
(gdb) run
...
Using GDB to debug a C application.
Figure 18: Debugging Die with GDB from the terminal.

4.1. Eclipse CDT

Working directly with the terminal will give us the highest level of freedom possible to coding. With returning to the console and typing make everything necessary will be re-compiled. However, the use of GDB directly will be quite tedious, so we will make the leap to Eclipse CDT. This environment will allow us to program with a methodology similar to that of Visual Studio and Xcode: Situate breakpoints, inspect the stack and variables, search for files within the code directory, multiple editions, mass searches, etc.

It is not imperative to use Eclipse. You can choose the editor/IDE you prefer.

The only difference is that we must use the generator Eclipse CDT4 - Unix Makefiles in CMake (Figure 19) that in addition to Makefile will create the files .cproject and .project needed to import the project into Eclipse.

CMake Generators for Linux based on Makefile.
Figure 19: Generators based on Makefile.

We open Eclipse and we do File->Import->Existing Projects into Workspace. A dialog box will appear where we indicate the build directory that we have configured in CMake (/home/fran/nappgui_build). Eclipse will open the project placing a tree with all the files on the left and we'll compile with Project->Build All. At the time of debugging (Die in this case) we will create a profile from Run->Debug Configurations->C/C++ Application. We press [Search Project...] and select Die from the drop-down list. Finally we press [Debug] to debug the application interactively (Figure 20).

Debugging the Die application from Eclipse.
Figure 20: Debugging the Die application with Eclipse.

Some interesting Eclipse CDT options under Window->Preferences.

  • Run/Debug->Launching->Terminate and Relaunch while launching.

4.2. update-alternatives

NAppGUI supports the GCC compiler since version 4.3 (2008). Given the number of existing Linux distributions and that each one includes its own version of GCC, we will use Ubuntu as the reference operating system (Table 3).

Table 3: GCC versions.
GCC Ubuntu
8.3.0 18.10
7.3.0 18.04 LTS
5.3.1 16.04 LTS
4.8.2 14.04 LTS
4.6.3 12.04 LTS
4.4 10.04 LTS

It is possible to have several versions of GCC installed on the same machine and to alternate between them in a similar way as we did with xcode-select, through utility update-alternatives of Linux. We assume that we are in Ubuntu 18.04:

Listing 8: Version of gcc installed.
1
2
gcc --version
gcc 7.4.0
Listing 9: Install gcc-6.
1
sudo apt-get install gcc-6 g++-6
Listing 10: Registry gcc-7 and gcc-6.
1
2
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 60 --slave /usr/bin/g++ g++ /usr/bin/g++-7
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 50 --slave /usr/bin/g++ g++ /usr/bin/g++-6
Listing 11: Switch to gcc-6.
1
2
3
4
5
sudo update-alternatives --set gcc /usr/bin/gcc-6
gcc --version
gcc 6.5.0
g++ --version
g++ 6.5.0
Listing 12: Return to the default version of gcc.
1
2
3
4
5
sudo update-alternatives --auto gcc
gcc --version
gcc 7.4.0
g++ --version
g++ 7.4.0

4.3. Toolkits

Unlike Windows and macOS, in Linux you can create user interfaces using different libraries (or toolkits) with GTK and Qt being the two most famous. NAppGUI uses GTK+3 for the graphical part in Linux (see Dependencies), although in the future it is possible that it also incorporates support for Qt, since it is the basis of the KDE desktop system, used in Kubuntu.

GTK+3 is found naturally in Ubuntu and many other distributions, although to compile it is necessary to install the developer version.

Listing 13: Install the development version of GTK+3.
1
sudo apt-get install libgtk-3-dev

4.4. Linux 32bits

To compile 32bit applications from an Ubuntu 64bits system it is necessary to install the package multilib:

1
sudo apt-get install gcc-multilib

But now there are problems to perform cross-compilation that includes the GTK+ library, so it will not be possible to use the same development machine to generate in both architectures, as in Windows. Console applications or libraries that do not access GTK can be compiled in 32bits from a 64bit computer.

It is not possible to compile in 32bits from an Ubuntu system of 64bit applications that use GTK+3.

4.5. ARM architecture

The architecture ARM Advanced RISC Machine is the predominant in the market of embedded devices such as smartphones and tablets. Currently, NAppGUI does not offer support for the development of iOS/Android mobile applications, but for other types of boards that support versions of Linux ARM "desktop", such as the Raspberry PI with Raspbian. To carry our code to Raspberry Pi you have to follow the same steps as in Ubuntu Linux (Figure 21). Both distributions are based on Debian, so we have GCC, CMake and Eclipse directly through apt-get.

Debugging a C application on a Raspberry Pi, using Eclipse.
Figure 21: Debugging the Die application on a Raspberry Pi.

5. Configurations

An NAppGUI application can be compiled in three different configurations, depending on the level of debugging we need.

  • Debug: Includes debug information in the binaries and code optimizations are not performed. It is the version for the developer.
  • Release: The debug information is removed and all possible optimizations are made. It is the version for the user.
  • ReleaseWithAssert: It is the Release version, but leaving the statements active Asserts. It is aimed at the end user, but in cases where it is necessary to obtain detailed information of possible anomalies, at the cost of a decrease in the overall performance of the program.

Both Visual Studio and Xcode are multi-configuration environments, that is, we can alternate between them directly from the editor itself. In Visual Studio we have a drop-down at the top of the editor (Figure 22).

Configuration selection menu in Visual Studio.
Figure 22: Changing settings in Visual Studio.

In Xcode is a bit more hidden. We make Product->Scheme->Edit Scheme. A pop-up window will appear. We select Run->Info->Build Configuration (Figure 23).

Xcode configuration selection menu.
Figure 23: Change of configuration in Xcode.

Unfortunately, Unix make does not support multiple configurations. This forces us to enter the property CMAKE_BUILD_CONFIG (Figure 24) to set the configuration in CMake before generating the Makefile. We will have to press again [Configure] and [Generate] for the new configuration to take effect.

Configuration selection menu in CMake.
Figure 24: Changing settings in CMake (Unix Makefile).

6. Installers

The last step, once a program has been created and refined, will be to create a package/installer that contains the executable and its possible dependencies, to distribute it comfortably to the final user. This packaging process has been automated in NAppGUI thanks to CPack, a utility included with CMake (Figure 25).

CMake dashboard with packaging options.
Figure 25: Packaging options in CMake.
  • Activate the CMAKE_PACKAGE option in the CMake panel.
  • Set in CMAKE_PACKAGE_PATH the directory where the generated packages are to be saved.
  • Select the type of packaging through the variable CMAKE_PACKAGE_GEN that admits three values:
    • TGZ: Creates a compressed package .tar.gz that can be opened with any .zip utility installed on the computer (Figure 26).
    • Packaging an application with .tar.gz.
      Figure 26: Packaging .tar.gz on Linux.
    • DragNDrop: (MacOS only). Create a dmg drive with a bundle .app and a link to the /Applications (Figure 27) folder.
    • Packaging an application in dmg.
      Figure 27: Dmg packaging in macOS.
    • NSIS: (Windows only). Create an installer using the Nullsoft scriptable install system (Figure 28) utility. It is necessary to have installed the software NSIS (only to create the installer). The user who receives the package does not need to have such software installed.
    • NSIS installer with the application we just created.
      Figure 28: NSIS installer.

Each package includes a copy of the license with which we are going to distribute the application. The content of it can be edited from the file /res/license.txt included in the resources directory of each project.

Packaging a program is time consuming. Activate CMAKE_PACKAGE only when we are going to distribute. During the development process it is advisable to deactivate the checkbox.
❮ Back
Next ❯