godotsharp_builds.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. /*************************************************************************/
  2. /* godotsharp_builds.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #include "godotsharp_builds.h"
  31. #include "core/vector.h"
  32. #include "main/main.h"
  33. #include "../glue/cs_glue_version.gen.h"
  34. #include "../godotsharp_dirs.h"
  35. #include "../mono_gd/gd_mono_class.h"
  36. #include "../mono_gd/gd_mono_marshal.h"
  37. #include "../utils/path_utils.h"
  38. #include "bindings_generator.h"
  39. #include "godotsharp_editor.h"
  40. #define PROP_NAME_MSBUILD_MONO "MSBuild (Mono)"
  41. #define PROP_NAME_MSBUILD_VS "MSBuild (VS Build Tools)"
  42. #define PROP_NAME_XBUILD "xbuild (Deprecated)"
  43. void godot_icall_BuildInstance_ExitCallback(MonoString *p_solution, MonoString *p_config, int p_exit_code) {
  44. String solution = GDMonoMarshal::mono_string_to_godot(p_solution);
  45. String config = GDMonoMarshal::mono_string_to_godot(p_config);
  46. GodotSharpBuilds::get_singleton()->build_exit_callback(MonoBuildInfo(solution, config), p_exit_code);
  47. }
  48. static Vector<const char *> _get_msbuild_hint_dirs() {
  49. Vector<const char *> ret;
  50. #ifdef OSX_ENABLED
  51. ret.push_back("/Library/Frameworks/Mono.framework/Versions/Current/bin/");
  52. ret.push_back("/usr/local/var/homebrew/linked/mono/bin/");
  53. #endif
  54. ret.push_back("/opt/novell/mono/bin/");
  55. return ret;
  56. }
  57. #ifdef UNIX_ENABLED
  58. String _find_build_engine_on_unix(const String &p_name) {
  59. String ret = path_which(p_name);
  60. if (ret.length())
  61. return ret;
  62. String ret_fallback = path_which(p_name + ".exe");
  63. if (ret_fallback.length())
  64. return ret_fallback;
  65. static Vector<const char *> locations = _get_msbuild_hint_dirs();
  66. for (int i = 0; i < locations.size(); i++) {
  67. String hint_path = locations[i] + p_name;
  68. if (FileAccess::exists(hint_path)) {
  69. return hint_path;
  70. }
  71. }
  72. return String();
  73. }
  74. #endif
  75. MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
  76. GodotSharpBuilds::BuildTool build_tool = GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool")));
  77. #if defined(WINDOWS_ENABLED)
  78. switch (build_tool) {
  79. case GodotSharpBuilds::MSBUILD_VS: {
  80. static String msbuild_tools_path = MonoRegUtils::find_msbuild_tools_path();
  81. if (msbuild_tools_path.length()) {
  82. if (!msbuild_tools_path.ends_with("\\"))
  83. msbuild_tools_path += "\\";
  84. return GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe");
  85. }
  86. print_verbose("Cannot find executable for '" PROP_NAME_MSBUILD_VS "'. Trying with '" PROP_NAME_MSBUILD_MONO "'...");
  87. } // FALL THROUGH
  88. case GodotSharpBuilds::MSBUILD_MONO: {
  89. String msbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("msbuild.bat");
  90. if (!FileAccess::exists(msbuild_path)) {
  91. WARN_PRINTS("Cannot find executable for '" PROP_NAME_MSBUILD_MONO "'. Tried with path: " + msbuild_path);
  92. }
  93. return GDMonoMarshal::mono_string_from_godot(msbuild_path);
  94. } break;
  95. case GodotSharpBuilds::XBUILD: {
  96. String xbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("xbuild.bat");
  97. if (!FileAccess::exists(xbuild_path)) {
  98. WARN_PRINTS("Cannot find executable for '" PROP_NAME_XBUILD "'. Tried with path: " + xbuild_path);
  99. }
  100. return GDMonoMarshal::mono_string_from_godot(xbuild_path);
  101. } break;
  102. default:
  103. ERR_EXPLAIN("You don't deserve to live");
  104. CRASH_NOW();
  105. }
  106. #elif defined(UNIX_ENABLED)
  107. static String msbuild_path = _find_build_engine_on_unix("msbuild");
  108. static String xbuild_path = _find_build_engine_on_unix("xbuild");
  109. if (build_tool == GodotSharpBuilds::XBUILD) {
  110. if (xbuild_path.empty()) {
  111. WARN_PRINT("Cannot find binary for '" PROP_NAME_XBUILD "'");
  112. return NULL;
  113. }
  114. } else {
  115. if (msbuild_path.empty()) {
  116. WARN_PRINT("Cannot find binary for '" PROP_NAME_MSBUILD_MONO "'");
  117. return NULL;
  118. }
  119. }
  120. return GDMonoMarshal::mono_string_from_godot(build_tool != GodotSharpBuilds::XBUILD ? msbuild_path : xbuild_path);
  121. #else
  122. (void)build_tool; // UNUSED
  123. ERR_EXPLAIN("Not implemented on this platform");
  124. ERR_FAIL_V(NULL);
  125. #endif
  126. }
  127. MonoString *godot_icall_BuildInstance_get_FrameworkPath() {
  128. #if defined(WINDOWS_ENABLED)
  129. const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info();
  130. if (mono_reg_info.assembly_dir.length()) {
  131. String framework_path = path_join(mono_reg_info.assembly_dir, "mono", "4.5");
  132. return GDMonoMarshal::mono_string_from_godot(framework_path);
  133. }
  134. ERR_EXPLAIN("Cannot find Mono's assemblies directory in the registry");
  135. ERR_FAIL_V(NULL);
  136. #else
  137. return NULL;
  138. #endif
  139. }
  140. MonoString *godot_icall_BuildInstance_get_MonoWindowsBinDir() {
  141. #if defined(WINDOWS_ENABLED)
  142. const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info();
  143. if (mono_reg_info.bin_dir.length()) {
  144. return GDMonoMarshal::mono_string_from_godot(mono_reg_info.bin_dir);
  145. }
  146. ERR_EXPLAIN("Cannot find Mono's binaries directory in the registry");
  147. ERR_FAIL_V(NULL);
  148. #else
  149. return NULL;
  150. #endif
  151. }
  152. MonoBoolean godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows() {
  153. #if defined(WINDOWS_ENABLED)
  154. return GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool"))) == GodotSharpBuilds::MSBUILD_MONO;
  155. #else
  156. return false;
  157. #endif
  158. }
  159. void GodotSharpBuilds::_register_internal_calls() {
  160. mono_add_internal_call("GodotSharpTools.Build.BuildSystem::godot_icall_BuildInstance_ExitCallback", (void *)godot_icall_BuildInstance_ExitCallback);
  161. mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildPath", (void *)godot_icall_BuildInstance_get_MSBuildPath);
  162. mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_FrameworkPath", (void *)godot_icall_BuildInstance_get_FrameworkPath);
  163. mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MonoWindowsBinDir", (void *)godot_icall_BuildInstance_get_MonoWindowsBinDir);
  164. mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows", (void *)godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows);
  165. }
  166. void GodotSharpBuilds::show_build_error_dialog(const String &p_message) {
  167. GodotSharpEditor::get_singleton()->show_error_dialog(p_message, "Build error");
  168. MonoBottomPanel::get_singleton()->show_build_tab();
  169. }
  170. bool GodotSharpBuilds::build_api_sln(const String &p_name, const String &p_api_sln_dir, const String &p_config) {
  171. String api_sln_file = p_api_sln_dir.plus_file(p_name + ".sln");
  172. String api_assembly_dir = p_api_sln_dir.plus_file("bin").plus_file(p_config);
  173. String api_assembly_file = api_assembly_dir.plus_file(p_name + ".dll");
  174. if (!FileAccess::exists(api_assembly_file)) {
  175. MonoBuildInfo api_build_info(api_sln_file, p_config);
  176. // TODO Replace this global NoWarn with '#pragma warning' directives on generated files,
  177. // once we start to actively document manually maintained C# classes
  178. api_build_info.custom_props.push_back("NoWarn=1591"); // Ignore missing documentation warnings
  179. if (!GodotSharpBuilds::get_singleton()->build(api_build_info)) {
  180. show_build_error_dialog("Failed to build " + p_name + " solution.");
  181. return false;
  182. }
  183. }
  184. return true;
  185. }
  186. bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type) {
  187. String assembly_file = p_assembly_name + ".dll";
  188. String assembly_src = p_src_dir.plus_file(assembly_file);
  189. String assembly_dst = p_dst_dir.plus_file(assembly_file);
  190. if (!FileAccess::exists(assembly_dst) ||
  191. FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst) ||
  192. GDMono::get_singleton()->metadata_is_api_assembly_invalidated(p_api_type)) {
  193. DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
  194. String xml_file = p_assembly_name + ".xml";
  195. if (da->copy(p_src_dir.plus_file(xml_file), p_dst_dir.plus_file(xml_file)) != OK)
  196. WARN_PRINTS("Failed to copy " + xml_file);
  197. String pdb_file = p_assembly_name + ".pdb";
  198. if (da->copy(p_src_dir.plus_file(pdb_file), p_dst_dir.plus_file(pdb_file)) != OK)
  199. WARN_PRINTS("Failed to copy " + pdb_file);
  200. Error err = da->copy(assembly_src, assembly_dst);
  201. memdelete(da);
  202. if (err != OK) {
  203. show_build_error_dialog("Failed to copy " + assembly_file);
  204. return false;
  205. }
  206. GDMono::get_singleton()->metadata_set_api_assembly_invalidated(p_api_type, false);
  207. }
  208. return true;
  209. }
  210. String GodotSharpBuilds::_api_folder_name(APIAssembly::Type p_api_type) {
  211. uint64_t api_hash = p_api_type == APIAssembly::API_CORE ?
  212. GDMono::get_singleton()->get_api_core_hash() :
  213. GDMono::get_singleton()->get_api_editor_hash();
  214. return String::num_uint64(api_hash) +
  215. "_" + String::num_uint64(BindingsGenerator::get_version()) +
  216. "_" + String::num_uint64(CS_GLUE_VERSION);
  217. }
  218. bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) {
  219. String api_name = p_api_type == APIAssembly::API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
  220. String api_build_config = "Release";
  221. EditorProgress pr("mono_build_release_" + api_name, "Building " + api_name + " solution...", 4);
  222. pr.step("Generating " + api_name + " solution");
  223. String core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
  224. .plus_file(_api_folder_name(APIAssembly::API_CORE))
  225. .plus_file(API_ASSEMBLY_NAME);
  226. String editor_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
  227. .plus_file(_api_folder_name(APIAssembly::API_EDITOR))
  228. .plus_file(EDITOR_API_ASSEMBLY_NAME);
  229. String api_sln_dir = p_api_type == APIAssembly::API_CORE ? core_api_sln_dir : editor_api_sln_dir;
  230. String api_sln_file = api_sln_dir.plus_file(api_name + ".sln");
  231. if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) {
  232. String core_api_assembly;
  233. if (p_api_type == APIAssembly::API_EDITOR) {
  234. core_api_assembly = core_api_sln_dir.plus_file("bin")
  235. .plus_file(api_build_config)
  236. .plus_file(API_ASSEMBLY_NAME ".dll");
  237. }
  238. #ifndef DEBUG_METHODS_ENABLED
  239. #error "How am I supposed to generate the bindings?"
  240. #endif
  241. BindingsGenerator *gen = BindingsGenerator::get_singleton();
  242. bool gen_verbose = OS::get_singleton()->is_stdout_verbose();
  243. Error err = p_api_type == APIAssembly::API_CORE ?
  244. gen->generate_cs_core_project(api_sln_dir, gen_verbose) :
  245. gen->generate_cs_editor_project(api_sln_dir, core_api_assembly, gen_verbose);
  246. if (err != OK) {
  247. show_build_error_dialog("Failed to generate " + api_name + " solution. Error: " + itos(err));
  248. return false;
  249. }
  250. }
  251. pr.step("Building " + api_name + " solution");
  252. if (!GodotSharpBuilds::build_api_sln(api_name, api_sln_dir, api_build_config))
  253. return false;
  254. pr.step("Copying " + api_name + " assembly");
  255. String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
  256. // Create assemblies directory if needed
  257. if (!DirAccess::exists(res_assemblies_dir)) {
  258. DirAccess *da = DirAccess::create_for_path(res_assemblies_dir);
  259. Error err = da->make_dir_recursive(res_assemblies_dir);
  260. memdelete(da);
  261. if (err != OK) {
  262. show_build_error_dialog("Failed to create assemblies directory. Error: " + itos(err));
  263. return false;
  264. }
  265. }
  266. // Copy the built assembly to the assemblies directory
  267. String api_assembly_dir = api_sln_dir.plus_file("bin").plus_file(api_build_config);
  268. if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name, p_api_type))
  269. return false;
  270. pr.step("Done");
  271. return true;
  272. }
  273. bool GodotSharpBuilds::build_project_blocking(const String &p_config) {
  274. if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
  275. return true; // No solution to build
  276. if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE))
  277. return false;
  278. if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR))
  279. return false;
  280. EditorProgress pr("mono_project_debug_build", "Building project solution...", 2);
  281. pr.step("Building project solution");
  282. MonoBuildInfo build_info(GodotSharpDirs::get_project_sln_path(), p_config);
  283. if (!GodotSharpBuilds::get_singleton()->build(build_info)) {
  284. GodotSharpBuilds::show_build_error_dialog("Failed to build project solution");
  285. return false;
  286. }
  287. pr.step("Done");
  288. return true;
  289. }
  290. bool GodotSharpBuilds::editor_build_callback() {
  291. return build_project_blocking("Tools");
  292. }
  293. GodotSharpBuilds *GodotSharpBuilds::singleton = NULL;
  294. void GodotSharpBuilds::build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code) {
  295. BuildProcess *match = builds.getptr(p_build_info);
  296. ERR_FAIL_NULL(match);
  297. BuildProcess &bp = *match;
  298. bp.on_exit(p_exit_code);
  299. }
  300. void GodotSharpBuilds::restart_build(MonoBuildTab *p_build_tab) {
  301. }
  302. void GodotSharpBuilds::stop_build(MonoBuildTab *p_build_tab) {
  303. }
  304. bool GodotSharpBuilds::build(const MonoBuildInfo &p_build_info) {
  305. BuildProcess *match = builds.getptr(p_build_info);
  306. if (match) {
  307. BuildProcess &bp = *match;
  308. bp.start(true);
  309. return bp.exit_code == 0;
  310. } else {
  311. BuildProcess bp = BuildProcess(p_build_info);
  312. bp.start(true);
  313. builds.set(p_build_info, bp);
  314. return bp.exit_code == 0;
  315. }
  316. }
  317. bool GodotSharpBuilds::build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) {
  318. BuildProcess *match = builds.getptr(p_build_info);
  319. if (match) {
  320. BuildProcess &bp = *match;
  321. bp.start();
  322. return !bp.exited; // failed to start
  323. } else {
  324. BuildProcess bp = BuildProcess(p_build_info, p_callback);
  325. bp.start();
  326. builds.set(p_build_info, bp);
  327. return !bp.exited; // failed to start
  328. }
  329. }
  330. GodotSharpBuilds::GodotSharpBuilds() {
  331. singleton = this;
  332. EditorNode::get_singleton()->add_build_callback(&GodotSharpBuilds::editor_build_callback);
  333. // Build tool settings
  334. EditorSettings *ed_settings = EditorSettings::get_singleton();
  335. EDITOR_DEF("mono/builds/build_tool", MSBUILD_MONO);
  336. ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/builds/build_tool", PROPERTY_HINT_ENUM,
  337. PROP_NAME_MSBUILD_MONO
  338. #ifdef WINDOWS_ENABLED
  339. "," PROP_NAME_MSBUILD_VS
  340. #endif
  341. "," PROP_NAME_XBUILD));
  342. }
  343. GodotSharpBuilds::~GodotSharpBuilds() {
  344. singleton = NULL;
  345. }
  346. void GodotSharpBuilds::BuildProcess::on_exit(int p_exit_code) {
  347. exited = true;
  348. exit_code = p_exit_code;
  349. build_tab->on_build_exit(p_exit_code == 0 ? MonoBuildTab::RESULT_SUCCESS : MonoBuildTab::RESULT_ERROR);
  350. build_instance.unref();
  351. if (exit_callback)
  352. exit_callback(exit_code);
  353. }
  354. void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
  355. _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
  356. exit_code = -1;
  357. String log_dirpath = build_info.get_log_dirpath();
  358. if (build_tab) {
  359. build_tab->on_build_start();
  360. } else {
  361. build_tab = memnew(MonoBuildTab(build_info, log_dirpath));
  362. MonoBottomPanel::get_singleton()->add_build_tab(build_tab);
  363. }
  364. if (p_blocking) {
  365. // Required in order to update the build tasks list
  366. Main::iteration();
  367. }
  368. if (!exited) {
  369. exited = true;
  370. String message = "Tried to start build process, but it is already running";
  371. build_tab->on_build_exec_failed(message);
  372. ERR_EXPLAIN(message);
  373. ERR_FAIL();
  374. }
  375. exited = false;
  376. // Remove old issues file
  377. String issues_file = "msbuild_issues.csv";
  378. DirAccessRef d = DirAccess::create_for_path(log_dirpath);
  379. if (d->file_exists(issues_file)) {
  380. Error err = d->remove(issues_file);
  381. if (err != OK) {
  382. exited = true;
  383. String file_path = ProjectSettings::get_singleton()->localize_path(log_dirpath).plus_file(issues_file);
  384. String message = "Cannot remove issues file: " + file_path;
  385. build_tab->on_build_exec_failed(message);
  386. ERR_EXPLAIN(message);
  387. ERR_FAIL();
  388. }
  389. }
  390. GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Build", "BuildInstance");
  391. MonoObject *mono_object = mono_object_new(mono_domain_get(), klass->get_mono_ptr());
  392. // Construct
  393. Variant solution = build_info.solution;
  394. Variant config = build_info.configuration;
  395. const Variant *ctor_args[2] = { &solution, &config };
  396. MonoException *exc = NULL;
  397. GDMonoMethod *ctor = klass->get_method(".ctor", 2);
  398. ctor->invoke(mono_object, ctor_args, &exc);
  399. if (exc) {
  400. exited = true;
  401. GDMonoUtils::debug_unhandled_exception(exc);
  402. String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc);
  403. build_tab->on_build_exec_failed(message);
  404. ERR_EXPLAIN(message);
  405. ERR_FAIL();
  406. }
  407. // Call Build
  408. String logger_assembly_path = GDMono::get_singleton()->get_editor_tools_assembly()->get_path();
  409. Variant logger_assembly = ProjectSettings::get_singleton()->globalize_path(logger_assembly_path);
  410. Variant logger_output_dir = log_dirpath;
  411. Variant custom_props = build_info.custom_props;
  412. const Variant *args[3] = { &logger_assembly, &logger_output_dir, &custom_props };
  413. exc = NULL;
  414. GDMonoMethod *build_method = klass->get_method(p_blocking ? "Build" : "BuildAsync", 3);
  415. build_method->invoke(mono_object, args, &exc);
  416. if (exc) {
  417. exited = true;
  418. GDMonoUtils::debug_unhandled_exception(exc);
  419. String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc);
  420. build_tab->on_build_exec_failed(message);
  421. ERR_EXPLAIN(message);
  422. ERR_FAIL();
  423. }
  424. // Build returned
  425. if (p_blocking) {
  426. exited = true;
  427. exit_code = klass->get_field("exitCode")->get_int_value(mono_object);
  428. if (exit_code != 0) {
  429. print_verbose("MSBuild finished with exit code " + itos(exit_code));
  430. }
  431. build_tab->on_build_exit(exit_code == 0 ? MonoBuildTab::RESULT_SUCCESS : MonoBuildTab::RESULT_ERROR);
  432. } else {
  433. build_instance = MonoGCHandle::create_strong(mono_object);
  434. exited = false;
  435. }
  436. }
  437. GodotSharpBuilds::BuildProcess::BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) :
  438. build_info(p_build_info),
  439. build_tab(NULL),
  440. exit_callback(p_callback),
  441. exited(true),
  442. exit_code(-1) {
  443. }