Browse Source

WIP Initial linking attempt

It doesn't work because argument strings cannot have spaces. I'm
committing now in case I want to refer to the concatenating version
later.

I made write*ToBuffer functions which don't require tokens
GeneralDependencyResolver
Macoy Madson 1 month ago
parent
commit
bfae509ef2
9 changed files with 241 additions and 50 deletions
  1. +1
    -1
      BuildAndRunTests.sh
  2. +3
    -0
      runtime/HotReloading.cake
  3. +22
    -13
      src/Converters.cpp
  4. +157
    -7
      src/ModuleManager.cpp
  5. +1
    -0
      src/RunProcessEnums.hpp
  6. +17
    -26
      src/Tokenizer.cpp
  7. +4
    -3
      src/Tokenizer.hpp
  8. +33
    -0
      src/Utilities.cpp
  9. +3
    -0
      src/Utilities.hpp

+ 1
- 1
BuildAndRunTests.sh View File

@@ -8,4 +8,4 @@
# jam -j4 && ./bin/cakelisp --enable-hot-reloading runtime/TestMain.cake runtime/TextAdventure.cake \
# && cd runtime && jam -j4

jam -j4 && ./bin/cakelisp --enable-hot-reloading runtime/TestMain.cake runtime/TextAdventure.cake
jam -j4 && ./bin/cakelisp --verbose-build-process runtime/TestMain.cake runtime/TextAdventure.cake

+ 3
- 0
runtime/HotReloading.cake View File

@@ -4,7 +4,10 @@
;; Include cakelisp source for DynamicLoader.hpp
(set-module-option build-time-compile-arguments
"-g" "-c" 'source-input "-o" 'object-output "-fPIC" "-Isrc/")
;; TODO: This only makes sense on a per-target basis. Instead, modules should be able to append
;; arguments to the link command only
(set-module-option build-time-linker "/usr/bin/clang++")
;; This needs to link -ldl and such (depending on platform...)
(set-module-option build-time-link-arguments
"-shared" "-o" 'library-output 'object-input)



+ 22
- 13
src/Converters.cpp View File

@@ -24,7 +24,8 @@ void lispNameStyleToCNameStyle(NameStyleMode mode, const char* name, char* buffe
switch (mode)
{
case NameStyleMode_Underscores:
if (!writeCharToBuffer('_', &bufferWrite, bufferOut, bufferOutSize, token))
if (!writeCharToBufferErrorToken('_', &bufferWrite, bufferOut, bufferOutSize,
token))
return;
break;
case NameStyleMode_CamelCase:
@@ -49,7 +50,7 @@ void lispNameStyleToCNameStyle(NameStyleMode mode, const char* name, char* buffe
// Special case for C++ namespaces: valid only if there are two in a row
if ((c == name && *c + 1 == ':') || (*(c + 1) == ':' || *(c - 1) == ':'))
{
if (!writeCharToBuffer(*c, &bufferWrite, bufferOut, bufferOutSize, token))
if (!writeCharToBufferErrorToken(*c, &bufferWrite, bufferOut, bufferOutSize, token))
return;
}
else
@@ -62,7 +63,8 @@ void lispNameStyleToCNameStyle(NameStyleMode mode, const char* name, char* buffe
"interpreting a special symbol?\n");

requiredSymbolConversion = true;
if (!writeStringToBuffer("Colon", &bufferWrite, bufferOut, bufferOutSize, token))
if (!writeStringToBufferErrorToken("Colon", &bufferWrite, bufferOut, bufferOutSize,
token))
return;
}
}
@@ -70,12 +72,13 @@ void lispNameStyleToCNameStyle(NameStyleMode mode, const char* name, char* buffe
{
if (upcaseNextCharacter)
{
if (!writeCharToBuffer(toupper(*c), &bufferWrite, bufferOut, bufferOutSize, token))
if (!writeCharToBufferErrorToken(toupper(*c), &bufferWrite, bufferOut,
bufferOutSize, token))
return;
}
else
{
if (!writeCharToBuffer(*c, &bufferWrite, bufferOut, bufferOutSize, token))
if (!writeCharToBufferErrorToken(*c, &bufferWrite, bufferOut, bufferOutSize, token))
return;
}

@@ -89,28 +92,34 @@ void lispNameStyleToCNameStyle(NameStyleMode mode, const char* name, char* buffe
switch (*c)
{
case '+':
if (!writeStringToBuffer("Add", &bufferWrite, bufferOut, bufferOutSize, token))
if (!writeStringToBufferErrorToken("Add", &bufferWrite, bufferOut,
bufferOutSize, token))
return;
break;
case '-':
if (!writeStringToBuffer("Sub", &bufferWrite, bufferOut, bufferOutSize, token))
if (!writeStringToBufferErrorToken("Sub", &bufferWrite, bufferOut,
bufferOutSize, token))
return;
break;
case '*':
if (!writeStringToBuffer("Mul", &bufferWrite, bufferOut, bufferOutSize, token))
if (!writeStringToBufferErrorToken("Mul", &bufferWrite, bufferOut,
bufferOutSize, token))
return;
break;
case '/':
if (!writeStringToBuffer("Div", &bufferWrite, bufferOut, bufferOutSize, token))
if (!writeStringToBufferErrorToken("Div", &bufferWrite, bufferOut,
bufferOutSize, token))
return;
break;
case '%':
if (!writeStringToBuffer("Mod", &bufferWrite, bufferOut, bufferOutSize, token))
if (!writeStringToBufferErrorToken("Mod", &bufferWrite, bufferOut,
bufferOutSize, token))
return;
break;
case '.':
// TODO: Decide how to handle object pathing
if (!writeStringToBuffer(".", &bufferWrite, bufferOut, bufferOutSize, token))
if (!writeStringToBufferErrorToken(".", &bufferWrite, bufferOut, bufferOutSize,
token))
return;
break;
default:
@@ -120,8 +129,8 @@ void lispNameStyleToCNameStyle(NameStyleMode mode, const char* name, char* buffe
"'%c' which has no conversion equivalent. It will be replaced with "
"'BadChar'",
name, *c);
if (!writeStringToBuffer("BadChar", &bufferWrite, bufferOut, bufferOutSize,
token))
if (!writeStringToBufferErrorToken("BadChar", &bufferWrite, bufferOut,
bufferOutSize, token))
return;
break;
}


+ 157
- 7
src/ModuleManager.cpp View File

@@ -67,6 +67,10 @@ void moduleManagerInitialize(ModuleManager& manager)
{ProcessCommandArgumentType_String, "-fPIC"}};

manager.environment.buildTimeLinkCommand.fileToExecute = "/usr/bin/clang++";
manager.environment.buildTimeLinkCommand.arguments = {
{ProcessCommandArgumentType_String, "-o"},
{ProcessCommandArgumentType_ExecutableOutput, EmptyString},
{ProcessCommandArgumentType_ObjectInput, EmptyString}};
}

// TODO: Add defaults for Windows
@@ -273,21 +277,50 @@ static void OnCompileProcessOutput(const char* output)
// TODO C/C++ error to Cakelisp token mapper
}

// enum BuildTargetType
// {
// BuildTargetType_Executable,
// BuildTargetType_Library
// };

// struct BuildTarget
// {
// BuildTargetType type;
// std::vector<std::string> filesToLink;
// };

struct BuiltObject
{
int buildStatus;
std::string filename;
};

void builtObjectsFree(std::vector<BuiltObject*>& objects)
{
for (BuiltObject* object : objects)
delete object;

objects.clear();
}

bool moduleManagerBuild(ModuleManager& manager)
{
int currentNumProcessesSpawned = 0;

int numModules = manager.modules.size();
std::vector<int> buildStatusCodes(numModules);
// Pointer because the objects can't move, status codes are pointed to
std::vector<BuiltObject*> builtObjects;

for (int moduleIndex = 0; moduleIndex < numModules; ++moduleIndex)
{
Module* module = manager.modules[moduleIndex];

printf("Build module %s\n", module->sourceOutputName.c_str());
if (log.buildProcess)
printf("Build module %s\n", module->sourceOutputName.c_str());
for (ModuleDependency& dependency : module->dependencies)
{
printf("\tRequires %s\n", dependency.name.c_str());
if (log.buildProcess)
printf("\tRequires %s\n", dependency.name.c_str());

// Cakelisp files are built at the module manager level, so we need not concern
// ourselves with them
@@ -303,10 +336,22 @@ bool moduleManagerBuild(ModuleManager& manager)
char buildFilename[MAX_NAME_LENGTH] = {0};
getFilenameFromPath(module->sourceOutputName.c_str(), buildFilename, sizeof(buildFilename));
if (!buildFilename[0])
{
builtObjectsFree(builtObjects);
return false;
}

// TODO: Trim .cake.cpp
char buildObjectName[MAX_PATH_LENGTH] = {0};
PrintfBuffer(buildObjectName, "%s/%s.o", cakelispWorkingDir, buildFilename);

// At this point, we do want to build the object. We might skip building it if it is cached.
// In that case, the status code should still be 0, as if we built and succeeded building it
BuiltObject* newBuiltObject = new BuiltObject;
newBuiltObject->buildStatus = 0;
newBuiltObject->filename = buildObjectName;
builtObjects.push_back(newBuiltObject);

if (!fileIsMoreRecentlyModified(module->sourceOutputName.c_str(), buildObjectName))
{
if (log.buildProcess)
@@ -328,16 +373,19 @@ bool moduleManagerBuild(ModuleManager& manager)
if (!buildArguments)
{
printf("error: failed to construct build arguments\n");
builtObjectsFree(builtObjects);
return false;
}
RunProcessArguments compileArguments = {};
compileArguments.fileToExecute = buildCommand.fileToExecute.c_str();
compileArguments.arguments = buildArguments;
// PrintProcessArguments(buildArguments);
if (runProcess(compileArguments, &buildStatusCodes[moduleIndex]) != 0)

if (runProcess(compileArguments, &newBuiltObject->buildStatus) != 0)
{
printf("error: failed to invoke compiler\n");
free(buildArguments);
builtObjectsFree(builtObjects);
return false;
}

@@ -357,16 +405,118 @@ bool moduleManagerBuild(ModuleManager& manager)
waitForAllProcessesClosed(OnCompileProcessOutput);
currentNumProcessesSpawned = 0;

for (int moduleIndex = 0; moduleIndex < numModules; ++moduleIndex)
// TODO: Make configurable
const char* outputExecutableName = "a.out";

int objectNameBufferSize = 0;
int numObjectsToLink = 0;
bool succeededBuild = true;
bool needsLink = false;
for (BuiltObject* object : builtObjects)
{
// Module* module = manager.modules[moduleIndex];
int buildResult = buildStatusCodes[moduleIndex];
int buildResult = object->buildStatus;
if (buildResult != 0)
{
printf("error: failed to build file\n");
printf("error: failed to make target %s\n", object->filename.c_str());
succeededBuild = false;
continue;
}

if (log.buildProcess)
printf("Linking %s\n", object->filename.c_str());

objectNameBufferSize += object->filename.size();
++numObjectsToLink;

// If all our objects are older than our executable, don't even link!
needsLink |= fileIsMoreRecentlyModified(object->filename.c_str(), outputExecutableName);
}

if (!succeededBuild)
{
builtObjectsFree(builtObjects);
return false;
}

if (!needsLink)
{
if (log.buildProcess)
printf("Skipping linking (no built objects are newer than cached executable)\n");

builtObjectsFree(builtObjects);
return true;
}

if (numObjectsToLink)
{
// Four objects means we need three spaces and a null terminator
int totalNameBufferSize = objectNameBufferSize + numObjectsToLink;
char* objectNameBuffer = new char[totalNameBufferSize];
char* writeHead = objectNameBuffer;
for (int i = 0; i < numObjectsToLink; ++i)
{
BuiltObject* object = builtObjects[i];

if (!writeStringToBuffer(object->filename.c_str(), &writeHead, objectNameBuffer,
totalNameBufferSize))
{
delete[] objectNameBuffer;
builtObjectsFree(builtObjects);
return false;
}

if (i < numObjectsToLink - 1)
{
if (!writeCharToBuffer(' ', &writeHead, objectNameBuffer, totalNameBufferSize))
{
delete[] objectNameBuffer;
builtObjectsFree(builtObjects);
return false;
}
}
}

if (log.buildProcess)
printf("Link '%s'\n", objectNameBuffer);

ProcessCommandInput linkTimeInputs[] = {
{ProcessCommandArgumentType_ExecutableOutput, outputExecutableName},
{ProcessCommandArgumentType_ObjectInput, objectNameBuffer}};
const char** linkArgumentList = MakeProcessArgumentsFromCommand(
manager.environment.buildTimeLinkCommand, linkTimeInputs, ArraySize(linkTimeInputs));
if (!linkArgumentList)
{
delete[] objectNameBuffer;
builtObjectsFree(builtObjects);
return false;
}
RunProcessArguments linkArguments = {};
linkArguments.fileToExecute =
manager.environment.buildTimeLinkCommand.fileToExecute.c_str();
linkArguments.arguments = linkArgumentList;
int linkStatus = 0;
if (runProcess(linkArguments, &linkStatus) != 0)
{
delete[] objectNameBuffer;
builtObjectsFree(builtObjects);
return false;
}

free(linkArgumentList);
delete[] objectNameBuffer;

waitForAllProcessesClosed(OnCompileProcessOutput);

succeededBuild = linkStatus == 0;
}

if (!succeededBuild)
{
builtObjectsFree(builtObjects);
return false;
}

builtObjectsFree(builtObjects);
return true;
}

+ 1
- 0
src/RunProcessEnums.hpp View File

@@ -10,4 +10,5 @@ enum ProcessCommandArgumentType

ProcessCommandArgumentType_ObjectInput,
ProcessCommandArgumentType_DynamicLibraryOutput,
ProcessCommandArgumentType_ExecutableOutput
};

+ 17
- 26
src/Tokenizer.cpp View File

@@ -243,22 +243,24 @@ bool appendTokenToString(const Token& token, char** at, char* bufferStart, int b
switch (token.type)
{
case TokenType_OpenParen:
return writeCharToBuffer('(', at, bufferStart, bufferSize, token);
return writeCharToBufferErrorToken('(', at, bufferStart, bufferSize, token);
case TokenType_CloseParen:
return writeCharToBuffer(')', at, bufferStart, bufferSize, token);
return writeCharToBufferErrorToken(')', at, bufferStart, bufferSize, token);
case TokenType_Symbol:
if (!writeStringToBuffer(token.contents.c_str(), at, bufferStart, bufferSize, token))
if (!writeStringToBufferErrorToken(token.contents.c_str(), at, bufferStart, bufferSize,
token))
return false;
// Must add space after symbol to separate it from everything else
return writeCharToBuffer(' ', at, bufferStart, bufferSize, token);
return writeCharToBufferErrorToken(' ', at, bufferStart, bufferSize, token);
case TokenType_String:
if (!writeStringToBuffer("\\\"", at, bufferStart, bufferSize, token))
if (!writeStringToBufferErrorToken("\\\"", at, bufferStart, bufferSize, token))
return false;
// TODO Need to delimit quotes properly (try e.g. "test\"" and see it break)
if (!writeStringToBuffer(token.contents.c_str(), at, bufferStart, bufferSize, token))
if (!writeStringToBufferErrorToken(token.contents.c_str(), at, bufferStart, bufferSize,
token))
return false;
// Must add space after string to separate it from everything else
return writeStringToBuffer("\\\" ", at, bufferStart, bufferSize, token);
return writeStringToBufferErrorToken("\\\" ", at, bufferStart, bufferSize, token);
break;
default:
ErrorAtToken(token, "cannot append token of this type to string");
@@ -278,36 +280,25 @@ void printTokens(const std::vector<Token>& tokens)
printf("\n");
}

bool writeCharToBuffer(char c, char** at, char* bufferStart, int bufferSize, const Token& token)
bool writeCharToBufferErrorToken(char c, char** at, char* bufferStart, int bufferSize,
const Token& token)
{
**at = c;
++(*at);
char* endOfBuffer = bufferStart + bufferSize - 1;
if (*at >= endOfBuffer)
if (!writeCharToBuffer(c, at, bufferStart, bufferSize))
{
ErrorAtTokenf(token, "buffer of size %d was too small. String will be cut off", bufferSize);
*endOfBuffer = '\0';
return false;
}

return true;
}

bool writeStringToBuffer(const char* str, char** at, char* bufferStart, int bufferSize,
const Token& token)
bool writeStringToBufferErrorToken(const char* str, char** at, char* bufferStart, int bufferSize,
const Token& token)
{
for (const char* c = str; *c != '\0'; ++c)
if (!writeStringToBuffer(str, at, bufferStart, bufferSize))
{
**at = *c;
++(*at);
char* endOfBuffer = bufferStart + bufferSize - 1;
if (*at >= endOfBuffer)
{
ErrorAtTokenf(token, "buffer of size %d was too small. String will be cut off",
bufferSize);
*(endOfBuffer) = '\0';
return false;
}
ErrorAtTokenf(token, "buffer of size %d was too small. String will be cut off", bufferSize);
return false;
}

return true;


+ 4
- 3
src/Tokenizer.hpp View File

@@ -38,7 +38,8 @@ bool validateParentheses(const std::vector<Token>& tokens);

void printTokens(const std::vector<Token>& tokens);

bool writeCharToBuffer(char c, char** at, char* bufferStart, int bufferSize, const Token& token);
bool writeStringToBuffer(const char* str, char** at, char* bufferStart, int bufferSize,
const Token& token);
bool writeCharToBufferErrorToken(char c, char** at, char* bufferStart, int bufferSize,
const Token& token);
bool writeStringToBufferErrorToken(const char* str, char** at, char* bufferStart, int bufferSize,
const Token& token);
bool appendTokenToString(const Token& token, char** at, char* bufferStart, int bufferSize);

+ 33
- 0
src/Utilities.cpp View File

@@ -27,3 +27,36 @@ FILE* fileOpen(const char* filename, const char* mode)
}
return file;
}

bool writeCharToBuffer(char c, char** at, char* bufferStart, int bufferSize)
{
**at = c;
++(*at);
char* endOfBuffer = bufferStart + (bufferSize - 1);
if (*at > endOfBuffer)
{
printf("error: buffer of size %d was too small. String will be cut off\n", bufferSize);
*endOfBuffer = '\0';
return false;
}

return true;
}

bool writeStringToBuffer(const char* str, char** at, char* bufferStart, int bufferSize)
{
for (const char* c = str; *c != '\0'; ++c)
{
**at = *c;
++(*at);
char* endOfBuffer = bufferStart + (bufferSize - 1);
if (*at > endOfBuffer)
{
printf("error: buffer of size %d was too small. String will be cut off\n", bufferSize);
*(endOfBuffer) = '\0';
return false;
}
}

return true;
}

+ 3
- 0
src/Utilities.hpp View File

@@ -22,6 +22,9 @@ void printIndentToDepth(int depth);
// TODO Replace with strcat()
#define PrintBuffer(buffer, output) SafeSnprinf(buffer, sizeof(buffer), "%s", output)

bool writeCharToBuffer(char c, char** at, char* bufferStart, int bufferSize);
bool writeStringToBuffer(const char* str, char** at, char* bufferStart, int bufferSize);

// TODO De-macroize these? It could be useful to keep as macros if I add __LINE__ etc. (to answer
// questions like "where is this error coming from?")



Loading…
Cancel
Save