1 // NAnt - A .NET build tool
\r
2 // Copyright (C) 2001 Gerry Shaw
\r
4 // This program is free software; you can redistribute it and/or modify
\r
5 // it under the terms of the GNU General Public License as published by
\r
6 // the Free Software Foundation; either version 2 of the License, or
\r
7 // (at your option) any later version.
\r
9 // This program is distributed in the hope that it will be useful,
\r
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
12 // GNU General Public License for more details.
\r
14 // You should have received a copy of the GNU General Public License
\r
15 // along with this program; if not, write to the Free Software
\r
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\r
18 // Gerry Shaw (gerry_shaw@yahoo.com)
\r
19 // Ian MacLean (ian_maclean@another.com)
\r
21 namespace SourceForge.NAnt {
\r
25 using System.Reflection;
\r
26 using System.Text.RegularExpressions;
\r
28 using System.Xml.XPath;
\r
29 using System.Collections;
\r
30 using System.Collections.Specialized;
\r
33 /// Central representation of an NAnt project.
\r
35 public class Project {
\r
37 public static readonly string BuildFilePattern = "*.build";
\r
40 /// Finds the file name for the build file in the specified directory.
\r
42 /// <param name="directory">The directory to look for a build file. When in doubt use Environment.CurrentDirectory for directory.</param>
\r
43 /// <returns>The path to the build file or <c>null</c> if no build file could be found.</returns>
\r
44 public static string FindBuildFileName(string directory) {
\r
45 string buildFileName = null;
\r
47 // find first file ending in .build
\r
48 DirectoryInfo directoryInfo = new DirectoryInfo(directory);
\r
49 FileInfo[] files = directoryInfo.GetFiles(BuildFilePattern);
\r
50 if (files.Length > 0) {
\r
51 buildFileName = Path.Combine(directory, files[0].Name);
\r
53 return buildFileName;
\r
57 string _defaultTargetName;
\r
58 string _baseDirectory;
\r
59 string _buildFileName;
\r
60 bool _verbose = false;
\r
62 StringCollection _buildTargets = new StringCollection();
\r
63 TaskCollection _tasks = new TaskCollection();
\r
64 TargetCollection _targets = new TargetCollection();
\r
65 XPathTextPositionMap _positionMap; // created when Xml document is loaded
\r
66 TaskFactory _taskFactory; // created in constructor
\r
67 PropertyDictionary _properties = new PropertyDictionary();
\r
70 _taskFactory = new TaskFactory(this);
\r
74 /// The name of the project.
\r
76 public string Name {
\r
77 get { return _name; }
\r
78 set { _name = value; }
\r
81 public string BaseDirectory {
\r
82 get { return _baseDirectory; }
\r
83 set { _baseDirectory = value; }
\r
86 public string BuildFileName {
\r
87 get { return _buildFileName; }
\r
88 set { _buildFileName = value; }
\r
92 /// When true tasks should output more output.
\r
94 public bool Verbose {
\r
95 get { return _verbose; }
\r
96 set { _verbose = value; }
\r
100 /// The list of targets to built.
\r
103 /// Targets are built in the order they appear in the collection. If
\r
104 /// the collection is empty the default target will be built.
\r
106 public StringCollection BuildTargets {
\r
107 get { return _buildTargets; }
\r
111 /// The list of tasks to perform before any targets executed.
\r
114 /// Tasks are executed in the order they appear in the collection.
\r
116 public TaskCollection Tasks {
\r
117 get { return _tasks; }
\r
120 public PropertyDictionary Properties {
\r
121 get { return _properties; }
\r
124 public TargetCollection Targets {
\r
125 get { return _targets; }
\r
128 public bool Run() {
\r
129 bool buildResult = false;
\r
131 DateTime startTime = DateTime.Now;
\r
133 if (BaseDirectory == null) {
\r
134 BaseDirectory = Environment.CurrentDirectory;
\r
136 BaseDirectory = Path.GetFullPath(BaseDirectory);
\r
138 if (BuildFileName == null || BuildFileName == String.Empty) {
\r
139 BuildFileName = FindBuildFileName(BaseDirectory);
\r
140 if (BuildFileName == null) {
\r
141 throw new BuildException(String.Format("Could not find a '{0}' file in '{1}'", BuildFilePattern, BaseDirectory));
\r
145 Log.WriteLine("Buildfile: {0}", BuildFileName);
\r
147 Log.WriteLine("Base Directory: {0}", BaseDirectory);
\r
150 XmlDocument doc = new XmlDocument();
\r
152 doc.Load(BuildFileName);
\r
153 // TODO: validate against xsd schema
\r
154 } catch (XmlException e) {
\r
155 throw new BuildException(String.Format("Could not load '{0}'", BuildFileName), e);
\r
159 Properties.Add("nant.buildfile", BuildFileName);
\r
164 Log.WriteLine("BUILD SUCCEEDED");
\r
166 TimeSpan buildTime = DateTime.Now - startTime;
\r
168 Log.WriteLine("Total time: {0} seconds", (int) buildTime.TotalSeconds);
\r
170 buildResult = true;
\r
171 } catch (BuildException e) {
\r
173 Log.WriteLine("BUILD FAILED");
\r
174 Log.WriteLine(e.Message);
\r
175 if (e.InnerException != null) {
\r
176 Log.WriteLine(e.InnerException.Message);
\r
178 } catch (Exception e) {
\r
179 // all other exceptions should have been caught
\r
181 Log.WriteLine("INTERNAL ERROR");
\r
182 Log.WriteLine(e.ToString());
\r
184 return buildResult;
\r
187 public int AddTasks(string assemblyPath) {
\r
190 if (assemblyPath == null) {
\r
191 assembly = Assembly.GetExecutingAssembly();
\r
193 assembly = Assembly.LoadFrom(assemblyPath);
\r
197 foreach(Type type in assembly.GetTypes()) {
\r
198 if (type.IsSubclassOf(typeof(Task)) && !type.IsAbstract) {
\r
199 if (_taskFactory.Builders.Add(new TaskBuilder(type.FullName, assemblyPath))) {
\r
207 public void Initialize(XmlDocument doc) {
\r
209 Name = doc.SelectSingleNode("project/@name").Value;
\r
211 // make it possible for user to override this value
\r
212 if (BaseDirectory == null) {
\r
213 BaseDirectory = doc.SelectSingleNode("project/@basedir").Value;
\r
216 // used only if BuildTargets collection is empty
\r
217 _defaultTargetName = doc.SelectSingleNode("project/@default").Value;
\r
219 // initialize builtin tasks
\r
222 // init static built in properties
\r
223 Properties.Add("nant.project.name", Name);
\r
224 Properties.Add("nant.base.dir", BaseDirectory);
\r
225 Properties.Add("nant.default.name", _defaultTargetName);
\r
227 // add all environment variables
\r
228 IDictionary variables = Environment.GetEnvironmentVariables();
\r
229 foreach (string name in variables.Keys) {
\r
230 string value = (string) variables[name];
\r
231 Properties.Add("nant.env." + name, value);
\r
234 // Load line Xpath to linenumber array
\r
235 _positionMap = new XPathTextPositionMap(doc.BaseURI);
\r
237 // process all the non-target nodes (these are global tasks for the project)
\r
238 XmlNodeList taskList = doc.SelectNodes("project/*[name() != 'target']");
\r
239 foreach (XmlNode taskNode in taskList) {
\r
241 // TODO: do somethiing like Project.CreateTask(taskNode) and have the project set the location
\r
242 TextPosition textPosition = _positionMap.GetTextPosition(taskNode);
\r
244 Task task = CreateTask(taskNode);
\r
245 if (task != null) {
\r
250 // execute global tasks now - before anything else
\r
251 // this lets us include tasks that do things like add more tasks
\r
252 foreach (Task task in Tasks) {
\r
256 // process all the targets
\r
257 XmlNodeList targetList = doc.SelectNodes("project/target");
\r
258 foreach (XmlNode targetNode in targetList) {
\r
259 Target target = new Target(this);
\r
260 target.Initialize(targetNode);
\r
261 Targets.Add(target);
\r
265 public void Execute() {
\r
266 if (BuildTargets.Count == 0) {
\r
267 BuildTargets.Add(_defaultTargetName);
\r
270 foreach(string targetName in BuildTargets) {
\r
271 Execute(targetName);
\r
275 public void Execute(string targetName) {
\r
276 Target target = Targets.Find(targetName);
\r
277 if (target == null) {
\r
278 throw new BuildException(String.Format("unknown target '{0}'", targetName));
\r
283 public Task CreateTask(XmlNode taskNode) {
\r
284 return CreateTask(taskNode, null);
\r
287 public Task CreateTask(XmlNode taskNode, Target target) {
\r
288 Task task = _taskFactory.CreateTask(taskNode, target);
\r
289 if (task != null) {
\r
290 // save task location in case of error
\r
291 TextPosition pos = _positionMap.GetTextPosition(taskNode);
\r
293 // initialize the task
\r
294 task.Initialize(taskNode, new Location(taskNode.BaseURI, pos.Line, pos.Column));
\r
299 public string ExpandText(string input) {
\r
300 string output = input;
\r
301 if (input != null) {
\r
302 const string pattern = @"\$\{([^\}]*)\}";
\r
303 foreach (Match m in Regex.Matches(input, pattern)) {
\r
304 if (m.Length > 0) {
\r
306 string token = m.ToString();
\r
307 string propertyName = m.Groups[1].Captures[0].Value;
\r
308 string propertyValue = Properties[propertyName];
\r
310 if (propertyValue != null) {
\r
311 output = output.Replace(token, propertyValue);
\r
319 public string GetFullPath(string path) {
\r
320 string baseDir = ExpandText(BaseDirectory);
\r
322 if (path != null) {
\r
323 if (!Path.IsPathRooted(path)) {
\r
324 path = Path.Combine(baseDir, path);
\r
329 return Path.GetFullPath(path);
\r