@@ -2,51 +2,75 @@ import 'dart:io';
22
33import 'package:code_assets/code_assets.dart' ;
44import 'package:hooks/hooks.dart' ;
5+ import 'package:logging/logging.dart' ;
6+ import 'package:native_toolchain_c/src/cbuilder/compiler_resolver.dart' ;
57
68const version = '3.5.4' ;
79const sourceCodeUrl = 'https://github.com/openssl/openssl/releases/download/openssl-$version /openssl-$version .tar.gz' ;
810const openSslDirName = 'openssl-$version ' ;
911const 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
1118void 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+
102135String 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