PageRenderTime 62ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/pTkMain.cs

https://github.com/coapp-deprecated/_ptk_deprecated
C# | 999 lines | 683 code | 118 blank | 198 comment | 84 complexity | e2083e4fe2dd5492ab2b5913adcbeb8c MD5 | raw file
Possible License(s): Apache-2.0
  1. //-----------------------------------------------------------------------
  2. // <copyright company="CoApp Project">
  3. // Copyright (c) 2011 Garrett Serack. All rights reserved.
  4. // </copyright>
  5. // <license>
  6. // The software is licensed under the Apache 2.0 License (the "License")
  7. // You may not use the software except in compliance with the License.
  8. // </license>
  9. //-----------------------------------------------------------------------
  10. namespace CoApp.Ptk {
  11. using System;
  12. using System.Collections.Generic;
  13. using System.IO;
  14. using System.Linq;
  15. using System.Reflection;
  16. using Toolkit.Configuration;
  17. using Toolkit.Exceptions;
  18. using Toolkit.Extensions;
  19. using Toolkit.Scripting.Languages.PropertySheet;
  20. using Toolkit.Utility;
  21. internal class pTkMain {
  22. /// <summary>
  23. /// Help message for the user
  24. /// </summary>
  25. private const string help =
  26. @"
  27. Usage:
  28. -------
  29. pTK [options] action [buildconfiguration...]
  30. Options:
  31. --------
  32. --help this help
  33. --nologo don't display the logo
  34. --load-config=<file> loads configuration from <file>
  35. --verbose prints verbose messages
  36. --rescan-tools rescan for tool paths
  37. --show-tools prints the path of the tools
  38. --load=<file> loads the build ptk buildinfo
  39. defaults to .\COPKG\.buildinfo
  40. --mingw-install=<path> specifies the location of the mingw install
  41. --msys-install=<path> specifies the location of the msys install
  42. Actions:
  43. build builds the product
  44. clean removes all files that are not part of the
  45. project source
  46. status shows any files present that should not be
  47. verify ensures that the product source matches the
  48. built and cleaned
  49. trace performs a build using CoApp Trace to gather
  50. build data
  51. list lists availible builds from buildinfo
  52. [buildconfiguration] optional; indicates the builds from the
  53. buildinfo file to act on. Defaults to all
  54. ";
  55. /// <summary>
  56. /// Wrapper for the Windows command line
  57. /// </summary>
  58. private ProcessUtility _cmdexe;
  59. /// <summary>
  60. /// Wrapper for git (source control)
  61. /// </summary>
  62. private ProcessUtility _gitexe;
  63. /// <summary>
  64. /// Wrapper for mercurial (source control)
  65. /// </summary>
  66. private ProcessUtility _hgexe;
  67. /// <summary>
  68. /// Wrapper for pTk (That's us!)
  69. /// </summary>
  70. private ProcessUtility _ptk;
  71. /// <summary>
  72. /// Wrapper for Trace. (Trace tells us what the build process does)
  73. /// </summary>
  74. private ProcessUtility _traceexe;
  75. private string _gitcmd;
  76. // private string _setenvcmd;
  77. // private string _vcvars;
  78. /// <summary>
  79. /// Command line to git.cmd
  80. /// </summary>
  81. // sdk batch file locations
  82. private string _setenvcmd71;
  83. private string _setenvcmd7;
  84. private string _setenvcmd6;
  85. /* private string _setenvcmdFeb2003; */
  86. private string _wdksetenvcmd7600;
  87. // compiler batch file locations
  88. private string _vcvarsallbat10;
  89. private string _vcvarsallbat9;
  90. private string _vcvarsallbat8;
  91. private string _vcvarsallbat7;
  92. private string _vcvarsallbat71;
  93. private string _vcvars32bat;
  94. private bool _useGit;
  95. private bool _useHg;
  96. /// <summary>
  97. /// Does the user want us to print more?
  98. /// </summary>
  99. private bool _verbose;
  100. private readonly Dictionary<string, string> _originalEnvironment = GetEnvironment();
  101. /// <summary>
  102. /// Tell the user which tools we are using?
  103. /// </summary>
  104. private bool _showTools;
  105. /// <summary>
  106. /// A list of temporary files for bookkeeping
  107. /// </summary>
  108. private readonly List<string> _tmpFiles= new List<string>();
  109. private string _searchPaths = "";
  110. /// <summary>
  111. /// Entry Point
  112. /// </summary>
  113. /// <param name="args"></param>
  114. /// <returns></returns>
  115. private static int Main(string[] args) {
  116. return new pTkMain().main(args);
  117. }
  118. /// <summary>
  119. /// Get the environment variables (key/value pairs)
  120. /// </summary>
  121. /// <remarks>
  122. /// Path variable may differ in output from actual path on some systems
  123. /// Run «reg query "hklm\system\currentcontrolset\control\Session manager\Environment" /v path» to verify
  124. /// Character limit for path on Vista is 1024 http://support.microsoft.com/kb/924032
  125. /// </remarks>
  126. /// <returns>A dictionary of path variables as strings</returns>
  127. private static Dictionary<string, string> GetEnvironment() {
  128. var env = Environment.GetEnvironmentVariables();
  129. return env.Keys.Cast<object>().ToDictionary(key => key.ToString(), key => env[key].ToString());
  130. }
  131. /// <summary>
  132. /// Resets application Environment
  133. /// </summary>
  134. private void ResetEnvironment() {
  135. foreach( var key in Environment.GetEnvironmentVariables().Keys ) {
  136. Environment.SetEnvironmentVariable(key.ToString(),string.Empty);
  137. }
  138. foreach (var key in _originalEnvironment.Keys) {
  139. Environment.SetEnvironmentVariable(key, _originalEnvironment[key]);
  140. }
  141. }
  142. private void SetVCCompiler(string compilerName, string compilerBatchFile, string arch) {
  143. using (new ConsoleColors(ConsoleColor.White, ConsoleColor.Black)) {
  144. Console.Write("Setting VC Compiler: ");
  145. }
  146. using (new ConsoleColors(ConsoleColor.Green, ConsoleColor.Black)) {
  147. Console.Write(compilerName);
  148. }
  149. using (new ConsoleColors(ConsoleColor.Yellow, ConsoleColor.Black)) {
  150. Console.WriteLine(" for [{0}]", arch);
  151. }
  152. if (string.IsNullOrEmpty(compilerBatchFile))
  153. throw new CoAppException("Cannot locate Visual C++ vcvars batch file command. Please install {0} (and use --rescan-tools). ".format(compilerName));
  154. _cmdexe.Exec(@"/c ""{0}"" /{1} & set ", compilerBatchFile, arch == "x86" ? "x86" : "x64");
  155. foreach (var x in _cmdexe.StandardOut.Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)) {
  156. if (x.Contains("=")) {
  157. var v = x.Split('=');
  158. Environment.SetEnvironmentVariable(v[0], v[1]);
  159. // Console.WriteLine("Setting ENV: [{0}]=[{1}]", v[0], v[1]);
  160. }
  161. }
  162. }
  163. private void SetSDK(string sdkName, string sdkBatchFile, string arch) {
  164. using (new ConsoleColors(ConsoleColor.White, ConsoleColor.Black)) {
  165. Console.Write("Setting SDK: ");
  166. }
  167. using (new ConsoleColors(ConsoleColor.Green, ConsoleColor.Black)) {
  168. Console.Write(sdkName);
  169. }
  170. using (new ConsoleColors(ConsoleColor.Yellow, ConsoleColor.Black)) {
  171. Console.WriteLine(" for [{0}]", arch);
  172. }
  173. var targetCpu = Environment.GetEnvironmentVariable("TARGET_CPU");
  174. if (string.IsNullOrEmpty(targetCpu) || (targetCpu == "x64" && arch == "x86") || (targetCpu == "x86" && arch != "x86")) {
  175. if (string.IsNullOrEmpty(sdkBatchFile))
  176. throw new CoAppException("Cannot locate SDK SetEnv command for SDK ({0}). Please install the Windows SDK {0}".format(sdkName));
  177. // Console.WriteLine(@"/c ""{0}"" /{1} & set ", _setenvcmd, arch == "x86" ? "x86" : "x64");
  178. _cmdexe.Exec(@"/c ""{0}"" /{1} & set ", sdkBatchFile, arch == "x86" ? "x86" : "x64");
  179. foreach (var x in _cmdexe.StandardOut.Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
  180. if (x.Contains("=")) {
  181. var v = x.Split('=');
  182. Environment.SetEnvironmentVariable(v[0], v[1]);
  183. // Console.WriteLine("Setting ENV: [{0}]=[{1}]", v[0], v[1]);
  184. }
  185. /*
  186. targetCpu = Environment.GetEnvironmentVariable("TARGET_CPU");
  187. if (string.IsNullOrEmpty(targetCpu) || (targetCpu == "x64" && arch == "x86") || (targetCpu == "x86" && arch != "x86")) {
  188. Console.WriteLine("Arch: {0}", arch);
  189. Console.WriteLine("TargetCPU: {0}", targetCpu);
  190. throw new CoAppException("Cannot set the SDK environment. Please install the Windows SDK ({0}) and use the setenv.cmd command to set your environment".format(sdkName));
  191. }
  192. */
  193. }
  194. }
  195. /// <summary>
  196. /// Set up environment and paths to use mingw
  197. /// </summary>
  198. /// <param name="arch">A string indicating the target platform</param>
  199. private void SetMingwCompiler( string arch) {
  200. using (new ConsoleColors(ConsoleColor.White, ConsoleColor.Black)) {
  201. Console.Write("Setting Compiler: ");
  202. }
  203. using (new ConsoleColors(ConsoleColor.Green, ConsoleColor.Black)) {
  204. Console.Write("mingw");
  205. }
  206. using (new ConsoleColors(ConsoleColor.Yellow, ConsoleColor.Black)) {
  207. Console.WriteLine(" for [{0}]", arch);
  208. }
  209. var mingwProgramFinder = new ProgramFinder("", Directory.GetDirectories(@"c:\\", "M*").Aggregate(_searchPaths+@"%ProgramFiles(x86)%;%ProgramFiles%;%ProgramW6432%", (current, dir) => dir + ";" + current));
  210. var gcc = mingwProgramFinder.ScanForFile("mingw32-gcc.exe");
  211. var msysmnt = mingwProgramFinder.ScanForFile("msysmnt.exe");
  212. if( string.IsNullOrEmpty(gcc)) {
  213. throw new ConsoleException("Unable to locate MinGW install location. Use --mingw-install=<path>\r\n (it will remember after that.)");
  214. }
  215. if (string.IsNullOrEmpty(msysmnt)) {
  216. throw new ConsoleException("Unable to locate MSYS install location. Use --msys-install=<path>\r\n (it will remember after that.)");
  217. }
  218. var msysBin = Path.GetDirectoryName(msysmnt);
  219. var msysPath = Path.GetDirectoryName(msysBin);
  220. var msysLocalBin = Path.Combine(msysPath, "local", "bin");
  221. var mingwBin = Path.GetDirectoryName(gcc);
  222. var mingwPath = Path.GetDirectoryName(mingwBin);
  223. var username = Environment.GetEnvironmentVariable("USERNAME") ?? "";
  224. var newPath = ".;" + mingwBin + ";" + msysBin + ";" + msysLocalBin + ";" + Environment.GetEnvironmentVariable("PATH");
  225. Environment.SetEnvironmentVariable("PATH", newPath);
  226. var tmpPath = Environment.GetEnvironmentVariable("TMP") ??
  227. Environment.GetEnvironmentVariable("TEMP") ??
  228. Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Temp").Replace("\\", "/");
  229. Environment.SetEnvironmentVariable("TMP", tmpPath);
  230. Environment.SetEnvironmentVariable("TEMP", tmpPath);
  231. Environment.SetEnvironmentVariable("WD", msysBin);
  232. Environment.SetEnvironmentVariable("TERM", "cygwin");
  233. var homedir = Environment.GetEnvironmentVariable("HOME");
  234. if( string.IsNullOrEmpty(homedir) ) {
  235. homedir = Path.Combine(Path.Combine(msysPath, "home"), username);
  236. if (!Directory.Exists(homedir)) {
  237. homedir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
  238. }
  239. Environment.SetEnvironmentVariable("HOME", homedir.Replace("\\", "/"));
  240. }
  241. Environment.SetEnvironmentVariable("HISTFILE", Path.Combine(homedir, ".bashhistory").Replace("\\", "/"));
  242. Environment.SetEnvironmentVariable("LOGNAME", username);
  243. Environment.SetEnvironmentVariable("MAKE_MODE", "unix");
  244. Environment.SetEnvironmentVariable("MSYSCON", "sh.exe");
  245. Environment.SetEnvironmentVariable("MSYSTEM", "MINGW32");
  246. }
  247. /// <summary>
  248. /// Change the designated compiler
  249. /// </summary>
  250. private void SwitchCompiler(string compiler, string platform) {
  251. switch( compiler ) {
  252. case "vc10":
  253. SetVCCompiler("Visual Studio 2010", _vcvarsallbat10, platform);
  254. break;
  255. case "vc9":
  256. SetVCCompiler("Visual Studio 2008", _vcvarsallbat9, platform);
  257. break;
  258. case "vc8":
  259. SetVCCompiler("Visual Studio 2005", _vcvarsallbat8, platform);
  260. break;
  261. case "vc7.1":
  262. SetVCCompiler("Visual Studio 2003", _vcvarsallbat71, platform);
  263. break;
  264. case "vc7":
  265. SetVCCompiler("Visual Studio 2002", _vcvarsallbat7, platform);
  266. break;
  267. case "vc6":
  268. SetVCCompiler("Visual Studio 98 (vc6)", _vcvars32bat, platform);
  269. break;
  270. case "sdk7.1":
  271. SetSDK("Windows Sdk 7.1", _setenvcmd71, platform);
  272. break;
  273. case "sdk7":
  274. SetSDK("Windows Sdk 7", _setenvcmd7, platform);
  275. break;
  276. case "sdk6":
  277. SetSDK("Windows Sdk 6", _setenvcmd6, platform);
  278. break;
  279. /*
  280. case "wdk7600":
  281. var wdkFolder = RegistryView.System[@"SOFTWARE\Wow6432Node\Microsoft\WDKDocumentation\7600.091201\Setup", "Build"].Value as string;
  282. if (string.IsNullOrEmpty(wdkFolder)) {
  283. wdkFolder = RegistryView.System[@"SOFTWARE\Microsoft\WDKDocumentation\7600.091201\Setup", "Build"].Value as string;
  284. }
  285. // C:\WinDDK\7600.16385.1\ fre x86 WIN7
  286. SetSDK("Windows WDK 7600", _wdksetenvcmd7600, platform);
  287. break;
  288. */
  289. case "mingw":
  290. SetMingwCompiler(platform);
  291. break;
  292. default :
  293. throw new ConsoleException("Unknown Compiler Selection: {0}", compiler);
  294. }
  295. }
  296. private void SwitchSdk( string sdk, string platform ) {
  297. switch (sdk) {
  298. case "sdk7.1":
  299. SetSDK("Windows Sdk 7.1", _setenvcmd71, platform);
  300. break;
  301. case "sdk7":
  302. SetSDK("Windows Sdk 7", _setenvcmd7, platform);
  303. break;
  304. case "sdk6":
  305. SetSDK("Windows Sdk 6", _setenvcmd6, platform);
  306. break;
  307. case "feb2003":
  308. SetSDK("Platform SDK Feb 2003", _setenvcmd6, platform);
  309. break;
  310. case "wdk7600":
  311. SetSDK("Windows WDK 7600", _wdksetenvcmd7600, platform);
  312. break;
  313. case "none":
  314. break;
  315. default:
  316. throw new ConsoleException("Unknown Compiler Selection: {0}", sdk);
  317. }
  318. }
  319. /// <summary>
  320. /// This is the main procedure
  321. /// </summary>
  322. /// <param name="args">Command line parameters</param>
  323. /// <returns>Error codes (0 for success, non-zero on Error)</returns>
  324. private int main(IEnumerable<string> args) {
  325. var options = args.Switches();
  326. var parameters = args.Parameters();
  327. var tempBuildinfo = (from a in @".\COPKG\".DirectoryEnumerateFilesSmarter("*.buildinfo", SearchOption.TopDirectoryOnly)
  328. orderby a.Length ascending
  329. select a.GetFullPath()).FirstOrDefault();
  330. // find PropertySheet location
  331. //we'll just use the default even though it won't work so I don't need to change the code much :)
  332. var buildinfo = tempBuildinfo ?? @".\COPKG\.buildinfo".GetFullPath();
  333. Console.CancelKeyPress += (x, y) => {
  334. Console.WriteLine("Stopping ptk.");
  335. if (_cmdexe != null)
  336. _cmdexe.Kill();
  337. if (_gitexe != null)
  338. _gitexe.Kill();
  339. if (_hgexe != null)
  340. _hgexe.Kill();
  341. if (_ptk != null)
  342. _ptk.Kill();
  343. if (_traceexe != null) {
  344. _traceexe.Kill();
  345. }
  346. };
  347. #region Parse Options
  348. // set up options which were defined by the user
  349. foreach (string arg in options.Keys) {
  350. IEnumerable<string> argumentParameters = options[arg];
  351. switch (arg) {
  352. case "nologo":
  353. // disable logo (will print "\n" anyway)
  354. this.Assembly().SetLogo("");
  355. break;
  356. case "verbose":
  357. _verbose = true;
  358. break;
  359. case "load":
  360. // user specified a custom PropertySheet
  361. buildinfo = argumentParameters.LastOrDefault().GetFullPath();
  362. break;
  363. case "mingw-install":
  364. case "msys-install":
  365. _searchPaths += argumentParameters.LastOrDefault().GetFullPath() + ";";
  366. break;
  367. case "rescan-tools":
  368. ProgramFinder.IgnoreCache = true;
  369. break;
  370. case "show-tools":
  371. _showTools = true;
  372. break;
  373. case "help":
  374. return Help();
  375. }
  376. }
  377. if (!File.Exists(buildinfo)) {
  378. return Fail("Unable to find buildinfo file [{0}]. \r\n\r\n Use --help for command line help.", buildinfo);
  379. }
  380. // make sure that we're in the parent directory of the .buildinfo file.
  381. Environment.CurrentDirectory = Path.GetDirectoryName(Path.GetDirectoryName(buildinfo));
  382. // tell the user what we are
  383. Logo();
  384. // tell the user we can't work without instructions
  385. if (parameters.Count() < 1) {
  386. return Fail("Missing action . \r\n\r\n Use --help for command line help.");
  387. }
  388. #endregion
  389. // set up several tools we need
  390. _cmdexe = new ProcessUtility("cmd.exe");
  391. var f = new ProgramFinder("").ScanForFile("trace.exe");
  392. if(!string.IsNullOrEmpty(f)) {
  393. _traceexe = new ProcessUtility(new ProgramFinder("").ScanForFile("trace.exe"));
  394. }
  395. _ptk = new ProcessUtility(Assembly.GetEntryAssembly().Location);
  396. // if this package is tracked by git, we can use git
  397. _useGit = Directory.Exists(".git".GetFullPath()); // if this package is tracked by mercurial, we can use mercurial
  398. _useHg = _useGit ? false : Directory.Exists(".hg".GetFullPath());
  399. // source control is mandatory! create a repository for this package
  400. if (!(_useGit || _useHg)) {
  401. return Fail("Source must be checked out using git or hg-git.");
  402. }
  403. // find git in the file system
  404. // - we prefer the CMD script over git.exe
  405. // git.exe may be located at "C:\Program Files\Git\bin"
  406. // git.cmd may be located at "C:\Program Files\Git\cmd"
  407. if (_useGit) {
  408. if (_verbose) {
  409. Console.WriteLine("Using git for verification");
  410. }
  411. // attemt to find git.cmd
  412. _gitcmd = ProgramFinder.ProgramFilesAndDotNet.ScanForFile("git.cmd");
  413. _gitexe = null;
  414. if (string.IsNullOrEmpty(_gitcmd)) {
  415. f = ProgramFinder.ProgramFilesAndDotNet.ScanForFile("git.exe");
  416. if (string.IsNullOrEmpty(f)) {
  417. return Fail("Can not find git.cmd or git.exe (required to perform verification.)");
  418. }
  419. _gitexe = new ProcessUtility(ProgramFinder.ProgramFilesAndDotNet.ScanForFile("git.exe"));
  420. }
  421. }
  422. if (_useHg) {
  423. f = ProgramFinder.ProgramFilesAndDotNet.ScanForFile("hg.exe");
  424. if (string.IsNullOrEmpty(f)) {
  425. return Fail("Can not find hg.exe (required to perform verification.)");
  426. }
  427. _hgexe = new ProcessUtility(f);
  428. }
  429. // find sdk batch files.
  430. _setenvcmd71 = ProgramFinder.ProgramFilesAndDotNetAndSdk.ScanForFile("setenv.cmd", excludeFilters: new[] { @"\Windows Azure SDK\**" , "winddk**" }, includeFilters: new [] {"sdk**", "v7.1**"}, rememberMissingFile:true, tagWithCosmeticVersion:"7.1");
  431. _setenvcmd7 = ProgramFinder.ProgramFilesAndDotNetAndSdk.ScanForFile("setenv.cmd", excludeFilters: new[] { @"\Windows Azure SDK\**", "7.1**", "winddk**" }, includeFilters: new[] { "sdk**", "v7**" }, rememberMissingFile: true, tagWithCosmeticVersion: "7.0");
  432. _setenvcmd6 = ProgramFinder.ProgramFilesAndDotNetAndSdk.ScanForFile("setenv.cmd", excludeFilters: new[] { @"\Windows Azure SDK\**", "winddk**" }, includeFilters: new[] { "sdk**", "6**" }, rememberMissingFile: true, tagWithCosmeticVersion: "6");
  433. _wdksetenvcmd7600 = ProgramFinder.ProgramFilesAndDotNetAndSdk.ScanForFile("setenv.bat", excludeFilters: new[] { @"\Windows Azure SDK\**"}, includeFilters: new[] { "winddk**" }, rememberMissingFile: true, tagWithCosmeticVersion: "7600.16385.1");
  434. _vcvarsallbat10 = ProgramFinder.ProgramFilesAndDotNetAndSdk.ScanForFile("vcvarsall.bat", includeFilters: new[] { "vc**", "10.0**" }, rememberMissingFile: true, tagWithCosmeticVersion: "10.0");
  435. _vcvarsallbat9 = ProgramFinder.ProgramFilesAndDotNetAndSdk.ScanForFile("vcvarsall.bat", includeFilters: new[] { "vc**", "9.0**" }, rememberMissingFile: true, tagWithCosmeticVersion: "9.0");
  436. _vcvarsallbat8 = ProgramFinder.ProgramFilesAndDotNetAndSdk.ScanForFile("vcvarsall.bat", includeFilters: new[] { "vc**", "8.0**" }, rememberMissingFile: true, tagWithCosmeticVersion: "8.0");
  437. _vcvarsallbat7 = ProgramFinder.ProgramFilesAndDotNetAndSdk.ScanForFile("vcvarsall.bat", includeFilters: new[] { "vc**", "7.0**" }, rememberMissingFile: true, tagWithCosmeticVersion: "7.0");
  438. _vcvarsallbat71 = ProgramFinder.ProgramFilesAndDotNetAndSdk.ScanForFile("vcvarsall.bat", includeFilters: new[] { "vc**", "7.1**" }, rememberMissingFile: true, tagWithCosmeticVersion: "7.1");
  439. _vcvars32bat = ProgramFinder.ProgramFilesAndDotNetAndSdk.ScanForFile("vcvars32.bat", includeFilters: new[] { "vc98**" }, rememberMissingFile: true, tagWithCosmeticVersion: "6");
  440. if (_showTools) {
  441. if (_useGit) {
  442. Console.Write("Git: {0}", _gitcmd ?? "");
  443. if (_gitexe != null) {
  444. Console.WriteLine(_gitexe.Executable ?? "");
  445. }
  446. }
  447. if (_useHg) {
  448. Console.WriteLine("hg: {0}", _hgexe.Executable);
  449. }
  450. Console.WriteLine("SDK Setenv (7.1): {0}", _vcvarsallbat10 ?? "Not-Found");
  451. Console.WriteLine("SDK Setenv (7.0): {0}", _setenvcmd7 ?? "Not-Found");
  452. Console.WriteLine("SDK Setenv (6): {0}", _setenvcmd6 ?? "Not-Found");
  453. Console.WriteLine("VC vcvarsall (10.0): {0}", _vcvarsallbat10 ?? "Not-Found");
  454. Console.WriteLine("VC vcvarsall (9.0): {0}", _vcvarsallbat9 ?? "Not-Found");
  455. Console.WriteLine("VC vcvarsall (8.0): {0}", _vcvarsallbat8 ?? "Not-Found");
  456. Console.WriteLine("VC vcvarsall (7.0): {0}", _vcvarsallbat7 ?? "Not-Found");
  457. Console.WriteLine("VC vcvarsall (7.1): {0}", _vcvarsallbat71 ?? "Not-Found");
  458. Console.WriteLine("VC vcvars32 (6): {0}", _vcvars32bat ?? "Not-Found");
  459. Console.WriteLine("ptk: {0}", _ptk.Executable);
  460. Console.WriteLine("trace: {0}", _traceexe.Executable);
  461. }
  462. // load property sheet (that is the .buildinfo file by default)
  463. PropertySheet propertySheet = null;
  464. try {
  465. // load and parse. propertySheet will contain everything else we need for later
  466. propertySheet = PropertySheet.Load(buildinfo);
  467. propertySheet.GetMacroValue += (valueName) => {
  468. return null;
  469. };
  470. propertySheet.GetCollection += (collectionName) => {
  471. return Enumerable.Empty<object>();
  472. };
  473. }
  474. catch (EndUserParseException pspe) {
  475. using (new ConsoleColors(ConsoleColor.Yellow, ConsoleColor.Black)) {
  476. Console.Write(pspe.Message);
  477. Console.WriteLine("--found '{0}'", pspe.Token.Data);
  478. }
  479. return Fail("Error parsing .buildinfo file");
  480. }
  481. var builds = from rule in propertySheet.Rules where rule.Name != "*" select rule;
  482. if (parameters.Count() > 1) {
  483. var allbuilds = builds;
  484. builds = parameters.Skip(1).Aggregate(Enumerable.Empty<Rule>(), (current, p) => current.Union(from build in allbuilds where build.Name.IsWildcardMatch(p) select build));
  485. }
  486. // are there even builds present?
  487. if(builds.Count() == 0 ) {
  488. return Fail("No valid build configurations selected.");
  489. }
  490. // do the user's bidding
  491. try {
  492. switch (parameters.FirstOrDefault().ToLower()) {
  493. case "build":
  494. Build(builds);
  495. break;
  496. case "clean":
  497. Clean(builds);
  498. using (new ConsoleColors(ConsoleColor.White, ConsoleColor.Black)) {
  499. Console.WriteLine("Project Cleaned.");
  500. }
  501. break;
  502. case "verify":
  503. Clean(builds); // clean up other builds in the list first.
  504. Verify(builds);
  505. using (new ConsoleColors(ConsoleColor.White, ConsoleColor.Black)) {
  506. Console.WriteLine("Project Verified.");
  507. }
  508. break;
  509. case "status":
  510. Status(builds);
  511. using (new ConsoleColors(ConsoleColor.White, ConsoleColor.Black)) {
  512. Console.WriteLine("Project is in clean state.");
  513. }
  514. break;
  515. case "trace":
  516. Trace(builds);
  517. break;
  518. case "list":
  519. Console.WriteLine("Buildinfo from [{0}]", buildinfo);
  520. (from build in builds
  521. let compiler = build["compiler"]
  522. let sdk = build["sdk"]
  523. let platform = build["platform"]
  524. let targets = build["targets"]
  525. select new {
  526. Configuration = build.Name,
  527. Compiler = compiler != null ? compiler.Value : "sdk7.1",
  528. Sdk = sdk != null ? sdk.Value : "sdk7.1",
  529. Platform = platform != null ? platform.Value : "x86",
  530. Number_of_Outputs = targets != null ? targets.Values.Count() : 0
  531. }).ToTable().ConsoleOut();
  532. break;
  533. default:
  534. return Fail("'{0}' is not a valid command. \r\n\r\n Use --help for assistance.");
  535. }
  536. }
  537. catch (ConsoleException e) {
  538. // these exceptions are expected
  539. return Fail(" {0}", e.Message);
  540. }
  541. catch (Exception e) {
  542. // it's probably okay to crash within proper commands (because something else crashed)
  543. Console.WriteLine(e.StackTrace);
  544. return Fail(" {0}", e.Message);
  545. }
  546. return 0;
  547. }
  548. /// <summary>
  549. /// Traces the changes made by a specific script
  550. /// </summary>
  551. /// <param name="script">Script to trace</param>
  552. /// <param name="traceFile">An output file</param>
  553. private void TraceExec( string script, string traceFile ) {
  554. // multiline scripts need to be executed with a temporary script,
  555. // everything else runs directly from the cmd prompt
  556. if (script.Contains("\r") || script.Contains("\n") ) {
  557. script =
  558. @"@echo off
  559. @setlocal
  560. {1}:
  561. @cd ""{0}""
  562. ".format(Environment.CurrentDirectory, Environment.CurrentDirectory[0]) + script;
  563. var scriptpath = WriteTempScript(script);
  564. _traceexe.ExecNoRedirections(@"--nologo ""--output-file={1}"" cmd.exe /c ""{0}""", scriptpath, traceFile);
  565. }
  566. else {
  567. _traceexe.ExecNoRedirections(@"--nologo ""--output-file={1}"" cmd.exe /c ""{0}""", script, traceFile);
  568. }
  569. }
  570. /// <summary>
  571. /// Create a temporary .cmd file
  572. /// </summary>
  573. /// <param name="text">The script to be written into the .cmd file</param>
  574. /// <returns>Full path to the temporary script</returns>
  575. private string WriteTempScript(string text) {
  576. var tmpFilename = "ptk_script".GenerateTemporaryFilename();
  577. _tmpFiles.Add(tmpFilename);
  578. // append proper file extension
  579. tmpFilename += ".cmd";
  580. _tmpFiles.Add(tmpFilename);
  581. File.WriteAllText(tmpFilename, text);
  582. return tmpFilename;
  583. }
  584. /// <summary>
  585. /// Runs a command line script
  586. /// </summary>
  587. /// <param name="script">A command line script</param>
  588. private void Exec(string script) {
  589. // multiline scripts need prepration,
  590. // everything else can be run straight from cmd
  591. if (script.Contains("\r") || script.Contains("\n") ) {
  592. // set up environment for the script
  593. script =
  594. @"@echo off
  595. @setlocal
  596. {1}:
  597. @cd ""{0}""
  598. ".format(Environment.CurrentDirectory, Environment.CurrentDirectory[0]) + script;
  599. // tell the user what we are about to run
  600. //Console.WriteLine(script);
  601. // create temporary file
  602. var scriptpath = WriteTempScript(script);
  603. // run it
  604. _cmdexe.ExecNoRedirections(@"/c ""{0}""", scriptpath);
  605. }
  606. else {
  607. // run script
  608. _cmdexe.ExecNoRedirections(@"/c ""{0}""", script);
  609. }
  610. // handle error conditions
  611. if( _cmdexe.ExitCode != 0 ) {
  612. throw new ConsoleException("Command Exited with value {0}", _cmdexe.ExitCode);
  613. }
  614. }
  615. private void SetCompilerSdkAndPlatform( Rule build ) {
  616. ResetEnvironment();
  617. var compilerProperty = build["compiler"];
  618. var compiler = compilerProperty != null ? compilerProperty.Value : "sdk7.1";
  619. var sdkProperty = build["sdk"];
  620. var sdk = sdkProperty != null ? sdkProperty.Value : "sdk7.1";
  621. var platformProperty = build["platform"];
  622. var platform = platformProperty != null ? platformProperty.Value : "x86";
  623. if (!compiler.Contains("sdk") && !compiler.Contains("wdk")) {
  624. SwitchSdk(sdk, platform);
  625. }
  626. SwitchCompiler(compiler,platform);
  627. }
  628. private void Clean(IEnumerable<Rule> builds) {
  629. foreach( var build in builds ) {
  630. SetCompilerSdkAndPlatform(build);
  631. var cmd = build["clean-command"];
  632. if( cmd == null )
  633. throw new ConsoleException("missing clean command in build {0}",build.Name);
  634. try {
  635. Exec(cmd.Value);
  636. } catch
  637. {
  638. //ignoring any failures from clean command.
  639. }
  640. File.Delete(Path.Combine(Environment.CurrentDirectory, "trace[{0}].xml".format(build.Name)));
  641. }
  642. }
  643. /// <summary>
  644. /// Builds all dependencies listed in a given build rule
  645. /// </summary>
  646. /// <param name="build">A build rule to which the dependencies should be built</param>
  647. private void BuildDependencies(Rule build) {
  648. // save current directory
  649. var pwd = Environment.CurrentDirectory;
  650. var uses = build["uses"];
  651. if (uses != null) {
  652. foreach (var useLabel in uses.Labels) {
  653. var use = build["uses"][useLabel];
  654. var config = string.Empty;
  655. var folder = string.Empty;
  656. // set folder and configuration as needed
  657. config = useLabel;
  658. folder = use.Value;
  659. folder = folder.GetFullPath();
  660. if (!Directory.Exists(folder)) {
  661. throw new ConsoleException("Dependency project [{0}] does not exist.", folder);
  662. }
  663. var depBuildinfo = Path.Combine(folder, @"copkg\.buildinfo");
  664. if (!File.Exists(depBuildinfo)) {
  665. throw new ConsoleException("Dependency project is missing buildinfo [{0}]", depBuildinfo);
  666. }
  667. // switch project directory
  668. Environment.CurrentDirectory = folder;
  669. // build dependency project
  670. _ptk.ExecNoRedirections("--nologo build {0}", config);
  671. if (_ptk.ExitCode != 0)
  672. throw new ConsoleException(
  673. "Dependency project failed to build [{0}] config={1}", depBuildinfo, string.IsNullOrEmpty(config) ? "all" : config);
  674. // reset directory to where we came from
  675. Environment.CurrentDirectory = pwd;
  676. }
  677. }
  678. }
  679. /// <summary>
  680. /// Runs build rules
  681. /// </summary>
  682. /// <param name="builds">A list of build rules to build</param>
  683. private void Build(IEnumerable<Rule> builds) {
  684. foreach (var build in builds) {
  685. // build dependencies first
  686. BuildDependencies(build);
  687. SetCompilerSdkAndPlatform(build);
  688. // read the build command from PropertySheet
  689. var cmd = build["build-command"];
  690. if (cmd == null)
  691. throw new ConsoleException("missing build command in build {0}", build.Name);
  692. // tell the user which build rule we are processing right now
  693. using (new ConsoleColors(ConsoleColor.White, ConsoleColor.Black)) {
  694. Console.WriteLine("Built Configuration [{0}]", build.Name);
  695. }
  696. // run this build command
  697. Exec(cmd.Value);
  698. }
  699. }
  700. /// <summary>
  701. /// Checks if the process chain clean/build/clean leaves excess or unaccounted files
  702. /// </summary>
  703. /// <remarks>
  704. /// Runs Clean, Build (and checks targets), Clean and Status (to check for excess files)
  705. /// </remarks>
  706. /// <param name="builds">A list of build rules to verify</param>
  707. private void Verify(IEnumerable<Rule> builds) {
  708. foreach (var build in builds) {
  709. Clean( build.SingleItemAsEnumerable());
  710. Build(build.SingleItemAsEnumerable());
  711. foreach (var targ in build["targets"].Values.Where(targ => !File.Exists(targ))) {
  712. throw new ConsoleException("Target [{0}] was not found.", targ);
  713. }
  714. using (new ConsoleColors(ConsoleColor.Gray, ConsoleColor.Black)) {
  715. Console.WriteLine("Targets Verified.");
  716. }
  717. Clean(build.SingleItemAsEnumerable());
  718. Status(build.SingleItemAsEnumerable());
  719. }
  720. }
  721. /// <summary>
  722. /// Checks if excess files are present in the project directory
  723. /// </summary>
  724. /// <remarks>Throws ConsoleException if excess files are found</remarks>
  725. /// <param name="builds">A list of build rules to check</param>
  726. private void Status(IEnumerable<Rule> builds) {
  727. foreach (var build in builds) {
  728. IEnumerable<string> results = new string[] { };
  729. // this returns all new files created by the build process
  730. if (_useGit) {
  731. results = Git("status -s");
  732. }
  733. else if (_useHg) {
  734. results = Hg("status");
  735. }
  736. // Zero results means clean directory
  737. if (results.Count() > 0) {
  738. Fail("Project directory is not clean:");
  739. using (new ConsoleColors(ConsoleColor.White, ConsoleColor.Black)) {
  740. // list offending files
  741. foreach (var result in results) {
  742. Console.WriteLine(" {0}", result);
  743. }
  744. }
  745. throw new ConsoleException("Failed.");
  746. }
  747. }
  748. }
  749. /// <summary>
  750. /// Trace a build process
  751. /// </summary>
  752. /// <param name="builds">The build rules to trace</param>
  753. private void Trace(IEnumerable<Rule> builds) {
  754. foreach (var build in builds) {
  755. // prepare dependencies. these are not part of the trace
  756. BuildDependencies(build);
  757. SetCompilerSdkAndPlatform(build);
  758. // does this build rule contain a build command?
  759. var cmd = build["build-command"];
  760. if (cmd == null)
  761. throw new ConsoleException("missing build command in build {0}", build.Name);
  762. // run trace
  763. TraceExec(cmd.Value, Path.Combine(Environment.CurrentDirectory, "trace[{0}].xml".format(build.Name)));
  764. }
  765. }
  766. /// <summary>
  767. /// Run a git command
  768. /// </summary>
  769. /// <param name="cmdLine">A command to run with git</param>
  770. /// <returns>Any line from git's output except for those containing "copkg"</returns>
  771. /// <example>
  772. /// Git ("status -s")
  773. /// </example>
  774. private IEnumerable<string> Git(string cmdLine) {
  775. if( !string.IsNullOrEmpty(_gitcmd) ) {
  776. _cmdexe.Exec(@"/c ""{0}"" {1}", _gitcmd, cmdLine);
  777. return from line in _cmdexe.StandardOut.Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries) where !line.ToLower().Contains("copkg") select line;
  778. } else {
  779. _gitexe.Exec(cmdLine);
  780. return from line in _gitexe.StandardOut.Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries) where !line.ToLower().Contains("copkg") select line;
  781. }
  782. }
  783. /// <summary>
  784. /// Run an Hg command
  785. /// </summary>
  786. /// <param name="cmdLine">A command to run with hg</param>
  787. /// <returns>Any line from git's output except for those containing "copkg"</returns>
  788. private IEnumerable<string> Hg(string cmdLine) {
  789. _hgexe.Exec(cmdLine);
  790. return from line in _hgexe.StandardOut.Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries) where !line.ToLower().Contains("copkg") select line;
  791. }
  792. #region fail/help/logo
  793. /// <summary>
  794. /// Print an error to the console
  795. /// </summary>
  796. /// <param name="text">An error message</param>
  797. /// <param name="par">A format string</param>
  798. /// <returns>Always returns 1</returns>
  799. /// <seealso cref="String.Format(string, object[])"/>
  800. /// <remarks>
  801. /// Format according to http://msdn.microsoft.com/en-us/library/b1csw23d.aspx
  802. /// </remarks>
  803. public static int Fail(string text, params object[] par) {
  804. Logo();
  805. using (new ConsoleColors(ConsoleColor.Red, ConsoleColor.Black)) {
  806. Console.WriteLine("Error:{0}", text.format(par));
  807. }
  808. return 1;
  809. }
  810. /// <summary>
  811. /// Print usage notes (help) and logo
  812. /// </summary>
  813. /// <returns>Always returns 0</returns>
  814. private static int Help() {
  815. Logo();
  816. using (new ConsoleColors(ConsoleColor.White, ConsoleColor.Black)) {
  817. help.Print();
  818. }
  819. return 0;
  820. }
  821. /// <summary>
  822. /// Print program logo, information an copyright notice once.
  823. /// </summary>
  824. /// <remarks>
  825. /// Recurring calls to the function will not print "\n" (blank line) instead.
  826. /// </remarks>
  827. private static void Logo() {
  828. using (new ConsoleColors(ConsoleColor.Cyan, ConsoleColor.Black)) {
  829. Assembly.GetEntryAssembly().Logo().Print();
  830. }
  831. Assembly.GetEntryAssembly().SetLogo("");
  832. }
  833. #endregion
  834. }
  835. }