Skip to content

Commit b11100a

Browse files
committed
Add ICU module implementations and enhance build process for various components
1 parent d854be1 commit b11100a

File tree

10 files changed

+744
-3
lines changed

10 files changed

+744
-3
lines changed

ebuild/EBuild.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,9 @@ public static void InitializeEBuild()
118118

119119
Console.OutputEncoding = Encoding.UTF8;
120120
Console.InputEncoding = Encoding.UTF8;
121-
/// Read the plugins dir to load plugins from
122-
Directory.GetFiles(Path.GetDirectoryName(typeof(EBuild).Assembly.Location)!, "*.ebuild.plugin.dll").ToList()
121+
// Read the plugins dir to load plugins from
122+
// This is disabled for security reasons for now. A better and safe plugin system is needed.
123+
/* Directory.GetFiles(Path.GetDirectoryName(typeof(EBuild).Assembly.Location)!, "*.ebuild.plugin.dll").ToList()
123124
.ForEach(pluginPath =>
124125
{
125126
try
@@ -138,7 +139,7 @@ public static void InitializeEBuild()
138139
{
139140
LoggerFactory.CreateLogger("EBuild").LogError(ex, $"Failed to load plugin from {pluginPath}");
140141
}
141-
});
142+
}); */
142143
List<Assembly> toLoadFromAssemblies = [typeof(EBuild).Assembly];
143144
foreach (var plugin in LoadedPlugins)
144145
{

examples/icu/icu-common.ebuild.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
namespace thirdparty.icu;
2+
3+
using ebuild.api;
4+
5+
6+
7+
8+
9+
public class IcuCommon : ModuleBase
10+
{
11+
public IcuCommon(ModuleContext context) : base(context)
12+
{
13+
this.Type = ModuleType.SharedLibrary;
14+
this.Name = "icuuc";
15+
this.OutputDirectory = "Binaries/icuuc";
16+
this.UseVariants = false;
17+
this.CppStandard = CppStandards.Cpp20;
18+
var sourceBase = Path.Join(context.ModuleDirectory.FullName, "source", "icu", "source", "common");
19+
20+
Definitions.Private.Add("U_COMMON_IMPLEMENTATION=1");
21+
if (context.Platform.Name == "windows")
22+
{
23+
Definitions.Private.Add("_CRT_SECURE_NO_WARNINGS");
24+
Definitions.Private.Add("_CRT_SECURE_NO_DEPRECATE");
25+
CompilerOptions.Add("/sdl-");
26+
}
27+
var icuCommonSources = File.ReadAllLines(Path.Join(sourceBase, "sources.txt"));
28+
Includes.Private.Add("source/icu/source/common"); // Ensure the unicode/* headers resolves to this correctly.
29+
SourceFiles.AddRange(icuCommonSources.Select(line => Path.Join(sourceBase, line.Trim())).Where(File.Exists));
30+
if(context.Platform.Name == "windows")
31+
{
32+
Libraries.Private.Add("Advapi32.lib");
33+
}
34+
if (context.RequestedOutput is not "static" and not "shared")
35+
{
36+
throw new Exception("Invalid output type for IcuCommon module. Must be either static or shared.");
37+
}
38+
}
39+
40+
41+
[OutputTransformer("static", "static")]
42+
void TransformToStaticLibrary()
43+
{
44+
this.Type = ModuleType.StaticLibrary;
45+
Definitions.Public.Add("U_STATIC_IMPLEMENTATION=1");
46+
}
47+
[OutputTransformer("shared", "shared")]
48+
void TransformToSharedLibrary()
49+
{
50+
this.Type = ModuleType.SharedLibrary;
51+
Dependencies.Private.Add("shared:icu-data");
52+
}
53+
}

examples/icu/icu-data.ebuild.cs

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
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

Comments
 (0)