1+ namespace thirdparty . icu ;
2+
3+ using System . ComponentModel ;
4+ using System . Reflection ;
5+ using System . Runtime . CompilerServices ;
6+ using System . Runtime . InteropServices ;
7+ using System . Diagnostics ;
8+ using ebuild . api ;
9+
10+
11+ public class IcuData : ModuleBase
12+ {
13+ Process RunAndWait ( string name , ProcessStartInfo psi )
14+ {
15+ return RunAndWait ( name , psi , out _ , out _ ) ;
16+ }
17+ Process RunAndWait ( string name , ProcessStartInfo psi , out string standardOut )
18+ {
19+ return RunAndWait ( name , psi , out standardOut , out _ ) ;
20+ }
21+
22+ Process RunAndWait ( string name , ProcessStartInfo psi , out string standardOut , out string standardError )
23+ {
24+ var process = Process . Start ( psi ) ?? throw new Exception ( $ "Failed to start process { psi . FileName } { psi . Arguments } .") ;
25+ var _out = string . Empty ;
26+ var _err = string . Empty ;
27+ process . OutputDataReceived += ( sender , e ) =>
28+ {
29+ if ( e . Data != null )
30+ {
31+ _out += e . Data + Environment . NewLine ;
32+ Console . WriteLine ( $ "[{ name } ] info: { e . Data } ") ;
33+ }
34+ } ;
35+ process . ErrorDataReceived += ( sender , e ) =>
36+ {
37+ if ( e . Data != null )
38+ {
39+ _err += e . Data + Environment . NewLine ;
40+ Console . Error . WriteLine ( $ "[{ name } ] error: { e . Data } ") ;
41+ }
42+ } ;
43+ process . BeginErrorReadLine ( ) ;
44+ process . BeginOutputReadLine ( ) ;
45+ process . WaitForExit ( ) ;
46+ standardOut = _out ;
47+ standardError = _err ;
48+ if ( process . ExitCode != 0 )
49+ {
50+ throw new Exception ( $ "[{ name } ] Process failed with exit code { process . ExitCode } . Error: { standardError } ") ;
51+ }
52+ return process ;
53+ }
54+
55+ public enum DataPackageType
56+ {
57+ Common ,
58+ Static ,
59+ Shared
60+ }
61+
62+ DataPackageType PackageType = DataPackageType . Common ;
63+
64+ [ ModuleOption ( ChangesResultBinary = false , Description = "Set maximum number of parallel processes when compiling dependencies. Default is number of processors." ) ]
65+ int MaxDependencyCompilationProcesses = Environment . ProcessorCount ;
66+ public IcuData ( ModuleContext context ) : base ( context )
67+ {
68+ this . Type = ModuleType . LibraryLoader ;
69+ this . Name = "icudt" ;
70+ this . OutputDirectory = "Binaries/icudt" ;
71+ this . UseVariants = false ;
72+
73+ Directory . CreateDirectory ( GetBinaryOutputDirectory ( ) ) ;
74+
75+ PackageType = context . SelfReference . GetOutput ( ) switch
76+ {
77+ "static" => DataPackageType . Static ,
78+ "shared" => DataPackageType . Shared ,
79+ "common" => DataPackageType . Common ,
80+ _ => throw new Exception ( "Invalid output type for IcuData module. Must be either static, shared or common." ) ,
81+ } ;
82+ var toLinkFile = string . Empty ;
83+ if ( PackageType == DataPackageType . Shared || PackageType == DataPackageType . Static )
84+ {
85+ if ( context . Platform . Name == "windows" )
86+ {
87+ toLinkFile = Path . Join ( context . ModuleDirectory . FullName , "Binaries" , "icudt" , "default" , ( PackageType == DataPackageType . Static ? "s" : string . Empty ) + "icudt77" + context . Platform . ExtensionForStaticLibrary ) ;
88+ }
89+ else if ( context . Platform . Name == "unix" )
90+ {
91+ toLinkFile = Path . Join ( context . ModuleDirectory . FullName , "Binaries" , "icudt" , "default" , "libicudt77" + context . Platform . ExtensionForStaticLibrary ) ;
92+ }
93+ this . Libraries . Public . Add ( toLinkFile ) ;
94+ }
95+ if ( File . Exists ( toLinkFile ) )
96+ {
97+ // already built, nothing to do.
98+ return ;
99+ }
100+ var ebuildPath = typeof ( ModuleBase ) . Assembly . Location . Replace ( "ebuild.api.dll" , "ebuild.dll" ) ;
101+ // If these are not added to prebuild steps like this. The generation or other steps might take too much time.
102+ this . PreBuildSteps . Add ( new ModuleBuildStep ( "Build icupkg" , ( workerType , cancellationToken ) =>
103+ {
104+ if ( ! File . Exists ( Path . Join ( context . ModuleDirectory . FullName , "Binaries" , "icupkg" , "default" , "icupkg" + context . Platform . ExtensionForExecutable ) ) )
105+ {
106+ Console . WriteLine ( "icupkg not found, building icupkg first." ) ;
107+ var processStartArgs = new System . Diagnostics . ProcessStartInfo
108+ {
109+ FileName = "dotnet" ,
110+ Arguments = $ "\" { ebuildPath } \" build { Path . Join ( context . ModuleDirectory . FullName , "icu-icupkg.ebuild.cs" ) } -p { MaxDependencyCompilationProcesses } ",
111+ RedirectStandardOutput = true ,
112+ RedirectStandardError = true ,
113+ UseShellExecute = false ,
114+ CreateNoWindow = true ,
115+ } ;
116+ var process = RunAndWait ( "compileIcuPkg" , processStartArgs ) ;
117+ if ( process . ExitCode != 0 )
118+ {
119+ throw new Exception ( $ "Ebuild process for icu-icupkg failed with exit code { process . ExitCode } .") ;
120+ }
121+ }
122+ return Task . CompletedTask ;
123+ } ) ) ;
124+ this . PreBuildSteps . Add ( new ModuleBuildStep ( "Run icupkg" , ( workerType , cancellationToken ) =>
125+ {
126+ // unpack data if not already unpacked and list file output is not there..
127+ if ( ! Directory . Exists ( Path . Join ( context . ModuleDirectory . FullName , "temp" , "pack_contents" ) ) || ! File . Exists ( Path . Join ( context . ModuleDirectory . FullName , "temp" , "pack_contents.lst" ) ) )
128+ {
129+ Console . WriteLine ( "Unpacking icu data package for processing." ) ;
130+ Directory . CreateDirectory ( Path . Join ( context . ModuleDirectory . FullName , "temp" , "pack_contents" ) ) ;
131+ var processStartArgs = new System . Diagnostics . ProcessStartInfo
132+ {
133+ FileName = Path . Join ( context . ModuleDirectory . FullName , "Binaries" , "icupkg" , "default" , "icupkg" + context . Platform . ExtensionForExecutable ) ,
134+ RedirectStandardOutput = true ,
135+ RedirectStandardError = true ,
136+ StandardOutputEncoding = System . Text . Encoding . UTF8 ,
137+ StandardErrorEncoding = System . Text . Encoding . UTF8 ,
138+ UseShellExecute = false ,
139+ CreateNoWindow = true ,
140+ } ;
141+ string [ ] argumentList = [
142+ "-d" ,
143+ Path . Join ( context . ModuleDirectory . FullName , "temp" , "pack_contents" ) ,
144+ "-x" ,
145+ "*" ,
146+ "-l" ,
147+ Path . Join ( context . ModuleDirectory . FullName , "data" , $ "{ ( BitConverter . IsLittleEndian ? "little" : "big" ) } ", $ "icudt77{ ( BitConverter . IsLittleEndian ? "l" : "b" ) } .dat")
148+ ] ;
149+ foreach ( var arg in argumentList )
150+ {
151+ processStartArgs . ArgumentList . Add ( arg ) ;
152+ }
153+ var process = RunAndWait ( "icupkg" , processStartArgs , out var standardOut , out var standardError ) ;
154+ if ( process . ExitCode == 0 )
155+ {
156+ Console . WriteLine ( "Unpacking icu data package completed successfully." ) ;
157+ }
158+
159+ Console . WriteLine ( "writing pack_contents.lst" ) ;
160+ File . WriteAllText ( Path . Join ( context . ModuleDirectory . FullName , "temp" , "pack_contents.lst" ) , standardOut ) ;
161+ }
162+ else
163+ {
164+ Console . WriteLine ( "icu data already unpacked, skipping unpacking." ) ;
165+ }
166+ return Task . CompletedTask ;
167+ } ) ) ;
168+
169+ this . PreBuildSteps . Add ( new ModuleBuildStep ( "Build pkgdata" , ( workerType , cancellationToken ) =>
170+ {
171+ // pkgdata
172+ if ( ! File . Exists ( Path . Join ( context . ModuleDirectory . FullName , "Binaries" , "pkgdata" , "default" , "pkgdata" + context . Platform . ExtensionForExecutable ) ) )
173+ {
174+ Console . WriteLine ( "pkgdata not found, building pkgdata first." ) ;
175+ var processStartArgs = new System . Diagnostics . ProcessStartInfo
176+ {
177+ FileName = "dotnet" ,
178+ Arguments = $ "\" { ebuildPath } \" build { Path . Join ( context . ModuleDirectory . FullName , "icu-pkgdata.ebuild.cs" ) } -p { MaxDependencyCompilationProcesses } ",
179+ RedirectStandardOutput = true ,
180+ RedirectStandardError = true ,
181+ UseShellExecute = false ,
182+ CreateNoWindow = true ,
183+ StandardErrorEncoding = System . Text . Encoding . UTF8 ,
184+ StandardOutputEncoding = System . Text . Encoding . UTF8 ,
185+ } ;
186+ var process = RunAndWait ( "compilePkgData" , processStartArgs ) ;
187+ }
188+ return Task . CompletedTask ;
189+ } ) ) ;
190+
191+ this . PreBuildSteps . Add ( new ModuleBuildStep ( "run pkgdata" , ( workerType , cancellationToken ) =>
192+ {
193+ if ( ! File . Exists ( toLinkFile ) ) // if the file to link does not exist, we need to build it.
194+ {
195+ Directory . CreateDirectory ( Path . Join ( context . ModuleDirectory . FullName , "temp" , "packaging_temp" ) ) ;
196+ // provide all the env vars for the required tools
197+ Console . WriteLine ( "icu data library not found, building icu data library." ) ;
198+ var processStartArgs = new System . Diagnostics . ProcessStartInfo
199+ {
200+ FileName = Path . Join ( context . ModuleDirectory . FullName , "Binaries" , "pkgdata" , "default" , "pkgdata" + context . Platform . ExtensionForExecutable ) ,
201+ RedirectStandardOutput = true ,
202+ RedirectStandardError = true ,
203+ StandardOutputEncoding = System . Text . Encoding . UTF8 ,
204+ StandardErrorEncoding = System . Text . Encoding . UTF8 ,
205+ UseShellExecute = false ,
206+ CreateNoWindow = true
207+ } ;
208+ List < string > argumentList = [
209+ "-c" ,
210+ "-m" ,
211+ PackageType switch
212+ {
213+ DataPackageType . Common => "common" ,
214+ DataPackageType . Static => "static" ,
215+ DataPackageType . Shared => "library" ,
216+ _ => throw new Exception ( "Invalid package type." ) ,
217+ } ,
218+ "-v" ,
219+ "-d" ,
220+ Path . Join ( context . ModuleDirectory . FullName , "Binaries" , "icudt" , "default" ) ,
221+ "-s" ,
222+ Path . Join ( context . ModuleDirectory . FullName , "temp" , "pack_contents" ) ,
223+ "-p" ,
224+ "icudt77" ,
225+ "-T" ,
226+ Path . Join ( context . ModuleDirectory . FullName , "temp" , "packaging_temp" ) ,
227+ "-L" ,
228+ "icudt77" ,
229+ ] ;
230+ if ( context . Platform . Name == "windows" && context . RequestedOutput == "shared" )
231+ {
232+ argumentList . Add ( "-a" ) ;
233+ argumentList . Add ( context . TargetArchitecture switch
234+ {
235+ Architecture . X64 => "x64" ,
236+ Architecture . X86 => "x86" ,
237+ Architecture . Arm64 => "arm64" ,
238+ Architecture . Arm => "arm" ,
239+ _ => throw new Exception ( "Invalid architecture." )
240+ } ) ;
241+ }
242+ argumentList . Add ( Path . Join ( context . ModuleDirectory . FullName , "temp" , "pack_contents.lst" ) ) ;
243+ foreach ( var arg in argumentList )
244+ {
245+ processStartArgs . ArgumentList . Add ( arg ) ;
246+ }
247+ var compilerFactory = ( context . Toolchain . GetCompilerFactory ( this , context . InstancingParams ?? throw new Exception ( "InstancingParams is null" ) ) ?? throw new Exception ( "CompilerFactory is null" ) ) ! ;
248+ var targetPath = Path . GetDirectoryName ( compilerFactory . GetExecutablePath ( this , context . InstancingParams ?? throw new Exception ( "InstancingParams is null" ) ) ) ;
249+ processStartArgs . Environment [ "PATH" ] = targetPath + ";" + ( Environment . GetEnvironmentVariable ( "PATH" ) ?? string . Empty ) ;
250+ Console . WriteLine ( "Using PATH: " + processStartArgs . Environment [ "PATH" ] ) ;
251+
252+ var process = RunAndWait ( "pkgdata" , processStartArgs ) ;
253+ if ( process . ExitCode == 0 )
254+ {
255+ Console . WriteLine ( "Packaging icu data library completed successfully." ) ;
256+ }
257+ // Copy the output to the binaries directory.
258+ var libFile = Path . Join ( context . ModuleDirectory . FullName , "temp" , "pack_contents" , "icudt77" + context . Platform . ExtensionForStaticLibrary ) ;
259+ if ( File . Exists ( libFile ) )
260+ {
261+ File . Copy ( libFile , Path . Join ( GetBinaryOutputDirectory ( ) , "icudt77" + context . Platform . ExtensionForStaticLibrary ) , true ) ;
262+ File . Delete ( libFile ) ;
263+ }
264+ var dllFile = Path . Join ( context . ModuleDirectory . FullName , "temp" , "pack_contents" , "icudt77" + context . Platform . ExtensionForSharedLibrary ) ;
265+ if ( File . Exists ( dllFile ) )
266+ {
267+ File . Copy ( dllFile , Path . Join ( GetBinaryOutputDirectory ( ) , "icudt77" + context . Platform . ExtensionForSharedLibrary ) , true ) ;
268+ File . Delete ( dllFile ) ;
269+ }
270+ }
271+ return Task . CompletedTask ;
272+ } ) ) ;
273+
274+
275+
276+
277+
278+
279+ }
280+ }
0 commit comments