e05c5c67e294ee514b02a8149e75a4cea1df6b27
[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
94 PNACL_TC_PATH := $(abspath $(NACL_SDK_ROOT)/toolchain/$(OSNAME)_pnacl)
95 PNACL_CXX := $(PNACL_TC_PATH)/bin/pnacl-clang
96 PNACL_FINALIZE := $(PNACL_TC_PATH)/bin/pnacl-finalize
97 CXXFLAGS := -I$(NACL_SDK_ROOT)/include
98 LDFLAGS := -L$(NACL_SDK_ROOT)/lib/pnacl/Release -lppapi_cpp -lppapi
99
100 #
101 # Disable DOS PATH warning when using Cygwin based tools Windows
102 #
103 CYGWIN ?= nodosfilewarning
104 export CYGWIN
105
106 # Declare the ALL target first, to make the 'all' target the default build
107 all: ../../{{{outname}}}/{{{app_name}}}.pexe
108
109 {{{app_name}}}.pexe: src/{{{cfiles.join(" src/")}}}
110 $(PNACL_CXX) -o $@ $^ -g -O0 $(CXXFLAGS) $(LDFLAGS) # For Debug
111 # $(PNACL_CXX) -o $@ $^ -O3 $(CXXFLAGS) $(LDFLAGS) # For Release
112
113 ../../{{{outname}}}/{{{app_name}}}.pexe: {{{app_name}}}.pexe
114 $(PNACL_FINALIZE) -o $@ $<
115 """.write_to_file(file)
116
117 ### generate the minimal index.html
118 if not outname.file_exists then outname.mkdir
119 file = "{outname}/index.html"
120
121 if not file.file_exists then """
122 <!DOCTYPE html>
123 <html>
124 <!--
125 This file was generated by Nit, any modification will be lost.
126 -->
127 <head>
128 <title>{{{app_name}}}</title>
129 <script src="js/pnacl_js.js"></script>
130 </head>
131 <body onload="pageDidLoad()">
132 <h1>PNaCl : Minimal HTML for {{{app_name}}}</h1>
133 <p>
134 <!--
135 Load the published pexe.
136 Note: Since this module does not use any real-estate in the browser, its
137 width and height are set to 0.
138
139 Note: The <embed> element is wrapped inside a <div>, which has both a 'load'
140 and a 'message' event listener attached. This wrapping method is used
141 instead of attaching the event listeners directly to the <embed> element to
142 ensure that the listeners are active before the NaCl module 'load' event
143 fires. This also allows you to use PPB_Messaging.PostMessage() (in C) or
144 pp::Instance.PostMessage() (in C++) from within the initialization code in
145 your module.
146 -->
147 <div id="listener">
148 <script type="text/javascript">
149 var listener = document.getElementById('listener');
150 listener.addEventListener('load', moduleDidLoad, true);
151 listener.addEventListener('message', handleMessage, true);
152 </script>
153
154 <embed id="{{{app_name}}}"
155 width=0 height=0
156 src="{{{app_name}}}.nmf"
157 type="application/x-pnacl" />
158 </div>
159 </p>
160 <h2>Status <code id="statusField">NO-STATUS</code></h2>
161 </body>
162 </html>
163 """.write_to_file(file)
164
165 ### generate pnacl_js.js in a folder named 'js'
166 dir = "{outname}/js/"
167 if not dir.file_exists then dir.mkdir
168 file = "{dir}/pnacl_js.js"
169 if not file.file_exists then """
170 // This file was generated by Nit, any modification will be lost.
171
172 {{{app_name}}}Module = null; // Global application object.
173 statusText = 'NO-STATUS';
174
175 // Indicate load success.
176 function moduleDidLoad() {
177 {{{app_name}}}Module = document.getElementById('{{{app_name}}}');
178 updateStatus('SUCCESS');
179 // Send a message to the Native Client module like that
180 //{{{app_name}}}Module.postMessage('Hello World');
181 }
182
183 // The 'message' event handler. This handler is fired when the NaCl module
184 // posts a message to the browser by calling PPB_Messaging.PostMessage()
185 // (in C) or pp::Instance.PostMessage() (in C++). This implementation
186 // simply displays the content of the message in an alert panel.
187 function handleMessage(message_event) {
188 console.log(message_event.data);
189 }
190
191 // If the page loads before the Native Client module loads, then set the
192 // status message indicating that the module is still loading. Otherwise,
193 // do not change the status message.
194 function pageDidLoad() {
195 if ({{{app_name}}}Module == null) {
196 updateStatus('LOADING...');
197 } else {
198 // It's possible that the Native Client module onload event fired
199 // before the page's onload event. In this case, the status message
200 // will reflect 'SUCCESS', but won't be displayed. This call will
201 // display the current message.
202 updateStatus();
203 }
204 }
205
206 // Set the global status message. If the element with id 'statusField'
207 // exists, then set its HTML to the status message as well.
208 // opt_message The message test. If this is null or undefined, then
209 // attempt to set the element with id 'statusField' to the value of
210 // |statusText|.
211 function updateStatus(opt_message) {
212 if (opt_message)
213 statusText = opt_message;
214 var statusField = document.getElementById('statusField');
215 if (statusField) {
216 statusField.innerHTML = statusText;
217 }
218 }
219 """.write_to_file(file)
220
221 ### generate the manifest file : app_name.nmf
222 # used to point the HTML to the Native Client module
223 # and optionally provide additional commands to the PNaCl translator in Chrome
224 file = "{outname}/{app_name}.nmf"
225 """
226 {
227 "program": {
228 "portable": {
229 "pnacl-translate": {
230 "url": "{{{app_name}}}.pexe"
231 }
232 }
233 }
234 }
235 """.write_to_file(file)
236 end
237
238 redef fun write_makefile(compiler, compile_dir, cfiles)
239 do
240 # Do nothing, already done in `write_files`
241 end
242
243 redef fun compile_c_code(compiler, compile_dir)
244 do
245 # Generate the pexe
246 toolcontext.exec_and_check(["make", "-C", pnacl_project_root], "PNaCl project error")
247 end
248 end