Skip to content
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
80 changes: 80 additions & 0 deletions .github/workflows/win32_prebuilt_assets.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: win32_prebuilt_assets

permissions:
contents: write

on:
pull_request:
branches: [main]
paths:
- .github/workflows/win32_prebuilt_assets.yml
- packages/win32/hook/**
- packages/win32/lib/**
- packages/win32/src/**
- packages/win32/tool/**
push:
tags:
- win32-prebuilt-assets-*
workflow_dispatch:

jobs:
build:
runs-on: windows-latest

defaults:
run:
working-directory: packages/win32

steps:
- name: πŸ“š Git Checkout
uses: actions/checkout@v6

- name: 🐦 Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
cache: true
cache-key: flutter-:os:-:channel:-:version:-:arch:-:hash:-${{ hashFiles('**/pubspec.lock') }}

- name: πŸ“¦ Install Dependencies
run: flutter pub get

- name: πŸ‘· Build Windows host
run: |
dart run tool/build.dart -owindows -aarm64
dart run tool/build.dart -owindows -ax64

- name: πŸ“‘ Upload artifacts
uses: actions/upload-artifact@v7
with:
name: windows-host
path: packages/win32/.dart_tool/win32/**/*.dll
if-no-files-found: error

release:
needs: build
if: startsWith(github.ref, 'refs/tags/win32-prebuilt-assets')
runs-on: ubuntu-latest

defaults:
run:
working-directory: packages/win32

steps:
- name: πŸ“š Git Checkout
uses: actions/checkout@v6

- name: ⬇️ Download assets
uses: actions/download-artifact@v8
with:
merge-multiple: true
path: packages/win32/.dart_tool/win32/

- name: πŸ” Display structure of downloaded assets
run: ls -R .dart_tool/win32/

- name: πŸš€ Create GitHub Release
uses: softprops/action-gh-release@v3
with:
files: packages/win32/.dart_tool/win32/**
fail_on_unmatched_files: true
29 changes: 11 additions & 18 deletions packages/generator/bin/generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -272,18 +272,21 @@ void generateDynamicLibrary(
writeToFile(filePath, buffer.toString());
}

/// Updates the two constants in `packages/win32/hook/build.dart`:
/// Updates the two constants in
/// `packages/win32/lib/src/hook_helpers/resources.g.dart`:
/// - `dynamicLibraries`: all dynamic libraries projected from the Win32 API
/// - `sources`: all dynamic libraries with wrappers preserving `GetLastError()`
/// results
///
/// These constants are used in the build process to determine which dynamic
/// libraries to include as code assets.
void updateDynamicLibrariesConstant(
void updateResources(
Set<winmd.ModuleRef> dynamicLibraries,
Set<winmd.ModuleRef> wrappedLibraries,
) {
final generatedBlock = StringBuffer()
..writeln('// THIS FILE IS AUTOGENERATED. DO NOT EDIT THIS FILE DIRECTLY.')
..writeln()
..writeln('const dynamicLibraries = {')
..writeAll(dynamicLibraries.map((l) => " '${l.name.toLowerCase()}',\n"))
..writeln('};')
Expand All @@ -294,21 +297,11 @@ void updateDynamicLibrariesConstant(
)
..writeln('];');

final buildFile = io.File(
io.Platform.script.resolve('../../win32/hook/build.dart').toFilePath(),
);

final content = buildFile.readAsStringSync();
final insertionPoint = content.indexOf('const dynamicLibraries = {');
if (insertionPoint == -1) {
throw StateError(
'Could not find `const dynamicLibraries` anchor in ${buildFile.path}',
);
}

buildFile.writeAsStringSync(
content.substring(0, insertionPoint) + generatedBlock.toString(),
);
io.File(
io.Platform.script
.resolve('../../win32/lib/src/hook_helpers/resources.g.dart')
.toFilePath(),
).writeAsStringSync(generatedBlock.toString());
}

/// Generates a Dart file that exports all the dynamic libraries projected from
Expand Down Expand Up @@ -594,7 +587,7 @@ void generateFunctions() {
);
}

updateDynamicLibrariesConstant(dynamicLibraries, wrappedLibraries);
updateResources(dynamicLibraries, wrappedLibraries);
generateDynamicLibraryExports(dynamicLibraries);
logger.info('πŸš€ Total functions generated: ${functions.length}');
}
Expand Down
200 changes: 36 additions & 164 deletions packages/win32/hook/build.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,175 +2,47 @@ import 'dart:io';

import 'package:code_assets/code_assets.dart';
import 'package:hooks/hooks.dart';
import 'package:logging/logging.dart';
import 'package:native_toolchain_c/native_toolchain_c.dart';

const win32ImportLibraries = [
'Advapi32',
'Bthprops',
'Crypt32',
'Dbghelp',
'Dxva2',
'Gdi32',
'Kernel32',
'KtmW32',
'Magnification',
'ole32',
'OleAut32',
'Psapi',
'runtimeobject',
'Setupapi',
'Shell32',
'User32',
'Version',
'Wevtapi',
'Winscard',
'Winspool',
'Wlanapi',
'Ws2_32',
'Wtsapi32',
];

Logger createDefaultLogger() {
final logger = Logger.detached('CBuilder')
..level = .INFO
..onRecord.listen((record) {
if (record.message.contains(
'Microsoft (R) C/C++ Optimizing Compiler Version',
)) {
// Skip logging compiler version info.
return;
}

if (record.message.contains('Copyright (C) Microsoft Corporation.')) {
// Skip logging copyright info.
return;
}

if (record.level >= .WARNING) {
stderr.writeln(record.message);
} else {
stdout.writeln(record.message);
}
if (record.error != null) {
stderr.writeln(record.error);
}
if (record.stackTrace != null) {
stderr.writeln(record.stackTrace);
}
});
return logger;
}
import 'package:win32/src/hook_helpers/c_build.dart';
import 'package:win32/src/hook_helpers/download.dart';
import 'package:win32/src/hook_helpers/hashes.g.dart';

void main(List<String> args) async {
await build(args, (input, output) async {
if (!input.config.buildCodeAssets) return;
if (input.config.code.targetOS != .windows) return;

final packageName = input.packageName;
final cbuilder = CBuilder.library(
name: packageName,
assetName: '$packageName.dart',
sources: sources,
libraries: win32ImportLibraries,
flags: const ['/nologo'],
);

String normalizeDynamicLibraryName(String library) =>
library.replaceAll('-', '_').split('.').first;

CodeAsset createCodeAsset(String library) => .new(
package: packageName,
name: 'src/win32/${normalizeDynamicLibraryName(library)}.g.dart',
linkMode: DynamicLoadingSystem(.file(library)),
);

output.assets.code.addAll(dynamicLibraries.map(createCodeAsset));

await cbuilder.run(
input: input,
output: output,
logger: createDefaultLogger(),
);
addDynamicLibrariesAsCodeAssets(output);

final localBuild = input.userDefines['local_build'] as bool? ?? false;
if (localBuild) {
await runBuild(input, output);
} else {
final CodeConfig(:targetOS, :targetArchitecture) = input.config.code;
final outputDirectory = Directory.fromUri(input.outputDirectory);
final file = await downloadAsset(
targetOS,
targetArchitecture,
outputDirectory,
);
final fileHash = await hashAsset(file);
final expectedHash =
assetHashes[targetOS.dylibFileName(
createTargetName(targetOS, targetArchitecture),
)];
if (fileHash != expectedHash) {
throw Exception(
'File $file was not downloaded correctly. '
'Found hash $fileHash, expected $expectedHash.',
);
}
output.assets.code.add(
.new(
package: 'win32',
name: 'win32.dart',
linkMode: DynamicLoadingBundled(),
file: file.uri,
),
);
}
});
}

const dynamicLibraries = {
'advapi32.dll',
'api-ms-win-core-apiquery-l2-1-0.dll',
'api-ms-win-core-comm-l1-1-1.dll',
'api-ms-win-core-comm-l1-1-2.dll',
'api-ms-win-core-handle-l1-1-0.dll',
'api-ms-win-core-path-l1-1-0.dll',
'api-ms-win-core-sysinfo-l1-2-3.dll',
'api-ms-win-core-winrt-error-l1-1-0.dll',
'api-ms-win-core-winrt-l1-1-0.dll',
'api-ms-win-core-winrt-string-l1-1-0.dll',
'api-ms-win-ro-typeresolution-l1-1-0.dll',
'api-ms-win-ro-typeresolution-l1-1-1.dll',
'api-ms-win-service-core-l1-1-3.dll',
'api-ms-win-service-core-l1-1-4.dll',
'api-ms-win-service-core-l1-1-5.dll',
'api-ms-win-shcore-scaling-l1-1-1.dll',
'api-ms-win-wsl-api-l1-1-0.dll',
'bluetoothapis.dll',
'bthprops.cpl',
'comctl32.dll',
'comdlg32.dll',
'crypt32.dll',
'dbghelp.dll',
'dwmapi.dll',
'dxva2.dll',
'gdi32.dll',
'iphlpapi.dll',
'kernel32.dll',
'ktmw32.dll',
'magnification.dll',
'netapi32.dll',
'ntdll.dll',
'ole32.dll',
'oleaut32.dll',
'powrprof.dll',
'propsys.dll',
'psapi.dll',
'rometadata.dll',
'scarddlg.dll',
'setupapi.dll',
'shell32.dll',
'shlwapi.dll',
'user32.dll',
'uxtheme.dll',
'version.dll',
'wevtapi.dll',
'winmm.dll',
'winscard.dll',
'winspool.drv',
'wlanapi.dll',
'ws2_32.dll',
'wtsapi32.dll',
'xinput1_4.dll',
};

const sources = [
'src/advapi32.g.c',
'src/bluetoothapis.g.c',
'src/bthprops.g.c',
'src/crypt32.g.c',
'src/dbghelp.g.c',
'src/dxva2.g.c',
'src/gdi32.g.c',
'src/kernel32.g.c',
'src/ktmw32.g.c',
'src/magnification.g.c',
'src/psapi.g.c',
'src/setupapi.g.c',
'src/shell32.g.c',
'src/user32.g.c',
'src/version.g.c',
'src/wevtapi.g.c',
'src/winscard.g.c',
'src/winspool.g.c',
'src/wlanapi.g.c',
'src/ws2_32.g.c',
'src/wtsapi32.g.c',
];
Loading
Loading