As described in Undocumented qmake, I declared an extra compiler in my qmake project file:
TEST = somefile.h
test_c.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}_1.cpp
test_c.input = TEST
test_c.commands = C:/somedir/some.exe /i ${QMAKE_FILE_IN} /o ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}_1.cpp
test_c.variable_out = SOURCES
test_c.name = MyTestC
QMAKE_EXTRA_COMPILERS += test_c
And this works fine. But I also want to generate a header file. I can easily make a second custom tool for parsing this file (or files, if >1 will be in TEST), but I don't want to parse each file twice. I tried:
test_c.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}_1.cpp \
${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}_2.cpp
Just to test that the extra compiler can make two files per run. I expected some error like "file somefile_2.cpp doesn't exist
", but project compiles without errors and second output file is ignored. In Makefile somefile_2.cpp
is not present.
Now I'm thinking about two variants:
Make an extra compiler that produces an archive, where all needed output files will be saved at once. Set tool1.variable_out = TOOL_1_OUT
, and add two more extra compilers with toolN.input = TOOL_1_OUT
to just "unzip" the archived files (one per tool) and append them to some variables.
In this case three executes will be called per one input file. This is not optimal, but at least the parser will run only once per file.
Experiment with the .output_function
option. Make a qmake function that returns the same name as .output
now does, but also append second filename to HEADERS
.
P.S. I am using MinGW x32 4.7, QtCreator 2.7.1, Qt 5.1.0, C++11.
Your variant #2 is the right idea. This works for me:
defineReplace(addToHeaders) {
source = $$1
source_split = $$split(source, ".")
source_without_extension = $$first(source_split)
HEADERS += ${QMAKE_VAR_OBJECTS_DIR}$${source_without_extension}_1.h
return(${QMAKE_VAR_OBJECTS_DIR}$${source_without_extension}_1.cpp)
}
defineReplace(FILE_IN_addToHeaders) {
# qmake emits a warning unless this function is defined; not sure why.
}
TEST = somefile.h
test_c.output_function = addToHeaders
test_c.input = TEST
test_c.commands = cp ${QMAKE_FILE_IN} ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}_1.cpp ; cp ${QMAKE_FILE_IN} ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}_1.h
test_c.variable_out = SOURCES
test_c.name = MyTestC
QMAKE_EXTRA_COMPILERS += test_c
It produces a Makefile which builds somefile_1.cpp and somefile_1.h, with somefile_1.cpp added to SOURCES and somefile_1.h added to HEADERS.
This works ok (variant #1):
MY_COMP = src/precompiled.h \
src/file2.h
GENERATE_FOLDER = generated/
# build package file
my_build.clean = $${GENERATE_FOLDER}gen_${QMAKE_FILE_BASE}.pack
my_build.depends = [somepath]/my_precompiler.exe
my_build.output = $${GENERATE_FOLDER}gen_${QMAKE_FILE_BASE}.pack
my_build.input = MY_COMP
my_build.commands = [somepath]/my_precompiler.exe /i ${QMAKE_FILE_IN} /o $${GENERATE_FOLDER}gen_${QMAKE_FILE_BASE}.pack
my_build.variable_out = MY_PACKAGES
my_build.name = "package build"
# unpack cpp
my_unpack_cpp.clean = $${GENERATE_FOLDER}${QMAKE_FILE_BASE}.cpp
my_unpack_cpp.depends = $${GENERATE_FOLDER}${QMAKE_FILE_BASE}.h
my_unpack_cpp.output = $${GENERATE_FOLDER}${QMAKE_FILE_BASE}.cpp
my_unpack_cpp.input = MY_PACKAGES
my_unpack_cpp.commands = [somepath]/my_precompiler.exe /unpack cpp /i ${QMAKE_FILE_IN} /o $${GENERATE_FOLDER}${QMAKE_FILE_BASE}.cpp
my_unpack_cpp.variable_out = GENERATED_SOURCES
my_unpack_cpp.dependency_type = TYPE_C
my_unpack_cpp.name = "unpack code"
my_unpack_cpp.CONFIG = no_link
# unpack header
my_unpack_h.clean = $${GENERATE_FOLDER}${QMAKE_FILE_BASE}.h
my_unpack_h.output = $${GENERATE_FOLDER}${QMAKE_FILE_BASE}.h
my_unpack_h.input = MY_PACKAGES
my_unpack_h.commands = [somepath]/my_precompiler.exe /unpack h /i ${QMAKE_FILE_IN} /o $${GENERATE_FOLDER}${QMAKE_FILE_BASE}.h
my_unpack_h.variable_out = HEADERS
my_unpack_h.name = "unpack header"
my_unpack_h.CONFIG = no_link
QMAKE_EXTRA_COMPILERS += my_build my_unpack_h my_unpack_cpp
With this technique number of output files per one parse may vary, but may be constant for all files in project, of course.
In my_precompiler
I parse file if unpack
option isn't preserved and build two files (cpp + h) into two QBuffers. After that I simply write builded data to QFile:
QDataStream ds(&file);
ds.setVersion(QDataStream::Qt_5_1);
ds << qCompress(output_cpp.data(), 9);
ds << qCompress(output_h.data(), 9);
file.close();
In fact, now qCompress isn't profitable, because generated files too small to compression size exceeded the size of the headers zlib - sizeof(.pack) > size(.h + .h).
Unpacking:
QByteArray ba;
QDataStream ds(&file);
ds.setVersion(QDataStream::Qt_5_1);
ds >> ba;
if(unpack != "cpp")
{
ds >> ba;
}
file.close();
ba = qUncompress(ba);
file.setFileName(output);
if(!file.open(QFile::WriteOnly | QFile::Truncate)) return 1;
file.write(ba);
file.close();
When generating:
Therefore I set this:
my_unpack_cpp.depends = $${GENERATE_FOLDER}${QMAKE_FILE_BASE}.h
So /unpack cpp (and, therefore, building) performs after building needed header file. And this:
my_build.depends = [somepath]/my_precompiler.exe
Sets builded pack (and, therefore, generated cpp+h) depends on my_precompiler, so all will be rebuilded if I modify and rebuild precompiler.
P.S. IMHO these lines must works as cleaners before rebuilding:
my_build.clean = $${GENERATE_FOLDER}gen_${QMAKE_FILE_BASE}.pack
my_unpack_cpp.clean = $${GENERATE_FOLDER}${QMAKE_FILE_BASE}.cpp
my_unpack_h.clean = $${GENERATE_FOLDER}${QMAKE_FILE_BASE}.h
But they don't :( At present I ignore that, but now if building .pack is failed than previously builded pack-file is used
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With