-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbuilder.lua
179 lines (153 loc) · 5.2 KB
/
builder.lua
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
-------------
-- BUILDER --
-------------
--[[
This is a custom build script that combines all the files here
into a single lua file, for easier distribution.
It looks for two keywords: return and require
RETURN
It looks for module level returns, which is always the last return in a
file
Identifies the name of the symbol returned
REQUIRE
It looks for require statements. It builds a dependency graph from those.
The graph should be a DAG, so it can be topologically sorted
and thus composed into a single file.
Note: LUA already freaks out to circular import,
so it is kinda guaranteed to be a DAG since the code runs.
CHECKS:
- dep graph should be DAG
- requires should not rename (imported symbol name should match the original in the source)
If both checks succeed, it can easily remove requires and returns and splice the files together.
]]
local lfs = require"lfs"
local function findReturn(file)
local ret = nil
for l in io.lines(file) do
local exp = l:match("return (%w+)$")
if exp then
ret = exp
end
end
return ret
end
local function findImports(file)
local imports = {}
for l in io.lines(file) do
if l:match("require") then
local as, name = l:match("(%w+) = require%(\"(%w+)\"%),?$")
if not (as and name) then
print("GODDAMN ERROR")
print("INVALID REQUIRE")
print(("IN FILE \"%s\""):format(file))
print(l)
os.exit()
end
imports[#imports + 1] = {name, as}
end
end
return imports
end
local function topsort(G)
local Gc = {}
local found = {}
while next(G)~=nil do
for n, edges in pairs(G) do
local f = true
for _, e in pairs(edges) do
if not found[e[1]] then
f = false
end
end
if f then
found[n:match("^(%w+)%.lua$")] = true
Gc[#Gc + 1] = {n, G[n]}
G[n] = nil
end
end
end
return Gc
end
-- write the file except last return and imports
-- add alias if necessery
local function build(file, aliases, returns)
local name = file[1]
local f = ""
f = f .. "-- *** BUILDER: SECTION START ***\n"
f = f .. ("-- *** THIS SECTION IS SOURCED FROM FILE \"%s\" ***\n"):format(name)
f = f .. "\n\n"
for l in io.lines(file[1]) do
local written = false
if not (l:match("require")) then
if not (l:match("return") and l:match(returns[name])) then
f = f .. l .. "\n"
written = true
end
elseif not l:match("local") then
f = f .. ("-- *** BUILDER: CHANGED ***\n-- ORIGINAL: \"%s\"\n"):format(l)
local as, from, sep = l:match("(%w+) = require%(\"(%w+)\"%)(,?)")
f = f .. ("%s = %s%s\n"):format(as, returns[from..".lua"], sep or "")
written = true
end
if not written then
f = f .. ("-- *** BUILDER: REMOVED ***\n-- %s\n"):format(l)
end
end
f = f .. "\n"
if aliases[name:match("^(%w+)%.lua$")] then
f = f .. "-- *** BUILDER: SETTING IMPORT ALIAS ***\n"
f = f .. ("local %s = %s\n"):format(aliases[name:match("^(%w+)%.lua$")], returns[name])
f = f .. "\n"
end
f = f .. "\n"
return f
end
-- iterate over all lua files in this directory, except itself
local G = {}
local returns = {}
for file in lfs.dir"." do
if file~="." and file~=".." and file~="builder.lua" and file ~= "bundle.lua" then
if file:match("%.lua$") then
local exp = findReturn(file)
returns[file] = exp
G[file] = findImports(file)
end
end
end
local aliases = {}
for _, imports in pairs(G) do
for _, i in ipairs(imports) do
local from, as = i[1], i[2]
if returns[from..".lua"]~=as then
if aliases[from] and aliases[from]~=as then
print("Error!")
print(("File '%s' is imported as multiple aliases!"):format(from))
print(("%s vs %s"):format(aliases[from], as))
print(("Note: importing it as %s does not count here"):format(from))
os.exit(-1)
end
aliases[from] = as
end
end
end
G = topsort(G)
local BUNDLE = "-- *** BUILDER: START OF FILE ***\n"
BUNDLE = BUNDLE .. "-- THIS IS AN AUTO-GENERATED FILE\n"
BUNDLE = BUNDLE .. "-- BUILT FROM THE DIFFERENT FILES OF YALG\n"
BUNDLE = BUNDLE .. "-- BY AN AUTOMATIC BUILD TOOL\n"
BUNDLE = BUNDLE .. "-- DO NOT EDIT THIS FILE DIRECTLY!\n"
BUNDLE = BUNDLE .. "--\n"
BUNDLE = BUNDLE .. os.date"-- TIMESTAMP: %Y. %B %d. %X\n"
local c = assert(io.popen('git log --format="%H" -n 1', 'r'), "Error: can't run git")
local h = assert(c:read("*a"):match("%w+"), "Error: git shown no output!!")
c:close()
BUNDLE = BUNDLE .. ("-- LATEST COMMIT HASH: %s\n\n\n"):format(h)
for _, n in ipairs(G) do
BUNDLE = BUNDLE .. build(n, aliases, returns)
end
BUNDLE = BUNDLE .. "-- *** BUILDER: RETURN ***\n"
BUNDLE = BUNDLE .. ("return %s\n\n"):format(returns[G[#G][1]])
BUNDLE = BUNDLE .. "-- *** BUILDER: END OF FILE ***"
local output = io.open("bundle.lua", "w")
output:write(BUNDLE)
output:close()