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); } }
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); }
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); } } }
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); } } }
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); } } } } } }
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(); } } }
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)); } }
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); } } }