Browse Source

Initial checkin of windows WIP support

windows-support
Macoy Madson 6 months ago
parent
commit
76a01808ee
5 changed files with 283 additions and 6 deletions
  1. +4
    -2
      BuildAndRunTests.sh
  2. +56
    -0
      CrossCompile_Windows.cake
  3. +142
    -0
      src/FileUtilities.cpp
  4. +3
    -3
      src/ModuleManager.cpp
  5. +78
    -1
      src/RunProcess.cpp

+ 4
- 2
BuildAndRunTests.sh View File

@ -20,5 +20,7 @@
# runtime/HotReloadingCodeModifier.cake runtime/TextAdventure.cake || exit $?
# TestMain is the loader. It doesn't care at all about fancy hot reloading macros, it just loads libs
./bin/cakelisp --verbose-processes --verbose-include-scanning \
runtime/HotLoader.cake || exit $?
# ./bin/cakelisp --verbose-processes --verbose-include-scanning \
# runtime/HotLoader.cake || exit $?
./bin/cakelisp CrossCompile_Windows.cake

+ 56
- 0
CrossCompile_Windows.cake View File

@ -0,0 +1,56 @@
(skip-build)
(set-cakelisp-option executable-output "bin/cakelisp.exe")
(add-c-search-directory module "src")
(add-cpp-build-dependency
"Tokenizer.cpp"
"Evaluator.cpp"
"Utilities.cpp"
"FileUtilities.cpp"
"Converters.cpp"
"Writer.cpp"
"Generators.cpp"
"GeneratorHelpers.cpp"
"RunProcess.cpp"
"OutputPreambles.cpp"
"DynamicLoader.cpp"
"ModuleManager.cpp"
"Logging.cpp"
"Main.cpp")
(add-build-options "-DWINDOWS")
;; (defun-comptime cakelisp-link-hook (manager (& ModuleManager)
;; linkCommand (& ProcessCommand)
;; linkTimeInputs (* ProcessCommandInput) numLinkTimeInputs int
;; &return bool)
;; (Log "Cakelisp: Adding link arguments\n")
;; ;; Dynamic loading
;; (on-call (field linkCommand arguments) push_back
;; (array ProcessCommandArgumentType_String
;; "-ldl"))
;; ;; Expose Cakelisp symbols for compile-time function symbol resolution
;; (on-call (field linkCommand arguments) push_back
;; (array ProcessCommandArgumentType_String
;; "-Wl,--export-dynamic"))
;; (return true))
;; (add-compile-time-hook pre-link cakelisp-link-hook)
(set-cakelisp-option build-time-compiler "/usr/bin/x86_64-w64-mingw32-g++")
(set-cakelisp-option build-time-compile-arguments
"-g" "-c" 'source-input "-o" 'object-output
'include-search-dirs 'additional-options)
(set-cakelisp-option compile-time-compiler "/usr/bin/x86_64-w64-mingw32-g++")
(set-cakelisp-option compile-time-compile-arguments
"-g" "-c" 'source-input "-o" 'object-output
'cakelisp-headers-include "-fPIC")
(set-cakelisp-option build-time-linker "/usr/bin/x86_64-w64-mingw32-g++")
(set-cakelisp-option build-time-link-arguments
"-o" 'executable-output 'object-input)
;; Use separate build configuration in case other things build files from src/
(add-build-config-label "CrossCompile_Windows")

+ 142
- 0
src/FileUtilities.cpp View File

@ -13,6 +13,12 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#elif WINDOWS
#define WIN32_LEAN_AND_MEAN
#include <fileapi.h>
#include <windows.h>
#else
#error Need to implement file utilities for this platform
#endif
@ -29,6 +35,26 @@ unsigned long fileGetLastModificationTime(const char* filename)
}
return (unsigned long)fileStat.st_mtime;
#elif WINDOWS
// Doesn't actually create new due to OPEN_EXISTING
HANDLE hFile =
CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, /*lpSecurityAttributes=*/nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, /*hTemplateFile=*/nullptr);
if (hFile == INVALID_HANDLE_VALUE)
return 0;
FILETIME ftCreate, ftAccess, ftWrite;
if (!GetFileTime(hFile, &ftCreate, &ftAccess, &ftWrite))
return 0;
ULARGE_INTEGER lv_Large;
lv_Large.LowPart = ftWrite.dwLowDateTime;
lv_Large.HighPart = ftWrite.dwHighDateTime;
__int64 ftWriteTime = lv_Large.QuadPart;
if (ftWriteTime < 0)
return 0;
return (unsigned long)ftWriteTime;
#else
return 0;
#endif
@ -55,6 +81,26 @@ bool fileIsMoreRecentlyModified(const char* filename, const char* reference)
// Logf("%s vs %s: %lu %lu\n", filename, reference, fileStat.st_mtime, referenceStat.st_mtime);
return fileStat.st_mtime > referenceStat.st_mtime;
#elif WINDOWS
// Doesn't actually create new due to OPEN_EXISTING
HANDLE hFile =
CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, /*lpSecurityAttributes=*/nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, /*hTemplateFile=*/nullptr);
HANDLE hReference =
CreateFile(reference, GENERIC_READ, FILE_SHARE_READ, /*lpSecurityAttributes=*/nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, /*hTemplateFile=*/nullptr);
if (hFile == INVALID_HANDLE_VALUE || hReference == INVALID_HANDLE_VALUE)
return 0;
FILETIME ftCreate, ftAccess, ftWrite;
if (!GetFileTime(hFile, &ftCreate, &ftAccess, &ftWrite))
return true;
FILETIME ftCreateRef, ftAccessRef, ftWriteRef;
if (!GetFileTime(hFile, &ftCreateRef, &ftAccessRef, &ftWriteRef))
return true;
return CompareFileTime(&ftWrite, &ftWriteRef) >= 1;
return true;
#else
// Err on the side of filename always being newer than the reference. The #error in the includes
// block should prevent this from ever being compiled anyways
@ -64,7 +110,13 @@ bool fileIsMoreRecentlyModified(const char* filename, const char* reference)
bool fileExists(const char* filename)
{
#ifdef UNIX
return access(filename, F_OK) != -1;
#elif WINDOWS
return GetFileAttributes(filename) != INVALID_FILE_ATTRIBUTES;
#else
return false;
#endif
}
void makeDirectory(const char* path)
@ -76,6 +128,12 @@ void makeDirectory(const char* path)
if (logging.fileSystem || errno != EEXIST)
perror("makeDirectory: ");
}
#elif WINDOWS
if (!CreateDirectory(path, /*lpSecurityAttributes=*/nullptr))
{
if (GetLastError() != ERROR_ALREADY_EXISTS)
Logf("makeDirectory failed to make %s", path);
}
#else
#error Need to be able to make directories on this platform
#endif
@ -88,6 +146,16 @@ void getDirectoryFromPath(const char* path, char* bufferOut, int bufferSize)
const char* dirName = dirname(pathCopy);
SafeSnprinf(bufferOut, bufferSize, "%s", dirName);
free(pathCopy);
#elif WINDOWS
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
// char fname[_MAX_FNAME];
// char ext[_MAX_EXT];
_splitpath_s(path, drive, sizeof(drive), dir, sizeof(dir),
/*fname=*/nullptr, 0,
/*ext=*/nullptr, 0);
_makepath_s(bufferOut, bufferSize, drive, dir, /*fname=*/nullptr,
/*ext=*/nullptr);
#else
#error Need to be able to strip file from path to get directory on this platform
#endif
@ -100,6 +168,14 @@ void getFilenameFromPath(const char* path, char* bufferOut, int bufferSize)
const char* fileName = basename(pathCopy);
SafeSnprinf(bufferOut, bufferSize, "%s", fileName);
free(pathCopy);
#elif WINDOWS
// char drive[_MAX_DRIVE];
// char dir[_MAX_DIR];
char fname[_MAX_FNAME];
char ext[_MAX_EXT];
_splitpath_s(path, /*drive=*/nullptr, 0, /*dir=*/nullptr, 0, fname, sizeof(fname), ext,
sizeof(ext));
_makepath_s(bufferOut, bufferSize, /*drive=*/nullptr, /*dir=*/nullptr, fname, ext);
#else
#error Need to be able to strip path to get filename on this platform
#endif
@ -129,8 +205,29 @@ const char* makeAbsolutePath_Allocated(const char* fromDirectory, const char* fi
// The path will be relative to the binary's working directory
return realpath(filePath, nullptr);
}
#elif WINDOWS
char* absolutePath = (char*)calloc(MAX_PATH_LENGTH, sizeof(char));
bool isValid = false;
if (fromDirectory)
{
char relativePath[MAX_PATH_LENGTH] = {0};
PrintfBuffer(relativePath, "%s/%s", fromDirectory, filePath);
isValid = _fullpath(absolutePath, relativePath, MAX_PATH_LENGTH);
}
else
{
isValid = _fullpath(absolutePath, filePath, MAX_PATH_LENGTH);
}
if (!isValid)
{
free(absolutePath);
return nullptr;
}
return absolutePath;
#else
#error Need to be able to normalize path on this platform
return nullptr;
#endif
}
@ -180,6 +277,50 @@ void makeAbsoluteOrRelativeToWorkingDir(const char* filePath, char* bufferOut, i
free((void*)workingDirAbsolute);
free((void*)filePathAbsolute);
#elif WINDOWS
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char fname[_MAX_FNAME];
char ext[_MAX_EXT];
_splitpath_s(filePath, drive, sizeof(drive), dir, sizeof(dir), fname, sizeof(fname), ext,
sizeof(ext));
// If it's already absolute, keep it that way
if (drive[0])
{
_makepath_s(bufferOut, bufferSize, drive, dir, fname, ext);
}
else
{
char workingDirAbsolute[MAX_PATH_LENGTH] = {0};
GetCurrentDirectory(sizeof(workingDirAbsolute), workingDirAbsolute);
const char* filePathAbsolute = makeAbsolutePath_Allocated(nullptr, filePath);
if (!filePathAbsolute)
{
SafeSnprinf(bufferOut, bufferSize, "%s", filePath);
return;
}
// Within the same directory?
size_t workingDirPathLength = strlen(workingDirAbsolute);
if (strncmp(workingDirAbsolute, filePathAbsolute, workingDirPathLength) == 0)
{
// The resolved path is within working dir
int trimTrailingSlash = filePathAbsolute[workingDirPathLength] == '/' ||
filePathAbsolute[workingDirPathLength] == '\\' ?
1 :
0;
const char* startRelativePath =
&filePathAbsolute[workingDirPathLength + trimTrailingSlash];
SafeSnprinf(bufferOut, bufferSize, "%s", startRelativePath);
}
else
{
// Resolved path is above working dir. Could still make this relative with ../ up to
// differing directory, if I find it's desired
SafeSnprinf(bufferOut, bufferSize, "%s", filePathAbsolute);
}
free((void*)filePathAbsolute);
}
#else
#error Need to be able to normalize path on this platform
#endif
@ -298,6 +439,7 @@ bool moveFile(const char* srcFilename, const char* destFilename)
void addExecutablePermission(const char* filename)
{
// Not necessary on Windows
#ifdef UNIX
// From man 2 chmod:
// S_IRUSR (00400) read by owner


+ 3
- 3
src/ModuleManager.cpp View File

@ -110,9 +110,9 @@ void moduleManagerInitialize(ModuleManager& manager)
{ProcessCommandArgumentType_ObjectInput, EmptyString}};
// TODO: Add defaults for Windows
#ifdef WINDOWS
#error Set sensible defaults for compile time build command
#endif
// #ifdef WINDOWS
// #error Set sensible defaults for compile time build command
// #endif
}
manager.environment.useCachedFiles = true;


+ 78
- 1
src/RunProcess.cpp View File

@ -9,6 +9,11 @@
#include <sys/types.h> // pid
#include <sys/wait.h> // waitpid
#include <unistd.h> // exec, fork
#elif WINDOWS
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#error Platform support is needed for running subprocesses
#endif
@ -25,7 +30,11 @@ typedef int ProcessId;
struct Subprocess
{
int* statusOut;
#ifdef UNIX
ProcessId processId;
#elif WINDOWS
PROCESS_INFORMATION* processInfo;
#endif
int pipeReadFileDescriptor;
std::string command;
};
@ -56,7 +65,6 @@ void subprocessReceiveStdOut(const char* processOutputBuffer)
int runProcess(const RunProcessArguments& arguments, int* statusOut)
{
#ifdef UNIX
if (logging.processes)
{
Log("RunProcess command: ");
@ -67,6 +75,7 @@ int runProcess(const RunProcessArguments& arguments, int* statusOut)
Log("\n");
}
#ifdef UNIX
int pipeFileDescriptors[2] = {0};
const int PipeRead = 0;
const int PipeWrite = 1;
@ -157,6 +166,61 @@ int runProcess(const RunProcessArguments& arguments, int* statusOut)
}
return 0;
#elif WINDOWS
STARTUPINFO startupInfo;
PROCESS_INFORMATION* processInfo = new PROCESS_INFORMATION;
ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
ZeroMemory(processInfo, sizeof(PROCESS_INFORMATION));
char* commandLineString = nullptr;
{
size_t commandLineLength = 0;
for (const char** arg = arguments.arguments; *arg != nullptr; ++arg)
{
commandLineLength += strlen(*arg);
// Room for space
commandLineLength += 1;
}
commandLineString = (char*)calloc(commandLineLength, sizeof(char));
commandLineString[commandLineLength] = '\0';
char* writeHead = commandLineString;
for (const char** arg = arguments.arguments; *arg != nullptr; ++arg)
{
if (!writeStringToBuffer(*arg, &writeHead, commandLineString, commandLineLength))
{
free(commandLineString);
return 1;
}
}
}
// Start the child process.
if (!CreateProcess(arguments.fileToExecute, // No module name (use command line)
commandLineString, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&startupInfo, // Pointer to STARTUPINFO structure
processInfo)) // Pointer to PROCESS_INFORMATION structure
{
Logf("CreateProcess failed (%d).\n", GetLastError());
return 1;
}
std::string command = "";
for (const char** arg = arguments.arguments; *arg != nullptr; ++arg)
{
command.append(*arg);
command.append(" ");
}
s_subprocesses.push_back({statusOut, processInfo, 0, command});
#endif
return 1;
}
@ -188,6 +252,19 @@ void waitForAllProcessesClosed(SubprocessOnOutputFunc onOutput)
// It's pretty useful to see the command which resulted in failure
if (*process.statusOut != 0)
Logf("%s\n", process.command.c_str());
#elif WINDOWS
// Wait until child process exits.
WaitForSingleObject(process.processInfo->hProcess, INFINITE);
DWORD exit_code;
if (!GetExitCodeProcess(process.processInfo->hProcess, &exit_code) || exit_code != 0)
Logf("%s\n", process.command.c_str());
*process.statusOut = exit_code;
// Close process and thread handles.
CloseHandle(process.processInfo->hProcess);
CloseHandle(process.processInfo->hThread);
#endif
}


Loading…
Cancel
Save