private void SaveSettings()
        {
            Settings    current = Parser.GetSettings();
            string      path    = SettingsPath;
            VirtualFile file    = fileSystem.GetFile(path);

            using (Stream stream = file.OpenWrite())
            {
                stream.SetLength(0);
                XDocument document = new XDocument(new XElement("Settings",
                                                                new XAttribute("AttributePrefix", current.AttributePrefix),
                                                                new XElement("CliRepositoryRoot", current.CliRepositoryRoot),
                                                                new XElement("CliRepositoryFileName", current.CliRepositoryFileName),
                                                                new XElement("CliRepositorySignatureFileName", current.CliRepositorySignatureFileName),
                                                                new XElement("HttpProxy", current.HttpProxy ?? string.Empty),
                                                                new XElement("LogFilePath", current.LogFilePath ?? "log.txt"),
                                                                new XElement("SystemCommands", current.UseSystemCommands.ToString(CultureInfo.InvariantCulture)),
                                                                new XElement("SDKS",
                                                                             current.SdkPaths.Select(p => new XElement("SDK", p))
                                                                             .Cast <object>().ToArray()),
                                                                new XElement("Templates",
                                                                             current.TemplateLocations.Select(l => new XElement("Template", l))
                                                                             .Cast <object>().ToArray())));
                document.Save(stream);
            }
        }
Example #2
0
        public async Task <VirtualFile> DownloadVersion(string version, VirtualDirectory downloadDirectory,
                                                        IProgressNotifier parentProgress = null,
                                                        string proxy = "")
        {
            await EnsureRepository(proxy).ConfigureAwait(false);

            string actualVersion = await CheckVersion(version, proxy).ConfigureAwait(false);

            CliVersionDefinition versionDefinition = repository.Version.FirstOrDefault(v => v.GetInformalVersion() == actualVersion);

            if (versionDefinition == null)
            {
                throw new UnkownVersionException(actualVersion);
            }

            FileDefinition downloadFile = GetCorrectVersion();

            if (downloadFile == null)
            {
                throw new UnsupportedSystemException(environmentService.PlatformName, environmentService.Architecture);
            }

            Uri downloadUri = InvalidUriFormatException.TryCreateUri(downloadFile.relPath,
                                                                     InvalidUriFormatException.TryCreateUri(settingsProvider.Settings.CliRepositoryRoot));
            VirtualFile result = downloadDirectory.File(downloadFile.name);

            using (Stream fileStream = result.OpenWrite())
            {
                await Download(downloadUri, fileStream, proxy, parentProgress).ConfigureAwait(false);
            }

            using (Stream fileStream = result.OpenRead())
            {
                try
                {
                    securityValidator.ValidateHash(fileStream, downloadFile.Fingerprint.hash, downloadFile.Fingerprint.algorithm);
                }
                catch (HashValidationException e)
                {
                    e.ValidationFileName = downloadUri.AbsolutePath;
                    throw;
                }
            }

            return(result);

            FileDefinition GetCorrectVersion()
            {
                FileDefinition[] correctArchitecture = versionDefinition.File
                                                       .Where(f => f.Architecture.ToString().Equals(environmentService.Architecture,
                                                                                                    StringComparison.OrdinalIgnoreCase))
                                                       .ToArray();
                return(correctArchitecture.FirstOrDefault(f => f.OS.ToString().Equals(environmentService.PlatformName,
                                                                                      StringComparison.OrdinalIgnoreCase)) ??
                       correctArchitecture.FirstOrDefault(f => f.OS == OSDefinition.unbound));
            }
        }
        public string MoveApplicationFile(string fileName, string newLocation)
        {
            VirtualFile oldFile = FileSystem.GetFile(Path.Combine(AssemblyDirectory, fileName));
            VirtualFile newFile = FileSystem.GetFile(Path.Combine(AssemblyDirectory, newLocation));

            using (Stream source = oldFile.OpenRead())
                using (Stream destination = newFile.OpenWrite())
                {
                    source.CopyTo(destination);
                }

            return(newFile.FullName);
        }
Example #4
0
 public static void WriteCMakeFile(VirtualFile cMakeFile, string name,
                                   string sourceDirectory = Constants.SourceFolderName)
 {
     using (Stream templateStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("PlcNext.Common.Project.templates.ProjectTemplates.CMakeLists.txt"))
         using (Stream fileStream = cMakeFile.OpenWrite())
             using (StreamReader reader = new StreamReader(templateStream))
                 using (StreamWriter writer = new StreamWriter(fileStream))
                 {
                     fileStream.SetLength(0);
                     string line;
                     while ((line = reader.ReadLine()) != null)
                     {
                         line = line.Replace("$(sourceDirectory)", sourceDirectory);
                         line = line.Replace("$(name)", name);
                         writer.WriteLine(line);
                     }
                 }
 }
Example #5
0
        public async Task InstallVersion(VirtualFile setup, VirtualDirectory tempDirectory, IProgressNotifier parentProgress = null)
        {
            VirtualDirectory destination = tempDirectory.Directory(Guid.NewGuid().ToByteString());

            using (IProgressNotifier progressNotifier = parentProgress?.Spawn(installationSteps.Count() + 1, "Installing version"))
            {
                await fileUnpackService.Unpack(setup, destination, progressNotifier).ConfigureAwait(false);

                foreach (IInstallationStep installationStep in installationSteps)
                {
                    await installationStep.Install(destination, progressNotifier).ConfigureAwait(false);
                }

                await ReplaceApplication().ConfigureAwait(false);
            }

            async Task ReplaceApplication()
            {
                userInterface.WriteInformation("Replace current version with new version");

                if (environmentService.Platform != OSPlatform.Windows)
                {
                    await destination.CopyToAsync(fileSystem.GetDirectory(environmentService.AssemblyDirectory)).ConfigureAwait(false);

                    tempDirectory.Delete();
                }
                else
                {
                    VirtualFile batchFile       = tempDirectory.File("copy_version.bat");
                    Assembly    currentAssembly = Assembly.GetAssembly(GetType());
                    using (Stream batchResourceStream = currentAssembly.GetManifestResourceStream("PlcNext.Common.Installation.copy_version.bat"))
                        using (Stream batchFileStream = batchFile.OpenWrite())
                        {
                            batchResourceStream?.CopyTo(batchFileStream);
                        }

                    processManager.StartProcess(batchFile.FullName,
                                                $"\"{destination.FullName}\" \"{environmentService.AssemblyDirectory}\" " +
                                                $"\"{tempDirectory.FullName}\" \"{binariesLocator.GetExecutable("application").Name}\"", userInterface, null, showOutput: false, showError: false, killOnDispose: false);
                }
            }
        }
Example #6
0
 private void UpdateProjectSettingsFile(string message)
 {
     if (projectFile == null)
     {
         //do not persist changes
         return;
     }
     using (Stream fileStream = projectFile.OpenWrite())
     {
         //if an existing file content is longer than the new content, the xml is corrupted
         //set the stream length to 0 to avoid this
         fileStream.SetLength(0);
         XmlSerializer serializer = new XmlSerializer(typeof(ProjectSettings));
         serializer.Serialize(fileStream, Value);
     }
     executionContext.Observable.OnNext(new ProjectSettingChange(() =>
     {
         projectFile.Restore();
     }, projectFile.Parent.FullName, message));
 }
        public async Task <IEnumerable <VirtualFile> > InitalizeTemplate(Entity dataModel, ChangeObservable observable)
        {
            bool forced = dataModel.Value <CommandDefinition>()
                          .Argument <BoolArgument>(TemplateCommandBuilder.ForcedArgumentName)
                          .Value;
            TemplateDescription template = dataModel.Template();

            List <VirtualFile> generatedFiles = new List <VirtualFile>(await InitializeFiles().ConfigureAwait(false));

            generatedFiles.AddRange(await InitializeSubTemplates().ConfigureAwait(false));

            Exception e = dataModel.GetCodeExceptions();

            if (e != null)
            {
                e.CompleteCodeExceptions(fileSystem.GetDirectory(dataModel.Root.Path));
                throw e;
            }

            return(generatedFiles);

            async Task <IEnumerable <VirtualFile> > InitializeFiles()
            {
                string basePath             = dataModel.Root.Path;
                HashSet <VirtualFile> files = new HashSet <VirtualFile>();

                foreach (templateFile file in template.File)
                {
                    (string content, Encoding encoding) = await GetResolvedTemplateContent(dataModel, file, template).ConfigureAwait(false);

                    VirtualFile destination = await GetFile(dataModel, file, forced, basePath, template).ConfigureAwait(false);

                    observable.OnNext(new Change(() => destination.Restore(),
                                                 $"Create file {Path.GetFileName(destination.Name)} for template " +
                                                 $"{template.name} in {destination.Parent.FullName}."));

                    using (Stream fileStream = destination.OpenWrite())
                        using (StreamWriter writer = new StreamWriter(fileStream, encoding))
                        {
                            fileStream.SetLength(0);
                            await writer.WriteAsync(content).ConfigureAwait(false);
                        }

                    files.Add(destination);
                }

                return(files);
            }

            async Task <IEnumerable <VirtualFile> > InitializeSubTemplates()
            {
                List <VirtualFile> files = new List <VirtualFile>();

                foreach (templateReference reference in SortByRelationship(template.AddTemplate ?? Enumerable.Empty <templateReference>()))
                {
                    if (repository.Template(reference.template) == null)
                    {
                        throw new TemplateReferenceNotDefinedException(reference.template);
                    }

                    CommandDefinitionBuilder pseudoDefinition = CommandDefinitionBuilder.Create()
                                                                .SetName(reference.template);
                    foreach (templateArgumentInstance argumentInstance in reference.Arguments)
                    {
                        AddArgument(argumentInstance, pseudoDefinition);
                    }

                    IEnumerable <IGrouping <string, templateRelationshipInstance> > grouped = (reference.Relationship ?? Enumerable.Empty <templateRelationshipInstance>()).GroupBy(r => r.name);
                    foreach (IGrouping <string, templateRelationshipInstance> relationshipInstances in grouped)
                    {
                        AddRelationships(relationshipInstances, pseudoDefinition, reference);
                    }

                    pseudoDefinition.CreateArgument()
                    .SetName(TemplateCommandBuilder.ForcedArgumentName)
                    .SetValue(forced)
                    .Build();
                    Entity referencedTemplateEntity = dataModel.Create(reference.template, pseudoDefinition.Build());
                    dataModel.AddEntity(referencedTemplateEntity);
                    files.AddRange(await InitalizeTemplate(referencedTemplateEntity, observable).ConfigureAwait(false));
                }

                return(files);

                void AddArgument(templateArgumentInstance templateArgumentInstance, CommandDefinitionBuilder commandDefinitionBuilder)
                {
                    string templateArgumentValue = resolver.Resolve(templateArgumentInstance.value, dataModel);
                    bool   argumentHasNoValue    = bool.TryParse(templateArgumentValue, out bool boolValue);

                    string[] argumentSplit   = templateArgumentValue.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
                    bool     isMultiArgument = argumentSplit.Length > 1;

                    if (argumentHasNoValue)
                    {
                        commandDefinitionBuilder.CreateArgument()
                        .SetName(templateArgumentInstance.name)
                        .SetValue(boolValue)
                        .Build();
                    }
                    else if (isMultiArgument)
                    {
                        commandDefinitionBuilder.CreateArgument()
                        .SetName(templateArgumentInstance.name)
                        .SetValue(argumentSplit)
                        .Build();
                    }
                    else
                    {
                        commandDefinitionBuilder.CreateArgument()
                        .SetName(templateArgumentInstance.name)
                        .SetValue(templateArgumentValue)
                        .Build();
                    }
                }

                void AddRelationships(IGrouping <string, templateRelationshipInstance> relationshipInstances,
                                      CommandDefinitionBuilder commandDefinitionBuilder, templateReference reference)
                {
                    templateRelationship relationshipDefinition = repository.Template(reference.template).Relationship
                                                                  ?.FirstOrDefault(r => r.name ==
                                                                                   relationshipInstances.Key);

                    if (relationshipDefinition == null)
                    {
                        throw new TemplateRelationshipNotFoundException(reference.template, relationshipInstances.Key);
                    }

                    bool multipleValues = relationshipDefinition.multiplicity == multiplicity.OneOrMore;

                    if (multipleValues)
                    {
                        string[] relationships = relationshipInstances.Select(r => resolver.Resolve(r.value, dataModel))
                                                 .ToArray();
                        commandDefinitionBuilder.CreateArgument()
                        .SetName(relationshipInstances.Key)
                        .SetValue(relationships)
                        .Build();
                    }
                    else
                    {
                        if (relationshipInstances.Count() != 1)
                        {
                            throw new RelationshipMultiplicityMismatchException(relationshipInstances.Key, reference.template);
                        }
                        commandDefinitionBuilder.CreateArgument()
                        .SetName(relationshipInstances.Key)
                        .SetValue(resolver.Resolve(relationshipInstances.Single().value, dataModel))
                        .Build();
                    }
                }

                IEnumerable <templateReference> SortByRelationship(IEnumerable <templateReference> references)
                {
                    List <templateReference> unsorted = references.ToList();
                    List <templateReference> sorted   = new List <templateReference>();

                    while (unsorted.Any())
                    {
                        Insert(unsorted[0]);
                    }

                    return(sorted);

                    void Insert(templateReference current, CycleChecker <templateReference> cycleChecker = null)
                    {
                        using (cycleChecker = cycleChecker?.SpawnChild() ?? new CycleChecker <templateReference>(
                                   ExceptionTexts.TemplateRelationshipCycle,
                                   () => cycleChecker = null))
                        {
                            cycleChecker.AddItem(current);
                            List <templateReference> dependent = new List <templateReference>();
                            foreach (templateRelationshipInstance relationshipInstance in current.Relationship ??
                                     Enumerable
                                     .Empty <
                                         templateRelationshipInstance
                                         >())
                            {
                                templateReference reference =
                                    unsorted.FirstOrDefault(r => HasRelationship(r, relationshipInstance));
                                if (reference != null)
                                {
                                    Insert(reference, cycleChecker);
                                }
                                else
                                {
                                    reference = sorted.FirstOrDefault(r => HasRelationship(r, relationshipInstance));
                                }

                                if (reference != null)
                                {
                                    dependent.Add(reference);
                                }
                            }

                            int skipping = dependent.Any() ? dependent.Select(d => sorted.IndexOf(d)).Max() : -1;
                            int index    = skipping + 1;
                            sorted.Insert(index, current);
                            unsorted.Remove(current);

                            bool HasRelationship(templateReference currentReference,
                                                 templateRelationshipInstance relationshipInstance)
                            {
                                templateArgumentInstance instance = currentReference.Arguments
                                                                    .FirstOrDefault(a => a.name
                                                                                    .Equals(
                                                                                        EntityKeys
                                                                                        .NameKey,
                                                                                        StringComparison
                                                                                        .OrdinalIgnoreCase));

                                return(instance?.value?.Equals(relationshipInstance.value,
                                                               StringComparison.OrdinalIgnoreCase) == true);
                            }
                        }
                    }
                }
            }
        }
Example #8
0
        public async Task Unpack(VirtualFile file, VirtualDirectory destination,
                                 IProgressNotifier parentProgress = null, ChangeObservable observable = null)
        {
            if (Path.GetExtension(file.Name)?.Equals(".zip", StringComparison.OrdinalIgnoreCase) == true)
            {
                await UnpackZipFile().ConfigureAwait(false);
            }
            else if (file.Name.EndsWith(".tar.xz", StringComparison.OrdinalIgnoreCase))
            {
                UnpackTarXzFile(file);
            }
            else if (Path.GetExtension(file.Name)?.Equals(".xz", StringComparison.OrdinalIgnoreCase) == true)
            {
                UnpackXzFile(file);
            }
            else if (Path.GetExtension(file.Name)?.Equals(".tar", StringComparison.OrdinalIgnoreCase) == true)
            {
                UnpackTarFile(file);
            }
            else if (Path.GetExtension(file.Name)?.Equals(".sh", StringComparison.OrdinalIgnoreCase) == true &&
                     environmentService.Platform != OSPlatform.Windows)
            {
                await UnpackSelfExtractingShellScript().ConfigureAwait(false);
            }
            else
            {
                throw new UnsupportedArchiveFormatException(file.FullName);
            }

            async Task UnpackSelfExtractingShellScript()
            {
                ValidateShellScript();
                StringBuilderUserInterface userInterface = new StringBuilderUserInterface(log, writeInformation: true, writeError: true);

                try
                {
                    using (parentProgress?.SpawnInfiniteProgress("Executing the shell script."))
                        using (IProcess process = processManager.StartProcess(file.FullName, $"-y -d \"{destination.FullName}\"", userInterface))
                        {
                            await process.WaitForExitAsync().ConfigureAwait(false);

                            if (process.ExitCode != 0)
                            {
                                throw new UnsupportedArchiveFormatException(file.FullName,
                                                                            new FormattableException(
                                                                                $"An error occured while executing the script.{Environment.NewLine}" +
                                                                                $"{userInterface.Error}"));
                            }
                        }
                }
                catch (Exception e)
                {
                    if (e is FormattableException)
                    {
                        throw;
                    }
                    throw new UnsupportedArchiveFormatException(file.FullName,
                                                                new FormattableException(
                                                                    $"An exception occured while executing the script.{Environment.NewLine}" +
                                                                    $"{e.Message}", e));
                }

                void ValidateShellScript()
                {
                    StringBuilderUserInterface validationUserInterface = new StringBuilderUserInterface(log, writeInformation: true, writeError: true);

                    try
                    {
                        using (IProcess process = processManager.StartProcess(file.FullName, "--help", validationUserInterface))
                        {
                            process.WaitForExit();
                        }
                    }
                    catch (UnauthorizedAccessException e)
                    {
                        throw new UnsupportedArchiveFormatException(file.FullName,
                                                                    new FormattableException(
                                                                        $"An exception occured while inspecting the script.{Environment.NewLine}" +
                                                                        $"This excpetion can occur when the file is not marked as executable.{Environment.NewLine}" +
                                                                        $"{e.Message}", e));
                    }
                    catch (Exception e)
                    {
                        if (e is FormattableException)
                        {
                            throw;
                        }
                        throw new UnsupportedArchiveFormatException(file.FullName,
                                                                    new FormattableException(
                                                                        $"An exception occured while inspecting the script.{Environment.NewLine}" +
                                                                        $"{e.Message}", e));
                    }

                    if (!Regex.IsMatch(validationUserInterface.Information,
                                       @"(?=.*(?:usage|Usage)).*(?=.*-y)(?=.*-d)"))
                    {
                        throw new UnsupportedArchiveFormatException(file.FullName,
                                                                    new FormattableException($"Did not find the expected usage information.{Environment.NewLine}" +
                                                                                             $"The expected information need to include the options '-y' and '-d'.{Environment.NewLine}" +
                                                                                             $"The following usage information was given:{Environment.NewLine}" +
                                                                                             $"{validationUserInterface.Information}"));
                    }
                }
            }

            async Task UnpackZipFile()
            {
                using (Stream fileStream = file.OpenRead())
                    using (ZipFile zipFile = new ZipFile(fileStream))
                        using (IProgressNotifier mainProgress = parentProgress?.Spawn(2, $"Extracting {file.FullName} to {destination.FullName}."))
                        {
                            archiveResultBuilder.Clear();
                            using (mainProgress?.SpawnInfiniteProgress("Test archive integrity."))
                            {
                                ZipFile copy = zipFile;
                                await Task.Run(() =>
                                {
                                    if (!copy.TestArchive(true, TestStrategy.FindAllErrors, ResultHandler))
                                    {
                                        throw new UnsupportedArchiveFormatException(
                                            file.FullName, new FormattableException(archiveResultBuilder.ToString()));
                                    }
                                }).ConfigureAwait(false);
                            }

                            double increment = (double)Constants.ProgressMaxResolution / zipFile.Count + 1;
                            using (IProgressNotifier extractProgress = mainProgress?.Spawn(Constants.ProgressMaxResolution, "Extract files"))
                            {
                                foreach (ZipEntry zipEntry in zipFile)
                                {
                                    extractProgress?.TickIncrement(increment);
                                    if (!zipEntry.IsFile)
                                    {
                                        continue;   // Ignore directories
                                    }

                                    byte[] buffer = new byte[Constants.StreamCopyBufferSize]; // 4K is optimum

                                    using (Stream zipStream = zipFile.GetInputStream(zipEntry))
                                    {
                                        string[]         path            = fileSystem.GetPath(zipEntry.Name);
                                        VirtualDirectory fileDestination = destination.Directory(path.Take(path.Length - 1).ToArray());
                                        VirtualFile      entryFile       = fileDestination.File(path.Last());

                                        // Unzip file in buffered chunks. This is just as fast as unpacking to a buffer the full size
                                        // of the file, but does not waste memory.
                                        using (Stream streamWriter = entryFile.OpenWrite())
                                        {
                                            StreamUtils.Copy(zipStream, streamWriter, buffer);
                                        }
                                    }
                                }
                            }
                        }
            }

            VirtualFile UnpackXzFile(VirtualFile packedFile)
            {
                using (Stream fileStream = packedFile.OpenRead())
                    using (XZStream xzStream = new XZStream(fileStream))
                        using (parentProgress?.SpawnInfiniteProgress($"Extracting {packedFile.Name}..."))
                        {
                            string[] path             = fileSystem.GetPath(packedFile.FullName);
                            string   relativeFilePath = path.Last().Substring(0, path.Last().LastIndexOf(".xz", StringComparison.OrdinalIgnoreCase));
                            if (destination.FileExists(relativeFilePath))
                            {
                                destination.File(relativeFilePath).Delete();
                            }
                            VirtualFile destinationFile = destination.File(relativeFilePath);
                            observable?.OnNext(new Change(() => destinationFile.Restore()));

                            byte[] buffer = new byte[Constants.StreamCopyBufferSize]; // 4K is optimum

                            using (Stream streamWriter = destinationFile.OpenWrite())
                            {
                                StreamUtils.Copy(xzStream, streamWriter, buffer);
                                return(destinationFile);
                            }
                        }
            }

            void UnpackTarFile(VirtualFile packedFile)
            {
                //sharpcompress
                using (Stream fileStream = packedFile.OpenRead())
                    using (TarArchive tarArchive = TarArchive.Open(fileStream))
                    {
                        double increment = (double)Constants.ProgressMaxResolution / tarArchive.Entries.Count;
                        using (IProgressNotifier extractProgress = parentProgress?.Spawn(Constants.ProgressMaxResolution, "Extracting .tar archive"))
                        {
                            foreach (TarArchiveEntry tarEntry in tarArchive.Entries)
                            {
                                extractProgress?.TickIncrement(increment);
                                if (tarEntry.IsDirectory)
                                {
                                    continue;       // Ignore directories
                                }

                                byte[] buffer = new byte[Constants.StreamCopyBufferSize]; // 4K is optimum

                                using (Stream tarStream = tarEntry.OpenEntryStream())
                                {
                                    string[]         path            = fileSystem.GetPath(tarEntry.Key);
                                    VirtualDirectory fileDestination = destination.Directory(path.Take(path.Length - 1).ToArray());
                                    if (fileDestination.FileExists(path.Last()))
                                    {
                                        fileDestination.File(path.Last()).Delete();
                                    }
                                    VirtualFile entryFile = fileDestination.File(path.Last());
                                    observable?.OnNext(new Change(() => entryFile.Restore()
                                                                  //, $"Unpack {tarEntry.Key}."
                                                                  ));

                                    //Unzip file in buffered chunks. This is just as fast as unpacking to a buffer the full size
                                    // of the file, but does not waste memory.
                                    using (Stream streamWriter = entryFile.OpenWrite())
                                    {
                                        StreamUtils.Copy(tarStream, streamWriter, buffer);
                                    }
                                }
                            }
                        }
                    }
            }

            void UnpackTarXzFile(VirtualFile packedFile)
            {
                using (IProgressNotifier subProgress = parentProgress?.Spawn(2))
                {
                    parentProgress = subProgress;
                    VirtualFile tarFile = UnpackXzFile(packedFile);
                    UnpackTarFile(tarFile);
                    tarFile.Delete();
                }
            }
        }
Example #9
0
        public void DeployFiles(Entity dataModel)
        {
            IEnumerable <Entity> deployableEntities = dataModel.Root.Hierarchy();
            ProjectEntity        project            = ProjectEntity.Decorate(dataModel.Root);

            // get targets to deploy for
            IEnumerable <Target> targets = null;

            CommandEntity command = CommandEntity.Decorate(dataModel);

            if (command.IsCommandArgumentSpecified(Constants.TargetArgumentName))
            {
                IEnumerable <string> rawTargets = command.GetMultiValueArgument(Constants.TargetArgumentName);

                if (rawTargets.Any())
                {
                    targets = targetParser.GetSpecificTargets(rawTargets, false).Select(t => t.Item1);
                }
                else
                {
                    targets = targetParser.Targets(project, false).ValidTargets;
                }
            }
            else
            {
                targets = targetParser.Targets(project, false).ValidTargets;
            }
            if (!targets.Any())
            {
                throw new NoTargetSpecifiedException();
            }


            foreach (Entity deployableEntity in deployableEntities)
            {
                DeployFilesFromTemplate(deployableEntity);
            }

            if (command.IsCommandArgumentSpecified(Constants.FilesArgumentName))
            {
                IEnumerable <string> files = command.GetMultiValueArgument(Constants.FilesArgumentName);

                foreach (string file in files)
                {
                    DeployFileFromArgument(file);
                }
            }

            void DeployFileFromArgument(string file)
            {
                Match match = FilesDecoder.Match(file);

                if (!match.Success)
                {
                    throw new FormattableException($"The input {file} could not be parsed. Expected pattern is <fileLocation>|<destination>[|<target>]");
                }

                string from      = match.Groups["from"].Value;
                string to        = match.Groups["destination"].Value;
                string rawTarget = match.Groups["target"].Value;

                VirtualDirectory baseDirectory     = null;
                string           relativePath      = null;
                bool             recreateStructure = false;

                string[] path          = fileSystem.GetPath(from);
                int      firstWildCard = path.TakeWhile(p => !p.Contains('*') && !p.Contains('?')).Count();

                if (firstWildCard != path.Length)
                {
                    baseDirectory     = fileSystem.GetDirectory(Path.Combine(path.Take(firstWildCard).ToArray()), project.Path, false);
                    relativePath      = Path.Combine(path.Skip(firstWildCard).ToArray());
                    recreateStructure = true;
                }

                if (recreateStructure)
                {
                    IEnumerable <VirtualFile> deployFiles = baseDirectory.Files(relativePath, true).ToArray();
                    if (!deployFiles.Any())
                    {
                        throw new DeployFileNotFoundException(@from);
                    }
                    foreach (VirtualFile deployFile in deployFiles)
                    {
                        string structure       = Path.GetDirectoryName(deployFile.GetRelativePath(baseDirectory));
                        string fileDestination = string.IsNullOrEmpty(structure) ? to : Path.Combine(to, structure);
                        DeployFile(deployFile.FullName, fileDestination);
                    }
                }
                else if (fileSystem.FileExists(from, project.Path))
                {
                    DeployFile(from, to);
                }
                else
                {
                    throw new DeployFileNotFoundException(@from);
                }

                void DeployFile(string sourceFile, string destinationDirectory)
                {
                    if (!string.IsNullOrEmpty(rawTarget))
                    {
                        DeployFileForRawTarget(rawTarget, sourceFile, destinationDirectory);
                    }
                    else
                    {
                        foreach (Target target in targets)
                        {
                            DeployFileForTarget(target, sourceFile, destinationDirectory);
                        }
                    }
                }

                void DeployFileForRawTarget(string target, string sourceFile, string destinationDirectory)
                {
                    Target parsedTarget = targetParser.ParseTarget(target, null, targets);

                    DeployFileForTarget(parsedTarget, sourceFile, destinationDirectory);
                }

                void DeployFileForTarget(Target target, string sourceFile, string destinationDirectory)
                {
                    VirtualFile fileToCopy = fileSystem.GetFile(sourceFile, project.Path);
                    VirtualFile copiedFile = fileSystem.GetDirectory(destinationDirectory, GetOutputDirectory(target).FullName).File(fileToCopy.Name);

                    using (Stream source = fileToCopy.OpenRead(true))
                        using (Stream destination = copiedFile.OpenWrite())
                        {
                            destination.SetLength(0);
                            source.CopyTo(destination);

                            executionContext.WriteVerbose($"Deployed file {fileToCopy.FullName} to {copiedFile.FullName}.");
                        }
                }
            }

            void DeployFilesFromTemplate(Entity deployableEntity)
            {
                TemplateDescription template = deployableEntity.Template();

                if (template == null)
                {
                    return;
                }

                foreach (templateFile file in template.File)
                {
                    if (!file.deployPathSpecified)
                    {
                        continue;
                    }

                    VirtualFile deployableFile = GetFile(file, dataModel.Root.Path, false, out string path);

                    DeployFile(file, deployableFile, path);
                }

                foreach (templateGeneratedFile generatedFile in template.GeneratedFile ?? Enumerable.Empty <templateGeneratedFile>())
                {
                    if (!generatedFile.deployPathSpecified)
                    {
                        continue;
                    }

                    VirtualFile deployableFile = GetFile(generatedFile, dataModel.Root.Path, true, out string path);

                    DeployFile(generatedFile, deployableFile, path);
                }

                VirtualFile GetDestination(templateFile file, Target target, string name)
                {
                    string basePath = GetOutputDirectory(target).FullName;
                    string path     = resolver.Resolve(file.deployPath ?? string.Empty, deployableEntity);

                    VirtualFile destination = fileSystem.GetFile(Path.Combine(path, name), basePath);

                    return(destination);
                }

                void DeployFile(templateFile file, VirtualFile deployableFile, string filePath)
                {
                    if (deployableFile == null)
                    {
                        executionContext.WriteVerbose($"Could not find file {filePath} in {dataModel.Root.Path}, the file will not be deployed.");
                        return;
                    }
                    foreach (Target target in targets)
                    {
                        VirtualFile destination = GetDestination(file, target, deployableFile.Name);

                        using (Stream source = deployableFile.OpenRead(true))
                            using (Stream dest = destination.OpenWrite())
                            {
                                dest.SetLength(0);
                                source.CopyTo(dest);
                            }

                        executionContext.WriteVerbose($"Deployed file {deployableFile.FullName} to {destination.FullName}.");
                    }
                }

                VirtualFile GetFile(templateFile file, string basePath, bool isGeneratedFile, out string realFilePath)
                {
                    string path = resolver.Resolve(file.path ?? string.Empty, deployableEntity);
                    string name = resolver.Resolve(file.name, deployableEntity);

                    if (isGeneratedFile && file is templateGeneratedFile generatedFile)
                    {
                        realFilePath = Path.Combine(path, name);
                        if (!Path.IsPathRooted(realFilePath))
                        {
                            realFilePath = Path.Combine(Constants.IntermediateFolderName, generatedFile.generator?.ToLowerInvariant() ?? string.Empty, realFilePath);
                        }
                    }
                    else
                    {
                        realFilePath = Path.Combine(path, name);
                    }

                    VirtualFile destination = fileSystem.FileExists(realFilePath, basePath)
                                                  ? fileSystem.GetFile(realFilePath, basePath)
                                                  : null;

                    return(destination);
                }
            }

            VirtualDirectory GetOutputDirectory(Target target)
            {
                string buildTypeFolder = command.IsCommandArgumentSpecified(Constants.BuildTypeArgumentName)
                                             ? FormatBuildType(command.GetSingleValueArgument(Constants.BuildTypeArgumentName))
                                             : Constants.ReleaseFolderName;
                string basePath = project.Path;

                if (!command.IsCommandArgumentSpecified(Constants.OutputArgumentName))
                {
                    return(fileSystem.GetDirectory(Path.Combine(basePath, Constants.LibraryFolderName, target.GetFullName().Replace(',', '_'),
                                                                buildTypeFolder)));
                }

                basePath = fileSystem.GetDirectory(command.GetSingleValueArgument(Constants.OutputArgumentName), basePath).FullName;
                basePath = Path.Combine(basePath, target.GetFullName().Replace(',', '_'), buildTypeFolder);

                return(fileSystem.GetDirectory(basePath));

                string FormatBuildType(string buildType)
                {
                    if (string.IsNullOrEmpty(buildType))
                    {
                        return(Constants.ReleaseFolderName);
                    }

                    return(buildType.Substring(0, 1).ToUpperInvariant() +
                           buildType.Substring(1).ToLowerInvariant());
                }
            }
        }
        public void Initialize(InstancesRegistrationSource exportProvider, Action <string> printMessage)
        {
            this.printMessage = printMessage;

            exportProvider.AddInstance(FileSystem);

            SetupSdks();

            SetupKeyFiles();

            SetupLibraryBuilder();

            Load("Templates", FileSystem.GetDirectory(AssemblyDirectory), ".meta", "._acf");

            create = true;

            void SetupKeyFiles()
            {
                VirtualFile keyFile = FileSystem.GetFile(Path.Combine(AssemblyDirectory, "public_cli_repository_key.xml"));

                using (Stream fileStream = keyFile.OpenWrite())
                    using (Stream resourcStream = Assembly.GetExecutingAssembly()
                                                  .GetManifestResourceStream("Test.PlcNext.Deployment.StandardPublicCliRepositoryKey.xml"))
                    {
                        fileStream.SetLength(0);
                        resourcStream.CopyTo(fileStream);
                    }
            }

            void SetupSdks()
            {
                string      assemblyName = Assembly.GetExecutingAssembly().GetName().Name;
                VirtualFile settingsFile = FileSystem.GetFile(Path.Combine(AssemblyDirectory, assemblyName, "settings.xml"));

                using (Stream fileStream = settingsFile.OpenWrite())
                    using (Stream resourcStream = Assembly.GetExecutingAssembly()
                                                  .GetManifestResourceStream("Test.PlcNext.Deployment.StandardSettings.xml"))
                        using (StreamReader reader = new StreamReader(resourcStream))
                            using (StreamWriter writer = new StreamWriter(fileStream))
                            {
                                fileStream.SetLength(0);
                                string line;
                                while ((line = reader.ReadLine()) != null)
                                {
                                    if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                                    {
                                        line = line.Replace(@"C:/MySdk", "/usr/bin/MySdk");
                                    }
                                    writer.WriteLine(line);
                                }
                            }

                VirtualDirectory sdkDirectory = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
                                                    ? FileSystem.GetDirectory(@"C:\MySdk")
                                                    : FileSystem.GetDirectory(@"/usr/bin/MySdk");
                VirtualFile propertiesFile = FileSystem.GetFile(Path.Combine(AssemblyDirectory, assemblyName, "sdk-properties.xml"));

                using (Stream fileStream = propertiesFile.OpenWrite())
                    using (Stream resourcStream = Assembly.GetExecutingAssembly()
                                                  .GetManifestResourceStream("Test.PlcNext.Deployment.StandardSdkProperties.xml"))
                        using (StreamReader reader = new StreamReader(resourcStream))
                            using (StreamWriter writer = new StreamWriter(fileStream))
                            {
                                fileStream.SetLength(0);
                                string line;
                                while ((line = reader.ReadLine()) != null)
                                {
                                    if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                                    {
                                        line = line.Replace(@"C:\MySdk", "/usr/bin/MySdk");
                                    }
                                    writer.WriteLine(line);
                                }
                            }
            }

            void SetupLibraryBuilder()
            {
                //HACK - implicit creates file
                FileSystem.GetFile(Path.Combine(AssemblyDirectory, "bin", "LibraryBuilder.Core"));
                VirtualFile fileNamesFile = FileSystem.GetFile(Path.Combine(AssemblyDirectory, "file-names.xml"));

                using (Stream fileStream = fileNamesFile.OpenWrite())
                    using (Stream resourcStream = Assembly.GetExecutingAssembly()
                                                  .GetManifestResourceStream("Test.PlcNext.Deployment.StandardFileNames.xml"))
                    {
                        resourcStream.CopyTo(fileStream);
                    }
            }
        }
        private string GenerateCommandOptions(ProjectEntity project, Dictionary <Entity, VirtualFile> projectLibraries, string projectName)
        {
            FileEntity       projectFileEntity  = FileEntity.Decorate(project);
            VirtualFile      commandOptions     = projectFileEntity.TempDirectory.File("CommandOptions.txt");
            CommandEntity    commandOrigin      = CommandEntity.Decorate(project.Origin);
            VirtualDirectory outputRoot         = fileSystem.GetDirectory(commandOrigin.Output, project.Path, false);
            List <string>    processedMetaFiles = new List <string>();

            executionContext.Observable.OnNext(new Change(() => { }, $"Create command options file {commandOptions.FullName}"));

            using (Stream stream = commandOptions.OpenWrite())
                using (StreamWriter writer = new StreamWriter(stream))
                {
                    writer.WriteLine($"{Constants.OutputOption} \"{MakeRelative(Path.Combine(outputRoot.FullName, projectName))}.{Constants.EngineeringLibraryExtension}\"");
                    writer.WriteLine($"{Constants.GuidOption} {project.Id:D}");

                    RenameAndWriteLibraryFile(writer);
                    WriteMetadata(writer);
                    AddAdditionalFiles(writer);
                }

            return(commandOptions.FullName);

            void RenameAndWriteLibraryFile(StreamWriter writer)
            {
                foreach (TargetEntity target in projectLibraries.Keys.Select(TargetEntity.Decorate))
                {
                    VirtualFile renamedLibrary = projectFileEntity
                                                 .TempDirectory.Directory(target.FullName.Replace(",", "_"))
                                                 .File("lib" + projectName +
                                                       Path.GetExtension(projectLibraries[target.Base].Name));
                    executionContext.Observable.OnNext(new Change(() => { }, $"Rename library file to {renamedLibrary.FullName}"));
                    using (Stream source = projectLibraries[target.Base].OpenRead(true))
                        using (Stream destination = renamedLibrary.OpenWrite())
                        {
                            source.CopyTo(destination);
                        }
                    writer.WriteLine(string.Format(CultureInfo.InvariantCulture,
                                                   Constants.PlcnextNativeLibraryOptionPattern,
                                                   renamedLibrary.Parent.FullName,
                                                   target.Name,
                                                   target.EngineerVersion,
                                                   guidFactory.Create().ToString("D", CultureInfo.InvariantCulture),
                                                   target.ShortFullName.Replace(",", "_")));
                }
            }

            void AddAdditionalFiles(StreamWriter writer)
            {
                foreach (Entity target in projectLibraries.Keys)
                {
                    VirtualDirectory          deployDirectory = DeployEntity.Decorate(target).DeployDirectory;
                    IEnumerable <VirtualFile> files           = deployDirectory
                                                                .Files(searchRecursive: true).Except(projectLibraries.Values)
                                                                .Where(f => !processedMetaFiles.Contains(f.GetRelativePath(deployDirectory)));
                    foreach (VirtualFile file in files)
                    {
                        writer.WriteLine(string.Format(CultureInfo.InvariantCulture,
                                                       "/file \":{0}:{1}\"",
                                                       file.GetRelativeOrAbsolutePath(projectFileEntity.Directory),
                                                       TargetEntity.Decorate(target).ShortFullName.Replace(",", "_")));
                    }
                }
            }

            void WriteMetadata(StreamWriter writer)
            {
                VirtualDirectory           deployDirectory   = DeployEntity.Decorate(projectLibraries.Keys.First()).DeployDirectory;
                HashSet <VirtualDirectory> createDirectories = new HashSet <VirtualDirectory>();

                foreach (VirtualFile metaFile in deployDirectory.Files(searchRecursive: true))
                {
                    string destinationPath;
                    string fileType;
                    switch (Path.GetExtension(metaFile.Name)?.ToUpperInvariant() ?? string.Empty)
                    {
                    case ".LIBMETA":
                        destinationPath = string.Empty;
                        fileType        = Constants.LibmetaFileType;
                        break;

                    case ".TYPEMETA":
                        destinationPath = string.Empty;
                        fileType        = Constants.TypemetaFileType;
                        break;

                    case ".COMPMETA":
                        CreateComponentDirectory(metaFile.Parent);
                        destinationPath = metaFile.Parent.Name;
                        fileType        = Constants.CompmetaFileType;
                        break;

                    case ".PROGMETA":
                        CreateProgramDirectory(metaFile.Parent);
                        destinationPath = $"{metaFile.Parent.Parent.Name}/{metaFile.Parent.Name}";
                        fileType        = Constants.ProgmetaFileType;
                        break;

                    default:
                        //do nothing all other files are not interesting
                        continue;
                    }
                    writer.WriteLine(string.Format(CultureInfo.InvariantCulture,
                                                   Constants.FileOptionPattern,
                                                   fileType,
                                                   MakeRelative(metaFile.FullName),
                                                   guidFactory.Create().ToString("D", CultureInfo.InvariantCulture),
                                                   destinationPath));
                    processedMetaFiles.Add(metaFile.GetRelativePath(deployDirectory));
                }

                void CreateComponentDirectory(VirtualDirectory componentDirectory)
                {
                    if (createDirectories.Contains(componentDirectory))
                    {
                        return;
                    }

                    writer.WriteLine(string.Format(CultureInfo.InvariantCulture,
                                                   Constants.DirectoryOptionPattern,
                                                   $"Logical Elements/{componentDirectory.Name}",
                                                   Constants.ComponentFolderType,
                                                   guidFactory.Create().ToString("D", CultureInfo.InvariantCulture)));
                    createDirectories.Add(componentDirectory);
                }

                void CreateProgramDirectory(VirtualDirectory programDirectory)
                {
                    if (createDirectories.Contains(programDirectory))
                    {
                        return;
                    }

                    CreateComponentDirectory(programDirectory.Parent);
                    writer.WriteLine(string.Format(CultureInfo.InvariantCulture,
                                                   Constants.DirectoryOptionPattern,
                                                   $"Logical Elements/{programDirectory.Parent.Name}/{programDirectory.Name}",
                                                   Constants.ProgramFolderType,
                                                   guidFactory.Create().ToString("D", CultureInfo.InvariantCulture)));
                    createDirectories.Add(programDirectory);
                }
            }

            string MakeRelative(string path)
            {
                return(path.GetRelativePath(projectFileEntity.Directory.FullName));
            }
        }
Example #12
0
        public (bool, VirtualDirectory) EnsureConfigured(BuildInformation buildInformation, bool showWarningsToUser, ChangeObservable observable = null,
                                                         bool throwOnError = false, bool showMessagesToUser = true)
        {
            if (!CmakeFileExists())
            {
                GenerateCmakeFile();
            }

            VirtualDirectory cmakeFolder = CreateCmakeFolder();

            LoadCmakeOptions();

            bool success = ConfigureCMake();

            return(success, cmakeFolder);

            void GenerateCmakeFile()
            {
                //It is important to get the name first before the cmake file is created in the next line.
                string      name      = buildInformation.RootEntity.Name;
                VirtualFile cMakeFile = buildInformation.RootFileEntity.Directory.File(Constants.CMakeFileName);

                //TODO Generate cmakefile with template system
                //TODO Set src folders in cmake file (consider foreign project structure)
                CMakeFileGenerator.WriteCMakeFile(cMakeFile, name);
                observable?.OnNext(new Change(() => { /*Do not delete, because user need to make changes perhaps*/ }, $"Generated cmake file {cMakeFile.FullName}"));
            }

            bool CmakeFileExists()
            {
                return(buildInformation.RootFileEntity.Directory.FileExists(Constants.CMakeFileName));
            }

            VirtualDirectory CreateCmakeFolder()
            {
                VirtualDirectory result = buildInformation.BuildEntity.BuildSystemDirectory;

                if (buildInformation.Configure && !buildInformation.NoConfigure)
                {
                    result.Clear();
                    observable?.OnNext(new Change(() => result.UnClear(), $"Cleared cmake directory."));
                }
                return(result);
            }

            string GetRealBuildType()
            {
                string buildType = !string.IsNullOrEmpty(buildInformation.BuildType)
                                       ? buildInformation.BuildType
                                       : Constants.ReleaseFolderName;

                return(buildType);
            }

            void LoadCmakeOptions()
            {
                if (!string.IsNullOrEmpty(buildInformation.BuildProperties))
                {
                    return;
                }

                if (buildInformation.RootFileEntity?.Directory?.FileExists(Constants.CMakeCommandArgsFileName) == true)
                {
                    string      cmakeArgs       = string.Empty;
                    VirtualFile commandArgsFile = buildInformation.RootFileEntity.Directory.File(Constants.CMakeCommandArgsFileName);
                    using (Stream fileStream = commandArgsFile.OpenRead())
                        using (StreamReader streamReader = new StreamReader(fileStream))
                        {
                            while (!streamReader.EndOfStream)
                            {
                                string line = streamReader.ReadLine();
                                if (string.IsNullOrEmpty(line))
                                {
                                    continue;
                                }
                                cmakeArgs = string.Join(" ", cmakeArgs, line);
                            }
                        }
                    buildInformation.BuildProperties          = cmakeArgs;
                    buildInformation.BuildPropertiesSetByFile = true;
                }
            }

            bool ConfigureCMake()
            {
                executionContext.WriteInformation("Checking if CMake needs to be reconfigured...", showMessagesToUser);
                if ((!cmakeFolder.FileExists("CMakeCache.txt") ||
                     buildInformation.Configure ||
                     !CacheHasValidTimestamp() ||
                     !IsCorrectlyConfigured() ||
                     OutputOptionDiffersFromStagingPrefix()) &&
                    !buildInformation.NoConfigure)
                {
                    string cmakeCommand = GenerateCmakeCommand(buildInformation.Target.Name,
                                                               buildInformation.Target.LongVersion);

                    executionContext.WriteInformation("Configuring CMake...", showMessagesToUser);

                    bool result = CallCmake(cmakeFolder, cmakeCommand, showMessagesToUser, throwOnError, showWarningsToUser);

                    AddTimestamp();

                    return(result);
                }

                if (!string.IsNullOrEmpty(buildInformation.BuildProperties) && !buildInformation.BuildPropertiesSetByFile)
                {
                    executionContext.WriteWarning($"The specified build options will not be used, " +
                                                  $"because no reconfiguration is necessary. " +
                                                  $"To force a reconfiguration please use the '--configure' command option.",
                                                  showMessagesToUser);
                }

                return(true);

                string GenerateCmakeCommand(string target, string version)
                {
                    List <string> commandParts = new List <string>();
                    string        sdkRoot      = buildInformation.SdkInformation.Root.FullName.Replace("\\", "/");

                    if (!buildInformation.BuildProperties.Contains("-DCMAKE_TOOLCHAIN_FILE="))
                    {
                        commandParts.Add(ToolchainFileOption.Replace("%SDK_ROOT%", sdkRoot));
                    }
                    if (!buildInformation.BuildProperties.Contains("-DARP_TOOLCHAIN_ROOT="))
                    {
                        commandParts.Add(ToolchainRootOption.Replace("%SDK_ROOT%", sdkRoot));
                    }
                    if (!buildInformation.BuildProperties.Contains("-DCMAKE_BUILD_TYPE="))
                    {
                        commandParts.Add(BuildTypeOption.Replace("%BUILD_TYPE%", GetRealBuildType()));
                    }
                    if (!buildInformation.BuildProperties.Contains("-DARP_DEVICE="))
                    {
                        commandParts.Add(DeviceOption.Replace("%TARGET%", $"\"{target}\""));
                    }
                    if (!buildInformation.BuildProperties.Contains("-DARP_DEVICE_VERSION="))
                    {
                        commandParts.Add(DeviceVersionOption.Replace("%VERSION%", $"\"{version}\""));
                    }
                    if (!buildInformation.BuildProperties.Contains("-DCMAKE_STAGING_PREFIX="))
                    {
                        commandParts.Add(StagingPrefixOption.Replace("%STAGING_PREFIX%", GenerateStagingPrefixForTarget()));
                    }
                    if (!buildInformation.BuildProperties.Contains("-DCMAKE_PREFIX_PATH=") &&
                        IsIncludePathAvailable(out string includePath))
                    {
                        commandParts.Add(PrefixPathOption.Replace("%PREFIX_PATH%", includePath));
                    }
                    if (!buildInformation.BuildProperties.Contains("-G "))
                    {
                        commandParts.Add(GeneratorOption);
                        if (buildInformation.SdkInformation.MakeFile != null && !buildInformation.BuildProperties.Contains("-DCMAKE_MAKE_PROGRAM "))
                        {
                            commandParts.Add(MakeFileOption.Replace("%MAKE_EXE%", $"\"{buildInformation.SdkInformation.MakeFile.FullName.Replace("\\", "/")}\""));
                        }
                    }
                    if (!string.IsNullOrEmpty(buildInformation.BuildProperties))
                    {
                        commandParts.Add(buildInformation.BuildProperties);
                    }
                    commandParts.Add($"\"{buildInformation.RootFileEntity.Directory.FullName.Replace("\\", "/")}\"");
                    return(string.Join(" ", commandParts));

                    string GenerateStagingPrefixForTarget()
                    {
                        string basePath = buildInformation.RootFileEntity.Directory.FullName;

                        return(buildInformation.Output != null
                                   ? OutputOptionFullPath()
                                   : Path.Combine(basePath, Constants.LibraryFolderName)
                               .Replace(Path.DirectorySeparatorChar, '/'));
                    }

                    bool IsIncludePathAvailable(out string path)
                    {
                        path = null;
                        if (!buildInformation.RootFileEntity.Directory.DirectoryExists("external"))
                        {
                            return(false);
                        }

                        Dictionary <Version, VirtualDirectory> versions = new Dictionary <Version, VirtualDirectory>();
                        VirtualDirectory externalDirectory = buildInformation.RootFileEntity.Directory.Directory("external");

                        foreach (VirtualDirectory directory in externalDirectory.Directories)
                        {
                            Match patternMatch = IncludeDirectoryPattern.Match(directory.Name);
                            if (!patternMatch.Success ||
                                !Version.TryParse(patternMatch.Groups["version"].Value, out Version includeVersion) ||
                                target != patternMatch.Groups["name"].Value)
                            {
                                continue;
                            }

                            versions.Add(includeVersion, directory);
                        }

                        Version actualVersion = Version.Parse(buildInformation.Target.Version);
                        Version bestMatch     = versions.Keys.Where(v => v <= actualVersion)
                                                .OrderByDescending(v => v)
                                                .FirstOrDefault();

                        if (bestMatch != null)
                        {
                            VirtualDirectory directory = versions[bestMatch];
                            if (directory.DirectoryExists(buildInformation.BuildType))
                            {
                                path = directory.Directory(buildInformation.BuildType).FullName;
                            }
                            else if (directory.DirectoryExists(Constants.ReleaseFolderName))
                            {
                                path = directory.Directory(Constants.ReleaseFolderName).FullName;
                            }
                            else
                            {
                                path = directory.FullName;
                            }
                        }
                        else
                        {
                            path = externalDirectory.FullName;
                        }

                        return(true);
                    }
                }

                string OutputOptionFullPath()
                {
                    return(fileSystem.GetDirectory(buildInformation.Output,
                                                   buildInformation.RootFileEntity.Directory.FullName)
                           .FullName.Replace(Path.DirectorySeparatorChar, '/'));
                }

                bool OutputOptionDiffersFromStagingPrefix()
                {
                    return(buildInformation.Output != null &&
                           !buildInformation.BuildEntity.BuildSystem.InstallationPaths.Any(p => p.StartsWith(OutputOptionFullPath(), StringComparison.Ordinal)));
                }

                bool IsCorrectlyConfigured()
                {
                    try
                    {
                        return(buildInformation.BuildEntity.HasBuildSystem &&
                               buildInformation.BuildEntity.BuildSystem != null);
                    }
                    catch (Exception e)
                    {
                        if (!IsTimeout(e))
                        {
                            executionContext.WriteVerbose($"The project is not correctly configured:{Environment.NewLine}{e}");
                            return(false);
                        }
                    }

                    return(true); //this is a timeout so we dont know if it is correctly configured

                    bool IsTimeout(Exception exception)
                    {
                        return(exception is TimeoutException ||
                               exception is AggregateException aggregate &&
                               aggregate.InnerExceptions.Any(e => e is TimeoutException));
                    }
                }

                void AddTimestamp()
                {
                    if (buildInformation.RootFileEntity?.Directory?.FileExists(Constants.CMakeCommandArgsFileName) == true)
                    {
                        VirtualFile commandArgsFile = buildInformation.RootFileEntity.Directory.File(Constants.CMakeCommandArgsFileName);
                        VirtualFile timestampFile   = cmakeFolder.File(Constants.CMakeTimestampFileName);

                        JObject timestamp = new JObject
                        {
                            new JProperty("FlagsWriteTime", new JValue(commandArgsFile.LastWriteTime))
                        };

                        using (Stream fileStream = timestampFile.OpenWrite())
                            using (StreamWriter streamWriter = new StreamWriter(fileStream, Encoding.UTF8))
                                using (JsonWriter jsonWriter = new JsonTextWriter(streamWriter))
                                {
                                    fileStream.SetLength(0);
                                    jsonWriter.Formatting = Formatting.Indented;
                                    timestamp.WriteTo(jsonWriter);
                                }
                    }
                    else
                    {
                        if (cmakeFolder.FileExists(Constants.CMakeTimestampFileName))
                        {
                            cmakeFolder.File(Constants.CMakeTimestampFileName).Delete();
                        }
                    }
                }

                bool CacheHasValidTimestamp()
                {
                    if (buildInformation.RootFileEntity?.Directory?.FileExists(Constants.CMakeCommandArgsFileName) == true)
                    {
                        DateTime commandArgsLastWriteTime = buildInformation.RootFileEntity
                                                            .Directory
                                                            .File(Constants.CMakeCommandArgsFileName)
                                                            .LastWriteTime;
                        if (cmakeFolder.FileExists(Constants.CMakeTimestampFileName))
                        {
                            VirtualFile timestampFile = cmakeFolder.File(Constants.CMakeTimestampFileName);
                            try
                            {
                                using (Stream fileStream = timestampFile.OpenRead())
                                    using (StreamReader reader = new StreamReader(fileStream))
                                        using (JsonReader jsonReader = new JsonTextReader(reader))
                                        {
                                            JObject fileContent = JObject.Load(jsonReader);
                                            if (fileContent.ContainsKey("FlagsWriteTime") &&
                                                fileContent["FlagsWriteTime"].Type == JTokenType.Date)
                                            {
                                                DateTime savedTimeStamp = fileContent["FlagsWriteTime"].Value <DateTime>();
                                                if (savedTimeStamp.CompareTo(commandArgsLastWriteTime) == 0)
                                                {
                                                    return(true);
                                                }
                                            }
                                        }
                            }
                            catch (JsonReaderException)
                            {
                                return(false);
                            }
                        }
                    }
                    else
                    {
                        if (!cmakeFolder.FileExists(Constants.CMakeTimestampFileName))
                        {
                            return(true);
                        }
                    }
                    return(false);
                }
            }
        }