Upgrading from 3.5 to 5.8. Compilation of templates threw an error.

Posts   
 
    
WilliamB
User
Posts: 17
Joined: 10-Feb-2022
# Posted on: 10-Feb-2022 18:14:58   

Hello, I am a newbie using LLBLGen. I am currently using version 5.8.3 and trying to migrate a project created originally with ver 3.5 The project has some custom templates (TDL and LPT both), a templatebinding file and custom preset. I added using a XML editor .NET 4.5.2 and .NET 4.8.0 to the list of supported platforms. I dropped the version 4.0 from the same list for the preset name and templatebinding file. I modified one of the custom templates (CommonTemplateInclude.lpt) and changed the name of the library referenced there: from: C:\Workspace\Code\Common\Assembly\SD.LLBLGen.Pro.ORMSupportClasses.NET20.dll to C:\Workspace\Code\Common\Assembly\SD.LLBLGen.Pro.ORMSupportClasses.dll Now when I try to generate the code (template group: SelfServicing) using the preset and templatebinding file I am getting the following error:

Exception type: GeneratorAbortException Compilation of templates threw errors: Error CS1002, at line: 43, pos: 10: ; expected

This is the stacktrace:

LLBLGen Pro version 5.8. Build 5.8.3
⦁   -----[Core exception]--------------------
⦁      at SD.LLBLGen.Pro.LptParser.DotNetTemplateEngine.CompileForTDLIncludeTemplates(ITask taskDefinition)
⦁      at SD.LLBLGen.Pro.TaskPerformers.CodeEmitter.CompileLptTemplatesIfNecessary(ITask taskDefinition)
⦁      at SD.LLBLGen.Pro.TaskPerformers.CodeEmitter.Perform(IGenerator executingGenerator, ITask taskDefinition, Dictionary`2 parameters)
⦁      at SD.LLBLGen.Pro.ApplicationCore.CodeGenerationMetaData.Tasks.Task.PerformElement(IGenerator executingGenerator, LogNode parentNode)
⦁      at SD.LLBLGen.Pro.ApplicationCore.CodeGenerationMetaData.Tasks.TaskGroupElement.Perform(IGenerator executingGenerator, LogNode parentNode)
⦁      at SD.LLBLGen.Pro.ApplicationCore.CodeGenerationMetaData.Tasks.TaskGroup.PerformElement(IGenerator executingGenerator, LogNode parentNode)
⦁      at SD.LLBLGen.Pro.ApplicationCore.CodeGenerationMetaData.Tasks.TaskGroupElement.Perform(IGenerator executingGenerator, LogNode parentNode)
⦁      at SD.LLBLGen.Pro.ApplicationCore.CodeGenerationMetaData.Tasks.TaskGroup.PerformElement(IGenerator executingGenerator, LogNode parentNode)
⦁      at SD.LLBLGen.Pro.ApplicationCore.CodeGenerationMetaData.Tasks.TaskGroupElement.Perform(IGenerator executingGenerator, LogNode parentNode)
⦁      at SD.LLBLGen.Pro.GeneratorCore.Generator.RunTasks(String rootNameSpaceToUse, String destinationRootFolder, String groupName)
⦁      at SD.LLBLGen.Pro.GeneratorCore.Generator.Start(CodeGenerationTaskBase codeGenTask, ApplicationConfiguration configurationSettings, Boolean performObsoleteFileCleanup)
⦁      at SD.LLBLGen.Pro.Gui.Classes.GuiController.PerformRunCodeGenerationCycleAction(List`1 codeGenTasks, List`1 vsNetProjectsCreated, List`1 sqlFilesGenerated, Boolean debugBuild, LogNode& tasksResultLog)

Next I am presented with a file named: lptDebugsourceInclude.cs with the following troubling output: This is just a fragment from line 30 onwards:

using SD.LLBLGen.Pro.Core;
using SD.LLBLGen.Pro.Core.GeneralDataStructures;
using SD.LLBLGen.Pro.DBDriverCore;
using SD.LLBLGen.Pro.LptParser;
using Oracle.DataAccess.Client;
using SD.LLBLGen.Pro.ORMSupportClasses;
using System.Diagnostics;
using LLBLGenVersion;
using Time;
using TemplateName;
using TemplateVersion;
using RootNamespace;
using If IsMappedOnResultset;
using EndIf;
using Foreach AdditionalNamespace CrLf;
using CurrentAdditionalNamespace;
using NextForeach;
using UserCodeRegion "AdditionalNamespaces";
using EndUserCodeRegion;
using CurrentTypedViewName;
using Foreach CustomProperty TypedView;
using CustomPropertyName;
using CustomPropertyValue;
using Foreach Attribute TypedView;
using Attribute;
using If UsePartialClasses;
using Foreach AdditionalInterface;
using CurrentAdditionalInterface;
using If Not IsMappedOnResultset;
using UserCodeRegion "AdditionalInterfacesView";
using Foreach TypedViewField CrLf;
using TypedViewFieldName;
using UserCodeRegion "AdditionalMembers";
using AmountOfTypedViewFields;
using If HasParameter NoCursors;
using Foreach Parameter NoCursors CrLf;
using CaseCamel CurrentParameterName;
using CurrentParameterDirection;
using Foreach InputParameter Comma;
using If IsNullable;
using If IsValueType;
using TypeOfParameter;
using Else;
using If HasInputAndOutputParameters NoCursors;
using Foreach OutputParameter NoCursors Comma;
using UserCodeRegion "AdditionalFields";
using Foreach TypedViewField;
using Foreach CustomProperty TypedViewField;
using FieldCaption;
using TypeOfTypedViewField;
using UserCodeRegion "InitClass";
using UserCodeRegion "InitMembers";
using UserCodeRegion "AdditionalColumnProperties";
using UserCodeRegion "CustomTypedViewCode";
using UserCodeRegion "AdditionalInterfacesRow";
using Description;
using SourceObjectName;
using SourceColumnName;

It looks like the tokens at the templates are not being parsed.

Well, any help I can help will be greatly appreciated. Thanks in advance, and please let me know if I left some necessary information out.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 11-Feb-2022 10:27:20   

It looks like you pass a TDL template to the .lpt template engine, could that be the case? TDL templates have to be in .template files, and .lpt files have to be in .lpt files. They have different task performers (code generators). It might help if you attach the preset file you're using, and if you have these: custom task definitions and the templatebindings file, so we can see which task performer is executed for the template.

.lpt templates use <% %> and use C# as template language and are compiled at generation time to generate output. TDL templates use <[ ]> for statement markers and are interpreted. See the SDK docs for more info: https://www.llblgen.com/Documentation/5.9/SDK/GeneratorandTasks.htm

Frans Bouma | Lead developer LLBLGen Pro
WilliamB
User
Posts: 17
Joined: 10-Feb-2022
# Posted on: 11-Feb-2022 16:21:02   

Otis wrote:

It looks like you pass a TDL template to the .lpt template engine, could that be the case? TDL templates have to be in .template files, and .lpt files have to be in .lpt files. They have different task performers (code generators). It might help if you attach the preset file you're using, and if you have these: custom task definitions and the templatebindings file, so we can see which task performer is executed for the template.

.lpt templates use <% %> and use C# as template language and are compiled at generation time to generate output. TDL templates use <[ ]> for statement markers and are interpreted. See the SDK docs for more info: https://www.llblgen.com/Documentation/5.9/SDK/GeneratorandTasks.htm

Thanks for your quick response and your time to read a post with that many lines.

I went through all the template files and there is no mismatch between the format of the content and the filename extension. These set of preset, custom templates and templatebinding is used by other dev team with LLBLgen v3.5 and works flawlessly.

I noticed though that some TDL templates are including LPT templates. I don't know if that cannot be done in LLBLGen 5.8.

Anyway, does anything else comes to your mind that could be causing this behavior?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 12-Feb-2022 09:51:23   

You can include lpt templates in TDL template, but not the other way around. Please attach your preset / custom templates / custom template bindings / custom task definitions (if any) so we can have a look. There is a template that contains TDL which is executed using the lpt task performer, as the <[ ]> statement marker in TDL is in .lpt a namespace declaration https://www.llblgen.com/Documentation/5.9/SDK/templates_lpt.htm#namespace-reference-declarations which is what you see with the using if... at the top of the generated lpt source code. Lpt templates are generated to a piece of code which writes the output and which is compiled, hence the code generator first generates a big file with all the templates, and when this file doesn't compile it's caused by an error in the .lpt template (e.g. you forgot a ; in a statement between <% and %>.

Frans Bouma | Lead developer LLBLGen Pro
WilliamB
User
Posts: 17
Joined: 10-Feb-2022
# Posted on: 17-Feb-2022 00:11:38   

Thanks again. I would like to answer sooner but I have been out of office for several days.

I found something curious: the template typedViews shipped with LLBLGen version 3.5 is a TDL template, but for version 5.8 is a TPL template. and among the custom templates from the former LLBLGen 3.5 project there was a typedView.template, its content identical to the template included with the installation(Shared folder). The template-binding file register this "custom template" with id: SD_TypedViewTemplate.

So, for my case, since I am using LLBLGen 5.8 and this versions comes with a template with the same name but defined as a LPT template, I just deleted the custom TDL template and modified the binding to point to the LPT template and changed its language attribute from TDL to C#. (See below)

This change allowed me to generate without compiling errors, but for some reason the output code for typedViews is not right: it looks like the LPT parser/compiler is not processing this template.

This is the modificationI made to the template-binding file. <templateBinding templateID="SD_TypedViewTemplate" filename="SharedTemplates\Net3.5\C#\typedView.lpt" templateLanguage="C#" />

This is a fragment of one of the generated typedView classes.

<%
    Project currentProject = _executingGenerator.ProjectDefinition;
    var typedView = _activeObject as TypedViewDefinition;
    var mapping = currentProject.GetGroupableModelElementMapping(typedView, _executingGenerator.DriverID);
    bool mappedOnTvfCallResultset = false;
    bool mappedOnTableView = false;
    bool mappedOnProcResultset = false;
    if(mapping == null)
    {
        mapping = currentProject.GetGroupableModelElementMapping(typedView, _executingGenerator.DriverID, true);
        mappedOnTvfCallResultset = true;
    }
    else
    {
        mappedOnTableView = ((mapping.MappedTarget.ElementType==ProjectElementMapTargetElementType.Table) || 
                                (mapping.MappedTarget.ElementType==ProjectElementMapTargetElementType.View));
        mappedOnProcResultset = (mapping.MappedTarget.ElementType==ProjectElementMapTargetElementType.StoredProcedureResultset);
    }
    List<FieldElement> fields = null;
    if(typedView.Fields.HasFieldOrdering && mappedOnTableView)
    {
        fields = typedView.Fields.ApplyFieldOrdering().ToList();
    }
    else
    {
        // grab fields from mapping and sort on ordinal, as ordering IS important (on ordinal of target) as it otherwise goes wrong with oracle cursors.
        fields = mapping.FieldMappings.OrderBy(fm => fm.MappedTarget.OrdinalPosition).Select(fm => fm.MappedFieldInstance)
                                                                                                        .Cast<FieldElement>().ToList();
    }
%>//////////////////////////////////////////////////////////////
// <auto-generated>This code was generated by LLBLGen Pro v<%=ApplicationConstants.LLBLGenProVersion%>.</auto-generated>
//////////////////////////////////////////////////////////////
// Code is generated on: <% if(currentProject.GetRealBoolSettingValue("TdlEmitTimeDateInOutputFiles")) {%><%=DateTime.Now.ToString("F")%><%}%>
// Code is generated using templates: <%=_executingGenerator.GetTemplateBindingsName(_templateID, _templateBindingDefinitionName)%>
// Templates vendor: Solutions Design.
//////////////////////////////////////////////////////////////
using System;
using System.ComponentModel;
using System.Data;
using System.Collections;
using System.Runtime.Serialization;
using <%=_executingGenerator.RootNamespaceToUse%>.HelperClasses;
using <%=_executingGenerator.RootNamespaceToUse%>.FactoryClasses;<% if(mappedOnProcResultset) {%>
using <%=_executingGenerator.RootNamespaceToUse%>.StoredProcedureCallerClasses;<% } %>
using SD.LLBLGen.Pro.ORMSupportClasses;
<%
    if(mappedOnTvfCallResultset)
    {
%>using <%=_executingGenerator.RootNamespaceToUse%>.RelationClasses;
<%  }

What do you think I could have done wrong now?

Thanks in advance for all the help.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 17-Feb-2022 10:41:12   

The template for selfservicing changed from TDL to .lpt indeed as we needed change things that wasn't expressible in TDL very clearly without adding code to the TDL parser. The binding is ok. I think you should look at the preset you're executing, and check if for the TypedViewClasses task, the CodeEmitter is still defined as the task performer. This is the TDL code generator. It should be DotNetTemplateEngine, like in the SD.Presets.SelfServicing.General preset that we ship with the designer.

It's likely the author of the custom templates also created a custom preset file. This is fine btw, you have to change some xml to change this. If you can't figure it out, please attach the preset (and if there are custom tasks, the task files too).

Frans Bouma | Lead developer LLBLGen Pro
WilliamB
User
Posts: 17
Joined: 10-Feb-2022
# Posted on: 17-Feb-2022 18:44:48   

Thank you Otis,

The preset I was using was referencing SD.Tasks.Generics.TypedViewClassGenerator (task performer: CodEmitter). I changed that to SD.Tasks.SelfServicing.TypedViewClassGenerator and it worked.

Problem has been solved.