1 // Copyright (c) Microsoft. All rights reserved. 2 // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 4 using System; 5 using System.IO; 6 using System.Text; 7 using Microsoft.Build.Framework; 8 using Microsoft.Build.Shared; 9 10 namespace Microsoft.Build.Tasks 11 { 12 /// <summary> 13 /// Appends a list of items to a file. One item per line with carriage returns in-between. 14 /// </summary> 15 public class WriteLinesToFile : TaskExtension 16 { 17 private ITaskItem _file = null; 18 private ITaskItem[] _lines = null; 19 private bool _overwrite = false; 20 private string _encoding = null; 21 22 // Default encoding taken from System.IO.WriteAllText() 23 private static readonly Encoding s_defaultEncoding = new UTF8Encoding(false, true); 24 25 /// <summary> 26 /// File to write lines to. 27 /// </summary> 28 [Required] 29 public ITaskItem File 30 { 31 get { return _file; } 32 set { _file = value; } 33 } 34 35 /// <summary> 36 /// Write each item as a line in the file. 37 /// </summary> 38 public ITaskItem[] Lines 39 { 40 get { return _lines; } 41 set { _lines = value; } 42 } 43 44 /// <summary> 45 /// If true, overwrite any existing file contents. 46 /// </summary> 47 public bool Overwrite 48 { 49 get { return _overwrite; } 50 set { _overwrite = value; } 51 } 52 53 /// <summary> 54 /// If true, overwrite any existing file contents. 55 /// </summary> 56 public string Encoding 57 { 58 get { return _encoding; } 59 set { _encoding = value; } 60 } 61 62 /// <summary> 63 /// If true, the target file specified, if it exists, will be read first to compare against 64 /// what the task would have written. If identical, the file is not written to disk and the 65 /// timestamp will be preserved. 66 /// </summary> 67 public bool WriteOnlyWhenDifferent { get; set; } 68 69 70 /// <summary> 71 /// Execute the task. 72 /// </summary> 73 /// <returns></returns> Execute()74 public override bool Execute() 75 { 76 bool success = true; 77 78 if (File != null) 79 { 80 // do not return if Lines is null, because we may 81 // want to delete the file in that case 82 StringBuilder buffer = new StringBuilder(); 83 if (Lines != null) 84 { 85 foreach (ITaskItem line in Lines) 86 { 87 buffer.AppendLine(line.ItemSpec); 88 } 89 } 90 91 Encoding encoding = s_defaultEncoding; 92 if (_encoding != null) 93 { 94 try 95 { 96 encoding = System.Text.Encoding.GetEncoding(_encoding); 97 } 98 catch (ArgumentException) 99 { 100 Log.LogErrorWithCodeFromResources("General.InvalidValue", "Encoding", "WriteLinesToFile"); 101 return false; 102 } 103 } 104 105 try 106 { 107 if (Overwrite) 108 { 109 if (buffer.Length == 0) 110 { 111 // if overwrite==true, and there are no lines to write, 112 // just delete the file to leave everything tidy. 113 System.IO.File.Delete(File.ItemSpec); 114 } 115 else 116 { 117 string contentsAsString = null; 118 119 try 120 { 121 // When WriteOnlyWhenDifferent is set, read the file and if they're the same return. 122 if (WriteOnlyWhenDifferent && FileUtilities.FileExistsNoThrow(File.ItemSpec)) 123 { 124 var existingContents = System.IO.File.ReadAllText(File.ItemSpec); 125 if (existingContents.Length == buffer.Length) 126 { 127 contentsAsString = buffer.ToString(); 128 if (existingContents.Equals(contentsAsString)) 129 { 130 Log.LogMessageFromResources(MessageImportance.Low, "WriteLinesToFile.SkippingUnchangedFile", File.ItemSpec); 131 return true; 132 } 133 } 134 } 135 } 136 catch (IOException) 137 { 138 Log.LogMessageFromResources(MessageImportance.Low, "WriteLinesToFile.ErrorReadingFile", File.ItemSpec); 139 } 140 141 if (contentsAsString == null) 142 { 143 contentsAsString = buffer.ToString(); 144 } 145 146 System.IO.File.WriteAllText(File.ItemSpec, contentsAsString, encoding); 147 } 148 } 149 else 150 { 151 System.IO.File.AppendAllText(File.ItemSpec, buffer.ToString(), encoding); 152 } 153 } 154 catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e)) 155 { 156 LogError(_file, e, ref success); 157 } 158 } 159 160 return success; 161 } 162 163 /// <summary> 164 /// Log an error. 165 /// </summary> 166 /// <param name="file">The being accessed</param> 167 /// <param name="e">The exception.</param> 168 /// <param name="success">Whether the task should return an error.</param> LogError(ITaskItem fileName, Exception e, ref bool success)169 private void LogError(ITaskItem fileName, Exception e, ref bool success) 170 { 171 Log.LogErrorWithCodeFromResources("WriteLinesToFile.ErrorOrWarning", fileName.ItemSpec, e.Message); 172 success = false; 173 } 174 } 175 } 176