Skip to content

Commit f22151b

Browse files
committed
Added ability to build arbitrary C++ files
This is necessary to interface with existing C++ code easily.
1 parent 5c12748 commit f22151b

9 files changed

+126
-23
lines changed

BuildAndRunTests.sh

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@
88
# jam -j4 && ./bin/cakelisp --enable-hot-reloading runtime/TestMain.cake runtime/TextAdventure.cake \
99
# && cd runtime && jam -j4
1010

11-
jam -j4 && ./bin/cakelisp --verbose-build-process runtime/TestMain.cake runtime/TextAdventure.cake
11+
jam -j4 && ./bin/cakelisp --verbose-build-process --verbose-processes \
12+
runtime/TestMain.cake runtime/TextAdventure.cake

runtime/HotReloading.cake

+5-2
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,21 @@
44
;; Include cakelisp source for DynamicLoader.hpp
55
(set-module-option build-time-compile-arguments
66
"-std=c++11" "-Wall" "-Wextra" "-Wno-unused-parameter" "-O0"
7+
;; TODO: Multiplatform support
8+
"-DUNIX"
79
"-g" "-c" 'source-input "-o" 'object-output "-fPIC" "-Isrc/")
810
;; TODO: This only makes sense on a per-target basis. Instead, modules should be able to append
911
;; arguments to the link command only
1012
(set-module-option build-time-linker "/usr/bin/clang++")
1113
;; This needs to link -ldl and such (depending on platform...)
1214
(set-cakelisp-option build-time-link-arguments
1315
"-shared" "-o" 'executable-output 'object-input
14-
;; TODO: Make this get built by cakelisp too?
15-
"src/DynamicLoader.o"
1616
;; TODO: OS dependent
1717
"-ldl" "-lpthread")
1818

19+
;; TODO: Relative vs. absolute paths
20+
(add-cpp-build-dependency "../src/DynamicLoader.cpp")
21+
1922
(import &comptime-only "Macros.cake")
2023
(c-import "<unordered_map>" "<vector>")
2124
(c-import "DynamicLoader.hpp")

src/Building.cpp

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include "Building.hpp"
2+
3+
#include "Utilities.hpp"
4+
#include "FileUtilities.hpp"
5+
6+
bool objectFilenameFromSourceFilename(const char* cakelispWorkingDir, const char* sourceFilename,
7+
char* bufferOut, int bufferSize)
8+
{
9+
char buildFilename[MAX_NAME_LENGTH] = {0};
10+
getFilenameFromPath(sourceFilename, buildFilename, sizeof(buildFilename));
11+
if (!buildFilename[0])
12+
return false;
13+
14+
// TODO: Trim .cake.cpp (etc.)
15+
SafeSnprinf(bufferOut, bufferSize, "%s/%s.o", cakelispWorkingDir, buildFilename);
16+
return true;
17+
}

src/Building.hpp

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#pragma once
2+
3+
bool objectFilenameFromSourceFilename(const char* cakelispWorkingDir, const char* sourceFilename,
4+
char* bufferOut, int bufferSize);

src/FileUtilities.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,12 @@ void getFilenameFromPath(const char* path, char* bufferOut, int bufferSize)
6969
#error Need to be able to strip path to get filename on this platform
7070
#endif
7171
}
72+
73+
void makePathRelativeToFile(const char* filePath, const char* referencedFilePath, char* bufferOut,
74+
int bufferSize)
75+
{
76+
getDirectoryFromPath(filePath, bufferOut, bufferSize);
77+
// TODO: Need to make this safe!
78+
strcat(bufferOut, "/");
79+
strcat(bufferOut, referencedFilePath);
80+
}

src/FileUtilities.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ void makeDirectory(const char* path);
99

1010
void getDirectoryFromPath(const char* path, char* bufferOut, int bufferSize);
1111
void getFilenameFromPath(const char* path, char* bufferOut, int bufferSize);
12+
void makePathRelativeToFile(const char* filePath, const char* referencedFilePath, char* bufferOut,
13+
int bufferSize);

src/Generators.cpp

+34
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,39 @@ bool ImportGenerator(EvaluatorEnvironment& environment, const EvaluatorContext&
395395
return true;
396396
}
397397

398+
bool AddDependencyGenerator(EvaluatorEnvironment& environment, const EvaluatorContext& context,
399+
const std::vector<Token>& tokens, int startTokenIndex,
400+
GeneratorOutput& output)
401+
{
402+
if (!context.module)
403+
{
404+
ErrorAtToken(tokens[startTokenIndex],
405+
"module cannot track dependency (potential internal error)");
406+
return false;
407+
}
408+
409+
int endInvocationIndex = FindCloseParenTokenIndex(tokens, startTokenIndex);
410+
411+
int invocationIndex = startTokenIndex + 1;
412+
413+
int nameIndex = getExpectedArgument("expected dependency name", tokens, startTokenIndex, 1,
414+
endInvocationIndex);
415+
if (nameIndex == -1)
416+
return false;
417+
if (!ExpectTokenType("add dependency", tokens[nameIndex], TokenType_String))
418+
return false;
419+
420+
if (tokens[invocationIndex].contents.compare("add-cpp-build-dependency") == 0)
421+
{
422+
ModuleDependency newDependency = {};
423+
newDependency.type = ModuleDependency_CFile;
424+
newDependency.name = tokens[nameIndex].contents;
425+
context.module->dependencies.push_back(newDependency);
426+
}
427+
428+
return true;
429+
}
430+
398431
bool DefunGenerator(EvaluatorEnvironment& environment, const EvaluatorContext& context,
399432
const std::vector<Token>& tokens, int startTokenIndex, GeneratorOutput& output)
400433
{
@@ -1970,6 +2003,7 @@ void importFundamentalGenerators(EvaluatorEnvironment& environment)
19702003
environment.generators["set-module-option"] = SetModuleOption;
19712004

19722005
environment.generators["skip-build"] = SkipBuildGenerator;
2006+
environment.generators["add-cpp-build-dependency"] = AddDependencyGenerator;
19732007

19742008
// Dispatches based on invocation name
19752009
const char* cStatementKeywords[] = {

src/Jamfile

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ RunProcess.cpp
1616
OutputPreambles.cpp
1717
DynamicLoader.cpp
1818
ModuleManager.cpp
19-
Logging.cpp ;
19+
Logging.cpp
20+
Building.cpp
21+
;
2022

2123
MakeLocate cakelisp$(SUFEXE) : bin ;
2224
MakeLocate libCakelisp.a : lib ;

src/ModuleManager.cpp

+50-19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "ModuleManager.hpp"
22

3+
#include "Building.hpp"
34
#include "Converters.hpp"
45
#include "DynamicLoader.hpp"
56
#include "Evaluator.hpp"
@@ -292,7 +293,10 @@ static void OnCompileProcessOutput(const char* output)
292293
struct BuiltObject
293294
{
294295
int buildStatus;
296+
std::string sourceFilename;
295297
std::string filename;
298+
299+
ProcessCommand* buildCommandOverride;
296300
};
297301

298302
void builtObjectsFree(std::vector<BuiltObject*>& objects)
@@ -315,6 +319,12 @@ bool moduleManagerBuild(ModuleManager& manager)
315319
{
316320
Module* module = manager.modules[moduleIndex];
317321

322+
ProcessCommand* buildCommandOverride =
323+
(!module->buildTimeBuildCommand.fileToExecute.empty() &&
324+
!module->buildTimeBuildCommand.arguments.empty()) ?
325+
&module->buildTimeBuildCommand :
326+
nullptr;
327+
318328
if (log.buildProcess)
319329
printf("Build module %s\n", module->sourceOutputName.c_str());
320330
for (ModuleDependency& dependency : module->dependencies)
@@ -326,48 +336,70 @@ bool moduleManagerBuild(ModuleManager& manager)
326336
// ourselves with them
327337
if (dependency.type == ModuleDependency_Cakelisp)
328338
continue;
339+
340+
if (dependency.type == ModuleDependency_CFile)
341+
{
342+
BuiltObject* newBuiltObject = new BuiltObject;
343+
newBuiltObject->buildStatus = 0;
344+
// TODO: Better to use search directories
345+
char sourceName[MAX_PATH_LENGTH] = {0};
346+
makePathRelativeToFile(module->sourceOutputName.c_str(), dependency.name.c_str(),
347+
sourceName, sizeof(sourceName));
348+
newBuiltObject->sourceFilename = sourceName;
349+
350+
char buildObjectName[MAX_PATH_LENGTH] = {0};
351+
if (!objectFilenameFromSourceFilename(cakelispWorkingDir,
352+
newBuiltObject->sourceFilename.c_str(),
353+
buildObjectName, sizeof(buildObjectName)))
354+
{
355+
builtObjectsFree(builtObjects);
356+
return false;
357+
}
358+
359+
newBuiltObject->filename = buildObjectName;
360+
newBuiltObject->buildCommandOverride = buildCommandOverride;
361+
builtObjects.push_back(newBuiltObject);
362+
}
329363
}
330364

331365
// TODO: Importing needs to set this on the module, not the dependency...
332366
if (module->skipBuild)
333367
continue;
334368

335-
// TODO: Lots of overlap between this and compile-time building
336-
char buildFilename[MAX_NAME_LENGTH] = {0};
337-
getFilenameFromPath(module->sourceOutputName.c_str(), buildFilename, sizeof(buildFilename));
338-
if (!buildFilename[0])
369+
char buildObjectName[MAX_PATH_LENGTH] = {0};
370+
if (!objectFilenameFromSourceFilename(cakelispWorkingDir, module->sourceOutputName.c_str(),
371+
buildObjectName, sizeof(buildObjectName)))
339372
{
340373
builtObjectsFree(builtObjects);
341374
return false;
342375
}
343376

344-
// TODO: Trim .cake.cpp
345-
char buildObjectName[MAX_PATH_LENGTH] = {0};
346-
PrintfBuffer(buildObjectName, "%s/%s.o", cakelispWorkingDir, buildFilename);
347-
348377
// At this point, we do want to build the object. We might skip building it if it is cached.
349378
// In that case, the status code should still be 0, as if we built and succeeded building it
350379
BuiltObject* newBuiltObject = new BuiltObject;
351380
newBuiltObject->buildStatus = 0;
381+
newBuiltObject->sourceFilename = module->sourceOutputName.c_str();
352382
newBuiltObject->filename = buildObjectName;
383+
newBuiltObject->buildCommandOverride = buildCommandOverride;
353384
builtObjects.push_back(newBuiltObject);
385+
}
354386

355-
if (!fileIsMoreRecentlyModified(module->sourceOutputName.c_str(), buildObjectName))
387+
for (BuiltObject* object : builtObjects)
388+
{
389+
if (!fileIsMoreRecentlyModified(object->sourceFilename.c_str(), object->filename.c_str()))
356390
{
357391
if (log.buildProcess)
358-
printf("Skipping compiling %s (using cached object)\n",
359-
module->sourceOutputName.c_str());
392+
printf("Skipping compiling %s (using cached object)\n", object->sourceFilename.c_str());
360393
}
361394
else
362395
{
363-
ProcessCommand& buildCommand = (!module->buildTimeBuildCommand.fileToExecute.empty() &&
364-
!module->buildTimeBuildCommand.arguments.empty()) ?
365-
module->buildTimeBuildCommand :
396+
ProcessCommand& buildCommand = object->buildCommandOverride ?
397+
*object->buildCommandOverride :
366398
manager.environment.buildTimeBuildCommand;
367399

368400
ProcessCommandInput buildTimeInputs[] = {
369-
{ProcessCommandArgumentType_SourceInput, {module->sourceOutputName.c_str()}},
370-
{ProcessCommandArgumentType_ObjectOutput, {buildObjectName}}};
401+
{ProcessCommandArgumentType_SourceInput, {object->sourceFilename.c_str()}},
402+
{ProcessCommandArgumentType_ObjectOutput, {object->filename.c_str()}}};
371403
const char** buildArguments = MakeProcessArgumentsFromCommand(
372404
buildCommand, buildTimeInputs, ArraySize(buildTimeInputs));
373405
if (!buildArguments)
@@ -381,7 +413,7 @@ bool moduleManagerBuild(ModuleManager& manager)
381413
compileArguments.arguments = buildArguments;
382414
// PrintProcessArguments(buildArguments);
383415

384-
if (runProcess(compileArguments, &newBuiltObject->buildStatus) != 0)
416+
if (runProcess(compileArguments, &object->buildStatus) != 0)
385417
{
386418
printf("error: failed to invoke compiler\n");
387419
free(buildArguments);
@@ -408,7 +440,6 @@ bool moduleManagerBuild(ModuleManager& manager)
408440
// TODO: Make configurable
409441
const char* outputExecutableName = "a.out";
410442

411-
int objectNameBufferSize = 0;
412443
int numObjectsToLink = 0;
413444
bool succeededBuild = true;
414445
bool needsLink = false;
@@ -424,7 +455,7 @@ bool moduleManagerBuild(ModuleManager& manager)
424455
}
425456

426457
if (log.buildProcess)
427-
printf("Linking %s\n", object->filename.c_str());
458+
printf("Need to link %s\n", object->filename.c_str());
428459

429460
++numObjectsToLink;
430461

0 commit comments

Comments
 (0)