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