corrade-vassal – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 #region BSD License
2 /*
3 Copyright (c) 2004 Crestez Leonard (cleonard@go.ro)
4  
5 Redistribution and use in source and binary forms, with or without modification, are permitted
6 provided that the following conditions are met:
7  
8 * Redistributions of source code must retain the above copyright notice, this list of conditions
9 and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
11 and the following disclaimer in the documentation and/or other materials provided with the
12 distribution.
13 * The name of the author may not be used to endorse or promote products derived from this software
14 without specific prior written permission.
15  
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
17 BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
22 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24 #endregion
25  
26 using System;
27 using System.Collections;
28 using System.Collections.Specialized;
29 using System.IO;
30 using System.Text.RegularExpressions;
31  
32 using Prebuild.Core.Attributes;
33 using Prebuild.Core.Interfaces;
34 using Prebuild.Core.Nodes;
35 using Prebuild.Core.Utilities;
36  
37 namespace Prebuild.Core.Targets
38 {
39 [Target("makefile")]
40 public class MakefileTarget : ITarget
41 {
42 #region Fields
43  
44 private Kernel m_Kernel = null;
45  
46 #endregion
47  
48 #region Private Methods
49  
50 // This converts a path relative to the path of a project to
51 // a path relative to the solution path.
52 private string NicePath(ProjectNode proj, string path)
53 {
54 string res;
55 SolutionNode solution = (SolutionNode)proj.Parent;
56 res = Path.Combine(Helper.NormalizePath(proj.FullPath, '/'), Helper.NormalizePath(path, '/'));
57 res = Helper.NormalizePath(res, '/');
58 res = res.Replace("/./", "/");
59 while (res.IndexOf("/../") >= 0)
60 {
61 int a = res.IndexOf("/../");
62 int b = res.LastIndexOf("/", a - 1);
63 res = res.Remove(b, a - b + 3);
64 }
65 res = Helper.MakePathRelativeTo(solution.FullPath, res);
66 if (res.StartsWith("./"))
67 res = res.Substring(2, res.Length - 2);
68 res = Helper.NormalizePath(res, '/');
69 return res;
70 }
71  
72 private void WriteProjectFiles(StreamWriter f, SolutionNode solution, ProjectNode project)
73 {
74 // Write list of source code files
75 f.WriteLine("SOURCES_{0} = \\", project.Name);
76 foreach (string file in project.Files)
77 if (project.Files.GetBuildAction(file) == BuildAction.Compile)
78 f.WriteLine("\t{0} \\", NicePath(project, file));
79 f.WriteLine();
80  
81 // Write list of resource files
82 f.WriteLine("RESOURCES_{0} = \\", project.Name);
83 foreach (string file in project.Files)
84 if (project.Files.GetBuildAction(file) == BuildAction.EmbeddedResource)
85 {
86 string path = NicePath(project, file);
87 f.WriteLine("\t-resource:{0},{1} \\", path, Path.GetFileName(path));
88 }
89 f.WriteLine();
90  
91 // There's also Content and None in BuildAction.
92 // What am I supposed to do with that?
93 }
94  
95 private string FindFileReference(string refName, ProjectNode project)
96 {
97 foreach (ReferencePathNode refPath in project.ReferencePaths)
98 {
99 string fullPath = NicePath(project, Helper.MakeFilePath(refPath.Path, refName, "dll"));
100 if (File.Exists(fullPath))
101 return fullPath;
102 }
103 return null;
104 }
105  
106 private void WriteProjectReferences(StreamWriter f, SolutionNode solution, ProjectNode project)
107 {
108 f.WriteLine("REFERENCES_{0} = \\", project.Name);
109 foreach (ReferenceNode refr in project.References)
110 {
111 string path;
112 // Project references change with configurations.
113 if (solution.ProjectsTable.Contains(refr.Name))
114 continue;
115 path = FindFileReference(refr.Name, project);
116 if (path != null)
117 f.WriteLine("\t-r:{0} \\", path);
118 else
119 f.WriteLine("\t-r:{0} \\", refr.Name);
120 }
121 f.WriteLine();
122 }
123  
124 private void WriteProjectDependencies(StreamWriter f, SolutionNode solution, ProjectNode project)
125 {
126 f.WriteLine("DEPENDENCIES_{0} = \\", project.Name);
127 f.WriteLine("\t$(SOURCES_{0}) \\", project.Name);
128 foreach (string file in project.Files)
129 if (project.Files.GetBuildAction(file) == BuildAction.EmbeddedResource)
130 f.WriteLine("\t{0} \\", NicePath(project, file));
131 f.WriteLine();
132 }
133  
134 private string ProjectTypeToExtension(ProjectType t)
135 {
136 if (t == ProjectType.Exe || t == ProjectType.WinExe)
137 {
138 return "exe";
139 }
140 else if (t == ProjectType.Library)
141 {
142 return "dll";
143 }
144 else
145 {
146 throw new FatalException("Bad ProjectType: {0}", t);
147 }
148 }
149  
150 private string ProjectTypeToTarget(ProjectType t)
151 {
152 if (t == ProjectType.Exe)
153 {
154 return "exe";
155 }
156 else if (t == ProjectType.WinExe)
157 {
158 return "winexe";
159 }
160 else if (t == ProjectType.Library)
161 {
162 return "library";
163 }
164 else
165 {
166 throw new FatalException("Bad ProjectType: {0}", t);
167 }
168 }
169  
170 private string ProjectOutput(ProjectNode project, ConfigurationNode config)
171 {
172 string filepath;
173 filepath = Helper.MakeFilePath((string)config.Options["OutputPath"],
174 project.AssemblyName, ProjectTypeToExtension(project.Type));
175 return NicePath(project, filepath);
176 }
177  
178 // Returns true if two configs in one project have the same output.
179 private bool ProjectClashes(ProjectNode project)
180 {
181 foreach (ConfigurationNode conf1 in project.Configurations)
182 foreach (ConfigurationNode conf2 in project.Configurations)
183 if (ProjectOutput(project, conf1) == ProjectOutput(project, conf2) && conf1 != conf2)
184 {
185 m_Kernel.Log.Write("Warning: Configurations {0} and {1} for project {2} output the same file",
186 conf1.Name, conf2.Name, project.Name);
187 m_Kernel.Log.Write("Warning: I'm going to use some timestamps(extra empty files).");
188 return true;
189 }
190 return false;
191 }
192  
193 private void WriteProject(StreamWriter f, SolutionNode solution, ProjectNode project)
194 {
195 f.WriteLine("# This is for project {0}", project.Name);
196 f.WriteLine();
197  
198 WriteProjectFiles(f, solution, project);
199 WriteProjectReferences(f, solution, project);
200 WriteProjectDependencies(f, solution, project);
201  
202 bool clash = ProjectClashes(project);
203  
204 foreach (ConfigurationNode conf in project.Configurations)
205 {
206 string outpath = ProjectOutput(project, conf);
207 string filesToClean = outpath;
208  
209 if (clash)
210 {
211 f.WriteLine("{0}-{1}: .{0}-{1}-timestamp", project.Name, conf.Name);
212 f.WriteLine();
213 f.Write(".{0}-{1}-timestamp: $(DEPENDENCIES_{0})", project.Name, conf.Name);
214 }
215 else
216 {
217 f.WriteLine("{0}-{1}: {2}", project.Name, conf.Name, outpath);
218 f.WriteLine();
219 f.Write("{2}: $(DEPENDENCIES_{0})", project.Name, conf.Name, outpath);
220 }
221 // Dependencies on other projects.
222 foreach (ReferenceNode refr in project.References)
223 if (solution.ProjectsTable.Contains(refr.Name))
224 {
225 ProjectNode refProj = (ProjectNode)solution.ProjectsTable[refr.Name];
226 if (ProjectClashes(refProj))
227 f.Write(" .{0}-{1}-timestamp", refProj.Name, conf.Name);
228 else
229 f.Write(" {0}", ProjectOutput(refProj, conf));
230 }
231 f.WriteLine();
232  
233 // make directory for output.
234 if (Path.GetDirectoryName(outpath) != "")
235 {
236 f.WriteLine("\tmkdir -p {0}", Path.GetDirectoryName(outpath));
237 }
238 // mcs command line.
239 f.Write("\tgmcs", project.Name);
240 f.Write(" -warn:{0}", conf.Options["WarningLevel"]);
241 if ((bool)conf.Options["DebugInformation"])
242 f.Write(" -debug");
243 if ((bool)conf.Options["AllowUnsafe"])
244 f.Write(" -unsafe");
245 if ((bool)conf.Options["CheckUnderflowOverflow"])
246 f.Write(" -checked");
247 if (project.StartupObject != "")
248 f.Write(" -main:{0}", project.StartupObject);
249 if ((string)conf.Options["CompilerDefines"] != "")
250 {
251 f.Write(" -define:\"{0}\"", conf.Options["CompilerDefines"]);
252 }
253  
254 f.Write(" -target:{0} -out:{1}", ProjectTypeToTarget(project.Type), outpath);
255  
256 // Build references to other projects. Now that sux.
257 // We have to reference the other project in the same conf.
258 foreach (ReferenceNode refr in project.References)
259 if (solution.ProjectsTable.Contains(refr.Name))
260 {
261 ProjectNode refProj;
262 refProj = (ProjectNode)solution.ProjectsTable[refr.Name];
263 f.Write(" -r:{0}", ProjectOutput(refProj, conf));
264 }
265  
266 f.Write(" $(REFERENCES_{0})", project.Name);
267 f.Write(" $(RESOURCES_{0})", project.Name);
268 f.Write(" $(SOURCES_{0})", project.Name);
269 f.WriteLine();
270  
271 // Copy references with localcopy.
272 foreach (ReferenceNode refr in project.References)
273 if (refr.LocalCopy)
274 {
275 string outPath, srcPath, destPath;
276 outPath = Helper.NormalizePath((string)conf.Options["OutputPath"]);
277 if (solution.ProjectsTable.Contains(refr.Name))
278 {
279 ProjectNode refProj;
280 refProj = (ProjectNode)solution.ProjectsTable[refr.Name];
281 srcPath = ProjectOutput(refProj, conf);
282 destPath = Path.Combine(outPath, Path.GetFileName(srcPath));
283 destPath = NicePath(project, destPath);
284 if (srcPath != destPath)
285 {
286 f.WriteLine("\tcp -f {0} {1}", srcPath, destPath);
287 filesToClean += " " + destPath;
288 }
289 continue;
290 }
291 srcPath = FindFileReference(refr.Name, project);
292 if (srcPath != null)
293 {
294 destPath = Path.Combine(outPath, Path.GetFileName(srcPath));
295 destPath = NicePath(project, destPath);
296 f.WriteLine("\tcp -f {0} {1}", srcPath, destPath);
297 filesToClean += " " + destPath;
298 }
299 }
300  
301 if (clash)
302 {
303 filesToClean += String.Format(" .{0}-{1}-timestamp", project.Name, conf.Name);
304 f.WriteLine("\ttouch .{0}-{1}-timestamp", project.Name, conf.Name);
305 f.Write("\trm -rf");
306 foreach (ConfigurationNode otherConf in project.Configurations)
307 if (otherConf != conf)
308 f.WriteLine(" .{0}-{1}-timestamp", project.Name, otherConf.Name);
309 f.WriteLine();
310 }
311 f.WriteLine();
312 f.WriteLine("{0}-{1}-clean:", project.Name, conf.Name);
313 f.WriteLine("\trm -rf {0}", filesToClean);
314 f.WriteLine();
315 }
316 }
317  
318 private void WriteIntro(StreamWriter f, SolutionNode solution)
319 {
320 f.WriteLine("# Makefile for {0} generated by Prebuild ( http://dnpb.sf.net )", solution.Name);
321 f.WriteLine("# Do not edit.");
322 f.WriteLine("#");
323  
324 f.Write("# Configurations:");
325 foreach (ConfigurationNode conf in solution.Configurations)
326 f.Write(" {0}", conf.Name);
327 f.WriteLine();
328  
329 f.WriteLine("# Projects:");
330 foreach (ProjectNode proj in solution.Projects)
331 f.WriteLine("#\t{0}", proj.Name);
332  
333 f.WriteLine("#");
334 f.WriteLine("# Building:");
335 f.WriteLine("#\t\"make\" to build everything under the default(first) configuration");
336 f.WriteLine("#\t\"make CONF\" to build every project under configuration CONF");
337 f.WriteLine("#\t\"make PROJ\" to build project PROJ under the default(first) configuration");
338 f.WriteLine("#\t\"make PROJ-CONF\" to build project PROJ under configuration CONF");
339 f.WriteLine("#");
340 f.WriteLine("# Cleaning (removing results of build):");
341 f.WriteLine("#\t\"make clean\" to clean everything, that's what you probably want");
342 f.WriteLine("#\t\"make CONF\" to clean everything for a configuration");
343 f.WriteLine("#\t\"make PROJ\" to clean everything for a project");
344 f.WriteLine("#\t\"make PROJ-CONF\" to clea project PROJ under configuration CONF");
345 f.WriteLine();
346 }
347  
348 private void WritePhony(StreamWriter f, SolutionNode solution)
349 {
350 string defconf = "";
351 foreach (ConfigurationNode conf in solution.Configurations)
352 {
353 defconf = conf.Name;
354 break;
355 }
356  
357 f.Write(".PHONY: all");
358 foreach (ProjectNode proj in solution.Projects)
359 f.Write(" {0} {0}-clean", proj.Name);
360 foreach (ConfigurationNode conf in solution.Configurations)
361 f.Write(" {0} {0}-clean", conf.Name);
362 foreach (ProjectNode proj in solution.Projects)
363 foreach (ConfigurationNode conf in solution.Configurations)
364 f.Write(" {0}-{1} {0}-{1}-clean", proj.Name, conf.Name);
365 f.WriteLine();
366 f.WriteLine();
367  
368 f.WriteLine("all: {0}", defconf);
369 f.WriteLine();
370  
371 f.Write("clean:");
372 foreach (ConfigurationNode conf in solution.Configurations)
373 f.Write(" {0}-clean", conf.Name);
374 f.WriteLine();
375 f.WriteLine();
376  
377 foreach (ConfigurationNode conf in solution.Configurations)
378 {
379 f.Write("{0}: ", conf.Name);
380 foreach (ProjectNode proj in solution.Projects)
381 f.Write(" {0}-{1}", proj.Name, conf.Name);
382 f.WriteLine();
383 f.WriteLine();
384  
385 f.Write("{0}-clean: ", conf.Name);
386 foreach (ProjectNode proj in solution.Projects)
387 f.Write(" {0}-{1}-clean", proj.Name, conf.Name);
388 f.WriteLine();
389 f.WriteLine();
390 }
391  
392 foreach (ProjectNode proj in solution.Projects)
393 {
394 f.WriteLine("{0}: {0}-{1}", proj.Name, defconf);
395 f.WriteLine();
396  
397 f.Write("{0}-clean:", proj.Name);
398 foreach (ConfigurationNode conf in proj.Configurations)
399 f.Write(" {0}-{1}-clean", proj.Name, conf.Name);
400 f.WriteLine();
401 f.WriteLine();
402 }
403 }
404  
405 private void WriteSolution(SolutionNode solution)
406 {
407 m_Kernel.Log.Write("Creating makefile for {0}", solution.Name);
408 m_Kernel.CurrentWorkingDirectory.Push();
409  
410 string file = "Makefile";// Helper.MakeFilePath(solution.FullPath, solution.Name, "make");
411 StreamWriter f = new StreamWriter(file);
412  
413 Helper.SetCurrentDir(Path.GetDirectoryName(file));
414  
415 using (f)
416 {
417 WriteIntro(f, solution);
418 WritePhony(f, solution);
419  
420 foreach (ProjectNode project in solution.Projects)
421 {
422 m_Kernel.Log.Write("...Creating Project: {0}", project.Name);
423 WriteProject(f, solution, project);
424 }
425 }
426  
427 m_Kernel.Log.Write("");
428 m_Kernel.CurrentWorkingDirectory.Pop();
429 }
430  
431 private void CleanSolution(SolutionNode solution)
432 {
433 m_Kernel.Log.Write("Cleaning makefile for {0}", solution.Name);
434  
435 string file = Helper.MakeFilePath(solution.FullPath, solution.Name, "make");
436 Helper.DeleteIfExists(file);
437  
438 m_Kernel.Log.Write("");
439 }
440  
441 #endregion
442  
443 #region ITarget Members
444  
445 public void Write(Kernel kern)
446 {
447 m_Kernel = kern;
448 foreach (SolutionNode solution in kern.Solutions)
449 WriteSolution(solution);
450 m_Kernel = null;
451 }
452  
453 public virtual void Clean(Kernel kern)
454 {
455 m_Kernel = kern;
456 foreach (SolutionNode sol in kern.Solutions)
457 CleanSolution(sol);
458 m_Kernel = null;
459 }
460  
461 public string Name
462 {
463 get
464 {
465 return "makefile";
466 }
467 }
468  
469 #endregion
470 }
471 }