Skip to content

Commit 2859933

Browse files
committed
Added windows support to build
1 parent f1d5842 commit 2859933

File tree

1 file changed

+118
-33
lines changed

1 file changed

+118
-33
lines changed

hook/build.dart

Lines changed: 118 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,75 @@ import 'dart:io';
22

33
import 'package:code_assets/code_assets.dart';
44
import 'package:hooks/hooks.dart';
5+
import 'package:logging/logging.dart';
6+
import 'package:native_toolchain_c/src/cbuilder/compiler_resolver.dart';
57

68
const version = '3.5.4';
79
const sourceCodeUrl = 'https://github.com/openssl/openssl/releases/download/openssl-$version/openssl-$version.tar.gz';
810
const openSslDirName = 'openssl-$version';
911
const configArgs = ['no-unit-test', 'no-asm', 'no-makedepend', 'no-ssl', 'no-apps', '-Wl,-headerpad_max_install_names'];
12+
// 'no-unit-test no-asm no-makedepend no-ssl no-apps -Wl,-headerpad_max_install_names'
13+
const perlDownloadUrl = 'https://strawberryperl.com/download/5.14.2.1/strawberry-perl-5.14.2.1-64bit-portable.zip';
14+
const jomDownloadUrl = 'https://download.qt.io/official_releases/jom/jom_1_1_5.zip';
15+
16+
Map<String, String> environment = {};
1017

1118
void main(List<String> args) async {
1219
await build(args, (input, output) async {
1320
if (input.config.buildCodeAssets) {
14-
final workDir = Directory(input.outputDirectory.path);
15-
final outputDir = Directory(input.outputDirectoryShared.path);
21+
final workDir = input.outputDirectory;
22+
final outputDir = input.outputDirectoryShared;
1623

1724
// download source code from openssl
18-
await runProcess('curl', ['-L', sourceCodeUrl, '-o', '$openSslDirName.tar.gz'], workingDirectory: workDir);
19-
20-
// unzip source code
21-
await runProcess('tar', ['-xzf', '$openSslDirName.tar.gz'], workingDirectory: workDir);
22-
// remove the tar.gz file
23-
await File(workDir.uri.resolve('$openSslDirName.tar.gz').toFilePath()).delete();
25+
await downloadAndExtract(sourceCodeUrl, '$openSslDirName.tar.gz', workDir, createFolderForExtraction: false);
2426

25-
final openSslDir = Directory(workDir.uri.resolve(openSslDirName).path);
27+
final openSslDir = workDir.resolve('$openSslDirName/');
2628

2729
// build source code, depends on the OS we are running on
2830
// Read https://github.com/openssl/openssl/blob/openssl-3.5.4/INSTALL.md#building-openssl
2931
final configName = resolveConfigName(input.config.code.targetOS, input.config.code.targetArchitecture);
3032
switch (OS.current) {
3133
case OS.windows:
34+
final msvcEnv = await resolveWindowsBuildEnvironment(input.config.code.targetArchitecture);
35+
// should have perl and jom installed
36+
final needDownloadPerl = !await isProgramInstalled('perl');
37+
final needDownloadJom = !await isProgramInstalled('jom');
38+
var perlProgram = 'perl';
39+
var jomProgram = 'jom';
40+
41+
if (needDownloadPerl) {
42+
await downloadAndExtract(perlDownloadUrl, 'perl.zip', workDir);
43+
perlProgram = workDir.resolve('./perl/perl/bin/perl.exe').toFilePath(windows: Platform.isWindows);
44+
}
45+
if (needDownloadJom) {
46+
await downloadAndExtract(jomDownloadUrl, 'jom.zip', workDir);
47+
jomProgram = workDir.resolve('./jom/jom.exe').toFilePath(windows: Platform.isWindows);
48+
}
49+
3250
// run ./Configure with the target OS and architecture
33-
await runProcess('perl', [
34-
'Configure',
35-
configName,
36-
...configArgs,
37-
// needed to build using multiple threads on Windows
38-
'/FS',
39-
], workingDirectory: openSslDir);
51+
await runProcess(
52+
perlProgram,
53+
[
54+
'Configure',
55+
configName,
56+
...configArgs,
57+
// needed to build using multiple threads on Windows
58+
'/FS',
59+
],
60+
workingDirectory: openSslDir,
61+
extraEnvironment: msvcEnv,
62+
);
4063

4164
// run jom to build the library
42-
await runProcess('jom', [
43-
// TODO: don't know if this is needed
44-
// 'build_sw',
45-
'-j',
46-
'${Platform.numberOfProcessors}',
47-
'-c',
48-
'user.openssl:windows_use_jom=True',
49-
], workingDirectory: openSslDir);
65+
await runProcess(jomProgram, ['-j', '${Platform.numberOfProcessors}'], workingDirectory: openSslDir, extraEnvironment: msvcEnv);
66+
67+
// delete perl and jom if downloaded
68+
if (needDownloadPerl) {
69+
await Directory(workDir.resolve('perl').toFilePath(windows: Platform.isWindows)).delete(recursive: true);
70+
}
71+
if (needDownloadJom) {
72+
await Directory(workDir.resolve('jom').toFilePath(windows: Platform.isWindows)).delete(recursive: true);
73+
}
5074
break;
5175
case OS.macOS:
5276
case OS.linux:
@@ -60,30 +84,30 @@ void main(List<String> args) async {
6084

6185
// copy the library to the output directory
6286
final libName = switch ((input.config.code.targetOS, input.config.code.linkModePreference)) {
63-
(OS.windows, LinkModePreference.static || LinkModePreference.preferStatic) => 'libcrypto.lib',
87+
(OS.windows, LinkModePreference.static || LinkModePreference.preferStatic) => 'libcrypto_static.lib',
6488
(OS.macOS, LinkModePreference.static || LinkModePreference.preferStatic) => 'libcrypto.a',
6589
(OS.linux, LinkModePreference.static || LinkModePreference.preferStatic) => 'libcrypto.a',
66-
(OS.windows, LinkModePreference.dynamic || LinkModePreference.preferDynamic) => 'libcrypto.dll',
90+
(OS.windows, LinkModePreference.dynamic || LinkModePreference.preferDynamic) => 'libcrypto-3-${input.config.code.targetArchitecture.name}.dll',
6791
(OS.macOS, LinkModePreference.dynamic || LinkModePreference.preferDynamic) => 'libcrypto.dylib',
6892
(OS.linux, LinkModePreference.dynamic || LinkModePreference.preferDynamic) => 'libcrypto.so',
6993
_ => throw UnsupportedError(
7094
'Unsupported target OS: ${input.config.code.targetOS.name} or link mode preference: ${input.config.code.linkModePreference.name}',
7195
),
7296
};
7397

74-
final libPath = outputDir.uri.resolve(libName).toFilePath();
75-
await File(openSslDir.uri.resolve(libName).path).copy(libPath);
98+
final libPath = outputDir.resolve(libName).toFilePath(windows: Platform.isWindows);
99+
await File(openSslDir.resolve(libName).toFilePath(windows: Platform.isWindows)).copy(libPath);
76100

77101
// delete the source code
78-
await openSslDir.delete(recursive: true);
102+
await Directory(openSslDir.toFilePath(windows: Platform.isWindows)).delete(recursive: true);
79103

80104
// add the library to dart code assets
81105
output.assets.code.add(
82106
CodeAsset(
83107
package: input.packageName,
84108
name: 'src/third_party/openssl.g.dart',
85109
linkMode: libName.linkMode,
86-
file: outputDir.uri.resolve(libName),
110+
file: outputDir.resolve(libName),
87111
),
88112
);
89113
}
@@ -99,6 +123,15 @@ extension on String {
99123
}
100124
}
101125

126+
Future<bool> isProgramInstalled(String programName) async {
127+
try {
128+
await runProcess(programName, []);
129+
return true;
130+
} catch (e) {
131+
return false;
132+
}
133+
}
134+
102135
String resolveConfigName(OS os, Architecture architecture) {
103136
return switch ((os, architecture)) {
104137
(OS.android, Architecture.arm) => 'android-arm',
@@ -132,13 +165,65 @@ String resolveConfigName(OS os, Architecture architecture) {
132165
};
133166
}
134167

135-
Future<void> runProcess(String executable, List<String> arguments, {Directory? workingDirectory}) async {
136-
final processResult = await Process.run(executable, arguments, workingDirectory: workingDirectory?.path);
168+
Future<Map<String, String>> resolveWindowsBuildEnvironment(Architecture architecture) async {
169+
final result = await runProcess('cmd.exe', [
170+
'/c',
171+
r'call C:\"Program Files"\"Microsoft Visual Studio"\2022\Community\VC\Auxiliary\Build\vcvars64.bat >nul && set',
172+
]);
173+
174+
return Map.fromEntries(
175+
result.trim().split('\n').map((line) {
176+
final parts = line.split('=');
177+
if (parts.length != 2) {
178+
return null;
179+
}
180+
return MapEntry(parts[0].trim(), parts[1].trim());
181+
}).nonNulls,
182+
);
183+
}
184+
185+
Future<String> runProcess(
186+
String executable,
187+
List<String> arguments, {
188+
Uri? workingDirectory,
189+
Map<String, String>? extraEnvironment,
190+
}) async {
191+
final processResult = await Process.run(
192+
executable,
193+
arguments,
194+
workingDirectory: workingDirectory?.toFilePath(windows: Platform.isWindows),
195+
environment: {...?extraEnvironment, ...environment},
196+
includeParentEnvironment: true,
197+
);
137198
print(processResult.stdout);
138199
if ((processResult.stderr as String).isNotEmpty) {
139200
print(processResult.stderr);
140201
}
141202
if (processResult.exitCode != 0) {
142-
throw ProcessException(executable, arguments, '', processResult.exitCode);
203+
throw ProcessException(executable, arguments, processResult.stderr, processResult.exitCode);
143204
}
205+
return processResult.stdout.toString();
206+
}
207+
208+
Future<void> downloadAndExtract(
209+
String url,
210+
String outputFileName,
211+
Uri workDir, {
212+
bool createFolderForExtraction = true,
213+
}) async {
214+
// download the file
215+
await runProcess('curl', ['-L', url, '-o', outputFileName], workingDirectory: workDir);
216+
217+
// unzip the file
218+
final isTarGz = outputFileName.endsWith('.tar.gz');
219+
final destinationPath = workDir.resolve(outputFileName.replaceAll('.tar.gz', '').replaceAll('.zip', ''));
220+
await Directory(destinationPath.toFilePath(windows: Platform.isWindows)).create(recursive: true);
221+
await runProcess('tar', [
222+
isTarGz ? '-xzf' : '-xf',
223+
outputFileName,
224+
if (createFolderForExtraction) ...['-C', destinationPath.toFilePath(windows: Platform.isWindows)],
225+
], workingDirectory: workDir);
226+
227+
// remove the tar.gz file
228+
await File(workDir.resolve(outputFileName).toFilePath(windows: Platform.isWindows)).delete();
144229
}

0 commit comments

Comments
 (0)