From 5d7e173536600bedc47360d078f9ceefabdba651 Mon Sep 17 00:00:00 2001 From: "Chaojie Yang (Pactera Technologies Inc)" Date: Fri, 24 Apr 2020 06:14:05 -0700 Subject: [PATCH 1/4] Delete task code that has been moved to the shared framework SDK in Arcade --- .../installer.tasks/BuildFPMToolPreReqs.cs | 352 ------------------ .../CopyNupkgAndChangeVersion.cs | 149 -------- .../CreateFrameworkListFile.cs | 211 ----------- .../tasks/installer.tasks/ExecWithRetries.cs | 116 ------ .../GenerateDebRepoUploadJsonFile.cs | 44 --- .../GenerateJsonObjectString.cs | 145 -------- .../ProcessSharedFrameworkDeps.cs | 83 ----- .../installer.tasks/RuntimeGraphManager.cs | 65 ---- .../tasks/installer.tasks/RuntimeReference.cs | 63 ---- .../installer.tasks/StabilizeWixFileId.cs | 107 ------ .../ZipFileExtractToDirectory.cs | 86 ----- .../installer.tasks/ZipFileGetEntries.cs | 50 --- 12 files changed, 1471 deletions(-) delete mode 100644 tools-local/tasks/installer.tasks/BuildFPMToolPreReqs.cs delete mode 100644 tools-local/tasks/installer.tasks/CopyNupkgAndChangeVersion.cs delete mode 100644 tools-local/tasks/installer.tasks/CreateFrameworkListFile.cs delete mode 100644 tools-local/tasks/installer.tasks/ExecWithRetries.cs delete mode 100644 tools-local/tasks/installer.tasks/GenerateDebRepoUploadJsonFile.cs delete mode 100644 tools-local/tasks/installer.tasks/GenerateJsonObjectString.cs delete mode 100644 tools-local/tasks/installer.tasks/ProcessSharedFrameworkDeps.cs delete mode 100644 tools-local/tasks/installer.tasks/RuntimeGraphManager.cs delete mode 100644 tools-local/tasks/installer.tasks/RuntimeReference.cs delete mode 100644 tools-local/tasks/installer.tasks/StabilizeWixFileId.cs delete mode 100644 tools-local/tasks/installer.tasks/ZipFileExtractToDirectory.cs delete mode 100644 tools-local/tasks/installer.tasks/ZipFileGetEntries.cs diff --git a/tools-local/tasks/installer.tasks/BuildFPMToolPreReqs.cs b/tools-local/tasks/installer.tasks/BuildFPMToolPreReqs.cs deleted file mode 100644 index 6ea7b65710640c..00000000000000 --- a/tools-local/tasks/installer.tasks/BuildFPMToolPreReqs.cs +++ /dev/null @@ -1,352 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Build.Framework; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace Microsoft.DotNet.Build.Tasks -{ - /// - /// This task prepares the command line parameters for running a RPM build using FPM tool and also updates the copyright and changelog file tokens. - /// If parses various values from the config json by first reading it into a model and then builds the required string for parameters and passes it back. - /// - /// - public class BuildFPMToolPreReqs : BuildTask - { - [Required] - public string InputDir { get; set; } - [Required] - public string OutputDir { get; set; } - [Required] - public string PackageVersion { get; set; } - [Required] - public string ConfigJsonFile { get; set; } - [Output] - public string FPMParameters { get; set; } - - public override bool Execute() - { - try - { - if (!File.Exists(ConfigJsonFile)) - { - throw new FileNotFoundException($"Expected file {ConfigJsonFile} was not found."); - } - - // Open the Config Json and read the values into the model - TextReader projectFileReader = File.OpenText(ConfigJsonFile); - if (projectFileReader != null) - { - string jsonFileText = projectFileReader.ReadToEnd(); - ConfigJson configJson = JsonConvert.DeserializeObject(jsonFileText); - - // Update the Changelog and Copyright files by replacing tokens with values from config json - UpdateChangelog(configJson, PackageVersion); - UpdateCopyRight(configJson); - - // Build the full list of parameters - FPMParameters = BuildCmdParameters(configJson, PackageVersion); - Log.LogMessage(MessageImportance.Normal, "Generated RPM paramters: " + FPMParameters); - } - else - { - throw new IOException($"Could not open the file {ConfigJsonFile} for reading."); - } - } - catch (Exception e) - { - Log.LogErrorFromException(e, true); - } - - return !Log.HasLoggedErrors; - } - - // Update the tokens in the changelog file from the config Json - private void UpdateChangelog(ConfigJson configJson, string package_version) - { - try - { - string changelogFile = Path.Combine(InputDir, "templates", "changelog"); - if (!File.Exists(changelogFile)) - { - throw new FileNotFoundException($"Expected file {changelogFile} was not found."); - } - string str = File.ReadAllText(changelogFile); - str = str.Replace("{PACKAGE_NAME}", configJson.Package_Name); - str = str.Replace("{PACKAGE_VERSION}", package_version); - str = str.Replace("{PACKAGE_REVISION}", configJson.Release.Package_Revision); - str = str.Replace("{URGENCY}", configJson.Release.Urgency); - str = str.Replace("{CHANGELOG_MESSAGE}", configJson.Release.Changelog_Message); - str = str.Replace("{MAINTAINER_NAME}", configJson.Maintainer_Name); - str = str.Replace("{MAINTAINER_EMAIL}", configJson.Maintainer_Email); - // The date format needs to be like Wed May 17 2017 - str = str.Replace("{DATE}", DateTime.UtcNow.ToString("ddd MMM dd yyyy")); - File.WriteAllText(changelogFile, str); - } - catch (Exception e) - { - Log.LogError("Exception while updating the changelog file: " + e.Message); - } - } - - private void UpdateCopyRight(ConfigJson configJson) - { - try - { - // Update the tokens in the copyright file from the config Json - string copyrightFile = Path.Combine(InputDir, "templates", "copyright"); - if (!File.Exists(copyrightFile)) - { - throw new FileNotFoundException($"Expected file {copyrightFile} was not found."); - } - string str = File.ReadAllText(copyrightFile); - str = str.Replace("{COPYRIGHT_TEXT}", configJson.CopyRight); - str = str.Replace("{LICENSE_NAME}", configJson.License.Type); - str = str.Replace("{LICENSE_NAME}", configJson.License.Type); - str = str.Replace("{LICENSE_TEXT}", configJson.License.Full_Text); - File.WriteAllText(copyrightFile, str); - } - catch (Exception e) - { - Log.LogError("Exception while updating the copyright file: " + e.Message); - } - } - - private string BuildCmdParameters(ConfigJson configJson, string package_version) - { - // Parameter list that needs to be passed to FPM tool: - // -s : is the input source type(dir) --Static - // -t : is the type of package(rpm) --Static - // -n : is for the name of the package --JSON - // -v : is the version to give to the package --ARG - // -a : architecture --JSON - // -d : is for all dependent packages. This can be used multiple times to specify the dependencies of the package. --JSON - // --rpm-os : the operating system to target this rpm --Static - // --rpm-changelog : the changelog from FILEPATH contents --ARG - // --rpm-summary : it is the RPM summary that shows in the Title --JSON - // --description : it is the description for the package --JSON - // -p : The actual package name (with path) for your package. --ARG+JSON - // --conflicts : Other packages/versions this package conflicts with provided as CSV --JSON - // --directories : Recursively add directories as being owned by the package. --JSON - // --after-install : FILEPATH to the script to be run after install of the package --JSON - // --after-remove : FILEPATH to the script to be run after package removal --JSON - // --license : the licensing name for the package. This will include the license type in the meta-data for the package, but will not include the associated license file within the package itself. --JSON - // --iteration : the iteration to give to the package. This comes from the package_revision --JSON - // --url : url for this package. --JSON - // --verbose : Set verbose output for FPM tool --Static - // : Add all the folder mappings for packge_root, docs, man pages --Static - - var parameters = new List(); - parameters.Add("-s dir"); - parameters.Add("-t rpm"); - parameters.Add(string.Concat("-n ", configJson.Package_Name)); - parameters.Add(string.Concat("-v ", package_version)); - parameters.Add(string.Concat("-a ", configJson.Control.Architecture)); - - // Build the list of dependencies as -d -d - if (configJson.Rpm_Dependencies != null) - { - IEnumerable dependencies; - - switch (configJson.Rpm_Dependencies) - { - case JArray dependencyArray: - dependencies = dependencyArray.ToObject(); - break; - - case JObject dependencyDictionary: - dependencies = dependencyDictionary - .ToObject>() - .Select(pair => new RpmDependency - { - Package_Name = pair.Key, - Package_Version = pair.Value - }); - break; - - default: - throw new ArgumentException( - "Expected 'rpm_dependencies' to be JArray or JObject, but found " + - configJson.Rpm_Dependencies.Type); - } - - foreach (RpmDependency rpmdep in dependencies) - { - string dependency = ""; - if (rpmdep.Package_Name != "") - { - // If no version is specified then the dependency is just the package without >= check - if (rpmdep.Package_Version == "") - { - dependency = rpmdep.Package_Name; - } - else - { - dependency = string.Concat(rpmdep.Package_Name, " >= ", rpmdep.Package_Version); - } - } - if (dependency != "") - { - parameters.Add(string.Concat("-d ", EscapeArg(dependency))); - } - } - } - - // Build the list of owned directories - if (configJson.Directories != null) - { - foreach (string dir in configJson.Directories) - { - if (dir != "") - { - parameters.Add(string.Concat("--directories ", EscapeArg(dir))); - } - } - } - - parameters.Add("--rpm-os linux"); - parameters.Add(string.Concat("--rpm-changelog ", EscapeArg(Path.Combine(InputDir, "templates", "changelog")))); // Changelog File - parameters.Add(string.Concat("--rpm-summary ", EscapeArg(configJson.Short_Description))); - parameters.Add(string.Concat("--description ", EscapeArg(configJson.Long_Description))); - parameters.Add(string.Concat("--maintainer ", EscapeArg(configJson.Maintainer_Name + " <" + configJson.Maintainer_Email + ">"))); - parameters.Add(string.Concat("--vendor ", EscapeArg(configJson.Vendor))); - parameters.Add(string.Concat("-p ", Path.Combine(OutputDir, configJson.Package_Name + ".rpm"))); - if (configJson.Package_Conflicts != null) parameters.Add(string.Concat("--conflicts ", EscapeArg(string.Join(",", configJson.Package_Conflicts)))); - if (configJson.After_Install_Source != null) parameters.Add(string.Concat("--after-install ", Path.Combine(InputDir, EscapeArg(configJson.After_Install_Source)))); - if (configJson.After_Remove_Source != null) parameters.Add(string.Concat("--after-remove ", Path.Combine(InputDir, EscapeArg(configJson.After_Remove_Source)))); - parameters.Add(string.Concat("--license ", EscapeArg(configJson.License.Type))); - parameters.Add(string.Concat("--iteration ", configJson.Release.Package_Revision)); - parameters.Add(string.Concat("--url ", "\"", EscapeArg(configJson.Homepage), "\"")); - parameters.Add("--verbose"); - - // Map all the payload directories as they need to install on the system - if (configJson.Install_Root != null) parameters.Add(string.Concat(Path.Combine(InputDir, "package_root/="), configJson.Install_Root)); // Package Files - if (configJson.Install_Man != null) parameters.Add(string.Concat(Path.Combine(InputDir, "docs", "host/="), configJson.Install_Man)); // Man Pages - if (configJson.Install_Doc != null) parameters.Add(string.Concat(Path.Combine(InputDir, "templates", "copyright="), configJson.Install_Doc)); // CopyRight File - - return string.Join(" ", parameters); - } - - private string EscapeArg(string arg) - { - var sb = new StringBuilder(); - - bool quoted = ShouldSurroundWithQuotes(arg); - if (quoted) sb.Append("\""); - - for (int i = 0; i < arg.Length; ++i) - { - var backslashCount = 0; - - // Consume All Backslashes - while (i < arg.Length && arg[i] == '\\') - { - backslashCount++; - i++; - } - - // Escape any backslashes at the end of the arg - // This ensures the outside quote is interpreted as - // an argument delimiter - if (i == arg.Length) - { - sb.Append('\\', 2 * backslashCount); - } - - // Escape any preceding backslashes and the quote - else if (arg[i] == '"') - { - sb.Append('\\', (2 * backslashCount) + 1); - sb.Append('"'); - } - - // Output any consumed backslashes and the character - else - { - sb.Append('\\', backslashCount); - sb.Append(arg[i]); - } - } - - if (quoted) sb.Append("\""); - - return sb.ToString(); - } - private bool ShouldSurroundWithQuotes(string argument) - { - // Don't quote already quoted strings - if (argument.StartsWith("\"", StringComparison.Ordinal) && - argument.EndsWith("\"", StringComparison.Ordinal)) - { - return false; - } - - // Only quote if whitespace exists in the string - if (argument.Contains(" ") || argument.Contains("\t") || argument.Contains("\n")) - { - return true; - } - return false; - } - - /// - /// Model classes for reading and storing the JSON. - /// - private class ConfigJson - { - public string Maintainer_Name { get; set; } - public string Maintainer_Email { get; set; } - public string Vendor { get; set; } - public string Package_Name { get; set; } - public string Install_Root { get; set; } - public string Install_Doc { get; set; } - public string Install_Man { get; set; } - public string Short_Description { get; set; } - public string Long_Description { get; set; } - public string Homepage { get; set; } - public string CopyRight { get; set; } - public Release Release { get; set; } - public Control Control { get; set; } - public License License { get; set; } - public JContainer Rpm_Dependencies { get; set; } - public List Package_Conflicts { get; set; } - public List Directories { get; set; } - public string After_Install_Source { get; set; } - public string After_Remove_Source { get; set; } - } - - private class Release - { - public string Package_Version { get; set; } - public string Package_Revision { get; set; } - public string Urgency { get; set; } - public string Changelog_Message { get; set; } - } - - private class Control - { - public string Priority { get; set; } - public string Section { get; set; } - public string Architecture { get; set; } - } - - private class License - { - public string Type { get; set; } - public string Full_Text { get; set; } - } - - private class RpmDependency - { - public string Package_Name { get; set; } - public string Package_Version { get; set; } - } - } -} diff --git a/tools-local/tasks/installer.tasks/CopyNupkgAndChangeVersion.cs b/tools-local/tasks/installer.tasks/CopyNupkgAndChangeVersion.cs deleted file mode 100644 index 35f7a42121402c..00000000000000 --- a/tools-local/tasks/installer.tasks/CopyNupkgAndChangeVersion.cs +++ /dev/null @@ -1,149 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Build.Framework; -using Newtonsoft.Json.Linq; -using NuGet.Versioning; -using System; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Xml.Linq; - -namespace Microsoft.DotNet.Build.Tasks -{ - public class CopyNupkgAndChangeVersion : BuildTask - { - [Required] - public string SourceFile { get; set; } - - [Required] - public string TargetFile { get; set; } - - [Required] - public string OriginalVersion { get; set; } - - [Required] - public string TargetVersion { get; set; } - - public string[] DependencyPackageIdsToChange { get; set; } - - public override bool Execute() - { - Directory.CreateDirectory(Path.GetDirectoryName(TargetFile)); - File.Copy(SourceFile, TargetFile, true); - - using (ZipArchive zip = ZipFile.Open(TargetFile, ZipArchiveMode.Update)) - { - RewriteNuspec(zip); - RewriteRuntimeJson(zip); - } - - return !Log.HasLoggedErrors; - } - - private void RewriteNuspec(ZipArchive zip) - { - foreach (var nuspec in zip.Entries.Where(e => e.FullName.EndsWith(".nuspec"))) - { - Rewrite(nuspec, s => - { - XDocument content = XDocument.Parse(s); - - RewriteNuspecPackageVersion(content); - RewriteNuspecDependencyVersions(content); - - return content.ToString(); - }); - } - } - - private void RewriteRuntimeJson(ZipArchive zip) - { - foreach (var runtimeJson in zip.Entries.Where(e => e.FullName == "runtime.json")) - { - Rewrite(runtimeJson, s => - { - JObject content = JObject.Parse(s); - - RewriteRuntimeJsonVersions(content); - - return content.ToString(); - }); - } - } - - private void RewriteNuspecPackageVersion(XDocument content) - { - XElement versionElement = content - .Element(CreateQualifiedName(content, "package")) - .Element(CreateQualifiedName(content, "metadata")) - .Element(CreateQualifiedName(content, "version")); - - if (versionElement.Value != OriginalVersion) - { - Log.LogError( - $"Original version is '{versionElement.Value}', " + - $"expected '{OriginalVersion}'"); - } - - versionElement.Value = TargetVersion; - } - - private void RewriteNuspecDependencyVersions(XDocument content) - { - foreach (var dependency in content - .Descendants(CreateQualifiedName(content, "dependency")) - .Where(x => - x.Attribute("version").Value == OriginalVersion && - DependencyPackageIdsToChange?.Contains(x.Attribute("id").Value) == true)) - { - dependency.Value = TargetVersion; - } - } - - private void RewriteRuntimeJsonVersions(JObject content) - { - var versionProperties = content - .Descendants() - .OfType() - .Where(p => - p.Value is JValue v && - v.Type == JTokenType.String); - - foreach (var p in versionProperties) - { - var range = VersionRange.Parse(p.Value.Value()); - - if (range.MinVersion.OriginalVersion == OriginalVersion) - { - var newRange = new VersionRange( - NuGetVersion.Parse(TargetVersion), - range.Float); - - p.Value = newRange.ToString(); - } - } - } - - private static XName CreateQualifiedName(XDocument doc, string name) - { - return doc.Root.GetDefaultNamespace().GetName(name); - } - - private static void Rewrite(ZipArchiveEntry entry, Func rewrite) - { - using (var stream = entry.Open()) - using (var reader = new StreamReader(stream)) - using (var writer = new StreamWriter(stream)) - { - var content = rewrite(reader.ReadToEnd()); - - stream.Position = 0; - stream.SetLength(0); - writer.Write(content); - } - } - } -} diff --git a/tools-local/tasks/installer.tasks/CreateFrameworkListFile.cs b/tools-local/tasks/installer.tasks/CreateFrameworkListFile.cs deleted file mode 100644 index 36a3dd59386c08..00000000000000 --- a/tools-local/tasks/installer.tasks/CreateFrameworkListFile.cs +++ /dev/null @@ -1,211 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Build.Framework; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Xml.Linq; - -namespace Microsoft.DotNet.Build.Tasks -{ - public class CreateFrameworkListFile : BuildTask - { - /// - /// Files to extract basic information from and include in the list. - /// - [Required] - public ITaskItem[] Files { get; set; } - - /// - /// A list of assembly names with classification info such as Profile. A - /// Profile="%(Profile)" attribute is included in the framework list for the matching Files - /// item if %(Profile) contains text. - /// - /// If *any* FileClassifications are passed: - /// - /// *Every* file that ends up listed in the framework list must have a matching - /// FileClassification. - /// - /// Additionally, every FileClassification must find exactly one File. - /// - /// This task fails if the conditions aren't met. This ensures the classification doesn't - /// become out of date when the list of files changes. - /// - /// %(Identity): Assembly name (including ".dll"). - /// %(Profile): List of profiles that apply, semicolon-delimited. - /// %(ReferencedByDefault): Empty (default) or "true"/"false". Indicates whether this file - /// should be referenced by default when the SDK uses this framework. - /// - public ITaskItem[] FileClassifications { get; set; } - - [Required] - public string TargetFile { get; set; } - - public string[] TargetFilePrefixes { get; set; } - - /// - /// Extra attributes to place on the root node. - /// - /// %(Identity): Attribute name. - /// %(Value): Attribute value. - /// - public ITaskItem[] RootAttributes { get; set; } - - public override bool Execute() - { - XAttribute[] rootAttributes = RootAttributes - ?.Select(item => new XAttribute(item.ItemSpec, item.GetMetadata("Value"))) - .ToArray(); - - var frameworkManifest = new XElement("FileList", rootAttributes); - - Dictionary fileClassLookup = FileClassifications - ?.ToDictionary( - item => item.ItemSpec, - item => item, - StringComparer.OrdinalIgnoreCase); - - var usedFileClasses = new HashSet(); - - foreach (var f in Files - .Where(IsTargetPathIncluded) - .Select(item => new - { - Item = item, - Filename = Path.GetFileName(item.ItemSpec), - TargetPath = item.GetMetadata("TargetPath"), - AssemblyName = FileUtilities.GetAssemblyName(item.ItemSpec), - FileVersion = FileUtilities.GetFileVersion(item.ItemSpec), - IsNative = item.GetMetadata("IsNative") == "true", - IsSymbolFile = item.GetMetadata("IsSymbolFile") == "true", - IsResourceFile = item.ItemSpec - .EndsWith(".resources.dll", StringComparison.OrdinalIgnoreCase) - }) - .Where(f => - !f.IsSymbolFile && - (f.Filename.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || f.IsNative)) - // Remove duplicate files this task is given. - .GroupBy(f => f.Item.ItemSpec) - .Select(g => g.First()) - // Make order stable between builds. - .OrderBy(f => f.TargetPath, StringComparer.Ordinal) - .ThenBy(f => f.Filename, StringComparer.Ordinal)) - { - string type = "Managed"; - - if (f.IsNative) - { - type = "Native"; - } - else if (f.IsResourceFile) - { - type = "Resources"; - } - - string path = Path.Combine(f.TargetPath, f.Filename).Replace('\\', '/'); - - if (path.StartsWith("runtimes/")) - { - var pathParts = path.Split('/'); - if (pathParts.Length > 1 && pathParts[1].Contains("_")) - { - // This file is a runtime file with a "rid" containing "_". This is assumed - // to mean it's a cross-targeting tool and shouldn't be deployed in a - // self-contained app. Leave it off the list. - continue; - } - } - - var element = new XElement( - "File", - new XAttribute("Type", type), - new XAttribute("Path", path)); - - if (f.IsResourceFile) - { - element.Add( - new XAttribute("Culture", Path.GetFileName(Path.GetDirectoryName(path)))); - } - - if (f.AssemblyName != null) - { - byte[] publicKeyToken = f.AssemblyName.GetPublicKeyToken(); - string publicKeyTokenHex; - - if (publicKeyToken != null) - { - publicKeyTokenHex = BitConverter.ToString(publicKeyToken) - .ToLowerInvariant() - .Replace("-", ""); - } - else - { - Log.LogError($"No public key token found for assembly {f.Item.ItemSpec}"); - publicKeyTokenHex = ""; - } - - element.Add( - new XAttribute("AssemblyName", f.AssemblyName.Name), - new XAttribute("PublicKeyToken", publicKeyTokenHex), - new XAttribute("AssemblyVersion", f.AssemblyName.Version)); - } - else if (!f.IsNative) - { - // This file isn't managed and isn't native. Leave it off the list. - continue; - } - - element.Add(new XAttribute("FileVersion", f.FileVersion)); - - if (fileClassLookup != null) - { - if (fileClassLookup.TryGetValue(f.Filename, out ITaskItem classItem)) - { - string profile = classItem.GetMetadata("Profile"); - - if (!string.IsNullOrEmpty(profile)) - { - element.Add(new XAttribute("Profile", profile)); - } - - string referencedByDefault = classItem.GetMetadata("ReferencedByDefault"); - - if (!string.IsNullOrEmpty(referencedByDefault)) - { - element.Add(new XAttribute("ReferencedByDefault", referencedByDefault)); - } - - usedFileClasses.Add(f.Filename); - } - else - { - Log.LogError($"File matches no classification: {f.Filename}"); - } - } - - frameworkManifest.Add(element); - } - - foreach (var unused in fileClassLookup - ?.Keys.Except(usedFileClasses).OrderBy(p => p) - ?? Enumerable.Empty()) - { - Log.LogError($"Classification matches no files: {unused}"); - } - - Directory.CreateDirectory(Path.GetDirectoryName(TargetFile)); - File.WriteAllText(TargetFile, frameworkManifest.ToString()); - - return !Log.HasLoggedErrors; - } - - private bool IsTargetPathIncluded(ITaskItem item) - { - return TargetFilePrefixes - ?.Any(prefix => item.GetMetadata("TargetPath")?.StartsWith(prefix) == true) ?? true; - } - } -} diff --git a/tools-local/tasks/installer.tasks/ExecWithRetries.cs b/tools-local/tasks/installer.tasks/ExecWithRetries.cs deleted file mode 100644 index 5e57c39f458195..00000000000000 --- a/tools-local/tasks/installer.tasks/ExecWithRetries.cs +++ /dev/null @@ -1,116 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// Initially copied from https://github.com/dotnet/buildtools/blob/6736870b84e06b75e7df32bb84d442db1b2afa10/src/Microsoft.DotNet.Build.Tasks/ExecWithRetries.cs - -using Microsoft.Build.Framework; -using Microsoft.Build.Tasks; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.DotNet.Build.Tasks -{ - /// - /// Run a command and retry if the exit code is not 0. - /// - public class ExecWithRetries : BuildTask - { - [Required] - public string Command { get; set; } - - public string WorkingDirectory { get; set; } - - public bool IgnoreStandardErrorWarningFormat { get; set; } - - public int MaxAttempts { get; set; } = 5; - - /// - /// Base, in seconds, raised to the power of the number of retries so far. - /// - public double RetryDelayBase { get; set; } = 6; - - /// - /// A constant, in seconds, added to (base^retries) to find the delay before retrying. - /// - /// The default is -1 to make the first retry instant, because ((base^0)-1) == 0. - /// - public double RetryDelayConstant { get; set; } = -1; - - /// - /// MSBuild message importance to use when logging stdout messages from the command. Default - /// is "High". - /// - public string StandardOutputImportance { get; set; } - - /// - /// MSBuild message importance to use when logging stderr messages from the command. Default - /// is "High". - /// - public string StandardErrorImportance { get; set; } - - private CancellationTokenSource _cancelTokenSource = new CancellationTokenSource(); - - private Exec _runningExec; - - public void Cancel() - { - _runningExec?.Cancel(); - _cancelTokenSource.Cancel(); - } - - public override bool Execute() - { - for (int i = 0; i < MaxAttempts; i++) - { - _runningExec = new Exec - { - BuildEngine = BuildEngine, - Command = Command, - WorkingDirectory = WorkingDirectory, - IgnoreStandardErrorWarningFormat = IgnoreStandardErrorWarningFormat, - StandardOutputImportance = StandardOutputImportance, - StandardErrorImportance = StandardErrorImportance, - LogStandardErrorAsError = false, - IgnoreExitCode = true - }; - - if (!_runningExec.Execute()) - { - Log.LogError("Child Exec task failed to execute."); - break; - } - - int exitCode = _runningExec.ExitCode; - if (exitCode == 0) - { - return true; - } - - string message = $"Exec FAILED: exit code {exitCode} (attempt {i + 1}/{MaxAttempts})"; - - if (i + 1 == MaxAttempts || _cancelTokenSource.IsCancellationRequested) - { - Log.LogError(message); - break; - } - - TimeSpan delay = TimeSpan.FromSeconds( - Math.Pow(RetryDelayBase, i) + RetryDelayConstant); - - Log.LogMessage(MessageImportance.High, $"{message} -- Retrying after {delay}..."); - - try - { - Task.Delay(delay, _cancelTokenSource.Token).Wait(); - } - catch (AggregateException e) when (e.InnerException is TaskCanceledException) - { - break; - } - } - return false; - } - } -} diff --git a/tools-local/tasks/installer.tasks/GenerateDebRepoUploadJsonFile.cs b/tools-local/tasks/installer.tasks/GenerateDebRepoUploadJsonFile.cs deleted file mode 100644 index 51e373915cb277..00000000000000 --- a/tools-local/tasks/installer.tasks/GenerateDebRepoUploadJsonFile.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Build.Framework; -using System.IO; - - -namespace Microsoft.DotNet.Build.Tasks -{ - public partial class GenerateDebRepoUploadJsonFile : BuildTask - { - private const string _debianRevisionNumber = "1"; - [Required] - public string RepoId { get; set; } - [Required] - public string UploadJsonFilename { get; set; } - [Required] - public string PackageName { get; set; } - [Required] - public string PackageVersion { get; set; } - [Required] - public string UploadUrl { get; set; } - - public override bool Execute() - { - File.Delete(UploadJsonFilename); - - using (var fileStream = File.Create(UploadJsonFilename)) - { - using (StreamWriter sw = new StreamWriter(fileStream)) - { - sw.WriteLine("{"); - sw.WriteLine($" \"name\":\"{PackageName}\","); - sw.WriteLine($" \"version\":\"{PackageVersion}-{_debianRevisionNumber}\","); - sw.WriteLine($" \"repositoryId\":\"{RepoId}\","); - sw.WriteLine($" \"sourceUrl\":\"{UploadUrl}\""); - sw.WriteLine("}"); - } - } - return !Log.HasLoggedErrors; - } - } -} \ No newline at end of file diff --git a/tools-local/tasks/installer.tasks/GenerateJsonObjectString.cs b/tools-local/tasks/installer.tasks/GenerateJsonObjectString.cs deleted file mode 100644 index 558c4b1654eeed..00000000000000 --- a/tools-local/tasks/installer.tasks/GenerateJsonObjectString.cs +++ /dev/null @@ -1,145 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Build.Framework; -using System; -using System.IO; -using System.Linq; -using System.Text; - -namespace Microsoft.DotNet.Build.Tasks -{ - public class GenerateJsonObjectString : BuildTask - { - private static readonly string __indent1 = new string(' ', 4); - private static readonly string __indent2 = new string(' ', 8); - - /// - /// Properties to include. If multiple properties have the same name, each property value is - /// included in an array. Only specify one value metadata: the first value is used. - /// - /// %(Identity): Name of the property. - /// %(String): String value of the property. This task adds quotes around it in the JSON. - /// %(Object): Object value of the property. Create this with a nested call to this task. - /// - [Required] - public ITaskItem[] Properties { get; set; } - - /// - /// If set, also write the output JSON string to this file. - /// - public string TargetFile { get; set; } - - [Output] - public string Json { get; set; } - - public override bool Execute() - { - var result = new StringBuilder(); - result.AppendLine("{"); - - bool firstProperty = true; - - foreach (var group in Properties.GroupBy(item => item.ItemSpec)) - { - if (firstProperty) - { - firstProperty = false; - } - else - { - result.AppendLine(","); - } - - result.Append(__indent1); - result.Append("\""); - result.Append(group.Key); - result.Append("\": "); - - if (group.Count() == 1) - { - ITaskItem item = group.First(); - WriteProperty(result, item, __indent1); - } - else - { - result.AppendLine("["); - - bool firstArrayLine = true; - - foreach (ITaskItem item in group) - { - if (firstArrayLine) - { - firstArrayLine = false; - } - else - { - result.AppendLine(","); - } - - result.Append(__indent2); - WriteProperty(result, item, __indent2); - } - - result.AppendLine(); - result.Append(__indent1); - result.Append("]"); - } - } - - result.AppendLine(); - result.AppendLine("}"); - - Json = result.ToString(); - - if (!string.IsNullOrEmpty(TargetFile)) - { - Directory.CreateDirectory(Path.GetDirectoryName(TargetFile)); - File.WriteAllText(TargetFile, Json); - } - - return !Log.HasLoggedErrors; - } - - private void WriteProperty(StringBuilder result, ITaskItem item, string indent) - { - string stringValue = item.GetMetadata("String"); - string objectValue = item.GetMetadata("Object"); - - if (!string.IsNullOrEmpty(stringValue)) - { - result.Append("\""); - result.Append(stringValue); - result.Append("\""); - } - else if (!string.IsNullOrEmpty(objectValue)) - { - bool firstObjectLine = true; - - foreach (var line in objectValue.Split( - new[] {Environment.NewLine}, - StringSplitOptions.RemoveEmptyEntries)) - { - if (firstObjectLine) - { - firstObjectLine = false; - } - else - { - result.AppendLine(); - result.Append(indent); - } - - result.Append(line); - } - } - else - { - Log.LogError($"Item '{item.ItemSpec}' has no String or Object value."); - result.Append("null"); - } - } - } -} diff --git a/tools-local/tasks/installer.tasks/ProcessSharedFrameworkDeps.cs b/tools-local/tasks/installer.tasks/ProcessSharedFrameworkDeps.cs deleted file mode 100644 index 687a8c1448b756..00000000000000 --- a/tools-local/tasks/installer.tasks/ProcessSharedFrameworkDeps.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; -using Microsoft.Extensions.DependencyModel; -using NuGet.Common; -using NuGet.ProjectModel; -using System; -using System.IO; -using System.Linq; - -namespace Microsoft.DotNet.Build.Tasks -{ - public partial class ProcessSharedFrameworkDeps : Task - { - [Required] - public string AssetsFilePath { get; set; } - - [Required] - public string DepsFilePath { get; set; } - - [Required] - public string[] PackagesToRemove { get; set; } - - [Required] - public string Runtime { get; set; } - - [Required] - public string BuildTasksAssemblyPath { get; set; } - - public override bool Execute() - { - EnsureInitialized(BuildTasksAssemblyPath); - - ExecuteCore(); - - return true; - } - - private void ExecuteCore() - { - DependencyContext context; - using (var depsStream = File.OpenRead(DepsFilePath)) - { - context = new DependencyContextJsonReader().Read(depsStream); - } - - LockFile lockFile = LockFileUtilities.GetLockFile(AssetsFilePath, NullLogger.Instance); - if (lockFile == null) - { - throw new ArgumentException($"Could not load a LockFile at '{AssetsFilePath}'.", nameof(AssetsFilePath)); - } - - var manager = new RuntimeGraphManager(); - var graph = manager.Collect(lockFile); - var expandedGraph = manager.Expand(graph, Runtime); - - var trimmedRuntimeLibraries = context.RuntimeLibraries; - - if (PackagesToRemove != null && PackagesToRemove.Any()) - { - trimmedRuntimeLibraries = RuntimeReference.RemoveReferences(context.RuntimeLibraries, PackagesToRemove); - } - - context = new DependencyContext( - context.Target, - context.CompilationOptions, - context.CompileLibraries, - trimmedRuntimeLibraries, - expandedGraph - ); - - using (var depsStream = File.Create(DepsFilePath)) - { - new DependencyContextWriter().Write(context, depsStream); - } - } - - partial void EnsureInitialized(string buildTasksAssemblyPath); - } -} diff --git a/tools-local/tasks/installer.tasks/RuntimeGraphManager.cs b/tools-local/tasks/installer.tasks/RuntimeGraphManager.cs deleted file mode 100644 index 2bedeef64b8440..00000000000000 --- a/tools-local/tasks/installer.tasks/RuntimeGraphManager.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyModel; -using NuGet.Packaging; -using NuGet.ProjectModel; -using NuGet.RuntimeModel; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace Microsoft.DotNet.Build.Tasks -{ - internal class RuntimeGraphManager - { - private const string RuntimeJsonFileName = "runtime.json"; - - public RuntimeGraph Collect(LockFile lockFile) - { - string userPackageFolder = lockFile.PackageFolders.FirstOrDefault()?.Path; - var fallBackFolders = lockFile.PackageFolders.Skip(1).Select(f => f.Path); - var packageResolver = new FallbackPackagePathResolver(userPackageFolder, fallBackFolders); - - var graph = RuntimeGraph.Empty; - foreach (var library in lockFile.Libraries) - { - if (string.Equals(library.Type, "package", StringComparison.OrdinalIgnoreCase)) - { - var runtimeJson = library.Files.FirstOrDefault(f => f == RuntimeJsonFileName); - if (runtimeJson != null) - { - var libraryPath = packageResolver.GetPackageDirectory(library.Name, library.Version); - var runtimeJsonFullName = Path.Combine(libraryPath, runtimeJson); - graph = RuntimeGraph.Merge(graph, JsonRuntimeFormat.ReadRuntimeGraph(runtimeJsonFullName)); - } - } - } - return graph; - } - - public IEnumerable Expand(RuntimeGraph runtimeGraph, string runtime) - { - var importers = FindImporters(runtimeGraph, runtime); - foreach (var importer in importers) - { - // ExpandRuntime return runtime itself as first item so we are skiping it - yield return new RuntimeFallbacks(importer, runtimeGraph.ExpandRuntime(importer).Skip(1)); - } - } - - private IEnumerable FindImporters(RuntimeGraph runtimeGraph, string runtime) - { - foreach (var runtimePair in runtimeGraph.Runtimes) - { - var expanded = runtimeGraph.ExpandRuntime(runtimePair.Key); - if (expanded.Contains(runtime)) - { - yield return runtimePair.Key; - } - } - } - } -} diff --git a/tools-local/tasks/installer.tasks/RuntimeReference.cs b/tools-local/tasks/installer.tasks/RuntimeReference.cs deleted file mode 100644 index ead20e864c0627..00000000000000 --- a/tools-local/tasks/installer.tasks/RuntimeReference.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.DependencyModel; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Microsoft.DotNet.Build.Tasks -{ - internal class RuntimeReference - { - public static List RemoveReferences(IReadOnlyList runtimeLibraries, IEnumerable packages) - { - List result = new List(); - - foreach (var runtimeLib in runtimeLibraries) - { - if (string.IsNullOrEmpty(packages.FirstOrDefault(elem => runtimeLib.Name.Equals(elem, StringComparison.OrdinalIgnoreCase)))) - { - List toRemoveDependecy = new List(); - foreach (var dependency in runtimeLib.Dependencies) - { - if (!string.IsNullOrEmpty(packages.FirstOrDefault(elem => dependency.Name.Equals(elem, StringComparison.OrdinalIgnoreCase)))) - { - toRemoveDependecy.Add(dependency); - } - } - - if (toRemoveDependecy.Count > 0) - { - List modifiedDependencies = new List(); - foreach (var dependency in runtimeLib.Dependencies) - { - if (!toRemoveDependecy.Contains(dependency)) - { - modifiedDependencies.Add(dependency); - } - } - - - result.Add(new RuntimeLibrary(runtimeLib.Type, - runtimeLib.Name, - runtimeLib.Version, - runtimeLib.Hash, - runtimeLib.RuntimeAssemblyGroups, - runtimeLib.NativeLibraryGroups, - runtimeLib.ResourceAssemblies, - modifiedDependencies, - runtimeLib.Serviceable)); - - } - else if (string.IsNullOrEmpty(packages.FirstOrDefault(elem => runtimeLib.Name.Equals(elem, StringComparison.OrdinalIgnoreCase)))) - { - result.Add(runtimeLib); - } - } - } - return result; - } - } -} diff --git a/tools-local/tasks/installer.tasks/StabilizeWixFileId.cs b/tools-local/tasks/installer.tasks/StabilizeWixFileId.cs deleted file mode 100644 index 6a76df1df2bd40..00000000000000 --- a/tools-local/tasks/installer.tasks/StabilizeWixFileId.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Build.Framework; -using System; -using System.Diagnostics; -using System.Linq; -using System.Xml.Linq; - -namespace Microsoft.DotNet.Build.Tasks -{ - /// - /// In a WiX source file, replaces the Id of a File with some given string in order to stabilize - /// it. This allows external tooling such as signature validators to rely on a stable identifier - /// for certain files. - /// - public class StabilizeWixFileId : BuildTask - { - /// - /// File to read from. This is expected to be an output from heat.exe. - /// - /// Expected format: - /// - /// - /// - /// - /// - /// - /// - /// ... - /// - [Required] - public string SourceFile { get; set; } - - /// - /// File to write to. May be the same as SourceFile. - /// - [Required] - public string OutputFile { get; set; } - - /// - /// Set of files to stabilize. This matches the end of the "Source" attribute in the WiX - /// source file. If exactly one match isn't found in the WiX source file, this task fails. - /// - /// %(Identity): The file source to replace. - /// %(ReplacementId): The replacement for Id that won't change per-build. - /// - [Required] - public ITaskItem[] FileElementToStabilize { get; set; } - - public override bool Execute() - { - XDocument content = XDocument.Load(SourceFile); - - XNamespace rootNamespace = content.Root.GetDefaultNamespace(); - XName GetQualifiedName(string name) => rootNamespace.GetName(name); - - foreach (var file in FileElementToStabilize) - { - string replacement = file.GetMetadata("ReplacementId"); - - if (string.IsNullOrEmpty(replacement)) - { - Log.LogError($"{nameof(FileElementToStabilize)} {file.ItemSpec} has null/empty ReplacementId metadata."); - continue; - } - - XElement[] matchingFileElements = content.Element(GetQualifiedName("Wix")) - .Elements(GetQualifiedName("Fragment")) - .SelectMany(f => f.Elements(GetQualifiedName("ComponentGroup"))) - .SelectMany(cg => cg.Elements(GetQualifiedName("Component"))) - .SelectMany(c => c.Elements(GetQualifiedName("File"))) - .Where(f => f.Attribute("Source")?.Value - ?.EndsWith(file.ItemSpec, StringComparison.OrdinalIgnoreCase) == true) - .ToArray(); - - if (matchingFileElements.Length != 1) - { - Log.LogError( - $"Expected 1 match for '{file.ItemSpec}', found {matchingFileElements.Length}: " + - string.Join(", ", matchingFileElements.Select(e => e.ToString()))); - - continue; - } - - XAttribute nameAttribute = matchingFileElements[0].Attribute("Id"); - - if (nameAttribute is null) - { - Log.LogError($"Match has no Id attribute: {matchingFileElements[0]}"); - continue; - } - - Log.LogMessage( - $"Setting '{file.ItemSpec}' Id to '{replacement}' for File with Source " + - matchingFileElements[0].Attribute("Source").Value); - - nameAttribute.Value = replacement; - } - - content.Save(OutputFile); - - return !Log.HasLoggedErrors; - } - } -} diff --git a/tools-local/tasks/installer.tasks/ZipFileExtractToDirectory.cs b/tools-local/tasks/installer.tasks/ZipFileExtractToDirectory.cs deleted file mode 100644 index e03a860819ec33..00000000000000 --- a/tools-local/tasks/installer.tasks/ZipFileExtractToDirectory.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Build.Framework; -using System; -using System.IO; -using System.IO.Compression; - -namespace Microsoft.DotNet.Build.Tasks -{ - public sealed class ZipFileExtractToDirectory : BuildTask - { - /// - /// The path to the archive to be extracted. - /// - [Required] - public string SourceArchive { get; set; } - - /// - /// The path of the directory to extract into. - /// - [Required] - public string DestinationDirectory { get; set; } - - /// - /// Indicates if the destination directory should be overwritten if it already exists. - /// - public bool OverwriteDestination { get; set; } - - /// - /// File entries to include in the extraction. Entries are relative - /// paths inside the archive. If null or empty, all files are extracted. - /// - public ITaskItem[] Include { get; set; } - - public override bool Execute() - { - try - { - if (Directory.Exists(DestinationDirectory)) - { - if (OverwriteDestination) - { - Log.LogMessage(MessageImportance.Low, $"'{DestinationDirectory}' already exists, trying to delete before unzipping..."); - Directory.Delete(DestinationDirectory, recursive: true); - } - else - { - Log.LogWarning($"'{DestinationDirectory}' already exists. Did you forget to set '{nameof(OverwriteDestination)}' to true?"); - } - } - - Log.LogMessage(MessageImportance.High, "Decompressing '{0}' into '{1}'...", SourceArchive, DestinationDirectory); - Directory.CreateDirectory(Path.GetDirectoryName(DestinationDirectory)); - - using (ZipArchive archive = ZipFile.OpenRead(SourceArchive)) - { - if (Include?.Length > 0) - { - foreach (ITaskItem entryItem in Include) - { - ZipArchiveEntry entry = archive.GetEntry(entryItem.ItemSpec); - string destinationPath = Path.Combine(DestinationDirectory, entryItem.ItemSpec); - - Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); - entry.ExtractToFile(destinationPath, overwrite: false); - } - } - else - { - archive.ExtractToDirectory(DestinationDirectory); - } - } - } - catch (Exception e) - { - // We have 2 log calls because we want a nice error message but we also want to capture the callstack in the log. - Log.LogError("An exception has occurred while trying to decompress '{0}' into '{1}'.", SourceArchive, DestinationDirectory); - Log.LogErrorFromException(e, /*show stack=*/ true, /*show detail=*/ true, DestinationDirectory); - return false; - } - return true; - } - } -} diff --git a/tools-local/tasks/installer.tasks/ZipFileGetEntries.cs b/tools-local/tasks/installer.tasks/ZipFileGetEntries.cs deleted file mode 100644 index 56b1e3804e1870..00000000000000 --- a/tools-local/tasks/installer.tasks/ZipFileGetEntries.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; -using System; -using System.IO.Compression; -using System.Linq; - -namespace Microsoft.DotNet.Build.Tasks -{ - public sealed class ZipFileGetEntries : BuildTask - { - /// - /// The path to the archive. - /// - [Required] - public string TargetArchive { get; set; } - - /// - /// Generated items where each ItemSpec is the relative location of a - /// file entry in the zip archive. - /// - [Output] - public ITaskItem[] Entries { get; set; } - - public override bool Execute() - { - try - { - using (ZipArchive archive = ZipFile.OpenRead(TargetArchive)) - { - Entries = archive.Entries - // Escape '%' so encoded '+' in the nupkg stays encoded through MSBuild. - .Select(e => new TaskItem(e.FullName.Replace("%", "%25"))) - .ToArray(); - } - } - catch (Exception e) - { - // We have 2 log calls because we want a nice error message but we also want to capture the callstack in the log. - Log.LogError($"An exception has occurred while trying to read entries from '{TargetArchive}'."); - Log.LogErrorFromException(e, /*show stack=*/ true, /*show detail=*/ true, TargetArchive); - return false; - } - return true; - } - } -} From 037f425853e5587da95404f07130e36efd6cb2e3 Mon Sep 17 00:00:00 2001 From: "Chaojie Yang (Pactera Technologies Inc)" Date: Fri, 24 Apr 2020 07:35:36 -0700 Subject: [PATCH 2/4] Remove RegenerateReadmeTable.cs --- .../installer.tasks/RegenerateReadmeTable.cs | 197 ------------------ 1 file changed, 197 deletions(-) delete mode 100644 tools-local/tasks/installer.tasks/RegenerateReadmeTable.cs diff --git a/tools-local/tasks/installer.tasks/RegenerateReadmeTable.cs b/tools-local/tasks/installer.tasks/RegenerateReadmeTable.cs deleted file mode 100644 index 57664a2989001b..00000000000000 --- a/tools-local/tasks/installer.tasks/RegenerateReadmeTable.cs +++ /dev/null @@ -1,197 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Build.Framework; -using System; -using System.IO; -using System.Linq; -using System.Text; - -namespace Microsoft.DotNet.Build.Tasks -{ - public class RegenerateReadmeTable : BuildTask - { - private const string TableComment = "generated table"; - private const string LinksComment = "links to include in table"; - - /// - /// A readme file that contains a Markdown table and a list of links. This task reads the - /// "links to include in table" section to find available links, then updates the - /// "generated table" section to include a Markdown table. Cells in the table are generated - /// by looking for links that apply to the current combination of platform and branch. - /// - /// The sections are marked by one-line html comments: - /// - /// - /// ... - /// - /// - [Required] - public string ReadmeFile { get; set; } - - /// - /// %(Identity): Name of this branch, as appears in the column header. - /// %(Abbr): Abbreviation of this branch, used to match up with link names. - /// - [Required] - public ITaskItem[] Branches { get; set; } - - /// - /// %(Identity): Name of this platform, as appears in bold as the first column of the row. - /// %(Parenthetical): An extra non-bold string to add after the platform name. - /// %(Abbr): Abbreviation of this platform, used to match up with link names. - /// - [Required] - public ITaskItem[] Platforms { get; set; } - - - private string Begin(string marker) => $""; - private string End(string marker) => $""; - - - public override bool Execute() - { - string[] readmeLines = File.ReadAllLines(ReadmeFile); - - if (readmeLines.Contains(Begin(LinksComment)) && - readmeLines.Contains(End(LinksComment))) - { - // In the links section, extract the name of each reference-style Markdown link. - // For example, grabs 'win-x86-badge-2.1.X' from - // [win-x86-badge-2.1.X]: https://example.org/foo - string[] links = readmeLines - .SkipWhile(line => line != Begin(LinksComment)) - .Skip(1) - .TakeWhile(line => line != End(LinksComment)) - .Where(line => line.StartsWith("[") && line.Contains("]:")) - .Select(line => line.Substring( - 1, - line.IndexOf("]:", StringComparison.Ordinal) - 1)) - .ToArray(); - - string[] rows = Platforms.Select(p => CreateRow(p, links)).ToArray(); - - // Final table to write to the file, with a newline before and after. - string[] table = new[] - { - "", - $"| Platform |{string.Concat(Branches.Select(p => $" {p.ItemSpec} |"))}", - $"| --- | {string.Concat(Enumerable.Repeat(" :---: |", Branches.Length))}" - }.Concat(rows).Concat(new[] - { - "" - }).ToArray(); - - if (readmeLines.Contains(Begin(TableComment)) && - readmeLines.Contains(End(TableComment))) - { - string[] beforeTable = readmeLines - .TakeWhile(line => line != Begin(TableComment)) - .Concat(new[] { Begin(TableComment) }) - .ToArray(); - - string[] afterTable = readmeLines - .Skip(beforeTable.Length) - .SkipWhile(line => line != End(TableComment)) - .ToArray(); - - File.WriteAllLines( - ReadmeFile, - beforeTable.Concat(table).Concat(afterTable)); - } - else - { - Log.LogError($"Readme '{ReadmeFile}' has no '{TableComment}' section."); - } - } - else - { - Log.LogError($"Readme '{ReadmeFile}' has no '{LinksComment}' section."); - } - - return !Log.HasLoggedErrors; - } - - private string CreateRow(ITaskItem platform, string[] links) - { - string parenthetical = platform.GetMetadata("Parenthetical"); - - string cells = string.Concat( - Branches.Select(branch => $" {CreateCell(platform, branch, links)} |")); - - return $"| **{platform.ItemSpec}**{parenthetical} |{cells}"; - } - - private string CreateCell(ITaskItem platform, ITaskItem branch, string[] links) - { - string branchAbbr = branch.GetMetadata("Abbr"); - if (string.IsNullOrEmpty(branchAbbr)) - { - Log.LogError($"Branch '{branch.ItemSpec}' has no Abbr metadata."); - } - - string platformAbbr = platform.GetMetadata("Abbr"); - if (string.IsNullOrEmpty(platformAbbr)) - { - Log.LogError($"Platform '{platform.ItemSpec}' has no Abbr metadata."); - } - - var sb = new StringBuilder(); - - string Link(string type) => $"{platformAbbr}-{type}-{branchAbbr}"; - - void AddLink(string name, string type) - { - string link = Link(type); - string checksum = Link($"{type}-checksum"); - - if (links.Contains(link)) - { - sb.Append("
"); - sb.Append($"[{name}][{link}]"); - if (links.Contains(checksum)) - { - sb.Append($" ([Checksum][{checksum}])"); - } - } - } - - string badge = Link("badge"); - string version = Link("version"); - - if (links.Contains(badge) && links.Contains(version)) - { - sb.Append($"[![][{badge}]][{version}]"); - } - - // Look for various types of links. The first parameter is the name of the link as it - // appears in the table cell. The second parameter is how this type of link is - // abbreviated in the link section. A generic checksum link is added for any of these - // that also have a '-checksum' link. - - AddLink("Installer", "installer"); - - AddLink("Runtime-Deps", "runtime-deps"); - AddLink("Host", "host"); - AddLink("Host FX Resolver", "hostfxr"); - AddLink("Shared Framework", "sharedfx"); - - AddLink("zip", "zip"); - AddLink("tar.gz", "targz"); - - AddLink("NetHost (zip)", "nethost-zip"); - AddLink("NetHost (tar.gz)", "nethost-targz"); - - AddLink("Symbols (zip)", "symbols-zip"); - AddLink("Symbols (tar.gz)", "symbols-targz"); - - if (sb.Length == 0) - { - sb.Append("N/A"); - } - - return sb.ToString(); - } - } -} From 876e32fcb1ea159feabff4922de9f35790d918cc Mon Sep 17 00:00:00 2001 From: "Chaojie Yang (Pactera Technologies Inc)" Date: Fri, 24 Apr 2020 07:41:36 -0700 Subject: [PATCH 3/4] Revert "Remove RegenerateReadmeTable.cs" This reverts commit 037f425853e5587da95404f07130e36efd6cb2e3. --- .../installer.tasks/RegenerateReadmeTable.cs | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 tools-local/tasks/installer.tasks/RegenerateReadmeTable.cs diff --git a/tools-local/tasks/installer.tasks/RegenerateReadmeTable.cs b/tools-local/tasks/installer.tasks/RegenerateReadmeTable.cs new file mode 100644 index 00000000000000..57664a2989001b --- /dev/null +++ b/tools-local/tasks/installer.tasks/RegenerateReadmeTable.cs @@ -0,0 +1,197 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Build.Framework; +using System; +using System.IO; +using System.Linq; +using System.Text; + +namespace Microsoft.DotNet.Build.Tasks +{ + public class RegenerateReadmeTable : BuildTask + { + private const string TableComment = "generated table"; + private const string LinksComment = "links to include in table"; + + /// + /// A readme file that contains a Markdown table and a list of links. This task reads the + /// "links to include in table" section to find available links, then updates the + /// "generated table" section to include a Markdown table. Cells in the table are generated + /// by looking for links that apply to the current combination of platform and branch. + /// + /// The sections are marked by one-line html comments: + /// + /// + /// ... + /// + /// + [Required] + public string ReadmeFile { get; set; } + + /// + /// %(Identity): Name of this branch, as appears in the column header. + /// %(Abbr): Abbreviation of this branch, used to match up with link names. + /// + [Required] + public ITaskItem[] Branches { get; set; } + + /// + /// %(Identity): Name of this platform, as appears in bold as the first column of the row. + /// %(Parenthetical): An extra non-bold string to add after the platform name. + /// %(Abbr): Abbreviation of this platform, used to match up with link names. + /// + [Required] + public ITaskItem[] Platforms { get; set; } + + + private string Begin(string marker) => $""; + private string End(string marker) => $""; + + + public override bool Execute() + { + string[] readmeLines = File.ReadAllLines(ReadmeFile); + + if (readmeLines.Contains(Begin(LinksComment)) && + readmeLines.Contains(End(LinksComment))) + { + // In the links section, extract the name of each reference-style Markdown link. + // For example, grabs 'win-x86-badge-2.1.X' from + // [win-x86-badge-2.1.X]: https://example.org/foo + string[] links = readmeLines + .SkipWhile(line => line != Begin(LinksComment)) + .Skip(1) + .TakeWhile(line => line != End(LinksComment)) + .Where(line => line.StartsWith("[") && line.Contains("]:")) + .Select(line => line.Substring( + 1, + line.IndexOf("]:", StringComparison.Ordinal) - 1)) + .ToArray(); + + string[] rows = Platforms.Select(p => CreateRow(p, links)).ToArray(); + + // Final table to write to the file, with a newline before and after. + string[] table = new[] + { + "", + $"| Platform |{string.Concat(Branches.Select(p => $" {p.ItemSpec} |"))}", + $"| --- | {string.Concat(Enumerable.Repeat(" :---: |", Branches.Length))}" + }.Concat(rows).Concat(new[] + { + "" + }).ToArray(); + + if (readmeLines.Contains(Begin(TableComment)) && + readmeLines.Contains(End(TableComment))) + { + string[] beforeTable = readmeLines + .TakeWhile(line => line != Begin(TableComment)) + .Concat(new[] { Begin(TableComment) }) + .ToArray(); + + string[] afterTable = readmeLines + .Skip(beforeTable.Length) + .SkipWhile(line => line != End(TableComment)) + .ToArray(); + + File.WriteAllLines( + ReadmeFile, + beforeTable.Concat(table).Concat(afterTable)); + } + else + { + Log.LogError($"Readme '{ReadmeFile}' has no '{TableComment}' section."); + } + } + else + { + Log.LogError($"Readme '{ReadmeFile}' has no '{LinksComment}' section."); + } + + return !Log.HasLoggedErrors; + } + + private string CreateRow(ITaskItem platform, string[] links) + { + string parenthetical = platform.GetMetadata("Parenthetical"); + + string cells = string.Concat( + Branches.Select(branch => $" {CreateCell(platform, branch, links)} |")); + + return $"| **{platform.ItemSpec}**{parenthetical} |{cells}"; + } + + private string CreateCell(ITaskItem platform, ITaskItem branch, string[] links) + { + string branchAbbr = branch.GetMetadata("Abbr"); + if (string.IsNullOrEmpty(branchAbbr)) + { + Log.LogError($"Branch '{branch.ItemSpec}' has no Abbr metadata."); + } + + string platformAbbr = platform.GetMetadata("Abbr"); + if (string.IsNullOrEmpty(platformAbbr)) + { + Log.LogError($"Platform '{platform.ItemSpec}' has no Abbr metadata."); + } + + var sb = new StringBuilder(); + + string Link(string type) => $"{platformAbbr}-{type}-{branchAbbr}"; + + void AddLink(string name, string type) + { + string link = Link(type); + string checksum = Link($"{type}-checksum"); + + if (links.Contains(link)) + { + sb.Append("
"); + sb.Append($"[{name}][{link}]"); + if (links.Contains(checksum)) + { + sb.Append($" ([Checksum][{checksum}])"); + } + } + } + + string badge = Link("badge"); + string version = Link("version"); + + if (links.Contains(badge) && links.Contains(version)) + { + sb.Append($"[![][{badge}]][{version}]"); + } + + // Look for various types of links. The first parameter is the name of the link as it + // appears in the table cell. The second parameter is how this type of link is + // abbreviated in the link section. A generic checksum link is added for any of these + // that also have a '-checksum' link. + + AddLink("Installer", "installer"); + + AddLink("Runtime-Deps", "runtime-deps"); + AddLink("Host", "host"); + AddLink("Host FX Resolver", "hostfxr"); + AddLink("Shared Framework", "sharedfx"); + + AddLink("zip", "zip"); + AddLink("tar.gz", "targz"); + + AddLink("NetHost (zip)", "nethost-zip"); + AddLink("NetHost (tar.gz)", "nethost-targz"); + + AddLink("Symbols (zip)", "symbols-zip"); + AddLink("Symbols (tar.gz)", "symbols-targz"); + + if (sb.Length == 0) + { + sb.Append("N/A"); + } + + return sb.ToString(); + } + } +} From e5378a2e1760e49eeb495f47ce1b37f2a3d5776b Mon Sep 17 00:00:00 2001 From: "Chaojie Yang (Pactera Technologies Inc)" Date: Fri, 24 Apr 2020 23:25:59 -0700 Subject: [PATCH 4/4] BuildFPMToolPreReqs.cs is still needed for packaging Linux_x64 --- .../installer.tasks/BuildFPMToolPreReqs.cs | 352 ++++++++++++++++++ 1 file changed, 352 insertions(+) create mode 100644 tools-local/tasks/installer.tasks/BuildFPMToolPreReqs.cs diff --git a/tools-local/tasks/installer.tasks/BuildFPMToolPreReqs.cs b/tools-local/tasks/installer.tasks/BuildFPMToolPreReqs.cs new file mode 100644 index 00000000000000..6ea7b65710640c --- /dev/null +++ b/tools-local/tasks/installer.tasks/BuildFPMToolPreReqs.cs @@ -0,0 +1,352 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Build.Framework; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Microsoft.DotNet.Build.Tasks +{ + /// + /// This task prepares the command line parameters for running a RPM build using FPM tool and also updates the copyright and changelog file tokens. + /// If parses various values from the config json by first reading it into a model and then builds the required string for parameters and passes it back. + /// + /// + public class BuildFPMToolPreReqs : BuildTask + { + [Required] + public string InputDir { get; set; } + [Required] + public string OutputDir { get; set; } + [Required] + public string PackageVersion { get; set; } + [Required] + public string ConfigJsonFile { get; set; } + [Output] + public string FPMParameters { get; set; } + + public override bool Execute() + { + try + { + if (!File.Exists(ConfigJsonFile)) + { + throw new FileNotFoundException($"Expected file {ConfigJsonFile} was not found."); + } + + // Open the Config Json and read the values into the model + TextReader projectFileReader = File.OpenText(ConfigJsonFile); + if (projectFileReader != null) + { + string jsonFileText = projectFileReader.ReadToEnd(); + ConfigJson configJson = JsonConvert.DeserializeObject(jsonFileText); + + // Update the Changelog and Copyright files by replacing tokens with values from config json + UpdateChangelog(configJson, PackageVersion); + UpdateCopyRight(configJson); + + // Build the full list of parameters + FPMParameters = BuildCmdParameters(configJson, PackageVersion); + Log.LogMessage(MessageImportance.Normal, "Generated RPM paramters: " + FPMParameters); + } + else + { + throw new IOException($"Could not open the file {ConfigJsonFile} for reading."); + } + } + catch (Exception e) + { + Log.LogErrorFromException(e, true); + } + + return !Log.HasLoggedErrors; + } + + // Update the tokens in the changelog file from the config Json + private void UpdateChangelog(ConfigJson configJson, string package_version) + { + try + { + string changelogFile = Path.Combine(InputDir, "templates", "changelog"); + if (!File.Exists(changelogFile)) + { + throw new FileNotFoundException($"Expected file {changelogFile} was not found."); + } + string str = File.ReadAllText(changelogFile); + str = str.Replace("{PACKAGE_NAME}", configJson.Package_Name); + str = str.Replace("{PACKAGE_VERSION}", package_version); + str = str.Replace("{PACKAGE_REVISION}", configJson.Release.Package_Revision); + str = str.Replace("{URGENCY}", configJson.Release.Urgency); + str = str.Replace("{CHANGELOG_MESSAGE}", configJson.Release.Changelog_Message); + str = str.Replace("{MAINTAINER_NAME}", configJson.Maintainer_Name); + str = str.Replace("{MAINTAINER_EMAIL}", configJson.Maintainer_Email); + // The date format needs to be like Wed May 17 2017 + str = str.Replace("{DATE}", DateTime.UtcNow.ToString("ddd MMM dd yyyy")); + File.WriteAllText(changelogFile, str); + } + catch (Exception e) + { + Log.LogError("Exception while updating the changelog file: " + e.Message); + } + } + + private void UpdateCopyRight(ConfigJson configJson) + { + try + { + // Update the tokens in the copyright file from the config Json + string copyrightFile = Path.Combine(InputDir, "templates", "copyright"); + if (!File.Exists(copyrightFile)) + { + throw new FileNotFoundException($"Expected file {copyrightFile} was not found."); + } + string str = File.ReadAllText(copyrightFile); + str = str.Replace("{COPYRIGHT_TEXT}", configJson.CopyRight); + str = str.Replace("{LICENSE_NAME}", configJson.License.Type); + str = str.Replace("{LICENSE_NAME}", configJson.License.Type); + str = str.Replace("{LICENSE_TEXT}", configJson.License.Full_Text); + File.WriteAllText(copyrightFile, str); + } + catch (Exception e) + { + Log.LogError("Exception while updating the copyright file: " + e.Message); + } + } + + private string BuildCmdParameters(ConfigJson configJson, string package_version) + { + // Parameter list that needs to be passed to FPM tool: + // -s : is the input source type(dir) --Static + // -t : is the type of package(rpm) --Static + // -n : is for the name of the package --JSON + // -v : is the version to give to the package --ARG + // -a : architecture --JSON + // -d : is for all dependent packages. This can be used multiple times to specify the dependencies of the package. --JSON + // --rpm-os : the operating system to target this rpm --Static + // --rpm-changelog : the changelog from FILEPATH contents --ARG + // --rpm-summary : it is the RPM summary that shows in the Title --JSON + // --description : it is the description for the package --JSON + // -p : The actual package name (with path) for your package. --ARG+JSON + // --conflicts : Other packages/versions this package conflicts with provided as CSV --JSON + // --directories : Recursively add directories as being owned by the package. --JSON + // --after-install : FILEPATH to the script to be run after install of the package --JSON + // --after-remove : FILEPATH to the script to be run after package removal --JSON + // --license : the licensing name for the package. This will include the license type in the meta-data for the package, but will not include the associated license file within the package itself. --JSON + // --iteration : the iteration to give to the package. This comes from the package_revision --JSON + // --url : url for this package. --JSON + // --verbose : Set verbose output for FPM tool --Static + // : Add all the folder mappings for packge_root, docs, man pages --Static + + var parameters = new List(); + parameters.Add("-s dir"); + parameters.Add("-t rpm"); + parameters.Add(string.Concat("-n ", configJson.Package_Name)); + parameters.Add(string.Concat("-v ", package_version)); + parameters.Add(string.Concat("-a ", configJson.Control.Architecture)); + + // Build the list of dependencies as -d -d + if (configJson.Rpm_Dependencies != null) + { + IEnumerable dependencies; + + switch (configJson.Rpm_Dependencies) + { + case JArray dependencyArray: + dependencies = dependencyArray.ToObject(); + break; + + case JObject dependencyDictionary: + dependencies = dependencyDictionary + .ToObject>() + .Select(pair => new RpmDependency + { + Package_Name = pair.Key, + Package_Version = pair.Value + }); + break; + + default: + throw new ArgumentException( + "Expected 'rpm_dependencies' to be JArray or JObject, but found " + + configJson.Rpm_Dependencies.Type); + } + + foreach (RpmDependency rpmdep in dependencies) + { + string dependency = ""; + if (rpmdep.Package_Name != "") + { + // If no version is specified then the dependency is just the package without >= check + if (rpmdep.Package_Version == "") + { + dependency = rpmdep.Package_Name; + } + else + { + dependency = string.Concat(rpmdep.Package_Name, " >= ", rpmdep.Package_Version); + } + } + if (dependency != "") + { + parameters.Add(string.Concat("-d ", EscapeArg(dependency))); + } + } + } + + // Build the list of owned directories + if (configJson.Directories != null) + { + foreach (string dir in configJson.Directories) + { + if (dir != "") + { + parameters.Add(string.Concat("--directories ", EscapeArg(dir))); + } + } + } + + parameters.Add("--rpm-os linux"); + parameters.Add(string.Concat("--rpm-changelog ", EscapeArg(Path.Combine(InputDir, "templates", "changelog")))); // Changelog File + parameters.Add(string.Concat("--rpm-summary ", EscapeArg(configJson.Short_Description))); + parameters.Add(string.Concat("--description ", EscapeArg(configJson.Long_Description))); + parameters.Add(string.Concat("--maintainer ", EscapeArg(configJson.Maintainer_Name + " <" + configJson.Maintainer_Email + ">"))); + parameters.Add(string.Concat("--vendor ", EscapeArg(configJson.Vendor))); + parameters.Add(string.Concat("-p ", Path.Combine(OutputDir, configJson.Package_Name + ".rpm"))); + if (configJson.Package_Conflicts != null) parameters.Add(string.Concat("--conflicts ", EscapeArg(string.Join(",", configJson.Package_Conflicts)))); + if (configJson.After_Install_Source != null) parameters.Add(string.Concat("--after-install ", Path.Combine(InputDir, EscapeArg(configJson.After_Install_Source)))); + if (configJson.After_Remove_Source != null) parameters.Add(string.Concat("--after-remove ", Path.Combine(InputDir, EscapeArg(configJson.After_Remove_Source)))); + parameters.Add(string.Concat("--license ", EscapeArg(configJson.License.Type))); + parameters.Add(string.Concat("--iteration ", configJson.Release.Package_Revision)); + parameters.Add(string.Concat("--url ", "\"", EscapeArg(configJson.Homepage), "\"")); + parameters.Add("--verbose"); + + // Map all the payload directories as they need to install on the system + if (configJson.Install_Root != null) parameters.Add(string.Concat(Path.Combine(InputDir, "package_root/="), configJson.Install_Root)); // Package Files + if (configJson.Install_Man != null) parameters.Add(string.Concat(Path.Combine(InputDir, "docs", "host/="), configJson.Install_Man)); // Man Pages + if (configJson.Install_Doc != null) parameters.Add(string.Concat(Path.Combine(InputDir, "templates", "copyright="), configJson.Install_Doc)); // CopyRight File + + return string.Join(" ", parameters); + } + + private string EscapeArg(string arg) + { + var sb = new StringBuilder(); + + bool quoted = ShouldSurroundWithQuotes(arg); + if (quoted) sb.Append("\""); + + for (int i = 0; i < arg.Length; ++i) + { + var backslashCount = 0; + + // Consume All Backslashes + while (i < arg.Length && arg[i] == '\\') + { + backslashCount++; + i++; + } + + // Escape any backslashes at the end of the arg + // This ensures the outside quote is interpreted as + // an argument delimiter + if (i == arg.Length) + { + sb.Append('\\', 2 * backslashCount); + } + + // Escape any preceding backslashes and the quote + else if (arg[i] == '"') + { + sb.Append('\\', (2 * backslashCount) + 1); + sb.Append('"'); + } + + // Output any consumed backslashes and the character + else + { + sb.Append('\\', backslashCount); + sb.Append(arg[i]); + } + } + + if (quoted) sb.Append("\""); + + return sb.ToString(); + } + private bool ShouldSurroundWithQuotes(string argument) + { + // Don't quote already quoted strings + if (argument.StartsWith("\"", StringComparison.Ordinal) && + argument.EndsWith("\"", StringComparison.Ordinal)) + { + return false; + } + + // Only quote if whitespace exists in the string + if (argument.Contains(" ") || argument.Contains("\t") || argument.Contains("\n")) + { + return true; + } + return false; + } + + /// + /// Model classes for reading and storing the JSON. + /// + private class ConfigJson + { + public string Maintainer_Name { get; set; } + public string Maintainer_Email { get; set; } + public string Vendor { get; set; } + public string Package_Name { get; set; } + public string Install_Root { get; set; } + public string Install_Doc { get; set; } + public string Install_Man { get; set; } + public string Short_Description { get; set; } + public string Long_Description { get; set; } + public string Homepage { get; set; } + public string CopyRight { get; set; } + public Release Release { get; set; } + public Control Control { get; set; } + public License License { get; set; } + public JContainer Rpm_Dependencies { get; set; } + public List Package_Conflicts { get; set; } + public List Directories { get; set; } + public string After_Install_Source { get; set; } + public string After_Remove_Source { get; set; } + } + + private class Release + { + public string Package_Version { get; set; } + public string Package_Revision { get; set; } + public string Urgency { get; set; } + public string Changelog_Message { get; set; } + } + + private class Control + { + public string Priority { get; set; } + public string Section { get; set; } + public string Architecture { get; set; } + } + + private class License + { + public string Type { get; set; } + public string Full_Text { get; set; } + } + + private class RpmDependency + { + public string Package_Name { get; set; } + public string Package_Version { get; set; } + } + } +}