// // CustomMakefile.cs // // Author: // Lluis Sanchez Gual // Ankit Jain // // Copyright (C) 2005 Novell, Inc (http://www.novell.com) // Copyright (C) 2007 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using MonoDevelop.Core; using System.Text.RegularExpressions; using MonoDevelop.Projects; using MonoDevelop.Core.Instrumentation; namespace MonoDevelop.Autotools { public class CustomMakefile { static Counter UpdatedMakefiles = InstrumentationService.CreateCounter ("Updated Makefiles", "Project Model"); string content; //FIXME: Improve the regex static string multilineMatch = @"(((?.*)(?.*?)\s*\\\n([ \t]*(?.*?)\s*\\\n)*[ \t]*(?.*?)(?> varToValuesDict; List dirtyVariables; public CustomMakefile (string file) { this.fileName = file; if (!File.Exists (file)) throw new FileNotFoundException (file); StreamReader sr = new StreamReader (file); content = sr.ReadToEnd (); sr.Close (); } //This is absolute path public string FileName { get { return fileName; } } public string Content { get { return content; } } Dictionary> VarToValuesDict { get { if (varToValuesDict == null) InitVarToValuesDict (); return varToValuesDict; } } List DirtyVariables { get { if (dirtyVariables == null) dirtyVariables = new List (); return dirtyVariables; } } static Regex varRegex = null; static Regex VariablesRegex { get { if (varRegex == null) varRegex = new Regex(@"[.|\n]*^(?[a-zA-Z_0-9]*)((?[ \t]*:?=[ \t]*$)|((?\s*:?=\s*)" + multilineMatch + "))", RegexOptions.Multiline | RegexOptions.Compiled); return varRegex; } } public ICollection GetVariables () { return VarToValuesDict.Keys; } string GetVariable (string var) { List list = GetListVariable (var); if (list == null) return null; StringBuilder sb = new StringBuilder (); sb.AppendFormat ("{0} =", var); if (list.Count > 1) { sb.Append (" "); foreach (string s in list) sb.AppendFormat (" \\\n\t{0}", s); } else if (list.Count == 1) { sb.Append (" " + list [0]); } return sb.ToString (); } public List GetListVariable (string var) { if (!VarToValuesDict.ContainsKey (var)) return null; return VarToValuesDict [var]; } public void SetListVariable (string var, List val) { if (!VarToValuesDict.ContainsKey (var)) return; VarToValuesDict [var] = val; if (!DirtyVariables.Contains (var)) DirtyVariables.Add (var); } void InitVarToValuesDict () { varToValuesDict = new Dictionary> (); foreach (Match m in VariablesRegex.Matches (content)) { if (!m.Success) continue; string varname = m.Groups ["varname"].Value; if (String.IsNullOrEmpty (varname)) continue; List list = new List (); foreach (Capture c in m.Groups["content"].Captures) { string val = c.Value.Trim (); if (val.Length == 0) continue; foreach (string s in val.Split (new char [] {' ', '\t'}, StringSplitOptions.RemoveEmptyEntries)) { if (s.Length > 0) list.Add (s); } } varToValuesDict [varname] = list; } } static Dictionary targetExps = new Dictionary (); public string GetTarget (string var) { //FIXME: //FILES = \ //\tabc.cs ---> the \t is not a must.. and there can be multiple \t's Regex targetExp; if (!targetExps.TryGetValue (var, out targetExp)) { targetExp = new Regex(@"[.|\n]*^" + var + @"(?\s*:\s*)" + multilineMatch + @"\t" + multilineMatch, RegexOptions.Multiline | RegexOptions.Compiled); targetExps [var] = targetExp; } return GetValue (var, targetExp); } string GetValue (string var, Regex exp) { Match match = exp.Match (content); if (!match.Success) return null; string value = ""; foreach (Capture c in match.Groups["content"].Captures) value += c.Value; return value; } public void AppendValueToVar (string var, string val) { List list = GetListVariable (var); if (list == null) ThrowMakefileVarNotFound (var); list.Add (val); } public void SetListVariable (string var, IEnumerable list) { //Set only if the variable exists in the makefile if (GetVariable (var) == null) ThrowMakefileVarNotFound (var); VarToValuesDict [var] = new List (list); } public void ClearVariableValue (string var) { if (GetVariable (var) == null) return; //ThrowMakefileVarNotFound (var); VarToValuesDict [var].Clear (); } static Dictionary varExps = new Dictionary (); void SaveVariable (string var) { Regex varExp; if (!varExps.TryGetValue (var, out varExp)) { varExp = new Regex(@"[.|\n]*^(?" + var + @"((?\s*:?=\s*\n)|((?\s*:?=\s*)" + multilineMatch + ")))", RegexOptions.Multiline | RegexOptions.Compiled); varExps [var] = varExp; } Match match = varExp.Match (content); if (!match.Success) return; Group grp = match.Groups ["var"]; int varLength = grp.ToString ().Trim (' ','\n').Length; //FIXME: Umm too expensive content = String.Concat ( content.Substring (0, grp.Index), GetVariable (var), content.Substring (grp.Index + varLength)); } public void Save () { string oldContent = content; foreach (string var in DirtyVariables) { if (VarToValuesDict [var] != null) SaveVariable (var); } DirtyVariables.Clear (); if (String.Compare (oldContent, content) == 0) return; using (StreamWriter sw = new StreamWriter (fileName)) sw.Write (content); UpdatedMakefiles++; FileService.NotifyFileChanged (fileName); } void ThrowMakefileVarNotFound (string var) { throw new InvalidOperationException (GettextCatalog.GetString ( "Makefile variable {0} not found in the file.", var)); } } }