diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml index e7279a8c13..2eaee85f08 100644 --- a/.github/workflows/msvc.yml +++ b/.github/workflows/msvc.yml @@ -60,7 +60,7 @@ jobs: PythonPath: ${{ steps.setup-python.outputs.python-path }} run: | Write-Output $env:PythonPath - ./develop.ps1 -release -qtVersion $env:qtVersion -antlrVersion $env:antlrVersion -pythonPath $env:PythonPath + ./develop.ps1 -release -qtVersion $env:qtVersion -antlrVersion $env:antlrVersion -pythonPath $env:PythonPath -setSystemEnvVars -generator "Ninja" - name: 'Configure and build with CMake' shell: bash diff --git a/.gitignore b/.gitignore index 8bc5d027bb..caa3c874a4 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,9 @@ changes *.tokens .gdb_history *.DS_Store +dependencies +msvc-env +aqtinstall.log # Example files *.beat @@ -60,3 +63,6 @@ tests/data/dissolve/input/TestOutput_* # Visual Studio .vscode/ .vs/ + +# CMake +CMakeUserPresets.json \ No newline at end of file diff --git a/develop.ps1 b/develop.ps1 index 8e4d9c04a6..db04d58023 100644 --- a/develop.ps1 +++ b/develop.ps1 @@ -16,22 +16,40 @@ These packages are installed into a folder called 'dependencies'. .PARAMETER qtVersion Qt version to install. Defaults to existing system Qt6 installation if none specified. + .PARAMETER systemQt + Path to existing installation of Qt6. .PARAMETER pythonPath Path to a Python executable. .PARAMETER forcePythonVersion Force installation of a given Python version. .PARAMETER antlrVersion ANTLR version to install. Defaults to ANTLR 4.13.1. + .PARAMETER msvcVersion + Version of MSVC to use. + .PARAMETER generator + Generator to use (options are "Visual Studio 17 2022", "Ninja"). + .PARAMETER setSystemEnvVars + Flag - set environment variables and PATH for dependencies at the system level, otherwise set in CMake presets "environment" property. .PARAMETER release Flag - install packages for release, otherwise debug. + .PARAMETER clean + Flag - remove existing setup folders and files before running script. Invoking this flag deletes the following: + - Dissolve installation folders ("/out", "/build") + - dependencies folder + - CMakeUserPresets.json #> param ( [string]$qtVersion, + [string]$systemQt, [string]$pythonPath, [string]$forcePythonVersion, + [string]$msvcVersion, + [string]$generator = "Visual Studio 17 2022", [string]$antlrVersion = "4.13.1", - [switch]$release = $false + [switch]$setSystemEnvVars = $false, + [switch]$release = $false, + [switch]$clean = $false ) $build = "Debug" @@ -58,6 +76,37 @@ $threading = [bool]::Parse('True') $dependencies = "dependencies" New-Item -ItemType Directory -Path $dependencies -ErrorAction SilentlyContinue +function Find-And-Remove { + <# + .SYNOPSIS + Remove Dissolve environment object, and recursively remove contents, if found in Dissolve project directory. + .DESCRIPTION + Deletes specified folder and contents, or file, if it exists. Path is relative to the Dissolve project directory. + .PARAMETER relativePath + Relative path to object for deletion. + #> + param ( + [string]$relativePath = "" + ) + + if (Test-Path -Path $relativePath) + { + Write-Host "Existing instance of object $relativePath found, cleaning up... " @info_colors + Remove-Item $relativePath -Recurse -Force + } + else + { + Write-Host "Existing instance of object $relativePath NOT found, could not clean up." @warn_colors + } +} + +# Clean existing environment +Find-And-Remove -relativePath "out" +Find-And-Remove -relativePath "build" +Find-And-Remove -relativePath "dependencies" +Find-And-Remove -relativePath "CMakeUserPresets.json" +Find-And-Remove -relativePath "msvc-env" + #Install key dependencies with Chocolatey [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) @@ -68,6 +117,13 @@ Write-Host "Installing key dependencies with Chocolatey... " @info_colors choco install -y ninja pkgconfiglite choco install -y cmake --version=3.30.1 --force +# Ensure CMake version is 3.30.1 +$cmakeVersion = "$(cmake --version)" +if (-not ($cmakeVersion -like "*3.30.1*")) +{ + choco install -y cmake.install --version=3.30.1 --force --installargs "ADD_CMAKE_TO_PATH=User" +} + # Find git, install if not found try { & "git" --version @@ -137,16 +193,21 @@ Write-Host "Installing Python packages... " @info_colors & $python -m pip install --upgrade pip & $python -m pip install aqtinstall conan==1.* -$systemPath = [Environment]::GetEnvironmentVariable("PATH", [EnvironmentVariableTarget]::Machine) +$pythonEnvPath = Join-Path -Path $projectDir -ChildPath "msvc-env\$pythonEnvSourceDir" + +if ($setSystemEnvVars) +{ + $systemPath = [Environment]::GetEnvironmentVariable("PATH", [EnvironmentVariableTarget]::Machine) -[Environment]::SetEnvironmentVariable("PATH", "$(Join-Path -Path $projectDir -ChildPath "msvc-env\$pythonEnvSourceDir");$systemPath", [EnvironmentVariableTarget]::Machine) -Write-Host "Python packages directory path added to system PATH." @info_colors + [Environment]::SetEnvironmentVariable("PATH", "$pythonEnvPath;$systemPath", [EnvironmentVariableTarget]::Machine) + Write-Host "Python packages directory path added to system PATH." @info_colors +} +# Install Qt6, or find existing system Qt6 installation $qt6CMakeDir = "" if (-not [string]::IsNullOrEmpty($qtVersion)) { - # Install Qt6 $qtInstallationDir = Join-Path -Path $dependencies -ChildPath "qt" New-Item -ItemType Directory -Path $qtInstallationDir -ErrorAction SilentlyContinue @@ -158,24 +219,40 @@ if (-not [string]::IsNullOrEmpty($qtVersion)) $qt6BinDir = Join-Path -Path $qt6Dir -ChildPath "bin" $qt6CMakeDir = Join-Path -Path $qt6Dir -ChildPath "lib\cmake" - Write-Host "Locating system PATH... " @info_colors - $systemPath = [Environment]::GetEnvironmentVariable("PATH", [EnvironmentVariableTarget]::Machine) - - Write-Host "Adding Qt6 directory to system PATH... " @info_colors - if ($systemPath -notmatch [regex]::Escape($qt6BinDir)) { - [Environment]::SetEnvironmentVariable("PATH", "$qt6BinDir;$systemPath", [EnvironmentVariableTarget]::Machine) - Write-Host "Qt6 binary directory path added to system PATH." @info_colors - } else { - Write-Host "Did not write to PATH: Qt6 binary directory path already exists in system PATH." @info_colors + if ($setSystemEnvVars) + { + Write-Host "Locating system PATH... " @info_colors + $systemPath = [Environment]::GetEnvironmentVariable("PATH", [EnvironmentVariableTarget]::Machine) + + Write-Host "Adding Qt6 directory to system PATH... " @info_colors + if ($systemPath -notmatch [regex]::Escape($qt6BinDir)) { + [Environment]::SetEnvironmentVariable("PATH", "$qt6BinDir;$systemPath", [EnvironmentVariableTarget]::Machine) + Write-Host "Qt6 binary directory path added to system PATH." @info_colors + } else { + Write-Host "Did not write to PATH: Qt6 binary directory path already exists in system PATH." @info_colors + } } } else { + # We attempt to use an existing installation of Qt Write-Host "Attempting to use existing system installation of Qt6... " @info_colors - $systemPath = [Environment]::GetEnvironmentVariable("PATH", [EnvironmentVariableTarget]::Machine) - if ($systemPath -notmatch [regex]::Escape($qtVersion)) { - Write-Host "Found Qt6 version that is NOT ${qtVersion} in system PATH. It is strongly recommended to use Qt ${qtVersion}" @info_colors + if (-not [string]::IsNullOrEmpty($systemQt)) + { + $qt6Version = Get-ChildItem -Path $systemQt -Directory | Where-Object { $_.Name -match '^\d+\.\d+\.\d+$'} | Select-Object -First 1 + $qt6ToolChain = Get-ChildItem -Path (Join-Path -Path $systemQt -ChildPath $qt6Version) -Directory | Where-Object { $_.Name -match '^msvc\d{4}_x64$'} | Select-Object -ExpandProperty Name + $qt6BinDir = "$systemQt\$($qt6Version.Name)\$qt6ToolChain\bin" + $qt6CMakeDir = "$systemQt\$($qt6Version.Name)\$qt6ToolChain\lib\cmake" + + if (-not (Test-Path -Path $qt6CMakeDir -PathType Container)) + { + Write-Host "Attempted to find the directory $qt6CMakeDir. Could NOT find a valid Qt6 installation." @warn_colors + } + } + else + { + Write-Host "Could NOT find a Qt6 installation. No path to Qt6 was supplied." @warn_colors } -} +} # Build/retrieve Freetype $freetypeVersion = "2.12.1" @@ -213,7 +290,7 @@ $freetypeBinPath = Join-Path -Path $projectDir -ChildPath "$dependencies\$freet $lib = [System.Environment]::GetEnvironmentVariable("LIB", [System.EnvironmentVariableTarget]::Machine) -if ($lib -notlike "*$freetypeInstall*") { +if (($setSystemEnvVars) -and ($lib -notlike "*$freetypeInstall*")) { Write-Host "Setting LIB environment variable with Freetype library... " @info_colors [System.Environment]::SetEnvironmentVariable("LIB", "$freetypeLibPath;$freetypeBinPath;$lib", [System.EnvironmentVariableTarget]::Machine) } @@ -223,7 +300,7 @@ $freetype2IncludePath = Join-Path -Path $projectDir -ChildPath "$dependencies\$ $include = [System.Environment]::GetEnvironmentVariable("INCLUDE", [System.EnvironmentVariableTarget]::Machine) -if ($include -notlike "*$freetypeRepo*") { +if (($setSystemEnvVars) -and ($include -notlike "*$freetypeRepo*")) { Write-Host "Setting INCLUDE environment variable with Freetype includes... " @info_colors [System.Environment]::SetEnvironmentVariable("INCLUDE", "$freetypeIncludePath;$freetype2IncludePath;$include", [System.EnvironmentVariableTarget]::Machine) } @@ -324,6 +401,7 @@ Set-Location -Path $projectDir Write-Host "Setting up Conan profile... " @info_colors try { + # TODO: Find Conan v1, not just any old Conan $conanVersion = conan --version Write-Output "Found conan version $conanVersion..." @@ -331,9 +409,13 @@ try { } catch { Write-Output "Could not find conan, adding Python scripts to path..." @info_colors $scripts = & $python -c "import sysconfig; print(sysconfig.get_path('scripts'))" - $systemPath = [Environment]::GetEnvironmentVariable("PATH", [EnvironmentVariableTarget]::Machine) - [Environment]::SetEnvironmentVariable("PATH", "$scripts;$systemPath", [EnvironmentVariableTarget]::Machine) - Write-Host "Python scripts path at location $scripts added to system PATH." @info_colors + + if ($setSystemEnvVars) + { + $systemPath = [Environment]::GetEnvironmentVariable("PATH", [EnvironmentVariableTarget]::Machine) + [Environment]::SetEnvironmentVariable("PATH", "$scripts;$systemPath", [EnvironmentVariableTarget]::Machine) + Write-Host "Python scripts path at location $scripts added to system PATH." @info_colors + } $conan = "$scripts/conan.exe" } @@ -358,6 +440,23 @@ $cacheVariables = @{ CMAKE_PREFIX_PATH = "$qt6CMakeDir" } +# For MSVC version != v143 latest, and Visual Studio generator specified, set toolset with cache variable +if ((-not [string]::IsNullOrEmpty($msvcVersion)) -and ($generator -eq "Visual Studio 17 2022")) +{ + $cacheVariables = $cacheVariables + @{ + CMAKE_GENERATOR_TOOLSET = "version=$msvcVersion" + } +} + +# For MSVC version != v143 latest, and Ninja generator specified, set toolset at preset level +if ((-not [string]::IsNullOrEmpty($msvcVersion)) -and ($generator -eq "Ninja")) +{ + $toolset = @{ + value = "version=$msvcVersion" + strategy = "external" + } +} + $cmakeUserPresets = [PSCustomObject]@{ version = 3 cmakeMinimumRequired = @{ @@ -372,20 +471,53 @@ $presets = @( name = "CLI-$build-MSVC" displayName = "CLI $build Build" description = "The preset for a CLI $build build on MSVC" + generator = $generator inherits = @("CLI-$build") }, [PSCustomObject]@{ name = "GUI-$build-MSVC" displayName = "GUI $build Build" description = "The preset for a GUI $build build on MSVC" + generator = $generator inherits = @("GUI-$build") } ) +# Set environment variables +if (-not $setSystemEnvVars) +{ + if ([string]::IsNullOrEmpty($scripts)) + { + $scripts = '' + } + else + { + $scripts = "$scripts;" + } + + $environment = @{ + PATH = "$scripts$qt6BinDir;$pythonEnvPath;`$penv{PATH}" + } +} + foreach ($preset in $presets) { + # Set CMake cache variables $preset | Add-Member -MemberType NoteProperty -Name cacheVariables -Value ($cacheVariables + @{ CONFIG = "$($preset.name)-x64" }) + + # Set environment variables + if (-not $setSystemEnvVars) + { + $preset | Add-Member -MemberType NoteProperty -Name environment -Value ($environment) + } + + # Set toolset + if ($toolset) + { + $preset | Add-Member -MemberType NoteProperty -Name toolset -Value ($toolset) + } + $cmakeUserPresets.configurePresets += $preset } diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt index 9b84f52298..2308828569 100644 --- a/src/modules/CMakeLists.txt +++ b/src/modules/CMakeLists.txt @@ -81,6 +81,8 @@ macro(dissolve_add_module_gui modulename) ${PROJECT_BINARY_DIR}/src ${CMAKE_BINARY_DIR}/src/gui/gui_autogen/include ${CMAKE_BINARY_DIR}/src/gui/widgets/widgets_autogen/include + ${CMAKE_BINARY_DIR}/src/gui/gui_autogen/include_${CMAKE_BUILD_TYPE} + ${CMAKE_BINARY_DIR}/src/gui/widgets/widgets_autogen/include_${CMAKE_BUILD_TYPE} ${Qt6Core_INCLUDE_DIRS} ${Qt6Gui_INCLUDE_DIRS} ${Qt6Widgets_INCLUDE_DIRS} diff --git a/web/docs/userguide/developers/vs2022.md b/web/docs/userguide/developers/vs2022.md index fbf9324a8b..916eb455bc 100644 --- a/web/docs/userguide/developers/vs2022.md +++ b/web/docs/userguide/developers/vs2022.md @@ -17,7 +17,7 @@ Download the Dissolve Github repository using the full Visual Studio 2022 Git in ### Install dependencies using powershell script -Open Developer Powershell for VS2022 from Windows as administrator (since system environment variables are modified), and navigate to the Dissolve Visual Studio repo folder. +Open Developer Powershell for VS2022 from Windows as administrator, and navigate to the Dissolve Visual Studio repo folder. Run the following Powershell command to enable scripts: ```shell @@ -37,7 +37,8 @@ This script can be used to install the following packages into a `dependencies` - Java A specific version of Qt can be specified for installation via the `-qtVersion` parameter (i.e. 6.4.2). -If you have an existing system Qt6 installation, the script can default to using this by ignoring the `-qtVersion` parameter. +If you have an existing system Qt6 installation, the script can attempt to use this instead via the `-systemQt` parameter. +When installing from the [official Qt website](https://www.qt.io/), the installation will usually be located in `C:\Qt6`, but this may vary depending on your system setup. Ensure that the system Qt `msvc2019_64` binaries (`bin`) and CMake module (`lib\cmake`) directories are added to the PATH prior to using your own Qt installation. The script defaults to configuring dependencies for `Debug`. If you want to develop in `Release` mode, use the appropriate flag: @@ -45,8 +46,15 @@ The script defaults to configuring dependencies for `Debug`. If you want to deve ./develop.ps1 -release -qtVersion 6.4.2 ``` +A flag `-clean` can be passed to the script to setup from a blank environment. + The output of the script is a custom `CMakeUserPresets.json` which contains configurations for building Dissolve CLI and GUI with CMake. +__WARNING__: Dissolve has a dependency on the antrl4-cpp-runtime package which is currently incompatible with MSVC latest (v143) and will lead to a compiler error. Therefore it is strongly recommended that you build this Dissolve MSVC environment with a previous version of MSVC - we recommend version [14.41.17.11](https://learn.microsoft.com/en-us/visualstudio/install/workload-component-id-vs-build-tools?view=vs-2022). This toolset is available to download using the Visual Studio Installer via the individual components section. +Once this toolset has been installed, navigate to the location of the system `C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC`. For example, the subdirectory corresponding to 14.41.17.11 is "14.41.34120". +When calling the `develop.ps1` script, include the following arguments `... -msvcVersion 14.41.34120 -generator "Ninja"`. + + ### Configure Dissolve in Visual Studio with CMake support In Visual Studio, open the Dissolve repo. In the configurations dropdown you should see either @@ -63,7 +71,9 @@ Once the preferred preset is selected, right-click the Dissolve `CMakeLists.txt` This should start the CMake configuration process, which involves using the Conan package manager to install the remaining dependencies. Visual Studio may have been set up to do this automatically when the project is opened, or if any saved changes are made to `CMakeLists.txt`. -Note: on configuration, you may experience a Conan error "locked by another concurrent conan process". Typically this can be resolved using the command `conan remove --locks` in a developer powershell. If this does not work, navigate to your C drive `.conan` folder and delete the `data` sub-directory. +__Note__: on CMake configuration in Visual Studio, you may experience the following Conan errors: +1) "locked by another concurrent conan process". Typically this can be resolved using the command `conan remove --locks` in a developer powershell. If this does not work, navigate to your C drive `.conan` folder and delete the `data` sub-directory. +2) Occasionally, configuring the cache leads to an unexplained CMake error `conan install failed=1`. If this happens try deleting the cache and reconfiguring, or ultimately, manually delete the build folder, restart Visual Studio and reconfigure from scratch. ### Build project