OLD | NEW |
(Empty) | |
| 1 |
| 2 using System; |
| 3 using System.Collections.Generic; |
| 4 using System.Text; |
| 5 using System.Collections; |
| 6 using System.IO; |
| 7 using System.Reflection; |
| 8 using System.Resources; |
| 9 using Microsoft.Build.Framework; |
| 10 using Microsoft.Build.Utilities; |
| 11 |
| 12 using System.Diagnostics; |
| 13 |
| 14 namespace NaCl.Build.CPPTasks |
| 15 { |
| 16 public class NaClCompile : ToolTask |
| 17 { |
| 18 private XamlParser m_XamlParser; |
| 19 private ITaskItem[] excludedInputPaths; |
| 20 private ITaskItem[] tlogReadFiles; |
| 21 private ITaskItem tlogCommandFile; |
| 22 private ITaskItem[] tlogWriteFiles; |
| 23 private CanonicalTrackedInputFiles trackedInputFiles; |
| 24 private bool skippedExecution; |
| 25 private ITaskItem[] compileSourceList; |
| 26 public bool BuildingInIDE { get; set; } |
| 27 private string m_toolname; |
| 28 private bool trackFileAccess; |
| 29 private bool minimalRebuildFromTracking; |
| 30 private string pathToLog; |
| 31 |
| 32 [Required] |
| 33 public string PropertiesFile { get; set; } |
| 34 |
| 35 [Required] |
| 36 public ITaskItem[] Sources { get; set; } |
| 37 |
| 38 [Required] |
| 39 public string NaCLCompilerPath { get; set; } |
| 40 |
| 41 [Required] |
| 42 public string OutputCommandLine { get; set; } |
| 43 |
| 44 [Required] |
| 45 public string TrackerLogDirectory { get; set; } |
| 46 |
| 47 |
| 48 protected override string GenerateFullPathToTool() { return ToolName; } |
| 49 |
| 50 public NaClCompile() |
| 51 : base(new ResourceManager("NaCl.Build.CPPTasks.Properties.Resources
", Assembly.GetExecutingAssembly())) |
| 52 { |
| 53 this.pathToLog = string.Empty; |
| 54 } |
| 55 |
| 56 protected IDictionary<string, string> GenerateCommandLinesFromTlog() |
| 57 { |
| 58 IDictionary<string, string> cmdLineDictionary = new Dictionary<strin
g, string>(StringComparer.OrdinalIgnoreCase); |
| 59 string tlogFilename = this.TLogCommandFile.GetMetadata("FullPath"); |
| 60 if (File.Exists(tlogFilename)) |
| 61 { |
| 62 using (StreamReader reader = File.OpenText(tlogFilename)) |
| 63 { |
| 64 string filename = string.Empty; |
| 65 for (string lineStr = reader.ReadLine(); lineStr != null; li
neStr = reader.ReadLine()) |
| 66 { |
| 67 if (lineStr.Length == 0 || |
| 68 (lineStr[0] == '^' && lineStr.Length == 1)) |
| 69 { |
| 70 Log.LogMessage(MessageImportance.High, "Invalid line
in command tlog"); |
| 71 break; |
| 72 } |
| 73 else if (lineStr[0] == '^') |
| 74 { |
| 75 filename = lineStr.Substring(1); |
| 76 } |
| 77 else |
| 78 { |
| 79 cmdLineDictionary[filename] = lineStr; |
| 80 } |
| 81 } |
| 82 } |
| 83 } |
| 84 return cmdLineDictionary; |
| 85 } |
| 86 |
| 87 protected override void LogEventsFromTextOutput(string singleLine, Messa
geImportance messageImportance) |
| 88 { |
| 89 base.LogEventsFromTextOutput(GCCUtilities.Convert_Output_GCC_to_VS(s
ingleLine), messageImportance); |
| 90 } |
| 91 |
| 92 private void ConstructReadTLog(ITaskItem[] compiledSources, CanonicalTra
ckedOutputFiles outputs) |
| 93 { |
| 94 string trackerPath = Path.GetFullPath(TlogDirectory + ReadTLogFilena
mes[0]); |
| 95 |
| 96 //save tlog for sources not compiled during this execution |
| 97 TaskItem readTrackerItem = new TaskItem(trackerPath); |
| 98 CanonicalTrackedInputFiles files = new CanonicalTrackedInputFiles(ne
w TaskItem[] { readTrackerItem }, Sources, outputs, false, false); |
| 99 files.RemoveEntriesForSource(compiledSources); |
| 100 files.SaveTlog(); |
| 101 |
| 102 //add tlog information for compiled sources |
| 103 using (StreamWriter writer = new StreamWriter(trackerPath, true, Enc
oding.Unicode)) |
| 104 { |
| 105 foreach (ITaskItem source in compiledSources) |
| 106 { |
| 107 string sourcePath = Path.GetFullPath(source.ItemSpec).ToUppe
rInvariant(); |
| 108 |
| 109 string objectFilePath = Path.GetFullPath(source.GetMetadata(
"ObjectFileName")); |
| 110 string depFilePath = Path.ChangeExtension(objectFilePath, ".
d"); |
| 111 |
| 112 try |
| 113 { |
| 114 if (File.Exists(depFilePath) == false) |
| 115 { |
| 116 Log.LogMessage(MessageImportance.High, depFilePath +
" not found"); |
| 117 } |
| 118 else |
| 119 { |
| 120 writer.WriteLine("^" + sourcePath); |
| 121 DependencyParser parser = new DependencyParser(depFi
lePath); |
| 122 |
| 123 foreach (string filename in parser.Dependencies) |
| 124 { |
| 125 //source itself not required |
| 126 if (filename == sourcePath) |
| 127 continue; |
| 128 |
| 129 if (File.Exists(filename) == false) |
| 130 { |
| 131 Log.LogMessage(MessageImportance.High, "File
" + sourcePath + " is missing dependency " + filename); |
| 132 } |
| 133 |
| 134 writer.WriteLine(filename); |
| 135 } |
| 136 |
| 137 //remove d file |
| 138 try |
| 139 { |
| 140 File.Delete(depFilePath); |
| 141 } |
| 142 finally |
| 143 { |
| 144 |
| 145 } |
| 146 } |
| 147 |
| 148 } |
| 149 catch (Exception) |
| 150 { |
| 151 Log.LogError("Failed to update " + readTrackerItem + " f
or " + sourcePath); |
| 152 } |
| 153 } |
| 154 } |
| 155 } |
| 156 |
| 157 private CanonicalTrackedOutputFiles OutputWriteTrackerLog(ITaskItem[] co
mpiledSources) |
| 158 { |
| 159 string path = Path.Combine(TlogDirectory, WriteTLogFilename); |
| 160 TaskItem item = new TaskItem(path); |
| 161 CanonicalTrackedOutputFiles trackedFiles = new CanonicalTrackedOutpu
tFiles(new TaskItem[] { item }); |
| 162 |
| 163 foreach (ITaskItem sourceItem in compiledSources) |
| 164 { |
| 165 //remove this entry associated with compiled source which is abo
ut to be recomputed |
| 166 trackedFiles.RemoveEntriesForSource(sourceItem); |
| 167 |
| 168 //add entry with updated information |
| 169 trackedFiles.AddComputedOutputForSourceRoot( Path.GetFullPath(so
urceItem.ItemSpec).ToUpperInvariant(), |
| 170 Path.GetFullPath(so
urceItem.GetMetadata("ObjectFileName")).ToUpperInvariant()); |
| 171 } |
| 172 |
| 173 //output tlog |
| 174 trackedFiles.SaveTlog(); |
| 175 |
| 176 return trackedFiles; |
| 177 } |
| 178 |
| 179 private void OutputCommandTrackerLog(ITaskItem[] compiledSources) |
| 180 { |
| 181 IDictionary<string, string> commandLines = GenerateCommandLinesFromT
log(); |
| 182 |
| 183 // |
| 184 if (compiledSources != null) |
| 185 { |
| 186 foreach (ITaskItem source in compiledSources) |
| 187 { |
| 188 string rmSource = FileTracker.FormatRootingMarker(source); |
| 189 commandLines[rmSource] = GenerateCommandLineFromProps(source
) + " " + source.GetMetadata("FullPath").ToUpperInvariant(); |
| 190 } |
| 191 } |
| 192 |
| 193 //write tlog |
| 194 using (StreamWriter writer = new StreamWriter(this.TLogCommandFile.G
etMetadata("FullPath"), false, Encoding.Unicode)) |
| 195 { |
| 196 foreach (KeyValuePair<string, string> p in commandLines) |
| 197 { |
| 198 string keyLine = "^" + p.Key; |
| 199 writer.WriteLine(keyLine); |
| 200 writer.WriteLine(p.Value); |
| 201 } |
| 202 } |
| 203 } |
| 204 |
| 205 protected string GenerateCommandLineFromProps(ITaskItem sourceFile) |
| 206 { |
| 207 StringBuilder commandLine = new StringBuilder(GCCUtilities.s_Command
LineLength); |
| 208 |
| 209 if (sourceFile != null) |
| 210 { |
| 211 string sourcePath = GCCUtilities.Convert_Path_Windows_To_Posix(s
ourceFile.ToString()); |
| 212 |
| 213 // Remove rtti items as they are not relevant in C compilation a
nd will produce warnings |
| 214 if (SourceIsC(sourceFile.ToString())) |
| 215 { |
| 216 commandLine.Replace("-fno-rtti", ""); |
| 217 commandLine.Replace("-frtti", ""); |
| 218 } |
| 219 |
| 220 //build command line from components and add required switches |
| 221 string props = m_XamlParser.Parse(sourceFile); |
| 222 commandLine.Append(props); |
| 223 commandLine.Append(" -MD -c "); |
| 224 commandLine.Append(sourcePath); |
| 225 |
| 226 } |
| 227 |
| 228 return commandLine.ToString(); |
| 229 } |
| 230 |
| 231 protected ITaskItem[] MergeOutOfDateSources(ITaskItem[] outOfDateSources
FromTracking, List<ITaskItem> outOfDateSourcesFromCommandLineChanges) |
| 232 { |
| 233 List<ITaskItem> mergedSources = new List<ITaskItem>(outOfDateSources
FromTracking); |
| 234 |
| 235 foreach (ITaskItem item in outOfDateSourcesFromCommandLineChanges) |
| 236 { |
| 237 if (!mergedSources.Contains(item)) |
| 238 { |
| 239 mergedSources.Add(item); |
| 240 } |
| 241 } |
| 242 |
| 243 return mergedSources.ToArray(); |
| 244 } |
| 245 |
| 246 protected bool ForcedRebuildRequired() |
| 247 { |
| 248 string tlogCommandPath = null; |
| 249 |
| 250 try |
| 251 { |
| 252 tlogCommandPath = this.TLogCommandFile.GetMetadata("FullPath"); |
| 253 } |
| 254 catch (Exception exception) |
| 255 { |
| 256 if (exception is InvalidOperationException || exception is NullR
eferenceException) |
| 257 return true; |
| 258 else |
| 259 throw; |
| 260 } |
| 261 |
| 262 //if command tlog file does not exist then force rebuild is required |
| 263 if (File.Exists(tlogCommandPath) == false) |
| 264 { |
| 265 return true; |
| 266 } |
| 267 else |
| 268 { |
| 269 return false; |
| 270 } |
| 271 } |
| 272 |
| 273 private int Compile(string pathToTool) |
| 274 { |
| 275 int returnCode = 0; |
| 276 |
| 277 foreach (ITaskItem sourceFileItem in CompileSourceList) |
| 278 { |
| 279 try |
| 280 { |
| 281 string commandLine = GenerateCommandLineFromProps(sourceFile
Item); |
| 282 |
| 283 base.Log.LogMessageFromText(Path.GetFileName(sourceFileItem.
ToString()), MessageImportance.High); |
| 284 |
| 285 if (OutputCommandLine == "true") |
| 286 { |
| 287 string logMessage = pathToTool + " " + commandLine; |
| 288 Log.LogMessageFromText(logMessage, MessageImportance.Hig
h); |
| 289 } |
| 290 |
| 291 |
| 292 // compile |
| 293 returnCode = base.ExecuteTool(pathToTool, commandLine, strin
g.Empty); |
| 294 } |
| 295 catch (Exception) |
| 296 { |
| 297 returnCode = base.ExitCode; |
| 298 } |
| 299 |
| 300 //abort if an error was encountered |
| 301 if (returnCode != 0) |
| 302 { |
| 303 return returnCode; |
| 304 } |
| 305 } |
| 306 return returnCode; |
| 307 } |
| 308 |
| 309 protected override int ExecuteTool(string pathToTool, string responseFil
eCommands, string commandLineCommands) |
| 310 { |
| 311 if (File.Exists(pathToTool) == false) |
| 312 { |
| 313 base.Log.LogMessageFromText("Unable to find NaCL compiler: " + p
athToTool, MessageImportance.High); |
| 314 return -1; |
| 315 } |
| 316 |
| 317 int returnCode = -1; |
| 318 |
| 319 try |
| 320 { |
| 321 returnCode = Compile(pathToTool); |
| 322 } |
| 323 finally |
| 324 { |
| 325 |
| 326 } |
| 327 return returnCode; |
| 328 } |
| 329 |
| 330 protected override bool SkipTaskExecution() |
| 331 { |
| 332 return this.skippedExecution; |
| 333 } |
| 334 |
| 335 protected void CalcSourcesToCompile() |
| 336 { |
| 337 if (this.TrackFileAccess || this.MinimalRebuildFromTracking) |
| 338 { |
| 339 this.SetTrackerLogPaths(); |
| 340 } |
| 341 |
| 342 //check if full recompile is required otherwise perform incremental |
| 343 if (this.ForcedRebuildRequired() || this.MinimalRebuildFromTracking
== false) |
| 344 { |
| 345 this.CompileSourceList = this.Sources; |
| 346 if ((this.CompileSourceList == null) || (this.CompileSourceList.
Length == 0)) |
| 347 { |
| 348 this.SkippedExecution = true; |
| 349 } |
| 350 } |
| 351 else |
| 352 { |
| 353 //retrieve list of sources out of date due to command line chang
es |
| 354 List<ITaskItem> outOfDateSourcesFromCommandLineChanges = this.Ge
tOutOfDateSourcesFromCommandLineChanges(); |
| 355 |
| 356 //retrieve sources out of date due to tracking |
| 357 CanonicalTrackedOutputFiles trackedOutputFiles = new CanonicalTr
ackedOutputFiles(this, this.TLogWriteFiles); |
| 358 this.TrackedInputFiles = new CanonicalTrackedInputFiles(this, th
is.TLogReadFiles, this.Sources, this.ExcludedInputPaths, trackedOutputFiles, tru
e, false); |
| 359 ITaskItem[] outOfDateSourcesFromTracking = this.TrackedInputFile
s.ComputeSourcesNeedingCompilation(); |
| 360 |
| 361 //merge out of date lists |
| 362 this.CompileSourceList = this.MergeOutOfDateSources(outOfDateSou
rcesFromTracking, outOfDateSourcesFromCommandLineChanges); |
| 363 |
| 364 if (this.CompileSourceList.Length == 0) |
| 365 { |
| 366 this.SkippedExecution = true; |
| 367 } |
| 368 else |
| 369 { |
| 370 //remove sources to compile from tracked file list |
| 371 this.TrackedInputFiles.RemoveEntriesForSource(this.CompileSo
urceList); |
| 372 trackedOutputFiles.RemoveEntriesForSource(this.CompileSource
List); |
| 373 this.TrackedInputFiles.SaveTlog(); |
| 374 trackedOutputFiles.SaveTlog(); |
| 375 |
| 376 this.SkippedExecution = false; |
| 377 } |
| 378 } |
| 379 } |
| 380 |
| 381 protected bool SourceIsC(string sourceFilename) |
| 382 { |
| 383 string fileExt = Path.GetExtension(sourceFilename.ToString()); |
| 384 |
| 385 if (fileExt == ".c") |
| 386 return true; |
| 387 else |
| 388 return false; |
| 389 } |
| 390 |
| 391 public override bool Execute() |
| 392 { |
| 393 bool returnResult = false; |
| 394 |
| 395 try |
| 396 { |
| 397 m_XamlParser = new XamlParser(PropertiesFile); |
| 398 m_toolname = Path.GetFileNameWithoutExtension(ToolName); |
| 399 ValidateParameters(); |
| 400 CalcSourcesToCompile(); |
| 401 |
| 402 returnResult = base.Execute(); |
| 403 |
| 404 // Update tracker log files if execution occurred |
| 405 //if (this.skippedExecution == false) |
| 406 { |
| 407 CanonicalTrackedOutputFiles outputs = OutputWriteTrackerLog(
CompileSourceList); |
| 408 ConstructReadTLog(CompileSourceList, outputs); |
| 409 OutputCommandTrackerLog(CompileSourceList); |
| 410 } |
| 411 } |
| 412 finally |
| 413 { |
| 414 |
| 415 } |
| 416 |
| 417 return returnResult; |
| 418 } |
| 419 |
| 420 protected List<ITaskItem> GetOutOfDateSourcesFromCommandLineChanges() |
| 421 { |
| 422 //get dictionary of source + command lines |
| 423 IDictionary<string, string> dictionary = this.GenerateCommandLinesFr
omTlog(); |
| 424 List<ITaskItem> outOfDateSources = new List<ITaskItem>(); |
| 425 |
| 426 //add sources to out of date list if the tlog dictionary string do n
ot match the generated command line string |
| 427 StringBuilder currentCommandLine = new StringBuilder(GCCUtilities.s_
CommandLineLength); |
| 428 foreach (ITaskItem sourceItem in Sources) |
| 429 { |
| 430 currentCommandLine.Length = 0; |
| 431 |
| 432 currentCommandLine.Append(GenerateCommandLineFromProps(sourceIte
m)); |
| 433 currentCommandLine.Append(" "); |
| 434 currentCommandLine.Append(sourceItem.GetMetadata("FullPath").ToU
pperInvariant()); |
| 435 |
| 436 string tlogCommandLine = null; |
| 437 if (dictionary.TryGetValue(FileTracker.FormatRootingMarker(sourc
eItem), out tlogCommandLine)) |
| 438 { |
| 439 if ((tlogCommandLine == null) || !currentCommandLine.ToStrin
g().Equals(tlogCommandLine, StringComparison.Ordinal)) |
| 440 { |
| 441 outOfDateSources.Add(sourceItem); |
| 442 } |
| 443 } |
| 444 else |
| 445 { |
| 446 outOfDateSources.Add(sourceItem); |
| 447 } |
| 448 } |
| 449 return outOfDateSources; |
| 450 } |
| 451 |
| 452 protected virtual void SetTrackerLogPaths() |
| 453 { |
| 454 if (this.TLogCommandFile == null) |
| 455 { |
| 456 string commandFile = Path.Combine(this.TlogDirectory, this.Comma
ndTLogFilename); |
| 457 this.TLogCommandFile = new TaskItem(commandFile); |
| 458 } |
| 459 |
| 460 if (this.TLogReadFiles == null) |
| 461 { |
| 462 this.TLogReadFiles = new ITaskItem[this.ReadTLogFilenames.Length
]; |
| 463 for (int n = 0; n < this.ReadTLogFilenames.Length; n++) |
| 464 { |
| 465 string readFile = Path.Combine(this.TlogDirectory, this.Read
TLogFilenames[n]); |
| 466 this.TLogReadFiles[n] = new TaskItem(readFile); |
| 467 } |
| 468 } |
| 469 |
| 470 if (this.TLogWriteFiles == null) |
| 471 { |
| 472 this.TLogWriteFiles = new ITaskItem[1]; |
| 473 string writeFile = Path.Combine(this.TlogDirectory, this.WriteTL
ogFilename); |
| 474 this.TLogWriteFiles[0] = new TaskItem(writeFile); |
| 475 } |
| 476 } |
| 477 |
| 478 |
| 479 //props |
| 480 protected string CommandTLogFilename |
| 481 { |
| 482 get |
| 483 { |
| 484 return m_toolname + ".compile.command.1.tlog"; |
| 485 } |
| 486 } |
| 487 |
| 488 protected string[] ReadTLogFilenames |
| 489 { |
| 490 get |
| 491 { |
| 492 return new string[] { m_toolname + ".compile.read.1.tlog" }; |
| 493 } |
| 494 } |
| 495 |
| 496 [Output] |
| 497 public bool SkippedExecution |
| 498 { |
| 499 get |
| 500 { |
| 501 return this.skippedExecution; |
| 502 } |
| 503 set |
| 504 { |
| 505 this.skippedExecution = value; |
| 506 } |
| 507 } |
| 508 |
| 509 public ITaskItem TLogCommandFile |
| 510 { |
| 511 get |
| 512 { |
| 513 return this.tlogCommandFile; |
| 514 } |
| 515 set |
| 516 { |
| 517 this.tlogCommandFile = value; |
| 518 } |
| 519 } |
| 520 |
| 521 protected string TlogDirectory |
| 522 { |
| 523 get |
| 524 { |
| 525 if (this.TrackerLogDirectory != null) |
| 526 { |
| 527 return this.TrackerLogDirectory; |
| 528 } |
| 529 return string.Empty; |
| 530 } |
| 531 } |
| 532 |
| 533 public bool MinimalRebuildFromTracking |
| 534 { |
| 535 get |
| 536 { |
| 537 return this.minimalRebuildFromTracking; |
| 538 } |
| 539 set |
| 540 { |
| 541 this.minimalRebuildFromTracking = value; |
| 542 } |
| 543 } |
| 544 |
| 545 |
| 546 public ITaskItem[] TLogReadFiles |
| 547 { |
| 548 get |
| 549 { |
| 550 return this.tlogReadFiles; |
| 551 } |
| 552 set |
| 553 { |
| 554 this.tlogReadFiles = value; |
| 555 } |
| 556 } |
| 557 |
| 558 public ITaskItem[] ExcludedInputPaths |
| 559 { |
| 560 get |
| 561 { |
| 562 return this.excludedInputPaths; |
| 563 } |
| 564 set |
| 565 { |
| 566 this.excludedInputPaths = value; |
| 567 } |
| 568 } |
| 569 |
| 570 |
| 571 public ITaskItem[] TLogWriteFiles |
| 572 { |
| 573 get |
| 574 { |
| 575 return this.tlogWriteFiles; |
| 576 } |
| 577 set |
| 578 { |
| 579 this.tlogWriteFiles = value; |
| 580 } |
| 581 } |
| 582 |
| 583 protected string WriteTLogFilename |
| 584 { |
| 585 get |
| 586 { |
| 587 return m_toolname + ".compile.write.1.tlog"; |
| 588 } |
| 589 } |
| 590 |
| 591 public bool TrackFileAccess |
| 592 { |
| 593 get |
| 594 { |
| 595 return this.trackFileAccess; |
| 596 } |
| 597 set |
| 598 { |
| 599 this.trackFileAccess = value; |
| 600 } |
| 601 } |
| 602 |
| 603 protected CanonicalTrackedInputFiles TrackedInputFiles |
| 604 { |
| 605 get |
| 606 { |
| 607 return this.trackedInputFiles; |
| 608 } |
| 609 set |
| 610 { |
| 611 this.trackedInputFiles = value; |
| 612 } |
| 613 } |
| 614 |
| 615 [Output] |
| 616 public ITaskItem[] CompileSourceList |
| 617 { |
| 618 get |
| 619 { |
| 620 return this.compileSourceList; |
| 621 } |
| 622 set |
| 623 { |
| 624 this.compileSourceList = value; |
| 625 } |
| 626 } |
| 627 |
| 628 protected override string ToolName |
| 629 { |
| 630 get |
| 631 { |
| 632 return NaCLCompilerPath; |
| 633 } |
| 634 } |
| 635 |
| 636 protected override Encoding ResponseFileEncoding |
| 637 { |
| 638 get |
| 639 { |
| 640 return Encoding.ASCII; |
| 641 } |
| 642 } |
| 643 } |
| 644 } |
OLD | NEW |