Merge branch 'master' into pnacl_integration
[nit.git] / src / pnacl_platform.nit
1 # This file is part of NIT ( http://www.nitlanguage.org )
2 #
3 # Copyright 2014 Johan Kayser <kayser.johan@gmail.com>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Compile program for the PNaCl platform
18 module pnacl_platform
19
20 import platform
21 import abstract_compiler
22
23 redef class ToolContext
24 redef fun platform_from_name(name)
25 do
26 if name == "pnacl" then return new PnaclPlatform
27 return super
28 end
29 end
30
31 class PnaclPlatform
32 super Platform
33
34 redef fun toolchain(toolcontext) do return new PnaclToolchain(toolcontext)
35 end
36
37 class PnaclToolchain
38 super MakefileToolchain
39
40 var pnacl_project_root: String
41
42 redef fun compile_dir
43 do
44 var normal_compile_dir = super
45 pnacl_project_root = "{normal_compile_dir}/pnacl/"
46 return "{normal_compile_dir}/pnacl/src/"
47 end
48
49 redef fun write_files(compiler, compile_dir, cfiles)
50 do
51 var app_name = compiler.mainmodule.name
52
53 # create compile_dir
54 var dir = pnacl_project_root
55 if not dir.file_exists then dir.mkdir
56
57 dir = compile_dir
58 if not dir.file_exists then dir.mkdir
59
60 # compile normal C files
61 super(compiler, compile_dir, cfiles)
62
63 # Gather extra C files generated elsewhere than in super
64 for f in compiler.extern_bodies do
65 if f isa ExternCFile then cfiles.add(f.filename.basename(""))
66 end
67
68 # Outname
69 var outname = toolcontext.opt_output.value
70 if outname == null then outname = "{compiler.mainmodule.name}"
71
72 ## Generate makefile
73 dir = pnacl_project_root
74 if not dir.file_exists then dir.mkdir
75 var file = "{dir}/Makefile"
76 """
77 # This file was generated by Nit, any modification will be lost.
78
79 # Get pepper directory for toolchain and includes.
80 #
81 # If NACL_SDK_ROOT is not set, then assume it can be found five directories up.
82 #
83 THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
84 NACL_SDK_ROOT ?= $(abspath $(dir $(THIS_MAKEFILE))../../../..)
85
86 # Project Build flags
87 WARNINGS := -Wno-long-long -Wall -Wswitch-enum -pedantic -Werror
88 CXXFLAGS := -pthread -std=gnu++98 $(WARNINGS)
89
90 #
91 # Compute tool paths
92 #
93 GETOS := python $(NACL_SDK_ROOT)/tools/getos.py
94 OSHELPERS = python $(NACL_SDK_ROOT)/tools/oshelpers.py
95 OSNAME := $(shell $(GETOS))
96 RM := $(OSHELPERS) rm
97
98 PNACL_TC_PATH := $(abspath $(NACL_SDK_ROOT)/toolchain/$(OSNAME)_pnacl)
99 PNACL_CXX := $(PNACL_TC_PATH)/bin/pnacl-clang
100 PNACL_FINALIZE := $(PNACL_TC_PATH)/bin/pnacl-finalize
101 CXXFLAGS := -I$(NACL_SDK_ROOT)/include
102 LDFLAGS := -L$(NACL_SDK_ROOT)/lib/pnacl/Release -lppapi_cpp -lppapi
103
104 #
105 # Disable DOS PATH warning when using Cygwin based tools Windows
106 #
107 CYGWIN ?= nodosfilewarning
108 export CYGWIN
109
110
111 # Declare the ALL target first, to make the 'all' target the default build
112 all: ../../{{{outname}}}/{{{app_name}}}.pexe
113
114 clean:
115 $(RM) {{{app_name}}}.pexe
116
117 {{{app_name}}}.pexe: src/{{{cfiles.join(" src/")}}}
118 $(PNACL_CXX) -o $@ $^ -g -O0 $(CXXFLAGS) $(LDFLAGS) # For Debug
119 # $(PNACL_CXX) -o $@ $^ -O3 $(CXXFLAGS) $(LDFLAGS) # For Release
120
121 ../../{{{outname}}}/{{{app_name}}}.pexe: {{{app_name}}}.pexe
122 $(PNACL_FINALIZE) -o $@ $<
123
124 #
125 # Makefile target to run the SDK's simple HTTP server and serve this example.
126 #
127 HTTPD_PY := python $(NACL_SDK_ROOT)/tools/httpd.py
128
129 .PHONY: serve
130 serve: all
131 $(HTTPD_PY) -C $(CURDIR)
132 """.write_to_file(file)
133
134 ### generate the minimal index.html
135 if not outname.file_exists then outname.mkdir
136 file = "{outname}/index.html"
137 if not file.file_exists then
138 """
139 <!DOCTYPE html>
140 <html>
141 <!--
142 This file was generated by Nit, any modification will be lost.
143 -->
144 <head>
145 <title>{{{app_name}}}</title>
146 <script src="js/pnacl_js.js"></script>
147 </head>
148 <body onload="pageDidLoad()">
149 <h1>PNaCl : Minimal HTML for {{{app_name}}}</h1>
150 <p>
151 <!--
152 Load the published pexe.
153 Note: Since this module does not use any real-estate in the browser, its
154 width and height are set to 0.
155
156 Note: The <embed> element is wrapped inside a <div>, which has both a 'load'
157 and a 'message' event listener attached. This wrapping method is used
158 instead of attaching the event listeners directly to the <embed> element to
159 ensure that the listeners are active before the NaCl module 'load' event
160 fires. This also allows you to use PPB_Messaging.PostMessage() (in C) or
161 pp::Instance.PostMessage() (in C++) from within the initialization code in
162 your module.
163 -->
164 <div id="listener">
165 <script type="text/javascript">
166 var listener = document.getElementById('listener');
167 listener.addEventListener('load', moduleDidLoad, true);
168 listener.addEventListener('message', handleMessage, true);
169 </script>
170
171 <embed id="{{{app_name}}}"
172 width=0 height=0
173 src="{{{app_name}}}.nmf"
174 type="application/x-pnacl" />
175 </div>
176 </p>
177 <h2>Status <code id="statusField">NO-STATUS</code></h2>
178 </body>
179 </html>
180 """.write_to_file(file)
181 end
182
183 ### generate pnacl_js.js in a folder named 'js'
184 dir = "{outname}/js/"
185 if not dir.file_exists then dir.mkdir
186 file = "{dir}/pnacl_js.js"
187 if not file.file_exists then
188 """
189 // This file was generated by Nit, any modification will be lost.
190
191 {{{app_name}}}Module = null; // Global application object.
192 statusText = 'NO-STATUS';
193
194 // Indicate load success.
195 function moduleDidLoad() {
196 {{{app_name}}}Module = document.getElementById('{{{app_name}}}');
197 updateStatus('SUCCESS');
198 // Send a message to the Native Client module like that
199 //{{{app_name}}}Module.postMessage('Hello World');
200 }
201
202 // The 'message' event handler. This handler is fired when the NaCl module
203 // posts a message to the browser by calling PPB_Messaging.PostMessage()
204 // (in C) or pp::Instance.PostMessage() (in C++). This implementation
205 // simply displays the content of the message in an alert panel.
206 function handleMessage(message_event) {
207 console.log(message_event.data);
208 }
209
210 // If the page loads before the Native Client module loads, then set the
211 // status message indicating that the module is still loading. Otherwise,
212 // do not change the status message.
213 function pageDidLoad() {
214 if ({{{app_name}}}Module == null) {
215 updateStatus('LOADING...');
216 } else {
217 // It's possible that the Native Client module onload event fired
218 // before the page's onload event. In this case, the status message
219 // will reflect 'SUCCESS', but won't be displayed. This call will
220 // display the current message.
221 updateStatus();
222 }
223 }
224
225 // Set the global status message. If the element with id 'statusField'
226 // exists, then set its HTML to the status message as well.
227 // opt_message The message test. If this is null or undefined, then
228 // attempt to set the element with id 'statusField' to the value of
229 // |statusText|.
230 function updateStatus(opt_message) {
231 if (opt_message)
232 statusText = opt_message;
233 var statusField = document.getElementById('statusField');
234 if (statusField) {
235 statusField.innerHTML = statusText;
236 }
237 }
238 """.write_to_file(file)
239 end
240
241 ### generate the manifest file : app_name.nmf
242 # used to point the HTML to the Native Client module
243 # and optionally provide additional commands to the PNaCl translator in Chrome
244 file = "{outname}/{app_name}.nmf"
245 """
246 {
247 "program": {
248 "portable": {
249 "pnacl-translate": {
250 "url": "{{{app_name}}}.pexe"
251 }
252 }
253 }
254 }
255 """.write_to_file(file)
256 end
257
258 redef fun write_makefile(compiler, compile_dir, cfiles)
259 do
260 # Do nothing, already done in `write_files`
261 end
262
263 redef fun compile_c_code(compiler, compile_dir)
264 do
265 # Generate the pexe
266 toolcontext.exec_and_check(["make", "-C", pnacl_project_root], "PNaCl project error")
267 end
268 end