Qt stylesheet guidance

Style sheets

Layout Management

How to tips

How to recompute Qt style-sheet based on Q_PROPERTY:

void QControl::RedrawWidgetStylesheet()
{
	style()->unpolish(this);
	style()->polish(this);
	update();
}

Problem with Qt application on MacOS Retina display

Standard Qt application on retina display looks ugly. Fuzzy fonts and images. This is how ORM Designer originaly looks on Retina:

ORM Designer on Retina before optimizations

Improve font autoscaling

First step how to improve Qt application on retina is by adding following definise to Info.plist

NSPrincipalClass
NSApplication
How to update Info.plist

If you’re updating existing application, it’s necessary to copy application to different location after Info.plist update. MacOS cache all .plist files and without that update will not be apply.

After applying this fix, this is how application looks:

How ORM Designer looks after fix

External sources

Qt QtDBus compilation

To add DBUS to Qt compilation, add following switch

./configure -opensource -dbus ....

If you get following error:

The QtDBus module cannot be enabled because libdbus-1 version 0.93 was not found.
Turn on verbose messaging (-v) to ./configure to see the final report.
If you believe this message is in error you may use the continue
switch (-continue) to ./configure to continue.

It’s necessary to install libsbus:

sudo apt-get install libdbus-1-3
Reading package lists... Done
Building dependency tree
Reading state information... Done
libdbus-1-3 is already the newest version.

and also developer package:

sudo apt-get install libdbus-1-dev

Now everything should work correctly.

Qt and Google breakpad Windows/Linux/MacOS

Integration of Google breakpad on any platform is really challenge. I didn’t figure out how to do it by using recommended ways. So I did it my way 😉

Create your own Qt project

As first thing I did when integrating Qt and Breakpad is creation of my own .pri file. I manually add all required files from Breakpad one-by-one, starting with src/platform/exception_handler continuing with all files included in previous ones. The complete .pri file you will find at end of this post.

After that I created simple CCrashHandler (inspired by qt-breakpad project) class which register exception handler. It’s necessary to implement this handler for each platform separately because each platform have different parameters in google_breakpad::ExceptionHandler() constructor. CrashHandler source code you can find also at end of this post.

Using Breakpad on Linux:

Article about Linux integration.

Notes Linux

  • GCC 4.6.3
  • Ubuntu 12.04.1 32/64bit

Get and Compile Breakpad

#get breakpad latest version
svn checkout http://google-breakpad.googlecode.com/svn/trunk/ google-breakpad-read-only

#compile all breakpad tools for extracting symbol files
./configure
make
sudo make install

#when compilation is ready, you should have installed file dump_syms from original directory /src/tools/linux/dump_syms in your /usr/bin
dump_syms

Generate symbol file and test debug info

Now how to generate and use symbol file:

#generate .sym file
dump_syms ./Application > Application.sym

#store sym file in the correct location
#this step is necessary. Without that minidump_stackwalk tool doesn't work

head -n1 Application.sym
#Result: MODULE Linux x86_64 6EDC6ACDB282125843FD59DA9C81BD830 Application

mkdir -p ./symbols/Application/6EDC6ACDB282125843FD59DA9C81BD830
mv Application.sym ./symbols/Application/6EDC6ACDB282125843FD59DA9C81BD830

#show stack with using minidump_stackwalk tool
minidump_stackwalk ./crash.dmp ./symbols

Result of minidump_stackwalk tool can look like this:

Use additional tools

There is script written by Mozzila corp which simplifies extracting and storing .sym file:
http://mxr.mozilla.org/mozilla-central/source/toolkit/crashreporter/tools/symbolstore.py?raw=1

Now with this script, you can do only one step instead of all described above.

python /path/to/script/symbolstore.py /usr/local/bin/dump_syms ./symbol-storage ./Application

This command correctly creates folder Application/uuid in path symbol-storage and copy generated .sym file inside.

Using Breakpad on MacOS:

To integrate Breakpad and Qt on MacOS I used the same article as for Linux because I’m using gcc and linux-like development toolchain. For native XCode integration you have to probably use this article.

Notes MacOS

  • GCC 4.6.3
  • Qt creator toolchain
  • Mac OS 10.7.3 64bit
  • Don’t using any of XCode tool chaing

Do almost all steps like on Linux integration. There is one exception, because you can’t compile Breakpad tools using linux-like configure&make. You have to build these tools with XCode.

Compile google_breakpad/src/tools/mac/dump_syms/dump_syms.xcodeproj with XCode

Before I managed to successfully compile dump_syms in XCode, I have to update Architectures Debug/Release to 64-bit Intel and BaseSDK to “Latest Mac OS X”. I also fix all issues displayed in the left part of XCode window.

Next step is change compilation type from Debug to Release. After a short searching I found a way in “Schema->Edit schema->Build configuration”.

To be honest, I was not sure what I did, but it worked ;-). Maybe there is some easier way how to compile dump_syms in xcode, but I don’t know how. This was my first contact with XCode ;-).

The application is compiled to path “/Users/User/Library/Developer/Xcode/DerivedData/dump_syms-bjvr/Build/Products/Release/dump_syms”. Now copy dump_syms to safe location from where you will use it.

The rest steps are same like in the Linux.

Using Breakpad on Windows

Notes Windows

Article about Windows integration.

  • Compile with Visual Studio 2010
  • Post-mortem debugging is done also via VS2010
  • Google breakpad under windows generates common .dmp files which can by simply load directly to VS

Generate symbol files and test debug info

You have to choices for Windows. First option is use standard .PDB and .DMP files mechanism in VS. Second is use the same way how you can did it for Linux and Mac.

First way: Let compiler generate .PDB file. Store this files and use them later in VS together with your crash dump.

Second way: Use dump_sys.exe. This file is located in the google breakpad directory tree: google-breakpad\src\tools\windows\binaries\dump_syms.exe .

dump_syms.exe AtomixDevelopment.exe >AtomixDevelopment.sym

And also like under Linux, you can use Python script symbolstore.py to extract symbols to correct directory:

python.exe symbolstore.py binaries\dump_syms.exe .\Symbols AtomixDevelopment.exe

Minidump stackwalk for windows:
http://code.google.com/p/pcxfirefox/source/browse/trunk/betterpgo/talos/talos/breakpad/win32/minidump_stackwalk.exe?r=19

The best thing at end

The most amazing thing I found about dump_syms is their platform independence. If you correctly extract symbol file on each platform and store it in one location, you can analyze any crashdump on one location. No matter what is source platform, you will analyze .dmp file by using minidump_stackwalk ./crash.dmp ./symbols.

How to include CrashHandler directly to your project

This is what I get when I follow all includes from .h and .cpp files starting with exception_handler.h for all platforms. With small modification you can include it to your project and your application will be fully breakpad-able ;-).

# ---------- HEADER -----------------------------------------------------------
OTHERS   += $$PWD/axCrashHandler.pri
HEADERS += $$PWD/CrashHandler.h
SOURCES += $$PWD/CrashHandler.cpp

BREAKPAD_PATH=$$EXTERNAL_LIBRARIES_PATH/breakpad-qt
INCLUDEPATH += $$BREAKPAD_PATH/src

OSMAC {
  HEADERS += $$BREAKPAD_PATH/src/client/mac/handler/exception_handler.h
  HEADERS += $$BREAKPAD_PATH/src/client/mac/crash_generation/crash_generation_client.h
  HEADERS += $$BREAKPAD_PATH/src/client/mac/crash_generation/crash_generation_server.h
  HEADERS += $$BREAKPAD_PATH/src/client/mac/crash_generation/client_info.h
  HEADERS += $$BREAKPAD_PATH/src/client/mac/handler/minidump_generator.h
  HEADERS += $$BREAKPAD_PATH/src/client/mac/handler/dynamic_images.h
  HEADERS += $$BREAKPAD_PATH/src/client/mac/handler/breakpad_nlist_64.h
  HEADERS += $$BREAKPAD_PATH/src/client/mac/handler/mach_vm_compat.h
  HEADERS += $$BREAKPAD_PATH/src/client/minidump_file_writer.h
  HEADERS += $$BREAKPAD_PATH/src/client/minidump_file_writer-inl.h
  HEADERS += $$BREAKPAD_PATH/src/common/mac/macho_utilities.h
  HEADERS += $$BREAKPAD_PATH/src/common/mac/byteswap.h
  HEADERS += $$BREAKPAD_PATH/src/common/mac/MachIPC.h
  HEADERS += $$BREAKPAD_PATH/src/common/mac/scoped_task_suspend-inl.h
  HEADERS += $$BREAKPAD_PATH/src/common/mac/file_id.h
  HEADERS += $$BREAKPAD_PATH/src/common/mac/macho_id.h
  HEADERS += $$BREAKPAD_PATH/src/common/mac/macho_walker.h
  HEADERS += $$BREAKPAD_PATH/src/common/mac/macho_utilities.h
  HEADERS += $$BREAKPAD_PATH/src/common/mac/bootstrap_compat.h
  HEADERS += $$BREAKPAD_PATH/src/common/mac/string_utilities.h
  HEADERS += $$BREAKPAD_PATH/src/common/linux/linux_libc_support.h
  HEADERS += $$BREAKPAD_PATH/src/common/string_conversion.h
  HEADERS += $$BREAKPAD_PATH/src/common/md5.h
  HEADERS += $$BREAKPAD_PATH/src/common/memory.h
  HEADERS += $$BREAKPAD_PATH/src/common/using_std_string.h
  HEADERS += $$BREAKPAD_PATH/src/common/convert_UTF.h
  HEADERS += $$BREAKPAD_PATH/src/processor/scoped_ptr.h
  HEADERS += $$BREAKPAD_PATH/src/google_breakpad/common/minidump_exception_mac.h
  HEADERS += $$BREAKPAD_PATH/src/google_breakpad/common/breakpad_types.h
  HEADERS += $$BREAKPAD_PATH/src/google_breakpad/common/minidump_format.h
  HEADERS += $$BREAKPAD_PATH/src/google_breakpad/common/minidump_size.h
  HEADERS += $$BREAKPAD_PATH/src/third_party/lss/linux_syscall_support.h

  SOURCES += $$BREAKPAD_PATH/src/client/mac/handler/exception_handler.cc
  SOURCES += $$BREAKPAD_PATH/src/client/mac/crash_generation/crash_generation_client.cc
  SOURCES += $$BREAKPAD_PATH/src/client/mac/crash_generation/crash_generation_server.cc
  SOURCES += $$BREAKPAD_PATH/src/client/mac/handler/minidump_generator.cc
  SOURCES += $$BREAKPAD_PATH/src/client/mac/handler/dynamic_images.cc
  SOURCES += $$BREAKPAD_PATH/src/client/mac/handler/breakpad_nlist_64.cc
  SOURCES += $$BREAKPAD_PATH/src/client/minidump_file_writer.cc
  SOURCES += $$BREAKPAD_PATH/src/common/mac/macho_id.cc
  SOURCES += $$BREAKPAD_PATH/src/common/mac/macho_walker.cc
  SOURCES += $$BREAKPAD_PATH/src/common/mac/macho_utilities.cc
  SOURCES += $$BREAKPAD_PATH/src/common/mac/string_utilities.cc
  SOURCES += $$BREAKPAD_PATH/src/common/mac/file_id.cc
  SOURCES += $$BREAKPAD_PATH/src/common/mac/MachIPC.mm
  SOURCES += $$BREAKPAD_PATH/src/common/mac/bootstrap_compat.cc
  SOURCES += $$BREAKPAD_PATH/src/common/md5.cc
  SOURCES += $$BREAKPAD_PATH/src/common/string_conversion.cc
  SOURCES += $$BREAKPAD_PATH/src/common/linux/linux_libc_support.cc
  SOURCES += $$BREAKPAD_PATH/src/common/convert_UTF.c
  LIBS += /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
  LIBS += /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices
  #breakpad app need debug info inside binaries
  QMAKE_CXXFLAGS+=-g
}

OSLIN {
  HEADERS += $$BREAKPAD_PATH/src/client/linux/handler/exception_handler.h
  HEADERS += $$BREAKPAD_PATH/src/client/linux/crash_generation/crash_generation_client.h
  HEADERS += $$BREAKPAD_PATH/src/client/linux/handler/minidump_descriptor.h
  HEADERS += $$BREAKPAD_PATH/src/client/linux/minidump_writer/minidump_writer.h
  HEADERS += $$BREAKPAD_PATH/src/client/linux/minidump_writer/line_reader.h
  HEADERS += $$BREAKPAD_PATH/src/client/linux/minidump_writer/linux_dumper.h
  HEADERS += $$BREAKPAD_PATH/src/client/linux/minidump_writer/linux_ptrace_dumper.h
  HEADERS += $$BREAKPAD_PATH/src/client/linux/minidump_writer/directory_reader.h
  HEADERS += $$BREAKPAD_PATH/src/client/linux/log/log.h
  HEADERS += $$BREAKPAD_PATH/src/client/minidump_file_writer-inl.h
  HEADERS += $$BREAKPAD_PATH/src/client/minidump_file_writer.h
  HEADERS += $$BREAKPAD_PATH/src/common/linux/linux_libc_support.h
  HEADERS += $$BREAKPAD_PATH/src/common/linux/eintr_wrapper.h
  HEADERS += $$BREAKPAD_PATH/src/common/linux/ignore_ret.h
  HEADERS += $$BREAKPAD_PATH/src/common/linux/file_id.h
  HEADERS += $$BREAKPAD_PATH/src/common/linux/memory_mapped_file.h
  HEADERS += $$BREAKPAD_PATH/src/common/linux/safe_readlink.h
  HEADERS += $$BREAKPAD_PATH/src/common/linux/guid_creator.h
  HEADERS += $$BREAKPAD_PATH/src/common/linux/elfutils.h
  HEADERS += $$BREAKPAD_PATH/src/common/linux/elfutils-inl.h
  HEADERS += $$BREAKPAD_PATH/src/common/using_std_string.h
  HEADERS += $$BREAKPAD_PATH/src/common/memory.h
  HEADERS += $$BREAKPAD_PATH/src/common/basictypes.h
  HEADERS += $$BREAKPAD_PATH/src/common/memory_range.h
  HEADERS += $$BREAKPAD_PATH/src/common/string_conversion.h
  HEADERS += $$BREAKPAD_PATH/src/common/convert_UTF.h
  HEADERS += $$BREAKPAD_PATH/src/google_breakpad/common/minidump_format.h
  HEADERS += $$BREAKPAD_PATH/src/google_breakpad/common/minidump_size.h
  HEADERS += $$BREAKPAD_PATH/src/google_breakpad/common/breakpad_types.h
  HEADERS += $$BREAKPAD_PATH/src/processor/scoped_ptr.h
  HEADERS += $$BREAKPAD_PATH/src/third_party/lss/linux_syscall_support.h
  SOURCES += $$BREAKPAD_PATH/src/client/linux/crash_generation/crash_generation_client.cc
  SOURCES += $$BREAKPAD_PATH/src/client/linux/handler/exception_handler.cc
  SOURCES += $$BREAKPAD_PATH/src/client/linux/handler/minidump_descriptor.cc
  SOURCES += $$BREAKPAD_PATH/src/client/linux/minidump_writer/minidump_writer.cc
  SOURCES += $$BREAKPAD_PATH/src/client/linux/minidump_writer/linux_dumper.cc
  SOURCES += $$BREAKPAD_PATH/src/client/linux/minidump_writer/linux_ptrace_dumper.cc
  SOURCES += $$BREAKPAD_PATH/src/client/linux/log/log.cc
  SOURCES += $$BREAKPAD_PATH/src/client/minidump_file_writer.cc
  SOURCES += $$BREAKPAD_PATH/src/common/linux/linux_libc_support.cc
  SOURCES += $$BREAKPAD_PATH/src/common/linux/file_id.cc
  SOURCES += $$BREAKPAD_PATH/src/common/linux/memory_mapped_file.cc
  SOURCES += $$BREAKPAD_PATH/src/common/linux/safe_readlink.cc
  SOURCES += $$BREAKPAD_PATH/src/common/linux/guid_creator.cc
  SOURCES += $$BREAKPAD_PATH/src/common/linux/elfutils.cc
  SOURCES += $$BREAKPAD_PATH/src/common/string_conversion.cc
  SOURCES += $$BREAKPAD_PATH/src/common/convert_UTF.c
  #breakpad app need debug info inside binaries
  QMAKE_CXXFLAGS+=-g
}

OSWIN {
  BREAKPAD_PATH=q:/Applications/breakpad-qt/third-party/latest-breakpad
  INCLUDEPATH += $$BREAKPAD_PATH/src
  HEADERS += $$BREAKPAD_PATH/src/common/windows/string_utils-inl.h
  HEADERS += $$BREAKPAD_PATH/src/common/windows/guid_string.h
  HEADERS += $$BREAKPAD_PATH/src/client/windows/handler/exception_handler.h
  HEADERS += $$BREAKPAD_PATH/src/client/windows/common/ipc_protocol.h
  HEADERS += $$BREAKPAD_PATH/src/google_breakpad/common/minidump_format.h 
  HEADERS += $$BREAKPAD_PATH/src/google_breakpad/common/breakpad_types.h 
  HEADERS += $$BREAKPAD_PATH/src/client/windows/crash_generation/crash_generation_client.h 
  HEADERS += $$BREAKPAD_PATH/src/processor/scoped_ptr.h 

  SOURCES += $$BREAKPAD_PATH/src/client/windows/handler/exception_handler.cc
  SOURCES += $$BREAKPAD_PATH/src/common/windows/string_utils.cc
  SOURCES += $$BREAKPAD_PATH/src/common/windows/guid_string.cc
  SOURCES += $$BREAKPAD_PATH/src/client/windows/crash_generation/crash_generation_client.cc 
}

And this is the minimal implementation of your new CrashHandler. My ownd crash handler is much more sophisticate to perform reporting, sending crash dump etc. But for purposes of this article this is what you need (And also I don’t want to share my whole know-how here ;-)))

CrashHandler.h

#pragma once
#include <QtCore/QString>

namespace Atomix
{
	class CrashHandlerPrivate;
	class CrashHandler
	{
	public:
		static CrashHandler* instance();
    void Init(const QString&  reportPath);

		void setReportCrashesToSystem(bool report);
		bool writeMinidump();

	private:
		CrashHandler();
		~CrashHandler();
		Q_DISABLE_COPY(CrashHandler)
		CrashHandlerPrivate* d;
	};
}

CrashHandler.cpp

#include "CrashHandler.h"
#include <QtCore/QDir>
#include <QtCore/QProcess>
#include <QtCore/QCoreApplication>
#include <QString>

#if defined(Q_OS_MAC)
#include "client/mac/handler/exception_handler.h"
#elif defined(Q_OS_LINUX)
#include "client/linux/handler/exception_handler.h"
#elif defined(Q_OS_WIN32)
#include "client/windows/handler/exception_handler.h"
#endif

namespace Atomix
{
	/************************************************************************/
	/* CrashHandlerPrivate                                                  */
	/************************************************************************/
	class CrashHandlerPrivate
	{
	public:
		CrashHandlerPrivate()
		{
			pHandler = NULL;
		}

		~CrashHandlerPrivate()
		{
			delete pHandler;
		}

		void InitCrashHandler(const QString& dumpPath);
		static google_breakpad::ExceptionHandler* pHandler;
		static bool bReportCrashesToSystem;
	};

	google_breakpad::ExceptionHandler* CrashHandlerPrivate::pHandler = NULL;
	bool CrashHandlerPrivate::bReportCrashesToSystem = false;

	/************************************************************************/
	/* DumpCallback                                                         */
	/************************************************************************/
#if defined(Q_OS_WIN32)
	bool DumpCallback(const wchar_t* _dump_dir,const wchar_t* _minidump_id,void* context,EXCEPTION_POINTERS* exinfo,MDRawAssertionInfo* assertion,bool success)
#elif defined(Q_OS_LINUX)
	bool DumpCallback(const google_breakpad::MinidumpDescriptor &md,void *context, bool success)
#elif defined(Q_OS_MAC)
	bool DumpCallback(const char* _dump_dir,const char* _minidump_id,void *context, bool success)
#endif
	{
		Q_UNUSED(context);
#if defined(Q_OS_WIN32)
		Q_UNUSED(_dump_dir);
		Q_UNUSED(_minidump_id);
		Q_UNUSED(assertion);
		Q_UNUSED(exinfo);
#endif
		qDebug("BreakpadQt crash");

		/*
		NO STACK USE, NO HEAP USE THERE !!!
		Creating QString's, using qDebug, etc. - everything is crash-unfriendly.
		*/
		return CrashHandlerPrivate::bReportCrashesToSystem ? success : true;
	}

	void CrashHandlerPrivate::InitCrashHandler(const QString& dumpPath)
	{
		if ( pHandler != NULL )
			return;

#if defined(Q_OS_WIN32)
		std::wstring pathAsStr = (const wchar_t*)dumpPath.utf16();
		pHandler = new google_breakpad::ExceptionHandler(
			pathAsStr,
			/*FilterCallback*/ 0,
			DumpCallback,
			/*context*/
			0,
			true
			);
#elif defined(Q_OS_LINUX)
		std::string pathAsStr = dumpPath.toStdString();
		google_breakpad::MinidumpDescriptor md(pathAsStr);
		pHandler = new google_breakpad::ExceptionHandler(
			md,
			/*FilterCallback*/ 0,
			DumpCallback,
			/*context*/ 0,
			true,
			-1
			);
#elif defined(Q_OS_MAC)
		std::string pathAsStr = dumpPath.toStdString();
		pHandler = new google_breakpad::ExceptionHandler(
			pathAsStr,
			/*FilterCallback*/ 0,
			DumpCallback,
			/*context*/
			0,
			true,
			NULL
			);
#endif
	}

	/************************************************************************/
	/* CrashHandler                                                         */
	/************************************************************************/
	CrashHandler* CrashHandler::instance()
	{
		static CrashHandler globalHandler;
		return &globalHandler;
	}

	CrashHandler::CrashHandler()
	{
		d = new CrashHandlerPrivate();
	}

	CrashHandler::~CrashHandler()
	{
		delete d;
	}

	void CrashHandler::setReportCrashesToSystem(bool report)
	{
		d->bReportCrashesToSystem = report;
	}

	bool CrashHandler::writeMinidump()
	{
		bool res = d->pHandler->WriteMinidump();
		if (res) {
			qDebug("BreakpadQt: writeMinidump() successed.");
		} else {
			qWarning("BreakpadQt: writeMinidump() failed.");
		}
		return res;
	}

	void CrashHandler::Init( const QString& reportPath )
	{
		d->InitCrashHandler(reportPath);
	}
}

Main.cpp

And here is how to use it:

#include "axCore/axCrashHandler/CrashHandler.h"
#include <QDebug>
#include <QCoreApplication>
#include <iostream>

int buggyFunc()
{
	delete reinterpret_cast<QString*>(0xFEE1DEAD);
	return 0;
}

int main(int argc, char * argv[])
{
	qDebug() << "App start";
	QCoreApplication app(argc, argv);

#if defined(Q_OS_WIN32)
	Atomix::CrashHandler::instance()->Init("c:\\dump");
#elif defined(Q_OS_LINUX)
	Atomix::CrashHandler::instance()->Init("/Users/dev/dump");
#elif defined(Q_OS_MAC)
	Atomix::CrashHandler::instance()->Init("/Users/User/dump");
#endif
	
	qDebug() << "CrashHandlerSet";
	buggyFunc();
	return 0;
}

QWidget: Must construct a QApplication before a QPaintDevice

Today I encounter this error after applying macdeployqt to my application. There is a lot of questions about this topic on google but not very answers.

The most important thing is setup following variable.

export DYLD_PRINT_LIBRARIES=1 

Using this you will se which libraries are loaded during execution of your application. The interesting thing is, that I see loading a libraries from their original directories instead of Application.app. But when I temporary moved these libraries from their location, libraries begin to load from then bundle location.

The reason for my “QWidget: Must construct a QApplication before a QPaintDevice” was executing an older version of QTitanRibbon from shared libraries path instead of the newest one compiled directly to /usr/lib.
The second reason for this issue was caused by another path in “export DYLD_LIBRARY_PATH”. It seems that application search for libraries first in DYLD_LIBRARY_PATH and after in paths from their inner records.

To turn off this debug output, use:

unset DYLD_PRINT_LIBRARIES

How to deploy Qt application on MacOS – part II

In first part of this article I introduced a manual way how to deploy MacOS application. Because doing all these stuff manually was a lot of hand work, Qt introduced small tool called macdeployqt.

This utility looks very good, but also have some drawbacks. As first I will show you how utility works:

macdeployqt Application.app

That’s all ;-). This command should do all the hard work for you. But in some cases it didn’t.

ERROR: no file at "/usr/lib/libboost_iostreams.dylib"
ERROR: no file at "/usr/lib/libboost_filesystem.dylib"
ERROR: no file at "/usr/lib/libboost_system.dylib"
ERROR: no file at "/usr/lib/libboost_thread.dylib"
ERROR: no file at "/usr/lib/libboost_date_time.dylib"
ERROR: no file at "/usr/lib/libboost_regex.dylib"
ERROR: no file at "/usr/lib/libboost_chrono.dylib"
ERROR: no file at "/usr/lib/libyaml-cpp.0.2.dylib"

The problem is that libraries mentioned in the previous list doesn’t has included absolute path. If you inspecit this library, you will se this:

otool -L libboost_regex.dylib
libboost_regex.dylib:
	libboost_regex.dylib (compatibility version 0.0.0, current version 0.0.0)
	/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 52.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)

And this is the problem. Inner path in library is only filename without full path, so when we link library with our executable, our executable also has only relative path. And when macdeployqt try to find this library, automatically try to search in /usr/lib.

One possible solution is copy libraries to /usr/lib. Second solution is update paths in libraries to absolute paths. The third way is fix library linker to include full paths in libraries.

I found out that the simplies way how to fix it is call install_name_tool after library compilation and fix the path (don’t forget to update library paths inside other library paths!)

  install_name_tool -id "$SHL_PATH/boost/lib/libboost_chrono.dylib" $SHL_PATH/boost/lib/libboost_chrono.dylib
  install_name_tool -id "$SHL_PATH/boost/lib/libboost_date_time.dylib" $SHL_PATH/boost/lib/libboost_date_time.dylib
  ...
  install_name_tool -change "libboost_system.dylib" "$SHL_PATH/boost/lib/libboost_system.dylib" $SHL_PATH/boost/lib/libboost_chrono.dylib
  install_name_tool -change "libboost_system.dylib" "$SHL_PATH/boost/lib/libboost_system.dylib" $SHL_PATH/boost/lib/libboost_filesystem.dylib

So, now when we have updated all libraries, we can try to create application bundle again

macdeployqt AtomixDevelopment.app/

And if we did everything correctly, now we should have a working app ;-).

Troubleshooting

In some cases you can get error “Permission defined”, “Bad file descriptor” or other similar errors when using macdeployqt.

ERROR: "install_name_tool: can't open input file: OrmDesigner2.app/Contents/Frameworks//libssl.1.0.0.dylib for writing (Permission denied)
install_name_tool: can't lseek to offset: 0 in file: OrmDesigner2.app/Contents/Frameworks//libssl.1.0.0.dylib for writing (Bad file descriptor)
install_name_tool: can't write new headers in file: OrmDesigner2.app/Contents/Frameworks//libssl.1.0.0.dylib (Bad file descriptor)
install_name_tool: can't close written on input file: OrmDesigner2.app/Contents/Frameworks//libssl.1.0.0.dylib (Bad file descriptor)

The reason is insufficient permissions for modifying copied libraries. Simple execute

sudo macdeployqt

and everything should work ok. Another workaround for this issue is using newer version of Qt. I figured out that on MacOS I wrongly use Qt 4.7.4. When I correct this to version Qt 4.8.2 this issue was solved.

DMG file

If you want to create simply DMG file, simply call macdeployqt with param –dmg. But this dmg doesn’t look so good:

This will need more investigation how to make DMG files nicer ;-).

Notes

  • Everytime you run macdeployqt run it on clean folder. The best way is to call rm -rf Application, compile app and than run macdeployqt

Notes to bug in macdeployqt

The problem is caused by invalid permissions. Copied libraries have the same permissions like in the source location. There are three different solutions:

  1. Use sudo and execute macdeployqt with root permissions
  2. Update external libraries permissions to rw, so also copies will have a correct permissions
  3. Update macdeployqt script. In file shared.cpp on line 91 is method copyFilePrintStatus. Here is a necessary to update copied files permissions to correct rw

In my case the problem was in file liblzma.5.dylib

Frameworks/liblzma.5.dylib is not writable (Permission denied)

Orinal library is located in path:

/usr/local/Cellar/xz/5.0.4/lib

How to deploy Qt application on MacOS – part I

As I wrote yesterday, I found the way how to proceed deployment on the Linux system. Today I have to manage it the same on the MacOS systems.

Note: the simplest way is introduced in the second part of this article.

The first usefull command for tracking dependencies between your application and other shared libraries is otool:

#path to executable, not .App directory!
otool -L Application.app/Contents/MacOS/AppExecutable

Now we can see paths to our libraries. The bad news is that MacOS doesn’t have any -RPATH and $ORIGIN. The good news is utility install_name_tool and variable @executable_path which works similar like $ORIGIN.
Note: For newer versions of MacOS there are more variables like @executable_path. On version 10.4 was introduced @loader_path and on version 10.5 apple introduce @rpath variable.

The step-by-step solution

As first step it’s necessary to upload all dependent libraries directly to the Application.app/Contents/MacOS/. For list all required files use otool:

cd Application.app/Contents/MacOS
otool -L AppExecutable

When we have copied all libraries, we need to change paths for and in all libraries.

Here is example how to set libxml2 library. As first step we will check how is library referred.

$user otool -L AtomixDevelopment

AtomixDevelopment:
	libboost_iostreams.dylib (compatibility version 0.0.0, current version 0.0.0)
	libboost_filesystem.dylib (compatibility version 0.0.0, current version 0.0.0)
	/Users/User/dev/SharedLibraries/libxml/lib/libxml2.2.dylib (compatibility version 10.0.0, current version 10.8.0)
	/Users/User/dev/SharedLibraries/libxslt/lib/libxslt.1.dylib (compatibility version 3.0.0, current version 3.26.0)
	/Users/User/dev/SharedLibraries/libxslt/lib/libexslt.0.dylib (compatibility version 9.0.0, current version 9.15.0)

And Library:

User$ otool -L libxml2.2.dylib
libxml2.2.dylib:
	/Users/User/dev/SharedLibraries/libxml/lib/libxml2.2.dylib (compatibility version 10.0.0, current version 10.8.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
	/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5)
	/usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)

When we checked libxml2.2.dylib library with otool, we can see that library has hardocoded our development path. This isn’t good ;-). So we now change this value to path relative to the executable and again verify this inner path.

install_name_tool -id "@loader_path/libxml2.2.dylib" libxml2.2.dylib
otool -L libxml2.2.dylib
libxml2.2.dylib:
	@loader_path/libxml2.2.dylib (compatibility version 10.0.0, current version 10.8.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
	/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5)
	/usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)

As you can see, now is path (first line) set up to reffer @loader_path/libxml2.2.dylib. It’s correct now. As second step we have to update path in our executable, because also there is path hardcoded to our library repository.

install_name_tool -change "/Users/User/dev/ExternalLibraries/../SharedLibraries/libxml/lib/libxml2.2.dylib" "@loader_path/libxml2.2.dylib" AtomixDevelopment

otool -L AtomixDevelopment
AtomixDevelopment:
	libboost_iostreams.dylib (compatibility version 0.0.0, current version 0.0.0)
	libboost_filesystem.dylib (compatibility version 0.0.0, current version 0.0.0)
	@loader_path/libxml2.2.dylib (compatibility version 10.0.0, current version 10.8.0)
        ...

So, now we have changed also path in our executable. And now we need to do all these steps for all libraries….. ;-).

 

Notes

To follow the MacOS application convention it’s better to place libraries to folder Application.app/Contents/Frameworks. Here is a directory schema for Qt example application plugAndPaint.

External links

Qt application crash when compiled for 64-bit VS2010

Callstack

>	QtGui4.dll!00000000652f071f()
 	[Frames below may be incorrect and/or missing, no symbols loaded for QtGui4.dll]
 	QtGui4.dll!00000000652f07ae()
 	QtGui4.dll!0000000065326821()
 	QtGui4.dll!0000000065327f30()
 	QtGui4.dll!00000000653284ba()
 	QtGui4.dll!000000006532a5f5()
 	QtGui4.dll!00000000652f4792()
 	QtGui4.dll!000000006532f701()
 	QtGui4.dll!000000006535be71()
 	QtCore4.dll!0000000059a0ab9f()
 	QtGui4.dll!0000000065306eb9()
 	QtCore4.dll!00000000599f7903()
 	QtGui4.dll!0000000064eaffa2()
 	QtGui4.dll!0000000064eb2e56()
 	QtCore4.dll!00000000599f7792()
 	QtGui4.dll!0000000064eec2a3()
 	QtGui4.dll!0000000064eee85e()
 	QtGui4.dll!0000000064eec265()
 	QtGui4.dll!0000000064eee85e()
 	QtGui4.dll!0000000064eec265()
 	QtGui4.dll!0000000064eee85e()
 	QtGui4.dll!0000000064efa870()
 	QtGui4.dll!0000000065208727()
 	QtGui4.dll!000000006527a082()
 	QtGui4.dll!0000000064eaffb6()
 	QtGui4.dll!0000000064eb2e56()
 	QtCore4.dll!00000000599f7792()
 	QtGui4.dll!0000000064ef264c()
 	QtGui4.dll!0000000064ef26c7()
 	QtGui4.dll!0000000064ef26c7()
 	QtGui4.dll!0000000064ef26c7()
 	QtGui4.dll!0000000064efe30c()
 	OrmDesigner2.exe!000000013f95a2cf()
 	OrmDesigner2.exe!000000013f95dce2()
 	kernel32.dll!000000007794652d()
 	ntdll.dll!0000000077c8c521()

The bug is probably caused by wrong alignment or incorrect VS2010 compiler optimalization.

Found resources:
https://bugreports.qt.nokia.com//browse/QTBUG-11445
https://connect.microsoft.com/VisualStudio/feedback/details/573262/incorrect-alignment-with-x64-optimizer-and-movaps
http://support.microsoft.com/kb/2280741
https://connect.microsoft.com/VisualStudio/Downloads/DownloadDetails.aspx?DownloadID=31433

Workarounds:

First way – Update VS2010 mkspec for Qt

go to $QTDIR/mkspecs/win32-msvc2010/qmake.conf and replace -02 with -01 (ie. reducing the optimzation level).Also /Ob0 could be used.

Microsoft hotfix for VS

http://archive.msdn.microsoft.com/KB2280741
File patch name VS10-KB2268081-x86.exe

Install Visual Studio 2010 SP1

Service pack could also contains required fix.
http://www.microsoft.com/download/en/details.aspx?id=23691

Problem with Qt compilation under Windows 64-bit

This short post will show you how to resolve following error during Qt compilation on 64-bit windows:

Creating library ..\..\..\..\lib\QtWebKitd4.lib and object ..\..\..\..\lib\Qt
WebKitd4.exp
PluginViewWin.obj : error LNK2019: unresolved external symbol _HBeginPaint refer
enced in function "private: static struct HDC__ * __cdecl WebCore::PluginView::h
ookedBeginPaint(struct HWND__ *,struct tagPAINTSTRUCT *)" ([email protected]
[email protected]@@[email protected]@[email protected]@PEAUtagP [email protected]@@Z)
PluginViewWin.obj : error LNK2019: unresolved external symbol _HEndPaint referen
ced in function "private: static int __cdecl WebCore::PluginView::hookedEndPaint
(struct HWND__ *,struct tagPAINTSTRUCT const *)" ([email protected]@Web
[email protected]@[email protected]@[email protected]@@Z)
..\..\..\..\lib\QtWebKitd4.dll : fatal error LNK1120: 2 unresolved externals
NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual Studio 9.0\
VC\BIN\x86_amd64\link.EXE"' : return code '0x460'
Stop.
NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual Studio 9.0\
VC\BIN\nmake.exe"' : return code '0x2'

This error is caused by bug in QMAKE_HOST variable used in WebCore.pro file. This variable is tested to determine destination platform (x86 or x64).
This variable contains x86 value also on 64-bit builds instead of x86_64 value. This value is tested on the following line in a WebCore.pro file:

win32:!win32-g++*:contains(QMAKE_HOST.arch, x86_64):{

If you want to fix this error, simply replace QMAKE_HOST by QMAKE_TARGET variable. Updated line should look like this:

win32:!win32-g++*:contains(QMAKE_TARGET.arch, x86_64):{

After this update all required asm defines are correctly included and build of WebKit will be successful.

Update for this error

In newer versions of Qt there is already applied fix the error with QMAKE_HOST / QMAKE_TARGET, but also there is a new problem ;-(.

The Problem is in the following “if” definition:

   if(win32-msvc2005|win32-msvc2008):equals(TEMPLATE_PREFIX, "vc") {
        SOURCES += \
            plugins/win/PaintHooks.asm
    }

When you compile your project for MSVC 2010, .asm file isn’t included. It’s necessary to add win32-msvc2010 to this definition. So updated WebCore.pro for Qt 4.8 will look like this:

 if(win32-msvc2005|win32-msvc2008|win32-msvc2010):equals(TEMPLATE_PREFIX, "vc") {
        SOURCES += \
            plugins/win/PaintHooks.asm
    }

Next problem solution

Next problem which I found during compilation is wrongly set QMAKE_TARGET.arg. For unspecified reason, when I compile 64bit library in 64bit system in 64bit VS command line, this variable has value x86 instead of x86_64.
The problem is in the qmake project.cpp file. QMAKE trying to determine 64/32 bit version based on the %PATH% configuration. Qmake search for VS-PATH\bin\amd64 of \bin\x86_amd64 string. Unfortunately there is a bug in concating searched string. This is how looks original project.cpp file at line 3177:

QString vcBin64 = qgetenv("VCINSTALLDIR").append("\\bin\\amd64");
QString vcBinX86_64 = qgetenv("VCINSTALLDIR").append("\\bin\\x86_amd64");

The problem is, that VCINSTALLDIR contains \ at the end of the string, and then we append \ again. So, result is:

QString vcBin64 = qgetenv("VCINSTALLDIR");
if ( vcBin64.at( vcBin64.size()-1 ) != '\\' )
  vcBin64.append("\\");
vcBin64.append("bin\\amd64");

QString vcBinX86_64 = qgetenv("VCINSTALLDIR");
if ( vcBinX86_64.at( vcBinX86_64.size()-1 ) != '\\' )
  vcBinX86_64.append("\\");
vcBinX86_64.append("bin\\x86_amd64");

Now, QMake correctly determine 32/64 bit compilation and then correctly include PaintHooks.asm and your Qt WebCore will be sucesfully compiled.

How to deploy Qt application on MacOs

Missing dylib libraries

If your application using some externals dynamic libraries (called .dylib in MacOs system), you have to provide all these libraries. The simplest way is to provide these libraries into the Application.app directory.

Find out which libraries are used

Qt provide really useful tool called “otool” which serves to lots of purpose. One of them is to detect which shared libraries are required by our application. For this purpose otool has parameter -L. The usage is following:

otool -L ./Test1.app/Contents/MacOS/Test1

Output could looks like this:

./Test1:
        libboost_iostreams.dylib (compatibility version 0.0.0, current version 0.0.0)
        libboost_filesystem.dylib (compatibility version 0.0.0, current version 0.0.0)
        libboost_system.dylib (compatibility version 0.0.0, current version 0.0.0)
        libboost_thread.dylib (compatibility version 0.0.0, current version 0.0.0)
        libboost_date_time.dylib (compatibility version 0.0.0, current version 0.0.0)
        libboost_regex.dylib (compatibility version 0.0.0, current version 0.0.0)
        /Users/dev/dev/SharedLibraries/libiconv/lib/libiconv.2.dylib (compatibility version 8.0.0, current version 8.0.0)
        /Users/dev/dev/ExternalLibraries/../SharedLibraries/libxml/lib/libxml2.2.dylib (compatibility version 10.0.0, current version 10.8.0)
        /Users/dev/dev/ExternalLibraries/../SharedLibraries/libxslt/lib/libxslt.1.dylib (compatibility version 3.0.0, current version 3.26.0)
        /Users/dev/dev/ExternalLibraries/../SharedLibraries/libxslt/lib/libexslt.0.dylib (compatibility version 9.0.0, current version 9.15.0)
        /Users/dev/dev/ExternalLibraries/yaml-cpp-0.2.5/build/libyaml-cpp.0.2.dylib (compatibility version 0.2.0, current version 0.2.5)
        QtGui.framework/Versions/4/QtGui (compatibility version 4.7.0, current version 4.7.0)
        QtCore.framework/Versions/4/QtCore (compatibility version 4.7.0, current version 4.7.0)
        /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.9.0)
        /usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 625.0.0)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.0)

Output from “otool” is simmilar to linux tool “ldd”.

The Mac Deployment Tool

Because manual copying and configuring all shared libraries to our application could be difficult, Qt have deployment tool which do all work for us. Tool is called “macdeployqt” and can be found in /Developer/Tools/Qt/macdeployqt.

 /Developer/Tools/Qt/macdeployqt ./GeneratedFilesMacOs/Debug/Test1.app

Note: Every time when you call macdeployqt tool, project have to be build from scratch. But is sufficient when whole result directory is removed and linked again:

rm -rf ./GeneratedFilesMacOs/Debug/Test1.app/
make

How to configure Linux for Qt programming.

How to configure script for automatic run after login

Create .bash_profile file in your home directory.

nano ~/.bash_profile

And enter following content to the new created file:

 # ~/.bash_profile
# include .bashrc if it exists
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi

How to configure new search path

If you want to have bin folder in your home directory, enter following code snippet to your .bash_profile file:

PATH=$PATH;/Users/thomas/bin

Conclusion & Note

It’s possible that all of these task can be done in simpler way, or can’t be done at all. All comments and tips are based on my current experience with my first Linux configuration in my life 😉

How to configure MacOs for Qt programming.

How to configure script for automatic run after login

Create startup-script(or any other)file in your home directory. Then enter following command

nano startup-script
chmod 777 ./startup-script
sudo defaults write com.apple.loginwindow LoginHook /Users/thomas/.startup-script

Another solution is to create .bash_profile file, which is launched automatically by system.

or create

How to configure new search path

If you want to have bin folder in your home directory, enter following code snippet to your .profile file located in your home directory:

export PATH=$PATH:/Users/user_name/bin

Open file using:

nano ./.profile

Note:Path is separated by char “:”.

How to instal apt-get alternative for MacOS

Mac OS have apt-get alternative called “port“.

Additional notes

Realtek RTL81xx macOs version:

How to mount Windows drive from MacOs

As first, create some directory where you want to mount your shared drive

mkdir mnt
cd mnt
mkdir computer_d
mount -t smbfs //user_name:[email protected]/d computer_d

Conclusion & Note

It’s possible that all of these task can be done in simpler way, or can’t be done at all. All comments and tips are based on my current experience with my first MacOS configuration in my life 😉

Qt correct setup for QMAKESPEC, QTDIR and PATH

When you want to use different configurations for different platforms in your .pro file, it’s necessary to correctly setup three variables: QMAKESPEC, QTDIR and PATH.

Here is how to configure for different platforms:

Microsoft Windows:

In SystemPropertes -> Enviroment variables add/update following values:

QTDIR = P:\QT\4.7.0
PATH = %QTDIR%\bin
QMAKESPEC=%QTDIR%\mkspecs\win32-msvc2005
LANG = en_US

Unix Bourne shell: (not tested)

QMAKESPEC=/usr/local/qt/mkspecs/linux-g++
PATH=$PATH:/local/qmake/bin
export QMAKESPEC PATH</pre>

Unix C shell: (not tested)

setenv QMAKESPEC /usr/local/qt/mkspecs/linux-g++
setenv PATH $PATH:/local/qmake/bin</pre>

Note

You can optionally use LANG variable to setup QtCreator language.

Old qt 3.0 guide: http://doc.trolltech.com/3.0/qmake-guide.html
How to configure enviroment: http://doc.qt.nokia.com/4.0/qmake-environment-reference.html