当前位置: 首页 > 工具软件 > Qmake > 使用案例 >

QMake的里里外外

郑哲彦
2023-12-01

Undocumented QMake
Jump to: navigation, search

Contents
1 Introduction
2 Undocumented variables
3 Custom tools
3.1 Further Examples
4 Config features
4.1 Custom install config
5 SUBDIRS projects
6 Undocumented modes
7 Undocumented functions
7.1 Program flow functions
7.2 Replace functions
8 Undocumented niceties
9 See also
9.1 About this Article

Introduction
qmake is a very powerful “meta-make” system that can be used to generate makefiles for a variety of compilers and platforms out of the same qmake project (.pro) file. The documentation for qmake has much improved since Qt3, but is still missing some information. This page aims to help with those examples.

Note that this information was written for Qt4. Some of it may work in Qt3. It should work in Qt5, or considered for removal.

Undocumented variables
The simplest sort of control that you can get over your generated makefile is by using the built-in variables that qmake provides. Of course, the challenge is that many of those variables are not listed in the documentation for qmake. But… you have a handy list of them all right there (along with a rich source of tricks and hacks written by the Trolls - I have mined it extensively for this page). In your qmake installation directory is a subdirectory called “mkspecs”. This contains the definitions for all the platform/compiler combinations that qmake supports (note that not all of them are “formally” supported!) There are directories named “features” at various points within this tree; in there you will find qmake code for many of the things you can enable via the CONFIG variable.

So if you are trying to figure out things like, “How do I change the name of the compiler that gets used in the makefile?” or “How can I change the way that file-copy is invoked for ‘make install’?” or things of that nature, the mkspecs directory is where you should look for the name of the variable you need to change.

Here are a few particularly useful ones (valid as of v4.3.4) discovered by digging through the qmake source:

DATE — the current date and time. (v4.3.4)
FILE — the current file name being parsed by qmake. (v4.3.4)
LINE — the current line number being parsed by qmake. (v4.3.4)
QMAKE_CACHE — the path to any .qmake.cache file in use. (v4.3.4)
DIR_SEPARATOR or QMAKE_DIR_SEP — A forward-slash or back-slash character, depending on the host platform.
IN_PWD — the base directory of the source tree. (v4.3.4)
QMAKE_NOFORCE — omit the use of the “FORCE” target.
QMAKE_utility — the command for a utility that will be assigned to the macro named utility in the generated Makefiles. The utility macro names are used as appropriate in the various standard targets. The value of these variables can be changed to specify different utility commands, and the variables - in $$variable_name or $(macro_name) form - can be use in defining commands for QMAKE_EXTRA_TARGETS. The utility names are
QMAKE_CHK_DIR_EXISTS
QMAKE_COPY
QMAKE_COPY_DIR
QMAKE_COPY_FILE
QMAKE_DEL_DIR
QMAKE_DEL_FILE
QMAKE_INSTALL_DIR
QMAKE_INSTALL_FILE
QMAKE_INSTALL_PROGRAM
QMAKE_LINK (This is used to link executables.)
QMAKE_LINK_SHLIB
QMAKE_MKDIR
QMAKE_MOVE
QMAKE_QMAKE
QMAKE_STRIP
QMAKE_SYMBOLIC_LINK (This is assigned to the SYMLINK macro.)
QMAKE_ARGS — the parameters used to invoke qmake.
QMAKE_PROJECT_DEPTH - when stacking a lot of *.pro (and sub folders) into a main *.pro it could be handy to set a value bigger then 4 to this variable, it seems this represents the number of sub folder that qmake refer to with relative paths (hardcoded is depth=4 in makefile.cpp) … this especially gets useful when using windows(nmake/jom) with space-characters in the root-path of the project
Custom tools
The documentation for qmake in Qt4 briefly mentions the possibility of custom “compilers”, but not much information is given to describe this.

There are a few special pseudo-variables that you can use inside of custom compilers. I say “pseudo-variables” for two reasons: first, they only use a single dollar-sign; and second, they are evaluated later than pretty much anything else you would want to use. For this reason, functions like $$replace(…) and operators like ~= will not do what you expect - they will act as if you are passing them an empty variable.

So hopefully the Trolls have already provided you with what you need…!

QMAKE_FILE_IN — this is the input filename(s), with path if provided, that the compiler is processing,
QMAKE_FILE_OUT — the contents of the “compiler.output” variable for the current value of ${QMAKE_FILE_IN} - that is to say, the current output file,
QMAKE_FILE_IN_BASE (or QMAKE_FILE_BASE) — the current input filename without extension,
QMAKE_FILE_IN_PATH (or QMAKE_FILE_PATH) — just the path of the current input file,
QMAKE_FILE_OUT_BASE — the current output filename without extension,
QMAKE_FILE_OUT_PATH — just the path of the current output file
QMAKE_FILE_EXT — the file extension of the input file, including the dot
The most basic custom tool definition usually looks something like this:

idl_c.output = ${QMAKE_FILE_IN}.c
idl_c.input = IDL
idl_c.commands = KaTeX parse error: Can't use function '$' in math mode at position 13: {QMAKE_IDL} $̲{QMAKE_FILE_IN}…{IDLFLAGS}
/h ${QMAKE_FILE_IN}.h /iid ${QMAKE_FILE_IN}.c
idl_c.variable_out = SOURCES
idl_c.name = MIDL

QMAKE_EXTRA_COMPILERS += idl_c
This example runs the Microsoft MIDL compiler on a .ODL file, and generates a .c and .h pair with the COM host information.

In order to define a custom tool, you must first pick a name for the compound variable (similar to a struct) to define. In the example above, I chose “idl.c”.

There are several properties which can be included in a custom tool definition:

.commands — specifies the command that should be run on each of the source files. Note that you will need to use a double $ on any normal qmake variables that you wish to expand (this does not include QMAKE_FILE_IN and friends),
.clean_commands — specifies the commands that should be executed to clean additionally generated output files,
.clean — sets the files that should be removed with make clean,
.depend_command — specifies the command that should be executed to generate dependency information,
.dependency_type — use one of the default dependency-walking algorithms built into qmake. As of version 4.3.3, legal options are TYPE_C and TYPE_UI.
.depends — sets the files that are dependencies for this step. The output of .depend_command, if used, should be specified here. This could also be used to check if the compiler executable itself has changed,
.input — sets the qmake variable that contains the list of files that should have this compiler run on them,
.name — this appears to just be an internal name used in qmake; just ensure you use a different value for each custom compiler,
.output — sets the output file name that the step will generate. The variable ${QMAKE_FILE_IN} may be used to base this on the input filename. It defaults to GENERATED_SOURCES,
.output_function — names a function defined with defineReplace that will be used to determine the output file name. The variable ${QMAKE_FILE_IN} will be passed to this function, and its return value will be used as the output file name. Make sure the function exists and actually returns something, or you will get misleading error messages.
.variable_out — the generated target files outputted from the build step will be added to this variable. In this case, the step generates a .c file so the files should be added to SOURCES.
.variables — not sure what this does…
.verify_function — connected to the function_verify flag in .CONFIG - but I am not sure what it does…
There is also a .CONFIG property, which itself has multiple special flags you can set using syntax identical to the main CONFIG variable:

combine — call the compiler with a list of all the files in the source variable, rather than once for each file,
explicit_dependencies — The comment in the source reads “compiler.CONFIG+=explicit_dependencies means that ONLY compiler.depends gets to cause Makefile dependencies”,
function_verify — see also .verify_function above,
ignore_no_exist — ignore (do not process) files in the input list which do not exist. If not set, a warning is reported and the input file is still processed,
moc_verify — I think this makes sure that the file should be run through the moc preprocessor before adding it as a moc target.
no_dependencies — do not do dependency generation on the files in the source variable,
no_link — the files that are created should not be added to OBJECTS — i.e., they are not compiled code which should be linked,
target_predeps — I think this makes sure that the custom compiler is run as the first thing in the project…
verify — not sure what this does. The custom compiler never gets invoked if enabled.
After you’ve defined the compound variable for the tool, you must then add that compound variable to QMAKE_EXTRA_COMPILERS. That signals qmake that it must look at the files you’ve specified and run that tool on them.

Further Examples
Here’s another (more unusual) example:

CFLAGS_FILE = . # Need to give some bogus input
compile_inp.output = compile.inp
compile_inp.input = CFLAGS_FILE
compile_inp.commands = echo >$(OBJECTS_DIR)compile.inp $(CXXFLAGS)
compile_inp.name = compile.inp
compile_inp.variable_out = JUNK
compile_inp.CONFIG = no_link
QMAKE_EXTRA_COMPILERS += compile_inp
This tool simply outputs the contents of the CXXFLAGS variable to a file called “compile.inp”. Since this tool is meant to generate a file of a fixed name, the variable passed into .input contains only “.” (current directory) which will always exist (but is not used anywhere in the rule). The construct $(foo) is used in this rule. This construct outputs a GNU Make or NMAKE format variable expansion, basically delaying the expansion of the variable until make or NMAKE is called on the generated makefile.

A way to compile different files with different CXXFLAGS (based on qt/src/gui/painting/painting.pri):

SOURCES_NOOPTIMIZE = somefile.cpp
nooptimize.name = nooptimize
nooptimize.input = SOURCES_NOOPTIMIZE
nooptimize.dependency_type = TYPE_C
nooptimize.variable_out = OBJECTS
nooptimize.output = Q M A K E V A R O B J E C T S D I R {QMAKE_VAR_OBJECTS_DIR} QMAKEVAROBJECTSDIR{QMAKE_FILE_IN_BASE} f i r s t ( Q M A K E E X T O B J ) n o o p t i m i z e . c o m m a n d s = {first(QMAKE_EXT_OBJ)} nooptimize.commands = first(QMAKEEXTOBJ)nooptimize.commands={QMAKE_CXX} $(CXXFLAGS) -O0 $(INCPATH) -c ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} # Note the -O0
QMAKE_EXTRA_COMPILERS += nooptimize
Of course you can specify different custom flags instead of the -O0 used to disable optimization.

And finally an example on how to call a batch file called “PreBuildEvent.bat” each time you compile your code (tested in VisualStudio, based on qt-creator-enterprise-src-3.1.0\share\qtcreator\static.pro):

PRE_BUILD_FILE = …/Applications/PreBuildEvents.bat

must use a variable as input

PHONY_DEPS = .
PreBuildEvent.input = PHONY_DEPS

use non-existing file here to execute every time

PreBuildEvent.output = phony.txt

the system call to the batch file

PreBuildEvent.commands = call $$PRE_BUILD_FILE

some name that displays during execution

PreBuildEvent.name = running Pre-Build steps…

“no_link” tells qmake we don’t need to add the output to the object files for linking, and “no_clean” means there is no clean step for them.

“target_predeps” tells qmake that the output of this needs to exist before we can do the rest of our compilation.

PreBuildEvent.CONFIG += no_link no_clean target_predeps

Add the compiler to the list of ‘extra compilers’.

QMAKE_EXTRA_COMPILERS += PreBuildEvent

Config features
There are several “switches” which can be added to the CONFIG variable which affect various behaviours of qmake (note that this doesn’t include CONFIG features specific to custom tools or installers):

app_bundle — makes the target into a bundle, instead of a standalone executable (Mac only).
compile_libtool — uses “libtool” for compiling and linking etc. instead of the normal compiler (*nix only).
echo_depend_creation — echoes messages to the screen during dependency creation (*nix only).
generate_pbxbuild_makefile — generates a makefile wrapper for the PowerBuilder project (Mac only).
GNUmake — lets the GNU make tool determine dependencies (*nix only).
lib_bundle — makes the target into a bundle, instead of a standalone library (Mac only).
no_autoqmake — prevents the outputted makefile from caling qmake if the .pro file has changed.
no_empty_targets — makes sure that SUBDIR-based projects don’t have targets that do nothing (they are filled in with “cd .” instead).
no_fix_and_continue — disables the GCC “fix and continue” feature (Mac only).
no_include_pwd — omits the current directory from the final INCLUDEPATH.
no_pb_munge_key — prevents qmake from MD5-hashing the project key (Mac only).
no_pbx_xcode — disables XCode support (Mac only).
no_smart_library_merge, no_lflags_merge — prevents removal of duplicate entries in the linker flags (*nix only).
no_zero_link — disables the GCC “zero-link” feature (Mac only).
object_parallel_to_source — recreate source folder tree for object files (replaces object_with_source).
object_with_source — outputs each object file into the same directory as its source file (replaced by object_parallel_to_source in the latest versions).
rpath_libdirs — adds QMAKE_LFLAGS_RPATH to the link flags (*nix only).
ordered — ensures that the projects are built in the order specified.
static_and_shared — generates makefiles for both static and shared builds. Note that shared_and_static is ignored. A primary makefile named M A K E F I L E ( M a k e f i l e b y d e f a u l t ) i s g e n e r a t e d a n d s u b s i d i a r y m a k e f i l e s n a m e d MAKEFILE (Makefile by default) is generated and subsidiary makefiles named MAKEFILE(Makefilebydefault)isgeneratedandsubsidiarymakefilesnamedMAKEFILE.StaticRelease and M A K E F I L E . S h a r e d R e l e a s e i f C O N F I G c o n t a i n s r e l e a s e ( t h e d e f a u l t f o r u n i x p l a t f o r m s ) , o r MAKEFILE.SharedRelease if CONFIG contains release (the default for unix platforms), or MAKEFILE.SharedReleaseifCONFIGcontainsrelease(thedefaultforunixplatforms),orMAKEFILE.StaticDebug and $$MAKEFILE.SharedDebug if CONFIG contains debug (the default for win32), or both if CONFIG contains debug_and_release (for a total of five makefiles) are generated. The primary makefile will contain a set of {static,shared}{debug,release}[-xxx] targets corresponding to the secondary makefiles that can be used to invoke the appropriate makefile rules. However the default first (as well as install, and uninstall) targets will only have a single prerequisite (only one library file is built by default) depending on the active CONFIG: static-release, static-debug, shared-release, or shared-debug; add static or shared to CONFIG, in addition to static_and_shared, and release or debug in addition to debug_and_release to get qmake to select the corresponding target for the first target prerequisite.
There are also several values that qmake will dynamically set in CONFIG when it is writing a makefile (not when XCode project files are being written) other than the primary Makefile - i.e. the additional subsidiary makefiles when debug_and_release and/or static_and_shared have been set:

build_pass — a subsidiary makefile is being written (build_pass is not set when the primary Makefile is being written).
Debug and DebugBuild — when debug_and_release has been set and the makefile filename extension contains “Debug”.
Release and ReleaseBuild — when debug_and_release has been set and the makefile filename extension contains “Release”.
Static and StaticBuild — when static_and_shared has been set and the makefile filename extension contains “Static”.
Shared and SharedBuild — when static_and_shared has been set and the makefile filename extension contains “Shared”.
When both debug_and_release and static_and_shared are used all four Debug/Release and Static/Shared combinations will occur in addition to build_pass.

By testing for these values in a build_pass scope appropriate makefile contents can be configured. For example, if the source code contains debug output sections conditional on the DEBUG_SECTION preprocessor macro definition the following qmake syntax enables defining the macro value at compile time:

build_pass: DebugBuild {
# Provide compile-time DEBUG_SECTION.
DEFINES += DEBUG_SECTION=$(DEBUG_SECTION)
# Provide console output on MS/Windows.
win32: CONFIG += console
}
After running qmake with a .pro file containing the above, the developer can build a debug version of the code as follows:

make DEBUG_SECTION=ENABLED_SECTION debug
Here ENABLED_SECTION is a symbol defined in the source code for the debug section output to be enabled. The shared-debug and/or static-debug targets will need to be specified if static_and_shared was set in the CONFIG list.

Additionally, there are several values with uncertain meaning:

explicitlib — ?
no_delete_multiple_files — related to custom targets and “make clean”,
no_fixpath — modifies how qmake mangles file paths to be relative (not sure exactly how, though),
subdir_first_pro, cd_change_global — have something to do with projects using the SUBDIRS template.
Another interesting value, for those who are bored with long compilation logs:

silent — the created makefile uses the “echo” command to output strings like “compiling x.cpp”, “moc x.h”, “linking x.exe”…
Do not use temporary file containing command line flags for calls to e.g. compiler or linker (the @C:\TEMP\nm1234.tmp) but write everything directly to command line (nmake specific). Useful for reproducible build logs:

no_batch — ? (Win32 NMAKE specific)
Another interesting functionality of qmake, at least since Qt4, is that it features an (undocumented) “config” switch that modify the value of the CONFIG variable on run-time without changing the content of the processed file. This is especially useful to replace build targets. Indeed qmake is unable to generate other build targets than the classic “release”, “debug”, “clean” and “install”. As the CONFIG variable is checked when resolving scopes it allows to create complex project structure based on targets which remains human-readable…

#sample project

TARGET = sample
TEMPLATE = app

SOURCES += main.cpp someclass.cpp
HEADERS += someclass.h

target_one {
DEFINES += BUILD_ONE
SOURCES += someclass_one.cpp
HEADERS += someclass_one.h
}

target_two {
DEFINES += BUILD_TWO
SOURCES += someclass_two.cpp
HEADERS += someclass_two.h
}
The above project would have 4 possibles outputs :

a simple app with only “someclass” implemented
the same app but with “someclass_one” added, makefile generation is done with the following command :
qmake -config target_one
the same app but with “someclass_two” added, makefile generation is done with the following command :
qmake -config target_two
the same app but with but optionnal classes added, makefile generation is done with the following command :
qmake -config “target_one target_two”
This trick will used by Edyuk so as to allow the *.pro format to become as powerful as well-known standards such as *.cbp, used by Code::Blocks, and *.vcproj, used by MSVC.

Custom install config
The following, undocumented switches can be added to the .CONFIG property of a custom install target (i.e. myInstallTarget.CONFIG). A target is a custom install target if it has been added to the INSTALLS list. It should be noted that “target” counts as a custom install target as well, if its .files property was explicitly defined in the project file.

no_check_exist — creates the install target even if the files to be installed do not exist at the time when qmake is run
Gotcha: There are two forms that qmake might use to install files: INSTALL_FILE and INSTALL_DIR. When an existing directory is being installed and no_check_exist is not applied the INSTALL_DIR form is used. However, if the directory does not exist when qmake is run (e.g. a subdirectory of documentation files that will be produced during the make run) and no_check_exist is applied the INSTALL_FILE form is used, which, on Unix systems, will fail during the make run. To prevent this, no_check_exist must not be applied in this case and an empty directory with the intended name must be created when qmake is run. To do this use the qmake “system” function with the appropriate command (this will be “mkdir -p” on Unix systems; just mkdir on Windows) and directory pathname (N.B.: the pathname must have the delimiters appropriate to the host system).

dummy_install — specifies than an install target is not actually used to install files
no_path — specifies that an install target has no path
Gotcha: A custom install target must explicitly define the .files and .path properties, unless both .dummy_install and .no_path are defined within the custom install target

SUBDIRS projects
SUBDIRS is a powerful method for breaking projects into smaller chunks. It’s actually much more powerful than is indicated in the documentation, though.

There are three different possible meanings of the values in the SUBDIRS variable. They can be directories, like the manual indicates; in this case, qmake will look for a .pro file with the same name as the directory. It can also be a .pro file, with or without a path, in which case it will go directly to that file. Or most powerfully, it can be a variable. In this case, one configures behaviour via compound variables, using the following keywords:

subdir — the path to the .pro file. This will behave as if you simply specified the directory.
file — the .pro file itself. This will behave as if you specified the full path and filename.
depends — a list of other SUBDIRS entries that this entry depends on.
makefile — it seems this sets the name of the makefile that will be generated and called for this target.
target — this sets the target within the makefile that will be called. (Probably most useful in combination with the “makefile” option.)
For example:

TEMPLATE = subdirs
SUBDIRS = sub_lib sub_tests sub_app

sub_lib.subdir = lib
sub_tests.file = tests/proj.pro
sub_tests.depends = sub_lib
sub_app.subdir = app
sub_app.depends = sub_lib
This makes it possible to use make -j 4 on your fancy quad-core system with a project that consists of several components that depend on each other. To simplify the process a bit, the following test function can be defined:

addSubdirs(subdirs,deps): Adds directories to the project that depend on

other directories

defineTest(addSubdirs) {
for(subdirs, 1) {
entries = f i l e s ( files( files(subdirs)
for(entry, entries) {
name = r e p l a c e ( e n t r y , [ / ] , ) S U B D I R S + = replace(entry, [/\\\\], _) SUBDIRS += replace(entry,[/],)SUBDIRS+=name
eval ( n a m e . s u b d i r = {name}.subdir = name.subdir=entry)
for(dep, 2):eval ( n a m e . d e p e n d s + = {name}.depends += name.depends+=replace(dep, [/\\], _))
export ( n a m e . s u b d i r ) e x p o r t ( {name}.subdir) export ( name.subdir)export({name}.depends)
}
}
export (SUBDIRS)
}
You can then use it like

addSubdirs (contrib/*)

addSubdirs (src/lib/kernel, contrib/module1 contrib/module2)
addSubdirs (src/lib/gui, src/lib/kernel contrib/module3 contrib/module4)

addSubdirs (src/tests/kernel, src/lib/kernel)
addSubdirs (src/tests/gui, src/lib/gui)

addSubdirs (src/main, src/lib/gui src/lib/kernel)

addSubdirs (src/modules/*, src/lib/kernel)
to define a project that has:

several contributed modules that should be compiled first
a kernel lib for non-gui related stuff that depends on some contrib modules
a gui lib that depends on the kernel lib and some other contrib modules
test benches for the kernel and gui libs
a main program that uses the gui and kernel libs
several modules that only depend on the kernel lib
and that compiles in parallel where possible.

Undocumented modes
Apart from the well known “-project” and “-makefile” modes, qmake support a few other switches that can put it in different modes.

-prl turn it into “prl generation mode”. Quite honestly I don’t know what that means. This is certainly related to the .prl files that are present in the $QTDIR/libs directory…
Undocumented functions
There are some very handy functions that do not appear in the Qt4 documentation. Some of these were not added until Qt 4.2, so beware…

Program flow functions
These are test functions as far as qmake is concerned, but I felt they really belong in their own section:

break() — Acts like a C break statement.
debug(level, msg) — Outputs a message to the qmake debug log (enabled by the -d option). The “level” parameter specifies the number of -d options that must be specified for this message to be displayed.
clear(var) — Initializes the variable to empty.
export(var) — When writing a custom function, variables declared are local to the function. To make the variable available to the calling context, use export(var).
next() — Acts like a C continue statement.
unset(var) — Deletes the variable entirely (it will act as if it had never been set).
Replace functions
These functions return a value:

files(glob) — Returns a list of files which match the specified glob pattern.
Actually, this function is documented as test function.
Undocumented niceties
qmake is a really powerful tool, if you were still a bit unsure of that, have a look:

scopes can be nested but also combined using logical operators. The following lines are equivalent :

win32|unix {
VAR += value
}

win32 {
VAR += value
}

unix {
VAR += value
}
wildcards can be used in scopes:

win32-msvc* {
# same as win32-msvc|win32-mscv.net
}
wildcards can potentially be used in file lists:

TEXTS += *.txt
However, wildcards in file lists are unsupported and not guaranteed to work for all generators! Use the $$files function instead!

Detecting whether static Qt build is being used:

qtConfig(static) {

this is a static build

}
See also
undocumented qmake by Paul John Floyd

About this Article
Hope this all makes your jobs a little easier…!

GordonSchumacher (author of the original article)
fullmetalcoder (information about config switch, undocumented modes and undocumented niceties)
soukupmi (batch-call example)
This article was originally created by Paul John Floyd (see link above), extended in the Qt Centre Wiki (original Link: http://www.qtcentre.org/wiki/index.php?title=Undocumented_qmake) and after the shutdown of the Qt Centre Wiki, it was copied into wiki.qt.io using the Qt Centre version from 11th March 2015.

 类似资料: