1. La historia corta, para el alero que viene con prisa
Mirá pues: la KB de ZelvaERP/ZelvaPOS andaba pesadísima. Cada cambio en GeneXus GX18 se sentía como ir en buseta en hora pico: abrir, guardar, especificar, generar y compilar era un sufrimiento.
Al inicio sospechamos de todo:
- El tamaño de la KB.
- El historial acumulado de 9 años.
- SQL Server.
- La máquina local.
- Visual Studio.
- IIS.
- K2BTools.
- GAM.
- El universo mismo conspirando contra el build.
Pero el rollo principal que logramos mejorar estaba en la etapa de compilación .NET Framework.
GeneXus estaba generando proyectos .csproj así:
<Project ToolsVersion="14.0">
pero al momento de compilar estaba usando este MSBuild:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe
Ese MSBuild no tenía claro qué hacer con ToolsVersion="14.0", entonces tiraba el clásico drama:
Project file contains ToolsVersion="14.0".
This toolset may be unknown or missing...
Treating the project as if it had ToolsVersion="4.0".
Y eso se repetía para un montón de proyectos. Resultado: más ruido, más tiempo, más desesperación.
La solución que quedó funcionando fue configurar MSBuild options así:
/v:minimal /m:6 /ToolsVersion:4.0 /p:GxExternalReference=GeneXus.Security.API.Common.dll
Con eso le dijimos a MSBuild:
“Dejá de hacerte el interesante con el 14.0. Usá 4.0 de una vez y compilemos esta vaina.”
Y sí: mejoró.
2. El ambiente donde pasó el relajo
La KB afectada fue ZelvaERP/ZelvaPOS, una KB grande y con varios años de historia.
Stack involucrado
- GeneXus GX18.
- Generador: .NET Framework.
- Build Mode: MSBuild.
- Seguridad integrada / GAM.
- K2BTools / K2B Patterns.
- IIS local.
- GeneXus Server.
- SQL Server como base interna de la KB.
Tamaño observado de la KB local
Los archivos SQL internos de la KB andaban más o menos así:
GX_KB_ZelvaERP.mdf ≈ 5.7 GB
GX_KB_ZelvaERP_log.ldf ≈ 1.0 GB
Eso no significa automáticamente que la KB esté dañada, pero sí nos dice que estamos hablando de una KB grandecita, con historia, con bastante carga y con suficiente peso como para no tratarla como una KB de ejemplo de capacitación.
3. Los síntomas: cuando el Build se puso en modo tortuga
Los síntomas eran bien claros:
- Guardar cambios tardaba más de lo normal.
- Especificar y generar se sentía pesado.
- La compilación .NET Framework se quedaba pegada bastante tiempo.
- En el Output de GeneXus aparecía una línea como esta:
"C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" /nologo /p:Configuration=Release /p:FrameworkPath="C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319" /v:q /m /p:GxExternalReference=GeneXus.Security.API.Common.dll "D:\Informacion\Proyectos\Zelva_ERP_GX18\Local\build\LastBuild.sln"
Cuando GeneXus muestra eso, ya no estamos hablando de especificación como tal. Ahí GeneXus ya generó y le está diciendo a MSBuild:
“Tomá esta solución
LastBuild.slny compilámela.”
El clavo estaba bastante metido en esa fase.
4. Primera pista: /v:q nos tenía viendo una película sin subtítulos
La configuración inicial usaba algo como:
/v:q
Ese parámetro hace que MSBuild sea súper callado. Muy elegante, sí, pero para diagnosticar es como revisar un carro con los ojos vendados.
Entonces cambiamos temporalmente a:
/v:minimal
Y ahí apareció el mensaje que nos abrió los ojos:
Project file contains ToolsVersion="14.0".
This toolset may be unknown or missing, in which case you may be able to resolve this by installing the appropriate version of MSBuild, or the build may have been forced to a particular ToolsVersion for policy reasons. Treating the project as if it had ToolsVersion="4.0".
Ese warning se repetía varias veces. Demasiadas. Y en una KB grande, warning repetido por cada proyecto es como gotera en invierno: al rato ya tenés inundada la sala.
5. Revisando si había MSBuild moderno instalado
Se probó en PowerShell/CMD:
where msbuild
Resultado:
No devolvió nada.
También se buscaron rutas típicas:
C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe
C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\MSBuild.exe
C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin\MSBuild.exe
C:\Program Files\Microsoft Visual Studio\2022\BuildTools\MSBuild\Current\Bin\MSBuild.exe
En ese momento no existían.
GeneXus estaba usando el viejo confiable:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe
Viejo confiable, sí. Pero viejo al fin.
6. El archivo .csproj cantó como canario
Abrimos un proyecto generado por GeneXus, por ejemplo:
D:\Informacion\Proyectos\Zelva_ERP_GX18\Local\build\erp_proveedores.wpcreardocumentocompra\erp_proveedores.wpcreardocumentocompra.csproj
Y encontramos esto:
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
Ahí estaba el meollo:
- GeneXus genera
.csprojconToolsVersion="14.0". - Pero GeneXus compila con MSBuild de
v4.0.30319. - Ese MSBuild no resuelve bien el toolset 14.0.
- Entonces cae a 4.0, pero después de quejarse por cada proyecto.
O sea, ya estaba usando 4.0 en la práctica, pero haciendo show antes.
7. ¿Qué rayos es ToolsVersion?
Aquí viene la parte importante para que no nos confundamos.
ToolsVersion no es la versión de .NET Framework de la aplicación.
ToolsVersion le dice a MSBuild qué conjunto de herramientas usar para compilar: archivos .targets, tareas de build, propiedades y rutas relacionadas con compiladores.
Ejemplo:
<Project ToolsVersion="14.0">
Eso significa:
“Este proyecto fue generado pensando en el toolset MSBuild 14.0.”
Históricamente:
| ToolsVersion | Asociado a |
|---|---|
4.0 | Visual Studio 2010 / 2012 |
12.0 | Visual Studio 2013 |
14.0 | Visual Studio 2015 |
15.0 | Visual Studio 2017 |
Pero ojo: una cosa es el toolset y otra cosa es el framework objetivo.
8. ToolsVersion no es TargetFrameworkVersion
Este punto es clave, porque si no, uno se asusta pensando que está degradando toda la aplicación.
| Concepto | Qué controla |
|---|---|
ToolsVersion | Qué toolset de MSBuild se usa para compilar |
TargetFrameworkVersion | Contra qué versión de .NET Framework compila la aplicación |
Compiler Path | Ruta del csc.exe configurado en GeneXus |
Build Mode = MSBuild | GeneXus compila usando MSBuild en vez de CSC directo |
Entonces, poner esto:
/ToolsVersion:4.0
no significa automáticamente:
“La aplicación ahora es .NET Framework 4.0 y nos fuimos para atrás como Windows XP.”
No. Significa que MSBuild va a procesar los proyectos usando el toolset 4.0.
Y en nuestro caso eso ya estaba pasando de todas formas, solo que con advertencias repetidas.
9. El intento con MSBuild moderno: buena idea, pero se metió otro duende
Se instaló Visual Studio/Build Tools y se probó ejecutar MSBuild moderno manualmente.
En PowerShell se intentó primero así:
"C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe" /nologo /p:Configuration=Release /v:minimal /m /p:GxExternalReference=GeneXus.Security.API.Common.dll "D:\Informacion\Proyectos\Zelva_ERP_GX18\Local\build\LastBuild.sln"
PowerShell se puso especial, porque cuando la ruta va entre comillas hay que usar &.
Forma correcta:
& "C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe" /nologo /p:Configuration=Release /v:minimal /m /p:GxExternalReference=GeneXus.Security.API.Common.dll "D:\Informacion\Proyectos\Zelva_ERP_GX18\Local\build\LastBuild.sln"
Pero luego salió este error:
error MSB3644: The reference assemblies for .NETFramework,Version=v4.0 were not found.
To resolve this, install the Developer Pack (SDK/Targeting Pack) for this framework version or retarget your application.
Eso nos dijo:
- El MSBuild moderno sí arrancó.
- Pero no encontró las Reference Assemblies / Targeting Pack necesarias para el framework objetivo de esos proyectos.
- Por tanto, migrar a MSBuild moderno no era solo cambiar una ruta y listo.
Ese camino requiere revisar:
- Target Framework.
- Developer Packs.
- IIS.
- GAM.
- K2BTools.
- DLLs externas.
- Compatibilidad general de la KB.
Como estábamos apagando un incendio de rendimiento, no nos fuimos por esa avenida todavía.
10. El Compiler Path no era el villano
En GeneXus vimos algo como:
Build Mode = MSBuild
Compiler Path = C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\csc.exe
A primera vista uno puede pensar:
“Ajá, aquí está el clavo. Dice MSBuild pero apunta a csc.exe.”
Pero no. Eso no es necesariamente problema.
Cuando Build Mode = MSBuild, GeneXus compila usando MSBuild. La propiedad Compiler Path puede seguir apuntando al csc.exe de .NET Framework.
Para .NET Framework 4.x, esta ruta es normal:
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\csc.exe
Aunque se instale .NET Framework 4.7.2 o 4.8, normalmente no aparece una carpeta nueva como:
C:\Windows\Microsoft.NET\Framework\v4.8
La rama 4.x vive normalmente bajo:
v4.0.30319
Los Developer Packs / Targeting Packs suelen aparecer más bien en:
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8
Así que no tocamos Compiler Path.
11. La solución aplicada: menos drama, más build
Se cambió la propiedad MSBuild options del generador .NET Framework.
Antes
En algún momento andaba así:
/v:q /m /p:GxExternalReference=GeneXus.Security.API.Common.dll
Luego para diagnosticar:
/v:minimal /m /p:GxExternalReference=GeneXus.Security.API.Common.dll
Después
Configuración final recomendada:
/v:minimal /m:6 /ToolsVersion:4.0 /p:GxExternalReference=GeneXus.Security.API.Common.dll
Qué hace cada cosa
| Parámetro | Qué hace |
|---|---|
/v:minimal | Muestra información útil sin convertir el Output en una novela. |
/m:6 | Permite compilar en paralelo usando hasta 6 procesos/proyectos. |
/ToolsVersion:4.0 | Fuerza a MSBuild a usar toolset 4.0 desde el inicio. |
/p:GxExternalReference=GeneXus.Security.API.Common.dll | Referencia usada por GeneXus/GAM/seguridad. Ya venía en la configuración. |
La joya de la corona fue:
/ToolsVersion:4.0
Ese parámetro eliminó el warning repetitivo del ToolsVersion="14.0".
12. Resultados de las pruebas
Pruebas con /m:4
Configuración:
/v:minimal /m:4 /ToolsVersion:4.0 /p:GxExternalReference=GeneXus.Security.API.Common.dll
| Prueba | Tiempo |
|---|---|
Build With This Only de PPOSfac004Ferre | 53 segundos |
Build With This Only de objeto K2B / WorkWith POScot001 | 93 segundos |
| Build All sin cambios relevantes | 83 segundos |
Pruebas con /m:6
Configuración:
/v:minimal /m:6 /ToolsVersion:4.0 /p:GxExternalReference=GeneXus.Security.API.Common.dll
| Prueba | Tiempo |
|---|---|
Build With This Only de PPOSfac004Ferre | 57 segundos |
Build With This Only de objeto K2B / WorkWith POScot001 | 65 segundos |
| Build All sin cambios relevantes | 74 segundos |
Lectura catracha de los resultados
- Para un Procedure pequeño,
/m:4fue un poquito mejor. - Para K2B / WorkWith,
/m:6le metió turbo. - Para Build All sin cambios,
/m:6también ganó.
Conclusión: para el trabajo real de esta KB, queda mejor:
/m:6
13. ¿Por qué un Procedure simple todavía tarda 50 y pico segundos?
Porque aunque el Procedure sea chiquito, GeneXus hace su ritual completo:
Integrated Security Initialization
GAM Applications Registration
Copy Module 'GeneXus'
Copy Module 'GeneXusSecurityCommon'
Copy Module 'GeneXusSecurity'
Copy Module 'GeneXusReporting'
Copy Module 'GeneXusUnanimo'
Target Environment update
Building changed objects list
Compressing static files
DeveloperMenu Compilation
Ese es el costo fijo del ecosistema.
No todo ese tiempo es el Procedure. Es GeneXus preparando el ambiente, verificando seguridad, copiando módulos, generando archivos base y compilando.
14. ¿Por qué un objeto K2B tarda más?
Porque K2B no llega solo. Llega con la familia entera.
En una prueba de POScot001, GeneXus terminó especificando/generando cosas como:
ReportWWPOScot001
POScot001General
EntityManagerPOScot001
WWPOScot001
ExportWWPOScot001
Además se generaron .cs, .js, permisos GAM y varias DLLs.
Entonces, cuando alguien dice:
“Solo cambié una cosita en el WorkWith.”
GeneXus responde:
“Sí, pero esa cosita mueve medio barrio.”
15. Build All sin objetos para especificar igual hace trabajo
En una prueba apareció:
No objects to Specify
Pero eso no significa “no hago nada”.
GeneXus igual puede hacer:
Pattern generation
Integrated Security Initialization
GAM Applications Registration
Copy Modules
Target Environment update
Generating Resources
Generation de archivos base
Compressing static files
DeveloperMenu Compilation
Y en una KB grande puede compilar muchísimas DLLs aunque no haya objetos nuevos para especificar.
Por eso un Build All de 74 segundos, en esta KB, ya no se ve tan loco.
16. Riesgos de usar /ToolsVersion:4.0
El riesgo teórico es este:
Que algún
.csprojrealmente necesite características específicas del toolset 14.0.
Pero en esta KB el riesgo se considera bajo porque antes de forzar /ToolsVersion:4.0, MSBuild ya estaba cayendo automáticamente a 4.0 y compilaba bien.
O sea, antes el flujo era:
.csproj pide 14.0
MSBuild viejo no encuentra 14.0
MSBuild se queja
MSBuild usa 4.0
Compila
Ahora es:
MSBuild usa 4.0 desde el inicio
Compila
Menos teatro, mismo resultado.
17. Qué no cambia esta solución
Esta solución no cambia:
- Objetos GeneXus.
- Código fuente de la KB.
- Target Framework de la aplicación.
- Base de datos de la aplicación.
- Base SQL interna de la KB.
- IIS.
- GAM.
- K2BTools.
- DLLs externas.
Tampoco edita manualmente archivos generados.
Eso es importante: no estamos metiendo mano quirúrgica en archivos que GeneXus después va a regenerar.
18. Qué NO hacer, por amor al build
No se recomienda:
- Editar manualmente los
.csprojgenerados por GeneXus. - Editar este archivo:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe.config
- Cambiar el
Compiler Pathsin una prueba controlada. - Cambiar Target Framework a lo loco.
- Borrar tablas internas de la KB en SQL Server.
- Hacer shrink agresivo del MDF/LDF como si fuera mantenimiento mágico.
- Desinstalar Visual Studio o IIS en modo desesperación sin revisar primero.
19. Cómo aplicar la solución en otra máquina del equipo
- Abrir GeneXus.
- Abrir la KB correspondiente.
- Ir a las propiedades del Environment / Generator .NET Framework.
- Confirmar:
Build Mode = MSBuild
- Buscar la propiedad:
MSBuild options
- Configurar:
/v:minimal /m:6 /ToolsVersion:4.0 /p:GxExternalReference=GeneXus.Security.API.Common.dll
- Guardar.
- Probar con un Procedure simple.
- Probar con un objeto K2B / WorkWith.
- Probar Build All sin cambios.
- Comparar tiempos.
20. Rollback: cómo volver atrás si algo se pone raro
Si en una máquina algo falla, volver al valor anterior de MSBuild options.
Ejemplo:
/v:minimal /m /p:GxExternalReference=GeneXus.Security.API.Common.dll
o si estaba bien silencioso:
/v:q /m /p:GxExternalReference=GeneXus.Security.API.Common.dll
Luego ejecutar Build With This Only y validar que compile.
21. Recomendaciones pendientes para seguir afinando la nave
21.1. Crear KB nueva desde GeneXus Server
Como la KB tiene varios años y un MDF grande, se recomienda crear una KB nueva desde GeneXus Server trayendo solo:
Trunk Version
La idea es comparar:
- Tamaño del MDF/LDF nuevo.
- Tiempo de apertura.
- Tiempo al guardar objetos.
- Build With This Only.
- Build All.
Pruebas sugeridas:
1. Build With This Only de PPOSfac004Ferre
2. Build With This Only de POScot001
3. Build All sin cambios
Si la KB nueva baja fuerte los tiempos, parte del problema restante está en la historia acumulada de la KB local vieja.
21.2. Revisar SQL Server de la KB
Pendientes recomendados:
- Revisar tamaño MDF.
- Revisar tamaño LDF.
- Revisar fragmentación de índices.
- Revisar memoria asignada a SQL Server.
- Confirmar si se usa SQL Server Express.
- Verificar que la KB esté en SSD local.
Nada de borrar tablas internas de GeneXus. Eso es jugar ruleta rusa con la KB.
21.3. Revisar antivirus
Evaluar exclusiones controladas para:
D:\Informacion\Proyectos\Zelva_ERP_GX18\Local\
D:\Informacion\Proyectos\Zelva_ERP_GX18\Local\build\
D:\Informacion\Proyectos\Zelva_ERP_GX18\Local\web\
D:\Informacion\Proyectos\Zelva_ERP_GX18\Local\web\bin\
Solo hacerlo si las políticas internas de seguridad lo permiten.
21.4. Evitar carpetas sincronizadas
No poner KBs grandes en:
OneDrive
Dropbox
Google Drive
Carpetas de red lentas
Para este tipo de KB: SSD local, ruta decente y sin sincronizadores metiendo mano.
22. Referencias útiles
- GeneXus – Build Mode property for .NET generator
https://docs.genexus.com/en/wiki?3936,Build+Mode+property+for+.NET+generator - GeneXus – MSBuild options property
https://docs.genexus.com/en/wiki?44168,MSBuild+options+property - GeneXus – Compile using MSBuild instead of CSC
https://docs.genexus.com/en/wiki?42661,Compile+using+MSBuild+instead+of+CSC - GeneXus – Csharp Compiler Path property
https://docs.genexus.com/en/wiki?9013,Csharp+Compiler+Path+property - GeneXus – Basic tips about Managing KBs using SQL Server
https://docs.genexus.com/en/wiki?6543,Basic+tips+about+Managing+KBs+using+SQL+Server - GeneXus – Create Knowledge Base from GeneXus Server
https://docs.genexus.com/en/wiki?22416,Create+Knowledge+Base+from+GeneXus+Server - Microsoft – MSBuild Toolset / ToolsVersion
https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-toolset-toolsversion - Microsoft – MSB3644: Reference assemblies not found
https://learn.microsoft.com/en-us/visualstudio/msbuild/errors/msb3644
23. Conclusión: le quitamos el freno de mano al build
El problema principal no era que GeneXus no pudiera compilar. Compilaba. El problema era que estaba compilando con un MSBuild viejo, leyendo proyectos generados con ToolsVersion="14.0", cayendo a ToolsVersion="4.0" y dejando un montón de warnings en el camino.
La solución fue decirle de frente:
/ToolsVersion:4.0
Y ya que estábamos afinando, dejamos:
/v:minimal /m:6 /ToolsVersion:4.0 /p:GxExternalReference=GeneXus.Security.API.Common.dll
Resultado:
- Menos warnings.
- Menos ruido.
- Menor tiempo de compilación.
- Builds incrementales más decentes.
- KB más soportable para el trabajo diario.
No resolvimos todos los males del universo GeneXus, pero sí le bajamos bastante al relajo del build. Y

Average Rating