LEFT | RIGHT |
1 using System; | 1 /* |
| 2 Copyright 2013 Google Inc |
| 3 |
| 4 Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 you may not use this file except in compliance with the License. |
| 6 You may obtain a copy of the License at |
| 7 |
| 8 http://www.apache.org/licenses/LICENSE-2.0 |
| 9 |
| 10 Unless required by applicable law or agreed to in writing, software |
| 11 distributed under the License is distributed on an "AS IS" BASIS, |
| 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 See the License for the specific language governing permissions and |
| 14 limitations under the License. |
| 15 */ |
| 16 |
| 17 using System; |
2 using System.Collections.Generic; | 18 using System.Collections.Generic; |
3 using System.Diagnostics; | 19 using System.Diagnostics; |
4 using System.IO; | 20 using System.IO; |
5 using System.Linq; | 21 using System.Linq; |
6 using System.Reflection; | 22 using System.Reflection; |
7 using System.Text; | 23 using System.Text; |
8 using System.Text.RegularExpressions; | 24 using System.Text.RegularExpressions; |
| 25 using Microsoft.Build.Evaluation; |
| 26 using Microsoft.Build.Framework; |
| 27 using Microsoft.Build.Logging; |
9 | 28 |
10 using CommandLine; | 29 using CommandLine; |
11 using CommandLine.Text; | 30 using CommandLine.Text; |
12 using Ionic.Zip; | 31 using Ionic.Zip; |
13 using Microsoft.Build.Evaluation; | |
14 using Microsoft.Build.Framework; | |
15 using Microsoft.Build.Logging; | |
16 | 32 |
17 using Google.Apis.Release.Repositories; | 33 using Google.Apis.Release.Repositories; |
| 34 using Google.Apis.Release.Wiki; |
18 using Google.Apis.Utils; | 35 using Google.Apis.Utils; |
19 using Google.Apis.Release.Wiki; | 36 using Google.Apis.Utils.Trace; |
20 | 37 |
21 namespace Google.Apis.Release | 38 namespace Google.Apis.Release |
22 { | 39 { |
| 40 /// <summary>The main program for creating a new Google.Apis release.</summa
ry> |
23 class Program | 41 class Program |
24 { | 42 { |
| 43 /// <summary>The options class which contains the different options to t
his publish release utility.</summary> |
25 public class Options | 44 public class Options |
26 { | 45 { |
27 #region HelpText | 46 #region HelpText |
28 | 47 |
29 const string VersionHelpText = "The version number of this release -
<Major.Minor.Build> only."; | 48 const string VersionHelpText = "The version number of this release -
<Major.Minor.Build> only."; |
30 const string OutputHelpText = "Define the output directory for this
build. " + | 49 const string OutputHelpText = "Define the output directory for this
build. " + |
31 "Notice that it's relative to current directory."; | 50 "Notice that it's relative to current directory."; |
32 const string StepHelpText = "Two options: '1' for building the core
library \n" + | 51 const string StepHelpText = "Two options: " + |
33 "'2' for compiling samples, updating wiki and push to contrib th
e new version \n" + | 52 "'1' for building the core library. \n" + |
34 "'3' for publishing on the local packages to NuGet"; | 53 "'2' for compiling samples, updating wiki and push to contrib th
e new version."; |
35 const string IsBetaHelpText = "Is this release is beta?"; | 54 const string IsBetaHelpText = "Is this release beta?"; |
36 const string NuGetApiKeyHelpText = "Define the NuGet API key to publ
ish to NuGet main repository"; | 55 const string NuGetApiKeyHelpText = "Define the NuGet API key to publ
ish to NuGet main repository."; |
37 | 56 |
38 [HelpOption] | 57 [HelpOption] |
39 public string GetHelp() | 58 public string GetHelp() |
40 { | 59 { |
41 return HelpText.AutoBuild(this, c => HelpText.DefaultParsingErro
rsHandler(this, c)); | 60 return HelpText.AutoBuild(this, c => HelpText.DefaultParsingErro
rsHandler(this, c)); |
42 } | 61 } |
43 | 62 |
44 #endregion | 63 #endregion |
45 | 64 |
46 [Option('v', "version", Required = true, | 65 [Option('v', "version", Required = true, HelpText = VersionHelpText)
] |
47 HelpText = VersionHelpText)] | |
48 public string Version { get; set; } | 66 public string Version { get; set; } |
49 | 67 |
50 [Option('d', "dir", Required = true, HelpText = OutputHelpText)] | 68 [Option('d', "dir", Required = true, HelpText = OutputHelpText)] |
51 public string OutputDirectory { get; set; } | 69 public string OutputDirectory { get; set; } |
52 | 70 |
53 [Option('s', "step", Required = true, HelpText = StepHelpText)] | 71 [Option('s', "step", Required = true, HelpText = StepHelpText)] |
54 public int Step { get; set; } | 72 public int Step { get; set; } |
55 | 73 |
56 [Option('b', "beta", DefaultValue = true, HelpText = IsBetaHelpText)
] | 74 [Option('b', "beta", DefaultValue = true, HelpText = IsBetaHelpText)
] |
57 public bool IsBeta { get; set; } | 75 public bool IsBeta { get; set; } |
58 | 76 |
59 [Option('k', "nuget_key", HelpText = NuGetApiKeyHelpText)] | 77 [Option('k', "nuget_key", HelpText = NuGetApiKeyHelpText)] |
60 public string NuGetApiKey { get; set; } | 78 public string NuGetApiKey { get; set; } |
61 } | 79 } |
62 | 80 |
63 static readonly TraceSource TraceSource = new TraceSource("Google.Apis")
; | 81 private static readonly TraceSource TraceSource = new TraceSource("Googl
e.Apis"); |
64 | 82 |
65 /// <summary> Command line arguments. </summary> | 83 /// <summary>Command line arguments.</summary> |
66 private readonly Options options; | 84 private readonly Options options; |
67 | 85 |
68 private int MajorVersion { get; set; } | 86 private int MajorVersion { get; set; } |
69 private int MinorVersion { get; set; } | 87 private int MinorVersion { get; set; } |
70 private int BuildVersion { get; set; } | 88 private int BuildVersion { get; set; } |
71 | 89 |
72 private string Tag | 90 private string Tag |
73 { | 91 { |
74 get { return options.Version + (options.IsBeta ? "-beta" : ""); } | 92 get { return options.Version + (options.IsBeta ? "-beta" : ""); } |
75 } | 93 } |
76 | 94 |
77 /// <summary>The "default" repository.</summary> | 95 /// <summary>Gets or sets the "default" repository.</summary> |
78 private Hg DefaultRepository { get; set; } | 96 private Hg DefaultRepository { get; set; } |
79 | 97 |
80 /// <summary>The "samples" repository.</summary> | 98 /// <summary>Gets or sets the "samples" repository.</summary> |
81 private Hg SamplesRepository { get; set; } | 99 private Hg SamplesRepository { get; set; } |
82 | 100 |
83 /// <summary>The "wiki" repository.</summary> | 101 /// <summary>Gets or sets the "wiki" repository.</summary> |
84 private Hg WikiRepository { get; set; } | 102 private Hg WikiRepository { get; set; } |
85 | 103 |
86 /// <summary>The "contrib" repository.</summary> | 104 /// <summary>Gets or sets the "contrib" repository.</summary> |
87 private Hg ContribRepository { get; set; } | 105 private Hg ContribRepository { get; set; } |
88 | 106 |
| 107 /// <summary>Gets all four repositories.</summary> |
89 private IEnumerable<Hg> AllRepositories | 108 private IEnumerable<Hg> AllRepositories |
90 { | 109 { |
91 get { return new List<Hg> { DefaultRepository, SamplesRepository, Wi
kiRepository, ContribRepository }; } | 110 get { return new List<Hg> { DefaultRepository, SamplesRepository, Wi
kiRepository, ContribRepository }; } |
92 } | 111 } |
93 | 112 |
| 113 /// <summary> |
| 114 /// Clones URL format which expects one parameter of the repository name
(the default repository should be· |
| 115 /// empty). |
| 116 /// </summary> |
| 117 private const string CloneUrlFormat = "https://code.google.com/p/google-
api-dotnet-client{0}/"; |
| 118 |
94 static void Main(string[] args) | 119 static void Main(string[] args) |
95 { | 120 { |
| 121 bool valid = true; |
| 122 |
96 var options = new Options(); | 123 var options = new Options(); |
97 if (!CommandLine.Parser.Default.ParseArguments(args, options)) | 124 if (!CommandLine.Parser.Default.ParseArguments(args, options)) |
98 { | 125 { |
99 Console.ReadKey(); | 126 Console.ReadKey(); |
100 return; | 127 return; |
101 } | 128 } |
102 | 129 |
| 130 if (options.Step > 2 || options.Step < 1) |
| 131 { |
| 132 TraceSource.TraceEvent(TraceEventType.Error, "Invalid Step. Vali
d step is '1' or '2'."); |
| 133 valid = false; |
| 134 } |
| 135 |
103 var match = Regex.Match(options.Version, @"^(\d+)\.(\d+)\.(\d)+$"); | 136 var match = Regex.Match(options.Version, @"^(\d+)\.(\d+)\.(\d)+$"); |
104 if (!match.Success) | 137 if (!match.Success) |
105 { | 138 { |
106 TraceSource.TraceEvent(TraceEventType.Error, | 139 TraceSource.TraceEvent(TraceEventType.Error, |
107 "Invalid version Number. Version should be in <Major>.<Minor
>.<Build> form."); | 140 "Invalid version Number. Version should be in <Major>.<Minor
>.<Build> form."); |
108 } | 141 valid = false; |
109 | 142 } |
110 var p = new Program(options) | 143 |
| 144 var program = new Program(options) |
111 { | 145 { |
112 MajorVersion = int.Parse(match.Groups[1].Value), | 146 MajorVersion = int.Parse(match.Groups[1].Value), |
113 MinorVersion = int.Parse(match.Groups[2].Value), | 147 MinorVersion = int.Parse(match.Groups[2].Value), |
114 BuildVersion = int.Parse(match.Groups[3].Value), | 148 BuildVersion = int.Parse(match.Groups[3].Value), |
115 }; | 149 }; |
116 | 150 |
117 try | 151 if (valid) |
118 { | 152 { |
119 p.Run(); | 153 try |
120 } | 154 { |
121 catch (Exception ex) | 155 program.Run(); |
122 { | 156 } |
123 TraceSource.TraceEvent(TraceEventType.Error, "Exception occurred
while running. Exception is: {0}", | 157 catch (Exception ex) |
124 ex.Message); | 158 { |
| 159 TraceSource.TraceEvent(TraceEventType.Error, "Exception occu
rred while running. Exception is: {0}", |
| 160 ex.Message); |
| 161 } |
125 } | 162 } |
126 | 163 |
127 Console.WriteLine("Press any key to continue..."); | 164 Console.WriteLine("Press any key to continue..."); |
128 Console.ReadKey(); | 165 Console.ReadKey(); |
129 } | 166 } |
130 | 167 |
| 168 /// <summary>The main release logic for creating a new release of Google
.Apis.</summary> |
131 private void Run() | 169 private void Run() |
132 { | 170 { |
133 const string CloneUrl = "https://code.google.com/p/google-api-dotnet
-client{0}/"; | 171 DefaultRepository = new Hg(new Uri(string.Format(CloneUrlFormat, "")
), "default"); |
134 | 172 |
135 DefaultRepository = Hg.Get("default", string.Format(CloneUrl, "")); | 173 // Step 1 is only for creating Google.Apis and Google.Apis.Authentic
ation packages |
136 | |
137 if (options.Step == 1) | 174 if (options.Step == 1) |
138 { | 175 { |
139 if (BuildVersion != 0) | 176 DoStep1(); |
140 { | 177 } |
141 DefaultRepository.Update(string.Format("{0}.{1}", MajorVersi
on, MinorVersion)); | 178 // Step 2 should be done after the NuGet publisher generated all the
APIs and the samples repository was· |
142 } | 179 // updated with the new packages |
143 | |
144 if (!HasIncomingChanges(new List<Hg> { DefaultRepository })) | |
145 { | |
146 //if (BuildDefaultRepository()) | |
147 { | |
148 CreateNuGetPackages(); | |
149 } | |
150 } | |
151 } | |
152 | |
153 else if (options.Step == 2) | 180 else if (options.Step == 2) |
154 { | 181 { |
155 Console.WriteLine(); | 182 DoStep2(); |
156 Console.WriteLine(); | 183 } |
| 184 } |
| 185 |
| 186 /// <summary>Creates Google.Apis and Google.Apis.Authentication packages
.</summary> |
| 187 private void DoStep1() |
| 188 { |
| 189 if (BuildVersion != 0) |
| 190 { |
| 191 DefaultRepository.Update(string.Format("{0}.{1}", MajorVersion,
MinorVersion)); |
| 192 } |
| 193 |
| 194 // if there are incoming changes those changes will be printed, othe
rwise we can continue in the process |
| 195 if (!HasIncomingChanges(new List<Hg> { DefaultRepository })) |
| 196 { |
| 197 // in case build fails the method will print its failures |
| 198 if (BuildDefaultRepository()) |
| 199 { |
| 200 CreateCoreNuGetPackages(); |
| 201 // TODO(peleyal): release notes should be part of the packag
e |
| 202 } |
| 203 } |
| 204 } |
| 205 |
| 206 /// <summary> |
| 207 /// Doing the following: |
| 208 /// <list type="number"> |
| 209 /// <item><description>Builds samples</description></item> |
| 210 /// <item><description>Creates a release notes</description></item> |
| 211 /// <item><description>Update wiki download page</description></item> |
| 212 /// <item><description>Create a new release in the contrib repository</d
escription></item> |
| 213 /// <item><description>Commits, Tags and Pushes</description></item> |
| 214 /// </list> |
| 215 /// </summary> |
| 216 private void DoStep2() |
| 217 { |
| 218 Console.WriteLine(); |
| 219 Console.WriteLine(); |
| 220 Console.WriteLine("========================="); |
| 221 Console.WriteLine("Prerequisites for Step 2:"); |
| 222 Console.WriteLine("You ran Step 1."); |
| 223 Console.WriteLine("You upgraded the Google.Apis NuGet packages for e
ach sample in the samples " + |
| 224 "repository and pushed that change."); |
| 225 Console.WriteLine("========================="); |
| 226 if (!CanContinue()) |
| 227 { |
| 228 return; |
| 229 } |
| 230 |
| 231 SamplesRepository = new Hg(new Uri(string.Format(CloneUrlFormat, ".s
amples")), "samples"); |
| 232 WikiRepository = new Hg(new Uri(string.Format(CloneUrlFormat, ".wiki
")), "wiki"); |
| 233 ContribRepository = new Hg(new Uri(string.Format(CloneUrlFormat, ".c
ontrib")), "contrib"); |
| 234 |
| 235 // if there are incoming changes those changes will be printed, othe
rwise we can continue in the· |
| 236 // process |
| 237 if (!HasIncomingChanges(AllRepositories)) |
| 238 { |
| 239 BuildSamples(); |
| 240 var notes = CreateContribNewRelease(); |
| 241 UpdateWiki(notes); |
| 242 |
| 243 foreach (var repository in AllRepositories) |
| 244 { |
| 245 repository.AddRemoveFiles(); |
| 246 } |
| 247 |
157 Console.WriteLine("========================="); | 248 Console.WriteLine("========================="); |
158 Console.WriteLine("Prerequisites for Step 2:"); | 249 Console.WriteLine("Commit, Tag and Push"); |
159 Console.WriteLine("You ran Step 1."); | 250 Console.WriteLine("========================="); |
160 Console.WriteLine("You upgrade the libraries in the samples repo
sitory and pushed that change."); | 251 if (!CanContinue()) |
| 252 { |
| 253 return; |
| 254 } |
| 255 |
| 256 // commit |
| 257 CommitAndTag(); |
| 258 |
| 259 // push |
| 260 foreach (Hg repository in AllRepositories) |
| 261 { |
| 262 repository.Push(); |
| 263 } |
| 264 |
| 265 // create branch |
| 266 PrintCreateBranch(); |
| 267 |
| 268 // publish core components to NuGet |
| 269 if (!string.IsNullOrEmpty(options.NuGetApiKey)) |
| 270 { |
| 271 PublishPackagesToNuGet(); |
| 272 Console.WriteLine("Now... you should run the NuGet publisher
to publish a new PCL " |
| 273 + "for each generated Google API. Run: " + |
| 274 "Google.Apis.NuGet.Publisher --all_apis true -m publishe
r -k [NUGET_KEY]"); |
| 275 } |
| 276 else |
| 277 { |
| 278 TraceSource.TraceEvent(TraceEventType.Error, "NuGet API key
is empty!"); |
| 279 } |
| 280 } |
| 281 } |
| 282 |
| 283 /// <summary>Asks the user if he wants to continue in the process.</summ
ary> |
| 284 /// <returns><c>true</c> if the user to press 'y' or 'yes' to continue</
returns> |
| 285 private bool CanContinue() |
| 286 { |
| 287 var yesOptions = new[] { "y", "yes" }; |
| 288 var noOptions = new[] { "n", "no" }; |
| 289 |
| 290 string input; |
| 291 do |
| 292 { |
161 Console.WriteLine("Press 'y' | 'yes' to continue, or 'n' | 'no'
to stop"); | 293 Console.WriteLine("Press 'y' | 'yes' to continue, or 'n' | 'no'
to stop"); |
162 Console.WriteLine("========================="); | 294 input = Console.ReadLine().ToLower(); |
163 var input = Console.ReadLine(); | 295 } while (!yesOptions.Contains(input) && !noOptions.Contains(input)); |
164 var yesOptions = new[] { "y", "yes" }; | 296 |
165 var noOptions = new[] { "n", "no" }; | 297 return yesOptions.Contains(input); |
166 while (!yesOptions.Contains(input) && !noOptions.Contains(input)
) | 298 } |
167 { | 299 |
168 Console.WriteLine("please press 'y' | 'yes' | 'n' | 'no' onl
y"); | 300 /// <summary>Publishes the core packages to NuGet main repository.</summ
ary> |
169 input = Console.ReadLine(); | |
170 } | |
171 | |
172 if (yesOptions.Contains(input)) | |
173 { | |
174 SamplesRepository = Hg.Get("samples", string.Format(CloneUrl
, ".samples")); | |
175 WikiRepository = Hg.Get("wiki", string.Format(CloneUrl, ".wi
ki")); | |
176 ContribRepository = Hg.Get("contrib", string.Format(CloneUrl
, ".contrib")); | |
177 | |
178 if (!HasIncomingChanges(new List<Hg> {· | |
179 DefaultRepository, SamplesRepository, WikiRepository, Co
ntribRepository })) | |
180 { | |
181 BuildSamples(); | |
182 var notes = BuildContribNewRelease(); | |
183 UpdateWiki(notes); | |
184 | |
185 AddRemoveFiles(); | |
186 | |
187 Console.WriteLine("========================="); | |
188 Console.WriteLine("Commit, Tag and Push"); | |
189 Console.WriteLine("Press 'y' | 'yes' to continue, or 'n'
| 'no' to stop"); | |
190 Console.WriteLine("========================="); | |
191 input = Console.ReadLine(); | |
192 while (!yesOptions.Contains(input) && !noOptions.Contain
s(input)) | |
193 { | |
194 Console.WriteLine("please press 'y' | 'yes' | 'n' |
'no' only"); | |
195 input = Console.ReadLine(); | |
196 } | |
197 | |
198 if (yesOptions.Contains(input)) | |
199 { | |
200 // commit | |
201 CommitAndTag(); | |
202 | |
203 // push | |
204 foreach (Hg repository in AllRepositories) | |
205 { | |
206 repository.Push(); | |
207 } | |
208 | |
209 // create branch | |
210 PrintCreateBranch(); | |
211 | |
212 // publish core components to NuGet | |
213 if (!string.IsNullOrEmpty(options.NuGetApiKey)) | |
214 { | |
215 PublishPackagesToNuGet(); | |
216 } | |
217 else | |
218 { | |
219 TraceSource.TraceEvent(TraceEventType.Error, "Nu
Get API key is empty!"); | |
220 } | |
221 | |
222 } | |
223 } | |
224 } | |
225 } | |
226 else | |
227 { | |
228 TraceSource.TraceEvent(TraceEventType.Error, "Invalid step optio
n!"); | |
229 } | |
230 } | |
231 | |
232 private void PublishPackagesToNuGet() | 301 private void PublishPackagesToNuGet() |
233 { | 302 { |
234 var apiNupkgPath = Path.Combine(NuGetUtils.LocalNuGetPackageFolder, | 303 var apiNupkgPath = Path.Combine(NuGetUtilities.LocalNuGetPackageFold
er, |
235 string.Format("Google.Apis.{0}.nupkg", Tag)); | 304 string.Format("Google.Apis.{0}.nupkg", Tag)); |
236 NuGetUtils.PublishToNuget(apiNupkgPath, options.NuGetApiKey); | 305 NuGetUtilities.PublishToNuget(apiNupkgPath, options.NuGetApiKey); |
237 | 306 |
238 var authenticationNupkgPath = Path.Combine(NuGetUtils.LocalNuGetPack
ageFolder, | 307 var authenticationNupkgPath = Path.Combine(NuGetUtilities.LocalNuGet
PackageFolder, |
239 string.Format("Google.Apis.Authentication.{0}.nupkg", Tag)); | 308 string.Format("Google.Apis.Authentication.{0}.nupkg", Tag)); |
240 NuGetUtils.PublishToNuget(authenticationNupkgPath, options.NuGetApiK
ey); | 309 NuGetUtilities.PublishToNuget(authenticationNupkgPath, options.NuGet
ApiKey); |
241 } | 310 } |
242 | 311 |
243 private void AddRemoveFiles() | 312 /// <summary> |
244 { | 313 /// Displays the user orders how to create a branch (only in case it's a
new major or minor release. |
245 foreach (var repository in AllRepositories) | 314 /// </summary> |
246 { | |
247 repository.AddUnversionedFiles(); | |
248 repository.RemoveDeletedFiles(); | |
249 } | |
250 } | |
251 | |
252 /// <summary>Displays the user orders how to create a branch.</summary> | |
253 private void PrintCreateBranch() | 315 private void PrintCreateBranch() |
254 { | 316 { |
255 if (BuildVersion != 0) | 317 if (BuildVersion != 0) |
256 { | 318 { |
257 // No need to branch in that case | 319 // No need to branch in that case |
258 return; | 320 return; |
259 } | 321 } |
260 | 322 |
261 // TODO(peleyal): automate this as well | 323 // TODO(peleyal): consider automate this as well |
262 Console.WriteLine("You should create a new branch for this release n
ow:"); | 324 Console.WriteLine("You should create a new branch for this release n
ow:"); |
263 Console.WriteLine("cd " + DefaultRepository.WorkingDirectory); | 325 Console.WriteLine("cd " + DefaultRepository.WorkingDirectory); |
264 var branchVersion = string.Format("{0}.{1}", MajorVersion, MinorVers
ion); | 326 var branchVersion = string.Format("{0}.{1}", MajorVersion, MinorVers
ion); |
265 Console.WriteLine("hg branch " + branchVersion); | 327 Console.WriteLine("hg branch " + branchVersion); |
266 Console.WriteLine(string.Format("hg commit -m create {0} branch", br
anchVersion)); | 328 Console.WriteLine(string.Format("hg commit -m create {0} branch", br
anchVersion)); |
267 Console.WriteLine("hg push --new-branch"); | 329 Console.WriteLine("hg push --new-branch"); |
268 } | 330 } |
269 | 331 |
| 332 /// <summary>Commits all changes in all repositories and tags the "defau
lt" repository.</summary> |
270 private void CommitAndTag() | 333 private void CommitAndTag() |
271 { | 334 { |
272 foreach (var repository in AllRepositories) | 335 foreach (var repository in AllRepositories) |
273 { | 336 { |
274 repository.AddUnversionedFiles(); | |
275 repository.RemoveDeletedFiles(); | |
276 | |
277 TraceSource.TraceEvent(TraceEventType.Information, "{0} - Commit
ting", repository.Name); | 337 TraceSource.TraceEvent(TraceEventType.Information, "{0} - Commit
ting", repository.Name); |
278 | 338 |
279 bool committed = repository.Commit("Release " + Tag); | 339 bool committed = repository.Commit("Release " + Tag); |
280 | 340 |
| 341 // TODO(peleyal): think to remove this if from this function. I
don't like a if inside a loop statement |
281 if (repository.Equals(DefaultRepository) && committed) | 342 if (repository.Equals(DefaultRepository) && committed) |
282 { | 343 { |
283 try | 344 try |
284 { | 345 { |
285 repository.Tag(Tag, false); | 346 repository.Tag(Tag, false); |
286 TraceSource.TraceEvent(TraceEventType.Information, "{0}
- Was tagged \"{1}\"", | 347 TraceSource.TraceEvent(TraceEventType.Information, "{0}
- Was tagged \"{1}\"", |
287 repository.Name, Tag); | 348 repository.Name, Tag); |
288 } | 349 } |
289 catch (Exception ex) | 350 catch (Exception ex) |
290 { | 351 { |
291 TraceSource.TraceEvent(TraceEventType.Error, "{0} - Tagg
ing Failed. Exception is: {1}", | 352 TraceSource.TraceEvent(TraceEventType.Error, "{0} - Tagg
ing Failed. Exception is: {1}", |
292 repository.Name, ex.Message); | 353 repository.Name, ex.Message); |
293 } | 354 } |
294 } | 355 } |
295 | 356 |
296 TraceSource.TraceEvent(TraceEventType.Information, "{0} - Commit
ted", repository.Name); | 357 TraceSource.TraceEvent(TraceEventType.Information, "{0} - Commit
ted", repository.Name); |
297 } | 358 } |
298 } | 359 } |
299 | 360 |
| 361 /// <summary>Updates the "Downloads" wiki page with the new release note
s.</summary> |
300 private void UpdateWiki(string notes) | 362 private void UpdateWiki(string notes) |
301 { | 363 { |
302 TraceSource.TraceEvent(TraceEventType.Information, "Updating wiki do
wnloads page"); | 364 TraceSource.TraceEvent(TraceEventType.Information, "Updating wiki do
wnloads page"); |
303 | 365 |
304 // TODO(peleyal): improve. Currently we count on that old release of
X.Y.Z is X.Y-1.0 | 366 // TODO(peleyal): improve. Currently we count on that old release of
X.Y.Z is X.Y-1.0 |
305 var oldVersion = string.Format("{0}.{1}.{2}", MajorVersion, MinorVer
sion - 1, 0); | 367 var oldVersion = string.Format("{0}.{1}.{2}", MajorVersion, MinorVer
sion - 1, 0); |
306 DownloadsPageUpdater.UpdateWiki(WikiRepository.WorkingDirectory, not
es, oldVersion, options.Version); | 368 DownloadsPageUpdater.UpdateWiki(WikiRepository.WorkingDirectory, not
es, oldVersion, options.Version); |
307 | 369 |
308 TraceSource.TraceEvent(TraceEventType.Information, "wiki downloads p
age was updated"); | 370 TraceSource.TraceEvent(TraceEventType.Information, "wiki downloads p
age was updated"); |
309 } | 371 } |
310 | 372 |
311 private string BuildContribNewRelease() | 373 /// <summary>Creates a new release in the "contrib" repository.</summary
> |
| 374 /// <returns>The release notes of this version</returns> |
| 375 private string CreateContribNewRelease() |
312 { | 376 { |
313 TraceSource.TraceEvent(TraceEventType.Information, "Building Contrib
release"); | 377 TraceSource.TraceEvent(TraceEventType.Information, "Building Contrib
release"); |
314 | 378 |
315 string releaseDir = ContribRepository.Combine(Tag); | 379 string releaseDir = ContribRepository.Combine(Tag); |
316 | 380 |
317 // Clear existing directories. | 381 // Clear existing directories. |
318 DirectoryUtils.ClearOrCreateDirectory(releaseDir); | 382 DirectoryUtilities.ClearOrCreateDirectory(releaseDir); |
319 string genDir = Path.Combine(releaseDir, "Generated"); | 383 string genDir = Path.Combine(releaseDir, "Generated"); |
320 Directory.CreateDirectory(genDir); | 384 Directory.CreateDirectory(genDir); |
321 | 385 |
322 #region [RELEASE_VERSION]/Generated/Bin | 386 #region [RELEASE_VERSION]/Generated/Bin |
323 | 387 |
324 string binDir = Path.Combine(genDir, "Bin"); | 388 string binDir = Path.Combine(genDir, "Bin"); |
325 TraceSource.TraceEvent(TraceEventType.Information, "Generating \"{0}
\" directory", | 389 TraceSource.TraceEvent(TraceEventType.Information, "Generating \"{0}
\" directory", |
326 DirectoryUtils.GetRelativePath(binDir, ContribRepository.Working
Directory)); | 390 DirectoryUtilities.GetRelativePath(binDir, ContribRepository.Wor
kingDirectory)); |
| 391 |
327 Directory.CreateDirectory(binDir); | 392 Directory.CreateDirectory(binDir); |
328 | 393 foreach (var project in ReleaseProjects) |
329 { | 394 { |
330 foreach (var project in ReleaseProjects) | 395 var releasePath = Path.Combine(project.DirectoryPath, "Bin", "Re
lease"); |
331 { | 396 foreach (var filePath in Directory.GetFiles(releasePath, "Google
.Apis.*")) |
332 var releasePath = Path.Combine(project.DirectoryPath, "Bin",
"Release"); | 397 { |
333 foreach (var filePath in Directory.GetFiles(releasePath, "Go
ogle.Apis.*")) | 398 File.Copy(filePath, |
334 { | 399 Path.Combine(binDir, filePath.Substring(filePath.LastInd
exOf("\\") + 1)), true); |
335 File.Copy(filePath, | 400 } |
336 Path.Combine(binDir, filePath.Substring(filePath.Las
tIndexOf("\\") + 1)), true); | 401 } |
337 } | 402 |
338 } | 403 // TODO(peleyal): Put also the nuspec and nupkg |
339 | |
340 // Put also the nuspec and nupkg | |
341 } | |
342 | 404 |
343 #endregion | 405 #endregion |
344 | 406 |
345 #region [RELEASE_VERSION]/ZipFiles | 407 #region [RELEASE_VERSION]/ZipFiles |
346 | 408 |
347 string zipFilesDir = Path.Combine(genDir, "ZipFiles"); | 409 string zipFilesDir = Path.Combine(genDir, "ZipFiles"); |
348 TraceSource.TraceEvent(TraceEventType.Information, "Generating \"{0}
\" directory", | 410 TraceSource.TraceEvent(TraceEventType.Information, "Generating \"{0}
\" directory", |
349 DirectoryUtils.GetRelativePath(zipFilesDir, ContribRepository.Wo
rkingDirectory)); | 411 DirectoryUtilities.GetRelativePath(zipFilesDir, ContribRepositor
y.WorkingDirectory)); |
350 | 412 |
351 Directory.CreateDirectory(zipFilesDir); | 413 Directory.CreateDirectory(zipFilesDir); |
352 { | 414 foreach (var project in ReleaseProjects) |
353 foreach (var project in ReleaseProjects) | 415 { |
354 { | 416 project.Build("Clean"); |
355 project.Build("Clean"); | 417 } |
356 } | 418 |
357 | 419 TraceSource.TraceEvent(TraceEventType.Information, "Release projects
were cleaned"); |
358 TraceSource.TraceEvent(TraceEventType.Information, "Release proj
ects were cleaned"); | 420 |
359 | 421 // source.zip |
360 // source.zip | 422 var fileNameFormat = "google-api-dotnet-client-{0}.{1}.zip"; |
361 var fileNameFormat = "google-api-dotnet-client-{0}.{1}.zip"; | 423 var sourceZipName = string.Format(fileNameFormat, Tag, "source"); |
362 var sourceZipName = string.Format(fileNameFormat, Tag, "source")
; | 424 using (var zip = new ZipFile(Path.Combine(zipFilesDir, sourceZipName
))) |
363 using (var zip = new ZipFile(Path.Combine(zipFilesDir, sourceZip
Name))) | 425 { |
364 { | 426 zip.AddDirectory(Path.Combine(DefaultRepository.WorkingDirectory
, "Src"), "Src"); |
365 zip.AddDirectory(Path.Combine(DefaultRepository.WorkingDirec
tory, "Src"), "Src"); | 427 zip.AddFile(Path.Combine(DefaultRepository.WorkingDirectory, "Go
ogleApisClient.sln"), ""); |
366 zip.AddFile(Path.Combine(DefaultRepository.WorkingDirectory,
"GoogleApisClient.sln"), ""); | 428 zip.AddFile(Path.Combine(DefaultRepository.WorkingDirectory, "LI
CENSE"), ""); |
367 zip.AddFile(Path.Combine(DefaultRepository.WorkingDirectory,
"LICENSE"), ""); | 429 zip.Save(); |
368 zip.Save(); | 430 } |
369 } | 431 TraceSource.TraceEvent(TraceEventType.Information, "{0} was created"
, sourceZipName); |
370 TraceSource.TraceEvent(TraceEventType.Information, "{0} was crea
ted", sourceZipName); | 432 |
371 | 433 // binary.zip |
372 // binary.zip | 434 var binaryZipName = string.Format(fileNameFormat, Tag, "binary"); |
373 var binaryZipName = string.Format(fileNameFormat, Tag, "binary")
; | 435 using (var zip = new ZipFile(Path.Combine(zipFilesDir, binaryZipName
))) |
374 using (var zip = new ZipFile(Path.Combine(zipFilesDir, binaryZip
Name))) | 436 { |
375 { | 437 Directory.GetFiles(binDir).ToList().ForEach(f => zip.AddFile(Pat
h.Combine(binDir, f), "")); |
376 Directory.GetFiles(binDir).ToList().ForEach(f => zip.AddFile
(Path.Combine(binDir, f), "")); | 438 zip.Save(); |
377 zip.Save(); | 439 } |
378 } | 440 TraceSource.TraceEvent(TraceEventType.Information, "{0} was created"
, binaryZipName); |
379 TraceSource.TraceEvent(TraceEventType.Information, "{0} was crea
ted", binaryZipName); | 441 |
380 | 442 |
381 | 443 // samples.zip |
382 // samples.zip | 444 var samplesZipName = string.Format(fileNameFormat, Tag, "samples"); |
383 var samplesZipName = string.Format(fileNameFormat, Tag, "samples
"); | 445 using (var zip = new ZipFile(Path.Combine(zipFilesDir, samplesZipNam
e))) |
384 using (var zip = new ZipFile(Path.Combine(zipFilesDir, samplesZi
pName))) | 446 { |
385 { | 447 foreach (var d in Directory.GetDirectories(SamplesRepository.Wor
kingDirectory)) |
386 foreach (var d in Directory.GetDirectories(SamplesRepository
.WorkingDirectory)) | 448 { |
| 449 if (!d.EndsWith(".hg")) |
387 { | 450 { |
388 if (!d.EndsWith(".hg")) | 451 var directoryName = d.Substring(d.LastIndexOf("\\") + 1)
; |
389 { | 452 zip.AddDirectory(Path.Combine(SamplesRepository.WorkingD
irectory, d), directoryName); |
390 var directoryName = d.Substring(d.LastIndexOf("\\")
+ 1); | |
391 zip.AddDirectory(Path.Combine(SamplesRepository.Work
ingDirectory, d), directoryName); | |
392 } | |
393 } | 453 } |
394 foreach (var f in Directory.GetFiles(SamplesRepository.Worki
ngDirectory, "*.sln")) | 454 } |
395 { | 455 foreach (var f in Directory.GetFiles(SamplesRepository.WorkingDi
rectory, "*.sln")) |
396 zip.AddFile(Path.Combine(SamplesRepository.WorkingDirect
ory, f), ""); | 456 { |
397 } | 457 zip.AddFile(Path.Combine(SamplesRepository.WorkingDirectory,
f), ""); |
398 zip.Save(); | 458 } |
399 } | 459 zip.Save(); |
400 | 460 } |
401 TraceSource.TraceEvent(TraceEventType.Information, "{0} was crea
ted", samplesZipName); | 461 |
402 } | 462 TraceSource.TraceEvent(TraceEventType.Information, "{0} was created"
, samplesZipName); |
403 | 463 |
404 #endregion | 464 #endregion |
405 | 465 |
406 #region [RELEASE_VERSION]/ReleaseNotes.txt | 466 #region [RELEASE_VERSION]/ReleaseNotes.txt |
407 | 467 |
408 var notes = CreateChangelog(); | 468 var notes = GetChangelog(); |
409 TraceSource.TraceEvent(TraceEventType.Information, "Creating Release
Notes file"); | 469 TraceSource.TraceEvent(TraceEventType.Information, "Creating Release
Notes file"); |
410 var noteFilePath = Path.Combine(genDir, "ReleaseNotes.txt"); | 470 var noteFilePath = Path.Combine(genDir, "ReleaseNotes.txt"); |
411 File.WriteAllText(noteFilePath, notes); | 471 File.WriteAllText(noteFilePath, notes); |
412 | 472 |
413 #endregion | 473 #endregion |
414 | 474 |
415 // open the created change-log and read again the notes (in case the
user had modified the file) | 475 // open the created change-log and read again the notes (in case the
user had modified the file) |
416 Process.Start(noteFilePath).WaitForExit(); | 476 Process.Start(noteFilePath).WaitForExit(); |
417 notes = File.ReadAllText(noteFilePath); | 477 notes = File.ReadAllText(noteFilePath); |
418 | 478 |
419 return notes; | 479 return notes; |
420 } | 480 } |
421 | 481 |
422 private string CreateChangelog() | 482 /// <summary> |
| 483 /// Returns the notes list of this release. It contains the change log o
f all pushes to the "default"· |
| 484 /// repository. |
| 485 /// </summary> |
| 486 private string GetChangelog() |
423 { | 487 { |
424 StringBuilder log = new StringBuilder(); | 488 StringBuilder log = new StringBuilder(); |
425 log.AppendLine("Google .NET Client Library"); | 489 log.AppendLine("Google .NET Client Library"); |
426 log.AppendLine(string.Format("Stable Release '{0}'", Tag)); | 490 log.AppendLine(string.Format("Stable Release '{0}'", Tag)); |
427 log.AppendLine(DateTime.UtcNow.ToLongDateString()); | 491 log.AppendLine(DateTime.UtcNow.ToLongDateString()); |
428 log.AppendLine("==========================================="); | 492 log.AppendLine("==========================================="); |
429 | 493 |
430 log.AppendLine().AppendLine("Changes:"); | 494 log.AppendLine().AppendLine("Changes:"); |
431 foreach (string line in DefaultRepository.CreateChangelist()) | 495 foreach (string line in DefaultRepository.CreateChangelist()) |
432 { | 496 { |
433 log.AppendLine(" " + line); | 497 log.AppendLine(" " + line); |
434 } | 498 } |
435 | 499 |
436 return log.ToString(); | 500 return log.ToString(); |
437 } | 501 } |
438 | 502 |
| 503 /// <summary>Builds all projects in the "samples" repository.</summary> |
439 private void BuildSamples() | 504 private void BuildSamples() |
440 { | 505 { |
441 // Build all the samples projects. | 506 // build all the samples projects. |
442 TraceSource.TraceEvent(TraceEventType.Information, "Building the sam
ples"); | 507 TraceSource.TraceEvent(TraceEventType.Information, "Building the sam
ples"); |
443 foreach (string csproj in Directory.GetFiles(SamplesRepository.Worki
ngDirectory, "*.csproj", | 508 foreach (string csproj in Directory.GetFiles(SamplesRepository.Worki
ngDirectory, "*.csproj", |
444 SearchOption.AllDirectories)) | 509 SearchOption.AllDirectories)) |
445 { | 510 { |
446 Project project = new Project(csproj); | 511 Project project = new Project(csproj); |
447 project.SetProperty("Configuration", "Release"); | 512 project.SetProperty("Configuration", "Release"); |
448 TraceSource.TraceEvent(TraceEventType.Information, "Building {0}
", project.GetName()); | 513 TraceSource.TraceEvent(TraceEventType.Information, "Building {0}
", project.GetName()); |
449 bool success = project.Build("Build", new[] { new ConsoleLogger(
LoggerVerbosity.Quiet) }); | 514 bool success = project.Build("Build", new[] { new ConsoleLogger(
LoggerVerbosity.Quiet) }); |
450 if (!success) | 515 if (!success) |
451 { | 516 { |
452 TraceSource.TraceEvent(TraceEventType.Error, "Building {0} F
AILED", project.GetName()); | 517 TraceSource.TraceEvent(TraceEventType.Error, "Building {0} F
AILED", project.GetName()); |
453 } | 518 } |
454 project.Build("Clean"); | 519 project.Build("Clean"); |
455 TraceSource.TraceEvent(TraceEventType.Information, "Building {0}
succeeded", project.GetName()); | 520 TraceSource.TraceEvent(TraceEventType.Information, "Building {0}
succeeded", project.GetName()); |
456 } | 521 } |
457 } | 522 } |
458 | 523 |
| 524 /// <summary>Returns <c>true</c> if one of the repositories has incoming
changes.</summary> |
459 private bool HasIncomingChanges(IEnumerable<Hg> repositories) | 525 private bool HasIncomingChanges(IEnumerable<Hg> repositories) |
460 { | 526 { |
461 foreach (var repository in repositories) | 527 foreach (var repository in repositories) |
462 { | 528 { |
463 if (repository.HasIncomingChanges) | 529 if (repository.HasIncomingChanges) |
464 { | 530 { |
465 TraceSource.TraceEvent(TraceEventType.Error, | 531 TraceSource.TraceEvent(TraceEventType.Error, |
466 "[{0}] has incoming changes. Run hg pull & update first!
", repository.Name); | 532 "[{0}] has incoming changes. Run hg pull & update first!
", repository.Name); |
467 return true; | 533 return true; |
468 } | 534 } |
469 } | 535 } |
470 return false; | 536 return false; |
471 } | 537 } |
472 | 538 |
473 | 539 /// <summary>Creates the Google.Apis and Google.Apis.Authentication NuGe
t packages.</summary> |
474 private void CreateNuGetPackages() | 540 private void CreateCoreNuGetPackages() |
475 { | 541 { |
476 // create a resource dir in the working folder | 542 // create a resource dir in the working folder |
477 var destDirectory = Path.Combine(Environment.CurrentDirectory, "Reso
urces"); | 543 var destDirectory = Path.Combine(Environment.CurrentDirectory, "Reso
urces"); |
478 if (Directory.Exists(destDirectory)) | 544 if (Directory.Exists(destDirectory)) |
479 { | 545 { |
480 Directory.Delete(destDirectory, true); | 546 Directory.Delete(destDirectory, true); |
481 } | 547 } |
482 | 548 |
483 FileInfo info = new FileInfo(Assembly.GetEntryAssembly().Location); | 549 FileInfo info = new FileInfo(Assembly.GetEntryAssembly().Location); |
484 DirectoryUtils.CopyDirectory(Path.Combine(info.Directory.FullName, "
Resources"), destDirectory); | 550 DirectoryUtilities.CopyDirectory(Path.Combine(info.Directory.FullNam
e, "Resources"), destDirectory); |
485 | 551 |
486 var newVersion = options.Version + "-beta"; | 552 var newVersion = options.Version + "-beta"; |
487 | 553 |
| 554 // get the Google.Apis and Google.Apis.Authentication nuspec files a
nd replace the version in it |
488 var apiNuspec = Path.Combine(destDirectory, "Google.Apis.VERSION.nus
pec"); | 555 var apiNuspec = Path.Combine(destDirectory, "Google.Apis.VERSION.nus
pec"); |
489 var newApiNuspec = apiNuspec.Replace("VERSION", newVersion); | 556 var newApiNuspec = apiNuspec.Replace("VERSION", newVersion); |
490 var authenticationNuspec = Path.Combine(destDirectory, "Google.Apis.
Authentication.VERSION.nuspec"); | 557 var authenticationNuspec = Path.Combine(destDirectory, "Google.Apis.
Authentication.VERSION.nuspec"); |
491 var newAuthenticationNuspec = authenticationNuspec.Replace("VERSION"
, newVersion); | 558 var newAuthenticationNuspec = authenticationNuspec.Replace("VERSION"
, newVersion); |
492 | 559 |
493 File.Move(apiNuspec, newApiNuspec); | 560 File.Move(apiNuspec, newApiNuspec); |
494 File.Move(authenticationNuspec, newAuthenticationNuspec); | 561 File.Move(authenticationNuspec, newAuthenticationNuspec); |
495 | 562 |
496 var allLines = File.ReadAllText(newApiNuspec).Replace("VERSION", new
Version); | 563 var allLines = File.ReadAllText(newApiNuspec).Replace("VERSION", new
Version); |
497 File.WriteAllText(newApiNuspec, allLines); | 564 File.WriteAllText(newApiNuspec, allLines); |
498 | 565 |
499 allLines = File.ReadAllText(newAuthenticationNuspec).Replace("VERSIO
N", newVersion); | 566 allLines = File.ReadAllText(newAuthenticationNuspec).Replace("VERSIO
N", newVersion); |
500 File.WriteAllText(newAuthenticationNuspec, allLines); | 567 File.WriteAllText(newAuthenticationNuspec, allLines); |
501 | 568 |
502 NuGetUtils.CreateLocalNupkgFile(newApiNuspec, Environment.CurrentDir
ectory); | 569 NuGetUtilities.CreateLocalNupkgFile(newApiNuspec, Environment.Curren
tDirectory); |
503 NuGetUtils.CreateLocalNupkgFile(newAuthenticationNuspec, Environment
.CurrentDirectory); | 570 NuGetUtilities.CreateLocalNupkgFile(newAuthenticationNuspec, Environ
ment.CurrentDirectory); |
504 } | 571 } |
505 | 572 |
506 private IEnumerable<Project> releaseProjects; | 573 private IEnumerable<Project> releaseProjects; |
| 574 /// <summary>Gets the release projects of from the "default" repository.
</summary> |
507 private IEnumerable<Project> ReleaseProjects | 575 private IEnumerable<Project> ReleaseProjects |
508 { | 576 { |
509 get | 577 get |
510 { | 578 { |
511 if (releaseProjects != null) | 579 if (releaseProjects != null) |
512 { | 580 { |
513 return releaseProjects; | 581 return releaseProjects; |
514 } | 582 } |
515 | 583 |
516 var releasePaths = new[]· | 584 var releasePaths = new[]· |
517 { | 585 { |
518 DefaultRepository.Combine("Src", "GoogleApis", "GoogleApis.c
sproj"), | 586 DefaultRepository.Combine("Src", "GoogleApis", "GoogleApis.c
sproj"), |
519 DefaultRepository.Combine("Src", "GoogleApis.DotNet4", "Goog
leApis.DotNet4.csproj"), | 587 DefaultRepository.Combine("Src", "GoogleApis.DotNet4", "Goog
leApis.DotNet4.csproj"), |
520 DefaultRepository.Combine("Src", "GoogleApis.Authentication.
OAuth2",· | 588 DefaultRepository.Combine("Src", "GoogleApis.Authentication.
OAuth2",· |
521 "GoogleApis.Authentication.OAuth2.csproj") | 589 "GoogleApis.Authentication.OAuth2.csproj") |
522 }; | 590 }; |
523 releaseProjects = (from path in releasePaths | 591 return releaseProjects = (from path in releasePaths |
524 select new Project(path)).ToList(); | 592 select new Project(path)).ToList(); |
525 return releaseProjects; | 593 } |
526 } | 594 } |
527 } | 595 |
528 | 596 /// <summary>Builds the "default" repository projects.</summary> |
| 597 /// <returns><c>true</c> if the default repository was build successfull
y</returns> |
529 private bool BuildDefaultRepository() | 598 private bool BuildDefaultRepository() |
530 { | 599 { |
531 TraceSource.TraceEvent(TraceEventType.Information, "Building project
s...."); | 600 TraceSource.TraceEvent(TraceEventType.Information, "Building project
s...."); |
532 | 601 |
533 var allProjects = new List<Project>(); | 602 var allProjects = new List<Project>(); |
534 allProjects.AddRange(ReleaseProjects); | 603 allProjects.AddRange(ReleaseProjects); |
535 allProjects.Add(new Project(DefaultRepository.Combine("Src", "Google
Apis.Tests", | 604 allProjects.Add(new Project(DefaultRepository.Combine("Src", "Google
Apis.Tests", |
536 "GoogleApis.Tests.csproj"))); | 605 "GoogleApis.Tests.csproj"))); |
537 allProjects.Add(new Project(DefaultRepository.Combine("Src", "Google
Apis.Authentication.OAuth2.Tests", | 606 allProjects.Add(new Project(DefaultRepository.Combine("Src", "Google
Apis.Authentication.OAuth2.Tests", |
538 "GoogleApis.Authentication.OAuth2.Tests.csproj"))); | 607 "GoogleApis.Authentication.OAuth2.Tests.csproj"))); |
(...skipping 19 matching lines...) Expand all Loading... |
558 | 627 |
559 if (!ReleaseProjects.Contains(project)) | 628 if (!ReleaseProjects.Contains(project)) |
560 { | 629 { |
561 // TODO(peleyal): run unit tests for all test projects | 630 // TODO(peleyal): run unit tests for all test projects |
562 } | 631 } |
563 } | 632 } |
564 | 633 |
565 return true; | 634 return true; |
566 } | 635 } |
567 | 636 |
| 637 /// <summary>Constructs a new program with the given options.</summary> |
568 public Program(Options options) | 638 public Program(Options options) |
569 { | 639 { |
570 this.options = options; | 640 this.options = options; |
571 | 641 |
572 string path = Path.GetFullPath(options.OutputDirectory); | 642 string path = Path.GetFullPath(options.OutputDirectory); |
573 if (!Directory.Exists(path)) | 643 if (!Directory.Exists(path)) |
574 { | 644 { |
575 Directory.CreateDirectory(path); | 645 Directory.CreateDirectory(path); |
576 } | 646 } |
577 | 647 |
578 Environment.CurrentDirectory = path; | 648 Environment.CurrentDirectory = path; |
579 } | 649 } |
580 } | 650 } |
581 } | 651 } |
LEFT | RIGHT |