package com.intellij.lang.javascript.flex.build; import com.intellij.compiler.CompilerWorkspaceConfiguration; import com.intellij.compiler.options.CompileStepBeforeRun; import com.intellij.execution.configurations.RunConfiguration; import com.intellij.flex.FlexCommonBundle; import com.intellij.flex.FlexCommonUtils; import com.intellij.flex.model.bc.BuildConfigurationNature; import com.intellij.flex.model.bc.OutputType; import com.intellij.flex.model.bc.TargetPlatform; 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.*; import com.intellij.lang.javascript.flex.projectStructure.model.FlexBuildConfiguration; import com.intellij.lang.javascript.flex.projectStructure.options.BCUtils; import com.intellij.lang.javascript.flex.projectStructure.ui.*; import com.intellij.lang.javascript.flex.run.FlashRunConfiguration; import com.intellij.lang.javascript.flex.run.FlashRunnerParameters; import com.intellij.lang.javascript.flex.sdk.FlexSdkUtils; import com.intellij.lang.javascript.flex.sdk.FlexmojosSdkType; import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationType; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.compiler.*; import com.intellij.openapi.module.Module; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable; import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureProblemType; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.Trinity; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.Navigatable; import com.intellij.ui.navigation.Place; import com.intellij.util.Consumer; import com.intellij.util.PathUtil; import gnu.trove.THashMap; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.event.HyperlinkEvent; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Map; import java.util.Set; public class ValidateFlashConfigurationsPrecompileTask implements CompileTask { private static final String FLASH_COMPILER_GROUP_ID = "Flash Compiler"; private boolean myParallelCompilationSuggested = false; static Collection<Trinity<Module, FlexBuildConfiguration, FlashProjectStructureProblem>> getProblems(final CompileScope scope, final Collection<Pair<Module, FlexBuildConfiguration>> modulesAndBCsToCompile) { final Collection<Trinity<Module, FlexBuildConfiguration, FlashProjectStructureProblem>> problems = new ArrayList<>(); for (final Pair<Module, FlexBuildConfiguration> moduleAndBC : modulesAndBCsToCompile) { final Module module = moduleAndBC.first; final FlexBuildConfiguration bc = moduleAndBC.second; final Consumer<FlashProjectStructureProblem> errorConsumer = problem -> problems.add(Trinity.create(module, bc, problem)); checkConfiguration(module, bc, false, errorConsumer); final RunConfiguration runConfig = CompileStepBeforeRun.getRunConfiguration(scope); if (bc.getNature().isApp() && runConfig instanceof FlashRunConfiguration) { final FlashRunnerParameters params = ((FlashRunConfiguration)runConfig).getRunnerParameters(); if (module.getName().equals(params.getModuleName()) && bc.getName().equals(params.getBCName())) { if (bc.getNature().isDesktopPlatform()) { FlashRunnerParameters.checkAirVersionIfCustomDescriptor(module, bc.getSdk(), bc.getAirDesktopPackagingOptions(), errorConsumer, false, "does not matter"); } else if (bc.getNature().isMobilePlatform()) { switch (params.getMobileRunTarget()) { case Emulator: switch (params.getAppDescriptorForEmulator()) { case Android: FlashRunnerParameters.checkAirVersionIfCustomDescriptor(module, bc.getSdk(), bc.getAndroidPackagingOptions(), errorConsumer, false, "does not matter"); break; case IOS: FlashRunnerParameters.checkAirVersionIfCustomDescriptor(module, bc.getSdk(), bc.getIosPackagingOptions(), errorConsumer, false, "does not matter"); break; } break; case AndroidDevice: checkPackagingOptions(module, bc.getSdk(), bc.getAndroidPackagingOptions(), false, PathUtil.getParentPath(bc.getActualOutputFilePath()), errorConsumer); break; case iOSSimulator: checkPackagingOptions(module, bc.getSdk(), bc.getIosPackagingOptions(), true, PathUtil.getParentPath(bc.getActualOutputFilePath()), errorConsumer); break; case iOSDevice: checkPackagingOptions(module, bc.getSdk(), bc.getIosPackagingOptions(), false, PathUtil.getParentPath(bc.getActualOutputFilePath()), errorConsumer); break; } } } } } checkSimilarOutputFiles(modulesAndBCsToCompile, trinity -> problems.add(trinity)); return problems; } private static boolean checkSimilarOutputFiles(final Collection<Pair<Module, FlexBuildConfiguration>> modulesAndBCsToCompile, final Consumer<Trinity<Module, FlexBuildConfiguration, FlashProjectStructureProblem>> errorConsumer) { final Map<String, Pair<Module, FlexBuildConfiguration>> outputPathToModuleAndBC = new THashMap<>(); for (Pair<Module, FlexBuildConfiguration> moduleAndBC : modulesAndBCsToCompile) { final FlexBuildConfiguration bc = moduleAndBC.second; final String outputFilePath = bc.getActualOutputFilePath(); checkOutputPathUnique(outputFilePath, moduleAndBC, outputPathToModuleAndBC, errorConsumer); } return true; } private static void checkOutputPathUnique(final String outputPath, final Pair<Module, FlexBuildConfiguration> moduleAndBC, final Map<String, Pair<Module, FlexBuildConfiguration>> outputPathToModuleAndBC, final Consumer<Trinity<Module, FlexBuildConfiguration, FlashProjectStructureProblem>> errorConsumer) { final String caseAwarePath = SystemInfo.isFileSystemCaseSensitive ? outputPath : outputPath.toLowerCase(); final Pair<Module, FlexBuildConfiguration> existing = outputPathToModuleAndBC.put(caseAwarePath, moduleAndBC); if (existing != null) { final String message = FlexBundle.message("same.output.files", existing.second.getName(), existing.first.getName(), FileUtil.toSystemDependentName(outputPath)); errorConsumer.consume(Trinity.create(moduleAndBC.first, moduleAndBC.second, FlashProjectStructureProblem .createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, moduleAndBC.second.getName(), message, FlexBCConfigurable.Location.OutputFileName))); } } public static void checkConfiguration(final Module module, final FlexBuildConfiguration bc, final boolean checkPackaging, final Consumer<FlashProjectStructureProblem> errorConsumer) { final Sdk sdk = bc.getSdk(); if (sdk == null) { errorConsumer.consume(FlashProjectStructureProblem.createDependenciesProblem(ProjectStructureProblemType.Severity.ERROR, FlexBundle.message("sdk.not.set"), DependenciesConfigurable.Location.SDK)); } if (sdk != null) { String version = sdk.getVersionString(); if (FlexSdkUtils.isAirSdkWithoutFlex(sdk)) { version = version.substring(FlexCommonUtils.AIR_SDK_VERSION_PREFIX.length()); } if (StringUtil.compareVersionNumbers(version, "0") < 0 || StringUtil.compareVersionNumbers(version, "100") > 0) { errorConsumer.consume(FlashProjectStructureProblem .createDependenciesProblem(ProjectStructureProblemType.Severity.ERROR, FlexBundle.message("sdk.version.unknown", sdk.getName()), DependenciesConfigurable.Location.SDK)); } if (FlexSdkUtils.isAirSdkWithoutFlex(sdk) && !bc.isPureAs()) { errorConsumer.consume(FlashProjectStructureProblem .createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle.message("air.sdk.requires.pure.as", sdk.getName()), FlexBCConfigurable.Location.Nature)); } } InfoFromConfigFile info = InfoFromConfigFile.DEFAULT; final String additionalConfigFilePath = bc.getCompilerOptions().getAdditionalConfigFilePath(); if (!additionalConfigFilePath.isEmpty()) { final VirtualFile additionalConfigFile = LocalFileSystem.getInstance().findFileByPath(additionalConfigFilePath); if (additionalConfigFile == null || additionalConfigFile.isDirectory()) { errorConsumer.consume(FlashProjectStructureProblem .createCompilerOptionsProblem(ProjectStructureProblemType.Severity.ERROR, FlexBundle .message("additional.config.file.not.found", FileUtil.toSystemDependentName(additionalConfigFilePath)), CompilerOptionsConfigurable.Location.AdditionalConfigFile)); } if (!bc.isTempBCForCompilation()) { info = FlexCompilerConfigFileUtil.getInfoFromConfigFile(additionalConfigFilePath); } } final BuildConfigurationNature nature = bc.getNature(); if (!nature.isLib() && info.getMainClass(module) == null && !bc.isTempBCForCompilation()) { if (bc.getMainClass().isEmpty()) { errorConsumer .consume(FlashProjectStructureProblem.createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle.message("main.class.not.set"), FlexBCConfigurable.Location.MainClass)); } else { if (FlexUtils.getPathToMainClassFile(bc.getMainClass(), module).isEmpty()) { errorConsumer.consume(FlashProjectStructureProblem .createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle.message("main.class.not.found", bc.getMainClass()), FlexBCConfigurable.Location.MainClass)); } } } if (info.getOutputFileName() == null && info.getOutputFolderPath() == null) { if (FileUtil.getNameWithoutExtension(bc.getOutputFileName()).isEmpty()) { errorConsumer.consume(FlashProjectStructureProblem .createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle.message("output.file.name.not.set"), FlexBCConfigurable.Location.OutputFileName)); } else { if (!nature.isLib() && !bc.getOutputFileName().toLowerCase().endsWith(".swf")) { errorConsumer.consume( FlashProjectStructureProblem.createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle.message("output.file.wrong.extension", "swf"), FlexBCConfigurable.Location.OutputFileName)); } if (nature.isLib() && !bc.getOutputFileName().toLowerCase().endsWith(".swc")) { errorConsumer.consume( FlashProjectStructureProblem.createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle.message("output.file.wrong.extension", "swc"), FlexBCConfigurable.Location.OutputFileName)); } } if (bc.getOutputFolder().isEmpty()) { if (BCUtils.isFlexUnitBC(bc)) { errorConsumer.consume(FlashProjectStructureProblem.FlexUnitOutputFolderProblem.INSTANCE); } else { errorConsumer.consume(FlashProjectStructureProblem .createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle.message("output.folder.not.set"), FlexBCConfigurable.Location.OutputFolder)); } } else if (!FileUtil.isAbsolute(bc.getOutputFolder())) { if (BCUtils.isFlexUnitBC(bc)) { errorConsumer.consume(FlashProjectStructureProblem.FlexUnitOutputFolderProblem.INSTANCE); } else { errorConsumer.consume(FlashProjectStructureProblem.createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle .message("output.folder.not.absolute", FileUtil.toSystemDependentName(bc.getOutputFolder())), FlexBCConfigurable.Location.OutputFolder)); } } } if (nature.isWebPlatform() && nature.isApp() && bc.isUseHtmlWrapper()) { if (bc.getWrapperTemplatePath().isEmpty()) { errorConsumer .consume(FlashProjectStructureProblem.createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle.message("html.template.folder.not.set"), FlexBCConfigurable.Location.HtmlTemplatePath)); } else { final VirtualFile templateDir = LocalFileSystem.getInstance().findFileByPath(bc.getWrapperTemplatePath()); if (templateDir == null || !templateDir.isDirectory()) { errorConsumer.consume(FlashProjectStructureProblem.createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle .message("html.template.folder.not.found", FileUtil.toSystemDependentName(bc.getWrapperTemplatePath())), FlexBCConfigurable.Location.HtmlTemplatePath)); } else { final VirtualFile templateFile = templateDir.findChild(FlexCommonUtils.HTML_WRAPPER_TEMPLATE_FILE_NAME); if (templateFile == null) { errorConsumer.consume(FlashProjectStructureProblem.createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexCommonBundle .message("no.index.template.html.file", templateDir.getPresentableUrl()), FlexBCConfigurable.Location.HtmlTemplatePath)); } else { // Probably heavy calculation. Will be checked only when real html template handling is performed /* try { if (!VfsUtilCore.loadText(templateFile).contains(FlexCompilationUtils.SWF_MACRO)) { errorConsumer.consume(FlashProjectStructureProblem.createGeneralOptionProblem( FlexBundle.message("no.swf.macro.in.template", FileUtil.toSystemDependentName(templateFile.getPath())), "html.template")); } } catch (IOException e) { errorConsumer.consume(FlashProjectStructureProblem.createGeneralOptionProblem( FlexBundle.message("failed.to.load.template.file", FileUtil.toSystemDependentName(templateFile.getPath()), e.getMessage()), "html.template")); } */ final String templateFolderPath = templateDir.getPath(); boolean ok = true; for (String url : ModuleRootManager.getInstance(module).getContentRootUrls()) { if (ok) { ok = checkWrapperFolderClash(bc, templateFolderPath, VfsUtilCore.urlToPath(url), "module content root", errorConsumer); } } for (String url : ModuleRootManager.getInstance(module).getSourceRootUrls()) { if (ok) { ok = checkWrapperFolderClash(bc, templateFolderPath, VfsUtilCore.urlToPath(url), "source folder", errorConsumer); } } final String outputFolderPath = StringUtil.notNullize(info.getOutputFolderPath(), bc.getOutputFolder()); if (ok && !outputFolderPath.isEmpty()) { ok = checkWrapperFolderClash(bc, templateFolderPath, outputFolderPath, "output folder", errorConsumer); } } } } } if (BCUtils.canHaveRLMsAndRuntimeStylesheets(bc)) { for (FlexBuildConfiguration.RLMInfo rlm : bc.getRLMs()) { if (rlm.MAIN_CLASS.isEmpty()) { errorConsumer .consume(FlashProjectStructureProblem.createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle.message("rlm.main.class.not.set"), FlexBCConfigurable.Location.RLMs)); } else { if (FlexUtils.getPathToMainClassFile(rlm.MAIN_CLASS, module).isEmpty()) { errorConsumer.consume(FlashProjectStructureProblem.createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle .message("rlm.main.class.not.found", rlm.MAIN_CLASS), FlexBCConfigurable.Location.RLMs)); } } if (bc.getMainClass().equals(rlm.MAIN_CLASS)) { errorConsumer.consume(FlashProjectStructureProblem.createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle .message("rlm.main.class.equal.to.bc.main.class", rlm.MAIN_CLASS), FlexBCConfigurable.Location.RLMs)); } if (bc.getOutputFileName().equals(rlm.OUTPUT_FILE)) { errorConsumer.consume(FlashProjectStructureProblem.createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle .message("rlm.output.equal.to.bc.output", rlm.OUTPUT_FILE), FlexBCConfigurable.Location.RLMs)); } if (rlm.OUTPUT_FILE.isEmpty()) { errorConsumer.consume(FlashProjectStructureProblem .createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle.message("rlm.output.file.name.not.specified"), FlexBCConfigurable.Location.RLMs)); } else { if (!rlm.OUTPUT_FILE.toLowerCase().endsWith(".swf")) { errorConsumer.consume(FlashProjectStructureProblem.createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle.message( "rlm.output.file.must.have.swf.extension"), FlexBCConfigurable.Location.RLMs)); } } } for (String cssPath : bc.getCssFilesToCompile()) { if (!cssPath.toLowerCase().endsWith(".css")) { errorConsumer.consume(FlashProjectStructureProblem.createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle .message("not.a.css.runtime.stylesheet", FileUtil.toSystemDependentName(cssPath)), FlexBCConfigurable.Location.RuntimeStyleSheets)); } else if (LocalFileSystem.getInstance().findFileByPath(cssPath) == null) { errorConsumer.consume(FlashProjectStructureProblem.createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle .message("css.not.found", FileUtil.toSystemDependentName(cssPath)), FlexBCConfigurable.Location.RuntimeStyleSheets)); } } } if (nature.isLib()) { for (String path : bc.getCompilerOptions().getFilesToIncludeInSWC()) { if (LocalFileSystem.getInstance().findFileByPath(path) == null) { errorConsumer.consume(FlashProjectStructureProblem.createCompilerOptionsProblem(ProjectStructureProblemType.Severity.ERROR, FlexBundle .message("file.to.include.in.swc.not.found", FileUtil.toSystemDependentName(path)), CompilerOptionsConfigurable.Location.FilesToIncludeInSwc)); } } } if (checkPackaging) { checkPackagingOptions(module, bc, errorConsumer); } } private static boolean checkWrapperFolderClash(final FlexBuildConfiguration bc, final String templateFolderPath, final String otherFolderPath, final String otherFolderDescription, final Consumer<FlashProjectStructureProblem> errorConsumer) { if (FileUtil.isAncestor(templateFolderPath, otherFolderPath, false)) { errorConsumer.consume(FlashProjectStructureProblem.createGeneralOptionProblem(ProjectStructureProblemType.Severity.ERROR, bc.getName(), FlexBundle.message( "html.wrapper.folder.clash", otherFolderDescription, FileUtil.toSystemDependentName(templateFolderPath)), FlexBCConfigurable.Location.HtmlTemplatePath)); return false; } return true; } public static void checkPackagingOptions(final Module module, final FlexBuildConfiguration bc, final Consumer<FlashProjectStructureProblem> errorConsumer) { if (bc.getOutputType() != OutputType.Application) return; if (bc.getTargetPlatform() == TargetPlatform.Desktop) { checkPackagingOptions(module, bc.getSdk(), bc.getAirDesktopPackagingOptions(), false, PathUtil.getParentPath(bc.getActualOutputFilePath()), errorConsumer); } else if (bc.getTargetPlatform() == TargetPlatform.Mobile) { if (bc.getAndroidPackagingOptions().isEnabled()) { checkPackagingOptions(module, bc.getSdk(), bc.getAndroidPackagingOptions(), false, PathUtil.getParentPath(bc.getActualOutputFilePath()), errorConsumer); } if (bc.getIosPackagingOptions().isEnabled()) { checkPackagingOptions(module, bc.getSdk(), bc.getIosPackagingOptions(), false, PathUtil.getParentPath(bc.getActualOutputFilePath()), errorConsumer); } } } private static void checkPackagingOptions(final Module module, final @Nullable Sdk sdk, final AirPackagingOptions packagingOptions, final boolean isForIosSimulator, final String outputFolderPath, final Consumer<FlashProjectStructureProblem> errorConsumer) { final String device = packagingOptions instanceof AndroidPackagingOptions ? "Android" : packagingOptions instanceof IosPackagingOptions ? "iOS" : ""; if (!packagingOptions.isUseGeneratedDescriptor()) { if (packagingOptions.getCustomDescriptorPath().isEmpty()) { errorConsumer.consume(FlashProjectStructureProblem .createPackagingOptionsProblem(ProjectStructureProblemType.Severity.ERROR, packagingOptions, FlexBundle.message("custom.descriptor.not.set", device), AirPackagingConfigurableBase.Location.CustomDescriptor)); } else { final VirtualFile descriptorFile = LocalFileSystem.getInstance().findFileByPath(packagingOptions.getCustomDescriptorPath()); if (descriptorFile == null || descriptorFile.isDirectory()) { errorConsumer.consume(FlashProjectStructureProblem .createPackagingOptionsProblem(ProjectStructureProblemType.Severity.ERROR, packagingOptions, FlexBundle .message("custom.descriptor.not.found", device, FileUtil.toSystemDependentName(packagingOptions.getCustomDescriptorPath())), AirPackagingConfigurableBase.Location.CustomDescriptor)); } else if (sdk != null && sdk.getSdkType() != FlexmojosSdkType.getInstance()) { FlashRunnerParameters.checkAirVersionIfCustomDescriptor(module, sdk, packagingOptions, errorConsumer, false, "does not matter"); } } } if (packagingOptions.getPackageFileName().isEmpty()) { errorConsumer.consume(FlashProjectStructureProblem.createPackagingOptionsProblem(ProjectStructureProblemType.Severity.ERROR, packagingOptions, FlexBundle .message("package.file.name.not.set", device), AirPackagingConfigurableBase.Location.PackageFileName)); } for (AirPackagingOptions.FilePathAndPathInPackage entry : packagingOptions.getFilesToPackage()) { final String fullPath = entry.FILE_PATH; String relPathInPackage = entry.PATH_IN_PACKAGE; relPathInPackage = StringUtil.trimStart(relPathInPackage, "/"); if (fullPath.isEmpty()) { errorConsumer.consume(FlashProjectStructureProblem .createPackagingOptionsProblem(ProjectStructureProblemType.Severity.ERROR, packagingOptions, FlexBundle .message("packaging.options.empty.file.name", device), AirPackagingConfigurableBase.Location.FilesToPackage)); } else { final VirtualFile file = LocalFileSystem.getInstance().findFileByPath(fullPath); if (file == null) { errorConsumer.consume(FlashProjectStructureProblem .createPackagingOptionsProblem(ProjectStructureProblemType.Severity.ERROR, packagingOptions, FlexBundle .message("packaging.options.file.not.found", device, FileUtil.toSystemDependentName(fullPath)), AirPackagingConfigurableBase.Location.FilesToPackage)); } if (relPathInPackage.isEmpty()) { errorConsumer.consume(FlashProjectStructureProblem .createPackagingOptionsProblem(ProjectStructureProblemType.Severity.ERROR, packagingOptions, FlexBundle .message("packaging.options.empty.relative.path", device), AirPackagingConfigurableBase.Location.FilesToPackage)); } if (file != null && file.isDirectory()) { if (FileUtil.isAncestor(file.getPath(), outputFolderPath, false)) { errorConsumer.consume(FlashProjectStructureProblem .createPackagingOptionsProblem(ProjectStructureProblemType.Severity.ERROR, packagingOptions, FlexBundle .message("folder.to.package.includes.output", device, file.getPresentableUrl()), AirPackagingConfigurableBase.Location.FilesToPackage)); } else if (!relPathInPackage.isEmpty() && !".".equals(relPathInPackage) && !fullPath.endsWith("/" + relPathInPackage)) { errorConsumer.consume( FlashProjectStructureProblem .createPackagingOptionsProblem(ProjectStructureProblemType.Severity.ERROR, packagingOptions, FlexBundle .message("packaging.options.relative.path.not.matches", device, FileUtil.toSystemDependentName(relPathInPackage)), AirPackagingConfigurableBase.Location.FilesToPackage)); } } } } if (packagingOptions instanceof IosPackagingOptions) { final String path = packagingOptions.getSigningOptions().getIOSSdkPath(); if (!path.isEmpty() && !new File(path).isDirectory()) { errorConsumer.consume(FlashProjectStructureProblem.createPackagingOptionsProblem(ProjectStructureProblemType.Severity.ERROR, packagingOptions, FlexBundle .message("packaging.options.bad.ios.sdk.path", device, FileUtil.toSystemDependentName(path)), AirPackagingConfigurableBase.Location.IosSdkPath)); } } final AirSigningOptions signingOptions = packagingOptions.getSigningOptions(); if (packagingOptions instanceof IosPackagingOptions && !isForIosSimulator) { final String provisioningProfilePath = signingOptions.getProvisioningProfilePath(); if (provisioningProfilePath.isEmpty()) { errorConsumer.consume(FlashProjectStructureProblem.createPackagingOptionsProblem(ProjectStructureProblemType.Severity.ERROR, packagingOptions, FlexBundle .message("ios.provisioning.profile.not.set"), AirPackagingConfigurableBase.Location.ProvisioningProfile)); } else { final VirtualFile provisioningProfile = LocalFileSystem.getInstance().findFileByPath(provisioningProfilePath); if (provisioningProfile == null || provisioningProfile.isDirectory()) { errorConsumer.consume(FlashProjectStructureProblem .createPackagingOptionsProblem(ProjectStructureProblemType.Severity.ERROR, packagingOptions, FlexBundle .message("ios.provisioning.profile.not.found", FileUtil.toSystemDependentName(provisioningProfilePath)), AirPackagingConfigurableBase.Location.ProvisioningProfile)); } } } final boolean tempCertificate = packagingOptions instanceof IosPackagingOptions ? isForIosSimulator : signingOptions.isUseTempCertificate(); if (!tempCertificate) { final String keystorePath = signingOptions.getKeystorePath(); if (keystorePath.isEmpty()) { errorConsumer.consume(FlashProjectStructureProblem.createPackagingOptionsProblem(ProjectStructureProblemType.Severity.ERROR, packagingOptions, FlexBundle.message("keystore.not.set", device), AirPackagingConfigurableBase.Location.Keystore)); } else { final VirtualFile keystore = LocalFileSystem.getInstance().findFileByPath(keystorePath); if (keystore == null || keystore.isDirectory()) { errorConsumer.consume(FlashProjectStructureProblem .createPackagingOptionsProblem(ProjectStructureProblemType.Severity.ERROR, packagingOptions, FlexBundle .message("keystore.not.found", device, FileUtil.toSystemDependentName(keystorePath)), AirPackagingConfigurableBase.Location.Keystore)); } } } } public boolean execute(final CompileContext context) { FlexCompilerHandler.getInstance(context.getProject()).getBuiltInFlexCompilerHandler().stopCompilerProcess(); return validateConfiguration(context); } private boolean validateConfiguration(final CompileContext context) { try { final Collection<Pair<Module, FlexBuildConfiguration>> modulesAndBCsToCompile = FlexBuildTargetScopeProvider.getModulesAndBCsToCompile(context.getCompileScope()); suggestParallelCompilationIfNeeded(context.getProject(), modulesAndBCsToCompile); final Collection<Trinity<Module, FlexBuildConfiguration, FlashProjectStructureProblem>> problems = ReadAction.compute(() -> getProblems(context.getCompileScope(), modulesAndBCsToCompile)); if (!problems.isEmpty()) { boolean hasErrors = false; for (Trinity<Module, FlexBuildConfiguration, FlashProjectStructureProblem> problem : problems) { if (problem.getThird().severity == ProjectStructureProblemType.Severity.ERROR) { hasErrors = true; break; } } if (hasErrors) { // todo remove this senseless error message when 'show first error in editor' functionality respect canNavigateToSource() context.addMessage(CompilerMessageCategory.ERROR, "Flash build configurations contain errors. " + "Double-click error message below to navigate to the corresponding field in the Project Structure dialog", null, -1, -1); } reportProblems(context, problems); return !hasErrors; } } catch (ConfigurationException e) { context.addMessage(CompilerMessageCategory.ERROR, FlexBundle.message("project.setup.problem", e.getMessage()), null, -1, -1); return false; } return true; } private void suggestParallelCompilationIfNeeded(final Project project, final Collection<Pair<Module, FlexBuildConfiguration>> modulesAndBCsToCompile) { if (myParallelCompilationSuggested) return; if (CompilerWorkspaceConfiguration.getInstance(project).PARALLEL_COMPILATION) return; if (modulesAndBCsToCompile.size() < 2) return; if (!independentBCsExist(modulesAndBCsToCompile)) return; final NotificationListener listener = new NotificationListener() { public void hyperlinkUpdate(@NotNull final Notification notification, @NotNull final HyperlinkEvent event) { notification.expire(); if ("enable".equals(event.getDescription())) { CompilerWorkspaceConfiguration.getInstance(project).PARALLEL_COMPILATION = true; final NotificationListener listener1 = new NotificationListener() { public void hyperlinkUpdate(@NotNull final Notification notification, @NotNull final HyperlinkEvent event) { notification.expire(); ShowSettingsUtil.getInstance().showSettingsDialog(project, CompilerBundle.message("compiler.configurable.display.name")); } }; new Notification(FLASH_COMPILER_GROUP_ID, FlexBundle.message("parallel.compilation.enabled"), FlexBundle.message("see.settings.compiler"), NotificationType.INFORMATION, listener1).notify(project); } else if ("open".equals(event.getDescription())) { ShowSettingsUtil.getInstance().showSettingsDialog(project, CompilerBundle.message("compiler.configurable.display.name")); } } }; new Notification(FLASH_COMPILER_GROUP_ID, FlexBundle.message("parallel.compilation.hint.title"), FlexBundle.message("parallel.compilation.hint"), NotificationType.INFORMATION, listener).notify(project); myParallelCompilationSuggested = true; } private static boolean independentBCsExist(final Collection<Pair<Module, FlexBuildConfiguration>> modulesAndBCsToCompile) { final Set<FlexBuildConfiguration> bcs = new THashSet<>(); for (Pair<Module, FlexBuildConfiguration> moduleAndBC : modulesAndBCsToCompile) { bcs.add(moduleAndBC.second); } int independentBCsCount = 0; OUTER: for (FlexBuildConfiguration bc : bcs) { for (final DependencyEntry entry : bc.getDependencies().getEntries()) { if (entry instanceof BuildConfigurationEntry) { final FlexBuildConfiguration dependencyBC = ((BuildConfigurationEntry)entry).findBuildConfiguration(); if (dependencyBC != null && bcs.contains(dependencyBC)) { continue OUTER; } } } independentBCsCount++; if (independentBCsCount > 1) { return true; } } return false; } private static void reportProblems(final CompileContext context, final Collection<Trinity<Module, FlexBuildConfiguration, FlashProjectStructureProblem>> problems) { for (Trinity<Module, FlexBuildConfiguration, FlashProjectStructureProblem> trinity : problems) { final Module module = trinity.getFirst(); final FlexBuildConfiguration bc = trinity.getSecond(); final FlashProjectStructureProblem problem = trinity.getThird(); final String message = problem instanceof FlashProjectStructureProblem.FlexUnitOutputFolderProblem ? problem.errorMessage : FlexBundle.message("bc.0.module.1.problem.2", bc.getName(), module.getName(), problem.errorMessage); final CompilerMessageCategory severity = problem.severity == ProjectStructureProblemType.Severity.ERROR ? CompilerMessageCategory.ERROR : CompilerMessageCategory.WARNING; context.addMessage(severity, message, null, -1, -1, new BCProblemNavigatable(module, bc.getName(), problem)); } } private static class BCProblemNavigatable implements Navigatable { @NotNull private final Module myModule; @NotNull private final String myBCNme; @NotNull private final FlashProjectStructureProblem myProblem; private BCProblemNavigatable(final @NotNull Module module, final @NotNull String bcName, final @NotNull FlashProjectStructureProblem problem) { myModule = module; myBCNme = bcName; myProblem = problem; } public boolean canNavigateToSource() { return false; } public boolean canNavigate() { return !myModule.isDisposed() && FlexBuildConfigurationManager.getInstance(myModule).findConfigurationByName(myBCNme) != null; } public void navigate(final boolean requestFocus) { final ProjectStructureConfigurable configurable = ProjectStructureConfigurable.getInstance(myModule.getProject()); ShowSettingsUtil.getInstance().editConfigurable(myModule.getProject(), configurable, () -> { final Place place; if (myProblem instanceof FlashProjectStructureProblem.FlexUnitOutputFolderProblem) { place = new Place() .putPath(ProjectStructureConfigurable.CATEGORY, configurable.getProjectConfig()); } else { place = FlexBuildConfigurationsExtension.getInstance().getConfigurator().getPlaceFor(myModule, myBCNme) .putPath(CompositeConfigurable.TAB_NAME, myProblem.tabName) .putPath(FlexBCConfigurable.LOCATION_ON_TAB, myProblem.locationOnTab); } configurable.navigateTo(place, true); }); } } }