PRE_TARGETDEPS не работает при использовании параллельных сборок

Я использую собственное управление ресурсами (вместо qrc) и пытаюсь интегрировать его в QtCreaor.

У меня есть скрипт Python, который генерирует исходный файл для компиляции. Я использую QMAKE_EXTRA_TARGETS/PRE_TARGETDEPS, чтобы сообщить QMake, что этот скрипт должен быть выполнен перед компиляцией файлов. Итак, я делаю в своем профайле:

CONFIG += ordered

generated_file.target   = my_custom_target
generated_file.commands = echo "Generating..." && d:/dev/vobs_ext_2015/tools_ext/python/Python34_light/python.exe $$PWD/pyc_res_generator.py -o $$PWD/generated/generated.cpp && echo "Generated!"
generated_file.depends  = FORCE

QMAKE_EXTRA_TARGETS            += generated_file

PRE_TARGETDEPS                 += my_custom_target

SOURCES                        += generated/generated.cpp

#DEPENDPATH = ./generated

С pyc_res_generator.py просто быть:

#! /usr/bin/env python
# -*- coding: utf8 *-*

import argparse

parser = argparse.ArgumentParser(description="", formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument("-o", type=str, help="Output file name")

args = parser.parse_args()

with open(args.o, 'w') as output_file:
    output_file.write( "// This is valid C++!" )

Чтобы проверить это, я пишу недопустимый C++ в сгенерированном.cpp (например, fjkfkfk). Когда я компилирую (цель Android), я вижу в журнале:

echo Generating... && python.exe pyc_res_generator.py -o generated/generated.cpp && echo Generated!
Generating... 
B:\Android\android-ndk-r11b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-g++ -c -Wno-psabi -march=armv7-a -mfloat-abi=softfp -mfpu=vfp -ffunction-sections -funwind-tables -fstack-protector -fno-short-enums -DANDROID -Wa,--noexecstack -fno-builtin-memmove -std=c++11 -g -g -marm -O0 -fno-omit-frame-pointer -Wall -Wno-psabi -W -D_REENTRANT -fPIC -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I..\TestQt -I. -IB:\QtCreator5_6_1\5.6\android_armv7\include -IB:\QtCreator5_6_1\5.6\android_armv7\include\QtWidgets -IB:\QtCreator5_6_1\5.6\android_armv7\include\QtGui -IB:\QtCreator5_6_1\5.6\android_armv7\include\QtCore -I. -isystem B:\Android\android-ndk-r11b\sources\cxx-stl\gnu-libstdc++\4.9\include -isystem B:\Android\android-ndk-r11b\sources\cxx-stl\gnu-libstdc++\4.9\libs\armeabi-v7a\include -isystem B:\Android\android-ndk-r11b\platforms\android-9\arch-arm\usr\include -IB:\QtCreator5_6_1\5.6\android_armv7\mkspecs\android-g++ -o main.obj ..\TestQt\main.cpp
B:\Android\android-ndk-r11b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-g++ -c -Wno-psabi -march=armv7-a -mfloat-abi=softfp -mfpu=vfp -ffunction-sections -funwind-tables -fstack-protector -fno-short-enums -DANDROID -Wa,--noexecstack -fno-builtin-memmove -std=c++11 -g -g -marm -O0 -fno-omit-frame-pointer -Wall -Wno-psabi -W -D_REENTRANT -fPIC -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I..\TestQt -I. -IB:\QtCreator5_6_1\5.6\android_armv7\include -IB:\QtCreator5_6_1\5.6\android_armv7\include\QtWidgets -IB:\QtCreator5_6_1\5.6\android_armv7\include\QtGui -IB:\QtCreator5_6_1\5.6\android_armv7\include\QtCore -I. -isystem B:\Android\android-ndk-r11b\sources\cxx-stl\gnu-libstdc++\4.9\include -isystem B:\Android\android-ndk-r11b\sources\cxx-stl\gnu-libstdc++\4.9\libs\armeabi-v7a\include -isystem B:\Android\android-ndk-r11b\platforms\android-9\arch-arm\usr\include -IB:\QtCreator5_6_1\5.6\android_armv7\mkspecs\android-g++ -o dialog.obj ..\TestQt\dialog.cpp
B:\Android\android-ndk-r11b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-g++ -c -Wno-psabi -march=armv7-a -mfloat-abi=softfp -mfpu=vfp -ffunction-sections -funwind-tables -fstack-protector -fno-short-enums -DANDROID -Wa,--noexecstack -fno-builtin-memmove -std=c++11 -g -g -marm -O0 -fno-omit-frame-pointer -Wall -Wno-psabi -W -D_REENTRANT -fPIC -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I..\TestQt -I. -IB:\QtCreator5_6_1\5.6\android_armv7\include -IB:\QtCreator5_6_1\5.6\android_armv7\include\QtWidgets -IB:\QtCreator5_6_1\5.6\android_armv7\include\QtGui -IB:\QtCreator5_6_1\5.6\android_armv7\include\QtCore -I. -isystem B:\Android\android-ndk-r11b\sources\cxx-stl\gnu-libstdc++\4.9\include -isystem B:\Android\android-ndk-r11b\sources\cxx-stl\gnu-libstdc++\4.9\libs\armeabi-v7a\include -isystem B:\Android\android-ndk-r11b\platforms\android-9\arch-arm\usr\include -IB:\QtCreator5_6_1\5.6\android_armv7\mkspecs\android-g++ -o generated.obj ..\TestQt\generated\generated.cpp
B:\QtCreator5_6_1\5.6\android_armv7\bin\moc.exe -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -IB:/QtCreator5_6_1/5.6/android_armv7/mkspecs/android-g++ -IC:/Users/jp225611/Documents/TestQt -IB:/QtCreator5_6_1/5.6/android_armv7/include -IB:/QtCreator5_6_1/5.6/android_armv7/include/QtWidgets -IB:/QtCreator5_6_1/5.6/android_armv7/include/QtGui -IB:/QtCreator5_6_1/5.6/android_armv7/include/QtCore -I. -IB:\Android\android-ndk-r11b/sources/cxx-stl/gnu-libstdc++/4.9/include -IB:\Android\android-ndk-r11b/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -IB:\Android\android-ndk-r11b/platforms/android-9/arch-arm//usr/include ..\TestQt\dialog.h -o moc_dialog.cpp
..\TestQt\generated\generated.cpp:1:1: error: 'fjkfkfk' does not name a type
   // This is valid C++!
   ^
makefile:670: recipe for target 'generated.obj' failed
mingw32-make: *** [generated.obj] Error 1
mingw32-make: *** Waiting for unfinished jobs....
Generated!
13:59:08: Le processus "B:\QtCreator5_6_1\Tools\mingw492_32\bin\mingw32-make.exe" s'est terminé avec le code 2.
Erreur lors de la compilation/déploiement du projet TestQt (kit : android_armeabi-v7a)
When executing step "Make"
13:59:08: Temps écoulé : 00:03.

И я вижу, что generated\generated.cpp был правильно сгенерирован pyc_res_generator.py (теперь он содержит // This is valid C++!)... но, видимо, слишком поздно, так как компилятор увидел fjkfkfk... Как видите, вывод сообщает:

Generating...
B:\Android\android-ndk-r11b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-g++ .... ..\TestQt\generated\generated.cpp
Generated...

Хотя можно было бы ожидать:

Generating...
Generated...
B:\Android\android-ndk-r11b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-g++ .... ..\TestQt\generated\generated.cpp

Это происходит только тогда, когда включены параллельные сборки (я установил для переменной среды MAKEFLAGS значение '-j8`)

Похоже, что моя пользовательская команда generated_file запускается до компиляции generated\generated.cpp, но система не ждет ее завершения перед фактической компиляцией generated\generated.cpp.

Я делаю что-то неправильно?


person jpo38    schedule 29.05.2018    source источник
comment
stackoverflow.com/q/15864689/17034   -  person Hans Passant    schedule 29.05.2018
comment
Цитата: поэтому, если у меня настроен флаг -j, он запускает мой скрипт параллельно с другими заданиями сборки. Это очень плохо.... Как вы поняли.   -  person Hans Passant    schedule 29.05.2018
comment
@HansPassant: только что проверил это, но разница в том, что связанный пост генерировал файл заголовка, тогда DEPENDPATH, по-видимому, помогает, в моем случае я генерирую исходный файл, и я не мог заставить его вести себя так, как ожидалось, даже при использовании DEPENDPATH.   -  person jpo38    schedule 29.05.2018
comment
Обратите внимание, что я заполнил Qtbug bugreports.qt.io/browse/QTBUG-68553.   -  person jpo38    schedule 30.05.2018


Ответы (3)


PRE_TARGETDEPS (настраиваемые цели) здесь не лучший подход, так как generated_file.target должно точно совпадать с именем цели Makefile, которое может различаться в разных параметрах:

  • теневая сборка включена или нет
  • хост-платформа (с использованием косой черты или обратной косой черты в качестве разделителя папок)
  • вероятно целевой компилятор/платформа

Однако пользовательский компилятор, по-видимому, работает намного лучше. Вот как делается издевательство над файлами. Пользовательский компилятор может сгенерировать новый файл для компиляции (и затем система ожидает создания файла перед его компиляцией, даже при выполнении параллельных сборок).

# Set fuiles to be generated in a variable
TO_GENERATE = $$PWD/generated/generated.cpp

# Specify custom command output file:
custom_generator.output  = $$PWD/generated/generated.cpp
# Specify custom command:
custom_generator.commands = 'echo "Generating..." && python $$PWD/pyc_res_generator.py -o $$PWD/generated/generated.cpp && echo "Generated..."'
# dependency:
custom_generator.depends = FORCE
# link to input file variable
custom_generator.input = TO_GENERATE
# link to variable to store generated file to
custom_generator.variable_out = SOURCES
# add to qmake:
QMAKE_EXTRA_COMPILERS += custom_generator

Тогда вам даже не нужно указывать SOURCES += $$PWD/generated/generated.cpp. Кроме того, $$PWD/generated/generated.cpp удаляется при очистке!

Это работает отлично, как и ожидалось!

person jpo38    schedule 31.05.2018

Вам нужно установить имя цели на имя файла, который она генерирует. Вот полный файл проекта, который мне подходит (qmake 3.1, Qt 5.10.1):

# Filename relative to the *build* directory.
generated_file.target   = generated/generated.cpp
generated_file.commands = 'echo "Generating..." && mkdir -p generated && echo "int main() {}" > generated/generated.cpp'
generated_file.depends  = FORCE

QMAKE_EXTRA_TARGETS     += generated_file

# Filename relative to the *source* directory.
SOURCES                 += $$OUT_PWD/generated/generated.cpp

Причина в том, что generated_file.target становится именем цели в Makefile, поэтому, когда потребуется generated.cpp, ваше пользовательское правило будет выполнено для создания файла.

Вы также можете удалить его из PRE_TARGETDEPS, потому что вы уже добавляете сгенерированный файл в SOURCES instead, что создает правильную зависимость.

Обратите внимание, что при таком подходе файл создается в каталоге build, а не в каталоге source. Вот почему вам нужно указать $$OUT_PWD при добавлении его в SOURCES: по умолчанию qmake, похоже, предполагает, что SOURCES относятся к исходному каталогу.

Если вместо этого вы хотите сгенерировать его в каталоге source, вы можете подумать, что везде ставите префикс $$PWD, но тогда qmake иногда использует абсолютные пути, а иногда относительные, и все терпит неудачу. Вероятно, вы можете обойти это с помощью $$absolute_path($$PWD), но я не пробовал.

Соответствующие разделы из сгенерированного Makefile (комментарии мои):

# How to create generated/generated.cpp.
# Depending on FORCE to make it always stale (fails if you create a file named FORCE :)).
generated/generated.cpp: FORCE
    echo "Generating..." && mkdir -p generated && echo "int main() {}" > generated/generated.cpp

# generated.o depends on generated/generated.cpp.
# Note that it's not in the same directory; we don't care.
generated.o: generated/generated.cpp 
    $(CXX) -c $(CXXFLAGS) $(INCPATH) -o generated.o generated/generated.cpp

# The final executable depends on the compiled generated.o.
OBJECTS = generated.o
$(TARGET):  $(OBJECTS)  
    $(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJCOMP) $(LIBS)

документация qmake довольно расплывчата. об этом, только заявив, что target должно быть «Именем пользовательской цели сборки». Однако пример показывает, что они устанавливают его на имя выходного файла. (В качестве бонуса также показано, как не повторять это имя в commands.)

person Thomas    schedule 29.05.2018
comment
Спасибо за вашу помощь. Но если я сделаю generated_file.target = $$PWD/generated/generated.cpp, пользовательская команда больше не будет вызываться (с оператором PRE_TARGETDEPS или без него). Содержимое файла остается fjkfkfk.... - person jpo38; 29.05.2018
comment
Попробуйте SOURCES += $$PWD/generated/generated.cpp, чтобы убедиться, что расположение файла непротиворечиво. Проверьте сгенерированный Makefile, если сомневаетесь. - person Thomas; 29.05.2018
comment
Я пробовал это. Используется $$PWD/generated/generated.cpp для generated_file.target, PRE_TARGETDEPS и SOURCES: такое же поведение. Также пытался удалить PRE_TARGETDEPS, но тогда Python не вызывается. Также пытался удалить SOURCES, но тогда generated.cpp не компилируется... так что, к сожалению, это не помогло. - person jpo38; 30.05.2018
comment
В Makefile я вижу инструкции по компиляции generated/generated.cpp и вызову python, но я не вижу правила для установления какой-то зависимости между ними.... - person jpo38; 30.05.2018
comment
Извините, да, это немного сложнее. Обновил мой ответ. - person Thomas; 30.05.2018
comment
Ваш материал работает, но он зависит от параметров сборки. Это работает, когда теневая сборка не отмечена (сборка выполняется в $$PWD), но если теневая сборка включена (сборка выполняется в подпапке $$PWD), пользовательская команда больше не выполняется. Замена generated_file.target = generated/generated.cpp на generated_file.target = ../generated/generated.cpp, затем он выполняется снова (но не будет, если вы отключите обратное построение тени). Пробовал generated_file.target = $$OUT_PWD/generated/generated.cpp безуспешно (пользовательская команда не выполнялась). Любая идея, как мы можем сделать это независимым от параметров сборки? - person jpo38; 30.05.2018
comment
Как вы упомянули, похоже, что generated_file.target должна быть точной строкой, используемой в Makefile (иногда ../generated/generated.cpp, иногда generated/generated.cpp... или, может быть, позже fullfapth...). Поэтому нам нужно найти способ получить именно эту строку для правильной настройки generated_file.target. - person jpo38; 30.05.2018
comment
Похоже, что generated_file.target = $$relative_path($$PWD/generated/generated.cpp,$$OUT_PWD) отлично работает как с теневой сборкой, так и без нее..... но я боюсь, что она может начать давать сбои, когда синтаксис akefile изменится в будущем... - person jpo38; 30.05.2018
comment
К сожалению, я не мог сделать эту работу. Как я уже упоминал, строка, указанная как generated_file.target, должна точно совпадать со строкой, используемой после generated.obj: в Makefile. И действительно трудно гарантировать это для всех целей с теневой сборкой или без нее (поскольку путь может иметь дополнительные .., но также может использоваться косая черта, а другая может использовать обратную косую черту). Для команды Qt похоже, что хороший подход заключается в использовании дополнительного компилятора, а не дополнительной цели.... - person jpo38; 31.05.2018
comment
Хм, я почти уверен, что тестировал это как с теневой сборкой, так и без нее. Использование пользовательского компилятора — еще одна жизнеспособная альтернатива, которую я успешно использовал для такого рода генерации кода. - person Thomas; 31.05.2018
comment
Можно заставить это работать, используя QMAKE_EXTRA_COMPILERS, см. мой ответ. Спасибо за ваше время, это действительно помогло понять, почему QMAKE_EXTRA_TARGETS не было хорошей идеей ;-) - person jpo38; 31.05.2018

Альтернатива основана на TEMPLATE=subdirs, чтобы учитывать libname.depends в среде параллельной сборки. Вам понадобятся два файла .pro в каждой папке приложения. И один в папке общей библиотеки. Любые другие папки в вашей области разработки, которые используют TEMPLATE=subdirs, должны быть совместимы.

например, TestApp с использованием LibLarry.

В папке TestApp есть bld.pro и TestApp.pro.

TestApp.pro:

#This is a TEMPLATE=subdirs, which gmake will interpret;  
# making sure the build dependencies are respected
TEMPLATE = subdirs
SUBDIRS += bld  mylib
bld.file = bld.pro
bld.depends = mylib # force library to be built first.
mylib.file = $${PATH_TO_LIB_LARRY}/liblarry.pro

блд.про:

TARGET=TestApp
SOURCES+=TestApp.cpp
PRE_TARGETDEPS+=$${PATH_TO_LIB_LARRY}/liblarry.a

#boilerplate...
QT -= gui
CONFIG = ...
include ($${PATH_TO_LIB_LARRY}/liblarry.pri) # contains include file info
CONFIG += link_prl # allow lib larry to specify its position in the linker input command.

Папка LibLarry имеет LibLarry.pro, содержащую

LibLarry.pro:

TARGET=larry
TEMPLATE=lib
CONFIG+=staticlib 
CONFIG+=create_prl # export information, so apps can link correctly
SOURCES+=my_shared_function.cpp
include (liblarry.pri)

библиотека.pri:

HEADERS += $$PWD/my_shared_function.h
INCLUDEPATH += $$PWD
person Rumex    schedule 06.08.2019