@@ -7,141 +7,174 @@ compiler = require 'compiler'
7
7
util = require ' echo-util'
8
8
{ spawn } = require ' child_process'
9
9
10
- # this needs to be ".slice 1" for the ejs compiled form.
11
- # we probably need some global property so we can do:
12
- #
13
- # slice_count = if ejs? then 1 else 2
14
- # argv = process.argv.slice slice_count
15
- #
16
- argv = process .argv .slice 2
10
+ slice_count = if __ejs? then 1 else 2
11
+ argv = process .argv .slice slice_count
12
+
13
+ external_modules = []
14
+ files = []
15
+
16
+ add_external_module = (modinfo ) ->
17
+ [library ,module_name ,module_entrypoint ,link_flags ] = modinfo .split ' ,'
18
+ external_modules .push { library, module_name, module_entrypoint, link_flags }
17
19
18
20
options =
19
- # our defaults:
20
- output_filename : null
21
- combine_scripts : false
22
- show_help : false
21
+ # our defaults:
22
+ output_filename : null
23
+ combine_scripts : false
24
+ show_help : false
25
+ link_with_cxx : false
23
26
24
27
args =
25
- " -s" : { flag : " combine_scripts" , help : " treat all toplevels as being 1 context, evaluating them in the order present on the command line" }
26
- " -o" : { option : " output_filename" , help : " name of the output file." }
27
- " --help" : { flag : " show_help" , help : " output this help info." }
28
+ " -s" : { flag : " combine_scripts" , help : " treat all toplevels as being 1 context, evaluating them in the order present on the command line" }
29
+ " -o" : { option : " output_filename" , help : " name of the output file." }
30
+ " -cxx" : { flag : " link_with_cxx" , help : " use clang++ to do the final link (default = no)" }
31
+ " --module" : { handler : add_external_module, help : " --module library.a,module-name,module_init,link_flags" }
32
+ " --help" : { flag : " show_help" , help : " output this help info." }
28
33
29
34
output_usage = ->
30
- console .warn ' Usage:' ;
31
- console .warn ' ejs [options] file1.js file2.js file.js ...'
35
+ console .warn ' Usage:' ;
36
+ console .warn ' ejs [options] file1.js file2.js file.js ...'
32
37
33
38
output_options = ->
34
- console .warn ' Options:'
35
- for a of args
36
- console .warn " #{ a} : #{ args[a].help } "
37
-
38
- files = []
39
+ console .warn ' Options:'
40
+ for a of args
41
+ console .warn " #{ a} : #{ args[a].help } "
42
+
39
43
if argv .length > 0
40
- for ai in [0 .. argv .length - 1 ]
41
- if args[argv[ai]]?
42
- o = args[argv[ai]]
43
- if o .flag ?
44
- options[o .flag ] = true
45
- else if o .option ?
46
- options[o .option ] = argv[++ ai]
47
- else
48
- # end of options signals the rest of the array is files
49
- files = argv .slice ai
50
- break
44
+ for ai in [0 .. argv .length - 1 ]
45
+ if args[argv[ai]]?
46
+ o = args[argv[ai]]
47
+ if o .flag ?
48
+ options[o .flag ] = true
49
+ else if o .option ?
50
+ options[o .option ] = argv[++ ai]
51
+ else if o .handler ?
52
+ o .handler argv[++ ai]
53
+ else
54
+ # end of options signals the rest of the array is files
55
+ files = argv .slice ai
56
+ break
51
57
52
58
if options .show_help
53
- output_usage ()
54
- console .warn ' '
55
- output_options ()
56
- process .exit 0
59
+ output_usage ()
60
+ console .warn ' '
61
+ output_options ()
62
+ process .exit 0
57
63
58
64
if files .length is 0
59
- output_usage ()
60
- process .exit 0
65
+ output_usage ()
66
+ process .exit 0
61
67
62
68
files_remaining = 0
63
69
64
- s_filenames = []
70
+ o_filenames = []
65
71
66
72
base_filenames = (path .basename file for file in files)
67
73
68
74
compiled_modules = []
69
75
70
76
compileFile = (filename , content , compileCallback ) ->
71
- parse_tree = esprima .parse content, loc : true , raw : true
72
-
73
- base_filename = util .genFreshFileName path .basename filename
74
- compiled_module = compiler .compile parse_tree, base_filename
75
-
76
- ll_filename = " /tmp/#{ base_filename} .ll"
77
- bc_filename = " /tmp/#{ base_filename} .bc"
78
- bc_opt_filename = " /tmp/#{ base_filename} .bc.opt"
79
- ll_opt_filename = " /tmp/#{ base_filename} .ll.opt"
80
- s_filename = " /tmp/#{ base_filename} .s"
81
-
82
- llvm_as_args = [" -o=#{ bc_filename} " , ll_filename]
83
- opt_args = [" -O3" , " -mem2reg" , " -o=#{ bc_opt_filename} " , bc_filename]
84
- llvm_dis_args = [" -o=#{ ll_opt_filename} " , bc_opt_filename]
85
- llc_args = [" -march=x86-64" , " -O2" , " -o=#{ s_filename} " , ll_opt_filename]
86
-
87
- compiled_module .writeToFile ll_filename
88
-
89
- compiled_modules .push filename : filename, module_toplevel : compiled_module .toplevel_name
90
-
91
- llvm_as = spawn " llvm-as" , llvm_as_args
92
- llvm_as .stderr .on " data" , (data ) -> console .warn " #{ data} "
93
- llvm_as .on " exit" , (code ) ->
94
- opt = spawn " opt" , opt_args
95
- opt .stderr .on " data" , (data ) -> console .warn " #{ data} "
96
- opt .on " exit" , (code ) ->
97
- llvm_dis = spawn " llvm-dis" , llvm_dis_args
98
- llvm_dis .stderr .on " data" , (data ) -> console .warn " #{ data} "
99
- llvm_dis .on " exit" , (code ) ->
100
- llc = spawn " llc" , llc_args
101
- llc .stderr .on " data" , (data ) -> console .warn " #{ data} "
102
- llc .on " exit" , (code ) ->
103
- s_filenames .push s_filename
104
- compileCallback ()
77
+ try
78
+ parse_tree = esprima .parse content, loc : true , raw : true
79
+ catch e
80
+ console .warn " parse error at #{ filename} :"
81
+ process .exit - 1
82
+
83
+ base_filename = util .genFreshFileName path .basename filename
84
+ compiled_module = compiler .compile parse_tree, base_filename
85
+
86
+ ll_filename = " /tmp/#{ base_filename} .ll"
87
+ bc_filename = " /tmp/#{ base_filename} .bc"
88
+ bc_opt_filename = " /tmp/#{ base_filename} .bc.opt"
89
+ ll_opt_filename = " /tmp/#{ base_filename} .ll.opt"
90
+ s_filename = " /tmp/#{ base_filename} .s"
91
+ o_filename = " /tmp/#{ base_filename} .o"
92
+
93
+ llvm_as_args = [" -o=#{ bc_filename} " , ll_filename]
94
+ opt_args = [" -O3" , " -mem2reg" , " -o=#{ bc_opt_filename} " , bc_filename]
95
+ llvm_dis_args = [" -o=#{ ll_opt_filename} " , bc_opt_filename]
96
+ llc_args = [" -march=x86-64" , " -O2" , " -o=#{ s_filename} " , ll_opt_filename]
97
+ clang_args = [" -march=x86-64" , " -O2" , " -c" , " -o" , o_filename, s_filename]
98
+
99
+ compiled_module .writeToFile ll_filename
100
+
101
+ compiled_modules .push filename : filename, module_toplevel : compiled_module .toplevel_name
102
+
103
+ llvm_as = spawn " llvm-as" , llvm_as_args
104
+ llvm_as .stderr .on " data" , (data ) -> console .warn " #{ data} "
105
+ llvm_as .on " exit" , (code ) ->
106
+ opt = spawn " opt" , opt_args
107
+ opt .stderr .on " data" , (data ) -> console .warn " #{ data} "
108
+ opt .on " exit" , (code ) ->
109
+ llvm_dis = spawn " llvm-dis" , llvm_dis_args
110
+ llvm_dis .stderr .on " data" , (data ) -> console .warn " #{ data} "
111
+ llvm_dis .on " exit" , (code ) ->
112
+ llc = spawn " llc" , llc_args
113
+ llc .stderr .on " data" , (data ) -> console .warn " #{ data} "
114
+ llc .on " exit" , (code ) ->
115
+ clang = spawn " clang" , clang_args
116
+ clang .stderr .on " data" , (data ) -> console .warn " #{ data} "
117
+ clang .on " exit" , (code ) ->
118
+ o_filenames .push o_filename
119
+ compileCallback ()
105
120
106
121
generate_require_map = ->
107
- sanitize = (filename , c_callable ) ->
108
- filename = filename .replace / \. js$ / , " "
109
- if c_callable
110
- filename = filename .replace / [. ,-\/ \\ ] / g , " _" # this is insanely inadequate
111
- filename
112
- map_path = " /tmp/#{ util .genFreshFileName path .basename files[0 ]} -require-map.c"
113
- map = fs .createWriteStream map_path
114
- map .write " typedef struct _EJSValue EJSValue;\n "
115
- map .write " typedef int EJSBool;\n "
116
- map .write " typedef EJSValue* (*ToplevelFunc) (EJSValue *env, EJSValue *this, int argc, EJSValue *exports);\n "
117
- map .write " typedef struct { const char* name; ToplevelFunc func; EJSValue *cached_exports; } EJSRequire;\n "
118
- compiled_modules .forEach ({ filename, module_toplevel }) ->
119
- sanitized = sanitize filename, true
120
- map .write " extern EJSValue* #{ module_toplevel} (EJSValue* env, EJSValue *this, int argc, EJSValue *exports);\n "
121
-
122
-
123
- map .write " EJSRequire _ejs_require_map[] = {\n "
124
- compiled_modules .forEach ({ filename, module_toplevel }) ->
125
- map .write " { \" #{ sanitize (path .basename filename), false } \" , #{ module_toplevel} , 0 },\n "
126
- map .write " { 0, 0, 0 }\n "
127
- map .write " };\n "
128
- map .write " const char *entry_filename = \" #{ sanitize base_filenames[0 ], false } \" ;\n " ;
129
- map .end ()
130
- map_path
122
+ sanitize = (filename , c_callable ) ->
123
+ filename = filename .replace / \. js$ / , " "
124
+ if c_callable
125
+ filename = filename .replace / [. ,-\/ \\ ] / g , " _" # this is insanely inadequate
126
+ filename
127
+ map_path = " /tmp/#{ util .genFreshFileName path .basename files[0 ]} -require-map.#{ if options .link_with_cxx then ' cpp' else ' c' } "
128
+ map = fs .createWriteStream map_path
129
+ if options .link_with_cxx
130
+ map .write " extern \" C\" {"
131
+ map .write " typedef struct _EJSValue EJSValue;\n "
132
+ map .write " typedef int EJSBool;\n "
133
+ map .write " typedef EJSValue* (*ExternalModuleEntry) (EJSValue *exports);\n "
134
+ map .write " typedef struct { const char* name; ExternalModuleEntry func; EJSValue *cached_exports; } EJSExternalModuleRequire;\n "
135
+ map .write " typedef EJSValue* (*ToplevelFunc) (EJSValue *env, EJSValue *_this, int argc, EJSValue **args);\n "
136
+ map .write " typedef struct { const char* name; ToplevelFunc func; EJSValue *cached_exports; } EJSRequire;\n "
137
+ external_modules .forEach ({ module_entrypoint }) ->
138
+ map .write " extern EJSValue* #{ module_entrypoint} (EJSValue *exports);\n "
139
+
140
+ compiled_modules .forEach ({ filename, module_toplevel }) ->
141
+ sanitized = sanitize filename, true
142
+ map .write " extern EJSValue* #{ module_toplevel} (EJSValue* env, EJSValue *_this, int argc, EJSValue **args);\n "
143
+
144
+ map .write " EJSRequire _ejs_require_map[] = {\n "
145
+ compiled_modules .forEach ({ filename, module_toplevel }) ->
146
+ map .write " { \" #{ sanitize (path .basename filename), false } \" , #{ module_toplevel} , 0 },\n "
147
+ map .write " { 0, 0, 0 }\n "
148
+ map .write " };\n "
149
+
150
+ map .write " EJSExternalModuleRequire _ejs_external_module_require_map[] = {\n "
151
+ external_modules .forEach ({ module_name, module_entrypoint }) ->
152
+ map .write " { \" #{ module_name} \" , #{ module_entrypoint} , 0 },\n "
153
+ map .write " { 0, 0, 0 }\n "
154
+ map .write " };\n "
155
+
156
+ map .write " const char *entry_filename = \" #{ sanitize base_filenames[0 ], false } \" ;\n " ;
157
+
158
+ if options .link_with_cxx
159
+ map .write " };"
160
+ map .end ()
161
+ map_path
131
162
132
163
133
164
file_compiled = ->
134
- console .log " done compiling file"
135
165
files_remaining = files_remaining - 1
136
- console .log " files remaining == #{ files_remaining} "
137
166
if files_remaining is 0
138
167
map_filename = generate_require_map compiled_modules
139
168
140
- clang_args = [" -o" , options .output_filename || " #{ files[0 ]} .exe" ].concat s_filenames
169
+ clang_args = [" -v " , " - o" , options .output_filename || " #{ files[0 ]} .exe" ].concat o_filenames
141
170
clang_args .push map_filename
142
171
clang_args .push path .resolve (path .dirname process .argv [1 ]), " runtime/libecho.a"
172
+ for extern_module in external_modules
173
+ clang_args .push extern_module .library
174
+ clang_args = clang_args .concat extern_module .link_flags .split " "
143
175
144
- clang = spawn " clang" , clang_args
176
+ console .warn " clang command line #{ (if options .link_with_cxx then ' clang++' else ' clang' )} #{ clang_args .join ' ' } "
177
+ clang = spawn (if options .link_with_cxx then " g++" else " clang" ), clang_args
145
178
clang .stderr .on " data" , (data ) -> console .warn " #{ data} "
146
179
clang .on " exit" , (code ) ->
147
180
console .warn " done."
@@ -155,5 +188,4 @@ if options.combine_scripts
155
188
else
156
189
files_remaining = files .length
157
190
files .forEach (filename) ->
158
- compileFile filename, (fs .readFileSync filename, ' utf-8' ), file_compiled
159
-
191
+ compileFile filename, (fs .readFileSync filename, ' utf-8' ), file_compiled
0 commit comments