SConstruct 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. #!/usr/bin/env python
  2. EnsureSConsVersion(0, 98, 1)
  3. import string
  4. import os
  5. import os.path
  6. import glob
  7. import sys
  8. import methods
  9. # moved below to compensate with module version string
  10. # methods.update_version()
  11. # scan possible build platforms
  12. platform_list = [] # list of platforms
  13. platform_opts = {} # options for each platform
  14. platform_flags = {} # flags for each platform
  15. active_platforms = []
  16. active_platform_ids = []
  17. platform_exporters = []
  18. global_defaults = []
  19. for x in glob.glob("platform/*"):
  20. if (not os.path.isdir(x) or not os.path.exists(x + "/detect.py")):
  21. continue
  22. tmppath = "./" + x
  23. sys.path.append(tmppath)
  24. import detect
  25. if (os.path.exists(x + "/export/export.cpp")):
  26. platform_exporters.append(x[9:])
  27. if (os.path.exists(x + "/globals/global_defaults.cpp")):
  28. global_defaults.append(x[9:])
  29. if (detect.is_active()):
  30. active_platforms.append(detect.get_name())
  31. active_platform_ids.append(x)
  32. if (detect.can_build()):
  33. x = x.replace("platform/", "") # rest of world
  34. x = x.replace("platform\\", "") # win32
  35. platform_list += [x]
  36. platform_opts[x] = detect.get_opts()
  37. platform_flags[x] = detect.get_flags()
  38. sys.path.remove(tmppath)
  39. sys.modules.pop('detect')
  40. module_list = methods.detect_modules()
  41. # print "Detected Platforms: "+str(platform_list)
  42. methods.save_active_platforms(active_platforms, active_platform_ids)
  43. custom_tools = ['default']
  44. platform_arg = ARGUMENTS.get("platform", ARGUMENTS.get("p", False))
  45. if (os.name == "posix"):
  46. pass
  47. elif (os.name == "nt"):
  48. if (os.getenv("VCINSTALLDIR") == None or platform_arg == "android" or platform_arg == "javascript"):
  49. custom_tools = ['mingw']
  50. env_base = Environment(tools=custom_tools)
  51. if 'TERM' in os.environ:
  52. env_base['ENV']['TERM'] = os.environ['TERM']
  53. env_base.AppendENVPath('PATH', os.getenv('PATH'))
  54. env_base.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
  55. env_base.global_defaults = global_defaults
  56. env_base.android_maven_repos = []
  57. env_base.android_flat_dirs = []
  58. env_base.android_dependencies = []
  59. env_base.android_gradle_plugins = []
  60. env_base.android_gradle_classpath = []
  61. env_base.android_java_dirs = []
  62. env_base.android_res_dirs = []
  63. env_base.android_aidl_dirs = []
  64. env_base.android_jni_dirs = []
  65. env_base.android_default_config = []
  66. env_base.android_manifest_chunk = ""
  67. env_base.android_permission_chunk = ""
  68. env_base.android_appattributes_chunk = ""
  69. env_base.disabled_modules = []
  70. env_base.use_ptrcall = False
  71. env_base.split_drivers = False
  72. env_base.module_version_string = ""
  73. # To decide whether to rebuild a file, use the MD5 sum only if the timestamp has changed.
  74. # http://scons.org/doc/production/HTML/scons-user/ch06.html#idm139837621851792
  75. env_base.Decider('MD5-timestamp')
  76. # Use cached implicit dependencies by default. Can be overridden by specifying `--implicit-deps-changed` in the command line.
  77. # http://scons.org/doc/production/HTML/scons-user/ch06s04.html
  78. env_base.SetOption('implicit_cache', 1)
  79. env_base.__class__.android_add_maven_repository = methods.android_add_maven_repository
  80. env_base.__class__.android_add_flat_dir = methods.android_add_flat_dir
  81. env_base.__class__.android_add_dependency = methods.android_add_dependency
  82. env_base.__class__.android_add_java_dir = methods.android_add_java_dir
  83. env_base.__class__.android_add_res_dir = methods.android_add_res_dir
  84. env_base.__class__.android_add_aidl_dir = methods.android_add_aidl_dir
  85. env_base.__class__.android_add_jni_dir = methods.android_add_jni_dir
  86. env_base.__class__.android_add_default_config = methods.android_add_default_config
  87. env_base.__class__.android_add_to_manifest = methods.android_add_to_manifest
  88. env_base.__class__.android_add_to_permissions = methods.android_add_to_permissions
  89. env_base.__class__.android_add_to_attributes = methods.android_add_to_attributes
  90. env_base.__class__.android_add_gradle_plugin = methods.android_add_gradle_plugin
  91. env_base.__class__.android_add_gradle_classpath = methods.android_add_gradle_classpath
  92. env_base.__class__.disable_module = methods.disable_module
  93. env_base.__class__.add_module_version_string = methods.add_module_version_string
  94. env_base.__class__.add_source_files = methods.add_source_files
  95. env_base.__class__.use_windows_spawn_fix = methods.use_windows_spawn_fix
  96. env_base.__class__.split_lib = methods.split_lib
  97. env_base["x86_libtheora_opt_gcc"] = False
  98. env_base["x86_libtheora_opt_vc"] = False
  99. # Build options
  100. customs = ['custom.py']
  101. profile = ARGUMENTS.get("profile", False)
  102. if profile:
  103. import os.path
  104. if os.path.isfile(profile):
  105. customs.append(profile)
  106. elif os.path.isfile(profile + ".py"):
  107. customs.append(profile + ".py")
  108. opts = Variables(customs, ARGUMENTS)
  109. # Target build options
  110. opts.Add('arch', "Platform-dependent architecture (arm/arm64/x86/x64/mips/etc)", '')
  111. opts.Add(EnumVariable('bits', "Target platform bits", 'default', ('default', '32', '64', 'fat')))
  112. opts.Add('p', "Platform (alias for 'platform')", '')
  113. opts.Add('platform', "Target platform (%s)" % ('|'.join(platform_list), ), '')
  114. opts.Add(EnumVariable('target', "Compilation target", 'debug', ('debug', 'release_debug', 'release')))
  115. opts.Add(BoolVariable('tools', "Build the tools a.k.a. the Godot editor", True))
  116. opts.Add(BoolVariable('use_lto', 'Use linking time optimization', False))
  117. # Components
  118. opts.Add(BoolVariable('deprecated', "Enable deprecated features", True))
  119. opts.Add(BoolVariable('gdscript', "Build GDSCript support", True))
  120. opts.Add(BoolVariable('minizip', "Build minizip archive support", True))
  121. opts.Add(BoolVariable('xaudio2', "XAudio2 audio driver", False))
  122. opts.Add(BoolVariable('xml', "XML format support for resources", True))
  123. # Advanced options
  124. opts.Add(BoolVariable('disable_3d', "Disable 3D nodes for smaller executable", False))
  125. opts.Add(BoolVariable('disable_advanced_gui', "Disable advance 3D gui nodes and behaviors", False))
  126. opts.Add('extra_suffix', "Custom extra suffix added to the base filename of all generated binary files", '')
  127. opts.Add('unix_global_settings_path', "UNIX-specific path to system-wide settings. Currently only used for templates", '')
  128. opts.Add(BoolVariable('verbose', "Enable verbose output for the compilation", False))
  129. opts.Add(BoolVariable('vsproj', "Generate Visual Studio Project.", False))
  130. opts.Add(EnumVariable('warnings', "Set the level of warnings emitted during compilation", 'no', ('extra', 'all', 'moderate', 'no')))
  131. opts.Add(BoolVariable('progress', "Show a progress indicator during build", True))
  132. opts.Add(BoolVariable('dev', "If yes, alias for verbose=yes warnings=all", False))
  133. # Thirdparty libraries
  134. opts.Add(BoolVariable('builtin_enet', "Use the builtin enet library", True))
  135. opts.Add(BoolVariable('builtin_freetype', "Use the builtin freetype library", True))
  136. opts.Add(BoolVariable('builtin_libogg', "Use the builtin libogg library", True))
  137. opts.Add(BoolVariable('builtin_libpng', "Use the builtin libpng library", True))
  138. opts.Add(BoolVariable('builtin_libtheora', "Use the builtin libtheora library", True))
  139. opts.Add(BoolVariable('builtin_libvorbis', "Use the builtin libvorbis library", True))
  140. opts.Add(BoolVariable('builtin_libvpx', "Use the builtin libvpx library", True))
  141. opts.Add(BoolVariable('builtin_libwebp', "Use the builtin libwebp library", True))
  142. opts.Add(BoolVariable('builtin_openssl', "Use the builtin openssl library", True))
  143. opts.Add(BoolVariable('builtin_opus', "Use the builtin opus library", True))
  144. opts.Add(BoolVariable('builtin_pcre2', "Use the builtin pcre2 library)", True))
  145. opts.Add(BoolVariable('builtin_recast', "Use the builtin recast library", True))
  146. opts.Add(BoolVariable('builtin_squish', "Use the builtin squish library", True))
  147. opts.Add(BoolVariable('builtin_zlib', "Use the builtin zlib library", True))
  148. opts.Add(BoolVariable('builtin_zstd', "Use the builtin zstd library", True))
  149. # Environment setup
  150. opts.Add("CXX", "C++ compiler")
  151. opts.Add("CC", "C compiler")
  152. opts.Add("CCFLAGS", "Custom flags for the C and C++ compilers")
  153. opts.Add("CFLAGS", "Custom flags for the C compiler")
  154. opts.Add("LINKFLAGS", "Custom flags for the linker")
  155. # add platform specific options
  156. for k in platform_opts.keys():
  157. opt_list = platform_opts[k]
  158. for o in opt_list:
  159. opts.Add(o)
  160. for x in module_list:
  161. module_enabled = True
  162. tmppath = "./modules/" + x
  163. sys.path.append(tmppath)
  164. import config
  165. enabled_attr = getattr(config, "is_enabled", None)
  166. if (callable(enabled_attr) and not config.is_enabled()):
  167. module_enabled = False
  168. sys.path.remove(tmppath)
  169. sys.modules.pop('config')
  170. opts.Add(BoolVariable('module_' + x + '_enabled', "Enable module '%s'" % (x, ), module_enabled))
  171. opts.Update(env_base) # update environment
  172. Help(opts.GenerateHelpText(env_base)) # generate help
  173. # add default include paths
  174. env_base.Append(CPPPATH=['#core', '#core/math', '#editor', '#drivers', '#'])
  175. # configure ENV for platform
  176. env_base.platform_exporters = platform_exporters
  177. """
  178. sys.path.append("./platform/"+env_base["platform"])
  179. import detect
  180. detect.configure(env_base)
  181. sys.path.remove("./platform/"+env_base["platform"])
  182. sys.modules.pop('detect')
  183. """
  184. if (env_base['target'] == 'debug'):
  185. env_base.Append(CPPFLAGS=['-DDEBUG_MEMORY_ALLOC'])
  186. env_base.Append(CPPFLAGS=['-DSCI_NAMESPACE'])
  187. if not env_base['deprecated']:
  188. env_base.Append(CPPFLAGS=['-DDISABLE_DEPRECATED'])
  189. env_base.platforms = {}
  190. selected_platform = ""
  191. if env_base['platform'] != "":
  192. selected_platform = env_base['platform']
  193. elif env_base['p'] != "":
  194. selected_platform = env_base['p']
  195. env_base["platform"] = selected_platform
  196. if selected_platform in platform_list:
  197. sys.path.append("./platform/" + selected_platform)
  198. import detect
  199. if "create" in dir(detect):
  200. env = detect.create(env_base)
  201. else:
  202. env = env_base.Clone()
  203. if env['dev']:
  204. env["warnings"] = "all"
  205. env['verbose'] = True
  206. if env['vsproj']:
  207. env.vs_incs = []
  208. env.vs_srcs = []
  209. def AddToVSProject(sources):
  210. for x in sources:
  211. if type(x) == type(""):
  212. fname = env.File(x).path
  213. else:
  214. fname = env.File(x)[0].path
  215. pieces = fname.split(".")
  216. if len(pieces) > 0:
  217. basename = pieces[0]
  218. basename = basename.replace('\\\\', '/')
  219. env.vs_srcs = env.vs_srcs + [basename + ".cpp"]
  220. env.vs_incs = env.vs_incs + [basename + ".h"]
  221. # print basename
  222. env.AddToVSProject = AddToVSProject
  223. env.extra_suffix = ""
  224. if env["extra_suffix"] != '':
  225. env.extra_suffix += '.' + env["extra_suffix"]
  226. CCFLAGS = env.get('CCFLAGS', '')
  227. env['CCFLAGS'] = ''
  228. env.Append(CCFLAGS=str(CCFLAGS).split())
  229. CFLAGS = env.get('CFLAGS', '')
  230. env['CFLAGS'] = ''
  231. env.Append(CFLAGS=str(CFLAGS).split())
  232. LINKFLAGS = env.get('LINKFLAGS', '')
  233. env['LINKFLAGS'] = ''
  234. env.Append(LINKFLAGS=str(LINKFLAGS).split())
  235. flag_list = platform_flags[selected_platform]
  236. for f in flag_list:
  237. if not (f[0] in ARGUMENTS): # allow command line to override platform flags
  238. env[f[0]] = f[1]
  239. # must happen after the flags, so when flags are used by configure, stuff happens (ie, ssl on x11)
  240. detect.configure(env)
  241. if (env["warnings"] == 'yes'):
  242. print("WARNING: warnings=yes is deprecated; assuming warnings=all")
  243. env.msvc = 0
  244. if (os.name == "nt" and os.getenv("VCINSTALLDIR") and (platform_arg == "windows" or platform_arg == "uwp")): # MSVC, needs to stand out of course
  245. env.msvc = 1
  246. disable_nonessential_warnings = ['/wd4267', '/wd4244', '/wd4305', '/wd4800'] # Truncations, narrowing conversions...
  247. if (env["warnings"] == 'extra'):
  248. env.Append(CCFLAGS=['/Wall']) # Implies /W4
  249. elif (env["warnings"] == 'all' or env["warnings"] == 'yes'):
  250. env.Append(CCFLAGS=['/W3'] + disable_nonessential_warnings)
  251. elif (env["warnings"] == 'moderate'):
  252. # C4244 shouldn't be needed here being a level-3 warning, but it is
  253. env.Append(CCFLAGS=['/W2'] + disable_nonessential_warnings)
  254. else: # 'no'
  255. env.Append(CCFLAGS=['/w'])
  256. else: # Rest of the world
  257. if (env["warnings"] == 'extra'):
  258. env.Append(CCFLAGS=['-Wall', '-Wextra'])
  259. elif (env["warnings"] == 'all' or env["warnings"] == 'yes'):
  260. env.Append(CCFLAGS=['-Wall'])
  261. elif (env["warnings"] == 'moderate'):
  262. env.Append(CCFLAGS=['-Wall', '-Wno-unused'])
  263. else: # 'no'
  264. env.Append(CCFLAGS=['-w'])
  265. #env['platform_libsuffix'] = env['LIBSUFFIX']
  266. suffix = "." + selected_platform
  267. if (env["target"] == "release"):
  268. if env["tools"]:
  269. print("Tools can only be built with targets 'debug' and 'release_debug'.")
  270. sys.exit(255)
  271. suffix += ".opt"
  272. env.Append(CCFLAGS=['-DNDEBUG'])
  273. elif (env["target"] == "release_debug"):
  274. if env["tools"]:
  275. suffix += ".opt.tools"
  276. else:
  277. suffix += ".opt.debug"
  278. else:
  279. if env["tools"]:
  280. suffix += ".tools"
  281. else:
  282. suffix += ".debug"
  283. if env["arch"] != "":
  284. suffix += "." + env["arch"]
  285. elif (env["bits"] == "32"):
  286. suffix += ".32"
  287. elif (env["bits"] == "64"):
  288. suffix += ".64"
  289. elif (env["bits"] == "fat"):
  290. suffix += ".fat"
  291. suffix += env.extra_suffix
  292. sys.path.remove("./platform/" + selected_platform)
  293. sys.modules.pop('detect')
  294. env.module_list = []
  295. env.doc_class_path={}
  296. for x in module_list:
  297. if not env['module_' + x + '_enabled']:
  298. continue
  299. tmppath = "./modules/" + x
  300. sys.path.append(tmppath)
  301. env.current_module = x
  302. import config
  303. if (config.can_build(selected_platform)):
  304. config.configure(env)
  305. env.module_list.append(x)
  306. try:
  307. doc_classes = config.get_doc_classes()
  308. doc_path = config.get_doc_path()
  309. for c in doc_classes:
  310. env.doc_class_path[c]="modules/"+x+"/"+doc_path
  311. except:
  312. pass
  313. sys.path.remove(tmppath)
  314. sys.modules.pop('config')
  315. methods.update_version(env.module_version_string)
  316. suffix += env.module_version_string
  317. env["PROGSUFFIX"] = suffix + env["PROGSUFFIX"]
  318. env["OBJSUFFIX"] = suffix + env["OBJSUFFIX"]
  319. env["LIBSUFFIX"] = suffix + env["LIBSUFFIX"]
  320. env["SHLIBSUFFIX"] = suffix + env["SHLIBSUFFIX"]
  321. if (env.use_ptrcall):
  322. env.Append(CPPFLAGS=['-DPTRCALL_ENABLED'])
  323. # to test 64 bits compiltion
  324. # env.Append(CPPFLAGS=['-m64'])
  325. if env['tools']:
  326. env.Append(CPPFLAGS=['-DTOOLS_ENABLED'])
  327. if env['disable_3d']:
  328. env.Append(CPPFLAGS=['-D_3D_DISABLED'])
  329. if env['gdscript']:
  330. env.Append(CPPFLAGS=['-DGDSCRIPT_ENABLED'])
  331. if env['disable_advanced_gui']:
  332. env.Append(CPPFLAGS=['-DADVANCED_GUI_DISABLED'])
  333. if env['minizip']:
  334. env.Append(CPPFLAGS=['-DMINIZIP_ENABLED'])
  335. if env['xml']:
  336. env.Append(CPPFLAGS=['-DXML_ENABLED'])
  337. if not env['verbose']:
  338. methods.no_verbose(sys, env)
  339. if (True): # FIXME: detect GLES3
  340. env.Append( BUILDERS = { 'GLES3_GLSL' : env.Builder(action = methods.build_gles3_headers, suffix = 'glsl.gen.h',src_suffix = '.glsl') } )
  341. Export('env')
  342. # build subdirs, the build order is dependent on link order.
  343. SConscript("core/SCsub")
  344. SConscript("servers/SCsub")
  345. SConscript("scene/SCsub")
  346. SConscript("editor/SCsub")
  347. SConscript("drivers/SCsub")
  348. SConscript("modules/SCsub")
  349. SConscript("main/SCsub")
  350. SConscript("platform/" + selected_platform + "/SCsub") # build selected platform
  351. # Microsoft Visual Studio Project Generation
  352. if env['vsproj']:
  353. env['CPPPATH'] = [Dir(path) for path in env['CPPPATH']]
  354. methods.generate_vs_project(env, GetOption("num_jobs"))
  355. # Check for the existence of headers
  356. conf = Configure(env)
  357. if ("check_c_headers" in env):
  358. for header in env["check_c_headers"]:
  359. if (conf.CheckCHeader(header[0])):
  360. if (env.msvc):
  361. env.Append(CCFLAGS=['/D' + header[1]])
  362. else:
  363. env.Append(CCFLAGS=['-D' + header[1]])
  364. else:
  365. print("No valid target platform selected.")
  366. print("The following were detected:")
  367. for x in platform_list:
  368. print("\t" + x)
  369. print("\nPlease run scons again with argument: platform=<string>")
  370. screen = sys.stdout
  371. node_count = 0
  372. node_count_max = 0
  373. node_count_interval = 1
  374. if ('env' in locals()):
  375. node_count_fname = str(env.Dir('#')) + '/.scons_node_count'
  376. def progress_function(node):
  377. global node_count, node_count_max, node_count_interval, node_count_fname
  378. node_count += node_count_interval
  379. if (node_count_max > 0 and node_count <= node_count_max):
  380. screen.write('\r[%3d%%] ' % (node_count * 100 / node_count_max))
  381. screen.flush()
  382. elif (node_count_max > 0 and node_count > node_count_max):
  383. screen.write('\r[100%] ')
  384. screen.flush()
  385. else:
  386. screen.write('\r[Initial build] ')
  387. screen.flush()
  388. def progress_finish(target, source, env):
  389. global node_count
  390. with open(node_count_fname, 'w') as f:
  391. f.write('%d\n' % node_count)
  392. if 'env' in locals() and env['progress']:
  393. try:
  394. with open(node_count_fname) as f:
  395. node_count_max = int(f.readline())
  396. except:
  397. pass
  398. Progress(progress_function, interval = node_count_interval)
  399. progress_finish_command = Command('progress_finish', [], progress_finish)
  400. AlwaysBuild(progress_finish_command)