From 15e2bd35a8da320f074942e814885f3d6eaf0706 Mon Sep 17 00:00:00 2001
From: Bent Bisballe Nyeng <deva@aasimon.org>
Date: Mon, 23 Dec 2024 12:14:46 +0100
Subject: WIP

---
 .gitmodules              |   6 +
 Jenkinsfile              |  25 +++
 bootstrap.bat            |  20 ++
 bootstrap.sh             |   2 +-
 clbootstrap.sh           |  48 +++++
 clrun.sh                 |  31 +++
 ctor.cc                  |  23 +++
 getopt-for-windows       |   1 +
 json                     |   1 +
 src/bootstrap.cc         |   8 +-
 src/configure.cc         | 108 +++++++++-
 src/ctor.h               |  31 ++-
 src/deps.cc              | 167 +++++++++++++++
 src/deps.h               |  12 ++
 src/execute.cc           | 227 ++++++++++++++++++++-
 src/libctor.cc           |   3 +-
 src/rebuild.cc           |  20 +-
 src/task.cc              |  12 +-
 src/task_ar.cc           |   3 +-
 src/task_cc.cc           |  22 +-
 src/task_fn.cc           |   2 +-
 src/task_ld.cc           |  23 ++-
 src/task_so.cc           |   3 +-
 src/tools.cc             | 520 ++++++++++++++++++++++++++++++++++++++++++++---
 src/tools.h              |  15 +-
 src/util.cc              | 107 ++++------
 src/util.h               |   2 +-
 test/ctor.cc             |  54 ++++-
 test/execute_test.cc     |   8 +-
 test/source_type_test.cc |   8 +-
 test/suite/test.sh       |   2 +
 test/tasks_test.cc       |  76 +++----
 test/testprog.cc         |  38 +++-
 test/tmpfile.h           |  31 ++-
 test/tools_test.cc       | 315 ++++++++++++++--------------
 35 files changed, 1607 insertions(+), 367 deletions(-)
 create mode 100644 bootstrap.bat
 create mode 100755 clbootstrap.sh
 create mode 100755 clrun.sh
 create mode 160000 getopt-for-windows
 create mode 160000 json
 create mode 100644 src/deps.cc
 create mode 100644 src/deps.h

diff --git a/.gitmodules b/.gitmodules
index c765825..383739b 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,3 +4,9 @@
 [submodule "test/uunit"]
 	path = test/uunit
 	url = git://git.drumgizmo.org/uunit.git
+[submodule "getopt-for-windows"]
+	path = getopt-for-windows
+	url = https://github.com/Chunde/getopt-for-windows.git
+[submodule "json"]
+	path = json
+	url = https://github.com/nlohmann/json.git
diff --git a/Jenkinsfile b/Jenkinsfile
index 290f412..3812aca 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -44,5 +44,30 @@ pipeline {
 			}
 		}
 		////////////////////////////////////////////////////
+		stage('Windows msvc') {
+			agent { label 'win10 && msvc' }
+			environment
+			{
+				VSDEVCMD="C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\BuildTools\\Common7\\Tools\\VsDevCmd.bat"
+			}
+			steps {
+				echo 'Cleaning workspace ...'
+				bat 'git clean -d -x -f'
+				dir ('build/test') {
+					writeFile file:'dummy', text:''
+				}
+				bat '"%VSDEVCMD%" && bootstrap.bat && snot.exe'
+				echo 'Testing (msvc) ...'
+				bat 'snot.exe check'
+			}
+			post {
+				always {
+					xunit(thresholds: [ skipped(failureThreshold: '0'),
+					                    failed(failureThreshold: '0') ],
+					      tools: [ CppUnit(pattern: 'build/test/*.xml') ])
+				}
+			}
+		}
+		////////////////////////////////////////////////////
 	}
 }
diff --git a/bootstrap.bat b/bootstrap.bat
new file mode 100644
index 0000000..2493eb6
--- /dev/null
+++ b/bootstrap.bat
@@ -0,0 +1,20 @@
+set CXX=cl.exe
+set CC=cl.exe
+set AR=lib.exe
+set LD=link.exe
+
+%CXX% /nologo /MT /std:c++20 /D_X86_ /EHsc /Isrc /Fo:build\ ctor.cc src\bootstrap.cc /link /out:snot.exe
+@if %errorlevel% neq 0 exit /b %errorlevel%
+
+snot.exe
+@if %errorlevel% neq 0 exit /b %errorlevel%
+
+%CXX% /nologo /std:c++20 /D_X86_ /EHsc /Isrc /Fo:build\ ctor.cc /c
+@if %errorlevel% neq 0 exit /b %errorlevel%
+%CXX% /nologo /std:c++20 /D_X86_ /EHsc /Isrc /Fo:build\test\ test\ctor.cc /c
+@if %errorlevel% neq 0 exit /b %errorlevel%
+%LD% /nologo build\libctor.lib build\ctor.obj build\test\ctor.obj /subsystem:console /out:snot.exe
+@if %errorlevel% neq 0 exit /b %errorlevel%
+
+snot.exe configure --ctor-includedir=src --ctor-libdir=build
+@if %errorlevel% neq 0 exit /b %errorlevel%
diff --git a/bootstrap.sh b/bootstrap.sh
index 9e8cdca..052d8b7 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -6,7 +6,7 @@ set -e
 CXX=$(which $CXX)
 
 echo "Bootstrapping..."
-$CXX -std=c++20 -Wall -O3 -Isrc -pthread src/bootstrap.cc ctor.cc test/ctor.cc -o ctor
+$CXX -std=c++20 -Wall -O3 -Isrc -pthread src/bootstrap.cc ctor.cc -o ctor
 ./ctor
 $CXX -std=c++20 -Wall -O3 -Isrc -pthread ctor.cc test/ctor.cc -L$BUILDDIR -lctor -o ctor
 ./ctor configure --ctor-includedir=src --ctor-libdir=$BUILDDIR --build-dir=$BUILDDIR
diff --git a/clbootstrap.sh b/clbootstrap.sh
new file mode 100755
index 0000000..47664d1
--- /dev/null
+++ b/clbootstrap.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+set -e
+set -x
+export WINEDEBUG=-all
+
+export BASE='C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.29.30133'
+export ONECORELIB="$BASE\lib\onecore\x86"
+export WINEPATH="$BASE\bin\Hostx86\x86"
+export INCLUDE="$BASE\include"
+export UCRT="C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\ucrt"
+export UCRTLIB="C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\ucrt\x86"
+export UM="C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um"
+export UMLIB="C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x86"
+export SHARED="C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\shared"
+echo Bootstrapping...
+
+export CL="/I\"$BASE\include\" /I\"$UCRT\" /I\"$UM\" /I\"$SHARED\" /link /LIBPATH:\"$UMLIB\" /LIBPATH:\"$ONECORELIB\" /LIBPATH:\"$UCRTLIB\""
+export LINK="/LIBPATH:\"$UMLIB\" /LIBPATH:\"$ONECORELIB\" /LIBPATH:\"$UCRTLIB\""
+export LIB="/LIBPATH:\"$UMLIB\" /LIBPATH:\"$ONECORELIB\" /LIBPATH:\"$UCRTLIB\""
+
+# https://docs.microsoft.com/en-us/cpp/build/reference/cl-environment-variables?view=msvc-170
+
+# set INCLUDE="$BASE\include";$UCRT;$UM;$SHARED
+# set LIB=
+# set LIBPATH=$UMLIB;$ONECORELIB;$UCRTLIB
+
+rm -Rf ctor.exe build/ configuration.cc
+mkdir build
+export CXX="cl.exe"
+export CC="cl.exe"
+export AR="lib.exe"
+export LD="link.exe"
+
+wine cl /nologo /std:c++20 /D_X86_ /EHsc /Isrc /Fo:build/ ctor.cc src/bootstrap.cc /link /out:ctor.exe
+#sleep 1
+wine ctor.exe
+#sleep 1
+wine cl /nologo /std:c++20 /D_X86_ /EHsc /Isrc /Fo:build/ ctor.cc build/libctor.lib /link /subsystem:console /out:ctor.exe
+#sleep 1
+wine ctor.exe configure --ctor-includedir=src --ctor-libdir=build
+#sleep 1
+
+#exit 0
+
+wine ctor.exe -a test/ctor.cc
+#sleep 1
+wine ctor.exe -v check
+#sleep 1
diff --git a/clrun.sh b/clrun.sh
new file mode 100755
index 0000000..d2e57f3
--- /dev/null
+++ b/clrun.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+set -e
+set -x
+export WINEDEBUG=-all
+
+rm -f ~/.wine/drive_c/number
+
+export BASE='C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.29.30133'
+export ONECORELIB="$BASE\lib\onecore\x86"
+export WINEPATH="$BASE\bin\Hostx86\x86"
+export INCLUDE="$BASE\include"
+export UCRT="C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\ucrt"
+export UCRTLIB="C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\ucrt\x86"
+export UM="C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um"
+export UMLIB="C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x86"
+export SHARED="C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\shared"
+
+export CL="/I\"$BASE\include\" /I\"$UCRT\" /I\"$UM\" /I\"$SHARED\" /link /LIBPATH:\"$UMLIB\" /LIBPATH:\"$ONECORELIB\" /LIBPATH:\"$UCRTLIB\""
+export LINK="/LIBPATH:\"$UMLIB\" /LIBPATH:\"$ONECORELIB\" /LIBPATH:\"$UCRTLIB\""
+export LIB="/LIBPATH:\"$UMLIB\" /LIBPATH:\"$ONECORELIB\" /LIBPATH:\"$UCRTLIB\""
+
+export CXX="cl.exe"
+export CC="cl.exe"
+export AR="lib.exe"
+export LD="link.exe"
+
+
+#wine ctor.exe
+
+$*
+
diff --git a/ctor.cc b/ctor.cc
index 92135c2..08a0bf8 100644
--- a/ctor.cc
+++ b/ctor.cc
@@ -15,6 +15,7 @@ ctor::build_configurations ctorConfigs(const ctor::settings& settings)
 			.sources = {
 				"src/build.cc",
 				"src/configure.cc",
+				"src/deps.cc",
 				"src/execute.cc",
 				"src/externals_manual.cc",
 				"src/libctor.cc",
@@ -29,8 +30,23 @@ ctor::build_configurations ctorConfigs(const ctor::settings& settings)
 				"src/tools.cc",
 				"src/util.cc",
 				"src/unittest.cc",
+				{ctor::toolchain::msvc, "getopt-for-windows/getopt.c"},
 			},
 			.flags = {
+				.cflags = {
+					"-O3",
+//					"-g",
+//					"-Wall",
+//					"-Wconversion",
+//					"-Wextra",
+					//"-Werror",
+					"-Isrc",
+					{ctor::toolchain::msvc, ctor::c_opt::custom, "/Igetopt-for-windows"},
+					{ctor::toolchain::msvc, ctor::c_opt::custom, "/D_X86_"},
+					{ctor::toolchain::msvc, ctor::c_opt::custom, "/EHsc"},
+					{ctor::toolchain::msvc, ctor::c_opt::custom, "/external:W0"},
+					{ctor::toolchain::msvc, ctor::c_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"},
+				},
 				.cxxflags = {
 					"-std=c++20",
 					"-O3",
@@ -42,6 +58,13 @@ ctor::build_configurations ctorConfigs(const ctor::settings& settings)
 					"-Wconversion",
 //					"-Wnrvo",
 					"-Isrc",
+					"-Ijson/include",
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/Igetopt-for-windows"},
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_X86_"},
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"},
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/external:W0"},
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"},
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/Dstrdup=_strdup"},
 				},
 			},
 		}
diff --git a/getopt-for-windows b/getopt-for-windows
new file mode 160000
index 0000000..76fe2df
--- /dev/null
+++ b/getopt-for-windows
@@ -0,0 +1 @@
+Subproject commit 76fe2df86f84771775fd2f1a641a09adbce28df7
diff --git a/json b/json
new file mode 160000
index 0000000..6be4e85
--- /dev/null
+++ b/json
@@ -0,0 +1 @@
+Subproject commit 6be4e8560023098fdb6d2047e6e6e5bc5dd52879
diff --git a/src/bootstrap.cc b/src/bootstrap.cc
index 471c844..9d895ba 100644
--- a/src/bootstrap.cc
+++ b/src/bootstrap.cc
@@ -41,7 +41,7 @@ bool ctor::configuration::has(const std::string& key) const
 	return false;
 }
 
-const std::string& ctor::configuration::get(const std::string& key, const std::string& default_value) const
+std::string ctor::configuration::get(const std::string& key, const std::string& default_value) const
 {
 	auto cxx_env = std::getenv("CXX");
 	if(key == ctor::cfg::build_cxx && cxx_env)
@@ -81,6 +81,12 @@ const std::string& ctor::configuration::get(const std::string& key, const std::s
 	return default_value;
 }
 
+std::vector<std::string> readDeps(const std::string& depFile,
+                                  ctor::toolchain toolchain)
+{
+	return {};
+}
+
 int main(int argc, char* argv[])
 {
 	auto args = std::span(argv, static_cast<std::size_t>(argc));
diff --git a/src/configure.cc b/src/configure.cc
index 995e340..d2c1053 100644
--- a/src/configure.cc
+++ b/src/configure.cc
@@ -8,6 +8,8 @@
 #include <fstream>
 #include <optional>
 #include <span>
+#include <vector>
+#include <deque>
 
 #include <getoptpp/getoptpp.hpp>
 
@@ -25,17 +27,49 @@ const std::filesystem::path configHeaderFile("config.h");
 std::map<std::string, std::string> external_includedir;
 std::map<std::string, std::string> external_libdir;
 
+#if !defined(_WIN32)
 const ctor::configuration& __attribute__((weak)) ctor::get_configuration()
+#else
+const ctor::configuration& default_get_configuration()
+#endif
 {
 	static ctor::configuration cfg;
 	static bool initialised{false};
 	if(!initialised)
 	{
-		cfg.build_toolchain = getToolChain(cfg.get(ctor::cfg::build_cxx, "/usr/bin/g++"));
+		std::string cxx_prog{"g++"};
+		auto cxx_env = getenv("CXX");
+		if(cxx_env)
+		{
+			cxx_prog = cxx_env;
+		}
+
+		cfg.build_toolchain = getToolChain(cfg.get(ctor::cfg::build_cxx, cxx_prog));
 		initialised = true;
 	}
 	return cfg;
 }
+#if defined(_WIN32)
+// Hack to make ctor::get_configuration "weak" linked
+// See:
+//   https://stackoverflow.com/questions/2290587/gcc-style-weak-linking-in-visual-studio
+// and
+//   https://stackoverflow.com/questions/11849853/how-to-list-functions-present-in-object-file
+#pragma comment(linker, "/alternatename:?get_configuration@ctor@@YAABUconfiguration@1@XZ=?default_get_configuration@@YAABUconfiguration@ctor@@XZ")
+
+/*
+
+?get_configuration@ctor@@YAABUconfiguration@1@XZ
+??__Fcfg@?1??get_configuration@ctor@@YAABUconfiguration@1@XZ@YAXXZ
+
+=
+
+?default_get_configuration@@YAABUconfiguration@ctor@@XZ
+??__Fcfg@?1??default_get_configuration@@YAABUconfiguration@ctor@@XZ@YAXXZ
+
+*/
+//#pragma comment(linker, "/alternatename:??__Fcfg@?1??get_configuration@ctor@@YAABUconfiguration@1@XZ@YAXXZ=??__Fcfg@?1??default_get_configuration@@YAABUconfiguration@ctor@@XZ@YAXXZ")
+#endif
 
 namespace ctor {
 std::optional<std::string> includedir;
@@ -69,7 +103,7 @@ bool ctor::configuration::has(const std::string& key) const
 	return tools.find(key) != tools.end();
 }
 
-const std::string& ctor::configuration::get(const std::string& key, const std::string& default_value) const
+std::string ctor::configuration::get(const std::string& key, const std::string& default_value) const
 {
 	if(key == ctor::cfg::ctor_includedir && ctor::includedir)
 	{
@@ -91,11 +125,47 @@ const std::string& ctor::configuration::get(const std::string& key, const std::s
 		return ctor::conf_values[key];
 	}
 
-	if(has(key))
+	if(tools.find(key) != tools.end())
 	{
 		return tools.at(key);
 	}
 
+	if(key == ctor::cfg::build_cxx)
+	{
+		auto e = std::getenv("CXX");
+		if(e)
+		{
+			return e;
+		}
+	}
+
+	if(key == ctor::cfg::build_cc)
+	{
+		auto e = std::getenv("CC");
+		if(e)
+		{
+			return e;
+		}
+	}
+
+	if(key == ctor::cfg::build_ld)
+	{
+		auto e = std::getenv("LD");
+		if(e)
+		{
+			return e;
+		}
+	}
+
+	if(key == ctor::cfg::build_ar)
+	{
+		auto e = std::getenv("AR");
+		if(e)
+		{
+			return e;
+		}
+	}
+
 	return default_value;
 }
 
@@ -136,6 +206,9 @@ std::ostream& operator<<(std::ostream& stream, const ctor::toolchain& toolchain)
 	case ctor::toolchain::gcc:
 		stream << "ctor::toolchain::gcc";
 		break;
+	case ctor::toolchain::msvc:
+		stream << "ctor::toolchain::msvc";
+		break;
 	case ctor::toolchain::clang:
 		stream << "ctor::toolchain::clang";
 		break;
@@ -358,13 +431,12 @@ int regenerateCache(ctor::settings& settings,
 
 	opt.add("help", no_argument, 'h',
 	        "Print this help text.",
-	        [&]() {
+	        [&]() -> int {
 		        std::cout << "Configure how to build with " << name << "\n";
 		        std::cout << "Usage: " << name << " configure [options]\n\n";
 		        std::cout << "Options:\n";
 		        opt.help();
 		        exit(0);
-		        return 0;
 	        });
 
 	opt.process(static_cast<int>(vargs.size()), vargs.data());
@@ -651,6 +723,7 @@ int regenerateCache(ctor::settings& settings,
 	{
 		ctor::conf_values[ctor::cfg::builddir] = builddir;
 	}
+
 	ctor::conf_values[ctor::cfg::host_cxx] = host_cxx;
 	ctor::conf_values[ctor::cfg::build_cxx] = build_cxx;
 
@@ -863,6 +936,31 @@ int configure(const ctor::settings& global_settings, int argc, char* argv[])
 		env["PATH"] = path_env;
 	}
 
+	// Env vars for msvc
+	auto cl_env = getenv("CL");
+	if(cl_env)
+	{
+		env["CL"] = cl_env;
+	}
+
+	auto lib_env = getenv("LIB");
+	if(lib_env)
+	{
+		env["LIB"] = lib_env;
+	}
+
+	auto link_env = getenv("LINK");
+	if(link_env)
+	{
+		env["LINK"] = link_env;
+	}
+
+	auto include_env = getenv("INCLUDE");
+	if(include_env)
+	{
+		env["INCLUDE"] = include_env;
+	}
+
 	auto ret = regenerateCache(settings, args_span[0], args, env);
 	if(ret != 0)
 	{
diff --git a/src/ctor.h b/src/ctor.h
index e0e2bef..3d963b3 100644
--- a/src/ctor.h
+++ b/src/ctor.h
@@ -59,6 +59,7 @@ enum class toolchain
 	none,
 	gcc,
 	clang,
+	msvc,
 };
 
 struct source
@@ -90,6 +91,9 @@ enum class cxx_opt
 	output,                          //  -o
 	debug,                           //  -g
 	warn_all,                        //  -Wall
+	warn_conversion,                 //  -Wconversion
+	warn_shadow,                     //  -Wshadow
+	warn_extra,                      //  -Wextra
 	warnings_as_errors,              //  -Werror
 	generate_dep_tree,               //  -MMD
 	no_link,                         //  -c
@@ -98,6 +102,7 @@ enum class cxx_opt
 	optimization,                    //  -O<arg>
 	position_independent_code,       //  -fPIC
 	position_independent_executable, //  -fPIE
+	define,                          //  -D<arg>[=<arg2>]
 	custom,                          // entire option taken verbatim from <arg>
 };
 
@@ -107,6 +112,9 @@ enum class c_opt
 	output,                          //  -o
 	debug,                           //  -g
 	warn_all,                        //  -Wall
+	warn_conversion,                 //  -Wconversion
+	warn_shadow,                     //  -Wshadow
+	warn_extra,                      //  -Wextra
 	warnings_as_errors,              //  -Werror
 	generate_dep_tree,               //  -MMD
 	no_link,                         //  -c
@@ -115,6 +123,7 @@ enum class c_opt
 	optimization,                    //  -O<arg>
 	position_independent_code,       //  -fPIC
 	position_independent_executable, //  -fPIE
+	define,                          //  -D<arg>[=<arg2>]
 	custom,                          // entire option taken verbatim from <arg>
 };
 
@@ -159,15 +168,21 @@ public:
 	flag(std::string_view str);
 	flag(const char* str);
 	flag(T opt_) : opt(opt_) {}
-	flag(T opt_, std::string_view arg_) : opt(opt_), arg(arg_) {}
-	flag(T opt_, const char* arg_) : opt(opt_), arg(arg_) {}
-	flag(ctor::toolchain toolchain_, T opt_) : toolchain(toolchain_), opt(opt_) {}
-	flag(ctor::toolchain toolchain_, T opt_, const char* arg_) : toolchain(toolchain_), opt(opt_), arg(arg_) {}
-	flag(ctor::toolchain toolchain_, T opt_, std::string_view arg_) : toolchain(toolchain_), opt(opt_), arg(arg_) {}
+	flag(T opt_, std::string_view arg_, std::string_view arg2_ = "")
+		: opt(opt_), arg(arg_), arg2(arg2_) {}
+	flag(T opt_, const char* arg_, const char* arg2_ = "")
+		: opt(opt_), arg(arg_), arg2(arg2_) {}
+	flag(ctor::toolchain toolchain_, T opt_)
+		: toolchain(toolchain_), opt(opt_) {}
+	flag(ctor::toolchain toolchain_, T opt_, const char* arg_, const char* arg2_ = "")
+		: toolchain(toolchain_), opt(opt_), arg(arg_), arg2(arg2_) {}
+	flag(ctor::toolchain toolchain_, T opt_, std::string_view arg_, std::string_view arg2_ = "")
+		: toolchain(toolchain_), opt(opt_), arg(arg_), arg2(arg2_) {}
 
 	ctor::toolchain toolchain{ctor::toolchain::any};
 	T opt{};
 	std::string arg;
+	std::string arg2;
 };
 
 using c_flag = ctor::flag<ctor::c_opt>;
@@ -253,12 +268,12 @@ namespace cfg
 constexpr auto builddir = "builddir";
 
 constexpr auto host_cc = "host-cc";
-constexpr auto host_cxx = "host-cpp";
+constexpr auto host_cxx = "host-cxx";
 constexpr auto host_ar = "host-ar";
 constexpr auto host_ld = "host-ld";
 
 constexpr auto build_cc = "build-cc";
-constexpr auto build_cxx = "build-cpp";
+constexpr auto build_cxx = "build-cxx";
 constexpr auto build_ar = "build-ar";
 constexpr auto build_ld = "build-ld";
 
@@ -269,7 +284,7 @@ constexpr auto ctor_libdir = "ctor-libdir";
 struct configuration
 {
 	bool has(const std::string& key) const;
-	const std::string& get(const std::string& key, const std::string& default_value = {}) const;
+	std::string get(const std::string& key, const std::string& default_value = {}) const;
 
 	ctor::toolchain host_toolchain{ctor::toolchain::none};
 	ctor::arch host_arch{ctor::arch::unknown};
diff --git a/src/deps.cc b/src/deps.cc
new file mode 100644
index 0000000..736acbf
--- /dev/null
+++ b/src/deps.cc
@@ -0,0 +1,167 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include "deps.h"
+
+#include "util.h"
+
+#include <nlohmann/json.hpp>
+
+#include <fstream>
+
+namespace {
+/* Format example:
+build/src/libctor_a-libctor_cc.o: src/libctor.cc \
+ src/getoptpp/getoptpp.hpp src/ctor.h src/configure.h src/rebuild.h \
+ src/tasks.h src/task.h src/build.h src/unittest.h
+ */
+std::vector<std::string> readDepsMake(const std::string& dep_file)
+{
+	auto str = readFile(dep_file);
+
+	std::vector<std::string> output;
+	std::string tmp;
+	bool start{false};
+	bool in_whitespace{false};
+	for(const auto& c : str)
+	{
+		if(c == '\\' || c == '\n')
+		{
+			continue;
+		}
+
+		if(c == ':')
+		{
+			start = true;
+			continue;
+		}
+
+		if(!start)
+		{
+			continue;
+		}
+
+		if(c == ' ' || c == '\t')
+		{
+			if(in_whitespace)
+			{
+				continue;
+			}
+
+			if(!tmp.empty())
+			{
+				output.push_back(tmp);
+			}
+			tmp.clear();
+			in_whitespace = true;
+		}
+		else
+		{
+			in_whitespace = false;
+			tmp += c;
+		}
+	}
+
+	if(!tmp.empty())
+	{
+		output.push_back(tmp);
+	}
+
+	return output;
+}
+
+// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1689r5.html
+// https://devblogs.microsoft.com/cppblog/introducing-source-dependency-reporting-with-msvc-in-visual-studio-2019-version-16-7/
+/* Format examples:
+{
+    "Version": "1.1",
+    "Data": {
+        "Source": "z:\\home\\deva\\docs\\c\\ctor\\src\\libctor.cc",
+        "ProvidedModule": "",
+        "Includes": [
+            "c:\\program files (x86)\\microsoft visual studio\\2019\\buildtools\\vc\\tools\\msvc\\14.29.30133\\include\\vector",
+            "c:\\program files (x86)\\microsoft visual studio\\2019\\buildtools\\vc\\tools\\msvc\\14.29.30133\\include\\yvals_core.h",
+            "c:\\program files (x86)\\microsoft visual studio\\2019\\buildtools\\vc\\tools\\msvc\\14.29.30133\\include\\vcruntime.h",
+.
+.
+.
+            "z:\\home\\deva\\docs\\c\\ctor\\src\\unittest.h"
+        ],
+        "ImportedModules": [],
+        "ImportedHeaderUnits": []
+    }
+}
+*/
+/*
+{
+    "Version": "1.2",
+    "Data": {
+        "Source": "c:\\jenkins\\workspace\\ctor-linux64\\src\\build.cc",
+        "ProvidedModule": "",
+        "Includes": [
+            "c:\\jenkins\\workspace\\ctor-linux64\\src\\build.h",
+            "c:\\program files (x86)\\microsoft visual studio\\2022\\buildtools\\vc\\tools\\msvc\\14.42.34433\\include\\string",
+            "c:\\program files (x86)\\microsoft visual studio\\2022\\buildtools\\vc\\tools\\msvc\\14.42.34433\\include\\yvals_core.h",
+.
+.
+.
+            "c:\\program files (x86)\\microsoft visual studio\\2022\\buildtools\\vc\\tools\\msvc\\14.42.34433\\include\\iostream"
+        ],
+        "ImportedModules": [],
+        "ImportedHeaderUnits": []
+    }
+}
+*/
+
+std::vector<std::string> readDepsJson(const std::string& dep_file)
+{
+	std::ifstream stream(dep_file);
+	auto json = nlohmann::json::parse(stream);
+	for(const auto& [key, value] : json.items())
+	{
+		if(key == "Data" && value.is_object())
+		{
+			for(const auto& [inner_key, inner_value] : value.items())
+			{
+				if(inner_key == "Includes" && inner_value.is_array())
+				{
+					std::vector<std::string> deps;
+					for(const auto& dep : inner_value)
+					{
+						deps.emplace_back(dep);
+					}
+					return deps;
+				}
+			}
+		}
+	}
+	return {};
+}
+}
+
+std::vector<std::string> readDeps(const std::string& dep_file,
+                                  ctor::toolchain toolchain)
+{
+	if(!std::filesystem::exists(dep_file))
+	{
+		return {};
+	}
+
+	switch(toolchain)
+	{
+	case ctor::toolchain::any:
+	case ctor::toolchain::none:
+		return {};
+
+	// json based:
+	case ctor::toolchain::msvc:
+		return readDepsJson(dep_file);
+
+	// makefile .d file based:
+	case ctor::toolchain::gcc:
+	case ctor::toolchain::clang:
+		return readDepsMake(dep_file);
+	}
+
+	return {};
+}
diff --git a/src/deps.h b/src/deps.h
new file mode 100644
index 0000000..be0dfb2
--- /dev/null
+++ b/src/deps.h
@@ -0,0 +1,12 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#pragma once
+
+#include "ctor.h"
+
+#include <vector>
+#include <string>
+
+std::vector<std::string> readDeps(const std::string& dep_file,
+                                  ctor::toolchain toolchain);
diff --git a/src/execute.cc b/src/execute.cc
index cbae899..58e0051 100644
--- a/src/execute.cc
+++ b/src/execute.cc
@@ -5,12 +5,22 @@
 
 #include "ctor.h"
 
-#include <unistd.h>
-#include <cstring>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <spawn.h>
+#if !defined(_WIN32)
+	#include <unistd.h>
+	#include <sys/types.h>
+	#include <sys/wait.h>
+	#include <spawn.h>
+#else
+	#define WINDOWS_LEAN_AND_MEAN
+	#include <windows.h>
+	#undef max
+#endif
+
 #include <iostream>
+#include <cstring>
+#include <vector>
+#include <deque>
+#include <filesystem>
 
 /*
 https://blog.famzah.net/2009/11/20/a-much-faster-popen-and-system-implementation-for-linux/
@@ -30,7 +40,11 @@ public:
 	{
 		for(const auto& arg : args)
 		{
+#if !defined(_WIN32)
 			push_back(strdup(arg.data()));
+#else
+			push_back(_strdup(arg.data()));
+#endif
 		}
 		push_back(nullptr);
 	}
@@ -44,6 +58,7 @@ public:
 	}
 };
 
+#if !defined(_WIN32)
 int parent_waitpid(pid_t pid)
 {
 	int status{};
@@ -55,9 +70,44 @@ int parent_waitpid(pid_t pid)
 
 	return WEXITSTATUS(status);
 }
+#endif //_WIN32
 } // namespace ::
 
+#if !defined(_WIN32)
 extern char **environ; // see 'man environ'
+#endif
+
+#if defined(_WIN32)
+//Returns the last Win32 error, in string format. Returns an empty string if there is no error.
+std::string GetLastErrorAsString()
+{
+	//Get the error message ID, if any.
+	DWORD errorMessageID = ::GetLastError();
+	if(errorMessageID == 0)
+	{
+		return std::string(); //No error message has been recorded
+	}
+
+	LPSTR messageBuffer = nullptr;
+
+	//Ask Win32 to give us the string version of that message ID.
+	//The parameters we pass in, tell Win32 to create the buffer that holds the message for us
+	// (because we don't yet know how long the message string will be).
+	size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+	                             FORMAT_MESSAGE_FROM_SYSTEM |
+	                             FORMAT_MESSAGE_IGNORE_INSERTS,
+	                             NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+	                             (LPSTR)&messageBuffer, 0, NULL);
+
+	//Copy the error message into a std::string.
+	std::string message(messageBuffer, size);
+
+	//Free the Win32's string's buffer.
+	LocalFree(messageBuffer);
+
+	return message;
+}
+#endif // _WIN32
 
 int execute(const ctor::settings& settings,
             const std::string& command,
@@ -92,6 +142,8 @@ int execute(const ctor::settings& settings,
 		std::cout << cmd << std::endl;
 	}
 
+#if !defined(_WIN32)
+
 #if 1
 	auto pid = vfork();
 	if(pid == 0)
@@ -133,5 +185,170 @@ int execute(const ctor::settings& settings,
 	return system(cmd.data());
 #endif
 
+#else // _WIN32
+	std::map<std::string, std::string> new_env;
+	{
+		auto env_strings = GetEnvironmentStrings();
+		const char* ptr = env_strings;
+		std::string key;
+		std::string value;
+		bool key_state{true};
+		while(true)
+		{
+			if(key_state) // searching for key
+			{
+				if(*ptr == '\0')
+				{
+					break;
+				}
+
+				if(*ptr != '=')
+				{
+					key += *ptr;
+				}
+				else
+				{
+					key_state = false;
+				}
+			}
+			else
+			{
+				if(*ptr != '\0')
+				{
+					value += *ptr;
+				}
+				else
+				{
+					new_env.insert({key, value});
+					key = {};
+					value = {};
+					key_state = true;
+				}
+			}
+		++ptr;
+		}
+		FreeEnvironmentStrings(env_strings);
+	}
+
+	// add new env vars (if any)
+	for(const auto& [key, value] : env)
+	{
+		//if(key == "CL" || key == "LINK" || key == "LIB")
+		{
+			new_env[key] = value;
+		}
+	}
+
+	std::string env_str;
+	for(const auto& [k,v] : new_env)
+	{
+		env_str += k + "=" + v;
+		env_str += '\0';
+	}
+	env_str += '\0';
+
+//	std::cout <<"#### Length of new env: " << env_str.size() << "\n";
+
+	STARTUPINFO si{};
+//	si.hStdInput = GetStdHandle(((DWORD)-10)/*STD_INPUT_HANDLE*/);
+//	si.hStdOutput = GetStdHandle(((DWORD)-11)/*STD_OUTPUT_HANDLE*/);
+//	si.hStdError = GetStdHandle(((DWORD)-12)/*STD_ERROR_HANDLE*/);
+//	si.dwFlags = /*STARTF_USESTDHANDLES*/0x00000100;
+
+	PROCESS_INFORMATION pi{};
+
+	si.cb = sizeof(si);
+
+	// TODO: Use SetDllDirectory(...) to set DLL search directory
+
+	if(terminate)
+	{
+		char tmpdir[MAX_PATH+1];
+		int cnt{0};
+
+		// The returned string ends with a backslash
+		if(GetTempPathA(sizeof(tmpdir), tmpdir) == 0)
+		{
+			std::cerr << "Could not read TMP folder\n";
+			return GetLastError();
+		}
+
+		char source[MAX_PATH];
+		HMODULE module = GetModuleHandle(0);
+		GetModuleFileNameA(module, source, MAX_PATH);
+
+		while(true)
+		{
+			if(cnt > 10) // If we need to try more than 10 times something is wrong
+			{
+				return 1;
+			}
+
+			std::filesystem::path tmp_file = settings.builddir;
+			tmp_file /= "tmp";
+			std::string target = tmp_file.string() + "-" + std::to_string(cnt);
+			std::cout << "move " << source << " => " << target <<std::endl;
+			if(MoveFileA(source, target.data()))
+			{
+				break; // success
+			}
+
+			auto err = GetLastError();
+			if(err == ERROR_ALREADY_EXISTS)
+			{
+				if(DeleteFileA(target.data()))
+				{
+					continue; // Try again
+				}
+
+				err = GetLastError();
+				if(err != ERROR_ACCESS_DENIED)
+				{
+					std::cerr << "Could not delete file\n";
+					return err;
+				}
+
+				cnt++;
+				continue; // Increment and try again
+			}
+			else
+			{
+				std::cerr << "Could not move file\n";
+				return err;
+			}
+		}
+	}
+
+	if(!CreateProcess(nullptr,           // lpApplicationName
+	                  (char*)cmd.data(), // lpCommandLine
+	                  nullptr,           // lpProcessAttributes
+	                  nullptr,           // lpThreadAttributes
+	                  TRUE,              // bInheritHandles
+	                  INHERIT_PARENT_AFFINITY |
+	                  //DETACHED_PROCESS |
+	                  ///*DEBUG_PROCESS*/ 0x00000001 |
+	                  ///*CREATE_NO_WINDOW*/ 0x08000000 |
+	                  ///*CREATE_BREAKAWAY_FROM_JOB*/ 0x01000000 |
+	                  /*CREATE_NEW_PROCESS_GROUP*/ 0x00000200 |
+	                  0,                 // dwCreationFlags
+	                  env_str.data(),    // lpEnvironment
+	                  nullptr,           // lpCurrentDirectory
+	                  &si,               // lpStartupInfo
+	                  &pi))              // lpProcessInformation
+	{
+		std::cout << "Could not execute " << command << ": " <<
+			GetLastErrorAsString() << "\n";
+		return 1;//_exit(1); // execve only returns if an error occurred
+	}
+
+	// Now 'pi.hProcess' contains the process HANDLE, which you can use to
+	// wait for it like this:
+	const DWORD infinite = 0xFFFFFFFF;
+	WaitForSingleObject(pi.hProcess, infinite);
+	DWORD exitCode{0};
+	GetExitCodeProcess(pi.hProcess, &exitCode);
+	return exitCode;
+#endif // _WIN32
+
 	return 1;
 }
diff --git a/src/libctor.cc b/src/libctor.cc
index b1fbaea..aaf17c9 100644
--- a/src/libctor.cc
+++ b/src/libctor.cc
@@ -159,7 +159,7 @@ int main(int argc, char* argv[])
 
 	opt.add("help", no_argument, 'h',
 	        "Print this help text.",
-	        [&]() {
+	        [&]() -> int {
 		        std::cout << "Usage: " << args[0] << " [options] [target] ...\n";
 		        std::cout <<
 R"_( where target can be either:
@@ -174,7 +174,6 @@ Options:
 )_";
 		        opt.help();
 		        exit(0);
-		        return 0;
 	        });
 
 	opt.process(argc, argv);
diff --git a/src/rebuild.cc b/src/rebuild.cc
index 1db5e83..865a45b 100644
--- a/src/rebuild.cc
+++ b/src/rebuild.cc
@@ -38,7 +38,10 @@ int reg(ctor::build_configurations (*cb)(const ctor::settings&),
 	{
 		auto pwd = std::filesystem::current_path();
 		auto rel = std::filesystem::relative(loc, pwd);
-		configFiles[numConfigFiles].file = strdup(rel.string().data()); // NOTE: This intentionally leaks memory
+		auto str = rel.string();
+		auto file = new char[str.size() + 1];
+		strncpy(file, str.data(), str.size() + 1);
+		configFiles[numConfigFiles].file = file; // NOTE: This intentionally leaks memory
 	}
 	else
 	{
@@ -71,7 +74,7 @@ int reg(const char* location)
 
 int unreg(const char* location)
 {
-	std::size_t found{0};
+	int found{0};
 	for(std::size_t i = 0; i < numConfigFiles;)
 	{
 		if(std::string(location) == configFiles[i].file)
@@ -169,6 +172,8 @@ bool recompileCheck(const ctor::settings& global_settings, int argc, char* argv[
 	config.flags.cxxflags.emplace_back(ctor::cxx_opt::optimization, "3");
 	config.flags.cxxflags.emplace_back(ctor::cxx_opt::cpp_std, "c++20");
 
+	config.flags.cxxflags.push_back({ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"});
+
 	const auto& c = ctor::get_configuration();
 	if(c.has(ctor::cfg::ctor_includedir))
 	{
@@ -180,13 +185,20 @@ bool recompileCheck(const ctor::settings& global_settings, int argc, char* argv[
 		config.flags.ldflags.emplace_back(ctor::ld_opt::library_path,
 		                                  c.get(ctor::cfg::ctor_libdir));
 	}
-	config.flags.ldflags.emplace_back(ctor::ld_opt::link, "ctor");
+	config.flags.ldflags.emplace_back(ctor::toolchain::msvc, ctor::ld_opt::link, "libctor.lib");
+	config.flags.ldflags.emplace_back(ctor::toolchain::gcc, ctor::ld_opt::link, "ctor");
+	config.flags.ldflags.emplace_back(ctor::toolchain::clang, ctor::ld_opt::link, "ctor");
 	config.flags.ldflags.emplace_back(ctor::ld_opt::strip);
 	config.flags.ldflags.emplace_back(ctor::ld_opt::threads);
 
+	config.flags.ldflags.push_back({ctor::toolchain::msvc, ctor::ld_opt::custom, "/subsystem:console"});
+
 	ctor::settings settings{global_settings};
 	settings.verbose = -1; // Make check completely silent.
-	settings.builddir += "/ctor"; // override builddir to use ctor subdir
+
+	// override builddir to use ctor subdir
+	auto ctor_builddir = std::filesystem::path(settings.builddir) / "ctor";
+	settings.builddir = ctor_builddir.string();
 
 	{
 		std::filesystem::path buildfile  = settings.builddir;
diff --git a/src/task.cc b/src/task.cc
index a9c0fee..ef7731b 100644
--- a/src/task.cc
+++ b/src/task.cc
@@ -3,7 +3,6 @@
 // See accompanying file LICENSE for details.
 #include "task.h"
 
-#include <unistd.h>
 #include <iostream>
 #include <algorithm>
 #include <utility>
@@ -46,11 +45,14 @@ int Task::registerDepTasks(const std::vector<std::shared_ptr<Task>>& tasks)
 
 bool Task::operator==(const std::string& depStr)
 {
+	std::filesystem::path generated_output = sourceDir;
+	generated_output /= target();
 	return
-		name() == depStr ||
-		target() == depStr ||
-		sourceDir + "/" + target() == depStr ||
-		targetFile().string() == depStr
+		(!derived() && name() == depStr) || // compare to name
+		(!derived() && config.target == depStr) || // compare to stated target (ex. foo.a)
+		target() == depStr || // compare to derived (derived to foo.lib on msvc)
+		generated_output  == depStr || // not sure what this is for?!
+		targetFile().string() == depStr // compare to target output file
 		;
 }
 
diff --git a/src/task_ar.cc b/src/task_ar.cc
index b16a2f9..406dbdb 100644
--- a/src/task_ar.cc
+++ b/src/task_ar.cc
@@ -93,6 +93,8 @@ int TaskAR::runInner()
 	append(args, ar_option(toolchain, ctor::ar_opt::add_index));
 	append(args, ar_option(toolchain, ctor::ar_opt::create));
 	append(args, ar_option(toolchain, ctor::ar_opt::output, targetFile().string()));
+	append(args, to_strings(toolchain, {ctor::toolchain::msvc, ctor::ar_opt::custom, "/nologo"}));
+
 	for(const auto& task : getDependsTasks())
 	{
 		args.push_back(task->targetFile().string());
@@ -188,7 +190,6 @@ std::string TaskAR::flagsString() const
 			flagsStr += str;
 		}
 	}
-	flagsStr += "\n";
 
 	for(const auto& dep : config.depends)
 	{
diff --git a/src/task_cc.cc b/src/task_cc.cc
index e1f3023..f2cd34f 100644
--- a/src/task_cc.cc
+++ b/src/task_cc.cc
@@ -12,6 +12,7 @@
 #include "execute.h"
 #include "util.h"
 #include "tools.h"
+#include "deps.h"
 
 TaskCC::TaskCC(const ctor::build_configuration& config_, const ctor::settings& settings_,
                const std::string& sourceDir_, const ctor::source& source)
@@ -136,7 +137,8 @@ bool TaskCC::dirtyInner()
 		}
 	}
 
-	auto depList = readDeps(depsFile.string());
+	auto toolchain = getToolChain(config.system);
+	auto depList = readDeps(depsFile.string(), toolchain);
 	for(const auto& dep : depList)
 	{
 		if(!std::filesystem::exists(dep) ||
@@ -173,24 +175,25 @@ int TaskCC::runInner()
 		flagsStream << flagsString();
 	}
 
+	std::string output;
 	if(settings.verbose == 0)
 	{
 		switch(sourceLanguage())
 		{
 		case ctor::language::c:
-			std::cout << "CC ";
+			output += "CC ";
 			break;
 		case ctor::language::cpp:
-			std::cout << "CXX ";
+			output += "CXX ";
 			break;
 		case ctor::language::automatic:
 		case ctor::language::assembler:
 			// Only c/c++ handled by this task type.
 			break;
 		}
-		std::cout <<
-			sourceFile.lexically_normal().string() << " => " <<
-			targetFile().lexically_normal().string() << std::endl;
+		output += sourceFile.lexically_normal().string() + " => " +
+			targetFile().lexically_normal().string() + '\n';
+		std::cout << output << std::flush;
 	}
 
 	const auto& cfg = ctor::get_configuration();
@@ -275,18 +278,21 @@ std::vector<std::string> TaskCC::flags() const
 		{
 			append(flags, to_strings(toolchain, flag));
 		}
+		append(flags, to_strings(toolchain, {ctor::toolchain::msvc, ctor::c_opt::custom, "/nologo"}));
 		return flags;
 	case ctor::language::cpp:
 		for(const auto& flag : config.flags.cxxflags)
 		{
 			append(flags, to_strings(toolchain, flag));
 		}
+		append(flags, to_strings(toolchain, {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/nologo"}));
 		return flags;
 	default:
 		std::cerr << "Unknown CC target type\n";
 		exit(1);
 		break;
 	}
+
 }
 
 std::string TaskCC::flagsString() const
@@ -310,7 +316,7 @@ std::vector<std::string> TaskCC::getCompilerArgs() const
 	{
 	case ctor::language::c:
 		{
-			append(args, c_option(toolchain, ctor::c_opt::generate_dep_tree));
+			append(args, c_option(toolchain, ctor::c_opt::generate_dep_tree, depsFile.string()));
 
 			if(std::filesystem::path(config.target).extension() == ".so")
 			{
@@ -352,7 +358,7 @@ std::vector<std::string> TaskCC::getCompilerArgs() const
 
 	case ctor::language::cpp:
 		{
-			append(args, cxx_option(toolchain, ctor::cxx_opt::generate_dep_tree));
+			append(args, cxx_option(toolchain, ctor::cxx_opt::generate_dep_tree, depsFile.string()));
 
 			if(std::filesystem::path(config.target).extension() == ".so")
 			{
diff --git a/src/task_fn.cc b/src/task_fn.cc
index 2fa0ad6..258897c 100644
--- a/src/task_fn.cc
+++ b/src/task_fn.cc
@@ -97,7 +97,7 @@ std::vector<std::string> TaskFn::depends() const
 
 std::string TaskFn::target() const
 {
-	return _targetFile;
+	return _targetFile.string();
 }
 
 std::filesystem::path TaskFn::targetFile() const
diff --git a/src/task_ld.cc b/src/task_ld.cc
index c8cd1ea..dbe17c5 100644
--- a/src/task_ld.cc
+++ b/src/task_ld.cc
@@ -115,6 +115,7 @@ int TaskLD::runInner()
 	}
 
 	append(args, ld_option(toolchain, ctor::ld_opt::output, targetFile().string()));
+	append(args, to_strings(toolchain, {ctor::toolchain::msvc, ctor::ld_opt::custom, "/nologo"}));
 
 	{ // Write flags to file.
 		std::ofstream flagsStream(flagsFile);
@@ -126,8 +127,27 @@ int TaskLD::runInner()
 		std::cout << "LD => " << targetFile().string() << std::endl;
 	}
 
-	auto tool = compiler();
+	std::string tool;
 	const auto& c = ctor::get_configuration();
+
+	if(toolchain == ctor::toolchain::gcc ||
+	   toolchain == ctor::toolchain::clang)
+	{
+		tool = compiler();
+	}
+	else // msvc
+	{
+		switch(outputSystem())
+		{
+		case ctor::output_system::host:
+			tool = c.get(ctor::cfg::host_ld, "/usr/bin/ld");
+			break;
+		case ctor::output_system::build:
+			tool = c.get(ctor::cfg::build_ld, "/usr/bin/ld");
+			break;
+		}
+	}
+
 	return execute(settings, tool, args, c.env, is_self);
 }
 
@@ -196,7 +216,6 @@ std::string TaskLD::flagsString() const
 			flagsStr += str;
 		}
 	}
-	flagsStr += "\n";
 
 	for(const auto& dep : config.depends)
 	{
diff --git a/src/task_so.cc b/src/task_so.cc
index 75b5f3c..d818d94 100644
--- a/src/task_so.cc
+++ b/src/task_so.cc
@@ -93,6 +93,8 @@ int TaskSO::runInner()
 
 	append(args, ld_option(toolchain, ctor::ld_opt::output, targetFile().string()));
 
+	append(args, to_strings(toolchain, {ctor::toolchain::msvc, ctor::ld_opt::custom, "/nologo"}));
+
 	for(const auto& task : getDependsTasks())
 	{
 		args.push_back(task->targetFile().string());
@@ -183,7 +185,6 @@ std::string TaskSO::flagsString() const
 			flagsStr += str;
 		}
 	}
-	flagsStr += "\n";
 
 	for(const auto& dep : config.depends)
 	{
diff --git a/src/tools.cc b/src/tools.cc
index 394a91c..7f16a0e 100644
--- a/src/tools.cc
+++ b/src/tools.cc
@@ -6,6 +6,7 @@
 #include <filesystem>
 #include <iostream>
 #include <sstream>
+#include <algorithm>
 
 #include <cassert>
 #include <cstdio>
@@ -20,6 +21,9 @@ std::ostream& operator<<(std::ostream& stream, const ctor::c_opt& opt)
 	case ctor::c_opt::output: stream << "ctor::c_opt::output"; break;
 	case ctor::c_opt::debug: stream << "ctor::c_opt::debug"; break;
 	case ctor::c_opt::warn_all: stream << "ctor::c_opt::warn_all"; break;
+	case ctor::c_opt::warn_conversion: stream << "ctor::c_opt::warn_conversion"; break;
+	case ctor::c_opt::warn_shadow: stream << "ctor::c_opt::warn_shadow"; break;
+	case ctor::c_opt::warn_extra: stream << "ctor::c_opt::warn_extra"; break;
 	case ctor::c_opt::warnings_as_errors: stream << "ctor::c_opt::warnings_as_errors"; break;
 	case ctor::c_opt::generate_dep_tree: stream << "ctor::c_opt::generate_dep_tree"; break;
 	case ctor::c_opt::no_link: stream << "ctor::c_opt::no_link"; break;
@@ -28,6 +32,7 @@ std::ostream& operator<<(std::ostream& stream, const ctor::c_opt& opt)
 	case ctor::c_opt::optimization: stream << "ctor::c_opt::optimization"; break;
 	case ctor::c_opt::position_independent_code: stream << "ctor::c_opt::position_independent_code"; break;
 	case ctor::c_opt::position_independent_executable: stream << "ctor::c_opt::position_independent_executable"; break;
+	case ctor::c_opt::define: stream << "ctor::c_opt::define"; break;
 	case ctor::c_opt::custom: stream << "ctor::c_opt::custom"; break;
 	}
 
@@ -42,6 +47,9 @@ std::ostream& operator<<(std::ostream& stream, const ctor::cxx_opt& opt)
 	case ctor::cxx_opt::output: stream << "ctor::cxx_opt::output"; break;
 	case ctor::cxx_opt::debug: stream << "ctor::cxx_opt::debug"; break;
 	case ctor::cxx_opt::warn_all: stream << "ctor::cxx_opt::warn_all"; break;
+	case ctor::cxx_opt::warn_conversion: stream << "ctor::cxx_opt::warn_conversion"; break;
+	case ctor::cxx_opt::warn_shadow: stream << "ctor::cxx_opt::warn_shadow"; break;
+	case ctor::cxx_opt::warn_extra: stream << "ctor::cxx_opt::warn_extra"; break;
 	case ctor::cxx_opt::warnings_as_errors: stream << "ctor::cxx_opt::warnings_as_errors"; break;
 	case ctor::cxx_opt::generate_dep_tree: stream << "ctor::cxx_opt::generate_dep_tree"; break;
 	case ctor::cxx_opt::no_link: stream << "ctor::cxx_opt::no_link"; break;
@@ -50,6 +58,7 @@ std::ostream& operator<<(std::ostream& stream, const ctor::cxx_opt& opt)
 	case ctor::cxx_opt::optimization: stream << "ctor::cxx_opt::optimization"; break;
 	case ctor::cxx_opt::position_independent_code: stream << "ctor::cxx_opt::position_independent_code"; break;
 	case ctor::cxx_opt::position_independent_executable: stream << "ctor::cxx_opt::position_independent_executable"; break;
+	case ctor::cxx_opt::define: stream << "ctor::cxx_opt::define"; break;
 	case ctor::cxx_opt::custom: stream << "ctor::cxx_opt::custom"; break;
 	}
 
@@ -109,18 +118,27 @@ ctor::toolchain getToolChain(const std::string& compiler)
 	std::filesystem::path cc(compiler);
 	auto cc_cmd = cc.stem().string();
 
+	std::cout << "getToolChain: [" << cc_cmd << "] => ";
+
 	// Note: "g++" is a substring of "clang++" so "clang++" must be tested first.
 	if(cc_cmd.find("clang++") != std::string::npos ||
 	   cc_cmd.find("clang") != std::string::npos)
 	{
+		std::cout << "clang" << std::endl;
 		return ctor::toolchain::clang;
 	}
 	else if(cc_cmd.find("g++") != std::string::npos ||
 	        cc_cmd.find("gcc") != std::string::npos ||
 	        cc_cmd.find("mingw") != std::string::npos)
 	{
+		std::cout << "gcc" << std::endl;
 		return ctor::toolchain::gcc;
 	}
+	else if(to_lower(cc_cmd).find("cl") != std::string::npos)
+	{
+		std::cout << "msvc" << std::endl;
+		return ctor::toolchain::msvc;
+	}
 
 	std::cerr << "Unsupported output system.\n";
 	return ctor::toolchain::gcc;
@@ -146,12 +164,262 @@ ctor::toolchain getToolChain(ctor::output_system system)
 		return getToolChain(cfg.get(ctor::cfg::build_cxx, "g++"));
 	}
 }
+namespace msvc {
+std::string get_arch([[maybe_unused]] ctor::output_system system)
+{
+	std::string arch;
+	// TODO
+	return arch;
+}
+
+ctor::arch get_arch([[maybe_unused]] const std::string& str)
+{
+	return ctor::arch::windows;
+}
+
+ctor::c_flag c_option(const std::string& flag)
+{
+	if(flag.starts_with("/I"))
+	{
+		std::string path = flag.substr(2);
+		path.erase(0, path.find_first_not_of(' '));
+		return { ctor::c_opt::include_path, path };
+	}
+
+	return { ctor::c_opt::custom, flag };
+}
+
+ctor::cxx_flag cxx_option(const std::string& flag)
+{
+	if(flag.starts_with("/I"))
+	{
+		std::string path = flag.substr(2);
+		path.erase(0, path.find_first_not_of(' '));
+		return { ctor::cxx_opt::include_path, path };
+	}
+
+	return { ctor::cxx_opt::custom, flag };
+}
+
+ctor::ld_flag ld_option(const std::string& flag)
+{
+	if(flag.starts_with("/L"))
+	{
+		std::string path = flag.substr(2);
+		path.erase(0, path.find_first_not_of(' '));
+		return { ctor::ld_opt::library_path, path };
+	}
+
+	return { ctor::ld_opt::custom, flag };
+}
+
+ctor::ar_flag ar_option(const std::string& flag)
+{
+	return { ctor::ar_opt::custom, flag };
+}
+
+std::vector<std::string> cxx_option(ctor::cxx_opt opt, const std::string& arg,
+                                    const std::string& arg2)
+{
+	switch(opt)
+	{
+	case ctor::cxx_opt::output:
+		return {"/Fo\"" + arg + "\""};
+	case ctor::cxx_opt::debug:
+		return {"/DEBUG"};
+	case ctor::cxx_opt::warn_all:
+		return {"/W4"};
+	case ctor::cxx_opt::warn_conversion:
+		return {"/W4"}; // TODO: This is incorrect
+	case ctor::cxx_opt::warn_shadow:
+		return {"/W4"}; // TODO: This is incorrect
+	case ctor::cxx_opt::warn_extra:
+		return {"/W4"}; // TODO: This is incorrect
+	case ctor::cxx_opt::warnings_as_errors:
+		return {"/WX"};
+	case ctor::cxx_opt::generate_dep_tree:
+		return {"/sourceDependencies", arg};
+	case ctor::cxx_opt::no_link:
+		return {"/c"};
+	case ctor::cxx_opt::include_path:
+		return {"/I" + arg};
+	case ctor::cxx_opt::cpp_std:
+		return {"/std:" + arg};
+	case ctor::cxx_opt::optimization:
+		{
+			int o{0};
+			try
+			{
+				o = std::stoi(arg);
+				o = std::clamp(o, 0, 2);
+			}
+			catch(...)
+			{
+				// bad number?
+			}
+			return {"/O" + std::to_string(o)};
+		}
+	case ctor::cxx_opt::position_independent_code:
+		return {}; // TODO?
+	case ctor::cxx_opt::position_independent_executable:
+		return {}; // TODO?
+	case ctor::cxx_opt::define:
+		if(!arg2.empty())
+		{
+			return {"/D" + arg + "=" + esc(arg2)};
+		}
+		else
+		{
+			return {"/D" + arg};
+		}
+	case ctor::cxx_opt::custom:
+		return {arg};
+	}
+
+	std::cerr << "Unsupported compiler option.\n";
+	return {};
+}
+
+std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg,
+                                  const std::string& arg2)
+{
+	switch(opt)
+	{
+	case ctor::c_opt::output:
+		return {"/Fo\"" + arg + "\""};
+	case ctor::c_opt::debug:
+		return {"/DEBUG"};
+	case ctor::c_opt::warn_all:
+		return {"/W4"};
+	case ctor::c_opt::warn_conversion:
+		return {"/W4"}; // TODO: This is incorrect
+	case ctor::c_opt::warn_shadow:
+		return {"/W4"}; // TODO: This is incorrect
+	case ctor::c_opt::warn_extra:
+		return {"/W4"}; // TODO: This is incorrect
+	case ctor::c_opt::warnings_as_errors:
+		return {"/WX"};
+	case ctor::c_opt::generate_dep_tree:
+		return {"/sourceDependencies", arg};
+	case ctor::c_opt::no_link:
+		return {"/c"};
+	case ctor::c_opt::include_path:
+		return {"/I" + arg};
+	case ctor::c_opt::c_std:
+		return {"/std:" + arg};
+	case ctor::c_opt::optimization:
+		{
+			int o{0};
+			try
+			{
+				o = std::stoi(arg);
+				o = std::clamp(o, 0, 2);
+			}
+			catch(...)
+			{
+				// bad number?
+			}
+			return {"/O" + std::to_string(o)};
+		}
+	case ctor::c_opt::position_independent_code:
+		return {}; // TODO?
+	case ctor::c_opt::position_independent_executable:
+		return {}; // TODO?
+	case ctor::c_opt::define:
+		if(!arg2.empty())
+		{
+			return {"/D" + arg + "=" + esc(arg2)};
+		}
+		else
+		{
+			return {"/D" + arg};
+		}
+	case ctor::c_opt::custom:
+		return {arg};
+	}
+
+	std::cerr << "Unsupported compiler option.\n";
+	return {};
+}
+
+std::vector<std::string> ld_option(ctor::ld_opt opt, const std::string& arg,
+                                   [[maybe_unused]]const std::string& arg2)
+{
+	switch(opt)
+	{
+	case ctor::ld_opt::output:
+		return {"/out:" + arg + ""};
+	case ctor::ld_opt::strip:
+		return {}; // TODO?
+	case ctor::ld_opt::warn_all:
+		return {"/Wall"};
+	case ctor::ld_opt::warnings_as_errors:
+		return {"/WX"};
+	case ctor::ld_opt::library_path:
+		return {"/LIBPATH:\""+arg+"\""};
+	case ctor::ld_opt::link:
+		return {arg}; // TODO?
+	case ctor::ld_opt::cpp_std:
+		return {"/std:" + arg};
+	case ctor::ld_opt::build_shared:
+		return {}; // TODO?
+	case ctor::ld_opt::threads:
+		return {}; // TODO?
+	case ctor::ld_opt::position_independent_code:
+		return {}; // TODO?
+	case ctor::ld_opt::position_independent_executable:
+		return {}; // TODO?
+	case ctor::ld_opt::custom:
+		return {arg};
+	}
+
+	std::cerr << "Unsupported compiler option.\n";
+	return {};
+}
+
+std::vector<std::string> ar_option(ctor::ar_opt opt, const std::string& arg,
+                                   [[maybe_unused]]const std::string& arg2)
+{
+	switch(opt)
+	{
+	case ctor::ar_opt::replace:
+		return {};
+	case ctor::ar_opt::add_index:
+		return {};
+	case ctor::ar_opt::create:
+		return {};
+	case ctor::ar_opt::output:
+		return {"/out:" + arg};
+	case ctor::ar_opt::custom:
+		return {arg};
+	}
+
+	std::cerr << "Unsupported compiler option.\n";
+	return {};
+}
+
+std::vector<std::string> asm_option(ctor::asm_opt opt, const std::string& arg,
+                                    [[maybe_unused]]const std::string& arg2)
+{
+	switch(opt)
+	{
+	case ctor::asm_opt::custom:
+		return {arg};
+	}
+
+	std::cerr << "Unsupported compiler option.\n";
+	return {};
+}
+} // msvc::
+
 
 namespace gcc {
-std::string get_arch(ctor::output_system system)
+std::string get_arch([[maybe_unused]] ctor::output_system system)
 {
+	std::string arch;
+	// TODO popen on windows
+#if !defined(_WIN32)
 	std::string cmd;
-
 	const auto& c = ctor::get_configuration();
 	switch(system)
 	{
@@ -172,7 +440,6 @@ std::string get_arch(ctor::output_system system)
 		return {};//ctor::arch::unknown;
 	}
 
-	std::string arch;
 	while(!feof(pipe))
 	{
 		constexpr auto buffer_size{1024};
@@ -198,6 +465,7 @@ std::string get_arch(ctor::output_system system)
 	// Remove 'Target: ' prefix
 	constexpr auto prefix_length{8};
 	arch = arch.substr(prefix_length);
+#endif // _WIN32
 	return arch;
 }
 
@@ -245,6 +513,61 @@ ctor::c_flag c_option(const std::string& flag)
 		return { ctor::c_opt::include_path, path };
 	}
 
+	if(flag.starts_with("-std="))
+	{
+		std::string std = flag.substr(5);
+		return { ctor::c_opt::c_std, std };
+	}
+
+	if(flag.starts_with("-O"))
+	{
+		std::string opt = flag.substr(2, 1);
+		return { ctor::c_opt::optimization, opt };
+	}
+
+	if(flag.starts_with("-Wall"))
+	{
+		return { ctor::c_opt::warn_all };
+	}
+
+	if(flag.starts_with("-Wconversion"))
+	{
+		return { ctor::c_opt::warn_conversion};
+	}
+
+	if(flag.starts_with("-Wshadow"))
+	{
+		return { ctor::c_opt::warn_shadow};
+	}
+
+	if(flag.starts_with("-Wextra"))
+	{
+		return { ctor::c_opt::warn_extra};
+	}
+
+	if(flag.starts_with("-Werror"))
+	{
+		return { ctor::c_opt::warnings_as_errors };
+	}
+
+	if(flag.starts_with("-g"))
+	{
+		return { ctor::c_opt::debug };
+	}
+
+	if(flag.starts_with("-D"))
+	{
+		std::string def = flag.substr(2);
+		auto pos = def.find('=');
+		if(pos != def.npos)
+		{
+			return { ctor::c_opt::define, def.substr(0, pos), def.substr(pos + 1) };
+		}
+		else
+		{
+			return { ctor::c_opt::define, def };
+		}
+	}
 	return { ctor::c_opt::custom, flag };
 }
 
@@ -257,6 +580,62 @@ ctor::cxx_flag cxx_option(const std::string& flag)
 		return { ctor::cxx_opt::include_path, path };
 	}
 
+	if(flag.starts_with("-std="))
+	{
+		std::string std = flag.substr(5);
+		return { ctor::cxx_opt::cpp_std, std };
+	}
+
+	if(flag.starts_with("-O"))
+	{
+		std::string opt = flag.substr(2, 1);
+		return { ctor::cxx_opt::optimization, opt };
+	}
+
+	if(flag.starts_with("-Wall"))
+	{
+		return { ctor::cxx_opt::warn_all };
+	}
+
+	if(flag.starts_with("-Werror"))
+	{
+		return { ctor::cxx_opt::warnings_as_errors };
+	}
+
+	if(flag.starts_with("-Wconversion"))
+	{
+		return { ctor::cxx_opt::warn_conversion};
+	}
+
+	if(flag.starts_with("-Wshadow"))
+	{
+		return { ctor::cxx_opt::warn_shadow};
+	}
+
+	if(flag.starts_with("-Wextra"))
+	{
+		return { ctor::cxx_opt::warn_extra};
+	}
+
+	if(flag.starts_with("-g"))
+	{
+		return { ctor::cxx_opt::debug };
+	}
+
+	if(flag.starts_with("-D"))
+	{
+		std::string def = flag.substr(2);
+		auto pos = def.find('=');
+		if(pos != def.npos)
+		{
+			return { ctor::cxx_opt::define, def.substr(0, pos), def.substr(pos + 1) };
+		}
+		else
+		{
+			return { ctor::cxx_opt::define, def };
+		}
+	}
+
 	return { ctor::cxx_opt::custom, flag };
 }
 
@@ -269,6 +648,11 @@ ctor::ld_flag ld_option(const std::string& flag)
 		return { ctor::ld_opt::library_path, path };
 	}
 
+	if(flag.starts_with("-pthread"))
+	{
+		return { ctor::ld_opt::threads };
+	}
+
 	return { ctor::ld_opt::custom, flag };
 }
 
@@ -277,7 +661,8 @@ ctor::ar_flag ar_option(const std::string& flag)
 	return { ctor::ar_opt::custom, flag };
 }
 
-std::vector<std::string> cxx_option(ctor::cxx_opt opt, const std::string& arg)
+std::vector<std::string> cxx_option(ctor::cxx_opt opt, const std::string& arg,
+                                    const std::string& arg2)
 {
 	switch(opt)
 	{
@@ -287,6 +672,12 @@ std::vector<std::string> cxx_option(ctor::cxx_opt opt, const std::string& arg)
 		return {"-g"};
 	case ctor::cxx_opt::warn_all:
 		return {"-Wall"};
+	case ctor::cxx_opt::warn_conversion:
+		return {"-Wconversion"};
+	case ctor::cxx_opt::warn_shadow:
+		return {"-Wshadow"};
+	case ctor::cxx_opt::warn_extra:
+		return {"-Wextra"};
 	case ctor::cxx_opt::warnings_as_errors:
 		return {"-Werror"};
 	case ctor::cxx_opt::generate_dep_tree:
@@ -303,6 +694,12 @@ std::vector<std::string> cxx_option(ctor::cxx_opt opt, const std::string& arg)
 		return {"-fPIC"};
 	case ctor::cxx_opt::position_independent_executable:
 		return {"-fPIE"};
+	case ctor::cxx_opt::define:
+		if(!arg2.empty())
+		{
+			return {"-D" + arg + "=" + arg2};
+		}
+		return {"-D" + arg};
 	case ctor::cxx_opt::custom:
 		return {arg};
 	}
@@ -311,7 +708,8 @@ std::vector<std::string> cxx_option(ctor::cxx_opt opt, const std::string& arg)
 	return {};
 }
 
-std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg)
+std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg,
+                                  const std::string& arg2)
 {
 	switch(opt)
 	{
@@ -321,6 +719,12 @@ std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg)
 		return {"-g"};
 	case ctor::c_opt::warn_all:
 		return {"-Wall"};
+	case ctor::c_opt::warn_conversion:
+		return {"-Wconversion"};
+	case ctor::c_opt::warn_shadow:
+		return {"-Wshadow"};
+	case ctor::c_opt::warn_extra:
+		return {"-Wextra"};
 	case ctor::c_opt::warnings_as_errors:
 		return {"-Werror"};
 	case ctor::c_opt::generate_dep_tree:
@@ -337,6 +741,12 @@ std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg)
 		return {"-fPIC"};
 	case ctor::c_opt::position_independent_executable:
 		return {"-fPIE"};
+	case ctor::c_opt::define:
+		if(!arg2.empty())
+		{
+			return {"-D" + arg + "=" + arg2};
+		}
+		return {"-D" + arg};
 	case ctor::c_opt::custom:
 		return {arg};
 	}
@@ -345,7 +755,8 @@ std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg)
 	return {};
 }
 
-std::vector<std::string> ld_option(ctor::ld_opt opt, const std::string& arg)
+std::vector<std::string> ld_option(ctor::ld_opt opt, const std::string& arg,
+                                   [[maybe_unused]]const std::string& arg2)
 {
 	switch(opt)
 	{
@@ -379,7 +790,8 @@ std::vector<std::string> ld_option(ctor::ld_opt opt, const std::string& arg)
 	return {};
 }
 
-std::vector<std::string> ar_option(ctor::ar_opt opt, const std::string& arg)
+std::vector<std::string> ar_option(ctor::ar_opt opt, const std::string& arg,
+                                   [[maybe_unused]]const std::string& arg2)
 {
 	switch(opt)
 	{
@@ -399,7 +811,8 @@ std::vector<std::string> ar_option(ctor::ar_opt opt, const std::string& arg)
 	return {};
 }
 
-std::vector<std::string> asm_option(ctor::asm_opt opt, const std::string& arg)
+std::vector<std::string> asm_option(ctor::asm_opt opt, const std::string& arg,
+                                    [[maybe_unused]]const std::string& arg2)
 {
 	switch(opt)
 	{
@@ -420,6 +833,8 @@ std::string get_arch(ctor::output_system system)
 	case ctor::toolchain::clang:
 	case ctor::toolchain::gcc:
 		return gcc::get_arch(system);
+	case ctor::toolchain::msvc:
+		return msvc::get_arch(system);
 	case ctor::toolchain::any:
 	case ctor::toolchain::none:
 		break;
@@ -435,6 +850,8 @@ ctor::arch get_arch(ctor::output_system system, const std::string& str)
 	case ctor::toolchain::clang:
 	case ctor::toolchain::gcc:
 		return gcc::get_arch(str);
+	case ctor::toolchain::msvc:
+		return msvc::get_arch(str);
 	case ctor::toolchain::any:
 	case ctor::toolchain::none:
 		break;
@@ -444,13 +861,16 @@ ctor::arch get_arch(ctor::output_system system, const std::string& str)
 
 std::vector<std::string> c_option(ctor::toolchain toolchain,
                                   ctor::c_opt opt,
-                                  const std::string& arg)
+                                  const std::string& arg,
+                                  const std::string& arg2)
 {
 	switch(toolchain)
 	{
 	case ctor::toolchain::gcc:
 	case ctor::toolchain::clang:
-		return gcc::c_option(opt, arg);
+		return gcc::c_option(opt, arg, arg2);
+	case ctor::toolchain::msvc:
+		return msvc::c_option(opt, arg, arg2);
 	case ctor::toolchain::any:
 		{
 			std::ostringstream ss;
@@ -459,6 +879,10 @@ std::vector<std::string> c_option(ctor::toolchain toolchain,
 			{
 				ss << ", \"" << arg << "\"";
 			}
+			if(!arg2.empty())
+			{
+				ss << ", \"" << arg2 << "\"";
+			}
 			ss << "}";
 			return { ss.str() };
 		}
@@ -472,13 +896,16 @@ std::vector<std::string> c_option(ctor::toolchain toolchain,
 
 std::vector<std::string> cxx_option(ctor::toolchain toolchain,
                                     ctor::cxx_opt opt,
-                                    const std::string& arg)
+                                    const std::string& arg,
+                                    const std::string& arg2)
 {
 	switch(toolchain)
 	{
 	case ctor::toolchain::gcc:
 	case ctor::toolchain::clang:
-		return gcc::cxx_option(opt, arg);
+		return gcc::cxx_option(opt, arg, arg2);
+	case ctor::toolchain::msvc:
+		return msvc::cxx_option(opt, arg, arg2);
 	case ctor::toolchain::any:
 		{
 			std::ostringstream ss;
@@ -487,6 +914,10 @@ std::vector<std::string> cxx_option(ctor::toolchain toolchain,
 			{
 				ss << ", \"" << arg << "\"";
 			}
+			if(!arg2.empty())
+			{
+				ss << ", \"" << arg2 << "\"";
+			}
 			ss << "}";
 			return { ss.str() };
 		}
@@ -500,13 +931,16 @@ std::vector<std::string> cxx_option(ctor::toolchain toolchain,
 
 std::vector<std::string> ld_option(ctor::toolchain toolchain,
                                    ctor::ld_opt opt,
-                                   const std::string& arg)
+                                   const std::string& arg,
+                                   const std::string& arg2)
 {
 	switch(toolchain)
 	{
 	case ctor::toolchain::gcc:
 	case ctor::toolchain::clang:
-		return gcc::ld_option(opt, arg);
+		return gcc::ld_option(opt, arg, arg2);
+	case ctor::toolchain::msvc:
+		return msvc::ld_option(opt, arg, arg2);
 	case ctor::toolchain::any:
 		{
 			std::ostringstream ss;
@@ -515,6 +949,10 @@ std::vector<std::string> ld_option(ctor::toolchain toolchain,
 			{
 				ss << ", \"" << arg << "\"";
 			}
+			if(!arg2.empty())
+			{
+				ss << ", \"" << arg2 << "\"";
+			}
 			ss << "}";
 			return { ss.str() };
 		}
@@ -528,13 +966,16 @@ std::vector<std::string> ld_option(ctor::toolchain toolchain,
 
 std::vector<std::string> ar_option(ctor::toolchain toolchain,
                                    ctor::ar_opt opt,
-                                   const std::string& arg)
+                                   const std::string& arg,
+                                   const std::string& arg2)
 {
 	switch(toolchain)
 	{
 	case ctor::toolchain::gcc:
 	case ctor::toolchain::clang:
-		return gcc::ar_option(opt, arg);
+		return gcc::ar_option(opt, arg, arg2);
+	case ctor::toolchain::msvc:
+		return msvc::ar_option(opt, arg, arg2);
 	case ctor::toolchain::any:
 		{
 			std::ostringstream ss;
@@ -543,6 +984,10 @@ std::vector<std::string> ar_option(ctor::toolchain toolchain,
 			{
 				ss << ", \"" << arg << "\"";
 			}
+			if(!arg2.empty())
+			{
+				ss << ", \"" << arg2 << "\"";
+			}
 			ss << "}";
 			return { ss.str() };
 		}
@@ -556,13 +1001,16 @@ std::vector<std::string> ar_option(ctor::toolchain toolchain,
 
 std::vector<std::string> asm_option(ctor::toolchain toolchain,
                                     ctor::asm_opt opt,
-                                    const std::string& arg)
+                                    const std::string& arg,
+                                    const std::string& arg2)
 {
 	switch(toolchain)
 	{
 	case ctor::toolchain::gcc:
 	case ctor::toolchain::clang:
-		return gcc::asm_option(opt, arg);
+		return gcc::asm_option(opt, arg, arg2);
+	case ctor::toolchain::msvc:
+		return msvc::asm_option(opt, arg, arg2);
 	case ctor::toolchain::any:
 		{
 			std::ostringstream ss;
@@ -571,6 +1019,10 @@ std::vector<std::string> asm_option(ctor::toolchain toolchain,
 			{
 				ss << ", \"" << arg << "\"";
 			}
+			if(!arg2.empty())
+			{
+				ss << ", \"" << arg2 << "\"";
+			}
 			ss << "}";
 			return { ss.str() };
 		}
@@ -590,6 +1042,8 @@ ctor::c_flag c_option(const std::string& flag, ctor::toolchain toolchain)
 	case ctor::toolchain::gcc:
 	case ctor::toolchain::clang:
 		return gcc::c_option(flag);
+	case ctor::toolchain::msvc:
+		return msvc::c_option(flag);
 	case ctor::toolchain::any:
 	case ctor::toolchain::none:
 		break;
@@ -605,6 +1059,8 @@ ctor::cxx_flag cxx_option(const std::string& flag, ctor::toolchain toolchain)
 	case ctor::toolchain::gcc:
 	case ctor::toolchain::clang:
 		return gcc::cxx_option(flag);
+	case ctor::toolchain::msvc:
+		return msvc::cxx_option(flag);
 	case ctor::toolchain::any:
 	case ctor::toolchain::none:
 		break;
@@ -620,6 +1076,8 @@ ctor::ld_flag ld_option(const std::string& flag, ctor::toolchain toolchain)
 	case ctor::toolchain::gcc:
 	case ctor::toolchain::clang:
 		return gcc::ld_option(flag);
+	case ctor::toolchain::msvc:
+		return msvc::ld_option(flag);
 	case ctor::toolchain::any:
 	case ctor::toolchain::none:
 		break;
@@ -635,6 +1093,8 @@ ctor::ar_flag ar_option(const std::string& flag, ctor::toolchain toolchain)
 	case ctor::toolchain::gcc:
 	case ctor::toolchain::clang:
 		return gcc::ar_option(flag);
+	case ctor::toolchain::msvc:
+		return msvc::ar_option(flag);
 	case ctor::toolchain::any:
 	case ctor::toolchain::none:
 		break;
@@ -649,6 +1109,7 @@ ctor::asm_flag asm_option(const std::string& flag, ctor::toolchain toolchain)
 	{
 	case ctor::toolchain::gcc:
 	case ctor::toolchain::clang:
+	case ctor::toolchain::msvc:
 	case ctor::toolchain::any:
 	case ctor::toolchain::none:
 		break;
@@ -665,7 +1126,7 @@ std::vector<std::string> to_strings(ctor::toolchain toolchain,
 	if(flag.toolchain == ctor::toolchain::any ||
 	   flag.toolchain == toolchain)
 	{
-		return c_option(toolchain, flag.opt, flag.arg);
+		return c_option(toolchain, flag.opt, flag.arg, flag.arg2);
 	}
 
 	return {};
@@ -677,7 +1138,7 @@ std::vector<std::string> to_strings(ctor::toolchain toolchain,
 	if(flag.toolchain == ctor::toolchain::any ||
 	   flag.toolchain == toolchain)
 	{
-		return cxx_option(toolchain, flag.opt, flag.arg);
+		return cxx_option(toolchain, flag.opt, flag.arg, flag.arg2);
 	}
 
 	return {};
@@ -689,7 +1150,7 @@ std::vector<std::string> to_strings(ctor::toolchain toolchain,
 	if(flag.toolchain == ctor::toolchain::any ||
 	   flag.toolchain == toolchain)
 	{
-		return ld_option(toolchain, flag.opt, flag.arg);
+		return ld_option(toolchain, flag.opt, flag.arg, flag.arg2);
 	}
 
 	return {};
@@ -701,7 +1162,7 @@ std::vector<std::string> to_strings(ctor::toolchain toolchain,
 	if(flag.toolchain == ctor::toolchain::any ||
 	   flag.toolchain == toolchain)
 	{
-		return ar_option(toolchain, flag.opt, flag.arg);
+		return ar_option(toolchain, flag.opt, flag.arg, flag.arg2);
 	}
 
 	return {};
@@ -713,7 +1174,7 @@ std::vector<std::string> to_strings(ctor::toolchain toolchain,
 	if(flag.toolchain == ctor::toolchain::any ||
 	   flag.toolchain == toolchain)
 	{
-		return asm_option(toolchain, flag.opt, flag.arg);
+		return asm_option(toolchain, flag.opt, flag.arg, flag.arg2);
 	}
 
 	return {};
@@ -732,10 +1193,11 @@ ctor::toolchain guess_toolchain(const std::string& opt)
 		return ctor::toolchain::gcc;
 	}
 
-	//if(opt[0] == '/')
-	//{
-	//	return ctor::toolchain::msvc;
-	//}
+	if(opt[0] == '/')
+	{
+		return ctor::toolchain::msvc;
+	}
+
 	return ctor::toolchain::any;
 }
 }
@@ -806,8 +1268,8 @@ ctor::target_type target_type_from_extension(ctor::toolchain toolchain,
 		}
 	}
 
-	if(toolchain == ctor::toolchain::any// ||
-	   //toolchain == ctor::toolchain::msvc ||
+	if(toolchain == ctor::toolchain::any ||
+	   toolchain == ctor::toolchain::msvc// ||
 	   //toolchain == ctor::toolchain::mingw ||
 		)
 	{
diff --git a/src/tools.h b/src/tools.h
index 188d49f..0e7fc15 100644
--- a/src/tools.h
+++ b/src/tools.h
@@ -32,31 +32,36 @@ ctor::toolchain getToolChain(ctor::output_system system);
 //! tool-chain
 std::vector<std::string> c_option(ctor::toolchain toolchain,
                                   ctor::c_opt option,
-                                  const std::string& arg = {});
+                                  const std::string& arg = {},
+                                  const std::string& arg2 = {});
 
 //! Get tool argument(s) for specific option type matching the supplied
 //! tool-chain
 std::vector<std::string> cxx_option(ctor::toolchain toolchain,
                                     ctor::cxx_opt option,
-                                    const std::string& arg = {});
+                                    const std::string& arg = {},
+                                    const std::string& arg2 = {});
 
 //! Get tool argument(s) for specific option type matching the supplied
 //! tool-chain
 std::vector<std::string> ld_option(ctor::toolchain toolchain,
                                    ctor::ld_opt option,
-                                   const std::string& arg = {});
+                                   const std::string& arg = {},
+                                   const std::string& arg2 = {});
 
 //! Get tool argument(s) for specific option type matching the supplied
 //! tool-chain
 std::vector<std::string> ar_option(ctor::toolchain toolchain,
                                    ctor::ar_opt option,
-                                   const std::string& arg = {});
+                                   const std::string& arg = {},
+                                   const std::string& arg2 = {});
 
 //! Get tool argument(s) for specific option type matching the supplied
 //! tool-chain
 std::vector<std::string> asm_option(ctor::toolchain toolchain,
                                     ctor::asm_opt option,
-                                    const std::string& arg = {});
+                                    const std::string& arg = {},
+                                    const std::string& arg2 = {});
 
 
 
diff --git a/src/util.cc b/src/util.cc
index 73b158d..f13a4a4 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -7,10 +7,17 @@
 #include <fstream>
 #include <algorithm>
 
+namespace {
+char to_lower_c(char ch)
+{
+	return static_cast<char>(::tolower(ch));
+}
+}
+
 std::string to_lower(const std::string& str)
 {
 	std::string out{str};
-	std::transform(out.begin(), out.end(), out.begin(), ::tolower);
+	std::transform(out.begin(), out.end(), out.begin(), to_lower_c);
 	return out;
 }
 
@@ -19,73 +26,18 @@ std::string readFile(const std::string& fileName)
 	std::ifstream ifs(fileName.c_str(),
 	                  std::ios::in | std::ios::binary | std::ios::ate);
 
-	std::ifstream::pos_type fileSize = ifs.tellg();
-	ifs.seekg(0, std::ios::beg);
-
-	std::vector<char> bytes(static_cast<std::size_t>(fileSize));
-	ifs.read(bytes.data(), fileSize);
-
-	return {bytes.data(), static_cast<std::size_t>(fileSize)};
-}
-
-std::vector<std::string> readDeps(const std::string& depFile)
-{
-	if(!std::filesystem::exists(depFile))
+	auto tell = ifs.tellg();
+	if(tell < 0)
 	{
 		return {};
 	}
+	auto fileSize = static_cast<std::size_t>(tell);
+	ifs.seekg(0, std::ios::beg);
 
-	auto str = readFile(depFile);
-
-	std::vector<std::string> output;
-	std::string tmp;
-	bool start{false};
-	bool in_whitespace{false};
-	for(const auto& c : str)
-	{
-		if(c == '\\' || c == '\n')
-		{
-			continue;
-		}
-
-		if(c == ':')
-		{
-			start = true;
-			continue;
-		}
-
-		if(!start)
-		{
-			continue;
-		}
-
-		if(c == ' ' || c == '\t')
-		{
-			if(in_whitespace)
-			{
-				continue;
-			}
-
-			if(!tmp.empty())
-			{
-				output.push_back(tmp);
-			}
-			tmp.clear();
-			in_whitespace = true;
-		}
-		else
-		{
-			in_whitespace = false;
-			tmp += c;
-		}
-	}
-
-	if(!tmp.empty())
-	{
-		output.push_back(tmp);
-	}
+	std::vector<char> bytes(fileSize);
+	ifs.read(bytes.data(), static_cast<long>(bytes.size()));
 
-	return output;
+	return { bytes.data(), bytes.size() };
 }
 
 ctor::language languageFromExtension(const std::filesystem::path& file)
@@ -229,15 +181,36 @@ std::string locate(const std::string& prog,
 		}
 	}
 
+	if(std::filesystem::exists(program + ".exe"))
+	{
+		if(check_executable(program + ".exe"))
+		{
+			return program + ".exe";
+		}
+	}
+
 	for(const auto& path_str : paths)
 	{
 		std::filesystem::path path(path_str);
-		auto prog_path = path / program;
-		if(std::filesystem::exists(prog_path))
 		{
-			if(check_executable(prog_path))
+			auto prog_path = path / program;
+			if(std::filesystem::exists(prog_path))
+			{
+				if(check_executable(prog_path))
+				{
+					return prog_path.string();
+				}
+			}
+		}
+
+		{
+			auto prog_path = path / (program + ".exe");
+			if(std::filesystem::exists(prog_path))
 			{
-				return prog_path.string();
+				if(check_executable(prog_path))
+				{
+					return prog_path.string();
+				}
 			}
 		}
 	}
diff --git a/src/util.h b/src/util.h
index af5bbd6..edeaf06 100644
--- a/src/util.h
+++ b/src/util.h
@@ -12,7 +12,7 @@
 std::string to_lower(const std::string& str);
 
 std::string readFile(const std::string& fileName);
-std::vector<std::string> readDeps(const std::string& depFile);
+std::vector<std::string> readDeps(const std::string& depFile, ctor::toolchain toolchain = ctor::toolchain::msvc);
 ctor::language languageFromExtension(const std::filesystem::path& file);
 std::string cleanUp(const std::string& path);
 
diff --git a/test/ctor.cc b/test/ctor.cc
index ccf1c31..3be9780 100644
--- a/test/ctor.cc
+++ b/test/ctor.cc
@@ -10,15 +10,18 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
 	return
 	{
 		{
+			.name = "testprog",
 			.type = ctor::target_type::unit_test,
 			.system = ctor::output_system::build,
 			.target = "testprog",
 			.sources = {
-				"testprog.cc",
+				{"testprog.cc"},
 			},
 			.flags = {
 				.cxxflags = {
-					"-std=c++20", "-O3", "-Wall", "-Werror",
+					"-std=c++20", "-O3", "-Wall",// "-Werror",
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"},
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"},
 				},
 			},
 		},
@@ -35,9 +38,11 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
 			.depends = { "testprog", },
 			.flags = {
 				.cxxflags = {
-					"-std=c++20", "-O3", "-Wall", "-Werror",
+					"-std=c++20", "-O3", "-Wall",// "-Werror",
 					"-I../src", "-Iuunit",
 					"-DOUTPUT=\"execute\"",
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"},
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"},
 				},
 				.ldflags = { "-pthread" },
 			},
@@ -50,12 +55,14 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
 				"tasks_test.cc",
 				"testmain.cc",
 			},
-			.depends = { "libctor_nomain.a" },
+			.depends = { "libctor_nomain.lib" },
 			.flags = {
 				.cxxflags = {
-					"-std=c++20", "-O3", "-Wall", "-Werror",
+					"-std=c++20", "-O3", "-Wall",// "-Werror",
 					"-I../src", "-Iuunit",
 					"-DOUTPUT=\"tasks\"",
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"},
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"},
 				},
 				.ldflags = { "-pthread" },
 			},
@@ -68,12 +75,14 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
 				"source_type_test.cc",
 				"testmain.cc",
 			},
-			.depends = { "libctor_nomain.a" },
+			.depends = { "libctor_nomain.lib" },
 			.flags = {
 				.cxxflags = {
-					"-std=c++20", "-O3", "-Wall", "-Werror",
+					"-std=c++20", "-O3", "-Wall",// "-Werror",
 					"-I../src", "-Iuunit",
 					"-DOUTPUT=\"source_type\"",
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"},
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"},
 				},
 				.ldflags = { "-pthread" },
 			},
@@ -91,19 +100,22 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
 			//.depends = { "libctor_nomain.a" },
 			.flags = {
 				.cxxflags = {
-					"-std=c++20", "-O3", "-Wall", "-Werror",
+					"-std=c++20", "-O3", "-Wall",// "-Werror",
 					"-I../src", "-Iuunit",
 					"-DOUTPUT=\"tools\"",
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"},
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"},
 				},
 			},
 		},
 		{
 			.type = ctor::target_type::unit_test_library,
 			.system = ctor::output_system::build,
-			.target = "libctor_nomain.a",
+			.target = "libctor_nomain.lib",
 			.sources = {
 				"../src/build.cc",
 				"../src/configure.cc",
+				"../src/deps.cc",
 				"../src/execute.cc",
 				"../src/rebuild.cc",
 				"../src/tasks.cc",
@@ -116,11 +128,33 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
 				"../src/tools.cc",
 				"../src/util.cc",
 				"../src/externals_manual.cc",
+				{ctor::toolchain::msvc, "../getopt-for-windows/getopt.c"},
 			},
 			.flags = {
+				.cflags = {
+					"-O3",
+//					"-g",
+//					"-Wall",
+//					"-Wconversion",
+//					"-Wextra",
+					//"-Werror",
+					"-I../src",
+					{ctor::toolchain::msvc, ctor::c_opt::custom, "/I../getopt-for-windows"},
+					{ctor::toolchain::msvc, ctor::c_opt::custom, "/D_X86_"},
+					{ctor::toolchain::msvc, ctor::c_opt::custom, "/EHsc"},
+					{ctor::toolchain::msvc, ctor::c_opt::custom, "/external:W0"},
+					{ctor::toolchain::msvc, ctor::c_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"},
+				},
 				.cxxflags = {
-					"-std=c++20", "-O3", "-Wall", "-Werror",
+					"-std=c++20", "-O3", "-Wall",// "-Werror",
 					"-I../src",
+					"-Ijson/include",
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/Igetopt-for-windows"},
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_X86_"},
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"},
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/external:W0"},
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"},
+					{ctor::toolchain::msvc, ctor::cxx_opt::custom, "/Dstrdup=_strdup"},
 				},
 				.ldflags = { "-pthread" },
 			},
diff --git a/test/execute_test.cc b/test/execute_test.cc
index 4c686bf..027ef3b 100644
--- a/test/execute_test.cc
+++ b/test/execute_test.cc
@@ -30,11 +30,11 @@ public:
 		uASSERT(!cmd.empty());
 
 		auto value = execute(s, cmd, {"retval", "0"}, {}, false);
-		uASSERT_EQUAL(0, value);
+		assert_equal(0, value, __FILE__, __LINE__);
 		value = execute(s, cmd, {"retval", "1"}, {}, false);
-		uASSERT_EQUAL(1, value);
+		assert_equal(1, value, __FILE__, __LINE__);
 		value = execute(s, "no-such-binary", {}, {}, false);
-		uASSERT_EQUAL(1, value);
+		assert_equal(1, value, __FILE__, __LINE__);
 	}
 
 	void env()
@@ -59,7 +59,7 @@ public:
 		env["LANG"] = "foo";
 
 		auto value = execute(s, cmd, {"envdump", tmp.get()}, env, false);
-		uASSERT_EQUAL(0, value);
+		assert_equal(0, value, __FILE__, __LINE__);
 
 		std::vector<std::string> vars;
 		{
diff --git a/test/source_type_test.cc b/test/source_type_test.cc
index 288f1e5..ced8e3a 100644
--- a/test/source_type_test.cc
+++ b/test/source_type_test.cc
@@ -53,22 +53,22 @@ public:
 	{
 		{ // c++
 			TestableTaskCC task("hello.cc");
-			uASSERT_EQUAL(ctor::language::cpp, task.language());
+			assert_equal(ctor::language::cpp, task.language(), __FILE__, __LINE__);
 		}
 
 		{ // c
 			TestableTaskCC task("hello.c");
-			uASSERT_EQUAL(ctor::language::c, task.language());
+			assert_equal(ctor::language::c, task.language(), __FILE__, __LINE__);
 		}
 
 		{ // asm
 			TestableTaskCC task("hello.s");
-			uASSERT_EQUAL(ctor::language::assembler, task.language());
+			assert_equal(ctor::language::assembler, task.language(), __FILE__, __LINE__);
 		}
 
 		{ // custom/explicit language
 			TestableTaskCC task( {"hello.foo", ctor::language::assembler} );
-			uASSERT_EQUAL(ctor::language::assembler, task.language());
+			assert_equal(ctor::language::assembler, task.language(), __FILE__, __LINE__);
 		}
 
 		// Note: Failure state will result in exit(1) so cannot be tested
diff --git a/test/suite/test.sh b/test/suite/test.sh
index c112351..23740a9 100755
--- a/test/suite/test.sh
+++ b/test/suite/test.sh
@@ -1,4 +1,6 @@
 #!/bin/bash
+#set -x
+
 : ${CXX:=g++}
 : ${CTORDIR:=../../build}
 : ${BUILDDIR:=build}
diff --git a/test/tasks_test.cc b/test/tasks_test.cc
index 3de6982..c6909c0 100644
--- a/test/tasks_test.cc
+++ b/test/tasks_test.cc
@@ -93,17 +93,17 @@ public:
 		using namespace std::string_literals;
 		ctor::settings settings{};
 		const auto& targets = getTargets(settings);
-		uASSERT_EQUAL(4u, targets.size());
+		assert_equal(4u, targets.size(), __FILE__, __LINE__);
 
-		uASSERT_EQUAL("target1"s, targets[0].config.target);
-		uASSERT_EQUAL("target2"s, targets[1].config.target);
-		uASSERT_EQUAL("target3"s, targets[2].config.target);
-		uASSERT_EQUAL("target4"s, targets[3].config.target);
+		assert_equal("target1"s, targets[0].config.target, __FILE__, __LINE__);
+		assert_equal("target2"s, targets[1].config.target, __FILE__, __LINE__);
+		assert_equal("target3"s, targets[2].config.target, __FILE__, __LINE__);
+		assert_equal("target4"s, targets[3].config.target, __FILE__, __LINE__);
 
-		uASSERT_EQUAL("test"s, targets[0].path);
-		uASSERT_EQUAL("test"s, targets[1].path);
-		uASSERT_EQUAL("test"s, targets[2].path);
-		uASSERT_EQUAL("test"s, targets[3].path);
+		assert_equal("test"s, targets[0].path, __FILE__, __LINE__);
+		assert_equal("test"s, targets[1].path, __FILE__, __LINE__);
+		assert_equal("test"s, targets[2].path, __FILE__, __LINE__);
+		assert_equal("test"s, targets[3].path, __FILE__, __LINE__);
 	}
 
 	void getTasks_test()
@@ -112,26 +112,26 @@ public:
 		ctor::settings settings{ .builddir = "foo" };
 		{
 			auto tasks = getTasks(settings);
-			uASSERT_EQUAL(6u, tasks.size());
+			assert_equal(6u, tasks.size(), __FILE__, __LINE__);
 			// Note: count() is used here because the order doesn't matter
-			uASSERT_EQUAL(1u, count(tasks, "target1"s));
-			uASSERT_EQUAL(1u, count(tasks, "target2"s));
-			uASSERT_EQUAL(1u, count(tasks, "target3"s));
-			uASSERT_EQUAL(1u, count(tasks, "target4"s));
-			uASSERT_EQUAL(1u, count(tasks, "test/target1-foo_cc.o"s));
-			uASSERT_EQUAL(1u, count(tasks, "test/target1-bar_c.o"s));
+			assert_equal(1u, count(tasks, "target1"s), __FILE__, __LINE__);
+			assert_equal(1u, count(tasks, "target2"s), __FILE__, __LINE__);
+			assert_equal(1u, count(tasks, "target3"s), __FILE__, __LINE__);
+			assert_equal(1u, count(tasks, "target4"s), __FILE__, __LINE__);
+			assert_equal(1u, count(tasks, "test/target1-foo_cc.o"s), __FILE__, __LINE__);
+			assert_equal(1u, count(tasks, "test/target1-bar_c.o"s), __FILE__, __LINE__);
 		}
 		{
 			auto tasks = getTasks(settings, {"target1", "target3"});
-			uASSERT_EQUAL(4u, tasks.size());
-			uASSERT_EQUAL(1u, count(tasks, "target1"s));
-			uASSERT_EQUAL(1u, count(tasks, "target3"s));
-			uASSERT_EQUAL(1u, count(tasks, "test/target1-foo_cc.o"s));
-			uASSERT_EQUAL(1u, count(tasks, "test/target1-bar_c.o"s));
+			assert_equal(4u, tasks.size(), __FILE__, __LINE__);
+			assert_equal(1u, count(tasks, "target1"s), __FILE__, __LINE__);
+			assert_equal(1u, count(tasks, "target3"s), __FILE__, __LINE__);
+			assert_equal(1u, count(tasks, "test/target1-foo_cc.o"s), __FILE__, __LINE__);
+			assert_equal(1u, count(tasks, "test/target1-bar_c.o"s), __FILE__, __LINE__);
 		}
 		{
 			auto tasks = getTasks(settings, {"no-such-target"});
-			uASSERT_EQUAL(0u, tasks.size());
+			assert_equal(0u, tasks.size(), __FILE__, __LINE__);
 		}
 	}
 
@@ -146,10 +146,10 @@ public:
 
 			for(auto& task : dirtyTasks)
 			{
-				uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
+				assert_equal(0, task->registerDepTasks(allTasks), __FILE__, __LINE__);
 			}
 
-			uASSERT_EQUAL(nullptr, getNextTask(allTasks, dirtyTasks));
+			assert_equal(nullptr, getNextTask(allTasks, dirtyTasks), __FILE__, __LINE__);
 		}
 
 		{ // Zero (One task, no dirty)
@@ -162,10 +162,10 @@ public:
 
 			for(auto& task : dirtyTasks)
 			{
-				uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
+				assert_equal(0, task->registerDepTasks(allTasks), __FILE__, __LINE__);
 			}
 
-			uASSERT_EQUAL(nullptr, getNextTask(allTasks, dirtyTasks));
+			assert_equal(nullptr, getNextTask(allTasks, dirtyTasks), __FILE__, __LINE__);
 		}
 
 		{ // One (One task, one dirty)
@@ -179,11 +179,11 @@ public:
 
 			for(auto& task : dirtyTasks)
 			{
-				uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
+				assert_equal(0, task->registerDepTasks(allTasks), __FILE__, __LINE__);
 			}
 
-			uASSERT_EQUAL(task1, getNextTask(allTasks, dirtyTasks));
-			uASSERT_EQUAL(0u, dirtyTasks.size());
+			assert_equal(task1, getNextTask(allTasks, dirtyTasks), __FILE__, __LINE__);
+			assert_equal(0u, dirtyTasks.size(), __FILE__, __LINE__);
 		}
 
 		{ // One (Two tasks, one dirty)
@@ -199,11 +199,11 @@ public:
 
 			for(auto& task : dirtyTasks)
 			{
-				uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
+				assert_equal(0, task->registerDepTasks(allTasks), __FILE__, __LINE__);
 			}
 
-			uASSERT_EQUAL(task2, getNextTask(allTasks, dirtyTasks));
-			uASSERT_EQUAL(0u, dirtyTasks.size());
+			assert_equal(task2, getNextTask(allTasks, dirtyTasks), __FILE__, __LINE__);
+			assert_equal(0u, dirtyTasks.size(), __FILE__, __LINE__);
 		}
 
 		{ // One (Two tasks, one dirty which depends on the other)
@@ -221,11 +221,11 @@ public:
 
 			for(auto& task : dirtyTasks)
 			{
-				uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
+				assert_equal(0, task->registerDepTasks(allTasks), __FILE__, __LINE__);
 			}
 
-			uASSERT_EQUAL(task2, getNextTask(allTasks, dirtyTasks));
-			uASSERT_EQUAL(0u, dirtyTasks.size());
+			assert_equal(task2, getNextTask(allTasks, dirtyTasks), __FILE__, __LINE__);
+			assert_equal(0u, dirtyTasks.size(), __FILE__, __LINE__);
 		}
 
 		{ // One (Two tasks, Both dirty, one depends on the other)
@@ -244,11 +244,11 @@ public:
 
 			for(auto& task : dirtyTasks)
 			{
-				uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
+				assert_equal(0, task->registerDepTasks(allTasks), __FILE__, __LINE__);
 			}
 
-			uASSERT_EQUAL(task1, getNextTask(allTasks, dirtyTasks));
-			uASSERT_EQUAL(1u, dirtyTasks.size());
+			assert_equal(task1, getNextTask(allTasks, dirtyTasks), __FILE__, __LINE__);
+			assert_equal(1u, dirtyTasks.size(), __FILE__, __LINE__);
 		}
 
 	}
diff --git a/test/testprog.cc b/test/testprog.cc
index dbfb665..58a31ac 100644
--- a/test/testprog.cc
+++ b/test/testprog.cc
@@ -2,7 +2,13 @@
 #include <fstream>
 #include <string>
 
-extern const char **environ; // see 'man environ'
+#if defined(_WIN32)
+#define WINDOWS_LEAN_AND_MEAN
+#include <windows.h>
+#undef max
+#else
+extern char **environ; // see 'man environ'
+#endif
 
 int main(int argc, const char* argv[])
 {
@@ -19,11 +25,41 @@ int main(int argc, const char* argv[])
 		{
 			return 0;
 		}
+
 		std::ofstream ostrm(argv[2], std::ios::binary);
+		if(ostrm.bad())
+		{
+			std::cout << "Error: Could not write to " << argv[2] << "\n";
+		}
+#if defined(_WIN32)
+		auto env_strings = GetEnvironmentStrings();
+		const char* ptr = env_strings;
+		std::string env;
+		while(true)
+		{
+			if(*ptr == '\0')
+			{
+				if(env.empty())
+				{
+					// no more
+					break;
+				}
+				ostrm << env << "\n";
+				env.clear();
+				++ptr;
+				continue;
+			}
+
+			env += *ptr;
+			++ptr;
+		}
+		FreeEnvironmentStrings(env_strings);
+#else
 		for(auto current = environ; *current; ++current)
 		{
 			ostrm << (*current) << "\n";
 		}
+#endif
 	}
 
 	if(cmd == "retval")
diff --git a/test/tmpfile.h b/test/tmpfile.h
index 5d114d0..a5d4043 100644
--- a/test/tmpfile.h
+++ b/test/tmpfile.h
@@ -4,7 +4,7 @@
 #pragma once
 
 #include <cstdlib>
-#include <unistd.h>
+#include <cstring>
 
 #ifdef _WIN32
 #define WIN32_LEAN_AND_MEAN
@@ -12,6 +12,9 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <io.h>
+#else
+#include <unistd.h>
 #endif
 
 struct tmp_file
@@ -19,23 +22,35 @@ struct tmp_file
 	tmp_file(const std::string& data = {})
 	{
 		int fd;
+		auto tmp_dir = std::filesystem::temp_directory_path();
+		auto tmp_file_template = tmp_dir / "ctor_tmp_file-XXXXXX";
+		filename = tmp_file_template.string();
+		auto buf = new char[filename.size() + 2];
+		memcpy(buf, filename.data(), filename.size() + 1);
 #ifdef _WIN32
-		char templ[] = "ctor_tmp_file-XXXXXX"; // buffer for filename
-		_mktemp_s(templ, sizeof(templ));
-		fd = open(templ, O_CREAT | O_RDWR);
+		_mktemp_s(buf, filename.size() + 2);
+		filename = buf;
+		fd = _open(filename.data(), O_CREAT | O_RDWR, _S_IWRITE);
+		auto sz = _write(fd, data.data(), data.size());
+		(void)sz;
+		_close(fd);
 #else
-		char templ[] = "/tmp/ctor_tmp_file-XXXXXX"; // buffer for filename
-		fd = mkstemp(templ);
-#endif
-		filename = templ;
+		fd = mkstemp(buf);
+		filename = buf;
 		auto sz = write(fd, data.data(), data.size());
 		(void)sz;
 		close(fd);
+#endif
+		delete[] buf;
 	}
 
 	~tmp_file()
 	{
+#ifdef _WIN32
+		_unlink(filename.data());
+#else
 		unlink(filename.data());
+#endif
 	}
 
 	const std::string& get() const
diff --git a/test/tools_test.cc b/test/tools_test.cc
index a428ea1..91da470 100644
--- a/test/tools_test.cc
+++ b/test/tools_test.cc
@@ -22,6 +22,9 @@ std::ostream& operator<<(std::ostream& stream, const ctor::toolchain& toolchain)
 	case ctor::toolchain::clang:
 		stream << "ctor::toolchain::clang";
 		break;
+	case ctor::toolchain::msvc:
+		stream << "ctor::toolchain::msvc";
+		break;
 	}
 	return stream;
 }
@@ -125,7 +128,7 @@ const ctor::configuration& ctor::get_configuration()
 	return cfg;
 }
 
-const std::string& ctor::configuration::get(const std::string& key, const std::string& defval) const
+std::string ctor::configuration::get(const std::string& key, [[maybe_unused]]const std::string& default_value) const
 {
 	if(key == ctor::cfg::host_cxx)
 	{
@@ -175,24 +178,24 @@ public:
 		//
 		// gcc
 		//
-		uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/gcc"));
-		uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/gcc-10"));
-		uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/x86_64-pc-linux-gnu-g++-9.3.0"));
+		assert_equal(ctor::toolchain::gcc, getToolChain("/usr/bin/gcc"), __FILE__, __LINE__);
+		assert_equal(ctor::toolchain::gcc, getToolChain("/usr/bin/gcc-10"), __FILE__, __LINE__);
+		assert_equal(ctor::toolchain::gcc, getToolChain("/usr/bin/x86_64-pc-linux-gnu-g++-9.3.0"), __FILE__, __LINE__);
 
-		uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/g++"));
-		uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/g++-10"));
-		uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/x86_64-pc-linux-gnu-g++-9.3.0"));
+		assert_equal(ctor::toolchain::gcc, getToolChain("/usr/bin/g++"), __FILE__, __LINE__);
+		assert_equal(ctor::toolchain::gcc, getToolChain("/usr/bin/g++-10"), __FILE__, __LINE__);
+		assert_equal(ctor::toolchain::gcc, getToolChain("/usr/bin/x86_64-pc-linux-gnu-g++-9.3.0"), __FILE__, __LINE__);
 
 		//
 		// clang
 		//
-		uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/bin/clang"));
-		uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/bin/clang-16"));
-		uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/lib/llvm/16/bin/i686-pc-linux-gnu-clang-16"));
+		assert_equal(ctor::toolchain::clang, getToolChain("/usr/bin/clang"), __FILE__, __LINE__);
+		assert_equal(ctor::toolchain::clang, getToolChain("/usr/bin/clang-16"), __FILE__, __LINE__);
+		assert_equal(ctor::toolchain::clang, getToolChain("/usr/lib/llvm/16/bin/i686-pc-linux-gnu-clang-16"), __FILE__, __LINE__);
 
-		uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/bin/clang++"));
-		uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/bin/clang++-16"));
-		uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/lib/llvm/16/bin/i686-pc-linux-gnu-clang++-16"));
+		assert_equal(ctor::toolchain::clang, getToolChain("/usr/bin/clang++"), __FILE__, __LINE__);
+		assert_equal(ctor::toolchain::clang, getToolChain("/usr/bin/clang++-16"), __FILE__, __LINE__);
+		assert_equal(ctor::toolchain::clang, getToolChain("/usr/lib/llvm/16/bin/i686-pc-linux-gnu-clang++-16"), __FILE__, __LINE__);
 	}
 
 
@@ -206,153 +209,153 @@ public:
 		//
 		exp = { "-o", "foo" };
 		act = c_option(ctor::toolchain::gcc, ctor::c_opt::output, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-g" };
 		act = c_option(ctor::toolchain::gcc, ctor::c_opt::debug);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Wall" };
 		act = c_option(ctor::toolchain::gcc, ctor::c_opt::warn_all);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Werror" };
 		act = c_option(ctor::toolchain::gcc, ctor::c_opt::warnings_as_errors);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-MMD" };
 		act = c_option(ctor::toolchain::gcc, ctor::c_opt::generate_dep_tree);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-c" };
 		act = c_option(ctor::toolchain::gcc, ctor::c_opt::no_link);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Ifoo" };
 		act = c_option(ctor::toolchain::gcc, ctor::c_opt::include_path, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-std=foo" };
 		act = c_option(ctor::toolchain::gcc, ctor::c_opt::c_std, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Ofoo" };
 		act = c_option(ctor::toolchain::gcc, ctor::c_opt::optimization, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-fPIC" };
 		act = c_option(ctor::toolchain::gcc, ctor::c_opt::position_independent_code);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-fPIE" };
 		act = c_option(ctor::toolchain::gcc, ctor::c_opt::position_independent_executable);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-foo" };
 		act = c_option(ctor::toolchain::gcc, ctor::c_opt::custom, "-foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		//
 		// clang
 		//
 		exp = { "-o", "foo" };
 		act = c_option(ctor::toolchain::clang, ctor::c_opt::output, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-g" };
 		act = c_option(ctor::toolchain::clang, ctor::c_opt::debug);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Wall" };
 		act = c_option(ctor::toolchain::clang, ctor::c_opt::warn_all);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Werror" };
 		act = c_option(ctor::toolchain::clang, ctor::c_opt::warnings_as_errors);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-MMD" };
 		act = c_option(ctor::toolchain::clang, ctor::c_opt::generate_dep_tree);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-c" };
 		act = c_option(ctor::toolchain::clang, ctor::c_opt::no_link);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Ifoo" };
 		act = c_option(ctor::toolchain::clang, ctor::c_opt::include_path, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-std=foo" };
 		act = c_option(ctor::toolchain::clang, ctor::c_opt::c_std, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Ofoo" };
 		act = c_option(ctor::toolchain::clang, ctor::c_opt::optimization, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-fPIC" };
 		act = c_option(ctor::toolchain::clang, ctor::c_opt::position_independent_code);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-fPIE" };
 		act = c_option(ctor::toolchain::clang, ctor::c_opt::position_independent_executable);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-foo" };
 		act = c_option(ctor::toolchain::clang, ctor::c_opt::custom, "-foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		//
 		// any
 		//
 		exp = { "{ctor::c_opt::output, \"foo\"}" };
 		act = c_option(ctor::toolchain::any, ctor::c_opt::output, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::c_opt::debug}" };
 		act = c_option(ctor::toolchain::any, ctor::c_opt::debug);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::c_opt::warn_all}" };
 		act = c_option(ctor::toolchain::any, ctor::c_opt::warn_all);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::c_opt::warnings_as_errors}" };
 		act = c_option(ctor::toolchain::any, ctor::c_opt::warnings_as_errors);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::c_opt::generate_dep_tree}" };
 		act = c_option(ctor::toolchain::any, ctor::c_opt::generate_dep_tree);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::c_opt::no_link}" };
 		act = c_option(ctor::toolchain::any, ctor::c_opt::no_link);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::c_opt::include_path, \"foo\"}" };
 		act = c_option(ctor::toolchain::any, ctor::c_opt::include_path, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::c_opt::c_std, \"foo\"}" };
 		act = c_option(ctor::toolchain::any, ctor::c_opt::c_std, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::c_opt::optimization, \"foo\"}" };
 		act = c_option(ctor::toolchain::any, ctor::c_opt::optimization, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::c_opt::position_independent_code}" };
 		act = c_option(ctor::toolchain::any, ctor::c_opt::position_independent_code);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::c_opt::position_independent_executable}" };
 		act = c_option(ctor::toolchain::any, ctor::c_opt::position_independent_executable);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::c_opt::custom, \"-foo\"}" };
 		act = c_option(ctor::toolchain::any, ctor::c_opt::custom, "-foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 	}
 
 	void getOption_toolchain_cxx_test()
@@ -365,153 +368,153 @@ public:
 		//
 		exp = { "-o", "foo" };
 		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::output, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-g" };
 		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::debug);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Wall" };
 		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::warn_all);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Werror" };
 		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::warnings_as_errors);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-MMD" };
 		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::generate_dep_tree);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-c" };
 		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::no_link);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Ifoo" };
 		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::include_path, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-std=foo" };
 		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::cpp_std, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Ofoo" };
 		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::optimization, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-fPIC" };
 		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::position_independent_code);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-fPIE" };
 		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::position_independent_executable);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-foo" };
 		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::custom, "-foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		//
 		// clang
 		//
 		exp = { "-o", "foo" };
 		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::output, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-g" };
 		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::debug);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Wall" };
 		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::warn_all);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Werror" };
 		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::warnings_as_errors);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-MMD" };
 		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::generate_dep_tree);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-c" };
 		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::no_link);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Ifoo" };
 		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::include_path, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-std=foo" };
 		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::cpp_std, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Ofoo" };
 		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::optimization, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-fPIC" };
 		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::position_independent_code);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-fPIE" };
 		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::position_independent_executable);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-foo" };
 		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::custom, "-foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		//
 		// any
 		//
 		exp = { "{ctor::cxx_opt::output, \"foo\"}" };
 		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::output, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::cxx_opt::debug}" };
 		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::debug);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::cxx_opt::warn_all}" };
 		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::warn_all);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::cxx_opt::warnings_as_errors}" };
 		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::warnings_as_errors);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::cxx_opt::generate_dep_tree}" };
 		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::generate_dep_tree);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::cxx_opt::no_link}" };
 		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::no_link);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::cxx_opt::include_path, \"foo\"}" };
 		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::include_path, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::cxx_opt::cpp_std, \"foo\"}" };
 		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::cpp_std, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::cxx_opt::optimization, \"foo\"}" };
 		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::optimization, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::cxx_opt::position_independent_code}" };
 		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::position_independent_code);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::cxx_opt::position_independent_executable}" };
 		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::position_independent_executable);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::cxx_opt::custom, \"-foo\"}" };
 		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::custom, "-foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 	}
 
 	void getOption_toolchain_ld_test()
@@ -524,153 +527,153 @@ public:
 		//
 		exp = { "-o", "foo" };
 		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::output, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-s" };
 		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::strip);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Wall" };
 		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::warn_all);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Werror" };
 		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::warnings_as_errors);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Lfoo" };
 		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::library_path, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-lfoo" };
 		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::link, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-std=foo" };
 		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::cpp_std, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-shared" };
 		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::build_shared);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-pthread" };
 		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::threads);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-fPIC" };
 		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::position_independent_code);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-fPIE" };
 		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::position_independent_executable);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-foo" };
 		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::custom, "-foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		//
 		// clang
 		//
 		exp = { "-o", "foo" };
 		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::output, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-s" };
 		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::strip);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Wall" };
 		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::warn_all);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Werror" };
 		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::warnings_as_errors);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-Lfoo" };
 		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::library_path, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-lfoo" };
 		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::link, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-std=foo" };
 		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::cpp_std, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-shared" };
 		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::build_shared);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-pthread" };
 		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::threads);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-fPIC" };
 		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::position_independent_code);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-fPIE" };
 		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::position_independent_executable);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-foo" };
 		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::custom, "-foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		//
 		// any
 		//
 		exp = { "{ctor::ld_opt::output, \"foo\"}" };
 		act = ld_option(ctor::toolchain::any, ctor::ld_opt::output, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::ld_opt::strip}" };
 		act = ld_option(ctor::toolchain::any, ctor::ld_opt::strip);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::ld_opt::warn_all}" };
 		act = ld_option(ctor::toolchain::any, ctor::ld_opt::warn_all);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::ld_opt::warnings_as_errors}" };
 		act = ld_option(ctor::toolchain::any, ctor::ld_opt::warnings_as_errors);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::ld_opt::library_path, \"foo\"}" };
 		act = ld_option(ctor::toolchain::any, ctor::ld_opt::library_path, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::ld_opt::link, \"foo\"}" };
 		act = ld_option(ctor::toolchain::any, ctor::ld_opt::link, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::ld_opt::cpp_std, \"foo\"}" };
 		act = ld_option(ctor::toolchain::any, ctor::ld_opt::cpp_std, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::ld_opt::build_shared}" };
 		act = ld_option(ctor::toolchain::any, ctor::ld_opt::build_shared);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::ld_opt::threads}" };
 		act = ld_option(ctor::toolchain::any, ctor::ld_opt::threads);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::ld_opt::position_independent_code}" };
 		act = ld_option(ctor::toolchain::any, ctor::ld_opt::position_independent_code);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::ld_opt::position_independent_executable}" };
 		act = ld_option(ctor::toolchain::any, ctor::ld_opt::position_independent_executable);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "{ctor::ld_opt::custom, \"-foo\"}" };
 		act = ld_option(ctor::toolchain::any, ctor::ld_opt::custom, "-foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 	}
 
 	void getOption_toolchain_ar_test()
@@ -683,53 +686,53 @@ public:
 		//
 		exp = { "-r" };
 		act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::replace);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-s" };
 		act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::add_index);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-c" };
 		act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::create);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "foo" };
 		act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::output, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-foo" };
 		act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::custom, "-foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		//
 		// clang
 		//
 		exp = { "-r" };
 		act = ar_option(ctor::toolchain::clang, ctor::ar_opt::replace);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-s" };
 		act = ar_option(ctor::toolchain::clang, ctor::ar_opt::add_index);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-c" };
 		act = ar_option(ctor::toolchain::clang, ctor::ar_opt::create);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "foo" };
 		act = ar_option(ctor::toolchain::clang, ctor::ar_opt::output, "foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { "-foo" };
 		act = ar_option(ctor::toolchain::clang, ctor::ar_opt::custom, "-foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		//
 		// any
 		//
 		exp = { "{ctor::ar_opt::custom, \"-foo\"}" };
 		act = ar_option(ctor::toolchain::any, ctor::ar_opt::custom, "-foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 }
 
 	void getOption_toolchain_asm_test()
@@ -742,21 +745,21 @@ public:
 		//
 		exp = { "-foo" };
 		act = asm_option(ctor::toolchain::gcc, ctor::asm_opt::custom, "-foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		//
 		// clang
 		//
 		exp = { "-foo" };
 		act = asm_option(ctor::toolchain::clang, ctor::asm_opt::custom, "-foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		//
 		// any
 		//
 		exp = { "{ctor::asm_opt::custom, \"-foo\"}" };
 		act = asm_option(ctor::toolchain::any, ctor::asm_opt::custom, "-foo");
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 	}
 
 
@@ -770,22 +773,22 @@ public:
 		//
 		exp = { ctor::c_opt::include_path, "foo" };
 		act = c_option("-Ifoo", ctor::toolchain::gcc);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { ctor::c_opt::custom, "foo" };
 		act = c_option("foo", ctor::toolchain::gcc);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		//
 		// clang
 		//
 		exp = { ctor::c_opt::include_path, "foo" };
 		act = c_option("-Ifoo", ctor::toolchain::clang);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { ctor::c_opt::custom, "foo" };
 		act = c_option("foo", ctor::toolchain::clang);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 	}
 
 	void getOption_str_cxx_test()
@@ -798,22 +801,22 @@ public:
 		//
 		exp = { ctor::cxx_opt::include_path, "foo" };
 		act = cxx_option("-Ifoo", ctor::toolchain::gcc);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { ctor::cxx_opt::custom, "foo" };
 		act = cxx_option("foo", ctor::toolchain::gcc);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		//
 		// clang
 		//
 		exp = { ctor::cxx_opt::include_path, "foo" };
 		act = cxx_option("-Ifoo", ctor::toolchain::clang);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { ctor::cxx_opt::custom, "foo" };
 		act = cxx_option("foo", ctor::toolchain::clang);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 	}
 
 	void getOption_str_ld_test()
@@ -826,22 +829,22 @@ public:
 		//
 		exp = { ctor::ld_opt::library_path, "foo" };
 		act = ld_option("-Lfoo", ctor::toolchain::gcc);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { ctor::ld_opt::custom, "foo" };
 		act = ld_option("foo", ctor::toolchain::gcc);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		//
 		// clang
 		//
 		exp = { ctor::ld_opt::library_path, "foo" };
 		act = ld_option("-Lfoo", ctor::toolchain::clang);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		exp = { ctor::ld_opt::custom, "foo" };
 		act = ld_option("foo", ctor::toolchain::clang);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 	}
 
 	void getOption_str_ar_test()
@@ -854,14 +857,14 @@ public:
 		//
 		exp = { ctor::ar_opt::custom, "foo" };
 		act = ar_option("foo", ctor::toolchain::gcc);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		//
 		// clang
 		//
 		exp = { ctor::ar_opt::custom, "foo" };
 		act = ar_option("foo", ctor::toolchain::clang);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 	}
 
 	void getOption_str_asm_test()
@@ -874,14 +877,14 @@ public:
 		//
 		exp = { ctor::asm_opt::custom, "foo" };
 		act = asm_option("foo", ctor::toolchain::gcc);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 
 		//
 		// clang
 		//
 		exp = { ctor::asm_opt::custom, "foo" };
 		act = asm_option("foo", ctor::toolchain::clang);
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 	}
 
 
@@ -893,7 +896,7 @@ public:
 		// Mismatching toolchain (required vs actual) results in no output
 		// otherwise to_strings is just a proxy for c_option
 		act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::c_opt::no_link});
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 	}
 
 	void to_strings_cxx_test()
@@ -904,7 +907,7 @@ public:
 		// Mismatching toolchain (required vs actual) results in no output
 		// otherwise to_strings is just a proxy for cxx_option
 		act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::cxx_opt::no_link});
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 	}
 
 	void to_strings_ld_test()
@@ -915,7 +918,7 @@ public:
 		// Mismatching toolchain (required vs actual) results in no output
 		// otherwise to_strings is just a proxy for ld_option
 		act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::ld_opt::strip});
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 	}
 
 	void to_strings_ar_test()
@@ -926,7 +929,7 @@ public:
 		// Mismatching toolchain (required vs actual) results in no output
 		// otherwise to_strings is just a proxy for ar_option
 		act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::ar_opt::custom, "foo"});
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 	}
 
 	void to_strings_asm_test()
@@ -937,7 +940,7 @@ public:
 		// Mismatching toolchain (required vs actual) results in no output
 		// otherwise to_strings is just a proxy for asm_option
 		act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::asm_opt::custom, "foo"});
-		uASSERT_EQUAL(exp, act);
+		assert_equal(exp, act, __FILE__, __LINE__);
 	}
 };
 
-- 
cgit v1.2.3