-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbuild.yue
229 lines (172 loc) · 6.62 KB
/
build.yue
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
-- Tsuki Build Script
import "lfs"
ERR = 1
OK = 0
BLOCKLIST_LINES =
["conf.lua"]: {
"%s*require%(\"yue\"%)[^\n]*",
}
traverse = (func, path, dirs) ->
-- Traverse a directory recursively and apply a function to files found
-- Returns OK on success, ERR on failure from the func result
-- @param func The function to apply to files
-- @param path The path to traverse from
traverse_stack = [ "#{path}/#{entry}" for entry in lfs.dir(path) ]
seen = {}
rv = OK
while #traverse_stack > 0
entry = table.remove(traverse_stack)
filename = entry\match("([^/]+)$")
-- Skip builtin traversals and nonexistent files/dirs
if filename == "." or filename == ".." or lfs.attributes(entry) == nil
continue
-- If we have seen this entry before, skip it
if #[e for _, e in ipairs seen when e == entry] != 0
continue
table.insert(seen, entry)
-- Recurse on directories and call the callback on files
if lfs.attributes(entry, "mode") == "directory"
for sentry in lfs.dir(entry)
table.insert(traverse_stack, "#{entry}/#{sentry}")
if dirs and not func(entry)
rv = ERR
else
if not func(entry)
rv = ERR
return rv
class TsukiBuild
new: (src) =>
-- Instantiate a new build of the directory
-- given in src
@src = src
build_file: (path) =>
-- Build a .yue file. Called as a callback by the
-- directory traversal. Returns OK on success, ERR on failure
rv = OK
if path\match("^.+%.yue$")
fd = io.popen("yue --target=5.1 #{path}")
output = fd\read("*a")
status = fd\close()
if not status
print "Error building #{path}"
print output
rv = ERR
return rv
clean_file: (path) =>
-- Clean .lua files. Called as a callback by the
-- directory traversal. Returns OK on success, and cannot fail
if path\match("^.+%.lua$") and not path\match("conf.lua") and not path\match("main.lua")
os.remove(path)
print "Removed #{path}"
return OK
dist_file: (path) =>
-- Clean a .yue file. Called as a callback by the
-- directory traversal. Returns OK on success, and cannot fail
if path\match("^.+%.yue$")
os.remove(path)
print "Removed #{path}"
return OK
delete_all: (path) =>
-- Delete a file or directory. Called as a callback by the
-- directory traversal. Returns OK on success, and cannot fail
if lfs.attributes(path, "mode") == "directory"
for entry in lfs.dir(path)
print "Removing #{path}/#{entry}"
os.remove("#{path}/#{entry}")
print "Removing #{path}"
os.remove(path)
else
print "Removing #{path}"
os.remove(path)
return OK
build: () =>
-- Run the build. Returns the status of the build
return traverse(@\build_file, @src, false)
clean: () =>
-- Clean the source directory of build files. Returns the status of the clean
return traverse(@\clean_file, @src, false)
dist: (yes) =>
-- Build for distribution by building lua files and removing Yuescript files.
rv = OK
-- Prompt for y/n, this is a dangerous operation
git_status = io.popen("git status -s")
if git_status\read("*a") != "" and not yes
print "Git tree is dirty. Commit changes and commit/remove untracked files or use the --yes flag"
print "Maybe you forgot to run 'build.yue -c'"
rv = ERR
return rv
git_status\close()
rv = @\build!
if rv == ERR
print "Build errors occured. Aborting."
return rv
answer = "n"
if yes
answer = "y"
else
io.write "Building distribution. Are you sure you want to remove all Yuescript files? (y/n) "
answer = io.read()
if answer == "y" or yes
rv = traverse(@\dist_file, @src, false)
for file, blocklist in pairs BLOCKLIST_LINES
print "Removing blocklist lines from #{file}"
file_h = io.open(file, "r")
content = file_h\read("*a")
file_h\close()
for _, line in ipairs blocklist
content = content\gsub(line, "")
file_h = io.open(file, "w")
file_h\write(content)
file_h\close()
print "Overwriting main.lua for distribution"
main_lua_contents = "require(\"src.Main\")"
main_lua = io.open("main.lua", "w")
main_lua\write(main_lua_contents)
main_lua\close()
else
print "Aborting: '#{answer}' is not y"
rv = ERR
return rv
test: (patterns, verbose) =>
rv = OK
for _, pattern in ipairs patterns
print("Running tests with pattern #{pattern}")
result = io.popen("busted -p #{pattern} #{@src}")
output = result\read("*a")
status = result\close!
if not status
rv = ERR
if not status or verbose
print(output)
if rv == ERR
print("Tests failed")
else
print("All tests passed")
return rv
main = (arg) ->
-- Create and run the build
argparse = require("argparse")
parser = argparse("build", "Build a Tsuki project")
parser\argument("src", "Source directory")
parser\flag("-c --clean", "Clean the build directory (remove lua build files)")\args("?")
parser\flag("-d --dist", "Build a distribution and remove all Yuescript files after building")\args("?")
parser\flag("-y --yes", "Answer yes to all prompts. THIS MAY BE DANGEROUS!")\args("?")
parser\flag("-t --test", "Run tests")\args("?")
parser\flag("-v --verbose", "Verbose output")\args("?")
parser\option("-p --pattern", "Pattern(s) to use to search for test files")\args("+")\default("Test.+%.yue")\defmode("u")
args = parser\parse(arg)
if #arg < 1
print "Usage: tsuki <srcdir>"
return
build = TsukiBuild(args["src"])
rv = OK
if args["test"]
rv = build\test(args["pattern"], args["verbose"])
elseif args["clean"]
rv = build\clean!
elseif args["dist"]
rv = build\dist(args["yes"])
else
rv = build\build!
return rv
os.exit(main(arg))