using System;
using System.IO;
using System.Linq;
using System.Reflection;
using OpenTap;
namespace Keysight.OpenTap.Sdk.MSBuild
{
///
/// Build actions which require OpenTAP should enter this context before referencing any OpenTAP types.
/// This is necessary because the OpenTAP dll is not in the same directory as this dll.
/// In addition, this context initializes session logs with a specific name, and with
/// a flag that allows them to be deleted so e.g. bin/Debug can be deleted without errors.
///
class OpenTapContext : IDisposable
{
// The static portion of this type MUST NOT reference OpenTAP.
#region Static Members
private static object loadLock = new object();
private static void loadOpenTap(string tapDir, string runtimeDir)
{
// This alters the value returned by 'ExecutorClient.ExeDir' which would otherwise return the location of
// OpenTap.dll which in an MSBuild context would be the nuget directory which leads to unexpected behavior
// because the expected location is the build directory in all common use cases.
Environment.SetEnvironmentVariable("OPENTAP_INIT_DIRECTORY", tapDir, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("OPENTAP_NO_UPDATE_CHECK", "true");
Environment.SetEnvironmentVariable("OPENTAP_DEBUG_INSTALL", "true");
lock (loadLock)
{
var loaded = AppDomain.CurrentDomain.GetAssemblies().Select(l => l.GetName().Name).ToArray();
string[] assemblies = { "OpenTap", "OpenTap.Package" };
foreach (var asmName in assemblies)
{
if (loaded.Contains(asmName))
continue; // already loaded - continue
Assembly.LoadFrom(Path.Combine(runtimeDir, $"{asmName}.dll"));
}
}
}
///
/// Help the runtime resolve OpenTAP. The correct OpenTAP dll is in a subdirectory and will not be found by
/// the default resolver. Also, the resolver will look for the debug version (9.4.0.0) because that's what this
/// assembly was compiled against. This is sort of a hack, but it should be fine.
///
///
public static IDisposable Create(string tapDir, string runtimeDir)
{
loadOpenTap(tapDir, runtimeDir);
return new OpenTapContext(tapDir);
}
///
/// Increment a number until we find an available filename.
///
///
///
static string numberedFileName(string logName)
{
var logNameNoExt = Path.Combine(Path.GetDirectoryName(logName), Path.GetFileNameWithoutExtension(logName));
var num = 1;
while (File.Exists(logName))
{
logName = $"{logNameNoExt} ({num}).txt";
num += 1;
}
return logName;
}
#endregion
#region Instance Members
///
/// Calling this method forces the runtime to resolve OpenTAP because SessionLogs is from the OpenTAP assembly.
///
public OpenTapContext(string tapDir)
{
var buildProc = System.Diagnostics.Process.GetCurrentProcess();
var timestamp = buildProc.StartTime.ToString("yyyy-MM-dd HH-mm-ss");
var pid = buildProc.Id;
string pathEnding = $"SessionLog.{pid} {timestamp}";
if (Assembly.GetEntryAssembly() != null && !String.IsNullOrWhiteSpace(Assembly.GetEntryAssembly().Location))
{
string exeName = Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location);
// Path example: /SessionLogs/tap/tap .txt
pathEnding = $"{exeName}.{pid} {timestamp}";
}
var logName = Path.GetFullPath(Path.Combine(tapDir, "SessionLogs", pathEnding + ".txt"));
logName = numberedFileName(logName);
SessionLogs.Initialize(logName, true);
}
///
/// Ensure the session log is flushed when this context is disposed.
///
public void Dispose()
{
Log.Flush();
SessionLogs.Flush();
}
#endregion
}
}