1 // MonoSolutionItemHandler.cs
4 // Lluis Sanchez Gual <lluis@novell.com>
6 // Copyright (c) 2008 Novell, Inc (http://www.novell.com)
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 using System
.Collections
;
31 using MonoDevelop
.Core
;
32 using MonoDevelop
.Core
.Execution
;
33 using System
.Text
.RegularExpressions
;
34 using MonoDevelop
.Projects
;
35 using System
.CodeDom
.Compiler
;
36 using MonoDevelop
.Ide
.Gui
;
37 using MonoDevelop
.Core
.ProgressMonitoring
;
38 using MonoDevelop
.Projects
.Extensions
;
40 namespace MonoDeveloper
42 public class MonoSolutionItemHandler
: ISolutionItemHandler
44 DotNetProject project
;
46 ArrayList refNames
= new ArrayList ();
51 public MonoSolutionItemHandler (DotNetProject project
)
53 this.project
= project
;
54 project
.FileAddedToProject
+= OnFileAddedToProject
;
55 project
.FileRemovedFromProject
+= OnFileRemovedFromProject
;
56 project
.FileRenamedInProject
+= OnFileRenamedInProject
;
59 public string SourcesFile
{
60 get { return outFile + ".sources"; }
63 public bool SyncFileName
{
67 public string ItemId
{
69 if (project
.ParentSolution
!= null)
70 return project
.ParentSolution
.GetRelativeChildPath (project
.FileName
);
76 public void Save (MonoDevelop
.Core
.IProgressMonitor monitor
)
80 internal void Read (MonoMakefile mkfile
)
84 string basePath
= Path
.GetDirectoryName (mkfile
.FileName
);
87 string targetAssembly
= mkfile
.GetVariable ("LIBRARY");
88 if (targetAssembly
== null) {
89 targetAssembly
= mkfile
.GetVariable ("PROGRAM");
90 if (Path
.GetDirectoryName (targetAssembly
) == "")
91 targetAssembly
= Path
.Combine (basePath
, targetAssembly
);
92 aname
= Path
.GetFileName (targetAssembly
);
94 aname
= Path
.GetFileName (targetAssembly
);
95 string targetName
= mkfile
.GetVariable ("LIBRARY_NAME");
96 if (targetName
!= null) targetAssembly
= targetName
;
97 targetAssembly
= "$(topdir)/class/lib/$(PROFILE)/" + targetAssembly
;
100 outFile
= Path
.Combine (basePath
, aname
);
101 project
.FileName
= mkfile
.FileName
;
103 ArrayList checkedFolders
= new ArrayList ();
106 string sources
= outFile
+ ".sources";
107 StreamReader sr
= new StreamReader (sources
);
109 while ((line
= sr
.ReadLine ()) != null) {
110 line
= line
.Trim (' ','\t');
112 string fname
= Path
.Combine (basePath
, line
);
113 project
.Files
.Add (new ProjectFile (fname
));
115 string dir
= Path
.GetDirectoryName (fname
);
116 if (!checkedFolders
.Contains (dir
)) {
117 checkedFolders
.Add (dir
);
118 fname
= Path
.Combine (dir
, "ChangeLog");
119 if (File
.Exists (fname
))
120 project
.Files
.Add (new ProjectFile (fname
, BuildAction
.Content
));
127 // Project references
128 string refs
= mkfile
.GetVariable ("LIB_MCS_FLAGS");
129 if (refs
== null || refs
== "") refs
= mkfile
.GetVariable ("LOCAL_MCS_FLAGS");
131 if (refs
!= null && refs
!= "") {
132 Regex
var = new Regex(@"(.*?/r:(?<ref>.*?)(( |\t)|$).*?)*");
133 Match match
= var.Match (refs
);
135 foreach (Capture c
in match
.Groups
["ref"].Captures
)
136 refNames
.Add (Path
.GetFileNameWithoutExtension (c
.Value
));
140 int i
= basePath
.LastIndexOf ("/mcs/", basePath
.Length
- 2);
141 string topdir
= basePath
.Substring (0, i
+ 4);
142 targetAssembly
= targetAssembly
.Replace ("$(topdir)", topdir
);
144 if (mkfile
.GetVariable ("NO_TEST") != "yes") {
145 string tname
= Path
.GetFileNameWithoutExtension (aname
) + "_test_";
146 testFileBase
= Path
.Combine (basePath
, tname
);
149 foreach (string sconf
in MonoMakefileFormat
.Configurations
) {
150 DotNetProjectConfiguration conf
= new DotNetProjectConfiguration (sconf
);
151 conf
.CompilationParameters
= project
.LanguageBinding
.CreateCompilationParameters (null);
152 conf
.OutputDirectory
= basePath
;
153 conf
.OutputAssembly
= Path
.GetFileName (targetAssembly
);
154 project
.Configurations
.Add (conf
);
158 IdeApp
.Workspace
.SolutionLoaded
+= CombineOpened
;
161 public void CombineOpened (object sender
, SolutionEventArgs args
)
163 if (args
.Solution
== project
.ParentSolution
) {
164 foreach (string pref
in refNames
) {
165 Project p
= project
.ParentSolution
.FindProjectByName (pref
);
166 if (p
!= null) project
.References
.Add (new ProjectReference (p
));
171 static Regex regexError
= new Regex (@"^(\s*(?<file>.*)\((?<line>\d*)(,(?<column>\d*[\+]*))?\)(:|)\s+)*(?<level>\w+)\s*(?<number>.*):\s(?<message>.*)",
172 RegexOptions
.Compiled
| RegexOptions
.ExplicitCapture
);
174 public BuildResult
RunTarget (MonoDevelop
.Core
.IProgressMonitor monitor
, string target
, string configuration
)
176 if (target
== ProjectService
.BuildTarget
)
178 else if (target
== ProjectService
.CleanTarget
)
181 DotNetProjectConfiguration conf
= (DotNetProjectConfiguration
) project
.GetConfiguration (configuration
);
183 StringWriter output
= new StringWriter ();
184 LogTextWriter tw
= new LogTextWriter ();
185 tw
.ChainWriter (output
);
186 tw
.ChainWriter (monitor
.Log
);
188 ProcessWrapper proc
= Runtime
.ProcessService
.StartProcess ("make", "PROFILE=" + conf
.Id
+ " " + target
, conf
.OutputDirectory
, monitor
.Log
, tw
, null);
189 proc
.WaitForOutput ();
191 CompilerResults cr
= new CompilerResults (null);
192 string[] lines
= output
.ToString().Split ('\n');
193 foreach (string line
in lines
) {
194 CompilerError err
= CreateErrorFromString (line
);
195 if (err
!= null) cr
.Errors
.Add (err
);
198 return new BuildResult (cr
, output
.ToString());
201 private CompilerError
CreateErrorFromString (string error_string
)
203 // When IncludeDebugInformation is true, prevents the debug symbols stats from braeking this.
204 if (error_string
.StartsWith ("WROTE SYMFILE") ||
205 error_string
.StartsWith ("make[") ||
206 error_string
.StartsWith ("OffsetTable") ||
207 error_string
.StartsWith ("Compilation succeeded") ||
208 error_string
.StartsWith ("Compilation failed"))
211 CompilerError error
= new CompilerError();
213 Match match
=regexError
.Match(error_string
);
217 string level
= match
.Result("${level}");
218 if (level
== "warning")
219 error
.IsWarning
= true;
220 else if (level
!= "error")
223 if (String
.Empty
!= match
.Result("${file}"))
224 error
.FileName
= Path
.Combine (project
.BaseDirectory
, match
.Result("${file}"));
225 if (String
.Empty
!= match
.Result("${line}"))
226 error
.Line
=Int32
.Parse(match
.Result("${line}"));
227 if (String
.Empty
!= match
.Result("${column}"))
228 error
.Column
= Int32
.Parse(match
.Result("${column}"));
229 error
.ErrorNumber
= match
.Result ("${number}");
230 error
.ErrorText
= match
.Result ("${message}");
234 void OnFileAddedToProject (object s
, ProjectFileEventArgs e
)
238 if (e
.ProjectFile
.BuildAction
!= BuildAction
.Compile
)
241 AddSourceFile (e
.ProjectFile
.Name
);
244 void OnFileRemovedFromProject (object s
, ProjectFileEventArgs e
)
248 if (e
.ProjectFile
.BuildAction
!= BuildAction
.Compile
)
251 RemoveSourceFile (e
.ProjectFile
.Name
);
254 void OnFileRenamedInProject (object s
, ProjectFileRenamedEventArgs e
)
258 if (e
.ProjectFile
.BuildAction
!= BuildAction
.Compile
)
261 if (RemoveSourceFile (e
.OldName
))
262 AddSourceFile (e
.NewName
);
265 void AddSourceFile (string sourceFile
)
267 StreamReader sr
= null;
268 StreamWriter sw
= null;
271 sr
= new StreamReader (outFile
+ ".sources");
272 sw
= new StreamWriter (outFile
+ ".sources.new");
274 string newFile
= project
.GetRelativeChildPath (sourceFile
);
275 if (newFile
.StartsWith ("./")) newFile
= newFile
.Substring (2);
278 while ((line
= sr
.ReadLine ()) != null) {
279 string file
= line
.Trim (' ','\t');
280 if (newFile
!= null && (file
== "" || string.Compare (file
, newFile
) > 0)) {
281 sw
.WriteLine (newFile
);
287 sw
.WriteLine (newFile
);
289 if (sr
!= null) sr
.Close ();
290 if (sw
!= null) sw
.Close ();
292 File
.Delete (outFile
+ ".sources");
293 File
.Move (outFile
+ ".sources.new", outFile
+ ".sources");
296 bool RemoveSourceFile (string sourceFile
)
298 StreamReader sr
= null;
299 StreamWriter sw
= null;
303 sr
= new StreamReader (outFile
+ ".sources");
304 sw
= new StreamWriter (outFile
+ ".sources.new");
306 string oldFile
= project
.GetRelativeChildPath (sourceFile
);
307 if (oldFile
.StartsWith ("./")) oldFile
= oldFile
.Substring (2);
310 while ((line
= sr
.ReadLine ()) != null) {
311 string file
= line
.Trim (' ','\t');
318 if (sr
!= null) sr
.Close ();
319 if (sw
!= null) sw
.Close ();
322 File
.Delete (outFile
+ ".sources");
323 File
.Move (outFile
+ ".sources.new", outFile
+ ".sources");
328 public void Dispose ()
330 project
.FileAddedToProject
-= OnFileAddedToProject
;
331 project
.FileRemovedFromProject
-= OnFileRemovedFromProject
;
332 project
.FileRenamedInProject
-= OnFileRenamedInProject
;
333 IdeApp
.Workspace
.SolutionLoaded
-= CombineOpened
;
336 public string GetTestFileBase ()
341 public object UnitTest
{
342 get { return unitTest; }
343 set { unitTest = value; }