SCsub 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. #!/usr/bin/env python
  2. Import('env')
  3. Import('env_modules')
  4. env_mono = env_modules.Clone()
  5. # TODO move functions to their own modules
  6. def make_cs_files_header(src, dst, version_dst):
  7. from compat import byte_to_str
  8. with open(dst, 'w') as header:
  9. header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n')
  10. header.write('#ifndef CS_COMPRESSED_H\n')
  11. header.write('#define CS_COMPRESSED_H\n\n')
  12. header.write('#ifdef TOOLS_ENABLED\n\n')
  13. header.write('#include "core/map.h"\n')
  14. header.write('#include "core/ustring.h"\n')
  15. inserted_files = ''
  16. import os
  17. latest_mtime = 0
  18. cs_file_count = 0
  19. for root, _, files in os.walk(src):
  20. files = [f for f in files if f.endswith('.cs')]
  21. for file in files:
  22. cs_file_count += 1
  23. filepath = os.path.join(root, file)
  24. filepath_src_rel = os.path.relpath(filepath, src)
  25. mtime = os.path.getmtime(filepath)
  26. latest_mtime = mtime if mtime > latest_mtime else latest_mtime
  27. with open(filepath, 'rb') as f:
  28. buf = f.read()
  29. decomp_size = len(buf)
  30. import zlib
  31. buf = zlib.compress(buf)
  32. name = str(cs_file_count)
  33. header.write('\n')
  34. header.write('// ' + filepath_src_rel + '\n')
  35. header.write('static const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n')
  36. header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decomp_size) + ';\n')
  37. header.write('static const unsigned char _cs_' + name + '_compressed[] = { ')
  38. for i, buf_idx in enumerate(range(len(buf))):
  39. if i > 0:
  40. header.write(', ')
  41. header.write(byte_to_str(buf[buf_idx]))
  42. inserted_files += '\tr_files.insert("' + filepath_src_rel.replace('\\', '\\\\') + '", ' \
  43. 'CompressedFile(_cs_' + name + '_compressed_size, ' \
  44. '_cs_' + name + '_uncompressed_size, ' \
  45. '_cs_' + name + '_compressed));\n'
  46. header.write(' };\n')
  47. header.write('\nstruct CompressedFile\n' '{\n'
  48. '\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n'
  49. '\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n'
  50. '\t{\n' '\t\tcompressed_size = p_comp_size;\n' '\t\tuncompressed_size = p_uncomp_size;\n'
  51. '\t\tdata = p_data;\n' '\t}\n' '\n\tCompressedFile() {}\n' '};\n'
  52. '\nvoid get_compressed_files(Map<String, CompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n'
  53. )
  54. header.write('\n#endif // TOOLS_ENABLED\n')
  55. header.write('\n#endif // CS_COMPRESSED_H\n')
  56. glue_version = int(latest_mtime) # The latest modified time will do for now
  57. with open(version_dst, 'w') as version_header:
  58. version_header.write('/* THIS FILE IS GENERATED DO NOT EDIT */\n')
  59. version_header.write('#ifndef CS_GLUE_VERSION_H\n')
  60. version_header.write('#define CS_GLUE_VERSION_H\n\n')
  61. version_header.write('#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n')
  62. version_header.write('\n#endif // CS_GLUE_VERSION_H\n')
  63. env_mono.add_source_files(env.modules_sources, '*.cpp')
  64. env_mono.add_source_files(env.modules_sources, 'glue/*.cpp')
  65. env_mono.add_source_files(env.modules_sources, 'mono_gd/*.cpp')
  66. env_mono.add_source_files(env.modules_sources, 'utils/*.cpp')
  67. if env['tools']:
  68. env_mono.add_source_files(env.modules_sources, 'editor/*.cpp')
  69. # NOTE: It is safe to generate this file here, since this is still executed serially
  70. make_cs_files_header('glue/Managed/Files', 'glue/cs_compressed.gen.h', 'glue/cs_glue_version.gen.h')
  71. vars = Variables()
  72. vars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True))
  73. vars.Add(BoolVariable('xbuild_fallback', 'If MSBuild is not found, fallback to xbuild', False))
  74. vars.Update(env_mono)
  75. # Glue sources
  76. if env_mono['mono_glue']:
  77. env_mono.Append(CPPDEFINES=['MONO_GLUE_ENABLED'])
  78. if env_mono['tools'] or env_mono['target'] != 'release':
  79. env_mono.Append(CPPDEFINES=['GD_MONO_HOT_RELOAD'])
  80. # Configure TLS checks
  81. import tls_configure
  82. conf = Configure(env_mono)
  83. tls_configure.configure(conf)
  84. env_mono = conf.Finish()
  85. # Build GodotSharpTools solution
  86. import os
  87. def find_nuget_unix():
  88. import os
  89. if 'NUGET_PATH' in os.environ:
  90. hint_path = os.environ['NUGET_PATH']
  91. if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
  92. return hint_path
  93. hint_path = os.path.join(hint_path, 'nuget')
  94. if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
  95. return hint_path
  96. import os.path
  97. import sys
  98. hint_dirs = ['/opt/novell/mono/bin']
  99. if sys.platform == 'darwin':
  100. hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs
  101. for hint_dir in hint_dirs:
  102. hint_path = os.path.join(hint_dir, 'nuget')
  103. if os.path.isfile(hint_path):
  104. return hint_path
  105. elif os.path.isfile(hint_path + '.exe'):
  106. return hint_path + '.exe'
  107. for hint_dir in os.environ['PATH'].split(os.pathsep):
  108. hint_dir = hint_dir.strip('"')
  109. hint_path = os.path.join(hint_dir, 'nuget')
  110. if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
  111. return hint_path
  112. if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK):
  113. return hint_path + '.exe'
  114. return None
  115. def find_nuget_windows():
  116. import os
  117. if 'NUGET_PATH' in os.environ:
  118. hint_path = os.environ['NUGET_PATH']
  119. if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
  120. return hint_path
  121. hint_path = os.path.join(hint_path, 'nuget.exe')
  122. if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
  123. return hint_path
  124. import mono_reg_utils as monoreg
  125. mono_root = ''
  126. bits = env['bits']
  127. if bits == '32':
  128. if os.getenv('MONO32_PREFIX'):
  129. mono_root = os.getenv('MONO32_PREFIX')
  130. else:
  131. mono_root = monoreg.find_mono_root_dir(bits)
  132. else:
  133. if os.getenv('MONO64_PREFIX'):
  134. mono_root = os.getenv('MONO64_PREFIX')
  135. else:
  136. mono_root = monoreg.find_mono_root_dir(bits)
  137. if mono_root:
  138. mono_bin_dir = os.path.join(mono_root, 'bin')
  139. nuget_mono = os.path.join(mono_bin_dir, 'nuget.bat')
  140. if os.path.isfile(nuget_mono):
  141. return nuget_mono
  142. # Standalone NuGet
  143. for hint_dir in os.environ['PATH'].split(os.pathsep):
  144. hint_dir = hint_dir.strip('"')
  145. hint_path = os.path.join(hint_dir, 'nuget.exe')
  146. if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
  147. return hint_path
  148. return None
  149. def find_msbuild_unix(filename):
  150. import os.path
  151. import sys
  152. hint_dirs = ['/opt/novell/mono/bin']
  153. if sys.platform == 'darwin':
  154. hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs
  155. for hint_dir in hint_dirs:
  156. hint_path = os.path.join(hint_dir, filename)
  157. if os.path.isfile(hint_path):
  158. return hint_path
  159. elif os.path.isfile(hint_path + '.exe'):
  160. return hint_path + '.exe'
  161. for hint_dir in os.environ['PATH'].split(os.pathsep):
  162. hint_dir = hint_dir.strip('"')
  163. hint_path = os.path.join(hint_dir, filename)
  164. if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
  165. return hint_path
  166. if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK):
  167. return hint_path + '.exe'
  168. return None
  169. def find_msbuild_windows():
  170. import mono_reg_utils as monoreg
  171. mono_root = ''
  172. bits = env['bits']
  173. if bits == '32':
  174. if os.getenv('MONO32_PREFIX'):
  175. mono_root = os.getenv('MONO32_PREFIX')
  176. else:
  177. mono_root = monoreg.find_mono_root_dir(bits)
  178. else:
  179. if os.getenv('MONO64_PREFIX'):
  180. mono_root = os.getenv('MONO64_PREFIX')
  181. else:
  182. mono_root = monoreg.find_mono_root_dir(bits)
  183. if not mono_root:
  184. raise RuntimeError('Cannot find mono root directory')
  185. framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5')
  186. mono_bin_dir = os.path.join(mono_root, 'bin')
  187. msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat')
  188. if os.path.isfile(msbuild_mono):
  189. # The (Csc/Vbc/Fsc)ToolExe environment variables are required when
  190. # building with Mono's MSBuild. They must point to the batch files
  191. # in Mono's bin directory to make sure they are executed with Mono.
  192. mono_msbuild_env = {
  193. 'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'),
  194. 'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'),
  195. 'FscToolExe': os.path.join(mono_bin_dir, 'fsharpc.bat')
  196. }
  197. return (msbuild_mono, framework_path, mono_msbuild_env)
  198. msbuild_tools_path = monoreg.find_msbuild_tools_path_reg()
  199. if msbuild_tools_path:
  200. return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), framework_path, {})
  201. return None
  202. def mono_build_solution(source, target, env):
  203. import subprocess
  204. import mono_reg_utils as monoreg
  205. from shutil import copyfile
  206. sln_path = os.path.abspath(str(source[0]))
  207. target_path = os.path.abspath(str(target[0]))
  208. framework_path = ''
  209. msbuild_env = os.environ.copy()
  210. # Needed when running from Developer Command Prompt for VS
  211. if 'PLATFORM' in msbuild_env:
  212. del msbuild_env['PLATFORM']
  213. # Find MSBuild
  214. if os.name == 'nt':
  215. msbuild_info = find_msbuild_windows()
  216. if msbuild_info is None:
  217. raise RuntimeError('Cannot find MSBuild executable')
  218. msbuild_path = msbuild_info[0]
  219. framework_path = msbuild_info[1]
  220. msbuild_env.update(msbuild_info[2])
  221. else:
  222. msbuild_path = find_msbuild_unix('msbuild')
  223. if msbuild_path is None:
  224. xbuild_fallback = env['xbuild_fallback']
  225. if xbuild_fallback and os.name == 'nt':
  226. print('Option \'xbuild_fallback\' not supported on Windows')
  227. xbuild_fallback = False
  228. if xbuild_fallback:
  229. print('Cannot find MSBuild executable, trying with xbuild')
  230. print('Warning: xbuild is deprecated')
  231. msbuild_path = find_msbuild_unix('xbuild')
  232. if msbuild_path is None:
  233. raise RuntimeError('Cannot find xbuild executable')
  234. else:
  235. raise RuntimeError('Cannot find MSBuild executable')
  236. print('MSBuild path: ' + msbuild_path)
  237. # Find NuGet
  238. nuget_path = find_nuget_windows() if os.name == 'nt' else find_nuget_unix()
  239. if nuget_path is None:
  240. raise RuntimeError('Cannot find NuGet executable')
  241. print('NuGet path: ' + nuget_path)
  242. # Do NuGet restore
  243. try:
  244. subprocess.check_call([nuget_path, 'restore', sln_path])
  245. except subprocess.CalledProcessError:
  246. raise RuntimeError('GodotSharpTools: NuGet restore failed')
  247. # Build solution
  248. build_config = 'Release'
  249. msbuild_args = [
  250. msbuild_path,
  251. sln_path,
  252. '/p:Configuration=' + build_config,
  253. ]
  254. if framework_path:
  255. msbuild_args += ['/p:FrameworkPathOverride=' + framework_path]
  256. try:
  257. subprocess.check_call(msbuild_args, env=msbuild_env)
  258. except subprocess.CalledProcessError:
  259. raise RuntimeError('GodotSharpTools: Build failed')
  260. # Copy files
  261. src_dir = os.path.abspath(os.path.join(sln_path, os.pardir, 'bin', build_config))
  262. dst_dir = os.path.abspath(os.path.join(target_path, os.pardir))
  263. asm_file = 'GodotSharpTools.dll'
  264. if not os.path.isdir(dst_dir):
  265. if os.path.exists(dst_dir):
  266. raise RuntimeError('Target directory is a file')
  267. os.makedirs(dst_dir)
  268. copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file))
  269. # Dependencies
  270. copyfile(os.path.join(src_dir, "DotNet.Glob.dll"), os.path.join(dst_dir, "DotNet.Glob.dll"))
  271. if env['tools']:
  272. output_dir = Dir('#bin').abspath
  273. editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools')
  274. mono_sln_builder = Builder(action=mono_build_solution)
  275. env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder})
  276. env_mono.MonoBuildSolution(
  277. os.path.join(editor_tools_dir, 'GodotSharpTools.dll'),
  278. 'editor/GodotSharpTools/GodotSharpTools.sln'
  279. )