qmake 4.8.4 has broken custom targets due to capitalisation. How do I work around it? - qmake

My .pro file has extra stuff in it:
win32 {
OUT_PWD_SHELL = $$replace(OUT_PWD, /, \\)
autoversion.target = $$OUT_PWD\\autoversioninfo.h
autoversion.depends = FORCE
autoversion.commands = $$PWD/../../AutoBuildVersion.exe $$replace(PWD, /, \\) $$OUT_PWD_SHELL
QMAKE_EXTRA_TARGETS += autoversion
PRE_TARGETDEPS += $$OUT_PWD\\autoversioninfo.h
}
This fails to work as expected because in the generated makefile DESTDIR_TARGET has a new dependency added that starts d:\ but the rule generated for autoversion starts with D:/. I can improve this slightly by replacing all / with \, but the case sensitivity still breaks it and the target is not built.
If I remove the full path from autoversion.target and PRE_TARGETDEPS then it solves that problem, but then when calculating dependencies, the rule for the cpp file that includes the generated header changes to give an explicit path to the header in the dependencies, and that path points to the source directory and not the output directory where the generated file is produced. This causes make to barf and not produce the generated file.
I don't know why qmake changes the case handling of the drive, it is very irritating, but how do I get this all to work correctly?

There is no good solution. The best I came up with is to use a phony target that always runs to generate the header file. On the downside this slows the build when the header file already exists, but on the upside, it allows the build to complete.
win32 {
OUT_PWD_SHELL = $$replace(OUT_PWD, /, \\)
gen_autoversion.target = GENERATE_AUTOVERSIONINFO
gen_autoversion.commands = $$PWD/../../AutoBuildVersion.exe $$replace(PWD, /, \\) $$OUT_PWD_SHELL
QMAKE_EXTRA_TARGETS += gen_autoversion
PRE_TARGETDEPS += GENERATE_AUTOVERSIONINFO
}
I am not sure that PRE_TARGETDEPS is actually needed here.

Related

How to write Bazel rules that work with external repositories?

The Bazel Starlark API does strange things with files in external repositories. I have the following Starlark snippet:
print(ctx.genfiles_dir)
print(ctx.genfiles_dir.path)
print(output_filename)
ret = ctx.new_file(ctx.genfiles_dir, output_filename)
print(ret.path)
It is creating the following output:
DEBUG: build_defs.bzl:292:5: <derived root>
DEBUG: build_defs.bzl:293:5: bazel-out/k8-fastbuild/genfiles
DEBUG: build_defs.bzl:294:5: google/protobuf/descriptor.upb.c
DEBUG: build_defs.bzl:296:5: bazel-out/k8-fastbuild/genfiles/external/com_google_protobuf/google/protobuf/descriptor.upb.c
That extra external/com_google_protobuf comes seemingly out of nowhere, and it makes my rule fail:
I tell protoc to generate into ctx.genfiles_dir.path (which is bazel-out/k8-fastbuild/genfiles).
So protoc generates bazel-out/k8-fastbuild/genfiles/google/protobuf/descriptor.upb.c
Bazel fails because I didn't generate bazel-out/k8-fastbuild/genfiles/external/com_google_protobuf/google/protobuf/descriptor.upb.c
Likewise, when I try to call file.short_path on a source file from an external repository, I get a result like ../com_google_protobuf/google/protobuf/descriptor.proto. This seems quite unhelpful, so I just wrote some manual code to strip off the leading ../com_google_protobuf/.
Am I missing something? How can I write this rule in a way that doesn't feel like I'm fighting Bazel the whole time?
Am I missing something?
The basic problem, as you already realized, is that you have two path "namespaces" the one that protoc sees (i.e. import paths) and the one that bazel sees (i.e. the path you pass to declare_file().
2 things to note:
1) All paths declared with declare_file() get the path <bin dir>/<package path incl. workspace>/<path you passed to declare_file()>
2) All actions are executed from <bin dir> (unless output_to_genfils=True in which case this switches to <gen dir> as in your example.
Trying to solve the exact same problem you encountered, I resorted to stripping the known path from the output_file's path to determine which directory to pass as p:
# This code is run from the context of the external protobuf dependency
proto_path = "google/a/b.proto"
output_file = ctx.actions.declare_file(proto_path)
# output_file.path would be `<gen_dir>/external/protobuf/google/a/b.proto`
# Strip the known proto_path from output_file.path
protoc_prefix = output_file.path[:-len(proto_path)]
print(protoc_prefix) # Prints: <gen_dir>/external/protobuf
command = "{protoc} {proto_paths} {cpp_out} {plugin} {plugin_options} {proto_file}".format(
...
cpp_out = "--cpp_out=" + protoc_prefix,
...
)
Alternatives
You may also be able to construct the same path with ctx.bin_dir, ctx.label.workspace_name, ctx.label.package, and ctx.label.name.
Misc.
proto_library recently gained an attribute strip_import_prefix. When used, the above is not correct, as all dependent files are symlinked into a new directory from which they have the relative paths declared with strip_import_prefix.
The path format is:
<bin dir>/<repo>/<package>/_virtual_base/<label name>/<path `import`ed in .proto files>
i.e.
<bin dir>/external/protobuf/_virtual_base/b_proto/google/a/b.proto
Assuming you are building an external repo called protobuf, which contains a BUILD file at its root with a target named b_proto, which in turn, relies on a proto_library wrapping google/a/b.proto AND uses the strip_import_prefix attribute.

Qmake get the target output file path

I have a qmake project that looks like this:
TEMPLATE = lib
CONFIG += dll
TARGET = mydll
SOURCES += ...
HEADERS += ....
Now I want to add an INSTALLS section, so I have:
target.path = /path/to/somedir/
target.files =./$$TARGET
INSTALLS+= target
Unfortunately this will not work, because $$TARGET contains the target name, and not the output file name. Is there a portable way to obtain the output file name? (Please no platform dependent string concatenation like lib + $$TARGET + .so)
You don't have to specify target.files, target is a special case and it's predefined in qmake.
http://qt-project.org/doc/qt-4.8/qmake-environment-reference.html#installs
If you append a built-in install set to the INSTALLS variable and do not specify files or extra members, qmake will decide what needs to be copied for you. Currently, the only supported built-in install set is target:
target.path = /usr/local/myprogram
INSTALLS += target
In the above lines, qmake knows what needs to be copied, and will handle the installation process automatically.

How to quell qmake's "WARNING: Failure to find:"?

I'm using PRE_TARGETDEPS to generate source files, and I'm adding the generated source files to SOURCES for compilation.
The output of my generator obviously doesn't exist at the time qmake is run, so qmake outputs WARNING: Failure to find: for each of the to-be-created source files.
How can I quell this warning, since I know my PRE_TARGETDEPS is going to produce those files?
Or, is there a better way to generate intermediate files using qmake?
Example
Here's a complete test.pro file that exhibits the problem:
TEMPLATE = lib
preprocess.commands += cat test.cc.in | sed 's/a/0/g' > test.0.cc ;
preprocess.commands += cat test.cc.in | sed 's/a/1/g' > test.1.cc ;
preprocess.depends = test.cc.in
QMAKE_EXTRA_TARGETS += preprocess
PRE_TARGETDEPS += preprocess
SOURCES = test.0.cc test.1.cc
Place this in an empty folder, and also create an empty test.cc.in file. Run qmake, and you'll see these warnings:
WARNING: Failure to find: test.0.cc
WARNING: Failure to find: test.1.cc
How can I quell this warning
From my reading of the qmake code, it looks like you can:
either have qmake ignore any filenames that don't exist - in which case they won't get built by your later steps
or have it write these warnings
I don't think either of these would be satisfactory for you.
Here's my reasoning.... I had a hunt for the text Failure to find in a Qt distribution I had to hand: qt4.8.1.
It appeared 3 times in in qmake/generators/makefile.cpp. The two blocks of code look like this:
QStringList
MakefileGenerator::findFilesInVPATH(QStringList l, uchar flags, const QString &vpath_var)
{
....
debug_msg(1, "%s:%d Failure to find %s in vpath (%s)",
__FILE__, __LINE__,
val.toLatin1().constData(), vpath.join("::").toLatin1().constData());
if(flags & VPATH_RemoveMissingFiles)
remove_file = true;
else if(flags & VPATH_WarnMissingFiles)
warn_msg(WarnLogic, "Failure to find: %s", val.toLatin1().constData());
....
else if(flags & VPATH_WarnMissingFiles)
warn_msg(WarnLogic, "Failure to find: %s", val.toLatin1().constData());
and this is called with:
l = findFilesInVPATH(l, (comp.flags & Compiler::CompilerRemoveNoExist) ?
VPATH_RemoveMissingFiles : VPATH_WarnMissingFiles, "VPATH_" + comp.variable_in);
So the flags parameter passed in to the first block will be either RemoveMissingFiles or WarnMissingFiles, depending on comp.flags & Compiler::CompilerRemoveNoExist.
Or, is there a better way to generate intermediate files using qmake?
I'm not sure that it's better - i.e. it's certainly complex - but this is what is done where I work...
In the .pro file, a system call is done, that:
generates the required files,
and then writes out their names to stdout.
Here's an example from the .pro, to show how it would be called:
SOURCES += $$system( python my_script_name.py )
You can of course pass arguments in to the python script, if you like
Things to note/limitations:
This means that the python script gets run whenever you run qmake, but not during individual make invocations
Each invocation of python really slows down our qmake steps - taking roughly twice as long as running qmake without launching python - but you could always use a different scripting language
This would fix your problem, in that by the time qmake processes the SOURCES value, the files have been created by the script.

Copy a file to the build directory after compiling project with Qt

I have a file "settings.ini" which needs to reside next to the Qt executable.
I can add a custom build step for this in Qt Creator which calls something like this:
copy %{sourceDir}/settings.ini %{buildDir}/settings.ini
This works great so far, but I'd like to include this in the *.pro file so I can put this up in our SVN too.
How can I do this using qmake/.pro-files only?
To copy %{sourceDir}/settings.ini to the build directory without requiring to call make install use:
copydata.commands = $(COPY_DIR) $$PWD/settings.ini $$OUT_PWD
first.depends = $(first) copydata
export(first.depends)
export(copydata.commands)
QMAKE_EXTRA_TARGETS += first copydata
$$PWD is the path of current .pro file. If your settings.ini file is not located in the same directory than the project file, then use something like $$PWD/more_dirs_here/settings.ini
Note: I found this solution here. I recommend to read the whole article as it explains how it works.
You probably want to use the INSTALLS keyword in QMake. It will require you to run make install after your build, but it does work cross-platform.
install_it.path = %{buildDir}
install_it.files += %{sourceDir}/settings.ini
INSTALLS += install_it
for osx bundles you can handle it this way
see Resource files in OS X bundle
add this to you project file:
APP_QML_FILES.files = path/to/file1.qml path/to/file2.qml
APP_QML_FILES.path = Contents/Resources
QMAKE_BUNDLE_DATA += APP_QML_FILES
this example copies the files to Contents/Resources
Compatible with Windows and Mac OSX Dev environments:
Change {AppName} to respective application name
# Define mac/windows specific target dirs
TARGETDIR = ''
macx {
TARGETDIR += $$OUT_PWD/{AppName}.app/Contents/MacOS/
}
else {
TARGETDIR += $$OUT_PWD
}
# Directories do not exist for the first build
# Without mkdata, build is successful after 5 tries. To avoid, use mkdata
mkdata.commands = $(MKDIR) $${TARGETDIR}
copydata.commands = $(COPY_FILE) $$PWD/settings.ini $${TARGETDIR}
first.depends = $(first) mkdata copydata
export(first.depends)
export(mkdata.commands)
export(copydata.commands)
QMAKE_EXTRA_TARGETS += first mkdata copydata
Happy to add Unix support if someone posts Unix solution in the comments.

How do I make sure that a directory name is quoted in OMake?

I have a relatively complicated suite of OMake files designed for cross-compiling on a specific platform. My source is in C++.
I'm building from Windows and I need to pass to the compiler include directories which have spaces in their names. The way that the includes string which is inserted in the command line to compile files is created is by the line:
public.PREFIXED_INCLUDES = $`(addprefix $(INCLUDES_OPT), $(set $(absname $(INCLUDES))))
At some other point in the OMake files I have a line like:
INCLUDES += $(dir "$(LIBRARY_LOCATION)/Path with spaces/include")
In the middle of the command line this expands to:
-IC:\Library location with spaces\Path with spaces\include
I want it to expand to:
-I"C:\Library location with spaces\Path with spaces\include"
I don't want to change anything but the "INCLUDES += ..." line if possible, although modifying something else in that file is also fine. I don't want to have to do something like change the definition of PREFIXED_INCLUDES, as that's in a suite of OMake files which are part of an SDK which may change beneath me. Is this possible? If so, how can I do it? If not, in what ways can I make sure that includes with spaces in them are quoted by modifying little makefile code (hopefully one line)?
The standard library function quote adds escaped quotes around its argument, so it should do the job:
INCLUDES += $(quote $(dir "$(LIBRARY_LOCATION)/Path with spaces/include"))
If needed, see quote in Omake manual.
In case someone else is having the same problem, I thought I'd share the solution I eventually went with, having never figured out how to surround with quotes. Instead of putting quotes around a name with spaces in it I ended up converting the path to the short (8.3) version. I did this via a a simple JScript file called shorten.js and a one line OMake function.
The script:
// Get Access to the file system.
var FileSystemObject = WScript.CreateObject("Scripting.FileSystemObject");
// Get the short path.
var shortPath = FileSystemObject.GetFolder(WScript.Arguments(0)).ShortPath;
// Output short path.
WScript.StdOut.Write(shortPath);
The function:
ShortDirectoryPath(longPath) =
return $(dir $(shell cscript /Nologo $(dir ./tools/shorten.js) "$(absname $(longPath))"))
So now I just use a line like the following for includes:
INCLUDES += $(ShortDirectoryPath $(dir "$(LIBRARY_LOCATION)/Path with spaces/include"))

Resources