diff --git a/qbs/imports/QtcAutotest.qbs b/qbs/imports/QtcAutotest.qbs
index 376c2f91c64b44d71fa05c67a8b4541c2b6ff898..0bd37a905b23018b5645117de02a25bc5411694b 100644
--- a/qbs/imports/QtcAutotest.qbs
+++ b/qbs/imports/QtcAutotest.qbs
@@ -5,6 +5,7 @@ import QtcProduct
 QtcProduct {
     type: "application"
     Depends { name: "Qt.test" }
+    Depends { name: "copyable_resource" }
     targetName: "tst_" + name.split(' ').join("")
 
     // This needs to be absolute, because it is passed to one of the source files.
diff --git a/qbs/modules/copyable_resource/copyable-resource.qbs b/qbs/modules/copyable_resource/copyable-resource.qbs
new file mode 100644
index 0000000000000000000000000000000000000000..d8dda99a91983398edd08c8b4fc4bd53731255c5
--- /dev/null
+++ b/qbs/modules/copyable_resource/copyable-resource.qbs
@@ -0,0 +1,32 @@
+import qbs
+import qbs.File
+import qbs.FileInfo
+
+Module {
+    property path targetDirectory
+    additionalProductTypes: "copied_resource"
+    Rule {
+        inputs: ["copyable_resource"]
+        outputFileTags: ["copied_resource"]
+        outputArtifacts: {
+            var destinationDir = input.moduleProperty("copyable_resource", "targetDirectory");
+            if (!destinationDir) {
+                // If the destination directory has not been explicitly set, replicate the
+                // structure from the source directory in the build directory.
+                destinationDir = project.buildDirectory + '/'
+                        + FileInfo.relativePath(project.sourceDirectory, input.filePath);
+            }
+            return [{
+                filePath: destinationDir + '/' + input.fileName,
+                fileTags: ["copied_resource"]
+            }];
+        }
+        prepare: {
+            var cmd = new JavaScriptCommand();
+            cmd.description = "Copying " + FileInfo.fileName(input.fileName);
+            cmd.highlight = "codegen";
+            cmd.sourceCode = function() { File.copy(input.filePath, output.filePath); };
+            return cmd;
+        }
+    }
+}
diff --git a/tests/auto/extensionsystem/copytransformer.qbs b/tests/auto/extensionsystem/copytransformer.qbs
deleted file mode 100644
index c306748a8c05ebb0cd5f73ac2e766c7e72df0a2f..0000000000000000000000000000000000000000
--- a/tests/auto/extensionsystem/copytransformer.qbs
+++ /dev/null
@@ -1,26 +0,0 @@
-import qbs
-import qbs.File
-import qbs.FileInfo
-
-Transformer {
-    property pathList sourceFiles
-    property path targetDirectory
-    inputs: sourceFiles
-    Artifact { fileName: targetDirectory }
-    prepare: {
-        var commands = []
-        for (var tag in inputs) {
-            for (var index in inputs[tag]) {
-                var artifact = inputs[tag][index];
-                var cmd = new JavaScriptCommand();
-                cmd.sourceFile = artifact.filePath;
-                cmd.description = "Copying '" + cmd.sourceFile + "' to '" + output.filePath + "/'.";
-                cmd.highlight = "codegen";
-                cmd.targetFilePath = output.filePath + '/' + FileInfo.fileName(cmd.sourceFile);
-                cmd.sourceCode = function() { File.copy(sourceFile, targetFilePath); }
-                commands.push(cmd);
-            }
-        }
-        return commands;
-    }
-}
diff --git a/tests/auto/extensionsystem/plugin.qbs b/tests/auto/extensionsystem/plugin.qbs
index 98c27e15c70887dccd2664c8fc64bb8192f52d7d..8a97ad522e089d7458c2bf7f54b1bdcec5307dab 100644
--- a/tests/auto/extensionsystem/plugin.qbs
+++ b/tests/auto/extensionsystem/plugin.qbs
@@ -1,6 +1,5 @@
 import qbs
 import qbs.FileInfo
-import "./copytransformer.qbs" as CopyTransformer
 import QtcFunctions
 
 DynamicLibrary {
@@ -8,6 +7,7 @@ DynamicLibrary {
     Depends { name: "ExtensionSystem" }
     Depends { name: "cpp" }
     Depends { name: "Qt.core" }
+    Depends { name: "copyable_resource" }
     targetName: QtcFunctions.qtLibraryName(qbs, name.split('_')[1])
     destinationDirectory: project.buildDirectory + '/'
                           + FileInfo.relativePath(project.ide_source_tree, sourceDirectory)
@@ -17,8 +17,10 @@ DynamicLibrary {
     ].concat(additionalRPaths)
     property pathList filesToCopy
     property pathList additionalRPaths: []
-    CopyTransformer {
-        sourceFiles: product.filesToCopy
-        targetDirectory: product.destinationDirectory
+    Group {
+        name: "resources"
+        fileTags: "copyable_resource"
+        copyable_resource.targetDirectory: product.destinationDirectory
+        files: product.filesToCopy
     }
 }
diff --git a/tests/auto/extensionsystem/pluginmanager/circularplugins/plugin1/plugin1.qbs b/tests/auto/extensionsystem/pluginmanager/circularplugins/plugin1/plugin1.qbs
index 106403770ab440b68e838b308ffec0605f57c79b..b90744efb61130712c93eb6bd9237642fc74306e 100644
--- a/tests/auto/extensionsystem/pluginmanager/circularplugins/plugin1/plugin1.qbs
+++ b/tests/auto/extensionsystem/pluginmanager/circularplugins/plugin1/plugin1.qbs
@@ -4,6 +4,6 @@ import "../../../plugin.qbs" as Plugin
 Plugin {
     name: "circular_plugin1"
     filesToCopy: "plugin.xml"
-    files: ["plugin1.h", "plugin1.cpp"].concat(filesToCopy)
+    files: ["plugin1.h", "plugin1.cpp"]
     cpp.defines: base.concat(["PLUGIN1_LIBRARY"])
 }
diff --git a/tests/auto/extensionsystem/pluginmanager/circularplugins/plugin2/plugin2.qbs b/tests/auto/extensionsystem/pluginmanager/circularplugins/plugin2/plugin2.qbs
index a54b405a905f3f17d409ad73f4c4fac96199f792..5bf7389297650bff3f84e08d2d3b0c7f2fb56e36 100644
--- a/tests/auto/extensionsystem/pluginmanager/circularplugins/plugin2/plugin2.qbs
+++ b/tests/auto/extensionsystem/pluginmanager/circularplugins/plugin2/plugin2.qbs
@@ -4,6 +4,6 @@ import "../../../plugin.qbs" as Plugin
 Plugin {
     name: "circular_plugin2"
     filesToCopy: "plugin.xml"
-    files: ["plugin2.h", "plugin2.cpp"].concat(filesToCopy)
+    files: ["plugin2.h", "plugin2.cpp"]
     cpp.defines: base.concat(["PLUGIN2_LIBRARY"])
 }
diff --git a/tests/auto/extensionsystem/pluginmanager/circularplugins/plugin3/plugin3.qbs b/tests/auto/extensionsystem/pluginmanager/circularplugins/plugin3/plugin3.qbs
index 600741b2234b13535a4e32e8341fb3f0d5deba2c..5bf2bf3fadb247e28db6bff19a8386458da0640f 100644
--- a/tests/auto/extensionsystem/pluginmanager/circularplugins/plugin3/plugin3.qbs
+++ b/tests/auto/extensionsystem/pluginmanager/circularplugins/plugin3/plugin3.qbs
@@ -4,6 +4,6 @@ import "../../../plugin.qbs" as Plugin
 Plugin {
     name: "circular_plugin3"
     filesToCopy: "plugin.xml"
-    files: ["plugin3.h", "plugin3.cpp"].concat(filesToCopy)
+    files: ["plugin3.h", "plugin3.cpp"]
     cpp.defines: base.concat(["PLUGIN3_LIBRARY"])
 }
diff --git a/tests/auto/extensionsystem/pluginmanager/correctplugins1/plugin1/plugin1.qbs b/tests/auto/extensionsystem/pluginmanager/correctplugins1/plugin1/plugin1.qbs
index 9c73779a72265df46e6fcd5c854a45b18aa70807..01f72ee6f3d456a20c12893ff240ab1b4248d155 100644
--- a/tests/auto/extensionsystem/pluginmanager/correctplugins1/plugin1/plugin1.qbs
+++ b/tests/auto/extensionsystem/pluginmanager/correctplugins1/plugin1/plugin1.qbs
@@ -10,6 +10,6 @@ Plugin {
         destinationDirectory + "/../plugin2",
         destinationDirectory + "/../plugin3"
     ]
-    files: ["plugin1.h", "plugin1.cpp"].concat(filesToCopy)
+    files: ["plugin1.h", "plugin1.cpp"]
     cpp.defines: base.concat(["PLUGIN1_LIBRARY"])
 }
diff --git a/tests/auto/extensionsystem/pluginmanager/correctplugins1/plugin2/plugin2.qbs b/tests/auto/extensionsystem/pluginmanager/correctplugins1/plugin2/plugin2.qbs
index 638cb3072a69b0edbca5c86e4d26a087990fbcc4..916c5dfebc474b49fb3a7d44981cca4585710617 100644
--- a/tests/auto/extensionsystem/pluginmanager/correctplugins1/plugin2/plugin2.qbs
+++ b/tests/auto/extensionsystem/pluginmanager/correctplugins1/plugin2/plugin2.qbs
@@ -4,6 +4,6 @@ import "../../../plugin.qbs" as Plugin
 Plugin {
     name: "correct_plugin2"
     filesToCopy: "plugin.spec"
-    files: ["plugin2.h", "plugin2.cpp"].concat(filesToCopy)
+    files: ["plugin2.h", "plugin2.cpp"]
     cpp.defines: base.concat(["PLUGIN2_LIBRARY"])
 }
diff --git a/tests/auto/extensionsystem/pluginmanager/correctplugins1/plugin3/plugin3.qbs b/tests/auto/extensionsystem/pluginmanager/correctplugins1/plugin3/plugin3.qbs
index 9f0a5167aaf54b6eefecf87f5aee9d3be055b5dd..3f0ba4829589114c442b6f0f8ea368b71a6dc71b 100644
--- a/tests/auto/extensionsystem/pluginmanager/correctplugins1/plugin3/plugin3.qbs
+++ b/tests/auto/extensionsystem/pluginmanager/correctplugins1/plugin3/plugin3.qbs
@@ -6,6 +6,6 @@ Plugin {
     Depends { name: "correct_plugin2" }
     filesToCopy: "plugin.spec"
     additionalRPaths: [destinationDirectory + "/../plugin2"]
-    files: ["plugin3.h", "plugin3.cpp"].concat(filesToCopy)
+    files: ["plugin3.h", "plugin3.cpp"]
     cpp.defines: base.concat(["PLUGIN3_LIBRARY"])
 }
diff --git a/tests/auto/extensionsystem/pluginmanager/test.qbs b/tests/auto/extensionsystem/pluginmanager/test.qbs
index cc930a49cfc68099de79911ee879a364b99b59bd..47958855c31a802eadf56a31df215fbead066945 100644
--- a/tests/auto/extensionsystem/pluginmanager/test.qbs
+++ b/tests/auto/extensionsystem/pluginmanager/test.qbs
@@ -1,6 +1,5 @@
 import qbs
 import QtcAutotest
-import "../copytransformer.qbs" as CopyTransformer
 
 QtcAutotest {
     name: "PluginManager autotest"
@@ -15,6 +14,8 @@ QtcAutotest {
     Group {
         id: pluginGroup
         name: "plugins"
+        fileTags: "copyable_resource"
+        copyable_resource.targetDirectory: product.destinationDirectory + "/plugins"
         files: [
             "plugins/otherplugin.xml",
             "plugins/plugin1.xml",
@@ -22,11 +23,6 @@ QtcAutotest {
         ]
     }
 
-    CopyTransformer {
-        sourceFiles: pluginGroup.files
-        targetDirectory: product.destinationDirectory + "/plugins"
-    }
-
     files: "tst_pluginmanager.cpp"
     cpp.defines: base.concat(['PLUGINMANAGER_TESTS_DIR="' + destinationDirectory + '"'])
 }
diff --git a/tests/auto/extensionsystem/pluginspec/test.qbs b/tests/auto/extensionsystem/pluginspec/test.qbs
index e87698c333e9804cb49fa7dd65c22c38bfbf730c..365266d5e99d52f399a52408c6e42f5be42e1653 100644
--- a/tests/auto/extensionsystem/pluginspec/test.qbs
+++ b/tests/auto/extensionsystem/pluginspec/test.qbs
@@ -1,7 +1,5 @@
 import qbs
-
 import QtcAutotest
-import "../copytransformer.qbs" as CopyTransformer
 
 QtcAutotest {
     name: "ExtensionSystem pluginspec autotest"
@@ -13,6 +11,8 @@ QtcAutotest {
     Group {
         id: testSpecsGroup
         name: "test specs"
+        fileTags: "copyable_resource"
+        copyable_resource.targetDirectory: product.destinationDirectory + "/testspecs"
         files: [
             "testspecs/simplespec.xml",
             "testspecs/simplespec_experimental.xml",
@@ -28,6 +28,8 @@ QtcAutotest {
     Group {
         id: testDependenciesGroup
         name: "test dependencies"
+        fileTags: "copyable_resource"
+        copyable_resource.targetDirectory: product.destinationDirectory + "/testdependencies"
         files: [
             "testdependencies/spec1.xml",
             "testdependencies/spec2.xml",
@@ -39,21 +41,8 @@ QtcAutotest {
     Group {
         id: specGroup
         name: "spec"
+        fileTags: "copyable_resource"
+        copyable_resource.targetDirectory: product.destinationDirectory + "/testdir"
         files: ["testdir/spec.xml"]
     }
-
-    CopyTransformer {
-        sourceFiles: testSpecsGroup.files
-        targetDirectory: product.destinationDirectory + "/testspecs"
-    }
-
-    CopyTransformer {
-        sourceFiles: testDependenciesGroup.files
-        targetDirectory: product.destinationDirectory + "/testdependencies"
-    }
-
-    CopyTransformer {
-        sourceFiles: specGroup.files
-        targetDirectory: product.destinationDirectory + "/testdir"
-    }
 }
diff --git a/tests/auto/extensionsystem/pluginspec/testplugin/testplugin.qbs b/tests/auto/extensionsystem/pluginspec/testplugin/testplugin.qbs
index 2ed6f66b597b5e8053bcd7b9b42baf3f2473d041..5bf464c01d49468973f8071018e845ec0b01794d 100644
--- a/tests/auto/extensionsystem/pluginspec/testplugin/testplugin.qbs
+++ b/tests/auto/extensionsystem/pluginspec/testplugin/testplugin.qbs
@@ -6,7 +6,7 @@ Plugin {
     files: [
         "testplugin.h", "testplugin.cpp",
         "testplugin_global.h"
-    ].concat(filesToCopy)
+    ]
     filesToCopy: "testplugin.xml"
     cpp.defines: base.concat(["MYPLUGIN_LIBRARY"])
 }