Fix #6582 - make builder work with buggy pkg-config
[roobuilder] / src / Project / Project.vala
1 //<Script type="text/javascript">
2
3 /**
4  * Project Object
5  * 
6  * Projects can only contain one directory... - it can import other projects..(later)
7  * 
8  * we need to sort out that - paths is currently a key/value array..
9  * 
10  * 
11  * 
12  */
13 namespace Project {
14          public errordomain Error {
15                 INVALID_TYPE,
16                 NEED_IMPLEMENTING,
17                 MISSING_FILE,
18                 INVALID_VALUE,
19                 INVALID_FORMAT
20         }
21
22         // static array of all projects.
23         public Gee.HashMap<string,Project>  projects;
24         
25         
26         
27         public bool  projects_loaded = false;
28
29         
30         
31         public class Project : Object {
32                 
33                 public signal void on_changed (); 
34         
35                 public string id;
36                 public string fn = ""; // just a md5...
37                 public string name = "";
38                 public string runhtml = "";
39                 public string base_template = "";
40                 public string rootURL = "";
41                 public string html_gen = "";
42                 
43                 public Gee.HashMap<string,string> paths;
44                 public Gee.HashMap<string,JsRender.JsRender> files ;
45                 //tree : false,
46                 public  string xtype;
47                 
48                 public Json.Object json_project_data;
49                 public Palete.RooDatabase roo_database;
50                 public Palete.Palete palete;
51                  
52                 bool is_scanned; 
53            
54                 
55                 public Project (string path) {
56                         
57                         this.name = GLib.Path.get_basename(path); // default..
58                         this.json_project_data = new Json.Object();
59                         
60                         this.is_scanned = false;
61                         this.paths = new Gee.HashMap<string,string>();
62                         this.files = new Gee.HashMap<string,JsRender.JsRender>();
63                         //XObject.extend(this, cfg);
64                         //this.files = { }; 
65                         if (path.length > 0) {
66                                 this.paths.set(path, "dir");
67                         }
68                         // dummy roo database...
69                         this.initRooDatabase();
70                         
71                         
72                         
73                 }
74                 public void  initRooDatabase()
75                 {
76                          
77                         this.roo_database = new Palete.RooDatabase.from_project(this);
78                 }
79                 
80                 
81                 
82                 public static void loadAll(bool force = false)
83                 {
84                         if (projects_loaded && !force) {
85                                 return;
86                         }
87
88                         var dirname = GLib.Environment.get_home_dir() + "/.Builder";
89                         var dir = File.new_for_path(dirname);
90                                 if (!dir.query_exists()) {
91                                 dir.make_directory();
92                                 return;
93                         }
94                         projects = new  Gee.HashMap<string,Project>();
95                           
96                    
97                         try {
98                                 var file_enum = dir.enumerate_children(
99                                                                 GLib.FileAttribute.STANDARD_DISPLAY_NAME, 
100                                         GLib.FileQueryInfoFlags.NONE, 
101                                         null
102                                 );
103                                 
104                                  
105                                 FileInfo next_file; 
106                                 while ((next_file = file_enum.next_file(null)) != null) {
107                                                 var fn = next_file.get_display_name();
108                                         if (!Regex.match_simple("\\.json$", fn)) {
109                                                 continue;
110                                         }
111                                         factoryFromFile(dirname + "/" + fn);
112                                 }       
113                         } catch(Error e) {
114                                 GLib.warning("oops - something went wrong scanning the projects\n");
115                         }
116                         
117
118                 }
119
120                 public static Gee.ArrayList<Project> allProjectsByName()
121                 {
122                         var ret = new Gee.ArrayList<Project>();
123                         var iter = projects.map_iterator();
124                                 while (iter.next()) {
125                                         ret.add(iter.get_value());
126                                 }
127                         // fixme -- sort...
128                         return ret;
129                 
130                 }
131                 
132                 public static Project getProject(string name)
133                 {
134                         
135                         var iter = projects.map_iterator();
136                         while (iter.next()) {
137                                 if (iter.get_value().name == name) {
138                                         return iter.get_value();
139                                 }
140                                 
141                         }
142                         
143                         return null;
144                 
145                 }
146                 
147                 public static string listAllToString()
148                 {
149                         var all = new Gee.ArrayList<Project>();
150
151                         var fiter = projects.map_iterator();
152                         
153                         while(fiter.next()) {
154                                 all.add(fiter.get_value());
155                         }
156                         
157                         all.sort((fa,fb) => {
158                                 return ((Project)fa).name.collate(((Project)fb).name);
159
160                         });
161
162                         var iter = all.list_iterator();
163                         var ret = "ID\tName\tDirectory\n";
164                         while (iter.next()) {
165                                 ret += "%s\t%s\t%s\n".printf(
166                                                 iter.get().fn,
167                                                 iter.get().name,
168                                                 iter.get().firstPath()
169                                                 );
170                          
171                                 
172                         }
173                         
174                         return ret;
175                 
176                 }
177                 
178                 
179                 
180                 public static Project getProjectByHash(string fn)
181                 {
182                         
183                         var iter = projects.map_iterator();
184                         while (iter.next()) {
185                                 if (iter.get_value().fn == fn) {
186                                         return iter.get_value();
187                                 }
188                                 
189                         }
190                         
191                         return null;
192                 
193                 }
194                 
195                 // load project data from project file.
196                 public static void   factoryFromFile(string jsonfile)
197                 {
198                          
199                         GLib.debug("parse %s", jsonfile);
200
201                         var pa = new Json.Parser();
202                         pa.load_from_file(jsonfile);
203                         var node = pa.get_root();
204
205                         
206                         if (node == null || node.get_node_type () != Json.NodeType.OBJECT) {
207                                 GLib.debug("SKIP " + jsonfile + " - invalid format?");
208                                 return;
209                         }
210                         
211                         var obj = node.get_object ();
212                         var xtype =  obj.get_string_member("xtype");
213
214
215                         var paths = obj.get_object_member("paths");
216                         var i = 0;
217                         var fpath = "";
218                         paths.foreach_member((sobj, key, val) => {
219                                 if (i ==0 ) {
220                                         fpath = key;
221                                 }
222                                         
223                         });
224                         Project proj;
225                         try {
226                                 proj = factory(xtype, fpath);
227                         } catch (Error e)  {
228                                 GLib.debug("Skip file - invalid file type");
229                                 return;
230                         }
231
232                         proj.json_project_data  = obj; // store the original object...
233                         
234                         proj.fn =  Path.get_basename(jsonfile).split(".")[0];
235
236                         // might not exist?
237
238                         if (obj.has_member("runhtml")) {
239                                         proj.runhtml  = obj.get_string_member("runhtml"); 
240                         }
241                         // might not exist?
242                         if (obj.has_member("base_template")) {
243                                         proj.base_template  = obj.get_string_member("base_template"); 
244                         }
245                         // might not exist?
246                         if (obj.has_member("rootURL")) {
247                                         proj.rootURL  = obj.get_string_member("rootURL"); 
248                         }
249                         
250                         if (obj.has_member("html_gen")) {
251                                         proj.html_gen  = obj.get_string_member("html_gen"); 
252                         }
253                         
254                         proj.name = obj.get_string_member("name");
255
256                          
257                         paths.foreach_member((sobj, key, val) => {
258                                 proj.paths.set(key, "dir");
259                         });
260                         proj.initRooDatabase();
261                         
262                         GLib.debug("Add Project %s", proj.id);
263                         
264                         projects.set(proj.id,proj);
265                         
266                         
267                         
268                         
269                 }
270                 
271                 
272                 public static Project factory(string xtype, string path) throws Error
273                 {
274
275                         // check to see if it's already loaded..
276
277                          
278                         var iter = projects.map_iterator();
279                         while (iter.next()) {
280                                 if (iter.get_value().hasPath( path)) {
281                                         return iter.get_value();
282                                  }
283                         }
284
285
286                         switch(xtype) {
287                                 case "Gtk":
288                                         return new Gtk(path);
289                                 case "Roo":
290                                         return new Roo(path);
291                                 //case "Flutter":
292                                 //      return new Flutter(path);
293                         }
294                         throw new Error.INVALID_TYPE("invalid project type");
295                                 
296                 }
297                 
298                 
299                 
300                  public static void  remove(Project project)
301                 {
302                         // delete the file..
303                         var dirname = GLib.Environment.get_home_dir() + "/.Builder";
304                                  
305                         FileUtils.unlink(dirname + "/" + project.fn + ".json");
306                         projects.unset(project.id,null);
307                         
308
309                 }
310                  
311
312                 public void save()
313                 {
314                                 // fixme..
315                         
316                         if (this.fn.length < 1) {
317                                 // make the filename..
318                                 //var t = new DateTime.now_local ();
319                                 //TimeVal tv;
320                                 //t.to_timeval(out tv);
321                                 //var str = "%l:%l".printf(tv.tv_sec,tv.tv_usec);
322                                 var str = this.firstPath();
323                                 
324                                         this.fn = GLib.Checksum.compute_for_string(GLib.ChecksumType.MD5, str, str.length);
325                         }
326
327                         var dirname = GLib.Environment.get_home_dir() + "/.Builder";
328                         var  s =  this.toJSON(false);
329                         FileUtils.set_contents(dirname + "/" + this.fn + ".json", s, s.length);  
330                         
331                         
332                 }
333
334                 
335                 
336                 public string toJSON(bool show_all)
337                 {
338                         
339                         
340                         this.json_project_data.set_string_member("name", this.name);
341                         this.json_project_data.set_string_member("fn", this.fn);
342                         this.json_project_data.set_string_member("xtype", this.xtype);
343                         this.json_project_data.set_string_member("runhtml", this.runhtml);
344                         this.json_project_data.set_string_member("rootURL", this.rootURL);
345                         this.json_project_data.set_string_member("base_template", this.base_template);
346                         this.json_project_data.set_string_member("rootURL", this.rootURL);
347                         this.json_project_data.set_string_member("html_gen", this.html_gen);                    
348  
349                         var paths = new Json.Object(); 
350
351
352                         var iter = this.paths.map_iterator();
353                         while (iter.next()) {
354                                 paths.set_string_member(iter.get_key(), "path");
355                         }
356                         this.json_project_data.set_object_member("paths", paths);
357
358                         
359                         if (show_all) {
360                                 var files = new Json.Array();
361                                 
362                                 
363                                 var fiter = this.files.map_iterator();
364                                 while (fiter.next()) {
365                                         files.add_string_element (fiter.get_key());
366                                 }
367                                 this.json_project_data.set_array_member("files", files);
368                                 
369                         }
370
371                 
372                         var  generator = new Json.Generator ();
373                         var  root = new Json.Node(Json.NodeType.OBJECT);
374                         root.init_object(this.json_project_data);
375                         generator.set_root (root);
376                         if (show_all) {
377                                 generator.pretty = true;
378                                 generator.indent = 4;
379                         }
380
381                         return  generator.to_data (null);
382                           
383                           
384                 }
385                 public string firstPath()
386                 {
387                         var iter = this.paths.map_iterator();
388                         while (iter.next()) {
389                                 return iter.get_key();
390                         }
391                   
392                         return "";
393                 }
394
395                 public bool hasPath(string path)
396                 {
397                         var iter = this.paths.map_iterator();
398                         while (iter.next()) {
399                                 if (iter.get_key() == path) {
400                                 return true;
401                         }
402                         }
403                   
404                         return false;
405                 }
406
407                 
408                 // returns the first path
409                 public string getName()
410                 {
411                         var iter = this.paths.map_iterator();
412                         while (iter.next()) {
413                                 return GLib.Path.get_basename(iter.get_key());
414                         }
415                   
416                         return "";
417                 }
418
419                 public Gee.ArrayList<JsRender.JsRender> sortedFiles()
420                 {
421                         var files = new Gee.ArrayList<JsRender.JsRender>();
422
423                         var fiter = this.files.map_iterator();
424                         while(fiter.next()) {
425                                 files.add(fiter.get_value());
426                         }
427                         files.sort((fa,fb) => {
428                                 return ((JsRender.JsRender)fa).name.collate(((JsRender.JsRender)fb).name);
429
430                         });
431                         return files;
432
433                 }
434                 
435          
436          
437                 public string listAllFilesToString()
438                 {
439                         this.scanDirs();
440                         var iter = this.sortedFiles().list_iterator();
441                         var ret = "ID\tName\tDirectory\n";
442                         while (iter.next()) {
443                                 ret += "%s\n".printf(
444                                                  
445                                                 iter.get().name
446                                                  
447                                                 );
448                          
449                                 
450                         }
451                         
452                         return ret;
453                 
454                 }
455                 
456          
457          
458          
459                 public JsRender.JsRender? getByName(string name)
460                 {
461                         
462                         var fiter = files.map_iterator();
463                         while(fiter.next()) {
464                          
465                                 var f = fiter.get_value();
466                                 
467                                 
468                                 GLib.debug ("Project.getByName: %s ?= %s" ,f.name , name);
469                                 if (f.name == name) {
470                                         return f;
471                                 }
472                         };
473                         return null;
474                 }
475                 public JsRender.JsRender? getByPath(string path)
476                 {
477                         
478                         var fiter = files.map_iterator();
479                         while(fiter.next()) {
480                          
481                                 var f = fiter.get_value();
482                                 
483                                 
484                                 //GLib.debug ("Project.getByName: %s ?= %s" ,f.name , name);
485                                 if (f.path == path) {
486                                         return f;
487                                 }
488                         };
489                         return null;
490                 }
491                 
492                 public JsRender.JsRender? getById(string id)
493                 {
494                         
495                         var fiter = files.map_iterator();
496                         while(fiter.next()) {
497                          
498                                 var f = fiter.get_value();
499                                 
500                                 
501                                 //console.log(f.id + '?=' + id);
502                                 if (f.id == id) {
503                                         return f;
504                                 }
505                                 };
506                         return null;
507                 }
508
509                 public JsRender.JsRender newFile (string name)
510                 {
511                         var ret =  JsRender.JsRender.factory(this.xtype, 
512                                                                                          this, 
513                                                                                          this.firstPath() + "/" + name + ".bjs");
514                         this.addFile(ret);
515                         return ret;
516                 }
517                 
518                 public JsRender.JsRender loadFileOnly (string path)
519                 {
520                         var xt = this.xtype;
521                         return JsRender.JsRender.factory(xt, this, path);
522                         
523                 } 
524                 
525                 public JsRender.JsRender create(string filename)
526                 {
527                         var ret = this.loadFileOnly(filename);
528                         ret.save();
529                         this.addFile(ret);
530                         return ret;
531                         
532                 }
533                         
534                          
535                 public void addFile(JsRender.JsRender pfile) { // add a single file, and trigger changed.
536                 
537                 
538                         this.files.set(pfile.path, pfile); // duplicate check?
539                         this.on_changed();
540                 }
541                 
542                 public void add(string path, string type)
543                 {
544                         this.paths.set(path,type);
545                         //Seed.print(" type is '" + type + "'");
546                         if (type == "dir") {
547                                 this.scanDir(path);
548                         //    console.dump(this.files);
549                         }
550                         if (type == "file" ) {
551                         
552                                 this.files.set(path,this.loadFileOnly( path ));
553                         }
554                         this.on_changed();
555                         
556                 }
557                  
558                 public void  scanDirs() // cached version
559                 {
560                         if (this.is_scanned) {
561                                 return;
562                         }
563                         this.scanDirsForce();
564                         //console.dump(this.files);
565                         
566                 }
567                  
568                 public void  scanDirsForce()
569                 {
570                         this.is_scanned = true;  
571                         var iter = this.paths.map_iterator();
572                         while (iter.next()) {
573                                 //print("path: " + iter.get_key() + " : " + iter.get_value() +"\n");
574                                 if (iter.get_value() != "dir") {
575                                         continue;
576                                 }
577                                 this.scanDir(iter.get_key());
578                         }
579                         //console.dump(this.files);
580                         
581                 }
582                         // list files.
583                 public void scanDir(string dir, int dp =0 ) 
584                 {
585                         //dp = dp || 0;
586                         //print("Project.Base: Running scandir on " + dir +"\n");
587                         if (dp > 5) { // no more than 5 deep?
588                                 return;
589                         }
590                         // this should be done async -- but since we are getting the proto up ...
591                         var other_files = new Gee.ArrayList<string>();
592                         var bjs_files = new Gee.ArrayList<string>();
593                         
594                         var subs = new GLib.List<string>();;            
595                         var f = File.new_for_path(dir);
596                         try {
597                                 var file_enum = f.enumerate_children(GLib.FileAttribute.STANDARD_DISPLAY_NAME, GLib.FileQueryInfoFlags.NONE, null);
598                                 
599                                  
600                                 FileInfo next_file; 
601                                 while ((next_file = file_enum.next_file(null)) != null) {
602                                         var fn = next_file.get_display_name();
603                         
604                                          
605                                         //print("trying"  + dir + "/" + fn +"\n");
606                                         
607                                         if (fn[0] == '.') { // skip hidden
608                                                 continue;
609                                         }
610                                         
611                                         if (FileUtils.test(dir  + "/" + fn, GLib.FileTest.IS_DIR)) {
612                                                 subs.append(dir  + "/" + fn);
613                                                 continue;
614                                         }
615                                         
616                                         if (!Regex.match_simple("\\.bjs$", fn)) {
617                                                 other_files.add(fn);
618                                                 //print("no a bjs\n");
619                                                 continue;
620                                         }
621                                         bjs_files.add(fn.substring(0, fn.length-4));
622                                         
623                                         var xt = this.xtype;
624                                         var el = JsRender.JsRender.factory(xt,this, dir + "/" + fn);
625                                         this.files.set( dir + "/" + fn, el);
626                                         // parent ?? 
627                                         
628                                          
629                                 }
630                         } catch (Error e) {
631                                 GLib.warning("Project::scanDirs failed : " + e.message + "\n");
632                         } catch (GLib.Error e) {
633                                 GLib.warning("Project::scanDirs failed : " + e.message + "\n");
634                         }
635                         foreach(var fn in other_files) {
636                                 var dpos = fn.last_index_of(".");
637                                 var without_ext = fn.substring(0, dpos);
638                                 if (bjs_files.contains(without_ext)) {
639                                         continue;
640                                 }
641                                 GLib.debug("Could have added %s/%s", dir, fn);
642                                 //var el = JsRender.JsRender.factory("plain",this, dir + "/" + fn);
643                                 //this.files.set( dir + "/" + fn, el);
644                         }
645                         
646                         for (var i = 0; i < subs.length(); i++) {
647                                  this.scanDir(subs.nth_data(i), dp+1);
648                         }
649                         
650                 }
651                 // wrapper around the javascript data...
652                 public string get_string_member(string key) {
653                         
654                         if (!this.json_project_data.has_member(key)) {
655                                 return "";
656                         }
657                         var  ret = this.json_project_data.get_string_member(key);
658                         if (ret == null) {
659                                 return "";
660                         }
661                         return ret;
662                         
663                 }
664                   
665         }
666 }
667