Search code examples
qtqmake

The value of the qmake variable from a file in the shadow directory


There is a file build.txt in the Qt creator Windows project directory with content:

7

And batch file build_inc.bat with content:

@echo off 
set /p var= <build.txt 
Set /A var= var+1 
echo %var% >build.txt
echo #define BUILD %var% >build.h
echo %var%

Content of *.pro file:

Release:QT       += core gui
Release:greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

Release:build_nr.commands = $$PWD/build_inc.bat
Release:build_nr.depends = FORCE
Release:QMAKE_EXTRA_TARGETS += build_nr
Release:PRE_TARGETDEPS += build_nr

Release:BUILD = $$cat(build.txt)

Release:message( $${BUILD} )
Release:TARGET = my_proj_$${BUILD}

Release:TEMPLATE = app
Release:DEFINES += QT_DEPRECATED_WARNINGS
Release:CONFIG += c++11
Release:SOURCES += \
        main.cpp \
        mainwindow.cpp
Release:HEADERS += \
        mainwindow.h
Release:FORMS += \
        mainwindow.ui

When I click run, an exe file with the name is created in the shadow directory my_proj_7.exe; a file build.txt is copied to the shadow directory and its contents:

1

When I click run again, the name of the executable file does not change, but the contents of the file build.txt in the shadow directory, it becomes equal to:

2

The contents of the file build.txt the project directory does not change and is equal to:

7

Desired behavior: so that every time the run button is pressed, an executable file is created with an increasing number at the end of the name, for example, my_proj_1, my_proj_2, my_proj_3, etc. How to assign the BUILD variable from the *.pro file the value of the file inside build.txt which is located in the shadow directory and not in the project directory?

Changes based on comments (15/04/2024): build_inc.cmd:

@echo off 
if exist build.h (for /F "tokens=3" %%I in (build.h) do set BuildNumber=%%I) else set BuildNumber=0
if "%~1" == "/R" ren %2 "%~n2_%BuildNumber%%~x2" & exit /B
set /A BuildNumber+=1
1>build.h echo #define BUILD %BuildNumber%

my_proj.pro:

Release:QT       += core gui
Release:greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

Release:build_nr.commands = $$PWD/build_inc.cmd
Release:build_nr.depends = FORCE
Release:QMAKE_EXTRA_TARGETS += build_nr
Release:PRE_TARGETDEPS += build_nr

Release:TARGET = my_proj

QMAKE_POST_LINK = $$PWD/build_inc.cmd /R ${DESTDIR}\\${TARGET}
#QMAKE_POST_LINK = %ComSpec% /c D:\QT_proj\my_proj\my_proj\build_inc.cmd /R my_proj.exe
Release:TEMPLATE = app
Release:DEFINES += QT_DEPRECATED_WARNINGS
Release:CONFIG += c++11
Release:SOURCES += \
        main.cpp \
        mainwindow.cpp
Release:HEADERS += \
        mainwindow.h
Release:FORMS += \
        mainwindow.ui

Compile output:

17:28:57: Running steps for project my_proj...
17:28:57: Starting: "C:\Qt\Qt5.13.0\Tools\mingw730_32\bin\mingw32-make.exe" clean -j12
C:/Qt/Qt5.13.0/Tools/mingw730_32/bin/mingw32-make -f Makefile.Release clean
C:/Qt/Qt5.13.0/Tools/mingw730_32/bin/mingw32-make -f Makefile.Debug clean
mingw32-make[1]: Entering directory 'D:/QT_proj/my_proj/shad'
del debug\my_proj_plugin_import.o
mingw32-make[1]: Entering directory 'D:/QT_proj/my_proj/shad'
del release\moc_mainwindow.cpp
del ui_mainwindow.h
ЌҐ г¤ Ґвбп ­ ©вЁ гЄ § ­­л© д ©«.
ЌҐ г¤ Ґвбп ­ ©вЁ D:\QT_proj\my_proj\shad\debug\my_proj_plugin_import.o
mingw32-make[1]: Leaving directory 'D:/QT_proj/my_proj/shad'
mingw32-make[1]: [Makefile.Release:102: compiler_moc_header_clean] Error 1 (ignored)
del release\main.o release\mainwindow.o release\my_proj_plugin_import.o release\moc_mainwindow.o
ЌҐ г¤ Ґвбп ­ ©вЁ гЄ § ­­л© д ©«.
mingw32-make[1]: [Makefile.Release:80: clean] Error 1 (ignored)
mingw32-make[1]: Leaving directory 'D:/QT_proj/my_proj/shad'
17:28:57: The process "C:\Qt\Qt5.13.0\Tools\mingw730_32\bin\mingw32-make.exe" exited normally.
17:28:57: Configuration unchanged, skipping qmake step.
17:28:57: Starting: "C:\Qt\Qt5.13.0\Tools\mingw730_32\bin\mingw32-make.exe" -j12
C:/Qt/Qt5.13.0/Tools/mingw730_32/bin/mingw32-make -f Makefile.Release
mingw32-make[1]: Entering directory 'D:/QT_proj/my_proj/shad'
D:/QT_proj/my_proj/my_proj/build_inc.cmd
D:\Qt\Qt5.6.0\5.6\Qt5.6_Static\bin\uic.exe ..\my_proj\mainwindow.ui -o ui_mainwindow.h
...
    compilation terminated.
mingw32-make[1]: *** [Makefile.Release:564: release/my_proj_plugin_import.o] Error 1
..\my_proj\main.cpp:11:1: fatal error: error closing -: Invalid argument
 }
 ^
compilation terminated.
mingw32-make[1]: *** [Makefile.Release:348: release/main.o] Error 1
..\my_proj\mainwindow.cpp:18:1: fatal error: error writing to -: Invalid argument
 }
 ^
compilation terminated.
mingw32-make[1]: *** [Makefile.Release:499: release/mainwindow.o] Error 1
mingw32-make[1]: Leaving directory 'D:/QT_proj/my_proj/shad'
mingw32-make: *** [Makefile:34: release] Error 2
17:28:58: The process "C:\Qt\Qt5.13.0\Tools\mingw730_32\bin\mingw32-make.exe" exited with code 2.
Error while building/deploying project my_proj (kit: Qt Static 5.6)
When executing step "Сборка"
17:28:58: Elapsed time: 00:02.

Application output:

17:23:05: Starting D:\QT_proj\my_proj\shad\release\my_proj.exe ...
17:23:05: Failed to start program. Path or permissions wrong?
17:23:05: D:/QT_proj/my_proj/shad/release/my_proj.exe exited with code -1
17:23:05: The process failed to start. Either the invoked program "D:/QT_proj/my_proj/shad/release/my_proj.exe" is missing, or you may have insufficient permissions to invoke the program.

Makefile.Release:

...
####### Files

SOURCES       = ..\my_proj\main.cpp \
        ..\my_proj\mainwindow.cpp \
        my_proj_plugin_import.cpp release\moc_mainwindow.cpp
OBJECTS       = release/main.o \
        release/mainwindow.o \
        release/my_proj_plugin_import.o \
        release/moc_mainwindow.o

DIST          =  ..\my_proj\mainwindow.h ..\my_proj\main.cpp \
        ..\my_proj\mainwindow.cpp \
        my_proj_plugin_import.cpp
QMAKE_TARGET  = my_proj
DESTDIR        = release\ #avoid trailing-slash linebreak
TARGET         = my_proj.exe
DESTDIR_TARGET = release\my_proj.exe

####### Build rules

first: all
all: Makefile.Release  $(DESTDIR_TARGET)

$(DESTDIR_TARGET): build_nr D:/Qt/Qt5.6.0/5.6/Qt5.6_Static/lib/libQt5Widgets.a D:/Qt/Qt5.6.0/5.6/Qt5.6_Static/lib/libQt5Gui.a D:/Qt/Qt5.6.0/5.6/Qt5.6_Static/lib/libQt5Core.a ui_mainwindow.h $(OBJECTS) 
    $(LINKER) $(LFLAGS) -o $(DESTDIR_TARGET) $(OBJECTS)  $(LIBS)
    D:/QT_proj/my_proj/my_proj/build_inc.cmd /R ${DESTDIR}${TARGET}

qmake: FORCE
    @$(QMAKE) -spec win32-g++ -o Makefile.Release ..\my_proj\my_proj.pro

qmake_all: FORCE
...

Solution

  • I recommend to change the batch file batch_inc.cmd to:

    @echo off
    set "BuildNumber=0" 
    if exist build.h for /F "tokens=3" %%I in (build.h) do set "BuildNumber=%%I" & goto CheckArgs
    :CheckArgs
    if "%~1" == "/R" goto RenameFile
    set /A BuildNumber+=1
    1>build.h echo #define BUILD %BuildNumber%
    exit /B
    :RenameFile
    if "%~2" == "" exit /B
    if exist %2 del "%~dpn2_%BuildNumber%%~x2" 2>nul & ren %2 "%~n2_%BuildNumber%%~x2"
    

    There is now always defined the environment variable BuildNumber even on header file build.h does not exist in the current working directory on execution of the batch file.

    The file build.h is processed until an non-empty line is found which has at least three substrings separated with normal spaces or horizontal tab. That should be of course the first line with #define BUILD 1 or whatever is the current number. No more lines are read from this file which should not have more lines at all. The third substring should be a number string without leading zeros.

    The batch file checks next if it is executed with first argument string is case-sensitive /R in which case a rename of the target file should be done.

    On first execution of build_inc.cmd with no argument string passed to it the batch file increments the build number read from file build.h and overwrites the existing build.h with just one line with #define BUILD and the incremented build number appended without a trailing space because of specifying the redirection 1>build.h unusually at the beginning of the command line instead of the end with a space after %BuildNumber% which command echo would output also into the batch file. The command line is modified by cmd.exe with moving 1>build.h to the end of the command line, but without adding in this case one more space left to the redirection 1>.

    The rename operation of the target file done on running the batch file with first argument being case-sensitive /R and second argument being the file to rename is more robust with this code.

    There is first checked if the batch file is executed with a second argument on first argument being the option /R. The batch file processing by cmd.exe is exited with second argument string not existing at all.

    Then is checked if the second argument string references a really existing file in which case it first deletes a perhaps already existing file with new file name and then renames the passed file by inserting left to the file extension an underscore and the current build number.

    The project file my_proj.pro should be:

    Release:QT *= core gui
    Release:greaterThan(QT_MAJOR_VERSION, 4): QT *= widgets
    
    Release:BUILD_NUMBER_SOURCE = .   # That is needed to give some bogus input.
    Release:build_number.name = Create build.h or update build number in build.h
    Release:build_number.commands = $${ComSpec} /D /C $${PWD}/build_inc.cmd
    Release:build_number.CONFIG = explicit_dependencies ignore_no_exist no_link
    Release:build_number.depends = FORCE
    Release:build_number.input = BUILD_NUMBER_SOURCE
    Release:build_number.output = $${PWD}/build.h
    
    Release:QMAKE_EXTRA_COMPILERS += build_number
    
    Release:TARGET = my_proj
    
    QMAKE_POST_LINK = $${PWD}/build_inc.cmd /R ${DESTDIR}${TARGET}
    # QMAKE_POST_LINK = $${ComSpec} /D /C $${PWD}/build_inc.cmd /R ${DESTDIR}${TARGET}
    
    Release:TEMPLATE = app
    Release:DEFINES += QT_DEPRECATED_WARNINGS
    Release:CONFIG += c++11
    Release:SOURCES += \
            main.cpp \
            mainwindow.cpp
    Release:HEADERS += \
            mainwindow.h
    Release:FORMS += \
            mainwindow.ui
    

    There is not anymore a custom target added because of no compiler is executed producing a library file or at least a source code file. The qmake variable PRE_TARGETDEPS lists libraries that the target depends on. The line Release:PRE_TARGETDEPS += build_nr is nonsense in my_proj.pro as build_nr is neither a library file nor a source code file nor a header file. It is not even an existing file.

    There is added now an extra compiler whereby the word compiler means here a program or a script interpreted by a program producing an output file.

    There is defined first the variable BUILD_NUMBER_SOURCE with . (current working directory) as value. Why this variable is necessary is explained below.

    The following line is more or less just a comment for naming the extra compiler with something meaningful.

    Release:build_number.name = Create build.h or update build number in build.h
    

    The next line is very important as it defines the executable/script to run:

    Release:build_number.commands = $${PWD}/build_inc.cmd
    

    There is executed in this case on build the Windows Command Processor as defined by the environment variable ComSpec with the option /C to run the batch file specified with its fully qualified file name. $${PWD} should not expand to a working directory path containing a space or one of these characters &()[]{}^=;!'+,`~ as otherwise it would be necessary to change the command line.

    The created Makefile.Release should contain now:

    compiler_build_number_make_all: build.h
    compiler_build_number_clean:
        -$(DEL_FILE) build.h
    build.h: . \
            FORCE
        D:/QT_proj/my_proj/my_proj/build_inc.cmd
    

    Please note that running clean results in the deletion of build.h and the next build starts again with build number 0 because of missing header file incremented to 1 which is written into newly created build.h on next build.

    Release:build_number.CONFIG = explicit_dependencies ignore_no_exist no_link
    Release:build_number.depends = FORCE
    Release:build_number.input = BUILD_NUMBER_SOURCE
    Release:build_number.output = $${PWD}/build.h
    

    There are three "compiler" configuration options defined next with:

    Release:build_number.CONFIG = explicit_dependencies ignore_no_exist no_link
    

    The Qt Wiki page Undocumented QMake explains them briefly.

    1. explicit_dependencies instructs qmake not searching in the source files for files on which the source files depend like header files included in source files and header files. qmake should write into the generated Makefile.Release only the files and variables specified with Release:build_number.depends. This is important here as there are not source files at all for this "compiler".
    2. ignore_no_exist instructs qmake not printing a warning on an input file does not exist on generating Makefile.Release. There are no input files and therefore this configuration option is useful for avoiding warnings.
    3. no_link instructs qmake not adding the output file to the list of files finally linked for creating the target (library or executable). A compiler usually generates an object file, but not this "compiler". It creates on not existing or updates on existing just a header file.

    The following line is important to run the batch file on every build:

    Release:build_number.depends = FORCE
    

    I think, but I am not sure, that FORCE defines the dependency on output of:

    qmake: FORCE
        @$(QMAKE) -spec win32-g++ -o Makefile.Release ..\my_proj\my_proj.pro
    

    The batch file is executed every time qmake is run to generate Makefile.Release new. This was in my tests always the case on making a build. It could be that your environment is different. In this case it would be necessary to redefine this line or make other changes regarding to input files and dependencies.

    The next line is:

    Release:build_number.input = BUILD_NUMBER_SOURCE
    

    This extra "compiler" does not need any input. But qmake expects for every compiler an input or it ignores build_number.commands and the batch file would never run before compilation of the source code files.

    There cannot be specified just . or a not existing file as qmake recognizes in this case the missing input file(s), outputs a warning and ignores again the .commands line resulting in no batch file execution. But qmake is happy with the definition of the variable BUILD_NUMBER_SOURCE referenced here as input for the extra "compiler" and generates the appropriate lines in Makefile.Release as posted above.

    The last line specified the output file generated by the extra "compiler":

    Release:build_number.output = $${PWD}/build.h
    

    The header file created or updated by the batch file is specified here with full path because of this "compiler" does not create a file in the directory in which the object files of the source files are generated by real compiler on building the target.

    Finally Release:QMAKE_EXTRA_COMPILERS += build_number adds this extra "compiler" to the generated Makefile.Release.

    It can be seen on output of a build process that the current working directory on generating the executable is: D:/QT_proj/my_proj/shad
    The file Makefile.Release contains the variable DESTDIR defined with release\ and the variable TARGET defined with my_proj.exe. The batch file can rename the generated target file only on passing its file name correct as second argument after first case-sensitive option /R with either the full path or the path relative to the current working directory on build after linking all static libraries and object files together to an executable.

    There is used for that reason as post link command:

    QMAKE_POST_LINK = $${PWD}/build_inc.cmd /R ${DESTDIR}${TARGET}
    

    That should result in Makefile.Releas in the command line:

    D:/QT_proj/my_proj/my_proj/build_inc.cmd /R ${DESTDIR}${TARGET}
    

    That should result in executing by make finally:

    %ComSpec% /C D:/QT_proj/my_proj/my_proj/build_inc.cmd /R release\my_proj.exe
    

    It worked in my tests also the second definition of QMAKE_POST_LINK command line which explicitly references the value of the environment variable ComSpec and the option /D to ignore the AutoRun registry value and the option /C to run the command line specified with the next three argument strings and then let the Windows Command Processor close itself after finishing processing of the specified batch file with the two argument strings.

    It should also work using ${DESTDIR_TARGET} instead of ${DESTDIR}${TARGET} on the QMAKE_POST_LINK line in my_proj.pro.