SConstruct 10 KB

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