Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEW-FEATURE] Explore possible nuget package installation during ixc run from apax package #242

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@ namespace AXSharp.Compiler;
public interface ICompilerOptions
{
string? OutputProjectFolder { get; set; }
string? ProjectFile { get; set; }
bool UseBase { get; set; }

bool NoDependencyUpdate { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,9 @@ public interface ITargetProject

void GenerateResources();

void GenerateCompanionData();

void InstallAXSharpDependencies(IEnumerable<object> dependencies);

IEnumerable<IReference> LoadReferences();
}
32 changes: 23 additions & 9 deletions src/AXSharp.compiler/src/AXSharp.Compiler/AXSharpConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class AXSharpConfig : ICompilerOptions
/// <summary>
/// Creates new instance of IxConfig object.
/// </summary>
[Obsolete("Use 'Create IxConfig' instead.")]
[Obsolete($"Use 'Create {nameof(RetrieveAXSharpConfig)} instead.")]
public AXSharpConfig()
{

Expand All @@ -31,7 +31,9 @@ public AXSharpConfig()
/// </summary>
public const string CONFIG_FILE_NAME = "AXSharp.config.json";





private string _outputProjectFolder = "ix";

/// <summary>
Expand All @@ -43,8 +45,18 @@ public string OutputProjectFolder
set => _outputProjectFolder = value;
}

/// <summary>
/// Gets or sets whether compiler should use $base for base types of a class.
/// </summary>
public bool UseBase { get; set; }

public bool NoDependencyUpdate { get; set; }


/// <summary>
/// Gets or sets name of the output project file.
/// </summary>
public string? ProjectFile { get; set; }

private string _axProjectFolder;

Expand All @@ -62,9 +74,9 @@ public string AxProjectFolder
/// Gets updated or creates default config for given AX project.
/// </summary>
/// <param name="directory">AX project directory</param>
/// <param name="cliCompilerOptions">Compiler options.</param>
/// <param name="newCompilerOptions">Compiler options.</param>
/// <returns>Ix configuration for given AX project.</returns>
public static AXSharpConfig UpdateAndGetIxConfig(string directory, ICompilerOptions? cliCompilerOptions = null)
public static AXSharpConfig UpdateAndGetAXSharpConfig(string directory, ICompilerOptions? newCompilerOptions = null)
{
var ixConfigFilePath = Path.Combine(directory, CONFIG_FILE_NAME);

Expand All @@ -81,7 +93,7 @@ public static AXSharpConfig UpdateAndGetIxConfig(string directory, ICompilerOpti
if (AXSharpConfig != null)
{
AXSharpConfig.AxProjectFolder = directory;
OverridesFromCli(AXSharpConfig, cliCompilerOptions);
OverridesFromCli(AXSharpConfig, newCompilerOptions);
}

using (StreamWriter file = File.CreateText(ixConfigFilePath))
Expand All @@ -107,7 +119,7 @@ public static AXSharpConfig UpdateAndGetIxConfig(string directory, ICompilerOpti
}


public static AXSharpConfig RetrieveIxConfig(string ixConfigFilePath)
public static AXSharpConfig RetrieveAXSharpConfig(string ixConfigFilePath)
{
try
{
Expand All @@ -128,13 +140,15 @@ public static AXSharpConfig RetrieveIxConfig(string ixConfigFilePath)

}

private static void OverridesFromCli(ICompilerOptions fromConfig, ICompilerOptions? fromCli)
private static void OverridesFromCli(ICompilerOptions fromConfig, ICompilerOptions? newCompilerOptions)
{
// No CLI params
if (fromCli == null)
if (newCompilerOptions == null)
return;

// Items to override from the CLI
fromConfig.OutputProjectFolder = fromCli.OutputProjectFolder ?? fromConfig.OutputProjectFolder;
fromConfig.OutputProjectFolder = newCompilerOptions.OutputProjectFolder ?? fromConfig.OutputProjectFolder;
fromConfig.ProjectFile = string.IsNullOrEmpty(newCompilerOptions.ProjectFile) ? fromConfig.ProjectFile : newCompilerOptions.ProjectFile;
fromConfig.NoDependencyUpdate = newCompilerOptions.NoDependencyUpdate;
}
}
13 changes: 9 additions & 4 deletions src/AXSharp.compiler/src/AXSharp.Compiler/AXSharpProject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// Third party licenses: https://github.com/ix-ax/axsharp/blob/master/notices.md

using System.Text;
using System.Xml.Linq;
using AX.ST.Semantic;
using AX.ST.Semantic.Model.Declarations;
using AX.ST.Semantic.Model.Declarations.Types;
Expand All @@ -14,6 +15,7 @@
using AX.Text;
using AXSharp.Compiler.Core;
using AXSharp.Compiler.Exceptions;
using Microsoft.CodeAnalysis.Diagnostics;
using Newtonsoft.Json;
using Polly;

Expand Down Expand Up @@ -42,16 +44,15 @@ public class AXSharpProject : IAXSharpProject
public AXSharpProject(AxProject axProject, IEnumerable<Type> builderTypes, Type targetProjectType, ICompilerOptions? cliCompilerOptions = null)
{
AxProject = axProject;
CompilerOptions = AXSharpConfig.UpdateAndGetIxConfig(axProject.ProjectFolder, cliCompilerOptions);
CompilerOptions = AXSharpConfig.UpdateAndGetAXSharpConfig(axProject.ProjectFolder, cliCompilerOptions);
OutputFolder = Path.GetFullPath(Path.Combine(AxProject.ProjectFolder, CompilerOptions.OutputProjectFolder));
if (cliCompilerOptions != null) UseBaseSymbol = cliCompilerOptions.UseBase;
BuilderTypes = builderTypes;
TargetProject = Activator.CreateInstance(targetProjectType, this) as ITargetProject ?? throw new
InvalidOperationException("Target project type must implement ITargetProject interface.");
}




/// <summary>
/// Get AX project.
/// </summary>
Expand Down Expand Up @@ -134,9 +135,12 @@ public void Generate()
TargetProject.ProvisionProjectStructure();
GenerateMetadata(compilation);
TargetProject.GenerateResources();
TargetProject.GenerateCompanionData();
Log.Logger.Information($"Compilation of project '{AxProject.SrcFolder}' done.");
}



/// <summary>
/// Cleans all output files from the output directory
/// </summary>
Expand Down Expand Up @@ -207,7 +211,8 @@ private IEnumerable<ISyntaxTree> GetReferences()

private void CompileProjectReferences(IEnumerable<IReference> referencedDependencies)
{
foreach (var ixProjectReference in AxProject.AXSharpReferences)
TargetProject.InstallAXSharpDependencies(AxProject.AXSharpReferences);
foreach (var ixProjectReference in AxProject.AXSharpReferences.OfType<AXSharpConfig>())
{

if (compiled.Contains(ixProjectReference.AxProjectFolder))
Expand Down
25 changes: 23 additions & 2 deletions src/AXSharp.compiler/src/AXSharp.Compiler/Apax.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public Apax()
/// <param name="projectFile">Project file from which the ApaxFile object will be created.</param>
/// <returns></returns>
/// <exception cref="FileNotFoundException"></exception>
public static Apax CreateApax(string projectFile)
public static Apax CreateApaxDto(string projectFile)
{
try
{
Expand All @@ -85,6 +85,27 @@ public static Apax CreateApax(string projectFile)
}
}

public static Apax TryCreateApaxDto(string projectFile)
{
try
{
if (!File.Exists(projectFile))
return null;

var deserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.IgnoreUnmatchedProperties()
.Build();

return deserializer.Deserialize<Apax>(File.ReadAllText(projectFile));
}
catch (FileNotFoundException)
{
throw new FileNotFoundException(
"'apax.yml' file was not found in the working directory. Make sure your current directory is simatic-ax project directory or provide source directory argument (for details see ixc --help)");
}
}

/// <summary>
/// Update version in an apax.yml file
/// </summary>
Expand All @@ -95,7 +116,7 @@ public static void UpdateVersion(string apaxFile, string version)
{
try
{
var apax = CreateApax(apaxFile);
var apax = CreateApaxDto(apaxFile);
apax.Version = version;


Expand Down
93 changes: 76 additions & 17 deletions src/AXSharp.compiler/src/AXSharp.Compiler/AxProject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
using System.IO;
using System;

namespace AXSharp.Compiler;

Expand All @@ -31,7 +32,7 @@ public AxProject(string axProjectFolder)
ProjectFolder = axProjectFolder;
ProjectFile = Path.Combine(ProjectFolder, "apax.yml");
SrcFolder = Path.Combine(axProjectFolder, "src");
ProjectInfo = Apax.CreateApax(ProjectFile);
ProjectInfo = Apax.CreateApaxDto(ProjectFile);
Sources = Directory.GetFiles(Path.Combine(ProjectFolder, "src"), "*.st", SearchOption.AllDirectories)
.Select(p => new SourceFileText(p));
}
Expand All @@ -51,7 +52,7 @@ public AxProject(string axProjectFolder, string[] sourceFiles)
ProjectFolder = axProjectFolder;
ProjectFile = Path.Combine(ProjectFolder, "apax.yml");
SrcFolder = Path.Combine(axProjectFolder, "src");
ProjectInfo = Apax.CreateApax(ProjectFile);
ProjectInfo = Apax.CreateApaxDto(ProjectFile);
Sources = sourceFiles.Select(p => new SourceFileText(p));
}

Expand Down Expand Up @@ -80,32 +81,90 @@ public AxProject(string axProjectFolder, string[] sourceFiles)
/// </summary>
public string SrcFolder { get; }

/// <summary>
/// Contains list of near-by project in the directory structure (-2 levels up).
/// </summary>
private static List<NearByProjects> nearByProjects;

private class NearByProjects
{
public Apax Apax { get; set; }
public FileInfo ApaxFile { get; set; }
}

private class InstalledDependencies
{
public Apax Apax { get; set; }
public CompanionInfo Companion { get; set; }

public FileInfo ApaxFile { get; set; }
}

/// <summary>
/// Gets paths of this project's references to other ix projects.
/// </summary>
public IEnumerable<AXSharpConfig> AXSharpReferences
public IEnumerable<object> AXSharpReferences => GetProjectDependencies();

private IEnumerable<object> GetProjectDependencies()
{
get
var dependencies = ProjectInfo.Dependencies ?? new Dictionary<string, string>();
var installedDependencies =
dependencies.Select(p => Path.Combine(ProjectFolder, ".apax", "packages",
p.Key.Replace('/', Path.DirectorySeparatorChar)))
.Select(p => new InstalledDependencies()
{
Apax = Apax.TryCreateApaxDto(Path.Combine(p, "apax.yml")),
Companion = CompanionInfo.TryFromFile(Path.Combine(p, CompanionInfo.COMPANIONS_FILE_NAME)),
ApaxFile = new FileInfo(Path.Combine(p, "apax.yml"))
}).ToList();


nearByProjects ??= Directory.EnumerateFiles(
Path.GetFullPath(Path.Combine(this.ProjectFolder, "../../..")),
"apax.yml", SearchOption.AllDirectories)
.Select(p => new FileInfo(p))
.Where(p => !p.Directory.FullName.Contains(".apax"))
.Select(a => new NearByProjects() { Apax = Apax.TryCreateApaxDto(a.FullName), ApaxFile = a })
.ToList();

var projectDependencies = new List<object>();

foreach (var dependency in dependencies)
{
var dependencies = ProjectInfo.Dependencies ?? new Dictionary<string, string>();
var hasSuchProject =
nearByProjects.FirstOrDefault(p => p.Apax.Name == dependency.Key && p.Apax.Version == dependency.Value);
if (hasSuchProject != null)
{
var pathAXSharpConfig =
Path.Combine(hasSuchProject.ApaxFile.Directory.FullName, AXSharpConfig.CONFIG_FILE_NAME);
if (File.Exists(pathAXSharpConfig))
{
projectDependencies.Add((AXSharpConfig.RetrieveAXSharpConfig(pathAXSharpConfig)));
}
}
}

var packagesDirectories =
dependencies.Select(p => Path.Combine(ProjectFolder, ".apax", "packages", p.Key.Replace('/',Path.DirectorySeparatorChar)));
foreach (var dependency in dependencies)
{
var dependencyWithCompanion = installedDependencies
.FirstOrDefault(p => p.Apax != null && p.Apax.Name == dependency.Key && p.Apax.Version == dependency.Value);


var retVal = packagesDirectories
.Where(p => Directory.Exists(p))
.Select(p => new DirectoryInfo(p))
.Select(p => Directory.EnumerateFiles(p.LinkTarget ?? p.FullName, AXSharpConfig.CONFIG_FILE_NAME, SearchOption.TopDirectoryOnly))
.SelectMany(p => p).Select(c => AXSharpConfig.RetrieveIxConfig(c));

if (retVal.Count() == 0)
if (dependencyWithCompanion?.Companion != null)
{
Log.Logger.Information("Retrieving possible project references from .apax packages did not produce results. " +
"If you have referenced AX# projects, the packages must be previously installed by 'apax install'");
var packageFile =
Path.Combine(dependencyWithCompanion.ApaxFile.Directory.FullName, "package.json");
if(File.Exists(packageFile))
projectDependencies.Add(dependencyWithCompanion.Companion);
}
}

return retVal;
if (!projectDependencies.Any())
{
Log.Logger.Information("Retrieving possible project references from .apax packages did not produce results. " +
"If you have referenced AX# projects, the packages must be previously installed by 'apax install'");
}

return projectDependencies;
}
}
Loading