in src/Amazon.Lambda.Tools/LambdaDotNetCLIWrapper.cs [152:354]
public int Publish(LambdaToolsDefaults defaults, string projectLocation, string outputLocation, string targetFramework, string configuration, string msbuildParameters, string architecture, 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, architecture)}");
}
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");
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);
var psiChmod = new ProcessStartInfo
{
FileName = chmodPath,
Arguments = "+rx \"" + filename + "\"",
WorkingDirectory = outputLocation,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using (var proc = new Process())
{
proc.StartInfo = psiChmod;
proc.Start();
proc.ErrorDataReceived += handler;
proc.OutputDataReceived += handler;
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
proc.EnableRaisingEvents = true;
proc.WaitForExit();
if (proc.ExitCode == 0)
{
this._logger?.WriteLine($"Changed permissions on published file (chmod +rx {filename}).");
}
}
}
}
}
return exitCode;
}