18 static List<StringPair> movedPairs =
new List<StringPair>();
19 public static string BuildTempDir = Path.Combine(Directory.GetParent(Application.dataPath).FullName,
"LLMUnityBuild");
20 static string movedCache = Path.Combine(BuildTempDir,
"moved.json");
22 [InitializeOnLoadMethod]
23 private static void InitializeOnLoad()
28 public static string PluginDir(
string platform,
bool relative =
false)
30 string pluginDir = Path.Combine(
"Plugins", platform,
"LLMUnity");
31 if (!relative) pluginDir = Path.Combine(Application.dataPath, pluginDir);
35 public static void Retry(System.Action action,
int retries = 10,
int delayMs = 100)
37 for (
int i = 0; i < retries; i++)
46 if (i == retries - 1)
LLMUnitySetup.LogError(e.Message,
true);
47 System.Threading.Thread.Sleep(delayMs);
49 catch (System.UnauthorizedAccessException e)
51 if (i == retries - 1)
LLMUnitySetup.LogError(e.Message,
true);;
52 System.Threading.Thread.Sleep(delayMs);
65 if (File.Exists(source))
67 string targetDir = Path.GetDirectoryName(target);
68 if (targetDir !=
"" && !Directory.Exists(targetDir)) Directory.CreateDirectory(targetDir);
69 Retry(() => actionCallback(source, target));
71 else if (Directory.Exists(source))
73 Directory.CreateDirectory(target);
74 List<string> filesAndDirs =
new List<string>();
75 filesAndDirs.AddRange(Directory.GetFiles(source));
76 filesAndDirs.AddRange(Directory.GetDirectories(source));
77 foreach (
string path
in filesAndDirs)
91 File.Copy(source, target,
true);
99 public static void CopyPath(
string source,
string target)
109 public static void MovePath(
string source,
string target)
121 string[] allowedDirs =
new string[] {
LLMUnitySetup.GetAssetPath(), BuildTempDir, PluginDir(
"Android"), PluginDir(
"iOS"), PluginDir(
"VisionOS") };
122 bool deleteOK =
false;
123 foreach (
string allowedDir
in allowedDirs) deleteOK = deleteOK ||
LLMUnitySetup.IsSubPath(path, allowedDir);
126 LLMUnitySetup.LogError($
"Safeguard: {path} will not be deleted because it may not be safe");
129 if (File.Exists(path)) Retry(() => File.Delete(path));
130 else if (Directory.Exists(path)) Retry(() => Directory.Delete(path,
true));
134 static void AddMovedPair(
string source,
string target)
136 movedPairs.Add(
new StringPair { source = source, target = target });
137 File.WriteAllText(movedCache, JsonUtility.ToJson(
new ListStringPair { pairs = movedPairs },
true));
140 static void AddTargetPair(
string target)
142 AddMovedPair(
"", target);
145 static bool MoveAction(
string source,
string target,
bool addEntry =
true)
147 Action<string, string> moveCallback;
148 if (File.Exists(source) || Directory.Exists(source)) moveCallback =
MovePath;
151 if (addEntry) AddMovedPair(source, target);
152 moveCallback(source, target);
156 static bool CopyAction(
string source,
string target,
bool addEntry =
true)
158 Action<string, string> copyCallback;
159 if (File.Exists(source)) copyCallback = File.Copy;
160 else if (Directory.Exists(source)) copyCallback =
CopyPath;
163 if (addEntry) AddTargetPair(target);
164 copyCallback(source, target);
168 static void CopyActionAddMeta(
string source,
string target)
170 CopyAction(source, target);
171 AddTargetPair(target +
".meta");
174 static void AddActionAddMeta(
string target)
176 AddTargetPair(target);
177 AddTargetPair(target +
".meta");
180 static string MobileSuffix(BuildTarget buildTarget)
182 return (buildTarget == BuildTarget.Android) ?
"so" :
"a";
185 static string MobilePluginPath(BuildTarget buildTarget,
string arch,
bool relative =
false)
187 string os = buildTarget.ToString();
188 return Path.Combine(PluginDir(os, relative), arch, $
"libllamalib_{os.ToLower()}.{MobileSuffix(buildTarget)}");
197 List<string> platforms =
new List<string>();
198 bool checkCUBLAS =
false;
201 case BuildTarget.StandaloneWindows:
202 case BuildTarget.StandaloneWindows64:
203 platforms.Add(
"win-x64");
206 case BuildTarget.StandaloneLinux64:
207 platforms.Add(
"linux-x64");
210 case BuildTarget.StandaloneOSX:
211 platforms.Add(
"osx-universal");
213 case BuildTarget.Android:
214 platforms.Add(
"android-arm64");
215 platforms.Add(
"android-x64");
217 case BuildTarget.iOS:
218 platforms.Add(
"ios-arm64");
220#if UNITY_2022_3_OR_NEWER
221 case BuildTarget.VisionOS:
222 platforms.Add(
"visionos-arm64");
229 string sourceName = Path.GetFileName(source);
230 if (!platforms.Contains(sourceName))
232 string target = Path.Combine(BuildTempDir, sourceName);
233 MoveAction(source, target);
234 MoveAction(source +
".meta", target +
".meta");
240 List<string> exclusionKeywords = LLMUnitySetup.CUBLAS ?
new List<string>() {
"tinyblas" } :
new List<string>() {
"cublas",
"cudart" };
241 foreach (
string platform
in platforms)
244 foreach (
string source
in Directory.GetFiles(platformDir))
246 string sourceName = Path.GetFileName(source);
247 foreach (
string exclusionKeyword
in exclusionKeywords)
249 if (sourceName.Contains(exclusionKeyword))
251 string target = Path.Combine(BuildTempDir, platform,
"native", sourceName);
252 MoveAction(source, target);
253 MoveAction(source +
".meta", target +
".meta");
261 bool isVisionOS =
false;
262#if UNITY_2022_3_OR_NEWER
263 isVisionOS = buildTarget == BuildTarget.VisionOS;
265 if (buildTarget == BuildTarget.Android || buildTarget == BuildTarget.iOS || isVisionOS)
267 foreach (
string platform
in platforms)
269 string source = Path.Combine(
LLMUnitySetup.
libraryPath, platform,
"native", $
"libllamalib_{platform}.{MobileSuffix(buildTarget)}");
270 string target = MobilePluginPath(buildTarget, platform.Split(
"-")[1].ToUpper());
271 string pluginDir = PluginDir(buildTarget.ToString());
272 MoveAction(source, target);
273 MoveAction(source +
".meta", target +
".meta");
274 AddActionAddMeta(pluginDir);
279 static void OnPostprocessAllAssets(
string[] importedAssets,
string[] deletedAssets,
string[] movedAssets,
string[] movedFromAssetPaths,
bool didDomainReload)
281 List<BuildTarget> buildTargets =
new List<BuildTarget>() { BuildTarget.iOS, BuildTarget.Android };
282#if UNITY_2022_3_OR_NEWER
283 buildTargets.Add(BuildTarget.VisionOS);
285 foreach (BuildTarget buildTarget
in buildTargets)
287 string platformDir = Path.Combine(
"Assets", PluginDir(buildTarget.ToString(),
true));
288 if (!Directory.Exists(platformDir))
continue;
289 foreach (
string archDir
in Directory.GetDirectories(platformDir))
291 string arch = Path.GetFileName(archDir);
292 string pathToPlugin = Path.Combine(
"Assets", MobilePluginPath(buildTarget, arch,
true));
293 for (
int i = 0; i < movedAssets.Length; i++)
295 if (movedAssets[i] == pathToPlugin)
297 var importer = AssetImporter.GetAtPath(pathToPlugin) as PluginImporter;
298 if (importer !=
null && importer.isNativePlugin)
300 importer.SetCompatibleWithPlatform(buildTarget,
true);
301 importer.SetPlatformData(buildTarget,
"CPU", arch);
302 AssetDatabase.ImportAsset(pathToPlugin);
322 public static void Build(BuildTarget buildTarget)
325 Directory.CreateDirectory(BuildTempDir);
335 if (!File.Exists(movedCache))
return;
336 List<StringPair> movedPairs = JsonUtility.FromJson<ListStringPair>(File.ReadAllText(movedCache)).pairs;
337 if (movedPairs ==
null)
return;
339 bool refresh =
false;
340 foreach (var pair
in movedPairs)
342 if (pair.source ==
"")
348 if (File.Exists(pair.source) || Directory.Exists(pair.source))
354 refresh |= MoveAction(pair.target, pair.source,
false);
358 if (refresh) AssetDatabase.Refresh();