Skip to content
Open
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
4 changes: 2 additions & 2 deletions BHoM_UI/Global/AssemblyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public bool MakeSureAssemblyIsLoadedForType(string type)
{
if (!BH.Engine.Base.Query.IsAssemblyLoaded(assemblyName))
{
string assemblyPath = Path.Combine(BH.Engine.Base.Query.BHoMFolder(), assemblyName + ".dll");
string assemblyPath = BH.Engine.UI.Query.AssemblyPath(assemblyName);
if (!File.Exists(assemblyPath))
{
BH.Engine.Base.Compute.RecordError($"Assembly file not found when trying to load assemblies for type {type}: {assemblyPath}");
Expand Down Expand Up @@ -162,7 +162,7 @@ public bool MakeSureAssemblyIsLoadedForExtensionMethod(string methodName, Type t
{
if (!BH.Engine.Base.Query.IsAssemblyLoaded(assemblyName))
{
string assemblyPath = Path.Combine(BH.Engine.Base.Query.BHoMFolder(), assemblyName + ".dll");
string assemblyPath = BH.Engine.UI.Query.AssemblyPath(assemblyName);
if (!File.Exists(assemblyPath))
{
BH.Engine.Base.Compute.RecordNote($"Assembly not found for extension method {methodName}: {assemblyPath}");
Expand Down
7 changes: 5 additions & 2 deletions BHoM_UI/Global/Initialisation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ public static bool Activate()

success &= LoadCodeElements();
success &= CreateAssemblyResolver();
success &= LoadToolkitSettings();
success &= LoadNewAssemblies();
success &= CreateSearchItems(CodeElements);
success &= LoadToolkitSettings();

CompletionTime = DateTime.UtcNow;

Expand Down Expand Up @@ -127,7 +127,10 @@ private static bool InitialiseToolkit(IInitialisationSettings settings)

// Make sure the assembly is loaded for that method
if (!string.IsNullOrEmpty(settings.InitialisationAssembly) && !BH.Engine.Base.Query.IsAssemblyLoaded(settings.InitialisationAssembly))
BH.Engine.Base.Compute.LoadAssembly(Path.Combine(BH.Engine.Base.Query.BHoMFolder(), settings.InitialisationAssembly + ".dll"));
{
string initAssemblyPath = BH.Engine.UI.Query.AssemblyPath(settings.InitialisationAssembly);
BH.Engine.Base.Compute.LoadAssembly(initAssemblyPath);
}

// Get method declaring type
List<Type> typeCandidates = Engine.Base.Create.AllTypes(typeName).Where(x => x.FullName == typeName).ToList();
Expand Down
47 changes: 39 additions & 8 deletions UI_Engine/Compute/LoadNewAssemblies.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,31 +55,62 @@ public static List<string> LoadNewAssemblies(Dictionary<string, DateTime> lastAs

// Make sure the keys for the assemblies are in lower case to avoid casing mismatching
Dictionary<string, DateTime> lastUpdateTimes = lastAssemblyUpdateTimes.ToDictionary(x => x.Key.ToLower(), x => x.Value);
HashSet<string> loadedAssemblies = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
HashSet<string> visitedAssemblies = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

List<string> loadedAssemblies = new List<string>();
// Pass 1: runtime-specific subdirectory (preferred over flat folder when present)
string bhomFolder = BH.Engine.Base.Query.BHoMFolder();
foreach (string subFolder in BH.Engine.UI.Query.SubFoldersForRuntime())
{
string runtimeFolder = Path.Combine(bhomFolder, subFolder);
LoadNewAssembliesForFolder(runtimeFolder, lastUpdateTimes, loadedAssemblies, visitedAssemblies);
}

// Pass 2: flat folder, skipping any assembly already handled by the runtime subdir pass
LoadNewAssembliesForFolder(bhomFolder, lastUpdateTimes, loadedAssemblies, visitedAssemblies);

return loadedAssemblies.ToList();
}

Regex regex = new Regex(@"oM$|_Engine$|_Adapter$");
foreach (string file in Directory.GetFiles(BH.Engine.Base.Query.BHoMFolder(), "*.dll", SearchOption.TopDirectoryOnly))

/*************************************/
/**** Private Methods ****/
/*************************************/

private static void LoadNewAssembliesForFolder(string folderPath, Dictionary<string, DateTime> lastUpdateTimes, HashSet<string> loadedAssemblies, HashSet<string> visitedAssemblies)
{
if (!Directory.Exists(folderPath))
return;

foreach (string file in Directory.GetFiles(folderPath, "*.dll", SearchOption.TopDirectoryOnly))
{
string name = Path.GetFileNameWithoutExtension(file);
if (regex.IsMatch(name))

if (m_AssemblyNameFilter.IsMatch(name) && !visitedAssemblies.Contains(name))
{
visitedAssemblies.Add(name);
string key = name.ToLower();

if (!lastUpdateTimes.ContainsKey(key) || lastUpdateTimes[key] < File.GetLastWriteTimeUtc(file))
{
Assembly assembly = BH.Engine.Base.Compute.LoadAssembly(file);
if (assembly != null)
{
BH.Engine.Base.Compute.RecordNote($"Assembly {name} loaded as it was newer than its last recorded update time.");
loadedAssemblies.Add(name);
}
}
}
}
}
}

return loadedAssemblies;
}


/*************************************/
/**** Private Fields ****/
/*************************************/

private static readonly Regex m_AssemblyNameFilter = new Regex(@"oM$|_Engine$|_Adapter$");

/*************************************/

}
Expand Down
70 changes: 70 additions & 0 deletions UI_Engine/Query/AssemblyPath.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
* Copyright (c) 2015 - 2026, the respective contributors. All rights reserved.
*
* Each contributor holds copyright over their respective contributions.
* The project versioning (Git) records all such contribution source information.
*
*
* The BHoM is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* The BHoM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
*/

using BH.Engine.Base;
using BH.oM.Base.Attributes;
using BH.oM.UI;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BH.Engine.UI
{
public static partial class Query
{
/*************************************/
/**** Public Methods ****/
/*************************************/

[Description("Returns the best on-disk path for a BHoM assembly, preferring the runtime-specific subdirectory (netX.0\\ or netfx\\) over the flat folder.")]
[Input("assemblyName", "Assembly name without extension, e.g. 'SQL_Adapter'")]
[Output("path", "Full path to the .dll file; the file may or may not exist.")]
public static string AssemblyPath(string assemblyName)
{
string bhomFolder = BH.Engine.Base.Query.BHoMFolder();

// First try to return an assembly from a runtime-specific folder
foreach (string subFolder in BH.Engine.UI.Query.SubFoldersForRuntime())
{
string runtimePath = Path.Combine(bhomFolder, subFolder, assemblyName + ".dll");
if (File.Exists(runtimePath))
return runtimePath;
}

//Then fallback to returning the assembly from the default rool folder
return Path.Combine(bhomFolder, assemblyName + ".dll");
}

/*************************************/
}
}







85 changes: 85 additions & 0 deletions UI_Engine/Query/SubFoldersForRuntime.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
* Copyright (c) 2015 - 2026, the respective contributors. All rights reserved.
*
* Each contributor holds copyright over their respective contributions.
* The project versioning (Git) records all such contribution source information.
*
*
* The BHoM is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* The BHoM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
*/

using BH.Engine.Base;
using BH.oM.Base.Attributes;
using BH.oM.UI;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace BH.Engine.UI
{
public static partial class Query
{
/*************************************/
/**** Public Methods ****/
/*************************************/

[Description("Returns the runtime-specific subdirectories of the BHoM Assemblies folder where assemblies compatible with the current .NET runtime can be found. " +
"Returns '.../Assemblies/netfx/' on .NET Framework and '.../Assemblies/netX.0/' on CoreCLR (.NET X).")]
[Output("subFolders", "runtime-specific subdirectories for the BHoM assemblies sorted in the order they should be traversed.")]
public static List<string> SubFoldersForRuntime()
{
if (m_SubFoldersForRuntime != null)
return m_SubFoldersForRuntime;


var desc = RuntimeInformation.FrameworkDescription;
if (desc.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase))
{
// Return 'netfx' if the framework is a .NET Framework
m_SubFoldersForRuntime = new List<string> { "netfx" };
}
else
{
// For .NET Core, return exact TFM first, then descend to lower versions as fallback
m_SubFoldersForRuntime = new List<string>();
int major = Environment.Version.Major;
for (int v = major; v >= 5; v--)
m_SubFoldersForRuntime.Add($"net{v}.0");
}

return m_SubFoldersForRuntime;
}


/*************************************/
/**** Private Fields ****/
/*************************************/

private static List<string> m_SubFoldersForRuntime = null;

/*************************************/
}
}