Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

qmake extra compiler with multiple outputs per file

Tags:

qmake

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:

  1. 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.

  2. 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.

like image 557
user2271079 Avatar asked Oct 04 '22 20:10

user2271079


2 Answers

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.

like image 141
smokris Avatar answered Oct 12 '22 11:10

smokris


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:

  1. Write #include "original header" in begin of generated header
  2. Write #include "generated header" in begin of generated code

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

like image 45
user2271079 Avatar answered Oct 12 '22 12:10

user2271079