SConstruct 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. import os
  2. import subprocess
  3. import sys
  4. import sysconfig
  5. import platform
  6. import numpy as np
  7. import SCons.Errors
  8. SCons.Warnings.warningAsException(True)
  9. # pending upstream fix - https://github.com/SCons/scons/issues/4461
  10. #SetOption('warn', 'all')
  11. TICI = os.path.isfile('/TICI')
  12. AGNOS = TICI
  13. Decider('MD5-timestamp')
  14. SetOption('num_jobs', int(os.cpu_count()/2))
  15. AddOption('--kaitai',
  16. action='store_true',
  17. help='Regenerate kaitai struct parsers')
  18. AddOption('--asan',
  19. action='store_true',
  20. help='turn on ASAN')
  21. AddOption('--ubsan',
  22. action='store_true',
  23. help='turn on UBSan')
  24. AddOption('--coverage',
  25. action='store_true',
  26. help='build with test coverage options')
  27. AddOption('--clazy',
  28. action='store_true',
  29. help='build with clazy')
  30. AddOption('--compile_db',
  31. action='store_true',
  32. help='build clang compilation database')
  33. AddOption('--ccflags',
  34. action='store',
  35. type='string',
  36. default='',
  37. help='pass arbitrary flags over the command line')
  38. AddOption('--snpe',
  39. action='store_true',
  40. help='use SNPE on PC')
  41. AddOption('--external-sconscript',
  42. action='store',
  43. metavar='FILE',
  44. dest='external_sconscript',
  45. help='add an external SConscript to the build')
  46. AddOption('--pc-thneed',
  47. action='store_true',
  48. dest='pc_thneed',
  49. help='use thneed on pc')
  50. AddOption('--minimal',
  51. action='store_false',
  52. dest='extras',
  53. default=os.path.exists(File('#.lfsconfig').abspath), # minimal by default on release branch (where there's no LFS)
  54. help='the minimum build to run openpilot. no tests, tools, etc.')
  55. ## Architecture name breakdown (arch)
  56. ## - larch64: linux tici aarch64
  57. ## - aarch64: linux pc aarch64
  58. ## - x86_64: linux pc x64
  59. ## - Darwin: mac x64 or arm64
  60. real_arch = arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
  61. if platform.system() == "Darwin":
  62. arch = "Darwin"
  63. brew_prefix = subprocess.check_output(['brew', '--prefix'], encoding='utf8').strip()
  64. elif arch == "aarch64" and AGNOS:
  65. arch = "larch64"
  66. assert arch in ["larch64", "aarch64", "x86_64", "Darwin"]
  67. lenv = {
  68. "PATH": os.environ['PATH'],
  69. "LD_LIBRARY_PATH": [Dir(f"#third_party/acados/{arch}/lib").abspath],
  70. "PYTHONPATH": Dir("#").abspath + ':' + Dir(f"#third_party/acados").abspath,
  71. "ACADOS_SOURCE_DIR": Dir("#third_party/acados").abspath,
  72. "ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath,
  73. "TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer"
  74. }
  75. rpath = lenv["LD_LIBRARY_PATH"].copy()
  76. if arch == "larch64":
  77. cpppath = [
  78. "#third_party/opencl/include",
  79. ]
  80. libpath = [
  81. "/usr/local/lib",
  82. "/usr/lib",
  83. "/system/vendor/lib64",
  84. f"#third_party/acados/{arch}/lib",
  85. ]
  86. libpath += [
  87. "#third_party/snpe/larch64",
  88. "#third_party/libyuv/larch64/lib",
  89. "/usr/lib/aarch64-linux-gnu"
  90. ]
  91. cflags = ["-DQCOM2", "-mcpu=cortex-a57"]
  92. cxxflags = ["-DQCOM2", "-mcpu=cortex-a57"]
  93. rpath += ["/usr/local/lib"]
  94. else:
  95. cflags = []
  96. cxxflags = []
  97. cpppath = []
  98. rpath += []
  99. # MacOS
  100. if arch == "Darwin":
  101. libpath = [
  102. f"#third_party/libyuv/{arch}/lib",
  103. f"#third_party/acados/{arch}/lib",
  104. f"{brew_prefix}/lib",
  105. f"{brew_prefix}/opt/openssl@3.0/lib",
  106. "/System/Library/Frameworks/OpenGL.framework/Libraries",
  107. ]
  108. cflags += ["-DGL_SILENCE_DEPRECATION"]
  109. cxxflags += ["-DGL_SILENCE_DEPRECATION"]
  110. cpppath += [
  111. f"{brew_prefix}/include",
  112. f"{brew_prefix}/opt/openssl@3.0/include",
  113. ]
  114. lenv["DYLD_LIBRARY_PATH"] = lenv["LD_LIBRARY_PATH"]
  115. # Linux
  116. else:
  117. libpath = [
  118. f"#third_party/acados/{arch}/lib",
  119. f"#third_party/libyuv/{arch}/lib",
  120. "/usr/lib",
  121. "/usr/local/lib",
  122. ]
  123. if arch == "x86_64":
  124. libpath += [
  125. f"#third_party/snpe/{arch}"
  126. ]
  127. rpath += [
  128. Dir(f"#third_party/snpe/{arch}").abspath,
  129. ]
  130. if GetOption('asan'):
  131. ccflags = ["-fsanitize=address", "-fno-omit-frame-pointer"]
  132. ldflags = ["-fsanitize=address"]
  133. elif GetOption('ubsan'):
  134. ccflags = ["-fsanitize=undefined"]
  135. ldflags = ["-fsanitize=undefined"]
  136. else:
  137. ccflags = []
  138. ldflags = []
  139. # no --as-needed on mac linker
  140. if arch != "Darwin":
  141. ldflags += ["-Wl,--as-needed", "-Wl,--no-undefined"]
  142. # Enable swaglog include in submodules
  143. cflags += ['-DSWAGLOG="\\"common/swaglog.h\\""']
  144. cxxflags += ['-DSWAGLOG="\\"common/swaglog.h\\""']
  145. ccflags_option = GetOption('ccflags')
  146. if ccflags_option:
  147. ccflags += ccflags_option.split(' ')
  148. env = Environment(
  149. ENV=lenv,
  150. CCFLAGS=[
  151. "-g",
  152. "-fPIC",
  153. "-O2",
  154. "-Wunused",
  155. "-Werror",
  156. "-Wshadow",
  157. "-Wno-unknown-warning-option",
  158. "-Wno-deprecated-register",
  159. "-Wno-register",
  160. "-Wno-inconsistent-missing-override",
  161. "-Wno-c99-designator",
  162. "-Wno-reorder-init-list",
  163. "-Wno-error=unused-but-set-variable",
  164. "-Wno-vla-cxx-extension",
  165. ] + cflags + ccflags,
  166. CPPPATH=cpppath + [
  167. "#",
  168. "#third_party/acados/include",
  169. "#third_party/acados/include/blasfeo/include",
  170. "#third_party/acados/include/hpipm/include",
  171. "#third_party/catch2/include",
  172. "#third_party/libyuv/include",
  173. "#third_party/json11",
  174. "#third_party/linux/include",
  175. "#third_party/snpe/include",
  176. "#third_party/qrcode",
  177. "#third_party",
  178. "#cereal",
  179. "#msgq",
  180. "#opendbc/can",
  181. "#third_party/maplibre-native-qt/include",
  182. f"#third_party/maplibre-native-qt/{arch}/include"
  183. ],
  184. CC='clang',
  185. CXX='clang++',
  186. LINKFLAGS=ldflags,
  187. RPATH=rpath,
  188. CFLAGS=["-std=gnu11"] + cflags,
  189. CXXFLAGS=["-std=c++1z"] + cxxflags,
  190. LIBPATH=libpath + [
  191. "#msgq_repo",
  192. "#third_party",
  193. "#selfdrive/pandad",
  194. "#common",
  195. "#rednose/helpers",
  196. ],
  197. CYTHONCFILESUFFIX=".cpp",
  198. COMPILATIONDB_USE_ABSPATH=True,
  199. REDNOSE_ROOT="#",
  200. tools=["default", "cython", "compilation_db", "rednose_filter"],
  201. toolpath=["#rednose_repo/site_scons/site_tools"],
  202. )
  203. if arch == "Darwin":
  204. # RPATH is not supported on macOS, instead use the linker flags
  205. darwin_rpath_link_flags = [f"-Wl,-rpath,{path}" for path in env["RPATH"]]
  206. env["LINKFLAGS"] += darwin_rpath_link_flags
  207. if GetOption('compile_db'):
  208. env.CompilationDatabase('compile_commands.json')
  209. # Setup cache dir
  210. cache_dir = '/data/scons_cache' if AGNOS else '/tmp/scons_cache'
  211. CacheDir(cache_dir)
  212. Clean(["."], cache_dir)
  213. node_interval = 5
  214. node_count = 0
  215. def progress_function(node):
  216. global node_count
  217. node_count += node_interval
  218. sys.stderr.write("progress: %d\n" % node_count)
  219. if os.environ.get('SCONS_PROGRESS'):
  220. Progress(progress_function, interval=node_interval)
  221. # Cython build environment
  222. py_include = sysconfig.get_paths()['include']
  223. envCython = env.Clone()
  224. envCython["CPPPATH"] += [py_include, np.get_include()]
  225. envCython["CCFLAGS"] += ["-Wno-#warnings", "-Wno-shadow", "-Wno-deprecated-declarations"]
  226. envCython["CCFLAGS"].remove("-Werror")
  227. envCython["LIBS"] = []
  228. if arch == "Darwin":
  229. envCython["LINKFLAGS"] = ["-bundle", "-undefined", "dynamic_lookup"] + darwin_rpath_link_flags
  230. else:
  231. envCython["LINKFLAGS"] = ["-pthread", "-shared"]
  232. Export('envCython')
  233. # Qt build environment
  234. qt_env = env.Clone()
  235. qt_modules = ["Widgets", "Gui", "Core", "Network", "Concurrent", "Multimedia", "Quick", "Qml", "QuickWidgets", "Location", "Positioning", "DBus", "Xml"]
  236. qt_libs = []
  237. if arch == "Darwin":
  238. qt_env['QTDIR'] = f"{brew_prefix}/opt/qt@5"
  239. qt_dirs = [
  240. os.path.join(qt_env['QTDIR'], "include"),
  241. ]
  242. qt_dirs += [f"{qt_env['QTDIR']}/include/Qt{m}" for m in qt_modules]
  243. qt_env["LINKFLAGS"] += ["-F" + os.path.join(qt_env['QTDIR'], "lib")]
  244. qt_env["FRAMEWORKS"] += [f"Qt{m}" for m in qt_modules] + ["OpenGL"]
  245. qt_env.AppendENVPath('PATH', os.path.join(qt_env['QTDIR'], "bin"))
  246. else:
  247. qt_install_prefix = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_PREFIX'], encoding='utf8').strip()
  248. qt_install_headers = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_HEADERS'], encoding='utf8').strip()
  249. qt_env['QTDIR'] = qt_install_prefix
  250. qt_dirs = [
  251. f"{qt_install_headers}",
  252. ]
  253. qt_gui_path = os.path.join(qt_install_headers, "QtGui")
  254. qt_gui_dirs = [d for d in os.listdir(qt_gui_path) if os.path.isdir(os.path.join(qt_gui_path, d))]
  255. qt_dirs += [f"{qt_install_headers}/QtGui/{qt_gui_dirs[0]}/QtGui", ] if qt_gui_dirs else []
  256. qt_dirs += [f"{qt_install_headers}/Qt{m}" for m in qt_modules]
  257. qt_libs = [f"Qt5{m}" for m in qt_modules]
  258. if arch == "larch64":
  259. qt_libs += ["GLESv2", "wayland-client"]
  260. qt_env.PrependENVPath('PATH', Dir("#third_party/qt5/larch64/bin/").abspath)
  261. elif arch != "Darwin":
  262. qt_libs += ["GL"]
  263. qt_env['QT3DIR'] = qt_env['QTDIR']
  264. # compatibility for older SCons versions
  265. try:
  266. qt_env.Tool('qt3')
  267. except SCons.Errors.UserError:
  268. qt_env.Tool('qt')
  269. qt_env['CPPPATH'] += qt_dirs# + ["#selfdrive/ui/qt/"]
  270. qt_flags = [
  271. "-D_REENTRANT",
  272. "-DQT_NO_DEBUG",
  273. "-DQT_WIDGETS_LIB",
  274. "-DQT_GUI_LIB",
  275. "-DQT_QUICK_LIB",
  276. "-DQT_QUICKWIDGETS_LIB",
  277. "-DQT_QML_LIB",
  278. "-DQT_CORE_LIB",
  279. "-DQT_MESSAGELOGCONTEXT",
  280. ]
  281. qt_env['CXXFLAGS'] += qt_flags
  282. qt_env['LIBPATH'] += ['#selfdrive/ui', f"#third_party/maplibre-native-qt/{arch}/lib"]
  283. qt_env['RPATH'] += [Dir(f"#third_party/maplibre-native-qt/{arch}/lib").srcnode().abspath]
  284. qt_env['LIBS'] = qt_libs
  285. if GetOption("clazy"):
  286. checks = [
  287. "level0",
  288. "level1",
  289. "no-range-loop",
  290. "no-non-pod-global-static",
  291. ]
  292. qt_env['CXX'] = 'clazy'
  293. qt_env['ENV']['CLAZY_IGNORE_DIRS'] = qt_dirs[0]
  294. qt_env['ENV']['CLAZY_CHECKS'] = ','.join(checks)
  295. Export('env', 'qt_env', 'arch', 'real_arch')
  296. # Build common module
  297. SConscript(['common/SConscript'])
  298. Import('_common', '_gpucommon')
  299. common = [_common, 'json11']
  300. gpucommon = [_gpucommon]
  301. Export('common', 'gpucommon')
  302. # Build messaging (cereal + msgq + socketmaster + their dependencies)
  303. SConscript(['msgq_repo/SConscript'])
  304. SConscript(['cereal/SConscript'])
  305. Import('socketmaster', 'msgq')
  306. messaging = [socketmaster, msgq, 'zmq', 'capnp', 'kj',]
  307. Export('messaging')
  308. # Build other submodules
  309. SConscript([
  310. 'body/board/SConscript',
  311. 'opendbc/can/SConscript',
  312. 'panda/SConscript',
  313. ])
  314. # Build rednose library
  315. SConscript(['rednose/SConscript'])
  316. # Build system services
  317. SConscript([
  318. 'system/proclogd/SConscript',
  319. 'system/ubloxd/SConscript',
  320. 'system/loggerd/SConscript',
  321. ])
  322. if arch != "Darwin":
  323. SConscript([
  324. 'system/sensord/SConscript',
  325. 'system/logcatd/SConscript',
  326. ])
  327. if arch == "larch64":
  328. SConscript(['system/camerad/SConscript'])
  329. # Build openpilot
  330. SConscript(['third_party/SConscript'])
  331. SConscript(['selfdrive/SConscript'])
  332. if Dir('#tools/cabana/').exists() and GetOption('extras'):
  333. SConscript(['tools/replay/SConscript'])
  334. if arch != "larch64":
  335. SConscript(['tools/cabana/SConscript'])
  336. external_sconscript = GetOption('external_sconscript')
  337. if external_sconscript:
  338. SConscript([external_sconscript])