@@ -25,45 +25,48 @@ class LinkerOptions {
2525  /// See also the `ld`  man page at https://linux.die.net/man/1/ld. 
2626final  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. 
3431final  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. 
4441LinkerOptions .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. 
5859LinkerOptions .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