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