@@ -25,45 +25,48 @@ class LinkerOptions {
2525 /// See also the `ld` man page at https://linux.die.net/man/1/ld.
2626 final bool gcSections;
2727
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;
3229
3330 /// Whether to strip debugging symbols from the binary.
3431 final bool stripDebug;
3532
3633 /// 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;
4035
41- final bool _generateLinkerScript ;
36+ final bool _keepAllSymbols ;
4237
4338 /// Create linking options manually for fine-grained control.
39+ ///
40+ /// If [symbolsToKeep] is null, all symbols will be kept.
4441 LinkerOptions .manual ({
4542 List <String >? flags,
4643 bool ? gcSections,
47- this . linkerScript,
44+ Uri ? linkerScript,
4845 this .stripDebug = true ,
4946 Iterable <String >? symbolsToKeep,
5047 }) : _linkerFlags = flags ?? [],
5148 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 ;
5454
5555 /// Create linking options to tree-shake symbols from the input files.
5656 ///
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.
5859 LinkerOptions .treeshake ({
5960 Iterable <String >? flags,
60- required Iterable <String >? symbols ,
61+ required Iterable <String >? symbolsToKeep ,
6162 this .stripDebug = true ,
6263 }) : _linkerFlags = flags? .toList (growable: false ) ?? [],
63- _symbolsToKeep = symbols? .toList (growable: false ),
64+ _symbols = symbolsToKeep? .toList (growable: false ) ?? const [],
65+ _keepAllSymbols = symbolsToKeep == null ,
6466 gcSections = true ,
65- linkerScript = null ,
66- _generateLinkerScript = symbols != null ;
67+ _linkerScriptMode = symbolsToKeep != null
68+ ? GenerateLinkerScript ()
69+ : null ;
6770
6871 Iterable <String > _toLinkerSyntax (Tool linker, Iterable <String > flagList) {
6972 if (linker.isClangLike) {
@@ -76,6 +79,19 @@ class LinkerOptions {
7679 }
7780}
7881
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+
7995extension LinkerOptionsExt on LinkerOptions {
8096 /// Takes [sourceFiles] and turns it into flags for the compiler driver while
8197 /// considering the current [LinkerOptions] .
@@ -99,8 +115,6 @@ extension LinkerOptionsExt on LinkerOptions {
99115 }
100116 }
101117
102- bool get _includeAllSymbols => _symbolsToKeep == null ;
103-
104118 Iterable <String > _sourceFilesToFlagsForClangLike (
105119 Tool tool,
106120 Iterable <String > sourceFiles,
@@ -109,33 +123,37 @@ extension LinkerOptionsExt on LinkerOptions {
109123 switch (targetOS) {
110124 case OS .macOS || OS .iOS:
111125 return [
112- if (! _includeAllSymbols ) ...sourceFiles,
126+ if (! _keepAllSymbols ) ...sourceFiles,
113127 ..._toLinkerSyntax (tool, [
114- if (_includeAllSymbols ) ...sourceFiles.map ((e) => '-force_load,$e ' ),
128+ if (_keepAllSymbols ) ...sourceFiles.map ((e) => '-force_load,$e ' ),
115129 ..._linkerFlags,
116- ..._symbolsToKeep ? .map ((symbol) => '-u,_$symbol ' ) ?? [] ,
130+ ..._symbols .map ((symbol) => '-u,_$symbol ' ),
117131 if (stripDebug) '-S' ,
118132 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 )}' ,
119137 ]),
120138 ];
121139
122140 case OS .android || OS .linux:
123141 final wholeArchiveSandwich =
124142 sourceFiles.any ((source) => source.endsWith ('.a' )) ||
125- _includeAllSymbols ;
143+ _keepAllSymbols ;
126144 return [
127145 if (wholeArchiveSandwich)
128146 ..._toLinkerSyntax (tool, ['--whole-archive' ]),
129147 ...sourceFiles,
130148 ..._toLinkerSyntax (tool, [
131149 ..._linkerFlags,
132- ..._symbolsToKeep ? .map ((symbol) => '-u,$symbol ' ) ?? [] ,
150+ ..._symbols .map ((symbol) => '-u,$symbol ' ),
133151 if (stripDebug) '--strip-debug' ,
134152 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 )}' ,
139157 if (wholeArchiveSandwich) '--no-whole-archive' ,
140158 ]),
141159 ];
@@ -152,21 +170,34 @@ extension LinkerOptionsExt on LinkerOptions {
152170 ) => [
153171 ...sourceFiles,
154172 '/link' ,
155- if (_includeAllSymbols ) ...sourceFiles.map ((e) => '/WHOLEARCHIVE:$e ' ),
173+ if (_keepAllSymbols ) ...sourceFiles.map ((e) => '/WHOLEARCHIVE:$e ' ),
156174 ..._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 )}' ,
166183 if (stripDebug) '/PDBSTRIPPED' ,
167184 if (gcSections) '/OPT:REF' ,
168185 ];
169186
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+
170201 static String _createClangLikeLinkScript (Iterable <String > symbols) {
171202 final tempDir = Directory .systemTemp.createTempSync ();
172203 final symbolsFileUri = tempDir.uri.resolve ('symbols.lds' );
0 commit comments