Fix #7309 - editing a file should use the best compile group, not the first found...
[roobuilder] / src / Project / Gtk.vala
1 //<Script type="text/javascript">
2 /**
3  * Gtk projects - normally vala based now..
4  * 
5  * should have a few extra features..
6  * 
7  * like:
8  *   compile flags etc..
9  *   different versions (eg. different files can compile different versions - eg. for testing.
10  *   
11  * If we model this like adjuta - then we would need a 'project' file that is actually in 
12  * the directory somewhere... - and is revision controlled etc..
13  * 
14  * builder.config ??
15  * 
16  * 
17  * 
18  * 
19  */
20  
21
22 namespace Project 
23 {
24         static int gtk_id = 1;
25  
26
27         public class Gtk : Project
28         {
29                 /**
30                 * Gir cache - it's local as we might want clear it if we modify the packages...
31                 *
32                 */
33                 public Gee.HashMap<string,Palete.Gir> gir_cache = null;
34                 public bool gir_cache_loaded = false;
35  
36           
37                 public Gtk(string path) {
38                   
39                   
40                         base(path);
41                         this.palete = new Palete.Gtk(this);
42                         
43                         this.gir_cache = new Gee.HashMap<string,Palete.Gir>();
44                         this.xtype = "Gtk";
45                         var gid = "project-gtk-%d".printf(gtk_id++);
46                         this.id = gid;
47                         try {
48                                 this.loadConfig();
49                         } catch (GLib.Error e )  {
50                                 // is tihs ok?
51                         }
52                 
53                 }
54                 public Gee.HashMap<string,GtkValaSettings> compilegroups;
55                 
56                 public void loadConfig() throws GLib.Error 
57                 {
58                         // load a builder.config JSON file.
59                         // 
60                         this.compilegroups = new  Gee.HashMap<string,GtkValaSettings>();
61                         
62                         
63                         var fn = this.firstPath() + "/config1.builder";
64                         GLib.debug("load: " + fn );
65                         
66                         if (!FileUtils.test(fn, FileTest.EXISTS)) {
67                                 this.compilegroups.set("_default_", new GtkValaSettings("_default_") );
68                                 return;
69                         }
70
71                         var pa = new Json.Parser();
72                         pa.load_from_file(fn);
73                         var node = pa.get_root();
74
75                         // should be an array really.
76                         if (node.get_node_type () != Json.NodeType.ARRAY) {
77                                 throw new Error.INVALID_FORMAT ("Unexpected element type %s", node.type_name ());
78                         }
79                         
80                         var obj = node.get_array ();
81                         for(var i= 0;i<obj.get_length();i++) {
82                                 var el = obj.get_object_element(i);
83                                 var vs = new GtkValaSettings.from_json(el);
84                 if (vs == null) {
85                     print("problem loading json file");
86                     continue;
87                 }
88                                 if (vs.name != "_default_") {
89                                         vs.parent = this.compilegroups.get("_default_");
90                                 }
91                                 this.compilegroups.set(vs.name,vs);
92                         }
93                         GLib.debug("%s\n",this.configToString ());
94                         
95                 }
96                 public string configToString()
97                 {
98                         var ar = new Json.Array();
99                         var iter = this.compilegroups.map_iterator();
100                         while(iter.next()) {
101                                  
102                                 ar.add_object_element(iter.get_value().toJson());
103                         }
104
105                         var generator = new Json.Generator ();
106                         generator.indent = 4;
107                         generator.pretty = true;
108                         var node = new Json.Node(Json.NodeType.ARRAY);
109                         node.set_array(ar);
110                         generator.set_root(node);
111                         return generator.to_data(null);
112                 }
113                 
114                 public void writeConfig()
115                 {
116                         var fn = this.firstPath() + "/config1.builder";
117                         GLib.debug("write: " + fn );
118
119                          
120                         var f = GLib.File.new_for_path(fn);
121                         var data_out = new GLib.DataOutputStream(
122                                         f.replace(null, false, GLib.FileCreateFlags.NONE, null)
123                         );
124                         data_out.put_string(this.configToString(), null);
125                         data_out.close(null);
126                         this.gir_cache_loaded = false; // force a reload.
127                         return ;
128                          
129                 }
130                 /**
131                  *  perhaps we should select the default in the window somewhere...
132                  */ 
133                 public string firstBuildModule()
134                 {
135                         var iter = this.compilegroups.map_iterator();
136                         while(iter.next()) {
137                                  
138                                  if (iter.get_value().name == "_default_") {
139                                          continue;
140                                  }
141                                  
142                                  return iter.get_value().name;
143                         }
144                         return "";
145                 }
146                 public string firstBuildModuleWith(JsRender.JsRender file)
147                 {
148                         
149                         var iter = this.compilegroups.map_iterator();
150                         while(iter.next()) {
151                                   
152                                  if (iter.get_value().name == "_default_") {
153                                          continue;
154                                  }
155                                  if (iter.get_value().has_file(file)) {
156                                         return iter.get_value().name;
157                                  }
158                                  
159                                  
160                         }
161                         return "";
162                 }
163                 
164                 public string relPath(string target)
165                 {
166                         var basename = this.firstPath();
167                         // eg. base = /home/xxx/fred/blogs
168                         // target = /home/xxx/fred/jones
169                         
170                         // this does not work correctly...
171                         var bb = basename;
172                         var prefix = "";
173                         while (true) {
174                                 if (    bb.length < target.length &&
175                                         target.substring(0, bb.length) == bb) {
176                                         
177                                         return prefix + target.substring(bb.length );
178                                 }
179                                 if (bb.length < 1) {
180                                         throw new Error.INVALID_FORMAT ("Could not work out relative path %s to %s",
181                                                                         basename, target);
182                                 }
183                                 bb = GLib.Path.get_dirname(bb);
184                                 prefix += "../";
185                                 
186                         }
187          
188                 }
189                 /**
190                  * get a list of files for a folder..
191                  * 
192                  * - in the project manager this has to list all possible compilable 
193                  *   files  - eg. exclue XXX.vala.c or XXX.c with the same name as 
194                  *   a vala file (so to ignore the generated files)
195                  * 
196                  * - for the editor navigation - this should exclude all files that
197                  *   are vala based on a bjs file..
198                  *  
199                  */
200                 
201                 public Gee.ArrayList<string> filesAll(string in_path,bool abspath = true)
202                 {
203                         var ret =  new Gee.ArrayList<string>();
204                         
205                         var dirname = this.resolve_path(
206                                 this.resolve_path_combine_path(this.firstPath(),in_path));
207                         
208                         GLib.debug("SCAN %s", dirname);
209                                 // scan the directory for files -- ending with vala || c
210                         
211
212                         var dir = File.new_for_path(dirname);
213                         if (!dir.query_exists()) {
214                                 GLib.debug("SCAN %s - skip - does not exist\n", dirname);
215                                 return ret;
216                         }
217                         var pathprefix = abspath ? dirname : in_path;
218            
219                         try {
220                                 var file_enum = dir.enumerate_children(
221                                         "standard::*", 
222                                         GLib.FileQueryInfoFlags.NONE, 
223                                         null
224                                 );
225                 
226                  
227                                 FileInfo next_file; 
228                                 while ((next_file = file_enum.next_file(null)) != null) {
229                                         var fn = next_file.get_display_name();
230                                         
231                                         if (next_file.get_file_type () == GLib.FileType.DIRECTORY) {
232                                          
233                                                 GLib.debug("SKIP %s not regular  ", fn);
234                                                 continue;
235                                         }
236                                         if (!Regex.match_simple("^text", next_file.get_content_type())) {
237                                                 continue;
238                                         }
239                                         GLib.debug("SCAN ADD %s : %s", fn, next_file.get_content_type());
240                                         ret.add(pathprefix + "/" + fn);
241                                          
242                                         // any other valid types???
243                                 
244                                 }
245                                 
246                         } catch(Error e) {
247                                 GLib.warning("oops - something went wrong scanning the projects\n");
248                         }       
249                         
250                         return ret;
251                 }
252                 
253                 public Gee.ArrayList<string> filesForCompile(string in_path, bool abspath = true)
254                 {
255                         var allfiles = this.filesAll(in_path,abspath);
256                         var ret =  new Gee.ArrayList<string>();
257                         
258                         
259                         for (var i = 0; i < allfiles.size; i ++) {
260                                 var fn = allfiles.get(i);
261                                 try {
262                                         if (Regex.match_simple("\\.vala$", fn)) {
263                                                 ret.add( fn);
264                                                 continue;
265                                         }
266                                         // vala.c -- ignore..
267                                         if (Regex.match_simple("\\.vala\\.c$", fn)) {
268                                                 continue;
269                                         }
270                                         // not a c file...
271                                         if (!Regex.match_simple("\\.c$", fn)) {
272                                                 continue;
273                                         }
274                                         
275                                         // is the c file the same as a vala file...
276                                         
277                                          
278                                         
279                                         var vv = (new Regex("\\.c$")).replace( fn, fn.length, 0, ".vala");
280                                 
281                                         
282                                                 
283                                         if (allfiles.index_of( vv) > -1) {
284                                                 continue;
285                                         }
286                                         // add the 'c' file..
287                                         ret.add(fn);
288                                 } catch (Error e) {
289                                         continue;
290                                 }
291                         }
292                         // sort.
293                         ret.sort((fa,fb) => {
294                                 return ((string)fa).collate((string) fb);
295                         });
296                         return ret;
297                         
298                 }
299                 
300                 public Gee.ArrayList<string> filesForOpen(string in_path)
301                 {
302                         var allfiles = this.filesAll(in_path);
303                         var ret =  new Gee.ArrayList<string>();
304                         GLib.debug("SCAN %s - %d files",in_path, allfiles.size);
305                         
306                         for (var i = 0; i < allfiles.size; i ++) {
307                                 var fn = allfiles.get(i);
308                                 var bn  = GLib.Path.get_basename(fn);
309                                 try {
310                                         
311                                         if (Regex.match_simple("\\.vala\\.c$", fn)) {
312                                                 GLib.debug("SKIP %s - vala.c",fn);
313
314                                                 continue;
315                                         }
316                                         
317                                         if (Regex.match_simple("\\.bjs$", fn)) {
318                                                 GLib.debug("SKIP %s - .bjs",fn);
319                                                 continue;
320                                         }
321                                         
322                                         if (Regex.match_simple("\\~$", fn)) {
323                                                 GLib.debug("SKIP %s - ~",fn);
324                                                 continue;
325                                         }
326                                         if (Regex.match_simple("\\.stamp$", fn)) {
327                                                 GLib.debug("SKIP %s - .o",fn);
328                                                 continue;
329                                         }
330                                         if ("stamp-h1" == bn) {
331                                                 GLib.debug("SKIP %s - .o",fn);
332                                                 continue;
333                                         }
334                                         
335                                         // confgure.am
336                                         if ("config.h" == bn || "config.h.in" == bn || "config.log" == bn  || "configure" == bn ) {
337                                                 if (allfiles.index_of( in_path +"/configure.ac") > -1) {
338                                                         continue;
339                                                 }
340                                         }
341                                         // makefile
342                                         if ("Makefile" == bn || "Makefile.in" == bn ) {
343                                                 if (allfiles.index_of( in_path +"/Makefile.am") > -1) {
344                                                         continue;
345                                                 }
346                                         }
347                                         
348                                         if (Regex.match_simple("^\\.", bn)) {
349                                                 GLib.debug("SKIP %s - hidden",fn);
350                                                 continue;
351                                         }
352                                         if (Regex.match_simple("\\.vala$", fn)) {
353                                                 var vv = (new Regex("\\.vala$")).replace( fn, fn.length, 0, ".bjs");
354                                                 if (allfiles.index_of( vv) > -1) {
355                                                         GLib.debug("SKIP %s - .vala (got bjs)",fn);
356                                                         continue;
357                                                 }
358                                                 GLib.debug("ADD %s",fn);
359                                                 ret.add( fn);
360                                                 continue;
361                                         }
362                                         // vala.c -- ignore..
363                                         
364                                         // not a c file...
365                                         if (Regex.match_simple("\\.c$", fn)) {
366                                                 
367                                                 var vv = (new Regex("\\.c$")).replace( fn, fn.length, 0, ".vala");
368                                                 if (allfiles.index_of( vv) > -1) {
369                                                         GLib.debug("SKIP %s - .c (got vala)",fn);
370                                                         continue;
371                                                 }
372                                                 GLib.debug("ADD %s",fn);                                                
373                                                 ret.add( fn);
374                                                 continue;
375                                         }
376                                         
377                                         if (GLib.Path.get_basename( fn) == "config1.builder") {
378                                                 continue;
379                                         }
380                                         // not .c / not .vala /not .bjs.. -- other type of file..
381                                         // allow ???
382                                         GLib.debug("ADD %s",fn);
383                                         // add the 'c' file..
384                                         ret.add(fn);
385                                 } catch (Error e) {
386                                         GLib.debug("Exception %s",e.message);
387                                         continue;
388                                 }
389                         }
390                         // sort.
391                         ret.sort((fa,fb) => {
392                                 return ((string)fa).collate((string) fb);
393                         });
394                         return ret;
395                         
396                 }
397                 
398                 
399                  
400  
401
402                 public   string  resolve_path_combine_path(string first, string second)
403                 {
404                         string ret = first;
405                         if (first.length > 0 && second.length > 0 && !first.has_suffix("/") && !second.has_prefix("/"))
406                         {
407                                 ret += "/";
408                         }
409                         //print("combined path = %s",  ret + second);
410                         return ret + second;
411                 }
412                 public   string  resolve_path_times(string part, int times, string? clue = null)
413                 {
414                         string ret = "";
415                         for (int i = 0; i < times; i++)
416                         {
417                                 if (clue != null && i > 0)
418                                 {
419                                         ret += clue;
420                                 }
421                                 ret += part;
422                         }
423                         return ret;
424                 }
425                 public   string resolve_path(string _path, string? relative = null)
426                 {
427                         string path = _path;
428                         if (relative != null)
429                         {
430                                 path = this.resolve_path_combine_path(path, relative);
431                         }
432                         string[] parts = path.split("/");
433                         string[] ret = {};
434                         int relative_parts = 0;
435                                         
436                         foreach (var part in parts)
437                         {
438                                 if (part.length < 1 || part == ".")
439                                 {
440                                         continue;
441                                 }
442                                 
443                                 if (part == "..")
444                                 {
445                                         if (ret.length > 0)
446                                         {
447                                                 ret = ret[0: ret.length -1];
448                                         }
449                                         else
450                                         {
451                                                 relative_parts++;
452                                         }
453                                         continue;
454                                 }
455                                 
456                                 ret += part;
457                         }
458                         
459                         path =  this.resolve_path_combine_path(this.resolve_path_times("..", relative_parts, "/"), string.joinv("/", ret));
460                         if (_path.has_prefix("/"))
461                         {
462                                 path = "/" + path;
463                         }
464                         return path;
465                 }
466                 
467                 public string[] vapidirs()
468                 {
469                         string[] ret = {};
470                         var sources = this.compilegroups.get("_default_").sources;
471                         for(var i =0; i< sources.size; i++) {
472                                 
473                                 var path = this.resolve_path( this.firstPath(), sources.get(i));
474                                 
475                                 if (Path.get_basename (path) == "vapi") {
476                                         GLib.debug("Adding VAPIDIR: %s\n", path);
477                                         ret += path;
478                                 }
479                                 
480                         }
481                         return ret;
482                         
483                 }
484                 public string[] sourcedirs()
485                 {
486                         string[] ret = {};
487                         var sources = this.compilegroups.get("_default_").sources;
488                         ret += this.firstPath();  
489                         for(var i =0; i< sources.size; i++) {
490                                 
491                                 var path = this.resolve_path( this.firstPath(), sources.get(i));
492                                 if (path == this.firstPath()) {
493                                         continue;
494                                 }
495                                 if (Path.get_basename (path) == "vapi") {
496                                         continue;
497                 
498                                 }
499                 //                      GLib.debug("Adding VAPIDIR: %s\n", path);
500                                 ret += path;            
501                         }
502                         return ret;
503                         
504                 }       
505
506         }
507         
508    
509 }