Coder Social home page Coder Social logo

qmakepatch's Introduction

QmakePatch — a patching utility for QMake executables

Allows changing some hard-coded values in qmake.exe (regardless of executable file format, be it ELF, PE or whatever), like QT_INSTALL_PATH and Qt version string, without relying on external settings in INI files and Windows registry. This helps to keep different Qt installations side by side on a single machine at the same time, including different flavors of the same version, which is especially handful for cross-compilation and other custom scenarios.

Patching relies on the fact that qmake.exe is usually built with plenty of spare space reserved specifically for this (although Qt authors do not provide stock tools for patching). The exact amount of reserved space varies by build and even between values of different kinds, so patching will only succeed if all replacement values fit into their destination areas, including at least one null character at the end of each area to serve as value terminator. This will not be an issue most of the time, but there is always a possibility that some adjustments may fail: for example, if we ever need to change the version string from "4.8.9" to "4.8.10", there probably will be no room to accommodate that longer string, since no additional space is usually reserved for that variable.

Worth to note is that distinct QMake versions may differ significantly in the degree of possible customization. For example, QMake 3.x (for Qt 5.x) only has 3 variables that specify installation prefixes, and 2 of them are expected to have the same value. This is in contrast with Qmake 1.x (for Qt 3.x) and QMake 2.x (for Qt 4.x), which both had a dozen of independently customizable paths.

Related work

Qt patcher by Developpez.com, aka DVP Qt patcher (HTTP, FTP — links may not be visible on GitHub website), is suited for the same task, but has several shortcomings:

  • Implemented as Qt GUI application which requires user interaction and disallows automated processing.

  • Tailored to Qt 4.x, so will not work for Qt 3.x or Qt 5.x.

  • Rewrites predefined variables all at once, using hardcoded path suffixes.

  • Does not rewrite version string.

  • Attempts to patch mkspecs/default/qmake.conf as well.

  • Minimum to none error checking.

Packaging tools from the official Qt SDK, particularly patch_qmake_qt_key.py script, may be noted for its simplicity and straight-through approach:

  • Implemented as Python script to be invoked manually or in batch.

  • Rewrites only a single variable at once.

  • Does not rewrite version string.

In contrast to that, QmakePatch was developed with the following goals in mind:

  • Console application in pure C which may be invoked directly or by shell scripts for automation and reuse.

  • Support all Qt versions known to the date: 3.x (Qmake 1.x), 4.x (QMake 2.x), 5.x (QMake 3.x).

  • Rewrite only those variables specified by user, allowing any replacement values for flexible relocation.

  • Rewrite Qt version string if possible.

  • Thorough error checking. Patching will only be completed if every single adjustment succeeds.

  • Leave the job of modifying text configs to other tools, which are much more suited for that.

Obviously, qmake.exe may be patched manually with the use of hex editor, — QmakePatch just makes this process easier and faster.

Invocation

Command line syntax (may be obtained with -h or --help):

	qmakepatch {qmake.exe} {version} [name=value ...]

where:

  • qmake.exe is the path to QMake executable, like qmake or ./qmake-qt5.

  • version is the new version string to be written, like 4.8.4. Pass an empty argument to skip this patch.

  • name=value pairs constitute variable names and their new values, separated by equals sign = with no whitespaces around it, like qt_prfxpath=/opt/qt4.

Any number of name and value pairs may be specified in command line; if none are specified, then only the version string will be rewritten. As already said, version is a mandatory positional parameter and thus must be present at all times, but may be set to an empty string for keeping the version value intact. Passing an empty string to a program is usually accomplished with a pair of single or double quotes, like '' and "". Similarly, quoting a parameter allows passing values with spaces in them, — like "qt_prfxpath=C:\Program Files\Qt4", — but using such paths is strongly discouraged traditionally, as they tend to be error-prone; more legitimate example (in the techical sense, not in legal one) would be "qt_lcnsuser=Open Source".

Note that the location of version string differs by QMake (Qt) major release, so QmakePatch uses the supplied value to choose the search method, and it is therefore only possible to rewrite version strings within each supported branch — not between major versions. And, as with other variables, the replacement value length should not exceed the reserved area size, which is usually equal to the original value length, — that is, no extra room is allocated for version string.

The specified QMake executable file will only be rewritten if all specified variables were found and successfully reassigned to their new values. Otherwise the file will be left intact, a diagnostic message output to stderr, and an error code returned:

  • 0 — success;
  • 1 — invocation syntax error;
  • 2 — malformed data supplied by user, such as name-value pair or version;
  • 3 — generic failure, such as memory allocation error;
  • 4 — file-related error, such as failure to read or write qmake.exe;
  • 5 — failure to find or replace a variable value.

For sanity checking purposes, an empirical limit is placed on how large a reserved area for any single variable may ever be: this is currenly set to 4096 bytes, which is definitely beyond typical amounts of 256 to 512 bytes (which are, in turn, beyond limitations of popular file systems). If that assumption will become broken some day, patching will fail and a corresponding diagnostic message will be output; should that happen, the limit must be changed in the source code, — refer to INSTALL.md for compilation instructions afterwards.

Examples

Changing the version string:

	qmakepatch ./qmake-qt5 5.5.1

Changing the settings file location in Qmake 1.x (Qt 3.x):

	qmakepatch ./qmake "" qt_cnfpath=/etc/qt-3.3.3

Changing the settings file location and installation date in QMake 2.x (Qt 4.x):

	qmakepatch ./qmake "" qt_stngpath=/etc/qt-4.8.4 qt_instdate=2016-06-28

Complete patching of QMake 2.x to be used with a guest Qt 4.x kit for cross-compilation:

	#!/bin/sh

	XC_ROOTDIR='/opt/mcst/fs.e90-v8.2.6.33'		# Guest system root.
	QT_BASEDIR="${XC_ROOTDIR}/opt/qt4"
	QMAKE_FILE="${QT_BASEDIR}/bin/qmake"
	QT_VERSION='4.8.4'

	qmakepatch "$QMAKE_FILE" "$QT_VERSION" \
		"qt_prfxpath=${QT_BASEDIR}" \
		"qt_docspath=${QT_BASEDIR}/doc" \
		"qt_hdrspath=${QT_BASEDIR}/include" \
		"qt_libspath=${QT_BASEDIR}/lib" \
		"qt_plugpath=${QT_BASEDIR}/plugins" \
		"qt_impspath=${QT_BASEDIR}/imports" \
		"qt_datapath=${QT_BASEDIR}" \
		"qt_trnspath=${QT_BASEDIR}/translations" \
		"qt_stngpath=${XC_ROOTDIR}/etc/xdg" \
		"qt_xmplpath=${QT_BASEDIR}/examples" \
		"qt_demopath=${QT_BASEDIR}/demos"

As demonstrated in this example, when it comes to cross-compilation, there usually is no need to alter QT_INSTALL_BINS (qt_binspath) from its native value, because other Qt tools may be used right as they are and where they are, coupled with any foreign build of the same major-minor branch.

An overall list of variable names that are known to be available for customization is presented in the next section.

Technical details

Early Qt releases, namely version 1.x and 2.x, did not have QMake at all.

Qmake 1.x, available as a part of Qt 3.x, typically has the following variables:

	QT_INSTALL_PREFIX:         qt_nstpath=/usr/lib/qt-3.3.3
	[anonymous]                qt_binpath=.../bin
	[anonymous]                qt_docpath=.../doc
	[anonymous]                qt_hdrpath=.../include
	[anonymous]                qt_libpath=.../lib
	[anonymous]                qt_plgpath=.../plugins
	QT_INSTALL_DATA:           qt_datpath=...
	[anonymous]                qt_trnpath=.../translations
	[anonymous]                qt_cnfpath=/etc/qt-3.3.3
	QMAKE_MKSPECS:             ${QT_INSTALL_DATA}/mkspecs

To the left are well-known names, in upper case, which can be referenced in project and spec files as $$[QT_INSTALL_PREFIX] and queried from command line as qmake -query QT_INSTALL_PREFIX; variables marked as anonymous are only used internally by Qmake and cannot be queried. To the right are internal names, in lower case, with their sample values. Alternatively, an expression like ${QT_INSTALL_DATA}/mkspecs denotes a value computed at run time from another variable plus some hardcoded suffix, and thus not customizable.

QMake 2.x, available as a part of Qt 4.x, typically has the following variables:

	[anonymous]                qt_lcnsuser=Open Source
	[anonymous]                qt_lcnsprod=Open Source
	[anonymous]                qt_instdate=2014-10-16
	QT_INSTALL_PREFIX:         qt_prfxpath=/opt/qt4
	QT_INSTALL_DOCS:           qt_docspath=.../doc
	QT_INSTALL_HEADERS:        qt_hdrspath=.../include
	QT_INSTALL_LIBS:           qt_libspath=.../lib
	QT_INSTALL_BINS:           qt_binspath=.../bin
	QT_INSTALL_PLUGINS:        qt_plugpath=.../plugins
	QT_INSTALL_IMPORTS:        qt_impspath=.../imports
	QT_INSTALL_DATA:           qt_datapath=...
	QT_INSTALL_TRANSLATIONS:   qt_trnspath=.../translations
	QT_INSTALL_CONFIGURATION:  qt_stngpath=/etc/xdg
	QT_INSTALL_EXAMPLES:       qt_xmplpath=.../examples
	QT_INSTALL_DEMOS:          qt_demopath=.../demos
	QMAKE_MKSPECS:             ${QT_INSTALL_DATA}/mkspecs

QMake 3.x, available as a part of Qt 5.x, typically has the following variables:

	QT_INSTALL_PREFIX:         qt_prfxpath=/usr
	[QT_INSTALL_EPREFIX]:      qt_epfxpath=/usr
	QT_HOST_PREFIX:            qt_hpfxpath=/usr

	QT_SYSROOT:                [empty]
	QT_INSTALL_PREFIX:         ${QT_INSTALL_EPREFIX}
	QT_INSTALL_ARCHDATA:       ${QT_INSTALL_EPREFIX}/lib/qt5
	QT_INSTALL_DATA:           ${QT_INSTALL_EPREFIX}/share/qt5
	QT_INSTALL_DOCS:           ${QT_INSTALL_EPREFIX}/share/doc/packages/qt5
	QT_INSTALL_HEADERS:        ${QT_INSTALL_EPREFIX}/include/qt5
	QT_INSTALL_LIBS:           ${QT_INSTALL_EPREFIX}/lib
	QT_INSTALL_LIBEXECS:       ${QT_INSTALL_EPREFIX}/lib/qt5/libexec
	QT_INSTALL_BINS:           ${QT_INSTALL_EPREFIX}/lib/qt5/bin
	QT_INSTALL_TESTS:          ${QT_INSTALL_EPREFIX}/tests
	QT_INSTALL_PLUGINS:        ${QT_INSTALL_EPREFIX}/lib/qt5/plugins
	QT_INSTALL_IMPORTS:        ${QT_INSTALL_EPREFIX}/lib/qt5/imports
	QT_INSTALL_QML:            ${QT_INSTALL_EPREFIX}/lib/qt5/qml
	QT_INSTALL_TRANSLATIONS:   ${QT_INSTALL_EPREFIX}/share/qt5/translations
	QT_INSTALL_CONFIGURATION:  /etc/xdg
	QT_INSTALL_EXAMPLES:       ${QT_INSTALL_EPREFIX}/lib/qt5/examples
	QT_INSTALL_DEMOS:          ${QT_INSTALL_EPREFIX}/lib/qt5/examples
	QT_HOST_DATA:              ${QT_HOST_PREFIX}/lib/qt5
	QT_HOST_BINS:              ${QT_HOST_PREFIX}/usr/lib/qt5/bin
	QT_HOST_LIBS:              ${QT_HOST_PREFIX}/usr/lib

As can be seen, QMake 3.x provides almost two dozens of variables, but only few of them can be customized by patching the executable file. Particularly, QT_SYSROOT and QT_INSTALL_CONFIGURATION, although not being dependent of other variables, are not easy to search and replace automatically, — not only because they lack a constant “beacon” before them, but also because their original value may be an empty string.

As to QT_INSTALL_PREFIX and QT_INSTALL_EPREFIX, the latter name is neither official nor directly queryable, and both are supposed to hold the same value — similarly to prefix and exec-prefix in configuration scripts generated by GNU autoconf, which all default to either /usr or /usr/local, depending on operating system conventions. Otherwise, if their values differ, an additional “development” flavor is created for each variable:

	QT_INSTALL_PREFIX:         ${QT_INSTALL_EPREFIX}
	QT_INSTALL_PREFIX/dev:     ${QT_INSTALL_PREFIX}
	QT_INSTALL_ARCHDATA:       ${QT_INSTALL_EPREFIX}/lib/qt5
	QT_INSTALL_ARCHDATA/dev:   ${QT_INSTALL_PREFIX}/lib/qt5
	...
	QT_INSTALL_DEMOS:          ${QT_INSTALL_EPREFIX}/lib/qt5/examples
	QT_INSTALL_DEMOS/dev:      ${QT_INSTALL_PREFIX}/lib/qt5/examples

Therefore, both values should be kept in sync with each other, and that is the user's responsibility.

qmakepatch's People

Contributors

samsonovanton avatar

Watchers

 avatar

Forkers

cetium

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.