forked from eriksvedang/cakelisp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathModuleManager.hpp
133 lines (107 loc) · 4.91 KB
/
ModuleManager.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#pragma once
#include <unordered_map>
#include <vector>
#include "Build.hpp"
#include "Evaluator.hpp"
#include "Exporting.hpp"
#include "ModuleManagerEnums.hpp"
#include "RunProcess.hpp"
#include "Tokenizer.hpp"
struct ModuleDependency
{
ModuleDependencyType type;
std::string name;
const Token* blameToken;
};
// Always update both of these. Signature helps validate call
extern const char* g_modulePreBuildHookSignature;
typedef bool (*ModulePreBuildHook)(ModuleManager& manager, Module* module);
struct ModuleExportScope
{
const std::vector<Token>* tokens;
int startTokenIndex; // Start of (export) invocation, not eval statements (for easier errors)
// Prevent double-evaluation
std::unordered_map<std::string, int> modulesEvaluatedExport;
};
struct CakelispDeferredImport
{
const Token* fileToImportToken;
CakelispImportOutput outputTo;
GeneratorOutput* spliceOutput;
Module* importedModule;
};
// A module is typically associated with a single file. Keywords like local mean in-module only
struct Module
{
const char* filename;
const std::vector<Token>* tokens;
GeneratorOutput* generatedOutput;
std::string sourceOutputName;
std::string headerOutputName;
std::vector<CakelispDeferredImport> cakelispImports;
std::vector<ModuleExportScope> exportScopes;
// As soon as the first importer evaluates any exports from this module, we can no longer add
// new exports, because then we would have to go back to the importing modules and re-evaluate.
// We could make it smart enough to do this, but it doesn't seem worth the effort now
bool exportScopesLocked;
// This could be determined by going definition-by-definition, but I'd rather also have a quick
// high-level version for performance and ease of use
RequiredFeature requiredFeatures;
RequiredFeatureReasonList requiredFeaturesReasons;
// Build system
std::vector<ModuleDependency> dependencies;
std::vector<std::string> cSearchDirectories;
std::vector<std::string> additionalBuildOptions;
std::vector<std::string> librarySearchDirectories;
std::vector<std::string> libraryRuntimeSearchDirectories;
std::vector<std::string> libraryDependencies;
// compilerLinkOptions goes to e.g. G++ to set up arguments to the actual linker.
// toLinkerOptions is forwarded to e.g. ld directly, not to G++
std::vector<std::string> compilerLinkOptions;
std::vector<std::string> toLinkerOptions;
// Do not build or link this module. Useful both for compile-time only files (which error
// because they are empty files) and for files only evaluated for their declarations (e.g. if
// the definitions are going to be provided via dynamic linking)
bool skipBuild;
// These make sense to overload if you want a compile-time dependency
ProcessCommand compileTimeBuildCommand;
ProcessCommand compileTimeLinkCommand;
ProcessCommand buildTimeBuildCommand;
// This doesn't really make sense
// ProcessCommand buildTimeLinkCommand;
std::vector<CompileTimeHook> preBuildHooks;
};
struct ModuleManager
{
// Shared environment across all modules
EvaluatorEnvironment environment;
Token globalPseudoInvocationName;
// Pointer only so things cannot move around
std::vector<Module*> modules;
// Cached directory, not necessarily the final artifacts directory (e.g. executable-output
// option sets different location for the final executable)
std::string buildOutputDir;
// If an existing cached build was run, check the current build's commands against the previous
// commands via CRC comparison. This ensures changing commands will cause rebuilds
ArtifactCrcTable cachedCommandCrcs;
// If any artifact no longer matches its crc in cachedCommandCrcs, the change will appear here
ArtifactCrcTable newCommandCrcs;
CAKELISP_API ~ModuleManager() = default;
};
CAKELISP_API void moduleManagerInitialize(ModuleManager& manager);
// Do not close opened dynamic libraries. Should by called by sub-instances of cakelisp instead of
// moduleManagerDestroy(), otherwise they may segfault
CAKELISP_API void moduleManagerDestroyKeepDynLibs(ModuleManager& manager);
// Note that this will close all dynamic libraries
CAKELISP_API void moduleManagerDestroy(ModuleManager& manager);
bool moduleLoadTokenizeValidate(const char* filename, const std::vector<Token>** tokensOut);
CAKELISP_API bool moduleManagerAddEvaluateFile(ModuleManager& manager, const char* filename,
Module** moduleOut);
CAKELISP_API bool moduleManagerEvaluateResolveReferences(ModuleManager& manager);
CAKELISP_API bool moduleManagerWriteGeneratedOutput(ModuleManager& manager);
CAKELISP_API bool moduleManagerBuildAndLink(ModuleManager& manager,
std::vector<std::string>& builtOutputs);
CAKELISP_API bool moduleManagerExecuteBuiltOutputs(ModuleManager& manager,
const std::vector<std::string>& builtOutputs);
// Initializes a normal environment and outputs all generators available to it
void listBuiltInGenerators();