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