/// <summary> /// Execute the dotnet store command on the provided package manifest /// </summary> /// <param name="defaults"></param> /// <param name="projectLocation"></param> /// <param name="outputLocation"></param> /// <param name="targetFramework"></param> /// <param name="packageManifest"></param> /// <param name="enableOptimization"></param> /// <returns></returns> public int Store(LambdaToolsDefaults defaults, string projectLocation, string outputLocation, string targetFramework, string packageManifest, bool enableOptimization) { if (outputLocation == null) { throw new ArgumentNullException(nameof(outputLocation)); } 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}"); } } 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 fullProjectLocation = this._workingDirectory; if (!string.IsNullOrEmpty(projectLocation)) { fullProjectLocation = Utilities.DetermineProjectLocation(this._workingDirectory, projectLocation); } var fullPackageManifest = Path.Combine(fullProjectLocation, packageManifest); _logger?.WriteLine($"... invoking 'dotnet store' for manifest {fullPackageManifest} into output directory {outputLocation}"); StringBuilder arguments = new StringBuilder("store"); if (!string.IsNullOrEmpty(outputLocation)) { arguments.Append($" --output \"{outputLocation}\""); } if (!string.IsNullOrEmpty(targetFramework)) { arguments.Append($" --framework \"{targetFramework}\""); } arguments.Append($" --manifest \"{fullPackageManifest}\""); arguments.Append($" --runtime {LambdaUtilities.DetermineRuntimeParameter(targetFramework)}"); if (!enableOptimization) { arguments.Append(" --skip-optimization"); } var psi = new ProcessStartInfo { FileName = dotnetCLI, Arguments = arguments.ToString(), WorkingDirectory = this._workingDirectory, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; var handler = (DataReceivedEventHandler)((o, e) => { if (string.IsNullOrEmpty(e.Data)) { return; } // Skip outputting this warning message as it adds a lot of noise to the output and is not actionable. // Full warning message being skipped: message NETSDK1062: // Unable to use package assets cache due to I/O error. This can occur when the same project is built // more than once in parallel. Performance may be degraded, but the build result will not be impacted. if (e.Data.Contains("message NETSDK1062")) { return; } _logger?.WriteLine("... store: " + e.Data); }); int exitCode; using (var proc = new Process()) { proc.StartInfo = psi; proc.Start(); proc.ErrorDataReceived += handler; proc.OutputDataReceived += handler; proc.BeginOutputReadLine(); proc.BeginErrorReadLine(); proc.EnableRaisingEvents = true; proc.WaitForExit(); exitCode = proc.ExitCode; } return(exitCode); }
/// <summary> /// Executes the dotnet publish command for the provided project /// </summary> /// <param name="defaults"></param> /// <param name="projectLocation"></param> /// <param name="outputLocation"></param> /// <param name="targetFramework"></param> /// <param name="configuration"></param> /// <param name="msbuildParameters"></param> /// <param name="deploymentTargetPackageStoreManifestContent"></param> public int Publish(LambdaToolsDefaults defaults, string projectLocation, string outputLocation, string targetFramework, string configuration, string msbuildParameters, IList <string> publishManifests) { if (outputLocation == null) { throw new ArgumentNullException(nameof(outputLocation)); } 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', working folder '{outputLocation}'"); 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 fullProjectLocation = this._workingDirectory; if (!string.IsNullOrEmpty(projectLocation)) { fullProjectLocation = Utilities.DetermineProjectLocation(this._workingDirectory, projectLocation); } StringBuilder arguments = new StringBuilder("publish"); if (!string.IsNullOrEmpty(projectLocation)) { arguments.Append($" \"{fullProjectLocation}\""); } 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(msbuildParameters)) { arguments.Append($" {msbuildParameters}"); } if (!string.Equals("netcoreapp1.0", targetFramework, StringComparison.OrdinalIgnoreCase)) { arguments.Append(" /p:GenerateRuntimeConfigurationFiles=true"); // Define an action to set the runtime and self-contained switches. var applyRuntimeSwitchAction = (Action)(() => { if (msbuildParameters == null || msbuildParameters.IndexOf("--runtime", StringComparison.InvariantCultureIgnoreCase) == -1) { arguments.Append($" --runtime {LambdaUtilities.DetermineRuntimeParameter(targetFramework)}"); } if (msbuildParameters == null || msbuildParameters.IndexOf("--self-contained", StringComparison.InvariantCultureIgnoreCase) == -1) { arguments.Append(" --self-contained false "); } }); // This is here to not change existing behavior for the 2.0 and 2.1 runtimes. For those runtimes if // cshtml files are being used we need to support that cshtml being compiled at runtime. In order to do that we // need to not turn PreserveCompilationContext which provides reference assemblies to the runtime // compilation and not set a runtime. // // If there are no cshtml then disable PreserveCompilationContext to reduce package size and continue // to use the same runtime identifier that we used when those runtimes were launched. if (new string[] { "netcoreapp2.0", "netcoreapp2.1" }.Contains(targetFramework)) { if (Directory.GetFiles(fullProjectLocation, "*.cshtml", SearchOption.AllDirectories).Length == 0) { applyRuntimeSwitchAction(); if (string.IsNullOrEmpty(msbuildParameters) || !msbuildParameters.Contains("PreserveCompilationContext")) { _logger?.WriteLine("... Disabling compilation context to reduce package size. If compilation context is needed pass in the \"/p:PreserveCompilationContext=false\" switch."); arguments.Append(" /p:PreserveCompilationContext=false"); } } } else { applyRuntimeSwitchAction(); } // If we have a manifest of packages already deploy in target deployment environment then write it to disk and add the // command line switch if (publishManifests != null && publishManifests.Count > 0) { foreach (var manifest in publishManifests) { arguments.Append($" --manifest \"{manifest}\""); } } } // echo the full dotnet command for debug _logger?.WriteLine($"... dotnet {arguments}"); var psi = new ProcessStartInfo { FileName = dotnetCLI, Arguments = arguments.ToString(), WorkingDirectory = this._workingDirectory, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; var handler = (DataReceivedEventHandler)((o, e) => { if (string.IsNullOrEmpty(e.Data)) { return; } _logger?.WriteLine("... publish: " + e.Data); }); int exitCode; using (var proc = new Process()) { proc.StartInfo = psi; proc.Start(); proc.ErrorDataReceived += handler; proc.OutputDataReceived += handler; proc.BeginOutputReadLine(); proc.BeginErrorReadLine(); proc.EnableRaisingEvents = true; proc.WaitForExit(); exitCode = proc.ExitCode; } if (exitCode == 0) { ProcessAdditionalFiles(defaults, outputLocation); var chmodPath = FindExecutableInPath("chmod"); var touchPath = FindExecutableInPath("touch"); 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 files = Directory.GetFiles(outputLocation, "*", SearchOption.TopDirectoryOnly); foreach (var file in files) { var filename = Path.GetFileName(file); ApplyCommand(outputLocation, chmodPath, $"+rx \"{filename}\"", handler); ApplyCommand(outputLocation, touchPath, $"-a -m -t 201512180130.09 \"{filename}\"", handler); } } } return(exitCode); }