internal static void GenerateModels(string modelsDirectory, string binPath) { if (!Directory.Exists(modelsDirectory)) { Directory.CreateDirectory(modelsDirectory); } foreach (var file in Directory.GetFiles(modelsDirectory, "*.generated.cs")) { File.Delete(file); } var umbraco = Application.GetApplication(); var typeModels = umbraco.GetAllTypes(); var ourFiles = Directory.GetFiles(modelsDirectory, "*.cs").ToDictionary(x => x, File.ReadAllText); var parseResult = new CodeParser().ParseWithReferencedAssemblies(ourFiles); var builder = new TextBuilder(typeModels, parseResult, UmbracoConfig.For.ModelsBuilder().ModelsNamespace); foreach (var typeModel in builder.GetModelsToGenerate()) { var sb = new StringBuilder(); builder.Generate(sb, typeModel); var filename = Path.Combine(modelsDirectory, typeModel.ClrName + ".generated.cs"); File.WriteAllText(filename, sb.ToString()); } // the idea was to calculate the current hash and to add it as an extra file to the compilation, // in order to be able to detect whether a DLL is consistent with an environment - however the // environment *might not* contain the local partial files, and thus it could be impossible to // calculate the hash. So... maybe that's not a good idea after all? /* * var currentHash = HashHelper.Hash(ourFiles, typeModels); * ourFiles["models.hash.cs"] = $@"using Umbraco.ModelsBuilder; * [assembly:ModelsBuilderAssembly(SourceHash = ""{currentHash}"")] * "; */ if (binPath != null) { //When bin directory is changed by config if (!Directory.Exists(binPath)) { Directory.CreateDirectory(binPath); } foreach (var file in Directory.GetFiles(modelsDirectory, "*.generated.cs")) { ourFiles[file] = File.ReadAllText(file); } var compiler = new Compiler(); compiler.Compile(builder.GetModelsNamespace(), ourFiles, binPath); } OutOfDateModelsStatus.Clear(); }
internal static void GenerateModels(string appData, string bin) { var modelsDirectory = Path.Combine(appData, "Models"); if (!Directory.Exists(modelsDirectory)) { Directory.CreateDirectory(modelsDirectory); } foreach (var file in Directory.GetFiles(modelsDirectory, "*.generated.cs")) { File.Delete(file); } var umbraco = Application.GetApplication(); var typeModels = umbraco.GetAllTypes(); var ourFiles = Directory.GetFiles(modelsDirectory, "*.cs").ToDictionary(x => x, File.ReadAllText); var parseResult = new CodeParser().ParseWithReferencedAssemblies(ourFiles); var builder = new TextBuilder(typeModels, parseResult, UmbracoConfig.For.ModelsBuilder().ModelsNamespace); foreach (var typeModel in builder.GetModelsToGenerate()) { var sb = new StringBuilder(); builder.Generate(sb, typeModel); var filename = Path.Combine(modelsDirectory, typeModel.ClrName + ".generated.cs"); File.WriteAllText(filename, sb.ToString()); } if (bin != null) { foreach (var file in Directory.GetFiles(modelsDirectory, "*.generated.cs")) { ourFiles[file] = File.ReadAllText(file); } var compiler = new Compiler(); compiler.Compile(builder.GetModelsNamespace(), ourFiles, bin); } OutOfDateModelsStatus.Clear(); }
private Assembly GetModelsAssembly(bool forceRebuild) { var modelsDirectory = UmbracoConfig.For.ModelsBuilder().ModelsDirectory; if (!Directory.Exists(modelsDirectory)) { Directory.CreateDirectory(modelsDirectory); } // must filter out *.generated.cs because we haven't deleted them yet! var ourFiles = Directory.Exists(modelsDirectory) ? Directory.GetFiles(modelsDirectory, "*.cs") .Where(x => !x.EndsWith(".generated.cs")) .ToDictionary(x => x, File.ReadAllText) : new Dictionary <string, string>(); var umbraco = Application.GetApplication(); var typeModels = umbraco.GetAllTypes(); var currentHash = HashHelper.Hash(ourFiles, typeModels); var modelsHashFile = Path.Combine(modelsDirectory, "models.hash"); var modelsSrcFile = Path.Combine(modelsDirectory, "models.generated.cs"); var projFile = Path.Combine(modelsDirectory, "all.generated.cs"); var dllPathFile = Path.Combine(modelsDirectory, "all.dll.path"); // caching the generated models speeds up booting // currentHash hashes both the types & the user's partials if (!forceRebuild) { _logger.Logger.Debug <PureLiveModelFactory>("Looking for cached models."); if (File.Exists(modelsHashFile) && File.Exists(projFile)) { var cachedHash = File.ReadAllText(modelsHashFile); if (currentHash != cachedHash) { _logger.Logger.Debug <PureLiveModelFactory>("Found obsolete cached models."); forceRebuild = true; } } else { _logger.Logger.Debug <PureLiveModelFactory>("Could not find cached models."); forceRebuild = true; } } Assembly assembly; if (forceRebuild == false) { // try to load the dll directly (avoid rebuilding) if (File.Exists(dllPathFile)) { var dllPath = File.ReadAllText(dllPathFile); if (File.Exists(dllPath)) { assembly = Assembly.LoadFile(dllPath); var attr = assembly.GetCustomAttribute <ModelsBuilderAssemblyAttribute>(); if (attr != null && attr.PureLive && attr.SourceHash == currentHash) { // if we were to resume at that revision, then _ver would keep increasing // and that is probably a bad idea - so, we'll always rebuild starting at // ver 1, but we remember we want to skip that one - so we never end up // with the "same but different" version of the assembly in memory _skipver = assembly.GetName().Version.Revision; _logger.Logger.Debug <PureLiveModelFactory>("Loading cached models (dll)."); return(assembly); } } } // mmust reset the version in the file else it would keep growing // loading cached modules only happens when the app restarts var text = File.ReadAllText(projFile); var match = AssemblyVersionRegex.Match(text); if (match.Success) { text = text.Replace(match.Value, "AssemblyVersion(\"0.0.0." + _ver + "\")"); File.WriteAllText(projFile, text); } // generate a marker file that will be a dependency // see note in RazorBuildProvider_CodeGenerationStarted // NO: using all.generated.cs as a dependency //File.WriteAllText(Path.Combine(modelsDirectory, "models.dep"), "VER:" + _ver); _ver++; assembly = BuildManager.GetCompiledAssembly(ProjVirt); File.WriteAllText(dllPathFile, assembly.Location); _logger.Logger.Debug <PureLiveModelFactory>("Loading cached models (source)."); return(assembly); } // need to rebuild _logger.Logger.Debug <PureLiveModelFactory>("Rebuilding models."); // generate code, save var code = GenerateModelsCode(ourFiles, typeModels); // add extra attributes, // PureLiveAssembly helps identifying Assemblies that contain PureLive models // AssemblyVersion is so that we have a different version for each rebuild var ver = _ver == _skipver ? ++_ver : _ver; _ver++; code = code.Replace("//ASSATTR", $@"[assembly: PureLiveAssembly] [assembly:ModelsBuilderAssembly(PureLive = true, SourceHash = ""{currentHash}"")] [assembly:System.Reflection.AssemblyVersion(""0.0.0.{ver}"")]"); File.WriteAllText(modelsSrcFile, code); // generate proj, save ourFiles["models.generated.cs"] = code; var proj = GenerateModelsProj(ourFiles); File.WriteAllText(projFile, proj); // compile and register assembly = BuildManager.GetCompiledAssembly(ProjVirt); File.WriteAllText(dllPathFile, assembly.Location); // assuming we can write and it's not going to cause exceptions... File.WriteAllText(modelsHashFile, currentHash); _logger.Logger.Debug <PureLiveModelFactory>("Done rebuilding."); return(assembly); }
private Assembly GetModelsAssembly(bool forceRebuild) { var appData = HostingEnvironment.MapPath("~/App_Data"); if (appData == null) { throw new Exception("Panic: appData is null."); } var modelsDirectory = Path.Combine(appData, "Models"); if (!Directory.Exists(modelsDirectory)) { Directory.CreateDirectory(modelsDirectory); } // must filter out *.generated.cs because we haven't deleted them yet! var ourFiles = Directory.Exists(modelsDirectory) ? Directory.GetFiles(modelsDirectory, "*.cs") .Where(x => !x.EndsWith(".generated.cs")) .ToDictionary(x => x, File.ReadAllText) : new Dictionary <string, string>(); var umbraco = Application.GetApplication(); var typeModels = umbraco.GetAllTypes(); var currentHash = Hash(ourFiles, typeModels); var modelsHashFile = Path.Combine(modelsDirectory, "models.hash"); var modelsSrcFile = Path.Combine(modelsDirectory, "models.generated.cs"); var projFile = Path.Combine(modelsDirectory, "all.generated.cs"); var projVirt = "~/App_Data/Models/all.generated.cs"; // caching the generated models speeds up booting // if you change your own partials, delete the .generated.cs file to force a rebuild if (!forceRebuild) { _logger.Logger.Debug <PureLiveModelFactory>("Looking for cached models."); if (File.Exists(modelsHashFile) && File.Exists(projFile)) { var cachedHash = File.ReadAllText(modelsHashFile); if (currentHash != cachedHash) { _logger.Logger.Debug <PureLiveModelFactory>("Found obsolete cached models."); forceRebuild = true; } } else { _logger.Logger.Debug <PureLiveModelFactory>("Could not find cached models."); forceRebuild = true; } } if (forceRebuild == false) { _logger.Logger.Debug <PureLiveModelFactory>("Loading cached models."); // mmust reset the version in the file else it would keep growing // loading cached modules only happens when the app restarts var text = File.ReadAllText(projFile); var match = AssemblyVersionRegex.Match(text); if (match.Success) { text = text.Replace(match.Value, "AssemblyVersion(\"0.0.0." + _ver++ + "\")"); File.WriteAllText(projFile, text); } return(BuildManager.GetCompiledAssembly(projVirt)); } // need to rebuild _logger.Logger.Debug <PureLiveModelFactory>("Rebuilding models."); // generate code, save var code = GenerateModelsCode(ourFiles, typeModels); // add extra attributes, // PureLiveAssembly helps identifying Assemblies that contain PureLive models // AssemblyVersion is so that we have a different version for each rebuild code = code.Replace("//ASSATTR", "[assembly: PureLiveAssembly, System.Reflection.AssemblyVersion(\"0.0.0." + _ver++ + "\")]"); File.WriteAllText(modelsSrcFile, code); // generate proj, save ourFiles["models.generated.cs"] = code; var proj = GenerateModelsProj(ourFiles); File.WriteAllText(projFile, proj); // compile and register var assembly = BuildManager.GetCompiledAssembly(projVirt); // assuming we can write and it's not going to cause exceptions... File.WriteAllText(modelsHashFile, currentHash); _logger.Logger.Debug <PureLiveModelFactory>("Done rebuilding."); return(assembly); }
private Assembly GetModelsAssembly(bool forceRebuild) { var modelsDirectory = UmbracoConfig.For.ModelsBuilder().ModelsDirectory; if (!Directory.Exists(modelsDirectory)) { Directory.CreateDirectory(modelsDirectory); } // must filter out *.generated.cs because we haven't deleted them yet! var ourFiles = Directory.Exists(modelsDirectory) ? Directory.GetFiles(modelsDirectory, "*.cs") .Where(x => !x.EndsWith(".generated.cs")) .ToDictionary(x => x, File.ReadAllText) : new Dictionary <string, string>(); var umbraco = Application.GetApplication(); var typeModels = umbraco.GetAllTypes(); var currentHash = HashHelper.Hash(ourFiles, typeModels); var modelsHashFile = Path.Combine(modelsDirectory, "models.hash"); var modelsSrcFile = Path.Combine(modelsDirectory, "models.generated.cs"); var projFile = Path.Combine(modelsDirectory, "all.generated.cs"); // caching the generated models speeds up booting // if you change your own partials, delete the .generated.cs file to force a rebuild if (!forceRebuild) { _logger.Logger.Debug <PureLiveModelFactory>("Looking for cached models."); if (File.Exists(modelsHashFile) && File.Exists(projFile)) { var cachedHash = File.ReadAllText(modelsHashFile); if (currentHash != cachedHash) { _logger.Logger.Debug <PureLiveModelFactory>("Found obsolete cached models."); forceRebuild = true; } } else { _logger.Logger.Debug <PureLiveModelFactory>("Could not find cached models."); forceRebuild = true; } } if (forceRebuild == false) { _logger.Logger.Debug <PureLiveModelFactory>("Loading cached models."); // mmust reset the version in the file else it would keep growing // loading cached modules only happens when the app restarts var text = File.ReadAllText(projFile); var match = AssemblyVersionRegex.Match(text); if (match.Success) { text = text.Replace(match.Value, "AssemblyVersion(\"0.0.0." + _ver + "\")"); File.WriteAllText(projFile, text); } // generate a marker file that will be a dependency // see note in RazorBuildProvider_CodeGenerationStarted // NO: using all.generated.cs as a dependency //File.WriteAllText(Path.Combine(modelsDirectory, "models.dep"), "VER:" + _ver); _ver++; return(BuildManager.GetCompiledAssembly(ProjVirt)); } // need to rebuild _logger.Logger.Debug <PureLiveModelFactory>("Rebuilding models."); // generate code, save var code = GenerateModelsCode(ourFiles, typeModels); // add extra attributes, // PureLiveAssembly helps identifying Assemblies that contain PureLive models // AssemblyVersion is so that we have a different version for each rebuild code = code.Replace("//ASSATTR", $@"[assembly: PureLiveAssembly] [assembly:ModelsBuilderAssembly(PureLive = true, SourceHash = ""{currentHash}"")] [assembly:System.Reflection.AssemblyVersion(""0.0.0.{_ver++}"")]"); File.WriteAllText(modelsSrcFile, code); // generate proj, save ourFiles["models.generated.cs"] = code; var proj = GenerateModelsProj(ourFiles); File.WriteAllText(projFile, proj); // compile and register var assembly = BuildManager.GetCompiledAssembly(ProjVirt); // assuming we can write and it's not going to cause exceptions... File.WriteAllText(modelsHashFile, currentHash); _logger.Logger.Debug <PureLiveModelFactory>("Done rebuilding."); return(assembly); }
private Assembly GetModelsAssembly(bool forceRebuild) { var modelsDirectory = UmbracoConfig.For.ModelsBuilder().ModelsDirectory; if (!Directory.Exists(modelsDirectory)) { Directory.CreateDirectory(modelsDirectory); } // must filter out *.generated.cs because we haven't deleted them yet! var ourFiles = Directory.Exists(modelsDirectory) ? Directory.GetFiles(modelsDirectory, "*.cs") .Where(x => !x.EndsWith(".generated.cs")) .ToDictionary(x => x, File.ReadAllText) : new Dictionary <string, string>(); var umbraco = Application.GetApplication(); var typeModels = umbraco.GetAllTypes(); var currentHash = HashHelper.Hash(ourFiles, typeModels); var modelsHashFile = Path.Combine(modelsDirectory, "models.hash"); var modelsSrcFile = Path.Combine(modelsDirectory, "models.generated.cs"); var projFile = Path.Combine(modelsDirectory, "all.generated.cs"); var dllPathFile = Path.Combine(modelsDirectory, "all.dll.path"); // caching the generated models speeds up booting // currentHash hashes both the types & the user's partials if (!forceRebuild) { _logger.Logger.Debug <PureLiveModelFactory>("Looking for cached models."); if (File.Exists(modelsHashFile) && File.Exists(projFile)) { var cachedHash = File.ReadAllText(modelsHashFile); if (currentHash != cachedHash) { _logger.Logger.Debug <PureLiveModelFactory>("Found obsolete cached models."); forceRebuild = true; } // else cachedHash matches currentHash, we can try to load an existing dll } else { _logger.Logger.Debug <PureLiveModelFactory>("Could not find cached models."); forceRebuild = true; } } Assembly assembly; if (!forceRebuild) { // try to load the dll directly (avoid rebuilding) // // ensure that the .dll file does not have a corresponding .dll.delete file // as that would mean the the .dll file is going to be deleted and should not // be re-used - that should not happen in theory, but better be safe // // ensure that the .dll file is in the curreng codegen directory - when IIS // or Express does a full restart, it can switch to an entirely new codegen // directory, and then we end up referencing a dll which is *not* in that // directory, and BuildManager fails to instanciate views ("the view found // at ... was not created"). // if (File.Exists(dllPathFile)) { var dllPath = File.ReadAllText(dllPathFile); var codegen = HttpRuntime.CodegenDir; _logger.Logger.Debug <PureLiveModelFactory>($"Cached models dll at {dllPath}."); if (File.Exists(dllPath) && !File.Exists(dllPath + ".delete") && dllPath.StartsWith(codegen)) { assembly = Assembly.LoadFile(dllPath); var attr = assembly.GetCustomAttribute <ModelsBuilderAssemblyAttribute>(); if (attr != null && attr.PureLive && attr.SourceHash == currentHash) { // if we were to resume at that revision, then _ver would keep increasing // and that is probably a bad idea - so, we'll always rebuild starting at // ver 1, but we remember we want to skip that one - so we never end up // with the "same but different" version of the assembly in memory _skipver = assembly.GetName().Version.Revision; _logger.Logger.Debug <PureLiveModelFactory>("Loading cached models (dll)."); return(assembly); } _logger.Logger.Debug <PureLiveModelFactory>("Cached models dll cannot be loaded (invalid assembly)."); } else if (!File.Exists(dllPath)) { _logger.Logger.Debug <PureLiveModelFactory>("Cached models dll does not exist."); } else if (File.Exists(dllPath + ".delete")) { _logger.Logger.Debug <PureLiveModelFactory>("Cached models dll is marked for deletion."); } else if (!dllPath.StartsWith(codegen)) { _logger.Logger.Debug <PureLiveModelFactory>("Cached models dll is in a different codegen directory."); } else { _logger.Logger.Debug <PureLiveModelFactory>("Cached models dll cannot be loaded (why?)."); } } // must reset the version in the file else it would keep growing // loading cached modules only happens when the app restarts var text = File.ReadAllText(projFile); var match = AssemblyVersionRegex.Match(text); if (match.Success) { text = text.Replace(match.Value, "AssemblyVersion(\"0.0.0." + _ver + "\")"); File.WriteAllText(projFile, text); } // generate a marker file that will be a dependency // see note in RazorBuildProvider_CodeGenerationStarted // NO: using all.generated.cs as a dependency //File.WriteAllText(Path.Combine(modelsDirectory, "models.dep"), "VER:" + _ver); _ver++; try { assembly = BuildManager.GetCompiledAssembly(ProjVirt); File.WriteAllText(dllPathFile, assembly.Location); } catch { _logger.Logger.Debug <PureLiveModelFactory>("Failed to compile."); // the dll file reference still points to the previous dll, which is obsolete // now and will be deleted by ASP.NET eventually, so better clear that reference. // also touch the proj file to force views to recompile - don't delete as it's // useful to have the source around for debuggin. try { if (File.Exists(dllPathFile)) { File.Delete(dllPathFile); } if (File.Exists(modelsHashFile)) { File.Delete(modelsHashFile); } if (File.Exists(projFile)) { File.SetLastWriteTime(projFile, DateTime.Now); } } catch { /* enough */ } throw; } _logger.Logger.Debug <PureLiveModelFactory>("Loading cached models (source)."); return(assembly); } // need to rebuild _logger.Logger.Debug <PureLiveModelFactory>("Rebuilding models."); // generate code, save var code = GenerateModelsCode(ourFiles, typeModels); // add extra attributes, // PureLiveAssembly helps identifying Assemblies that contain PureLive models // AssemblyVersion is so that we have a different version for each rebuild var ver = _ver == _skipver ? ++_ver : _ver; _ver++; code = code.Replace("//ASSATTR", $@"[assembly: PureLiveAssembly] [assembly:ModelsBuilderAssembly(PureLive = true, SourceHash = ""{currentHash}"")] [assembly:System.Reflection.AssemblyVersion(""0.0.0.{ver}"")]"); File.WriteAllText(modelsSrcFile, code); // generate proj, save ourFiles["models.generated.cs"] = code; var proj = GenerateModelsProj(ourFiles); File.WriteAllText(projFile, proj); // compile and register try { assembly = BuildManager.GetCompiledAssembly(ProjVirt); File.WriteAllText(dllPathFile, assembly.Location); File.WriteAllText(modelsHashFile, currentHash); } catch { _logger.Logger.Debug <PureLiveModelFactory>("Failed to compile."); // the dll file reference still points to the previous dll, which is obsolete // now and will be deleted by ASP.NET eventually, so better clear that reference. // also touch the proj file to force views to recompile - don't delete as it's // useful to have the source around for debuggin. try { if (File.Exists(dllPathFile)) { File.Delete(dllPathFile); } if (File.Exists(modelsHashFile)) { File.Delete(modelsHashFile); } if (File.Exists(projFile)) { File.SetLastWriteTime(projFile, DateTime.Now); } } catch { /* enough */ } throw; } _logger.Logger.Debug <PureLiveModelFactory>("Done rebuilding."); return(assembly); }