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