/* Copyright (C) 2006 Matthias Braun Scott Ellington This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ using System; using System.IO; using System.Collections.Generic; using System.Reflection; using System.Text; using MonoDevelop.Projects; using MonoDevelop.Core.Serialization; using MonoDevelop.Core; using MonoDevelop.Core.Collections; using MonoDevelop.Core.Assemblies; using Mono.Addins; using MonoDevelop.Deployment; namespace MonoDevelop.Autotools { public class SimpleProjectMakefileHandler : IMakefileHandler { string resourcedir = "resources"; bool generateAutotools = true; // Handle files to be deployed Dictionary deployDirs; Dictionary deployFileVars; Dictionary allDeployVars; List builtFiles; StringBuilder deployFileCopyVars; StringBuilder deployFileCopyTargets; //used only for simple makefile generation StringBuilder templateFilesTargets = null; StringBuilder installTarget = null; StringBuilder installDeps = null; List installDirs = null; StringBuilder uninstallTarget = null; // handle configuration specific variables StringBuilder conf_vars; // Variables for project files StringBuilder files; StringBuilder res_files; StringBuilder extras; StringBuilder datafiles; // custom commands StringBuilder customCommands; // store all refs for easy access Set pkgs; public bool CanDeploy (SolutionItem entry, MakefileType type) { Project project = entry as Project; if ( project == null ) return false; if ( FindSetupForProject ( project ) == null ) return false; return true; } ISimpleAutotoolsSetup FindSetupForProject (Project project) { object[] items = AddinManager.GetExtensionObjects ("/MonoDevelop/Autotools/SimpleSetups", typeof(ISimpleAutotoolsSetup)); foreach ( ISimpleAutotoolsSetup setup in items) { if ( setup.CanDeploy ( project ) ) return setup; } return null; } public Makefile Deploy (AutotoolsContext ctx, SolutionItem entry, IProgressMonitor monitor) { generateAutotools = ctx.MakefileType == MakefileType.AutotoolsMakefile; monitor.BeginTask ( GettextCatalog.GetString ( "Creating {0} for Project {1}", generateAutotools ? "Makefile.am" : "Makefile", entry.Name), 1 ); Makefile makefile = new Makefile (); try { if ( !CanDeploy (entry, generateAutotools ? MakefileType.AutotoolsMakefile : MakefileType.SimpleMakefile) ) throw new Exception ( GettextCatalog.GetString ("Not a deployable project.") ); Project project = entry as Project; TemplateEngine templateEngine = new TemplateEngine(); ISimpleAutotoolsSetup setup = FindSetupForProject ( project ); // Handle files to be deployed deployDirs = new Dictionary (); deployFileVars = new Dictionary (); builtFiles = new List (); deployFileCopyVars = new StringBuilder (); deployFileCopyTargets = new StringBuilder (); //used only for simple makefile generation templateFilesTargets = null; installTarget = null; installDeps = null; installDirs = null; uninstallTarget = null; // handle configuration specific variables conf_vars = new StringBuilder (); // grab all project files files = new StringBuilder (); res_files = new StringBuilder (); extras = new StringBuilder (); datafiles = new StringBuilder (); Set extraFiles = new Set (); string includes = String.Empty; string references, dllReferences; DotNetProject netProject = project as DotNetProject; ProcessProjectReferences (netProject, out references, out dllReferences, ctx); templateEngine.Variables["REFERENCES"] = references; templateEngine.Variables["DLL_REFERENCES"] = dllReferences; templateEngine.Variables["WARNING"] = "Warning: This is an automatically generated file, do not edit!"; DotNetProject dotnetProject = entry as DotNetProject; if (dotnetProject != null) { string resgen = "resgen"; if (System.Environment.Version.Major >= 2) { switch (dotnetProject.TargetFramework.ClrVersion) { case ClrVersion.Net_1_1: resgen = "resgen1"; break; default: resgen = "resgen2"; break; } } templateEngine.Variables ["RESGEN"] = resgen; } string pfpath = null; foreach (ProjectFile projectFile in project.Files) { pfpath = FileService.NormalizeRelativePath (projectFile.FilePath.ToRelative (project.BaseDirectory)); switch ( projectFile.BuildAction ) { case BuildAction.Compile: if ( projectFile.Subtype != Subtype.Code ) continue; files.AppendFormat ( "\\\n\t{0} ", MakefileData.ToMakefilePath (pfpath)); break; case BuildAction.Content: case BuildAction.None: extraFiles.Add (MakefileData.ToMakefilePath (pfpath)); break; case BuildAction.EmbeddedResource: if ( !projectFile.FilePath.IsChildPathOf ( ctx.BaseDirectory ) ) { // file is not within directory hierarchy, copy it in string rdir = Path.Combine (Path.GetDirectoryName (project.FileName), resourcedir); if ( !Directory.Exists ( rdir ) ) Directory.CreateDirectory ( rdir ); string newPath = Path.Combine (rdir, Path.GetFileName ( projectFile.FilePath )); FileService.CopyFile ( projectFile.FilePath, newPath ) ; pfpath = project.GetRelativeChildPath (newPath); pfpath = FileService.NormalizeRelativePath (pfpath); } if (!String.IsNullOrEmpty (projectFile.ResourceId) && projectFile.ResourceId != Path.GetFileName (pfpath)) res_files.AppendFormat ("\\\n\t{0},{1} ", MakefileData.ToMakefilePath (pfpath), MakefileData.EscapeString (projectFile.ResourceId)); else res_files.AppendFormat ("\\\n\t{0} ", MakefileData.ToMakefilePath (pfpath)); break; case "FileCopy": datafiles.AppendFormat ("\\\n\t{0} ", MakefileData.ToMakefilePath (pfpath)); break; } } if (!generateAutotools) { templateFilesTargets = new StringBuilder (); installTarget = new StringBuilder (); uninstallTarget = new StringBuilder (); installDeps = new StringBuilder (); installDirs = new List (); customCommands = new StringBuilder (); string programFilesDir = ctx.DeployContext.GetDirectory (TargetDirectory.ProgramFiles); //FIXME:temp programFilesDir = TranslateDir (programFilesDir); installDirs.Add (programFilesDir); installTarget.Append ("\tmake pre-install-local-hook prefix=$(prefix)\n"); installTarget.Append ("\tmake install-satellite-assemblies prefix=$(prefix)\n"); installTarget.AppendFormat ("\tmkdir -p '$(DESTDIR){0}'\n", programFilesDir); installTarget.AppendFormat ("\t$(call cp,$(ASSEMBLY),$(DESTDIR){0})\n", programFilesDir); installTarget.AppendFormat ("\t$(call cp,$(ASSEMBLY_MDB),$(DESTDIR){0})\n", programFilesDir); //remove dir? uninstallTarget.Append ("\tmake pre-uninstall-local-hook prefix=$(prefix)\n"); uninstallTarget.Append ("\tmake uninstall-satellite-assemblies prefix=$(prefix)\n"); uninstallTarget.AppendFormat ("\t$(call rm,$(ASSEMBLY),$(DESTDIR){0})\n", programFilesDir); uninstallTarget.AppendFormat ("\t$(call rm,$(ASSEMBLY_MDB),$(DESTDIR){0})\n", programFilesDir); installDeps.Append (" $(ASSEMBLY) $(ASSEMBLY_MDB)"); conf_vars.AppendFormat ("srcdir=.\n"); conf_vars.AppendFormat ("top_srcdir={0}\n\n", FileService.AbsoluteToRelativePath (project.BaseDirectory, ctx.TargetSolution.BaseDirectory)); conf_vars.AppendFormat ("include $(top_srcdir)/config.make\n\n"); // Don't emit for top level project makefile(eg. pdn.make), as it would be // included by top level solution makefile if (ctx.TargetSolution.BaseDirectory != project.BaseDirectory){ string customhooks = Path.Combine (project.BaseDirectory, "custom-hooks.make"); bool include = File.Exists (customhooks); includes = "include $(top_srcdir)/Makefile.include\n"; includes += String.Format ("{0}include $(srcdir)/custom-hooks.make\n\n", include ? "" : "#"); if (include) makefile.SetVariable ("EXTRA_DIST", "$(srcdir)/custom-hooks.make"); } } bool buildEnabled; List configSections = new List (); allDeployVars = new Dictionary (); foreach (SolutionConfiguration combineConfig in ctx.TargetSolution.Configurations) { DotNetProjectConfiguration config = GetProjectConfig (combineConfig.Id, project, out buildEnabled) as DotNetProjectConfiguration; if (config == null) continue; ConfigSection configSection = new ConfigSection (combineConfig.Id); string assembly = MakefileData.GetUnixPath (project.GetRelativeChildPath (config.CompiledOutputName)); configSection.BuildVariablesBuilder.AppendFormat ("ASSEMBLY_COMPILER_COMMAND = {0}\n", setup.GetCompilerCommand ( project, config.Id ) ); configSection.BuildVariablesBuilder.AppendFormat ("ASSEMBLY_COMPILER_FLAGS = {0}\n", setup.GetCompilerFlags ( project, config.Id ) ); // add check for compiler command in configure.ac ctx.AddCommandCheck ( setup.GetCompilerCommand ( project, config.Id ) ); configSection.BuildVariablesBuilder.AppendFormat ("ASSEMBLY = {0}\n", AutotoolsContext.EscapeStringForAutomake (assembly)); configSection.BuildVariablesBuilder.AppendFormat ("ASSEMBLY_MDB = {0}\n", config.DebugMode ? "$(ASSEMBLY).mdb" : String.Empty); string target; switch (config.CompileTarget) { case CompileTarget.Exe: target = "exe"; break; case CompileTarget.Library: target = "library"; break; case CompileTarget.WinExe: target = "winexe"; break; case CompileTarget.Module: target = "module"; break; default: throw new Exception( GettextCatalog.GetString ("Unknown target {0}", config.CompileTarget ) ); } configSection.BuildVariablesBuilder.AppendFormat ( "COMPILE_TARGET = {0}\n", target ); // for project references, we need a ref to the dll for the current configuration StringWriter projectReferences = new StringWriter(); string pref = null; foreach (ProjectReference reference in netProject.References) { if (reference.ReferenceType != ReferenceType.Project) continue; Project refp = GetProjectFromName (reference.Reference, ctx.TargetSolution); if (!(refp is DotNetProject)) continue; DotNetProjectConfiguration dnpc = GetProjectConfig (combineConfig.Id, refp, out buildEnabled) as DotNetProjectConfiguration; if ( dnpc == null ) throw new Exception ( GettextCatalog.GetString ("Could not add reference to project '{0}'", refp.Name) ); projectReferences.WriteLine (" \\"); projectReferences.Write ("\t"); pref = project.GetRelativeChildPath (dnpc.CompiledOutputName); projectReferences.Write (MakefileData.ToMakefilePath (pref)); } configSection.BuildVariablesBuilder.AppendFormat ( "PROJECT_REFERENCES = {0}\n", projectReferences.ToString() ); string buildDir = project.GetRelativeChildPath (config.OutputDirectory); configSection.BuildVariablesBuilder.AppendFormat ("BUILD_DIR = {0}\n", MakefileData.ToMakefilePath (buildDir)); // Register files built by this configuration. // Built files won't be distributed. foreach (string bfile in builtFiles) ctx.AddBuiltFile (Path.Combine (config.OutputDirectory, bfile)); DeployFileCollection deployFiles = DeployService.GetDeployFiles ( ctx.DeployContext, new SolutionItem[] { project }, config.Selector); ProcessDeployFilesForConfig (deployFiles, project, configSection, ctx, config); configSections.Add (configSection); if (!generateAutotools) { EmitCustomCommandTargets (config.CustomCommands, project, customCommands, combineConfig.Id, new CustomCommandType [] { CustomCommandType.BeforeBuild, CustomCommandType.AfterBuild, CustomCommandType.BeforeClean, CustomCommandType.AfterClean}, monitor); } else { if (config.CustomCommands.Count > 0) monitor.ReportWarning (GettextCatalog.GetString ("Custom commands are not supported for autotools based makefiles. Ignoring.")); } // Register files generated by the compiler ctx.AddBuiltFile (project.GetOutputFileName (combineConfig.Selector)); if (config.DebugMode) ctx.AddBuiltFile (project.GetOutputFileName (combineConfig.Selector) + ".mdb"); if (config.SignAssembly) { string spath = project.GetRelativeChildPath (config.AssemblyKeyFile); spath = FileService.NormalizeRelativePath (spath); extraFiles.Add (MakefileData.ToMakefilePath (spath)); } if (buildEnabled && pkgs.Count > 0) ctx.AddRequiredPackages (combineConfig.Id, pkgs); } foreach (string ef in extraFiles) extras.AppendFormat ("\\\n\t{0} ", ef); Dictionary commonDeployVars = new Dictionary (allDeployVars); foreach (ConfigSection configSection in configSections) { List toRemove = new List (); foreach (KeyValuePair pair in commonDeployVars) { if (!configSection.DeployFileVars.ContainsKey (pair.Key)) toRemove.Add (pair.Key); } foreach (string s in toRemove) commonDeployVars.Remove (s); } //emit the config sections here.. to conf_vars foreach (ConfigSection configSection in configSections) { conf_vars.AppendFormat (generateAutotools ? "if ENABLE_{0}\n" : "ifeq ($(CONFIG),{0})\n", ctx.EscapeAndUpperConfigName (configSection.Name)); conf_vars.Append (configSection.BuildVariablesBuilder.ToString ()); conf_vars.Append ("\n"); if (ctx.Switches != null) { foreach (Switch s in ctx.Switches) { conf_vars.AppendLine (string.Format (@"if ENABLE_{0} ASSEMBLY_COMPILER_FLAGS += -define:{1} endif", s.SwitchName.Replace ('-', '_').ToUpperInvariant (), s.Define)); } } foreach (KeyValuePair pair in allDeployVars) { string targetDeployVar = pair.Key; if (pair.Value.File.ContainsPathReferences) //Template files are not handled per-config continue; if (configSection.DeployFileVars.ContainsKey (targetDeployVar)) { //use the dfile from the config section DeployFile dfile = configSection.DeployFileVars [targetDeployVar]; string fname = MakefileData.ToMakefilePath ( FileService.AbsoluteToRelativePath ( Path.GetFullPath (project.BaseDirectory), Path.GetFullPath (dfile.SourcePath))); conf_vars.AppendFormat ("{0}_SOURCE={1}\n", targetDeployVar, fname); if (!commonDeployVars.ContainsKey (targetDeployVar)) { //FOO_DLL=$(BUILD_DIR)/foo.dll conf_vars.AppendFormat ("{0}=$(BUILD_DIR)/{1}\n", targetDeployVar, MakefileData.ToMakefilePath (dfile.RelativeTargetPath)); } } else { // not common and not part of @configSection conf_vars.AppendFormat ("{0}=\n", pair.Key); } } conf_vars.Append ( "\nendif\n\n" ); } conf_vars.AppendFormat ("AL={0}\n", (dotnetProject.TargetFramework.ClrVersion == ClrVersion.Net_2_0) ? "al2" : "al"); conf_vars.AppendFormat ("SATELLITE_ASSEMBLY_NAME=$(notdir $(basename $(ASSEMBLY))).resources.dll\n"); foreach (KeyValuePair pair in allDeployVars) { HandleDeployFile (pair.Value, pair.Key, project, ctx); if (commonDeployVars.ContainsKey (pair.Key)) { //FOO_DLL=$(BUILD_DIR)/foo.dll deployFileCopyVars.AppendFormat ("{0} = $(BUILD_DIR)/{1}\n", pair.Key, MakefileData.ToMakefilePath (pair.Value.File.RelativeTargetPath)); } } conf_vars.Append ('\n'); StringBuilder vars = new StringBuilder (); foreach (KeyValuePair pair in deployDirs) { //PROGRAM_FILES= .. etc conf_vars.AppendFormat ("{0} = {1} \n\n", pair.Key, pair.Value.ToString ()); //Build list of deploy dir variables vars.AppendFormat ("$({0}) ", pair.Key); } if (!generateAutotools) { installTarget.Insert (0, String.Format ("install-local:{0}\n", installDeps.ToString ())); installTarget.Append ("\tmake post-install-local-hook prefix=$(prefix)\n"); uninstallTarget.Insert (0, String.Format ("uninstall-local:{0}\n", installDeps.ToString ())); uninstallTarget.Append ("\tmake post-uninstall-local-hook prefix=$(prefix)\n"); } if (!generateAutotools && customCommands.Length > 0) customCommands.Insert (0, "# Targets for Custom commands\n"); templateEngine.Variables["CONFIG_VARS"] = conf_vars.ToString (); templateEngine.Variables["DEPLOY_FILE_VARS"] = vars.ToString (); templateEngine.Variables["COPY_DEPLOY_FILES_VARS"] = deployFileCopyVars.ToString(); templateEngine.Variables["COPY_DEPLOY_FILES_TARGETS"] = deployFileCopyTargets.ToString(); templateEngine.Variables["ALL_TARGET"] = (ctx.TargetSolution.BaseDirectory == project.BaseDirectory) ? "all-local" : "all"; templateEngine.Variables["INCLUDES"] = includes; templateEngine.Variables["FILES"] = files.ToString(); templateEngine.Variables["RESOURCES"] = res_files.ToString(); templateEngine.Variables["EXTRAS"] = extras.ToString(); templateEngine.Variables["DATA_FILES"] = datafiles.ToString(); templateEngine.Variables["CLEANFILES"] = vars.ToString (); if (!generateAutotools) { templateEngine.Variables["TEMPLATE_FILES_TARGETS"] = templateFilesTargets.ToString(); templateEngine.Variables["INSTALL_TARGET"] = installTarget.ToString(); templateEngine.Variables["UNINSTALL_TARGET"] = uninstallTarget.ToString(); templateEngine.Variables["CUSTOM_COMMAND_TARGETS"] = customCommands.ToString(); } // Create project specific makefile Stream stream = ctx.GetTemplateStream ( generateAutotools ? "Makefile.am.project.template" : "Makefile.noauto.project.template"); StreamReader reader = new StreamReader (stream); string txt = templateEngine.Process ( reader ); reader.Close(); makefile.Append ( txt ); monitor.Step (1); } finally { monitor.EndTask (); } return makefile; } void ProcessProjectReferences (DotNetProject project, out string references, out string dllReferences, AutotoolsContext ctx) { StringWriter refWriter = new StringWriter(); StringWriter dllRefWriter = new StringWriter(); pkgs = new Set (); // grab pkg-config references foreach (ProjectReference reference in project.References) { if (reference.ReferenceType == ReferenceType.Package) { // Get pkg-config keys SystemPackage pkg = reference.Package; if (pkg != null && !pkg.IsCorePackage) { if ( pkgs.Contains(pkg) ) continue; pkgs.Add(pkg); refWriter.WriteLine (" \\"); if (generateAutotools) { refWriter.Write ("\t$("); refWriter.Write (AutotoolsContext.GetPkgConfigVariable(pkg.Name)); refWriter.Write ("_LIBS)"); } else { refWriter.Write ("\t-pkg:{0}", pkg.Name); } pkgs.Add (pkg); } else { refWriter.WriteLine (" \\"); // store all refs for easy access AssemblyName assembly = SystemAssemblyService.ParseAssemblyName (reference.Reference); refWriter.Write ("\t" + assembly.Name); refWriter.Write (""); } } else if (reference.ReferenceType == ReferenceType.Assembly) { string assemblyPath = Path.GetFullPath (reference.Reference); dllRefWriter.WriteLine (" \\"); dllRefWriter.Write ("\t"); ctx.AddGlobalReferencedFile (MakefileData.ToMakefilePath (FileService.AbsoluteToRelativePath ( Path.GetFullPath (ctx.BaseDirectory), assemblyPath))); dllRefWriter.Write (MakefileData.ToMakefilePath (FileService.AbsoluteToRelativePath ( project.BaseDirectory, assemblyPath))); } else if (reference.ReferenceType == ReferenceType.Project) continue; // handled per-config else throw new Exception ( GettextCatalog.GetString ("Project reference type '{0}' not supported yet", reference.ReferenceType.ToString() ) ); } references = refWriter.ToString (); dllReferences = dllRefWriter.ToString (); } // Populates configSection.DeployFileVars with unique DeployFiles for a particular config void ProcessDeployFilesForConfig (DeployFileCollection deployFiles, Project project, ConfigSection configSection, AutotoolsContext ctx, DotNetProjectConfiguration config) { //@deployFiles can have duplicates Dictionary uniqueDeployFiles = new Dictionary (); foreach (DeployFile dfile in deployFiles) { if (dfile.SourcePath == project.GetOutputFileName (configSection.Selector)) continue; // DeployFileCollection can have duplicates, ignore them string key = dfile.RelativeTargetPath; if (!dfile.ContainsPathReferences) key += dfile.SourcePath; if (uniqueDeployFiles.ContainsKey (key)) continue; uniqueDeployFiles [key] = dfile; string targetDeployVar = GetDeployVar (deployFileVars, dfile.RelativeTargetPath); configSection.DeployFileVars [targetDeployVar] = dfile; DeployFileData data = new DeployFileData (); data.File = dfile; data.Configuration = config; allDeployVars [targetDeployVar] = data; } } // Handle unique deploy files, emits non-perconfig stuff, like targets for deploy files, // un/install commands void HandleDeployFile (DeployFileData data, string targetDeployVar, Project project, AutotoolsContext ctx) { DeployFile dfile = data.File; string dependencyDeployFile = null; //Dependency for the deployfile target if (dfile.ContainsPathReferences) { // Template file, copy to .in file string full_fname = Path.Combine (project.BaseDirectory, Path.GetFileName (dfile.RelativeTargetPath)); string fname = full_fname; string infname = fname + ".in"; if (File.Exists (infname) && project.IsFileInProject (infname)) { string datadir = Path.Combine (project.BaseDirectory, "data"); if (!Directory.Exists (datadir)) Directory.CreateDirectory (datadir); infname = Path.Combine (datadir, Path.GetFileName (dfile.RelativeTargetPath) + ".in"); } //Absolute path required File.Copy (dfile.SourcePath, infname, true); //Path relative to TargetCombine fname = FileService.NormalizeRelativePath ( FileService.AbsoluteToRelativePath (ctx.TargetSolution.BaseDirectory, full_fname)); infname = fname + ".in"; ctx.AddAutoconfFile (MakefileData.ToMakefilePath (fname)); ctx.AddGeneratedFile (full_fname + ".in"); //Path relative to project fname = FileService.NormalizeRelativePath ( FileService.AbsoluteToRelativePath (project.BaseDirectory, full_fname)); infname = fname + ".in"; extras.AppendFormat ("\\\n\t{0} ", MakefileData.ToMakefilePath (infname)); //dependencyDeployFile here should be filename relative to the project dependencyDeployFile = fname; } else { dependencyDeployFile = String.Format ("$({0}_SOURCE)", targetDeployVar); } builtFiles.Add (Path.GetFileName (dfile.RelativeTargetPath)); if (dfile.ContainsPathReferences) deployFileCopyTargets.AppendFormat ("$(eval $(call emit-deploy-wrapper,{0},{1}{2}))\n", targetDeployVar, MakefileData.ToMakefilePath (dependencyDeployFile), (dfile.FileAttributes & DeployFileAttributes.Executable) != 0 ? ",x" : String.Empty); else { // The emit-deploy-target macro copies the deployable file to the output directory. // This is not needed if the file is already there (e.g. for an .mdb file) if (Path.GetFullPath (dfile.SourcePath) != Path.GetFullPath (Path.Combine (data.Configuration.OutputDirectory, dfile.RelativeTargetPath))) deployFileCopyTargets.AppendFormat ("$(eval $(call emit-deploy-target,{0}))\n", targetDeployVar); } switch (dfile.TargetDirectoryID) { case TargetDirectory.Gac: // TODO break; default: string var; if (dfile.TargetDirectoryID != TargetDirectory.Binaries) { string ddir = FileService.NormalizeRelativePath (dfile.RelativeTargetPath.ParentDirectory.ToString().Trim ('/',' ')); if (ddir.Length > 0) ddir = "/" + ddir; var = ctx.GetDeployDirectoryVar (dfile.TargetDirectoryID + ddir); } else var = "BINARIES"; StringBuilder sb; if (!deployDirs.TryGetValue (var, out sb)) { sb = new StringBuilder (); deployDirs [var] = sb; } sb.AppendFormat ("\\\n\t$({0}) ", targetDeployVar); break; } if (!generateAutotools) { string installDir = Path.GetDirectoryName (ctx.DeployContext.GetResolvedPath (dfile.TargetDirectoryID, dfile.RelativeTargetPath)); //FIXME: temp installDir = TranslateDir (installDir); if (!installDirs.Contains (installDir)) { installTarget.AppendFormat ("\tmkdir -p '$(DESTDIR){0}'\n", installDir); installDirs.Add (installDir); } installTarget.AppendFormat ("\t$(call cp,$({0}),$(DESTDIR){1})\n", targetDeployVar, installDir); uninstallTarget.AppendFormat ("\t$(call rm,$({1}),$(DESTDIR){0})\n", installDir, targetDeployVar); } } string TranslateDir (string dir) { dir = dir.Replace ("@prefix@", "$(prefix)"); dir = dir.Replace ("@PACKAGE@", "$(PACKAGE)"); dir = dir.Replace ("@expanded_libdir@", "$(libdir)"); dir = dir.Replace ("@expanded_bindir@", "$(bindir)"); dir = dir.Replace ("@expanded_datadir@", "$(datadir)"); return dir; } void EmitCustomCommandTargets (CustomCommandCollection commands, Project project, StringBuilder builder, string configName, CustomCommandType[] types, IProgressMonitor monitor) { bool warned = false; configName = configName.ToUpper (); foreach (CustomCommandType type in types) { bool targetEmitted = false; for (int i = 0; i < commands.Count; i ++) { CustomCommand cmd = commands [i]; if (cmd.Type != type) { if (!warned && Array.IndexOf (types, cmd.Type) < 0) { //Warn (only once) if unsupported custom command is found, StringBuilder types_list = new StringBuilder (); foreach (CustomCommandType t in types) types_list.AppendFormat ("{0}, ", t); monitor.ReportWarning (GettextCatalog.GetString ( "Custom commands of only the following types are supported: {0}.", types_list.ToString ())); warned = true; } continue; } if (!targetEmitted) { builder.AppendFormat ("{0}_{1}:\n", configName, type.ToString ()); targetEmitted = true; } string dir, exe, args; ResolveCustomCommand (project, cmd, out dir, out exe, out args); builder.AppendFormat ("\t(cd {0} && {1} {2})\n", dir, exe, args); } if (targetEmitted) builder.Append ("\n"); } } void ResolveCustomCommand (Project project, CustomCommand cmd, out string dir, out string exe, out string args) { dir = exe = args = null; string [,] customtags = new string [,] { {"ProjectDir", "$(srcdir)"}, {"TargetName", "$(notdir $(ASSEMBLY))"}, {"TargetDir", "$(BUILD_DIR)"}, {"CombineDir", "$(top_srcdir)"} }; int i = cmd.Command.IndexOf (' '); if (i == -1) { exe = cmd.Command; args = string.Empty; } else { exe = cmd.Command.Substring (0, i); args = StringParserService.Parse (cmd.Command.Substring (i + 1), customtags); } dir = (string.IsNullOrEmpty (cmd.WorkingDir) ? "$(srcdir)" : StringParserService.Parse (cmd.WorkingDir, customtags)); } // Get the Project config corresponding to its @parentConfig internal static SolutionItemConfiguration GetProjectConfig (string parentConfig, SolutionEntityItem entry, out bool enabled) { enabled = false; SolutionConfiguration solutionConfig = entry.ParentSolution.Configurations [parentConfig] as SolutionConfiguration; if (solutionConfig == null) return null; foreach (SolutionConfigurationEntry cce in solutionConfig.Configurations) { if (cce.Item == entry) { enabled = cce.Build; return entry.Configurations [cce.ItemConfiguration]; } } return null; } string GetDeployVar (Dictionary dict, string filePath) { filePath = MonoDevelop.Core.FileService.NormalizeRelativePath (filePath); string name = Path.GetFileName (filePath); name = name.Replace (Path.DirectorySeparatorChar, '_'); name = name.Replace ('.', '_'); name = name.ToUpper (); name = AutotoolsContext.EscapeStringForAutoconf (name).Trim ('_'); // Avoid var name colisions int n = 2; string extName = name; string regPath; while (dict.TryGetValue (extName, out regPath) && regPath != filePath) { extName = name + (n++); } dict [extName] = filePath; return extName; } Project GetProjectFromName (string name, Solution targetSolution) { Project refp = null; if (targetSolution != null) refp = targetSolution.FindProjectByName (name); if (refp == null) throw new Exception ( GettextCatalog.GetString ("Couldn't find referenced project '{0}'", name ) ); return refp; } } class DeployFileData { public DeployFile File; public DotNetProjectConfiguration Configuration; } }