diff --git a/.github/actions/build-windows/action.yml b/.github/actions/build-windows/action.yml index ab4f160..01506bb 100644 --- a/.github/actions/build-windows/action.yml +++ b/.github/actions/build-windows/action.yml @@ -77,6 +77,7 @@ runs: cmake --build build --parallel --target install mkdir -p D:/export copy D:/libssh2/install/bin/libssh2.dll D:/export/libssh2.dll + Get-ChildItem "$env:OPENSSL_ROOT_DIR/bin/libcrypto*.dll", "$env:OPENSSL_ROOT_DIR/bin/libssl*.dll" -File | Copy-Item -Destination D:/export - name: Checkout libgit2 uses: actions/checkout@v6 diff --git a/CHANGELOG.md b/CHANGELOG.md index 563e9a7..d1bfe20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [1.11.4] - 2026-06-29 +### Fixed +- Windows: bundle OpenSSL runtime DLLs with the generated artifact and load + them before `libssh2.dll`, so plain Dart and Flutter tests no longer depend + on OpenSSL being available on `PATH`. + ## [1.11.2] - 2026-06-04 ### Fixed - macOS: build the release `libgit2.dylib` with libssh2 and OpenSSL linked diff --git a/lib/src/util.dart b/lib/src/util.dart index 55107af..409cd75 100644 --- a/lib/src/util.dart +++ b/lib/src/util.dart @@ -81,7 +81,7 @@ void _loadPlatformDependencies(String packageRoot) { } else if (Platform.isMacOS) { // macOS release artifacts link libssh2/OpenSSL statically into libgit2. } else if (Platform.isWindows) { - DynamicLibrary.open(p.join(packageRoot, 'windows', 'libssh2.dll')); + _loadWindowsDependencies(packageRoot); } } catch (e) { stderr.writeln( @@ -92,6 +92,34 @@ void _loadPlatformDependencies(String packageRoot) { } } +void _loadWindowsDependencies(String packageRoot) { + final windowsDir = Directory(p.join(packageRoot, 'windows')); + if (!windowsDir.existsSync()) { + DynamicLibrary.open(p.join(windowsDir.path, 'libssh2.dll')); + return; + } + + for (final prefix in ['libcrypto', 'libssl']) { + final libraries = + windowsDir + .listSync() + .whereType() + .where( + (file) => + p.basename(file.path).toLowerCase().startsWith(prefix) && + p.extension(file.path).toLowerCase() == '.dll', + ) + .toList() + ..sort((a, b) => a.path.compareTo(b.path)); + + for (final library in libraries) { + DynamicLibrary.open(library.path); + } + } + + DynamicLibrary.open(p.join(windowsDir.path, 'libssh2.dll')); +} + String _resolvePackageRoot() { final packageUri = _tryResolvePackageUri(); if (packageUri != null) { diff --git a/pubspec.yaml b/pubspec.yaml index 49ef7b6..41f33d9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: git2dart_binaries description: Dart bindings to libgit2, provides ability to use libgit2 library in Dart and Flutter. -version: 1.11.2 +version: 1.11.4 repository: https://github.com/DartGit-dev/git2dart_binaries environment: diff --git a/test/windows_packaging_test.dart b/test/windows_packaging_test.dart new file mode 100644 index 0000000..2f8eb71 --- /dev/null +++ b/test/windows_packaging_test.dart @@ -0,0 +1,99 @@ +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:path/path.dart' as p; + +void main() { + group('Windows packaging', () { + test('exports OpenSSL runtime libraries with Windows artifacts', () { + final action = + File( + p.join('.github', 'actions', 'build-windows', 'action.yml'), + ).readAsStringSync(); + + expect(action, contains('libcrypto*.dll')); + expect(action, contains('libssl*.dll')); + expect(action, contains('Copy-Item -Destination D:/export')); + }); + + test('bundles versioned OpenSSL runtime libraries in Flutter apps', () { + final cmake = + File(p.join('windows', 'CMakeLists.txt')).readAsStringSync(); + + expect(cmake, contains('file(GLOB git2dart_binaries_openssl_libraries')); + expect(cmake, contains('libcrypto*.dll')); + expect(cmake, contains('libssl*.dll')); + expect(cmake, isNot(contains('libcrypto-1_1-x64.dll'))); + }); + + test( + 'package-root loader works in a plain Dart process', + () async { + final packageConfig = File('.dart_tool/package_config.json').absolute; + expect(await packageConfig.exists(), isTrue); + + final tempDir = await Directory.systemTemp.createTemp( + 'git2dart_binaries_windows_loader_', + ); + try { + final script = File('${tempDir.path}/load_git2dart_binaries.dart'); + await script.writeAsString(r''' +import 'dart:io'; + +import 'package:git2dart_binaries/src/util.dart' as git2; + +void main() { + final initCount = git2.libgit2.git_libgit2_init(); + if (initCount < 1) { + stderr.writeln('git_libgit2_init returned $initCount'); + exit(1); + } + + git2.libgit2.git_libgit2_shutdown(); + git2.libgit2.git_libgit2_shutdown(); + stdout.writeln('plain-dart-libgit2-ok'); +} +'''); + + final result = await Process.run( + _dartExecutable(), + ['--packages=${packageConfig.path}', script.path], + workingDirectory: Directory.current.path, + ).timeout(const Duration(seconds: 30)); + + expect( + result.exitCode, + 0, + reason: + 'plain Dart loader process failed or crashed.\n' + 'stdout:\n${result.stdout}\n' + 'stderr:\n${result.stderr}', + ); + expect(result.stdout, contains('plain-dart-libgit2-ok')); + } finally { + await tempDir.delete(recursive: true); + } + }, + skip: + _canRunWindowsLoaderTest() + ? null + : 'Windows loader test requires generated bindings and artifacts', + ); + }); +} + +bool _canRunWindowsLoaderTest() { + return Platform.isWindows && + File(p.join('lib', 'src', 'bindings.dart')).existsSync() && + File(p.join('windows', 'libgit2.dll')).existsSync() && + File(p.join('windows', 'libssh2.dll')).existsSync() && + Directory('windows').listSync().whereType().any((file) { + final name = p.basename(file.path).toLowerCase(); + return name.startsWith('libcrypto') && name.endsWith('.dll'); + }); +} + +String _dartExecutable() { + final executable = File(Platform.resolvedExecutable).uri.pathSegments.last; + return executable == 'dart' ? Platform.resolvedExecutable : 'dart'; +} diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index b803711..186c592 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -47,8 +47,13 @@ target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) # List of absolute paths to libraries that should be bundled with the plugin. # This list could contain prebuilt libraries, or libraries created by an # external build triggered from this build file. +file(GLOB git2dart_binaries_openssl_libraries + "${CMAKE_CURRENT_SOURCE_DIR}/libcrypto*.dll" + "${CMAKE_CURRENT_SOURCE_DIR}/libssl*.dll" +) + set(git2dart_binaries_bundled_libraries - "${CMAKE_CURRENT_SOURCE_DIR}/libcrypto-1_1-x64.dll" + ${git2dart_binaries_openssl_libraries} "${CMAKE_CURRENT_SOURCE_DIR}/libgit2.dll" "${CMAKE_CURRENT_SOURCE_DIR}/libssh2.dll" PARENT_SCOPE