Undocumented qmake
Материал из Wiki.crossplatform.ru
Содержание |
Введение
qmake очень мощная "meta-make" система, которая может быть использована для создания makefile для различных компиляторов и платформ из одного и того же файла проекта qmake (.pro). Но в документации по qmake отсутствует много информации - есть много, очень много вариантов, которые просто не описаны в Trolltech документации. Эта страница призвана помочь с этими примерами. (Заметим, что эта информация относится к Qt4; возможно будет работать в Qt3, но не факт.)
Недокументированные переменные
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 I have come across only by digging through the qmake source (and the first version they are confirmed to exist in):
- _DATE_ — текущая дата и время. (v4.3.4)
- _FILE_ — имя файла в данный момент обрабатываемого 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)
- IN_PWD — the base directory of the source tree. (v4.3.4)
- PWD — Текущий рабочий каталог. Be aware that this is not necessarily either the location of the .pro file, or the current qmake (.pri?) file - it's the directory the user was in when they invoked qmake. (v4.3.4)
- OUT_PWD — indicates the output directory that the current target will be written to. In normal usage, OUT_PWD will be the same as IN_PWD, unless you are attempting an out-of-source build - in which case it will indicate the base directory of the build tree. Not to be confused with DESTDIR, which indicates the directory relative to OUT_PWD in which to place the build target. (v4.3.4)
- QMAKE_HOST — a compound variable containing details about the OS in use: (v4.3.4)
- QMAKE_HOST.arch — returns the results of "uname -a" (and returns results of the same format on Windows).
- QMAKE_HOST.name — returns the network name ("uname -n").
- QMAKE_HOST.os — returns the results of "uname -o" or "Windows".
- QMAKE_HOST.version — returns the results of "uname -v", or one of the following on Windows:
- Win95
- Win98
- WinMe
- WinNT
- Win2000
- Win2003 (maybe??)
- WinXP
- WinVista
Пользовательские функции
qmake distinguishes two types of function :
- conditional functions that may take parameters and return true or false. They can be used much like scopes to customize your projects depending to some configuration features, file availability...
- data processing functions that may take parameters and return a value (or a list of value) that can be assigned to a variable.
The creation of custom functions of these two types can be achieved using two already existing functions, respectively:
- defineTest(nameOfTestFunction),
- defineReplace(nameOfDataProcessingFunction).
Whatever the type of function you define, the above strings have to be followed by a '{' on the same line as the defineX statement. It indeed appears that, as with scopes and conditionnals, qmake gets mad when the bracket isn't on the same line... To return from the function, the return(value) function is used, and, of course, the function definition ends with a '}'.
Пользовательские инструменты
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
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 = $${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.
- .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 — do not generate an error (warning?) if the files in the source variable do not exist,
- 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...
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.
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.
Особенности конфигурации
There are also several "switches" which can be added to the CONFIG variable which affect various behaviours of qmake. The ones that I have uncovered are as follows (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_with_source — outputs each object file into the same directory its source file came from,
- rpath_libdirs — adds QMAKE_LFLAGS_RPATH to the link flags (*nix only).
- ordered — ensures that the projects are built in the order specified.
Additionally, there are several that I don't know quite what they do:
- explicitlib — ?
- no_batch — ? (Win32 NMAKE specific),
- 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 log :
- silent — the created makefile uses the "echo" command to output strings like "compiling x.cpp", "moc x.h", "linking x.exe"...
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 ot become as powerful as well-known standards such as *.cbp, used by Code::Blocks, and *.vcproj, used by MSVC.
Проекты типа SUBDIRS
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 = $$files($$subdirs) for(entry, entries) { name = $$replace(entry, [/\\\\], _) SUBDIRS += $$name eval ($${name}.subdir = $$entry) for(dep, 2):eval ($${name}.depends += $$replace(dep, [/\\\\], _)) 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.
Недокументированные режимы
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...
- -set and -query turn it into "properties mode". qmake is then able to give you the values of some Qt specific variables that are hard-coded into it on build time. Additionally, user-selected properties may be defined and queried. These values can be obtained by a Qt application through the QLibraryInfo but the qmake switch allows shell scripts to know about them.
Built-in properties values are :
- QMAKE_MKSPECS
- QMAKE_VERSION
- QT_INSTALL_BINS
- QT_INSTALL_CONFIGURATION
- QT_INSTALL_DATA
- QT_INSTALL_DEMOS
- QT_INSTALL_DOCS
- QT_INSTALL_EXAMPLES
- QT_INSTALL_HEADERS
- QT_INSTALL_LIBS
- QT_INSTALL_PLUGINS
- QT_INSTALL_PREFIX
- QT_INSTALL_TRANSLATIONS
- QT_VERSION
For example, if Qt 4.1.3 is installed in /usr/local/Trolltech/Qt-4.1.3 (the default) :
$ qmake -query QT_VERSION 4.1.3 $ qmake -query QT_INSTALL_PREFIX /usr/local/Trolltech/Qt-4.1.3
User-defined properties are global to the system - they are not per-project. Under Win32, they are stored in the Registry at HKEY_CURRENT_USER\Software\Trolltech\QMake\2.00a.
You can also get the values of these variables by using $$[varname] (note the use of the square brackets). Курсивное начертание
Недокументированные функции
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...
Тест-функции
These functions return true or false:
- defined(func, type) — Returns true if func is defined; type must be either "test" or "replace", to match "defineTest" or "defineReplace".
- equals(var1, var2) (also works as isEqual) — Returns true if var1 is equal to var2 (string comparison).
- greaterThan(var1, var2) — Returns true if var1 is greater than var2 (as an integer).
- inFile(file, var, val) — Returns true if a variable var is defined in the specified file. Additionally, it can test to see if it has the requested value.
- load(string) — Sort of a cross between include() and CONFIG += [feature]. load(foo) will look for a file called "foo.prf" in the standard feature path, and execute its contents immediately. Features that are contained within CONFIG are executed last, after the .pro file has finished processing. Like include(), it will return true if the file was found.
- lessThan(var1, var2) — Returns true if var1 is less than var2 (as an integer).
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:
- cat(file) — Returns the contents of the specified file.
- files(glob) — Returns a list of files which match the specified glob pattern.
- first(var) — equivalent to member(var, 0).
- fromfile(filename, var) — I haven't played with this, but it appears to return the value of a single variable from the file indicated. That is, if "xyz.pri" contains the line FOO = stuff, then MYFOO = $$fromfile(xyz.pri, FOO) will set MYFOO to "stuff".
- escape_expand(string) — Does what quote() probably should have done originally, it replaces \\, \n, \t, and \r with backslash, newline, tab, and carriage return respectively.
- last(var) — Returns the last member of var.
- list(...) — Heck if I can figure out what this is for. It may be passed any number of arguments, and creates variables named .QMAKE_INTERNAL_TMP_VAR_nn, counting up by the number of arguments that have been passed to list() since qmake was invoked. The values of those variables correspond to the parameters passed to list().
- lower(string) — Converts the string to lower case.
- member(var, start, count) — In addition to what is already documented, you may specify a number of elements to return: member(FOO, 1, 2) will return the second and third members of the variable FOO.
- prompt(string): This will display the provided string - adding a ? to the end if it doesn't already contain one - and wait for a reply from the user. The return value is the user's response.
- quote(): As of Qt 4.2, this function simply returns the args that were passed to it. You probably want escape_expand(), or perhaps re_escape().
- re_escape(string): Escapes all special regular expression characters in the string.
- replace(var, regex, replace) — Searches the variable for the specified regular expression, and replaces it with the "replace" string.
- section(var, sep, start, count) — Kind of a cross between member() and split(), this function returns portions of a string, as separated by "sep". For example:
PARMLIST = foo/bar/dead/beef MIDPARMS = section(PARMLIST, "/", 1, 2)
MIDPARMS will now contain "bar/dead".
- split(var, sep) — This splits the specified variable at each occurrence of "sep", and returns the result.
- upper(string) — Converts the string to upper case.
Недокументированные тонкости
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 }
- wildcard can be used nearly everywhere : scopes, values, ...
win32-msvc* { # same as win32-msvc|win32-mscv.net }
TEXTS += *.txt
Смотрите также
- Undocumented qmake by Paul John Floyd