Qt: Detect in the .pro file if compiling on 32 bit or 64 bit platform

QtLogo-CodeLess

Couple of months earlier I wrote how to compile Qt projects (.pro files) from command line both on OS X  and Windows. Basically it comes down to calling qmake executable and passing path to your .pro file and path to make spec file for specific compiler. It goes something like this:

C:\QT5_build\bin\qmake.exe [Path to your .pro file] 
-r spec C:\QT5_build\mkspecs\win32-msvc2010

I am using shared .pri configuration files which I later include in my .pro files to have consistent settings across different project. In my base .pri settings files I define where are shared libraries, include files, platform specific compiler flags.

One of the things I want to control is where should output go, namely DESTDIR. I wanted to for my 32 bit builds to land in Output folder and 64 bit in Outputx64, both on Windows and OS X. Here is my shared configuration .pri file:

BUILD_TARGET = SimpleProjectName

win32{
	PROJECT_FOLDER = ..\\..
}
else{
	PROJECT_FOLDER = ../..
}


macx-clang{
    OUTPUT_FOLDER = $$PROJECT_FOLDER/Outputx64
}

macx-clang-32{
    OUTPUT_FOLDER = $$PROJECT_FOLDER/Output
}

win32{
	SOURCE_FOLDER = $$PROJECT_FOLDER\\Source
	OUTPUT_FOLDER = $$PROJECT_FOLDER\\Output$$(Platform)
	TEMP_FOLDER =   $$OUTPUT_FOLDER\\Temp
	MODULE_TEMP_FOLDER = $$TEMP_FOLDER\\$$BUILD_TARGET
	GENERATED_FOLDER = $$OUTPUT_FOLDER\\Generated
	MODULE_GENERATED_FOLDER = $$MODULE_TEMP_FOLDER\\Generated
}
else{
	SOURCE_FOLDER = $$PROJECT_FOLDER/Source
	TEMP_FOLDER = $$OUTPUT_FOLDER/Temp
	MODULE_TEMP_FOLDER = $$TEMP_FOLDER/$$BUILD_TARGET
	GENERATED_FOLDER = $$OUTPUT_FOLDER/Generated
	MODULE_GENERATED_FOLDER = $$MODULE_TEMP_FOLDER/Generated
}

CONFIG(debug, debug|release): CONFIGURATION = Debug
CONFIG(release, debug|release): CONFIGURATION = Release

win32{
	CONFIGURATION_OUTPUT_FOLDER = $$OUTPUT_FOLDER\\$$CONFIGURATION
	CONFIGURATION_MODULE_TEMP_FOLDER = $$MODULE_TEMP_FOLDER\\$$CONFIGURATION
}
else{
	CONFIGURATION_OUTPUT_FOLDER = $$OUTPUT_FOLDER/$$CONFIGURATION
	CONFIGURATION_MODULE_TEMP_FOLDER = $$MODULE_TEMP_FOLDER/$$CONFIGURATION
}

# set intermediate directories
win32{
	UI_DIR = $$GENERATED_FOLDER\\Qt\\$$BUILD_TARGET
	RCC_DIR = $$CONFIGURATION_MODULE_TEMP_FOLDER
	MOC_DIR = $$MODULE_GENERATED_FOLDER
	OBJECTS_DIR= $$CONFIGURATION_MODULE_TEMP_FOLDER
}
else{
	UI_DIR = $$GENERATED_FOLDER/Qt/$$BUILD_TARGET
	RCC_DIR = $$CONFIGURATION_MODULE_TEMP_FOLDER
	MOC_DIR = $$MODULE_GENERATED_FOLDER
	OBJECTS_DIR= $$CONFIGURATION_MODULE_TEMP_FOLDER
}

DESTDIR = $$CONFIGURATION_OUTPUT_FOLDER

With this configuration my project files are normally under "Source" folder and then I will get to right next to it Output or Outputx64 depending on -arch I am using. Inside Output folder there will be Debug or Release folder, based on configuration I am compiling. Next to Debug and Release there will be folders for Temp and Generated files. Something like this:

build output

Key lines for mac OS X are number 11 and number 15. macx-clang is 64bit build configuration and macx-clang-32 is 32 bit build configuration. On line 21 I set Windows build configuration based on $$(Platform) variable. This will in case of 64 bit build add “x64” to “Output”. This flag works only for windows as far as I know. 

Note: In order to be able to build both 32 and 64 bit executables with Qt on OS X platform Qt must be also compiled for desired architecture.  To be able to build Qt on OS X for 32bit architecture Qt build must be configured with -platform macx-clang-32 flag before calling make command.   For example:

./configure -opensource -confirm-license -platform macx-clang-32 -debug-and-release -no-compile-examples ….

Build Qt projects on Windows and OS X from command line

Let us assume you have a project build with Qt version 5.x. You have your Qt installation on your system either Windows, Linux or Mac Os X, doesn't really matter as long as you are compiling your application from Qt creator. What to do if you want to automate this process and you can't install Qt on your, let us assume, build machine. There are several different reasons why you don't want to install Qt on each machine you are building it on. In my particular case application is hosted on TeamFoundation server and code is being pulled and build from there using Python scripts. Whole idea is to make build and deployment process self sufficient and easy to maintain. Python script should get latest code from source control and basically build Qt project from command line with same code on both Windows and Mac Os X. 

Qt projects consist of .pro file which is basically your project file where you define you build settings, which files you want to build, c, c++, h etc. This file is very basic version of make file, but it's NOT. Alongside your standard C++ files you will probably have UI files if you are building application with windows. Those files are basically xml files describing your UI, windows, controls etc. Qt has a special tool inside it's bin directory call "uic" or "uic.exe" depending on which system and this executable is responsible for converting those UI files into "reasonable" C++ code, normally into .h files which all C++ compilers can actually recognise and compile. This tool will come later become my problem on Mac Os X system.

Qt build process

How Qt creator and actual qt build process works is quite simple. Inside Qt installation "bin" folder there is a tool named "qmake". Qmake has a task to take your .pro project file and create universal make file which can be compiled with any extern make program. Make is essentially tool that based on configuration in background calls compiler which eventually compiles your code into library or executable for particular underlaying operating system. If you click on Projects icon inside Qt creator you can see this steps with command line parameters being used. 

Build or copy Qt installation

To be able to compile your application outside qt creator you will need content from Qt installation. One way is to install on local system and copy content of the installation or to build from source. Building from source can be very time consuming and frustrating. Building from source can take hours even if you exclude components you don't need such as example code etc. Basically you have different compilers plus different operating system you need to compile for, it adds up. It is definitely easier to download installer and install it locally and copy content from there.

Qt Qt installation folder

 

In my case I had Qt 5.2.0 and under 5.2.0 folder which is a version there is "clang_64" folder and this means this Qt was build with/for clang 64bit compiler (not to go into many details here). In case of Windows installation this folder will be called for example "msvc2010" if you have version for Visual Studio 2010 C++ compiler. 

To be able to build Qt from command line you will entire content from this folder. Bin subfolder contains various tools such as aforementioned Qmake and uic , lib folder is where Qt prebuild libraries lie, include folder contains header files and "mkspecs" folder contains instructions for Qmake how to build make file for each particular compiler.

Build from command line

Almost there. Let us assume we copied Qt installation on following locations C:\QT5_build for Windows and /Users/yourname/QT5_build for OS X. Build is two step process. First you run Qmake agains your .pro project file and based on your settings it will generate Make, Make.Debug and Make.Release files.

Note:

Since we are not using installed version of Qt we need to instruct Qmake where hour Qt headers and libraries are. This is done with qt.conf file. Make qt.conf text file and save it into "bin" folder where Qmake executable is. Content should be as simple as this

[Paths]
Prefix = C:/QT5_build

Notice forward slash. Forward slash must be used for both Windows and OS X. Also single slash instead of double

In first step execute your Qmake against your project file to generate make files. 

Windows

For a Windows it would be:

C:\QT5_build\bin\qmake.exe [Path to your .pro file] -r spec C:\QT5_build\mkspecs\win32-msvc2010

By specifying "win32-msvc2010" I am telling Qmake to generate Make file for Visual Studio 2010 compiler. This same thing does Qt creator, passes same parameters to QMake and then afterwords calles JOM make tool to compile from those make files. Jom is clone of Microsoft Nmake and it just supports running multiple  commands in parallel.

In second step you call Nmake with your generated Make file like this:

nmake /f [Path to your Make file] [Debug/Release]

Last thing to do Windows would be to check your application, run it and if necessary add needed Qt runtime libraries since build tools will not do that for you.

Mac OS X

This process is quite simple when it's done on Windows, it's quite other story on OS X. Steps are the same, first you run Qmake against your project file and then you call simple Unix make against make files, BUT. Since we took Qt build out of actual installation tools inside bin folder will not work since they have static links and will look for dependent libraries under absolute path where they were installed.

So first running QMake:

./Users/yourname/QT5_build/bin/qmake [Your .pro file] -r -spec /Users/yourname/QT5_build/mkspecs/macx-clang

In my case this returns with following error:

sh: line 1: 25283 Trace/BPT trap: 5       /Users/bbizic/QT5_11_Build/bin/uic -d Forms/advancedviewsettingsdialog.ui
dyld: Library not loaded: /Users/bbizic/Qt5.2.0/5.2.0/clang_64/lib/QtCore.framework/Versions/5/QtCore
  Referenced from: /Users/bbizic/QT5_11_Build/bin/uic
  Reason: image not found

So what happened is QMake called tool named "uic" to convert .UI into C++ header files. Uic returned error saying it can't find dependent library from absolute path where it was originally installed. If we run "otool -L" tool against uic we can see where it is actually looking for this library.

otool -L uic

uic:
	/Users/bbizic/Qt5.2.0/5.2.0/clang_64/lib/QtCore.framework/Versions/5/QtCore (compatibility version 5.2.0, current version 5.2.0)
	/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 56.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)

Only way I found to work around this was to use install_name_tool. I am not saying it's the only or the best way, it's just the way I solved it. install_name_tool is command line tool which comes, I think with installation of XCode. 

install_name_tool

It allows you to change those absolute paths inside dynamic libraries or executables. 

There are several ways of locating shared libraries:

  1. @executable_path: relative to main executable
  2. @loader_path: relative to the referring binary
  3. @rpath: relative to any of a list of paths
Syntax is as follows:
install_name_tool -change /currentPath @rpath/newpath myexecutable

Note that first parameter after -change must be exactly what is inside executable. 

See man dyld and man install_name_tool for more information.

 

Finally when uic tool is patched with correct paths you can run your Qmake.

After Qmake is finished generating make files you run following command to finally build your project.

make -f [Path to your make file] [debug/release]

Finally on Mac OS X deploying necessary Qt libraries with your application can be done using tool macdeployqt. You can read more about deployment on Mac Os X here. Since this tools is also part of QT installation bin folder it may also need be patched with correct paths with install_name_tool like described above. 

That is it, it should compile and run. If you find better solution or any other thoughts or question on subject please leave comment below.

About me

Bizic Bojan is Freelance Software Development Consultant with focus on C++, Qt and .NET based software solutions development. 

 

Disclaimer:

The opinions expressed herein are my own personal opinions and do not represent my employer’s view in any way.

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.