using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Text; using System.Diagnostics; using System.Runtime.InteropServices; namespace Amazon.Common.DotNetCli.Tools { /// /// Wrapper around the dotnet cli used to execute the publish command. /// public class DotNetCLIWrapper : AbstractCLIWrapper { public DotNetCLIWrapper(IToolLogger logger, string workingDirectory) : base(logger, workingDirectory) { } /// /// Generates deployment manifest for staged content /// /// /// /// /// public int Publish(string projectLocation, string outputLocation, string targetFramework, string configuration, string additionalPublishOptions) { if (Directory.Exists(outputLocation)) { try { Directory.Delete(outputLocation, true); _logger?.WriteLine("Deleted previous publish folder"); } catch (Exception e) { _logger?.WriteLine($"Warning unable to delete previous publish folder: {e.Message}"); } } _logger?.WriteLine($"... invoking 'dotnet publish'"); var dotnetCLI = FindExecutableInPath("dotnet.exe"); if (dotnetCLI == null) dotnetCLI = FindExecutableInPath("dotnet"); if (string.IsNullOrEmpty(dotnetCLI)) throw new Exception("Failed to locate dotnet CLI executable. Make sure the dotnet CLI is installed in the environment PATH."); StringBuilder arguments = new StringBuilder("publish"); if (!string.IsNullOrEmpty(projectLocation)) { arguments.Append($" \"{Utilities.DetermineProjectLocation(this._workingDirectory, projectLocation)}\""); } if (!string.IsNullOrEmpty(outputLocation)) { arguments.Append($" --output \"{outputLocation}\""); } if (!string.IsNullOrEmpty(configuration)) { arguments.Append($" --configuration \"{configuration}\""); } if (!string.IsNullOrEmpty(targetFramework)) { arguments.Append($" --framework \"{targetFramework}\""); } if (!string.IsNullOrEmpty(additionalPublishOptions)) { arguments.Append($" {additionalPublishOptions}"); } var psi = new ProcessStartInfo { FileName = dotnetCLI, Arguments = arguments.ToString(), WorkingDirectory = this._workingDirectory, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; this._logger?.WriteLine($"Executing: dotnet {psi.Arguments}"); int exitCode = base.ExecuteCommand(psi, "dotnet publish"); if (exitCode != 0) return exitCode; var chmodPath = FindExecutableInPath("chmod"); if (!string.IsNullOrEmpty(chmodPath) && File.Exists(chmodPath)) { // as we are not invoking through a shell, which would handle // wildcard expansion for us, we need to invoke per-file var dllFiles = Directory.GetFiles(outputLocation, "*.dll", SearchOption.TopDirectoryOnly); foreach (var dllFile in dllFiles) { var dllFilename = Path.GetFileName(dllFile); var psiChmod = new ProcessStartInfo { FileName = chmodPath, Arguments = "+r \"" + dllFilename + "\"", WorkingDirectory = outputLocation, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; if(base.ExecuteCommand(psiChmod, "dotnet publish") == 0) { this._logger?.WriteLine($"Changed permissions on published dll (chmod +r {dllFilename})."); } } } return 0; } public static Version GetSdkVersion() { var dotnetCLI = FindExecutableInPath("dotnet.exe"); if (dotnetCLI == null) dotnetCLI = FindExecutableInPath("dotnet"); if (string.IsNullOrEmpty(dotnetCLI)) throw new Exception("Failed to locate dotnet CLI executable. Make sure the dotnet CLI is installed in the environment PATH."); var results = Utilities.ExecuteShellCommand(null, dotnetCLI, "--list-sdks"); if(results.ExitCode != 0) throw new Exception("Command \"dotnet --list-sdks\" failed, captured output: \n" + results.Stdout); var maxSdkVersion = ParseListSdkOutput(results.Stdout); if (maxSdkVersion == null) { throw new Exception("Failed to parse latest SDK version from captured output:\n" + results.Stdout); } return maxSdkVersion; } public static Version ParseListSdkOutput(string listSdkOutput) { var outputLines = listSdkOutput.Split('\n'); for (int i = outputLines.Length - 1; i >= 0; i--) { var line = outputLines[i].Trim(); if (string.IsNullOrEmpty(line)) continue; var tokens = line.Split(' '); // There should be at least 2 tokens, the version and the path to the SDK. There might be more than 2 tokens if the path to the SDK contained spaces. if (tokens.Length < 2) continue; if(Version.TryParse(tokens[0], out var version)) { return version; } } return null; } } }