From de97890634edeb9ea6978b53931cf0027e50b5bd Mon Sep 17 00:00:00 2001 From: istalri Date: Fri, 29 May 2026 09:31:14 +0200 Subject: [PATCH] Changed generate.dart script to dynamically look for needed files for linux/windows/macos with newest version --- scripts/generate.dart | 207 ++++++++++++++++++++++++++++++------------ 1 file changed, 151 insertions(+), 56 deletions(-) diff --git a/scripts/generate.dart b/scripts/generate.dart index f6b9512..9ce40f6 100644 --- a/scripts/generate.dart +++ b/scripts/generate.dart @@ -4,8 +4,7 @@ import "package:path/path.dart" as path; void main(List args) async { final Directory repoDir = Directory.fromUri(Platform.script.resolve("../gomuks")); - - print("Generating FFI Bindings..."); + print("Generating FFI Bindings for ${Platform.operatingSystem}..."); int? parseVersion(String name) { final RegExpMatch? match = RegExp(r"^(\d+)(?:\.(\d+))?(?:\.(\d+))?$").firstMatch(name); @@ -17,66 +16,148 @@ void main(List args) async { final int major = int.tryParse(match.group(1)!) ?? 0; final int minor = int.tryParse(match.group(2) ?? "0") ?? 0; final int patch = int.tryParse(match.group(3) ?? "0") ?? 0; - return major * 10000 + minor * 100 + patch; //Single number to compare versions based on integer math + return major * 10000 + minor * 100 + patch; } - final Directory gccBaseDir = Directory("/usr/lib/gcc/x86_64-pc-linux-gnu"); - String? latestGccIncludePath; + final List systemIncludePaths = []; + late String targetTriple; + late String libclangName; - if (await gccBaseDir.exists()) { - final Iterable entries = gccBaseDir.listSync().whereType(); + if (Platform.isLinux) { + targetTriple = "x86_64-pc-linux-gnu"; + libclangName = "libclang.so"; + + final Directory gccBaseDir = Directory("/usr/lib/gcc/x86_64-pc-linux-gnu"); + + if (await gccBaseDir.exists()) { + final List dirs = gccBaseDir.listSync().whereType().toList(); + + dirs.sort((a, b) { + final int va = parseVersion(path.basename(a.path)) ?? 0; + final int vb = parseVersion(path.basename(b.path)) ?? 0; + return vb.compareTo(va); + }); + + if (dirs.isNotEmpty) { + final Directory latest = dirs.first; + final String inc = path.join(latest.path, "include"); + systemIncludePaths.add(inc); + systemIncludePaths.add(path.dirname(inc)); + print("Using GCC: ${path.basename(latest.path)}"); + } + } + + final Directory clangDir = Directory("/usr/lib/clang"); + + if (await clangDir.exists()) { + final List dirs = clangDir.listSync().whereType().toList(); + dirs.sort((a, b) { + final int va = parseVersion(path.basename(a.path)) ?? 0; + final int vb = parseVersion(path.basename(b.path)) ?? 0; + return vb.compareTo(va); + }); + + if (dirs.isNotEmpty) { + systemIncludePaths.add(path.join(dirs.first.path, "include")); + } + } + + systemIncludePaths.addAll(["/usr/include", "/usr/include/x86_64-linux-gnu", "/usr/local/include"]); + + } else if (Platform.isMacOS) { + final String arch = _detectMacArch(); + targetTriple = "$arch-apple-darwin"; + libclangName = "libclang.dylib"; + + final String? sdkPath = _runCommand("xcrun", ["--show-sdk-path"]); + if (sdkPath != null) { + systemIncludePaths.add(path.join(sdkPath, "usr", "include")); + print("Using Xcode SDK: $sdkPath"); + } + + final String? clangResDir = _runCommand("clang", ["-print-resource-dir"]); + if (clangResDir != null) { + final String inc = path.join(clangResDir, "include"); + + if (Directory(inc).existsSync()) { + systemIncludePaths.add(inc); + } + } + + final String brewPrefix = arch == "arm64" ? "/opt/homebrew" : "/usr/local"; + final Directory brewGcc = Directory(path.join(brewPrefix, "opt", "gcc", "lib", "gcc", "current", "include")); + + if (brewGcc.existsSync()) { + systemIncludePaths.add(brewGcc.path); + print("Using Homebrew GCC"); + } + + systemIncludePaths.addAll(["$brewPrefix/include", "/usr/local/include"]); + + } else if (Platform.isWindows) { + + targetTriple = "x86_64-pc-windows-msvc"; + libclangName = "libclang.dll"; + + final String vswhere = r"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"; + final String? vsPath = _runCommand(vswhere, ["-latest", "-property", "installationPath"]); + + if (vsPath != null) { + final Directory msvcDir = Directory(path.join(vsPath, "VC", "Tools", "MSVC")); + + if (msvcDir.existsSync()) { + final List dirs = msvcDir.listSync().whereType().toList(); + dirs.sort((a, b) { + final int va = parseVersion(path.basename(a.path)) ?? 0; + final int vb = parseVersion(path.basename(b.path)) ?? 0; + return vb.compareTo(va); + }); + + if (dirs.isNotEmpty) { + systemIncludePaths.add(path.join(dirs.first.path, "include")); + print("Using MSVC: ${path.basename(dirs.first.path)}"); + } + } + } + + final Directory sdkDir = Directory(r"C:\Program Files (x86)\Windows Kits\10\Include"); + if (sdkDir.existsSync()) { + final List dirs = sdkDir.listSync().whereType().toList(); + + dirs.sort((a, b) { + final int va = parseVersion(path.basename(a.path)) ?? 0; + final int vb = parseVersion(path.basename(b.path)) ?? 0; + return vb.compareTo(va); + }); + + if (dirs.isNotEmpty) { + final String sdkVer = dirs.first.path; + for (final String sub in ["um", "ucrt", "shared"]) { + final String p = path.join(sdkVer, sub); + if (Directory(p).existsSync()) systemIncludePaths.add(p); + } + print("Using Windows SDK: ${path.basename(sdkVer)}"); + } + } + + final Directory llvmDir = Directory(r"C:\Program Files\LLVM\include"); - final List versionDirs = entries.where((dir) { - final String name = path.basename(dir.path); - return parseVersion(name) != null; - }).toList(); - - if (versionDirs.isNotEmpty) { - versionDirs.sort((a, b) { - final int verA = parseVersion(path.basename(a.path))!; - final int verB = parseVersion(path.basename(b.path))!; - return verB.compareTo(verA); // Descending - }); - - final Directory latestDir = versionDirs.first; - latestGccIncludePath = path.join(latestDir.path, "include"); - print("Successfully detected GCC version: ${path.basename(latestDir.path)} -> Using: $latestGccIncludePath"); - } else { - print("!Warning!: No GCC version directories found in ${gccBaseDir.path}"); + if (llvmDir.existsSync()) { + systemIncludePaths.add(llvmDir.path); } + } else { - print("!Warning!: GCC base directory not found at ${gccBaseDir.path}"); + + print("Error: Unsupported platform: ${Platform.operatingSystem}"); + exit(1); + } - final List systemIncludePaths = []; - - if (latestGccIncludePath != null) { - systemIncludePaths.add(latestGccIncludePath); - systemIncludePaths.add(path.dirname(latestGccIncludePath)); - } - - final Directory clangDir = Directory("/usr/lib/clang"); - if (await clangDir.exists()) { - final List clangEntries = clangDir.listSync().whereType().toList(); - if (clangEntries.isNotEmpty) { - clangEntries.sort((a, b) { - final verA = parseVersion(path.basename(a.path)) ?? 0; - final verB = parseVersion(path.basename(b.path)) ?? 0; - return verB.compareTo(verA); - }); - systemIncludePaths.add(path.join(clangEntries.first.path, "include")); - } - } - -//Standard Paths - systemIncludePaths.addAll([ - "/usr/include", - "/usr/include/x86_64-linux-gnu", - "/usr/local/include", - ]); + print("Target: $targetTriple"); + print("Include paths: ${systemIncludePaths.length} found"); final String? libclangPath = Platform.environment["LIBCLANG_PATH"]; - + FfiGenerator( output: Output( dartFile: Platform.script.resolve("../lib/src/third_party/gomuks.g.dart"), @@ -85,16 +166,30 @@ void main(List args) async { entryPoints: [File(path.join(repoDir.path, "pkg", "ffi", "gomuksffi.h")).uri], compilerOptions: [ "--no-warnings", - "-target", "x86_64-pc-linux-gnu", - ...systemIncludePaths.map((path) => "-I$path"), + "-target", targetTriple, + ...systemIncludePaths.map((p) => "-I$p"), ], ), functions: Functions.includeAll, ).generate( libclangDylib: libclangPath == null ? null - : Uri.file(path.join(libclangPath, "libclang.so")), + : Uri.file(path.join(libclangPath, libclangName)), ); - + print("Done!"); +} + +String _detectMacArch() { + final String? res = _runCommand("uname", ["-m"]); + return res?.trim() == "arm64" ? "arm64" : "x86_64"; +} + +String? _runCommand(String exe, List args) { + try { + final ProcessResult res = Process.runSync(exe, args); + return res.exitCode == 0 ? (res.stdout as String).trim() : null; + } catch (_) { + return null; + } } \ No newline at end of file