Skip to content

Commit

Permalink
[NEW-FEATURE] Implements package metadata retrieval from ix project (#80
Browse files Browse the repository at this point in the history
)

* [ixc] adds retrieveal of metadata from nuget package
* [ixc] adds documentation on packaging

---------

Co-authored-by: PTKu <[email protected]>
  • Loading branch information
PTKu and PTKu authored Feb 18, 2023
1 parent 33b4c6f commit 864a5ef
Show file tree
Hide file tree
Showing 23 changed files with 234 additions and 31 deletions.
2 changes: 2 additions & 0 deletions docs/_navbar.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
* [Attributes](/articles/compiler/ATTRIBUTES.md)
* [Added members](/articles/compiler/ADDED_MEMBERS.md)
* [Config file](/articles/compiler/CONFIG_FILE.md)
* [Packaging and dependencies](/articles/compiler/PACKAGING.md)

* ---
* [Connectors](/articles/connectors/README.md)
* ---
* [Blazor rendering](/articles/blazor/README.md)
* [Blazor layouts](/articles/blazor/LAYOUTS.md)
* [Blazor controls](/articles/blazor/LIBRARIES.md)
* [Blazor auto-rendering](/articles/blazor/RENDERABLECONTENT.md)
* ---
* # API
* [Connector API](/api/Ix.Connector/Ix.Connector.md)
Expand Down
2 changes: 2 additions & 0 deletions docs/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
* [Attributes](/articles/compiler/ATTRIBUTES.md)
* [Added members](/articles/compiler/ADDED_MEMBERS.md)
* [Config file](/articles/compiler/CONFIG_FILE.md)
* [Packaging and dependencies](/articles/compiler/PACKAGING.md)

* ---
* [Connectors](/articles/connectors/README.md)
* ---
* [Blazor rendering](/articles/blazor/README.md)
* [Blazor layouts](/articles/blazor/LAYOUTS.md)
* [Blazor controls](/articles/blazor/LIBRARIES.md)
* [Blazor auto-rendering](/articles/blazor/RENDERABLECONTENT.md)
* ---
* [APIs](/apis.md)
* ----
Binary file added docs/articles/compiler/2023-02-18-09-32-22.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 1 addition & 3 deletions docs/articles/compiler/CONFIG_FILE.MD
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ Some compilation options can be overrided by CLI, such as `OutputProjectFolder`

~~~json
{
"OutputProjectFolder":"ix",
"AxProjectFolder":"C:\\MyPlcProject"
"OutputProjectFolder":"ix",
}
~~~

Expand All @@ -26,6 +25,5 @@ Some compilation options can be overrided by CLI, such as `OutputProjectFolder`
| ------------------- | ------------------------------------------------------------------------------------------------------ | ------- |
| 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 | |
| AxProjectFolder | Sets the folder of the source AX project. | |


28 changes: 28 additions & 0 deletions docs/articles/compiler/PACKAGING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Creating NuGet package from Twin library

NuGet is the preferred way to manage dependencies in an ix project. For creating NuGet packages you would follow the same procedure as with ordinary NuGet packages.

## Additional configuration

To properly create and consume a package you'd need to add the PLC project's metadata to your NuGet package.

You can do it by adding the following in the respective `csproj` file of your twin project.

~~~ XML
<ItemGroup>
<Folder Include=".meta\" />
<Content Include=".meta\**"/>
</ItemGroup>
~~~

or

Set the files in the `.meta` folder to build action to `Content`.

![](2023-02-18-09-32-22.png)


## 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.
8 changes: 7 additions & 1 deletion docs/articles/compiler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Entry.Plc.weather.GeoLocation.Write();
- [Attributes](ATTRIBUTES.md)
- [Added members](ADDED_MEMBERS.md)
- [Config file](CONFIG_FILE.md)
- [Packaging and dependency management](PACKAGING.md)

IX compiles transpiles following project blocks:

Expand All @@ -83,4 +84,9 @@ IX compiles transpiles following project blocks:
- [User defined data types](https://console.simatic-ax.siemens.io/docs/st/language/types-and-variables#user-defined-data-types)
- [Strucured types](https://console.simatic-ax.siemens.io/docs/st/language/types-and-variables#structured-type-without-relative-addressing)
- [Data type with named values as enums](https://console.simatic-ax.siemens.io/docs/st/language/types-and-variables#data-type-with-named-values)
-[Enumerations](https://console.simatic-ax.siemens.io/docs/st/language/types-and-variables#enumeration)
-[Enumerations](https://console.simatic-ax.siemens.io/docs/st/language/types-and-variables#enumeration)




34 changes: 21 additions & 13 deletions src/ix.compiler/src/IX.Cs.Compiler/CsProject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -255,30 +255,38 @@ private static IEnumerable<IReference> PackageReferences(XDocument csproj, strin
.Root!
.Elements("ItemGroup")
.SelectMany(ig => ig.Elements("PackageReference"))
.Select(pr => new PackageReference(pr, projectFile));
.Select(pr => PackageReference.CreateFromReferenceNode(pr, projectFile));
}

private static string PackageReferenceNugetPath(PackageReference package)
{
return Path.Combine(NugetDir, package.Name, GetBestMatchedVersion(package));
return Path.Combine(NugetDir, package.Name, GetBestMatchedVersion(package.Name, package.Version));
}

internal static string GetBestMatchedVersion(PackageReference package)
internal static string GetBestMatchedVersion(string packageName, string packageVersion)
{
var packageDirectory = Path.Combine(NugetDir, package.Name);

if (Directory.Exists(packageDirectory))
try
{
var packages = Directory.EnumerateDirectories(packageDirectory).Select(p => new DirectoryInfo(p))
.Select(v => new NuGetVersion(v.Name));
var packageDirectory = Path.Combine(NugetDir, packageName);

var versionRange = VersionRange.Parse(package.Version);
var bestMatch = versionRange.FindBestMatch(packages);
if (Directory.Exists(packageDirectory))
{
var packages = Directory.EnumerateDirectories(packageDirectory).Select(p => new DirectoryInfo(p))
.Select(v => new NuGetVersion(v.Name));

if (bestMatch != null) return bestMatch.ToNormalizedString();
}
var versionRange = VersionRange.Parse(packageVersion);
var bestMatch = versionRange.FindBestMatch(packages);

return package.Version;
if (bestMatch != null) return bestMatch.ToNormalizedString();
}

return packageVersion;
}
catch (Exception e)
{
throw new FailedToDeterminePackageVersion();
}

}

private static IEnumerable<IReference> ProjectReferences(XDocument csproj, string directory)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace Ix.Compiler;

public class FailedToDeterminePackageVersion : Exception
{
}
40 changes: 29 additions & 11 deletions src/ix.compiler/src/IX.Cs.Compiler/PackageReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,21 @@ public class PackageReference : IPackageReference
/// <param name="packageReferenceNode">Package reference node from csproj file.</param>
/// <param name="projectFile">Csproj file.</param>
/// <exception cref="FailedToRetrievePackageReferenceException"></exception>
public PackageReference(XElement packageReferenceNode, string projectFile)
public static PackageReference CreateFromReferenceNode(XElement packageReferenceNode, string projectFile)
{
string include = string.Empty;
try
{
Include = packageReferenceNode.Attribute(XName.Get(nameof(Include)))?.Value ?? packageReferenceNode.Attribute(XName.Get("Update"))?.Value;
Version = packageReferenceNode.Attribute(XName.Get(nameof(Version))).Value;
ReferencePath = PackageReferenceNugetPath(this);
include = packageReferenceNode.Attribute(XName.Get(nameof(Include)))?.Value ?? packageReferenceNode.Attribute(XName.Get("Update"))?.Value;
var version = packageReferenceNode.Attribute(XName.Get(nameof(Version))).Value;
var referencePath = PackageReferenceNugetPath(include, version);

return new PackageReference(referencePath, include, version);
}
catch (Exception ex)
{
throw new FailedToRetrievePackageReferenceException(
$"Could not parse 'Name' or 'Version' of the package '{Include}' reference in project {projectFile}",
$"Could not parse 'Name' or 'Version' of the package '{include}' reference in project {projectFile}",
ex);
}
}
Expand All @@ -46,11 +49,17 @@ public PackageReference(XElement packageReferenceNode, string projectFile)
/// Create new instance of <see cref="PackageReference" /> from <see cref="PackageDependency" />
/// </summary>
/// <param name="packageDependency">Package dependency</param>
public PackageReference(PackageDependency packageDependency)
public PackageReference(PackageDependency packageDependency)
: this(PackageReferenceNugetPath(packageDependency.Id, packageDependency.VersionRange.OriginalString), packageDependency.Id, packageDependency.VersionRange.OriginalString)
{

}

public PackageReference(string packageFolder, string id, string version)
{
Include = packageDependency.Id;
Version = packageDependency.VersionRange.OriginalString;
ReferencePath = PackageReferenceNugetPath(this);
Include = id;
Version = version;
ReferencePath = packageFolder;
}

/// <summary>
Expand All @@ -73,8 +82,17 @@ public PackageReference(PackageDependency packageDependency)
/// </summary>
public string ReferencePath { get; }

private static string PackageReferenceNugetPath(PackageReference package)
private static string PackageReferenceNugetPath(string packageName, string packageVersion)
{
return Path.Combine(NugetDir, package.Name, CsProject.GetBestMatchedVersion(package));
return Path.Combine(NugetDir, packageName, CsProject.GetBestMatchedVersion(packageName, packageVersion));
}

/// <inheritdoc />
public string MetadataPath => Path.Combine(ReferencePath, "content", ".meta", "meta.json");

/// <inheritdoc />
public string ProjectInfo => Path.Combine(ReferencePath, "content", ".meta", "sourceinfo.json");

/// <inheritdoc />
public bool IsIxDependency => File.Exists(MetadataPath);
}
9 changes: 9 additions & 0 deletions src/ix.compiler/src/IX.Cs.Compiler/ProjectReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,13 @@ public ProjectReference(string directory, string projectPath)
/// Returns version (defaults to 0.0.0.0);
/// </summary>
public string Version => "0.0.0.0";

/// <inheritdoc />
public string MetadataPath => Path.Combine(ReferencePath, ".meta", "meta.json");

/// <inheritdoc />
public string ProjectInfo => Path.Combine(ReferencePath, ".meta", "sourceinfo.json");

/// <inheritdoc />
public bool IsIxDependency => File.Exists(MetadataPath);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,18 @@ public interface IReference
string ReferencePath { get; }
string Version { get; }

public string MetadataPath => Path.Combine(ReferencePath, ".meta", "meta.json");
/// <summary>
/// Gets file path of meta data for this reference.
/// </summary>
string MetadataPath { get; }

public string ProjectInfo => Path.Combine(ReferencePath, ".meta", "sourceinfo.json");
/// <summary>
/// Get file path of ix project information for this reference.
/// </summary>
string ProjectInfo { get; }

public bool IsIxDependency => File.Exists(MetadataPath);
/// <summary>
/// Gets whether this reference is ix project.
/// </summary>
bool IsIxDependency { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,18 @@
</ItemGroup>

<ItemGroup>
<Content Include="samples\packaging\contentFiles\any\net6.0\.meta\meta.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="samples\packaging\contentFiles\any\net6.0\.meta\sourceinfo.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="samples\packaging\content\.meta\meta.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="samples\packaging\content\.meta\sourceinfo.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="samples\units\.gitignore">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
Expand Down Expand Up @@ -354,6 +366,15 @@
</ItemGroup>

<ItemGroup>
<None Update="samples\packaging\ix.framework.core.nuspec">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="samples\packaging\lib\net6.0\ix.framework.core.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="samples\packaging\[Content_Types].xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="samples\units\src\file_with_unsupported.st">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
Expand Down
44 changes: 44 additions & 0 deletions src/ix.compiler/tests/Ix.Compiler.CsTests/PackageReferenceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Xunit.Abstractions;
using YamlDotNet.Core;

namespace Ix.Compiler.CsTests
{
public class PackageReferenceTests
{
private readonly string testFolder;

private ITestOutputHelper output;

protected IEnumerable<Type> builders;

protected string OutputSubFolder;

public PackageReferenceTests(ITestOutputHelper output)
{
this.output = output;

#pragma warning disable CS8604 // Possible null reference argument.
var executingAssemblyFileInfo
= new FileInfo(Assembly.GetExecutingAssembly().FullName);
#pragma warning restore CS8604 // Possible null reference argument.

testFolder = executingAssemblyFileInfo.Directory!.FullName;
}

[Fact]
public void retrieve_metadata_from_package_success()
{
var packageFolder = Path.Combine(testFolder, $@"samples\packaging\");
var packageReference = new PackageReference(packageFolder, "ix.framework.core", "0.0.0");

Assert.True(File.Exists(packageReference.MetadataPath));
Assert.True(packageReference.IsIxDependency);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml" />
<Default Extension="psmdcp" ContentType="application/vnd.openxmlformats-package.core-properties+xml" />
<Default Extension="dll" ContentType="application/octet" />
<Default Extension="json" ContentType="application/octet" />
<Default Extension="nuspec" ContentType="application/octet" />
</Types>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Type="http://schemas.microsoft.com/packaging/2010/07/manifest" Target="/ix.framework.core.nuspec" Id="RD93A6E0D5F092AD4" />
<Relationship Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="/package/services/metadata/core-properties/4c98e6798bcd4b0ebb9e869bf1e81990.psmdcp" Id="RC47489BFB7BC9D85" />
</Relationships>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["NAMESPACE ix.framework.core\nCLASS IxContext END_CLASSEND_NAMESPACE","NAMESPACE ix.framework.core\nCLASS _NULL_CONTEXT END_CLASSEND_NAMESPACE","NAMESPACE ix.framework.core\nCLASS IxObject END_CLASSEND_NAMESPACE","NAMESPACE ix.framework.core\nINTERFACE IIxContext END_INTERFACEEND_NAMESPACE","NAMESPACE ix.framework.core\nINTERFACE IIxObject END_INTERFACEEND_NAMESPACE","NAMESPACE ix.framework.core\nCLASS IxComponent END_CLASSEND_NAMESPACE","NAMESPACE ix.framework.core\nTYPE eIxTaskState : INT (item0 := 0); END_TYPEEND_NAMESPACE","NAMESPACE ix.framework.core\nCLASS IxTask END_CLASSEND_NAMESPACE","NAMESPACE ix.framework.core\nINTERFACE IIxComponent END_INTERFACEEND_NAMESPACE","NAMESPACE ix.framework.core\nINTERFACE IIxServiceable END_INTERFACEEND_NAMESPACE","NAMESPACE ix.framework.core\nINTERFACE IIxCoordinator END_INTERFACEEND_NAMESPACE","NAMESPACE ix.framework.core\nTYPE CoordinatorStates : INT (item0 := 0); END_TYPEEND_NAMESPACE","NAMESPACE ix.framework.core\nINTERFACE IIxTask END_INTERFACEEND_NAMESPACE","NAMESPACE ix.framework.core\nINTERFACE IIxTaskState END_INTERFACEEND_NAMESPACE","NAMESPACE ix.framework.core\nTYPE eIxSequenceMode : INT (item0 := 0); END_TYPEEND_NAMESPACE","NAMESPACE ix.framework.core\nTYPE eIxSteppingMode : INT (item0 := 0); END_TYPEEND_NAMESPACE","NAMESPACE ix.framework.core\nCLASS IxSequencer END_CLASSEND_NAMESPACE","NAMESPACE ix.framework.core\nCLASS IxStep END_CLASSEND_NAMESPACE","NAMESPACE ix.framework.core\nEND_NAMESPACE","NAMESPACE ix.framework.core\nEND_NAMESPACE","NAMESPACE ix.framework.core\nEND_NAMESPACE","NAMESPACE ix.framework.core\nEND_NAMESPACE","NAMESPACE ix.framework.core\nINTERFACE IIxStep END_INTERFACEEND_NAMESPACE"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"ax-source":"../ctrl"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["NAMESPACE ix.framework.core\nCLASS IxContext END_CLASSEND_NAMESPACE","NAMESPACE ix.framework.core\nCLASS _NULL_CONTEXT END_CLASSEND_NAMESPACE","NAMESPACE ix.framework.core\nCLASS IxObject END_CLASSEND_NAMESPACE","NAMESPACE ix.framework.core\nINTERFACE IIxContext END_INTERFACEEND_NAMESPACE","NAMESPACE ix.framework.core\nINTERFACE IIxObject END_INTERFACEEND_NAMESPACE","NAMESPACE ix.framework.core\nCLASS IxComponent END_CLASSEND_NAMESPACE","NAMESPACE ix.framework.core\nTYPE eIxTaskState : INT (item0 := 0); END_TYPEEND_NAMESPACE","NAMESPACE ix.framework.core\nCLASS IxTask END_CLASSEND_NAMESPACE","NAMESPACE ix.framework.core\nINTERFACE IIxComponent END_INTERFACEEND_NAMESPACE","NAMESPACE ix.framework.core\nINTERFACE IIxServiceable END_INTERFACEEND_NAMESPACE","NAMESPACE ix.framework.core\nINTERFACE IIxCoordinator END_INTERFACEEND_NAMESPACE","NAMESPACE ix.framework.core\nTYPE CoordinatorStates : INT (item0 := 0); END_TYPEEND_NAMESPACE","NAMESPACE ix.framework.core\nINTERFACE IIxTask END_INTERFACEEND_NAMESPACE","NAMESPACE ix.framework.core\nINTERFACE IIxTaskState END_INTERFACEEND_NAMESPACE","NAMESPACE ix.framework.core\nTYPE eIxSequenceMode : INT (item0 := 0); END_TYPEEND_NAMESPACE","NAMESPACE ix.framework.core\nTYPE eIxSteppingMode : INT (item0 := 0); END_TYPEEND_NAMESPACE","NAMESPACE ix.framework.core\nCLASS IxSequencer END_CLASSEND_NAMESPACE","NAMESPACE ix.framework.core\nCLASS IxStep END_CLASSEND_NAMESPACE","NAMESPACE ix.framework.core\nEND_NAMESPACE","NAMESPACE ix.framework.core\nEND_NAMESPACE","NAMESPACE ix.framework.core\nEND_NAMESPACE","NAMESPACE ix.framework.core\nEND_NAMESPACE","NAMESPACE ix.framework.core\nINTERFACE IIxStep END_INTERFACEEND_NAMESPACE"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"ax-source":"../ctrl"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>ix.framework.core</id>
<version>0.1.1-19-desing-component-base-class.38</version>
<authors>ix.framework.core</authors>
<description>Package Description</description>
<dependencies>
<group targetFramework="net6.0">
<dependency id="Ix.Connector" version="0.13.3-alpha.56" exclude="Build,Analyzers" />
</group>
</dependencies>
<contentFiles>
<files include="any/net6.0/.meta/meta.json" buildAction="Content" />
<files include="any/net6.0/.meta/sourceinfo.json" buildAction="Content" />
</contentFiles>
</metadata>
</package>
Binary file not shown.
Loading

0 comments on commit 864a5ef

Please sign in to comment.