52

如何使用 qmake 将文件从我的项目复制到输出目录?

我正在 Linux 上编译,但将来我会在 Mac 和 Windows 上编译它。

4

9 回答 9

52

您可以使用 qmake 函数来实现可重用性:

# 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)
}

然后按如下方式使用它:

copyToDestdir($$OTHER_FILES) # a variable containing multiple paths
copyToDestdir(run.sh) # a single filename
copyToDestdir(run.sh README) # multiple files
于 2012-04-07T22:03:20.313 回答
31

这是我们的一个项目中的一个例子。它显示了如何将文件复制到DESTDIRWindows 和 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))
    }
}
于 2010-10-21T03:58:54.297 回答
28

Qt 5.6将此添加为未记录的功能:

CONFIG += file_copies

发明一个名称来描述您要复制的文件:

COPIES += myDocumentation

.files在其成员中列出要复制的文件:

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

在成员中指定目标路径.path

myDocumentation.path = $$OUT_PWD/documentation

(可选)指定要从源路径中修剪的基本路径:

myDocumentation.base = $$PWD/text/docs

它基本上可以通过与此处的许多其他答案相同的事情来工作。有关血腥的详细信息,请参见file_copies.prf

该界面与INSTALLS.

于 2019-01-12T18:45:14.753 回答
14

如果你使用 make install,你可以使用qmake 的 INSTALLS 变量。这是一个例子:

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

然后执行make install.

于 2010-10-21T19:35:46.313 回答
4

copy_files.prf在 qmake 用于配置功能的路径之一中创建一个文件。该文件应如下所示:

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}

这个怎么运作

第一部分定义了一个额外的编译器,它将从COPY_FILES变量中读取输入文件名。下一部分定义了用于合成对应于每个输入的输出文件名的函数。然后我们定义用于调用这个“编译器”的命令,这取决于我们所处的 shell 类型。

然后我们定义一个额外的 makefile 目标 copy_files.cookie,它依赖于目标compiler_copy_files_make_all。后者是 qmake 为我们在第一步中定义的额外编译器生成的目标的名称。这意味着在copy_files.cookie构建目标时,它将调用额外的编译器来复制文件。

我们指定一个命令由这个目标运行,它将touch文件copy_files.cookiecompiler_copy_files_make_all. 通过触摸这些文件,我们确保make不会再次尝试复制文件,除非它们的时间戳比被触摸的文件更新。最后,我们添加copy_files.cookiemake all目标的依赖项列表中。

如何使用它

在您的.pro文件中,添加copy_filesCONFIG变量:

CONFIG += copy_files

然后将文件添加到COPY_FILES变量中:

COPY_FILES += docs/*.txt
于 2015-10-23T19:36:53.090 回答
3

我发现我不得不修改 sje397 给出的答案。对于Qt5 Beta1QtCreator 2.5.2。作为构建完成后的附加步骤,我使用此脚本将 qml 文件复制到目标目录。

我的 .pro 文件有以下代码

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))
}

}

请注意,我使用 $$PWD_WIN 将源文件的完整路径提供给复制命令。

于 2012-09-28T12:07:01.380 回答
1

除了Jake 的回答和@Phlucious 评论之外,还可以使用defineReplace更适合此用例的 qmake 函数。使用提供的示例后,我遇到了一个问题,其中 qmake 跳过了我添加的最后一个帖子链接操作。尽管内容一直看起来都很好,但这可能是变量导出的问题。长话短说,这是修改后的代码

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)
}

这个通用的复制功能可能会被一些方便的功能使用,比如这个

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

第二个只接受一个参数(一个或多个文件)并提供固定路径。与参考答案中的几乎相同。

但现在注意调用差异

QMAKE_POST_LINK += $$copyToBuilddir(deploy.bat)

如您所见,您可以将返回的命令附加到 QMAKE_PRE_LINK 以获得更大的灵活性。

于 2016-06-01T07:21:32.020 回答
1

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)
}
于 2018-09-30T06:46:17.357 回答
1

首先,定义以下两个函数以同时支持 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)
}

然后您可以使用以下代码调用之前定义的函数将文件复制到特定文件夹中,并在必要时创建目录。这是在Win32下测试的,欢迎Linux测试。

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)
于 2018-07-22T04:45:08.933 回答