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