[mapguide-commits] r10262 - in branches/4.0/MgDev: . Bindings Bindings/src/Bindings Bindings/src/Bindings/DotNet Bindings/src/Bindings/DotNet/Portable Common Oem Portable Portable/DotNetUnitTest Portable/MgPortable Portable/MgPortable/Services/Rendering Portable/UnitTest Server/src/Gws Server/src/Gws/GwsCommon Server/src/Gws/GwsQueryEngine cmake/configs cmake/modules

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Tue Nov 3 05:53:49 PST 2026


Author: jng
Date: 2026-11-03 05:53:48 -0800 (Tue, 03 Nov 2026)
New Revision: 10262

Added:
   branches/4.0/MgDev/cmake/configs/Platform.ini.in
Modified:
   branches/4.0/MgDev/Bindings/setup_linux_native_libs.cmd
   branches/4.0/MgDev/Bindings/src/Bindings/CMakeLists.txt
   branches/4.0/MgDev/Bindings/src/Bindings/DotNet/CMakeLists.txt
   branches/4.0/MgDev/Bindings/src/Bindings/DotNet/Portable/CMakeLists.txt
   branches/4.0/MgDev/CMakeLists.txt
   branches/4.0/MgDev/Common/CMakeLists.txt
   branches/4.0/MgDev/Oem/CMakeLists.txt
   branches/4.0/MgDev/Portable/CMakeLists.txt
   branches/4.0/MgDev/Portable/DotNetUnitTest/Program.cs
   branches/4.0/MgDev/Portable/DotNetUnitTest/ResourceServiceTests.cs
   branches/4.0/MgDev/Portable/MgPortable/CMakeLists.txt
   branches/4.0/MgDev/Portable/MgPortable/Services/Rendering/MappingUtil.cpp
   branches/4.0/MgDev/Portable/UnitTest/CMakeLists.txt
   branches/4.0/MgDev/Portable/UnitTest/TestFeatureService.cpp
   branches/4.0/MgDev/Portable/UnitTest/TestPerformance.cpp
   branches/4.0/MgDev/Portable/UnitTest/TestResourceService.cpp
   branches/4.0/MgDev/Portable/UnitTest/TestServiceFactory.cpp
   branches/4.0/MgDev/Portable/UnitTest/main.cpp
   branches/4.0/MgDev/Server/src/Gws/CMakeLists.txt
   branches/4.0/MgDev/Server/src/Gws/GwsCommon/CMakeLists.txt
   branches/4.0/MgDev/Server/src/Gws/GwsQueryEngine/CMakeLists.txt
   branches/4.0/MgDev/Server/src/Gws/GwsQueryEngine/GwsFlatFdoReader.cpp
   branches/4.0/MgDev/cmake/modules/FindMapGuideThirdparty.cmake
   branches/4.0/MgDev/cmake_bootstrap.sh
   branches/4.0/MgDev/cmake_build.sh
Log:
#2897: Add Linux build support for MapGuide Portable

Modified: branches/4.0/MgDev/Bindings/setup_linux_native_libs.cmd
===================================================================
--- branches/4.0/MgDev/Bindings/setup_linux_native_libs.cmd	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Bindings/setup_linux_native_libs.cmd	2026-11-03 13:53:48 UTC (rev 10262)
@@ -1,4 +1,5 @@
 SET TARBALL=%1
+SET PORTABLE_TARBALL=%2
 SET SOVER=4.0.0
 if "%TARBALL%"=="" goto :NoTarball
 if not exist "%TARBALL%" goto :NoTarball
@@ -27,7 +28,25 @@
 copy /Y /L "%~p0\linux-x64\lib64\libMgWebSupport-%SOVER%.so" "%~p0\src\Managed\DotNet\OSGeo.MapGuide.Web\runtimes\linux-x64\native"
 copy /Y /L "%~p0\linux-x64\lib64\libMgHttpHandler-%SOVER%.so" "%~p0\src\Managed\DotNet\OSGeo.MapGuide.Web\runtimes\linux-x64\native"
 copy /Y /L "%~p0\linux-x64\lib64\liblib_json.so" "%~p0\src\Managed\DotNet\OSGeo.MapGuide.Web\runtimes\linux-x64\native"
+rem MgPortable setup. The portable tarball is optional; it carries MgPortable, its SWIG glue and
+rem the GWS/Renderers/Stylization/Security libraries the portable API needs.
+if "%PORTABLE_TARBALL%"=="" goto :Done
+if not exist "%PORTABLE_TARBALL%" goto :NoPortableTarball
+"%CD%\..\BuildTools\WebTools\7-Zip\7z.exe" x %PORTABLE_TARBALL% -so | "%CD%\..\BuildTools\WebTools\7-Zip\7z.exe" 7z x -aoa -si -ttar -o"%~p0\linux-x64"
+set PORTABLE_NATIVE=%~p0\src\Managed\DotNet\OSGeo.MapGuide.Portable\runtimes\linux-x64\native
+copy /Y /L "%~p0\linux-x64\lib64\libPortableUnmanagedApi.so" "%PORTABLE_NATIVE%"
+copy /Y /L "%~p0\linux-x64\lib64\libMgPortable-%SOVER%.so" "%PORTABLE_NATIVE%"
+copy /Y /L "%~p0\linux-x64\lib64\libMgMdfModel-%SOVER%.so" "%PORTABLE_NATIVE%"
+copy /Y /L "%~p0\linux-x64\lib64\libMgMdfParser-%SOVER%.so" "%PORTABLE_NATIVE%"
+copy /Y /L "%~p0\linux-x64\lib64\libMgRenderers-%SOVER%.so" "%PORTABLE_NATIVE%"
+copy /Y /L "%~p0\linux-x64\lib64\libMgStylization-%SOVER%.so" "%PORTABLE_NATIVE%"
+copy /Y /L "%~p0\linux-x64\lib64\libMgGwsCommon-%SOVER%.so" "%PORTABLE_NATIVE%"
+copy /Y /L "%~p0\linux-x64\lib64\libMgGwsQueryEngine-%SOVER%.so" "%PORTABLE_NATIVE%"
+copy /Y /L "%~p0\linux-x64\lib64\libMgGwsResource-%SOVER%.so" "%PORTABLE_NATIVE%"
 goto :Done
+:NoPortableTarball
+echo No MgPortable linux native libs tarball specified or tarball does not exist.
+goto :Done
 :NoTarball
 echo No linux native libs tarball specified or tarball does not exist.
 :Done
\ No newline at end of file

Modified: branches/4.0/MgDev/Bindings/src/Bindings/CMakeLists.txt
===================================================================
--- branches/4.0/MgDev/Bindings/src/Bindings/CMakeLists.txt	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Bindings/src/Bindings/CMakeLists.txt	2026-11-03 13:53:48 UTC (rev 10262)
@@ -12,13 +12,15 @@
     ${XERCESC_INCLUDE_DIR}
 )
 
-# We only need to build the .net SWIG glue libs if building for the common subset
-# We don't build this in a regular full end-to-end MG build
-if (MG_COMMON_SUBSET_ONLY)
+# The .net SWIG glue is only needed for the common subset, or for MgPortable (whose glue is the
+# portable API). A regular full end-to-end MG build does not build it otherwise.
+if (MG_COMMON_SUBSET_ONLY OR WITH_PORTABLE)
     add_subdirectory(DotNet)
-else (MG_COMMON_SUBSET_ONLY)
+endif (MG_COMMON_SUBSET_ONLY OR WITH_PORTABLE)
+
+if (NOT MG_COMMON_SUBSET_ONLY)
     if (WITH_JAVA)
         add_subdirectory(Java)
     endif (WITH_JAVA)
     add_subdirectory(Php)
-endif (MG_COMMON_SUBSET_ONLY)
\ No newline at end of file
+endif (NOT MG_COMMON_SUBSET_ONLY)
\ No newline at end of file

Modified: branches/4.0/MgDev/Bindings/src/Bindings/DotNet/CMakeLists.txt
===================================================================
--- branches/4.0/MgDev/Bindings/src/Bindings/DotNet/CMakeLists.txt	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Bindings/src/Bindings/DotNet/CMakeLists.txt	2026-11-03 13:53:48 UTC (rev 10262)
@@ -1,7 +1,17 @@
+file(COPY "dotnetcore_split.i" DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
+
+# PortableApiGen.xml %imports the Foundation, Geometry and PlatformBase glue, so those three are
+# always generated. The MapGuideCommon and Web glue is only needed by the common-libs subset
+# (the published .NET packages); a full build with WITH_PORTABLE only needs the portable glue.
 add_subdirectory(Foundation)
 add_subdirectory(Geometry)
 add_subdirectory(PlatformBase)
-add_subdirectory(MapGuideCommon)
-add_subdirectory(Web)
 
-file(COPY "dotnetcore_split.i" DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
\ No newline at end of file
+if (MG_COMMON_SUBSET_ONLY)
+    add_subdirectory(MapGuideCommon)
+    add_subdirectory(Web)
+endif (MG_COMMON_SUBSET_ONLY)
+
+if (WITH_PORTABLE)
+    add_subdirectory(Portable)
+endif (WITH_PORTABLE)
\ No newline at end of file

Modified: branches/4.0/MgDev/Bindings/src/Bindings/DotNet/Portable/CMakeLists.txt
===================================================================
--- branches/4.0/MgDev/Bindings/src/Bindings/DotNet/Portable/CMakeLists.txt	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Bindings/src/Bindings/DotNet/Portable/CMakeLists.txt	2026-11-03 13:53:48 UTC (rev 10262)
@@ -1,5 +1,16 @@
 project(PortableUnmanagedApi)
 
+# MgPortable.h (and the services it includes) pull in the public DWF Toolkit headers.
+include_directories(
+    ${MG_PORTABLE_DIR}/MgPortable
+    ${MG_OEM_DWF_INCLUDE_DIR}
+    ${MG_OEM_DWF_INCLUDE_DIR}/dwf
+)
+
+# Keep the portable artifacts together so they can be packaged and excluded as one subtree.
+set(MG_COMPONENT "portable")
+set_install_subdir("portable/")
+
 set(PortableUnmanagedApi_SRCS
     ${CMAKE_CURRENT_BINARY_DIR}/PortableUnmanagedApi_wrap.cpp
 )
@@ -6,11 +17,18 @@
 
 add_library(PortableUnmanagedApi SHARED ${PortableUnmanagedApi_SRCS})
 add_dependencies(PortableUnmanagedApi IMake)
+# PortableApiGen.xml %imports the generated Foundation/Geometry/PlatformBase interfaces.
+add_dependencies(PortableUnmanagedApi
+    FoundationUnmanagedApi
+    GeometryUnmanagedApi
+    PlatformBaseUnmanagedApi)
+add_definitions(-DDWFCORE_STATIC -DDWFTK_STATIC -DDWFTK_USE_DWFCORE_ZLIB -DDWFTK_BUILD_EXPAT -DWHIP_STATIC_LIB)
 if (UNIX)
     add_definitions(-DDOTNETCORE)
     # NOTE: We don't care where this source is generated because the C# source is only built on windows
     set(MG_DOTNET_PROJECT_DIR ${CMAKE_CURRENT_BINARY_DIR})
-    set_target_properties(PortableUnmanagedApi PROPERTIES INSTALL_RPATH "$ORIGIN")
+    set_target_properties(PortableUnmanagedApi PROPERTIES
+        INSTALL_RPATH "$ORIGIN:$ORIGIN/../../lib64")
 endif (UNIX)
 if (MSVC)
     add_definitions(-DSWIG_PUBLIC_API)

Modified: branches/4.0/MgDev/CMakeLists.txt
===================================================================
--- branches/4.0/MgDev/CMakeLists.txt	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/CMakeLists.txt	2026-11-03 13:53:48 UTC (rev 10262)
@@ -100,7 +100,10 @@
 set(MG_SERVER_COMMON_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Server/src/Common")
 set(MG_SERVER_SERVICES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Server/src/Services")
 set(MG_SERVER_GWS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Server/src/Gws")
+set(MG_PORTABLE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Portable")
 
+option(WITH_PORTABLE "Build MgPortable (the portable MapGuide API)" OFF)
+
 set(MG_AUTO_GENERATED_HEADER_DIR "${CMAKE_CURRENT_BINARY_DIR}/IncludeAutogenerated")
 set(MG_UNIT_TEST_DATA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/UnitTest/TestData")
 

Modified: branches/4.0/MgDev/Common/CMakeLists.txt
===================================================================
--- branches/4.0/MgDev/Common/CMakeLists.txt	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Common/CMakeLists.txt	2026-11-03 13:53:48 UTC (rev 10262)
@@ -25,6 +25,13 @@
 if (NOT MG_COMMON_SUBSET_ONLY)
     add_subdirectory(Renderers)
     add_subdirectory(Stylization)
+elseif (WITH_PORTABLE)
+    # MgPortable renders through Renderers/Stylization, so a portable common-subset build needs
+    # them too. Install them beside MgPortable so the common tarball can exclude them.
+    set_install_subdir("portable/")
+    add_subdirectory(Renderers)
+    add_subdirectory(Stylization)
+    set_install_subdir("")
 endif (NOT MG_COMMON_SUBSET_ONLY)
 
 if (NOT MG_COMMON_SUBSET_ONLY)

Modified: branches/4.0/MgDev/Oem/CMakeLists.txt
===================================================================
--- branches/4.0/MgDev/Oem/CMakeLists.txt	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Oem/CMakeLists.txt	2026-11-03 13:53:48 UTC (rev 10262)
@@ -24,9 +24,11 @@
 # at the moment, we are just using the system-provided version of GEOS
 #set(GEOS_ENABLE_TESTS OFF)
 #add_subdirectory(geos)
-if (NOT MG_COMMON_SUBSET_ONLY)
+# MgPortable consumes the DWF Toolkit (DrawingService), so the DWF tree is required whenever
+# MgPortable is built, including in a common-libs subset tree.
+if (NOT MG_COMMON_SUBSET_ONLY OR WITH_PORTABLE)
     add_subdirectory(DWFTK)
-endif (NOT MG_COMMON_SUBSET_ONLY)
+endif (NOT MG_COMMON_SUBSET_ONLY OR WITH_PORTABLE)
 
 add_subdirectory(CsMapLibrary)
 

Modified: branches/4.0/MgDev/Portable/CMakeLists.txt
===================================================================
--- branches/4.0/MgDev/Portable/CMakeLists.txt	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Portable/CMakeLists.txt	2026-11-03 13:53:48 UTC (rev 10262)
@@ -11,5 +11,11 @@
 set(MG_COMPONENT "portable")
 set_install_subdir("portable/")
 
+# MgPortable links the GWS libraries. A full build has already added them under Server; a
+# common-libs subset tree has not, so add only them here.
+if (MG_COMMON_SUBSET_ONLY)
+    add_subdirectory(${MG_SERVER_GWS_DIR} ${CMAKE_CURRENT_BINARY_DIR}/Gws)
+endif (MG_COMMON_SUBSET_ONLY)
+
 add_subdirectory(MgPortable)
 add_subdirectory(UnitTest)
\ No newline at end of file

Modified: branches/4.0/MgDev/Portable/DotNetUnitTest/Program.cs
===================================================================
--- branches/4.0/MgDev/Portable/DotNetUnitTest/Program.cs	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Portable/DotNetUnitTest/Program.cs	2026-11-03 13:53:48 UTC (rev 10262)
@@ -22,11 +22,10 @@
             }
             catch (AssertException ex)
             {
-                //Console.WriteLine(ex.Message);
                 failed++;
                 Console.Write("F");
+                Console.WriteLine("\nFAILED: " + method.Method.Name + ": " + ex.Message);
                 Trace.TraceInformation("FAILED: " + method.Method.Name);
-                //Trace.TraceInformation("F");
             }
         }
 
@@ -34,6 +33,8 @@
         {
             MgPortablePlatform.Initialize("Platform.ini");
 
+            try
+            {
             var resTest = new ResourceServiceTests();
             try
             {
@@ -140,6 +141,12 @@
 
             Console.WriteLine("\n{0} tests passed {1} tests failed", passed, failed);
             Trace.TraceInformation("{0} tests passed {1} tests failed", passed, failed);
+            Environment.ExitCode = failed == 0 ? 0 : 1;
+            }
+            finally
+            {
+                MgPortablePlatform.Terminate();
+            }
         }
     }
 }

Modified: branches/4.0/MgDev/Portable/DotNetUnitTest/ResourceServiceTests.cs
===================================================================
--- branches/4.0/MgDev/Portable/DotNetUnitTest/ResourceServiceTests.cs	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Portable/DotNetUnitTest/ResourceServiceTests.cs	2026-11-03 13:53:48 UTC (rev 10262)
@@ -20,12 +20,13 @@
         string resourceDataName = "World_Countries.sdf";
         string resourceDataName2 = "New_World_Countries.sdf";
 
-        string repositoryHeaderFileName = "..\\UnitTestFiles\\SampleRepositoryHeader.xm";
-        string repositoryContentFileName = "..\\UnitTestFiles\\SampleRepositoryContent.xm";
-        string resourceContentFileName = "..\\UnitTestFiles\\TEST.FeatureSource";
-        string resourceContentFileName2 = "..\\UnitTestFiles\\TEST.LayerDefinition";
-        string dataFileName = "..\\UnitTestFiles\\World_Countries.sdf";
-        string packageName = "..\\UnitTestFiles\\Shuttle.zip";
+        // Forward slashes are accepted by the .NET file APIs on Windows and are required on POSIX.
+        string repositoryHeaderFileName = "../UnitTestFiles/SampleRepositoryHeader.xm";
+        string repositoryContentFileName = "../UnitTestFiles/SampleRepositoryContent.xm";
+        string resourceContentFileName = "../UnitTestFiles/TEST.FeatureSource";
+        string resourceContentFileName2 = "../UnitTestFiles/TEST.LayerDefinition";
+        string dataFileName = "../UnitTestFiles/World_Countries.sdf";
+        string packageName = "../UnitTestFiles/Shuttle.zip";
 
         
         public void TestStart()
@@ -227,7 +228,8 @@
                 string xml = byteReader.ToString();
                 Assert.True(xml.Length > 0);
                 Assert.True(xml.Contains("<FeatureSource"));
-                Assert.True(xml.EndsWith("</FeatureSource>"));
+                // The fixture ends with a newline, which some platforms preserve.
+                Assert.True(xml.TrimEnd().EndsWith("</FeatureSource>"));
             }
             catch (MgException ex)
             {
@@ -482,11 +484,11 @@
 
                 var resource = new MgResourceIdentifier("Library://UnitTests/Data/TEST.FeatureSource");
 
-                var source = new MgByteSource("..\\UnitTestFiles\\TEST.FeatureSource");
+                var source = new MgByteSource("../UnitTestFiles/TEST.FeatureSource");
                 var reader = source.GetReader();
                 service.SetResource(resource, reader, null);
 
-                var dataSource = new MgByteSource("..\\UnitTestFiles\\TEST.sdf");
+                var dataSource = new MgByteSource("../UnitTestFiles/TEST.sdf");
                 var dataReader = dataSource.GetReader();
                 service.SetResourceData(resource, "TEST.sdf", "File", dataReader);
 
@@ -512,7 +514,10 @@
             {
                 var fact = new MgPortableServiceFactory();
                 var service = (MgResourceService)fact.CreateService(MgServiceType.ResourceService);
-                Assert.ThrowsWithCode(() => service.EnumerateUnmanagedData("", true, "", ""), MgExceptionCodes.MgNotImplementedException);
+                // The type must be Folders, Files or Both; MgResourceUnmanagedDataType is a
+                // PlatformBase constant class that the SWIG surface does not expose.
+                Assert.AreEqual(MgMimeType.Xml, service.EnumerateUnmanagedData("", false, "Folders", "").MimeType);
+                Assert.AreEqual(MgMimeType.Xml, service.EnumerateUnmanagedData("", true, "Files", "").MimeType);
             }
             catch (MgException ex)
             {

Modified: branches/4.0/MgDev/Portable/MgPortable/CMakeLists.txt
===================================================================
--- branches/4.0/MgDev/Portable/MgPortable/CMakeLists.txt	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Portable/MgPortable/CMakeLists.txt	2026-11-03 13:53:48 UTC (rev 10262)
@@ -30,6 +30,10 @@
 add_definitions(-DDWFCORE_STATIC -DDWFTK_STATIC -DDWFTK_USE_DWFCORE_ZLIB -DDWFTK_BUILD_EXPAT -DWHIP_STATIC_LIB)
 
 add_library(MgPortable${MG_VERSION_SUFFIX} SHARED ${MgPortable_SRCS})
+# The library installs beside the GWS libraries under <prefix>/portable/lib64, while the
+# common libraries it links stay in <prefix>/lib64.
+set_target_properties(MgPortable${MG_VERSION_SUFFIX} PROPERTIES
+    INSTALL_RPATH "$ORIGIN:$ORIGIN/../../lib64")
 install(TARGETS MgPortable${MG_VERSION_SUFFIX} DESTINATION ${LIB_INSTALL_DIR} COMPONENT ${MG_COMPONENT})
 install_symlink(libMgPortable${MG_VERSION_SUFFIX}.so libMgPortable.so ${MG_COMPONENT})
 
@@ -38,12 +42,15 @@
     ${XERCESC_LIBRARIES}
     ${FDO_LIBRARY}
     ${FDO_EXPRESSIONENGINE_LIBRARY}
-    #dwfcore
-    #dwftk
-    #dwfwhiptk
+    # DrawingService.cpp and DrawingServiceUtil.cpp use the DWF Toolkit directly. The static
+    # libraries are linked the same way MgServerDrawingService links them.
+    -Wl,--exclude-libs,ALL
+    dwfcore-1.7.0
+    dwftk-7.7.0
     MgMdfModel${MG_VERSION_SUFFIX}
     MgMdfParser${MG_VERSION_SUFFIX}
     MgFoundation${MG_VERSION_SUFFIX}
+    MgGeometry${MG_VERSION_SUFFIX}
     MgPlatformBase${MG_VERSION_SUFFIX}
     MgSecurity${MG_VERSION_SUFFIX}
     MgRenderers${MG_VERSION_SUFFIX}
@@ -50,5 +57,6 @@
     MgStylization${MG_VERSION_SUFFIX}
     MgGwsCommon${MG_VERSION_SUFFIX}
     MgGwsQueryEngine${MG_VERSION_SUFFIX}
+    dl
     pthread
 )
\ No newline at end of file

Modified: branches/4.0/MgDev/Portable/MgPortable/Services/Rendering/MappingUtil.cpp
===================================================================
--- branches/4.0/MgDev/Portable/MgPortable/Services/Rendering/MappingUtil.cpp	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Portable/MgPortable/Services/Rendering/MappingUtil.cpp	2026-11-03 13:53:48 UTC (rev 10262)
@@ -35,6 +35,7 @@
 #include "SymbolVisitor.h"
 #include "SymbolDefinition.h"
 #include "Services/Feature/TransformCache.h"
+#include "System/TimerUtil.h"
 
 #include <algorithm>
 
@@ -46,7 +47,12 @@
 #ifndef _WIN32
 #define _wcsnicmp wcsncasecmp
 
-extern long GetTickCount();
+// The timing calls below are Debug-only and use GetTickCount(), which only exists on Windows.
+// Provide the same millisecond counter on POSIX so a Debug build links.
+static long GetTickCount()
+{
+    return static_cast<long>(MgPortableTimerUtil::GetTime());
+}
 #endif
 
 ///////////////////////////////////////////////////////////////////////////////

Modified: branches/4.0/MgDev/Portable/UnitTest/CMakeLists.txt
===================================================================
--- branches/4.0/MgDev/Portable/UnitTest/CMakeLists.txt	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Portable/UnitTest/CMakeLists.txt	2026-11-03 13:53:48 UTC (rev 10262)
@@ -5,7 +5,6 @@
     ${CMAKE_CURRENT_SOURCE_DIR}
     ${CMAKE_CURRENT_SOURCE_DIR}/../MgPortable
     ${CMAKE_CURRENT_SOURCE_DIR}/../MgPortable/Services
-    ${CPPUNIT_INCLUDE_DIR}
     ${MG_COMMON_DIR}/PlatformBase
     ${MG_COMMON_DIR}/CoordinateSystem
     ${MG_COMMON_DIR}/Security
@@ -40,10 +39,10 @@
     ${ACE_LIBRARY}
     ${FDO_LIBRARY}
     ${XERCESC_LIBRARIES}
-    ${CPPUNIT_LIBRARIES}
     MgMdfParser${MG_VERSION_SUFFIX}
     MgMdfModel${MG_VERSION_SUFFIX}
     MgFoundation${MG_VERSION_SUFFIX}
+    MgGeometry${MG_VERSION_SUFFIX}
     MgPlatformBase${MG_VERSION_SUFFIX}
     MgSecurity${MG_VERSION_SUFFIX}
     MgPortable${MG_VERSION_SUFFIX}

Modified: branches/4.0/MgDev/Portable/UnitTest/TestFeatureService.cpp
===================================================================
--- branches/4.0/MgDev/Portable/UnitTest/TestFeatureService.cpp	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Portable/UnitTest/TestFeatureService.cpp	2026-11-03 13:53:48 UTC (rev 10262)
@@ -90,7 +90,7 @@
 
         const STRING provider = L"OSGeo.SDF";
 #ifdef _WIN32
-        const STRING connectionString = L"File=..\\UnitTestFiles\\Sheboygan_Parcels.sdf";
+        const STRING connectionString = L"File=../UnitTestFiles/Sheboygan_Parcels.sdf";
 #else
         const STRING connectionString = L"File=../UnitTestFiles/Sheboygan_Parcels.sdf";
 #endif

Modified: branches/4.0/MgDev/Portable/UnitTest/TestPerformance.cpp
===================================================================
--- branches/4.0/MgDev/Portable/UnitTest/TestPerformance.cpp	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Portable/UnitTest/TestPerformance.cpp	2026-11-03 13:53:48 UTC (rev 10262)
@@ -81,8 +81,8 @@
             MgResourceIdentifier resourceIdentifier1(L"Library://UnitTests/Data/Sheboygan_Parcels.FeatureSource");
 
 #ifdef _WIN32
-            STRING resourceContentFileName1 = L"..\\UnitTestFiles\\Sheboygan_Parcels.FeatureSource";
-            STRING dataFileName1 = L"..\\UnitTestFiles\\Sheboygan_Parcels.sdf";
+            STRING resourceContentFileName1 = L"../UnitTestFiles/Sheboygan_Parcels.FeatureSource";
+            STRING dataFileName1 = L"../UnitTestFiles/Sheboygan_Parcels.sdf";
 #else
             STRING resourceContentFileName1 = L"../UnitTestFiles/Sheboygan_Parcels.FeatureSource";
             STRING dataFileName1 = L"../UnitTestFiles/Sheboygan_Parcels.sdf";

Modified: branches/4.0/MgDev/Portable/UnitTest/TestResourceService.cpp
===================================================================
--- branches/4.0/MgDev/Portable/UnitTest/TestResourceService.cpp	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Portable/UnitTest/TestResourceService.cpp	2026-11-03 13:53:48 UTC (rev 10262)
@@ -51,12 +51,12 @@
 STRING resourceDataName2 = L"New_World_Countries.sdf";
 
 #ifdef _WIN32
-STRING repositoryHeaderFileName = L"..\\UnitTestFiles\\SampleRepositoryHeader.xml";
-STRING repositoryContentFileName = L"..\\UnitTestFiles\\SampleRepositoryContent.xml";
-STRING resourceContentFileName = L"..\\UnitTestFiles\\TEST.FeatureSource";
-STRING resourceContentFileName2 = L"..\\UnitTestFiles\\TEST.LayerDefinition";
-STRING dataFileName = L"..\\UnitTestFiles\\World_Countries.sdf";
-STRING packageName = L"..\\UnitTestFiles\\Shuttle.zip";
+STRING repositoryHeaderFileName = L"../UnitTestFiles/SampleRepositoryHeader.xml";
+STRING repositoryContentFileName = L"../UnitTestFiles/SampleRepositoryContent.xml";
+STRING resourceContentFileName = L"../UnitTestFiles/TEST.FeatureSource";
+STRING resourceContentFileName2 = L"../UnitTestFiles/TEST.LayerDefinition";
+STRING dataFileName = L"../UnitTestFiles/World_Countries.sdf";
+STRING packageName = L"../UnitTestFiles/Shuttle.zip";
 #else
 STRING repositoryHeaderFileName = L"../UnitTestFiles/SampleRepositoryHeader.xml";
 STRING repositoryContentFileName = L"../UnitTestFiles/SampleRepositoryContent.xml";
@@ -958,8 +958,8 @@
         resource = new MgResourceIdentifier(L"Library://UnitTests/Data/TEST.FeatureSource");
 
         #ifdef _WIN32
-        STRING rcName = L"..\\UnitTestFiles\\TEST.FeatureSource";
-        STRING dfName = L"..\\UnitTestFiles\\TEST.sdf";
+        STRING rcName = L"../UnitTestFiles/TEST.FeatureSource";
+        STRING dfName = L"../UnitTestFiles/TEST.sdf";
         #else
         STRING rcName = L"../UnitTestFiles/TEST.FeatureSource";
         STRING dfName = L"../UnitTestFiles/TEST.sdf";
@@ -1148,7 +1148,7 @@
             pService->DeleteResource(&resId);
         }
 
-        Ptr<MgByteSource> byteSource = new MgByteSource(L"..\\UnitTestFiles\\PackageTest.mgp");
+        Ptr<MgByteSource> byteSource = new MgByteSource(L"../UnitTestFiles/PackageTest.mgp");
         Ptr<MgByteReader> byteReader = byteSource->GetReader();
 
         //This package contains an UPDATEREPOSITORY directive, which should no-op when loaded by mg-desktop

Modified: branches/4.0/MgDev/Portable/UnitTest/TestServiceFactory.cpp
===================================================================
--- branches/4.0/MgDev/Portable/UnitTest/TestServiceFactory.cpp	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Portable/UnitTest/TestServiceFactory.cpp	2026-11-03 13:53:48 UTC (rev 10262)
@@ -897,23 +897,23 @@
                 MgResourceIdentifier resourceIdentifier8(L"Library://UnitTests/Data/FdoJoin.FeatureSource");
                 MgResourceIdentifier resourceIdentifier9(L"Library://UnitTests/Data/SecuredCredentials.FeatureSource");
 #ifdef _WIN32
-                STRING resourceContentFileName1 = L"..\\UnitTestFiles\\Sheboygan_Parcels.FeatureSource";
-                STRING resourceContentFileName2 = L"..\\UnitTestFiles\\Redding_Parcels.FeatureSource";
-                STRING resourceContentFileName3 = L"..\\UnitTestFiles\\Sheboygan_BuildingOutlines.FeatureSource";
-                STRING resourceContentFileName4 = L"..\\UnitTestFiles\\Sheboygan_VotingDistricts.FeatureSource";
-                STRING resourceContentFileName5 = L"..\\UnitTestFiles\\TESTChainedInner1ToManyJoin.FeatureSource";
-                STRING resourceContentFileName6 = L"..\\UnitTestFiles\\Empty.FeatureSource";
-                STRING resourceContentFileName7 = L"..\\UnitTestFiles\\Sheboygan_Parcels_Writable.FeatureSource";
-                STRING resourceContentFileName8 = L"..\\UnitTestFiles\\UT_FdoJoin.FeatureSource";
-                STRING resourceContentFileName9 = L"..\\UnitTestFiles\\SecuredCredentials.fs";
-                STRING dataFileName1 = L"..\\UnitTestFiles\\Sheboygan_Parcels.sdf";
-                STRING dataFileName2 = L"..\\UnitTestFiles\\Redding_Parcels.shp";
-                STRING dataFileName3 = L"..\\UnitTestFiles\\Redding_Parcels.dbf";
-                STRING dataFileName4 = L"..\\UnitTestFiles\\Redding_Parcels.shx";
-                STRING dataFileName5 = L"..\\UnitTestFiles\\Sheboygan_BuildingOutlines.sdf";
-                STRING dataFileName6 = L"..\\UnitTestFiles\\Sheboygan_VotingDistricts.sdf";
-                STRING dataFileName7 = L"..\\UnitTestFiles\\Empty.sdf";
-                STRING dataFileName8 = L"..\\UnitTestFiles\\JoinTest.sqlite";
+                STRING resourceContentFileName1 = L"../UnitTestFiles/Sheboygan_Parcels.FeatureSource";
+                STRING resourceContentFileName2 = L"../UnitTestFiles/Redding_Parcels.FeatureSource";
+                STRING resourceContentFileName3 = L"../UnitTestFiles/Sheboygan_BuildingOutlines.FeatureSource";
+                STRING resourceContentFileName4 = L"../UnitTestFiles/Sheboygan_VotingDistricts.FeatureSource";
+                STRING resourceContentFileName5 = L"../UnitTestFiles/TESTChainedInner1ToManyJoin.FeatureSource";
+                STRING resourceContentFileName6 = L"../UnitTestFiles/Empty.FeatureSource";
+                STRING resourceContentFileName7 = L"../UnitTestFiles/Sheboygan_Parcels_Writable.FeatureSource";
+                STRING resourceContentFileName8 = L"../UnitTestFiles/UT_FdoJoin.FeatureSource";
+                STRING resourceContentFileName9 = L"../UnitTestFiles/SecuredCredentials.fs";
+                STRING dataFileName1 = L"../UnitTestFiles/Sheboygan_Parcels.sdf";
+                STRING dataFileName2 = L"../UnitTestFiles/Redding_Parcels.shp";
+                STRING dataFileName3 = L"../UnitTestFiles/Redding_Parcels.dbf";
+                STRING dataFileName4 = L"../UnitTestFiles/Redding_Parcels.shx";
+                STRING dataFileName5 = L"../UnitTestFiles/Sheboygan_BuildingOutlines.sdf";
+                STRING dataFileName6 = L"../UnitTestFiles/Sheboygan_VotingDistricts.sdf";
+                STRING dataFileName7 = L"../UnitTestFiles/Empty.sdf";
+                STRING dataFileName8 = L"../UnitTestFiles/JoinTest.sqlite";
 #else
                 STRING resourceContentFileName1 = L"../UnitTestFiles/Sheboygan_Parcels.FeatureSource";
                 STRING resourceContentFileName2 = L"../UnitTestFiles/Redding_Parcels.FeatureSource";

Modified: branches/4.0/MgDev/Portable/UnitTest/main.cpp
===================================================================
--- branches/4.0/MgDev/Portable/UnitTest/main.cpp	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Portable/UnitTest/main.cpp	2026-11-03 13:53:48 UTC (rev 10262)
@@ -53,6 +53,15 @@
 #if !defined(USING_VLD)
     _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
 
+    // The leak report is written to the debugger's output, which a plain command-line run
+    // discards. Set MG_CRT_LEAK_CHECK=1 to send it to stderr instead, so it shows up in the
+    // run's console output and in the log file a test run tees to.
+    if (getenv("MG_CRT_LEAK_CHECK") != NULL)
+    {
+        _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
+        _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+    }
+
     #ifdef _DEBUG
     // to debug memory leaks, set a breakpoint here and set iBlock
     // to the block allocation you want to break on

Modified: branches/4.0/MgDev/Server/src/Gws/CMakeLists.txt
===================================================================
--- branches/4.0/MgDev/Server/src/Gws/CMakeLists.txt	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Server/src/Gws/CMakeLists.txt	2026-11-03 13:53:48 UTC (rev 10262)
@@ -1,7 +1,14 @@
 set(MG_COMPONENT "common")
 set(CMAKE_INSTALL_PREFIX "${MG_INSTALL_PREFIX}")
 
-set_install_subdir("")
+# In a common-libs subset tree the GWS libraries are only built for MgPortable. Install them
+# beside it so the common tarball can exclude them wholesale.
+if (MG_COMMON_SUBSET_ONLY AND WITH_PORTABLE)
+    set(MG_COMPONENT "portable")
+    set_install_subdir("portable/")
+else (MG_COMMON_SUBSET_ONLY AND WITH_PORTABLE)
+    set_install_subdir("")
+endif (MG_COMMON_SUBSET_ONLY AND WITH_PORTABLE)
 
 add_subdirectory(GwsCommon)
 add_subdirectory(GwsQueryEngine)

Modified: branches/4.0/MgDev/Server/src/Gws/GwsCommon/CMakeLists.txt
===================================================================
--- branches/4.0/MgDev/Server/src/Gws/GwsCommon/CMakeLists.txt	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Server/src/Gws/GwsCommon/CMakeLists.txt	2026-11-03 13:53:48 UTC (rev 10262)
@@ -24,5 +24,13 @@
 )
 
 add_library(MgGwsCommon${MG_VERSION_SUFFIX} SHARED ${GwsCommon_SRCS})
+# A portable build links the GWS libraries into libMgPortable with --no-undefined, so their own
+# FDO/Foundation/Geometry dependencies have to be explicit rather than resolved by the server
+# process at load time.
+target_link_libraries(MgGwsCommon${MG_VERSION_SUFFIX}
+    ${FDO_LIBRARY}
+    MgFoundation${MG_VERSION_SUFFIX}
+    MgGeometry${MG_VERSION_SUFFIX}
+)
 install(TARGETS MgGwsCommon${MG_VERSION_SUFFIX} DESTINATION ${LIB_INSTALL_DIR} COMPONENT ${MG_COMPONENT})
 install_symlink(libMgGwsCommon${MG_VERSION_SUFFIX}.so libMgGwsCommon.so ${MG_COMPONENT})
\ No newline at end of file

Modified: branches/4.0/MgDev/Server/src/Gws/GwsQueryEngine/CMakeLists.txt
===================================================================
--- branches/4.0/MgDev/Server/src/Gws/GwsQueryEngine/CMakeLists.txt	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Server/src/Gws/GwsQueryEngine/CMakeLists.txt	2026-11-03 13:53:48 UTC (rev 10262)
@@ -50,5 +50,10 @@
 )
 
 add_library(MgGwsQueryEngine${MG_VERSION_SUFFIX} SHARED ${GwsQueryEngine_SRCS})
+target_link_libraries(MgGwsQueryEngine${MG_VERSION_SUFFIX}
+    ${FDO_LIBRARY}
+    ${FDO_EXPRESSIONENGINE_LIBRARY}
+    MgGwsCommon${MG_VERSION_SUFFIX}
+)
 install(TARGETS MgGwsQueryEngine${MG_VERSION_SUFFIX} DESTINATION ${LIB_INSTALL_DIR} COMPONENT ${MG_COMPONENT})
 install_symlink(libMgGwsQueryEngine${MG_VERSION_SUFFIX}.so libMgGwsQueryEngine.so ${MG_COMPONENT})
\ No newline at end of file

Modified: branches/4.0/MgDev/Server/src/Gws/GwsQueryEngine/GwsFlatFdoReader.cpp
===================================================================
--- branches/4.0/MgDev/Server/src/Gws/GwsQueryEngine/GwsFlatFdoReader.cpp	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/Server/src/Gws/GwsQueryEngine/GwsFlatFdoReader.cpp	2026-11-03 13:53:48 UTC (rev 10262)
@@ -26,7 +26,16 @@
 #include "GwsQueryEngineImp.h"
 
 #ifndef _WIN32
-extern long GetTickCount();
+#include <sys/time.h>
+
+// CGwsTimer is compiled on every platform but the Windows GetTickCount() it calls does not exist
+// on POSIX. Only GWSTIMER expands to it, which is a Debug-only macro.
+static long GetTickCount()
+{
+    timeval tv;
+    gettimeofday(&tv, NULL);
+    return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+}
 #endif
 
 

Added: branches/4.0/MgDev/cmake/configs/Platform.ini.in
===================================================================
--- branches/4.0/MgDev/cmake/configs/Platform.ini.in	                        (rev 0)
+++ branches/4.0/MgDev/cmake/configs/Platform.ini.in	2026-11-03 13:53:48 UTC (rev 10262)
@@ -0,0 +1,336 @@
+# *****************************************************************************
+# MapGuide Portable Platform Configuration File
+#
+# The following configuration is based on a single CPU with a single core.
+#
+# WARNING: BE VERY CAREFUL WHEN MODIFYING THIS FILE AS IT COULD
+#          ADVERSLY IMPACT APPLICATION PERFORMANCE
+#
+# When saving this file use a UTF-8 encoding.
+#
+# *****************************************************************************
+# COMMON VALIDATION INFORMATION
+#
+# (Unless otherwise noted under the property description)
+# -----------------------------------------------------------------------------
+# String Property                  Range of String Length   Reserved Characters
+# -----------------------------------------------------------------------------
+# File Name                        0 <  Length <= 128       \/:*?"<>|
+# Folder Name                      0 <  Length <= 128       \/:*?"<>|
+# Log Parameters                   0 <= Length <= 1024
+# Password                         0 <= Length <= 64        \t\r\n\v\f
+# Path                             0 <  Length <= 255       *?"<>|
+# -----------------------------------------------------------------------------
+# Numeric Property                 Range of Numeric Value
+# -----------------------------------------------------------------------------
+# Port Number                      0 <= Value <= 65535
+# *****************************************************************************
+
+[GeneralProperties]
+# *****************************************************************************
+# G E N E R A L
+#
+# Property Name                    Description
+# -----------------------------------------------------------------------------
+# DefaultMessageLocale             ISO 639-1 name for the message locale
+#                                       Length = 2
+#                                       Example: en
+# FdoPath                          Path where FDO is installed
+# Locale                           The server's locale. If left blank it will default to the operating system locale
+#                                       0 <= Length <= 255
+#                                       Format           : lang[_country_region.code_page]
+#                                       Example (Windows): English_United States.1252
+#                                       Example Linux)   : en_US.iso88591
+# LogsDelimiter                    Delimiter that separates the data fields in
+#                                  the log files
+# LogsDetail                       Level of logging detail per service
+#                                       0 - Errors without parameters (default)
+#                                       1 - Warnings and Errors with parameters
+#                                       2 - Trace, Warnings, and Errors
+#                                       3 - Internal Trace, Trace, Warnings, and Errors
+#                                       Service keys: ResourceService, DrawingService, FeatureService, MappingService,
+#                                           RenderingService, TileService, KmlService, ServerAdminService, SiteService
+#                                       Example: MappingService:3,FeatureService:1
+# LogsPath                         Path where log files are stored
+# MentorDictionaryPath             Path where the CS-Map cooridnate system dictionaries is installed
+# PreCacheMaps                     The list of maps to precache at server startup.
+#                                       Value = Map resource name(s) separated by ","
+#                                       Example: Library://Samples/Sheboygan/Maps/Sheboygan.MapDefinition
+# Renderer                         Image renderer to use (GD or AGG)
+# ResourcesPath                    Path where the localization resource files are stored
+# TempPath                         Path where the temporary files are stored
+# WfsDocumentPath                  Document path location for WFS documents
+# WmsDocumentPath                  Document path location for WMS documents
+# *****************************************************************************
+DefaultMessageLocale               = en
+FdoPath                            = @FDO_LIBRARY_DIR@
+MentorDictionaryPath               = @MG_INSTALL_COORDSYS_PREFIX_ABS@
+Locale                             =
+LogsDelimiter                      = \t
+LogsDetail                         =
+LogsPath                           = Logs/
+PreCacheMaps					   =
+Renderer                           = AGG
+ResourcesPath                      = Resources/
+TempPath                           = Temp/
+WfsDocumentPath                    = Wfs/
+WmsDocumentPath                    = Wms/
+
+[DrawingServiceProperties]
+# *****************************************************************************
+# D R A W I N G  S E R V I C E
+#
+# Property Name                    Description
+# -----------------------------------------------------------------------------
+# *****************************************************************************
+
+[FeatureServiceProperties]
+# *****************************************************************************
+# F E A T U R E  S E R V I C E
+#
+# Property Name                    Description
+# -----------------------------------------------------------------------------
+# CacheSize                        Max # of internal data objects to cache
+#                                  (schemas, classes, etc...)
+#                                       0 < Value <= 5000
+# CacheTimeLimit                   Time duration in seconds for how long to
+#                                  cache the internal data objects
+#                                       0 < Value <= 2147483647
+# CacheTimerInterval               Time interval in seconds for when the server
+#                                  checks for expired cache entries
+#                                       0 < Value <= 2147483647
+# DataConnectionPoolEnabled        FDO connection pooling
+#                                       0 = disabled, 1 = enabled
+# DataConnectionPoolExcludedProviders  The list of providers to exclude from connection pooling.
+#                                       0 <= Length <= 1024
+#                                       Value = provider name(s) separated by ","
+#                                       Example: OSGeo.SDF,OSGeo.SHP
+# JoinQueryBatchSize               Join query batch size
+#                                       1 < Value <= 10000
+# UseFdoJoinOptimization           Determines whether to use FDO Join APIs for feature joins between  
+#                                  classes from the same Feature Source for FDO providers that support it 
+#                                  1 = Use FDO Join APIs if possible, 0 = Fallback to GwsQueryEngine 
+# *****************************************************************************
+CacheSize                          = 100
+CacheTimeLimit                     = 86400
+CacheTimerInterval                 = 3600
+DataConnectionPoolEnabled          = 1
+DataConnectionPoolExcludedProviders= OSGeo.SDF,OSGeo.SHP,OSGeo.SQLite
+JoinQueryBatchSize                 = 1000
+UseFdoJoinOptimization             = 1
+
+[MappingServiceProperties]
+# *****************************************************************************
+# M A P P I N G  S E R V I C E
+#
+# Property Name                    Description
+# -----------------------------------------------------------------------------
+# LegendFont                       Font to use when rendering legend elements
+#                                       0 < Length <= 255
+# *****************************************************************************
+LegendFont                         = Arial
+
+[RenderingServiceProperties]
+# *****************************************************************************
+# R E N D E R I N G  S E R V I C E
+#
+# Property Name                    Description
+# -----------------------------------------------------------------------------
+# TileExtentOffset                 Max request extent offset to use when
+#                                  requesting features for a tile, specified as
+#                                  a factor of the tile size
+#                                       0.0 <= Value <= 1.0
+# RasterGridSize                   Size of raster re-projection grid in pixels
+#                                       0 < Value <= 2147483647
+# MinRasterGridSize                Minimum size of raster re-projection grid in
+#                                  pixels. This should be less than RasterGridSize.
+#                                       0 < Value <= 2147483647
+# RasterGridSizeOverrideRatio      If the RasterGridSize is larger than the
+#                                  image's height or width multiplied by the
+#                                  RasterGridSizeOverrideRatio, then the
+#                                  RasterGridSize is overridden with this value.
+#                                  Set to 0 or 1 to disable the override.
+#                                       0.0 <= Value <= 1.0
+# RasterGridSizeForPlot            Size of raster re-projection grid in pixels
+#                                  for plot
+# MinRasterGridSizeForPlot         Minimum size of raster re-projection grid in
+#                                  pixels for plot. This must be less than
+#                                  RasterGridSizeForPlot.
+# RasterGridSizeOverrideRatioForPlot If the RasterGridSizeForPlot is larger than the
+#                                  image's height or width multiplied by the
+#                                  RasterGridSizeOverrideRatioForPlot, then the
+#                                  RasterGridSizeForPlot is overridden with this
+#                                  value.
+#                                  Change this setting to 1 to disable the
+#                                  override.
+# RenderSelectionBatchSize         The batch size to use when rendering a selection
+# ClampPoints                      Specifies whether point coordinates are clamped
+#                                  to integer values before passing them to the
+#                                  AGG renderer (experimental)
+#                                       0 = false and 1 = true
+# GeneralizeData                   Specifies whether feature geometry is generalized
+#                                  before being rendered (experimental)
+#                                       0 = false and 1 = true
+# *****************************************************************************
+TileExtentOffset                   = 0.35
+RasterGridSize                     = 100
+MinRasterGridSize                  = 10
+RasterGridSizeOverrideRatio        = 0.25
+RasterGridSizeForPlot              = 50
+MinRasterGridSizeForPlot           = 5
+RasterGridSizeOverrideRatioForPlot = 0.10
+RenderSelectionBatchSize           = 50000
+ClampPoints                        = 0
+GeneralizeData                     = 0
+
+[ResourceServiceProperties]
+# *****************************************************************************
+# R E S O U R C E  S E R V I C E
+#
+# Property Name                    Description
+# -----------------------------------------------------------------------------
+# LibraryRepositoryPath            Path where the Library repository is stored
+# LibraryResourceDataFilePath      Path where the Library resource data files
+#                                  are stored
+# PackagesPath                     Path where the resource packages can be found
+# ResourceDataFileTrashFolderName  Folder where the repository trash files are
+#                                  stored
+# ResourceSchemaFilePath           Path where the resource schema files are
+#                                  stored
+# SessionRepositoryPath            Path where the Session repository is stored
+# SessionResourceDataFilePath      Path where the Session resource data files
+#                                  are stored
+# SiteRepositoryPath               Path where the Site repository is stored
+# *****************************************************************************
+LibraryRepositoryPath              = Repositories/Library/Content/
+LibraryResourceDataFilePath        = Repositories/Library/DataFiles/
+PackagesPath                       = Packages/
+ResourceDataFileTrashFolderName    = Trash
+ResourceSchemaFilePath             = Schema/
+SessionRepositoryPath              = Repositories/Session/Content/
+SessionResourceDataFilePath        = Repositories/Session/DataFiles/
+SiteRepositoryPath                 = Repositories/Site/
+
+[TileServiceProperties]
+# *****************************************************************************
+# T I L E  S E R V I C E
+#
+# Property Name                    Description
+# -----------------------------------------------------------------------------
+# RenderOnly                       Renders the tile only
+#                                       0 = false and 1 = true
+# TileCachePath                    Root path of the image tile cache
+# TileColumnsPerFolder             Number of columns of tiles per folder
+#                                       0 < Value <= 1000
+# TileRowsPerFolder                Number of rows of tiles per folder
+#                                       0 < Value <= 1000
+# DefaultTileSizeX                 Width of generated tiles in pixels
+#                                       50 < value <= 10000
+# DefaultTileSizeY                 Height of generated tiles in pixels
+#                                       50 < value <= 10000
+# ImageFormat                      Image format for generated tiles
+#                                       PNG, PNG8, GIF or JPG
+# *****************************************************************************
+RenderOnly                         = 0
+TileCachePath                      = Repositories/TileCache/
+TileColumnsPerFolder               = 30
+TileRowsPerFolder                  = 30
+DefaultTileSizeX                   = 300
+DefaultTileSizeY                   = 300
+ImageFormat                        = PNG
+
+[AccessLogProperties]
+# *****************************************************************************
+# A C C E S S  L O G
+#
+# Property Name                    Description
+# -----------------------------------------------------------------------------
+# Enabled                          0 = log disabled, 1 = log enabled
+# Filename                         Name of the log file
+# Parameters                       Log parameters
+# *****************************************************************************
+Enabled                            = 1
+Filename                           = "Access.log"
+Parameters                         = CLIENT,CLIENTIP,USER,OPID
+
+[AdminLogProperties]
+# *****************************************************************************
+# A D M I N  L O G (Not used by mg-portable)
+#
+# Property Name                    Description
+# -----------------------------------------------------------------------------
+# Enabled                          0 = log disabled, 1 = log enabled
+# Filename                         Name of the log file
+# Parameters                       Log parameters
+# *****************************************************************************
+Enabled                            = 1
+Filename                           = "Admin.log"
+Parameters                         = CLIENT,CLIENTIP,USER,OPID
+
+[AuthenticationLogProperties]
+# *****************************************************************************
+# A U T H E N T I C A T I O N  L O G (Not used by mg-portable)
+#
+# Property Name                    Description
+# -----------------------------------------------------------------------------
+# Enabled                          0 = log disabled, 1 = log enabled
+# Filename                         Name of the log file
+# Parameters                       Log parameters
+# *****************************************************************************
+Enabled                            = 1
+Filename                           = "Authentication.log"
+Parameters                         = CLIENT,CLIENTIP,USER
+
+[ErrorLogProperties]
+# *****************************************************************************
+# E R R O R  L O G
+#
+# Property Name                    Description
+# -----------------------------------------------------------------------------
+# Enabled                          0 = log disabled, 1 = log enabled
+# Filename                         Name of the log file
+# Parameters                       Log parameters
+# *****************************************************************************
+Enabled                            = 1
+Filename                           = "Error.log"
+Parameters                         = CLIENT,CLIENTIP,USER,ERROR,STACKTRACE
+
+[TraceLogProperties]
+# *****************************************************************************
+# T R A C E  L O G
+#
+# Property Name                    Description
+# -----------------------------------------------------------------------------
+# Enabled                          0 = log disabled, 1 = log enabled
+# Filename                         Name of the log file
+# Parameters                       Log parameters
+# *****************************************************************************
+Enabled                            = 0
+Filename                           = "Trace.log"
+Parameters                         = INFO
+
+[FontAliases]
+# *****************************************************************************
+# F O N T  A L I A S E S
+# This section is used to map a font family name to another.  The left side
+# is a name that might be specified by the user and the right side is what
+# font to actually use.
+# *****************************************************************************
+MS ゴシック = MS Gothic
+MS Pゴシック = MS PGothic
+MS 明朝 = MS Mincho
+MS P明朝 = MS PMincho
+굴림 = Gulim
+굴림체 = GulimChe
+돋움 = Dotum
+돋움체 = DotumChe
+바탕 = Batang
+바탕체 = BatangChe
+궁서 = Gungsuh
+궁서체 = GungsuhChe
+
+[UnmanagedDataMappings]
+# *****************************************************************************
+# U N M A N A G E D  D A T A  M A P P I N G S
+# This section is used to map a mapping name to an unmanaged data folder.
+# *****************************************************************************
\ No newline at end of file

Modified: branches/4.0/MgDev/cmake/modules/FindMapGuideThirdparty.cmake
===================================================================
--- branches/4.0/MgDev/cmake/modules/FindMapGuideThirdparty.cmake	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/cmake/modules/FindMapGuideThirdparty.cmake	2026-11-03 13:53:48 UTC (rev 10262)
@@ -77,7 +77,7 @@
     find_package(XercesC REQUIRED)
 endif (INTERNAL_XERCES)
 
-if (NOT MG_COMMON_SUBSET_ONLY)
+if (NOT MG_COMMON_SUBSET_ONLY OR WITH_PORTABLE)
 
     if (INTERNAL_GD)
         message(STATUS "Using internal GD")
@@ -155,7 +155,7 @@
         find_package(PNG REQUIRED)
     endif (INTERNAL_PNG)
 
-endif (NOT MG_COMMON_SUBSET_ONLY)
+endif (NOT MG_COMMON_SUBSET_ONLY OR WITH_PORTABLE)
 
 if (INTERNAL_JSON)
     message(STATUS "Using internal jsoncpp")
@@ -199,4 +199,10 @@
 
 endif (NOT MG_COMMON_SUBSET_ONLY)
 
+# MgPortable and the GWS libraries a portable common-subset build adds need FDO. The rest of the
+# subset does not, so only look for it when portable is on.
+if (MG_COMMON_SUBSET_ONLY AND WITH_PORTABLE)
+    find_package(FDO REQUIRED)
+endif (MG_COMMON_SUBSET_ONLY AND WITH_PORTABLE)
+
 find_package(TCLAP REQUIRED)
\ No newline at end of file

Modified: branches/4.0/MgDev/cmake_bootstrap.sh
===================================================================
--- branches/4.0/MgDev/cmake_bootstrap.sh	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/cmake_bootstrap.sh	2026-11-03 13:53:48 UTC (rev 10262)
@@ -10,6 +10,7 @@
 INTERNAL_JSON=0
 INTERNAL_GEOS=0
 COMMON_SUBSET_ONLY=0
+WITH_PORTABLE=0
 BUILD_CPU=32
 BUILD_CONFIG=Release
 HAVE_SYSTEM_XERCES=0
@@ -31,6 +32,11 @@
         --common-subset-only)
             COMMON_SUBSET_ONLY=1
             ;;
+        --with-portable)
+            # MgPortable needs the renderers/stylization libraries even in a common-subset
+            # tree, so the graphics thirdparty libs have to be built there too.
+            WITH_PORTABLE=1
+            ;;
         --with-all-internal)
             INTERNAL_ACE=1
             INTERNAL_ZLIB=1
@@ -91,6 +97,8 @@
             echo "  --with-internal-json [Build/Use internal JsonCpp]"
             echo "  --with-internal-geos [Build/Use internal GEOS]"
             echo "  --with-all-internal [Build/Use all internal libs]"
+            echo "  --common-subset-only [Only prepare the common libs subset]"
+            echo "  --with-portable [Also build the graphics libs a portable subset needs]"
             echo "  --with-ccache [Use ccache]"
             exit
             ;;
@@ -197,7 +205,13 @@
     check_build
 fi
 mkdir -p "${OEM_WORK_DIR}/gd"
-if [ "$COMMON_SUBSET_ONLY" != "1" ] && [ "$INTERNAL_ZLIB" = "1" ]; then
+# Renderers/Stylization are part of a portable common-subset build, and they need the graphics
+# thirdparty libraries, so build them even when the rest of the subset does not use them.
+BUILD_GRAPHICS_LIBS=0
+if [ "$COMMON_SUBSET_ONLY" != "1" ] || [ "$WITH_PORTABLE" = "1" ]; then
+    BUILD_GRAPHICS_LIBS=1
+fi
+if [ "$BUILD_GRAPHICS_LIBS" = "1" ] && [ "$INTERNAL_ZLIB" = "1" ]; then
     echo "Building zlib (internal)"
     cp -Rf "${SOURCE_DIR}/Oem/gd/zlib" "${OEM_WORK_DIR}/gd/zlib"
     cd "${OEM_WORK_DIR}/gd/zlib" || exit
@@ -211,7 +225,7 @@
     make && make install
     check_build
 fi
-if [ "$COMMON_SUBSET_ONLY" != "1" ] && [ "$INTERNAL_LIBPNG" = "1" ]; then
+if [ "$BUILD_GRAPHICS_LIBS" = "1" ] && [ "$INTERNAL_LIBPNG" = "1" ]; then
     echo "Building libpng (internal)"
     cp -Rf "${SOURCE_DIR}/Oem/gd/lpng" "${OEM_WORK_DIR}/gd/lpng"
     cd "${OEM_WORK_DIR}/gd/lpng" || exit
@@ -230,7 +244,7 @@
         check_build
     fi
 fi
-if [ "$COMMON_SUBSET_ONLY" != "1" ] && [ "$INTERNAL_JPEG" = "1" ]; then
+if [ "$BUILD_GRAPHICS_LIBS" = "1" ] && [ "$INTERNAL_JPEG" = "1" ]; then
     echo "Building libjpeg (internal)"
     cp -Rf "${SOURCE_DIR}/Oem/gd/jpeg" "${OEM_WORK_DIR}/gd/jpeg"
     cd "${OEM_WORK_DIR}/gd/jpeg" || exit
@@ -255,7 +269,7 @@
         check_build
     fi
 fi
-if [ "$COMMON_SUBSET_ONLY" != "1" ] && [ "$INTERNAL_FREETYPE" = "1" ]; then
+if [ "$BUILD_GRAPHICS_LIBS" = "1" ] && [ "$INTERNAL_FREETYPE" = "1" ]; then
     echo "Building freetype (internal)"
     cp -Rf "${SOURCE_DIR}/Oem/gd/freetype" "${OEM_WORK_DIR}/gd/freetype"
     cd "${OEM_WORK_DIR}/gd/freetype" || exit
@@ -286,7 +300,7 @@
     PHP_ZLIB_DIR="$OEM_WORK_DIR/install/zlib"
 fi
 
-if [ "$COMMON_SUBSET_ONLY" != "1" ] && [ "$INTERNAL_GD" = "1" ]; then
+if [ "$BUILD_GRAPHICS_LIBS" = "1" ] && [ "$INTERNAL_GD" = "1" ]; then
     echo "Building GD (internal)"
     cp -Rf "${SOURCE_DIR}/Oem/gd/gd" "${OEM_WORK_DIR}/gd/gd"
     cd "${OEM_WORK_DIR}/gd/gd" || exit

Modified: branches/4.0/MgDev/cmake_build.sh
===================================================================
--- branches/4.0/MgDev/cmake_build.sh	2026-11-03 12:22:56 UTC (rev 10261)
+++ branches/4.0/MgDev/cmake_build.sh	2026-11-03 13:53:48 UTC (rev 10262)
@@ -60,6 +60,8 @@
             echo "  --oem-working-dir [OEM build directory, default: ~/mapguide_oem_build]"
             echo "  --with-asan [build with ASAN (if available)]"
             echo "  --with-gold [link with gold instead of ld (if available)]"
+            echo "  --common-subset-only [Configure the common libs subset]"
+            echo "  --with-portable [Build MgPortable and its SWIG glue]"
             echo "  --ninja [Use ninja build system]"
             echo "  --help [Display usage]"
             exit



More information about the mapguide-commits mailing list