QMake - how to copy a file to the output

QtQt CreatorQmake

Qt Problem Overview


How can I copy a file from my project to the output directory with qmake?

I'm compiling on Linux but in the future I'll compile it on Mac and Windows.

Qt Solutions


Solution 1 - Qt

You can use a qmake function for reusability:

# Copies the given files to the destination directory
defineTest(copyToDestdir) {
    files = $$1

    for(FILE, files) {
        DDIR = $$DESTDIR

        # Replace slashes in paths with backslashes for Windows
        win32:FILE ~= s,/,\\,g
        win32:DDIR ~= s,/,\\,g

        QMAKE_POST_LINK += $$QMAKE_COPY $$quote($$FILE) $$quote($$DDIR) $$escape_expand(\\n\\t)
    }

    export(QMAKE_POST_LINK)
}

then use it as follows:

copyToDestdir($$OTHER_FILES) # a variable containing multiple paths
copyToDestdir(run.sh) # a single filename
copyToDestdir(run.sh README) # multiple files

Solution 2 - Qt

Here's an example from one of our projects. It shows how to copy files to the DESTDIR for Windows and Linux.

linux-g++{
    #...
    EXTRA_BINFILES += \
        $${THIRDPARTY_PATH}/gstreamer-0.10/linux/plugins/libgstrtp.so \
        $${THIRDPARTY_PATH}/gstreamer-0.10/linux/plugins/libgstvideo4linux2.so
    for(FILE,EXTRA_BINFILES){
        QMAKE_POST_LINK += $$quote(cp $${FILE} $${DESTDIR}$$escape_expand(\n\t))
    }
}

win32 {
    #...
    EXTRA_BINFILES += \
        $${THIRDPARTY_PATH}/glib-2.0/win32/bin/libglib-2.0.dll \
        $${THIRDPARTY_PATH}/glib-2.0/win32/bin/libgmodule-2.0.dll
    EXTRA_BINFILES_WIN = $${EXTRA_BINFILES}
    EXTRA_BINFILES_WIN ~= s,/,\\,g
        DESTDIR_WIN = $${DESTDIR}
    DESTDIR_WIN ~= s,/,\\,g
    for(FILE,EXTRA_BINFILES_WIN){
                QMAKE_POST_LINK +=$$quote(cmd /c copy /y $${FILE} $${DESTDIR_WIN}$$escape_expand(\n\t))
    }
}

Solution 3 - Qt

Qt 5.6 added this as an undocumented feature:

CONFIG += file_copies

Invent a name to describe the files you want to copy:

COPIES += myDocumentation

List the files that you want to copy, in its .files member:

myDocumentation.files = $$files(text/docs/*.txt)

Specify the destination path in the .path member:

myDocumentation.path = $$OUT_PWD/documentation

Optionally specify a base path to be trimmed from the source paths:

myDocumentation.base = $$PWD/text/docs

It basically works by doing the same things as many of the other answers here. See file_copies.prf for the gory details.

The interface is very similar to that for INSTALLS.

Solution 4 - Qt

If you use make install, you can use the INSTALLS variable of qmake. Here is an example:

images.path    = $${DESTDIR}/images
images.files   += images/splashscreen.png
images.files   += images/logo.png
INSTALLS       += images

then execute make install.

Solution 5 - Qt

Create a file copy_files.prf in one of the paths which qmake uses for config features. The file should look like this:

QMAKE_EXTRA_COMPILERS += copy_files
copy_files.name = COPY
copy_files.input = COPY_FILES
copy_files.CONFIG = no_link

copy_files.output_function = fileCopyDestination
defineReplace(fileCopyDestination) {
    return($$shadowed($$1))
}

win32:isEmpty(MINGW_IN_SHELL) {
    # Windows shell
    copy_files.commands = copy /y ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT}
    TOUCH = copy /y nul
}
else {
    # Unix shell
    copy_files.commands = mkdir -p `dirname ${QMAKE_FILE_OUT}` && cp ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT}
    TOUCH = touch
}

QMAKE_EXTRA_TARGETS += copy_files_cookie
copy_files_cookie.target = copy_files.cookie
copy_files_cookie.depends = compiler_copy_files_make_all

win32:!mingw {
    # NMake/MSBuild
    copy_files_cookie.commands = $$TOUCH $** && $$TOUCH $@
}
else {
    # GNU Make
    copy_files_cookie.commands = $$TOUCH $<  && $$TOUCH $@
}

PRE_TARGETDEPS += $${copy_files_cookie.target}

How it works

The first part defines an extra compiler which will read input filenames from the COPY_FILES variable. The next part defines the function which it will use to synthesize an output filename corresponding to each input. Then we define the commands used to invoke this "compiler", depending on which kind of shell we are in.

Then we define an extra makefile target copy_files.cookie, which depends on the target compiler_copy_files_make_all. The latter is the name of the target which qmake generates for the extra compiler we defined in the first step. This means that when the copy_files.cookie target is built, it will invoke the extra compiler to copy the files.

We specify a command to be run by this target, which will touch the files copy_files.cookie and compiler_copy_files_make_all. By touching these files, we ensure that make will not try to copy the files again unless their timestamps are more recent than the touched files. Finally, we add copy_files.cookie to the list of dependencies of the make all target.

How to use it

In your .pro file, add copy_files to the CONFIG variable:

CONFIG += copy_files

Then add the files to the COPY_FILES variable:

COPY_FILES += docs/*.txt

Solution 6 - Qt

I found I had to modify the answer given by sje397. for Qt5 Beta1 together with QtCreator 2.5.2. I use this script to copy the qml files to the destination directory as an addition step after the build is complete.

My .pro file has the following code

OTHER_FILES += \
    Application.qml

# Copy qml files post build
win32 {
    DESTDIR_WIN = $${DESTDIR}
    DESTDIR_WIN ~= s,/,\\,g
    PWD_WIN = $${PWD}
    PWD_WIN ~= s,/,\\,g
    for(FILE, OTHER_FILES){
        QMAKE_POST_LINK += $$quote(cmd /c copy /y $${PWD_WIN}\\$${FILE} $${DESTDIR_WIN}$$escape_expand(\\n\\t))
    }
}
unix {
    for(FILE, OTHER_FILES){
        QMAKE_POST_LINK += $$quote(cp $${PWD}/$${FILE} $${DESTDIR}$$escape_expand(\\n\\t))
}

}

Note that I use use $$PWD_WIN to supply the full path to the source file to the copy command.

Solution 7 - Qt

As addition to Jake's answer and @Phlucious comment one may use qmake defineReplace function which suites better for this use case. After using the provided example I encountered a problem, where qmake skipped the last post link action I added. That might be an issue with the export of the variable though the content looked pretty well all the time. Long story short, here ist the modified code

defineReplace(copyToDir) {
    files = $$1
    DIR = $$2
    LINK =

    for(FILE, files) {
        LINK += $$QMAKE_COPY $$shell_path($$FILE) $$shell_path($$DIR) $$escape_expand(\\n\\t)
    }
    return($$LINK)
}

This general copy function may be used by some convenience functions like this one

defineReplace(copyToBuilddir) {
    return($$copyToDir($$1, $$OUT_PWD))
}

The second one take only one argument (one or more files) and provides a fixed path. Pretty much the same as in the references answer.

But now note the invocation difference

QMAKE_POST_LINK += $$copyToBuilddir(deploy.bat)

As you may see, you can attach the returned command to QMAKE_PRE_LINK for even more flexibility.

Solution 8 - Qt

First, define the following two function for supporting both Windows/Unix.

defineReplace(nativePath) {
    OUT_NATIVE_PATH = $$1
    # Replace slashes in paths with backslashes for Windows
    win32:OUT_NATIVE_PATH ~= s,/,\\,g
    return($$OUT_NATIVE_PATH)
}

# Copies the given files to the destination directory
defineReplace(copyToDestDirCommands) {
    variable_files = $$1
    files = $$eval($$variable_files)
    DDIR = $$nativePath($$2)
    win32:DDIR ~= s,/,\\,g
    POST_LINK = echo "Copying files to $$DDIR" $$escape_expand(\\n\\t)

    win32 {
        POST_LINK += $$QMAKE_MKDIR $$quote($$DDIR) 2>&1 & set errorlevel=0 $$escape_expand(\\n\\t)
    }
    !win32 {
        POST_LINK += $$QMAKE_MKDIR -p $$quote($$DDIR) $$escape_expand(\\n\\t)
    }

    for(ORIGINAL_FILE, files) {
        FILE = $$nativePath($$ORIGINAL_FILE)
        POST_LINK += $$QMAKE_COPY $$quote($$FILE) $$quote($$DDIR) $$escape_expand(\\n\\t)
    }

    return ($$POST_LINK)
}

Then you can use the following code to call the funcitons that defined before to copy files into specific folder, and also creating the directory when necessary. This is tested under Win32, Linux tests are welcome.

BATOS_FILES = \
    $$BATOS_BIN_ROOT/batos-core.dll \
    $$BATOS_BIN_ROOT/batos-pfw.dll \
    $$BATOS_BIN_ROOT/dre.dll \
    $$BATOS_BIN_ROOT/log4qt.dll

QMAKE_POST_LINK += $$copyToDestDirCommands(BATOS_FILES, $$DESTDIR)

BATOS_PLUGINS_FILES = \
    $$BATOS_BIN_ROOT/plugins/com.xaf.plugin-manager.dll \
    $$BATOS_BIN_ROOT/plugins/org.commontk.eventadmin.dll

QMAKE_POST_LINK += $$copyToDestDirCommands(BATOS_PLUGINS_FILES, $$DESTDIR/plugins)

Solution 9 - Qt

First, define below (which is from XD framework) somewhere, like inside functions.prf file:

# --------------------------------------
# This file defines few useful functions
# --------------------------------------

#copyDir(source, destination)
# using "shell_path()" to correct path depending on platform
# escaping quotes and backslashes for file paths
defineTest(copyDir) {
    #append copy command
    !isEmpty(xd_copydir.commands): xd_copydir.commands += && \\$$escape_expand(\n\t)
    xd_copydir.commands += ( $(COPY_DIR) \"$$shell_path($$1)\" \"$$shell_path($$2)\" || echo \"copy failed\" )
    #the qmake generated MakeFile contains "first" and we depend that on "xd_copydir"
    first.depends *= xd_copydir
    QMAKE_EXTRA_TARGETS *= first xd_copydir

    export(first.depends)
    export(xd_copydir.commands)
    export(QMAKE_EXTRA_TARGETS)
}

#copy(source, destination) (i.e. the name "copyFile" was reserved)
defineTest(copyFile) {
    #append copy command
    !isEmpty(xd_copyfile.commands): xd_copyfile.commands += && \\$$escape_expand(\n\t)
    xd_copyfile.commands += ( $(COPY_FILE) \"$$shell_path($$1)\" \"$$shell_path($$2)\" || echo \"copy failed\" )
    #the qmake generated MakeFile contains "first" and we depend that on "xd_copyfile"
    first.depends *= xd_copyfile
    QMAKE_EXTRA_TARGETS *= first xd_copyfile

    export(first.depends)
    export(xd_copyfile.commands)
    export(QMAKE_EXTRA_TARGETS)
}

and use it in your project like:

include($$PWD/functions.prf) #optional

copyFile($$PWD/myfile1.txt, $$DESTDIR/myfile1.txt)
copyFile($$PWD/README.txt, $$DESTDIR/README.txt)
copyFile($$PWD/LICENSE, $$DESTDIR/LICENSE)
copyDir($$PWD/redist, $$DESTDIR/redist) #copy "redist" folder to "$$DESTDIR"

Note that all files would get copied before the link operation is done (which can be useful).

xd_functions.prf full script

But when you need something like copyFileLater(source, destination), to only copy files once build is done then consider using below code (which is from XD framework under Apache 2.0 license):

# --------------------------------------
# This file defines few useful functions
# --------------------------------------

xd_command_count = 0

#xd_prebuild(prefix, command)
defineTest(xd_prebuild) {
    #generate target name with number
    xd_command_count = $$num_add($$xd_command_count, 1)
    name = $$1$$xd_command_count
    #append command
    eval( $${name}.commands += ( \$\$2 ) );
    #the qmake generated "MakeFile" should contain "first"
    #   and we depend that on new command
    !contains( first.depends, $$name ) {
        !isEmpty(first.depends): first.depends += \\$$escape_expand(\\n)
        first.depends += $$name
    }

    QMAKE_EXTRA_TARGETS *= first $$name

    export(xd_command_count)
    export($${name}.commands)
    export(first.depends)
    export(QMAKE_EXTRA_TARGETS)

    #eval( warning(xd_push_command: $${name}.commands += \$\${$${name}.commands}) )
}
#xd_postbuild(command)
defineTest(xd_postbuild) {
    !isEmpty(QMAKE_POST_LINK): QMAKE_POST_LINK = $$QMAKE_POST_LINK$$escape_expand(\\n\\t)
    QMAKE_POST_LINK = $${QMAKE_POST_LINK}$$quote(-$$1)

    export(QMAKE_POST_LINK)
}
#xd_escape(path)
#   resolves path like built-in functions (i.e. counts input relative to $$PWD)
defineReplace(xd_escape) {
    1 = $$absolute_path($$1)
    #using "shell_path()" to correct path depending on platform
    #   escaping quotes and backslashes for file paths
    1 = $$shell_path($$1)
    return($$quote($$1))
}

#copyFile(source, destination)
#   this will both copy and rename "source" to "destination", However like "copy_file()":
#       if "destination" is path to existing directory or ends with slash (i.e. "/" or "\\"),
#       will just copy to existing "destination" directory without any rename
#
#   note: this is executed before build, but after qmake did exit
#       so use "copy_file(...)" instead if the output file is required in qmake script
#       like for example if "write_file(...)" is called on the output...
defineTest(copyFile) {
    #note that "$(COPY_FILE)" is generated by qmake from "$$QMAKE_COPY_FILE"
    xd_prebuild(xd_copyfile, $(COPY_FILE) $$xd_escape($$1) $$xd_escape($$2) || echo copyFile-failed)
}

#copyFileLater(source, destination = $(DESTDIR))
#   note: this is executed after build is done, hence the name copy-later
defineTest(copyFileLater) {
    destDir = $$2
    isEmpty(destDir): destDir = $(DESTDIR)
    #append copy command
    xd_postbuild($(COPY_FILE) $$xd_escape($$1) $$xd_escape($$destDir) || echo copyFileLater-failed)

    #!build_pass:warning(copyFile: $$1 to: $$destDir)
}

#copyDir(source, destination)
defineTest(copyDir) {
    xd_prebuild(xd_copydir, $(COPY_DIR) $$xd_escape($$1) $$xd_escape($$2) || echo copyDir-failed)
}
#copyDirLater(source, destination = $(DESTDIR))
#   note: this is executed after build is done, hence the name copy-later
defineTest(copyDirLater) {
    destDir = $$2
    isEmpty(destDir): destDir = $(DESTDIR)
    #append copy command
    xd_postbuild($(COPY_DIR) $$xd_escape($$1) $$xd_escape($$destDir) || echo copyDirLater-failed)

    #!build_pass:warning(copyFile: $$1 to: $$destDir)
}

#makeDir(destination)
defineTest(makeDir) {
    xd_prebuild(xd_makedir, $(MKDIR) $$xd_escape($$1) || echo makeDir-failed: \"$$1\")
}
defineTest(makeDirLater) {
    xd_postbuild( $(MKDIR) $$xd_escape($$1) || echo makeDirLater-failed )
    #!build_pass:warning(makeDirLater: $$1)
}

defineTest(deleteFile) {
    xd_prebuild(xd_delfile, $(DEL_FILE) $$xd_escape($$1) || echo deleteFile-failed)
}
defineTest(deleteFileLater) {
    xd_postbuild( $(DEL_FILE) $$xd_escape($$1) || echo deleteFileLater-failed )
    #!build_pass:warning(deleteFileLater: $$1)
}
defineTest(deleteDir) {
    xd_prebuild(xd_delfile, $(DEL_DIR) $$xd_escape($$1) || echo deleteDir-failed)
}
defineTest(deleteDirLater) {
    xd_postbuild( $(DEL_DIR) $$xd_escape($$1) || echo deleteDirLater-failed )
    #!build_pass:warning(deleteFileLater: $$1)
}

#qmakeLater(qmake-script-file-path-to-run)
#   note that inside the script runned by this method
#   $$OUT_PWD will be same as original $$OUT_PWD of qmakeLater(...) caller project
#   since there is the "Makefile" that executes our custom qmake
defineTest(qmakeRun) {
    xd_postbuild( $(QMAKE) $$xd_escape($$1) -r -spec \"$$shell_path($$QMAKESPEC)\" )
    #!build_pass:warning(qmakeLater: $$1)
}
defineTest(qmakeLater) {
    xd_postbuild( $(QMAKE) $$xd_escape($$1) -r -spec \"$$shell_path($$QMAKESPEC)\" )
    #!build_pass:warning(qmakeLater: $$1)
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRaphaelView Question on Stackoverflow
Solution 1 - QtJake PetroulesView Answer on Stackoverflow
Solution 2 - Qtsje397View Answer on Stackoverflow
Solution 3 - QtOktalistView Answer on Stackoverflow
Solution 4 - QtCaleb Huitt - cjhuittView Answer on Stackoverflow
Solution 5 - QtOktalistView Answer on Stackoverflow
Solution 6 - QteatyourgreensView Answer on Stackoverflow
Solution 7 - QtmaxikView Answer on Stackoverflow
Solution 8 - QtlygstateView Answer on Stackoverflow
Solution 9 - QtTop-MasterView Answer on Stackoverflow