diff --git a/appveyor.yml b/appveyor.yml
index 941a3068..ff466c5a 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,5 +1,15 @@
environment:
matrix:
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
+ LLVM_VERSION: 7.0.0
+ LLVM_VERSION_SHORT: 70
+ VS_MAJOR_VERSION: 14 # Just use LLVM built with VS 2015
+ USE_CMAKE: 1
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+ LLVM_VERSION: 7.0.0
+ LLVM_VERSION_SHORT: 70
+ VS_MAJOR_VERSION: 14 # Just use LLVM built with VS 2015
+ USE_CMAKE: 1
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
LLVM_VERSION: 7.0.0
LLVM_VERSION_SHORT: 70
diff --git a/src/MSVCSetupAPI.h b/src/MSVCSetupAPI.h
new file mode 100644
index 00000000..0e2863dc
--- /dev/null
+++ b/src/MSVCSetupAPI.h
@@ -0,0 +1,514 @@
+//
+// Copyright (C) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+//
+//
+// The MIT License (MIT)
+//
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+#pragma once
+
+// Constants
+//
+#ifndef E_NOTFOUND
+#define E_NOTFOUND HRESULT_FROM_WIN32(ERROR_NOT_FOUND)
+#endif
+
+#ifndef E_FILENOTFOUND
+#define E_FILENOTFOUND HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)
+#endif
+
+// Enumerations
+//
+///
+/// The state of an instance.
+///
+enum InstanceState : unsigned {
+ ///
+ /// The instance state has not been determined.
+ ///
+ eNone = 0,
+
+ ///
+ /// The instance installation path exists.
+ ///
+ eLocal = 1,
+
+ ///
+ /// A product is registered to the instance.
+ ///
+ eRegistered = 2,
+
+ ///
+ /// No reboot is required for the instance.
+ ///
+ eNoRebootRequired = 4,
+
+ ///
+ /// The instance represents a complete install.
+ ///
+ eComplete = MAXUINT,
+};
+
+// Forward interface declarations
+//
+#ifndef __ISetupInstance_FWD_DEFINED__
+#define __ISetupInstance_FWD_DEFINED__
+typedef struct ISetupInstance ISetupInstance;
+#endif
+
+#ifndef __ISetupInstance2_FWD_DEFINED__
+#define __ISetupInstance2_FWD_DEFINED__
+typedef struct ISetupInstance2 ISetupInstance2;
+#endif
+
+#ifndef __IEnumSetupInstances_FWD_DEFINED__
+#define __IEnumSetupInstances_FWD_DEFINED__
+typedef struct IEnumSetupInstances IEnumSetupInstances;
+#endif
+
+#ifndef __ISetupConfiguration_FWD_DEFINED__
+#define __ISetupConfiguration_FWD_DEFINED__
+typedef struct ISetupConfiguration ISetupConfiguration;
+#endif
+
+#ifndef __ISetupConfiguration2_FWD_DEFINED__
+#define __ISetupConfiguration2_FWD_DEFINED__
+typedef struct ISetupConfiguration2 ISetupConfiguration2;
+#endif
+
+#ifndef __ISetupPackageReference_FWD_DEFINED__
+#define __ISetupPackageReference_FWD_DEFINED__
+typedef struct ISetupPackageReference ISetupPackageReference;
+#endif
+
+#ifndef __ISetupHelper_FWD_DEFINED__
+#define __ISetupHelper_FWD_DEFINED__
+typedef struct ISetupHelper ISetupHelper;
+#endif
+
+// Forward class declarations
+//
+#ifndef __SetupConfiguration_FWD_DEFINED__
+#define __SetupConfiguration_FWD_DEFINED__
+
+#ifdef __cplusplus
+typedef class SetupConfiguration SetupConfiguration;
+#endif
+
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Interface definitions
+//
+EXTERN_C const IID IID_ISetupInstance;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+///
+/// Information about an instance of a product.
+///
+struct DECLSPEC_UUID("B41463C3-8866-43B5-BC33-2B0676F7F42E")
+ DECLSPEC_NOVTABLE ISetupInstance : public IUnknown {
+ ///
+ /// Gets the instance identifier (should match the name of the parent instance
+ /// directory).
+ ///
+ /// The instance identifier.
+ /// Standard HRESULT indicating success or failure, including
+ /// E_FILENOTFOUND if the instance state does not exist.
+ STDMETHOD(GetInstanceId)(_Out_ BSTR *pbstrInstanceId) = 0;
+
+ ///
+ /// Gets the local date and time when the installation was originally
+ /// installed.
+ ///
+ /// The local date and time when the installation
+ /// was originally installed.
+ /// Standard HRESULT indicating success or failure, including
+ /// E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the
+ /// property is not defined.
+ STDMETHOD(GetInstallDate)(_Out_ LPFILETIME pInstallDate) = 0;
+
+ ///
+ /// Gets the unique name of the installation, often indicating the branch and
+ /// other information used for telemetry.
+ ///
+ /// The unique name of the installation,
+ /// often indicating the branch and other information used for
+ /// telemetry.
+ /// Standard HRESULT indicating success or failure, including
+ /// E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the
+ /// property is not defined.
+ STDMETHOD(GetInstallationName)(_Out_ BSTR *pbstrInstallationName) = 0;
+
+ ///
+ /// Gets the path to the installation root of the product.
+ ///
+ /// The path to the installation root of
+ /// the product.
+ /// Standard HRESULT indicating success or failure, including
+ /// E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the
+ /// property is not defined.
+ STDMETHOD(GetInstallationPath)(_Out_ BSTR *pbstrInstallationPath) = 0;
+
+ ///
+ /// Gets the version of the product installed in this instance.
+ ///
+ /// The version of the product
+ /// installed in this instance.
+ /// Standard HRESULT indicating success or failure, including
+ /// E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the
+ /// property is not defined.
+ STDMETHOD(GetInstallationVersion)(_Out_ BSTR *pbstrInstallationVersion) = 0;
+
+ ///
+ /// Gets the display name (title) of the product installed in this instance.
+ ///
+ /// The LCID for the display name.
+ /// The display name (title) of the product
+ /// installed in this instance.
+ /// Standard HRESULT indicating success or failure, including
+ /// E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the
+ /// property is not defined.
+ STDMETHOD(GetDisplayName)(_In_ LCID lcid, _Out_ BSTR *pbstrDisplayName) = 0;
+
+ ///
+ /// Gets the description of the product installed in this instance.
+ ///
+ /// The LCID for the description.
+ /// The description of the product installed in
+ /// this instance.
+ /// Standard HRESULT indicating success or failure, including
+ /// E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the
+ /// property is not defined.
+ STDMETHOD(GetDescription)(_In_ LCID lcid, _Out_ BSTR *pbstrDescription) = 0;
+
+ ///
+ /// Resolves the optional relative path to the root path of the instance.
+ ///
+ /// A relative path within the instance to
+ /// resolve, or NULL to get the root path.
+ /// The full path to the optional relative
+ /// path within the instance. If the relative path is NULL, the root path will
+ /// always terminate in a backslash.
+ /// Standard HRESULT indicating success or failure, including
+ /// E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the
+ /// property is not defined.
+ STDMETHOD(ResolvePath)
+ (_In_opt_z_ LPCOLESTR pwszRelativePath, _Out_ BSTR *pbstrAbsolutePath) = 0;
+};
+#endif
+
+EXTERN_C const IID IID_ISetupInstance2;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+///
+/// Information about an instance of a product.
+///
+struct DECLSPEC_UUID("89143C9A-05AF-49B0-B717-72E218A2185C")
+ DECLSPEC_NOVTABLE ISetupInstance2 : public ISetupInstance {
+ ///
+ /// Gets the state of the instance.
+ ///
+ /// The state of the instance.
+ /// Standard HRESULT indicating success or failure, including
+ /// E_FILENOTFOUND if the instance state does not exist.
+ STDMETHOD(GetState)(_Out_ InstanceState *pState) = 0;
+
+ ///
+ /// Gets an array of package references registered to the instance.
+ ///
+ /// Pointer to an array of .
+ /// Standard HRESULT indicating success or failure, including
+ /// E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the
+ /// packages property is not defined.
+ STDMETHOD(GetPackages)(_Out_ LPSAFEARRAY *ppsaPackages) = 0;
+
+ ///
+ /// Gets a pointer to the that represents
+ /// the registered product.
+ ///
+ /// Pointer to an instance of . This may be NULL if does not return .
+ /// Standard HRESULT indicating success or failure, including
+ /// E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the
+ /// packages property is not defined.
+ STDMETHOD(GetProduct)
+ (_Outptr_result_maybenull_ ISetupPackageReference **ppPackage) = 0;
+
+ ///
+ /// Gets the relative path to the product application, if available.
+ ///
+ /// The relative path to the product
+ /// application, if available.
+ /// Standard HRESULT indicating success or failure, including
+ /// E_FILENOTFOUND if the instance state does not exist.
+ STDMETHOD(GetProductPath)
+ (_Outptr_result_maybenull_ BSTR *pbstrProductPath) = 0;
+};
+#endif
+
+EXTERN_C const IID IID_IEnumSetupInstances;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+///
+/// A enumerator of installed objects.
+///
+struct DECLSPEC_UUID("6380BCFF-41D3-4B2E-8B2E-BF8A6810C848")
+ DECLSPEC_NOVTABLE IEnumSetupInstances : public IUnknown {
+ ///
+ /// Retrieves the next set of product instances in the enumeration sequence.
+ ///
+ /// The number of product instances to retrieve.
+ /// A pointer to an array of .
+ /// A pointer to the number of product instances
+ /// retrieved. If celt is 1 this parameter may be NULL.
+ /// S_OK if the number of elements were fetched, S_FALSE if nothing
+ /// was fetched (at end of enumeration), E_INVALIDARG if celt is greater than
+ /// 1 and pceltFetched is NULL, or E_OUTOFMEMORY if an could not be allocated.
+ STDMETHOD(Next)
+ (_In_ ULONG celt, _Out_writes_to_(celt, *pceltFetched) ISetupInstance **rgelt,
+ _Out_opt_ _Deref_out_range_(0, celt) ULONG *pceltFetched) = 0;
+
+ ///
+ /// Skips the next set of product instances in the enumeration sequence.
+ ///
+ /// The number of product instances to skip.
+ /// S_OK if the number of elements could be skipped; otherwise,
+ /// S_FALSE;
+ STDMETHOD(Skip)(_In_ ULONG celt) = 0;
+
+ ///
+ /// Resets the enumeration sequence to the beginning.
+ ///
+ /// Always returns S_OK;
+ STDMETHOD(Reset)(void) = 0;
+
+ ///
+ /// Creates a new enumeration object in the same state as the current
+ /// enumeration object: the new object points to the same place in the
+ /// enumeration sequence.
+ ///
+ /// A pointer to a pointer to a new interface. If the method fails, this
+ /// parameter is undefined.
+ /// S_OK if a clone was returned; otherwise, E_OUTOFMEMORY.
+ STDMETHOD(Clone)(_Deref_out_opt_ IEnumSetupInstances **ppenum) = 0;
+};
+#endif
+
+EXTERN_C const IID IID_ISetupConfiguration;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+///
+/// Gets information about product instances set up on the machine.
+///
+struct DECLSPEC_UUID("42843719-DB4C-46C2-8E7C-64F1816EFD5B")
+ DECLSPEC_NOVTABLE ISetupConfiguration : public IUnknown {
+ ///
+ /// Enumerates all completed product instances installed.
+ ///
+ /// An enumeration of completed, installed
+ /// product instances.
+ /// Standard HRESULT indicating success or failure.
+ STDMETHOD(EnumInstances)(_Out_ IEnumSetupInstances **ppEnumInstances) = 0;
+
+ ///
+ /// Gets the instance for the current process path.
+ ///
+ /// The instance for the current process
+ /// path.
+ /// The instance for the current process path, or E_NOTFOUND if not
+ /// found.
+ STDMETHOD(GetInstanceForCurrentProcess)
+ (_Out_ ISetupInstance **ppInstance) = 0;
+
+ ///
+ /// Gets the instance for the given path.
+ ///
+ /// The instance for the given path.
+ /// The instance for the given path, or E_NOTFOUND if not
+ /// found.
+ STDMETHOD(GetInstanceForPath)
+ (_In_z_ LPCWSTR wzPath, _Out_ ISetupInstance **ppInstance) = 0;
+};
+#endif
+
+EXTERN_C const IID IID_ISetupConfiguration2;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+///
+/// Gets information about product instances.
+///
+struct DECLSPEC_UUID("26AAB78C-4A60-49D6-AF3B-3C35BC93365D")
+ DECLSPEC_NOVTABLE ISetupConfiguration2 : public ISetupConfiguration {
+ ///
+ /// Enumerates all product instances.
+ ///
+ /// An enumeration of all product
+ /// instances.
+ /// Standard HRESULT indicating success or failure.
+ STDMETHOD(EnumAllInstances)(_Out_ IEnumSetupInstances **ppEnumInstances) = 0;
+};
+#endif
+
+EXTERN_C const IID IID_ISetupPackageReference;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+///
+/// A reference to a package.
+///
+struct DECLSPEC_UUID("da8d8a16-b2b6-4487-a2f1-594ccccd6bf5")
+ DECLSPEC_NOVTABLE ISetupPackageReference : public IUnknown {
+ ///
+ /// Gets the general package identifier.
+ ///
+ /// The general package identifier.
+ /// Standard HRESULT indicating success or failure.
+ STDMETHOD(GetId)(_Out_ BSTR *pbstrId) = 0;
+
+ ///
+ /// Gets the version of the package.
+ ///
+ /// The version of the package.
+ /// Standard HRESULT indicating success or failure.
+ STDMETHOD(GetVersion)(_Out_ BSTR *pbstrVersion) = 0;
+
+ ///
+ /// Gets the target process architecture of the package.
+ ///
+ /// The target process architecture of the
+ /// package.
+ /// Standard HRESULT indicating success or failure.
+ STDMETHOD(GetChip)(_Out_ BSTR *pbstrChip) = 0;
+
+ ///
+ /// Gets the language and optional region identifier.
+ ///
+ /// The language and optional region
+ /// identifier.
+ /// Standard HRESULT indicating success or failure.
+ STDMETHOD(GetLanguage)(_Out_ BSTR *pbstrLanguage) = 0;
+
+ ///
+ /// Gets the build branch of the package.
+ ///
+ /// The build branch of the package.
+ /// Standard HRESULT indicating success or failure.
+ STDMETHOD(GetBranch)(_Out_ BSTR *pbstrBranch) = 0;
+
+ ///
+ /// Gets the type of the package.
+ ///
+ /// The type of the package.
+ /// Standard HRESULT indicating success or failure.
+ STDMETHOD(GetType)(_Out_ BSTR *pbstrType) = 0;
+
+ ///
+ /// Gets the unique identifier consisting of all defined tokens.
+ ///
+ /// The unique identifier consisting of all
+ /// defined tokens.
+ /// Standard HRESULT indicating success or failure, including
+ /// E_UNEXPECTED if no Id was defined (required).
+ STDMETHOD(GetUniqueId)(_Out_ BSTR *pbstrUniqueId) = 0;
+};
+#endif
+
+EXTERN_C const IID IID_ISetupHelper;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+///
+/// Helper functions.
+///
+///
+/// You can query for this interface from the
+/// class.
+///
+struct DECLSPEC_UUID("42b21b78-6192-463e-87bf-d577838f1d5c")
+ DECLSPEC_NOVTABLE ISetupHelper : public IUnknown {
+ ///
+ /// Parses a dotted quad version string into a 64-bit unsigned integer.
+ ///
+ /// The dotted quad version string to parse, e.g.
+ /// 1.2.3.4.
+ /// A 64-bit unsigned integer representing the
+ /// version. You can compare this to other versions.
+ /// Standard HRESULT indicating success or failure.
+ STDMETHOD(ParseVersion)
+ (_In_ LPCOLESTR pwszVersion, _Out_ PULONGLONG pullVersion) = 0;
+
+ ///
+ /// Parses a dotted quad version string into a 64-bit unsigned integer.
+ ///
+ /// The string containing 1 or 2 dotted quad
+ /// version strings to parse, e.g. [1.0,) that means 1.0.0.0 or newer.
+ /// A 64-bit unsigned integer representing the
+ /// minimum version, which may be 0. You can compare this to other
+ /// versions.
+ /// A 64-bit unsigned integer representing the
+ /// maximum version, which may be MAXULONGLONG. You can compare this to other
+ /// versions.
+ /// Standard HRESULT indicating success or failure.
+ STDMETHOD(ParseVersionRange)
+ (_In_ LPCOLESTR pwszVersionRange, _Out_ PULONGLONG pullMinVersion,
+ _Out_ PULONGLONG pullMaxVersion) = 0;
+};
+#endif
+
+// Class declarations
+//
+EXTERN_C const CLSID CLSID_SetupConfiguration;
+
+#ifdef __cplusplus
+///
+/// This class implements , , and .
+///
+class DECLSPEC_UUID("177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D") SetupConfiguration;
+#endif
+
+// Function declarations
+//
+///
+/// Gets an that provides information about
+/// product instances installed on the machine.
+///
+/// The that
+/// provides information about product instances installed on the
+/// machine.
+/// Reserved for future use.
+/// Standard HRESULT indicating success or failure.
+STDMETHODIMP GetSetupConfiguration(_Out_ ISetupConfiguration **ppConfiguration,
+ _Reserved_ LPVOID pReserved);
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/src/tcompiler.cpp b/src/tcompiler.cpp
index e1fd79c4..d7c6762c 100644
--- a/src/tcompiler.cpp
+++ b/src/tcompiler.cpp
@@ -2918,7 +2918,7 @@ static int terra_disassemble(lua_State * L) {
return 0;
}
-static bool FindLinker(terra_State * T, LLVM_PATH_TYPE * linker) {
+static bool FindLinker(terra_State * T, LLVM_PATH_TYPE * linker, const char* target) {
#ifndef _WIN32
#if LLVM_VERSION >= 36
*linker = *sys::findProgramByName("gcc");
@@ -2933,7 +2933,8 @@ static bool FindLinker(terra_State * T, LLVM_PATH_TYPE * linker) {
#else
lua_getfield(T->L, LUA_GLOBALSINDEX, "terra");
lua_getfield(T->L, -1, "getvclinker");
- lua_call(T->L, 0, 3);
+ lua_pushstring(T->L, target);
+ lua_call(T->L, 1, 3);
*linker = LLVM_PATH_TYPE(lua_tostring(T->L,-3));
const char * vclib = lua_tostring(T->L,-2);
const char * vcpath = lua_tostring(T->L,-1);
@@ -2962,7 +2963,9 @@ static bool SaveAndLink(TerraCompilationUnit * CU, Module * M, std::vectorT,&linker)) {
+ std::string arch(CU->TT->Triple);
+ arch.erase(arch.find_first_of('-'));
+ if(FindLinker(CU->T,&linker, arch.c_str())) {
unlink(tmpnamebuf);
terra_pusherror(CU->T,"llvm: failed to find linker");
return true;
diff --git a/src/terra.cpp b/src/terra.cpp
index ee0c6320..34975e88 100644
--- a/src/terra.cpp
+++ b/src/terra.cpp
@@ -21,8 +21,18 @@
#include
#else
#define NOMINMAX
+#include
#include
#include
+#include
+#include "MSVCSetupAPI.h"
+
+_COM_SMARTPTR_TYPEDEF(ISetupConfiguration, __uuidof(ISetupConfiguration));
+_COM_SMARTPTR_TYPEDEF(ISetupConfiguration2, __uuidof(ISetupConfiguration2));
+_COM_SMARTPTR_TYPEDEF(ISetupHelper, __uuidof(ISetupHelper));
+_COM_SMARTPTR_TYPEDEF(IEnumSetupInstances, __uuidof(IEnumSetupInstances));
+_COM_SMARTPTR_TYPEDEF(ISetupInstance, __uuidof(ISetupInstance));
+_COM_SMARTPTR_TYPEDEF(ISetupInstance2, __uuidof(ISetupInstance2));
#endif
static char * vstringf(const char * fmt, va_list ap) {
@@ -207,6 +217,198 @@ static bool pushterrahome(lua_State * L) {
return true;
}
+// On windows, gets the value of a given registry key in HKEY_LOCAL_MACHINE, or returns nil otherwise.
+static int query_reg_value(lua_State* L)
+{
+ if(lua_gettop(L) != 2)
+ lua_pushnil(L);
+ else
+ {
+ auto key = luaL_checkstring(L, 1);
+ auto value = luaL_checkstring(L, 2);
+
+ HKEY pkey;
+ if(!key || !value || RegOpenKeyExA(HKEY_LOCAL_MACHINE, key, 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &pkey) != S_OK)
+ lua_pushnil(L);
+ else
+ {
+ DWORD type = 0;
+ DWORD sz = 0;
+ LSTATUS r = RegQueryValueExA(pkey, value, 0, &type, 0, &sz);
+ if(r != S_OK)
+ lua_pushnil(L);
+ else
+ {
+ BYTE* data = (BYTE*)malloc(sz + 1);
+ r = RegQueryValueExA(pkey, value, 0, &type, data, &sz);
+ data[sz] = 0;
+ if(!data || r != S_OK)
+ lua_pushnil(L);
+ else
+ {
+ switch(type)
+ {
+ case REG_BINARY:
+ lua_pushlstring(L, (const char*)data, sz);
+ break;
+ case REG_DWORD:
+ lua_pushinteger(L, *(int32_t*)data);
+ break;
+ case REG_QWORD:
+ lua_pushinteger(L, *(int64_t*)data); // This doesn't really work but we attempt it anyway
+ break;
+ case REG_EXPAND_SZ: {
+ sz = ExpandEnvironmentStringsA((const char*)data, 0, 0);
+ char* buf = (char*)malloc(sz);
+ ExpandEnvironmentStringsA((const char*)data, buf, sz);
+ lua_pushstring(L, (const char*)buf);
+ free(buf);
+ } break;
+ case REG_MULTI_SZ:
+ case REG_SZ:
+ lua_pushstring(L, (const char*)data);
+ break;
+ }
+ }
+ if(data)
+ free(data);
+ }
+
+ RegCloseKey(pkey);
+ }
+ }
+ return 1;
+}
+
+struct InitializeCOMRAII
+{
+ InitializeCOMRAII() { ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); }
+ ~InitializeCOMRAII() { ::CoUninitialize(); }
+};
+
+// Based on clang's MSVC.cpp file
+static bool findVCToolChainViaSetupConfig(lua_State* L)
+{
+ InitializeCOMRAII com;
+ HRESULT HR;
+
+ ISetupConfigurationPtr Query;
+ HR = Query.CreateInstance(__uuidof(SetupConfiguration));
+ if(FAILED(HR))
+ return false;
+
+ IEnumSetupInstancesPtr EnumInstances;
+ HR = ISetupConfiguration2Ptr(Query)->EnumAllInstances(&EnumInstances);
+ if(FAILED(HR))
+ return false;
+
+ ISetupInstancePtr Instance;
+ HR = EnumInstances->Next(1, &Instance, nullptr);
+ if(HR != S_OK)
+ return false;
+
+ ISetupInstancePtr NewestInstance;
+ uint64_t NewestVersionNum = 0;
+ do
+ {
+ bstr_t VersionString;
+ uint64_t VersionNum;
+ HR = Instance->GetInstallationVersion(VersionString.GetAddress());
+ if(FAILED(HR))
+ continue;
+ HR = ISetupHelperPtr(Query)->ParseVersion(VersionString, &VersionNum);
+ if(FAILED(HR))
+ continue;
+ if(!NewestVersionNum || (VersionNum > NewestVersionNum))
+ {
+ NewestInstance = Instance;
+ NewestVersionNum = VersionNum;
+ }
+ } while((HR = EnumInstances->Next(1, &Instance, nullptr)) == S_OK);
+
+ if(!NewestInstance)
+ return false;
+
+ bstr_t VCPathWide;
+ HR = NewestInstance->ResolvePath(L"VC", VCPathWide.GetAddress());
+ if(FAILED(HR))
+ return false;
+
+ std::wstring ToolsVersionFilePath(VCPathWide);
+ ToolsVersionFilePath += L"\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt";
+
+ FILE* f = nullptr;
+ auto open_result = _wfopen_s(&f, ToolsVersionFilePath.c_str(), L"rt");
+ if(open_result != 0 || !f)
+ return false;
+ fseek(f, 0, SEEK_END);
+ size_t tools_file_size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ std::string version;
+ version.resize(tools_file_size);
+ auto retval = fgets((char*)version.data(), version.size(), f);
+ fclose(f);
+
+ if(!retval)
+ return false;
+
+ while(!version.back()) // strip trailing null terminators so whitespace removal works
+ version.pop_back();
+ version.erase(version.find_last_not_of(" \n\r\t\v") + 1);
+
+ std::string ToolchainPath;
+ ToolchainPath.resize(WideCharToMultiByte(CP_UTF8, 0, VCPathWide, VCPathWide.length(), 0, 0, NULL, NULL));
+ ToolchainPath.resize(WideCharToMultiByte(CP_UTF8, 0, VCPathWide, VCPathWide.length(), (char*)ToolchainPath.data(), ToolchainPath.capacity(), NULL, NULL));
+ ToolchainPath += "\\Tools\\MSVC\\";
+ ToolchainPath += version;
+ lua_pushstring(L, ToolchainPath.c_str());
+ return true;
+}
+
+static int find_visual_studio_toolchain(lua_State* L)
+{
+ if(!findVCToolChainViaSetupConfig(L))
+ lua_pushnil(L);
+ return 1;
+}
+
+static int list_subdirectories(lua_State* L)
+{
+ if(lua_gettop(L) != 1)
+ lua_pushnil(L);
+ else
+ {
+ std::string path = luaL_checkstring(L, 1);
+ if(path.back() != '\\')
+ path += '\\';
+ path += '*';
+
+ WIN32_FIND_DATAA find_data;
+ auto handle = FindFirstFileA(path.c_str(), &find_data);
+ if(handle == INVALID_HANDLE_VALUE)
+ lua_pushnil(L);
+ else
+ {
+ lua_newtable(L);
+ int i = 0;
+ BOOL success = TRUE;
+ while(success)
+ {
+ if((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (find_data.cFileName[0] != '.'))
+ {
+ lua_pushnumber(L, ++i); // Make sure we start at 1
+ lua_pushstring(L, find_data.cFileName);
+ lua_settable(L, -3);
+ }
+
+ success = FindNextFileA(handle, &find_data);
+ }
+
+ FindClose(handle);
+ }
+ }
+ return 1;
+}
#endif
static void setterrahome(lua_State * L) {
@@ -245,6 +447,19 @@ int terra_initwithoptions(lua_State * L, terra_Options * options) {
if(err) {
return err;
}
+
+#ifdef _WIN32
+ // These functions are required by terralib.lua
+ lua_getfield(T->L, LUA_GLOBALSINDEX, "terra");
+ lua_pushcfunction(T->L, query_reg_value);
+ lua_setfield(T->L, -2, "queryregvalue");
+ lua_pushcfunction(T->L, find_visual_studio_toolchain);
+ lua_setfield(T->L, -2, "findvisualstudiotoolchain");
+ lua_pushcfunction(T->L, list_subdirectories);
+ lua_setfield(T->L, -2, "listsubdirectories");
+ lua_pop(T->L, 1); //'terra' global
+#endif
+
err = terra_loadandrunbytecodes(T->L,(const unsigned char*)luaJIT_BC_strict,luaJIT_BC_strict_SIZE, "strict.lua")
|| terra_loadandrunbytecodes(T->L,(const unsigned char*)luaJIT_BC_terralist,luaJIT_BC_terralist_SIZE, "terralist.lua")
|| terra_loadandrunbytecodes(T->L,(const unsigned char*)luaJIT_BC_asdl,luaJIT_BC_asdl_SIZE, "asdl.lua")
diff --git a/src/terralib.lua b/src/terralib.lua
index dbbf8130..32e39beb 100644
--- a/src/terralib.lua
+++ b/src/terralib.lua
@@ -4043,12 +4043,16 @@ end
-- configure path variables
-terra.cudahome = os.getenv("CUDA_HOME") or (ffi.os == "Windows" and os.getenv("CUDA_PATH")) or "/usr/local/cuda"
+if ffi.os == "Windows" then
+ terra.cudahome = os.getenv("CUDA_PATH")
+else
+ terra.cudahome = os.getenv("CUDA_HOME") or "/usr/local/cuda"
+end
terra.cudalibpaths = ({ OSX = {driver = "/usr/local/cuda/lib/libcuda.dylib", runtime = "$CUDA_HOME/lib/libcudart.dylib", nvvm = "$CUDA_HOME/nvvm/lib/libnvvm.dylib"};
Linux = {driver = "libcuda.so", runtime = "$CUDA_HOME/lib64/libcudart.so", nvvm = "$CUDA_HOME/nvvm/lib64/libnvvm.so"};
Windows = {driver = "nvcuda.dll", runtime = "$CUDA_HOME\\bin\\cudart64_*.dll", nvvm = "$CUDA_HOME\\nvvm\\bin\\nvvm64_*.dll"}; })[ffi.os]
-- OS's that are not supported by CUDA will have an undefined value here
-if terra.cudalibpaths then
+if terra.cudalibpaths and terra.cudahome then
for name,path in pairs(terra.cudalibpaths) do
path = path:gsub("%$CUDA_HOME",terra.cudahome)
if path:match("%*") and ffi.os == "Windows" then
@@ -4074,39 +4078,127 @@ if ffi.os == "Windows" then
end
terra.includepath = os.getenv("INCLUDE")
- function terra.getvclinker()
+ function terra.getvclinker(target)
local vclib = os.getenv("LIB")
local vcpath = terra.vcpath or os.getenv("Path")
vclib,vcpath = "LIB="..vclib,"Path="..vcpath
return terra.vclinker,vclib,vcpath
end
else
- -- this is the reason we can't have nice things
- local function registrystring(key,value,default)
- local F = io.popen( ([[reg query "%s" /v "%s"]]):format(key,value) )
- local result = F and F:read("*all"):match("REG_SZ%W*([^\n]*)\n")
- return result or default
+ local function compareversion(form, a, b)
+ if (a == nil) or (b == nil) then return true end
+
+ local alist = {}
+ for e in string.gmatch(a, form) do table.insert(alist, tonumber(e)) end
+ local blist = {}
+ for e in string.gmatch(b, form) do table.insert(blist, tonumber(e)) end
+
+ for i=1,#alist do
+ if alist[i] ~= blist[i] then
+ return alist[i] > blist[i]
+ end
+ end
+ return false
end
- terra.vshome = registrystring([[HKLM\Software\WOW6432Node\Microsoft\VisualStudio\12.0]],"ShellFolder",[[C:\Program Files (x86)\Microsoft Visual Studio 12.0\]])
- local windowsdk = registrystring([[HKLM\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v8.1]],"InstallationFolder",[[C:\Program Files (x86)\Windows Kits\8.1\]])
-
+
+ -- First find the latest Windows SDK installed using the registry
+ local installedroots = [[SOFTWARE\Microsoft\Windows Kits\Installed Roots]]
+ local windowsdk = terra.queryregvalue(installedroots, "KitsRoot10")
+ if windowsdk == nil then
+ windowsdk = terra.queryregvalue(installedroots, "KitsRoot81")
+ if windowsdk == nil then
+ error "Can't find windows SDK version 8.1 or 10! Try running Terra in a Native Tools Developer Console instead."
+ end
+
+ terra.systemincludes:insertall{ "shared", "um", "winrt" }
+ terra.systemincludes = terra.systemincludes:map(function(e) return windowsdk..[[include\]]..e end)
+
+ local version = nil
+ for i, v in ipairs(terra.listsubdirectories(windowsdk .. "lib")) do
+ if compareversion("%d+", v, version) then
+ version = v
+ end
+ end
+ if version == nil then
+ error "Can't find valid version subdirectory in the SDK! Is the Windows 8.1 SDK installation corrupt?"
+ end
+
+ terra.sdklib = windowsdk .. "lib\\" .. version
+ else
+ -- Find highest version. For sanity reasons, we assume the same version folders are in both lib/ and include/
+ local version = nil
+ for i, v in ipairs(terra.listsubdirectories(windowsdk .. "include")) do
+ if compareversion("%d+", v, version) then
+ version = v
+ end
+ end
+ if version == nil then
+ error "Can't find valid version subdirectory in the SDK! Is the SDK installation corrupt?"
+ end
+
+ terra.systemincludes:insertall{ "ucrt", "shared", "um", "winrt" }
+ terra.systemincludes = terra.systemincludes:map(function(e) return windowsdk..version..[[\include\]]..e end)
+ terra.sdklib = windowsdk .. "lib\\" .. version
+ end
+
+ terra.vshome = terra.findvisualstudiotoolchain()
+ if terra.vshome == nil then
+ terra.vshome = terra.queryregvalue([[SOFTWARE\WOW6432Node\Microsoft\VisualStudio\14.0]], "ShellFolder") or
+ terra.queryregvalue([[SOFTWARE\WOW6432Node\Microsoft\VisualStudio\12.0]], "ShellFolder") or
+ terra.queryregvalue([[SOFTWARE\WOW6432Node\Microsoft\VisualStudio\11.0]], "ShellFolder") or
+ terra.queryregvalue([[SOFTWARE\WOW6432Node\Microsoft\VisualStudio\10.0]], "ShellFolder")
+
+ if terra.vshome == nil then
+ error "Can't find Visual Studio either via COM or the registry! Try running Terra in a Native Tools Developer Console instead."
+ end
+ terra.vshome = terra.vshome .. "VC\\"
+ terra.vsarch64 = "amd64" -- Before 2017, Visual Studio had it's own special architecture convention, because who needs standards
+ terra.vslinkpath = function(host, target)
+ if string.lower(host) == string.lower(target) then
+ return ([[BIN\%s\]]):format(host)
+ else
+ return ([[BIN\%s_%s\]]):format(host, target)
+ end
+ end
+ else
+ if terra.vshome[#terra.vshome] ~= '\\' then
+ terra.vshome = terra.vshome .. "\\"
+ end
+ terra.vsarch64 = "x64"
+ terra.vslinkpath = function(host, target) return ([[bin\Host%s\%s\]]):format(host, target) end
+ end
+
terra.systemincludes:insertall {
- ("%sVC/INCLUDE"):format(terra.vshome),
- ("%sVC/ATLMFC/INCLUDE"):format(terra.vshome),
- ("%sinclude/shared"):format(windowsdk),
- ("%sinclude/um"):format(windowsdk),
- ("%sinclude/winrt"):format(windowsdk),
- ("%s/include"):format(terra.cudahome)
+ ([[%sINCLUDE]]):format(terra.vshome),
+ ([[%sATLMFC\INCLUDE]]):format(terra.vshome)
}
-
- function terra.getvclinker() --get the linker, and guess the needed environment variables for Windows if they are not set ...
- local linker = terra.vshome..[[VC\BIN\x86_amd64\link.exe]]
- local vclib = terra.vclib or string.gsub([[%VC\LIB\amd64;%VC\ATLMFC\LIB\amd64;C:\Program Files (x86)\Windows Kits\8.1\lib\winv6.3\um\x64;]],"%%",terra.vshome)
- local vcpath = terra.vcpath or (os.getenv("Path") or "")..";"..terra.vshome..[[VC\BIN;]]
+
+ function terra.getvclinker(target) --get the linker, and guess the needed environment variables for Windows if they are not set ...
+ target = target or "x86_64"
+ local winarch = target
+ if target == "x86_64" then
+ target = terra.vsarch64
+ winarch = "x64" -- Unbelievably, Visual Studio didn't follow the Windows SDK convention until 2017+
+ elseif target == "aarch64" or target == "aarch64_be" then
+ target = "arm64"
+ winarch = "arm64"
+ end
+
+ local host = ffi.arch
+ if host == "x64" then
+ host = terra.vsarch64
+ end
+
+ local linker = terra.vshome..terra.vslinkpath(host, target).."link.exe"
+ local vclib = ([[%s\um\%s;%s\ucrt\%s;]]):format(terra.sdklib, winarch, terra.sdklib, winarch) .. ([[%sLIB\%s;%sATLMFC\LIB\%s;]]):format(terra.vshome, target, terra.vshome, target)
+ local vcpath = terra.vcpath or (os.getenv("Path") or "")..";"..terra.vshome..[[BIN;]]..terra.vshome..terra.vslinkpath(host, host)..";" -- deals with VS2017 cross-compile nonsense: https://github.com/rust-lang/rust/issues/31063
vclib,vcpath = "LIB="..vclib,"Path="..vcpath
return linker,vclib,vcpath
end
end
+ if terra.cudahome then
+ terra.systemincludes:insertall{terra.cudahome.."\\include"}
+ end
end