package com.intellij.javascript.flex.maven; import com.intellij.flex.model.bc.OutputType; import com.intellij.ide.IdeBundle; import com.intellij.lang.javascript.flex.FlexBundle; import com.intellij.lang.javascript.flex.FlexUtils; import com.intellij.lang.javascript.flex.projectStructure.FlexBuildConfigurationsExtension; import com.intellij.lang.javascript.flex.projectStructure.model.ModifiableFlexBuildConfiguration; import com.intellij.lang.javascript.flex.projectStructure.model.impl.FlexProjectConfigurationEditor; import com.intellij.openapi.module.Module; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable; import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.changes.ChangeListManager; import com.intellij.openapi.vcs.changes.IgnoredBeanFactory; import com.intellij.openapi.vcs.changes.IgnoredFileBean; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.idea.maven.model.MavenExplicitProfiles; import org.jetbrains.idea.maven.model.MavenWorkspaceMap; import org.jetbrains.idea.maven.project.*; import org.jetbrains.idea.maven.server.MavenEmbedderWrapper; import org.jetbrains.idea.maven.server.MavenServerExecutionResult; import org.jetbrains.idea.maven.utils.MavenLog; import org.jetbrains.idea.maven.utils.MavenProcessCanceledException; import org.jetbrains.idea.maven.utils.MavenProgressIndicator; import org.jetbrains.idea.maven.utils.MavenUtil; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; public class Flexmojos3GenerateConfigTask extends MavenProjectsProcessorBasicTask { private static final String TEMPORARY_FILE_CONTENT = "Remove this file"; private final Module myModule; private final String myConfigFilePath; private final FlexConfigInformer myFlexConfigInformer; public Flexmojos3GenerateConfigTask(final Module module, final MavenProject mavenProject, final MavenProjectsTree mavenTree, final String configFilePath, final FlexConfigInformer flexConfigInformer) { super(mavenProject, mavenTree); myModule = module; myConfigFilePath = configFilePath; myFlexConfigInformer = flexConfigInformer; } public void perform(final Project project, final MavenEmbeddersManager embeddersManager, final MavenConsole console, final MavenProgressIndicator indicator) throws MavenProcessCanceledException { if (myModule.isDisposed()) return; indicator.setText(FlexBundle.message("generating.flex.config.for", myMavenProject.getDisplayName())); final MavenProjectsTree.EmbedderTask task = new MavenProjectsTree.EmbedderTask() { public void run(MavenEmbedderWrapper embedder) throws MavenProcessCanceledException { List<VirtualFile> temporaryFiles = null; final IgnoredFileBean[] filesToIgnoreOriginal = ChangeListManager.getInstance(project).getFilesToIgnore(); try { MavenWorkspaceMap workspaceMap = new MavenWorkspaceMap(); temporaryFiles = mavenIdToOutputFileMapping(workspaceMap, project, myTree.getProjects()); embedder.customizeForStrictResolve(workspaceMap, console, indicator); final String generateConfigGoal = FlexmojosImporter.FLEXMOJOS_GROUP_ID + ":" + FlexmojosImporter.FLEXMOJOS_ARTIFACT_ID + ":generate-config-" + myMavenProject.getPackaging(); final MavenExplicitProfiles profilesIds = myMavenProject.getActivatedProfilesIds(); MavenServerExecutionResult result = embedder .execute(myMavenProject.getFile(), profilesIds.getEnabledProfiles(), profilesIds.getDisabledProfiles(), Collections.singletonList(generateConfigGoal)); if (result.projectData == null) { myFlexConfigInformer.showFlexConfigWarningIfNeeded(project); } MavenUtil.invokeAndWaitWriteAction(project, () -> { // need to refresh externally created file final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByPath(myConfigFilePath); if (file != null) { file.refresh(false, false); updateMainClass(myModule, file); } }); } catch (MavenProcessCanceledException e) { throw e; } catch (Exception e) { myFlexConfigInformer.showFlexConfigWarningIfNeeded(project); console.printException(e); MavenLog.LOG.warn(e); } finally { ChangeListManager.getInstance(project).setFilesToIgnore(filesToIgnoreOriginal); if (temporaryFiles != null && !temporaryFiles.isEmpty()) { removeTemporaryFiles(project, temporaryFiles); } } } }; myTree.executeWithEmbedder(myMavenProject, embeddersManager, MavenEmbeddersManager.FOR_POST_PROCESSING, console, indicator, task); } /** * For SWF- and SWC-packaged maven projects returned result contains mapping to respective SWF/SWC target file. * If such SWF/SWC file doesn't not exist - temporary file is created. * Caller of this method is responsible for removing placeholder files * (see {@link #removeTemporaryFiles(com.intellij.openapi.project.Project, java.util.Collection)}).<br> * For not SWF/SWC projects - reference to pom.xml file is placed in result map. */ private static List<VirtualFile> mavenIdToOutputFileMapping(final MavenWorkspaceMap workspaceMap, final Project project, final Collection<MavenProject> mavenProjects) throws IOException { final Ref<IOException> exception = new Ref<>(); final List<VirtualFile> temporaryFiles = new ArrayList<>(); MavenUtil.invokeAndWaitWriteAction(project, () -> { try { for (MavenProject mavenProject : mavenProjects) { if (ArrayUtil.contains(mavenProject.getPackaging(), FlexmojosImporter.SUPPORTED_PACKAGINGS)) { final String outputFilePath = FlexmojosImporter.getOutputFilePath(mavenProject); final int lastSlashIndex = outputFilePath.lastIndexOf("/"); final String outputFileName = outputFilePath.substring(lastSlashIndex + 1); final String outputFolderPath = outputFilePath.substring(0, Math.max(0, lastSlashIndex)); VirtualFile outputFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(outputFilePath); if (outputFile == null) { final VirtualFile outputDir = VfsUtil.createDirectoryIfMissing(outputFolderPath); if (outputDir == null) throw new IOException(IdeBundle.message("error.failed.to.create.directory", outputFolderPath)); // if maven project is not compiled and output file doesn't exist flexmojos fails to generate Flex compiler configuration file. // Workaround is to create empty placeholder file. ChangeListManager.getInstance(project).addFilesToIgnore(IgnoredBeanFactory.ignoreFile(outputFilePath, project)); outputFile = FlexUtils.addFileWithContent(outputFileName, TEMPORARY_FILE_CONTENT, outputDir); if (outputFile == null) throw new IOException(IdeBundle.message("error.message.unable.to.create.file", outputFileName)); temporaryFiles.add(outputFile); } workspaceMap.register(mavenProject.getMavenId(), new File(mavenProject.getFile().getPath()), new File(outputFile.getPath())); } else { workspaceMap.register(mavenProject.getMavenId(), new File(mavenProject.getFile().getPath())); } } } catch (IOException e) { exception.set(e); } }); if (!exception.isNull()) throw exception.get(); return temporaryFiles; } private static void removeTemporaryFiles(final Project project, final Collection<VirtualFile> files) { MavenUtil.invokeAndWaitWriteAction(project, () -> { for (VirtualFile file : files) { try { if (file.isValid() && file.getLength() == TEMPORARY_FILE_CONTENT.length() && new String(file.contentsToByteArray()).equals(TEMPORARY_FILE_CONTENT)) { file.delete(Flexmojos3GenerateConfigTask.class); } } catch (IOException e) {/*ignore*/} } }); } public static void updateMainClass(final Module module, final VirtualFile configFile) { if (FlexBuildConfigurationsExtension.getInstance().getConfigurator().getConfigEditor() != null) return; // Project Structure open try { final String mainClassPath = FlexUtils.findXMLElement(configFile.getInputStream(), "<flex-config><file-specs><path-element>"); final VirtualFile mainClassFile = mainClassPath == null ? null : LocalFileSystem.getInstance().findFileByPath(mainClassPath); if (mainClassFile == null || mainClassFile.isDirectory()) return; final VirtualFile sourceRoot = ProjectRootManager.getInstance(module.getProject()).getFileIndex().getSourceRootForFile(mainClassFile); final String relativePath = sourceRoot == null ? null : VfsUtilCore.getRelativePath(mainClassFile, sourceRoot, '/'); final String mainClass = relativePath == null ? mainClassFile.getNameWithoutExtension() : FileUtil.getNameWithoutExtension(relativePath).replace('/', '.'); final ModifiableRootModel modifiableModel = ModuleRootManager.getInstance(module).getModifiableModel(); final LibraryTable.ModifiableModel librariesModel = ProjectLibraryTable.getInstance(module.getProject()).getModifiableModel(); final FlexProjectConfigurationEditor flexEditor = FlexProjectConfigurationEditor .createEditor(module.getProject(), Collections.singletonMap(module, modifiableModel), librariesModel, null); final ModifiableFlexBuildConfiguration[] bcs = flexEditor.getConfigurations(module); final ModifiableFlexBuildConfiguration mainBC = ContainerUtil.find(bcs, bc -> bc.getOutputType() == OutputType.Application && module.getName().equals(bc.getName())); if (mainBC != null) { mainBC.setMainClass(mainClass); } flexEditor.commit(); Disposer.dispose(librariesModel); modifiableModel.dispose(); } catch (IOException ignore) {/**/} catch (ConfigurationException ignore) {/**/} } }