diff --git a/.github/workflows/msys.yml b/.github/workflows/msys.yml
index 35881c7a050393c9637446fd164d4bf4f5470eba..70dcb9697e697bc0b66212ece4ec9995368af4e7 100644
--- a/.github/workflows/msys.yml
+++ b/.github/workflows/msys.yml
@@ -9,6 +9,9 @@ jobs:
         shell: msys2 {0}
     steps:
       - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+
       - uses: msys2/setup-msys2@v2
         with:
           msystem: MINGW64
diff --git a/git_version.h.in b/git_version.h.in
new file mode 100644
index 0000000000000000000000000000000000000000..3d6ece787a7d7d6160b151a6ba96387d6f81cf62
--- /dev/null
+++ b/git_version.h.in
@@ -0,0 +1,6 @@
+#define BUILD_GIT_VERSION_NUMBER @git_version_number@
+#define BUILD_GIT_VERSION_STRING @git_version_string@
+#define RELEASE_VERSION @release_version@
+#define TAGGED_RELEASE 0
+#define INSTALLER_VERSION "0.0.0"
+#define RESOURCE_BASE_VERSION 0, 0, 0
diff --git a/meson.build b/meson.build
index 8effd1e33101f64324f935c8a02b38d8a42b6768..fd558be4d96908f11fee9f2130fa97b3ae73a2b1 100644
--- a/meson.build
+++ b/meson.build
@@ -1,6 +1,6 @@
 project('Aegisub', ['c', 'cpp'],
         license: 'BSD-3-Clause',
-        meson_version: '>=0.55.0',
+        meson_version: '>=0.56.0',
         default_options: ['cpp_std=c++11', 'buildtype=debugoptimized'],
         version: '3.3.3')
 
@@ -14,17 +14,48 @@ if host_machine.system() == 'windows'
     endif
 endif
 
-if host_machine.system() == 'windows' 
-    version_sh = find_program('tools/version.ps1')
+version_inc = include_directories('.')
+
+conf_data = configuration_data()
+
+git_describe = run_command('git', 'describe')
+
+if git_describe.returncode() == 0
+    last_svn_hash = '16cd907fe7482cb54a7374cd28b8501f138116be'
+    svn_revision = 6962
+    git_count = run_command('git', 'rev-list', '--count', last_svn_hash + '..HEAD')
+    git_release = run_command('git', 'describe', '--exact-match')
+
+    git_describe_str = git_describe.stdout().strip().substring(1)
+    git_version_number = svn_revision + git_count.stdout().strip().to_int()
+
+    if git_release.returncode() == 0
+        git_version_string = git_describe_str
+    else
+        branch_cmd = run_command('git', 'symbolic-ref', 'HEAD')
+        branch = branch_cmd.stdout().strip().substring(11)
+        git_hash_cmd = run_command('git', 'rev-parse', '--short', 'HEAD')
+        git_hash = git_hash_cmd.stdout().strip()
+
+        git_version_template = '@0@-@1@-@2@'
+        git_version_string = git_version_template.format(git_version_number,
+                                                         branch,
+                                                         git_hash)
+    endif
 else
-    version_sh = find_program('tools/version.sh')
+    warning('git describe failed. Using default version number (' + meson.project_version() + ')')
+    git_version_number = 42
+    git_describe_str = meson.project_version()
+    git_version_string = meson.project_version()
 endif
-version_inc = include_directories('.')
-version_h = custom_target('git_version.h',
-                          command: [version_sh, meson.current_build_dir(), meson.current_source_dir()],
-                          build_by_default: true,
-                          build_always_stale: true, # has internal check whether target file will be refreshed
-                          output: ['git_version.h'])
+
+conf_data.set('git_version_number', git_version_number)
+conf_data.set_quoted('release_version', git_describe_str)
+conf_data.set_quoted('git_version_string', git_version_string)
+
+configure_file(input: 'git_version.h.in',
+               output: 'git_version.h',
+               configuration: conf_data)
 
 if host_machine.system() == 'darwin' and get_option('build_osx_bundle')
     prefix = meson.current_build_dir() / 'Aegisub.app' / 'Contents'
@@ -283,7 +314,7 @@ if not deps.contains(luajit)
     luajit_inc = luajit_sp.get_variable('incdir')
     deps += luajit_sp.get_variable('luajit_dep')
 else
-    luajit_inc = include_directories(luajit.get_pkgconfig_variable('includedir'))
+    luajit_inc = include_directories(luajit.get_variable(pkgconfig: 'includedir'))
 endif
 subdir('subprojects/luabins/src')
 
diff --git a/src/meson.build b/src/meson.build
index d4eab8a86cd8a35ef5cf0498e7aa90cd08ac7eef..81f06c016b9ae7b68994ae2ba5af6187de879e4c 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -248,7 +248,7 @@ endif
 aegisub_cpp_pch = ['include/agi_pre.h']
 aegisub_c_pch = ['include/agi_pre_c.h']
 
-aegisub = executable('aegisub', aegisub_src, version_h, acconf,
+aegisub = executable('aegisub', aegisub_src, acconf,
                      link_with: [libresrc, libluabins, libaegisub],
                      include_directories: [libaegisub_inc, libresrc_inc, version_inc, deps_inc],
                      cpp_pch: aegisub_cpp_pch,
@@ -256,4 +256,4 @@ aegisub = executable('aegisub', aegisub_src, version_h, acconf,
                      install: true,
                      install_dir: bindir,
                      dependencies: deps,
-                     gui_app: true)
+                     win_subsystem: 'windows')
diff --git a/tools/version.ps1 b/tools/version.ps1
deleted file mode 100644
index 58c7ce5e1d268a98c17c8ec261d4baa9b4ab0b57..0000000000000000000000000000000000000000
--- a/tools/version.ps1
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/usr/bin/env powershell
-
-param (
-  [Parameter(Position = 0, Mandatory = $false)]
-  [string]$BuildRoot = $null,
-  [Parameter(Position = 1, Mandatory = $false)]
-  [string]$SourceRoot = $null
-)
-
-$lastSvnRevision = 6962
-$lastSvnHash = '16cd907fe7482cb54a7374cd28b8501f138116be'
-$defineNumberMatch = [regex] '^#define\s+(\w+)\s+(\d+)$'
-$defineStringMatch = [regex] "^#define\s+(\w+)\s+[`"']?(.+?)[`"']?$"
-$semVerMatch = [regex] 'v?(\d+)\.(\d+).(\d+)(?:-(\w+))?'
-
-$repositoryRootPath = Join-Path $PSScriptRoot .. | Resolve-Path
-if (!(git -C $repositoryRootPath rev-parse --is-inside-work-tree 2>$null)) {
-  throw "$repositoryRootPath is not a git repository"
-}
-
-if ($BuildRoot -eq $null -or $BuildRoot.Trim() -eq "")  {
-  $BuildRoot = $repositoryRootPath
-}
-
-# support legacy in-tree builds
-if ([System.IO.Path]::GetFullPath([System.IO.Path]::Combine((pwd).Path, $BuildRoot)) -eq
-  [System.IO.Path]::GetFullPath([System.IO.Path]::Combine((pwd).Path, $repositoryRootPath))) {
-    $BuildRoot = Join-Path $repositoryRootPath 'build'
-  }
-$gitVersionHeaderPath = Join-Path $BuildRoot 'git_version.h'
-
-$version = @{}
-if (Test-Path $gitVersionHeaderPath) {
-  Get-Content $gitVersionHeaderPath | %{$_.Trim()} | ?{$_} | %{
-    switch -regex ($_) {
-      $defineNumberMatch {
-        $version[$Matches[1]] = [int]$Matches[2];
-      }
-      $defineStringMatch {
-        $version[$Matches[1]] = $Matches[2];
-      }
-    }
-  }
-}
-
-$gitRevision = $lastSvnRevision + ((git -C $repositoryRootPath log --pretty=oneline "$($lastSvnHash)..HEAD" 2>$null | Measure-Object).Count)
-$gitBranch = git -C $repositoryRootPath symbolic-ref --short HEAD 2>$null
-$gitHash = git -C $repositoryRootPath rev-parse --short HEAD 2>$null
-$gitVersionString = $gitRevision, $gitBranch, $gitHash -join '-'
-$exactGitTag = git -C $repositoryRootPath describe --exact-match --tags 2>$null
-
-if ($gitVersionString -eq $version['BUILD_GIT_VERSION_STRING']) {
-  exit 0
-}
-
-if ($exactGitTag -match $semVerMatch) {
-  $version['TAGGED_RELEASE'] = $true
-  $version['RESOURCE_BASE_VERSION'] = $Matches[1..3]
-  $version['INSTALLER_VERSION'] = $gitVersionString = ($Matches[1..3] -join '.') + @("-$($Matches[4])",'')[!$Matches[4]]
-} else {
-  foreach ($rev in (git -C $repositoryRootPath rev-list --tags 2>$null)) {
-    $tag = git -C $repositoryRootPath describe --exact-match --tags $rev 2>$null
-    if ($tag -match $semVerMatch) {#
-      $version['TAGGED_RELEASE'] = $false
-      $version['RESOURCE_BASE_VERSION'] = $Matches[1..3] + $gitRevision
-      $version['INSTALLER_VERSION'] = ($Matches[1..3] -join '.') + "-" + $gitBranch
-      break;
-    }
-  }
-}
-
-$version['BUILD_GIT_VERSION_NUMBER'] = $gitRevision
-$version['BUILD_GIT_VERSION_STRING'] = $gitVersionString
-$version['RELEASE_VERSION'] = $gitVersionString
-
-$version.GetEnumerator() | %{
-  $type = $_.Value.GetType()
-  $value = $_.Value
-  $fmtValue = switch ($type) {
-    ([string]) {"`"$value`""}
-    ([int]) {$value.ToString()}
-    ([bool]) {([int]$value).ToString()}
-    ([object[]]) {$value -join ', '}
-    default {
-      Write-Host "no format known for type '$type' - trying default string conversion" -ForegroundColor Red
-      {"`"$($value.ToString())`""}
-    }
-  }
-  "#define $($_.Key) $($fmtValue)"
-} | Out-File -FilePath $gitVersionHeaderPath -Encoding utf8
diff --git a/tools/version.sh b/tools/version.sh
deleted file mode 100755
index 2266bba88750caa1e85226b2609e87d35d4aab07..0000000000000000000000000000000000000000
--- a/tools/version.sh
+++ /dev/null
@@ -1,66 +0,0 @@
-builddir="$1"
-srcdir="$2"
-export GIT_DIR="${srcdir}/.git"
-
-# If no git repo try to read from the existing git_version.h, for building from tarballs
-version_h_path="${builddir}/git_version.h"
-if ! test -d "${srcdir}/.git"; then
-  if test -f "${version_h_path}"; then
-    while read line; do
-      set -- $line
-      export $2=$(echo $3 | sed 's/"//g')
-    done < "${version_h_path}"
-    if test x$BUILD_GIT_VERSION_NUMBER != x -a x$BUILD_GIT_VERSION_STRING != x; then
-      exit 0
-    else
-      echo "invalid git_version.h"
-      exit 2
-    fi
-  else
-    echo "git repo not found and no cached git_version.h"
-    exit 2
-  fi
-fi
-
-last_svn_revision=6962
-last_svn_hash="16cd907fe7482cb54a7374cd28b8501f138116be"
-
-git_revision=$(expr $last_svn_revision + $(git rev-list --count $last_svn_hash..HEAD))
-git_version_str=$(git describe --exact-match 2> /dev/null)
-installer_version='0.0.0'
-resource_version='0, 0, 0'
-if test x$git_version_str != x; then
-  git_version_str="${git_version_str##v}"
-  tagged_release=1
-  if [ $(echo $git_version_str | grep '\d\.\d\.\d') ]; then
-    installer_version=$git_version_str
-    resource_version=$(echo $git_version_str | sed 's/\./, /g')
-  fi
-else
-  git_branch="$(git symbolic-ref HEAD 2> /dev/null)" || git_branch="(unnamed branch)"
-  git_branch="${git_branch##refs/heads/}"
-  git_hash=$(git rev-parse --short HEAD)
-
-  git_version_str="${git_revision}-${git_branch}-${git_hash}"
-  tagged_release=0
-fi
-
-last_release=$(git describe --tags)
-
-
-new_version_h="\
-#define BUILD_GIT_VERSION_NUMBER ${git_revision}
-#define BUILD_GIT_VERSION_STRING \"${git_version_str}\"
-#define RELEASE_VERSION \"${last_release#v}\"
-#define TAGGED_RELEASE ${tagged_release}
-#define INSTALLER_VERSION \"${installer_version}\"
-#define RESOURCE_BASE_VERSION ${resource_version}"
-
-
-# Write it only if it's changed to avoid spurious rebuilds
-# This bizzare comparison method is due to that newlines in shell variables are very exciting
-case "$(cat ${version_h_path} 2> /dev/null)"
-in
-  "${new_version_h}");;
-  *) echo "${new_version_h}" > "${version_h_path}"
-esac