BOO-999
[boo.git] / src / Boo.Microsoft.Build.Tasks / Booc.boo
blob3ded51ffc1bcb84f54d7786e224142dacd9760be
1 #region license
2 // Copyright (c) 2003, 2004, 2005 Rodrigo B. de Oliveira (rbo@acm.org)
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without modification,
6 // are permitted provided that the following conditions are met:
7 //
8 // * Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright notice,
11 // this list of conditions and the following disclaimer in the documentation
12 // and/or other materials provided with the distribution.
13 // * Neither the name of Rodrigo B. de Oliveira nor the names of its
14 // contributors may be used to endorse or promote products derived from this
15 // software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #endregion
29 namespace Boo.Microsoft.Build.Tasks
31 import Microsoft.Build.Framework
32 import Microsoft.Build.Tasks
33 import Microsoft.Build.Utilities
34 import System
35 import System.Diagnostics
36 import System.IO
37 import System.Globalization
38 import System.Text.RegularExpressions
39 import System.Threading
41 class Booc(ManagedCompiler):
42 """
43 Represents the Boo compiler MSBuild task.
45 Authors:
46 Sorin Ionescu (sorin.ionescu@gmail.com)
47 """
48 def constructor():
49 NoLogo = true
51 Pipeline:
52 """
53 Gets/sets a specific pipeline to add to the compiler process.
54 """
55 get:
56 return Bag['Pipelines'] as string
57 set:
58 Bag['Pipelines'] = value
59 NoStandardLib:
60 """
61 Gets/sets if we want to link to the standard libraries or not.
62 """
63 get:
64 return GetBoolParameterWithDefault("NoStandardLib", false)
65 set:
66 Bag['NoStandardLib'] = value
67 WhiteSpaceAgnostic:
68 """
69 Gets/sets if we want to use whitespace agnostic mode.
70 """
71 get:
72 return GetBoolParameterWithDefault("WhiteSpaceAgnostic", false)
73 set:
74 Bag['WhiteSpaceAgnostic'] = value
75 Ducky:
76 """
77 Gets/sets if we want to use ducky mode.
78 """
79 get:
80 return GetBoolParameterWithDefault("Ducky", false)
81 set:
82 Bag['Ducky'] = value
83 Verbosity:
84 """
85 Gets/sets the verbosity level.
86 """
87 get:
88 return Bag['Verbosity'] as string
89 set:
90 Bag['Verbosity'] = value
92 Culture:
93 """
94 Gets/sets the culture.
95 """
96 get:
97 return Bag['Culture'] as string
98 set:
99 Bag['Culture'] = value
101 SourceDirectory:
103 Gets/sets the source directory.
105 get:
106 return Bag['Source Directory'] as string
107 set:
108 Bag['Source Directory'] = value
110 DefineSymbols:
112 Gets/sets the conditional compilation symbols.
114 get:
115 return Bag['DefineSymbols'] as string
116 set:
117 Bag['DefineSymbols'] = value
119 ToolName:
121 Gets the tool name.
123 get:
124 return "booc.exe"
126 override def Execute():
128 Executes the task.
130 Returns:
131 true if the task completed successfully; otherwise, false.
133 boocCommandLine = CommandLineBuilderExtension()
134 AddResponseFileCommands(boocCommandLine)
136 warningPattern = regex(
137 '^(?<file>.*?)(\\((?<line>\\d+),(?<column>\\d+)\\):)?' +
138 '(\\s?)(?<code>BCW\\d{4}):(\\s)WARNING:(\\s)(?<message>.*)$',
139 RegexOptions.Compiled)
140 # Captures the file, line, column, code, and message from a BOO warning
141 # in the form of: Program.boo(1,1): BCW0000: WARNING: This is a warning.
143 errorPattern = regex(
144 '^(((?<file>.*?)\\((?<line>\\d+),(?<column>\\d+)\\): )?' +
145 '(?<code>BCE\\d{4})|(?<errorType>Fatal) error):' +
146 '( Boo.Lang.Compiler.CompilerError:)?' +
147 ' (?<message>.*?)($| --->)',
148 RegexOptions.Compiled |
149 RegexOptions.ExplicitCapture |
150 RegexOptions.Multiline)
152 * Captures the file, line, column, code, error type, and message from a
153 * BOO error of the form of:
154 * 1. Program.boo(1,1): BCE0000: This is an error.
155 * 2. Program.boo(1,1): BCE0000: Boo.Lang.Compiler.CompilerError:
156 * This is an error. ---> Program.boo:4:19: This is an error
157 * 3. BCE0000: This is an error.
158 * 4. Fatal error: This is an error.
160 * The second line of the following error format is not cought because
161 * .NET does not support if|then|else in regular expressions,
162 * and the regex will be horrible complicated.
163 * The second line is as worthless as the first line.
164 * Therefore, it is not worth implementing it.
166 * Fatal error: This is an error.
167 * Parameter name: format.
170 buildSuccess = true
171 outputLine = String.Empty
172 errorLine = String.Empty
173 readingDoneEvents = (ManualResetEvent(false), ManualResetEvent(false))
175 boocProcessStartInfo = ProcessStartInfo(
176 FileName: GenerateFullPathToTool(),
177 Arguments: boocCommandLine.ToString(),
178 ErrorDialog: false,
179 CreateNoWindow: true,
180 RedirectStandardError: true,
181 RedirectStandardInput: false,
182 RedirectStandardOutput: true,
183 UseShellExecute: false)
185 boocProcess = Process(StartInfo: boocProcessStartInfo)
187 parseOutput = def(line as string):
188 warningPatternMatch = warningPattern.Match(line)
189 errorPatternMatch = errorPattern.Match(line)
191 if warningPatternMatch.Success:
192 lineOut = 0
193 columnOut = 0
194 int.TryParse(warningPatternMatch.Groups['line'].Value, lineOut)
195 int.TryParse(warningPatternMatch.Groups['column'].Value, columnOut)
196 Log.LogWarning(
197 null,
198 warningPatternMatch.Groups['code'].Value,
199 null,
200 warningPatternMatch.Groups['file'].Value,
201 lineOut,
202 columnOut,
205 warningPatternMatch.Groups['message'].Value)
207 elif errorPatternMatch.Success:
208 code = errorPatternMatch.Groups['code'].Value
209 code = 'BCE0000' if string.IsNullOrEmpty(code)
210 file = errorPatternMatch.Groups['file'].Value
211 file = 'BOOC' if string.IsNullOrEmpty(file)
213 try:
214 lineNumber = int.Parse(
215 errorPatternMatch.Groups['line'].Value,
216 NumberStyles.Integer)
218 except FormatException:
219 lineNumber = 0
221 try:
222 columnNumber = int.Parse(
223 errorPatternMatch.Groups['column'].Value,
224 NumberStyles.Integer)
226 except FormatException:
227 columnNumber = 0
229 Log.LogError(
230 errorPatternMatch.Groups['errorType'].Value.ToLower(),
231 code,
232 null,
233 file,
234 lineNumber,
235 columnNumber,
238 errorPatternMatch.Groups['message'].Value)
240 buildSuccess = false
242 else:
243 Log.LogMessage(MessageImportance.Normal, line)
245 readStandardOutput = def():
246 while true:
247 outputLine = boocProcess.StandardOutput.ReadLine()
249 if outputLine:
250 parseOutput(outputLine)
252 else:
253 readingDoneEvents[0].Set()
254 break
256 readStandardError = def():
257 while true:
258 errorLine = boocProcess.StandardError.ReadLine()
260 if errorLine:
261 parseOutput(errorLine)
263 else:
264 readingDoneEvents[1].Set()
265 break
267 standardOutputReadingThread = Thread(readStandardOutput as ThreadStart)
268 standardErrorReadingThread = Thread(readStandardError as ThreadStart)
269 # Two threads are required (MSDN); otherwise, a deadlock WILL occur.
271 try:
272 boocProcess.Start()
274 Log.LogMessage(
275 MessageImportance.High,
276 "${ToolName} ${boocProcess.StartInfo.Arguments}",
277 null)
279 standardOutputReadingThread.Start()
280 standardErrorReadingThread.Start()
282 WaitHandle.WaitAny((readingDoneEvents[0],))
283 WaitHandle.WaitAny((readingDoneEvents[1],))
284 # MSBuild runs on an STA thread, and WaitHandle.WaitAll()
285 # is not supported.
287 except e as Exception:
288 Log.LogErrorFromException(e)
289 buildSuccess = false
291 ensure:
292 boocProcess.Close()
294 return buildSuccess
296 protected override def AddCommandLineCommands(
297 commandLine as CommandLineBuilderExtension):
299 Adds command line commands.
301 Remarks:
302 It prevents <ManagedCompiler> from adding the standard commands.
304 pass
306 protected override def AddResponseFileCommands(
307 commandLine as CommandLineBuilderExtension):
309 Generates the Boo compiler command line.
311 Returns:
312 The Boo compiler command line.
313 """
314 commandLine.AppendSwitchIfNotNull('-t:', TargetType)
315 commandLine.AppendSwitchIfNotNull('-o:', OutputAssembly)
316 commandLine.AppendSwitchIfNotNull('-c:', Culture)
317 commandLine.AppendSwitchIfNotNull('-srcdir:', SourceDirectory)
318 commandLine.AppendSwitchIfNotNull('-keyfile:', KeyFile)
319 commandLine.AppendSwitchIfNotNull('-keycontainer:', KeyContainer)
320 commandLine.AppendSwitchIfNotNull('-p:', Pipeline)
321 commandLine.AppendSwitchIfNotNull('-define:', DefineSymbols)
322 commandLine.AppendSwitchIfNotNull("-lib:", AdditionalLibPaths, ",")
324 if NoLogo:
325 commandLine.AppendSwitch('-nologo')
326 if NoConfig:
327 commandLine.AppendSwitch('-noconfig')
328 if NoStandardLib:
329 commandLine.AppendSwitch('-nostdlib')
330 if DelaySign:
331 commandLine.AppendSwitch('-delaysign')
332 if WhiteSpaceAgnostic:
333 commandLine.AppendSwitch('-wsa')
334 if Ducky:
335 commandLine.AppendSwitch('-ducky')
337 if EmitDebugInformation:
338 commandLine.AppendSwitch('-debug')
339 else:
340 commandLine.AppendSwitch('-debug-')
342 if ResponseFiles:
343 for rsp in ResponseFiles:
344 commandLine.AppendSwitchIfNotNull("@", rsp.ItemSpec)
346 if References:
347 for reference in References:
348 commandLine.AppendSwitchIfNotNull('-r:', reference.ItemSpec)
350 if Resources:
351 for resource in Resources:
352 commandLine.AppendSwitchIfNotNull('-resource:', resource.ItemSpec)
354 if Verbosity:
355 if string.Compare(
356 Verbosity,
357 'Normal',
358 StringComparison.InvariantCultureIgnoreCase) == 0:
359 pass
361 elif string.Compare(
362 Verbosity,
363 'Warning',
364 StringComparison.InvariantCultureIgnoreCase) == 0:
366 commandLine.AppendSwitch('-v')
368 elif string.Compare(
369 Verbosity,
370 'Info',
371 StringComparison.InvariantCultureIgnoreCase) == 0:
373 commandLine.AppendSwitch('-vv')
375 elif string.Compare(
376 Verbosity,
377 'Verbose',
378 StringComparison.InvariantCultureIgnoreCase) == 0:
380 commandLine.AppendSwitch('-vvv')
382 else:
383 Log.LogErrorWithCodeFromResources(
384 'Vbc.EnumParameterHasInvalidValue',
385 'Verbosity',
386 Verbosity,
387 'Normal, Warning, Info, Verbose')
389 commandLine.AppendFileNamesIfNotNull(Sources, ' ')
391 protected override def GenerateFullPathToTool():
393 Generats the full path to booc.exe.
395 path = ""
397 if ToolPath:
398 path = Path.Combine(ToolPath, ToolName)
400 return path if File.Exists(path)
402 path = Path.Combine(
403 Path.GetDirectoryName(typeof(Booc).Assembly.Location),
404 ToolName)
406 return path if File.Exists(path)
408 path = ToolLocationHelper.GetPathToDotNetFrameworkFile(
409 ToolName,
410 TargetDotNetFrameworkVersion.VersionLatest)
412 return path if File.Exists(path)
414 /* //removed this error message for mono compatibility
415 Log.LogErrorWithCodeFromResources(
416 "General.FrameworksFileNotFound",
417 ToolName,
418 ToolLocationHelper.GetDotNetFrameworkVersionFolderPrefix(
419 TargetDotNetFrameworkVersion.Version20))
421 path = "booc"
423 return path