Exemplo n.º 1
0
        /// <summary>
        /// Generate an html report for the current deployment
        /// </summary>
        public string FormatDeploymentResults()
        {
            StringBuilder currentReport = new StringBuilder();

            currentReport.Append(@"<h2>Results :</h2>");
            currentReport.Append(@"<div class='IndentDiv'>");

            if (HasBeenCancelled)
            {
                // the process has been canceled
                currentReport.Append(@"<div><img style='padding-right: 20px;' src='Warning30x30' height='15px'>The deployment has been canceled by the user</div>");
            }
            else if (CompilationHasFailed)
            {
                // provide info on the possible error!
                currentReport.Append(@"<div><img style='padding-right: 20px;' src='Error30x30' height='15px'>At least one process has ended in error, the compilation has been canceled</div>");

                if (_proCompilation.CompilationFailedOnMaxUser)
                {
                    currentReport.Append(@"<div><img style='padding-right: 20px;' src='Help30x30' height='15px'>One or more processes started for this compilation tried to connect to the database and failed because the maximum number of connection has been reached (error 748). To correct this problem, you can either :<br><li>reduce the number of processes to use for each core of your computer</li><li>or increase the maximum of connections for your database (-n parameter in the PROSERVE command)</li></div>");
                }
            }
            else if (_deploymentErrorOccured)
            {
                currentReport.Append(@"<div><img style='padding-right: 20px;' src='Error30x30' height='15px'>Le déploiement a échoué</div>");
            }

            var           listLinesCompilation = new List <Tuple <int, string> >();
            StringBuilder line = new StringBuilder();

            var totalDeployedFiles   = 0;
            var nbDeploymentError    = 0;
            var nbCompilationError   = 0;
            var nbCompilationWarning = 0;

            // compilation errors
            foreach (var fileInError in _proCompilation.ListFilesToCompile.Where(file => file.Errors != null))
            {
                bool hasError   = fileInError.Errors.Exists(error => error.Level >= ErrorLevel.Error);
                bool hasWarning = fileInError.Errors.Exists(error => error.Level < ErrorLevel.Error);

                if (hasError || hasWarning)
                {
                    // only add compilation errors
                    line.Clear();
                    line.Append("<div %ALTERNATE%style=\"background-repeat: no-repeat; background-image: url('" + (hasError ? "Error30x30" : "Warning30x30") + "'); padding-left: 35px; padding-top: 6px; padding-bottom: 6px;\">");
                    line.Append(ProExecutionCompile.FormatCompilationResultForSingleFile(fileInError.SourcePath, fileInError, null));
                    line.Append("</div>");
                    listLinesCompilation.Add(new Tuple <int, string>(hasError ? 3 : 2, line.ToString()));
                }

                if (hasError)
                {
                    nbCompilationError++;
                }
                else if (hasWarning)
                {
                    nbCompilationWarning++;
                }
            }

            // for each deploy step
            var listLinesByStep = new Dictionary <int, List <Tuple <int, string> > > {
                { 0, new List <Tuple <int, string> >() }
            };

            foreach (var kpv in _filesToDeployPerStep)
            {
                // group either by directory name or by pack name
                var groupDirectory = kpv.Value.GroupBy(deploy => deploy.GroupKey).Select(deploys => deploys.ToList()).ToList();

                foreach (var group in groupDirectory.OrderByDescending(list => list.First().DeployType).ThenBy(list => list.First().GroupKey))
                {
                    var deployFailed = group.Exists(deploy => !deploy.IsOk);
                    var first        = group.First();

                    line.Clear();
                    line.Append("<div %ALTERNATE%style=\"background-repeat: no-repeat; background-image: url('" + (deployFailed ? "Error30x30" : "Ok30x30") + "'); padding-left: 35px; padding-top: 6px; padding-bottom: 6px;\">");
                    line.Append(first.ToStringGroupHeader());
                    foreach (var fileToDeploy in group.OrderBy(deploy => deploy.To))
                    {
                        line.Append(fileToDeploy.ToStringDescription(kpv.Key <= 1 ? _proEnv.BaseLocalPath : _proEnv.BaseCompilationPath));
                    }
                    line.Append("</div>");

                    if (!listLinesByStep.ContainsKey(kpv.Key))
                    {
                        listLinesByStep.Add(kpv.Key, new List <Tuple <int, string> >());
                    }

                    listLinesByStep[kpv.Key].Add(new Tuple <int, string>(deployFailed ? 3 : 1, line.ToString()));

                    if (deployFailed)
                    {
                        nbDeploymentError += group.Count(deploy => !deploy.IsOk);
                    }
                    else
                    {
                        totalDeployedFiles += group.Count;
                    }
                }
            }

            // compilation
            currentReport.Append(@"<div style='padding-top: 7px; padding-bottom: 7px;'>Compiling <b>" + _proCompilation.NbFilesToCompile + "</b> files : <b>" + Utils.GetNbFilesPerType(_proCompilation.GetListOfFileToCompile.Select(compile => compile.SourcePath).ToList()).Aggregate("", (current, kpv) => current + (@"<img style='padding-right: 5px;' src='" + Utils.GetExtensionImage(kpv.Key.ToString(), true) + "' height='15px'><span style='padding-right: 12px;'>x" + kpv.Value + "</span>")) + "</b></div>");

            // compilation time
            currentReport.Append(@"<div><img style='padding-right: 20px;' src='Clock' height='15px'>Total elapsed time for the compilation : <b>" + _proCompilation.TotalCompilationTime + @"</b></div>");

            if (nbCompilationError > 0)
            {
                currentReport.Append("<div><img style='padding-right: 20px;' src='Error30x30' height='15px'>" + nbCompilationError + " files with compilation error(s)</div>");
            }
            if (nbCompilationWarning > 0)
            {
                currentReport.Append("<div><img style='padding-right: 20px;' src='Warning30x30' height='15px'>" + nbCompilationWarning + " files with compilation warning(s)</div>");
            }
            if (_proCompilation.NumberOfFilesTreated - nbCompilationError - nbCompilationWarning > 0)
            {
                currentReport.Append("<div><img style='padding-right: 20px;' src='Ok30x30' height='15px'>" + (_proCompilation.NumberOfFilesTreated - nbCompilationError - nbCompilationWarning) + " files compiled correctly</div>");
            }

            // deploy
            currentReport.Append(@"<div style='padding-top: 7px; padding-bottom: 7px;'>Deploying <b>" + totalDeployedFiles + "</b> files : <b>" + Utils.GetNbFilesPerType(_filesToDeployPerStep.SelectMany(pair => pair.Value).Select(deploy => deploy.To).ToList()).Aggregate("", (current, kpv) => current + (@"<img style='padding-right: 5px;' src='" + Utils.GetExtensionImage(kpv.Key.ToString(), true) + "' height='15px'><span style='padding-right: 12px;'>x" + kpv.Value + "</span>")) + "</b></div>");

            // deployment time
            currentReport.Append(@"<div><img style='padding-right: 20px;' src='Clock' height='15px'>Total elapsed time for the deployment : <b>" + TotalDeploymentTime + @"</b></div>");

            if (nbDeploymentError > 0)
            {
                currentReport.Append("<div><img style='padding-right: 20px;' src='Error30x30' height='15px'>" + nbDeploymentError + " files not deployed</div>");
            }
            if (totalDeployedFiles - nbDeploymentError > 0)
            {
                currentReport.Append("<div><img style='padding-right: 20px;' src='Ok30x30' height='15px'>" + (totalDeployedFiles - nbDeploymentError) + " files deployed correctly</div>");
            }

            // compilation
            if (listLinesCompilation.Count > 0)
            {
                currentReport.Append("<h3>Compilation details :</h3>");
                var boolAlternate = false;
                foreach (var listLine in listLinesCompilation.OrderByDescending(tuple => tuple.Item1))
                {
                    currentReport.Append(listLine.Item2.Replace("%ALTERNATE%", boolAlternate ? "class='AlternatBackColor' " : "class='NormalBackColor' "));
                    boolAlternate = !boolAlternate;
                }
            }

            // deployment steps
            foreach (var listLinesKpv in listLinesByStep.Where(pair => pair.Value != null && pair.Value.Count > 0))
            {
                currentReport.Append("<h3>Deployment step " + listLinesKpv.Key + " details :</h3>");
                var boolAlternate2 = false;
                foreach (var listLine in listLinesKpv.Value.OrderByDescending(tuple => tuple.Item1))
                {
                    currentReport.Append(listLine.Item2.Replace("%ALTERNATE%", boolAlternate2 ? "class='AlternatBackColor' " : "class='NormalBackColor' "));
                    boolAlternate2 = !boolAlternate2;
                }
            }

            currentReport.Append(@"</div>");

            return(currentReport.ToString());
        }
Exemplo n.º 2
0
        /// <summary>
        /// Compiles the list of files given
        /// </summary>
        public bool CompileFiles(List <FileToCompile> filesToCompile)
        {
            if (filesToCompile == null || filesToCompile.Count == 0)
            {
                EndOfCompilation();
                return(true);
            }

            // init
            StartingTime     = DateTime.Now;
            NbFilesToCompile = filesToCompile.Count;

            // now we do a list of those files, sorted from the biggest (in size) to the smallest file
            filesToCompile.Sort((file1, file2) => file2.Size.CompareTo(file1.Size));

            // we want to dispatch all those files in a fair way among the Prowin processes we will create...
            var numberOfProcesses = MonoProcess ? 1 : Math.Max(NumberOfProcessesPerCore, 1) * Environment.ProcessorCount;

            // ensure that each process will at least take in 10 files, starting a new process for 1 file to compile isn't efficient!
            numberOfProcesses = Math.Min(numberOfProcesses, NbFilesToCompile / 10);

            var fileLists      = new List <List <FileToCompile> >();
            var currentProcess = 0;

            foreach (var file in filesToCompile)
            {
                // create a new process when needed
                if (currentProcess >= fileLists.Count)
                {
                    fileLists.Add(new List <FileToCompile>());
                }

                // assign the file to the current process
                fileLists[currentProcess].Add(file);

                // we will assign the next file to the next process...
                currentProcess++;
                if (currentProcess == numberOfProcesses)
                {
                    currentProcess = 0;
                }
            }

            _processesRunning = fileLists.Count;

            // init the compilation on each process
            _processes.Clear();
            for (int i = 0; i < fileLists.Count; i++)
            {
                var exec = new ProExecutionCompile(ProEnv)
                {
                    Files = fileLists[i],
                    NeedDatabaseConnection = true,
                    NoBatch        = true,
                    IsTestMode     = IsTestMode,
                    IsAnalysisMode = IsAnalysisMode
                };
                exec.OnExecutionOk     += OnExecutionOk;
                exec.OnExecutionFailed += OnExecutionFailed;
                exec.OnCompilationOk   += OnExecCompilationOk;

                if (RFilesOnly)
                {
                    exec.CompileWithDebugList = false;
                    exec.CompileWithXref      = false;
                    exec.CompileWithListing   = false;
                }

                _processes.Add(exec);
            }

            // launch the compile process
            return(_processes.All(exec => exec.Start()));
        }
Exemplo n.º 3
0
        /// <summary>
        /// Creates a list of files to deploy after a compilation,
        /// for each Origin file will correspond one (or more if it's a .cls) .r file,
        /// and one .lst if the option has been checked
        /// </summary>
        public static List <FileToDeploy> GetFilesToDeployAfterCompilation(ProExecutionCompile execution)
        {
            var outputList = new List <FileToDeploy>();

            try {
                var filesCompiled = execution.Files.ToList();

                // Handle the case of .cls files, for which several .r code are compiled
                foreach (var clsFile in execution.Files.Where(file => file.SourcePath.EndsWith(ProExecutionHandleCompilation.ExtCls, StringComparison.CurrentCultureIgnoreCase)))
                {
                    // if the file we compiled inherits from another class or if another class inherits of our file,
                    // there is more than 1 *.r file generated. Moreover, they are generated in their package folders

                    // for each *.r file in the compilation output directory
                    foreach (var rCodeFilePath in Directory.EnumerateFiles(clsFile.CompilationOutputDir, "*" + ProExecutionHandleCompilation.ExtR, SearchOption.AllDirectories))
                    {
                        try {
                            // find the path of the source
                            var relativePath = rCodeFilePath.Replace(clsFile.CompilationOutputDir, "").TrimStart('\\');

                            // if this is actually the .cls file we want to compile, the .r file isn't necessary directly in the compilation dir like we expect,
                            // it can be in folders corresponding to the package of the class
                            if (Path.GetFileNameWithoutExtension(clsFile.SourcePath ?? "").Equals(Path.GetFileNameWithoutExtension(relativePath)))
                            {
                                // correct .r path
                                clsFile.CompOutputR = rCodeFilePath;
                                continue;
                            }

                            // otherwise, try to get the source .cls for this .r
                            var sourcePath = execution.ProEnv.FindFirstFileInPropath(Path.ChangeExtension(relativePath, ProExecutionHandleCompilation.ExtCls));

                            // if the source isn't already in the files that needed to be compiled, we add it
                            if (!string.IsNullOrEmpty(sourcePath) && !filesCompiled.Exists(compiledFile => compiledFile.SourcePath.Equals(sourcePath)))
                            {
                                filesCompiled.Add(new FileToCompile(sourcePath)
                                {
                                    CompilationOutputDir = clsFile.CompilationOutputDir,
                                    CompiledSourcePath   = sourcePath,
                                    CompOutputR          = rCodeFilePath
                                });
                            }
                        } catch (Exception e) {
                            ErrorHandler.LogError(e);
                        }
                    }
                }

                // for each .r
                foreach (var compiledFile in filesCompiled)
                {
                    if (string.IsNullOrEmpty(compiledFile.CompOutputR))
                    {
                        continue;
                    }
                    foreach (var deployNeeded in execution.ProEnv.Deployer.GetTargetsNeeded(compiledFile.SourcePath, 0, DeployTransferRuleTarget.File))
                    {
                        string targetRPath;
                        if (execution.ProEnv.CompileLocally)
                        {
                            targetRPath = Path.Combine(deployNeeded.TargetBasePath, Path.GetFileName(compiledFile.CompOutputR));
                        }
                        else
                        {
                            targetRPath = Path.Combine(deployNeeded.TargetBasePath, compiledFile.CompOutputR.Replace(compiledFile.CompilationOutputDir, "").TrimStart('\\'));
                        }

                        // add .r and .lst (if needed) to the list of files to deploy
                        outputList.Add(deployNeeded.Set(compiledFile.CompOutputR, targetRPath));

                        // listing
                        if (execution.CompileWithListing && !string.IsNullOrEmpty(compiledFile.CompOutputLis))
                        {
                            outputList.Add(deployNeeded.Copy(compiledFile.CompOutputLis, Path.ChangeExtension(targetRPath, ProExecutionHandleCompilation.ExtLis)));
                        }

                        // xref
                        if (execution.CompileWithXref && !string.IsNullOrEmpty(compiledFile.CompOutputXrf))
                        {
                            outputList.Add(deployNeeded.Copy(compiledFile.CompOutputXrf, Path.ChangeExtension(targetRPath, execution.UseXmlXref ? ProExecutionHandleCompilation.ExtXrfXml : ProExecutionHandleCompilation.ExtXrf)));
                        }

                        // debug-list
                        if (execution.CompileWithDebugList && !string.IsNullOrEmpty(compiledFile.CompOutputDbg))
                        {
                            outputList.Add(deployNeeded.Copy(compiledFile.CompOutputDbg, Path.ChangeExtension(targetRPath, ProExecutionHandleCompilation.ExtDbg)));
                        }
                    }
                }
            } catch (Exception e) {
                ErrorHandler.ShowErrors(e, "Failed to find .r codes to deploy");
            }
            return(outputList);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Generate an html report for the current deployment
        /// </summary>
        public string FormatDeploymentReport()
        {
            StringBuilder currentReport = new StringBuilder();

            currentReport.Append("<div class='NormalBackColor'>");
            currentReport.Append(@"
                <table class='ToolTipName' style='margin-bottom: 0px; width: 100%'>
                    <tr>
                        <td rowspan='2' style='width: 95px; padding-left: 10px'><img src='Report_64x64' width='64' height='64' /></td>
                        <td class='NotificationTitle'>Deployment report</td>
                    </tr>
                    <tr>
                        <td class='NotificationSubTitle'>" + (HasBeenCancelled ? "<img style='padding-right: 2px;' src='Warning30x30' height='25px'>Canceled by the user" : (!CompilationHasFailed ? "<img style='padding-right: 2px;' src='Ok30x30' height='25px'>" + (IsTestMode ? "Test done!" : "Done!") : " <img style='padding-right: 2px;' src='Error30x30' height='25px'>An error has occurred...")) + @"</td>
                    </tr>
                </table>");

            currentReport.Append(@"<h2 style='margin-top: 8px; margin-bottom: 8px;'>Parameters :</h2>");

            currentReport.Append(@"                     
                <div style='margin-left: 8px; margin-right: 8px;'>
                    <table style='width: 100%' class='NormalBackColor'>
                        <tr><td style='width: 40%; padding-right: 20px'>Compilation starting time :</td><td><b>" + _proCompilation.StartingTime + @"</b></td></tr>
                        <tr><td style='padding-right: 20px'>Number of cores detected on this computer :</td><td><b>" + Environment.ProcessorCount + @" cores</b></td></tr>
                        <tr><td style='padding-right: 20px'>Number of Prowin processes used for the compilation :</td><td><b>" + _proCompilation.TotalNumberOfProcesses + @" processes</b></td></tr>
                        <tr><td style='padding-right: 20px'>Forced to mono process? :</td><td><b>" + _proCompilation.MonoProcess + (_proEnv.IsDatabaseSingleUser ? " (connected to database in single user mode!)" : "") + @"</b></td></tr>
                        <tr><td style='width: 40%; padding-right: 20px'>Total number of files being compile :</td><td><b>" + _proCompilation.NbFilesToCompile + @" files</b></td></tr>
                        <tr><td style='width: 40%; padding-right: 20px'>Source directory :</td><td><b>" + _currentProfile.SourceDirectory.ToHtmlLink() + @"</b></td></tr>
                        <tr><td style='width: 40%; padding-right: 20px'>Target deployment directory :</td><td><b>" + _proEnv.BaseCompilationPath.ToHtmlLink() + @"</b></td></tr>
                    </table>           
                </div>");

            currentReport.Append(@"<h2 style='margin-top: 8px; margin-bottom: 8px;'>Results :</h2>");

            if (HasBeenCancelled)
            {
                // the process has been canceled
                currentReport.Append(@"<div><img style='padding-right: 20px; padding-left: 5px;' src='Warning30x30' height='15px'>The deployment has been canceled by the user</div>");
            }
            else if (CompilationHasFailed)
            {
                // provide info on the possible error!
                currentReport.Append(@"<div><img style='padding-right: 20px; padding-left: 5px;' src='Error30x30' height='15px'>At least one process has ended in error, the compilation has been canceled</div>");

                if (_proCompilation.CompilationFailedOnMaxUser)
                {
                    currentReport.Append(@"<div><img style='padding-right: 20px; padding-left: 5px;' src='Help' height='15px'>One or more processes started for this compilation tried to connect to the database and failed because the maximum number of connection has been reached (error 748). To correct this problem, you can either :<br><li>reduce the number of processes to use for each core of your computer</li><li>or increase the maximum of connections for your database (-n parameter in the PROSERVE command)</li></div>");
                }
            }


            var           listLinesCompilation = new List <Tuple <int, string> >();
            StringBuilder line = new StringBuilder();

            var totalDeployedFiles   = 0;
            var nbDeploymentError    = 0;
            var nbCompilationError   = 0;
            var nbCompilationWarning = 0;

            // compilation errors
            foreach (var fileInError in _proCompilation.ListFilesToCompile.Where(file => file.Errors != null))
            {
                bool hasError   = fileInError.Errors.Exists(error => error.Level >= ErrorLevel.Error);
                bool hasWarning = fileInError.Errors.Exists(error => error.Level < ErrorLevel.Error);

                if (hasError || hasWarning)
                {
                    // only add compilation errors
                    line.Clear();
                    line.Append("<div %ALTERNATE%style=\"background-repeat: no-repeat; background-image: url('" + (hasError ? "Error30x30" : "Warning30x30") + "'); padding-left: 40px; padding-top: 6px; padding-bottom: 6px;\">");
                    line.Append(ProExecutionCompile.FormatCompilationResultForSingleFile(fileInError.SourcePath, fileInError, null));
                    line.Append("</div>");
                    listLinesCompilation.Add(new Tuple <int, string>(hasError ? 3 : 2, line.ToString()));
                }

                if (hasError)
                {
                    nbCompilationError++;
                }
                else if (hasWarning)
                {
                    nbCompilationWarning++;
                }
            }

            // for each deploy step
            var listLinesByStep = new Dictionary <int, List <Tuple <int, string> > > {
                { 0, new List <Tuple <int, string> >() }
            };

            foreach (var kpv in _filesToDeployPerStep)
            {
                // group either by directory name or by pack name
                var groupDirectory = kpv.Value.GroupBy(deploy => deploy.GroupKey).Select(deploys => deploys.ToList()).ToList();

                foreach (var group in groupDirectory.OrderByDescending(list => list.First().DeployType).ThenBy(list => list.First().GroupKey))
                {
                    var deployFailed = group.Exists(deploy => !deploy.IsOk);
                    var first        = group.First();

                    line.Clear();
                    line.Append("<div %ALTERNATE%style=\"background-repeat: no-repeat; background-image: url('" + (deployFailed ? "Error30x30" : "Ok30x30") + "'); padding-left: 40px; padding-top: 6px; padding-bottom: 6px;\">");
                    line.Append(first.ToStringGroupHeader());
                    foreach (var fileToDeploy in group.OrderBy(deploy => deploy.To))
                    {
                        line.Append(fileToDeploy.ToStringDescription(kpv.Key <= 1 ? _currentProfile.SourceDirectory : _proEnv.BaseCompilationPath));
                    }
                    line.Append("</div>");

                    if (!listLinesByStep.ContainsKey(kpv.Key))
                    {
                        listLinesByStep.Add(kpv.Key, new List <Tuple <int, string> >());
                    }

                    listLinesByStep[kpv.Key].Add(new Tuple <int, string>(deployFailed ? 3 : 1, line.ToString()));

                    if (deployFailed)
                    {
                        nbDeploymentError += group.Count(deploy => !deploy.IsOk);
                    }
                    else
                    {
                        totalDeployedFiles += group.Count;
                    }
                }
            }

            // compilation
            currentReport.Append(@"<div style='padding-top: 7px; padding-bottom: 7px;'>Compiling <b>" + _proCompilation.NbFilesToCompile + "</b> files : <b>" + Utils.GetNbFilesPerType(_proCompilation.GetListOfFileToCompile.Select(compile => compile.SourcePath).ToList()).Aggregate("", (current, kpv) => current + (@"<img style='padding-right: 5px;' src='" + Utils.GetExtensionImage(kpv.Key.ToString(), true) + "' height='15px'><span style='padding-right: 12px;'>x" + kpv.Value + "</span>")) + "</b></div>");

            // compilation time
            currentReport.Append(@"<div><img style='padding-right: 20px; padding-left: 5px;' src='Time' height='15px'>Total elapsed time for the compilation : <b>" + _proCompilation.TotalCompilationTime + @"</b></div>");

            if (nbCompilationError > 0)
            {
                currentReport.Append("<div><img style='padding-right: 20px; padding-left: 5px;' src='Error30x30' height='15px'>" + nbCompilationError + " files with compilation error(s)</div>");
            }
            if (nbCompilationWarning > 0)
            {
                currentReport.Append("<div><img style='padding-right: 20px; padding-left: 5px;' src='Warning30x30' height='15px'>" + nbCompilationWarning + " files with compilation warning(s)</div>");
            }
            if (_proCompilation.NumberOfFilesTreated - nbCompilationError - nbCompilationWarning > 0)
            {
                currentReport.Append("<div><img style='padding-right: 20px; padding-left: 5px;' src='Ok30x30' height='15px'>" + (_proCompilation.NumberOfFilesTreated - nbCompilationError - nbCompilationWarning) + " files compiled correctly</div>");
            }

            // deploy
            currentReport.Append(@"<div style='padding-top: 7px; padding-bottom: 7px;'>Deploying <b>" + totalDeployedFiles + "</b> files : <b>" + Utils.GetNbFilesPerType(_filesToDeployPerStep.SelectMany(pair => pair.Value).Select(deploy => deploy.To).ToList()).Aggregate("", (current, kpv) => current + (@"<img style='padding-right: 5px;' src='" + Utils.GetExtensionImage(kpv.Key.ToString(), true) + "' height='15px'><span style='padding-right: 12px;'>x" + kpv.Value + "</span>")) + "</b></div>");

            // deployment time
            currentReport.Append(@"<div><img style='padding-right: 20px; padding-left: 5px;' src='Time' height='15px'>Total elapsed time for the deployment : <b>" + TotalDeploymentTime + @"</b></div>");

            if (nbDeploymentError > 0)
            {
                currentReport.Append("<div><img style='padding-right: 20px; padding-left: 5px;' src='Error30x30' height='15px'>" + nbDeploymentError + " files not deployed</div>");
            }
            if (totalDeployedFiles - nbDeploymentError > 0)
            {
                currentReport.Append("<div><img style='padding-right: 20px; padding-left: 5px;' src='Ok30x30' height='15px'>" + (totalDeployedFiles - nbDeploymentError) + " files deployed correctly</div>");
            }

            // compilation
            if (listLinesCompilation.Count > 0)
            {
                currentReport.Append("<h3 style='margin-top: 7px; margin-bottom: 7px;'>Compilation details :</h3>");
                var boolAlternate = false;
                foreach (var listLine in listLinesCompilation.OrderByDescending(tuple => tuple.Item1))
                {
                    currentReport.Append(listLine.Item2.Replace("%ALTERNATE%", boolAlternate ? "class='AlternatBackColor' " : "class='NormalBackColor' "));
                    boolAlternate = !boolAlternate;
                }
            }

            // deployment steps
            foreach (var listLinesKpv in listLinesByStep)
            {
                currentReport.Append("<h3 style='margin-top: 7px; margin-bottom: 7px;'>Deployment step " + listLinesKpv.Key + " details :</h3>");
                var boolAlternate2 = false;
                foreach (var listLine in listLinesKpv.Value.OrderByDescending(tuple => tuple.Item1))
                {
                    currentReport.Append(listLine.Item2.Replace("%ALTERNATE%", boolAlternate2 ? "class='AlternatBackColor' " : "class='NormalBackColor' "));
                    boolAlternate2 = !boolAlternate2;
                }
            }

            currentReport.Append("</div>");

            return(currentReport.ToString());
        }