package _VisualDVM.Passes.All; import Common.CommonConstants; import Common.Passes.Pass; import Common.Passes.PassException; import Common.Utils.Utils_; import Common.Visual.UI; import _VisualDVM.Constants; import _VisualDVM.Global; import _VisualDVM.GlobalData.Compiler.Compiler; import _VisualDVM.GlobalData.Compiler.CompilerType; import _VisualDVM.GlobalData.Makefile.Makefile; import _VisualDVM.GlobalData.Module.Module; import _VisualDVM.GlobalData.Settings.SettingName; import _VisualDVM.Passes.PassCode; import _VisualDVM.ProjectData.Files.DBProjectFile; import _VisualDVM.ProjectData.LanguageName; import _VisualDVM.ProjectData.Project.db_project_info; import _VisualDVM.Utils; import org.apache.commons.io.FileUtils; import java.io.BufferedReader; import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.file.Paths; import java.util.LinkedHashMap; import java.util.Vector; public class Precompilation extends Pass { protected static String name_to_kill = ""; protected static boolean killed = false; protected String makepath; //----------------------->>> protected File workspace; //рабочая папка. protected Compiler fortranCompiler; protected Makefile makefile; protected File makefileFile; protected int performanceTime; protected int exitCode; //----------------------->>> protected Process process = null; protected String output = ""; protected Vector outputLines = new Vector<>(); protected static void unpackPrecompilationMessages(db_project_info target, String text) throws Exception { boolean messageStarted = false; String messageFile = ""; int messageLine = CommonConstants.Nan; int messageType = CommonConstants.Nan; Vector messageText = new Vector<>(); String[] nw = text.split("\n"); for (String S : nw) { if (messageStarted) { String s = S.toLowerCase(); if (s.startsWith("warning:")) { messageText.add(S.substring(9)); messageType = 0; } else if (s.startsWith("error:")) { messageText.add(S.substring(7)); messageType = 1; } else if (s.startsWith("note:")) { messageText.add(S.substring(6)); messageType = 2; } if (messageType != CommonConstants.Nan) { target.db.files.Data.get(messageFile).CreateAndAddNewMessage(messageType, String.join("\n", messageText), messageLine, Constants.compiler_group); messageStarted = false; } else { if (!S.isEmpty()) messageText.add(S); } } else { //анализ начала строки. for (String name : target.db.files.Data.keySet()) { if (S.startsWith(Utils_.toU(name))) { String[] data = S.split(":"); if (data.length > 1) { messageFile = name; messageLine = Integer.parseInt(data[1]); messageType = CommonConstants.Nan; messageText = new Vector<>(); messageStarted = true; } break; } } } } } @Override protected boolean needsAnimation() { return true; } @Override protected boolean canStart(Object... args) throws Exception { target = Global.mainModule.getProject(); if (Utils_.isWindows()) { //-- makepath = Global.properties.LocalMakePathWindows; if (makepath.isEmpty()) { Log.Writeln_("Не задан путь к make.exe.\n " + "Укажите его в меню глобальных настроек." + "\nПредварительная компиляция будет отключена."); Global.mainModule.getPass(PassCode.UpdateSetting).Do(SettingName.Precompilation, "0"); return false; } if (!(new File(makepath).exists())) { Log.Writeln_("Файла по пути к make.exe не существует.\n" + "Проверьте правильность его указания в глобальных настройках." + "\nПредварительная компиляция будет отключена."); Global.mainModule.getPass(PassCode.UpdateSetting).Do(SettingName.Precompilation, "0"); return false; } //-- } process = null; if (!target.languageName.equals(LanguageName.fortran)) { Log.Writeln_("Предварительная компиляция поддерживается только для FORTRAN."); return false; } return true; } protected String getLinkFlags() { return ""; } protected String getFortranFlags() { return ""; } protected void prepareForParse() throws Exception { target.CleanAnalyses(); } @Override protected void performPreparation() throws Exception { target.CleanInterruptFile(); //- Global.mainModule.getPass(PassCode.Save).Do(); //----> prepareForParse(); //----> //- performanceTime = 0; exitCode = CommonConstants.Nan; //------------------ workspace = Paths.get(Global.TempDirectory.getAbsolutePath(), Utils_.getDateName("precompilation")).toFile(); FileUtils.forceMkdir(workspace); //------------------------------>> fortranCompiler = new Compiler(); fortranCompiler.call_command = "gfortran"; fortranCompiler.type = CompilerType.gnu; makefileFile = Paths.get(workspace.getAbsolutePath(), "Makefile").toFile(); makefile = new Makefile() { @Override public Compiler getCompiler() { return fortranCompiler; } }; makefile.flags = getLinkFlags(); LinkedHashMap modules = new LinkedHashMap<>(); Module fortranModule = new Module(LanguageName.fortran, null) { @Override public boolean isSelected() { return true; //всегда выбран. нужно для генерации. } @Override public Compiler getCompiler() { return fortranCompiler; } }; fortranModule.flags = getFortranFlags(); modules.put(LanguageName.fortran, fortranModule); //------------------------------>> target.Clone(workspace, false); //------------------------------>> FileUtils.write(makefileFile, genMakefileText(modules)); //--- } protected String genMakefileText(LinkedHashMap modules) throws Exception { return makefile.GenerateForPrecompilation(target, modules); } @Override protected void showPreparation() throws Exception { Global.mainModule.getUI().getMainWindow().getProjectWindow().ShowNoAnalyses(); if (Global.mainModule.HasFile()) { Global.mainModule.getFile().form.ShowNoMessages(); Global.mainModule.getFile().form.ShowNoAnalyses(); } } @Override protected void body() throws Exception { if (Utils_.isWindows()) { name_to_kill = "make.exe"; StartProcess(Utils_.DQuotes(Global.properties.LocalMakePathWindows) + " -j " + Global.properties.Kernels, target.compilation_maxtime); } else StartProcess("make -j " + Global.properties.Kernels, target.compilation_maxtime); } protected void StartProcess(String command, int TA) throws Exception { killed = false; output = ""; process = Utils.startScript(workspace, workspace, Utils_.getDateName("start_task_script"), command, null); outputLines.clear(); InputStream stdout = process.getInputStream(); InputStreamReader isrStdout = new InputStreamReader(stdout); BufferedReader brStdout = new BufferedReader(isrStdout); String line; //- while ((line = brStdout.readLine()) != null) { ShowMessage2(line); outputLines.add(line); } output = String.join("\n", outputLines); if (killed) { throw new PassException("Процесс был убит"); } } @Override protected boolean validate() { return outputLines.stream().noneMatch(line -> line.toLowerCase().startsWith("error")); } @Override protected void performDone() throws Exception { } @Override protected void performFinish() throws Exception { for (DBProjectFile file : target.db.files.Data.values()) { if (!file.last_assembly_name.isEmpty()) { String replacement = file.last_assembly_name + " " + Utils_.RBrackets(file.name); output = output.replace(file.last_assembly_name, replacement); } } target.updateCompilationOut(output); //---------------------------------- target.db.BeginTransaction(); unpackPrecompilationMessages(target, output); for (DBProjectFile file : target.db.files.Data.values()) file.father.db.Update(file); target.db.Commit(); //---------------------------------- } @Override protected void showFinish() throws Exception { Global.mainModule.getUI().getMainWindow().getProjectWindow().RefreshProjectTreeAndMessages(); if (Global.mainModule.HasFile()) { Global.mainModule.getFile().form.ShowCompilationOutput(); Global.mainModule.getFile().form.FocusCompilationOut(); } } @Override public void Interrupt() throws Exception { if (Utils_.isWindows()) { if (!name_to_kill.isEmpty()) { killed = true; Process killer = Runtime.getRuntime().exec("taskkill /FI \"IMAGENAME eq " + name_to_kill + "\" /F /T"); killer.waitFor(); } } else { UI.Info("Прерывание процессов под Linux не реализовано"); } } }