// Copyright Keysight Technologies 2012-2019
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at http://mozilla.org/MPL/2.0/.
using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
namespace Keysight.OpenTap.Sdk.MSBuild
{
public static class BuildType
{
public const string None = "None";
public const string Package = "Package";
}
public static class InstallType
{
public const string None = "None";
public const string PackageDef = "PackageDef";
}
///
/// MSBuild Task to help package plugin. This task is used by the OpenTAP SDK project template
///
[Serializable]
public class PackageTask : Task
{
[Required]
public string ConfFile { get; set; }
[Required]
public string Dir { get; set; }
public string Build { get; set; }
public string Install { get; set; }
public PackageTask()
{
Build = BuildType.Package;
Install = InstallType.PackageDef;
}
void confFileRun(string packagePath)
{
ProcessStartInfo info = new ProcessStartInfo(packagePath);
info.Arguments = String.Format("package create \"{0}\" --project-directory \"{1}\" -v", ConfFile, Directory.GetCurrentDirectory());
switch (Build)
{
case BuildType.Package:
info.Arguments += " -o \"\""; // Means "make tappackage with the correct name"
break;
}
switch (Install)
{
case InstallType.PackageDef:
if (Build != BuildType.Package)
info.Arguments += " -o \"\""; // Means "make tappackage with the correct name"
info.Arguments += " --install"; // Means "copy files not already present and output package xml to make it appear installed"
break;
}
info.WorkingDirectory = Dir;
Log.LogMessage("{0} {1}", packagePath, info.Arguments);
info.WindowStyle = ProcessWindowStyle.Normal;
info.RedirectStandardError = true;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
Process p = Process.Start(info);
// find the output filename from the stdout of the Tap.Package.exe process
// (we do this in a separate thread in parallel with reading stderr to avoid a potential deadlock)
string filename = null;
var task = System.Threading.Tasks.Task.Run(() =>
{
// Handle all non-error log messages
while (!p.StandardOutput.EndOfStream)
{
string line = p.StandardOutput.ReadLine();
// Catch the name of the generated file.
Regex sucessRegex = new Regex(@"TAP plugin package '(?[^']+)' containing '(?[^']+)' successfully created.");
Match m = sucessRegex.Match(line);
if (m.Success)
{
filename = m.Groups["filename"].Value;
}
// extract messages of type Warning, Information and Debug. These are messages coming from OpenTAP.
Regex msgRegex = new Regex(@":[ ]*(?.*)[ ]*:[ ]*(?Warning|Information|Debug)[ ]*:[ ]*(?.+)");
Match msgMatch = msgRegex.Match(line);
if (msgMatch.Success)
{
string source = msgMatch.Groups["source"].Value;
string type = msgMatch.Groups["type"].Value;
string message = msgMatch.Groups["msg"].Value;
switch(type)
{
case "Warning":
BuildEngine.LogWarningEvent(new BuildWarningEventArgs(source, "", ConfFile, 1, 1, 0, 0, message, "", "tap.exe"));
break;
case "Information":
BuildEngine.LogMessageEvent(new BuildMessageEventArgs(source, "", ConfFile, 1, 1, 0, 0, message, "", "tap.exe", MessageImportance.High));
break;
case "Debug":
Log.LogMessage(source, "1", "", ConfFile, 1, 1, 0, 0, MessageImportance.Normal, message);
break;
}
}
}
});
// Handle all error messages
while (!p.StandardError.EndOfStream)
{
string line = p.StandardError.ReadLine();
// Match Dotfuscator errors.
Regex dtofErrorRegex = new Regex(@"(?[^\(]+)\((?[0-9]+),(?[0-9]+)\): error: (?.+)");
Match dotfErrorMatch = dtofErrorRegex.Match(line);
if (dotfErrorMatch.Success)
{
int lineNum = int.Parse(dotfErrorMatch.Groups["line"].Value);
int columnNum = int.Parse(dotfErrorMatch.Groups["column"].Value);
string message = dotfErrorMatch.Groups["msg"].Value;
BuildErrorEventArgs errorEvent = new BuildErrorEventArgs("Xml", "", ConfFile, lineNum, columnNum, 0, 0, message, "", "tap.exe");
BuildEngine.LogErrorEvent(errorEvent);
}
else
{
// extract messages of type Error. These are messages coming from OpenTAP.
Regex tapErrorRegex = new Regex(@":[ ]*(?.*)[ ]*:[ ]*Error[ ]*:[ ]*(?.+)");
Match tapErrorMatch = tapErrorRegex.Match(line);
if (tapErrorMatch.Success)
{
string source = tapErrorMatch.Groups["source"].Value;
string message = tapErrorMatch.Groups["msg"].Value;
BuildEngine.LogErrorEvent(new BuildErrorEventArgs(source, "", ConfFile, 1, 1, 0, 0, message, "", "tap.exe"));
}
else
Log.LogError(line);
}
}
task.Wait();
p.WaitForExit();
// Make sure we log an error. If Tap.Package did not succeed
if (p.ExitCode == 0)
{
if(!string.IsNullOrEmpty(filename))
Log.LogMessage(MessageImportance.High, "Created package '{0}'", Path.Combine(Dir, filename));
else
Log.LogMessage(MessageImportance.High, "Created package in '{0}'", Dir);
}
else
Log.LogError("Error {1} creating package from {0}", ConfFile, p.ExitCode);
}
public override bool Execute()
{
string packagePath = GetTapExePath();
if (!File.Exists(packagePath))
{
Log.LogError("Cannot find OpenTAP installation. Is the OpenTAP SDK installed?");
return false;
}
if (ConfFile == null)
{
Log.LogError("Missing package cofiguration file (ConfFile).");
return false;
}
var be3 = BuildEngine as IBuildEngine3;
if (be3 != null) be3.Yield();
try
{
confFileRun(packagePath);
}
finally
{
if (be3 != null) be3.Reacquire();
}
return !Log.HasLoggedErrors;
}
private string GetTapExePath()
{
try
{
string installRootDir = Path.GetDirectoryName(this.GetType().Assembly.Location);
for(int i = 0; i<4;i++)
{
if (File.Exists(Path.Combine(installRootDir,"tap.exe")))
{
return Path.Combine(installRootDir, "tap.exe");
}
installRootDir = Path.GetDirectoryName(installRootDir);
}
}
catch
{
}
return null;
}
private string formatOutput(string output, bool appendVersionToOutput, string assemblyPath)
{
if (appendVersionToOutput == false)
return output;
string extension = Path.GetExtension(output);
string name = Path.GetFileNameWithoutExtension(output);
string version = FileVersionInfo.GetVersionInfo(assemblyPath).ProductVersion;
return String.Format("{0}.{1}{2}", name, version, extension);
}
}
}