From 4a1e31b0c1aa8a7935ba33654adfc154598f88fb Mon Sep 17 00:00:00 2001 From: Abel Braaksma Date: Mon, 2 Oct 2017 20:18:36 +0200 Subject: [PATCH] This adds backup, restore, coloration and many more checks to the update-vsintegration.cmd (#3672) * This adds backup, restore, coloration and many more checks to the update-vsintegration.cmd * This adds backup, restore, coloration and many more checks to the update-vsintegration.cmd * Remove ambiguous an irrelevant instruction, improved help and instructions * Fix a scenario where the return code wasn't nonzero for error conditions, fixes not creating backup dir when not backing up --- vsintegration/update-vsintegration.cmd | 726 +++++++++++++++++++++---- 1 file changed, 621 insertions(+), 105 deletions(-) diff --git a/vsintegration/update-vsintegration.cmd b/vsintegration/update-vsintegration.cmd index e7d1d85ea..9dcb36f92 100644 --- a/vsintegration/update-vsintegration.cmd +++ b/vsintegration/update-vsintegration.cmd @@ -3,30 +3,148 @@ @rem See License.txt in the project root for license information. @rem =========================================================================================================== -rem @echo off -setlocal +@rem Notes/instructions for modifications: +@rem +@rem * Do not use "::" for comments, as the line will be parsed and can create spurious +@rem errors, i.e. if it contains variables, "|" or ">" characters, esp. within "IF" +@rem and "FOR" compound statements +@rem +@rem * The coloring method uses the colors from color /h through a hacky trick with findstr. +@rem Only use filename-safe characters if you use CALL :colorEcho +@rem +@rem * Parts of this batch file require administrator permission. If such permissions aren't +@rem available, a warning will be issued and relevant parts will not be executed. +@rem +@rem * Currently, only one paramter is parsed and combinations are not possible +@rem +@rem * Installation of F# FSC compiler and FSI are done in the SHARED SDK directory. Henceforth +@rem each installation of Visual Studio 2017 will use the updated FSC.exe and the commandline +@rem FSI.exe. The in-product VS FSI plugin, syntax highlighting and IntelliSense must be +@rem installed through VSIXInstaller.exe debug\net40\bin\VisualFSharpOpenSource.vsix +@rem +@rem This procedure needs to be changed once F# supports multiple side-by-side installations +@rem at which point everything will go through VSIXInstaller.exe -if /i "%1" == "debug" goto :ok -if /i "%1" == "release" goto :ok +@echo off +setlocal EnableDelayedExpansion -echo Clobbers existing Visual Studio installation of F# bits -echo Usage: -echo update-vsintegration.cmd debug -echo update-vsintegration.cmd release -exit /b 1 +rem Count errors, warnings and succesful copies +set ERRORCOUNT=0 +set WARNCOUNT=0 +set COPYCOUNT=0 + +rem Enable colors, but can ONLY BE USED WITH PRINTING LINES THAT FIT IN A FILENAME! +for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do ( + set "DEL=%%a" +) + +if /i "%1" == "debug" ( + set ACTION=debug + set DEPLOY=yes + set BINDIR=%~dp0..\%1\net40\bin + goto :ok +) +if /i "%1" == "release" ( + set ACTION=release + set DEPLOY=yes + set BINDIR=%~dp0..\%1\net40\bin + goto :ok +) +if /i "%1" == "restore" ( + set ACTION=restore + set DEPLOY=no + set BINDIR=%~dp0..\%1 + goto :ok +) +if /i "%1" == "backup" ( + set ACTION=backup + set DEPLOY=no + set BINDIR=%~dp0..\restore + goto :ok +) + +set GOTOHELP=yes :ok -set BINDIR=%~dp0..\%1\net40\bin +set RESTOREDIR=%~dp0..\restore set TOPDIR=%~dp0.. +rem By using a token that does not exist in paths, this will resolve any ".." and "." in the path, even if path contains spaces +FOR /F "tokens=*" %%I IN ("%RESTOREDIR%") DO set RESTOREDIR=%%~fI +FOR /F "tokens=*" %%I IN ("%BINDIR%") DO set BINDIR=%%~fI +FOR /F "tokens=*" %%I IN ("%TOPDIR%") DO set TOPDIR=%%~fI + +if /i "%GOTOHELP%" == "yes" goto :help +GOTO :start + + +:help + +echo. +echo Installs or restores F# SDK bits, which applies system-wide to all Visual Studio +echo 2017 installations. After running this, each project targeting F# 4.1 will use +echo your locally built FSC.exe. It will not update other F# tools, see remarks below. +echo. +echo Requires Administrator privileges for removing/restoring strong-naming. +echo. +echo Syntax: %0 [debug^|release^|restore^|backup] +echo. +echo debug integrates debug builds of FSC, FSI ^& tools +echo release integrates release builds of FSC, FSI ^& tools +echo restore restores original SDK from an earlier backup +echo backup backups the files that would be overwritten, does not deploy anything +echo. +echo Paths used: +echo. +echo Root location: %TOPDIR% +echo Debug bin location: %TOPDIR%\debug\net40\bin +echo Release bin location: %TOPDIR%\release\net40\bin +echo Backup location: %RESTOREDIR% +echo. +echo Remarks: +echo. +echo This script should only be run after build.cmd has completed successfully. +echo. +echo Clearing the git repository may clear the backup directory. To be on the safe +echo side, you should place a copy of the backup dir outside of the git repo. +echo. +echo This batch script will only update the relevant SDK bits, and remove or restore +echo strong-naming automatically. It is recommended that you also update the F# Tools +echo by running the following two commands after a build of "build vs" or +echo "build vs debug" has completed. More instructions in DEVGUIDE.md in the root. +echo. +echo For Release builds: +echo. +echo ^> VSIXInstaller.exe /u:"VisualFSharp" +echo ^> VSIXInstaller.exe release\net40\bin\VisualFSharpOpenSource.vsix +echo. +echo For Debug builds: +echo. +echo ^> VSIXInstaller.exe /u:"VisualFSharp" +echo ^> VSIXInstaller.exe debug\net40\bin\VisualFSharpOpenSource.vsix +echo. + +exit /b 1 + +:start + +echo. +if "%DEPLOY%" == "yes" echo Starting deployment of %ACTION% bits. +if not "%DEPLOY%" == "yes" echo Starting %ACTION% +echo. + +rem This check whether we're started with administrator rights +CALL :checkPrequisites + if /i "%PROCESSOR_ARCHITECTURE%"=="x86" set X86_PROGRAMFILES=%ProgramFiles% if /I "%PROCESSOR_ARCHITECTURE%"=="AMD64" set X86_PROGRAMFILES=%ProgramFiles(x86)% set REGEXE32BIT=reg.exe if not "%OSARCH%"=="x86" set REGEXE32BIT=%WINDIR%\syswow64\reg.exe -::See https://stackoverflow.com/a/17113667/111575 on 2^>NUL for suppressing the error "ERROR: The system was unable to find the specified registry key or value." from reg.exe, this fixes #3619 +rem See https://stackoverflow.com/a/17113667/111575 on 2^>NUL for suppressing the error "ERROR: The system was unable to find the specified registry key or value." from reg.exe, this fixes #3619 +rem The delims are a TAB and a SPACE, do not normalize it! FOR /F "tokens=2* delims= " %%A IN ('%REGEXE32BIT% QUERY "HKLM\Software\WOW6432Node\Microsoft\Microsoft SDKs\NETFXSDK\4.6.2\WinSDK-NetFx40Tools" /v InstallationFolder 2^>NUL') DO SET WINSDKNETFXTOOLS=%%B if "%WINSDKNETFXTOOLS%"=="" FOR /F "tokens=2* delims= " %%A IN ('%REGEXE32BIT% QUERY "HKLM\Software\WOW6432Node\Microsoft\Microsoft SDKs\NETFXSDK\4.6.1\WinSDK-NetFx40Tools" /v InstallationFolder 2^>NUL') DO SET WINSDKNETFXTOOLS=%%B if "%WINSDKNETFXTOOLS%"=="" FOR /F "tokens=2* delims= " %%A IN ('%REGEXE32BIT% QUERY "HKLM\Software\Microsoft\Microsoft SDKs\NETFXSDK\4.6\WinSDK-NetFx40Tools" /v InstallationFolder 2^>NUL') DO SET WINSDKNETFXTOOLS=%%B @@ -43,112 +161,510 @@ set NGEN64=%windir%\Microsoft.NET\Framework64\v4.0.30319\ngen.exe set FSHARPVERSION=4.1 set FSHARPVERSION2=41 +rem The various locations of the SDK and tools + +rem SDK path, will be created if it doesn't exist set COMPILERSDKPATH=%X86_PROGRAMFILES%\Microsoft SDKs\F#\%FSHARPVERSION%\Framework\v4.0 -mkdir "%COMPILERSDKPATH%" -copy /y "%BINDIR%\fsc.exe" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\fsc.exe.config" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\FSharp.Build.dll" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\FSharp.Compiler.Private.dll" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\FSharp.Compiler.Interactive.Settings.dll" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\fsi.exe" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\fsi.exe.config" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\fsiAnyCpu.exe" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\fsiAnyCpu.exe.config" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\Microsoft.FSharp.Targets" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\Microsoft.Portable.FSharp.Targets" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\Microsoft.FSharp.NetSdk.props" "%COMPILERSDKPATH%" -copy /y "%BINDIR%\Microsoft.FSharp.NetSdk.targets" "%COMPILERSDKPATH%" -copy /y "%TOPDIR%\vsintegration\src\SupportedRuntimes\SupportedRuntimes.xml" "%COMPILERSDKPATH%" +rem Main assemblies path, will be created if it doesn't exist set COMPILERMAINASSEMBLIESPATH=%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.%FSHARPVERSION%.0 -mkdir "%COMPILERMAINASSEMBLIESPATH%" -copy /y "%BINDIR%\FSharp.Core.dll" "%COMPILERMAINASSEMBLIESPATH%" -copy /y "%BINDIR%\FSharp.Core.optdata" "%COMPILERMAINASSEMBLIESPATH%" -copy /y "%BINDIR%\FSharp.Core.sigdata" "%COMPILERMAINASSEMBLIESPATH%" -copy /y "%BINDIR%\FSharp.Core.xml" "%COMPILERMAINASSEMBLIESPATH%" +rem The .NET Core 3.7 assemblies path, will be created if it doesn't exist set COMPILER7ASSEMBLIESPATH=%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETCore\3.7.%FSHARPVERSION2%.0 -mkdir "%COMPILER7ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable7\bin\FSharp.Core.dll" "%COMPILER7ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable7\bin\FSharp.Core.optdata" "%COMPILER7ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable7\bin\FSharp.Core.sigdata" "%COMPILER7ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable7\bin\FSharp.Core.xml" "%COMPILER7ASSEMBLIESPATH%" +rem The .NET Core 3.78 assemblies path, will be created if it doesn't exist set COMPILER78ASSEMBLIESPATH=%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETCore\3.78.%FSHARPVERSION2%.0 -mkdir "%COMPILER78ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable78\bin\FSharp.Core.dll" "%COMPILER78ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable78\bin\FSharp.Core.optdata" "%COMPILER78ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable78\bin\FSharp.Core.sigdata" "%COMPILER78ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable78\bin\FSharp.Core.xml" "%COMPILER78ASSEMBLIESPATH%" +rem The .NET Core 3.259 assemblies path, will be created if it doesn't exist set COMPILER259ASSEMBLIESPATH=%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETCore\3.259.%FSHARPVERSION2%.0 -mkdir "%COMPILER259ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable259\bin\FSharp.Core.dll" "%COMPILER259ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable259\bin\FSharp.Core.optdata" "%COMPILER259ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable259\bin\FSharp.Core.sigdata" "%COMPILER259ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable259\bin\FSharp.Core.xml" "%COMPILER259ASSEMBLIESPATH%" +rem The .NET Portable 3.47 assemblies path, will be created if it doesn't exist set COMPILER47ASSEMBLIESPATH=%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETPortable\3.47.%FSHARPVERSION2%.0 -mkdir "%COMPILER47ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable47\bin\FSharp.Core.dll" "%COMPILER47ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable47\bin\FSharp.Core.optdata" "%COMPILER47ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable47\bin\FSharp.Core.sigdata" "%COMPILER47ASSEMBLIESPATH%" -copy /y "%BINDIR%\..\..\portable47\bin\FSharp.Core.xml" "%COMPILER47ASSEMBLIESPATH%" +rem Try to create target and backup folders, if needed +set RESTOREBASE=%RESTOREDIR% + +rem Only create backup dirs if we are backupping or restoring +rem (in the latter case, the directories should already be there, but if not, it prevents errors later on) +if "!DEPLOY!" == "no" ( + CALL :tryCreateFolder "!RESTOREBASE!\compiler_sdk" + CALL :tryCreateFolder "!RESTOREBASE!\main_assemblies" + CALL :tryCreateFolder "!RESTOREBASE!\profile_7" + CALL :tryCreateFolder "!RESTOREBASE!\profile_78" + CALL :tryCreateFolder "!RESTOREBASE!\profile_259" + CALL :tryCreateFolder "!RESTOREBASE!\profile_47" +) +CALL :tryCreateFolder "!COMPILERSDKPATH!" +CALL :tryCreateFolder "!COMPILERMAINASSEMBLIESPATH!" +CALL :tryCreateFolder "!COMPILER7ASSEMBLIESPATH!" & +CALL :tryCreateFolder "!COMPILER78ASSEMBLIESPATH!" +CALL :tryCreateFolder "!COMPILER259ASSEMBLIESPATH!" +CALL :tryCreateFolder "!COMPILER47ASSEMBLIESPATH!" + +rem If one or more directories could not be created, exit early with a non-zero error code +if "!CREATEFAILED!"=="true" CALL :exitFailDir & EXIT /B 1 + +rem Deploying main files, fsi.exe and fsc.exe and related + +echo. +CALL :colorEcho 02 "[!ACTION!] Processing files for compiler_sdk" & echo. + +set SOURCEDIR=%BINDIR% +set RESTOREDIR=!RESTOREBASE!\compiler_sdk +CALL :checkAvailability compiler_sdk +if "!BIN_AVAILABLE!" == "true" ( + CALL :backupAndOrCopy fsc.exe "!COMPILERSDKPATH!" + CALL :backupAndOrCopy fsc.exe.config "%COMPILERSDKPATH%" + CALL :backupAndOrCopy FSharp.Build.dll "%COMPILERSDKPATH%" + CALL :backupAndOrCopy FSharp.Compiler.Private.dll "%COMPILERSDKPATH%" + CALL :backupAndOrCopy FSharp.Compiler.Interactive.Settings.dll "%COMPILERSDKPATH%" + CALL :backupAndOrCopy fsi.exe "%COMPILERSDKPATH%" + CALL :backupAndOrCopy fsi.exe.config "%COMPILERSDKPATH%" + CALL :backupAndOrCopy fsiAnyCpu.exe "%COMPILERSDKPATH%" + CALL :backupAndOrCopy fsiAnyCpu.exe.config "%COMPILERSDKPATH%" + CALL :backupAndOrCopy Microsoft.FSharp.Targets "%COMPILERSDKPATH%" + CALL :backupAndOrCopy Microsoft.Portable.FSharp.Targets "%COMPILERSDKPATH%" + CALL :backupAndOrCopy Microsoft.FSharp.NetSdk.props "%COMPILERSDKPATH%" + CALL :backupAndOrCopy Microsoft.FSharp.NetSdk.targets "%COMPILERSDKPATH%" + + rem Special casing for SupportedRuntimes.xml, it has a different source directory, it's always there + set SOURCEDIR="%TOPDIR%\vsintegration\src\SupportedRuntimes" + CALL :backupAndOrCopy SupportedRuntimes.xml "%COMPILERSDKPATH%" +) + + + +rem Deploying main assemblies + +echo. +CALL :colorEcho 02 "[!ACTION!] Processing files for main_assemblies" & echo. + +set SOURCEDIR=%BINDIR% +set RESTOREDIR=!RESTOREBASE!\main_assemblies +CALL :checkAvailability main_assemblies +if "!BIN_AVAILABLE!" == "true" ( + CALL :backupAndOrCopy FSharp.Core.dll "%COMPILERMAINASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.optdata "%COMPILERMAINASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.sigdata "%COMPILERMAINASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.xml "%COMPILERMAINASSEMBLIESPATH%" +) + + +rem Deploying for .NET Core 3.7 + +echo. +CALL :colorEcho 02 "[!ACTION!] Processing files for profile_7" & echo. + +set SOURCEDIR=%BINDIR%\..\..\portable7\bin +set RESTOREDIR=!RESTOREBASE!\profile_7 +CALL :checkAvailability profile_7 +if "!BIN_AVAILABLE!" == "true" ( + CALL :backupAndOrCopy FSharp.Core.dll "%COMPILER7ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.optdata "%COMPILER7ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.sigdata "%COMPILER7ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.xml "%COMPILER7ASSEMBLIESPATH%" +) + + +rem Deploying for .NET Core 3.78 + +echo. +CALL :colorEcho 02 "[!ACTION!] Processing files for profile_78" & echo. + +set SOURCEDIR=%BINDIR%\..\..\portable78\bin +set RESTOREDIR=!RESTOREBASE!\profile_78 +CALL :checkAvailability profile_78 +if "!BIN_AVAILABLE!" == "true" ( + CALL :backupAndOrCopy FSharp.Core.dll "%COMPILER78ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.optdata "%COMPILER78ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.sigdata "%COMPILER78ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.xml "%COMPILER78ASSEMBLIESPATH%" +) + + +rem Deploying for .NET Core 3.259 + +echo. +CALL :colorEcho 02 "[!ACTION!] Processing files for profile_259" & echo. + +set SOURCEDIR=%BINDIR%\..\..\portable259\bin +set RESTOREDIR=!RESTOREBASE!\profile_259 +CALL :checkAvailability profile_259 +if "!BIN_AVAILABLE!" == "true" ( + CALL :backupAndOrCopy FSharp.Core.dll "%COMPILER259ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.optdata "%COMPILER259ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.sigdata "%COMPILER259ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.xml "%COMPILER259ASSEMBLIESPATH%" +) + + +rem Deploying for .NET Portable 3.47 + +echo. +CALL :colorEcho 02 "[!ACTION!] Processing files for profile_47" & echo. + +set SOURCEDIR=%BINDIR%\..\..\portable47\bin +set RESTOREDIR=!RESTOREBASE!\profile_47 +CALL :checkAvailability profile_47 +if "!BIN_AVAILABLE!" == "true" ( + CALL :backupAndOrCopy FSharp.Core.dll "%COMPILER47ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.optdata "%COMPILER47ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.sigdata "%COMPILER47ASSEMBLIESPATH%" + CALL :backupAndOrCopy FSharp.Core.xml "%COMPILER47ASSEMBLIESPATH%" +) + +REM TODO: this was already here (2017-09-28) and was already commented out, I think (AB) that these redirects aren't necessary anymore and can be permanently removed REM echo ^^^^^ ^^^^^ > "%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.%FSHARPVERSION%.0\pub.config" -if /I "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( - REG ADD "HKLM\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\F# %FSHARPVERSION% Core Assemblies (Open Source)" /ve /t REG_SZ /f /d "%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.%FSHARPVERSION%.0\ - REG ADD "HKLM\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.50709\AssemblyFoldersEx\F# %FSHARPVERSION% Core Assemblies (Open Source)" /ve /t REG_SZ /f /d "%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.%FSHARPVERSION%.0\ -) -REG ADD "HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\F# %FSHARPVERSION% Core Assemblies (Open Source)" /ve /t REG_SZ /f /d "%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.%FSHARPVERSION%.0\ -REG ADD "HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.50709\AssemblyFoldersEx\F# %FSHARPVERSION% Core Assemblies (Open Source)" /ve /t REG_SZ /f /d "%X86_PROGRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.%FSHARPVERSION%.0\ - -rem Disable strong-name validation for F# binaries built from open source that are signed with the microsoft key -%SN32% -Vr FSharp.Core,b03f5f7f11d50a3a -%SN32% -Vr FSharp.Build,b03f5f7f11d50a3a -%SN32% -Vr FSharp.Compiler.Interactive.Settings,b03f5f7f11d50a3a -%SN32% -Vr HostedCompilerServer,b03f5f7f11d50a3a - -%SN32% -Vr FSharp.Compiler,b03f5f7f11d50a3a -%SN32% -Vr FSharp.Compiler.Server.Shared,b03f5f7f11d50a3a -%SN32% -Vr FSharp.Editor,b03f5f7f11d50a3a -%SN32% -Vr FSharp.LanguageService,b03f5f7f11d50a3a -%SN32% -Vr FSharp.LanguageService.Base,b03f5f7f11d50a3a -%SN32% -Vr FSharp.ProjectSystem.Base,b03f5f7f11d50a3a -%SN32% -Vr FSharp.ProjectSystem.FSharp,b03f5f7f11d50a3a -%SN32% -Vr FSharp.ProjectSystem.PropertyPages,b03f5f7f11d50a3a -%SN32% -Vr FSharp.VS.FSI,b03f5f7f11d50a3a -%SN32% -Vr VisualFSharp.Unittests,b03f5f7f11d50a3a -%SN32% -Vr VisualFSharp.Salsa,b03f5f7f11d50a3a - -if /i "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( - %SN64% -Vr FSharp.Core,b03f5f7f11d50a3a - %SN64% -Vr FSharp.Build,b03f5f7f11d50a3a - %SN64% -Vr FSharp.Compiler.Interactive.Settings,b03f5f7f11d50a3a - %SN64% -Vr HostedCompilerServer,b03f5f7f11d50a3a - - %SN64% -Vr FSharp.Compiler,b03f5f7f11d50a3a - %SN64% -Vr FSharp.Compiler.Server.Shared,b03f5f7f11d50a3a - %SN64% -Vr FSharp.Editor,b03f5f7f11d50a3a - %SN64% -Vr FSharp.LanguageService,b03f5f7f11d50a3a - %SN64% -Vr FSharp.LanguageService.Base,b03f5f7f11d50a3a - %SN64% -Vr FSharp.ProjectSystem.Base,b03f5f7f11d50a3a - %SN64% -Vr FSharp.ProjectSystem.FSharp,b03f5f7f11d50a3a - %SN64% -Vr FSharp.ProjectSystem.PropertyPages,b03f5f7f11d50a3a - %SN64% -Vr FSharp.VS.FSI,b03f5f7f11d50a3a - %SN64% -Vr VisualFSharp.Unittests,b03f5f7f11d50a3a - %SN64% -Vr VisualFSharp.Salsa,b03f5f7f11d50a3a -) - -rem NGen fsc, fsi, fsiAnyCpu, and FSharp.Build.dll - -"%NGEN32%" install "%COMPILERSDKPATH%\fsc.exe" /queue:1 -"%NGEN32%" install "%COMPILERSDKPATH%\fsi.exe" /queue:1 -"%NGEN32%" install "%COMPILERSDKPATH%\fsiAnyCpu.exe" /queue:1 -"%NGEN32%" install "%COMPILERSDKPATH%\FSharp.Build.dll" /queue:1 - -if /i "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( - "%NGEN64%" install "%COMPILERSDKPATH%\fsiAnyCpu.exe" /queue:1 - "%NGEN64%" install "%COMPILERSDKPATH%\FSharp.Build.dll" /queue:1 +rem To add registry keys and to change strong-name validation requires Administrator access + +if "%DEPLOY%" == "yes" if "!ISADMIN!" == "yes" ( + echo. + CALL :colorEcho 02 "[!ACTION!] Setting or adding registry keys for open source assemblies" & echo. + if /I "!PROCESSOR_ARCHITECTURE!"=="AMD64" ( + REG ADD "HKLM\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\F# !FSHARPVERSION! Core Assemblies (Open Source)" /ve /t REG_SZ /f /d "!X86_PROGRAMFILES!\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.!FSHARPVERSION!.0\ + REG ADD "HKLM\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.50709\AssemblyFoldersEx\F# !FSHARPVERSION! Core Assemblies (Open Source)" /ve /t REG_SZ /f /d "!X86_PROGRAMFILES!\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.!FSHARPVERSION!.0\ + ) + REG ADD "HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\F# !FSHARPVERSION! Core Assemblies (Open Source)" /ve /t REG_SZ /f /d "!X86_PROGRAMFILES!\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.!FSHARPVERSION!.0\ + REG ADD "HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.50709\AssemblyFoldersEx\F# !FSHARPVERSION! Core Assemblies (Open Source)" /ve /t REG_SZ /f /d "!X86_PROGRAMFILES!\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.!FSHARPVERSION!.0\ + + rem Disable strong-name validation for F# binaries built from open source that are signed with the microsoft key + echo. + CALL :colorEcho 02 "[!ACTION!] Removing strong-name validation of F# binaries" & echo. + !SN32! -Vr FSharp.Core,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.Build,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.Compiler.Interactive.Settings,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr HostedCompilerServer,b03f5f7f11d50a3a 1>NUL 2>NUL + + !SN32! -Vr FSharp.Compiler,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.Compiler.Server.Shared,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.Editor,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.LanguageService,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.LanguageService.Base,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.ProjectSystem.Base,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.ProjectSystem.FSharp,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.ProjectSystem.PropertyPages,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr FSharp.VS.FSI,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr VisualFSharp.Unittests,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN32! -Vr VisualFSharp.Salsa,b03f5f7f11d50a3a 1>NUL 2>NUL + + REM Do this *in addition* to the above for x64 systems + if /i "!PROCESSOR_ARCHITECTURE!"=="AMD64" ( + !SN64! -Vr FSharp.Core,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.Build,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.Compiler.Interactive.Settings,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr HostedCompilerServer,b03f5f7f11d50a3a 1>NUL 2>NUL + + !SN64! -Vr FSharp.Compiler,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.Compiler.Server.Shared,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.Editor,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.LanguageService,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.LanguageService.Base,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.ProjectSystem.Base,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.ProjectSystem.FSharp,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.ProjectSystem.PropertyPages,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr FSharp.VS.FSI,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr VisualFSharp.Unittests,b03f5f7f11d50a3a 1>NUL 2>NUL + !SN64! -Vr VisualFSharp.Salsa,b03f5f7f11d50a3a 1>NUL 2>NUL + ) + + rem NGen fsc, fsi, fsiAnyCpu, and FSharp.Build.dll + + echo. + CALL :colorEcho 02 "[!ACTION!] Queuing for NGEN of FSI and FSC binaries" & echo. + echo [!ACTION!] NGEN of "!COMPILERSDKPATH!\fsc.exe" + "!NGEN32!" install "!COMPILERSDKPATH!\fsc.exe" /queue:1 1>NUL + echo [!ACTION!] NGEN of "!COMPILERSDKPATH!\fsi.exe" + "!NGEN32!" install "!COMPILERSDKPATH!\fsi.exe" /queue:1 1>NUL + echo [!ACTION!] NGEN of "!COMPILERSDKPATH!\fsiAnyCpu.exe" + "!NGEN32!" install "!COMPILERSDKPATH!\fsiAnyCpu.exe" /queue:1 1>NUL + echo [!ACTION!] NGEN of "!COMPILERSDKPATH!\FSharp.Build.dll" + "!NGEN32!" install "!COMPILERSDKPATH!\FSharp.Build.dll" /queue:1 1>NUL + + if /i "!PROCESSOR_ARCHITECTURE!"=="AMD64" ( + echo [!ACTION!] NGEN64 of "!COMPILERSDKPATH!\fsiAnyCpu.exe" + "!NGEN64!" install "!COMPILERSDKPATH!\fsiAnyCpu.exe" /queue:1 1>NUL + echo [!ACTION!] NGEN64 of "!COMPILERSDKPATH!\FSharp.Build.dll" + "!NGEN64!" install "!COMPILERSDKPATH!\FSharp.Build.dll" /queue:1 1>NUL + ) +) + +if "%DEPLOY%" == "yes" if "!ISADMIN!" == "no" ( + echo. + CALL :colorEcho 0E "[!ACTION!] SKIPPED (no admin) Setting or adding registry keys for open source assemblies" & echo. + CALL :colorEcho 0E "[!ACTION!] SKIPPED (no admin) Removing strong-name validation of F# binaries" & echo. + CALL :colorEcho 02 "[!ACTION!] SKIPPED (no admin) Queuing for NGEN of FSI and FSC binaries" & echo. + SET /A WARNCOUNT+=3 +) + +rem Re-enable certain settings when restoring, NGEN the original files again, requires admin rights +if "%ACTION%" == "restore" if "!ISADMIN!" == "yes" ( + + rem Re-enable strong-name validation for F# binaries that were previously installed + echo. + CALL :colorEcho 02 "[!ACTION!] Re-enabling strong-name validation of original F# binaries" & echo. + !SN32! -Vu FSharp.Core,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.Build,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.Compiler.Interactive.Settings,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu HostedCompilerServer,b03f5f7f11d50a3a 2>NUL 1>NUL + + !SN32! -Vu FSharp.Compiler,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.Compiler.Server.Shared,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.Editor,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.LanguageService,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.LanguageService.Base,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.ProjectSystem.Base,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.ProjectSystem.FSharp,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.ProjectSystem.PropertyPages,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu FSharp.VS.FSI,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu VisualFSharp.Unittests,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN32! -Vu VisualFSharp.Salsa,b03f5f7f11d50a3a 2>NUL 1>NUL + + REM Do this *in addition* to the above for x64 systems + if /i "!PROCESSOR_ARCHITECTURE!"=="AMD64" ( + !SN64! -Vu FSharp.Core,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.Build,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.Compiler.Interactive.Settings,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu HostedCompilerServer,b03f5f7f11d50a3a 2>NUL 1>NUL + + !SN64! -Vu FSharp.Compiler,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.Compiler.Server.Shared,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.Editor,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.LanguageService,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.LanguageService.Base,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.ProjectSystem.Base,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.ProjectSystem.FSharp,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.ProjectSystem.PropertyPages,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu FSharp.VS.FSI,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu VisualFSharp.Unittests,b03f5f7f11d50a3a 2>NUL 1>NUL + !SN64! -Vu VisualFSharp.Salsa,b03f5f7f11d50a3a 2>NUL 1>NUL + ) + + rem NGen fsc, fsi, fsiAnyCpu, and FSharp.Build.dll + + echo. + CALL :colorEcho 02 "[!ACTION!] Queuing for NGEN of FSI and FSC binaries" & echo. + echo [!ACTION!] NGEN of "!COMPILERSDKPATH!\fsc.exe" + "!NGEN32!" install "!COMPILERSDKPATH!\fsc.exe" /queue:1 1>NUL + echo [!ACTION!] NGEN of "!COMPILERSDKPATH!\fsi.exe" + "!NGEN32!" install "!COMPILERSDKPATH!\fsi.exe" /queue:1 1>NUL + echo [!ACTION!] NGEN of "!COMPILERSDKPATH!\fsiAnyCpu.exe" + "!NGEN32!" install "!COMPILERSDKPATH!\fsiAnyCpu.exe" /queue:1 1>NUL + echo [!ACTION!] NGEN of "!COMPILERSDKPATH!\FSharp.Build.dll" + "!NGEN32!" install "!COMPILERSDKPATH!\FSharp.Build.dll" /queue:1 1>NUL + + if /i "!PROCESSOR_ARCHITECTURE!"=="AMD64" ( + echo [!ACTION!] NGEN64 of "!COMPILERSDKPATH!\fsiAnyCpu.exe" + "!NGEN64!" install "!COMPILERSDKPATH!\fsiAnyCpu.exe" /queue:1 1>NUL + echo [!ACTION!] NGEN64 of "!COMPILERSDKPATH!\FSharp.Build.dll" + "!NGEN64!" install "!COMPILERSDKPATH!\FSharp.Build.dll" /queue:1 1>NUL + ) +) + +if "%ACTION%" == "restore" if "!ISADMIN!" == "no" ( + CALL :colorEcho 0E "[!ACTION!] SKIPPED (no admin) Re-enabling strong-name validation of original F# binaries" & echo. + CALL :colorEcho 0E "[!ACTION!] SKIPPED (no admin) Queuing for NGEN of FSI and FSC binaries" & echo. + set /A WARNCOUNT+=2 +) +GOTO :summary + +:checkAvailability +rem Checks whether a given source is available, issues a warning otherwise, SOURCEDIR must be set to the appropriate binaries + +rem This will simultaneously remove the quotes of the original param and add the filename to it, then it is surrounded by quotes again +FOR /F "usebackq tokens=*" %%I IN ('%SOURCEDIR%') DO set SOURCE="%%~fI\*" +if not exist !SOURCE! ( + rem For debug and release deploy it matters, but for restore and backup we don't care + set BIN_AVAILABLE=true + if "!DEPLOY!" == "yes" ( + echo [!ACTION!] Source bindir does not exist: !SOURCE! + CALL :colorEcho 0E "[!ACTION!] Source binaries not found, deploy of %1 skipped" & echo. & set /A WARNCOUNT+=1 + set BIN_AVAILABLE=false + ) + +) else ( + set BIN_AVAILABLE=true ) + +EXIT /B + + +:backupAndOrCopy +rem Creates a backup and copies, depending on whether debug, release, restore or backup is selected + +rem This will simultaneously remove the quotes of the original param and add the filename to it, then it is surrounded by quotes again +FOR /F "usebackq tokens=*" %%I IN ('%2') DO set TARGET="%%~fI\%1" +FOR /F "usebackq tokens=*" %%I IN ('%RESTOREDIR%') DO set BACKUP="%%~fI\%1" +FOR /F "usebackq tokens=*" %%I IN ('%SOURCEDIR%') DO set SOURCE="%%~fI\%1" + +if "%ACTION%" == "backup" ( + rem When backing up, the target becomes the source + + if not exist !TARGET! ( + rem Remove a file from the backup location if it is not part of this SDK install + DEL /f !BACKUP! 1>NUL 2>NUL + ) else ( + rem Otherwise, copy over the original + CALL :copyFile !TARGET! !BACKUP! + ) +) + +if "%ACTION%" == "restore" ( + rem When restoring, the backup location becomes the source + + if not exist !BACKUP! ( + rem If this file didn't exist in the previous installation, we should remove it to prevent confusion of left-over bits + DEL /f !TARGET! 1>NUL 2>NUL + ) else ( + rem Otherwise, copy over the original + CALL :copyFile !BACKUP! !TARGET! + ) +) + +if "%DEPLOY%" == "yes" ( + rem Deploy of debug or release build, depending on selected action + CALL :copyFile !SOURCE! !TARGET! +) + + +EXIT /B + +rem Copies a file and logs errors in red, warnings in yellow +:copyFile +FOR /F "usebackq tokens=*" %%I IN ('%1') DO set SOURCE="%%~fI" +FOR /F "usebackq tokens=*" %%I IN ('%2') DO set TARGET="%%~fI" + +echo [%ACTION%] source: !SOURCE! +echo [%ACTION%] target: !TARGET! +if EXIST !SOURCE! ( + copy /y !SOURCE! !TARGET! 1>NUL 2>copyresult.log + if "!errorlevel!" == "0" echo [!ACTION!] 1 file copied & set /A COPYCOUNT+=1 + if not "!errorlevel!" == "0" ( + set /p COPYRESULT=nul + set COPYRESULT= +) else ( + if "%ACTION%" == "backup" CALL [backup] File not found, nothing to backup + if "%ACTION%" == "restore" CALL :colorEcho 0E "[restore] File not found, not able to restore, possibly it didn't exist originally" & echo. & set /A WARNCOUNT+=1 + if "%DEPLOY%" == "yes" CALL :colorEcho 0C "[!ACTION!] File not found, not able to deploy" & echo. & set /A ERRORCOUNT+=1 +) + +EXIT /B + +rem Creates a folder, if it already exists, it will do nothing, if there's an access-denied, it will set %CREATEFAILED% to true +:tryCreateFolder + +rem Add a backslash safely, by taking care of auxiliary quotes +FOR /F "usebackq tokens=*" %%I IN ('%1') DO set FOLDER_TO_BE_CREATED="%%~fI\" + +if not exist !FOLDER_TO_BE_CREATED! ( + mkdir !FOLDER_TO_BE_CREATED! 2>NUL + if "!errorlevel!" EQU "0" ( + echo [!ACTION!] Created directory !FOLDER_TO_BE_CREATED! + ) else ( + set CREATEFAILED=true + echo Failed to create %1 + CALL :colorEcho 0C "Could not create directory, check access rights or whether a file with that name exists " + echo. + echo. + ) +) + +EXIT /B + +:summary + +echo. +if not "%ACTION%" == "restore" if not "%ACTION%" == "backup" echo Finished installing F# SDK and other bits. The following directories were updated and & echo a backup is written to %RESTOREDIR%. +if "%ACTION%" == "restore" echo Finished restoring original F# SDK and other bits. The following directories were used while & echo restoring a backup from %RESTOREDIR%. +if "%ACTION%" == "backup" echo Finished creating a backup in %RESTOREBASE%. + +echo. +echo Root location: %TOPDIR% +if "!ACTION!" == "debug" echo Debug bin location: %TOPDIR%\debug\net40\bin +if "!ACTION!" == "release" echo Release bin location: %TOPDIR%\release\net40\bin +if "!DEPLOY!" == "no" echo Backup location: %RESTOREBASE% +echo. +echo Target locations used: +echo. +echo Win SDK tools: %WINSDKNETFXTOOLS% +echo Compiler SDK path: %COMPILERSDKPATH% +echo F# compiler main assemblies: %COMPILERMAINASSEMBLIESPATH% +echo Portable profile 7: %COMPILER7ASSEMBLIESPATH% +echo Portable profile 78: %COMPILER78ASSEMBLIESPATH% +echo Portable profile 259: %COMPILER259ASSEMBLIESPATH% +echo Portable profile 47: %COMPILER47ASSEMBLIESPATH% +echo. + +rem Display success, warning, error counts + +if "%ACTION%" == "backup" SET VERB=backed up +if "%ACTION%" == "restore" SET VERB=restored +if "%DEPLOY%" == "yes" SET VERB=deployed +CALL :colorEcho 0A "A total of %COPYCOUNT% file(s) were %VERB%." & echo. + +if %ERRORCOUNT% equ 1 CALL :colorEcho 0C "%ERRORCOUNT% error reported, see log" & echo. +if %ERRORCOUNT% gtr 1 CALL :colorEcho 0C "%ERRORCOUNT% errors reported, see log" & echo. +if %ERRORCOUNT% equ 0 CALL :colorEcho 0A "No errors reported" & echo. + +if %WARNCOUNT% equ 1 CALL :colorEcho 0E "%WARNCOUNT% warning reported, see log" & echo. +if %WARNCOUNT% gtr 1 CALL :colorEcho 0E "%WARNCOUNT% warnings reported, see log" & echo. +if %WARNCOUNT% equ 0 CALL :colorEcho 0A "No warnings reported" & echo. + +rem Return non-zero error code for use-cases where this script is called from other scripts +if %ERRORCOUNT% gtr 0 EXIT /B 1 +EXIT /B 0 + +GOTO :EOF + +:exitFailDir + +echo. +CALL :colorEcho 0C "One or more directories failed to be created. No files have been copied." & echo. +echo. +echo Possible causes include: +echo - Insufficient rights to create directories in this folder +echo - A file with that name already exists +echo. +echo No error is raised if the directory exists. +echo No files were copied or backed up. +echo. + +rem Return non-zero error code for use-cases where this script is called from other scripts +EXIT /B 1 + +:checkPrequisites +rem Whether or not we have administrator rights + +SET ISADMIN=yes + +rem The error level of NET SESSION is set to 2 when you don't have administrator rights, simplest hack +net sessions 1>NUL 2>NUL +if %ERRORLEVEL% GTR 0 ( + SET ISADMIN=no + CALL :colorEcho 0E "[!ACTION!] Started without administrator access, strong-naming will not be adjusted, reg-keys not changed" & echo. + SET /A WARNCOUNT+=1 +) + +EXIT /B + + +rem See: https://stackoverflow.com/a/21666354/111575 +rem Prevent accidentally entering the colorEcho label +GOTO :EOF +:colorEcho + "%~2" +findstr /v /a:%1 /R "^$" "%~2" nul +del "%~2" > nul 2>&1i \ No newline at end of file -- GitLab