diff --git a/CMakeLists.txt b/CMakeLists.txt
index f81d0794cad410f3bc62497eb9c9b90ce122f31e..0cc3c7c669a2a166867bd04ea0af4991efcc275e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -59,7 +59,7 @@ opt(HXT "Enable HXT library (for reparametrization and meshing)" ${DEFAULT})
 opt(KBIPACK "Enable Kbipack (neeeded by homology solver)" ${DEFAULT})
 opt(MATHEX "Enable Mathex expression parser (used by plugins and options)" ${DEFAULT})
 opt(MED "Enable MED mesh and post file formats" ${DEFAULT})
-opt(MESH "Enable mesh module (required by GUI)" ${DEFAULT})
+opt(MESH "Enable mesh module" ${DEFAULT})
 opt(METIS "Enable Metis mesh partitioner" ${DEFAULT})
 opt(MMG "Enable Mmg mesh adaptation interface" ${DEFAULT})
 opt(MPEG_ENCODE "Enable built-in MPEG movie encoder" ${DEFAULT})
@@ -681,8 +681,8 @@ if(ENABLE_BUILD_IOS)
 endif()
 
 if(HAVE_FLTK OR ENABLE_GRAPHICS)
-  if(NOT HAVE_MESH OR NOT HAVE_POST OR NOT HAVE_PLUGINS OR NOT HAVE_ONELAB)
-    message(SEND_ERROR "Cannot compile GUI without Mesh, Post, Plugin and ONELAB")
+  if(NOT HAVE_POST OR NOT HAVE_PLUGINS OR NOT HAVE_ONELAB)
+    message(SEND_ERROR "Cannot compile GUI without Post, Plugin and ONELAB")
   endif()
 
   find_package(JPEG)
@@ -787,34 +787,6 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/DiscreteIntegration AND
   set_config_option(HAVE_DINTEGRATION "DIntegration")
 endif()
 
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/HighOrderMeshOptimizer AND
-   EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/MeshOptimizer AND
-   EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/MeshQualityOptimizer AND
-   ENABLE_OPTHOM AND HAVE_MESH)
-  add_subdirectory(contrib/HighOrderMeshOptimizer)
-  include_directories(contrib/HighOrderMeshOptimizer)
-  add_subdirectory(contrib/MeshOptimizer)
-  include_directories(contrib/MeshOptimizer)
-  include_directories(${CMAKE_CURRENT_BINARY_DIR}/contrib/MeshOptimizer)
-  add_subdirectory(contrib/MeshQualityOptimizer)
-  include_directories(contrib/MeshQualityOptimizer)
-  set_config_option(HAVE_OPTHOM "OptHom")
-endif()
-
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/domhex AND
-   ENABLE_DOMHEX AND HAVE_MESH)
-  add_subdirectory(contrib/domhex)
-  include_directories(contrib/domhex)
-  set_config_option(HAVE_DOMHEX "DomHex")
-endif()
-
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/QuadTri AND
-   ENABLE_QUADTRI AND HAVE_MESH)
-  add_subdirectory(contrib/QuadTri)
-  include_directories(contrib/QuadTri)
-  set_config_option(HAVE_QUADTRI "QuadTri")
-endif()
-
 if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/kbipack AND ENABLE_KBIPACK)
   set_config_option(HAVE_KBIPACK "Kbipack")
   add_subdirectory(contrib/kbipack)
@@ -920,6 +892,32 @@ if(HAVE_MESH)
     endif()
   endif()
 
+  if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/HighOrderMeshOptimizer AND
+     EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/MeshOptimizer AND
+     EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/MeshQualityOptimizer AND
+     ENABLE_OPTHOM)
+    add_subdirectory(contrib/HighOrderMeshOptimizer)
+    include_directories(contrib/HighOrderMeshOptimizer)
+    add_subdirectory(contrib/MeshOptimizer)
+    include_directories(contrib/MeshOptimizer)
+    include_directories(${CMAKE_CURRENT_BINARY_DIR}/contrib/MeshOptimizer)
+    add_subdirectory(contrib/MeshQualityOptimizer)
+    include_directories(contrib/MeshQualityOptimizer)
+    set_config_option(HAVE_OPTHOM "OptHom")
+  endif()
+
+  if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/domhex AND ENABLE_DOMHEX)
+    add_subdirectory(contrib/domhex)
+    include_directories(contrib/domhex)
+    set_config_option(HAVE_DOMHEX "DomHex")
+  endif()
+
+  if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/QuadTri AND ENABLE_QUADTRI)
+    add_subdirectory(contrib/QuadTri)
+    include_directories(contrib/QuadTri)
+    set_config_option(HAVE_QUADTRI "QuadTri")
+  endif()
+
   if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/blossom AND ENABLE_BLOSSOM)
     add_subdirectory(contrib/blossom)
     include_directories(contrib/blossom/MATCH contrib/blossom/concorde97
@@ -950,6 +948,48 @@ if(HAVE_MESH)
       set_config_option(HAVE_MMG "Mmg")
     endif()
   endif()
+
+  if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/hxt AND ENABLE_HXT)
+    add_subdirectory(contrib/hxt)
+    include_directories(BEFORE ${HXT_INC_DIRS})
+    set_config_option(HAVE_HXT "Hxt")
+    # do not use arithmetic contraction in predicates.c
+    if(MSVC OR (CMAKE_C_COMPILER_ID STREQUAL "Intel" AND WIN32))
+      set_source_files_properties(
+          "${CMAKE_CURRENT_SOURCE_DIR}/contrib/hxt/predicates/src/predicates.c"
+          PROPERTIES COMPILE_FLAGS "/fp:strict")
+    elseif(CMAKE_C_COMPILER_ID STREQUAL "Intel")
+      set_source_files_properties(
+          "${CMAKE_CURRENT_SOURCE_DIR}/contrib/hxt/predicates/src/predicates.c"
+          PROPERTIES COMPILE_FLAGS "-fp-model strict")
+    elseif(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
+      set_source_files_properties(
+          "${CMAKE_CURRENT_SOURCE_DIR}/contrib/hxt/predicates/src/predicates.c"
+          PROPERTIES COMPILE_FLAGS  "-fno-unsafe-math-optimizations -ffp-contract=off")
+    else()
+      message(WARNING "Unknown compiler: make sure compiled functions from "
+              "predicates.c do not use extended double precision and follow "
+              "the IEEE754 standard. It is crucial for the robustness of "
+              "geometric predicates.")
+    endif()
+    if(MSVC)
+      add_definitions(/bigobj)
+    endif()
+  endif()
+
+  if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/QuadMeshingTools
+     AND ENABLE_QUADMESHINGTOOLS)
+    set_config_option(HAVE_QUADMESHINGTOOLS "QuadMeshingTools")
+    add_subdirectory(contrib/QuadMeshingTools)
+    include_directories(BEFORE contrib/QuadMeshingTools)
+  endif()
+
+  if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/WinslowUntangler
+     AND ENABLE_WINSLOWUNTANGLER)
+    set_config_option(HAVE_WINSLOWUNTANGLER "WinslowUntangler")
+    add_subdirectory(contrib/WinslowUntangler)
+    include_directories(BEFORE contrib/WinslowUntangler)
+  endif()
 endif()
 
 if(ENABLE_MED OR ENABLE_CGNS)
@@ -1184,50 +1224,10 @@ if(HAVE_SOLVER)
   endif()
 endif()
 
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/hxt AND ENABLE_HXT)
-  add_subdirectory(contrib/hxt)
-  include_directories(BEFORE ${HXT_INC_DIRS})
-  set_config_option(HAVE_HXT "Hxt")
-  # do not use arithmetic contraction in predicates.c
-  if(MSVC OR (CMAKE_C_COMPILER_ID STREQUAL "Intel" AND WIN32))
-    set_source_files_properties(
-        "${CMAKE_CURRENT_SOURCE_DIR}/contrib/hxt/predicates/src/predicates.c"
-        PROPERTIES COMPILE_FLAGS "/fp:strict")
-  elseif(CMAKE_C_COMPILER_ID STREQUAL "Intel")
-    set_source_files_properties(
-        "${CMAKE_CURRENT_SOURCE_DIR}/contrib/hxt/predicates/src/predicates.c"
-        PROPERTIES COMPILE_FLAGS "-fp-model strict")
-  elseif(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
-    set_source_files_properties(
-        "${CMAKE_CURRENT_SOURCE_DIR}/contrib/hxt/predicates/src/predicates.c"
-        PROPERTIES COMPILE_FLAGS  "-fno-unsafe-math-optimizations -ffp-contract=off")
-  else()
-    message(WARNING "Unknown compiler: make sure compiled functions from "
-            "predicates.c do not use extended double precision and follow "
-            "the IEEE754 standard. It is crucial for the robustness of "
-            "geometric predicates.")
-  endif()
-  if(MSVC)
-    add_definitions(/bigobj)
-  endif()
-endif()
-
 if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/pro AND ENABLE_PRO)
   add_subdirectory(pro)
 endif()
 
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/QuadMeshingTools AND ENABLE_QUADMESHINGTOOLS)
-  set_config_option(HAVE_QUADMESHINGTOOLS "QuadMeshingTools")
-  add_subdirectory(contrib/QuadMeshingTools)
-  include_directories(BEFORE contrib/QuadMeshingTools)
-endif()
-
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/WinslowUntangler AND ENABLE_WINSLOWUNTANGLER)
-  set_config_option(HAVE_WINSLOWUNTANGLER "WinslowUntangler")
-  add_subdirectory(contrib/WinslowUntangler)
-  include_directories(BEFORE contrib/WinslowUntangler)
-endif()
-
 if(ENABLE_OCC)
   set(OCC_MINIMAL_VERSION "6.9.1")
   if(WIN32)
diff --git a/src/common/GmshGlobal.cpp b/src/common/GmshGlobal.cpp
index dc1e54fd36b6004810a73aabf18147db602fdfaa..4482e030ad6c877823f359f058e138495f657a90 100644
--- a/src/common/GmshGlobal.cpp
+++ b/src/common/GmshGlobal.cpp
@@ -439,6 +439,7 @@ int GmshFLTK(int argc, char **argv)
     break;
   }
 
+#if defined(HAVE_POST) && defined(HAVE_MESH)
   // read background mesh if any
   if(!CTX::instance()->bgmFileName.empty()) {
     // If the background mesh is an octree (we us p4est), then we load the
@@ -461,6 +462,7 @@ int GmshFLTK(int argc, char **argv)
         Msg::Error("Invalid background mesh (no view)");
     }
   }
+#endif
 
   // listen to external solvers
   if(CTX::instance()->solver.listen) {
diff --git a/src/fltk/FlGui.cpp b/src/fltk/FlGui.cpp
index db5a67f1c1ec48a06f1a3f2a61a42a609fa8f6ae..2527835e95249eaaad4423d342c1ffb3dfdb64b6 100644
--- a/src/fltk/FlGui.cpp
+++ b/src/fltk/FlGui.cpp
@@ -36,7 +36,6 @@
 #include "OS.h"
 #include "MElement.h"
 #include "PView.h"
-#include "Field.h"
 #include "Plugin.h"
 #include "PluginManager.h"
 #include "OpenFile.h"
@@ -48,9 +47,15 @@
 #include "gl2ps.h"
 #include "gmshPopplerWrapper.h"
 #include "PixelBuffer.h"
+
+#if defined(HAVE_MESH)
+#include "Field.h"
+#endif
+
 #if defined(HAVE_TOUCHBAR)
 #include "touchBar.h"
 #endif
+
 #if defined(HAVE_3M)
 #include "3M.h"
 #endif
@@ -1129,7 +1134,9 @@ void FlGui::updateViews(bool numberOfViewsHasChanged, bool deleteWidgets)
 
 void FlGui::updateFields()
 {
+#if defined(HAVE_MESH)
   fields->editField(GModel::current()->getFields()->get(fields->selected_id));
+#endif
 }
 
 void FlGui::resetVisibility()
diff --git a/src/fltk/classificationEditor.cpp b/src/fltk/classificationEditor.cpp
index 0e70edc584c2e984e77e25a1b4a948f897afc751..5e48a17e6706f58cd9ebea9a5d0dbb1d622e7ad0 100644
--- a/src/fltk/classificationEditor.cpp
+++ b/src/fltk/classificationEditor.cpp
@@ -6,6 +6,7 @@
 #include <FL/Fl_Tabs.H>
 #include <FL/Fl_Box.H>
 #include <FL/Fl_Return_Button.H>
+#include "GmshConfig.h"
 #include "FlGui.h"
 #include "classificationEditor.h"
 #include "paletteWindow.h"
@@ -16,11 +17,14 @@
 #include "GmshMessage.h"
 #include "MLine.h"
 #include "MQuadrangle.h"
-#include "meshGFaceDelaunayInsertion.h"
 #include "discreteEdge.h"
 #include "discreteFace.h"
 #include "GModelParametrize.h"
 
+#if defined(HAVE_MESH)
+#include "meshGFaceDelaunayInsertion.h"
+#endif
+
 static void NoElementsSelectedMode(classificationEditor *e)
 {
   e->buttons[CLASS_BUTTON_SELECT_ELEMENTS]->activate();
@@ -57,6 +61,7 @@ static void update_edges_cb(Fl_Widget *w, void *data)
     delete e->selected->lines[i];
   e->selected->lines.clear();
 
+#if defined(HAVE_MESH)
   double threshold = e->inputs[CLASS_VALUE_ANGLE]->value() / 180. * M_PI;
   for(std::size_t i = 0; i < e->edges_detected.size(); i++) {
     edge_angle ea = e->edges_detected[i];
@@ -70,6 +75,7 @@ static void update_edges_cb(Fl_Widget *w, void *data)
       e->selected->lines.push_back(new MLine(ea.v1, ea.v2));
     }
   }
+#endif
 
   Msg::Info("Edges: %d inside, %d boundary, %d selected",
             (int)e->edges_detected.size(), (int)e->edges_lonly.size(),
@@ -143,9 +149,14 @@ static void select_elements_cb(Fl_Widget *w, void *data)
     CTX::instance()->pickElements = 0;
   }
 
+#if defined(HAVE_MESH)
   e2t_cont adj;
   buildEdgeToElements(e->elements, adj);
   buildListOfEdgeAngle(adj, e->edges_detected, e->edges_lonly);
+#else
+  Msg::Error("Classification requires mesh module");
+#endif
+
   ElementsSelectedMode(e);
   update_edges_cb(nullptr, data);
   Msg::StatusGl("");
diff --git a/src/fltk/classificationEditor.h b/src/fltk/classificationEditor.h
index 749ee88a6907337f5d08f5d8ac83693689ac9b3c..dbecc7452e2f905c9168a775d9edd15f6a72e705 100644
--- a/src/fltk/classificationEditor.h
+++ b/src/fltk/classificationEditor.h
@@ -11,9 +11,13 @@
 #include <FL/Fl_Window.H>
 #include <FL/Fl_Check_Button.H>
 #include <FL/Fl_Value_Input.H>
+#include "GmshConfig.h"
 #include "GModel.h"
 #include "MElement.h"
+
+#if defined(HAVE_MESH)
 #include "meshGFaceOptimize.h"
+#endif
 
 #define CLASS_BUTTON_SELECT_ELEMENTS 0
 #define CLASS_BUTTON_SELECT_ALL_ELEMENTS 1
@@ -38,7 +42,11 @@ public:
   Fl_Check_Button *toggles[4];
   Fl_Value_Input *inputs[1];
   GEdge *selected;
+#if defined(HAVE_MESH)
   std::vector<edge_angle> edges_detected, edges_lonly;
+#else
+  std::vector<double> edges_detected, edges_lonly;
+#endif
   classificationEditor();
   void show() { window->show(); }
 };
diff --git a/src/fltk/fieldWindow.cpp b/src/fltk/fieldWindow.cpp
index 4c978223aa4b3d394e2b6ec657b1ac878432b7c8..3be7b27cfe05b4c23882aa68445b25fcf54068f3 100644
--- a/src/fltk/fieldWindow.cpp
+++ b/src/fltk/fieldWindow.cpp
@@ -15,27 +15,33 @@
 #include <FL/Fl_Round_Button.H>
 #include <FL/Fl_Value_Input.H>
 #include <FL/fl_draw.H>
+#include "GmshConfig.h"
+#include "GmshDefines.h"
 #include "FlGui.h"
 #include "drawContext.h"
 #include "fieldWindow.h"
 #include "paletteWindow.h"
 #include "fileDialogs.h"
-#include "GmshDefines.h"
 #include "GModel.h"
 #include "PView.h"
 #include "GmshMessage.h"
-#include "Field.h"
 #include "scriptStringInterface.h"
 #include "StringUtils.h"
 #include "Options.h"
 #include "Context.h"
 
+#if defined(HAVE_MESH)
+#include "Field.h"
+#endif
+
 void field_cb(Fl_Widget *w, void *data)
 {
   FlGui::instance()->fields->win->show();
   FlGui::instance()->fields->editField(nullptr);
 }
 
+#if defined(HAVE_MESH)
+
 static void field_delete_cb(Fl_Widget *w, void *data)
 {
   Field *f = (Field *)FlGui::instance()->fields->editor_group->user_data();
@@ -94,6 +100,8 @@ static void field_select_file_cb(Fl_Widget *w, void *data)
   }
 }
 
+#endif
+
 fieldWindow::fieldWindow(int deltaFontSize) : _deltaFontSize(deltaFontSize)
 {
   FL_NORMAL_SIZE -= deltaFontSize;
@@ -112,6 +120,7 @@ fieldWindow::fieldWindow(int deltaFontSize) : _deltaFontSize(deltaFontSize)
                           "Size fields");
   win->box(GMSH_WINDOW_BOX);
 
+#if defined(HAVE_MESH)
   int x = WB, y = WB, w = (int)(1.5 * BB), h = height - 2 * WB - 3 * BH;
 
   Fl_Menu_Button *new_btn = new Fl_Menu_Button(x, y, w, BH, "New");
@@ -188,6 +197,9 @@ fieldWindow::fieldWindow(int deltaFontSize) : _deltaFontSize(deltaFontSize)
                                     width - 9 * WB - 5 * BB,
                                     height - 3 * BH - 5 * WB));
   win->size_range(width0, height0);
+
+#endif
+
   win->position(CTX::instance()->fieldPosition[0],
                 CTX::instance()->fieldPosition[1]);
   win->end();
@@ -200,6 +212,7 @@ fieldWindow::fieldWindow(int deltaFontSize) : _deltaFontSize(deltaFontSize)
 
 void fieldWindow::loadFieldViewList()
 {
+#if defined(HAVE_MESH)
   put_on_view_btn->clear();
   put_on_view_btn->add("Create new view");
   put_on_view_btn->activate();
@@ -208,10 +221,12 @@ void fieldWindow::loadFieldViewList()
     s << "Put on View [" << i << "]";
     put_on_view_btn->add(s.str().c_str());
   }
+#endif
 }
 
 void fieldWindow::loadFieldList()
 {
+#if defined(HAVE_MESH)
   FieldManager &fields = *GModel::current()->getFields();
   Field *selected_field = (Field *)editor_group->user_data();
   browser->clear();
@@ -225,10 +240,12 @@ void fieldWindow::loadFieldList()
     browser->add(sstream.str().c_str(), field);
     if(it->second == selected_field) browser->select(i_entry);
   }
+#endif
 }
 
 void fieldWindow::saveFieldOptions()
 {
+#if defined(HAVE_MESH)
   auto input = options_widget.begin();
   Field *f = (Field *)editor_group->user_data();
   std::ostringstream sstream;
@@ -302,10 +319,12 @@ void fieldWindow::saveFieldOptions()
     scriptSetBackgroundField(-1, GModel::current()->getFileName());
     loadFieldList();
   }
+#endif
 }
 
 void fieldWindow::loadFieldOptions()
 {
+#if defined(HAVE_MESH)
   Field *f = (Field *)editor_group->user_data();
   auto input = options_widget.begin();
   for(auto it = f->options.begin(); it != f->options.end(); it++) {
@@ -361,10 +380,12 @@ void fieldWindow::loadFieldOptions()
       "Only a single field can be set as background field.\n"
       "To combine multiple fields use the Min or Max fields.");
   }
+#endif
 }
 
 void fieldWindow::editField(Field *f)
 {
+#if defined(HAVE_MESH)
   editor_group->user_data(f);
   put_on_view_btn->deactivate();
   delete_btn->deactivate();
@@ -450,4 +471,5 @@ void fieldWindow::editField(Field *f)
   put_on_view_btn->activate();
   delete_btn->activate();
   loadFieldList();
+#endif
 }
diff --git a/src/fltk/graphicWindow.cpp b/src/fltk/graphicWindow.cpp
index 1eaba97e49ff7275b8d1572b4736591a665b1cea..472c6d17ad2c3addcc886013f28dde259f89b017 100644
--- a/src/fltk/graphicWindow.cpp
+++ b/src/fltk/graphicWindow.cpp
@@ -61,10 +61,15 @@ typedef unsigned long intptr_t;
 #include "StringUtils.h"
 #include "OS.h"
 #include "onelabUtils.h"
+
+#if defined(HAVE_MESH)
 #include "gmshCrossFields.h"
+#endif
+
 #if defined(HAVE_3M)
 #include "3M.h"
 #endif
+
 #if defined(HAVE_TOUCHBAR)
 #include "touchBar.h"
 #endif
@@ -2178,6 +2183,8 @@ void mesh_3d_cb(Fl_Widget *w, void *data)
   drawContext::global()->draw();
 }
 
+#if defined(HAVE_MESH)
+
 static void mesh_modify_parts(Fl_Widget *w, void *data,
                               const std::string &action)
 {
@@ -2413,8 +2420,10 @@ static void mesh_untangle_cb(Fl_Widget *w, void *data)
 
 static void mesh_cross_compute_cb(Fl_Widget *w, void *data)
 {
+#if defined(HAVE_MESH)
   std::vector<int> tags;
   computeCrossField(GModel::current(), tags);
+#endif
   drawContext::global()->draw();
 }
 
@@ -2811,6 +2820,8 @@ static void mesh_define_compound_entity_cb(Fl_Widget *w, void *data)
   action_point_line_surface_volume(10, (const char *)data);
 }
 
+#endif // HAVE_MESH
+
 // clang-format off
 
 // The static menus (we cannot use the 'g', 'm' 's' and 'p' mnemonics since they
@@ -4648,6 +4659,7 @@ static menuItem static_modules[] = {
   {"0Modules/Geometry/Remove last script command",
    (Fl_Callback *)geometry_remove_last_command_cb},
   {"0Modules/Geometry/Edit script", (Fl_Callback *)geometry_edit_cb},
+#if defined(HAVE_MESH)
   {"0Modules/Mesh/Define/Size at points", (Fl_Callback *)mesh_define_length_cb},
   {"0Modules/Mesh/Define/Size fields", (Fl_Callback *)field_cb},
   {"0Modules/Mesh/Define/Embedded/Point",
@@ -4715,6 +4727,7 @@ static menuItem static_modules[] = {
    (void *)"volumes"},
   {"0Modules/Mesh/Inspect", (Fl_Callback *)mesh_inspect_cb},
   {"0Modules/Mesh/Save", (Fl_Callback *)mesh_save_cb},
+#endif
 };
 
 void onelabGroup::_addGmshMenus()
diff --git a/src/fltk/highOrderToolsWindow.cpp b/src/fltk/highOrderToolsWindow.cpp
index 2c926aab2b2d3c3e414bc13284bbe93a22bd6036..68fcdcbc67999b8b7cab9f62fd89b4487ca5ce67 100644
--- a/src/fltk/highOrderToolsWindow.cpp
+++ b/src/fltk/highOrderToolsWindow.cpp
@@ -3,7 +3,6 @@
 // See the LICENSE.txt file in the Gmsh root directory for license information.
 // Please report all issues on https://gitlab.onelab.info/gmsh/gmsh/issues.
 
-#include "GmshConfig.h"
 #include <string>
 #include <sstream>
 #include <map>
@@ -23,6 +22,9 @@
 #include "GModel.h"
 #include "MElement.h"
 #include "Context.h"
+
+#if defined(HAVE_MESH)
+
 #include "Generator.h"
 #include "HighOrder.h"
 
@@ -268,7 +270,10 @@ static void getMeshInfoForHighOrder(GModel *gm, int &meshOrder, bool &complete,
 
 highOrderToolsWindow::highOrderToolsWindow(int deltaFontSize)
 {
+#if defined(HAVE_MESH)
   getMeshInfoForHighOrder(GModel::current(), meshOrder, complete, CAD);
+#endif
+
   FL_NORMAL_SIZE -= deltaFontSize;
 
   int width = 3 * IW + 4 * WB;
@@ -513,3 +518,39 @@ void highordertools_cb(Fl_Widget *w, void *data)
   else
     FlGui::instance()->highordertools->show(false);
 }
+
+#else // HAVE_MESH
+
+highOrderToolsWindow::highOrderToolsWindow(int deltaFontSize)
+{
+  FL_NORMAL_SIZE -= deltaFontSize;
+
+  int width = 3 * IW + 4 * WB;
+  int height = 24 * BH;
+  win = new paletteWindow(width, height,
+                          CTX::instance()->nonModalWindows ? true : false,
+                          "High-order tools");
+  win->box(GMSH_WINDOW_BOX);
+
+  int y = WB;
+  int x = 2 * WB;
+
+  box = new Fl_Box(x, y, width - 4 * WB, BH);
+  box->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
+  box->label("High-order tools requires mesh module");
+
+  win->position(CTX::instance()->hotPosition[0],
+                CTX::instance()->hotPosition[1]);
+  win->end();
+  FL_NORMAL_SIZE += deltaFontSize;
+}
+
+void highOrderToolsWindow::show(bool redrawOnly)
+{
+}
+
+void highordertools_cb(Fl_Widget *w, void *data)
+{
+}
+
+#endif
diff --git a/src/fltk/partitionDialog.cpp b/src/fltk/partitionDialog.cpp
index 2d2d1fd26e61294e52591c4daeae3dfae3c6906a..1723071077cffa2fb80f05160f48cf60d54ede8d 100644
--- a/src/fltk/partitionDialog.cpp
+++ b/src/fltk/partitionDialog.cpp
@@ -27,11 +27,12 @@
 #include "GModel.h"
 #include "drawContext.h"
 #include "Options.h"
-#include "meshPartition.h"
 #include "Context.h"
 #include "fileDialogs.h"
 
-#if defined(HAVE_METIS)
+#if defined(HAVE_MESH) && defined(HAVE_METIS)
+
+#include "meshPartition.h"
 
 // Forward declarations of some callbacks
 void partition_opt_num_partitions_cb(Fl_Widget *widget, void *data);
diff --git a/src/fltk/statisticsWindow.cpp b/src/fltk/statisticsWindow.cpp
index 0c0c7f36c1d7806c60a72d455bc0283069acacd3..0655903bce71c3925897f3114d4d967b6262d8a4 100644
--- a/src/fltk/statisticsWindow.cpp
+++ b/src/fltk/statisticsWindow.cpp
@@ -13,10 +13,13 @@
 #include "GModel.h"
 #include "MElement.h"
 #include "PView.h"
-#include "Generator.h"
 #include "Context.h"
 #include "OS.h"
+
+#if defined(HAVE_MESH)
+#include "Generator.h"
 #include "Field.h"
+#endif
 
 enum QM_HISTO {
   QMH_SICN_XY,
@@ -230,12 +233,18 @@ void statisticsWindow::compute(bool elementQuality)
   int num = 0;
   static double s[50];
   static char label[50][256];
-  bool visibleOnly = visible->value() ? true : false;
 
+#if defined(HAVE_MESH)
+  bool visibleOnly = visible->value() ? true : false;
   if(elementQuality)
     GetStatistics(s, quality, visibleOnly);
   else
     GetStatistics(s, nullptr, visibleOnly);
+#else
+  for(int i = 0; i < 3; i++)
+    for(int j = 0; j < 100; j++)
+      quality[i][j] = 0;
+#endif
 
   // geom
   sprintf(label[num], "%g", s[0]);
diff --git a/src/fltk/touchBar.mm b/src/fltk/touchBar.mm
index 69711584aa773d5b056a7550d468d8998db14840..6b3fc1b67ff70146b85d6427946a8a1ce82e05d4 100644
--- a/src/fltk/touchBar.mm
+++ b/src/fltk/touchBar.mm
@@ -344,6 +344,7 @@ static NSString *touchBarItemViewSlider = @"com.something.item_viewSlider";
 
 - (void)buttonRunMesh:(id)sender
 {
+#if defined(HAVE_MESH)
   NSInteger segment = ((NSSegmentedControl *)sender).selectedSegment;
   switch(segment) {
   case 0: mesh_1d_cb(0, 0); break;
@@ -352,6 +353,9 @@ static NSString *touchBarItemViewSlider = @"com.something.item_viewSlider";
   default: break;
   }
   FlGui::check(); // to see meshing messages in the fltk gui
+#else
+  Msg::Warning("Mesh module not available");
+#endif
 }
 
 - (void)buttonGeo:(id)sender
diff --git a/src/fltk/viewButton.cpp b/src/fltk/viewButton.cpp
index 3d63df0ead989b21482943ec157cd768618725b4..4829442d0f12903b71a9c9264fa6b2debad52846 100644
--- a/src/fltk/viewButton.cpp
+++ b/src/fltk/viewButton.cpp
@@ -22,11 +22,14 @@ typedef unsigned long intptr_t;
 #include "PViewOptions.h"
 #include "Options.h"
 #include "OpenFile.h"
-#include "Field.h"
 #include "OS.h"
 #include "onelabGroup.h"
 #include "viewButton.h"
 
+#if defined(HAVE_MESH)
+#include "Field.h"
+#endif
+
 static void view_toggle_cb(Fl_Widget *w, void *data)
 {
   int num = (intptr_t)data;
@@ -260,9 +263,13 @@ static void view_all_visible_cb(Fl_Widget *w, void *data)
 
 static void view_applybgmesh_cb(Fl_Widget *w, void *data)
 {
+#if defined(HAVE_MESH)
   int index = (intptr_t)data;
   if(index >= 0 && index < (int)PView::list.size())
     GModel::current()->getFields()->setBackgroundMesh(index);
+#else
+  Msg::Error("Background mesh requires mesh module");
+#endif
 }
 
 viewButton::viewButton(int x, int y, int w, int h, int num, Fl_Color col)
diff --git a/src/geo/GFace.cpp b/src/geo/GFace.cpp
index 2f28db174ec09c77460aa54086fbae9a0f99b948..e722965390a18dd418cd1b4116b2226a81e6864e 100644
--- a/src/geo/GFace.cpp
+++ b/src/geo/GFace.cpp
@@ -21,13 +21,13 @@
 #include "discreteEdge.h"
 #include "discreteFace.h"
 #include "ExtrudeParams.h"
-#include "Field.h"
 
 #if defined(HAVE_MESH)
 #include "meshGFace.h"
 #include "meshGFaceOptimize.h"
 #include "BackgroundMeshTools.h"
 #include "meshGFaceBipartiteLabelling.h"
+#include "Field.h"
 #endif
 
 #if defined(HAVE_ALGLIB)
diff --git a/src/geo/scriptStringInterface.cpp b/src/geo/scriptStringInterface.cpp
index 85abde1352691d73703748e9de3a818852250639..2719a4c198098cb6b10a14c2a414f5196d102c8c 100644
--- a/src/geo/scriptStringInterface.cpp
+++ b/src/geo/scriptStringInterface.cpp
@@ -5,7 +5,6 @@
 
 #include <string.h>
 #include <sstream>
-#include "Field.h"
 #include "GmshConfig.h"
 #include "GmshMessage.h"
 #include "GModel.h"
@@ -20,6 +19,10 @@
 #include "OS.h"
 #include "Parser.h"
 
+#if defined(HAVE_MESH)
+#include "Field.h"
+#endif
+
 #if defined(HAVE_ONELAB)
 #include "onelab.h"
 #endif
@@ -459,6 +462,7 @@ void scriptAddFieldOption(int field_id, const std::string &option_name,
                           const std::string &option_value, int option_type,
                           const std::string &fileName)
 {
+#if defined(HAVE_MESH)
   for(auto &lang : CTX::instance()->scriptLang) {
     std::ostringstream sstream;
     if(lang == "geo") {
@@ -493,6 +497,7 @@ void scriptAddFieldOption(int field_id, const std::string &option_name,
     }
     scriptAddCommand(sstream.str(), fileName, lang);
   }
+#endif
 }
 
 void scriptAddField(int field_id, const std::string &type_name,