[gnome-code-assistance/wip/vala: 17/20] [backends/vala] Fix makefile integration for vala



commit 7b0eb9df35c373a40e24976a201f4fb6a5cfa2e3
Author: Jesse van den Kieboom <jessevdk gmail com>
Date:   Mon Nov 18 09:57:57 2013 +0100

    [backends/vala] Fix makefile integration for vala

 backends/vala/makefileintegration.vala |  716 ++++++++++++++++----------------
 1 files changed, 359 insertions(+), 357 deletions(-)
---
diff --git a/backends/vala/makefileintegration.vala b/backends/vala/makefileintegration.vala
index 6e4ab3e..d69e67e 100644
--- a/backends/vala/makefileintegration.vala
+++ b/backends/vala/makefileintegration.vala
@@ -17,62 +17,37 @@
  * along with gedit-code-assistant.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-namespace Gca.Backends.Vala
+class MakefileIntegration
 {
-
-public errordomain MakefileIntegrationError
-{
-       MISSING_MAKEFILE,
-       MISSING_TARGET,
-       MISSING_MAKE_OUTPUT
-}
-
-class MakefileIntegration : Object
-{
-       private class Cache
+       static uint file_hash(File f)
        {
-               private File d_source;
-               private File? d_makefile;
-               private string[] d_args;
-
-               public Cache(File source, File? makefile, string[] args)
-               {
-                       d_source = source;
-                       d_makefile = makefile;
-                       d_args = args;
-               }
-
-               public File makefile
-               {
-                       get { return d_makefile; }
-               }
+               return f.hash();
+       }
 
-               public File source
-               {
-                       get { return d_source; }
-               }
+       static bool file_equal(File f1, File f2)
+       {
+               return f1.equal(f2);
+       }
 
-               public string[] args
+       class Makefile
+       {
+               class Source
                {
-                       get { return d_args; }
-                       set { d_args = value; }
+                       public TimeVal mtime;
+                       public string[] flags;
                }
-       }
 
-       private class Makefile
-       {
                private File d_file;
-               private Gee.ArrayList<File> d_sources;
-               private FileMonitor ?d_monitor;
-               private uint d_timeoutid;
-
-               public signal void changed();
+               private Gee.HashMap<File, Source> d_sources;
+               private TimeVal d_mtime;
+               private FileMonitor? d_monitor;
 
                public Makefile(File file)
                {
                        d_file = file;
-                       d_timeoutid = 0;
-                       d_monitor = null;
+                       d_sources = new Gee.HashMap<File, Source>(file_hash, file_equal);
+
+                       update_mtime();
 
                        try
                        {
@@ -83,471 +58,498 @@ class MakefileIntegration : Object
                                return;
                        }
 
-                       d_sources = new Gee.ArrayList<File>();
+                       d_monitor.changed.connect(on_changed);
+               }
 
-                       d_monitor.changed.connect(on_makefile_changed);
+               public File file
+               {
+                       get { return d_file; }
                }
 
-               public bool valid
+               private TimeVal file_mtime(File f)
                {
-                       get
+                       try
                        {
-                               return d_monitor != null;
+                               var info = f.query_info(FileAttribute.TIME_MODIFIED, FileQueryInfoFlags.NONE);
+                               return info.get_modification_time();
                        }
+                       catch {}
+
+                       return TimeVal() {
+                               tv_sec = 0,
+                               tv_usec = 0
+                       };
                }
 
-               public void add(File source)
+               private void update_mtime()
                {
-                       d_sources.add(source);
+                       d_mtime = file_mtime(d_file);
                }
 
-               public bool remove(File source)
+               private void on_changed()
                {
-                       d_sources.remove(source);
+                       update_mtime();
+               }
+
+               private Source make_source(string[] flags)
+               {
+                       var ret = new Source();
+
+                       ret.flags = flags;
+                       ret.mtime = d_mtime;
 
-                       return (d_sources.size == 0);
+                       return ret;
                }
 
-               public Gee.ArrayList<File> sources
+               public void dispose()
                {
-                       get { return d_sources; }
+                       if (d_monitor != null)
+                       {
+                               d_monitor.cancel();
+                               d_monitor = null;
+                       }
                }
 
-               public File file
+               public void add(File source, string[] flags)
                {
-                       get { return d_file; }
+                       d_sources[file] = make_source(flags);
                }
 
-               private void on_makefile_changed(File file, File ?other, FileMonitorEvent event_type)
+               public bool remove(File source)
                {
-                       if (event_type == FileMonitorEvent.CHANGED ||
-                           event_type == FileMonitorEvent.CREATED)
+                       if (d_sources.unset(source) && d_sources.size == 0)
                        {
-                               if (d_timeoutid != 0)
-                               {
-                                       Source.remove(d_timeoutid);
-                               }
+                               dispose();
+                               return true;
+                       }
 
-                               d_timeoutid = Timeout.add(100, on_makefile_timeout);
+                       return false;
+               }
+
+               private bool newer(TimeVal t1, TimeVal t2)
+               {
+                       if (t1.tv_sec == t2.tv_sec)
+                       {
+                               return t1.tv_usec > t2.tv_usec;
+                       }
+                       else
+                       {
+                               return t1.tv_sec > t2.tv_sec;
                        }
                }
 
-               private bool on_makefile_timeout()
+               public bool up_to_date_for(File source)
                {
-                       d_timeoutid = 0;
+                       var s = d_sources[source];
+
+                       if (s != null)
+                       {
+                               return !newer(d_mtime, s.mtime);
+                       }
+                       else
+                       {
+                               return false;
+                       }
+               }
 
-                       changed();
+               public string[]? flags_for_file(File source)
+               {
+                       var s = d_sources[source];
 
-                       return false;
+                       if (s != null)
+                       {
+                               return s.flags;
+                       }
+                       else
+                       {
+                               return null;
+                       }
                }
-               
        }
 
-       private Gee.HashMap<File, Cache> d_argsCache;
-       private Gee.HashMap<File, Makefile> d_makefileCache;
-
-       public signal void arguments_changed(File file);
+       private Gee.HashMap<File, Makefile> d_cache;
+       private Gee.HashMap<File, Makefile> d_file_to_makefile;
 
-       construct
+       public MakefileIntegration()
        {
-               d_argsCache = new Gee.HashMap<File, Cache>();
-               d_makefileCache = new Gee.HashMap<File, Makefile>();
+               d_cache = new Gee.HashMap<File, Makefile>(file_hash, file_equal);
+               d_file_to_makefile = new Gee.HashMap<File, Makefile>(file_hash, file_equal);
        }
 
-       private File ?makefile_for(File file,
-                                  Cancellable ?cancellable = null) throws IOError,
-                                                                          Error
+       public bool changed_for_file(File f)
        {
-               File ?ret = null;
+               var makefile = makefile_for(f);
 
-               File? par = file.get_parent();
+               if (makefile == null)
+               {
+                       return false;
+               }
 
-               while (par != null && ret == null)
+               var m = d_cache[makefile];
+
+               if (m != null)
                {
-                       File makefile = par.get_child("Makefile");
+                       return m.up_to_date_for(f);
+               }
+
+               return true;
+       }
 
-                       if (makefile.query_exists(cancellable))
+       public void dispose(File f)
+       {
+               var m = d_file_to_makefile[f];
+
+               if (m != null)
+               {
+                       if (m.remove(f))
                        {
-                               ret = makefile;
+                               d_cache.unset(m.file);
                        }
 
-                       par = par.get_parent();
+                       d_file_to_makefile.unset(f);
+               }
+       }
+
+       public string[]? flags_for_file(File f)
+       {
+               var makefile = makefile_for(f);
+
+               if (makefile == null)
+               {
+                       return null;
                }
 
-               if (ret != null)
+               var m = d_cache[makefile];
+
+               if (m != null)
                {
-                       log("GcaVala", LogLevelFlags.LEVEL_DEBUG,
-                           "Resolved makefile for `%s': `%s'",
-                           file.get_path(),
-                           ret.get_path());
+                       if (m.up_to_date_for(f))
+                       {
+                               return m.flags_for_file(f);
+                       }
                }
 
-               return ret;
+               var targets = targets_from_make(makefile, f);
+               var flags = flags_from_targets(makefile, f, targets);
+
+               return update_cache(makefile, f, flags);
        }
 
-       private string[] targets_from_make(File makefile,
-                                          File source) throws SpawnError,
-                                                              RegexError,
-                                                              MakefileIntegrationError
+       private string[]? update_cache(File makefile, File f, string[] flags)
        {
-               File wd = makefile.get_parent();
-               string basen = wd.get_relative_path(source);
+               var m = d_cache[makefile];
 
-               string[] args = new string[] {
-                       "make",
-                       "-p",
-                       "-n",
-                       null
-               };
+               if (m == null)
+               {
+                       m = new Makefile(makefile);
+                       d_cache[makefile] = m;
+               }
 
-               string outstr;
+               m.add(f, flags);
+               d_file_to_makefile[f] = m;
 
-               /* Spawn make to find out which target has the source as a
-                  dependency */
-               Process.spawn_sync(wd.get_path(),
-                                  args,
-                                  null,
-                                  SpawnFlags.SEARCH_PATH |
-                                  SpawnFlags.STDERR_TO_DEV_NULL,
-                                  null,
-                                  out outstr);
+               return flags;
+       }
 
-               /* Scan the output to find the target */
-               string reg = "^([^:\n]*?(\\.stamp:|:)).*%s".printf(Regex.escape_string(basen));
+       private File? find_subdir_with_path(File parent, string relpath)
+       {
+               // All dirs in parent, recursively
+               var dirs = new File[]{parent};
 
-               Regex regex = new Regex(reg, RegexCompileFlags.MULTILINE);
-               MatchInfo info;
+               while (dirs.length != 0)
+               {
+                       var d = dirs[0];
+                       dirs = dirs[1:dirs.length];
 
-               var ret = new string[1];
+                       FileEnumerator iter;
 
-               if (regex.match(outstr, 0, out info))
-               {
-                       while (true)
+                       try
                        {
-                               var target = info.fetch(1);
-                               target = target.substring(0, target.length - 1);
+                               var attrs = FileAttribute.STANDARD_NAME + "," + FileAttribute.STANDARD_TYPE;
+                               iter = d.enumerate_children(attrs, FileQueryInfoFlags.NONE);
+                       } catch {
+                               continue;
+                       }
 
-                               if (target.has_suffix(".stamp"))
-                               {
-                                       ret[0] = target;
-                               }
-                               else
-                               {
-                                       ret += target;
-                               }
+                       FileInfo? info;
 
-                               if (!info.next())
+                       try
+                       {
+                               while ((info = iter.next_file()) != null)
                                {
-                                       break;
-                               }
-                       }
-               }
+                                       if (info.get_file_type() == FileType.DIRECTORY)
+                                       {
+                                               File dir = iter.get_child(info);
+                                               File child = dir.get_child(relpath);
 
-               if (ret[0] == null)
-               {
-                       ret = ret[1:ret.length];
-               }
+                                               if (child.query_exists())
+                                               {
+                                                       var mf = makefile_for(child, false);
 
-               if (ret.length != 0)
-               {
-                       return ret;
+                                                       if (mf != null)
+                                                       {
+                                                               return mf;
+                                                       }
+                                               }
+
+                                               dirs += dir;
+                                       }
+                               }
+                       } catch { continue; }
                }
 
-               throw new MakefileIntegrationError.MISSING_TARGET(
-                       "Could not find make target for %s".printf(basen));
+               return null;
        }
 
-       private string[] ?flags_from_targets(File     makefile,
-                                            File     source,
-                                            string[] targets) throws SpawnError,
-                                                                     MakefileIntegrationError,
-                                                                     ShellError
+       private File? subdir_makefile_for(File parent, File f)
        {
-               /* Fake make to build the target and extract the flags */
-               var wd = makefile.get_parent();
-               string relsource = wd.get_relative_path(source);
-
-               string fakecc = "__GCA_VALA_COMPILE_ARGS__";
+               var relpath = Path.get_dirname(parent.get_relative_path(f));
+               return find_subdir_with_path(parent, relpath);
+       }
 
-               string?[] args = new string?[] {
-                       "make",
-                       "-s",
-                       "-i",
-                       "-n",
-                       "-W",
-                       relsource,
-                       "V=1",
-                       "VALAC=" + fakecc
-               };
+       private File ?makefile_for(File f, bool tryac = true)
+       {
+               var fromcache = d_file_to_makefile[f];
 
-               foreach (var target in targets)
+               if (fromcache != null)
                {
-                       args += target;
+                       return fromcache.file;
                }
 
-               args += null;
+               var parent = f.get_parent();
+               var tocheck = new string[] {"configure.ac", "configure.in", "configure"};
 
-               log("GcaVala", LogLevelFlags.LEVEL_DEBUG,
-                   "Running: %s",
-                   string.joinv(" ", args));
-
-               string outstr;
+               while (parent != null)
+               {
+                       var makefile = parent.get_child("Makefile");
 
-               Process.spawn_sync(makefile.get_parent().get_path(),
-                                  args,
-                                  null,
-                                  SpawnFlags.SEARCH_PATH |
-                                  SpawnFlags.STDERR_TO_DEV_NULL,
-                                  null,
-                                  out outstr);
+                       if (makefile.query_exists())
+                       {
+                               return makefile;
+                       }
 
-               /* Extract args */
-               int idx = outstr.last_index_of(fakecc);
+                       foreach (var c in tocheck)
+                       {
+                               var cc = parent.get_child(c);
 
-               if (idx < 0)
-               {
-                       throw new MakefileIntegrationError.MISSING_MAKE_OUTPUT("Make output did not contain 
flags");
-               }
+                               if (cc.query_exists())
+                               {
+                                       var ret = subdir_makefile_for(parent, f);
 
-               string[] retargs;
-               string[] parts = outstr.substring(idx).split("\n");
+                                       if (ret != null)
+                                       {
+                                               return ret;
+                                       }
 
-               Shell.parse_argv(parts[0], out retargs);
+                                       break;
+                               }
+                       }
 
-               log("GcaVala", LogLevelFlags.LEVEL_DEBUG,
-                   "Parsed command: %s => '%s'\n",
-                   parts[0],
-                   string.joinv("', '", retargs));
+                       parent = parent.get_parent();
+               }
 
-               return retargs;
+               return null;
        }
 
-       private async void makefile_changed_async(Makefile makefile)
+       private string[] targets_from_make(File makefile, File source)
        {
-               ThreadFunc<void *> func = () => {
-                       foreach (File file in makefile.sources)
-                       {
-                               find_for_makefile(makefile.file, file);
-                       }
+               File wd = makefile.get_parent();
+               var relpath = wd.get_relative_path(source);
 
-                       return null;
+               var lookfor = new string[] {
+                       wd.get_relative_path(source)
                };
 
-               try
-               {
-                       new Thread<void *>.try("find makefile", func);
-                       yield;
-               }
-               catch
+               var bname = source.get_basename();
+
+               if (bname != relpath)
                {
+                       lookfor += bname;
                }
-       }
 
-       private void on_makefile_changed(Makefile makefile)
-       {
-               makefile_changed_async.begin(makefile);
-       }
+               string[] args = new string[] {
+                       "make",
+                       "-p",
+                       "-n",
+                       "-s",
+                       null
+               };
 
-       private void find_for_makefile(File makefile, File file)
-       {
-               string[] targets;
-               string[] args = {};
+               string outstr;
 
+               /* Spawn make to find out which target has the source as a
+                  dependency */
                try
                {
-                       targets = targets_from_make(makefile, file);
-
-                       log("GcaVala", LogLevelFlags.LEVEL_DEBUG,
-                           "Makefile make targets for `%s': `%s'",
-                           file.get_path(),
-                           string.joinv(", ", targets));
-
-                       args = flags_from_targets(makefile, file, targets);
-
-                       log("GcaVala", LogLevelFlags.LEVEL_DEBUG,
-                           "Compile flags for `%s': `%s`",
-                           file.get_path(),
-                           string.joinv("`, `", args));
+                       Process.spawn_sync(wd.get_path(),
+                                          args,
+                                          null,
+                                          SpawnFlags.SEARCH_PATH |
+                                          SpawnFlags.STDERR_TO_DEV_NULL,
+                                          null,
+                                          out outstr);
                }
-               catch (Error e)
+               catch (SpawnError e)
                {
-                       stderr.printf("Makefile error: %s\n", e.message);
+                       return new string[0];
                }
 
-               lock(d_makefileCache)
+               var targets = new string[0];
+               var found = new Gee.HashSet<string>();
+
+               while (lookfor.length > 0)
                {
-                       lock(d_argsCache)
+                       for (var i = 0; i < lookfor.length; i++)
+                       {
+                               lookfor[i] = Regex.escape_string(lookfor[i]);
+                       }
+
+                       var relookfor = string.joinv("|", lookfor);
+                       lookfor = new string[0];
+
+                       Regex reg;
+
+                       try
+                       {
+                               reg = new Regex("^([^:\n ]+):.*\\b(%s)\\b".printf(relookfor), 
RegexCompileFlags.MULTILINE);
+                       } catch (Error e) { stderr.printf("regex: %s\n", e.message); continue; }
+
+                       MatchInfo info;
+
+                       reg.match(outstr, 0, out info);
+
+                       while (info.matches())
                        {
-                               if (d_argsCache.has_key(file))
+                               var target = info.fetch(1);
+
+                               try
                                {
-                                       d_argsCache[file].args = args;
-                               }
-                               else
+                                       info.next();
+                               } catch {};
+
+                               if (target[0] == '#' || target[0] == '.' || target.has_suffix("-am"))
                                {
-                                       Cache c = new Cache(file, makefile, args);
-                                       d_argsCache[file] = c;
+                                       continue;
                                }
 
-                               if (!d_makefileCache.has_key(makefile))
+                               if (found.contains(target))
                                {
-                                       Makefile m = new Makefile(makefile);
-                                       m.add(file);
-
-                                       m.changed.connect(on_makefile_changed);
-                                       d_makefileCache[makefile] = m;
+                                       continue;
                                }
+
+                               targets += target;
+                               found.add(target);
+                               lookfor += target;
                        }
                }
 
-               changed_in_idle(file);
-       }
+               var sorted = new Gee.ArrayList<string>.wrap(targets);
 
-       private void changed_in_idle(File file)
-       {
-               Idle.add(() => {
-                       arguments_changed(file);
-                       return false;
+               sorted.sort((a, b) => {
+                       var sa = a.has_suffix(".stamp");
+                       var sb = b.has_suffix(".stamp");
+
+                       if (sa == sb)
+                       {
+                               return 0;
+                       }
+
+                       return sa ? -1 : 1;
                });
+
+               return sorted.to_array();
        }
 
-       private async void find_async(File file)
+       private string[] flags_from_targets(File makefile, File source, string[] targets)
        {
-               ThreadFunc<void *> func = () => {
-                       File ?makefile = null;
+               if (targets.length == 0)
+               {
+                       return new string[0];
+               }
 
-                       try
-                       {
-                               makefile = makefile_for(file);
-                       }
-                       catch (Error e)
-                       {
-                               makefile = null;
-                       }
+               var fakevalac = "__GCA_VALA_COMPILE_ARGS__";
 
-                       if (makefile == null)
-                       {
-                               Cache c = new Cache(file, null, new string[] {});
-                               d_argsCache[file] = c;
+               var wd = makefile.get_parent();
+               var relsource = wd.get_relative_path(source);
 
-                               changed_in_idle(file);
-                               return null;
-                       }
+               var args = new string?[] {
+                       "make",
+                       "-s",
+                       "-i",
+                       "-n",
+                       "-W",
+                       relsource,
+                       "V=1",
+                       "VALAC=" + fakevalac
+               };
 
-                       find_for_makefile(makefile, file);
+               foreach (var target in targets)
+               {
+                       args += target;
+               }
 
-                       lock(d_makefileCache)
-                       {
-                               if (d_makefileCache.has_key(file))
-                               {
-                                       d_makefileCache[makefile].add(file);
-                               }
-                       }
+               args += null;
 
-                       return null;
-               };
+               string outstr;
 
                try
                {
-                       new Thread<void *>.try("findasync", func);
-                       yield;
+                       Process.spawn_sync(wd.get_path(),
+                                          args,
+                                          null,
+                                          SpawnFlags.SEARCH_PATH |
+                                          SpawnFlags.STDERR_TO_DEV_NULL,
+                                          null,
+                                          out outstr);
                }
-               catch
+               catch (SpawnError e)
                {
+                       return new string[0];
                }
-       }
 
-       public new string[]? get(File file)
-       {
-               string[] ?ret = null;
+               /* Extract args */
+               int pos = outstr.index_of(fakevalac);
 
-               lock(d_argsCache)
+               if (pos < 0)
                {
-                       if (d_argsCache.has_key(file))
-                       {
-                               ret = d_argsCache[file].args;
-                       }
-                       else
-                       {
-                               monitor(file);
-                       }
+                       return new string[0];
                }
 
-               return ret;
-       }
+               int epos = outstr.index_of("\n", pos);
 
-       public async string[] args_for_file(File file) throws MakefileIntegrationError {
-               lock(d_argsCache)
+               if (epos < 0)
                {
-                       if (d_argsCache.has_key(file))
-                       {
-                               return d_argsCache[file].;
-
-                               lock(d_makefileCache)
-                               {
-                                       return 
-                               }
-                       }
+                       epos = outstr.length;
                }
-       }
-
-       public void monitor(File file)
-       {
-               bool hascache;
 
-               lock(d_argsCache)
-               {
-                       hascache = d_argsCache.has_key(file);
-               }
+               string[] retargs;
+               var sargs = outstr[pos+fakevalac.length:epos-pos];
 
-               if (hascache)
+               try
                {
-                       arguments_changed(file);
+                       Shell.parse_argv(sargs, out retargs);
                }
-               else
+               catch (ShellError e)
                {
-                       find_async.begin(file, (source, res) => find_async.end(res));
+                       return new string[0];
                }
-       }
 
-       public void remove_monitor(File file)
-       {
-               lock(d_argsCache)
-               {
-                       if (d_argsCache.has_key(file))
-                       {
-                               Cache c = d_argsCache[file];
-
-                               lock (d_makefileCache)
-                               {
-                                       if (d_makefileCache.has_key(c.makefile))
-                                       {
-                                               Makefile m = d_makefileCache[c.makefile];
-
-                                               if (m.remove(file))
-                                               {
-                                                       d_makefileCache.unset(c.makefile);
-                                               }
-                                       }
-                               }
+               log("GcaVala", LogLevelFlags.LEVEL_DEBUG,
+                   "Parsed command: %s => '%s'\n",
+                   sargs,
+                   string.joinv("', '", retargs));
 
-                               d_argsCache.unset(file);
-                       }
-               }
+               return retargs;
        }
 }
 
+#if MAIN
 public static int main(string[] a){
-       var ml = new MainLoop();
-
        MakefileIntegration it = new MakefileIntegration();
 
-       it.monitor(File.new_for_commandline_arg("dbus.vala"));
-
-       ml.run();
-
+       stdout.printf("Flags: %s\n", string.joinv(", ", 
it.flags_for_file(File.new_for_commandline_arg(a[1]))));
        return 0;
 }
-
-}
+#endif
 
 /* vi:ex:ts=4 */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]