173173in ~/.arduino15/preferences.txt, to disable the aggressive caching feature:
174174 https://forum.arduino.cc/t/no-aggressively-cache-compiled-core-in-ide-1-8-15/878954/2
175175
176+ Added workaround for `compiler.cache_core=true` case.
177+ See `if use_aggressive_caching_workaround:` in main().
178+
1761797) Suspected but not confirmed. A quick edit and rebuild don't always work well.
177180Build does not work as expected. This does not fail often. Maybe PIC NIC.
178181"""
191194docs_url = "https://arduino-esp8266.readthedocs.io/en/latest/faq/a06-global-build-options.html"
192195
193196def print_msg (* args , ** kwargs ):
194- print (* args , flush = True , ** kwargs )
197+ print (* args , ** kwargs )
195198
196199
200+ # I prefer error messages to stand out; however, using stderr for a different
201+ # color does not work on the new Arduino IDE 2.0 RC4. Also, separate pipes,
202+ # buffering, and multiple threads with output can create mixed-up messages.
203+ # Bring attention to errors with a blank line and lines starting with "*** ".
204+ # Let multiple prints buffer to aid them in staying together.
197205def print_err (* args , ** kwargs ):
198- print (* args , flush = True , file = sys .stderr , ** kwargs ) # file=sys.stderr,
206+ if (args [0 ])[0 ] != ' ' :
207+ print ("" )
208+ print ("*** " , end = '' )
209+ print (* args , ** kwargs )
199210
200211
201212def copy_create_build_file (source_fqfn , build_target_fqfn ):
@@ -221,18 +232,19 @@ def copy_create_build_file(source_fqfn, build_target_fqfn):
221232 else :
222233 # Place holder - Must have an empty file to satisfy parameter list
223234 # specifications in platform.txt.
224- open (build_target_fqfn , 'w' ).close ()
235+ with open (build_target_fqfn , 'w' ):
236+ pass
225237 return True # file changed
226238
227239
228240def add_include_line (build_opt_fqfn , include_fqfn ):
229241 if not os .path .exists (include_fqfn ):
230242 # If file is missing, we need an place holder
231- open (include_fqfn , 'w' ).close ()
243+ with open (include_fqfn , 'w' ):
244+ pass
232245 print ("add_include_line: Created " + include_fqfn )
233- build_opt = open (build_opt_fqfn , 'a' )
234- build_opt .write ('-include "' + include_fqfn .replace ('\\ ' , '\\ \\ ' ) + '"\n ' )
235- build_opt .close ()
246+ with open (build_opt_fqfn , 'a' ) as build_opt :
247+ build_opt .write ('-include "' + include_fqfn .replace ('\\ ' , '\\ \\ ' ) + '"\n ' )
236248
237249
238250def extract_create_build_opt_file (globals_h_fqfn , file_name , build_opt_fqfn ):
@@ -261,7 +273,7 @@ def extract_create_build_opt_file(globals_h_fqfn, file_name, build_opt_fqfn):
261273 if line == build_opt_signature :
262274 if complete_comment :
263275 build_opt_error = True
264- print_err ("Multiple embedded build.opt blocks in " + file_name + ":" + str (line_no ))
276+ print_err (" Multiple embedded build.opt blocks in " + file_name + ":" + str (line_no ))
265277 continue
266278 print_msg ("Extracting embedded compiler command-line options from " + file_name + ":" + str (line_no ))
267279 for line in src :
@@ -280,27 +292,26 @@ def extract_create_build_opt_file(globals_h_fqfn, file_name, build_opt_fqfn):
280292 continue
281293 # some consistency checking before writing - give some hints about what is wrong
282294 elif line == build_opt_signature :
283- print_err ("Double begin before end for embedded build.opt block in " + file_name + ":" + str (line_no ))
295+ print_err (" Double begin before end for embedded build.opt block in " + file_name + ":" + str (line_no ))
284296 build_opt_error = True
285297 elif line .startswith (build_opt_signature ):
286- print_err ("build.opt signature block ignored, trailing character for embedded build.opt block in " + file_name + ":" + str (line_no ))
298+ print_err (" build.opt signature block ignored, trailing character for embedded build.opt block in " + file_name + ":" + str (line_no ))
287299 build_opt_error = True
288300 elif "/*" in line or "*/" in line :
289- print_err ("Nesting issue for embedded build.opt block in " + file_name + ":" + str (line_no ))
301+ print_err (" Nesting issue for embedded build.opt block in " + file_name + ":" + str (line_no ))
290302 build_opt_error = True
291303 else :
292304 build_opt .write (line + "\n " )
293305 elif line .startswith (build_opt_signature ):
294- print_err ("build.opt signature block ignored, trailing character for embedded build.opt block in " + file_name + ":" + str (line_no ))
306+ print_err (" build.opt signature block ignored, trailing character for embedded build.opt block in " + file_name + ":" + str (line_no ))
295307 build_opt_error = True
296- src .close ()
297308 if not complete_comment or build_opt_error :
298309 build_opt .truncate (0 )
299310 build_opt .close ()
300311 if build_opt_error :
301312 # this will help the script start over when the issue is fixed
302313 os .remove (globals_h_fqfn )
303- print_err ("Extraction failed" )
314+ print_err (" Extraction failed" )
304315 # Don't let the failure get hidden by a spew of nonsensical error
305316 # messages that will follow. Bring things to a halt.
306317 sys .exit (1 )
@@ -312,54 +323,66 @@ def extract_create_build_opt_file(globals_h_fqfn, file_name, build_opt_fqfn):
312323
313324
314325def enable_override (enable , commonhfile_fqfn ):
315- file = open (commonhfile_fqfn , 'w' )
316- if enable :
317- file .write ("//Override aggressive caching\n " )
318- file .close ()
326+ with open (commonhfile_fqfn , 'w' ) as file :
327+ if enable :
328+ file .write ("//Override aggressive caching\n " )
319329 # enabled when getsize(commonhfile_fqfn) is non-zero, disabled when zero
320330
331+
321332def find_preferences_txt ():
322333 platform_name = platform .system ()
323334 # OS Path list from:
324335 # https://www.arduino.cc/en/hacking/preferences
325- if "Windows" == platform_name :
326- fqfn = os .path .expanduser ("\Arduino15\preferences.txt" ) # Windows
336+ if "Linux" == platform_name :
337+ # Test for portable 1ST
338+ # <Arduino IDE installation folder>/portable/preferences.txt (when used in portable mode)
339+ # For more on portable mode see https://docs.arduino.cc/software/ide-v1/tutorials/PortableIDE
340+ # Working directory must be set to the location of the Arduino IDE executable.
341+ fqfn = "./portable/preferences.txt" # Linux portable - verified
327342 if os .path .exists (fqfn ):
328343 return fqfn
329- fqfn = os .path .expanduser ("\Documents\ArduinoData\ preferences.txt" ) # Windows app version
344+ fqfn = os .path .expanduser ("~/.arduino15/ preferences.txt" ) # Linux - verified
330345 if os .path .exists (fqfn ):
331346 return fqfn
332- elif "Darwin" == platform_name :
333- fqfn = os .path .expanduser ("~/Library/Arduino15/preferences.txt" ) # Max OS X
347+ elif "Windows" == platform_name :
348+ fqfn = ".\portable\preferences.txt"
349+ if os .path .exists (fqfn ):
350+ return fqfn
351+ fqfn = os .path .expanduser ("~\Documents\ArduinoData\preferences.txt" ) # Windows app version - verified
334352 if os .path .exists (fqfn ):
335353 return fqfn
336- elif "Linux" == platform_name :
337- fqfn = os .path .expanduser ("~/.arduino15/preferences.txt" ) # Linux - works
354+ fqfn = os .path .expanduser ("~\Arduino15\preferences.txt" ) # Windows
338355 if os .path .exists (fqfn ):
339356 return fqfn
340- # Where and how would I find this?
341- # <Arduino IDE installation folder>/portable/preferences.txt (when used in portable mode)
357+ elif "Darwin" == platform_name :
358+ # Skip portable on Macs. Portable is not compatable with Macs
359+ # see https://docs.arduino.cc/software/ide-v1/tutorials/PortableIDE
360+ fqfn = os .path .expanduser ("~/Library/Arduino15/preferences.txt" ) # Max OS X
361+ if os .path .exists (fqfn ):
362+ return fqfn
363+
364+ print_err ("File preferences.txt not found on " + platform_name )
342365 return ""
343366
344367
345368def get_preferences_txt (file_fqfn , key ):
346- with open (file_fqfn ) as fd :
347- for line in fd :
369+ with open (file_fqfn ) as file :
370+ for line in file :
348371 name , value = line .partition ("=" )[::2 ]
349372 if name .strip ().lower () == key :
350373 if value .strip ().lower () == 'true' :
351- print_msg ("found compiler.cache_core " + value .strip ()) #D debug
352374 return True
353375 else :
354376 return False
377+ print_err ("Key " + key + " not found in preferences.txt. Default to true." )
355378 return True # If we don't find it just assume it is set True
356379
357380
358381def check_preferences_txt ():
359382 file_fqfn = find_preferences_txt ()
360383 if file_fqfn == "" :
361384 return True # cannot find file assume enabled
362- print_msg ("\n found preferences " + file_fqfn ) #D debug
385+ print_msg ("Using preferences from " + file_fqfn )
363386 return get_preferences_txt (file_fqfn , "compiler.cache_core" )
364387
365388
@@ -368,25 +391,27 @@ def touch(fname, times=None):
368391 os .utime (fname , times )
369392
370393
371- def time_sync (globals_h_fqfn , commonhfile_fqfn ):
372- ts = time .time ()
373- touch (globals_h_fqfn , (ts , ts ))
374- touch (commonhfile_fqfn , (ts , ts ))
394+ def synchronous_touch (globals_h_fqfn , commonhfile_fqfn ):
395+ with open (globals_h_fqfn , 'a' ):
396+ os .utime (globals_h_fqfn )
397+ ts = os .stat (globals_h_fqfn )
398+ with open (commonhfile_fqfn , 'a' ):
399+ os .utime (commonhfile_fqfn , ns = (ts .st_atime_ns , ts .st_mtime_ns ))
375400
376401
377402def main ():
378403 global build_opt_signature
379404 global docs_url
380405 num_include_lines = 1
381- use_aggressive_caching_workaround = check_preferences_txt () #? preliminary
406+ use_aggressive_caching_workaround = check_preferences_txt ()
382407
383408 if len (sys .argv ) >= 5 :
384409 source_globals_h_fqfn = os .path .normpath (sys .argv [1 ])
385410 globals_name = os .path .basename (source_globals_h_fqfn )
386411 globals_h_fqfn = os .path .normpath (sys .argv [2 ])
387412 build_path = os .path .dirname (globals_h_fqfn )
388- build_opt_fqfn = os .path .normpath (sys .argv [3 ])
389413 # Assumption: globals_h_fqfn and build_opt_fqfn have the same dirname
414+ build_opt_fqfn = os .path .normpath (sys .argv [3 ])
390415 commonhfile_fqfn = os .path .normpath (sys .argv [4 ])
391416
392417 if os .path .exists (commonhfile_fqfn ):
@@ -425,21 +450,26 @@ def main():
425450 copy_create_build_file (source_globals_h_fqfn , globals_h_fqfn )
426451
427452 # globals_h_fqfn timestamp was only updated if the source changed. This
428- # controls the rebuild on change. We can always extact a new build.opt
453+ # controls the rebuild on change. We can always extract a new build.opt
429454 # w/o triggering a needless rebuild.
430455 embedded_options = extract_create_build_opt_file (globals_h_fqfn , globals_name , build_opt_fqfn )
431456
432457 if use_aggressive_caching_workaround :
458+ # When a Sketch owns a "Sketch.ino.globals.h" file in the build tree
459+ # that exactly matches the timestamp of "CommonHFile.h" in the
460+ # platform source tree, it owns the core cache. If not, or
461+ # "Sketch.ino.globals.h" has changed, rebuild core.
433462 if os .path .getsize (commonhfile_fqfn ):
434- print ("os.path.getmtime(globals_h_fqfn) " + str (os .path .getmtime (globals_h_fqfn )))
435- print ("os.path.getmtime(commonhfile_fqfn) " + str (os .path .getmtime (commonhfile_fqfn )))
436463 if (os .path .getmtime (globals_h_fqfn ) != os .path .getmtime (commonhfile_fqfn )):
437464 # Need to rebuild core.a
438465 # touching commonhfile_fqfn in the source core tree will cause rebuild.
439- time_sync (globals_h_fqfn , commonhfile_fqfn )
466+ # Looks like touching or writing unrelated files in the source core tree will cause rebuild.
467+ synchronous_touch (globals_h_fqfn , commonhfile_fqfn )
468+ print_msg ("Using 'aggressive caching' workaround." )
440469 elif os .path .getsize (globals_h_fqfn ):
441470 enable_override (True , commonhfile_fqfn )
442- time_sync (globals_h_fqfn , commonhfile_fqfn )
471+ synchronous_touch (globals_h_fqfn , commonhfile_fqfn )
472+ print_msg ("Using 'aggressive caching' workaround." )
443473
444474 add_include_line (build_opt_fqfn , commonhfile_fqfn )
445475 add_include_line (build_opt_fqfn , globals_h_fqfn )
0 commit comments