maven resource builder: show path to problematic file if copying failed (ZD-66983)
[idea/community.git] / plugins / maven / jps-plugin / src / org / jetbrains / jps / maven / compiler / MavenResourcesBuilder.java
1 package org.jetbrains.jps.maven.compiler;
2
3 import com.intellij.openapi.diagnostic.Logger;
4 import com.intellij.openapi.util.io.FileUtil;
5 import com.intellij.openapi.util.io.FileUtilRt;
6 import org.jetbrains.annotations.NotNull;
7 import org.jetbrains.jps.builders.BuildOutputConsumer;
8 import org.jetbrains.jps.builders.DirtyFilesHolder;
9 import org.jetbrains.jps.builders.FileProcessor;
10 import org.jetbrains.jps.builders.storage.BuildDataPaths;
11 import org.jetbrains.jps.incremental.CompileContext;
12 import org.jetbrains.jps.incremental.ProjectBuildException;
13 import org.jetbrains.jps.incremental.StopBuildException;
14 import org.jetbrains.jps.incremental.TargetBuilder;
15 import org.jetbrains.jps.incremental.messages.BuildMessage;
16 import org.jetbrains.jps.incremental.messages.CompilerMessage;
17 import org.jetbrains.jps.incremental.messages.ProgressMessage;
18 import org.jetbrains.jps.maven.model.JpsMavenExtensionService;
19 import org.jetbrains.jps.maven.model.impl.*;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.UnsupportedEncodingException;
24 import java.util.*;
25
26 /**
27  * @author Eugene Zhuravlev
28  *         Date: 10/6/11
29  */
30 public class MavenResourcesBuilder extends TargetBuilder<MavenResourceRootDescriptor, MavenResourcesTarget> {
31   private static final Logger LOG = Logger.getInstance(MavenResourcesBuilder.class);
32   public static final String BUILDER_NAME = "Maven Resources Compiler";
33
34   public MavenResourcesBuilder() {
35     super(Arrays.asList(MavenResourcesTargetType.PRODUCTION, MavenResourcesTargetType.TEST));
36   }
37
38   @Override
39   public void build(@NotNull final MavenResourcesTarget target, @NotNull final DirtyFilesHolder<MavenResourceRootDescriptor, MavenResourcesTarget> holder, @NotNull final BuildOutputConsumer outputConsumer, @NotNull final CompileContext context) throws ProjectBuildException, IOException {
40     final BuildDataPaths dataPaths = context.getProjectDescriptor().dataManager.getDataPaths();
41     final MavenProjectConfiguration projectConfig = JpsMavenExtensionService.getInstance().getMavenProjectConfiguration(dataPaths);
42     if (projectConfig == null) {
43       context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, "Maven project configuration required for module '" + target.getModule().getName() + "' isn't available. Compilation of Maven projects is supported only if external build is started from an IDE."));
44       throw new StopBuildException();
45     }
46
47     final MavenModuleResourceConfiguration config = target.getModuleResourcesConfiguration(dataPaths);
48     if (config == null) {
49       return;
50     }
51
52     final Map<MavenResourceRootDescriptor, List<File>> files = new HashMap<MavenResourceRootDescriptor, List<File>>();
53
54     holder.processDirtyFiles(new FileProcessor<MavenResourceRootDescriptor, MavenResourcesTarget>() {
55
56       @Override
57       public boolean apply(MavenResourcesTarget t, File file, MavenResourceRootDescriptor rd) throws IOException {
58         assert target == t;
59
60         List<File> fileList = files.get(rd);
61         if (fileList == null) {
62           fileList = new ArrayList<File>();
63           files.put(rd, fileList);
64         }
65
66         fileList.add(file);
67         return true;
68       }
69     });
70
71     MavenResourceRootDescriptor[] roots = files.keySet().toArray(new MavenResourceRootDescriptor[files.keySet().size()]);
72     Arrays.sort(roots, new Comparator<MavenResourceRootDescriptor>() {
73       @Override
74       public int compare(MavenResourceRootDescriptor r1, MavenResourceRootDescriptor r2) {
75         int res = r1.getIndexInPom() - r2.getIndexInPom();
76
77         if (r1.isOverwrite()) {
78           assert r2.isOverwrite(); // 'overwrite' parameters is common for all roots in module.
79
80           return res;
81         }
82
83         if (r1.getConfiguration().isFiltered && !r2.getConfiguration().isFiltered) return 1;
84         if (!r1.getConfiguration().isFiltered && r2.getConfiguration().isFiltered) return -1;
85
86         if (!r1.getConfiguration().isFiltered) {
87           res = -res;
88         }
89
90         return res;
91       }
92     });
93
94     MavenResourceFileProcessor fileProcessor = new MavenResourceFileProcessor(projectConfig, target.getModule().getProject(), config);
95
96     context.processMessage(new ProgressMessage("Copying resources... [" + target.getModule().getName() + "]"));
97     for (MavenResourceRootDescriptor rd : roots) {
98       for (File file : files.get(rd)) {
99
100         String relPath = FileUtil.getRelativePath(rd.getRootFile(), file);
101         if (relPath == null) {
102           continue;
103         }
104         final String outputDirectory = target.isTests() ? config.testOutputDirectory : config.outputDirectory;
105         final File outputDir = MavenResourcesTarget.getOutputDir(target.getModuleOutputDir(), rd.getConfiguration(), outputDirectory);
106         if (outputDir == null) {
107           continue;
108         }
109         File outputFile = new File(outputDir, relPath);
110         String sourcePath = file.getPath();
111         try {
112           fileProcessor.copyFile(file, outputFile, rd.getConfiguration(), context, FileUtilRt.ALL_FILES);
113           outputConsumer.registerOutputFile(outputFile, Collections.singleton(sourcePath));
114         }
115         catch (UnsupportedEncodingException e) {
116           context.processMessage(
117             new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.INFO, "Resource was not copied: " + e.getMessage(), sourcePath));
118         }
119         catch (IOException e) {
120           context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, "Failed to copy '" + sourcePath + "' to '" + outputFile.getAbsolutePath() + "': " + e.getMessage()));
121           LOG.info(e);
122         }
123
124         if (context.getCancelStatus().isCanceled()) {
125           return;
126         }
127       }
128     }
129
130     context.checkCanceled();
131
132     context.processMessage(new ProgressMessage(""));
133   }
134
135   @NotNull
136   public String getPresentableName() {
137     return BUILDER_NAME;
138   }
139 }