diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000..11aff852f --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,58 @@ +name: docs + +on: + push: + branches: [ "dev" ] + paths: + - 'src/**' + - 'docfx/**' + - '.github/workflows/docs.yml' + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: [self-hosted, Windows, X64, L2, AX] + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '10.0.x' + + - name: Install DocFX + run: dotnet tool restore || dotnet tool install docfx --global + + - name: Build solution + run: .\build.ps1 + + - name: Generate documentation + run: dotnet docfx docfx/docfx.json + + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/CODEOWNERS b/CODEOWNERS index 29f65faf2..0ed7f4641 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1,2 @@ -@MTS spol. s r.o. @TomKovac +@TomKovac +@PTKu diff --git a/docfx/api/index.md b/docfx/api/index.md index 42492181c..3aa79e4ba 100644 --- a/docfx/api/index.md +++ b/docfx/api/index.md @@ -1,3 +1,12 @@ # AXSharp API Documentation -![](~/images/banner.png) \ No newline at end of file +![](~/images/banner.png) + +Browse the auto-generated API reference for all AX# packages: + +- **AXSharp.Connector** — core connectivity layer, twin objects, and data exchange primitives +- **AXSharp.Compiler** — twin compiler (ixc) internals and code generation +- **AXSharp.Blazor** — Blazor UI rendering components and auto-generated views +- **AXSharp.Abstractions** — shared interfaces and presentation attributes + +Use the table of contents on the left to navigate namespaces and types. \ No newline at end of file diff --git a/docfx/articles/compiler/CONFIG_FILE.MD b/docfx/articles/compiler/CONFIG_FILE.MD index 9eb158280..b6f1adedd 100644 --- a/docfx/articles/compiler/CONFIG_FILE.MD +++ b/docfx/articles/compiler/CONFIG_FILE.MD @@ -1,29 +1,53 @@ # Ixc config file -**AXSharp.config.json** can contain some settings for the compiler. Upon the first run of the ixc the default `AXSharp.config.json` file is created if not previoulsy created manually. - -Some compilation options can be overrided by CLI, such as `OutputProjectFolder` (-o) will override `OutputProjectFolder` setting in the config file. +**AXSharp.config.json** can contain settings for the compiler. Upon the first run of `ixc` a default `AXSharp.config.json` file is created if one does not already exist. +Some options can be overridden from the CLI. For example, the `-o` flag overrides `OutputProjectFolder`. **Default config file** ~~~json { - "OutputProjectFolder":"ix", + "OutputProjectFolder": "ix" } ~~~ -**Config file example** +**Config file example (library with UI companion)** ~~~json { - "OutputProjectFolder":"..\\ix-app" + "OutputProjectFolder": "../ix", + "UiHostProject": "../blazorapp/blazorapp.csproj" } ~~~ -| Parameter | Meaning | Default | -| ------------------- | ------------------------------------------------------------------------------------------------------ | ------- | -| OutputProjectFolder | Sets the directory where the ixc will emmit the twin project.Use path relative to apax project folder. | 'ix' | -| | The value is overridable from the CLI | | +## Parameters + +| Parameter | CLI flag | Meaning | Default | +| --- | --- | --- | --- | +| `OutputProjectFolder` | `-o` | Directory where `ixc` emits the twin project. Path relative to the AX project folder. | `ix` | +| `ProjectFile` | `-p` | Name of the output `.csproj` file. | *(project name)* | +| `UseBase` | `-b` | Use `$base` for base types in inherited classes. Obsolete. | `false` | +| `NoDependencyUpdate` | `-u` | Prevent dependent twin packages from being installed via apax. | `false` | +| `IgnoreS7Pragmas` | `-s` | Ignore `S7.extern` pragmas; compile all members regardless of communication settings. | `false` | +| `SkipDependencyCompilation` | `-d` | Skip compilation of referenced AX# dependency projects. | `false` | +| `TargetPlatfromMoniker` | `-t` | Target platform: `ax` or `tia`. | `ax` | +| `UiHostProject` | `-a` | Path (relative to the AX project folder or absolute) of the `.csproj` that hosts or consumes UI companion NuGet packages. In library development this is the Blazor/UI application project; in application development this is the application project itself. When set, `ixc` automatically installs UI companion package references into this project. | *(none)* | + +## UI companion packages + +When a PLC library ships a UI companion package (Blazor components, auto-generated views, etc.), `ixc` can automatically wire up the dependency for you. + +**Library author** — set `UiHostProject` in the library's `AXSharp.config.json` to the path of the UI project that is built alongside the twin project. When `ixc` runs it will: +1. Determine the UI package ID from the `` element in the referenced `.csproj`, or fall back to `.UI` when the file does not exist. +2. Write that ID and the library version into `axsharp.companion.json` alongside the usual twin connector fields. +**Consumer** — set `UiHostProject` in your project's `AXSharp.config.json` to point at the application project (e.g. a Blazor server app) that should receive the dependency. When `ixc` resolves a dependency that carries UI companion info, it automatically adds the corresponding NuGet package reference (or project reference when working directly from source) to the configured host project. + +~~~json +{ + "OutputProjectFolder": "../ix", + "UiHostProject": "../MyBlazorApp/MyBlazorApp.csproj" +} +~~~ diff --git a/docfx/articles/compiler/PACKAGING.md b/docfx/articles/compiler/PACKAGING.md index 6af6b6f80..586f422ea 100644 --- a/docfx/articles/compiler/PACKAGING.md +++ b/docfx/articles/compiler/PACKAGING.md @@ -25,4 +25,53 @@ Set the files in the `.meta` folder to build action to `Content`. ## Versioning > **Important** -> The APAX package and respective Twin NuGet package must be released with the same version number. APAX package and NuGet package with the same version number are considered aligned. \ No newline at end of file +> The APAX package and respective Twin NuGet package must be released with the same version number. APAX package and NuGet package with the same version number are considered aligned. + +## UI companion packages + +A PLC library can ship a UI companion package alongside the twin connector — for example a set of auto-generated Blazor components. `ixc` handles the bookkeeping automatically via `axsharp.companion.json`. + +### Library author + +Set `UiHostProject` in your library's `AXSharp.config.json` to the path of the UI `.csproj` within your repository: + +~~~json +{ + "OutputProjectFolder": "../ix", + "UiHostProject": "../MyLib.UI/MyLib.UI.csproj" +} +~~~ + +When `ixc` compiles the library it: + +1. Reads the `` from the UI project file (falls back to `.UI` if the file is absent). +2. Writes `UiId` and `UiVersion` into `axsharp.companion.json` next to the existing twin connector fields. + +~~~json +{ + "Id": "MyLib.Twin", + "Version": "1.2.3", + "UiId": "MyLib.UI", + "UiVersion": "1.2.3" +} +~~~ + +> The UI package version always matches the library version — publish both together. + +### Consumer + +Set `UiHostProject` in your application's `AXSharp.config.json` to the Blazor or UI host project that should receive the dependency: + +~~~json +{ + "OutputProjectFolder": "ix", + "UiHostProject": "../MyBlazorApp/MyBlazorApp.csproj" +} +~~~ + +When `ixc` resolves a dependency whose `axsharp.companion.json` contains `UiId`/`UiVersion`, it automatically: + +- Adds a **NuGet package reference** to `UiHostProject` when the dependency is consumed as a NuGet package. +- Adds a **project reference** to `UiHostProject` when the dependency is consumed directly from source (i.e. the dependency has its own `AXSharp.config.json` with `UiHostProject` set). + +If `UiHostProject` is not configured, the UI reference step is skipped and an informational message is logged. \ No newline at end of file diff --git a/docfx/articles/connectors/README.md b/docfx/articles/connectors/README.md index 3632a85c2..5c8a9ec0d 100644 --- a/docfx/articles/connectors/README.md +++ b/docfx/articles/connectors/README.md @@ -78,8 +78,8 @@ During the batched read operation, the values are stored in *LastValue* property During a batched write operation, the values written to the controller were retrieved from the *Cyclic* property of the corresponding Primitive Twin. -> **NOTE** -> Accessing *Cyclic* property for **writing** will not result in an autmatic subscription for cyclic reading. Acessing the *LastValue* property will neither result in an automatic subcription for reading. +> **NOTE** +> Accessing *Cyclic* property for **writing** will not result in an automatic subscription for cyclic reading. Accessing the *LastValue* property will neither result in an automatic subscription for reading. ~~~ C# // in this namespace are extension methods for batched operations. @@ -93,11 +93,11 @@ public class BatchedAccess await Entry.PlcTwin.Settings.ReadAsync(); // Write values to the console - Console.WriteLine($"{Entry.PlcTwin.Settings.PosX.Symbol}:{Entry.PlcTwin.MAIN.Settings.PosX.LastValue}); + Console.WriteLine($"{Entry.PlcTwin.Settings.PosX.Symbol}:{Entry.PlcTwin.Settings.PosX.LastValue}"); - Console.WriteLine($"{Entry.PlcTwin.Settings.PosY.Symbol}:{Entry.PlcTwin.Settings.PosY.LastValue}); + Console.WriteLine($"{Entry.PlcTwin.Settings.PosY.Symbol}:{Entry.PlcTwin.Settings.PosY.LastValue}"); - Console.WriteLine($"{Entry.PlcTwin.Settings.PosZ.Symbol}:{Entry.PlcTwin.Settings.PosZ.LastValue}); + Console.WriteLine($"{Entry.PlcTwin.Settings.PosZ.Symbol}:{Entry.PlcTwin.Settings.PosZ.LastValue}"); } @@ -108,11 +108,13 @@ public class BatchedAccess Entry.PlcTwin.Settings.PosZ.Cyclic = 130.0f; // Writes all values of the settings structure. - await Entry.PlcController.MAIN.Settings.WriteAsynch(); + await Entry.PlcTwin.Settings.WriteAsync(); } } ~~~ +## Polling + Polling allows an application to query a structure or variable at different intervals. The values are stored in the `Cyclic` property of value types. The polled variables are retrieved from the controller in the same way as during cyclic reading but at a specified interval. Unlike automatic subscription mode, the polled values can be unsubscribed. To enable polling, the subscription of the twin connector must be set to `Polling`. @@ -256,7 +258,7 @@ _length : REAL; ~~~ ~~~ C# -// Writes unit of the '_lenght' variable to the console. +// Writes unit of the '_length' variable to the console. System.Console.WriteLine(PlcTwin._app._length.AttributeUnits); ~~~ @@ -334,10 +336,10 @@ VAR someWString : WSTRING; END_VAR -someString := '<#This woule be localized#> and this would stay as it is'; +someString := '<#This would be localized#> and this would stay as it is'; -someWString := "<#This woule be localized#> and this would stay as it is"; +someWString := "<#This would be localized#> and this would stay as it is"; ~~~ Connectors implement features that allow localizing of the texts (controller defined and added attributes of string type). For the localization to work the twin assembly must be provided with a resource file (*.resx). Resource files can be generated using [ixr tool](~/articles/ixr/IXR.md). You will need to add the resource file to your **Twin project** and set the resource code generation to *public*. diff --git a/docfx/articles/connectors/WebAPI.md b/docfx/articles/connectors/WebAPI.md index bc245e716..d5dc8dc4b 100644 --- a/docfx/articles/connectors/WebAPI.md +++ b/docfx/articles/connectors/WebAPI.md @@ -23,7 +23,7 @@ Here is an example on how to set polling subscription mode: Entry.Plc.Connector.SubscriptionMode = AXSharp.Connector.ReadSubscriptionMode.Polling; ``` -For more details about polling within your components, see [Polling Data from the Controller in Components](../blazor/LIBRARIES.md#polling-data-from-the-plc) and [General Polling](README.md#polling). +For more details about polling within your components, see [Polling Data from the Controller in Components](../blazor/LIBRARIES.md#optimizing-plc-data-polling) and [General Polling](README.md#polling). ### Monitoring and Logging diff --git a/docfx/articles/intro.md b/docfx/articles/intro.md index d6574e1ca..462b8cdc2 100644 --- a/docfx/articles/intro.md +++ b/docfx/articles/intro.md @@ -9,7 +9,7 @@ * [Config file](~/articles/compiler/CONFIG_FILE.md) * [Packaging and dependencies](~/articles/compiler/PACKAGING.md) --- -* [Documenation compiler](~/articles/ixd/IXD.md) +* [Documentation compiler](~/articles/ixd/IXD.md) --- * [Resx compiler](~/articles/ixr/IXR.md) --- diff --git a/docfx/articles/toc.yml b/docfx/articles/toc.yml index cfdc35c97..c98b603d8 100644 --- a/docfx/articles/toc.yml +++ b/docfx/articles/toc.yml @@ -18,10 +18,12 @@ - name: Packaging and dependencies href: ~/articles/compiler/PACKAGING.md -- name: Documenation compiler +- name: Documentation compiler href: ~/articles/ixd/IXD.md - name: Localizables compiler href: ~/articles/ixr/IXR.md +- name: Localizables to Resx + href: ~/articles/ltr/LTR.md - name: Connectors href: ~/articles/connectors/README.md items: @@ -38,4 +40,6 @@ href: ~/articles/blazor/LIBRARIES.md - name: Blazor auto-rendering href: ~/articles/blazor/RENDERABLECONTENT.md +- name: Coding style + href: ~/articles/general/CODING_STYLE.md diff --git a/docfx/docfx.json b/docfx/docfx.json index cf561e8f8..d46ee19b0 100644 --- a/docfx/docfx.json +++ b/docfx/docfx.json @@ -26,16 +26,16 @@ ], "build": { "globalMetadata" : { - "_appTitle" : {"api/index.md" : "API"}, + "_appTitle" : "AX#", "_appName" : "AX#", - "_appFooter" : " Generated by DocFx. © MTS spol. s r.o., MTS spol. s r.o., and awesome contributors", + "_appFooter" : " Generated by DocFx. © MTS spol. s r.o. and awesome contributors", "_enableSearch" : true, "_appFaviconPath" : "images/favicon.ico" }, "sitemap": { "baseUrl": "https://inxton.github.io/axsharp/", - "priority": 0.1, + "priority": 0.5, "changefreq": "weekly" }, @@ -65,17 +65,7 @@ ] } ], - "overwrite": [ - { - "files": [ - "apidoc/**.md" - ], - "exclude": [ - "obj/**", - "_site/**" - ] - } - ], + "overwrite": [], "dest": "../docs", "globalMetadataFiles": [], "fileMetadataFiles": [], diff --git a/docfx/index.md b/docfx/index.md index 5fd7c2a64..c36287f00 100644 --- a/docfx/index.md +++ b/docfx/index.md @@ -36,13 +36,9 @@ The presentations provide a mechanism for automated UI generation from PLC code. ## Prerequisites - [axcode](https://axcite.me) -- [dotnet 6](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) -- [dotnet 7](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) -- [dotnet 7](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) +- [dotnet 8](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) - [vscode](https://code.visualstudio.com/Download) or [vs2022](https://visualstudio.microsoft.com/vs/) -> **Make sure you have installed both .NET6 and .NET7.** - ## Getting started Make sure you meet all [prerequisites](#prerequisites). @@ -66,7 +62,7 @@ There are different ways you can use to create new project. At this point we sup **IMPORTANT! Prepare your PLC and AX project** -*Before you start using any of the method below yoiu will need to set up your PLC. Using TIA portal you need to enable WebAPI interface [see here](https://console.simatic-ax.siemens.io/docs/hwld/PlcWebServer) and [here](https://youtu.be/d9EX2FixY1A?t=151) is a very informative youtube video.* +*Before you start using any of the methods below you will need to set up your PLC. Using TIA portal you need to enable WebAPI interface [see here](https://console.simatic-ax.siemens.io/docs/hwld/PlcWebServer) and [here](https://youtu.be/d9EX2FixY1A?t=151) is a very informative youtube video.* [!Video https://youtu.be/d9EX2FixY1A?t=151] @@ -75,15 +71,9 @@ There are different ways you can use to create new project. At this point we sup Install AX# template package -When using .NET7 ~~~ dotnet new install AXSharp.templates ~~~ - -When using .NET6 and earlier -~~~ -dotnet new --install AXSharp.templates -~~~ > [!NOTE] > Make sure all nuget feed sources are available at the time of installation of packages. If you are not sure run `dotnet nuget list source` and check that the sources listed are reachable. @@ -107,11 +97,11 @@ Create new project from template ~~~ dotnet new [shortname] -n YOUR_PROJECT_NAME -#e.g. dotnet new axeblazor -n MyFristAXSharpBlazorProject +#e.g. dotnet new axeblazor -n MyFirstAXSharpBlazorProject ~~~ **When prompted about script execution allow the script to run (answer (Y)es) in order to finish the scaffolding of the project.** -If you're creating the project using Visual Studion you will need to run the script manually. +If you're creating the project using Visual Studio you will need to run the script manually. Consult README.md file located in your new project for additional information. @@ -168,7 +158,7 @@ Copyright (C) 2022 author --version Display version information. ~~~ -Before usage apax commanad ensure that you are logged in +Before usage apax command ensure that you are logged in ~~~ apax login ~~~ diff --git a/src/AXSharp.blazor/tests/sandbox/ax-blazor-example/AXSharp.config.json b/src/AXSharp.blazor/tests/sandbox/ax-blazor-example/AXSharp.config.json index d4aa97a22..61ddd4500 100644 --- a/src/AXSharp.blazor/tests/sandbox/ax-blazor-example/AXSharp.config.json +++ b/src/AXSharp.blazor/tests/sandbox/ax-blazor-example/AXSharp.config.json @@ -1 +1 @@ -{"OutputProjectFolder":"ix","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"ax","ProjectFile":"ax_blazor_example.csproj"} \ No newline at end of file +{"OutputProjectFolder":"ix","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"ax","ProjectFile":"ax_blazor_example.csproj","UiHostProject":null} \ No newline at end of file diff --git a/src/AXSharp.compiler/src/AXSharp.Compiler.Abstractions/ICompilerOptions.cs b/src/AXSharp.compiler/src/AXSharp.Compiler.Abstractions/ICompilerOptions.cs index 9c751c257..07ca8c912 100644 --- a/src/AXSharp.compiler/src/AXSharp.Compiler.Abstractions/ICompilerOptions.cs +++ b/src/AXSharp.compiler/src/AXSharp.Compiler.Abstractions/ICompilerOptions.cs @@ -23,4 +23,12 @@ public interface ICompilerOptions /// Provides target platform moniker to instruct the compiler about target specific options. /// string TargetPlatfromMoniker { get; set; } + + /// + /// Path (relative to the AX project folder or absolute) of the .csproj that hosts/consumes + /// UI companion NuGet packages. In library development this is the Blazor/UI application project; + /// in application development this is the application project itself. + /// + string? UiHostProject { get; set; } + } \ No newline at end of file diff --git a/src/AXSharp.compiler/src/AXSharp.Compiler/AXSharpConfig.cs b/src/AXSharp.compiler/src/AXSharp.Compiler/AXSharpConfig.cs index af2e175d8..50d060a89 100644 --- a/src/AXSharp.compiler/src/AXSharp.Compiler/AXSharpConfig.cs +++ b/src/AXSharp.compiler/src/AXSharp.Compiler/AXSharpConfig.cs @@ -63,6 +63,14 @@ public string OutputProjectFolder /// public string? ProjectFile { get; set; } + /// + /// Path (relative to the AX project folder or absolute) of the .csproj that hosts/consumes + /// UI companion NuGet packages. In library development this is the Blazor/UI application project; + /// in application development this is the application project itself. + /// + public string? UiHostProject { get; set; } + + private string _axProjectFolder; /// @@ -200,5 +208,8 @@ private static void OverridesFromCli(ICompilerOptions fromConfig, ICompilerOptio fromConfig.IgnoreS7Pragmas = newCompilerOptions.IgnoreS7Pragmas; fromConfig.SkipDependencyCompilation = newCompilerOptions.SkipDependencyCompilation; fromConfig.TargetPlatfromMoniker = newCompilerOptions.TargetPlatfromMoniker; + fromConfig.UiHostProject = string.IsNullOrEmpty(newCompilerOptions.UiHostProject) + ? fromConfig.UiHostProject + : newCompilerOptions.UiHostProject; } } \ No newline at end of file diff --git a/src/AXSharp.compiler/src/AXSharp.Compiler/CompanionInfo.cs b/src/AXSharp.compiler/src/AXSharp.Compiler/CompanionInfo.cs index b4dc934fa..e74efd420 100644 --- a/src/AXSharp.compiler/src/AXSharp.Compiler/CompanionInfo.cs +++ b/src/AXSharp.compiler/src/AXSharp.Compiler/CompanionInfo.cs @@ -1,61 +1,56 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Newtonsoft.Json; +namespace AXSharp.Compiler +{ + public class CompanionInfo + { + public const string COMPANIONS_FILE_NAME = "axsharp.companion.json"; + public string Id { get; set; } + public string Version { get; set; } - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using Newtonsoft.Json; + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string? UiId { get; set; } - namespace AXSharp.Compiler - { - public class CompanionInfo + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string? UiVersion { get; set; } + + private static string ToJson(CompanionInfo info) { + return JsonConvert.SerializeObject(info); + } - public const string COMPANIONS_FILE_NAME = "axsharp.companion.json"; - - public string Id { get; set; } - public string Version { get; set; } - - private static string ToJson(CompanionInfo info) - { - return JsonConvert.SerializeObject(info); - } - - private static CompanionInfo FromJson(string json) - { - return JsonConvert.DeserializeObject(json); - } - - // Serialize the object to a file - public static void ToFile(CompanionInfo info, string filePath) - { - var json = ToJson(info); - File.WriteAllText(filePath, json); - } - - // Deserialize the object from a file - public static CompanionInfo? FromFile(string filePath) - { - var json = File.ReadAllText(filePath); - return FromJson(json); - } - - public static CompanionInfo? TryFromFile(string filePath) - { - if (!File.Exists(filePath)) - return null; - - var json = File.ReadAllText(filePath); - return FromJson(json); - } - } - } + private static CompanionInfo FromJson(string json) + { + return JsonConvert.DeserializeObject(json); + } + // Serialize the object to a file + public static void ToFile(CompanionInfo info, string filePath) + { + var json = ToJson(info); + File.WriteAllText(filePath, json); + } + + // Deserialize the object from a file + public static CompanionInfo? FromFile(string filePath) + { + var json = File.ReadAllText(filePath); + return FromJson(json); + } + public static CompanionInfo? TryFromFile(string filePath) + { + if (!File.Exists(filePath)) + return null; + + var json = File.ReadAllText(filePath); + return FromJson(json); + } + } +} diff --git a/src/AXSharp.compiler/src/AXSharp.Cs.Compiler/CsProject.cs b/src/AXSharp.compiler/src/AXSharp.Cs.Compiler/CsProject.cs index 0d1cceb9f..bf1d69677 100644 --- a/src/AXSharp.compiler/src/AXSharp.Cs.Compiler/CsProject.cs +++ b/src/AXSharp.compiler/src/AXSharp.Cs.Compiler/CsProject.cs @@ -385,10 +385,56 @@ public void InstallAXSharpDependencies(IEnumerable dependencies) { case CompanionInfo package: AddNuGetPackageReference(dependent, package.Id, package.Version); + + if (!string.IsNullOrEmpty(package.UiId) && !string.IsNullOrEmpty(package.UiVersion)) + { + var targetApp = this.AxSharpProject.CompilerOptions?.UiHostProject; + if (!string.IsNullOrEmpty(targetApp)) + { + var resolvedTargetApp = Path.IsPathRooted(targetApp) + ? targetApp + : Path.GetFullPath(Path.Combine(this.AxSharpProject.AxProject.ProjectFolder, targetApp)); + + if (File.Exists(resolvedTargetApp)) + AddNuGetPackageReference(resolvedTargetApp, package.UiId, package.UiVersion); + else + Log.Logger.Warning($"UiHostProject '{resolvedTargetApp}' not found. UI package '{package.UiId}' was not installed."); + } + else + { + Log.Logger.Information($"Companion '{package.Id}' has UI package '{package.UiId}' but no UiHostProject is configured. Skipping UI package installation."); + } + } break; case AXSharpConfig project: var projectPath = Path.GetFullPath(Path.Combine(project.AxProjectFolder, project.OutputProjectFolder, project.ProjectFile)); AddProjectReference(dependent, GetRelativePath(dependent, projectPath)); + + if (!string.IsNullOrEmpty(project.UiHostProject)) + { + var uiHostProject = this.AxSharpProject.CompilerOptions?.UiHostProject; + if (!string.IsNullOrEmpty(uiHostProject)) + { + var resolvedUiHost = Path.IsPathRooted(uiHostProject) + ? uiHostProject + : Path.GetFullPath(Path.Combine(this.AxSharpProject.AxProject.ProjectFolder, uiHostProject)); + + var resolvedDepUiProject = Path.IsPathRooted(project.UiHostProject) + ? project.UiHostProject + : Path.GetFullPath(Path.Combine(project.AxProjectFolder, project.UiHostProject)); + + if (File.Exists(resolvedUiHost) && File.Exists(resolvedDepUiProject)) + AddProjectReference(resolvedUiHost, GetRelativePath(resolvedUiHost, resolvedDepUiProject)); + else if (!File.Exists(resolvedUiHost)) + Log.Logger.Warning($"UiHostProject '{resolvedUiHost}' not found. UI project reference '{resolvedDepUiProject}' was not installed."); + else + Log.Logger.Warning($"Dependency UI project '{resolvedDepUiProject}' not found. UI project reference was not installed."); + } + else + { + Log.Logger.Information($"Dependency '{project.ProjectFile}' has UI project '{project.UiHostProject}' but no UiHostProject is configured in this project. Skipping UI project reference."); + } + } break; } } @@ -667,6 +713,21 @@ static string GetPackageId(string csprojPath) return Path.GetFileNameWithoutExtension(csprojPath); } + private string? ResolveUiId(string? uiHostProject) + { + if (string.IsNullOrEmpty(uiHostProject)) + return null; + + var resolved = Path.IsPathRooted(uiHostProject) + ? uiHostProject + : Path.GetFullPath(Path.Combine(this.AxSharpProject.AxProject.ProjectFolder, uiHostProject)); + + if (File.Exists(resolved)) + return GetPackageId(resolved); + + return (this.AxSharpProject.AxProject.ProjectInfo.Name ?? Path.GetFileNameWithoutExtension(resolved)) + ".UI"; + } + public void GenerateCompanionData() { var compilerOptions = this.AxSharpProject.CompilerOptions; @@ -677,7 +738,17 @@ public void GenerateCompanionData() var packageId = GetPackageId(Path.Combine(this.AxSharpProject.OutputFolder, compilerOptions.ProjectFile)); - CompanionInfo.ToFile(new CompanionInfo() { Id = packageId, Version = this.AxSharpProject.AxProject.ProjectInfo.Version }, Path.Combine(this.AxSharpProject.AxProject.ProjectFolder, CompanionInfo.COMPANIONS_FILE_NAME)); + var version = this.AxSharpProject.AxProject.ProjectInfo.Version ?? string.Empty; + var uiId = ResolveUiId(compilerOptions.UiHostProject); + CompanionInfo.ToFile( + new CompanionInfo() + { + Id = packageId, + Version = version, + UiId = uiId, + UiVersion = uiId != null ? version : null + }, + Path.Combine(this.AxSharpProject.AxProject.ProjectFolder, CompanionInfo.COMPANIONS_FILE_NAME)); } } } diff --git a/src/AXSharp.compiler/src/ixc/Options.cs b/src/AXSharp.compiler/src/ixc/Options.cs index b9816a75a..55066b798 100644 --- a/src/AXSharp.compiler/src/ixc/Options.cs +++ b/src/AXSharp.compiler/src/ixc/Options.cs @@ -50,5 +50,10 @@ internal class Options : ICompilerOptions [Option('v', "verbosity", Required = false, Default = LogEventLevel.Information, HelpText = "Level of compiler output. Possible options Verbose, Debug, Information, Warning, Error, Fatal")] public LogEventLevel Versbosity { get; set; } + + [Option('a', "ui-host-project", Required = false, Default = null, + HelpText = "Path (relative to AX project folder or absolute) of the .csproj that hosts/consumes UI companion NuGet packages. In library development this is the Blazor/UI application; in application development this is the application project itself.")] + public string? UiHostProject { get; set; } + } diff --git a/src/AXSharp.compiler/src/ixd/Options.cs b/src/AXSharp.compiler/src/ixd/Options.cs index 1dd4e44c3..fd60b7d0b 100644 --- a/src/AXSharp.compiler/src/ixd/Options.cs +++ b/src/AXSharp.compiler/src/ixd/Options.cs @@ -37,5 +37,7 @@ internal class Options : ICompilerOptions [Option('t', "target-platform-moniker", Required = false, Default = "ax", HelpText = "Instructs the compiler to adjust for target platform differences. Possible values 'ax', 'tia'")] public string TargetPlatfromMoniker { get; set; } + + public string? UiHostProject { get; set; } } } diff --git a/src/AXSharp.compiler/src/ixr/Options.cs b/src/AXSharp.compiler/src/ixr/Options.cs index 2caf2d1ca..67b42c5ba 100644 --- a/src/AXSharp.compiler/src/ixr/Options.cs +++ b/src/AXSharp.compiler/src/ixr/Options.cs @@ -36,5 +36,7 @@ internal class Options : ICompilerOptions [Option('t', "target-platform-moniker", Required = false, Default = "ax", HelpText = "Instructs the compiler to adjust for target platform differences. Possible values 'ax', 'tia'")] public string TargetPlatfromMoniker { get; set; } + + public string? UiHostProject { get; set; } } } diff --git a/src/AXSharp.compiler/tests/AXSharp.Compiler.CsTests/Cs/CompilerTestOptions.cs b/src/AXSharp.compiler/tests/AXSharp.Compiler.CsTests/Cs/CompilerTestOptions.cs index 311af751f..077d99f3b 100644 --- a/src/AXSharp.compiler/tests/AXSharp.Compiler.CsTests/Cs/CompilerTestOptions.cs +++ b/src/AXSharp.compiler/tests/AXSharp.Compiler.CsTests/Cs/CompilerTestOptions.cs @@ -28,4 +28,5 @@ public string? OutputProjectFolder public bool IgnoreS7Pragmas { get => false; set { } } public bool SkipDependencyCompilation { get => false; set { } } public string TargetPlatfromMoniker { get; set; } = "ax"; + public string? UiHostProject { get; set; } } \ No newline at end of file diff --git a/src/AXSharp.compiler/tests/AXSharp.CompilerTests/CompanionInfoTests.cs b/src/AXSharp.compiler/tests/AXSharp.CompilerTests/CompanionInfoTests.cs new file mode 100644 index 000000000..e842ef015 --- /dev/null +++ b/src/AXSharp.compiler/tests/AXSharp.CompilerTests/CompanionInfoTests.cs @@ -0,0 +1,158 @@ +// AXSharp.CompilerTests +// Copyright (c) 2023 MTS spol. s r.o., and Contributors. All Rights Reserved. +// Contributors: https://github.com/inxton/axsharp/graphs/contributors +// See the LICENSE file in the repository root for more information. +// https://github.com/inxton/axsharp/blob/dev/LICENSE +// Third party licenses: https://github.com/inxton/axsharp/blob/master/notices.md + +using Xunit; +using AXSharp.Compiler; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace AXSharp.CompilerTests +{ + public class CompanionInfoTests + { + [Fact] + public void CompanionInfo_serializes_UiId_and_UiVersion_when_set() + { + var info = new CompanionInfo { Id = "My.Twin", Version = "1.0.0", UiId = "My.Twin.UI", UiVersion = "1.0.0" }; + var json = JObject.Parse(JsonConvert.SerializeObject(info)); + + Assert.Equal("My.Twin.UI", json["UiId"]?.Value()); + Assert.Equal("1.0.0", json["UiVersion"]?.Value()); + } + + [Fact] + public void CompanionInfo_omits_UiId_and_UiVersion_from_json_when_null() + { + var info = new CompanionInfo { Id = "My.Twin", Version = "1.0.0" }; + var json = JObject.Parse(JsonConvert.SerializeObject(info)); + + Assert.False(json.ContainsKey("UiId")); + Assert.False(json.ContainsKey("UiVersion")); + } + + [Fact] + public void CompanionInfo_round_trips_through_file_with_ui_fields() + { + var tempFile = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.json"); + try + { + var original = new CompanionInfo { Id = "My.Twin", Version = "2.3.4", UiId = "My.Twin.UI", UiVersion = "2.3.4" }; + CompanionInfo.ToFile(original, tempFile); + var restored = CompanionInfo.TryFromFile(tempFile); + + Assert.NotNull(restored); + Assert.Equal(original.Id, restored!.Id); + Assert.Equal(original.Version, restored.Version); + Assert.Equal(original.UiId, restored.UiId); + Assert.Equal(original.UiVersion, restored.UiVersion); + } + finally + { + File.Delete(tempFile); + } + } + + [Fact] + public void CompanionInfo_round_trips_through_file_without_ui_fields() + { + var tempFile = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.json"); + try + { + var original = new CompanionInfo { Id = "My.Twin", Version = "1.0.0" }; + CompanionInfo.ToFile(original, tempFile); + var restored = CompanionInfo.TryFromFile(tempFile); + + Assert.NotNull(restored); + Assert.Equal(original.Id, restored!.Id); + Assert.Equal(original.Version, restored.Version); + Assert.Null(restored.UiId); + Assert.Null(restored.UiVersion); + } + finally + { + File.Delete(tempFile); + } + } + + [Fact] + public void CompanionInfo_TryFromFile_returns_null_when_file_missing() + { + var result = CompanionInfo.TryFromFile(Path.Combine(Path.GetTempPath(), "nonexistent_companion.json")); + Assert.Null(result); + } + + [Fact] + public void CompanionInfo_deserializes_legacy_json_without_ui_fields() + { + var tempFile = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.json"); + try + { + // Simulate a legacy axsharp.companion.json that has no UiId/UiVersion + File.WriteAllText(tempFile, "{\"Id\":\"Legacy.Twin\",\"Version\":\"0.9.0\"}"); + var result = CompanionInfo.TryFromFile(tempFile); + + Assert.NotNull(result); + Assert.Equal("Legacy.Twin", result!.Id); + Assert.Equal("0.9.0", result.Version); + Assert.Null(result.UiId); + Assert.Null(result.UiVersion); + } + finally + { + File.Delete(tempFile); + } + } + + [Fact] + public void CompanionInfo_with_UiId_derived_from_csproj_PackageId_element() + { + // Simulate a .csproj that declares a PackageId + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempDir); + var csprojPath = Path.Combine(tempDir, "MyApp.UI.csproj"); + File.WriteAllText(csprojPath, "Acme.App.UI"); + + var companionFile = Path.Combine(tempDir, CompanionInfo.COMPANIONS_FILE_NAME); + try + { + // Build a companion as GenerateCompanionData would when UiHostProject csproj exists + var info = new CompanionInfo { Id = "Acme.Twin", Version = "1.2.3", UiId = "Acme.App.UI", UiVersion = "1.2.3" }; + CompanionInfo.ToFile(info, companionFile); + var restored = CompanionInfo.TryFromFile(companionFile); + + Assert.Equal("Acme.App.UI", restored!.UiId); + Assert.Equal("1.2.3", restored.UiVersion); + } + finally + { + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void CompanionInfo_with_UiId_uses_apax_name_plus_UI_suffix_when_csproj_missing() + { + // When the UiHostProject csproj does not exist, UiId = apaxName + ".UI" + const string apaxName = "my-lib"; + var expectedUiId = apaxName + ".UI"; + + var tempFile = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.json"); + try + { + var info = new CompanionInfo { Id = "my-lib", Version = "0.1.0", UiId = expectedUiId, UiVersion = "0.1.0" }; + CompanionInfo.ToFile(info, tempFile); + var restored = CompanionInfo.TryFromFile(tempFile); + + Assert.Equal(expectedUiId, restored!.UiId); + } + finally + { + File.Delete(tempFile); + } + } + } +} diff --git a/src/AXSharp.compiler/tests/AXSharp.CompilerTests/CompilerTestOptions.cs b/src/AXSharp.compiler/tests/AXSharp.CompilerTests/CompilerTestOptions.cs index 5f70e37e9..f6f5c7f8d 100644 --- a/src/AXSharp.compiler/tests/AXSharp.CompilerTests/CompilerTestOptions.cs +++ b/src/AXSharp.compiler/tests/AXSharp.CompilerTests/CompilerTestOptions.cs @@ -30,4 +30,5 @@ public string? OutputProjectFolder public bool IgnoreS7Pragmas { get => false; set { } } public bool SkipDependencyCompilation { get => false; set { } } public string TargetPlatfromMoniker { get; set; } = "ax"; + public string? UiHostProject { get; set; } } \ No newline at end of file diff --git a/src/AXSharp.compiler/tests/AXSharp.CompilerTests/IxConfigTests.cs b/src/AXSharp.compiler/tests/AXSharp.CompilerTests/IxConfigTests.cs index 94a7ada1e..9db2c3040 100644 --- a/src/AXSharp.compiler/tests/AXSharp.CompilerTests/IxConfigTests.cs +++ b/src/AXSharp.compiler/tests/AXSharp.CompilerTests/IxConfigTests.cs @@ -96,5 +96,74 @@ public void RetrieveIxConfig_should_throw_exception_when_unable_to_process_confi var ixConfigFile = Path.Combine(apaxFolder, "AXSharp.config.json1"); Assert.Throws(() =>AXSharpConfig.RetrieveAXSharpConfig(ixConfigFile)); } + + [Fact] + public void UpdateAndGetAXSharpConfig_should_persist_and_restore_UiHostProject() + { + var apaxFolder = Path.Combine(testFolder, "samples", "plt", "app"); + var ixConfigFile = Path.Combine(apaxFolder, "AXSharp.config.json"); + if (File.Exists(ixConfigFile)) + File.Delete(ixConfigFile); + +#pragma warning disable CS0618 + var written = new AXSharpConfig() +#pragma warning restore CS0618 + { + AxProjectFolder = apaxFolder, + OutputProjectFolder = "ix", + UiHostProject = "../app/app.csproj" + }; + + AXSharpConfig.UpdateAndGetAXSharpConfig(apaxFolder, written); + var result = AXSharpConfig.UpdateAndGetAXSharpConfig(apaxFolder); + + Assert.Equal("../app/app.csproj", result.UiHostProject); + } + + [Fact] + public void OverridesFromCli_should_override_UiHostProject_when_cli_provides_value() + { + var apaxFolder = Path.Combine(testFolder, "samples", "plt", "lib"); + var ixConfigFile = Path.Combine(apaxFolder, "AXSharp.config.json"); + Assert.True(File.Exists(ixConfigFile)); + +#pragma warning disable CS0618 + var cliOptions = new AXSharpConfig() +#pragma warning restore CS0618 + { + AxProjectFolder = apaxFolder, + UiHostProject = "../blazorapp/blazorapp.csproj" + }; + + var result = AXSharpConfig.UpdateAndGetAXSharpConfig(apaxFolder, cliOptions); + + Assert.Equal("../blazorapp/blazorapp.csproj", result.UiHostProject); + } + + [Fact] + public void OverridesFromCli_should_preserve_config_UiHostProject_when_cli_value_is_null() + { + var apaxFolder = Path.Combine(testFolder, "samples", "plt", "app"); + var ixConfigFile = Path.Combine(apaxFolder, "AXSharp.config.json"); + if (File.Exists(ixConfigFile)) + File.Delete(ixConfigFile); + +#pragma warning disable CS0618 + var seed = new AXSharpConfig() +#pragma warning restore CS0618 + { + AxProjectFolder = apaxFolder, + OutputProjectFolder = "ix", + UiHostProject = "../app/app.csproj" + }; + AXSharpConfig.UpdateAndGetAXSharpConfig(apaxFolder, seed); + +#pragma warning disable CS0618 + var cliOptions = new AXSharpConfig() { AxProjectFolder = apaxFolder }; +#pragma warning restore CS0618 + var result = AXSharpConfig.UpdateAndGetAXSharpConfig(apaxFolder, cliOptions); + + Assert.Equal("../app/app.csproj", result.UiHostProject); + } } } \ No newline at end of file diff --git a/src/AXSharp.compiler/tests/integration/actual/app/AXSharp.config.json b/src/AXSharp.compiler/tests/integration/actual/app/AXSharp.config.json index 1414fda43..199218a4d 100644 --- a/src/AXSharp.compiler/tests/integration/actual/app/AXSharp.config.json +++ b/src/AXSharp.compiler/tests/integration/actual/app/AXSharp.config.json @@ -1 +1 @@ -{"OutputProjectFolder":"samples\\units\\ix\\tia","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"tia","ProjectFile":"app.csproj"} \ No newline at end of file +{"OutputProjectFolder":"samples\\units\\ix\\tia","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"tia","ProjectFile":"app.csproj","UiHostProject":null} \ No newline at end of file diff --git a/src/AXSharp.compiler/tests/integration/actual/lib1/AXSharp.config.json b/src/AXSharp.compiler/tests/integration/actual/lib1/AXSharp.config.json index 2e3707c38..0e93f4af8 100644 --- a/src/AXSharp.compiler/tests/integration/actual/lib1/AXSharp.config.json +++ b/src/AXSharp.compiler/tests/integration/actual/lib1/AXSharp.config.json @@ -1 +1 @@ -{"OutputProjectFolder":"samples\\units\\ix\\tia","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"tia","ProjectFile":"lib1.csproj"} \ No newline at end of file +{"OutputProjectFolder":"samples\\units\\ix\\tia","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"tia","ProjectFile":"lib1.csproj","UiHostProject":null} \ No newline at end of file diff --git a/src/AXSharp.compiler/tests/integration/actual/lib2/AXSharp.config.json b/src/AXSharp.compiler/tests/integration/actual/lib2/AXSharp.config.json index d95071d21..60710e792 100644 --- a/src/AXSharp.compiler/tests/integration/actual/lib2/AXSharp.config.json +++ b/src/AXSharp.compiler/tests/integration/actual/lib2/AXSharp.config.json @@ -1 +1 @@ -{"OutputProjectFolder":"samples\\units\\ix\\tia","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"tia","ProjectFile":"lib2.csproj"} \ No newline at end of file +{"OutputProjectFolder":"samples\\units\\ix\\tia","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"tia","ProjectFile":"lib2.csproj","UiHostProject":null} \ No newline at end of file diff --git a/src/AXSharp.connectors/tests/ax-test-project/AXSharp.config.json b/src/AXSharp.connectors/tests/ax-test-project/AXSharp.config.json index a743b7860..d9ec71863 100644 --- a/src/AXSharp.connectors/tests/ax-test-project/AXSharp.config.json +++ b/src/AXSharp.connectors/tests/ax-test-project/AXSharp.config.json @@ -1 +1 @@ -{"OutputProjectFolder":"ix","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"ax","ProjectFile":"ax_test_project.csproj"} \ No newline at end of file +{"OutputProjectFolder":"ix","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"ax","ProjectFile":"ax_test_project.csproj","UiHostProject":null} \ No newline at end of file diff --git a/src/AXSharp.examples/hello.world.console/hello.world.console.plc/AXSharp.config.json b/src/AXSharp.examples/hello.world.console/hello.world.console.plc/AXSharp.config.json index 1f8b32355..bf6484090 100644 --- a/src/AXSharp.examples/hello.world.console/hello.world.console.plc/AXSharp.config.json +++ b/src/AXSharp.examples/hello.world.console/hello.world.console.plc/AXSharp.config.json @@ -1 +1 @@ -{"OutputProjectFolder":"ix","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"ax","ProjectFile":"hello_world_console_plc.csproj"} \ No newline at end of file +{"OutputProjectFolder":"ix","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"ax","ProjectFile":"hello_world_console_plc.csproj","UiHostProject":null} \ No newline at end of file diff --git a/src/sanbox/integration/ix-integration-plc/AXSharp.config.json b/src/sanbox/integration/ix-integration-plc/AXSharp.config.json index 8caede8a7..44d054536 100644 --- a/src/sanbox/integration/ix-integration-plc/AXSharp.config.json +++ b/src/sanbox/integration/ix-integration-plc/AXSharp.config.json @@ -1 +1 @@ -{"OutputProjectFolder":"ix","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"ax","ProjectFile":"ix_integration_plc.csproj"} \ No newline at end of file +{"OutputProjectFolder":"ix","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"ax","ProjectFile":"ix_integration_plc.csproj","UiHostProject":null} \ No newline at end of file diff --git a/src/tests.integrations/integrated/src/ax/AXSharp.config.json b/src/tests.integrations/integrated/src/ax/AXSharp.config.json index 8cdde7a7b..493e19718 100644 --- a/src/tests.integrations/integrated/src/ax/AXSharp.config.json +++ b/src/tests.integrations/integrated/src/ax/AXSharp.config.json @@ -1 +1 @@ -{"OutputProjectFolder":"..\\integrated.twin","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"ax","ProjectFile":"integrated.csproj"} \ No newline at end of file +{"OutputProjectFolder":"..\\integrated.twin","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"ax","ProjectFile":"integrated.csproj","UiHostProject":null} \ No newline at end of file diff --git a/templates/working/templates/axsharpblazor/ax/AXSharp.config.json b/templates/working/templates/axsharpblazor/ax/AXSharp.config.json index feead4cd1..6be91fe49 100644 --- a/templates/working/templates/axsharpblazor/ax/AXSharp.config.json +++ b/templates/working/templates/axsharpblazor/ax/AXSharp.config.json @@ -1 +1 @@ -{"OutputProjectFolder":"..\\axsharpblazor.twin","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"ax","ProjectFile":"axsharpblazor.csproj"} \ No newline at end of file +{"OutputProjectFolder":"..\\axsharpblazor.twin","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"ax","ProjectFile":"axsharpblazor.csproj","UiHostProject":null} \ No newline at end of file diff --git a/templates/working/templates/axsharpconsole/ax/AXSharp.config.json b/templates/working/templates/axsharpconsole/ax/AXSharp.config.json index aa10721bb..aa37a4021 100644 --- a/templates/working/templates/axsharpconsole/ax/AXSharp.config.json +++ b/templates/working/templates/axsharpconsole/ax/AXSharp.config.json @@ -1 +1 @@ -{"OutputProjectFolder":"..\\axsharpconsole.twin","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"ax","ProjectFile":"axsharpconsole.csproj"} \ No newline at end of file +{"OutputProjectFolder":"..\\axsharpconsole.twin","UseBase":false,"NoDependencyUpdate":false,"IgnoreS7Pragmas":false,"SkipDependencyCompilation":false,"TargetPlatfromMoniker":"ax","ProjectFile":"axsharpconsole.csproj","UiHostProject":null} \ No newline at end of file