using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using Microsoft.Win32; namespace YekNebTasks { public static class Utilities { private static String sdksDirectory = String.Empty; private static String sevenZip = String.Empty; private static String cmake = String.Empty; private static String git = String.Empty; private static String patch = String.Empty; private static String python = String.Empty; public static String GetSDKsDirectory() { if (!String.IsNullOrEmpty(sdksDirectory)) { return sdksDirectory; } Assembly currentAssembly = Assembly.GetExecutingAssembly(); String currentAssemblyDirectory = Path.GetDirectoryName( currentAssembly.Location); String dir = currentAssemblyDirectory; do { String SDKsProj = Path.Combine(dir, "SDKs.proj"); if (File.Exists(SDKsProj)) { sdksDirectory = dir; break; } DirectoryInfo directoryInfo = Directory.GetParent(dir); dir = directoryInfo.FullName; } while (String.IsNullOrEmpty(sdksDirectory)); return sdksDirectory; } public static String FindFileOnPath(String FileName) { if (String.IsNullOrEmpty(FileName)) { return String.Empty; } String FoundFile = String.Empty; FileName = Environment.ExpandEnvironmentVariables(FileName); if (File.Exists(FileName)) { FoundFile = Path.GetFullPath(FileName); } else if (Path.GetDirectoryName(FileName) == String.Empty) { var path = Environment.GetEnvironmentVariable("PATH") ?? String.Empty; foreach (var dir in path.Split(Path.PathSeparator)) { var dirL = dir.Trim(); var fullPath = Path.GetFullPath(Path.Combine(dirL, FileName)); if (File.Exists(fullPath)) { FoundFile = fullPath; break; } } } return FoundFile; } public static String FindFileUsingCandidates( String[] candidates) { String FoundFile = String.Empty; foreach (String candidate in candidates) { String candidateL = Environment.ExpandEnvironmentVariables( candidate); if (File.Exists(candidateL)) { FoundFile = candidateL; break; } } return FoundFile; } public static String Find7za() { if (!String.IsNullOrEmpty(sevenZip)) { return sevenZip; } String SDKsDirectory = GetSDKsDirectory(); if (String.IsNullOrEmpty(SDKsDirectory)) { sevenZip = FindFileOnPath("7za.exe"); if (!File.Exists(sevenZip)) { sevenZip = FindFileOnPath("7z.exe"); } return sevenZip; } sevenZip = Path.Combine(SDKsDirectory, "tools", "7za.exe"); if (!File.Exists(sevenZip)) { sevenZip = FindFileOnPath("7za.exe"); } if (!File.Exists(sevenZip)) { sevenZip = FindFileOnPath("7z.exe"); } return sevenZip; } public static String FindCMake() { if (!String.IsNullOrEmpty(cmake)) { return cmake; } const String FileName = "cmake.exe"; String[] candidates = new String[]{ @"%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe", @"%ProgramFiles%\CMake\bin\cmake.exe", @"%ProgramW6432%\CMake\bin\cmake.exe", @"%SystemDrive%\msys64\mingw64\bin\cmake.exe", @"%SystemDrive%\msys64\mingw32\bin\cmake.exe", @"%SystemDrive%\msys64\usr\bin\cmake.exe" }; cmake = Utilities.FindFileUsingCandidates(candidates); if (String.IsNullOrEmpty(cmake)) { cmake = Utilities.FindFileOnPath(FileName); } return cmake; } public static String FindGit() { if (!String.IsNullOrEmpty(git)) { return git; } const String FileName = "git.exe"; String[] candidates = new String[]{ @"%ProgramFiles%\Git\cmd\git.exe", @"%ProgramW6432%\Git\cmd\git.exe", @"%SystemDrive%\msys64\mingw64\bin\git.exe", @"%SystemDrive%\msys64\mingw32\bin\git.exe", @"%SystemDrive%\msys64\usr\bin\git.exe", @"%SystemDrive%\MinGW\msys\1.0\bin\git.exe" }; git = Utilities.FindFileUsingCandidates(candidates); if (String.IsNullOrEmpty(git)) { git = Utilities.FindFileOnPath(FileName); } return git; } public static String FindPatch() { if (!String.IsNullOrEmpty(patch)) { return patch; } const String FileName = "patch.exe"; String[] candidates = new String[]{ @"%SystemDrive%\msys64\mingw64\bin\patch.exe", @"%SystemDrive%\msys64\mingw32\bin\patch.exe", @"%SystemDrive%\msys64\usr\bin\patch.exe", @"%SystemDrive%\MinGW\msys\1.0\bin\patch.exe" }; patch = Utilities.FindFileUsingCandidates(candidates); if (String.IsNullOrEmpty(patch)) { String SDKsDirectory = GetSDKsDirectory(); if (!String.IsNullOrEmpty(SDKsDirectory)) { var toolsDir = Path.Combine(SDKsDirectory, "tools"); var fullPath = Path.Combine(toolsDir, FileName); if (File.Exists(fullPath)) { patch = fullPath; } } } if (String.IsNullOrEmpty(patch)) { patch = Utilities.FindFileOnPath(FileName); } return patch; } public static String FindPython() { if (!String.IsNullOrEmpty(python)) { return python; } const String FileName = "python.exe"; String[] candidates = new String[]{ @"%ProgramFiles(x86)%\Microsoft Visual Studio\Shared\Python36_64\python.exe", @"%ProgramFiles%\Python36\python.exe", @"%ProgramW6432%\Python36\python.exe", @"%SystemDrive%\Python36\python3.6.exe", @"%SystemDrive%\Python36\python3.exe", @"%SystemDrive%\Python36\python.exe", @"%SystemDrive%\Python35\python3.5.exe", @"%SystemDrive%\Python35\python3.exe", @"%SystemDrive%\Python35\python.exe", @"%SystemDrive%\Python34\python3.4.exe", @"%SystemDrive%\Python34\python3.exe", @"%SystemDrive%\Python34\python.exe", @"%SystemDrive%\msys64\mingw64\bin\python3.6.exe", @"%SystemDrive%\msys64\mingw64\bin\python3.5.exe", @"%SystemDrive%\msys64\mingw64\bin\python3.exe", @"%SystemDrive%\msys64\mingw32\bin\python3.6.exe", @"%SystemDrive%\msys64\mingw32\bin\python3.5.exe", @"%SystemDrive%\msys64\mingw32\bin\python3.exe", @"%SystemDrive%\msys64\usr\bin\python3.6.exe", @"%SystemDrive%\msys64\usr\bin\python3.5.exe", @"%SystemDrive%\msys64\usr\bin\python3.exe" }; python = Utilities.FindFileUsingCandidates(candidates); if (String.IsNullOrEmpty(python)) { python = Utilities.FindFileOnPath(FileName); } return python; } public static String FindNMake() { String nmake = Utilities.FindFileOnPath("nmake.exe"); if (!String.IsNullOrEmpty(nmake)) { return nmake; } String visualStudioDir = GetVisualStudioInstallDirectory("15.0"); if ( String.IsNullOrEmpty(visualStudioDir) || !Directory.Exists(visualStudioDir) ) { visualStudioDir = GetVisualStudioInstallDirectory("14.0"); } if ( String.IsNullOrEmpty(visualStudioDir) || !Directory.Exists(visualStudioDir) ) { return String.Empty; } String vcDir = Path.Combine(visualStudioDir, "VC"); if (!Directory.Exists(vcDir)) { return String.Empty; } String[] foundFiles = Directory.GetFiles( vcDir, "nmake.exe", SearchOption.AllDirectories); if (foundFiles.Length == 0) { return String.Empty; } CultureInfo culture = CultureInfo.CurrentCulture; foreach (var candidate in foundFiles) { if (culture.CompareInfo.IndexOf(candidate, "x64", CompareOptions.IgnoreCase) >= 0) { nmake = candidate; } } return nmake; } public static String GetVisualStudioInstallDirectory(String version) { const String VS7RegKeyName = @"SOFTWARE\Microsoft\VisualStudio\SxS\VS7"; const String VS7RegKeyX64Name = @"SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7"; String regKeyName; if (Environment.Is64BitOperatingSystem) { regKeyName = VS7RegKeyX64Name; } else { regKeyName = VS7RegKeyName; } String installDirectory = String.Empty; using (RegistryKey regKey = Registry.LocalMachine.OpenSubKey(regKeyName)) { if (regKey != null) { installDirectory = regKey.GetValue(version) as String; } } return installDirectory; } } public class FindFileOnPath : Task { [Required] public String FileName { get; set; } [Output] public String FoundFile { get; set; } public override bool Execute() { var path = Environment.GetEnvironmentVariable("PATH") ?? String.Empty; FoundFile = Utilities.FindFileOnPath(FileName); Log.LogMessage( MessageImportance.Normal, "FindFileOnPath status: PATH = {0}; FoundFile = {1}", path, FoundFile); return true; } } public class Find7za : Task { [Output] public String FoundFile { get; set; } public override bool Execute() { FoundFile = Utilities.Find7za(); if ( !String.IsNullOrEmpty(FoundFile) && File.Exists(FoundFile) ) { return true; } return false; } } public class FindCMake : Task { [Output] public String FoundFile { get; set; } public override bool Execute() { FoundFile = Utilities.FindCMake(); if ( !String.IsNullOrEmpty(FoundFile) && File.Exists(FoundFile) ) { return true; } return false; } } public class FindGit : Task { [Output] public String FoundFile { get; set; } public override bool Execute() { FoundFile = Utilities.FindGit(); if ( !String.IsNullOrEmpty(FoundFile) && File.Exists(FoundFile) ) { return true; } return false; } } public class FindPatch : Task { [Output] public String FoundFile { get; set; } public override bool Execute() { FoundFile = Utilities.FindPatch(); if ( !String.IsNullOrEmpty(FoundFile) && File.Exists(FoundFile) ) { return true; } return false; } } public class FindPython : Task { [Output] public String FoundFile { get; set; } public override bool Execute() { FoundFile = Utilities.FindPython(); if ( !String.IsNullOrEmpty(FoundFile) && File.Exists(FoundFile) ) { return true; } return false; } } public class FindNMake : Task { [Output] public String FoundFile { get; set; } public override bool Execute() { FoundFile = Utilities.FindNMake(); if ( !String.IsNullOrEmpty(FoundFile) && File.Exists(FoundFile) ) { return true; } return false; } } public class GetVisualStudioInstallDirectory : Task { [Required] public String Version { get; set; } [Output] public String InstallDirectory { get; set; } public override bool Execute() { InstallDirectory = Utilities.GetVisualStudioInstallDirectory(Version); if ( !String.IsNullOrEmpty(InstallDirectory) && Directory.Exists(InstallDirectory) ) { return true; } return false; } } public class SetConsoleTitle : Task { [Required] public String Title { get; set; } public override bool Execute() { Console.Title = Title; return true; } } public class GetConsoleTitle : Task { [Output] public String Title { get; set; } public override bool Execute() { Title = Console.Title; return true; } } public class ExtractWith7za : Task { [Required] public String FileToExtract { get; set; } public String OutputDirectory { get; set; } public override bool Execute() { if (String.IsNullOrEmpty(OutputDirectory)) { OutputDirectory = Utilities.GetSDKsDirectory(); } if (String.IsNullOrEmpty(OutputDirectory)) { Log.LogError( "ExtractWith7za: No OutputDirectory."); return false; } if (!ExtractFile(FileToExtract)) { Log.LogError( "ExtractWith7za: Failed to extract '{0}.'", FileToExtract); return false; } if (!FileToExtract.Contains(".tar.")) { return true; } String fileName = Path.GetFileName(FileToExtract); int pos = fileName.LastIndexOf('.'); String tarFileName = fileName.Remove(pos); String tarFile = Path.Combine( OutputDirectory, tarFileName); if (File.Exists(tarFile)) { if (!ExtractFile(tarFile)) { Log.LogError( "ExtractWith7za: Failed to extract '{0}.'", tarFile); return false; } File.Delete(tarFile); } return true; } private String Build7zaCommandLine(String fileToExtract) { const String format = @"======================================== ExtractWith7za.Build7zaCommandLine: Command Line {0} ========================================"; CommandLineBuilder builder = new CommandLineBuilder(); builder.AppendSwitch("x"); builder.AppendFileNameIfNotNull(fileToExtract); builder.AppendSwitch("-aoa"); builder.AppendSwitch("-o\"" + OutputDirectory + "\""); String ret = builder.ToString(); Log.LogMessage( MessageImportance.Normal, format, ret); return ret; } private ProcessStartInfo GetProcessStartInfo(String fileToExtract) { String sevenZip = Utilities.Find7za(); if (String.IsNullOrEmpty(sevenZip)) { Log.LogError("ExtractWith7za: Failed to find 7za."); return null; } ProcessStartInfo procStartInfo = new ProcessStartInfo(); procStartInfo.CreateNoWindow = true; procStartInfo.FileName = sevenZip; procStartInfo.RedirectStandardOutput = true; procStartInfo.UseShellExecute = false; procStartInfo.Arguments = Build7zaCommandLine(fileToExtract); return procStartInfo; } private bool ExtractFile(String fileToExtract) { const String format = @"======================================== ExtractWith7za.ExtractFile: Output {0} ========================================"; if (String.IsNullOrEmpty(fileToExtract)) { Log.LogError( "ExtractWith7za error: fileToExtract is null or empty."); return false; } if (!File.Exists(fileToExtract)) { Log.LogError( "ExtractWith7za error: fileToExtract does not exist."); return false; } ProcessStartInfo procStartInfo = GetProcessStartInfo(fileToExtract); if (procStartInfo == null) { Log.LogError( "ExtractWith7za error: Failed to get process start info."); return false; } using (Process process = new Process()) { process.StartInfo = procStartInfo; process.Start(); process.WaitForExit(); if (process.ExitCode != 0) { Log.LogError( "ExtractWith7za error: The process failed with exit code {0}.", process.ExitCode); return false; } String output = process.StandardOutput.ReadToEnd(); Log.LogMessage( MessageImportance.Normal, format, output); } return true; } } }