1 .. _tutorial2: 2 3 ###################################### 4 C++ Tutorial: Getting Started (Part 2) 5 ###################################### 6 7 .. contents:: 8 :local: 9 :backlinks: none 10 :depth: 2 11 12 Overview 13 ======== 14 15 This tutorial shows how to convert the finished PNaCl web application from 16 :doc:`Part 1 <tutorial-part1>` to use the Native Client SDK build system and 17 common JavaScript files. It also demonstrates some techniques to make your web 18 application `Content Security Policy (CSP)-compliant 19 </apps/contentSecurityPolicy>`_, which is necessary for `Chrome Apps 20 </apps/about_apps>`_. 21 22 Using the Native Client SDK build system makes it easy to build with all of the 23 SDK toolchains, and switch between the Debug and Release configurations. It 24 also simplifies the makefiles for your project, as we'll see in the next 25 section. Finally, it adds some useful commands for :ref:`running 26 <running_the_sdk_examples>` and :ref:`debugging <debugging_the_sdk_examples>` 27 your application. 28 29 The finished code for this example can be found in the 30 ``pepper_$(VERSION)/getting_started/part2`` directory in the Native Client SDK 31 download. 32 33 Using the Native Client SDK build system 34 ======================================== 35 36 This section describes how to use the SDK build system. To do so, we'll make 37 changes in the makefile. Because the makefile in part1 and part2 are so 38 different, it is easier to start from scratch. Here is the contents of the new 39 makefile. The following sections will describe it in more detail. 40 41 Simplifying the Makefile 42 ------------------------ 43 44 The makefile from part1 only supports one toolchain (PNaCl) and one 45 configuration (Release). It also only supports one source file. It's relatively 46 simple, but if we want to add support for multiple toolchains, configurations, 47 source files, or build steps, it would grow increasingly complex. The SDK build 48 system uses a set of variables and macros to make this possible, without 49 significantly increasing the complexity of the makefile. 50 51 Here is the new makefile, supporting three toolchains (PNaCl, Newlib NaCl, 52 Glibc NaCl) and two configurations (Debug, Release). 53 54 .. naclcode:: 55 56 VALID_TOOLCHAINS := pnacl newlib glibc 57 58 NACL_SDK_ROOT ?= $(abspath $(CURDIR)/../..) 59 include $(NACL_SDK_ROOT)/tools/common.mk 60 61 TARGET = part2 62 LIBS = ppapi_cpp ppapi 63 64 CFLAGS = -Wall 65 SOURCES = hello_tutorial.cc 66 67 # Build rules generated by macros from common.mk: 68 69 $(foreach src,$(SOURCES),$(eval $(call COMPILE_RULE,$(src),$(CFLAGS)))) 70 71 # The PNaCl workflow uses both an unstripped and finalized/stripped binary. 72 # On NaCl, only produce a stripped binary for Release configs (not Debug). 73 ifneq (,$(or $(findstring pnacl,$(TOOLCHAIN)),$(findstring Release,$(CONFIG)))) 74 $(eval $(call LINK_RULE,$(TARGET)_unstripped,$(SOURCES),$(LIBS),$(DEPS))) 75 $(eval $(call STRIP_RULE,$(TARGET),$(TARGET)_unstripped)) 76 else 77 $(eval $(call LINK_RULE,$(TARGET),$(SOURCES),$(LIBS),$(DEPS))) 78 endif 79 80 $(eval $(call NMF_RULE,$(TARGET),)) 81 82 Choosing valid toolchains, and including common.mk 83 -------------------------------------------------- 84 85 The makefile begins by specifying the toolchains that are valid for this 86 project. The Native Client SDK build system supports multi-toolchain projects 87 for its examples and libraries, but generally you will choose one toolchain 88 when you begin your project and never change it. Please see the 89 :ref:`Toolchains section of the Native Client overview <toolchains>` for more 90 information. 91 92 For this example, we support the ``pnacl``, ``newlib`` and ``glibc`` toolchains. 93 94 .. naclcode:: 95 96 VALID_TOOLCHAINS := pnacl newlib glibc 97 98 Next, as a convenience, we specify where to find ``NACL_SDK_ROOT``. Because 99 this example is located in ``pepper_$(VERSION)/getting_started/part2``, the 100 root of the SDK is two directories up. 101 102 .. naclcode:: 103 104 NACL_SDK_ROOT ?= $(abspath $(CURDIR)/../..) 105 106 .. Note:: 107 :class: note 108 109 In your own projects, you can use the absolute path to your installed SDK 110 here. You can also override this default by setting the ``NACL_SDK_ROOT`` 111 environment variable. See :ref:`Step 5 of Part 1 of this tutorial 112 <tutorial_step_5>` for more details. 113 114 Next, we include the file ``tools/common.mk``. This file provides the 115 functionality for the Native Client SDK build system, including new build rules 116 to compile and link a project, which we'll use below. 117 118 .. naclcode:: 119 120 include $(NACL_SDK_ROOT)/tools/common.mk 121 122 Configuring your project 123 ------------------------ 124 125 After including ``tools/common.mk``, we configure the project by specifying its 126 name, the sources and libraries it uses: 127 128 .. naclcode:: 129 130 TARGET = part2 131 LIBS = ppapi_cpp ppapi 132 133 CFLAGS = -Wall 134 SOURCES = hello_tutorial.cc 135 136 These variable names are not required and not used by the SDK build system; 137 they are only used in the rules described below. By convention, all SDK 138 makefiles use the following variables: 139 140 TARGET 141 The name of the project to build. This variable determines the name of the 142 library or executable that will be generated. In the above example, we call 143 the target ``part2``, which will generate an executable called 144 ``part2.pexe`` for PNaCl. For NaCl toolchains, the executable's file name 145 will be given a suffix for its architecture. For example, the ARM executable 146 is called ``part2_arm.nexe``. 147 148 LIBS 149 A list of libraries that this executable needs to link against. The library 150 search path is already set up to only look in the directory for the current 151 toolchain and architecture. In this example, we link against ``ppapi_cpp`` 152 and ``ppapi``. ``ppapi_cpp`` is needed to use the `Pepper C++ interface 153 </native-client/pepper_stable/cpp/>`_. ``ppapi`` is needed for communicating 154 with the browser. 155 156 CFLAGS 157 A list of extra flags to pass to the compiler. In this example, we pass 158 ``-Wall``, which turns on all warnings. 159 160 LDFLAGS 161 A list of additional flags to pass to the linker. This example does not need 162 any special linker flags, so this variable is omitted. 163 164 SOURCES 165 A list of C or C++ sources to compile, separated by spaces. If you have a 166 long list of sources, it may be easier to read if you put each file on its 167 own line, and use ``\`` as a line-continuation character. Here's an example: 168 169 .. naclcode:: 170 171 SOURCES = foo.cc \ 172 bar.cc \ 173 baz.cc \ 174 quux.cc 175 176 Build macros 177 ------------ 178 179 For many projects, the following build macros do not need to be changed; they 180 will use the variables we've defined above. 181 182 .. naclcode:: 183 184 $(foreach src,$(SOURCES),$(eval $(call COMPILE_RULE,$(src),$(CFLAGS)))) 185 186 ifneq (,$(or $(findstring pnacl,$(TOOLCHAIN)),$(findstring Release,$(CONFIG)))) 187 $(eval $(call LINK_RULE,$(TARGET)_unstripped,$(SOURCES),$(LIBS),$(DEPS))) 188 $(eval $(call STRIP_RULE,$(TARGET),$(TARGET)_unstripped)) 189 else 190 $(eval $(call LINK_RULE,$(TARGET),$(SOURCES),$(LIBS),$(DEPS))) 191 endif 192 193 $(eval $(call NMF_RULE,$(TARGET),)) 194 195 The first line defines rules to compile each source in ``SOURCES``, using the 196 flags in ``CFLAGS``: 197 198 .. naclcode:: 199 200 $(foreach src,$(SOURCES),$(eval $(call COMPILE_RULE,$(src),$(CFLAGS)))) 201 202 The next six lines define rules to link the object files into one or more 203 executables. When ``TOOLCHAIN`` is ``pnacl``, there is only one executable 204 generated: in the example above, ``part2.pexe``. When using a NaCl toolchain, 205 there will be three executables generated, one for each architecture: in the 206 example above, ``part2_arm.nexe``, ``part2_x86_32.nexe`` and 207 ``part2_x86_64.nexe``. 208 209 When ``CONFIG`` is ``Release``, each executable is also stripped to remove 210 debug information and reduce the file size. Otherwise, when the ``TOOLCHAIN`` 211 is ``pnacl``, the workflow involves creating an unstripped binary for debugging 212 and then finalizing it and stripping it for publishing. 213 214 .. naclcode:: 215 216 ifneq (,$(or $(findstring pnacl,$(TOOLCHAIN)),$(findstring Release,$(CONFIG)))) 217 $(eval $(call LINK_RULE,$(TARGET)_unstripped,$(SOURCES),$(LIBS),$(DEPS))) 218 $(eval $(call STRIP_RULE,$(TARGET),$(TARGET)_unstripped)) 219 else 220 $(eval $(call LINK_RULE,$(TARGET),$(SOURCES),$(LIBS),$(DEPS))) 221 endif 222 223 Finally, the NMF rule generates a NaCl manifest file (``.nmf``) that references 224 each executable generated in the previous step: 225 226 .. naclcode:: 227 228 $(eval $(call NMF_RULE,$(TARGET),)) 229 230 Making index.html work for Chrome Apps 231 ====================================== 232 233 This section describes the changes necessary to make the HTML and JavaScript in 234 part1 CSP-compliant. This is required if you want to build a `Chrome App 235 </apps/about_apps>`_, but is not necessary if you want to use PNaCl on the open 236 web. 237 238 CSP rules 239 --------- 240 241 `Chrome Apps CSP </apps/contentSecurityPolicy#what>`_ restricts you from doing 242 the following: 243 244 * You cant use inline scripting in your Chrome App pages. The restriction 245 bans both ``<script>`` blocks and event handlers (``<button onclick="...">``). 246 * You cant reference any external resources in any of your app files (except 247 for video and audio resources). You cant embed external resources in an 248 iframe. 249 * You cant use string-to-JavaScript methods like ``eval()`` and ``new 250 Function()``. 251 252 Making index.html CSP-compliant 253 ------------------------------- 254 255 To make our application CSP-compliant, we have to remove inline scripting. As 256 described above, we can't use inline ``<script>`` blocks or event handlers. This 257 is easy to do---we'll just reference some new files from our script tag, and 258 remove all of our inlined scripts: 259 260 .. naclcode:: 261 262 <head> 263 ... 264 <script type="text/javascript" src="common.js"></script> 265 <script type="text/javascript" src="example.js"></script> 266 </head> 267 268 ``common.js`` has shared code used by all SDK examples, and is described 269 later in this document. ``example.js`` is a script that has code specific to 270 this example. 271 272 We also need to remove the inline event handler on the body tag: 273 274 .. naclcode:: 275 276 <body onload="pageDidLoad()"> 277 ... 278 279 This logic is now handled by ``common.js``. 280 281 Making index.html support different toolchains and configurations 282 ----------------------------------------------------------------- 283 284 Finally, there are a few changes to ``index.html`` that are not necessary for 285 CSP-compliance, but help make the SDK examples more generic. 286 287 First, we add some `data attributes 288 <https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_data_attributes>`_ 289 to the body element to specify the name, supported toolchains, supported 290 configurations, and path to the ``.nmf`` file: 291 292 .. naclcode:: 293 294 <body data-name="part2" 295 data-tools="newlib glibc pnacl" 296 data-configs="Debug Release" 297 data-path="{tc}/{config}"> 298 ... 299 300 ``common.js`` will read these data attributes to allow you to load the same 301 example with different toolchains by changing the URL's `query string 302 <http://en.wikipedia.org/wiki/Query_string>`_. For example, you can load the 303 glibc Debug version of this example by navigating to 304 ``index.html?tc=glibc&config=Debug``. 305 306 Next, we remove the ``embed`` element that is described in HTML. This will be 307 automatically added for us by ``common.js``, based on the current 308 toolchain/configuration combination: 309 310 .. naclcode:: 311 312 <!-- 313 Just as in part1, the <embed> element will be wrapped inside the <div> 314 element with the id "listener". In part1, the embed was specified in HTML, 315 here the common.js module creates a new <embed> element and adds it to the 316 <div> for us. 317 --> 318 <div id="listener"></div> 319 320 Sharing common code with common.js 321 ================================== 322 323 ``common.js`` contains JavaScript code that each example uses to create a 324 NaCl module, handle messages from that module and other common tasks like 325 displaying the module load status and logging messages. Explaining all of 326 ``common.js`` is outside the scope of this document, but please look at the 327 documentation in that file for more information. 328 329 Loading the page and creating the module 330 ---------------------------------------- 331 332 Since we've added ``<script>`` tags for ``common.js`` and ``example.js`` to the 333 ``head`` element, they will be loaded and executed before the rest of the 334 document has been parsed. As a result, we have to wait for the page to finish 335 loading before we try to create the embed element and add it to the page. 336 337 We can do that by calling ``addEventListener`` and listening for the 338 ``DOMContentLoaded`` event: 339 340 .. naclcode:: 341 342 // Listen for the DOM content to be loaded. This event is fired when parsing of 343 // the page's document has finished. 344 document.addEventListener('DOMContentLoaded', function() { 345 ... 346 }); 347 348 Inside this function, we parse the URL query string, and compare that to the 349 data attributes: 350 351 .. naclcode:: 352 353 // From https://developer.mozilla.org/en-US/docs/DOM/window.location 354 var searchVars = {}; 355 if (window.location.search.length > 1) { 356 var pairs = window.location.search.substr(1).split('&'); 357 for (var key_ix = 0; key_ix < pairs.length; key_ix++) { 358 var keyValue = pairs[key_ix].split('='); 359 searchVars[unescape(keyValue[0])] = 360 keyValue.length > 1 ? unescape(keyValue[1]) : ''; 361 } 362 } 363 364 ... 365 366 var toolchains = body.dataset.tools.split(' '); 367 var configs = body.dataset.configs.split(' '); 368 369 ... 370 371 var tc = toolchains.indexOf(searchVars.tc) !== -1 ? 372 searchVars.tc : toolchains[0]; 373 374 // If the config value is included in the search vars, use that. 375 // Otherwise default to Release if it is valid, or the first value if 376 // Release is not valid. 377 if (configs.indexOf(searchVars.config) !== -1) 378 var config = searchVars.config; 379 else if (configs.indexOf('Release') !== -1) 380 var config = 'Release'; 381 else 382 var config = configs[0]; 383 384 Then ``domContentLoaded`` is called, which performs some checks to see if the 385 browser supports Native Client, then creates the NaCl module. 386 387 .. naclcode:: 388 389 function domContentLoaded(name, tool, path, width, height, attrs) { 390 updateStatus('Page loaded.'); 391 if (!browserSupportsNaCl(tool)) { 392 updateStatus( 393 'Browser does not support NaCl (' + tool + '), or NaCl is disabled'); 394 } else if (common.naclModule == null) { 395 updateStatus('Creating embed: ' + tool); 396 397 // We use a non-zero sized embed to give Chrome space to place the bad 398 // plug-in graphic, if there is a problem. 399 width = typeof width !== 'undefined' ? width : 200; 400 height = typeof height !== 'undefined' ? height : 200; 401 attachDefaultListeners(); 402 createNaClModule(name, tool, path, width, height, attrs); 403 } else { 404 // It's possible that the Native Client module onload event fired 405 // before the page's onload event. In this case, the status message 406 // will reflect 'SUCCESS', but won't be displayed. This call will 407 // display the current message. 408 updateStatus('Waiting.'); 409 } 410 } 411 412 ``attachDefaultListeners`` is added before the creation of the module, to make 413 sure that no messages are lost. Note that ``window.attachListeners`` is also 414 called; this is the way that ``common.js`` allows each example to configure 415 itself differently. If an example defines the ``attachListeners`` function, it 416 will be called by ``common.js``. 417 418 .. naclcode:: 419 420 function attachDefaultListeners() { 421 var listenerDiv = document.getElementById('listener'); 422 listenerDiv.addEventListener('load', moduleDidLoad, true); 423 listenerDiv.addEventListener('message', handleMessage, true); 424 listenerDiv.addEventListener('crash', handleCrash, true); 425 if (typeof window.attachListeners !== 'undefined') { 426 window.attachListeners(); 427 } 428 } 429 430 Finally, ``createNaClModule`` actually creates the ``embed``, and appends it as 431 a child of the element with id ``listener``: 432 433 .. naclcode:: 434 435 function createNaClModule(name, tool, path, width, height, attrs) { 436 var moduleEl = document.createElement('embed'); 437 moduleEl.setAttribute('name', 'nacl_module'); 438 moduleEl.setAttribute('id', 'nacl_module'); 439 moduleEl.setAttribute('width', width); 440 moduleEl.setAttribute('height', height); 441 moduleEl.setAttribute('path', path); 442 moduleEl.setAttribute('src', path + '/' + name + '.nmf'); 443 444 ... 445 446 var mimetype = mimeTypeForTool(tool); 447 moduleEl.setAttribute('type', mimetype); 448 449 var listenerDiv = document.getElementById('listener'); 450 listenerDiv.appendChild(moduleEl); 451 ... 452 } 453 454 When the module finishes loading, it will dispatch a ``load`` event, and the 455 event listener function that was registered above (``moduleDidLoad``) will be 456 called. Note that ``common.js`` allows each example to define a 457 ``window.moduleDidLoad`` function, that will be called here as well. 458 459 .. naclcode:: 460 461 function moduleDidLoad() { 462 common.naclModule = document.getElementById('nacl_module'); 463 updateStatus('RUNNING'); 464 465 if (typeof window.moduleDidLoad !== 'undefined') { 466 window.moduleDidLoad(); 467 } 468 } 469 470 Example-specific behavior with example.js 471 ========================================= 472 473 As described in the previous section, ``common.js`` will call certain functions 474 during the module loading process. This example only needs to respond to two: 475 ``moduleDidLoad`` and ``handleMessage``. 476 477 .. naclcode:: 478 479 // This function is called by common.js when the NaCl module is 480 // loaded. 481 function moduleDidLoad() { 482 // Once we load, hide the plugin. In this example, we don't display anything 483 // in the plugin, so it is fine to hide it. 484 common.hideModule(); 485 486 // After the NaCl module has loaded, common.naclModule is a reference to the 487 // NaCl module's <embed> element. 488 // 489 // postMessage sends a message to it. 490 common.naclModule.postMessage('hello'); 491 } 492 493 // This function is called by common.js when a message is received from the 494 // NaCl module. 495 function handleMessage(message) { 496 var logEl = document.getElementById('log'); 497 logEl.textContent += message.data; 498 } 499