summaryrefslogtreecommitdiffstats
path: root/native_client_sdk/src
diff options
context:
space:
mode:
authorbradnelson@google.com <bradnelson@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-21 01:09:05 +0000
committerbradnelson@google.com <bradnelson@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-21 01:09:05 +0000
commit07ffef1a601366619755a4f15cf4aa1b2e9ecfa7 (patch)
treeb88600e931428dbe4f0d28ad1d94732f398ede4c /native_client_sdk/src
parent0f9067badc4ebce72372d06f1e75550cfb8af945 (diff)
downloadchromium_src-07ffef1a601366619755a4f15cf4aa1b2e9ecfa7.zip
chromium_src-07ffef1a601366619755a4f15cf4aa1b2e9ecfa7.tar.gz
chromium_src-07ffef1a601366619755a4f15cf4aa1b2e9ecfa7.tar.bz2
Adding in (mostly) verbatim landing of nacl sdk.
Dropping large binaries. Redo of http://crrev.com/110822 checkdeps + checkperms have now beep updated in preparation. BUG=None TEST=None R=noelallen@google.com TBR git-svn-id: svn://svn.chromium.org/chrome/trunk/src@110872 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk/src')
-rw-r--r--native_client_sdk/src/.gitignore33
-rw-r--r--native_client_sdk/src/AUTHORS7
-rw-r--r--native_client_sdk/src/COPYING28
-rw-r--r--native_client_sdk/src/DEPS124
-rw-r--r--native_client_sdk/src/LICENSE28
-rw-r--r--native_client_sdk/src/NOTICE2434
-rw-r--r--native_client_sdk/src/OWNERS4
-rwxr-xr-xnative_client_sdk/src/PRESUBMIT.py23
-rw-r--r--native_client_sdk/src/README24
-rw-r--r--native_client_sdk/src/build_tools/MkLink/MkLink.c412
-rw-r--r--native_client_sdk/src/build_tools/MkLink/MkLink.sln26
-rw-r--r--native_client_sdk/src/build_tools/MkLink/MkLink.vcproj484
-rw-r--r--native_client_sdk/src/build_tools/MkLink/Release Unicode/MkLink.dllbin0 -> 5632 bytes
-rw-r--r--native_client_sdk/src/build_tools/README_NSIS.txt59
-rw-r--r--native_client_sdk/src/build_tools/__init__.py10
-rwxr-xr-xnative_client_sdk/src/build_tools/apply_patch.py534
-rw-r--r--native_client_sdk/src/build_tools/build.scons289
-rw-r--r--native_client_sdk/src/build_tools/build_utils.py307
-rwxr-xr-xnative_client_sdk/src/build_tools/buildbot_run.py78
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/build.scons37
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.cc16
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.gyp170
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.h25
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.rules6
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.sln110
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub64.rules6
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub_test.cc13
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub_test.rules6
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub_test64.rules6
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/event_common.cc68
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/linux/debug_stub_linux.cc15
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/osx/debug_stub_osx.cc15
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/platform_common.cc40
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/posix/debug_stub_posix.cc12
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/posix/mutex_impl.cc58
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/posix/platform_impl.cc106
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/posix/thread_impl.cc123
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/run_debug_stub_test.rules6
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/transport_common.cc295
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/win/debug_stub_win.cc47
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/win/mutex_impl.cc89
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/win/platform_impl.cc105
-rw-r--r--native_client_sdk/src/build_tools/debug_server/debug_stub/win/thread_impl.cc438
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/abi.cc209
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/abi.h114
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/abi_test.cc140
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/build.scons57
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp.gyp162
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp.rules6
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp64.rules6
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp_test.rules6
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp_test64.rules6
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/host.cc524
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/host.h149
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/host_test.cc132
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/packet.cc392
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/packet.h122
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/packet_test.cc106
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/session.cc316
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/session.h86
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/session_mock.cc192
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/session_mock.h78
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/session_test.cc285
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/target.cc636
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/target.h159
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/target_test.cc61
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/test.cc152
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/test.h37
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/util.cc125
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/util.h41
-rw-r--r--native_client_sdk/src/build_tools/debug_server/gdb_rsp/util_test.cc74
-rw-r--r--native_client_sdk/src/build_tools/debug_server/nacl_debug.cc322
-rw-r--r--native_client_sdk/src/build_tools/debug_server/nacl_debug.h113
-rw-r--r--native_client_sdk/src/build_tools/debug_server/port/event.h33
-rw-r--r--native_client_sdk/src/build_tools/debug_server/port/mutex.h61
-rw-r--r--native_client_sdk/src/build_tools/debug_server/port/platform.h52
-rw-r--r--native_client_sdk/src/build_tools/debug_server/port/port.gyp54
-rw-r--r--native_client_sdk/src/build_tools/debug_server/port/std_types.h44
-rw-r--r--native_client_sdk/src/build_tools/debug_server/port/thread.h64
-rw-r--r--native_client_sdk/src/build_tools/debug_server/port/transport.h47
-rwxr-xr-xnative_client_sdk/src/build_tools/generate_installers.py160
-rwxr-xr-xnative_client_sdk/src/build_tools/generate_windows_installer.py158
-rwxr-xr-xnative_client_sdk/src/build_tools/html_checker.py96
-rwxr-xr-xnative_client_sdk/src/build_tools/install_boost/install_boost.py110
-rw-r--r--native_client_sdk/src/build_tools/install_gtest/install_gtest.py239
-rw-r--r--native_client_sdk/src/build_tools/install_gtest/nacl-gmock-1.5.0.patch29
-rw-r--r--native_client_sdk/src/build_tools/install_gtest/nacl-gtest-1.5.0.patch226
-rw-r--r--native_client_sdk/src/build_tools/install_nsis.py155
-rwxr-xr-xnative_client_sdk/src/build_tools/install_third_party.py78
-rwxr-xr-xnative_client_sdk/src/build_tools/installer_contents.py181
-rw-r--r--native_client_sdk/src/build_tools/json/naclsdk_manifest.json193
-rwxr-xr-xnative_client_sdk/src/build_tools/make_nacl_tools.py213
-rw-r--r--native_client_sdk/src/build_tools/make_nsis_installer.py58
-rw-r--r--native_client_sdk/src/build_tools/make_sdk_installer.nsi103
-rwxr-xr-xnative_client_sdk/src/build_tools/make_sdk_tools.py135
-rw-r--r--native_client_sdk/src/build_tools/nacl_sdk_scons/__init__.py10
-rw-r--r--native_client_sdk/src/build_tools/nacl_sdk_scons/make_nacl_env.py205
-rw-r--r--native_client_sdk/src/build_tools/nacl_sdk_scons/nacl_utils.py495
-rwxr-xr-xnative_client_sdk/src/build_tools/nacl_sdk_scons/nacl_utils_test.py111
-rw-r--r--native_client_sdk/src/build_tools/nacl_sdk_scons/nmf_test.py196
-rw-r--r--native_client_sdk/src/build_tools/nacl_sdk_scons/site_tools/__init__.py10
-rwxr-xr-xnative_client_sdk/src/build_tools/nacl_sdk_scons/site_tools/create_nmf.py328
-rw-r--r--native_client_sdk/src/build_tools/nacl_sdk_scons/site_tools/nacl_tools.py487
-rwxr-xr-xnative_client_sdk/src/build_tools/naclsdk19
-rwxr-xr-xnative_client_sdk/src/build_tools/naclsdk.bat24
-rw-r--r--native_client_sdk/src/build_tools/nsis_script.py189
-rw-r--r--native_client_sdk/src/build_tools/path_set.py156
-rw-r--r--native_client_sdk/src/build_tools/sdk_tools/__init__.py7
-rwxr-xr-xnative_client_sdk/src/build_tools/sdk_tools/sdk_update.py1039
-rwxr-xr-xnative_client_sdk/src/build_tools/sdk_tools/set_nacl_env.py491
-rwxr-xr-xnative_client_sdk/src/build_tools/sdk_tools/update_manifest.py391
-rw-r--r--native_client_sdk/src/build_tools/tar_archive.py156
-rw-r--r--native_client_sdk/src/build_tools/tests/__init__.py7
-rw-r--r--native_client_sdk/src/build_tools/tests/apply_patch_test.py249
-rwxr-xr-xnative_client_sdk/src/build_tools/tests/build_utils_test.py104
-rw-r--r--native_client_sdk/src/build_tools/tests/fake_gsutil.bat3
-rwxr-xr-xnative_client_sdk/src/build_tools/tests/fake_gsutil.py40
-rw-r--r--native_client_sdk/src/build_tools/tests/install_nsis_test.py103
-rwxr-xr-xnative_client_sdk/src/build_tools/tests/installer_contents_test.py66
-rwxr-xr-xnative_client_sdk/src/build_tools/tests/installer_test.py480
-rw-r--r--native_client_sdk/src/build_tools/tests/naclsdk_manifest_test.json89
-rw-r--r--native_client_sdk/src/build_tools/tests/nsis_script_test.py147
-rw-r--r--native_client_sdk/src/build_tools/tests/nsis_test_archive/test_dir/test_dir_file1.txt3
-rw-r--r--native_client_sdk/src/build_tools/tests/nsis_test_archive/test_dir/test_dir_file2.txt3
-rw-r--r--native_client_sdk/src/build_tools/tests/nsis_test_archive/test_file.txt1
-rw-r--r--native_client_sdk/src/build_tools/tests/path_set_test.py174
-rw-r--r--native_client_sdk/src/build_tools/tests/sdk_test_cache/naclsdk_manifest.json70
-rwxr-xr-xnative_client_sdk/src/build_tools/tests/sdk_update_test.py185
-rwxr-xr-xnative_client_sdk/src/build_tools/tests/set_nacl_env_test.py129
-rw-r--r--native_client_sdk/src/build_tools/tests/set_nacl_env_test_archive/Makefile20
-rw-r--r--native_client_sdk/src/build_tools/tests/set_nacl_env_test_archive/hello_world_c.c287
-rw-r--r--native_client_sdk/src/build_tools/tests/tar_archive_test.py200
-rw-r--r--native_client_sdk/src/build_tools/tests/test_links.tgzbin0 -> 444 bytes
-rw-r--r--native_client_sdk/src/build_tools/tests/test_links.tgz.manifest11
-rw-r--r--native_client_sdk/src/build_tools/tests/test_sdk_section.nsh8
-rwxr-xr-xnative_client_sdk/src/build_tools/tests/update_manifest_test.py341
-rw-r--r--native_client_sdk/src/codereview.settings9
-rw-r--r--native_client_sdk/src/documentation/Doxyfile1663
-rw-r--r--native_client_sdk/src/documentation/build.scons28
-rwxr-xr-xnative_client_sdk/src/documentation/check.sh26
-rw-r--r--native_client_sdk/src/documentation/footer.dox22
-rw-r--r--native_client_sdk/src/documentation/header.dox14
-rw-r--r--native_client_sdk/src/documentation/images-dox/README.txt9
-rw-r--r--native_client_sdk/src/documentation/index.dox46
-rw-r--r--native_client_sdk/src/documentation/modules.dox22
-rw-r--r--native_client_sdk/src/documentation/stylesheet-dox.css478
-rw-r--r--native_client_sdk/src/documentation/stylesheet.css101
-rw-r--r--native_client_sdk/src/examples/build.scons82
-rw-r--r--native_client_sdk/src/examples/common/check_browser.js178
-rw-r--r--native_client_sdk/src/examples/favicon.icobin0 -> 1150 bytes
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/bind.js22
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/build.scons66
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/callback.h92
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/cube.cc268
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/cube.h98
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/dragger.js134
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/fullscreen_tumbler.html57
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/opengl_context.cc84
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/opengl_context.h94
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/opengl_context_ptrs.h22
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/scripting_bridge.cc96
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/scripting_bridge.h53
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/shader_util.cc97
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/shader_util.h30
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/trackball.js296
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/transforms.cc117
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/transforms.h45
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/tumbler.cc202
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/tumbler.h83
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/tumbler.js133
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/tumbler_module.cc45
-rw-r--r--native_client_sdk/src/examples/fullscreen_tumbler/vector3.js91
-rw-r--r--native_client_sdk/src/examples/geturl/build.scons41
-rw-r--r--native_client_sdk/src/examples/geturl/geturl.cc96
-rw-r--r--native_client_sdk/src/examples/geturl/geturl.html125
-rw-r--r--native_client_sdk/src/examples/geturl/geturl_handler.cc137
-rw-r--r--native_client_sdk/src/examples/geturl/geturl_handler.h81
-rw-r--r--native_client_sdk/src/examples/geturl/geturl_success.html20
-rw-r--r--native_client_sdk/src/examples/hello_world/build.scons59
-rw-r--r--native_client_sdk/src/examples/hello_world/hello_world.cc130
-rw-r--r--native_client_sdk/src/examples/hello_world/hello_world.html126
-rw-r--r--native_client_sdk/src/examples/hello_world/helper_functions.cc22
-rw-r--r--native_client_sdk/src/examples/hello_world/helper_functions.h34
-rw-r--r--native_client_sdk/src/examples/hello_world/test_helper_functions.cc51
-rw-r--r--native_client_sdk/src/examples/hello_world_c/build.scons40
-rw-r--r--native_client_sdk/src/examples/hello_world_c/hello_world_c.c287
-rw-r--r--native_client_sdk/src/examples/hello_world_c/hello_world_c.html126
-rw-r--r--native_client_sdk/src/examples/hello_world_c/hello_world_c_dbg.html126
-rw-r--r--native_client_sdk/src/examples/httpd.cmd8
-rwxr-xr-xnative_client_sdk/src/examples/httpd.py125
-rw-r--r--native_client_sdk/src/examples/index.html51
-rw-r--r--native_client_sdk/src/examples/index_staging.html113
-rw-r--r--native_client_sdk/src/examples/input_events/build.scons41
-rw-r--r--native_client_sdk/src/examples/input_events/input_events.cc251
-rw-r--r--native_client_sdk/src/examples/input_events/input_events.html58
-rw-r--r--native_client_sdk/src/examples/load_progress/build.scons51
-rw-r--r--native_client_sdk/src/examples/load_progress/load_progress.cc69
-rw-r--r--native_client_sdk/src/examples/load_progress/load_progress.html232
-rw-r--r--native_client_sdk/src/examples/mouselock/build.scons51
-rw-r--r--native_client_sdk/src/examples/mouselock/mouselock.cc329
-rw-r--r--native_client_sdk/src/examples/mouselock/mouselock.h102
-rw-r--r--native_client_sdk/src/examples/mouselock/mouselock.html108
-rw-r--r--native_client_sdk/src/examples/multithreaded_input_events/build.scons41
-rw-r--r--native_client_sdk/src/examples/multithreaded_input_events/custom_events.cc111
-rw-r--r--native_client_sdk/src/examples/multithreaded_input_events/custom_events.h133
-rw-r--r--native_client_sdk/src/examples/multithreaded_input_events/mt_input_events.cc323
-rw-r--r--native_client_sdk/src/examples/multithreaded_input_events/mt_input_events.html80
-rw-r--r--native_client_sdk/src/examples/multithreaded_input_events/shared_queue.h137
-rw-r--r--native_client_sdk/src/examples/multithreaded_input_events/thread_safe_ref_count.h63
-rw-r--r--native_client_sdk/src/examples/pi_generator/build.scons40
-rw-r--r--native_client_sdk/src/examples/pi_generator/pi_generator.cc240
-rw-r--r--native_client_sdk/src/examples/pi_generator/pi_generator.h117
-rw-r--r--native_client_sdk/src/examples/pi_generator/pi_generator.html83
-rw-r--r--native_client_sdk/src/examples/pi_generator/pi_generator_dbg.html83
-rw-r--r--native_client_sdk/src/examples/pi_generator/pi_generator_module.cc34
-rw-r--r--native_client_sdk/src/examples/pong/build.scons40
-rw-r--r--native_client_sdk/src/examples/pong/pong.cc414
-rw-r--r--native_client_sdk/src/examples/pong/pong.h92
-rw-r--r--native_client_sdk/src/examples/pong/pong.html74
-rw-r--r--native_client_sdk/src/examples/pong/pong_module.cc34
-rw-r--r--native_client_sdk/src/examples/pong/view.cc162
-rw-r--r--native_client_sdk/src/examples/pong/view.h75
-rwxr-xr-xnative_client_sdk/src/examples/scons39
-rwxr-xr-xnative_client_sdk/src/examples/scons.bat43
-rw-r--r--native_client_sdk/src/examples/sine_synth/build.scons40
-rw-r--r--native_client_sdk/src/examples/sine_synth/sine_synth.cc183
-rw-r--r--native_client_sdk/src/examples/sine_synth/sine_synth.html80
-rw-r--r--native_client_sdk/src/examples/tumbler/bind.js22
-rw-r--r--native_client_sdk/src/examples/tumbler/build.scons55
-rw-r--r--native_client_sdk/src/examples/tumbler/callback.h92
-rw-r--r--native_client_sdk/src/examples/tumbler/cube.cc267
-rw-r--r--native_client_sdk/src/examples/tumbler/cube.h97
-rw-r--r--native_client_sdk/src/examples/tumbler/dragger.js134
-rw-r--r--native_client_sdk/src/examples/tumbler/opengl_context.cc84
-rw-r--r--native_client_sdk/src/examples/tumbler/opengl_context.h93
-rw-r--r--native_client_sdk/src/examples/tumbler/opengl_context_ptrs.h22
-rw-r--r--native_client_sdk/src/examples/tumbler/scripting_bridge.cc95
-rw-r--r--native_client_sdk/src/examples/tumbler/scripting_bridge.h52
-rw-r--r--native_client_sdk/src/examples/tumbler/shader_util.cc95
-rw-r--r--native_client_sdk/src/examples/tumbler/shader_util.h29
-rw-r--r--native_client_sdk/src/examples/tumbler/trackball.js296
-rw-r--r--native_client_sdk/src/examples/tumbler/transforms.cc116
-rw-r--r--native_client_sdk/src/examples/tumbler/transforms.h45
-rw-r--r--native_client_sdk/src/examples/tumbler/tumbler.cc137
-rw-r--r--native_client_sdk/src/examples/tumbler/tumbler.h64
-rw-r--r--native_client_sdk/src/examples/tumbler/tumbler.html33
-rw-r--r--native_client_sdk/src/examples/tumbler/tumbler.js133
-rw-r--r--native_client_sdk/src/examples/tumbler/tumbler_module.cc45
-rw-r--r--native_client_sdk/src/examples/tumbler/vector3.js91
-rw-r--r--native_client_sdk/src/libraries/build.scons41
-rw-r--r--native_client_sdk/src/libraries/c_salt/test/gtest_event_listener.cc100
-rw-r--r--native_client_sdk/src/libraries/c_salt/test/gtest_event_listener.h51
-rw-r--r--native_client_sdk/src/libraries/c_salt/test/gtest_instance.cc46
-rw-r--r--native_client_sdk/src/libraries/c_salt/test/gtest_instance.h34
-rw-r--r--native_client_sdk/src/libraries/c_salt/test/gtest_module.cc28
-rw-r--r--native_client_sdk/src/libraries/c_salt/test/gtest_nacl_environment.cc19
-rw-r--r--native_client_sdk/src/libraries/c_salt/test/gtest_nacl_environment.h39
-rw-r--r--native_client_sdk/src/libraries/c_salt/test/gtest_runner.cc76
-rw-r--r--native_client_sdk/src/libraries/c_salt/test/gtest_runner.h62
-rw-r--r--native_client_sdk/src/libraries/c_salt/threading/condition_lock.h89
-rw-r--r--native_client_sdk/src/libraries/c_salt/threading/pthread_ext.h15
-rw-r--r--native_client_sdk/src/libraries/c_salt/threading/ref_count.h46
-rw-r--r--native_client_sdk/src/libraries/c_salt/threading/scoped_mutex_lock.h32
-rw-r--r--native_client_sdk/src/libraries/c_salt/threading/thread_condition.h76
-rw-r--r--native_client_sdk/src/libraries/gtest/build.scons112
-rwxr-xr-xnative_client_sdk/src/libraries/scons38
-rwxr-xr-xnative_client_sdk/src/libraries/scons.bat43
-rw-r--r--native_client_sdk/src/main.scons1065
-rw-r--r--native_client_sdk/src/project_templates/README45
-rw-r--r--native_client_sdk/src/project_templates/c/build.scons15
-rw-r--r--native_client_sdk/src/project_templates/c/project_file.c227
-rw-r--r--native_client_sdk/src/project_templates/cc/build.scons16
-rw-r--r--native_client_sdk/src/project_templates/cc/project_file.cc87
-rw-r--r--native_client_sdk/src/project_templates/html/project_file.html95
-rwxr-xr-xnative_client_sdk/src/project_templates/init_project.py502
-rwxr-xr-xnative_client_sdk/src/project_templates/init_project_test.py256
-rwxr-xr-xnative_client_sdk/src/project_templates/scons32
-rwxr-xr-xnative_client_sdk/src/project_templates/scons.bat31
-rw-r--r--native_client_sdk/src/project_templates/test.scons26
-rw-r--r--native_client_sdk/src/project_templates/vs/project_file.sln20
-rw-r--r--native_client_sdk/src/project_templates/vs/project_file.vcproj184
-rwxr-xr-xnative_client_sdk/src/scons37
-rwxr-xr-xnative_client_sdk/src/scons.bat28
283 files changed, 37511 insertions, 0 deletions
diff --git a/native_client_sdk/src/.gitignore b/native_client_sdk/src/.gitignore
new file mode 100644
index 0000000..05bda61
--- /dev/null
+++ b/native_client_sdk/src/.gitignore
@@ -0,0 +1,33 @@
+# This is a standard .gitignore file, used to exclude certain files
+# from a 'git status' command, or similar
+
+# Ignore these filename patterns
+*.cmd
+*.gz
+*.nexe
+*.nmf
+*.o
+*.pyc
+*.pyo
+
+# Ignore all hidden files
+.*
+
+# Ignore all directories than contain these names
+scons-out/
+third_party/
+toolchain/
+
+# Ignore these particular directories (relative to src directory)
+/build_tools/NSIS/
+/build_tools/packages/
+/build_tools/sdk_install_name.nsh
+/build_tools/sdk_section.nsh
+/build_tools/tests/apply_patch_test_archive/
+/build_tools/toolchain_archives/
+/chrome_binaries/
+/debugger/NativeClientVSX/
+/debugger/nacl-gdb_server/x64/
+/experimental/webgtt/tests/nacltest/*.cc
+/site_scons/
+/staging/
diff --git a/native_client_sdk/src/AUTHORS b/native_client_sdk/src/AUTHORS
new file mode 100644
index 0000000..24df0cd
--- /dev/null
+++ b/native_client_sdk/src/AUTHORS
@@ -0,0 +1,7 @@
+# Below is a list of people and organizations that have contributed source
+# code to the Native Client SDK project. Names should be added to the list
+# like this:
+#
+# Name/Organization <email address>
+
+Google Inc. native-client-discuss@google.com
diff --git a/native_client_sdk/src/COPYING b/native_client_sdk/src/COPYING
new file mode 100644
index 0000000..2ebf7c0
--- /dev/null
+++ b/native_client_sdk/src/COPYING
@@ -0,0 +1,28 @@
+Copyright 2010, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/native_client_sdk/src/DEPS b/native_client_sdk/src/DEPS
new file mode 100644
index 0000000..efe627b
--- /dev/null
+++ b/native_client_sdk/src/DEPS
@@ -0,0 +1,124 @@
+vars = {
+ "chrome_milestone": "17",
+ "chrome_rev": "109032",
+ "chromium_trunk": "http://src.chromium.org/svn/trunk",
+ # Get PPAPI directly from Chrome, not via the NaCl repo.
+ "gmock_trunk": "http://googlemock.googlecode.com/svn/trunk/",
+ "gmock_version": "382",
+ "gtest_trunk": "http://googletest.googlecode.com/svn/trunk/",
+ "gtest_version": "570",
+ "native_client_trunk": "http://src.chromium.org/native_client/trunk",
+ "native_client_version": "7141",
+ # Note: The following version should exactly match the toolchain version in
+ # the native_client DEPS file at version native_client_version
+ # TODO(mball) find some clever way to extract this from NaCl DEPS
+ "pnacl_toolchain_version": "7107",
+ "x86_toolchain_version": "7098",
+ # ARM is not supported, this number can stay pinned at 6645.
+ "arm_trusted_toolchain_version": "6645",
+ "pymox": "http://pymox.googlecode.com/svn/trunk",
+ "pymox_version": "61",
+}
+# When we have ARM and PNaCl support, we'll need to add toolchain support
+# for those too.
+
+deps = {
+ "nacl_deps":
+ File(Var("native_client_trunk") + "/src/native_client/DEPS@" +
+ Var("native_client_version")),
+ # Please keep these in alphabetical order, by path
+ "src/site_scons":
+ Var("native_client_trunk") + "/src/native_client/site_scons@" +
+ Var("native_client_version"),
+ "src/third_party/gmock":
+ Var("gmock_trunk") + "@" + Var("gmock_version"),
+ "src/third_party/gtest":
+ Var("gtest_trunk") + "@" + Var("gtest_version"),
+ "src/third_party/native_client/base":
+ Var("chromium_trunk") + "/src/base@" + Var("chrome_rev"),
+ "src/third_party/native_client/build":
+ Var("chromium_trunk") + "/src/build@" + Var("chrome_rev"),
+ "src/third_party/native_client/gpu":
+ Var("chromium_trunk") + "/src/gpu@" + Var("chrome_rev"),
+ "src/third_party/native_client/native_client":
+ Var("native_client_trunk") + "/src/native_client/@" +
+ Var("native_client_version"),
+ "src/third_party/native_client/ppapi":
+ Var("chromium_trunk") + "/src/ppapi@" + Var("chrome_rev"),
+ "src/third_party/pymox":
+ Var("pymox") + "@" + Var("pymox_version"),
+ "src/third_party/scons-2.0.1":
+ Var("native_client_trunk") + "/src/third_party/scons-2.0.1@" +
+ Var("native_client_version"),
+ "src/third_party/native_client/third_party/pylib":
+ Var("native_client_trunk") + "/src/third_party/pylib@" +
+ Var("native_client_version"),
+ "src/third_party/native_client/third_party/scons-2.0.1":
+ Var("native_client_trunk") + "/src/third_party/scons-2.0.1@" +
+ Var("native_client_version"),
+ "src/third_party/native_client/third_party/simplejson":
+ (Var("chromium_trunk") + "/tools/build/third_party/simplejson@" +
+ Var("chrome_rev")),
+ "src/third_party/native_client/tools/valgrind":
+ Var("chromium_trunk") + "/src/tools/valgrind@" + Var("chrome_rev"),
+}
+
+deps_os = {
+ # Please keep these in alphabetical order, by path
+ "win": {
+ "src/experimental/visual_studio_plugin/third_party/native_client/"
+ "src/shared/gio":
+ Var("native_client_trunk") + "/src/native_client/src/shared/gio@"
+ + Var("native_client_version"),
+ "src/experimental/visual_studio_plugin/third_party/native_client/"
+ "src/shared/platform":
+ Var("native_client_trunk") + "/src/native_client/src/shared/platform@"
+ + Var("native_client_version"),
+ "src/experimental/visual_studio_plugin/third_party/native_client/"
+ "src/trusted/debug_stub":
+ Var("native_client_trunk") + "/src/native_client/src/trusted/debug_stub@"
+ + Var("native_client_version"),
+ "src/experimental/visual_studio_plugin/third_party/native_client/"
+ "src/trusted/service_runtime/include":
+ Var("native_client_trunk")
+ + "/src/native_client/src/trusted/service_runtime/include@"
+ + Var("native_client_version"),
+ "src/experimental/visual_studio_plugin/third_party/native_client/"
+ "src/include":
+ Var("native_client_trunk") + "/src/native_client/src/include@"
+ + Var("native_client_version"),
+ "src/experimental/visual_studio_plugin/third_party/native_client/"
+ "src/trusted/port":
+ Var("native_client_trunk") + "/src/native_client/src/trusted/port@"
+ + Var("native_client_version"),
+ "src/experimental/visual_studio_plugin/third_party/native_client/"
+ "src/trusted/gdb_rsp":
+ Var("native_client_trunk") + "/src/native_client/src/trusted/gdb_rsp@"
+ + Var("native_client_version"),
+ "src/third_party/native_client/third_party/mingw-w64/mingw/bin":
+ From("nacl_deps", "third_party/mingw-w64/mingw/bin"),
+ },
+}
+
+hooks = [
+ {
+ "pattern": ".",
+ "action": [
+ "python",
+ "src/third_party/native_client/native_client/build/"
+ "download_toolchains.py",
+ "--x86-version", Var("x86_toolchain_version"),
+ "--pnacl-version", Var("pnacl_toolchain_version"),
+ "--arm-trusted-version", Var("arm_trusted_toolchain_version"),
+ "--toolchain-dir", "src/toolchain",
+ "--save-downloads-dir", "src/build_tools/toolchain_archives",
+ ]
+ },
+ {
+ "pattern": ".",
+ "action": [
+ "python",
+ "src/build_tools/install_third_party.py", "--all-toolchains",
+ ]
+ }
+]
diff --git a/native_client_sdk/src/LICENSE b/native_client_sdk/src/LICENSE
new file mode 100644
index 0000000..21b7ca0
--- /dev/null
+++ b/native_client_sdk/src/LICENSE
@@ -0,0 +1,28 @@
+Copyright 2011, The Native Client Authors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/native_client_sdk/src/NOTICE b/native_client_sdk/src/NOTICE
new file mode 100644
index 0000000..d43b2be
--- /dev/null
+++ b/native_client_sdk/src/NOTICE
@@ -0,0 +1,2434 @@
+======================================================================
+native_client_sdk is licensed as follows
+ (Cf. COPYING):
+native_client_sdk is licensed as follows
+ (Cf. LICENSE):
+======================================================================
+Copyright 2010, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+======================================================================
+toolchain/linux_x86 is licensed as follows
+ (Cf. toolchain/linux_x86/COPYING):
+toolchain/mac_x86 is licensed as follows
+ (Cf. toolchain/mac_x86/COPYING):
+toolchain/win_x86 is licensed as follows
+ (Cf. toolchain/win_x86/COPYING):
+toolchain/linux_x86_newlib is licensed as follows
+ (Cf. toolchain/linux_x86_newlib/COPYING):
+toolchain/mac_x86_newlib is licensed as follows
+ (Cf. toolchain/mac_x86_newlib/COPYING):
+toolchain/win_x86_newlib is licensed as follows
+ (Cf. toolchain/win_x86_newlib/COPYING):
+======================================================================
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
+
+
+======================================================================
+toolchain/linux_x86 is licensed as follows
+ (Cf. toolchain/linux_x86/COPYING.LIB):
+toolchain/mac_x86 is licensed as follows
+ (Cf. toolchain/mac_x86/COPYING.LIB):
+toolchain/win_x86 is licensed as follows
+ (Cf. toolchain/win_x86/COPYING.LIB):
+toolchain/linux_x86_newlib is licensed as follows
+ (Cf. toolchain/linux_x86_newlib/COPYING.LIB):
+toolchain/mac_x86_newlib is licensed as follows
+ (Cf. toolchain/mac_x86_newlib/COPYING.LIB):
+toolchain/win_x86_newlib is licensed as follows
+ (Cf. toolchain/win_x86_newlib/COPYING.LIB):
+======================================================================
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard. To achieve this, non-free programs must
+be allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control
+compilation and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at least
+ three years, to give the same user the materials specified in
+ Subsection 6a, above, for a charge no more than the cost of
+ performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License
+may add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should
+have at least the "copyright" line and a pointer to where the full
+notice is found.
+
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James
+ Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
+
+======================================================================
+toolchain/linux_x86 is licensed as follows
+ (Cf. toolchain/linux_x86/COPYING.RUNTIME):
+toolchain/mac_x86 is licensed as follows
+ (Cf. toolchain/mac_x86/COPYING.RUNTIME):
+toolchain/win_x86 is licensed as follows
+ (Cf. toolchain/win_x86/COPYING.RUNTIME):
+toolchain/linux_x86_newlib is licensed as follows
+ (Cf. toolchain/linux_x86_newlib/COPYING.RUNTIME):
+toolchain/mac_x86_newlib is licensed as follows
+ (Cf. toolchain/mac_x86_newlib/COPYING.RUNTIME):
+toolchain/win_x86_newlib is licensed as follows
+ (Cf. toolchain/win_x86_newlib/COPYING.RUNTIME):
+======================================================================
+GCC RUNTIME LIBRARY EXCEPTION
+
+Version 3.1, 31 March 2009
+
+Copyright (C) 2009 Free Software Foundation, Inc. <http://fsf.org/>
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+This GCC Runtime Library Exception ("Exception") is an additional
+permission under section 7 of the GNU General Public License, version
+3 ("GPLv3"). It applies to a given file (the "Runtime Library") that
+bears a notice placed by the copyright holder of the file stating that
+the file is governed by GPLv3 along with this Exception.
+
+When you use GCC to compile a program, GCC may combine portions of
+certain GCC header files and runtime libraries with the compiled
+program. The purpose of this Exception is to allow compilation of
+non-GPL (including proprietary) programs to use, in this way, the
+header files and runtime libraries covered by this Exception.
+
+0. Definitions.
+
+A file is an "Independent Module" if it either requires the Runtime
+Library for execution after a Compilation Process, or makes use of an
+interface provided by the Runtime Library, but is not otherwise based
+on the Runtime Library.
+
+"GCC" means a version of the GNU Compiler Collection, with or without
+modifications, governed by version 3 (or a specified later version) of
+the GNU General Public License (GPL) with the option of using any
+subsequent versions published by the FSF.
+
+"GPL-compatible Software" is software whose conditions of propagation,
+modification and use would permit combination with GCC in accord with
+the license of GCC.
+
+"Target Code" refers to output from any compiler for a real or virtual
+target processor architecture, in executable form or suitable for
+input to an assembler, loader, linker and/or execution
+phase. Notwithstanding that, Target Code does not include data in any
+format that is used as a compiler intermediate representation, or used
+for producing a compiler intermediate representation.
+
+The "Compilation Process" transforms code entirely represented in
+non-intermediate languages designed for human-written code, and/or in
+Java Virtual Machine byte code, into Target Code. Thus, for example,
+use of source code generators and preprocessors need not be considered
+part of the Compilation Process, since the Compilation Process can be
+understood as starting with the output of the generators or
+preprocessors.
+
+A Compilation Process is "Eligible" if it is done using GCC, alone or
+with other GPL-compatible software, or if it is done without using any
+work based on GCC. For example, using non-GPL-compatible Software to
+optimize any GCC intermediate representations would not qualify as an
+Eligible Compilation Process.
+
+1. Grant of Additional Permission.
+
+You have permission to propagate a work of Target Code formed by
+combining the Runtime Library with Independent Modules, even if such
+propagation would otherwise violate the terms of GPLv3, provided that
+all Target Code was generated by Eligible Compilation Processes. You
+may then convey such a combination under terms of your choice,
+consistent with the licensing of the Independent Modules.
+
+2. No Weakening of GCC Copyleft.
+
+The availability of this Exception does not imply any general
+presumption that third-party software is unaffected by the copyleft
+requirements of the license of GCC.
+
+
+
+======================================================================
+toolchain/linux_x86 is licensed as follows
+ (Cf. toolchain/linux_x86/COPYING3):
+toolchain/mac_x86 is licensed as follows
+ (Cf. toolchain/mac_x86/COPYING3):
+toolchain/win_x86 is licensed as follows
+ (Cf. toolchain/win_x86/COPYING3):
+toolchain/linux_x86_newlib is licensed as follows
+ (Cf. toolchain/linux_x86_newlib/COPYING3):
+toolchain/mac_x86_newlib is licensed as follows
+ (Cf. toolchain/mac_x86_newlib/COPYING3):
+toolchain/win_x86_newlib is licensed as follows
+ (Cf. toolchain/win_x86_newlib/COPYING3):
+======================================================================
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
+
+
+======================================================================
+toolchain/linux_x86 is licensed as follows
+ (Cf. toolchain/linux_x86/COPYING3.LIB):
+toolchain/mac_x86 is licensed as follows
+ (Cf. toolchain/mac_x86/COPYING3.LIB):
+toolchain/win_x86 is licensed as follows
+ (Cf. toolchain/win_x86/COPYING3.LIB):
+toolchain/linux_x86_newlib is licensed as follows
+ (Cf. toolchain/linux_x86_newlib/COPYING3.LIB):
+toolchain/mac_x86_newlib is licensed as follows
+ (Cf. toolchain/mac_x86_newlib/COPYING3.LIB):
+toolchain/win_x86_newlib is licensed as follows
+ (Cf. toolchain/win_x86_newlib/COPYING3.LIB):
+======================================================================
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
+
+
+
+======================================================================
+third_party/pymox is licensed as follows
+ (Cf. third_party/pymox/COPYING):
+======================================================================
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+======================================================================
+third_party/scons-2.0.1 is licensed as follows
+ (Cf. third_party/scons-2.0.1/LICENSE):
+======================================================================
+
+Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+ The SCons Foundation
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+======================================================================
+third_party/gmock is licensed as follows
+ (Cf. third_party/gmock/COPYING)
+======================================================================
+
+Copyright 2008, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+======================================================================
+third_party/gmock/scripts/generator is licensed as follows
+ (Cf. third_party/gmock/scripts/generator/COPYING)
+======================================================================
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [2007] Neal Norwitz
+ Portions Copyright [2007] Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+======================================================================
+third_party/gtest is licensed as follows
+ (Cf. third_party/gtest/COPYING)
+======================================================================
+
+Copyright 2008, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+======================================================================
+toolchain/linux_x86/x86_64-nacl/include/boost/ is licensed as follows
+ (Cf. toolchain/linux_x86/x86_64-nacl/include/boost/LICENSE_1_0.txt):
+toolchain/mac_x86/x86_64-nacl/include/boost/ is licensed as follows
+ (Cf. toolchain/mac_x86/x86_64-nacl/include/boost/LICENSE_1_0.txt):
+toolchain/win_x86/x86_64-nacl/include/boost/ is licensed as follows
+ (Cf. toolchain/win_x86/x86_64-nacl/include/boost/LICENSE_1_0.txt):
+toolchain/linux_x86_newlib/x86_64-nacl/include/boost/ is licensed as follows
+ (Cf. toolchain/linux_x86_newlib/x86_64-nacl/include/boost/LICENSE_1_0.txt):
+toolchain/mac_x86_newlib/x86_64-nacl/include/boost/ is licensed as follows
+ (Cf. toolchain/mac_x86_newlib/x86_64-nacl/include/boost/LICENSE_1_0.txt):
+toolchain/win_x86_newlib/x86_64-nacl/include/boost/ is licensed as follows
+ (Cf. toolchain/win_x86_newlib/x86_64-nacl/include/boost/LICENSE_1_0.txt):
+======================================================================
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/native_client_sdk/src/OWNERS b/native_client_sdk/src/OWNERS
new file mode 100644
index 0000000..c76eba0
--- /dev/null
+++ b/native_client_sdk/src/OWNERS
@@ -0,0 +1,4 @@
+set noparent
+bradnelson@chromium.org
+noelallen@chromium.org
+erikkay@chromium.org
diff --git a/native_client_sdk/src/PRESUBMIT.py b/native_client_sdk/src/PRESUBMIT.py
new file mode 100755
index 0000000..8e41521
--- /dev/null
+++ b/native_client_sdk/src/PRESUBMIT.py
@@ -0,0 +1,23 @@
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Documentation on PRESUBMIT.py can be found at:
+# http://www.chromium.org/developers/how-tos/depottools/presubmit-scripts
+
+
+def CheckChangeOnUpload(input_api, output_api):
+ report = []
+ affected_files = input_api.AffectedFiles(include_deletes=False)
+ report.extend(input_api.canned_checks.PanProjectChecks(
+ input_api, output_api, project_name='Native Client'))
+ return report
+
+
+def CheckChangeOnCommit(input_api, output_api):
+ report = []
+ report.extend(CheckChangeOnUpload(input_api, output_api))
+ report.extend(input_api.canned_checks.CheckTreeIsOpen(
+ input_api, output_api,
+ json_url='http://naclsdk-status.appspot.com/current?format=json'))
+ return report
diff --git a/native_client_sdk/src/README b/native_client_sdk/src/README
new file mode 100644
index 0000000..8c32d1f
--- /dev/null
+++ b/native_client_sdk/src/README
@@ -0,0 +1,24 @@
+Welcome to the Native Client SDK.
+
+Native Client Tools Bundle
+Version: ${VERSION}
+Revision: ${REVISION}
+Build Date: ${DATE}
+
+Please refer to the online documentation here:
+
+ http://code.google.com/chrome/nativeclient
+
+OTHER DEVELOPMENT
+
+If you want to contribute to the Native Client SDK itself, the code site is
+here:
+
+ http://code.google.com/p/nativeclient-sdk/
+
+
+KNOWN ISSUES
+
+Please refer to the online documentation here:
+
+ http://code.google.com/chrome/nativeclient/docs/releasenotes.html
diff --git a/native_client_sdk/src/build_tools/MkLink/MkLink.c b/native_client_sdk/src/build_tools/MkLink/MkLink.c
new file mode 100644
index 0000000..a821af0
--- /dev/null
+++ b/native_client_sdk/src/build_tools/MkLink/MkLink.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright 2008, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <windows.h>
+#include <commctrl.h>
+#include <nsis/pluginapi.h>
+#include <winternl.h>
+
+enum { kLargeBuf = 1024, kSmallBuf = 256 } ;
+
+#if defined(_MSC_VER)
+/* Ensure these are treated as functions and not inlined as intrinsics, or disable /Oi */
+#pragma warning(disable:4164) /* intrinsic function not declared */
+#pragma function(memcpy, memset, memcmp)
+#endif
+
+HMODULE hNrDll = NULL;
+NTSTATUS (NTAPI *fNtCreateFile) (PHANDLE FileHandle,
+ ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
+ PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize,
+ ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition,
+ ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength);
+NTSTATUS (NTAPI *fNtClose) (HANDLE Handle);
+
+HMODULE hKernel32 = NULL;
+BOOL (WINAPI *fCreateHardLink) (TCHAR * linkFileName,
+ TCHAR * existingFileName, LPSECURITY_ATTRIBUTES lpSecurityAttributes);
+BOOL (WINAPI *fCreateSymbolicLink) (TCHAR * linkFileName,
+ TCHAR * existingFileName, DWORD flags);
+
+#undef CreateHardLink
+#undef CreateSymbolicLink
+#ifdef UNICODE
+#define CreateHardLink "CreateHardLinkW"
+#define CreateSymbolicLink "CreateSymbolicLinkW"
+#else
+#define CreateHardLink "CreateHardLinkA"
+#define CreateSymbolicLink "CreateSymbolicLinkA"
+#endif
+
+BOOL MakeHardLink(TCHAR *linkFileName, TCHAR *existingFileName) {
+ if (!hKernel32)
+ hKernel32 = LoadLibrary(_T("KERNEL32.DLL"));
+ if (hKernel32) {
+ if (!fCreateHardLink)
+ fCreateHardLink = GetProcAddress(hKernel32, CreateHardLink);
+ if (fCreateHardLink)
+ return fCreateHardLink(linkFileName, existingFileName, NULL);
+ }
+ return FALSE;
+}
+
+BOOL MakeSymLink(TCHAR *linkFileName, TCHAR *existingFileName, BOOL dirLink) {
+ TCHAR *f1 = HeapAlloc(GetProcessHeap(), 0, sizeof(TCHAR)*kLargeBuf);
+ TCHAR *f2 = HeapAlloc(GetProcessHeap(), 0, sizeof(TCHAR)*kLargeBuf);
+ SECURITY_ATTRIBUTES sec_attr = { sizeof (SECURITY_ATTRIBUTES), NULL, FALSE};
+ OBJECT_ATTRIBUTES obj_attr;
+ IO_STATUS_BLOCK io_block;
+ TCHAR *p, *q;
+ HANDLE f;
+ BOOL status;
+ if (!f1 || !f2)
+ return FALSE;
+ lstrcpy(f1, linkFileName);
+ for (p = f1; p[0]; p++)
+ if (p[0] == _T('/'))
+ p[0] = _T('\\');
+ q = f1;
+ while (q[0]) {
+ p = q;
+ do {
+ q++;
+ } while (q[0] && q[0] != '\\');
+ }
+ if (p[0] = '\\') {
+ TCHAR c = p[1];
+ p[1] = 0;
+ status = GetVolumeInformation(f1, NULL, 0, NULL, NULL, NULL,
+ f2, sizeof(f2));
+ p[1] = c;
+ } else {
+ status = GetVolumeInformation(NULL, NULL, 0, NULL, NULL, NULL,
+ f2, sizeof(f2));
+ }
+ /* If it's NFS then we can create real symbolic link. */
+ if (!lstrcmpi(f2, _T("NFS"))) {
+ lstrcpy(f2, existingFileName);
+ for (p = f2; p[0]; p++)
+ if (p[0] == _T('\\'))
+ p[0] = _T('/');
+ if (!hNrDll)
+ hNrDll = LoadLibrary(_T("NTDLL.DLL"));
+ if (hNrDll) {
+ if (!fNtCreateFile)
+ fNtCreateFile = GetProcAddress(hNrDll, "NtCreateFile");
+ if (!fNtClose)
+ fNtClose = GetProcAddress(hNrDll, "NtClose");
+ if (fNtCreateFile && fNtClose) {
+ struct {
+ ULONG offset;
+ UCHAR flags;
+ UCHAR nameLength;
+ USHORT valueLength;
+ CHAR name[21];
+ /* To prevent troubles with alignment */
+ CHAR value[kLargeBuf*sizeof(WCHAR)];
+ } *ea_info = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ sizeof(*ea_info));
+ WCHAR *fn = HeapAlloc(GetProcessHeap(),
+ 0,
+ sizeof(TCHAR)*kLargeBuf);
+ UNICODE_STRING n = { lstrlen(f1), kLargeBuf, fn };
+ ea_info->nameLength = 20;
+ lstrcpy(ea_info->name, "NfsSymlinkTargetName");
+#ifdef UNICODE
+ lstrcpy(fn, f1);
+ lstrcpy((LPWSTR)ea_info->value, existingFileName);
+#else
+ MultiByteToWideChar(CP_ACP, 0, f1, -1, fn, kLargeBuf);
+ MultiByteToWideChar(CP_ACP, 0, existingFileName, -1,
+ (LPWSTR)ea_info->value,
+ sizeof(ea_info->value)/sizeof(WCHAR));
+#endif
+ ea_info->valueLength =
+ lstrlenW((LPWSTR)ea_info->value)*sizeof(WCHAR);
+ InitializeObjectAttributes(&obj_attr, &n, OBJ_CASE_INSENSITIVE,
+ NULL, NULL);
+ status = fNtCreateFile(
+ &f, FILE_WRITE_DATA | FILE_WRITE_EA | SYNCHRONIZE, &obj_attr,
+ &io_block, NULL, FILE_ATTRIBUTE_SYSTEM, FILE_SHARE_READ |
+ FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_CREATE,
+ FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
+ &ea_info, 1024 * sizeof (WCHAR));
+ if (NT_SUCCESS(status)) {
+ fNtClose(f);
+ HeapFree(GetProcessHeap(), 0, fn);
+ HeapFree(GetProcessHeap(), 0, ea_info);
+ HeapFree(GetProcessHeap(), 0, f2);
+ HeapFree(GetProcessHeap(), 0, f1);
+ return TRUE;
+ }
+ HeapFree(GetProcessHeap(), 0, fn);
+ HeapFree(GetProcessHeap(), 0, ea_info);
+ }
+ }
+ }
+ lstrcpy(f2, existingFileName);
+ for (p = f2; p[0]; p++)
+ if (p[0] == _T('/'))
+ p[0] = _T('\\');
+ if (!hKernel32)
+ hKernel32 = LoadLibrary(_T("KERNEL32.DLL"));
+ if (hKernel32) {
+ if (!fCreateSymbolicLink)
+ fCreateSymbolicLink = GetProcAddress(hKernel32, CreateSymbolicLink);
+ if (fCreateSymbolicLink) {
+ if (fCreateSymbolicLink(f1, f2,
+ dirLink ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0)) {
+ HeapFree(GetProcessHeap(), 0, f2);
+ HeapFree(GetProcessHeap(), 0, f1);
+ return TRUE;
+ }
+ }
+ }
+ if (dirLink) {
+ /* Ignore errors - file may already exist */
+ CreateDirectory(f1, NULL);
+ f = CreateFile(f1, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
+ NULL );
+ if (f != INVALID_HANDLE_VALUE) {
+ struct rp_info {
+ DWORD tag;
+ DWORD dataLength;
+ WORD reserved1;
+ WORD targetLength;
+ WORD targetMaxLength;
+ WORD reserved2;
+ WCHAR target[kLargeBuf+4];
+ } *rp_info = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ sizeof(*rp_info));
+ DWORD len;
+ WCHAR *startlink, *endlink;
+ rp_info->tag = IO_REPARSE_TAG_MOUNT_POINT;
+ rp_info->target[0] = L'\\';
+ rp_info->target[1] = L'?';
+ rp_info->target[2] = L'?';
+ rp_info->target[3] = L'\\';
+ if (((f2[0] == _T('\\')) && (f2[1] == _T('\\'))) ||
+ ((f2[1] == _T(':')) && (f2[2] == _T('\\')))) {
+#ifdef UNICODE
+ lstrcpy(rp_info->target+4, f2);
+#else
+ MultiByteToWideChar(CP_ACP, 0, f2, -1,
+ rp_info->target+4, kLargeBuf);
+#endif
+ } else {
+#ifdef UNICODE
+ GetFullPathNameW(f1, 1024, rp_info->target+4, &startlink);
+ lstrcpy(startlink, f2);
+#else
+ MultiByteToWideChar(CP_ACP, 0, f1, -1,
+ (LPWSTR)f1, kLargeBuf/sizeof(WCHAR));
+ GetFullPathNameW(f1, 1024, rp_info->target+4, &startlink);
+ MultiByteToWideChar(CP_ACP, 0, f2, -1,
+ startlink, kLargeBuf+4-(startlink-rp_info->target));
+#endif
+ }
+ /* Remove "XXX/../" and replace "/" with "\" */
+ for (startlink = endlink = rp_info->target+4;
+ endlink[0]; startlink++, endlink++) {
+ startlink[0] = endlink[0];
+ if ((startlink[0] == L'\\') &&
+ (startlink[-1] == L'.') &&
+ (startlink[-2] == L'.')) {
+ for (startlink--; startlink > rp_info->target+4 &&
+ startlink[0] != L'\\'; startlink--)
+ { }
+ for (startlink--; startlink > rp_info->target+4 &&
+ startlink[0] != L'\\'; startlink--)
+ { }
+ if (startlink < rp_info->target+4)
+ startlink = rp_info->target+4;
+ }
+ }
+ startlink[0] = endlink[0];
+ rp_info->targetLength = lstrlenW(rp_info->target)*sizeof(WCHAR);
+ rp_info->targetMaxLength = rp_info->targetLength+sizeof(WCHAR);
+ rp_info->dataLength = rp_info->targetMaxLength
+ +FIELD_OFFSET(struct rp_info, target)
+ -FIELD_OFFSET(struct rp_info, reserved1)
+ +sizeof(WCHAR);
+ if (DeviceIoControl(f, 0x900A4, rp_info,
+ rp_info->dataLength
+ +FIELD_OFFSET(struct rp_info, reserved1),
+ NULL, 0, &len, NULL)) {
+ CloseHandle(f);
+ HeapFree(GetProcessHeap(), 0, rp_info);
+ HeapFree(GetProcessHeap(), 0, f2);
+ HeapFree(GetProcessHeap(), 0, f1);
+ return TRUE;
+ }
+ CloseHandle(f);
+ RemoveDirectory(f1);
+ HeapFree(GetProcessHeap(), 0, rp_info);
+ }
+ }
+ for (p = f2; p[0]; p++)
+ if (p[0] == _T('\\'))
+ p[0] = _T('/');
+ f = CreateFile(f1, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM, NULL);
+ if (f != INVALID_HANDLE_VALUE) {
+ struct {
+ WCHAR sig[4];
+ WCHAR value[kLargeBuf];
+ } *link_info = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ sizeof(*link_info));
+ DWORD towrite, written;
+ link_info->sig[0] = 0x6e49;
+ link_info->sig[1] = 0x7874;
+ link_info->sig[2] = 0x4e4c;
+ link_info->sig[3] = 0x014b;
+#ifdef UNICODE
+ lstrcpy(link_info->value, f2);
+#else
+ MultiByteToWideChar(CP_ACP, 0, f2, -1, link_info->value, kLargeBuf);
+#endif
+ towrite = lstrlenW(link_info->value)*sizeof(WCHAR)+sizeof(link_info->sig);
+ WriteFile(f, link_info, towrite, &written, NULL);
+ CloseHandle(f);
+ if (written == towrite) {
+ HeapFree(GetProcessHeap(), 0, link_info);
+ HeapFree(GetProcessHeap(), 0, f2);
+ HeapFree(GetProcessHeap(), 0, f1);
+ return TRUE;
+ }
+ HeapFree(GetProcessHeap(), 0, link_info);
+ }
+ HeapFree(GetProcessHeap(), 0, f2);
+ HeapFree(GetProcessHeap(), 0, f1);
+ return FALSE;
+}
+
+HINSTANCE instance;
+
+HWND parent, list;
+
+/*
+ * Show message in NSIS details window.
+ */
+void NSISprint(const TCHAR *str) {
+ if (list && *str) {
+ LVITEM item = {
+ /* mask */ LVIF_TEXT,
+ /* iItem */ SendMessage(list, LVM_GETITEMCOUNT, 0, 0),
+ /* iSubItem */ 0, /* state */ 0, /* stateMask */ 0,
+ /* pszText */ (TCHAR *) str, /* cchTextMax */ 0,
+ /* iImage */ 0, /* lParam */ 0, /* iIndent */ 0,
+ /* iGroupId */ 0, /* cColumns */ 0, /* puColumns */ NULL,
+ /* piColFmt */ NULL, /* iGroup */ 0};
+ ListView_InsertItem(list, &item);
+ ListView_EnsureVisible(list, item.iItem, 0);
+ }
+}
+
+enum linktype { HARDLINK, SOFTLINKD, SOFTLINKF };
+
+void makelink(HWND hwndParent, int string_size, TCHAR *variables,
+ stack_t **stacktop, extra_parameters *extra, enum linktype type) {
+ TCHAR *msg = HeapAlloc(GetProcessHeap(), 0, sizeof(TCHAR)*kLargeBuf);
+ TCHAR *from = HeapAlloc(GetProcessHeap(), 0, sizeof(TCHAR)*kSmallBuf);
+ TCHAR *to = HeapAlloc(GetProcessHeap(), 0, sizeof(TCHAR)*kSmallBuf);
+ TCHAR *msgFormat =
+ type == HARDLINK ? _T("Link: \"%s\" to \"%s\"%s") :
+ type == SOFTLINKD ? _T("Symbolic Directory Link: \"%s\" to \"%s\"%s") :
+ _T("Symbolic Link: \"%s\" to \"%s\"%s");
+ BOOL res;
+ parent = hwndParent;
+ list = FindWindowEx(FindWindowEx(parent, NULL, _T("#32770"), NULL),
+ NULL, _T("SysListView32"), NULL);
+
+ EXDLL_INIT();
+
+ if (!msg || !from || !to) {
+ MessageBox(parent, _T("Fatal error: no memory for MkLink"), 0, MB_OK);
+ }
+
+ if (popstringn(from, kSmallBuf)) {
+ MessageBox(parent,
+ _T("Usage: MkLink::Hard \"to_file\" \"from_file\" "), 0, MB_OK);
+ }
+
+ if (popstringn(to, kSmallBuf)) {
+ MessageBox(parent,
+ _T("Usage: MkLink::Hard \"fo_file\" \"from_file\" "),0,MB_OK);
+ }
+
+ switch (type) {
+ case HARDLINK:
+ res = MakeHardLink(from, to);
+ break;
+ case SOFTLINKD:
+ res = MakeSymLink(from, to, TRUE);
+ break;
+ case SOFTLINKF:
+ res = MakeSymLink(from, to, FALSE);
+ break;
+ }
+ wsprintf(msg, msgFormat, to, from, res ? _T("") : _T(" - fail..."));
+ NSISprint(msg);
+
+ HeapFree(GetProcessHeap(), 0, to);
+ HeapFree(GetProcessHeap(), 0, from);
+ HeapFree(GetProcessHeap(), 0, msg);
+}
+
+void __declspec(dllexport) Hard(HWND hwndParent, int string_size,
+ TCHAR *variables, stack_t **stacktop,
+ extra_parameters *extra) {
+ makelink(hwndParent, string_size, variables, stacktop, extra, HARDLINK);
+}
+
+void __declspec(dllexport) SoftD(HWND hwndParent, int string_size,
+ TCHAR *variables, stack_t **stacktop,
+ extra_parameters *extra) {
+ makelink(hwndParent, string_size, variables, stacktop, extra, SOFTLINKD);
+}
+
+void __declspec(dllexport) SoftF(HWND hwndParent, int string_size,
+ TCHAR *variables, stack_t **stacktop,
+ extra_parameters *extra) {
+ makelink(hwndParent, string_size, variables, stacktop, extra, SOFTLINKF);
+}
+
+BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) {
+ instance = hInst;
+ return TRUE;
+}
diff --git a/native_client_sdk/src/build_tools/MkLink/MkLink.sln b/native_client_sdk/src/build_tools/MkLink/MkLink.sln
new file mode 100644
index 0000000..05a60ff
--- /dev/null
+++ b/native_client_sdk/src/build_tools/MkLink/MkLink.sln
@@ -0,0 +1,26 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MkLink", "MkLink.vcproj", "{5E3E2AFD-1D6B-4997-A9B5-8ECBC8F6C31A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug Unicode|Win32 = Debug Unicode|Win32
+ Debug|Win32 = Debug|Win32
+ Release Unicode|Win32 = Release Unicode|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {5E3E2AFD-1D6B-4997-A9B5-8ECBC8F6C31A}.Debug Unicode|Win32.ActiveCfg = Debug Unicode|Win32
+ {5E3E2AFD-1D6B-4997-A9B5-8ECBC8F6C31A}.Debug Unicode|Win32.Build.0 = Debug Unicode|Win32
+ {5E3E2AFD-1D6B-4997-A9B5-8ECBC8F6C31A}.Debug|Win32.ActiveCfg = Debug|Win32
+ {5E3E2AFD-1D6B-4997-A9B5-8ECBC8F6C31A}.Debug|Win32.Build.0 = Debug|Win32
+ {5E3E2AFD-1D6B-4997-A9B5-8ECBC8F6C31A}.Release Unicode|Win32.ActiveCfg = Release Unicode|Win32
+ {5E3E2AFD-1D6B-4997-A9B5-8ECBC8F6C31A}.Release Unicode|Win32.Build.0 = Release Unicode|Win32
+ {5E3E2AFD-1D6B-4997-A9B5-8ECBC8F6C31A}.Release|Win32.ActiveCfg = Release|Win32
+ {5E3E2AFD-1D6B-4997-A9B5-8ECBC8F6C31A}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/native_client_sdk/src/build_tools/MkLink/MkLink.vcproj b/native_client_sdk/src/build_tools/MkLink/MkLink.vcproj
new file mode 100644
index 0000000..0067ad6
--- /dev/null
+++ b/native_client_sdk/src/build_tools/MkLink/MkLink.vcproj
@@ -0,0 +1,484 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="MkLink"
+ ProjectGUID="{5E3E2AFD-1D6B-4997-A9B5-8ECBC8F6C31A}"
+ RootNamespace="MkLink"
+ TargetFrameworkVersion="0"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/MkLink.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ InlineFunctionExpansion="0"
+ AdditionalIncludeDirectories="."
+ PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;_USRDLL;EXDLL_EXPORTS;NSISCALL=__stdcall"
+ StringPooling="true"
+ RuntimeLibrary="2"
+ BufferSecurityCheck="false"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Release/MkLink.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="odbc32.lib odbccp32.lib nsis\pluginapi.lib"
+ OutputFile=".\Release/MkLink.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ GenerateManifest="false"
+ IgnoreAllDefaultLibraries="true"
+ IgnoreDefaultLibraryNames="libc.lib"
+ ProgramDatabaseFile=".\Release/MkLink.pdb"
+ OptimizeForWindows98="1"
+ EntryPointSymbol="DllMain"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary=".\Release/MkLink.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release/MkLink.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\Debug"
+ IntermediateDirectory=".\Debug"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug/MkLink.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="."
+ PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;_USRDLL;EXDLL_EXPORTS;NSISCALL=__stdcall"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="0"
+ RuntimeLibrary="3"
+ BufferSecurityCheck="false"
+ PrecompiledHeaderFile=".\Debug/MkLink.pch"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="odbc32.lib odbccp32.lib nsis\pluginapi.lib"
+ OutputFile=".\Debug/MkLink.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ GenerateManifest="false"
+ IgnoreAllDefaultLibraries="true"
+ IgnoreDefaultLibraryNames=""
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Debug/MkLink.pdb"
+ EntryPointSymbol="DllMain"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary=".\Debug/MkLink.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Debug/MkLink.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release Unicode|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release Unicode/MkLink.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ InlineFunctionExpansion="0"
+ AdditionalIncludeDirectories="."
+ PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;_USRDLL;EXDLL_EXPORTS;NSISCALL=__stdcall"
+ StringPooling="true"
+ RuntimeLibrary="2"
+ BufferSecurityCheck="false"
+ EnableFunctionLevelLinking="true"
+ PrecompiledHeaderFile=".\Release Unicode/MkLink.pch"
+ AssemblerListingLocation=".\Release Unicode/"
+ ObjectFile=".\Release Unicode/"
+ ProgramDataBaseFileName=".\Release Unicode/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="odbc32.lib odbccp32.lib nsis\pluginapi.lib"
+ OutputFile=".\Release Unicode/MkLink.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ GenerateManifest="false"
+ IgnoreAllDefaultLibraries="true"
+ IgnoreDefaultLibraryNames="libc.lib"
+ ProgramDatabaseFile=".\Release Unicode/MkLink.pdb"
+ OptimizeForWindows98="1"
+ EntryPointSymbol="DllMain"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary=".\Release Unicode/MkLink.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Release Unicode/MkLink.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug Unicode|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug Unicode/MkLink.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="."
+ PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;_USRDLL;EXDLL_EXPORTS;NSISCALL=__stdcall"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="0"
+ RuntimeLibrary="3"
+ BufferSecurityCheck="false"
+ PrecompiledHeaderFile=".\Debug Unicode/MkLink.pch"
+ AssemblerListingLocation=".\Debug Unicode/"
+ ObjectFile=".\Debug Unicode/"
+ ProgramDataBaseFileName=".\Debug Unicode/"
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="odbc32.lib odbccp32.lib nsis\pluginapi.lib"
+ OutputFile=".\Debug Unicode/MkLink.dll"
+ LinkIncremental="2"
+ SuppressStartupBanner="true"
+ GenerateManifest="false"
+ IgnoreAllDefaultLibraries="true"
+ IgnoreDefaultLibraryNames=""
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile=".\Debug Unicode/MkLink.pdb"
+ EntryPointSymbol="DllMain"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary=".\Debug Unicode/MkLink.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ OutputFile=".\Debug Unicode/MkLink.bsc"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+ >
+ <File
+ RelativePath=".\MkLink.c"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl"
+ >
+ <File
+ RelativePath="nsis\pluginapi.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/native_client_sdk/src/build_tools/MkLink/Release Unicode/MkLink.dll b/native_client_sdk/src/build_tools/MkLink/Release Unicode/MkLink.dll
new file mode 100644
index 0000000..c4c604d
--- /dev/null
+++ b/native_client_sdk/src/build_tools/MkLink/Release Unicode/MkLink.dll
Binary files differ
diff --git a/native_client_sdk/src/build_tools/README_NSIS.txt b/native_client_sdk/src/build_tools/README_NSIS.txt
new file mode 100644
index 0000000..e51141a
--- /dev/null
+++ b/native_client_sdk/src/build_tools/README_NSIS.txt
@@ -0,0 +1,59 @@
+Note: This whole process takes about 1:30 to 2 hours for someone who is pretty
+familiar with it. If this is your first attempt, it's best to plan for this to
+take the whole day.
+
+Step 0: Make sure your toolchain dir is clean.
+ The easiest way to do this is to remove these dirs and then do a gclient sync:
+ rmdir src\toolchain
+ rmdir src\third_party
+ scons -c examples
+ gclient sync
+ Or: start with a fresh checkout from nothing.
+
+Step 1: Generate a base copy of make_native_client_sdk.nsi
+ Edit generate_windows_installer.py so that it runs everything up to
+ make_native_client_sdk2.sh.
+ Easiest: add a 'break' after waiting for make_native_client_sdk.sh subprocess.
+ This is around line 194.
+ In src, python build_tools\generate_installers.py --development
+ This will produce build_tools\make_native_client_sdk.nsi
+
+Step 3: Create a back-up and an editable copy of make_native_client_sdk.nsi
+ Make 2 copies of make_native_client_sdk.nsi:
+ copy make_native_client_sdk.nsi make_native_client_sdk-orig.nsi
+ copy make_native_client_sdk.nsi make_native_client_sdk-new.nsi
+
+Step 4: Edit the .nsi file to contain the appropriate changes.
+ Edit the "-new" version of make_native_client_sdk.
+ Manually apply the patch in nsi_patch.txt (and any other necessary edits) to
+ make_native_client_sdk-new.nsi
+
+Step 5: Create the new patch file.
+ cd build_tools
+ copy nsi_patch.txt nsi_patch-old.txt
+ ..\third_party\cygwin\bin\diff -Naur make_native_client_sdk-orig.nsi make_native_client_sdk-new.nsi > nsi_patch.txt
+ Note: the order of -orig and -new is important.
+
+Step 6: Edit the patch file.
+ Edit nsi_patch.txt
+ Change every occurrence of native_client_sdk_0_x_y to %NACL_SDK_FULL_NAME%
+ Change every occurrence of third_party\cygwin\ to %$CYGWIN_DIR%
+ Note: the trailing '\' on third_party\cygwin_dir\ is important.
+ At the top of the file, change the diff file names to both be
+ make_native_client_sdk.nsi
+
+Step 7: Test the patch.
+ (In the build_tools dir):
+ copy make_native_client_sdk-orig.nsi make_native_client_sdk.nsi
+ ..\third_party\cygwin\bin\bash make_native_client_sdk2.sh -V 0.x.y -v -n
+ Note: |x| and |y| have to match the original values in
+ native_client_sdk_0_x_y from Step 6.
+ Note: This step can take a long time (5 min or more).
+
+Step 8: Test the installer.
+ Step 7 should have produced nacl_sdk.exe in the root of the nacl-sdk project.
+ Run this from the Finder, and install the SDK to see if it works.
+
+Step 9: Upload the changes.
+ If everything works, make a CL.
+ REMEMBER: remove the 'break' statement you added in Step 1.
diff --git a/native_client_sdk/src/build_tools/__init__.py b/native_client_sdk/src/build_tools/__init__.py
new file mode 100644
index 0000000..c324a56
--- /dev/null
+++ b/native_client_sdk/src/build_tools/__init__.py
@@ -0,0 +1,10 @@
+#! -*- python -*-
+
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""build_tools Package
+
+This package contains general python utilities that are used for
+creating the Native Client SDK."""
diff --git a/native_client_sdk/src/build_tools/apply_patch.py b/native_client_sdk/src/build_tools/apply_patch.py
new file mode 100755
index 0000000..bc1f126
--- /dev/null
+++ b/native_client_sdk/src/build_tools/apply_patch.py
@@ -0,0 +1,534 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+""" Utility for applying svn-diff-style patches to files.
+
+This file contains a collection of classes for parsing svn-diff patch files
+and applying these patches to files in a directory. The main API class is
+ApplyPatch. It has two entry point: method Parse to parse a patch file and
+method Apply to apply that patch to a base directory.
+
+Most other classes are internal only. Each class corresponds to a structure in
+the patch file, such as _Range, which corresponds to the range line identified
+by the heading and traling '@@'.
+"""
+
+__author__ = 'gwink@google.com (Georges Winkenbach)'
+
+import os
+import re
+import shutil
+import sys
+import tempfile
+
+# Constants: regular expressions for parsing patch lines.
+RE_HEADER_ORIGINAL_FILE = re.compile('---\s+([^\s]+)')
+RE_HEADER_OUT_FILE = re.compile('\+\+\+\s+([^\s]+)')
+RE_RANGE = re.compile('@@\s*-(\d+)(?:,(\d+))?\s+\+(\d+)(?:,(\d+))?\s*@@')
+
+# Constants: type of diff lines.
+CONTEXTUAL_DIFF_LINE = 0
+ADDED_DIFF_LINE = 1
+DELETED_DIFF_LINE = 2
+NOT_A_DIFF_LINE = 3
+
+
+class Error(Exception):
+ ''' Exceptions raised in this file.
+
+ Attributes:
+ error: a string that describes the error.
+ context: a string that shows the context where the error occured.
+ '''
+
+ def __init__(self, context='', error='error'):
+ ''' Init Error with context and error info. '''
+ super(Error, self).__init__(context, error)
+ self.error = error
+ self.context = context
+
+ def __str__(self):
+ out = 'Error %s.' % self._error
+ return out
+
+
+class _Range(object):
+ ''' Corresponds to a range info in the patch file.
+
+ _Range encapsulates the source- and destination-file range information
+ found in the patch file.
+
+ Attributes:
+ src_start_line: index to the first line in the source file.
+ src_line_count: the number of lines in the source file.
+ dest_start_line: index to the first line in the destination file.
+ dest_line_count: the number of lines in the destination file.
+ '''
+ def __init__(self):
+ ''' Init _Range with empty source and destination range info. '''
+ self.src_start_line = 0
+ self.src_line_count = 0
+ self.dest_start_line = 0
+ self.dest_line_count = 0
+
+ def Parse(self, diff_lines):
+ ''' Parse a range diff line.
+
+ Parse the line at index 0 in diff_lines as a range-info line and set the
+ range info accordingly. Raise an Error exception if the line is not a valid
+ range line. If successful, the line at index 0 is removed from diff_lines.
+
+ Arguments:
+ diff_lines: a list of diff lines read from the patch file.
+ '''
+ match = re.match(RE_RANGE, diff_lines[0])
+ if not match:
+ raise Error(context=diff_lines[0], error='Bad range info')
+
+ # Range in source file. The line count is optional and defaults to 1.
+ self.src_start_line = int(match.group(1))
+ self.src_line_count = 1
+ if (match.group(2)):
+ self.src_line_count = int(match.group(2))
+ # Range in destination file. The line count is optional and defaults to 1.
+ self.dest_start_line = int(match.group(3))
+ self.dest_line_count = 1
+ if (match.group(4)):
+ self.dest_line_count = int(match.group(4))
+
+ diff_lines.pop(0)
+
+
+class _ChangeHunk(object):
+ ''' A ChangeHunk is a range followed by a series of diff lines in the patch.
+
+ Encapsulate the info represented by a range line and the subsequent diff
+ lines in the patch file.
+
+ Attributes:
+ range: the range info, an instance of _Range
+ lines: a list containing the diff lines that follow the range info in
+ the patch file.
+ '''
+
+ def __init__(self):
+ ''' Init an empty _ChangeHunk. '''
+ self.range = None
+ self.lines = []
+
+ @staticmethod
+ def _DiffLineType(line):
+ ''' Helper function that determines the type of a diff line.
+
+ Arguments:
+ line: the diff line whose type must be determined.
+ Return:
+ One of CONTEXTUAL_DIFF_LINE, DELETED_DIFF_LINE, ADDED_DIFF_LINE or
+ NOT_A_DIFF_LINE.
+ '''
+ # A contextual line starts with a space or can be an empty line.
+ if line[0] in [' ', '\n', '\r']:
+ return CONTEXTUAL_DIFF_LINE
+ # A delete line starts with '-'; but '---' denotes a header line.
+ elif line[0] == '-' and not line.startswith('---'):
+ return DELETED_DIFF_LINE
+ # A add line starts with '+'; but '+++' denotes a header line.
+ elif line[0] == '+' and not line.startswith('+++'):
+ return ADDED_DIFF_LINE
+ # Anything else is not a diff line.
+ else:
+ return NOT_A_DIFF_LINE
+
+ def _ParseLines(self, diff_lines):
+ ''' Parse diff lines for a change-hunk.
+
+ Parse the diff lines, update the change-hunk info accordingly and validate
+ the hunk info. The first line in diff_lines should be the line that follows
+ the range info in the patch file.. The subsequent lines should be diff
+ lines. Raise an Error exception if the hunk info doesn't validate. Matched
+ lines are removed from diff_lines.
+
+ Arguments:
+ diff_lines: a list of diff lines read from the patch file.
+ '''
+ src_line_count = 0
+ dest_line_count = 0
+ index = 0
+ for index, line in enumerate(diff_lines):
+ line_type = self._DiffLineType(line)
+ if line_type == CONTEXTUAL_DIFF_LINE:
+ # Contextual line (maybe an empty line).
+ src_line_count += 1
+ dest_line_count += 1
+ elif line_type == DELETED_DIFF_LINE:
+ # Deleted line.
+ src_line_count += 1
+ elif line_type == ADDED_DIFF_LINE:
+ # Added line.
+ dest_line_count += 1
+ else:
+ # Done.
+ break;
+
+ # Store each valid diff line into the local list.
+ self.lines.append(line)
+
+ # Remove lines that have been consumed from list.
+ del diff_lines[0:index]
+
+ # Validate the change hunk.
+ if (src_line_count != self.range.src_line_count) or (dest_line_count !=
+ self.range.dest_line_count):
+ raise Error(context=self.lines[0],
+ error='Range info doesn\'t match lines that follow')
+
+ def Parse(self, diff_lines):
+ ''' Parse a change-hunk.
+
+ Parse the range and subsequent diff lines for a change hunk. The first line
+ in diff_lines should be a range line starting with @@. Raise an Error
+ exception if the hunk info doesn't validate. Matched lines are removed from
+ diff_lines.
+
+ Arguments:
+ diff_lines: a list of diff lines read from the patch file.
+ '''
+ self.range = _Range()
+ self.range.Parse(diff_lines)
+ self._ParseLines(diff_lines)
+
+ def Apply(self, line_num, in_file, out_file):
+ ''' Apply the change hunk to in_file.
+
+ Apply the change hunk to in_file, outputing the result to out_file. Raise a
+ Error exception if any inconsistency is detected.
+
+ Arguments:
+ line_num: the last line number read from in_file, or 0 if the file has not
+ yet been read from.
+ in_file: the input (source) file. Maybe None if the hunk only adds
+ lines to a new file.
+ out_file: the output (destination) file.
+ Return:
+ the line number of the last line read (consumed) from in_file.
+ '''
+ # If range starts at N, consume all lines from line_num to N-1. Don't read
+ # line N yet; what we'll do with it depend on the first diff line. Each
+ # line is written to the output file.
+ while line_num < self.range.src_start_line - 1:
+ out_file.write(in_file.readline())
+ line_num = line_num + 1
+
+ for diff_line in self.lines:
+ diff_line_type = self._DiffLineType(diff_line)
+ if diff_line_type == CONTEXTUAL_DIFF_LINE:
+ # Remove the contextual-line marker, if present.
+ if diff_line[0] == ' ':
+ diff_line = diff_line[1:]
+ # Contextual line should match the input line.
+ line = in_file.readline()
+ line_num = line_num + 1
+ if not line == diff_line:
+ raise Error(context=line, error='Contextual line mismatched')
+ out_file.write(line)
+ elif diff_line_type == DELETED_DIFF_LINE:
+ # Deleted line: consume it from in_file, verify that it matches the
+ # diff line (with marker removed), do not write it to out_file.
+ line = in_file.readline()
+ line_num = line_num + 1
+ if not line == diff_line[1:]:
+ raise Error(context=line, error='Deleted line mismatched')
+ elif diff_line_type == ADDED_DIFF_LINE:
+ # Added line: simply write it to out_file, without the add-line marker.
+ diff_line = diff_line[1:]
+ out_file.write(diff_line)
+ else:
+ # Not a valid diff line; this is an internal error.
+ raise Error(context=diff_line, error='Unexpected diff line')
+
+ return line_num
+
+
+class _PatchHeader(object):
+ ''' The header portion of a patch.
+
+ Encapsulates a header, the two lines prefixed with '---' and '+++' before
+ a series of change hunks.
+
+ Attributes:
+ in_file_name: the file name for the input (source) file.
+ out_file_name: the file name for the output (new) file.
+ '''
+ def __init__(self):
+ ''' Init _PatchHeader with no filenames. '''
+ self.in_file_name = None
+ self.out_file_name = None
+
+ def Parse(self, diff_lines):
+ ''' Parse header info.
+
+ Parse the header information, the two lines starting with '---' and '+++'
+ respectively. Gather the in (source) file name and out (new) file name
+ locally. The first line in diff_lines should be a header line starting
+ with '---'. Raise an Error exception if no header info is found. Matched
+ lines are consumed (remove from diff_lines).
+
+ Arguments:
+ diff_lines: a list of diff lines read from the patch.
+ '''
+ match = self._MatchHeader(RE_HEADER_ORIGINAL_FILE, diff_lines)
+ self.in_file_name = match.group(1)
+ match = self._MatchHeader(RE_HEADER_OUT_FILE, diff_lines)
+ self.out_file_name = match.group(1)
+
+ def _MatchHeader(self, header_re, diff_lines):
+ ''' Helper function to match a single header line using regular expression
+ header_re.
+ '''
+ match = re.match(header_re, diff_lines[0])
+ if not match:
+ raise Error(context=diff_lines[0], error='Bad header')
+ diff_lines.pop(0)
+ return match
+
+
+class _Patch(object):
+ ''' A _Patch is the header and all the change hunks pertaining to a single
+ file.
+
+ Encapsulates all the information for a single-file patch, including the
+ header and all the subsequent change hunks found in the patch file.
+
+ Attributes:
+ header: the header info, an instance of _PatchHeader.
+ hunks: a list of _ChangeHunks.
+ '''
+ def __init__(self):
+ ''' Init an empty _Patch. '''
+ self.header = None
+ self.hunks = []
+
+ def _SrcFileIsOptional(self):
+ ''' Helper function that returns true if it's ok for this patch to not have
+ source file. (It's ok to not have a source file if the patch has a
+ single hunk and that hunk only adds lines to the destination file.
+ '''
+ return len(self.hunks) == 1 and self.hunks[0].range.src_line_count == 0
+
+ def Parse(self, diff_lines):
+ ''' Parse a complete single-file patch.
+
+ Parse a complete patch for a single file, updating the local patch info
+ accordingly. Raise an Error exception if any inconsistency is detected.
+ Patch lines from diff_lines are consumed as they are matched. The first line
+ in diff_lines should be a header line starting with '---'.
+
+ Arguments:
+ diff_lines: a list diff lines read from the patch file.
+ '''
+ # Parse the patch header
+ self.header = _PatchHeader()
+ self.header.Parse(diff_lines)
+ # The next line must be a range.
+ if not diff_lines[0].startswith('@@'):
+ raise Error(context=diff_lines[0], error='Bad range info')
+
+ # Parse the range.
+ while diff_lines:
+ if diff_lines[0].startswith('@@'):
+ hunk = _ChangeHunk()
+ hunk.Parse(diff_lines)
+ self.hunks.append(hunk)
+ else:
+ break
+
+ def Apply(self, base_path):
+ ''' Apply the patch to the directory base_path.
+
+ Apply the single-file patch using base_path as the root directory.
+ The source file name, extracted from the header, is appended to base_path
+ to derive the full path to the file to be patched. The file is patched
+ in-place. That is, if the patching is successful, the source file path
+ will conatin the patched file when this method exits.
+
+ Argument:
+ base_path: path to the root directory.
+ '''
+ # Form an absolute path to the source file and verify that it exists if
+ # it is required.
+ in_path = os.path.abspath(
+ os.path.join(base_path, self.header.in_file_name))
+ if not os.path.exists(in_path) and not self._SrcFileIsOptional():
+ raise Error(context=in_path, error='Original file doesn\'t exists')
+
+ in_file = None
+ out_file = None
+ temp_out_path = None
+ try:
+ # Patch the input file into a temporary output file.
+ if os.path.exists(in_path):
+ in_file = open(in_path)
+ with tempfile.NamedTemporaryFile(delete=False) as out_file:
+ line_num = 0
+ # Apply all the change hunks.
+ for hunk in self.hunks:
+ line_num = hunk.Apply(line_num, in_file, out_file)
+
+ # We ran out of change hunks, but there may be lines left in the input
+ # file. Copy them to the output file.
+ if in_file:
+ for line in in_file:
+ out_file.write(line)
+
+ # Close the input and output files; retain the temporary output file
+ # path so we can rename it later.
+ temp_out_path = out_file.name
+ out_file.close()
+ out_file = None
+ if in_file:
+ in_file.close()
+ in_file = None
+
+ # Rename the output file to the original input file name. Windows
+ # forces us to delete the original file first. Linux is fussy about
+ # renaming a temp file. So we copy and delete instead.
+ if os.path.exists(in_path):
+ os.remove(in_path)
+ shutil.copy(temp_out_path, in_path)
+ os.remove(temp_out_path)
+ except:
+ raise
+ finally:
+ if in_file: in_file.close()
+ if out_file: out_file.close()
+ if temp_out_path and os.path.exists(temp_out_path):
+ os.remove(temp_out_path)
+
+
+class _PatchSet(object):
+ ''' All the patches from a patch file.
+
+ Encapsulates all the patches found in a patch file.
+ '''
+ def __init__(self):
+ self._patches = []
+
+ def Parse(self, patch_file):
+ ''' Parse all the patches in a patch file.
+
+ Parse all the patches from a given patch file and store each patch info
+ into list _patches. Raise an Error exception if an inconsistency is found.
+
+ Argument:
+ patch_file: a file descriptor for the patch file, ready for reading.
+ '''
+ diff_lines = patch_file.readlines()
+ while diff_lines:
+ # Look for the start of a header.
+ if diff_lines[0].startswith('---'):
+ patch = _Patch()
+ patch.Parse(diff_lines)
+ self._patches.append(patch)
+ else:
+ # Skip unrecognized lines.
+ diff_lines.pop(0)
+
+ def Apply(self, base_path):
+ ''' Apply all the patches.
+
+ Apply all the patches to files using base_path as the root directory.
+ Raise an Error exception if an inconsistency is found. Files are patched
+ in-place.
+
+ Argument:
+ base_path: path to the root directory in which to apply the patches.
+ '''
+ for patch in self._patches:
+ patch.Apply(base_path)
+
+
+class ApplyPatch(object):
+ ''' Read a patch fileand apply it to a given base directory.
+
+ ApplyPatch provides support for reading and parsing a patch file and
+ applying the patches to files relative to a given base directory.
+ '''
+
+ def __init__(self):
+ ''' Init ApplyPatch to empty. '''
+ self._patch_set = None
+
+ def Parse(self, patch_path):
+ ''' Parse a patch file.
+
+ Parse the patch file at path patch_path and store the patch info into
+ _patch_set. Parsing-error messages are sent to stderr.
+
+ Argument:
+ patch_path: path to the patch file.
+
+ Return:
+ True if the patch file is successfully read and ready to apply and False
+ otherwise.
+ '''
+ try:
+ if not os.path.exists(patch_path):
+ raise Error(context=patch_path, error="Patch file not found");
+ with open(patch_path) as patch_file:
+ self._patch_set = _PatchSet()
+ self._patch_set.Parse(patch_file)
+
+ except Error as (error):
+ sys.stderr.write('Patch error: %s.\n' % error.error)
+ sys.stderr.write('At line: %s\n' % error.context)
+ return False
+
+ return True
+
+ def Apply(self, base_path):
+ ''' Apply a patch to files.
+
+ Apply an entire set of patches to files using base_path as the base
+ directory. The files are patched in place. Patching-error message are output
+ to stderr.
+
+ Argument:
+ base_path: path to the base directory in which to apply the patches.
+
+ Return:
+ True if all the patches are applied successfully and False otherwise.
+ '''
+ try:
+ if not os.path.exists(base_path):
+ raise Error(context=base_path, error="Invalid base path");
+ if not os.path.isdir(base_path):
+ raise Error(context=base_path, error="Base path is not a directory");
+ self._patch_set.Apply(base_path)
+
+ except Error as (error):
+ sys.stderr.write('Processing error: %s.\n' % error.error)
+ sys.stderr.write('>>> %s\n' % error.context)
+ return False
+
+ return True
+
+
+def main(argv):
+ apply_patch = ApplyPatch()
+ if apply_patch.Parse(os.path.abspath(argv[0])):
+ if apply_patch.Apply(os.path.abspath(argv[1])):
+ sys.exit(0)
+
+ sys.exit(1)
+
+
+if __name__ == '__main__':
+ if len(sys.argv) != 3:
+ sys.stderr.write('Usage: %s patch-file base-dir\n' %
+ os.path.basename(sys.argv[0]))
+ sys.exit(1)
+ else:
+ main(sys.argv[1:])
+
diff --git a/native_client_sdk/src/build_tools/build.scons b/native_client_sdk/src/build_tools/build.scons
new file mode 100644
index 0000000..16d86f1
--- /dev/null
+++ b/native_client_sdk/src/build_tools/build.scons
@@ -0,0 +1,289 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Build file for running scripts in the build_tools directory
+
+Adapted from scons documentation: http://www.scons.org/wiki/UnitTests
+and from ../project_templates/test.scons
+"""
+
+__author__ = 'mball@google.com (Matt Ball)'
+
+from build_tools import build_utils
+from build_tools import make_nacl_tools
+from build_tools import make_sdk_tools
+import os
+import sys
+
+Import('env')
+
+# Add the system path to this environment so that we can run gclient and such
+env.AppendENVPath('PATH', os.environ['PATH'])
+# Duplicate all the shell environment variables, so that various Visual Studio
+# tools will have the variables they expect (e.g. VS 2008 needs VS90COMNTOOLS).
+# Do this using "if not in" instead of update(), so that os.environ variables
+# don't override variables in env['ENV'].
+for shell_var in os.environ:
+ if shell_var not in env['ENV']:
+ env['ENV'][shell_var] = os.environ[shell_var]
+
+
+def run_generate_installer(env, target, source):
+ '''Runs the generate_installer script to create the SDK installer'''
+ args = []
+ if env['IS_WINDOWS']:
+ from build_tools import generate_windows_installer
+ return generate_windows_installer.main(args)
+ else:
+ from build_tools import generate_installers
+ return generate_installers.main(args)
+
+installer_node = env.Command(
+ target='generate_installers_dummy_target.txt',
+ source='generate_installers.py',
+ action=run_generate_installer)
+
+env.Depends(installer_node, env.GetToolchainNode())
+env.Depends(installer_node, env.GetInstallerPrereqsNode())
+env.AddNodeAliases(installer_node, ['bot'], 'installer')
+
+install_file = 'nacl-sdk.exe' if sys.platform == 'win32' else 'nacl-sdk.tgz'
+installer = os.path.join(os.path.dirname(env['ROOT_DIR']), install_file)
+
+def BuildSdkTools(env, target, source):
+ make_sdk_tools.MakeSdkTools(str(target[0].abspath), str(target[1].abspath))
+
+sdk_tools_node = env.Command(
+ target=['nacl_sdk.zip', 'sdk_tools.tgz'],
+ source=[os.path.join('sdk_tools', 'sdk_update.py'),
+ 'make_sdk_tools.py',
+ os.path.join(env['ROOT_DIR'], 'LICENSE')],
+ action=BuildSdkTools)
+
+env.AddNodeAliases(sdk_tools_node, ['bot'], 'sdk_tools')
+
+installer_test_node = env.CreatePythonUnitTest(
+ 'tests/installer_test.py',
+ [installer_node if not env['USE_EXISTING_INSTALLER'] else installer],
+ buffered=False,
+ params=['--jobs=%s' % env['JOB_COUNT'],
+ '--outdir=%s' % os.path.join('scons-out', 'sdk_installer'),
+ '--nacl-sdk', env.File('nacl_sdk.zip'),
+ '--sdk-tools', env.File('sdk_tools.tgz'),
+ installer],
+ banner='test installer',)
+
+env.AddNodeToTestSuite(installer_test_node, ['bot'],
+ 'run_installer_test', 'large')
+
+env.Depends(env.GetInstallerTestNode(), installer_test_node)
+
+#------------------------------------------------------------------------------
+# Add build_tools unit tests
+
+def AddPythonUnitTest(command, file, size='small', **kwargs):
+ ''' Adds a new python unit test
+
+ Args:
+ command: The scons command to run this unit test
+ file: The python file containing the unit test
+ size: How big is this test? small, medium, or large
+ kwargs: See named parameters to CreatePythonUnitTest
+
+ returns:
+ test node for this newly created test'''
+ test_node = env.CreatePythonUnitTest(file, **kwargs)
+ env.AddNodeToTestSuite(test_node, ['bot'], command, size)
+ return test_node
+
+AddPythonUnitTest('run_build_utils_test', 'tests/build_utils_test.py')
+AddPythonUnitTest('run_tar_archive_test', 'tests/tar_archive_test.py')
+AddPythonUnitTest('run_update_manifest_test',
+ 'tests/update_manifest_test.py', size='medium')
+AddPythonUnitTest('run_sdk_update_test', 'tests/sdk_update_test.py')
+AddPythonUnitTest('run_set_nacl_env_test', 'tests/set_nacl_env_test.py',
+ dependencies=[env.GetToolchainNode()])
+AddPythonUnitTest('run_installer_contents_test',
+ 'tests/installer_contents_test.py')
+AddPythonUnitTest('run_nacl_utils_test', 'nacl_sdk_scons/nacl_utils_test.py')
+AddPythonUnitTest('run_path_set_test', 'tests/path_set_test.py')
+AddPythonUnitTest('run_apply_patch_test', 'tests/apply_patch_test.py')
+AddPythonUnitTest('run_nmf_test',
+ 'nacl_sdk_scons/nmf_test.py',
+ size='medium',
+ dependencies=[env.GetToolchainNode()],
+ params=['--toolchain-dir=%s' %
+ env['NACL_TOOLCHAIN_ROOTS'][('x86', 'glibc')]])
+AddPythonUnitTest('run_html_check_test',
+ 'html_checker.py',
+ params=[os.path.join(env['ROOT_DIR'], 'examples', name)
+ for name in ['index.html', 'index_staging.html']])
+
+if env['IS_WINDOWS']:
+ AddPythonUnitTest('run_install_nsis_test', 'tests/install_nsis_test.py')
+ AddPythonUnitTest('run_nsis_script_test', 'tests/nsis_script_test.py')
+
+
+#------------------------------------------------------------------------------
+# Put together the toolchain
+
+import gyp_extract
+
+ppapi_base = os.path.join(env['ROOT_DIR'],
+ 'third_party',
+ 'native_client',
+ 'ppapi')
+
+# Load ppapi_cpp.gypi
+ppapi_cpp_gypi = gyp_extract.LoadGypFile(os.path.join(ppapi_base,
+ 'ppapi_cpp.gypi'))
+
+# Load ppapi_gl.gypi
+ppapi_gl_gypi = gyp_extract.LoadGypFile(os.path.join(ppapi_base,
+ 'ppapi_gl.gypi'))
+
+# From ppapi_cpp.gypi:ppapi_c:c/[^/]*\.h
+c_headers = gyp_extract.GypTargetSources(
+ ppapi_cpp_gypi, 'ppapi_c', 'c/[^/]*\.h')
+
+# From ppapi_cpp.gypi:ppapi_c:c/dev/[^/]*\.h
+c_dev_headers = gyp_extract.GypTargetSources(
+ ppapi_cpp_gypi, 'ppapi_c', 'c/dev/[^/]*\.h')
+
+# From ppapi_cpp.gypi:ppapi_cpp_objects:cpp/[^/]*\.h
+# From ppapi_cpp.gypi:ppapi_cpp:cpp/[^/]*\.h
+cpp_headers = (
+ gyp_extract.GypTargetSources(
+ ppapi_cpp_gypi, 'ppapi_cpp_objects', 'cpp/[^/]*\.h') +
+ gyp_extract.GypTargetSources(
+ ppapi_cpp_gypi, 'ppapi_cpp', 'cpp/[^/]*\.h')
+)
+
+# From ppapi_cpp.gypi:ppapi_cpp_objects:cpp/dev/[^/]*\.h
+cpp_dev_headers = gyp_extract.GypTargetSources(
+ ppapi_cpp_gypi, 'ppapi_cpp_objects', 'cpp/dev/[^/]*\.h')
+
+# From ppapi_gl.gypi:ppapi_gles2:.*\.h
+gles2_headers = gyp_extract.GypTargetSources(
+ ppapi_gl_gypi, 'ppapi_gles2', '.*\.h')
+
+
+c_header_install = env.AddHeaderToSdk(
+ [os.path.join(ppapi_base, h) for h in c_headers], os.path.join('ppapi', 'c'))
+c_dev_header_install = env.AddHeaderToSdk(
+ [os.path.join(ppapi_base, h) for h in c_dev_headers],
+ os.path.join('ppapi', 'c', 'dev'))
+cpp_header_install = env.AddHeaderToSdk(
+ [os.path.join(ppapi_base, h) for h in cpp_headers],
+ os.path.join('ppapi', 'cpp'))
+cpp_dev_header_install = env.AddHeaderToSdk(
+ [os.path.join(ppapi_base, h) for h in cpp_dev_headers],
+ os.path.join('ppapi', 'cpp', 'dev'))
+
+# TODO(dspringer): Remove these lines when trusted ppapi builds are no longer
+# needed for debugging.
+# -------- 8< Cut here --------
+# From ppapi_cpp.gypi:ppapi_cpp_objects:cpp/[^/]*\.cc
+# From ppapi_cpp.gypi:ppapi_cpp:cpp/[^/]*\.cc
+cpp_trusted_sources = (
+ gyp_extract.GypTargetSources(
+ ppapi_cpp_gypi, 'ppapi_cpp_objects', 'cpp/[^/]*\.cc') +
+ gyp_extract.GypTargetSources(
+ ppapi_cpp_gypi, 'ppapi_cpp', 'cpp/[^/]*\.cc')
+)
+
+cpp_trusted_source_install = env.AddHeaderToSdk(
+ [os.path.join(ppapi_base, cpp) for cpp in cpp_trusted_sources],
+ subdir=os.path.join('ppapi', 'cpp'),
+ base_dirs=[os.path.join(env['ROOT_DIR'], 'third_party')])
+# -------- 8< Cut here --------
+
+#env.AddLibraryToSdk(['ppapi_cpp'])
+env.Requires('ppapi_cpp', [c_header_install,
+ c_dev_header_install,
+ cpp_header_install,
+ cpp_dev_header_install,
+ # TODO(dspringer): Remove this when trusted ppapi
+ # builds are no longer needed.
+ cpp_trusted_source_install])
+
+# GLES2 headers go into the GLES2 subdir, Khronos headers into the KHR subdir,
+# EGL header go in the EGL subdir.
+gl_base = os.path.join(ppapi_base, 'lib', 'gl', 'include')
+egl_base = os.path.join(gl_base, 'EGL')
+egl_header_install = env.AddHeaderToSdk([
+ os.path.join(egl_base, 'egl.h'),
+ os.path.join(egl_base, 'eglext.h'),
+ os.path.join(egl_base, 'eglplatform.h'),
+ ], 'EGL')
+
+gles2_base = os.path.join(gl_base, 'GLES2')
+gles2_header_install = env.AddHeaderToSdk([
+ os.path.join(gles2_base, 'gl2.h'),
+ os.path.join(gles2_base, 'gl2ext.h'),
+ os.path.join(gles2_base, 'gl2platform.h'),
+ ], 'GLES2')
+khr_header_install = env.AddHeaderToSdk([
+ os.path.join(gl_base, 'KHR', 'khrplatform.h'),
+ ], 'KHR')
+ppapi_gles2_header_install = env.AddHeaderToSdk(
+ [os.path.join(ppapi_base, h) for h in gles2_headers],
+ os.path.join('ppapi', 'gles2'))
+#env.AddLibraryToSdk(['ppapi_gles2'])
+env.Requires('ppapi_gles2', [
+ egl_header_install,
+ gles2_header_install,
+ khr_header_install,
+ ppapi_gles2_header_install,
+])
+
+
+#------------------------------------------------------------------------------
+# Build Native Client components that are not included in the toolchain zip
+
+def build_nacl_tools(env, target, source):
+ ''' Tool for running make_nacl_tools
+ This builds sel_ldr, ncval, and the nacl libraries'''
+ build_utils.BotAnnotator().BuildStep('build NaCl tools')
+ for key, toolchain in env['NACL_TOOLCHAIN_ROOTS'].items():
+ (_, variant) = key
+ make_nacl_tools_args = [
+ '--toolchain',
+ toolchain,
+ '--jobs',
+ env['JOB_COUNT'],
+ '--nacl_dir',
+ os.path.join(env['ROOT_DIR'], 'third_party', 'native_client'),
+ ]
+ if env.GetOption('clean'):
+ make_nacl_tools_args.extend(['--clean'])
+ args = make_nacl_tools_args + ['--lib=%s' % variant]
+ print 'Running make_nacl_tools with ', args
+ sys.stdout.flush()
+ make_nacl_tools.main(args)
+
+exe = '.exe' if sys.platform in ['cygwin', 'win32'] else ''
+
+tools = [
+ os.path.join('bin', 'sel_ldr_x86_32%s' % exe),
+ os.path.join('bin', 'sel_ldr_x86_64%s' % exe),
+ os.path.join('bin', 'ncval_x86_32%s' % exe),
+ os.path.join('bin', 'ncval_x86_64%s' % exe),
+ os.path.join('runtime', 'irt_core_x86_32.nexe'),
+ os.path.join('runtime', 'irt_core_x86_64.nexe'),
+ ]
+
+all_tools = []
+for dir in env['NACL_TOOLCHAIN_ROOTS'].values():
+ all_tools += [os.path.join(dir, tool) for tool in tools]
+
+nacl_tools_cmd = env.Command(all_tools,
+ ['make_nacl_tools.py',
+ os.path.join(env['ROOT_DIR'], 'DEPS')],
+ build_nacl_tools)
+env.Depends(nacl_tools_cmd, env.GetHeadersNode())
+env.Depends(env.GetToolchainNode(), nacl_tools_cmd)
+env.AddCleanAction([], build_nacl_tools, ['toolchain', 'bot'], nacl_tools_cmd)
diff --git a/native_client_sdk/src/build_tools/build_utils.py b/native_client_sdk/src/build_tools/build_utils.py
new file mode 100644
index 0000000..4f92e03
--- /dev/null
+++ b/native_client_sdk/src/build_tools/build_utils.py
@@ -0,0 +1,307 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Small utility library of python functions used by the various package
+installers.
+"""
+
+import datetime
+import errno
+import fileinput
+import os
+import platform
+import re
+import shutil
+import subprocess
+import sys
+
+from nacl_sdk_scons import nacl_utils
+
+
+def ChromeMilestone():
+ '''Extract chrome_milestone from src/DEPS
+
+ Returns:
+ Chrome milestone variable value from src/DEPS.
+ '''
+ parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+ def From(x, y):
+ return ''
+ def Var(x):
+ return ''
+ def File(x):
+ return ''
+ with open(os.path.join(parent_dir, 'DEPS'), 'r') as fh:
+ exec(fh.read())
+ return vars.get('chrome_milestone')
+
+
+#------------------------------------------------------------------------------
+# Parameters
+
+# Revision numbers for the SDK
+PLATFORM_VERSION = 'pepper_' + ChromeMilestone()
+
+TOOLCHAIN_AUTODETECT = "AUTODETECT"
+
+#------------------------------------------------------------------------------
+# Functions
+
+# Make all the directories in |abs_path|. If |abs_path| points to a regular
+# file, it is removed before an attempt to make the directories. If |abs_path|
+# already points to a directory, this method does nothing.
+def ForceMakeDirs(abs_path, mode=0755):
+ if os.path.isdir(abs_path):
+ return
+ try:
+ # Remove an existing regular file; ignore errors (e.g. file doesn't exist).
+ # If there are permission problems, they will be caught by the exception
+ # handler around the os.makedirs call.
+ os.remove(abs_path)
+ except:
+ pass
+ try:
+ os.makedirs(abs_path, mode)
+ except OSError, (os_errno, os_strerr):
+ # If the error is anything but EEXIST (file already exists), then print an
+ # informative message and re-raise. It is not and error if the directory
+ # already exists.
+ if os_errno != errno.EEXIST:
+ print 'ForceMakeDirs(%s, 0%o) FAILED: %s' % (abs_path, mode, os_strerr)
+ raise
+ pass
+
+
+# patch version 2.6 doesn't work. Most of our Linux distros use patch 2.6.
+# Returns |True| if the version of patch is usable (that is, not version 2.6).
+# |shell_env| is the enviromnent used to run the subprocesses like patch and
+# sed. If |shell_env| is None, then os.environ is used.
+def CheckPatchVersion(shell_env=None):
+ if shell_env is None:
+ shell_env = os.environ
+ patch = subprocess.Popen("patch --version",
+ shell=True,
+ env=shell_env,
+ stdout=subprocess.PIPE)
+ sed = subprocess.Popen("sed q",
+ shell=True,
+ env=shell_env,
+ stdin=patch.stdout,
+ stdout=subprocess.PIPE)
+ sed_output = sed.communicate()[0]
+ if sed_output.strip() == 'patch 2.6':
+ print "patch 2.6 is incompatible with these scripts."
+ print "Please install either version 2.5.9 (or earlier)"
+ print "or version 2.6.1 (or later)."
+ return False
+ return True
+
+
+# Build a toolchain path based on the platform type. |base_dir| is the root
+# directory which includes the platform-specific toolchain. This could be
+# something like "/usr/local/mydir/nacl_sdk/src". If |base_dir| is None, then
+# the environment variable NACL_SDK_ROOT is used (if it's set).
+# This method assumes that the platform-specific toolchain is found under
+# <base_dir>/toolchain/<platform_variant>.
+def NormalizeToolchain(toolchain=TOOLCHAIN_AUTODETECT,
+ base_dir=None,
+ arch=nacl_utils.DEFAULT_TOOLCHAIN_ARCH,
+ variant=nacl_utils.DEFAULT_TOOLCHAIN_VARIANT):
+ if toolchain == TOOLCHAIN_AUTODETECT:
+ if base_dir is None:
+ base_dir = os.getenv('NACL_SDK_ROOT', '')
+ normalized_toolchain = nacl_utils.ToolchainPath(base_dir=base_dir,
+ arch=arch,
+ variant=variant)
+ else:
+ normalized_toolchain = os.path.abspath(toolchain)
+ return normalized_toolchain
+
+
+def SupportedNexeBitWidths():
+ '''Return a list of .nexe bit widths that are supported by the host.
+
+ Each supported bit width means the host can run a .nexe with the corresponding
+ instruction set architecture. For example, if this function returns the
+ list [32, 64], then the host can run both 32- and 64-bit .nexes.
+
+ Note: on Windows, environment variables are used to determine the host's bit
+ width instead of the |platform| package. This is because (up until python
+ 2.7) the |platform| package returns the bit-width used to build python, not
+ the host's bit width.
+
+ Returns: A list of supported nexe word widths (in bits) supported by the host
+ (typically 32 or 64). Returns an empty list if the word_width cannot be
+ determined.
+ '''
+ bit_widths = []
+ if sys.platform == 'win32':
+ # On Windows, the best way to detect the word size is to look at these
+ # env vars. python 2.6 and earlier on Windows (in particular the
+ # python on the Windows bots) generally always say they are 32-bits,
+ # even though the host is 64-bits. See this thread for more:
+ # http://stackoverflow.com/questions/7164843/
+ # in-python-how-do-you-determine-whether-the-kernel-is-running-in-\
+ # 32-bit-or-64-bit
+ if ('64' in os.environ.get('PROCESSOR_ARCHITECTURE', '') or
+ '64' in os.environ.get('PROCESSOR_ARCHITEW6432', '')):
+ bit_widths = [64]
+ else:
+ bit_widths = [32]
+ elif sys.platform == 'darwin':
+ # Mac can handle 32- and 64-bit .nexes.
+ bit_widths = [32, 64]
+ else:
+ # Linux 64 can handle both 32- and 64-bit.
+ machine = platform.machine()
+ bit_widths = [32, 64] if '64' in machine else [32]
+
+ return bit_widths
+
+
+def RawVersion():
+ '''Returns the Raw version number of the SDK in a dotted format'''
+ return '.'.join(GetVersionNumbers())
+
+
+def GetVersionNumbers():
+ '''Returns a list of 3 strings containing the version identifier'''
+ rev = str(SVNRevision())
+ return [PLATFORM_VERSION, rev]
+
+
+def SVNRevision():
+ '''Returns the Subversion revision of this file.
+
+ This file either needs to be in either a subversion repository or
+ a git repository that is sync'd to a subversion repository using git-svn.'''
+ run_path = os.path.dirname(os.path.abspath(__file__))
+ p = subprocess.Popen('svn info', shell=True, stdout=subprocess.PIPE,
+ cwd=run_path)
+ if p.wait() != 0:
+ p = subprocess.Popen('git svn info', shell=True, stdout=subprocess.PIPE,
+ cwd=run_path)
+ if p.wait() != 0:
+ raise AssertionError('Cannot determine SVN revision of this repository');
+
+ svn_info = p.communicate()[0]
+ m = re.search('Revision: ([0-9]+)', svn_info)
+ if m:
+ return int(m.group(1))
+ else:
+ raise AssertionError('Cannot extract revision number from svn info')
+
+
+def VersionString():
+ '''Returns the version of native client based on the svn revision number.'''
+ return 'native_client_sdk_%s' % '_'.join(GetVersionNumbers())
+
+
+def JoinPathToNaClRepo(*args, **kwargs):
+ '''Use os.path.join() to join the argument list to the NaCl repo location.
+
+ Assumes that the Native Client repo is DEPSed into this repo under
+ third_party/native_client. This has to match the target dirs in the DEPS
+ file.
+
+ If the key 'root_dir' is set, then this path is prepended to the NaCl repo
+ path.
+
+ Args:
+ args: A list of path elements to append to the NaCl repo root.
+ kwargs: If the 'root_dir' key is present, this gets prepended to the
+ final path.
+
+ Return: An OS-native path to the DEPSed in root of the NaCl repo.
+ '''
+ nacl_path = os.path.join('third_party', 'native_client', *args)
+ root_path = kwargs.get('root_dir')
+ return os.path.join(root_path, nacl_path) if root_path else nacl_path
+
+
+class BotAnnotator:
+ '''Interface to Bot Annotations
+
+ See http://www.chromium.org/developers/testing/chromium-build-infrastructure/buildbot-annotations
+ '''
+
+ def __init__(self, stream=sys.stdout):
+ self._stream = stream
+
+ def Print(self, message):
+ '''Display a message to the output stream and flush so the bots see it'''
+ self._stream.write("%s\n" % message)
+ self._stream.flush()
+
+ def BuildStep(self, name):
+ self.Print("BUILD_STEP %s" % name)
+ # mball: Disabling buildbot annotations because it's more confusing than
+ # useful when running multiple jobs simultaneously. To re-enable
+ # annotations, using the following line instead of the previous:
+ # self.Print("@@@BUILD_STEP %s@@@" % name)
+
+ def BuildStepFailure(self):
+ '''Signal a failure in the current build step to the annotator'''
+ self.Print("@@@STEP_FAILURE@@@")
+
+ def Run(self, *popenargs, **kwargs):
+ '''Implements the functionality of subprocess.check_output, but also
+ prints out the command-line and the command output.
+
+ Do not set stdout to anything because this function will redirect it
+ using a pipe.
+
+ Arguments:
+ See subprocess.Popen
+
+ returns:
+ a string containing the command output
+ '''
+ if 'stdout' in kwargs or 'stderr' in kwargs:
+ raise ValueError('stdout or stderr argument not allowed.')
+ command = kwargs.get("args")
+ if command is None:
+ command = popenargs[0]
+ self.Print('Running %s' % command)
+ process = subprocess.Popen(stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ *popenargs,
+ **kwargs)
+ output, error_output = process.communicate()
+ if error_output:
+ self.Print("%s\nStdErr for %s:\n%s" % (output, command, error_output))
+ else:
+ self.Print(output)
+
+ retcode = process.poll() # Note - calling wait() can cause a deadlock
+ if retcode != 0:
+ raise subprocess.CalledProcessError(retcode, command)
+ return output
+
+ #TODO(mball) Add the other possible build annotations, as needed
+
+
+def UpdateReadMe(filename):
+ '''Updates the README file in the SDK with the current date and version'''
+
+ for line in fileinput.input(filename, inplace=1):
+ sys.stdout.write(line.replace('${VERSION}', PLATFORM_VERSION)
+ .replace('${REVISION}', str(SVNRevision()))
+ .replace('${DATE}', str(datetime.date.today())))
+
+def CleanDirectory(dir):
+ '''Cleans all the contents of a given directory.
+ This works even when there are Windows Junctions in the directory
+
+ Args:
+ dir: The directory to clean
+ '''
+ if sys.platform != 'win32':
+ shutil.rmtree(dir, ignore_errors=True)
+ else:
+ # Intentionally ignore return value since a directory might be in use.
+ subprocess.call(['rmdir', '/Q', '/S', dir],
+ env=os.environ.copy(),
+ shell=True)
diff --git a/native_client_sdk/src/build_tools/buildbot_run.py b/native_client_sdk/src/build_tools/buildbot_run.py
new file mode 100755
index 0000000..29ea596
--- /dev/null
+++ b/native_client_sdk/src/build_tools/buildbot_run.py
@@ -0,0 +1,78 @@
+#!/usr/bin/python2.6
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''Entry point for both build and try bots'''
+
+import os
+import subprocess
+import sys
+
+# Add scons to the python path (as nacl_utils.py requires it).
+PARENT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+sys.path.append(os.path.join(PARENT_DIR, 'third_party/scons-2.0.1/engine'))
+
+import build_utils
+
+
+def Archive(revision, chrome_milestone):
+ """Archive the sdk to google storage.
+
+ Args:
+ revision: SDK svn revision number.
+ chrome_milestone: Chrome milestone (m14 etc), this is for.
+ """
+ if sys.platform in ['cygwin', 'win32']:
+ src = 'nacl-sdk.exe'
+ dst = 'naclsdk_win.exe'
+ elif sys.platform in ['darwin']:
+ src = 'nacl-sdk.tgz'
+ dst = 'naclsdk_mac.tgz'
+ else:
+ src = 'nacl-sdk.tgz'
+ dst = 'naclsdk_linux.tgz'
+ bucket_path = 'nativeclient-mirror/nacl/nacl_sdk/pepper_%s_%s/%s' % (
+ chrome_milestone, revision, dst)
+ full_dst = 'gs://%s' % bucket_path
+ subprocess.check_call(
+ '/b/build/scripts/slave/gsutil cp -a public-read %s %s' % (
+ src, full_dst), shell=True)
+ url = 'https://commondatastorage.googleapis.com/%s' % bucket_path
+ print '@@@STEP_LINK@download@%s@@@' % url
+ sys.stdout.flush()
+
+
+def main(argv):
+ if sys.platform in ['cygwin', 'win32']:
+ # Windows build
+ command = 'scons.bat'
+ else:
+ # Linux and Mac build
+ command = 'scons'
+
+ parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+ params = [os.path.join(parent_dir, command)] + argv
+ def Run(parameters):
+ print '\nRunning ', parameters
+ sys.stdout.flush()
+ subprocess.check_call(' '.join(parameters), shell=True, cwd=parent_dir)
+
+ print '@@@BUILD_STEP generate sdk@@@'
+ sys.stdout.flush()
+
+ Run(params + ['-c'])
+ Run(params + ['bot'])
+
+ # Archive on non-trybots.
+ if '-sdk' in os.environ.get('BUILDBOT_BUILDERNAME', ''):
+ print '@@@BUILD_STEP archive build@@@'
+ sys.stdout.flush()
+ Archive(revision=os.environ.get('BUILDBOT_GOT_REVISION'),
+ chrome_milestone=build_utils.ChromeMilestone())
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/build.scons b/native_client_sdk/src/build_tools/debug_server/debug_stub/build.scons
new file mode 100644
index 0000000..1fb4741
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/build.scons
@@ -0,0 +1,37 @@
+# -*- python -*-
+# Copyright 2010 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can
+# be found in the LICENSE file.
+
+# This file needs to be in sync with $SOURCE_ROOT/ppapi/ppapi.gyp
+# at the revision specified in $SOURCE_ROOT/native_client/DEPS.
+
+Import('env')
+
+debug_sources = [
+ 'debug_stub.cc',
+ 'event_common.cc',
+ 'platform_common.cc',
+ 'transport_common.cc',
+ ]
+
+# TODO(noelallen)
+# Still need to add Linux and Mac implementation. Move "common" to
+# above section once their dependancies are satisfied.
+if env.Bit('windows'):
+ debug_sources += [
+ 'win/debug_stub_win.cc',
+ 'win/mutex_impl.cc',
+ 'win/platform_impl.cc',
+ 'win/thread_impl.cc',
+ ]
+else:
+ debug_sources += [
+ 'posix/debug_stub_posix.cc',
+ 'posix/mutex_impl.cc',
+ 'posix/platform_impl.cc',
+ 'posix/thread_impl.cc',
+ ]
+
+
+env.DualLibrary('debug_stub', debug_sources)
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.cc b/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.cc
new file mode 100644
index 0000000..1eb9770
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.cc
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include "native_client/src/debug_server/debug_stub/debug_stub.h"
+
+void NaClDebugStubInit() {
+ NaClDebugStubPlatformInit();
+}
+
+void NaClDebugStubFini() {
+ NaClDebugStubPlatformFini();
+}
+
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.gyp b/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.gyp
new file mode 100644
index 0000000..c53ff04
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.gyp
@@ -0,0 +1,170 @@
+#
+# Copyright 2010 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can
+# be found in the LICENSE file.
+#
+{
+ 'includes': [
+ '../../../build/common.gypi',
+ ],
+ 'variables': {
+ 'common_sources': [
+ 'debug_stub.h',
+ 'debug_stub.cc',
+ 'event_common.cc',
+ 'platform_common.cc',
+ 'transport_common.cc',
+ ],
+ 'conditions': [
+ ['OS=="linux" or OS=="mac"', {
+ 'platform_sources': [
+ 'posix/debug_stub_posix.cc',
+ 'posix/mutex_impl.cc',
+ 'posix/platform_impl.cc',
+ 'posix/thread_impl.cc',
+ ],
+ }],
+ ['OS=="win"', {
+ 'platform_sources': [
+ 'win/debug_stub_win.cc',
+ 'win/mutex_impl.cc',
+ 'win/platform_impl.cc',
+ 'win/thread_impl.cc',
+ ],
+ }],
+ ],
+ },
+
+ 'target_defaults': {
+ 'variables': {
+ 'target_base': 'none',
+ },
+ 'target_conditions': [
+ ['OS=="linux" or OS=="mac"', {
+ 'cflags': [
+ '-Wno-long-long',
+ ],
+ }],
+ ['target_base=="debug_stub"', {
+ 'sources': [
+ '<@(common_sources)',
+ '<@(platform_sources)',
+ ],
+ }],
+ ['target_base=="debug_stub_test"', {
+ 'sources': [
+ 'debug_stub_test.cc',
+ ],
+ }],
+ ],
+ },
+ 'targets': [
+ # ----------------------------------------------------------------------
+ {
+ 'target_name': 'debug_stub',
+ 'type': 'static_library',
+ 'variables': {
+ 'target_base': 'debug_stub',
+ },
+ 'dependencies': [
+ '<(DEPTH)/native_client/src/trusted/gdb_rsp/gdb_rsp.gyp:gdb_rsp',
+ ],
+ },
+ # ---------------------------------------------------------------------
+ {
+ 'target_name': 'debug_stub_test',
+ 'type': 'executable',
+ 'variables': {
+ 'target_base': 'debug_stub_test',
+ },
+ 'dependencies': [
+ 'debug_stub',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS=="win"', {
+ 'targets': [
+ # ---------------------------------------------------------------------
+ {
+ 'target_name': 'debug_stub64',
+ 'type': 'static_library',
+ 'variables': {
+ 'target_base': 'debug_stub',
+ 'win_target': 'x64',
+ },
+ 'dependencies': [
+ '<(DEPTH)/native_client/src/trusted/gdb_rsp/gdb_rsp.gyp:gdb_rsp64',
+ ],
+ },
+ # ---------------------------------------------------------------------
+ {
+ 'target_name': 'debug_stub_test64',
+ 'type': 'executable',
+ 'variables': {
+ 'target_base': 'debug_stub_test',
+ 'win_target': 'x64',
+ },
+ 'dependencies': [
+ 'debug_stub64',
+ ],
+ },
+ # ---------------------------------------------------------------------
+ {
+ 'target_name': 'run_debug_stub_test',
+ 'message': 'running test run_imc_tests',
+ 'type': 'none',
+ 'dependencies': [
+ 'debug_stub_test',
+ 'debug_stub_test64',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'run_debug_stub_test',
+ 'msvs_cygwin_shell': 0,
+ 'inputs': [
+ '<(DEPTH)/native_client/tests/debug_stub/test_debug_stub.py',
+ '<(PRODUCT_DIR)/debug_stub_test',
+ ],
+ 'outputs': [
+ '<(PRODUCT_DIR)/test-output/debug_stub_test.out',
+ ],
+ 'action': [
+ '<@(python_exe)',
+ '<(DEPTH)/native_client/tests/debug_stub/test_debug_stub.py',
+ '<(PRODUCT_DIR)/debug_stub_test',
+ '>',
+ '<@(_outputs)',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['MSVS_OS_BITS==64', {
+ 'actions': [
+ {
+ 'action_name': 'run_debug_stub_test64',
+ 'msvs_cygwin_shell': 0,
+ 'inputs': [
+ '<(DEPTH)/native_client/tests/debug_stub/test_debug_stub.py',
+ '<(PRODUCT_DIR)/debug_stub_test',
+ ],
+ 'outputs': [
+ '<(PRODUCT_DIR)/test-output/debug_stub_test.out',
+ ],
+ 'action': [
+ '<@(python_exe)',
+ '<(DEPTH)/native_client/tests/debug_stub/test_debug_stub.py',
+ '<(PRODUCT_DIR)/debug_stub_test64',
+ '>',
+ '<@(_outputs)',
+ ],
+ },
+ ],
+ }],
+ ],
+ },
+ ],
+ }],
+ ],
+}
+
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.h b/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.h
new file mode 100644
index 0000000..7d94d72
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#ifndef NATIVE_CLIENT_DEBUG_STUB_DEBUG_STUB_H_
+#define NATIVE_CLIENT_DEBUG_STUB_DEBUG_STUB_H_
+
+#include "native_client/src/include/nacl_base.h"
+
+EXTERN_C_BEGIN
+
+void NaClDebugStubInit();
+void NaClDebugStubFini();
+
+/*
+ * Platform-specific init/fini functions
+ */
+void NaClDebugStubPlatformInit();
+void NaClDebugStubPlatformFini();
+
+EXTERN_C_END
+
+#endif /* NATIVE_CLIENT_DEBUG_STUB_DEBUG_STUB_H_ */
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.rules b/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.rules
new file mode 100644
index 0000000..3ca1cae
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.rules
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioToolFile Name="debug_stub" Version="8.00">
+ <Rules>
+ <CustomBuildRule AdditionalDependencies="..\..\..\..\native_client\tools\win_as.py;$(InputPath)" CommandLine="..\..\..\..\native_client\tools\win_py.cmd ..\..\..\..\native_client\tools\win_as.py -a $(PlatformName) -o $(IntDir)\$(InputName).obj -p ..\..\..\.. $(InputPath)" ExecutionDescription="Building assembly language file $(InputPath)" FileExtensions="S" Name="assembler (gnu-compatible)" Outputs="$(IntDir)\$(InputName).obj"/>
+ </Rules>
+</VisualStudioToolFile>
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.sln b/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.sln
new file mode 100644
index 0000000..b399c1b
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub.sln
@@ -0,0 +1,110 @@
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "(debug_stub)", "debug_stub", "{C44F267D-CA3D-2707-9AF9-6331B7BDD7F3}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "(gdb_rsp)", "gdb_rsp", "{36DDA403-5397-3B36-4C08-47C4ED351C15}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "debug_stub", "debug_stub.vcproj", "{D9221BEA-DBFE-4677-4909-B88BA2E5A104}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "debug_stub64", "debug_stub64.vcproj", "{C3E9609B-A809-EF38-CA53-2962E844062E}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "debug_stub_test", "debug_stub_test.vcproj", "{216DF189-70D2-3010-9D62-6AD9D13C62FD}"
+ ProjectSection(ProjectDependencies) = postProject
+ {D9221BEA-DBFE-4677-4909-B88BA2E5A104} = {D9221BEA-DBFE-4677-4909-B88BA2E5A104}
+ {0F659521-D577-1DFD-46FC-6DBEE3E1DFD6} = {0F659521-D577-1DFD-46FC-6DBEE3E1DFD6}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "debug_stub_test64", "debug_stub_test64.vcproj", "{59F612B2-1E67-81E5-6F41-F56978638136}"
+ ProjectSection(ProjectDependencies) = postProject
+ {C3E9609B-A809-EF38-CA53-2962E844062E} = {C3E9609B-A809-EF38-CA53-2962E844062E}
+ {78AAD972-57EA-27B4-5F13-3CD2B7E8ABFC} = {78AAD972-57EA-27B4-5F13-3CD2B7E8ABFC}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gdb_rsp", "..\gdb_rsp\gdb_rsp.vcproj", "{0F659521-D577-1DFD-46FC-6DBEE3E1DFD6}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gdb_rsp64", "..\gdb_rsp\gdb_rsp64.vcproj", "{78AAD972-57EA-27B4-5F13-3CD2B7E8ABFC}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "run_debug_stub_test", "run_debug_stub_test.vcproj", "{B7A7E685-394E-D4CF-B572-8A968A5E46AC}"
+ ProjectSection(ProjectDependencies) = postProject
+ {216DF189-70D2-3010-9D62-6AD9D13C62FD} = {216DF189-70D2-3010-9D62-6AD9D13C62FD}
+ {59F612B2-1E67-81E5-6F41-F56978638136} = {59F612B2-1E67-81E5-6F41-F56978638136}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Release|x64 = Release|x64
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {0F659521-D577-1DFD-46FC-6DBEE3E1DFD6}.Release|x64.ActiveCfg = Release|x64
+ {0F659521-D577-1DFD-46FC-6DBEE3E1DFD6}.Release|x64.Build.0 = Release|x64
+ {0F659521-D577-1DFD-46FC-6DBEE3E1DFD6}.Debug|Win32.ActiveCfg = Debug|Win32
+ {0F659521-D577-1DFD-46FC-6DBEE3E1DFD6}.Debug|Win32.Build.0 = Debug|Win32
+ {0F659521-D577-1DFD-46FC-6DBEE3E1DFD6}.Debug|x64.ActiveCfg = Debug|x64
+ {0F659521-D577-1DFD-46FC-6DBEE3E1DFD6}.Debug|x64.Build.0 = Debug|x64
+ {0F659521-D577-1DFD-46FC-6DBEE3E1DFD6}.Release|Win32.ActiveCfg = Release|Win32
+ {0F659521-D577-1DFD-46FC-6DBEE3E1DFD6}.Release|Win32.Build.0 = Release|Win32
+ {216DF189-70D2-3010-9D62-6AD9D13C62FD}.Release|x64.ActiveCfg = Release|x64
+ {216DF189-70D2-3010-9D62-6AD9D13C62FD}.Release|x64.Build.0 = Release|x64
+ {216DF189-70D2-3010-9D62-6AD9D13C62FD}.Debug|Win32.ActiveCfg = Debug|Win32
+ {216DF189-70D2-3010-9D62-6AD9D13C62FD}.Debug|Win32.Build.0 = Debug|Win32
+ {216DF189-70D2-3010-9D62-6AD9D13C62FD}.Debug|x64.ActiveCfg = Debug|x64
+ {216DF189-70D2-3010-9D62-6AD9D13C62FD}.Debug|x64.Build.0 = Debug|x64
+ {216DF189-70D2-3010-9D62-6AD9D13C62FD}.Release|Win32.ActiveCfg = Release|Win32
+ {216DF189-70D2-3010-9D62-6AD9D13C62FD}.Release|Win32.Build.0 = Release|Win32
+ {59F612B2-1E67-81E5-6F41-F56978638136}.Release|x64.ActiveCfg = Release|x64
+ {59F612B2-1E67-81E5-6F41-F56978638136}.Release|x64.Build.0 = Release|x64
+ {59F612B2-1E67-81E5-6F41-F56978638136}.Debug|Win32.ActiveCfg = Debug|x64
+ {59F612B2-1E67-81E5-6F41-F56978638136}.Debug|Win32.Build.0 = Debug|x64
+ {59F612B2-1E67-81E5-6F41-F56978638136}.Debug|x64.ActiveCfg = Debug|x64
+ {59F612B2-1E67-81E5-6F41-F56978638136}.Debug|x64.Build.0 = Debug|x64
+ {59F612B2-1E67-81E5-6F41-F56978638136}.Release|Win32.ActiveCfg = Release|x64
+ {59F612B2-1E67-81E5-6F41-F56978638136}.Release|Win32.Build.0 = Release|x64
+ {78AAD972-57EA-27B4-5F13-3CD2B7E8ABFC}.Release|x64.ActiveCfg = Release|x64
+ {78AAD972-57EA-27B4-5F13-3CD2B7E8ABFC}.Release|x64.Build.0 = Release|x64
+ {78AAD972-57EA-27B4-5F13-3CD2B7E8ABFC}.Debug|Win32.ActiveCfg = Debug|x64
+ {78AAD972-57EA-27B4-5F13-3CD2B7E8ABFC}.Debug|Win32.Build.0 = Debug|x64
+ {78AAD972-57EA-27B4-5F13-3CD2B7E8ABFC}.Debug|x64.ActiveCfg = Debug|x64
+ {78AAD972-57EA-27B4-5F13-3CD2B7E8ABFC}.Debug|x64.Build.0 = Debug|x64
+ {78AAD972-57EA-27B4-5F13-3CD2B7E8ABFC}.Release|Win32.ActiveCfg = Release|x64
+ {78AAD972-57EA-27B4-5F13-3CD2B7E8ABFC}.Release|Win32.Build.0 = Release|x64
+ {B7A7E685-394E-D4CF-B572-8A968A5E46AC}.Release|x64.ActiveCfg = Release|x64
+ {B7A7E685-394E-D4CF-B572-8A968A5E46AC}.Release|x64.Build.0 = Release|x64
+ {B7A7E685-394E-D4CF-B572-8A968A5E46AC}.Debug|Win32.ActiveCfg = Debug|Win32
+ {B7A7E685-394E-D4CF-B572-8A968A5E46AC}.Debug|Win32.Build.0 = Debug|Win32
+ {B7A7E685-394E-D4CF-B572-8A968A5E46AC}.Debug|x64.ActiveCfg = Debug|x64
+ {B7A7E685-394E-D4CF-B572-8A968A5E46AC}.Debug|x64.Build.0 = Debug|x64
+ {B7A7E685-394E-D4CF-B572-8A968A5E46AC}.Release|Win32.ActiveCfg = Release|Win32
+ {B7A7E685-394E-D4CF-B572-8A968A5E46AC}.Release|Win32.Build.0 = Release|Win32
+ {C3E9609B-A809-EF38-CA53-2962E844062E}.Release|x64.ActiveCfg = Release|x64
+ {C3E9609B-A809-EF38-CA53-2962E844062E}.Release|x64.Build.0 = Release|x64
+ {C3E9609B-A809-EF38-CA53-2962E844062E}.Debug|Win32.ActiveCfg = Debug|x64
+ {C3E9609B-A809-EF38-CA53-2962E844062E}.Debug|Win32.Build.0 = Debug|x64
+ {C3E9609B-A809-EF38-CA53-2962E844062E}.Debug|x64.ActiveCfg = Debug|x64
+ {C3E9609B-A809-EF38-CA53-2962E844062E}.Debug|x64.Build.0 = Debug|x64
+ {C3E9609B-A809-EF38-CA53-2962E844062E}.Release|Win32.ActiveCfg = Release|x64
+ {C3E9609B-A809-EF38-CA53-2962E844062E}.Release|Win32.Build.0 = Release|x64
+ {D9221BEA-DBFE-4677-4909-B88BA2E5A104}.Release|x64.ActiveCfg = Release|x64
+ {D9221BEA-DBFE-4677-4909-B88BA2E5A104}.Release|x64.Build.0 = Release|x64
+ {D9221BEA-DBFE-4677-4909-B88BA2E5A104}.Debug|Win32.ActiveCfg = Debug|Win32
+ {D9221BEA-DBFE-4677-4909-B88BA2E5A104}.Debug|Win32.Build.0 = Debug|Win32
+ {D9221BEA-DBFE-4677-4909-B88BA2E5A104}.Debug|x64.ActiveCfg = Debug|x64
+ {D9221BEA-DBFE-4677-4909-B88BA2E5A104}.Debug|x64.Build.0 = Debug|x64
+ {D9221BEA-DBFE-4677-4909-B88BA2E5A104}.Release|Win32.ActiveCfg = Release|Win32
+ {D9221BEA-DBFE-4677-4909-B88BA2E5A104}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {C3E9609B-A809-EF38-CA53-2962E844062E} = {C44F267D-CA3D-2707-9AF9-6331B7BDD7F3}
+ {59F612B2-1E67-81E5-6F41-F56978638136} = {C44F267D-CA3D-2707-9AF9-6331B7BDD7F3}
+ {D9221BEA-DBFE-4677-4909-B88BA2E5A104} = {C44F267D-CA3D-2707-9AF9-6331B7BDD7F3}
+ {216DF189-70D2-3010-9D62-6AD9D13C62FD} = {C44F267D-CA3D-2707-9AF9-6331B7BDD7F3}
+ {B7A7E685-394E-D4CF-B572-8A968A5E46AC} = {C44F267D-CA3D-2707-9AF9-6331B7BDD7F3}
+ {0F659521-D577-1DFD-46FC-6DBEE3E1DFD6} = {36DDA403-5397-3B36-4C08-47C4ED351C15}
+ {78AAD972-57EA-27B4-5F13-3CD2B7E8ABFC} = {36DDA403-5397-3B36-4C08-47C4ED351C15}
+ EndGlobalSection
+EndGlobal
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub64.rules b/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub64.rules
new file mode 100644
index 0000000..09cae6f
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub64.rules
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioToolFile Name="debug_stub64" Version="8.00">
+ <Rules>
+ <CustomBuildRule AdditionalDependencies="..\..\..\..\native_client\tools\win_as.py;$(InputPath)" CommandLine="..\..\..\..\native_client\tools\win_py.cmd ..\..\..\..\native_client\tools\win_as.py -a $(PlatformName) -o $(IntDir)\$(InputName).obj -p ..\..\..\.. $(InputPath)" ExecutionDescription="Building assembly language file $(InputPath)" FileExtensions="S" Name="assembler (gnu-compatible)" Outputs="$(IntDir)\$(InputName).obj"/>
+ </Rules>
+</VisualStudioToolFile>
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub_test.cc b/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub_test.cc
new file mode 100644
index 0000000..69aefde
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub_test.cc
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2008 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+#include "native_client/src/include/portability.h"
+
+int main(int argc, char* argv[]) {
+ UNREFERENCED_PARAMETER(argc);
+ UNREFERENCED_PARAMETER(argv);
+ // TODO(noelallen): implement me
+ return 0;
+}
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub_test.rules b/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub_test.rules
new file mode 100644
index 0000000..ba96b7c
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub_test.rules
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioToolFile Name="debug_stub_test" Version="8.00">
+ <Rules>
+ <CustomBuildRule AdditionalDependencies="..\..\..\..\native_client\tools\win_as.py;$(InputPath)" CommandLine="..\..\..\..\native_client\tools\win_py.cmd ..\..\..\..\native_client\tools\win_as.py -a $(PlatformName) -o $(IntDir)\$(InputName).obj -p ..\..\..\.. $(InputPath)" ExecutionDescription="Building assembly language file $(InputPath)" FileExtensions="S" Name="assembler (gnu-compatible)" Outputs="$(IntDir)\$(InputName).obj"/>
+ </Rules>
+</VisualStudioToolFile>
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub_test64.rules b/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub_test64.rules
new file mode 100644
index 0000000..279f11a
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/debug_stub_test64.rules
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioToolFile Name="debug_stub_test64" Version="8.00">
+ <Rules>
+ <CustomBuildRule AdditionalDependencies="..\..\..\..\native_client\tools\win_as.py;$(InputPath)" CommandLine="..\..\..\..\native_client\tools\win_py.cmd ..\..\..\..\native_client\tools\win_as.py -a $(PlatformName) -o $(IntDir)\$(InputName).obj -p ..\..\..\.. $(InputPath)" ExecutionDescription="Building assembly language file $(InputPath)" FileExtensions="S" Name="assembler (gnu-compatible)" Outputs="$(IntDir)\$(InputName).obj"/>
+ </Rules>
+</VisualStudioToolFile>
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/event_common.cc b/native_client_sdk/src/build_tools/debug_server/debug_stub/event_common.cc
new file mode 100644
index 0000000..5e85c81
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/event_common.cc
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include "native_client/src/shared/platform/nacl_sync.h"
+
+/*
+ * Define the common gdb_utils IEvent interface to use the NaCl version.
+ * IEvent only suports single trigger Signal API.
+ */
+
+#if NACL_WINDOWS
+# include "native_client/src/shared/platform/win/lock.h"
+# include "native_client/src/shared/platform/win/condition_variable.h"
+#elif NACL_LINUX || NACL_OSX
+# include "native_client/src/shared/platform/linux/lock.h"
+# include "native_client/src/shared/platform/linux/condition_variable.h"
+#endif
+
+#include "native_client/src/debug_server/port/event.h"
+
+namespace port {
+
+class Event : public IEvent {
+ public:
+ Event() : signaled_(false) { }
+ ~Event() {}
+
+ void Wait() {
+ /* Start the wait owning the lock */
+ lock_.Acquire();
+
+ /* We can skip this if already signaled */
+ while (!signaled_) {
+ /* Otherwise, try and wait, which release the lock */
+ cond_.Wait(lock_);
+
+ /* We exit the wait owning the lock again. */
+ };
+
+ /* Clear the signal then unlock. */
+ signaled_ = false;
+ lock_.Release();
+ }
+
+ void Signal() {
+ signaled_ = true;
+ cond_.Signal();
+ }
+
+ public:
+ volatile bool signaled_;
+ NaCl::Lock lock_;
+ NaCl::ConditionVariable cond_;
+};
+
+IEvent* IEvent::Allocate() {
+ return new Event;
+}
+
+void IEvent::Free(IEvent *ievent) {
+ delete static_cast<Event*>(ievent);
+}
+
+} // End of port namespace
+
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/linux/debug_stub_linux.cc b/native_client_sdk/src/build_tools/debug_server/debug_stub/linux/debug_stub_linux.cc
new file mode 100644
index 0000000..00e0926
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/linux/debug_stub_linux.cc
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2008 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+#include "native_client/src/trusted/debug_stub/debug_stub.h"
+
+void NaClDebugStubPlatformInit() {
+ // TODO(noelallen): implement me
+}
+
+void NaClDebugStubPlatformFini() {
+ // TODO(noelallen): implement me
+}
+
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/osx/debug_stub_osx.cc b/native_client_sdk/src/build_tools/debug_server/debug_stub/osx/debug_stub_osx.cc
new file mode 100644
index 0000000..00e0926
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/osx/debug_stub_osx.cc
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2008 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+#include "native_client/src/trusted/debug_stub/debug_stub.h"
+
+void NaClDebugStubPlatformInit() {
+ // TODO(noelallen): implement me
+}
+
+void NaClDebugStubPlatformFini() {
+ // TODO(noelallen): implement me
+}
+
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/platform_common.cc b/native_client_sdk/src/build_tools/debug_server/debug_stub/platform_common.cc
new file mode 100644
index 0000000..4c9f098
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/platform_common.cc
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <map>
+
+#include "native_client/src/shared/platform/nacl_log.h"
+#include "native_client/src/debug_server/gdb_rsp/abi.h"
+#include "native_client/src/debug_server/port/platform.h"
+
+namespace port {
+
+// Log a message
+void IPlatform::LogInfo(const char *fmt, ...) {
+ va_list argptr;
+ va_start(argptr, fmt);
+
+ NaClLogV(LOG_INFO, fmt, argptr);
+}
+
+void IPlatform::LogWarning(const char *fmt, ...) {
+ va_list argptr;
+ va_start(argptr, fmt);
+
+ NaClLogV(LOG_WARNING, fmt, argptr);
+}
+
+void IPlatform::LogError(const char *fmt, ...) {
+ va_list argptr;
+ va_start(argptr, fmt);
+
+ NaClLogV(LOG_ERROR, fmt, argptr);
+}
+
+} // End of port namespace
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/posix/debug_stub_posix.cc b/native_client_sdk/src/build_tools/debug_server/debug_stub/posix/debug_stub_posix.cc
new file mode 100644
index 0000000..8d8558e
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/posix/debug_stub_posix.cc
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include "native_client/src/trusted/debug_stub/debug_stub.h"
+
+// No platform specific initialization need for POSIX
+void NaClDebugStubPlatformInit() {}
+void NaClDebugStubPlatformFini() {}
+
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/posix/mutex_impl.cc b/native_client_sdk/src/build_tools/debug_server/debug_stub/posix/mutex_impl.cc
new file mode 100644
index 0000000..f2548d7
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/posix/mutex_impl.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <pthread.h>
+
+#include "native_client/src/debug_server/port/mutex.h"
+
+/*
+ * Unfortunately NaClSync does not have the correct recursive
+ * property so we need to use our own version.
+ */
+
+namespace port {
+
+class Mutex : public IMutex {
+ public:
+ Mutex() {
+ pthread_mutexattr_t attr;
+
+ // Create a recursive mutex, so we can reenter on the same thread
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&mutex_, &attr);
+ pthread_mutexattr_destroy(&attr);
+ }
+
+ ~Mutex() {
+ pthread_mutex_destroy(&mutex_);
+ }
+
+ virtual void Lock() {
+ pthread_mutex_lock(&mutex_);
+ }
+
+ virtual bool Try() {
+ return pthread_mutex_trylock(&mutex_) == 0;
+ }
+
+ virtual void Unlock() {
+ pthread_mutex_unlock(&mutex_);
+ }
+
+ public:
+ pthread_mutex_t mutex_;
+};
+
+IMutex* IMutex::Allocate() {
+ return new Mutex;
+}
+
+void IMutex::Free(IMutex *mutex) {
+ delete static_cast<Mutex*>(mutex);
+}
+
+} // namespace port
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/posix/platform_impl.cc b/native_client_sdk/src/build_tools/debug_server/debug_stub/posix/platform_impl.cc
new file mode 100644
index 0000000..d6e0727
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/posix/platform_impl.cc
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <pthread.h>
+
+#include <map>
+#include <vector>
+
+#include "native_client/src/shared/platform/nacl_log.h"
+#include "native_client/src/debug_server/gdb_rsp/abi.h"
+#include "native_client/src/debug_server/gdb_rsp/util.h"
+#include "native_client/src/debug_server/port/event.h"
+#include "native_client/src/debug_server/port/platform.h"
+
+#include "native_client/src/trusted/service_runtime/nacl_config.h"
+#include "native_client/src/trusted/service_runtime/sel_ldr.h"
+#include "native_client/src/trusted/service_runtime/sel_rt.h"
+
+
+/*
+ * Define the OS specific portions of gdb_utils IPlatform interface.
+ */
+
+
+// TODO(noelallen) : Add POSIX implementation. These functions
+// represent a minimal implementation to allow the debugging
+// code to link and run.
+static port::IEvent* GetLaunchEvent() {
+ static port::IEvent* event_ = port::IEvent::Allocate();
+ return event_;
+}
+
+namespace port {
+
+struct StartInfo_t {
+ port::IPlatform::ThreadFunc_t func_;
+ void *cookie_;
+ volatile uint32_t id_;
+};
+
+// Get the OS id of this thread
+uint32_t IPlatform::GetCurrentThread() {
+ return static_cast<uint32_t>(syscall(SYS_gettid));
+}
+
+// Use start stub, to record thread id, and signal launcher
+static void *StartFunc(void* cookie) {
+ StartInfo_t* info = reinterpret_cast<StartInfo_t*>(cookie);
+ info->id_ = (uint32_t) syscall(SYS_gettid);
+
+ printf("Started thread...\n");
+ GetLaunchEvent()->Signal();
+ info->func_(info->cookie_);
+
+ return NULL;
+}
+
+uint32_t IPlatform::CreateThread(ThreadFunc_t func, void* cookie) {
+ pthread_t thread;
+ StartInfo_t info;
+
+ // Setup the thread information
+ info.func_ = func;
+ info.cookie_ = cookie;
+
+ printf("Creating thread...\n");
+
+ // Redirect to stub and wait for signal before continuing
+ if (pthread_create(&thread, NULL, StartFunc, &info) == 0) {
+ GetLaunchEvent()->Wait();
+ printf("Found thread...\n");
+ return info.id_;
+ }
+
+ return 0;
+}
+
+void IPlatform::Relinquish(uint32_t msec) {
+ usleep(msec * 1000);
+}
+
+bool IPlatform::GetMemory(uint64_t virt, uint32_t len, void *dst) {
+ UNREFERENCED_PARAMETER(virt);
+ UNREFERENCED_PARAMETER(len);
+ UNREFERENCED_PARAMETER(dst);
+ return false;
+}
+
+bool IPlatform::SetMemory(uint64_t virt, uint32_t len, void *src) {
+ UNREFERENCED_PARAMETER(virt);
+ UNREFERENCED_PARAMETER(len);
+ UNREFERENCED_PARAMETER(src);
+ return false;
+}
+
+} // End of port namespace
+
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/posix/thread_impl.cc b/native_client_sdk/src/build_tools/debug_server/debug_stub/posix/thread_impl.cc
new file mode 100644
index 0000000..f8bdfa1
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/posix/thread_impl.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <stdexcept>
+
+#include "native_client/src/shared/platform/nacl_log.h"
+#include "native_client/src/debug_server/port/mutex.h"
+#include "native_client/src/debug_server/port/thread.h"
+
+/*
+ * Define the OS specific portions of gdb_utils IThread interface.
+ */
+
+namespace port {
+
+static IMutex* ThreadGetLock() {
+ static IMutex* mutex_ = IMutex::Allocate();
+ return mutex_;
+}
+
+static IThread::ThreadMap_t *ThreadGetMap() {
+ static IThread::ThreadMap_t* map_ = new IThread::ThreadMap_t;
+ return map_;
+}
+
+// TODO(noelallen) : Add POSIX implementation. These functions
+// represent a minimal implementation to allow the debugging
+// code to link and run.
+class Thread : public IThread {
+ public:
+ explicit Thread(uint32_t id) : ref_(1), id_(id), state_(DEAD) {}
+ ~Thread() {}
+
+ uint32_t GetId() {
+ return id_;
+ }
+
+ State GetState() {
+ return state_;
+ }
+
+ virtual bool Suspend() {
+ return false;
+ }
+
+ virtual bool Resume() {
+ return false;
+ }
+
+ virtual bool SetStep(bool on) {
+ UNREFERENCED_PARAMETER(on);
+ return false;
+ }
+
+ virtual bool GetRegister(uint32_t index, void *dst, uint32_t len) {
+ UNREFERENCED_PARAMETER(index);
+ UNREFERENCED_PARAMETER(dst);
+ UNREFERENCED_PARAMETER(len);
+ return false;
+ }
+
+ virtual bool SetRegister(uint32_t index, void* src, uint32_t len) {
+ UNREFERENCED_PARAMETER(index);
+ UNREFERENCED_PARAMETER(src);
+ UNREFERENCED_PARAMETER(len);
+ return false;
+ }
+
+ virtual void* GetContext() { return NULL; }
+
+ private:
+ uint32_t ref_;
+ uint32_t id_;
+ State state_;
+
+ friend class IThread;
+};
+
+IThread* IThread::Acquire(uint32_t id, bool create) {
+ MutexLock lock(ThreadGetLock());
+ Thread* thread;
+ ThreadMap_t &map = *ThreadGetMap();
+
+ // Check if we have that thread
+ if (map.count(id)) {
+ thread = static_cast<Thread*>(map[id]);
+ thread->ref_++;
+ return thread;
+ }
+
+ // If not, can we create it?
+ if (create) {
+ // If not add it to the map
+ thread = new Thread(id);
+ map[id] = thread;
+ return thread;
+ }
+
+ return NULL;
+}
+
+void IThread::Release(IThread *ithread) {
+ MutexLock lock(ThreadGetLock());
+ Thread* thread = static_cast<Thread*>(ithread);
+ thread->ref_--;
+
+ if (thread->ref_ == 0) {
+ ThreadGetMap()->erase(thread->id_);
+ delete static_cast<IThread*>(thread);
+ }
+}
+
+void IThread::SetExceptionCatch(IThread::CatchFunc_t func, void *cookie) {
+ UNREFERENCED_PARAMETER(func);
+ UNREFERENCED_PARAMETER(cookie);
+}
+
+
+} // End of port namespace
+
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/run_debug_stub_test.rules b/native_client_sdk/src/build_tools/debug_server/debug_stub/run_debug_stub_test.rules
new file mode 100644
index 0000000..2e368f4
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/run_debug_stub_test.rules
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioToolFile Name="run_debug_stub_test" Version="8.00">
+ <Rules>
+ <CustomBuildRule AdditionalDependencies="..\..\..\..\native_client\tools\win_as.py;$(InputPath)" CommandLine="..\..\..\..\native_client\tools\win_py.cmd ..\..\..\..\native_client\tools\win_as.py -a $(PlatformName) -o $(IntDir)\$(InputName).obj -p ..\..\..\.. $(InputPath)" ExecutionDescription="Building assembly language file $(InputPath)" FileExtensions="S" Name="assembler (gnu-compatible)" Outputs="$(IntDir)\$(InputName).obj"/>
+ </Rules>
+</VisualStudioToolFile>
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/transport_common.cc b/native_client_sdk/src/build_tools/debug_server/debug_stub/transport_common.cc
new file mode 100644
index 0000000..191b72c
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/transport_common.cc
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#ifdef _WIN32
+#include <windows.h>
+#ifndef AF_IPX
+#include <winsock2.h>
+#endif
+#define SOCKET_HANDLE SOCKET
+#else
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define SOCKET_HANDLE int
+#define closesocket close
+#endif
+
+#include <stdlib.h>
+#include <string>
+
+#include "native_client/src/debug_server/gdb_rsp/util.h"
+#include "native_client/src/debug_server/port/platform.h"
+#include "native_client/src/debug_server/port/transport.h"
+
+using gdb_rsp::stringvec;
+using gdb_rsp::StringSplit;
+
+namespace port {
+
+typedef int socklen_t;
+
+class Transport : public ITransport {
+ public:
+ Transport() {
+ handle_ = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ }
+
+ explicit Transport(SOCKET_HANDLE s) {
+ handle_ = s;
+ }
+
+ ~Transport() {
+ if (handle_ != -1) closesocket(handle_);
+ }
+
+ // Read from this transport, return a negative value if there is an error
+ // otherwise return the number of bytes actually read.
+ virtual int32_t Read(void *ptr, int32_t len) {
+ return ::recv(handle_, reinterpret_cast<char *>(ptr), len, 0);
+ }
+
+ // Write to this transport, return a negative value if there is an error
+ // otherwise return the number of bytes actually written.
+ virtual int32_t Write(const void *ptr, int32_t len) {
+ return ::send(handle_, reinterpret_cast<const char *>(ptr), len, 0);
+ }
+
+ // Return true if data becomes availible or false after ms milliseconds.
+ virtual bool ReadWaitWithTimeout(uint32_t ms = 0) {
+ fd_set fds;
+
+ FD_ZERO(&fds);
+ FD_SET(handle_, &fds);
+
+ // We want a "non-blocking" check
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec= ms * 1000;
+
+ // Check if this file handle can select on read
+ int cnt = select(0, &fds, 0, 0, &timeout);
+
+ // If we are ready, or if there is an error. We return true
+ // on error, to "timeout" and let the next IO request fail.
+ if (cnt != 0) return true;
+
+ return false;
+ }
+
+// On windows, the header that defines this has other definition
+// colitions, so we define it outselves just in case
+#ifndef SD_BOTH
+#define SD_BOTH 2
+#endif
+
+ virtual void Disconnect() {
+ // Shutdown the conneciton in both diections. This should
+ // always succeed, and nothing we can do if this fails.
+ (void) ::shutdown(handle_, SD_BOTH);
+ }
+
+ protected:
+ SOCKET_HANDLE handle_;
+};
+
+// Convert string in the form of [addr][:port] where addr is a
+// IPv4 address or host name, and port is a 16b tcp/udp port.
+// Both portions are optional, and only the portion of the address
+// provided is updated. Values are provided in network order.
+static bool StringToIPv4(const std::string &instr, uint32_t *addr,
+ uint16_t *port) {
+ // Make a copy so the are unchanged unless we succeed
+ uint32_t outaddr = *addr;
+ uint16_t outport = *port;
+
+ // Substrings of the full ADDR:PORT
+ std::string addrstr;
+ std::string portstr;
+
+ // We should either have one or two tokens in the form of:
+ // IP - IP, NUL
+ // IP: - IP, NUL
+ // :PORT - NUL, PORT
+ // IP:PORT - IP, PORT
+
+ // Search for the port marker
+ size_t portoff = instr.find(':');
+
+ // If we found a ":" before the end, get both substrings
+ if ((portoff != std::string::npos) && (portoff + 1 < instr.size())) {
+ addrstr = instr.substr(0, portoff);
+ portstr = instr.substr(portoff + 1, std::string::npos);
+ } else {
+ // otherwise the entire string is the addr portion.
+ addrstr = instr;
+ portstr = "";
+ }
+
+ // If the address portion was provided, update it
+ if (addrstr.size()) {
+ // Special case 0.0.0.0 which means any IPv4 interface
+ if (addrstr == "0.0.0.0") {
+ outaddr = 0;
+ } else {
+ struct hostent *host = gethostbyname(addrstr.data());
+
+ // Check that we found an IPv4 host
+ if ((NULL == host) || (AF_INET != host->h_addrtype)) return false;
+
+ // Make sure the IP list isn't empty.
+ if (0 == host->h_addr_list[0]) return false;
+
+ // Use the first one.
+ uint32_t *addrarray = reinterpret_cast<uint32_t*>(host->h_addr_list);
+ outaddr = addrarray[0];
+ }
+ }
+
+ // if the port portion was provided, then update it
+ if (portstr.size()) {
+ int val = atoi(portstr.data());
+ if ((val < 0) || (val > 65535)) return false;
+ outport = ntohs(static_cast<uint16_t>(val));
+ }
+
+ // We haven't failed, so set the values
+ *addr = outaddr;
+ *port = outport;
+ return true;
+}
+
+
+static SOCKET_HANDLE s_ServerSock;
+
+void Init()
+{
+ WSADATA wsaData;
+ WORD wVersionRequested = MAKEWORD( 1, 1 );
+ int res = ::WSAStartup( wVersionRequested, &wsaData );
+ printf("::WSAStartup -> %d\n", res);
+}
+
+
+static bool SocketInit() {
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+ Init();
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+ long sc = (long)::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+ s_ServerSock = sc;
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+ if (s_ServerSock == -1) {
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+ IPlatform::LogError("Failed to create socket.\n");
+ return false;
+ }
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+
+ return true;
+}
+
+static bool SocketsAvailable() {
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+ static bool _init = SocketInit();
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+ return _init;
+}
+
+static bool BuildSockAddr(const char *addr, struct sockaddr_in *sockaddr) {
+ std::string addrstr = addr;
+ uint32_t *pip = reinterpret_cast<uint32_t*>(&sockaddr->sin_addr.s_addr);
+ uint16_t *pport = reinterpret_cast<uint16_t*>(&sockaddr->sin_port);
+
+ sockaddr->sin_family = AF_INET;
+ return StringToIPv4(addrstr, pip, pport);
+}
+
+ITransport* ITransport::Connect(const char *addr) {
+ if (!SocketsAvailable()) return NULL;
+
+ SOCKET_HANDLE s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (s == -1) {
+ IPlatform::LogError("Failed to create connection socket.\n");
+ return NULL;
+ }
+
+ struct sockaddr_in saddr;
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = htonl(0x7F000001);
+ saddr.sin_port = htons(4014);
+
+ // Override portions address that are provided
+ if (addr) BuildSockAddr(addr, &saddr);
+
+ if (::connect(s, reinterpret_cast<sockaddr*>(&saddr), sizeof(saddr)) != 0) {
+ IPlatform::LogError("Failed to connect.\n");
+ return NULL;
+ }
+
+ return new Transport(s);
+}
+
+ITransport* ITransport::Accept(const char *addr) {
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+ static bool listening = false;
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+ if (!SocketsAvailable()) return NULL;
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+
+ if (!listening) {
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+ struct sockaddr_in saddr;
+ socklen_t addrlen = static_cast<socklen_t>(sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = htonl(0x7F000001);
+ saddr.sin_port = htons(4014);
+
+ // Override portions address that are provided
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+ if (addr) BuildSockAddr(addr, &saddr);
+
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+ struct sockaddr *psaddr = reinterpret_cast<struct sockaddr *>(&saddr);
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+ if (bind(s_ServerSock, psaddr, addrlen)) {
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+ IPlatform::LogError("Failed to bind server.\n");
+ return NULL;
+ }
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+
+ if (listen(s_ServerSock, 1)) {
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+ IPlatform::LogError("Failed to listen.\n");
+ return NULL;
+ }
+ printf("---> %d %s\n", __LINE__, __FILE__); fflush(stdout);
+
+ listening = true;
+ }
+
+ if (listening) {
+ SOCKET_HANDLE s = ::accept(s_ServerSock, NULL, 0);
+ if (-1 != s) return new Transport(s);
+ return NULL;
+ }
+
+ return NULL;
+}
+
+void ITransport::Free(ITransport* itrans) {
+ Transport* trans = static_cast<Transport*>(itrans);
+ delete trans;
+}
+
+} // namespace port
+
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/win/debug_stub_win.cc b/native_client_sdk/src/build_tools/debug_server/debug_stub/win/debug_stub_win.cc
new file mode 100644
index 0000000..147799e
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/win/debug_stub_win.cc
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <windows.h>
+
+#ifndef AF_IPX
+#include <winsock2.h>
+#endif
+
+#include "native_client/src/debug_server/debug_stub/debug_stub.h"
+#include "native_client/src/debug_server/port/platform.h"
+
+
+using port::IPlatform;
+
+void NaClDebugStubPlatformInit() {
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int err;
+
+ // Make sure to request the use of sockets.
+ // NOTE: It is safe to call Startup multiple times
+ wVersionRequested = MAKEWORD(2, 2);
+ err = WSAStartup(wVersionRequested, &wsaData);
+ if (err != 0) {
+ // We could not find a matching DLL
+ IPlatform::LogError("WSAStartup failed with error: %d\n", err);
+ exit(-1);
+ }
+
+ if (HIBYTE(wsaData.wVersion) != 2) {
+ // We couldn't get a matching version
+ IPlatform::LogError("Could not find a usable version of Winsock.dll\n");
+ WSACleanup();
+ exit(-1);
+ }
+}
+
+void NaClDebugStubPlatformFini() {
+ WSACleanup();
+}
+
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/win/mutex_impl.cc b/native_client_sdk/src/build_tools/debug_server/debug_stub/win/mutex_impl.cc
new file mode 100644
index 0000000..c9757ee
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/win/mutex_impl.cc
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <windows.h>
+#include <exception>
+
+#include "native_client/src/debug_server/port/mutex.h"
+
+/*
+ * Define the gdb_utils IMutex interface to use the NaCl version.
+ * Unfortunately NaClSync does not have the correct recursive
+ * property so we need to use our own version.
+ */
+
+namespace port {
+
+class Mutex : public IMutex {
+ public:
+ Mutex() : handle_(NULL) {
+ handle_ = CreateMutex(NULL, FALSE, NULL);
+
+ // If we fail to create the mutex, then abort the app.
+ if (NULL == handle_) throw std::exception("Failed to create mutex.");
+ }
+
+ ~Mutex() {
+ // If constructor where to throw, then the mutex could be unset
+ if (NULL == handle_) return;
+
+ // This should always succeed.
+ (void) CloseHandle(handle_);
+ }
+
+ void Lock() {
+ DWORD ret;
+ do {
+ ret = WaitForSingleObject(handle_, INFINITE);
+ if (WAIT_OBJECT_0 == ret) return;
+
+ // If this lock wasn't abandoned or locked
+ // the there must be an error.
+ if (WAIT_ABANDONED != ret) {
+ throw std::exception("Lock failed on mutex.");
+ }
+ } while (WAIT_ABANDONED == ret);
+ }
+
+ bool Try() {
+ DWORD ret = WaitForSingleObject(handle_, 0);
+ switch (ret) {
+ case WAIT_OBJECT_0:
+ return true;
+
+ // The lock was already taken, we will need to retry.
+ case WAIT_TIMEOUT:
+ return false;
+
+ // The lock was abandoned and ownership transfered, it is still unlocked
+ // so we can just retry.
+ case WAIT_ABANDONED:
+ return false;
+
+ // Unrecoverable error.
+ default:
+ throw std::exception("Try failed on mutex.");
+ }
+ }
+
+ void Mutex::Unlock() {
+ (void) ReleaseMutex(handle_);
+ }
+
+ private:
+ HANDLE handle_;
+};
+
+IMutex* IMutex::Allocate() {
+ return new Mutex;
+}
+
+void IMutex::Free(IMutex *mutex) {
+ delete static_cast<Mutex*>(mutex);
+}
+
+} // End of port namespace
+
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/win/platform_impl.cc b/native_client_sdk/src/build_tools/debug_server/debug_stub/win/platform_impl.cc
new file mode 100644
index 0000000..d585968
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/win/platform_impl.cc
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <process.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <windows.h>
+
+#include <exception>
+
+#include "native_client/src/shared/platform/nacl_log.h"
+#include "native_client/src/debug_server/gdb_rsp/abi.h"
+#include "native_client/src/debug_server/port/platform.h"
+
+/*
+ * Define the OS specific portions of gdb_utils IPlatform interface.
+ */
+int GetLastErrorString(char* buffer, size_t length) {
+ DWORD error = GetLastError();
+ return FormatMessageA(
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ error,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ buffer,
+ static_cast<DWORD>((64 * 1024 < length) ? 64 * 1024 : length),
+ NULL) ? 0 : -1;
+}
+
+
+static DWORD Reprotect(void *ptr, uint32_t len, DWORD newflags) {
+ DWORD oldflags;
+ if (!VirtualProtect(ptr, len, newflags, &oldflags)) {
+ char msg[256] = {0};
+ GetLastErrorString(msg, sizeof(msg) - 1);
+ msg[sizeof(msg) - 1] = 0;
+ printf("Failed with %d [%s]\n", GetLastError(), msg);
+ return -1;
+ }
+
+ FlushInstructionCache(GetCurrentProcess(), ptr, len);
+ return oldflags;
+}
+
+namespace port {
+
+// Called to request the platform start/stop the thread
+uint32_t IPlatform::GetCurrentThread() {
+ return static_cast<uint32_t>(GetCurrentThreadId());
+}
+
+/*
+ * Since the windows compiler does not use __stdcall by default, we need to
+ * modify this function pointer.
+ */
+typedef unsigned (__stdcall *WinThreadFunc_t)(void *cookie);
+
+uint32_t IPlatform::CreateThread(IPlatform::ThreadFunc_t func, void* cookie) {
+ uint32_t id;
+ /*
+ * We use our own code here instead of NaClThreadCtor because
+ * it does not report the thread ID only the handle.
+ * TODO(noelallen) - Merge port and platform
+ */
+ uintptr_t res = _beginthreadex(NULL, 0,
+ reinterpret_cast<WinThreadFunc_t>(func),
+ cookie, 0, &id);
+
+ return id;
+}
+
+void IPlatform::Relinquish(uint32_t msec) {
+ Sleep(msec);
+}
+
+bool IPlatform::GetMemory(uint64_t virt, uint32_t len, void *dst) {
+ uint32_t oldFlags = Reprotect(reinterpret_cast<void*>(virt),
+ len, PAGE_READONLY);
+
+ if (oldFlags == -1) return false;
+
+ memcpy(dst, reinterpret_cast<void*>(virt), len);
+ (void) Reprotect(reinterpret_cast<void*>(virt), len, oldFlags);
+ return true;
+}
+
+bool IPlatform::SetMemory(uint64_t virt, uint32_t len, void *src) {
+ uint32_t oldFlags = Reprotect(reinterpret_cast<void*>(virt),
+ len, PAGE_EXECUTE_READWRITE);
+
+ if (oldFlags == -1) return false;
+
+ memcpy(reinterpret_cast<void*>(virt), src, len);
+ FlushInstructionCache(GetCurrentProcess(),
+ reinterpret_cast<void*>(virt), len);
+ (void) Reprotect(reinterpret_cast<void*>(virt), len, oldFlags);
+ return true;
+}
+
+} // End of port namespace
+
diff --git a/native_client_sdk/src/build_tools/debug_server/debug_stub/win/thread_impl.cc b/native_client_sdk/src/build_tools/debug_server/debug_stub/win/thread_impl.cc
new file mode 100644
index 0000000..6998386
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/debug_stub/win/thread_impl.cc
@@ -0,0 +1,438 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <windows.h>
+#include <exception>
+#include <stdexcept>
+
+#include "native_client/src/debug_server/port/mutex.h"
+#include "native_client/src/debug_server/port/thread.h"
+
+/*
+ * Define the OS specific portions of gdb_utils IThread interface.
+ */
+
+#define AAA(x) case x: return #x
+
+//0x40010006
+
+ const char* ExceptionCodeStr(int code) {
+ switch(code) {
+ AAA(EXCEPTION_ACCESS_VIOLATION);
+ AAA(EXCEPTION_ARRAY_BOUNDS_EXCEEDED);
+ AAA(EXCEPTION_BREAKPOINT);
+ AAA(EXCEPTION_DATATYPE_MISALIGNMENT);
+ AAA(EXCEPTION_FLT_DENORMAL_OPERAND);
+ AAA(EXCEPTION_FLT_DIVIDE_BY_ZERO);
+ AAA(EXCEPTION_FLT_INEXACT_RESULT);
+ AAA(EXCEPTION_FLT_INVALID_OPERATION);
+ AAA(EXCEPTION_FLT_OVERFLOW);
+ AAA(EXCEPTION_FLT_STACK_CHECK);
+ AAA(EXCEPTION_FLT_UNDERFLOW);
+ AAA(EXCEPTION_ILLEGAL_INSTRUCTION);
+ AAA(EXCEPTION_IN_PAGE_ERROR);
+ AAA(EXCEPTION_INT_DIVIDE_BY_ZERO);
+ AAA(EXCEPTION_INT_OVERFLOW);
+ AAA(EXCEPTION_INVALID_DISPOSITION);
+ AAA(EXCEPTION_NONCONTINUABLE_EXCEPTION);
+ AAA(EXCEPTION_PRIV_INSTRUCTION);
+ AAA(EXCEPTION_SINGLE_STEP);
+ AAA(EXCEPTION_STACK_OVERFLOW);
+ }
+ return "N/A";
+}
+
+namespace port {
+
+static IThread::CatchFunc_t s_CatchFunc = NULL;
+static void* s_CatchCookie = NULL;
+static PVOID s_OldCatch = NULL;
+
+enum PosixSignals {
+ SIGINT = 2,
+ SIGQUIT = 3,
+ SIGILL = 4,
+ SIGTRACE= 5,
+ SIGBUS = 7,
+ SIGFPE = 8,
+ SIGKILL = 9,
+ SIGSEGV = 11,
+ SIGSTKFLT = 16,
+};
+
+
+static IMutex* ThreadGetLock() {
+ static IMutex* mutex_ = IMutex::Allocate();
+ return mutex_;
+}
+
+static IThread::ThreadMap_t *ThreadGetMap() {
+ static IThread::ThreadMap_t* map_ = new IThread::ThreadMap_t;
+ return map_;
+}
+
+static int8_t ExceptionToSignal(int ex) {
+ switch (ex) {
+ case EXCEPTION_GUARD_PAGE:
+ case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+ case EXCEPTION_DATATYPE_MISALIGNMENT:
+ case EXCEPTION_ACCESS_VIOLATION:
+ case EXCEPTION_IN_PAGE_ERROR:
+ return SIGSEGV;
+
+ case EXCEPTION_BREAKPOINT:
+ case EXCEPTION_SINGLE_STEP:
+ return SIGTRACE;
+
+ case EXCEPTION_FLT_DENORMAL_OPERAND:
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ case EXCEPTION_FLT_INEXACT_RESULT:
+ case EXCEPTION_FLT_INVALID_OPERATION:
+ case EXCEPTION_FLT_OVERFLOW:
+ case EXCEPTION_FLT_STACK_CHECK:
+ case EXCEPTION_FLT_UNDERFLOW:
+ return SIGFPE;
+
+ case EXCEPTION_INT_DIVIDE_BY_ZERO:
+ case EXCEPTION_INT_OVERFLOW:
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ case EXCEPTION_PRIV_INSTRUCTION:
+ return SIGILL;
+
+ case EXCEPTION_STACK_OVERFLOW:
+ return SIGSTKFLT;
+
+ case CONTROL_C_EXIT:
+ return SIGQUIT;
+
+ case EXCEPTION_NONCONTINUABLE_EXCEPTION:
+ case EXCEPTION_INVALID_DISPOSITION:
+ case EXCEPTION_INVALID_HANDLE:
+ return SIGILL;
+ }
+ return SIGILL;
+}
+
+
+#ifdef _WIN64
+static void *GetPointerToRegInCtx(CONTEXT *ctx, int32_t num) {
+ switch (num) {
+ case 0: return &ctx->Rax;
+ case 1: return &ctx->Rbx;
+ case 2: return &ctx->Rcx;
+ case 3: return &ctx->Rdx;
+ case 4: return &ctx->Rsi;
+ case 5: return &ctx->Rdi;
+ case 6: return &ctx->Rbp;
+ case 7: return &ctx->Rsp;
+ case 8: return &ctx->R8;
+ case 9: return &ctx->R9;
+ case 10:return &ctx->R10;
+ case 11:return &ctx->R11;
+ case 12:return &ctx->R12;
+ case 13:return &ctx->R13;
+ case 14:return &ctx->R14;
+ case 15:return &ctx->R15;
+ case 16:return &ctx->Rip;
+ case 17:return &ctx->EFlags;
+ case 18:return &ctx->SegCs;
+ case 19:return &ctx->SegSs;
+ case 20:return &ctx->SegDs;
+ case 21:return &ctx->SegEs;
+ case 22:return &ctx->SegFs;
+ case 23:return &ctx->SegGs;
+ }
+
+ throw std::out_of_range("Register index out of range.");
+}
+
+static int GetSizeofRegInCtx(int32_t num) {
+ CONTEXT *ctx = NULL;
+ switch (num) {
+ case 0: return sizeof ctx->Rax;
+ case 1: return sizeof ctx->Rbx;
+ case 2: return sizeof ctx->Rcx;
+ case 3: return sizeof ctx->Rdx;
+ case 4: return sizeof ctx->Rsi;
+ case 5: return sizeof ctx->Rdi;
+ case 6: return sizeof ctx->Rbp;
+ case 7: return sizeof ctx->Rsp;
+ case 8: return sizeof ctx->R8;
+ case 9: return sizeof ctx->R9;
+ case 10:return sizeof ctx->R10;
+ case 11:return sizeof ctx->R11;
+ case 12:return sizeof ctx->R12;
+ case 13:return sizeof ctx->R13;
+ case 14:return sizeof ctx->R14;
+ case 15:return sizeof ctx->R15;
+ case 16:return sizeof ctx->Rip;
+ case 17:return sizeof ctx->EFlags;
+ case 18:return sizeof ctx->SegCs;
+ case 19:return sizeof ctx->SegSs;
+ case 20:return sizeof ctx->SegDs;
+ case 21:return sizeof ctx->SegEs;
+ case 22:return sizeof ctx->SegFs;
+ case 23:return sizeof ctx->SegGs;
+ }
+
+ throw std::out_of_range("Register index out of range.");
+}
+
+#else
+
+static void *GetPointerToRegInCtx(CONTEXT *ctx, int32_t num) {
+ switch (num) {
+ case 0: return &ctx->Eax;
+ case 1: return &ctx->Ecx;
+ case 2: return &ctx->Edx;
+ case 3: return &ctx->Ebx;
+ case 4: return &ctx->Ebp;
+ case 5: return &ctx->Esp;
+ case 6: return &ctx->Esi;
+ case 7: return &ctx->Edi;
+ case 8: return &ctx->Eip;
+ case 9: return &ctx->EFlags;
+ case 10:return &ctx->SegCs;
+ case 11:return &ctx->SegSs;
+ case 12:return &ctx->SegDs;
+ case 13:return &ctx->SegEs;
+ case 14:return &ctx->SegFs;
+ case 15:return &ctx->SegGs;
+ }
+
+ throw std::out_of_range("Register index out of range.");
+}
+
+static int GetSizeofRegInCtx(int32_t num) {
+ CONTEXT *ctx = NULL;
+ switch (num) {
+ case 0: return sizeof ctx->Eax;
+ case 1: return sizeof ctx->Ecx;
+ case 2: return sizeof ctx->Edx;
+ case 3: return sizeof ctx->Ebx;
+ case 4: return sizeof ctx->Ebp;
+ case 5: return sizeof ctx->Esp;
+ case 6: return sizeof ctx->Esi;
+ case 7: return sizeof ctx->Edi;
+ case 8: return sizeof ctx->Eip;
+ case 9: return sizeof ctx->EFlags;
+ case 10:return sizeof ctx->SegCs;
+ case 11:return sizeof ctx->SegSs;
+ case 12:return sizeof ctx->SegDs;
+ case 13:return sizeof ctx->SegEs;
+ case 14:return sizeof ctx->SegFs;
+ case 15:return sizeof ctx->SegGs;
+ }
+
+ throw std::out_of_range("Register index out of range.");
+}
+#endif
+
+
+class Thread : public IThread {
+ public:
+ explicit Thread(uint32_t id) : ref_(1), id_(id),
+ handle_(NULL), state_(RUNNING) {
+ handle_ = OpenThread(THREAD_ALL_ACCESS, false, id);
+ memset(&context_, 0, sizeof(context_));
+ context_.ContextFlags = CONTEXT_ALL;
+ if (NULL == handle_) state_ = DEAD;
+ }
+
+ ~Thread() {
+ if (NULL == handle_) return;
+
+ // This should always succeed, so ignore the return.
+ (void) CloseHandle(handle_);
+ }
+
+ uint32_t GetId() {
+ return id_;
+ }
+
+ State GetState() {
+ return state_;
+ }
+
+ virtual bool Suspend() {
+ MutexLock lock(ThreadGetLock());
+ if (state_ != RUNNING) return false;
+
+ // Attempt to suspend the thread
+ DWORD count = SuspendThread(handle_);
+
+ // Ignore result, since there is nothing we can do about
+ // it at this point.
+ (void) GetThreadContext(handle_, &context_);
+
+ if (count != -1) {
+ state_ = SUSPENDED;
+ return true;
+ }
+
+ return false;
+ }
+
+ virtual bool Resume() {
+ MutexLock lock(ThreadGetLock());
+ if (state_ != SUSPENDED) return false;
+
+ // Ignore result, since there is nothing we can do about
+ // it at this point if the set fails.
+ (void) SetThreadContext(handle_, &context_);
+
+ // Attempt to resume the thread
+ if (ResumeThread(handle_) != -1) {
+ state_ = RUNNING;
+ return true;
+ }
+
+ return false;
+ }
+
+ #define TRAP_FLAG (1 << 8)
+ virtual bool SetStep(bool on) {
+ if ((state_ == RUNNING) || (state_ == DEAD)) return false;
+
+ if (on) {
+ context_.EFlags |= TRAP_FLAG;
+ } else {
+ context_.EFlags &= ~TRAP_FLAG;
+ }
+ return true;
+ }
+
+ virtual bool GetRegister(uint32_t index, void *dst, uint32_t len) {
+ uint32_t clen = GetSizeofRegInCtx(index);
+ void* src = GetPointerToRegInCtx(&context_, index);
+
+ if ((state_ == RUNNING) || (state_ == DEAD)) return false;
+
+ // TODO(noelallen) we assume big endian
+ if (clen < len) len = clen;
+ memcpy(dst, src, len);
+
+ return true;
+ }
+
+ virtual bool SetRegister(uint32_t index, void* src, uint32_t len) {
+ uint32_t clen = GetSizeofRegInCtx(index);
+ void* dst = GetPointerToRegInCtx(&context_, index);
+
+ if ((state_ == RUNNING) || (state_ == DEAD) ||
+ (state_ != SYSCALL)) return false;
+
+ // TODO(noelallen) we assume big endian
+ if (clen < len) len = clen;
+ memcpy(dst, src, len);
+
+ return true;
+ }
+
+ virtual void* GetContext() { return &context_; }
+
+ //swi_lsp: non-browser app; disable
+
+ static LONG NTAPI ExceptionCatch(PEXCEPTION_POINTERS ep) {
+/*
+ printf("---> ExceptionCatch(PEXCEPTION_POINTERS: addr=%p code=%d=0x%X=%s [%d-%s]\n",
+ ep->ExceptionRecord->ExceptionAddress,
+ ep->ExceptionRecord->ExceptionCode,
+ ep->ExceptionRecord->ExceptionCode,
+ ExceptionCodeStr(ep->ExceptionRecord->ExceptionCode), ep->ExceptionRecord->NumberParameters,
+ ep->ExceptionRecord->ExceptionInformation[1]);
+*/
+ if(DBG_PRINTEXCEPTION_C == ep->ExceptionRecord->ExceptionCode)
+ return EXCEPTION_CONTINUE_EXECUTION;
+
+ uint32_t id = static_cast<uint32_t>(GetCurrentThreadId());
+ Thread* thread = static_cast<Thread*>(Acquire(id));
+
+ // If we are not tracking this thread, then ignore it
+ if (NULL == thread) return EXCEPTION_CONTINUE_SEARCH;
+
+ State old_state = thread->state_;
+ thread->state_ = SIGNALED;
+ int8_t sig = ExceptionToSignal(ep->ExceptionRecord->ExceptionCode);
+
+ void *ctx = thread->GetContext();
+
+ memcpy(ctx, ep->ContextRecord, sizeof(CONTEXT));
+ if (NULL != s_CatchFunc) s_CatchFunc(id, sig, s_CatchCookie);
+ memcpy(ep->ContextRecord, ctx, sizeof(CONTEXT));
+
+ thread->state_ = old_state;
+ Release(thread);
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+
+
+
+ private:
+ uint32_t ref_;
+ uint32_t id_;
+ State state_;
+ HANDLE handle_;
+ CONTEXT context_;
+
+ friend class IThread;
+};
+
+IThread* IThread::Acquire(uint32_t id, bool create) {
+ MutexLock lock(ThreadGetLock());
+ Thread* thread;
+ ThreadMap_t &map = *ThreadGetMap();
+
+ // Check if we have that thread
+ if (map.count(id)) {
+ thread = static_cast<Thread*>(map[id]);
+ thread->ref_++;
+ return thread;
+ }
+
+ // If not, can we create it?
+ if (create) {
+ // If not add it to the map
+ thread = new Thread(id);
+ if (NULL == thread->handle_) {
+ delete thread;
+ return NULL;
+ }
+
+ map[id] = thread;
+ return thread;
+ }
+
+ return NULL;
+}
+
+void IThread::Release(IThread *ithread) {
+ MutexLock lock(ThreadGetLock());
+ Thread* thread = static_cast<Thread*>(ithread);
+ thread->ref_--;
+
+ if (thread->ref_ == 0) {
+ ThreadGetMap()->erase(thread->id_);
+ delete static_cast<IThread*>(thread);
+ }
+}
+
+void IThread::SetExceptionCatch(IThread::CatchFunc_t func, void *cookie) {
+ MutexLock lock(ThreadGetLock());
+
+ // Remove our old catch if there is one, this allows us to add again
+ if (NULL != s_OldCatch) RemoveVectoredExceptionHandler(s_OldCatch);
+
+ // Add the new one, at the front of the list
+ s_OldCatch = AddVectoredExceptionHandler(1, Thread::ExceptionCatch);
+ s_CatchFunc = func;
+ s_CatchCookie = cookie;
+}
+
+
+} // End of port namespace
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/abi.cc b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/abi.cc
new file mode 100644
index 0000000..b1bbd04
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/abi.cc
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <assert.h>
+
+#include <map>
+#include <string>
+
+#include "native_client/src/debug_server/gdb_rsp/abi.h"
+#include "native_client/src/debug_server/port/platform.h"
+
+using port::IPlatform;
+
+namespace gdb_rsp {
+
+#define MINIDEF(x, name, purpose) { #name, sizeof(x), Abi::purpose, 0, 0 }
+#define BPDEF(x) { sizeof(x), x }
+
+static Abi::RegDef RegsX86_64[] = {
+ MINIDEF(uint64_t, rax, GENERAL),
+ MINIDEF(uint64_t, rbx, GENERAL),
+ MINIDEF(uint64_t, rcx, GENERAL),
+ MINIDEF(uint64_t, rdx, GENERAL),
+ MINIDEF(uint64_t, rsi, GENERAL),
+ MINIDEF(uint64_t, rdi, GENERAL),
+ MINIDEF(uint64_t, rbp, GENERAL),
+ MINIDEF(uint64_t, rsp, GENERAL),
+ MINIDEF(uint64_t, r8, GENERAL),
+ MINIDEF(uint64_t, r9, GENERAL),
+ MINIDEF(uint64_t, r10, GENERAL),
+ MINIDEF(uint64_t, r11, GENERAL),
+ MINIDEF(uint64_t, r12, GENERAL),
+ MINIDEF(uint64_t, r13, GENERAL),
+ MINIDEF(uint64_t, r14, GENERAL),
+ MINIDEF(uint64_t, r15, GENERAL),
+ MINIDEF(uint64_t, rip, INST_PTR),
+ MINIDEF(uint32_t, eflags, FLAGS),
+ MINIDEF(uint32_t, cs, SEGMENT),
+ MINIDEF(uint32_t, ss, SEGMENT),
+ MINIDEF(uint32_t, ds, SEGMENT),
+ MINIDEF(uint32_t, es, SEGMENT),
+ MINIDEF(uint32_t, fs, SEGMENT),
+ MINIDEF(uint32_t, gs, SEGMENT),
+};
+
+static Abi::RegDef RegsX86_32[] = {
+ MINIDEF(uint32_t, eax, GENERAL),
+ MINIDEF(uint32_t, ecx, GENERAL),
+ MINIDEF(uint32_t, edx, GENERAL),
+ MINIDEF(uint32_t, ebx, GENERAL),
+ MINIDEF(uint32_t, esp, GENERAL),
+ MINIDEF(uint32_t, ebp, GENERAL),
+ MINIDEF(uint32_t, esi, GENERAL),
+ MINIDEF(uint32_t, edi, GENERAL),
+ MINIDEF(uint32_t, eip, INST_PTR),
+ MINIDEF(uint32_t, eflags, FLAGS),
+ MINIDEF(uint32_t, cs, SEGMENT),
+ MINIDEF(uint32_t, ss, SEGMENT),
+ MINIDEF(uint32_t, ds, SEGMENT),
+ MINIDEF(uint32_t, es, SEGMENT),
+ MINIDEF(uint32_t, fs, SEGMENT),
+ MINIDEF(uint32_t, gs, SEGMENT),
+};
+
+static Abi::RegDef RegsArm[] = {
+ MINIDEF(uint32_t, r0, GENERAL),
+ MINIDEF(uint32_t, r1, GENERAL),
+ MINIDEF(uint32_t, r2, GENERAL),
+ MINIDEF(uint32_t, r3, GENERAL),
+ MINIDEF(uint32_t, r4, GENERAL),
+ MINIDEF(uint32_t, r5, GENERAL),
+ MINIDEF(uint32_t, r6, GENERAL),
+ MINIDEF(uint32_t, r7, GENERAL),
+ MINIDEF(uint32_t, r8, GENERAL),
+ MINIDEF(uint32_t, r9, GENERAL),
+ MINIDEF(uint32_t, r10, GENERAL),
+ MINIDEF(uint32_t, r11, GENERAL),
+ MINIDEF(uint32_t, r12, GENERAL),
+ MINIDEF(uint32_t, sp, STACK_PTR),
+ MINIDEF(uint32_t, lr, LINK_PTR),
+ MINIDEF(uint32_t, pc, INST_PTR),
+};
+
+static uint8_t BPCodeX86[] = { 0xCC };
+
+static Abi::BPDef BPX86 = BPDEF(BPCodeX86);
+
+static AbiMap_t s_Abis;
+
+// AbiInit & AbiIsAvailable
+// This pair of functions work together as singleton to
+// ensure the module has been correctly initialized. All
+// dependant functions should call AbiIsAvailable to ensure
+// the module is ready.
+static bool AbiInit() {
+ Abi::Register("i386", RegsX86_32, sizeof(RegsX86_32), &BPX86);
+ Abi::Register("i386:x86-64", RegsX86_64, sizeof(RegsX86_64), &BPX86);
+
+ // TODO(cbiffle) Figure out how to REALLY detect ARM, and define Breakpoint
+ Abi::Register("iwmmxt", RegsArm, sizeof(RegsArm), NULL);
+
+ return true;
+}
+
+static bool AbiIsAvailable() {
+ static bool initialized_ = AbiInit();
+ return initialized_;
+}
+
+
+
+Abi::Abi() {}
+Abi::~Abi() {}
+
+void Abi::Register(const char *name, RegDef *regs,
+ uint32_t bytes, const BPDef *bp) {
+ uint32_t offs = 0;
+ const uint32_t cnt = bytes / sizeof(RegDef);
+
+ // Build indexes and offsets
+ for (uint32_t loop = 0; loop < cnt; loop++) {
+ regs[loop].index_ = loop;
+ regs[loop].offset_ = offs;
+ offs += regs[loop].bytes_;
+ }
+
+ Abi *abi = new Abi;
+
+ abi->name_ = name;
+ abi->regCnt_ = cnt;
+ abi->regDefs_= regs;
+ abi->ctxSize_ = offs;
+ abi->bpDef_ = bp;
+
+ s_Abis[name] = abi;
+}
+
+const Abi* Abi::Find(const char *name) {
+ if (!AbiIsAvailable()) {
+ IPlatform::LogError("Failed to initalize ABIs.");
+ return NULL;
+ }
+
+ AbiMap_t::const_iterator itr = s_Abis.find(name);
+ if (itr == s_Abis.end()) return NULL;
+
+ return itr->second;
+}
+
+const Abi* Abi::Get() {
+ static const Abi* abi = NULL;
+
+ if ((NULL == abi) && AbiIsAvailable()) {
+#ifdef GDB_RSP_ABI_ARM
+ abi = Abi::Find("iwmmxt");
+#elif GDB_RSP_ABI_X86_64
+ abi = Abi::Find("i386:x86-64");
+#elif GDB_RSP_ABI_X86
+ abi = Abi::Find("i386");
+#else
+#error "Unknown CPU architecture."
+#endif
+ }
+
+ return abi;
+}
+
+const char* Abi::GetName() const {
+ return name_;
+}
+
+const Abi::BPDef *Abi::GetBreakpointDef() const {
+ return bpDef_;
+}
+
+uint32_t Abi::GetContextSize() const {
+ return ctxSize_;
+}
+
+uint32_t Abi::GetRegisterCount() const {
+ return regCnt_;
+}
+
+const Abi::RegDef *Abi::GetRegisterDef(uint32_t index) const {
+ if (index >= regCnt_) return NULL;
+
+ return &regDefs_[index];
+}
+
+const Abi::RegDef *Abi::GetRegisterType(RegType rtype, uint32_t nth) const {
+ uint32_t typeNum = 0;
+
+ // Scan for the "nth" register of rtype;
+ for (uint32_t regNum = 0; regNum < regCnt_; regNum++) {
+ if (rtype == regDefs_[regNum].type_) {
+ if (typeNum == nth) return &regDefs_[regNum];
+ typeNum++;
+ }
+ }
+
+ // Otherwise we failed to find it
+ return NULL;
+}
+
+} // namespace gdb_rsp
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/abi.h b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/abi.h
new file mode 100644
index 0000000..d360274
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/abi.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+// This module provides an interface for performing ABI specific
+// functions needed by the Target and Host. ABI objects are provided
+// to the rest of the system as "const" to prevent accidental modification.
+// None of the resources in the Abi object are actually owned by the Abi
+// object, they are assumed to be static and never destroyed.
+//
+// This module will only throw standard errors
+// std::bad_alloc - when out of memory
+// std::out_of_range - when using an out of range regsiter index
+//
+// It is required that Init be called prior to calling Find to ensure
+// the various built-in ABIs have been registered.
+
+#ifndef NATIVE_CLIENT_GDB_RSP_ABI_H_
+#define NATIVE_CLIENT_GDB_RSP_ABI_H_ 1
+
+#include <map>
+#include <string>
+
+#include "native_client/src/debug_server/port/std_types.h"
+
+namespace gdb_rsp {
+
+class Abi {
+ public:
+ enum RegType {
+ GENERAL,
+ LINK_PTR,
+ INST_PTR,
+ STACK_PTR,
+ FRAME_PTR,
+ BASE_PTR,
+ SEGMENT,
+ TLS,
+ FLAGS,
+ CONTROL,
+ REG_TYPE_CNT
+ };
+
+ // Defines an individual register
+ struct RegDef {
+ const char *name_;
+ uint32_t bytes_;
+ RegType type_;
+ uint32_t index_;
+ uint32_t offset_;
+ };
+
+ // Defines how breakpoints work.
+ // code_ points to a series of bytes which will be placed in the code to
+ // create the breakpoint. size_ is the size of that array. We use a 32b
+ // size since the memory modification API only supports a 32b size.
+ struct BPDef {
+ uint32_t size_;
+ uint8_t *code_;
+ };
+
+ // Returns the registered name of this ABI.
+ const char *GetName() const;
+
+ // Returns a pointer to the breakpoint definition.
+ const BPDef *GetBreakpointDef() const;
+
+ // Returns the size of the thread context.
+ uint32_t GetContextSize() const;
+
+ // Returns the number of registers visbible.
+ uint32_t GetRegisterCount() const;
+
+ // Returns a definition of the register at the provided index, or
+ // NULL if it does not exist.
+ const RegDef *GetRegisterDef(uint32_t index) const;
+
+ // Returns the 'nth' register of the type specified or NULL if it
+ // does not exist.
+ const RegDef *GetRegisterType(RegType rt, uint32_t index = 0) const;
+
+ // Called to assign a set of register definitions to an ABI.
+ // This function is non-reentrant.
+ static void Register(const char *name, RegDef *defs,
+ uint32_t cnt, const BPDef *bp);
+
+ // Called to search the map for a matching Abi by name.
+ // This function is reentrant.
+ static const Abi *Find(const char *name);
+
+ // Get the ABI of for the running application.
+ static const Abi *Get();
+
+ protected:
+ const char *name_;
+ const RegDef *regDefs_;
+ uint32_t regCnt_;
+ uint32_t ctxSize_;
+ const BPDef *bpDef_;
+
+ private:
+ Abi();
+ ~Abi();
+ void operator =(const Abi&);
+};
+
+typedef std::map<std::string, Abi*> AbiMap_t;
+
+} // namespace gdb_rsp
+
+#endif // NATIVE_CLIENT_GDB_RSP_ABI_H_
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/abi_test.cc b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/abi_test.cc
new file mode 100644
index 0000000..6f966b5
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/abi_test.cc
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "native_client/src/debug_server/gdb_rsp/abi.h"
+
+using gdb_rsp::Abi;
+
+int VerifyAbi(const char *name, uint32_t regs) {
+ int errs = 0;
+
+ const Abi *abi = Abi::Find(name);
+ if (NULL != abi) {
+ uint32_t regCnt = abi->GetRegisterCount();
+ uint32_t byteCnt= abi->GetContextSize();
+ uint32_t bytes = 0;
+ uint32_t loop = 0;
+
+ if (strcmp(abi->GetName(), name)) {
+ printf("Incorrect name for ABI %s.\n", name);
+ errs++;
+ }
+
+ if (regCnt != regs) {
+ printf("Incorrect number of registers for ABI %s.\n", name);
+ errs++;
+ }
+
+ if (abi->GetRegisterDef(regs) != NULL) {
+ printf("Unexpected register for ABI %s.\n", name);
+ errs++;
+ }
+
+ for (loop = 0; loop < regs; loop++) {
+ const Abi::RegDef *def = abi->GetRegisterDef(loop);
+ if (NULL == def) {
+ printf("Missing register for ABI %s, reg %d.\n", name, loop);
+ errs++;
+ break;
+ }
+
+ if (NULL == def->name_) {
+ printf("Missing register name for ABI %s, reg %d.\n", name, loop);
+ errs++;
+ break;
+ }
+
+ if (loop != def->index_) {
+ printf("Index mismatch for ABI %s, reg %d.\n", name, loop);
+ errs++;
+ break;
+ }
+
+ if ((1 > def->bytes_) || (def->bytes_ > byteCnt)) {
+ printf("Index mismatch for ABI %s, reg %d.\n", name, loop);
+ errs++;
+ break;
+ }
+
+ if ((def->type_ < Abi::GENERAL) || (def->type_ >= Abi::REG_TYPE_CNT)) {
+ printf("Illegal register type ABI %s, reg %d.\n", name, loop);
+ errs++;
+ break;
+ }
+
+ if (def->offset_ != bytes) {
+ printf("Offset mismatch in ABI %s, reg %d.\n", name, loop);
+ errs++;
+ break;
+ }
+
+ bytes += def->bytes_;
+ }
+
+ if (bytes != byteCnt) {
+ printf("Context size mismatch for ABI %s.\n", name);
+ errs++;
+ }
+
+ if (abi->GetRegisterType(Abi::GENERAL) == NULL) {
+ printf("Missing general registers for ABI %s.\n", name);
+ errs++;
+ }
+
+ if (abi->GetRegisterType(Abi::INST_PTR) == NULL) {
+ printf("Missing instruction pointer for ABI %s.\n", name);
+ errs++;
+ }
+ } else {
+ printf("Could not find ABI %s.\n", name);
+ errs++;
+ }
+ return errs;
+}
+
+int TestAbi() {
+ int errs = 0;
+
+ // TODO(cbiffle) Figure out how to REALLY detect ARM
+ errs += VerifyAbi("iwmmxt", 16);
+ errs += VerifyAbi("i386", 16);
+ errs += VerifyAbi("i386:x86-64", 24);
+
+ // Get the default ABI
+ const Abi* abi = Abi::Get();
+ if (NULL == abi) {
+ printf("Failed to get default ABI.\n");
+ errs++;
+ }
+
+ // Get a generic register
+ const Abi::RegDef *def = abi->GetRegisterType(Abi::GENERAL);
+ if (NULL == def) {
+ printf("Failed to get a generic register on the default ABI %s.\n",
+ abi->GetName());
+ errs++;
+ } else {
+ if (def->bytes_ != sizeof(intptr_t)) {
+ printf("Generic register %d != %d pointer size for %s\n",
+ static_cast<int>(def->bytes_),
+ static_cast<int>(sizeof(intptr_t)),
+ abi->GetName());
+ errs++;
+ }
+ }
+
+ if (NULL != Abi::Find("non-existant")) {
+ printf("Found 'non-existant' ABI.\n");
+ errs++;
+ }
+
+ return errs;
+}
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/build.scons b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/build.scons
new file mode 100644
index 0000000..3303c13
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/build.scons
@@ -0,0 +1,57 @@
+# -*- python -*-
+# Copyright 2010 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can
+# be found in the LICENSE file.
+
+# This file needs to be in sync with $SOURCE_ROOT/ppapi/ppapi.gyp
+# at the revision specified in $SOURCE_ROOT/native_client/DEPS.
+
+Import('env')
+
+# this is needed for including gdb_utils files
+env.Append(CPPPATH=['${SOURCE_ROOT}/gdb_utils/src'])
+
+if env.Bit('windows'):
+ env.Append(CPPDEFINES=['WIN32'])
+ if env.Bit('target_x86_64'):
+ env.Append(CPPDEFINES=['WIN64'])
+
+if env.Bit('target_arm'):
+ env.Append(CPPDEFINES=['GDB_RSP_ABI_ARM'])
+elif env.Bit('target_x86'):
+ env.Append(CPPDEFINES=['GDB_RSP_ABI_X86'])
+ if env.Bit('target_x86_64'):
+ env.Append(CPPDEFINES=['GDB_RSP_ABI_X86_64'])
+else:
+ raise Exception("Unknown target")
+
+rsp_sources = [
+ 'abi.cc',
+ 'host.cc',
+ 'packet.cc',
+ 'session.cc',
+ 'target.cc',
+ 'util.cc',
+ ]
+
+rsp_test_sources = [
+ 'abi_test.cc',
+ 'host_test.cc',
+ 'packet_test.cc',
+ 'session_test.cc',
+ 'session_mock.cc',
+ 'target_test.cc',
+ 'util_test.cc',
+ 'test.cc'
+ ]
+
+# Build only for Win64
+env.DualLibrary('gdb_rsp', rsp_sources)
+gdb_rsp_test_exe = env.ComponentProgram('gdb_rsp_unittest',
+ rsp_test_sources,
+ EXTRA_LIBS=['gdb_rsp'])
+
+node = env.CommandTest(
+ 'gdb_rsp_unittest.out',
+ command=[gdb_rsp_test_exe])
+env.AddNodeToTestSuite(node, ['small_tests'], 'run_gdb_rsp_tests')
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp.gyp b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp.gyp
new file mode 100644
index 0000000..e634300
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp.gyp
@@ -0,0 +1,162 @@
+# -*- python -*-
+# Copyright 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+{
+ 'includes': [
+ '../../../build/common.gypi',
+ ],
+ 'target_defaults': {
+ 'conditions': [
+ ['target_arch=="ia32"', {
+ 'defines': [
+ 'GDB_RSP_ABI_X86',
+ ],
+ }],
+ ['target_arch=="x64"', {
+ 'defines': [
+ 'GDB_RSP_ABI_X86',
+ 'GDB_RSP_ABI_X86_64',
+ ],
+ }],
+ ['target_arch=="arm"', {
+ 'defines': [
+ 'GDB_RSP_ABI_ARM',
+ ],
+ }],
+ ],
+ 'target_conditions': [
+ ['OS=="linux" or OS=="mac"', {
+ 'cflags': [
+ '-fexceptions',
+ ],
+ 'cflags_cc' : [
+ '-frtti',
+ ]
+ }],
+ ['OS=="mac"', {
+ 'xcode_settings': {
+ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', # -fexceptions
+ 'GCC_ENABLE_CPP_RTTI': 'YES', # -frtti
+ }
+ }],
+ ['OS=="win"', {
+ 'msvs_settings': {
+ 'VCCLCompilerTool': {
+ 'ExceptionHandling': '1',
+ 'RuntimeTypeInfo': 'true',
+ },
+ }
+ }],
+ ],
+ },
+ 'variables': {
+ 'gdb_rsp_sources': [
+ 'abi.cc',
+ 'abi.h',
+ 'host.cc',
+ 'host.h',
+ 'packet.cc',
+ 'packet.h',
+ 'session.cc',
+ 'session.h',
+ 'target.cc',
+ 'target.h',
+ 'util.cc',
+ 'util.h',
+ ],
+ 'gdb_test_sources': [
+ 'abi_test.cc',
+ 'packet_test.cc',
+ 'host_test.cc',
+ 'session_mock.h',
+ 'session_mock.cc',
+ 'session_test.cc',
+ 'target_test.cc',
+ 'util_test.cc',
+ 'test.cc',
+ 'test.h',
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'gdb_rsp',
+ 'type': 'static_library',
+ 'sources': [
+ '<@(gdb_rsp_sources)',
+ ],
+ },
+ {
+ 'target_name': 'gdb_rsp_test',
+ 'type': 'executable',
+ 'sources': [
+ '<@(gdb_test_sources)',
+ ],
+ 'dependencies': [
+ 'gdb_rsp',
+ ]
+ },
+ ],
+ 'conditions': [
+ ['OS=="win"', {
+ 'targets': [
+ {
+ 'target_name': 'gdb_rsp64',
+ 'type': 'static_library',
+ 'sources': [
+ '<@(gdb_rsp_sources)',
+ ],
+ 'defines': [
+ 'GDB_RSP_ABI_X86_64',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ },
+ {
+ 'target_name': 'gdb_rsp_test64',
+ 'type': 'executable',
+ 'sources': [
+ '<@(gdb_test_sources)',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ 'dependencies': [
+ 'gdb_rsp64',
+ ]
+ },
+ ],
+ }],
+ ],
+}
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp.rules b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp.rules
new file mode 100644
index 0000000..bc56170
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp.rules
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioToolFile Name="gdb_rsp" Version="8.00">
+ <Rules>
+ <CustomBuildRule AdditionalDependencies="..\..\..\..\native_client\tools\win_as.py;$(InputPath)" CommandLine="..\..\..\..\native_client\tools\win_py.cmd ..\..\..\..\native_client\tools\win_as.py -a $(PlatformName) -o $(IntDir)\$(InputName).obj -p ..\..\..\.. $(InputPath)" ExecutionDescription="Building assembly language file $(InputPath)" FileExtensions="S" Name="assembler (gnu-compatible)" Outputs="$(IntDir)\$(InputName).obj"/>
+ </Rules>
+</VisualStudioToolFile>
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp64.rules b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp64.rules
new file mode 100644
index 0000000..79d04ca
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp64.rules
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioToolFile Name="gdb_rsp64" Version="8.00">
+ <Rules>
+ <CustomBuildRule AdditionalDependencies="..\..\..\..\native_client\tools\win_as.py;$(InputPath)" CommandLine="..\..\..\..\native_client\tools\win_py.cmd ..\..\..\..\native_client\tools\win_as.py -a $(PlatformName) -o $(IntDir)\$(InputName).obj -p ..\..\..\.. $(InputPath)" ExecutionDescription="Building assembly language file $(InputPath)" FileExtensions="S" Name="assembler (gnu-compatible)" Outputs="$(IntDir)\$(InputName).obj"/>
+ </Rules>
+</VisualStudioToolFile>
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp_test.rules b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp_test.rules
new file mode 100644
index 0000000..6a3ddbdd3
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp_test.rules
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioToolFile Name="gdb_rsp_test" Version="8.00">
+ <Rules>
+ <CustomBuildRule AdditionalDependencies="..\..\..\..\native_client\tools\win_as.py;$(InputPath)" CommandLine="..\..\..\..\native_client\tools\win_py.cmd ..\..\..\..\native_client\tools\win_as.py -a $(PlatformName) -o $(IntDir)\$(InputName).obj -p ..\..\..\.. $(InputPath)" ExecutionDescription="Building assembly language file $(InputPath)" FileExtensions="S" Name="assembler (gnu-compatible)" Outputs="$(IntDir)\$(InputName).obj"/>
+ </Rules>
+</VisualStudioToolFile>
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp_test64.rules b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp_test64.rules
new file mode 100644
index 0000000..f63bc2e
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/gdb_rsp_test64.rules
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioToolFile Name="gdb_rsp_test64" Version="8.00">
+ <Rules>
+ <CustomBuildRule AdditionalDependencies="..\..\..\..\native_client\tools\win_as.py;$(InputPath)" CommandLine="..\..\..\..\native_client\tools\win_py.cmd ..\..\..\..\native_client\tools\win_as.py -a $(PlatformName) -o $(IntDir)\$(InputName).obj -p ..\..\..\.. $(InputPath)" ExecutionDescription="Building assembly language file $(InputPath)" FileExtensions="S" Name="assembler (gnu-compatible)" Outputs="$(IntDir)\$(InputName).obj"/>
+ </Rules>
+</VisualStudioToolFile>
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/host.cc b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/host.cc
new file mode 100644
index 0000000..9b00b03
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/host.cc
@@ -0,0 +1,524 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "native_client/src/debug_server/gdb_rsp/abi.h"
+#include "native_client/src/debug_server/gdb_rsp/host.h"
+#include "native_client/src/debug_server/gdb_rsp/packet.h"
+#include "native_client/src/debug_server/gdb_rsp/session.h"
+#include "native_client/src/debug_server/gdb_rsp/util.h"
+
+#include "native_client/src/debug_server/port/std_types.h"
+#include "native_client/src/debug_server/port/platform.h"
+
+#ifdef WIN32
+#define snprintf sprintf_s
+#endif
+
+
+using std::string;
+using port::IPlatform;
+
+namespace gdb_rsp {
+
+Host::Thread::Thread(const Abi *abi, uint32_t id)
+ : id_(id), ctx_(NULL), abi_(abi) {
+ ctx_ = new uint8_t[abi_->GetContextSize()];
+ assert(NULL != ctx_);
+}
+
+Host::Thread::~Thread() {
+ delete[] ctx_;
+}
+
+uint32_t Host::Thread::GetId() const {
+ return id_;
+}
+
+const Abi *Host::Thread::GetAbi() const {
+ return abi_;
+}
+
+void Host::Thread::GetRegister(uint32_t index, void *dst) const {
+ assert(NULL != dst);
+ assert(index < abi_->GetRegisterCount());
+
+ const Abi::RegDef *def = abi_->GetRegisterDef(index);
+ uint8_t *ptr = ctx_ + def->offset_;
+ memcpy(dst, ptr, def->bytes_);
+}
+
+void Host::Thread::SetRegister(uint32_t index, const void *src) {
+ assert(src);
+ assert(index < abi_->GetRegisterCount());
+
+ const Abi::RegDef *def = abi_->GetRegisterDef(index);
+ uint8_t *ptr = ctx_ + def->offset_;
+ memcpy(ptr, src, def->bytes_);
+}
+
+
+// Construct unitialzied
+Host::Host(Session *session) : session_(session), abi_(NULL),
+ status_(HS_UNINIT) { }
+
+Host::~Host() { }
+
+bool Host::Init() {
+ string reply;
+
+ properties_.clear();
+
+ // Set the default max packet size. We use the default value
+ // that GDB appears to like which is 1K - xsum/etc overhead.
+ properties_["PacketSize"] = "3fa";
+
+ // Get properties
+ if (!Request("qSupported", &reply)) return false;
+
+ // Parse the semicolon delimited properties
+ stringvec tokens = StringSplit(reply.data(), ";");
+ for (uint32_t loop = 0; loop < tokens.size(); loop++) {
+ // Properties are in the form of "Key=Val", "Key+", or "Key-"
+ string &prop = tokens[loop];
+
+ // If the form is Key=Val, there should be two components Key and Val
+ stringvec keyval = StringSplit(prop, "=");
+ if (keyval.size() == 2) {
+ properties_[keyval[0]] = keyval[1];
+ continue;
+ }
+
+ // Strip off the + or - by splitting to get the name.
+ keyval = StringSplit(prop, "+-");
+ // Size==1 means we got either XXX, XXX+, or XXX-
+ if (keyval.size() == 1) {
+ size_t len = prop.length();
+ // In which case the last character must be + or -, or we ignore.
+ switch (prop[len - 1]) {
+ case '+' :
+ properties_[keyval[0]] = "true";
+ continue;
+ case '-' :
+ properties_[keyval[0]] = "false";
+ continue;
+ }
+ }
+
+ // Otherwise it was a malformed property
+ IPlatform::LogError("Returned feature with strange assignment: %s",
+ prop.data());
+ }
+
+ // Must support and use property read method to get CPU type
+ if (!ReadObject("features", "target.xml", &reply)) return false;
+
+ // Search for start of "architecture" tag
+ const char *name = strstr(reply.data(), "<architecture>");
+ if (NULL != name) {
+ // Size of "<architecture>"
+ const int nameStart = 14;
+ char *str = strdup(&name[nameStart]);
+ char *term = strchr(str, '<');
+ *term = 0;
+
+ abi_ = Abi::Find(str);
+ free(str);
+ }
+
+ // Check if we failed to find the correct ABI
+ if (NULL == abi_) {
+ IPlatform::LogError("Failed to find ABI for %s\n", reply.data());
+ return false;
+ }
+
+ return Update();
+}
+
+bool Host::Update() {
+ ThreadVector_t old_ids;
+ ThreadVector_t new_ids;
+
+ ThreadMap_t::const_iterator itr = threads_.begin();
+ while (itr != threads_.end()) {
+ old_ids.push_back(itr->first);
+ itr++;
+ }
+
+ if (!RequestThreadList(&new_ids)) return false;
+
+ for (uint32_t loop = 0; loop < new_ids.size(); loop++) {
+ // Large enough for log10(2^32) + NUL + "Hg";
+ char tmp[16];
+
+ uint32_t id = new_ids[loop];
+ string request;
+ string ignore_reply;
+
+ snprintf(tmp, sizeof(tmp), "Hg%x", id);
+ if (!Request(tmp, &ignore_reply)) {
+ IPlatform::LogError("Failed to set thread context for %d.\n", id);
+ continue;
+ }
+
+ Packet req, resp;
+ req.AddString("g");
+ if (!Send(&req, &resp)) {
+ IPlatform::LogError("Failed to get thread registers for %d.\n", id);
+ continue;
+ }
+
+ Thread *thread = threads_[id];
+ if (NULL == thread) {
+ thread = new Thread(abi_, id);
+ threads_[id] = thread;
+ }
+
+ resp.GetBlock(thread->ctx_, abi_->GetContextSize());
+ }
+
+ // Update the current state
+ string reply;
+ if (Request("?", &reply)) return ParseStopPacket(reply.data());
+
+ // If we are not "broken" then we must have failed to update
+ return false;
+}
+
+bool Host::GetThreads(ThreadVector_t* threads) const {
+ // We can get threads if stopped
+ if (HS_STOPPED != status_) return false;
+
+ threads->clear();
+ ThreadMap_t::const_iterator itr = threads_.begin();
+ while (itr != threads_.end()) {
+ threads->push_back(itr->first);
+ itr++;
+ }
+
+ return true;
+}
+
+bool Host::Step() {
+ Packet out;
+
+ // We can only step if we are stopped
+ if (HS_STOPPED != status_) return false;
+
+ out.AddRawChar('s');
+ if (SendOnly(&out)) {
+ // We are running again (even if we expect to immediately break)
+ status_ = HS_RUNNING;
+ return true;
+ }
+
+ return false;
+}
+
+bool Host::Continue() {
+ Packet out;
+
+ // We can only step if we are stopped
+ if (HS_STOPPED != status_) return false;
+
+ out.AddRawChar('c');
+ if (SendOnly(&out)) {
+ // We are running again
+ status_ = HS_RUNNING;
+ return true;
+ }
+
+ return false;
+}
+
+// Wait to see if we receive a break
+bool Host::WaitForBreak() {
+ // We can not wait if we are not running
+ if (HS_RUNNING != status_) return false;
+
+ Packet rx;
+ std::string str;
+ if (session_->GetPacket(&rx) == false) return false;
+
+ rx.GetString(&str);
+ if (ParseStopPacket(str.data())) return Update();
+ return false;
+}
+
+
+
+Host::Thread* Host::GetThread(uint32_t id) {
+ ThreadMap_t::const_iterator itr;
+ itr = threads_.find(id);
+
+ if (itr == threads_.end()) return NULL;
+
+ return itr->second;
+}
+
+bool Host::ParseStopPacket(const char *data) {
+ if (strlen(data) < 3) return false;
+
+ // Stop in the form of (S|T|W|X)[XX]{n=xxx,r=yyy;{...}}
+ // where XX is the result code (Unix signal number for stops)
+ // or the form of OXX{XX..} where XX is a hex pair encoded string.
+ switch (data[0]) {
+ // Both S & T signals are a normal stop
+ case 'S':
+ case 'T':
+ status_ = HS_STOPPED;
+ break;
+
+ case 'W':
+ status_ = HS_EXIT;
+ break;
+
+ case 'X':
+ status_ = HS_TERMINATED;
+ break;
+
+ case 'O':
+ return true;
+
+ default:
+ return false;
+ }
+
+ if (!NibblesToByte(&data[1], &lastSignal_)) return false;
+
+ return true;
+}
+
+
+bool Host::RequestThreadList(ThreadVector_t* ids) {
+ string reply;
+
+ ids->clear();
+
+ if (!Request("qfThreadInfo", &reply)) return false;
+
+ do {
+ // Check if we are done
+ if (reply == "l") return true;
+
+ if (reply[0] != 'm') {
+ IPlatform::LogError("Expecting diffent thread format: %s\n",
+ reply.data());
+ return false;
+ }
+
+ stringvec replys = StringSplit(&reply[1], ",");
+ for (uint32_t loop = 0; loop < replys.size(); loop++) {
+ ids->push_back(strtol(replys[loop].data(), NULL, 16));
+ }
+ } while (Request("qsThreadInfo", &reply));
+
+ IPlatform::LogError("Failed request of qsThreadInfo.\n", reply.data());
+ return false;
+}
+
+bool Host::HasProperty(const char *name) const {
+ std::map<string, string>::const_iterator itr;
+ itr = properties_.find(name);
+
+ return properties_.end() != itr;
+}
+
+bool Host::ReadProperty(const char *name, string* val) const {
+ std::map<string, string>::const_iterator itr;
+ itr = properties_.find(name);
+
+ if (properties_.end() == itr) return false;
+
+ *val = itr->second;
+ return true;
+}
+
+
+bool Host::ReadObject(const char *type, const char *name, string *reply) {
+ uint32_t offset = 0;
+ uint32_t maxTX = 1024;
+ reply->clear();
+
+ if (ReadProperty("PacketSize", reply)) {
+ maxTX = static_cast<uint32_t>(strtol(reply->data(), NULL, 16));
+ }
+
+ while (1) {
+ // We make this 64B to hold 2xlog16(2^64) + NUL + 14 characters although
+ // in practice these tend to be 16b values.
+ char tmp[64];
+
+ string replyPiece;
+ string query = "qXfer:";
+ query += type;
+ query += ":read:";
+ query += name;
+ snprintf(tmp, sizeof(tmp), ":%x,%x", offset, maxTX);
+ query += tmp;
+
+ if (!Request(query, &replyPiece)) return false;
+
+ if (replyPiece[0] == 'l') {
+ *reply += &replyPiece[1];
+ return true;
+ }
+
+ if ((replyPiece[0] == 'E') && (replyPiece.length() == 3)) {
+ return false;
+ }
+
+ if (replyPiece[0] != 'm') {
+ IPlatform::LogError("Expecting more or end signal got:\n\t%s.",
+ replyPiece.data());
+ return false;
+ }
+
+ *reply += replyPiece;
+ offset += static_cast<uint32_t>(replyPiece.length());
+ }
+
+ return true;
+}
+
+bool Host::Break() {
+ char brk[2] = { 0x03, 0x00 };
+
+ // We can only break if running
+ if (HS_RUNNING != status_) return false;
+
+ status_ = HS_STOPPING;
+ return RequestOnly(brk);
+}
+
+bool Host::Detach() {
+ // We can only detach if stopped
+ if (HS_STOPPED != status_) return false;
+
+ return RequestOnly("d");
+}
+
+int32_t Host::GetSignal() {
+ // We always return the lasted cached value
+ return lastSignal_;
+}
+
+Host::Status Host::GetStatus() {
+ return status_;
+}
+
+bool Host::GetMemory(void *dst, uint64_t addr, uint32_t size) {
+ char *ptr = reinterpret_cast<char *>(dst);
+ uint32_t maxTX = 1024;
+ string reply;
+ Packet pktReq, pktReply;
+
+ // We can only access memory if stopped
+ if (HS_STOPPED != status_) return false;
+
+ if (ReadProperty("PacketSize", &reply)) {
+ maxTX = strtoul(reply.data(), NULL, 16);
+ }
+
+ // Reply is encoded as two nibbles plus a single char
+ // Mxxxxxxxx...
+ maxTX = (maxTX - 1) / 2;
+
+ while (size) {
+ uint32_t len = size;
+ if (len > maxTX) len = maxTX;
+
+ pktReq.Clear();
+ pktReq.AddRawChar('m');
+ pktReq.AddNumberSep(addr, ',');
+ pktReq.AddNumberSep(size, 0);
+
+ if (!Send(&pktReq, &pktReply)) return false;
+
+ if (!pktReply.GetBlock(ptr, len)) return false;
+
+ ptr += len;
+ addr += len;
+ size -= len;
+ }
+
+ return true;
+}
+
+bool Host::SetMemory(const void *src, uint64_t addr, uint32_t size) {
+ const char *ptr = reinterpret_cast<const char *>(src);
+ uint32_t maxTX = 1024;
+ string reply;
+ Packet pktReq, pktReply;
+
+ // We can only access memory if stopped
+ if (HS_STOPPED != status_) return false;
+
+ if (ReadProperty("PacketSize", &reply)) {
+ maxTX = strtoul(reply.data(), NULL, 16);
+ }
+
+ // Reply is encoded as two nibbles plus a single char(3), plus address (16)
+ // a and size (8) or Maaaaaaaaaaaaaaaa,ssssssss:(27)
+ maxTX = (maxTX - 27) / 2;
+
+ while (size) {
+ uint32_t len = size;
+ if (len > maxTX) len = maxTX;
+
+ pktReq.Clear();
+ pktReq.AddRawChar('M');
+ pktReq.AddNumberSep(addr, ',');
+ pktReq.AddNumberSep(len, ':');
+ pktReq.AddBlock(src, len);
+
+ if (!Send(&pktReq, &pktReply)) return false;
+
+ ptr += len;
+ addr += len;
+ size -= len;
+ }
+
+ return true;
+}
+
+bool Host::RequestOnly(const string& req) {
+ Packet pktReq;
+
+ pktReq.AddString(req.data());
+ return SendOnly(&pktReq);
+}
+
+bool Host::Request(const string& req, string *resp) {
+ Packet pktReq, pktResp;
+
+ pktReq.AddString(req.data());
+ bool result = Send(&pktReq, &pktResp);
+
+ pktResp.GetString(resp);
+
+ // Check for error code on return
+ if ((resp->length() == 3) && (resp->data()[0] == 'E')) return false;
+
+ return result;
+}
+
+bool Host::SendOnly(Packet *tx) {
+ return session_->SendPacketOnly(tx);
+}
+
+bool Host::Send(Packet *tx, Packet *rx) {
+ if (!session_->SendPacket(tx)) return false;
+ return session_->GetPacket(rx);
+}
+
+} // namespace gdb_rsp
+
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/host.h b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/host.h
new file mode 100644
index 0000000..230393c
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/host.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+// This module provides an object for handling RSP responsibilities of
+// the host side of the connection. The host behaves like a cache, and
+// is responsible for syncronization of state between the Target and Host.
+// For example, the Host is responsible for updating the thread context
+// before restarting the Target, and for updating it's internal array of
+// threads whenever the Target stops.
+
+#ifndef NATIVE_CLIENT_GDB_RSP_HOST_H_
+#define NATIVE_CLIENT_GDB_RSP_HOST_H_ 1
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "native_client/src/debug_server/port/std_types.h"
+
+namespace gdb_rsp {
+
+class Abi;
+class Packet;
+class Session;
+
+class Host {
+ public:
+ enum Status {
+ HS_UNINIT = -1, // Host is uninitialized
+ HS_RUNNING = 0, // The target is running (no sig since cont.)
+ HS_STOPPING = 1, // Host has issued a break request
+ HS_STOPPED = 2, // Host has received a signal and is stopped
+ HS_EXIT = 3, // Host has received an exit code
+ HS_TERMINATED = 4 // host has received a termination code
+ };
+
+ // The Host::Thread class represents a thread on the Target side, providing
+ // a cache for its state, which is automatically updated by the parent Host
+ // object whenever the target starts or stops.
+ class Thread {
+ public:
+ Thread(const Abi *abi, uint32_t id);
+ ~Thread();
+
+ public:
+ uint32_t GetId() const;
+ const Abi *GetAbi() const;
+ void GetRegister(uint32_t index, void *dst) const;
+ void SetRegister(uint32_t index, const void *src);
+
+ private:
+ uint32_t id_;
+ uint8_t *ctx_;
+ const Abi *abi_;
+
+ friend class Host;
+ };
+
+ typedef std::map<uint32_t, Host::Thread*> ThreadMap_t;
+ typedef std::map<std::string, std::string> PropertyMap_t;
+ typedef std::vector<uint32_t> ThreadVector_t;
+
+ explicit Host(Session *session);
+ ~Host();
+ bool Init();
+
+ // The following functions are provided cached values when possible.
+ // For instance, GetSignal, GetThreads, and GetThread, will return
+ // values that were computed during Update.
+ // cause a communication between the host and target, so the public
+ // functions above should be used when possible.
+
+ // Issue a break request if the target is still running. This is
+ // asynchronous, we won't actually be "broken" until we get the signal
+ bool Break();
+
+ // Requests that we cleanly detach from the target.
+ bool Detach();
+
+ // Get the current status of the Target.
+ Status GetStatus();
+
+ // Get the last signal (which put us into the broken state)
+ int32_t GetSignal();
+
+ // Get a list of currently active threads
+ bool GetThreads(ThreadVector_t *threads) const;
+
+ // Get a thread object by ID.
+ Thread *GetThread(uint32_t id);
+
+ // Get and set a block of target memory.
+ bool GetMemory(void *dst, uint64_t addr, uint32_t size);
+ bool SetMemory(const void *src, uint64_t addr, uint32_t size);
+
+ // Read locally cached properties
+ bool HasProperty(const char *name) const;
+ bool ReadProperty(const char *name, std::string *val) const;
+
+ // Read remote object
+ bool ReadObject(const char *type, const char *name, std::string *val);
+
+ // Set the SINGLE STEP flag on the current thread context, and
+ // putting the target back into the RUN state.
+ bool Step();
+
+ // Issue a step request, putting us back into the RUN state.
+ bool Continue();
+
+ // Wait upto the session's packet timeout to see if we receive a break
+ bool WaitForBreak();
+
+ // The following functions are internal only and cause communication to
+ // happen between the target and host. These functions will always
+ // cause a communication between the host and target, so the public
+ // functions above should be used when possible.
+ protected:
+ // Called whenever the target transitions from running to stopped to
+ // fetch information about the current state.
+ bool Update();
+
+ bool Send(Packet *req, Packet *resp);
+ bool SendOnly(Packet *req);
+
+ bool Request(const std::string &req, std::string *resp);
+ bool RequestOnly(const std::string &req);
+ bool RequestThreadList(ThreadVector_t *ids);
+
+ // Parse a string, returning true and update if a valid stop packet
+ bool ParseStopPacket(const char *data);
+
+ private:
+ Session *session_;
+ const Abi *abi_;
+
+ PropertyMap_t properties_;
+ ThreadMap_t threads_;
+ int32_t lastSignal_;
+ Status status_;
+};
+
+
+} // namespace gdb_rsp
+
+#endif // NATIVE_CLIENT_GDB_RSP_HOST_H_
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/host_test.cc b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/host_test.cc
new file mode 100644
index 0000000..31576f5
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/host_test.cc
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+#include <sstream>
+#include <vector>
+
+#include "native_client/src/debug_server/gdb_rsp/abi.h"
+#include "native_client/src/debug_server/gdb_rsp/host.h"
+#include "native_client/src/debug_server/gdb_rsp/session_mock.h"
+#include "native_client/src/debug_server/gdb_rsp/test.h"
+
+using gdb_rsp::Abi;
+using gdb_rsp::Host;
+using gdb_rsp::SessionMock;
+
+int VerifyProp(Host* host, const char *key, const char *val) {
+ std::string out;
+ if (!host->ReadProperty(key, &out)) {
+ printf("Could not find key: %s.\n", key);
+ return 1;
+ }
+
+ if (out != val) {
+ printf("Property mismatch\n\tVALUE:%s\n\tEXPECTED:%s\n",
+ out.data(), val);
+ return 1;
+ }
+
+ return 0;
+}
+
+int TestHost() {
+ int errs = 0;
+ SessionMock *ses = gdb_rsp::GetGoldenSessionMock(false, false);
+ const gdb_rsp::Abi* abi = gdb_rsp::Abi::Get();
+
+ Host *host = new Host(ses);
+ std::string val;
+ if (!host->Init()) {
+ printf("Failed to init host.\n");
+ return 1;
+ }
+
+ // Verify that the Init/Update transactions were
+ // correctly applied.
+ if (!host->HasProperty("qXfer:features:read")) {
+ printf("Missing property: qXfer:features:read\n");
+ errs++;
+ }
+
+ errs += VerifyProp(host, "PacketSize", "7cf");
+ errs += VerifyProp(host, "qXfer:libraries:read", "true");
+ Host::Thread* thread = host->GetThread(0x1234);
+ if (NULL == thread) {
+ printf("Failed to find expected thead 1234.\n");
+ errs++;
+ } else {
+ for (uint32_t a = 0; a < abi->GetRegisterCount(); a++) {
+ const Abi::RegDef* def = abi->GetRegisterDef(a);
+ uint64_t val;
+ thread->GetRegister(a, &val);
+ if (static_cast<uint32_t>(val) != a) {
+ errs++;
+ printf("Register %s(%d) failed to match expected value: %x",
+ def->name_, a, static_cast<int>(val));
+ break;
+ }
+ }
+ }
+
+ if (host->GetStatus() != Host::HS_STOPPED) {
+ printf("We expect to be stopped at this point.\n");
+ errs++;
+ }
+
+ if (host->GetSignal() != 5) {
+ printf("We expect to see signal 5.\n");
+ errs++;
+ }
+
+ if (ses->PeekAction() != SessionMock::DISCONNECT) {
+ printf("We did not consume all the actions.\n");
+ errs++;
+ }
+
+ // Pop off the DC, and continue processing.
+ ses->GetAction();
+
+ // Verify we are now in a running state
+ ses->AddAction(SessionMock::SEND, "c");
+ host->Continue();
+
+ // We should be running, and timing out on wait
+ if (host->GetStatus() != Host::HS_RUNNING) {
+ printf("We expect to be running at this point.\n");
+ errs++;
+ }
+ if (host->WaitForBreak()) {
+ printf("We should have timed out.\n");
+ errs++;
+ }
+
+ // Insert a signal 11 for us to wait on
+ ses->AddAction(SessionMock::RECV, "S11");
+ AddMockUpdate(ses, 0x11);
+ if (!host->WaitForBreak()) {
+ printf("We should have gotten a signal.\n");
+ errs++;
+ }
+ if (host->GetStatus() != Host::HS_STOPPED) {
+ printf("We expect to be stopped at this point.\n");
+ errs++;
+ }
+ if (host->GetSignal() != 0x11) {
+ printf("We expect to see signal 11.\n");
+ errs++;
+ }
+
+ delete host;
+ delete ses;
+ return errs;
+}
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/packet.cc b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/packet.cc
new file mode 100644
index 0000000..819abcf
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/packet.cc
@@ -0,0 +1,392 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include "native_client/src/debug_server/gdb_rsp/packet.h"
+#include "native_client/src/debug_server/gdb_rsp/util.h"
+#include "native_client/src/debug_server/port/platform.h"
+
+using std::string;
+using port::IPlatform;
+
+
+namespace gdb_rsp {
+
+#define MIN_PAD 1
+#define GROW_SIZE 64
+
+Packet::Packet() {
+ seq_ = -1;
+ Clear();
+}
+
+void Packet::Clear() {
+ data_.clear();
+ data_.resize(GROW_SIZE);
+ data_[0] = 0;
+
+ read_index_ = 0;
+ write_index_ = 0;
+}
+
+void Packet::Rewind() {
+ read_index_ = 0;
+}
+
+bool Packet::EndOfPacket() const {
+ return (read_index_ >= write_index_);
+}
+
+void Packet::AddRawChar(char ch) {
+ // Grow by a fixed amount whenever we are within the pad boundry.
+ // The pad boundry allows for the addition of NUL termination.
+ if (data_.size() <= (write_index_ + MIN_PAD)) {
+ data_.resize(data_.size() + GROW_SIZE);
+ }
+
+ // Add character and always null terminate.
+ data_[write_index_++] = ch;
+ data_[write_index_] = 0;
+}
+
+void Packet::AddWord8(uint8_t ch) {
+ char seq1, seq2;
+
+ IntToNibble(ch >> 4, &seq1);
+ IntToNibble(ch & 0xF, &seq2);
+
+ AddRawChar(seq1);
+ AddRawChar(seq2);
+}
+
+void Packet::AddBlock(const void *ptr, uint32_t len) {
+ assert(ptr);
+
+ const char *p = (const char *) ptr;
+
+ for (uint32_t offs = 0; offs < len; offs++) {
+ AddWord8(p[offs]);
+ }
+}
+
+void Packet::AddWord16(uint16_t val) {
+ AddBlock(&val, sizeof(val));
+}
+
+void Packet::AddWord32(uint32_t val) {
+ AddBlock(&val, sizeof(val));
+}
+
+void Packet::AddWord64(uint64_t val) {
+ AddBlock(&val, sizeof(val));
+}
+
+void Packet::AddString(const char *str) {
+ assert(str);
+
+ while (*str) {
+ AddRawChar(*str);
+ str++;
+ }
+}
+
+void Packet::AddHexString(const char *str) {
+ assert(str);
+
+ while (*str) {
+ AddWord8(*str);
+ str++;
+ }
+}
+
+void Packet::AddNumberSep(uint64_t val, char sep) {
+ char out[sizeof(val) * 2];
+ int nibbles = 0;
+ size_t a;
+
+ // Check for -1 optimization
+ if (val == static_cast<uint64_t>(-1)) {
+ AddRawChar('-');
+ AddRawChar('1');
+ } else {
+ // Assume we have the valuse 0x00001234
+ for (a = 0; a < sizeof(val); a++) {
+ uint8_t byte = static_cast<uint8_t>(val & 0xFF);
+
+ // Stream in with bytes reverse, starting at least significant
+ // So we store 4, then 3, 2, 1
+ IntToNibble(byte & 0xF, &out[nibbles++]);
+ IntToNibble(byte >> 4, &out[nibbles++]);
+
+ // Get the next 8 bits;
+ val >>= 8;
+
+ // Supress leading zeros, so we are done when val hits zero
+ if (val == 0) break;
+ }
+
+ // Strip the high zero for this byte if needed
+ if ((nibbles > 1) && (out[nibbles-1] == '0')) nibbles--;
+
+ // Now write it out reverse to correct the order
+ while (nibbles) {
+ nibbles--;
+ AddRawChar(out[nibbles]);
+ }
+ }
+
+ // If we asked for a sperator, insert it
+ if (sep) AddRawChar(sep);
+}
+
+bool Packet::GetNumberSep(uint64_t *val, char *sep) {
+ uint64_t out = 0;
+ char ch;
+
+ if (!GetRawChar(&ch)) return false;
+
+ // Check for -1
+ if (ch == '-') {
+ if (!GetRawChar(&ch)) return false;
+
+ if (ch == '1') {
+ *val = -1;
+
+ ch = 0;
+ GetRawChar(&ch);
+ if (sep) *sep = ch;
+ return true;
+ }
+ return false;
+ }
+
+ do {
+ int nib;
+
+ // Check for separator
+ if (!NibbleToInt(ch, &nib)) break;
+
+ // Add this nibble.
+ out = (out << 4) + nib;
+
+ // Get the next character (if availible)
+ ch = 0;
+ if (!GetRawChar(&ch)) break;
+ } while (1);
+
+ // Set the value;
+ *val = out;
+
+ // Add the separator if the user wants it...
+ if (sep != NULL) *sep = ch;
+
+ return true;
+}
+
+bool Packet::GetRawChar(char *ch) {
+ assert(ch != NULL);
+
+ if (read_index_ >= write_index_)
+ return false;
+
+ *ch = data_[read_index_++];
+
+ // Check for RLE X*N, where X is the value, N is the reps.
+ if (*ch == '*') {
+ if (read_index_ < 2) {
+ IPlatform::LogError("Unexpected RLE at start of packet.\n");
+ return false;
+ }
+
+ if (read_index_ >= write_index_) {
+ IPlatform::LogError("Unexpected EoP during RLE.\n");
+ return false;
+ }
+
+ // GDB does not use "CTRL" characters in the stream, so the
+ // number of reps is encoded as the ASCII value beyond 28
+ // (which when you add a min rep size of 4, forces the rep
+ // character to be ' ' (32) or greater).
+ int32_t cnt = (data_[read_index_] - 28);
+ if (cnt < 3) {
+ IPlatform::LogError("Unexpected RLE length.\n");
+ return false;
+ }
+
+ // We have just read '*' and incremented the read pointer,
+ // so here is the old state, and expected new state.
+ //
+ // Assume N = 5, we grow by N - size of encoding (3).
+ //
+ // OldP: R W
+ // OldD: 012X*N89 = 8 chars
+ // Size: 012X*N89__ = 10 chars
+ // Move: 012X*__N89 = 10 chars
+ // Fill: 012XXXXX89 = 10 chars
+ // NewP: R W (shifted 5 - 3)
+ //
+ // To accomplish this we must first, resize the vector then move
+ // all remaining characters to the right, by the delta between
+ // the run length, and encoding size. This moves one more char
+ // than needed (the 'N'), but is easier to understand.
+ // NOTE: We add one to the resize to allow for zero termination.
+ data_.resize(write_index_ + cnt - 3 + 1);
+ memmove(&data_[read_index_ + cnt - 3], &data_[read_index_],
+ write_index_ - read_index_);
+
+ // Now me must go back and fill over the previous '*' with the
+ // repeated character for the length of the run minus the original
+ // character which is already correct
+ *ch = data_[read_index_ - 2];
+ memset(&data_[read_index_ - 1], *ch, cnt - 1);
+
+ // Now we update the write_index_, and reterminate the string.
+ write_index_ = data_.size() - 1;
+ data_[write_index_] = 0;
+ }
+ return true;
+}
+
+bool Packet::GetWord8(uint8_t *ch) {
+ assert(ch);
+
+ char seq1, seq2;
+ int val1, val2;
+
+ // Get two ASCII hex values
+ if (!GetRawChar(&seq1)) return false;
+ if (!GetRawChar(&seq2)) return false;
+
+ // Convert them to ints
+ if (!NibbleToInt(seq1, &val1)) return false;
+ if (!NibbleToInt(seq2, &val2)) return false;
+
+ *ch = (val1 << 4) + val2;
+ return true;
+}
+
+bool Packet::GetBlock(void *ptr, uint32_t len) {
+ assert(ptr);
+
+ uint8_t *p = reinterpret_cast<uint8_t *>(ptr);
+ bool res = true;
+
+ for (uint32_t offs = 0; offs < len; offs++) {
+ res = GetWord8(&p[offs]);
+ if (false == res) break;
+ }
+
+ return res;
+}
+
+bool Packet::GetWord16(uint16_t *ptr) {
+ assert(ptr);
+ return GetBlock(ptr, sizeof(*ptr));
+}
+
+bool Packet::GetWord32(uint32_t *ptr) {
+ assert(ptr);
+ return GetBlock(ptr, sizeof(*ptr));
+}
+
+bool Packet::GetWord64(uint64_t *ptr) {
+ assert(ptr);
+ return GetBlock(ptr, sizeof(*ptr));
+}
+
+
+bool Packet::GetString(string* str) {
+ if (EndOfPacket()) return false;
+
+ *str = &data_[read_index_];
+ read_index_ = write_index_;
+ return true;
+}
+
+bool Packet::GetHexString(string* str) {
+ char ch;
+ if (EndOfPacket()) return false;
+
+ // Pull values until we hit a seperator
+ str->clear();
+ while (GetRawChar(&ch)) {
+ if (NibbleToInt(ch, NULL)) {
+ *str += ch;
+ } else {
+ read_index_--;
+ break;
+ }
+ }
+ return true;
+}
+
+bool Packet::GetStringCB(void *ctx, StrFunc_t cb) {
+ assert(NULL != ctx);
+
+ if (EndOfPacket()) {
+ cb(ctx, NULL);
+ return false;
+ }
+
+ cb(ctx, &data_[read_index_]);
+ read_index_ = write_index_;
+ return true;
+}
+
+bool Packet::GetHexStringCB(void *ctx, StrFunc_t cb) {
+ assert(NULL != ctx);
+
+ std::string out;
+ char ch;
+
+ if (EndOfPacket()) {
+ cb(ctx, NULL);
+ return false;
+ }
+
+ // Pull values until we hit a seperator
+ while (GetRawChar(&ch)) {
+ if (NibbleToInt(ch, NULL)) {
+ out += ch;
+ } else {
+ read_index_--;
+ break;
+ }
+ }
+
+ // Call the CB with the availible string
+ cb(ctx, out.data());
+ return true;
+}
+
+
+const char *Packet::GetPayload() const {
+ return &data_[0];
+}
+
+bool Packet::GetSequence(int32_t *ch) const {
+ assert(ch);
+
+ if (seq_ != -1) {
+ *ch = seq_;
+ return true;
+ }
+
+ return false;
+}
+
+void Packet::SetSequence(int32_t val) {
+ seq_ = val;
+}
+
+} // namespace gdb_rsp
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/packet.h b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/packet.h
new file mode 100644
index 0000000..d94c24e
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/packet.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+
+// This module provides interfaces for writing to and reading from a
+// GDB RSP packet. A packet represents a single data transfer from one
+// RSP endpoint to another excluding all additional encapsulating data
+// generated by the session layer. See:
+// http://ftp.gnu.org/old-gnu/Manuals/gdb-5.1.1/html_node/gdb_129.html#SEC134
+//
+// The packet object stores the data in a character vector in a cooked format,
+// tracking both a read and write position within the stream. An empty
+// packet still contains a valid pointer to NULL, so it is always safe to
+// get the Payload and use it as a string. In addition packets may be
+// sequenced by setting an 8 bit sequence number which helps both sides
+// detect when packets have been lost. By default the sequence number is not
+// set which is represented as a -1.
+//
+// This API is not expected to throw unless the underlying vector attempts
+// to resize when the system is out of memory, in which case it will throw
+// a std::bad_alloc.
+#ifndef NATIVE_CLIENT_GDB_RSP_PACKET_H_
+#define NATIVE_CLIENT_GDB_RSP_PACKET_H_ 1
+
+#include <string>
+#include <vector>
+
+#include "native_client/src/debug_server/port/std_types.h"
+
+namespace gdb_rsp {
+
+class Packet {
+ typedef void (*StrFunc_t)(void *ctx, const char *str);
+
+ public:
+ Packet();
+
+ // Empty the vector and reset the read/write pointers.
+ void Clear();
+
+ // Reset the read pointer, allowing the packet to be re-read.
+ void Rewind();
+
+ // Return true of the read pointer has reached the write pointer.
+ bool EndOfPacket() const;
+
+ // Store a single raw 8 bit value
+ void AddRawChar(char ch);
+
+ // Store a block of data as hex pairs per byte
+ void AddBlock(const void *ptr, uint32_t len);
+
+ // Store an 8, 16, 32, or 64 bit word as a block without removing preceeding
+ // zeros. This is used for fixed sized fields.
+ void AddWord8(uint8_t val);
+ void AddWord16(uint16_t val);
+ void AddWord32(uint32_t val);
+ void AddWord64(uint64_t val);
+
+ // Store a number up to 64 bits, with preceeding zeros removed. Since
+ // zeros can be removed, the width of this number is unknown, and always
+ // followed by NUL or a seperator (non hex digit).
+ void AddNumberSep(uint64_t val, char sep);
+
+ // Add a raw string. This is dangerous since the other side may incorrectly
+ // interpret certain special characters such as: ":,#$"
+ void AddString(const char *str);
+
+ // Add a string stored as a stream of ASCII hex digit pairs. It is safe
+ // to use any character in this stream. If this does not terminate the
+ // packet, there should be a sperator (non hex digit) immediately following.
+ void AddHexString(const char *str);
+
+ // Retrieve a single character if available
+ bool GetRawChar(char *ch);
+
+ // Retreive "len" ASCII character pairs.
+ bool GetBlock(void *ptr, uint32_t len);
+
+ // Retreive a 8, 16, 32, or 64 bit word as pairs of hex digits. These
+ // functions will always consume bits/4 characters from the stream.
+ bool GetWord8(uint8_t *val);
+ bool GetWord16(uint16_t *val);
+ bool GetWord32(uint32_t *val);
+ bool GetWord64(uint64_t *val);
+
+ // Retreive a number and the seperator. If SEP is null, the seperator is
+ // consumed but thrown away.
+ bool GetNumberSep(uint64_t *val, char *sep);
+
+ // Get a string from the stream
+ bool GetString(std::string *str);
+ bool GetHexString(std::string *str);
+
+ // Callback with the passed in context, and a NUL terminated string.
+ // These methods provide a means to avoid an extra memcpy.
+ bool GetStringCB(void *ctx, StrFunc_t cb);
+ bool GetHexStringCB(void *ctx, StrFunc_t cb);
+
+ // Return a pointer to the entire packet payload
+ const char *GetPayload() const;
+
+ // Returns true and the sequence number, or false if it is unset.
+ bool GetSequence(int32_t *seq) const;
+
+ // Set the sequence number.
+ void SetSequence(int32_t seq);
+
+ private:
+ int32_t seq_;
+ std::vector<char> data_;
+ size_t read_index_;
+ size_t write_index_;
+};
+
+} // namespace gdb_rsp
+
+#endif // NATIVE_CLIENT_GDB_RSP_PACKET_H_
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/packet_test.cc b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/packet_test.cc
new file mode 100644
index 0000000..dc94d81
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/packet_test.cc
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+#include <sstream>
+#include <vector>
+
+#include "native_client/src/debug_server/gdb_rsp/packet.h"
+#include "native_client/src/debug_server/gdb_rsp/test.h"
+
+using gdb_rsp::Packet;
+
+int VerifyPacket(Packet *wr, Packet *rd, void *ctx, PacketFunc_t tx) {
+ int errs = 0;
+ char ch;
+ std::string str;
+
+ uint8_t byte = 0;
+ uint16_t word16 = 0;
+ uint32_t word32 = 0;
+
+ // Clear the packet may be reused
+ wr->Clear();
+
+ // Verify non alligned writes
+ wr->AddRawChar('X');
+ wr->AddWord16(0x1234);
+ wr->AddWord32(0x56789ABC);
+ wr->AddWord8(0xFF);
+ if (tx) tx(ctx, wr, rd);
+ rd->GetString(&str);
+ if (strcmp("X3412bc9a7856ff", str.data())) errs++;
+
+ // Verify rewind
+ rd->Rewind();
+ rd->GetRawChar(&ch);
+ if (ch != 'X') errs++;
+
+ rd->GetWord16(&word16);
+ if (word16 != 0x1234) errs++;
+
+ rd->GetWord32(&word32);
+ if (word32 != 0x56789ABC) errs++;
+
+ rd->GetWord8(&byte);
+ if (byte != 0xFF) errs++;
+
+ // Check Empty Send
+ wr->Clear();
+ if (tx) tx(ctx, wr, rd);
+ if (rd->GetRawChar(&ch)) {
+ errs++;
+ printf("Was expecting an empty packet\n");
+ }
+
+ // Check nibble/byte order
+ // Check edge cases of 1, 0, -1, -2
+ wr->AddNumberSep(0x123, ',');
+ wr->AddNumberSep(1, ',');
+ wr->AddNumberSep(0, ',');
+ wr->AddNumberSep(static_cast<uint64_t>(-1), ',');
+ wr->AddNumberSep(static_cast<uint64_t>(-2), 0);
+ if (tx) tx(ctx, wr, rd);
+ rd->GetString(&str);
+ if (strcmp("123,1,0,-1,fffffffffffffffe", str.data()) != 0) errs++;
+
+ // We push "compress" which was generated by a real gdbserver, through into
+ // our packet, then pull it out as a block to decompress, then check against
+ // the 32b values generated by a real gdb debugger. (Inline golden file)
+ const char *compress = "0*488c7280038c72800d4c72800030**fd00637702020* 23"
+ "0*\"2b0*\"2b0*\"2b0*\"530*\"2b0*}00080f83f00c022f8"
+ "07c022f80f3c80040*&80ff3f0**80ff3f7f030* 30*\"0f*"
+ " 0* 58050* 85f7196c2b0*\"b0b801010*}0*}0*b801f0* ";
+ uint32_t vals[16] = { 0, 0, 0, 0x28C788, 0x28C738, 0x28c7d4, 3, 0, 0x776300FD,
+ 0x202, 0x23, 0x2B, 0x2B, 0x2B, 0x53, 0x2B };
+ uint32_t tmp[16];
+
+ wr->Clear();
+ wr->AddString(compress);
+ if (tx) tx(ctx, wr, rd);
+ rd->GetBlock(tmp, sizeof(vals));
+ if (memcmp(tmp, vals, sizeof(vals))) {
+ errs++;
+ printf("Failed to decompress as expected.\n");
+ }
+
+ if (errs)
+ printf("FAILED PACKET TEST\n");
+
+ return errs;
+}
+
+int TestPacket() {
+ int errs = 0;
+ Packet pkt;
+
+ errs += VerifyPacket(&pkt, &pkt, NULL, NULL);
+ return errs;
+}
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/session.cc b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/session.cc
new file mode 100644
index 0000000..f62ad87
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/session.cc
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <string>
+#include <sstream>
+
+#include "native_client/src/debug_server/gdb_rsp/packet.h"
+#include "native_client/src/debug_server/gdb_rsp/session.h"
+#include "native_client/src/debug_server/gdb_rsp/util.h"
+
+#include "native_client/src/debug_server/port/mutex.h"
+#include "native_client/src/debug_server/port/platform.h"
+#include "native_client/src/debug_server/port/transport.h"
+
+using port::IPlatform;
+using port::ITransport;
+using port::IMutex;
+using port::MutexLock;
+
+// Use a timeout of 1 second
+int const kSessionTimeoutMs = 1000;
+
+namespace gdb_rsp {
+
+Session::Session()
+ : mutex_(NULL),
+ io_(NULL),
+ flags_(0),
+ seq_(0),
+ connected_(false) {
+}
+
+Session::~Session() {
+ if (mutex_) IMutex::Free(mutex_);
+}
+
+
+bool Session::Init(port::ITransport *transport) {
+ if (NULL == transport) return false;
+
+ mutex_ = IMutex::Allocate();
+ if (NULL == mutex_) return false;
+
+ connected_ = true;
+ io_ = transport;
+ return true;
+}
+
+void Session::SetFlags(uint32_t flags) {
+ flags_ |= flags;
+}
+
+void Session::ClearFlags(uint32_t flags) {
+ flags_ &= ~flags;
+}
+
+uint32_t Session::GetFlags() {
+ return flags_;
+}
+
+bool Session::DataAvailable() {
+ assert(io_);
+
+ return io_->ReadWaitWithTimeout(kSessionTimeoutMs);
+}
+
+bool Session::Connected() {
+ return connected_;
+}
+
+bool Session::GetChar(char *ch) {
+ assert(io_);
+
+ // Attempt to select this IO for reading.
+ if (DataAvailable() == false) return false;
+
+ int32_t len = io_->Read(ch, 1);
+
+ // If data is "availible" but we can't read, it must be closed.
+ if (len < 1) {
+ io_->Disconnect();
+ connected_ = false;
+ return false;
+ }
+
+ return true;
+}
+
+
+bool Session::SendPacket(Packet *pkt) {
+ MutexLock lock(mutex_);
+ char ch;
+
+ do {
+ if (!SendPacketOnly(pkt)) return false;
+
+ // If ACKs are off, we are done.
+ if (GetFlags() & IGNORE_ACK) break;
+
+ // Otherwise, poll for '+'
+ if (!GetChar(&ch)) return false;
+
+ // Retry if we didn't get a '+'
+ } while (ch != '+');
+
+ return true;
+}
+
+
+bool Session::SendPacketOnly(Packet *pkt) {
+ MutexLock lock(mutex_);
+
+ const char *ptr;
+ char ch;
+ std::stringstream outstr;
+
+ char run_xsum = 0;
+ int32_t seq;
+
+ ptr = pkt->GetPayload();
+
+ if (!pkt->GetSequence(&seq) && (GetFlags() & USE_SEQ)) {
+ pkt->SetSequence(seq_++);
+ }
+
+ // Signal start of response
+ outstr << '$';
+
+ // If there is a sequence, send as two nibble 8bit value + ':'
+ if (pkt->GetSequence(&seq)) {
+ IntToNibble((seq & 0xFF) >> 4, &ch);
+ outstr << ch;
+ run_xsum += ch;
+
+ IntToNibble(seq & 0xF, &ch);
+ outstr << ch;
+ run_xsum += ch;
+
+ ch = ':';
+ outstr << ch;
+ run_xsum += ch;
+ }
+
+ // Send the main payload
+ int offs = 0;
+ while ((ch = ptr[offs++]) != 0) {
+ outstr << ch;
+ run_xsum += ch;
+ }
+
+ // Send XSUM as two nible 8bit value preceeded by '#'
+ outstr << '#';
+ IntToNibble((run_xsum >> 4) & 0xF, &ch);
+ outstr << ch;
+ IntToNibble(run_xsum & 0xF, &ch);
+ outstr << ch;
+
+ return SendStream(outstr.str().data());
+}
+
+// We do not take the mutex here since we already have it
+// this function is protected so it can't be called directly.
+bool Session::SendStream(const char *out) {
+ int32_t len = static_cast<int32_t>(strlen(out));
+ int32_t sent = 0;
+
+ assert(io_);
+
+ while (sent < len) {
+ const char *cur = &out[sent];
+ int32_t tx = io_->Write(cur, len - sent);
+
+ if (tx <= 0) {
+ IPlatform::LogWarning("Send of %d bytes : '%s' failed.\n", len, out);
+ io_->Disconnect();
+ connected_ = false;
+ return false;
+ }
+
+ sent += tx;
+ }
+
+ if (GetFlags() & DEBUG_SEND) IPlatform::LogInfo("TX %s\n", out);
+ return true;
+}
+
+
+// Attempt to receive a packet
+bool Session::GetPacket(Packet *pkt) {
+ assert(io_);
+
+ MutexLock lock(mutex_);
+
+ char run_xsum, fin_xsum, ch;
+ std::string in;
+ int has_seq, offs;
+
+ // If nothing is waiting, return false
+ if (!io_->ReadWaitWithTimeout(kSessionTimeoutMs)) return false;
+
+ // Toss characters until we see a start of command
+ do {
+ if (!GetChar(&ch)) return false;
+ in += ch;
+ } while (ch != '$');
+
+ retry:
+ has_seq = 1;
+ offs = 0;
+
+ // If nothing is waiting, return NONE
+ if (!io_->ReadWaitWithTimeout(kSessionTimeoutMs)) return false;
+
+ // Clear the stream
+ pkt->Clear();
+
+ // Prepare XSUM calc
+ run_xsum = 0;
+ fin_xsum = 0;
+
+ // Stream in the characters
+ while (1) {
+ if (!GetChar(&ch)) return false;
+
+ in += ch;
+ // Check SEQ statemachine xx:
+ switch (offs) {
+ case 0:
+ case 1:
+ if (!NibbleToInt(ch, 0)) has_seq = 0;
+ break;
+
+ case 2:
+ if (ch != ':') has_seq = 0;
+ break;
+ }
+ offs++;
+
+ // If we see a '#' we must be done with the data
+ if (ch == '#') break;
+
+ // If we see a '$' we must have missed the last cmd
+ if (ch == '$') {
+ IPlatform::LogInfo("RX Missing $, retry.\n");
+ goto retry;
+ }
+ // Keep a running XSUM
+ run_xsum += ch;
+ pkt->AddRawChar(ch);
+ }
+
+
+ // Get two Nibble XSUM
+ if (!GetChar(&ch)) return false;
+ in += ch;
+
+ int val;
+ NibbleToInt(ch, & val);
+ fin_xsum = val << 4;
+
+ if (!GetChar(&ch)) return false;
+ in += ch;
+ NibbleToInt(ch, &val);
+ fin_xsum |= val;
+
+ if (GetFlags() & DEBUG_RECV) IPlatform::LogInfo("RX %s\n", in.data());
+
+ // Pull off teh sequence number if we have one
+ if (has_seq) {
+ uint8_t seq;
+ char ch;
+
+ pkt->GetWord8(&seq);
+ pkt->SetSequence(seq);
+ pkt->GetRawChar(&ch);
+ if (ch != ':') {
+ IPlatform::LogError("RX mismatched SEQ.\n");
+ return false;
+ }
+ }
+
+ // If ACKs are off, we are done.
+ if (GetFlags() & IGNORE_ACK) return true;
+
+ // If the XSUMs don't match, signal bad packet
+ if (fin_xsum == run_xsum) {
+ char out[4] = { '+', 0, 0, 0};
+ int32_t seq;
+
+ // If we have a sequence number
+ if (pkt->GetSequence(&seq)) {
+ // Respond with Sequence number
+ IntToNibble(seq >> 4, &out[1]);
+ IntToNibble(seq & 0xF, &out[2]);
+ }
+ return SendStream(out);
+ } else {
+ // Resend a bad XSUM and look for retransmit
+ SendStream("-");
+
+ IPlatform::LogInfo("RX Bad XSUM, retry\n");
+ goto retry;
+ }
+
+ return true;
+}
+
+} // End of namespace gdb_rsp
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/session.h b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/session.h
new file mode 100644
index 0000000..ee3ce31
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/session.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+
+// This module provides interfaces for writing to and reading from a
+// GDB RSP connection. The connection uses a generic transport
+// object for sending packets from one endpoint to another. The session
+// is responsible to delievery including generation of checksums and
+// retransmits and handling timeouts as needed. See:
+// http:// ftp.gnu.org/old-gnu/Manuals/gdb-5.1.1/html_node/gdb_129.html#SEC134
+//
+// All data is read one character at a time through 'GetChar' which will
+// poll on DataAvail and timeout after one second, allowing the session
+// to handle blocking transports.
+//
+// The session object is not reentrant, and will have unpredictable
+// results if two threads attempt to read from the session at the same
+// time.
+//
+// This module may throw a std::bad_alloc if there is an error while trying
+// to resize the packet. In addition, the underlying ITransport is free
+// to throw an exception if the connection is lost, which will pass out
+// of any packet send or receive function.
+#ifndef NATIVE_CLIENT_GDB_RSP_SESSION_H_
+#define NATIVE_CLIENT_GDB_RSP_SESSION_H_ 1
+
+#include <sstream>
+
+#include "native_client/src/debug_server/port/std_types.h"
+#include "native_client/src/debug_server/port/mutex.h"
+#include "native_client/src/debug_server/port/transport.h"
+
+namespace gdb_rsp {
+
+class Packet;
+
+// Session is not inteded to be derived from, protected members are
+// protected only for unit testing purposes.
+class Session {
+ public:
+ Session();
+ virtual ~Session();
+
+ enum {
+ IGNORE_ACK = 1, // Do not emit or wait for '+' from RSP stream.
+ USE_SEQ = 2, // Automatically use a sequence number
+ DEBUG_SEND = 4, // Log all SENDs
+ DEBUG_RECV = 8, // Log all RECVs
+ DEBUG_MASK = (DEBUG_SEND | DEBUG_RECV)
+ };
+
+ public:
+ virtual bool Init(port::ITransport *transport);
+ virtual void SetFlags(uint32_t flags);
+ virtual void ClearFlags(uint32_t flags);
+ virtual uint32_t GetFlags();
+
+ virtual bool SendPacketOnly(Packet *packet);
+ virtual bool SendPacket(Packet *packet);
+ virtual bool GetPacket(Packet *packet);
+ virtual bool DataAvailable();
+ virtual bool Connected();
+
+ protected:
+ virtual bool GetChar(char *ch);
+ virtual bool SendStream(const char *str);
+
+ private:
+ Session(const Session&);
+ Session &operator=(const Session&);
+
+ protected:
+ port::IMutex *mutex_; // Lock to enforce correct response order.
+ port::ITransport *io_; // Transport object not owned by the Session.
+ uint32_t flags_; // Session flags for Sequence/Ack generation.
+ uint8_t seq_; // Next sequence number to use or -1.
+ bool connected_; // Is the connection still valid.
+};
+
+} // namespace gdb_rsp
+
+#endif // NATIVE_CLIENT_GDB_RSP_SESSION_H_
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/session_mock.cc b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/session_mock.cc
new file mode 100644
index 0000000..55afe22
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/session_mock.cc
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string>
+#include <sstream>
+
+#include "native_client/src/debug_server/gdb_rsp/abi.h"
+#include "native_client/src/debug_server/gdb_rsp/session.h"
+#include "native_client/src/debug_server/gdb_rsp/session_mock.h"
+#include "native_client/src/debug_server/port/platform.h"
+#include "native_client/src/debug_server/port/thread.h"
+#include "native_client/src/debug_server/port/transport.h"
+
+#ifdef WIN32
+#define snprintf _snprintf
+#endif
+
+namespace gdb_rsp {
+
+SessionMock::SessionMock(bool target) : Session() {
+ target_ = target;
+ cur_ = 0;
+
+ Session::Init(new TransportMock);
+ SetFlags(IGNORE_ACK);
+}
+
+SessionMock::~SessionMock() {
+ for (size_t a = 0; a < actions_.size(); a++)
+ delete actions_[a];
+
+ delete static_cast<TransportMock*>(io_);
+}
+
+bool SessionMock::SendPacketOnly(gdb_rsp::Packet *packet) {
+ if (PeekAction() == SEND) {
+ Action *pact = GetAction();
+ const char *str = packet->GetPayload();
+
+ if (GetFlags() & DEBUG_SEND) port::IPlatform::LogInfo("TX %s\n", str);
+
+ // Check if we match
+ if (pact->str_ == str) return true;
+
+ port::IPlatform::LogInfo("Mismatch:\n\tSENDING: %s\n\tEXPECT: %s\n",
+ str, pact->str_.data());
+ }
+ return false;
+}
+
+bool SessionMock::GetPacket(gdb_rsp::Packet *packet) {
+ if (PeekAction() == RECV) {
+ Action *pact = GetAction();
+ packet->Clear();
+ packet->AddString(pact->str_.data());
+ if (GetFlags() & DEBUG_RECV) {
+ port::IPlatform::LogInfo("RX %s\n", pact->str_.data());
+ }
+ return true;
+ }
+ return false;
+}
+
+bool SessionMock::DataAvailable() {
+ return PeekAction() == RECV;
+}
+
+SessionMock::ActionType SessionMock::PeekAction() {
+ if (cur_ < actions_.size()) {
+ Action* pact = actions_[cur_];
+ ActionType at = pact->type_;
+ if (DISCONNECT == at) {
+ connected_ = false;
+ }
+ return at;
+ }
+ return TIMEOUT;
+}
+
+SessionMock::Action* SessionMock::GetAction() {
+ if (cur_ < actions_.size()) return actions_[cur_++];
+ return NULL;
+}
+
+void SessionMock::AddAction(ActionType act, const char *str) {
+ Action *pact = new Action;
+
+ pact->type_ = act;
+ pact->str_ = str;
+ actions_.push_back(pact);
+}
+
+void SessionMock::AddTransaction(const char *snd, const char *rcv) {
+ if (target_) {
+ // If we are a target, we get a command, then respond
+ AddAction(RECV, snd);
+ AddAction(SEND, rcv);
+ } else {
+ // If we are a host, we send a command, and expect a reply
+ AddAction(SEND, snd);
+ AddAction(RECV, rcv);
+ }
+}
+
+void AddMockInit(SessionMock* ses) {
+ const gdb_rsp::Abi* abi = gdb_rsp::Abi::Get();
+
+ // Setup supported query
+ ses->AddTransaction(
+ "qSupported",
+ "PacketSize=7cf;qXfer:libraries:read+;qXfer:features:read+");
+
+ // Setup arch query
+ std::string str = "l<target><architecture>";
+ str += abi->GetName();
+ str += "</architecture></target>";
+ ses->AddTransaction(
+ "qXfer:features:read:target.xml:0,7cf",
+ str.data());
+}
+
+void AddMockUpdate(SessionMock* ses, uint8_t sig) {
+ const gdb_rsp::Abi* abi = gdb_rsp::Abi::Get();
+
+ // Setup thread query
+ ses->AddTransaction(
+ "qfThreadInfo",
+ "m1234");
+ ses->AddTransaction(
+ "qsThreadInfo",
+ "l");
+ ses->AddTransaction(
+ "Hg1234",
+ "OK");
+
+ // Setup Register Query
+ std::string str = "";
+ port::IThread *thread = port::IThread::Acquire(0x1234, true);
+ for (uint32_t a = 0; a < abi->GetRegisterCount(); a++) {
+ char tmp[8];
+ const Abi::RegDef* def = abi->GetRegisterDef(a);
+ uint64_t val = static_cast<uint64_t>(a);
+ uint8_t *pval = reinterpret_cast<uint8_t*>(&val);
+
+ // Create a hex version of the register number of
+ // the specified zero padded to the correct size
+ for (uint32_t b = 0; b < def->bytes_; b++) {
+ snprintf(tmp, sizeof(tmp), "%02x", pval[b]);
+ str += tmp;
+ }
+
+ thread->SetRegister(a, &val, def->bytes_);
+ }
+ ses->AddTransaction("g", str.data());
+
+ char tmp[8];
+ snprintf(tmp, sizeof(tmp), "S%02x", sig);
+ ses->AddTransaction("?", tmp);
+}
+
+// GetGoldenSessionMock
+//
+// The function bellow generates a mock session and preloads it
+// with expected inputs and outputs for a common connection case.
+// For GDB, that's:
+// 1) qSupported - Querry for supported features
+// 2) qXfer:features:read:target.xml:0,7cf - Query for target arch.
+// 3) qfThreadInfo/qsThreadInfo - Query for active threads
+// 4) Hg1234 - Set current register context to retreived thread id
+// 5) g - Get current thread registers
+// 6) disconnect
+SessionMock *GetGoldenSessionMock(bool target, bool debug) {
+ SessionMock *ses = new SessionMock(target);
+
+ if (debug) ses->SetFlags(Session::DEBUG_RECV | Session::DEBUG_SEND);
+
+ AddMockInit(ses);
+ AddMockUpdate(ses, 5);
+
+ ses->AddAction(SessionMock::DISCONNECT, "");
+ return ses;
+}
+
+} // namespace gdb_rsp
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/session_mock.h b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/session_mock.h
new file mode 100644
index 0000000..f6c7b28
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/session_mock.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+#ifndef NATIVE_CLIENT_GDB_RSP_SESSION_MOCK_H_
+#define NATIVE_CLIENT_GDB_RSP_SESSION_MOCK_H_ 1
+
+#include <string>
+#include <vector>
+
+#include "native_client/src/debug_server/gdb_rsp/packet.h"
+#include "native_client/src/debug_server/gdb_rsp/session.h"
+#include "native_client/src/debug_server/port/platform.h"
+#include "native_client/src/debug_server/port/transport.h"
+
+namespace gdb_rsp {
+
+class SessionMock : public Session {
+ public:
+ enum ActionType {
+ SEND,
+ RECV,
+ TIMEOUT,
+ DISCONNECT
+ };
+
+ struct Action {
+ std::string str_;
+ ActionType type_;
+ };
+
+ class TransportMock : public port::ITransport {
+ public:
+ virtual int32_t Read(void *ptr, int32_t len) {
+ (void) ptr;
+ (void) len;
+ return -1;
+ }
+ virtual int32_t Write(const void *ptr, int32_t len) {
+ (void) ptr;
+ (void) len;
+ return -1;
+ }
+ virtual bool ReadWaitWithTimeout(uint32_t ms) {
+ (void) ms;
+ return false;
+ }
+ virtual void Disconnect() { }
+ };
+
+ explicit SessionMock(bool target);
+ ~SessionMock();
+
+ bool SendPacketOnly(gdb_rsp::Packet *packet);
+ bool GetPacket(gdb_rsp::Packet *packet);
+ bool DataAvailable();
+ ActionType PeekAction();
+ Action *GetAction();
+
+ void AddAction(ActionType act, const char *str);
+ void AddTransaction(const char *snd, const char *rcv);
+
+ private:
+ std::vector<Action*> actions_;
+ size_t cur_;
+ bool target_;
+};
+
+SessionMock *GetGoldenSessionMock(bool target, bool debug);
+void AddMockInit(SessionMock *ses);
+void AddMockUpdate(SessionMock *ses, uint8_t sig);
+
+
+
+} // namespace gdb_rsp
+
+#endif // NATIVE_CLIENT_GDB_RSP_SESSION_MOCK_H_
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/session_test.cc b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/session_test.cc
new file mode 100644
index 0000000..da35f27
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/session_test.cc
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+#include <sstream>
+
+#include "native_client/src/debug_server/gdb_rsp/session.h"
+#include "native_client/src/debug_server/gdb_rsp/test.h"
+#include "native_client/src/debug_server/port/platform.h"
+
+using gdb_rsp::Session;
+using gdb_rsp::Packet;
+
+// Transport simulation class, this stores data and a r/w index
+// to simulate one direction of a pipe, or a pipe to self.
+class SharedVector {
+ public:
+ SharedVector() : rd(0), wr(0) {}
+
+ public:
+ std::vector<char> data;
+ volatile uint32_t rd;
+ volatile uint32_t wr;
+};
+
+// Simulates a transport (such as a socket), the reports "ready"
+// when polled, but fails on TX/RX.
+class DCSocketTransport : public port::ITransport {
+ public:
+ virtual int32_t Read(void *ptr, int32_t len) {
+ (void) ptr;
+ (void) len;
+ return -1;
+ }
+
+ virtual int32_t Write(const void *ptr, int32_t len) {
+ (void) ptr;
+ (void) len;
+ return -1;
+ }
+
+ virtual bool ReadWaitWithTimeout(uint32_t ms) {
+ (void) ms;
+ return true;
+ }
+
+ virtual void Disconnect() {}
+ virtual bool DataAvail() { return true; }
+};
+
+
+// Simulate a transport transmitting data Q'd in TX and verifying that
+// inbound data matches expected "golden" string.
+class GoldenTransport : public port::ITransport {
+ public:
+ GoldenTransport(const char *rx, const char *tx, int cnt) {
+ rx_ = rx;
+ tx_ = tx;
+ cnt_ = cnt;
+ txCnt_ = 0;
+ rxCnt_ = 0;
+ errs_ = 0;
+ disconnected_ = false;
+ }
+
+ virtual int32_t Read(void *ptr, int32_t len) {
+ if (disconnected_) return -1;
+ memcpy(ptr, &rx_[rxCnt_], len);
+ rxCnt_ += len;
+ if (static_cast<int>(strlen(rx_)) < rxCnt_) {
+ printf("End of RX\n");
+ errs_++;
+ }
+ return len;
+ }
+
+ // Read from this link, return a negative value if there is an error
+ virtual int32_t Write(const void *ptr, int32_t len) {
+ const char *str = reinterpret_cast<const char *>(ptr);
+ if (disconnected_) return -1;
+ if (strncmp(str, &tx_[txCnt_], len) != 0) {
+ printf("TX mismatch in %s vs %s.\n", str, &tx_[txCnt_]);
+ errs_++;
+ }
+ txCnt_ += len;
+ return len;
+ }
+
+ virtual bool ReadWaitWithTimeout(uint32_t ms) {
+ if (disconnected_) return true;
+
+ for (int loop = 0; loop < 8; loop++) {
+ if (DataAvail()) return true;
+ port::IPlatform::Relinquish(ms >> 3);
+ }
+ return false;
+ }
+
+ virtual void Disconnect() {
+ disconnected_ = true;
+ }
+
+ virtual bool DataAvail() {
+ return rxCnt_ < static_cast<int>(strlen(rx_));
+ }
+
+ int errs() { return errs_; }
+
+
+ protected:
+ const char *rx_;
+ const char *tx_;
+ int cnt_;
+ int rxCnt_;
+ int txCnt_;
+ int errs_;
+ bool disconnected_;
+};
+
+
+class TestTransport : public port::ITransport {
+ public:
+ TestTransport(SharedVector *rvec, SharedVector *wvec) {
+ rvector_ = rvec;
+ wvector_ = wvec;
+ disconnected_ = false;
+ }
+
+ virtual int32_t Read(void *ptr, int32_t len) {
+ if (disconnected_) return -1;
+ DataAvail();
+
+ int max = rvector_->wr - rvector_->rd;
+ if (max > len)
+ max = len;
+
+ if (max > 0) {
+ char *src = &rvector_->data[rvector_->rd];
+ memcpy(ptr, src, max);
+ }
+ rvector_->rd += max;
+ return max;
+ }
+
+ virtual int32_t Write(const void *ptr, int32_t len) {
+ if (disconnected_) return -1;
+
+ wvector_->data.resize(wvector_->wr + len);
+ memcpy(&wvector_->data[wvector_->wr], ptr, len);
+ wvector_->wr += len;
+ return len;
+ }
+
+ virtual bool ReadWaitWithTimeout(uint32_t ms) {
+ if (disconnected_) return true;
+
+ for (int loop = 0; loop < 8; loop++) {
+ if (DataAvail()) return true;
+ port::IPlatform::Relinquish(ms >> 3);
+ }
+ return false;
+ }
+
+ virtual void Disconnect() {
+ disconnected_ = true;
+ }
+
+ // Return true if vec->data is availible (
+ virtual bool DataAvail() {
+ return (rvector_->rd < rvector_->wr);
+ }
+
+ protected:
+ SharedVector *rvector_;
+ SharedVector *wvector_;
+ bool disconnected_;
+};
+
+
+int TestSession() {
+ int errs = 0;
+ Packet pktOut;
+ Packet pktIn;
+ SharedVector vec;
+
+ // Create a "loopback" session by using the same
+ // FIFO for ingress and egress.
+ Session cli;
+ Session srv;
+
+ if (cli.Init(NULL)) {
+ printf("Initializing with NULL did not fail.\n");
+ errs++;
+ }
+
+ cli.Init(new TestTransport(&vec, &vec));
+ srv.Init(new TestTransport(&vec, &vec));
+
+ // Check, Set,Clear,Get flags.
+ cli.ClearFlags(static_cast<uint32_t>(-1));
+ cli.SetFlags(Session::IGNORE_ACK | Session::DEBUG_RECV);
+ if (cli.GetFlags() != (Session::IGNORE_ACK + Session::DEBUG_RECV)) {
+ printf("SetFlag failed.\n");
+ errs++;
+ }
+ cli.ClearFlags(Session::IGNORE_ACK | Session::DEBUG_SEND);
+ if (cli.GetFlags() != Session::DEBUG_RECV) {
+ printf("ClearFlag failed.\n");
+ errs++;
+ }
+
+ // Check Send Packet of known value.
+ const char *str = "1234";
+
+ pktOut.AddString(str);
+ cli.SendPacketOnly(&pktOut);
+ srv.GetPacket(&pktIn);
+ std::string out;
+ pktIn.GetString(&out);
+ if (out != str) {
+ printf("Send Only failed.\n");
+ errs++;
+ }
+
+ // Check send against golden transactions
+ const char tx[] = { "$1234#ca+" };
+ const char rx[] = { "+$OK#9a" };
+ GoldenTransport gold(rx, tx, 2);
+ Session uni;
+ uni.Init(&gold);
+
+ pktOut.Clear();
+ pktOut.AddString(str);
+ if (!uni.SendPacket(&pktOut)) {
+ printf("Send failed.\n");
+ errs++;
+ }
+ if (!uni.GetPacket(&pktIn)) {
+ printf("Get failed.\n");
+ errs++;
+ }
+ pktIn.GetString(&out);
+ if (out != "OK") {
+ printf("Send/Get failed.\n");
+ errs++;
+ }
+
+ // Check that a closed Transport reports to session
+ if (!uni.Connected()) {
+ printf("Expecting uni to be connected.\n");
+ errs++;
+ }
+ gold.Disconnect();
+ uni.GetPacket(&pktIn);
+ if (uni.Connected()) {
+ printf("Expecting uni to be disconnected.\n");
+ errs++;
+ }
+
+ // Check that a failed read/write reports DC
+ DCSocketTransport dctrans;
+ Session dctest;
+ dctest.Init(&dctrans);
+ if (!dctest.Connected()) {
+ printf("Expecting dctest to be connected.\n");
+ errs++;
+ }
+ dctest.GetPacket(&pktIn);
+ if (dctest.Connected()) {
+ printf("Expecting dctest to be disconnected.\n");
+ errs++;
+ }
+
+ errs += gold.errs();
+ return errs;
+}
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/target.cc b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/target.cc
new file mode 100644
index 0000000..fe36e95
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/target.cc
@@ -0,0 +1,636 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "native_client/src/debug_server/gdb_rsp/abi.h"
+#include "native_client/src/debug_server/gdb_rsp/packet.h"
+#include "native_client/src/debug_server/gdb_rsp/target.h"
+#include "native_client/src/debug_server/gdb_rsp/session.h"
+#include "native_client/src/debug_server/gdb_rsp/util.h"
+
+#include "native_client/src/debug_server/port/platform.h"
+#include "native_client/src/debug_server/port/thread.h"
+
+#ifdef WIN32
+#define snprintf sprintf_s
+#endif
+
+using std::string;
+
+using port::IEvent;
+using port::IMutex;
+using port::IPlatform;
+using port::IThread;
+using port::MutexLock;
+
+namespace gdb_rsp {
+
+
+Target::Target(const Abi* abi)
+ : abi_(abi),
+ mutex_(NULL),
+ sig_start_(NULL),
+ sig_done_(NULL),
+ send_done_(false),
+ ctx_(NULL),
+ cur_signal_(-1),
+ sig_thread_(0),
+ run_thread_(-1),
+ reg_thread_(-1) {
+ if (NULL == abi_) abi_ = Abi::Get();
+}
+
+Target::~Target() {
+ Destroy();
+}
+
+bool Target::Init() {
+ string targ_xml = "l<target><architecture>";
+
+ targ_xml += abi_->GetName();
+ targ_xml += "</architecture></target>";
+
+ // Set a more specific result which won't change.
+ properties_["target.xml"] = targ_xml;
+ properties_["Supported"] =
+ "PacketSize=7cf;qXfer:libraries:read+;qXfer:features:read+";
+
+ mutex_ = IMutex::Allocate();
+ sig_start_ = IEvent::Allocate();
+ sig_done_ = IEvent::Allocate();
+ ctx_ = new uint8_t[abi_->GetContextSize()];
+
+ if ((NULL == mutex_) || (NULL == sig_start_) || (NULL == sig_done_)
+ || (NULL == ctx_)) {
+ Destroy();
+ return false;
+ }
+
+ // Allow one exception to happen
+ sig_start_->Signal();
+ return true;
+}
+
+void Target::Destroy() {
+ if (mutex_) IMutex::Free(mutex_);
+ if (sig_start_) IEvent::Free(sig_start_);
+ if (sig_done_) IEvent::Free(sig_done_);
+
+ delete[] ctx_;
+}
+
+bool Target::AddTemporaryBreakpoint(uint64_t address) {
+ const Abi::BPDef *bp = abi_->GetBreakpointDef();
+
+ // If this ABI does not support breakpoints then fail
+ if (NULL == bp) return false;
+
+ // If we alreay have a breakpoint here then don't add it
+ BreakMap_t::iterator itr = breakMap_.find(address);
+ if (itr != breakMap_.end()) return false;
+
+ uint8_t *data = new uint8_t[bp->size_];
+ if (NULL == data) return false;
+
+ // Copy the old code from here
+ if (IPlatform::GetMemory(address, bp->size_, data) == false) {
+ delete[] data;
+ return false;
+ }
+ if (IPlatform::SetMemory(address, bp->size_, bp->code_) == false) {
+ delete[] data;
+ return false;
+ }
+
+ breakMap_[address] = data;
+ return true;
+}
+
+bool Target::RemoveTemporaryBreakpoints() {
+ const Abi::BPDef *bp = abi_->GetBreakpointDef();
+
+ // Iterate through the map, removing breakpoints
+ while (!breakMap_.empty()) {
+ // Copy the key/value locally
+ BreakMap_t::iterator cur = breakMap_.begin();
+ uint64_t addr = cur->first;
+ uint8_t *data = cur->second;
+
+ // Then remove it from the map
+ breakMap_.erase(cur);
+
+ // Copy back the old code, and free the data
+ IPlatform::SetMemory(addr, bp->size_, data);
+ delete[] data;
+ }
+
+ return true;
+}
+
+
+
+void Target::Signal(uint32_t id, int8_t sig, bool wait) {
+ // Wait for this signal's turn in the signal Q.
+ sig_start_->Wait();
+ {
+ // Now lock the target, sleeping all active threads
+ MutexLock lock(mutex_);
+
+ // Suspend all threads except this one
+ uint32_t curId;
+ bool more = GetFirstThreadId(&curId);
+ while (more) {
+ if (curId != id) {
+ IThread *thread = threads_[curId];
+ thread->Suspend();
+ }
+ more = GetNextThreadId(&curId);
+ }
+
+ // Signal the stub (Run thread) that we are ready to process
+ // a trap, by updating the signal information and releasing
+ // the lock.
+ reg_thread_ = id;
+ run_thread_ = id;
+ cur_signal_ = sig;
+ }
+
+ // Wait for permission to continue
+ if (wait) sig_done_->Wait();
+}
+
+void Target::Run(Session *ses) {
+ bool first = true;
+ do {
+ // Give everyone else a chance to use the lock
+ IPlatform::Relinquish(100);
+
+ // Lock to prevent anyone else from modifying threads
+ // or updating the signal information.
+ MutexLock lock(mutex_);
+ Packet recv, reply;
+
+ uint32_t id = 0;
+
+ // If no signal is waiting for this iteration...
+ if (-1 == cur_signal_) {
+ // but the debugger is talking to us then force a break
+ if (ses->DataAvailable()) {
+ // set signal to 0 to signify paused
+ cur_signal_ = 0;
+
+ // put all the threads to sleep.
+ uint32_t curId;
+ bool more = GetFirstThreadId(&curId);
+ while (more) {
+ if (curId != id) {
+ IThread *thread = threads_[curId];
+ thread->Suspend();
+ }
+ more = GetNextThreadId(&curId);
+ }
+ } else {
+ // otherwise, nothing to do so try again.
+ continue;
+ }
+ } else {
+ // otherwise there really is an exception so get the id of the thread
+ id = GetRegThreadId();
+ }
+
+ // If we got this far, then there is some kind of signal.
+ // So first, remove the breakpoints
+ RemoveTemporaryBreakpoints();
+
+ // Next update the current thread info
+ char tmp[16];
+ snprintf(tmp, sizeof(tmp), "QC%x", id);
+ properties_["C"] = tmp;
+
+ if (first) {
+ // First time on a connection, we don't sent the signal
+ first = false;
+ } else {
+ // All other times, send the signal that triggered us
+ Packet pktOut;
+ pktOut.AddRawChar('S');
+ pktOut.AddWord8(cur_signal_);
+ ses->SendPacketOnly(&pktOut);
+ }
+
+ // Now we are ready to process commands
+ // Loop through packets until we process a continue
+ // packet.
+ do {
+ if (ses->GetPacket(&recv)) {
+ reply.Clear();
+ if (ProcessPacket(&recv, &reply)) {
+ // If this is a continue command, break out of this loop
+ break;
+ } else {
+ // Othwerise send the reponse
+ ses->SendPacket(&reply);
+ }
+ }
+ } while (ses->Connected());
+
+
+ // Now that we are done, we want to continue in the "correct order".
+ // This means letting the active thread go first, in case we are single
+ // stepping and want to catch it again. This is a desired behavior but
+ // it is not guaranteed since another thread may already be in an
+ // exception state and next in line to notify the target.
+
+ // If the run thread is not the exception thread, wake it up now.
+ uint32_t run_thread = GetRunThreadId();
+ if (run_thread != id
+ && run_thread != static_cast<uint32_t>(-1)) {
+ IThread* thread = threads_[run_thread];
+ thread->Resume();
+ }
+
+ // Next, wake up the exception thread, if there is one and it needs
+ // to wake up.
+ if (id && send_done_) sig_done_->Signal();
+
+ // Now wake up everyone else
+ uint32_t curId;
+ bool more = GetFirstThreadId(&curId);
+ while (more) {
+ if ((curId != id) && (curId != GetRunThreadId())) {
+ IThread *thread = threads_[curId];
+ thread->Resume();
+ }
+ more = GetNextThreadId(&curId);
+ }
+
+ // Reset the signal value
+ cur_signal_ = -1;
+
+ // If we took an exception, let the handler resume and allow
+ // the next exception to come in.
+ if (cur_signal_) {
+ sig_done_->Signal();
+ sig_start_->Signal();
+ }
+
+ // Continue running until the connection is lost.
+ } while (ses->Connected());
+}
+
+
+
+
+bool Target::GetFirstThreadId(uint32_t *id) {
+ threadItr_ = threads_.begin();
+ return GetNextThreadId(id);
+}
+
+bool Target::GetNextThreadId(uint32_t *id) {
+ if (threadItr_ == threads_.end()) return false;
+
+ *id = (*threadItr_).first;
+ threadItr_++;
+
+ return true;
+}
+
+
+
+bool Target::ProcessPacket(Packet* pktIn, Packet* pktOut) {
+ char cmd;
+ int32_t seq = -1;
+ ErrDef err = NONE;
+
+ // Clear the outbound message
+ pktOut->Clear();
+
+ // Pull out the sequence.
+ pktIn->GetSequence(&seq);
+ if (seq != -1) pktOut->SetSequence(seq);
+
+ // Find the command
+ pktIn->GetRawChar(&cmd);
+
+ switch (cmd) {
+ // IN : $?
+ // OUT: $Sxx
+ case '?':
+ pktOut->AddRawChar('S');
+ pktOut->AddWord8(cur_signal_);
+ break;
+
+ // IN : $d
+ // OUT: -NONE-
+ case 'd':
+ Detach();
+ break;
+
+ // IN : $g
+ // OUT: $xx...xx
+ case 'g': {
+ uint32_t id = GetRegThreadId();
+ if (0 == id) {
+ err = BAD_ARGS;
+ break;
+ }
+
+ IThread *thread = GetThread(id);
+ if (NULL == thread) {
+ err = BAD_ARGS;
+ break;
+ }
+
+ // Copy OS preserved registers to GDB payload
+ for (uint32_t a = 0; a < abi_->GetRegisterCount(); a++) {
+ const Abi::RegDef *def = abi_->GetRegisterDef(a);
+ thread->GetRegister(a, &ctx_[def->offset_], def->bytes_);
+ }
+
+ pktOut->AddBlock(ctx_, abi_->GetContextSize());
+ break;
+ }
+
+ // IN : $Gxx..xx
+ // OUT: $OK
+ case 'G': {
+ uint32_t id = GetRegThreadId();
+ if (0 == id) {
+ err = BAD_ARGS;
+ break;
+ }
+
+ IThread *thread = threads_[id];
+ if (NULL == thread) {
+ err = BAD_ARGS;
+ break;
+ }
+
+ // GDB payload to OS registers
+ for (uint32_t a = 0; a < abi_->GetRegisterCount(); a++) {
+ const Abi::RegDef *def = abi_->GetRegisterDef(a);
+ thread->SetRegister(a, &ctx_[def->offset_], def->bytes_);
+ }
+ pktOut->AddBlock(ctx_, abi_->GetContextSize());
+ break;
+ }
+
+ // IN : $H(c/g)(-1,0,xxxx)
+ // OUT: $OK
+ case 'H': {
+ char type;
+ uint64_t id;
+
+ if (!pktIn->GetRawChar(&type)) {
+ err = BAD_FORMAT;
+ break;
+ }
+ if (!pktIn->GetNumberSep(&id, 0)) {
+ err = BAD_FORMAT;
+ break;
+ }
+
+ if (threads_.begin() == threads_.end()) {
+ err = BAD_ARGS;
+ break;
+ }
+
+ // If we are using "any" get the first thread
+ if (id == static_cast<uint64_t>(-1)) id = threads_.begin()->first;
+
+ // Verify that we have the thread
+ if (threads_.find(static_cast<uint32_t>(id)) == threads_.end()) {
+ err = BAD_ARGS;
+ break;
+ }
+
+ pktOut->AddString("OK");
+ switch (type) {
+ case 'g':
+ reg_thread_ = static_cast<uint32_t>(id);
+ break;
+
+ case 'c':
+ run_thread_ = static_cast<uint32_t>(id);
+ break;
+
+ default:
+ err = BAD_ARGS;
+ break;
+ }
+ break;
+ }
+
+ // IN : $maaaa,llll
+ // OUT: $xx..xx
+ case 'm': {
+ uint64_t addr;
+ uint64_t wlen;
+ uint32_t len;
+ if (!pktIn->GetNumberSep(&addr, 0)) {
+ err = BAD_FORMAT;
+ break;
+ }
+
+ if (!pktIn->GetNumberSep(&wlen, 0)) {
+ err = BAD_FORMAT;
+ break;
+ }
+
+ len = static_cast<uint32_t>(wlen);
+ uint8_t *block = new uint8_t[len];
+ if (!port::IPlatform::GetMemory(addr, len, block)) err = FAILED;
+
+ pktOut->AddBlock(block, len);
+ break;
+ }
+
+ // IN : $Maaaa,llll:xx..xx
+ // OUT: $OK
+ case 'M': {
+ uint64_t addr;
+ uint64_t wlen;
+ uint32_t len;
+
+ if (!pktIn->GetNumberSep(&addr, 0)) {
+ err = BAD_FORMAT;
+ break;
+ }
+ if (!pktIn->GetNumberSep(&wlen, 0)) {
+ err = BAD_FORMAT;
+ break;
+ }
+
+ len = static_cast<uint32_t>(wlen);
+ uint8_t *block = new uint8_t[len];
+ pktIn->GetBlock(block, len);
+
+ if (!port::IPlatform::SetMemory(addr, len, block)) err = FAILED;
+
+ pktOut->AddString("OK");
+ break;
+ }
+
+ case 'q': {
+ string tmp;
+ const char *str = &pktIn->GetPayload()[1];
+ stringvec toks = StringSplit(str, ":;");
+ PropertyMap_t::const_iterator itr = properties_.find(toks[0]);
+
+ // If this is a thread query
+ if (!strcmp(str, "fThreadInfo") || !strcmp(str, "sThreadInfo")) {
+ uint32_t curr;
+ bool more = false;
+ if (str[0] == 'f') {
+ more = GetFirstThreadId(&curr);
+ } else {
+ more = GetNextThreadId(&curr);
+ }
+
+ if (!more) {
+ pktOut->AddString("l");
+ } else {
+ pktOut->AddString("m");
+ pktOut->AddNumberSep(curr, 0);
+ }
+ break;
+ }
+
+ // Check for architecture query
+ tmp = "Xfer:features:read:target.xml";
+ if (!strncmp(str, tmp.data(), tmp.length())) {
+ stringvec args = StringSplit(&str[tmp.length()+1], ",");
+ if (args.size() != 2) break;
+
+ const char *out = properties_["target.xml"].data();
+ int offs = strtol(args[0].data(), NULL, 16);
+ int max = strtol(args[1].data(), NULL, 16) + offs;
+ int len = static_cast<int>(strlen(out));
+
+ if (max >= len) max = len;
+
+ while (offs < max) {
+ pktOut->AddRawChar(out[offs]);
+ offs++;
+ }
+ break;
+ }
+
+ // Check the property cache
+ if (itr != properties_.end()) {
+ pktOut->AddString(itr->second.data());
+ }
+ break;
+ }
+
+ case 'T': {
+ uint64_t id;
+ if (!pktIn->GetNumberSep(&id, 0)) {
+ err = BAD_FORMAT;
+ break;
+ }
+
+ if (GetThread(static_cast<uint32_t>(id)) == NULL) {
+ err = BAD_ARGS;
+ break;
+ }
+
+ pktOut->AddString("OK");
+ break;
+ }
+
+ case 's': {
+ IThread *thread = GetThread(GetRunThreadId());
+ if (thread) thread->SetStep(true);
+ return true;
+ }
+
+ case 'c':
+ return true;
+
+ default: {
+ // If the command is not recognzied, ignore it by sending an
+ // empty reply.
+ string str;
+ pktIn->GetString(&str);
+ port::IPlatform::LogError("Unknown command: %s", str.data());
+ return false;
+ }
+ }
+
+ // If there is an error, return the error code instead of a payload
+ if (err) {
+ pktOut->Clear();
+ pktOut->AddRawChar('E');
+ pktOut->AddWord8(err);
+ }
+ return false;
+}
+
+
+void Target::TrackThread(IThread* thread) {
+ uint32_t id = thread->GetId();
+ mutex_->Lock();
+ threads_[id] = thread;
+ mutex_->Unlock();
+}
+
+void Target::IgnoreThread(IThread* thread) {
+ uint32_t id = thread->GetId();
+ mutex_->Lock();
+ ThreadMap_t::iterator itr = threads_.find(id);
+
+ if (itr != threads_.end()) threads_.erase(itr);
+ mutex_->Lock();
+}
+
+
+void Target::Detach() {
+ port::IPlatform::LogInfo("Requested Detach.\n");
+}
+
+
+uint32_t Target::GetRegThreadId() const {
+ ThreadMap_t::const_iterator itr;
+
+ switch (reg_thread_) {
+ // If we wany "any" then try the signal'd thread first
+ case 0:
+ case 0xFFFFFFFF:
+ itr = threads_.begin();
+ break;
+
+ default:
+ itr = threads_.find(reg_thread_);
+ break;
+ }
+
+ if (itr == threads_.end()) return 0;
+
+ return itr->first;
+}
+
+uint32_t Target::GetRunThreadId() const {
+ return run_thread_;
+}
+
+IThread* Target::GetThread(uint32_t id) {
+ ThreadMap_t::const_iterator itr;
+ itr = threads_.find(id);
+ if (itr != threads_.end()) return itr->second;
+
+ return NULL;
+}
+
+
+} // namespace gdb_rsp
+
+
+
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/target.h b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/target.h
new file mode 100644
index 0000000..24f93f0
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/target.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+
+// This module provides interfaces for accessing the debugging state of
+// the target. The target can use either the thread that took the
+// exception or run in it's own thread. To respond to the host, the
+// application must call the run function with a valid Transport
+// which will then be polled for commands. The target will return
+// from Run when the host disconnects, or requests a continue.
+//
+// The object is protected by a mutex, so that it is legal to track or
+// ignore threads as an exception takes place.
+//
+// The Run function expects that all threads of interest are stopped
+// with the Step flag cleared before Run is called. It is expected that
+// and that all threads are updated with thier modified contexts and
+// restarted when the target returns from Run.
+
+#ifndef NATIVE_CLIENT_GDB_RSP_TARGET_H_
+#define NATIVE_CLIENT_GDB_RSP_TARGET_H_ 1
+
+#include <map>
+#include <string>
+
+#include "native_client/src/debug_server/gdb_rsp/util.h"
+
+#include "native_client/src/debug_server/port/event.h"
+#include "native_client/src/debug_server/port/platform.h"
+#include "native_client/src/debug_server/port/mutex.h"
+#include "native_client/src/debug_server/port/thread.h"
+
+namespace gdb_rsp {
+
+class Abi;
+class Packet;
+class Session;
+
+class Target {
+ public:
+ enum ErrDef {
+ NONE = 0,
+ BAD_FORMAT = 1,
+ BAD_ARGS = 2,
+ FAILED = 3
+ };
+
+ enum State {
+ UNINIT = 0,
+ RUNNING = 1,
+ STOPPED = 2
+ };
+
+ typedef ErrDef (*QFunc_t)(Target *, stringvec&, std::string);
+ typedef std::map<uint32_t, port::IThread*> ThreadMap_t;
+ typedef std::map<std::string, std::string> PropertyMap_t;
+ typedef std::map<uint64_t, uint8_t*> BreakMap_t;
+
+ public:
+ // Contruct a Target object. By default use the native ABI.
+ explicit Target(const Abi *abi = NULL);
+ ~Target();
+
+ // Init must be the first function called to correctlty
+ // build the Target internal structures.
+ bool Init();
+
+ // Add and remove temporary breakpoints. These breakpoints
+ // must be added just before we start running, and removed
+ // just before we stop running to prevent the debugger from
+ // seeing the modified memory.
+ bool AddTemporaryBreakpoint(uint64_t address);
+ bool RemoveTemporaryBreakpoints();
+
+ // This function should be called by a tracked thread when it takes
+ // an exception. It takes sig_start_ to prevent other exceptions
+ // from signalling thread. If wait is true, it will then block on
+ // sig_done_ until a continue is issued by the host.
+ void Signal(uint32_t id, int8_t sig, bool wait);
+
+ // This function will spin on a session, until it closes. If an
+ // exception is caught, it will signal the exception thread by
+ // setting sig_done_.
+ void Run(Session *ses);
+
+ // This function causes the target to track the state
+ // of the specified thread and make it availible to
+ // a connected host.
+ void TrackThread(port::IThread *thread);
+
+ // This function causes the target to stop tracking the
+ // state of the specified thread, which will no longer
+ // be visible to the host.
+ void IgnoreThread(port::IThread *thread);
+
+ protected:
+ // This function always succeedes, since all errors
+ // are reported as an error string of "E<##>" where
+ // the two digit number. The error codes are not
+ // not documented, so this implementation uses
+ // ErrDef as errors codes. This function returns
+ // true a request to continue (or step) is processed.
+ bool ProcessPacket(Packet *pktIn, Packet *pktOut);
+
+ void Destroy();
+ void Detach();
+
+ bool GetFirstThreadId(uint32_t *id);
+ bool GetNextThreadId(uint32_t *id);
+
+ uint32_t GetRegThreadId() const;
+ uint32_t GetRunThreadId() const;
+ port::IThread *GetThread(uint32_t id);
+
+ public:
+ const Abi *abi_;
+
+ // This mutex protects debugging state (threads_, cur_signal, sig_thread_)
+ port::IMutex *mutex_;
+
+ // This event is signalled when the target is really to process an
+ // exception. It ensures only one exception is processed at a time.
+ port::IEvent *sig_start_;
+
+ // This event is signalled when the target done processing an exception.
+ port::IEvent *sig_done_;
+
+ // This value is set if the exception cather is requesting a continue signal
+ bool send_done_;
+
+ ThreadMap_t threads_;
+ ThreadMap_t::const_iterator threadItr_;
+ BreakMap_t breakMap_;
+
+
+ PropertyMap_t properties_;
+
+ uint8_t *ctx_; // Context Scratchpad
+
+ // The current signal and signaling thread data is protected by
+ // the mutex, and can only be owned by one thread at a time.
+ // These values should only be written via the "Signal" member,
+ // which will ensure that a new signal does not overwrite a
+ // previous signal.
+ volatile int8_t cur_signal_;
+ volatile uint32_t sig_thread_;
+
+ uint32_t run_thread_; // Which thread to issue step commands on
+ uint32_t reg_thread_; // Which thread to issue context (reg) commands on
+};
+
+
+} // namespace gdb_rsp
+
+#endif // NATIVE_CLIENT_GDB_RSP_TARGET_H_
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/target_test.cc b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/target_test.cc
new file mode 100644
index 0000000..75a63bc
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/target_test.cc
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+#include <sstream>
+#include <vector>
+
+#include "native_client/src/debug_server/gdb_rsp/abi.h"
+#include "native_client/src/debug_server/gdb_rsp/session_mock.h"
+#include "native_client/src/debug_server/gdb_rsp/target.h"
+#include "native_client/src/debug_server/gdb_rsp/test.h"
+
+using gdb_rsp::Abi;
+using gdb_rsp::SessionMock;
+using gdb_rsp::Target;
+
+int TestTarget() {
+ int errs = 0;
+
+ SessionMock *ses = gdb_rsp::GetGoldenSessionMock(true, false);
+ const gdb_rsp::Abi* abi = gdb_rsp::Abi::Get();
+
+ Target *target = new Target(NULL);
+ if (!target->Init()) {
+ printf("Failed to INIT target.\n");
+ return 1;
+ }
+
+ // Create a pseudo thread with registers set to their index
+ port::IThread *thread = port::IThread::Acquire(0x1234, true);
+ for (uint32_t a = 0; a < abi->GetRegisterCount(); a++) {
+ const Abi::RegDef* def = abi->GetRegisterDef(a);
+ uint64_t val = static_cast<uint64_t>(a);
+ thread->SetRegister(a, &val, def->bytes_);
+ }
+ target->TrackThread(thread);
+
+ // Pretend we just got a signal on that thread
+ target->Signal(thread->GetId(), 5, false);
+
+ // Run the session to completion.
+ target->Run(ses);
+
+ if (ses->PeekAction() != SessionMock::DISCONNECT) {
+ printf("We did not consume all the actions.\n");
+ errs++;
+ }
+
+ delete target;
+ delete ses;
+ return errs;
+}
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/test.cc b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/test.cc
new file mode 100644
index 0000000..83a4c7c
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/test.cc
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "native_client/src/debug_server/gdb_rsp/test.h"
+#include "native_client/src/debug_server/port/platform.h"
+
+// Mock portability objects
+namespace port {
+void IPlatform::Relinquish(uint32_t msec) {
+ (void) msec;
+ return;
+}
+
+void IPlatform::LogInfo(const char *fmt, ...) {
+ va_list argptr;
+ va_start(argptr, fmt);
+
+ vprintf(fmt, argptr);
+}
+
+void IPlatform::LogWarning(const char *fmt, ...) {
+ va_list argptr;
+ va_start(argptr, fmt);
+
+ vprintf(fmt, argptr);
+}
+
+void IPlatform::LogError(const char *fmt, ...) {
+ va_list argptr;
+ va_start(argptr, fmt);
+
+ vprintf(fmt, argptr);
+}
+
+// The unit tests are singly threaded, so we just do nothing
+// for synchronization
+class MutexMock : public IMutex {
+ void Lock() {}
+ void Unlock() {}
+ bool Try() { return true; }
+};
+
+IMutex* IMutex::Allocate() { return new MutexMock; }
+void IMutex::Free(IMutex* mtx) { delete static_cast<MutexMock*>(mtx); }
+
+class EventMock : public IEvent {
+ void Signal() {}
+ void Wait() {}
+};
+
+IEvent* IEvent::Allocate() { return new EventMock; }
+void IEvent::Free(IEvent* e) { delete static_cast<EventMock*>(e); }
+
+class ThreadMock : public IThread {
+ public:
+ explicit ThreadMock(uint32_t id) {
+ id_ = id;
+ ctx_ = new uint8_t[gdb_rsp::Abi::Get()->GetContextSize()];
+ }
+ ~ThreadMock() { delete[] ctx_; }
+
+ uint32_t GetId() { return id_; }
+ State GetState() { return IThread::SIGNALED; }
+
+ bool SetStep(bool on) {
+ (void) on;
+ return true;
+ }
+
+ bool GetRegister(uint32_t index, void *dst, uint32_t len) {
+ const gdb_rsp::Abi* abi = gdb_rsp::Abi::Get();
+ const gdb_rsp::Abi::RegDef *reg = abi->GetRegisterDef(index);
+ memcpy(dst, ctx_ + reg->offset_, len);
+ return true;
+ }
+
+ bool SetRegister(uint32_t index, void *src, uint32_t len) {
+ const gdb_rsp::Abi* abi = gdb_rsp::Abi::Get();
+ const gdb_rsp::Abi::RegDef *reg = abi->GetRegisterDef(index);
+ memcpy(ctx_ + reg->offset_, src, len);
+ return true;
+ }
+
+ bool Suspend() { return true; }
+ bool Resume() { return true; }
+
+ virtual void *GetContext() { return ctx_; }
+
+ private:
+ uint8_t *ctx_;
+ uint32_t id_;
+};
+
+IThread* IThread::Acquire(uint32_t id, bool create) {
+ if (create) return new ThreadMock(id);
+ return NULL;
+}
+
+
+bool port::IPlatform::GetMemory(uint64_t addr, uint32_t len, void *dst) {
+ intptr_t iptr = static_cast<intptr_t>(addr);
+ void *src = reinterpret_cast<void*>(iptr);
+ memcpy(dst, src, len);
+ return true;
+}
+
+bool port::IPlatform::SetMemory(uint64_t addr, uint32_t len, void *src) {
+ intptr_t iptr = static_cast<intptr_t>(addr);
+ void *dst = reinterpret_cast<void*>(iptr);
+ memcpy(dst, src, len);
+ return true;
+}
+
+
+} // End of namespace port
+
+int main(int argc, const char *argv[]) {
+ int errs = 0;
+
+ (void) argc;
+ (void) argv;
+
+ printf("Testing Utils.\n");
+ errs += TestUtil();
+
+ printf("Testing ABI.\n");
+ errs += TestAbi();
+
+ printf("Testing Packets.\n");
+ errs += TestPacket();
+
+ printf("Testing Session.\n");
+ errs += TestSession();
+
+ printf("Testing Host.\n");
+ errs += TestHost();
+
+ printf("Testing Target.\n");
+ errs += TestTarget();
+
+ if (errs) printf("FAILED with %d errors.\n", errs);
+ return errs;
+}
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/test.h b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/test.h
new file mode 100644
index 0000000..ae0b32b
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/test.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+#ifndef NATIVE_CLIENT_GDB_RSP_TEST_H_
+#define NATIVE_CLIENT_GDB_RSP_TEST_H_ 1
+
+#include <vector>
+
+#include "native_client/src/debug_server/port/std_types.h"
+#include "native_client/src/debug_server/port/platform.h"
+#include "native_client/src/debug_server/port/transport.h"
+
+#include "native_client/src/debug_server/gdb_rsp/abi.h"
+#include "native_client/src/debug_server/gdb_rsp/host.h"
+#include "native_client/src/debug_server/gdb_rsp/packet.h"
+#include "native_client/src/debug_server/gdb_rsp/session.h"
+#include "native_client/src/debug_server/gdb_rsp/target.h"
+#include "native_client/src/debug_server/gdb_rsp/util.h"
+
+typedef void (*PacketFunc_t)(void *ctx,
+ gdb_rsp::Packet *wr,
+ gdb_rsp::Packet *rd);
+
+int VerifyPacket(gdb_rsp::Packet *wr, gdb_rsp::Packet *rd,
+ void *ctx, PacketFunc_t tx);
+
+int TestAbi();
+int TestHost();
+int TestPacket();
+int TestSession();
+int TestTarget();
+int TestUtil();
+
+#endif // NATIVE_CLIENT_GDB_RSP_TEST_H_
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/util.cc b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/util.cc
new file mode 100644
index 0000000..df752ae
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/util.cc
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include "native_client/src/debug_server/gdb_rsp/util.h"
+#include "native_client/src/debug_server/port/std_types.h"
+
+using std::string;
+
+namespace gdb_rsp {
+
+bool NibbleToInt(char ch, int *val) {
+ // Check for nibble of a-f
+ if ((ch >= 'a') && (ch <= 'f')) {
+ if (val) *val = (ch - 'a' + 10);
+ return true;
+ }
+
+ // Check for nibble of A-F
+ if ((ch >= 'A') && (ch <= 'F')) {
+ if (val) *val = (ch - 'A' + 10);
+ return true;
+ }
+
+ // Check for nibble of 0-9
+ if ((ch >= '0') && (ch <= '9')) {
+ if (val) *val = (ch - '0');
+ return true;
+ }
+
+ // Not a valid nibble representation
+ return false;
+}
+
+bool IntToNibble(int nibble, char *ch) {
+ // Verify this value fits in a nibble
+ if (nibble != (nibble & 0xF)) return false;
+
+ nibble &= 0xF;
+ if (nibble < 10) {
+ if (ch) *ch = '0' + nibble;
+ } else {
+ // Although uppercase maybe more readible GDB
+ // expects lower case values.
+ if (ch) *ch = 'a' + (nibble - 10);
+ }
+
+ return true;
+}
+
+bool NibblesToByte(const char *inStr, int *outInt) {
+ int o1, o2;
+
+ if (NibbleToInt(inStr[0], &o1) && NibbleToInt(inStr[1], &o2)) {
+ *outInt = (o1 << 4) + o2;
+ return true;
+ }
+
+ return false;
+}
+
+#ifdef WIN32
+int snprintf(char *buf, size_t size, const char *fmt, ...) {
+ va_list argptr;
+ va_start(argptr, fmt);
+
+ int len = vsnprintf(buf, size, fmt, argptr);
+ return len;
+}
+#endif
+
+stringvec StringSplit(const string &instr, const char *delim) {
+ int count = 0;
+ const char *in = instr.data();
+ const char *start = in;
+
+ stringvec ovec;
+
+ // Check if we have nothing to do
+ if (NULL == in) return ovec;
+ if (NULL == delim) {
+ ovec.push_back(string(in));
+ return ovec;
+ }
+
+ while (*in) {
+ int len = 0;
+ // Toss all preceeding delimiters
+ while (*in && strchr(delim, *in)) in++;
+
+ // If we still have something to process
+ if (*in) {
+ std::string token;
+ start = in;
+ len = 0;
+ // Keep moving forward for all valid chars
+ while (*in && (strchr(delim, *in) == NULL)) {
+ len++;
+ in++;
+ }
+
+ // Build this token and add it to the array.
+ ovec.resize(count + 1);
+ ovec[count].assign(start, len);
+ count++;
+ }
+ }
+
+ return ovec;
+}
+
+
+
+} // End of namespace gdb_rsp
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/util.h b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/util.h
new file mode 100644
index 0000000..2f18f4e
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/util.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#ifndef NATIVE_CLIENT_GDB_RSP_UTIL_H_
+#define NATIVE_CLIENT_GDB_RSP_UTIL_H_ 1
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "native_client/src/debug_server/port/std_types.h"
+
+namespace gdb_rsp {
+
+typedef std::vector<std::string> stringvec;
+
+// Convert from ASCII (0-9,a-f,A-F) to 4b unsigned or return
+// false if the input char is unexpected.
+bool NibbleToInt(char inChar, int *outInt);
+
+// Convert from 0-15 to ASCII (0-9,a-f) or return false
+// if the input is not a value from 0-15.
+bool IntToNibble(int inInt, char *outChar);
+
+// Convert a pair of nibbles to a value from 0-255 or return
+// false if ethier input character is not a valid nibble.
+bool NibblesToByte(const char *inStr, int *outInt);
+
+#ifdef WIN32
+int snprintf(char *str, size_t size, const char *fmt, ...);
+#endif
+
+stringvec StringSplit(const std::string& instr, const char *delim);
+
+} // namespace gdb_rsp
+
+#endif // NATIVE_CLIENT_GDB_RSP_UTIL_H_
+
diff --git a/native_client_sdk/src/build_tools/debug_server/gdb_rsp/util_test.cc b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/util_test.cc
new file mode 100644
index 0000000..15f3dfe
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/gdb_rsp/util_test.cc
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+
+#include "native_client/src/debug_server/gdb_rsp/util.h"
+
+using std::string;
+using gdb_rsp::NibbleToInt;
+using gdb_rsp::IntToNibble;
+using gdb_rsp::stringvec;
+
+
+int TestUtil() {
+ char goodStr[] = "0123456789abcdefABCDEF";
+ int good;
+ int val;
+ int a;
+ int errCnt = 0;
+
+ // Make sure we find expected and only expected conversions (0-9,a-f,A-F)
+ good = 0;
+ for (a = 0; a < 256; a++) {
+ // NOTE: Exclude 'zero' since the terminator is found by strchr
+ bool found = a && (strrchr(goodStr, a) != NULL);
+ bool xvert = NibbleToInt(static_cast<char>(a), &val);
+ if (xvert != found) {
+ printf("FAILED NibbleToInt of '%c'(#%d), convertion %s.\n",
+ a, a, xvert ? "succeeded" : "failed");
+ errCnt++;
+ }
+ }
+
+ good = 0;
+ for (a = -256; a < 256; a++) {
+ char ch;
+ bool xvert = IntToNibble(a, &ch);
+
+ if (xvert) {
+ good++;
+ if (!NibbleToInt(ch, &val)) {
+ printf("FAILED IntToNibble on good NibbleToInt of #%d\n.\n", a);
+ errCnt++;
+ }
+ // NOTE: check if IntToNible matches NibbleToInt looking at both
+ // possitive and negative values of -15 to +15
+ if (val != a) {
+ printf("FAILED IntToNibble != NibbleToInt of #%d\n.\n", a);
+ errCnt++;
+ }
+ }
+ }
+
+ // Although we check -15 to +15 above, we realy only want -7 to +15
+ // to verify unsiged (0-15) plus signed (-7 to -1).
+ if (good != 16) {
+ printf("FAILED IntToNibble range of 0 to 15.\n");
+ errCnt++;
+ }
+
+ string xml = "qXfer:features:read:target.xml:0,7ca";
+ string str;
+ stringvec vec, vec2;
+
+ return errCnt;
+}
+
diff --git a/native_client_sdk/src/build_tools/debug_server/nacl_debug.cc b/native_client_sdk/src/build_tools/debug_server/nacl_debug.cc
new file mode 100644
index 0000000..6ecd89d
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/nacl_debug.cc
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+
+#include <vector>
+#include <map>
+
+/*
+ * NaCl Functions for intereacting with debuggers
+ */
+
+#include "native_client/src/debug_server/gdb_rsp/session.h"
+#include "native_client/src/debug_server/gdb_rsp/target.h"
+#include "native_client/src/debug_server/port/platform.h"
+#include "native_client/src/debug_server/port/thread.h"
+
+#include "native_client/src/include/nacl_string.h"
+#include "native_client/src/shared/platform/nacl_log.h"
+#include "native_client/src/shared/platform/nacl_threads.h"
+#include "native_client/src/debug_server/debug_stub/debug_stub.h"
+#include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
+#include "native_client/src/debug_server/nacl_debug.h"
+
+#include "native_client/src/trusted/service_runtime/sel_ldr.h"
+
+using port::IPlatform;
+using port::IThread;
+using port::ITransport;
+
+using gdb_rsp::Session;
+using gdb_rsp::Target;
+
+#ifdef WIN32
+/* Disable warning for unwind disabled when exceptions used */
+#pragma warning(disable:4530)
+#endif
+
+/*
+ * These macro wraps all debugging stub calls to prevent C++ code called
+ * by the debugging stub to throw and exception past the C API. We use
+ * this technique to allow the use of STL templates. We catch bad_alloc
+ * seperately purely to provide information for debugging purposes.
+ */
+#define DBG_CATCH_ALL \
+ catch(std::bad_alloc) { \
+ NaClLog(LOG_FATAL, "nacl_debug(%d) : Failed to allocate.\n", __LINE__); \
+ exit(-1); \
+ } \
+ catch(std::exception e) { \
+ NaClLog(LOG_FATAL, "nacl_debug(%d) : Caught exception: %s.\n", \
+ __LINE__ , e.what()); \
+ exit(-1); \
+ } \
+ catch(...) { \
+ NaClLog(LOG_FATAL, "nacl_debug(%d) : Unexpected exception.\n", __LINE__);\
+ exit(-1); \
+ }
+
+
+enum NaClDebugStatus {
+ NDS_DISABLED = 0,
+ NDS_ENABLED = 1,
+ NDS_STOPPED = 2
+};
+
+/*
+ * Remove name mangling to make it easier to find, incase we want to toggle
+ * internal stub debugging from an external debugger.
+ */
+extern "C" {
+ uint32_t nacl_debug_allowed = 0;
+}
+
+struct NaClDebugState {
+ NaClDebugState() : target_(NULL), app_(NULL), break_(false),
+ errCode_(0), status_(NDS_DISABLED) {
+#ifdef _DEBUG
+ /*
+ * When compiling DEBUG we allow an environment variable to enable
+ * debugging, otherwise debugging could be allowed on a release
+ * build by modifying nacl_debug_allowed.
+ */
+ if (NULL != getenv("NACL_DEBUG_ENABLE")) nacl_debug_allowed = 1;
+#endif
+ status_ = nacl_debug_allowed ? NDS_ENABLED : NDS_DISABLED;
+
+ /* Initialize the stub if and only if debugging is enabled. */
+ if (status_) {
+ NaClDebugStubInit();
+ target_ = new Target();
+
+ /* If we failed to initialize the target, turn off debugging */
+ if ((NULL == target_) || (!target_->Init())) status_ = NDS_DISABLED;
+ }
+ }
+
+ ~NaClDebugState() {
+ delete target_;
+ }
+
+ Target* target_;
+ struct NaClApp *app_;
+ bool break_;
+ volatile int errCode_;
+ NaClDebugStatus status_;
+ nacl::string path_;
+ std::vector<const char *> arg_;
+ std::vector<const char *> env_;
+};
+
+/*
+ * NOTE: We use a singleton to delay construction allowing someone
+ * to enable debugging only before the first use of this object.
+ */
+static NaClDebugState *NaClDebugGetState() {
+ static NaClDebugState state;
+ return &state;
+}
+
+void NaClDebugSetAllow(int enable) throw() {
+#ifdef NACL_DEBUG_STUB
+ nacl_debug_allowed = enable;
+#else
+ UNREFERENCED_PARAMETER(enable);
+#endif
+}
+
+void NaClDebugSetStartBroken(int enable) throw() {
+#ifdef NACL_DEBUG_STUB
+ NaClDebugState* state = NaClDebugGetState();
+ state->break_ = enable ? true : false;
+#else
+ UNREFERENCED_PARAMETER(enable);
+#endif
+}
+
+
+
+void WINAPI NaClStubThread(void *ptr) {
+#ifdef NACL_DEBUG_STUB
+ uint32_t id = static_cast<uint32_t>(GetCurrentThreadId());
+ printf("---> NaClStubThread(void *ptr) thread id = %d == 0x%X\n", id, id);
+
+ Target *targ = reinterpret_cast<Target*>(ptr);
+ while (1) {
+ ITransport* trans = NULL;
+ Session* ses = NULL;
+
+ try {
+ // Wait for a connection.
+ printf("---> %d\n", __LINE__); fflush(stdout);
+ trans = ITransport::Accept("0.0.0.0:4014");
+ printf("---> %d\n", __LINE__); fflush(stdout);
+ if (NULL == trans) continue;
+ printf("---> %d\n", __LINE__); fflush(stdout);
+
+ // Create a new session for this connection
+ ses = new Session();
+ ses->Init(trans);
+ ses->SetFlags(Session::DEBUG_MASK);
+
+ // Run this session for as long as it lasts
+ targ->Run(ses);
+ }
+ catch(...) {
+ printf("---> %d\n", __LINE__); fflush(stdout);
+ delete ses;
+ printf("---> %d\n", __LINE__); fflush(stdout);
+ ITransport::Free(trans);
+ printf("---> %d\n", __LINE__); fflush(stdout);
+ }
+ printf("---> %d\n", __LINE__); fflush(stdout);
+ }
+#else
+ UNREFERENCED_PARAMETER(ptr);
+#endif
+}
+
+void NaClExceptionCatcher(uint32_t id, int8_t sig, void *cookie) {
+#ifdef NACL_DEBUG_STUB
+ Target* targ = static_cast<Target*>(cookie);
+
+ /* Signal the target that we caught something */
+ IPlatform::LogWarning("Caught signal %d on thread %Xh.\n", sig, id);
+ targ->Signal(id, sig, true);
+#else
+ UNREFERENCED_PARAMETER(id);
+ UNREFERENCED_PARAMETER(sig);
+ UNREFERENCED_PARAMETER(cookie);
+#endif
+}
+
+
+int NaClDebugIsEnabled(void) throw() {
+#ifdef NACL_DEBUG_STUB
+ try {
+ return (NDS_ENABLED == NaClDebugGetState()->status_) ? 1 : 0;
+ } DBG_CATCH_ALL
+#endif
+
+ return false;
+}
+
+void NaClDebugSetAppPath(const char *path) throw() {
+ try {
+ if (NaClDebugIsEnabled()) NaClDebugGetState()->path_ = path;
+ } DBG_CATCH_ALL
+}
+
+
+void NaClDebugSetAppInfo(struct NaClApp *app) throw() {
+#ifdef NACL_DEBUG_STUB
+ if (NaClDebugIsEnabled()) {
+ NaClDebugState *state = NaClDebugGetState();
+ state->app_ = app;
+ }
+#else
+ UNREFERENCED_PARAMETER(app);
+#endif
+}
+
+
+void NaClDebugSetAppEnvironment(int argc, char const * const argv[],
+ int envc, char const * const envv[]) throw() {
+ if (NaClDebugIsEnabled()) {
+ int a;
+ try {
+ /*
+ * Copy the pointer arrays. We use ptrs instead of strings
+ * since the data persits and it prevents an extra copy.
+ */
+ NaClDebugGetState()->arg_.resize(argc);
+ for (a = 0; a < argc; a++) NaClDebugGetState()->arg_[a] = argv[a];
+ NaClDebugGetState()->env_.resize(envc);
+ for (a = 0; a < envc; a++) NaClDebugGetState()->env_[a] = envv[a];
+ } DBG_CATCH_ALL
+ }
+}
+
+void NaClDebugThreadPrepDebugging(struct NaClAppThread *natp) throw() {
+ UNREFERENCED_PARAMETER(natp);
+
+#ifdef NACL_DEBUG_STUB
+ if (NaClDebugIsEnabled()) {
+ NaClDebugState *state = NaClDebugGetState();
+ uint32_t id = IPlatform::GetCurrentThread();
+ IThread* thread = IThread::Acquire(id, true);
+ state->target_->TrackThread(thread);
+
+ /*
+ * TODO(noelallen) We need to associate the natp with this thread
+ * so we can get to the untrusted context preserved on a syscall.
+ */
+ }
+#endif
+}
+
+void NaClDebugThreadStopDebugging(struct NaClAppThread *natp) throw() {
+ UNREFERENCED_PARAMETER(natp);
+
+#ifdef NACL_DEBUG_STUB
+ if (NaClDebugIsEnabled()) {
+ NaClDebugState *state = NaClDebugGetState();
+ uint32_t id = IPlatform::GetCurrentThread();
+ IThread* thread = IThread::Acquire(id, false);
+ state->target_->IgnoreThread(thread);
+ IThread::Release(thread);
+
+ /*
+ * TODO(noelallen) We need to associate the natp with this thread
+ * so we can get to the thread once we support freeing a thread
+ * from a different thread than the executing one.
+ */
+ }
+#endif
+}
+
+
+int NaClDebugStart(void) throw() {
+#ifdef NACL_DEBUG_STUB
+ if (NaClDebugIsEnabled()) {
+ NaClThread *thread = new NaClThread;
+
+ if (NULL == thread) return false;
+ NaClDebugState *state = NaClDebugGetState();
+
+ /* If break on start has been requested, add a temp breakpoint. */
+ if (state->break_) {
+ struct NaClApp* app = state->app_;
+ state->target_->AddTemporaryBreakpoint(app->entry_pt + app->mem_start);
+ }
+
+ NaClLog(LOG_WARNING, "nacl_debug(%d) : Debugging started.\n", __LINE__);
+ IThread::SetExceptionCatch(NaClExceptionCatcher, state->target_);
+ return NaClThreadCtor(thread, NaClStubThread, state->target_,
+ NACL_KERN_STACK_SIZE);
+ }
+#endif
+ return 0;
+}
+
+void NaClDebugStop(int ErrCode) throw() {
+#ifdef NACL_DEBUG_STUB
+
+ /*
+ * We check if debugging is enabled since this check is the only
+ * mechanism for allocating the state object. We free the
+ * resources but not the object itself. Instead we mark it as
+ * STOPPED to prevent it from getting recreated.
+ */
+ if (NaClDebugIsEnabled()) {
+ NaClDebugGetState()->status_ = NDS_STOPPED;
+ NaClDebugGetState()->errCode_ = ErrCode;
+ }
+#else
+ UNREFERENCED_PARAMETER(ErrCode);
+#endif
+}
+
diff --git a/native_client_sdk/src/build_tools/debug_server/nacl_debug.h b/native_client_sdk/src/build_tools/debug_server/nacl_debug.h
new file mode 100644
index 0000000..37a7b50
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/nacl_debug.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+/*
+ * This module provides a 'C' style interface to the NaCl debugging
+ * related functions, and drives the C++ debugging stub. The functions
+ * defined here are safe to call, because they always check if debugging
+ * is enabled and catch all exceptions throws by underlying C++ libraries.
+ * For this reason, the debug stub library should not be called directly.
+ *
+ * The behavior of the debug stub is controlled through a combination of
+ * a set of environment (described bellow), and conditional compilation
+ * based on definition of _DEBUG.
+ *
+ * These functions are non-reentrant except for the functions notifying
+ * the debug stub that thread has been created or is about to be
+ * destroyed. It is expected the other functions will all be called by
+ * the main thread.
+ */
+
+#ifndef NATIVE_CLIENT_SERVICE_RUNTIME_NACL_DEBUG_H_
+#define NATIVE_CLIENT_SERVICE_RUNTIME_NACL_DEBUG_H_
+
+#include "native_client/src/include/nacl_base.h"
+#include "native_client/src/include/portability.h"
+
+EXTERN_C_BEGIN
+
+struct NaClApp;
+struct NaClAppThread;
+
+/*
+ * Allows debugging if the feature has been enabled by compile flags.
+ * The conditional compile means setting this to non-zero only signals
+ * that debugging is requested, NaClDebugIsEnabled will need to be called
+ * to determine if debugging is actually enabled.
+ */
+void NaClDebugSetAllow(int val) NO_THROW;
+
+/*
+ * If debugging is enabled, setting this to true will cause the NEXE
+ * to break immediately to allow a debugger to attach.
+ */
+void NaClDebugSetStartBroken(int val) NO_THROW;
+
+/*
+ * Returns non-zero if debugging is allowed. This check is done via the
+ * NACL_DEBUG_ENABLE environment variable.
+ */
+//int NaClDebugIsEnabled(void) NO_THROW; //???
+
+/*
+ * These functions should be called before the debug server has started
+ * to prevent a debugger from attaching and failing to retrieve
+ * information about the App (NEXE) that is running. These functions are
+ * safe to call even if debugging has not been enabled.
+ */
+//void NaClDebugSetAppPath(const char *path) NO_THROW; //???
+void NaClDebugSetAppInfo(struct NaClApp *app) NO_THROW;
+void NaClDebugSetAppEnvironment(int argc, char const * const argv[],
+ int envc, char const * const envv[]) NO_THROW;
+
+/*
+ * This function must be called each time we start a new App thread by the
+ * by the untrusted thread itself as early as possible. The function will
+ * notifying the debuging stub of it's existance, and preparing the thread
+ * for signal or exception handling. For each thread that calls this
+ * function, if debugging is:
+ *
+ * ENABLED : all exceptions will be sent to the debug stub.
+ * DISABLED: all exceptions will be sent to breakpad.
+ */
+void NaClDebugThreadPrepDebugging(struct NaClAppThread *natp) NO_THROW;
+
+
+/*
+ * This function notifies the debug stub that the provided thread
+ * should no longer be debugged. This is typically because the thread
+ * is about to halt. TODO(noelallen) Unlike "Prep", this function can
+ * be called by any thread.
+ */
+void NaClDebugThreadStopDebugging(struct NaClAppThread *natp) NO_THROW;
+
+
+/*
+ * This function notifies the debug stub that the system is ready to
+ * allow debugging, and should finish preparation for debuggers to attach.
+ * The function will launch a new thread to act as a sever processing GDB
+ * RSP based connections. How a remote debugger connects to the debugging
+ * stub is controlled by the following environment variables:
+ * NACL_DEBUG_ENABLE - if defined, debugging is allowed
+ * NACL_DEBUG_IP - IPv4 address on which to bind (default "127.0.0.1")
+ * NACL_DEBUG_PORT - Port(s) on which to listen (default "8000:8010")
+ */
+int NaClDebugStart(void) NO_THROW;
+
+
+/*
+ * Signals the debugging thread (if one has started), that it should
+ * release all debugging resources and halt. Prior to closing any
+ * connections, the debugging stub has the opportunity to notify
+ * an attached debugger (assuming it is in a state to receive the
+ * message) of the App exit code.
+ */
+void NaClDebugStop(int exitCode) NO_THROW;
+
+
+EXTERN_C_END
+
+#endif /* NATIVE_CLIENT_SERVICE_RUNTIME_NACL_DEBUG_H_ */
diff --git a/native_client_sdk/src/build_tools/debug_server/port/event.h b/native_client_sdk/src/build_tools/debug_server/port/event.h
new file mode 100644
index 0000000..cd6a28e
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/port/event.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+
+// This module defines the interface for using platform specific events.
+// It is expected that the Allocation function will return NULL on any
+// failure instead of throwing an exception. This module is expected
+// to throw a std::exception when an unexpected OS error is encoutered.
+#ifndef NATIVE_CLIENT_PORT_EVENT_H_
+#define NATIVE_CLIENT_PORT_EVENT_H_ 1
+
+namespace port {
+
+class IEvent {
+ public:
+ virtual void Signal() = 0; // Free one waiting thread
+ virtual void Wait() = 0; // Suspend this thread waiting for signal
+
+ static IEvent *Allocate(); // Allocate an IEvent
+ static void Free(IEvent *e); // Free the IEvent
+
+ protected:
+ virtual ~IEvent() {} // Prevent delete of base pointer
+};
+
+
+} // namespace port
+
+#endif // NATIVE_CLIENT_PORT_EVENT_H_
+
diff --git a/native_client_sdk/src/build_tools/debug_server/port/mutex.h b/native_client_sdk/src/build_tools/debug_server/port/mutex.h
new file mode 100644
index 0000000..44458bb6
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/port/mutex.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+
+// This module defines the interface for using platform specific mutexes.
+// It is expected that the Allocation function will return NULL on any
+// failure instead of throwing an exception. This module is expected
+// to throw a std::exception when an unexpected OS error is encoutered.
+//
+// The mutex is owned by a single thread at a time. The thread that
+// owns the mutex is free to call Lock multiple times, however it must
+// call Unlock the same number of times to release the lock.
+#ifndef NATIVE_CLIENT_PORT_MUTEX_H_
+#define NATIVE_CLIENT_PORT_MUTEX_H_ 1
+
+#include <assert.h>
+#include <stddef.h>
+
+namespace port {
+
+class IMutex {
+ public:
+ virtual void Lock() = 0; // Block until the mutex is taken
+ virtual void Unlock() = 0; // Unlock the mutext
+ virtual bool Try() = 0; // Try to lock, but return immediately
+
+ static IMutex *Allocate(); // Allocate a mutex
+ static void Free(IMutex *mtx); // Free a mutex
+
+ protected:
+ virtual ~IMutex() {} // Prevent delete of base pointer
+};
+
+
+// MutexLock
+// A MutexLock object will lock on construction and automatically
+// unlock on destruction of the object as the object goes out of scope.
+class MutexLock {
+ public:
+ explicit MutexLock(IMutex *mutex) : mutex_(mutex) {
+ assert(NULL != mutex_);
+ mutex_->Lock();
+ }
+ ~MutexLock() {
+ mutex_->Unlock();
+ }
+
+ private:
+ IMutex *mutex_;
+ MutexLock(const MutexLock&);
+ MutexLock &operator=(const MutexLock&);
+};
+
+
+} // namespace port
+
+#endif // NATIVE_CLIENT_PORT_MUTEX_H_
+
diff --git a/native_client_sdk/src/build_tools/debug_server/port/platform.h b/native_client_sdk/src/build_tools/debug_server/port/platform.h
new file mode 100644
index 0000000..b79103d0
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/port/platform.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+
+// This module defines the interface for platform specific support
+// functions, such as thread creation, logging, exception catching,
+// etc... This API is not expected to throw, and instead will return
+// false on any function that can fail. Since this is a collection
+// of helpers functions there is expected to be only one platform object
+// which can be retrieved with the static "Get" member.
+#ifndef NATIVE_CLIENT_PORT_PLATFORM_H_
+#define NATIVE_CLIENT_PORT_PLATFORM_H_ 1
+
+#include "native_client/src/debug_server/port/std_types.h"
+#include "native_client/src/debug_server/port/transport.h"
+
+namespace port {
+
+class IPlatform {
+ public:
+ typedef void (*ThreadFunc_t)(void *cookie);
+
+ // Get the id of the currently executing thread
+ static uint32_t GetCurrentThread();
+
+ // Called to request the platform start/stop the thread
+ static uint32_t CreateThread(ThreadFunc_t func, void *cookie);
+
+ // Request the current thread relinquish execution of msec milliseconds
+ static void Relinquish(uint32_t msec);
+
+ // Called to get or set process memory.
+ // NOTE: These functions should change the protection of the underlying
+ // page if needed to provide access. It should only return false if
+ // the page is not mapped into the debugged process.
+ static bool GetMemory(uint64_t address, uint32_t length, void *dst);
+ static bool SetMemory(uint64_t address, uint32_t length, void *src);
+
+ // Log a message
+ static void LogInfo(const char *fmt, ...);
+ static void LogWarning(const char *fmt, ...);
+ static void LogError(const char *fmt, ...);
+};
+
+} // namespace port
+
+
+#endif // NATIVE_CLIENT_PORT_PLATFORM_H_
+
diff --git a/native_client_sdk/src/build_tools/debug_server/port/port.gyp b/native_client_sdk/src/build_tools/debug_server/port/port.gyp
new file mode 100644
index 0000000..a0d9dc2
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/port/port.gyp
@@ -0,0 +1,54 @@
+# -*- python -*-
+# Copyright 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+{
+ 'includes': [
+ '../../src/build/common.gypi',
+ ],
+ 'variables': {
+ 'common_sources': [
+ 'event.h',
+ 'mutex.h',
+ 'platform.h',
+ 'thread.h',
+ 'transport.h',
+ 'std_types.h',
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'port',
+ 'type': 'static_library',
+ 'sources': [
+ '<@(common_sources)',
+ ],
+ },
+ ],
+}
diff --git a/native_client_sdk/src/build_tools/debug_server/port/std_types.h b/native_client_sdk/src/build_tools/debug_server/port/std_types.h
new file mode 100644
index 0000000..69b1d6d
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/port/std_types.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+#ifndef NATIVE_CLIENT_PORT_STD_TYPES_H_
+#define NATIVE_CLIENT_PORT_STD_TYPES_H_
+
+#ifdef WIN32
+
+// Disable warning for Windows "safe" vsprintf_s, strcpy_s, etc...
+// since we use the same version for Linux/Mac.
+#pragma warning(disable:4996)
+
+// For intptr_t
+#include <crtdefs.h>
+
+typedef signed char int8;
+typedef short int16;
+typedef int int32;
+typedef long long int64;
+
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef unsigned int uint32;
+typedef unsigned long long uint64;
+
+typedef signed char int8_t;
+typedef short int16_t;
+typedef int int32_t;
+typedef long long int64_t;
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+typedef unsigned long long uint64_t;
+
+#else
+#include <stdint.h>
+#endif
+
+#endif // NATIVE_CLIENT_PORT_STD_TYPES_H_
+
diff --git a/native_client_sdk/src/build_tools/debug_server/port/thread.h b/native_client_sdk/src/build_tools/debug_server/port/thread.h
new file mode 100644
index 0000000..c22637e
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/port/thread.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+
+// This module defines the interface for interacting with platform specific
+// threads . This API provides a mechanism to query for a thread, by using
+// the acquire method with the ID of a pre-existing thread. The register
+// accessors are expected to return false if the thread is not in a state
+// where the registers can be accessed, such RUNNING or SYSCALL. This API
+// will throw:
+// std::exception - if a unexpected OS Error is encountered.
+// std::out_of_range - if the register index is out of range.
+
+#ifndef NATIVE_CLIENT_PORT_THREAD_H_
+#define NATIVE_CLIENT_PORT_THREAD_H_ 1
+
+#include <stdlib.h>
+#include <map>
+
+#include "native_client/src/debug_server/port/std_types.h"
+
+namespace port {
+
+class IThread {
+ public:
+ enum State {
+ DEAD =-1, // The thread has exited or been killed
+ RUNNING = 0, // The thread is currently running
+ SUSPENDED= 1, // The thread has been suspended
+ SIGNALED = 2, // The thread is signaled
+ SYSCALL = 3 // In a sys call, it's registers can not be modified.
+ };
+
+ typedef void (*CatchFunc_t)(uint32_t id, int8_t sig, void *cookie);
+ typedef std::map<uint32_t, IThread*> ThreadMap_t;
+
+ virtual uint32_t GetId() = 0;
+ virtual State GetState() = 0;
+
+ virtual bool SetStep(bool on) = 0;
+
+ virtual bool GetRegister(uint32_t index, void *dst, uint32_t len) = 0;
+ virtual bool SetRegister(uint32_t index, void *src, uint32_t len) = 0;
+
+ virtual bool Suspend() = 0;
+ virtual bool Resume() = 0;
+
+ virtual void *GetContext() = 0;
+
+ static IThread *Acquire(uint32_t id, bool create = true);
+ static void Release(IThread *thread);
+ static void SetExceptionCatch(CatchFunc_t func, void *cookie);
+
+ protected:
+ virtual ~IThread() {} // Prevent delete of base pointer
+};
+
+} // namespace port
+
+#endif // PORT_THREAD_H_
+
diff --git a/native_client_sdk/src/build_tools/debug_server/port/transport.h b/native_client_sdk/src/build_tools/debug_server/port/transport.h
new file mode 100644
index 0000000..e42a9b4
--- /dev/null
+++ b/native_client_sdk/src/build_tools/debug_server/port/transport.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can
+ * be found in the LICENSE file.
+ */
+
+
+// This module provides interfaces for an IO stream. The stream is
+// expected to throw a std::exception if the stream is terminated on
+// either side.
+#ifndef NATIVE_CLIENT_PORT_TRANSPORT_H_
+#define NATIVE_CLIENT_PORT_TRANSPORT_H_
+
+#include "native_client/src/debug_server/port/std_types.h"
+
+namespace port {
+
+class ITransport {
+ public:
+ // Read from this transport, return a negative value if there is an error
+ // otherwise return the number of bytes actually read.
+ virtual int32_t Read(void *ptr, int32_t len) = 0;
+
+ // Write to this transport, return a negative value if there is an error
+ // otherwise return the number of bytes actually written.
+ virtual int32_t Write(const void *ptr, int32_t len) = 0;
+
+ // Return true once read will not block or false after ms milliseconds.
+ virtual bool ReadWaitWithTimeout(uint32_t ms) = 0;
+
+ // Disconnect the transport, R/W and Select will now throw an exception
+ virtual void Disconnect() = 0;
+
+ // Attempt to connect to, or accept connection at the specified address
+ static ITransport *Connect(const char *addr);
+ static ITransport *Accept(const char *addr);
+ static void Free(ITransport *transport);
+
+ protected:
+ virtual ~ITransport() {} // Prevent delete of base pointer
+};
+
+
+} // namespace port
+
+#endif // NATIVE_CLIENT_PORT_TRANSPORT_H_
+
diff --git a/native_client_sdk/src/build_tools/generate_installers.py b/native_client_sdk/src/build_tools/generate_installers.py
new file mode 100755
index 0000000..348324c
--- /dev/null
+++ b/native_client_sdk/src/build_tools/generate_installers.py
@@ -0,0 +1,160 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Assemble the final installer for each platform.
+
+At this time this is just a tarball.
+"""
+
+import build_utils
+import installer_contents
+import optparse
+import os
+import re
+import shutil
+import stat
+import string
+import subprocess
+import sys
+
+EXCLUDE_DIRS = ['.download',
+ '.svn']
+
+INSTALLER_NAME = 'nacl-sdk.tgz'
+
+
+# A list of all platforms that should use the Windows-based build strategy
+# (which makes a self-extracting zip instead of a tarball).
+WINDOWS_BUILD_PLATFORMS = ['cygwin', 'win32']
+
+
+# Return True if |file| should be excluded from the tarball.
+def ExcludeFile(dir, file):
+ return (file.startswith('.DS_Store') or
+ file.startswith('._') or file == "make.cmd" or
+ file == 'DEPS' or file == 'codereview.settings' or
+ (file == "httpd.cmd"))
+
+
+def main(argv):
+ bot = build_utils.BotAnnotator()
+ bot.Print('generate_installers is starting.')
+
+ # Cache the current location so we can return here before removing the
+ # temporary dirs.
+ script_dir = os.path.abspath(os.path.dirname(__file__))
+ home_dir = os.path.realpath(os.path.dirname(os.path.dirname(script_dir)))
+
+ version_dir = build_utils.VersionString()
+ parent_dir = os.path.dirname(script_dir)
+ deps_file = os.path.join(parent_dir, 'DEPS')
+
+ # Create a temporary directory using the version string, then move the
+ # contents of src to that directory, clean the directory of unwanted
+ # stuff and finally tar it all up using the platform's tar. There seems to
+ # be a problem with python's tarfile module and symlinks.
+ temp_dir = os.path.join(script_dir, 'installers_temp')
+ installer_dir = os.path.join(temp_dir, version_dir)
+ bot.Print('generate_installers chose installer directory: %s' %
+ (installer_dir))
+ try:
+ os.makedirs(installer_dir, mode=0777)
+ except OSError:
+ pass
+
+ # Decide environment to run in per platform.
+ env = os.environ.copy()
+ # Set up the required env variables for the scons builders.
+ env['NACL_SDK_ROOT'] = parent_dir
+ env['NACL_TARGET_PLATFORM'] = '.' # Use the repo's toolchain.
+
+ # Build the experimental projects.
+ bot.BuildStep('build experimental')
+ bot.Print('generate_installers is building the experimental projects.')
+ experimental_path = os.path.join(home_dir, 'src', 'experimental')
+ scons_path = os.path.join(experimental_path, 'scons')
+ scons_cmd = scons_path + ' --nacl-platform="."'
+ subprocess.check_call(scons_cmd,
+ cwd=experimental_path,
+ env=env,
+ shell=True)
+
+ # Use native tar to copy the SDK into the build location
+ # because copytree has proven to be error prone and is not supported on mac.
+ # We use a buffer for speed here. -1 causes the default OS size to be used.
+ bot.BuildStep('copy to install dir')
+ bot.Print('generate_installers is copying contents to install directory.')
+ tar_src_dir = os.path.realpath(os.curdir)
+ all_contents = (installer_contents.INSTALLER_CONTENTS +
+ installer_contents.DOCUMENTATION_FILES)
+ if sys.platform == 'darwin':
+ all_contents += installer_contents.MAC_ONLY_CONTENTS
+ else:
+ all_contents += installer_contents.LINUX_ONLY_CONTENTS
+
+ all_contents_string = string.join(
+ installer_contents.GetFilesFromPathList(all_contents) +
+ installer_contents.GetDirectoriesFromPathList(all_contents),
+ ' ')
+ tar_cf = subprocess.Popen('tar cf - %s' % all_contents_string,
+ bufsize=-1,
+ cwd=tar_src_dir, env=env, shell=True,
+ stdout=subprocess.PIPE)
+ tar_xf = subprocess.Popen('tar xfv -',
+ cwd=installer_dir, env=env, shell=True,
+ stdin=tar_cf.stdout)
+ assert tar_xf.wait() == 0
+ assert tar_cf.poll() == 0
+
+ # This loop prunes the result of os.walk() at each excluded dir, so that it
+ # doesn't descend into the excluded dir.
+ bot.Print('generate_installers is pruning installer directory')
+ for root, dirs, files in os.walk(installer_dir):
+ rm_dirs = []
+ for excl in EXCLUDE_DIRS:
+ if excl in dirs:
+ dirs.remove(excl)
+ rm_dirs.append(os.path.join(root, excl))
+ for rm_dir in rm_dirs:
+ shutil.rmtree(rm_dir)
+ rm_files = [os.path.join(root, f) for f in files if ExcludeFile(root, f)]
+ for rm_file in rm_files:
+ os.remove(rm_file)
+
+ # Update the README file with date and version number
+ build_utils.UpdateReadMe(os.path.join(installer_dir, 'README'))
+
+ bot.BuildStep('create archive')
+ bot.Print('generate_installers is creating the installer archive')
+ # Now that the SDK directory is copied and cleaned out, tar it all up using
+ # the native platform tar.
+
+ # Set the default shell command and output name. Create a compressed tar
+ # archive from the contents of |input|. The contents of the tar archive
+ # have to be relative to |input| without including |input| in the path.
+ # Then copy the resulting archive to the |output| directory and make it
+ # read-only to group and others.
+ ar_cmd = ('tar -cvzf %(INSTALLER_NAME)s -C %(input)s . && '
+ 'cp %(INSTALLER_NAME)s %(output)s && chmod 644 %(output)s')
+
+ archive = os.path.join(home_dir, INSTALLER_NAME)
+ subprocess.check_call(
+ ar_cmd % (
+ {'INSTALLER_NAME':INSTALLER_NAME,
+ 'input':installer_dir,
+ 'output':archive}),
+ cwd=temp_dir,
+ env=env,
+ shell=True)
+
+ # Clean up.
+ shutil.rmtree(temp_dir)
+ return 0
+
+
+if __name__ == '__main__':
+ print "Directly running generate_installers.py is no longer supported."
+ print "Please instead run './scons installer' from the src directory."
+ sys.exit(1)
diff --git a/native_client_sdk/src/build_tools/generate_windows_installer.py b/native_client_sdk/src/build_tools/generate_windows_installer.py
new file mode 100755
index 0000000..5111e37
--- /dev/null
+++ b/native_client_sdk/src/build_tools/generate_windows_installer.py
@@ -0,0 +1,158 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Assemble the final installer for windows."""
+
+import build_utils
+import installer_contents
+import make_nsis_installer
+import optparse
+import os
+import shutil
+import stat
+import string
+import subprocess
+import sys
+import tar_archive
+
+IGNORE_PATTERN = ('.download*', '.svn*')
+
+def main(argv):
+ bot = build_utils.BotAnnotator()
+ bot.Print('generate_windows_installer is starting.')
+
+ # Make sure that we are running python version 2.6 or higher
+ (major, minor) = sys.version_info[:2]
+ assert major == 2 and minor >= 6
+ # Cache the current location so we can return here before removing the
+ # temporary dirs.
+ script_dir = os.path.abspath(os.path.dirname(__file__))
+ home_dir = os.path.realpath(os.path.dirname(os.path.dirname(script_dir)))
+
+ version_dir = build_utils.VersionString()
+ parent_dir = os.path.dirname(script_dir)
+ deps_file = os.path.join(parent_dir, 'DEPS')
+
+ # Create a temporary directory using the version string, then move the
+ # contents of src to that directory, clean the directory of unwanted
+ # stuff and finally create an installer.
+ temp_dir = os.path.join(script_dir, 'installers_temp')
+ installer_dir = os.path.join(temp_dir, version_dir)
+ bot.Print('generate_windows_installer chose installer directory: %s' %
+ (installer_dir))
+ try:
+ os.makedirs(installer_dir, mode=0777)
+ except OSError:
+ pass
+
+ env = os.environ.copy()
+ # Set up the required env variables for the scons builders.
+ env['NACL_SDK_ROOT'] = parent_dir
+ env['NACL_TARGET_PLATFORM'] = '.' # Use the repo's toolchain.
+
+ # Build the experimental projects.
+ bot.BuildStep('build experimental')
+ bot.Print('generate_windows_installer is building the experimental projects.')
+ experimental_path = os.path.join(home_dir, 'src', 'experimental')
+ scons_path = os.path.join(experimental_path, 'scons.bat')
+ scons_cmd = scons_path + ' --nacl-platform="."'
+ subprocess.check_call(scons_cmd, cwd=experimental_path, env=env)
+
+ # On windows we use copytree to copy the SDK into the build location
+ # because there is no native tar and using cygwin's version has proven
+ # to be error prone.
+
+ # In case previous run didn't succeed, clean this out so copytree can make
+ # its target directories.
+ bot.BuildStep('copy to install dir')
+ bot.Print('generate_windows_installer is cleaning out install directory.')
+ shutil.rmtree(installer_dir)
+ bot.Print('generate_windows_installer: copying files to install directory.')
+ all_contents = installer_contents.INSTALLER_CONTENTS + \
+ installer_contents.WINDOWS_ONLY_CONTENTS
+ for copy_source_dir in installer_contents.GetDirectoriesFromPathList(
+ all_contents):
+ copy_target_dir = os.path.join(installer_dir, copy_source_dir)
+ bot.Print("Copying %s to %s" % (copy_source_dir, copy_target_dir))
+ shutil.copytree(copy_source_dir,
+ copy_target_dir,
+ symlinks=True,
+ ignore=shutil.ignore_patterns(*IGNORE_PATTERN))
+ for copy_source_file in installer_contents.GetFilesFromPathList(
+ all_contents):
+ copy_target_file = os.path.join(installer_dir, copy_source_file)
+ bot.Print("Copying %s to %s" % (copy_source_file, copy_target_file))
+ if not os.path.exists(os.path.dirname(copy_target_file)):
+ os.makedirs(os.path.dirname(copy_target_file))
+ shutil.copy(copy_source_file, copy_target_file)
+
+ # Do special processing on the user-readable documentation files.
+ for copy_source_file in installer_contents.DOCUMENTATION_FILES:
+ copy_target_file = os.path.join(installer_dir, copy_source_file + '.txt')
+ bot.Print("Copying %s to %s" % (copy_source_file, copy_target_file))
+ with open(copy_source_file, "U") as source_file:
+ text = source_file.read().replace("\n", "\r\n")
+ with open(copy_target_file, "wb") as dest_file:
+ dest_file.write(text)
+
+ # Update the README.txt file with date and version number
+ build_utils.UpdateReadMe(os.path.join(installer_dir, 'README.txt'))
+
+ # Clean out the cruft.
+ bot.Print('generate_windows_installer: cleaning up installer directory.')
+
+ # Make everything read/write (windows needs this).
+ for root, dirs, files in os.walk(installer_dir):
+ def UpdatePermissions(list):
+ for file in map(lambda f: os.path.join(root, f), list):
+ os.chmod(file, os.lstat(file).st_mode | stat.S_IWRITE | stat.S_IREAD)
+ UpdatePermissions(dirs)
+ UpdatePermissions(files)
+
+ # Grab the toolchain manifest files and massage them so their paths are
+ # corrected for the actual toolchain layout in the SDK. The newlib toolchain
+ # manifest has these changes made:
+ # 1. Transform path components 'sdk/nacl-sdk' to 'toolchain/win_x86_newlib'
+ # 2. Remove the spurious 'sdk' directory entry.
+ # The glibc manifests can be used unmolested.
+ bot.BuildStep('generate toolchain manifests')
+ def TransformNewlibPath(npath):
+ return os.path.normpath(npath.replace('sdk/nacl-sdk',
+ 'toolchain/win_x86_newlib'))
+ newlib_manifest = tar_archive.TarArchive()
+ newlib_manifest.path_filter = TransformNewlibPath
+ newlib_manifest_path = os.path.join(
+ home_dir,
+ 'src',
+ installer_contents.GetToolchainManifest('newlib'))
+ newlib_manifest.InitWithManifest(newlib_manifest_path)
+ newlib_manifest.dirs.discard('sdk')
+
+ glibc_manifest_path = os.path.join(
+ home_dir,
+ 'src',
+ installer_contents.GetToolchainManifest('glibc'))
+ glibc_manifest = tar_archive.TarArchive()
+ glibc_manifest.InitWithManifest(glibc_manifest_path)
+
+ # Merge the newlib and glibc manifests and send them to the script generator.
+ bot.BuildStep('create Windows installer')
+ bot.Print('generate_windows_installer is creating the windows installer.')
+ build_tools_dir = os.path.join(home_dir, 'src', 'build_tools')
+ make_nsis_installer.MakeNsisInstaller(
+ installer_dir,
+ cwd=build_tools_dir,
+ toolchain_manifests=newlib_manifest | glibc_manifest)
+ bot.Print("Installer created!")
+
+ # Clean up.
+ shutil.rmtree(temp_dir)
+ return 0
+
+
+if __name__ == '__main__':
+ print "Directly running generate_windows_installer.py is no longer supported."
+ print "Please instead run 'scons.bat installer' from the src directory."
+ sys.exit(1)
diff --git a/native_client_sdk/src/build_tools/html_checker.py b/native_client_sdk/src/build_tools/html_checker.py
new file mode 100755
index 0000000..b00ffad
--- /dev/null
+++ b/native_client_sdk/src/build_tools/html_checker.py
@@ -0,0 +1,96 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''This file provides a simple HTML checker, based on the standard HTMLParser
+library.'''
+
+import HTMLParser
+import os
+import sys
+
+
+class HTMLChecker(HTMLParser.HTMLParser):
+ '''A simple html parser that can find attribute tags and validate syntax'''
+
+ def __init__(self):
+ self.tag_list = []
+ self.links = set()
+ HTMLParser.HTMLParser.__init__(self)
+
+ def handle_starttag(self, tag, attrs):
+ attributes = dict(attrs)
+ # Skip tags if they're within a <script> tag so that we don't get confused.
+ if not self.tag_list or self.tag_list[-1] != 'script':
+ self.tag_list.append(tag)
+ if tag == 'a' and attributes.get('href'):
+ self.links.add(attributes['href'])
+
+ def handle_endtag(self, tag):
+ try:
+ matching_tag = self.tag_list.pop()
+ except IndexError:
+ raise Exception('Unmatched tag %s at %s' % (tag, self.getpos()))
+ if matching_tag != tag:
+ if matching_tag == 'script':
+ self.tag_list.append(matching_tag)
+ else:
+ raise Exception('Wrong tag: Expected %s but got %s at %s' %
+ (matching_tag, tag, self.getpos()))
+
+ def close(self):
+ if self.tag_list:
+ raise Exception('Reached end-of-file with unclosed tags: %s'
+ % self.tag_list)
+ HTMLParser.HTMLParser.close(self)
+
+
+def ValidateFile(filename):
+ '''Run simple html syntax checks on given file to validate tags and links
+
+ Args:
+ filename: Name of file to validate
+
+ Returns:
+ tuple containing:
+ set of urls from this file
+ set of absolute paths from this file'''
+ (directory, basename) = os.path.split(os.path.abspath(filename))
+ parser = HTMLChecker()
+ with open(filename, 'r') as file:
+ parser.feed(file.read())
+ parser.close()
+ files = set()
+ urls = set()
+ for link in parser.links:
+ if link.startswith('http://') or link.startswith('https://'):
+ urls.add(link)
+ else:
+ files.add(os.path.abspath(os.path.join(directory, link)))
+ return urls, files
+
+
+def ValidateAllLinks(filenames):
+ '''Validate all the links in filename and all linked files on this domain'''
+ validated_files = set()
+ validated_urls = set()
+ need_to_validate = set([os.path.abspath(file) for file in filenames])
+ while need_to_validate:
+ file = need_to_validate.pop()
+ print 'Evaluating %s' % file
+ urls, files = ValidateFile(file)
+ validated_files.add(file)
+ need_to_validate |= files - validated_files
+
+
+def main(argv):
+ '''Run ValidateFile on each argument
+
+ Args:
+ argv: Command-line arguments'''
+ ValidateAllLinks(argv)
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:])) \ No newline at end of file
diff --git a/native_client_sdk/src/build_tools/install_boost/install_boost.py b/native_client_sdk/src/build_tools/install_boost/install_boost.py
new file mode 100755
index 0000000..ee93e58
--- /dev/null
+++ b/native_client_sdk/src/build_tools/install_boost/install_boost.py
@@ -0,0 +1,110 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Install boost headers into a list of toolchains, in such a way that it gets
+pulled into the SDK installer. Note that this script only installs boost
+headers, and does not build any of the boost libraries that require building.
+"""
+
+import build_utils
+import os
+import shutil
+import sys
+import tarfile
+import tempfile
+import urllib
+
+from optparse import OptionParser
+
+# The original boost distro can be found here:
+# http://sourceforge.net/projects/boost/files/boost/1.47.0/\
+# boost_1_47_0.tar.gz/download
+BOOST_URL = ('http://commondatastorage.googleapis.com/nativeclient-mirror'
+ '/nacl/boost_1_47_0.tar.gz')
+BOOST_PATH = 'boost_1_47_0'
+
+
+def DownloadAndExtract(working_dir, url, path):
+ boost_path = os.path.abspath(os.path.join(working_dir, path))
+ print 'Download: %s' % url
+ try:
+ (tgz_file, headers) = urllib.urlretrieve(url, '%s.tgz' % boost_path)
+ tar = None
+ try:
+ tar = tarfile.open(tgz_file)
+ tar.extractall(working_dir)
+ finally:
+ if tar:
+ tar.close()
+ except (URLError, ContentTooShortError):
+ print 'Error retrieving %s' % url
+ raise
+
+
+# Install the boost headers into the toolchains.
+def InstallBoost(options):
+ # Create a temporary working directory. This is where all the tar files go
+ # and where the packages get built prior to installation in the toolchain.
+ working_dir = tempfile.mkdtemp(prefix='boost')
+ try:
+ DownloadAndExtract(working_dir, BOOST_URL, BOOST_PATH)
+ except:
+ print "Error in download"
+ return 1
+ boost_include = options.third_party_dir
+ build_utils.ForceMakeDirs(boost_include)
+ boost_path = os.path.abspath(os.path.join(working_dir, BOOST_PATH))
+ # Copy the headers.
+ print 'Installing boost headers into %s...' % boost_include
+ dst_include_dir = os.path.join(boost_include, 'boost')
+ shutil.rmtree(dst_include_dir, ignore_errors=True)
+ shutil.copytree(os.path.join(boost_path, 'boost'),
+ dst_include_dir,
+ symlinks=True)
+ # Copy the license file.
+ print 'Installing boost license...'
+ shutil.copy(os.path.join(boost_path, 'LICENSE_1_0.txt'), dst_include_dir)
+
+ # Clean up.
+ shutil.rmtree(working_dir, ignore_errors=True)
+ return 0
+
+
+# Parse the command-line args and set up the options object. There is one
+# command-line switch:
+# --toolchain=<path to the platform-specific toolchain>
+# e.g.: --toolchain=../toolchain/mac-x86
+# default is 'toolchain'.
+# --toolchain can appear more than once, the Boost library is
+# installed into each toolchain listed.
+def main(argv):
+ parser = OptionParser()
+ parser.add_option(
+ '-t', '--toolchain', dest='toolchains',
+ action='append',
+ type='string',
+ help='NaCl toolchain directory')
+ parser.add_option(
+ '--third-party', dest='third_party_dir',
+ type='string',
+ default='third_party',
+ help='location of third_party directory')
+ (options, args) = parser.parse_args(argv)
+ if args:
+ print 'WARNING: unrecognized argument: %s' % str(args)
+ parser.print_help()
+
+ if not options.toolchains:
+ options.toolchains = [build_utils.TOOLCHAIN_AUTODETECT]
+ options.toolchains = [build_utils.NormalizeToolchain(tc)
+ for tc in options.toolchains]
+
+ print "Installing boost into %s" % str(options.third_party_dir)
+ return InstallBoost(options)
+
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/native_client_sdk/src/build_tools/install_gtest/install_gtest.py b/native_client_sdk/src/build_tools/install_gtest/install_gtest.py
new file mode 100644
index 0000000..7c489b0
--- /dev/null
+++ b/native_client_sdk/src/build_tools/install_gtest/install_gtest.py
@@ -0,0 +1,239 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Install GTest and GMock that can be linked to a NaCl module. By default
+this script builds both the 32- and 64-bit versions of the libraries, and
+installs them in <toolchain>/nacl/usr/lib and <toolchain>/nacl64/usr/lib. The
+header files are also installed in <toolchain>/<ncal_spec>/usr/include.
+"""
+
+import build_utils
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+import urllib
+
+from optparse import OptionParser
+
+# Default values for the --toolchain and --bit-spec command line arguments.
+BIT_SPEC_DEFAULT = "32,64"
+
+# The original gtest distro can be found here:
+# http://code.google.com/p/googletest/downloads/detail?name=gtest-1.5.0.tar.gz
+GTEST_URL = ("http://commondatastorage.googleapis.com/nativeclient-mirror/nacl/"
+ "gtest-1.5.0.tgz")
+GTEST_PATH = "gtest-1.5.0"
+GTEST_PATCH_FILE = "nacl-gtest-1.5.0.patch"
+
+# The original gmock distro can be found here:
+# http://googlemock.googlecode.com/files/gmock-1.5.0.tar.gz
+GMOCK_URL = ("http://commondatastorage.googleapis.com/nativeclient-mirror/nacl/"
+ "gmock-1.5.0.tgz")
+GMOCK_PATH = "gmock-1.5.0"
+GMOCK_PATCH_FILE = "nacl-gmock-1.5.0.patch"
+
+
+# Create a temporary working directory. This is where all the tar files go
+# and where the packages get built prior to installation in the toolchain.
+def MakeWorkingDir(options):
+ # Pick work directory.
+ if not options.working_dir:
+ options.working_dir = tempfile.mkdtemp(prefix='gtest')
+ options.must_clean_up = True
+ # Go into working area.
+ options.old_cwd = os.getcwd()
+ os.chdir(options.working_dir)
+
+
+# Download GTest and GMock into the working directory, then extract them.
+def DownloadAndExtractAll(options):
+ def DownloadAndExtract(url, path):
+ print "Download: %s" % url
+ (zip_file, headers) = urllib.urlretrieve(url, '%s.tgz' % path)
+ p = subprocess.Popen('tar xzf %s' % (zip_file),
+ env=options.shell_env,
+ shell=True)
+ assert p.wait() == 0
+
+ os.chdir(options.working_dir)
+ try:
+ DownloadAndExtract(GTEST_URL, GTEST_PATH)
+ DownloadAndExtract(GMOCK_URL, GMOCK_PATH)
+ except (URLError, ContentTooShortError):
+ os.chdir(options.old_cwd)
+ print "Error retrieving %s" % url
+ raise
+
+ os.chdir(options.old_cwd)
+
+
+# Apply the patch files to the extracted GTest and GMock pacakges.
+def PatchAll(options):
+ def Patch(abs_path, patch_file):
+ print "Patching %s with: %s" % (abs_path, patch_file)
+ p = subprocess.Popen('chmod -R a+w . && patch -p0 < %s' % (patch_file),
+ cwd=abs_path,
+ env=options.shell_env,
+ shell=True)
+ assert p.wait() == 0
+
+ Patch(options.working_dir, os.path.join(options.script_dir, GTEST_PATCH_FILE))
+ Patch(options.working_dir, os.path.join(options.script_dir, GMOCK_PATCH_FILE))
+
+
+# Build GTest and GMock, then install them into the toolchain. Note that
+# GTest has to be built and installed into the toolchain before GMock can be
+# built, because GMock relies on headers from GTest. This method sets up all
+# the necessary shell environment variables for the makefiles, such as CC and
+# CXX.
+def BuildAndInstallAll(options):
+ def BuildInPath(abs_path, shell_env):
+ # Run make clean and make in |abs_path|. Assumes there is a makefile in
+ # |abs_path|, if not then the assert will trigger.
+ print "Building in %s" % (abs_path)
+ p = subprocess.Popen('make clean && make -j4',
+ cwd=abs_path,
+ env=shell_env,
+ shell=True)
+ assert p.wait() == 0
+
+ def InstallLib(lib, src_path, dst_path, shell_env):
+ # Use the install untility to install |lib| from |src_path| into
+ # |dst_path|.
+ p = subprocess.Popen("install -m 644 %s %s" % (lib, dst_path),
+ cwd=src_path,
+ env=shell_env,
+ shell=True)
+ assert p.wait() == 0
+
+ # Build and install for each bit-spec.
+ for bit_spec in options.bit_spec.split(','):
+ if bit_spec == '64':
+ nacl_spec = 'x86_64-nacl'
+ else:
+ nacl_spec = 'i686-nacl'
+
+ print 'Building gtest and gmock for NaCl spec: %s.' % nacl_spec
+ # Make sure the target directories exist.
+ nacl_usr_include = os.path.join(options.toolchain,
+ nacl_spec,
+ 'usr',
+ 'include')
+ nacl_usr_lib = os.path.join(options.toolchain,
+ nacl_spec,
+ 'usr',
+ 'lib')
+ build_utils.ForceMakeDirs(nacl_usr_include)
+ build_utils.ForceMakeDirs(nacl_usr_lib)
+
+ # Set up the nacl-specific environment variables used by make.
+ build_env = options.shell_env.copy()
+ toolchain_bin = os.path.join(options.toolchain, 'bin')
+ build_env['CC'] = os.path.join(toolchain_bin, '%s-gcc' % nacl_spec)
+ build_env['CXX'] = os.path.join(toolchain_bin, '%s-g++' % nacl_spec)
+ build_env['AR'] = os.path.join(toolchain_bin, '%s-ar' % nacl_spec)
+ build_env['RANLIB'] = os.path.join(toolchain_bin, '%s-ranlib' % nacl_spec)
+ build_env['LD'] = os.path.join(toolchain_bin, '%s-ld' % nacl_spec)
+ build_env['NACL_TOOLCHAIN_ROOT'] = options.toolchain
+
+ # GTest has to be built & installed before GMock can be built.
+ gtest_path = os.path.join(options.working_dir, GTEST_PATH)
+ BuildInPath(gtest_path, build_env)
+ gtest_tar_excludes = "--exclude='gtest-death-test.h' \
+ --exclude='gtest-death-test-internal.h'"
+ tar_cf = subprocess.Popen("tar cf - %s gtest" % gtest_tar_excludes,
+ cwd=os.path.join(gtest_path, 'include'),
+ env=build_env,
+ shell=True,
+ stdout=subprocess.PIPE)
+ tar_xf = subprocess.Popen("tar xfp -",
+ cwd=nacl_usr_include,
+ env=build_env,
+ shell=True,
+ stdin=tar_cf.stdout)
+ tar_copy_err = tar_xf.communicate()[1]
+ InstallLib('libgtest.a', gtest_path, nacl_usr_lib, build_env)
+
+ gmock_path = os.path.join(options.working_dir, GMOCK_PATH)
+ BuildInPath(gmock_path, build_env)
+ tar_cf = subprocess.Popen("tar cf - gmock",
+ cwd=os.path.join(gmock_path, 'include'),
+ env=options.shell_env,
+ shell=True,
+ stdout=subprocess.PIPE)
+ tar_xf = subprocess.Popen("tar xfp -",
+ cwd=nacl_usr_include,
+ env=options.shell_env,
+ shell=True,
+ stdin=tar_cf.stdout)
+ tar_copy_err = tar_xf.communicate()[1]
+ InstallLib('libgmock.a', gmock_path, nacl_usr_lib, build_env)
+
+
+# Main driver method that creates a working directory, then downloads and
+# extracts GTest and GMock, patches and builds them both, then installs them
+# into the toolchain specified in |options.toolchain|.
+def InstallTestingLibs(options):
+ MakeWorkingDir(options)
+ try:
+ DownloadAndExtractAll(options)
+ PatchAll(options)
+ except:
+ return 1
+
+ BuildAndInstallAll(options)
+ # Clean up.
+ if options.must_clean_up:
+ shutil.rmtree(options.working_dir, ignore_errors=True)
+ return 0
+
+
+# Parse the command-line args and set up the options object. There are two
+# command-line switches:
+# --toolchain=<path to the platform-specific toolchain>
+# e.g.: --toolchain=../toolchain/mac-x86
+# default is |TOOLCHAIN_AUTODETECT| which means try to determine
+# the toolchain dir from |sys.platform|.
+# --bit-spec=<comma-separated list of instruction set bit-widths
+# e.g.: --bit_spec=32,64
+# default is "32,64" which means build 32- and 64-bit versions
+# of the libraries.
+def main(argv):
+ shell_env = os.environ;
+ if not build_utils.CheckPatchVersion(shell_env):
+ sys.exit(0)
+
+ parser = OptionParser()
+ parser.add_option(
+ '-t', '--toolchain', dest='toolchain',
+ default=build_utils.TOOLCHAIN_AUTODETECT,
+ help='where to put the testing libraries')
+ parser.add_option(
+ '-b', '--bit-spec', dest='bit_spec',
+ default=BIT_SPEC_DEFAULT,
+ help='comma separated list of instruction set bit-widths')
+ parser.add_option(
+ '-w', '--working_dir', dest='working_dir',
+ default=None,
+ help='where to untar and build the test libs.')
+ (options, args) = parser.parse_args(argv)
+ if args:
+ print 'ERROR: invalid argument: %s' % str(args)
+ parser.print_help()
+ sys.exit(1)
+
+ options.must_clean_up = False
+ options.shell_env = shell_env
+ options.script_dir = os.path.abspath(os.path.dirname(__file__))
+ options.toolchain = build_utils.NormalizeToolchain(options.toolchain)
+ print "Installing testing libs into toolchain %s" % options.toolchain
+
+ return InstallTestingLibs(options)
+
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/native_client_sdk/src/build_tools/install_gtest/nacl-gmock-1.5.0.patch b/native_client_sdk/src/build_tools/install_gtest/nacl-gmock-1.5.0.patch
new file mode 100644
index 0000000..0f22bb1
--- /dev/null
+++ b/native_client_sdk/src/build_tools/install_gtest/nacl-gmock-1.5.0.patch
@@ -0,0 +1,29 @@
+diff -Naur gmock-1.5.0/Makefile gmock-1.5.0.nacl/Makefile
+--- gmock-1.5.0/Makefile 1969-12-31 17:00:00.000000000 -0700
++++ gmock-1.5.0.nacl/Makefile 2010-07-07 14:28:27.000000000 -0600
+@@ -0,0 +1,25 @@
++CXXFLAGS = -O0 -g
++INCLUDE = -Iinclude \
++ -I. \
++ -I$(NACL_TOOLCHAIN_ROOT)/nacl/usr/include \
++ -I$(NACL_TOOLCHAIN_ROOT)/nacl64/usr/include
++LIB_GMOCK = libgmock.a
++OBJ_DIR = obj
++
++OBJ_FILES = gmock-all.o \
++ gmock_main.o
++
++all: $(LIB_GMOCK)
++ @echo "Making libgmock"
++
++clean:
++ rm -rf $(OBJ_DIR) $(LIB_GMOCK)
++
++$(OBJ_DIR):
++ mkdir $(OBJ_DIR)
++
++$(OBJ_FILES): %.o: src/%.cc $(OBJ_DIR)
++ $(CXX) -c $(INCLUDE) $(CXXFLAGS) $< -o $(OBJ_DIR)/$@
++
++$(LIB_GMOCK): $(OBJ_DIR) $(OBJ_FILES)
++ cd $(OBJ_DIR) && $(AR) rcs ../$(LIB_GMOCK) $(OBJ_FILES)
diff --git a/native_client_sdk/src/build_tools/install_gtest/nacl-gtest-1.5.0.patch b/native_client_sdk/src/build_tools/install_gtest/nacl-gtest-1.5.0.patch
new file mode 100644
index 0000000..389c51f
--- /dev/null
+++ b/native_client_sdk/src/build_tools/install_gtest/nacl-gtest-1.5.0.patch
@@ -0,0 +1,226 @@
+diff -Naur gtest-1.5.0/Makefile gtest-1.5.0.nacl/Makefile
+--- gtest-1.5.0/Makefile 1969-12-31 17:00:00.000000000 -0700
++++ gtest-1.5.0.nacl/Makefile 2010-07-08 09:49:37.000000000 -0600
+@@ -0,0 +1,25 @@
++CXXFLAGS = -O0 -g
++INCLUDE = -Iinclude -I.
++LIB_GTEST = libgtest.a
++OBJ_DIR = obj
++
++OBJ_FILES = gtest.o \
++ gtest-filepath.o \
++ gtest-port.o \
++ gtest-test-part.o \
++ gtest-typed-test.o
++
++all: $(LIB_GTEST)
++ @echo "Making libgtest"
++
++clean:
++ rm -rf $(OBJ_DIR) $(LIB_GTEST)
++
++$(OBJ_DIR):
++ mkdir $(OBJ_DIR)
++
++$(OBJ_FILES): %.o: src/%.cc $(OBJ_DIR)
++ $(CXX) -c $(INCLUDE) $(CXXFLAGS) $< -o $(OBJ_DIR)/$@
++
++$(LIB_GTEST): $(OBJ_DIR) $(OBJ_FILES)
++ cd $(OBJ_DIR) && $(AR) rcs ../$(LIB_GTEST) $(OBJ_FILES)
+diff -Naur gtest-1.5.0/include/gtest/gtest.h gtest-1.5.0.nacl/include/gtest/gtest.h
+--- gtest-1.5.0/include/gtest/gtest.h 2010-04-15 16:02:03.000000000 -0600
++++ gtest-1.5.0.nacl/include/gtest/gtest.h 2010-07-08 09:49:37.000000000 -0600
+@@ -56,7 +56,6 @@
+
+ #include <gtest/internal/gtest-internal.h>
+ #include <gtest/internal/gtest-string.h>
+-#include <gtest/gtest-death-test.h>
+ #include <gtest/gtest-message.h>
+ #include <gtest/gtest-param-test.h>
+ #include <gtest/gtest_prod.h>
+diff -Naur gtest-1.5.0/include/gtest/internal/gtest-port.h gtest-1.5.0.nacl/include/gtest/internal/gtest-port.h
+--- gtest-1.5.0/include/gtest/internal/gtest-port.h 2010-04-15 16:02:02.000000000 -0600
++++ gtest-1.5.0.nacl/include/gtest/internal/gtest-port.h 2010-07-08 09:49:37.000000000 -0600
+@@ -222,14 +222,13 @@
+ #endif // __CYGWIN__
+
+ #if GTEST_OS_CYGWIN || GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_SYMBIAN || \
+- GTEST_OS_SOLARIS || GTEST_OS_AIX
++ GTEST_OS_SOLARIS || GTEST_OS_AIX && !defined(__native_client__)
+
+ // On some platforms, <regex.h> needs someone to define size_t, and
+ // won't compile otherwise. We can #include it here as we already
+ // included <stdlib.h>, which is guaranteed to define size_t through
+ // <stddef.h>.
+ #include <regex.h> // NOLINT
+-#include <strings.h> // NOLINT
+ #include <sys/types.h> // NOLINT
+ #include <time.h> // NOLINT
+ #include <unistd.h> // NOLINT
+@@ -485,7 +484,9 @@
+ #if (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \
+ (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \
+ GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX)
++#if !defined(__native_client__)
+ #define GTEST_HAS_DEATH_TEST 1
++#endif
+ #include <vector> // NOLINT
+ #endif
+
+@@ -839,11 +840,7 @@
+ // testing Google Test's own constructs. Don't use it in user tests,
+ // either directly or indirectly.
+ inline void SleepMilliseconds(int n) {
+- const timespec time = {
+- 0, // 0 seconds.
+- n * 1000L * 1000L, // And n ms.
+- };
+- nanosleep(&time, NULL);
++ usleep(100 * n);
+ }
+
+ // Allows a controller thread to pause execution of newly created
+diff -Naur gtest-1.5.0/src/gtest-death-test.cc gtest-1.5.0.nacl/src/gtest-death-test.cc
+--- gtest-1.5.0/src/gtest-death-test.cc 2010-04-15 16:02:04.000000000 -0600
++++ gtest-1.5.0.nacl/src/gtest-death-test.cc 2010-07-08 09:49:38.000000000 -0600
+@@ -34,6 +34,8 @@
+ #include <gtest/gtest-death-test.h>
+ #include <gtest/internal/gtest-port.h>
+
++#undef GTEST_HAS_DEATH_TEST
++
+ #if GTEST_HAS_DEATH_TEST
+
+ #if GTEST_OS_MAC
+diff -Naur gtest-1.5.0/src/gtest-filepath.cc gtest-1.5.0.nacl/src/gtest-filepath.cc
+--- gtest-1.5.0/src/gtest-filepath.cc 2010-04-15 16:02:04.000000000 -0600
++++ gtest-1.5.0.nacl/src/gtest-filepath.cc 2010-07-08 09:49:38.000000000 -0600
+@@ -96,21 +96,6 @@
+ #endif
+ }
+
+-// Returns the current working directory, or "" if unsuccessful.
+-FilePath FilePath::GetCurrentDir() {
+-#if GTEST_OS_WINDOWS_MOBILE
+- // Windows CE doesn't have a current directory, so we just return
+- // something reasonable.
+- return FilePath(kCurrentDirectoryString);
+-#elif GTEST_OS_WINDOWS
+- char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
+- return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
+-#else
+- char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
+- return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
+-#endif // GTEST_OS_WINDOWS_MOBILE
+-}
+-
+ // Returns a copy of the FilePath with the case-insensitive extension removed.
+ // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
+ // FilePath("dir/file"). If a case-insensitive extension is not
+diff -Naur gtest-1.5.0/src/gtest-internal-inl.h gtest-1.5.0.nacl/src/gtest-internal-inl.h
+--- gtest-1.5.0/src/gtest-internal-inl.h 2010-04-15 16:02:04.000000000 -0600
++++ gtest-1.5.0.nacl/src/gtest-internal-inl.h 2010-07-08 09:49:39.000000000 -0600
+@@ -67,13 +67,6 @@
+
+ namespace testing {
+
+-// Declares the flags.
+-//
+-// We don't want the users to modify this flag in the code, but want
+-// Google Test's own unit tests to be able to access it. Therefore we
+-// declare it here as opposed to in gtest.h.
+-GTEST_DECLARE_bool_(death_test_use_fork);
+-
+ namespace internal {
+
+ // The value of GetTestTypeId() as seen from within the Google Test
+@@ -154,10 +147,7 @@
+ break_on_failure_ = GTEST_FLAG(break_on_failure);
+ catch_exceptions_ = GTEST_FLAG(catch_exceptions);
+ color_ = GTEST_FLAG(color);
+- death_test_style_ = GTEST_FLAG(death_test_style);
+- death_test_use_fork_ = GTEST_FLAG(death_test_use_fork);
+ filter_ = GTEST_FLAG(filter);
+- internal_run_death_test_ = GTEST_FLAG(internal_run_death_test);
+ list_tests_ = GTEST_FLAG(list_tests);
+ output_ = GTEST_FLAG(output);
+ print_time_ = GTEST_FLAG(print_time);
+@@ -174,10 +164,7 @@
+ GTEST_FLAG(break_on_failure) = break_on_failure_;
+ GTEST_FLAG(catch_exceptions) = catch_exceptions_;
+ GTEST_FLAG(color) = color_;
+- GTEST_FLAG(death_test_style) = death_test_style_;
+- GTEST_FLAG(death_test_use_fork) = death_test_use_fork_;
+ GTEST_FLAG(filter) = filter_;
+- GTEST_FLAG(internal_run_death_test) = internal_run_death_test_;
+ GTEST_FLAG(list_tests) = list_tests_;
+ GTEST_FLAG(output) = output_;
+ GTEST_FLAG(print_time) = print_time_;
+@@ -193,10 +180,7 @@
+ bool break_on_failure_;
+ bool catch_exceptions_;
+ String color_;
+- String death_test_style_;
+- bool death_test_use_fork_;
+ String filter_;
+- String internal_run_death_test_;
+ bool list_tests_;
+ String output_;
+ bool print_time_;
+@@ -699,18 +683,6 @@
+ void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc,
+ Test::TearDownTestCaseFunc tear_down_tc,
+ TestInfo * test_info) {
+- // In order to support thread-safe death tests, we need to
+- // remember the original working directory when the test program
+- // was first invoked. We cannot do this in RUN_ALL_TESTS(), as
+- // the user may have changed the current directory before calling
+- // RUN_ALL_TESTS(). Therefore we capture the current directory in
+- // AddTestInfo(), which is called to register a TEST or TEST_F
+- // before main() is reached.
+- if (original_working_dir_.IsEmpty()) {
+- original_working_dir_.Set(FilePath::GetCurrentDir());
+- GTEST_CHECK_(!original_working_dir_.IsEmpty())
+- << "Failed to get the current working directory.";
+- }
+
+ GetTestCase(test_info->test_case_name(),
+ test_info->test_case_comment(),
+diff -Naur gtest-1.5.0/src/gtest.cc gtest-1.5.0.nacl/src/gtest.cc
+--- gtest-1.5.0/src/gtest.cc 2010-04-15 16:02:04.000000000 -0600
++++ gtest-1.5.0.nacl/src/gtest.cc 2010-07-08 09:49:40.000000000 -0600
+@@ -57,7 +57,9 @@
+ #include <limits.h>
+ #include <sched.h>
+ // Declares vsnprintf(). This header is not available on Windows.
++#if !defined(__native_client__)
+ #include <strings.h>
++#endif
+ #include <sys/mman.h>
+ #include <sys/time.h>
+ #include <unistd.h>
+@@ -4597,13 +4599,7 @@
+ ParseBoolFlag(arg, kCatchExceptionsFlag,
+ &GTEST_FLAG(catch_exceptions)) ||
+ ParseStringFlag(arg, kColorFlag, &GTEST_FLAG(color)) ||
+- ParseStringFlag(arg, kDeathTestStyleFlag,
+- &GTEST_FLAG(death_test_style)) ||
+- ParseBoolFlag(arg, kDeathTestUseFork,
+- &GTEST_FLAG(death_test_use_fork)) ||
+ ParseStringFlag(arg, kFilterFlag, &GTEST_FLAG(filter)) ||
+- ParseStringFlag(arg, kInternalRunDeathTestFlag,
+- &GTEST_FLAG(internal_run_death_test)) ||
+ ParseBoolFlag(arg, kListTestsFlag, &GTEST_FLAG(list_tests)) ||
+ ParseStringFlag(arg, kOutputFlag, &GTEST_FLAG(output)) ||
+ ParseBoolFlag(arg, kPrintTimeFlag, &GTEST_FLAG(print_time)) ||
+diff -Naur gtest-1.5.0/src/gtest_main.cc gtest-1.5.0.nacl/src/gtest_main.cc
+--- gtest-1.5.0/src/gtest_main.cc 2010-04-15 16:02:04.000000000 -0600
++++ gtest-1.5.0.nacl/src/gtest_main.cc 2010-07-08 10:14:13.000000000 -0600
+@@ -33,7 +33,6 @@
+
+ int main(int argc, char **argv) {
+ std::cout << "Running main() from gtest_main.cc\n";
+-
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+ }
diff --git a/native_client_sdk/src/build_tools/install_nsis.py b/native_client_sdk/src/build_tools/install_nsis.py
new file mode 100644
index 0000000..9400347
--- /dev/null
+++ b/native_client_sdk/src/build_tools/install_nsis.py
@@ -0,0 +1,155 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Install the NSIS compiler and its SDK."""
+
+import os
+import shutil
+import subprocess
+import zipfile
+
+
+# The name of the archive that contains the AccessControl extensions.
+ACCESS_CONTROL_ZIP = 'AccessControl.zip'
+# The AccessControl plugin. The installer check for this before installing.
+ACCESS_CONTROL_DLL = 'AccessControl.dll'
+# The name of the MkLnk extension DLL. This is checked into the SDK repo.
+MKLINK_DLL = os.path.join('MkLink', 'Release Unicode', 'MkLink.dll')
+# The NSIS compiler. The installer checks for this before installing.
+NSIS_COMPILER = 'makensis.exe'
+# The default directory name for the NSIS installation.
+NSIS_DIR = 'NSIS'
+# The name of the NSIS installer. This file is checked into the SDK repo.
+NSIS_INSTALLER = 'nsis-2.46-Unicode-setup.exe'
+
+def MakeDirsIgnoreExist(dir_path, mode=0755):
+ '''Recursively make a directory path.
+
+ Recursively makes all the paths in |dir_path|. If |dir_path| already exists,
+ do nothing.
+
+ Args:
+ dir_path: A directory path. All necessary comonents of this path are
+ created.
+ mode: The mode to use if creating |dir_path|. Default is 0755.
+ '''
+ if not os.path.exists(dir_path):
+ os.makedirs(dir_path, mode=mode)
+
+
+def InstallNsis(installer_exe, target_dir, force=False):
+ '''Install NSIS into |target_dir|.
+
+ Args:
+ installer_exe: The full path to the NSIS self-extracting installer.
+
+ target_dir: The target directory for NSIS. The installer is run in this
+ directory and produces a directory named NSIS that contains the NSIS
+ compiler, etc. Must be defined.
+
+ force: Whether or not to force an installation.
+ '''
+ if not os.path.exists(installer_exe):
+ raise IOError('%s not found' % installer_exe)
+
+ if not os.path.isabs(installer_exe):
+ raise ValueError('%s must be an absolute path' % installer_exe)
+ if not os.path.isabs(target_dir):
+ raise ValueError('%s must be an absolute path' % target_dir)
+
+ if force or not os.path.exists(os.path.join(target_dir, NSIS_COMPILER)):
+ MakeDirsIgnoreExist(target_dir)
+ subprocess.check_call([installer_exe,
+ '/S',
+ '/D=%s' % target_dir],
+ cwd=os.path.dirname(installer_exe),
+ shell=True)
+
+
+def InstallAccessControlExtensions(cwd,
+ access_control_zip,
+ target_dir,
+ force=False):
+ '''Install the AccessControl extensions into the NSIS directory.
+
+ Args:
+ cwd: The current working directory.
+
+ access_control_zip: The full path of the AccessControl.zip file. The
+ contents of this file are extracted using python's zipfile package.
+
+ target_dir: The full path of the target directory for the AccessControl
+ extensions.
+
+ force: Whether or not to force an installation.
+ '''
+ if not os.path.exists(access_control_zip):
+ raise IOError('%s not found' % access_control_zip)
+
+ dst_plugin_dir = os.path.join(target_dir, 'Plugins')
+ if force or not os.path.exists(os.path.join(dst_plugin_dir,
+ ACCESS_CONTROL_DLL)):
+ MakeDirsIgnoreExist(dst_plugin_dir)
+ zip_file = zipfile.ZipFile(access_control_zip, 'r')
+ try:
+ zip_file.extractall(target_dir)
+ finally:
+ zip_file.close()
+ # Move the AccessControl plugin DLLs into the main NSIS Plugins directory.
+ access_control_plugin_dir = os.path.join(target_dir,
+ 'AccessControl',
+ 'Plugins')
+ access_control_plugins = [os.path.join(access_control_plugin_dir, p)
+ for p in os.listdir(access_control_plugin_dir)]
+ for plugin in access_control_plugins:
+ shutil.copy(plugin, dst_plugin_dir)
+
+
+def InstallMkLinkExtensions(mklink_dll, target_dir, force=False):
+ '''Install the AccessControl extensions into the NSIS directory.
+
+ Args:
+ mklink_dll: The full path of the MkLink.dll file.
+
+ target_dir: The full path of the target directory for the MkLink
+ extensions.
+
+ force: Whether or not to force an installation.
+ '''
+ if not os.path.exists(mklink_dll):
+ raise IOError('%s not found' % mklink_dll)
+
+ dst_plugin_dir = os.path.join(target_dir, 'Plugins')
+ dst_mklink_dll = os.path.join(dst_plugin_dir, os.path.basename(mklink_dll))
+ if force or not os.path.exists(dst_mklink_dll):
+ MakeDirsIgnoreExist(dst_plugin_dir)
+ # Copy the MkLink plugin DLLs into the main NSIS Plugins directory.
+ shutil.copy(mklink_dll, dst_mklink_dll)
+
+
+def Install(cwd, target_dir=None, force=False):
+ '''Install the entire NSIS compiler and SDK with extensions.
+
+ Installs the NSIS SDK and extensions into |target_dir|. By default, the
+ target directory is NSIS_DIR under |cwd|.
+
+ If NSIS is already installed and |force| is False, do nothing. If |force|
+ is True or NSIS is not already installed, then install NSIS and the necessary
+ extensions.
+
+ Args:
+ cwd: The current working directory.
+
+ target_dir: NSIS is installed here. If |target_dir| is None, then NSIS is
+ installed in its default location, which is NSIS_DIR under |cwd|.
+
+ force: True means install NSIS whether it already exists or not.
+ '''
+ # If the NSIS compiler and SDK hasn't been installed, do so now.
+ nsis_dir = target_dir or os.path.join(cwd, NSIS_DIR)
+ InstallNsis(os.path.join(cwd, NSIS_INSTALLER), nsis_dir, force=force)
+ InstallMkLinkExtensions(os.path.join(cwd, MKLINK_DLL), nsis_dir, force=force)
+ InstallAccessControlExtensions(
+ cwd, os.path.join(cwd, ACCESS_CONTROL_ZIP), nsis_dir, force=force)
diff --git a/native_client_sdk/src/build_tools/install_third_party.py b/native_client_sdk/src/build_tools/install_third_party.py
new file mode 100755
index 0000000..431a684
--- /dev/null
+++ b/native_client_sdk/src/build_tools/install_third_party.py
@@ -0,0 +1,78 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Build and install all the third-party tools and libraries required to build
+the SDK code. To add a script, add it to the array |THIRD_PARTY_SCRIPTS|.
+Before running the scripts, a couple of environment variables get set:
+ PYTHONPATH - append this script's dir to the search path for module import.
+ NACL_SDK_ROOT - forced to point to the root of this repo.
+"""
+
+import os
+import subprocess
+import sys
+
+from optparse import OptionParser
+
+# Append to PYTHONPATH in this very non-compliant way so that this script can be
+# run from a DEPS hook, where the normal path rules don't apply.
+SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
+THIRD_PARTY_DIR = os.path.join(os.path.dirname(SCRIPT_DIR), 'third_party')
+SCONS_DIR = os.path.join(THIRD_PARTY_DIR, 'scons-2.0.1', 'engine')
+sys.path.append(SCRIPT_DIR)
+sys.path.append(SCONS_DIR)
+
+import build_utils
+
+
+THIRD_PARTY_SCRIPTS = [
+ os.path.join('install_boost', 'install_boost.py'),
+]
+
+
+def main(argv):
+ parser = OptionParser()
+ parser.add_option(
+ '-a', '--all-toolchains', dest='all_toolchains',
+ action='store_true',
+ help='Install into all available toolchains.')
+ (options, args) = parser.parse_args(argv)
+ if args:
+ print 'ERROR: invalid argument: %s' % str(args)
+ parser.print_help()
+ sys.exit(1)
+
+ python_paths = [SCRIPT_DIR, SCONS_DIR]
+ shell_env = os.environ.copy()
+ python_paths += [shell_env.get('PYTHONPATH', '')]
+ shell_env['PYTHONPATH'] = os.pathsep.join(python_paths)
+
+ # Force NACL_SDK_ROOT to point to the toolchain in this repo.
+ nacl_sdk_root = os.path.dirname(SCRIPT_DIR)
+ shell_env['NACL_SDK_ROOT'] = nacl_sdk_root
+
+ script_argv = [arg for arg in argv if not arg in ['-a', '--all-toolchains']]
+ if options.all_toolchains:
+ script_argv += [
+ '--toolchain=%s' % (
+ build_utils.NormalizeToolchain(base_dir=nacl_sdk_root,
+ arch='x86',
+ variant='glibc')),
+ '--toolchain=%s' % (
+ build_utils.NormalizeToolchain(base_dir=nacl_sdk_root,
+ arch='x86',
+ variant='newlib')),
+ '--third-party=%s' % THIRD_PARTY_DIR,
+ ]
+
+ for script in THIRD_PARTY_SCRIPTS:
+ print "Running install script: %s" % os.path.join(SCRIPT_DIR, script)
+ py_command = [sys.executable, os.path.join(SCRIPT_DIR, script)]
+ subprocess.check_call(py_command + script_argv, env=shell_env)
+
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/native_client_sdk/src/build_tools/installer_contents.py b/native_client_sdk/src/build_tools/installer_contents.py
new file mode 100755
index 0000000..cf26893
--- /dev/null
+++ b/native_client_sdk/src/build_tools/installer_contents.py
@@ -0,0 +1,181 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""White-list of directories and files that get installed in the SDK.
+
+Note that the list of directories is kept separate from the list of files
+because copying a directory is treated differently than copying files on
+Windows.
+"""
+
+import os
+import sys
+from nacl_sdk_scons import nacl_utils
+
+
+# For each path in this list, its entire contents are added to the SDK
+# installer. Directories are denoted by a trailing '/' - this is important
+# because they have to be treated separately on Windows for plain files (the
+# copy mechanism is different). Note that the path separator is hard-coded to
+# be '/'. When accessing these lists, use the Get*() functions, which replace
+# the '/' with the correct platform-specific path separator as defined by
+# os.path.
+INSTALLER_CONTENTS = [
+ 'build_tools/nacl_sdk_scons/make_nacl_env.py',
+ 'build_tools/nacl_sdk_scons/nacl_utils.py',
+ 'build_tools/nacl_sdk_scons/site_tools/',
+ 'examples/build.scons',
+ 'examples/common/',
+ 'examples/favicon.ico',
+ 'examples/geturl/',
+ 'examples/hello_world/',
+ 'examples/hello_world_c/',
+ 'examples/httpd.py',
+ 'examples/index.html',
+ 'examples/index_staging.html',
+ 'examples/input_events/',
+ 'examples/load_progress/',
+ 'examples/mouselock/',
+ 'examples/multithreaded_input_events/',
+ 'examples/pi_generator/',
+ 'examples/pong/',
+ 'examples/scons',
+ 'examples/sine_synth/',
+ 'examples/tumbler/',
+ 'examples/fullscreen_tumbler/',
+ 'project_templates/README',
+ 'project_templates/c/',
+ 'project_templates/cc/',
+ 'project_templates/html/',
+ 'project_templates/init_project.py',
+ 'project_templates/scons',
+ 'project_templates/vs/',
+ 'third_party/scons-2.0.1/',
+]
+
+INSTALLER_CONTENTS.append('%s/' % nacl_utils.ToolchainPath(base_dir='',
+ variant='newlib'))
+INSTALLER_CONTENTS.append('%s/' % nacl_utils.ToolchainPath(base_dir='',
+ variant='glibc'))
+
+LINUX_ONLY_CONTENTS = [
+ 'third_party/ppapi/',
+]
+
+MAC_ONLY_CONTENTS = [
+ 'third_party/ppapi/',
+]
+
+WINDOWS_ONLY_CONTENTS = [
+ 'examples/httpd.cmd',
+ 'examples/scons.bat',
+ 'project_templates/scons.bat',
+ 'debugger/nacl-gdb_server/x64/Release/',
+ 'debugger/nacl-gdb_server/Release/',
+ 'debugger/nacl-bpad/x64/Release/'
+]
+
+# These files are user-readable documentation files, and as such get some
+# further processing on Windows (\r\n line-endings and .txt file suffix).
+# On non-Windows platforms, the installer content list is the union of
+# |INSTALLER_CONTENTS| and |DOCUMENTATION_FILES|.
+DOCUMENTATION_FILES = [
+ 'AUTHORS',
+ 'COPYING',
+ 'LICENSE',
+ 'NOTICE',
+ 'README',
+]
+
+
+def GetToolchainManifest(toolchain):
+ '''Get the toolchain manifest file.
+
+ These manifest files are used to create NSIS file sections for the
+ toolchains. The manifest files are considered the source of truth for
+ symbolic links and other filesystem-specific information that is not
+ discoverable using python in Windows.
+
+ Args:
+ toolchain: The toolchain variant. Currently supported values are 'newlib'
+ and 'glibc'.
+
+ Returns:
+ The os-specific path to the toolchain manifest file, relative to the SDK's
+ src directory.
+ '''
+ WINDOWS_TOOLCHAIN_MANIFESTS = {
+ 'newlib': 'naclsdk_win_x86.tgz.manifest',
+ 'glibc': 'toolchain_win_x86.tar.xz.manifest',
+ }
+ MAC_TOOLCHAIN_MANIFESTS = {
+ 'newlib': 'naclsdk_mac_x86.tgz.manifest',
+ 'glibc': 'toolchain_mac_x86.tar.bz2.manifest',
+ }
+ LINUX_TOOLCHAIN_MANIFESTS = {
+ 'newlib': 'naclsdk_linux_x86.tgz.manifest',
+ 'glibc': 'toolchain_linux_x86.tar.xz.manifest',
+ }
+ manifest_file = None
+ if sys.platform == 'win32':
+ manifest_file = WINDOWS_TOOLCHAIN_MANIFESTS[toolchain]
+ elif sys.platform == 'darwin':
+ manifest_file = MAC_TOOLCHAIN_MANIFESTS[toolchain]
+ elif sys.platform == 'linux2':
+ manifest_file = LINUX_TOOLCHAIN_MANIFESTS[toolchain]
+ if manifest_file:
+ return os.path.join('build_tools', 'toolchain_archives', manifest_file)
+ else:
+ return None
+
+
+def ConvertToOSPaths(path_list):
+ '''Convert '/' path separators to OS-specific path separators.
+
+ For each file in |path_list|, replace each occurence of the '/' path
+ separator to the OS-specific separator as defined by os.path.
+
+ Args:
+ path_list: A list of file paths that use '/' as the path sparator.
+
+ Returns:
+ A new list where each element represents the same file paths as in
+ |path_list|, but using the os-specific path separator.
+ '''
+ return [os.path.join(*path.split('/')) for path in path_list]
+
+
+def GetDirectoriesFromPathList(path_list):
+ '''Return a list of all the content directories.
+
+ The paths in the returned list are formatted to be OS-specific, and are
+ ready to be used in file IO operations.
+
+ Args:
+ path_list: A list of paths that use '/' as the path separator.
+
+ Returns:
+ A list of paths to be included in the SDK installer. The paths all have
+ OS-specific separators.
+ '''
+ return ConvertToOSPaths(
+ [dir for dir in path_list if dir.endswith('/')])
+
+
+def GetFilesFromPathList(path_list):
+ '''Return a list of all the content files.
+
+ The paths in the returned list are formatted to be OS-specific, and are
+ ready to be used in file IO operations.
+
+ Args:
+ path_list: A list of paths that use '/' as the path separator.
+
+ Returns:
+ A list of paths to be included in the SDK installer. The paths all have
+ OS-specific separators.
+ '''
+ return ConvertToOSPaths(
+ [dir for dir in path_list if not dir.endswith('/')])
diff --git a/native_client_sdk/src/build_tools/json/naclsdk_manifest.json b/native_client_sdk/src/build_tools/json/naclsdk_manifest.json
new file mode 100644
index 0000000..24d0a02
--- /dev/null
+++ b/native_client_sdk/src/build_tools/json/naclsdk_manifest.json
@@ -0,0 +1,193 @@
+{
+ "bundles": [
+ {
+ "name": "sdk_tools",
+ "description": "Native Client SDK Tools, revision 1.12",
+ "stability": "stable",
+ "recommended": "yes",
+ "version": 1,
+ "archives": [
+ {
+ "url": "http://commondatastorage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/1.12/sdk_tools.tgz",
+ "checksum": {
+ "sha1": "abb4c35ac08611e671acc825d1722c1792c94c8e"
+ },
+ "host_os": "mac",
+ "size": 29088
+ },
+ {
+ "url": "http://commondatastorage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/1.12/sdk_tools.tgz",
+ "checksum": {
+ "sha1": "abb4c35ac08611e671acc825d1722c1792c94c8e"
+ },
+ "host_os": "linux",
+ "size": 29088
+ },
+ {
+ "url": "http://commondatastorage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/1.12/sdk_tools.tgz",
+ "checksum": {
+ "sha1": "abb4c35ac08611e671acc825d1722c1792c94c8e"
+ },
+ "host_os": "win",
+ "size": 29088
+ }
+ ],
+ "revision": 12
+ },
+ {
+ "name": "gdb_builds",
+ "description": "gdb (Gnu Debugger) build for debugging x86-64 binary from win64 host",
+ "stability": "beta",
+ "recommended": "no",
+ "version": 0,
+ "archives": [
+ {
+ "url": "http://commondatastorage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/gdb_builds/0.1/gdb-win-builds.tgz",
+ "checksum": {
+ "sha1": "94c78870edcd50f95bb0db040252e766bb275c50"
+ },
+ "host_os": "win",
+ "size": 34727612
+ }
+ ],
+ "revision": 1
+ },
+ {
+ "name": "pepper_14",
+ "description": "Chrome 14 bundle, revision 1052",
+ "stability": "post_stable",
+ "recommended": "no",
+ "version": 14,
+ "archives": [
+ {
+ "url": "http://commondatastorage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/pepper_14_1052/naclsdk_mac.tgz",
+ "checksum": {
+ "sha1": "e4b12fcb2dde33ff8b56e9ac3b83830a46205880"
+ },
+ "host_os": "mac",
+ "size": 51242839
+ },
+ {
+ "url": "http://commondatastorage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/pepper_14_1052/naclsdk_linux.tgz",
+ "checksum": {
+ "sha1": "52e6dd6efbd060956ac62b20dc3ba4bf0d34bcca"
+ },
+ "host_os": "linux",
+ "size": 55819823
+ },
+ {
+ "url": "http://commondatastorage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/pepper_14_1052/naclsdk_win.exe",
+ "checksum": {
+ "sha1": "9451dae079d19c7c5b270a4557aeec5d964858b7"
+ },
+ "host_os": "win",
+ "size": 18970313
+ }
+ ],
+ "revision": 1052
+ },
+ {
+ "name": "pepper_15",
+ "description": "Chrome 15 bundle, revision 1239",
+ "stability": "stable",
+ "recommended": "yes",
+ "version": 15,
+ "archives": [
+ {
+ "url": "http://commondatastorage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/pepper_15_1239/naclsdk_mac.tgz",
+ "checksum": {
+ "sha1": "605888f7d0e1055dd0be9a7366b61467fcb74804"
+ },
+ "host_os": "mac",
+ "size": 149170725
+ },
+ {
+ "url": "http://commondatastorage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/pepper_15_1239/naclsdk_linux.tgz",
+ "checksum": {
+ "sha1": "9a118167b20fe7ada0c4cd735e7d7200970b9b01"
+ },
+ "host_os": "linux",
+ "size": 151245433
+ },
+ {
+ "url": "http://commondatastorage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/pepper_15_1239/naclsdk_win.exe",
+ "checksum": {
+ "sha1": "0843e07989a9cf4afebde75a4b15cfaf2804fd27"
+ },
+ "host_os": "win",
+ "size": 58275621
+ }
+ ],
+ "revision": 1239
+ },
+ {
+ "name": "pepper_16",
+ "description": "Chrome 16 bundle, revision 1364",
+ "stability": "beta",
+ "recommended": "no",
+ "version": 16,
+ "archives": [
+ {
+ "url": "http://commondatastorage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/pepper_16_1364/naclsdk_mac.tgz",
+ "checksum": {
+ "sha1": "4a3cbc1c4d590bb3ea05647e41637b19655006cf"
+ },
+ "host_os": "mac",
+ "size": 150305285
+ },
+ {
+ "url": "http://commondatastorage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/pepper_16_1364/naclsdk_linux.tgz",
+ "checksum": {
+ "sha1": "3d067c3c388081a6d561293ed1b7990fb3aac259"
+ },
+ "host_os": "linux",
+ "size": 151528839
+ },
+ {
+ "url": "http://commondatastorage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/pepper_16_1364/naclsdk_win.exe",
+ "checksum": {
+ "sha1": "687fad7b2b59812dbf65ca2808234a3d3caee350"
+ },
+ "host_os": "win",
+ "size": 66730335
+ }
+ ],
+ "revision": 1364
+ },
+ {
+ "name": "pepper_17",
+ "description": "Chrome 17 bundle, revision 1368",
+ "stability": "dev",
+ "recommended": "no",
+ "version": 17,
+ "archives": [
+ {
+ "url": "http://commondatastorage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/pepper_17_1368/naclsdk_mac.tgz",
+ "checksum": {
+ "sha1": "5639fbcf758d112fddcaec0475354a055015b950"
+ },
+ "host_os": "mac",
+ "size": 141618408
+ },
+ {
+ "url": "http://commondatastorage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/pepper_17_1368/naclsdk_linux.tgz",
+ "checksum": {
+ "sha1": "a400319ed68fb07184f4ab46f1b87df9bb07d49d"
+ },
+ "host_os": "linux",
+ "size": 140484500
+ },
+ {
+ "url": "http://commondatastorage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/pepper_17_1368/naclsdk_win.exe",
+ "checksum": {
+ "sha1": "2c30d26670969075d0106181c1406033b10ae3f7"
+ },
+ "host_os": "win",
+ "size": 70688802
+ }
+ ],
+ "revision": 1368
+ }
+ ],
+ "manifest_version": 1
+}
diff --git a/native_client_sdk/src/build_tools/make_nacl_tools.py b/native_client_sdk/src/build_tools/make_nacl_tools.py
new file mode 100755
index 0000000..834031f
--- /dev/null
+++ b/native_client_sdk/src/build_tools/make_nacl_tools.py
@@ -0,0 +1,213 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Build NaCl tools (e.g. sel_ldr and ncval) at a given revision."""
+
+import build_utils
+import optparse
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+
+bot = build_utils.BotAnnotator()
+
+
+# The suffix used for NaCl moduels that are installed, such as irt_core.
+NEXE_SUFFIX = '.nexe'
+
+def MakeInstallDirs(options):
+ '''Create the necessary install directories in the SDK staging area.
+ '''
+ install_dir = os.path.join(options.toolchain, 'bin');
+ if not os.path.exists(install_dir):
+ os.makedirs(install_dir)
+ runtime_dir = os.path.join(options.toolchain, 'runtime');
+ if not os.path.exists(runtime_dir):
+ os.makedirs(runtime_dir)
+
+
+def Build(options):
+ '''Build 32-bit and 64-bit versions of needed NaCL tools and libs.'''
+ nacl_dir = os.path.join(options.nacl_dir, 'native_client')
+ toolchain_option = 'naclsdk_mode=custom:%s' % options.toolchain
+ libc_option = '' if options.lib == 'newlib' else ' --nacl_glibc'
+ if sys.platform == 'win32':
+ scons = os.path.join(nacl_dir, 'scons.bat')
+ bits32 = 'vcvarsall.bat x86 && '
+ bits64 = 'vcvarsall.bat x86_amd64 && '
+ else:
+ scons = os.path.join(nacl_dir, 'scons')
+ bits32 = ''
+ bits64 = ''
+
+ # Build sel_ldr and ncval.
+ def BuildTools(prefix, bits, target):
+ cmd = '%s%s -j %s --mode=%s platform=x86-%s naclsdk_validate=0 %s %s%s' % (
+ prefix, scons, options.jobs, options.variant, bits, target,
+ toolchain_option, libc_option)
+ bot.Run(cmd, shell=True, cwd=nacl_dir)
+
+ BuildTools(bits32, '32', 'sdl=none sel_ldr ncval')
+ BuildTools(bits64, '64', 'sdl=none sel_ldr ncval')
+
+ # Build irt_core, which is needed for running .nexes with sel_ldr.
+ def BuildIRT(bits):
+ cmd = '%s -j %s irt_core --mode=opt-host,nacl platform=x86-%s %s' % (
+ scons, options.jobs, bits, toolchain_option)
+ bot.Run(cmd, shell=True, cwd=nacl_dir)
+
+ # only build the IRT using the newlib chain. glibc does not support IRT.
+ if options.lib == 'newlib':
+ BuildIRT(32)
+ BuildIRT(64)
+
+ # Build and install untrusted libraries.
+ def BuildAndInstallLibsAndHeaders(bits):
+ cmd = ('%s install --mode=opt-host,nacl libdir=%s includedir=%s '
+ 'platform=x86-%s force_sel_ldr=none %s%s') % (
+ scons,
+ os.path.join(options.toolchain,
+ 'x86_64-nacl',
+ 'lib32' if bits == 32 else 'lib'),
+ os.path.join(options.toolchain, 'x86_64-nacl', 'include'),
+ bits,
+ toolchain_option,
+ libc_option)
+ bot.Run(cmd, shell=True, cwd=nacl_dir)
+
+ BuildAndInstallLibsAndHeaders(32)
+ BuildAndInstallLibsAndHeaders(64)
+
+
+def Install(options, tools=[], runtimes=[]):
+ '''Install the NaCl tools and runtimes into the SDK staging area.
+
+ Assumes that all necessary artifacts are built into the NaCl scons-out/staging
+ directory, and copies them from there into the SDK staging area under
+ toolchain.
+
+ Args:
+ options: The build options object. This is populated from command-line
+ args at start-up.
+ tools: A list of tool names, these should *not* have any executable
+ suffix - this utility adds that (e.g. '.exe' on Windows).
+ runtimes: A list of IRT runtimes. These artifacts should *not* have any
+ suffix attached - this utility adds the '.nexe' suffix along with an
+ ISA-specific string (e.g. '_x86_32').
+ '''
+ # TODO(bradnelson): add an 'install' alias to the main build for this.
+ nacl_dir = os.path.join(options.nacl_dir, 'native_client')
+ tool_build_path_32 = os.path.join(nacl_dir,
+ 'scons-out',
+ '%s-x86-32' % (options.variant),
+ 'staging')
+ tool_build_path_64 = os.path.join(nacl_dir,
+ 'scons-out',
+ '%s-x86-64' % (options.variant),
+ 'staging')
+
+ for nacl_tool in tools:
+ shutil.copy(os.path.join(tool_build_path_32,
+ '%s%s' % (nacl_tool, options.exe_suffix)),
+ os.path.join(options.toolchain,
+ 'bin',
+ '%s_x86_32%s' % (nacl_tool, options.exe_suffix)))
+ shutil.copy(os.path.join(tool_build_path_64,
+ '%s%s' % (nacl_tool, options.exe_suffix)),
+ os.path.join(options.toolchain,
+ 'bin',
+ '%s_x86_64%s' % (nacl_tool, options.exe_suffix)))
+
+ irt_build_path_32 = os.path.join(nacl_dir,
+ 'scons-out',
+ 'nacl_irt-x86-32',
+ 'staging')
+ irt_build_path_64 = os.path.join(nacl_dir,
+ 'scons-out',
+ 'nacl_irt-x86-64',
+ 'staging')
+ for nacl_irt in runtimes:
+ shutil.copy(os.path.join(irt_build_path_32,
+ '%s%s' % (nacl_irt, NEXE_SUFFIX)),
+ os.path.join(options.toolchain,
+ 'runtime',
+ '%s_x86_32%s' % (nacl_irt, NEXE_SUFFIX)))
+ shutil.copy(os.path.join(irt_build_path_64,
+ '%s%s' % (nacl_irt, NEXE_SUFFIX)),
+ os.path.join(options.toolchain,
+ 'runtime',
+ '%s_x86_64%s' % (nacl_irt, NEXE_SUFFIX)))
+
+
+def BuildNaClTools(options):
+ if(options.clean):
+ bot.Print('Removing scons-out')
+ scons_out = os.path.join(options.nacl_dir, 'native_client', 'scons-out')
+ build_utils.CleanDirectory(scons_out)
+ else:
+ MakeInstallDirs(options)
+ Build(options)
+ Install(options, tools=['sel_ldr', 'ncval'], runtimes=['irt_core'])
+ return 0
+
+
+def main(argv):
+ if sys.platform in ['win32', 'cygwin']:
+ exe_suffix = '.exe'
+ else:
+ exe_suffix = ''
+
+ script_dir = os.path.abspath(os.path.dirname(__file__))
+
+ parser = optparse.OptionParser()
+ parser.add_option(
+ '-t', '--toolchain', dest='toolchain',
+ default='toolchain',
+ help='where to put the NaCl tool binaries')
+ parser.add_option(
+ '-l', '--lib', dest='lib',
+ default='newlib',
+ help='whether to build against newlib (default) or glibc')
+ parser.add_option(
+ '-c', '--clean', action='store_true', dest='clean',
+ default=False,
+ help='whether to clean up the checkout files')
+ parser.add_option(
+ '-j', '--jobs', dest='jobs', default='1',
+ help='Number of parallel jobs to use while building nacl tools')
+ parser.add_option(
+ '-n', '--nacl_dir', dest='nacl_dir',
+ default=os.path.join(script_dir, 'packages', 'native_client'),
+ help='Location of Native Client repository used for building tools')
+ (options, args) = parser.parse_args(argv)
+ if args:
+ parser.print_help()
+ bot.Print('ERROR: invalid argument(s): %s' % args)
+ return 1
+
+ options.toolchain = os.path.abspath(options.toolchain)
+ options.exe_suffix = exe_suffix
+ # Pick variant.
+ if sys.platform in ['win32', 'cygwin']:
+ variant = 'dbg-win'
+ elif sys.platform == 'darwin':
+ variant = 'dbg-mac'
+ elif sys.platform in ['linux', 'linux2']:
+ variant = 'dbg-linux'
+ else:
+ assert False
+ options.variant = variant
+
+ if options.lib not in ['newlib', 'glibc']:
+ bot.Print('ERROR: --lib must either be newlib or glibc')
+ return 1
+
+ return BuildNaClTools(options)
+
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/native_client_sdk/src/build_tools/make_nsis_installer.py b/native_client_sdk/src/build_tools/make_nsis_installer.py
new file mode 100644
index 0000000..38035bd
--- /dev/null
+++ b/native_client_sdk/src/build_tools/make_nsis_installer.py
@@ -0,0 +1,58 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Create the NSIS installer for the SDK."""
+
+import os
+import subprocess
+
+from build_tools import build_utils
+from build_tools import install_nsis
+from build_tools import nsis_script
+
+# TODO(dspringer): |toolchain_manifests| is not currently used by any callers.
+def MakeNsisInstaller(installer_dir,
+ sdk_version=None,
+ cwd=None,
+ toolchain_manifests=None):
+ '''Create the NSIS installer
+
+ Args:
+ installer_dir: The directory containing all the artifacts that get packaged
+ in the NSIS installer.
+
+ sdk_version: A string representing the SDK version. The string is expected
+ to be a '.'-separated triple representing <major>.<minor>.<build>. If
+ this argument is None, then the default is the value returned by
+ build_utils.RawVersion()
+
+ cwd: The current working directory. Various artifacts (such as the NSIS
+ installer) are expected to be in this directory. Defaults to the
+ script's directory.
+
+ toolchain_manifests: A dictionary of manifests for things in the
+ toolchain directory. The dictionary can have these keys:
+ 'files': a set of plain files
+ 'dirs': a set of directories
+ 'symlinks': a dictionary of symbolic links
+ 'links': a dictionary of hard links.
+ For more details on these sets and dictionaries, please see the
+ tar_archive module.
+ '''
+ if not sdk_version:
+ sdk_version = build_utils.RawVersion()
+ sdk_full_name = 'native_client_sdk_%s' % sdk_version.replace('.', '_')
+
+ if not cwd:
+ cwd = os.path.abspath(os.path.dirname(__file__))
+
+ install_nsis.Install(cwd)
+ script = nsis_script.NsisScript(os.path.join(cwd, 'make_sdk_installer.nsi'))
+ script.install_dir = os.path.join('C:%s' % os.sep, sdk_full_name)
+ script.InitFromDirectory(installer_dir)
+ if toolchain_manifests:
+ toolchain_manifests.PrependPath(installer_dir)
+ script |= toolchain_manifests
+ script.Compile()
diff --git a/native_client_sdk/src/build_tools/make_sdk_installer.nsi b/native_client_sdk/src/build_tools/make_sdk_installer.nsi
new file mode 100644
index 0000000..9cae0af
--- /dev/null
+++ b/native_client_sdk/src/build_tools/make_sdk_installer.nsi
@@ -0,0 +1,103 @@
+!include "FileFunc.nsh"
+!include "LogicLib.nsh"
+!include "MUI2.nsh"
+!include "Sections.nsh"
+!include "x64.nsh"
+
+RequestExecutionLevel user
+SetCompressor /solid lzma
+SetCompressorDictSize 128
+Name "Native Client SDK"
+OutFile ../../nacl-sdk.exe
+
+; The full SDK install name is generated from the version string.
+!include sdk_install_name.nsh
+
+Var SVV_CmdLineParameters
+Var SVV_SelChangeInProgress
+
+!define MUI_HEADERIMAGE
+!define MUI_WELCOMEFINISHPAGE_BITMAP \
+ "${NSISDIR}\Contrib\Graphics\Wizard\win.bmp"
+
+!define MUI_WELCOMEPAGE_TITLE "Welcome to the Native Client SDK Setup Wizard"
+!define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the \
+installation of the Native Client SDK $\r$\n$\r$\nThe Native Client SDK \
+includes a GNU toolchain adopted for Native Client use and some examples. You \
+need Google Chrome to test the examples.$\r$\n$\r$\nYou will also need to \
+install Python (please visit www.python.org/download)$\r$\n$\r$\n$_CLICK"
+
+!define MUI_COMPONENTSPAGE_SMALLDESC
+
+!insertmacro MUI_PAGE_WELCOME
+!insertmacro MUI_PAGE_COMPONENTS
+!insertmacro MUI_PAGE_DIRECTORY
+!insertmacro MUI_PAGE_INSTFILES
+
+!define MUI_FINISHPAGE_LINK \
+ "Visit the Native Client site for news, FAQs and support"
+!define MUI_FINISHPAGE_LINK_LOCATION \
+ "http://code.google.com/chrome/nativeclient"
+
+!define MUI_FINISHPAGE_SHOWREADME
+!define MUI_FINISHPAGE_SHOWREADME_TEXT "Show release notes"
+!define MUI_FINISHPAGE_SHOWREADME_FUNCTION ShowReleaseNotes
+
+!insertmacro MUI_PAGE_FINISH
+
+!insertmacro MUI_LANGUAGE "English"
+
+Section "" sec_Preinstall
+ Push $R0
+ CreateDirectory "$INSTDIR"
+ ; Owner can do anything
+ AccessControlW::GrantOnFile "$INSTDIR" "(S-1-3-0)" "FullAccess"
+ ; Group can read
+ AccessControlW::GrantOnFile "$INSTDIR" "(S-1-3-1)" "Traverse + GenericRead"
+ ; "Everyone" can read too
+ AccessControlW::GrantOnFile "$INSTDIR" "(S-1-1-0)" "Traverse + GenericRead"
+ FileClose $R0
+ Pop $R0
+SectionEnd
+
+; The SDK Section commands are in a generated file.
+!include sdk_section.nsh
+
+!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
+ !insertmacro MUI_DESCRIPTION_TEXT \
+ ${NativeClientSDK} \
+ "Native Client SDK - toolchain and examples"
+!insertmacro MUI_FUNCTION_DESCRIPTION_END
+
+Function .onInit
+ ${GetParameters} $SVV_CmdLineParameters
+ Push $R0
+ ClearErrors
+ ${GetOptions} $SVV_CmdLineParameters "/?" $R0
+ IfErrors +1 HelpMessage
+ ${GetOptions} $SVV_CmdLineParameters "--help" $R0
+ IfErrors +3 +1
+HelpMessage:
+ MessageBox MB_OK "Recognized common options:$\n \
+ /D=InstDir - use InstDir as target instead of usual $INSTDIR$\n \
+ /NCRC - disables the CRC check$\n \
+ /S - Silent install"
+ Abort
+ Pop $R0
+FunctionEnd
+
+Function .onSelChange
+ ${If} $SVV_SelChangeInProgress == 0
+ StrCpy $SVV_SelChangeInProgress 1
+ Push $R0
+ IntOp $R0 ${SF_SELECTED} | ${SF_BOLD}
+ SectionSetFlags ${NativeClientSDK} $R0
+ Pop $R0
+ StrCpy $SVV_SelChangeInProgress 0
+ ${EndIf}
+FunctionEnd
+
+Function ShowReleaseNotes
+ ExecShell "open" \
+ "http://code.google.com/chrome/nativeclient/docs/releasenotes.html"
+FunctionEnd
diff --git a/native_client_sdk/src/build_tools/make_sdk_tools.py b/native_client_sdk/src/build_tools/make_sdk_tools.py
new file mode 100755
index 0000000..7981883
--- /dev/null
+++ b/native_client_sdk/src/build_tools/make_sdk_tools.py
@@ -0,0 +1,135 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Create the base (auto)updater for the Native Client SDK"""
+
+import py_compile
+import optparse
+import os
+import shutil
+import sys
+import tarfile
+import zipfile
+
+from build_tools.sdk_tools import update_manifest
+from build_tools.sdk_tools import sdk_update
+
+NACL_SDK = 'nacl_sdk'
+
+
+def ZipDirectory(dirpath, zippath):
+ '''Create a zipfile from the contents of a given directory.
+
+ The path of the resulting contents in the zipfile will match that of the
+ last directory name in dirpath.
+
+ Args:
+ dirpath: Path to directory to add to zipfile
+ zippath: filename of resulting zipfile
+ '''
+ zip = None
+ try:
+ zip = zipfile.ZipFile(zippath, 'w', zipfile.ZIP_DEFLATED)
+ basedir = '%s%s' % (os.path.dirname(dirpath), os.sep)
+ for root, dirs, files in os.walk(dirpath):
+ if os.path.basename(root)[0] == '.':
+ continue # skip hidden directories
+ dirname = root.replace(basedir, '')
+ for file in files:
+ zip.write(os.path.join(root, file), os.path.join(dirname, file))
+ finally:
+ if zip:
+ zip.close()
+
+
+def WriteTarFile(outname, in_dir, tar_dir=''):
+ '''Create a new compressed tarball from a given directory
+
+ Args:
+ outname: path and filename of the gzipped tar file
+ in_dir: source directory that will be tar'd and gzipped
+ tar_dir: root directory within the tarball'''
+ tar_file = None
+ try:
+ tar_file = tarfile.open(outname, 'w:gz')
+ tar_file.add(in_dir, tar_dir)
+ finally:
+ if tar_file:
+ tar_file.close()
+
+
+def MakeSdkTools(nacl_sdk_filename, sdk_tools_filename):
+ '''Make the nacl_sdk and sdk_tools tarballs
+
+ The nacl_sdk package contains these things:
+
+ nacl_sdk/
+ naclsdk(.bat) - The main entry point for updating the SDK
+ sdk_tools/
+ sdk_update.py - Performs the work in checking for updates
+ python/
+ python.exe - (optional) python executable, shipped with Windows
+ ... - other python files and directories
+ sdk_cache/
+ naclsdk_manifest.json - manifest file with information about sdk_tools
+
+ Args:
+ nacl_sdk_filename: name of zipfile that the user directly downloads
+ sdk_tools_filename: name of tarball that has the sdk_tools directory
+ '''
+ base_dir = os.path.abspath(os.path.dirname(__file__))
+ base_dir_parent = os.path.dirname(base_dir)
+ temp_dir = os.path.join(base_dir, NACL_SDK)
+ if os.path.exists(temp_dir):
+ shutil.rmtree(temp_dir)
+ os.mkdir(temp_dir)
+ for dir in ['sdk_tools', 'sdk_cache']:
+ os.mkdir(os.path.join(temp_dir, dir))
+ shutil.copy2(os.path.join(base_dir, 'naclsdk'), temp_dir)
+ shutil.copy2(os.path.join(base_dir, 'naclsdk.bat'), temp_dir)
+ with open(os.path.join(base_dir_parent, 'LICENSE'), "U") as source_file:
+ text = source_file.read().replace("\n", "\r\n")
+ with open(os.path.join(temp_dir, 'sdk_tools', 'LICENSE'), "wb") as dest_file:
+ dest_file.write(text)
+
+ tool_list = ['sdk_update.py', 'set_nacl_env.py']
+ for tool in tool_list:
+ shutil.copy2(os.path.join(base_dir, 'sdk_tools', tool),
+ os.path.join(temp_dir, 'sdk_tools'))
+ py_compile.compile(os.path.join(temp_dir, 'sdk_tools', tool))
+
+ update_manifest_options = [
+ '--bundle-revision=%s' % sdk_update.MINOR_REV,
+ '--bundle-version=%s' % sdk_update.MAJOR_REV,
+ '--description=Native Client SDK Tools, revision %s.%s' % (
+ sdk_update.MAJOR_REV, sdk_update.MINOR_REV),
+ '--bundle-name=sdk_tools',
+ '--recommended=yes',
+ '--stability=stable',
+ '--manifest-version=%s' % sdk_update.SDKManifest().MANIFEST_VERSION,
+ '--manifest-file=%s' %
+ os.path.join(temp_dir, 'sdk_cache', 'naclsdk_manifest.json')]
+ if 0 != update_manifest.main(update_manifest_options):
+ raise Exception('update_manifest terminated abnormally.')
+ ZipDirectory(temp_dir, nacl_sdk_filename)
+ WriteTarFile(sdk_tools_filename, os.path.join(temp_dir, 'sdk_tools'), '')
+ shutil.rmtree(temp_dir, ignore_errors=True)
+
+
+def main(argv):
+ parser = optparse.OptionParser()
+ parser.add_option(
+ '-n', '--nacl-sdk', dest='nacl_sdk', default='nacl_sdk.zip',
+ help='name of the resulting nacl_sdk zipfile')
+ parser.add_option(
+ '-s', '--sdk-tools', dest='sdk_tools', default='sdk_tools.tgz',
+ help='name of the resulting sdk_tools tarball')
+ (options, args) = parser.parse_args(argv)
+ MakeSdkTools(options.nacl_sdk, options.sdk_tools)
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/native_client_sdk/src/build_tools/nacl_sdk_scons/__init__.py b/native_client_sdk/src/build_tools/nacl_sdk_scons/__init__.py
new file mode 100644
index 0000000..33dca08
--- /dev/null
+++ b/native_client_sdk/src/build_tools/nacl_sdk_scons/__init__.py
@@ -0,0 +1,10 @@
+#! -*- python -*-
+
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""build_tools/nacl_sdk_scons Package
+
+This package contains general python scons utilities that are used for
+creating the Native Client SDK."""
diff --git a/native_client_sdk/src/build_tools/nacl_sdk_scons/make_nacl_env.py b/native_client_sdk/src/build_tools/nacl_sdk_scons/make_nacl_env.py
new file mode 100644
index 0000000..acff50d
--- /dev/null
+++ b/native_client_sdk/src/build_tools/nacl_sdk_scons/make_nacl_env.py
@@ -0,0 +1,205 @@
+# -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''Construct an Environment that uses the NaCl toolchain to build C/C++ code.
+The base dir for the NaCl toolchain is in the NACL_SDK_ROOT environment
+variable.
+'''
+
+import nacl_utils
+import os
+
+from SCons import Script
+
+def NaClEnvironment(use_c_plus_plus_libs=False,
+ nacl_platform=None,
+ toolchain_arch=None,
+ toolchain_variant=None,
+ use_ppapi=True,
+ install_subdir=None,
+ lib_prefix=None):
+ '''Make an Environment that uses the NaCl toolchain to build sources.
+
+ This modifies a default construction Environment to point the compilers and
+ other bintools at the NaCl-specific versions, adds some tools that set certain
+ build flags needed by the NaCl-specific tools, and adds a custom Builder that
+ generates .nmf files.
+
+ Args:
+ use_c_plus_plus_libs: Indicate whether to insert the C++ NaCl libs at the
+ right place in the list of LIBS.
+ nacl_platform: The target NaCl/Chrome/Papper platform for which the
+ environment, e.g. 'pepper_14'.
+ toolchain_arch: The target architecture of the toolchain (e.g., x86, pnacl)
+ toolchain_variant: The libc of the toolchain (e.g., newlib, glibc)
+ use_ppapi: flag indicating whether to compile again ppapi libraries
+ install_subdir: subdirectory within the NACL_INSTALL_ROOT for this project.
+ lib_prefix: an optional list of path components to prepend to the library
+ path. These components are joined with appropriate path separators
+ Examples: ['..', '..'], ['..', 'peer_directory'].
+ Returns:
+ A SCons Environment with all the various Tool and keywords set to build
+ NaCl modules.
+ '''
+
+ def GetCommandLineOption(option_name, option_value, option_default):
+ '''Small helper function to get a command line option.
+
+ Returns a command-line option value, which can be overridden. If the
+ option is set on the command line, then that value is favoured over the
+ internally set value. If option is neither set on the command line nor
+ given a value, its default is used.
+
+ Args:
+ option_name: The name of the command line option, e.g. "variant".
+ option_value: The initial value of the option. This value is used if the
+ its not set via the command line. Can be None.
+ option_default: If the option value hasn't been set via the command line
+ nor via an internal value, then this default value is used. Can be
+ None.
+
+ Returns:
+ The value of the command-line option, according to the override rules
+ described above.
+ '''
+ cmd_line_value = Script.GetOption(option_name)
+ if not cmd_line_value and not option_value:
+ cmd_line_value = option_default
+ return cmd_line_value or option_value
+
+ nacl_utils.AddCommandLineOptions()
+ env = Script.Environment()
+
+ # We must have a nacl_platform, either as argument to this function or from
+ # the command line. However, if we're cleaning we can relax this requirement.
+ # (And our build bots will be much happier that way.)
+ nacl_platform_from_option = Script.GetOption('nacl_platform')
+ if not nacl_platform_from_option and not nacl_platform:
+ if Script.GetOption('clean'):
+ nacl_platform_from_option='.'
+ else:
+ raise ValueError('NaCl platform not specified')
+
+ # Setup the base dir for tools, etc. Favor the nacl platform specified on
+ # the command line if there's a conflict.
+ nacl_platform_to_use = nacl_platform_from_option or nacl_platform
+
+ toolchain_variant = GetCommandLineOption(
+ 'variant', toolchain_variant, nacl_utils.DEFAULT_TOOLCHAIN_VARIANT)
+ toolchain_arch = GetCommandLineOption(
+ 'architecture', toolchain_arch, nacl_utils.DEFAULT_TOOLCHAIN_ARCH)
+
+ base_dir = os.getenv('NACL_SDK_ROOT', '')
+ base_dir = os.path.join(base_dir, nacl_platform_to_use)
+ toolchain = nacl_utils.ToolchainPath(base_dir=base_dir,
+ arch=toolchain_arch,
+ variant=toolchain_variant)
+ if (toolchain is None):
+ raise ValueError('Cannot find a NaCl toolchain')
+
+ tool_bin_path = os.path.join(toolchain, 'bin')
+ tool_runtime_path = os.path.join(toolchain, 'runtime')
+ staging_dir = os.path.abspath(os.getenv(
+ 'NACL_INSTALL_ROOT', os.path.join(os.getenv('NACL_SDK_ROOT', '.'),
+ 'staging')))
+ if install_subdir:
+ staging_dir = os.path.join(staging_dir, install_subdir)
+ lib_prefix = lib_prefix or []
+ if type(lib_prefix) is not list:
+ # Break path down into list of directory components
+ lib_prefix = filter(lambda x:x, lib_prefix.split('/'))
+
+ # Invoke the various *nix tools that the NativeClient SDK resembles. This
+ # is done so that SCons doesn't try to invoke cl.exe on Windows in the
+ # Object builder.
+ env.Tool('g++')
+ env.Tool('gcc')
+ env.Tool('gnulink')
+ env.Tool('ar')
+ env.Tool('as')
+
+ env.Tool('nacl_tools')
+ # TODO(dspringer): Figure out how to make this dynamic and then compute it
+ # based on the desired target arch.
+ env.Replace(tools=['nacl_tools'],
+ # Replace the normal unix tools with the NaCl ones. Note the
+ # use of the NACL_ARCHITECTURE prefix for the tools. This
+ # Environment variable is set in nacl_tools.py; it has no
+ # default value.
+ CC=os.path.join(tool_bin_path, '${NACL_ARCHITECTURE}gcc'),
+ CXX=os.path.join(tool_bin_path, '${NACL_ARCHITECTURE}g++'),
+ AR=os.path.join(tool_bin_path, '${NACL_ARCHITECTURE}ar'),
+ AS=os.path.join(tool_bin_path, '${NACL_ARCHITECTURE}as'),
+ GDB=os.path.join(tool_bin_path, '${NACL_ARCHITECTURE}gdb'),
+ # NOTE: use g++ for linking so we can handle C AND C++.
+ LINK=os.path.join(tool_bin_path, '${NACL_ARCHITECTURE}g++'),
+ LD=os.path.join(tool_bin_path, '${NACL_ARCHITECTURE}ld'),
+ STRIP=os.path.join(tool_bin_path, '${NACL_ARCHITECTURE}strip'),
+ NACL_SEL_LDR32=os.path.join(tool_bin_path, 'sel_ldr_x86_32'),
+ NACL_IRT_CORE32=os.path.join(tool_runtime_path,
+ 'irt_core_x86_32.nexe'),
+ NACL_SEL_LDR64=os.path.join(tool_bin_path, 'sel_ldr_x86_64'),
+ NACL_IRT_CORE64=os.path.join(tool_runtime_path,
+ 'irt_core_x86_64.nexe'),
+ RANLIB=os.path.join(tool_bin_path, '${NACL_ARCHITECTURE}ranlib'),
+ ASFLAGS=['${EXTRA_ASFLAGS}',
+ ],
+ # c specific
+ EXTRA_CFLAGS=[],
+ CFLAGS=['${EXTRA_CFLAGS}',
+ '-std=gnu99',
+ ],
+ # c++ specific
+ EXTRA_CXXFLAGS=[],
+ CXXFLAGS=['${EXTRA_CXXFLAGS}',
+ '-std=gnu++98',
+ '-Wno-long-long',
+ ],
+ # Both C and C++
+ CCFLAGS=['${EXTRA_CCFLAGS}',
+ '-Wall',
+ '-Wswitch-enum',
+ '-pthread',
+ ],
+ CPPDEFINES=[# _GNU_SOURCE ensures that strtof() gets declared.
+ ('_GNU_SOURCE', 1),
+ # This ensures that PRId64 etc. get defined.
+ ('__STDC_FORMAT_MACROS', '1'),
+ # strdup, and other common stuff
+ ('_BSD_SOURCE', '1'),
+ ('_POSIX_C_SOURCE', '199506'),
+ ('_XOPEN_SOURCE', '600'),
+ ],
+ CPPPATH=[],
+ LINKFLAGS=['${EXTRA_LINKFLAGS}',
+ ],
+ # The NaCl environment makes '.nexe' executables. If this is
+ # not explicitly set, then SCons on Windows doesn't understand
+ # how to construct a Program builder properly.
+ PROGSUFFIX='.nexe',
+ # Target NaCl platform info.
+ TARGET_NACL_PLATFORM=nacl_platform_to_use,
+ NACL_TOOLCHAIN_VARIANT=toolchain_variant,
+ NACL_TOOLCHAIN_ROOT=toolchain,
+ NACL_INSTALL_ROOT=staging_dir,
+ NACL_LIB_PREFIX=lib_prefix,
+ )
+ # This supresses the "MS_DOS style path" warnings on Windows. It's benign on
+ # all other platforms.
+ env['ENV']['CYGWIN'] = 'nodosfilewarning'
+
+ # Append the common NaCl libs.
+ if use_ppapi:
+ common_nacl_libs = ['ppapi']
+ if use_c_plus_plus_libs:
+ common_nacl_libs.extend(['ppapi_cpp'])
+ env.Append(LIBS=common_nacl_libs)
+
+ gen_nmf_builder = env.Builder(suffix='.nmf',
+ action=nacl_utils.GenerateNmf)
+ env.Append(BUILDERS={'GenerateNmf': gen_nmf_builder})
+
+ return env
diff --git a/native_client_sdk/src/build_tools/nacl_sdk_scons/nacl_utils.py b/native_client_sdk/src/build_tools/nacl_sdk_scons/nacl_utils.py
new file mode 100644
index 0000000..3e012fa
--- /dev/null
+++ b/native_client_sdk/src/build_tools/nacl_sdk_scons/nacl_utils.py
@@ -0,0 +1,495 @@
+# -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''Small utility library of python functions used by the various helper
+scripts.
+'''
+# Needed for Python 2.5 -- Unnecessary (but harmless) for 2.6
+from __future__ import with_statement
+
+import optparse
+import os
+import sys
+
+from SCons import Script
+from site_tools import create_nmf
+
+#------------------------------------------------------------------------------
+# Parameters
+
+# The newlib toolchain variant. .nexes built with this variant require a
+# different manifest file format than those built with glibc.
+NEWLIB_TOOLCHAIN_VARIANT = 'newlib'
+
+# The default toolchain architecture.
+DEFAULT_TOOLCHAIN_ARCH = 'x86'
+
+# The default toolchain variant.
+DEFAULT_TOOLCHAIN_VARIANT = NEWLIB_TOOLCHAIN_VARIANT
+
+# Map the string stored in |sys.platform| into a toolchain host specifier.
+# @private
+__PLATFORM_TO_HOST_MAP = {
+ 'win32': 'windows',
+ 'cygwin': 'windows',
+ 'linux2': 'linux',
+ 'darwin': 'mac'
+}
+
+# Map a platform host to a map of possible architectures and toolchains.
+# The platform host can be derived from sys.platform using |PLATFORM_HOST_MAP_}|
+# (see above). (Note: if this map changes, you must also update the copy in
+# set_nacl_env.py.)
+# TODO(gwink): This map is duplicated in set_nacl_env.py. Find a good way to
+# share it instead.
+# @private
+__HOST_TO_TOOLCHAIN_MAP = {
+ 'windows': {
+ 'x86': {
+ 'glibc': 'win_x86',
+ 'newlib': 'win_x86_newlib',
+ },
+ },
+ 'linux': {
+ 'x86': {
+ 'glibc': 'linux_x86',
+ 'newlib': 'linux_x86_newlib',
+ },
+ 'pnacl': {
+ 'newlib-32': 'pnacl_linux_i686_newlib',
+ 'newlib-64': 'pnacl_linux_x86_64_newlib',
+ 'glibc-64': 'pnacl_linux_x86_64_glibc',
+ },
+ 'arm': {
+ 'newlib': 'pnacl_linux_x86_64_newlib',
+ 'glibc': 'pnacl_linux_x86_64_glibc',
+ },
+ },
+ 'mac': {
+ 'x86': {
+ 'glibc': 'mac_x86',
+ 'newlib': 'mac_x86_newlib',
+ },
+ 'pnacl': {
+ 'newlib': 'pnacl_darwin_i386_newlib',
+ },
+ },
+}
+
+# Various architecture spec objects suitable for use with
+# nacl_env_ext.SetArchFlags()
+ARCH_SPECS = {
+ 'x86-32': {
+ 'arch': 'x86',
+ 'subarch': '32'
+ },
+ 'x86-64': {
+ 'arch': 'x86',
+ 'subarch': '64'
+ },
+ 'arm': {
+ 'arch': 'ARM',
+ 'subarch': ''
+ }
+}
+
+# Default values for 'arch' and 'subarch'.
+DEFAULT_ARCH = 'x86'
+DEFAULT_SUBARCH = '32'
+
+#------------------------------------------------------------------------------
+# Functions
+
+def AddCommandLineOptions():
+ '''Register the cmd-line options.
+
+ Registers --nacl-platform, --architecture and --variant. This function can be
+ called multiple times. Only the first call registers the option.
+
+ Args:
+ None
+
+ Returns:
+ None
+ '''
+
+ try:
+ Script.AddOption(
+ '--nacl-platform',
+ dest='nacl_platform',
+ nargs=1,
+ type='string',
+ action='store',
+ help='target pepper version')
+ Script.Help(' --nacl-platform '
+ 'Specify the pepper version to build for '
+ '(e.g. --nacl-platform="pepper_14").\n')
+
+ Script.AddOption(
+ '--architecture',
+ dest='architecture',
+ nargs=1,
+ type='string',
+ action='store',
+ help='NaCl target architecture')
+ Script.Help(' --architecture '
+ 'Specify the NaCl target architecture to build (e.g. '
+ '--architecture="glibc"). Possible values are "x86", "pnacl" '
+ '"arm". Not all target architectures are available on all '
+ 'host platforms. Defaults to "x86"\n')
+
+ Script.AddOption(
+ '--variant',
+ dest='variant',
+ nargs=1,
+ type='string',
+ action='store',
+ help='NaCl toolchain variant')
+ Script.Help(' --variant '
+ 'Specify the NaCl toolchain variant to use when '
+ 'building (e.g. --variant="glibc"). Possible values are '
+ '"glibc", "newlib"; when --architecture=pnacl is specified, '
+ 'values must include bit-width, e.g. "glibc-64". Defaults to '
+ '"newlib"\n')
+
+ except optparse.OptionConflictError:
+ pass
+
+
+def PrintNaclPlatformBanner(module_name, nacl_platform, variant):
+ '''Print a banner that shows what nacl platform is used to build a module.
+
+ Args:
+ module_name: The name of the module. Printed as-is.
+ nacl_platform: The name - a.k.a. folder name - of the nacl platform.
+ Printed as-is.
+ variant: The toolchain variant, one of 'newlib', 'glibc', 'pnacl', etc.
+ Returns:
+ None
+ '''
+
+ # Don't print the banner if we're just cleaning files.
+ if not Script.GetOption('clean'):
+ print '---------------------------------------------------------------'
+ print ('+ Project "%s" is using NaCl platform "%s", toolchain "%s"' %
+ (module_name, nacl_platform, variant))
+ print '---------------------------------------------------------------'
+ print ''
+ sys.stdout.flush()
+
+
+def ToolchainPath(base_dir=None,
+ arch=DEFAULT_TOOLCHAIN_ARCH,
+ variant=DEFAULT_TOOLCHAIN_VARIANT):
+ '''Build a toolchain path based on the platform type.
+
+ |base_dir| is the root directory which includes the platform-specific
+ toolchain. This could be something like "/usr/local/mydir/nacl_sdk/src". If
+ |base_dir| is None, then the environment variable NACL_SDK_ROOT is used (if
+ it's set). This method assumes that the platform-specific toolchain is found
+ under <base_dir>/toolchain/<platform_spec>.
+
+ Args:
+ base_dir: The pathname of the root directory that contains the toolchain.
+ The toolchain is expected to be in a dir called 'toolchain'
+ within |base_dir|.
+ variant: The toolchain variant, can be one of 'newlib', 'glibc', etc.
+ Defaults to 'newlib'.
+ Returns:
+ The concatenated platform-specific path to the toolchain. This will look
+ like base_dir/toolchain/mac_x86
+ '''
+
+ if base_dir is None:
+ base_dir = os.getenv('NACL_SDK_ROOT', '')
+ if sys.platform in __PLATFORM_TO_HOST_MAP:
+ host_platform = __PLATFORM_TO_HOST_MAP[sys.platform]
+ toolchain_map = __HOST_TO_TOOLCHAIN_MAP[host_platform]
+ if arch in toolchain_map:
+ isa_toolchain = toolchain_map[arch]
+ if variant in isa_toolchain:
+ return os.path.normpath(os.path.join(
+ base_dir, 'toolchain', isa_toolchain[variant]))
+ else:
+ raise ValueError('ERROR: Variant "%s" not in toolchain "%s/%s".' %
+ (variant, host_platform, arch))
+ else:
+ raise ValueError('ERROR: Architecture "%s" not supported on host "%s".' %
+ (arch, host_platform))
+
+ else:
+ raise ValueError('ERROR: Unsupported host platform "%s".' % sys.platform)
+
+
+def GetJSONFromNexeSpec(nexe_spec):
+ '''Generate a JSON string that represents the architecture-to-nexe mapping
+ in |nexe_spec|.
+
+ The nexe spec is a simple dictionary, whose keys are architecture names and
+ values are the nexe files that should be loaded for the corresponding
+ architecture. For example:
+ {'x86-32': 'hello_world_x86_32.nexe',
+ 'x86-64': 'hello_world_x86_64.nexe',
+ 'arm': 'hello_world_ARM.nexe'}
+
+ Args:
+ nexe_spec: The dictionary that maps architectures to .nexe files.
+ Returns:
+ A JSON string representing |nexe_spec|.
+ '''
+ nmf_json = '{\n'
+ nmf_json += ' "program": {\n'
+
+ # Add an entry in the JSON for each specified architecture. Note that this
+ # loop emits a trailing ',' for every line but the last one.
+ if nexe_spec and len(nexe_spec):
+ line_count = len(nexe_spec)
+ for arch_key in nexe_spec:
+ line_count -= 1
+ eol_char = ',' if line_count > 0 else ''
+ nmf_json += ' "%s": {"url": "%s"}%s\n' % (arch_key,
+ nexe_spec[arch_key],
+ eol_char)
+
+ nmf_json += ' }\n'
+ nmf_json += '}\n'
+ return nmf_json
+
+
+def GenerateNmf(target, source, env):
+ '''This function is used to create a custom Builder that produces .nmf files.
+
+ The .nmf files are given in the list of targets. This expects the .nexe
+ mapping to be given as the value of the 'nexes' keyword in |env|. To add
+ this function as a Builder, use this SCons code:
+ gen_nmf_builder = nacl_env.Builder(suffix='.nmf',
+ action=nacl_utils.GenerateNmf)
+ nacl_env.Append(BUILDERS={'GenerateNmf': gen_nmf_builder})
+ To invoke the Builder, do this, for example:
+ # See examples/hello_world/build.scons for more details.
+ hello_world_opt_nexes = [nacl_env.NaClProgram(....), ....]
+ nacl_env.GenerateNmf(target='hello_world.nmf',
+ source=hello_world_opt_nexes,
+ nexes={'x86-32': 'hello_world_x86_32.nexe',
+ 'x86-64': 'hello_world_x86_64.nexe',
+ 'arm': 'hello_world_ARM.nexe'})
+
+ A Builder that invokes this function is added to the NaCl Environment by
+ the NaClEnvironment() function in make_nacl_env.py
+
+ Args:
+ target: The list of targets to build. This is expected to be a list of
+ File Nodes that point to the required .nmf files.
+ source: The list of sources that the targets depend on. This is typically
+ a list of File Nodes that represent .nexes
+ env: The SCons construction Environment that provides the build context.
+ Returns:
+ None on success. Raises a ValueError() if there are missing parameters,
+ such as the 'nexes' keyword in |env|.
+ '''
+
+ if target == None or source == None:
+ raise ValueError('No value given for target or source.')
+
+ nexes = env.get('nexes', [])
+ if len(nexes) == 0:
+ raise ValueError('No value for "nexes" keyword.')
+
+ for target_file in target:
+ # If any of the following functions raises an exception, just let the
+ # exception bubble up to the calling context. This will produce the
+ # correct SCons error.
+ target_path = target_file.get_abspath()
+ if env['NACL_TOOLCHAIN_VARIANT'] == NEWLIB_TOOLCHAIN_VARIANT:
+ nmf_json = GetJSONFromNexeSpec(nexes)
+ else:
+ nmf = create_nmf.NmfUtils(
+ objdump=os.path.join(env['NACL_TOOLCHAIN_ROOT'], 'bin',
+ 'x86_64-nacl-objdump'),
+ main_files=[str(file) for file in source],
+ lib_path=[os.path.join(env['NACL_TOOLCHAIN_ROOT'], 'x86_64-nacl', dir)
+ for dir in ['lib', 'lib32']],
+ lib_prefix=env['NACL_LIB_PREFIX'])
+ nmf_json = nmf.GetJson()
+ if env.get('NACL_INSTALL_ROOT', None):
+ nmf.StageDependencies(env['NACL_INSTALL_ROOT'])
+ with open(target_path, 'w') as nmf_file:
+ nmf_file.write(nmf_json)
+
+ # Return None to indicate success.
+ return None
+
+
+def GetArchFromSpec(arch_spec):
+ '''Pick out the values for 'arch' and 'subarch' from |arch_spec|, providing
+ default values in case nothing is specified.
+
+ Args:
+ arch_spec: An object that can have keys 'arch' and 'subarch'.
+ Returns:
+ A tuple (arch, subarch) that contains either the values of the
+ corresponding keys in |arch_spec| or a default value.
+ '''
+ if arch_spec == None:
+ return (DEFAULT_ARCH, DEFAULT_SUBARCH)
+ arch = arch_spec.get('arch', DEFAULT_ARCH)
+ subarch = arch_spec.get('subarch', DEFAULT_SUBARCH)
+ return (arch, subarch)
+
+
+def GetArchName(arch_spec):
+ ''' Return a name of the form arch_subarch for the given arch spec.
+
+ Args:
+ arch_spec: An object containing 'arch' and 'subarch' keys that describe
+ the instruction set architecture of the output program. See
+ |ARCH_SPECS| in nacl_utils.py for valid examples.
+
+ Returns:
+ A string with the arch name.
+ '''
+ return '%s_%s' % GetArchFromSpec(arch_spec)
+
+
+def MakeNaClCommonEnvironment(nacl_env,
+ arch_spec=ARCH_SPECS['x86-32'],
+ is_debug=False):
+ '''Make a clone of nacl_env that is suitable for building programs or
+ libraries for a specific build variant.
+
+ Make a cloned NaCl Environment and setup variables for options like optimized
+ versus debug CCFLAGS.
+
+ Args:
+ nacl_env: A SCons construction environment. This is typically the return
+ value of NaClEnvironment() (see above).
+ arch_spec: An object containing 'arch' and 'subarch' keys that describe
+ the instruction set architecture of the output program. See
+ |ARCH_SPECS| in nacl_utils.py for valid examples.
+ is_debug: Indicates whether this program should be built for debugging or
+ optimized.
+
+ Returns:
+ A SCons Environment setup with options for the specified variant of a NaCl
+ module or library.
+ '''
+ arch_name = GetArchName(arch_spec)
+ env = nacl_env.Clone()
+ env.AppendOptCCFlags(is_debug)
+ env.AppendArchFlags(arch_spec)
+
+ # Wrap linker command with TEMPFILE so that if lines are longer than
+ # MAXLINELENGTH, the tools will be run with @tmpfile. This isn't needed
+ # for any of the sdk examples, but if people cargo cult them for other
+ # purposes, they can end up hitting command line limits on Windows where
+ # MAXLINELENGTH can be as low as 2048.
+ env['LINKCOM'] = '${TEMPFILE("' + env['LINKCOM'] + '")}'
+ env['SHLINKCOM'] = '${TEMPFILE ' + env['SHLINKCOM'] + '")}'
+
+ return env
+
+
+def MakeNaClModuleEnvironment(nacl_env,
+ sources,
+ module_name='nacl',
+ arch_spec=ARCH_SPECS['x86-32'],
+ is_debug=False,
+ build_dir_prefix=''):
+ '''Make a NaClProgram Node for a specific build variant.
+
+ Make a NaClProgram Node in a cloned Environment. Set the environment
+ in the cloned Environment variables for things like optimized versus debug
+ CCFLAGS, and also adds a Program builder that makes a NaCl module. The name
+ of the module is derived from |module_name|, |arch_spec| and |is_debug|; for
+ example:
+ MakeNaClModuleEnvironment(nacl_env, sources, module_name='hello_world',
+ arch_spec=nacl_utils.ARCH_SPECS['x86-64'], is_debug=True)
+ will produce a NaCl module named
+ hello_world_x86_64_dbg.nexe
+
+ Args:
+ nacl_env: A SCons construction environment. This is typically the return
+ value of NaClEnvironment() (see above).
+ sources: A list of source Nodes used to build the NaCl module.
+ module_name: The name of the module. The name of the output program
+ incorporates this as its prefix.
+ arch_spec: An object containing 'arch' and 'subarch' keys that describe
+ the instruction set architecture of the output program. See
+ |ARCH_SPECS| in nacl_utils.py for valid examples.
+ is_debug: Indicates whether this program should be built for debugging or
+ optimized.
+ build_dir_prefix: Allows user to prefix the build directory with an
+ additional string.
+
+ Returns:
+ A SCons Environment that builds the specified variant of a NaCl module.
+ '''
+ debug_name = 'dbg' if is_debug else 'opt'
+ arch_name = GetArchName(arch_spec)
+ env = MakeNaClCommonEnvironment(nacl_env, arch_spec, is_debug)
+ return env.NaClProgram('%s_%s%s' % (module_name,
+ arch_name,
+ '_dbg' if is_debug else ''),
+ sources,
+ variant_dir='%s%s_%s' %
+ (build_dir_prefix, debug_name, arch_name))
+
+
+def MakeNaClStaticLibEnvironment(nacl_env,
+ sources,
+ lib_name='nacl',
+ arch_spec=ARCH_SPECS['x86-32'],
+ is_debug=False,
+ build_dir_prefix='',
+ lib_dir=''):
+ '''Make a NaClStaticLib Node for a specific build variant.
+
+ Make a NaClStaticLib Node in a cloned Environment. Set the environment
+ in the cloned Environment variables for things like optimized versus debug
+ CCFLAGS, and also adds a Program builder that makes a NaCl static library.
+ The name of the library is derived from |lib_name|, |arch_spec| and
+ |is_debug|; for example:
+ MakeNaClStaticLibEnvironment(nacl_env, sources, lib_name='c_salt',
+ arch_spec=nacl_utils.ARCH_SPECS['x86-64'], is_debug=True)
+ will produce a NaCl static library named
+ libc_salt_x86_64_dbg.a
+
+ Args:
+ nacl_env: A SCons construction environment. This is typically the return
+ value of NaClEnvironment() (see above).
+ sources: A list of source Nodes used to build the NaCl library.
+ lib_name: The name of the library.
+ arch_spec: An object containing 'arch' and 'subarch' keys that describe
+ the instruction set architecture of the output program. See
+ |ARCH_SPECS| in nacl_utils.py for valid examples.
+ is_debug: Indicates whether this program should be built for debugging or
+ optimized.
+ build_dir_prefix: Allows user to prefix the build directory with an
+ additional string.
+ lib_dir: Where to output the final library file. Lib files are placed in
+ directories within lib_dir, based on the arch type and build type.
+ E.g. Specifying lib_dir = 'dest' for lib name 'c_salt' on x86-32 debug
+ creates the library file 'dest/lib-x86-32/dbg/libc_salt.a'
+
+ Returns:
+ A SCons Environment that builds the specified variant of a NaCl module.
+ '''
+ env = MakeNaClCommonEnvironment(nacl_env, arch_spec, is_debug)
+ debug_name = 'dbg' if is_debug else 'opt'
+ arch_name = GetArchName(arch_spec)
+ target_name = '%s_%s%s' % (lib_name, arch_name, debug_name)
+ variant_dir = '%s%s_%s' % (build_dir_prefix, debug_name, arch_name)
+
+ arch, subarch = GetArchFromSpec(arch_spec)
+ lib_subdir = 'lib-' + arch
+ if subarch: lib_subdir = lib_subdir + '-' + subarch
+ lib_dir = os.path.join(lib_dir, lib_subdir, debug_name)
+
+ return env.NaClStaticLib(target_name,
+ sources,
+ variant_dir,
+ lib_name,
+ lib_dir)
diff --git a/native_client_sdk/src/build_tools/nacl_sdk_scons/nacl_utils_test.py b/native_client_sdk/src/build_tools/nacl_sdk_scons/nacl_utils_test.py
new file mode 100755
index 0000000..015f98f
--- /dev/null
+++ b/native_client_sdk/src/build_tools/nacl_sdk_scons/nacl_utils_test.py
@@ -0,0 +1,111 @@
+#!/usr/bin/python2.6
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Unit tests for nacl_utils.py."""
+
+import fileinput
+import mox
+import nacl_utils
+import os
+import sys
+import unittest
+
+
+def TestMock(file_path, open_func):
+ temp_file = open_func(file_path)
+ temp_file.close()
+
+
+class TestNaClUtils(unittest.TestCase):
+ """Class for test cases to cover globally declared helper functions."""
+
+ def setUp(self):
+ self.script_dir = os.path.abspath(os.path.dirname(__file__))
+ self.mock_factory = mox.Mox()
+ self.InitializeResourceMocks()
+
+ def InitializeResourceMocks(self):
+ """Can be called multiple times if multiple functions need to be tested."""
+ self.fileinput_mock = self.mock_factory.CreateMock(fileinput)
+ self.os_mock = self.mock_factory.CreateMock(os)
+ self.sys_mock = self.mock_factory.CreateMock(sys)
+
+ def testToolchainPath(self):
+ output = nacl_utils.ToolchainPath('nacl_sdk_root')
+ head, tail = os.path.split(output)
+ base, toolchain = os.path.split(head)
+ self.assertEqual('nacl_sdk_root', base)
+ self.assertEqual('toolchain', toolchain)
+ self.assertRaises(ValueError,
+ nacl_utils.ToolchainPath,
+ 'nacl_sdk_root',
+ arch='nosucharch')
+ self.assertRaises(ValueError,
+ nacl_utils.ToolchainPath,
+ 'nacl_sdk_root',
+ variant='nosuchvariant')
+
+ def testGetJSONFromNexeSpec(self):
+ valid_empty_json = '{\n "program": {\n }\n}\n'
+ null_json = nacl_utils.GetJSONFromNexeSpec(None)
+ self.assertEqual(null_json, valid_empty_json)
+ empty_json = nacl_utils.GetJSONFromNexeSpec({})
+ self.assertEqual(empty_json, valid_empty_json)
+ nexes = {'x86-32': 'nacl_x86_32.nexe',
+ 'x86-64': 'nacl_x86_64.nexe',
+ 'arm': 'nacl_ARM.nexe'}
+ json = nacl_utils.GetJSONFromNexeSpec(nexes)
+ # Assert that the resulting JSON has all the right parts: the "nexes"
+ # dict, followed by one entry for each architecture. Also make sure that
+ # the last entry doesn't have a trailing ','
+ json_lines = json.splitlines()
+ self.assertEqual(len(json_lines), 7)
+ self.assertEqual(json_lines[0], '{')
+ self.assertEqual(json_lines[1], ' "program": {')
+ self.assertTrue(json_lines[2].endswith(','))
+ self.assertTrue(json_lines[3].endswith(','))
+ self.assertFalse(json_lines[4].endswith(','))
+ self.assertEqual(json_lines[5], ' }')
+ self.assertEqual(json_lines[6], '}')
+ # Assert that the key-value pair lines have the right form. The order
+ # of the keys doesn't matter. Note that the key values are enclosed in
+ # "" (e.g. "x86-32") - this is intentional.
+ valid_arch_keys = ['"x86-32"', '"x86-64"', '"arm"']
+ for line in json_lines[2:4]:
+ key_value = line.split(':')
+ self.assertEqual(len(key_value), 3)
+ self.assertTrue(key_value[0].lstrip().rstrip() in valid_arch_keys)
+
+ def testGenerateNmf(self):
+ # Assert that failure cases properly fail.
+ self.assertRaises(ValueError, nacl_utils.GenerateNmf, None, None, None)
+ self.assertRaises(ValueError, nacl_utils.GenerateNmf, [], [], {})
+
+ def testGetArchFromSpec(self):
+ default_arch, default_subarch = nacl_utils.GetArchFromSpec(None)
+ self.assertEqual(default_arch, nacl_utils.DEFAULT_ARCH)
+ self.assertEqual(default_subarch, nacl_utils.DEFAULT_SUBARCH)
+ default_arch, subarch = nacl_utils.GetArchFromSpec({'subarch': '64'})
+ self.assertEqual(default_arch, nacl_utils.DEFAULT_ARCH)
+ self.assertEqual(subarch, '64')
+ arch, default_subarch = nacl_utils.GetArchFromSpec({'arch': 'x86'})
+ self.assertEqual(arch, 'x86')
+ self.assertEqual(default_subarch, nacl_utils.DEFAULT_SUBARCH)
+ arch, subarch = nacl_utils.GetArchFromSpec({'arch': 'x86', 'subarch': '64'})
+ self.assertEqual(arch, 'x86')
+ self.assertEqual(subarch, '64')
+
+
+def RunTests():
+ return_value = 1
+ test_suite = unittest.TestLoader().loadTestsFromTestCase(TestNaClUtils)
+ test_results = unittest.TextTestRunner(verbosity=2).run(test_suite)
+ if test_results.wasSuccessful():
+ return_value = 0
+ return return_value
+
+if __name__ == '__main__':
+ sys.exit(RunTests())
diff --git a/native_client_sdk/src/build_tools/nacl_sdk_scons/nmf_test.py b/native_client_sdk/src/build_tools/nacl_sdk_scons/nmf_test.py
new file mode 100644
index 0000000..4788890
--- /dev/null
+++ b/native_client_sdk/src/build_tools/nacl_sdk_scons/nmf_test.py
@@ -0,0 +1,196 @@
+#!/usr/bin/python2.6
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Unit tests for nmf.py."""
+
+import exceptions
+import json
+import optparse
+import os
+import subprocess
+import sys
+import tempfile
+import unittest
+
+from build_tools import build_utils
+from build_tools.nacl_sdk_scons.site_tools import create_nmf
+
+SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+X86_32 = 'x86-32'
+X86_64 = 'x86-64'
+ARCHS = [X86_32, X86_64]
+
+
+def CallCreateNmf(args):
+ '''Calls the create_nmf.py utility and returns stdout as a string
+
+ Args:
+ args: command-line arguments as a list (not including program name)
+
+ Returns:
+ string containing stdout
+
+ Raises:
+ subprocess.CalledProcessError: non-zero return code from sdk_update'''
+ command = [sys.executable, os.path.join(SCRIPT_DIR, 'site_tools',
+ 'create_nmf.py')] + args
+ process = subprocess.Popen(stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ args=command)
+ output, error_output = process.communicate()
+
+ retcode = process.poll() # Note - calling wait() can cause a deadlock
+ if retcode != 0:
+ print "\nCallCreateNmf(%s)" % command
+ print "stdout=%s\nstderr=%s" % (output, error_output)
+ sys.stdout.flush()
+ raise subprocess.CalledProcessError(retcode, command)
+ return output
+
+
+def GlobalInit(options, temp_files):
+ '''Global initialization for testing nmf file creation.
+
+ Args:
+ options: object containing gcc and gpp defines
+ files: (output) A list that will contain all generated nexes. Each
+ nexe is a value in a map, keyed by architecture.
+
+ Returns:
+ A list of filemaps, where each filemap is a dict with key=architecture
+ and value=filename
+ '''
+ hello_world_c = (
+ '#include <stdio.h>\n'
+ 'int main(void) { printf("Hello World!\\n"); return 0; }\n')
+ hello_world_cc = (
+ '#include <iostream>\n'
+ 'int main(void) { std::cout << "Hello World!\\n"; return 0; }\n')
+
+ files = []
+ def MakeNexe(contents, suffix, compiler):
+ file_map = {}
+ for arch in ARCHS:
+ source_filename = None
+ with tempfile.NamedTemporaryFile(delete=False,
+ suffix=suffix) as temp_file:
+ source_filename = temp_file.name
+ temp_files.append(source_filename)
+ temp_file.write(contents)
+ nexe_filename = os.path.splitext(source_filename)[0] + '.nexe'
+ file_map[arch] = nexe_filename
+ temp_files.append(nexe_filename)
+ subprocess.check_call([compiler,
+ '-m32' if arch == X86_32 else '-m64',
+ source_filename,
+ '-o', nexe_filename])
+ files.append(file_map)
+
+ c_file = MakeNexe(hello_world_c, '.c', options.gcc)
+ cc_file = MakeNexe(hello_world_cc, '.cc', options.gpp)
+ return files
+
+
+def TestingClosure(toolchain_dir, file_map):
+ '''Closure to provide variables to the test cases
+
+ Args:
+ toolchain_dir: path to toolchain that we are testing
+ file_map: dict of nexe files by architecture.
+ '''
+
+ class TestNmf(unittest.TestCase):
+ ''' Test basic functionality of the sdk_update package '''
+
+ def setUp(self):
+ self.objdump = (os.path.join(toolchain_dir,
+ 'bin',
+ 'x86_64-nacl-objdump'))
+ self.library_paths = [os.path.join(toolchain_dir, 'x86_64-nacl', lib)
+ for lib in ['lib32', 'lib']]
+ self.lib_options = ['--library-path=%s' % lib
+ for lib in self.library_paths]
+
+ def testRunCreateNmf(self):
+ json_text = CallCreateNmf(
+ self.lib_options + ['--objdump', self.objdump] + file_map.values())
+ obj = json.loads(json_text)
+ # For now, just do a simple sanity check that there is a file
+ # and program section.
+ self.assertTrue(obj.get('files'))
+ self.assertTrue(obj.get('program'))
+ for arch in ['x86-32', 'x86-64']:
+ self.assertEqual(obj['program'][arch]['url'],
+ '%s/runnable-ld.so' % arch)
+ self.assertEqual(obj['files']['main.nexe'][arch]['url'],
+ os.path.basename(file_map[arch]))
+ for filename, rest in obj['files'].items():
+ if filename == 'main.nexe':
+ continue
+ self.assertEqual('/'.join([arch, filename]),
+ rest[arch]['url'])
+
+ def testGenerateManifest(self):
+ nmf = create_nmf.NmfUtils(
+ objdump=self.objdump,
+ main_files=file_map.values(),
+ lib_path=self.library_paths)
+ nmf_json = nmf.GetManifest()
+ for arch in ['x86-32', 'x86-64']:
+ self.assertEqual(nmf_json['program'][arch]['url'],
+ '%s/runnable-ld.so' % arch)
+ self.assertEqual(nmf_json['files']['main.nexe'][arch]['url'],
+ os.path.basename(file_map[arch]))
+ for filename, rest in nmf_json['files'].items():
+ if filename == 'main.nexe':
+ continue
+ self.assertEqual('/'.join([arch, filename]),
+ rest[arch]['url'])
+
+ return TestNmf
+
+
+def GlobalTeardown(temp_files):
+ '''Remove all the temporary files in temp_files'''
+ for filename in temp_files:
+ if os.path.exists(filename):
+ os.remove(filename)
+
+
+def main(argv):
+ '''Usage: %prog [options]
+
+ Runs the unit tests on the nmf utility'''
+ parser = optparse.OptionParser(usage=main.__doc__)
+
+ parser.add_option(
+ '-t', '--toolchain-dir', dest='toolchain_dir',
+ help='(required) root directory of toolchain')
+
+ (options, args) = parser.parse_args(argv)
+
+ options.gcc = os.path.join(options.toolchain_dir, 'bin', 'x86_64-nacl-gcc')
+ options.gpp = os.path.join(options.toolchain_dir, 'bin', 'x86_64-nacl-g++')
+ options.objdump = os.path.join(options.toolchain_dir, 'bin',
+ 'x86_64-nacl-objdump')
+
+ success = True
+ temp_files = []
+ try:
+ nexe_maps = GlobalInit(options, temp_files)
+ for file_map in nexe_maps:
+ suite = unittest.TestLoader().loadTestsFromTestCase(
+ TestingClosure(toolchain_dir=options.toolchain_dir,
+ file_map=file_map))
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+ success = result.wasSuccessful() and success
+ finally:
+ GlobalTeardown(temp_files)
+
+ return int(not success) # 0 = success, 1 = failure
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/native_client_sdk/src/build_tools/nacl_sdk_scons/site_tools/__init__.py b/native_client_sdk/src/build_tools/nacl_sdk_scons/site_tools/__init__.py
new file mode 100644
index 0000000..33d78a0
--- /dev/null
+++ b/native_client_sdk/src/build_tools/nacl_sdk_scons/site_tools/__init__.py
@@ -0,0 +1,10 @@
+#! -*- python -*-
+
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""build_tools/nacl_sdk_scons/site_tools Package
+
+This package contains general python scons utilities that are used for
+creating the Native Client SDK."""
diff --git a/native_client_sdk/src/build_tools/nacl_sdk_scons/site_tools/create_nmf.py b/native_client_sdk/src/build_tools/nacl_sdk_scons/site_tools/create_nmf.py
new file mode 100755
index 0000000..e055809
--- /dev/null
+++ b/native_client_sdk/src/build_tools/nacl_sdk_scons/site_tools/create_nmf.py
@@ -0,0 +1,328 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from __future__ import with_statement
+
+import errno
+import optparse
+import os
+import re
+import shutil
+import subprocess
+import sys
+import urllib
+
+try:
+ import json
+except ImportError:
+ import simplejson as json
+
+NeededMatcher = re.compile('^ *NEEDED *([^ ]+)\n$')
+FormatMatcher = re.compile('^(.+):\\s*file format (.+)\n$')
+
+FORMAT_ARCH_MAP = {
+ # Names returned by Linux's objdump:
+ 'elf64-x86-64': 'x86-64',
+ 'elf32-i386': 'x86-32',
+ # Names returned by x86_64-nacl-objdump:
+ 'elf64-nacl': 'x86-64',
+ 'elf32-nacl': 'x86-32',
+ # TODO(mball): Add support for 'arm-32' and 'portable' architectures
+ # 'elf32-little': 'arm-32',
+ }
+
+# These constants are used within nmf files.
+RUNNABLE_LD = 'runnable-ld.so' # Name of the dynamic loader
+MAIN_NEXE = 'main.nexe' # Name of entry point for execution
+PROGRAM_KEY = 'program' # Key of the program section in an nmf file
+URL_KEY = 'url' # Key of the url field for a particular file in an nmf file
+FILES_KEY = 'files' # Key of the files section in an nmf file
+
+# The proper name of the dynamic linker, as kept in the IRT. This is
+# excluded from the nmf file by convention.
+LD_NACL_MAP = {
+ 'x86-32': 'ld-nacl-x86-32.so.1',
+ 'x86-64': 'ld-nacl-x86-64.so.1',
+}
+
+_debug_mode = False # Set to True to enable extra debug prints
+
+
+def DebugPrint(message):
+ if _debug_mode:
+ sys.stderr.write('%s\n' % message)
+ sys.stderr.flush()
+
+
+class Error(Exception):
+ '''Local Error class for this file.'''
+ pass
+
+
+class ArchFile(object):
+ '''Simple structure containing information about
+
+ Attributes:
+ arch: Architecture of this file (e.g., x86-32)
+ filename: name of this file
+ path: Full path to this file on the build system
+ url: Relative path to file in the staged web directory.
+ Used for specifying the "url" attribute in the nmf file.'''
+ def __init__(self, arch, name, path='', url=None):
+ self.arch = arch
+ self.name = name
+ self.path = path
+ self.url = url or '/'.join([arch, name])
+
+ def __str__(self):
+ '''Return the file path when invoked with the str() function'''
+ return self.path
+
+
+class NmfUtils(object):
+ '''Helper class for creating and managing nmf files
+
+ Attributes:
+ manifest: A JSON-structured dict containing the nmf structure
+ needed: A dict with key=filename and value=ArchFile (see GetNeeded)
+ '''
+
+ def __init__(self, main_files=None, objdump='x86_64-nacl-objdump',
+ lib_path=None, extra_files=None, lib_prefix=None):
+ ''' Constructor
+
+ Args:
+ main_files: List of main entry program files. These will be named
+ files->main.nexe for dynamic nexes, and program for static nexes
+ objdump: path to x86_64-nacl-objdump tool (or Linux equivalent)
+ lib_path: List of paths to library directories
+ extra_files: List of extra files to include in the nmf
+ lib_prefix: A list of path components to prepend to the library paths,
+ both for staging the libraries and for inclusion into the nmf file.
+ Examples: ['..'], ['lib_dir'] '''
+ self.objdump = objdump
+ self.main_files = main_files or []
+ self.extra_files = extra_files or []
+ self.lib_path = lib_path or []
+ self.manifest = None
+ self.needed = None
+ self.lib_prefix = lib_prefix or []
+
+ def GleanFromObjdump(self, files):
+ '''Get architecture and dependency information for given files
+
+ Args:
+ files: A dict with key=filename and value=list or set of archs. E.g.:
+ { '/path/to/my.nexe': ['x86-32', 'x86-64'],
+ '/path/to/libmy.so': ['x86-32'],
+ '/path/to/my2.nexe': None } # Indicates all architectures
+
+ Returns: A tuple with the following members:
+ input_info: A dict with key=filename and value=ArchFile of input files.
+ Includes the input files as well, with arch filled in if absent.
+ Example: { '/path/to/my.nexe': ArchFile(my.nexe),
+ '/path/to/libfoo.so': ArchFile(libfoo.so) }
+ needed: A set of strings formatted as "arch/name". Example:
+ set(['x86-32/libc.so', 'x86-64/libgcc.so'])
+ '''
+ DebugPrint("GleanFromObjdump(%s)" % ([self.objdump, '-p'] + files.keys()))
+ proc = subprocess.Popen([self.objdump, '-p'] + files.keys(),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, bufsize=-1)
+ input_info = {}
+ needed = set()
+ output, err_output = proc.communicate()
+ for line in output.splitlines(True):
+ # Objdump should display the architecture first and then the dependencies
+ # second for each file in the list.
+ matched = FormatMatcher.match(line)
+ if matched is not None:
+ filename = matched.group(1)
+ arch = FORMAT_ARCH_MAP[matched.group(2)]
+ if files[filename] is None or arch in files[filename]:
+ name = os.path.basename(filename)
+ input_info[filename] = ArchFile(
+ arch=arch,
+ name=name,
+ path=filename,
+ url='/'.join(self.lib_prefix + [arch, name]))
+ matched = NeededMatcher.match(line)
+ if matched is not None:
+ if files[filename] is None or arch in files[filename]:
+ needed.add('/'.join([arch, matched.group(1)]))
+ status = proc.poll()
+ if status != 0:
+ raise Error('%s\nStdError=%s\nobjdump failed with error code: %d' %
+ (output, err_output, status))
+ return input_info, needed
+
+ def FindLibsInPath(self, name):
+ '''Finds the set of libraries matching |name| within lib_path
+
+ Args:
+ name: name of library to find
+
+ Returns:
+ A list of system paths that match the given name within the lib_path'''
+ files = []
+ for dir in self.lib_path:
+ file = os.path.join(dir, name)
+ if os.path.exists(file):
+ files.append(file)
+ if not files:
+ raise Error('cannot find library %s' % name)
+ return files
+
+ def GetNeeded(self):
+ '''Collect the list of dependencies for the main_files
+
+ Returns:
+ A dict with key=filename and value=ArchFile of input files.
+ Includes the input files as well, with arch filled in if absent.
+ Example: { '/path/to/my.nexe': ArchFile(my.nexe),
+ '/path/to/libfoo.so': ArchFile(libfoo.so) }'''
+ if not self.needed:
+ DebugPrint('GetNeeded(%s)' % self.main_files)
+ examined = set()
+ all_files, unexamined = self.GleanFromObjdump(
+ dict([(file, None) for file in self.main_files]))
+ for name, arch_file in all_files.items():
+ arch_file.url = os.path.basename(name)
+ if unexamined:
+ unexamined.add('/'.join([arch_file.arch, RUNNABLE_LD]))
+ while unexamined:
+ files_to_examine = {}
+ for arch_name in unexamined:
+ arch, name = arch_name.split('/')
+ for path in self.FindLibsInPath(name):
+ files_to_examine.setdefault(path, set()).add(arch)
+ new_files, needed = self.GleanFromObjdump(files_to_examine)
+ all_files.update(new_files)
+ examined |= unexamined
+ unexamined = needed - examined
+ # With the runnable-ld.so scheme we have today, the proper name of
+ # the dynamic linker should be excluded from the list of files.
+ ldso = [LD_NACL_MAP[arch] for arch in set(FORMAT_ARCH_MAP.values())]
+ for name, arch_map in all_files.items():
+ if arch_map.name in ldso:
+ del all_files[name]
+ self.needed = all_files
+ return self.needed
+
+ def StageDependencies(self, destination_dir):
+ '''Copies over the dependencies into a given destination directory
+
+ Each library will be put into a subdirectory that corresponds to the arch.
+
+ Args:
+ destination_dir: The destination directory for staging the dependencies
+ '''
+ needed = self.GetNeeded()
+ for source, arch_file in needed.items():
+ destination = os.path.join(destination_dir,
+ urllib.url2pathname(arch_file.url))
+ try:
+ os.makedirs(os.path.dirname(destination))
+ except OSError as exception_info:
+ if exception_info.errno != errno.EEXIST:
+ raise
+ if (os.path.normcase(os.path.abspath(source)) !=
+ os.path.normcase(os.path.abspath(destination))):
+ shutil.copy2(source, destination)
+
+ def _GenerateManifest(self):
+ programs = {}
+ files = {}
+
+ def add_files(needed):
+ for filename, arch_file in needed.items():
+ files.setdefault(arch_file.arch, set()).add(arch_file.name)
+
+ needed = self.GetNeeded()
+ add_files(needed)
+
+ for filename in self.main_files:
+ arch_file = needed[filename]
+ programs[arch_file.arch] = arch_file.name
+
+ filemap = {}
+ for arch in files:
+ for file in files[arch]:
+ if file not in programs.values() and file != RUNNABLE_LD:
+ filemap.setdefault(file, set()).add(arch)
+
+ def arch_name(arch, file):
+ # nmf files expect unix-style path separators
+ return {URL_KEY: '/'.join(self.lib_prefix + [arch, file])}
+
+ # TODO(mcgrathr): perhaps notice a program with no deps
+ # (i.e. statically linked) and generate program=nexe instead?
+ manifest = {PROGRAM_KEY: {}, FILES_KEY: {MAIN_NEXE: {}}}
+ for arch in programs:
+ manifest[PROGRAM_KEY][arch] = arch_name(arch, RUNNABLE_LD)
+ manifest[FILES_KEY][MAIN_NEXE][arch] = {URL_KEY: programs[arch]}
+
+ for file in filemap:
+ manifest[FILES_KEY][file] = dict([(arch, arch_name(arch, file))
+ for arch in filemap[file]])
+ self.manifest = manifest
+
+ def GetManifest(self):
+ '''Returns a JSON-formatted dict containing the NaCl dependencies'''
+ if not self.manifest:
+ self._GenerateManifest()
+
+ return self.manifest
+
+ def GetJson(self):
+ '''Returns the Manifest as a JSON-formatted string'''
+ pretty_string = json.dumps(self.GetManifest(), indent=2)
+ # json.dumps sometimes returns trailing whitespace and does not put
+ # a newline at the end. This code fixes these problems.
+ pretty_lines = pretty_string.split('\n')
+ return '\n'.join([line.rstrip() for line in pretty_lines]) + '\n'
+
+
+def Main(argv):
+ parser = optparse.OptionParser(
+ usage='Usage: %prog [options] nexe [extra_libs...]')
+ parser.add_option('-o', '--output', dest='output',
+ help='Write manifest file to FILE (default is stdout)',
+ metavar='FILE')
+ parser.add_option('-D', '--objdump', dest='objdump', default='objdump',
+ help='Use TOOL as the "objdump" tool to run',
+ metavar='TOOL')
+ parser.add_option('-L', '--library-path', dest='lib_path',
+ action='append', default=[],
+ help='Add DIRECTORY to library search path',
+ metavar='DIRECTORY')
+ parser.add_option('-s', '--stage-dependencies', dest='stage_dependencies',
+ help='Destination directory for staging libraries',
+ metavar='DIRECTORY')
+ (options, args) = parser.parse_args(argv)
+
+ if len(args) < 1:
+ parser.print_usage()
+ sys.exit(1)
+
+ nmf = NmfUtils(objdump=options.objdump,
+ main_files=args,
+ lib_path=options.lib_path)
+
+ manifest = nmf.GetManifest()
+
+ if options.output is None:
+ sys.stdout.write(nmf.GetJson())
+ else:
+ with open(options.output, 'w') as output:
+ output.write(nmf.GetJson())
+
+ if options.stage_dependencies:
+ nmf.StageDependencies(options.stage_dependencies)
+
+
+# Invoke this file directly for simple testing.
+if __name__ == '__main__':
+ sys.exit(Main(sys.argv[1:]))
diff --git a/native_client_sdk/src/build_tools/nacl_sdk_scons/site_tools/nacl_tools.py b/native_client_sdk/src/build_tools/nacl_sdk_scons/site_tools/nacl_tools.py
new file mode 100644
index 0000000..5e50e9e
--- /dev/null
+++ b/native_client_sdk/src/build_tools/nacl_sdk_scons/site_tools/nacl_tools.py
@@ -0,0 +1,487 @@
+# -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''Extend the SCons Environment object with NaCl-specific builders and
+modifiers.
+'''
+
+from SCons import Script
+
+import nacl_utils
+import os
+import SCons
+
+def FilterOut(env, **kw):
+ """Removes values from existing construction variables in an Environment.
+
+ The values to remove should be a list. For example:
+
+ env.FilterOut(CPPDEFINES=['REMOVE_ME', 'ME_TOO'])
+
+ Args:
+ env: Environment to alter.
+ kw: (Any other named arguments are values to remove).
+ """
+
+ kw = SCons.Environment.copy_non_reserved_keywords(kw)
+
+ for key, val in kw.items():
+ if key in env:
+ # Filter out the specified values without modifying the original list.
+ # This helps isolate us if a list is accidently shared
+ # NOTE if env[key] is a UserList, this changes the type into a plain
+ # list. This is OK because SCons also does this in semi_deepcopy
+ env[key] = [item for item in env[key] if item not in val]
+
+ # TODO: SCons.Environment.Append() has much more logic to deal with various
+ # types of values. We should handle all those cases in here too. (If
+ # variable is a dict, etc.)
+
+def AppendOptCCFlags(env, is_debug=False):
+ '''Append a set of CCFLAGS that will build a debug or optimized variant
+ depending on the value of |is_debug|.
+
+ Uses optional build-specific flags for debug and optimized builds. To set
+ these in your build.scons files you can do something like this:
+ nacl_env.Append(DEBUG_CCFLAGS=['-gfull'],
+ OPT_CCFLAGS=['-ffast-math',
+ '-mfpmath=sse',
+ '-msse2'])
+
+ Args:
+ env: Environment to modify.
+ is_debug: Whether to set the option flags for debugging or not. Default
+ value is False.
+ '''
+
+ if is_debug:
+ env.Append(CCFLAGS=['${DEBUG_CCFLAGS}',
+ '-O0',
+ '-g',
+ ])
+ else:
+ env.Append(CCFLAGS=['${OPT_CCFLAGS}',
+ '-O3',
+ '-fno-stack-protector',
+ '-fomit-frame-pointer',
+ ])
+
+
+def AppendArchFlags(env, arch_spec):
+ '''Append a set of architecture-specific flags to the environment.
+
+ |arch_spec| is expected to be a map containing the keys "arch" and "subarch".
+ Supported keys are:
+ arch: x86
+ subarch: 32 | 64
+
+ Args:
+ env: Environment to modify.
+ arch_spec: A dictionary with keys describing the arch and subarch to build.
+ Possible values are: 'arch': x86; 'subarch': 32 or 64.
+ '''
+
+ arch, subarch = nacl_utils.GetArchFromSpec(arch_spec)
+ cc_arch_flags = ['-m%s' % subarch]
+ as_arch_flags = ['--%s' % subarch]
+ if subarch == '64':
+ ld_arch_flags = ['-m64']
+ env['NACL_ARCHITECTURE'] = 'x86_64-nacl-'
+ else:
+ ld_arch_flags = ['-m32']
+ env['NACL_ARCHITECTURE'] = 'i686-nacl-'
+ env.Append(ASFLAGS=as_arch_flags,
+ CCFLAGS=cc_arch_flags,
+ LINKFLAGS=ld_arch_flags)
+
+
+def NaClProgram(env, target, sources, variant_dir='obj'):
+ '''Add a Program to env that builds its objects in the directory specified
+ by |variant_dir|.
+
+ This is slightly different than VariantDir() in that the sources can live in
+ the same directory as the calling SConscript file.
+
+ Args:
+ env: Environment to modify.
+ target: The target name that depends on the object files. E.g.
+ "hello_world_x86_32.nexe"
+ sources: The list of source files that are used to build the objects.
+ variant_dir: The built object files are put in this directory. Default
+ value is "obj".
+
+ Returns:
+ The Program Node.
+ '''
+
+ program_objects = []
+ for src_file in sources:
+ obj_file = os.path.splitext(src_file)[0] + env.get('OBJSUFFIX', '.o')
+ program_objects.append(env.StaticObject(
+ target=os.path.join(variant_dir, obj_file), source=src_file))
+ env.Clean('.', variant_dir)
+ return env.Program(target, program_objects)
+
+
+def NaClTestProgram(env,
+ test_sources,
+ arch_spec,
+ module_name='nacl_test',
+ target_name='test'):
+ '''Modify |env| to include an Alias node for a test named |test_name|.
+
+ This node will build the desired NaCl module with the debug flags turned on.
+ The Alias node has a build action that runs the test under sel_ldr. |env| is
+ expected to have variables named 'NACL_SEL_LDR<x>', and 'NACL_IRT_CORE<x>'
+ where <x> is the various architectures supported (e.g. NACL_SEL_LDR32 and
+ NACL_SEL_LLDR64)
+
+ Args:
+ env: Environment to modify.
+ test_sources: The list of source files that are used to build the objects.
+ arch_spec: A dictionary with keys describing the arch and subarch to build.
+ Possible values are: 'arch': x86; 'subarch': 32 or 64.
+ module_name: The name of the module. The name of the output program
+ incorporates this as its prefix.
+ target_name: The name of the final Alias node. This name can be given on
+ the command line. For example:
+ nacl_env.NaClTestProgram(nacl_utils.ARCH_SPECS['x86-32'],
+ 'hello_world_test',
+ 'test32')
+ will let you say: ./scons test32 to build and run hello_world_test.
+ Returns:
+ A list of Nodes, one for each architecture-specific test.
+ '''
+
+ arch, subarch = nacl_utils.GetArchFromSpec(arch_spec)
+ # Create multi-level dictionary for sel_ldr binary name.
+ NACL_SEL_LDR = {'x86' :
+ {'32': '$NACL_SEL_LDR32',
+ '64': '$NACL_SEL_LDR64'
+ }
+ }
+ NACL_IRT_CORE = {'x86' :
+ {'32': '$NACL_IRT_CORE32',
+ '64': '$NACL_IRT_CORE64'
+ }
+ }
+ arch_sel_ldr = NACL_SEL_LDR[arch][subarch]
+ # if |arch| and |subarch| are not found, a KeyError exception will be
+ # thrown, which will generate a stack trace for debugging.
+ test_program = nacl_utils.MakeNaClModuleEnvironment(
+ env,
+ test_sources,
+ module_name,
+ arch_spec,
+ is_debug=True,
+ build_dir_prefix='test_')
+ test_node = env.Alias(target_name,
+ source=test_program,
+ action=arch_sel_ldr +
+ ' -B %s' % NACL_IRT_CORE[arch][subarch] +
+ ' $SOURCE')
+ # Tell SCons that |test_node| never goes out of date, so that you don't see
+ # '<test_node> is up to date.'
+ env.AlwaysBuild(test_node)
+
+
+def NaClStaticLib(env, target, sources, variant_dir='obj',
+ lib_name='', lib_dir=''):
+ '''Add a StaticLibrary to env that builds its objects in the directory
+ specified by |variant_dir|.
+
+ This is slightly different than VariantDir() in that the sources can live in
+ the same directory as the calling SConscript file.
+
+ Args:
+ env: Environment to modify.
+ target: The target name that depends on the object files. E.g.
+ "c_salt_x86_32" yields the target name libc_salt_x86_32.a.
+ sources: The list of source files that are used to build the objects.
+ variant_dir: The built object files are put in this directory. Default
+ value is "obj".
+ lib_name: The final name for the library. E.g. lib_name='c_salt' yields
+ the final library name libc_)salt.a. Defaults to the target name.
+ lib_dir: The final library file is placed in that directory. The directory
+ is created if it doesn't already exists. Default is '.'.
+
+ Returns:
+ The StaticLibrary Node.
+ '''
+
+ program_objects = []
+ for src_file in sources:
+ obj_file = os.path.splitext(src_file)[0] + env.get('OBJSUFFIX', '.o')
+ program_objects.append(env.StaticObject(
+ target=os.path.join(variant_dir, obj_file), source=src_file))
+ env.Clean('.', variant_dir)
+ lib_file = env.StaticLibrary(target, program_objects)
+
+ # If either a lib_name or lib_dir, we must move the library file to its
+ # final destination.
+ if lib_dir or lib_name:
+ # Map lib_name to an actual file name that includes the correct prefix and
+ # suffix, both extracted from the target library name generated by scons.
+ if lib_name:
+ final_lib_name = lib_file[0].name.replace(target, lib_name)
+ else:
+ final_lib_name = lib_file[0].name
+ # Add an action to move the file.
+ install_node = env.InstallAs(os.path.join(lib_dir, final_lib_name),
+ lib_file)
+ # Add lib_name as an alias. This is the target that will build and install
+ # the libraries.
+ if lib_name:
+ env.Alias(lib_name, install_node)
+
+ return lib_file
+
+
+def NaClStrippedInstall(env, dir='', source=None):
+ '''Strip the target node.
+
+ Args:
+ env: Environment to modify.
+ dir: The root install directory.
+ source: a list of a list of Nodes representing the executables to be
+ stripped.
+
+ Returns:
+ A list of Install Nodes that strip each buildable in |source|.
+ '''
+ stripped_install_nodes = []
+ if not source:
+ return stripped_install_nodes
+ for source_nodes in source:
+ for strip_node in source_nodes:
+ # Use the construction Environment used to create the buildable object.
+ # Each environment has various important properties, such as the target
+ # architecture and tools prefix.
+ strip_env = strip_node.get_env()
+ install_node = strip_env.Install(dir=strip_env['NACL_INSTALL_ROOT'],
+ source=strip_node)
+ strip_env.AddPostAction(install_node, "%s $TARGET" % strip_env['STRIP'])
+ stripped_install_nodes.append(install_node)
+
+ return stripped_install_nodes
+
+
+def NaClModules(env, sources, module_name, is_debug=False):
+ '''Produce one construction Environment for each supported instruction set
+ architecture.
+
+ Args:
+ env: Environment to modify.
+ sources: The list of source files that are used to build the objects.
+ module_name: The name of the module.
+ is_debug: Whether to set the option flags for debugging or not. Default
+ value is False.
+
+ Returns:
+ A list of SCons build Nodes, each one with settings specific to an
+ instruction set architecture.
+ '''
+ return [
+ nacl_utils.MakeNaClModuleEnvironment(
+ env,
+ sources,
+ module_name=module_name,
+ arch_spec=nacl_utils.ARCH_SPECS['x86-32'],
+ is_debug=is_debug),
+ nacl_utils.MakeNaClModuleEnvironment(
+ env,
+ sources,
+ module_name=module_name,
+ arch_spec=nacl_utils.ARCH_SPECS['x86-64'],
+ is_debug=is_debug),
+ ]
+
+
+def NaClStaticLibraries(env, sources, lib_name, is_debug=False, lib_dir=''):
+ '''Produce one static-lib construction Environment for each supported
+ instruction set architecture.
+
+ Args:
+ env: Environment to modify.
+ sources: The list of source files that are used to build the objects.
+ lib_name: The name of the static lib.
+ is_debug: Whether to set the option flags for debugging or not. Default
+ value is False.
+ lib_dir: Where to output the final library file. Lib files are placed in
+ directories within lib_dir, based on the arch type and build type.
+ E.g. Specifying lib_dir = 'dest' for lib name 'c_salt' on x86-32 debug
+ creates the library file 'dest/lib-x86-32/dbg/libc_salt.a'
+
+ Returns:
+ A list of SCons build Nodes, each one with settings specific to an
+ instruction set architecture.
+ '''
+ return [
+ nacl_utils.MakeNaClStaticLibEnvironment(
+ env,
+ sources,
+ lib_name=lib_name,
+ arch_spec=nacl_utils.ARCH_SPECS['x86-32'],
+ is_debug=is_debug,
+ lib_dir=lib_dir),
+ nacl_utils.MakeNaClStaticLibEnvironment(
+ env,
+ sources,
+ lib_name=lib_name,
+ arch_spec=nacl_utils.ARCH_SPECS['x86-64'],
+ is_debug=is_debug,
+ lib_dir=lib_dir),
+ ]
+
+
+def InstallPrebuilt(env, module_name):
+ '''Create the 'install_prebuilt' target.
+
+ install_prebuilt is used by the SDK build machinery to provide a prebuilt
+ version of the example in the SDK installer. This pseudo-builder adds an
+ Alias node called 'install_prebuilt' that depends on the main .nmf file of
+ the example. The .nmf file in turn has all the right dependencies to build
+ the necessary NaCl modules. As a final step, the opt variant directories
+ are removed. Once this build is done, the SDK builder can include the
+ example directory in its installer.
+
+ Args:
+ env: Environment to modify.
+ module_name: The name of the module.
+
+ Returns:
+ The Alias node representing the install_prebuilt target.
+ '''
+
+ return env.Alias('install_prebuilt',
+ source=['%s.nmf' % module_name],
+ action=Script.Delete([env.Dir('opt_x86_32'),
+ env.Dir('opt_x86_64')]))
+
+
+def AllNaClModules(env, sources, module_name):
+ '''Add a builder for both the debug and optimized variant of every supported
+ instruction set architecture.
+
+ Add one builder for each variant of the NaCl module, and also generate the
+ .nmf that loads the resulting NaCl modules. The .nmf file is named
+ |module_name|.nmf; similarly all other build products have |module_name| in
+ their name, e.g.
+ nacl_env.AllNaClModules(sources, module_name='hello_world')
+ produces these files, when x86-64 and x86-32 architectures are supported:
+ hello_world.nmf
+ hello_world_dbg.nmf
+ hello_world_x86_32.nexe
+ hello_world_x86_64.nexe
+ hello_world_x86_32_dbg.nexe
+ hello_world_x86_64_dbg.nexe
+ Object files go in variant directories named 'dbg_*' for debug builds and
+ 'opt_*' for optimized builds, where the * is a string describing the
+ architecture, e.g. 'x86_32'.
+
+ Args:
+ env: Environment to modify.
+ sources: The list of source files that are used to build the objects.
+ module_name: The name of the module.
+
+ Returns:
+ A 2-tuple of SCons Program nodes, the first element is the node that
+ builds optimized .nexes; the second builds the debug .nexes.
+ '''
+
+ opt_nexes = env.NaClModules(sources, module_name, is_debug=False)
+ env.GenerateNmf(target='%s.nmf' % module_name,
+ source=opt_nexes,
+ nexes={'x86-32': '%s_x86_32.nexe' % module_name,
+ 'x86-64': '%s_x86_64.nexe' % module_name})
+
+ dbg_nexes = env.NaClModules(sources, module_name, is_debug=True)
+ env.GenerateNmf(target='%s_dbg.nmf' % module_name,
+ source=dbg_nexes,
+ nexes={'x86-32': '%s_x86_32_dbg.nexe' % module_name,
+ 'x86-64': '%s_x86_64_dbg.nexe' % module_name})
+ nacl_utils.PrintNaclPlatformBanner(module_name,
+ nacl_platform=env['TARGET_NACL_PLATFORM'],
+ variant=env['NACL_TOOLCHAIN_VARIANT'])
+
+ return opt_nexes, dbg_nexes
+
+
+def AllNaClStaticLibraries(env, sources, lib_name, lib_dir=''):
+ '''Add a builder for both the debug and optimized variant of every supported
+ instruction set architecture.
+
+ Add one builder for each variant of the NaCl static library. The library
+ names have |lib_name| in their name, e.g.
+ nacl_env.AllNaClModules(sources, lib_name='c_salt')
+ produces these files, when x86-64 and x86-32 architectures are supported:
+ libc_salt_x86_32.a
+ libc_salt_x86_64.a
+ Object files go in variant directories named 'dbg_*' for debug builds and
+ 'opt_*' for optimized builds, where the * is a string describing the
+ architecture, e.g. 'x86_32'.
+
+ Args:
+ env: Environment to modify.
+ sources: The list of source files that are used to build the objects.
+ lib_name: The name of the static library.
+ lib_dir: Where to output the final library files. Lib files are placed in
+ directories within lib_dir, based on the arch type and build type.
+ E.g. Specifying lib_dir = 'dest' for lib name 'c_salt' on x-86-32 debug
+ creates the library file 'dest/lib-x86-32/dbg/libc_salt.a'
+
+ Returns:
+ A 2-tuple of SCons StaticLibrary nodes, the first element is the node that
+ builds optimized libs; the second builds the debug libs.
+ '''
+
+ opt_libs = env.NaClStaticLibraries(sources, lib_name, is_debug=False,
+ lib_dir=lib_dir)
+ dbg_libs = env.NaClStaticLibraries(sources, lib_name, is_debug=True,
+ lib_dir=lib_dir)
+ nacl_utils.PrintNaclPlatformBanner(lib_name,
+ nacl_platform=env['TARGET_NACL_PLATFORM'],
+ variant=env['NACL_TOOLCHAIN_VARIANT'])
+
+ return opt_libs, dbg_libs
+
+
+def generate(env):
+ '''SCons entry point for this tool.
+
+ Args:
+ env: The SCons Environment to modify.
+
+ NOTE: SCons requires the use of this name, which fails lint.
+ '''
+ nacl_utils.AddCommandLineOptions()
+
+ env.AddMethod(AllNaClModules)
+ env.AddMethod(AllNaClStaticLibraries)
+ env.AddMethod(AppendOptCCFlags)
+ env.AddMethod(AppendArchFlags)
+ env.AddMethod(FilterOut)
+ env.AddMethod(InstallPrebuilt)
+ env.AddMethod(NaClProgram)
+ env.AddMethod(NaClTestProgram)
+ env.AddMethod(NaClStaticLib)
+ env.AddMethod(NaClModules)
+ env.AddMethod(NaClStaticLibraries)
+ env.AddMethod(NaClStrippedInstall)
+
+
+def exists(env):
+ '''The NaCl tool is always valid. This is a required entry point for SCons
+ Tools.
+
+ Args:
+ env: The SCons Environment this tool will run in.
+
+ Returns:
+ Always returns True.
+ '''
+ return True
+
diff --git a/native_client_sdk/src/build_tools/naclsdk b/native_client_sdk/src/build_tools/naclsdk
new file mode 100755
index 0000000..dc48499
--- /dev/null
+++ b/native_client_sdk/src/build_tools/naclsdk
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+readonly SCRIPT_DIR="$(dirname "$0")"
+readonly SCRIPT_DIR_ABS="$(cd "${SCRIPT_DIR}" ; pwd -P)"
+readonly SDK_TOOLS="${SCRIPT_DIR_ABS}/sdk_tools"
+readonly SDK_TOOLS_UPDATE="${SCRIPT_DIR_ABS}/sdk_tools_update"
+
+if [[ -e "${SDK_TOOLS_UPDATE}" ]]; then
+ echo "Updating sdk_tools"
+ rm -rf "${SDK_TOOLS}"
+ mv "${SDK_TOOLS_UPDATE}" "${SDK_TOOLS}"
+fi
+
+export PYTHONPATH="${SDK_TOOLS}"
+python "${SDK_TOOLS}/sdk_update.py" "$@"
diff --git a/native_client_sdk/src/build_tools/naclsdk.bat b/native_client_sdk/src/build_tools/naclsdk.bat
new file mode 100755
index 0000000..ed24701
--- /dev/null
+++ b/native_client_sdk/src/build_tools/naclsdk.bat
@@ -0,0 +1,24 @@
+@echo off
+
+:: Copyright (c) 2011 The Native Client Authors. All rights reserved.
+:: Use of this source code is governed by a BSD-style license that can be
+:: found in the LICENSE file.
+
+setlocal
+
+set SCRIPT_DIR=%~dp0
+set SDK_TOOLS=%SCRIPT_DIR%sdk_tools
+set SDK_TOOLS_UPDATE=%SCRIPT_DIR%sdk_tools_update
+set PYTHON_DIR=%SCRIPT_DIR%python
+
+if exist %SDK_TOOLS_UPDATE% (
+ echo Updating sdk_tools
+ if exist %SDK_TOOLS% (
+ rmdir /q/s %SDK_TOOLS%
+ )
+ move %SDK_TOOLS_UPDATE% %SDK_TOOLS%
+)
+
+set PYTHONPATH=%SCRIPT_DIR%
+
+python %SDK_TOOLS%\sdk_update.py %*
diff --git a/native_client_sdk/src/build_tools/nsis_script.py b/native_client_sdk/src/build_tools/nsis_script.py
new file mode 100644
index 0000000..3ee026f
--- /dev/null
+++ b/native_client_sdk/src/build_tools/nsis_script.py
@@ -0,0 +1,189 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Class that represents an NSIS installer script."""
+
+import os
+import subprocess
+import tarfile
+import tempfile
+
+from build_tools import path_set
+
+
+class Error(Exception):
+ pass
+
+
+class NsisScript(path_set.PathSet):
+ '''Container for a NSIS script file
+
+ Use this class to create and manage an NSIS script. You can construct this
+ class with an existing script file. You can Compile() the script to produce
+ and NSIS installer.
+ '''
+
+ def __init__(self, script_file='nsis.nsi'):
+ path_set.PathSet.__init__(self)
+ self._script_file = script_file
+ self._install_dir = os.path.join('C:%s' % os.sep, 'nsis_install')
+ self._relative_install_root = None
+
+ @property
+ def script_file(self):
+ '''The output NSIS script file. Read-only.'''
+ return self._script_file
+
+ @property
+ def install_dir(self):
+ '''The default directory where the NSIS installer will unpack its contents.
+
+ This must be a full path, including the dirve letter. E.g.:
+ C:\native_client_sdk
+ '''
+ return self._install_dir
+
+ @install_dir.setter
+ def install_dir(self, new_install_dir):
+ if (os.path.isabs(new_install_dir) and
+ len(os.path.splitdrive(new_install_dir)[0]) > 0):
+ self._install_dir = new_install_dir
+ else:
+ raise Error('install_dir must be an absolute path')
+
+ def InitFromDirectory(self,
+ artifact_dir,
+ dir_filter=None,
+ file_filter=None):
+ '''Create the list of installer artifacts.
+
+ Creates three lists:
+ 1. a list of directories that need to be generated by the installer
+ 2. a list of files that get installed into those directories
+ 3. a list of symbolic links that need to be created by the installer
+ These lists are used later on when generating the section commands that are
+ compiled into the final installer
+
+ Args:
+ artifact_dir: The directory containing the files to add to the NSIS
+ installer script. The NSIS installer will reproduce this directory
+ structure.
+
+ dir_filter: A filter function for directories. This can be written as a
+ list comprehension. If dir_filter is not None, then it is called
+ with |_dirs|, and |_dirs| is replaced with the filter's
+ output.
+
+ file_filter: A filter function for files. This can be written as a
+ list comprehension. If file_filter is not None, then it is called
+ with |_files|, and |_files| is replaced with the filter's
+ output.
+ '''
+ self._relative_install_root = artifact_dir
+ self.Reset()
+ for root, dirs, files in os.walk(artifact_dir):
+ map(lambda dir: self._dirs.add(os.path.join(root, dir)), dirs)
+ map(lambda file: self._files.add(os.path.join(root, file)), files)
+ if dir_filter:
+ self._dirs = set(dir_filter(self._dirs))
+ if file_filter:
+ self._files = set(file_filter(self._files))
+
+ def CreateInstallNameScript(self, cwd='.'):
+ '''Write out the installer name script.
+
+ The installer name script is in the special NSIS include file
+ sdk_install_name.nsh. This file is expected to be in |cwd|. If
+ sdk_install_name.nsh already exists, it is overwritten.
+
+ Args:
+ cwd: The directory where sdk_install_name.sdk is placed.
+ '''
+ with open(os.path.join(cwd, 'sdk_install_name.nsh'), 'wb') as script:
+ script.write('InstallDir %s\n' % self._install_dir)
+
+ def NormalizeInstallPath(self, path):
+ '''Normalize |path| for installation.
+
+ If |_relative_install_root| is set, then normalize |path| by making it
+ relative to |_relative_install_root|.
+
+ Args:
+ path: The path to normalize.
+
+ Returns: the normalized path. If |_relative_install_root| is None, then
+ the return value is the same as |path|.
+ '''
+ if (self._relative_install_root and
+ path.startswith(self._relative_install_root)):
+ return path[len(self._relative_install_root) + 1:]
+ else:
+ return path
+
+ def CreateSectionNameScript(self, cwd='.'):
+ '''Write out the section script.
+
+ The section script is in the special NSIS include file sdk_section.nsh.
+ This file is expected to be in |cwd|. If sdk_section.nsh already exists,
+ it is overwritten.
+
+ If |_relative_install_root| is set, then that part of the path is stripped
+ off of the installed file and directory names. This will cause the files
+ to be installed as if they were relative to |_relative_install_root| and
+ not in an absolute location.
+
+ Args:
+ cwd: The directory where sdk_section.sdk is placed.
+ '''
+
+ def SymlinkType(symlink):
+ '''Return whether the source of symlink is a file or a directory.'''
+ symlink_basename = os.path.basename(symlink)
+ for file in self._files:
+ if os.path.basename(file) == symlink_basename:
+ return 'SoftF'
+ return 'SoftD'
+
+ with open(os.path.join(cwd, 'sdk_section.nsh'), 'wb') as script:
+ script.write('Section "!Native Client SDK" NativeClientSDK\n')
+ script.write(' SectionIn RO\n')
+ script.write(' SetOutPath $INSTDIR\n')
+ for dir in self._dirs:
+ dir = self.NormalizeInstallPath(dir)
+ script.write(' CreateDirectory "%s"\n' % os.path.join('$INSTDIR', dir))
+ for file in self._files:
+ file_norm = self.NormalizeInstallPath(file)
+ script.write(' File "/oname=%s" "%s"\n' % (file_norm, file))
+ for src, symlink in self._symlinks.items():
+ src_norm = self.NormalizeInstallPath(src)
+ link_type = SymlinkType(symlink)
+ script.write(' MkLink::%s "%s" "%s"\n' % (
+ link_type, os.path.join('$INSTDIR', src_norm), symlink))
+ for src, link in self._links.items():
+ src_norm = self.NormalizeInstallPath(src)
+ link_norm = self.NormalizeInstallPath(link)
+ script.write(' MkLink::Hard "%s" "%s"\n' % (
+ os.path.join('$INSTDIR', src_norm),
+ os.path.join('$INSTDIR', link_norm)))
+ script.write('SectionEnd\n')
+
+ def Compile(self):
+ '''Compile the script.
+
+ Compilation happens in a couple of steps: first, the install directory
+ script is generated from |_install_dir|, then the section commands script
+ is generated from |_files|. Finally |_script_file| is compiled, which
+ produces the NSIS installer specified by the OutFile property in
+ |_script_file|.
+ '''
+ working_dir = os.path.dirname(self._script_file)
+ self.CreateInstallNameScript(cwd=working_dir)
+ self.CreateSectionNameScript(cwd=working_dir)
+ # Run the NSIS compiler.
+ subprocess.check_call([os.path.join('NSIS', 'makensis'),
+ '/V2',
+ self._script_file],
+ cwd=working_dir,
+ shell=True)
diff --git a/native_client_sdk/src/build_tools/path_set.py b/native_client_sdk/src/build_tools/path_set.py
new file mode 100644
index 0000000..7108ff6
--- /dev/null
+++ b/native_client_sdk/src/build_tools/path_set.py
@@ -0,0 +1,156 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Class that contains paths classified into four types."""
+
+import os
+
+
+class Error(Exception):
+ pass
+
+
+class PathSet(object):
+ '''Container for paths classified into four types.
+
+ Paths in this container are broken into four separate collections, each
+ accessible as a property (see the "Attributes" section, below).
+
+ Attributes:
+ files: A set of plain files, keys are platform-specific normalized path
+ names. Might be empty.
+ dirs: A set of directories, keys are platform-specific normalized path
+ names. Might be empty.
+ symlinks: A dictionary of symbolic links - these are not followed. Keys
+ are platform-specific normalized path names. The value of each key is
+ the source file of the link (also a platform-specific normalized path).
+ Might be empty.
+ links: A dictionary of hard links. Keys are platform-specific normalized
+ path names. The value of each key is the source file of the link (also
+ a platform-specific normalized path). Might be empty.
+ '''
+
+ def __init__(self):
+ self.Reset()
+
+ def __ior__(self, other):
+ '''Override |= to merge |other| into this path set.'''
+ (self._files, self._dirs,
+ self._symlinks, self._links) = self._MergeWithPathSet(other)
+ return self
+
+ def __or__(self, other):
+ '''Override | to produce the merger of |self| and |other|.'''
+ merged_path_set = PathSet()
+ (merged_path_set.files,
+ merged_path_set.dirs,
+ merged_path_set.symlinks,
+ merged_path_set.links) = self._MergeWithPathSet(other)
+ return merged_path_set
+
+ def _MergeWithPathSet(self, path_set):
+ '''Merge this path set with |path_set|.
+
+ Forms the union of the |_files| and |_dirs| sets, then merges the
+ |_symlinks| and |_links| dicts. The dictionaries are merged in such that
+ keys are not duplicated: the values of the keys in |path_set| take
+ precedence in the returned dictionaries.
+
+ Any keys in either the symlink or links dictionaries that also exist in
+ either of the files or dicts sets are removed from the latter, meaning that
+ symlinks or links which overlap file or directory entries take precedence.
+
+ Args:
+ path_set: The other path set. Must be an object with four properties:
+ files (a set), dirs (a set), symlinks (a dict), links (a dict).
+ Returns:
+ A four-tuple (files, dirs, symlinks, links) which is the result of merging
+ the two PathSets.
+ '''
+ def DiscardOverlappingLinks(links_dict):
+ '''Discard all overlapping keys from files and dirs.'''
+ for link in links_dict:
+ self._files.discard(link)
+ self._dirs.discard(link)
+
+ DiscardOverlappingLinks(path_set.symlinks)
+ DiscardOverlappingLinks(path_set.links)
+ return (self._files | path_set.files,
+ self._dirs | path_set.dirs,
+ dict(self._symlinks.items() + path_set.symlinks.items()),
+ dict(self._links.items() + path_set.links.items()))
+
+ @property
+ def files(self):
+ '''The set of plain files.'''
+ return self._files
+
+ @files.setter
+ def files(self, new_file_set):
+ if isinstance(new_file_set, set):
+ self._files = new_file_set
+ else:
+ raise Error('files property must be a set')
+
+ @property
+ def dirs(self):
+ '''The set of directories.'''
+ return self._dirs
+
+ @dirs.setter
+ def dirs(self, new_dir_set):
+ if isinstance(new_dir_set, set):
+ self._dirs = new_dir_set
+ else:
+ raise Error('dirs property must be a set')
+
+ @property
+ def symlinks(self):
+ '''The dictionary of symbolic links.'''
+ return self._symlinks
+
+ @symlinks.setter
+ def symlinks(self, new_symlinks_dict):
+ if isinstance(new_symlinks_dict, dict):
+ self._symlinks = new_symlinks_dict
+ else:
+ raise Error('symlinks property must be a dict')
+
+ @property
+ def links(self):
+ '''The dictionary of hard links.'''
+ return self._links
+
+ @links.setter
+ def links(self, new_links_dict):
+ if isinstance(new_links_dict, dict):
+ self._links = new_links_dict
+ else:
+ raise Error('links property must be a dict')
+
+ def Reset(self):
+ '''Reset all attributes to empty containers.'''
+ self._files = set()
+ self._dirs = set()
+ self._symlinks = dict()
+ self._links = dict()
+
+ def PrependPath(self, path_prefix):
+ '''Prepend paths in all collections with |path_prefix|.
+
+ All the keys in all the colletions get prepended with |path_prefix|. The
+ resulting path is an os-specific path.
+
+ Args:
+ path_prefix: The path to prepend to all collection keys.
+ '''
+ prepend_path = lambda p: os.path.join(path_prefix, p)
+ def PrependToLinkDict(link_dict):
+ return dict([prepend_path(p), link] for p, link in link_dict.items())
+
+ self._files = set(map(prepend_path, self._files))
+ self._dirs = set(map(prepend_path, self._dirs))
+ self._symlinks = PrependToLinkDict(self._symlinks)
+ self._links = PrependToLinkDict(self._links)
diff --git a/native_client_sdk/src/build_tools/sdk_tools/__init__.py b/native_client_sdk/src/build_tools/sdk_tools/__init__.py
new file mode 100644
index 0000000..15d26bf
--- /dev/null
+++ b/native_client_sdk/src/build_tools/sdk_tools/__init__.py
@@ -0,0 +1,7 @@
+#! -*- python -*-
+
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""sdk_tools Package"""
diff --git a/native_client_sdk/src/build_tools/sdk_tools/sdk_update.py b/native_client_sdk/src/build_tools/sdk_tools/sdk_update.py
new file mode 100755
index 0000000..d5c6f25
--- /dev/null
+++ b/native_client_sdk/src/build_tools/sdk_tools/sdk_update.py
@@ -0,0 +1,1039 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''A simple tool to update the Native Client SDK to the latest version'''
+
+import cStringIO
+import errno
+import exceptions
+import hashlib
+import json
+import optparse
+import os
+import shutil
+import subprocess
+import sys
+import tarfile
+import tempfile
+import time
+import urllib2
+import urlparse
+
+
+#------------------------------------------------------------------------------
+# Constants
+
+# Bump the MINOR_REV every time you check this file in.
+MAJOR_REV = 1
+MINOR_REV = 12
+
+GLOBAL_HELP = '''Usage: naclsdk [options] command [command_options]
+
+naclsdk is a simple utility that updates the Native Client (NaCl)
+Software Developer's Kit (SDK). Each component is kept as a 'bundle' that
+this utility can download as as subdirectory into the SDK.
+
+Commands:
+ help [command] - Get either general or command-specific help
+ list - Lists the available bundles
+ update/install - Updates/installs bundles in the SDK
+
+Example Usage:
+ naclsdk list
+ naclsdk update --force pepper_17
+ naclsdk install recommended
+ naclsdk help update'''
+
+MANIFEST_FILENAME='naclsdk_manifest.json'
+SDK_TOOLS='sdk_tools' # the name for this tools directory
+USER_DATA_DIR='sdk_cache'
+
+HTTP_CONTENT_LENGTH = 'Content-Length' # HTTP Header field for content length
+
+# The following SSL certificates are used to validate the SSL connection
+# to https://commondatastorage.googleapis.com
+# TODO(mball): Validate at least one of these certificates.
+# http://stackoverflow.com/questions/1087227/validate-ssl-certificates-with-python
+
+EQUIFAX_SECURE_CA_CERTIFICATE='''-----BEGIN CERTIFICATE-----
+MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
+UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
+dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
+MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
+dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
+AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
+BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
+cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
+AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
+MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
+aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
+ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
+IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
+MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
+A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
+7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
+1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
+-----END CERTIFICATE-----'''
+
+GOOGLE_INTERNET_AUTHORITY_CERTIFICATE='''-----BEGIN CERTIFICATE-----
+MIICsDCCAhmgAwIBAgIDC2dxMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT
+MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0
+aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDkwNjA4MjA0MzI3WhcNMTMwNjA3MTk0MzI3
+WjBGMQswCQYDVQQGEwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzEiMCAGA1UEAxMZ
+R29vZ2xlIEludGVybmV0IEF1dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAye23pIucV+eEPkB9hPSP0XFjU5nneXQUr0SZMyCSjXvlKAy6rWxJfoNf
+NFlOCnowzdDXxFdF7dWq1nMmzq0yE7jXDx07393cCDaob1FEm8rWIFJztyaHNWrb
+qeXUWaUr/GcZOfqTGBhs3t0lig4zFEfC7wFQeeT9adGnwKziV28CAwEAAaOBozCB
+oDAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFL/AMOv1QxE+Z7qekfv8atrjaxIk
+MB8GA1UdIwQYMBaAFEjmaPkr0rKV10fYIyAQTzOYkJ/UMBIGA1UdEwEB/wQIMAYB
+Af8CAQAwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20v
+Y3Jscy9zZWN1cmVjYS5jcmwwDQYJKoZIhvcNAQEFBQADgYEAuIojxkiWsRF8YHde
+BZqrocb6ghwYB8TrgbCoZutJqOkM0ymt9e8kTP3kS8p/XmOrmSfLnzYhLLkQYGfN
+0rTw8Ktx5YtaiScRhKqOv5nwnQkhClIZmloJ0pC3+gz4fniisIWvXEyZ2VxVKfml
+UUIuOss4jHg7y/j7lYe8vJD5UDI=
+-----END CERTIFICATE-----'''
+
+GOOGLE_USER_CONTENT_CERTIFICATE='''-----BEGIN CERTIFICATE-----
+MIIEPDCCA6WgAwIBAgIKUaoA4wADAAAueTANBgkqhkiG9w0BAQUFADBGMQswCQYD
+VQQGEwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzEiMCAGA1UEAxMZR29vZ2xlIElu
+dGVybmV0IEF1dGhvcml0eTAeFw0xMTA4MTIwMzQ5MjlaFw0xMjA4MTIwMzU5Mjla
+MHExCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1N
+b3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgSW5jMSAwHgYDVQQDFBcqLmdv
+b2dsZXVzZXJjb250ZW50LmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
+uDmDvqlKBj6DppbENEuUmwVsHe5hpixV0bn6D+Ujy3mWUP9HtkO35/RmeFf4/y9i
+nGy78uWO6tk9QY1PsPSiyZN6LgplalBdkTeODCGAieVOVJFhHQ0KM330qDy9sKNM
+rwdMOfLPzkBMYPyr1C7CCm24j//aFiMCxD40bDQXRJkCAwEAAaOCAgQwggIAMB0G
+A1UdDgQWBBRyHxPfv+Lnm2Kgid72ja3pOszMsjAfBgNVHSMEGDAWgBS/wDDr9UMR
+Pme6npH7/Gra42sSJDBbBgNVHR8EVDBSMFCgTqBMhkpodHRwOi8vd3d3LmdzdGF0
+aWMuY29tL0dvb2dsZUludGVybmV0QXV0aG9yaXR5L0dvb2dsZUludGVybmV0QXV0
+aG9yaXR5LmNybDBmBggrBgEFBQcBAQRaMFgwVgYIKwYBBQUHMAKGSmh0dHA6Ly93
+d3cuZ3N0YXRpYy5jb20vR29vZ2xlSW50ZXJuZXRBdXRob3JpdHkvR29vZ2xlSW50
+ZXJuZXRBdXRob3JpdHkuY3J0MCEGCSsGAQQBgjcUAgQUHhIAVwBlAGIAUwBlAHIA
+dgBlAHIwgdUGA1UdEQSBzTCByoIXKi5nb29nbGV1c2VyY29udGVudC5jb22CFWdv
+b2dsZXVzZXJjb250ZW50LmNvbYIiKi5jb21tb25kYXRhc3RvcmFnZS5nb29nbGVh
+cGlzLmNvbYIgY29tbW9uZGF0YXN0b3JhZ2UuZ29vZ2xlYXBpcy5jb22CEGF0Z2ds
+c3RvcmFnZS5jb22CEiouYXRnZ2xzdG9yYWdlLmNvbYIUKi5zLmF0Z2dsc3RvcmFn
+ZS5jb22CCyouZ2dwaHQuY29tgglnZ3BodC5jb20wDQYJKoZIhvcNAQEFBQADgYEA
+XDvIl0/id823eokdFpLA8bL3pb7wQGaH0i3b29572aM7cDKqyxmTBbwi9mMMgbxy
+E/St8DoSEQg3cJ/t2UaTXtw8wCrA6M1dS/RFpNLfV84QNcVdNhLmKEuZjpa+miUK
+8OtYzFSMdfwXrbqKgkAIaqUs6m+LWKG/AQShp6DvTPo=
+-----END CERTIFICATE-----'''
+
+# Some commonly-used key names.
+ARCHIVES_KEY = 'archives'
+BUNDLES_KEY = 'bundles'
+NAME_KEY = 'name'
+REVISION_KEY = 'revision'
+VERSION_KEY = 'version'
+
+# Valid values for bundle.stability field
+STABILITY_LITERALS = [
+ 'obsolete', 'post_stable', 'stable', 'beta', 'dev', 'canary']
+# Valid values for the archive.host_os field
+HOST_OS_LITERALS = frozenset(['mac', 'win', 'linux', 'all'])
+# Valid values for bundle-recommended field.
+YES_NO_LITERALS = ['yes', 'no']
+# Valid keys for various sdk objects, used for validation.
+VALID_ARCHIVE_KEYS = frozenset(['host_os', 'size', 'checksum', 'url'])
+VALID_BUNDLES_KEYS = frozenset([
+ ARCHIVES_KEY, NAME_KEY, VERSION_KEY, REVISION_KEY,
+ 'description', 'desc_url', 'stability', 'recommended',
+ ])
+VALID_MANIFEST_KEYS = frozenset(['manifest_version', BUNDLES_KEY])
+
+
+#------------------------------------------------------------------------------
+# General Utilities
+
+
+_debug_mode = False
+_quiet_mode = False
+
+
+def DebugPrint(msg):
+ '''Display a message to stderr if debug printing is enabled
+
+ Note: This function appends a newline to the end of the string
+
+ Args:
+ msg: A string to send to stderr in debug mode'''
+ if _debug_mode:
+ sys.stderr.write("%s\n" % msg)
+ sys.stderr.flush()
+
+
+def InfoPrint(msg):
+ '''Display an informational message to stdout if not in quiet mode
+
+ Note: This function appends a newline to the end of the string
+
+ Args:
+ mgs: A string to send to stdio when not in quiet mode'''
+ if not _quiet_mode:
+ sys.stdout.write("%s\n" % msg)
+ sys.stdout.flush()
+
+
+def WarningPrint(msg):
+ '''Display an informational message to stderr.
+
+ Note: This function appends a newline to the end of the string
+
+ Args:
+ mgs: A string to send to stderr.'''
+ sys.stderr.write("WARNING: %s\n" % msg)
+ sys.stderr.flush()
+
+
+class Error(Exception):
+ '''Generic error/exception for sdk_update module'''
+ pass
+
+
+def GetHostOS():
+ '''Returns the host_os value that corresponds to the current host OS'''
+ return {
+ 'linux2': 'linux',
+ 'darwin': 'mac',
+ 'cygwin': 'win',
+ 'win32': 'win'
+ }[sys.platform]
+
+
+def ExtractInstaller(installer, outdir):
+ '''Extract the SDK installer into a given directory
+
+ If the outdir already exists, then this function deletes it
+
+ Args:
+ installer: full path of the SDK installer
+ outdir: output directory where to extract the installer
+
+ Raises:
+ CalledProcessError - if the extract operation fails'''
+ if os.path.exists(outdir):
+ RemoveDir(outdir)
+
+ if os.path.splitext(installer)[1] == '.exe':
+ # If the installer has extension 'exe', assume it's a Windows NSIS-style
+ # installer that handles silent (/S) and relocated (/D) installs.
+ command = [installer, '/S', '/D=%s' % outdir]
+ subprocess.check_call(command)
+ else:
+ os.mkdir(outdir)
+ tar_file = None
+ try:
+ tar_file = tarfile.open(installer)
+ tar_file.extractall(path=outdir)
+ finally:
+ if tar_file:
+ tar_file.close()
+
+
+def RemoveDir(outdir):
+ '''Removes the given directory
+
+ On Unix systems, this just runs shutil.rmtree, but on Windows, this doesn't
+ work when the directory contains junctions (as does our SDK installer).
+ Therefore, on Windows, it runs rmdir /S /Q as a shell command. This always
+ does the right thing on Windows.
+
+ Args:
+ outdir: The directory to delete
+
+ Raises:
+ CalledProcessError - if the delete operation fails on Windows
+ OSError - if the delete operation fails on Linux
+ '''
+
+ DebugPrint('Removing %s' % outdir)
+ if sys.platform == 'win32':
+ subprocess.check_call(['rmdir /S /Q', outdir], shell=True)
+ else:
+ shutil.rmtree(outdir)
+
+
+def RenameDir(srcdir, destdir):
+ '''Renames srcdir to destdir. Removes destdir before doing the
+ rename if it already exists.'''
+
+ max_tries = 100
+
+ for num_tries in xrange(max_tries):
+ try:
+ if os.path.exists(destdir):
+ RemoveDir(destdir)
+ os.rename(srcdir, destdir)
+ return
+ except OSError as err:
+ if err.errno != errno.EACCES:
+ raise err
+ # If we are here, we didn't exit due to raised exception, so we are
+ # handling a Windows flaky access error. Sleep one second and try
+ # again.
+ time.sleep(1)
+ # end of while loop -- could not RenameDir
+ raise Error('Could not RenameDir %s => %s after %d tries.\n' %
+ 'Please check that no shells or applications '
+ 'are accessing files in %s.'
+ % (srcdir, destdir, num_tries, destdir))
+
+
+def ShowProgress(progress):
+ ''' A download-progress function used by class Archive.
+ (See DownloadAndComputeHash).'''
+ global count # A divider, so we don't emit dots too often.
+
+ if progress == 0:
+ count = 0
+ elif progress == 100:
+ sys.stdout.write('\n')
+ else:
+ count = count + 1
+ if count > 10:
+ sys.stdout.write('.')
+ sys.stdout.flush()
+ count = 0
+
+
+class ProgressFunction(object):
+ '''Create a progress function for a file with a given size'''
+
+ def __init__(self, file_size=0):
+ '''Constructor
+
+ Args:
+ file_size: number of bytes in file. 0 indicates unknown'''
+ self.dots = 0
+ self.file_size = int(file_size)
+
+ def GetProgressFunction(self):
+ '''Returns a progress function based on a known file size'''
+ def ShowKnownProgress(progress):
+ if progress == 0:
+ sys.stdout.write('|%s|\n' % ('=' * 48))
+ else:
+ new_dots = progress * 50 / self.file_size - self.dots
+ sys.stdout.write('.' * new_dots)
+ self.dots += new_dots
+ if progress == self.file_size:
+ sys.stdout.write('\n')
+ sys.stdout.flush()
+
+ return ShowKnownProgress
+
+
+def DownloadAndComputeHash(from_stream, to_stream=None, progress_func=None):
+ ''' Download the archive data from from-stream and generate sha1 and
+ size info.
+
+ Args:
+ from_stream: An input stream that supports read.
+ to_stream: [optional] the data is written to to_stream if it is
+ provided.
+ progress_func: [optional] A function used to report download progress. If
+ provided, progress_func is called with progress=0 at the
+ beginning of the download, periodically with progress=1
+ during the download, and progress=100 at the end.
+
+ Return
+ A tuple (sha1, size) where sha1 is a sha1-hash for the archive data and
+ size is the size of the archive data in bytes.'''
+ # Use a no-op progress function if none is specified.
+ def progress_no_op(progress):
+ pass
+ if not progress_func:
+ progress_func = progress_no_op
+
+ sha1_hash = hashlib.sha1()
+ size = 0
+ progress_func(progress=0)
+ while(1):
+ data = from_stream.read(32768)
+ if not data:
+ break
+ sha1_hash.update(data)
+ size += len(data)
+ if to_stream:
+ to_stream.write(data)
+ progress_func(size)
+
+ progress_func(progress=100)
+ return sha1_hash.hexdigest(), size
+
+
+class Archive(dict):
+ ''' A placeholder for sdk archive information. We derive Archive from
+ dict so that it is easily serializable. '''
+ def __init__(self, host_os_name):
+ ''' Create a new archive for the given host-os name. '''
+ self['host_os'] = host_os_name
+
+ def CopyFrom(self, dict):
+ ''' Update the content of the archive by copying values from the given
+ dictionary.
+
+ Args:
+ dict: The dictionary whose values must be copied to the archive.'''
+ for key, value in dict.items():
+ self[key] = value
+
+ def Validate(self):
+ ''' Validate the content of the archive object. Raise an Error if
+ an invalid or missing field is found.
+ Returns: True if self is a valid bundle.
+ '''
+ host_os = self.get('host_os', None)
+ if host_os and host_os not in HOST_OS_LITERALS:
+ raise Error('Invalid host-os name in archive')
+ # Ensure host_os has a valid string. We'll use it for pretty printing.
+ if not host_os:
+ host_os = 'all (default)'
+ if not self.get('url', None):
+ raise Error('Archive "%s" has no URL' % host_os)
+ # Verify that all key names are valid.
+ for key, val in self.iteritems():
+ if key not in VALID_ARCHIVE_KEYS:
+ raise Error('Archive "%s" has invalid attribute "%s"' % (host_os, key))
+
+ def _OpenURLStream(self):
+ ''' Open a file-like stream for the archives's url. Raises an Error if the
+ url can't be opened.
+
+ Return:
+ A file-like object from which the archive's data can be read.'''
+ try:
+ url_stream = urllib2.urlopen(self['url'])
+ except urllib2.URLError:
+ raise Error('Cannot open "%s" for archive %s' %
+ (self['url'], self['host_os']))
+
+ return url_stream
+
+ def ComputeSha1AndSize(self):
+ ''' Compute the sha1 hash and size of the archive's data. Raises
+ an Error if the url can't be opened.
+
+ Return:
+ A tuple (sha1, size) with the sha1 hash and data size respectively.'''
+ stream = None
+ sha1 = None
+ size = 0
+ try:
+ print 'Scanning archive to generate sha1 and size info:'
+ stream = self._OpenURLStream()
+ content_length = int(stream.info()[HTTP_CONTENT_LENGTH])
+ sha1, size = DownloadAndComputeHash(from_stream=stream,
+ progress_func=ShowProgress)
+ if size != content_length:
+ raise Error('Download size mismatch for %s.\n'
+ 'Expected %s bytes but got %s' %
+ (self['url'], content_length, size))
+ finally:
+ if stream: stream.close()
+ return sha1, size
+
+ def DownloadToFile(self, dest_path):
+ ''' Download the archive's data to a file at dest_path. As a side effect,
+ computes the sha1 hash and data size, both returned as a tuple. Raises
+ an Error if the url can't be opened, or an IOError exception if
+ dest_path can't be opened.
+
+ Args:
+ dest_path: Path for the file that will receive the data.
+ Return:
+ A tuple (sha1, size) with the sha1 hash and data size respectively.'''
+ sha1 = None
+ size = 0
+ with open(dest_path, 'wb') as to_stream:
+ from_stream = None
+ try:
+ from_stream = self._OpenURLStream()
+ content_length = int(from_stream.info()[HTTP_CONTENT_LENGTH])
+ progress_function = ProgressFunction(
+ content_length).GetProgressFunction()
+ InfoPrint('Downloading %s' % self['url'])
+ sha1, size = DownloadAndComputeHash(
+ from_stream,
+ to_stream=to_stream,
+ progress_func=progress_function)
+ if size != content_length:
+ raise Error('Download size mismatch for %s.\n'
+ 'Expected %s bytes but got %s' %
+ (self['url'], content_length, size))
+ finally:
+ if from_stream: from_stream.close()
+ return sha1, size
+
+ def Update(self, url):
+ ''' Update the archive with the new url. Automatically update the
+ archive's size and checksum fields. Raises an Error if the url is
+ is invalid. '''
+ self['url'] = url
+ sha1, size = self.ComputeSha1AndSize()
+ self['size'] = size
+ self['checksum'] = {'sha1': sha1}
+
+
+class Bundle(dict):
+ ''' A placeholder for sdk bundle information. We derive Bundle from
+ dict so that it is easily serializable.'''
+ def __init__(self, obj):
+ ''' Create a new bundle with the given bundle name.'''
+ if isinstance(obj, str) or isinstance(obj, unicode):
+ dict.__init__(self, [(ARCHIVES_KEY, []), (NAME_KEY, obj)])
+ else:
+ dict.__init__(self, obj)
+
+ def MergeWithBundle(self, bundle):
+ '''Merge this bundle with |bundle|.
+
+ Merges dict in |bundle| with this one in such a way that keys are not
+ duplicated: the values of the keys in |bundle| take precedence in the
+ returned dictionary.
+
+ Any keys in either the symlink or links dictionaries that also exist in
+ either of the files or dicts sets are removed from the latter, meaning that
+ symlinks or links which overlap file or directory entries take precedence.
+
+ Args:
+ bundle: The other bundle. Must be a dict.
+ Returns:
+ A dict which is the result of merging the two Bundles.
+ '''
+ return Bundle(self.items() + bundle.items())
+
+ def CopyFrom(self, dict):
+ ''' Update the content of the bundle by copying values from the given
+ dictionary.
+
+ Args:
+ dict: The dictionary whose values must be copied to the bundle.'''
+ for key, value in dict.items():
+ if key == ARCHIVES_KEY:
+ archives = []
+ for a in value:
+ new_archive = Archive(a['host_os'])
+ new_archive.CopyFrom(a)
+ archives.append(new_archive)
+ self[ARCHIVES_KEY] = archives
+ else:
+ self[key] = value
+
+ def Validate(self):
+ ''' Validate the content of the bundle. Raise an Error if an invalid or
+ missing field is found. '''
+ # Check required fields.
+ if not self.get(NAME_KEY, None):
+ raise Error('Bundle has no name')
+ if self.get(REVISION_KEY, None) == None:
+ raise Error('Bundle "%s" is missing a revision number' % self[NAME_KEY])
+ if self.get(VERSION_KEY, None) == None:
+ raise Error('Bundle "%s" is missing a version number' % self[NAME_KEY])
+ if not self.get('description', None):
+ raise Error('Bundle "%s" is missing a description' % self[NAME_KEY])
+ if not self.get('stability', None):
+ raise Error('Bundle "%s" is missing stability info' % self[NAME_KEY])
+ if self.get('recommended', None) == None:
+ raise Error('Bundle "%s" is missing the recommended field' %
+ self[NAME_KEY])
+ # Check specific values
+ if self['stability'] not in STABILITY_LITERALS:
+ raise Error('Bundle "%s" has invalid stability field: "%s"' %
+ (self[NAME_KEY], self['stability']))
+ if self['recommended'] not in YES_NO_LITERALS:
+ raise Error(
+ 'Bundle "%s" has invalid recommended field: "%s"' %
+ (self[NAME_KEY], self['recommended']))
+ # Verify that all key names are valid.
+ for key, val in self.iteritems():
+ if key not in VALID_BUNDLES_KEYS:
+ raise Error('Bundle "%s" has invalid attribute "%s"' %
+ (self[NAME_KEY], key))
+ # Validate the archives
+ for archive in self[ARCHIVES_KEY]:
+ archive.Validate()
+
+ def GetArchive(self, host_os_name):
+ ''' Retrieve the archive for the given host os.
+
+ Args:
+ host_os_name: name of host os whose archive must be retrieved.
+ Return:
+ An Archive instance or None if it doesn't exist.'''
+ for archive in self[ARCHIVES_KEY]:
+ if archive['host_os'] == host_os_name:
+ return archive
+ return None
+
+ def UpdateArchive(self, host_os, url):
+ ''' Update or create the archive for host_os with the new url.
+ Automatically updates the archive size and checksum info by downloading
+ the data from the given archive. Raises an Error if the url is invalid.
+
+ Args:
+ host_os: name of host os whose archive must be updated or created.
+ url: the new url for the archive.'''
+ archive = self.GetArchive(host_os)
+ if not archive:
+ archive = Archive(host_os_name=host_os)
+ self[ARCHIVES_KEY].append(archive)
+ archive.Update(url)
+
+
+class SDKManifest(object):
+ '''This class contains utilities for manipulation an SDK manifest string
+
+ For ease of unit-testing, this class should not contain any file I/O.
+ '''
+
+ def __init__(self):
+ '''Create a new SDKManifest object with default contents'''
+ self.MANIFEST_VERSION = 1
+ self._manifest_data = {
+ "manifest_version": self.MANIFEST_VERSION,
+ "bundles": [],
+ }
+
+ def _ValidateManifest(self):
+ '''Validate the Manifest file and raises an exception for problems'''
+ # Validate the manifest top level
+ if self._manifest_data["manifest_version"] > self.MANIFEST_VERSION:
+ raise Error("Manifest version too high: %s" %
+ self._manifest_data["manifest_version"])
+ # Verify that all key names are valid.
+ for key, val in self._manifest_data.iteritems():
+ if key not in VALID_MANIFEST_KEYS:
+ raise Error('Manifest has invalid attribute "%s"' % key)
+ # Validate each bundle
+ for bundle in self._manifest_data[BUNDLES_KEY]:
+ bundle.Validate()
+
+ def GetBundle(self, name):
+ ''' Get a bundle from the array of bundles.
+
+ Args:
+ name: the name of the bundle to return.
+ Return:
+ The first bundle with the given name, or None if it is not found.'''
+ if not BUNDLES_KEY in self._manifest_data:
+ return None
+ bundles = filter(lambda b: b[NAME_KEY] == name,
+ self._manifest_data[BUNDLES_KEY])
+ if len(bundles) > 1:
+ WarningPrint("More than one bundle with name '%s' exists." % name)
+ return bundles[0] if len(bundles) > 0 else None
+
+ def SetBundle(self, new_bundle):
+ '''Replace named bundle. Add if absent.
+
+ Args:
+ name: Name of the bundle to replace or add.
+ bundle: The bundle.
+ '''
+ name = new_bundle[NAME_KEY]
+ if not BUNDLES_KEY in self._manifest_data:
+ self._manifest_data[BUNDLES_KEY] = []
+ bundles = self._manifest_data[BUNDLES_KEY]
+ # Delete any bundles from the list, then add the new one. This has the
+ # effect of replacing the bundle if it already exists. It also removes all
+ # duplicate bundles.
+ for i, bundle in enumerate(bundles):
+ if bundle[NAME_KEY] == name:
+ del bundles[i]
+ bundles.append(new_bundle)
+
+ def LoadManifestString(self, json_string, all_hosts=False):
+ ''' Load a JSON manifest string. Raises an exception if json_string
+ is not well-formed JSON.
+
+ Args:
+ json_string: a JSON-formatted string containing the previous manifest
+ all_hosts: True indicates that we should load bundles for all hosts.
+ False (default) says to only load bundles for the current host'''
+ new_manifest = json.loads(json_string)
+ for key, value in new_manifest.items():
+ if key == BUNDLES_KEY:
+ # Remap each bundle in |value| to a Bundle instance
+ bundles = []
+ for b in value:
+ new_bundle = Bundle(b[NAME_KEY])
+ new_bundle.CopyFrom(b)
+ # Only add this archive if it's supported on this platform.
+ # However, the sdk_tools bundle might not have an archive entry,
+ # but is still always valid.
+ if (all_hosts or new_bundle.GetArchive(GetHostOS()) or
+ b[NAME_KEY] == 'sdk_tools'):
+ bundles.append(new_bundle)
+ self._manifest_data[key] = bundles
+ else:
+ self._manifest_data[key] = value
+ self._ValidateManifest()
+
+ def GetManifestString(self):
+ '''Returns the current JSON manifest object, pretty-printed'''
+ pretty_string = json.dumps(self._manifest_data, sort_keys=False, indent=2)
+ # json.dumps sometimes returns trailing whitespace and does not put
+ # a newline at the end. This code fixes these problems.
+ pretty_lines = pretty_string.split('\n')
+ return '\n'.join([line.rstrip() for line in pretty_lines]) + '\n'
+
+
+class SDKManifestFile(object):
+ ''' This class provides basic file I/O support for manifest objects.'''
+
+ def __init__(self, json_filepath):
+ '''Create a new SDKManifest object with default contents.
+
+ If |json_filepath| is specified, and it exists, its contents are loaded and
+ used to initialize the internal manifest.
+
+ Args:
+ json_filepath: path to jason file to read/write, or None to write a new
+ manifest file to stdout.
+ '''
+ self._json_filepath = json_filepath
+ self._manifest = SDKManifest()
+ if self._json_filepath:
+ self._LoadFile()
+
+ def _LoadFile(self):
+ '''Load the manifest from the JSON file.
+
+ This function returns quietly if the file doesn't exit.
+ '''
+ if not os.path.exists(self._json_filepath):
+ return
+
+ with open(self._json_filepath, 'r') as f:
+ json_string = f.read()
+ if json_string:
+ self._manifest.LoadManifestString(json_string, all_hosts=True)
+
+ def WriteFile(self):
+ '''Write the json data to the file. If not file name was specified, the
+ data is written to stdout.'''
+ json_string = self._manifest.GetManifestString()
+ if not self._json_filepath:
+ # No file is specified; print the json data to stdout
+ sys.stdout.write(json_string)
+ else:
+ # Write the JSON data to a temp file.
+ temp_file_name = None
+ # TODO(dspringer): Use file locks here so that multiple sdk_updates can
+ # run at the same time.
+ with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
+ f.write(json_string)
+ temp_file_name = f.name
+ # Move the temp file to the actual file.
+ if os.path.exists(self._json_filepath):
+ os.remove(self._json_filepath)
+ shutil.move(temp_file_name, self._json_filepath)
+
+ def GetBundles(self):
+ '''Return all the bundles in |_manifest|'''
+ return self._manifest._manifest_data[BUNDLES_KEY]
+
+ def GetBundleNamed(self, name):
+ '''Return the first bundle named |name| or None if it doesn't exist'''
+ return self._manifest.GetBundle(name)
+
+ def BundleNeedsUpdate(self, bundle):
+ '''Decides if a bundle needs to be updated.
+
+ A bundle needs to be updated if it is not installed (doesn't exist in this
+ manifest file) or if its revision is later than the revision in this file.
+
+ Args:
+ bundle: The Bundle to test.
+ Returns:
+ True if Bundle needs to be updated.
+ '''
+ if NAME_KEY not in bundle:
+ raise KeyError("Bundle must have a 'name' key.")
+ local_bundle = self.GetBundleNamed(bundle[NAME_KEY])
+ return (local_bundle == None) or (
+ (local_bundle[VERSION_KEY], local_bundle[REVISION_KEY]) <
+ (bundle[VERSION_KEY], bundle[REVISION_KEY]))
+
+ def MergeBundle(self, bundle):
+ '''Merge a Bundle into this manifest.
+
+ The new bundle is added if not present, or merged into the existing bundle.
+
+ Args:
+ bundle: The bundle to merge.
+ '''
+ if NAME_KEY not in bundle:
+ raise KeyError("Bundle must have a 'name' key.")
+ local_bundle = self.GetBundleNamed(bundle[NAME_KEY])
+ if not local_bundle:
+ self._manifest.SetBundle(bundle)
+ else:
+ self._manifest.SetBundle(local_bundle.MergeWithBundle(bundle))
+
+
+class ManifestTools(object):
+ '''Wrapper class for supporting the SDK manifest file'''
+
+ def __init__(self, options):
+ self._options = options
+ self._manifest = SDKManifest()
+
+ def LoadManifest(self):
+ DebugPrint("Running LoadManifest")
+ try:
+ # TODO(mball): Add certificate validation on the server
+ url_stream = urllib2.urlopen(self._options.manifest_url)
+ except urllib2.URLError:
+ raise Error('Unable to open %s' % self._options.manifest_url)
+
+ manifest_stream = cStringIO.StringIO()
+ sha1, size = DownloadAndComputeHash(
+ url_stream, manifest_stream)
+ self._manifest.LoadManifestString(manifest_stream.getvalue())
+
+ def GetBundles(self):
+ return self._manifest._manifest_data[BUNDLES_KEY]
+
+
+#------------------------------------------------------------------------------
+# Commands
+
+
+def List(options, argv):
+ '''Usage: %prog [options] list
+
+ Lists the available SDK bundles that are available for download.'''
+ def PrintBundles(bundles):
+ for bundle in bundles:
+ InfoPrint(' %s' % bundle[NAME_KEY])
+ for key, value in bundle.iteritems():
+ if key not in [ARCHIVES_KEY, NAME_KEY]:
+ InfoPrint(' %s: %s' % (key, value))
+
+ DebugPrint("Running List command with: %s, %s" %(options, argv))
+
+ parser = optparse.OptionParser(usage=List.__doc__)
+ (list_options, args) = parser.parse_args(argv)
+ tools = ManifestTools(options)
+ tools.LoadManifest()
+ bundles = tools.GetBundles()
+ InfoPrint('Available bundles:')
+ PrintBundles(bundles)
+ # Print the local information.
+ local_manifest = SDKManifestFile(os.path.join(options.user_data_dir,
+ options.manifest_filename))
+ bundles = local_manifest.GetBundles()
+ InfoPrint('\nCurrently installed bundles:')
+ PrintBundles(bundles)
+
+
+def Update(options, argv):
+ '''Usage: %prog [options] update [target]
+
+ Updates the Native Client SDK to a specified version. By default, this
+ command updates all the recommended components. The update process works
+ like this:
+ 1. Fetch the manifest from the mirror.
+ 2. Load manifest from USER_DATA_DIR - if there is no local manifest file,
+ make an empty manifest object.
+ 3. Update each the bundle:
+ for bundle in bundles:
+ # Compare bundle versions & revisions.
+ # Test if local version.revision < mirror OR local doesn't exist.
+ if local_manifest < mirror_manifest:
+ update(bundle)
+ update local_manifest with mirror_manifest for bundle
+ write manifest to disk. Use locks.
+ else:
+ InfoPrint('bundle is up-to-date')
+
+ Targets:
+ recommended: (default) Install/Update all recommended components
+ all: Install/Update all available components
+ bundle_name: Install/Update only the given bundle
+ '''
+ DebugPrint("Running Update command with: %s, %s" % (options, argv))
+ ALL='all' # Update all bundles
+ RECOMMENDED='recommended' # Only update the bundles with recommended=yes
+
+ parser = optparse.OptionParser(usage=Update.__doc__)
+ parser.add_option(
+ '-F', '--force', dest='force',
+ default=False, action='store_true',
+ help='Force updating existing components that already exist')
+ (update_options, args) = parser.parse_args(argv)
+ if len(args) == 0:
+ args = [RECOMMENDED]
+ tools = ManifestTools(options)
+ tools.LoadManifest()
+ bundles = tools.GetBundles()
+ local_manifest = SDKManifestFile(os.path.join(options.user_data_dir,
+ options.manifest_filename))
+ for bundle in bundles:
+ bundle_name = bundle[NAME_KEY]
+ bundle_path = os.path.join(options.sdk_root_dir, bundle_name)
+ bundle_update_path = '%s_update' % bundle_path
+ if not (bundle_name in args or
+ ALL in args or (RECOMMENDED in args and
+ bundle[RECOMMENDED] == 'yes')):
+ continue
+ def UpdateBundle():
+ '''Helper to install a bundle'''
+ archive = bundle.GetArchive(GetHostOS())
+ (scheme, host, path, _, _, _) = urlparse.urlparse(archive['url'])
+ dest_filename = os.path.join(options.user_data_dir, path.split('/')[-1])
+ sha1, size = archive.DownloadToFile(os.path.join(options.user_data_dir,
+ dest_filename))
+ if sha1 != archive['checksum']['sha1']:
+ raise Error("SHA1 checksum mismatch on '%s'. Expected %s but got %s" %
+ (bundle_name, archive['checksum']['sha1'], sha1))
+ if size != archive['size']:
+ raise Error("Size mismatch on Archive. Expected %s but got %s bytes" %
+ (archive['size'], size))
+ InfoPrint('Updating bundle %s to version %s, revision %s' % (
+ (bundle_name, bundle[VERSION_KEY], bundle[REVISION_KEY])))
+ ExtractInstaller(dest_filename, bundle_update_path)
+ if bundle_name != SDK_TOOLS:
+ RenameDir(bundle_update_path, bundle_path)
+ os.remove(dest_filename)
+ local_manifest.MergeBundle(bundle)
+ local_manifest.WriteFile()
+ # Test revision numbers, update the bundle accordingly.
+ # TODO(dspringer): The local file should be refreshed from disk each
+ # iteration thought this loop so that multiple sdk_updates can run at the
+ # same time.
+ if local_manifest.BundleNeedsUpdate(bundle):
+ if (not update_options.force and os.path.exists(bundle_path) and
+ bundle_name != SDK_TOOLS):
+ WarningPrint('%s already exists, but has an update available.\n'
+ 'Run update with the --force option to overwrite the '
+ 'existing directory.\nWarning: This will overwrite any '
+ 'modifications you have made within this directory.'
+ % bundle_name)
+ else:
+ UpdateBundle()
+ else:
+ InfoPrint('%s is already up-to-date.' % bundle_name)
+
+ # Validate the arg list against the available bundle names. Raises an
+ # error if any invalid bundle names or args are detected.
+ valid_args = set([ALL, RECOMMENDED] +
+ [bundle[NAME_KEY] for bundle in bundles])
+ bad_args = set(args) - valid_args
+ if len(bad_args) > 0:
+ raise Error("Unrecognized bundle name or argument: '%s'" %
+ ', '.join(bad_args))
+
+
+#------------------------------------------------------------------------------
+# Command-line interface
+
+
+def main(argv):
+ '''Main entry for the sdk_update utility'''
+ parser = optparse.OptionParser(usage=GLOBAL_HELP)
+ DEFAULT_SDK_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+ parser.add_option(
+ '-U', '--manifest-url', dest='manifest_url',
+ default='https://commondatastorage.googleapis.com/nativeclient-mirror/'
+ 'nacl/nacl_sdk/%s' % MANIFEST_FILENAME,
+ help='override the default URL for the NaCl manifest file')
+ parser.add_option(
+ '-d', '--debug', dest='debug',
+ default=False, action='store_true',
+ help='enable displaying debug information to stderr')
+ parser.add_option(
+ '-q', '--quiet', dest='quiet',
+ default=False, action='store_true',
+ help='suppress displaying informational prints to stdout')
+ parser.add_option(
+ '-u', '--user-data-dir', dest='user_data_dir',
+ # TODO(mball): the default should probably be in something like
+ # ~/.naclsdk (linux), or ~/Library/Application Support/NaClSDK (mac),
+ # or %HOMEPATH%\Application Data\NaClSDK (i.e., %APPDATA% on windows)
+ default=os.path.join(DEFAULT_SDK_ROOT, USER_DATA_DIR),
+ help="specify location of NaCl SDK's data directory")
+ parser.add_option(
+ '-s', '--sdk-root-dir', dest='sdk_root_dir',
+ default=DEFAULT_SDK_ROOT,
+ help="location where the SDK bundles are installed")
+ parser.add_option(
+ '-v', '--version', dest='show_version',
+ action='store_true',
+ help='show version information and exit')
+ parser.add_option(
+ '-m', '--manifest', dest='manifest_filename',
+ default=MANIFEST_FILENAME,
+ help="name of local manifest file relative to user-data-dir")
+
+ COMMANDS = {
+ 'list': List,
+ 'update': Update,
+ 'install': Update,
+ }
+
+ # Separate global options from command-specific options
+ global_argv = argv
+ command_argv = []
+ for index, arg in enumerate(argv):
+ if arg in COMMANDS:
+ global_argv = argv[:index]
+ command_argv = argv[index:]
+ break
+
+ (options, args) = parser.parse_args(global_argv)
+ args += command_argv
+
+ global _debug_mode, _quiet_mode
+ _debug_mode = options.debug
+ _quiet_mode = options.quiet
+
+ def PrintHelpAndExit(unused_options=None, unused_args=None):
+ parser.print_help()
+ exit(1)
+
+ if options.show_version:
+ print "Native Client SDK Updater, version %s.%s" % (MAJOR_REV, MINOR_REV)
+ exit(0)
+
+ if not args:
+ print "Need to supply a command"
+ PrintHelpAndExit()
+
+ def DefaultHandler(unused_options=None, unused_args=None):
+ print "Unknown Command: %s" % args[0]
+ PrintHelpAndExit()
+
+ def InvokeCommand(args):
+ command = COMMANDS.get(args[0], DefaultHandler)
+ command(options, args[1:])
+
+ if args[0] == 'help':
+ if len(args) == 1:
+ PrintHelpAndExit()
+ else:
+ InvokeCommand([args[1], '-h'])
+ else:
+ # Make sure the user_data_dir exists.
+ if not os.path.exists(options.user_data_dir):
+ os.makedirs(options.user_data_dir)
+ InvokeCommand(args)
+
+ return 0 # Success
+
+
+if __name__ == '__main__':
+ return_value = 1
+ try:
+ return_value = main(sys.argv[1:])
+ except exceptions.SystemExit:
+ raise
+ except Error as error:
+ print "Error: %s" % error
+
+ sys.exit(return_value)
diff --git a/native_client_sdk/src/build_tools/sdk_tools/set_nacl_env.py b/native_client_sdk/src/build_tools/sdk_tools/set_nacl_env.py
new file mode 100755
index 0000000..a0c11ca
--- /dev/null
+++ b/native_client_sdk/src/build_tools/sdk_tools/set_nacl_env.py
@@ -0,0 +1,491 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+''' A tool to setup the NaCl build env and invoke a command such as make '''
+
+__author__ = 'gwink@google.com (Georges Winkenbach)'
+
+import optparse
+import os
+import re
+import subprocess
+import sys
+
+# The default sdk platform to use if the user doesn't specify one.
+__DEFAULT_SDK_PLATFORM = 'pepper_15'
+
+# Usage info.
+__GLOBAL_HELP = '''%prog options [command]
+
+set-nacl-env is a utility that sets up the environment required to build
+NaCl modules and invokes an optional command in a shell. If no command
+is specified, set-nacl-env spawns a new shell instead. Optionally, the user
+can request that the settings are printed to stdout.
+'''
+
+# Map the string stored in |sys.platform| into a toolchain host specifier.
+__PLATFORM_TO_HOST_MAP = {
+ 'win32': 'windows',
+ 'cygwin': 'windows',
+ 'linux2': 'linux',
+ 'darwin': 'mac',
+ }
+
+# Map key triplet of (host, arch, variant) keys to the corresponding subdir in
+# the toolchain path. For instance (mac, x86-32, newlib) maps to mac_x86_newlib.
+# Note to NaCl eng.: this map is duplicated in nack_utils.py; you must keep them
+# synched.
+__HOST_TO_TOOLCHAIN_MAP = {
+ 'mac': { # Host arch variant
+ 'x86-32': {
+ 'newlib': 'mac_x86_newlib', # Mac x86-32 newlib
+ 'glibc' : 'mac_x86'}, # Mac x86-32 glibc
+ 'x86-64': {
+ 'newlib': 'mac_x86_newlib', # Mac x86-64 newlib
+ 'glibc' : 'mac_x86'}, # Mac x86-64 glibc
+ },
+ 'windows': {
+ 'x86-32': {
+ 'newlib': 'win_x86_newlib', # Windows x86-32 newlib
+ 'glibc' : 'win_x86'}, # Windows x86-32 glibc
+ 'x86-64': {
+ 'newlib': 'win_x86_newlib', # Windows x86-64 newlib
+ 'glibc' : 'win_x86'}, # Windows x86-64 glibc
+ },
+ 'linux': {
+ 'x86-32': {
+ 'newlib': 'linux_x86_newlib', # Windows x86-32 newlib
+ 'glibc' : 'linux_x86'}, # Windows x86-32 glibc
+ 'x86-64': {
+ 'newlib': 'linux_x86_newlib', # Windows x86-64 newlib
+ 'glibc' : 'linux_x86'}, # Windows x86-64 glibc
+ },
+ }
+
+# Map architecture specification to the corresponding tool-name prefix.
+# @private
+__VALID_ARCH_SPECS = {
+ 'x86-32': 'i686',
+ 'x86-64': 'x86_64',
+ }
+
+# Valid lib variants.
+__VALID_VARIANT = ['glibc', 'newlib']
+
+# Lists of env keys for build tools. Note: Each matching value is actually a
+# format template with fields for 'prefix' such as 'i686-nacl-' and 'extras'
+# such as ' -m64'.
+__BUILD_TOOLS = {
+ 'CC': '{prefix}gcc{extras}',
+ 'CXX': '{prefix}g++{extras}',
+ 'AR': '{prefix}ar{extras}',
+ 'LINK': '{prefix}g++{extras}',
+ 'STRIP': '{prefix}strip',
+ 'RANLIB': '{prefix}ranlib',
+ }
+
+# List of env keys for build options with corresponding settings that are
+# common to all build configurations.
+__BUILD_OPTIONS = {
+ 'CFLAGS': ['-std=gnu99', '-Wall', '-Wswitch-enum', '-g'],
+ 'CXXFLAGS': ['-std=gnu++98', '-Wswitch-enum', '-g', '-pthread'],
+ 'CPPFLAGS': ['-D_GNU_SOURCE=1', '-D__STDC_FORMAT_MACROS=1'],
+ 'LDFLAGS': [],
+ }
+
+# All the build-flags env keys in one list.
+__ALL_ENV_KEYS = __BUILD_TOOLS.keys() + __BUILD_OPTIONS.keys()
+
+# Map build types to the corresponding build flags.
+__BUILD_TYPES = {
+ 'debug': ['-O0'],
+ 'release': ['-O3'],
+ }
+
+
+def FormatOptionList(option_list, prefix='', separator=' '):
+ ''' Format a list of build-option items into a string.
+
+ Format a list of build-option items into a string suitable for output.
+
+ Args:
+ prefix: a prefix string to prepend to each list item. For instance,
+ prefix='-D' with item='__DEBUG' generates '-D__DEBUG'.
+ separator: a separator string to insert between items.
+
+ Returns:
+ A formatted string. An empty string if list is empty.
+ '''
+ return separator.join([prefix + item for item in option_list])
+
+
+def GetNaclSdkRoot():
+ ''' Produce a string with the full path to the NaCl SDK root.
+
+ The path to nacl-sdk root is derived from one of two sources. First, if
+ NACL_SDK_ROOT is defined in env it is assumed to contain the desired sdk
+ root. That makes it possible for this tool to run from any location. If
+ NACL_SDK_ROOT is not defined or is empty, the sdk root is taken as the
+ parent directory to this script's file. This works well when this script
+ is ran from the sdk_tools directory in the standard SDK installation.
+
+ Returns:
+ A string with the path to the NaCl SDK root.
+ '''
+ if 'NACL_SDK_ROOT' in os.environ:
+ return os.environ['NACL_SDK_ROOT']
+ else:
+ return os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+
+def GetToolchainPath(options):
+ ''' Build the path to the toolchain directory.
+
+ Given the host, sdk-root directory, sdk platform, architecture and library
+ variant, this function builds the path to the toolchain directory.
+
+ Examples:
+ For
+ sdk_root == 'c:/cool_code/nacl_sdk'
+ arch == 'x86-32'
+ lib variant == 'newlib'
+ nacl platform = 'pepper_17'
+ host == 'mac'
+ this function returns :
+ toolchain_path == /cool_code/nacl_sdk/pepper_17/toolchain/mac_x86_newlib
+
+ Args:
+ options: the options instances containing attributes options.host,
+ options.arch, options.lib_variant, options.sdk_root and
+ options.sdk_platform.
+
+ Returns:
+ A string containing the absolute path to the base directory for the
+ toolchain.
+ '''
+ host = options.host
+ arch = options.arch
+ variant = options.lib_variant
+ toolchain_dir = __HOST_TO_TOOLCHAIN_MAP[host][arch][variant]
+ base_dir = os.path.abspath(options.sdk_root)
+ return os.path.join(base_dir, options.sdk_platform, 'toolchain',
+ toolchain_dir)
+
+
+def ConfigureBaseEnv(merge):
+ ''' Configure and return a new env instance with the essential options.
+
+ Create and return a new env instance with the base configuration. That env
+ contains at least an empty entry for each key defined in __ALL_ENV_KEYS.
+ However, if merge is True, a copy of the current os.environ is used to seed
+ env.
+
+ Argument:
+ merge: True ==> merge build configuration with os.environ..
+
+ Returns:
+ A base env map.
+ '''
+ env = {}
+ if merge:
+ for key, value in os.environ.items():
+ env[key] = [value]
+ # Ensure that every env key has a default definition.
+ for key in __ALL_ENV_KEYS:
+ env.setdefault(key, [])
+ return env
+
+
+def SetBuildTools(env, tool_bin_path, tool_prefix, extras_flags=''):
+ ''' Configure the build tools build flags in env.
+
+ Given the absolute path to the toolchain's bin directory, tool_prefix and
+ optional extra_flags build flags, set the entries for the build tools
+ in env. For instance, using the sample path from GetToolchainPath above and
+ tool_prefix = 'i686-nacl-' we would get
+
+ env['CC'] =
+ '/cool_code/nacl_sdk/pepper_17/toolchain/mac_x86_newlib/bin/i686-nacl-gcc'
+
+ Args:
+ env: the env map to setup.
+ tool_bin_path: the absolute path to the toolchain's bin directory.
+ tool_prefix: a string with the tool's prefix, such as 'i686-nacl-'.
+ extra_flags: optional extra flags, such as ' -m64'.
+ '''
+ for key, val in __BUILD_TOOLS.iteritems():
+ tool_name = val.format(prefix=tool_prefix, extras=extras_flags)
+ env[key] = os.path.join(tool_bin_path, tool_name)
+
+
+def SetRuntimeTools(env, tool_runtime_path):
+ ''' Setup the runtime tools in env.
+
+ Given an absolute path to the toolchain's runtime directory, setup the
+ entries for the runtime tools in env.
+
+ Args:
+ env: the env map to setup.
+ tool_runtime_path: the absolute path to the toolchain's runtime directory.
+ '''
+ env['NACL_IRT_CORE32'] = os.path.join(tool_runtime_path,
+ 'irt_core_x86_32.nexe')
+ env['NACL_IRT_CORE64'] = os.path.join(tool_runtime_path,
+ 'irt_core_x86_64.nexe')
+
+
+def SetCommonBuildOptions(env, options):
+ ''' Set the common build options, such as CFLAGS.
+
+ Set the build options, such as CFLAGS that are common to all build
+ configurations, given the built type.
+
+ Args:
+ env: the env map to set.
+ build_type: one of 'debug' or 'release'.
+ '''
+ # Set the build flags from __BUILD_OPTIONS.
+ for key, val in __BUILD_OPTIONS.iteritems():
+ env[key].extend(val)
+ # Add the build-type specific flags.
+ env['CFLAGS'].extend(__BUILD_TYPES[options.build_type])
+ env['CXXFLAGS'].extend(__BUILD_TYPES[options.build_type])
+ if not options.no_ppapi:
+ env['LDFLAGS'].extend(['-lppapi'])
+
+
+def SetupX86Env(options):
+ ''' Generate a new env map for X86 builds.
+
+ Generate and return a new env map for x86-NN architecture. The NN bit
+ size is derived from options.arch.
+
+ Argument:
+ options: the cmd-line options.
+
+ Returns:
+ A new env map with the build configuration flags set.
+ '''
+ env = ConfigureBaseEnv(options.merge)
+
+ # Where to find tools and libraries within the toolchain directory.
+ tool_bin_path = os.path.join(options.toolchain_path, 'bin')
+ tool_runtime_path = os.path.join(options.toolchain_path, 'runtime')
+
+ # Store the bin paths into env. This isn't really part of the build
+ # environment. But it's nice to have there for reference.
+ env['NACL_TOOL_BIN_PATH'] = tool_bin_path
+ env['NACL_TOOL_RUNTIME_PATH'] = tool_runtime_path
+
+ if options.arch == 'x86-32':
+ SetBuildTools(env, tool_bin_path, 'i686-nacl-', extras_flags=' -m32')
+ else:
+ assert(options.arch == 'x86-64')
+ SetBuildTools(env, tool_bin_path, 'x86_64-nacl-', extras_flags=' -m64')
+ SetRuntimeTools(env, tool_runtime_path)
+ SetCommonBuildOptions(env, options)
+ return env
+
+
+def dump(options, env, template):
+ ''' Dump the build settings in env to stdout.
+
+ Args:
+ options: the cmd-line options, used to output the target buid configuartion.
+ env: the env map with the build flags.
+ template: a fiormatting template used to format options output. It must
+ contain format fields 'option' and 'value'.
+ '''
+ if options.pretty_print:
+ print '\nConfiguration:'
+ print '-------------'
+ print ' Host = %s' % options.host
+ print ' NaCl SDK root = %s' % options.sdk_root
+ print ' SDK platform = %s' % options.sdk_platform
+ print ' Target architecture = %s' % options.arch
+ print ' Lib variant = %s' % options.lib_variant
+
+ if options.pretty_print:
+ print '\nNaCl toolchain paths:'
+ print '-------------------------'
+ print ' toolchain = %s' % options.toolchain_path
+ print ' toolchain bin = %s' % env['NACL_TOOL_BIN_PATH']
+ print ' toolchain runtime = %s' % env['NACL_TOOL_RUNTIME_PATH']
+
+ if options.pretty_print:
+ print '\nBuild tools:'
+ print '-----------'
+ print template.format(option='CC', value=env['CC'])
+ print template.format(option='CXX', value=env['CXX'])
+ print template.format(option='AR', value=env['AR'])
+ print template.format(option='LINK', value=env['LINK'])
+ print template.format(option='STRIP', value=env['STRIP'])
+ print template.format(option='RANLIB', value=env['RANLIB'])
+
+ if options.pretty_print:
+ print '\nBuild settings:'
+ print '--------------'
+ print template.format(option='CFLAGS',
+ value=FormatOptionList(option_list=env['CFLAGS']))
+ print template.format(option='CXXFLAGS',
+ value=FormatOptionList(option_list=env['CXXFLAGS']))
+ print template.format(option='LDFLAGS',
+ value=FormatOptionList(option_list=env['LDFLAGS']))
+ print template.format(option='CPPFLAGS',
+ value=FormatOptionList(option_list=env['CPPFLAGS']))
+ if options.pretty_print:
+ print '\nRuntime tools:'
+ print '-------------'
+ print template.format(option='NACL_IRT_CORE32', value=env['NACL_IRT_CORE32'])
+ print template.format(option='NACL_IRT_CORE64', value=env['NACL_IRT_CORE64'])
+ print ''
+
+
+def NormalizeEnv(env):
+ ''' Returns a copy of env normalized.
+
+ Internally, this script uses lists to keep track of build settings in env.
+ This function converts these list to space-separated strings of items,
+ suitable for use as a subprocess env.
+
+ Argument:
+ env: env map that must be normalized.
+
+ Returns:
+ A copy of env with lists converted to strings.
+ '''
+ norm_env = {}
+ for key, value in env.iteritems():
+ if isinstance(value, list):
+ norm_env[key] = ' '.join(value)
+ else:
+ norm_env[key] = value
+ return norm_env
+
+
+def RunCommandOrShell(cmd_list, env):
+ ''' Run the command in cmd_list or a shell if cmd_list is empty.
+
+ Run the command in cmd_list using a normalized copy of env. For instance,
+ cmd_list might contain the items ['make', 'application'], which would
+ cause command 'make application' to run in the current directory. If cmd_list
+ is empty, this function will spawn a new sbushell instead.
+
+ Args:
+ cmd_list: the command list to run.
+ env: the environment to use.
+ '''
+ # If cmd_list is empty, set it up to spawn a shell instead. If cmd_list
+ # isn't empty, it will run in the current shell (for security and so that the
+ # user can see the output).
+ new_shell = False
+ if cmd_list:
+ # Normalize cmd_list by building a list of individual arguments.
+ new_cmd_list = []
+ for item in cmd_list:
+ new_cmd_list += item.split()
+ cmd_list = new_cmd_list
+ else:
+ # Build a shell command.
+ new_shell = True
+ if sys.platform == 'win32':
+ cmd_list = ['cmd']
+ else:
+ cmd_list = ['/bin/bash', '-s']
+ return subprocess.call(cmd_list, env=NormalizeEnv(env), shell=new_shell)
+
+
+def GenerateBuildSettings(options, args):
+ ''' Generate the build settings and dump them or invoke a command.
+
+ Given the cmd-line options and remaining cmd-line arguments, generate the
+ required build settings and either dump them to stdout or invoke a shell
+ command.
+
+ Args:
+ options: cmd-line options.
+ args: unconsumed cmd-line arguments.
+
+ Returns:
+ 0 in case of success or a command result code otherwise.
+ '''
+ # A few generated options, which we store in options for convenience.
+ options.host = __PLATFORM_TO_HOST_MAP[sys.platform]
+ options.sdk_root = GetNaclSdkRoot()
+ options.toolchain_path = GetToolchainPath(options)
+
+ env = SetupX86Env(options)
+ if options.dump:
+ dump(options, env, template=options.format_template)
+ return 0
+ else:
+ return RunCommandOrShell(args, env)
+
+
+def main(argv):
+ ''' Do main stuff, mainly.
+ '''
+ parser = optparse.OptionParser(usage=__GLOBAL_HELP)
+ parser.add_option(
+ '-a', '--arch', dest='arch',
+ choices=['x86-32', 'x86-64'],
+ default='x86-64',
+ help='The target architecture; one of x86-32 or x86-64. '
+ '[default = %default.]')
+ parser.add_option(
+ '-A', '--no_ppapi', dest='no_ppapi',
+ default=False,
+ action='store_true',
+ help='Do not add -lppapi to the link settings.')
+ parser.add_option(
+ '-d', '--dump', dest='dump',
+ default=False,
+ action='store_true',
+ help='Dump the build settings to stdout')
+ parser.add_option(
+ '-D', '--pretty_print', dest='pretty_print',
+ default=False,
+ action='store_true',
+ help='Print section headers when dumping to stdout')
+ parser.add_option(
+ '-f', '--format_template', dest='format_template',
+ default=' {option}={value}',
+ help="The formatting template used to output (option, value) pairs."
+ "[default='%default.']")
+ parser.add_option(
+ '-n', '--no_merge', dest='merge',
+ default=True,
+ action='store_false',
+ help='Do not merge the build options with current environment. By default'
+ ' %prog merges the build flags with the current environment vars.'
+ ' This option turns that off.')
+ parser.add_option(
+ '-p', '--platform', dest='sdk_platform',
+ default=__DEFAULT_SDK_PLATFORM,
+ help='The SDK platform to use; e.g. pepper_16. [default = %default.]')
+ parser.add_option(
+ '-t', '--build_type', dest='build_type',
+ choices=__BUILD_TYPES.keys(),
+ default='debug',
+ help='The desired build type; one of debug or release.'
+ ' [default = %default.]')
+ parser.add_option(
+ '-v', '--variant', dest='lib_variant',
+ choices=['glibc', 'newlib'], default='newlib',
+ help='The lib variant to use; one of glibc or newlib. '
+ '[default = %default.]')
+
+ (options, args) = parser.parse_args(argv)
+
+ # Verify that we're running on a supported host.
+ if sys.platform not in __PLATFORM_TO_HOST_MAP:
+ sys.stderr.write('Platform %s is not supported.' % sys.platform)
+ return 1
+
+ return GenerateBuildSettings(options, args)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:])) \ No newline at end of file
diff --git a/native_client_sdk/src/build_tools/sdk_tools/update_manifest.py b/native_client_sdk/src/build_tools/sdk_tools/update_manifest.py
new file mode 100755
index 0000000..5460943
--- /dev/null
+++ b/native_client_sdk/src/build_tools/sdk_tools/update_manifest.py
@@ -0,0 +1,391 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''Utility to update the SDK manifest file in the build_tools directory'''
+
+import optparse
+import os
+import re
+import sdk_update
+import string
+import subprocess
+import sys
+
+HELP='''"Usage: %prog [-b bundle] [options]"
+
+Actions for particular bundles:
+ sdk_tools: Upload the most recently built nacl_sdk.zip and sdk_tools.tgz
+ files to the server and update the manifest file
+ pepper_??: Download the latest pepper builds off the appropriate branch,
+ upload these files to the appropriate location on the server, and
+ update the manifest file.
+ <others>: Only update manifest file -- you'll need to upload the file yourself
+'''
+
+# Map option keys to manifest attribute key. Option keys are used to retrieve
+# option values from cmd-line options. Manifest attribute keys label the
+# corresponding value in the manifest object.
+OPTION_KEY_MAP = {
+ # option key manifest attribute key
+ 'bundle_desc_url': 'desc_url',
+ 'bundle_revision': sdk_update.REVISION_KEY,
+ 'bundle_version': sdk_update.VERSION_KEY,
+ 'desc': 'description',
+ 'recommended': 'recommended',
+ 'stability': 'stability',
+ }
+# Map options keys to platform key, as stored in the bundle.
+OPTION_KEY_TO_PLATFORM_MAP = {
+ 'mac_arch_url': 'mac',
+ 'win_arch_url': 'win',
+ 'linux_arch_url': 'linux',
+ 'all_arch_url': 'all',
+ }
+
+NACL_SDK_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(
+ os.path.abspath(__file__))))
+
+BUILD_TOOLS_OUT = os.path.join(NACL_SDK_ROOT, 'scons-out', 'build', 'obj',
+ 'build_tools')
+
+BUNDLE_SDK_TOOLS = 'sdk_tools'
+BUNDLE_PEPPER_MATCHER = re.compile('^pepper_([0-9]+)$')
+IGNORE_OPTIONS = set(['gsutil', 'manifest_file', 'upload', 'root_url'])
+
+
+class Error(Exception):
+ '''Generic error/exception for update_manifest module'''
+ pass
+
+
+def UpdateBundle(bundle, options):
+ ''' Update the bundle per content of the options.
+
+ Args:
+ options: options data. Attributes that are used are also deleted from
+ options.'''
+ # Check, set and consume individual bundle options.
+ for option_key, attribute_key in OPTION_KEY_MAP.iteritems():
+ option_val = getattr(options, option_key, None)
+ if option_val is not None:
+ bundle[attribute_key] = option_val
+ delattr(options, option_key);
+ # Validate what we have so far; we may just avoid going through a lengthy
+ # download, just to realize that some other trivial stuff is missing.
+ bundle.Validate()
+ # Check and consume archive-url options.
+ for option_key, host_os in OPTION_KEY_TO_PLATFORM_MAP.iteritems():
+ platform_url = getattr(options, option_key, None)
+ if platform_url is not None:
+ bundle.UpdateArchive(host_os, platform_url)
+ delattr(options, option_key);
+
+
+class UpdateSDKManifest(sdk_update.SDKManifest):
+ '''Adds functions to SDKManifest that are only used in update_manifest'''
+
+ def _ValidateBundleName(self, name):
+ ''' Verify that name is a valid bundle.
+
+ Args:
+ name: the proposed name for the bundle.
+
+ Return:
+ True if the name is valid for a bundle, False otherwise.'''
+ valid_char_set = '()-_.%s%s' % (string.ascii_letters, string.digits)
+ name_len = len(name)
+ return (name_len > 0 and all(c in valid_char_set for c in name))
+
+ def _UpdateManifestVersion(self, options):
+ ''' Update the manifest version number from the options
+
+ Args:
+ options: options data containing an attribute self.manifest_version '''
+ version_num = int(options.manifest_version)
+ self._manifest_data['manifest_version'] = version_num
+ del options.manifest_version
+
+ def _UpdateBundle(self, options):
+ ''' Update or setup a bundle from the options.
+
+ Args:
+ options: options data containing at least a valid bundle_name
+ attribute. Other relevant bundle attributes will also be
+ used (and consumed) by this function. '''
+ # Get and validate the bundle name
+ if not self._ValidateBundleName(options.bundle_name):
+ raise Error('Invalid bundle name: "%s"' % options.bundle_name)
+ bundle_name = options.bundle_name
+ del options.bundle_name
+ # Get the corresponding bundle, or create it.
+ bundle = self.GetBundle(bundle_name)
+ if not bundle:
+ bundle = sdk_update.Bundle(bundle_name)
+ self.SetBundle(bundle)
+ UpdateBundle(bundle, options)
+
+ def _VerifyAllOptionsConsumed(self, options, bundle_name):
+ ''' Verify that all the options have been used. Raise an exception if
+ any valid option has not been used. Returns True if all options have
+ been consumed.
+
+ Args:
+ options: the object containing the remaining unused options attributes.
+ bundl_name: The name of the bundle, or None if it's missing.'''
+ # Any option left in the list should have value = None
+ for key, val in options.__dict__.items():
+ if val != None and key not in IGNORE_OPTIONS:
+ if bundle_name:
+ raise Error('Unused option "%s" for bundle "%s"' % (key, bundle_name))
+ else:
+ raise Error('No bundle name specified')
+ return True;
+
+ def UpdateManifest(self, options):
+ ''' Update the manifest object with values from the command-line options
+
+ Args:
+ options: options object containing attribute for the command-line options.
+ Note that all the non-trivial options are consumed.
+ '''
+ # Go over all the options and update the manifest data accordingly.
+ # Valid options are consumed as they are used. This gives us a way to
+ # verify that all the options are used.
+ if options.manifest_version is not None:
+ self._UpdateManifestVersion(options)
+ # Keep a copy of bundle_name, which will be consumed by UpdateBundle, for
+ # use in _VerifyAllOptionsConsumed below.
+ bundle_name = options.bundle_name
+ if bundle_name is not None:
+ self._UpdateBundle(options)
+ self._VerifyAllOptionsConsumed(options, bundle_name)
+ self._ValidateManifest()
+
+
+class GsUtil(object):
+ def __init__(self, gsutil):
+ '''gsutil is the path to the gsutil executable'''
+ self.gsutil = gsutil
+ self.root = 'gs://nativeclient-mirror/nacl/nacl_sdk'
+
+ def GetURI(self, path):
+ '''Return the full gs:// URI for a given relative path'''
+ return '/'.join([self.root, path])
+
+ def Run(self, command):
+ '''Runs gsutil with a given argument list and returns exit status'''
+ args = [self.gsutil] + command
+ print 'GSUtil.Run(%s)' % args
+ sys.stdout.flush()
+ return subprocess.call(args)
+
+ def CheckIfExists(self, path):
+ '''Check whether a given path exists on commondatastorage
+
+ Args:
+ path: path relative to SDK root directory on the server
+
+ Returns: True if it exists, False if it does not'''
+ # todo(mball): Be a little more intelligent about this check and compare
+ # the output strings against expected values
+ return self.Run(['ls', self.GetURI(path)]) == 0
+
+ def Copy(self, source, destination):
+ '''Copies a given source file to a destination path and makes it readable
+
+ Args:
+ source: path to source file on local filesystem
+ destination: path to destination, relative to root directory'''
+ args = ['cp', '-a', 'public-read', source, self.GetURI(destination)]
+ if self.Run(args) != 0:
+ raise Error('Unable to copy %s to %s' % (source, destination))
+
+
+class UpdateSDKManifestFile(sdk_update.SDKManifestFile):
+ '''Adds functions to SDKManifestFile that are only used in update_manifest'''
+
+ def __init__(self, options):
+ '''Create a new SDKManifest object with default contents.
+
+ If |json_filepath| is specified, and it exists, its contents are loaded and
+ used to initialize the internal manifest.
+
+ Args:
+ json_filepath: path to json file to read/write, or None to write a new
+ manifest file to stdout.
+ '''
+ # Strip-off all the I/O-based options that do not relate to bundles
+ self._json_filepath = options.manifest_file
+ self.gsutil = GsUtil(options.gsutil)
+ self.options = options
+ self._manifest = UpdateSDKManifest()
+ if self._json_filepath:
+ self._LoadFile()
+
+ def _HandleSDKTools(self):
+ '''Handles the sdk_tools bundle'''
+ # General sanity checking of parameters
+ SDK_TOOLS_FILES = ['sdk_tools.tgz', 'nacl_sdk.zip']
+ options = self.options
+ if options.bundle_version is None:
+ options.bundle_version = sdk_update.MAJOR_REV
+ if options.bundle_version != sdk_update.MAJOR_REV:
+ raise Error('Specified version (%s) does not match MAJOR_REV (%s)' %
+ (options.bundle_version, sdk_update.MAJOR_REV))
+ if options.bundle_revision is None:
+ options.bundle_revision = sdk_update.MINOR_REV
+ if options.bundle_revision != sdk_update.MINOR_REV:
+ raise Error('Specified revision (%s) does not match MINOR_REV (%s)' %
+ (options.bundle_revision, sdk_update.MINOR_REV))
+ version = '%s.%s' % (options.bundle_version, options.bundle_revision)
+ # Update the remaining options
+ if options.desc is None:
+ options.desc = ('Native Client SDK Tools, revision %s.%s' %
+ (options.bundle_version, options.bundle_revision))
+ options.recommended = options.recommended or 'yes'
+ options.stability = options.stability or 'stable'
+ if options.upload:
+ # Check whether the tools already exist
+ for name in SDK_TOOLS_FILES:
+ path = '/'.join([version, name])
+ if self.gsutil.CheckIfExists(path):
+ raise Error('File already exists at %s' % path)
+ # Upload the tools files to the server
+ for name in SDK_TOOLS_FILES:
+ source = os.path.join(BUILD_TOOLS_OUT, name)
+ destination = '/'.join([version, name])
+ self.gsutil.Copy(source, destination)
+ url = '/'.join([options.root_url, version, 'sdk_tools.tgz'])
+ options.mac_arch_url = options.mac_arch_url or url
+ options.linux_arch_url = options.linux_arch_url or url
+ options.win_arch_url = options.win_arch_url or url
+
+ def _HandlePepper(self):
+ '''Handles the pepper bundles'''
+ options = self.options
+ match = BUNDLE_PEPPER_MATCHER.match(options.bundle_name)
+ if match is not None:
+ options.bundle_version = int(match.group(1))
+ if options.bundle_version is None:
+ raise Error('Need to specify a bundle version')
+ if options.bundle_revision is None:
+ raise Error('Need to specify a bundle revision')
+ if options.desc is None:
+ options.desc = ('Chrome %s bundle, revision %s' %
+ (options.bundle_version, options.bundle_revision))
+ root_url = '%s/pepper_%s_%s' % (options.root_url, options.bundle_version,
+ options.bundle_revision)
+ options.mac_arch_url = '/'.join([root_url, 'naclsdk_mac.tgz'])
+ options.linux_arch_url = '/'.join([root_url, 'naclsdk_linux.tgz'])
+ options.win_arch_url = '/'.join([root_url, 'naclsdk_win.exe'])
+
+ def HandleBundles(self):
+ '''Handles known bundles by automatically uploading files'''
+ bundle_name = self.options.bundle_name
+ if bundle_name == BUNDLE_SDK_TOOLS:
+ self._HandleSDKTools()
+ elif bundle_name.startswith('pepper'):
+ self._HandlePepper()
+
+ def UpdateWithOptions(self):
+ ''' Update the manifest file with the given options. Create the manifest
+ if it doesn't already exists. Raises an Error if the manifest doesn't
+ validate after updating.
+
+ Args:
+ options: option data'''
+ # UpdateManifest does not know how to deal with file-related options
+ self._manifest.UpdateManifest(self.options)
+ self.WriteFile()
+
+
+def main(argv):
+ '''Main entry for update_manifest.py'''
+
+ buildtools_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+ parser = optparse.OptionParser(usage=HELP)
+
+ # Setup options
+ parser.add_option(
+ '-b', '--bundle-version', dest='bundle_version',
+ type='int',
+ default=None,
+ help='Required: Version number for the bundle.')
+ parser.add_option(
+ '-B', '--bundle-revision', dest='bundle_revision',
+ type='int',
+ default=None,
+ help='Required: Revision number for the bundle.')
+ parser.add_option(
+ '-d', '--description', dest='desc',
+ default=None,
+ help='Required: Description for this bundle.')
+ parser.add_option(
+ '-f', '--manifest-file', dest='manifest_file',
+ default=os.path.join(buildtools_dir, 'json',
+ sdk_update.MANIFEST_FILENAME),
+ help='location of manifest file to read and update')
+ parser.add_option(
+ '-g', '--gsutil', dest='gsutil',
+ default='gsutil', help='location of gsutil tool for uploading bundles')
+ parser.add_option(
+ '-L', '--linux-archive', dest='linux_arch_url',
+ default=None,
+ help='URL for the Linux archive.')
+ parser.add_option(
+ '-M', '--mac-archive', dest='mac_arch_url',
+ default=None,
+ help='URL for the Mac archive.')
+ parser.add_option(
+ '-n', '--bundle-name', dest='bundle_name',
+ default=None,
+ help='Required: Name of the bundle.')
+ parser.add_option(
+ '-r', '--recommended', dest='recommended',
+ choices=sdk_update.YES_NO_LITERALS,
+ default=None,
+ help='Required: whether this bundle is recommended. one of "yes" or "no"')
+ parser.add_option(
+ '-R', '--root-url', dest='root_url',
+ default='http://commondatastorage.googleapis.com/nativeclient-mirror/'
+ 'nacl/nacl_sdk',
+ help='Root url for uploading')
+ parser.add_option(
+ '-s', '--stability', dest='stability',
+ choices=sdk_update.STABILITY_LITERALS,
+ default=None,
+ help='Required: Stability for this bundle; one of. '
+ '"obsolete", "post_stable", "stable", "beta", "dev", "canary".')
+ parser.add_option(
+ '-u', '--desc-url', dest='bundle_desc_url',
+ default=None,
+ help='Optional: URL to follow to read additional bundle info.')
+ parser.add_option(
+ '-U', '--upload', dest='upload', default=False, action='store_true',
+ help='Indicates whether to upload bundle to server')
+ parser.add_option(
+ '-v', '--manifest-version', dest='manifest_version',
+ type='int',
+ default=None,
+ help='Required for new manifest files: '
+ 'Version number for the manifest.')
+ parser.add_option(
+ '-W', '--win-archive', dest='win_arch_url',
+ default=None,
+ help='URL for the Windows archive.')
+
+ # Parse options and arguments and check.
+ (options, args) = parser.parse_args(argv)
+ if len(args) > 0:
+ parser.error('These arguments were not understood: %s' % args)
+
+ manifest_file = UpdateSDKManifestFile(options)
+ manifest_file.HandleBundles()
+ manifest_file.UpdateWithOptions()
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/native_client_sdk/src/build_tools/tar_archive.py b/native_client_sdk/src/build_tools/tar_archive.py
new file mode 100644
index 0000000..7d29340
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tar_archive.py
@@ -0,0 +1,156 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Class that produces a table of contents form a tar archive."""
+
+import os
+import tarfile
+
+from build_tools import path_set
+
+
+class Error(Exception):
+ pass
+
+
+class TarArchive(path_set.PathSet):
+ '''Container for a tar archive table of contents.
+
+ The table of contents is an enumeration of each node in the archive, stored
+ as the attributes of a PathSet (see path_set.py for details).
+
+ The tar archive can be taken directly from a tarball (as long as the format
+ is supported by the tarfile module), or from a manifest file that was
+ generated using tar -tv.
+
+ Attributes:
+ path_filter: A callable that gets applied to all paths in the object's tar
+ archive before the paths are added to any of the sets or dictionaries.
+ Setting this callable to None has the same effect as using a
+ pass-through filter (such as lambda x: x). This property cannot be
+ deleted.
+ '''
+
+ def __init__(self):
+ path_set.PathSet.__init__(self)
+ self._path_filter = os.path.normpath
+
+ @property
+ def path_filter(self):
+ '''A filter to apply to paths in the tar archive.'''
+ return self._path_filter
+
+ @path_filter.setter
+ def path_filter(self, path_filter):
+ self._path_filter = path_filter or (lambda x: x)
+
+ @path_filter.deleter
+ def path_filter(self):
+ raise Error('path_filter cannot be deleted')
+
+ def InitWithTarFile(self, tar_archive_file):
+ '''Initialize the object using a tar-format archive file.
+
+ Wipes out any old table of contents data and replaces it with the new table
+ of contents from |tar_archive_file|. If the given tar archive doesn't
+ exist, or if it is not in a recognizable format, this method does nothing.
+
+ Args:
+ tar_archive_file: The archive file. This is expected to be a file in a
+ tar format that the python tarfile module recognizes.
+ Raises:
+ OSError if |tar_archive_file| doesn't exist.
+ '''
+ def MakePathSet(condition):
+ '''Helper function used with a lambda to generate a set of path names.'''
+ return set([self.path_filter(tarinfo.name)
+ for tarinfo in tar_archive if condition(tarinfo)])
+
+ def MakeLinksDict(condition):
+ '''Helper function used with a lambda to generate the link dicitonaries.
+
+ Note that accessing tarinfo.linkname raises an exception if
+ the TarInfo member is not a link, which is why there are two separate
+ helper functions.
+ '''
+ return dict([(self.path_filter(tarinfo.name),
+ self.path_filter(tarinfo.linkname))
+ for tarinfo in tar_archive if condition(tarinfo)])
+
+ if not os.path.exists(tar_archive_file):
+ raise OSError('%s does not exist' % tar_archive_file)
+ tar_archive = None
+ try:
+ tar_archive = tarfile.open(tar_archive_file)
+ self.files = MakePathSet(lambda x: x.isfile())
+ self.dirs = MakePathSet(lambda x: x.isdir())
+ self.symlinks = MakeLinksDict(lambda x: x.issym())
+ self.links = MakeLinksDict(lambda x: x.islnk())
+ finally:
+ if tar_archive:
+ tar_archive.close()
+
+ def InitWithManifest(self, tar_manifest_file):
+ '''Parse a tar-style manifest file and return the table of contents.
+
+ Wipes out any old table of contents data and replaces it with the new table
+ of contents from |tar_manifest_file|. If the given manifest file doesn't
+ exist this method does nothing.
+
+ Args:
+ tar_manifest_file: The manifest file. This is expected to be a file that
+ contains the result of tar -tv on the associated tarball.
+ Raises:
+ OSError if |tar_archive_file| doesn't exist.
+ '''
+ # Index values into the manifest entry list. Note that some of these
+ # indices are negative (counting back from the last element), this is
+ # because the intermediate fields (such as date) from tar -tv differ from
+ # platform to platform. The last fields in the link members are always the
+ # same, however. All symlinks end in ['link_src', '->', 'link']; all hard
+ # links end in ['link_src', 'link', 'to', 'link'].
+ PERM_BITS_INDEX = 0
+ # The symbolic link name is the third-from-last entry.
+ SYMLINK_FILENAME_INDEX = -3
+ # The hard link name is the fourth-from-last entry.
+ HLINK_FILENAME_INDEX = -4
+ LAST_ITEM_INDEX = -1
+
+ if not os.path.exists(tar_manifest_file):
+ raise OSError('%s does not exist' % tar_manifest_file)
+ self.Reset()
+ with open(tar_manifest_file) as manifest:
+ for manifest_item in map(lambda line: line.split(), manifest):
+ # Parse a single tar -tv entry in a manifest.
+ # The tar -tv entry is represented as a list of strings. The first
+ # string represents the permission bits; the first bit indicates the
+ # kind of entry. Depending on the kind of entry, other fields represent
+ # the member's path name, link source, etc. An entry list might look
+ # like this:
+ # ['hrwxr-xr-x', 'Administrators/Domain', 'Users', '0', '2011-08-09',
+ # '08:11', 'toolchain/win_x86/bin/i686-nacl-g++.exe', 'link', 'to',
+ # 'toolchain/win_x86/bin/i686-nacl-addr2line.exe']
+ # This example is a hard link from i686-nacl-addr2line.exe to
+ # i686-nacl-g++.exe.
+ file_type = manifest_item[PERM_BITS_INDEX][0]
+ if file_type == 'd':
+ # A directory: the name is the final element of the entry.
+ self.dirs.add(self.path_filter(manifest_item[LAST_ITEM_INDEX]))
+ elif file_type == 'h':
+ # A hard link: the last element is the source of the hard link, and is
+ # entered as the key in the |links| dictionary.
+ link_name = self.path_filter(manifest_item[HLINK_FILENAME_INDEX])
+ self.links[link_name] = self.path_filter(
+ manifest_item[LAST_ITEM_INDEX])
+ elif file_type == 'l':
+ # A symbolic link: the last element is the source of the symbolic
+ # link.
+ link_name = self.path_filter(manifest_item[SYMLINK_FILENAME_INDEX])
+ self.symlinks[link_name] = self.path_filter(
+ manifest_item[LAST_ITEM_INDEX])
+ else:
+ # Everything else is considered a plain file.
+ self.files.add(self.path_filter(manifest_item[LAST_ITEM_INDEX]))
+
diff --git a/native_client_sdk/src/build_tools/tests/__init__.py b/native_client_sdk/src/build_tools/tests/__init__.py
new file mode 100644
index 0000000..395f4ed
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/__init__.py
@@ -0,0 +1,7 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Package for build_tools/tests"""
diff --git a/native_client_sdk/src/build_tools/tests/apply_patch_test.py b/native_client_sdk/src/build_tools/tests/apply_patch_test.py
new file mode 100644
index 0000000..bb15fc3
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/apply_patch_test.py
@@ -0,0 +1,249 @@
+#!/usr/bin/python2.6
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Unit tests for apply_patch.py."""
+
+import os
+import sys
+import unittest
+import tempfile
+
+from build_tools import apply_patch
+
+
+class TestRange(unittest.TestCase):
+ """ Test class _Range. """
+
+ def setUp(self):
+ pass
+
+ def testParseValidRange(self):
+ """ Test _Range.Parse with a valid range. """
+ diff_line = ['@@ -1,10 +5,9 @@']
+ range = apply_patch._Range()
+ range.Parse(diff_line)
+ self.assertEqual(range.src_start_line, 1)
+ self.assertEqual(range.src_line_count, 10)
+ self.assertEqual(range.dest_start_line, 5)
+ self.assertEqual(range.dest_line_count, 9)
+
+ def testParseValidRangeWithDefaults(self):
+ """ Test _Range.Parse with a valid range with default counts. """
+ diff_line = ['@@ -1 +5 @@']
+ range = apply_patch._Range()
+ range.Parse(diff_line)
+ self.assertEqual(range.src_start_line, 1)
+ self.assertEqual(range.src_line_count, 1)
+ self.assertEqual(range.dest_start_line, 5)
+ self.assertEqual(range.dest_line_count, 1)
+
+ def testParseInvalidRanges(self):
+ """ Test _Range.Parse with invalid ranges. """
+ diff_line = ['@@ ,10 +5,9 @@']
+ range = apply_patch._Range()
+ self.assertRaises(apply_patch.Error, range.Parse, diff_line)
+ diff_line = ['@@ -,10 +5,9 @@']
+ range = apply_patch._Range()
+ self.assertRaises(apply_patch.Error, range.Parse, diff_line)
+ diff_line = ['@@ -1,10 5,9 @@']
+ range = apply_patch._Range()
+ self.assertRaises(apply_patch.Error, range.Parse, diff_line)
+ diff_line = ['@ -5,10 +5,9 @@']
+ range = apply_patch._Range()
+ self.assertRaises(apply_patch.Error, range.Parse, diff_line)
+
+
+class TestChangeHunk(unittest.TestCase):
+ """ Test class _ChangeHunk. """
+
+ def setUp(self):
+ self._change_hunk = apply_patch._ChangeHunk()
+
+ def testDiffLineType(self):
+ """ Test function _ChangeHunk._DiffLineType. """
+ diff_line_pairs = [
+ [' a contextual line', apply_patch.CONTEXTUAL_DIFF_LINE],
+ ['\n', apply_patch.CONTEXTUAL_DIFF_LINE],
+ ['\r', apply_patch.CONTEXTUAL_DIFF_LINE],
+ [ '\n\r', apply_patch.CONTEXTUAL_DIFF_LINE],
+ ['+one added line', apply_patch.ADDED_DIFF_LINE],
+ ['-one delete line', apply_patch.DELETED_DIFF_LINE],
+ ['not valid', apply_patch.NOT_A_DIFF_LINE],
+ ['---not valid', apply_patch.NOT_A_DIFF_LINE],
+ ['+++not valid', apply_patch.NOT_A_DIFF_LINE],
+ ]
+ for pair in diff_line_pairs:
+ self.assertEqual(self._change_hunk._DiffLineType(pair[0]), pair[1])
+
+ def testParseWithValidLines(self):
+ """ Test function _ChangeHunk.Parse with valid diff lines. """
+ diff_lines = [
+ '@@ -5,4 +10,4 @@',
+ ' A contextual line.',
+ '-A line deleted from source.',
+ '-Another deleted line.',
+ '\r', # An empty contextual line.
+ '+A line added to destination.',
+ '+Another added line.',
+ '--- Begin next header.',
+ ]
+ num_input_lines = len(diff_lines)
+ self._change_hunk.Parse(diff_lines)
+
+ self.assertEqual(self._change_hunk.range.src_start_line, 5)
+ self.assertEqual(self._change_hunk.range.src_line_count, 4)
+ self.assertEqual(self._change_hunk.range.dest_start_line, 10)
+ self.assertEqual(self._change_hunk.range.dest_line_count, 4)
+ self.assertEqual(len(self._change_hunk.lines), num_input_lines - 2)
+
+ def testParseWithInvalidLines(self):
+ """ Test function _ChangeHunk.Parse with invalid diff lines. """
+ diff_lines = [
+ '@@ -5,4 +10,4 @@',
+ ' A contextual line.',
+ '-Another deleted line.',
+ '\r', # An empty contextual line.
+ '+A line added to destination.',
+ '+Another added line.',
+ '--- Begin next header.',
+ ]
+ num_input_lines = len(diff_lines)
+ self.assertRaises(apply_patch.Error, self._change_hunk.Parse, diff_lines)
+
+ def testApply(self):
+ """ Test function _ChangeHunk.Apply. """
+ with tempfile.SpooledTemporaryFile() as in_file:
+ with tempfile.SpooledTemporaryFile() as out_file:
+ in_file.write('aaaaaaaaaa\n')
+ in_file.write('bbbbbbbbbb\n')
+ in_file.seek(0)
+
+ diff_lines = [
+ '@@ -1,2 +1,2 @@',
+ ' aaaaaaaaaa\n',
+ '-bbbbbbbbbb\n',
+ '+cccccccccc\n',
+ '--- Begin next header.\n',
+ ]
+
+ self._change_hunk.Parse(diff_lines)
+ self._change_hunk.Apply(0, in_file, out_file)
+ out_file.seek(0)
+ self.assertEqual(out_file.readline(), 'aaaaaaaaaa\n');
+ self.assertEqual(out_file.readline(), 'cccccccccc\n');
+
+class TestPatchHeader(unittest.TestCase):
+ """ Test class _PatchHeader. """
+
+ def setUp(self):
+ self._header = apply_patch._PatchHeader()
+
+ def testParseValidHeader(self):
+ """ Test function _PatchHeader.Parse with a valid header. """
+ diff_lines = [
+ '--- dir/file.txt 1969-12-31 17:00:00.000000000 -0700\n',
+ '+++ dir/file_new.txt 2010-07-08 09:49:37.000000000 -0600\n'
+ ]
+ self._header.Parse(diff_lines)
+ self.assertEqual(self._header.in_file_name, 'dir/file.txt')
+ self.assertEqual(self._header.out_file_name, 'dir/file_new.txt')
+
+ def testParseInvalidHeader(self):
+ """ Test function _PatchHeader.Parse with invalid headers. """
+ diff_lines = [
+ '-- dir/file.txt 1969-12-31 17:00:00.000000000 -0700\n',
+ '+++ dir/file_new.txt 2010-07-08 09:49:37.000000000 -0600\n'
+ ]
+ self.assertRaises(apply_patch.Error, self._header.Parse, diff_lines)
+ diff_lines = [
+ '---\n',
+ '+++ dir/file_new.txt 2010-07-08 09:49:37.000000000 -0600\n'
+ ]
+ self.assertRaises(apply_patch.Error, self._header.Parse, diff_lines)
+ diff_lines = [
+ '-- dir/file.txt 1969-12-31 17:00:00.000000000 -0700\n',
+ '+++\n'
+ ]
+ self.assertRaises(apply_patch.Error, self._header.Parse, diff_lines)
+ diff_lines = [
+ '@@ dir/file.txt 1969-12-31 17:00:00.000000000 -0700\n',
+ '+++ dir/file_new.txt 2010-07-08 09:49:37.000000000 -0600\n'
+ ]
+ self.assertRaises(apply_patch.Error, self._header.Parse, diff_lines)
+ diff_lines = [
+ '--- dir/file.txt 1969-12-31 17:00:00.000000000 -0700\n',
+ '@@ dir/file_new.txt 2010-07-08 09:49:37.000000000 -0600\n'
+ ]
+ self.assertRaises(apply_patch.Error, self._header.Parse, diff_lines)
+
+class TestPatch(unittest.TestCase):
+ """ Test class _Patch. """
+
+ TEST_DIR = 'apply_patch_test_archive'
+ TEST_FILE = 'original_file.txt'
+ TEST_DATA = [
+ 'aaaaaaaaaa\n',
+ 'bbbbbbbbbb\n',
+ 'cccccccccc\n',
+ 'dddddddddd\n',
+ 'eeeeeeeeee\n',
+ 'ffffffffff\n',
+ ]
+
+ def setUp(self):
+ self._patch = apply_patch._Patch()
+
+ def testParseAndApply(self):
+ """ Test function _Patch.Parse. """
+ diff_lines = [
+ '--- ' + TestPatch.TEST_DIR + '/' + TestPatch.TEST_FILE + '\n',
+ '+++ ' + TestPatch.TEST_DIR + '/new' + TestPatch.TEST_FILE + '\n',
+ '@@ -1,0 +1,1 @@\n',
+ '+zzzzzzzzzz\n',
+ '@@ -3,2 +3,2 @@\n',
+ ' cccccccccc\n',
+ '-dddddddddd\n',
+ '+----------\n',
+ ]
+ self._patch.Parse(diff_lines)
+
+ # Create the original data file.
+ script_dir = os.path.dirname(__file__)
+ test_dir = os.path.join(script_dir, TestPatch.TEST_DIR)
+ file_path = os.path.join(test_dir, TestPatch.TEST_FILE)
+ if not os.path.exists(test_dir):
+ os.mkdir(test_dir)
+ with open(file_path, 'w+b') as file:
+ file.truncate(0)
+ file.writelines(TestPatch.TEST_DATA)
+
+ # Apply the patch and verify patched file
+ patched_data = [
+ 'zzzzzzzzzz\n',
+ 'aaaaaaaaaa\n',
+ 'bbbbbbbbbb\n',
+ 'cccccccccc\n',
+ '----------\n',
+ 'eeeeeeeeee\n',
+ 'ffffffffff\n',
+ ]
+ self._patch.Apply(script_dir)
+ with open(file_path) as file:
+ self.assertEqual(file.readline(), patched_data.pop(0))
+
+
+def RunTests():
+
+ outcome = True
+ for test_class in [TestRange, TestChangeHunk, TestPatchHeader, TestPatch]:
+ suite = unittest.TestLoader().loadTestsFromTestCase(test_class)
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+ outcome = outcome and result.wasSuccessful()
+
+ return int(not outcome)
+
+if __name__ == '__main__':
+ sys.exit(RunTests())
diff --git a/native_client_sdk/src/build_tools/tests/build_utils_test.py b/native_client_sdk/src/build_tools/tests/build_utils_test.py
new file mode 100755
index 0000000..1347d48
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/build_utils_test.py
@@ -0,0 +1,104 @@
+#!/usr/bin/python2.6
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Unit tests for build_utils.py."""
+
+__author__ = 'mball@google.com (Matt Ball)'
+
+import platform
+import os
+import subprocess
+import sys
+import tarfile
+import unittest
+
+from build_tools import build_utils
+import mox
+
+
+class TestBuildUtils(unittest.TestCase):
+ """This class tests basic functionality of the build_utils package"""
+ def setUp(self):
+ self.mock_factory = mox.Mox()
+
+ def testArchitecture(self):
+ """Testing the Architecture function"""
+ bit_widths = build_utils.SupportedNexeBitWidths()
+ # Make sure word-width of either 32 or 64.
+ self.assertTrue(32 in bit_widths or 64 in bit_widths)
+ if sys.platform in ['linux', 'linux2']:
+ self.assertTrue(32 in bit_widths)
+ if '64' in platform.machine():
+ self.assertTrue(64 in bit_widths)
+ elif sys.platform == 'darwin':
+ # Mac should have both 32- and 64-bit support.
+ self.assertTrue(32 in bit_widths)
+ self.assertTrue(64 in bit_widths)
+ else:
+ # Windows supports either 32- or 64-bit, but not both.
+ self.assertEqual(1, len(bit_widths))
+
+ def testBotAnnotatorPrint(self):
+ """Testing the Print function of the BotAnnotator class"""
+ stdout_mock = self.mock_factory.CreateMock(sys.stdout)
+ stdout_mock.write("My Bot Message\n")
+ stdout_mock.flush()
+ stdout_mock.write("BUILD_STEP MyBuildStep\n")
+ stdout_mock.flush()
+ self.mock_factory.ReplayAll()
+ bot = build_utils.BotAnnotator(stdout_mock)
+ bot.Print("My Bot Message")
+ bot.BuildStep("MyBuildStep")
+ self.mock_factory.VerifyAll()
+
+ def testBotAnnotatorRun(self):
+ """Testing the 'Run' command of the BotAnnotator class"""
+ out_string = 'hello'
+ print_command = ['python', '-c',
+ "import sys; sys.stdout.write('%s')" % out_string]
+ error_command = ['python', '-c', "import sys; sys.exit(1)"]
+ stdout_mock = self.mock_factory.CreateMock(sys.stdout)
+ stdout_mock.write('Running %s\n' % print_command)
+ stdout_mock.flush()
+ stdout_mock.write('%s\n' % out_string)
+ stdout_mock.flush()
+ stdout_mock.write('Running %s\n' % error_command)
+ stdout_mock.flush()
+ stdout_mock.write('\n')
+ stdout_mock.flush()
+ self.mock_factory.ReplayAll()
+ bot = build_utils.BotAnnotator(stdout_mock)
+ run_output = bot.Run(print_command)
+ self.assertEqual(run_output, "%s" % out_string)
+ self.assertRaises(subprocess.CalledProcessError, bot.Run, error_command)
+ self.mock_factory.VerifyAll()
+
+ def testJoinPathToNaClRepo(self):
+ """Testing the 'JoinPathToNaClRepo' utility function."""
+ # Test an empty arg list.
+ test_dir = os.path.join('third_party', 'native_client')
+ self.assertEqual(test_dir, build_utils.JoinPathToNaClRepo())
+ # Test an empty arg list with just the root_dir key set.
+ test_dir = os.path.join('test_root', test_dir)
+ self.assertEqual(test_dir,
+ build_utils.JoinPathToNaClRepo(root_dir='test_root'))
+ # Test non-empty arg lists and with and without root_dir.
+ test_dir = os.path.join('third_party', 'native_client', 'testing', 'file')
+ self.assertEqual(test_dir,
+ build_utils.JoinPathToNaClRepo('testing', 'file'))
+ test_dir = os.path.join('test_root', test_dir)
+ self.assertEqual(test_dir,
+ build_utils.JoinPathToNaClRepo('testing', 'file', root_dir='test_root'))
+
+
+def RunTests():
+ suite = unittest.TestLoader().loadTestsFromTestCase(TestBuildUtils)
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+
+ return int(not result.wasSuccessful())
+
+if __name__ == '__main__':
+ sys.exit(RunTests())
diff --git a/native_client_sdk/src/build_tools/tests/fake_gsutil.bat b/native_client_sdk/src/build_tools/tests/fake_gsutil.bat
new file mode 100644
index 0000000..86cc640
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/fake_gsutil.bat
@@ -0,0 +1,3 @@
+@echo off
+rem Simple Wrapper function to allow running fake_gsutil on Windows
+python "%~dp0fake_gsutil.py" %*
diff --git a/native_client_sdk/src/build_tools/tests/fake_gsutil.py b/native_client_sdk/src/build_tools/tests/fake_gsutil.py
new file mode 100755
index 0000000..f145162
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/fake_gsutil.py
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''Fake implementation of gsutils, which is the utility used to upload files
+to commondatastorage'''
+
+import optparse
+import sys
+
+
+def HandleLS(args):
+ if len(args) == 0:
+ print 'gs://nativeclient-upload/'
+ return 0
+ print ('InvalidUriError: Attempt to get key for "%s" failed. '
+ 'This probably indicates the URI is invalid.' % args[0])
+ return 1
+
+
+def UnknownCommand(args):
+ return 0
+
+
+def HandleCP(args):
+ return 0
+
+
+def main(args):
+ if len(args) == 0:
+ return 0
+ COMMANDS = {
+ 'ls': HandleLS,
+ 'cp': HandleCP,
+ }
+ return COMMANDS.get(args[0], UnknownCommand)(args[1:])
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/native_client_sdk/src/build_tools/tests/install_nsis_test.py b/native_client_sdk/src/build_tools/tests/install_nsis_test.py
new file mode 100644
index 0000000..b9d0c34
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/install_nsis_test.py
@@ -0,0 +1,103 @@
+#!/usr/bin/python2.6
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Unit tests for install_nsis.py."""
+
+import os
+import shutil
+import subprocess
+import sys
+import unittest
+
+from build_tools import install_nsis
+
+
+class TestInstallNsis(unittest.TestCase):
+ """This class tests basic functionality of the install_nsis package"""
+ def setUp(self):
+ self.nsis_installer_ = os.path.join(os.path.abspath('build_tools'),
+ install_nsis.NSIS_INSTALLER)
+ self.target_dir_ = os.path.join(os.path.dirname(self.nsis_installer_),
+ 'nsis_test',
+ 'NSIS')
+ def tearDown(self):
+ shutil.rmtree(os.path.dirname(self.target_dir_), ignore_errors=True)
+
+ def testNsisInstallerExists(self):
+ """Ensure that the correct version of NSIS is present."""
+ self.assertTrue(os.path.exists(self.nsis_installer_))
+
+ def testBogusNsisInstaller(self):
+ """Make sure the installer handles invalid directory names."""
+ self.assertRaises(IOError, install_nsis.InstallNsis, 'bogus', 'not_a_dir')
+
+ def testNsisInstaller(self):
+ """Make sure the installer produces an NSIS directory."""
+ install_nsis.InstallNsis(self.nsis_installer_, self.target_dir_)
+ self.assertTrue(os.path.exists(os.path.join(self.target_dir_,
+ 'makensis.exe')))
+
+ def testAccessControlExtensions(self):
+ """Make sure that the AccessControl extensions can be installed."""
+ script_dir = os.path.dirname(self.nsis_installer_)
+ install_nsis.InstallAccessControlExtensions(
+ script_dir,
+ os.path.join(script_dir, install_nsis.ACCESS_CONTROL_ZIP),
+ self.target_dir_)
+ self.assertTrue(os.path.exists(
+ os.path.join(self.target_dir_, 'Plugins', 'AccessControl.dll')))
+ self.assertTrue(os.path.exists(
+ os.path.join(self.target_dir_, 'Plugins', 'AccessControlW.dll')))
+
+ def testMkLinkExtensions(self):
+ """Make sure the MkLink extensions are installed."""
+ script_dir = os.path.dirname(self.nsis_installer_)
+ install_nsis.InstallMkLinkExtensions(
+ os.path.join(script_dir, install_nsis.MKLINK_DLL),
+ self.target_dir_)
+ self.assertTrue(os.path.exists(
+ os.path.join(self.target_dir_, 'Plugins', 'MkLink.dll')))
+
+ def testForceTargetInstall(self):
+ """Test that a force install to a target directory works."""
+ try:
+ # Mock the NSIS install directories so that Install() thinks NSIS is
+ # already installed.
+ os.makedirs(os.path.join(self.target_dir_, 'Plugins'), mode=0777)
+ except OSError:
+ pass
+ self.assertFalse(os.path.exists(os.path.join(self.target_dir_,
+ 'makensis.exe')))
+ self.assertFalse(os.path.exists(
+ os.path.join(self.target_dir_, 'Plugins', 'AccessControl.dll')))
+ self.assertFalse(os.path.exists(
+ os.path.join(self.target_dir_, 'Plugins', 'AccessControlW.dll')))
+ self.assertFalse(os.path.exists(
+ os.path.join(self.target_dir_, 'Plugins', 'MkLink.dll')))
+
+ install_nsis.Install(os.path.dirname(self.nsis_installer_),
+ target_dir=self.target_dir_,
+ force=True)
+
+ self.assertTrue(os.path.exists(os.path.join(self.target_dir_,
+ 'makensis.exe')))
+ self.assertTrue(os.path.exists(
+ os.path.join(self.target_dir_, 'Plugins', 'AccessControl.dll')))
+ self.assertTrue(os.path.exists(
+ os.path.join(self.target_dir_, 'Plugins', 'AccessControlW.dll')))
+ self.assertTrue(os.path.exists(
+ os.path.join(self.target_dir_, 'Plugins', 'MkLink.dll')))
+
+
+def RunTests():
+ suite = unittest.TestLoader().loadTestsFromTestCase(TestInstallNsis)
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+
+ return int(not result.wasSuccessful())
+
+
+if __name__ == '__main__':
+ sys.exit(RunTests())
diff --git a/native_client_sdk/src/build_tools/tests/installer_contents_test.py b/native_client_sdk/src/build_tools/tests/installer_contents_test.py
new file mode 100755
index 0000000..79858a7
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/installer_contents_test.py
@@ -0,0 +1,66 @@
+#!/usr/bin/python2.6
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Unit tests for installer_contents.py."""
+
+import os
+import sys
+import unittest
+
+from build_tools import installer_contents
+
+
+class TestInstallerContents(unittest.TestCase):
+ """This class tests basic functionality of the installer_contents package"""
+ def setUp(self):
+ self.mock_path_list = ['file',
+ 'file/in/a/path',
+ 'dir/spec/',
+ '',
+ '/abs/path',
+ ]
+
+
+ def testConvertToOSPaths(self):
+ output = installer_contents.ConvertToOSPaths(self.mock_path_list)
+ self.assertEqual(len(output), len(self.mock_path_list))
+ self.assertEqual(output[0], 'file')
+ self.assertEqual(output[1], os.path.join('file', 'in', 'a', 'path'))
+ self.assertEqual(output[2], os.path.join('dir', 'spec', ''))
+ self.assertEqual(output[3], '')
+ self.assertEqual(output[4], os.path.join('abs', 'path'))
+
+ def testGetDirectoriesFromPathList(self):
+ output = installer_contents.GetDirectoriesFromPathList(self.mock_path_list)
+ self.assertEqual(1, len(output))
+ self.assertEqual(output[0], os.path.join('dir', 'spec', ''))
+
+ def testGetFilesFromPathList(self):
+ output = installer_contents.GetFilesFromPathList(self.mock_path_list)
+ self.assertEqual(4, len(output))
+ self.assertEqual(output[0], 'file')
+ self.assertEqual(output[1], os.path.join('file', 'in', 'a', 'path'))
+ self.assertEqual(output[2], '')
+ self.assertEqual(output[3], os.path.join('abs', 'path'))
+
+ def testGetToolchainManifest(self):
+ self.assertRaises(KeyError,
+ installer_contents.GetToolchainManifest,
+ 'notatoolchain')
+ newlib_manifest_path = installer_contents.GetToolchainManifest('newlib')
+ self.assertTrue(os.path.exists(newlib_manifest_path))
+ glibc_manifest_path = installer_contents.GetToolchainManifest('glibc')
+ self.assertTrue(os.path.exists(glibc_manifest_path))
+
+
+def RunTests():
+ suite = unittest.TestLoader().loadTestsFromTestCase(TestInstallerContents)
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+
+ return int(not result.wasSuccessful())
+
+if __name__ == '__main__':
+ sys.exit(RunTests())
diff --git a/native_client_sdk/src/build_tools/tests/installer_test.py b/native_client_sdk/src/build_tools/tests/installer_test.py
new file mode 100755
index 0000000..3026619
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/installer_test.py
@@ -0,0 +1,480 @@
+#!/usr/bin/python
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+""" Tests for the SDK Installer
+
+The general structure is as follows:
+
+ 1. Extract the installer into a given temporary directory
+ 2. Run tests -- See TestSDK class (e.g., testExamples)
+ 3. Remove the installer directory
+"""
+
+from __future__ import with_statement
+
+import cStringIO
+import datetime
+import httplib
+import optparse
+import os
+import platform
+import shutil
+import socket
+import string
+import subprocess
+import sys
+import time
+import unittest
+import urllib
+import zipfile
+
+from build_tools import build_utils
+from build_tools import html_checker
+from build_tools.sdk_tools import sdk_update
+from build_tools.sdk_tools import update_manifest
+
+annotator = build_utils.BotAnnotator()
+
+
+def TestingClosure(_outdir, _jobs):
+ '''This closure provides the variables needed by the various tests
+
+ Args:
+ _outdir: The output directory that holds the extracted installer
+ _jobs: Number of parallel jobs for Make or Scons
+
+ Returns:
+ A TestCase class that can be used by the unittest loader
+ '''
+ toolchain_base = build_utils.NormalizeToolchain(base_dir=_outdir)
+ toolchain_bin = os.path.join(toolchain_base, 'bin')
+ toolchain_runtime = os.path.join(toolchain_base, 'runtime')
+ gcc64 = os.path.join(toolchain_bin, 'x86_64-nacl-gcc')
+ sel_ldr64 = os.path.join(toolchain_bin, 'sel_ldr_x86_64')
+ irt_core64 = os.path.join(toolchain_runtime, 'irt_core_x86_64.nexe')
+ scons = 'scons.bat' if sys.platform == 'win32' else 'scons'
+ staging_path = os.path.join(_outdir, 'staging')
+
+ class TestSDK(unittest.TestCase):
+ '''Contains tests that run within an extracted SDK installer'''
+
+ def GetBotShellEnv(self):
+ '''Massage |env| so its env variables will work on the bots.'''
+ env = os.environ.copy()
+ env['NACL_TARGET_PLATFORM'] = '.' # Use the repo's toolchain.
+ env['NACL_PROJECT_ROOT'] = _outdir # Put project templates here.
+ env['NACL_SDK_ROOT'] = _outdir
+ return env
+
+ def SconsCommand(self, scons_path=''):
+ '''Helper to cons up a scons command. Sets nacl platform to '.' so that
+ SCons uses the repo's toolchain.
+ '''
+ return [os.path.join(scons_path, scons),
+ '-j', _jobs,
+ '--nacl-platform=.'
+ ]
+
+ def buildExamplesWithFlags(self, flags=None):
+ '''A small helper function that runs scons with arch and variant flags.
+
+ Args:
+ flags: Any extra flags to pass to scons. Must be an array,
+ can be empty.
+ '''
+ flags = flags or []
+ path = os.path.join(_outdir, 'examples')
+ command = self.SconsCommand(path) + flags
+ env = self.GetBotShellEnv()
+ annotator.Run(command, cwd=path, env=env)
+
+ def testBuildExamplesVariant(self):
+ '''Verify non-default toolchain SDK example build.'''
+
+ # Note that --nacl-platform is set to ".". This is done so that the bots
+ # will use the repo's toolchain, instead of a platform-specific one.
+ # There are no platform-specific toolchains in the repo.
+ self.buildExamplesWithFlags(['--architecture=x86',
+ '--variant=newlib'])
+ self.buildExamplesWithFlags(['--architecture=x86',
+ '--variant=glibc'])
+ print "Test with bogus architectures, variants."
+ print "We expect these tests to throw exceptions:"
+ self.assertRaises(subprocess.CalledProcessError,
+ self.buildExamplesWithFlags,
+ flags=['--architecture=nosucharch'])
+ self.assertRaises(subprocess.CalledProcessError,
+ self.buildExamplesWithFlags,
+ flags=['--variant=nosuchvariant'])
+
+ def testStagedHtmlFiles(self):
+ self.buildExamplesWithFlags(['--architecture=x86', '--variant=glibc'])
+ html_checker.ValidateAllLinks([os.path.join(staging_path, 'index.html')])
+
+ def testReadMe(self):
+ '''Check that the current build version and date are in the README file'''
+
+ filename = 'README.txt' if sys.platform == 'win32' else 'README'
+ with open(os.path.join(_outdir, filename), 'r') as file:
+ contents = file.read()
+ version = 'Version: %s' % build_utils.PLATFORM_VERSION
+ annotator.Print('Checking that SDK version = %s' % version)
+ self.assertTrue(contents.count(version) == 1,
+ 'Version mismatch in %s' % filename)
+ revision = 'Revision: %s' % str(build_utils.SVNRevision())
+ annotator.Print('Checking that SDK revision = %s' % revision)
+ self.assertTrue(contents.count(revision) == 1,
+ 'Revision mismatch in %s' % filename)
+
+ # Check that the README contains either the current date or yesterday's
+ # date (which happens when building over midnight)
+ self.assertEqual(
+ 1,
+ contents.count(str(datetime.date.today())) +
+ contents.count(str(datetime.date.today() -
+ datetime.timedelta(days=1))),
+ "Cannot find today's or yesterday's date in README")
+
+ def testRunHelloWorldUnittest(self):
+ '''Verify that we can build and run the hello_world unit test.'''
+ bit_widths = build_utils.SupportedNexeBitWidths()
+ test_targets = ['test%d' % bits for bits in bit_widths]
+
+ if len(test_targets) == 0:
+ annotator.Print('No test targets found')
+ return
+ print 'running targets: %s' % str(test_targets)
+ path = os.path.join(_outdir, 'examples', 'hello_world')
+ command = self.SconsCommand(os.path.join(path, '..')) + test_targets
+ env = self.GetBotShellEnv()
+ annotator.Run(command, cwd=path, env=env)
+
+ def testHttpd(self):
+ '''Test the simple HTTP server.
+
+ Run the simple server and make sure it quits when processing an URL that
+ has the ?quit=1 parameter set. This test runs the server on the default
+ port (5103) and on a specified port.
+ '''
+
+ DEFAULT_SERVER_PORT = 5103
+
+ def runAndQuitHttpServer(port=DEFAULT_SERVER_PORT,
+ alternate_cwd=None,
+ extra_args=[],
+ should_fail=False):
+ '''A small helper function to launch the simple HTTP server.
+
+ This function launches the simple HTTP server, then waits for its
+ banner output to appear. If the banner doesn't appear within 10
+ seconds, the test fails. The banner is checked validate that it
+ displays the right port number.
+
+ Once the server is verified as running, this function sends it a GET
+ request with the ?quit=1 URL parmeter. It then waits to see if the
+ server process exits with a return code of 0. If the server process
+ doesn't exit within 20 seconds, the test fails.
+
+ Args:
+ port: The port to use, defaults to 5103.
+ '''
+ path = staging_path
+ command = [sys.executable, os.path.join(path, 'httpd.py')]
+ # Add the port only if it's not the default.
+ if port != DEFAULT_SERVER_PORT:
+ command += [str(port)]
+ command += extra_args
+ # Can't use annotator.Run() because the HTTP server doesn't stop, which
+ # causes Run() to hang.
+ annotator.Print('Starting server: %s' % command)
+ annotator.Print('extra_args=%s' % str(extra_args))
+ current_working_dir = path if alternate_cwd is None else alternate_cwd
+ annotator.Print('cwd=%s' % current_working_dir)
+ process = subprocess.Popen(command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ cwd=current_working_dir,
+ env=self.GetBotShellEnv())
+ self.assertNotEqual(None, process)
+ # Wait until the process starts by trying to send it a GET request until
+ # the server responds, or until the timeout expires. If the timeout
+ # expires, fail the test.
+ time_start = time.time()
+ time_now = time_start
+ timeout_time = time_start + 20 # 20 sec timeout.
+ output = ''
+ conn = None
+ while time_now < timeout_time:
+ conn = httplib.HTTPConnection('localhost', port)
+ try:
+ # Send the quit request.
+ conn.request("GET", "/?quit=1")
+ output = process.stdout.readline()
+ break
+ except socket.error:
+ # If the server is not listening for connections, then
+ # HTTPConnetion() raises a socket exception, not one of the
+ # exceptions defined in httplib. In order to resend a request in
+ # this case, the original connection has to be closed and re-opened.
+ conn.close()
+ conn = None
+ time.sleep(1) # Wait a second to try again.
+ time_now = time.time()
+
+ # If we expect the test to fail (e.g. bad directory without
+ # --no_dir_check) then there should be no connection.
+ if should_fail:
+ self.assertEqual(None, conn)
+ return_code = process.poll()
+ # If the process has not terminated, return_code will be None
+ # but since the server should have failed to launch, it should
+ # have terminated by now.
+ self.assertNotEqual(return_code, None)
+ return
+
+ self.assertNotEqual(None, conn)
+ # Validate the first line of the startup banner. An example of the
+ # full line is:
+ # INFO:root:Starting local server on port 5103
+ self.assertTrue(output.startswith('INFO:root:Starting'))
+ self.assertEqual(1, output.count(str(port)))
+ annotator.Print('Server startup banner: %s' % output)
+
+ # Close down the connection and wait for the server to quit.
+ conn.getresponse()
+ conn.close()
+
+ # Check to see if the server quit properly. It should quit within
+ # 0.5 seconds, so if the first poll() indicates that the process is
+ # still running, wait 1 sec and then poll again. If the process is
+ # still running after 20 sec, then fail the test.
+ return_code = process.poll()
+ poll_count = 0
+ while return_code == None and poll_count < 20:
+ time.sleep(1)
+ return_code = process.poll()
+ poll_count += 1
+ self.assertEqual(0, return_code)
+
+ runAndQuitHttpServer()
+ runAndQuitHttpServer(5280)
+ # Make sure server works outside examles with --no_dir_check.
+ runAndQuitHttpServer(5281, alternate_cwd=_outdir,
+ extra_args=['--no_dir_check'])
+ # Make sure it works in examples with --no_dir_check.
+ runAndQuitHttpServer(5281, extra_args=['--no_dir_check'])
+ # Make sure the test fails if --no_dir_check is left out and the CWD
+ # is not examples.
+ runAndQuitHttpServer(5281, alternate_cwd=_outdir, should_fail=True)
+ # Retest port 5281 with the default parameters.
+ runAndQuitHttpServer(5281)
+
+ def testProjectTemplates(self):
+ '''Create and build projects from project_templates.'''
+
+ def initAndCompileProject(project_name, flags=[]):
+ '''A small helper function that runs init_project.py and then runs
+ a scons build in the resulting directory.
+
+ Args:
+ project_name: The project's name, set the --name= parameter for
+ init_project to this value.
+ flags: Any extra flags to pass to init_project. Must be an array,
+ can be empty.
+ '''
+ path = os.path.join(_outdir, 'project_templates')
+ project_path = os.path.join(_outdir, project_name)
+ scons_command = self.SconsCommand(project_path)
+ init_project_command = [sys.executable,
+ 'init_project.py',
+ '--name=%s' % project_name,
+ '--nacl-platform=.'] + flags
+ env = self.GetBotShellEnv()
+ annotator.Run(init_project_command, cwd=path, env=env)
+ annotator.Run(scons_command, cwd=project_path, env=env)
+
+ initAndCompileProject('test_c_project', flags=['-c'])
+ initAndCompileProject('test_cc_project')
+ # Calling init_project again with the same names should cause an error
+ # because the project already exists, and we don't overwrite projects.
+ print "Rerunning init_project again to test overwriting previous project."
+ print "We expect these tests to throw exceptions:"
+ self.assertRaises(subprocess.CalledProcessError,
+ initAndCompileProject,
+ 'test_c_project',
+ flags=['-c'])
+ self.assertRaises(subprocess.CalledProcessError,
+ initAndCompileProject,
+ 'test_cc_project')
+
+ # valgrind is deprecated as of pepper_17.
+ def deprecatedTestValgrind(self):
+ '''Verify that Valgrind works properly (Linux 64-bit only)'''
+
+ bit_widths = build_utils.SupportedNexeBitWidths()
+ if (not sys.platform.startswith('linux')) or (64 not in bit_widths):
+ annotator.Print('Not running on 64-bit Linux -- skip')
+ return
+ true_basename = os.path.join(_outdir, 'true')
+ true_c_filename = '%s.c' % true_basename
+ true_nexe_filename = '%s.nexe' % true_basename
+ with open(true_c_filename, 'w') as true_file:
+ true_file.write('int main(void) { return 0; }\n')
+ annotator.Run([gcc64, '-o', true_nexe_filename, '-m64', '-O0',
+ '-Wl,-u,have_nacl_valgrind_interceptors', '-g',
+ true_c_filename, '-lvalgrind'])
+ memcheck = os.path.join(_outdir, 'third_party', 'valgrind', 'memcheck.sh')
+ annotator.Run([memcheck, sel_ldr64,
+ # The -B flag is needed by sel_ldr to find an Integrated
+ # Runtime that it can use to run .nexes.
+ '-B', irt_core64,
+ '-Q', true_nexe_filename])
+
+ return TestSDK
+
+
+def ExtractInstaller(installer, outdir, bundle_name, nacl_sdk):
+ '''Extract the SDK installer into a given directory
+
+ If the outdir already exists, then this function deletes it
+
+ Args:
+ installer: full path of the SDK installer
+ outdir: output directory where to extract the installer
+ bundle_name: name of sdk bundle within outdir
+ nacl_sdk: filename of nacl_sdk tarball
+
+ Raises:
+ OSError - if the outdir already exists
+ CalledProcessError - if the extract operation fails
+ '''
+
+ annotator.Print('Extracting installer %s into %s' % (installer, outdir))
+
+ if os.path.exists(outdir):
+ RemoveDir(outdir)
+
+ os.mkdir(outdir)
+
+ if sys.platform == 'win32':
+ zip_file = None
+ try:
+ zip_file = zipfile.ZipFile(nacl_sdk)
+ zip_file.extractall(path=outdir)
+ finally:
+ if zip_file:
+ zip_file.close()
+ else:
+ # Unfortunately, the zipfile module does not retain file permissions
+ # when extracting executable scripts. For now, just use the unzip
+ # that comes with Linux and Mac.
+ subprocess.check_call(['unzip', nacl_sdk], cwd=outdir)
+
+ outdir = os.path.join(outdir, 'nacl_sdk')
+
+ manifest_filename = os.path.join(outdir, 'test_manifest.json')
+ update_manifest_options = [
+ '--bundle-revision=1',
+ '--bundle-version=2',
+ '--description=installer_test bundle',
+ '--%s-archive=file://%s' % (
+ sdk_update.GetHostOS(),
+ urllib.pathname2url(os.path.abspath(installer))),
+ '--bundle-name=%s' % bundle_name,
+ '--recommended=yes',
+ '--stability=stable',
+ '--manifest-version=1',
+ '--manifest-file=%s' % manifest_filename]
+ annotator.Print('Running update manifest with %s' % update_manifest_options)
+ if 0 != update_manifest.main(update_manifest_options):
+ raise Exception('update_manifest terminated abnormally.')
+
+ naclsdk_options = [
+ os.path.join(outdir,
+ 'naclsdk.bat' if sys.platform == 'win32' else 'naclsdk'),
+ '--manifest-url=file://%s' % urllib.pathname2url(manifest_filename),
+ '--sdk-root-dir=%s' % outdir,
+ '--user-data-dir=%s' % outdir,
+ 'update']
+ annotator.Print('Running naclsdk with %s' % naclsdk_options)
+ subprocess.check_call(naclsdk_options)
+
+
+def RemoveDir(outdir):
+ '''Removes the given directory
+
+ On Unix systems, this just runs shutil.rmtree, but on Windows, this doesn't
+ work when the directory contains junctions (as does our SDK installer).
+ Therefore, on Windows, it runs rmdir /S /Q as a shell command. This always
+ does the right thing on Windows.
+
+ Args:
+ outdir: The directory to delete
+
+ Raises:
+ CalledProcessError - if the delete operation fails on Windows
+ OSError - if the delete operation fails on Linux
+ '''
+
+ annotator.Print('Removing %s' % outdir)
+ if sys.platform == 'win32':
+ subprocess.check_call(['rmdir /S /Q', outdir], shell=True)
+ else:
+ shutil.rmtree(outdir)
+
+
+def main():
+ '''Main entry for installer tests
+
+ Returns:
+ 0: Success
+ 1: Failure
+
+ Also, raises various exceptions for error conditions.
+ '''
+
+ BUNDLE_NAME = 'test_sdk_bundle'
+ parser = optparse.OptionParser(
+ usage='Usage: %prog [options] sdk_installer')
+ parser.add_option(
+ '-o', '--outdir', dest='outdir', default='sdk_temp_dir',
+ help='temporary output directory for holding the installer')
+ parser.add_option(
+ '-j', '--jobs', dest='jobs', default=1,
+ help='number of parallel jobs to run')
+ parser.add_option(
+ '-n', '--nacl-sdk', dest='nacl_sdk', default='nacl_sdk.zip',
+ help='location of the nacl_sdk tarball')
+ parser.add_option(
+ '-s', '--sdk-tools', dest='sdk_tools', default='sdk_tools.tgz',
+ help='location of the sdk_tools tarball')
+
+ options, args = parser.parse_args()
+
+ if len(args) == 0:
+ parser.error('Must provide an sdk_installer')
+
+ if len(args) > 1:
+ parser.error('Must provide only one sdk_installer')
+
+ installer = args[0]
+ outdir = os.path.abspath(options.outdir)
+
+ annotator.Print("Running with installer = %s, outdir = %s, jobs = %s" % (
+ installer, outdir, options.jobs))
+ ExtractInstaller(installer, outdir, BUNDLE_NAME, options.nacl_sdk)
+
+ suite = unittest.TestLoader().loadTestsFromTestCase(
+ TestingClosure(os.path.join(outdir, 'nacl_sdk', BUNDLE_NAME),
+ options.jobs))
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+
+ RemoveDir(outdir)
+
+ return int(not result.wasSuccessful())
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/native_client_sdk/src/build_tools/tests/naclsdk_manifest_test.json b/native_client_sdk/src/build_tools/tests/naclsdk_manifest_test.json
new file mode 100644
index 0000000..a1eb57ea
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/naclsdk_manifest_test.json
@@ -0,0 +1,89 @@
+{
+ "manifest_version": 1,
+ "bundles": [
+ {
+ "name": "sdk_tools",
+ "revision": 1,
+ "version": 2,
+ "description": "Native Client SDK Tools, revision 1",
+ "stability": "stable",
+ "recommended": "yes",
+ "archives": [
+ {
+ "host_os": "linux",
+ "size": 30018483,
+ "checksum": { "sha1": "638bf0020c6f013ebef420d03fd7bb28593047b4" },
+ "url": "nacl_sdk_linux_1.tgz"
+ },
+ {
+ "host_os": "mac",
+ "size": 30227408,
+ "checksum": { "sha1": "638bf0020c6f013ebef420d03fd7bb28593047b4" },
+ "url": "nacl_sdk_mac_1.tgz"
+ },
+ {
+ "host_os": "win",
+ "size": 36440315,
+ "checksum": { "sha1": "638bf0020c6f013ebef420d03fd7bb28593047b4" },
+ "url": "nacl_sdk_win_1.tgz"
+ }
+ ]
+ },
+ {
+ "name": "test_1",
+ "version": 1,
+ "revision": 2,
+ "description": "Test Bundle version 1",
+ "stability": "stable",
+ "recommended": "yes",
+ "archives": [
+ {
+ "host_os": "linux",
+ "size": 30018483,
+ "checksum": { "sha1": "638bf0020c6f013ebef420d03fd7bb28593047b4" },
+ "url": "nacl_linux_pepper_14_1.tgz"
+ },
+ {
+ "host_os": "mac",
+ "size": 30227408,
+ "checksum": { "sha1": "638bf0020c6f013ebef420d03fd7bb28593047b4" },
+ "url": "nacl_mac_pepper_14_1.tgz"
+ },
+ {
+ "host_os": "win",
+ "size": 36440315,
+ "checksum": { "sha1": "638bf0020c6f013ebef420d03fd7bb28593047b4" },
+ "url": "nacl_win_pepper_14_1.tgz"
+ }
+ ]
+ },
+ {
+ "name": "test_2",
+ "version": 1,
+ "revision": 2,
+ "description": "Test Bundle version 2",
+ "stability": "stable",
+ "recommended": "yes",
+ "archives": [
+ {
+ "host_os": "linux",
+ "size": 30018483,
+ "checksum": { "sha1": "638bf0020c6f013ebef420d03fd7bb28593047b4" },
+ "url": "nacl_linux_pepper_15_1.tgz"
+ },
+ {
+ "host_os": "mac",
+ "size": 30227408,
+ "checksum": { "sha1": "638bf0020c6f013ebef420d03fd7bb28593047b4" },
+ "url": "nacl_mac_pepper_15_1.tgz"
+ },
+ {
+ "host_os": "win",
+ "size": 36440315,
+ "checksum": { "sha1": "638bf0020c6f013ebef420d03fd7bb28593047b4" },
+ "url": "nacl_win_pepper_15_1.tgz"
+ }
+ ]
+ }
+ ]
+}
diff --git a/native_client_sdk/src/build_tools/tests/nsis_script_test.py b/native_client_sdk/src/build_tools/tests/nsis_script_test.py
new file mode 100644
index 0000000..877bde1
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/nsis_script_test.py
@@ -0,0 +1,147 @@
+#!/usr/bin/python2.6
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Unit tests for nsis_script.py."""
+
+import filecmp
+import os
+import sys
+import tarfile
+import tempfile
+import unittest
+
+from build_tools import nsis_script
+
+
+class TestNsisScript(unittest.TestCase):
+ """This class tests basic functionality of the nsis_script package"""
+
+ def FilterSvn(self, list):
+ '''A filter used to remove .svn dirs from installer lists.'''
+ return [elt for elt in list if '.svn' not in elt]
+
+ def testConstructor(self):
+ """Test default constructor."""
+ script = nsis_script.NsisScript('test_script.nsi')
+ self.assertEqual('test_script.nsi', script.script_file)
+ self.assertEqual(0, len(script.files))
+ self.assertEqual(0, len(script.dirs))
+ self.assertEqual(0, len(script.symlinks))
+ self.assertEqual(0, len(script.links))
+
+ def testInstallDir(self):
+ """Test install directory accessor/mutator."""
+ script = nsis_script.NsisScript('test_script.nsi')
+ test_inst_dir = os.path.join('C:%s' % os.sep, 'test_install_dir')
+ script.install_dir = test_inst_dir
+ self.assertEqual(test_inst_dir, script.install_dir)
+
+ def testBadInstallDir(self):
+ """Test install directory mutator with bad data"""
+ script = nsis_script.NsisScript('test_script.nsi')
+ try:
+ script.install_dir = 'bogus_path'
+ self.fail('install_dir failed to throw an exception with bogus_path')
+ except nsis_script.Error:
+ pass
+ else:
+ raise
+
+ def testInitFromDirectory(self):
+ """Test creation of artifact lists from an archive directory."""
+ script = nsis_script.NsisScript('test_script.nsi')
+ archive_dir = os.path.join('build_tools', 'tests', 'nsis_test_archive')
+ script.InitFromDirectory(archive_dir,
+ dir_filter=self.FilterSvn,
+ file_filter=self.FilterSvn)
+ file_set = script.files
+ self.assertEqual(3, len(file_set))
+ self.assertTrue(os.path.join(archive_dir, 'test_file.txt') in file_set)
+ self.assertTrue(os.path.join(archive_dir, 'test_dir', 'test_dir_file1.txt')
+ in file_set)
+ self.assertTrue(os.path.join(archive_dir, 'test_dir', 'test_dir_file2.txt')
+ in file_set)
+ dir_set = script.dirs
+ self.assertEqual(1, len(dir_set))
+ self.assertTrue(os.path.join(archive_dir, 'test_dir') in dir_set)
+
+ def testCreateInstallNameScript(self):
+ """Test the install name include script."""
+ test_dir = os.path.join('build_tools', 'tests')
+ script = nsis_script.NsisScript(os.path.join(test_dir, 'test_script.nsi'))
+ script.CreateInstallNameScript(cwd=test_dir)
+ install_name_script = open(os.path.join(test_dir, 'sdk_install_name.nsh'),
+ 'r')
+ self.assertTrue(script.install_dir in install_name_script.read())
+ install_name_script.close()
+ os.remove(os.path.join(test_dir, 'sdk_install_name.nsh'))
+
+ def testNormalizeInstallPath(self):
+ """Test NormalizeInstallPath."""
+ # If InitFromDirectory() is not called, then install paths are unchanged.
+ test_dir = os.path.join('build_tools', 'tests')
+ script = nsis_script.NsisScript(os.path.join(test_dir, 'test_script.nsi'))
+ test_path = os.path.join('C:', 'test', 'path')
+ path = script.NormalizeInstallPath(test_path)
+ self.assertEqual(test_path, path)
+ # Set a relative install path.
+ archive_dir = os.path.join(test_dir, 'nsis_test_archive')
+ script.InitFromDirectory(archive_dir,
+ dir_filter=self.FilterSvn,
+ file_filter=self.FilterSvn)
+ test_path = os.path.join('test', 'relative', 'path')
+ path = script.NormalizeInstallPath(os.path.join(archive_dir, test_path))
+ self.assertEqual(test_path, path)
+
+ def testCreateSectionNameScript(self):
+ """Test the section name script."""
+ test_dir = os.path.join('build_tools', 'tests')
+ script = nsis_script.NsisScript(os.path.join(test_dir, 'test_script.nsi'))
+ archive_dir = os.path.join(test_dir, 'nsis_test_archive')
+ script.InitFromDirectory(archive_dir,
+ dir_filter=self.FilterSvn,
+ file_filter=self.FilterSvn)
+ script.CreateSectionNameScript(cwd=test_dir)
+ # When comparing the contents of the script with the golden file, note
+ # that the 'File' lines can be in any order. All the other section commands
+ # have to be in the correct order.
+ def GetSectionCommands(section_script):
+ '''Split all the section commands into File and other commands.
+
+ Returns:
+ A tuple (commands, file_set), where |commands| is an ordered list of
+ NSIS section commands, and |file_set| is an unordered set of NSIS File
+ commands.
+ '''
+ commands = []
+ file_set = set()
+ with open(section_script) as script_file:
+ commands = script_file.readlines(); # All the commands in order.
+ file_set = set([file_cmd
+ for file_cmd in commands if 'File ' in file_cmd])
+ # Remove the File commands from the in-order command list.
+ for file in file_set:
+ commands.remove(file)
+ return commands, file_set
+
+ test_commands, test_files = GetSectionCommands(
+ os.path.join(test_dir, 'test_sdk_section.nsh'))
+ script_commands, script_files = GetSectionCommands(
+ os.path.join(test_dir, 'sdk_section.nsh'))
+ self.assertEqual(test_commands, script_commands)
+ self.assertEqual(test_files, script_files) # Uses set() equality.
+ os.remove(os.path.join(test_dir, 'sdk_section.nsh'))
+
+
+def RunTests():
+ suite = unittest.TestLoader().loadTestsFromTestCase(TestNsisScript)
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+
+ return int(not result.wasSuccessful())
+
+
+if __name__ == '__main__':
+ sys.exit(RunTests())
diff --git a/native_client_sdk/src/build_tools/tests/nsis_test_archive/test_dir/test_dir_file1.txt b/native_client_sdk/src/build_tools/tests/nsis_test_archive/test_dir/test_dir_file1.txt
new file mode 100644
index 0000000..96fc3a7
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/nsis_test_archive/test_dir/test_dir_file1.txt
@@ -0,0 +1,3 @@
+This is a test file for nsis_script_test.py
+
+It is file 1 within a directory. \ No newline at end of file
diff --git a/native_client_sdk/src/build_tools/tests/nsis_test_archive/test_dir/test_dir_file2.txt b/native_client_sdk/src/build_tools/tests/nsis_test_archive/test_dir/test_dir_file2.txt
new file mode 100644
index 0000000..f448ca9
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/nsis_test_archive/test_dir/test_dir_file2.txt
@@ -0,0 +1,3 @@
+This is a test file for nsis_script_test.py
+
+It is file 2 within a directory. \ No newline at end of file
diff --git a/native_client_sdk/src/build_tools/tests/nsis_test_archive/test_file.txt b/native_client_sdk/src/build_tools/tests/nsis_test_archive/test_file.txt
new file mode 100644
index 0000000..83066e0
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/nsis_test_archive/test_file.txt
@@ -0,0 +1 @@
+This is a test file for nsis_script_test.py \ No newline at end of file
diff --git a/native_client_sdk/src/build_tools/tests/path_set_test.py b/native_client_sdk/src/build_tools/tests/path_set_test.py
new file mode 100644
index 0000000..3b6d115
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/path_set_test.py
@@ -0,0 +1,174 @@
+#!/usr/bin/python2.6
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Unit tests for path_set.py."""
+
+import os
+import sys
+import unittest
+
+from build_tools import path_set
+
+
+class TestPathSet(unittest.TestCase):
+ """This class tests basic functionality of the installer_contents package"""
+
+ def setUp(self):
+ self.pathset = path_set.PathSet()
+
+ def testConstructor(self):
+ """Test default constructor."""
+ self.assertEqual(0, len(self.pathset.files))
+ self.assertEqual(0, len(self.pathset.dirs))
+ self.assertEqual(0, len(self.pathset.symlinks))
+ self.assertEqual(0, len(self.pathset.links))
+
+ def testSetAttributes(self):
+ self.pathset.files = set(['file1', 'file2'])
+ self.pathset.dirs = set(['dir1', 'dir2'])
+ self.pathset.symlinks = {'symlink': 'symlink_target'}
+ self.pathset.links = {'link': 'link_target'}
+ self.assertEqual(2, len(self.pathset.files))
+ self.assertTrue('file1' in self.pathset.files)
+ self.assertTrue('file2' in self.pathset.files)
+ self.assertFalse('dir1' in self.pathset.files)
+ self.assertEqual(2, len(self.pathset.dirs))
+ self.assertTrue('dir1' in self.pathset.dirs)
+ self.assertTrue('dir2' in self.pathset.dirs)
+ self.assertFalse('file1' in self.pathset.dirs)
+ self.assertEqual(1, len(self.pathset.symlinks))
+ self.assertTrue('symlink' in self.pathset.symlinks)
+ self.assertFalse('dir1' in self.pathset.symlinks)
+ self.assertEqual(1, len(self.pathset.links))
+ self.assertTrue('link' in self.pathset.links)
+ self.assertFalse('file1' in self.pathset.links)
+ self.pathset.dirs.discard('dir1')
+ self.assertFalse('dir11' in self.pathset.dirs)
+
+ def testSetBadAttributes(self):
+ try:
+ self.pathset.files = ['this', 'is', 'a', 'list']
+ self.fail('set files failed to throw an exception with non-set')
+ except path_set.Error:
+ pass
+ else:
+ raise
+
+ try:
+ self.pathset.dirs = ['dir', 'list']
+ self.fail('set dirs failed to throw an exception with non-set')
+ except path_set.Error:
+ pass
+ else:
+ raise
+
+ try:
+ self.pathset.symlinks = 10
+ self.fail('set symlinks failed to throw an exception with non-dict')
+ except path_set.Error:
+ pass
+ else:
+ raise
+
+ try:
+ self.pathset.links = 'string of links'
+ self.fail('set links failed to throw an exception with non-dict')
+ except path_set.Error:
+ pass
+ else:
+ raise
+
+ def testOrOperator(self):
+ self.pathset.files = set(['file1', 'file2', 'file3'])
+ self.pathset.dirs = set(['dir1', 'dir2'])
+ self.pathset.symlinks = {'symlink': 'symlink_target'}
+ self.pathset.links = {'link': 'link_target'}
+ pathset2 = path_set.PathSet()
+ pathset2.files = set(['file1', 'file4'])
+ pathset2.dirs = set(['dir1', 'dir3'])
+ pathset2.symlinks = {'symlink2': 'symlink_target2',
+ 'dir2': 'link_to_dir',
+ 'file3': 'link_to_file'}
+ pathset2.links = {'link2': 'link_target2'}
+ merged_pathset = self.pathset | pathset2
+ self.assertFalse('file3' in self.pathset.files)
+ self.assertFalse('dir3' in self.pathset.dirs)
+ self.assertTrue('file1' in merged_pathset.files)
+ self.assertTrue('file2' in merged_pathset.files)
+ self.assertFalse('file3' in merged_pathset.files)
+ self.assertTrue('file4' in merged_pathset.files)
+ self.assertFalse('dir1' in merged_pathset.files)
+ self.assertTrue('dir1' in merged_pathset.dirs)
+ self.assertFalse('dir2' in merged_pathset.dirs)
+ self.assertTrue('dir3' in merged_pathset.dirs)
+ self.assertFalse('file1' in merged_pathset.dirs)
+ self.assertTrue('symlink' in merged_pathset.symlinks)
+ self.assertTrue('symlink2' in merged_pathset.symlinks)
+ self.assertFalse('dir1' in merged_pathset.symlinks)
+ self.assertTrue('link' in merged_pathset.links)
+ self.assertTrue('link2' in merged_pathset.links)
+ self.assertFalse('file1' in merged_pathset.links)
+
+ def testOrEqualsOperator(self):
+ self.pathset.files = set(['file1', 'file2', 'file3'])
+ self.pathset.dirs = set(['dir1', 'dir2'])
+ self.pathset.symlinks = {'symlink': 'symlink_target'}
+ self.pathset.links = {'link': 'link_target'}
+ pathset2 = path_set.PathSet()
+ pathset2.files = set(['file1', 'file4'])
+ pathset2.dirs = set(['dir1', 'dir3'])
+ pathset2.symlinks = {'symlink2': 'symlink_target2',
+ 'dir2': 'link_to_dir',
+ 'file3': 'link_to_file'}
+ pathset2.links = {'link2': 'link_target2'}
+ self.pathset |= pathset2
+ self.assertTrue('file1' in self.pathset.files)
+ self.assertTrue('file2' in self.pathset.files)
+ self.assertFalse('file3' in self.pathset.files)
+ self.assertFalse('dir1' in self.pathset.files)
+ self.assertTrue('dir1' in self.pathset.dirs)
+ self.assertFalse('dir2' in self.pathset.dirs)
+ self.assertTrue('dir3' in self.pathset.dirs)
+ self.assertFalse('file1' in self.pathset.dirs)
+ self.assertTrue('symlink' in self.pathset.symlinks)
+ self.assertTrue('symlink2' in self.pathset.symlinks)
+ self.assertFalse('dir1' in self.pathset.symlinks)
+ self.assertTrue('link' in self.pathset.links)
+ self.assertTrue('link2' in self.pathset.links)
+ self.assertFalse('file1' in self.pathset.links)
+
+ def testPrependPath(self):
+ path_prefix = os.path.join('C:%s' % os.sep, 'path', 'prefix')
+ self.pathset.files = set(['file1', 'file2'])
+ self.pathset.dirs = set(['dir1', 'dir2'])
+ self.pathset.symlinks = {'symlink': 'symlink_target'}
+ self.pathset.links = {'link': 'link_target'}
+ prepended_files = set([os.path.join(path_prefix, f)
+ for f in self.pathset.files])
+ prepended_dirs = set([os.path.join(path_prefix, d)
+ for d in self.pathset.dirs])
+ prepended_symlinks = {os.path.join(path_prefix, 'symlink'):
+ 'symlink_target'}
+ prepended_links = {os.path.join(path_prefix, 'link'): 'link_target'}
+ self.pathset.PrependPath(path_prefix)
+ self.assertTrue(isinstance(self.pathset.files, set))
+ self.assertTrue(isinstance(self.pathset.dirs, set))
+ self.assertTrue(isinstance(self.pathset.symlinks, dict))
+ self.assertTrue(isinstance(self.pathset.links, dict))
+ self.assertEqual(prepended_files, self.pathset.files)
+ self.assertEqual(prepended_dirs, self.pathset.dirs)
+ self.assertEqual(prepended_symlinks, self.pathset.symlinks)
+ self.assertEqual(prepended_links, self.pathset.links)
+
+
+def RunTests():
+ suite = unittest.TestLoader().loadTestsFromTestCase(TestPathSet)
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+
+ return int(not result.wasSuccessful())
+
+if __name__ == '__main__':
+ sys.exit(RunTests())
diff --git a/native_client_sdk/src/build_tools/tests/sdk_test_cache/naclsdk_manifest.json b/native_client_sdk/src/build_tools/tests/sdk_test_cache/naclsdk_manifest.json
new file mode 100644
index 0000000..77e1e4e
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/sdk_test_cache/naclsdk_manifest.json
@@ -0,0 +1,70 @@
+{
+ "manifest_version": 1,
+ "bundles": [
+ {
+ "name": "sdk_tools",
+ "revision": 1,
+ "version": 2,
+ "description": "Native Client SDK Tools, revision 1",
+ "stability": "stable",
+ "recommended": "yes",
+ "archives": [
+ {
+ "host_os": "linux",
+ "size": 30018483,
+ "checksum": { "sha1": "638bf0020c6f013ebef420d03fd7bb28593047b4" },
+ "url": "nacl_sdk_linux_1.tgz"
+ },
+ {
+ "host_os": "mac",
+ "size": 30227408,
+ "checksum": { "sha1": "638bf0020c6f013ebef420d03fd7bb28593047b4" },
+ "url": "nacl_sdk_mac_1.tgz"
+ },
+ {
+ "host_os": "win",
+ "size": 36440315,
+ "checksum": { "sha1": "638bf0020c6f013ebef420d03fd7bb28593047b4" },
+ "url": "nacl_sdk_win_1.tgz"
+ }
+ ]
+ },
+ {
+ "name": "test_1",
+ "version": 1,
+ "revision": 1,
+ "description": "Test Bundle version 1",
+ "stability": "stable",
+ "recommended": "yes",
+ "archives": [
+ {
+ "host_os": "linux",
+ "size": 30018483,
+ "checksum": { "sha1": "638bf0020c6f013ebef420d03fd7bb28593047b4" },
+ "url": "nacl_linux_pepper_14_1.tgz"
+ },
+ {
+ "host_os": "mac",
+ "size": 30227408,
+ "checksum": { "sha1": "638bf0020c6f013ebef420d03fd7bb28593047b4" },
+ "url": "nacl_mac_pepper_14_1.tgz"
+ },
+ {
+ "host_os": "win",
+ "size": 36440315,
+ "checksum": { "sha1": "638bf0020c6f013ebef420d03fd7bb28593047b4" },
+ "url": "nacl_win_pepper_14_1.tgz"
+ }
+ ]
+ },
+ {
+ "name": "empty_bundle",
+ "version": 0,
+ "revision": 0,
+ "description": "Empty Test Bundle, Version 0",
+ "stability": "dev",
+ "recommended": "no",
+ "archives": []
+ }
+ ]
+}
diff --git a/native_client_sdk/src/build_tools/tests/sdk_update_test.py b/native_client_sdk/src/build_tools/tests/sdk_update_test.py
new file mode 100755
index 0000000..3196379
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/sdk_update_test.py
@@ -0,0 +1,185 @@
+#!/usr/bin/python2.6
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Unit tests for sdk_update.py."""
+
+import exceptions
+import mox
+import os
+import subprocess
+import sys
+import tempfile
+import unittest
+import urllib
+
+from build_tools.sdk_tools import sdk_update
+
+
+SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+PARENT_DIR = os.path.dirname(SCRIPT_DIR)
+
+
+class FakeOptions(object):
+ ''' Just a placeholder for options '''
+ pass
+
+
+def CallSDKUpdate(args):
+ '''Calls the sdk_update.py utility and returns stdout as a string
+
+ Args:
+ args: command-line arguments as a list (not including program name)
+
+ Returns:
+ string tuple containing (stdout, stderr)
+
+ Raises:
+ subprocess.CalledProcessError: non-zero return code from sdk_update'''
+ command = ['python', os.path.join(PARENT_DIR, 'sdk_tools',
+ 'sdk_update.py')] + args
+ process = subprocess.Popen(stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ args=command)
+ output, error_output = process.communicate()
+
+ retcode = process.poll() # Note - calling wait() can cause a deadlock
+ if retcode != 0:
+ raise subprocess.CalledProcessError(retcode, command)
+ return output, error_output
+
+
+class TestSDKUpdate(unittest.TestCase):
+ ''' Test basic functionality of the sdk_update package '''
+ def setUp(self):
+ self._options = FakeOptions()
+ self._options.manifest_url = 'file://%s' % urllib.pathname2url(
+ os.path.join(SCRIPT_DIR, 'naclsdk_manifest_test.json'))
+ self._options.user_data_dir = os.path.join(SCRIPT_DIR, 'sdk_test_cache')
+
+ def testBadArg(self):
+ '''Test that using a bad argument results in an error'''
+ self.assertRaises(subprocess.CalledProcessError, CallSDKUpdate, ['--bad'])
+
+ def testGetHostOS(self):
+ '''Test that the GetHostOS function returns a valid value'''
+ self.assertTrue(sdk_update.GetHostOS() in ['linux', 'mac', 'win'])
+
+ def testHelp(self):
+ '''Test that basic help works'''
+ # Running any help command should call sys.exit()
+ self.assertRaises(exceptions.SystemExit, sdk_update.main, ['-h'])
+
+ def testList(self):
+ '''Test the List function'''
+ command = ['--user-data-dir=%s' %
+ os.path.join(SCRIPT_DIR, 'sdk_test_cache'),
+ '--manifest-url=file://%s' %
+ urllib.pathname2url(os.path.join(
+ SCRIPT_DIR, 'naclsdk_manifest_test.json')),
+ 'list']
+ bundle_list = CallSDKUpdate(command)[0]
+ # Just do some simple sanity checks on the resulting string
+ self.assertEqual(bundle_list.count('sdk_tools'), 2)
+ self.assertEqual(bundle_list.count('test_1'), 2)
+ self.assertEqual(bundle_list.count('test_2'), 1)
+ self.assertEqual(bundle_list.count('description:'), 6)
+
+ def testUpdateHelp(self):
+ '''Test the help for the update command'''
+ self.assertRaises(exceptions.SystemExit,
+ sdk_update.main, ['help', 'update'])
+
+ def testUpdateBogusBundle(self):
+ '''Test running update with a bogus bundle'''
+ self.assertRaises(sdk_update.Error,
+ sdk_update.main,
+ ['update', 'bogusbundle'])
+
+ def testSDKManifestFile(self):
+ '''Test SDKManifestFile'''
+ manifest_file = sdk_update.SDKManifestFile(
+ os.path.join(self._options.user_data_dir,
+ sdk_update.MANIFEST_FILENAME))
+ self.assertNotEqual(None, manifest_file)
+ bundles = manifest_file.GetBundles()
+ self.assertEqual(3, len(bundles))
+ test_bundle = manifest_file.GetBundleNamed('test_1')
+ self.assertNotEqual(None, test_bundle)
+ self.assertTrue('revision' in test_bundle)
+ self.assertEqual(1, test_bundle['revision'])
+
+ def testNeedsUpdate(self):
+ '''Test that the test_1 bundle needs updating'''
+ tools = sdk_update.ManifestTools(self._options)
+ tools.LoadManifest()
+ bundles = tools.GetBundles()
+ self.assertEqual(3, len(bundles))
+ local_manifest = sdk_update.SDKManifestFile(
+ os.path.join(self._options.user_data_dir,
+ sdk_update.MANIFEST_FILENAME))
+ self.assertNotEqual(None, local_manifest)
+ for bundle in bundles:
+ bundle_name = bundle['name']
+ self.assertTrue('revision' in bundle)
+ if bundle_name == 'test_1':
+ self.assertTrue(local_manifest.BundleNeedsUpdate(bundle))
+ elif bundle_name == 'test_2':
+ self.assertTrue(local_manifest.BundleNeedsUpdate(bundle))
+ else:
+ self.assertFalse(local_manifest.BundleNeedsUpdate(bundle))
+
+ def testMergeManifests(self):
+ '''Test merging a Bundle into a manifest file'''
+ tools = sdk_update.ManifestTools(self._options)
+ tools.LoadManifest()
+ bundles = tools.GetBundles()
+ self.assertEqual(3, len(bundles))
+ local_manifest = sdk_update.SDKManifestFile(
+ os.path.join(self._options.user_data_dir,
+ sdk_update.MANIFEST_FILENAME))
+ self.assertEqual(None, local_manifest.GetBundleNamed('test_2'))
+ for bundle in bundles:
+ local_manifest.MergeBundle(bundle)
+ self.assertNotEqual(None, local_manifest.GetBundleNamed('test_2'))
+ for bundle in bundles:
+ self.assertFalse(local_manifest.BundleNeedsUpdate(bundle))
+
+ def testMergeBundle(self):
+ '''Test MergeWithBundle'''
+ tools = sdk_update.ManifestTools(self._options)
+ tools.LoadManifest()
+ bundles = tools.GetBundles()
+ self.assertEqual(3, len(bundles))
+ local_manifest = sdk_update.SDKManifestFile(
+ os.path.join(self._options.user_data_dir,
+ sdk_update.MANIFEST_FILENAME))
+ self.assertNotEqual(None, local_manifest)
+ # Test the | operator.
+ for bundle in bundles:
+ bundle_name = bundle['name']
+ if bundle_name == 'test_2':
+ continue
+ local_test_bundle = local_manifest.GetBundleNamed(bundle_name)
+ merged_bundle = local_test_bundle.MergeWithBundle(bundle)
+ self.assertTrue('revision' in merged_bundle)
+ if bundle_name == 'test_1':
+ self.assertEqual(2, merged_bundle['revision'])
+ else:
+ self.assertEqual(1, merged_bundle['revision'])
+ merged_bundle.Validate()
+
+ def testVersion(self):
+ '''Test that showing the version works'''
+ self.assertRaises(exceptions.SystemExit, sdk_update.main, ['--version'])
+
+def main():
+ suite = unittest.TestLoader().loadTestsFromTestCase(TestSDKUpdate)
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+
+ return int(not result.wasSuccessful())
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/native_client_sdk/src/build_tools/tests/set_nacl_env_test.py b/native_client_sdk/src/build_tools/tests/set_nacl_env_test.py
new file mode 100755
index 0000000..e0a7d6f
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/set_nacl_env_test.py
@@ -0,0 +1,129 @@
+#!/usr/bin/python2.6
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Unit tests for set_nacl_env.py."""
+
+import glob
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+import unittest
+
+from build_tools.sdk_tools import set_nacl_env
+
+
+SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+SDK_ROOT_DIR = os.path.dirname(os.path.dirname(SCRIPT_DIR))
+
+
+class FakeOptions(object):
+ ''' Just a placeholder for options '''
+ pass
+
+
+class TestSetNaclEnv(unittest.TestCase):
+ ''' Test basic functionality of the set_nacl_env package '''
+ def setUp(self):
+ self._options = FakeOptions()
+ self._options.host = 'mac'
+ self._options.lib_variant = 'newlib'
+ self._options.sdk_root = SDK_ROOT_DIR
+ self._options.sdk_platform = 'pepper_17'
+ self._options.build_type = 'debug'
+ self._options.no_ppapi = False
+ self._options.merge = False
+
+ self._env = {}
+ self._env['NACL_SDK_ROOT'] = SDK_ROOT_DIR
+
+ self._temp_dir = tempfile.mkdtemp(prefix='tmp_set_nacl_env_test')
+
+ def tearDown(self):
+ shutil.rmtree(self._temp_dir, ignore_errors=True)
+
+ def testBuildEnvX86_32(self):
+ ''' Test setting up a x86_32 build env '''
+ self._options.arch = 'x86-32'
+ self._options.toolchain_path = set_nacl_env.GetToolchainPath(self._options)
+ env = set_nacl_env.SetupX86Env(self._options)
+ # Verify a few essential build options.
+ self.assertTrue('CFLAGS' in env)
+ self.assertTrue('CC' in env)
+ self.assertTrue('-m32' in env['CC'])
+ self.assertTrue('pepper_17' in env['CC'])
+ self.assertTrue('CXX' in env)
+ self.assertTrue('-m32' in env['CXX'])
+
+ def testBuildEnvX86_64(self):
+ ''' Test setting up a x86_64 build env '''
+ self._options.arch = 'x86-64'
+ self._options.toolchain_path = set_nacl_env.GetToolchainPath(self._options)
+ env = set_nacl_env.SetupX86Env(self._options)
+ # Verify a few essential build options.
+ self.assertTrue('CFLAGS' in env)
+ self.assertTrue('CC' in env)
+ self.assertTrue('-m64' in env['CC'])
+ self.assertTrue('pepper_17' in env['CC'])
+ self.assertTrue('CXX' in env)
+ self.assertTrue('-m64' in env['CXX'])
+
+ def testBuildWithMake(self):
+ ''' Test building hello_world_c with make '''
+ def MakeClean():
+ ''' Invoke 'make clean' in the current directory and check the outcome.
+ '''
+ cmd = [script, 'make clean --silent']
+ self.assertEqual(0, subprocess.call(cmd, env=self._env, shell=False,
+ cwd=self._temp_dir))
+ self.assertFalse(os.path.exists('hello_world_c.nexe'))
+
+ # Can't use make on Windows
+ if sys.platform == 'win32':
+ return
+
+ # The test directories are not generally writable. To be able to run make,
+ # we copy the files to a temp directory instead.
+ self.assertTrue(self._temp_dir)
+ src_dir = os.path.join(SCRIPT_DIR, 'set_nacl_env_test_archive')
+ for file in glob.iglob(os.path.join(src_dir, '*')):
+ shutil.copy2(file, self._temp_dir)
+
+ script = os.path.join(SDK_ROOT_DIR, 'build_tools', 'sdk_tools',
+ 'set_nacl_env.py')
+ nexe_path = os.path.join(self._temp_dir, 'hello_world_c.nexe')
+
+ # Build and verify the 32-bit version.
+ options = ['--platform=.', '--arch=x86-32']
+ cmd = [script] + options + ['make hello_world_c.nexe']
+ self.assertEqual(0, subprocess.call(cmd, env=self._env, shell=False,
+ cwd=self._temp_dir))
+ self.assertTrue(os.path.exists(nexe_path))
+ size_32 = os.path.getsize(nexe_path)
+ MakeClean()
+
+ # Build and verify the 64-bit version.
+ options = ['--platform=.', '--arch=x86-64']
+ cmd = [script] + options + ['make hello_world_c.nexe']
+ self.assertEqual(0, subprocess.call(cmd, env=self._env, shell=False,
+ cwd=self._temp_dir))
+ self.assertTrue(os.path.exists(nexe_path))
+ size_64 = os.path.getsize(nexe_path)
+ MakeClean()
+
+ # Verify that 64-bit version of nexe is larger than 32-bit version.
+ self.assertTrue(size_64 > size_32)
+
+
+def main():
+ suite = unittest.TestLoader().loadTestsFromTestCase(TestSetNaclEnv)
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+
+ return int(not result.wasSuccessful())
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/native_client_sdk/src/build_tools/tests/set_nacl_env_test_archive/Makefile b/native_client_sdk/src/build_tools/tests/set_nacl_env_test_archive/Makefile
new file mode 100644
index 0000000..7db0ce7
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/set_nacl_env_test_archive/Makefile
@@ -0,0 +1,20 @@
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+EXAMPLE_DIR = .
+EXAMPLE = hello_world_c.nexe
+
+all : $(EXAMPLE)
+
+clean :
+ rm -f $(EXAMPLE) hello_world_c.o hello_world_c.nexe
+
+SRCS_ = $(EXAMPLE_DIR)/*.c
+
+hello_world_c.o : $(SRCS_)
+ $(CXX) $(CPPFLAGS) -I$(EXAMPLE_DIR) $(CXXFLAGS) -c \
+ $(EXAMPLE_DIR)/hello_world_c.c
+
+$(EXAMPLE) : hello_world_c.o
+ $(LINK) $(CFLAGS) $(LDFLAGS) $^ -o $@
diff --git a/native_client_sdk/src/build_tools/tests/set_nacl_env_test_archive/hello_world_c.c b/native_client_sdk/src/build_tools/tests/set_nacl_env_test_archive/hello_world_c.c
new file mode 100644
index 0000000..939e724
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/set_nacl_env_test_archive/hello_world_c.c
@@ -0,0 +1,287 @@
+/* Copyright (c) 2011 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/** @file hello_world.c
+ * This example demonstrates loading, running and scripting a very simple
+ * NaCl module.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/c/pp_module.h"
+#include "ppapi/c/pp_var.h"
+#include "ppapi/c/ppb.h"
+#include "ppapi/c/ppb_instance.h"
+#include "ppapi/c/ppb_messaging.h"
+#include "ppapi/c/ppb_var.h"
+#include "ppapi/c/ppp.h"
+#include "ppapi/c/ppp_instance.h"
+#include "ppapi/c/ppp_messaging.h"
+
+struct MessageInfo {
+ PP_Instance instance;
+ struct PP_Var message;
+};
+
+static const char* const kReverseTextMethodId = "reverseText";
+static const char* const kFortyTwoMethodId = "fortyTwo";
+static const char kMessageArgumentSeparator = ':';
+static const char kNullTerminator = '\0';
+
+static struct PPB_Messaging* ppb_messaging_interface = NULL;
+static struct PPB_Var* ppb_var_interface = NULL;
+static PP_Module module_id = 0;
+
+
+/**
+ * Returns a mutable C string contained in the @a var or NULL if @a var is not
+ * string. This makes a copy of the string in the @ var and adds a NULL
+ * terminator. Note that VarToUtf8() does not guarantee the NULL terminator on
+ * the returned string. See the comments for VatToUtf8() in ppapi/c/ppb_var.h
+ * for more info. The caller is responsible for freeing the returned memory.
+ * @param[in] var PP_Var containing string.
+ * @return a C string representation of @a var.
+ * @note The caller is responsible for freeing the returned string.
+ */
+static char* VarToCStr(struct PP_Var var) {
+ uint32_t len = 0;
+ if (ppb_var_interface != NULL) {
+ const char* var_c_str = ppb_var_interface->VarToUtf8(var, &len);
+ if (len > 0) {
+ char* c_str = (char*)malloc(len + 1);
+ memcpy(c_str, var_c_str, len);
+ c_str[len] = kNullTerminator;
+ return c_str;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Creates new string PP_Var from C string. The resulting object will be a
+ * refcounted string object. It will be AddRef()ed for the caller. When the
+ * caller is done with it, it should be Release()d.
+ * @param[in] str C string to be converted to PP_Var
+ * @return PP_Var containing string.
+ */
+static struct PP_Var CStrToVar(const char* str) {
+ if (ppb_var_interface != NULL) {
+ return ppb_var_interface->VarFromUtf8(module_id, str, strlen(str));
+ }
+ return PP_MakeUndefined();
+}
+
+/**
+ * Reverse C string in-place.
+ * @param[in,out] str C string to be reversed
+ */
+static void ReverseStr(char* str) {
+ char* right = str + strlen(str) - 1;
+ char* left = str;
+ while (left < right) {
+ char tmp = *left;
+ *left++ = *right;
+ *right-- = tmp;
+ }
+}
+
+/**
+ * A simple function that always returns 42.
+ * @return always returns the integer 42
+ */
+static struct PP_Var FortyTwo() {
+ return PP_MakeInt32(42);
+}
+
+/**
+ * Called when the NaCl module is instantiated on the web page. The identifier
+ * of the new instance will be passed in as the first argument (this value is
+ * generated by the browser and is an opaque handle). This is called for each
+ * instantiation of the NaCl module, which is each time the <embed> tag for
+ * this module is encountered.
+ *
+ * If this function reports a failure (by returning @a PP_FALSE), the NaCl
+ * module will be deleted and DidDestroy will be called.
+ * @param[in] instance The identifier of the new instance representing this
+ * NaCl module.
+ * @param[in] argc The number of arguments contained in @a argn and @a argv.
+ * @param[in] argn An array of argument names. These argument names are
+ * supplied in the <embed> tag, for example:
+ * <embed id="nacl_module" dimensions="2">
+ * will produce two arguments, one named "id" and one named "dimensions".
+ * @param[in] argv An array of argument values. These are the values of the
+ * arguments listed in the <embed> tag. In the above example, there will
+ * be two elements in this array, "nacl_module" and "2". The indices of
+ * these values match the indices of the corresponding names in @a argn.
+ * @return @a PP_TRUE on success.
+ */
+static PP_Bool Instance_DidCreate(PP_Instance instance,
+ uint32_t argc,
+ const char* argn[],
+ const char* argv[]) {
+ return PP_TRUE;
+}
+
+/**
+ * Called when the NaCl module is destroyed. This will always be called,
+ * even if DidCreate returned failure. This routine should deallocate any data
+ * associated with the instance.
+ * @param[in] instance The identifier of the instance representing this NaCl
+ * module.
+ */
+static void Instance_DidDestroy(PP_Instance instance) {
+}
+
+/**
+ * Called when the position, the size, or the clip rect of the element in the
+ * browser that corresponds to this NaCl module has changed.
+ * @param[in] instance The identifier of the instance representing this NaCl
+ * module.
+ * @param[in] position The location on the page of this NaCl module. This is
+ * relative to the top left corner of the viewport, which changes as the
+ * page is scrolled.
+ * @param[in] clip The visible region of the NaCl module. This is relative to
+ * the top left of the plugin's coordinate system (not the page). If the
+ * plugin is invisible, @a clip will be (0, 0, 0, 0).
+ */
+static void Instance_DidChangeView(PP_Instance instance,
+ const struct PP_Rect* position,
+ const struct PP_Rect* clip) {
+}
+
+/**
+ * Notification that the given NaCl module has gained or lost focus.
+ * Having focus means that keyboard events will be sent to the NaCl module
+ * represented by @a instance. A NaCl module's default condition is that it
+ * will not have focus.
+ *
+ * Note: clicks on NaCl modules will give focus only if you handle the
+ * click event. You signal if you handled it by returning @a true from
+ * HandleInputEvent. Otherwise the browser will bubble the event and give
+ * focus to the element on the page that actually did end up consuming it.
+ * If you're not getting focus, check to make sure you're returning true from
+ * the mouse click in HandleInputEvent.
+ * @param[in] instance The identifier of the instance representing this NaCl
+ * module.
+ * @param[in] has_focus Indicates whether this NaCl module gained or lost
+ * event focus.
+ */
+static void Instance_DidChangeFocus(PP_Instance instance,
+ PP_Bool has_focus) {
+}
+
+/**
+ * Handler that gets called after a full-frame module is instantiated based on
+ * registered MIME types. This function is not called on NaCl modules. This
+ * function is essentially a place-holder for the required function pointer in
+ * the PPP_Instance structure.
+ * @param[in] instance The identifier of the instance representing this NaCl
+ * module.
+ * @param[in] url_loader A PP_Resource an open PPB_URLLoader instance.
+ * @return PP_FALSE.
+ */
+static PP_Bool Instance_HandleDocumentLoad(PP_Instance instance,
+ PP_Resource url_loader) {
+ /* NaCl modules do not need to handle the document load function. */
+ return PP_FALSE;
+}
+
+/**
+ * Handler for messages coming in from the browser via postMessage. Extracts
+ * the method call from @a message, parses it for method name and value, then
+ * calls the appropriate function. In the case of the reverseString method, the
+ * message format is a simple colon-separated string. The first part of the
+ * string up to the colon is the method name; after that is the string argument.
+ * @param[in] instance The instance ID.
+ * @param[in] message The contents, copied by value, of the message sent from
+ * browser via postMessage.
+ */
+void Messaging_HandleMessage(PP_Instance instance, struct PP_Var var_message) {
+ if (var_message.type != PP_VARTYPE_STRING) {
+ /* Only handle string messages */
+ return;
+ }
+ char* message = VarToCStr(var_message);
+ if (message == NULL)
+ return;
+ struct PP_Var var_result = PP_MakeUndefined();
+ if (strncmp(message, kFortyTwoMethodId, strlen(kFortyTwoMethodId)) == 0) {
+ var_result = FortyTwo();
+ } else if (strncmp(message,
+ kReverseTextMethodId,
+ strlen(kReverseTextMethodId)) == 0) {
+ /* Use everything after the ':' in |message| as the string argument. */
+ char* string_arg = strchr(message, kMessageArgumentSeparator);
+ if (string_arg != NULL) {
+ string_arg += 1; /* Advance past the ':' separator. */
+ ReverseStr(string_arg);
+ var_result = CStrToVar(string_arg);
+ }
+ }
+ free(message);
+
+ /* Echo the return result back to browser. Note that HandleMessage is always
+ * called on the main thread, so it's OK to post the message back to the
+ * browser directly from here. This return post is asynchronous.
+ */
+ ppb_messaging_interface->PostMessage(instance, var_result);
+ /* If the message was created using VarFromUtf8() it needs to be released.
+ * See the comments about VarFromUtf8() in ppapi/c/ppb_var.h for more
+ * information.
+ */
+ if (var_result.type == PP_VARTYPE_STRING) {
+ ppb_var_interface->Release(var_result);
+ }
+}
+
+/**
+ * Entry points for the module.
+ * Initialize needed interfaces: PPB_Core, PPB_Messaging and PPB_Var.
+ * @param[in] a_module_id module ID
+ * @param[in] get_browser pointer to PPB_GetInterface
+ * @return PP_OK on success, any other value on failure.
+ */
+PP_EXPORT int32_t PPP_InitializeModule(PP_Module a_module_id,
+ PPB_GetInterface get_browser) {
+ module_id = a_module_id;
+ ppb_messaging_interface =
+ (struct PPB_Messaging*)(get_browser(PPB_MESSAGING_INTERFACE));
+ ppb_var_interface = (struct PPB_Var*)(get_browser(PPB_VAR_INTERFACE));
+
+ return PP_OK;
+}
+
+/**
+ * Returns an interface pointer for the interface of the given name, or NULL
+ * if the interface is not supported.
+ * @param[in] interface_name name of the interface
+ * @return pointer to the interface
+ */
+PP_EXPORT const void* PPP_GetInterface(const char* interface_name) {
+ if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) {
+ static struct PPP_Instance instance_interface = {
+ &Instance_DidCreate,
+ &Instance_DidDestroy,
+ &Instance_DidChangeView,
+ &Instance_DidChangeFocus,
+ &Instance_HandleDocumentLoad,
+ };
+ return &instance_interface;
+ } else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0) {
+ static struct PPP_Messaging messaging_interface = {
+ &Messaging_HandleMessage
+ };
+ return &messaging_interface;
+ }
+ return NULL;
+}
+
+/**
+ * Called before the plugin module is unloaded.
+ */
+PP_EXPORT void PPP_ShutdownModule() {
+}
diff --git a/native_client_sdk/src/build_tools/tests/tar_archive_test.py b/native_client_sdk/src/build_tools/tests/tar_archive_test.py
new file mode 100644
index 0000000..d16c193
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/tar_archive_test.py
@@ -0,0 +1,200 @@
+#!/usr/bin/python2.6
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Unit tests for tar_archive.py."""
+
+import os
+import sys
+import tarfile
+import unittest
+
+from build_tools import tar_archive
+
+
+class TestTarArchive(unittest.TestCase):
+ """This class tests basic functionality of the tar_archive package"""
+
+ def setUp(self):
+ self.archive = tar_archive.TarArchive()
+
+ def ValidateTableOfContents(self, archive, path_filter=None):
+ '''Helper method to validate an archive table of contents.
+
+ The test table of contents file has these entries (from tar tv):
+ drwxr-xr-x test_links/
+ drwxr-xr-x test_links/test_dir/
+ lrwxr-xr-x test_links/test_dir_slnk -> test_dir
+ -rw-r--r-- test_links/test_hlnk_file_dst1.txt
+ hrw-r--r-- test_links/test_hlnk_file_dst2.txt link to \
+ test_links/test_hlnk_file_dst1.txt
+ hrw-r--r-- test_links/test_hlnk_file_src.txt link to \
+ test_links/test_hlnk_file_dst1.txt
+ lrwxr-xr-x test_links/test_slnk_file_dst.txt -> test_slnk_file_src.txt
+ -rw-r--r-- test_links/test_slnk_file_src.txt
+ -rw-r--r-- test_links/test_dir/test_file.txt
+ hrw-r--r-- test_links/test_dir/test_hlnk_file_dst3.txt \
+ test_links/test_hlnk_file_dst1.txt
+ lrwxr-xr-x test_links/test_dir/test_slnk_file_dst2.txt -> \
+ ../test_slnk_file_src.txt
+
+ Args:
+ archive: The TarArchive object under test.
+ '''
+ if not path_filter:
+ path_filter = lambda p: os.path.join('test_links', p)
+
+ self.assertEqual(3, len(archive.files))
+ self.assertTrue(path_filter('test_slnk_file_src.txt') in archive.files)
+ self.assertTrue(path_filter(os.path.join('test_dir', 'test_file.txt')) in
+ archive.files)
+ for file in archive.files:
+ self.assertFalse('_dir' in os.path.basename(file))
+ self.assertTrue(path_filter(os.path.join('test_dir', 'test_file.txt')) in
+ archive.files)
+
+ self.assertEqual(2, len(archive.dirs))
+ self.assertTrue(path_filter('test_dir') in archive.dirs)
+ for dir in archive.dirs:
+ self.assertFalse('slnk' in dir)
+ self.assertFalse('.txt' in dir)
+ self.assertFalse(dir in archive.files)
+ self.assertFalse(dir in archive.symlinks.keys())
+
+ self.assertEqual(3, len(archive.symlinks))
+ self.assertTrue(path_filter('test_dir_slnk') in archive.symlinks)
+ self.assertTrue(path_filter('test_slnk_file_dst.txt') in archive.symlinks)
+ for path, target in archive.symlinks.items():
+ self.assertFalse(path in archive.files)
+ self.assertFalse(path in archive.dirs)
+ # Make sure the target exists in either |archive.files| or
+ # |archive.dirs|. The target path in the archive is relative to the
+ #source file's path.
+ target_path = os.path.normpath(os.path.join(
+ os.path.dirname(path), target))
+ self.assertTrue((target_path in archive.files) or
+ (target_path in archive.dirs))
+
+ self.assertEqual(3, len(archive.links))
+ # There is no "source" file for hard links like there is for a symbolic
+ # link, so there it's possible that the hlnk_src file is in the
+ # |archive.links| set, which is OK as long as one of the hlnk files is
+ # in the |archive.files| set. Make sure that only hlnk files are in the
+ # |archive.links| list.
+ for path, target in archive.links.items():
+ self.assertTrue('test_hlnk_file' in path)
+ self.assertFalse(path in archive.files)
+ self.assertFalse(path in archive.dirs)
+ self.assertTrue(target in archive.files)
+ self.assertFalse(target in archive.dirs)
+
+ def testConstructor(self):
+ """Test default constructor."""
+ self.assertEqual(0, len(self.archive.files))
+ self.assertEqual(0, len(self.archive.dirs))
+ self.assertEqual(0, len(self.archive.symlinks))
+ self.assertEqual(0, len(self.archive.links))
+
+ def testFromTarball(self):
+ """Testing the TarArchive when using a tarball"""
+ # Use a known test archive to validate the TOC entries.
+ self.archive.InitWithTarFile(os.path.join('build_tools',
+ 'tests',
+ 'test_links.tgz'))
+ self.ValidateTableOfContents(self.archive)
+
+ def testFromTarballBadFile(self):
+ """Testing the TarArchive when using a bad tarball"""
+ self.assertRaises(OSError,
+ self.archive.InitWithTarFile,
+ 'nosuchfile')
+ self.assertRaises(tarfile.ReadError,
+ self.archive.InitWithTarFile,
+ os.path.join('build_tools',
+ 'tests',
+ 'test_links.tgz.manifest'))
+
+ def testFromManifest(self):
+ """Testing the TarArchive when using a manifest file"""
+ # Use a known test manifest to validate the TOC entries.
+ # The test manifest file is the output of tar -tv on the tarball used in
+ # testGetArchiveTableOfContents().
+ self.archive.InitWithManifest(os.path.join('build_tools',
+ 'tests',
+ 'test_links.tgz.manifest'))
+ self.ValidateTableOfContents(self.archive)
+
+ def testFromManifestBadFile(self):
+ """Testing the TarArchive when using a bad manifest file"""
+ self.assertRaises(OSError, self.archive.InitWithManifest, 'nosuchfile')
+
+ def testPathFilter(self):
+ """Testing the TarArchive when applying a path filter"""
+ def StripTestLinks(tar_path):
+ # Strip off the leading 'test_links/' path component.
+ pos = tar_path.find('test_links/')
+ if pos >= 0:
+ return os.path.normpath(tar_path[len('test_links/'):])
+ else:
+ return os.path.normpath(tar_path)
+
+ self.archive.path_filter = StripTestLinks
+ self.archive.InitWithTarFile(os.path.join('build_tools',
+ 'tests',
+ 'test_links.tgz'))
+ self.ValidateTableOfContents(self.archive, path_filter=lambda p: p)
+ self.archive.InitWithManifest(os.path.join('build_tools',
+ 'tests',
+ 'test_links.tgz.manifest'))
+ self.ValidateTableOfContents(self.archive, path_filter=lambda p: p)
+
+ def testPathFilterNone(self):
+ """Testing the TarArchive when applying a None path filter"""
+ # Verify that the paths in the |archive| object have the tar-style '/'
+ # separator.
+ def ValidateTarStylePaths(archive):
+ def AssertTarPath(iterable):
+ for i in iterable:
+ self.assertTrue(len(i.split('/')) > 0)
+
+ self.assertEqual(3, len(archive.files))
+ AssertTarPath(archive.files)
+ self.assertEqual(2, len(archive.dirs))
+ AssertTarPath(archive.dirs)
+ self.assertEqual(3, len(archive.symlinks))
+ AssertTarPath(archive.symlinks.keys())
+ self.assertEqual(3, len(archive.links))
+ AssertTarPath(archive.links.keys())
+
+ self.archive.path_filter = None
+ self.archive.InitWithTarFile(os.path.join('build_tools',
+ 'tests',
+ 'test_links.tgz'))
+ ValidateTarStylePaths(self.archive)
+ self.archive.InitWithManifest(os.path.join('build_tools',
+ 'tests',
+ 'test_links.tgz.manifest'))
+ ValidateTarStylePaths(self.archive)
+
+ def testDeletePathFilter(self):
+ # Note: due to the use of del() here, self.assertRaises() can't be used.
+ # Also, the with self.assertRaises() idiom is not in python 2.6 so it
+ # can't be used either.
+ try:
+ del(self.archive.path_filter)
+ except tar_archive.Error:
+ pass
+ else:
+ raise
+
+def RunTests():
+ suite = unittest.TestLoader().loadTestsFromTestCase(TestTarArchive)
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+
+ return int(not result.wasSuccessful())
+
+
+if __name__ == '__main__':
+ sys.exit(RunTests())
diff --git a/native_client_sdk/src/build_tools/tests/test_links.tgz b/native_client_sdk/src/build_tools/tests/test_links.tgz
new file mode 100644
index 0000000..898f147
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/test_links.tgz
Binary files differ
diff --git a/native_client_sdk/src/build_tools/tests/test_links.tgz.manifest b/native_client_sdk/src/build_tools/tests/test_links.tgz.manifest
new file mode 100644
index 0000000..dff6fbc2
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/test_links.tgz.manifest
@@ -0,0 +1,11 @@
+drwxr-xr-x 0 user staff 0 Aug 30 14:53 test_links/
+drwxr-xr-x 0 user staff 0 Sep 6 16:30 test_links/test_dir/
+lrwxr-xr-x 0 user staff 0 Aug 30 14:53 test_links/test_dir_slnk -> test_dir
+-rw-r--r-- 0 user staff 35 Aug 30 14:53 test_links/test_hlnk_file_dst1.txt
+hrw-r--r-- 0 user staff 0 Aug 30 14:53 test_links/test_hlnk_file_dst2.txt link to test_links/test_hlnk_file_dst1.txt
+hrw-r--r-- 0 user staff 0 Aug 30 14:53 test_links/test_hlnk_file_src.txt link to test_links/test_hlnk_file_dst1.txt
+lrwxr-xr-x 0 user staff 0 Aug 30 14:52 test_links/test_slnk_file_dst.txt -> test_slnk_file_src.txt
+-rw-r--r-- 0 user staff 33 Aug 30 14:51 test_links/test_slnk_file_src.txt
+-rw-r--r-- 0 user staff 38 Aug 30 14:52 test_links/test_dir/test_file.txt
+hrw-r--r-- 0 user staff 0 Aug 30 14:53 test_links/test_dir/test_hlnk_file_dst3.txt link to test_links/test_hlnk_file_dst1.txt
+lrwxr-xr-x 0 user staff 0 Sep 6 16:29 test_links/test_dir/test_slnk_file_dst2.txt -> ../test_slnk_file_src.txt
diff --git a/native_client_sdk/src/build_tools/tests/test_sdk_section.nsh b/native_client_sdk/src/build_tools/tests/test_sdk_section.nsh
new file mode 100644
index 0000000..ac78aa7
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/test_sdk_section.nsh
@@ -0,0 +1,8 @@
+Section "!Native Client SDK" NativeClientSDK
+ SectionIn RO
+ SetOutPath $INSTDIR
+ CreateDirectory "$INSTDIR\test_dir"
+ File "/oname=test_file.txt" "build_tools\tests\nsis_test_archive\test_file.txt"
+ File "/oname=test_dir\test_dir_file1.txt" "build_tools\tests\nsis_test_archive\test_dir\test_dir_file1.txt"
+ File "/oname=test_dir\test_dir_file2.txt" "build_tools\tests\nsis_test_archive\test_dir\test_dir_file2.txt"
+SectionEnd
diff --git a/native_client_sdk/src/build_tools/tests/update_manifest_test.py b/native_client_sdk/src/build_tools/tests/update_manifest_test.py
new file mode 100755
index 0000000..ce2c89a
--- /dev/null
+++ b/native_client_sdk/src/build_tools/tests/update_manifest_test.py
@@ -0,0 +1,341 @@
+#!/usr/bin/python2.6
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Unit tests for update_manifest.py."""
+
+__author__ = 'mball@google.com (Matt Ball)'
+
+import errno
+import os
+import SimpleHTTPServer
+import SocketServer
+import sys
+import tempfile
+import threading
+import unittest
+import urlparse
+
+from build_tools.sdk_tools import sdk_update
+from build_tools.sdk_tools import update_manifest
+
+TEST_DIR = os.path.dirname(os.path.abspath(__file__))
+
+
+def RemoveFile(filename):
+ '''Remove a filename if it exists and do nothing if it doesn't exist'''
+ try:
+ os.remove(filename)
+ except OSError as error:
+ if error.errno != errno.ENOENT:
+ raise
+
+
+def GetHTTPHandler(path, length=None):
+ '''Returns a simple HTTP Request Handler that only servers up a given file
+
+ Args:
+ path: path and filename of the file to serve up
+ length: (optional) only serve up the first |length| bytes
+
+ Returns:
+ A SimpleHTTPRequestHandler class'''
+ class HTTPHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
+ def do_GET(self):
+ with open(path, 'rb') as f:
+ # This code is largely lifted from SimpleHTTPRequestHandler.send_head
+ self.send_response(200)
+ self.send_header("Content-type", self.guess_type(path))
+ fs = os.fstat(f.fileno())
+ self.send_header("Content-Length", str(fs[6]))
+ self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
+ self.end_headers()
+ if length != None:
+ self.wfile.write(f.read(length))
+ else:
+ self.copyfile(f, self.wfile)
+ return HTTPHandler
+
+
+class FakeOptions(object):
+ ''' Just a place holder for options '''
+ def __init__(self):
+ self.bundle_desc_url = None
+ self.bundle_name = None
+ self.bundle_version = None
+ self.bundle_revision = None
+ self.desc = None
+ self.gsutil = os.path.join(TEST_DIR, 'fake_gsutil.bat'
+ if sys.platform == 'win32' else 'fake_gsutil.py')
+ self.linux_arch_url = None
+ self.mac_arch_url = None
+ self.manifest_file = os.path.join(TEST_DIR, 'naclsdk_manifest_test.json')
+ self.manifest_version = None
+ self.recommended = None
+ self.root_url = 'http://localhost/test_url'
+ self.stability = None
+ self.upload = False
+ self.win_arch_url = None
+
+
+class TestUpdateManifest(unittest.TestCase):
+ ''' Test basic functionality of the update_manifest package.
+
+ Note that update_manifest.py now imports sdk_update.py, so this file
+ tests the update_manifest features within sdk_update.'''
+
+ def setUp(self):
+ self._json_boilerplate=(
+ '{\n'
+ ' "bundles": [],\n'
+ ' "manifest_version": 1\n'
+ '}\n')
+ self._temp_dir = tempfile.gettempdir()
+ # os.path.join('build_tools', 'tests', 'test_archive')
+ self._manifest = update_manifest.UpdateSDKManifest()
+
+ def testJSONBoilerplate(self):
+ ''' Test creating a manifest object'''
+ self.assertEqual(self._manifest.GetManifestString(),
+ self._json_boilerplate)
+ # Test using a manifest file with a version that is too high
+ self.assertRaises(sdk_update.Error,
+ self._manifest.LoadManifestString,
+ '{"manifest_version": 2}')
+
+ def testWriteLoadManifestFile(self):
+ ''' Test writing to and loading from a manifest file'''
+ # Remove old test file
+ file_path = os.path.join(self._temp_dir, 'temp_manifest.json')
+ if os.path.exists(file_path):
+ os.remove(file_path)
+ # Create a basic manifest file
+ manifest_file = sdk_update.SDKManifestFile(file_path)
+ manifest_file.WriteFile();
+ self.assertTrue(os.path.exists(file_path))
+ # Test re-loading the file
+ manifest_file._manifest._manifest_data['manifest_version'] = 0
+ manifest_file._LoadFile()
+ self.assertEqual(manifest_file._manifest.GetManifestString(),
+ self._json_boilerplate)
+ os.remove(file_path)
+
+ def testValidateBundleName(self):
+ ''' Test validating good and bad bundle names '''
+ self.assertTrue(
+ self._manifest._ValidateBundleName('A_Valid.Bundle-Name(1)'))
+ self.assertFalse(self._manifest._ValidateBundleName('A bad name'))
+ self.assertFalse(self._manifest._ValidateBundleName('A bad/name'))
+ self.assertFalse(self._manifest._ValidateBundleName('A bad;name'))
+ self.assertFalse(self._manifest._ValidateBundleName('A bad,name'))
+
+ def testUpdateManifestVersion(self):
+ ''' Test updating the manifest version number '''
+ options = FakeOptions()
+ options.manifest_version = 99
+ self.assertEqual(self._manifest._manifest_data['manifest_version'], 1)
+ self._manifest._UpdateManifestVersion(options)
+ self.assertEqual(self._manifest._manifest_data['manifest_version'], 99)
+
+ def testVerifyAllOptionsConsumed(self):
+ ''' Test function _VerifyAllOptionsConsumed '''
+ options = FakeOptions()
+ options.opt1 = None
+ self.assertTrue(self._manifest._VerifyAllOptionsConsumed(options, None))
+ options.opt2 = 'blah'
+ self.assertRaises(update_manifest.Error,
+ self._manifest._VerifyAllOptionsConsumed,
+ options,
+ 'no bundle name')
+
+ def testBundleUpdate(self):
+ ''' Test function Bundle.Update '''
+ bundle = sdk_update.Bundle('test')
+ options = FakeOptions()
+ options.bundle_revision = 1
+ options.bundle_version = 2
+ options.desc = 'What a hoot'
+ options.stability = 'dev'
+ options.recommended = 'yes'
+ update_manifest.UpdateBundle(bundle, options)
+ self.assertEqual(bundle['revision'], 1)
+
+ def testUpdateManifestModifyTopLevel(self):
+ ''' Test function UpdateManifest: modifying top-level info '''
+ options = FakeOptions()
+ options.manifest_version = 0
+ options.bundle_name = None
+ self._manifest.UpdateManifest(options)
+ self.assertEqual(self._manifest._manifest_data['manifest_version'], 0)
+
+ def testUpdateManifestModifyBundle(self):
+ ''' Test function UpdateManifest: adding/modifying a bundle '''
+ # Add a bundle
+ options = FakeOptions()
+ options.manifest_version = 1
+ options.bundle_name = 'test'
+ options.bundle_revision = 2
+ options.bundle_version = 3
+ options.desc = 'nice bundle'
+ options.stability = 'canary'
+ options.recommended = 'yes'
+ self._manifest.UpdateManifest(options)
+ bundle = self._manifest.GetBundle('test')
+ self.assertNotEqual(bundle, None)
+ # Modify the same bundle
+ options = FakeOptions()
+ options.manifest_version = None
+ options.bundle_name = 'test'
+ options.desc = 'changed'
+ self._manifest.UpdateManifest(options)
+ bundle = self._manifest.GetBundle('test')
+ self.assertEqual(bundle['description'], 'changed')
+
+ def testUpdateManifestBadBundle1(self):
+ ''' Test function UpdateManifest: bad bundle data '''
+ options = FakeOptions()
+ options.manifest_version = None
+ options.bundle_name = 'test'
+ options.stability = 'excellent'
+ self.assertRaises(sdk_update.Error,
+ self._manifest.UpdateManifest,
+ options)
+
+ def testUpdateManifestBadBundle2(self):
+ ''' Test function UpdateManifest: incomplete bundle data '''
+ options = FakeOptions()
+ options.manifest_version = None
+ options.bundle_name = 'another_bundle'
+ self.assertRaises(sdk_update.Error,
+ self._manifest.UpdateManifest,
+ options)
+
+ def testUpdateManifestArchiveComputeSha1AndSize(self):
+ ''' Test function Archive.Update '''
+ temp_file_path = None
+ try:
+ with tempfile.NamedTemporaryFile(delete=False) as temp_file:
+ # Create a temp file with some data
+ temp_file.write(r'abcdefghijklmnopqrstuvwxyz0123456789')
+ temp_file_path = temp_file.name
+ # Windows requires that we close the file before reading from it.
+ temp_file.close()
+
+ # Create an archive with a url to the file we created above.
+ url_parts = urlparse.ParseResult('file', '', temp_file_path, '', '', '')
+ url = urlparse.urlunparse(url_parts)
+ archive = sdk_update.Archive('mac')
+ archive.Update(url)
+ self.assertEqual(archive['checksum']['sha1'],
+ 'd2985049a677bbc4b4e8dea3b89c4820e5668e3a')
+ finally:
+ if temp_file_path and os.path.exists(temp_file_path):
+ os.remove(temp_file_path)
+
+ def testUpdateManifestArchiveValidate(self):
+ ''' Test function Archive.Validate '''
+ # Test invalid host-os name
+ archive = sdk_update.Archive('atari')
+ self.assertRaises(sdk_update.Error, archive.Validate)
+ # Test missing url
+ archive['host_os'] = 'mac'
+ self.assertRaises(sdk_update.Error, archive.Validate)
+ # Valid archive
+ archive['url'] = 'http://www.google.com'
+ archive.Validate()
+ # Test invalid key name
+ archive['guess'] = 'who'
+ self.assertRaises(sdk_update.Error, archive.Validate)
+
+ def testUpdatePartialFile(self):
+ '''Test updating with a partially downloaded file'''
+ server = None
+ server_thread = None
+ temp_filename = os.path.join(self._temp_dir,
+ 'testUpdatePartialFile_temp.txt')
+ try:
+ # Create a new local server on an arbitrary port that just serves-up
+ # the first 10 bytes of this file.
+ server = SocketServer.TCPServer(
+ ("", 0), GetHTTPHandler(__file__, 10))
+ ip, port = server.server_address
+ server_thread = threading.Thread(target=server.serve_forever)
+ server_thread.start()
+
+ archive = sdk_update.Archive('mac')
+ self.assertRaises(sdk_update.Error,
+ archive.Update,
+ 'http://localhost:%s' % port)
+ try:
+ self.assertRaises(sdk_update.Error,
+ archive.DownloadToFile,
+ temp_filename)
+ finally:
+ RemoveFile(temp_filename)
+ finally:
+ if server_thread and server_thread.isAlive():
+ server.shutdown()
+ server_thread.join()
+
+ def testUpdateManifestMain(self):
+ ''' test the main function from update_manifest '''
+ temp_filename = os.path.join(self._temp_dir, 'testUpdateManifestMain.json')
+ try:
+ argv = ['--bundle-version', '0',
+ '--bundle-revision', '0',
+ '--description', 'test bundle for update_manifest unit tests',
+ '--bundle-name', 'test_bundle',
+ '--stability', 'dev',
+ '--recommended', 'no',
+ '--manifest-file', temp_filename]
+ update_manifest.main(argv)
+ finally:
+ RemoveFile(temp_filename)
+
+ def testHandleSDKTools(self):
+ '''Test the handling of the sdk_tools bundle'''
+ options = FakeOptions()
+ options.bundle_name = 'sdk_tools'
+ options.upload = True
+ options.bundle_version = 0
+ self.assertRaises(
+ update_manifest.Error,
+ update_manifest.UpdateSDKManifestFile(options).HandleBundles)
+ options.bundle_version = None
+ options.bundle_revision = 0
+ self.assertRaises(
+ update_manifest.Error,
+ update_manifest.UpdateSDKManifestFile(options).HandleBundles)
+ options.bundle_revision = None
+ update_manifest.UpdateSDKManifestFile(options).HandleBundles()
+
+ def testHandlePepper(self):
+ '''Test the handling of pepper bundles'''
+ options = FakeOptions()
+ options.bundle_name = 'pepper'
+ options.bundle_version = None
+ self.assertRaises(
+ update_manifest.Error,
+ update_manifest.UpdateSDKManifestFile(options).HandleBundles)
+ options.bundle_version = 1
+ options.bundle_revision = None
+ self.assertRaises(
+ update_manifest.Error,
+ update_manifest.UpdateSDKManifestFile(options).HandleBundles)
+ options.bundle_revision = 0
+ update_manifest.UpdateSDKManifestFile(options).HandleBundles()
+ options.bundle_name = 'pepper_1'
+ options.bundle_version = None
+ update_manifest.UpdateSDKManifestFile(options).HandleBundles()
+
+
+def main():
+ suite = unittest.TestLoader().loadTestsFromTestCase(TestUpdateManifest)
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+
+ return int(not result.wasSuccessful())
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/native_client_sdk/src/codereview.settings b/native_client_sdk/src/codereview.settings
new file mode 100644
index 0000000..d1405a3
--- /dev/null
+++ b/native_client_sdk/src/codereview.settings
@@ -0,0 +1,9 @@
+# This file is used by gcl to get repository specific information.
+CODE_REVIEW_SERVER: codereview.chromium.org
+CC_LIST: native-client-reviews@googlegroups.com
+VIEW_VC: http://code.google.com/p/nativeclient-sdk/source/detail?r=
+STATUS: http://naclsdk-status.appspot.com/status
+TRY_ON_UPLOAD: True
+TRYSERVER_PROJECT: naclsdk
+TRYSERVER_SVN_URL: svn://svn.chromium.org/chrome-try/try-nacl
+DEPTH: 0
diff --git a/native_client_sdk/src/documentation/Doxyfile b/native_client_sdk/src/documentation/Doxyfile
new file mode 100644
index 0000000..54b8213
--- /dev/null
+++ b/native_client_sdk/src/documentation/Doxyfile
@@ -0,0 +1,1663 @@
+# Doxyfile 1.7.2
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = "c_salt"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = scons-out/documentation
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = YES
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = ./c_salt \
+ ./examples \
+ ./documentation
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.vhd *.vhdl
+
+FILE_PATTERNS = *.h \
+ *.dox
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS = _*.h
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH = ./documentation/images-dox
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER = documentation/header.dox
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER = documentation/footer.dox
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET = documentation/stylesheet-dox.css
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 30
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [0,1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+# Note that a value of 0 will completely suppress the enum values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 251
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = YES
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED = __native_client__ \
+ DOXYGEN_SHOULD_SKIP_THIS \
+ __attribute__(x)= \
+ EXTERN_C_BEGIN= \
+ EXTERN_C_END=
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = NO
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = FreeSans.ttf
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = YES
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif.
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 10
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/native_client_sdk/src/documentation/build.scons b/native_client_sdk/src/documentation/build.scons
new file mode 100644
index 0000000..1b6dd43
--- /dev/null
+++ b/native_client_sdk/src/documentation/build.scons
@@ -0,0 +1,28 @@
+#! -*- python -*-
+#
+# Copyright (c) 2010 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""scons script for building the Doxygen-based documentation for c_salt"""
+
+Import('env')
+
+env.SetDefault(DOXYGEN=ARGUMENTS.get('DOXYGEN', 'doxygen'))
+
+# Gate on host platform rather than target, since all are supported.
+if env['PLATFORM'] in ['win32', 'cygwin']:
+ env['ENV']['NACL_SDK_PLATFORM'] = 'windows'
+elif env['PLATFORM'] in ['darwin']:
+ env['ENV']['NACL_SDK_PLATFORM'] = 'mac'
+else:
+ env['ENV']['NACL_SDK_PLATFORM'] = 'linux'
+
+# The output is generated into scons-out/doc (c.f. Doxyfile).
+# Point your browser at scons-out/doc/html/index.html
+node = env.Command(
+ target='doxygen.log',
+ source='Doxyfile',
+ action='${DOXYGEN} documentation/Doxyfile 2>&1 > ${TARGET}')
+AlwaysBuild(node)
+env.AddNodeAliases(node, [], 'docs')
diff --git a/native_client_sdk/src/documentation/check.sh b/native_client_sdk/src/documentation/check.sh
new file mode 100755
index 0000000..ce3b49f
--- /dev/null
+++ b/native_client_sdk/src/documentation/check.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# simple script to check html via tidy. Either specify html files on
+# command line or rely on default which checks all html files in
+# current directory
+set -o nounset
+set -o errexit
+
+
+CheckFile () {
+ echo "========================================"
+ echo "checking $1"
+ echo "========================================"
+ tidy -e -q $1
+}
+
+
+if [ $# -eq 0 ] ; then
+ for file in *.html ; do
+ CheckFile ${file}
+ done
+else
+ for file in $* ; do
+ CheckFile ${file}
+ done
+fi
diff --git a/native_client_sdk/src/documentation/footer.dox b/native_client_sdk/src/documentation/footer.dox
new file mode 100644
index 0000000..8a944c1
--- /dev/null
+++ b/native_client_sdk/src/documentation/footer.dox
@@ -0,0 +1,22 @@
+ <p id="license">
+ Except as otherwise
+ <a href="http://code.google.com/policies.html#restrictions">noted</a>,
+ the content of this page is licensed under a
+ <a href="http://www.google.com/url?sa=D&amp;q=http%3A%2F%2Fcreativecommons.org/licenses/by/2.5/">Creative Commons
+ Attribution 2.5 license</a>.
+ </p>
+
+ <p align="center"><b>Warning: This API is
+ currently in development and is subject to change.</b></p>
+
+ <address>
+ &copy;2010 Google
+ </address>
+
+ <address>
+ Generated $date by
+ <a href="http://www.doxygen.org/index.html">doxygen</a> $doxygenversion
+ </address>
+
+ </body>
+</html>
diff --git a/native_client_sdk/src/documentation/header.dox b/native_client_sdk/src/documentation/header.dox
new file mode 100644
index 0000000..a261dd3
--- /dev/null
+++ b/native_client_sdk/src/documentation/header.dox
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+ <title>$title</title>
+ <link href="./tabs.css" rel="stylesheet" type="text/css">
+ <link href="./stylesheet-dox.css" rel="stylesheet" type="text/css">
+ <link href="./stylesheet-dox-all.css" rel="stylesheet" type="text/css">
+ </head>
+ <body>
+ <div id="toplinks">
+ <a href="http://code.google.com/p/nativeclient-sdk/">
+ Native Client SDK homepage</a>
+ </div>
diff --git a/native_client_sdk/src/documentation/images-dox/README.txt b/native_client_sdk/src/documentation/images-dox/README.txt
new file mode 100644
index 0000000..9a9e18c
--- /dev/null
+++ b/native_client_sdk/src/documentation/images-dox/README.txt
@@ -0,0 +1,9 @@
+This directory holds all images that go into the doxygen-generated
+API reference doc. To include an image, use code like the following:
+
+@image html figure.jpg
+
+If you want a caption, specify it like this:
+
+@image html figure.jpg "Test image"
+
diff --git a/native_client_sdk/src/documentation/index.dox b/native_client_sdk/src/documentation/index.dox
new file mode 100644
index 0000000..2af602e
--- /dev/null
+++ b/native_client_sdk/src/documentation/index.dox
@@ -0,0 +1,46 @@
+/** @mainpage c_salt Reference Documentation
+
+ This reference documentation describes the c_salt API, which is
+ an open-source API for browser plugins that layers on
+ top of the Pepper API and the NPAPI.
+ You can use the c_salt API
+ in <a href="http://code.google.com/p/nativeclient-sdk">Native Client</a>
+ modules to communicate with the Google Chrome browser.
+ This page has the following contents:
+
+ - @ref reading
+ - @ref modules
+ - @ref about
+
+
+ @section reading Before you start
+
+ This documentation assumes that you have read and understood
+ the following pages in the Native Client SDK project:
+
+ - <a href="http://code.google.com/p/nativeclient-sdk/">Getting started</a>
+
+
+ @section modules API categories
+
+The c_salt API consists of a C++ API in the c_salt namespace.
+
+(Details TBD)...
+
+
+ @section about About this doc
+
+ <p>
+ The tabs at the top of each page take you to the following sections.
+ </p>
+
+ - <b>Main Page</b>: This page
+ - <a href="modules.html"><b>Modules</b></a>: Lets you find API by functional area
+ - <a href="annotated.html"><b>Data Structures</b></a>:
+ List of classes and data structures in c_salt.
+ - <a href="files.html"><b>Files</b></a>:
+ The header files used to generate this documentation,
+ with file descriptions and links to generated doc.
+ Don't miss the <a href="globals.html">File member index</a>.
+
+ */ \ No newline at end of file
diff --git a/native_client_sdk/src/documentation/modules.dox b/native_client_sdk/src/documentation/modules.dox
new file mode 100644
index 0000000..3923ccb
--- /dev/null
+++ b/native_client_sdk/src/documentation/modules.dox
@@ -0,0 +1,22 @@
+// This is a doxygen file that describes the documentation groups
+
+/**
+
+@defgroup c_salt C Salt C++ API
+Description of c_salt C++ API goes here.
+
+@defgroup examples c_salt examples
+Here are the examples that use c_salt
+
+@defgroup npapi NPAPI bindings for c_salt
+@ingroup c_salt
+Defines the NPAPI bindings for c_salt
+
+@defgroup ppapi PPAPI bindings for c_salt
+@ingroup c_salt
+Defines the PPAPI (i.e. Pepper2) bindings for c_salt
+
+@defgroup tests c_salt integration and unit tests
+Integration and unit tests for c_salt.
+
+*/
diff --git a/native_client_sdk/src/documentation/stylesheet-dox.css b/native_client_sdk/src/documentation/stylesheet-dox.css
new file mode 100644
index 0000000..81d1a42
--- /dev/null
+++ b/native_client_sdk/src/documentation/stylesheet-dox.css
@@ -0,0 +1,478 @@
+body, table, div, p, dl {
+ font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif;
+ font-size: 12px;
+}
+
+/* @group Heading Levels */
+
+h1 {
+ text-align: center;
+ font-size: 150%;
+}
+
+h1 a, h2 a, h3 a, h4 a {
+ font-weight:bold;
+}
+
+h2 {
+ font-size: 120%;
+ margin-top: 2.0em;
+ margin-bottom: 0.5em;
+}
+
+h3 {
+ font-size: 100%;
+}
+
+div.contents {
+ margin-top: 2.0em;
+}
+
+/* @end */
+
+caption {
+ font-weight: bold;
+ font-size: 9px;
+}
+
+div.qindex, div.navpath, div.navtab{
+ background-color: #e8eef2;
+ border: 1px solid #84b0c7;
+ text-align: center;
+ margin: 2px;
+ padding: 2px;
+}
+
+div.qindex, div.navpath {
+ width: 100%;
+ line-height: 140%;
+}
+
+div.navtab {
+ margin-right: 15px;
+}
+
+/* @group Link Styling */
+
+a {
+ color: #153788;
+ font-weight: normal;
+ text-decoration: none;
+}
+
+.contents a:visited {
+ color: #1b77c5;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+a.qindex {
+ font-weight: bold;
+}
+
+a.qindexHL {
+ font-weight: bold;
+ background-color: #6666cc;
+ color: #ffffff;
+ border: 1px double #9295C2;
+}
+
+a.el {
+ font-weight: bold;
+}
+
+a.elRef {
+}
+
+a.code {
+}
+
+a.codeRef {
+}
+
+/* @end */
+
+dl.el {
+ margin-left: -1cm;
+}
+
+.fragment {
+ font-family: monospace, fixed;
+ font-size: 105%;
+}
+
+pre.fragment {
+ border: 1px solid #CCCCCC;
+ background-color: #f5f5f5;
+ padding: 4px 6px;
+ margin: 4px 8px 4px 2px;
+}
+
+div.ah {
+ background-color: black;
+ font-weight: bold;
+ color: #ffffff;
+ margin-bottom: 3px;
+ margin-top: 3px
+}
+
+div.groupHeader {
+ margin-left: 16px;
+ margin-top: 12px;
+ margin-bottom: 6px;
+ font-weight: bold;
+}
+
+div.groupText {
+ margin-left: 16px;
+ font-style: italic;
+}
+
+body {
+ background: white;
+ color: black;
+ margin-right: 20px;
+ margin-left: 20px;
+}
+
+td.indexkey {
+ background-color: #e8eef2;
+ font-weight: bold;
+ border: 1px solid #CCCCCC;
+ margin: 2px 0px 2px 0;
+ padding: 2px 10px;
+}
+
+td.indexvalue {
+ background-color: #e8eef2;
+ border: 1px solid #CCCCCC;
+ padding: 2px 10px;
+ margin: 2px 0px;
+}
+
+tr.memlist {
+ background-color: #f0f0f0;
+}
+
+p.formulaDsp {
+ text-align: center;
+}
+
+img.formulaDsp {
+}
+
+img.formulaInl {
+ vertical-align: middle;
+}
+
+/* @group Code Colorization */
+
+span.keyword {
+ color: #008000
+}
+
+span.keywordtype {
+ color: #604020
+}
+
+span.keywordflow {
+ color: #e08000
+}
+
+span.comment {
+ color: #800000
+}
+
+span.preprocessor {
+ color: #806020
+}
+
+span.stringliteral {
+ color: #002080
+}
+
+span.charliteral {
+ color: #008080
+}
+
+span.vhdldigit {
+ color: #ff00ff
+}
+
+span.vhdlchar {
+ color: #000000
+}
+
+span.vhdlkeyword {
+ color: #700070
+}
+
+span.vhdllogic {
+ color: #ff0000
+}
+
+/* @end */
+
+.search {
+ color: #003399;
+ font-weight: bold;
+}
+
+form.search {
+ margin-bottom: 0px;
+ margin-top: 0px;
+}
+
+input.search {
+ font-size: 75%;
+ color: #000080;
+ font-weight: normal;
+ background-color: #e8eef2;
+}
+
+td.tiny {
+ font-size: 75%;
+}
+
+.dirtab {
+ padding: 4px;
+ border-collapse: collapse;
+ border: 1px solid #84b0c7;
+}
+
+th.dirtab {
+ background: #e8eef2;
+ font-weight: bold;
+}
+
+hr {
+ height: 0;
+ border: none;
+ border-top: 1px solid #666;
+}
+
+/* @group Member Descriptions */
+
+.mdescLeft, .mdescRight,
+.memItemLeft, .memItemRight,
+.memTemplItemLeft, .memTemplItemRight, .memTemplParams {
+ background-color: #FAFAFA;
+ border: none;
+ margin: 4px;
+ padding: 1px 0 0 8px;
+}
+
+.mdescLeft, .mdescRight {
+ padding: 0px 8px 4px 8px;
+ color: #555;
+}
+
+.memItemLeft, .memItemRight, .memTemplParams {
+ border-top: 1px solid #ccc;
+}
+
+.memTemplParams {
+ color: #606060;
+}
+
+/* @end */
+
+/* @group Member Details */
+
+/* Styles for detailed member documentation */
+
+.memtemplate {
+ font-size: 80%;
+ color: #606060;
+ font-weight: normal;
+ margin-left: 3px;
+}
+
+.memnav {
+ background-color: #e8eef2;
+ border: 1px solid #84b0c7;
+ text-align: center;
+ margin: 2px;
+ margin-right: 15px;
+ padding: 2px;
+}
+
+.memitem {
+ padding: 0;
+}
+
+.memname {
+ white-space: nowrap;
+ font-weight: bold;
+}
+
+.memproto, .memdoc {
+ border: 1px solid #84b0c7;
+}
+
+.memproto {
+ padding: 0;
+ background-color: #d5e1e8;
+ font-weight: bold;
+ -webkit-border-top-left-radius: 8px;
+ -webkit-border-top-right-radius: 8px;
+ -moz-border-radius-topleft: 8px;
+ -moz-border-radius-topright: 8px;
+}
+
+.memdoc {
+ padding: 2px 5px;
+ background-color: #eef3f5;
+ border-top-width: 0;
+ -webkit-border-bottom-left-radius: 8px;
+ -webkit-border-bottom-right-radius: 8px;
+ -moz-border-radius-bottomleft: 8px;
+ -moz-border-radius-bottomright: 8px;
+}
+
+.memdoc p, .memdoc dl, .memdoc ul {
+ margin: 6px 0;
+}
+
+.paramkey {
+ text-align: right;
+}
+
+.paramtype {
+ white-space: nowrap;
+}
+
+.paramname {
+ color: #602020;
+ white-space: nowrap;
+}
+.paramname em {
+ font-style: normal;
+}
+
+/* @end */
+
+/* @group Directory (tree) */
+
+/* for the tree view */
+
+.ftvtree {
+ font-family: sans-serif;
+ margin: 0.5em;
+}
+
+/* these are for tree view when used as main index */
+
+.directory {
+ font-size: 9pt;
+ font-weight: bold;
+}
+
+.directory h3 {
+ margin: 0px;
+ margin-top: 1em;
+ font-size: 11pt;
+}
+
+/*
+The following two styles can be used to replace the root node title
+with an image of your choice. Simply uncomment the next two styles,
+specify the name of your image and be sure to set 'height' to the
+proper pixel height of your image.
+*/
+
+/*
+.directory h3.swap {
+ height: 61px;
+ background-repeat: no-repeat;
+ background-image: url("yourimage.gif");
+}
+.directory h3.swap span {
+ display: none;
+}
+*/
+
+.directory > h3 {
+ margin-top: 0;
+}
+
+.directory p {
+ margin: 0px;
+ white-space: nowrap;
+}
+
+.directory div {
+ display: none;
+ margin: 0px;
+}
+
+.directory img {
+ vertical-align: -30%;
+}
+
+/* these are for tree view when not used as main index */
+
+.directory-alt {
+ font-size: 100%;
+ font-weight: bold;
+}
+
+.directory-alt h3 {
+ margin: 0px;
+ margin-top: 1em;
+ font-size: 11pt;
+}
+
+.directory-alt > h3 {
+ margin-top: 0;
+}
+
+.directory-alt p {
+ margin: 0px;
+ white-space: nowrap;
+}
+
+.directory-alt div {
+ display: none;
+ margin: 0px;
+}
+
+.directory-alt img {
+ vertical-align: -30%;
+}
+
+/* @end */
+
+address {
+ font-style: normal;
+ text-align: center;
+ font-size: 90%;
+ color: gray;
+}
+
+DIV.tabs A, #toplinks
+{
+ font-size : 9px;
+}
+
+#toplinks {
+ text-align: right;
+ margin-bottom: -1.9em;
+}
+
+.pending {
+ /* display:none; */
+ color:red; font-weight:bold;
+}
+
+#license {
+ color:gray;
+ font-size:90%;
+ border-top:1px solid;
+ border-color:gray;
+ padding-top:1em;
+ margin-top:3em;
+ text-align:center;
+}
diff --git a/native_client_sdk/src/documentation/stylesheet.css b/native_client_sdk/src/documentation/stylesheet.css
new file mode 100644
index 0000000..3d17af0
--- /dev/null
+++ b/native_client_sdk/src/documentation/stylesheet.css
@@ -0,0 +1,101 @@
+@charset "utf-8";
+a:link { color: #0000cc; }
+body {
+ background: #fff; margin: 3px 8px;
+ font-family: arial, sans-serif;
+}
+
+body { font-size: 83%;}
+pre, code, p kbd { font-size: 120%;}
+
+h1 { font-size: x-large; }
+h2 { font-size: large; }
+h3 { font-size: medium; }
+h4 { font-size: small; }
+
+img { border: 1px solid #ccc; }
+
+pre {
+ margin-left: 2em;
+ padding: 0.5em;
+ border-left: 3px solid #ccc;
+}
+
+pre.no-bar {
+ padding: 0;
+ border-left: 0;
+}
+
+table {
+ border: 1px solid #999999;
+ border-collapse: collapse;
+}
+
+th {
+ border: 1px solid #999999;
+ padding: 0.25em 0.5em;
+ background: #ccc;
+}
+
+td {
+ border-bottom: 1px dotted #999999;
+ border-left: 1px dotted #999999;
+ text-align: left;
+ padding: 0.25em 0.5em;
+}
+
+td pre.listing {
+ margin: 0.25em 0.25em 0.25em 0;
+}
+
+pre.listing {
+ padding: 0;
+ background-color: #fff;
+ border: none;
+}
+
+div#toplink {
+ font-size: small;
+ text-align: right;
+ margin-bottom: -2em;
+}
+
+.caption {
+ font-weight:bold
+}
+
+#license {
+ color:gray;
+ font-size:small;
+ border-top:1px solid;
+ border-color:gray;
+ padding:1em;
+ margin-top:3em;
+ text-align:center;
+}
+
+.technote {
+ border:1px solid #999;
+ background:#ccc;
+ margin:0em 5em;
+ padding: 0.25em 0.5em;
+}
+
+.notapplicable {
+ color:lightgray;
+ font-style:italic;
+}
+
+pre kbd {
+ background:rgb(221, 248, 204);
+}
+
+table caption {
+ font-style:italic;
+ text-align:left;
+}
+
+.comment {
+ display:none; /* comment this line out if you want to see comments */
+ color:red; font-weight:bold;
+}
diff --git a/native_client_sdk/src/examples/build.scons b/native_client_sdk/src/examples/build.scons
new file mode 100644
index 0000000..f804d4b
--- /dev/null
+++ b/native_client_sdk/src/examples/build.scons
@@ -0,0 +1,82 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import shutil
+import sys
+
+"""
+Build file for the NaCl SDK Examples
+
+This file runs all the scons files in the various example sub-directories.
+Do not invoke this script directly, but instead use the scons or scons.bat
+wrapper function. E.g.
+
+Linux or Mac:
+ ./scons [Options...]
+
+Windows:
+ scons.bat [Options...]
+"""
+
+#------------------------------------------------------------------------------
+HELP_STRING = """
+===============================================================================
+Help for NaCl SDK Examples
+===============================================================================
+
+* cleaning: ./scons -c
+* build a target: ./scons <target>
+* clean a target: ./scons -c <target>
+
+Supported targets:
+ * fullscreen_tumbler Build the fullscreen-tumbler example.
+ * geturl Build the geturl example.
+ * hello_world Build the hello_world example.
+ * hello_world_c Build the hello_world_c example.
+ * input_events Build the input_events example.
+ * load_progress Build the load_progress example.
+ * mouselock Build the mouselock example.
+ * multithreaded_input_events Build the multithreaded input_events example.
+ * pi_generator Build the pi_generator example.
+ * pong Build the pong example.
+ * sine_synth Build the sine_synth example.
+ * tumbler Build the tumbler example.
+"""
+
+example_directories = [
+ 'fullscreen_tumbler',
+ 'geturl',
+ 'hello_world',
+ 'hello_world_c',
+ 'input_events',
+ 'load_progress',
+ 'mouselock',
+ 'multithreaded_input_events',
+ 'pi_generator',
+ 'pong',
+ 'sine_synth',
+ 'tumbler',
+ ]
+
+Help(HELP_STRING)
+
+staging_dir = os.path.abspath(os.getenv(
+ 'NACL_INSTALL_ROOT', os.path.join(os.getenv('NACL_SDK_ROOT', '.'),
+ 'staging')))
+general_files = Install(staging_dir, ['httpd.py'])
+general_files.extend(InstallAs(os.path.join(staging_dir, 'index.html'),
+ 'index_staging.html'))
+
+if sys.platform in ['win32', 'cygwin']:
+ general_files.extend(Install(staging_dir, 'httpd.cmd'))
+
+SConscript([os.path.join(dir, 'build.scons') for dir in example_directories])
+
+Default(['install'] + general_files + example_directories)
+if GetOption('clean'):
+ print "Removing the staging directory at %s" % staging_dir
+ shutil.rmtree(staging_dir, ignore_errors=True)
diff --git a/native_client_sdk/src/examples/common/check_browser.js b/native_client_sdk/src/examples/common/check_browser.js
new file mode 100644
index 0000000..9636ad0
--- /dev/null
+++ b/native_client_sdk/src/examples/common/check_browser.js
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2011 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * @fileoverview This file provides a BrowserChecker Javascript class.
+ * Users can create a BrowserChecker object, invoke checkBrowser(|version|),
+ * and then use getIsValidBrowser() and getBrowserSupportStatus()
+ * to determine if the browser version is greater than |version|
+ * and if the Native Client plugin is found.
+ */
+
+// Create a namespace object
+var browser_version = browser_version || {};
+
+/**
+ * Class to provide checking for version and NativeClient.
+ * @param {integer} arg1 An argument that indicates major version of Chrome we
+ * require, such as 14.
+ */
+
+/**
+ * Constructor for the BrowserChecker. Sets the major version of
+ * Chrome that is required to |minChromeVersion|.
+ * @param minChromeVersion The earliest major version of chrome that
+ * is supported. If the Chrome browser version is less than
+ * |minChromeVersion| then |isValidBrowswer| will be set to false.
+ * @param opt_maxChromeVersion Ignored. Retained for backwards compatibility.
+ * @param appVersion The application version string.
+ * @param plugins The plugins that exist in the browser.
+ * @constructor
+ */
+browser_version.BrowserChecker = function(minChromeVersion,
+ appVersion, plugins,
+ opt_maxChromeVersion) {
+ /**
+ * Version specified by the user. This class looks to see if the browser
+ * version is >= |minChromeVersion_|.
+ * @type {integer}
+ * @private
+ */
+ this.minChromeVersion_ = minChromeVersion;
+
+ /**
+ * List of Browser plugin objects.
+ * @type {Ojbect array}
+ * @private
+ */
+ this.plugins_ = plugins;
+
+ /**
+ * Application version string from the Browser.
+ * @type {integer}
+ * @private
+ */
+ this.appVersion_ = appVersion;
+
+ /**
+ * Flag used to indicate if the browser has Native Client and is if the
+ * browser version is recent enough.
+ * @type {boolean}
+ * @private
+ */
+ this.isValidBrowser_ = false;
+
+ /**
+ * Actual major version of Chrome -- found by querying the browser.
+ * @type {integer}
+ * @private
+ */
+ this.chromeVersion_ = null;
+
+ /**
+ * Browser support status. This allows the user to get a detailed status
+ * rather than using this.browserSupportMessage.
+ */
+ this.browserSupportStatus_ =
+ browser_version.BrowserChecker.StatusValues.UNKNOWN;
+}
+
+/**
+ * The values used for BrowserChecker status to indicate success or
+ * a specific error.
+ * @enum {id}
+ */
+browser_version.BrowserChecker.StatusValues = {
+ UNKNOWN: 0,
+ NACL_ENABLED: 1,
+ UNKNOWN_BROWSER: 2,
+ CHROME_VERSION_TOO_OLD: 3,
+ NACL_NOT_ENABLED: 4,
+ NOT_USING_SERVER: 5
+};
+
+/**
+ * Determines if the plugin with name |name| exists in the browser.
+ * @param {string} name The name of the plugin.
+ * @param {Object array} plugins The plugins in this browser.
+ * @return {bool} |true| if the plugin is found.
+ */
+browser_version.BrowserChecker.prototype.pluginExists = function(name,
+ plugins) {
+ for (var index=0; index < plugins.length; index++) {
+ var plugin = this.plugins_[index];
+ var plugin_name = plugin['name'];
+ // If the plugin is not found, you can use the Javascript console
+ // to see the names of the plugins that were found when debugging.
+ if (plugin_name.indexOf(name) != -1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Returns browserSupportStatus_ which indicates if the browser supports
+ * Native Client. Values are defined as literals in
+ * browser_version.BrowserChecker.StatusValues.
+ * @ return {int} Level of NaCl support.
+ */
+browser_version.BrowserChecker.prototype.getBrowserSupportStatus = function() {
+ return this.browserSupportStatus_;
+}
+
+/**
+ * Returns isValidBrowser (true/false) to indicate if the browser supports
+ * Native Client.
+ * @ return {bool} If this browser has NativeClient and correct version.
+ */
+browser_version.BrowserChecker.prototype.getIsValidBrowser = function() {
+ return this.isValidBrowser_;
+}
+
+/**
+ * Checks to see if this browser can support Native Client applications.
+ * For Chrome browsers, checks to see if the "Native Client" plugin is
+ * enabled.
+ */
+browser_version.BrowserChecker.prototype.checkBrowser = function() {
+ var versionPatt = /Chrome\/(\d+)\.(\d+)\.(\d+)\.(\d+)/;
+ var result = this.appVersion_.match(versionPatt);
+
+ // |result| stores the Chrome version number.
+ if (!result) {
+ this.isValidBrowser_ = false;
+ this.browserSupportStatus_ =
+ browser_version.BrowserChecker.StatusValues.UNKNOWN_BROWSER;
+ } else {
+ this.chromeVersion_ = result[1];
+ // We know we have Chrome, check version and/or plugin named Native Client
+ if (this.chromeVersion_ >= this.minChromeVersion_) {
+ var found_nacl = this.pluginExists('Native Client', this.plugins_);
+ if (found_nacl) {
+ this.isValidBrowser_ = true;
+ this.browserSupportStatus_ =
+ browser_version.BrowserChecker.StatusValues.NACL_ENABLED;
+ } else {
+ this.isValidBrowser_ = false;
+ this.browserSupportStatus_ =
+ browser_version.BrowserChecker.StatusValues.NACL_NOT_ENABLED;
+ }
+ } else {
+ // We are in a version that is less than |minChromeVersion_|
+ this.isValidBrowser_ = false;
+ this.browserSupportStatus_ =
+ browser_version.BrowserChecker.StatusValues.CHROME_VERSION_TOO_OLD;
+ }
+ }
+ var my_protocol = window.location.protocol;
+ if (my_protocol.indexOf('file') == 0) {
+ this.isValidBrowser_ = false;
+ this.browserSupportStatus_ =
+ browser_version.BrowserChecker.StatusValues.NOT_USING_SERVER;
+ }
+}
+
diff --git a/native_client_sdk/src/examples/favicon.ico b/native_client_sdk/src/examples/favicon.ico
new file mode 100644
index 0000000..ee7c943
--- /dev/null
+++ b/native_client_sdk/src/examples/favicon.ico
Binary files differ
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/bind.js b/native_client_sdk/src/examples/fullscreen_tumbler/bind.js
new file mode 100644
index 0000000..92fbbd2
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/bind.js
@@ -0,0 +1,22 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview This class implements an extension to Function object that
+ * lets you bind a scope for |this| to a function.
+ */
+
+/**
+ * Bind a scope to a function. Used to bind an object to |this| for event
+ * handlers.
+ * @param {!Object} scope The scope in which the function executes. |scope|
+ * becomes |this| during function execution.
+ * @return {function} the bound version of the original function.
+ */
+Function.prototype.bind = function(scope) {
+ var boundContext = this;
+ return function() {
+ return boundContext.apply(scope, arguments);
+ }
+}
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/build.scons b/native_client_sdk/src/examples/fullscreen_tumbler/build.scons
new file mode 100644
index 0000000..26d14f2
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/build.scons
@@ -0,0 +1,66 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import make_nacl_env
+import nacl_utils
+import os
+
+nacl_env = make_nacl_env.NaClEnvironment(
+ use_c_plus_plus_libs=True, nacl_platform=os.getenv('NACL_TARGET_PLATFORM'),
+ install_subdir='fullscreen_tumbler', lib_prefix='..')
+nacl_env.Append(
+ # Add a CPPPATH that enables the full-path #include directives, such as
+ # #include "examples/sine_synth/sine_synth.h"
+ CPPPATH=[os.path.dirname(os.path.dirname(os.getcwd()))],
+ # Strict ANSI compliance.
+ EXTRA_CCFLAGS=['-pedantic'],
+ LIBS=['ppapi_gles2'],
+ )
+
+sources = [
+ 'cube.cc',
+ 'opengl_context.cc',
+ 'scripting_bridge.cc',
+ 'shader_util.cc',
+ 'transforms.cc',
+ 'tumbler.cc',
+ 'tumbler_module.cc',
+ ]
+
+opt_nexes, dbg_nexes = nacl_env.AllNaClModules(sources, 'fullscreen_tumbler')
+
+# This target is used by the SDK build system to provide a prebuilt version
+# of the example in the SDK installer.
+nacl_env.InstallPrebuilt('fullscreen_tumbler')
+
+common_files = [
+ 'check_browser.js',
+ ]
+common_files = [
+ os.path.join(os.path.dirname(os.getcwd()), 'common', common_file)
+ for common_file in common_files]
+
+app_files = [
+ 'fullscreen_tumbler.html',
+ 'fullscreen_tumbler.nmf',
+ 'bind.js',
+ 'dragger.js',
+ 'trackball.js',
+ 'tumbler.js',
+ 'vector3.js',
+ ]
+
+# Split the install of the .nexes from the other app sources so that the strip
+# action is applied to the .nexes only.
+install_nexes = nacl_env.NaClStrippedInstall(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=opt_nexes)
+install_app = nacl_env.Install(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=app_files)
+common_dir = os.path.join(os.path.dirname(nacl_env['NACL_INSTALL_ROOT']),
+ 'common')
+install_common = nacl_env.Install(dir=common_dir, source=common_files)
+nacl_env.Alias('install',
+ source=[install_app, install_common, install_nexes])
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/callback.h b/native_client_sdk/src/examples/fullscreen_tumbler/callback.h
new file mode 100644
index 0000000..4d67262
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/callback.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_TUMBLER_CALLBACK_H_
+#define EXAMPLES_TUMBLER_CALLBACK_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace tumbler {
+
+class ScriptingBridge;
+
+// Templates used to support method call-backs when a method or property is
+// accessed from the browser code.
+
+// Class suite used to publish a method name to Javascript. Typical use is
+// like this:
+// photo::MethodCallback<Calculator>* calculate_callback_;
+// calculate_callback_ =
+// new scripting::MethodCallback<Calculator>(this,
+// &Calculator::Calculate);
+// bridge->AddMethodNamed("calculate", calculate_callback_);
+// ...
+// delete calculate_callback_;
+//
+// The caller must delete the callback.
+
+// Methods get parameters as a dictionary that maps parameter names to values.
+typedef std::map<std::string, std::string> MethodParameter;
+
+// Pure virtual class used in STL containers.
+class MethodCallbackExecutor {
+ public:
+ virtual ~MethodCallbackExecutor() {}
+ virtual void Execute(
+ const ScriptingBridge& bridge,
+ const MethodParameter& parameters) = 0;
+};
+
+template <class T>
+class MethodCallback : public MethodCallbackExecutor {
+ public:
+ typedef void (T::*Method)(
+ const ScriptingBridge& bridge,
+ const MethodParameter& parameters);
+
+ MethodCallback(T* instance, Method method)
+ : instance_(instance), method_(method) {}
+ virtual ~MethodCallback() {}
+ virtual void Execute(
+ const ScriptingBridge& bridge,
+ const MethodParameter& parameters) {
+ // Use "this->" to force C++ to look inside our templatized base class; see
+ // Effective C++, 3rd Ed, item 43, p210 for details.
+ ((this->instance_)->*(this->method_))(bridge, parameters);
+ }
+
+ private:
+ T* instance_;
+ Method method_;
+};
+
+template <class T>
+class ConstMethodCallback : public MethodCallbackExecutor {
+ public:
+ typedef void (T::*ConstMethod)(
+ const ScriptingBridge& bridge,
+ const MethodParameter& parameters) const;
+
+ ConstMethodCallback(const T* instance, ConstMethod method)
+ : instance_(instance), const_method_(method) {}
+ virtual ~ConstMethodCallback() {}
+ virtual void Execute(
+ const ScriptingBridge& bridge,
+ const MethodParameter& parameters) {
+ // Use "this->" to force C++ to look inside our templatized base class; see
+ // Effective C++, 3rd Ed, item 43, p210 for details.
+ ((this->instance_)->*(this->const_method_))(bridge, parameters);
+ }
+
+ private:
+ const T* instance_;
+ ConstMethod const_method_;
+};
+
+} // namespace tumbler
+
+#endif // EXAMPLES_TUMBLER_CALLBACK_H_
+
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/cube.cc b/native_client_sdk/src/examples/fullscreen_tumbler/cube.cc
new file mode 100644
index 0000000..5b8bc4c
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/cube.cc
@@ -0,0 +1,268 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/fullscreen_tumbler/cube.h"
+
+#include <algorithm>
+
+#include "examples/fullscreen_tumbler/shader_util.h"
+#include "examples/fullscreen_tumbler/transforms.h"
+
+namespace tumbler {
+
+static const size_t kVertexCount = 24;
+static const int kIndexCount = 36;
+
+Cube::Cube(SharedOpenGLContext opengl_context)
+ : opengl_context_(opengl_context),
+ width_(1),
+ height_(1) {
+ eye_[0] = eye_[1] = 0.0f;
+ eye_[2] = 2.0f;
+ orientation_[0] = 0.0f;
+ orientation_[1] = 0.0f;
+ orientation_[2] = 0.0f;
+ orientation_[3] = 1.0f;
+}
+
+Cube::~Cube() {
+ glDeleteBuffers(3, cube_vbos_);
+ glDeleteProgram(shader_program_object_);
+}
+
+void Cube::PrepareOpenGL() {
+ CreateShaders();
+ CreateCube();
+ glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ glEnable(GL_DEPTH_TEST);
+}
+
+void Cube::Resize(int width, int height) {
+ width_ = std::max(width, 1);
+ height_ = std::max(height, 1);
+ // Set the viewport
+ glViewport(0, 0, width_, height_);
+ // Compute the perspective projection matrix with a 60 degree FOV.
+ GLfloat aspect = static_cast<GLfloat>(width_) / static_cast<GLfloat>(height_);
+ transform_4x4::LoadIdentity(perspective_proj_);
+ transform_4x4::Perspective(perspective_proj_, 60.0f, aspect, 1.0f, 20.0f);
+}
+
+void Cube::Draw() {
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ // Compute a new model-view matrix, then use that to make the composite
+ // model-view-projection matrix: MVP = MV . P.
+ GLfloat model_view[16];
+ ComputeModelViewTransform(model_view);
+ transform_4x4::Multiply(mvp_matrix_, model_view, perspective_proj_);
+
+ glBindBuffer(GL_ARRAY_BUFFER, cube_vbos_[0]);
+ glUseProgram(shader_program_object_);
+ glEnableVertexAttribArray(position_location_);
+ glVertexAttribPointer(position_location_,
+ 3,
+ GL_FLOAT,
+ GL_FALSE,
+ 3 * sizeof(GLfloat),
+ NULL);
+ glEnableVertexAttribArray(color_location_);
+ glBindBuffer(GL_ARRAY_BUFFER, cube_vbos_[1]);
+ glVertexAttribPointer(color_location_,
+ 3,
+ GL_FLOAT,
+ GL_FALSE,
+ 3 * sizeof(GLfloat),
+ NULL);
+ glUniformMatrix4fv(mvp_location_, 1, GL_FALSE, mvp_matrix_);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cube_vbos_[2]);
+ glDrawElements(GL_TRIANGLES, kIndexCount, GL_UNSIGNED_SHORT, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+bool Cube::CreateShaders() {
+ const char vertex_shader_src[] =
+ "uniform mat4 u_mvpMatrix; \n"
+ "attribute vec4 a_position; \n"
+ "attribute vec3 a_color; \n"
+ "varying lowp vec4 v_color; \n"
+ "void main() \n"
+ "{ \n"
+ " v_color.xyz = a_color; \n"
+ " v_color.w = 1.0; \n"
+ " gl_Position = u_mvpMatrix * a_position; \n"
+ "} \n";
+
+ const char fragment_shader_src[] =
+ "varying lowp vec4 v_color; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_FragColor = v_color; \n"
+ "} \n";
+
+ // Load the shaders and get a linked program object
+ shader_program_object_ =
+ shader_util::CreateProgramFromVertexAndFragmentShaders(
+ vertex_shader_src, fragment_shader_src);
+ if (shader_program_object_ == 0)
+ return false;
+ position_location_ = glGetAttribLocation(shader_program_object_,
+ "a_position");
+ color_location_ = glGetAttribLocation(shader_program_object_, "a_color");
+ mvp_location_ = glGetUniformLocation(shader_program_object_, "u_mvpMatrix");
+ return true;
+}
+
+void Cube::CreateCube() {
+ static const GLfloat cube_vertices[] = {
+ // Vertex coordinates interleaved with color values
+ // Bottom
+ -0.5f, -0.5f, -0.5f,
+ -0.5f, -0.5f, 0.5f,
+ 0.5f, -0.5f, 0.5f,
+ 0.5f, -0.5f, -0.5f,
+ // Top
+ -0.5f, 0.5f, -0.5f,
+ -0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5f, -0.5f,
+ // Back
+ -0.5f, -0.5f, -0.5f,
+ -0.5f, 0.5f, -0.5f,
+ 0.5f, 0.5f, -0.5f,
+ 0.5f, -0.5f, -0.5f,
+ // Front
+ -0.5f, -0.5f, 0.5f,
+ -0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5f, 0.5f,
+ 0.5f, -0.5f, 0.5f,
+ // Left
+ -0.5f, -0.5f, -0.5f,
+ -0.5f, -0.5f, 0.5f,
+ -0.5f, 0.5f, 0.5f,
+ -0.5f, 0.5f, -0.5f,
+ // Right
+ 0.5f, -0.5f, -0.5f,
+ 0.5f, -0.5f, 0.5f,
+ 0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5f, -0.5f
+ };
+
+ static const GLfloat cube_colors[] = {
+ // Vertex coordinates interleaved with color values
+ // Bottom
+ 1.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+ // Top
+ 0.0, 1.0, 0.0,
+ 0.0, 1.0, 0.0,
+ 0.0, 1.0, 0.0,
+ 0.0, 1.0, 0.0,
+ // Back
+ 0.0, 0.0, 1.0,
+ 0.0, 0.0, 1.0,
+ 0.0, 0.0, 1.0,
+ 0.0, 0.0, 1.0,
+ // Front
+ 1.0, 0.0, 1.0,
+ 1.0, 0.0, 1.0,
+ 1.0, 0.0, 1.0,
+ 1.0, 0.0, 1.0,
+ // Left
+ 1.0, 1.0, 0.0,
+ 1.0, 1.0, 0.0,
+ 1.0, 1.0, 0.0,
+ 1.0, 1.0, 0.0,
+ // Right
+ 0.0, 1.0, 1.0,
+ 0.0, 1.0, 1.0,
+ 0.0, 1.0, 1.0,
+ 0.0, 1.0, 1.0
+ };
+
+ static const GLushort cube_indices[] = {
+ // Bottom
+ 0, 2, 1,
+ 0, 3, 2,
+ // Top
+ 4, 5, 6,
+ 4, 6, 7,
+ // Back
+ 8, 9, 10,
+ 8, 10, 11,
+ // Front
+ 12, 15, 14,
+ 12, 14, 13,
+ // Left
+ 16, 17, 18,
+ 16, 18, 19,
+ // Right
+ 20, 23, 22,
+ 20, 22, 21
+ };
+
+ // Generate the VBOs and upload them to the graphics context.
+ glGenBuffers(3, cube_vbos_);
+ glBindBuffer(GL_ARRAY_BUFFER, cube_vbos_[0]);
+ glBufferData(GL_ARRAY_BUFFER,
+ kVertexCount * sizeof(GLfloat) * 3,
+ cube_vertices,
+ GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, cube_vbos_[1]);
+ glBufferData(GL_ARRAY_BUFFER,
+ kVertexCount * sizeof(GLfloat) * 3,
+ cube_colors,
+ GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cube_vbos_[2]);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER,
+ kIndexCount * sizeof(GL_UNSIGNED_SHORT),
+ cube_indices,
+ GL_STATIC_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+void Cube::ComputeModelViewTransform(GLfloat* model_view) {
+ // This method takes into account the possiblity that |orientation_|
+ // might not be normalized.
+ double sqrx = orientation_[0] * orientation_[0];
+ double sqry = orientation_[1] * orientation_[1];
+ double sqrz = orientation_[2] * orientation_[2];
+ double sqrw = orientation_[3] * orientation_[3];
+ double sqrLength = 1.0 / (sqrx + sqry + sqrz + sqrw);
+
+ transform_4x4::LoadIdentity(model_view);
+ model_view[0] = (sqrx - sqry - sqrz + sqrw) * sqrLength;
+ model_view[5] = (-sqrx + sqry - sqrz + sqrw) * sqrLength;
+ model_view[10] = (-sqrx - sqry + sqrz + sqrw) * sqrLength;
+
+ double temp1 = orientation_[0] * orientation_[1];
+ double temp2 = orientation_[2] * orientation_[3];
+ model_view[1] = 2.0 * (temp1 + temp2) * sqrLength;
+ model_view[4] = 2.0 * (temp1 - temp2) * sqrLength;
+
+ temp1 = orientation_[0] * orientation_[2];
+ temp2 = orientation_[1] * orientation_[3];
+ model_view[2] = 2.0 * (temp1 - temp2) * sqrLength;
+ model_view[8] = 2.0 * (temp1 + temp2) * sqrLength;
+ temp1 = orientation_[1] * orientation_[2];
+ temp2 = orientation_[0] * orientation_[3];
+ model_view[6] = 2.0 * (temp1 + temp2) * sqrLength;
+ model_view[9] = 2.0 * (temp1 - temp2) * sqrLength;
+ model_view[3] = 0.0;
+ model_view[7] = 0.0;
+ model_view[11] = 0.0;
+
+ // Concatenate the translation to the eye point.
+ model_view[12] = -eye_[0];
+ model_view[13] = -eye_[1];
+ model_view[14] = -eye_[2];
+ model_view[15] = 1.0;
+}
+
+} // namespace tumbler
+
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/cube.h b/native_client_sdk/src/examples/fullscreen_tumbler/cube.h
new file mode 100644
index 0000000..42af1cc
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/cube.h
@@ -0,0 +1,98 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_TUMBLER_CUBE_H_
+#define EXAMPLES_TUMBLER_CUBE_H_
+
+#include <GLES2/gl2.h>
+#include <vector>
+#include "examples/fullscreen_tumbler/opengl_context.h"
+#include "examples/fullscreen_tumbler/opengl_context_ptrs.h"
+
+namespace tumbler {
+
+// The Cube class provides a place to implement 3D rendering. It has a
+// frame that it occupies in a browser window.
+class Cube {
+ public:
+ explicit Cube(SharedOpenGLContext opengl_context);
+ ~Cube();
+
+ // Called once when a new RenderContext is first bound to the view. The
+ // bound context is guaranteed to be current and valid before calling this
+ // method.
+ void PrepareOpenGL();
+
+ // Called whenever the size of the browser view changes. This method is
+ // called at least once when the view is first made visible. Clamps the
+ // sizes to 1.
+ void Resize(int width, int height);
+
+ // Called every time the view need to be drawn. The bound context is
+ // guaranteed to be current and valid before this method is called. The
+ // visible portion of the context is flushed to the browser after this
+ // method returns.
+ void Draw();
+
+ // Accessor for width and height. To change these, call Resize.
+ const int width() const {
+ return width_;
+ }
+
+ const int height() const {
+ return height_;
+ }
+
+ // Accessor/mutator for the camera orientation.
+ void GetOrientation(std::vector<float>* orientation) const {
+ if (!orientation)
+ return;
+ (*orientation)[0] = static_cast<float>(orientation_[0]);
+ (*orientation)[1] = static_cast<float>(orientation_[1]);
+ (*orientation)[2] = static_cast<float>(orientation_[2]);
+ (*orientation)[3] = static_cast<float>(orientation_[3]);
+ }
+ void SetOrientation(const std::vector<float>& orientation) {
+ orientation_[0] = static_cast<GLfloat>(orientation[0]);
+ orientation_[1] = static_cast<GLfloat>(orientation[1]);
+ orientation_[2] = static_cast<GLfloat>(orientation[2]);
+ orientation_[3] = static_cast<GLfloat>(orientation[3]);
+ }
+
+ private:
+ // Create the shaders used to draw the cube, and link them into a program.
+ // Initializes |shader_progam_object_|, |position_loction_| and
+ // |mvp_location_|.
+ bool CreateShaders();
+
+ // Generates a cube as a series of GL_TRIANGLE_STRIPs, and initializes
+ // |index_count_| to the number of indices in the index list used as a VBO.
+ // Creates the |vbo_ids_| required for the vertex and index data and uploads
+ // the the VBO data.
+ void CreateCube();
+
+ // Build up the model-view transform from the eye and orienation properties.
+ // Assumes that |model_view| is a 4x4 matrix.
+ void ComputeModelViewTransform(GLfloat* model_view);
+
+ SharedOpenGLContext opengl_context_;
+ int width_;
+ int height_;
+ GLuint shader_program_object_; // The compiled shaders.
+ GLint position_location_; // The position attribute location.
+ GLint color_location_; // The color attribute location.
+ GLint mvp_location_; // The Model-View-Projection composite matrix.
+ GLuint cube_vbos_[3];
+ GLfloat eye_[3]; // The eye point of the virtual camera.
+ // The orientation of the virtual camera stored as a quaternion. The
+ // quaternion is laid out as {{x, y, z}, w}.
+ GLfloat orientation_[4];
+ GLfloat perspective_proj_[16];
+ GLfloat mvp_matrix_[16];
+};
+
+} // namespace tumbler
+
+#endif // EXAMPLES_TUMBLER_CUBE_H_
+
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/dragger.js b/native_client_sdk/src/examples/fullscreen_tumbler/dragger.js
new file mode 100644
index 0000000..232d8b5
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/dragger.js
@@ -0,0 +1,134 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview This class implements a mouse-drag event. It registers for
+ * mousedown events, and when it sees one, starts capturing mousemove events
+ * until it gets a mousup event. It manufactures three drag events: the
+ * DRAG_START, DRAG and DRAG_END.
+ */
+
+// Requires bind
+
+/**
+ * Constructor for the Dragger. Register for mousedown events that happen on
+ * |opt_target|. If |opt_target| is null or undefined, then this object
+ * observes mousedown on the whole document.
+ * @param {?Element} opt_target The event target. Defaults to the whole
+ * document.
+ * @constructor
+ */
+tumbler.Dragger = function(opt_target) {
+ /**
+ * The event target.
+ * @type {Element}
+ * @private
+ */
+ this.target_ = opt_target || document;
+
+ /**
+ * The array of objects that get notified of drag events. Each object in
+ * this array get sent a handleStartDrag(), handleDrag() and handleEndDrag()
+ * message.
+ * @type {Array.<Object>}
+ * @private
+ */
+ this.listeners_ = [];
+
+ /**
+ * Flag to indicate whether the object is in a drag sequence or not.
+ * @type {boolean}
+ * @private
+ */
+ this.isDragging_ = false;
+
+ /**
+ * The function objects that get attached as event handlers. These are
+ * cached so that they can be removed on mouse up.
+ * @type {function}
+ * @private
+ */
+ this.boundMouseMove_ = null;
+ this.boundMouseUp_ = null;
+
+ this.target_.addEventListener('mousedown',
+ this.onMouseDown.bind(this),
+ false);
+}
+
+/**
+ * The ids used for drag event types.
+ * @enum {string}
+ */
+tumbler.Dragger.DragEvents = {
+ DRAG_START: 'dragstart', // Start a drag sequence
+ DRAG: 'drag', // Mouse moved during a drag sequence.
+ DRAG_END: 'dragend' // End a drag sewquence.
+};
+
+/**
+ * Add a drag listener. Each listener should respond to thhree methods:
+ * handleStartDrag(), handleDrag() and handleEndDrag(). This method assumes
+ * that |listener| does not already exist in the array of listeners.
+ * @param {!Object} listener The object that will listen to drag events.
+ */
+tumbler.Dragger.prototype.addDragListener = function(listener) {
+ this.listeners_.push(listener);
+}
+
+/**
+ * Handle a mousedown event: register for mousemove and mouseup, then tell
+ * the target that is has a DRAG_START event.
+ * @param {Event} event The mousedown event that triggered this method.
+ */
+tumbler.Dragger.prototype.onMouseDown = function(event) {
+ this.boundMouseMove_ = this.onMouseMove.bind(this);
+ this.boundMouseUp_ = this.onMouseUp.bind(this);
+ this.target_.addEventListener('mousemove', this.boundMouseMove_);
+ this.target_.addEventListener('mouseup', this.boundMouseUp_);
+ this.isDragging_ = true;
+ var dragStartEvent = { type: tumbler.Dragger.DragEvents.DRAG_START,
+ clientX: event.offsetX,
+ clientY: event.offsetY };
+ var i;
+ for (i = 0; i < this.listeners_.length; ++i) {
+ this.listeners_[i].handleStartDrag(this.target_, dragStartEvent);
+ }
+}
+
+/**
+ * Handle a mousemove event: tell the target that is has a DRAG event.
+ * @param {Event} event The mousemove event that triggered this method.
+ */
+tumbler.Dragger.prototype.onMouseMove = function(event) {
+ if (!this.isDragging_)
+ return;
+ var dragEvent = { type: tumbler.Dragger.DragEvents.DRAG,
+ clientX: event.offsetX,
+ clientY: event.offsetY};
+ var i;
+ for (i = 0; i < this.listeners_.length; ++i) {
+ this.listeners_[i].handleDrag(this.target_, dragEvent);
+ }
+}
+
+/**
+ * Handle a mouseup event: un-register for mousemove and mouseup, then tell
+ * the target that is has a DRAG_END event.
+ * @param {Event} event The mouseup event that triggered this method.
+ */
+tumbler.Dragger.prototype.onMouseUp = function(event) {
+ this.target_.removeEventListener('mouseup', this.boundMouseUp_, false);
+ this.target_.removeEventListener('mousemove', this.boundMouseMove_, false);
+ this.boundMouseUp_ = null;
+ this.boundMouseMove_ = null;
+ this.isDragging_ = false;
+ var dragEndEvent = { type: tumbler.Dragger.DragEvents.DRAG_END,
+ clientX: event.offsetX,
+ clientY: event.offsetY};
+ var i;
+ for (i = 0; i < this.listeners_.length; ++i) {
+ this.listeners_[i].handleEndDrag(this.target_, dragEndEvent);
+ }
+}
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/fullscreen_tumbler.html b/native_client_sdk/src/examples/fullscreen_tumbler/fullscreen_tumbler.html
new file mode 100644
index 0000000..a255b66
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/fullscreen_tumbler.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+ <!--
+ Copyright (c) 2011 The Native Client Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+ <head>
+ <title>Interactive Cube Fullscreen Example</title>
+ <script type="text/javascript">
+ // Provide the tumbler namespace
+ tumbler = {};
+ // Fullscreen support is in Chrome version 16.
+ tumbler.CHROME_MINIMUM_VERSION = 16;
+ </script>
+ <script type="text/javascript" src="../common/check_browser.js"></script>
+ <script type="text/javascript" src="bind.js"></script>
+ <script type="text/javascript" src="dragger.js"></script>
+ <script type="text/javascript" src="tumbler.js"></script>
+ <script type="text/javascript" src="vector3.js"></script>
+ <script type="text/javascript" src="trackball.js"></script>
+ <script type="text/javascript">
+ // Check for Native Client support in the browser before the DOM loads.
+ var isValidBrowser = false;
+ var browserSupportStatus = 0;
+ var checker = new browser_version.BrowserChecker(
+ tumbler.CHROME_MINIMUM_VERSION,
+ navigator["appVersion"],
+ navigator["plugins"]);
+ checker.checkBrowser();
+
+ isValidBrowser = checker.getIsValidBrowser();
+ browserSupportStatus = checker.getBrowserSupportStatus();
+ </script>
+ </head>
+ <body id="bodyId">
+ <h1>Interactive Cube Example</h1>
+ <p>
+ The Native Client module executed in this page draws a 3D cube
+ and allows you to rotate it using a virtual trackball method. To toggle
+ the view to/from fullscreen, press the Enter key.
+ </p>
+ <div id="tumbler_view"></div>
+ <script type="text/javascript">
+ if (browserSupportStatus ==
+ browser_version.BrowserChecker.StatusValues.CHROME_VERSION_TOO_OLD) {
+ alert('This example will only work on Chrome version ' +
+ tumbler.CHROME_MINIMUM_VERSION +
+ ' or later.');
+ } else {
+ tumbler.application = new tumbler.Application();
+ tumbler.application.run('tumbler_view');
+ }
+ </script>
+ </body>
+</HTML>
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/opengl_context.cc b/native_client_sdk/src/examples/fullscreen_tumbler/opengl_context.cc
new file mode 100644
index 0000000..98ee3c3
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/opengl_context.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/fullscreen_tumbler/opengl_context.h"
+
+#include <pthread.h>
+#include "ppapi/cpp/completion_callback.h"
+#include "ppapi/gles2/gl2ext_ppapi.h"
+
+namespace {
+// This is called by the brower when the 3D context has been flushed to the
+// browser window.
+void FlushCallback(void* data, int32_t result) {
+ static_cast<tumbler::OpenGLContext*>(data)->set_flush_pending(false);
+}
+} // namespace
+
+namespace tumbler {
+
+OpenGLContext::OpenGLContext(pp::Instance* instance)
+ : pp::Graphics3DClient(instance),
+ flush_pending_(false) {
+ pp::Module* module = pp::Module::Get();
+ assert(module);
+ gles2_interface_ = static_cast<const struct PPB_OpenGLES2*>(
+ module->GetBrowserInterface(PPB_OPENGLES2_INTERFACE));
+ assert(gles2_interface_);
+}
+
+OpenGLContext::~OpenGLContext() {
+ glSetCurrentContextPPAPI(0);
+}
+
+bool OpenGLContext::MakeContextCurrent(pp::Instance* instance) {
+ if (instance == NULL) {
+ glSetCurrentContextPPAPI(0);
+ return false;
+ }
+ // Lazily create the Pepper context.
+ if (context_.is_null()) {
+ int32_t attribs[] = {
+ PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8,
+ PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 24,
+ PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 8,
+ PP_GRAPHICS3DATTRIB_SAMPLES, 0,
+ PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0,
+ PP_GRAPHICS3DATTRIB_WIDTH, size_.width(),
+ PP_GRAPHICS3DATTRIB_HEIGHT, size_.height(),
+ PP_GRAPHICS3DATTRIB_NONE
+ };
+ context_ = pp::Graphics3D(instance, pp::Graphics3D(), attribs);
+ if (context_.is_null()) {
+ glSetCurrentContextPPAPI(0);
+ return false;
+ }
+ instance->BindGraphics(context_);
+ }
+ glSetCurrentContextPPAPI(context_.pp_resource());
+ return true;
+}
+
+void OpenGLContext::InvalidateContext(pp::Instance* instance) {
+ glSetCurrentContextPPAPI(0);
+}
+
+void OpenGLContext::ResizeContext(const pp::Size& size) {
+ size_ = size;
+ if (!context_.is_null()) {
+ context_.ResizeBuffers(size.width(), size.height());
+ }
+}
+
+
+void OpenGLContext::FlushContext() {
+ if (flush_pending()) {
+ // A flush is pending so do nothing; just drop this flush on the floor.
+ return;
+ }
+ set_flush_pending(true);
+ context_.SwapBuffers(pp::CompletionCallback(&FlushCallback, this));
+}
+} // namespace tumbler
+
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/opengl_context.h b/native_client_sdk/src/examples/fullscreen_tumbler/opengl_context.h
new file mode 100644
index 0000000..6a5369b
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/opengl_context.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_TUMBLER_OPENGL_CONTEXT_H_
+#define EXAMPLES_TUMBLER_OPENGL_CONTEXT_H_
+
+///
+/// @file
+/// OpenGLContext manages the OpenGL context in the browser that is associated
+/// with a @a pp::Instance instance.
+///
+
+#include <assert.h>
+#include <pthread.h>
+
+#include <algorithm>
+#include <string>
+
+#include "examples/fullscreen_tumbler/opengl_context_ptrs.h"
+#include "ppapi/c/ppb_opengles2.h"
+#include "ppapi/cpp/graphics_3d.h"
+#include "ppapi/cpp/graphics_3d_client.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/size.h"
+
+namespace tumbler {
+
+/// OpenGLContext manages an OpenGL rendering context in the browser.
+///
+class OpenGLContext : public pp::Graphics3DClient {
+ public:
+ explicit OpenGLContext(pp::Instance* instance);
+
+ /// Release all the in-browser resources used by this context, and make this
+ /// context invalid.
+ virtual ~OpenGLContext();
+
+ /// The Graphics3DClient interfcace.
+ virtual void Graphics3DContextLost() {
+ assert(!"Unexpectedly lost graphics context");
+ }
+
+ /// Make @a this the current 3D context in @a instance.
+ /// @param instance The instance of the NaCl module that will receive the
+ /// the current 3D context.
+ /// @return success.
+ bool MakeContextCurrent(pp::Instance* instance);
+
+ /// Flush the contents of this context to the browser's 3D device.
+ void FlushContext();
+
+ /// Make the underlying 3D device invalid, so that any subsequent rendering
+ /// commands will have no effect. The next call to MakeContextCurrent() will
+ /// cause the underlying 3D device to get rebound and start receiving
+ /// receiving rendering commands again. Use InvalidateContext(), for
+ /// example, when resizing the context's viewing area.
+ void InvalidateContext(pp::Instance* instance);
+
+ /// Resize the context.
+ void ResizeContext(const pp::Size& size);
+
+ /// The OpenGL ES 2.0 interface.
+ const struct PPB_OpenGLES2* gles2() const {
+ return gles2_interface_;
+ }
+
+ /// The PP_Resource needed to make GLES2 calls through the Pepper interface.
+ const PP_Resource gl_context() const {
+ return context_.pp_resource();
+ }
+
+ /// Indicate whether a flush is pending. This can only be called from the
+ /// main thread; it is not thread safe.
+ bool flush_pending() const {
+ return flush_pending_;
+ }
+ void set_flush_pending(bool flag) {
+ flush_pending_ = flag;
+ }
+
+ private:
+ pp::Size size_;
+ pp::Graphics3D context_;
+ bool flush_pending_;
+
+ const struct PPB_OpenGLES2* gles2_interface_;
+};
+
+} // namespace tumbler
+
+#endif // EXAMPLES_TUMBLER_OPENGL_CONTEXT_H_
+
+
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/opengl_context_ptrs.h b/native_client_sdk/src/examples/fullscreen_tumbler/opengl_context_ptrs.h
new file mode 100644
index 0000000..3478521
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/opengl_context_ptrs.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_TUMBLER_OPENGL_CONTEXT_PTRS_H_
+#define EXAMPLES_TUMBLER_OPENGL_CONTEXT_PTRS_H_
+
+// A convenience wrapper for a shared OpenGLContext pointer type. As other
+// smart pointer types are needed, add them here.
+
+#include <tr1/memory>
+
+namespace tumbler {
+
+class OpenGLContext;
+
+typedef std::tr1::shared_ptr<OpenGLContext> SharedOpenGLContext;
+
+} // namespace tumbler
+
+#endif // EXAMPLES_TUMBLER_OPENGL_CONTEXT_PTRS_H_
+
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/scripting_bridge.cc b/native_client_sdk/src/examples/fullscreen_tumbler/scripting_bridge.cc
new file mode 100644
index 0000000..8a7a54d
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/scripting_bridge.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/fullscreen_tumbler/scripting_bridge.h"
+
+namespace {
+const char* const kWhiteSpaceCharacters = " \t";
+
+// Helper function to pull out the next token in |token_string|. A token is
+// delimited by whitespace. Scanning begins at |*pos|, if pos goes beyond the
+// end of |token_string|, it is set to std::string::npos and an empty string
+// is returned. On return, |*pos| will point to the beginning of the next
+// token. |pos| must not be NULL.
+const std::string ScanToken(const std::string& token_string, size_t* pos) {
+ std::string token;
+ if (*pos == std::string::npos) {
+ return token;
+ }
+ size_t token_start_pos = token_string.find_first_not_of(kWhiteSpaceCharacters,
+ *pos);
+ size_t token_end_pos = token_string.find_first_of(kWhiteSpaceCharacters,
+ token_start_pos);
+ if (token_start_pos != std::string::npos) {
+ token = token_string.substr(token_start_pos, token_end_pos);
+ }
+ *pos = token_end_pos;
+ return token;
+}
+
+// Take a string of the form 'name:value' and split it into two strings, one
+// containing 'name' and the other 'value'. If the ':' separator is missing,
+// or is the last character in |parameter|, |parameter| is copied to
+// |param_name|, |param_value| is left unchanged and false is returned.
+bool ParseParameter(const std::string& parameter,
+ std::string* param_name,
+ std::string* param_value) {
+ bool success = false;
+ size_t sep_pos = parameter.find_first_of(':');
+ if (sep_pos != std::string::npos) {
+ *param_name = parameter.substr(0, sep_pos);
+ if (sep_pos < parameter.length() - 1) {
+ *param_value = parameter.substr(sep_pos + 1);
+ success = true;
+ } else {
+ success = false;
+ }
+ } else {
+ *param_name = parameter;
+ success = false;
+ }
+ return success;
+}
+} // namespace
+
+namespace tumbler {
+
+bool ScriptingBridge::AddMethodNamed(const std::string& method_name,
+ SharedMethodCallbackExecutor method) {
+ if (method_name.size() == 0 || method == NULL)
+ return false;
+ method_dictionary_.insert(
+ std::pair<std::string, SharedMethodCallbackExecutor>(method_name,
+ method));
+ return true;
+}
+
+bool ScriptingBridge::InvokeMethod(const std::string& method) {
+ size_t current_pos = 0;
+ const std::string method_name = ScanToken(method, &current_pos);
+ MethodDictionary::iterator method_iter;
+ method_iter = method_dictionary_.find(method_name);
+ if (method_iter != method_dictionary_.end()) {
+ // Pull out the method parameters and build a dictionary that maps
+ // parameter names to values.
+ std::map<std::string, std::string> param_dict;
+ while (current_pos != std::string::npos) {
+ const std::string parameter = ScanToken(method, &current_pos);
+ if (parameter.length()) {
+ std::string param_name;
+ std::string param_value;
+ if (ParseParameter(parameter, &param_name, &param_value)) {
+ // Note that duplicate parameter names will override each other. The
+ // last one in the method string will be used.
+ param_dict[param_name] = param_value;
+ }
+ }
+ }
+ (*method_iter->second).Execute(*this, param_dict);
+ return true;
+ }
+ return false;
+}
+
+} // namespace tumbler
+
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/scripting_bridge.h b/native_client_sdk/src/examples/fullscreen_tumbler/scripting_bridge.h
new file mode 100644
index 0000000..b1888cf
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/scripting_bridge.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_TUMBLER_SCRIPTING_BRIDGE_H_
+#define EXAMPLES_TUMBLER_SCRIPTING_BRIDGE_H_
+
+#include <map>
+#include <string>
+#include <tr1/memory>
+#include <vector>
+
+#include "examples/fullscreen_tumbler/callback.h"
+#include "ppapi/cpp/var.h"
+
+namespace tumbler {
+
+class MethodCallbackExecutor;
+
+// This class handles the interface between the browser and the NaCl module.
+// There is a single point of entry from the browser: postMessage(). The
+// string passed to postMessage() has this format:
+// 'function_name arg_name0:arg_0 arg_name1:arg1 ...'
+// The arguments have undetermined type; they are placed in a map of argument
+// names and values. Values are all strings, it is up to the target code to
+// do any type coercion.
+// Methods called by the scripting bridge must have a signature like this:
+// void Method(const ScriptingBridge& bridge,
+// const ParameterDictionary&);
+class ScriptingBridge {
+ public:
+ // Shared pointer type used in the method map.
+ typedef std::tr1::shared_ptr<MethodCallbackExecutor>
+ SharedMethodCallbackExecutor;
+
+ virtual ~ScriptingBridge() {}
+
+ // Causes |method_name| to be published as a method that can be called via
+ // postMessage() from the browser. Associates this method with |method|.
+ bool AddMethodNamed(const std::string& method_name,
+ SharedMethodCallbackExecutor method);
+
+ bool InvokeMethod(const std::string& method);
+
+ private:
+ typedef std::map<std::string, SharedMethodCallbackExecutor> MethodDictionary;
+
+ MethodDictionary method_dictionary_;
+};
+
+} // namespace tumbler
+#endif // EXAMPLES_TUMBLER_SCRIPTING_BRIDGE_H_
+
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/shader_util.cc b/native_client_sdk/src/examples/fullscreen_tumbler/shader_util.cc
new file mode 100644
index 0000000..c2c647d
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/shader_util.cc
@@ -0,0 +1,97 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/fullscreen_tumbler/shader_util.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+namespace shader_util {
+
+GLuint CreateShaderOfType(GLenum type, const char *shader_src) {
+ GLuint shader;
+ GLint compiled;
+
+ // Create the shader object
+ shader = glCreateShader(type);
+
+ if (shader == 0)
+ return 0;
+
+ // Load and compile the shader source
+ glShaderSource(shader, 1, &shader_src, NULL);
+ glCompileShader(shader);
+
+ // Check the compile status
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (compiled == 0) {
+ GLint info_len = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_len);
+ if (info_len > 1) {
+ char* info_log =
+ reinterpret_cast<char*>(malloc(sizeof(*info_log) * info_len));
+ glGetShaderInfoLog(shader, info_len, NULL, info_log);
+ // TODO(dspringer): We could really use a logging API.
+ printf("Error compiling shader:\n%s\n", info_log);
+ free(info_log);
+ }
+ glDeleteShader(shader);
+ return 0;
+ }
+
+ return shader;
+}
+
+GLuint CreateProgramFromVertexAndFragmentShaders(
+ const char *vertex_shader_src, const char *fragment_shader_src) {
+ GLuint vertex_shader;
+ GLuint fragment_shader;
+ GLuint program_object;
+ GLint linked;
+
+ // Load the vertex/fragment shaders
+ vertex_shader = CreateShaderOfType(GL_VERTEX_SHADER, vertex_shader_src);
+ if (vertex_shader == 0)
+ return 0;
+ fragment_shader = CreateShaderOfType(GL_FRAGMENT_SHADER, fragment_shader_src);
+ if (fragment_shader == 0) {
+ glDeleteShader(vertex_shader);
+ return 0;
+ }
+
+ // Create the program object and attach the shaders.
+ program_object = glCreateProgram();
+ if (program_object == 0)
+ return 0;
+ glAttachShader(program_object, vertex_shader);
+ glAttachShader(program_object, fragment_shader);
+
+ // Link the program
+ glLinkProgram(program_object);
+
+ // Check the link status
+ glGetProgramiv(program_object, GL_LINK_STATUS, &linked);
+ if (linked == 0) {
+ GLint info_len = 0;
+ glGetProgramiv(program_object, GL_INFO_LOG_LENGTH, &info_len);
+ if (info_len > 1) {
+ char* info_log = reinterpret_cast<char*>(malloc(info_len));
+ glGetProgramInfoLog(program_object, info_len, NULL, info_log);
+ // TODO(dspringer): We could really use a logging API.
+ printf("Error linking program:\n%s\n", info_log);
+ free(info_log);
+ }
+ glDeleteProgram(program_object);
+ return 0;
+ }
+
+ // Delete these here because they are attached to the program object.
+ glDeleteShader(vertex_shader);
+ glDeleteShader(fragment_shader);
+
+ return program_object;
+}
+
+} // namespace shader_util
+
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/shader_util.h b/native_client_sdk/src/examples/fullscreen_tumbler/shader_util.h
new file mode 100644
index 0000000..fdf9cbd
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/shader_util.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Some simple helper functions that load shaders and create program objects.
+
+#ifndef EXAMPLES_TUMBLER_SHADER_UTIL_H_
+#define EXAMPLES_TUMBLER_SHADER_UTIL_H_
+
+#include <GLES2/gl2.h>
+
+namespace shader_util {
+
+// Load and compile a shader. |type| can be one of GL_VERTEX_SHADER or
+// GL_FRAGMENT_SHADER. Returns a non-0 value representing the compiled
+// shader on success, 0 on failure. The caller is responsible for deleting
+// the returned shader using glDeleteShader().
+GLuint CreateShaderOfType(GLenum type, const char *shader_src);
+
+// Load and compile the vertex and fragment shaders, then link these together
+// into a complete program. Returns a non-0 value representing the program on,
+// success or 0 on failure. The caller is responsible for deleting the
+// returned program using glDeleteProgram().
+GLuint CreateProgramFromVertexAndFragmentShaders(
+ const char *vertex_shader_src, const char *fragment_shader_src);
+
+} // namespace shader_util
+
+#endif // EXAMPLES_TUMBLER_SHADER_UTIL_H_
+
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/trackball.js b/native_client_sdk/src/examples/fullscreen_tumbler/trackball.js
new file mode 100644
index 0000000..88b9a62
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/trackball.js
@@ -0,0 +1,296 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Implement a virtual trackball in the tumbler.Trackball
+ * class. This class maps 2D mouse events to 3D rotations by simulating a
+ * trackball that you roll by dragging the mouse. There are two principle
+ * methods in the class: startAtPointInFrame which you use to begin a trackball
+ * simulation and rollToPoint, which you use while dragging the mouse. The
+ * rollToPoint method returns a rotation expressed as a quaternion.
+ */
+
+
+// Requires tumbler.Application
+// Requires tumbler.DragEvent
+// Requires tumbler.Vector3
+
+/**
+ * Constructor for the Trackball object. This class maps 2D mouse drag events
+ * into 3D rotations by simulating a trackball. The idea is to simulate
+ * clicking on the trackball, and then rolling it as you drag the mouse.
+ * The math behind the trackball is simple: start with a vector from the first
+ * mouse-click on the ball to the center of the 3D view. At the same time, set
+ * the radius of the ball to be the smaller dimension of the 3D view. As you
+ * drag the mouse around in the 3D view, a second vector is computed from the
+ * surface of the ball to the center. The axis of rotation is the cross
+ * product of these two vectors, and the angle of rotation is the angle between
+ * the two vectors.
+ * @constructor
+ */
+tumbler.Trackball = function() {
+ /**
+ * The square of the trackball's radius. The math never looks at the radius,
+ * but looks at the radius squared.
+ * @type {number}
+ * @private
+ */
+ this.sqrRadius_ = 0;
+
+ /**
+ * The 3D vector representing the point on the trackball where the mouse
+ * was clicked. Default is pointing stright through the center of the ball.
+ * @type {Object}
+ * @private
+ */
+ this.rollStart_ = new tumbler.Vector3(0, 0, 1);
+
+ /**
+ * The 2D center of the frame that encloses the trackball.
+ * @type {!Object}
+ * @private
+ */
+ this.center_ = { x: 0, y: 0 };
+
+ /**
+ * Cached camera orientation. When a drag START event happens this is set to
+ * the current orientation in the calling view's plugin. The default is the
+ * identity quaternion.
+ * @type {Array.<number>}
+ * @private
+ */
+ this.cameraOrientation_ = [0, 0, 0, 1];
+};
+
+/**
+ * Compute the dimensions of the virtual trackball to fit inside |frameSize|.
+ * The radius of the trackball is set to be 1/2 of the smaller of the two frame
+ * dimensions, the center point is at the midpoint of each side.
+ * @param {!goog.math.Size} frameSize 2D-point representing the size of the
+ * element that encloses the virtual trackball.
+ * @private
+ */
+tumbler.Trackball.prototype.initInFrame_ = function(frameSize) {
+ // Compute the radius of the virtual trackball. This is 1/2 of the smaller
+ // of the frame's width and height.
+ var halfFrameSize = 0.5 * Math.min(frameSize.width, frameSize.height);
+ // Cache the square of the trackball's radius.
+ this.sqrRadius_ = halfFrameSize * halfFrameSize;
+ // Figure the center of the view.
+ this.center_.x = frameSize.width * 0.5;
+ this.center_.y = frameSize.height * 0.5;
+};
+
+/**
+ * Method to convert (by translation) a 2D client point from a coordinate space
+ * with origin in the lower-left corner of the client view to a space with
+ * origin in the center of the client view. Use this method before mapping the
+ * 2D point to he 3D tackball point (see also the projectOnTrackball_() method).
+ * Call the startAtPointInFrame before calling this method so that the
+ * |center_| property is correctly initialized.
+ * @param {!Object} clientPoint map this point to the coordinate space with
+ * origin in thecenter of the client view.
+ * @return {Object} the converted point.
+ * @private
+ */
+tumbler.Trackball.prototype.convertClientPoint_ = function(clientPoint) {
+ var difference = { x: clientPoint.x - this.center_.x,
+ y: clientPoint.y - this.center_.y }
+ return difference;
+};
+
+/**
+ * Method to map a 2D point to a 3D point on the virtual trackball that was set
+ * up using the startAtPointInFrame method. If the point lies outside of the
+ * radius of the virtual trackball, then the z-coordinate of the 3D point
+ * is set to 0.
+ * @param {!Object.<x, y>} point 2D-point in the coordinate space with origin
+ * in the center of the client view.
+ * @return {tumbler.Vector3} the 3D point on the virtual trackball.
+ * @private
+ */
+tumbler.Trackball.prototype.projectOnTrackball_ = function(point) {
+ var sqrRadius2D = point.x * point.x + point.y * point.y;
+ var zValue;
+ if (sqrRadius2D > this.sqrRadius_) {
+ // |point| lies outside the virtual trackball's sphere, so use a virtual
+ // z-value of 0. This is equivalent to clicking on the horizontal equator
+ // of the trackball.
+ zValue = 0;
+ } else {
+ // A sphere can be defined as: r^2 = x^2 + y^2 + z^2, so z =
+ // sqrt(r^2 - (x^2 + y^2)).
+ zValue = Math.sqrt(this.sqrRadius_ - sqrRadius2D);
+ }
+ var trackballPoint = new tumbler.Vector3(point.x, point.y, zValue);
+ return trackballPoint;
+};
+
+/**
+ * Method to start up the trackball. The trackball works by pretending that a
+ * ball encloses the 3D view. You roll this pretend ball with the mouse. For
+ * example, if you click on the center of the ball and move the mouse straight
+ * to the right, you roll the ball around its Y-axis. This produces a Y-axis
+ * rotation. You can click on the "edge" of the ball and roll it around
+ * in a circle to get a Z-axis rotation.
+ * @param {!Object.<x, y>} startPoint 2D-point, usually the mouse-down
+ * point.
+ * @param {!Object.<width, height>} frameSize 2D-point representing the size of
+ * the element that encloses the virtual trackball.
+ */
+tumbler.Trackball.prototype.startAtPointInFrame =
+ function(startPoint, frameSize) {
+ this.initInFrame_(frameSize);
+ // Compute the starting vector from the surface of the ball to its center.
+ this.rollStart_ = this.projectOnTrackball_(
+ this.convertClientPoint_(startPoint));
+};
+
+/**
+ * Method to roll the virtual trackball; call this in response to a mouseDrag
+ * event. Takes |dragPoint| and projects it from 2D mouse coordinates onto the
+ * virtual track ball that was set up in startAtPointInFrame method.
+ * Returns a quaternion that represents the rotation from |rollStart_| to
+ * |rollEnd_|.
+ * @param {!Object.<x, y>} dragPoint 2D-point representing the
+ * destination mouse point.
+ * @return {Array.<number>} a quaternion that represents the rotation from
+ * the point wnere the mouse was clicked on the trackball to this point.
+ * The quaternion looks like this: [[v], cos(angle/2)], where [v] is the
+ * imaginary part of the quaternion and is computed as [x, y, z] *
+ * sin(angle/2).
+ */
+tumbler.Trackball.prototype.rollToPoint = function(dragPoint) {
+ var rollTo = this.convertClientPoint_(dragPoint);
+ if ((Math.abs(this.rollStart_.x - rollTo.x) <
+ tumbler.Trackball.DOUBLE_EPSILON) &&
+ (Math.abs(this.rollStart_.y, rollTo.y) <
+ tumbler.Trackball.DOUBLE_EPSILON)) {
+ // Not enough change in the vectors to roll the ball, return the identity
+ // quaternion.
+ return [0, 0, 0, 1];
+ }
+
+ // Compute the ending vector from the surface of the ball to its center.
+ var rollEnd = this.projectOnTrackball_(rollTo);
+
+ // Take the cross product of the two vectors. r = s X e
+ var rollVector = this.rollStart_.cross(rollEnd);
+ var invStartMag = 1.0 / this.rollStart_.magnitude();
+ var invEndMag = 1.0 / rollEnd.magnitude();
+
+ // cos(a) = (s . e) / (||s|| ||e||)
+ var cosAng = this.rollStart_.dot(rollEnd) * invStartMag * invEndMag;
+ // sin(a) = ||(s X e)|| / (||s|| ||e||)
+ var sinAng = rollVector.magnitude() * invStartMag * invEndMag;
+ // Build a quaternion that represents the rotation about |rollVector|.
+ // Use atan2 for a better angle. If you use only cos or sin, you only get
+ // half the possible angles, and you can end up with rotations that flip
+ // around near the poles.
+ var rollHalfAngle = Math.atan2(sinAng, cosAng) * 0.5;
+ rollVector.normalize();
+ // The quaternion looks like this: [[v], cos(angle/2)], where [v] is the
+ // imaginary part of the quaternion and is computed as [x, y, z] *
+ // sin(angle/2).
+ rollVector.scale(Math.sin(rollHalfAngle));
+ var ballQuaternion = [rollVector.x,
+ rollVector.y,
+ rollVector.z,
+ Math.cos(rollHalfAngle)];
+ return ballQuaternion;
+};
+
+/**
+ * Handle the drag START event: grab the current camera orientation from the
+ * sending view and set up the virtual trackball.
+ * @param {!tumbler.Application} view The view controller that called this
+ * method.
+ * @param {!tumbler.DragEvent} dragStartEvent The DRAG_START event that
+ * triggered this handler.
+ */
+tumbler.Trackball.prototype.handleStartDrag =
+ function(controller, dragStartEvent) {
+ // Cache the camera orientation. The orientations from the trackball as it
+ // rolls are concatenated to this orientation and pushed back into the
+ // plugin on the other side of the JavaScript bridge.
+ controller.setCameraOrientation(this.cameraOrientation_);
+ // Invert the y-coordinate for the trackball computations.
+ var frameSize = { width: controller.offsetWidth,
+ height: controller.offsetHeight };
+ var flippedY = { x: dragStartEvent.clientX,
+ y: frameSize.height - dragStartEvent.clientY };
+ this.startAtPointInFrame(flippedY, frameSize);
+};
+
+/**
+ * Handle the drag DRAG event: concatenate the current orientation to the
+ * cached orientation. Send this final value through to the GSPlugin via the
+ * setValueForKey() method.
+ * @param {!tumbler.Application} view The view controller that called this
+ * method.
+ * @param {!tumbler.DragEvent} dragEvent The DRAG event that triggered this
+ * handler.
+ */
+tumbler.Trackball.prototype.handleDrag =
+ function(controller, dragEvent) {
+ // Flip the y-coordinate so that the 2D origin is in the lower-left corner.
+ var frameSize = { width: controller.offsetWidth,
+ height: controller.offsetHeight };
+ var flippedY = { x: dragEvent.clientX,
+ y: frameSize.height - dragEvent.clientY };
+ controller.setCameraOrientation(
+ tumbler.multQuaternions(this.rollToPoint(flippedY),
+ this.cameraOrientation_));
+};
+
+/**
+ * Handle the drag END event: get the final orientation and concatenate it to
+ * the cached orientation.
+ * @param {!tumbler.Application} view The view controller that called this
+ * method.
+ * @param {!tumbler.DragEvent} dragEndEvent The DRAG_END event that triggered
+ * this handler.
+ */
+tumbler.Trackball.prototype.handleEndDrag =
+ function(controller, dragEndEvent) {
+ // Flip the y-coordinate so that the 2D origin is in the lower-left corner.
+ var frameSize = { width: controller.offsetWidth,
+ height: controller.offsetHeight };
+ var flippedY = { x: dragEndEvent.clientX,
+ y: frameSize.height - dragEndEvent.clientY };
+ this.cameraOrientation_ = tumbler.multQuaternions(this.rollToPoint(flippedY),
+ this.cameraOrientation_);
+ controller.setCameraOrientation(this.cameraOrientation_);
+};
+
+/**
+ * A utility function to multiply two quaterions. Returns the product q0 * q1.
+ * This is effectively the same thing as concatenating the two rotations
+ * represented in each quaternion together. Note that quaternion multiplication
+ * is NOT commutative: q0 * q1 != q1 * q0.
+ * @param {!Array.<number>} q0 A 4-element array representing the first
+ * quaternion.
+ * @param {!Array.<number>} q1 A 4-element array representing the second
+ * quaternion.
+ * @return {Array.<number>} A 4-element array representing the product q0 * q1.
+ */
+tumbler.multQuaternions = function(q0, q1) {
+ // Return q0 * q1 (note the order).
+ var qMult = [
+ q0[3] * q1[0] + q0[0] * q1[3] + q0[1] * q1[2] - q0[2] * q1[1],
+ q0[3] * q1[1] - q0[0] * q1[2] + q0[1] * q1[3] + q0[2] * q1[0],
+ q0[3] * q1[2] + q0[0] * q1[1] - q0[1] * q1[0] + q0[2] * q1[3],
+ q0[3] * q1[3] - q0[0] * q1[0] - q0[1] * q1[1] - q0[2] * q1[2]
+ ];
+ return qMult;
+};
+
+/**
+ * Real numbers that are less than this distance apart are considered
+ * equivalent.
+ * TODO(dspringer): It seems as though there should be a const like this
+ * in Closure somewhere (goog.math?).
+ * @type {number}
+ */
+tumbler.Trackball.DOUBLE_EPSILON = 1.0e-16;
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/transforms.cc b/native_client_sdk/src/examples/fullscreen_tumbler/transforms.cc
new file mode 100644
index 0000000..609f8fd
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/transforms.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/fullscreen_tumbler/transforms.h"
+
+#include <GLES2/gl2.h>
+#include <math.h>
+#include <string.h>
+
+namespace transform_4x4 {
+
+static const GLfloat kPI = 3.1415926535897932384626433832795f;
+
+void Translate(GLfloat* m, GLfloat tx, GLfloat ty, GLfloat tz) {
+ m[12] += (m[0] * tx + m[4] * ty + m[8] * tz);
+ m[13] += (m[1] * tx + m[5] * ty + m[9] * tz);
+ m[14] += (m[2] * tx + m[6] * ty + m[10] * tz);
+ m[15] += (m[3] * tx + m[7] * ty + m[11] * tz);
+}
+
+void Frustum(GLfloat* m,
+ GLfloat left,
+ GLfloat right,
+ GLfloat bottom,
+ GLfloat top,
+ GLfloat near_z,
+ GLfloat far_z) {
+ GLfloat delta_x = right - left;
+ GLfloat delta_y = top - bottom;
+ GLfloat delta_z = far_z - near_z;
+ GLfloat frustum[16];
+
+ if ((near_z <= 0.0f) || (far_z <= 0.0f) ||
+ (delta_x <= 0.0f) || (delta_y <= 0.0f) || (delta_z <= 0.0f))
+ return;
+
+ frustum[0] = 2.0f * near_z / delta_x;
+ frustum[1] = frustum[2] = frustum[3] = 0.0f;
+
+ frustum[5] = 2.0f * near_z / delta_y;
+ frustum[4] = frustum[6] = frustum[7] = 0.0f;
+
+ frustum[8] = (right + left) / delta_x;
+ frustum[9] = (top + bottom) / delta_y;
+ frustum[10] = -(near_z + far_z) / delta_z;
+ frustum[11] = -1.0f;
+
+ frustum[14] = -2.0f * near_z * far_z / delta_z;
+ frustum[12] = frustum[13] = frustum[15] = 0.0f;
+
+ transform_4x4::Multiply(m, frustum, m);
+}
+
+
+void Perspective(GLfloat* m,
+ GLfloat fovy,
+ GLfloat aspect,
+ GLfloat near_z,
+ GLfloat far_z) {
+ GLfloat frustum_w, frustum_h;
+
+ frustum_h = tanf((fovy * 0.5f) / 180.0f * kPI) * near_z;
+ frustum_w = frustum_h * aspect;
+ transform_4x4::Frustum(m, -frustum_w, frustum_w, -frustum_h, frustum_h,
+ near_z, far_z);
+}
+
+void Multiply(GLfloat *m, GLfloat *a, GLfloat* b) {
+ GLfloat tmp[16];
+ // tmp = a . b
+ GLfloat a0, a1, a2, a3;
+ a0 = a[0];
+ a1 = a[1];
+ a2 = a[2];
+ a3 = a[3];
+ tmp[0] = a0 * b[0] + a1 * b[4] + a2 * b[8] + a3 * b[12];
+ tmp[1] = a0 * b[1] + a1 * b[5] + a2 * b[9] + a3 * b[13];
+ tmp[2] = a0 * b[2] + a1 * b[6] + a2 * b[10] + a3 * b[14];
+ tmp[3] = a0 * b[3] + a1 * b[7] + a2 * b[11] + a3 * b[15];
+
+ a0 = a[4];
+ a1 = a[5];
+ a2 = a[6];
+ a3 = a[7];
+ tmp[4] = a0 * b[0] + a1 * b[4] + a2 * b[8] + a3 * b[12];
+ tmp[5] = a0 * b[1] + a1 * b[5] + a2 * b[9] + a3 * b[13];
+ tmp[6] = a0 * b[2] + a1 * b[6] + a2 * b[10] + a3 * b[14];
+ tmp[7] = a0 * b[3] + a1 * b[7] + a2 * b[11] + a3 * b[15];
+
+ a0 = a[8];
+ a1 = a[9];
+ a2 = a[10];
+ a3 = a[11];
+ tmp[8] = a0 * b[0] + a1 * b[4] + a2 * b[8] + a3 * b[12];
+ tmp[9] = a0 * b[1] + a1 * b[5] + a2 * b[9] + a3 * b[13];
+ tmp[10] = a0 * b[2] + a1 * b[6] + a2 * b[10] + a3 * b[14];
+ tmp[11] = a0 * b[3] + a1 * b[7] + a2 * b[11] + a3 * b[15];
+
+ a0 = a[12];
+ a1 = a[13];
+ a2 = a[14];
+ a3 = a[15];
+ tmp[12] = a0 * b[0] + a1 * b[4] + a2 * b[8] + a3 * b[12];
+ tmp[13] = a0 * b[1] + a1 * b[5] + a2 * b[9] + a3 * b[13];
+ tmp[14] = a0 * b[2] + a1 * b[6] + a2 * b[10] + a3 * b[14];
+ tmp[15] = a0 * b[3] + a1 * b[7] + a2 * b[11] + a3 * b[15];
+ memcpy(m, tmp, sizeof(GLfloat) * 4 * 4);
+}
+
+void LoadIdentity(GLfloat* m) {
+ memset(m, 0, sizeof(GLfloat) * 4 * 4);
+ m[0] = m[5] = m[10] = m[15] = 1.0f;
+}
+
+} // namespace transform_4x4
+
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/transforms.h b/native_client_sdk/src/examples/fullscreen_tumbler/transforms.h
new file mode 100644
index 0000000..5ac3d6e
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/transforms.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_TUMBLER_TRANSFORMS_H_
+#define EXAMPLES_TUMBLER_TRANSFORMS_H_
+
+#include <GLES2/gl2.h>
+
+// A very simple set of 4x4 matrix routines. In all these routines, the input
+// matrix is assumed to be a 4x4 of GLfloats.
+
+namespace transform_4x4 {
+
+// Pre-multply |m| with a projection transformation 4x4 matrix from a
+// truncated pyramid viewing frustum.
+void Frustum(GLfloat* m,
+ GLfloat left,
+ GLfloat right,
+ GLfloat bottom,
+ GLfloat top,
+ GLfloat near_z,
+ GLfloat far_z);
+
+// Replace |m| with the 4x4 identity matrix.
+void LoadIdentity(GLfloat* m);
+
+// |m| <- |a| . |b|. |m| can point at the same memory as either |a| or |b|.
+void Multiply(GLfloat *m, GLfloat *a, GLfloat* b);
+
+// Pre-multiply |m| with a single-point perspective matrix based on the viewing
+// frustum whose view angle is |fovy|.
+void Perspective(GLfloat* m,
+ GLfloat fovy,
+ GLfloat aspect,
+ GLfloat near_z,
+ GLfloat far_z);
+
+// Pre-multiply |m| with a matrix that represents a translation by |tx|, |ty|,
+// |tz|.
+void Translate(GLfloat* m, GLfloat tx, GLfloat ty, GLfloat tz);
+} // namespace transform_4x4
+
+#endif // EXAMPLES_TUMBLER_TRANSFORMS_H_
+
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/tumbler.cc b/native_client_sdk/src/examples/fullscreen_tumbler/tumbler.cc
new file mode 100644
index 0000000..3c38639
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/tumbler.cc
@@ -0,0 +1,202 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/fullscreen_tumbler/tumbler.h"
+
+#include <stdio.h>
+
+#include <cstdlib>
+#include <cstring>
+#include <string>
+#include <vector>
+
+#include "examples/fullscreen_tumbler/cube.h"
+#include "examples/fullscreen_tumbler/opengl_context.h"
+#include "examples/fullscreen_tumbler/scripting_bridge.h"
+#include "ppapi/cpp/input_event.h"
+#include "ppapi/cpp/rect.h"
+#include "ppapi/cpp/size.h"
+#include "ppapi/cpp/var.h"
+
+namespace {
+const uint32_t kKeyEnter = 0x0D;
+const size_t kQuaternionElementCount = 4;
+const char* const kArrayStartCharacter = "[";
+const char* const kArrayEndCharacter = "]";
+const char* const kArrayDelimiter = ",";
+
+// Return the value of parameter named |param_name| from |parameters|. If
+// |param_name| doesn't exist, then return an empty string.
+std::string GetParameterNamed(
+ const std::string& param_name,
+ const tumbler::MethodParameter& parameters) {
+ tumbler::MethodParameter::const_iterator i =
+ parameters.find(param_name);
+ if (i == parameters.end()) {
+ return "";
+ }
+ return i->second;
+}
+
+// Convert the JSON string |array| into a vector of floats. |array| is
+// expected to be a string bounded by '[' and ']' and containing a
+// comma-delimited list of numbers. Any errors result in the return of an
+// empty array.
+std::vector<float> CreateArrayFromJSON(const std::string& json_array) {
+ std::vector<float> float_array;
+ size_t array_start_pos = json_array.find_first_of(kArrayStartCharacter);
+ size_t array_end_pos = json_array.find_last_of(kArrayEndCharacter);
+ if (array_start_pos == std::string::npos ||
+ array_end_pos == std::string::npos)
+ return float_array; // Malformed JSON: missing '[' or ']'.
+ // Pull out the array elements.
+ size_t token_pos = array_start_pos + 1;
+ while (token_pos < array_end_pos) {
+ float_array.push_back(strtof(json_array.data() + token_pos, NULL));
+ size_t delim_pos = json_array.find_first_of(kArrayDelimiter, token_pos);
+ if (delim_pos == std::string::npos)
+ break;
+ token_pos = delim_pos + 1;
+ }
+ return float_array;
+}
+} // namespace
+
+namespace tumbler {
+
+Tumbler::Tumbler(PP_Instance instance)
+ : pp::Instance(instance),
+ full_screen_(this),
+ has_focus_(false),
+ cube_(NULL) {
+ RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
+ RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD);
+}
+
+Tumbler::~Tumbler() {
+ // Destroy the cube view while GL context is current.
+ opengl_context_->MakeContextCurrent(this);
+ delete cube_;
+}
+
+bool Tumbler::Init(uint32_t /* argc */,
+ const char* /* argn */[],
+ const char* /* argv */[]) {
+ // Add all the methods to the scripting bridge.
+ ScriptingBridge::SharedMethodCallbackExecutor set_orientation_method(
+ new tumbler::MethodCallback<Tumbler>(
+ this, &Tumbler::SetCameraOrientation));
+ scripting_bridge_.AddMethodNamed("setCameraOrientation",
+ set_orientation_method);
+ return true;
+}
+
+void Tumbler::HandleMessage(const pp::Var& message) {
+ if (!message.is_string())
+ return;
+ scripting_bridge_.InvokeMethod(message.AsString());
+}
+
+bool Tumbler::HandleInputEvent(const pp::InputEvent& event) {
+ switch (event.GetType()) {
+ case PP_INPUTEVENT_TYPE_UNDEFINED:
+ break;
+ case PP_INPUTEVENT_TYPE_MOUSEDOWN:
+ // If we do not yet have focus, return true. In return Chrome will give
+ // focus to the NaCl embed.
+ return !has_focus_;
+ break;
+ case PP_INPUTEVENT_TYPE_KEYDOWN:
+ HandleKeyDownEvent(pp::KeyboardInputEvent(event));
+ break;
+ case PP_INPUTEVENT_TYPE_MOUSEUP:
+ case PP_INPUTEVENT_TYPE_MOUSEMOVE:
+ case PP_INPUTEVENT_TYPE_MOUSEENTER:
+ case PP_INPUTEVENT_TYPE_MOUSELEAVE:
+ case PP_INPUTEVENT_TYPE_WHEEL:
+ case PP_INPUTEVENT_TYPE_RAWKEYDOWN:
+ case PP_INPUTEVENT_TYPE_KEYUP:
+ case PP_INPUTEVENT_TYPE_CHAR:
+ case PP_INPUTEVENT_TYPE_CONTEXTMENU:
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START:
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE:
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END:
+ case PP_INPUTEVENT_TYPE_IME_TEXT:
+ default:
+ return false;
+ }
+ return false;
+}
+
+void Tumbler::DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
+ // Note: When switching to fullscreen, the new View position will be a
+ // rectangle that encompasses the entire screen - e.g. 1900x1200 - with its
+ // top-left corner at (0, 0). When switching back to the windowed screen the
+ // position returns to what it was before going to fullscreen.
+ int cube_width = cube_ ? cube_->width() : 0;
+ int cube_height = cube_ ? cube_->height() : 0;
+ if (position.size().width() == cube_width &&
+ position.size().height() == cube_height) {
+ return; // Size didn't change, no need to update anything.
+ }
+
+ if (opengl_context_ == NULL)
+ opengl_context_.reset(new OpenGLContext(this));
+ opengl_context_->InvalidateContext(this);
+ opengl_context_->ResizeContext(position.size());
+ if (!opengl_context_->MakeContextCurrent(this))
+ return;
+ if (cube_ == NULL) {
+ cube_ = new Cube(opengl_context_);
+ cube_->PrepareOpenGL();
+ }
+ cube_->Resize(position.size().width(), position.size().height());
+ DrawSelf();
+}
+
+void Tumbler::DidChangeFocus(bool focus) {
+ has_focus_ = focus;
+}
+
+void Tumbler::DrawSelf() {
+ if (cube_ == NULL || opengl_context_ == NULL)
+ return;
+ opengl_context_->MakeContextCurrent(this);
+ cube_->Draw();
+ opengl_context_->FlushContext();
+}
+
+void Tumbler::HandleKeyDownEvent(const pp::KeyboardInputEvent& key_event) {
+ // Pressing the Enter key toggles the view to/from full screen.
+ if (key_event.GetKeyCode() == kKeyEnter) {
+ if (!full_screen_.IsFullscreen()) {
+ if (!full_screen_.SetFullscreen(true)) {
+ printf("Failed to switch to fullscreen mode.\n");
+ }
+ } else {
+ if (!full_screen_.SetFullscreen(false)) {
+ printf("Failed to switch to normal mode.\n");
+ }
+ }
+ }
+}
+
+void Tumbler::SetCameraOrientation(
+ const tumbler::ScriptingBridge& bridge,
+ const tumbler::MethodParameter& parameters) {
+ // |parameters| is expected to contain one object named "orientation", whose
+ // value is a JSON string that represents an array of four floats.
+ if (parameters.size() != 1 || cube_ == NULL)
+ return;
+ std::string orientation_desc = GetParameterNamed("orientation", parameters);
+ std::vector<float> orientation = CreateArrayFromJSON(orientation_desc);
+ if (orientation.size() != kQuaternionElementCount) {
+ return;
+ }
+ cube_->SetOrientation(orientation);
+ DrawSelf();
+}
+
+} // namespace tumbler
+
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/tumbler.h b/native_client_sdk/src/examples/fullscreen_tumbler/tumbler.h
new file mode 100644
index 0000000..d752ea3
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/tumbler.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_TUMBLER_TUMBLER_H_
+#define EXAMPLES_TUMBLER_TUMBLER_H_
+
+#include <pthread.h>
+#include <map>
+#include <vector>
+
+#include "examples/fullscreen_tumbler/cube.h"
+#include "examples/fullscreen_tumbler/opengl_context.h"
+#include "examples/fullscreen_tumbler/opengl_context_ptrs.h"
+#include "examples/fullscreen_tumbler/scripting_bridge.h"
+#include "ppapi/cpp/fullscreen.h"
+#include "ppapi/cpp/instance.h"
+
+namespace pp {
+class KeyboardInputEvent;
+} // namespace pp
+
+
+namespace tumbler {
+
+class Tumbler : public pp::Instance {
+ public:
+ explicit Tumbler(PP_Instance instance);
+
+ // The dtor makes the 3D context current before deleting the cube view, then
+ // destroys the 3D context both in the module and in the browser.
+ virtual ~Tumbler();
+
+ // Called by the browser when the NaCl module is loaded and all ready to go.
+ virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]);
+
+ // Called whenever the in-browser window changes size.
+ virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip);
+
+ // Called by the browser when the NaCl canvas gets or loses focus.
+ virtual void DidChangeFocus(bool has_focus);
+
+ // Called by the browser to handle the postMessage() call in Javascript.
+ virtual void HandleMessage(const pp::Var& message);
+
+ // Called by the browser to handle incoming input events.
+ virtual bool HandleInputEvent(const pp::InputEvent& event);
+
+ // Bind and publish the module's methods to JavaScript.
+ void InitializeMethods(ScriptingBridge* bridge);
+
+ // Set the camera orientation to the quaternion in |args[0]|. |args| must
+ // have length at least 1; the first element is expeted to be an Array
+ // object containing 4 floating point number elements (the quaternion).
+ // This method is bound to the JavaScript "setCameraOrientation" method and
+ // is called like this:
+ // module.setCameraOrientation([0.0, 1.0, 0.0, 0.0]);
+ void SetCameraOrientation(
+ const tumbler::ScriptingBridge& bridge,
+ const tumbler::MethodParameter& parameters);
+
+ // Called to draw the contents of the module's browser area.
+ void DrawSelf();
+
+ private:
+ // Process key-down input events.
+ void HandleKeyDownEvent(const pp::KeyboardInputEvent& key_event);
+
+ pp::Fullscreen full_screen_;
+ bool has_focus_;
+
+ // Browser connectivity and scripting support.
+ ScriptingBridge scripting_bridge_;
+
+ SharedOpenGLContext opengl_context_;
+ // Wouldn't it be awesome if we had boost::scoped_ptr<>?
+ Cube* cube_;
+};
+
+} // namespace tumbler
+
+#endif // EXAMPLES_TUMBLER_TUMBLER_H_
+
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/tumbler.js b/native_client_sdk/src/examples/fullscreen_tumbler/tumbler.js
new file mode 100644
index 0000000..58096e4
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/tumbler.js
@@ -0,0 +1,133 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview The tumbler Application object. This object instantiates a
+ * Trackball object and connects it to the element named |tumbler_content|.
+ * It also conditionally embeds a debuggable module or a release module into
+ * the |tumbler_content| element.
+ */
+
+// Requires tumbler
+// Requires tumbler.Dragger
+// Requires tumbler.Trackball
+
+/**
+ * Constructor for the Application class. Use the run() method to populate
+ * the object with controllers and wire up the events.
+ * @constructor
+ */
+tumbler.Application = function() {
+ /**
+ * The native module for the application. This refers to the module loaded
+ * using the <embed> tag.
+ * @type {Element}
+ * @private
+ */
+ this.module_ = null;
+
+ /**
+ * The trackball object.
+ * @type {tumbler.Trackball}
+ * @private
+ */
+ this.trackball_ = null;
+
+ /**
+ * The mouse-drag event object.
+ * @type {tumbler.Dragger}
+ * @private
+ */
+ this.dragger_ = null;
+
+ /**
+ * The function objects that get attached as event handlers. These are
+ * cached so that they can be removed when they are no longer needed.
+ * @type {function}
+ * @private
+ */
+ this.boundModuleDidLoad_ = null;
+}
+
+/**
+ * The ids used for elements in the DOM. The Tumbler Application expects these
+ * elements to exist.
+ * @enum {string}
+ * @private
+ */
+tumbler.Application.DomIds_ = {
+ MODULE: 'tumbler', // The <embed> element representing the NaCl module
+ VIEW: 'tumbler_view' // The <div> containing the NaCl element.
+}
+
+/**
+ * Called by the module loading function once the module has been loaded.
+ * @param {?Element} nativeModule The instance of the native module.
+ */
+tumbler.Application.prototype.moduleDidLoad = function() {
+ this.module_ = document.getElementById(tumbler.Application.DomIds_.MODULE);
+ // Unbind the load function.
+ this.boundModuleDidLoad_ = null;
+
+ /**
+ * Set the camera orientation property on the NaCl module.
+ * @param {Array.<number>} orientation A 4-element array representing the
+ * camera orientation as a quaternion.
+ */
+ this.module_.setCameraOrientation = function(orientation) {
+ var methodString = 'setCameraOrientation ' +
+ 'orientation:' +
+ JSON.stringify(orientation);
+ this.postMessage(methodString);
+ }
+
+ this.trackball_ = new tumbler.Trackball();
+ this.dragger_ = new tumbler.Dragger(this.module_);
+ this.dragger_.addDragListener(this.trackball_);
+}
+
+/**
+ * Asserts that cond is true; issues an alert and throws an Error otherwise.
+ * @param {bool} cond The condition.
+ * @param {String} message The error message issued if cond is false.
+ */
+tumbler.Application.prototype.assert = function(cond, message) {
+ if (!cond) {
+ message = "Assertion failed: " + message;
+ alert(message);
+ throw new Error(message);
+ }
+}
+
+/**
+ * The run() method starts and 'runs' the application. The trackball object
+ * is allocated and all the events get wired up.
+ * @param {?String} opt_contentDivName The id of a DOM element in which to
+ * embed the Native Client module. If unspecified, defaults to
+ * VIEW. The DOM element must exist.
+ */
+tumbler.Application.prototype.run = function(opt_contentDivName) {
+ contentDivName = opt_contentDivName || tumbler.Application.DomIds_.VIEW;
+ var contentDiv = document.getElementById(contentDivName);
+ this.assert(contentDiv, "Missing DOM element '" + contentDivName + "'");
+
+ // Note that the <EMBED> element is wrapped inside a <DIV>, which has a 'load'
+ // event listener attached. This method is used instead of attaching the
+ // 'load' event listener directly to the <EMBED> element to ensure that the
+ // listener is active before the NaCl module 'load' event fires.
+ this.boundModuleDidLoad_ = this.moduleDidLoad.bind(this);
+ contentDiv.addEventListener('load', this.boundModuleDidLoad_, true);
+
+ // Load the published .nexe. This includes the 'nacl' attribute which
+ // shows how to load multi-architecture modules. Each entry in the "nexes"
+ // object in the .nmf manifest file is a key-value pair: the key is the
+ // runtime ('x86-32', 'x86-64', etc.); the value is a URL for the desired
+ // NaCl module. To load the debug versions of your .nexes, set the 'nacl'
+ // attribute to the _dbg.nmf version of the manifest file.
+ contentDiv.innerHTML = '<embed id="'
+ + tumbler.Application.DomIds_.MODULE + '" '
+ + 'src=fullscreen_tumbler.nmf '
+ + 'type="application/x-nacl" '
+ + 'width="480" height="480" />'
+}
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/tumbler_module.cc b/native_client_sdk/src/examples/fullscreen_tumbler/tumbler_module.cc
new file mode 100644
index 0000000..37e8bd0
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/tumbler_module.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/fullscreen_tumbler/tumbler.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/gles2/gl2ext_ppapi.h"
+
+/// The Module class. The browser calls the CreateInstance() method to create
+/// an instance of your NaCl module on the web page. The browser creates a new
+/// instance for each <embed> tag with type="application/x-nacl".
+class TumberModule : public pp::Module {
+ public:
+ TumberModule() : pp::Module() {}
+ virtual ~TumberModule() {
+ glTerminatePPAPI();
+ }
+
+ /// Called by the browser when the module is first loaded and ready to run.
+ /// This is called once per module, not once per instance of the module on
+ /// the page.
+ virtual bool Init() {
+ return glInitializePPAPI(get_browser_interface()) == GL_TRUE;
+ }
+
+ /// Create and return a Tumbler instance object.
+ /// @param[in] instance The browser-side instance.
+ /// @return the plugin-side instance.
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new tumbler::Tumbler(instance);
+ }
+};
+
+namespace pp {
+/// Factory function called by the browser when the module is first loaded.
+/// The browser keeps a singleton of this module. It calls the
+/// CreateInstance() method on the object you return to make instances. There
+/// is one instance per <embed> tag on the page. This is the main binding
+/// point for your NaCl module with the browser.
+Module* CreateModule() {
+ return new TumberModule();
+}
+} // namespace pp
+
diff --git a/native_client_sdk/src/examples/fullscreen_tumbler/vector3.js b/native_client_sdk/src/examples/fullscreen_tumbler/vector3.js
new file mode 100644
index 0000000..a79f781
--- /dev/null
+++ b/native_client_sdk/src/examples/fullscreen_tumbler/vector3.js
@@ -0,0 +1,91 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview A 3D vector class. Proviudes some utility functions on
+ * 3-dimentional vectors.
+ */
+
+// Requires tumbler
+
+/**
+ * Constructor for the Vector3 object. This class contains a 3-tuple that
+ * represents a vector in 3D space.
+ * @param {?number} opt_x The x-coordinate for this vector. If null or
+ * undefined, the x-coordinate value is set to 0.
+ * @param {?number} opt_y The y-coordinate for this vector. If null or
+ * undefined, the y-coordinate value is set to 0.
+ * @param {?number} opt_z The z-coordinate for this vector. If null or
+ * undefined, the z-coordinate value is set to 0.
+ * @constructor
+ */
+tumbler.Vector3 = function(opt_x, opt_y, opt_z) {
+ /**
+ * The vector's 3-tuple.
+ * @type {number}
+ */
+ this.x = opt_x || 0;
+ this.y = opt_y || 0;
+ this.z = opt_z || 0;
+}
+
+/**
+ * Method to return the magnitude of a Vector3.
+ * @return {number} the magnitude of the vector.
+ */
+tumbler.Vector3.prototype.magnitude = function() {
+ return Math.sqrt(this.dot(this));
+}
+
+/**
+ * Normalize the vector in-place.
+ * @return {number} the magnitude of the vector.
+ */
+tumbler.Vector3.prototype.normalize = function() {
+ var mag = this.magnitude();
+ if (mag < tumbler.Vector3.DOUBLE_EPSILON)
+ return 0.0; // |this| is equivalent to the 0-vector, don't normalize.
+ this.scale(1.0 / mag);
+ return mag;
+}
+
+/**
+ * Scale the vector in-place by |s|.
+ * @param {!number} s The scale factor.
+ */
+tumbler.Vector3.prototype.scale = function(s) {
+ this.x *= s;
+ this.y *= s;
+ this.z *= s;
+}
+
+/**
+ * Compute the dot product: |this| . v.
+ * @param {!tumbler.Vector3} v The vector to dot.
+ * @return {number} the result of |this| . v.
+ */
+tumbler.Vector3.prototype.dot = function(v) {
+ return this.x * v.x + this.y * v.y + this.z * v.z;
+}
+
+/**
+ * Compute the cross product: |this| X v.
+ * @param {!tumbler.Vector3} v The vector to cross with.
+ * @return {tumbler.Vector3} the result of |this| X v.
+ */
+tumbler.Vector3.prototype.cross = function(v) {
+ var vCross = new tumbler.Vector3(this.y * v.z - this.z * v.y,
+ this.z * v.x - this.x * v.z,
+ this.x * v.y - this.y * v.x);
+ return vCross;
+}
+
+/**
+ * Real numbers that are less than this distance apart are considered
+ * equivalent.
+ * TODO(dspringer): It seems as though there should be a const like this
+ * in generally available somewhere.
+ * @type {number}
+ */
+tumbler.Vector3.DOUBLE_EPSILON = 1.0e-16;
diff --git a/native_client_sdk/src/examples/geturl/build.scons b/native_client_sdk/src/examples/geturl/build.scons
new file mode 100644
index 0000000..f855644
--- /dev/null
+++ b/native_client_sdk/src/examples/geturl/build.scons
@@ -0,0 +1,41 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import make_nacl_env
+import os
+
+nacl_env = make_nacl_env.NaClEnvironment(
+ use_c_plus_plus_libs=True, nacl_platform=os.getenv('NACL_TARGET_PLATFORM'),
+ install_subdir='geturl', lib_prefix='..')
+nacl_env.Append(
+ # Add a CPPPATH that enables the full-path #include directives, such as
+ # #include "examples/sine_synth/sine_synth.h"
+ CPPPATH=[os.path.dirname(os.path.dirname(os.getcwd()))],
+ # Strict ANSI compliance.
+ EXTRA_CCFLAGS=['-pedantic'],
+ )
+
+sources = ['geturl.cc', 'geturl_handler.cc']
+
+opt_nexes, dbg_nexes = nacl_env.AllNaClModules(sources, 'geturl')
+
+# This target is used by the SDK build system to provide a prebuilt version
+# of the example in the SDK installer.
+nacl_env.InstallPrebuilt('geturl')
+
+app_files = [
+ 'geturl.html',
+ 'geturl_success.html',
+ 'geturl.nmf',
+ ]
+
+# Split the install of the .nexes from the other app sources so that the strip
+# action is applied to the .nexes only.
+install_nexes = nacl_env.NaClStrippedInstall(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=opt_nexes)
+install_app = nacl_env.Install(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=app_files)
+nacl_env.Alias('install', source=install_app + install_nexes)
diff --git a/native_client_sdk/src/examples/geturl/geturl.cc b/native_client_sdk/src/examples/geturl/geturl.cc
new file mode 100644
index 0000000..48d5cbb
--- /dev/null
+++ b/native_client_sdk/src/examples/geturl/geturl.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This example demonstrates how to load content of the page into NaCl module.
+
+#include <cstdio>
+#include <string>
+#include "examples/geturl/geturl_handler.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/url_loader.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/cpp/var.h"
+
+// These are the method names as JavaScript sees them.
+namespace {
+const char* const kLoadUrlMethodId = "getUrl";
+static const char kMessageArgumentSeparator = ':';
+
+// Exception strings. These are passed back to the browser when errors
+// happen during property accesses or method calls.
+const char* const kExceptionStartFailed = "GetURLHandler::Start() failed";
+const char* const kExceptionURLNotAString = "URL is not a string";
+} // namespace
+
+// The Instance class. One of these exists for each instance of your NaCl
+// module on the web page. The browser will ask the Module object to create
+// a new Instance for each occurrence of the <embed> tag that has these
+// attributes:
+// type="application/x-nacl"
+// src="geturl.nmf"
+class GetURLInstance : public pp::Instance {
+ public:
+ explicit GetURLInstance(PP_Instance instance) : pp::Instance(instance) {}
+ virtual ~GetURLInstance() {}
+
+ // Called by the browser to handle the postMessage() call in Javascript.
+ // The message in this case is expected to contain the string 'getUrl'
+ // followed by a ':' separator, then the URL to fetch. If a valid message
+ // of the form 'getUrl:URL' is received, then start up an asynchronous
+ // download of URL. In the event that errors occur, this method posts an
+ // error string back to the browser.
+ virtual void HandleMessage(const pp::Var& var_message);
+};
+
+void GetURLInstance::HandleMessage(const pp::Var& var_message) {
+ if (!var_message.is_string()) {
+ return;
+ }
+ std::string message = var_message.AsString();
+ if (message.find(kLoadUrlMethodId) == 0) {
+ // The argument to getUrl is everything after the first ':'.
+ size_t sep_pos = message.find_first_of(kMessageArgumentSeparator);
+ if (sep_pos != std::string::npos) {
+ std::string url = message.substr(sep_pos + 1);
+ printf("GetURLInstance::HandleMessage('%s', '%s')\n",
+ message.c_str(),
+ url.c_str());
+ fflush(stdout);
+ GetURLHandler* handler = GetURLHandler::Create(this, url);
+ if (handler != NULL) {
+ // Starts asynchronous download. When download is finished or when an
+ // error occurs, |handler| posts the results back to the browser
+ // vis PostMessage and self-destroys.
+ handler->Start();
+ }
+ }
+ }
+}
+
+
+// The Module class. The browser calls the CreateInstance() method to create
+// an instance of you NaCl module on the web page. The browser creates a new
+// instance for each <embed> tag with type="application/x-nacl".
+class GetURLModule : public pp::Module {
+ public:
+ GetURLModule() : pp::Module() {}
+ virtual ~GetURLModule() {}
+
+ // Create and return a GetURLInstance object.
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new GetURLInstance(instance);
+ }
+};
+
+// Factory function called by the browser when the module is first loaded.
+// The browser keeps a singleton of this module. It calls the
+// CreateInstance() method on the object you return to make instances. There
+// is one instance per <embed> tag on the page. This is the main binding
+// point for your NaCl module with the browser.
+namespace pp {
+Module* CreateModule() {
+ return new GetURLModule();
+}
+} // namespace pp
+
diff --git a/native_client_sdk/src/examples/geturl/geturl.html b/native_client_sdk/src/examples/geturl/geturl.html
new file mode 100644
index 0000000..c4a0c79
--- /dev/null
+++ b/native_client_sdk/src/examples/geturl/geturl.html
@@ -0,0 +1,125 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+ <!--
+ Copyright (c) 2011 The Native Client Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+<head>
+ <title>Get URL</title>
+
+ <script type="text/javascript">
+ geturlModule = null; // Global application object.
+ statusText = 'NO-STATUS';
+
+ // Indicate success when the NaCl module has loaded.
+ function moduleDidLoad() {
+ geturlModule = document.getElementById('geturl');
+ updateStatus('SUCCESS');
+ }
+
+ // If the page loads before the Native Client module loads, then set the
+ // status message indicating that the module is still loading. Otherwise,
+ // do not change the status message.
+ function pageDidLoad() {
+ if (geturl == null) {
+ updateStatus('LOADING...');
+ } else {
+ // It's possible that the Native Client module onload event fired
+ // before the page's onload event. In this case, the status message
+ // will reflect 'SUCCESS', but won't be displayed. This call will
+ // display the current message.
+ updateStatus();
+ }
+ }
+
+ // Called from the NaCl module via PostMessage(). The message data
+ // contains a URL followed by a '\n' separator character and the result
+ // text. The result test itself can contain '\n' characters, only the first
+ // '\n' is considered when separating the message parameters.
+ function handleMessage(message_event) {
+ var logElt = document.getElementById('general_output');
+ // Find the first line break. This separates the URL data from the
+ // result text. Note that the result text can contain any number of
+ // '\n' characters, so split() won't work here.
+ var url = message_event.data;
+ var result = '';
+ var eol_pos = message_event.data.indexOf("\n");
+ if (eol_pos != -1) {
+ url = message_event.data.substring(0, eol_pos);
+ if (eol_pos < message_event.data.length - 1) {
+ result = message_event.data.substring(eol_pos + 1);
+ }
+ }
+ logElt.textContent += 'FULLY QUALIFIED URL: ' + url + '\n';
+ logElt.textContent += 'RESULT:\n' + result + '\n';
+ }
+
+ function loadUrl() {
+ geturlModule.postMessage('getUrl:geturl_success.html');
+ }
+
+ // Set the global status message. If the element with id 'statusField'
+ // exists, then set its HTML to the status message as well.
+ // @param opt_message The message text. If this is null or undefined, then
+ // attempt to set the element with id 'status_field' to the value of
+ // @a statusText.
+ function updateStatus(opt_message) {
+ if (opt_message)
+ statusText = opt_message;
+ var statusField = document.getElementById('status_field');
+ if (statusField) {
+ statusField.innerHTML = statusText;
+ }
+ }
+ </script>
+</head>
+<body onload="pageDidLoad()">
+
+<h1>Native Client GetURL Module</h1>
+<p>
+<table border=5 cellpadding=5% summary="A title and a result log">
+ <tr>
+ <td valign=top><pre id='general_output' class='notrun'></pre></td>
+ </tr>
+</table>
+
+ <form name="geturl_form" action="" method="get">
+ <input type="button" value="Get URL" onclick="loadUrl()"/>
+ </form>
+
+ <!-- Load the published .nexe. This includes the 'src' attribute which
+ shows how to load multi-architecture modules. Each entry in the "nexes"
+ object in the .nmf manifest file is a key-value pair: the key is the runtime
+ ('x86-32', 'x86-64', etc.); the value is a URL for the desired NaCl module.
+ To load the debug versions of your .nexes, set the 'src' attribute to the
+ _dbg.nmf version of the manifest file.
+
+ Note: The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
+ and a 'message' event listener attached. This wrapping method is used
+ instead of attaching the event listeners directly to the <EMBED> element to
+ ensure that the listeners are active before the NaCl module 'load' event
+ fires. This also allows you to use PPB_Messaging.PostMessage() (in C) or
+ pp::Instance.PostMessage() (in C++) from within the initialization code in
+ your NaCl module.
+ -->
+ <div id="listener">
+ <script type="text/javascript">
+ var listener = document.getElementById('listener')
+ listener.addEventListener('load', moduleDidLoad, true);
+ listener.addEventListener('message', handleMessage, true);
+ </script>
+
+ <embed name="nacl_module"
+ id="geturl"
+ width=0 height=0
+ src="geturl.nmf"
+ type="application/x-nacl" />
+ </div>
+</p>
+
+<h2>Module loading status</h2>
+<div id="status_field">NO-STATUS</div>
+</body>
+</html>
diff --git a/native_client_sdk/src/examples/geturl/geturl_handler.cc b/native_client_sdk/src/examples/geturl/geturl_handler.cc
new file mode 100644
index 0000000..f6536fc
--- /dev/null
+++ b/native_client_sdk/src/examples/geturl/geturl_handler.cc
@@ -0,0 +1,137 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/geturl/geturl_handler.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/c/ppb_instance.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/cpp/var.h"
+
+namespace {
+bool IsError(int32_t result) {
+ return ((PP_OK != result) && (PP_OK_COMPLETIONPENDING != result));
+}
+} // namespace
+
+GetURLHandler* GetURLHandler::Create(pp::Instance* instance,
+ const std::string& url) {
+ return new GetURLHandler(instance, url);
+}
+
+GetURLHandler::GetURLHandler(pp::Instance* instance,
+ const std::string& url)
+ : instance_(instance),
+ url_(url),
+ url_request_(instance),
+ url_loader_(instance),
+ cc_factory_(this) {
+ url_request_.SetURL(url);
+ url_request_.SetMethod("GET");
+}
+
+GetURLHandler::~GetURLHandler() {
+}
+
+void GetURLHandler::Start() {
+ pp::CompletionCallback cc =
+ cc_factory_.NewRequiredCallback(&GetURLHandler::OnOpen);
+ url_loader_.Open(url_request_, cc);
+}
+
+void GetURLHandler::OnOpen(int32_t result) {
+ if (result != PP_OK) {
+ ReportResultAndDie(url_, "pp::URLLoader::Open() failed", false);
+ return;
+ }
+ // Here you would process the headers. A real program would want to at least
+ // check the HTTP code and potentially cancel the request.
+ // pp::URLResponseInfo response = loader_.GetResponseInfo();
+
+ // Start streaming.
+ ReadBody();
+}
+
+void GetURLHandler::AppendDataBytes(const char* buffer, int32_t num_bytes) {
+ if (num_bytes <= 0)
+ return;
+ // Make sure we don't get a buffer overrun.
+ num_bytes = std::min(READ_BUFFER_SIZE, num_bytes);
+ url_response_body_.reserve(url_response_body_.size() + num_bytes);
+ url_response_body_.insert(url_response_body_.end(),
+ buffer,
+ buffer + num_bytes);
+}
+
+void GetURLHandler::OnRead(int32_t result) {
+ if (result == PP_OK) {
+ // Streaming the file is complete.
+ ReportResultAndDie(url_, url_response_body_, true);
+ } else if (result > 0) {
+ // The URLLoader just filled "result" number of bytes into our buffer.
+ // Save them and perform another read.
+ AppendDataBytes(buffer_, result);
+ ReadBody();
+ } else {
+ // A read error occurred.
+ ReportResultAndDie(url_,
+ "pp::URLLoader::ReadResponseBody() result<0",
+ false);
+ }
+}
+
+void GetURLHandler::ReadBody() {
+ // Note that you specifically want an "optional" callback here. This will
+ // allow ReadBody() to return synchronously, ignoring your completion
+ // callback, if data is available. For fast connections and large files,
+ // reading as fast as we can will make a large performance difference
+ // However, in the case of a synchronous return, we need to be sure to run
+ // the callback we created since the loader won't do anything with it.
+ pp::CompletionCallback cc =
+ cc_factory_.NewOptionalCallback(&GetURLHandler::OnRead);
+ int32_t result = PP_OK;
+ do {
+ result = url_loader_.ReadResponseBody(buffer_, sizeof(buffer_), cc);
+ // Handle streaming data directly. Note that we *don't* want to call
+ // OnRead here, since in the case of result > 0 it will schedule
+ // another call to this function. If the network is very fast, we could
+ // end up with a deeply recursive stack.
+ if (result > 0) {
+ AppendDataBytes(buffer_, result);
+ }
+ } while (result > 0);
+
+ if (result != PP_OK_COMPLETIONPENDING) {
+ // Either we reached the end of the stream (result == PP_OK) or there was
+ // an error. We want OnRead to get called no matter what to handle
+ // that case, whether the error is synchronous or asynchronous. If the
+ // result code *is* COMPLETIONPENDING, our callback will be called
+ // asynchronously.
+ cc.Run(result);
+ }
+}
+
+void GetURLHandler::ReportResultAndDie(const std::string& fname,
+ const std::string& text,
+ bool success) {
+ ReportResult(fname, text, success);
+ delete this;
+}
+
+void GetURLHandler::ReportResult(const std::string& fname,
+ const std::string& text,
+ bool success) {
+ if (success)
+ printf("GetURLHandler::ReportResult(Ok).\n");
+ else
+ printf("GetURLHandler::ReportResult(Err). %s\n", text.c_str());
+ fflush(stdout);
+ if (instance_) {
+ pp::Var var_result(fname + "\n" + text);
+ instance_->PostMessage(var_result);
+ }
+}
+
diff --git a/native_client_sdk/src/examples/geturl/geturl_handler.h b/native_client_sdk/src/examples/geturl/geturl_handler.h
new file mode 100644
index 0000000..928f22c
--- /dev/null
+++ b/native_client_sdk/src/examples/geturl/geturl_handler.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_GETURL_GETURL_HANDLER_H_
+#define EXAMPLES_GETURL_GETURL_HANDLER_H_
+
+#include <string>
+#include "ppapi/cpp/completion_callback.h"
+#include "ppapi/cpp/url_loader.h"
+#include "ppapi/cpp/url_request_info.h"
+#include "ppapi/cpp/instance.h"
+
+#define READ_BUFFER_SIZE 4096
+
+// GetURLHandler is used to download data from |url|. When download is
+// finished or when an error occurs, it posts a message back to the browser
+// with the results encoded in the message as a string and self-destroys.
+//
+// EXAMPLE USAGE:
+// GetURLHandler* handler* = GetURLHandler::Create(instance,url);
+// handler->Start();
+//
+class GetURLHandler {
+ public:
+ // Creates instance of GetURLHandler on the heap.
+ // GetURLHandler objects shall be created only on the heap (they
+ // self-destroy when all data is in).
+ static GetURLHandler* Create(pp::Instance* instance_,
+ const std::string& url);
+ // Initiates page (URL) download.
+ void Start();
+
+ private:
+ GetURLHandler(pp::Instance* instance_, const std::string& url);
+ ~GetURLHandler();
+
+ // Callback fo the pp::URLLoader::Open().
+ // Called by pp::URLLoader when response headers are received or when an
+ // error occurs (in response to the call of pp::URLLoader::Open()).
+ // Look at <ppapi/c/ppb_url_loader.h> and
+ // <ppapi/cpp/url_loader.h> for more information about pp::URLLoader.
+ void OnOpen(int32_t result);
+
+ // Callback fo the pp::URLLoader::ReadResponseBody().
+ // |result| contains the number of bytes read or an error code.
+ // Appends data from this->buffer_ to this->url_response_body_.
+ void OnRead(int32_t result);
+
+ // Reads the response body (asynchronously) into this->buffer_.
+ // OnRead() will be called when bytes are received or when an error occurs.
+ void ReadBody();
+
+ // Append data bytes read from the URL onto the internal buffer. Does
+ // nothing if |num_bytes| is 0.
+ void AppendDataBytes(const char* buffer, int32_t num_bytes);
+
+ // Post a message back to the browser with the download results.
+ void ReportResult(const std::string& fname,
+ const std::string& text,
+ bool success);
+ // Post a message back to the browser with the download results and
+ // self-destroy. |this| is no longer valid when this method returns.
+ void ReportResultAndDie(const std::string& fname,
+ const std::string& text,
+ bool success);
+
+ pp::Instance* instance_; // Weak pointer.
+ std::string url_; // URL to be downloaded.
+ pp::URLRequestInfo url_request_;
+ pp::URLLoader url_loader_; // URLLoader provides an API to download URLs.
+ char buffer_[READ_BUFFER_SIZE]; // Temporary buffer for reads.
+ std::string url_response_body_; // Contains accumulated downloaded data.
+ pp::CompletionCallbackFactory<GetURLHandler> cc_factory_;
+
+ GetURLHandler(const GetURLHandler&);
+ void operator=(const GetURLHandler&);
+};
+
+#endif // EXAMPLES_GETURL_GETURL_HANDLER_H_
+
diff --git a/native_client_sdk/src/examples/geturl/geturl_success.html b/native_client_sdk/src/examples/geturl/geturl_success.html
new file mode 100644
index 0000000..8f2f112
--- /dev/null
+++ b/native_client_sdk/src/examples/geturl/geturl_success.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+ <!--
+ Copyright (c) 2010 The Native Client Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+ <head>
+ <title>PPAPI geturl example</title>
+ <META HTTP-EQUIV="Pragma" CONTENT="no-cache" />
+ <META HTTP-EQUIV="Expires" CONTENT="-1" />
+ </head>
+ <body>
+ <h1>PPAPI geturl example</h1>
+ The PPAPI geturl example fetches the contents of this page.
+ If you are seeing the contents of this page as part of the test output,
+ then the test passed.
+ </body>
+</html>
diff --git a/native_client_sdk/src/examples/hello_world/build.scons b/native_client_sdk/src/examples/hello_world/build.scons
new file mode 100644
index 0000000..51dc5df
--- /dev/null
+++ b/native_client_sdk/src/examples/hello_world/build.scons
@@ -0,0 +1,59 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import make_nacl_env
+import nacl_utils
+import os
+
+nacl_env = make_nacl_env.NaClEnvironment(
+ use_c_plus_plus_libs=True, nacl_platform=os.getenv('NACL_TARGET_PLATFORM'),
+ install_subdir='hello_world', lib_prefix='..')
+nacl_test_env = make_nacl_env.NaClEnvironment(
+ use_c_plus_plus_libs=True,
+ nacl_platform=os.getenv('NACL_TARGET_PLATFORM'),
+ use_ppapi=False)
+for env in [nacl_env, nacl_test_env]:
+ env.Append(
+ # Add a CPPPATH that enables the full-path #include directives, such as
+ # #include "examples/sine_synth/sine_synth.h"
+ CPPPATH=[os.path.dirname(os.path.dirname(os.getcwd()))],
+ # Strict ANSI compliance.
+ CCFLAGS=['-pedantic', '-Werror'],
+ )
+
+sources = ['hello_world.cc', 'helper_functions.cc']
+test_sources = ['helper_functions.cc', 'test_helper_functions.cc']
+
+opt_nexes, dbg_nexes = nacl_env.AllNaClModules(sources, 'hello_world')
+
+nacl_test_32 = nacl_test_env.Clone()
+nacl_test_32.NaClTestProgram(test_sources,
+ nacl_utils.ARCH_SPECS['x86-32'],
+ module_name='hello_world_test',
+ target_name='test32')
+
+nacl_test_64 = nacl_test_env.Clone()
+nacl_test_64.NaClTestProgram(test_sources,
+ nacl_utils.ARCH_SPECS['x86-64'],
+ module_name='hello_world_test',
+ target_name='test64')
+
+# This target is used by the SDK build system to provide a prebuilt version
+# of the example in the SDK installer.
+nacl_env.InstallPrebuilt('hello_world')
+
+app_files = [
+ 'hello_world.html',
+ 'hello_world.nmf',
+ ]
+
+# Split the install of the .nexes from the other app sources so that the strip
+# action is applied to the .nexes only.
+install_nexes = nacl_env.NaClStrippedInstall(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=opt_nexes)
+install_app = nacl_env.Install(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=app_files)
+nacl_env.Alias('install', source=install_app + install_nexes)
diff --git a/native_client_sdk/src/examples/hello_world/hello_world.cc b/native_client_sdk/src/examples/hello_world/hello_world.cc
new file mode 100644
index 0000000..df67961
--- /dev/null
+++ b/native_client_sdk/src/examples/hello_world/hello_world.cc
@@ -0,0 +1,130 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/// @file
+/// This example demonstrates loading, running and scripting a very simple NaCl
+/// module. To load the NaCl module, the browser first looks for the
+/// CreateModule() factory method (at the end of this file). It calls
+/// CreateModule() once to load the module code from your .nexe. After the
+/// .nexe code is loaded, CreateModule() is not called again.
+///
+/// Once the .nexe code is loaded, the browser then calls the
+/// HelloWorldModule::CreateInstance()
+/// method on the object returned by CreateModule(). It calls CreateInstance()
+/// each time it encounters an <embed> tag that references your NaCl module.
+
+#include <cstdio>
+#include <cstring>
+#include <string>
+#include "examples/hello_world/helper_functions.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/cpp/var.h"
+
+namespace hello_world {
+/// Method name for ReverseText, as seen by JavaScript code.
+const char* const kReverseTextMethodId = "reverseText";
+
+/// Method name for FortyTwo, as seen by Javascript code. @see FortyTwo()
+const char* const kFortyTwoMethodId = "fortyTwo";
+
+/// Separator character for the reverseText method.
+static const char kMessageArgumentSeparator = ':';
+
+/// This is the module's function that invokes FortyTwo and converts the return
+/// value from an int32_t to a pp::Var for return.
+pp::Var MarshallFortyTwo() {
+ return pp::Var(FortyTwo());
+}
+
+/// This function is passed the arg list from the JavaScript call to
+/// @a reverseText.
+/// It makes sure that there is one argument and that it is a string, returning
+/// an error message if it is not.
+/// On good input, it calls ReverseText and returns the result. The result is
+/// then sent back via a call to PostMessage.
+pp::Var MarshallReverseText(const std::string& text) {
+ return pp::Var(ReverseText(text));
+}
+
+/// The Instance class. One of these exists for each instance of your NaCl
+/// module on the web page. The browser will ask the Module object to create
+/// a new Instance for each occurrence of the <embed> tag that has these
+/// attributes:
+/// <pre>
+/// type="application/x-nacl"
+/// nacl="hello_world.nmf"
+/// </pre>
+class HelloWorldInstance : public pp::Instance {
+ public:
+ explicit HelloWorldInstance(PP_Instance instance) : pp::Instance(instance) {}
+ virtual ~HelloWorldInstance() {}
+
+ /// Called by the browser to handle the postMessage() call in Javascript.
+ /// Detects which method is being called from the message contents, and
+ /// calls the appropriate function. Posts the result back to the browser
+ /// asynchronously.
+ /// @param[in] var_message The message posted by the browser. The possible
+ /// messages are 'fortyTwo' and 'reverseText:Hello World'. Note that
+ /// the 'reverseText' form contains the string to reverse following a ':'
+ /// separator.
+ virtual void HandleMessage(const pp::Var& var_message);
+};
+
+void HelloWorldInstance::HandleMessage(const pp::Var& var_message) {
+ if (!var_message.is_string()) {
+ return;
+ }
+ std::string message = var_message.AsString();
+ pp::Var return_var;
+ if (message == kFortyTwoMethodId) {
+ // Note that no arguments are passed in to FortyTwo.
+ return_var = MarshallFortyTwo();
+ } else if (message.find(kReverseTextMethodId) == 0) {
+ // The argument to reverseText is everything after the first ':'.
+ size_t sep_pos = message.find_first_of(kMessageArgumentSeparator);
+ if (sep_pos != std::string::npos) {
+ std::string string_arg = message.substr(sep_pos + 1);
+ return_var = MarshallReverseText(string_arg);
+ }
+ }
+ // Post the return result back to the browser. Note that HandleMessage() is
+ // always called on the main thread, so it's OK to post the return message
+ // directly from here. The return post is asynhronous: PostMessage returns
+ // immediately.
+ PostMessage(return_var);
+}
+
+/// The Module class. The browser calls the CreateInstance() method to create
+/// an instance of your NaCl module on the web page. The browser creates a new
+/// instance for each <embed> tag with
+/// <code>type="application/x-nacl"</code>.
+class HelloWorldModule : public pp::Module {
+ public:
+ HelloWorldModule() : pp::Module() {}
+ virtual ~HelloWorldModule() {}
+
+ /// Create and return a HelloWorldInstance object.
+ /// @param[in] instance a handle to a plug-in instance.
+ /// @return a newly created HelloWorldInstance.
+ /// @note The browser is responsible for calling @a delete when done.
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new HelloWorldInstance(instance);
+ }
+};
+} // namespace hello_world
+
+
+namespace pp {
+/// Factory function called by the browser when the module is first loaded.
+/// The browser keeps a singleton of this module. It calls the
+/// CreateInstance() method on the object you return to make instances. There
+/// is one instance per <embed> tag on the page. This is the main binding
+/// point for your NaCl module with the browser.
+/// @return new HelloWorldModule.
+/// @note The browser is responsible for deleting returned @a Module.
+Module* CreateModule() {
+ return new hello_world::HelloWorldModule();
+}
+} // namespace pp
diff --git a/native_client_sdk/src/examples/hello_world/hello_world.html b/native_client_sdk/src/examples/hello_world/hello_world.html
new file mode 100644
index 0000000..16dc42c
--- /dev/null
+++ b/native_client_sdk/src/examples/hello_world/hello_world.html
@@ -0,0 +1,126 @@
+<!DOCTYPE html>
+<html>
+ <!--
+ Copyright (c) 2011 The Native Client Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+<head>
+ <title>Hello, World!</title>
+
+ <script type="text/javascript">
+ helloWorldModule = null; // Global application object.
+ statusText = 'NO-STATUS';
+
+ // Indicate success when the NaCl module has loaded.
+ function moduleDidLoad() {
+ helloWorldModule = document.getElementById('hello_world');
+ updateStatus('SUCCESS');
+ }
+
+ // Handle a message coming from the NaCl module.
+ function handleMessage(message_event) {
+ alert(message_event.data);
+ }
+
+ // If the page loads before the Native Client module loads, then set the
+ // status message indicating that the module is still loading. Otherwise,
+ // do not change the status message.
+ function pageDidLoad() {
+ // Set the focus on the text input box. Doing this means you can press
+ // return as soon as the page loads, and it will fire the reversetText()
+ // function.
+ document.forms.helloForm.inputBox.focus();
+ if (helloWorldModule == null) {
+ updateStatus('LOADING...');
+ } else {
+ // It's possible that the Native Client module onload event fired
+ // before the page's onload event. In this case, the status message
+ // will reflect 'SUCCESS', but won't be displayed. This call will
+ // display the current message.
+ updateStatus();
+ }
+ }
+
+ function fortyTwo() {
+ helloWorldModule.postMessage('fortyTwo');
+ }
+
+ function reverseText() {
+ // Grab the text from the text box, pass it into reverseText()
+ var inputBox = document.forms.helloForm.inputBox;
+ helloWorldModule.postMessage('reverseText:' + inputBox.value);
+ // Note: a |false| return tells the <form> tag to cancel the GET action
+ // when submitting the form.
+ return false;
+ }
+
+ // Set the global status message. If the element with id 'statusField'
+ // exists, then set its HTML to the status message as well.
+ // opt_message The message test. If this is null or undefined, then
+ // attempt to set the element with id 'statusField' to the value of
+ // |statusText|.
+ function updateStatus(opt_message) {
+ if (opt_message)
+ statusText = opt_message;
+ var statusField = document.getElementById('statusField');
+ if (statusField) {
+ statusField.innerHTML = statusText;
+ }
+ }
+ </script>
+</head>
+<body onload="pageDidLoad()">
+
+<h1>Native Client Simple Module</h1>
+<p>
+ <form name="helloForm"
+ action=""
+ method="get"
+ onsubmit="return reverseText()">
+ <input type="text" id="inputBox" name="inputBox" value="Hello world" /><p/>
+ <input type="button" value="Call fortyTwo()" onclick="fortyTwo()" />
+ <input type="submit" value="Call reverseText()" />
+ </form>
+ <!-- Load the published .nexe. This includes the 'src' attribute which
+ shows how to load multi-architecture modules. Each entry in the "nexes"
+ object in the .nmf manifest file is a key-value pair: the key is the runtime
+ ('x86-32', 'x86-64', etc.); the value is a URL for the desired NaCl module.
+ To load the debug versions of your .nexes, set the 'src' attribute to the
+ _dbg.nmf version of the manifest file.
+
+ Note: The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
+ and a 'message' event listener attached. This wrapping method is used
+ instead of attaching the event listeners directly to the <EMBED> element to
+ ensure that the listeners are active before the NaCl module 'load' event
+ fires. This also allows you to use PPB_Messaging.PostMessage() (in C) or
+ pp::Instance.PostMessage() (in C++) from within the initialization code in
+ your NaCl module.
+ -->
+ <div id="listener">
+ <script type="text/javascript">
+ var listener = document.getElementById('listener')
+ listener.addEventListener('load', moduleDidLoad, true);
+ listener.addEventListener('message', handleMessage, true);
+ </script>
+
+ <embed name="nacl_module"
+ id="hello_world"
+ width=0 height=0
+ src="hello_world.nmf"
+ type="application/x-nacl" />
+ </div>
+
+</p>
+
+<p>If the module is working correctly, a click on the "Call fortyTwo()" button
+ should open a popup dialog containing <b>42</b> as its value.</p>
+
+<p> Clicking on the "Call reverseText()" button
+ should open a popup dialog containing the textbox contents and its reverse
+ as its value.</p>
+
+<h2>Status</h2>
+<div id="statusField">NO-STATUS</div>
+</body>
+</html>
diff --git a/native_client_sdk/src/examples/hello_world/helper_functions.cc b/native_client_sdk/src/examples/hello_world/helper_functions.cc
new file mode 100644
index 0000000..5ed26b5
--- /dev/null
+++ b/native_client_sdk/src/examples/hello_world/helper_functions.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/hello_world/helper_functions.h"
+
+#include <algorithm>
+
+namespace hello_world {
+
+int32_t FortyTwo() {
+ return 42;
+}
+
+std::string ReverseText(const std::string& text) {
+ std::string reversed_string(text);
+ // Use reverse to reverse |reversed_string| in place.
+ std::reverse(reversed_string.begin(), reversed_string.end());
+ return reversed_string;
+}
+} // namespace hello_world
+
diff --git a/native_client_sdk/src/examples/hello_world/helper_functions.h b/native_client_sdk/src/examples/hello_world/helper_functions.h
new file mode 100644
index 0000000..69ab874
--- /dev/null
+++ b/native_client_sdk/src/examples/hello_world/helper_functions.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_HELLO_WORLD_HELPER_FUNCTIONS_H_
+#define EXAMPLES_HELLO_WORLD_HELPER_FUNCTIONS_H_
+
+/// @file
+/// These functions are stand-ins for your complicated computations which you
+/// want to run in native code. We do two very simple things: return 42, and
+/// reverse a string. But you can imagine putting more complicated things here
+/// which might be difficult or slow to achieve in JavaScript, such as
+/// cryptography, artificial intelligence, signal processing, physics modeling,
+/// etc. See hello_world.cc for the code which is required for loading a NaCl
+/// application and exposing methods to JavaScript.
+
+#include <ppapi/c/pp_stdint.h>
+#include <string>
+
+namespace hello_world {
+
+/// This is the module's function that does the work to compute the value 42.
+int32_t FortyTwo();
+
+/// This function is passed a string and returns a copy of the string with the
+/// characters in reverse order.
+/// @param[in] text The string to reverse.
+/// @return A copy of @a text with the characters in reverse order.
+std::string ReverseText(const std::string& text);
+
+} // namespace hello_world
+
+#endif // EXAMPLES_HELLO_WORLD_HELPER_FUNCTIONS_H_
+
diff --git a/native_client_sdk/src/examples/hello_world/test_helper_functions.cc b/native_client_sdk/src/examples/hello_world/test_helper_functions.cc
new file mode 100644
index 0000000..f73f71a
--- /dev/null
+++ b/native_client_sdk/src/examples/hello_world/test_helper_functions.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is an example of a simple unit test to verify that the logic helper
+// functions works as expected. Note that this looks like a 'normal' C++
+// program, with a main function. It is compiled and linked using the NaCl
+// toolchain, so in order to run it, you must use 'sel_ldr_x86_32' or
+// 'sel_ldr_x86_64' from the toolchain's bin directory.
+//
+// For example (assuming the toolchain bin directory is in your path):
+// sel_ldr_x86_32 test_helper_functions_x86_32_dbg.nexe
+//
+// You can also use the 'test32', or 'test64' SCons target to run these tests.
+// For example, this will run the test in 32-bit mode on Mac or Linux:
+// ../scons test32
+// On Windows 64:
+// ..\scons test64
+
+#include "examples/hello_world/helper_functions.h"
+
+#include <cassert>
+#include <cstdio>
+#include <string>
+
+// A very simple macro to print 'passed' if boolean_expression is true and
+// 'FAILED' otherwise.
+// This is meant to approximate the functionality you would get from a real test
+// framework. You should feel free to build and use the test framework of your
+// choice.
+#define EXPECT_EQUAL(left, right)\
+printf("Check: \"" #left "\" == \"" #right "\" %s\n", \
+ ((left) == (right)) ? "passed" : "FAILED")
+
+using hello_world::FortyTwo;
+using hello_world::ReverseText;
+
+int main() {
+ EXPECT_EQUAL(FortyTwo(), 42);
+
+ std::string empty_string;
+ EXPECT_EQUAL(ReverseText(empty_string), empty_string);
+
+ std::string palindrome("able was i ere i saw elba");
+ EXPECT_EQUAL(ReverseText(palindrome), palindrome);
+
+ std::string alphabet("abcdefghijklmnopqrstuvwxyz");
+ std::string alphabet_backwards("zyxwvutsrqponmlkjihgfedcba");
+ EXPECT_EQUAL(ReverseText(alphabet), alphabet_backwards);
+}
+
diff --git a/native_client_sdk/src/examples/hello_world_c/build.scons b/native_client_sdk/src/examples/hello_world_c/build.scons
new file mode 100644
index 0000000..90666e5
--- /dev/null
+++ b/native_client_sdk/src/examples/hello_world_c/build.scons
@@ -0,0 +1,40 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import make_nacl_env
+import os
+
+nacl_env = make_nacl_env.NaClEnvironment(
+ nacl_platform=os.getenv('NACL_TARGET_PLATFORM'),
+ install_subdir='hello_world_c', lib_prefix='..')
+nacl_env.Append(
+ # Add a CPPPATH that enables the full-path #include directives, such as
+ # #include "examples/sine_synth/sine_synth.h"
+ CPPPATH=[os.path.dirname(os.path.dirname(os.getcwd()))],
+ # Strict ANSI compliance.
+ EXTRA_CCFLAGS=['-pedantic'],
+ )
+
+sources = ['hello_world_c.c']
+
+opt_nexes, dbg_nexes = nacl_env.AllNaClModules(sources, 'hello_world_c')
+
+# This target is used by the SDK build system to provide a prebuilt version
+# of the example in the SDK installer.
+nacl_env.InstallPrebuilt('hello_world_c')
+
+app_files = [
+ 'hello_world_c.html',
+ 'hello_world_c.nmf',
+ ]
+
+# Split the install of the .nexes from the other app sources so that the strip
+# action is applied to the .nexes only.
+install_nexes = nacl_env.NaClStrippedInstall(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=opt_nexes)
+install_app = nacl_env.Install(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=app_files)
+nacl_env.Alias('install', source=install_app + install_nexes)
diff --git a/native_client_sdk/src/examples/hello_world_c/hello_world_c.c b/native_client_sdk/src/examples/hello_world_c/hello_world_c.c
new file mode 100644
index 0000000..939e724
--- /dev/null
+++ b/native_client_sdk/src/examples/hello_world_c/hello_world_c.c
@@ -0,0 +1,287 @@
+/* Copyright (c) 2011 The Native Client Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/** @file hello_world.c
+ * This example demonstrates loading, running and scripting a very simple
+ * NaCl module.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/c/pp_module.h"
+#include "ppapi/c/pp_var.h"
+#include "ppapi/c/ppb.h"
+#include "ppapi/c/ppb_instance.h"
+#include "ppapi/c/ppb_messaging.h"
+#include "ppapi/c/ppb_var.h"
+#include "ppapi/c/ppp.h"
+#include "ppapi/c/ppp_instance.h"
+#include "ppapi/c/ppp_messaging.h"
+
+struct MessageInfo {
+ PP_Instance instance;
+ struct PP_Var message;
+};
+
+static const char* const kReverseTextMethodId = "reverseText";
+static const char* const kFortyTwoMethodId = "fortyTwo";
+static const char kMessageArgumentSeparator = ':';
+static const char kNullTerminator = '\0';
+
+static struct PPB_Messaging* ppb_messaging_interface = NULL;
+static struct PPB_Var* ppb_var_interface = NULL;
+static PP_Module module_id = 0;
+
+
+/**
+ * Returns a mutable C string contained in the @a var or NULL if @a var is not
+ * string. This makes a copy of the string in the @ var and adds a NULL
+ * terminator. Note that VarToUtf8() does not guarantee the NULL terminator on
+ * the returned string. See the comments for VatToUtf8() in ppapi/c/ppb_var.h
+ * for more info. The caller is responsible for freeing the returned memory.
+ * @param[in] var PP_Var containing string.
+ * @return a C string representation of @a var.
+ * @note The caller is responsible for freeing the returned string.
+ */
+static char* VarToCStr(struct PP_Var var) {
+ uint32_t len = 0;
+ if (ppb_var_interface != NULL) {
+ const char* var_c_str = ppb_var_interface->VarToUtf8(var, &len);
+ if (len > 0) {
+ char* c_str = (char*)malloc(len + 1);
+ memcpy(c_str, var_c_str, len);
+ c_str[len] = kNullTerminator;
+ return c_str;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Creates new string PP_Var from C string. The resulting object will be a
+ * refcounted string object. It will be AddRef()ed for the caller. When the
+ * caller is done with it, it should be Release()d.
+ * @param[in] str C string to be converted to PP_Var
+ * @return PP_Var containing string.
+ */
+static struct PP_Var CStrToVar(const char* str) {
+ if (ppb_var_interface != NULL) {
+ return ppb_var_interface->VarFromUtf8(module_id, str, strlen(str));
+ }
+ return PP_MakeUndefined();
+}
+
+/**
+ * Reverse C string in-place.
+ * @param[in,out] str C string to be reversed
+ */
+static void ReverseStr(char* str) {
+ char* right = str + strlen(str) - 1;
+ char* left = str;
+ while (left < right) {
+ char tmp = *left;
+ *left++ = *right;
+ *right-- = tmp;
+ }
+}
+
+/**
+ * A simple function that always returns 42.
+ * @return always returns the integer 42
+ */
+static struct PP_Var FortyTwo() {
+ return PP_MakeInt32(42);
+}
+
+/**
+ * Called when the NaCl module is instantiated on the web page. The identifier
+ * of the new instance will be passed in as the first argument (this value is
+ * generated by the browser and is an opaque handle). This is called for each
+ * instantiation of the NaCl module, which is each time the <embed> tag for
+ * this module is encountered.
+ *
+ * If this function reports a failure (by returning @a PP_FALSE), the NaCl
+ * module will be deleted and DidDestroy will be called.
+ * @param[in] instance The identifier of the new instance representing this
+ * NaCl module.
+ * @param[in] argc The number of arguments contained in @a argn and @a argv.
+ * @param[in] argn An array of argument names. These argument names are
+ * supplied in the <embed> tag, for example:
+ * <embed id="nacl_module" dimensions="2">
+ * will produce two arguments, one named "id" and one named "dimensions".
+ * @param[in] argv An array of argument values. These are the values of the
+ * arguments listed in the <embed> tag. In the above example, there will
+ * be two elements in this array, "nacl_module" and "2". The indices of
+ * these values match the indices of the corresponding names in @a argn.
+ * @return @a PP_TRUE on success.
+ */
+static PP_Bool Instance_DidCreate(PP_Instance instance,
+ uint32_t argc,
+ const char* argn[],
+ const char* argv[]) {
+ return PP_TRUE;
+}
+
+/**
+ * Called when the NaCl module is destroyed. This will always be called,
+ * even if DidCreate returned failure. This routine should deallocate any data
+ * associated with the instance.
+ * @param[in] instance The identifier of the instance representing this NaCl
+ * module.
+ */
+static void Instance_DidDestroy(PP_Instance instance) {
+}
+
+/**
+ * Called when the position, the size, or the clip rect of the element in the
+ * browser that corresponds to this NaCl module has changed.
+ * @param[in] instance The identifier of the instance representing this NaCl
+ * module.
+ * @param[in] position The location on the page of this NaCl module. This is
+ * relative to the top left corner of the viewport, which changes as the
+ * page is scrolled.
+ * @param[in] clip The visible region of the NaCl module. This is relative to
+ * the top left of the plugin's coordinate system (not the page). If the
+ * plugin is invisible, @a clip will be (0, 0, 0, 0).
+ */
+static void Instance_DidChangeView(PP_Instance instance,
+ const struct PP_Rect* position,
+ const struct PP_Rect* clip) {
+}
+
+/**
+ * Notification that the given NaCl module has gained or lost focus.
+ * Having focus means that keyboard events will be sent to the NaCl module
+ * represented by @a instance. A NaCl module's default condition is that it
+ * will not have focus.
+ *
+ * Note: clicks on NaCl modules will give focus only if you handle the
+ * click event. You signal if you handled it by returning @a true from
+ * HandleInputEvent. Otherwise the browser will bubble the event and give
+ * focus to the element on the page that actually did end up consuming it.
+ * If you're not getting focus, check to make sure you're returning true from
+ * the mouse click in HandleInputEvent.
+ * @param[in] instance The identifier of the instance representing this NaCl
+ * module.
+ * @param[in] has_focus Indicates whether this NaCl module gained or lost
+ * event focus.
+ */
+static void Instance_DidChangeFocus(PP_Instance instance,
+ PP_Bool has_focus) {
+}
+
+/**
+ * Handler that gets called after a full-frame module is instantiated based on
+ * registered MIME types. This function is not called on NaCl modules. This
+ * function is essentially a place-holder for the required function pointer in
+ * the PPP_Instance structure.
+ * @param[in] instance The identifier of the instance representing this NaCl
+ * module.
+ * @param[in] url_loader A PP_Resource an open PPB_URLLoader instance.
+ * @return PP_FALSE.
+ */
+static PP_Bool Instance_HandleDocumentLoad(PP_Instance instance,
+ PP_Resource url_loader) {
+ /* NaCl modules do not need to handle the document load function. */
+ return PP_FALSE;
+}
+
+/**
+ * Handler for messages coming in from the browser via postMessage. Extracts
+ * the method call from @a message, parses it for method name and value, then
+ * calls the appropriate function. In the case of the reverseString method, the
+ * message format is a simple colon-separated string. The first part of the
+ * string up to the colon is the method name; after that is the string argument.
+ * @param[in] instance The instance ID.
+ * @param[in] message The contents, copied by value, of the message sent from
+ * browser via postMessage.
+ */
+void Messaging_HandleMessage(PP_Instance instance, struct PP_Var var_message) {
+ if (var_message.type != PP_VARTYPE_STRING) {
+ /* Only handle string messages */
+ return;
+ }
+ char* message = VarToCStr(var_message);
+ if (message == NULL)
+ return;
+ struct PP_Var var_result = PP_MakeUndefined();
+ if (strncmp(message, kFortyTwoMethodId, strlen(kFortyTwoMethodId)) == 0) {
+ var_result = FortyTwo();
+ } else if (strncmp(message,
+ kReverseTextMethodId,
+ strlen(kReverseTextMethodId)) == 0) {
+ /* Use everything after the ':' in |message| as the string argument. */
+ char* string_arg = strchr(message, kMessageArgumentSeparator);
+ if (string_arg != NULL) {
+ string_arg += 1; /* Advance past the ':' separator. */
+ ReverseStr(string_arg);
+ var_result = CStrToVar(string_arg);
+ }
+ }
+ free(message);
+
+ /* Echo the return result back to browser. Note that HandleMessage is always
+ * called on the main thread, so it's OK to post the message back to the
+ * browser directly from here. This return post is asynchronous.
+ */
+ ppb_messaging_interface->PostMessage(instance, var_result);
+ /* If the message was created using VarFromUtf8() it needs to be released.
+ * See the comments about VarFromUtf8() in ppapi/c/ppb_var.h for more
+ * information.
+ */
+ if (var_result.type == PP_VARTYPE_STRING) {
+ ppb_var_interface->Release(var_result);
+ }
+}
+
+/**
+ * Entry points for the module.
+ * Initialize needed interfaces: PPB_Core, PPB_Messaging and PPB_Var.
+ * @param[in] a_module_id module ID
+ * @param[in] get_browser pointer to PPB_GetInterface
+ * @return PP_OK on success, any other value on failure.
+ */
+PP_EXPORT int32_t PPP_InitializeModule(PP_Module a_module_id,
+ PPB_GetInterface get_browser) {
+ module_id = a_module_id;
+ ppb_messaging_interface =
+ (struct PPB_Messaging*)(get_browser(PPB_MESSAGING_INTERFACE));
+ ppb_var_interface = (struct PPB_Var*)(get_browser(PPB_VAR_INTERFACE));
+
+ return PP_OK;
+}
+
+/**
+ * Returns an interface pointer for the interface of the given name, or NULL
+ * if the interface is not supported.
+ * @param[in] interface_name name of the interface
+ * @return pointer to the interface
+ */
+PP_EXPORT const void* PPP_GetInterface(const char* interface_name) {
+ if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) {
+ static struct PPP_Instance instance_interface = {
+ &Instance_DidCreate,
+ &Instance_DidDestroy,
+ &Instance_DidChangeView,
+ &Instance_DidChangeFocus,
+ &Instance_HandleDocumentLoad,
+ };
+ return &instance_interface;
+ } else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0) {
+ static struct PPP_Messaging messaging_interface = {
+ &Messaging_HandleMessage
+ };
+ return &messaging_interface;
+ }
+ return NULL;
+}
+
+/**
+ * Called before the plugin module is unloaded.
+ */
+PP_EXPORT void PPP_ShutdownModule() {
+}
diff --git a/native_client_sdk/src/examples/hello_world_c/hello_world_c.html b/native_client_sdk/src/examples/hello_world_c/hello_world_c.html
new file mode 100644
index 0000000..e5511c6
--- /dev/null
+++ b/native_client_sdk/src/examples/hello_world_c/hello_world_c.html
@@ -0,0 +1,126 @@
+<!DOCTYPE html>
+<html>
+ <!--
+ Copyright (c) 2011 The Native Client Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+<head>
+ <title>Hello, World!</title>
+
+ <script type="text/javascript">
+ helloWorldModule = null; // Global application object.
+ statusText = 'NO-STATUS';
+
+ // Indicate success when the NaCl module has loaded.
+ function moduleDidLoad() {
+ helloWorldModule = document.getElementById('hello_world');
+ updateStatus('SUCCESS');
+ }
+
+ // Handle a message coming from the NaCl module.
+ function handleMessage(message_event) {
+ alert(message_event.data);
+ }
+
+ // If the page loads before the Native Client module loads, then set the
+ // status message indicating that the module is still loading. Otherwise,
+ // do not change the status message.
+ function pageDidLoad() {
+ // Set the focus on the text input box. Doing this means you can press
+ // return as soon as the page loads, and it will fire the reversetText()
+ // function.
+ document.forms.helloForm.inputBox.focus();
+ if (helloWorldModule == null) {
+ updateStatus('LOADING...');
+ } else {
+ // It's possible that the Native Client module onload event fired
+ // before the page's onload event. In this case, the status message
+ // will reflect 'SUCCESS', but won't be displayed. This call will
+ // display the current message.
+ updateStatus();
+ }
+ }
+
+ function fortyTwo() {
+ helloWorldModule.postMessage('fortyTwo');
+ }
+
+ function reverseText() {
+ // Grab the text from the text box, pass it into reverseText()
+ var inputBox = document.forms.helloForm.inputBox;
+ helloWorldModule.postMessage('reverseText:' + inputBox.value);
+ // Note: a |false| return tells the <form> tag to cancel the GET action
+ // when submitting the form.
+ return false;
+ }
+
+ // Set the global status message. If the element with id 'statusField'
+ // exists, then set its HTML to the status message as well.
+ // opt_message The message test. If this is null or undefined, then
+ // attempt to set the element with id 'statusField' to the value of
+ // |statusText|.
+ function updateStatus(opt_message) {
+ if (opt_message)
+ statusText = opt_message;
+ var statusField = document.getElementById('statusField');
+ if (statusField) {
+ statusField.innerHTML = statusText;
+ }
+ }
+ </script>
+</head>
+<body onload="pageDidLoad()">
+
+<h1>Native Client Simple Module</h1>
+<p>
+ <form name="helloForm"
+ action=""
+ method="get"
+ onsubmit="return reverseText()">
+ <input type="text" id="inputBox" name="inputBox" value="Hello world" /><p/>
+ <input type="button" value="Call fortyTwo()" onclick="fortyTwo()" />
+ <input type="submit" value="Call reverseText()" />
+ </form>
+ <!-- Load the published .nexe. This includes the 'src' attribute which
+ shows how to load multi-architecture modules. Each entry in the "nexes"
+ object in the .nmf manifest file is a key-value pair: the key is the runtime
+ ('x86-32', 'x86-64', etc.); the value is a URL for the desired NaCl module.
+ To load the debug versions of your .nexes, set the 'src' attribute to the
+ _dbg.nmf version of the manifest file.
+
+ Note: The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
+ and a 'message' event listener attached. This wrapping method is used
+ instead of attaching the event listeners directly to the <EMBED> element to
+ ensure that the listeners are active before the NaCl module 'load' event
+ fires. This also allows you to use PPB_Messaging.PostMessage() (in C) or
+ pp::Instance.PostMessage() (in C++) from within the initialization code in
+ your NaCl module.
+ -->
+ <div id="listener">
+ <script type="text/javascript">
+ var listener = document.getElementById('listener')
+ listener.addEventListener('load', moduleDidLoad, true);
+ listener.addEventListener('message', handleMessage, true);
+ </script>
+
+ <embed name="nacl_module"
+ id="hello_world"
+ width=0 height=0
+ src="hello_world_c.nmf"
+ type="application/x-nacl" />
+ </div>
+
+</p>
+
+<p>If the module is working correctly, a click on the "Call fortyTwo()" button
+ should open a popup dialog containing <b>42</b> as its value.</p>
+
+<p> Clicking on the "Call reverseText()" button
+ should open a popup dialog containing the textbox contents and its reverse
+ as its value.</p>
+
+<h2>Status</h2>
+<div id="statusField">NO-STATUS</div>
+</body>
+</html>
diff --git a/native_client_sdk/src/examples/hello_world_c/hello_world_c_dbg.html b/native_client_sdk/src/examples/hello_world_c/hello_world_c_dbg.html
new file mode 100644
index 0000000..c93fb79
--- /dev/null
+++ b/native_client_sdk/src/examples/hello_world_c/hello_world_c_dbg.html
@@ -0,0 +1,126 @@
+<!DOCTYPE html>
+<html>
+ <!--
+ Copyright (c) 2011 The Native Client Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+<head>
+ <title>Hello, World!</title>
+
+ <script type="text/javascript">
+ helloWorldModule = null; // Global application object.
+ statusText = 'NO-STATUS';
+
+ // Indicate success when the NaCl module has loaded.
+ function moduleDidLoad() {
+ helloWorldModule = document.getElementById('hello_world');
+ updateStatus('SUCCESS');
+ }
+
+ // Handle a message coming from the NaCl module.
+ function handleMessage(message_event) {
+ alert(message_event.data);
+ }
+
+ // If the page loads before the Native Client module loads, then set the
+ // status message indicating that the module is still loading. Otherwise,
+ // do not change the status message.
+ function pageDidLoad() {
+ // Set the focus on the text input box. Doing this means you can press
+ // return as soon as the page loads, and it will fire the reversetText()
+ // function.
+ document.forms.helloForm.inputBox.focus();
+ if (helloWorldModule == null) {
+ updateStatus('LOADING...');
+ } else {
+ // It's possible that the Native Client module onload event fired
+ // before the page's onload event. In this case, the status message
+ // will reflect 'SUCCESS', but won't be displayed. This call will
+ // display the current message.
+ updateStatus();
+ }
+ }
+
+ function fortyTwo() {
+ helloWorldModule.postMessage('fortyTwo');
+ }
+
+ function reverseText() {
+ // Grab the text from the text box, pass it into reverseText()
+ var inputBox = document.forms.helloForm.inputBox;
+ helloWorldModule.postMessage('reverseText:' + inputBox.value);
+ // Note: a |false| return tells the <form> tag to cancel the GET action
+ // when submitting the form.
+ return false;
+ }
+
+ // Set the global status message. If the element with id 'statusField'
+ // exists, then set its HTML to the status message as well.
+ // opt_message The message test. If this is null or undefined, then
+ // attempt to set the element with id 'statusField' to the value of
+ // |statusText|.
+ function updateStatus(opt_message) {
+ if (opt_message)
+ statusText = opt_message;
+ var statusField = document.getElementById('statusField');
+ if (statusField) {
+ statusField.innerHTML = statusText;
+ }
+ }
+ </script>
+</head>
+<body onload="pageDidLoad()">
+
+<h1>Native Client Simple Module</h1>
+<p>
+ <form name="helloForm"
+ action=""
+ method="get"
+ onsubmit="return reverseText()">
+ <input type="text" id="inputBox" name="inputBox" value="Hello world" /><p>
+ <input type="button" value="Call fortyTwo()" onclick="fortyTwo()" />
+ <input type="submit" value="Call reverseText()" />
+ </form>
+ <!-- Load the published .nexe. This includes the 'src' attribute which
+ shows how to load multi-architecture modules. Each entry in the "nexes"
+ object in the .nmf manifest file is a key-value pair: the key is the runtime
+ ('x86-32', 'x86-64', etc.); the value is a URL for the desired NaCl module.
+ To load the debug versions of your .nexes, set the 'src' attribute to the
+ _dbg.nmf version of the manifest file.
+
+ Note: The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
+ and a 'message' event listener attached. This wrapping method is used
+ instead of attaching the event listeners directly to the <EMBED> element to
+ ensure that the listeners are active before the NaCl module 'load' event
+ fires. This also allows you to use PPB_Messaging.PostMessage() (in C) or
+ pp::Instance.PostMessage() (in C++) from within the initialization code in
+ your NaCl module.
+ -->
+ <div id="listener">
+ <script type="text/javascript">
+ var listener = document.getElementById('listener')
+ listener.addEventListener('load', moduleDidLoad, true);
+ listener.addEventListener('message', handleMessage, true);
+ </script>
+
+ <embed name="nacl_module"
+ id="hello_world"
+ width=0 height=0
+ src="hello_world_c_dbg.nmf"
+ type="application/x-nacl" />
+ </div>
+
+</p>
+
+<p>If the module is working correctly, a click on the "Call fortyTwo()" button
+ should open a popup dialog containing <b>42</b> as its value.</p>
+
+<p> Clicking on the "Call reverseText()" button
+ should open a popup dialog containing the textbox contents and its reverse
+ as its value.</p>
+
+<h2>Status</h2>
+<div id="statusField">NO-STATUS</div>
+</body>
+</html>
diff --git a/native_client_sdk/src/examples/httpd.cmd b/native_client_sdk/src/examples/httpd.cmd
new file mode 100644
index 0000000..7c64624
--- /dev/null
+++ b/native_client_sdk/src/examples/httpd.cmd
@@ -0,0 +1,8 @@
+@echo off
+setlocal
+
+PATH=%CYGWIN%;%PATH%
+REM Use the path to this file (httpd.cmd) to get the
+REM path to httpd.py, so that we can run httpd.cmd from
+REM any directory. Pass up to 9 arguments to httpd.py.
+python %~dp0\httpd.py %1 %2 %3 %4 %5 %6 %7 %8 %9
diff --git a/native_client_sdk/src/examples/httpd.py b/native_client_sdk/src/examples/httpd.py
new file mode 100755
index 0000000..3fa8b22
--- /dev/null
+++ b/native_client_sdk/src/examples/httpd.py
@@ -0,0 +1,125 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+
+"""A tiny web server.
+
+This is intended to be used for testing, and only run from within the examples
+directory.
+"""
+
+import BaseHTTPServer
+import logging
+import optparse
+import os
+import SimpleHTTPServer
+import SocketServer
+import sys
+import urlparse
+
+logging.getLogger().setLevel(logging.INFO)
+
+# Using 'localhost' means that we only accept connections
+# via the loop back interface.
+SERVER_PORT = 5103
+SERVER_HOST = ''
+
+# We only run from the examples or staging directory so
+# that not too much is exposed via this HTTP server. Everything in the
+# directory is served, so there should never be anything potentially sensitive
+# in the serving directory, especially if the machine might be a
+# multi-user machine and not all users are trusted. We only serve via
+# the loopback interface.
+
+SAFE_DIR_COMPONENTS = ['staging', 'examples']
+
+def SanityCheckDirectory():
+ if os.path.basename(os.getcwd()) in SAFE_DIR_COMPONENTS:
+ return
+ logging.error('For security, httpd.py should only be run from one of the')
+ logging.error('following directories: %s' % SAFE_DIR_COMPONENTS)
+ logging.error('We are currently in %s', os.getcwd())
+ sys.exit(1)
+
+
+# An HTTP server that will quit when |is_running| is set to False. We also use
+# SocketServer.ThreadingMixIn in order to handle requests asynchronously for
+# faster responses.
+class QuittableHTTPServer(SocketServer.ThreadingMixIn,
+ BaseHTTPServer.HTTPServer):
+ def serve_forever(self, timeout=0.5):
+ self.is_running = True
+ self.timeout = timeout
+ while self.is_running:
+ self.handle_request()
+
+ def shutdown(self):
+ self.is_running = False
+ return 1
+
+
+# "Safely" split a string at |sep| into a [key, value] pair. If |sep| does not
+# exist in |str|, then the entire |str| is the key and the value is set to an
+# empty string.
+def KeyValuePair(str, sep='='):
+ if sep in str:
+ return str.split(sep)
+ else:
+ return [str, '']
+
+
+# A small handler that looks for '?quit=1' query in the path and shuts itself
+# down if it finds that parameter.
+class QuittableHTTPHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
+ def do_GET(self):
+ (_, _, _, query, _) = urlparse.urlsplit(self.path)
+ url_params = dict([KeyValuePair(key_value)
+ for key_value in query.split('&')])
+ if 'quit' in url_params and '1' in url_params['quit']:
+ self.send_response(200, 'OK')
+ self.send_header('Content-type', 'text/html')
+ self.send_header('Content-length', '0')
+ self.end_headers()
+ self.server.shutdown()
+ return
+
+ SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
+
+
+def Run(server_address,
+ server_class=QuittableHTTPServer,
+ handler_class=QuittableHTTPHandler):
+ httpd = server_class(server_address, handler_class)
+ logging.info("Starting local server on port %d", server_address[1])
+ logging.info("To shut down send http://localhost:%d?quit=1",
+ server_address[1])
+ try:
+ httpd.serve_forever()
+ except KeyboardInterrupt:
+ logging.info("Received keyboard interrupt.")
+ httpd.server_close()
+
+ logging.info("Shutting down local server on port %d", server_address[1])
+
+
+if __name__ == '__main__':
+ usage_str = "usage: %prog [options] [optional_portnum]"
+ parser = optparse.OptionParser(usage=usage_str)
+ parser.add_option(
+ '--no_dir_check', dest='do_safe_check',
+ action='store_false', default=True,
+ help='Do not ensure that httpd.py is being run from a safe directory.')
+ (options, args) = parser.parse_args(sys.argv)
+ if options.do_safe_check:
+ SanityCheckDirectory()
+ if len(args) > 2:
+ print 'Too many arguments specified.'
+ parser.print_help()
+ elif len(args) == 2:
+ Run((SERVER_HOST, int(args[1])))
+ else:
+ Run((SERVER_HOST, SERVER_PORT))
+ sys.exit(0)
diff --git a/native_client_sdk/src/examples/index.html b/native_client_sdk/src/examples/index.html
new file mode 100644
index 0000000..3d72c28
--- /dev/null
+++ b/native_client_sdk/src/examples/index.html
@@ -0,0 +1,51 @@
+<!--
+ Copyright (c) 2011 The Native Client Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+-->
+
+<!DOCTYPE html PUBLIC
+ "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<style type="text/css">
+dt {
+ font-weight: bold;
+}
+dd {
+ margin-bottom: 12pt;
+ width: 600px;
+}
+</style>
+<link href="http://code.google.com/css/codesite.css" rel="stylesheet"
+ type="text/css" />
+<title>Native Client Examples</title>
+</head>
+<body>
+<h2>Native Client Examples</h2>
+<p>The examples are no longer pre-built in the SDK. To try out the Native
+Client examples right now in your Chrome web browser, please see the
+<a href="http://www.gonacl.com/dev/sdk.html">SDK page on GoNaCl.com</a> and
+download the SDK examples from the
+<a href="https://chrome.google.com/webstore/">Chrome Web Store</a>.</p>
+<p>If you would like to build and run the examples within the SDK
+then run these commands, starting from the examples directory:</p><br />
+<strong>Windows</strong>
+<blockquote><code>
+cd %NACL_SDK_ROOT%\%NACL_TARGET_PLATFORM%\examples<br />
+scons<br />
+cd %NACL_SDK_ROOT%\staging<br />
+httpd<br />
+</code></blockquote>
+<strong>Mac/Linux</strong>
+<blockquote><code>
+cd $NACL_SDK_ROOT/$NACL_TARGET_PLATFORM/examples<br />
+./scons<br />
+cd $NACL_SDK_ROOT/staging<br />
+./httpd.py<br />
+</code></blockquote>
+<p>Happy hacking!</p>
+</body>
+</html>
diff --git a/native_client_sdk/src/examples/index_staging.html b/native_client_sdk/src/examples/index_staging.html
new file mode 100644
index 0000000..c90ec1a
--- /dev/null
+++ b/native_client_sdk/src/examples/index_staging.html
@@ -0,0 +1,113 @@
+<!--
+ Copyright (c) 2011 The Native Client Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+-->
+
+<!DOCTYPE html PUBLIC
+ "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<style type="text/css">
+dt {
+ font-weight: bold;
+}
+dd {
+ margin-bottom: 12pt;
+ width: 600px;
+}
+</style>
+<link href="http://code.google.com/css/codesite.css" rel="stylesheet"
+ type="text/css" />
+<title>Native Client Examples</title>
+</head>
+<body>
+<h2>Native Client Examples</h2>
+<p>This page lists all of the examples available in the most recent Native Client SDK bundle. Each example is designed to teach a few specific Native Client programming concepts.</p>
+<dl>
+ <dt><a href="hello_world_c/hello_world_c.html">Hello World in C</a></dt>
+ <dd>The Hello World In C example demonstrates the basic structure of all Native Client applications. This example loads a Native Client module and responds to button click events by showing alert panels.
+
+ <p>Teaching focus: Basic HTML, JavaScript, and module architecture; Messaging API.</p>
+ </dd>
+ <dt><a href="hello_world/hello_world.html">Hello World in C++</a></dt>
+ <dd>The Hello World C++ example demonstrates the basic structure of all Native Client applications. This example loads a Native Client module and responds to button click events by showing alert panels.
+
+ <p>Teaching focus: Basic HTML, JavaScript, and module architecture; Messaging API.</p>
+ </dd>
+<dt><a href="load_progress/load_progress.html">Load Progress</a></dt>
+ <dd> The Load Progress example demonstrates how to listen for and handle events that occur while a
+ NaCl module loads. This example listens for different load event types and dispatches different events to their respective handler. This example also checks for valid browser
+ version and shows how to calculate and display loading progress.
+
+ <p>Teaching focus: Progress event handling.</p>
+ </dd>
+ <dt><a href="pi_generator/pi_generator.html">Pi Generator</a></dt>
+ <dd> The Pi Generator example demonstrates creating a helper thread that estimate pi using the Monte Carlo
+ method while randomly putting 1,000,000,000 points inside a 2D square that shares two
+ sides with a quarter circle.
+
+ <p>Teaching focus: Thread creation, 2D graphics, view change events.</p>
+ </dd>
+<dt><a href="input_events/input_events.html">Input Events</a></dt>
+ <dd> The Input Events example demonstrates how to handle events triggered by the user. This example allows a user
+ to interact with a square representing a module instance. Events are displayed on the screen as the user clicks, scrolls, types, inside or outside
+ of the square.
+
+ <p>Teaching focus: Keyboard and mouse input, view change, and focus events.</p>
+ </dd>
+<dt><a href="sine_synth/sine_synth.html">Sine Wave Synthesizer</a></dt>
+ <dd> The Sine Wave Synthesizer example demonstrates playing sound (a sine wave).
+
+ <p>Teaching focus: Audio.</p>
+ </dd>
+<dt><a href="pong/pong.html">Pong</a></dt>
+ <dd> The Pong example demonstrates how to create a basic 2D video game and how to store application
+ information in a local persistent file. This game uses up and
+ down arrow keyboard input events to move the paddle.
+
+ <p>Teaching focus: File I/O, 2D graphics, input events.</p>
+ </dd>
+ <dt><a href="geturl/geturl.html">Get URL</a></dt>
+ <dd> The Get URL example demonstrates fetching an URL and then displaying its contents.
+
+ <p>Teaching focus: URL loading.</p>
+ </dd>
+ <dt><a href="multithreaded_input_events/mt_input_events.html">Multi-threaded Input Events</a></dt>
+ <dd>The Multithreaded Input Events example combines HTML, Javascript,
+ and C++ (the C++ is compiled to create a .nexe file).
+ The C++ shows how to handle input events in a multi-threaded application.
+ The main thread converts input events to non-pepper events and puts them on
+ a queue. The worker thread pulls them off of the queue, converts them to a
+ string, and then uses CallOnMainThread so that PostMessage can be send the
+ result of the worker thread to the browser.
+ </dd>
+ <dt><a href="tumbler/tumbler.html">Tumbler</a></dt>
+ <dd> The Tumbler example demonstrates how to create a 3D cube that you can rotate with your mouse while pressing the
+ left mouse button. This example creates a 3D context and draws to it using
+ OpenGL ES. The JavaScript implements a virtual trackball interface to
+ map mouse movements into 3D rotations using simple 3D vector math and
+ quaternions.
+
+ <p>Teaching focus: 3D graphics</p>
+ </dd>
+ <dt><a href="fullscreen_tumbler/fullscreen_tumbler.html">Full-screen Tumbler</a></dt>
+ <dd> This is a modified version of the Tumbler example above that supports
+ full-screen display. It is in every way identical to Tumbler in
+ functionality, except that it adds the ability to switch to/from
+ full-screen display by pressing the Enter key.
+
+ <p>Teaching focus: Full-screen</p>
+ </dd>
+ <dt><a href="mouselock/mouselock.html">Mouse Locker</a></dt>
+ <dd> The Mouselock example demonstrates how to use the MouseLock API to hide
+ the mouse cursor. Mouse lock is only available in full-screen mode. You can
+ lock and unlock the mouse while in full-screen mode by pressing the Enter key.
+
+ <p>Teaching focus: Mouse lock, Full-screen</p>
+ </dd>
+</dl>
+</body>
+</html>
diff --git a/native_client_sdk/src/examples/input_events/build.scons b/native_client_sdk/src/examples/input_events/build.scons
new file mode 100644
index 0000000..3ccfc5b
--- /dev/null
+++ b/native_client_sdk/src/examples/input_events/build.scons
@@ -0,0 +1,41 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import make_nacl_env
+import nacl_utils
+import os
+
+nacl_env = make_nacl_env.NaClEnvironment(
+ use_c_plus_plus_libs=True, nacl_platform=os.getenv('NACL_TARGET_PLATFORM'),
+ install_subdir='input_events', lib_prefix='..')
+nacl_env.Append(
+ # Add a CPPPATH that enables the full-path #include directives, such as
+ # #include "examples/sine_synth/sine_synth.h"
+ CPPPATH=[os.path.dirname(os.path.dirname(os.getcwd()))],
+ # Strict ANSI compliance.
+ EXTRA_CCFLAGS=['-pedantic'],
+ )
+
+sources = ['input_events.cc']
+
+opt_nexes, dbg_nexes = nacl_env.AllNaClModules(sources, 'input_events')
+
+# This target is used by the SDK build system to provide a prebuilt version
+# of the example in the SDK installer.
+nacl_env.InstallPrebuilt('input_events')
+
+app_files = [
+ 'input_events.html',
+ 'input_events.nmf',
+ ]
+
+# Split the install of the .nexes from the other app sources so that the strip
+# action is applied to the .nexes only.
+install_nexes = nacl_env.NaClStrippedInstall(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=opt_nexes)
+install_app = nacl_env.Install(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=app_files)
+nacl_env.Alias('install', source=install_app + install_nexes)
diff --git a/native_client_sdk/src/examples/input_events/input_events.cc b/native_client_sdk/src/examples/input_events/input_events.cc
new file mode 100644
index 0000000..8ef2419
--- /dev/null
+++ b/native_client_sdk/src/examples/input_events/input_events.cc
@@ -0,0 +1,251 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// C headers
+#include <cassert>
+#include <cstdio>
+
+// C++ headers
+#include <sstream>
+#include <string>
+
+// NaCl
+#include "ppapi/cpp/input_event.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/cpp/point.h"
+#include "ppapi/cpp/var.h"
+
+namespace {
+const char* const kDidChangeView = "DidChangeView";
+const char* const kHandleInputEvent = "DidHandleInputEvent";
+const char* const kDidChangeFocus = "DidChangeFocus";
+const char* const kHaveFocus = "HaveFocus";
+const char* const kDontHaveFocus = "DontHaveFocus";
+
+// Convert a given modifier to a descriptive string. Note that the actual
+// declared type of modifier in each of the event classes is uint32_t, but it is
+// expected to be interpreted as a bitfield of 'or'ed PP_InputEvent_Modifier
+// values.
+std::string ModifierToString(uint32_t modifier) {
+ std::string s;
+ if (modifier & PP_INPUTEVENT_MODIFIER_SHIFTKEY) {
+ s += "shift ";
+ }
+ if (modifier & PP_INPUTEVENT_MODIFIER_CONTROLKEY) {
+ s += "ctrl ";
+ }
+ if (modifier & PP_INPUTEVENT_MODIFIER_ALTKEY) {
+ s += "alt ";
+ }
+ if (modifier & PP_INPUTEVENT_MODIFIER_METAKEY) {
+ s += "meta ";
+ }
+ if (modifier & PP_INPUTEVENT_MODIFIER_ISKEYPAD) {
+ s += "keypad ";
+ }
+ if (modifier & PP_INPUTEVENT_MODIFIER_ISAUTOREPEAT) {
+ s += "autorepeat ";
+ }
+ if (modifier & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) {
+ s += "left-button-down ";
+ }
+ if (modifier & PP_INPUTEVENT_MODIFIER_MIDDLEBUTTONDOWN) {
+ s += "middle-button-down ";
+ }
+ if (modifier & PP_INPUTEVENT_MODIFIER_RIGHTBUTTONDOWN) {
+ s += "right-button-down ";
+ }
+ if (modifier & PP_INPUTEVENT_MODIFIER_CAPSLOCKKEY) {
+ s += "caps-lock ";
+ }
+ if (modifier & PP_INPUTEVENT_MODIFIER_NUMLOCKKEY) {
+ s += "num-lock ";
+ }
+ return s;
+}
+
+std::string MouseButtonToString(PP_InputEvent_MouseButton button) {
+ switch (button) {
+ case PP_INPUTEVENT_MOUSEBUTTON_NONE:
+ return "None";
+ case PP_INPUTEVENT_MOUSEBUTTON_LEFT:
+ return "Left";
+ case PP_INPUTEVENT_MOUSEBUTTON_MIDDLE:
+ return "Middle";
+ case PP_INPUTEVENT_MOUSEBUTTON_RIGHT:
+ return "Right";
+ default:
+ std::ostringstream stream;
+ stream << "Unrecognized ("
+ << static_cast<int32_t>(button)
+ << ")";
+ return stream.str();
+ }
+}
+
+} // namespace
+
+class EventInstance : public pp::Instance {
+ public:
+ explicit EventInstance(PP_Instance instance)
+ : pp::Instance(instance) {
+ RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_WHEEL);
+ RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD);
+ }
+ virtual ~EventInstance() {}
+
+ /// Clicking outside of the instance's bounding box
+ /// will create a DidChangeFocus event (the NaCl instance is
+ /// out of focus). Clicking back inside the instance's
+ /// bounding box will create another DidChangeFocus event
+ /// (the NaCl instance is back in focus). The default is
+ /// that the instance is out of focus.
+ void DidChangeFocus(bool focus) {
+ PostMessage(pp::Var(kDidChangeFocus));
+ if (focus == true) {
+ PostMessage(pp::Var(kHaveFocus));
+ } else {
+ PostMessage(pp::Var(kDontHaveFocus));
+ }
+ }
+
+ /// Scrolling the mouse wheel causes a DidChangeView event.
+ void DidChangeView(const pp::Rect& position,
+ const pp::Rect& clip) {
+ PostMessage(pp::Var(kDidChangeView));
+ }
+
+ void GotKeyEvent(const pp::KeyboardInputEvent& key_event,
+ const std::string& kind) {
+ std::ostringstream stream;
+ stream << pp_instance() << ":"
+ << " Key event:" << kind
+ << " modifier:" << ModifierToString(key_event.GetModifiers())
+ << " key_code:" << key_event.GetKeyCode()
+ << " time:" << key_event.GetTimeStamp()
+ << " text:" << key_event.GetCharacterText().DebugString()
+ << "\n";
+ PostMessage(stream.str());
+ }
+
+ void GotMouseEvent(const pp::MouseInputEvent& mouse_event,
+ const std::string& kind) {
+ std::ostringstream stream;
+ stream << pp_instance() << ":"
+ << " Mouse event:" << kind
+ << " modifier:" << ModifierToString(mouse_event.GetModifiers())
+ << " button:" << MouseButtonToString(mouse_event.GetButton())
+ << " x:" << mouse_event.GetPosition().x()
+ << " y:" << mouse_event.GetPosition().y()
+ << " click_count:" << mouse_event.GetClickCount()
+ << " time:" << mouse_event.GetTimeStamp()
+ << "\n";
+ PostMessage(stream.str());
+ }
+
+ void GotWheelEvent(const pp::WheelInputEvent& wheel_event) {
+ std::ostringstream stream;
+ stream << pp_instance() << ": Wheel event."
+ << " modifier:" << ModifierToString(wheel_event.GetModifiers())
+ << " deltax:" << wheel_event.GetDelta().x()
+ << " deltay:" << wheel_event.GetDelta().y()
+ << " wheel_ticks_x:" << wheel_event.GetTicks().x()
+ << " wheel_ticks_y:"<< wheel_event.GetTicks().y()
+ << " scroll_by_page: "
+ << (wheel_event.GetScrollByPage() ? "true" : "false")
+ << "\n";
+ PostMessage(stream.str());
+ }
+
+ // Handle an incoming input event by switching on type and dispatching
+ // to the appropriate subtype handler.
+ //
+ // HandleInputEvent operates on the main Pepper thread. In large
+ // real-world applications, you'll want to create a separate thread
+ // that puts events in a queue and handles them independant of the main
+ // thread so as not to slow down the browser. There is an additional
+ // version of this example in the examples directory that demonstrates
+ // this best practice.
+ virtual bool HandleInputEvent(const pp::InputEvent& event) {
+ PostMessage(pp::Var(kHandleInputEvent));
+ switch (event.GetType()) {
+ case PP_INPUTEVENT_TYPE_UNDEFINED:
+ break;
+ case PP_INPUTEVENT_TYPE_MOUSEDOWN:
+ GotMouseEvent(pp::MouseInputEvent(event), "Down");
+ break;
+ case PP_INPUTEVENT_TYPE_MOUSEUP:
+ GotMouseEvent(pp::MouseInputEvent(event), "Up");
+ break;
+ case PP_INPUTEVENT_TYPE_MOUSEMOVE:
+ GotMouseEvent(pp::MouseInputEvent(event), "Move");
+ break;
+ case PP_INPUTEVENT_TYPE_MOUSEENTER:
+ GotMouseEvent(pp::MouseInputEvent(event), "Enter");
+ break;
+ case PP_INPUTEVENT_TYPE_MOUSELEAVE:
+ GotMouseEvent(pp::MouseInputEvent(event), "Leave");
+ break;
+ case PP_INPUTEVENT_TYPE_WHEEL:
+ GotWheelEvent(pp::WheelInputEvent(event));
+ break;
+ case PP_INPUTEVENT_TYPE_RAWKEYDOWN:
+ GotKeyEvent(pp::KeyboardInputEvent(event), "RawKeyDown");
+ break;
+ case PP_INPUTEVENT_TYPE_KEYDOWN:
+ GotKeyEvent(pp::KeyboardInputEvent(event), "Down");
+ break;
+ case PP_INPUTEVENT_TYPE_KEYUP:
+ GotKeyEvent(pp::KeyboardInputEvent(event), "Up");
+ break;
+ case PP_INPUTEVENT_TYPE_CHAR:
+ GotKeyEvent(pp::KeyboardInputEvent(event), "Character");
+ break;
+ case PP_INPUTEVENT_TYPE_CONTEXTMENU:
+ GotKeyEvent(pp::KeyboardInputEvent(event), "Context");
+ break;
+ // Note that if we receive an IME event we just send a message back
+ // to the browser to indicate we have received it.
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START:
+ PostMessage(pp::Var("PP_INPUTEVENT_TYPE_IME_COMPOSITION_START"));
+ break;
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE:
+ PostMessage(pp::Var("PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE"));
+ break;
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END:
+ PostMessage(pp::Var("PP_INPUTEVENT_TYPE_IME_COMPOSITION_END"));
+ break;
+ case PP_INPUTEVENT_TYPE_IME_TEXT:
+ PostMessage(pp::Var("PP_INPUTEVENT_TYPE_IME_COMPOSITION_TEXT"));
+ break;
+ default:
+ assert(false);
+ return false;
+ }
+ return true;
+ }
+};
+
+// The EventModule provides an implementation of pp::Module that creates
+// EventInstance objects when invoked. This is part of the glue code that makes
+// our example accessible to ppapi.
+class EventModule : public pp::Module {
+ public:
+ EventModule() : pp::Module() {}
+ virtual ~EventModule() {}
+
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new EventInstance(instance);
+ }
+};
+
+// Implement the required pp::CreateModule function that creates our specific
+// kind of Module (in this case, EventModule). This is part of the glue code
+// that makes our example accessible to ppapi.
+namespace pp {
+ Module* CreateModule() {
+ return new EventModule();
+ }
+}
diff --git a/native_client_sdk/src/examples/input_events/input_events.html b/native_client_sdk/src/examples/input_events/input_events.html
new file mode 100644
index 0000000..3f1695d
--- /dev/null
+++ b/native_client_sdk/src/examples/input_events/input_events.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <!--
+ Copyright (c) 2011 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+<head>
+ <title>Input Events</title>
+
+ <script type="text/javascript">
+ var kMaxArraySize = 20;
+ var messageArray = new Array();
+
+ function $(id) {
+ return document.getElementById(id);
+ }
+
+ function receiveMessage(message) {
+ // Show last |kMaxArraySize| events in html.
+ messageArray.push(message.data);
+ if (messageArray.length > kMaxArraySize) {
+ messageArray.shift();
+ }
+ var newData = messageArray.join('<BR>');
+ document.getElementById('eventString').innerHTML = newData;
+ // Print event to console.
+ console.log(message.data);
+ }
+ </script>
+</head>
+<body>
+<h1>InputEvent Handling Example</h1>
+ <div id="listener">
+ <script type="text/javascript">
+ $('listener').addEventListener('message', receiveMessage, true);
+ </script>
+
+ <embed name="nacl_module"
+ id="event_module"
+ width=400 height=400
+ src="input_events.nmf"
+ type="application/x-nacl"
+ style="background-color:gray" />
+ </div>
+<p>
+This example demonstrates handling of input events in PPAPI.</p>
+<p>
+Each time an input event happens in the context of the gray box,
+the embedded NaCl module posts a message describing the event
+back to JavaScript, which prints a message to the JavaScript
+console in Chrome and to a string on the page.</p>
+<h2>Events</h2>
+<pre>
+<p><b id='eventString'>None</b></p>
+</pre>
+</body>
+</html>
diff --git a/native_client_sdk/src/examples/load_progress/build.scons b/native_client_sdk/src/examples/load_progress/build.scons
new file mode 100644
index 0000000..1c8a6a6
--- /dev/null
+++ b/native_client_sdk/src/examples/load_progress/build.scons
@@ -0,0 +1,51 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import make_nacl_env
+import os
+
+nacl_env = make_nacl_env.NaClEnvironment(
+ use_c_plus_plus_libs=True, nacl_platform=os.getenv('NACL_TARGET_PLATFORM'),
+ install_subdir='load_progress', lib_prefix='..')
+nacl_env.Append(
+ # Add a CPPPATH that enables the full-path #include directives, such as
+ # #include "examples/sine_synth/sine_synth.h"
+ CPPPATH=[os.path.dirname(os.path.dirname(os.getcwd()))],
+ # Strict ANSI compliance.
+ CCFLAGS=['-pedantic', '-Werror'],
+ )
+
+sources = ['load_progress.cc']
+
+opt_nexes, dbg_nexes = nacl_env.AllNaClModules(sources, 'load_progress')
+
+# This target is used by the SDK build system to provide a prebuilt version
+# of the example in the SDK installer.
+nacl_env.InstallPrebuilt('load_progress')
+
+common_files = [
+ 'check_browser.js',
+ ]
+common_files = [
+ os.path.join(os.path.dirname(os.getcwd()), 'common', common_file)
+ for common_file in common_files]
+
+app_files = [
+ 'load_progress.html',
+ 'load_progress.nmf',
+ ]
+
+# Split the install of the .nexes from the other app sources so that the strip
+# action is applied to the .nexes only.
+install_nexes = nacl_env.NaClStrippedInstall(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=opt_nexes)
+install_app = nacl_env.Install(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=app_files)
+common_dir = os.path.join(os.path.dirname(nacl_env['NACL_INSTALL_ROOT']),
+ 'common')
+install_common = nacl_env.Install(dir=common_dir, source=common_files)
+nacl_env.Alias('install',
+ source=[install_app, install_common, install_nexes])
diff --git a/native_client_sdk/src/examples/load_progress/load_progress.cc b/native_client_sdk/src/examples/load_progress/load_progress.cc
new file mode 100644
index 0000000..07ce440
--- /dev/null
+++ b/native_client_sdk/src/examples/load_progress/load_progress.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/// @file
+/// This example demonstrates loading and running a very simple NaCl
+/// module. To load the NaCl module, the browser first looks for the
+/// CreateModule() factory method (at the end of this file). It calls
+/// CreateModule() once to load the module code from your .nexe. After the
+/// .nexe code is loaded, CreateModule() is not called again.
+///
+/// Once the .nexe code is loaded, the browser then calls the
+/// LoadProgressModule::CreateInstance()
+/// method on the object returned by CreateModule(). It calls CreateInstance()
+/// each time it encounters an <embed> tag that references your NaCl module.
+
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/cpp/var.h"
+
+namespace load_progress {
+/// The Instance class. One of these exists for each instance of your NaCl
+/// module on the web page. The browser will ask the Module object to create
+/// a new Instance for each occurrence of the <embed> tag that has these
+/// attributes:
+/// <pre>
+/// type="application/x-nacl"
+/// nacl="hello_world.nmf"
+/// </pre>
+class LoadProgressInstance : public pp::Instance {
+ public:
+ explicit LoadProgressInstance(PP_Instance instance)
+ : pp::Instance(instance) {}
+ virtual ~LoadProgressInstance() {}
+};
+
+/// The Module class. The browser calls the CreateInstance() method to create
+/// an instance of your NaCl module on the web page. The browser creates a new
+/// instance for each <embed> tag with
+/// <code>type="application/x-nacl"</code>.
+class LoadProgressModule : public pp::Module {
+ public:
+ LoadProgressModule() : pp::Module() {}
+ virtual ~LoadProgressModule() {}
+
+ /// Create and return a HelloWorldInstance object.
+ /// @param[in] instance a handle to a plug-in instance.
+ /// @return a newly created HelloWorldInstance.
+ /// @note The browser is responsible for calling @a delete when done.
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new LoadProgressInstance(instance);
+ }
+};
+} // namespace load_progress
+
+
+namespace pp {
+/// Factory function called by the browser when the module is first loaded.
+/// The browser keeps a singleton of this module. It calls the
+/// CreateInstance() method on the object you return to make instances. There
+/// is one instance per <embed> tag on the page. This is the main binding
+/// point for your NaCl module with the browser.
+/// @return new LoadProgressModule.
+/// @note The browser is responsible for deleting returned @a Module.
+Module* CreateModule() {
+ return new load_progress::LoadProgressModule();
+}
+} // namespace pp
+
diff --git a/native_client_sdk/src/examples/load_progress/load_progress.html b/native_client_sdk/src/examples/load_progress/load_progress.html
new file mode 100644
index 0000000..4c2dc47
--- /dev/null
+++ b/native_client_sdk/src/examples/load_progress/load_progress.html
@@ -0,0 +1,232 @@
+<!DOCTYPE html>
+<html>
+ <!--
+ Copyright (c) 2011 The Native Client Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+<head>
+ <title>Load Progress Example</title>
+ <script type="text/javascript" src="../common/check_browser.js"></script>
+ <script type="text/javascript">
+ // Check for Native Client support in the browser before the DOM loads.
+ var isValidBrowser = false;
+ var browserSupportStatus = 0;
+ var checker = new browser_version.BrowserChecker(
+ 14, // Minumum Chrome version.
+ navigator["appVersion"],
+ navigator["plugins"]);
+ checker.checkBrowser();
+
+ loadProgressModule = null; // Global application object.
+ statusText = 'NO-STATUS';
+
+ isValidBrowser = checker.getIsValidBrowser();
+ browserSupportStatus = checker.getBrowserSupportStatus();
+
+ // Handler that gets called when the NaCl module starts loading. This
+ // event is always triggered when an <EMBED> tag has a MIME type of
+ // application/x-nacl.
+ function moduleDidStartLoad() {
+ appendToEventLog('loadstart');
+ }
+
+ // Progress event handler. |event| contains a couple of interesting
+ // properties that are used in this example:
+ // total The size of the NaCl module in bytes. Note that this value
+ // is 0 until |lengthComputable| is true. In particular, this
+ // value is 0 for the first 'progress' event.
+ // loaded The number of bytes loaded so far.
+ // lengthComputable A boolean indicating that the |total| field
+ // represents a valid length.
+ //
+ // event The ProgressEvent that triggered this handler.
+ function moduleLoadProgress(event) {
+ var loadPercent = 0.0;
+ var loadPercentString;
+ if (event.lengthComputable && event.total > 0) {
+ loadPercent = event.loaded / event.total * 100.0;
+ loadPercentString = loadPercent + '%';
+ } else {
+ // The total length is not yet known.
+ loadPercent = -1.0;
+ loadPercentString = 'Computing...';
+ }
+ appendToEventLog('progress: ' + loadPercentString +
+ ' (' + event.loaded + ' of ' + event.total + ' bytes)');
+ }
+
+ // Handler that gets called if an error occurred while loading the NaCl
+ // module. Note that the event does not carry any meaningful data about
+ // the error, you have to check lastError on the <EMBED> element to find
+ // out what happened.
+ function moduleLoadError() {
+ appendToEventLog('error: ' + loadProgressModule.lastError);
+ }
+
+ // Handler that gets called if the NaCl module load is aborted.
+ function moduleLoadAbort() {
+ appendToEventLog('abort');
+ }
+
+ // When the NaCl module has loaded indicate success.
+ function moduleDidLoad() {
+ loadProgressModule = document.getElementById('load_progress');
+ appendToEventLog('load');
+ updateStatus('SUCCESS');
+ }
+
+ // Handler that gets called when the NaCl module loading has completed.
+ // You will always get one of these events, regardless of whether the NaCl
+ // module loaded successfully or not. For example, if there is an error
+ // during load, you will get an 'error' event and a 'loadend' event. Note
+ // that if the NaCl module loads successfully, you will get both a 'load'
+ // event and a 'loadend' event.
+ function moduleDidEndLoad() {
+ appendToEventLog('loadend');
+ var lastError = event.target.lastError;
+ if (lastError == undefined || lastError.length == 0) {
+ lastError = '&lt;none&gt;';
+ }
+ appendToEventLog('lastError: ' + lastError);
+ }
+
+
+ // Handle a message coming from the NaCl module.
+ function handleMessage(message_event) {
+ alert(message_event.data);
+ }
+
+ // Set the global status message. Updates the 'status_field' element with
+ // the new text.
+ // opt_message The message text. If this is null or undefined, then
+ // attempt to set the element with id 'status_field' to the value of
+ // |statusText|.
+ function updateStatus(opt_message) {
+ if (opt_message)
+ statusText = opt_message;
+ var statusField = document.getElementById('status_field');
+ if (statusField) {
+ statusField.innerHTML = statusText;
+ }
+ }
+
+ // Append an event name to the 'event_log_field' element. Event names
+ // are separated by a <br> tag so they get listed one per line.
+ // logMessage The message to append to the log.
+ function appendToEventLog(logMessage) {
+ var eventLogField = document.getElementById('event_log_field');
+ if (eventLogField.innerHTML.length == 0) {
+ eventLogField.innerHTML = logMessage;
+ } else {
+ eventLogField.innerHTML = eventLogField.innerHTML +
+ '<br />' +
+ logMessage;
+ }
+ }
+ </script>
+</head>
+<body>
+
+<h1>Native Client Load Event Example</h1>
+
+<h2>Event Log</h2>
+<div id="event_log_field"></div>
+<h2>Status</h2>
+<div id="status_field">NO-STATUS</div>
+
+<div id="listener">
+ <script type="text/javascript">
+ var listener = document.getElementById('listener');
+ listener.addEventListener('loadstart', moduleDidStartLoad, true);
+ listener.addEventListener('progress', moduleLoadProgress, true);
+ listener.addEventListener('error', moduleLoadError, true);
+ listener.addEventListener('abort', moduleLoadAbort, true);
+ listener.addEventListener('load', moduleDidLoad, true);
+ listener.addEventListener('loadend', moduleDidEndLoad, true);
+ listener.addEventListener('message', handleMessage, true);
+
+ switch (browserSupportStatus) {
+ case browser_version.BrowserChecker.StatusValues.NACL_ENABLED:
+ appendToEventLog('Native Client plugin enabled.');
+ break;
+ case browser_version.BrowserChecker.StatusValues.UNKNOWN_BROWSER:
+ updateStatus('UNKNOWN BROWSER');
+ break;
+ case browser_version.BrowserChecker.StatusValues.CHROME_VERSION_TOO_OLD:
+ appendToEventLog(
+ 'Chrome too old: You must use Chrome version 14 or later.');
+ updateStatus('NEED CHROME 14 OR LATER');
+ break;
+ case browser_version.BrowserChecker.StatusValues.NACL_NOT_ENABLED:
+ appendToEventLog(
+ 'NaCl disabled: Native Client is not enabled.<br>' +
+ 'Please go to <b>chrome://plugins</b> and enable Native Client ' +
+ 'plugin.');
+ updateStatus('NaCl NOT ENABLED');
+ break;
+ case browser_version.BrowserChecker.StatusValues.NOT_USING_SERVER:
+ appendToEventLog(
+ 'file: URL detected, please use a web server to host Native ' +
+ 'Client applications.');
+ updateStatus('NaCl NOT ENABLED');
+ default:
+ appendToEventLog('Unknown error: Unable to detect browser and/or ' +
+ 'Native Client support.');
+ updateStatus('UNKNOWN ERROR');
+ break;
+ }
+ </script>
+
+ <!-- Load the published .nexe. This includes the 'src' attribute which
+ shows how to load multi-architecture modules. Each entry in the "nexes"
+ object in the .nmf manifest file is a key-value pair: the key is the runtime
+ ('x86-32', 'x86-64', etc.); the value is a URL for the desired NaCl module.
+ To load the debug versions of your .nexes, set the 'src' attribute to the
+ _dbg.nmf version of the manifest file.
+
+ Note: The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
+ and a 'message' event listener attached. This wrapping method is used
+ instead of attaching the event listeners directly to the <EMBED> element to
+ ensure that the listeners are active before the NaCl module 'load' event
+ fires. This also allows you to use PPB_Messaging.PostMessage() (in C) or
+ pp::Instance.PostMessage() (in C++) from within the initialization code in
+ your NaCl module.
+ -->
+ <embed name="nacl_module"
+ id="load_progress"
+ width=0 height=0
+ src="load_progress.nmf"
+ type="application/x-nacl" />
+
+ <script type="text/javascript">
+ loadProgressModule = document.getElementById('load_progress');
+ // Futher diagnose NaCl loading.
+ if (loadProgressModule == null ||
+ typeof loadProgressModule.readyState == 'undefined') {
+ switch (browserSupportStatus) {
+ case browser_version.BrowserChecker.StatusValues.NACL_ENABLED:
+ // The NaCl plugin is enabled and running, it's likely that the flag
+ // isn't set.
+ appendToEventLog(
+ 'NaCl flag disabled: The Native Client flag is not enabled.<br>' +
+ 'Please go to <b>chrome://flags</b> enable Native Client and ' +
+ 'relaunch your browser. See also: ' +
+ '<a href="http://code.google.com/chrome/nativeclient/docs/' +
+ 'running.html">Running Web Applications that Use Native Client' +
+ '</a>');
+ updateStatus('NaCl NOT ENABLED');
+ break;
+ case browser_version.BrowserChecker.StatusValues.UNKNOWN_BROWSER:
+ appendToEventLog('Native Client applications are not supported by ' +
+ 'this browser.');
+ break;
+ default:
+ appendToEventLog('Unknown error when loading Native Client ' +
+ 'application.');
+ }
+ }
+ </script>
+</div>
+</body>
+</html>
diff --git a/native_client_sdk/src/examples/mouselock/build.scons b/native_client_sdk/src/examples/mouselock/build.scons
new file mode 100644
index 0000000..f56186e
--- /dev/null
+++ b/native_client_sdk/src/examples/mouselock/build.scons
@@ -0,0 +1,51 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import make_nacl_env
+import os
+
+nacl_env = make_nacl_env.NaClEnvironment(
+ use_c_plus_plus_libs=True, nacl_platform=os.getenv('NACL_TARGET_PLATFORM'),
+ install_subdir='mouselock', lib_prefix='..')
+nacl_env.Append(
+ # Add a CPPPATH that enables the full-path #include directives, such as
+ # #include "examples/sine_synth/sine_synth.h"
+ CPPPATH=[os.path.dirname(os.path.dirname(os.getcwd()))],
+ # Strict ANSI compliance.
+ CCFLAGS=['-pedantic', '-Werror'],
+ )
+
+sources = ['mouselock.cc']
+
+opt_nexes, dbg_nexes = nacl_env.AllNaClModules(sources, 'mouselock')
+
+# This target is used by the SDK build system to provide a prebuilt version
+# of the example in the SDK installer.
+nacl_env.InstallPrebuilt('mouselock')
+
+common_files = [
+ 'check_browser.js',
+ ]
+common_files = [
+ os.path.join(os.path.dirname(os.getcwd()), 'common', common_file)
+ for common_file in common_files]
+
+app_files = [
+ 'mouselock.html',
+ 'mouselock.nmf',
+ ]
+
+# Split the install of the .nexes from the other app sources so that the strip
+# action is applied to the .nexes only.
+install_nexes = nacl_env.NaClStrippedInstall(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=opt_nexes)
+install_app = nacl_env.Install(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=app_files)
+common_dir = os.path.join(os.path.dirname(nacl_env['NACL_INSTALL_ROOT']),
+ 'common')
+install_common = nacl_env.Install(dir=common_dir, source=common_files)
+nacl_env.Alias('install',
+ source=[install_app, install_common, install_nexes])
diff --git a/native_client_sdk/src/examples/mouselock/mouselock.cc b/native_client_sdk/src/examples/mouselock/mouselock.cc
new file mode 100644
index 0000000..b0e18fd
--- /dev/null
+++ b/native_client_sdk/src/examples/mouselock/mouselock.cc
@@ -0,0 +1,329 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/mouselock/mouselock.h"
+
+#include <cmath>
+#include <cstdlib>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+// Indicate the direction of the mouse location relative to the center of the
+// view. These values are used to determine which 2D quadrant the needle lies
+// in.
+typedef enum {
+ kLeft = 0,
+ kRight = 1,
+ kUp = 2,
+ kDown = 3
+} MouseDirection;
+
+namespace {
+const int kCentralSpotRadius = 5;
+const uint32_t kReturnKeyCode = 13;
+const uint32_t kBackgroundColor = 0xff606060;
+const uint32_t kLockedForegroundColor = 0xfff08080;
+const uint32_t kUnlockedForegroundColor = 0xff80f080;
+} // namespace
+
+namespace mouselock {
+
+MouseLockInstance::~MouseLockInstance() {
+ free(background_scanline_);
+ background_scanline_ = NULL;
+}
+
+bool MouseLockInstance::Init(uint32_t argc,
+ const char* argn[],
+ const char* argv[]) {
+ RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE |
+ PP_INPUTEVENT_CLASS_KEYBOARD);
+ return true;
+}
+
+bool MouseLockInstance::HandleInputEvent(const pp::InputEvent& event) {
+ switch (event.GetType()) {
+ case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
+ is_context_bound_ = false;
+ if (fullscreen_.IsFullscreen()) {
+ // Leaving fullscreen mode also unlocks the mouse if it was locked.
+ // In this case, the browser will call MouseLockLost() on this
+ // instance.
+ if (!fullscreen_.SetFullscreen(false)) {
+ Log("Could not leave fullscreen mode\n");
+ }
+ } else {
+ if (!fullscreen_.SetFullscreen(true)) {
+ Log("Could not set fullscreen mode\n");
+ } else {
+ pp::MouseInputEvent mouse_event(event);
+ if (mouse_event.GetButton() == PP_INPUTEVENT_MOUSEBUTTON_LEFT &&
+ !mouse_locked_) {
+ LockMouse(callback_factory_.NewRequiredCallback(
+ &MouseLockInstance::DidLockMouse));
+ }
+ }
+ }
+ return true;
+ }
+
+ case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
+ pp::MouseInputEvent mouse_event(event);
+ mouse_movement_ = mouse_event.GetMovement();
+ Paint();
+ return true;
+ }
+
+ case PP_INPUTEVENT_TYPE_KEYDOWN: {
+ pp::KeyboardInputEvent key_event(event);
+ // Lock the mouse when the Enter key is pressed.
+ if (key_event.GetKeyCode() == kReturnKeyCode) {
+ if (mouse_locked_) {
+ UnlockMouse();
+ } else {
+ LockMouse(callback_factory_.NewRequiredCallback(
+ &MouseLockInstance::DidLockMouse));
+ }
+ }
+ return true;
+ }
+
+ case PP_INPUTEVENT_TYPE_MOUSEUP:
+ case PP_INPUTEVENT_TYPE_MOUSEENTER:
+ case PP_INPUTEVENT_TYPE_MOUSELEAVE:
+ case PP_INPUTEVENT_TYPE_WHEEL:
+ case PP_INPUTEVENT_TYPE_RAWKEYDOWN:
+ case PP_INPUTEVENT_TYPE_KEYUP:
+ case PP_INPUTEVENT_TYPE_CHAR:
+ case PP_INPUTEVENT_TYPE_CONTEXTMENU:
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START:
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE:
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END:
+ case PP_INPUTEVENT_TYPE_IME_TEXT:
+ case PP_INPUTEVENT_TYPE_UNDEFINED:
+ default:
+ return false;
+ }
+}
+
+void MouseLockInstance::DidChangeView(const pp::Rect& position,
+ const pp::Rect& clip) {
+ int width = position.size().width();
+ int height = position.size().height();
+
+ // When entering into full-screen mode, DidChangeView() gets called twice.
+ // The first time, any 2D context will fail to bind to this pp::Instacne.
+ if (width == width_ && height == height_ && is_context_bound_) {
+ return;
+ }
+ width_ = width;
+ height_ = height;
+
+ device_context_ = pp::Graphics2D(this, pp::Size(width_, height_), false);
+ waiting_for_flush_completion_ = false;
+ free(background_scanline_);
+ background_scanline_ = NULL;
+ is_context_bound_ = BindGraphics(device_context_);
+ if (!is_context_bound_) {
+ Log("Could not bind to 2D context\n");
+ return;
+ }
+ background_scanline_ = static_cast<uint32_t*>(
+ malloc(width_ * sizeof(*background_scanline_)));
+ uint32_t* bg_pixel = background_scanline_;
+ for (int x = 0; x < width_; ++x) {
+ *bg_pixel++ = kBackgroundColor;
+ }
+ Paint();
+}
+
+void MouseLockInstance::MouseLockLost() {
+ if (mouse_locked_) {
+ mouse_locked_ = false;
+ Paint();
+ } else {
+ PP_NOTREACHED();
+ }
+}
+
+void MouseLockInstance::DidLockMouse(int32_t result) {
+ mouse_locked_ = result == PP_OK;
+ mouse_movement_.set_x(0);
+ mouse_movement_.set_y(0);
+ Paint();
+}
+
+void MouseLockInstance::DidFlush(int32_t result) {
+ waiting_for_flush_completion_ = false;
+}
+
+void MouseLockInstance::Paint() {
+ if (waiting_for_flush_completion_) {
+ return;
+ }
+ pp::ImageData image = PaintImage(width_, height_);
+ if (image.is_null()) {
+ Log("Could not create image data\n");
+ return;
+ }
+ device_context_.ReplaceContents(&image);
+ waiting_for_flush_completion_ = true;
+ device_context_.Flush(
+ callback_factory_.NewRequiredCallback(&MouseLockInstance::DidFlush));
+}
+
+pp::ImageData MouseLockInstance::PaintImage(int width, int height) {
+ pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
+ pp::Size(width, height), false);
+ if (image.is_null() || image.data() == NULL)
+ return image;
+
+ ClearToBackground(&image);
+ uint32_t foreground_color = mouse_locked_ ? kLockedForegroundColor :
+ kUnlockedForegroundColor;
+ DrawCenterSpot(&image, foreground_color);
+ DrawNeedle(&image, foreground_color);
+ return image;
+}
+
+void MouseLockInstance::ClearToBackground(pp::ImageData* image) {
+ if (image == NULL) {
+ Log("ClearToBackground with NULL image");
+ return;
+ }
+ if (background_scanline_ == NULL)
+ return;
+ int image_height = image->size().height();
+ int image_width = image->size().width();
+ for (int y = 0; y < image_height; ++y) {
+ uint32_t* scanline = image->GetAddr32(pp::Point(0, y));
+ memcpy(scanline,
+ background_scanline_,
+ image_width * sizeof(*background_scanline_));
+ }
+}
+
+void MouseLockInstance::DrawCenterSpot(pp::ImageData* image,
+ uint32_t spot_color) {
+ if (image == NULL) {
+ Log("DrawCenterSpot with NULL image");
+ return;
+ }
+ // Draw the center spot. The ROI is bounded by the size of the spot, plus
+ // one pixel.
+ int center_x = image->size().width() / 2;
+ int center_y = image->size().height() / 2;
+ int region_of_interest_radius = kCentralSpotRadius + 1;
+
+ for (int y = center_y - region_of_interest_radius;
+ y < center_y + region_of_interest_radius;
+ ++y) {
+ for (int x = center_x - region_of_interest_radius;
+ x < center_x + region_of_interest_radius;
+ ++x) {
+ if (GetDistance(x, y, center_x, center_y) < kCentralSpotRadius) {
+ *image->GetAddr32(pp::Point(x, y)) = spot_color;
+ }
+ }
+ }
+}
+
+void MouseLockInstance::DrawNeedle(pp::ImageData* image,
+ uint32_t needle_color) {
+ if (image == NULL) {
+ Log("DrawNeedle with NULL image");
+ return;
+ }
+ if (GetDistance(mouse_movement_.x(), mouse_movement_.y(), 0, 0) <=
+ kCentralSpotRadius) {
+ return;
+ }
+
+ int abs_mouse_x = std::abs(mouse_movement_.x());
+ int abs_mouse_y = std::abs(mouse_movement_.y());
+ int center_x = image->size().width() / 2;
+ int center_y = image->size().height() / 2;
+ pp::Point vertex(mouse_movement_.x() + center_x,
+ mouse_movement_.y() + center_y);
+ pp::Point anchor_1;
+ pp::Point anchor_2;
+ MouseDirection direction = kLeft;
+
+ if (abs_mouse_x >= abs_mouse_y) {
+ anchor_1.set_x(center_x);
+ anchor_1.set_y(center_y - kCentralSpotRadius);
+ anchor_2.set_x(center_x);
+ anchor_2.set_y(center_y + kCentralSpotRadius);
+ direction = (mouse_movement_.x() < 0) ? kLeft : kRight;
+ if (direction == kLeft)
+ anchor_1.swap(anchor_2);
+ } else {
+ anchor_1.set_x(center_x + kCentralSpotRadius);
+ anchor_1.set_y(center_y);
+ anchor_2.set_x(center_x - kCentralSpotRadius);
+ anchor_2.set_y(center_y);
+ direction = (mouse_movement_.y() < 0) ? kUp : kDown;
+ if (direction == kUp)
+ anchor_1.swap(anchor_2);
+ }
+
+ for (int y = center_y - abs_mouse_y; y < center_y + abs_mouse_y; ++y) {
+ for (int x = center_x - abs_mouse_x; x < center_x + abs_mouse_x; ++x) {
+ bool within_bound_1 =
+ ((y - anchor_1.y()) * (vertex.x() - anchor_1.x())) >
+ ((vertex.y() - anchor_1.y()) * (x - anchor_1.x()));
+ bool within_bound_2 =
+ ((y - anchor_2.y()) * (vertex.x() - anchor_2.x())) <
+ ((vertex.y() - anchor_2.y()) * (x - anchor_2.x()));
+ bool within_bound_3 =
+ (direction == kUp && y < center_y) ||
+ (direction == kDown && y > center_y) ||
+ (direction == kLeft && x < center_x) ||
+ (direction == kRight && x > center_x);
+
+ if (within_bound_1 && within_bound_2 && within_bound_3) {
+ *image->GetAddr32(pp::Point(x, y)) = needle_color;
+ }
+ }
+ }
+}
+
+
+void MouseLockInstance::Log(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ char buf[512];
+ vsnprintf(buf, sizeof(buf) - 1, format, args);
+ buf[sizeof(buf) - 1] = '\0';
+ va_end(args);
+
+ pp::Var value(buf);
+ PostMessage(value);
+}
+
+} // namespace mouselock
+
+// This object is the global object representing this plugin library as long
+// as it is loaded.
+class MouseLockModule : public pp::Module {
+ public:
+ MouseLockModule() : pp::Module() {}
+ virtual ~MouseLockModule() {}
+
+ // Override CreateInstance to create your customized Instance object.
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new mouselock::MouseLockInstance(instance);
+ }
+};
+
+namespace pp {
+
+// Factory function for your specialization of the Module object.
+Module* CreateModule() {
+ return new MouseLockModule();
+}
+
+} // namespace pp
+
diff --git a/native_client_sdk/src/examples/mouselock/mouselock.h b/native_client_sdk/src/examples/mouselock/mouselock.h
new file mode 100644
index 0000000..88650d6
--- /dev/null
+++ b/native_client_sdk/src/examples/mouselock/mouselock.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cmath>
+
+#include "ppapi/c/ppb_fullscreen.h"
+#include "ppapi/c/ppb_input_event.h"
+#include "ppapi/cpp/completion_callback.h"
+#include "ppapi/cpp/fullscreen.h"
+#include "ppapi/cpp/mouse_lock.h"
+#include "ppapi/cpp/graphics_2d.h"
+#include "ppapi/cpp/image_data.h"
+#include "ppapi/cpp/input_event.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/cpp/rect.h"
+#include "ppapi/cpp/var.h"
+
+namespace mouselock {
+
+class MouseLockInstance : public pp::Instance, public pp::MouseLock {
+ public:
+ explicit MouseLockInstance(PP_Instance instance)
+ : pp::Instance(instance),
+ pp::MouseLock(this),
+ width_(0),
+ height_(0),
+ mouse_locked_(false),
+ waiting_for_flush_completion_(false),
+ callback_factory_(this),
+ fullscreen_(this),
+ is_context_bound_(false),
+ background_scanline_(NULL) {
+ }
+ virtual ~MouseLockInstance();
+
+ // Called by the browser when the NaCl module is loaded and all ready to go.
+ virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]);
+
+ // Called by the browser to handle incoming input events.
+ virtual bool HandleInputEvent(const pp::InputEvent& event);
+
+ // Called whenever the in-browser window changes size.
+ virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip);
+
+ // Called by the browser when mouselock is lost. This happens when the NaCl
+ // module exits fullscreen mode.
+ virtual void MouseLockLost();
+
+ private:
+ // Return the Cartesian distance between two points.
+ double GetDistance(int point_1_x, int point_1_y,
+ int point_2_x, int point_2_y) {
+ return sqrt(pow(static_cast<double>(point_1_x - point_2_x), 2) +
+ pow(static_cast<double>(point_1_y - point_2_y), 2));
+ }
+
+ // Called when mouse lock has been acquired. Used as a callback to
+ // pp::MouseLock.LockMouse().
+ void DidLockMouse(int32_t result);
+
+ // Called when the 2D context has been flushed to the browser window. Used
+ // as a callback to pp::Graphics2D.Flush().
+ void DidFlush(int32_t result);
+
+ // Creates a new paint buffer, paints it then flush it to the 2D context. If
+ // a flush is pending, this does nothing.
+ void Paint();
+
+ // Create a new pp::ImageData and paint the graphics that represent the mouse
+ // movement in it. Return the new pp::ImageData.
+ pp::ImageData PaintImage(int width, int height);
+
+ // Fill the image with the backgroud color.
+ void ClearToBackground(pp::ImageData* image);
+
+ // Draw a spot in |spot_color| in the center of the image. The radius of the
+ // spot is defined by a constant value in mouselock.cc
+ void DrawCenterSpot(pp::ImageData* image, uint32_t spot_color);
+
+ // Draw the needle when the mouse is outside of the central spot.
+ void DrawNeedle(pp::ImageData* image, uint32_t needle_color);
+
+ // Print the printf-style format to the "console" via PostMessage.
+ void Log(const char* format, ...);
+
+ int width_;
+ int height_;
+
+ bool mouse_locked_;
+ pp::Point mouse_movement_;
+ bool waiting_for_flush_completion_;
+ pp::CompletionCallbackFactory<MouseLockInstance> callback_factory_;
+
+ pp::Fullscreen fullscreen_;
+ pp::Graphics2D device_context_;
+ bool is_context_bound_;
+ uint32_t* background_scanline_;
+};
+
+} // namespace mouselock
diff --git a/native_client_sdk/src/examples/mouselock/mouselock.html b/native_client_sdk/src/examples/mouselock/mouselock.html
new file mode 100644
index 0000000..eaa44dc
--- /dev/null
+++ b/native_client_sdk/src/examples/mouselock/mouselock.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+<!--
+Copyright (c) 2011 The Native Client Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<head>
+ <meta http-equiv="Pragma" content="no-cache" />
+ <meta http-equiv="Expires" content="-1" />
+ <script type="text/javascript" src="../common/check_browser.js"></script>
+ <script>
+ // Check for Native Client support in the browser before the DOM loads.
+ var isValidBrowser = false;
+ var browserSupportStatus = 0;
+ // Fullscreen and mouselock support is in Chrome version 16.
+ var CHROME_MINIMUM_VERSION = 16;
+
+ var checker = new browser_version.BrowserChecker(
+ CHROME_MINIMUM_VERSION,
+ navigator["appVersion"],
+ navigator["plugins"]);
+ checker.checkBrowser();
+
+ isValidBrowser = checker.getIsValidBrowser();
+ browserSupportStatus = checker.getBrowserSupportStatus();
+
+ function handleMessage(message_event) {
+ console.log(message_event.data);
+ }
+ </script>
+ <title>Full-screen and Mouse-lock Example</title>
+</head>
+<body title="This tooltip should not be shown if the mouse is locked.">
+ <h1>Full-screen and Mouse-lock Example</h1>
+ <ul>
+ <li>There are two different kinds of fullscreen mode: "tab fullscreen" and
+ "browser fullscreen".
+ <ul>
+ <li>Tab fullscreen refers to when a tab enters fullscreen mode via the
+ JS or Pepper fullscreen API.</li>
+ <li>Browser fullscreen refers to the user putting the browser itself
+ into fullscreen mode from the UI (e.g., pressing F11).</li>
+ </ul>
+ <span style="font-weight:bold">
+ NOTE: Mouse lock is only allowed in "tab fullscreen" mode.
+ </span>
+ </li>
+ <li>Lock mouse:
+ <ul>
+ <li>left click in the grey box; or</li>
+ <li>right click in the box to ensure that it is focused and
+ then press Enter key. (You could verify that the tooltip window is
+ dismissed properly by this second approach.)</li>
+ </ul>
+ </li>
+ <li>Unlock mouse voluntarily (i.e., NaCl module unlocks mouse):
+ <ul>
+ <li>press Enter.</li>
+ </ul>
+ </li>
+ <li>Unlock mouse involuntarily (i.e. Chrome unlocks mouse):
+ <ul>
+ <li>lose focus; or</li>
+ <li>press Esc key; or</li>
+ <li>exit from the "tab fullscreen" mode.</li>
+ </ul>
+ </li>
+ </ul>
+ <p>Clicking the mouse inside the grey square takes the NaCl module to/from
+ combined fullscreen and mouselock mode.</p>
+ <p>While in fullscreen, pressing Enter will exit/enter mouse lock mode.</p>
+ <!-- Load the published .nexe. This includes the 'src' attribute which
+ shows how to load multi-architecture modules. Each entry in the "nexes"
+ object in the .nmf manifest file is a key-value pair: the key is the runtime
+ ('x86-32', 'x86-64', etc.); the value is a URL for the desired NaCl module.
+ To load the debug versions of your .nexes, set the 'src' attribute to the
+ _dbg.nmf version of the manifest file.
+
+ Note: The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
+ and a 'message' event listener attached. This wrapping method is used
+ instead of attaching the event listeners directly to the <EMBED> element to
+ ensure that the listeners are active before the NaCl module 'load' event
+ fires. This also allows you to use PPB_Messaging.PostMessage() (in C) or
+ pp::Instance.PostMessage() (in C++) from within the initialization code in
+ your NaCl module.
+ -->
+ <div id="listener">
+ <script type="text/javascript">
+ if (browserSupportStatus ==
+ browser_version.BrowserChecker.StatusValues.CHROME_VERSION_TOO_OLD) {
+ alert('This example will only work on Chrome version ' +
+ CHROME_MINIMUM_VERSION +
+ ' or later.');
+ } else {
+ var listener = document.getElementById('listener')
+ listener.addEventListener('message', handleMessage, true);
+ // Create two instances of the NaCl module.
+ listener.innerHTML = '<embed id="mouselock_view" ' +
+ 'type="application/x-nacl" ' +
+ 'src="mouselock.nmf" ' +
+ 'width="300" height="300" />';
+ }
+ </script>
+ </div>
+</body>
+</html>
diff --git a/native_client_sdk/src/examples/multithreaded_input_events/build.scons b/native_client_sdk/src/examples/multithreaded_input_events/build.scons
new file mode 100644
index 0000000..00da894
--- /dev/null
+++ b/native_client_sdk/src/examples/multithreaded_input_events/build.scons
@@ -0,0 +1,41 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import make_nacl_env
+import nacl_utils
+import os
+
+nacl_env = make_nacl_env.NaClEnvironment(
+ use_c_plus_plus_libs=True, nacl_platform=os.getenv('NACL_TARGET_PLATFORM'),
+ install_subdir='multithreaded_input_events', lib_prefix='..')
+nacl_env.Append(
+ # Add a CPPPATH that enables the full-path #include directives, such as
+ # #include "examples/sine_synth/sine_synth.h"
+ CPPPATH=[os.path.dirname(os.path.dirname(os.getcwd()))],
+ # Strict ANSI compliance.
+ EXTRA_CCFLAGS=['-pedantic'],
+ )
+
+sources = ['mt_input_events.cc', 'custom_events.cc']
+
+opt_nexes, dbg_nexes = nacl_env.AllNaClModules(sources, 'mt_input_events')
+
+# This target is used by the SDK build system to provide a prebuilt version
+# of the example in the SDK installer.
+nacl_env.InstallPrebuilt('mt_input_events')
+
+app_files = [
+ 'mt_input_events.html',
+ 'mt_input_events.nmf',
+ ]
+
+# Split the install of the .nexes from the other app sources so that the strip
+# action is applied to the .nexes only.
+install_nexes = nacl_env.NaClStrippedInstall(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=opt_nexes)
+install_app = nacl_env.Install(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=app_files)
+nacl_env.Alias('install', source=install_app + install_nexes)
diff --git a/native_client_sdk/src/examples/multithreaded_input_events/custom_events.cc b/native_client_sdk/src/examples/multithreaded_input_events/custom_events.cc
new file mode 100644
index 0000000..b281da5
--- /dev/null
+++ b/native_client_sdk/src/examples/multithreaded_input_events/custom_events.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <sstream>
+
+#include "examples/multithreaded_input_events/custom_events.h"
+
+namespace event_queue {
+
+// Convert a given modifier to a descriptive string. Note that the actual
+// declared type of modifier in each of the event classes is uint32_t, but it is
+// expected to be interpreted as a bitfield of 'or'ed PP_InputEvent_Modifier
+// values.
+std::string ModifierToString(uint32_t modifier) {
+ std::string s;
+ if (modifier & kShiftKeyModifier) {
+ s += "shift ";
+ }
+ if (modifier & kControlKeyModifier) {
+ s += "ctrl ";
+ }
+ if (modifier & kAltKeyModifier) {
+ s += "alt ";
+ }
+ if (modifier & kMetaKeyModifer) {
+ s += "meta ";
+ }
+ if (modifier & kKeyPadModifier) {
+ s += "keypad ";
+ }
+ if (modifier & kAutoRepeatModifier) {
+ s += "autorepeat ";
+ }
+ if (modifier & kLeftButtonModifier) {
+ s += "left-button-down ";
+ }
+ if (modifier & kMiddleButtonModifier) {
+ s += "middle-button-down ";
+ }
+ if (modifier & kRightButtonModifier) {
+ s += "right-button-down ";
+ }
+ if (modifier & kCapsLockModifier) {
+ s += "caps-lock ";
+ }
+ if (modifier & kNumLockModifier) {
+ s += "num-lock ";
+ }
+ return s;
+}
+
+
+std::string KeyEvent::ToString() const {
+ std::ostringstream stream;
+ stream << " Key event:"
+ << " modifier:" << string_event_modifiers()
+ << " key_code:" << key_code_
+ << " time:" << timestamp_
+ << " text:" << text_
+ << "\n";
+ return stream.str();
+}
+
+std::string MouseEvent::ToString() const {
+ std::ostringstream stream;
+ stream << " Mouse event:"
+ << " modifier:" << string_event_modifiers()
+ << " button:" << MouseButtonToString(mouse_button_)
+ << " x:" << x_position_
+ << " y:" << y_position_
+ << " click_count:" << click_count_
+ << " time:" << timestamp_
+ << "\n";
+ return stream.str();
+}
+
+std::string WheelEvent::ToString() const {
+ std::ostringstream stream;
+ stream << "Wheel event."
+ << " modifier:" << string_event_modifiers()
+ << " deltax:" << delta_x_
+ << " deltay:" << delta_y_
+ << " wheel_ticks_x:" << ticks_x_
+ << " wheel_ticks_y:" << ticks_y_
+ << " scroll_by_page: " << scroll_by_page_
+ << "\n";
+ return stream.str();
+}
+
+std::string MouseEvent::MouseButtonToString(MouseButton button) const {
+ switch (button) {
+ case kNone:
+ return "None";
+ case kLeft:
+ return "Left";
+ case kMiddle:
+ return "Middle";
+ case kRight:
+ return "Right";
+ default:
+ std::ostringstream stream;
+ stream << "Unrecognized ("
+ << static_cast<int32_t>(button)
+ << ")";
+ return stream.str();
+ }
+}
+
+} // end namespace
+
diff --git a/native_client_sdk/src/examples/multithreaded_input_events/custom_events.h b/native_client_sdk/src/examples/multithreaded_input_events/custom_events.h
new file mode 100644
index 0000000..029fb7a
--- /dev/null
+++ b/native_client_sdk/src/examples/multithreaded_input_events/custom_events.h
@@ -0,0 +1,133 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CUSTOM_EVENTS_H
+#define CUSTOM_EVENTS_H
+
+#include <stdint.h>
+#include <string>
+namespace event_queue {
+
+// These functions and classes are used to define a non-Pepper set of
+// events. This is typical of what many developers might do, since it
+// would be common to convert a Pepper event into some other more
+// application-specific type of event (SDL, Qt, etc.).
+
+// Constants used for Event::event_modifers_ (which is an int)
+// are given below. Use powers of 2 so we can use bitwise AND/OR operators.
+const uint32_t kShiftKeyModifier = 1 << 0;
+const uint32_t kControlKeyModifier = 1 << 1;
+const uint32_t kAltKeyModifier = 1 << 2;
+const uint32_t kMetaKeyModifer = 1 << 3;
+const uint32_t kKeyPadModifier = 1 << 4;
+const uint32_t kAutoRepeatModifier = 1 << 5;
+const uint32_t kLeftButtonModifier = 1 << 6;
+const uint32_t kMiddleButtonModifier = 1 << 7;
+const uint32_t kRightButtonModifier = 1 << 8;
+const uint32_t kCapsLockModifier = 1 << 9;
+const uint32_t kNumLockModifier = 1 << 10;
+
+std::string ModifierToString(uint32_t modifier);
+
+// Abstract base class for an Event -- ToString() is not defined.
+// With polymorphism, we can have a collection of Event* and call
+// ToString() on each one to be able to display the details of each
+// event.
+class Event {
+ public:
+ // Constructor for the base class.
+ // |modifiers| is an int that uses bit fields to set specific
+ // changes, such as the Alt key, specific button, etc. See
+ // ModifierToString() and constants defined in custom_events.cc.
+ explicit Event(uint32_t modifiers)
+ : event_modifiers_(modifiers) {}
+ uint32_t event_modifiers() const {return event_modifiers_;}
+ std::string string_event_modifiers() const {
+ return ModifierToString(event_modifiers_);
+ }
+ // Convert the WheelEvent to a string
+ virtual std::string ToString() const = 0;
+ virtual ~Event() {}
+
+ private:
+ uint32_t event_modifiers_;
+};
+
+// Class for a keyboard event.
+class KeyEvent : public Event {
+ public:
+ // KeyEvent Constructor. |modifiers| is passed to Event base class.
+ // |keycode| is the ASCII value, |time| is a timestamp,
+ // |text| is the value as a string.
+ KeyEvent(uint32_t modifiers, uint32_t keycode, double time,
+ std::string text) :
+ Event(modifiers), key_code_(keycode),
+ timestamp_(time), text_(text) {}
+ // Convert the WheelEvent to a string
+ virtual std::string ToString() const;
+
+ private:
+ uint32_t key_code_;
+ double timestamp_;
+ std::string text_;
+};
+class MouseEvent : public Event {
+ public:
+ // Specify a mouse button, with kNone available for initialization.
+ enum MouseButton {kNone, kLeft, kMiddle, kRight};
+
+ // MouseEvent Constructor. |modifiers| is passed to Event base class.
+ // |button| specifies which button
+ // |xpos| and |ypos| give the location,
+ // |clicks| is how many times this same |xpos|,|ypos|
+ // has been clicked in a row. |time| is a timestamp,
+ MouseEvent(uint32_t modifiers, MouseButton button, uint32_t xpos,
+ uint32_t ypos, uint32_t clicks, double time)
+ : Event(modifiers), mouse_button_(button),
+ x_position_(xpos), y_position_(ypos),
+ click_count_(clicks), timestamp_(time) {}
+ // Convert the WheelEvent to a string
+ virtual std::string ToString() const;
+
+ private:
+ MouseButton mouse_button_;
+ uint32_t x_position_;
+ uint32_t y_position_;
+ uint32_t click_count_;
+ double timestamp_;
+
+ std::string MouseButtonToString(MouseButton button) const;
+};
+
+
+class WheelEvent : public Event {
+ public:
+ // WheelEvent Constructor. |modifiers| is passed to Event base class.
+ // |xticks| and |yticks| specify number of mouse wheel ticks.
+ // |scroll_by_page| indicates if we have scrolled past the current
+ // page. |time| is a timestamp,
+ WheelEvent(int modifiers, uint32_t dx, uint32_t dy,
+ uint32_t xticks, uint32_t yticks, bool scroll_by_page,
+ float time) :
+ Event(modifiers), delta_x_(dx), delta_y_(dy),
+ ticks_x_(xticks), ticks_y_(yticks),
+ scroll_by_page_(scroll_by_page), timestamp_(time) {}
+ // Convert the WheelEvent to a string
+ virtual std::string ToString() const;
+
+ private:
+ uint32_t delta_x_;
+ uint32_t delta_y_;
+ uint32_t ticks_x_;
+ uint32_t ticks_y_;
+ bool scroll_by_page_;
+ double timestamp_;
+};
+
+
+
+} // end namespace
+
+#endif // CUSTOM_EVENTS_H
+
diff --git a/native_client_sdk/src/examples/multithreaded_input_events/mt_input_events.cc b/native_client_sdk/src/examples/multithreaded_input_events/mt_input_events.cc
new file mode 100644
index 0000000..a392ba2
--- /dev/null
+++ b/native_client_sdk/src/examples/multithreaded_input_events/mt_input_events.cc
@@ -0,0 +1,323 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// C headers
+#include <cassert>
+#include <cstdio>
+
+// C++ headers
+#include <sstream>
+#include <string>
+
+#include "examples/multithreaded_input_events/custom_events.h"
+#include "examples/multithreaded_input_events/shared_queue.h"
+#include "examples/multithreaded_input_events/thread_safe_ref_count.h"
+
+// NaCl
+#include "ppapi/cpp/completion_callback.h"
+#include "ppapi/cpp/input_event.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/cpp/point.h"
+#include "ppapi/cpp/var.h"
+
+namespace event_queue {
+const char* const kDidChangeView = "DidChangeView";
+const char* const kHandleInputEvent = "DidHandleInputEvent";
+const char* const kDidChangeFocus = "DidChangeFocus";
+const char* const kHaveFocus = "HaveFocus";
+const char* const kDontHaveFocus = "DontHaveFocus";
+const char* const kCancelMessage = "CANCEL";
+
+// Convert a pepper inputevent modifier value into a
+// custom event modifier.
+unsigned int ConvertEventModifier(uint32_t pp_modifier) {
+ unsigned int custom_modifier = 0;
+ if (pp_modifier & PP_INPUTEVENT_MODIFIER_SHIFTKEY) {
+ custom_modifier |= kShiftKeyModifier;
+ }
+ if (pp_modifier & PP_INPUTEVENT_MODIFIER_CONTROLKEY) {
+ custom_modifier |= kControlKeyModifier;
+ }
+ if (pp_modifier & PP_INPUTEVENT_MODIFIER_ALTKEY) {
+ custom_modifier |= kAltKeyModifier;
+ }
+ if (pp_modifier & PP_INPUTEVENT_MODIFIER_METAKEY) {
+ custom_modifier |= kMetaKeyModifer;
+ }
+ if (pp_modifier & PP_INPUTEVENT_MODIFIER_ISKEYPAD) {
+ custom_modifier |= kKeyPadModifier;
+ }
+ if (pp_modifier & PP_INPUTEVENT_MODIFIER_ISAUTOREPEAT) {
+ custom_modifier |= kAutoRepeatModifier;
+ }
+ if (pp_modifier & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) {
+ custom_modifier |= kLeftButtonModifier;
+ }
+ if (pp_modifier & PP_INPUTEVENT_MODIFIER_MIDDLEBUTTONDOWN) {
+ custom_modifier |= kMiddleButtonModifier;
+ }
+ if (pp_modifier & PP_INPUTEVENT_MODIFIER_RIGHTBUTTONDOWN) {
+ custom_modifier |= kRightButtonModifier;
+ }
+ if (pp_modifier & PP_INPUTEVENT_MODIFIER_CAPSLOCKKEY) {
+ custom_modifier |= kCapsLockModifier;
+ }
+ if (pp_modifier & PP_INPUTEVENT_MODIFIER_NUMLOCKKEY) {
+ custom_modifier |= kNumLockModifier;
+ }
+ return custom_modifier;
+}
+
+class EventInstance : public pp::Instance {
+ public:
+ explicit EventInstance(PP_Instance instance)
+ : pp::Instance(instance),
+ event_thread_(NULL),
+ callback_factory_(this) {
+ RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_WHEEL);
+ RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD);
+ }
+
+ // Not guaranteed to be called in Pepper, but a good idea to cancel the
+ // queue and signal to workers to die if it is called.
+ virtual ~EventInstance() {
+ CancelQueueAndWaitForWorker();
+ }
+
+ // Create the 'worker thread'.
+ bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
+ pthread_create(&event_thread_, NULL, ProcessEventOnWorkerThread, this);
+ return true;
+ }
+
+ /// Clicking outside of the instance's bounding box
+ /// will create a DidChangeFocus event (the NaCl instance is
+ /// out of focus). Clicking back inside the instance's
+ /// bounding box will create another DidChangeFocus event
+ /// (the NaCl instance is back in focus). The default is
+ /// that the instance is out of focus.
+ void DidChangeFocus(bool focus) {
+ PostMessage(pp::Var(kDidChangeFocus));
+ if (focus == true) {
+ PostMessage(pp::Var(kHaveFocus));
+ } else {
+ PostMessage(pp::Var(kDontHaveFocus));
+ }
+ }
+
+ /// Scrolling the mouse wheel causes a DidChangeView event.
+ void DidChangeView(const pp::Rect& position,
+ const pp::Rect& clip) {
+ PostMessage(pp::Var(kDidChangeView));
+ }
+
+ /// Called by the browser to handle the postMessage() call in Javascript.
+ /// Detects which method is being called from the message contents, and
+ /// calls the appropriate function. Posts the result back to the browser
+ /// asynchronously.
+ /// @param[in] var_message The message posted by the browser. The only
+ /// supported message is |kCancelMessage|. If we receive this, we
+ /// cancel the shared queue.
+ virtual void HandleMessage(const pp::Var& var_message) {
+ std::string message = var_message.AsString();
+ if (kCancelMessage == message) {
+ std::string reply = "Received cancel : only Focus events will be "
+ "displayed. Worker thread for mouse/wheel/keyboard will exit.";
+ PostMessage(pp::Var(reply));
+ printf("Calling cancel queue\n");
+ CancelQueueAndWaitForWorker();
+ }
+ }
+
+ // HandleInputEvent operates on the main Pepper thread. Here we
+ // illustrate copying the Pepper input event to our own custom event type.
+ // Since we need to use Pepper API calls to convert it, we must do the
+ // conversion on the main thread. Once we have converted it to our own
+ // event type, we push that into a thread-safe queue and quickly return.
+ // The worker thread can process the custom event and do whatever
+ // (possibly slow) things it wants to do without making the browser
+ // become unresponsive.
+ // We dynamically allocate a sub-class of our custom event (Event)
+ // so that the queue can contain an Event*.
+ virtual bool HandleInputEvent(const pp::InputEvent& event) {
+ Event* event_ptr = NULL;
+ switch (event.GetType()) {
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START:
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE:
+ case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END:
+ case PP_INPUTEVENT_TYPE_IME_TEXT:
+ // these cases are not handled...fall through below...
+ case PP_INPUTEVENT_TYPE_UNDEFINED:
+ break;
+ case PP_INPUTEVENT_TYPE_MOUSEDOWN:
+ case PP_INPUTEVENT_TYPE_MOUSEUP:
+ case PP_INPUTEVENT_TYPE_MOUSEMOVE:
+ case PP_INPUTEVENT_TYPE_MOUSEENTER:
+ case PP_INPUTEVENT_TYPE_MOUSELEAVE:
+ {
+ pp::MouseInputEvent mouse_event(event);
+ PP_InputEvent_MouseButton pp_button = mouse_event.GetButton();
+ MouseEvent::MouseButton mouse_button = MouseEvent::kNone;
+ switch (pp_button) {
+ case PP_INPUTEVENT_MOUSEBUTTON_NONE:
+ mouse_button = MouseEvent::kNone;
+ break;
+ case PP_INPUTEVENT_MOUSEBUTTON_LEFT:
+ mouse_button = MouseEvent::kLeft;
+ break;
+ case PP_INPUTEVENT_MOUSEBUTTON_MIDDLE:
+ mouse_button = MouseEvent::kMiddle;
+ break;
+ case PP_INPUTEVENT_MOUSEBUTTON_RIGHT:
+ mouse_button = MouseEvent::kRight;
+ break;
+ }
+ event_ptr = new MouseEvent(
+ ConvertEventModifier(mouse_event.GetModifiers()),
+ mouse_button, mouse_event.GetPosition().x(),
+ mouse_event.GetPosition().y(), mouse_event.GetClickCount(),
+ mouse_event.GetTimeStamp());
+ }
+ break;
+ case PP_INPUTEVENT_TYPE_WHEEL:
+ {
+ pp::WheelInputEvent wheel_event(event);
+ event_ptr = new WheelEvent(
+ ConvertEventModifier(wheel_event.GetModifiers()),
+ wheel_event.GetDelta().x(), wheel_event.GetDelta().y(),
+ wheel_event.GetTicks().x(), wheel_event.GetTicks().y(),
+ wheel_event.GetScrollByPage(), wheel_event.GetTimeStamp());
+ }
+ break;
+ case PP_INPUTEVENT_TYPE_RAWKEYDOWN:
+ case PP_INPUTEVENT_TYPE_KEYDOWN:
+ case PP_INPUTEVENT_TYPE_KEYUP:
+ case PP_INPUTEVENT_TYPE_CHAR:
+ case PP_INPUTEVENT_TYPE_CONTEXTMENU:
+ {
+ pp::KeyboardInputEvent key_event(event);
+ event_ptr = new KeyEvent(
+ ConvertEventModifier(key_event.GetModifiers()),
+ key_event.GetKeyCode(), key_event.GetTimeStamp(),
+ key_event.GetCharacterText().DebugString());
+ }
+ break;
+ default:
+ {
+ // For any unhandled events, send a message to the browser
+ // so that the user is aware of these and can investigate.
+ std::stringstream oss;
+ oss << "Default (unhandled) event, type=" << event.GetType();
+ PostMessage(oss.str());
+ }
+ break;
+ }
+ event_queue_.Push(event_ptr);
+ return true;
+ }
+
+ // Return an event from the thread-safe queue, waiting for a new event
+ // to occur if the queue is empty. Set |was_queue_cancelled| to indicate
+ // whether the queue was cancelled. If it was cancelled, then the
+ // Event* will be NULL.
+ const Event* GetEventFromQueue(bool *was_queue_cancelled) {
+ Event* event;
+ QueueGetResult result = event_queue_.GetItem(&event, kWait);
+ if (result == kQueueWasCancelled) {
+ *was_queue_cancelled = true;
+ return NULL;
+ }
+ *was_queue_cancelled = false;
+ return event;
+ }
+
+ // This method is called from the worker thread using CallOnMainThread.
+ // It is not static, and allows PostMessage to be called.
+ void* PostStringToBrowser(int32_t result, std::string data_to_send) {
+ PostMessage(pp::Var(data_to_send));
+ return 0;
+ }
+
+ // |ProcessEventOnWorkerThread| is a static method that is run
+ // by a thread. It pulls events from the queue, converts
+ // them to a string, and calls CallOnMainThread so that
+ // PostStringToBrowser will be called, which will call PostMessage
+ // to send the converted event back to the browser.
+ static void* ProcessEventOnWorkerThread(void* param) {
+ EventInstance* event_instance = static_cast<EventInstance*>(param);
+ while (1) {
+ // Grab a generic Event* so that down below we can call
+ // event->ToString(), which will use the correct virtual method
+ // to convert the event to a string. This 'conversion' is
+ // the 'work' being done on the worker thread. In an application
+ // the work might involve changing application state based on
+ // the event that was processed.
+ bool queue_cancelled;
+ const Event* event = event_instance->GetEventFromQueue(&queue_cancelled);
+ if (queue_cancelled) {
+ printf("Queue was cancelled, worker thread exiting\n");
+ pthread_exit(NULL);
+ }
+ std::string event_string = event->ToString();
+ delete event;
+ // Need to invoke callback on main thread.
+ pp::Module::Get()->core()->CallOnMainThread(
+ 0,
+ event_instance->callback_factory().NewCallback(
+ &EventInstance::PostStringToBrowser,
+ event_string));
+ } // end of while loop.
+ return 0;
+ }
+
+ // Return the callback factory.
+ // Allows the static method (ProcessEventOnWorkerThread) to use
+ // the |event_instance| pointer to get the factory.
+ pp::CompletionCallbackFactory<EventInstance, ThreadSafeRefCount>&
+ callback_factory() {
+ return callback_factory_;
+ }
+
+ private:
+ // Cancels the queue (which will cause the thread to exit).
+ // Wait for the thread. Set |event_thread_| to NULL so we only
+ // execute the body once.
+ void CancelQueueAndWaitForWorker() {
+ if (event_thread_) {
+ event_queue_.CancelQueue();
+ pthread_join(event_thread_, NULL);
+ event_thread_ = NULL;
+ }
+ }
+ pthread_t event_thread_;
+ LockingQueue<Event*> event_queue_;
+ pp::CompletionCallbackFactory<EventInstance, ThreadSafeRefCount>
+ callback_factory_;
+};
+
+// The EventModule provides an implementation of pp::Module that creates
+// EventInstance objects when invoked. This is part of the glue code that makes
+// our example accessible to ppapi.
+class EventModule : public pp::Module {
+ public:
+ EventModule() : pp::Module() {}
+ virtual ~EventModule() {}
+
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new EventInstance(instance);
+ }
+};
+
+} // namespace
+
+// Implement the required pp::CreateModule function that creates our specific
+// kind of Module (in this case, EventModule). This is part of the glue code
+// that makes our example accessible to ppapi.
+namespace pp {
+ Module* CreateModule() {
+ return new event_queue::EventModule();
+ }
+}
+
diff --git a/native_client_sdk/src/examples/multithreaded_input_events/mt_input_events.html b/native_client_sdk/src/examples/multithreaded_input_events/mt_input_events.html
new file mode 100644
index 0000000..7e85e1c
--- /dev/null
+++ b/native_client_sdk/src/examples/multithreaded_input_events/mt_input_events.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<html>
+ <!--
+ Copyright (c) 2011 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+<head>
+ <title>Input Events</title>
+
+ <script type="text/javascript">
+ var kMaxArraySize = 20;
+ var messageArray = new Array();
+ var eventModule = null;
+
+ function $(id) {
+ return document.getElementById(id);
+ }
+
+ // Indicate success when the NaCl module has loaded.
+ function moduleDidLoad() {
+ eventModule = document.getElementById('event_module');
+ }
+
+ function receiveMessage(message) {
+ // Show last |kMaxArraySize| events in html.
+ messageArray.push(message.data);
+ if (messageArray.length > kMaxArraySize) {
+ messageArray.shift();
+ }
+ var newData = messageArray.join('<BR>');
+ document.getElementById('eventString').innerHTML = newData;
+ // Print event to console.
+ console.log(message.data);
+ }
+ function cancelQueue() {
+ if (eventModule == null) {
+ console.log('Module is not loaded.');
+ return;
+ }
+ eventModule.postMessage('CANCEL');
+ }
+ </script>
+</head>
+<body>
+<h1>InputEvent Handling Example</h1>
+ <div id="listener">
+ <script type="text/javascript">
+ $('listener').addEventListener('message', receiveMessage, true);
+ $('listener').addEventListener('load', moduleDidLoad, true);
+ </script>
+ <button onclick="cancelQueue()">Kill worker thread and queue</button>
+ <p/>
+ <embed name="nacl_module"
+ id="event_module"
+ width=400 height=400
+ src="mt_input_events.nmf"
+ type="application/x-nacl"
+ style="background-color:gray" />
+ </div>
+<p>
+This example demonstrates handling of input events in PPAPI.</p>
+<p>
+Each time an input event happens in the context of the gray box,
+the main thread in the embedded NaCl module converts it from a Pepper input
+event to a non-Pepper event and puts this custom event onto a shared queue.
+A worker thread in the embedded NaCl module reads events from the queue,
+and converts each event to a string and then uses CallOnMainThread to post a
+message describing the event back to JavaScript, which prints a message to the
+JavaScript console in Chrome and to a string on the page.</p>
+<p>
+If you press the 'Kill worker thread and queue' button, then the main thread
+(which puts events on the queue) will call CancelQueue, indicating that the
+main thread will no longer put events on the queue. When the worker sees that
+the shared queue has been cancelled, the worker thread will terminate.</p>
+<h2>Events</h2>
+<pre><p><b id='eventString'>None</b></p>
+</pre>
+</body>
+</html>
diff --git a/native_client_sdk/src/examples/multithreaded_input_events/shared_queue.h b/native_client_sdk/src/examples/multithreaded_input_events/shared_queue.h
new file mode 100644
index 0000000..22ff7ae
--- /dev/null
+++ b/native_client_sdk/src/examples/multithreaded_input_events/shared_queue.h
@@ -0,0 +1,137 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHARED_QUEUE_H
+#define SHARED_QUEUE_H
+
+#include <pthread.h>
+#include <cassert>
+#include <deque>
+
+#include "examples/multithreaded_input_events/thread_safe_ref_count.h"
+
+namespace event_queue {
+
+// This file provides a queue that uses a mutex and condition variable so that
+// one thread can put pointers into the queue and another thread can pull items
+// out of the queue.
+
+const int kPthreadMutexSuccess = 0;
+
+// Specifies whether we want to wait for the queue.
+enum QueueWaitingFlag {
+ kWait = 0,
+ kDontWait
+};
+
+// Indicates if we got an item, did not wait, or if the queue was cancelled.
+enum QueueGetResult {
+ kReturnedItem = 0,
+ kDidNotWait = 1,
+ kQueueWasCancelled
+};
+
+// LockingQueue contains a collection of <T>, such as a collection of
+// objects or pointers. The Push() method is used to add items to the
+// queue in a thread-safe manner. The GetItem() is used to retrieve
+// items from the queue in a thread-safe manner.
+template <class T>
+class LockingQueue {
+ public:
+ LockingQueue() : quit_(false) {
+ int result = pthread_mutex_init(&queue_mutex_, NULL);
+ assert(result == 0);
+ result = pthread_cond_init(&queue_condition_var_, NULL);
+ assert(result == 0);
+ }
+ ~LockingQueue() {
+ pthread_mutex_destroy(&queue_mutex_);
+ }
+
+ // The producer (who instantiates the queue) calls this to tell the
+ // consumer that the queue is no longer being used.
+ void CancelQueue() {
+ ScopedLock scoped_mutex(&queue_mutex_);
+ quit_ = true;
+ // Signal the condition var so that if a thread is waiting in
+ // GetItem the thread will wake up and see that the queue has
+ // been cancelled.
+ pthread_cond_signal(&queue_condition_var_);
+ }
+
+ // The consumer calls this to see if the queue has been cancelled by
+ // the producer. If so, the thread should not call GetItem and may
+ // need to terminate -- i.e. in a case where the producer created
+ // the consumer thread.
+ bool IsCancelled() {
+ ScopedLock scoped_mutex(&queue_mutex_);
+ return quit_;
+ }
+
+ // Grabs the mutex and pushes a new item to the end of the queue if the
+ // queue is not full. Signals the condition variable so that a thread
+ // that is waiting will wake up and grab the item.
+ void Push(const T& item) {
+ ScopedLock scoped_mutex(&queue_mutex_);
+ the_queue_.push_back(item);
+ pthread_cond_signal(&queue_condition_var_);
+ }
+
+ // Tries to pop the front element from the queue; returns an enum:
+ // kReturnedItem if an item is returned in |item_ptr|,
+ // kDidNotWait if |wait| was kDontWait and the queue was empty,
+ // kQueueWasCancelled if the producer called CancelQueue().
+ // If |wait| is kWait, GetItem will wait to return until the queue
+ // contains an item (unless the queue is cancelled).
+ QueueGetResult GetItem(T* item_ptr, QueueWaitingFlag wait) {
+ // Because we use both pthread_mutex_lock and pthread_cond_wait,
+ // we directly use the mutex instead of using ScopedLock.
+ ScopedLock scoped_mutex(&queue_mutex_);
+ // Use a while loop to get an item. If the user does not want to wait,
+ // we will exit from the loop anyway, unlocking the mutex.
+ // If the user does want to wait, we will wait for pthread_cond_wait,
+ // and the while loop will check is_empty_no_locking() one more
+ // time so that a spurious wake-up of pthread_cond_wait is handled.
+ // If |quit_| has been set, break out of the loop.
+ while (!quit_ && is_empty_no_locking()) {
+ // If user doesn't want to wait, return...
+ if (kDontWait == wait) {
+ return kDidNotWait;
+ }
+ // Wait for signal to occur.
+ pthread_cond_wait(&queue_condition_var_, &queue_mutex_);
+ }
+ // Check to see if quit_ woke us up
+ if (quit_) {
+ return kQueueWasCancelled;
+ }
+
+ // At this point, the queue was either not empty or, if it was empty,
+ // we called pthread_cond_wait (which released the mutex, waited for the
+ // signal to occur, and then atomically reacquired the mutex).
+ // Thus, if we are here, the queue cannot be empty because we either
+ // had the mutex and verified it was not empty, or we waited for the
+ // producer to put an item in and signal a single thread (us).
+ T& item = the_queue_.front();
+ *item_ptr = item;
+ the_queue_.pop_front();
+ return kReturnedItem;
+ }
+
+ private:
+ std::deque<T> the_queue_;
+ bool quit_;
+ pthread_mutex_t queue_mutex_;
+ pthread_cond_t queue_condition_var_;
+
+ // This is used by methods that already have the lock.
+ bool is_empty_no_locking() const {
+ return the_queue_.empty();
+ }
+};
+
+} // end of unnamed namespace
+
+#endif // SHARED_QUEUE_H
+
diff --git a/native_client_sdk/src/examples/multithreaded_input_events/thread_safe_ref_count.h b/native_client_sdk/src/examples/multithreaded_input_events/thread_safe_ref_count.h
new file mode 100644
index 0000000..f4e6beb
--- /dev/null
+++ b/native_client_sdk/src/examples/multithreaded_input_events/thread_safe_ref_count.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THREAD_SAFE_REF_COUNT_H
+#define THREAD_SAFE_REF_COUNT_H
+
+#include <pthread.h>
+#include <cassert>
+
+const int kPthreadMutexSuccess = 0;
+
+namespace event_queue {
+// Some synchronization helper classes.
+
+class ScopedLock {
+ public:
+ explicit ScopedLock(pthread_mutex_t* mutex)
+ : mutex_(mutex) {
+ if (pthread_mutex_lock(mutex_) != kPthreadMutexSuccess) {
+ mutex_ = NULL;
+ }
+ }
+ ~ScopedLock() {
+ if (mutex_ != NULL) {
+ pthread_mutex_unlock(mutex_);
+ }
+ }
+ private:
+ ScopedLock& operator=(const ScopedLock&); // Not implemented, do not use.
+ ScopedLock(const ScopedLock&); // Not implemented, do not use.
+ pthread_mutex_t* mutex_; // Weak reference, passed in to constructor.
+};
+
+class ThreadSafeRefCount {
+ public:
+ ThreadSafeRefCount()
+ : ref_(0) {
+ Init();
+ }
+
+ void Init() {
+ pthread_mutex_init(&mutex_, NULL);
+ }
+
+ int32_t AddRef() {
+ ScopedLock s(&mutex_);
+ return ++ref_;
+ }
+
+ int32_t Release() {
+ ScopedLock s(&mutex_);
+ return --ref_;
+ }
+
+ private:
+ int32_t ref_;
+ pthread_mutex_t mutex_;
+};
+
+} // namespace
+#endif // THREAD_SAFE_REF_COUNT_H
+
diff --git a/native_client_sdk/src/examples/pi_generator/build.scons b/native_client_sdk/src/examples/pi_generator/build.scons
new file mode 100644
index 0000000..8148242
--- /dev/null
+++ b/native_client_sdk/src/examples/pi_generator/build.scons
@@ -0,0 +1,40 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import make_nacl_env
+import os
+
+nacl_env = make_nacl_env.NaClEnvironment(
+ use_c_plus_plus_libs=True, nacl_platform=os.getenv('NACL_TARGET_PLATFORM'),
+ install_subdir='pi_generator', lib_prefix='..')
+nacl_env.Append(
+ # Add a CPPPATH that enables the full-path #include directives, such as
+ # #include "examples/sine_synth/sine_synth.h"
+ CPPPATH=[os.path.dirname(os.path.dirname(os.getcwd()))],
+ # Strict ANSI compliance.
+ EXTRA_CCFLAGS=['-pedantic'],
+ )
+
+sources = ['pi_generator.cc', 'pi_generator_module.cc']
+
+opt_nexes, dbg_nexes = nacl_env.AllNaClModules(sources, 'pi_generator')
+
+# This target is used by the SDK build system to provide a prebuilt version
+# of the example in the SDK installer.
+nacl_env.InstallPrebuilt('pi_generator')
+
+app_files = [
+ 'pi_generator.html',
+ 'pi_generator.nmf',
+ ]
+
+# Split the install of the .nexes from the other app sources so that the strip
+# action is applied to the .nexes only.
+install_nexes = nacl_env.NaClStrippedInstall(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=opt_nexes)
+install_app = nacl_env.Install(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=app_files)
+nacl_env.Alias('install', source=install_app + install_nexes)
diff --git a/native_client_sdk/src/examples/pi_generator/pi_generator.cc b/native_client_sdk/src/examples/pi_generator/pi_generator.cc
new file mode 100644
index 0000000..f5e6e4d
--- /dev/null
+++ b/native_client_sdk/src/examples/pi_generator/pi_generator.cc
@@ -0,0 +1,240 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/pi_generator/pi_generator.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cassert>
+#include <cmath>
+#include <cstring>
+#include <string>
+#include "ppapi/cpp/completion_callback.h"
+#include "ppapi/cpp/var.h"
+
+namespace {
+const int kPthreadMutexSuccess = 0;
+const char* const kPaintMethodId = "paint";
+const double kInvalidPiValue = -1.0;
+const int kMaxPointCount = 1000000000; // The total number of points to draw.
+const uint32_t kOpaqueColorMask = 0xff000000; // Opaque pixels.
+const uint32_t kRedMask = 0xff0000;
+const uint32_t kBlueMask = 0xff;
+const uint32_t kRedShift = 16;
+const uint32_t kBlueShift = 0;
+
+// This is called by the browser when the 2D context has been flushed to the
+// browser window.
+void FlushCallback(void* data, int32_t result) {
+ static_cast<pi_generator::PiGenerator*>(data)->set_flush_pending(false);
+}
+} // namespace
+
+namespace pi_generator {
+
+// A small helper RAII class that implementes a scoped pthread_mutex lock.
+class ScopedMutexLock {
+ public:
+ explicit ScopedMutexLock(pthread_mutex_t* mutex) : mutex_(mutex) {
+ if (pthread_mutex_lock(mutex_) != kPthreadMutexSuccess) {
+ mutex_ = NULL;
+ }
+ }
+ ~ScopedMutexLock() {
+ if (mutex_)
+ pthread_mutex_unlock(mutex_);
+ }
+ bool is_valid() const {
+ return mutex_ != NULL;
+ }
+ private:
+ pthread_mutex_t* mutex_; // Weak reference.
+};
+
+// A small helper RAII class used to acquire and release the pixel lock.
+class ScopedPixelLock {
+ public:
+ explicit ScopedPixelLock(PiGenerator* image_owner)
+ : image_owner_(image_owner), pixels_(image_owner->LockPixels()) {}
+
+ ~ScopedPixelLock() {
+ pixels_ = NULL;
+ image_owner_->UnlockPixels();
+ }
+
+ uint32_t* pixels() const {
+ return pixels_;
+ }
+ private:
+ PiGenerator* image_owner_; // Weak reference.
+ uint32_t* pixels_; // Weak reference.
+
+ ScopedPixelLock(); // Not implemented, do not use.
+};
+
+PiGenerator::PiGenerator(PP_Instance instance)
+ : pp::Instance(instance),
+ graphics_2d_context_(NULL),
+ pixel_buffer_(NULL),
+ flush_pending_(false),
+ quit_(false),
+ compute_pi_thread_(0),
+ pi_(0.0) {
+ pthread_mutex_init(&pixel_buffer_mutex_, NULL);
+}
+
+PiGenerator::~PiGenerator() {
+ quit_ = true;
+ if (compute_pi_thread_) {
+ pthread_join(compute_pi_thread_, NULL);
+ }
+ DestroyContext();
+ // The ComputePi() thread should be gone by now, so there is no need to
+ // acquire the mutex for |pixel_buffer_|.
+ delete pixel_buffer_;
+ pthread_mutex_destroy(&pixel_buffer_mutex_);
+}
+
+void PiGenerator::DidChangeView(const pp::Rect& position,
+ const pp::Rect& clip) {
+ if (position.size().width() == width() &&
+ position.size().height() == height())
+ return; // Size didn't change, no need to update anything.
+
+ // Create a new device context with the new size.
+ DestroyContext();
+ CreateContext(position.size());
+ // Delete the old pixel buffer and create a new one.
+ ScopedMutexLock scoped_mutex(&pixel_buffer_mutex_);
+ delete pixel_buffer_;
+ pixel_buffer_ = NULL;
+ if (graphics_2d_context_ != NULL) {
+ pixel_buffer_ = new pp::ImageData(this,
+ PP_IMAGEDATAFORMAT_BGRA_PREMUL,
+ graphics_2d_context_->size(),
+ false);
+ }
+}
+
+bool PiGenerator::Init(uint32_t argc, const char* argn[], const char* argv[]) {
+ pthread_create(&compute_pi_thread_, NULL, ComputePi, this);
+ return true;
+}
+
+uint32_t* PiGenerator::LockPixels() {
+ void* pixels = NULL;
+ // Do not use a ScopedMutexLock here, since the lock needs to be held until
+ // the matching UnlockPixels() call.
+ if (pthread_mutex_lock(&pixel_buffer_mutex_) == kPthreadMutexSuccess) {
+ if (pixel_buffer_ != NULL && !pixel_buffer_->is_null()) {
+ pixels = pixel_buffer_->data();
+ }
+ }
+ return reinterpret_cast<uint32_t*>(pixels);
+}
+
+void PiGenerator::HandleMessage(const pp::Var& var_message) {
+ if (!var_message.is_string()) {
+ PostMessage(pp::Var(kInvalidPiValue));
+ }
+ std::string message = var_message.AsString();
+ if (message == kPaintMethodId) {
+ Paint();
+ } else {
+ PostMessage(pp::Var(kInvalidPiValue));
+ }
+}
+
+void PiGenerator::UnlockPixels() const {
+ pthread_mutex_unlock(&pixel_buffer_mutex_);
+}
+
+void PiGenerator::Paint() {
+ ScopedMutexLock scoped_mutex(&pixel_buffer_mutex_);
+ if (!scoped_mutex.is_valid()) {
+ return;
+ }
+ FlushPixelBuffer();
+ // Post the current estimate of Pi back to the browser.
+ pp::Var pi_estimate(pi());
+ // Paint() is called on the main thread, so no need for CallOnMainThread()
+ // here. It's OK to just post the message.
+ PostMessage(pi_estimate);
+}
+
+void PiGenerator::CreateContext(const pp::Size& size) {
+ ScopedMutexLock scoped_mutex(&pixel_buffer_mutex_);
+ if (!scoped_mutex.is_valid()) {
+ return;
+ }
+ if (IsContextValid())
+ return;
+ graphics_2d_context_ = new pp::Graphics2D(this, size, false);
+ if (!BindGraphics(*graphics_2d_context_)) {
+ printf("Couldn't bind the device context\n");
+ }
+}
+
+void PiGenerator::DestroyContext() {
+ ScopedMutexLock scoped_mutex(&pixel_buffer_mutex_);
+ if (!scoped_mutex.is_valid()) {
+ return;
+ }
+ if (!IsContextValid())
+ return;
+ delete graphics_2d_context_;
+ graphics_2d_context_ = NULL;
+}
+
+void PiGenerator::FlushPixelBuffer() {
+ if (!IsContextValid())
+ return;
+ // Note that the pixel lock is held while the buffer is copied into the
+ // device context and then flushed.
+ graphics_2d_context_->PaintImageData(*pixel_buffer_, pp::Point());
+ if (flush_pending())
+ return;
+ set_flush_pending(true);
+ graphics_2d_context_->Flush(pp::CompletionCallback(&FlushCallback, this));
+}
+
+void* PiGenerator::ComputePi(void* param) {
+ int count = 0; // The number of points put inside the inscribed quadrant.
+ unsigned int seed = 1;
+ PiGenerator* pi_generator = static_cast<PiGenerator*>(param);
+ srand(seed);
+ for (int i = 1; i <= kMaxPointCount && !pi_generator->quit(); ++i) {
+ ScopedPixelLock scoped_pixel_lock(pi_generator);
+ uint32_t* pixel_bits = scoped_pixel_lock.pixels();
+ if (pixel_bits == NULL) {
+ // Note that if the pixel buffer never gets initialized, this won't ever
+ // paint anything. Which is probably the right thing to do. Also, this
+ // clause means that the image will not get the very first few Pi dots,
+ // since it's possible that this thread starts before the pixel buffer is
+ // initialized.
+ continue;
+ }
+ double x = static_cast<double>(rand_r(&seed)) / RAND_MAX;
+ double y = static_cast<double>(rand_r(&seed)) / RAND_MAX;
+ double distance = sqrt(x * x + y * y);
+ int px = x * pi_generator->width();
+ int py = (1.0 - y) * pi_generator->height();
+ uint32_t color = pixel_bits[pi_generator->width() * py + px];
+ if (distance < 1.0) {
+ // Set color to blue.
+ ++count;
+ pi_generator->pi_ = 4.0 * count / i;
+ color += 4 << kBlueShift;
+ color &= kBlueMask;
+ } else {
+ // Set color to red.
+ color += 4 << kRedShift;
+ color &= kRedMask;
+ }
+ pixel_bits[pi_generator->width() * py + px] = color | kOpaqueColorMask;
+ }
+ return 0;
+}
+
+} // namespace pi_generator
diff --git a/native_client_sdk/src/examples/pi_generator/pi_generator.h b/native_client_sdk/src/examples/pi_generator/pi_generator.h
new file mode 100644
index 0000000..beecf41
--- /dev/null
+++ b/native_client_sdk/src/examples/pi_generator/pi_generator.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_PI_GENERATOR_PI_GENERATOR_H_
+#define EXAMPLES_PI_GENERATOR_PI_GENERATOR_H_
+
+#include <pthread.h>
+#include <map>
+#include <vector>
+#include "ppapi/cpp/graphics_2d.h"
+#include "ppapi/cpp/image_data.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/rect.h"
+#include "ppapi/cpp/size.h"
+
+namespace pi_generator {
+
+// The Instance class. One of these exists for each instance of your NaCl
+// module on the web page. The browser will ask the Module object to create
+// a new Instance for each occurrence of the <embed> tag that has these
+// attributes:
+// type="application/x-nacl"
+// nacl="pi_generator.nmf"
+class PiGenerator : public pp::Instance {
+ public:
+ explicit PiGenerator(PP_Instance instance);
+ virtual ~PiGenerator();
+
+ // Start up the ComputePi() thread.
+ virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]);
+
+ // Update the graphics context to the new size, and regenerate |pixel_buffer_|
+ // to fit the new size as well.
+ virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip);
+
+ // Called by the browser to handle the postMessage() call in Javascript.
+ // The message in this case is expected to contain the string 'paint', and
+ // if so this invokes the Paint() function. If |var_message| is not a string
+ // type, or contains something other than 'paint', this method posts an
+ // invalid value for Pi (-1.0) back to the browser.
+ virtual void HandleMessage(const pp::Var& var_message);
+
+ // Return a pointer to the pixels represented by |pixel_buffer_|. When this
+ // method returns, the underlying |pixel_buffer_| object is locked. This
+ // call must have a matching UnlockPixels() or various threading errors
+ // (e.g. deadlock) will occur.
+ uint32_t* LockPixels();
+ // Release the image lock acquired by LockPixels().
+ void UnlockPixels() const;
+
+ // Flushes its contents of |pixel_buffer_| to the 2D graphics context. The
+ // ComputePi() thread fills in |pixel_buffer_| pixels as it computes Pi.
+ // This method is called by HandleMessage when a message containing 'paint'
+ // is received. Echos the current value of pi as computed by the Monte Carlo
+ // method by posting the value back to the browser.
+ void Paint();
+
+ bool quit() const {
+ return quit_;
+ }
+
+ // |pi_| is computed in the ComputePi() thread.
+ double pi() const {
+ return pi_;
+ }
+
+ int width() const {
+ return pixel_buffer_ ? pixel_buffer_->size().width() : 0;
+ }
+ int height() const {
+ return pixel_buffer_ ? pixel_buffer_->size().height() : 0;
+ }
+
+ // Indicate whether a flush is pending. This can only be called from the
+ // main thread; it is not thread safe.
+ bool flush_pending() const {
+ return flush_pending_;
+ }
+ void set_flush_pending(bool flag) {
+ flush_pending_ = flag;
+ }
+
+ private:
+ // Create and initialize the 2D context used for drawing.
+ void CreateContext(const pp::Size& size);
+ // Destroy the 2D drawing context.
+ void DestroyContext();
+ // Push the pixels to the browser, then attempt to flush the 2D context. If
+ // there is a pending flush on the 2D context, then update the pixels only
+ // and do not flush.
+ void FlushPixelBuffer();
+
+ bool IsContextValid() const {
+ return graphics_2d_context_ != NULL;
+ }
+
+ mutable pthread_mutex_t pixel_buffer_mutex_;
+ pp::Graphics2D* graphics_2d_context_;
+ pp::ImageData* pixel_buffer_;
+ bool flush_pending_;
+ bool quit_;
+ pthread_t compute_pi_thread_;
+ double pi_;
+
+ // ComputePi() estimates Pi using Monte Carlo method and it is executed by a
+ // separate thread created in SetWindow(). ComputePi() puts kMaxPointCount
+ // points inside the square whose length of each side is 1.0, and calculates
+ // the ratio of the number of points put inside the inscribed quadrant divided
+ // by the total number of random points to get Pi/4.
+ static void* ComputePi(void* param);
+};
+
+} // namespace pi_generator
+
+#endif // EXAMPLES_PI_GENERATOR_PI_GENERATOR_H_
+
diff --git a/native_client_sdk/src/examples/pi_generator/pi_generator.html b/native_client_sdk/src/examples/pi_generator/pi_generator.html
new file mode 100644
index 0000000..f63e73d
--- /dev/null
+++ b/native_client_sdk/src/examples/pi_generator/pi_generator.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+ <!--
+ Copyright (c) 2011 The Native Client Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+ <head>
+ <title>Monte Carlo Estimate for Pi</title>
+ <script type="text/javascript">
+ var piGenerator = null;
+ var paintInterval = null;
+
+ // Start up the paint timer when the NaCl module has loaded.
+ function moduleDidLoad() {
+ piGenerator = document.getElementById('piGenerator');
+ paintInterval = setInterval('piGenerator.postMessage("paint")', 5);
+ }
+
+ // Handle a message coming from the NaCl module. The message payload is
+ // assumed to contain the current estimated value of Pi. Update the Pi
+ // text display with this value.
+ function handleMessage(message_event) {
+ document.form.pi.value = message_event.data;
+ }
+
+ function pageDidUnload() {
+ clearInterval(paintInterval);
+ }
+ </script>
+ </head>
+ <body id="bodyId" onunload="pageDidUnload()">
+ <h1>Monte Carlo Estimate for Pi</h1>
+ <p>
+ The Native Client module executed in this page creates a thread
+ that estimates pi (&pi;) using the Monte Carlo method.
+ The thread randomly puts 1,000,000,000 points
+ inside a square that shares two sides with a quarter circle (a quadrant).
+ Because the area of
+ the quadrant is r&#178;&pi;/4
+ and the area of
+ the square is r&#178;,
+ dividing the number of points inside the quadrant
+ by the number of points inside the square gives us
+ an estimate of &pi;/4.
+ The textbox under the square
+ shows the current estimate of &pi;.
+ </p>
+ <!-- Load the published .nexe. This includes the 'src' attribute which
+ shows how to load multi-architecture modules. Each entry in the "nexes"
+ object in the .nmf manifest file is a key-value pair: the key is the
+ runtime ('x86-32', 'x86-64', etc.); the value is a URL for the desired NaCl
+ module. To load the debug versions of your .nexes, set the 'src'
+ attribute to the _dbg.nmf version of the manifest file.
+
+ Note: The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
+ and a 'message' event listener attached. This wrapping method is used
+ instead of attaching the event listeners directly to the <EMBED> element to
+ ensure that the listeners are active before the NaCl module 'load' event
+ fires. This also allows you to use PPB_Messaging.PostMessage() (in C) or
+ pp::Instance.PostMessage() (in C++) from within the initialization code in
+ your NaCl module.
+ -->
+ <div id="listener">
+ <script type="text/javascript">
+ var listener = document.getElementById('listener')
+ listener.addEventListener('load', moduleDidLoad, true);
+ listener.addEventListener('message', handleMessage, true);
+ </script>
+
+ <embed name="nacl_module"
+ id="piGenerator"
+ width=200 height=200
+ src="pi_generator.nmf"
+ type="application/x-nacl" />
+ </div>
+ <br />
+ <form name="form">
+ <input type="text" size="15" name="pi" />
+ </form>
+ </body>
+</html>
diff --git a/native_client_sdk/src/examples/pi_generator/pi_generator_dbg.html b/native_client_sdk/src/examples/pi_generator/pi_generator_dbg.html
new file mode 100644
index 0000000..3308b15
--- /dev/null
+++ b/native_client_sdk/src/examples/pi_generator/pi_generator_dbg.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+ <!--
+ Copyright (c) 2011 The Native Client Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+ <head>
+ <title>Monte Carlo Estimate for Pi</title>
+ <script type="text/javascript">
+ var piGenerator = null;
+ var paintInterval = null;
+
+ // Start up the paint timer when the NaCl module has loaded.
+ function moduleDidLoad() {
+ piGenerator = document.getElementById('piGenerator');
+ paintInterval = setInterval('piGenerator.postMessage("paint")', 5);
+ }
+
+ // Handle a message coming from the NaCl module. The message payload is
+ // assumed to contain the current estimated value of Pi. Update the Pi
+ // text display with this value.
+ function handleMessage(message_event) {
+ document.form.pi.value = message_event.data;
+ }
+
+ function pageDidUnload() {
+ clearInterval(paintInterval);
+ }
+ </script>
+ </head>
+ <body id="bodyId" onunload="pageDidUnload()">
+ <h1>Monte Carlo Estimate for Pi</h1>
+ <p>
+ The Native Client module executed in this page creates a thread
+ that estimates pi (&pi;) using the Monte Carlo method.
+ The thread randomly puts 1,000,000,000 points
+ inside a square that shares two sides with a quarter circle (a quadrant).
+ Because the area of
+ the quadrant is r&#178;&pi;/4
+ and the area of
+ the square is r&#178;,
+ dividing the number of points inside the quadrant
+ by the number of points inside the square gives us
+ an estimate of &pi;/4.
+ The textbox under the square
+ shows the current estimate of &pi;.
+ </p>
+ <!-- Load the published .nexe. This includes the 'src' attribute which
+ shows how to load multi-architecture modules. Each entry in the "nexes"
+ object in the .nmf manifest file is a key-value pair: the key is the
+ runtime ('x86-32', 'x86-64', etc.); the value is a URL for the desired NaCl
+ module. To load the debug versions of your .nexes, set the 'src'
+ attribute to the _dbg.nmf version of the manifest file.
+
+ Note: The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
+ and a 'message' event listener attached. This wrapping method is used
+ instead of attaching the event listeners directly to the <EMBED> element to
+ ensure that the listeners are active before the NaCl module 'load' event
+ fires. This also allows you to use PPB_Messaging.PostMessage() (in C) or
+ pp::Instance.PostMessage() (in C++) from within the initialization code in
+ your NaCl module.
+ -->
+ <div id="listener">
+ <script type="text/javascript">
+ var listener = document.getElementById('listener')
+ listener.addEventListener('load', moduleDidLoad, true);
+ listener.addEventListener('message', handleMessage, true);
+ </script>
+
+ <embed name="nacl_module"
+ id="piGenerator"
+ width=200 height=200
+ src="pi_generator_dbg.nmf"
+ type="application/x-nacl" />
+ </div>
+ <br />
+ <form name="form">
+ <input type="text" size="15" name="pi" />
+ </form>
+ </body>
+</html>
diff --git a/native_client_sdk/src/examples/pi_generator/pi_generator_module.cc b/native_client_sdk/src/examples/pi_generator/pi_generator_module.cc
new file mode 100644
index 0000000..8477fbf
--- /dev/null
+++ b/native_client_sdk/src/examples/pi_generator/pi_generator_module.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <ppapi/cpp/module.h>
+
+#include "examples/pi_generator/pi_generator.h"
+
+namespace pi_generator {
+// The Module class. The browser calls the CreateInstance() method to create
+// an instance of your NaCl module on the web page. The browser creates a new
+// instance for each <embed> tag with type="application/x-nacl".
+class PiGeneratorModule : public pp::Module {
+ public:
+ PiGeneratorModule() : pp::Module() {}
+ virtual ~PiGeneratorModule() {}
+
+ // Create and return a PiGeneratorInstance object.
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new PiGenerator(instance);
+ }
+};
+} // namespace pi_generator
+
+// Factory function called by the browser when the module is first loaded.
+// The browser keeps a singleton of this module. It calls the
+// CreateInstance() method on the object you return to make instances. There
+// is one instance per <embed> tag on the page. This is the main binding
+// point for your NaCl module with the browser.
+namespace pp {
+Module* CreateModule() {
+ return new pi_generator::PiGeneratorModule();
+}
+} // namespace pp
diff --git a/native_client_sdk/src/examples/pong/build.scons b/native_client_sdk/src/examples/pong/build.scons
new file mode 100644
index 0000000..a438bb0
--- /dev/null
+++ b/native_client_sdk/src/examples/pong/build.scons
@@ -0,0 +1,40 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import make_nacl_env
+import os
+
+nacl_env = make_nacl_env.NaClEnvironment(
+ use_c_plus_plus_libs=True, nacl_platform=os.getenv('NACL_TARGET_PLATFORM'),
+ install_subdir='pong', lib_prefix='..')
+nacl_env.Append(
+ # Add a CPPPATH that enables the full-path #include directives, such as
+ # #include "examples/sine_synth/sine_synth.h"
+ CPPPATH=[os.path.dirname(os.path.dirname(os.getcwd()))],
+ # Strict ANSI compliance.
+ EXTRA_CCFLAGS=['-pedantic'],
+ )
+
+sources = ['pong.cc', 'pong_module.cc', 'view.cc']
+
+opt_nexes, dbg_nexes = nacl_env.AllNaClModules(sources, 'pong')
+
+# This target is used by the SDK build system to provide a prebuilt version
+# of the example in the SDK installer.
+nacl_env.InstallPrebuilt('pong')
+
+app_files = [
+ 'pong.html',
+ 'pong.nmf',
+ ]
+
+# Split the install of the .nexes from the other app sources so that the strip
+# action is applied to the .nexes only.
+install_nexes = nacl_env.NaClStrippedInstall(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=opt_nexes)
+install_app = nacl_env.Install(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=app_files)
+nacl_env.Alias('install', source=install_app + install_nexes)
diff --git a/native_client_sdk/src/examples/pong/pong.cc b/native_client_sdk/src/examples/pong/pong.cc
new file mode 100644
index 0000000..75ffc7c
--- /dev/null
+++ b/native_client_sdk/src/examples/pong/pong.cc
@@ -0,0 +1,414 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/pong/pong.h"
+
+#include <stdio.h>
+#include <cmath>
+#include <string>
+#include "examples/pong/view.h"
+#include "ppapi/c/pp_file_info.h"
+#include "ppapi/c/ppb_file_io.h"
+#include "ppapi/cpp/completion_callback.h"
+#include "ppapi/cpp/file_io.h"
+#include "ppapi/cpp/file_ref.h"
+#include "ppapi/cpp/file_system.h"
+#include "ppapi/cpp/input_event.h"
+#include "ppapi/cpp/rect.h"
+#include "ppapi/cpp/var.h"
+
+namespace {
+
+const uint32_t kSpaceBar = 0x20;
+const uint32_t kUpArrow = 0x26;
+const uint32_t kDownArrow = 0x28;
+
+const int32_t kMaxPointsAllowed = 256;
+const std::string kResetScoreMethodId = "resetScore";
+const uint32_t kUpdateDistance = 4;
+const int32_t kUpdateInterval = 17; // milliseconds
+
+} // namespace
+
+namespace pong {
+
+// Callbacks that are called asynchronously by the system as a result of various
+// pp::FileIO methods.
+namespace AsyncCallbacks {
+// Callback that is called as a result of pp::FileIO::Flush
+void FlushCallback(void*, int32_t) {
+}
+
+// Callback that is called as a result of pp::FileIO::Write
+void WriteCallback(void* data, int32_t bytes_written) {
+ if (bytes_written < 0)
+ return; // error
+ Pong* pong = static_cast<Pong*>(data);
+ pong->offset_ += bytes_written;
+ if (pong->offset_ == pong->bytes_buffer_.length()) {
+ pong->file_io_->Flush(pp::CompletionCallback(FlushCallback, NULL));
+ } else {
+ // Not all the bytes to be written have been written, so call
+ // pp::FileIO::Write again.
+ pong->file_io_->Write(pong->offset_, &pong->bytes_buffer_[pong->offset_],
+ pong->bytes_buffer_.length() - pong->offset_,
+ pp::CompletionCallback(WriteCallback, pong));
+ }
+}
+
+// Callback that is called as a result of pp::FileSystem::Open
+void FileSystemOpenCallback(void* data, int32_t result) {
+ if (result != PP_OK)
+ return;
+ Pong* pong = static_cast<Pong*>(data);
+ pong->UpdateScoreFromFile();
+}
+
+// Callback that is called as a result of pp::FileIO::Read
+void ReadCallback(void* data, int32_t bytes_read) {
+ if (bytes_read < 0)
+ return; // error
+ Pong* pong = static_cast<Pong*>(data);
+ pong->bytes_to_read_ -= bytes_read;
+ if (pong->bytes_to_read_ == 0) {
+ // File has been read to completion. Parse the bytes to get the scores.
+ pong->UpdateScoreFromBuffer();
+ } else {
+ pong->offset_ += bytes_read;
+ pong->file_io_->Read(pong->offset_,
+ &pong->bytes_buffer_[pong->offset_],
+ pong->bytes_to_read_,
+ pp::CompletionCallback(ReadCallback, pong));
+ }
+}
+
+// Callback that is called as a result of pp::FileIO::Query
+void QueryCallback(void* data, int32_t result) {
+ if (result != PP_OK)
+ return;
+ Pong* pong = static_cast<Pong*>(data);
+ pong->bytes_to_read_ = pong->file_info_.size;
+ pong->offset_ = 0;
+ pong->bytes_buffer_.resize(pong->bytes_to_read_);
+ pong->file_io_->Read(pong->offset_,
+ &pong->bytes_buffer_[0],
+ pong->bytes_to_read_,
+ pp::CompletionCallback(ReadCallback, pong));
+}
+
+// Callback that is called as a result of pp::FileIO::Open
+void FileOpenCallback(void*data, int32_t result) {
+ if (result != PP_OK) {
+ return;
+ }
+ Pong* pong = static_cast<Pong*>(data);
+ // Query the file in order to get the file size.
+ pong->file_io_->Query(&pong->file_info_, pp::CompletionCallback(QueryCallback,
+ pong));
+}
+
+// Callback that is called as a result of pp::Core::CallOnMainThread
+void UpdateCallback(void* data, int32_t /*result*/) {
+ Pong* pong = static_cast<Pong*>(data);
+ pong->Update();
+}
+
+} // namespace AsyncCallbacks
+
+class UpdateScheduler {
+ public:
+ UpdateScheduler(int32_t delay, Pong* pong)
+ : delay_(delay), pong_(pong) {}
+ ~UpdateScheduler() {
+ pp::Core* core = pp::Module::Get()->core();
+ core->CallOnMainThread(delay_, pp::CompletionCallback(
+ AsyncCallbacks::UpdateCallback, pong_));
+ }
+
+ private:
+ int32_t delay_; // milliseconds
+ Pong* pong_; // weak
+};
+
+Pong::Pong(PP_Instance instance)
+ : pp::Instance(instance),
+ bytes_to_read_(0),
+ offset_(0),
+ file_io_(NULL),
+ file_ref_(NULL),
+ file_system_(NULL),
+ view_(NULL),
+ delta_x_(0),
+ delta_y_(0),
+ player_score_(0),
+ computer_score_(0) {
+ // Request to receive input events.
+ RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_KEYBOARD);
+}
+
+Pong::~Pong() {
+ delete view_;
+ file_io_->Close();
+ delete file_io_;
+ delete file_ref_;
+ delete file_system_;
+}
+
+bool Pong::Init(uint32_t argc, const char* argn[], const char* argv[]) {
+ view_ = new View(this);
+ // Read the score from file.
+ file_system_ = new pp::FileSystem(this, PP_FILESYSTEMTYPE_LOCALPERSISTENT);
+ file_ref_ = new pp::FileRef(*file_system_, "/pong_score");
+ // We kick off a series of callbacks which open a file, query the file for
+ // it's length in bytes, read the file contents, and then update the score
+ // display based on the file contents.
+ int32_t rv = file_system_->Open(
+ 1024, pp::CompletionCallback(AsyncCallbacks::FileSystemOpenCallback,
+ this));
+ if (rv != PP_OK_COMPLETIONPENDING) {
+ PostMessage(pp::Var("ERROR: Could not open local persistent file system."));
+ return true;
+ }
+ UpdateScoreDisplay();
+ UpdateScheduler(kUpdateInterval, this);
+ return true;
+}
+
+void Pong::DidChangeView(const pp::Rect& position,
+ const pp::Rect& clip) {
+ pp::Size view_size = view_->GetSize();
+ const bool view_was_empty = view_size.IsEmpty();
+ view_->UpdateView(position, clip, this);
+ if (view_was_empty)
+ ResetPositions();
+}
+
+bool Pong::HandleInputEvent(const pp::InputEvent& event) {
+ if (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEUP) {
+ // By notifying the browser mouse clicks are handled, the application window
+ // is able to get focus and receive key events.
+ return true;
+ } else if (event.GetType() == PP_INPUTEVENT_TYPE_KEYUP) {
+ pp::KeyboardInputEvent key = pp::KeyboardInputEvent(event);
+ return view_->KeyUp(key);
+ } else if (event.GetType() == PP_INPUTEVENT_TYPE_KEYDOWN) {
+ pp::KeyboardInputEvent key = pp::KeyboardInputEvent(event);
+ return view_->KeyDown(key);
+ }
+ return false;
+}
+
+
+void Pong::HandleMessage(const pp::Var& var_message) {
+ if (!var_message.is_string())
+ return;
+ std::string message = var_message.AsString();
+ if (message == kResetScoreMethodId) {
+ ResetScore();
+ }
+}
+
+void Pong::Update() {
+ // Schedule another update
+ UpdateScheduler(kUpdateInterval, this);
+
+ const uint32_t key_code = view_->last_key_code();
+ if (key_code == kSpaceBar) {
+ ResetPositions();
+ return;
+ }
+ if (ball_.right() < court_.x()) {
+ ++computer_score_;
+ if (computer_score_ > kMaxPointsAllowed) {
+ ResetScore();
+ } else {
+ WriteScoreToFile();
+ UpdateScoreDisplay();
+ }
+ ResetPositions();
+ return;
+ }
+ if (ball_.x() > court_.right()) {
+ ++player_score_;
+ if (player_score_ > kMaxPointsAllowed) {
+ ResetScore();
+ } else {
+ WriteScoreToFile();
+ UpdateScoreDisplay();
+ }
+ ResetPositions();
+ return;
+ }
+ // Update human controlled paddle
+ if (key_code == kUpArrow) {
+ left_paddle_.Offset(0, -kUpdateDistance);
+ if (left_paddle_.y() - 1 < court_.y()) {
+ left_paddle_.Offset(0, court_.y() - left_paddle_.y() + 1);
+ }
+ } else if (key_code == kDownArrow) {
+ left_paddle_.Offset(0, kUpdateDistance);
+ if (left_paddle_.bottom() + 1 > court_.bottom()) {
+ left_paddle_.Offset(0, court_.bottom() - left_paddle_.bottom() - 1);
+ }
+ }
+
+ // Update AI controlled paddle
+ BallDirection direction = RightPaddleNextMove();
+ if (direction == kUpDirection) {
+ right_paddle_.Offset(0, -kUpdateDistance);
+ if (right_paddle_.y() < court_.y() + 1) {
+ right_paddle_.Offset(0, court_.y() - right_paddle_.y() + 1);
+ }
+ } else if (direction == kDownDirection) {
+ right_paddle_.Offset(0, kUpdateDistance);
+ if (right_paddle_.bottom() > court_.bottom() - 1) {
+ right_paddle_.Offset(0, court_.bottom() - right_paddle_.bottom() - 1);
+ }
+ }
+
+ // Bounce ball off bottom of screen
+ if (ball_.bottom() >= court_.bottom() - 1) {
+ ball_.Offset(0, court_.bottom() - ball_.bottom() - 1);
+ delta_y_ = -delta_y_;
+ }
+ // Bounce ball off top of screen
+ if (ball_.y() <= court_.y() + 1) {
+ ball_.Offset(0, court_.y() - ball_.y() + 1);
+ delta_y_ = -delta_y_;
+ }
+ // Bounce ball off human controlled paddle
+ if (left_paddle_.Intersects(ball_)) {
+ delta_x_ = abs(delta_x_);
+ if (ball_.CenterPoint().y() <
+ left_paddle_.y() + left_paddle_.height() / 5) {
+ delta_y_ -= kUpdateDistance;
+ if (delta_y_ == 0)
+ delta_y_ = -kUpdateDistance;
+ } else if (ball_.CenterPoint().y() >
+ left_paddle_.bottom() - left_paddle_.height() / 5) {
+ delta_y_ += kUpdateDistance;
+ if (delta_y_ == 0)
+ delta_y_ = kUpdateDistance;
+ }
+ }
+ // Bounce ball off ai controlled paddle
+ if (right_paddle_.Intersects(ball_)) {
+ delta_x_ = -abs(delta_x_);
+ if (ball_.CenterPoint().y() >
+ right_paddle_.bottom() - right_paddle_.height() / 5) {
+ delta_y_ += kUpdateDistance;
+ if (delta_y_ == 0)
+ delta_y_ = kUpdateDistance;
+ } else if (ball_.CenterPoint().y() <
+ right_paddle_.y() + right_paddle_.height() / 5) {
+ delta_y_ -= kUpdateDistance;
+ if (delta_y_ == 0)
+ delta_y_ -= kUpdateDistance;
+ }
+ }
+
+ // Move ball
+ ball_.Offset(delta_x_, delta_y_);
+
+ view_->set_ball_rect(ball_);
+ view_->set_left_paddle_rect(left_paddle_);
+ view_->set_right_paddle_rect(right_paddle_);
+ view_->Draw();
+}
+
+void Pong::UpdateScoreFromBuffer() {
+ size_t pos = bytes_buffer_.find_first_of(':');
+ player_score_ = ::atoi(bytes_buffer_.substr(0, pos).c_str());
+ computer_score_ = ::atoi(bytes_buffer_.substr(pos + 1).c_str());
+ UpdateScoreDisplay();
+}
+
+void Pong::UpdateScoreFromFile() {
+ file_io_ = new pp::FileIO(this);
+ file_io_->Open(*file_ref_,
+ PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_WRITE |
+ PP_FILEOPENFLAG_CREATE,
+ pp::CompletionCallback(AsyncCallbacks::FileOpenCallback,
+ this));
+}
+
+void Pong::WriteScoreToFile() {
+ if (file_io_ == NULL)
+ return;
+ // Write the score in <player score>:<computer score> format.
+ size_t score_string_length = 1 + (player_score_ ? log(player_score_) : 1) + 1
+ + (computer_score_ ? log(computer_score_) : 1) + 1;
+ bytes_buffer_.resize(score_string_length);
+ snprintf(&bytes_buffer_[0], bytes_buffer_.length(), "%i:%i", player_score_,
+ computer_score_);
+ offset_ = 0; // overwrite score in file.
+ file_io_->Write(offset_,
+ bytes_buffer_.c_str(),
+ bytes_buffer_.length(),
+ pp::CompletionCallback(AsyncCallbacks::WriteCallback, this));
+}
+
+void Pong::ResetPositions() {
+ pp::Size court_size = view_->GetSize();
+ pp::Rect court_rect(court_size);
+ court_.SetRect(court_rect);
+
+ pp::Rect rect;
+ rect.set_width(20);
+ rect.set_height(20);
+ rect.set_x((court_rect.x() + court_rect.width()) / 2 - rect.width() / 2);
+ rect.set_y(court_rect.y() + court_rect.height() - rect.height());
+ ball_.SetRect(rect);
+
+ const float paddle_width = 10;
+ const float paddle_height = 99;
+ const float paddle_pos_y =
+ (court_rect.y() + court_rect.height()) / 2 - rect.height() / 2;
+ rect.set_width(paddle_width);
+ rect.set_height(paddle_height);
+ rect.set_x(court_rect.x() + court_rect.width() / 5 + 1);
+ rect.set_y(paddle_pos_y);
+ left_paddle_.SetRect(rect);
+
+ rect.set_width(paddle_width);
+ rect.set_height(paddle_height);
+ rect.set_x(court_rect.x() + 4 * court_rect.width() / 5 - rect.width() - 1);
+ rect.set_y(paddle_pos_y);
+ right_paddle_.SetRect(rect);
+
+ delta_x_ = delta_y_ = kUpdateDistance;
+}
+
+void Pong::ResetScore() {
+ player_score_ = 0;
+ computer_score_ = 0;
+ UpdateScoreDisplay();
+}
+
+Pong::BallDirection Pong::RightPaddleNextMove() const {
+ static int32_t last_ball_y = 0;
+ int32_t ball_y = ball_.CenterPoint().y();
+ BallDirection ball_direction =
+ ball_y < last_ball_y ? kUpDirection : kDownDirection;
+ last_ball_y = ball_y;
+
+ if (ball_y < right_paddle_.y())
+ return kUpDirection;
+ if (ball_y > right_paddle_.bottom())
+ return kDownDirection;
+ return ball_direction;
+}
+
+void Pong::UpdateScoreDisplay() {
+ if (file_io_ == NULL)
+ return; // Since we cant't save the score to file, do nothing and return.
+ size_t message_length = 18 + (player_score_ ? log(player_score_) : 1) + 1
+ + (computer_score_ ? log(computer_score_) : 1) + 1;
+ std::string score_message(message_length, '\0');
+ snprintf(&score_message[0], score_message.length(),
+ "You: %i Computer: %i", player_score_, computer_score_);
+ PostMessage(pp::Var(score_message));
+}
+
+} // namespace pong
diff --git a/native_client_sdk/src/examples/pong/pong.h b/native_client_sdk/src/examples/pong/pong.h
new file mode 100644
index 0000000..8f38e3e
--- /dev/null
+++ b/native_client_sdk/src/examples/pong/pong.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_PONG_PONG_H_
+#define EXAMPLES_PONG_PONG_H_
+
+#include <string>
+
+#include "ppapi/c/pp_file_info.h"
+#include "ppapi/cpp/graphics_2d.h"
+#include "ppapi/cpp/image_data.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/rect.h"
+#include "ppapi/cpp/size.h"
+
+namespace pp {
+class FileIO;
+class FileRef;
+class FileSystem;
+class Rect;
+} // namespace pp
+
+namespace pong {
+
+class View;
+
+// The Instance class. One of these exists for each instance of your NaCl
+// module on the web page. The browser will ask the Module object to create
+// a new Instance for each occurrence of the <embed> tag that has these
+// attributes:
+// type="application/x-nacl"
+// nacl="pong.nmf"
+class Pong : public pp::Instance {
+ public:
+ explicit Pong(PP_Instance instance);
+ virtual ~Pong();
+
+ // Open the file (if available) that stores the game scores.
+ virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]);
+
+ // Update the graphics context to the new size, and regenerate |pixel_buffer_|
+ // to fit the new size as well.
+ virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip);
+
+ virtual bool HandleInputEvent(const pp::InputEvent& event);
+
+ // Called by the browser to handle the postMessage() call in Javascript.
+ // The message in this case is expected to contain the string 'update', or
+ // 'resetScore' in order to invoke either the Update or ResetScore function
+ // respectively.
+ virtual void HandleMessage(const pp::Var& var_message);
+
+ void Update();
+ void UpdateScoreFromBuffer();
+ void UpdateScoreFromFile();
+ void WriteScoreToFile();
+
+ PP_FileInfo file_info_;
+ int32_t bytes_to_read_;
+ int64_t offset_;
+ pp::FileIO* file_io_;
+ pp::FileRef* file_ref_;
+ pp::FileSystem* file_system_;
+ std::string bytes_buffer_;
+
+ private:
+ Pong(const Pong&); // Disallow copy
+
+ enum BallDirection {
+ kUpDirection = 0,
+ kDownDirection
+ };
+ void ResetPositions();
+ void ResetScore();
+ BallDirection RightPaddleNextMove() const;
+ void UpdateScoreDisplay();
+
+ View* view_;
+ pp::Rect left_paddle_;
+ pp::Rect right_paddle_;
+ pp::Rect ball_;
+ pp::Rect court_;
+ int32_t delta_x_;
+ int32_t delta_y_;
+ int player_score_;
+ int computer_score_;
+};
+
+} // namespace pong
+
+#endif // EXAMPLES_PONG_PONG_H_
diff --git a/native_client_sdk/src/examples/pong/pong.html b/native_client_sdk/src/examples/pong/pong.html
new file mode 100644
index 0000000..59a5915
--- /dev/null
+++ b/native_client_sdk/src/examples/pong/pong.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+ <!--
+ Copyright (c) 2011 The Native Client Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+ <head>
+ <title>Pong</title>
+ <script type="text/javascript">
+ var pong = null;
+ var paintInterval = null;
+
+ // Handle a message coming from the NaCl module. The message payload is
+ // assumed to contain the current game score. Update the score text
+ // display with this value.
+ // Note that errors are also sent to this handler. A message starting
+ // with 'ERROR' is considered an error, all other strings are assumed
+ // to be scores.
+ function handleMessage(message_event) {
+ if (message_event.data.indexOf('ERROR') == 0) {
+ document.getElementById('error_log').innerHTML = message_event.data;
+ } else {
+ document.getElementById('score').innerHTML = message_event.data;
+ }
+ }
+
+ function pageDidUnload() {
+ clearInterval(paintInterval);
+ }
+ function resetScore() {
+ pong = document.getElementById('pong');
+ pong.postMessage("resetScore");
+ }
+ </script>
+ </head>
+ <body id="bodyId" onunload="pageDidUnload()">
+ <h1>Pong</h1>
+ <!-- Load the published .nexe. This includes the 'src' attribute which
+ shows how to load multi-architecture modules. Each entry in the "nexes"
+ object in the .nmf manifest file is a key-value pair: the key is the
+ runtime ('x86-32', 'x86-64', etc.); the value is a URL for the desired NaCl
+ module. To load the debug versions of your .nexes, set the 'src'
+ attribute to the _dbg.nmf version of the manifest file.
+
+ Note: The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
+ and a 'message' event listener attached. This wrapping method is used
+ instead of attaching the event listeners directly to the <EMBED> element to
+ ensure that the listeners are active before the NaCl module 'load' event
+ fires. This also allows you to use PPB_Messaging.PostMessage() (in C) or
+ pp::Instance.PostMessage() (in C++) from within the initialization code in
+ your NaCl module.
+ -->
+ <div id="listener">
+ <script type="text/javascript">
+ window.webkitStorageInfo.requestQuota(PERSISTENT, 1024);
+ var listener = document.getElementById('listener')
+ listener.addEventListener('message', handleMessage, true);
+ </script>
+
+ <embed name="nacl_module"
+ id="pong"
+ width=800 height=600
+ src="pong.nmf"
+ type="application/x-nacl" />
+ </div>
+ <br />
+ <p id="score">
+ </p>
+ <button onclick="resetScore()">Reset score</button>
+ <p id="error_log"></p>
+ </body>
+</html>
diff --git a/native_client_sdk/src/examples/pong/pong_module.cc b/native_client_sdk/src/examples/pong/pong_module.cc
new file mode 100644
index 0000000..c74ea28
--- /dev/null
+++ b/native_client_sdk/src/examples/pong/pong_module.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <ppapi/cpp/module.h>
+
+#include "examples/pong/pong.h"
+
+namespace pong {
+// The Module class. The browser calls the CreateInstance() method to create
+// an instance of your NaCl module on the web page. The browser creates a new
+// instance for each <embed> tag with type="application/x-nacl".
+class PongModule : public pp::Module {
+ public:
+ PongModule() : pp::Module() {}
+ virtual ~PongModule() {}
+
+ // Create and return a PiGeneratorInstance object.
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new Pong(instance);
+ }
+};
+} // namespace pong
+
+// Factory function called by the browser when the module is first loaded.
+// The browser keeps a singleton of this module. It calls the
+// CreateInstance() method on the object you return to make instances. There
+// is one instance per <embed> tag on the page. This is the main binding
+// point for your NaCl module with the browser.
+namespace pp {
+Module* CreateModule() {
+ return new pong::PongModule();
+}
+} // namespace pp
diff --git a/native_client_sdk/src/examples/pong/view.cc b/native_client_sdk/src/examples/pong/view.cc
new file mode 100644
index 0000000..4bb11b9
--- /dev/null
+++ b/native_client_sdk/src/examples/pong/view.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/pong/view.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "ppapi/cpp/completion_callback.h"
+#include "ppapi/cpp/graphics_2d.h"
+#include "ppapi/cpp/image_data.h"
+#include "ppapi/cpp/input_event.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/point.h"
+#include "ppapi/cpp/var.h"
+
+// Input event key codes. PPAPI uses Windows Virtual key codes.
+const uint32_t kSpaceBar = 0x20;
+const uint32_t kUpArrow = 0x26;
+const uint32_t kDownArrow = 0x28;
+
+namespace {
+
+const uint32_t kOpaqueColorMask = 0xff000000; // Opaque pixels.
+const uint32_t kWhiteMask = 0xffffff;
+
+// This is called by the browser when the 2D context has been flushed to the
+// browser window.
+void FlushCallback(void* data, int32_t result) {
+ static_cast<pong::View*>(data)->set_flush_pending(false);
+}
+
+} // namespace
+
+namespace pong {
+View::View(pp::Instance* instance)
+ : instance_(instance), last_key_code_(0x0), flush_pending_(false),
+ graphics_2d_context_(NULL), pixel_buffer_(NULL) {}
+
+View::~View() {
+ DestroyContext();
+ delete pixel_buffer_;
+}
+
+pp::Size View::GetSize() const {
+ pp::Size size;
+ if (graphics_2d_context_) {
+ size.SetSize(graphics_2d_context_->size().width(),
+ graphics_2d_context_->size().height());
+ }
+ return size;
+}
+
+bool View::KeyDown(const pp::KeyboardInputEvent& key) {
+ last_key_code_ = key.GetKeyCode();
+ if (last_key_code_ == kSpaceBar || last_key_code_ == kUpArrow ||
+ last_key_code_ == kDownArrow)
+ return true;
+ return false;
+}
+
+bool View::KeyUp(const pp::KeyboardInputEvent& key) {
+ if (last_key_code_ == key.GetKeyCode()) {
+ last_key_code_ = 0x0; // Indicates key code is not set.
+ }
+ return false;
+}
+
+void View::Draw() {
+ uint32_t* pixels = static_cast<uint32_t*>(pixel_buffer_->data());
+ if (NULL == pixels)
+ return;
+ // Clear the buffer
+ const int32_t height = pixel_buffer_->size().height();
+ const int32_t width = pixel_buffer_->size().width();
+ for (int32_t py = 0; py < height; ++py) {
+ for (int32_t px = 0; px < width; ++px) {
+ const int32_t pos = px + py * width;
+ uint32_t color = kOpaqueColorMask;
+ // Draw the paddles
+ if (left_paddle_rect_.Contains(px, py) ||
+ right_paddle_rect_.Contains(px, py)) {
+ color |= kWhiteMask;
+ } else {
+ pp::Point center_point = ball_rect_.CenterPoint();
+ float radius = ball_rect_.width() / 2;
+ float distance_x = px - center_point.x();
+ float distance_y = py - center_point.y();
+ float distance =
+ sqrt(distance_x * distance_x + distance_y * distance_y);
+ // Draw the ball
+ if (distance <= radius)
+ color |= kWhiteMask;
+ }
+ pixels[pos] = color;
+ }
+ }
+
+ FlushPixelBuffer();
+}
+
+void View::UpdateView(const pp::Rect& position,
+ const pp::Rect& clip,
+ pp::Instance* instance) {
+ const int32_t width =
+ pixel_buffer_ ? pixel_buffer_->size().width() : 0;
+ const int32_t height =
+ pixel_buffer_ ? pixel_buffer_->size().height() : 0;
+
+ if (position.size().width() == width &&
+ position.size().height() == height)
+ return; // Size didn't change, no need to update anything.
+
+ // Create a new device context with the new size.
+ DestroyContext();
+ CreateContext(position.size(), instance);
+ // Delete the old pixel buffer and create a new one.
+ delete pixel_buffer_;
+ pixel_buffer_ = NULL;
+ if (graphics_2d_context_ != NULL) {
+ pixel_buffer_ = new pp::ImageData(instance, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
+ graphics_2d_context_->size(),
+ false);
+ }
+}
+
+void View::CreateContext(const pp::Size& size, pp::Instance* instance) {
+ if (IsContextValid())
+ return;
+ graphics_2d_context_ = new pp::Graphics2D(instance, size,
+ false);
+ if (!instance->BindGraphics(*graphics_2d_context_)) {
+ instance_->PostMessage(pp::Var("ERROR: Couldn't bind the device context"));
+ }
+}
+
+void View::DestroyContext() {
+ if (!IsContextValid())
+ return;
+ delete graphics_2d_context_;
+ graphics_2d_context_ = NULL;
+}
+
+void View::FlushPixelBuffer() {
+ if (!IsContextValid())
+ return;
+ // Note that the pixel lock is held while the buffer is copied into the
+ // device context and then flushed.
+ graphics_2d_context_->PaintImageData(*pixel_buffer_, pp::Point());
+ if (flush_pending_)
+ return;
+ flush_pending_ = true;
+ graphics_2d_context_->Flush(pp::CompletionCallback(&FlushCallback, this));
+}
+
+bool View::IsContextValid() const {
+ return graphics_2d_context_ != NULL;
+}
+
+} // namespace pong
diff --git a/native_client_sdk/src/examples/pong/view.h b/native_client_sdk/src/examples/pong/view.h
new file mode 100644
index 0000000..269ff99
--- /dev/null
+++ b/native_client_sdk/src/examples/pong/view.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_PONG_VIEW_H_
+#define EXAMPLES_PONG_VIEW_H_
+
+#include "ppapi/cpp/rect.h"
+
+namespace pp {
+class Graphics2D;
+class ImageData;
+class Instance;
+class KeyboardInputEvent;
+class Rect;
+class Size;
+} // namespace pp
+
+namespace pong {
+
+class View {
+ public:
+ explicit View(pp::Instance* instance);
+ ~View();
+
+ const uint32_t& last_key_code() const {
+ return last_key_code_;
+ }
+ void set_left_paddle_rect(const pp::Rect& left_paddle_rect) {
+ left_paddle_rect_ = left_paddle_rect;
+ }
+ void set_right_paddle_rect(const pp::Rect& right_paddle_rect) {
+ right_paddle_rect_ = right_paddle_rect;
+ }
+ void set_ball_rect(const pp::Rect& ball_rect) {
+ ball_rect_ = ball_rect;
+ }
+ void set_flush_pending(bool flush_pending) {
+ flush_pending_ = flush_pending;
+ }
+ pp::Size GetSize() const;
+ bool KeyDown(const pp::KeyboardInputEvent& key);
+ bool KeyUp(const pp::KeyboardInputEvent& key);
+ void Draw();
+ void UpdateView(const pp::Rect& position,
+ const pp::Rect& clip,
+ pp::Instance* instance);
+
+ private:
+ pp::Instance* const instance_; // weak
+ // Create and initialize the 2D context used for drawing.
+ void CreateContext(const pp::Size& size, pp::Instance* instance);
+ // Destroy the 2D drawing context.
+ void DestroyContext();
+ // Push the pixels to the browser, then attempt to flush the 2D context. If
+ // there is a pending flush on the 2D context, then update the pixels only
+ // and do not flush.
+ void FlushPixelBuffer();
+ bool IsContextValid() const;
+
+ uint32_t last_key_code_;
+ // Geometry for drawing
+ pp::Rect left_paddle_rect_;
+ pp::Rect right_paddle_rect_;
+ pp::Rect ball_rect_;
+ // Drawing stuff
+ bool flush_pending_;
+ pp::Graphics2D* graphics_2d_context_;
+ pp::ImageData* pixel_buffer_;
+};
+
+
+} // namespace pong
+
+#endif // EXAMPLES_PONG_VIEW_H_
diff --git a/native_client_sdk/src/examples/scons b/native_client_sdk/src/examples/scons
new file mode 100755
index 0000000..7742fd7
--- /dev/null
+++ b/native_client_sdk/src/examples/scons
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+readonly SCRIPT_DIR="$(dirname "$0")"
+readonly SCRIPT_DIR_ABS="$(cd "${SCRIPT_DIR}" ; pwd -P)"
+
+# NACL_SDK_ROOT must be set.
+if [ x"${NACL_SDK_ROOT}"x == "xx" ] ; then
+ echo "Error: NACL_SDK_ROOT is not set."
+ exit 1;
+fi
+
+# NACL_TARGET_PLATFORM is really the name of a folder with the base dir -
+# usually NACL_SDK_ROOT - within which the toolchain for the target platform
+# are found.
+# Replace the platform with the name of your target platform. For example, to
+# build applications that target the pepper_17 API, set
+# NACL_TARGET_PLATFORM="pepper_17"
+if [ x"${NACL_TARGET_PLATFORM}"x == "xx" ] ; then
+ export NACL_TARGET_PLATFORM="pepper_17"
+fi
+
+readonly NACL_PLATFORM_DIR="${NACL_SDK_ROOT}/${NACL_TARGET_PLATFORM}"
+readonly BASE_SCRIPT="${NACL_PLATFORM_DIR}/third_party/scons-2.0.1/script/scons"
+
+export SCONS_LIB_DIR="${NACL_PLATFORM_DIR}/third_party/scons-2.0.1/engine"
+export PYTHONPATH="${NACL_PLATFORM_DIR}/third_party/scons-2.0.1/engine"
+# We have to do this because scons overrides PYTHONPATH and does not preserve
+# what is provided by the OS. The custom variable name won't be overwritten.
+export PYMOX="${NACL_PLATFORM_DIR}/third_party/pymox"
+
+"${BASE_SCRIPT}" --file=build.scons \
+ --site-dir="${NACL_PLATFORM_DIR}/build_tools/nacl_sdk_scons" \
+ $*
+
diff --git a/native_client_sdk/src/examples/scons.bat b/native_client_sdk/src/examples/scons.bat
new file mode 100755
index 0000000..2a67758
--- /dev/null
+++ b/native_client_sdk/src/examples/scons.bat
@@ -0,0 +1,43 @@
+@echo off
+
+:: Copyright (c) 2011 The Native Client Authors. All rights reserved.
+:: Use of this source code is governed by a BSD-style license that can be
+:: found in the LICENSE file.
+
+setlocal
+
+:: NACL_SDK_ROOT must be set.
+if not defined NACL_SDK_ROOT (
+ echo Error: NACL_SDK_ROOT is not set.
+ echo Please set NACL_SDK_ROOT to the full path of the Native Client SDK.
+ echo For example:
+ echo set NACL_SDK_ROOT=D:\nacl_sdk
+ goto end
+)
+
+:: NACL_TARGET_PLATFORM is really the name of a folder with the base dir -
+:: usually NACL_SDK_ROOT - within which the toolchain for the target platform
+:: are found.
+:: Replace the platform with the name of your target platform. For example, to
+:: build applications that target the pepper_17 API, set
+:: NACL_TARGET_PLATFORM=pepper_17
+if not defined NACL_TARGET_PLATFORM (
+ set NACL_TARGET_PLATFORM=pepper_17
+)
+
+set NACL_PLATFORM_DIR=%NACL_SDK_ROOT%\%NACL_TARGET_PLATFORM%
+
+set SCONS_LIB_DIR=%NACL_PLATFORM_DIR%\third_party\scons-2.0.1\engine
+set PYTHONPATH=%NACL_PLATFORM_DIR%\third_party\scons-2.0.1\engine
+
+:: We have to do this because scons overrides PYTHONPATH and does not preserve
+:: what is provided by the OS. The custom variable name won't be overwritten.
+set PYMOX=%NACL_PLATFORM_DIR%\third_party\pymox
+
+:: Run the included copy of scons.
+python -O -OO "%NACL_PLATFORM_DIR%\third_party\scons-2.0.1\script\scons" ^
+--warn no-visual-c-missing ^
+--file=build.scons ^
+--site-dir="%NACL_PLATFORM_DIR%\build_tools\nacl_sdk_scons" %*
+
+:end
diff --git a/native_client_sdk/src/examples/sine_synth/build.scons b/native_client_sdk/src/examples/sine_synth/build.scons
new file mode 100644
index 0000000..5b10dc8
--- /dev/null
+++ b/native_client_sdk/src/examples/sine_synth/build.scons
@@ -0,0 +1,40 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import make_nacl_env
+import os
+
+nacl_env = make_nacl_env.NaClEnvironment(
+ use_c_plus_plus_libs=True, nacl_platform=os.getenv('NACL_TARGET_PLATFORM'),
+ install_subdir='sine_synth', lib_prefix='..')
+nacl_env.Append(
+ # Add a CPPPATH that enables the full-path #include directives, such as
+ # #include "examples/sine_synth/sine_synth.h"
+ CPPPATH=[os.path.dirname(os.path.dirname(os.getcwd()))],
+ # Strict ANSI compliance.
+ EXTRA_CCFLAGS=['-pedantic'],
+ )
+
+sources = ['sine_synth.cc']
+
+opt_nexes, dbg_nexes = nacl_env.AllNaClModules(sources, 'sine_synth')
+
+# This target is used by the SDK build system to provide a prebuilt version
+# of the example in the SDK installer.
+nacl_env.InstallPrebuilt('sine_synth')
+
+app_files = [
+ 'sine_synth.html',
+ 'sine_synth.nmf',
+ ]
+
+# Split the install of the .nexes from the other app sources so that the strip
+# action is applied to the .nexes only.
+install_nexes = nacl_env.NaClStrippedInstall(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=opt_nexes)
+install_app = nacl_env.Install(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=app_files)
+nacl_env.Alias('install', source=install_app + install_nexes)
diff --git a/native_client_sdk/src/examples/sine_synth/sine_synth.cc b/native_client_sdk/src/examples/sine_synth/sine_synth.cc
new file mode 100644
index 0000000..2305020
--- /dev/null
+++ b/native_client_sdk/src/examples/sine_synth/sine_synth.cc
@@ -0,0 +1,183 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <sstream>
+#include "ppapi/cpp/audio.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/cpp/var.h"
+
+namespace {
+const char* const kPlaySoundId = "playSound";
+const char* const kStopSoundId = "stopSound";
+const char* const kSetFrequencyId = "setFrequency";
+static const char kMessageArgumentSeparator = ':';
+
+const double kDefaultFrequency = 440.0;
+const double kPi = 3.141592653589;
+const double kTwoPi = 2.0 * kPi;
+// The sample count we will request.
+const uint32_t kSampleFrameCount = 4096u;
+// Only supporting stereo audio for now.
+const uint32_t kChannels = 2u;
+} // namespace
+
+namespace sine_synth {
+// The Instance class. One of these exists for each instance of your NaCl
+// module on the web page. The browser will ask the Module object to create
+// a new Instance for each occurrence of the <embed> tag that has these
+// attributes:
+// type="application/x-nacl"
+// src="sine_synth.nmf"
+class SineSynthInstance : public pp::Instance {
+ public:
+ explicit SineSynthInstance(PP_Instance instance)
+ : pp::Instance(instance),
+ frequency_(kDefaultFrequency),
+ theta_(0),
+ sample_frame_count_(kSampleFrameCount) {}
+ virtual ~SineSynthInstance() {}
+
+ // Called by the browser once the NaCl module is loaded and ready to
+ // initialize. Creates a Pepper audio context and initializes it. Returns
+ // true on success. Returning false causes the NaCl module to be deleted and
+ // no other functions to be called.
+ virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]);
+
+ // Called by the browser to handle the postMessage() call in Javascript.
+ // |var_message| is expected to be a string that contains the name of the
+ // method to call. Note that the setFrequency method takes a single
+ // parameter, the frequency. The frequency parameter is encoded as a string
+ // and appended to the 'setFrequency' method name after a ':'. Examples
+ // of possible message strings are:
+ // playSound
+ // stopSound
+ // setFrequency:880
+ // If |var_message| is not a recognized method name, this method does nothing.
+ virtual void HandleMessage(const pp::Var& var_message);
+
+ // Set the frequency of the sine wave to |frequency|. Posts a message back
+ // to the browser with the new frequency value.
+ void SetFrequency(double frequency);
+
+ // The frequency property accessor.
+ double frequency() const { return frequency_; }
+
+ private:
+ static void SineWaveCallback(void* samples,
+ uint32_t buffer_size,
+ void* data) {
+ SineSynthInstance* sine_synth_instance =
+ reinterpret_cast<SineSynthInstance*>(data);
+ const double frequency = sine_synth_instance->frequency();
+ const double delta = kTwoPi * frequency / PP_AUDIOSAMPLERATE_44100;
+ const int16_t max_int16 = std::numeric_limits<int16_t>::max();
+
+ int16_t* buff = reinterpret_cast<int16_t*>(samples);
+
+ // Make sure we can't write outside the buffer.
+ assert(buffer_size >= (sizeof(*buff) * kChannels *
+ sine_synth_instance->sample_frame_count_));
+
+ for (size_t sample_i = 0;
+ sample_i < sine_synth_instance->sample_frame_count_;
+ ++sample_i, sine_synth_instance->theta_ += delta) {
+ // Keep theta_ from going beyond 2*Pi.
+ if (sine_synth_instance->theta_ > kTwoPi) {
+ sine_synth_instance->theta_ -= kTwoPi;
+ }
+ double sin_value(std::sin(sine_synth_instance->theta_));
+ int16_t scaled_value = static_cast<int16_t>(sin_value * max_int16);
+ for (size_t channel = 0; channel < kChannels; ++channel) {
+ *buff++ = scaled_value;
+ }
+ }
+ }
+
+ pp::Audio audio_;
+ double frequency_;
+
+ // The last parameter sent to the sin function. Used to prevent sine wave
+ // skips on buffer boundaries.
+ double theta_;
+
+ // The count of sample frames per channel in an audio buffer.
+ uint32_t sample_frame_count_;
+};
+
+bool SineSynthInstance::Init(uint32_t argc,
+ const char* argn[],
+ const char* argv[]) {
+ // Ask the device for an appropriate sample count size.
+ sample_frame_count_ =
+ pp::AudioConfig::RecommendSampleFrameCount(PP_AUDIOSAMPLERATE_44100,
+ kSampleFrameCount);
+ audio_ = pp::Audio(this,
+ pp::AudioConfig(this,
+ PP_AUDIOSAMPLERATE_44100,
+ sample_frame_count_),
+ SineWaveCallback,
+ this);
+ return true;
+}
+
+void SineSynthInstance::HandleMessage(const pp::Var& var_message) {
+ if (!var_message.is_string()) {
+ return;
+ }
+ std::string message = var_message.AsString();
+ if (message == kPlaySoundId) {
+ audio_.StartPlayback();
+ } else if (message == kStopSoundId) {
+ audio_.StopPlayback();
+ } else if (message.find(kSetFrequencyId) == 0) {
+ // The argument to setFrequency is everything after the first ':'.
+ size_t sep_pos = message.find_first_of(kMessageArgumentSeparator);
+ if (sep_pos != std::string::npos) {
+ std::string string_arg = message.substr(sep_pos + 1);
+ // Got the argument value as a string: try to convert it to a number.
+ std::istringstream stream(string_arg);
+ double double_value;
+ if (stream >> double_value) {
+ SetFrequency(double_value);
+ return;
+ }
+ }
+ }
+}
+
+void SineSynthInstance::SetFrequency(double frequency) {
+ frequency_ = frequency;
+ PostMessage(pp::Var(frequency_));
+}
+
+// The Module class. The browser calls the CreateInstance() method to create
+// an instance of your NaCl module on the web page. The browser creates a new
+// instance for each <embed> tag with type="application/x-nacl".
+class SineSynthModule : public pp::Module {
+ public:
+ SineSynthModule() : pp::Module() {}
+ ~SineSynthModule() {}
+
+ // Create and return a HelloWorldInstance object.
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new SineSynthInstance(instance);
+ }
+};
+
+} // namespace sine_synth
+
+// Factory function called by the browser when the module is first loaded.
+// The browser keeps a singleton of this module. It calls the
+// CreateInstance() method on the object you return to make instances. There
+// is one instance per <embed> tag on the page. This is the main binding
+// point for your NaCl module with the browser.
+namespace pp {
+Module* CreateModule() {
+ return new sine_synth::SineSynthModule();
+}
+} // namespace pp
diff --git a/native_client_sdk/src/examples/sine_synth/sine_synth.html b/native_client_sdk/src/examples/sine_synth/sine_synth.html
new file mode 100644
index 0000000..3d1609b
--- /dev/null
+++ b/native_client_sdk/src/examples/sine_synth/sine_synth.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+<!--
+Copyright (c) 2011 The Native Client Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<head>
+ <title>Sine Wave Synthesizer</title>
+ <script type="text/javascript">
+ sineSynth = null; // Global application object.
+
+ // Indicate success when the NaCl module has loaded.
+ function moduleDidLoad() {
+ sineSynth = document.getElementById('sineSynth');
+ document.getElementById('frequency_field').value = 440;
+ }
+
+ // Handle a message coming from the NaCl module. The message payload
+ // contains the frequency value. Update the frequency field with this
+ // value.
+ function handleMessage(message_event) {
+ document.getElementById('frequency_field').value = message_event.data;
+ }
+
+ function toggleSound(flag) {
+ sineSynth.postMessage('setFrequency:' +
+ document.getElementById('frequency_field').value);
+ if (flag) {
+ sineSynth.postMessage('playSound');
+ } else {
+ sineSynth.postMessage('stopSound');
+ }
+ }
+
+ function changeFrequency(freq) {
+ sineSynth.postMessage('setFrequency:' + freq);
+ }
+ </script>
+</head>
+
+<body id="bodyId">
+ <h1>Sine Wave Synthesizer</h1>
+ <p>Click the button to start and stop the sine wave playing.</p>
+ <button onclick="toggleSound(true)">Play</button>
+ <button onclick="toggleSound(false)">Stop</button>
+ <p>Enter the frequency of the sine wave:</p>
+ <input type="text" size="15" id="frequency_field"
+ value="#undef" onchange="changeFrequency(this.value)" />
+ <!-- Load the published .nexe. This includes the 'src' attribute which
+ shows how to load multi-architecture modules. Each entry in the "nexes"
+ object in the .nmf manifest file is a key-value pair: the key is the runtime
+ ('x86-32', 'x86-64', etc.); the value is a URL for the desired NaCl module.
+ To load the debug versions of your .nexes, set the 'src' attribute to the
+ _dbg.nmf version of the manifest file.
+
+ Note: The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
+ and a 'message' event listener attached. This wrapping method is used
+ instead of attaching the event listeners directly to the <EMBED> element to
+ ensure that the listeners are active before the NaCl module 'load' event
+ fires. This also allows you to use PPB_Messaging.PostMessage() (in C) or
+ pp::Instance.PostMessage() (in C++) from within the initialization code in
+ your NaCl module.
+ -->
+ <div id="listener">
+ <script type="text/javascript">
+ var listener = document.getElementById('listener')
+ listener.addEventListener('load', moduleDidLoad, true);
+ listener.addEventListener('message', handleMessage, true);
+ </script>
+
+ <embed name="nacl_module"
+ id="sineSynth"
+ width=0 height=0
+ src="sine_synth.nmf"
+ type="application/x-nacl" />
+ </div>
+</body>
+</html>
diff --git a/native_client_sdk/src/examples/tumbler/bind.js b/native_client_sdk/src/examples/tumbler/bind.js
new file mode 100644
index 0000000..92fbbd2
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/bind.js
@@ -0,0 +1,22 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview This class implements an extension to Function object that
+ * lets you bind a scope for |this| to a function.
+ */
+
+/**
+ * Bind a scope to a function. Used to bind an object to |this| for event
+ * handlers.
+ * @param {!Object} scope The scope in which the function executes. |scope|
+ * becomes |this| during function execution.
+ * @return {function} the bound version of the original function.
+ */
+Function.prototype.bind = function(scope) {
+ var boundContext = this;
+ return function() {
+ return boundContext.apply(scope, arguments);
+ }
+}
diff --git a/native_client_sdk/src/examples/tumbler/build.scons b/native_client_sdk/src/examples/tumbler/build.scons
new file mode 100644
index 0000000..4c42b7c
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/build.scons
@@ -0,0 +1,55 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import make_nacl_env
+import nacl_utils
+import os
+
+nacl_env = make_nacl_env.NaClEnvironment(
+ use_c_plus_plus_libs=True, nacl_platform=os.getenv('NACL_TARGET_PLATFORM'),
+ install_subdir='tumbler', lib_prefix='..')
+nacl_env.Append(
+ # Add a CPPPATH that enables the full-path #include directives, such as
+ # #include "examples/sine_synth/sine_synth.h"
+ CPPPATH=[os.path.dirname(os.path.dirname(os.getcwd()))],
+ # Strict ANSI compliance.
+ EXTRA_CCFLAGS=['-pedantic'],
+ LIBS=['ppapi_gles2'],
+ )
+
+sources = [
+ 'cube.cc',
+ 'opengl_context.cc',
+ 'scripting_bridge.cc',
+ 'shader_util.cc',
+ 'transforms.cc',
+ 'tumbler.cc',
+ 'tumbler_module.cc',
+ ]
+
+opt_nexes, dbg_nexes = nacl_env.AllNaClModules(sources, 'tumbler')
+
+# This target is used by the SDK build system to provide a prebuilt version
+# of the example in the SDK installer.
+nacl_env.InstallPrebuilt('tumbler')
+
+app_files = [
+ 'tumbler.html',
+ 'tumbler.nmf',
+ 'bind.js',
+ 'dragger.js',
+ 'trackball.js',
+ 'tumbler.js',
+ 'vector3.js',
+ ]
+
+# Split the install of the .nexes from the other app sources so that the strip
+# action is applied to the .nexes only.
+install_nexes = nacl_env.NaClStrippedInstall(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=opt_nexes)
+install_app = nacl_env.Install(dir=nacl_env['NACL_INSTALL_ROOT'],
+ source=app_files)
+nacl_env.Alias('install', source=install_app + install_nexes)
diff --git a/native_client_sdk/src/examples/tumbler/callback.h b/native_client_sdk/src/examples/tumbler/callback.h
new file mode 100644
index 0000000..4d67262
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/callback.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_TUMBLER_CALLBACK_H_
+#define EXAMPLES_TUMBLER_CALLBACK_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace tumbler {
+
+class ScriptingBridge;
+
+// Templates used to support method call-backs when a method or property is
+// accessed from the browser code.
+
+// Class suite used to publish a method name to Javascript. Typical use is
+// like this:
+// photo::MethodCallback<Calculator>* calculate_callback_;
+// calculate_callback_ =
+// new scripting::MethodCallback<Calculator>(this,
+// &Calculator::Calculate);
+// bridge->AddMethodNamed("calculate", calculate_callback_);
+// ...
+// delete calculate_callback_;
+//
+// The caller must delete the callback.
+
+// Methods get parameters as a dictionary that maps parameter names to values.
+typedef std::map<std::string, std::string> MethodParameter;
+
+// Pure virtual class used in STL containers.
+class MethodCallbackExecutor {
+ public:
+ virtual ~MethodCallbackExecutor() {}
+ virtual void Execute(
+ const ScriptingBridge& bridge,
+ const MethodParameter& parameters) = 0;
+};
+
+template <class T>
+class MethodCallback : public MethodCallbackExecutor {
+ public:
+ typedef void (T::*Method)(
+ const ScriptingBridge& bridge,
+ const MethodParameter& parameters);
+
+ MethodCallback(T* instance, Method method)
+ : instance_(instance), method_(method) {}
+ virtual ~MethodCallback() {}
+ virtual void Execute(
+ const ScriptingBridge& bridge,
+ const MethodParameter& parameters) {
+ // Use "this->" to force C++ to look inside our templatized base class; see
+ // Effective C++, 3rd Ed, item 43, p210 for details.
+ ((this->instance_)->*(this->method_))(bridge, parameters);
+ }
+
+ private:
+ T* instance_;
+ Method method_;
+};
+
+template <class T>
+class ConstMethodCallback : public MethodCallbackExecutor {
+ public:
+ typedef void (T::*ConstMethod)(
+ const ScriptingBridge& bridge,
+ const MethodParameter& parameters) const;
+
+ ConstMethodCallback(const T* instance, ConstMethod method)
+ : instance_(instance), const_method_(method) {}
+ virtual ~ConstMethodCallback() {}
+ virtual void Execute(
+ const ScriptingBridge& bridge,
+ const MethodParameter& parameters) {
+ // Use "this->" to force C++ to look inside our templatized base class; see
+ // Effective C++, 3rd Ed, item 43, p210 for details.
+ ((this->instance_)->*(this->const_method_))(bridge, parameters);
+ }
+
+ private:
+ const T* instance_;
+ ConstMethod const_method_;
+};
+
+} // namespace tumbler
+
+#endif // EXAMPLES_TUMBLER_CALLBACK_H_
+
diff --git a/native_client_sdk/src/examples/tumbler/cube.cc b/native_client_sdk/src/examples/tumbler/cube.cc
new file mode 100644
index 0000000..c062c81
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/cube.cc
@@ -0,0 +1,267 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/tumbler/cube.h"
+
+#include <algorithm>
+
+#include "examples/tumbler/shader_util.h"
+#include "examples/tumbler/transforms.h"
+
+namespace tumbler {
+
+static const size_t kVertexCount = 24;
+static const int kIndexCount = 36;
+
+Cube::Cube(SharedOpenGLContext opengl_context)
+ : opengl_context_(opengl_context),
+ width_(1),
+ height_(1) {
+ eye_[0] = eye_[1] = 0.0f;
+ eye_[2] = 2.0f;
+ orientation_[0] = 0.0f;
+ orientation_[1] = 0.0f;
+ orientation_[2] = 0.0f;
+ orientation_[3] = 1.0f;
+}
+
+Cube::~Cube() {
+ glDeleteBuffers(3, cube_vbos_);
+ glDeleteProgram(shader_program_object_);
+}
+
+void Cube::PrepareOpenGL() {
+ CreateShaders();
+ CreateCube();
+ glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ glEnable(GL_DEPTH_TEST);
+}
+
+void Cube::Resize(int width, int height) {
+ width_ = std::max(width, 1);
+ height_ = std::max(height, 1);
+ // Set the viewport
+ glViewport(0, 0, width_, height_);
+ // Compute the perspective projection matrix with a 60 degree FOV.
+ GLfloat aspect = static_cast<GLfloat>(width_) / static_cast<GLfloat>(height_);
+ transform_4x4::LoadIdentity(perspective_proj_);
+ transform_4x4::Perspective(perspective_proj_, 60.0f, aspect, 1.0f, 20.0f);
+}
+
+void Cube::Draw() {
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ // Compute a new model-view matrix, then use that to make the composite
+ // model-view-projection matrix: MVP = MV . P.
+ GLfloat model_view[16];
+ ComputeModelViewTransform(model_view);
+ transform_4x4::Multiply(mvp_matrix_, model_view, perspective_proj_);
+
+ glBindBuffer(GL_ARRAY_BUFFER, cube_vbos_[0]);
+ glUseProgram(shader_program_object_);
+ glEnableVertexAttribArray(position_location_);
+ glVertexAttribPointer(position_location_,
+ 3,
+ GL_FLOAT,
+ GL_FALSE,
+ 3 * sizeof(GLfloat),
+ NULL);
+ glEnableVertexAttribArray(color_location_);
+ glBindBuffer(GL_ARRAY_BUFFER, cube_vbos_[1]);
+ glVertexAttribPointer(color_location_,
+ 3,
+ GL_FLOAT,
+ GL_FALSE,
+ 3 * sizeof(GLfloat),
+ NULL);
+ glUniformMatrix4fv(mvp_location_, 1, GL_FALSE, mvp_matrix_);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cube_vbos_[2]);
+ glDrawElements(GL_TRIANGLES, kIndexCount, GL_UNSIGNED_SHORT, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+bool Cube::CreateShaders() {
+ const char vertex_shader_src[] =
+ "uniform mat4 u_mvpMatrix; \n"
+ "attribute vec4 a_position; \n"
+ "attribute vec3 a_color; \n"
+ "varying lowp vec4 v_color; \n"
+ "void main() \n"
+ "{ \n"
+ " v_color.xyz = a_color; \n"
+ " v_color.w = 1.0; \n"
+ " gl_Position = u_mvpMatrix * a_position; \n"
+ "} \n";
+
+ const char fragment_shader_src[] =
+ "varying lowp vec4 v_color; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_FragColor = v_color; \n"
+ "} \n";
+
+ // Load the shaders and get a linked program object
+ shader_program_object_ =
+ shader_util::CreateProgramFromVertexAndFragmentShaders(
+ vertex_shader_src, fragment_shader_src);
+ if (shader_program_object_ == 0)
+ return false;
+ position_location_ = glGetAttribLocation(shader_program_object_,
+ "a_position");
+ color_location_ = glGetAttribLocation(shader_program_object_, "a_color");
+ mvp_location_ = glGetUniformLocation(shader_program_object_, "u_mvpMatrix");
+ return true;
+}
+
+void Cube::CreateCube() {
+ static const GLfloat cube_vertices[] = {
+ // Vertex coordinates interleaved with color values
+ // Bottom
+ -0.5f, -0.5f, -0.5f,
+ -0.5f, -0.5f, 0.5f,
+ 0.5f, -0.5f, 0.5f,
+ 0.5f, -0.5f, -0.5f,
+ // Top
+ -0.5f, 0.5f, -0.5f,
+ -0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5f, -0.5f,
+ // Back
+ -0.5f, -0.5f, -0.5f,
+ -0.5f, 0.5f, -0.5f,
+ 0.5f, 0.5f, -0.5f,
+ 0.5f, -0.5f, -0.5f,
+ // Front
+ -0.5f, -0.5f, 0.5f,
+ -0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5f, 0.5f,
+ 0.5f, -0.5f, 0.5f,
+ // Left
+ -0.5f, -0.5f, -0.5f,
+ -0.5f, -0.5f, 0.5f,
+ -0.5f, 0.5f, 0.5f,
+ -0.5f, 0.5f, -0.5f,
+ // Right
+ 0.5f, -0.5f, -0.5f,
+ 0.5f, -0.5f, 0.5f,
+ 0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5f, -0.5f
+ };
+
+ static const GLfloat cube_colors[] = {
+ // Vertex coordinates interleaved with color values
+ // Bottom
+ 1.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+ 1.0, 0.0, 0.0,
+ // Top
+ 0.0, 1.0, 0.0,
+ 0.0, 1.0, 0.0,
+ 0.0, 1.0, 0.0,
+ 0.0, 1.0, 0.0,
+ // Back
+ 0.0, 0.0, 1.0,
+ 0.0, 0.0, 1.0,
+ 0.0, 0.0, 1.0,
+ 0.0, 0.0, 1.0,
+ // Front
+ 1.0, 0.0, 1.0,
+ 1.0, 0.0, 1.0,
+ 1.0, 0.0, 1.0,
+ 1.0, 0.0, 1.0,
+ // Left
+ 1.0, 1.0, 0.0,
+ 1.0, 1.0, 0.0,
+ 1.0, 1.0, 0.0,
+ 1.0, 1.0, 0.0,
+ // Right
+ 0.0, 1.0, 1.0,
+ 0.0, 1.0, 1.0,
+ 0.0, 1.0, 1.0,
+ 0.0, 1.0, 1.0
+ };
+
+ static const GLushort cube_indices[] = {
+ // Bottom
+ 0, 2, 1,
+ 0, 3, 2,
+ // Top
+ 4, 5, 6,
+ 4, 6, 7,
+ // Back
+ 8, 9, 10,
+ 8, 10, 11,
+ // Front
+ 12, 15, 14,
+ 12, 14, 13,
+ // Left
+ 16, 17, 18,
+ 16, 18, 19,
+ // Right
+ 20, 23, 22,
+ 20, 22, 21
+ };
+
+ // Generate the VBOs and upload them to the graphics context.
+ glGenBuffers(3, cube_vbos_);
+ glBindBuffer(GL_ARRAY_BUFFER, cube_vbos_[0]);
+ glBufferData(GL_ARRAY_BUFFER,
+ kVertexCount * sizeof(GLfloat) * 3,
+ cube_vertices,
+ GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, cube_vbos_[1]);
+ glBufferData(GL_ARRAY_BUFFER,
+ kVertexCount * sizeof(GLfloat) * 3,
+ cube_colors,
+ GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cube_vbos_[2]);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER,
+ kIndexCount * sizeof(GL_UNSIGNED_SHORT),
+ cube_indices,
+ GL_STATIC_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+void Cube::ComputeModelViewTransform(GLfloat* model_view) {
+ // This method takes into account the possiblity that |orientation_|
+ // might not be normalized.
+ double sqrx = orientation_[0] * orientation_[0];
+ double sqry = orientation_[1] * orientation_[1];
+ double sqrz = orientation_[2] * orientation_[2];
+ double sqrw = orientation_[3] * orientation_[3];
+ double sqrLength = 1.0 / (sqrx + sqry + sqrz + sqrw);
+
+ transform_4x4::LoadIdentity(model_view);
+ model_view[0] = (sqrx - sqry - sqrz + sqrw) * sqrLength;
+ model_view[5] = (-sqrx + sqry - sqrz + sqrw) * sqrLength;
+ model_view[10] = (-sqrx - sqry + sqrz + sqrw) * sqrLength;
+
+ double temp1 = orientation_[0] * orientation_[1];
+ double temp2 = orientation_[2] * orientation_[3];
+ model_view[1] = 2.0 * (temp1 + temp2) * sqrLength;
+ model_view[4] = 2.0 * (temp1 - temp2) * sqrLength;
+
+ temp1 = orientation_[0] * orientation_[2];
+ temp2 = orientation_[1] * orientation_[3];
+ model_view[2] = 2.0 * (temp1 - temp2) * sqrLength;
+ model_view[8] = 2.0 * (temp1 + temp2) * sqrLength;
+ temp1 = orientation_[1] * orientation_[2];
+ temp2 = orientation_[0] * orientation_[3];
+ model_view[6] = 2.0 * (temp1 + temp2) * sqrLength;
+ model_view[9] = 2.0 * (temp1 - temp2) * sqrLength;
+ model_view[3] = 0.0;
+ model_view[7] = 0.0;
+ model_view[11] = 0.0;
+
+ // Concatenate the translation to the eye point.
+ model_view[12] = -eye_[0];
+ model_view[13] = -eye_[1];
+ model_view[14] = -eye_[2];
+ model_view[15] = 1.0;
+}
+
+} // namespace tumbler
diff --git a/native_client_sdk/src/examples/tumbler/cube.h b/native_client_sdk/src/examples/tumbler/cube.h
new file mode 100644
index 0000000..6a992c7
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/cube.h
@@ -0,0 +1,97 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_TUMBLER_CUBE_H_
+#define EXAMPLES_TUMBLER_CUBE_H_
+
+#include <GLES2/gl2.h>
+#include <vector>
+#include "examples/tumbler/opengl_context.h"
+#include "examples/tumbler/opengl_context_ptrs.h"
+
+namespace tumbler {
+
+// The Cube class provides a place to implement 3D rendering. It has a
+// frame that it occupies in a browser window.
+class Cube {
+ public:
+ explicit Cube(SharedOpenGLContext opengl_context);
+ ~Cube();
+
+ // Called once when a new RenderContext is first bound to the view. The
+ // bound context is guaranteed to be current and valid before calling this
+ // method.
+ void PrepareOpenGL();
+
+ // Called whenever the size of the browser view changes. This method is
+ // called at least once when the view is first made visible. Clamps the
+ // sizes to 1.
+ void Resize(int width, int height);
+
+ // Called every time the view need to be drawn. The bound context is
+ // guaranteed to be current and valid before this method is called. The
+ // visible portion of the context is flushed to the browser after this
+ // method returns.
+ void Draw();
+
+ // Accessor for width and height. To change these, call Resize.
+ const int width() const {
+ return width_;
+ }
+
+ const int height() const {
+ return height_;
+ }
+
+ // Accessor/mutator for the camera orientation.
+ void GetOrientation(std::vector<float>* orientation) const {
+ if (!orientation)
+ return;
+ (*orientation)[0] = static_cast<float>(orientation_[0]);
+ (*orientation)[1] = static_cast<float>(orientation_[1]);
+ (*orientation)[2] = static_cast<float>(orientation_[2]);
+ (*orientation)[3] = static_cast<float>(orientation_[3]);
+ }
+ void SetOrientation(const std::vector<float>& orientation) {
+ orientation_[0] = static_cast<GLfloat>(orientation[0]);
+ orientation_[1] = static_cast<GLfloat>(orientation[1]);
+ orientation_[2] = static_cast<GLfloat>(orientation[2]);
+ orientation_[3] = static_cast<GLfloat>(orientation[3]);
+ }
+
+ private:
+ // Create the shaders used to draw the cube, and link them into a program.
+ // Initializes |shader_progam_object_|, |position_loction_| and
+ // |mvp_location_|.
+ bool CreateShaders();
+
+ // Generates a cube as a series of GL_TRIANGLE_STRIPs, and initializes
+ // |index_count_| to the number of indices in the index list used as a VBO.
+ // Creates the |vbo_ids_| required for the vertex and index data and uploads
+ // the the VBO data.
+ void CreateCube();
+
+ // Build up the model-view transform from the eye and orienation properties.
+ // Assumes that |model_view| is a 4x4 matrix.
+ void ComputeModelViewTransform(GLfloat* model_view);
+
+ SharedOpenGLContext opengl_context_;
+ int width_;
+ int height_;
+ GLuint shader_program_object_; // The compiled shaders.
+ GLint position_location_; // The position attribute location.
+ GLint color_location_; // The color attribute location.
+ GLint mvp_location_; // The Model-View-Projection composite matrix.
+ GLuint cube_vbos_[3];
+ GLfloat eye_[3]; // The eye point of the virtual camera.
+ // The orientation of the virtual camera stored as a quaternion. The
+ // quaternion is laid out as {{x, y, z}, w}.
+ GLfloat orientation_[4];
+ GLfloat perspective_proj_[16];
+ GLfloat mvp_matrix_[16];
+};
+
+} // namespace tumbler
+
+#endif // EXAMPLES_TUMBLER_CUBE_H_
diff --git a/native_client_sdk/src/examples/tumbler/dragger.js b/native_client_sdk/src/examples/tumbler/dragger.js
new file mode 100644
index 0000000..232d8b5
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/dragger.js
@@ -0,0 +1,134 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview This class implements a mouse-drag event. It registers for
+ * mousedown events, and when it sees one, starts capturing mousemove events
+ * until it gets a mousup event. It manufactures three drag events: the
+ * DRAG_START, DRAG and DRAG_END.
+ */
+
+// Requires bind
+
+/**
+ * Constructor for the Dragger. Register for mousedown events that happen on
+ * |opt_target|. If |opt_target| is null or undefined, then this object
+ * observes mousedown on the whole document.
+ * @param {?Element} opt_target The event target. Defaults to the whole
+ * document.
+ * @constructor
+ */
+tumbler.Dragger = function(opt_target) {
+ /**
+ * The event target.
+ * @type {Element}
+ * @private
+ */
+ this.target_ = opt_target || document;
+
+ /**
+ * The array of objects that get notified of drag events. Each object in
+ * this array get sent a handleStartDrag(), handleDrag() and handleEndDrag()
+ * message.
+ * @type {Array.<Object>}
+ * @private
+ */
+ this.listeners_ = [];
+
+ /**
+ * Flag to indicate whether the object is in a drag sequence or not.
+ * @type {boolean}
+ * @private
+ */
+ this.isDragging_ = false;
+
+ /**
+ * The function objects that get attached as event handlers. These are
+ * cached so that they can be removed on mouse up.
+ * @type {function}
+ * @private
+ */
+ this.boundMouseMove_ = null;
+ this.boundMouseUp_ = null;
+
+ this.target_.addEventListener('mousedown',
+ this.onMouseDown.bind(this),
+ false);
+}
+
+/**
+ * The ids used for drag event types.
+ * @enum {string}
+ */
+tumbler.Dragger.DragEvents = {
+ DRAG_START: 'dragstart', // Start a drag sequence
+ DRAG: 'drag', // Mouse moved during a drag sequence.
+ DRAG_END: 'dragend' // End a drag sewquence.
+};
+
+/**
+ * Add a drag listener. Each listener should respond to thhree methods:
+ * handleStartDrag(), handleDrag() and handleEndDrag(). This method assumes
+ * that |listener| does not already exist in the array of listeners.
+ * @param {!Object} listener The object that will listen to drag events.
+ */
+tumbler.Dragger.prototype.addDragListener = function(listener) {
+ this.listeners_.push(listener);
+}
+
+/**
+ * Handle a mousedown event: register for mousemove and mouseup, then tell
+ * the target that is has a DRAG_START event.
+ * @param {Event} event The mousedown event that triggered this method.
+ */
+tumbler.Dragger.prototype.onMouseDown = function(event) {
+ this.boundMouseMove_ = this.onMouseMove.bind(this);
+ this.boundMouseUp_ = this.onMouseUp.bind(this);
+ this.target_.addEventListener('mousemove', this.boundMouseMove_);
+ this.target_.addEventListener('mouseup', this.boundMouseUp_);
+ this.isDragging_ = true;
+ var dragStartEvent = { type: tumbler.Dragger.DragEvents.DRAG_START,
+ clientX: event.offsetX,
+ clientY: event.offsetY };
+ var i;
+ for (i = 0; i < this.listeners_.length; ++i) {
+ this.listeners_[i].handleStartDrag(this.target_, dragStartEvent);
+ }
+}
+
+/**
+ * Handle a mousemove event: tell the target that is has a DRAG event.
+ * @param {Event} event The mousemove event that triggered this method.
+ */
+tumbler.Dragger.prototype.onMouseMove = function(event) {
+ if (!this.isDragging_)
+ return;
+ var dragEvent = { type: tumbler.Dragger.DragEvents.DRAG,
+ clientX: event.offsetX,
+ clientY: event.offsetY};
+ var i;
+ for (i = 0; i < this.listeners_.length; ++i) {
+ this.listeners_[i].handleDrag(this.target_, dragEvent);
+ }
+}
+
+/**
+ * Handle a mouseup event: un-register for mousemove and mouseup, then tell
+ * the target that is has a DRAG_END event.
+ * @param {Event} event The mouseup event that triggered this method.
+ */
+tumbler.Dragger.prototype.onMouseUp = function(event) {
+ this.target_.removeEventListener('mouseup', this.boundMouseUp_, false);
+ this.target_.removeEventListener('mousemove', this.boundMouseMove_, false);
+ this.boundMouseUp_ = null;
+ this.boundMouseMove_ = null;
+ this.isDragging_ = false;
+ var dragEndEvent = { type: tumbler.Dragger.DragEvents.DRAG_END,
+ clientX: event.offsetX,
+ clientY: event.offsetY};
+ var i;
+ for (i = 0; i < this.listeners_.length; ++i) {
+ this.listeners_[i].handleEndDrag(this.target_, dragEndEvent);
+ }
+}
diff --git a/native_client_sdk/src/examples/tumbler/opengl_context.cc b/native_client_sdk/src/examples/tumbler/opengl_context.cc
new file mode 100644
index 0000000..f59013b
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/opengl_context.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/tumbler/opengl_context.h"
+
+#include <pthread.h>
+#include "ppapi/cpp/completion_callback.h"
+#include "ppapi/gles2/gl2ext_ppapi.h"
+
+namespace {
+// This is called by the brower when the 3D context has been flushed to the
+// browser window.
+void FlushCallback(void* data, int32_t result) {
+ static_cast<tumbler::OpenGLContext*>(data)->set_flush_pending(false);
+}
+} // namespace
+
+namespace tumbler {
+
+OpenGLContext::OpenGLContext(pp::Instance* instance)
+ : pp::Graphics3DClient(instance),
+ flush_pending_(false) {
+ pp::Module* module = pp::Module::Get();
+ assert(module);
+ gles2_interface_ = static_cast<const struct PPB_OpenGLES2*>(
+ module->GetBrowserInterface(PPB_OPENGLES2_INTERFACE));
+ assert(gles2_interface_);
+}
+
+OpenGLContext::~OpenGLContext() {
+ glSetCurrentContextPPAPI(0);
+}
+
+bool OpenGLContext::MakeContextCurrent(pp::Instance* instance) {
+ if (instance == NULL) {
+ glSetCurrentContextPPAPI(0);
+ return false;
+ }
+ // Lazily create the Pepper context.
+ if (context_.is_null()) {
+ int32_t attribs[] = {
+ PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8,
+ PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 24,
+ PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 8,
+ PP_GRAPHICS3DATTRIB_SAMPLES, 0,
+ PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0,
+ PP_GRAPHICS3DATTRIB_WIDTH, size_.width(),
+ PP_GRAPHICS3DATTRIB_HEIGHT, size_.height(),
+ PP_GRAPHICS3DATTRIB_NONE
+ };
+ context_ = pp::Graphics3D(instance, pp::Graphics3D(), attribs);
+ if (context_.is_null()) {
+ glSetCurrentContextPPAPI(0);
+ return false;
+ }
+ instance->BindGraphics(context_);
+ }
+ glSetCurrentContextPPAPI(context_.pp_resource());
+ return true;
+}
+
+void OpenGLContext::InvalidateContext(pp::Instance* instance) {
+ glSetCurrentContextPPAPI(0);
+}
+
+void OpenGLContext::ResizeContext(const pp::Size& size) {
+ size_ = size;
+ if (!context_.is_null()) {
+ context_.ResizeBuffers(size.width(), size.height());
+ }
+}
+
+
+void OpenGLContext::FlushContext() {
+ if (flush_pending()) {
+ // A flush is pending so do nothing; just drop this flush on the floor.
+ return;
+ }
+ set_flush_pending(true);
+ context_.SwapBuffers(pp::CompletionCallback(&FlushCallback, this));
+}
+} // namespace tumbler
+
diff --git a/native_client_sdk/src/examples/tumbler/opengl_context.h b/native_client_sdk/src/examples/tumbler/opengl_context.h
new file mode 100644
index 0000000..b75060d
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/opengl_context.h
@@ -0,0 +1,93 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_TUMBLER_OPENGL_CONTEXT_H_
+#define EXAMPLES_TUMBLER_OPENGL_CONTEXT_H_
+
+///
+/// @file
+/// OpenGLContext manages the OpenGL context in the browser that is associated
+/// with a @a pp::Instance instance.
+///
+
+#include <assert.h>
+#include <pthread.h>
+
+#include <algorithm>
+#include <string>
+
+#include "examples/tumbler/opengl_context_ptrs.h"
+#include "ppapi/c/ppb_opengles2.h"
+#include "ppapi/cpp/graphics_3d_client.h"
+#include "ppapi/cpp/graphics_3d.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/size.h"
+
+namespace tumbler {
+
+/// OpenGLContext manages an OpenGL rendering context in the browser.
+///
+class OpenGLContext : public pp::Graphics3DClient {
+ public:
+ explicit OpenGLContext(pp::Instance* instance);
+
+ /// Release all the in-browser resources used by this context, and make this
+ /// context invalid.
+ virtual ~OpenGLContext();
+
+ /// The Graphics3DClient interfcace.
+ virtual void Graphics3DContextLost() {
+ assert(!"Unexpectedly lost graphics context");
+ }
+
+ /// Make @a this the current 3D context in @a instance.
+ /// @param instance The instance of the NaCl module that will receive the
+ /// the current 3D context.
+ /// @return success.
+ bool MakeContextCurrent(pp::Instance* instance);
+
+ /// Flush the contents of this context to the browser's 3D device.
+ void FlushContext();
+
+ /// Make the underlying 3D device invalid, so that any subsequent rendering
+ /// commands will have no effect. The next call to MakeContextCurrent() will
+ /// cause the underlying 3D device to get rebound and start receiving
+ /// receiving rendering commands again. Use InvalidateContext(), for
+ /// example, when resizing the context's viewing area.
+ void InvalidateContext(pp::Instance* instance);
+
+ /// Resize the context.
+ void ResizeContext(const pp::Size& size);
+
+ /// The OpenGL ES 2.0 interface.
+ const struct PPB_OpenGLES2* gles2() const {
+ return gles2_interface_;
+ }
+
+ /// The PP_Resource needed to make GLES2 calls through the Pepper interface.
+ const PP_Resource gl_context() const {
+ return context_.pp_resource();
+ }
+
+ /// Indicate whether a flush is pending. This can only be called from the
+ /// main thread; it is not thread safe.
+ bool flush_pending() const {
+ return flush_pending_;
+ }
+ void set_flush_pending(bool flag) {
+ flush_pending_ = flag;
+ }
+
+ private:
+ pp::Size size_;
+ pp::Graphics3D context_;
+ bool flush_pending_;
+
+ const struct PPB_OpenGLES2* gles2_interface_;
+};
+
+} // namespace tumbler
+
+#endif // EXAMPLES_TUMBLER_OPENGL_CONTEXT_H_
+
diff --git a/native_client_sdk/src/examples/tumbler/opengl_context_ptrs.h b/native_client_sdk/src/examples/tumbler/opengl_context_ptrs.h
new file mode 100644
index 0000000..3478521
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/opengl_context_ptrs.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_TUMBLER_OPENGL_CONTEXT_PTRS_H_
+#define EXAMPLES_TUMBLER_OPENGL_CONTEXT_PTRS_H_
+
+// A convenience wrapper for a shared OpenGLContext pointer type. As other
+// smart pointer types are needed, add them here.
+
+#include <tr1/memory>
+
+namespace tumbler {
+
+class OpenGLContext;
+
+typedef std::tr1::shared_ptr<OpenGLContext> SharedOpenGLContext;
+
+} // namespace tumbler
+
+#endif // EXAMPLES_TUMBLER_OPENGL_CONTEXT_PTRS_H_
+
diff --git a/native_client_sdk/src/examples/tumbler/scripting_bridge.cc b/native_client_sdk/src/examples/tumbler/scripting_bridge.cc
new file mode 100644
index 0000000..e74bd9e
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/scripting_bridge.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/tumbler/scripting_bridge.h"
+
+namespace {
+const char* const kWhiteSpaceCharacters = " \t";
+
+// Helper function to pull out the next token in |token_string|. A token is
+// delimited by whitespace. Scanning begins at |*pos|, if pos goes beyond the
+// end of |token_string|, it is set to std::string::npos and an empty string
+// is returned. On return, |*pos| will point to the beginning of the next
+// token. |pos| must not be NULL.
+const std::string ScanToken(const std::string& token_string, size_t* pos) {
+ std::string token;
+ if (*pos == std::string::npos) {
+ return token;
+ }
+ size_t token_start_pos = token_string.find_first_not_of(kWhiteSpaceCharacters,
+ *pos);
+ size_t token_end_pos = token_string.find_first_of(kWhiteSpaceCharacters,
+ token_start_pos);
+ if (token_start_pos != std::string::npos) {
+ token = token_string.substr(token_start_pos, token_end_pos);
+ }
+ *pos = token_end_pos;
+ return token;
+}
+
+// Take a string of the form 'name:value' and split it into two strings, one
+// containing 'name' and the other 'value'. If the ':' separator is missing,
+// or is the last character in |parameter|, |parameter| is copied to
+// |param_name|, |param_value| is left unchanged and false is returned.
+bool ParseParameter(const std::string& parameter,
+ std::string* param_name,
+ std::string* param_value) {
+ bool success = false;
+ size_t sep_pos = parameter.find_first_of(':');
+ if (sep_pos != std::string::npos) {
+ *param_name = parameter.substr(0, sep_pos);
+ if (sep_pos < parameter.length() - 1) {
+ *param_value = parameter.substr(sep_pos + 1);
+ success = true;
+ } else {
+ success = false;
+ }
+ } else {
+ *param_name = parameter;
+ success = false;
+ }
+ return success;
+}
+} // namespace
+
+namespace tumbler {
+
+bool ScriptingBridge::AddMethodNamed(const std::string& method_name,
+ SharedMethodCallbackExecutor method) {
+ if (method_name.size() == 0 || method == NULL)
+ return false;
+ method_dictionary_.insert(
+ std::pair<std::string, SharedMethodCallbackExecutor>(method_name,
+ method));
+ return true;
+}
+
+bool ScriptingBridge::InvokeMethod(const std::string& method) {
+ size_t current_pos = 0;
+ const std::string method_name = ScanToken(method, &current_pos);
+ MethodDictionary::iterator method_iter;
+ method_iter = method_dictionary_.find(method_name);
+ if (method_iter != method_dictionary_.end()) {
+ // Pull out the method parameters and build a dictionary that maps
+ // parameter names to values.
+ std::map<std::string, std::string> param_dict;
+ while (current_pos != std::string::npos) {
+ const std::string parameter = ScanToken(method, &current_pos);
+ if (parameter.length()) {
+ std::string param_name;
+ std::string param_value;
+ if (ParseParameter(parameter, &param_name, &param_value)) {
+ // Note that duplicate parameter names will override each other. The
+ // last one in the method string will be used.
+ param_dict[param_name] = param_value;
+ }
+ }
+ }
+ (*method_iter->second).Execute(*this, param_dict);
+ return true;
+ }
+ return false;
+}
+
+} // namespace tumbler
diff --git a/native_client_sdk/src/examples/tumbler/scripting_bridge.h b/native_client_sdk/src/examples/tumbler/scripting_bridge.h
new file mode 100644
index 0000000..3e2f73b
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/scripting_bridge.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_TUMBLER_SCRIPTING_BRIDGE_H_
+#define EXAMPLES_TUMBLER_SCRIPTING_BRIDGE_H_
+
+#include <map>
+#include <string>
+#include <tr1/memory>
+#include <vector>
+
+#include "examples/tumbler/callback.h"
+#include "ppapi/cpp/var.h"
+
+namespace tumbler {
+
+class MethodCallbackExecutor;
+
+// This class handles the interface between the browser and the NaCl module.
+// There is a single point of entry from the browser: postMessage(). The
+// string passed to postMessage() has this format:
+// 'function_name arg_name0:arg_0 arg_name1:arg1 ...'
+// The arguments have undetermined type; they are placed in a map of argument
+// names and values. Values are all strings, it is up to the target code to
+// do any type coercion.
+// Methods called by the scripting bridge must have a signature like this:
+// void Method(const ScriptingBridge& bridge,
+// const ParameterDictionary&);
+class ScriptingBridge {
+ public:
+ // Shared pointer type used in the method map.
+ typedef std::tr1::shared_ptr<MethodCallbackExecutor>
+ SharedMethodCallbackExecutor;
+
+ virtual ~ScriptingBridge() {}
+
+ // Causes |method_name| to be published as a method that can be called via
+ // postMessage() from the browser. Associates this method with |method|.
+ bool AddMethodNamed(const std::string& method_name,
+ SharedMethodCallbackExecutor method);
+
+ bool InvokeMethod(const std::string& method);
+
+ private:
+ typedef std::map<std::string, SharedMethodCallbackExecutor> MethodDictionary;
+
+ MethodDictionary method_dictionary_;
+};
+
+} // namespace tumbler
+#endif // EXAMPLES_TUMBLER_SCRIPTING_BRIDGE_H_
diff --git a/native_client_sdk/src/examples/tumbler/shader_util.cc b/native_client_sdk/src/examples/tumbler/shader_util.cc
new file mode 100644
index 0000000..2bbfc84
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/shader_util.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/tumbler/shader_util.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+namespace shader_util {
+
+GLuint CreateShaderOfType(GLenum type, const char *shader_src) {
+ GLuint shader;
+ GLint compiled;
+
+ // Create the shader object
+ shader = glCreateShader(type);
+
+ if (shader == 0)
+ return 0;
+
+ // Load and compile the shader source
+ glShaderSource(shader, 1, &shader_src, NULL);
+ glCompileShader(shader);
+
+ // Check the compile status
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (compiled == 0) {
+ GLint info_len = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_len);
+ if (info_len > 1) {
+ char* info_log = reinterpret_cast<char*>(malloc(sizeof(char) * info_len));
+ glGetShaderInfoLog(shader, info_len, NULL, info_log);
+ // TODO(dspringer): We could really use a logging API.
+ printf("Error compiling shader:\n%s\n", info_log);
+ free(info_log);
+ }
+ glDeleteShader(shader);
+ return 0;
+ }
+
+ return shader;
+}
+
+GLuint CreateProgramFromVertexAndFragmentShaders(
+ const char *vertex_shader_src, const char *fragment_shader_src) {
+ GLuint vertex_shader;
+ GLuint fragment_shader;
+ GLuint program_object;
+ GLint linked;
+
+ // Load the vertex/fragment shaders
+ vertex_shader = CreateShaderOfType(GL_VERTEX_SHADER, vertex_shader_src);
+ if (vertex_shader == 0)
+ return 0;
+ fragment_shader = CreateShaderOfType(GL_FRAGMENT_SHADER, fragment_shader_src);
+ if (fragment_shader == 0) {
+ glDeleteShader(vertex_shader);
+ return 0;
+ }
+
+ // Create the program object and attach the shaders.
+ program_object = glCreateProgram();
+ if (program_object == 0)
+ return 0;
+ glAttachShader(program_object, vertex_shader);
+ glAttachShader(program_object, fragment_shader);
+
+ // Link the program
+ glLinkProgram(program_object);
+
+ // Check the link status
+ glGetProgramiv(program_object, GL_LINK_STATUS, &linked);
+ if (linked == 0) {
+ GLint info_len = 0;
+ glGetProgramiv(program_object, GL_INFO_LOG_LENGTH, &info_len);
+ if (info_len > 1) {
+ char* info_log = reinterpret_cast<char*>(malloc(info_len));
+ glGetProgramInfoLog(program_object, info_len, NULL, info_log);
+ // TODO(dspringer): We could really use a logging API.
+ printf("Error linking program:\n%s\n", info_log);
+ free(info_log);
+ }
+ glDeleteProgram(program_object);
+ return 0;
+ }
+
+ // Delete these here because they are attached to the program object.
+ glDeleteShader(vertex_shader);
+ glDeleteShader(fragment_shader);
+
+ return program_object;
+}
+
+} // namespace shader_util
diff --git a/native_client_sdk/src/examples/tumbler/shader_util.h b/native_client_sdk/src/examples/tumbler/shader_util.h
new file mode 100644
index 0000000..635b16b
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/shader_util.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Some simple helper functions that load shaders and create program objects.
+
+#ifndef EXAMPLES_TUMBLER_SHADER_UTIL_H_
+#define EXAMPLES_TUMBLER_SHADER_UTIL_H_
+
+#include <GLES2/gl2.h>
+
+namespace shader_util {
+
+// Load and compile a shader. |type| can be one of GL_VERTEX_SHADER or
+// GL_FRAGMENT_SHADER. Returns a non-0 value representing the compiled
+// shader on success, 0 on failure. The caller is responsible for deleting
+// the returned shader using glDeleteShader().
+GLuint CreateShaderOfType(GLenum type, const char *shader_src);
+
+// Load and compile the vertex and fragment shaders, then link these together
+// into a complete program. Returns a non-0 value representing the program on,
+// success or 0 on failure. The caller is responsible for deleting the
+// returned program using glDeleteProgram().
+GLuint CreateProgramFromVertexAndFragmentShaders(
+ const char *vertex_shader_src, const char *fragment_shader_src);
+
+} // namespace shader_util
+
+#endif // EXAMPLES_TUMBLER_SHADER_UTIL_H_
diff --git a/native_client_sdk/src/examples/tumbler/trackball.js b/native_client_sdk/src/examples/tumbler/trackball.js
new file mode 100644
index 0000000..88b9a62
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/trackball.js
@@ -0,0 +1,296 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Implement a virtual trackball in the tumbler.Trackball
+ * class. This class maps 2D mouse events to 3D rotations by simulating a
+ * trackball that you roll by dragging the mouse. There are two principle
+ * methods in the class: startAtPointInFrame which you use to begin a trackball
+ * simulation and rollToPoint, which you use while dragging the mouse. The
+ * rollToPoint method returns a rotation expressed as a quaternion.
+ */
+
+
+// Requires tumbler.Application
+// Requires tumbler.DragEvent
+// Requires tumbler.Vector3
+
+/**
+ * Constructor for the Trackball object. This class maps 2D mouse drag events
+ * into 3D rotations by simulating a trackball. The idea is to simulate
+ * clicking on the trackball, and then rolling it as you drag the mouse.
+ * The math behind the trackball is simple: start with a vector from the first
+ * mouse-click on the ball to the center of the 3D view. At the same time, set
+ * the radius of the ball to be the smaller dimension of the 3D view. As you
+ * drag the mouse around in the 3D view, a second vector is computed from the
+ * surface of the ball to the center. The axis of rotation is the cross
+ * product of these two vectors, and the angle of rotation is the angle between
+ * the two vectors.
+ * @constructor
+ */
+tumbler.Trackball = function() {
+ /**
+ * The square of the trackball's radius. The math never looks at the radius,
+ * but looks at the radius squared.
+ * @type {number}
+ * @private
+ */
+ this.sqrRadius_ = 0;
+
+ /**
+ * The 3D vector representing the point on the trackball where the mouse
+ * was clicked. Default is pointing stright through the center of the ball.
+ * @type {Object}
+ * @private
+ */
+ this.rollStart_ = new tumbler.Vector3(0, 0, 1);
+
+ /**
+ * The 2D center of the frame that encloses the trackball.
+ * @type {!Object}
+ * @private
+ */
+ this.center_ = { x: 0, y: 0 };
+
+ /**
+ * Cached camera orientation. When a drag START event happens this is set to
+ * the current orientation in the calling view's plugin. The default is the
+ * identity quaternion.
+ * @type {Array.<number>}
+ * @private
+ */
+ this.cameraOrientation_ = [0, 0, 0, 1];
+};
+
+/**
+ * Compute the dimensions of the virtual trackball to fit inside |frameSize|.
+ * The radius of the trackball is set to be 1/2 of the smaller of the two frame
+ * dimensions, the center point is at the midpoint of each side.
+ * @param {!goog.math.Size} frameSize 2D-point representing the size of the
+ * element that encloses the virtual trackball.
+ * @private
+ */
+tumbler.Trackball.prototype.initInFrame_ = function(frameSize) {
+ // Compute the radius of the virtual trackball. This is 1/2 of the smaller
+ // of the frame's width and height.
+ var halfFrameSize = 0.5 * Math.min(frameSize.width, frameSize.height);
+ // Cache the square of the trackball's radius.
+ this.sqrRadius_ = halfFrameSize * halfFrameSize;
+ // Figure the center of the view.
+ this.center_.x = frameSize.width * 0.5;
+ this.center_.y = frameSize.height * 0.5;
+};
+
+/**
+ * Method to convert (by translation) a 2D client point from a coordinate space
+ * with origin in the lower-left corner of the client view to a space with
+ * origin in the center of the client view. Use this method before mapping the
+ * 2D point to he 3D tackball point (see also the projectOnTrackball_() method).
+ * Call the startAtPointInFrame before calling this method so that the
+ * |center_| property is correctly initialized.
+ * @param {!Object} clientPoint map this point to the coordinate space with
+ * origin in thecenter of the client view.
+ * @return {Object} the converted point.
+ * @private
+ */
+tumbler.Trackball.prototype.convertClientPoint_ = function(clientPoint) {
+ var difference = { x: clientPoint.x - this.center_.x,
+ y: clientPoint.y - this.center_.y }
+ return difference;
+};
+
+/**
+ * Method to map a 2D point to a 3D point on the virtual trackball that was set
+ * up using the startAtPointInFrame method. If the point lies outside of the
+ * radius of the virtual trackball, then the z-coordinate of the 3D point
+ * is set to 0.
+ * @param {!Object.<x, y>} point 2D-point in the coordinate space with origin
+ * in the center of the client view.
+ * @return {tumbler.Vector3} the 3D point on the virtual trackball.
+ * @private
+ */
+tumbler.Trackball.prototype.projectOnTrackball_ = function(point) {
+ var sqrRadius2D = point.x * point.x + point.y * point.y;
+ var zValue;
+ if (sqrRadius2D > this.sqrRadius_) {
+ // |point| lies outside the virtual trackball's sphere, so use a virtual
+ // z-value of 0. This is equivalent to clicking on the horizontal equator
+ // of the trackball.
+ zValue = 0;
+ } else {
+ // A sphere can be defined as: r^2 = x^2 + y^2 + z^2, so z =
+ // sqrt(r^2 - (x^2 + y^2)).
+ zValue = Math.sqrt(this.sqrRadius_ - sqrRadius2D);
+ }
+ var trackballPoint = new tumbler.Vector3(point.x, point.y, zValue);
+ return trackballPoint;
+};
+
+/**
+ * Method to start up the trackball. The trackball works by pretending that a
+ * ball encloses the 3D view. You roll this pretend ball with the mouse. For
+ * example, if you click on the center of the ball and move the mouse straight
+ * to the right, you roll the ball around its Y-axis. This produces a Y-axis
+ * rotation. You can click on the "edge" of the ball and roll it around
+ * in a circle to get a Z-axis rotation.
+ * @param {!Object.<x, y>} startPoint 2D-point, usually the mouse-down
+ * point.
+ * @param {!Object.<width, height>} frameSize 2D-point representing the size of
+ * the element that encloses the virtual trackball.
+ */
+tumbler.Trackball.prototype.startAtPointInFrame =
+ function(startPoint, frameSize) {
+ this.initInFrame_(frameSize);
+ // Compute the starting vector from the surface of the ball to its center.
+ this.rollStart_ = this.projectOnTrackball_(
+ this.convertClientPoint_(startPoint));
+};
+
+/**
+ * Method to roll the virtual trackball; call this in response to a mouseDrag
+ * event. Takes |dragPoint| and projects it from 2D mouse coordinates onto the
+ * virtual track ball that was set up in startAtPointInFrame method.
+ * Returns a quaternion that represents the rotation from |rollStart_| to
+ * |rollEnd_|.
+ * @param {!Object.<x, y>} dragPoint 2D-point representing the
+ * destination mouse point.
+ * @return {Array.<number>} a quaternion that represents the rotation from
+ * the point wnere the mouse was clicked on the trackball to this point.
+ * The quaternion looks like this: [[v], cos(angle/2)], where [v] is the
+ * imaginary part of the quaternion and is computed as [x, y, z] *
+ * sin(angle/2).
+ */
+tumbler.Trackball.prototype.rollToPoint = function(dragPoint) {
+ var rollTo = this.convertClientPoint_(dragPoint);
+ if ((Math.abs(this.rollStart_.x - rollTo.x) <
+ tumbler.Trackball.DOUBLE_EPSILON) &&
+ (Math.abs(this.rollStart_.y, rollTo.y) <
+ tumbler.Trackball.DOUBLE_EPSILON)) {
+ // Not enough change in the vectors to roll the ball, return the identity
+ // quaternion.
+ return [0, 0, 0, 1];
+ }
+
+ // Compute the ending vector from the surface of the ball to its center.
+ var rollEnd = this.projectOnTrackball_(rollTo);
+
+ // Take the cross product of the two vectors. r = s X e
+ var rollVector = this.rollStart_.cross(rollEnd);
+ var invStartMag = 1.0 / this.rollStart_.magnitude();
+ var invEndMag = 1.0 / rollEnd.magnitude();
+
+ // cos(a) = (s . e) / (||s|| ||e||)
+ var cosAng = this.rollStart_.dot(rollEnd) * invStartMag * invEndMag;
+ // sin(a) = ||(s X e)|| / (||s|| ||e||)
+ var sinAng = rollVector.magnitude() * invStartMag * invEndMag;
+ // Build a quaternion that represents the rotation about |rollVector|.
+ // Use atan2 for a better angle. If you use only cos or sin, you only get
+ // half the possible angles, and you can end up with rotations that flip
+ // around near the poles.
+ var rollHalfAngle = Math.atan2(sinAng, cosAng) * 0.5;
+ rollVector.normalize();
+ // The quaternion looks like this: [[v], cos(angle/2)], where [v] is the
+ // imaginary part of the quaternion and is computed as [x, y, z] *
+ // sin(angle/2).
+ rollVector.scale(Math.sin(rollHalfAngle));
+ var ballQuaternion = [rollVector.x,
+ rollVector.y,
+ rollVector.z,
+ Math.cos(rollHalfAngle)];
+ return ballQuaternion;
+};
+
+/**
+ * Handle the drag START event: grab the current camera orientation from the
+ * sending view and set up the virtual trackball.
+ * @param {!tumbler.Application} view The view controller that called this
+ * method.
+ * @param {!tumbler.DragEvent} dragStartEvent The DRAG_START event that
+ * triggered this handler.
+ */
+tumbler.Trackball.prototype.handleStartDrag =
+ function(controller, dragStartEvent) {
+ // Cache the camera orientation. The orientations from the trackball as it
+ // rolls are concatenated to this orientation and pushed back into the
+ // plugin on the other side of the JavaScript bridge.
+ controller.setCameraOrientation(this.cameraOrientation_);
+ // Invert the y-coordinate for the trackball computations.
+ var frameSize = { width: controller.offsetWidth,
+ height: controller.offsetHeight };
+ var flippedY = { x: dragStartEvent.clientX,
+ y: frameSize.height - dragStartEvent.clientY };
+ this.startAtPointInFrame(flippedY, frameSize);
+};
+
+/**
+ * Handle the drag DRAG event: concatenate the current orientation to the
+ * cached orientation. Send this final value through to the GSPlugin via the
+ * setValueForKey() method.
+ * @param {!tumbler.Application} view The view controller that called this
+ * method.
+ * @param {!tumbler.DragEvent} dragEvent The DRAG event that triggered this
+ * handler.
+ */
+tumbler.Trackball.prototype.handleDrag =
+ function(controller, dragEvent) {
+ // Flip the y-coordinate so that the 2D origin is in the lower-left corner.
+ var frameSize = { width: controller.offsetWidth,
+ height: controller.offsetHeight };
+ var flippedY = { x: dragEvent.clientX,
+ y: frameSize.height - dragEvent.clientY };
+ controller.setCameraOrientation(
+ tumbler.multQuaternions(this.rollToPoint(flippedY),
+ this.cameraOrientation_));
+};
+
+/**
+ * Handle the drag END event: get the final orientation and concatenate it to
+ * the cached orientation.
+ * @param {!tumbler.Application} view The view controller that called this
+ * method.
+ * @param {!tumbler.DragEvent} dragEndEvent The DRAG_END event that triggered
+ * this handler.
+ */
+tumbler.Trackball.prototype.handleEndDrag =
+ function(controller, dragEndEvent) {
+ // Flip the y-coordinate so that the 2D origin is in the lower-left corner.
+ var frameSize = { width: controller.offsetWidth,
+ height: controller.offsetHeight };
+ var flippedY = { x: dragEndEvent.clientX,
+ y: frameSize.height - dragEndEvent.clientY };
+ this.cameraOrientation_ = tumbler.multQuaternions(this.rollToPoint(flippedY),
+ this.cameraOrientation_);
+ controller.setCameraOrientation(this.cameraOrientation_);
+};
+
+/**
+ * A utility function to multiply two quaterions. Returns the product q0 * q1.
+ * This is effectively the same thing as concatenating the two rotations
+ * represented in each quaternion together. Note that quaternion multiplication
+ * is NOT commutative: q0 * q1 != q1 * q0.
+ * @param {!Array.<number>} q0 A 4-element array representing the first
+ * quaternion.
+ * @param {!Array.<number>} q1 A 4-element array representing the second
+ * quaternion.
+ * @return {Array.<number>} A 4-element array representing the product q0 * q1.
+ */
+tumbler.multQuaternions = function(q0, q1) {
+ // Return q0 * q1 (note the order).
+ var qMult = [
+ q0[3] * q1[0] + q0[0] * q1[3] + q0[1] * q1[2] - q0[2] * q1[1],
+ q0[3] * q1[1] - q0[0] * q1[2] + q0[1] * q1[3] + q0[2] * q1[0],
+ q0[3] * q1[2] + q0[0] * q1[1] - q0[1] * q1[0] + q0[2] * q1[3],
+ q0[3] * q1[3] - q0[0] * q1[0] - q0[1] * q1[1] - q0[2] * q1[2]
+ ];
+ return qMult;
+};
+
+/**
+ * Real numbers that are less than this distance apart are considered
+ * equivalent.
+ * TODO(dspringer): It seems as though there should be a const like this
+ * in Closure somewhere (goog.math?).
+ * @type {number}
+ */
+tumbler.Trackball.DOUBLE_EPSILON = 1.0e-16;
diff --git a/native_client_sdk/src/examples/tumbler/transforms.cc b/native_client_sdk/src/examples/tumbler/transforms.cc
new file mode 100644
index 0000000..79cb9cf
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/transforms.cc
@@ -0,0 +1,116 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/tumbler/transforms.h"
+
+#include <math.h>
+#include <string.h>
+#include <GLES2/gl2.h>
+
+namespace transform_4x4 {
+
+static const GLfloat kPI = 3.1415926535897932384626433832795f;
+
+void Translate(GLfloat* m, GLfloat tx, GLfloat ty, GLfloat tz) {
+ m[12] += (m[0] * tx + m[4] * ty + m[8] * tz);
+ m[13] += (m[1] * tx + m[5] * ty + m[9] * tz);
+ m[14] += (m[2] * tx + m[6] * ty + m[10] * tz);
+ m[15] += (m[3] * tx + m[7] * ty + m[11] * tz);
+}
+
+void Frustum(GLfloat* m,
+ GLfloat left,
+ GLfloat right,
+ GLfloat bottom,
+ GLfloat top,
+ GLfloat near_z,
+ GLfloat far_z) {
+ GLfloat delta_x = right - left;
+ GLfloat delta_y = top - bottom;
+ GLfloat delta_z = far_z - near_z;
+ GLfloat frustum[16];
+
+ if ((near_z <= 0.0f) || (far_z <= 0.0f) ||
+ (delta_x <= 0.0f) || (delta_y <= 0.0f) || (delta_z <= 0.0f))
+ return;
+
+ frustum[0] = 2.0f * near_z / delta_x;
+ frustum[1] = frustum[2] = frustum[3] = 0.0f;
+
+ frustum[5] = 2.0f * near_z / delta_y;
+ frustum[4] = frustum[6] = frustum[7] = 0.0f;
+
+ frustum[8] = (right + left) / delta_x;
+ frustum[9] = (top + bottom) / delta_y;
+ frustum[10] = -(near_z + far_z) / delta_z;
+ frustum[11] = -1.0f;
+
+ frustum[14] = -2.0f * near_z * far_z / delta_z;
+ frustum[12] = frustum[13] = frustum[15] = 0.0f;
+
+ transform_4x4::Multiply(m, frustum, m);
+}
+
+
+void Perspective(GLfloat* m,
+ GLfloat fovy,
+ GLfloat aspect,
+ GLfloat near_z,
+ GLfloat far_z) {
+ GLfloat frustum_w, frustum_h;
+
+ frustum_h = tanf((fovy * 0.5f) / 180.0f * kPI) * near_z;
+ frustum_w = frustum_h * aspect;
+ transform_4x4::Frustum(m, -frustum_w, frustum_w, -frustum_h, frustum_h,
+ near_z, far_z);
+}
+
+void Multiply(GLfloat *m, GLfloat *a, GLfloat* b) {
+ GLfloat tmp[16];
+ // tmp = a . b
+ GLfloat a0, a1, a2, a3;
+ a0 = a[0];
+ a1 = a[1];
+ a2 = a[2];
+ a3 = a[3];
+ tmp[0] = a0 * b[0] + a1 * b[4] + a2 * b[8] + a3 * b[12];
+ tmp[1] = a0 * b[1] + a1 * b[5] + a2 * b[9] + a3 * b[13];
+ tmp[2] = a0 * b[2] + a1 * b[6] + a2 * b[10] + a3 * b[14];
+ tmp[3] = a0 * b[3] + a1 * b[7] + a2 * b[11] + a3 * b[15];
+
+ a0 = a[4];
+ a1 = a[5];
+ a2 = a[6];
+ a3 = a[7];
+ tmp[4] = a0 * b[0] + a1 * b[4] + a2 * b[8] + a3 * b[12];
+ tmp[5] = a0 * b[1] + a1 * b[5] + a2 * b[9] + a3 * b[13];
+ tmp[6] = a0 * b[2] + a1 * b[6] + a2 * b[10] + a3 * b[14];
+ tmp[7] = a0 * b[3] + a1 * b[7] + a2 * b[11] + a3 * b[15];
+
+ a0 = a[8];
+ a1 = a[9];
+ a2 = a[10];
+ a3 = a[11];
+ tmp[8] = a0 * b[0] + a1 * b[4] + a2 * b[8] + a3 * b[12];
+ tmp[9] = a0 * b[1] + a1 * b[5] + a2 * b[9] + a3 * b[13];
+ tmp[10] = a0 * b[2] + a1 * b[6] + a2 * b[10] + a3 * b[14];
+ tmp[11] = a0 * b[3] + a1 * b[7] + a2 * b[11] + a3 * b[15];
+
+ a0 = a[12];
+ a1 = a[13];
+ a2 = a[14];
+ a3 = a[15];
+ tmp[12] = a0 * b[0] + a1 * b[4] + a2 * b[8] + a3 * b[12];
+ tmp[13] = a0 * b[1] + a1 * b[5] + a2 * b[9] + a3 * b[13];
+ tmp[14] = a0 * b[2] + a1 * b[6] + a2 * b[10] + a3 * b[14];
+ tmp[15] = a0 * b[3] + a1 * b[7] + a2 * b[11] + a3 * b[15];
+ memcpy(m, tmp, sizeof(GLfloat) * 4 * 4);
+}
+
+void LoadIdentity(GLfloat* m) {
+ memset(m, 0, sizeof(GLfloat) * 4 * 4);
+ m[0] = m[5] = m[10] = m[15] = 1.0f;
+}
+
+} // namespace transform_4x4
diff --git a/native_client_sdk/src/examples/tumbler/transforms.h b/native_client_sdk/src/examples/tumbler/transforms.h
new file mode 100644
index 0000000..5ac3d6e
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/transforms.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_TUMBLER_TRANSFORMS_H_
+#define EXAMPLES_TUMBLER_TRANSFORMS_H_
+
+#include <GLES2/gl2.h>
+
+// A very simple set of 4x4 matrix routines. In all these routines, the input
+// matrix is assumed to be a 4x4 of GLfloats.
+
+namespace transform_4x4 {
+
+// Pre-multply |m| with a projection transformation 4x4 matrix from a
+// truncated pyramid viewing frustum.
+void Frustum(GLfloat* m,
+ GLfloat left,
+ GLfloat right,
+ GLfloat bottom,
+ GLfloat top,
+ GLfloat near_z,
+ GLfloat far_z);
+
+// Replace |m| with the 4x4 identity matrix.
+void LoadIdentity(GLfloat* m);
+
+// |m| <- |a| . |b|. |m| can point at the same memory as either |a| or |b|.
+void Multiply(GLfloat *m, GLfloat *a, GLfloat* b);
+
+// Pre-multiply |m| with a single-point perspective matrix based on the viewing
+// frustum whose view angle is |fovy|.
+void Perspective(GLfloat* m,
+ GLfloat fovy,
+ GLfloat aspect,
+ GLfloat near_z,
+ GLfloat far_z);
+
+// Pre-multiply |m| with a matrix that represents a translation by |tx|, |ty|,
+// |tz|.
+void Translate(GLfloat* m, GLfloat tx, GLfloat ty, GLfloat tz);
+} // namespace transform_4x4
+
+#endif // EXAMPLES_TUMBLER_TRANSFORMS_H_
+
diff --git a/native_client_sdk/src/examples/tumbler/tumbler.cc b/native_client_sdk/src/examples/tumbler/tumbler.cc
new file mode 100644
index 0000000..57343c4
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/tumbler.cc
@@ -0,0 +1,137 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/tumbler/tumbler.h"
+
+#include <cstdlib>
+#include <cstring>
+#include <string>
+#include <vector>
+
+#include "examples/tumbler/cube.h"
+#include "examples/tumbler/opengl_context.h"
+#include "examples/tumbler/scripting_bridge.h"
+#include "ppapi/cpp/rect.h"
+#include "ppapi/cpp/size.h"
+#include "ppapi/cpp/var.h"
+
+namespace {
+const size_t kQuaternionElementCount = 4;
+const char* const kArrayStartCharacter = "[";
+const char* const kArrayEndCharacter = "]";
+const char* const kArrayDelimiter = ",";
+
+// Return the value of parameter named |param_name| from |parameters|. If
+// |param_name| doesn't exist, then return an empty string.
+std::string GetParameterNamed(
+ const std::string& param_name,
+ const tumbler::MethodParameter& parameters) {
+ tumbler::MethodParameter::const_iterator i =
+ parameters.find(param_name);
+ if (i == parameters.end()) {
+ return "";
+ }
+ return i->second;
+}
+
+// Convert the JSON string |array| into a vector of floats. |array| is
+// expected to be a string bounded by '[' and ']', and a comma-delimited list
+// of numbers. Any errors result in the return of an empty array.
+std::vector<float> CreateArrayFromJSON(const std::string& json_array) {
+ std::vector<float> float_array;
+ size_t array_start_pos = json_array.find_first_of(kArrayStartCharacter);
+ size_t array_end_pos = json_array.find_last_of(kArrayEndCharacter);
+ if (array_start_pos == std::string::npos ||
+ array_end_pos == std::string::npos)
+ return float_array; // Malformed JSON: missing '[' or ']'.
+ // Pull out the array elements.
+ size_t token_pos = array_start_pos + 1;
+ while (token_pos < array_end_pos) {
+ float_array.push_back(strtof(json_array.data() + token_pos, NULL));
+ size_t delim_pos = json_array.find_first_of(kArrayDelimiter, token_pos);
+ if (delim_pos == std::string::npos)
+ break;
+ token_pos = delim_pos + 1;
+ }
+ return float_array;
+}
+} // namespace
+
+namespace tumbler {
+
+Tumbler::Tumbler(PP_Instance instance)
+ : pp::Instance(instance),
+ cube_(NULL) {
+}
+
+Tumbler::~Tumbler() {
+ // Destroy the cube view while GL context is current.
+ opengl_context_->MakeContextCurrent(this);
+ delete cube_;
+}
+
+bool Tumbler::Init(uint32_t /* argc */,
+ const char* /* argn */[],
+ const char* /* argv */[]) {
+ // Add all the methods to the scripting bridge.
+ ScriptingBridge::SharedMethodCallbackExecutor set_orientation_method(
+ new tumbler::MethodCallback<Tumbler>(
+ this, &Tumbler::SetCameraOrientation));
+ scripting_bridge_.AddMethodNamed("setCameraOrientation",
+ set_orientation_method);
+ return true;
+}
+
+void Tumbler::HandleMessage(const pp::Var& message) {
+ if (!message.is_string())
+ return;
+ scripting_bridge_.InvokeMethod(message.AsString());
+}
+
+void Tumbler::DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
+ int cube_width = cube_ ? cube_->width() : 0;
+ int cube_height = cube_ ? cube_->height() : 0;
+ if (position.size().width() == cube_width &&
+ position.size().height() == cube_height)
+ return; // Size didn't change, no need to update anything.
+
+ if (opengl_context_ == NULL)
+ opengl_context_.reset(new OpenGLContext(this));
+ opengl_context_->InvalidateContext(this);
+ opengl_context_->ResizeContext(position.size());
+ if (!opengl_context_->MakeContextCurrent(this))
+ return;
+ if (cube_ == NULL) {
+ cube_ = new Cube(opengl_context_);
+ cube_->PrepareOpenGL();
+ }
+ cube_->Resize(position.size().width(), position.size().height());
+ DrawSelf();
+}
+
+void Tumbler::DrawSelf() {
+ if (cube_ == NULL || opengl_context_ == NULL)
+ return;
+ opengl_context_->MakeContextCurrent(this);
+ cube_->Draw();
+ opengl_context_->FlushContext();
+}
+
+void Tumbler::SetCameraOrientation(
+ const tumbler::ScriptingBridge& bridge,
+ const tumbler::MethodParameter& parameters) {
+ // |parameters| is expected to contain one object named "orientation", whose
+ // value is a JSON string that represents an array of four floats.
+ if (parameters.size() != 1 || cube_ == NULL)
+ return;
+ std::string orientation_desc = GetParameterNamed("orientation", parameters);
+ std::vector<float> orientation = CreateArrayFromJSON(orientation_desc);
+ if (orientation.size() != kQuaternionElementCount) {
+ return;
+ }
+ cube_->SetOrientation(orientation);
+ DrawSelf();
+}
+} // namespace tumbler
+
diff --git a/native_client_sdk/src/examples/tumbler/tumbler.h b/native_client_sdk/src/examples/tumbler/tumbler.h
new file mode 100644
index 0000000..1738ec3
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/tumbler.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_TUMBLER_TUMBLER_H_
+#define EXAMPLES_TUMBLER_TUMBLER_H_
+
+#include <pthread.h>
+#include <map>
+#include <vector>
+
+#include "examples/tumbler/cube.h"
+#include "examples/tumbler/opengl_context.h"
+#include "examples/tumbler/opengl_context_ptrs.h"
+#include "examples/tumbler/scripting_bridge.h"
+#include "ppapi/cpp/instance.h"
+
+namespace tumbler {
+
+class Tumbler : public pp::Instance {
+ public:
+ explicit Tumbler(PP_Instance instance);
+
+ // The dtor makes the 3D context current before deleting the cube view, then
+ // destroys the 3D context both in the module and in the browser.
+ virtual ~Tumbler();
+
+ // Called by the browser when the NaCl module is loaded and all ready to go.
+ virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]);
+
+ // Called whenever the in-browser window changes size.
+ virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip);
+
+ // Called by the browser to handle the postMessage() call in Javascript.
+ virtual void HandleMessage(const pp::Var& message);
+
+ // Bind and publish the module's methods to JavaScript.
+ void InitializeMethods(ScriptingBridge* bridge);
+
+ // Set the camera orientation to the quaternion in |args[0]|. |args| must
+ // have length at least 1; the first element is expeted to be an Array
+ // object containing 4 floating point number elements (the quaternion).
+ // This method is bound to the JavaScript "setCameraOrientation" method and
+ // is called like this:
+ // module.setCameraOrientation([0.0, 1.0, 0.0, 0.0]);
+ void SetCameraOrientation(
+ const tumbler::ScriptingBridge& bridge,
+ const tumbler::MethodParameter& parameters);
+
+ // Called to draw the contents of the module's browser area.
+ void DrawSelf();
+
+ private:
+ // Browser connectivity and scripting support.
+ ScriptingBridge scripting_bridge_;
+
+ SharedOpenGLContext opengl_context_;
+ // Wouldn't it be awesome if we had boost::scoped_ptr<>?
+ Cube* cube_;
+};
+
+} // namespace tumbler
+
+#endif // EXAMPLES_TUMBLER_TUMBLER_H_
diff --git a/native_client_sdk/src/examples/tumbler/tumbler.html b/native_client_sdk/src/examples/tumbler/tumbler.html
new file mode 100644
index 0000000..a3002da
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/tumbler.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+ <!--
+ Copyright (c) 2011 The Native Client Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+ <head>
+ <title>Interactive Cube Example</title>
+ <script type="text/javascript">
+ // Provide the tumbler namespace
+ tumbler = {};
+ </script>
+ <script type="text/javascript" src="bind.js"></script>
+ <script type="text/javascript" src="dragger.js"></script>
+ <script type="text/javascript" src="tumbler.js"></script>
+ <script type="text/javascript" src="vector3.js"></script>
+ <script type="text/javascript" src="trackball.js"></script>
+ </head>
+ <body id="bodyId">
+ <h1>Interactive Cube Example</h1>
+ <p>
+ The Native Client module executed in this page draws a 3D cube
+ and allows you to rotate it using a virtual trackball method.
+ </p>
+ <div id="tumbler_view"></div>
+ <script type="text/javascript">
+ tumbler.application = new tumbler.Application();
+ tumbler.application.run('tumbler_view');
+ </script>
+ </body>
+</HTML>
diff --git a/native_client_sdk/src/examples/tumbler/tumbler.js b/native_client_sdk/src/examples/tumbler/tumbler.js
new file mode 100644
index 0000000..e8e42eb
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/tumbler.js
@@ -0,0 +1,133 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview The tumbler Application object. This object instantiates a
+ * Trackball object and connects it to the element named |tumbler_content|.
+ * It also conditionally embeds a debuggable module or a release module into
+ * the |tumbler_content| element.
+ */
+
+// Requires tumbler
+// Requires tumbler.Dragger
+// Requires tumbler.Trackball
+
+/**
+ * Constructor for the Application class. Use the run() method to populate
+ * the object with controllers and wire up the events.
+ * @constructor
+ */
+tumbler.Application = function() {
+ /**
+ * The native module for the application. This refers to the module loaded
+ * via the <embed> tag.
+ * @type {Element}
+ * @private
+ */
+ this.module_ = null;
+
+ /**
+ * The trackball object.
+ * @type {tumbler.Trackball}
+ * @private
+ */
+ this.trackball_ = null;
+
+ /**
+ * The mouse-drag event object.
+ * @type {tumbler.Dragger}
+ * @private
+ */
+ this.dragger_ = null;
+
+ /**
+ * The function objects that get attached as event handlers. These are
+ * cached so that they can be removed when they are no longer needed.
+ * @type {function}
+ * @private
+ */
+ this.boundModuleDidLoad_ = null;
+}
+
+/**
+ * The ids used for elements in the DOM. The Tumlber Application expects these
+ * elements to exist.
+ * @enum {string}
+ * @private
+ */
+tumbler.Application.DomIds_ = {
+ MODULE: 'tumbler', // The <embed> element representing the NaCl module
+ VIEW: 'tumbler_view' // The <div> containing the NaCl element.
+}
+
+/**
+ * Called by the module loading function once the module has been loaded.
+ * @param {?Element} nativeModule The instance of the native module.
+ */
+tumbler.Application.prototype.moduleDidLoad = function() {
+ this.module_ = document.getElementById(tumbler.Application.DomIds_.MODULE);
+ // Unbind the load function.
+ this.boundModuleDidLoad_ = null;
+
+ /**
+ * Set the camera orientation property on the NaCl module.
+ * @param {Array.<number>} orientation A 4-element array representing the
+ * camera orientation as a quaternion.
+ */
+ this.module_.setCameraOrientation = function(orientation) {
+ var methodString = 'setCameraOrientation ' +
+ 'orientation:' +
+ JSON.stringify(orientation);
+ this.postMessage(methodString);
+ }
+
+ this.trackball_ = new tumbler.Trackball();
+ this.dragger_ = new tumbler.Dragger(this.module_);
+ this.dragger_.addDragListener(this.trackball_);
+}
+
+/**
+ * Asserts that cond is true; issues an alert and throws an Error otherwise.
+ * @param {bool} cond The condition.
+ * @param {String} message The error message issued if cond is false.
+ */
+tumbler.Application.prototype.assert = function(cond, message) {
+ if (!cond) {
+ message = "Assertion failed: " + message;
+ alert(message);
+ throw new Error(message);
+ }
+}
+
+/**
+ * The run() method starts and 'runs' the application. The trackball object
+ * is allocated and all the events get wired up.
+ * @param {?String} opt_contentDivName The id of a DOM element in which to
+ * embed the Native Client module. If unspecified, defaults to
+ * VIEW. The DOM element must exist.
+ */
+tumbler.Application.prototype.run = function(opt_contentDivName) {
+ contentDivName = opt_contentDivName || tumbler.Application.DomIds_.VIEW;
+ var contentDiv = document.getElementById(contentDivName);
+ this.assert(contentDiv, "Missing DOM element '" + contentDivName + "'");
+
+ // Note that the <EMBED> element is wrapped inside a <DIV>, which has a 'load'
+ // event listener attached. This method is used instead of attaching the
+ // 'load' event listener directly to the <EMBED> element to ensure that the
+ // listener is active before the NaCl module 'load' event fires.
+ this.boundModuleDidLoad_ = this.moduleDidLoad.bind(this);
+ contentDiv.addEventListener('load', this.boundModuleDidLoad_, true);
+
+ // Load the published .nexe. This includes the 'nacl' attribute which
+ // shows how to load multi-architecture modules. Each entry in the "nexes"
+ // object in the .nmf manifest file is a key-value pair: the key is the
+ // runtime ('x86-32', 'x86-64', etc.); the value is a URL for the desired
+ // NaCl module. To load the debug versions of your .nexes, set the 'nacl'
+ // attribute to the _dbg.nmf version of the manifest file.
+ contentDiv.innerHTML = '<embed id="'
+ + tumbler.Application.DomIds_.MODULE + '" '
+ + 'src=tumbler.nmf '
+ + 'type="application/x-nacl" '
+ + 'width="480" height="480" />'
+}
diff --git a/native_client_sdk/src/examples/tumbler/tumbler_module.cc b/native_client_sdk/src/examples/tumbler/tumbler_module.cc
new file mode 100644
index 0000000..932e564
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/tumbler_module.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/tumbler/tumbler.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/gles2/gl2ext_ppapi.h"
+
+/// The Module class. The browser calls the CreateInstance() method to create
+/// an instance of your NaCl module on the web page. The browser creates a new
+/// instance for each <embed> tag with type="application/x-nacl".
+class TumberModule : public pp::Module {
+ public:
+ TumberModule() : pp::Module() {}
+ virtual ~TumberModule() {
+ glTerminatePPAPI();
+ }
+
+ /// Called by the browser when the module is first loaded and ready to run.
+ /// This is called once per module, not once per instance of the module on
+ /// the page.
+ virtual bool Init() {
+ return glInitializePPAPI(get_browser_interface()) == GL_TRUE;
+ }
+
+ /// Create and return a Tumbler instance object.
+ /// @param[in] instance The browser-side instance.
+ /// @return the plugin-side instance.
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new tumbler::Tumbler(instance);
+ }
+};
+
+namespace pp {
+/// Factory function called by the browser when the module is first loaded.
+/// The browser keeps a singleton of this module. It calls the
+/// CreateInstance() method on the object you return to make instances. There
+/// is one instance per <embed> tag on the page. This is the main binding
+/// point for your NaCl module with the browser.
+Module* CreateModule() {
+ return new TumberModule();
+}
+} // namespace pp
+
diff --git a/native_client_sdk/src/examples/tumbler/vector3.js b/native_client_sdk/src/examples/tumbler/vector3.js
new file mode 100644
index 0000000..a79f781
--- /dev/null
+++ b/native_client_sdk/src/examples/tumbler/vector3.js
@@ -0,0 +1,91 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview A 3D vector class. Proviudes some utility functions on
+ * 3-dimentional vectors.
+ */
+
+// Requires tumbler
+
+/**
+ * Constructor for the Vector3 object. This class contains a 3-tuple that
+ * represents a vector in 3D space.
+ * @param {?number} opt_x The x-coordinate for this vector. If null or
+ * undefined, the x-coordinate value is set to 0.
+ * @param {?number} opt_y The y-coordinate for this vector. If null or
+ * undefined, the y-coordinate value is set to 0.
+ * @param {?number} opt_z The z-coordinate for this vector. If null or
+ * undefined, the z-coordinate value is set to 0.
+ * @constructor
+ */
+tumbler.Vector3 = function(opt_x, opt_y, opt_z) {
+ /**
+ * The vector's 3-tuple.
+ * @type {number}
+ */
+ this.x = opt_x || 0;
+ this.y = opt_y || 0;
+ this.z = opt_z || 0;
+}
+
+/**
+ * Method to return the magnitude of a Vector3.
+ * @return {number} the magnitude of the vector.
+ */
+tumbler.Vector3.prototype.magnitude = function() {
+ return Math.sqrt(this.dot(this));
+}
+
+/**
+ * Normalize the vector in-place.
+ * @return {number} the magnitude of the vector.
+ */
+tumbler.Vector3.prototype.normalize = function() {
+ var mag = this.magnitude();
+ if (mag < tumbler.Vector3.DOUBLE_EPSILON)
+ return 0.0; // |this| is equivalent to the 0-vector, don't normalize.
+ this.scale(1.0 / mag);
+ return mag;
+}
+
+/**
+ * Scale the vector in-place by |s|.
+ * @param {!number} s The scale factor.
+ */
+tumbler.Vector3.prototype.scale = function(s) {
+ this.x *= s;
+ this.y *= s;
+ this.z *= s;
+}
+
+/**
+ * Compute the dot product: |this| . v.
+ * @param {!tumbler.Vector3} v The vector to dot.
+ * @return {number} the result of |this| . v.
+ */
+tumbler.Vector3.prototype.dot = function(v) {
+ return this.x * v.x + this.y * v.y + this.z * v.z;
+}
+
+/**
+ * Compute the cross product: |this| X v.
+ * @param {!tumbler.Vector3} v The vector to cross with.
+ * @return {tumbler.Vector3} the result of |this| X v.
+ */
+tumbler.Vector3.prototype.cross = function(v) {
+ var vCross = new tumbler.Vector3(this.y * v.z - this.z * v.y,
+ this.z * v.x - this.x * v.z,
+ this.x * v.y - this.y * v.x);
+ return vCross;
+}
+
+/**
+ * Real numbers that are less than this distance apart are considered
+ * equivalent.
+ * TODO(dspringer): It seems as though there should be a const like this
+ * in generally available somewhere.
+ * @type {number}
+ */
+tumbler.Vector3.DOUBLE_EPSILON = 1.0e-16;
diff --git a/native_client_sdk/src/libraries/build.scons b/native_client_sdk/src/libraries/build.scons
new file mode 100644
index 0000000..abc1abc
--- /dev/null
+++ b/native_client_sdk/src/libraries/build.scons
@@ -0,0 +1,41 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Build file for the NaCl SDK Libraries
+
+This file runs all the scons files in the various libraries sub-directories.
+Do not invoke this script directly, but instead use the scons or scons.bat
+wrapper function. E.g.
+
+Linux or Mac:
+ ./scons [Options...]
+
+Windows:
+ scons.bat [Options...]
+"""
+
+#------------------------------------------------------------------------------
+HELP_STRING = """
+===============================================================================
+Help for NaCl SDK Libraries
+===============================================================================
+
+* cleaning: ./scons -c
+* build a target: ./scons <target>
+* clean a target: ./scons -c <target>
+
+Supported targets:
+ * gtest_libs Build the gtest and gmock libraries.
+"""
+
+libraries_sconscripts = [
+ 'gtest/build.scons',
+ ]
+
+Help(HELP_STRING)
+
+SConscript(libraries_sconscripts)
diff --git a/native_client_sdk/src/libraries/c_salt/test/gtest_event_listener.cc b/native_client_sdk/src/libraries/c_salt/test/gtest_event_listener.cc
new file mode 100644
index 0000000..0afb3ed
--- /dev/null
+++ b/native_client_sdk/src/libraries/c_salt/test/gtest_event_listener.cc
@@ -0,0 +1,100 @@
+// Copyright 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "c_salt/test/gtest_event_listener.h"
+
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/cpp/var.h"
+
+namespace c_salt {
+
+GTestEventListener::GTestEventListener(pp::Instance* instance)
+ : instance_(instance),
+ factory_(this) {
+ assert(pp::Module::Get()->core()->IsMainThread());
+}
+
+void GTestEventListener::OnTestProgramStart(
+ const ::testing::UnitTest& unit_test) {
+ std::stringstream msg;
+ int num_tests = unit_test.test_to_run_count();
+ int num_test_cases = unit_test.test_case_to_run_count();
+
+ msg << "::start::" << num_tests << " test";
+ if (num_tests > 1) msg << 's';
+ msg << " from "<< num_test_cases << " test case";
+ if (num_test_cases > 1) msg << 's';
+ msg << '.';
+ PostMessage(msg.str());
+}
+
+void GTestEventListener::OnTestCaseStart(
+ const ::testing::TestCase& test_case) {
+ // not currently used
+}
+
+void GTestEventListener::OnTestStart(const ::testing::TestInfo& test_info) {
+ // not currently used
+}
+
+void GTestEventListener::OnTestPartResult(
+ const ::testing::TestPartResult& test_part_result) {
+ if (test_part_result.failed()) {
+ std::stringstream msg;
+ msg << "::test_failed::";
+ msg << test_part_result.file_name();
+ msg << "::" << test_part_result.line_number() << "::";
+ msg << test_part_result.summary();
+ PostMessage(msg.str());
+
+ msg.str("");
+ msg << "::failure_log::";
+ msg << test_part_result.summary();
+ PostMessage(msg.str());
+ }
+}
+
+void GTestEventListener::OnTestEnd(const ::testing::TestInfo& test_info) {
+ std::stringstream msg;
+ msg << "::test_end::";
+ msg << test_info.test_case_name() << "." << test_info.name();
+
+ msg << (test_info.result()->Failed() ? ": FAILED" : ": OK");
+ PostMessage(msg.str());
+}
+
+void GTestEventListener::OnTestCaseEnd(
+ const ::testing::TestCase& test_case) {
+ // not used
+}
+
+void GTestEventListener::OnTestProgramEnd(
+ const ::testing::UnitTest& unit_test) {
+ // Print info about what test and test cases ran.
+ int num_passed_tests = unit_test.successful_test_count();
+ int num_failed_tests = unit_test.failed_test_count();
+ std::stringstream msg;
+ msg << "::Result::";
+ msg << ((num_failed_tests > 0) ? "failed::" : "success::");
+ msg << num_passed_tests << "::" << num_failed_tests << "::";
+ PostMessage(msg.str());
+}
+
+void GTestEventListener::PostMessage(const std::string& str) {
+ if (pp::Module::Get()->core()->IsMainThread()) {
+ instance_->PostMessage(str);
+ } else {
+ pp::CompletionCallback cc = factory_.NewCallback(
+ &GTestEventListener::PostMessageCallback, str);
+ pp::Module::Get()->core()->CallOnMainThread(0, cc, PP_OK);
+ }
+}
+
+void GTestEventListener::PostMessageCallback(int32_t result,
+ const std::string& str) {
+ instance_->PostMessage(str);
+}
+
+} // namespace c_salt
+
diff --git a/native_client_sdk/src/libraries/c_salt/test/gtest_event_listener.h b/native_client_sdk/src/libraries/c_salt/test/gtest_event_listener.h
new file mode 100644
index 0000000..6419683
--- /dev/null
+++ b/native_client_sdk/src/libraries/c_salt/test/gtest_event_listener.h
@@ -0,0 +1,51 @@
+// Copyright 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef C_SALT_TEST_GTEST_EVENT_LISTENER_H_
+#define C_SALT_TEST_GTEST_EVENT_LISTENER_H_
+
+#include <string>
+#include "c_salt/threading/ref_count.h"
+#include "gtest/gtest.h"
+#include "ppapi/cpp/completion_callback.h"
+
+namespace pp {
+class Instance;
+} // namespace pp
+
+namespace c_salt {
+
+// GTestEventListener is a gtest event listener that performs two functions:
+// 1. It redirects output to PostMessage rather than printf, so that test events
+// are dispatched to JS.
+// 2. Channels post-message dispatches to the main thread via callbacks, so that
+// it can be used from any thread.
+//
+// GTestEventListener formats gtest event messages to be parsed by javascript
+// helper functions found in gtest_runner.js
+class GTestEventListener : public ::testing::EmptyTestEventListener {
+ public:
+ explicit GTestEventListener(pp::Instance* instance);
+
+ // TestEventListener overrides.
+ virtual void OnTestProgramStart(const ::testing::UnitTest& unit_test);
+ virtual void OnTestCaseStart(const ::testing::TestCase& test_case);
+ virtual void OnTestStart(const ::testing::TestInfo& test_info);
+ virtual void OnTestPartResult(
+ const ::testing::TestPartResult& test_part_result);
+ virtual void OnTestEnd(const ::testing::TestInfo& test_info);
+ virtual void OnTestCaseEnd(const ::testing::TestCase& test_case);
+ virtual void OnTestProgramEnd(const ::testing::UnitTest& unit_test);
+
+ private:
+ void PostMessage(const std::string& str);
+ void PostMessageCallback(int32_t result, const std::string& str);
+
+ pp::Instance* instance_;
+ pp::CompletionCallbackFactory<GTestEventListener,
+ ::threading::RefCount> factory_;
+};
+
+} // namespace c_salt
+#endif // C_SALT_TEST_GTEST_EVENT_LISTENER_H_
+
diff --git a/native_client_sdk/src/libraries/c_salt/test/gtest_instance.cc b/native_client_sdk/src/libraries/c_salt/test/gtest_instance.cc
new file mode 100644
index 0000000..1445bd9
--- /dev/null
+++ b/native_client_sdk/src/libraries/c_salt/test/gtest_instance.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "c_salt/test/gtest_instance.h"
+#include "c_salt/test/gtest_runner.h"
+#include "ppapi/cpp/var.h"
+
+namespace c_salt {
+
+GTestInstance::GTestInstance(PP_Instance instance)
+ : pp::Instance(instance) {
+}
+
+GTestInstance::~GTestInstance() {
+}
+
+bool GTestInstance::Init(uint32_t /* argc */, const char* /* argn */[],
+ const char* /* argv */[]) {
+ // Create a GTestRunner thread/singleton.
+ int local_argc = 0;
+ c_salt::GTestRunner::CreateGTestRunnerThread(this, local_argc, NULL);
+ return true;
+}
+
+void GTestInstance::HandleMessage(const pp::Var& var_message) {
+ if (!var_message.is_string()) {
+ PostMessage("Invalid message");
+ return;
+ }
+
+ std::string message = var_message.AsString();
+ if (message == "RunGTest") {
+ // This is our signal to start running the tests. Results from the tests
+ // are posted through GTestEventListener.
+ c_salt::GTestRunner::gtest_runner()->RunAllTests();
+ } else {
+ std::string return_var;
+ return_var = "Unknown message ";
+ return_var += message;
+ PostMessage(return_var);
+ }
+}
+
+} // namespace c_salt
+
diff --git a/native_client_sdk/src/libraries/c_salt/test/gtest_instance.h b/native_client_sdk/src/libraries/c_salt/test/gtest_instance.h
new file mode 100644
index 0000000..aa180f8
--- /dev/null
+++ b/native_client_sdk/src/libraries/c_salt/test/gtest_instance.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef C_SALT_TEST_GTEST_INSTANCE_H_
+#define C_SALT_TEST_GTEST_INSTANCE_H_
+
+#include "ppapi/cpp/instance.h"
+
+namespace c_salt {
+
+// GTestInstance is a NaCl instance specifically dedicated to running
+// gtest-based unit tests. It creates a GTestRunner thread/singleton pair as
+// part of its Init function and runs all registered gtests once it
+// receives a 'RunGTest' message in its post-message handler. Results from the
+// test are posted back to JS through GTestEventListener.
+//
+// All tests are run from a background thread and must be written accordingly.
+// Although that may complicate the test code a little, it allows the tests
+// to be synchronized. I.e. Each test is launched and the thread is blocked
+// until the outcome is known and reported.
+class GTestInstance : public pp::Instance {
+ public:
+ explicit GTestInstance(PP_Instance instance);
+ virtual ~GTestInstance();
+
+ // pp::Instance overrides.
+ virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]);
+ virtual void HandleMessage(const pp::Var& var_message);
+};
+
+} // namespace c_salt
+
+#endif // C_SALT_TEST_GTEST_INSTANCE_H_
+
diff --git a/native_client_sdk/src/libraries/c_salt/test/gtest_module.cc b/native_client_sdk/src/libraries/c_salt/test/gtest_module.cc
new file mode 100644
index 0000000..3cc0ad0
--- /dev/null
+++ b/native_client_sdk/src/libraries/c_salt/test/gtest_module.cc
@@ -0,0 +1,28 @@
+// Copyright 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "c_salt/test/gtest_instance.h"
+#include "ppapi/cpp/module.h"
+
+namespace c_salt {
+
+// GTestModule is a NaCl module dedicated to running gtest-based unit tests.
+// It creates an NaCl instance based on GTestInstance.
+class GTestModule : public pp::Module {
+ public:
+ GTestModule() : pp::Module() {}
+ ~GTestModule() {}
+
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new c_salt::GTestInstance(instance);
+ }
+};
+
+} // namespace c_salt
+
+namespace pp {
+Module* CreateModule() {
+ return new c_salt::GTestModule();
+}
+} // namespace pp
+
diff --git a/native_client_sdk/src/libraries/c_salt/test/gtest_nacl_environment.cc b/native_client_sdk/src/libraries/c_salt/test/gtest_nacl_environment.cc
new file mode 100644
index 0000000..416226b
--- /dev/null
+++ b/native_client_sdk/src/libraries/c_salt/test/gtest_nacl_environment.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "c_salt/test/gtest_nacl_environment.h"
+
+namespace c_salt {
+
+pp::Instance* GTestNaclEnvironment::global_instance_ = NULL;
+
+void GTestNaclEnvironment::SetUp() {
+ // Check that the global instance is set before running the tests.
+ assert(global_instance_ != NULL);
+}
+
+void GTestNaclEnvironment::TearDown() {
+}
+
+} // namespace c_salt
+
diff --git a/native_client_sdk/src/libraries/c_salt/test/gtest_nacl_environment.h b/native_client_sdk/src/libraries/c_salt/test/gtest_nacl_environment.h
new file mode 100644
index 0000000..8183565
--- /dev/null
+++ b/native_client_sdk/src/libraries/c_salt/test/gtest_nacl_environment.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef C_SALT_TEST_GTEST_NACL_ENVIRONMENT_H_
+#define C_SALT_TEST_GTEST_NACL_ENVIRONMENT_H_
+
+#include <cassert>
+#include "gtest/gtest.h"
+
+namespace pp {
+class Instance;
+} // namespace pp
+
+namespace c_salt {
+
+// A custom environment for NaCl gtest.
+class GTestNaclEnvironment : public ::testing::Environment {
+ public:
+ // Set the global NaCl instance that will be shared by all the tests.
+ static void set_global_instance(pp::Instance* instance) {
+ global_instance_ = instance;
+ }
+
+ // Get the global NaCl instance.
+ static pp::Instance* global_instance() { return global_instance_; }
+
+ // Environment overrides.
+ virtual void SetUp();
+ virtual void TearDown();
+
+ private:
+ static pp::Instance* global_instance_;
+};
+
+} // namespace c_salt
+
+#endif // C_SALT_TEST_GTEST_NACL_ENVIRONMENT_H_
+
diff --git a/native_client_sdk/src/libraries/c_salt/test/gtest_runner.cc b/native_client_sdk/src/libraries/c_salt/test/gtest_runner.cc
new file mode 100644
index 0000000..d6afab0
--- /dev/null
+++ b/native_client_sdk/src/libraries/c_salt/test/gtest_runner.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "c_salt/test/gtest_runner.h"
+
+#include <cassert>
+
+#include "c_salt/test/gtest_event_listener.h"
+#include "c_salt/test/gtest_nacl_environment.h"
+#include "gtest/gtest.h"
+
+namespace c_salt {
+
+pthread_t GTestRunner::g_test_runner_thread_ = NACL_PTHREAD_ILLEGAL_THREAD_ID;
+GTestRunner* GTestRunner::gtest_runner_ = NULL;
+
+void GTestRunner::CreateGTestRunnerThread(pp::Instance* instance,
+ int argc, char** argv) {
+ assert(g_test_runner_thread_ == NACL_PTHREAD_ILLEGAL_THREAD_ID);
+ if (g_test_runner_thread_ == NACL_PTHREAD_ILLEGAL_THREAD_ID) {
+ gtest_runner_ = new GTestRunner();
+ gtest_runner_->Init(instance, argc, argv);
+ pthread_create(&g_test_runner_thread_, NULL, ThreadFunc, NULL);
+ }
+}
+
+void GTestRunner::RunAllTests() {
+ status_signal_.Lock();
+ assert(status_ == kIdle);
+ status_ = kRunTests;
+ status_signal_.Signal();
+ status_signal_.Unlock();
+}
+
+void* GTestRunner::ThreadFunc(void* param) {
+ gtest_runner_->RunLoop();
+ delete gtest_runner_;
+ gtest_runner_ = NULL;
+ pthread_exit(NULL);
+ return NULL;
+}
+
+GTestRunner::GTestRunner() : status_(kIdle) { }
+
+void GTestRunner::Init(pp::Instance* instance, int argc, char** argv) {
+ // Init gtest.
+ testing::InitGoogleTest(&argc, argv);
+
+ // Add GTestEventListener to the list of listeners. That will cause the
+ // test output to go to nacltest.js through PostMessage.
+ ::testing::TestEventListeners& listeners =
+ ::testing::UnitTest::GetInstance()->listeners();
+ delete listeners.Release(listeners.default_result_printer());
+ listeners.Append(new c_salt::GTestEventListener(instance));
+
+ // We use our own gtest environment, mainly to make the nacl instance
+ // available to the individual unit tests.
+ c_salt::GTestNaclEnvironment* test_env = new c_salt::GTestNaclEnvironment();
+ test_env->set_global_instance(instance);
+ ::testing::AddGlobalTestEnvironment(test_env);
+}
+
+void GTestRunner::RunLoop() {
+ // Stay idle until RunAlltests is called.
+ status_signal_.Lock();
+ while (status_ == kIdle) {
+ status_signal_.Wait();
+ }
+
+ assert(status_ == kRunTests);
+ status_signal_.Unlock();
+ RUN_ALL_TESTS();
+}
+
+} // namespace c_salt
+
diff --git a/native_client_sdk/src/libraries/c_salt/test/gtest_runner.h b/native_client_sdk/src/libraries/c_salt/test/gtest_runner.h
new file mode 100644
index 0000000..d8c2a6e
--- /dev/null
+++ b/native_client_sdk/src/libraries/c_salt/test/gtest_runner.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef C_SALT_TEST_GTEST_RUNNER_H_
+#define C_SALT_TEST_GTEST_RUNNER_H_
+
+#include "c_salt/threading/pthread_ext.h"
+#include "c_salt/threading/thread_condition.h"
+
+namespace pp {
+class Instance;
+} // namespace pp
+
+namespace c_salt {
+
+// GTestRunner is a threaded singleton for running gtest-based unit tests.
+class GTestRunner {
+ public:
+ // Factory function to create the background gtest thread and associated
+ // GTestRunner singleton. It is an error to call the factory function more
+ // than once that raises an assert in debug mde.
+ //
+ // Parameters:
+ // instance: the NaCl instance.
+ // argc: arg count from pp::Instance::Init.
+ // argv: the arguments from pp::Instance::Init.
+ static void CreateGTestRunnerThread(pp::Instance* instance,
+ int argc, char** argv);
+
+ // Get the GTestRunner singleton.
+ // @return A pointer to the GTestRunner singleton.
+ static GTestRunner* gtest_runner() { return gtest_runner_; }
+
+ // Tell the GTestRunner to run all gtest unit tests.
+ void RunAllTests();
+
+ protected:
+ // The pthread thread function.
+ static void* ThreadFunc(void* param);
+
+ // Don't try to create instances directly. Use the factory function instead.
+ GTestRunner();
+ void Init(pp::Instance* instance, int argc, char** argv);
+
+ // The thread run loop exits once all test have run.
+ void RunLoop();
+
+ private:
+ // The status and associated status signal are used to control the state of
+ // the thread run loop.
+ enum Status { kIdle, kRunTests };
+ c_salt::threading::ThreadCondition status_signal_;
+ Status status_;
+
+ static pthread_t g_test_runner_thread_;
+ static GTestRunner* gtest_runner_;
+};
+
+} // namespace c_salt
+
+#endif // C_SALT_TEST_GTEST_RUNNER_H_
+
diff --git a/native_client_sdk/src/libraries/c_salt/threading/condition_lock.h b/native_client_sdk/src/libraries/c_salt/threading/condition_lock.h
new file mode 100644
index 0000000..1d6b962
--- /dev/null
+++ b/native_client_sdk/src/libraries/c_salt/threading/condition_lock.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONDITION_LOCK_H_
+#define CONDITION_LOCK_H_
+
+#include <pthread.h>
+
+namespace threading {
+// Class to manage a lock associated with a specific value. The calling thread
+// can ask to acquire the lock only when the lock is in a certain condition.
+class ConditionLock {
+ public:
+ ConditionLock() : condition_value_(0) {
+ InitLock();
+ }
+ explicit ConditionLock(int32_t condition_value)
+ : condition_value_(condition_value) {
+ InitLock();
+ }
+
+ virtual ~ConditionLock() {
+ pthread_cond_destroy(&condition_condition_);
+ pthread_mutex_destroy(&condition_lock_);
+ }
+
+ // Acquire the mutex without regard to the condition.
+ void Lock() {
+ pthread_mutex_lock(&condition_lock_);
+ }
+
+ // Acquire the mutex lock when the lock values are equal. Blocks the
+ // calling thread until the lock can be acquired and the condition is met.
+ void LockWhenCondition(int32_t condition_value) {
+ Lock();
+ while (condition_value != condition_value_) {
+ pthread_cond_wait(&condition_condition_, &condition_lock_);
+ }
+ // When this method returns, |contition_lock_| will be acquired. The
+ // calling thread must unlock it.
+ }
+
+ // Acquire the mutex lock when the lock values are _NOT_ equal. Blocks the
+ // calling thread until the lock can be acquired and the condition is met.
+ void LockWhenNotCondition(int32_t condition_value) {
+ Lock();
+ while (condition_value == condition_value_) {
+ pthread_cond_wait(&condition_condition_, &condition_lock_);
+ }
+ // When this method returns, |contition_lock_| will be acquired. The
+ // calling thread must unlock it.
+ }
+
+ // Release the lock without changing the condition. Signal the condition
+ // so that threads waiting in LockWhenCondtion() will wake up. If there are
+ // no threads waiting for the signal, this has the same effect as a simple
+ // mutex unlock.
+ void Unlock() {
+ pthread_cond_broadcast(&condition_condition_);
+ pthread_mutex_unlock(&condition_lock_);
+ }
+
+ // Release the lock, setting the condition's value. This assumes that
+ // |condition_lock_| has been acquired.
+ void UnlockWithCondition(unsigned int condition_value) {
+ condition_value_ = condition_value;
+ Unlock();
+ }
+
+ // Return the current condition value without any mutex protection.
+ int32_t condition_value() const {
+ return condition_value_;
+ }
+
+ private:
+ void InitLock() {
+ pthread_mutex_init(&condition_lock_, NULL);
+ pthread_cond_init(&condition_condition_, NULL);
+ }
+
+ pthread_mutex_t condition_lock_;
+ pthread_cond_t condition_condition_;
+ int32_t condition_value_;
+};
+} // namespace threading
+
+#endif // CONDITION_LOCK_H_
+
diff --git a/native_client_sdk/src/libraries/c_salt/threading/pthread_ext.h b/native_client_sdk/src/libraries/c_salt/threading/pthread_ext.h
new file mode 100644
index 0000000..2152549
--- /dev/null
+++ b/native_client_sdk/src/libraries/c_salt/threading/pthread_ext.h
@@ -0,0 +1,15 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PTHREAD_EXT_H_
+#define PTHREAD_EXT_H_
+
+// Include wrapper on pthread.h, with a few handy constants.
+
+#include <pthread.h>
+
+#define PTHREAD_MUTEX_SUCCESS 0
+
+#endif // PTHREAD_EXT_H_
+
diff --git a/native_client_sdk/src/libraries/c_salt/threading/ref_count.h b/native_client_sdk/src/libraries/c_salt/threading/ref_count.h
new file mode 100644
index 0000000..55d3db1
--- /dev/null
+++ b/native_client_sdk/src/libraries/c_salt/threading/ref_count.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef REF_COUNT_H_
+#define REF_COUNT_H_
+
+#include "c_salt/threading/pthread_ext.h"
+
+namespace threading {
+
+// A thread-safe reference counter for class CompletionCallbackFactory.
+class RefCount {
+ public:
+ RefCount() : ref_(0) {
+ pthread_mutex_init(&mutex_, NULL);
+ }
+ ~RefCount() {
+ pthread_mutex_destroy(&mutex_);
+ }
+
+ int32_t AddRef() {
+ int32_t ret_val = 0;
+ if (pthread_mutex_lock(&mutex_) == PTHREAD_MUTEX_SUCCESS) {
+ ret_val = ++ref_;
+ pthread_mutex_unlock(&mutex_);
+ }
+ return ret_val;
+ }
+
+ int32_t Release() {
+ int32_t ret_val = -1;
+ if (pthread_mutex_lock(&mutex_) == PTHREAD_MUTEX_SUCCESS) {
+ ret_val = --ref_;
+ pthread_mutex_unlock(&mutex_);
+ }
+ return ret_val;
+ }
+
+ private:
+ int32_t ref_;
+ pthread_mutex_t mutex_;
+};
+
+} // namespace threading
+#endif // REF_COUNT_H_
+
diff --git a/native_client_sdk/src/libraries/c_salt/threading/scoped_mutex_lock.h b/native_client_sdk/src/libraries/c_salt/threading/scoped_mutex_lock.h
new file mode 100644
index 0000000..03868ae
--- /dev/null
+++ b/native_client_sdk/src/libraries/c_salt/threading/scoped_mutex_lock.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SCOPED_MUTEX_LOCK_H_
+#define SCOPED_MUTEX_LOCK_H_
+
+#include "c_salt/threading/pthread_ext.h"
+
+namespace threading {
+// A small helper RAII class that implements a scoped pthread_mutex lock.
+class ScopedMutexLock {
+ public:
+ explicit ScopedMutexLock(pthread_mutex_t* mutex) : mutex_(mutex) {
+ if (pthread_mutex_lock(mutex_) != PTHREAD_MUTEX_SUCCESS) {
+ mutex_ = NULL;
+ }
+ }
+ ~ScopedMutexLock() {
+ if (mutex_)
+ pthread_mutex_unlock(mutex_);
+ }
+ bool is_valid() const {
+ return mutex_ != NULL;
+ }
+ private:
+ pthread_mutex_t* mutex_; // Weak reference.
+};
+} // namespace threading
+
+#endif // SCOPED_MUTEX_LOCK_H_
+
diff --git a/native_client_sdk/src/libraries/c_salt/threading/thread_condition.h b/native_client_sdk/src/libraries/c_salt/threading/thread_condition.h
new file mode 100644
index 0000000..cacd4dae
--- /dev/null
+++ b/native_client_sdk/src/libraries/c_salt/threading/thread_condition.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2011 The Native Client Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THREAD_CONDITION_H_
+#define THREAD_CONDITION_H_
+
+#include "c_salt/threading/pthread_ext.h"
+
+struct timespec;
+
+namespace c_salt {
+namespace threading {
+// A wrapper class for condition signaling. Contains a mutex and condition
+// pair.
+class ThreadCondition {
+ public:
+ // Initialize the mutex and the condition.
+ ThreadCondition() {
+ pthread_mutex_init(&cond_mutex_, NULL);
+ pthread_cond_init(&condition_, NULL);
+ }
+
+ virtual ~ThreadCondition() {
+ pthread_cond_destroy(&condition_);
+ pthread_mutex_destroy(&cond_mutex_);
+ }
+
+ // Lock the mutex, do this before signalling the condition.
+ void Lock() {
+ pthread_mutex_lock(&cond_mutex_);
+ }
+
+ // Unlock the mutex, do this after raising a signal or after returning from
+ // Wait().
+ void Unlock() {
+ pthread_mutex_unlock(&cond_mutex_);
+ }
+
+ // Signal the condition. This will cause Wait() to return.
+ void Signal() {
+ pthread_cond_broadcast(&condition_);
+ }
+
+ // Wait for a Signal(). Note that this can spuriously return, so you should
+ // have a guard bool to see if the condtion is really true. E.g., in the
+ // calling thread:
+ // cond_lock->Lock();
+ // cond_true = true;
+ // cond_lock->Signal();
+ // cond_lock->Unlock();
+ // In the worker thread:
+ // cond_lock->Lock();
+ // while (!cond_true) {
+ // cond_lock->Wait();
+ // }
+ // cond_lock->Unlock();
+ void Wait() {
+ pthread_cond_wait(&condition_, &cond_mutex_);
+ }
+
+ // Same as Wait, but wait at most until abs_time. Returns false if the system
+ // time exceeds abs_time before the condition is signaled.
+ bool TimedWait(struct timespec *abs_time) {
+ return (pthread_cond_timedwait(&condition_, &cond_mutex_, abs_time) == 0);
+ }
+
+ private:
+ pthread_mutex_t cond_mutex_;
+ pthread_cond_t condition_;
+};
+} // namespace threading
+} // namespace c_salt
+
+#endif // THREAD_CONDITION_H_
+
diff --git a/native_client_sdk/src/libraries/gtest/build.scons b/native_client_sdk/src/libraries/gtest/build.scons
new file mode 100644
index 0000000..55a124e
--- /dev/null
+++ b/native_client_sdk/src/libraries/gtest/build.scons
@@ -0,0 +1,112 @@
+# -*- python -*-
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import build_utils
+import glob
+import os
+import shutil
+import subprocess
+import sys
+
+from SCons import Script
+
+# Directories used throughout this script.
+script_dir = os.path.abspath(os.getcwd())
+sdk_root_dir = os.getenv('NACL_SDK_ROOT')
+build_tools_dir = os.path.join(sdk_root_dir, 'build_tools')
+libraries_dir = os.path.join(sdk_root_dir, 'libraries')
+
+# Add the path to build_tools to the shell's python path.
+shell_env = os.environ.copy()
+python_paths = [build_tools_dir]
+python_paths += [shell_env.get('PYTHONPATH', '')]
+shell_env['PYTHONPATH'] = os.pathsep.join(python_paths)
+
+# Argv for the install-gtest python script.
+script_argv = [
+ '--toolchain=%s' % (
+ build_utils.NormalizeToolchain(base_dir=sdk_root_dir,
+ arch='x86',
+ variant='glibc')),
+ '--toolchain=%s' % (
+ build_utils.NormalizeToolchain(base_dir=sdk_root_dir,
+ arch='x86',
+ variant='newlib')),
+ '--working_dir=%s' % script_dir
+ ]
+
+# The scons build env.
+build_env = Script.Environment().Clone()
+
+# Where the src for gtest and gmock will be found after running the install
+# script. We keep them around as a sentinel, to indicate they they have been
+# installed. (See BuildGTestLibs below.)
+gtest_src = os.path.join(script_dir, 'gtest-1.5.0')
+gmock_src = os.path.join(script_dir, 'gmock-1.5.0')
+
+
+def BuildGTestLibs(env, target, source):
+ '''Build and install the gtest/gmock libraries.
+
+ This invokes the gtest_install.py script in the build_tools directory. In turn
+ that scripts downloads, untar, patches and build the gtest/gmock libraries.
+ Finally, the libraries and related include files are copied to the toolchain.
+
+ Args:
+ env: The construction Environment() that is building the examples.
+ target: The target that triggered this build. Not used.
+ source: The sources used for this build. Not used.
+ '''
+ # If our sentinel, the gtest source is present, do not build.
+ if os.path.exists(gtest_src):
+ return
+ # Remove any old gmock source if still present.
+ shutil.rmtree(gmock_src, ignore_errors=True)
+ # Invoke the gtest install script.
+ script = os.path.join(build_tools_dir, 'install_gtest', 'install_gtest.py')
+ py_command = [sys.executable, script]
+ subprocess.check_call(py_command + script_argv, env=shell_env)
+
+ # Clean up: remove left-over tgz files.
+ for f in glob.iglob(os.path.join(script_dir, '*.tgz')):
+ os.remove(f)
+
+
+def CleanGTestLibs(env, target, suite_name):
+ '''Clean the gtest/gmock libraries sources.
+
+ This does a partial clean up of the gtest/gmock projects. It removes the src
+ directories. However, the actual libraries and related includes in the
+ toolchains are not removed. It is however sufficient to trigger a full
+ rebuild of gtest/gmock.
+
+ Args:
+ env: The construction Environment() that is building the examples.
+ target: The target that triggered this build.
+ suite_name: A suite name that should cause this target to be cleaned.
+ '''
+ # Only do this in 'clean' mode.
+ if not build_env.GetOption('clean'):
+ return
+ # Only clean target if it's on the cmd line or it's a clean all.
+ clean_this = True
+ if len(COMMAND_LINE_TARGETS) > 0:
+ clean_this = False
+ for cl_target in COMMAND_LINE_TARGETS:
+ if cl_target == suite_name or cl_target == target:
+ clean_this = True
+ break
+ # Delete the src trees for gtest and gmock.
+ if clean_this:
+ shutil.rmtree(gmock_src, ignore_errors=True)
+ shutil.rmtree(gtest_src, ignore_errors=True)
+
+
+gtest_libs_builder = build_env.Alias('gtest_libs', [], BuildGTestLibs)
+build_env.AlwaysBuild(gtest_libs_builder)
+CleanGTestLibs(build_env, 'gtest_libs', 'bot')
+
+# ----------------------------------------------------------------------------
+build_env.Default('gtest_libs')
diff --git a/native_client_sdk/src/libraries/scons b/native_client_sdk/src/libraries/scons
new file mode 100755
index 0000000..053e32d
--- /dev/null
+++ b/native_client_sdk/src/libraries/scons
@@ -0,0 +1,38 @@
+#!/bin/bash
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+readonly SCRIPT_DIR="$(dirname "$0")"
+readonly SCRIPT_DIR_ABS="$(cd "${SCRIPT_DIR}" ; pwd -P)"
+
+# NACL_SDK_ROOT must be set.
+if [ x"${NACL_SDK_ROOT}"x == "xx" ] ; then
+ echo "Error: NACL_SDK_ROOT is not set."
+ exit 1;
+fi
+
+# NACL_TARGET_PLATFORM is really the name of a folder with the base dir -
+# usually NACL_SDK_ROOT - within which the toolchain for the target platform
+# are found.
+# Replace the platform with the name of your target platform. For example, to
+# build applications that target the pepper_17 API, set
+# NACL_TARGET_PLATFORM="pepper_17"
+if [ x"${NACL_TARGET_PLATFORM}"x == "xx" ] ; then
+ export NACL_TARGET_PLATFORM="pepper_17"
+fi
+
+readonly NACL_PLATFORM_DIR="${NACL_SDK_ROOT}/${NACL_TARGET_PLATFORM}"
+readonly BASE_SCRIPT="${NACL_PLATFORM_DIR}/third_party/scons-2.0.1/script/scons"
+
+export SCONS_LIB_DIR="${NACL_PLATFORM_DIR}/third_party/scons-2.0.1/engine"
+export PYTHONPATH="${NACL_PLATFORM_DIR}/third_party/scons-2.0.1/engine:${NACL_PLATFORM_DIR}/build_tools"
+# We have to do this because scons overrides PYTHONPATH and does not preserve
+# what is provided by the OS. The custom variable name won't be overwritten.
+export PYMOX="${NACL_PLATFORM_DIR}/third_party/pymox"
+
+"${BASE_SCRIPT}" --file=build.scons \
+ --site-dir="${NACL_PLATFORM_DIR}/build_tools/nacl_sdk_scons" \
+ $*
+
diff --git a/native_client_sdk/src/libraries/scons.bat b/native_client_sdk/src/libraries/scons.bat
new file mode 100755
index 0000000..889a757
--- /dev/null
+++ b/native_client_sdk/src/libraries/scons.bat
@@ -0,0 +1,43 @@
+@echo off
+
+:: Copyright (c) 2011 The Native Client Authors. All rights reserved.
+:: Use of this source code is governed by a BSD-style license that can be
+:: found in the LICENSE file.
+
+setlocal
+
+:: NACL_SDK_ROOT must be set.
+if not defined NACL_SDK_ROOT (
+ echo Error: NACL_SDK_ROOT is not set.
+ echo Please set NACL_SDK_ROOT to the full path of the Native Client SDK.
+ echo For example:
+ echo set NACL_SDK_ROOT=D:\nacl_sdk
+ goto end
+)
+
+:: NACL_TARGET_PLATFORM is really the name of a folder with the base dir -
+:: usually NACL_SDK_ROOT - within which the toolchain for the target platform
+:: are found.
+:: Replace the platform with the name of your target platform. For example, to
+:: build applications that target the pepper_17 API, set
+:: NACL_TARGET_PLATFORM=pepper_17
+if not defined NACL_TARGET_PLATFORM (
+ set NACL_TARGET_PLATFORM=pepper_17
+)
+
+set NACL_PLATFORM_DIR=%NACL_SDK_ROOT%\%NACL_TARGET_PLATFORM%
+
+set SCONS_LIB_DIR=%NACL_PLATFORM_DIR%\third_party\scons-2.0.1\engine
+set PYTHONPATH=%NACL_PLATFORM_DIR%\third_party\scons-2.0.1\engine;%NACL_PLATFORM_DIR%\build_tools
+
+:: We have to do this because scons overrides PYTHONPATH and does not preserve
+:: what is provided by the OS. The custom variable name won't be overwritten.
+set PYMOX=%NACL_PLATFORM_DIR%\third_party\pymox
+
+:: Run the included copy of scons.
+python -O -OO "%NACL_PLATFORM_DIR%\third_party\scons-2.0.1\script\scons" ^
+--warn no-visual-c-missing ^
+--file=build.scons ^
+--site-dir="%NACL_PLATFORM_DIR%\build_tools\nacl_sdk_scons" %*
+
+:end
diff --git a/native_client_sdk/src/main.scons b/native_client_sdk/src/main.scons
new file mode 100644
index 0000000..557333d
--- /dev/null
+++ b/native_client_sdk/src/main.scons
@@ -0,0 +1,1065 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+""" Main scons script for Native Client SDK builds.
+
+Do not invoke this script directly, but instead use the scons or scons.bat
+wrapper function. E.g.
+
+Linux or Mac:
+ ./scons [Options...]
+
+Windows:
+ scons.bat [Options...]
+"""
+
+from __future__ import with_statement
+
+import os
+import platform
+import subprocess
+import sys
+import toolchainbinaries
+from build_tools import build_utils
+
+# ----------------------------------------------------------------------------
+HELP_STRING = """
+===============================================================================
+Help for NaCl SDK
+===============================================================================
+
+* cleaning: ./scons -c
+* build a target: ./scons <target>
+
+Supported targets:
+ * bot Runs everything that the build and try bots run.
+ * debug_server_x64 Build the out-of-process debug_server.
+ * debug_server_Win32 Build the out-of-process debug_server.
+ * docs Build all of the Doxygen documentation.
+ * examples Build the examples.
+ * experimental Build the experimental projects.
+ * installer Build the SDK installer.
+ * nacl-bpad Build the native client crash reporting tool.
+ * sdk_tools Build nacl_sdk.zip and sdk_tools.tgz
+ * toolchain Update the toolchain's headers and libraries.
+ * vsx Build the Visual Studio Plugin.
+
+Flags:
+ * USE_EXISTING_INSTALLER=1 Do not rebuild the installer if it exists.
+ * chrome_browser_path=<full_path> Download Chrome to <full_path>.
+ * SHOW_BROWSER=1 Don't suppress browser GUI while performing
+ browser tests in Linux.
+
+More targets are listed below in the automatically generated help section.
+
+===============================================================================
+Automatically generated help follows:
+===============================================================================
+"""
+
+# ----------------------------------------------------------------------------
+# Perform some environment checks before running.
+# Note that scons should set NACL_SDK_ROOT before this script runs.
+
+if os.getenv('NACL_SDK_ROOT') is None:
+ sys.stderr.write('NACL_SDK_ROOT must be defined as the root directory'
+ ' of NaCl SDK.\n')
+ sys.exit(1)
+
+# By default, run with a parallel build (i.e. '-j num_jobs').
+# Use a default value proportional to the number of cpu cores on the system.
+# To run a serial build, explicitly type '-j 1' on the command line.
+try:
+ import multiprocessing
+ CORE_COUNT = multiprocessing.cpu_count()
+except (ImportError, NotImplementedError):
+ CORE_COUNT = 2 # Our buildbots seem to be dual-core typically
+
+SetOption('num_jobs', CORE_COUNT * 2)
+print 'Building with', GetOption('num_jobs'), 'parallel jobs'
+
+# ----------------------------------------------------------------------------
+# The environment_list contains all the build environments that we want to
+# specify. Selecting a particular environment is done using the --mode option.
+# Each environment that we support gets appended to this list.
+environment_list = []
+
+# ----------------------------------------------------------------------------
+# Create the base environment, from which all other environments are derived.
+base_env = Environment(
+ tools = ['component_setup'],
+ CPPPATH = ['$MAIN_DIR'],
+ CPPDEFINES = [
+ 'BOOST_ALL_NO_LIB',
+ ],
+ NACL_TOOLCHAIN_ROOTS = {
+ ('x86', 'newlib'):
+ build_utils.NormalizeToolchain(arch='x86', variant='newlib'),
+ ('x86', 'glibc'):
+ build_utils.NormalizeToolchain(arch='x86', variant='glibc'),
+ },
+ ROOT_DIR = os.path.abspath(os.getcwd()),
+ IS_WINDOWS = sys.platform in ['cygwin', 'win32'],
+ IS_LINUX = sys.platform == 'linux2',
+ IS_MAC = sys.platform == 'darwin',
+ JOB_COUNT = GetOption('num_jobs')
+)
+
+# It is possible to override these values on the command line by typing
+# something like this:
+# PYTHON=/path/to/my/python
+base_env.SetDefault(
+ PYTHON = ARGUMENTS.get('PYTHON', 'python'),
+ USE_EXISTING_INSTALLER = ARGUMENTS.get('USE_EXISTING_INSTALLER', False),
+ SHOW_BROWSER = ARGUMENTS.get('SHOW_BROWSER', False),
+)
+
+base_env.Append(
+ BUILD_SCONSCRIPTS = [
+ # Keep in alphabetical order
+ 'build_tools/build.scons',
+ 'debugger/build.scons',
+ 'documentation/build.scons',
+ 'experimental/visual_studio_plugin/build.scons',
+ 'experimental/webgtt/tests/nacltest/test.scons',
+ 'project_templates/test.scons',
+ ],
+)
+
+base_env.Help(HELP_STRING)
+
+KNOWN_SUITES = frozenset([
+ 'bot',
+ ])
+
+
+def HasBotTarget(env):
+ if 'bot' in COMMAND_LINE_TARGETS:
+ return True
+ return False
+
+base_env.AddMethod(HasBotTarget)
+
+
+def CheckSuiteName(suite, node_name):
+ '''Check whether a given test suite or alias name is a known name.
+
+ If the suite name is not in the approved list, then this function throws
+ an exception, with the node_name within the error message.
+
+ Args:
+ suite: a name of a suite that must be in the KNOWN_SUITES set
+ node_name: The name of the node. This is used for error messages
+ '''
+ if suite not in KNOWN_SUITES:
+ raise Exception('Testsuite/Alias "%s" for target "%s" is unknown' %
+ (suite, node_name))
+
+
+def AddNodeToTestSuite(env, node, suite_names, node_name, test_size='all'):
+ '''Adds a test node to a given set of suite names
+
+ These tests are automatically added to the run_all_tests target and are
+ listed in the help screen.
+
+ This function is loosely based on a function of the same name in the
+ Native Client repository
+
+ Args:
+ env - The environment from which this function was called
+ node - A scons node (e.g., file, command, etc) to be added to set suite
+ suite_names - A list of test suite names. For none, pass an empty list
+ node_name - The target name used for running this test
+ test_size - The relative run-time of this test: small, medium, or large
+ '''
+
+ # CommandTest can return an empty list when it silently discards a test
+ if not node:
+ return
+
+ AlwaysBuild(node)
+
+ for s in suite_names:
+ CheckSuiteName(s, node_name)
+ env.Alias(s, node)
+
+ if test_size not in ['small', 'medium', 'large', 'all']:
+ raise Exception('Invalid test size for %s' % node_name)
+
+ # Note that COMPONENT_TEST_SIZE is set to 'large' by default, which
+ # populates a largely redundant list of 'large' tests. Note that all
+ # tests are added to 'all', so setting test_size='all' is a no-op
+ env.ComponentTestOutput(node_name, node, COMPONENT_TEST_SIZE=test_size)
+
+base_env.AddMethod(AddNodeToTestSuite)
+
+
+def ShouldBeCleaned(env, targets, suite_names, node_name):
+ '''Determines whether a given set of targets require cleaning.
+
+ Args:
+ env - The calling environment.
+ targets - Any build artifacts to which a cleaning step might apply.
+ Any false object indicates that this check is skipped.
+ suite_names - Any suites that might produce |targets|
+ node_name - A node that might produce |targets|
+ '''
+ if not env.GetOption('clean'):
+ return False
+
+ if len(COMMAND_LINE_TARGETS) > 0:
+ clean_this = False
+ for cl_target in COMMAND_LINE_TARGETS:
+ if cl_target in suite_names or cl_target == node_name:
+ clean_this = True
+ break
+ if not clean_this:
+ return False
+
+ if not targets:
+ return True
+ for target in targets:
+ if os.path.exists(target):
+ return True
+ return False
+
+
+def AddCleanAction(env, targets, action, suite_names, node_name):
+ '''Adds a cleanup action that scons cannot detect automatically.
+
+ Cleaning will only occur if there is a match between the suite or nodes
+ specified on the command line, and suite_names or node_name or if no
+ suite or nodes are specified on the command line. Also, at least one of the
+ targets must exist on the file system.
+
+ Args:
+ env - The calling environment
+ targets - Artifacts to be cleaned.
+ action - The action to be performed. It is up to the caller to ensure
+ that |action| will actually remove |targets|
+ suite_names - Any suites to which this cleanup target applies.
+ node_name - Any nodes to which this cleanup target applies.
+ '''
+ if ShouldBeCleaned(env, targets, suite_names, node_name):
+ env.Execute(action)
+
+base_env.AddMethod(AddCleanAction)
+
+
+def AddNodeAliases(env, node, suite_names, node_name):
+ '''Allow a given node to be built under a different name or as a suite
+
+ Args:
+ env - The calling environment
+ node - A target node to add to a known build alias (e.g., 'bot')
+ suite_names - A list of suite names. For none, pass an empty list. This
+ node will be run whenever any of these suites are invoked.
+ Each suite name must match a string in KNOWN_SUITES.
+ node_name - The name of this node, when run by itself
+ '''
+
+ if not node:
+ return
+
+ for s in suite_names:
+ CheckSuiteName(s, node_name)
+ env.Alias(s, node)
+
+ env.Alias(node_name, node)
+
+base_env.AddMethod(AddNodeAliases)
+
+
+def CreatePythonUnitTest(env, filename, dependencies=None, disabled=False,
+ params=None, buffered=True, banner=None):
+ """Returns a new build command that will run a unit test with a given file.
+
+ Args:
+ env: SCons environment
+ filename: The python file that contains the unit test
+ dependencies: An optional list of other files that this unit test uses
+ disabled: Setting this to True will prevent the test from running
+ params: Optional additional parameters for python command
+ buffered: True=stdout is buffered until entirely complete;
+ False=stdout is immediately displayed as it occurs.
+ banner: (optional) annotation banner for build/try bots
+
+ Returns:
+ A SCons command node
+ """
+ dependencies = dependencies or []
+ params = params or []
+
+ basename = os.path.splitext(os.path.basename(filename))[0]
+ outfilename = "%s_output.txt" % basename
+
+
+ def RunPythonUnitTest(env, target, source):
+ """Runs unit tests using the given target as a command.
+
+ The argument names of this test are not very intuitive but match what is
+ used conventionally throughout scons. If the string "PASSED" does not
+ occur in target when this exits, the test has failed; also a scons
+ convention.
+
+ Args:
+ env: SCons's current environment.
+ target: Where to write the result of the test.
+ source: The command to run as the test.
+
+ Returns:
+ None for good status
+ An error string for bad status
+ """
+ bot = build_utils.BotAnnotator()
+ if banner:
+ bot.BuildStep(banner)
+
+ if disabled:
+ sys.stdout.write("Test %s is disabled.\n" % basename)
+ sys.stdout.flush()
+ return None # return with good status
+
+ import subprocess
+
+ app = [str(env['PYTHON']), str(source[0].abspath)] + map(
+ lambda param: param if type(param) is str else str(param.abspath),
+ params)
+ bot.Print('Running: %s' % app)
+ app_env = os.environ.copy()
+ # We have to do this because scons overrides PYTHONPATH and does
+ # not preserve what is provided by the OS.
+ python_path = [env['ROOT_DIR'], app_env['PYMOX'], app_env['PYTHONPATH']]
+ app_env['PYTHONPATH'] = os.pathsep.join(python_path)
+ ret_val = 'Error: General Test Failure' # Indicates failure, by default
+ target_str = str(target[0])
+ with open(target_str, 'w') as outfile:
+ def Write(str):
+ if buffered:
+ outfile.write(str)
+ outfile.flush()
+ else:
+ sys.stdout.write(str)
+ sys.stdout.flush()
+ Write('\n-----Begin output for Test: %s\n' % basename)
+ if subprocess.call(app, env=app_env,
+ stdout=outfile if buffered else None,
+ stderr=outfile if buffered else None):
+ Write('-----Error: unit test failed\n')
+ ret_val = 'Error: Test Failure in %s' % basename
+ else:
+ ret_val = None # Indicates success
+
+ Write('-----End output for Test: %s\n' % basename)
+ if buffered:
+ with open(target_str, 'r') as resultfile:
+ sys.stdout.write(resultfile.read())
+ sys.stdout.flush()
+
+ if ret_val:
+ bot.BuildStepFailure()
+
+ return ret_val
+
+ cmd = env.Command(outfilename, filename, RunPythonUnitTest)
+ env.Depends(cmd, dependencies)
+ # Create dependencies for all the env.File parameters and other scons nodes
+ for param in params:
+ if type(param) is not str:
+ env.Depends(cmd, param)
+
+ return cmd
+
+base_env.AddMethod(CreatePythonUnitTest)
+
+
+# ----------------------------------------------------------------------------
+# Support for running Chrome. These functions access the construction
+# Environment() to produce a path to Chrome.
+
+# A Dir object representing the directory where the Chrome binaries are kept.
+# You can use chrome_binaries_dir= to set this on the command line. Defaults
+# to chrome_binaries.
+base_env['CHROME_DOWNLOAD_DIR'] = \
+ base_env.Dir(ARGUMENTS.get('chrome_binaries_dir', '#chrome_binaries'))
+
+
+def ChromeArchitectureSpec(env):
+ '''Determine the architecture spec for the Chrome binary.
+
+ The architecture spec is a string that represents the host architecture.
+ Possible values are:
+ x86-32
+ x86-64
+ On Mac and Windows, the architecture spec is always x86-32, because there are
+ no 64-bit version available.
+
+ Returns: An architecture spec string for the host CPU.
+ '''
+ arch, _ = platform.architecture();
+ # On Mac and Windows, always use a 32-bit version of Chrome (64-bit versions
+ # are not available).
+ if env['IS_WINDOWS'] or env['IS_MAC']:
+ arch = 'x86-32'
+ else:
+ arch = 'x86-64' if '64' in arch else 'x86-32'
+ return arch
+
+base_env.AddMethod(ChromeArchitectureSpec)
+
+
+def GetDefaultChromeBinary(env):
+ '''Get a File object that represents a Chrome binary.
+
+ By default, the test infrastructure will download a copy of Chrome that can
+ be used for testing. This method returns a File object that represents the
+ downloaded Chrome binary that can be run by tests. Note that the path to the
+ binary is specific to the host platform, for example the path on Linux
+ is <chrome_dir>/linux/<arch>/chrome, while on Mac it's
+ <chrome_dir>/mac/<arch>/Chromium.app/Contents.MacOS/Chromium.
+
+ Returns: A File object representing the Chrome binary.
+ '''
+ if env['IS_LINUX']:
+ os_name = 'linux'
+ binary = 'chrome'
+ elif env['IS_WINDOWS']:
+ os_name = 'windows'
+ binary = 'chrome.exe'
+ elif env['IS_MAC']:
+ os_name = 'mac'
+ binary = 'Chromium.app/Contents/MacOS/Chromium'
+ else:
+ raise Exception('Unsupported OS')
+
+ return env.File(os.path.join(
+ '${CHROME_DOWNLOAD_DIR}',
+ '%s_%s' % (os_name, env.ChromeArchitectureSpec()),
+ binary))
+
+base_env.AddMethod(GetDefaultChromeBinary)
+
+
+def GetChromeBinary(env):
+ '''Return a File object that represents the downloaded Chrome binary.
+
+ If chrome_browser_path is specified on the command line, then return a File
+ object that represents that path. Otherwise, return a File object
+ representing the default downloaded Chrome (see GetDefaultChromeBinary(),
+ above).
+
+ Returns: A File object representing a Chrome binary.
+ '''
+ return env.File(ARGUMENTS.get('chrome_browser_path',
+ env.GetDefaultChromeBinary()))
+
+base_env.AddMethod(GetChromeBinary)
+
+
+def DependsOnChrome(env, dependency):
+ '''Create a dependency on the download of Chrome.
+
+ Creates a dependency in |env| such that Chrome gets downloaded (if necessary)
+ whenever |dependency| changes. Uses the Chrome downloader scripts built
+ into NaCl; this script expects NaCl to be DEPS'ed into
+ third_party/native_client/native_client.
+
+ The Chrome binary is added as a precious node to the base Environment. If
+ we added it to the build environment env, then downloading chrome would in
+ effect be specified for multiple environments.
+ '''
+ if not hasattr(base_env, 'download_chrome_node'):
+ chrome_binary = env.GetDefaultChromeBinary()
+ download_chrome_script = build_utils.JoinPathToNaClRepo(
+ 'native_client', 'build', 'download_chrome.py',
+ root_dir=env['ROOT_DIR'])
+ base_env.download_chrome_node = env.Command(
+ chrome_binary,
+ [],
+ '${PYTHON} %s --arch=%s --dst=${CHROME_DOWNLOAD_DIR}' %
+ (download_chrome_script, env.ChromeArchitectureSpec()))
+ # This stops Scons from deleting the file before running the step above.
+ env.NoClean(chrome_binary)
+ env.Precious(chrome_binary)
+ env.Depends(dependency, base_env.download_chrome_node)
+
+base_env.AddMethod(DependsOnChrome)
+
+
+# ----------------------------------------------------------------------------
+# Targets for updating sdk headers and libraries
+# NACL_SDK_XXX vars are defined by site_scons/site_tools/naclsdk.py
+# NOTE: Our task here is complicated by the fact that there might already be
+# some (outdated) headers/libraries at the new location
+# One of the hacks we employ here is to make every library depend
+# on the installation on ALL headers (sdk_headers)
+
+# Contains all the headers to be installed
+sdk_headers = base_env.Alias('extra_sdk_update_header', [])
+# Contains all the libraries and .o files to be installed
+libs_platform = base_env.Alias('extra_sdk_libs_platform', [])
+libs = base_env.Alias('extra_sdk_libs', [])
+base_env.Alias('extra_sdk_update', [libs, libs_platform])
+
+AlwaysBuild(sdk_headers)
+
+# ----------------------------------------------------------------------------
+# The following section contains proxy nodes which can be used to create
+# dependencies between targets that are not in the same scope or environment.
+toolchain_node = base_env.Alias('toolchain', [])
+
+
+def GetToolchainNode(env):
+ '''Returns the node associated with the toolchain build target'''
+ return toolchain_node
+
+base_env.AddMethod(GetToolchainNode)
+
+
+def GetHeadersNode(env):
+ return sdk_headers
+
+base_env.AddMethod(GetHeadersNode)
+
+installer_prereqs_node = base_env.Alias('installer_prereqs', [])
+
+
+def GetInstallerPrereqsNode(env):
+ return installer_prereqs_node
+
+base_env.AddMethod(GetInstallerPrereqsNode)
+
+installer_test_node = base_env.Alias('installer_test_node', [])
+
+
+def GetInstallerTestNode(env):
+ return installer_test_node
+
+base_env.AddMethod(GetInstallerTestNode)
+
+
+def AddHeaderToSdk(env, nodes, subdir = 'nacl/', base_dirs = None):
+ """Add a header file to the toolchain. By default, Native Client-specific
+ headers go under nacl/, but there are non-specific headers, such as
+ the OpenGLES2 headers, that go under their own subdir.
+
+ Args:
+ env: Environment in which we were called.
+ nodes: A list of node objects to add to the toolchain
+ subdir: This is appended to each base_dir
+ base_dirs: A list of directories to install the node to"""
+ if not base_dirs:
+ # TODO(mball): This won't work for PNaCl:
+ base_dirs = [os.path.join(dir, 'x86_64-nacl', 'include')
+ for dir in env['NACL_TOOLCHAIN_ROOTS'].values()]
+
+ for base_dir in base_dirs:
+ node = env.Replicate(os.path.join(base_dir, subdir), nodes)
+ env.Depends(sdk_headers, node)
+ env.Depends(toolchain_node, node)
+ return node
+
+base_env.AddMethod(AddHeaderToSdk)
+
+
+def AddLibraryToSdkHelper(env, nodes, is_lib, is_platform):
+ """"Helper function to install libs/objs into the toolchain
+ and associate the action with the extra_sdk_update.
+
+ Args:
+ env: Environment in which we were called.
+ nodes: list of libc/objs
+ is_lib: treat nodes as libs
+ is_platform: nodes are truly platform specific
+ """
+ env.Requires(nodes, sdk_headers)
+
+ dir = ARGUMENTS.get('extra_sdk_lib_destination')
+ if not dir:
+ dir = '${NACL_SDK_LIB}/'
+
+ if is_lib:
+ n = env.ReplicatePublished(dir, nodes, 'link')
+ else:
+ n = env.Replicate(dir, nodes)
+
+ if is_platform:
+ env.Alias('extra_sdk_libs_platform', n)
+ else:
+ env.Alias('extra_sdk_libs', n)
+ return n
+
+
+def AddLibraryToSdk(env, nodes, is_platform=False):
+ return AddLibraryToSdkHelper(env, nodes, True, is_platform)
+
+base_env.AddMethod(AddLibraryToSdk)
+
+
+# ----------------------------------------------------------------------------
+# This is a simple environment that is primarily for targets that aren't built
+# directly by scons, and therefore don't need any special environment setup.
+build_env = base_env.Clone(
+ BUILD_TYPE = 'build',
+ BUILD_GROUPS = ['default', 'all'],
+ BUILD_TYPE_DESCRIPTION = 'Default build environment',
+ HOST_PLATFORMS = '*',
+ )
+
+environment_list.append(build_env)
+
+# ----------------------------------------------------------------------------
+# Get the appropriate build command depending on the environment.
+
+
+def SconsBuildCommand(env):
+ '''Return the build command used to run separate scons instances.
+ Args:
+ env: The construction Environment() that is building using scons.
+ Returns:
+ A string representing the platform-specific build command that will run the
+ scons instances.
+ '''
+ if env['IS_WINDOWS']:
+ return 'scons.bat --jobs=%s' % GetOption('num_jobs')
+ else:
+ return './scons --jobs=%s' % GetOption('num_jobs')
+
+# ----------------------------------------------------------------------------
+# Add a builder for examples. This adds an Alias() node named 'examples' that
+# is always built. There is some special handling for the clean mode, since
+# SCons relies on actual build products for its clean processing and will not
+# run Alias() actions during clean unless they actually produce something.
+
+
+def BuildExamples(env, target, source):
+ '''Build the examples.
+
+ This runs the build command in the 'examples' directory.
+
+ Args:
+ env: The construction Environment() that is building the examples.
+ target: The target that triggered this build. Not used.
+ source: The sources used for this build. Not used.
+ '''
+ os_env = os.environ.copy()
+ os_env['NACL_TARGET_PLATFORM'] = '.'
+ subprocess.check_call(SconsBuildCommand(env),
+ cwd='examples',
+ env=os_env,
+ shell=True)
+
+
+def CleanExamples(env, target, source):
+ '''Clean the examples.
+
+ This runs the clean command in the 'examples' directory.
+
+ Args:
+ env: The construction Environment() that is building the examples.
+ '''
+ os_env = os.environ.copy()
+ os_env['NACL_TARGET_PLATFORM'] = '.'
+ subprocess.check_call('%s --clean' % SconsBuildCommand(env),
+ cwd='examples',
+ env=os_env,
+ shell=True)
+
+
+examples_builder = build_env.Alias('examples', [toolchain_node], BuildExamples)
+build_env.AlwaysBuild(examples_builder)
+build_env.AddCleanAction(['examples'], CleanExamples, ['bot'],
+ examples_builder)
+
+
+def DependsOnExamples(env, dependency):
+ env.Depends(dependency, examples_builder)
+
+build_env.AddMethod(DependsOnExamples)
+
+
+# ----------------------------------------------------------------------------
+# Add a builder for experimental projects. This adds an Alias() node named
+# 'experimental' that is always built. There is some special handling for the
+# clean mode, since SCons relies on actual build products for its clean
+# processing and will not run Alias() actions during clean unless they actually
+# produce something.
+
+
+def BuildExperimental(env, target, source):
+ '''Build the experimental projects.
+
+ This runs the build command in the 'experimental' directory.
+
+ Args:
+ env: The construction Environment() that is building the experimental
+ projects.
+ target: The target that triggered this build. Not used.
+ source: The sources used for this build. Not used.
+ '''
+ subprocess.check_call(SconsBuildCommand(env),
+ cwd='experimental',
+ shell=True)
+
+
+def CleanExperimental(env, target, source):
+ '''Clean the experimental projects.
+
+ This runs the clean command in the 'experimental' directory.
+
+ Args:
+ env: The construction Environment() that is building the experimental
+ projects.
+ '''
+ subprocess.check_call('%s --clean' % SconsBuildCommand(env),
+ cwd='experimental',
+ shell=True)
+
+
+experimental_builder = build_env.Alias('experimental', [toolchain_node],
+ BuildExperimental)
+build_env.AlwaysBuild(experimental_builder)
+build_env.AddCleanAction(['experimental'], CleanExperimental, ['bot'],
+ experimental_builder)
+
+# ----------------------------------------------------------------------------
+# Add helper functions that build/clean a test by invoking scons under the
+# test directory (|cwd|, when specified). These functions are meant to be
+# called from corresponding project-specific 'Build<project>Test' and
+# 'Clean<project>Test' functions in the local test.scons scripts. Note that the
+# CleanNaClTest does not require a |cwd| because its cwd is always '.'
+
+
+def BuildNaClTest(env, cwd):
+ '''Build the test.
+
+ This runs the build command in the test directory from which it is called.
+
+ Args:
+ env: The construction Environment() that is building the test.
+ cwd: The directory under which the test's build.scons rests.
+ '''
+ subprocess.check_call('%s stage' % SconsBuildCommand(env),
+ cwd=cwd,
+ shell=True)
+
+build_env.AddMethod(BuildNaClTest)
+
+
+def CleanNaClTest(env):
+ '''Clean the test.
+
+ This runs the clean command in the test directory from which it is called.
+
+ Args:
+ env: The construction Environment() that is building the test.
+ cwd: The directory under which the test's build.scons rests.
+ '''
+ subprocess.check_call('%s stage --clean' % SconsBuildCommand(env),
+ shell=True)
+ # The step above still leaves behind two empty 'opt' directories, so a second
+ # cleaning pass is necessary.
+ subprocess.check_call('%s --clean' % SconsBuildCommand(env),
+ shell=True)
+
+build_env.AddMethod(CleanNaClTest)
+
+# ----------------------------------------------------------------------------
+# Enable PPAPIBrowserTester() functionality using nacltest.js
+# NOTE: The three main functions in this section: PPAPIBrowserTester(),
+# CommandTest(), and AutoDepsCommand() are 'LITE' versions of their counterparts
+# provided by Native Client @ third_party/native_client/native_client/SConstruct
+
+
+def SetupBrowserEnv(env):
+ '''Set up the environment for running the browser.
+
+ This copies environment parameters provided by the OS in order to run the
+ browser reliably.
+
+ Args:
+ env: The construction Environment() that runs the browser.
+ '''
+ EXTRA_ENV = ['XAUTHORITY', 'HOME', 'DISPLAY', 'SSH_TTY', 'KRB5CCNAME']
+ for var_name in EXTRA_ENV:
+ if var_name in os.environ:
+ env['ENV'][var_name] = os.environ[var_name]
+
+ env.Append(
+ PYTHONPATH = [
+ build_utils.JoinPathToNaClRepo(
+ 'third_party', 'pylib',
+ root_dir=os.getenv('NACL_SDK_ROOT')),
+ build_utils.JoinPathToNaClRepo(
+ 'tools', 'valgrind',
+ root_dir=os.getenv('NACL_SDK_ROOT')),
+ ]
+ )
+
+
+def PPAPIBrowserTester(env,
+ target,
+ url,
+ files,
+ timeout=20):
+ '''The main test wrapper for browser integration tests.
+
+ This constructs the command that invokes the browser_tester.py script on an
+ existing Chrome binary (to be downloaded if necessary).
+
+ Args:
+ env: The construction Environment() that runs the browser.
+ target: The output file to which the output of the test is to be written.
+ url: The test web page.
+ files: The files necessary for the web page to be served.
+ timeout: How long to wait for a response before concluding failure.
+
+ Returns: A command node that executes the browser test.
+ '''
+
+ env = env.Clone()
+ SetupBrowserEnv(env)
+
+ python_tester_script = build_utils.JoinPathToNaClRepo(
+ 'native_client', 'tools', 'browser_tester', 'browser_tester.py',
+ root_dir=env['ROOT_DIR'])
+
+ # Check if browser GUI needs to be suppressed (possible only in Linux)
+ headless_prefix = []
+ if not env['SHOW_BROWSER'] and env['IS_LINUX']:
+ headless_prefix = ['xvfb-run', '--auto-servernum']
+
+ command = headless_prefix + [
+ '${PYTHON}', python_tester_script,
+ '--browser_path', env.GetChromeBinary(),
+ '--url', url,
+ # Fail if there is no response for X seconds.
+ '--timeout', str(timeout)]
+
+ for dep_file in files:
+ command.extend(['--file', dep_file])
+
+ cmd = env.CommandTest(target,
+ command,
+ # Set to 'huge' so that the browser tester's timeout
+ # takes precedence over the default of the test suite.
+ size='huge',
+ capture_output=False)
+ env.DependsOnChrome(cmd)
+
+ return cmd
+
+build_env.AddMethod(PPAPIBrowserTester)
+
+
+def CommandTest(env,
+ name,
+ command,
+ size='small',
+ capture_output=True):
+ '''The wrapper for testing execution of a command and logging details.
+
+ This constructs the command that invokes the command_tester.py script on a
+ given command.
+
+ Args:
+ env: The construction Environment() that runs the command.
+ name: The output file to which the output of the tester is to be written.
+ command: The command to be tested.
+ size: This dictates certain timeout thresholds.
+ capture_output: This specifies whether the command's output needs to be
+ captured for further processing. When this option is False,
+ stdout and stderr will be streamed out. For more info, see
+ <NACL_REPO>/native_client/tools/command_tester.py
+
+ Returns: A command node that executes the command test.
+ '''
+ TEST_TIME_THRESHOLD = {
+ 'small': 2,
+ 'medium': 10,
+ 'large': 60,
+ 'huge': 1800,
+ }
+
+ if not name.endswith('out') or name.startswith('$'):
+ raise Exception('ERROR: bad test filename for test output %r' % name)
+
+ arch_string = env.ChromeArchitectureSpec();
+ if env['IS_LINUX']:
+ platform_string = 'linux'
+ elif env['IS_MAC']:
+ platform_string = 'mac'
+ elif env['IS_WINDOWS']:
+ platform_string = 'windows'
+
+ name = '${TARGET_ROOT}/test_results/' + name
+ max_time = TEST_TIME_THRESHOLD[size]
+
+ script_flags = ['--name', name,
+ '--report', name,
+ '--time_warning', str(max_time),
+ '--time_error', str(10 * max_time),
+ '--perf_env_description', platform_string + '_' + arch_string,
+ '--arch', 'x86',
+ '--subarch', arch_string[-2:],
+ ]
+ if not capture_output:
+ script_flags.extend(['--capture_output', '0'])
+
+ test_script = build_utils.JoinPathToNaClRepo(
+ 'native_client', 'tools', 'command_tester.py',
+ root_dir=env['ROOT_DIR'])
+ command = ['${PYTHON}', test_script] + script_flags + command
+ return AutoDepsCommand(env, name, command)
+
+build_env.AddMethod(CommandTest)
+
+
+def AutoDepsCommand(env, name, command):
+ """AutoDepsCommand() takes a command as an array of arguments. Each
+ argument may either be:
+
+ * a string, or
+ * a Scons file object, e.g. one created with env.File() or as the
+ result of another build target.
+
+ In the second case, the file is automatically declared as a
+ dependency of this command.
+
+ Args:
+ env: The construction Environment() that runs the command.
+ name: The target file to which the output is to be written.
+ command: The command to be executed.
+
+ Returns: A command node in the standard SCons format.
+ """
+ deps = []
+ for index, arg in enumerate(command):
+ if not isinstance(arg, str):
+ if len(Flatten(arg)) != 1:
+ # Do not allow this, because it would cause "deps" to get out
+ # of sync with the indexes in "command".
+ # See http://code.google.com/p/nativeclient/issues/detail?id=1086
+ raise AssertionError('Argument to AutoDepsCommand() actually contains '
+ 'multiple (or zero) arguments: %r' % arg)
+ command[index] = '${SOURCES[%d].abspath}' % len(deps)
+ deps.append(arg)
+
+ return env.Command(name, deps, ' '.join(command))
+
+build_env.AddMethod(AutoDepsCommand)
+
+
+def BuildVSSolution(env, target_name, solution, project_config=None):
+ """BuildVSSolution() Builds a Visual Studio solution.
+
+ Args:
+ env: The construction Environment() that runs the command.
+ target_name: The name of the target. Build output will be written to
+ [target_name]_build_output.txt.
+ solution: The solution to build.
+ project_config: A valid project configuration string to pass into devenv.
+ This provides support for building specific configurations, i.e.
+ 'Debug|Win32', 'Debug|x64', 'Release|Win32', 'Release|x64'. Note that the
+ string must be in quotes to work. devenv will default to Win32 if this
+ is not provided.
+
+ Returns the Command() used to build the target, so other targets can be made
+ to depend on it.
+ """
+ vs_build_action = ['vcvarsall', '&&', 'devenv', '${SOURCE}', '/build']
+ if project_config:
+ vs_build_action.extend([project_config])
+
+ build_command = env.Command(target='%s_build_output.txt' % target_name,
+ source=solution,
+ action=' '.join(vs_build_action))
+ env.AddNodeAliases(build_command, ['bot'], target_name)
+ return build_command
+
+build_env.AddMethod(BuildVSSolution)
+
+
+def CleanVSSolution(env, target_name, solution_dir):
+ """CleanVSSolution() Cleans up a Visual Studio solution's build results.
+
+ The clean target created by this function is added to the 'bot' target as
+ well as the target specified. The function will clean any build artifacts
+ that could possibly be generated under the solution directory.
+
+ Args:
+ env: The construction Environment() that runs the command.
+ target_name: The name of the target which builds whatever should be cleaned
+ up.
+ solution_dir: The directory under which VS build artifacts are to be
+ expected. This function will look for Debug, Release, and x64 build
+ targets.
+ """
+ clean_targets = [os.path.join(solution_dir, 'Debug'),
+ os.path.join(solution_dir, 'Release'),
+ os.path.join(solution_dir, 'x64')]
+
+ for target in clean_targets:
+ clean_action = ['rmdir', '/Q', '/S', target]
+ env.AddCleanAction([target],
+ Action(' '.join(clean_action)),
+ ['bot'],
+ target_name)
+
+build_env.AddMethod(CleanVSSolution)
+
+
+def TestVSSolution(env, target_name, test_container, type, size, build_cmd):
+ """Defines a set of tests to be added to the scons test set.
+
+ This function adds a test solution generated by Visual Studio. It can either
+ run mstest or, for natively compiled solutions, it can run an executable.
+
+ Args:
+ env: The construction Environment() that runs the command.
+ target_name: The name of the target which resulted in the test container.
+ A name that clearly marks the target as a test is recommended here.
+ test_container: The fully qualified path to the dll or exe that contains
+ the tests.
+ type: The type test package to expect as a string. This can be 'dll' or
+ 'exe'.
+ size: Which test harness to add the tests to; small, medium, or large
+ build_cmd: The command which builds the target being tested.
+ """
+ test_action = test_container
+ if type is 'dll':
+ test_action = ' '.join(['vcvarsall',
+ '&&',
+ 'mstest',
+ '/testcontainer:%s' % test_container,
+ '/resultsfile:${TARGET}'])
+
+ # Can't use the test container as SOURCE because it is generated indirectly
+ # and using it as source would declare an explicit dependency on a target,
+ # generated by scons. Files that exist in the environment can be used in that
+ # way, but only if they exist at the time when scons starts to run, or if
+ # the are explicit Command targets. As a result, source is empty.
+ test_command = env.Command(
+ target='%s_test_results.trx' % target_name,
+ source='',
+ action=test_action)
+
+ env.Depends(test_command, build_cmd)
+ env.AddNodeToTestSuite(test_command,
+ ['bot'],
+ target_name,
+ size)
+ return test_command
+
+build_env.AddMethod(TestVSSolution)
+
+
+# ----------------------------------------------------------------------------
+BuildComponents(environment_list)
+
+# Require specifying an explicit target only when not cleaning
+if not GetOption('clean'):
+ Default(None)
diff --git a/native_client_sdk/src/project_templates/README b/native_client_sdk/src/project_templates/README
new file mode 100644
index 0000000..5aca398
--- /dev/null
+++ b/native_client_sdk/src/project_templates/README
@@ -0,0 +1,45 @@
+Welcome to the Native Client SDK project_templates directory.
+
+Currently, this directory contains a mechanism to allow a developer to
+bootstrap a native client project and write a lot of the NaCl-specific code
+automatically, so the developer can add her own functionality quickly. The
+projects created are designed to be self-contained, meaning that they have
+everything they need to run, except a server.
+
+To start a project, run "./init_project.py" or "python init_project.py"
+depending on your system configuration. The script will give you usage
+information when run with -h or when insufficient or malformed arguments are
+provided.
+
+The result of the script is a project with the name you provide at a location
+of your choice. If you have your own server, you may create the project in
+a location it serves. Otherwise, you may create a project under the SDK
+examples directory and examples/httpd.py to serve your project quickly - at
+least temporarily.
+
+In the future, this directory is intended as a repository for useful stub code,
+code snippets, and code generators that can be used to facilitate rapid
+development. For now we support initial project setup via init_project.py, but
+any generically useful code can be added here if it follows a reasonable
+organization. The organization is as follows:
+
+project_templates:
+ Contains any top-level scripting elements that apply or may come in useful
+ for the generation of all NaCl/Pepper2 projects, common Makefile sections,
+ and this README.
+project_templates/[language]:
+ For any given language there should be a directory with a name that is
+ commonly associated with that language.
+project_templates/[topic]
+project_templates/[language]/[topic]
+ For any given programming topic, such as audio, 2d, or 3d programming, there
+ should be a directory at the root level for any components that are not
+ language specific and that may apply to that topic. For corresponding
+ components that are language specific, a sub-directory for the topic may
+ also be created under the language directory.
+
+Note that the layout in this directory does not reflect the layout of the
+projects that are created. It is merely a set of simple guidelines to help
+organize generic code and utilities so they can be managed here. How
+generated projects are laid out is left up to the design of the particular
+code-generator.
diff --git a/native_client_sdk/src/project_templates/c/build.scons b/native_client_sdk/src/project_templates/c/build.scons
new file mode 100644
index 0000000..fcfcf0e
--- /dev/null
+++ b/native_client_sdk/src/project_templates/c/build.scons
@@ -0,0 +1,15 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import make_nacl_env
+import os
+
+nacl_env = make_nacl_env.NaClEnvironment(
+ nacl_platform=os.getenv('NACL_TARGET_PLATFORM'))
+
+sources = ['<PROJECT_NAME>.c']
+
+nacl_env.AllNaClModules(sources, '<PROJECT_NAME>')
diff --git a/native_client_sdk/src/project_templates/c/project_file.c b/native_client_sdk/src/project_templates/c/project_file.c
new file mode 100644
index 0000000..7f25bd6
--- /dev/null
+++ b/native_client_sdk/src/project_templates/c/project_file.c
@@ -0,0 +1,227 @@
+/** @file <PROJECT_NAME>.c
+ * This example demonstrates loading, running and scripting a very simple
+ * NaCl module.
+ */
+#include <stdlib.h>
+#include <string.h>
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/c/pp_module.h"
+#include "ppapi/c/pp_var.h"
+#include "ppapi/c/ppb.h"
+#include "ppapi/c/ppb_instance.h"
+#include "ppapi/c/ppb_messaging.h"
+#include "ppapi/c/ppb_var.h"
+#include "ppapi/c/ppp.h"
+#include "ppapi/c/ppp_instance.h"
+#include "ppapi/c/ppp_messaging.h"
+
+static PP_Module module_id = 0;
+static struct PPB_Messaging* messaging_interface = NULL;
+static struct PPB_Var* var_interface = NULL;
+
+/**
+ * Returns a mutable C string contained in the @a var or NULL if @a var is not
+ * string. This makes a copy of the string in the @a var and adds a NULL
+ * terminator. Note that VarToUtf8() does not guarantee the NULL terminator on
+ * the returned string. See the comments for VarToUtf8() in ppapi/c/ppb_var.h
+ * for more info. The caller is responsible for freeing the returned memory.
+ * @param[in] var PP_Var containing string.
+ * @return a mutable C string representation of @a var.
+ * @note The caller is responsible for freeing the returned string.
+ */
+/* TODO(sdk_user): 2. Uncomment this when you need it. It is commented out so
+ * that the compiler doesn't complain about unused functions.
+ */
+#if 0
+static char* AllocateCStrFromVar(struct PP_Var var) {
+ uint32_t len = 0;
+ if (var_interface != NULL) {
+ const char* var_c_str = var_interface->VarToUtf8(var, &len);
+ if (len > 0) {
+ char* c_str = (char*)malloc(len + 1);
+ memcpy(c_str, var_c_str, len);
+ c_str[len] = '\0';
+ return c_str;
+ }
+ }
+ return NULL;
+}
+#endif
+
+/**
+ * Creates a new string PP_Var from C string. The resulting object will be a
+ * refcounted string object. It will be AddRef()ed for the caller. When the
+ * caller is done with it, it should be Release()d.
+ * @param[in] str C string to be converted to PP_Var
+ * @return PP_Var containing string.
+ */
+/* TODO(sdk_user): 3. Uncomment this when you need it. It is commented out so
+ * that the compiler doesn't complain about unused functions.
+ */
+#if 0
+static struct PP_Var AllocateVarFromCStr(const char* str) {
+ if (var_interface != NULL)
+ return var_interface->VarFromUtf8(module_id, str, strlen(str));
+ return PP_MakeUndefined();
+}
+#endif
+
+/**
+ * Called when the NaCl module is instantiated on the web page. The identifier
+ * of the new instance will be passed in as the first argument (this value is
+ * generated by the browser and is an opaque handle). This is called for each
+ * instantiation of the NaCl module, which is each time the <embed> tag for
+ * this module is encountered.
+ *
+ * If this function reports a failure (by returning @a PP_FALSE), the NaCl
+ * module will be deleted and DidDestroy will be called.
+ * @param[in] instance The identifier of the new instance representing this
+ * NaCl module.
+ * @param[in] argc The number of arguments contained in @a argn and @a argv.
+ * @param[in] argn An array of argument names. These argument names are
+ * supplied in the <embed> tag, for example:
+ * <embed id="nacl_module" dimensions="2">
+ * will produce two arguments, one named "id" and one named "dimensions".
+ * @param[in] argv An array of argument values. These are the values of the
+ * arguments listed in the <embed> tag. In the above example, there will
+ * be two elements in this array, "nacl_module" and "2". The indices of
+ * these values match the indices of the corresponding names in @a argn.
+ * @return @a PP_TRUE on success.
+ */
+static PP_Bool Instance_DidCreate(PP_Instance instance,
+ uint32_t argc,
+ const char* argn[],
+ const char* argv[]) {
+ return PP_TRUE;
+}
+
+/**
+ * Called when the NaCl module is destroyed. This will always be called,
+ * even if DidCreate returned failure. This routine should deallocate any data
+ * associated with the instance.
+ * @param[in] instance The identifier of the instance representing this NaCl
+ * module.
+ */
+static void Instance_DidDestroy(PP_Instance instance) {
+}
+
+/**
+ * Called when the position, the size, or the clip rect of the element in the
+ * browser that corresponds to this NaCl module has changed.
+ * @param[in] instance The identifier of the instance representing this NaCl
+ * module.
+ * @param[in] position The location on the page of this NaCl module. This is
+ * relative to the top left corner of the viewport, which changes as the
+ * page is scrolled.
+ * @param[in] clip The visible region of the NaCl module. This is relative to
+ * the top left of the plugin's coordinate system (not the page). If the
+ * plugin is invisible, @a clip will be (0, 0, 0, 0).
+ */
+static void Instance_DidChangeView(PP_Instance instance,
+ const struct PP_Rect* position,
+ const struct PP_Rect* clip) {
+}
+
+/**
+ * Notification that the given NaCl module has gained or lost focus.
+ * Having focus means that keyboard events will be sent to the NaCl module
+ * represented by @a instance. A NaCl module's default condition is that it
+ * will not have focus.
+ *
+ * Note: clicks on NaCl modules will give focus only if you handle the
+ * click event. You signal if you handled it by returning @a true from
+ * HandleInputEvent. Otherwise the browser will bubble the event and give
+ * focus to the element on the page that actually did end up consuming it.
+ * If you're not getting focus, check to make sure you're returning true from
+ * the mouse click in HandleInputEvent.
+ * @param[in] instance The identifier of the instance representing this NaCl
+ * module.
+ * @param[in] has_focus Indicates whether this NaCl module gained or lost
+ * event focus.
+ */
+static void Instance_DidChangeFocus(PP_Instance instance,
+ PP_Bool has_focus) {
+}
+
+/**
+ * Handler that gets called after a full-frame module is instantiated based on
+ * registered MIME types. This function is not called on NaCl modules. This
+ * function is essentially a place-holder for the required function pointer in
+ * the PPP_Instance structure.
+ * @param[in] instance The identifier of the instance representing this NaCl
+ * module.
+ * @param[in] url_loader A PP_Resource an open PPB_URLLoader instance.
+ * @return PP_FALSE.
+ */
+static PP_Bool Instance_HandleDocumentLoad(PP_Instance instance,
+ PP_Resource url_loader) {
+ /* NaCl modules do not need to handle the document load function. */
+ return PP_FALSE;
+}
+
+
+/**
+ * Handler for messages coming in from the browser via postMessage. The
+ * @a var_message can contain anything: a JSON string; a string that encodes
+ * method names and arguments; etc. For example, you could use JSON.stringify
+ * in the browser to create a message that contains a method name and some
+ * parameters, something like this:
+ * var json_message = JSON.stringify({ "myMethod" : "3.14159" });
+ * nacl_module.postMessage(json_message);
+ * On receipt of this message in @a var_message, you could parse the JSON to
+ * retrieve the method name, match it to a function call, and then call it with
+ * the parameter.
+ * @param[in] instance The instance ID.
+ * @param[in] message The contents, copied by value, of the message sent from
+ * browser via postMessage.
+ */
+void Messaging_HandleMessage(PP_Instance instance, struct PP_Var var_message) {
+ /* TODO(sdk_user): 1. Make this function handle the incoming message. */
+}
+
+/**
+ * Entry points for the module.
+ * Initialize instance interface and scriptable object class.
+ * @param[in] a_module_id Module ID
+ * @param[in] get_browser_interface Pointer to PPB_GetInterface
+ * @return PP_OK on success, any other value on failure.
+ */
+PP_EXPORT int32_t PPP_InitializeModule(PP_Module a_module_id,
+ PPB_GetInterface get_browser_interface) {
+ module_id = a_module_id;
+ var_interface = (struct PPB_Var*)(get_browser_interface(PPB_VAR_INTERFACE));
+ messaging_interface =
+ (struct PPB_Messaging*)(get_browser_interface(PPB_MESSAGING_INTERFACE));
+ return PP_OK;
+}
+
+/**
+ * Returns an interface pointer for the interface of the given name, or NULL
+ * if the interface is not supported.
+ * @param[in] interface_name name of the interface
+ * @return pointer to the interface
+ */
+PP_EXPORT const void* PPP_GetInterface(const char* interface_name) {
+ if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) {
+ static struct PPP_Instance instance_interface = {
+ &Instance_DidCreate,
+ &Instance_DidDestroy,
+ &Instance_DidChangeView,
+ &Instance_DidChangeFocus,
+ &Instance_HandleDocumentLoad
+ };
+ return &instance_interface;
+ } else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0) {
+ static struct PPP_Messaging messaging_interface = {
+ &Messaging_HandleMessage
+ };
+ return &messaging_interface;
+ }
+ return NULL;
+}
+
+/**
+ * Called before the plugin module is unloaded.
+ */
+PP_EXPORT void PPP_ShutdownModule() {
+}
diff --git a/native_client_sdk/src/project_templates/cc/build.scons b/native_client_sdk/src/project_templates/cc/build.scons
new file mode 100644
index 0000000..537d17b
--- /dev/null
+++ b/native_client_sdk/src/project_templates/cc/build.scons
@@ -0,0 +1,16 @@
+#! -*- python -*-
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import make_nacl_env
+import nacl_utils
+import os
+
+nacl_env = make_nacl_env.NaClEnvironment(
+ use_c_plus_plus_libs=True, nacl_platform=os.getenv('NACL_TARGET_PLATFORM'))
+
+sources = ['<PROJECT_NAME>.cc']
+
+nacl_env.AllNaClModules(sources, '<PROJECT_NAME>')
diff --git a/native_client_sdk/src/project_templates/cc/project_file.cc b/native_client_sdk/src/project_templates/cc/project_file.cc
new file mode 100644
index 0000000..c0123fe
--- /dev/null
+++ b/native_client_sdk/src/project_templates/cc/project_file.cc
@@ -0,0 +1,87 @@
+/// @file <PROJECT_NAME>.cc
+/// This example demonstrates loading, running and scripting a very simple NaCl
+/// module. To load the NaCl module, the browser first looks for the
+/// CreateModule() factory method (at the end of this file). It calls
+/// CreateModule() once to load the module code from your .nexe. After the
+/// .nexe code is loaded, CreateModule() is not called again.
+///
+/// Once the .nexe code is loaded, the browser than calls the CreateInstance()
+/// method on the object returned by CreateModule(). It calls CreateInstance()
+/// each time it encounters an <embed> tag that references your NaCl module.
+///
+/// The browser can talk to your NaCl module via the postMessage() Javascript
+/// function. When you call postMessage() on your NaCl module from the browser,
+/// this becomes a call to the HandleMessage() method of your pp::Instance
+/// subclass. You can send messages back to the browser by calling the
+/// PostMessage() method on your pp::Instance. Note that these two methods
+/// (postMessage() in Javascript and PostMessage() in C++) are asynchronous.
+/// This means they return immediately - there is no waiting for the message
+/// to be handled. This has implications in your program design, particularly
+/// when mutating property values that are exposed to both the browser and the
+/// NaCl module.
+
+#include <cstdio>
+#include <string>
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/cpp/var.h"
+
+/// The Instance class. One of these exists for each instance of your NaCl
+/// module on the web page. The browser will ask the Module object to create
+/// a new Instance for each occurence of the <embed> tag that has these
+/// attributes:
+/// type="application/x-nacl"
+/// src="<PROJECT_NAME>.nmf"
+/// To communicate with the browser, you must override HandleMessage() for
+/// receiving messages from the borwser, and use PostMessage() to send messages
+/// back to the browser. Note that this interface is entirely asynchronous.
+class <ProjectName>Instance : public pp::Instance {
+ public:
+ /// The constructor creates the plugin-side instance.
+ /// @param[in] instance the handle to the browser-side plugin instance.
+ explicit <ProjectName>Instance(PP_Instance instance) : pp::Instance(instance)
+ {}
+ virtual ~<ProjectName>Instance() {}
+
+ /// Handler for messages coming in from the browser via postMessage(). The
+ /// @a var_message can contain anything: a JSON string; a string that encodes
+ /// method names and arguments; etc. For example, you could use
+ /// JSON.stringify in the browser to create a message that contains a method
+ /// name and some parameters, something like this:
+ /// var json_message = JSON.stringify({ "myMethod" : "3.14159" });
+ /// nacl_module.postMessage(json_message);
+ /// On receipt of this message in @a var_message, you could parse the JSON to
+ /// retrieve the method name, match it to a function call, and then call it
+ /// with the parameter.
+ /// @param[in] var_message The message posted by the browser.
+ virtual void HandleMessage(const pp::Var& var_message) {
+ // TODO(sdk_user): 1. Make this function handle the incoming message.
+ }
+};
+
+/// The Module class. The browser calls the CreateInstance() method to create
+/// an instance of your NaCl module on the web page. The browser creates a new
+/// instance for each <embed> tag with type="application/x-nacl".
+class <ProjectName>Module : public pp::Module {
+ public:
+ <ProjectName>Module() : pp::Module() {}
+ virtual ~<ProjectName>Module() {}
+
+ /// Create and return a <ProjectName>Instance object.
+ /// @param[in] instance The browser-side instance.
+ /// @return the plugin-side instance.
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new <ProjectName>Instance(instance);
+ }
+};
+
+namespace pp {
+/// Factory function called by the browser when the module is first loaded.
+/// The browser keeps a singleton of this module. It calls the
+/// CreateInstance() method on the object you return to make instances. There
+/// is one instance per <embed> tag on the page. This is the main binding
+/// point for your NaCl module with the browser.
+Module* CreateModule() {
+ return new <ProjectName>Module();
+}
+} // namespace pp
diff --git a/native_client_sdk/src/project_templates/html/project_file.html b/native_client_sdk/src/project_templates/html/project_file.html
new file mode 100644
index 0000000..0b21ad9
--- /dev/null
+++ b/native_client_sdk/src/project_templates/html/project_file.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title><ProjectName>!</title>
+
+ <script type="text/javascript">
+ <ProjectName>Module = null; // Global application object.
+ statusText = 'NO-STATUS';
+
+ // Indicate load success.
+ function moduleDidLoad() {
+ <ProjectName>Module = document.getElementById('<PROJECT_NAME>');
+ updateStatus('SUCCESS');
+ }
+
+ // The 'message' event handler. This handler is fired when the NaCl module
+ // posts a message to the browser by calling PPB_Messaging.PostMessage()
+ // (in C) or pp::Instance.PostMessage() (in C++). This implementation
+ // simply displays the content of the message in an alert panel.
+ function handleMessage(message_event) {
+ alert(message_event.data);
+ }
+
+ // If the page loads before the Native Client module loads, then set the
+ // status message indicating that the module is still loading. Otherwise,
+ // do not change the status message.
+ function pageDidLoad() {
+ if (<ProjectName>Module == null) {
+ updateStatus('LOADING...');
+ } else {
+ // It's possible that the Native Client module onload event fired
+ // before the page's onload event. In this case, the status message
+ // will reflect 'SUCCESS', but won't be displayed. This call will
+ // display the current message.
+ updateStatus();
+ }
+ }
+
+ // Set the global status message. If the element with id 'statusField'
+ // exists, then set its HTML to the status message as well.
+ // opt_message The message test. If this is null or undefined, then
+ // attempt to set the element with id 'statusField' to the value of
+ // |statusText|.
+ function updateStatus(opt_message) {
+ if (opt_message)
+ statusText = opt_message;
+ var statusField = document.getElementById('status_field');
+ if (statusField) {
+ statusField.innerHTML = statusText;
+ }
+ }
+ </script>
+</head>
+<body onload="pageDidLoad()">
+
+<h1>Native Client Module <ProjectName></h1>
+<p>
+ <!-- Load the published .nexe. This includes the 'nacl' attribute which
+ shows how to load multi-architecture modules. Each entry in the "nexes"
+ object in the .nmf manifest file is a key-value pair: the key is the
+ instruction set architecture ('x86-32', 'x86-64', etc.); the value is a URL
+ for the desired NaCl module.
+ To load the debug versions of your .nexes, set the 'nacl' attribute to the
+ _dbg.nmf version of the manifest file.
+
+ Note: Since this NaCl module does not use any real-estate in the browser,
+ it's width and height are set to 0.
+
+ Note: The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
+ and a 'message' event listener attached. This wrapping method is used
+ instead of attaching the event listeners directly to the <EMBED> element to
+ ensure that the listeners are active before the NaCl module 'load' event
+ fires. This also allows you to use PPB_Messaging.PostMessage() (in C) or
+ pp::Instance.PostMessage() (in C++) from within the initialization code in
+ your NaCl module.
+ -->
+ <div id="listener">
+ <script type="text/javascript">
+ var listener = document.getElementById('listener');
+ listener.addEventListener('load', moduleDidLoad, true);
+ listener.addEventListener('message', handleMessage, true);
+ </script>
+
+ <embed name="nacl_module"
+ id="<PROJECT_NAME>"
+ width=0 height=0
+ src="<PROJECT_NAME>.nmf"
+ type="application/x-nacl" />
+ </div>
+</p>
+
+<h2>Status</h2>
+<div id="status_field">NO-STATUS</div>
+</body>
+</html>
diff --git a/native_client_sdk/src/project_templates/init_project.py b/native_client_sdk/src/project_templates/init_project.py
new file mode 100755
index 0000000..4b5bad2
--- /dev/null
+++ b/native_client_sdk/src/project_templates/init_project.py
@@ -0,0 +1,502 @@
+#!/usr/bin/python2.6
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""A simple project generator for Native Client projects written in C or C++.
+
+This script accepts a few argument which it uses as a description of a new NaCl
+project. It sets up a project with a given name and a given primary language
+(default: C++, optionally, C) using the appropriate files from this area.
+This script does not handle setup for complex applications, just the basic
+necessities to get a functional native client application stub. When this
+script terminates a compileable project stub will exist with the specified
+name, at the specified location.
+
+GetCamelCaseName(): Converts an underscore name to a camel case name.
+GetCodeDirectory(): Decides what directory to pull source code from.
+GetCodeSoureFiles(): Decides what source files to pull into the stub.
+GetCommonSourceFiles(): Gives list of files needed by all project types.
+GetHTMLDirectory(): Decides what directory to pull HTML stub from.
+GetHTMLSourceFiles(): Gives HTML files to be included in project stub.
+GetTargetFileName(): Converts a source file name into a project file name.
+ParseArguments(): Parses the arguments provided by the user.
+ReplaceInFile(): Replaces a given string with another in a given file.
+ProjectInitializer: Maintains some state applicable to setting up a project.
+main(): Executes the script.
+"""
+
+__author__ = 'mlinck@google.com (Michael Linck)'
+
+import fileinput
+import optparse
+import os.path
+import shutil
+import sys
+import uuid
+
+# A list of all platforms that should have make.cmd.
+WINDOWS_BUILD_PLATFORMS = ['cygwin', 'win32']
+
+# Tags that will be replaced in our the new project's source files.
+PROJECT_NAME_TAG = '<PROJECT_NAME>'
+PROJECT_NAME_CAMEL_CASE_TAG = '<ProjectName>'
+SDK_ROOT_TAG = '<NACL_SDK_ROOT>'
+NACL_PLATFORM_TAG = '<NACL_PLATFORM>'
+VS_PROJECT_UUID_TAG = '<VS_PROJECT_UUID>'
+VS_SOURCE_UUID_TAG = '<VS_SOURCE_UUID>'
+VS_HEADER_UUID_TAG = '<VS_HEADER_UUID>'
+VS_RESOURCE_UUID_TAG = '<VS_RESOURCE_UUID>'
+
+# This string is the part of the file name that will be replaced.
+PROJECT_FILE_NAME = 'project_file'
+
+# Lists of source files that will be used for the new project.
+COMMON_PROJECT_FILES = ['scons']
+C_SOURCE_FILES = ['build.scons', '%s.c' % PROJECT_FILE_NAME]
+CC_SOURCE_FILES = ['build.scons', '%s.cc' % PROJECT_FILE_NAME]
+HTML_FILES = ['%s.html' % PROJECT_FILE_NAME]
+VS_FILES = ['%s.sln' % PROJECT_FILE_NAME, '%s.vcproj' % PROJECT_FILE_NAME]
+
+# Error needs to be a class, since we 'raise' it in several places.
+class Error(Exception):
+ pass
+
+
+def GetCamelCaseName(lower_case_name):
+ """Converts an underscore name to a camel case name.
+
+ Args:
+ lower_case_name: The name in underscore-delimited lower case format.
+
+ Returns:
+ The name in camel case format.
+ """
+ camel_case_name = ''
+ name_parts = lower_case_name.split('_')
+ for part in name_parts:
+ if part:
+ camel_case_name += part.capitalize()
+ return camel_case_name
+
+
+def GetCodeDirectory(is_c_project, project_templates_dir):
+ """Decides what directory to pull source code from.
+
+ Args:
+ is_c_project: A boolean indicating whether this project is in C or not.
+ project_templates_dir: The path to the project_templates directory.
+
+ Returns:
+ The code directory for the given project type.
+ """
+ stub_directory = ''
+ if is_c_project:
+ stub_directory = os.path.join(project_templates_dir, 'c')
+ else:
+ stub_directory = os.path.join(project_templates_dir, 'cc')
+ return stub_directory
+
+
+def GetCodeSourceFiles(is_c_project):
+ """Decides what source files to pull into the stub.
+
+ Args:
+ is_c_project: A boolean indicating whether this project is in C or not.
+
+ Returns:
+ The files that are specific to the requested type of project and live in its
+ directory.
+ """
+ project_files = []
+ if is_c_project:
+ project_files = C_SOURCE_FILES
+ else:
+ project_files = CC_SOURCE_FILES
+ return project_files
+
+
+def GetCommonSourceFiles():
+ """Gives list of files needed by all project types.
+
+ Returns:
+ The files C and C++ projects have in common. These are the files that live
+ in the top level project_templates directory.
+ """
+ project_files = COMMON_PROJECT_FILES
+ if sys.platform in WINDOWS_BUILD_PLATFORMS:
+ project_files.extend(['scons.bat'])
+ return project_files
+
+
+def GetVsDirectory(project_templates_dir):
+ """Decides what directory to pull Visual Studio stub from.
+
+ Args:
+ project_templates_dir: The path to the project_templates directory.
+
+ Returns:
+ The directory where the HTML stub is to be found.
+ """
+ return os.path.join(project_templates_dir, 'vs')
+
+
+def GetVsProjectFiles():
+ """Gives VisualStudio files to be included in project stub.
+
+ Returns:
+ The VisualStudio files needed for the project.
+ """
+ return VS_FILES
+
+
+def GetHTMLDirectory(project_templates_dir):
+ """Decides what directory to pull HTML stub from.
+
+ Args:
+ project_templates_dir: The path to the project_templates directory.
+
+ Returns:
+ The directory where the HTML stub is to be found.
+ """
+ return os.path.join(project_templates_dir, 'html')
+
+
+def GetHTMLSourceFiles():
+ """Gives HTML files to be included in project stub.
+
+ Returns:
+ The HTML files needed for the project.
+ """
+ return HTML_FILES
+
+
+def GetTargetFileName(source_file_name, project_name):
+ """Converts a source file name into a project file name.
+
+ Args:
+ source_file_name: The name of a file that is to be included in the project
+ stub, as it appears at the source location.
+ project_name: The name of the project that is being generated.
+
+ Returns:
+ The target file name for a given source file. All project files are run
+ through this filter and it modifies them as needed.
+ """
+ target_file_name = ''
+ if source_file_name.startswith(PROJECT_FILE_NAME):
+ target_file_name = source_file_name.replace(PROJECT_FILE_NAME,
+ project_name)
+ else:
+ target_file_name = source_file_name
+ return target_file_name
+
+
+def GetDefaultProjectDir():
+ """Determines the default project directory.
+
+ The default directory root for new projects is called 'nacl_projects' under
+ the user's home directory. There are two ways to override this: you can set
+ the NACL_PROJECT_ROOT environment variable, or use the --directory option.
+
+ Returns:
+ An os-specific path to the default project directory, which is called
+ 'nacl_projects' under the user's home directory.
+ """
+ return os.getenv('NACL_PROJECT_ROOT',
+ os.path.join(os.path.expanduser('~'), 'nacl_projects'))
+
+
+def ParseArguments(argv):
+ """Parses the arguments provided by the user.
+
+ Parses the command line options and makes sure the script errors when it is
+ supposed to.
+
+ Args:
+ argv: The argument array.
+
+ Returns:
+ The options structure that represents the arguments after they have been
+ parsed.
+ """
+ parser = optparse.OptionParser()
+ parser.add_option(
+ '-n', '--name', dest='project_name',
+ default='',
+ help=('Required: the name of the new project to be stubbed out.\n'
+ 'Please use lower case names with underscore, i.e. hello_world.'))
+ parser.add_option(
+ '-d', '--directory', dest='project_directory',
+ default=GetDefaultProjectDir(),
+ help=('Optional: If set, the new project will be created under this '
+ 'directory and the directory created if necessary.'))
+ parser.add_option(
+ '-c', action='store_true', dest='is_c_project',
+ default=False,
+ help=('Optional: If set, this will generate a C project. Default '
+ 'is C++.'))
+ parser.add_option(
+ '-p', '--nacl-platform', dest='nacl_platform',
+ default='pepper_17',
+ help=('Optional: if set, the new project will target the given nacl\n'
+ 'platform. Default is the most current platform. e.g. pepper_17'))
+ parser.add_option(
+ '--vsproj', action='store_true', dest='is_vs_project',
+ default=False,
+ help=('Optional: If set, generate Visual Studio project files.'))
+ result = parser.parse_args(argv)
+ options = result[0]
+ args = result[1]
+ #options, args) = parser.parse_args(argv)
+ if args:
+ parser.print_help()
+ sys.exit(1)
+ elif not options.project_name.islower():
+ print('--name missing or in incorrect format. Please use -h for '
+ 'instructions.')
+ sys.exit(1)
+ return options
+
+
+class ProjectInitializer(object):
+ """Maintains the state of the project that is being created."""
+
+ def __init__(self, is_c_project, is_vs_project, project_name,
+ project_location, nacl_platform, project_templates_dir,
+ os_resource=os):
+ """Initializes all the fields that are known after parsing the parameters.
+
+ Args:
+ is_c_project: A boolean indicating whether this project is in C or not.
+ is_vs_project: A boolean indicating whether this project has Visual
+ Studio support.
+ project_name: A string containing the name of the project to be created.
+ project_location: A path indicating where the new project is to be placed.
+ project_templates_dir: The path to the project_templates directory.
+ os_resource: A resource to be used as os. Provided for unit testing.
+ """
+ self.__is_c_project = is_c_project
+ self.__is_vs_project = is_vs_project
+ self.__project_files = []
+ self.__project_name = project_name
+ self.__project_location = project_location
+ self.__nacl_platform = nacl_platform
+ self.__project_templates_dir = project_templates_dir
+ # System resources are properties so mocks can be inserted.
+ self.__fileinput = fileinput
+ self.__os = os_resource
+ self.__shutil = shutil
+ self.__sys = sys
+ self.__CreateProjectDirectory()
+
+ def CopyAndRenameFiles(self, source_dir, file_names):
+ """Places files in the new project's directory and renames them as needed.
+
+ Copies the given files from the given source directory into the new
+ project's directory, renaming them as necessary. Each file that is created
+ in the project directory is also added to self.__project_files.
+
+ Args:
+ source_dir: A path indicating where the files are to be copied from.
+ file_names: The list of files that is to be copied out of source_dir.
+ """
+ for source_file_name in file_names:
+ target_file_name = GetTargetFileName(source_file_name,
+ self.__project_name)
+ copy_source_file = self.os.path.join(source_dir, source_file_name)
+ copy_target_file = self.os.path.join(self.__project_dir, target_file_name)
+ self.shutil.copy(copy_source_file, copy_target_file)
+ self.__project_files += [copy_target_file]
+
+ def __CreateProjectDirectory(self):
+ """Creates the project's directory and any parents as necessary."""
+ self.__project_dir = self.os.path.join(self.__project_location,
+ self.__project_name)
+ if self.os.path.exists(self.__project_dir):
+ raise Error("Error: directory '%s' already exists" % self.__project_dir)
+ self.os.makedirs(self.__project_dir)
+
+ def PrepareDirectoryContent(self):
+ """Prepares the directory for the new project.
+
+ This function's job is to know what directories need to be used and what
+ files need to be copied and renamed. It uses several tiny helper functions
+ to do this.
+ There are three locations from which files are copied to create a project.
+ That number may change in the future.
+ """
+ code_source_dir = GetCodeDirectory(self.__is_c_project,
+ self.__project_templates_dir)
+ code_source_files = GetCodeSourceFiles(self.__is_c_project)
+ html_source_dir = GetHTMLDirectory(self.__project_templates_dir)
+ html_source_files = GetHTMLSourceFiles()
+ common_source_files = GetCommonSourceFiles()
+ self.CopyAndRenameFiles(code_source_dir, code_source_files)
+ self.CopyAndRenameFiles(html_source_dir, html_source_files)
+ self.CopyAndRenameFiles(self.__project_templates_dir,
+ common_source_files)
+ if self.__is_vs_project:
+ vs_source_dir = GetVsDirectory(self.__project_templates_dir)
+ vs_files = GetVsProjectFiles()
+ self.CopyAndRenameFiles(vs_source_dir, vs_files)
+ print('init_project has copied the appropriate files to: %s' %
+ self.__project_dir)
+
+ def PrepareFileContent(self):
+ """Changes contents of files in the new project as needed.
+
+ Goes through each file in the project that is being created and replaces
+ contents as necessary.
+ """
+ camel_case_name = GetCamelCaseName(self.__project_name)
+ sdk_root_dir = os.getenv('NACL_SDK_ROOT', None)
+ if not sdk_root_dir:
+ raise Error("Error: NACL_SDK_ROOT is not set")
+ sdk_root_dir = self.os.path.abspath(sdk_root_dir)
+ if self.__is_vs_project:
+ project_uuid = str(uuid.uuid4()).upper()
+ vs_source_uuid = str(uuid.uuid4()).upper()
+ vs_header_uuid = str(uuid.uuid4()).upper()
+ vs_resource_uuid = str(uuid.uuid4()).upper()
+ for project_file in self.__project_files:
+ self.ReplaceInFile(project_file, PROJECT_NAME_TAG, self.__project_name)
+ self.ReplaceInFile(project_file,
+ PROJECT_NAME_CAMEL_CASE_TAG,
+ camel_case_name)
+ self.ReplaceInFile(project_file, SDK_ROOT_TAG, sdk_root_dir)
+ self.ReplaceInFile(project_file, NACL_PLATFORM_TAG, self.__nacl_platform)
+ if self.__is_vs_project:
+ self.ReplaceInFile(project_file, VS_PROJECT_UUID_TAG, project_uuid)
+ self.ReplaceInFile(project_file, VS_SOURCE_UUID_TAG, vs_source_uuid)
+ self.ReplaceInFile(project_file, VS_HEADER_UUID_TAG, vs_header_uuid)
+ self.ReplaceInFile(project_file, VS_RESOURCE_UUID_TAG, vs_resource_uuid)
+
+ def ReplaceInFile(self, file_path, old_text, new_text):
+ """Replaces a given string with another in a given file.
+
+ Args:
+ file_path: The path to the file that is to be modified.
+ old_text: The text that is to be removed.
+ new_text: The text that is to be added in place of old_text.
+ """
+ for line in self.fileinput.input(file_path, inplace=1, mode='U'):
+ self.sys.stdout.write(line.replace(old_text, new_text))
+
+ # The following properties exist to make unit testing possible.
+
+ def _GetFileinput(self):
+ """Accessor for Fileinput property."""
+ return self.__fileinput
+
+ def __GetFileinput(self):
+ """Indirect Accessor for _GetFileinput."""
+ return self._GetFileinput()
+
+ def _SetFileinput(self, fileinput_resource):
+ """Accessor for Fileinput property."""
+ self.__fileinput = fileinput_resource
+
+ def __SetFileinput(self, fileinput_resource):
+ """Indirect Accessor for _SetFileinput."""
+ return self._SetFileinput(fileinput_resource)
+
+ fileinput = property(
+ __GetFileinput, __SetFileinput,
+ doc="""Gets and sets the resource to use as fileinput.""")
+
+ def _GetOS(self):
+ """Accessor for os property."""
+ return self.__os
+
+ def __GetOS(self):
+ """Indirect Accessor for _GetOS."""
+ return self._GetOS()
+
+ def _SetOS(self, os_resource):
+ """Accessor for os property."""
+ self.__os = os_resource
+
+ def __SetOS(self, os_resource):
+ """Indirect Accessor for _SetOS."""
+ return self._SetOS(os_resource)
+
+ os = property(__GetOS, __SetOS,
+ doc="""Gets and sets the resource to use as os.""")
+
+ def _GetShutil(self):
+ """Accessor for shutil property."""
+ return self.__shutil
+
+ def __GetShutil(self):
+ """Indirect Accessor for _GetShutil."""
+ return self._GetShutil()
+
+ def _SetShutil(self, shutil_resource):
+ """Accessor for shutil property."""
+ self.__shutil = shutil_resource
+
+ def __SetShutil(self, shutil_resource):
+ """Indirect Accessor for _SetShutil."""
+ return self._SetShutil(shutil_resource)
+
+ shutil = property(__GetShutil, __SetShutil,
+ doc="""Gets and sets the resource to use as shutil.""")
+
+ def _GetSys(self):
+ """Accessor for sys property."""
+ return self.__sys
+
+ def __GetSys(self):
+ """Indirect Accessor for _GetSys."""
+ return self._GetSys()
+
+ def _SetSys(self, sys_resource):
+ """Accessor for sys property."""
+ self.__sys = sys_resource
+
+ def __SetSys(self, sys_resource):
+ """Indirect Accessor for _SetSys."""
+ return self._SetSys(sys_resource)
+
+ sys = property(__GetSys, __SetSys,
+ doc="""Gets and sets the resource to use as sys.""")
+
+
+def main(argv):
+ """Prepares the new project.
+
+ Args:
+ argv: The arguments passed to the script by the shell.
+ """
+ print 'init_project parsing its arguments.'
+ script_dir = os.path.abspath(os.path.dirname(__file__))
+ options = ParseArguments(argv)
+ print 'init_project is preparing your project.'
+ # Check to see if the project is going into the SDK bundle. If so, issue a
+ # warning.
+ sdk_root_dir = os.getenv('NACL_SDK_ROOT', None)
+ if sdk_root_dir:
+ if os.path.normpath(options.project_directory).count(
+ os.path.normpath(sdk_root_dir)) > 0:
+ print('WARNING: It looks like you are creating projects in the NaCl SDK '
+ 'directory %s.\nThese might be removed at the next update.' %
+ sdk_root_dir)
+ project_initializer = ProjectInitializer(options.is_c_project,
+ options.is_vs_project,
+ options.project_name,
+ options.project_directory,
+ options.nacl_platform,
+ script_dir)
+ project_initializer.PrepareDirectoryContent()
+ project_initializer.PrepareFileContent()
+ return 0
+
+
+if __name__ == '__main__':
+ try:
+ sys.exit(main(sys.argv[1:]))
+ except Exception as error:
+ print error
+ sys.exit(1)
diff --git a/native_client_sdk/src/project_templates/init_project_test.py b/native_client_sdk/src/project_templates/init_project_test.py
new file mode 100755
index 0000000..681916f
--- /dev/null
+++ b/native_client_sdk/src/project_templates/init_project_test.py
@@ -0,0 +1,256 @@
+#!/usr/bin/python2.6
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Unit tests for init_project.py."""
+
+__author__ = 'mlinck@google.com (Michael Linck)'
+
+import fileinput
+import os
+import shutil
+import sys
+import unittest
+import mox
+import init_project
+
+
+def TestMock(file_path, open_func):
+ temp_file = open_func(file_path)
+ temp_file.close()
+
+
+class TestGlobalFunctions(unittest.TestCase):
+ """Class for test cases to cover globally declared helper functions."""
+
+ def testGetCamelCaseName(self):
+ output = init_project.GetCamelCaseName('camel_case_name')
+ self.assertEqual(output, 'CamelCaseName')
+ output = init_project.GetCamelCaseName('print_42')
+ self.assertEqual(output, 'Print42')
+
+ def testGetCodeDirectory(self):
+ output = init_project.GetCodeDirectory(True, '')
+ self.assertEqual(output, 'c')
+ output = init_project.GetCodeDirectory(False, '')
+ self.assertEqual(output, 'cc')
+ output = init_project.GetCodeDirectory(True, 'test')
+ self.assertEqual(output, 'test/c')
+ output = init_project.GetCodeDirectory(False, 'test')
+ self.assertEqual(output, 'test/cc')
+
+ def testGetCodeSourceFiles(self):
+ output = init_project.GetCodeSourceFiles(False)
+ self.assertEqual(output, init_project.CC_SOURCE_FILES)
+ output = init_project.GetCodeSourceFiles(True)
+ self.assertEqual(output, init_project.C_SOURCE_FILES)
+
+ def testGetCommonSourceFiles(self):
+ output = init_project.GetCommonSourceFiles()
+ expected_output_linux = init_project.COMMON_PROJECT_FILES
+ expected_output_windows = init_project.COMMON_PROJECT_FILES
+ expected_output_windows.extend(['scons.bat'])
+ linux_match = (output == expected_output_linux)
+ windows_match = (output == expected_output_windows)
+ passed = (linux_match | windows_match)
+ self.assertTrue(passed)
+
+ def testGetHTMLDirectory(self):
+ output = init_project.GetHTMLDirectory('')
+ self.assertEqual(output, 'html')
+ output = init_project.GetHTMLDirectory('test')
+ self.assertEqual(output, 'test/html')
+
+ def testGetHTMLSourceFiles(self):
+ output = init_project.GetHTMLSourceFiles()
+ self.assertEqual(output, init_project.HTML_FILES)
+
+ def testGetTargetFileName(self):
+ output = init_project.GetTargetFileName('project_file.cc', 'bonkers')
+ self.assertEqual(output, 'bonkers.cc')
+ output = init_project.GetTargetFileName('constant.html', 'bonkers')
+ self.assertEqual(output, 'constant.html')
+
+ def testParseArguments(self):
+ output = init_project.ParseArguments(['-n', 'test_name', '-d', 'test/dir'])
+ self.assertEqual(output.is_c_project, False)
+ self.assertEqual(output.project_name, 'test_name')
+ self.assertEqual(output.project_directory, 'test/dir')
+ output = init_project.ParseArguments(['-n', 'test_name_2', '-c'])
+ self.assertEqual(output.is_c_project, True)
+ self.assertEqual(output.project_name, 'test_name_2')
+ self.assertEqual(output.project_directory,
+ init_project.GetDefaultProjectDir())
+
+
+class TestProjectInitializer(unittest.TestCase):
+ """Class for test cases to cover public interface of ProjectInitializer."""
+
+ def setUp(self):
+ self.script_dir = os.path.abspath(os.path.dirname(__file__))
+ self.nacl_src_dir = os.getenv('NACL_SDK_ROOT', None)
+ self.mock_factory = mox.Mox()
+ # This mock is only valid for initialization and will be overwritten
+ # after ward by self.os_mock.
+ init_path_mock = self.mock_factory.CreateMock(os.path)
+ init_path_mock.join('test/dir', 'test_project').AndReturn(
+ 'test/dir/test_project')
+ init_path_mock.exists('test/dir/test_project').AndReturn(False)
+ init_os_mock = self.mock_factory.CreateMock(os)
+ init_os_mock.path = init_path_mock
+ init_os_mock.makedirs('test/dir/test_project')
+ self.mock_factory.ReplayAll()
+ self.test_subject = init_project.ProjectInitializer(
+ # True => is C project, False => is vs project
+ True, False, 'test_project', 'test/dir', 'pepper_14', self.script_dir,
+ init_os_mock)
+ self.mock_factory.VerifyAll()
+ self.InitializeResourceMocks()
+
+ def InitializeResourceMocks(self):
+ """Can be called multiple times if multiple functions need to be tested."""
+ self.fileinput_mock = self.mock_factory.CreateMock(fileinput)
+ self.test_subject.fileinput = self.fileinput_mock
+ self.os_mock = self.mock_factory.CreateMock(os)
+ self.test_subject.os = self.os_mock
+ self.shutil_mock = self.mock_factory.CreateMock(shutil)
+ self.test_subject.shutil = self.shutil_mock
+ self.sys_mock = self.mock_factory.CreateMock(sys)
+ self.test_subject.sys = self.sys_mock
+
+ def testCopyAndRenameFiles(self):
+ self.shutil_mock.copy('source/dir/normal_name.txt',
+ 'test/dir/test_project/normal_name.txt')
+ self.shutil_mock.copy('source/dir/project_file.txt',
+ 'test/dir/test_project/test_project.txt')
+ self.os_mock.path = os.path
+ self.mock_factory.ReplayAll()
+ self.test_subject.CopyAndRenameFiles(
+ 'source/dir', ['normal_name.txt', 'project_file.txt'])
+ self.mock_factory.VerifyAll()
+
+ def testPrepareDirectoryContent(self):
+ self.shutil_mock.copy(
+ '%s/c/build.scons' % self.script_dir,
+ 'test/dir/test_project/build.scons')
+ self.shutil_mock.copy(
+ '%s/c/project_file.c' % self.script_dir,
+ 'test/dir/test_project/test_project.c')
+ self.shutil_mock.copy(
+ '%s/html/project_file.html' % self.script_dir,
+ 'test/dir/test_project/test_project.html')
+ self.shutil_mock.copy(
+ '%s/scons' % self.script_dir,
+ 'test/dir/test_project/scons')
+ self.shutil_mock.copy(
+ '%s/scons.bat' % self.script_dir,
+ 'test/dir/test_project/scons.bat')
+ self.os_mock.path = os.path
+ self.mock_factory.ReplayAll()
+ self.test_subject.PrepareDirectoryContent()
+ self.mock_factory.VerifyAll()
+
+ def testPrepareFileContent(self):
+ self.testCopyAndRenameFiles()
+ # We need a new set of resource mocks since the old ones have already been
+ # used.
+ self.InitializeResourceMocks()
+ path_mock = self.mock_factory.CreateMock(os.path)
+ stdout_mock = self.mock_factory.CreateMock(sys.stdout)
+ self.os_mock.path = path_mock
+ path_mock.abspath(self.nacl_src_dir).AndReturn(self.nacl_src_dir)
+ self.fileinput_mock.input(
+ 'test/dir/test_project/normal_name.txt',
+ inplace=1, mode='U').AndReturn(
+ ['A line with <PROJECT_NAME>.',
+ 'A line with <ProjectName>.',
+ 'A line with <NACL_SDK_ROOT>.',
+ 'A line with <NACL_PLATFORM>.'])
+ stdout_mock.write('A line with test_project.')
+ stdout_mock.write('A line with <ProjectName>.')
+ stdout_mock.write('A line with <NACL_SDK_ROOT>.')
+ stdout_mock.write('A line with <NACL_PLATFORM>.')
+ self.fileinput_mock.input(
+ 'test/dir/test_project/normal_name.txt',
+ inplace=1, mode='U').AndReturn(
+ ['A line with test_project.',
+ 'A line with <ProjectName>.',
+ 'A line with <NACL_SDK_ROOT>.',
+ 'A line with <NACL_PLATFORM>.'])
+ stdout_mock.write('A line with test_project.')
+ stdout_mock.write('A line with TestProject.')
+ stdout_mock.write('A line with <NACL_SDK_ROOT>.')
+ stdout_mock.write('A line with <NACL_PLATFORM>.')
+ self.fileinput_mock.input(
+ 'test/dir/test_project/normal_name.txt',
+ inplace=1, mode='U').AndReturn(
+ ['A line with test_project.',
+ 'A line with TestProject.',
+ 'A line with <NACL_SDK_ROOT>.',
+ 'A line with <NACL_PLATFORM>.'])
+ stdout_mock.write('A line with test_project.')
+ stdout_mock.write('A line with TestProject.')
+ stdout_mock.write('A line with %s.' % self.nacl_src_dir)
+ stdout_mock.write('A line with <NACL_PLATFORM>.')
+ self.fileinput_mock.input(
+ 'test/dir/test_project/normal_name.txt',
+ inplace=1, mode='U').AndReturn(
+ ['A line with test_project.',
+ 'A line with TestProject.',
+ 'A line with some/dir.',
+ 'A line with <NACL_PLATFORM>.'])
+ stdout_mock.write('A line with test_project.')
+ stdout_mock.write('A line with TestProject.')
+ stdout_mock.write('A line with some/dir.')
+ stdout_mock.write('A line with pepper_14.')
+ # One multi-line file with different replacements has already been mocked
+ # so we make this next test simpler.
+ self.fileinput_mock.input(
+ 'test/dir/test_project/test_project.txt',
+ inplace=1, mode='U').AndReturn(['A line with no replaceable text.'])
+ stdout_mock.write('A line with no replaceable text.')
+ self.fileinput_mock.input(
+ 'test/dir/test_project/test_project.txt',
+ inplace=1, mode='U').AndReturn(['A line with no replaceable text.'])
+ stdout_mock.write('A line with no replaceable text.')
+ self.fileinput_mock.input(
+ 'test/dir/test_project/test_project.txt',
+ inplace=1, mode='U').AndReturn(['A line with no replaceable text.'])
+ stdout_mock.write('A line with no replaceable text.')
+ self.fileinput_mock.input(
+ 'test/dir/test_project/test_project.txt',
+ inplace=1, mode='U').AndReturn(['A line with no replaceable text.'])
+ stdout_mock.write('A line with no replaceable text.')
+ self.sys_mock.stdout = stdout_mock
+ self.mock_factory.ReplayAll()
+ self.test_subject.PrepareFileContent()
+ self.mock_factory.VerifyAll()
+
+ def testReplaceInFile(self):
+ self.fileinput_mock.input('test/path', inplace=1, mode='U').AndReturn(
+ ['A sentence replace_me.'])
+ stdout_mock = self.mock_factory.CreateMock(sys.stdout)
+ stdout_mock.write('A sentence with_this.')
+ self.sys_mock.stdout = stdout_mock
+ self.mock_factory.ReplayAll()
+ self.test_subject.ReplaceInFile('test/path', 'replace_me', 'with_this')
+ self.mock_factory.VerifyAll()
+
+
+def RunTests():
+ # It's possible to do this with one suite instead of two, but then it's
+ # harder to read the test output.
+ return_value = 1
+ suite_one = unittest.TestLoader().loadTestsFromTestCase(TestGlobalFunctions)
+ result_one = unittest.TextTestRunner(verbosity=2).run(suite_one)
+ suite_two = unittest.TestLoader().loadTestsFromTestCase(
+ TestProjectInitializer)
+ result_two = unittest.TextTestRunner(verbosity=2).run(suite_two)
+ if result_one.wasSuccessful() and result_two.wasSuccessful():
+ return_value = 0
+ return return_value
+
+if __name__ == '__main__':
+ sys.exit(RunTests())
diff --git a/native_client_sdk/src/project_templates/scons b/native_client_sdk/src/project_templates/scons
new file mode 100755
index 0000000..2b7e0d3
--- /dev/null
+++ b/native_client_sdk/src/project_templates/scons
@@ -0,0 +1,32 @@
+#!/bin/bash
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+readonly SCRIPT_DIR="$(dirname "$0")"
+readonly SCRIPT_DIR_ABS="$(cd "${SCRIPT_DIR}" ; pwd -P)"
+
+export NACL_SDK_ROOT=<NACL_SDK_ROOT>
+# NACL_TARGET_PLATFORM is really the name of a folder with the base dir -
+# usually NACL_SDK_ROOT - within which the toolchain for the target platform
+# are found.
+# Replace the platform with the name of your target platform. For example, to
+# build applications that target the pepper_17 API, set
+# NACL_TARGET_PLATFORM="pepper_17"
+export NACL_TARGET_PLATFORM="<NACL_PLATFORM>"
+
+readonly NACL_PLATFORM_DIR="${NACL_SDK_ROOT}/${NACL_TARGET_PLATFORM}"
+readonly BASE_SCRIPT="${NACL_PLATFORM_DIR}/third_party/scons-2.0.1/script/scons"
+
+export SCONS_LIB_DIR="${NACL_PLATFORM_DIR}/third_party/scons-2.0.1/engine"
+export PYTHONPATH="${NACL_PLATFORM_DIR}/third_party/scons-2.0.1/engine"
+# We have to do this because scons overrides PYTHONPATH and does not preserve
+# what is provided by the OS. The custom variable name won't be overwritten.
+export PYMOX="${NACL_PLATFORM_DIR}/third_party/pymox"
+
+"${BASE_SCRIPT}" --file=build.scons \
+ --site-dir="${NACL_PLATFORM_DIR}/build_tools/nacl_sdk_scons" \
+ $*
+
diff --git a/native_client_sdk/src/project_templates/scons.bat b/native_client_sdk/src/project_templates/scons.bat
new file mode 100755
index 0000000..f487e28
--- /dev/null
+++ b/native_client_sdk/src/project_templates/scons.bat
@@ -0,0 +1,31 @@
+@echo off
+
+:: Copyright (c) 2011 The Native Client Authors. All rights reserved.
+:: Use of this source code is governed by a BSD-style license that can be
+:: found in the LICENSE file.
+
+setlocal
+
+set NACL_SDK_ROOT=<NACL_SDK_ROOT>
+:: NACL_TARGET_PLATFORM is really the name of a folder with the base dir -
+:: usually NACL_SDK_ROOT - within which the toolchain for the target platform
+:: are found.
+:: Replace the platform with the name of your target platform. For example, to
+:: build applications that target the pepper_17 API, set
+:: NACL_TARGET_PLATFORM=pepper_17
+set NACL_TARGET_PLATFORM=<NACL_PLATFORM>
+
+set NACL_PLATFORM_DIR=%NACL_SDK_ROOT%\%NACL_TARGET_PLATFORM%
+
+:: Set the PYTHONPATH and SCONS_LIB_DIR so we can import SCons modules
+set SCONS_LIB_DIR=%NACL_PLATFORM_DIR%\third_party\scons-2.0.1\engine
+set PYTHONPATH=%NACL_PLATFORM_DIR%\third_party\scons-2.0.1\engine
+
+:: We have to do this because scons overrides PYTHONPATH and does not preserve
+:: what is provided by the OS. The custom variable name won't be overwritten.
+set PYMOX=%NACL_PLATFORM_DIR%\third_party\pymox
+
+:: Run the included copy of scons.
+python -O -OO "%NACL_PLATFORM_DIR%\third_party\scons-2.0.1\script\scons" ^
+--file=build.scons ^
+--site-dir="%NACL_PLATFORM_DIR%\build_tools\nacl_sdk_scons" %*
diff --git a/native_client_sdk/src/project_templates/test.scons b/native_client_sdk/src/project_templates/test.scons
new file mode 100644
index 0000000..c00e2a8
--- /dev/null
+++ b/native_client_sdk/src/project_templates/test.scons
@@ -0,0 +1,26 @@
+#! -*- python -*-
+#
+# Copyright (c) 2010 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Build file for tests of init_project interface.
+
+Adapted from scons documentation: http://www.scons.org/wiki/UnitTests
+
+RunUnitTests(): Runs a comment and uses the return code to determine success.
+"""
+
+__author__ = 'mlinck@google.com (Michael Linck)'
+
+import os
+import sys
+
+Import('env')
+
+#TODO(mlinck) Enable this test again when it works on Windows
+cmd = env.CreatePythonUnitTest(filename='init_project_test.py',
+ dependencies=['init_project.py'],
+ disabled=env['IS_WINDOWS'])
+
+env.AddNodeToTestSuite(cmd, ['bot'], 'run_init_project_test', 'small')
diff --git a/native_client_sdk/src/project_templates/vs/project_file.sln b/native_client_sdk/src/project_templates/vs/project_file.sln
new file mode 100644
index 0000000..05f5169
--- /dev/null
+++ b/native_client_sdk/src/project_templates/vs/project_file.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "<PROJECT_NAME>", "<PROJECT_NAME>.vcproj", "{<VS_PROJECT_UUID>}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {<VS_PROJECT_UUID>}.Debug|Win32.ActiveCfg = Debug|Win32
+ {<VS_PROJECT_UUID>}.Debug|Win32.Build.0 = Debug|Win32
+ {<VS_PROJECT_UUID>}.Release|Win32.ActiveCfg = Release|Win32
+ {<VS_PROJECT_UUID>}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/native_client_sdk/src/project_templates/vs/project_file.vcproj b/native_client_sdk/src/project_templates/vs/project_file.vcproj
new file mode 100644
index 0000000..d2b0f25
--- /dev/null
+++ b/native_client_sdk/src/project_templates/vs/project_file.vcproj
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="<PROJECT_NAME>"
+ ProjectGUID="{<VS_PROJECT_UUID>"
+ RootNamespace="<PROJECT_NAME>"
+ Keyword="MakeFileProj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(NACL_SDK_ROOT)\<NACL_PLATFORM>\examples\scons"
+ ReBuildCommandLine="$(NACL_SDK_ROOT)\<NACL_PLATFORM>\examples\scons -c &amp;&amp; $(NACL_SDK_ROOT)\<NACL_PLATFORM>\examples\scons"
+ CleanCommandLine="$(NACL_SDK_ROOT)\<NACL_PLATFORM>\examples\scons -c"
+ Output="<PROJECT_NAME>_x86_64_dbg.nexe"
+ PreprocessorDefinitions="WIN32;_DEBUG"
+ IncludeSearchPath=""
+ ForcedIncludes=""
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine=""
+ ReBuildCommandLine=""
+ CleanCommandLine=""
+ Output="<PROJECT_NAME>.exe"
+ PreprocessorDefinitions="WIN32;NDEBUG"
+ IncludeSearchPath=""
+ ForcedIncludes=""
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{<VS_SOURCE_UUID>}"
+ >
+ <File
+ RelativePath=".\build.scons"
+ >
+ </File>
+ <File
+ RelativePath=".\<PROJECT_NAME>.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{<VS_HEADER_UUID>}"
+ >
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{<VS_RESOURCE_UUID>}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="<PROJECT_NAME>"
+ ProjectGUID="{<VS_PROJECT_UUID>"
+ RootNamespace="<PROJECT_NAME>"
+ Keyword="MakeFileProj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine="$(NACL_SDK_ROOT)\<NACL_PLATFORM>\examples\scons"
+ ReBuildCommandLine="$(NACL_SDK_ROOT)\<NACL_PLATFORM>\examples\scons -c &amp;&amp; $(NACL_SDK_ROOT)\<NACL_PLATFORM>\examples\scons"
+ CleanCommandLine="$(NACL_SDK_ROOT)\<NACL_PLATFORM>\examples\scons -c"
+ Output="<PROJECT_NAME>_x86_64_dbg.nexe"
+ PreprocessorDefinitions="WIN32;_DEBUG"
+ IncludeSearchPath=""
+ ForcedIncludes=""
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine=""
+ ReBuildCommandLine=""
+ CleanCommandLine=""
+ Output="<PROJECT_NAME>.exe"
+ PreprocessorDefinitions="WIN32;NDEBUG"
+ IncludeSearchPath=""
+ ForcedIncludes=""
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{<VS_SOURCE_UUID>}"
+ >
+ <File
+ RelativePath=".\build.scons"
+ >
+ </File>
+ <File
+ RelativePath=".\<PROJECT_NAME>.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{<VS_HEADER_UUID>}"
+ >
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{<VS_RESOURCE_UUID>}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/native_client_sdk/src/scons b/native_client_sdk/src/scons
new file mode 100755
index 0000000..c14f666
--- /dev/null
+++ b/native_client_sdk/src/scons
@@ -0,0 +1,37 @@
+#!/bin/bash
+#
+# Copyright (c) 2011 The Native Client Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+readonly SCRIPT_DIR="$(dirname "$0")"
+readonly SCRIPT_DIR_ABS="$(cd "${SCRIPT_DIR}" ; pwd -P)"
+
+# Use the batch file as an entry point if on cygwin.
+if [ "x${OSTYPE}" = "xcygwin" ]; then
+ # Use extended globbing (cygwin should always have it).
+ shopt -s extglob
+ # Filter out cygwin python (everything under /usr or /bin, or *cygwin*).
+ export PATH=${PATH/#\/bin*([^:])/}
+ export PATH=${PATH//:\/bin*([^:])/}
+ export PATH=${PATH/#\/usr*([^:])/}
+ export PATH=${PATH//:\/usr*([^:])/}
+ export PATH=${PATH/#*([^:])cygwin*([^:])/}
+ export PATH=${PATH//:*([^:])cygwin*([^:])/}
+ "${SCRIPT_DIR_ABS}/scons.bat" $*
+ exit
+fi
+
+readonly BASE_SCRIPT="${SCRIPT_DIR_ABS}/third_party/scons-2.0.1/script/scons"
+
+export NACL_SDK_ROOT="${SCRIPT_DIR_ABS}"
+
+export SCONS_LIB_DIR="${NACL_SDK_ROOT}/third_party/scons-2.0.1/engine"
+export PYTHONPATH="${SCRIPT_DIR_ABS}/third_party/scons-2.0.1/engine:${SCRIPT_DIR_ABS}/third_party/native_client/native_client/build"
+# We have to do this because scons overrides PYTHONPATH and does not preserve
+# what is provided by the OS. The custom variable name won't be overwritten.
+export PYMOX="${NACL_SDK_ROOT}/third_party/pymox"
+
+"${BASE_SCRIPT}" --file=main.scons $*
+
diff --git a/native_client_sdk/src/scons.bat b/native_client_sdk/src/scons.bat
new file mode 100755
index 0000000..0a6d3db
--- /dev/null
+++ b/native_client_sdk/src/scons.bat
@@ -0,0 +1,28 @@
+@echo off
+
+:: Copyright (c) 2011 The Native Client Authors. All rights reserved.
+:: Use of this source code is governed by a BSD-style license that can be
+:: found in the LICENSE file.
+
+setlocal
+
+set NACL_SDK_ROOT=%~dp0
+
+:: Preserve a copy of the PATH (in case we need it later, mainly for cygwin).
+set PRESCONS_PATH=%PATH%
+
+:: Set the PYTHONPATH and SCONS_LIB_DIR so we can import SCons modules
+set SCONS_LIB_DIR=%~dp0third_party\scons-2.0.1\engine
+set PYTHONPATH=%~dp0third_party\scons-2.0.1\engine;%~dp0third_party\native_client\native_client\build
+
+:: We have to do this because scons overrides PYTHONPATH and does not preserve
+:: what is provided by the OS. The custom variable name won't be overwritten.
+set PYMOX=%~dp0third_party\pymox
+
+:: Stop incessant CYGWIN complains about "MS-DOS style path"
+set CYGWIN=nodosfilewarning %CYGWIN%
+
+:: Run the included copy of scons.
+python -O -OO "%~dp0third_party\scons-2.0.1\script\scons" --file=main.scons %*
+
+:end