@@ -25,45 +25,48 @@ class LinkerOptions {
25
25
/// See also the `ld` man page at https://linux.die.net/man/1/ld.
26
26
final bool gcSections;
27
27
28
- /// The linker script to be passed via `--version-script` .
29
- ///
30
- /// See also the `ld` man page at https://linux.die.net/man/1/ld.
31
- final Uri ? linkerScript;
28
+ final LinkerScriptMode ? _linkerScriptMode;
32
29
33
30
/// Whether to strip debugging symbols from the binary.
34
31
final bool stripDebug;
35
32
36
33
/// The symbols to keep in the resulting binaries.
37
- ///
38
- /// If null all symbols will be kept.
39
- final List <String >? _symbolsToKeep;
34
+ final List <String > _symbols;
40
35
41
- final bool _generateLinkerScript ;
36
+ final bool _keepAllSymbols ;
42
37
43
38
/// Create linking options manually for fine-grained control.
39
+ ///
40
+ /// If [symbolsToKeep] is null, all symbols will be kept.
44
41
LinkerOptions .manual ({
45
42
List <String >? flags,
46
43
bool ? gcSections,
47
- this . linkerScript,
44
+ Uri ? linkerScript,
48
45
this .stripDebug = true ,
49
46
Iterable <String >? symbolsToKeep,
50
47
}) : _linkerFlags = flags ?? [],
51
48
gcSections = gcSections ?? true ,
52
- _symbolsToKeep = symbolsToKeep? .toList (growable: false ),
53
- _generateLinkerScript = false ;
49
+ _symbols = symbolsToKeep? .toList (growable: false ) ?? const [],
50
+ _keepAllSymbols = symbolsToKeep == null ,
51
+ _linkerScriptMode = linkerScript != null
52
+ ? ManualLinkerScript (script: linkerScript)
53
+ : null ;
54
54
55
55
/// Create linking options to tree-shake symbols from the input files.
56
56
///
57
- /// The [symbols] specify the symbols which should be kept.
57
+ /// The [symbolsToKeep] specify the symbols which should be kept. Passing
58
+ /// `null` implies that all symbols should be kept.
58
59
LinkerOptions .treeshake ({
59
60
Iterable <String >? flags,
60
- required Iterable <String >? symbols ,
61
+ required Iterable <String >? symbolsToKeep ,
61
62
this .stripDebug = true ,
62
63
}) : _linkerFlags = flags? .toList (growable: false ) ?? [],
63
- _symbolsToKeep = symbols? .toList (growable: false ),
64
+ _symbols = symbolsToKeep? .toList (growable: false ) ?? const [],
65
+ _keepAllSymbols = symbolsToKeep == null ,
64
66
gcSections = true ,
65
- linkerScript = null ,
66
- _generateLinkerScript = symbols != null ;
67
+ _linkerScriptMode = symbolsToKeep != null
68
+ ? GenerateLinkerScript ()
69
+ : null ;
67
70
68
71
Iterable <String > _toLinkerSyntax (Tool linker, Iterable <String > flagList) {
69
72
if (linker.isClangLike) {
@@ -76,6 +79,19 @@ class LinkerOptions {
76
79
}
77
80
}
78
81
82
+ sealed class LinkerScriptMode {}
83
+
84
+ final class GenerateLinkerScript extends LinkerScriptMode {}
85
+
86
+ final class ManualLinkerScript extends LinkerScriptMode {
87
+ /// The linker script to be passed via `--version-script` .
88
+ ///
89
+ /// See also the `ld` man page at https://linux.die.net/man/1/ld.
90
+ final Uri script;
91
+
92
+ ManualLinkerScript ({required this .script});
93
+ }
94
+
79
95
extension LinkerOptionsExt on LinkerOptions {
80
96
/// Takes [sourceFiles] and turns it into flags for the compiler driver while
81
97
/// considering the current [LinkerOptions] .
@@ -99,8 +115,6 @@ extension LinkerOptionsExt on LinkerOptions {
99
115
}
100
116
}
101
117
102
- bool get _includeAllSymbols => _symbolsToKeep == null ;
103
-
104
118
Iterable <String > _sourceFilesToFlagsForClangLike (
105
119
Tool tool,
106
120
Iterable <String > sourceFiles,
@@ -109,33 +123,37 @@ extension LinkerOptionsExt on LinkerOptions {
109
123
switch (targetOS) {
110
124
case OS .macOS || OS .iOS:
111
125
return [
112
- if (! _includeAllSymbols ) ...sourceFiles,
126
+ if (! _keepAllSymbols ) ...sourceFiles,
113
127
..._toLinkerSyntax (tool, [
114
- if (_includeAllSymbols ) ...sourceFiles.map ((e) => '-force_load,$e ' ),
128
+ if (_keepAllSymbols ) ...sourceFiles.map ((e) => '-force_load,$e ' ),
115
129
..._linkerFlags,
116
- ..._symbolsToKeep ? .map ((symbol) => '-u,_$symbol ' ) ?? [] ,
130
+ ..._symbols .map ((symbol) => '-u,_$symbol ' ),
117
131
if (stripDebug) '-S' ,
118
132
if (gcSections) '-dead_strip' ,
133
+ if (_linkerScriptMode is ManualLinkerScript )
134
+ '-exported_symbols_list,${_linkerScriptMode .script .toFilePath ()}'
135
+ else if (_linkerScriptMode is GenerateLinkerScript )
136
+ '-exported_symbols_list,${_createMacSymbolList (_symbols )}' ,
119
137
]),
120
138
];
121
139
122
140
case OS .android || OS .linux:
123
141
final wholeArchiveSandwich =
124
142
sourceFiles.any ((source) => source.endsWith ('.a' )) ||
125
- _includeAllSymbols ;
143
+ _keepAllSymbols ;
126
144
return [
127
145
if (wholeArchiveSandwich)
128
146
..._toLinkerSyntax (tool, ['--whole-archive' ]),
129
147
...sourceFiles,
130
148
..._toLinkerSyntax (tool, [
131
149
..._linkerFlags,
132
- ..._symbolsToKeep ? .map ((symbol) => '-u,$symbol ' ) ?? [] ,
150
+ ..._symbols .map ((symbol) => '-u,$symbol ' ),
133
151
if (stripDebug) '--strip-debug' ,
134
152
if (gcSections) '--gc-sections' ,
135
- if (linkerScript != null )
136
- '--version-script=${linkerScript ! .toFilePath ()}'
137
- else if (_generateLinkerScript && _symbolsToKeep != null )
138
- '--version-script=${_createClangLikeLinkScript (_symbolsToKeep )}' ,
153
+ if (_linkerScriptMode is ManualLinkerScript )
154
+ '--version-script=${_linkerScriptMode . script .toFilePath ()}'
155
+ else if (_linkerScriptMode is GenerateLinkerScript )
156
+ '--version-script=${_createClangLikeLinkScript (_symbols )}' ,
139
157
if (wholeArchiveSandwich) '--no-whole-archive' ,
140
158
]),
141
159
];
@@ -152,21 +170,34 @@ extension LinkerOptionsExt on LinkerOptions {
152
170
) => [
153
171
...sourceFiles,
154
172
'/link' ,
155
- if (_includeAllSymbols ) ...sourceFiles.map ((e) => '/WHOLEARCHIVE:$e ' ),
173
+ if (_keepAllSymbols ) ...sourceFiles.map ((e) => '/WHOLEARCHIVE:$e ' ),
156
174
..._linkerFlags,
157
- ..._symbolsToKeep? .map (
158
- (symbol) =>
159
- '/INCLUDE:${targetArch == Architecture .ia32 ? '_' : '' }$symbol ' ,
160
- ) ??
161
- [],
162
- if (linkerScript != null )
163
- '/DEF:${linkerScript !.toFilePath ()}'
164
- else if (_generateLinkerScript && _symbolsToKeep != null )
165
- '/DEF:${_createClLinkScript (_symbolsToKeep )}' ,
175
+ ..._symbols.map (
176
+ (symbol) =>
177
+ '/INCLUDE:${targetArch == Architecture .ia32 ? '_' : '' }$symbol ' ,
178
+ ),
179
+ if (_linkerScriptMode is ManualLinkerScript )
180
+ '/DEF:${_linkerScriptMode .script .toFilePath ()}'
181
+ else if (_linkerScriptMode is GenerateLinkerScript )
182
+ '/DEF:${_createClLinkScript (_symbols )}' ,
166
183
if (stripDebug) '/PDBSTRIPPED' ,
167
184
if (gcSections) '/OPT:REF' ,
168
185
];
169
186
187
+ /// This creates a list of exported symbols.
188
+ ///
189
+ /// If this is not set, some symbols might be kept. This can be inspected
190
+ /// using `ld -why_live` , see https://www.unix.com/man_page/osx/1/ld/, where
191
+ /// the reason will show up as `global-dont-strip` .
192
+ /// This might possibly be a Rust only feature.
193
+ static String _createMacSymbolList (Iterable <String > symbols) {
194
+ final tempDir = Directory .systemTemp.createTempSync ();
195
+ final symbolsFileUri = tempDir.uri.resolve ('exported_symbols_list.txt' );
196
+ final symbolsFile = File .fromUri (symbolsFileUri)..createSync ();
197
+ symbolsFile.writeAsStringSync (symbols.map ((e) => '_$e ' ).join ('\n ' ));
198
+ return symbolsFileUri.toFilePath ();
199
+ }
200
+
170
201
static String _createClangLikeLinkScript (Iterable <String > symbols) {
171
202
final tempDir = Directory .systemTemp.createTempSync ();
172
203
final symbolsFileUri = tempDir.uri.resolve ('symbols.lds' );
0 commit comments