@@ -23,12 +23,15 @@ class MbedBSPGenerator
2323
2424 public readonly string Version ;
2525
26+ LinkerScriptCache _LinkerScriptCache ;
27+
2628 public MbedBSPGenerator ( string version )
2729 {
2830 Directory . CreateDirectory ( outputDir ) ;
2931 mbedRoot = Path . GetFullPath ( Path . Combine ( outputDir , "mbed" ) ) ;
3032 Version = version ;
3133
34+ _LinkerScriptCache = new LinkerScriptCache ( toolchainDir , mbedRoot ) ;
3235 nameRules = new List < KeyValuePair < Regex , string > > ( ) ;
3336 foreach ( var line in File . ReadAllLines ( Path . Combine ( dataDir , "DeviceNameRules.txt" ) ) )
3437 {
@@ -91,21 +94,6 @@ public void UpdateGitAndRescanTargets(bool skipRescan = false)
9194 if ( proc . ExitCode != 0 )
9295 throw new Exception ( "Git exited with code " + proc . ExitCode ) ;
9396
94- foreach ( var lds in Directory . GetFiles ( mbedRoot , "*.ld" , SearchOption . AllDirectories ) )
95- {
96- if ( File . ReadAllText ( lds ) . Contains ( "\n #if" ) )
97- {
98- ProcessStartInfo preprocessInfo = new ProcessStartInfo ( $@ "{ toolchainDir } \bin\arm-eabi-cpp.exe", $ "-P -C { lds } -o { lds } .preprocessed") ;
99- preprocessInfo . UseShellExecute = false ;
100- preprocessInfo . EnvironmentVariables [ "PATH" ] += $@ ";{ toolchainDir } \bin";
101- proc = Process . Start ( preprocessInfo ) ;
102- proc . WaitForExit ( ) ;
103-
104- File . Copy ( lds + ".preprocessed" , lds , true ) ;
105- File . Delete ( lds + ".preprocessed" ) ;
106- }
107- }
108-
10997 var patchedFile = Path . Combine ( mbedRoot , @"tools\config\__init__.py" ) ;
11098 var lines = File . ReadAllLines ( patchedFile ) . ToList ( ) ;
11199 var idx2 = Enumerable . Range ( 0 , lines . Count ) . First ( i => lines [ i ] . Contains ( "self.value = int(value) if isinstance(value, bool) else value" ) ) ;
@@ -225,28 +213,130 @@ public string[] ConvertPaths(IEnumerable<string> rawPaths)
225213 {
226214 if ( fn . Length == mbedRoot . Length && fn . ToLower ( ) == mbedRoot . ToLower ( ) )
227215 return "$$SYS:BSP_ROOT$$" ;
228- if ( fn . StartsWith ( mbedRoot , StringComparison . InvariantCulture ) )
216+ if ( fn . StartsWith ( mbedRoot , StringComparison . InvariantCultureIgnoreCase ) )
229217 return "$$SYS:BSP_ROOT$$/" + fn . Substring ( mbedRoot . Length + 1 ) . Replace ( '\\ ' , '/' ) ;
230218 throw new Exception ( "Source path is not inside the mbed BSP: " + fn ) ;
231219 } ) . ToArray ( ) ;
232220 }
233221
222+ public string ConvertPath ( string physicalPath ) => ConvertPaths ( new [ ] { physicalPath } ) [ 0 ] ;
223+
234224 public struct ConvertedHexFile
235225 {
236226 public uint LoadAddress ;
237227 public int Size ;
238228 public string RelativePath ;
239229 public string SectionName ;
230+
231+ public int End => ( int ) LoadAddress + Size ;
240232 }
241233
242234 Dictionary < string , ConvertedHexFile > _ConvertedHexFiles = new Dictionary < string , ConvertedHexFile > ( ) ;
243- Dictionary < string , string > _PatchedLinkerScripts = new Dictionary < string , string > ( StringComparer . InvariantCultureIgnoreCase ) ;
244- Dictionary < string , string > _PatchedLinkerScriptsReverse = new Dictionary < string , string > ( StringComparer . InvariantCultureIgnoreCase ) ;
245235
246- public void ConvertSoftdevicesAndPatchTarget ( MCU mcu , string [ ] hexFiles )
236+ static int AlignHexFileSize ( int size )
247237 {
248- string baseDir = Path . Combine ( mbedRoot , "SysprogsGenerated" ) ;
249- Directory . CreateDirectory ( baseDir ) ;
238+ const int alignment = 4096 ;
239+ return ( ( size + alignment - 1 ) / alignment ) * alignment ;
240+ }
241+
242+ class LinkerScriptCache
243+ {
244+ public readonly string BaseDir ;
245+ readonly string _ToolchainDir ;
246+ readonly string _MBEDRoot ;
247+
248+ public LinkerScriptCache ( string toolchainDir , string mbedRoot )
249+ {
250+ _ToolchainDir = toolchainDir ;
251+ _MBEDRoot = mbedRoot ;
252+ BaseDir = Path . Combine ( mbedRoot , "SysprogsGenerated" ) ;
253+ Directory . CreateDirectory ( BaseDir ) ;
254+ }
255+
256+ Dictionary < KeyValuePair < string , int > , string > _PreprocessedScripts = new Dictionary < KeyValuePair < string , int > , string > ( ) ;
257+ HashSet < string > _UsedGeneratedPaths = new HashSet < string > ( StringComparer . InvariantCultureIgnoreCase ) ;
258+
259+ public string ProvidePreprocessedLinkerScript ( string baseScript , int appStart )
260+ {
261+ baseScript = baseScript . Replace ( "$$SYS:BSP_ROOT$$" , _MBEDRoot ) ;
262+
263+ var key = new KeyValuePair < string , int > ( baseScript , appStart ) ;
264+ if ( _PreprocessedScripts . TryGetValue ( key , out var val ) )
265+ return val ;
266+
267+ if ( ! File . ReadAllText ( baseScript ) . Contains ( "\n #if" ) )
268+ return _PreprocessedScripts [ key ] = baseScript ;
269+
270+ ProcessStartInfo preprocessInfo = new ProcessStartInfo ( $@ "{ _ToolchainDir } \bin\arm-eabi-cpp.exe", $ "-P -C { baseScript } -o { baseScript } .preprocessed") ;
271+ if ( appStart != 0 )
272+ preprocessInfo . Arguments += $ " -DMBED_APP_START=0x{ appStart : x} ";
273+
274+ preprocessInfo . UseShellExecute = false ;
275+ preprocessInfo . EnvironmentVariables [ "PATH" ] += $@ ";{ _ToolchainDir } \bin";
276+ var proc = Process . Start ( preprocessInfo ) ;
277+ proc . WaitForExit ( ) ;
278+
279+ string nameBase = $ "{ BaseDir } \\ { Path . GetFileNameWithoutExtension ( baseScript ) } -{ appStart : x} ";
280+ for ( int iter = 0 ; iter < 100000 ; iter ++ )
281+ {
282+ string newName = nameBase ;
283+ if ( iter > 0 )
284+ newName += $ "-{ iter } ";
285+
286+ newName += ".ld" ;
287+ if ( _UsedGeneratedPaths . Contains ( newName ) )
288+ continue ;
289+
290+ _UsedGeneratedPaths . Add ( newName ) ;
291+
292+ File . Copy ( baseScript + ".preprocessed" , newName , true ) ;
293+ File . Delete ( baseScript + ".preprocessed" ) ;
294+ return _PreprocessedScripts [ key ] = newName ;
295+ }
296+
297+ throw new Exception ( "Failed to locate a name for preprocessed " + baseScript ) ;
298+ }
299+
300+ Dictionary < string , string > _PatchedScripts = new Dictionary < string , string > ( ) ;
301+
302+ public string ProvideNameForPatchedScript ( string originalScript , out bool alreadyCreated )
303+ {
304+ if ( _PatchedScripts . TryGetValue ( originalScript , out var result ) )
305+ {
306+ alreadyCreated = true ;
307+ return result ;
308+ }
309+
310+ alreadyCreated = false ;
311+ string nameBase = $ "{ BaseDir } \\ { Path . GetFileNameWithoutExtension ( originalScript ) } -sd";
312+
313+ for ( int iter = 0 ; iter < 100000 ; iter ++ )
314+ {
315+ string newName = nameBase ;
316+ if ( iter > 0 )
317+ newName += $ "-{ iter } ";
318+
319+ newName += ".ld" ;
320+ if ( _UsedGeneratedPaths . Contains ( newName ) )
321+ continue ;
322+
323+ _UsedGeneratedPaths . Add ( newName ) ;
324+ return newName ;
325+ }
326+
327+ throw new Exception ( "Failed to locate a name for patched " + originalScript ) ;
328+ }
329+ }
330+
331+ public string PreprocessLinkerScriptIfNeeded ( string linkerScript )
332+ {
333+ return _LinkerScriptCache . ProvidePreprocessedLinkerScript ( linkerScript , 0 ) ;
334+ }
335+
336+ //Returns true if the linker script was preprocessed.
337+ public bool ConvertSoftdevicesAndPatchTarget ( MCU mcu , string [ ] hexFiles )
338+ {
339+ var baseDir = _LinkerScriptCache . BaseDir ;
250340 File . WriteAllText ( Path . Combine ( baseDir , ".mbedignore" ) , "*" ) ;
251341
252342 foreach ( var hexFile in hexFiles )
@@ -281,33 +371,25 @@ public void ConvertSoftdevicesAndPatchTarget(MCU mcu, string[] hexFiles)
281371
282372 if ( ! generatedCFile . StartsWith ( mbedRoot , StringComparison . InvariantCultureIgnoreCase ) )
283373 throw new Exception ( "HEX file outside mbed root" ) ;
284- _ConvertedHexFiles [ hexFile ] = new ConvertedHexFile { LoadAddress = parsedFile . LoadAddress , RelativePath = generatedCFile . Substring ( mbedRoot . Length + 1 ) , SectionName = sectionName , Size = parsedFile . Data . Length } ;
374+ _ConvertedHexFiles [ hexFile ] = new ConvertedHexFile
375+ {
376+ LoadAddress = parsedFile . LoadAddress ,
377+ RelativePath = generatedCFile . Substring ( mbedRoot . Length + 1 ) ,
378+ SectionName = sectionName ,
379+ Size = AlignHexFileSize ( parsedFile . Data . Length )
380+ } ;
285381 }
286382
287383 var hexFileList = hexFiles . Select ( hf => _ConvertedHexFiles [ hf ] ) . ToList ( ) ;
288384 if ( hexFiles . Length == 0 )
289- return ;
385+ return false ;
290386
291387 hexFileList . Sort ( ( a , b ) => a . LoadAddress . CompareTo ( b . LoadAddress ) ) ;
292- var linkerScript = mcu . CompilationFlags . LinkerScript ;
293- string patchedLinkerScript ;
388+ var linkerScript = _LinkerScriptCache . ProvidePreprocessedLinkerScript ( mcu . CompilationFlags . LinkerScript , hexFileList . Last ( ) . End ) ;
294389
295- if ( ! _PatchedLinkerScripts . TryGetValue ( linkerScript , out patchedLinkerScript ) )
390+ string patchedLinkerScript = _LinkerScriptCache . ProvideNameForPatchedScript ( linkerScript , out bool scriptAlreadyPatched ) ;
391+ if ( ! scriptAlreadyPatched )
296392 {
297- patchedLinkerScript = Path . Combine ( baseDir , Path . GetFileName ( mcu . CompilationFlags . LinkerScript ) ) ;
298- if ( _PatchedLinkerScriptsReverse . TryGetValue ( patchedLinkerScript , out string tmp ) && tmp != linkerScript )
299- {
300- for ( int i = 2 ; ; i ++ )
301- {
302- patchedLinkerScript = Path . Combine ( baseDir , Path . GetFileNameWithoutExtension ( mcu . CompilationFlags . LinkerScript ) + "_x" + i + Path . GetExtension ( mcu . CompilationFlags . LinkerScript ) ) ;
303- if ( ! _PatchedLinkerScriptsReverse . ContainsKey ( patchedLinkerScript ) )
304- break ;
305- }
306-
307- }
308- _PatchedLinkerScripts [ linkerScript ] = patchedLinkerScript ;
309- _PatchedLinkerScriptsReverse [ patchedLinkerScript ] = linkerScript ;
310-
311393 List < string > linkerScriptLines = File . ReadAllLines ( linkerScript . Replace ( "$$SYS:BSP_ROOT$$" , mbedRoot ) ) . ToList ( ) ;
312394
313395 int firstMemorySectionLine = Enumerable . Range ( 0 , linkerScript . Length )
@@ -338,13 +420,16 @@ public void ConvertSoftdevicesAndPatchTarget(MCU mcu, string[] hexFiles)
338420 File . WriteAllLines ( patchedLinkerScript . Replace ( "$$SYS:BSP_ROOT$$" , mbedRoot ) , linkerScriptLines ) ;
339421 }
340422
341- mcu . CompilationFlags . LinkerScript = ConvertPaths ( new [ ] { patchedLinkerScript } ) [ 0 ] ;
423+ mcu . CompilationFlags . LinkerScript = ConvertPath ( patchedLinkerScript ) ;
342424 mcu . AdditionalSourceFiles = mcu . AdditionalSourceFiles . Concat ( hexFileList . Select ( h => "$$SYS:BSP_ROOT$$/" + h . RelativePath . Replace ( '\\ ' , '/' ) ) ) . ToArray ( ) ;
425+ return true ;
343426 }
344427
345428 public void DetectAndApplyMemorySizes ( MCU mcu , string linkerScript )
346429 {
347430 Console . Write ( "." ) ;
431+ linkerScript = _LinkerScriptCache . ProvidePreprocessedLinkerScript ( linkerScript , 0 ) ;
432+
348433 string tmpFile = Path . GetTempPath ( ) + "LinkerScriptQuery.c" ;
349434 File . WriteAllText ( tmpFile , "" ) ;
350435 string mapFile = Path . ChangeExtension ( tmpFile , ".map" ) ;
0 commit comments