Ver código fonte

A batch file for quick builds

The batch file allows for the project to be built quickly using the built
in tools of Windows to get the dependencies needed. It automatically
installs CMake if not found. If building with MinGW it installs Python and
uses the script to get MInGW.

A complete fire and forget script to build the project which might help
complete newbies.
Matt Clarkson 10 anos atrás
pai
commit
ad0f624c79
1 arquivos alterados com 866 adições e 0 exclusões
  1. 866 0
      build.cmd

+ 866 - 0
build.cmd

@@ -0,0 +1,866 @@
+:: Make sure the extensions are enabled
+@verify other 2>nul
+@setlocal EnableDelayedExpansion
+@if errorlevel 1 (
+  call :print_usage "Failed to enable extensions"
+  exit /b 1
+)
+
+::Change the code page to unicode
+@chcp 65001 1>nul 2>nul
+@if errorlevel 1 (
+  call :print_usage "Failed to change the code page to unicode"
+  exit /b 1
+)
+
+:: Set up some global variables
+@set project=civetweb
+@set "script_name=%~nx0"
+@set "script_folder=%~dp0"
+@set "script_folder=%script_folder:~0,-1%"
+@set "output_path=%script_folder%\output"
+@set "build_path=%output_path%\build"
+@set "install_path=%output_path%\install"
+@set build_shared=OFF
+@set build_type=Release
+@set dependency_path=%TEMP%\%project%-build-dependencies
+
+:: Check the command line parameters
+@set logging_level=1
+@set "options=%* "
+@if not "!options!"=="!options:/? =!" set usage="Convenience script to build %project% with CMake"
+@for %%a in (%options%) do @(
+  @set arg=%%~a
+  @set arg=!arg: =!
+  @set one=!arg:~0,1!
+  @set two=!arg:~0,2!
+  @if /i [!arg!] == [/q] set quiet=true
+  @if /i [!two!] == [/v] call :verbosity "!arg!"
+  @if /i [!arg!] == [/s] set build_shared=ON
+  @if /i [!arg!] == [/d] set build_type=Debug
+  @if /i not [!one!] == [/] (
+    if not defined generator (
+      set generator=!arg!
+    ) else (
+      set usage="Too many generators: !method! !arg!" ^
+                "There should only be one generator parameter"
+    )
+  )
+)
+@if defined quiet (
+  set logging_level=0
+)
+@if not defined generator (
+  set generator=MSVC
+)
+@if /i not [%generator%] == [MinGW] (
+  if /i not [%generator%] == [MSVC] (
+    call :print_usage "Invalid argument: %generator%"
+    exit /b 1
+  )
+)
+
+:: Set up the logging
+@set log_folder=%output_path%\logs
+@call :iso8601 timestamp
+@set log_path=%log_folder%\%timestamp%.log
+@set log_keep=10
+
+:: Only keep a certain amount of logs
+@set /a "log_keep=log_keep-1"
+@if not exist %log_folder% @mkdir %log_folder%
+@for /f "skip=%log_keep%" %%f in ('dir /b /o-D /tc %log_folder%') do @(
+  call :log 4 "Removing old log file %log_folder%\%%f"
+  del %log_folder%\%%f
+)
+
+:: Set up some more global variables
+@call :architecture arch
+@call :windows_version win_ver win_ver_major win_ver_minor win_ver_rev
+@call :script_source script_source
+@if [%script_source%] == [explorer] (
+  set /a "logging_level=logging_level+1"
+)
+
+:: Print the usage or start the script
+@set exit_code=0
+@if defined usage (
+  call :print_usage %usage%
+) else (
+  call :main
+  @if errorlevel 1 (
+    @call :log 0 "Failed to build the %project% project"
+    @set exit_code=1
+  )
+)
+
+:: Tell the user where the built files are
+@call :log 5
+@call :log 0 "The built files are available in %install_path%"
+
+:: Stop the script if the user double clicked
+@if [%script_source%] == [explorer] (
+  pause
+)
+
+@exit /b %exit_code%
+@endlocal
+@goto :eof
+
+:: -------------------------- Functions start here ----------------------------
+
+:main - Main function that performs the build
+@setlocal
+@call :log 6
+@call :log 2 "Welcome to the %project% build script"
+@call :log 6 "------------------------------------"
+@call :log 6
+@call :log 2 "This script builds the project using CMake"
+@call :log 6
+@call :log 2 "Generating %generator%..."
+@call :log 6
+@set methods=dependencies ^
+             generate ^
+             build ^
+             install
+@for %%m in (%methods%) do @(
+  call :log 3 "Excuting the '%%m' method"
+  call :log 8
+  call :%%~m
+  if errorlevel 1 (
+    call :log 0 "Failed to complete the '%%~m' dependency routine"
+    call :log 0 "View the log at %log_path%"
+    exit /b 1
+  )
+)
+@call :log 6 "------------------------------------"
+@call :log 2 "Build complete"
+@call :log 6
+@endlocal
+@goto :eof
+
+:print_usage - Prints the usage of the script
+:: %* - message to print, each argument on it's own line
+@setlocal
+@for %%a in (%*) do @echo.%%~a
+@echo.
+@echo.build [/?][/v[v...]^|/q][MinGW^|MSVC]
+@echo.
+@echo.  [MinGW^|(MSVC)]
+@echo.              Builds the library with one of the compilers
+@echo.  /s          Builds shared libraries
+@echo.  /d          Builds a debug variant of the project
+@echo.  /v          Sets the output to be more verbose
+@echo.  /v[v...]    Extra verbosity, /vv, /vvv, etc
+@echo.  /q          Quiets the output
+@echo.  /?          Shows this usage message
+@echo.
+@endlocal
+@goto :eof
+
+:dependencies - Installs any prerequisites for the build
+@setlocal EnableDelayedExpansion
+@if errorlevel 1 (
+  call :log 0 "Failed to enable extensions"
+  exit /b 1
+)
+@call :log 5
+@call :log 0 "Installing dependencies for %generator%"
+@if /i [%generator%] == [MinGW] (
+  call :mingw compiler_path
+  @if errorlevel 1 (
+    @call :log 5
+    @call :log 0 "Failed to find MinGW"
+    @exit /b 1
+  )
+  set "PATH=!compiler_path!;%PATH%"
+  @call :find_in_path gcc_executable gcc.exe
+  @if errorlevel 1 (
+    @call :log 5
+    @call :log 0 "Failed to find gcc.exe"
+    @exit /b 1
+  )
+)
+@if [%reboot_required%] equ [1] call :reboot
+@endlocal & set "PATH=%PATH%"
+@goto :eof
+
+:generate - Uses CMake to generate the build files
+@setlocal EnableDelayedExpansion
+@if errorlevel 1 (
+  call :log 0 "Failed to enable extensions"
+  exit /b 1
+)
+@call :log 5
+@call :log 0 "Generating CMake files for %generator%"
+@call :cmake cmake_executable
+@if errorlevel 1 (
+  @call :log 5
+  @call :log 0 "Need CMake to create the build files"
+  @exit /b 1
+)
+@if /i [%generator%] == [MinGW] @(
+  @set "generator_var=-G "MinGW Makefiles^""
+)
+@if /i [%generator%] == [MSVC] @(
+  rem We could figure out the correct MSVS generator here
+)
+@call :iso8601 iso8601
+@set output=%temp%\cmake-%iso8601%.log
+@if not exist %build_path% mkdir %build_path%
+@cd %build_path%
+@"%cmake_executable%" ^
+  !generator_var! ^
+  -DCMAKE_BUILD_TYPE=!build_type! ^
+  -DBUILD_SHARED_LIBS=!build_shared! ^
+  "%script_folder%" > "%output%"
+@if errorlevel 1 (
+  @call :log 5
+  @call :log 0 "Failed to generate build files with CMake"
+  @call :log_append "%output%"
+  @cd %script_folder%
+  @exit /b 1
+)
+@cd %script_folder%
+@endlocal
+@goto :eof
+
+:build - Builds the library
+@setlocal EnableDelayedExpansion
+@if errorlevel 1 (
+  call :log 0 "Failed to enable extensions"
+  exit /b 1
+)
+@call :log 5
+@call :log 0 "Building %project% with %generator%"
+@if /i [%generator%] == [MinGW] @(
+  @call :find_in_path mingw32_make_executable mingw32-make.exe
+  @if errorlevel 1 (
+    @call :log 5
+    @call :log 0 "Failed to find mingw32-make"
+    @exit /b 1
+  )
+  @set "build_command=^"!mingw32_make_executable!^" all test"
+)
+@if /i [%generator%] == [MSVC] @(
+  @call :msbuild msbuild_executable
+  @if errorlevel 1 (
+    @call :log 5
+    @call :log 0 "Failed to find MSBuild"
+    @exit /b 1
+  )
+  @set "build_command=^"!msbuild_executable!^" /m:4 /p:Configuration=%build_type% %project%.sln"
+)
+@if not defined build_command (
+  @call :log 5
+  @call :log 0 "No build command for %generator%"
+  @exit /b 1
+)
+@cd %build_path%
+@call :iso8601 iso8601
+@set output=%temp%\build-%iso8601%.log
+@call :log 7
+@call :log 2 "Build command: %build_command:"=%"
+@%build_command% > "%output%"
+@if errorlevel 1 (
+  @call :log_append "%output%"
+  @call :log 5
+  @call :log 0 "Failed to complete the build"
+  @exit /b 1
+)
+@call :log_append "%output%"
+@cd %script_folder%
+@endlocal
+@goto :eof
+
+:install - Installs the built files
+@setlocal
+@call :log 5
+@call :log 0 "Installing built files"
+@call :cmake cmake_executable
+@if errorlevel 1 (
+  @call :log 5
+  @call :log 0 "Need CMake to install the built files"
+  @exit /b 1
+)
+@call :iso8601 iso8601
+@set output=%temp%\install-%iso8601%.log
+@"%cmake_executable%" ^
+  "-DCMAKE_INSTALL_PREFIX=%install_path%" ^
+  -P "%build_path%/cmake_install.cmake" ^
+  > "%output%"
+@if errorlevel 1 (
+  @call :log_append "%output%"
+  @call :log 5
+  @call :log 0 "Failed to install the files"
+  @exit /b 1
+)
+@call :log_append "%output%"
+@endlocal
+@goto :eof
+
+:script_source - Determines if the script was ran from the cli or explorer
+:: %1 - The return variable [cli|explorer]
+@verify other 2>nul
+@setlocal EnableDelayedExpansion
+@if errorlevel 1 (
+  call :log 0 "Failed to enable extensions"
+  exit /b 1
+)
+@call :log 3 "Attempting to detect the script source"
+@echo "The invocation command was: '%cmdcmdline%'" >> %log_path%
+@for /f "tokens=1-3,*" %%a in ("%cmdcmdline%") do @(
+  set cmd=%%~a
+  set arg1=%%~b
+  set arg2=%%~c
+  set rest=%%~d
+)
+@set quote="
+@if "!arg2:~0,1!" equ "!quote!" (
+  if "!arg2:~-1!" neq "!quote!" (
+    set "arg2=!arg2:~1!"
+  )
+)
+@call :log 4 "cmd  = %cmd%"
+@call :log 4 "arg1 = %arg1%"
+@call :log 4 "arg2 = %arg2%"
+@call :log 4 "rest = %rest%"
+@call :log 4 "src  = %~f0"
+@if /i "%arg2%" == "call" (
+  set script_source=cli
+) else (
+  @if /i "%arg1%" == "/c" (
+    set script_source=explorer
+  ) else (
+    set script_source=cli
+  )
+)
+@call :log 3 "The script was invoked from %script_source%"
+@endlocal & set "%~1=%script_source%"
+@goto :eof
+
+:architecture - Finds the system architecture
+:: %1 - The return variable [x86|x86_64]
+@setlocal
+@call :log 3 "Determining the processor architecture"
+@set "key=HKLM\System\CurrentControlSet\Control\Session Manager\Environment"
+@set "var=PROCESSOR_ARCHITECTURE"
+@for /f "skip=2 tokens=2,*" %%a in ('reg query "%key%" /v "%var%"') do @set "arch=%%b"
+@if "%arch%" == "AMD64" set arch=x86_64
+@call :log 4 "arch = %arch%"
+@endlocal & set "%~1=%arch%"
+@goto :eof
+
+:md5 - Gets the MD5 checksum for a file
+:: %1 - The hash
+:: %2 - The file path
+@setlocal
+@set var=%~1
+@set file_path=%~2
+@if [%var%] == [] exit /b 1
+@if "%file_path%" == "" exit /b 1
+@if not exist "%file_path%" exit /b 1
+@for /f "skip=3 tokens=1,*" %%a in ('powershell Get-FileHash -Algorithm MD5 "'%file_path%'"') do @set hash=%%b
+@if not defined hash (
+  call :log 6
+  call :log 0 "Failed to get MD5 hash for %file_path%"
+  exit /b 1
+)
+@endlocal & set "%var%=%hash: =%"
+@goto :eof
+
+:windows_version - Checks the windows version
+:: %1 - The windows version
+:: %2 - The major version number return variable
+:: %3 - The minor version number return variable
+:: %4 - The revision version number return variable
+@setlocal
+@call :log 3 "Retrieving the Windows version"
+@for /f "tokens=2 delims=[]" %%x in ('ver') do @set win_ver=%%x
+@set win_ver=%win_ver:Version =%
+@set win_ver_major=%win_ver:~0,1%
+@set win_ver_minor=%win_ver:~2,1%
+@set win_ver_rev=%win_ver:~4%
+@call :log 4 "win_ver = %win_ver%"
+@endlocal & set "%~1=%win_ver%" ^
+          & set "%~2=%win_ver_major%" ^
+          & set "%~3=%win_ver_minor%" ^
+          & set "%~4=%win_ver_rev%"
+@goto :eof
+
+:find_in_path - Finds a program of file in the PATH
+@setlocal
+@set var=%~1
+@set file=%~2
+@if [%var%] == [] exit /b 1
+@if [%file%] == [] exit /b 1
+@call :log 3 "Searching PATH for %file%"
+@for %%x in ("%file%") do @set "file_path=%%~f$PATH:x"
+@if not defined file_path exit /b 1
+@endlocal & set "%var%=%file_path%"
+@goto :eof
+
+:administrator_check - Checks for administrator priviledges
+@setlocal
+@call :log 2 "Checking for administrator priviledges"
+@set "key=HKLM\Software\VCA\Tool Chain\Admin Check"
+@reg add "%key%" /v Elevated /t REG_DWORD /d 1 /f > nul 2>&1
+@if errorlevel 1 exit /b 1
+@reg delete "%key%" /va /f > nul 2>&1
+@endlocal
+@goto :eof
+
+:log_append - Appends another file into the current logging file
+:: %1 - the file_path to the file to concatenate
+@setlocal
+@set file_path=%~1
+@if [%file_path%] == [] exit /b 1
+@call :log 3 "Appending to log: %file_path%"
+@call :iso8601 iso8601
+@set temp_log=%temp%\append-%iso8601%.log
+@call :log 4 "Using temp file %temp_log%"
+@type "%log_path%" "%file_path%" > "%temp_log%" 2>nul
+@move /y "%temp_log%" "%log_path%" 1>nul
+@del "%file_path% 2>nul
+@del "%temp_log% 2>nul
+@endlocal
+@goto :eof
+
+:iso8601 - Returns the current time in ISO8601 format
+:: %1 - the return variable
+:: %2 - format [extended|basic*]
+:: iso8601 - contains the resulting timestamp
+@setlocal
+@wmic Alias /? >NUL 2>&1 || @exit /b 1
+@set "var=%~1"
+@if "%var%" == "" @exit /b 1
+@set "format=%~2"
+@if "%format%" == "" set format=basic
+@for /F "skip=1 tokens=1-6" %%g IN ('wmic Path Win32_UTCTime Get Day^,Hour^,Minute^,Month^,Second^,Year /Format:table') do @(
+  @if "%%~l"=="" goto :iso8601_done
+  @set "yyyy=%%l"
+  @set "mm=00%%j"
+  @set "dd=00%%g"
+  @set "hour=00%%h"
+  @set "minute=00%%i"
+  @set "seconds=00%%k"
+)
+:iso8601_done
+@set mm=%mm:~-2%
+@set dd=%dd:~-2%
+@set hour=%hour:~-2%
+@set minute=%minute:~-2%
+@set seconds=%seconds:~-2%
+@if /i [%format%] == [extended] (
+  set iso8601=%yyyy%-%mm%-%dd%T%hour%:%minute%:%seconds%Z
+) else (
+  if /i [%format%] == [basic] (
+    set iso8601=%yyyy%%mm%%dd%T%hour%%minute%%seconds%Z
+  ) else (
+    @exit /b 1
+  )
+)
+@set iso8601=%iso8601: =0%
+@endlocal & set %var%=%iso8601%
+@goto :eof
+
+:verbosity - Processes the verbosity parameter '/v[v...]
+:: %1 - verbosity given on the command line
+:: logging_level - set to the number of v's
+@setlocal
+@set logging_level=0
+@set verbosity=%~1
+:verbosity_loop
+@set verbosity=%verbosity:~1%
+@if not [%verbosity%] == [] @(
+  set /a "logging_level=logging_level+1"
+  goto verbosity_loop
+)
+@endlocal & set logging_level=%logging_level%
+@goto :eof
+
+:log - Logs a message, depending on verbosity
+:: %1 - level
+::       [0-4] for CLI logging
+::       [5-9] for GUI logging
+:: %2 - message to print
+@setlocal
+@set "level=%~1"
+@set "msg=%~2"
+@if "%log_folder%" == "" (
+  echo Logging was used to early in the script, log_folder isn't set yet
+  goto :eof
+)
+@if "%log_path%" == "" (
+  echo Logging was used to early in the script, log_path isn't set yet
+  goto :eof
+)
+@if not exist "%log_folder%" mkdir "%log_folder%"
+@if not exist "%log_path%" echo. 1>nul 2>"%log_path%"
+@echo.%msg% >> "%log_path%"
+@if %level% geq 5 (
+  @if [%script_source%] == [explorer] (
+    set /a "level=level-5"
+  ) else (
+    @goto :eof
+  )
+)
+@if "%logging_level%" == "" (
+  echo Logging was used to early in the script, logging_level isn't set yet
+  goto :eof
+)
+@if %logging_level% geq %level% echo.%msg% 1>&2
+@endlocal
+@goto :eof
+
+
+:start_browser - Opens the default browser to a URL
+:: %1 - the url to open
+@setlocal
+@set url=%~1
+@call :log 4 "Opening default browser: %url%"
+@start %url%
+@endlocal
+@goto :eof
+
+:find_cmake - Finds cmake on the command line or in the registry
+:: %1 - the cmake file path
+@setlocal
+@set var=%~1
+@if [%var%] == [] exit /b 1
+@call :log 6
+@call :log 6 "Finding CMake"
+@call :log 6 "--------------"
+@call :find_in_path cmake_executable cmake.exe
+@if not errorlevel 1 goto found_cmake
+@for /l %%i in (5,-1,0) do @(
+@for /l %%j in (9,-1,0) do @(
+@for /l %%k in (9,-1,0) do @(
+@for %%l in (HKCU HKLM) do @(
+@for %%m in (SOFTWARE SOFTWARE\Wow6432Node) do @(
+  @reg query "%%l\%%m\Kitware\CMake %%i.%%j.%%k" /ve > nul 2>nul
+  @if not errorlevel 1 (
+    @for /f "skip=2 tokens=2,*" %%a in ('reg query "%%l\%%m\Kitware\CMake %%i.%%j.%%k" /ve') do @(
+      @if exist "%%b\bin\cmake.exe" (
+        @set "cmake_executable=%%b\bin\cmake.exe"
+        goto found_cmake
+      )
+    )
+  )
+)))))
+@call :log 5
+@call :log 0 "Failed to find cmake"
+@exit /b 1
+:found_cmake
+@endlocal & set "%var%=%cmake_executable%"
+@goto :eof
+
+:cmake - Finds cmake and installs it if necessary
+:: %1 - the cmake file path
+@setlocal
+@set var=%~1
+@if [%var%] == [] exit /b 1
+@call :log 6
+@call :log 6 "Checking for CMake"
+@call :log 6 "------------------"
+@call :find_cmake cmake_executable cmake.exe
+@if not errorlevel 1 goto got_cmake
+@set checksum=C00267A3D3D9619A7A2E8FA4F46D7698
+@set version=3.2.2
+@call :install_nsis cmake http://www.cmake.org/files/v%version:~0,3%/cmake-%version%-win32-x86.exe %checksum%
+@if errorlevel 1 (
+  call :log 5
+  call :log 0 "Failed to install cmake"
+  @exit /b 1
+)
+@call :find_cmake cmake_executable cmake.exe
+@if not errorlevel 1 goto got_cmake
+@call :log 5
+@call :log 0 "Failed to check for cmake"
+@exit /b 1
+:got_cmake
+@endlocal & set "%var%=%cmake_executable%"
+@goto :eof
+
+:mingw - Finds MinGW, installing it if needed
+:: %1 - the compiler path that should be added to PATH
+@setlocal EnableDelayedExpansion
+@if errorlevel 1 (
+  @call :log 5
+  @call :log 0 "Failed to enable extensions"
+  @exit /b 1
+)
+@set var=%~1
+@if [%var%] == [] exit /b 1
+@call :log 6
+@call :log 6 "Checking for MinGW"
+@call :log 6 "------------------"
+@call :find_in_path gcc_executable gcc.exe
+@if not errorlevel 1 (
+  @for %%a in ("%gcc_executable%") do @set "compiler_path=%%~dpa"
+  goto got_mingw
+)
+@call :log 7
+@call :log 2 "Downloading MinGW"
+@if %logging_level% leq 1 set "logging=/q"
+@if %logging_level% gtr 1 set "logging=/v"
+@set output_path=
+@for /f %%a in ('call
+    "%script_folder%\mingw.cmd"
+    %logging%
+    /arch "%arch%"
+    "%dependency_path%"'
+) do @set "compiler_path=%%a\"
+@if not defined compiler_path (
+  @call :log_append "%output%"
+  @call :log 5
+  @call :log 0 "Failed to download MinGW"
+  @exit /b 1
+)
+:got_mingw
+@call :log 5
+@call :log 0 "Found MinGW: %compiler_path%gcc.exe"
+@endlocal & set "%var%=%compiler_path%"
+@goto :eof
+
+:msbuild - Finds MSBuild
+:: %1 - the path to MSBuild executable
+@setlocal
+@set var=%~1
+@if [%var%] == [] exit /b 1
+@call :find_in_path msbuild_executable msbuild.exe
+@if not errorlevel 1 goto got_msbuild
+@for /l %%i in (20,-1,4) do @(
+@for /l %%j in (9,-1,0) do @(
+@for %%k in (HKCU HKLM) do @(
+@for %%l in (SOFTWARE SOFTWARE\Wow6432Node) do @(
+  @reg query "%%k\%%l\Microsoft\MSBuild\%%i.%%j" /v MSBuildOverrideTasksPath > nul 2>nul
+  @if not errorlevel 1 (
+    @for /f "skip=2 tokens=2,*" %%a in ('reg query "%%k\%%l\Microsoft\MSBuild\%%i.%%j" /v MSBuildOverrideTasksPath') do @(
+      @if exist "%%bmsbuild.exe" (
+        @set "msbuild_executable=%%bmsbuild.exe"
+        goto got_msbuild
+      )
+    )
+  )
+))))
+@call :log 5
+@call :log 0 "Failed to check for MSBuild"
+@exit /b 1
+:got_msbuild
+@endlocal & set "%var%=%msbuild_executable%"
+@goto :eof
+
+:download - Downloads a file from the internet
+:: %1 - the url of the file to download
+:: %2 - the file to download to
+:: %3 - the MD5 checksum of the file (optional)
+@setlocal EnableDelayedExpansion
+@if errorlevel 1 (
+  call :print_usage "Failed to enable extensions"
+  exit /b 1
+)
+@set url=%~1
+@set file_path=%~2
+@set checksum=%~3
+@for %%a in (%file_path%) do @set dir_path=%%~dpa
+@for %%a in (%file_path%) do @set file_name=%%~nxa
+@if [%url%] == [] exit /b 1
+@if [%file_path%] == [] exit /b 1
+@if [%dir_path%] == [] exit /b 1
+@if [%file_name%] == [] exit /b 1
+@if not exist "%dir_path%" mkdir "%dir_path%"
+@call :log 1 "Downloading %url%"
+@call :iso8601 iso8601
+@set temp_path=%temp%\download-%iso8601%-%file_name%
+@call :log 3 "Using temp file %temp_path%"
+@powershell Invoke-WebRequest "%url%" -OutFile %temp_path%
+@if errorlevel 1 (
+  call :log 0 "Failed to download %url%"
+  exit /b 1
+)
+@if [%checksum%] neq [] (
+  @call :log 4 "Checking %checksum% against %temp_path%"
+  @call :md5 hash "%temp_path%"
+  if "!hash!" neq "%checksum%" (
+    call :log 0 "Failed to match checksum: %temp_path%"
+    call :log 0 "Hash    : !hash!"
+    call :log 0 "Checksum: %checksum%"
+    exit /b 1
+  ) else (
+    call :log 3 "Checksum matched: %temp_path%"
+    call :log 3 "Hash    : !hash!"
+    call :log 3 "Checksum: %checksum%"
+  )
+)
+@call :log 4 "Renaming %temp_path% to %file_path%"
+@move /y "%temp_path%" "%file_path%" 1>nul
+@endlocal
+@goto :eof
+
+:install_msi - Installs a dependency from an Microsoft Installer package (.msi)
+:: %1 - [string] name of the project to install
+:: %2 - The location of the .msi, a url must start with 'http://' or file_path
+:: %3 - The checksum of the msi (optional)
+@setlocal
+@set name=%~1
+@set file_path=%~2
+@set checksum=%~3
+@set msi=%~nx2
+@set msi_path=%dependency_path%\%msi%
+@if [%name%] == [] exit /b 1
+@if [%file_path%] == [] exit /b 1
+@if [%msi%] == [] exit /b 1
+@if [%msi_path%] == [] exit /b 1
+@for %%x in (msiexec.exe) do @set "msiexec_path=%%~f$PATH:x"
+@if "msiexec_path" == "" (
+  call :log 0 "Failed to find the Microsoft package installer (msiexec.exe)"
+  call :log 6
+  call :log 0 "Please install it from the Microsoft Download center"
+  call :log 6
+  choice /C YN /T 60 /D N /M "Would you like to go there now?"
+  if !errorlevel! equ 1 call :start_browser ^
+    "http://search.microsoft.com/DownloadResults.aspx?q=Windows+Installer"
+  exit /b 1
+)
+@call :log 6
+@call :log 1 "Installing the '%name%' dependency"
+@call :log 6 "-------------------------------------"
+@call :administrator_check
+@if errorlevel 1 (
+  call :log 0 "You must run %~nx0 in elevated mode to install '%name%'"
+  call :log 5 "Right-Click and select 'Run as Administrator'
+  call :log 0 "Install the dependency manually by running %file_path%"
+  @exit /b 740
+)
+@if [%file_path:~0,4%] == [http] (
+  if not exist "%msi_path%" (
+    call :download "%file_path%" "%msi_path%" %checksum%
+    if errorlevel 1 (
+      call :log 0 "Failed to download the %name% dependency"
+      exit /b 1
+    )
+  )
+) else (
+  call :log 2 "Copying MSI %file_path% to %msi_path%"
+  call :log 7
+  if not exist "%msi_path%" (
+    xcopy /q /y /z "%file_path%" "%msi_path%" 1>nul
+    if errorlevel 1 (
+      call :log 0 "Failed to copy the Microsoft Installer"
+      exit /b 1
+    )
+  )
+)
+@call :log 1 "Running the %msi%"
+@call :log 6
+@set msi_log=%temp%\msiexec-%timestamp%.log
+@call :log 3 "Logging to: %msi_log%"
+@msiexec /i "%msi_path%" /passive /log "%msi_log%" ALLUSERS=1
+@set msi_errorlevel=%errorlevel%
+@call :log_append "%msi_log%"
+@if %msi_errorlevel% equ 0 goto install_msi_success
+@if %msi_errorlevel% equ 3010 goto install_msi_success_reboot
+@if %msi_errorlevel% equ 1641 goto install_msi_success_reboot
+@if %msi_errorlevel% equ 3015 goto install_msi_in_progress_reboot
+@if %msi_errorlevel% equ 1615 goto install_msi_in_progress_reboot
+@call :log 0 "Microsoft Installer failed: %msi_errorlevel%"
+@call :log 0 "Install the dependency manually by running %msi_path%"
+@exit /b 1
+:install_msi_in_progress_reboot
+@call :log 0 "The installation requires a reboot to continue"
+@call :log 5
+@call :reboot
+@exit /b 1
+:install_msi_success_reboot
+@call :log 3 "The installation requires a reboot to be fully functional"
+@set reboot_required=1
+:install_msi_success
+@call :log 2 "Successfully installed %name%"
+@call :log 7
+@endlocal & set reboot_required=%reboot_required%
+@goto :eof
+
+:install_nsis - Installs a dependency from an Nullsoft Installer package (.exe)
+:: %1 - [string] name of the project to install
+:: %2 - The location of the .exe, a url must start with 'http://' or file_path
+:: %3 - The checksum of the exe (optional)
+@setlocal
+@set name=%~1
+@set file_path=%~2
+@set checksum=%~3
+@set exe=%~nx2
+@set exe_path=%dependency_path%\%exe%
+@if [%name%] == [] exit /b 1
+@if [%file_path%] == [] exit /b 1
+@if [%exe%] == [] exit /b 1
+@if [%exe_path%] == [] exit /b 1
+@call :log 6
+@call :log 1 "Installing the '%name%' dependency"
+@call :log 6 "-------------------------------------"
+@call :administrator_check
+@if errorlevel 1 (
+  call :log 0 "You must run %~nx0 in elevated mode to install '%name%'"
+  call :log 5 "Right-Click and select 'Run as Administrator'
+  call :log 0 "Install the dependency manually by running %file_path%"
+  @exit /b 740
+)
+@if [%file_path:~0,4%] == [http] (
+  if not exist "%exe_path%" (
+    call :download "%file_path%" "%exe_path%" %checksum%
+    if errorlevel 1 (
+      call :log 0 "Failed to download the %name% dependency"
+      exit /b 1
+    )
+  )
+) else (
+  call :log 2 "Copying installer %file_path% to %exe_path%"
+  call :log 7
+  if not exist "%exe_path%" (
+    xcopy /q /y /z "%file_path%" "%exe_path%" 1>nul
+    if errorlevel 1 (
+      call :log 0 "Failed to copy the Nullsoft Installer"
+      exit /b 1
+    )
+  )
+)
+@call :log 1 "Running the %exe%"
+@call :log 6
+@"%exe_path%" /S
+@set nsis_errorlevel=%errorlevel%
+@if %nsis_errorlevel% equ 0 goto install_nsis_success
+@if %nsis_errorlevel% equ 3010 goto install_nsis_success_reboot
+@if %nsis_errorlevel% equ 1641 goto install_nsis_success_reboot
+@if %nsis_errorlevel% equ 3015 goto install_nsis_in_progress_reboot
+@if %nsis_errorlevel% equ 1615 goto install_nsis_in_progress_reboot
+@call :log 0 "Nullsoft Installer failed: %nsis_errorlevel%"
+@call :log 0 "Install the dependency manually by running %exe_path%"
+@exit /b 1
+:install_nsis_in_progress_reboot
+@call :log 0 "The installation requires a reboot to continue"
+@call :log 5
+@call :reboot
+@exit /b 1
+:install_nsis_success_reboot
+@call :log 3 "The installation requires a reboot to be fully functional"
+@set reboot_required=1
+:install_nsis_success
+@call :log 2 "Successfully installed %name%"
+@call :log 7
+@endlocal & set reboot_required=%reboot_required%
+@goto :eof
+
+:reboot - Asks the user if they would like to reboot then stops the script
+@setlocal
+@call :log 6 "-------------------------------------------"
+@choice /C YN /T 60 /D N /M "The %method% requires a reboot, reboot now?"
+@set ret=%errorlevel%
+@call :log 6
+@if %ret% equ 1 (
+  @shutdown /r
+) else (
+  @call :log 0 "You will need to reboot to complete the %method%"
+  @call :log 5
+)
+@endlocal
+@goto :eof