Skip to content

Commit 4e02da2

Browse files
committed
[WIP] reshaping linking
1 parent ef9a590 commit 4e02da2

File tree

5 files changed

+119
-58
lines changed

5 files changed

+119
-58
lines changed

src/Xamarin.Android.Build.Tasks/Tasks/GetNativeRuntimeComponents.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ void MakeLibItem (string libName, List<ITaskItem> libraries, HashSet<string> uni
114114
foreach (string abi in uniqueAbis) {
115115
var item = new TaskItem (libName);
116116
item.SetMetadata (KnownMetadata.Abi, abi);
117+
item.SetMetadata (KnownMetadata.NativeSharedLibrary, "true");
117118
libraries.Add (item);
118119
}
119120
}
@@ -139,6 +140,7 @@ void MakeArchiveItem (NativeRuntimeComponents.Archive archive, List<ITaskItem> a
139140
Log.LogDebugMessage ($" creating msbuild item for archive '{archive.Name}'");
140141
ITaskItem newItem = DoMakeItem ("_ResolvedNativeArchive", resolvedArchive, uniqueAbis);
141142
newItem.SetMetadata (KnownMetadata.NativeLinkWholeArchive, archive.WholeArchive.ToString ());
143+
newItem.SetMetadata (KnownMetadata.NativeLinkItemSet, archive.SetName);
142144
if (archive.DontExportSymbols) {
143145
newItem.SetMetadata (KnownMetadata.NativeDontExportSymbols, "true");
144146
}
@@ -163,7 +165,7 @@ void HackMakeArchiveItem (NativeRuntimeComponents.Archive archive, List<ITaskIte
163165
var relativeArtifactPaths = new List<(string path, string abi)> ();
164166
string archiveName = Path.GetFileName (archive.Name);
165167
string commonClrObjDir = Path.Combine ("artifacts", "obj", "coreclr");
166-
const string config = "Debug"; // or Release
168+
const string config = "Release";
167169

168170
if (IsArchive ("libcoreclr.a")) {
169171
archiveName = "libcoreclr_static.a";
@@ -190,7 +192,7 @@ void HackMakeArchiveItem (NativeRuntimeComponents.Archive archive, List<ITaskIte
190192
foreach ((string relPath, string abi) in relativeArtifactPaths) {
191193
string filePath = Path.Combine (HackLocalClrRepoPath, relPath, archiveName);
192194
if (!File.Exists (filePath)) {
193-
Log.LogWarning ($" [HACK] file {filePath} not found");
195+
Log.LogError ($" [HACK] file {filePath} not found");
194196
continue;
195197
}
196198
Log.LogWarning ($" [HACK] adding runtime component '{filePath}'");
@@ -199,6 +201,7 @@ void HackMakeArchiveItem (NativeRuntimeComponents.Archive archive, List<ITaskIte
199201
tempItem.SetMetadata (KnownMetadata.RuntimeIdentifier, MonoAndroidHelper.AbiToRid (abi));
200202
ITaskItem newItem = DoMakeItem ("_ResolvedNativeArchive", tempItem, uniqueAbis);
201203
newItem.SetMetadata (KnownMetadata.NativeLinkWholeArchive, archive.WholeArchive.ToString ());
204+
newItem.SetMetadata (KnownMetadata.NativeLinkItemSet, archive.SetName);
202205
if (archive.DontExportSymbols) {
203206
newItem.SetMetadata (KnownMetadata.NativeDontExportSymbols, "true");
204207
}

src/Xamarin.Android.Build.Tasks/Tasks/LinkNativeRuntime.cs

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,65 @@ void LinkRuntime (ITaskItem abiItem)
7676
NdkRootPath = AndroidNdkDirectory,
7777
NdkApiLevel = AndroidApiLevel,
7878
};
79-
linker.Link (
79+
80+
List<ITaskItem> items = OrganizeCommandLineItemsCLR (abi);
81+
bool success = linker.Link (
8082
outputRuntime,
81-
GetAbiItems (NativeObjectFiles, "_NativeAssemblyTarget", abi),
82-
GetAbiItems (NativeArchives, "_SelectedNativeArchive", abi),
83-
GetAbiItems (LinkLibraries, "_RequiredLinkLibraries", abi),
83+
items,
8484
GetAbiItems (NativeLinkStartFiles, "_NativeLinkStartFiles", abi),
8585
GetAbiItems (NativeLinkEndFiles, "_NativeLinkEndFiles", abi),
8686
GetAbiItems (NativeSymbolsToExport, "_NativeSymbolsToExport", abi)
8787
);
88+
89+
if (!success) {
90+
Log.LogError ($"Failed to link native runtime {outputRuntime}");
91+
}
92+
}
93+
94+
// Puts object files, static archives in the correct order. This is a bit clumsy, but unfortunately necessary
95+
List<ITaskItem> OrganizeCommandLineItemsCLR (string abi)
96+
{
97+
// Code farther down the method does NOT check whether a set is present, it assumes that. This is on purpose, to
98+
// let the exception be thrown should a required (and assumed to be present) set be missing.
99+
var sets = new Dictionary<string, List<ITaskItem>> (StringComparer.Ordinal);
100+
foreach (ITaskItem item in GetAbiItems (NativeArchives, "_SelectedNativeArchive", abi)) {
101+
string setName = item.GetRequiredMetadata ("_SelectedNativeArchive", KnownMetadata.NativeLinkItemSet, Log);
102+
if (!sets.TryGetValue (setName, out List<ITaskItem>? items)) {
103+
items = new List<ITaskItem> ();
104+
sets.Add (setName, items);
105+
}
106+
107+
items.Add (item);
108+
}
109+
110+
var ret = new List<ITaskItem> ();
111+
112+
// First go our own object files...
113+
ret.AddRange (GetAbiItems (NativeObjectFiles, "_NativeAssemblyTarget", abi));
114+
115+
// ...then go our runtime archives...
116+
ret.AddRange (sets[NativeRuntimeComponents.KnownSets.XamarinAndroidRuntime]);
117+
118+
// ...followed by CoreCLR components...
119+
ret.AddRange (sets[NativeRuntimeComponents.KnownSets.CoreClrRuntime]);
120+
121+
// ...and after them the BCL PAL libraries...
122+
ret.AddRange (sets[NativeRuntimeComponents.KnownSets.BCL]);
123+
124+
// ...and then the C/C++ runtime libraries
125+
var systemLibs = new Dictionary<string, ITaskItem> (StringComparer.Ordinal);
126+
foreach (ITaskItem item in GetAbiItems (LinkLibraries, "_RequiredLinkLibraries", abi)) {
127+
systemLibs.Add (Path.GetFileName (item.ItemSpec), item);
128+
}
129+
130+
ret.Add (systemLibs["log"]);
131+
ret.AddRange (sets[NativeRuntimeComponents.KnownSets.CplusPlusRuntime]);
132+
ret.Add (systemLibs["z"]);
133+
ret.Add (systemLibs["m"]);
134+
ret.Add (systemLibs["dl"]);
135+
ret.Add (systemLibs["c"]);
136+
137+
return ret;
88138
}
89139

90140
List<ITaskItem> GetAbiItems (ITaskItem[] source, string itemName, string abi)

src/Xamarin.Android.Build.Tasks/Utilities/KnownMetadata.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ namespace Xamarin.Android.Tasks;
33
static class KnownMetadata
44
{
55
public const string Abi = "Abi";
6+
public const string NativeDontExportSymbols = "DontExportSymbols";
7+
public const string NativeLinkItemSet = "NativeLinkItemSet";
68
public const string NativeLinkWholeArchive = "LinkWholeArchive";
9+
public const string NativeSharedLibrary = "NativeSharedLibrary";
710
public const string RuntimeIdentifier = "RuntimeIdentifier";
8-
public const string NativeDontExportSymbols = "DontExportSymbols";
911
}

src/Xamarin.Android.Build.Tasks/Utilities/NativeLinker.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,7 @@ public NativeLinker (TaskLoggingHelper log, string abi, string soname, string bi
9696
extraArgs.Add ($"-L {MonoAndroidHelper.QuoteFileNameArgument (nativeLibsDir)}");
9797
}
9898

99-
public bool Link (ITaskItem outputLibraryPath, List<ITaskItem> objectFiles, List<ITaskItem> archives, List<ITaskItem> libraries,
100-
List<ITaskItem> linkStartFiles, List<ITaskItem> linkEndFiles, ICollection<ITaskItem>? exportDynamicSymbols = null)
99+
public bool Link (ITaskItem outputLibraryPath, List<ITaskItem> linkItems, List<ITaskItem> linkStartFiles, List<ITaskItem> linkEndFiles, ICollection<ITaskItem>? exportDynamicSymbols = null)
101100
{
102101
if (UseNdkLibraries) {
103102
if (String.IsNullOrEmpty (NdkRootPath)) {
@@ -111,9 +110,7 @@ public bool Link (ITaskItem outputLibraryPath, List<ITaskItem> objectFiles, List
111110

112111
log.LogDebugMessage ($"Linking: {outputLibraryPath}");
113112
EnsureCorrectAbi (outputLibraryPath);
114-
EnsureCorrectAbi (objectFiles);
115-
EnsureCorrectAbi (archives);
116-
EnsureCorrectAbi (libraries);
113+
EnsureCorrectAbi (linkItems);
117114
EnsureCorrectAbi (linkStartFiles);
118115
EnsureCorrectAbi (linkEndFiles);
119116

@@ -147,8 +144,7 @@ public bool Link (ITaskItem outputLibraryPath, List<ITaskItem> objectFiles, List
147144

148145
var excludeExportsLibs = new List<string> ();
149146
WriteFilesToResponseFile (sw, linkStartFiles);
150-
WriteFilesToResponseFile (sw, objectFiles);
151-
WriteFilesToResponseFile (sw, archives);
147+
WriteFilesToResponseFile (sw, linkItems);
152148

153149
if (exportDynamicSymbols != null && exportDynamicSymbols.Count > 0) {
154150
foreach (ITaskItem symbolItem in exportDynamicSymbols) {
@@ -161,10 +157,6 @@ public bool Link (ITaskItem outputLibraryPath, List<ITaskItem> objectFiles, List
161157
sw.WriteLine ($"--exclude-libs={libs}");
162158
}
163159

164-
foreach (ITaskItem libItem in libraries) {
165-
sw.WriteLine ($"-l{libItem.ItemSpec}");
166-
}
167-
168160
WriteFilesToResponseFile (sw, linkEndFiles);
169161
sw.Flush ();
170162

@@ -197,7 +189,10 @@ void WriteFilesToResponseFile (StreamWriter sw, List<ITaskItem> files)
197189

198190
if (wholeArchive) {
199191
sw.Write ("--whole-archive ");
192+
} else if (IsNativeSharedLibrary (file)) {
193+
sw.Write ("-l");
200194
}
195+
201196
sw.Write (MonoAndroidHelper.QuoteFileNameArgument (file.ItemSpec));
202197
// string abi = file.GetMetadata ("Abi") ?? String.Empty;
203198
// string destDir = Path.Combine ("/tmp/t", abi);
@@ -212,6 +207,7 @@ void WriteFilesToResponseFile (StreamWriter sw, List<ITaskItem> files)
212207

213208
bool IncludeWholeArchive (ITaskItem item) => ParseBooleanMetadata (item, KnownMetadata.NativeLinkWholeArchive);
214209
bool ExcludeFromExports (ITaskItem item) => ParseBooleanMetadata (item, KnownMetadata.NativeDontExportSymbols);
210+
bool IsNativeSharedLibrary (ITaskItem item) => ParseBooleanMetadata (item, KnownMetadata.NativeSharedLibrary);
215211

216212
bool ParseBooleanMetadata (ITaskItem item, string metadata)
217213
{
@@ -381,6 +377,8 @@ bool RunCommand (string label, string binaryPath, List<string> args, List<string
381377
return true;
382378
}
383379

380+
// TODO: collect stdout and stderr messages and log with LogError or LogMessage, depending on
381+
// process exit code.
384382
void OnOutputData (string linkerName, object sender, DataReceivedEventArgs e)
385383
{
386384
if (e.Data != null) {

src/Xamarin.Android.Build.Tasks/Utilities/NativeRuntimeComponents.cs

Lines changed: 48 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ namespace Xamarin.Android.Tasks;
77

88
class NativeRuntimeComponents
99
{
10+
public sealed class KnownSets
11+
{
12+
public const string BCL = "bcl";
13+
public const string CoreClrRuntime = "coreclr";
14+
public const string CplusPlusRuntime = "c++";
15+
public const string XamarinAndroidRuntime = "xaruntime";
16+
}
17+
1018
internal class Archive
1119
{
1220
public readonly string Name;
@@ -15,13 +23,16 @@ internal class Archive
1523
public readonly bool WholeArchive;
1624
public bool DontExportSymbols { get; set; }
1725
public HashSet<string>? SymbolsToPreserve { get; set; }
26+
public string SetName { get; }
27+
1828
public readonly bool NeedsClrHack;
1929

2030
Func<Archive, bool> shouldInclude;
2131

22-
public Archive (string name, Func<Archive, bool>? include = null, bool wholeArchive = false, string? jniOnLoadName = null, bool needsClrHack = false)
32+
public Archive (string name, string setName, Func<Archive, bool>? include = null, bool wholeArchive = false, string? jniOnLoadName = null, bool needsClrHack = false)
2333
{
2434
Name = name;
35+
SetName = setName;
2536
shouldInclude = include == null ? ((Archive arch) => true) : include;
2637
WholeArchive = wholeArchive;
2738
JniOnLoadName = jniOnLoadName;
@@ -32,21 +43,39 @@ public Archive (string name, Func<Archive, bool>? include = null, bool wholeArch
3243
sealed class ClangBuiltinsArchive : Archive
3344
{
3445
public ClangBuiltinsArchive (string clangAbi)
35-
: base ($"libclang_rt.builtins-{clangAbi}-android.a")
46+
: base ($"libclang_rt.builtins-{clangAbi}-android.a", KnownSets.CplusPlusRuntime)
3647
{}
3748
}
3849

3950
class AndroidArchive : Archive
4051
{
4152
public AndroidArchive (string name, bool wholeArchive = false)
42-
: base (name, wholeArchive: wholeArchive)
53+
: base (name, KnownSets.XamarinAndroidRuntime, wholeArchive: wholeArchive)
4354
{}
4455
}
4556

4657
sealed class BclArchive : Archive
4758
{
4859
public BclArchive (string name, bool wholeArchive = false, string? jniOnLoadName = null)
49-
: base (name, wholeArchive: wholeArchive, jniOnLoadName: jniOnLoadName, needsClrHack: true)
60+
: base (name, KnownSets.BCL, wholeArchive: wholeArchive, jniOnLoadName: jniOnLoadName, needsClrHack: true)
61+
{
62+
DontExportSymbols = true;
63+
}
64+
}
65+
66+
sealed class ClrArchive : Archive
67+
{
68+
public ClrArchive (string name, bool wholeArchive = false)
69+
: base (name, KnownSets.CoreClrRuntime, wholeArchive: wholeArchive, needsClrHack: true)
70+
{
71+
DontExportSymbols = true;
72+
}
73+
}
74+
75+
sealed class CplusPlusArchive : Archive
76+
{
77+
public CplusPlusArchive (string name)
78+
: base (name, KnownSets.CplusPlusRuntime)
5079
{
5180
DontExportSymbols = true;
5281
}
@@ -59,36 +88,21 @@ public BclArchive (string name, bool wholeArchive = false, string? jniOnLoadName
5988
public readonly List<string> LinkStartFiles;
6089
public readonly List<string> LinkEndFiles;
6190

62-
// LINK_LIBRARIES = pal/src/eventprovider/dummyprovider/libeventprovider.a -llog nativeresources/libnativeresourcestring.a shared_minipal/libminipal.a -ldl -latomic -lm
6391
public NativeRuntimeComponents (ITaskItem[] monoComponents)
6492
{
6593
this.monoComponents = monoComponents;
6694
KnownArchives = new () {
6795
// CoreCLR runtime + BCL
68-
new Archive ("libcoreclr.a", needsClrHack: true) {
69-
DontExportSymbols = true,
70-
},
71-
new Archive ("libcoreclrminipal.a", needsClrHack: true) {
72-
DontExportSymbols = true,
73-
},
74-
new Archive ("libgc_pal.a", needsClrHack: true) {
75-
DontExportSymbols = true,
76-
},
77-
new Archive ("libcoreclrpal.a", wholeArchive: true, needsClrHack: true) {
78-
DontExportSymbols = true,
79-
},
80-
new Archive ("libeventprovider.a", needsClrHack: true) {
81-
DontExportSymbols = true,
82-
},
83-
new Archive ("libnativeresourcestring.a", needsClrHack: true) {
84-
DontExportSymbols = true,
85-
},
86-
new Archive ("libminipal.a", needsClrHack: true) {
87-
DontExportSymbols = true,
88-
},
89-
new Archive ("libbrotlicommon.a", needsClrHack: true),
90-
new Archive ("libbrotlidec.a", needsClrHack: true),
91-
new Archive ("libbrotlienc.a", needsClrHack: true),
96+
new ClrArchive ("libcoreclr.a"),
97+
new ClrArchive ("libcoreclrminipal.a"),
98+
new ClrArchive ("libgc_pal.a"),
99+
new ClrArchive ("libcoreclrpal.a", wholeArchive: true),
100+
new ClrArchive ("libeventprovider.a"),
101+
new ClrArchive ("libnativeresourcestring.a"),
102+
new ClrArchive ("libminipal.a"),
103+
new ClrArchive ("libbrotlicommon.a"),
104+
new ClrArchive ("libbrotlidec.a"),
105+
new ClrArchive ("libbrotlienc.a"),
92106

93107
new BclArchive ("libSystem.Globalization.Native.a"),
94108
new BclArchive ("libSystem.IO.Compression.Native.a"),
@@ -121,23 +135,17 @@ public NativeRuntimeComponents (ITaskItem[] monoComponents)
121135
new AndroidArchive ("libxa-shared-bits-release.a"),
122136
new AndroidArchive ("libxamarin-startup-release.a"),
123137

138+
// C++ standard library
139+
new CplusPlusArchive ("libc++_static.a"),
140+
new CplusPlusArchive ("libc++abi.a"),
141+
124142
// LLVM clang built-ins archives
125143
new ClangBuiltinsArchive ("aarch64"),
126144
new ClangBuiltinsArchive ("arm"),
127145
new ClangBuiltinsArchive ("i686"),
128146
new ClangBuiltinsArchive ("x86_64"),
129147

130-
// C++ standard library
131-
new Archive ("libc++_static.a") {
132-
DontExportSymbols = true,
133-
},
134-
new Archive ("libc++abi.a") {
135-
DontExportSymbols = true,
136-
},
137-
138-
new Archive ("libunwind.a") {
139-
DontExportSymbols = true,
140-
},
148+
new CplusPlusArchive ("libunwind.a"), // techically it's from clang
141149
};
142150

143151
// Just the base names of libraries to link into the unified runtime. Must have all the dependencies of all the static archives we

0 commit comments

Comments
 (0)