SCsub 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  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. # Configure TLS checks
  79. import tls_configure
  80. conf = Configure(env_mono)
  81. tls_configure.configure(conf)
  82. env_mono = conf.Finish()
  83. # Build GodotSharpTools solution
  84. import os
  85. def find_nuget_unix():
  86. import os
  87. if 'NUGET_PATH' in os.environ:
  88. hint_path = os.environ['NUGET_PATH']
  89. if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
  90. return hint_path
  91. hint_path = os.path.join(hint_path, 'nuget')
  92. if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
  93. return hint_path
  94. import os.path
  95. import sys
  96. hint_dirs = ['/opt/novell/mono/bin']
  97. if sys.platform == 'darwin':
  98. hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs
  99. for hint_dir in hint_dirs:
  100. hint_path = os.path.join(hint_dir, 'nuget')
  101. if os.path.isfile(hint_path):
  102. return hint_path
  103. elif os.path.isfile(hint_path + '.exe'):
  104. return hint_path + '.exe'
  105. for hint_dir in os.environ['PATH'].split(os.pathsep):
  106. hint_dir = hint_dir.strip('"')
  107. hint_path = os.path.join(hint_dir, 'nuget')
  108. if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
  109. return hint_path
  110. if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK):
  111. return hint_path + '.exe'
  112. return None
  113. def find_nuget_windows():
  114. import os
  115. if 'NUGET_PATH' in os.environ:
  116. hint_path = os.environ['NUGET_PATH']
  117. if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
  118. return hint_path
  119. hint_path = os.path.join(hint_path, 'nuget.exe')
  120. if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
  121. return hint_path
  122. import mono_reg_utils as monoreg
  123. mono_root = ''
  124. bits = env['bits']
  125. if bits == '32':
  126. if os.getenv('MONO32_PREFIX'):
  127. mono_root = os.getenv('MONO32_PREFIX')
  128. else:
  129. mono_root = monoreg.find_mono_root_dir(bits)
  130. else:
  131. if os.getenv('MONO64_PREFIX'):
  132. mono_root = os.getenv('MONO64_PREFIX')
  133. else:
  134. mono_root = monoreg.find_mono_root_dir(bits)
  135. if mono_root:
  136. mono_bin_dir = os.path.join(mono_root, 'bin')
  137. nuget_mono = os.path.join(mono_bin_dir, 'nuget.bat')
  138. if os.path.isfile(nuget_mono):
  139. return nuget_mono
  140. # Standalone NuGet
  141. for hint_dir in os.environ['PATH'].split(os.pathsep):
  142. hint_dir = hint_dir.strip('"')
  143. hint_path = os.path.join(hint_dir, 'nuget.exe')
  144. if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
  145. return hint_path
  146. return None
  147. def find_msbuild_unix(filename):
  148. import os.path
  149. import sys
  150. hint_dirs = ['/opt/novell/mono/bin']
  151. if sys.platform == 'darwin':
  152. hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs
  153. for hint_dir in hint_dirs:
  154. hint_path = os.path.join(hint_dir, filename)
  155. if os.path.isfile(hint_path):
  156. return hint_path
  157. elif os.path.isfile(hint_path + '.exe'):
  158. return hint_path + '.exe'
  159. for hint_dir in os.environ['PATH'].split(os.pathsep):
  160. hint_dir = hint_dir.strip('"')
  161. hint_path = os.path.join(hint_dir, filename)
  162. if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
  163. return hint_path
  164. if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK):
  165. return hint_path + '.exe'
  166. return None
  167. def find_msbuild_windows():
  168. import mono_reg_utils as monoreg
  169. mono_root = ''
  170. bits = env['bits']
  171. if bits == '32':
  172. if os.getenv('MONO32_PREFIX'):
  173. mono_root = os.getenv('MONO32_PREFIX')
  174. else:
  175. mono_root = monoreg.find_mono_root_dir(bits)
  176. else:
  177. if os.getenv('MONO64_PREFIX'):
  178. mono_root = os.getenv('MONO64_PREFIX')
  179. else:
  180. mono_root = monoreg.find_mono_root_dir(bits)
  181. if not mono_root:
  182. raise RuntimeError('Cannot find mono root directory')
  183. framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5')
  184. mono_bin_dir = os.path.join(mono_root, 'bin')
  185. msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat')
  186. if os.path.isfile(msbuild_mono):
  187. # The (Csc/Vbc/Fsc)ToolExe environment variables are required when
  188. # building with Mono's MSBuild. They must point to the batch files
  189. # in Mono's bin directory to make sure they are executed with Mono.
  190. mono_msbuild_env = {
  191. 'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'),
  192. 'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'),
  193. 'FscToolExe': os.path.join(mono_bin_dir, 'fsharpc.bat')
  194. }
  195. return (msbuild_mono, framework_path, mono_msbuild_env)
  196. msbuild_tools_path = monoreg.find_msbuild_tools_path_reg()
  197. if msbuild_tools_path:
  198. return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), framework_path, {})
  199. return None
  200. def mono_build_solution(source, target, env):
  201. import subprocess
  202. import mono_reg_utils as monoreg
  203. from shutil import copyfile
  204. sln_path = os.path.abspath(str(source[0]))
  205. target_path = os.path.abspath(str(target[0]))
  206. framework_path = ''
  207. msbuild_env = os.environ.copy()
  208. # Needed when running from Developer Command Prompt for VS
  209. if 'PLATFORM' in msbuild_env:
  210. del msbuild_env['PLATFORM']
  211. # Find MSBuild
  212. if os.name == 'nt':
  213. msbuild_info = find_msbuild_windows()
  214. if msbuild_info is None:
  215. raise RuntimeError('Cannot find MSBuild executable')
  216. msbuild_path = msbuild_info[0]
  217. framework_path = msbuild_info[1]
  218. msbuild_env.update(msbuild_info[2])
  219. else:
  220. msbuild_path = find_msbuild_unix('msbuild')
  221. if msbuild_path is None:
  222. xbuild_fallback = env['xbuild_fallback']
  223. if xbuild_fallback and os.name == 'nt':
  224. print('Option \'xbuild_fallback\' not supported on Windows')
  225. xbuild_fallback = False
  226. if xbuild_fallback:
  227. print('Cannot find MSBuild executable, trying with xbuild')
  228. print('Warning: xbuild is deprecated')
  229. msbuild_path = find_msbuild_unix('xbuild')
  230. if msbuild_path is None:
  231. raise RuntimeError('Cannot find xbuild executable')
  232. else:
  233. raise RuntimeError('Cannot find MSBuild executable')
  234. print('MSBuild path: ' + msbuild_path)
  235. # Find NuGet
  236. nuget_path = find_nuget_windows() if os.name == 'nt' else find_nuget_unix()
  237. if nuget_path is None:
  238. raise RuntimeError('Cannot find NuGet executable')
  239. print('NuGet path: ' + nuget_path)
  240. # Do NuGet restore
  241. try:
  242. subprocess.check_call([nuget_path, 'restore', sln_path])
  243. except subprocess.CalledProcessError:
  244. raise RuntimeError('GodotSharpTools: NuGet restore failed')
  245. # Build solution
  246. build_config = 'Release'
  247. msbuild_args = [
  248. msbuild_path,
  249. sln_path,
  250. '/p:Configuration=' + build_config,
  251. ]
  252. if framework_path:
  253. msbuild_args += ['/p:FrameworkPathOverride=' + framework_path]
  254. try:
  255. subprocess.check_call(msbuild_args, env=msbuild_env)
  256. except subprocess.CalledProcessError:
  257. raise RuntimeError('GodotSharpTools: Build failed')
  258. # Copy files
  259. src_dir = os.path.abspath(os.path.join(sln_path, os.pardir, 'bin', build_config))
  260. dst_dir = os.path.abspath(os.path.join(target_path, os.pardir))
  261. asm_file = 'GodotSharpTools.dll'
  262. if not os.path.isdir(dst_dir):
  263. if os.path.exists(dst_dir):
  264. raise RuntimeError('Target directory is a file')
  265. os.makedirs(dst_dir)
  266. copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file))
  267. # Dependencies
  268. copyfile(os.path.join(src_dir, "DotNet.Glob.dll"), os.path.join(dst_dir, "DotNet.Glob.dll"))
  269. if env['tools']:
  270. output_dir = Dir('#bin').abspath
  271. editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools')
  272. mono_sln_builder = Builder(action=mono_build_solution)
  273. env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder})
  274. env_mono.MonoBuildSolution(
  275. os.path.join(editor_tools_dir, 'GodotSharpTools.dll'),
  276. 'editor/GodotSharpTools/GodotSharpTools.sln'
  277. )