2
// AspNetAppProject.cs: ASP.NET "Web Application" project type
5
// Michael Hutchinson <m.j.hutchinson@gmail.com>
7
// Copyright (C) 2006 Michael Hutchinson
10
// This source code is licenced under The MIT License:
12
// Permission is hereby granted, free of charge, to any person obtaining
13
// a copy of this software and associated documentation files (the
14
// "Software"), to deal in the Software without restriction, including
15
// without limitation the rights to use, copy, modify, merge, publish,
16
// distribute, sublicense, and/or sell copies of the Software, and to
17
// permit persons to whom the Software is furnished to do so, subject to
18
// the following conditions:
20
// The above copyright notice and this permission notice shall be
21
// included in all copies or substantial portions of the Software.
23
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35
using System.Collections.Generic;
36
using MonoDevelop.Core;
37
using MonoDevelop.Core.Gui;
38
using MonoDevelop.Core.Execution;
39
using MonoDevelop.Core.ProgressMonitoring;
40
using MonoDevelop.Projects;
41
using MonoDevelop.Projects.Dom;
42
using MonoDevelop.Core.Serialization;
43
using MonoDevelop.Deployment;
45
using MonoDevelop.AspNet.Parser;
46
using MonoDevelop.AspNet.Parser.Dom;
47
using MonoDevelop.AspNet.Deployment;
48
using MonoDevelop.AspNet.Gui;
50
namespace MonoDevelop.AspNet
52
[DataInclude (typeof(AspNetAppProjectConfiguration))]
53
public class AspNetAppProject : DotNetProject
55
[ItemProperty("XspParameters", IsExternal=true)]
56
protected XspParameters xspParameters = new XspParameters ();
58
[ItemProperty ("VerifyCodeBehindFields", IsExternal=true)]
59
protected bool verifyCodeBehindFields = true;
61
[ItemProperty ("VerifyCodeBehindEvents", IsExternal=true)]
62
protected bool verifyCodeBehindEvents = true;
64
[ItemProperty("WebDeployTargets", IsExternal=true)]
65
[ItemProperty ("Target", ValueType=typeof(WebDeployTarget), Scope="*")]
66
protected WebDeployTargetCollection webDeployTargets = new WebDeployTargetCollection ();
70
public override string ProjectType {
71
get { return "AspNetApp"; }
74
public override bool IsLibraryBasedProjectType {
78
protected override void OnEndLoad ()
82
//FIX: old version of MD didn't set CompileTarget to Library for ASP.NET projects,
83
// but implicitly assumed they were always libraries. This is not compatible with VS/MSBuild,
84
// so we automatically "upgrade" this value.
85
if (CompileTarget != CompileTarget.Library)
86
CompileTarget = CompileTarget.Library;
89
public XspParameters XspParameters {
90
get { return xspParameters; }
93
public bool VerifyCodeBehindFields {
94
get { return verifyCodeBehindFields; }
95
set { verifyCodeBehindFields = value; }
98
//TODO: make this do something
99
public bool VerifyCodeBehindEvents {
100
get { return verifyCodeBehindEvents; }
101
set { verifyCodeBehindEvents = value; }
104
public WebDeployTargetCollection WebDeployTargets {
105
get { return webDeployTargets; }
108
public override ClrVersion[] SupportedClrVersions {
110
ClrVersion[] versions = base.SupportedClrVersions;
111
if (versions == null)
114
bool ver1 = false, ver2 = false;
115
foreach (ClrVersion version in versions) {
116
if (version == ClrVersion.Net_1_1)
118
if (version == ClrVersion.Net_2_0)
123
return new ClrVersion[] { ClrVersion.Net_1_1, ClrVersion.Net_2_0 };
124
else return new ClrVersion[] { ClrVersion.Net_1_1 };
126
return new ClrVersion[] { ClrVersion.Net_2_0 };
136
public AspNetAppProject ()
140
public AspNetAppProject (string languageName)
141
: base (languageName)
145
public AspNetAppProject (string languageName, ProjectCreateInformation info, XmlElement projectOptions)
146
: base (languageName, info, projectOptions)
150
public override SolutionItemConfiguration CreateConfiguration (string name)
152
AspNetAppProjectConfiguration conf = new AspNetAppProjectConfiguration ();
155
conf.CompilationParameters = LanguageBinding.CreateCompilationParameters (null);
161
#region build/prebuild/execute
164
protected override BuildResult DoBuild (IProgressMonitor monitor, string configuration)
166
//if no files are set to compile, then some compilers will error out
167
//though this is valid with ASP.NET apps, so we just avoid calling the compiler in this case
168
bool needsCompile = false;
169
foreach (ProjectFile pf in Files) {
170
if (pf.BuildAction == BuildAction.Compile) {
178
return base.DoBuild (monitor, configuration);
180
return new BuildResult ();
183
static bool CheckXsp (string command)
186
ProcessWrapper p = Runtime.ProcessService.StartProcess (command, "--version", null, null);
194
protected override bool OnGetCanExecute (MonoDevelop.Projects.ExecutionContext context, string configuration)
196
return context.ExecutionHandlerFactory.SupportsPlatform (ExecutionPlatform.Native);
199
protected override void DoExecute (IProgressMonitor monitor, ExecutionContext context, string config)
201
//check XSP is available
203
AspNetAppProjectConfiguration configuration = (AspNetAppProjectConfiguration) GetConfiguration (config);
205
ClrVersion clrVersion = configuration.ClrVersion;
206
string xspVersion = (clrVersion == ClrVersion.Net_1_1)? "xsp" : "xsp2";
207
if (!CheckXsp (xspVersion)) {
208
monitor.ReportError (string.Format ("The \"{0}\" web server cannot be started. Please ensure that it is installed.",xspVersion), null);
212
IConsole console = null;
213
AggregatedOperationMonitor operationMonitor = new AggregatedOperationMonitor (monitor);
216
IExecutionHandler handler = context.ExecutionHandlerFactory.CreateExecutionHandler ("Native");
218
throw new Exception ("Could not obtain platform handler.");
220
if (configuration.ExternalConsole)
221
console = context.ExternalConsoleFactory.CreateConsole (!configuration.PauseConsoleOutput);
223
console = context.ConsoleFactory.CreateConsole (!configuration.PauseConsoleOutput);
225
monitor.Log.WriteLine ("Running web server...");
227
//set mono debug mode if project's in debug mode
228
Dictionary<string, string> envVars = new Dictionary<string,string> ();
229
if (configuration.DebugMode)
230
envVars ["MONO_OPTIONS"] = "--debug";
232
IProcessAsyncOperation op = handler.Execute (xspVersion, XspParameters.GetXspParameters (), BaseDirectory, envVars, console);
233
operationMonitor.AddOperation (op); //handles cancellation
235
//launch a separate thread to detect the running server and launch a web browser
236
string url = String.Format ("http://{0}:{1}", this.XspParameters.Address, this.XspParameters.Port);
237
BrowserLauncherOperation browserLauncher = BrowserLauncher.LaunchWhenReady (url);
238
operationMonitor.AddOperation (browserLauncher);
240
//report errors from the browser launcher
241
browserLauncher.Completed += delegate (IAsyncOperation blop) {
243
MessageService.ShowError (
244
GettextCatalog.GetString ("Error launching web browser"),
245
((BrowserLauncherOperation)blop).Error.ToString ()
249
op.WaitForCompleted ();
250
monitor.Log.WriteLine ("The web server exited with code: {0}", op.ExitCode);
252
//if server shut down before browser launched, abort browser launch
253
if (!browserLauncher.IsCompleted) {
254
browserLauncher.Cancel ();
255
browserLauncher.WaitForCompleted ();
257
} catch (Exception ex) {
258
monitor.ReportError ("Could not launch web server.", ex);
260
operationMonitor.Dispose ();
268
#region File utility methods
270
public WebSubtype DetermineWebSubtype (ProjectFile file)
272
if (LanguageBinding != null && LanguageBinding.IsSourceCodeFile (file.FilePath))
273
return WebSubtype.Code;
274
return DetermineWebSubtype (file.Name);
277
public static WebSubtype DetermineWebSubtype (string fileName)
279
string extension = System.IO.Path.GetExtension (fileName);
280
if (extension == null)
281
return WebSubtype.None;
282
extension = extension.ToLower ().TrimStart ('.');
284
//NOTE: No way to identify WebSubtype.Code from here
285
//use the instance method for that
289
return WebSubtype.WebForm;
291
return WebSubtype.MasterPage;
293
return WebSubtype.WebHandler;
295
return WebSubtype.WebControl;
297
return WebSubtype.WebService;
299
return WebSubtype.Global;
303
return WebSubtype.WebImage;
305
return WebSubtype.WebSkin;
307
return WebSubtype.Config;
309
return WebSubtype.BrowserDefinition;
311
return WebSubtype.Axd;
313
return WebSubtype.Sitemap;
315
return WebSubtype.Css;
319
return WebSubtype.Html;
321
return WebSubtype.JavaScript;
323
return WebSubtype.None;
329
#region special files
333
#region Reference handling
335
protected override void OnReferenceAddedToProject (ProjectReferenceEventArgs e)
337
//short-circuit if the project is being deserialised
339
base.OnReferenceAddedToProject (e);
343
UpdateWebConfigRefs ();
345
base.OnReferenceAddedToProject (e);
348
protected override void OnReferenceRemovedFromProject (ProjectReferenceEventArgs e)
350
//short-circuit if the project is being deserialised
352
base.OnReferenceAddedToProject (e);
356
UpdateWebConfigRefs ();
358
base.OnReferenceRemovedFromProject (e);
361
void UpdateWebConfigRefs ()
363
List<string> refs = new List<string> ();
364
foreach (ProjectReference reference in References) {
365
//local copied assemblies are copied to the bin directory so ASP.NET references them automatically
366
if (reference.LocalCopy && (reference.ReferenceType == ReferenceType.Project || reference.ReferenceType == ReferenceType.Assembly))
368
if (string.IsNullOrEmpty (reference.Reference))
370
//these assemblies are referenced automatically by ASP.NET
371
if (IsSystemReference (reference.Reference))
373
//bypass non dotnet projects
374
if ((reference.ReferenceType == ReferenceType.Project) &&
375
(!(reference.OwnerProject.ParentSolution.FindProjectByName (reference.Reference) is DotNetProject)))
377
refs.Add (reference.Reference);
380
string webConfigPath = WebConfigPath;
381
if (!File.Exists (webConfigPath))
384
MonoDevelop.Projects.Text.IEditableTextFile textFile =
385
MonoDevelop.DesignerSupport.OpenDocumentFileProvider.Instance.GetEditableTextFile (webConfigPath);
386
//use textfile API because it's write safe (writes out to another file then moves)
387
if (textFile == null)
388
textFile = MonoDevelop.Projects.Text.TextFile.ReadFile (webConfigPath);
390
//can't use System.Web.Configuration.WebConfigurationManager, as it can only access virtual paths within an app
391
//so need full manual handling
393
System.Xml.XmlDocument doc = new XmlDocument ();
395
//FIXME: PreserveWhitespace doesn't handle whitespace in attribute lists
396
//doc.PreserveWhitespace = true;
397
doc.LoadXml (textFile.Text);
399
//hunt our way to the assemblies element, creating elements if necessary
400
XmlElement configElement = doc.DocumentElement;
401
if (configElement == null || string.Compare (configElement.Name, "configuration", StringComparison.InvariantCultureIgnoreCase) != 0) {
402
configElement = (XmlElement) doc.AppendChild (doc.CreateNode (XmlNodeType.Document, "configuration", null));
404
XmlElement webElement = GetNamedXmlElement (doc, configElement, "system.web");
405
XmlElement compilationNode = GetNamedXmlElement (doc, webElement, "compilation");
406
XmlElement assembliesNode = GetNamedXmlElement (doc, compilationNode, "assemblies");
408
List<XmlNode> existingAdds = new List<XmlNode> ();
409
foreach (XmlNode node in assembliesNode)
410
if (string.Compare (node.Name, "add", StringComparison.InvariantCultureIgnoreCase) == 0)
411
existingAdds.Add (node);
413
//add refs to the doc if they're not in it
414
foreach (string reference in refs) {
417
while (index < existingAdds.Count) {
418
XmlNode node = existingAdds[index];
419
XmlAttribute att = (XmlAttribute) node.Attributes.GetNamedItem ("assembly");
422
string refAtt = att.Value;
423
if (refAtt != null && refAtt == reference) {
424
existingAdds.RemoveAt (index);
432
XmlElement newAdd = doc.CreateElement ("add");
433
XmlAttribute newAtt = doc.CreateAttribute ("assembly");
434
newAtt.Value = reference;
435
newAdd.Attributes.Append (newAtt);
436
assembliesNode.AppendChild (newAdd);
440
//any nodes that weren't removed from the existingAdds list are old/redundant, so remove from doc
441
foreach (XmlNode node in existingAdds)
442
assembliesNode.RemoveChild (node);
444
using (StringWriter writer = new StringWriter ()) {
446
textFile.Text = writer.ToString ();
448
MonoDevelop.Projects.Text.TextFile tf = textFile as MonoDevelop.Projects.Text.TextFile;
451
} catch (Exception e) {
452
LoggingService.LogWarning ("Could not modify application web.config in project " + this.Name, e);
457
XmlElement GetNamedXmlElement (XmlDocument doc, XmlElement parent, string name)
459
XmlElement result = null;
460
foreach (XmlNode node in parent.ChildNodes) {
461
XmlElement elem = node as XmlElement;
462
if (elem != null && string.Compare (elem.Name, name, StringComparison.InvariantCultureIgnoreCase) == 0) {
467
if (result == null) {
468
result = (XmlElement) parent.AppendChild (doc.CreateElement (name));
473
string WebConfigPath {
474
get { return Path.Combine (this.BaseDirectory, "web.config"); }
477
bool IsSystemReference (string reference)
479
foreach (string defaultPrefix in defaultAssemblyRefPrefixes)
480
if (reference.StartsWith (defaultPrefix))
485
static string[] defaultAssemblyRefPrefixes = new string[] {
488
"System.Configuration,",
491
"System.Web.Services,",
494
"System.EnterpriseServices,",
495
"System.Web.Mobile,",
500
#region File event handlers
502
protected override void OnFileAddedToProject (ProjectFileEventArgs e)
504
//short-circuit if the project is being deserialised
506
base.OnFileAddedToProject (e);
510
IEnumerable<string> filesToAdd = MonoDevelop.DesignerSupport.CodeBehind.GuessDependencies
511
(this, e.ProjectFile, groupedExtensions);
513
if (Path.GetFullPath (e.ProjectFile.FilePath) == Path.GetFullPath (WebConfigPath))
514
UpdateWebConfigRefs ();
516
//let the base fire the event before we add files
517
//don't want to fire events out of order of files being added
518
base.OnFileAddedToProject (e);
520
//make sure that the parent and child files are in the project
521
if (filesToAdd != null) {
522
foreach (string file in filesToAdd) {
523
//NOTE: this only adds files if they are not already in the project
529
public override string GetDefaultBuildAction (string fileName)
532
WebSubtype type = DetermineWebSubtype (fileName);
533
if (type == WebSubtype.Code)
534
return BuildAction.Compile;
535
if (type != WebSubtype.None)
536
return BuildAction.Content;
538
return base.GetDefaultBuildAction (fileName);
541
static string[] groupedExtensions = { ".aspx", ".master", ".ashx", ".ascx", ".asmx", ".asax" };
545
public List<string> GetNotPresentSpecialDirectories ()
547
List<string> notPresent = new List<string> ();
549
if (TargetFramework.ClrVersion == MonoDevelop.Core.ClrVersion.Net_2_0)
550
foreach (string dir in specialDirs20)
551
if (Files.GetFile (Path.Combine (BaseDirectory, dir)) == null)
552
notPresent.Add (dir);
557
static readonly string [] specialDirs20 = new string [] {
564
"App_LocalResources",
565
"App_GlobalResources",
568
protected override IList<string> GetCommonBuildActions ()
570
return new string[] {
574
BuildAction.EmbeddedResource,
580
public enum WebSubtype