Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 87 additions & 75 deletions editor/editor_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1181,94 +1181,101 @@ void EditorNode::_fs_changed() {
String export_error;
Error err = OK;
// It's important to wait for the first scan to finish; otherwise, scripts or resources might not be imported.
if (!export_defer.preset.is_empty() && !EditorFileSystem::get_singleton()->is_scanning()) {
String preset_name = export_defer.preset;
// Ensures export_project does not loop infinitely, because notifications may
// come during the export.
export_defer.preset = "";
Ref<EditorExportPreset> export_preset;
for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); ++i) {
export_preset = EditorExport::get_singleton()->get_export_preset(i);
if (export_preset->get_name() == preset_name) {
break;
}
export_preset.unref();
}

if (export_preset.is_null()) {
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (da->file_exists("res://export_presets.cfg")) {
err = FAILED;
export_error = vformat(
"Invalid export preset name: %s.\nThe following presets were detected in this project's `export_presets.cfg`:\n\n",
preset_name);
for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); ++i) {
// Write the preset name between double quotes since it needs to be written between quotes on the command line if it contains spaces.
export_error += vformat(" \"%s\"\n", EditorExport::get_singleton()->get_export_preset(i)->get_name());
if (!EditorFileSystem::get_singleton()->is_scanning()) {
if (!export_defer.preset.is_empty()) {
String preset_name = export_defer.preset;
// Ensures export_project does not loop infinitely, because notifications may
// come during the export.
export_defer.preset = "";
Ref<EditorExportPreset> export_preset;
for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); ++i) {
export_preset = EditorExport::get_singleton()->get_export_preset(i);
if (export_preset->get_name() == preset_name) {
break;
}
export_preset.unref();
}

if (export_preset.is_null()) {
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (da->file_exists("res://export_presets.cfg")) {
err = FAILED;
export_error = vformat(
"Invalid export preset name: %s.\nThe following presets were detected in this project's `export_presets.cfg`:\n\n",
preset_name);
for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); ++i) {
// Write the preset name between double quotes since it needs to be written between quotes on the command line if it contains spaces.
export_error += vformat(" \"%s\"\n", EditorExport::get_singleton()->get_export_preset(i)->get_name());
}
} else {
err = FAILED;
export_error = "This project doesn't have an `export_presets.cfg` file at its root.\nCreate an export preset from the \"Project > Export\" dialog and try again.";
}
} else {
err = FAILED;
export_error = "This project doesn't have an `export_presets.cfg` file at its root.\nCreate an export preset from the \"Project > Export\" dialog and try again.";
}
} else {
Ref<EditorExportPlatform> platform = export_preset->get_platform();
const String export_path = export_defer.path.is_empty() ? export_preset->get_export_path() : export_defer.path;
if (export_path.is_empty()) {
err = FAILED;
export_error = vformat("Export preset \"%s\" doesn't have a default export path, and none was specified.", preset_name);
} else if (platform.is_null()) {
err = FAILED;
export_error = vformat("Export preset \"%s\" doesn't have a matching platform.", preset_name);
} else {
export_preset->update_value_overrides();
if (export_defer.pack_only) { // Only export .pck or .zip data pack.
if (export_path.ends_with(".zip")) {
if (export_defer.patch) {
err = platform->export_zip_patch(export_preset, export_defer.debug, export_path, export_defer.patches);
Ref<EditorExportPlatform> platform = export_preset->get_platform();
const String export_path = export_defer.path.is_empty() ? export_preset->get_export_path() : export_defer.path;
if (export_path.is_empty()) {
err = FAILED;
export_error = vformat("Export preset \"%s\" doesn't have a default export path, and none was specified.", preset_name);
} else if (platform.is_null()) {
err = FAILED;
export_error = vformat("Export preset \"%s\" doesn't have a matching platform.", preset_name);
} else {
export_preset->update_value_overrides();
if (export_defer.pack_only) { // Only export .pck or .zip data pack.
if (export_path.ends_with(".zip")) {
if (export_defer.patch) {
err = platform->export_zip_patch(export_preset, export_defer.debug, export_path, export_defer.patches);
} else {
err = platform->export_zip(export_preset, export_defer.debug, export_path);
}
} else if (export_path.ends_with(".pck")) {
if (export_defer.patch) {
err = platform->export_pack_patch(export_preset, export_defer.debug, export_path, export_defer.patches);
} else {
err = platform->export_pack(export_preset, export_defer.debug, export_path);
}
} else {
err = platform->export_zip(export_preset, export_defer.debug, export_path);
ERR_PRINT(vformat("Export path \"%s\" doesn't end with a supported extension.", export_path));
err = FAILED;
}
} else { // Normal project export.
String config_error;
bool missing_templates;
if (export_defer.android_build_template) {
export_template_manager->install_android_template(export_preset);
}
} else if (export_path.ends_with(".pck")) {
if (export_defer.patch) {
err = platform->export_pack_patch(export_preset, export_defer.debug, export_path, export_defer.patches);
if (!platform->can_export(export_preset, config_error, missing_templates, export_defer.debug)) {
ERR_PRINT(vformat("Cannot export project with preset \"%s\" due to configuration errors:\n%s", preset_name, config_error));
err = missing_templates ? ERR_FILE_NOT_FOUND : ERR_UNCONFIGURED;
} else {
err = platform->export_pack(export_preset, export_defer.debug, export_path);
platform->clear_messages();
err = platform->export_project(export_preset, export_defer.debug, export_path);
}
} else {
ERR_PRINT(vformat("Export path \"%s\" doesn't end with a supported extension.", export_path));
err = FAILED;
}
} else { // Normal project export.
String config_error;
bool missing_templates;
if (export_defer.android_build_template) {
export_template_manager->install_android_template(export_preset);
}
if (!platform->can_export(export_preset, config_error, missing_templates, export_defer.debug)) {
ERR_PRINT(vformat("Cannot export project with preset \"%s\" due to configuration errors:\n%s", preset_name, config_error));
err = missing_templates ? ERR_FILE_NOT_FOUND : ERR_UNCONFIGURED;
} else {
platform->clear_messages();
err = platform->export_project(export_preset, export_defer.debug, export_path);
if (err != OK) {
export_error = vformat("Project export for preset \"%s\" failed.", preset_name);
} else if (platform->get_worst_message_type() >= EditorExportPlatform::EXPORT_MESSAGE_WARNING) {
export_error = vformat("Project export for preset \"%s\" completed with warnings.", preset_name);
}
}
if (err != OK) {
export_error = vformat("Project export for preset \"%s\" failed.", preset_name);
} else if (platform->get_worst_message_type() >= EditorExportPlatform::EXPORT_MESSAGE_WARNING) {
export_error = vformat("Project export for preset \"%s\" completed with warnings.", preset_name);
}
}
}

if (err != OK) {
ERR_PRINT(export_error);
_exit_editor(EXIT_FAILURE);
return;
if (err != OK) {
ERR_PRINT(export_error);
_exit_editor(EXIT_FAILURE);
return;
}
if (!export_error.is_empty()) {
WARN_PRINT(export_error);
}
_exit_editor(EXIT_SUCCESS);
}
if (!export_error.is_empty()) {
WARN_PRINT(export_error);

if (!build_profile_path.is_empty()) {
err = build_profile_manager->generate_build_profile_from_project(build_profile_path);
_exit_editor(err == OK ? EXIT_SUCCESS : EXIT_FAILURE);
}
_exit_editor(EXIT_SUCCESS);
}
}

Expand Down Expand Up @@ -5632,6 +5639,11 @@ bool EditorNode::is_project_exporting() const {
return project_export && project_export->is_exporting();
}

void EditorNode::generate_build_profile(const String &p_path) {
build_profile_path = p_path;
cmdline_mode = true;
}

void EditorNode::show_accept(const String &p_text, const String &p_title) {
current_menu_option = -1;
if (accept) {
Expand Down
4 changes: 4 additions & 0 deletions editor/editor_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,8 @@ class EditorNode : public Node {

bool unfocused_low_processor_usage_mode_enabled = true;

String build_profile_path;

static EditorBuildCallback build_callbacks[MAX_BUILD_CALLBACKS];
static EditorPluginInitializeCallback plugin_init_callbacks[MAX_INIT_CALLBACKS];
static int build_callback_count;
Expand Down Expand Up @@ -942,6 +944,8 @@ class EditorNode : public Node {
Error export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only, bool p_android_build_template, bool p_patch, const Vector<String> &p_patches);
bool is_project_exporting() const;

void generate_build_profile(const String &p_path);

Control *get_gui_base() { return gui_base; }

void save_scene_to_path(String p_file, bool p_with_preview = true) {
Expand Down
6 changes: 6 additions & 0 deletions editor/settings/editor_build_profile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1271,6 +1271,12 @@ void EditorBuildProfileManager::_export_profile(const String &p_path) {
}
}

Error EditorBuildProfileManager::generate_build_profile_from_project(const String &p_path) {
edited.instantiate();
_detect_from_project();
return edited->save_to_file(p_path);
}

Ref<EditorBuildProfile> EditorBuildProfileManager::get_current_profile() {
return edited;
}
Expand Down
1 change: 1 addition & 0 deletions editor/settings/editor_build_profile.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ class EditorBuildProfileManager : public AcceptDialog {
void _notification(int p_what);

public:
Error generate_build_profile_from_project(const String &p_path);
Ref<EditorBuildProfile> get_current_profile();

static EditorBuildProfileManager *get_singleton() { return singleton; }
Expand Down
31 changes: 24 additions & 7 deletions main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,7 @@ void Main::print_help(const char *p_binary) {
print_help_option("--gdscript-docs <path>", "Rather than dumping the engine API, generate API reference from the inline documentation in the GDScript files found in <path> (used with --doctool).\n", CLI_OPTION_AVAILABILITY_EDITOR);
#endif
print_help_option("--build-solutions", "Build the scripting solutions (e.g. for C# projects). Implies --editor and requires a valid project to edit.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--generate-build-profile <path>", "Generate an engine compilation profile by detecting the configuration from the project and save it to a given file. The path should be absolute.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--dump-gdextension-interface", "Generate a GDExtension header file \"gdextension_interface.h\" in the current folder. This file is the base file required to implement a GDExtension.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--dump-extension-api", "Generate a JSON dump of the Godot API for GDExtension bindings named \"extension_api.json\" in the current folder.\n", CLI_OPTION_AVAILABILITY_EDITOR);
print_help_option("--dump-extension-api-with-docs", "Generate JSON dump of the Godot API like the previous option, but including documentation.\n", CLI_OPTION_AVAILABILITY_EDITOR);
Expand Down Expand Up @@ -1559,14 +1560,14 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
quit_after = 1;
} else if (arg == "--export-release" || arg == "--export-debug" ||
arg == "--export-pack" || arg == "--export-patch") { // Export project
// Actually handling is done in start().
// Actual handling is done in start().
editor = true;
cmdline_tool = true;
wait_for_import = true;
main_args.push_back(arg);
} else if (arg == "--patches") {
if (N) {
// Actually handling is done in start().
// Actual handling is done in start().
main_args.push_back(arg);
main_args.push_back(N->get());

Expand All @@ -1575,12 +1576,18 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->print("Missing comma-separated list of patches after --patches, aborting.\n");
goto error;
}
} else if (arg == "--generate-build-profile") {
// Actual handling is done in start().
editor = true;
cmdline_tool = true;
wait_for_import = true;
main_args.push_back(arg);
#ifndef DISABLE_DEPRECATED
} else if (arg == "--export") { // For users used to 3.x syntax.
OS::get_singleton()->print("The Godot 3 --export option was changed to more explicit --export-release / --export-debug / --export-pack options.\nSee the --help output for details.\n");
goto error;
} else if (arg == "--convert-3to4") {
// Actually handling is done in start().
// Actual handling is done in start().
cmdline_tool = true;
main_args.push_back(arg);

Expand All @@ -1595,7 +1602,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
}
}
} else if (arg == "--validate-conversion-3to4") {
// Actually handling is done in start().
// Actual handling is done in start().
cmdline_tool = true;
main_args.push_back(arg);

Expand All @@ -1611,7 +1618,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
}
#endif // DISABLE_DEPRECATED
} else if (arg == "--doctool") {
// Actually handling is done in start().
// Actual handling is done in start().
cmdline_tool = true;

// `--doctool` implies `--headless` to avoid spawning an unnecessary window
Expand Down Expand Up @@ -3839,6 +3846,7 @@ int Main::start() {
bool export_pack_only = false;
bool install_android_build_template = false;
bool export_patch = false;
String build_profile_path;
#ifdef MODULE_GDSCRIPT_ENABLED
String gdscript_docs_path;
#endif
Expand Down Expand Up @@ -3926,11 +3934,11 @@ int Main::start() {
#endif
} else if (E->get() == "--export-release") {
ERR_FAIL_COND_V_MSG(!editor && !found_project, EXIT_FAILURE, "Please provide a valid project path when exporting, aborting.");
editor = true; //needs editor
editor = true;
_export_preset = E->next()->get();
} else if (E->get() == "--export-debug") {
ERR_FAIL_COND_V_MSG(!editor && !found_project, EXIT_FAILURE, "Please provide a valid project path when exporting, aborting.");
editor = true; //needs editor
editor = true;
_export_preset = E->next()->get();
export_debug = true;
} else if (E->get() == "--export-pack") {
Expand All @@ -3946,6 +3954,10 @@ int Main::start() {
export_patch = true;
} else if (E->get() == "--patches") {
patches = E->next()->get().split(",", false);
} else if (E->get() == "--generate-build-profile") {
ERR_FAIL_COND_V_MSG(!editor && !found_project, EXIT_FAILURE, "Please provide a valid project path when generating a build configuration, aborting.");
editor = true;
build_profile_path = E->next()->get();
#endif
} else {
// The parameter does not match anything known, don't skip the next argument
Expand Down Expand Up @@ -4380,6 +4392,11 @@ int Main::start() {
game_path = ""; // Do not load anything.
}

if (!build_profile_path.is_empty()) {
editor_node->generate_build_profile(build_profile_path);
game_path = ""; // Do not load anything.
}

OS::get_singleton()->benchmark_end_measure("Startup", "Editor");
}
#endif
Expand Down
1 change: 1 addition & 0 deletions misc/dist/shell/_godot.zsh-completion
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ _arguments \
'--doctool[dump the engine API reference to the given path in XML format, merging if existing files are found]:path to base Godot build directory (optional):_dirs' \
'--no-docbase[disallow dumping the base types (used with --doctool)]' \
'--build-solutions[build the scripting solutions (e.g. for C# projects)]' \
'--generate-build-profile[Generate an engine compilation profile by detecting the configuration from the project and save it to a given file. The path should be absolute]:path of profile' \
'--dump-gdextension-interface[generate GDExtension header file 'gdextension_interface.h' in the current folder. This file is the base file required to implement a GDExtension.]' \
'--dump-extension-api[generate JSON dump of the Godot API for GDExtension bindings named "extension_api.json" in the current folder]' \
'--benchmark[benchmark the run time and print it to console]' \
Expand Down
1 change: 1 addition & 0 deletions misc/dist/shell/godot.bash-completion
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ _complete_godot_options() {
--doctool
--no-docbase
--build-solutions
--generate-build-profile
--dump-gdextension-interface
--dump-extension-api
--benchmark
Expand Down
1 change: 1 addition & 0 deletions misc/dist/shell/godot.fish
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ complete -c godot -l validate-conversion-3to4 -d "Shows what elements will be re
complete -c godot -l doctool -d "Dump the engine API reference to the given path in XML format, merging if existing files are found" -r
complete -c godot -l no-docbase -d "Disallow dumping the base types (used with --doctool)"
complete -c godot -l build-solutions -d "Build the scripting solutions (e.g. for C# projects)"
complete -c godot -l generate-build-profile -d "Generate an engine compilation profile by detecting the configuration from the project and save it to a given file. The path should be absolute" -x
complete -c godot -l dump-gdextension-interface -d "Generate GDExtension header file 'gdextension_interface.h' in the current folder. This file is the base file required to implement a GDExtension"
complete -c godot -l dump-extension-api -d "Generate JSON dump of the Godot API for GDExtension bindings named 'extension_api.json' in the current folder"
complete -c godot -l benchmark -d "Benchmark the run time and print it to console"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4669,7 +4669,10 @@ void RenderForwardClustered::_update_dirty_geometry_pipelines() {
while (geometry_surface_compilation_dirty_list.first() != nullptr) {
GeometryInstanceSurfaceDataCache *surface_cache = geometry_surface_compilation_dirty_list.first()->self();
_mesh_generate_all_pipelines_for_surface_cache(surface_cache, global_pipeline_data_compiled);
surface_cache->compilation_dirty_element.remove_from_list();

if (surface_cache->compilation_dirty_element.in_list()) {
surface_cache->compilation_dirty_element.remove_from_list();
}
}
}
}
Expand Down