fixes to new project - compile rules and handle notices from compiler
[roobuilder] / src / Palete / ValaSourceCompiler.vala
1
2 // valac TreeBuilder.vala --pkg libvala-0.24 --pkg posix -o /tmp/treebuilder
3
4 /**
5  *  this only deals with the compiling (when builder is run with different args..)
6  * 
7  */
8
9
10 namespace Palete {
11         
12          
13         public class ValaSourceReport  : Vala.Report {
14
15                 private Project.Project project;
16                 public string filepath = "";
17                 
18                 public string tmpname;
19                 
20                 //public Gee.ArrayList<ValaSourceNotice> notices;
21                 public Json.Object result;
22                  
23                 //public Gee.HashMap<int,string> line_errors;
24                 
25                 public void  compile_notice(string type, string filename, int line, string message) {
26                          
27  
28                          GLib.debug("%s %s %d %s", type, filename, line, message);
29                          
30                          if (!this.result.has_member(type+"-TOTAL")) {
31                                  this.result.set_int_member(type+"-TOTAL", 1);
32                          } else {
33                                 this.result.set_int_member(type+"-TOTAL", 
34                                         this.result.get_int_member(type+"-TOTAL") +1 
35                                 );
36                          }
37                          
38                          
39                          if (!this.result.has_member(type)) {
40                                  this.result.set_object_member(type, new Json.Object());
41                          }
42                          var t = this.result.get_object_member(type);
43                          if (!t.has_member(filename)) {
44                                  t.set_object_member(filename, new Json.Object());
45                          }
46                          var tt = t.get_object_member(filename);
47                          if (!tt.has_member(line.to_string())) {
48                                  tt.set_array_member(line.to_string(), new Json.Array());
49                          }
50                          var tl = tt.get_array_member(line.to_string());
51                          tl.add_string_element(message);
52                          
53                 }
54                 
55                 
56          
57                 public ValaSourceReport(string filepath, string tmpname, Project.Project project)
58                 {
59                         base();
60                         this.project = project;
61                         this.filepath = filepath;
62                         this.tmpname = tmpname;
63                         this.result = new Json.Object();
64                         this.result.set_boolean_member("success", true);
65                         this.result.set_string_member("message", "");
66                         
67                         
68                         
69                         //this.line_errors = new Gee.HashMap<int,string> ();
70                         //this.notices = new Gee.ArrayList<ValaSourceNotice>();
71                 }
72                  
73                 
74                 public override void note (Vala.SourceReference? source, string message) {
75                     if (source == null) {
76                                 return;
77                                 //stderr.printf ("My error: %s\n", message);
78                         }
79                         
80                         if (source.file.filename != this.tmpname) {
81                                 this.compile_notice("WARN", source.file.filename , source.begin.line, "Notice: " + message);
82                                 return;
83                         }
84                         this.compile_notice("WARN", this.filepath, source.begin.line, "Notice: " +  message);
85                 }
86                 
87                 public override void warn (Vala.SourceReference? source, string message) {
88                          
89                         if (source == null) {
90                                 return;
91                                 //stderr.printf ("My error: %s\n", message);
92                         }
93                         
94                         if (source.file.filename != this.tmpname) {
95                                 this.compile_notice("WARN", source.file.filename , source.begin.line, message);
96                                 return;
97                         }
98                         this.compile_notice("WARN", this.filepath, source.begin.line, message);
99                         
100                 }
101                 public override void depr (Vala.SourceReference? source, string message) {
102                          
103                         if (source == null) {
104                                 return;
105                                 //stderr.printf ("My error: %s\n", message);
106                         }
107                         
108                         if (source.file.filename != this.tmpname) {
109                                 this.compile_notice("DEPR", source.file.filename, source.begin.line, message);
110                                 return;
111                         }
112                         this.compile_notice("DEPR",  this.filepath, source.begin.line, message);
113                         
114                 }
115                 
116                 public override void err (Vala.SourceReference? source, string message) {
117                         errors++;
118                         if (source == null) {
119                                 return;
120                                 //stderr.printf ("My error: %s\n", message);
121                         }
122                         if (source.file.filename != this.tmpname) {
123                                 this.compile_notice("ERR", source.file.filename, source.begin.line, message);
124                                 GLib.debug ("Other file: Got error error: %d:  %s\n", source.begin.line, message);
125                                 return;
126                         }
127                          
128                          
129                         this.compile_notice("ERR", this.filepath, source.begin.line, message);
130                         GLib.debug ("Test file: Got error error: %d: %s\n", source.begin.line, message);
131                 }
132                 /*
133                 public void dump()
134                 {
135                         var iter = this.line_errors.map_iterator();
136                         while (iter.next()) {
137                                 print ("%d : %s\n\n", iter.get_key(), iter.get_value());
138                         }
139                 }
140                 */
141
142         }
143
144         public class ValaSourceCompiler : Object {
145
146                 public static void jerr(string str)
147                 {
148                         var ret = new Json.Object(); 
149                         ret.set_boolean_member("success", false);
150                         ret.set_string_member("message", str);
151                         
152                         var  generator = new Json.Generator ();
153                         var  root = new Json.Node(Json.NodeType.OBJECT);
154                         root.init_object(ret);
155                         generator.set_root (root);
156                          
157                         generator.pretty = true;
158                         generator.indent = 4;
159                  
160
161                         print("%s\n",  generator.to_data (null));
162                         GLib.Process.exit(Posix.EXIT_FAILURE);
163                         
164                 }
165
166                 public static void buildApplication()
167                 {
168                         //print("build based on Application settings\n");
169                         
170                         if (BuilderApplication.opt_compile_target == null) {
171                                 jerr("missing compile target --target");
172                         }
173                         
174                         Project.Project.loadAll();
175                         var proj = Project.Project.getProjectByPath(BuilderApplication.opt_compile_project);
176                         proj.load();
177                         
178                         if (proj == null) {
179                                 jerr("could not load test project %s".printf( BuilderApplication.opt_compile_project));
180                         }
181                         
182                         if (proj.xtype != "Gtk") {
183                                 jerr("%s is not a Gtk Project".printf( BuilderApplication.opt_compile_project));
184                         }
185                         var gproj = (Project.Gtk)proj;
186                         
187                         
188                         if (!gproj.compilegroups.has_key(BuilderApplication.opt_compile_target)) {
189                                 jerr("missing compile target %s".printf(BuilderApplication.opt_compile_target));
190                         }
191                         var skip_file = "";
192                         if (BuilderApplication.opt_compile_skip != null) {
193                                 skip_file = BuilderApplication.opt_compile_skip;
194                         }
195                         var add_file = "";
196                         if (BuilderApplication.opt_compile_add != null) {
197                                 add_file = BuilderApplication.opt_compile_add;
198                         }
199                         
200                         
201                         var vs = new ValaSourceCompiler(gproj,  add_file, BuilderApplication.opt_compile_target,   skip_file);
202                         if (BuilderApplication.opt_compile_output != null) {
203                                 vs.output = BuilderApplication.opt_compile_output;
204                         }
205                         vs.compile();
206                         
207                         
208                 }
209
210                 Vala.CodeContext context;
211                 ValaSourceReport report;
212                 Project.Gtk project;
213                 public string build_module;
214                 public string filepath = "";
215                 public string original_filepath;
216                 public int line_offset = 0;
217                 public string output;
218                 
219                 // file.project , file.path, file.build_module, ""
220                 public ValaSourceCompiler(Project.Gtk project, string filepath, string build_module, string original_filepath) {
221                         base();
222                         //this.file = file;
223                         this.filepath = filepath;
224                         this.build_module = build_module;
225                         this.original_filepath = original_filepath;
226                         this.project =  project;
227                         this.output = "";
228                         
229                 }
230                 public void dumpCode(string str) 
231                 {
232                         var ls = str.split("\n");
233                         for (var i=0;i < ls.length; i++) {
234                                 print("%d : %s\n", i+1, ls[i]);
235                         }
236                 }
237                 
238                  
239                 
240                 public void compile( )
241                 {
242                         // init context:
243                         var valac = "valac " ;
244                         
245                         context = new Vala.CodeContext ();
246                         Vala.CodeContext.push (context);
247                 
248                         context.experimental = false;
249                         context.experimental_non_null = false;
250 #if VALA_0_56
251                         var ver=56;
252
253 #elif VALA_0_36
254                         var ver=36;
255  
256 #endif
257                         // this is automatic now.. no need to do it manually apparently..
258                         //for (int i = 2; i <= ver; i += 2) {
259                         //      context.add_define ("VALA_0_%d".printf (i));
260                         //}
261                         
262                         
263                         
264                          
265                         var vapidirs = this.project.vapidirs();
266                         // order is important ...
267                          vapidirs +=  Path.get_dirname (context.get_vapi_path("gee-0.8")) ; //usr/share/vala/vapi 
268                          vapidirs +=  Path.get_dirname (context.get_vapi_path("glib-2.0")) ; // usr/share/vala-XXX/vapi
269                                                         
270                         for(var i =0 ; i < vapidirs.length; i++) {
271                                 GLib.debug("Add Vapidir = %s" , vapidirs[i]);
272                         
273                                 valac += " --vapidir=" + vapidirs[i];
274                         }
275                                 
276                         
277                         context.vapi_directories = vapidirs;
278                         context.report.enable_warnings = true;
279                         context.metadata_directories = { };
280                         context.gir_directories = {};
281                         context.save_temps = true; // keep c sources = is it faster?
282                         //context.thread = true;
283                         valac += " --thread ";
284                         
285                         // we should parse the compilegroup to find out the flags..
286                         context.debug = true;
287                         valac += " -g ";
288                         
289                         this.report = new ValaSourceReport(this.original_filepath, this.filepath, this.project);
290                         context.report = this.report;
291                         
292                         valac += " -b  " + this.project.path; //."GLib.Environment.get_home_dir() + " ";
293                         context.basedir = this.project.path; // GLib.Environment.get_home_dir(); //Posix.realpath (".");
294                 
295                         this.project.makeProjectSubdir("build");
296                         context.directory = this.project.path + "/build"; //null; //??? causes target to end up in the right place at present..
297                 
298
299                         // add default packages:
300                         //if (settings.profile == "gobject-2.0" || settings.profile == "gobject" || settings.profile == null) {
301 #if VALA_0_56
302                         context.set_target_profile (Vala.Profile.GOBJECT);
303 #elif VALA_0_36
304                         context.profile = Vala.Profile.GOBJECT;
305 #endif
306                         var ns_ref = new Vala.UsingDirective (new Vala.UnresolvedSymbol (null, "GLib", null));
307                         context.root.add_using_directive (ns_ref);
308                         if (this.filepath != "") { 
309                                 var source_file = new Vala.SourceFile (
310                                                 context, 
311                                                 Vala.SourceFileType.SOURCE, 
312                                                 this.filepath 
313                                         );
314                                 source_file.add_using_directive (ns_ref);
315                                 context.add_source_file (source_file);
316                         }       
317                 // add all the files (except the current one) - this.file.path
318                 var pr = this.project;
319
320                 
321                 if (this.build_module.length > 0) {
322                                 var cg =  pr.compilegroups.get(this.build_module);
323                                 if (this.output.length < 1) {
324                                         this.output =  cg.name;
325                                 }
326                                 
327
328                                 for (var i = 0; i < cg.sources.size; i++) {
329                                         var path = cg.sources.get(i);
330                                         GLib.debug("Try add source file %s", path);
331                                         // flip bjs to vala
332                                         if (path.has_suffix(".bjs")) {
333                                                 path  = path.splice(path.length -4, path.length, ".vala");
334                                                 GLib.debug("Change source file %s", path);
335                                         }
336                                         if (!path.has_suffix(".vala") && path.has_suffix(".c") ) {
337                                                 continue;
338                                         }
339                                         if (!FileUtils.test(pr.path + "/" + path, FileTest.EXISTS)) {
340                                                 continue;
341                                         }       
342                         // skip thie original
343                                         if (pr.path + "/" + path == this.original_filepath) {
344                                                 GLib.debug("Add orig source file %s", path);
345                                                 valac += " " + path;
346                                                 continue;
347                                         }
348                                         if (FileUtils.test(pr.path + "/" + path, FileTest.IS_DIR)) {
349                                                 continue;
350                                         }
351                                         GLib.debug("Add source file %s", path);
352                                         
353                                         valac += " " + pr.path + "/" + path;
354                                         
355                                         if ( path.has_suffix(".c")) {
356                                                 context.add_c_source_file(path);
357                                                 continue;
358                                         }
359                                         
360                                         
361                                         var xsf = new Vala.SourceFile (
362                                                 context,
363                                                 Vala.SourceFileType.SOURCE, 
364                                                 pr.path + "/" +  path
365                                         );
366                                         xsf.add_using_directive (ns_ref);
367                                         context.add_source_file(xsf);
368                                         
369                                 }
370                         }
371                         
372                         // print("%s\n", valac); -
373                         // default.. packages..
374                         context.add_external_package ("glib-2.0"); 
375                         context.add_external_package ("gobject-2.0");
376                         // user defined ones..
377                         
378
379                 for (var i = 0; i < pr.packages.size; i++) {
380                         
381                         var pkg = pr.packages.get(i);
382                         // do not add libvala versions except the one that matches the one we are compiled against..
383                         if (Regex.match_simple("^libvala", pkg) && pkg != ("libvala-0." + ver.to_string())) {
384                         GLib.debug("Skip libvala Package: %s" , pkg);
385                                 continue;
386                         }
387                         GLib.debug("Add Package: %s" ,pkg);
388                                 valac += " --pkg " + pr.packages.get(i);
389                                 if (!this.has_vapi(context.vapi_directories, pkg)) {
390                                         GLib.debug("Skip vapi '%s' - does not exist", pkg);
391                                         continue;
392                                 }
393                                 
394                                 context.add_external_package(pkg);
395                         }
396                 
397                          //Vala.Config.PACKAGE_SUFFIX.substring (1)
398                         
399                         // add the modules...
400                         
401                         context.output = this.output == "" ? "/tmp/testrun" : this.output;
402                         valac += " -o " + context.output;
403                         GLib.debug("%s", valac);
404 #if VALA_0_56
405                         context.set_target_glib_version("2.32");
406 #elif VALA_0_36                 
407                         context.target_glib_major = 2;
408                         context.target_glib_minor = 32;
409 #endif
410                         valac += " --target-glib=2.32 ";
411                 
412                         //add_documented_files (context, settings.source_files);
413                 
414                         Vala.Parser parser = new Vala.Parser ();
415                         parser.parse (context);
416                         //gir_parser.parse (context);
417                         if (context.report.get_errors () > 0) {
418                                 Vala.CodeContext.pop ();
419                                 GLib.debug("parse got errors");
420                                 //((ValaSourceReport)context.report).dump();
421                                 this.report.result.set_boolean_member("success", false);
422                                 this.report.result.set_string_member("message", "Parse failed");
423                                 
424                                 this.outputResult();
425                                 return;
426                         }
427
428
429                         
430                         // check context:
431                         context.check ();
432                         if (context.report.get_errors () > 0) {
433                                 Vala.CodeContext.pop ();
434                                 GLib.debug("check got errors");
435                                 //((ValaSourceReport)context.report).dump();
436                                 this.report.result.set_boolean_member("success", false);
437                                 this.report.result.set_string_member("message", "Check failed");
438                                 
439                                 this.outputResult();
440                                 return;
441                         }
442                         
443                         if (this.output == "") {
444                                 Vala.CodeContext.pop ();
445                                 this.outputResult();
446                                 return;
447                         }
448                         
449 // none of this works on vala-40 as the API is not publicly visible
450                         
451
452                         GLib.debug("calling emit");
453                         context.codegen = new Vala.GDBusServerModule ();
454                          
455                         
456                         context.codegen.emit (context);
457                         
458                         if (BuilderApplication.opt_skip_linking) {
459                                 GLib.debug("skip linking is set = outputing result");
460                                 Vala.CodeContext.pop ();
461                                 this.outputResult();
462                                 GLib.Process.exit(Posix.EXIT_SUCCESS);
463                                  
464                         }
465                         
466                         /* --- - only if we are actually doing a full build.- no added benifet for inline complier
467                         on my laptop a 5s upto here.. then 40+s doing this.. - no additional warnings really (although if we are using 'C' code it maight be usefull
468                         */
469                         
470                         GLib.debug("this.filepath = %s" , this.filepath);
471                         
472                         if (this.filepath == "") { 
473                                 GLib.debug("calling ccompiler");
474                                 var ccompiler = new Vala.CCodeCompiler ();
475
476                                 
477                                 
478                                 var cc_command = Environment.get_variable ("CC");
479                                 
480                                 string [] cc_options = { "-lm" ,  "-pg"};
481                                 // ccache - would be nice, but we use multiple input files - which causes problems.
482                                 // would have to modify ccompile a bit, to handle this..
483                                 /*.
484                                 if (FileUtils.test("/usr/bin/ccache", FileTest.EXISTS)) {
485                                         GLib.debug("Using ccache");
486                                         cc_command = "/usr/bin/ccache " + (cc_command == null  ? "cc" : cc_command) ;
487                                 } else {
488                                         GLib.debug("Try installing ccache to speed things up");
489
490                                 }
491                                 */
492                                  
493                                 valac += " -X -lm -X -pg";
494                                 context.verbose_mode = true;
495         #if VALA_0_56
496                                 ccompiler.compile (context, cc_command, cc_options);                    
497         #elif VALA_0_36
498                                 var pkg_config_command = Environment.get_variable ("PKG_CONFIG");
499                                 ccompiler.compile (context, cc_command, cc_options, pkg_config_command);
500                                 // newer ones got rid fo pkg config command? not sure why.
501                                 
502         #endif
503                         }
504                         
505                         
506                         
507                         //if (this.filepath != "") {
508                         //      GLib.FileUtils.unlink(this.filepath);
509                         //}
510                         //print("%s\n", valac);
511                         Vala.CodeContext.pop ();
512         
513                         
514                         this.outputResult();
515                         GLib.Process.exit(Posix.EXIT_SUCCESS);
516                 
517                 }
518                 public bool has_vapi(string[] dirs,  string vapi) 
519                 {
520                         for(var i =0 ; i < dirs.length; i++) {
521                                 GLib.debug("check VAPI - %s", dirs[i] + "/" + vapi + ".vapi");
522                                 if (!FileUtils.test( dirs[i] + "/" + vapi + ".vapi", FileTest.EXISTS)) {
523                                         continue;
524                                 }   
525                                 return true;
526                         }
527                         return false;
528                         
529                 }
530                 
531                 public void outputResult()
532                 {
533                         var generator = new Json.Generator ();
534                     generator.indent = 1;
535                     generator.pretty = true;
536                     var node = new Json.Node(Json.NodeType.OBJECT);
537                     node.set_object(this.report.result);
538                     
539                     generator.set_root(node);
540                          
541                         generator.pretty = true;
542                         generator.indent = 4;
543                  
544
545                         print("%s\n",  generator.to_data (null));
546                         GLib.Process.exit(Posix.EXIT_SUCCESS);
547                         
548                          
549                 }
550  
551         }
552 }
553 /*
554 int main (string[] args) {
555
556         var a = new ValaSource(file);
557         a.create_valac_tree();
558         return 0;
559 }
560 */
561
562