Home | History | Annotate | Download | only in tutorial
      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