My Marlin configs for Fabrikator Mini and CTC i3 Pro B
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

common-dependencies.py 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. #
  2. # common-dependencies.py
  3. # Convenience script to check dependencies and add libs and sources for Marlin Enabled Features
  4. #
  5. import subprocess,os,re
  6. PIO_VERSION_MIN = (5, 0, 3)
  7. try:
  8. from platformio import VERSION as PIO_VERSION
  9. weights = (1000, 100, 1)
  10. version_min = sum([x[0] * float(re.sub(r'[^0-9]', '.', str(x[1]))) for x in zip(weights, PIO_VERSION_MIN)])
  11. version_cur = sum([x[0] * float(re.sub(r'[^0-9]', '.', str(x[1]))) for x in zip(weights, PIO_VERSION)])
  12. if version_cur < version_min:
  13. print()
  14. print("**************************************************")
  15. print("****** An update to PlatformIO is ******")
  16. print("****** required to build Marlin Firmware. ******")
  17. print("****** ******")
  18. print("****** Minimum version: ", PIO_VERSION_MIN, " ******")
  19. print("****** Current Version: ", PIO_VERSION, " ******")
  20. print("****** ******")
  21. print("****** Update PlatformIO and try again. ******")
  22. print("**************************************************")
  23. print()
  24. exit(1)
  25. except SystemExit:
  26. exit(1)
  27. except:
  28. print("Can't detect PlatformIO Version")
  29. from platformio.package.meta import PackageSpec
  30. from platformio.project.config import ProjectConfig
  31. Import("env")
  32. #print(env.Dump())
  33. try:
  34. verbose = int(env.GetProjectOption('custom_verbose'))
  35. except:
  36. verbose = 0
  37. def blab(str):
  38. if verbose:
  39. print(str)
  40. FEATURE_CONFIG = {}
  41. def add_to_feat_cnf(feature, flines):
  42. try:
  43. feat = FEATURE_CONFIG[feature]
  44. except:
  45. FEATURE_CONFIG[feature] = {}
  46. # Get a reference to the FEATURE_CONFIG under construction
  47. feat = FEATURE_CONFIG[feature]
  48. # Split up passed lines on commas or newlines and iterate
  49. # Add common options to the features config under construction
  50. # For lib_deps replace a previous instance of the same library
  51. atoms = re.sub(r',\\s*', '\n', flines).strip().split('\n')
  52. for line in atoms:
  53. parts = line.split('=')
  54. name = parts.pop(0)
  55. if name in ['build_flags', 'extra_scripts', 'src_filter', 'lib_ignore']:
  56. feat[name] = '='.join(parts)
  57. else:
  58. for dep in line.split(','):
  59. lib_name = re.sub(r'@([~^]|[<>]=?)?[\d.]+', '', dep.strip()).split('=').pop(0)
  60. lib_re = re.compile('(?!^' + lib_name + '\\b)')
  61. feat['lib_deps'] = list(filter(lib_re.match, feat['lib_deps'])) + [dep]
  62. def load_config():
  63. items = ProjectConfig().items('features')
  64. for key in items:
  65. feature = key[0].upper()
  66. if not feature in FEATURE_CONFIG:
  67. FEATURE_CONFIG[feature] = { 'lib_deps': [] }
  68. add_to_feat_cnf(feature, key[1])
  69. # Add options matching custom_marlin.MY_OPTION to the pile
  70. all_opts = env.GetProjectOptions()
  71. for n in all_opts:
  72. mat = re.match(r'custom_marlin\.(.+)', n[0])
  73. if mat:
  74. try:
  75. val = env.GetProjectOption(n[0])
  76. except:
  77. val = None
  78. if val:
  79. add_to_feat_cnf(mat.group(1).upper(), val)
  80. def get_all_known_libs():
  81. known_libs = []
  82. for feature in FEATURE_CONFIG:
  83. feat = FEATURE_CONFIG[feature]
  84. if not 'lib_deps' in feat:
  85. continue
  86. for dep in feat['lib_deps']:
  87. known_libs.append(PackageSpec(dep).name)
  88. return known_libs
  89. def get_all_env_libs():
  90. env_libs = []
  91. lib_deps = env.GetProjectOption('lib_deps')
  92. for dep in lib_deps:
  93. env_libs.append(PackageSpec(dep).name)
  94. return env_libs
  95. def set_env_field(field, value):
  96. proj = env.GetProjectConfig()
  97. proj.set("env:" + env['PIOENV'], field, value)
  98. # All unused libs should be ignored so that if a library
  99. # exists in .pio/lib_deps it will not break compilation.
  100. def force_ignore_unused_libs():
  101. env_libs = get_all_env_libs()
  102. known_libs = get_all_known_libs()
  103. diff = (list(set(known_libs) - set(env_libs)))
  104. lib_ignore = env.GetProjectOption('lib_ignore') + diff
  105. blab("Ignore libraries: %s" % lib_ignore)
  106. set_env_field('lib_ignore', lib_ignore)
  107. def apply_features_config():
  108. load_config()
  109. for feature in FEATURE_CONFIG:
  110. if not env.MarlinFeatureIsEnabled(feature):
  111. continue
  112. feat = FEATURE_CONFIG[feature]
  113. if 'lib_deps' in feat and len(feat['lib_deps']):
  114. blab("Adding lib_deps for %s... " % feature)
  115. # feat to add
  116. deps_to_add = {}
  117. for dep in feat['lib_deps']:
  118. deps_to_add[PackageSpec(dep).name] = dep
  119. # Does the env already have the dependency?
  120. deps = env.GetProjectOption('lib_deps')
  121. for dep in deps:
  122. name = PackageSpec(dep).name
  123. if name in deps_to_add:
  124. del deps_to_add[name]
  125. # Are there any libraries that should be ignored?
  126. lib_ignore = env.GetProjectOption('lib_ignore')
  127. for dep in deps:
  128. name = PackageSpec(dep).name
  129. if name in deps_to_add:
  130. del deps_to_add[name]
  131. # Is there anything left?
  132. if len(deps_to_add) > 0:
  133. # Only add the missing dependencies
  134. set_env_field('lib_deps', deps + list(deps_to_add.values()))
  135. if 'build_flags' in feat:
  136. f = feat['build_flags']
  137. blab("Adding build_flags for %s: %s" % (feature, f))
  138. new_flags = env.GetProjectOption('build_flags') + [ f ]
  139. env.Replace(BUILD_FLAGS=new_flags)
  140. if 'extra_scripts' in feat:
  141. blab("Running extra_scripts for %s... " % feature)
  142. env.SConscript(feat['extra_scripts'], exports="env")
  143. if 'src_filter' in feat:
  144. blab("Adding src_filter for %s... " % feature)
  145. src_filter = ' '.join(env.GetProjectOption('src_filter'))
  146. # first we need to remove the references to the same folder
  147. my_srcs = re.findall(r'[+-](<.*?>)', feat['src_filter'])
  148. cur_srcs = re.findall(r'[+-](<.*?>)', src_filter)
  149. for d in my_srcs:
  150. if d in cur_srcs:
  151. src_filter = re.sub(r'[+-]' + d, '', src_filter)
  152. src_filter = feat['src_filter'] + ' ' + src_filter
  153. set_env_field('src_filter', [src_filter])
  154. env.Replace(SRC_FILTER=src_filter)
  155. if 'lib_ignore' in feat:
  156. blab("Adding lib_ignore for %s... " % feature)
  157. lib_ignore = env.GetProjectOption('lib_ignore') + [feat['lib_ignore']]
  158. set_env_field('lib_ignore', lib_ignore)
  159. #
  160. # Find a compiler, considering the OS
  161. #
  162. ENV_BUILD_PATH = os.path.join(env.Dictionary('PROJECT_BUILD_DIR'), env['PIOENV'])
  163. GCC_PATH_CACHE = os.path.join(ENV_BUILD_PATH, ".gcc_path")
  164. def search_compiler():
  165. try:
  166. filepath = env.GetProjectOption('custom_gcc')
  167. blab("Getting compiler from env")
  168. return filepath
  169. except:
  170. pass
  171. if os.path.exists(GCC_PATH_CACHE):
  172. blab("Getting g++ path from cache")
  173. with open(GCC_PATH_CACHE, 'r') as f:
  174. return f.read()
  175. # Find the current platform compiler by searching the $PATH
  176. # which will be in a platformio toolchain bin folder
  177. path_regex = re.escape(env['PROJECT_PACKAGES_DIR'])
  178. gcc = "g++"
  179. if env['PLATFORM'] == 'win32':
  180. path_separator = ';'
  181. path_regex += r'.*\\bin'
  182. gcc += ".exe"
  183. else:
  184. path_separator = ':'
  185. path_regex += r'/.+/bin'
  186. # Search for the compiler
  187. for pathdir in env['ENV']['PATH'].split(path_separator):
  188. if not re.search(path_regex, pathdir, re.IGNORECASE):
  189. continue
  190. for filepath in os.listdir(pathdir):
  191. if not filepath.endswith(gcc):
  192. continue
  193. # Use entire path to not rely on env PATH
  194. filepath = os.path.sep.join([pathdir, filepath])
  195. # Cache the g++ path to no search always
  196. if os.path.exists(ENV_BUILD_PATH):
  197. blab("Caching g++ for current env")
  198. with open(GCC_PATH_CACHE, 'w+') as f:
  199. f.write(filepath)
  200. return filepath
  201. filepath = env.get('CXX')
  202. blab("Couldn't find a compiler! Fallback to %s" % filepath)
  203. return filepath
  204. #
  205. # Use the compiler to get a list of all enabled features
  206. #
  207. def load_marlin_features():
  208. if 'MARLIN_FEATURES' in env:
  209. return
  210. # Process defines
  211. build_flags = env.get('BUILD_FLAGS')
  212. build_flags = env.ParseFlagsExtended(build_flags)
  213. cxx = search_compiler()
  214. cmd = ['"' + cxx + '"']
  215. # Build flags from board.json
  216. #if 'BOARD' in env:
  217. # cmd += [env.BoardConfig().get("build.extra_flags")]
  218. for s in build_flags['CPPDEFINES']:
  219. if isinstance(s, tuple):
  220. cmd += ['-D' + s[0] + '=' + str(s[1])]
  221. else:
  222. cmd += ['-D' + s]
  223. cmd += ['-D__MARLIN_DEPS__ -w -dM -E -x c++ buildroot/share/PlatformIO/scripts/common-dependencies.h']
  224. cmd = ' '.join(cmd)
  225. blab(cmd)
  226. define_list = subprocess.check_output(cmd, shell=True).splitlines()
  227. marlin_features = {}
  228. for define in define_list:
  229. feature = define[8:].strip().decode().split(' ')
  230. feature, definition = feature[0], ' '.join(feature[1:])
  231. marlin_features[feature] = definition
  232. env['MARLIN_FEATURES'] = marlin_features
  233. #
  234. # Return True if a matching feature is enabled
  235. #
  236. def MarlinFeatureIsEnabled(env, feature):
  237. load_marlin_features()
  238. r = re.compile('^' + feature + '$')
  239. found = list(filter(r.match, env['MARLIN_FEATURES']))
  240. # Defines could still be 'false' or '0', so check
  241. some_on = False
  242. if len(found):
  243. for f in found:
  244. val = env['MARLIN_FEATURES'][f]
  245. if val in [ '', '1', 'true' ]:
  246. some_on = True
  247. elif val in env['MARLIN_FEATURES']:
  248. some_on = env.MarlinFeatureIsEnabled(val)
  249. return some_on
  250. #
  251. # Add a method for other PIO scripts to query enabled features
  252. #
  253. env.AddMethod(MarlinFeatureIsEnabled)
  254. #
  255. # Add dependencies for enabled Marlin features
  256. #
  257. apply_features_config()
  258. force_ignore_unused_libs()