ac661db18cfb4c7970edce38916804f2f86ba942
[idea/community.git] / jps / jps-builders / src / org / jetbrains / jps / incremental / artifacts / builders / LayoutElementBuildersRegistry.java
1 /*
2  * Copyright 2000-2012 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.jetbrains.jps.incremental.artifacts.builders;
17
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.openapi.util.text.StringUtil;
20 import com.intellij.util.containers.ClassMap;
21 import com.intellij.util.containers.ContainerUtil;
22 import org.jetbrains.annotations.NotNull;
23 import org.jetbrains.annotations.Nullable;
24 import org.jetbrains.jps.builders.BuildTarget;
25 import org.jetbrains.jps.builders.TargetOutputIndex;
26 import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType;
27 import org.jetbrains.jps.incremental.ModuleBuildTarget;
28 import org.jetbrains.jps.incremental.artifacts.instructions.ArtifactCompilerInstructionCreator;
29 import org.jetbrains.jps.incremental.artifacts.instructions.ArtifactInstructionsBuilderContext;
30 import org.jetbrains.jps.incremental.artifacts.instructions.CopyToDirectoryInstructionCreator;
31 import org.jetbrains.jps.model.artifact.JpsArtifact;
32 import org.jetbrains.jps.model.artifact.elements.*;
33 import org.jetbrains.jps.model.java.*;
34 import org.jetbrains.jps.model.module.JpsModule;
35 import org.jetbrains.jps.model.module.JpsModuleSourceRoot;
36 import org.jetbrains.jps.service.JpsServiceManager;
37 import org.jetbrains.jps.util.JpsPathUtil;
38
39 import java.io.File;
40 import java.util.Collection;
41 import java.util.Collections;
42 import java.util.List;
43 import java.util.Set;
44
45 public class LayoutElementBuildersRegistry {
46   private static final Logger LOG = Logger.getInstance(LayoutElementBuildersRegistry.class);
47
48   private static class InstanceHolder {
49
50     static final LayoutElementBuildersRegistry ourInstance = new LayoutElementBuildersRegistry();
51   }
52   public static LayoutElementBuildersRegistry getInstance() {
53     return InstanceHolder.ourInstance;
54   }
55
56   private final ClassMap<LayoutElementBuilderService> myBuilders;
57
58   private LayoutElementBuildersRegistry() {
59     myBuilders = new ClassMap<>();
60     LayoutElementBuilderService<?>[] standardBuilders = {
61       new RootElementBuilder(), new DirectoryElementBuilder(), new ArchiveElementBuilder(), new DirectoryCopyElementBuilder(),
62       new FileCopyElementBuilder(), new ExtractedDirectoryElementBuilder(), new ModuleOutputElementBuilder(),
63       new ModuleSourceElementBuilder(),
64       new ModuleTestOutputElementBuilder(), new ComplexElementBuilder(), new ArtifactOutputElementBuilder()
65     };
66     for (LayoutElementBuilderService<?> builder : standardBuilders) {
67       myBuilders.put(builder.getElementClass(), builder);
68     }
69     for (LayoutElementBuilderService builder : JpsServiceManager.getInstance().getExtensions(LayoutElementBuilderService.class)) {
70       myBuilders.put(builder.getElementClass(), builder);
71     }
72   }
73
74   public void generateInstructions(JpsArtifact artifact, CopyToDirectoryInstructionCreator creator, ArtifactInstructionsBuilderContext context) {
75     context.enterArtifact(artifact);
76     generateInstructions(artifact.getRootElement(), creator, context);
77   }
78
79   public Collection<BuildTarget<?>> getDependencies(JpsPackagingElement element, TargetOutputIndex outputIndex) {
80     LayoutElementBuilderService builder = getElementBuilder(element);
81     if (builder != null) {
82       //noinspection unchecked
83       return builder.getDependencies(element, outputIndex);
84     }
85     return Collections.emptyList();
86   }
87
88   private void generateInstructions(JpsPackagingElement layoutElement, ArtifactCompilerInstructionCreator instructionCreator,
89                                    ArtifactInstructionsBuilderContext builderContext) {
90     final LayoutElementBuilderService builder = getElementBuilder(layoutElement);
91     if (builder != null) {
92       //noinspection unchecked
93       builder.generateInstructions(layoutElement, instructionCreator, builderContext);
94     }
95   }
96
97   private LayoutElementBuilderService<?> getElementBuilder(JpsPackagingElement layoutElement) {
98     final LayoutElementBuilderService<?> builder = myBuilders.get(layoutElement.getClass());
99     if (builder == null) {
100       LOG.error("Builder not found for artifact output layout element of class " + layoutElement.getClass());
101     }
102     return builder;
103   }
104
105   private void generateChildrenInstructions(JpsCompositePackagingElement element, ArtifactCompilerInstructionCreator instructionCreator,
106                                             ArtifactInstructionsBuilderContext builderContext) {
107     generateInstructions(element.getChildren(), instructionCreator, builderContext);
108   }
109
110   private void generateSubstitutionInstructions(JpsComplexPackagingElement element,
111                                                 ArtifactCompilerInstructionCreator instructionCreator,
112                                                 ArtifactInstructionsBuilderContext builderContext) {
113     final List<JpsPackagingElement> substitution = element.getSubstitution();
114     if (substitution != null) {
115       generateInstructions(substitution, instructionCreator, builderContext);
116     }
117   }
118
119   private void generateInstructions(final List<JpsPackagingElement> elements, ArtifactCompilerInstructionCreator instructionCreator,
120                                     ArtifactInstructionsBuilderContext builderContext) {
121     for (JpsPackagingElement child : elements) {
122       generateInstructions(child, instructionCreator, builderContext);
123     }
124   }
125
126   private static void generateModuleSourceInstructions(@NotNull List<JpsModuleSourceRoot> roots,
127                                                        @NotNull ArtifactCompilerInstructionCreator creator,
128                                                        @NotNull JpsPackagingElement contextElement) {
129     for (JpsModuleSourceRoot root : roots) {
130       File source = root.getFile();
131       ArtifactCompilerInstructionCreator target;
132       JavaSourceRootProperties javaProperties = root.getProperties(JavaModuleSourceRootTypes.SOURCES);
133       if (javaProperties != null) {
134         String prefix = javaProperties.getPackagePrefix().replace('.', '/');
135         target = creator.subFolderByRelativePath(prefix);
136       } else {
137         target = creator;
138       }
139
140       target.addDirectoryCopyInstructions(source, null, target.getInstructionsBuilder().createCopyingHandler(source, contextElement, target));
141     }
142   }
143
144   private static void generateModuleOutputInstructions(@Nullable String outputUrl,
145                                                        @NotNull ArtifactCompilerInstructionCreator creator,
146                                                        @NotNull JpsPackagingElement contextElement) {
147     if (outputUrl != null) {
148       File directory = JpsPathUtil.urlToFile(outputUrl);
149       creator.addDirectoryCopyInstructions(directory, null, creator.getInstructionsBuilder().createCopyingHandler(directory, contextElement, creator));
150     }
151   }
152
153   private class RootElementBuilder extends LayoutElementBuilderService<JpsArtifactRootElement> {
154     RootElementBuilder() {
155       super(JpsArtifactRootElement.class);
156     }
157
158     @Override
159     public void generateInstructions(JpsArtifactRootElement element, ArtifactCompilerInstructionCreator instructionCreator, ArtifactInstructionsBuilderContext builderContext) {
160       generateChildrenInstructions(element, instructionCreator, builderContext);
161     }
162   }
163
164   private class DirectoryElementBuilder extends LayoutElementBuilderService<JpsDirectoryPackagingElement> {
165     DirectoryElementBuilder() {
166       super(JpsDirectoryPackagingElement.class);
167     }
168
169     @Override
170     public void generateInstructions(JpsDirectoryPackagingElement element,
171                                      ArtifactCompilerInstructionCreator instructionCreator,
172                                      ArtifactInstructionsBuilderContext builderContext) {
173       generateChildrenInstructions(element, instructionCreator.subFolder(element.getDirectoryName()), builderContext);
174     }
175   }
176
177   private class ArchiveElementBuilder extends LayoutElementBuilderService<JpsArchivePackagingElement> {
178     ArchiveElementBuilder() {
179       super(JpsArchivePackagingElement.class);
180     }
181
182     @Override
183     public void generateInstructions(JpsArchivePackagingElement element, ArtifactCompilerInstructionCreator instructionCreator,
184                                      ArtifactInstructionsBuilderContext builderContext) {
185       generateChildrenInstructions(element, instructionCreator.archive(element.getArchiveName()), builderContext);
186     }
187   }
188
189   private static class DirectoryCopyElementBuilder extends LayoutElementBuilderService<JpsDirectoryCopyPackagingElement> {
190     DirectoryCopyElementBuilder() {
191       super(JpsDirectoryCopyPackagingElement.class);
192     }
193
194     @Override
195     public void generateInstructions(JpsDirectoryCopyPackagingElement element, ArtifactCompilerInstructionCreator instructionCreator,
196                                      ArtifactInstructionsBuilderContext builderContext) {
197       final String dirPath = element.getDirectoryPath();
198       if (dirPath != null) {
199         final File directory = new File(dirPath);
200         instructionCreator.addDirectoryCopyInstructions(directory, null, instructionCreator.getInstructionsBuilder().createCopyingHandler(directory, element, instructionCreator));
201       }
202     }
203
204     @Override
205     public Collection<? extends BuildTarget<?>> getDependencies(@NotNull JpsDirectoryCopyPackagingElement element,
206                                                                 TargetOutputIndex outputIndex) {
207       String dirPath = element.getDirectoryPath();
208       if (dirPath != null) {
209         return outputIndex.getTargetsByOutputFile(new File(dirPath));
210       }
211       return Collections.emptyList();
212     }
213   }
214
215   private static class FileCopyElementBuilder extends LayoutElementBuilderService<JpsFileCopyPackagingElement> {
216     FileCopyElementBuilder() {
217       super(JpsFileCopyPackagingElement.class);
218     }
219
220     @Override
221     public void generateInstructions(JpsFileCopyPackagingElement element, ArtifactCompilerInstructionCreator instructionCreator,
222                                      ArtifactInstructionsBuilderContext builderContext) {
223       final String filePath = element.getFilePath();
224       if (filePath != null) {
225         final File file = new File(filePath);
226         final String fileName = element.getRenamedOutputFileName();
227         String outputFileName = fileName != null ? fileName : file.getName();
228         instructionCreator.addFileCopyInstruction(file, outputFileName,
229                                                   instructionCreator.getInstructionsBuilder().createCopyingHandler(file, element, instructionCreator));
230       }
231     }
232
233     @Override
234     public Collection<? extends BuildTarget<?>> getDependencies(@NotNull JpsFileCopyPackagingElement element,
235                                                                 TargetOutputIndex outputIndex) {
236       String filePath = element.getFilePath();
237       if (filePath != null) {
238         return outputIndex.getTargetsByOutputFile(new File(filePath));
239       }
240       return Collections.emptyList();
241     }
242   }
243
244   private static class ExtractedDirectoryElementBuilder extends LayoutElementBuilderService<JpsExtractedDirectoryPackagingElement> {
245     ExtractedDirectoryElementBuilder() {
246       super(JpsExtractedDirectoryPackagingElement.class);
247     }
248
249     @Override
250     public void generateInstructions(JpsExtractedDirectoryPackagingElement element,
251                                      ArtifactCompilerInstructionCreator instructionCreator,
252                                      ArtifactInstructionsBuilderContext builderContext) {
253       final String jarPath = element.getFilePath();
254       final String pathInJar = element.getPathInJar();
255       instructionCreator.addExtractDirectoryInstruction(new File(jarPath), pathInJar);
256     }
257   }
258
259   private static class ModuleOutputElementBuilder extends LayoutElementBuilderService<JpsProductionModuleOutputPackagingElement> {
260     ModuleOutputElementBuilder() {
261       super(JpsProductionModuleOutputPackagingElement.class);
262     }
263
264     @Override
265     public void generateInstructions(JpsProductionModuleOutputPackagingElement element,
266                                      ArtifactCompilerInstructionCreator instructionCreator,
267                                      ArtifactInstructionsBuilderContext builderContext) {
268       generateModuleOutputInstructions(element.getOutputUrl(), instructionCreator, element);
269     }
270
271     @Override
272     public Collection<? extends BuildTarget<?>> getDependencies(@NotNull JpsProductionModuleOutputPackagingElement element,
273                                                                 TargetOutputIndex outputIndex) {
274       JpsModule module = element.getModuleReference().resolve();
275       if (module != null) {
276         return Collections.singletonList(new ModuleBuildTarget(module, JavaModuleBuildTargetType.PRODUCTION));
277       }
278       return Collections.emptyList();
279     }
280   }
281
282   private static class ModuleSourceElementBuilder extends LayoutElementBuilderService<JpsProductionModuleSourcePackagingElement> {
283     ModuleSourceElementBuilder() {
284       super(JpsProductionModuleSourcePackagingElement.class);
285     }
286
287     @Override
288     public void generateInstructions(JpsProductionModuleSourcePackagingElement element,
289                                      ArtifactCompilerInstructionCreator instructionCreator,
290                                      ArtifactInstructionsBuilderContext builderContext) {
291       JpsModule module = element.getModuleReference().resolve();
292       if (module != null) {
293         List<JpsModuleSourceRoot> productionSources = ContainerUtil.filter(module.getSourceRoots(), root -> JavaModuleSourceRootTypes.PRODUCTION.contains(root.getRootType()));
294         generateModuleSourceInstructions(productionSources, instructionCreator, element);
295       }
296     }
297
298     @Override
299     public Collection<? extends BuildTarget<?>> getDependencies(@NotNull JpsProductionModuleSourcePackagingElement element,
300                                                                 TargetOutputIndex outputIndex) {
301       return Collections.emptyList();
302     }
303   }
304
305   private static class ModuleTestOutputElementBuilder extends LayoutElementBuilderService<JpsTestModuleOutputPackagingElement> {
306     ModuleTestOutputElementBuilder() {
307       super(JpsTestModuleOutputPackagingElement.class);
308     }
309
310     @Override
311     public void generateInstructions(JpsTestModuleOutputPackagingElement element,
312                                      ArtifactCompilerInstructionCreator instructionCreator,
313                                      ArtifactInstructionsBuilderContext builderContext) {
314       generateModuleOutputInstructions(element.getOutputUrl(), instructionCreator, element);
315     }
316
317     @Override
318     public Collection<? extends BuildTarget<?>> getDependencies(@NotNull JpsTestModuleOutputPackagingElement element,
319                                                                 TargetOutputIndex outputIndex) {
320       JpsModule module = element.getModuleReference().resolve();
321       if (module != null) {
322         return Collections.singletonList(new ModuleBuildTarget(module, JavaModuleBuildTargetType.TEST));
323       }
324       return Collections.emptyList();
325     }
326   }
327
328   private class ComplexElementBuilder extends LayoutElementBuilderService<JpsComplexPackagingElement> {
329     ComplexElementBuilder() {
330       super(JpsComplexPackagingElement.class);
331     }
332
333     @Override
334     public void generateInstructions(JpsComplexPackagingElement element,
335                                      ArtifactCompilerInstructionCreator instructionCreator,
336                                      ArtifactInstructionsBuilderContext builderContext) {
337       generateSubstitutionInstructions(element, instructionCreator, builderContext);
338     }
339   }
340
341   private class ArtifactOutputElementBuilder extends LayoutElementBuilderService<JpsArtifactOutputPackagingElement> {
342     ArtifactOutputElementBuilder() {
343       super(JpsArtifactOutputPackagingElement.class);
344     }
345
346     @Override
347     public void generateInstructions(JpsArtifactOutputPackagingElement element,
348                                      ArtifactCompilerInstructionCreator instructionCreator,
349                                      ArtifactInstructionsBuilderContext builderContext) {
350       final JpsArtifact artifact = element.getArtifactReference().resolve();
351       if (artifact == null) return;
352
353       Set<JpsArtifact> parentArtifacts = builderContext.getParentArtifacts();
354       List<JpsPackagingElement> customLayout = getCustomArtifactLayout(artifact, parentArtifacts);
355
356       final String outputPath = artifact.getOutputPath();
357       if (StringUtil.isEmpty(outputPath) || customLayout != null) {
358         try {
359           if (builderContext.enterArtifact(artifact)) {
360             if (customLayout != null) {
361               LayoutElementBuildersRegistry.this.generateInstructions(customLayout, instructionCreator, builderContext);
362             }
363             else {
364               generateSubstitutionInstructions(element, instructionCreator, builderContext);
365             }
366           }
367         }
368         finally {
369           builderContext.leaveArtifact(artifact);
370         }
371         return;
372       }
373
374       final JpsPackagingElement rootElement = artifact.getRootElement();
375       final File outputDir = new File(outputPath);
376       if (rootElement instanceof JpsArchivePackagingElement) {
377         final String fileName = ((JpsArchivePackagingElement)rootElement).getArchiveName();
378         instructionCreator.addFileCopyInstruction(new File(outputDir, fileName), fileName);
379       }
380       else {
381         instructionCreator.addDirectoryCopyInstructions(outputDir);
382       }
383     }
384
385     @Nullable
386     private List<JpsPackagingElement> getCustomArtifactLayout(@NotNull JpsArtifact artifact, @NotNull Set<JpsArtifact> parentArtifacts) {
387       for (ArtifactLayoutCustomizationService service : JpsServiceManager.getInstance().getExtensions(ArtifactLayoutCustomizationService.class)) {
388         List<JpsPackagingElement> elements = service.getCustomizedLayout(artifact, parentArtifacts);
389         if (elements != null) {
390           return elements;
391         }
392       }
393       return null;
394     }
395   }
396 }